package logic import ( "context" "strconv" "lone-services/pkg/modelbase" "lone-services/pkg/utils" "lone-services/pkg/validate" user "lone-services/rpc/user/pb" "lone-services/services/user/internal/dao" "lone-services/services/user/internal/model" "lone-services/services/user/internal/svc" "lone-services/services/user/validator" "github.com/zeromicro/go-zero/core/logx" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "gorm.io/gorm" ) type InternalCreateLogic struct { ctx context.Context svcCtx *svc.ServiceContext logx.Logger } func NewInternalCreateLogic(ctx context.Context, svcCtx *svc.ServiceContext) *InternalCreateLogic { return &InternalCreateLogic{ ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx), } } func (l *InternalCreateLogic) InternalCreate(in *user.InternalCreateReq) (*user.InternalCreateData, error) { var req validator.InternalCreateValidator if msg := validate.ValidateFromProto(in, &req); msg != utils.StringEmpty { return nil, status.Error(codes.InvalidArgument, msg) } clientCodes := uniqueNonEmpty(req.ClientCode) if len(clientCodes) == 0 { return nil, status.Error(codes.InvalidArgument, "端编码不能为空") } clientModel := model.ClientModel{}.Init() subjectTypes := make([]string, 0, len(clientCodes)) seenSubject := make(map[string]struct{}, len(clientCodes)) for _, code := range clientCodes { var client dao.Client if err := clientModel.GetOne(modelbase.Params{ Eq: map[string]string{ "code": code, "status": strconv.Itoa(int(dao.StatusEnabled)), }, }, &client); err != nil { l.Errorf("internal create client: %v", err) return nil, status.Error(codes.Internal, utils.Fail.Msg) } if client.Id < utils.NumberOne { return nil, status.Error(codes.InvalidArgument, "端配置不存在或已禁用: "+code) } if client.SubjectType == utils.StringEmpty { return nil, status.Error(codes.InvalidArgument, "端主体类型未配置: "+code) } if _, ok := seenSubject[client.SubjectType]; !ok { seenSubject[client.SubjectType] = struct{}{} subjectTypes = append(subjectTypes, client.SubjectType) } } encryptMobile, encErr := utils.EncryptPhone(req.Mobile) if encErr != nil { l.Errorf("internal create encrypt mobile: %v", encErr) return nil, status.Error(codes.Internal, utils.ErrorEncryptAesError.Msg) } var ( userId int64 name = req.Name ) credentialTypes := uniqueNonEmpty(req.CredentialType) txErr := l.svcCtx.DB.WithContext(l.ctx).Transaction(func(tx *gorm.DB) error { userModel := model.UserModel{}.Init() userModel.Base = userModel.Base.WithTX(tx) var exist dao.UserRow if err := userModel.GetOne(modelbase.Params{ Eq: map[string]string{"mobile": encryptMobile}, }, &exist); err != nil { return err } if exist.Id > utils.NumberZero { userId = exist.Id if name == utils.StringEmpty { name = exist.Name } if exist.Status == dao.StatusDisabled { return status.Error(codes.FailedPrecondition, "用户已禁用") } } else { add := dao.UserCreate{ Mobile: encryptMobile, Name: req.Name, Avatar: req.Avatar, Gender: uint8(req.Gender), Birthday: req.Birthday, Status: dao.StatusEnabled, } if err := userModel.Create(&add); err != nil { return err } userId = add.Id } if err := ensureUserDetail(tx, userId); err != nil { return err } for _, subjectType := range subjectTypes { for _, credType := range credentialTypes { if err := ensureCredential(tx, userId, credType, req.Identifier, req.Secret, encryptMobile, subjectType); err != nil { return err } } } for _, code := range clientCodes { if err := ensureUserClient(tx, userId, code, req.BizId); err != nil { return err } } return nil }) if txErr != nil { if st, ok := status.FromError(txErr); ok { return nil, st.Err() } l.Errorf("internal create tx: %v", txErr) return nil, status.Error(codes.Internal, utils.Fail.Msg) } return &user.InternalCreateData{ UserId: userId, Mobile: encryptMobile, Name: name, }, nil } func ensureCredential(tx *gorm.DB, userId int64, credentialType, identifier, secret, encryptMobile, subjectType string) error { if subjectType == utils.StringEmpty { return status.Error(codes.InvalidArgument, "主体类型不能为空") } if identifier == utils.StringEmpty { identifier = encryptMobile } else if credentialType != dao.CredentialTypeOpenid && credentialType != dao.CredentialTypeUnionid { encrypted, encErr := utils.EncryptPhone(identifier) if encErr != nil { return status.Error(codes.Internal, utils.ErrorEncryptAesError.Msg) } identifier = encrypted } hashedSecret := utils.StringEmpty if credentialType == dao.CredentialTypePassword && secret != utils.StringEmpty { hashed, err := utils.EncryptPassword(secret) if err != nil { return err } hashedSecret = hashed } credModel := model.UserCredentialModel{}.Init() credModel.Base = credModel.Base.WithTX(tx) var exist dao.UserCredential if err := credModel.GetOne(modelbase.Params{ Eq: map[string]string{ "credential_type": credentialType, "identifier": identifier, "subject_type": subjectType, }, }, &exist); err != nil { return err } if exist.Id > utils.NumberZero { if exist.UserId != userId { return status.Error(codes.AlreadyExists, "登录凭证已被其他用户占用") } if hashedSecret == utils.StringEmpty { return nil } _, err := credModel.Edit(modelbase.Params{ Eq: map[string]string{"id": strconv.FormatInt(exist.Id, utils.NumberTen)}, }, map[string]interface{}{ "secret": hashedSecret, "status": dao.StatusEnabled, }) return err } return credModel.Create(&dao.UserCredential{ UserId: userId, CredentialType: credentialType, Identifier: identifier, Secret: hashedSecret, SubjectType: subjectType, ExtraJson: dao.ExtraJsonEmpty, Status: dao.StatusEnabled, }) } func ensureUserClient(tx *gorm.DB, userId int64, clientCode string, bizId int64) error { clientModel := model.UserClientModel{}.Init() clientModel.Base = clientModel.Base.WithTX(tx) var exist dao.UserClient if err := clientModel.GetOne(modelbase.Params{ Eq: map[string]string{ "user_id": strconv.FormatInt(userId, utils.NumberTen), "client_code": clientCode, }, }, &exist); err != nil { return err } if exist.Id > utils.NumberZero { updates := map[string]interface{}{} if exist.Status != dao.StatusEnabled { updates["status"] = dao.StatusEnabled } if bizId > utils.NumberZero && exist.BizId != bizId { updates["biz_id"] = bizId } if len(updates) == 0 { return nil } _, err := clientModel.Edit(modelbase.Params{ Eq: map[string]string{"id": strconv.FormatInt(exist.Id, utils.NumberTen)}, }, updates) return err } return clientModel.Create(&dao.UserClient{ UserId: userId, ClientCode: clientCode, BizId: bizId, Status: dao.StatusEnabled, OpenedAt: utils.Now(), }) } func uniqueNonEmpty(items []string) []string { out := make([]string, 0, len(items)) seen := make(map[string]struct{}, len(items)) for _, item := range items { if item == utils.StringEmpty { continue } if _, ok := seen[item]; ok { continue } seen[item] = struct{}{} out = append(out, item) } return out }