package logic import ( "context" "errors" "strconv" "strings" "lone-services/pkg/modelbase" "lone-services/pkg/utils" "lone-services/services/user/internal/config" "lone-services/services/user/internal/dao" "lone-services/services/user/internal/model" jsoniter "github.com/json-iterator/go" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "gorm.io/gorm" ) func clientAllowsGrant(allowedGrants, grantType string) bool { if allowedGrants == utils.StringEmpty { return false } var grants []string if err := jsoniter.UnmarshalFromString(allowedGrants, &grants); err != nil { return strings.Contains(allowedGrants, grantType) } for _, g := range grants { if g == grantType { return true } } return false } func loadEnabledClient(clientCode string) (*dao.Client, error) { var client dao.Client clientModel := model.ClientModel{}.Init() if err := clientModel.GetOne(modelbase.Params{ Eq: map[string]string{ "code": clientCode, "status": strconv.Itoa(int(dao.StatusEnabled)), }, }, &client); err != nil { return nil, err } if client.Id < utils.NumberOne { return nil, status.Error(codes.InvalidArgument, "端配置不存在或已禁用") } return &client, nil } func findCredential(credentialType, identifier string) (*dao.UserCredential, error) { var cred dao.UserCredential credModel := model.UserCredentialModel{}.Init() if err := credModel.GetOne(modelbase.Params{ Eq: map[string]string{ "credential_type": credentialType, "identifier": identifier, }, }, &cred); err != nil { return nil, err } if cred.Id < utils.NumberOne { return nil, nil } return &cred, nil } func loadEnabledUser(userId int64) (*dao.UserRow, error) { var row dao.UserRow userModel := model.UserModel{}.Init() if err := userModel.GetOne(modelbase.Params{ Eq: map[string]string{ "id": strconv.FormatInt(userId, utils.NumberTen), "status": strconv.Itoa(int(dao.StatusEnabled)), }, }, &row); err != nil { return nil, err } if row.Id < utils.NumberOne { return nil, status.Error(codes.NotFound, utils.ErrorNotFund.Msg) } return &row, nil } func resolveOrCreateWechatUser(ctx context.Context, db *gorm.DB, openid, unionid, clientCode string) (int64, error) { openCred, err := findCredential(dao.CredentialTypeOpenid, openid) if err != nil { return utils.NumberZero, err } if openCred != nil { if openCred.Status == dao.StatusDisabled { return utils.NumberZero, status.Error(codes.FailedPrecondition, "登录凭证已禁用") } if err := ensureUserClient(db, openCred.UserId, clientCode); err != nil { return utils.NumberZero, err } return openCred.UserId, nil } var userId int64 if unionid != utils.StringEmpty { unionCred, uErr := findCredential(dao.CredentialTypeUnionid, unionid) if uErr != nil { return utils.NumberZero, uErr } if unionCred != nil { if unionCred.Status == dao.StatusDisabled { return utils.NumberZero, status.Error(codes.FailedPrecondition, "登录凭证已禁用") } userId = unionCred.UserId } } txErr := db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { if userId < utils.NumberOne { userModel := model.UserModel{}.Init() userModel.Base = userModel.Base.WithTX(tx) add := dao.UserCreate{Status: dao.StatusEnabled} if err := userModel.Create(&add); err != nil { return err } userId = add.Id } if err := createWechatCredentials(tx, userId, openid, unionid); err != nil { return err } return ensureUserClient(tx, userId, clientCode) }) if txErr != nil { if st, ok := status.FromError(txErr); ok { return utils.NumberZero, st.Err() } return utils.NumberZero, txErr } return userId, nil } func createWechatCredentials(tx *gorm.DB, userId int64, openid, unionid string) error { credModel := model.UserCredentialModel{}.Init() credModel.Base = credModel.Base.WithTX(tx) var existOpen dao.UserCredential if err := credModel.GetOne(modelbase.Params{ Eq: map[string]string{ "credential_type": dao.CredentialTypeOpenid, "identifier": openid, }, }, &existOpen); err != nil { return err } if existOpen.Id < utils.NumberOne { if err := credModel.Create(&dao.UserCredential{ UserId: userId, CredentialType: dao.CredentialTypeOpenid, Identifier: openid, ExtraJson: dao.ExtraJsonEmpty, Status: dao.StatusEnabled, }); err != nil { return err } } else if existOpen.UserId != userId { return status.Error(codes.AlreadyExists, "openid已被其他用户占用") } if unionid == utils.StringEmpty { return nil } var existUnion dao.UserCredential if err := credModel.GetOne(modelbase.Params{ Eq: map[string]string{ "credential_type": dao.CredentialTypeUnionid, "identifier": unionid, }, }, &existUnion); err != nil { return err } if existUnion.Id < utils.NumberOne { return credModel.Create(&dao.UserCredential{ UserId: userId, CredentialType: dao.CredentialTypeUnionid, Identifier: unionid, ExtraJson: dao.ExtraJsonEmpty, Status: dao.StatusEnabled, }) } if existUnion.UserId != userId { return status.Error(codes.AlreadyExists, "unionid 已被其他用户占用") } return nil } func issueLoginToken(ctx context.Context, jwtAuth *config.JWTAuth, saleSvcName string, row *dao.UserRow, client *dao.Client) (*dao.Token, error) { if jwtAuth == nil || jwtAuth.Access == nil || jwtAuth.Refresh == nil { return nil, errors.New("jwt not initialized") } mobile := decryptMobile(row.Mobile) var saleId int64 userType := dao.TypeUser if client != nil { userType = subjectTypeToUserType(client.SubjectType) } if userType == dao.TypeSale { saleInfo, err := fetchSaleByUserId(ctx, saleSvcName, row.Id) if err != nil { return nil, err } if saleInfo != nil { saleId = saleInfo.Id } } session := toSession(*row, mobile, client, saleId) ret, err := buildJwtToken(jwtAuth, session) if err != nil { return nil, err } if st := setLogin(jwtAuth, ret.AccessToken, ret.RefreshToken, session); st.Code != utils.Ok.Code { return nil, errors.New(st.Msg) } return &ret, nil }