From 972b10eeaee68f831c63530e19d9c6f0965ffc5a Mon Sep 17 00:00:00 2001 From: liangzai <2440983361@qq.com> Date: Sun, 11 May 2025 19:39:56 +0800 Subject: [PATCH] =?UTF-8?q?1=E3=80=81=E7=A4=BA=E4=BE=8B=E6=8A=A5=E5=91=8A?= =?UTF-8?q?=E6=94=B9=E6=9C=AA=E7=99=BB=E5=BD=95=E5=8F=AF=E8=AE=BF=E9=97=AE?= =?UTF-8?q?=202=E3=80=81=E5=A2=9E=E5=8A=A0=E4=BB=A3=E7=90=86=E6=8E=A8?= =?UTF-8?q?=E5=B9=BF=E6=8A=A5=E5=91=8A=E6=9F=A5=E8=AF=A2=E6=B3=A8=E5=86=8C?= =?UTF-8?q?UUID=E8=B4=A6=E6=88=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/user/cmd/api/desc/query.api | 8 +- app/user/cmd/api/internal/handler/routes.go | 41 +++---- .../internal/logic/query/queryservicelogic.go | 100 +++++++----------- .../middleware/authinterceptormiddleware.go | 55 ++++++++++ .../cmd/api/internal/service/userService.go | 65 ++++++++++++ .../cmd/api/internal/svc/servicecontext.go | 5 + app/user/model/vars.go | 1 + common/ctxdata/ctxData.go | 32 +++++- 8 files changed, 222 insertions(+), 85 deletions(-) create mode 100644 app/user/cmd/api/internal/middleware/authinterceptormiddleware.go create mode 100644 app/user/cmd/api/internal/service/userService.go diff --git a/app/user/cmd/api/desc/query.api b/app/user/cmd/api/desc/query.api index 0ef2f5c..feda57c 100644 --- a/app/user/cmd/api/desc/query.api +++ b/app/user/cmd/api/desc/query.api @@ -31,6 +31,7 @@ type QueryItem { @server ( prefix: api/v1 group: query + middleware: AuthInterceptor ) service main { @doc "query service agent" @@ -86,9 +87,6 @@ service main { @handler queryProvisionalOrder get /query/provisional_order/:id (QueryProvisionalOrderReq) returns (QueryProvisionalOrderResp) - @doc "查询示例" - @handler queryExample - get /query/example (QueryExampleReq) returns (QueryExampleResp) @doc "查询列表" @handler queryList @@ -204,6 +202,10 @@ service main { @handler queryDetail get /query/:id (QueryDetailReq) returns (QueryDetailResp) + @doc "查询示例" + @handler queryExample + get /query/example (QueryExampleReq) returns (QueryExampleResp) + } type QuerySingleTestReq { diff --git a/app/user/cmd/api/internal/handler/routes.go b/app/user/cmd/api/internal/handler/routes.go index 2e9210b..a276f61 100644 --- a/app/user/cmd/api/internal/handler/routes.go +++ b/app/user/cmd/api/internal/handler/routes.go @@ -241,19 +241,22 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) { ) server.AddRoutes( - []rest.Route{ - { - // query service agent - Method: http.MethodPost, - Path: "/query/service_agent/:product", - Handler: query.QueryServiceAgentHandler(serverCtx), - }, - { - Method: http.MethodPost, - Path: "/query/service_app/:product", - Handler: query.QueryServiceAppHandler(serverCtx), - }, - }, + rest.WithMiddlewares( + []rest.Middleware{serverCtx.AuthInterceptor}, + []rest.Route{ + { + // query service agent + Method: http.MethodPost, + Path: "/query/service_agent/:product", + Handler: query.QueryServiceAgentHandler(serverCtx), + }, + { + Method: http.MethodPost, + Path: "/query/service_app/:product", + Handler: query.QueryServiceAppHandler(serverCtx), + }, + }..., + ), rest.WithPrefix("/api/v1"), ) @@ -272,12 +275,6 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) { server.AddRoutes( []rest.Route{ - { - // 查询示例 - Method: http.MethodGet, - Path: "/query/example", - Handler: query.QueryExampleHandler(serverCtx), - }, { // 查询列表 Method: http.MethodGet, @@ -327,6 +324,12 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) { Path: "/query/:id", Handler: query.QueryDetailHandler(serverCtx), }, + { + // 查询示例 + Method: http.MethodGet, + Path: "/query/example", + Handler: query.QueryExampleHandler(serverCtx), + }, { Method: http.MethodPost, Path: "/query/single/test", diff --git a/app/user/cmd/api/internal/logic/query/queryservicelogic.go b/app/user/cmd/api/internal/logic/query/queryservicelogic.go index 0f663c5..7f7dbdb 100644 --- a/app/user/cmd/api/internal/logic/query/queryservicelogic.go +++ b/app/user/cmd/api/internal/logic/query/queryservicelogic.go @@ -2,12 +2,10 @@ package query import ( "context" - "database/sql" "encoding/hex" "encoding/json" "fmt" "qnc-server/app/user/cmd/api/internal/service" - "qnc-server/app/user/model" "qnc-server/common/ctxdata" jwtx "qnc-server/common/jwt" "qnc-server/common/xerr" @@ -17,7 +15,6 @@ import ( "github.com/pkg/errors" "github.com/zeromicro/go-zero/core/stores/redis" - "github.com/zeromicro/go-zero/core/stores/sqlx" "qnc-server/app/user/cmd/api/internal/svc" "qnc-server/app/user/cmd/api/internal/types" @@ -120,9 +117,9 @@ func (l *QueryServiceLogic) ProcessMarriageLogic(req *types.QueryServiceReq) (*t "id_card": data.IDCard, "mobile": data.Mobile, } - userID, err := l.GetOrCreateUser(data.Mobile) + userID, err := l.GetOrCreateUser() if err != nil { - return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 解密后的数据格式不正确: %v", err) + return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 处理用户失败: %v", err) } cacheNo, cacheDataErr := l.CacheData(params, "marriage", userID) if cacheDataErr != nil { @@ -180,9 +177,9 @@ func (l *QueryServiceLogic) ProcessHomeServiceLogic(req *types.QueryServiceReq) "id_card": data.IDCard, "mobile": data.Mobile, } - userID, err := l.GetOrCreateUser(data.Mobile) + userID, err := l.GetOrCreateUser() if err != nil { - return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 解密后的数据格式不正确: %v", err) + return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 处理用户失败: %v", err) } cacheNo, cacheDataErr := l.CacheData(params, "homeservice", userID) if cacheDataErr != nil { @@ -241,9 +238,9 @@ func (l *QueryServiceLogic) ProcessRiskAssessmentLogic(req *types.QueryServiceRe "id_card": data.IDCard, "mobile": data.Mobile, } - userID, err := l.GetOrCreateUser(data.Mobile) + userID, err := l.GetOrCreateUser() if err != nil { - return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 解密后的数据格式不正确: %v", err) + return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 处理用户失败: %v", err) } cacheNo, cacheDataErr := l.CacheData(params, "riskassessment", userID) if cacheDataErr != nil { @@ -301,9 +298,9 @@ func (l *QueryServiceLogic) ProcessCompanyInfoLogic(req *types.QueryServiceReq) "id_card": data.IDCard, "mobile": data.Mobile, } - userID, err := l.GetOrCreateUser(data.Mobile) + userID, err := l.GetOrCreateUser() if err != nil { - return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 解密后的数据格式不正确: %v", err) + return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 处理用户失败: %v", err) } cacheNo, cacheDataErr := l.CacheData(params, "companyinfo", userID) if cacheDataErr != nil { @@ -362,9 +359,9 @@ func (l *QueryServiceLogic) ProcessRentalInfoLogic(req *types.QueryServiceReq) ( "id_card": data.IDCard, "mobile": data.Mobile, } - userID, err := l.GetOrCreateUser(data.Mobile) + userID, err := l.GetOrCreateUser() if err != nil { - return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 解密后的数据格式不正确: %v", err) + return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 处理用户失败: %v", err) } cacheNo, cacheDataErr := l.CacheData(params, "rentalinfo", userID) if cacheDataErr != nil { @@ -423,9 +420,9 @@ func (l *QueryServiceLogic) ProcessPreLoanBackgroundCheckLogic(req *types.QueryS "id_card": data.IDCard, "mobile": data.Mobile, } - userID, err := l.GetOrCreateUser(data.Mobile) + userID, err := l.GetOrCreateUser() if err != nil { - return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 解密后的数据格式不正确: %v", err) + return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 处理用户失败: %v", err) } cacheNo, cacheDataErr := l.CacheData(params, "preloanbackgroundcheck", userID) if cacheDataErr != nil { @@ -483,9 +480,9 @@ func (l *QueryServiceLogic) ProcessBackgroundCheckLogic(req *types.QueryServiceR "id_card": data.IDCard, "mobile": data.Mobile, } - userID, err := l.GetOrCreateUser(data.Mobile) + userID, err := l.GetOrCreateUser() if err != nil { - return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 解密后的数据格式不正确: %v", err) + return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 处理用户失败: %v", err) } cacheNo, cacheDataErr := l.CacheData(params, "backgroundcheck", userID) if cacheDataErr != nil { @@ -773,7 +770,7 @@ func (l *QueryServiceLogic) ProcessTocPhoneThreeElementsLogic(req *types.QuerySe func (l *QueryServiceLogic) ProcessTocPhoneTwoElementsLogic(req *types.QueryServiceReq) (*types.QueryServiceResp, error) { userID, getUidErr := ctxdata.GetUidFromCtx(l.ctx) if getUidErr != nil { - return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 获取用户信息失败, %+v", getUidErr) + return nil, getUidErr } // AES解密 @@ -1390,51 +1387,36 @@ func (l *QueryServiceLogic) CacheData(params map[string]interface{}, Product str return outTradeNo, nil } -func (l *QueryServiceLogic) GetOrCreateUser(mobile string) (int64, error) { - agentIdentifier, ok := l.ctx.Value("agentIdentifier").(string) - app, appOk := l.ctx.Value("app").(bool) - if (!ok || agentIdentifier == "") && (!appOk || app == false) { - // 不是代理查询 - userID, getUidErr := ctxdata.GetUidFromCtx(l.ctx) - if getUidErr != nil { - return 0, getUidErr - } - return userID, nil +// GetOrCreateUser 获取或创建用户 +// 1. 如果上下文中已有用户ID,直接返回 +// 2. 如果是代理查询或APP请求,创建新用户 +// 3. 其他情况返回未登录错误 +func (l *QueryServiceLogic) GetOrCreateUser() (int64, error) { + // 尝试获取用户ID + userID, err := ctxdata.GetUidFromCtx(l.ctx) + if err == nil { + return userID, nil // 已有用户ID,直接返回 } - userModel, err := l.svcCtx.UserModel.FindOneByMobile(l.ctx, sql.NullString{String: mobile, Valid: true}) - if err != nil && !errors.Is(err, model.ErrNotFound) { + // 如果不是未登录错误,说明是其他错误,直接返回 + if !ctxdata.IsNoUserIdError(err) { return 0, err } - // 没有则创建账号 - if userModel == nil { - userModel = &model.User{Mobile: sql.NullString{String: mobile, Valid: true}} - // if len(userModel.Nickname) == 0 { - // userModel.Nickname = mobile - // } - if transErr := l.svcCtx.UserModel.Trans(l.ctx, func(ctx context.Context, session sqlx.Session) error { - insertResult, userInsertErr := l.svcCtx.UserModel.Insert(ctx, session, userModel) - if userInsertErr != nil { - return userInsertErr - } - lastId, lastInsertIdErr := insertResult.LastInsertId() - if lastInsertIdErr != nil { - return lastInsertIdErr - } - userModel.Id = lastId - userAuth := new(model.UserAuth) - userAuth.UserId = lastId - userAuth.AuthKey = mobile - userAuth.AuthType = model.UserAuthTypeAgentPromote - if _, userAuthInsertErr := l.svcCtx.UserAuthModel.Insert(ctx, session, userAuth); userAuthInsertErr != nil { - return userAuthInsertErr - } - - return nil - }); transErr != nil { - return 0, transErr - } + // 检查是否是代理查询或APP请求 + isAgentQuery := false + if agentID, ok := l.ctx.Value("agentIdentifier").(string); ok && agentID != "" { + isAgentQuery = true } - return userModel.Id, nil + if app, ok := l.ctx.Value("app").(bool); ok && app { + isAgentQuery = true + } + + // 如果不是代理查询或APP请求,返回未登录错误 + if !isAgentQuery { + return 0, ctxdata.ErrNoUserIdInCtx + } + + // 创建新用户 + return l.svcCtx.UserService.RegisterUUIDUser(l.ctx) } diff --git a/app/user/cmd/api/internal/middleware/authinterceptormiddleware.go b/app/user/cmd/api/internal/middleware/authinterceptormiddleware.go new file mode 100644 index 0000000..186c164 --- /dev/null +++ b/app/user/cmd/api/internal/middleware/authinterceptormiddleware.go @@ -0,0 +1,55 @@ +package middleware + +import ( + "context" + "net/http" + "qnc-server/app/user/cmd/api/internal/config" + "qnc-server/common/ctxdata" + jwtx "qnc-server/common/jwt" + "qnc-server/common/xerr" + + "github.com/pkg/errors" + "github.com/zeromicro/go-zero/rest/httpx" +) + +const ( + // 定义错误码 + ErrCodeUnauthorized = 401 +) + +type AuthInterceptorMiddleware struct { + Config config.Config +} + +func NewAuthInterceptorMiddleware(c config.Config) *AuthInterceptorMiddleware { + return &AuthInterceptorMiddleware{ + Config: c, + } +} + +func (m *AuthInterceptorMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + // 从请求头中获取Authorization字段 + authHeader := r.Header.Get("Authorization") + + // 如果没有Authorization头,直接放行 + if authHeader == "" { + next(w, r) + return + } + + // 解析JWT令牌 + userId, err := jwtx.ParseJwtToken(authHeader, m.Config.JwtAuth.AccessSecret) + if err != nil { + // JWT解析失败,返回401错误 + httpx.Error(w, errors.Wrapf(xerr.NewErrCode(ErrCodeUnauthorized), "token解析失败: %v", err)) + return + } + + // 将用户ID添加到请求上下文 + ctx := context.WithValue(r.Context(), ctxdata.CtxKeyJwtUserId, userId) + + // 使用新的上下文继续处理请求 + next(w, r.WithContext(ctx)) + } +} diff --git a/app/user/cmd/api/internal/service/userService.go b/app/user/cmd/api/internal/service/userService.go new file mode 100644 index 0000000..b50f395 --- /dev/null +++ b/app/user/cmd/api/internal/service/userService.go @@ -0,0 +1,65 @@ +package service + +import ( + "context" + "qnc-server/app/user/model" + + "github.com/google/uuid" + "github.com/zeromicro/go-zero/core/stores/sqlx" +) + +type UserService struct { + userModel model.UserModel + userAuthModel model.UserAuthModel +} + +// NewUserService 创建UserService实例 +func NewUserService(userModel model.UserModel, userAuthModel model.UserAuthModel) *UserService { + return &UserService{ + userModel: userModel, + userAuthModel: userAuthModel, + } +} + +// GenerateUUIDUserId 生成UUID用户ID +func (s *UserService) GenerateUUIDUserId(ctx context.Context) (string, error) { + id := uuid.NewString() + return id, nil +} + +// RegisterUUIDUser 注册UUID用户,返回用户ID +func (s *UserService) RegisterUUIDUser(ctx context.Context) (int64, error) { + // 生成UUID + uuidStr, err := s.GenerateUUIDUserId(ctx) + if err != nil { + return 0, err + } + + var userId int64 + err = s.userModel.Trans(ctx, func(ctx context.Context, session sqlx.Session) error { + // 创建用户记录 + user := &model.User{} + result, err := s.userModel.Insert(ctx, session, user) + if err != nil { + return err + } + userId, err = result.LastInsertId() + if err != nil { + return err + } + + // 创建用户认证记录 + userAuth := &model.UserAuth{ + UserId: userId, + AuthType: model.UserAuthTypeUUID, + AuthKey: uuidStr, + } + _, err = s.userAuthModel.Insert(ctx, session, userAuth) + return err + }) + if err != nil { + return 0, err + } + + return userId, nil +} diff --git a/app/user/cmd/api/internal/svc/servicecontext.go b/app/user/cmd/api/internal/svc/servicecontext.go index 3c6c8c3..dd709fe 100644 --- a/app/user/cmd/api/internal/svc/servicecontext.go +++ b/app/user/cmd/api/internal/svc/servicecontext.go @@ -17,6 +17,7 @@ type ServiceContext struct { Config config.Config Redis *redis.Redis SourceInterceptor rest.Middleware + AuthInterceptor rest.Middleware UserModel model.UserModel UserAuthModel model.UserAuthModel ProductModel model.ProductModel @@ -52,6 +53,7 @@ type ServiceContext struct { AsynqService *service.AsynqService // 客户端 VerificationService *service.VerificationService AgentService *service.AgentService + UserService *service.UserService } func NewServiceContext(c config.Config) *ServiceContext { @@ -114,10 +116,12 @@ func NewServiceContext(c config.Config) *ServiceContext { agentCommissionDeductionModel, agentWalletModel, agentLinkModel, agentOrderModel, agentRewardsModel, agentMembershipConfigModel, agentMembershipRechargeOrderModel, agentMembershipUserConfigModel, agentProductConfigModel, agentPlatformDeductionModel, agentActiveStatModel, agentWithdrawalModel) + userService := service.NewUserService(userModel, userAuthModel) return &ServiceContext{ Config: c, Redis: redis.MustNewRedis(redisConf), SourceInterceptor: middleware.NewSourceInterceptorMiddleware().Handle, + AuthInterceptor: middleware.NewAuthInterceptorMiddleware(c).Handle, AlipayService: alipayService, WechatPayService: wechatPayService, ApplePayService: applePayService, @@ -153,6 +157,7 @@ func NewServiceContext(c config.Config) *ServiceContext { AgentActiveStatModel: agentActiveStatModel, AgentWithdrawalModel: agentWithdrawalModel, ExampleModel: exampleModel, + UserService: userService, } } diff --git a/app/user/model/vars.go b/app/user/model/vars.go index ce8b059..84ef412 100644 --- a/app/user/model/vars.go +++ b/app/user/model/vars.go @@ -16,6 +16,7 @@ var UserAuthTypeWxMini string = "wx_mini" var UserAuthTypeWxh5 string = "wx_h5" var UserAuthTypeAgentDirect string = "agent_direct" var UserAuthTypeAgentPromote string = "agent_promote" +var UserAuthTypeUUID string = "uuid" var AgentDeductionTypeCost string = "cost" var AgentDeductionTypePricing string = "pricing" diff --git a/common/ctxdata/ctxData.go b/common/ctxdata/ctxData.go index 7433137..ba3e4c6 100644 --- a/common/ctxdata/ctxData.go +++ b/common/ctxdata/ctxData.go @@ -3,23 +3,47 @@ package ctxdata import ( "context" "encoding/json" + "errors" "fmt" ) const CtxKeyJwtUserId = "userId" +// 定义错误类型 +var ( + ErrNoUserIdInCtx = errors.New("上下文中没有用户ID") // 未登录 + ErrInvalidUserId = errors.New("用户ID格式无效") // 数据异常 +) + // GetUidFromCtx 从 context 中获取用户 ID func GetUidFromCtx(ctx context.Context) (int64, error) { // 尝试从上下文中获取 jwtUserId - jsonUid, ok := ctx.Value(CtxKeyJwtUserId).(json.Number) - if !ok { - return 0, fmt.Errorf("无法获取用户 ID, CtxKeyJwtUserId = %v", CtxKeyJwtUserId) + value := ctx.Value(CtxKeyJwtUserId) + if value == nil { + return 0, ErrNoUserIdInCtx } + + // 尝试转换为 json.Number + jsonUid, ok := value.(json.Number) + if !ok { + return 0, fmt.Errorf("%w: 期望类型 json.Number, 实际类型 %T", ErrInvalidUserId, value) + } + // 转换为 int64 uid, err := jsonUid.Int64() if err != nil { - return 0, fmt.Errorf("用户 ID 转换失败: %v", err) + return 0, fmt.Errorf("%w: %v", ErrInvalidUserId, err) } return uid, nil } + +// IsNoUserIdError 判断是否是未登录错误 +func IsNoUserIdError(err error) bool { + return errors.Is(err, ErrNoUserIdInCtx) +} + +// IsInvalidUserIdError 判断是否是用户ID格式错误 +func IsInvalidUserIdError(err error) bool { + return errors.Is(err, ErrInvalidUserId) +}