145 lines
4.3 KiB
Go
145 lines
4.3 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"time"
|
|
|
|
appErrors "github.com/topfans/backend/pkg/errors"
|
|
"github.com/topfans/backend/pkg/logger"
|
|
notifPb "github.com/topfans/backend/pkg/proto/notification"
|
|
"github.com/topfans/backend/services/notificationService/repository"
|
|
"go.uber.org/zap"
|
|
"google.golang.org/grpc/codes"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// ========== 设备注册服务 ==========
|
|
//
|
|
// UserDeviceService 处理客户端推送 token (cid) 的注册与注销。
|
|
// 调用方:gateway 通过 Dubbo RPC 转发 App 端的 RegisterDevice / UnregisterDevice。
|
|
//
|
|
// 行为约定:
|
|
// - RegisterDevice:按 cid upsert;同 cid 已存在则更新归属/platform/version。
|
|
// - UnregisterDevice:cid 非空时按 cid 注销;cid 为空时按 user_id 注销该用户所有设备(主动登出场景)。
|
|
|
|
// UserDeviceService 设备服务。
|
|
type UserDeviceService struct {
|
|
db *gorm.DB
|
|
repo *repository.UserDeviceRepository
|
|
}
|
|
|
|
// NewUserDeviceService 创建 UserDeviceService。
|
|
func NewUserDeviceService(db *gorm.DB) *UserDeviceService {
|
|
return &UserDeviceService{
|
|
db: db,
|
|
repo: repository.NewUserDeviceRepository(db),
|
|
}
|
|
}
|
|
|
|
// DB 返回底层 gorm.DB(供同包 NotificationService 调用查询 cids)。
|
|
func (s *UserDeviceService) DB() *gorm.DB { return s.db }
|
|
|
|
// RegisterDevice 注册/更新 cid。
|
|
func (s *UserDeviceService) RegisterDevice(
|
|
ctx context.Context,
|
|
userID int64,
|
|
req *notifPb.RegisterDeviceRequest,
|
|
) (*notifPb.RegisterDeviceResponse, error) {
|
|
if userID <= 0 {
|
|
return nil, appErrors.NewError(codes.InvalidArgument, "user_id is required")
|
|
}
|
|
if req == nil || strings.TrimSpace(req.Cid) == "" {
|
|
return nil, appErrors.NewError(codes.InvalidArgument, "cid is required")
|
|
}
|
|
|
|
// 平台号做白名单简单保护(后续可加更严格的枚举)
|
|
platform := strings.ToLower(strings.TrimSpace(req.Platform))
|
|
switch platform {
|
|
case "ios", "android", "harmony", "":
|
|
// 通过
|
|
default:
|
|
// 未知平台也允许写入,便于鸿蒙/小程序等新通道接入;只 warn
|
|
logger.Logger.Warn("register device with unknown platform",
|
|
zap.Int64("user_id", userID),
|
|
zap.String("platform", platform))
|
|
}
|
|
|
|
d, err := s.repo.UpsertByCID(ctx, nil,
|
|
strings.TrimSpace(req.Cid),
|
|
userID,
|
|
platform,
|
|
strings.TrimSpace(req.AppVersion),
|
|
strings.TrimSpace(req.DeviceModel),
|
|
)
|
|
if err != nil {
|
|
logger.Logger.Error("RegisterDevice upsert failed",
|
|
zap.Int64("user_id", userID),
|
|
zap.String("cid", req.Cid),
|
|
zap.Error(err))
|
|
return nil, appErrors.NewError(codes.Internal, "register device failed")
|
|
}
|
|
|
|
logger.Logger.Info("device registered",
|
|
zap.Int64("id", d.ID),
|
|
zap.Int64("user_id", userID),
|
|
zap.String("platform", d.Platform),
|
|
zap.String("cid", d.CID))
|
|
|
|
return ¬ifPb.RegisterDeviceResponse{
|
|
Base: appErrors.FormatSuccessResponse(),
|
|
Id: d.ID,
|
|
}, nil
|
|
}
|
|
|
|
// UnregisterDevice 注销 cid;cid 为空时注销 user 的所有设备。
|
|
func (s *UserDeviceService) UnregisterDevice(
|
|
ctx context.Context,
|
|
userID int64,
|
|
req *notifPb.UnregisterDeviceRequest,
|
|
) (*notifPb.UnregisterDeviceResponse, error) {
|
|
if userID <= 0 {
|
|
return nil, appErrors.NewError(codes.InvalidArgument, "user_id is required")
|
|
}
|
|
if req == nil {
|
|
return nil, appErrors.NewError(codes.InvalidArgument, "request is nil")
|
|
}
|
|
|
|
cid := strings.TrimSpace(req.Cid)
|
|
var affected int64
|
|
var err error
|
|
|
|
if cid != "" {
|
|
affected, err = s.repo.DeactivateByCID(ctx, nil, cid)
|
|
} else {
|
|
// cid 为空:注销该用户全部(批量 UPDATE)
|
|
res := s.db.WithContext(ctx).Exec(`
|
|
UPDATE public.user_devices
|
|
SET active = FALSE, updated_at = $1
|
|
WHERE user_id = $2 AND active = TRUE
|
|
`, time.Now().UnixMilli(), userID)
|
|
affected, err = res.RowsAffected, res.Error
|
|
}
|
|
if err != nil {
|
|
logger.Logger.Error("UnregisterDevice failed",
|
|
zap.Int64("user_id", userID),
|
|
zap.String("cid", cid),
|
|
zap.Error(err))
|
|
return nil, appErrors.NewError(codes.Internal, "unregister device failed")
|
|
}
|
|
|
|
logger.Logger.Info("device unregistered",
|
|
zap.Int64("user_id", userID),
|
|
zap.String("cid", cid),
|
|
zap.Int64("affected", affected))
|
|
|
|
return ¬ifPb.UnregisterDeviceResponse{
|
|
Base: appErrors.FormatSuccessResponse(),
|
|
Affected: int32(affected),
|
|
}, nil
|
|
}
|
|
|
|
// ListActiveCIDs 获取某用户的活跃 cid 列表(供 NotificationService 推送时调用)。
|
|
func (s *UserDeviceService) ListActiveCIDs(ctx context.Context, userID int64) ([]string, error) {
|
|
return s.repo.ListActiveCIDsByUserID(ctx, userID)
|
|
} |