From 2d3ca4c18e3e3fd37d77ca583b9961becbd52d83 Mon Sep 17 00:00:00 2001 From: liangzai <2440983361@qq.com> Date: Sat, 24 May 2025 14:26:20 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E4=BB=A3=E7=90=86=E5=AE=9E?= =?UTF-8?q?=E5=90=8D=E8=AE=A4=E8=AF=81=E5=92=8C=E6=8E=88=E6=9D=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/user/cmd/api/desc/agent.api | 15 +- app/user/cmd/api/desc/pay.api | 13 + app/user/cmd/api/desc/query.api | 18 + app/user/cmd/api/desc/user.api | 49 +- app/user/cmd/api/etc/main.dev.yaml | 8 +- app/user/cmd/api/etc/main.yaml | 6 + app/user/cmd/api/internal/config/config.go | 8 + .../handler/agent/agentrealnamehandler.go | 29 ++ .../auth/getfaceverifyresulthandler.go | 29 ++ .../handler/auth/initfaceverifyhandler.go | 29 ++ .../auth/rejectauthorizationhandler.go | 29 ++ .../handler/pay/querypaymentcheckhandler.go | 29 ++ .../handler/query/confirmquerystatehandler.go | 29 ++ app/user/cmd/api/internal/handler/routes.go | 38 +- .../logic/agent/agentrealnamelogic.go | 99 ++++ .../logic/agent/agentwithdrawallogic.go | 17 +- .../logic/agent/applyforagentlogic.go | 3 - .../internal/logic/agent/getagentinfologic.go | 14 +- .../logic/auth/getfaceverifyresultlogic.go | 122 +++++ .../logic/auth/initfaceverifylogic.go | 134 ++++++ .../logic/auth/rejectauthorizationlogic.go | 30 ++ .../internal/logic/pay/alipaycallbacklogic.go | 50 +- .../api/internal/logic/pay/paymentlogic.go | 58 ++- .../logic/pay/querypaymentchecklogic.go | 100 ++++ .../logic/pay/wechatpaycallbacklogic.go | 46 +- .../logic/query/confirmquerystatelogic.go | 62 +++ .../internal/logic/query/querylistlogic.go | 67 ++- .../internal/logic/query/queryservicelogic.go | 20 +- .../api/internal/queue/paySuccessNotify.go | 211 +-------- .../cmd/api/internal/svc/servicecontext.go | 27 +- app/user/cmd/api/internal/types/types.go | 86 +++- app/user/model/agentAuditModel_gen.go | 15 +- app/user/model/agentModel_gen.go | 37 +- app/user/model/agentRealNameModel.go | 27 ++ app/user/model/agentRealNameModel_gen.go | 411 ++++++++++++++++ app/user/model/authorizationFaceModel.go | 27 ++ app/user/model/authorizationFaceModel_gen.go | 439 +++++++++++++++++ app/user/model/authorizationModel.go | 27 ++ app/user/model/authorizationModel_gen.go | 412 ++++++++++++++++ app/user/model/vars.go | 24 + common/jwt/jwtx_test.go | 110 +++++ deploy/script/README.md | 163 +++++++ deploy/script/codegen.js | 444 ++++++++++++++++++ deploy/script/config.json | 46 ++ deploy/script/encrypt_mobile.go | 102 ---- deploy/script/gen_api.js | 108 +++++ deploy/script/gen_models.js | 172 +++++++ deploy/script/gen_models.ps1 | 119 ++++- deploy/script/package.json | 26 + deploy/sql/agent_real_name.sql | 51 ++ go.mod | 21 +- go.sum | 39 +- pkg/core/aliyun/cloudauth/client.go | 178 +++++++ pkg/lzkit/crypto/crypto_test.go | 31 ++ 54 files changed, 4069 insertions(+), 435 deletions(-) create mode 100644 app/user/cmd/api/internal/handler/agent/agentrealnamehandler.go create mode 100644 app/user/cmd/api/internal/handler/auth/getfaceverifyresulthandler.go create mode 100644 app/user/cmd/api/internal/handler/auth/initfaceverifyhandler.go create mode 100644 app/user/cmd/api/internal/handler/auth/rejectauthorizationhandler.go create mode 100644 app/user/cmd/api/internal/handler/pay/querypaymentcheckhandler.go create mode 100644 app/user/cmd/api/internal/handler/query/confirmquerystatehandler.go create mode 100644 app/user/cmd/api/internal/logic/agent/agentrealnamelogic.go create mode 100644 app/user/cmd/api/internal/logic/auth/getfaceverifyresultlogic.go create mode 100644 app/user/cmd/api/internal/logic/auth/initfaceverifylogic.go create mode 100644 app/user/cmd/api/internal/logic/auth/rejectauthorizationlogic.go create mode 100644 app/user/cmd/api/internal/logic/pay/querypaymentchecklogic.go create mode 100644 app/user/cmd/api/internal/logic/query/confirmquerystatelogic.go create mode 100644 app/user/model/agentRealNameModel.go create mode 100644 app/user/model/agentRealNameModel_gen.go create mode 100644 app/user/model/authorizationFaceModel.go create mode 100644 app/user/model/authorizationFaceModel_gen.go create mode 100644 app/user/model/authorizationModel.go create mode 100644 app/user/model/authorizationModel_gen.go create mode 100644 common/jwt/jwtx_test.go create mode 100644 deploy/script/README.md create mode 100644 deploy/script/codegen.js create mode 100644 deploy/script/config.json delete mode 100644 deploy/script/encrypt_mobile.go create mode 100644 deploy/script/gen_api.js create mode 100644 deploy/script/gen_models.js create mode 100644 deploy/script/package.json create mode 100644 deploy/sql/agent_real_name.sql create mode 100644 pkg/core/aliyun/cloudauth/client.go create mode 100644 pkg/lzkit/crypto/crypto_test.go diff --git a/app/user/cmd/api/desc/agent.api b/app/user/cmd/api/desc/agent.api index d062537..b0ae6c0 100644 --- a/app/user/cmd/api/desc/agent.api +++ b/app/user/cmd/api/desc/agent.api @@ -65,6 +65,9 @@ service main { // 下级贡献详情 @handler GetAgentSubordinateContributionDetail get /subordinate/contribution/detail (GetAgentSubordinateContributionDetailReq) returns (GetAgentSubordinateContributionDetailResp) + + @handler AgentRealName + post /real_name (AgentRealNameReq) returns (AgentRealNameResp) } type ( @@ -75,8 +78,8 @@ type ( level string `json:"level"` region string `json:"region"` mobile string `json:"mobile"` - wechatID string `json:"wechat_id"` expiryTime string `json:"expiry_time"` + isRealName bool `json:"is_real_name"` } // 查询代理申请状态响应 AgentAuditStatusResp { @@ -150,6 +153,15 @@ type ( DescendantWithdrawCount int64 `json:"descendant_withdraw_count"` // 下级提现次数 DescendantWithdrawAmount float64 `json:"descendant_withdraw_amount"` // 下级提现总额 } + AgentRealNameReq { + Name string `json:"name"` + IDCard string `json:"id_card"` + Mobile string `json:"mobile"` + Code string `json:"code"` + } + AgentRealNameResp { + Status string `json:"status"` + } ) @server ( @@ -331,7 +343,6 @@ type ( AgentApplyReq { Region string `json:"region"` Mobile string `json:"mobile"` - WechatID string `json:"wechat_id"` Code string `json:"code"` Ancestor string `json:"ancestor,optional"` } diff --git a/app/user/cmd/api/desc/pay.api b/app/user/cmd/api/desc/pay.api index 5f363ab..4ade5c2 100644 --- a/app/user/cmd/api/desc/pay.api +++ b/app/user/cmd/api/desc/pay.api @@ -42,6 +42,9 @@ service main { @handler PaymentCheck post /pay/check (PaymentCheckReq) returns (PaymentCheckResp) + + @handler QueryPaymentCheck + post /pay/query_check (QueryPaymentCheckReq) returns (QueryPaymentCheckResp) } type ( @@ -62,6 +65,16 @@ type ( Type string `json:"type"` Status string `json:"status"` } + QueryPaymentCheckReq { + OrderNo string `json:"order_no" validate:"required"` + } + QueryPaymentCheckResp { + OrderStatus string `json:"order_status"` + AuthorizationStatus string `json:"authorization_status"` + Name string `json:"name"` + IdCard string `json:"id_card"` + ProductName string `json:"product_name"` + } ) type ( diff --git a/app/user/cmd/api/desc/query.api b/app/user/cmd/api/desc/query.api index feda57c..ac536c0 100644 --- a/app/user/cmd/api/desc/query.api +++ b/app/user/cmd/api/desc/query.api @@ -20,6 +20,9 @@ type Query { CreateTime string `json:"create_time"` // 创建时间 UpdateTime string `json:"update_time"` // 更新时间 QueryState string `json:"query_state"` // 查询状态 + IsPaid bool `json:"is_paid"` // 是否支付 + IsQueryCompleted bool `json:"is_query_completed"` // 查询是否完成 + IsAuthCompleted bool `json:"is_auth_completed"` // 授权是否完成 } @@ -107,6 +110,10 @@ service main { @doc "更新查询数据" @handler updateQueryData post /query/update_data (UpdateQueryDataReq) returns (UpdateQueryDataResp) + + @doc "确认查询状态" + @handler confirmQueryState + post /query/confirm_state (ConfirmQueryStateReq) returns (ConfirmQueryStateResp) } // 获取查询临时订单 @@ -218,3 +225,14 @@ type QuerySingleTestResp { Api string `json:"api"` } +// 新增接口的请求、响应类型定义 +type ( + ConfirmQueryStateReq { + OrderID int64 `json:"order_id" validate:"required"` + } + ConfirmQueryStateResp { + OrderState string `json:"order_state"` // 查询状态,例如"pending"、"success"、"failed"等 + QueryState string `json:"query_state"` + } +) + diff --git a/app/user/cmd/api/desc/user.api b/app/user/cmd/api/desc/user.api index bdc4e04..2831ade 100644 --- a/app/user/cmd/api/desc/user.api +++ b/app/user/cmd/api/desc/user.api @@ -146,22 +146,63 @@ type ( //============================> auth v1 <============================ @server ( - prefix: api/v1 + prefix: api/v1/auth group: auth ) service main { @doc "get mobile verify code" @handler sendSms - post /auth/sendSms (sendSmsReq) + post /sendSms (sendSmsReq) + + @doc "发起人脸认证" + @handler initFaceVerify + post /face/init (InitFaceVerifyReq) returns (InitFaceVerifyResp) + + @doc "查询人脸认证结果" + @handler getFaceVerifyResult + post /face/result (GetFaceVerifyResultReq) returns (GetFaceVerifyResultResp) + + @doc "第三方拒绝授权" + @handler rejectAuthorization + post /rejectAuthorization (RejectAuthorizationReq) returns (RejectAuthorizationResp) } type ( sendSmsReq { Mobile string `json:"mobile" validate:"required,mobile"` - ActionType string `json:"actionType" validate:"required,oneof=login register query agentApply bindMobile"` + ActionType string `json:"actionType" validate:"required,oneof=login register query agentApply bindMobile realName"` + } + + // 发起人脸认证请求 + InitFaceVerifyReq { + MetaInfo string `json:"meta_info" validate:"required"` // H5端获取的MetaInfo,JSON格式 + OrderNo string `json:"order_no" validate:"required"` // 订单号 + AuthType int64 `json:"auth_type" validate:"required"` // 认证类型 + } + + // 发起人脸认证响应 + InitFaceVerifyResp { + CertifyId string `json:"certify_id"` // 认证ID + CertifyUrl string `json:"certify_url"` // 跳转认证页面URL + } + + // 查询人脸认证结果请求 + GetFaceVerifyResultReq { + CertifyId string `json:"certify_id" validate:"required"` // 认证ID + } + + // 查询人脸认证结果响应 + GetFaceVerifyResultResp { + Passed bool `json:"passed"` // 是否通过 + OrderID int64 `json:"order_id"` + AuthType int64 `json:"auth_type"` + } + RejectAuthorizationReq { + OrderNo string `json:"order_no" validate:"required"` // 订单号 + } + RejectAuthorizationResp { } ) - //============================> notification v1 <============================ @server ( prefix: api/v1 diff --git a/app/user/cmd/api/etc/main.dev.yaml b/app/user/cmd/api/etc/main.dev.yaml index c850489..76a67d1 100644 --- a/app/user/cmd/api/etc/main.dev.yaml +++ b/app/user/cmd/api/etc/main.dev.yaml @@ -37,7 +37,7 @@ Alipay: AlipayRootCertPath: "etc/merchant/alipayRootCert.crt" IsProduction: true NotifyUrl: "https://6m4685017o.goho.co/api/v1/pay/alipay/callback" - ReturnURL: "http://192.168.10.13:5679/payment/result" + ReturnURL: "http://192.168.10.16:5679/authorization" Wxpay: AppID: "wx442ee1ac1ee75917" @@ -64,3 +64,9 @@ SystemConfig: WechatH5: AppID: "wx442ee1ac1ee75917" AppSecret: "c80474909db42f63913b7a307b3bee17" +CloudAuth: + AccessKeyId: "LTAI5tSWnaq1kvUawsV4ayL8" + AccessKeySecret: "fSGdUzm4TGtkus9rUVzipSOhEtimG5" + Endpoint: "cloudauth.aliyuncs.com" + SceneId: 1000013341 + ReturnUrl: "https://www.quannengcha.com/authorization/result" diff --git a/app/user/cmd/api/etc/main.yaml b/app/user/cmd/api/etc/main.yaml index eea3949..3371e03 100644 --- a/app/user/cmd/api/etc/main.yaml +++ b/app/user/cmd/api/etc/main.yaml @@ -65,3 +65,9 @@ SystemConfig: WechatH5: AppID: "wx442ee1ac1ee75917" AppSecret: "c80474909db42f63913b7a307b3bee17" +CloudAuth: + AccessKeyId: "LTAI5tSWnaq1kvUawsV4ayL8" + AccessKeySecret: "fSGdUzm4TGtkus9rUVzipSOhEtimG5" + Endpoint: "cloudauth.aliyuncs.com" + SceneId: 1000013341 + ReturnUrl: "https://www.quannengcha.com/authorization/face/result" diff --git a/app/user/cmd/api/internal/config/config.go b/app/user/cmd/api/internal/config/config.go index aacc028..ec7b9f0 100644 --- a/app/user/cmd/api/internal/config/config.go +++ b/app/user/cmd/api/internal/config/config.go @@ -20,6 +20,7 @@ type Config struct { YushanConfig YushanConfig SystemConfig SystemConfig WechatH5 WechatH5Config + CloudAuth CloudAuthConfig } // JwtAuth 用于 JWT 鉴权配置 @@ -92,3 +93,10 @@ type WechatH5Config struct { AppID string AppSecret string } +type CloudAuthConfig struct { + AccessKeyId string + AccessKeySecret string + Endpoint string + SceneId int64 + ReturnUrl string +} diff --git a/app/user/cmd/api/internal/handler/agent/agentrealnamehandler.go b/app/user/cmd/api/internal/handler/agent/agentrealnamehandler.go new file mode 100644 index 0000000..ad8380b --- /dev/null +++ b/app/user/cmd/api/internal/handler/agent/agentrealnamehandler.go @@ -0,0 +1,29 @@ +package agent + +import ( + "net/http" + + "github.com/zeromicro/go-zero/rest/httpx" + "qnc-server/app/user/cmd/api/internal/logic/agent" + "qnc-server/app/user/cmd/api/internal/svc" + "qnc-server/app/user/cmd/api/internal/types" + "qnc-server/common/result" + "qnc-server/pkg/lzkit/validator" +) + +func AgentRealNameHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var req types.AgentRealNameReq + if err := httpx.Parse(r, &req); err != nil { + result.ParamErrorResult(r, w, err) + return + } + if err := validator.Validate(req); err != nil { + result.ParamValidateErrorResult(r, w, err) + return + } + l := agent.NewAgentRealNameLogic(r.Context(), svcCtx) + resp, err := l.AgentRealName(&req) + result.HttpResult(r, w, resp, err) + } +} diff --git a/app/user/cmd/api/internal/handler/auth/getfaceverifyresulthandler.go b/app/user/cmd/api/internal/handler/auth/getfaceverifyresulthandler.go new file mode 100644 index 0000000..afb2920 --- /dev/null +++ b/app/user/cmd/api/internal/handler/auth/getfaceverifyresulthandler.go @@ -0,0 +1,29 @@ +package auth + +import ( + "net/http" + + "github.com/zeromicro/go-zero/rest/httpx" + "qnc-server/app/user/cmd/api/internal/logic/auth" + "qnc-server/app/user/cmd/api/internal/svc" + "qnc-server/app/user/cmd/api/internal/types" + "qnc-server/common/result" + "qnc-server/pkg/lzkit/validator" +) + +func GetFaceVerifyResultHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var req types.GetFaceVerifyResultReq + if err := httpx.Parse(r, &req); err != nil { + result.ParamErrorResult(r, w, err) + return + } + if err := validator.Validate(req); err != nil { + result.ParamValidateErrorResult(r, w, err) + return + } + l := auth.NewGetFaceVerifyResultLogic(r.Context(), svcCtx) + resp, err := l.GetFaceVerifyResult(&req) + result.HttpResult(r, w, resp, err) + } +} diff --git a/app/user/cmd/api/internal/handler/auth/initfaceverifyhandler.go b/app/user/cmd/api/internal/handler/auth/initfaceverifyhandler.go new file mode 100644 index 0000000..c74f236 --- /dev/null +++ b/app/user/cmd/api/internal/handler/auth/initfaceverifyhandler.go @@ -0,0 +1,29 @@ +package auth + +import ( + "net/http" + + "github.com/zeromicro/go-zero/rest/httpx" + "qnc-server/app/user/cmd/api/internal/logic/auth" + "qnc-server/app/user/cmd/api/internal/svc" + "qnc-server/app/user/cmd/api/internal/types" + "qnc-server/common/result" + "qnc-server/pkg/lzkit/validator" +) + +func InitFaceVerifyHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var req types.InitFaceVerifyReq + if err := httpx.Parse(r, &req); err != nil { + result.ParamErrorResult(r, w, err) + return + } + if err := validator.Validate(req); err != nil { + result.ParamValidateErrorResult(r, w, err) + return + } + l := auth.NewInitFaceVerifyLogic(r.Context(), svcCtx) + resp, err := l.InitFaceVerify(&req) + result.HttpResult(r, w, resp, err) + } +} diff --git a/app/user/cmd/api/internal/handler/auth/rejectauthorizationhandler.go b/app/user/cmd/api/internal/handler/auth/rejectauthorizationhandler.go new file mode 100644 index 0000000..2c700e6 --- /dev/null +++ b/app/user/cmd/api/internal/handler/auth/rejectauthorizationhandler.go @@ -0,0 +1,29 @@ +package auth + +import ( + "net/http" + + "github.com/zeromicro/go-zero/rest/httpx" + "qnc-server/app/user/cmd/api/internal/logic/auth" + "qnc-server/app/user/cmd/api/internal/svc" + "qnc-server/app/user/cmd/api/internal/types" + "qnc-server/common/result" + "qnc-server/pkg/lzkit/validator" +) + +func RejectAuthorizationHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var req types.RejectAuthorizationReq + if err := httpx.Parse(r, &req); err != nil { + result.ParamErrorResult(r, w, err) + return + } + if err := validator.Validate(req); err != nil { + result.ParamValidateErrorResult(r, w, err) + return + } + l := auth.NewRejectAuthorizationLogic(r.Context(), svcCtx) + resp, err := l.RejectAuthorization(&req) + result.HttpResult(r, w, resp, err) + } +} diff --git a/app/user/cmd/api/internal/handler/pay/querypaymentcheckhandler.go b/app/user/cmd/api/internal/handler/pay/querypaymentcheckhandler.go new file mode 100644 index 0000000..4ddb776 --- /dev/null +++ b/app/user/cmd/api/internal/handler/pay/querypaymentcheckhandler.go @@ -0,0 +1,29 @@ +package pay + +import ( + "net/http" + + "github.com/zeromicro/go-zero/rest/httpx" + "qnc-server/app/user/cmd/api/internal/logic/pay" + "qnc-server/app/user/cmd/api/internal/svc" + "qnc-server/app/user/cmd/api/internal/types" + "qnc-server/common/result" + "qnc-server/pkg/lzkit/validator" +) + +func QueryPaymentCheckHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var req types.QueryPaymentCheckReq + if err := httpx.Parse(r, &req); err != nil { + result.ParamErrorResult(r, w, err) + return + } + if err := validator.Validate(req); err != nil { + result.ParamValidateErrorResult(r, w, err) + return + } + l := pay.NewQueryPaymentCheckLogic(r.Context(), svcCtx) + resp, err := l.QueryPaymentCheck(&req) + result.HttpResult(r, w, resp, err) + } +} diff --git a/app/user/cmd/api/internal/handler/query/confirmquerystatehandler.go b/app/user/cmd/api/internal/handler/query/confirmquerystatehandler.go new file mode 100644 index 0000000..381a050 --- /dev/null +++ b/app/user/cmd/api/internal/handler/query/confirmquerystatehandler.go @@ -0,0 +1,29 @@ +package query + +import ( + "net/http" + + "github.com/zeromicro/go-zero/rest/httpx" + "qnc-server/app/user/cmd/api/internal/logic/query" + "qnc-server/app/user/cmd/api/internal/svc" + "qnc-server/app/user/cmd/api/internal/types" + "qnc-server/common/result" + "qnc-server/pkg/lzkit/validator" +) + +func ConfirmQueryStateHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var req types.ConfirmQueryStateReq + if err := httpx.Parse(r, &req); err != nil { + result.ParamErrorResult(r, w, err) + return + } + if err := validator.Validate(req); err != nil { + result.ParamValidateErrorResult(r, w, err) + return + } + l := query.NewConfirmQueryStateLogic(r.Context(), svcCtx) + resp, err := l.ConfirmQueryState(&req) + result.HttpResult(r, w, resp, err) + } +} diff --git a/app/user/cmd/api/internal/handler/routes.go b/app/user/cmd/api/internal/handler/routes.go index a276f61..b44c940 100644 --- a/app/user/cmd/api/internal/handler/routes.go +++ b/app/user/cmd/api/internal/handler/routes.go @@ -40,6 +40,11 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) { Path: "/product_config", Handler: agent.GetAgentProductConfigHandler(serverCtx), }, + { + Method: http.MethodPost, + Path: "/real_name", + Handler: agent.AgentRealNameHandler(serverCtx), + }, { Method: http.MethodGet, Path: "/subordinate/contribution/detail", @@ -144,14 +149,32 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) { server.AddRoutes( []rest.Route{ + { + // 发起人脸认证 + Method: http.MethodPost, + Path: "/face/init", + Handler: auth.InitFaceVerifyHandler(serverCtx), + }, + { + // 查询人脸认证结果 + Method: http.MethodPost, + Path: "/face/result", + Handler: auth.GetFaceVerifyResultHandler(serverCtx), + }, + { + // 第三方拒绝授权 + Method: http.MethodPost, + Path: "/rejectAuthorization", + Handler: auth.RejectAuthorizationHandler(serverCtx), + }, { // get mobile verify code Method: http.MethodPost, - Path: "/auth/sendSms", + Path: "/sendSms", Handler: auth.SendSmsHandler(serverCtx), }, }, - rest.WithPrefix("/api/v1"), + rest.WithPrefix("/api/v1/auth"), ) server.AddRoutes( @@ -206,6 +229,11 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) { Path: "/pay/payment", Handler: pay.PaymentHandler(serverCtx), }, + { + Method: http.MethodPost, + Path: "/pay/query_check", + Handler: pay.QueryPaymentCheckHandler(serverCtx), + }, }..., ), rest.WithJwt(serverCtx.Config.JwtAuth.AccessSecret), @@ -275,6 +303,12 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) { server.AddRoutes( []rest.Route{ + { + // 确认查询状态 + Method: http.MethodPost, + Path: "/query/confirm_state", + Handler: query.ConfirmQueryStateHandler(serverCtx), + }, { // 查询列表 Method: http.MethodGet, diff --git a/app/user/cmd/api/internal/logic/agent/agentrealnamelogic.go b/app/user/cmd/api/internal/logic/agent/agentrealnamelogic.go new file mode 100644 index 0000000..516fde8 --- /dev/null +++ b/app/user/cmd/api/internal/logic/agent/agentrealnamelogic.go @@ -0,0 +1,99 @@ +package agent + +import ( + "context" + "database/sql" + "fmt" + "time" + + "qnc-server/app/user/cmd/api/internal/service" + "qnc-server/app/user/cmd/api/internal/svc" + "qnc-server/app/user/cmd/api/internal/types" + "qnc-server/app/user/model" + "qnc-server/common/ctxdata" + "qnc-server/common/xerr" + "qnc-server/pkg/lzkit/crypto" + + "github.com/pkg/errors" + "github.com/zeromicro/go-zero/core/logx" + "github.com/zeromicro/go-zero/core/stores/redis" +) + +type AgentRealNameLogic struct { + logx.Logger + ctx context.Context + svcCtx *svc.ServiceContext +} + +func NewAgentRealNameLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AgentRealNameLogic { + return &AgentRealNameLogic{ + Logger: logx.WithContext(ctx), + ctx: ctx, + svcCtx: svcCtx, + } +} + +func (l *AgentRealNameLogic) AgentRealName(req *types.AgentRealNameReq) (resp *types.AgentRealNameResp, err error) { + userID, err := ctxdata.GetUidFromCtx(l.ctx) + if err != nil { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取用户ID失败, %v", err) + } + secretKey := l.svcCtx.Config.Encrypt.SecretKey + encryptedMobile, err := crypto.EncryptMobile(req.Mobile, secretKey) + if err != nil { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "代理实名, 加密手机号失败: %v", err) + } + // 检查手机号是否在一分钟内已发送过验证码 + redisKey := fmt.Sprintf("%s:%s", "realName", encryptedMobile) + cacheCode, err := l.svcCtx.Redis.Get(redisKey) + if err != nil { + if errors.Is(err, redis.Nil) { + return nil, errors.Wrapf(xerr.NewErrMsg("验证码已过期"), "代理实名, 验证码过期: %s", encryptedMobile) + } + return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "代理实名, 读取验证码redis缓存失败, mobile: %s, err: %+v", encryptedMobile, err) + } + if cacheCode != req.Code { + return nil, errors.Wrapf(xerr.NewErrMsg("验证码不正确"), "代理实名, 验证码不正确: %s", encryptedMobile) + } + agent, err := l.svcCtx.AgentModel.FindOneByUserId(l.ctx, userID) + if err != nil { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取代理信息失败, %v", err) + } + + agentRealName, err := l.svcCtx.AgentRealNameModel.FindOneByAgentId(l.ctx, agent.Id) + if err != nil && !errors.Is(err, model.ErrNotFound) { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取代理实名信息失败, %v", err) + } + + if agentRealName != nil && agentRealName.Status == model.AgentRealNameStatusApproved { + return nil, errors.Wrapf(xerr.NewErrMsg("代理实名信息已审核通过"), "代理实名信息已审核通过") + } + // 三要素验证 + threeVerification := service.ThreeFactorVerificationRequest{ + Name: req.Name, + IDCard: req.IDCard, + Mobile: req.Mobile, + } + verification, err := l.svcCtx.VerificationService.ThreeFactorVerification(threeVerification) + if err != nil { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "三要素验证失败: %v", err) + } + if !verification.Passed { + return nil, errors.Wrapf(xerr.NewErrCodeMsg(xerr.SERVER_COMMON_ERROR, verification.Err.Error()), "三要素验证不通过: %v", err) + } + agentRealName = &model.AgentRealName{ + AgentId: agent.Id, + Status: model.AgentRealNameStatusApproved, + Name: req.Name, + IdCard: req.IDCard, + ApproveTime: sql.NullTime{Time: time.Now(), Valid: true}, + } + _, err = l.svcCtx.AgentRealNameModel.Insert(l.ctx, nil, agentRealName) + if err != nil { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "添加代理实名信息失败, %v", err) + } + + return &types.AgentRealNameResp{ + Status: agentRealName.Status, + }, nil +} diff --git a/app/user/cmd/api/internal/logic/agent/agentwithdrawallogic.go b/app/user/cmd/api/internal/logic/agent/agentwithdrawallogic.go index 787e99c..5eb9af6 100644 --- a/app/user/cmd/api/internal/logic/agent/agentwithdrawallogic.go +++ b/app/user/cmd/api/internal/logic/agent/agentwithdrawallogic.go @@ -60,11 +60,22 @@ func (l *AgentWithdrawalLogic) AgentWithdrawal(req *types.WithdrawalReq) (*types if err != nil { return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取用户ID失败: %v", err) } - - // 查询代理信息 agentModel, err := l.svcCtx.AgentModel.FindOneByUserId(l.ctx, userID) if err != nil { - return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询代理信息失败: %v", err) + return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询代理信息失败: %v", err) + } + agentRealName, err := l.svcCtx.AgentRealNameModel.FindOneByAgentId(l.ctx, agentModel.Id) + if err != nil { + if errors.Is(err, model.ErrNotFound) { + return errors.Wrapf(xerr.NewErrMsg("您未进行实名认证, 无法提现"), "您未进行实名认证") + } + return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询代理实名信息失败: %v", err) + } + if agentRealName.Status != model.AgentRealNameStatusApproved { + return errors.Wrapf(xerr.NewErrMsg("您的实名认证未通过, 无法提现"), "您的实名认证未通过") + } + if agentRealName.Name != req.PayeeName { + return errors.Wrapf(xerr.NewErrMsg("您的实名认证信息不匹配, 无法提现"), "您的实名认证信息不匹配") } // 查询钱包 diff --git a/app/user/cmd/api/internal/logic/agent/applyforagentlogic.go b/app/user/cmd/api/internal/logic/agent/applyforagentlogic.go index 8f26b97..3a9a964 100644 --- a/app/user/cmd/api/internal/logic/agent/applyforagentlogic.go +++ b/app/user/cmd/api/internal/logic/agent/applyforagentlogic.go @@ -8,7 +8,6 @@ import ( jwtx "qnc-server/common/jwt" "qnc-server/common/xerr" "qnc-server/pkg/lzkit/crypto" - "qnc-server/pkg/lzkit/lzUtils" "time" "github.com/pkg/errors" @@ -108,7 +107,6 @@ func (l *ApplyForAgentLogic) ApplyForAgent(req *types.AgentApplyReq) (resp *type agentAudit.UserId = user.Id agentAudit.Mobile = encryptedMobile agentAudit.Region = req.Region - agentAudit.WechatId = lzUtils.StringToNullString(req.WechatID) agentAudit.Status = 1 _, insetAgentAuditErr := l.svcCtx.AgentAuditModel.Insert(transCtx, session, &agentAudit) if insetAgentAuditErr != nil { @@ -131,7 +129,6 @@ func (l *ApplyForAgentLogic) ApplyForAgent(req *types.AgentApplyReq) (resp *type agentModel.Mobile = agentAudit.Mobile agentModel.Region = agentAudit.Region agentModel.UserId = agentAudit.UserId - agentModel.WechatId = lzUtils.StringToNullString(req.WechatID) agentModelInsert, insertAgentModelErr := l.svcCtx.AgentModel.Insert(transCtx, session, &agentModel) if insertAgentModelErr != nil { return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "代理申请, 新增代理失败: %+v", insertAgentModelErr) diff --git a/app/user/cmd/api/internal/logic/agent/getagentinfologic.go b/app/user/cmd/api/internal/logic/agent/getagentinfologic.go index 6d9b2a6..96090a6 100644 --- a/app/user/cmd/api/internal/logic/agent/getagentinfologic.go +++ b/app/user/cmd/api/internal/logic/agent/getagentinfologic.go @@ -6,12 +6,12 @@ import ( "qnc-server/common/ctxdata" "qnc-server/common/xerr" "qnc-server/pkg/lzkit/crypto" - "qnc-server/pkg/lzkit/lzUtils" "github.com/pkg/errors" "qnc-server/app/user/cmd/api/internal/svc" "qnc-server/app/user/cmd/api/internal/types" + "qnc-server/app/user/model" "github.com/zeromicro/go-zero/core/logx" ) @@ -64,6 +64,16 @@ func (l *GetAgentInfoLogic) GetAgentInfo() (resp *types.AgentInfoResp, err error if err != nil { return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取代理信息, 解密手机号失败: %v", err) } + + IsRealName := false + agentRealName, err := l.svcCtx.AgentRealNameModel.FindOneByAgentId(l.ctx, agent.Id) + if err != nil && !errors.Is(err, model.ErrNotFound) { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取代理实名信息失败, %v", err) + } + if agentRealName != nil { + IsRealName = true + } + return &types.AgentInfoResp{ AgentID: agent.Id, Level: agent.LevelName, @@ -72,6 +82,6 @@ func (l *GetAgentInfoLogic) GetAgentInfo() (resp *types.AgentInfoResp, err error Region: agent.Region, Mobile: agent.Mobile, ExpiryTime: agent.MembershipExpiryTime.Time.Format("2006-01-02 15:04:05"), - WechatID: lzUtils.NullStringToString(agent.WechatId), + IsRealName: IsRealName, }, nil } diff --git a/app/user/cmd/api/internal/logic/auth/getfaceverifyresultlogic.go b/app/user/cmd/api/internal/logic/auth/getfaceverifyresultlogic.go new file mode 100644 index 0000000..c7b48fd --- /dev/null +++ b/app/user/cmd/api/internal/logic/auth/getfaceverifyresultlogic.go @@ -0,0 +1,122 @@ +package auth + +import ( + "context" + "fmt" + "qnc-server/common/xerr" + + "github.com/bytedance/sonic" + "github.com/pkg/errors" + + "qnc-server/app/user/cmd/api/internal/svc" + "qnc-server/app/user/cmd/api/internal/types" + "qnc-server/app/user/model" + + "github.com/zeromicro/go-zero/core/logx" + "github.com/zeromicro/go-zero/core/stores/sqlx" +) + +type GetFaceVerifyResultLogic struct { + logx.Logger + ctx context.Context + svcCtx *svc.ServiceContext +} + +func NewGetFaceVerifyResultLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetFaceVerifyResultLogic { + return &GetFaceVerifyResultLogic{ + Logger: logx.WithContext(ctx), + ctx: ctx, + svcCtx: svcCtx, + } +} + +func (l *GetFaceVerifyResultLogic) GetFaceVerifyResult(req *types.GetFaceVerifyResultReq) (resp *types.GetFaceVerifyResultResp, err error) { + // 1. 查询认证明细 + face, err := l.svcCtx.AuthorizationFaceModel.FindOneByCertifyId(l.ctx, req.CertifyId) + if err != nil { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询认证明细失败: %v", err) + } + if face == nil { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询认证明细为空") + } + + // 2. 查询主授权(如后续需要可用) + auth, err := l.svcCtx.AuthorizationModel.FindOne(l.ctx, face.AuthorizationId) + if err != nil { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询授权主表失败: %v", err) + } + + // 3. 判断人脸认证明细和主授权状态,若均已是"success",则直接返回通过 + if (face.Status == model.AuthorizationFaceStatusSuccess) && (auth.Status == model.AuthorizationStatusSuccess) { + return &types.GetFaceVerifyResultResp{ + OrderID: auth.OrderId, + Passed: true, + }, nil + } + + // 4. 调用阿里云接口查询认证结果 + describeResp, err := l.svcCtx.CloudAuthService.DescribeFaceVerify(req.CertifyId) + if err != nil { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "校验人脸识别结果失败: %v", err) + } + + // 5. 判断认证状态并更新(使用事务) + if describeResp.Passed { + // 使用事务更新状态 + err = l.svcCtx.AuthorizationModel.Trans(l.ctx, func(ctx context.Context, session sqlx.Session) error { + order, err := l.svcCtx.OrderModel.FindOne(ctx, auth.OrderId) + if err != nil { + return errors.Wrapf(err, "查询订单失败") + } + redisKey := fmt.Sprintf(types.QueryCacheKey, order.UserId, order.OrderNo) + cache, cacheErr := l.svcCtx.Redis.GetCtx(ctx, redisKey) + if cacheErr != nil { + return fmt.Errorf("获取缓存内容失败: %+v", cacheErr) + } + var data types.QueryCacheLoad + err = sonic.Unmarshal([]byte(cache), &data) + if err != nil { + return fmt.Errorf("解析缓存内容失败: %+v", err) + } + // 插入新queryModel + query := &model.Query{ + OrderId: auth.OrderId, + UserId: auth.UserId, + ProductId: order.ProductId, + QueryParams: data.Params, + QueryState: "pending", + } + _, insertQueryErr := l.svcCtx.QueryModel.Insert(ctx, session, query) + if insertQueryErr != nil { + return errors.Wrapf(insertQueryErr, "保存查询失败") + } + // 更新主授权状态 + auth.Status = model.AuthorizationStatusSuccess + _, err = l.svcCtx.AuthorizationModel.Update(ctx, session, auth) + if err != nil { + return errors.Wrapf(err, "更新授权状态失败") + } + // 更新人脸认证明细状态 + face.Status = model.AuthorizationFaceStatusSuccess + _, err = l.svcCtx.AuthorizationFaceModel.Update(ctx, session, face) + if err != nil { + return errors.Wrapf(err, "更新人脸认证状态失败") + } + if asyncErr := l.svcCtx.AsynqService.SendQueryTask(auth.OrderId); asyncErr != nil { + logx.Errorf("异步任务调度失败: %v", asyncErr) + return asyncErr + } + return nil + }) + if err != nil { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新认证状态失败: %v", err) + } + } + // 6. 返回resp(resp.Passed := describeResp.Passed)和err(如有) + resp = &types.GetFaceVerifyResultResp{ + OrderID: auth.OrderId, + Passed: describeResp.Passed, + AuthType: auth.AuthType.Int64, + } + return resp, nil +} diff --git a/app/user/cmd/api/internal/logic/auth/initfaceverifylogic.go b/app/user/cmd/api/internal/logic/auth/initfaceverifylogic.go new file mode 100644 index 0000000..06e4809 --- /dev/null +++ b/app/user/cmd/api/internal/logic/auth/initfaceverifylogic.go @@ -0,0 +1,134 @@ +package auth + +import ( + "context" + "database/sql" + "encoding/hex" + "math/rand" + "strconv" + "time" + + "qnc-server/app/user/cmd/api/internal/svc" + "qnc-server/app/user/cmd/api/internal/types" + "qnc-server/app/user/model" + "qnc-server/common/xerr" + "qnc-server/pkg/core/aliyun/cloudauth" + "qnc-server/pkg/lzkit/crypto" + + "github.com/pkg/errors" + "github.com/zeromicro/go-zero/core/logx" + "github.com/zeromicro/go-zero/core/stores/sqlx" +) + +type InitFaceVerifyLogic struct { + logx.Logger + ctx context.Context + svcCtx *svc.ServiceContext +} + +func NewInitFaceVerifyLogic(ctx context.Context, svcCtx *svc.ServiceContext) *InitFaceVerifyLogic { + return &InitFaceVerifyLogic{ + Logger: logx.WithContext(ctx), + ctx: ctx, + svcCtx: svcCtx, + } +} + +// 生成带前缀的随机OuterOrderNo(阿里云人脸验证单号) +func genOuterOrderNo() string { + prefix := "FACE_" + timestamp := time.Now().UnixNano() + randNum := rand.Intn(1000000) + return prefix + strconv.FormatInt(timestamp, 10) + strconv.Itoa(randNum) +} + +func (l *InitFaceVerifyLogic) InitFaceVerify(req *types.InitFaceVerifyReq) (resp *types.InitFaceVerifyResp, err error) { + order, err := l.svcCtx.OrderModel.FindOneByOrderNo(l.ctx, req.OrderNo) + if err != nil { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询订单失败: %v", err) + } + + if order.Status != "paid" { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "订单未支付") + } + + authorization, err := l.svcCtx.AuthorizationModel.FindOneByOrderId(l.ctx, order.Id) + if err != nil { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询授权信息失败: %v", err) + } + + if authorization.Status != "pending" { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "授权信息状态不正确") + } + key, decodeErr := hex.DecodeString(l.svcCtx.Config.Encrypt.SecretKey) + if decodeErr != nil { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询授权信息失败: %v", decodeErr) + } + if authorization.GrantType == "face" { + authorizationFaceBuilder := l.svcCtx.AuthorizationFaceModel.SelectBuilder(). + Where("authorization_id = ? AND status = ? AND certify_id != '' AND certify_url != ''", + authorization.Id, model.AuthorizationStatusPending). + OrderBy("create_time DESC"). + Limit(1) + existingFaces, err := l.svcCtx.AuthorizationFaceModel.FindAll(l.ctx, authorizationFaceBuilder, "") + if err != nil { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询人脸认证记录失败: %v", err) + } + if len(existingFaces) > 0 { + // 如果存在记录,直接返回最新的认证信息 + return &types.InitFaceVerifyResp{ + CertifyId: existingFaces[0].CertifyId, + CertifyUrl: existingFaces[0].CertifyUrl, + }, nil + } + + } + outerOrderNo := genOuterOrderNo() + name, err := crypto.AesDecrypt(authorization.TargetName, key) + if err != nil { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "解密姓名失败: %v", err) + } + idCard, err := crypto.AesDecrypt(authorization.TargetIdcard, key) + if err != nil { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "解密身份证号失败: %v", err) + } + initFaceVerifyResp, err := l.svcCtx.CloudAuthService.InitFaceVerify(cloudauth.InitFaceVerifyParam{ + OuterOrderNo: outerOrderNo, + CertName: string(name), + CertNo: string(idCard), + MetaInfo: req.MetaInfo, + }) + if err != nil { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "初始化人脸认证失败: %v", err) + } + err = l.svcCtx.AuthorizationModel.Trans(l.ctx, func(ctx context.Context, session sqlx.Session) error { + authorization.AuthType = sql.NullInt64{ + Int64: req.AuthType, + Valid: true, + } + _, err = l.svcCtx.AuthorizationModel.Update(l.ctx, session, authorization) + if err != nil { + return err + } + authorizationFace := &model.AuthorizationFace{ + AuthorizationId: authorization.Id, + CertifyId: initFaceVerifyResp.CertifyId, + CertifyUrl: initFaceVerifyResp.CertifyUrl, + Status: model.AuthorizationStatusPending, + CertifyUrlExpireAt: time.Now().Add(time.Minute * 30), + OuterOrderNo: outerOrderNo, + } + _, err = l.svcCtx.AuthorizationFaceModel.Insert(l.ctx, session, authorizationFace) + if err != nil { + return err + } + return nil + }) + if err != nil { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "更新授权信息失败: %v", err) + } + return &types.InitFaceVerifyResp{ + CertifyId: initFaceVerifyResp.CertifyId, + CertifyUrl: initFaceVerifyResp.CertifyUrl, + }, nil +} diff --git a/app/user/cmd/api/internal/logic/auth/rejectauthorizationlogic.go b/app/user/cmd/api/internal/logic/auth/rejectauthorizationlogic.go new file mode 100644 index 0000000..8fcbf50 --- /dev/null +++ b/app/user/cmd/api/internal/logic/auth/rejectauthorizationlogic.go @@ -0,0 +1,30 @@ +package auth + +import ( + "context" + + "qnc-server/app/user/cmd/api/internal/svc" + "qnc-server/app/user/cmd/api/internal/types" + + "github.com/zeromicro/go-zero/core/logx" +) + +type RejectAuthorizationLogic struct { + logx.Logger + ctx context.Context + svcCtx *svc.ServiceContext +} + +func NewRejectAuthorizationLogic(ctx context.Context, svcCtx *svc.ServiceContext) *RejectAuthorizationLogic { + return &RejectAuthorizationLogic{ + Logger: logx.WithContext(ctx), + ctx: ctx, + svcCtx: svcCtx, + } +} + +func (l *RejectAuthorizationLogic) RejectAuthorization(req *types.RejectAuthorizationReq) (resp *types.RejectAuthorizationResp, err error) { + // todo: add your logic here and delete this line + + return +} diff --git a/app/user/cmd/api/internal/logic/pay/alipaycallbacklogic.go b/app/user/cmd/api/internal/logic/pay/alipaycallbacklogic.go index 196de4f..3a36a5e 100644 --- a/app/user/cmd/api/internal/logic/pay/alipaycallbacklogic.go +++ b/app/user/cmd/api/internal/logic/pay/alipaycallbacklogic.go @@ -2,8 +2,11 @@ package pay import ( "context" + "encoding/hex" + "encoding/json" "fmt" "net/http" + "qnc-server/pkg/lzkit/crypto" "qnc-server/pkg/lzkit/lzUtils" "strings" "time" @@ -11,6 +14,7 @@ import ( "github.com/smartwalle/alipay/v3" "qnc-server/app/user/cmd/api/internal/svc" + "qnc-server/app/user/cmd/api/internal/types" "qnc-server/app/user/model" "github.com/zeromicro/go-zero/core/logx" @@ -95,9 +99,48 @@ func (l *AlipayCallbackLogic) handleQueryOrderPayment(w http.ResponseWriter, not } if order.Status == "paid" { - if asyncErr := l.svcCtx.AsynqService.SendQueryTask(order.Id); asyncErr != nil { - logx.Errorf("异步任务调度失败: %v", asyncErr) - return asyncErr + redisKey := fmt.Sprintf(types.QueryCacheKey, order.UserId, order.OrderNo) + cache, cacheErr := l.svcCtx.Redis.Get(redisKey) + if cacheErr != nil { + return fmt.Errorf("获取缓存内容失败: %+v", cacheErr) + } + var data types.QueryCacheLoad + err = json.Unmarshal([]byte(cache), &data) + if err != nil { + return fmt.Errorf("解析缓存内容失败: %+v", err) + } + secretKey := l.svcCtx.Config.Encrypt.SecretKey + key, decodeErr := hex.DecodeString(secretKey) + if decodeErr != nil { + return fmt.Errorf("获取AES密钥失败: %+v", decodeErr) + } + decryptData, aesdecryptErr := crypto.AesDecrypt(data.Params, key) + if aesdecryptErr != nil { + return fmt.Errorf("解密参数失败: %+v", aesdecryptErr) + } + var paramsMap map[string]string + if err := json.Unmarshal([]byte(decryptData), ¶msMap); err != nil { + return fmt.Errorf("解析参数失败: %+v", err) + } + encryptName, err := crypto.AesEncrypt([]byte(paramsMap["name"]), key) + if err != nil { + return fmt.Errorf("生成订单, 加密姓名失败: %+v", err) + } + encryptIdcard, err := crypto.AesEncrypt([]byte(paramsMap["id_card"]), key) + if err != nil { + return fmt.Errorf("生成订单, 加密身份证号失败: %+v", err) + } + _, err = l.svcCtx.AuthorizationModel.Insert(l.ctx, nil, &model.Authorization{ + OrderId: order.Id, + UserId: order.UserId, + TargetName: encryptName, + TargetIdcard: encryptIdcard, + GrantType: model.GrantTypeFace, + Status: model.AuthorizationStatusPending, + }) + if err != nil { + logx.Errorf("支付宝支付回调,插入授权信息失败: %+v", err) + return fmt.Errorf("插入授权信息失败: %+v", err) } } @@ -183,6 +226,7 @@ func (l *AlipayCallbackLogic) handleAgentVipOrderPayment(w http.ResponseWriter, return nil } +// 退款 func (l *AlipayCallbackLogic) handleRefund(order *model.AgentMembershipRechargeOrder) error { ctx := context.Background() // 退款 diff --git a/app/user/cmd/api/internal/logic/pay/paymentlogic.go b/app/user/cmd/api/internal/logic/pay/paymentlogic.go index 7758203..df9da44 100644 --- a/app/user/cmd/api/internal/logic/pay/paymentlogic.go +++ b/app/user/cmd/api/internal/logic/pay/paymentlogic.go @@ -5,6 +5,7 @@ import ( "encoding/hex" "encoding/json" "fmt" + "os" "qnc-server/app/user/cmd/api/internal/svc" "qnc-server/app/user/cmd/api/internal/types" "qnc-server/app/user/model" @@ -125,6 +126,14 @@ func (l *PaymentLogic) QueryOrderPayment(req *types.PaymentReq, session sqlx.Ses amount = 0.01 } var orderID int64 + Status := "pending" + env := os.Getenv("ENV") + if env == "" { + env = "production" + } + if env == "development" { + Status = "paid" + } order := model.Order{ OrderNo: outTradeNo, UserId: userID, @@ -132,7 +141,7 @@ func (l *PaymentLogic) QueryOrderPayment(req *types.PaymentReq, session sqlx.Ses PaymentPlatform: req.PayMethod, PaymentScene: "app", Amount: amount, - Status: "pending", + Status: Status, } orderInsertResult, insertOrderErr := l.svcCtx.OrderModel.Insert(l.ctx, session, &order) if insertOrderErr != nil { @@ -157,6 +166,53 @@ func (l *PaymentLogic) QueryOrderPayment(req *types.PaymentReq, session sqlx.Ses return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "生成订单, 保存代理订单失败: %+v", agentOrderInsert) } } + + if env == "development" { + redisKey := fmt.Sprintf(types.QueryCacheKey, order.UserId, order.OrderNo) + cache, cacheErr := l.svcCtx.Redis.Get(redisKey) + if cacheErr != nil { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成订单, 获取缓存内容失败: %+v", cacheErr) + } + var data types.QueryCacheLoad + err = json.Unmarshal([]byte(cache), &data) + if err != nil { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成订单, 解析缓存内容失败: %+v", err) + } + secretKey := l.svcCtx.Config.Encrypt.SecretKey + key, decodeErr := hex.DecodeString(secretKey) + if decodeErr != nil { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成订单, 获取AES密钥失败: %+v", decodeErr) + } + decryptData, aesdecryptErr := crypto.AesDecrypt(data.Params, key) + if aesdecryptErr != nil { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成订单, 解密参数失败: %+v", aesdecryptErr) + } + var paramsMap map[string]string + if err := json.Unmarshal([]byte(decryptData), ¶msMap); err != nil { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成订单, 解析参数失败: %+v", err) + } + + encryptName, err := crypto.AesEncrypt([]byte(paramsMap["name"]), key) + if err != nil { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成订单, 加密姓名失败: %+v", err) + } + encryptIdcard, err := crypto.AesEncrypt([]byte(paramsMap["id_card"]), key) + if err != nil { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成订单, 加密身份证号失败: %+v", err) + } + _, err = l.svcCtx.AuthorizationModel.Insert(l.ctx, nil, &model.Authorization{ + OrderId: orderID, + UserId: order.UserId, + TargetName: encryptName, + TargetIdcard: encryptIdcard, + GrantType: model.GrantTypeFace, + Status: model.AuthorizationStatusPending, + }) + if err != nil { + logx.Errorf("支付宝支付回调,插入授权信息失败: %+v", err) + return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成订单, 插入授权信息失败: %+v", err) + } + } return &PaymentTypeResp{amount: amount, outTradeNo: outTradeNo, description: product.ProductName}, nil } func (l *PaymentLogic) AgentVipOrderPayment(req *types.PaymentReq, session sqlx.Session) (resp *PaymentTypeResp, err error) { diff --git a/app/user/cmd/api/internal/logic/pay/querypaymentchecklogic.go b/app/user/cmd/api/internal/logic/pay/querypaymentchecklogic.go new file mode 100644 index 0000000..0f55322 --- /dev/null +++ b/app/user/cmd/api/internal/logic/pay/querypaymentchecklogic.go @@ -0,0 +1,100 @@ +package pay + +import ( + "context" + "encoding/hex" + "strings" + + "qnc-server/app/user/cmd/api/internal/svc" + "qnc-server/app/user/cmd/api/internal/types" + "qnc-server/common/xerr" + "qnc-server/pkg/lzkit/crypto" + + "github.com/pkg/errors" + "github.com/zeromicro/go-zero/core/logx" +) + +type QueryPaymentCheckLogic struct { + logx.Logger + ctx context.Context + svcCtx *svc.ServiceContext +} + +func NewQueryPaymentCheckLogic(ctx context.Context, svcCtx *svc.ServiceContext) *QueryPaymentCheckLogic { + return &QueryPaymentCheckLogic{ + Logger: logx.WithContext(ctx), + ctx: ctx, + svcCtx: svcCtx, + } +} + +func (l *QueryPaymentCheckLogic) QueryPaymentCheck(req *types.QueryPaymentCheckReq) (resp *types.QueryPaymentCheckResp, err error) { + order, err := l.svcCtx.OrderModel.FindOneByOrderNo(l.ctx, req.OrderNo) + if err != nil { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询订单失败: %v", err) + } + product, err := l.svcCtx.ProductModel.FindOne(l.ctx, order.ProductId) + if err != nil { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询产品失败: %v", err) + } + resp = new(types.QueryPaymentCheckResp) + resp.OrderStatus = order.Status + resp.ProductName = product.ProductName + + if order.Status == "paid" { + authorization, err := l.svcCtx.AuthorizationModel.FindOneByOrderId(l.ctx, order.Id) + if err != nil { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询授权信息失败: %v", err) + } + resp.AuthorizationStatus = authorization.Status + key, decodeErr := hex.DecodeString(l.svcCtx.Config.Encrypt.SecretKey) + if decodeErr != nil { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询授权信息失败: %v", decodeErr) + } + decryptName, err := crypto.AesDecrypt(authorization.TargetName, key) + if err != nil { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询授权信息失败: %v", err) + } + idCard, err := crypto.AesDecrypt(authorization.TargetIdcard, key) + if err != nil { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询授权信息失败: %v", err) + } + resp.Name = maskName(string(decryptName)) + resp.IdCard = maskIDCard(string(idCard)) + } + + return resp, nil +} + +// 姓名脱敏 +func maskName(name string) string { + // 将字符串转换为rune切片以正确处理中文字符 + runes := []rune(name) + length := len(runes) + + if length <= 1 { + return name + } + + if length == 2 { + // 两个字:保留第一个字,第二个字用*替代 + return string(runes[0]) + "*" + } + + // 三个字及以上:保留首尾字,中间用*替代 + first := string(runes[0]) + last := string(runes[length-1]) + mask := strings.Repeat("*", length-2) + + return first + mask + last +} + +// 身份证号脱敏 +func maskIDCard(idCard string) string { + length := len(idCard) + if length <= 10 { + return idCard // 如果长度太短,可能不是身份证,不处理 + } + // 保留前3位和后4位 + return idCard[:3] + strings.Repeat("*", length-7) + idCard[length-4:] +} diff --git a/app/user/cmd/api/internal/logic/pay/wechatpaycallbacklogic.go b/app/user/cmd/api/internal/logic/pay/wechatpaycallbacklogic.go index b46077b..ce1e474 100644 --- a/app/user/cmd/api/internal/logic/pay/wechatpaycallbacklogic.go +++ b/app/user/cmd/api/internal/logic/pay/wechatpaycallbacklogic.go @@ -2,10 +2,14 @@ package pay import ( "context" + "encoding/hex" + "encoding/json" "fmt" "net/http" "qnc-server/app/user/cmd/api/internal/service" + "qnc-server/app/user/cmd/api/internal/types" "qnc-server/app/user/model" + "qnc-server/pkg/lzkit/crypto" "qnc-server/pkg/lzkit/lzUtils" "strings" "time" @@ -197,15 +201,41 @@ func (l *WechatPayCallbackLogic) handleRefund(order *model.AgentMembershipRechar return refundErr } if refund.IsSuccess() { - logx.Errorf("支付宝退款成功, orderID: %d", order.Id) - // 更新订单状态为退款 - order.Status = "refunded" - updateOrderErr := l.svcCtx.AgentMembershipRechargeOrderModel.UpdateWithVersion(ctx, nil, order) - if updateOrderErr != nil { - logx.Errorf("更新订单状态失败,订单ID: %d, 错误: %v", order.Id, updateOrderErr) - return fmt.Errorf("更新订单状态失败: %v", updateOrderErr) + redisKey := fmt.Sprintf(types.QueryCacheKey, order.UserId, order.OrderNo) + cache, cacheErr := l.svcCtx.Redis.Get(redisKey) + if cacheErr != nil { + return fmt.Errorf("获取缓存内容失败: %+v", cacheErr) + } + var data types.QueryCacheLoad + err := json.Unmarshal([]byte(cache), &data) + if err != nil { + return fmt.Errorf("解析缓存内容失败: %+v", err) + } + secretKey := l.svcCtx.Config.Encrypt.SecretKey + key, decodeErr := hex.DecodeString(secretKey) + if decodeErr != nil { + return fmt.Errorf("获取AES密钥失败: %+v", decodeErr) + } + decryptData, aesdecryptErr := crypto.AesDecrypt(data.Params, key) + if aesdecryptErr != nil { + return fmt.Errorf("解密参数失败: %+v", aesdecryptErr) + } + var paramsMap map[string]string + if err := json.Unmarshal([]byte(decryptData), ¶msMap); err != nil { + return fmt.Errorf("解析参数失败: %+v", err) + } + _, err = l.svcCtx.AuthorizationModel.Insert(l.ctx, nil, &model.Authorization{ + OrderId: order.Id, + UserId: order.UserId, + TargetName: paramsMap["name"], + TargetIdcard: paramsMap["id_card"], + GrantType: model.GrantTypeFace, + Status: model.AuthorizationStatusPending, + }) + if err != nil { + logx.Errorf("支付宝支付回调,插入授权信息失败: %+v", err) + return fmt.Errorf("插入授权信息失败: %+v", err) } - return nil } else { logx.Errorf("支付宝退款失败:%v", refundErr) return refundErr diff --git a/app/user/cmd/api/internal/logic/query/confirmquerystatelogic.go b/app/user/cmd/api/internal/logic/query/confirmquerystatelogic.go new file mode 100644 index 0000000..411ad67 --- /dev/null +++ b/app/user/cmd/api/internal/logic/query/confirmquerystatelogic.go @@ -0,0 +1,62 @@ +package query + +import ( + "context" + "qnc-server/app/user/cmd/api/internal/svc" + "qnc-server/app/user/cmd/api/internal/types" + "qnc-server/app/user/model" + "qnc-server/common/xerr" + + "github.com/pkg/errors" + "github.com/zeromicro/go-zero/core/logx" +) + +type ConfirmQueryStateLogic struct { + logx.Logger + ctx context.Context + svcCtx *svc.ServiceContext +} + +func NewConfirmQueryStateLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ConfirmQueryStateLogic { + return &ConfirmQueryStateLogic{ + Logger: logx.WithContext(ctx), + ctx: ctx, + svcCtx: svcCtx, + } +} + +func (l *ConfirmQueryStateLogic) ConfirmQueryState(req *types.ConfirmQueryStateReq) (resp *types.ConfirmQueryStateResp, err error) { + // 1. 通过 order_id 查询订单,判断订单是否已支付 + order, err := l.svcCtx.OrderModel.FindOne(l.ctx, req.OrderID) + if err != nil { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询订单失败: %v", err) + } + if order == nil { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "订单不存在") + } + // 若订单未支付,则直接返回 OrderState := "pending", QueryState := "" + if order.Status != model.OrderStatusPaid { + return &types.ConfirmQueryStateResp{ + OrderState: order.Status, + QueryState: "", + }, nil + } + + // 2. 订单已支付,查询 query 状态(假设通过 order_id 查询 query 记录,例如使用 QueryModel.FindOneByOrderId) + query, err := l.svcCtx.QueryModel.FindOneByOrderId(l.ctx, req.OrderID) + if err != nil { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询 query 失败: %v", err) + } + if query == nil { + // 若 query 不存在,则返回 OrderState := "paid", QueryState := "" + return &types.ConfirmQueryStateResp{ + OrderState: order.Status, + QueryState: "", + }, nil + } + // 否则,返回 OrderState := "paid", QueryState := query.QueryState + return &types.ConfirmQueryStateResp{ + OrderState: query.QueryState, + QueryState: query.QueryState, + }, nil +} diff --git a/app/user/cmd/api/internal/logic/query/querylistlogic.go b/app/user/cmd/api/internal/logic/query/querylistlogic.go index ec339d0..7caa5c1 100644 --- a/app/user/cmd/api/internal/logic/query/querylistlogic.go +++ b/app/user/cmd/api/internal/logic/query/querylistlogic.go @@ -4,11 +4,11 @@ import ( "context" "qnc-server/app/user/cmd/api/internal/svc" "qnc-server/app/user/cmd/api/internal/types" + "qnc-server/app/user/model" "qnc-server/common/ctxdata" "qnc-server/common/xerr" "github.com/Masterminds/squirrel" - "github.com/jinzhu/copier" "github.com/pkg/errors" "github.com/zeromicro/go-zero/core/logx" ) @@ -30,35 +30,66 @@ func NewQueryListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *QueryLi func (l *QueryListLogic) QueryList(req *types.QueryListReq) (resp *types.QueryListResp, err error) { userID, getUidErr := ctxdata.GetUidFromCtx(l.ctx) if getUidErr != nil { - return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "报告列表查询, 获取用户信息失败, %+v", getUidErr) + return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "订单列表查询, 获取用户信息失败, %+v", getUidErr) } - // 直接构建查询query表的条件 - build := l.svcCtx.QueryModel.SelectBuilder().Where(squirrel.Eq{ - "user_id": userID, + // 构建查询订单表的条件,排除未支付的订单 + build := l.svcCtx.OrderModel.SelectBuilder().Where(squirrel.And{ + squirrel.Eq{ + "user_id": userID, + }, + squirrel.NotEq{ + "status": model.OrderStatusPending, + }, }) - // 直接从query表分页查询 - queryList, total, err := l.svcCtx.QueryModel.FindPageListByPageWithTotal(l.ctx, build, req.Page, req.PageSize, "create_time DESC") + // 从订单表分页查询 + orderList, total, err := l.svcCtx.OrderModel.FindPageListByPageWithTotal(l.ctx, build, req.Page, req.PageSize, "create_time DESC") if err != nil { - return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "报告列表查询, 查找报告列表错误, %+v", err) + return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "订单列表查询, 查找订单列表错误, %+v", err) } var list []types.Query - if len(queryList) > 0 { - for _, queryModel := range queryList { + if len(orderList) > 0 { + for _, orderModel := range orderList { var query types.Query - query.CreateTime = queryModel.CreateTime.Format("2006-01-02 15:04:05") - query.UpdateTime = queryModel.UpdateTime.Format("2006-01-02 15:04:05") - copyErr := copier.Copy(&query, queryModel) - if copyErr != nil { - return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "报告列表查询, 报告结构体复制失败, %+v", err) - } - product, findProductErr := l.svcCtx.ProductModel.FindOne(l.ctx, queryModel.ProductId) + query.CreateTime = orderModel.CreateTime.Format("2006-01-02 15:04:05") + query.UpdateTime = orderModel.UpdateTime.Format("2006-01-02 15:04:05") + // 设置订单ID + query.OrderId = orderModel.Id + query.UserId = orderModel.UserId + + // 获取商品信息 + product, findProductErr := l.svcCtx.ProductModel.FindOne(l.ctx, orderModel.ProductId) if findProductErr != nil { - return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "报告列表查询, 获取商品信息失败, %+v", err) + return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "订单列表查询, 获取商品信息失败, %+v", findProductErr) } query.ProductName = product.ProductName + + // 设置订单支付状态 + query.IsPaid = orderModel.Status == model.OrderStatusPaid + + // 查询查询状态 + queryInfo, findQueryErr := l.svcCtx.QueryModel.FindOneByOrderId(l.ctx, orderModel.Id) + if findQueryErr == nil { + // 查询存在 + query.Id = queryInfo.Id + query.QueryState = queryInfo.QueryState + query.IsQueryCompleted = queryInfo.QueryState == model.QueryStateSuccess + } else { + query.QueryState = "未创建" + query.IsQueryCompleted = false + } + + // 获取授权状态 + authInfo, findAuthErr := l.svcCtx.AuthorizationModel.FindOneByOrderId(l.ctx, orderModel.Id) + if findAuthErr == nil { + // 授权存在 + query.IsAuthCompleted = authInfo.Status == model.AuthorizationStatusSuccess + } else { + query.IsAuthCompleted = false + } + list = append(list, query) } } diff --git a/app/user/cmd/api/internal/logic/query/queryservicelogic.go b/app/user/cmd/api/internal/logic/query/queryservicelogic.go index 7f7dbdb..057c763 100644 --- a/app/user/cmd/api/internal/logic/query/queryservicelogic.go +++ b/app/user/cmd/api/internal/logic/query/queryservicelogic.go @@ -463,16 +463,16 @@ func (l *QueryServiceLogic) ProcessBackgroundCheckLogic(req *types.QueryServiceR } // 校验验证码 - //verifyCodeErr := l.VerifyCode(data.Mobile, data.Code) - //if verifyCodeErr != nil { - // return nil, verifyCodeErr - //} - // - //// 校验三要素 - //verifyErr := l.Verify(data.Name, data.IDCard, data.Mobile) - //if verifyErr != nil { - // return nil, verifyErr - //} + verifyCodeErr := l.VerifyCode(data.Mobile, data.Code) + if verifyCodeErr != nil { + return nil, verifyCodeErr + } + + // 校验三要素 + verifyErr := l.Verify(data.Name, data.IDCard, data.Mobile) + if verifyErr != nil { + return nil, verifyErr + } // 缓存 params := map[string]interface{}{ diff --git a/app/user/cmd/api/internal/queue/paySuccessNotify.go b/app/user/cmd/api/internal/queue/paySuccessNotify.go index 918e3ff..68a5e0f 100644 --- a/app/user/cmd/api/internal/queue/paySuccessNotify.go +++ b/app/user/cmd/api/internal/queue/paySuccessNotify.go @@ -12,10 +12,9 @@ import ( "qnc-server/app/user/model" "qnc-server/pkg/lzkit/crypto" "qnc-server/pkg/lzkit/lzUtils" - "regexp" - "strings" "time" + "github.com/bytedance/sonic" "github.com/hibiken/asynq" "github.com/zeromicro/go-zero/core/logx" ) @@ -60,7 +59,7 @@ func (l *PaySuccessNotifyUserHandler) ProcessTask(ctx context.Context, t *asynq. return fmt.Errorf("获取缓存内容失败: %+v", cacheErr) } var data types.QueryCacheLoad - err = json.Unmarshal([]byte(cache), &data) + err = sonic.Unmarshal([]byte(cache), &data) if err != nil { return fmt.Errorf("解析缓存内容失败: %+v", err) } @@ -74,38 +73,8 @@ func (l *PaySuccessNotifyUserHandler) ProcessTask(ctx context.Context, t *asynq. return fmt.Errorf("解密参数失败: %+v", aesdecryptErr) } - // 敏感数据脱敏处理 - desensitizedParams, err := l.desensitizeParams(decryptData) - if err != nil { - return fmt.Errorf("脱敏处理失败: %+v", err) - } - - // 对脱敏后的数据进行AES加密 - encryptedParams, encryptErr := crypto.AesEncrypt(desensitizedParams, key) - if encryptErr != nil { - return fmt.Errorf("加密脱敏数据失败: %+v", encryptErr) - } - - query := &model.Query{ - OrderId: order.Id, - UserId: order.UserId, - ProductId: product.Id, - QueryParams: encryptedParams, - QueryState: "pending", - } - result, insertQueryErr := l.svcCtx.QueryModel.Insert(ctx, nil, query) - if insertQueryErr != nil { - return fmt.Errorf("保存查询失败: %+v", insertQueryErr) - } - - // 获取插入后的ID - queryId, err := result.LastInsertId() - if err != nil { - return fmt.Errorf("获取插入的查询ID失败: %+v", err) - } - // 从数据库中查询完整的查询记录 - query, err = l.svcCtx.QueryModel.FindOne(ctx, queryId) + query, err := l.svcCtx.QueryModel.FindOneByOrderId(ctx, order.Id) if err != nil { return fmt.Errorf("获取插入后的查询记录失败: %+v", err) } @@ -204,177 +173,3 @@ func (l *PaySuccessNotifyUserHandler) handleError(ctx context.Context, err error return asynq.SkipRetry } - -// desensitizeParams 对敏感数据进行脱敏处理 -func (l *PaySuccessNotifyUserHandler) desensitizeParams(data []byte) ([]byte, error) { - // 解析JSON数据到map - var paramsMap map[string]interface{} - if err := json.Unmarshal(data, ¶msMap); err != nil { - return nil, fmt.Errorf("解析JSON数据失败: %v", err) - } - - // 处理可能包含敏感信息的字段 - for key, value := range paramsMap { - if strValue, ok := value.(string); ok { - // 根据字段名和内容判断并脱敏 - if isNameField(key) && len(strValue) > 0 { - // 姓名脱敏 - paramsMap[key] = maskName(strValue) - } else if isIDCardField(key) && len(strValue) > 10 { - // 身份证号脱敏 - paramsMap[key] = maskIDCard(strValue) - } else if isPhoneField(key) && len(strValue) >= 8 { - // 手机号脱敏 - paramsMap[key] = maskPhone(strValue) - } else if len(strValue) > 3 { - // 其他所有未匹配的字段都进行通用脱敏 - paramsMap[key] = maskGeneral(strValue) - } - } else if mapValue, ok := value.(map[string]interface{}); ok { - // 递归处理嵌套的map - for subKey, subValue := range mapValue { - if subStrValue, ok := subValue.(string); ok { - if isNameField(subKey) && len(subStrValue) > 0 { - mapValue[subKey] = maskName(subStrValue) - } else if isIDCardField(subKey) && len(subStrValue) > 10 { - mapValue[subKey] = maskIDCard(subStrValue) - } else if isPhoneField(subKey) && len(subStrValue) >= 8 { - mapValue[subKey] = maskPhone(subStrValue) - } else if len(subStrValue) > 3 { - // 其他所有未匹配的字段都进行通用脱敏 - mapValue[subKey] = maskGeneral(subStrValue) - } - } - } - } - } - - // 将处理后的map重新序列化为JSON - return json.Marshal(paramsMap) -} - -// 判断是否为姓名字段 -func isNameField(key string) bool { - key = strings.ToLower(key) - return strings.Contains(key, "name") || strings.Contains(key, "姓名") || - strings.Contains(key, "owner") || strings.Contains(key, "user") -} - -// 判断是否为身份证字段 -func isIDCardField(key string) bool { - key = strings.ToLower(key) - return strings.Contains(key, "idcard") || strings.Contains(key, "id_card") || - strings.Contains(key, "身份证") || strings.Contains(key, "证件号") -} - -// 判断是否为手机号字段 -func isPhoneField(key string) bool { - key = strings.ToLower(key) - return strings.Contains(key, "phone") || strings.Contains(key, "mobile") || - strings.Contains(key, "手机") || strings.Contains(key, "电话") -} - -// 判断是否包含敏感数据模式 -func containsSensitivePattern(value string) bool { - // 检查是否包含连续的数字或字母模式 - numPattern := regexp.MustCompile(`\d{6,}`) - return numPattern.MatchString(value) -} - -// 姓名脱敏 -func maskName(name string) string { - // 将字符串转换为rune切片以正确处理中文字符 - runes := []rune(name) - length := len(runes) - - if length <= 1 { - return name - } - - if length == 2 { - // 两个字:保留第一个字,第二个字用*替代 - return string(runes[0]) + "*" - } - - // 三个字及以上:保留首尾字,中间用*替代 - first := string(runes[0]) - last := string(runes[length-1]) - mask := strings.Repeat("*", length-2) - - return first + mask + last -} - -// 身份证号脱敏 -func maskIDCard(idCard string) string { - length := len(idCard) - if length <= 10 { - return idCard // 如果长度太短,可能不是身份证,不处理 - } - // 保留前3位和后4位 - return idCard[:3] + strings.Repeat("*", length-7) + idCard[length-4:] -} - -// 手机号脱敏 -func maskPhone(phone string) string { - length := len(phone) - if length < 8 { - return phone // 如果长度太短,可能不是手机号,不处理 - } - // 保留前3位和后4位 - return phone[:3] + strings.Repeat("*", length-7) + phone[length-4:] -} - -// 通用敏感信息脱敏 - 根据字符串长度比例进行脱敏 -func maskGeneral(value string) string { - length := len(value) - - // 小于3个字符的不脱敏 - if length <= 3 { - return value - } - - // 根据字符串长度计算保留字符数 - var prefixLen, suffixLen int - - switch { - case length <= 6: // 短字符串 - // 保留首尾各1个字符 - prefixLen, suffixLen = 1, 1 - case length <= 10: // 中等长度字符串 - // 保留首部30%和尾部20%的字符 - prefixLen = int(float64(length) * 0.3) - suffixLen = int(float64(length) * 0.2) - case length <= 20: // 较长字符串 - // 保留首部25%和尾部15%的字符 - prefixLen = int(float64(length) * 0.25) - suffixLen = int(float64(length) * 0.15) - default: // 非常长的字符串 - // 保留首部20%和尾部10%的字符 - prefixLen = int(float64(length) * 0.2) - suffixLen = int(float64(length) * 0.1) - } - - // 确保至少有一个字符被保留 - if prefixLen < 1 { - prefixLen = 1 - } - if suffixLen < 1 { - suffixLen = 1 - } - - // 确保前缀和后缀总长不超过总长度的80% - if prefixLen+suffixLen > int(float64(length)*0.8) { - // 调整为总长度的80% - totalVisible := int(float64(length) * 0.8) - // 前缀占60%,后缀占40% - prefixLen = int(float64(totalVisible) * 0.6) - suffixLen = totalVisible - prefixLen - } - - // 创建脱敏后的字符串 - prefix := value[:prefixLen] - suffix := value[length-suffixLen:] - masked := strings.Repeat("*", length-prefixLen-suffixLen) - - return prefix + masked + suffix -} diff --git a/app/user/cmd/api/internal/svc/servicecontext.go b/app/user/cmd/api/internal/svc/servicecontext.go index dd709fe..9214e8b 100644 --- a/app/user/cmd/api/internal/svc/servicecontext.go +++ b/app/user/cmd/api/internal/svc/servicecontext.go @@ -5,6 +5,7 @@ import ( "qnc-server/app/user/cmd/api/internal/middleware" "qnc-server/app/user/cmd/api/internal/service" "qnc-server/app/user/model" + "qnc-server/pkg/core/aliyun/cloudauth" "github.com/hibiken/asynq" "github.com/zeromicro/go-zero/core/logx" @@ -41,8 +42,11 @@ type ServiceContext struct { AgentPlatformDeductionModel model.AgentPlatformDeductionModel AgentActiveStatModel model.AgentActiveStatModel AgentWithdrawalModel model.AgentWithdrawalModel + AgentRealNameModel model.AgentRealNameModel ExampleModel model.ExampleModel GlobalNotificationsModel model.GlobalNotificationsModel + AuthorizationModel model.AuthorizationModel + AuthorizationFaceModel model.AuthorizationFaceModel AlipayService *service.AliPayService WechatPayService *service.WechatPayService ApplePayService *service.ApplePayService @@ -54,6 +58,9 @@ type ServiceContext struct { VerificationService *service.VerificationService AgentService *service.AgentService UserService *service.UserService + + // core service + CloudAuthService *cloudauth.CloudAuthClient } func NewServiceContext(c config.Config) *ServiceContext { @@ -75,7 +82,17 @@ func NewServiceContext(c config.Config) *ServiceContext { Concurrency: 10, }, ) - + cloudAuthService, err := cloudauth.NewCloudAuthClient(cloudauth.CloudAuthConfig{ + AccessKeyId: c.CloudAuth.AccessKeyId, + AccessKeySecret: c.CloudAuth.AccessKeySecret, + Endpoint: c.CloudAuth.Endpoint, + SceneId: c.CloudAuth.SceneId, + ReturnUrl: c.CloudAuth.ReturnUrl, + }) + if err != nil { + logx.Errorf("初始化阿里云人脸认证服务失败: %+v", err) + panic(err) + } westDexService := service.NewWestDexService(c) yushanService := service.NewYushanService(c) productFeatureModel := model.NewProductFeatureModel(db, c.CacheRedis) @@ -104,7 +121,10 @@ func NewServiceContext(c config.Config) *ServiceContext { agentPlatformDeductionModel := model.NewAgentPlatformDeductionModel(db, c.CacheRedis) agentActiveStatModel := model.NewAgentActiveStatModel(db, c.CacheRedis) agentWithdrawalModel := model.NewAgentWithdrawalModel(db, c.CacheRedis) + agentRealNameModel := model.NewAgentRealNameModel(db, c.CacheRedis) exampleModel := model.NewExampleModel(db, c.CacheRedis) + authorizationModel := model.NewAuthorizationModel(db, c.CacheRedis) + authorizationFaceModel := model.NewAuthorizationFaceModel(db, c.CacheRedis) alipayService := service.NewAliPayService(c) wechatPayService := service.NewWechatPayService(c, userAuthModel, service.InitTypePlatformCert) @@ -117,6 +137,7 @@ func NewServiceContext(c config.Config) *ServiceContext { agentMembershipConfigModel, agentMembershipRechargeOrderModel, agentMembershipUserConfigModel, agentProductConfigModel, agentPlatformDeductionModel, agentActiveStatModel, agentWithdrawalModel) userService := service.NewUserService(userModel, userAuthModel) + return &ServiceContext{ Config: c, Redis: redis.MustNewRedis(redisConf), @@ -156,8 +177,12 @@ func NewServiceContext(c config.Config) *ServiceContext { AgentPlatformDeductionModel: agentPlatformDeductionModel, AgentActiveStatModel: agentActiveStatModel, AgentWithdrawalModel: agentWithdrawalModel, + AgentRealNameModel: agentRealNameModel, ExampleModel: exampleModel, + AuthorizationModel: authorizationModel, + AuthorizationFaceModel: authorizationFaceModel, UserService: userService, + CloudAuthService: cloudAuthService, } } diff --git a/app/user/cmd/api/internal/types/types.go b/app/user/cmd/api/internal/types/types.go index a5b215e..ce7f88f 100644 --- a/app/user/cmd/api/internal/types/types.go +++ b/app/user/cmd/api/internal/types/types.go @@ -26,7 +26,6 @@ type AgentActivateMembershipResp struct { type AgentApplyReq struct { Region string `json:"region"` Mobile string `json:"mobile"` - WechatID string `json:"wechat_id"` Code string `json:"code"` Ancestor string `json:"ancestor,optional"` } @@ -58,8 +57,8 @@ type AgentInfoResp struct { Level string `json:"level"` Region string `json:"region"` Mobile string `json:"mobile"` - WechatID string `json:"wechat_id"` ExpiryTime string `json:"expiry_time"` + IsRealName bool `json:"is_real_name"` } type AgentMembershipProductConfigReq struct { @@ -98,6 +97,17 @@ type AgentProductConfigResp struct { AgentProductConfig []AgentProductConfig } +type AgentRealNameReq struct { + Name string `json:"name"` + IDCard string `json:"id_card"` + Mobile string `json:"mobile"` + Code string `json:"code"` +} + +type AgentRealNameResp struct { + Status string `json:"status"` +} + type AgentSubordinateContributionDetail struct { ID int64 `json:"id"` CreateTime string `json:"create_time"` @@ -148,6 +158,15 @@ type Commission struct { CreateTime string `json:"create_time"` } +type ConfirmQueryStateReq struct { + OrderID int64 `json:"order_id" validate:"required"` +} + +type ConfirmQueryStateResp struct { + OrderState string `json:"order_state"` // 查询状态,例如"pending"、"success"、"failed"等 + QueryState string `json:"query_state"` +} + type DirectPushReport struct { TotalCommission float64 `json:"total_commission"` TotalReport int `json:"total_report"` @@ -211,6 +230,16 @@ type GetCommissionResp struct { List []Commission `json:"list"` // 查询列表 } +type GetFaceVerifyResultReq struct { + CertifyId string `json:"certify_id" validate:"required"` // 认证ID +} + +type GetFaceVerifyResultResp struct { + Passed bool `json:"passed"` // 是否通过 + OrderID int64 `json:"order_id"` + AuthType int64 `json:"auth_type"` +} + type GetLinkDataReq struct { LinkIdentifier string `form:"link_identifier"` } @@ -262,6 +291,17 @@ type IapCallbackReq struct { TransactionReceipt string `json:"transaction_receipt" validate:"required"` } +type InitFaceVerifyReq struct { + MetaInfo string `json:"meta_info" validate:"required"` // H5端获取的MetaInfo,JSON格式 + OrderNo string `json:"order_no" validate:"required"` // 订单号 + AuthType int64 `json:"auth_type" validate:"required"` // 认证类型 +} + +type InitFaceVerifyResp struct { + CertifyId string `json:"certify_id"` // 认证ID + CertifyUrl string `json:"certify_url"` // 跳转认证页面URL +} + type MobileCodeLoginReq struct { Mobile string `json:"mobile"` Code string `json:"code" validate:"required"` @@ -336,15 +376,18 @@ type ProductResponse struct { } type Query struct { - Id int64 `json:"id"` // 主键ID - OrderId int64 `json:"order_id"` // 订单ID - UserId int64 `json:"user_id"` // 用户ID - ProductName string `json:"product_name"` // 产品ID - QueryParams map[string]interface{} `json:"query_params"` - QueryData []QueryItem `json:"query_data"` - CreateTime string `json:"create_time"` // 创建时间 - UpdateTime string `json:"update_time"` // 更新时间 - QueryState string `json:"query_state"` // 查询状态 + Id int64 `json:"id"` // 主键ID + OrderId int64 `json:"order_id"` // 订单ID + UserId int64 `json:"user_id"` // 用户ID + ProductName string `json:"product_name"` // 产品ID + QueryParams map[string]interface{} `json:"query_params"` + QueryData []QueryItem `json:"query_data"` + CreateTime string `json:"create_time"` // 创建时间 + UpdateTime string `json:"update_time"` // 更新时间 + QueryState string `json:"query_state"` // 查询状态 + IsPaid bool `json:"is_paid"` // 是否支付 + IsQueryCompleted bool `json:"is_query_completed"` // 查询是否完成 + IsAuthCompleted bool `json:"is_auth_completed"` // 授权是否完成 } type QueryDetailByOrderIdReq struct { @@ -394,6 +437,18 @@ type QueryListResp struct { List []Query `json:"list"` // 查询列表 } +type QueryPaymentCheckReq struct { + OrderNo string `json:"order_no" validate:"required"` +} + +type QueryPaymentCheckResp struct { + OrderStatus string `json:"order_status"` + AuthorizationStatus string `json:"authorization_status"` + Name string `json:"name"` + IdCard string `json:"id_card"` + ProductName string `json:"product_name"` +} + type QueryProvisionalOrderReq struct { Id string `path:"id"` } @@ -457,6 +512,13 @@ type RegisterResp struct { RefreshAfter int64 `json:"refreshAfter"` } +type RejectAuthorizationReq struct { + OrderNo string `json:"order_no" validate:"required"` // 订单号 +} + +type RejectAuthorizationResp struct { +} + type Rewards struct { Type string `json:"type"` Amount float64 `json:"amount"` @@ -545,5 +607,5 @@ type GetAppVersionResp struct { type SendSmsReq struct { Mobile string `json:"mobile" validate:"required,mobile"` - ActionType string `json:"actionType" validate:"required,oneof=login register query agentApply bindMobile"` + ActionType string `json:"actionType" validate:"required,oneof=login register query agentApply bindMobile realName"` } diff --git a/app/user/model/agentAuditModel_gen.go b/app/user/model/agentAuditModel_gen.go index 32e014a..a827590 100644 --- a/app/user/model/agentAuditModel_gen.go +++ b/app/user/model/agentAuditModel_gen.go @@ -60,7 +60,6 @@ type ( UserId int64 `db:"user_id"` Region string `db:"region"` Mobile string `db:"mobile"` - WechatId sql.NullString `db:"wechat_id"` Status int64 `db:"status"` AuditReason sql.NullString `db:"audit_reason"` CreateTime time.Time `db:"create_time"` @@ -84,11 +83,11 @@ func (m *defaultAgentAuditModel) Insert(ctx context.Context, session sqlx.Sessio qncAgentAuditIdKey := fmt.Sprintf("%s%v", cacheQncAgentAuditIdPrefix, data.Id) qncAgentAuditUserIdKey := fmt.Sprintf("%s%v", cacheQncAgentAuditUserIdPrefix, data.UserId) return m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) { - query := fmt.Sprintf("insert into %s (%s) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", m.table, agentAuditRowsExpectAutoSet) + query := fmt.Sprintf("insert into %s (%s) values (?, ?, ?, ?, ?, ?, ?, ?, ?)", m.table, agentAuditRowsExpectAutoSet) if session != nil { - return session.ExecCtx(ctx, query, data.UserId, data.Region, data.Mobile, data.WechatId, data.Status, data.AuditReason, data.AuditTime, data.DeleteTime, data.DelState, data.Version) + return session.ExecCtx(ctx, query, data.UserId, data.Region, data.Mobile, data.Status, data.AuditReason, data.AuditTime, data.DeleteTime, data.DelState, data.Version) } - return conn.ExecCtx(ctx, query, data.UserId, data.Region, data.Mobile, data.WechatId, data.Status, data.AuditReason, data.AuditTime, data.DeleteTime, data.DelState, data.Version) + return conn.ExecCtx(ctx, query, data.UserId, data.Region, data.Mobile, data.Status, data.AuditReason, data.AuditTime, data.DeleteTime, data.DelState, data.Version) }, qncAgentAuditIdKey, qncAgentAuditUserIdKey) } @@ -139,9 +138,9 @@ func (m *defaultAgentAuditModel) Update(ctx context.Context, session sqlx.Sessio return m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) { query := fmt.Sprintf("update %s set %s where `id` = ?", m.table, agentAuditRowsWithPlaceHolder) if session != nil { - return session.ExecCtx(ctx, query, newData.UserId, newData.Region, newData.Mobile, newData.WechatId, newData.Status, newData.AuditReason, newData.AuditTime, newData.DeleteTime, newData.DelState, newData.Version, newData.Id) + return session.ExecCtx(ctx, query, newData.UserId, newData.Region, newData.Mobile, newData.Status, newData.AuditReason, newData.AuditTime, newData.DeleteTime, newData.DelState, newData.Version, newData.Id) } - return conn.ExecCtx(ctx, query, newData.UserId, newData.Region, newData.Mobile, newData.WechatId, newData.Status, newData.AuditReason, newData.AuditTime, newData.DeleteTime, newData.DelState, newData.Version, newData.Id) + return conn.ExecCtx(ctx, query, newData.UserId, newData.Region, newData.Mobile, newData.Status, newData.AuditReason, newData.AuditTime, newData.DeleteTime, newData.DelState, newData.Version, newData.Id) }, qncAgentAuditIdKey, qncAgentAuditUserIdKey) } @@ -162,9 +161,9 @@ func (m *defaultAgentAuditModel) UpdateWithVersion(ctx context.Context, session sqlResult, err = m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) { query := fmt.Sprintf("update %s set %s where `id` = ? and version = ? ", m.table, agentAuditRowsWithPlaceHolder) if session != nil { - return session.ExecCtx(ctx, query, newData.UserId, newData.Region, newData.Mobile, newData.WechatId, newData.Status, newData.AuditReason, newData.AuditTime, newData.DeleteTime, newData.DelState, newData.Version, newData.Id, oldVersion) + return session.ExecCtx(ctx, query, newData.UserId, newData.Region, newData.Mobile, newData.Status, newData.AuditReason, newData.AuditTime, newData.DeleteTime, newData.DelState, newData.Version, newData.Id, oldVersion) } - return conn.ExecCtx(ctx, query, newData.UserId, newData.Region, newData.Mobile, newData.WechatId, newData.Status, newData.AuditReason, newData.AuditTime, newData.DeleteTime, newData.DelState, newData.Version, newData.Id, oldVersion) + return conn.ExecCtx(ctx, query, newData.UserId, newData.Region, newData.Mobile, newData.Status, newData.AuditReason, newData.AuditTime, newData.DeleteTime, newData.DelState, newData.Version, newData.Id, oldVersion) }, qncAgentAuditIdKey, qncAgentAuditUserIdKey) if err != nil { return err diff --git a/app/user/model/agentModel_gen.go b/app/user/model/agentModel_gen.go index bbdd4f9..a3eb7e7 100644 --- a/app/user/model/agentModel_gen.go +++ b/app/user/model/agentModel_gen.go @@ -58,18 +58,17 @@ type ( } Agent struct { - Id int64 `db:"id"` - UserId int64 `db:"user_id"` - LevelName string `db:"level_name"` // 代理等级 - Region string `db:"region"` - Mobile string `db:"mobile"` - WechatId sql.NullString `db:"wechat_id"` - MembershipExpiryTime sql.NullTime `db:"membership_expiry_time"` // 会员过期时间 - CreateTime time.Time `db:"create_time"` - UpdateTime time.Time `db:"update_time"` - DeleteTime sql.NullTime `db:"delete_time"` // 删除时间 - DelState int64 `db:"del_state"` // 删除状态 - Version int64 `db:"version"` // 版本号 + Id int64 `db:"id"` + UserId int64 `db:"user_id"` + LevelName string `db:"level_name"` // 代理等级 + Region string `db:"region"` + Mobile string `db:"mobile"` + MembershipExpiryTime sql.NullTime `db:"membership_expiry_time"` // 会员过期时间 + CreateTime time.Time `db:"create_time"` + UpdateTime time.Time `db:"update_time"` + DeleteTime sql.NullTime `db:"delete_time"` // 删除时间 + DelState int64 `db:"del_state"` // 删除状态 + Version int64 `db:"version"` // 版本号 } ) @@ -86,11 +85,11 @@ func (m *defaultAgentModel) Insert(ctx context.Context, session sqlx.Session, da qncAgentMobileKey := fmt.Sprintf("%s%v", cacheQncAgentMobilePrefix, data.Mobile) qncAgentUserIdKey := fmt.Sprintf("%s%v", cacheQncAgentUserIdPrefix, data.UserId) return m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) { - query := fmt.Sprintf("insert into %s (%s) values (?, ?, ?, ?, ?, ?, ?, ?, ?)", m.table, agentRowsExpectAutoSet) + query := fmt.Sprintf("insert into %s (%s) values (?, ?, ?, ?, ?, ?, ?, ?)", m.table, agentRowsExpectAutoSet) if session != nil { - return session.ExecCtx(ctx, query, data.UserId, data.LevelName, data.Region, data.Mobile, data.WechatId, data.MembershipExpiryTime, data.DeleteTime, data.DelState, data.Version) + return session.ExecCtx(ctx, query, data.UserId, data.LevelName, data.Region, data.Mobile, data.MembershipExpiryTime, data.DeleteTime, data.DelState, data.Version) } - return conn.ExecCtx(ctx, query, data.UserId, data.LevelName, data.Region, data.Mobile, data.WechatId, data.MembershipExpiryTime, data.DeleteTime, data.DelState, data.Version) + return conn.ExecCtx(ctx, query, data.UserId, data.LevelName, data.Region, data.Mobile, data.MembershipExpiryTime, data.DeleteTime, data.DelState, data.Version) }, qncAgentIdKey, qncAgentMobileKey, qncAgentUserIdKey) } @@ -162,9 +161,9 @@ func (m *defaultAgentModel) Update(ctx context.Context, session sqlx.Session, ne return m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) { query := fmt.Sprintf("update %s set %s where `id` = ?", m.table, agentRowsWithPlaceHolder) if session != nil { - return session.ExecCtx(ctx, query, newData.UserId, newData.LevelName, newData.Region, newData.Mobile, newData.WechatId, newData.MembershipExpiryTime, newData.DeleteTime, newData.DelState, newData.Version, newData.Id) + return session.ExecCtx(ctx, query, newData.UserId, newData.LevelName, newData.Region, newData.Mobile, newData.MembershipExpiryTime, newData.DeleteTime, newData.DelState, newData.Version, newData.Id) } - return conn.ExecCtx(ctx, query, newData.UserId, newData.LevelName, newData.Region, newData.Mobile, newData.WechatId, newData.MembershipExpiryTime, newData.DeleteTime, newData.DelState, newData.Version, newData.Id) + return conn.ExecCtx(ctx, query, newData.UserId, newData.LevelName, newData.Region, newData.Mobile, newData.MembershipExpiryTime, newData.DeleteTime, newData.DelState, newData.Version, newData.Id) }, qncAgentIdKey, qncAgentMobileKey, qncAgentUserIdKey) } @@ -186,9 +185,9 @@ func (m *defaultAgentModel) UpdateWithVersion(ctx context.Context, session sqlx. sqlResult, err = m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) { query := fmt.Sprintf("update %s set %s where `id` = ? and version = ? ", m.table, agentRowsWithPlaceHolder) if session != nil { - return session.ExecCtx(ctx, query, newData.UserId, newData.LevelName, newData.Region, newData.Mobile, newData.WechatId, newData.MembershipExpiryTime, newData.DeleteTime, newData.DelState, newData.Version, newData.Id, oldVersion) + return session.ExecCtx(ctx, query, newData.UserId, newData.LevelName, newData.Region, newData.Mobile, newData.MembershipExpiryTime, newData.DeleteTime, newData.DelState, newData.Version, newData.Id, oldVersion) } - return conn.ExecCtx(ctx, query, newData.UserId, newData.LevelName, newData.Region, newData.Mobile, newData.WechatId, newData.MembershipExpiryTime, newData.DeleteTime, newData.DelState, newData.Version, newData.Id, oldVersion) + return conn.ExecCtx(ctx, query, newData.UserId, newData.LevelName, newData.Region, newData.Mobile, newData.MembershipExpiryTime, newData.DeleteTime, newData.DelState, newData.Version, newData.Id, oldVersion) }, qncAgentIdKey, qncAgentMobileKey, qncAgentUserIdKey) if err != nil { return err diff --git a/app/user/model/agentRealNameModel.go b/app/user/model/agentRealNameModel.go new file mode 100644 index 0000000..50bca1f --- /dev/null +++ b/app/user/model/agentRealNameModel.go @@ -0,0 +1,27 @@ +package model + +import ( + "github.com/zeromicro/go-zero/core/stores/cache" + "github.com/zeromicro/go-zero/core/stores/sqlx" +) + +var _ AgentRealNameModel = (*customAgentRealNameModel)(nil) + +type ( + // AgentRealNameModel is an interface to be customized, add more methods here, + // and implement the added methods in customAgentRealNameModel. + AgentRealNameModel interface { + agentRealNameModel + } + + customAgentRealNameModel struct { + *defaultAgentRealNameModel + } +) + +// NewAgentRealNameModel returns a model for the database table. +func NewAgentRealNameModel(conn sqlx.SqlConn, c cache.CacheConf) AgentRealNameModel { + return &customAgentRealNameModel{ + defaultAgentRealNameModel: newAgentRealNameModel(conn, c), + } +} diff --git a/app/user/model/agentRealNameModel_gen.go b/app/user/model/agentRealNameModel_gen.go new file mode 100644 index 0000000..0ec7aec --- /dev/null +++ b/app/user/model/agentRealNameModel_gen.go @@ -0,0 +1,411 @@ +// Code generated by goctl. DO NOT EDIT! + +package model + +import ( + "context" + "database/sql" + "fmt" + "strings" + + "time" + + "github.com/Masterminds/squirrel" + "github.com/pkg/errors" + "github.com/zeromicro/go-zero/core/stores/builder" + "github.com/zeromicro/go-zero/core/stores/cache" + "github.com/zeromicro/go-zero/core/stores/sqlc" + "github.com/zeromicro/go-zero/core/stores/sqlx" + "github.com/zeromicro/go-zero/core/stringx" + "qnc-server/common/globalkey" +) + +var ( + agentRealNameFieldNames = builder.RawFieldNames(&AgentRealName{}) + agentRealNameRows = strings.Join(agentRealNameFieldNames, ",") + agentRealNameRowsExpectAutoSet = strings.Join(stringx.Remove(agentRealNameFieldNames, "`id`", "`create_time`", "`update_time`"), ",") + agentRealNameRowsWithPlaceHolder = strings.Join(stringx.Remove(agentRealNameFieldNames, "`id`", "`create_time`", "`update_time`"), "=?,") + "=?" + + cacheQncAgentRealNameIdPrefix = "cache:qnc:agentRealName:id:" + cacheQncAgentRealNameAgentIdPrefix = "cache:qnc:agentRealName:agentId:" +) + +type ( + agentRealNameModel interface { + Insert(ctx context.Context, session sqlx.Session, data *AgentRealName) (sql.Result, error) + FindOne(ctx context.Context, id int64) (*AgentRealName, error) + FindOneByAgentId(ctx context.Context, agentId int64) (*AgentRealName, error) + Update(ctx context.Context, session sqlx.Session, data *AgentRealName) (sql.Result, error) + UpdateWithVersion(ctx context.Context, session sqlx.Session, data *AgentRealName) error + Trans(ctx context.Context, fn func(context context.Context, session sqlx.Session) error) error + SelectBuilder() squirrel.SelectBuilder + DeleteSoft(ctx context.Context, session sqlx.Session, data *AgentRealName) error + FindSum(ctx context.Context, sumBuilder squirrel.SelectBuilder, field string) (float64, error) + FindCount(ctx context.Context, countBuilder squirrel.SelectBuilder, field string) (int64, error) + FindAll(ctx context.Context, rowBuilder squirrel.SelectBuilder, orderBy string) ([]*AgentRealName, error) + FindPageListByPage(ctx context.Context, rowBuilder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*AgentRealName, error) + FindPageListByPageWithTotal(ctx context.Context, rowBuilder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*AgentRealName, int64, error) + FindPageListByIdDESC(ctx context.Context, rowBuilder squirrel.SelectBuilder, preMinId, pageSize int64) ([]*AgentRealName, error) + FindPageListByIdASC(ctx context.Context, rowBuilder squirrel.SelectBuilder, preMaxId, pageSize int64) ([]*AgentRealName, error) + Delete(ctx context.Context, session sqlx.Session, id int64) error + } + + defaultAgentRealNameModel struct { + sqlc.CachedConn + table string + } + + AgentRealName struct { + Id int64 `db:"id"` // 主键ID + AgentId int64 `db:"agent_id"` // 代理ID + Name string `db:"name"` // 实名姓名 + IdCard string `db:"id_card"` // 身份证号 + Status string `db:"status"` // 认证状态(认证中、通过、拒绝) + DelState int64 `db:"del_state"` // 删除状态 + Version int64 `db:"version"` // 版本号 + CreateTime time.Time `db:"create_time"` // 创建时间 + UpdateTime time.Time `db:"update_time"` // 更新时间 + ApproveTime sql.NullTime `db:"approve_time"` // 认证通过时间 + RejectTime sql.NullTime `db:"reject_time"` // 认证拒绝时间 + DeleteTime sql.NullTime `db:"delete_time"` // 删除时间 + } +) + +func newAgentRealNameModel(conn sqlx.SqlConn, c cache.CacheConf) *defaultAgentRealNameModel { + return &defaultAgentRealNameModel{ + CachedConn: sqlc.NewConn(conn, c), + table: "`agent_real_name`", + } +} + +func (m *defaultAgentRealNameModel) Insert(ctx context.Context, session sqlx.Session, data *AgentRealName) (sql.Result, error) { + data.DelState = globalkey.DelStateNo + qncAgentRealNameAgentIdKey := fmt.Sprintf("%s%v", cacheQncAgentRealNameAgentIdPrefix, data.AgentId) + qncAgentRealNameIdKey := fmt.Sprintf("%s%v", cacheQncAgentRealNameIdPrefix, data.Id) + return m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) { + query := fmt.Sprintf("insert into %s (%s) values (?, ?, ?, ?, ?, ?, ?, ?, ?)", m.table, agentRealNameRowsExpectAutoSet) + if session != nil { + return session.ExecCtx(ctx, query, data.AgentId, data.Name, data.IdCard, data.Status, data.DelState, data.Version, data.ApproveTime, data.RejectTime, data.DeleteTime) + } + return conn.ExecCtx(ctx, query, data.AgentId, data.Name, data.IdCard, data.Status, data.DelState, data.Version, data.ApproveTime, data.RejectTime, data.DeleteTime) + }, qncAgentRealNameAgentIdKey, qncAgentRealNameIdKey) +} + +func (m *defaultAgentRealNameModel) FindOne(ctx context.Context, id int64) (*AgentRealName, error) { + qncAgentRealNameIdKey := fmt.Sprintf("%s%v", cacheQncAgentRealNameIdPrefix, id) + var resp AgentRealName + err := m.QueryRowCtx(ctx, &resp, qncAgentRealNameIdKey, func(ctx context.Context, conn sqlx.SqlConn, v interface{}) error { + query := fmt.Sprintf("select %s from %s where `id` = ? and del_state = ? limit 1", agentRealNameRows, m.table) + return conn.QueryRowCtx(ctx, v, query, id, globalkey.DelStateNo) + }) + switch err { + case nil: + return &resp, nil + case sqlc.ErrNotFound: + return nil, ErrNotFound + default: + return nil, err + } +} + +func (m *defaultAgentRealNameModel) FindOneByAgentId(ctx context.Context, agentId int64) (*AgentRealName, error) { + qncAgentRealNameAgentIdKey := fmt.Sprintf("%s%v", cacheQncAgentRealNameAgentIdPrefix, agentId) + var resp AgentRealName + err := m.QueryRowIndexCtx(ctx, &resp, qncAgentRealNameAgentIdKey, m.formatPrimary, func(ctx context.Context, conn sqlx.SqlConn, v interface{}) (i interface{}, e error) { + query := fmt.Sprintf("select %s from %s where `agent_id` = ? and del_state = ? limit 1", agentRealNameRows, m.table) + if err := conn.QueryRowCtx(ctx, &resp, query, agentId, globalkey.DelStateNo); err != nil { + return nil, err + } + return resp.Id, nil + }, m.queryPrimary) + switch err { + case nil: + return &resp, nil + case sqlc.ErrNotFound: + return nil, ErrNotFound + default: + return nil, err + } +} + +func (m *defaultAgentRealNameModel) Update(ctx context.Context, session sqlx.Session, newData *AgentRealName) (sql.Result, error) { + data, err := m.FindOne(ctx, newData.Id) + if err != nil { + return nil, err + } + qncAgentRealNameAgentIdKey := fmt.Sprintf("%s%v", cacheQncAgentRealNameAgentIdPrefix, data.AgentId) + qncAgentRealNameIdKey := fmt.Sprintf("%s%v", cacheQncAgentRealNameIdPrefix, data.Id) + return m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) { + query := fmt.Sprintf("update %s set %s where `id` = ?", m.table, agentRealNameRowsWithPlaceHolder) + if session != nil { + return session.ExecCtx(ctx, query, newData.AgentId, newData.Name, newData.IdCard, newData.Status, newData.DelState, newData.Version, newData.ApproveTime, newData.RejectTime, newData.DeleteTime, newData.Id) + } + return conn.ExecCtx(ctx, query, newData.AgentId, newData.Name, newData.IdCard, newData.Status, newData.DelState, newData.Version, newData.ApproveTime, newData.RejectTime, newData.DeleteTime, newData.Id) + }, qncAgentRealNameAgentIdKey, qncAgentRealNameIdKey) +} + +func (m *defaultAgentRealNameModel) UpdateWithVersion(ctx context.Context, session sqlx.Session, newData *AgentRealName) error { + + oldVersion := newData.Version + newData.Version += 1 + + var sqlResult sql.Result + var err error + + data, err := m.FindOne(ctx, newData.Id) + if err != nil { + return err + } + qncAgentRealNameAgentIdKey := fmt.Sprintf("%s%v", cacheQncAgentRealNameAgentIdPrefix, data.AgentId) + qncAgentRealNameIdKey := fmt.Sprintf("%s%v", cacheQncAgentRealNameIdPrefix, data.Id) + sqlResult, err = m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) { + query := fmt.Sprintf("update %s set %s where `id` = ? and version = ? ", m.table, agentRealNameRowsWithPlaceHolder) + if session != nil { + return session.ExecCtx(ctx, query, newData.AgentId, newData.Name, newData.IdCard, newData.Status, newData.DelState, newData.Version, newData.ApproveTime, newData.RejectTime, newData.DeleteTime, newData.Id, oldVersion) + } + return conn.ExecCtx(ctx, query, newData.AgentId, newData.Name, newData.IdCard, newData.Status, newData.DelState, newData.Version, newData.ApproveTime, newData.RejectTime, newData.DeleteTime, newData.Id, oldVersion) + }, qncAgentRealNameAgentIdKey, qncAgentRealNameIdKey) + if err != nil { + return err + } + updateCount, err := sqlResult.RowsAffected() + if err != nil { + return err + } + if updateCount == 0 { + return ErrNoRowsUpdate + } + + return nil +} + +func (m *defaultAgentRealNameModel) DeleteSoft(ctx context.Context, session sqlx.Session, data *AgentRealName) error { + data.DelState = globalkey.DelStateYes + data.DeleteTime = sql.NullTime{Time: time.Now(), Valid: true} + if err := m.UpdateWithVersion(ctx, session, data); err != nil { + return errors.Wrapf(errors.New("delete soft failed "), "AgentRealNameModel delete err : %+v", err) + } + return nil +} + +func (m *defaultAgentRealNameModel) FindSum(ctx context.Context, builder squirrel.SelectBuilder, field string) (float64, error) { + + if len(field) == 0 { + return 0, errors.Wrapf(errors.New("FindSum Least One Field"), "FindSum Least One Field") + } + + builder = builder.Columns("IFNULL(SUM(" + field + "),0)") + + query, values, err := builder.Where("del_state = ?", globalkey.DelStateNo).ToSql() + if err != nil { + return 0, err + } + + var resp float64 + err = m.QueryRowNoCacheCtx(ctx, &resp, query, values...) + switch err { + case nil: + return resp, nil + default: + return 0, err + } +} + +func (m *defaultAgentRealNameModel) FindCount(ctx context.Context, builder squirrel.SelectBuilder, field string) (int64, error) { + + if len(field) == 0 { + return 0, errors.Wrapf(errors.New("FindCount Least One Field"), "FindCount Least One Field") + } + + builder = builder.Columns("COUNT(" + field + ")") + + query, values, err := builder.Where("del_state = ?", globalkey.DelStateNo).ToSql() + if err != nil { + return 0, err + } + + var resp int64 + err = m.QueryRowNoCacheCtx(ctx, &resp, query, values...) + switch err { + case nil: + return resp, nil + default: + return 0, err + } +} + +func (m *defaultAgentRealNameModel) FindAll(ctx context.Context, builder squirrel.SelectBuilder, orderBy string) ([]*AgentRealName, error) { + + builder = builder.Columns(agentRealNameRows) + + if orderBy == "" { + builder = builder.OrderBy("id DESC") + } else { + builder = builder.OrderBy(orderBy) + } + + query, values, err := builder.Where("del_state = ?", globalkey.DelStateNo).ToSql() + if err != nil { + return nil, err + } + + var resp []*AgentRealName + err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...) + switch err { + case nil: + return resp, nil + default: + return nil, err + } +} + +func (m *defaultAgentRealNameModel) FindPageListByPage(ctx context.Context, builder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*AgentRealName, error) { + + builder = builder.Columns(agentRealNameRows) + + if orderBy == "" { + builder = builder.OrderBy("id DESC") + } else { + builder = builder.OrderBy(orderBy) + } + + if page < 1 { + page = 1 + } + offset := (page - 1) * pageSize + + query, values, err := builder.Where("del_state = ?", globalkey.DelStateNo).Offset(uint64(offset)).Limit(uint64(pageSize)).ToSql() + if err != nil { + return nil, err + } + + var resp []*AgentRealName + err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...) + switch err { + case nil: + return resp, nil + default: + return nil, err + } +} + +func (m *defaultAgentRealNameModel) FindPageListByPageWithTotal(ctx context.Context, builder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*AgentRealName, int64, error) { + + total, err := m.FindCount(ctx, builder, "id") + if err != nil { + return nil, 0, err + } + + builder = builder.Columns(agentRealNameRows) + + if orderBy == "" { + builder = builder.OrderBy("id DESC") + } else { + builder = builder.OrderBy(orderBy) + } + + if page < 1 { + page = 1 + } + offset := (page - 1) * pageSize + + query, values, err := builder.Where("del_state = ?", globalkey.DelStateNo).Offset(uint64(offset)).Limit(uint64(pageSize)).ToSql() + if err != nil { + return nil, total, err + } + + var resp []*AgentRealName + err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...) + switch err { + case nil: + return resp, total, nil + default: + return nil, total, err + } +} + +func (m *defaultAgentRealNameModel) FindPageListByIdDESC(ctx context.Context, builder squirrel.SelectBuilder, preMinId, pageSize int64) ([]*AgentRealName, error) { + + builder = builder.Columns(agentRealNameRows) + + if preMinId > 0 { + builder = builder.Where(" id < ? ", preMinId) + } + + query, values, err := builder.Where("del_state = ?", globalkey.DelStateNo).OrderBy("id DESC").Limit(uint64(pageSize)).ToSql() + if err != nil { + return nil, err + } + + var resp []*AgentRealName + err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...) + switch err { + case nil: + return resp, nil + default: + return nil, err + } +} + +func (m *defaultAgentRealNameModel) FindPageListByIdASC(ctx context.Context, builder squirrel.SelectBuilder, preMaxId, pageSize int64) ([]*AgentRealName, error) { + + builder = builder.Columns(agentRealNameRows) + + if preMaxId > 0 { + builder = builder.Where(" id > ? ", preMaxId) + } + + query, values, err := builder.Where("del_state = ?", globalkey.DelStateNo).OrderBy("id ASC").Limit(uint64(pageSize)).ToSql() + if err != nil { + return nil, err + } + + var resp []*AgentRealName + err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...) + switch err { + case nil: + return resp, nil + default: + return nil, err + } +} + +func (m *defaultAgentRealNameModel) Trans(ctx context.Context, fn func(ctx context.Context, session sqlx.Session) error) error { + + return m.TransactCtx(ctx, func(ctx context.Context, session sqlx.Session) error { + return fn(ctx, session) + }) + +} + +func (m *defaultAgentRealNameModel) SelectBuilder() squirrel.SelectBuilder { + return squirrel.Select().From(m.table) +} +func (m *defaultAgentRealNameModel) Delete(ctx context.Context, session sqlx.Session, id int64) error { + data, err := m.FindOne(ctx, id) + if err != nil { + return err + } + + qncAgentRealNameAgentIdKey := fmt.Sprintf("%s%v", cacheQncAgentRealNameAgentIdPrefix, data.AgentId) + qncAgentRealNameIdKey := fmt.Sprintf("%s%v", cacheQncAgentRealNameIdPrefix, id) + _, err = m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) { + query := fmt.Sprintf("delete from %s where `id` = ?", m.table) + if session != nil { + return session.ExecCtx(ctx, query, id) + } + return conn.ExecCtx(ctx, query, id) + }, qncAgentRealNameAgentIdKey, qncAgentRealNameIdKey) + return err +} +func (m *defaultAgentRealNameModel) formatPrimary(primary interface{}) string { + return fmt.Sprintf("%s%v", cacheQncAgentRealNameIdPrefix, primary) +} +func (m *defaultAgentRealNameModel) queryPrimary(ctx context.Context, conn sqlx.SqlConn, v, primary interface{}) error { + query := fmt.Sprintf("select %s from %s where `id` = ? and del_state = ? limit 1", agentRealNameRows, m.table) + return conn.QueryRowCtx(ctx, v, query, primary, globalkey.DelStateNo) +} + +func (m *defaultAgentRealNameModel) tableName() string { + return m.table +} diff --git a/app/user/model/authorizationFaceModel.go b/app/user/model/authorizationFaceModel.go new file mode 100644 index 0000000..f476e88 --- /dev/null +++ b/app/user/model/authorizationFaceModel.go @@ -0,0 +1,27 @@ +package model + +import ( + "github.com/zeromicro/go-zero/core/stores/cache" + "github.com/zeromicro/go-zero/core/stores/sqlx" +) + +var _ AuthorizationFaceModel = (*customAuthorizationFaceModel)(nil) + +type ( + // AuthorizationFaceModel is an interface to be customized, add more methods here, + // and implement the added methods in customAuthorizationFaceModel. + AuthorizationFaceModel interface { + authorizationFaceModel + } + + customAuthorizationFaceModel struct { + *defaultAuthorizationFaceModel + } +) + +// NewAuthorizationFaceModel returns a model for the database table. +func NewAuthorizationFaceModel(conn sqlx.SqlConn, c cache.CacheConf) AuthorizationFaceModel { + return &customAuthorizationFaceModel{ + defaultAuthorizationFaceModel: newAuthorizationFaceModel(conn, c), + } +} diff --git a/app/user/model/authorizationFaceModel_gen.go b/app/user/model/authorizationFaceModel_gen.go new file mode 100644 index 0000000..2d077a5 --- /dev/null +++ b/app/user/model/authorizationFaceModel_gen.go @@ -0,0 +1,439 @@ +// Code generated by goctl. DO NOT EDIT! + +package model + +import ( + "context" + "database/sql" + "fmt" + "strings" + + "time" + + "github.com/Masterminds/squirrel" + "github.com/pkg/errors" + "github.com/zeromicro/go-zero/core/stores/builder" + "github.com/zeromicro/go-zero/core/stores/cache" + "github.com/zeromicro/go-zero/core/stores/sqlc" + "github.com/zeromicro/go-zero/core/stores/sqlx" + "github.com/zeromicro/go-zero/core/stringx" + "qnc-server/common/globalkey" +) + +var ( + authorizationFaceFieldNames = builder.RawFieldNames(&AuthorizationFace{}) + authorizationFaceRows = strings.Join(authorizationFaceFieldNames, ",") + authorizationFaceRowsExpectAutoSet = strings.Join(stringx.Remove(authorizationFaceFieldNames, "`id`", "`create_time`", "`update_time`"), ",") + authorizationFaceRowsWithPlaceHolder = strings.Join(stringx.Remove(authorizationFaceFieldNames, "`id`", "`create_time`", "`update_time`"), "=?,") + "=?" + + cacheQncAuthorizationFaceIdPrefix = "cache:qnc:authorizationFace:id:" + cacheQncAuthorizationFaceCertifyIdPrefix = "cache:qnc:authorizationFace:certifyId:" + cacheQncAuthorizationFaceOuterOrderNoPrefix = "cache:qnc:authorizationFace:outerOrderNo:" +) + +type ( + authorizationFaceModel interface { + Insert(ctx context.Context, session sqlx.Session, data *AuthorizationFace) (sql.Result, error) + FindOne(ctx context.Context, id int64) (*AuthorizationFace, error) + FindOneByCertifyId(ctx context.Context, certifyId string) (*AuthorizationFace, error) + FindOneByOuterOrderNo(ctx context.Context, outerOrderNo string) (*AuthorizationFace, error) + Update(ctx context.Context, session sqlx.Session, data *AuthorizationFace) (sql.Result, error) + UpdateWithVersion(ctx context.Context, session sqlx.Session, data *AuthorizationFace) error + Trans(ctx context.Context, fn func(context context.Context, session sqlx.Session) error) error + SelectBuilder() squirrel.SelectBuilder + DeleteSoft(ctx context.Context, session sqlx.Session, data *AuthorizationFace) error + FindSum(ctx context.Context, sumBuilder squirrel.SelectBuilder, field string) (float64, error) + FindCount(ctx context.Context, countBuilder squirrel.SelectBuilder, field string) (int64, error) + FindAll(ctx context.Context, rowBuilder squirrel.SelectBuilder, orderBy string) ([]*AuthorizationFace, error) + FindPageListByPage(ctx context.Context, rowBuilder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*AuthorizationFace, error) + FindPageListByPageWithTotal(ctx context.Context, rowBuilder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*AuthorizationFace, int64, error) + FindPageListByIdDESC(ctx context.Context, rowBuilder squirrel.SelectBuilder, preMinId, pageSize int64) ([]*AuthorizationFace, error) + FindPageListByIdASC(ctx context.Context, rowBuilder squirrel.SelectBuilder, preMaxId, pageSize int64) ([]*AuthorizationFace, error) + Delete(ctx context.Context, session sqlx.Session, id int64) error + } + + defaultAuthorizationFaceModel struct { + sqlc.CachedConn + table string + } + + AuthorizationFace struct { + Id int64 `db:"id"` + AuthorizationId int64 `db:"authorization_id"` // 关联授权主表ID + CertifyId string `db:"certify_id"` + OuterOrderNo string `db:"outer_order_no"` // 外部业务流水号 + CertifyUrl string `db:"certify_url"` + CertifyUrlExpireAt time.Time `db:"certify_url_expire_at"` // 认证链接过期时间 + Status string `db:"status"` // 认证状态:pending待认证、success成功、fail失败 + ResultMsg sql.NullString `db:"result_msg"` + PdfUrl sql.NullString `db:"pdf_url"` + CreateTime time.Time `db:"create_time"` + UpdateTime time.Time `db:"update_time"` + DeleteTime sql.NullTime `db:"delete_time"` // 删除时间 + DelState int64 `db:"del_state"` + Version int64 `db:"version"` // 版本号 + } +) + +func newAuthorizationFaceModel(conn sqlx.SqlConn, c cache.CacheConf) *defaultAuthorizationFaceModel { + return &defaultAuthorizationFaceModel{ + CachedConn: sqlc.NewConn(conn, c), + table: "`authorization_face`", + } +} + +func (m *defaultAuthorizationFaceModel) Insert(ctx context.Context, session sqlx.Session, data *AuthorizationFace) (sql.Result, error) { + data.DelState = globalkey.DelStateNo + qncAuthorizationFaceCertifyIdKey := fmt.Sprintf("%s%v", cacheQncAuthorizationFaceCertifyIdPrefix, data.CertifyId) + qncAuthorizationFaceIdKey := fmt.Sprintf("%s%v", cacheQncAuthorizationFaceIdPrefix, data.Id) + qncAuthorizationFaceOuterOrderNoKey := fmt.Sprintf("%s%v", cacheQncAuthorizationFaceOuterOrderNoPrefix, data.OuterOrderNo) + return m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) { + query := fmt.Sprintf("insert into %s (%s) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", m.table, authorizationFaceRowsExpectAutoSet) + if session != nil { + return session.ExecCtx(ctx, query, data.AuthorizationId, data.CertifyId, data.OuterOrderNo, data.CertifyUrl, data.CertifyUrlExpireAt, data.Status, data.ResultMsg, data.PdfUrl, data.DeleteTime, data.DelState, data.Version) + } + return conn.ExecCtx(ctx, query, data.AuthorizationId, data.CertifyId, data.OuterOrderNo, data.CertifyUrl, data.CertifyUrlExpireAt, data.Status, data.ResultMsg, data.PdfUrl, data.DeleteTime, data.DelState, data.Version) + }, qncAuthorizationFaceCertifyIdKey, qncAuthorizationFaceIdKey, qncAuthorizationFaceOuterOrderNoKey) +} + +func (m *defaultAuthorizationFaceModel) FindOne(ctx context.Context, id int64) (*AuthorizationFace, error) { + qncAuthorizationFaceIdKey := fmt.Sprintf("%s%v", cacheQncAuthorizationFaceIdPrefix, id) + var resp AuthorizationFace + err := m.QueryRowCtx(ctx, &resp, qncAuthorizationFaceIdKey, func(ctx context.Context, conn sqlx.SqlConn, v interface{}) error { + query := fmt.Sprintf("select %s from %s where `id` = ? and del_state = ? limit 1", authorizationFaceRows, m.table) + return conn.QueryRowCtx(ctx, v, query, id, globalkey.DelStateNo) + }) + switch err { + case nil: + return &resp, nil + case sqlc.ErrNotFound: + return nil, ErrNotFound + default: + return nil, err + } +} + +func (m *defaultAuthorizationFaceModel) FindOneByCertifyId(ctx context.Context, certifyId string) (*AuthorizationFace, error) { + qncAuthorizationFaceCertifyIdKey := fmt.Sprintf("%s%v", cacheQncAuthorizationFaceCertifyIdPrefix, certifyId) + var resp AuthorizationFace + err := m.QueryRowIndexCtx(ctx, &resp, qncAuthorizationFaceCertifyIdKey, m.formatPrimary, func(ctx context.Context, conn sqlx.SqlConn, v interface{}) (i interface{}, e error) { + query := fmt.Sprintf("select %s from %s where `certify_id` = ? and del_state = ? limit 1", authorizationFaceRows, m.table) + if err := conn.QueryRowCtx(ctx, &resp, query, certifyId, globalkey.DelStateNo); err != nil { + return nil, err + } + return resp.Id, nil + }, m.queryPrimary) + switch err { + case nil: + return &resp, nil + case sqlc.ErrNotFound: + return nil, ErrNotFound + default: + return nil, err + } +} + +func (m *defaultAuthorizationFaceModel) FindOneByOuterOrderNo(ctx context.Context, outerOrderNo string) (*AuthorizationFace, error) { + qncAuthorizationFaceOuterOrderNoKey := fmt.Sprintf("%s%v", cacheQncAuthorizationFaceOuterOrderNoPrefix, outerOrderNo) + var resp AuthorizationFace + err := m.QueryRowIndexCtx(ctx, &resp, qncAuthorizationFaceOuterOrderNoKey, m.formatPrimary, func(ctx context.Context, conn sqlx.SqlConn, v interface{}) (i interface{}, e error) { + query := fmt.Sprintf("select %s from %s where `outer_order_no` = ? and del_state = ? limit 1", authorizationFaceRows, m.table) + if err := conn.QueryRowCtx(ctx, &resp, query, outerOrderNo, globalkey.DelStateNo); err != nil { + return nil, err + } + return resp.Id, nil + }, m.queryPrimary) + switch err { + case nil: + return &resp, nil + case sqlc.ErrNotFound: + return nil, ErrNotFound + default: + return nil, err + } +} + +func (m *defaultAuthorizationFaceModel) Update(ctx context.Context, session sqlx.Session, newData *AuthorizationFace) (sql.Result, error) { + data, err := m.FindOne(ctx, newData.Id) + if err != nil { + return nil, err + } + qncAuthorizationFaceCertifyIdKey := fmt.Sprintf("%s%v", cacheQncAuthorizationFaceCertifyIdPrefix, data.CertifyId) + qncAuthorizationFaceIdKey := fmt.Sprintf("%s%v", cacheQncAuthorizationFaceIdPrefix, data.Id) + qncAuthorizationFaceOuterOrderNoKey := fmt.Sprintf("%s%v", cacheQncAuthorizationFaceOuterOrderNoPrefix, data.OuterOrderNo) + return m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) { + query := fmt.Sprintf("update %s set %s where `id` = ?", m.table, authorizationFaceRowsWithPlaceHolder) + if session != nil { + return session.ExecCtx(ctx, query, newData.AuthorizationId, newData.CertifyId, newData.OuterOrderNo, newData.CertifyUrl, newData.CertifyUrlExpireAt, newData.Status, newData.ResultMsg, newData.PdfUrl, newData.DeleteTime, newData.DelState, newData.Version, newData.Id) + } + return conn.ExecCtx(ctx, query, newData.AuthorizationId, newData.CertifyId, newData.OuterOrderNo, newData.CertifyUrl, newData.CertifyUrlExpireAt, newData.Status, newData.ResultMsg, newData.PdfUrl, newData.DeleteTime, newData.DelState, newData.Version, newData.Id) + }, qncAuthorizationFaceCertifyIdKey, qncAuthorizationFaceIdKey, qncAuthorizationFaceOuterOrderNoKey) +} + +func (m *defaultAuthorizationFaceModel) UpdateWithVersion(ctx context.Context, session sqlx.Session, newData *AuthorizationFace) error { + + oldVersion := newData.Version + newData.Version += 1 + + var sqlResult sql.Result + var err error + + data, err := m.FindOne(ctx, newData.Id) + if err != nil { + return err + } + qncAuthorizationFaceCertifyIdKey := fmt.Sprintf("%s%v", cacheQncAuthorizationFaceCertifyIdPrefix, data.CertifyId) + qncAuthorizationFaceIdKey := fmt.Sprintf("%s%v", cacheQncAuthorizationFaceIdPrefix, data.Id) + qncAuthorizationFaceOuterOrderNoKey := fmt.Sprintf("%s%v", cacheQncAuthorizationFaceOuterOrderNoPrefix, data.OuterOrderNo) + sqlResult, err = m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) { + query := fmt.Sprintf("update %s set %s where `id` = ? and version = ? ", m.table, authorizationFaceRowsWithPlaceHolder) + if session != nil { + return session.ExecCtx(ctx, query, newData.AuthorizationId, newData.CertifyId, newData.OuterOrderNo, newData.CertifyUrl, newData.CertifyUrlExpireAt, newData.Status, newData.ResultMsg, newData.PdfUrl, newData.DeleteTime, newData.DelState, newData.Version, newData.Id, oldVersion) + } + return conn.ExecCtx(ctx, query, newData.AuthorizationId, newData.CertifyId, newData.OuterOrderNo, newData.CertifyUrl, newData.CertifyUrlExpireAt, newData.Status, newData.ResultMsg, newData.PdfUrl, newData.DeleteTime, newData.DelState, newData.Version, newData.Id, oldVersion) + }, qncAuthorizationFaceCertifyIdKey, qncAuthorizationFaceIdKey, qncAuthorizationFaceOuterOrderNoKey) + if err != nil { + return err + } + updateCount, err := sqlResult.RowsAffected() + if err != nil { + return err + } + if updateCount == 0 { + return ErrNoRowsUpdate + } + + return nil +} + +func (m *defaultAuthorizationFaceModel) DeleteSoft(ctx context.Context, session sqlx.Session, data *AuthorizationFace) error { + data.DelState = globalkey.DelStateYes + data.DeleteTime = sql.NullTime{Time: time.Now(), Valid: true} + if err := m.UpdateWithVersion(ctx, session, data); err != nil { + return errors.Wrapf(errors.New("delete soft failed "), "AuthorizationFaceModel delete err : %+v", err) + } + return nil +} + +func (m *defaultAuthorizationFaceModel) FindSum(ctx context.Context, builder squirrel.SelectBuilder, field string) (float64, error) { + + if len(field) == 0 { + return 0, errors.Wrapf(errors.New("FindSum Least One Field"), "FindSum Least One Field") + } + + builder = builder.Columns("IFNULL(SUM(" + field + "),0)") + + query, values, err := builder.Where("del_state = ?", globalkey.DelStateNo).ToSql() + if err != nil { + return 0, err + } + + var resp float64 + err = m.QueryRowNoCacheCtx(ctx, &resp, query, values...) + switch err { + case nil: + return resp, nil + default: + return 0, err + } +} + +func (m *defaultAuthorizationFaceModel) FindCount(ctx context.Context, builder squirrel.SelectBuilder, field string) (int64, error) { + + if len(field) == 0 { + return 0, errors.Wrapf(errors.New("FindCount Least One Field"), "FindCount Least One Field") + } + + builder = builder.Columns("COUNT(" + field + ")") + + query, values, err := builder.Where("del_state = ?", globalkey.DelStateNo).ToSql() + if err != nil { + return 0, err + } + + var resp int64 + err = m.QueryRowNoCacheCtx(ctx, &resp, query, values...) + switch err { + case nil: + return resp, nil + default: + return 0, err + } +} + +func (m *defaultAuthorizationFaceModel) FindAll(ctx context.Context, builder squirrel.SelectBuilder, orderBy string) ([]*AuthorizationFace, error) { + + builder = builder.Columns(authorizationFaceRows) + + if orderBy == "" { + builder = builder.OrderBy("id DESC") + } else { + builder = builder.OrderBy(orderBy) + } + + query, values, err := builder.Where("del_state = ?", globalkey.DelStateNo).ToSql() + if err != nil { + return nil, err + } + + var resp []*AuthorizationFace + err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...) + switch err { + case nil: + return resp, nil + default: + return nil, err + } +} + +func (m *defaultAuthorizationFaceModel) FindPageListByPage(ctx context.Context, builder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*AuthorizationFace, error) { + + builder = builder.Columns(authorizationFaceRows) + + if orderBy == "" { + builder = builder.OrderBy("id DESC") + } else { + builder = builder.OrderBy(orderBy) + } + + if page < 1 { + page = 1 + } + offset := (page - 1) * pageSize + + query, values, err := builder.Where("del_state = ?", globalkey.DelStateNo).Offset(uint64(offset)).Limit(uint64(pageSize)).ToSql() + if err != nil { + return nil, err + } + + var resp []*AuthorizationFace + err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...) + switch err { + case nil: + return resp, nil + default: + return nil, err + } +} + +func (m *defaultAuthorizationFaceModel) FindPageListByPageWithTotal(ctx context.Context, builder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*AuthorizationFace, int64, error) { + + total, err := m.FindCount(ctx, builder, "id") + if err != nil { + return nil, 0, err + } + + builder = builder.Columns(authorizationFaceRows) + + if orderBy == "" { + builder = builder.OrderBy("id DESC") + } else { + builder = builder.OrderBy(orderBy) + } + + if page < 1 { + page = 1 + } + offset := (page - 1) * pageSize + + query, values, err := builder.Where("del_state = ?", globalkey.DelStateNo).Offset(uint64(offset)).Limit(uint64(pageSize)).ToSql() + if err != nil { + return nil, total, err + } + + var resp []*AuthorizationFace + err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...) + switch err { + case nil: + return resp, total, nil + default: + return nil, total, err + } +} + +func (m *defaultAuthorizationFaceModel) FindPageListByIdDESC(ctx context.Context, builder squirrel.SelectBuilder, preMinId, pageSize int64) ([]*AuthorizationFace, error) { + + builder = builder.Columns(authorizationFaceRows) + + if preMinId > 0 { + builder = builder.Where(" id < ? ", preMinId) + } + + query, values, err := builder.Where("del_state = ?", globalkey.DelStateNo).OrderBy("id DESC").Limit(uint64(pageSize)).ToSql() + if err != nil { + return nil, err + } + + var resp []*AuthorizationFace + err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...) + switch err { + case nil: + return resp, nil + default: + return nil, err + } +} + +func (m *defaultAuthorizationFaceModel) FindPageListByIdASC(ctx context.Context, builder squirrel.SelectBuilder, preMaxId, pageSize int64) ([]*AuthorizationFace, error) { + + builder = builder.Columns(authorizationFaceRows) + + if preMaxId > 0 { + builder = builder.Where(" id > ? ", preMaxId) + } + + query, values, err := builder.Where("del_state = ?", globalkey.DelStateNo).OrderBy("id ASC").Limit(uint64(pageSize)).ToSql() + if err != nil { + return nil, err + } + + var resp []*AuthorizationFace + err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...) + switch err { + case nil: + return resp, nil + default: + return nil, err + } +} + +func (m *defaultAuthorizationFaceModel) Trans(ctx context.Context, fn func(ctx context.Context, session sqlx.Session) error) error { + + return m.TransactCtx(ctx, func(ctx context.Context, session sqlx.Session) error { + return fn(ctx, session) + }) + +} + +func (m *defaultAuthorizationFaceModel) SelectBuilder() squirrel.SelectBuilder { + return squirrel.Select().From(m.table) +} +func (m *defaultAuthorizationFaceModel) Delete(ctx context.Context, session sqlx.Session, id int64) error { + data, err := m.FindOne(ctx, id) + if err != nil { + return err + } + + qncAuthorizationFaceCertifyIdKey := fmt.Sprintf("%s%v", cacheQncAuthorizationFaceCertifyIdPrefix, data.CertifyId) + qncAuthorizationFaceIdKey := fmt.Sprintf("%s%v", cacheQncAuthorizationFaceIdPrefix, id) + qncAuthorizationFaceOuterOrderNoKey := fmt.Sprintf("%s%v", cacheQncAuthorizationFaceOuterOrderNoPrefix, data.OuterOrderNo) + _, err = m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) { + query := fmt.Sprintf("delete from %s where `id` = ?", m.table) + if session != nil { + return session.ExecCtx(ctx, query, id) + } + return conn.ExecCtx(ctx, query, id) + }, qncAuthorizationFaceCertifyIdKey, qncAuthorizationFaceIdKey, qncAuthorizationFaceOuterOrderNoKey) + return err +} +func (m *defaultAuthorizationFaceModel) formatPrimary(primary interface{}) string { + return fmt.Sprintf("%s%v", cacheQncAuthorizationFaceIdPrefix, primary) +} +func (m *defaultAuthorizationFaceModel) queryPrimary(ctx context.Context, conn sqlx.SqlConn, v, primary interface{}) error { + query := fmt.Sprintf("select %s from %s where `id` = ? and del_state = ? limit 1", authorizationFaceRows, m.table) + return conn.QueryRowCtx(ctx, v, query, primary, globalkey.DelStateNo) +} + +func (m *defaultAuthorizationFaceModel) tableName() string { + return m.table +} diff --git a/app/user/model/authorizationModel.go b/app/user/model/authorizationModel.go new file mode 100644 index 0000000..3fd3d96 --- /dev/null +++ b/app/user/model/authorizationModel.go @@ -0,0 +1,27 @@ +package model + +import ( + "github.com/zeromicro/go-zero/core/stores/cache" + "github.com/zeromicro/go-zero/core/stores/sqlx" +) + +var _ AuthorizationModel = (*customAuthorizationModel)(nil) + +type ( + // AuthorizationModel is an interface to be customized, add more methods here, + // and implement the added methods in customAuthorizationModel. + AuthorizationModel interface { + authorizationModel + } + + customAuthorizationModel struct { + *defaultAuthorizationModel + } +) + +// NewAuthorizationModel returns a model for the database table. +func NewAuthorizationModel(conn sqlx.SqlConn, c cache.CacheConf) AuthorizationModel { + return &customAuthorizationModel{ + defaultAuthorizationModel: newAuthorizationModel(conn, c), + } +} diff --git a/app/user/model/authorizationModel_gen.go b/app/user/model/authorizationModel_gen.go new file mode 100644 index 0000000..da099d9 --- /dev/null +++ b/app/user/model/authorizationModel_gen.go @@ -0,0 +1,412 @@ +// Code generated by goctl. DO NOT EDIT! + +package model + +import ( + "context" + "database/sql" + "fmt" + "strings" + + "time" + + "github.com/Masterminds/squirrel" + "github.com/pkg/errors" + "github.com/zeromicro/go-zero/core/stores/builder" + "github.com/zeromicro/go-zero/core/stores/cache" + "github.com/zeromicro/go-zero/core/stores/sqlc" + "github.com/zeromicro/go-zero/core/stores/sqlx" + "github.com/zeromicro/go-zero/core/stringx" + "qnc-server/common/globalkey" +) + +var ( + authorizationFieldNames = builder.RawFieldNames(&Authorization{}) + authorizationRows = strings.Join(authorizationFieldNames, ",") + authorizationRowsExpectAutoSet = strings.Join(stringx.Remove(authorizationFieldNames, "`id`", "`create_time`", "`update_time`"), ",") + authorizationRowsWithPlaceHolder = strings.Join(stringx.Remove(authorizationFieldNames, "`id`", "`create_time`", "`update_time`"), "=?,") + "=?" + + cacheQncAuthorizationIdPrefix = "cache:qnc:authorization:id:" + cacheQncAuthorizationOrderIdPrefix = "cache:qnc:authorization:orderId:" +) + +type ( + authorizationModel interface { + Insert(ctx context.Context, session sqlx.Session, data *Authorization) (sql.Result, error) + FindOne(ctx context.Context, id int64) (*Authorization, error) + FindOneByOrderId(ctx context.Context, orderId int64) (*Authorization, error) + Update(ctx context.Context, session sqlx.Session, data *Authorization) (sql.Result, error) + UpdateWithVersion(ctx context.Context, session sqlx.Session, data *Authorization) error + Trans(ctx context.Context, fn func(context context.Context, session sqlx.Session) error) error + SelectBuilder() squirrel.SelectBuilder + DeleteSoft(ctx context.Context, session sqlx.Session, data *Authorization) error + FindSum(ctx context.Context, sumBuilder squirrel.SelectBuilder, field string) (float64, error) + FindCount(ctx context.Context, countBuilder squirrel.SelectBuilder, field string) (int64, error) + FindAll(ctx context.Context, rowBuilder squirrel.SelectBuilder, orderBy string) ([]*Authorization, error) + FindPageListByPage(ctx context.Context, rowBuilder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*Authorization, error) + FindPageListByPageWithTotal(ctx context.Context, rowBuilder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*Authorization, int64, error) + FindPageListByIdDESC(ctx context.Context, rowBuilder squirrel.SelectBuilder, preMinId, pageSize int64) ([]*Authorization, error) + FindPageListByIdASC(ctx context.Context, rowBuilder squirrel.SelectBuilder, preMaxId, pageSize int64) ([]*Authorization, error) + Delete(ctx context.Context, session sqlx.Session, id int64) error + } + + defaultAuthorizationModel struct { + sqlc.CachedConn + table string + } + + Authorization struct { + Id int64 `db:"id"` + OrderId int64 `db:"order_id"` + GrantType string `db:"grant_type"` // 授权类型:face人脸,后续可扩展 + AuthType sql.NullInt64 `db:"auth_type"` // 1本人,2他人 + UserId int64 `db:"user_id"` + TargetName string `db:"target_name"` + TargetIdcard string `db:"target_idcard"` + Status string `db:"status"` // 授权状态:pending待授权、success已授权、expired已失效、revoked已撤销 + CreateTime time.Time `db:"create_time"` + UpdateTime time.Time `db:"update_time"` + DeleteTime sql.NullTime `db:"delete_time"` // 删除时间 + DelState int64 `db:"del_state"` + Version int64 `db:"version"` // 版本号 + } +) + +func newAuthorizationModel(conn sqlx.SqlConn, c cache.CacheConf) *defaultAuthorizationModel { + return &defaultAuthorizationModel{ + CachedConn: sqlc.NewConn(conn, c), + table: "`authorization`", + } +} + +func (m *defaultAuthorizationModel) Insert(ctx context.Context, session sqlx.Session, data *Authorization) (sql.Result, error) { + data.DelState = globalkey.DelStateNo + qncAuthorizationIdKey := fmt.Sprintf("%s%v", cacheQncAuthorizationIdPrefix, data.Id) + qncAuthorizationOrderIdKey := fmt.Sprintf("%s%v", cacheQncAuthorizationOrderIdPrefix, data.OrderId) + return m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) { + query := fmt.Sprintf("insert into %s (%s) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", m.table, authorizationRowsExpectAutoSet) + if session != nil { + return session.ExecCtx(ctx, query, data.OrderId, data.GrantType, data.AuthType, data.UserId, data.TargetName, data.TargetIdcard, data.Status, data.DeleteTime, data.DelState, data.Version) + } + return conn.ExecCtx(ctx, query, data.OrderId, data.GrantType, data.AuthType, data.UserId, data.TargetName, data.TargetIdcard, data.Status, data.DeleteTime, data.DelState, data.Version) + }, qncAuthorizationIdKey, qncAuthorizationOrderIdKey) +} + +func (m *defaultAuthorizationModel) FindOne(ctx context.Context, id int64) (*Authorization, error) { + qncAuthorizationIdKey := fmt.Sprintf("%s%v", cacheQncAuthorizationIdPrefix, id) + var resp Authorization + err := m.QueryRowCtx(ctx, &resp, qncAuthorizationIdKey, func(ctx context.Context, conn sqlx.SqlConn, v interface{}) error { + query := fmt.Sprintf("select %s from %s where `id` = ? and del_state = ? limit 1", authorizationRows, m.table) + return conn.QueryRowCtx(ctx, v, query, id, globalkey.DelStateNo) + }) + switch err { + case nil: + return &resp, nil + case sqlc.ErrNotFound: + return nil, ErrNotFound + default: + return nil, err + } +} + +func (m *defaultAuthorizationModel) FindOneByOrderId(ctx context.Context, orderId int64) (*Authorization, error) { + qncAuthorizationOrderIdKey := fmt.Sprintf("%s%v", cacheQncAuthorizationOrderIdPrefix, orderId) + var resp Authorization + err := m.QueryRowIndexCtx(ctx, &resp, qncAuthorizationOrderIdKey, m.formatPrimary, func(ctx context.Context, conn sqlx.SqlConn, v interface{}) (i interface{}, e error) { + query := fmt.Sprintf("select %s from %s where `order_id` = ? and del_state = ? limit 1", authorizationRows, m.table) + if err := conn.QueryRowCtx(ctx, &resp, query, orderId, globalkey.DelStateNo); err != nil { + return nil, err + } + return resp.Id, nil + }, m.queryPrimary) + switch err { + case nil: + return &resp, nil + case sqlc.ErrNotFound: + return nil, ErrNotFound + default: + return nil, err + } +} + +func (m *defaultAuthorizationModel) Update(ctx context.Context, session sqlx.Session, newData *Authorization) (sql.Result, error) { + data, err := m.FindOne(ctx, newData.Id) + if err != nil { + return nil, err + } + qncAuthorizationIdKey := fmt.Sprintf("%s%v", cacheQncAuthorizationIdPrefix, data.Id) + qncAuthorizationOrderIdKey := fmt.Sprintf("%s%v", cacheQncAuthorizationOrderIdPrefix, data.OrderId) + return m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) { + query := fmt.Sprintf("update %s set %s where `id` = ?", m.table, authorizationRowsWithPlaceHolder) + if session != nil { + return session.ExecCtx(ctx, query, newData.OrderId, newData.GrantType, newData.AuthType, newData.UserId, newData.TargetName, newData.TargetIdcard, newData.Status, newData.DeleteTime, newData.DelState, newData.Version, newData.Id) + } + return conn.ExecCtx(ctx, query, newData.OrderId, newData.GrantType, newData.AuthType, newData.UserId, newData.TargetName, newData.TargetIdcard, newData.Status, newData.DeleteTime, newData.DelState, newData.Version, newData.Id) + }, qncAuthorizationIdKey, qncAuthorizationOrderIdKey) +} + +func (m *defaultAuthorizationModel) UpdateWithVersion(ctx context.Context, session sqlx.Session, newData *Authorization) error { + + oldVersion := newData.Version + newData.Version += 1 + + var sqlResult sql.Result + var err error + + data, err := m.FindOne(ctx, newData.Id) + if err != nil { + return err + } + qncAuthorizationIdKey := fmt.Sprintf("%s%v", cacheQncAuthorizationIdPrefix, data.Id) + qncAuthorizationOrderIdKey := fmt.Sprintf("%s%v", cacheQncAuthorizationOrderIdPrefix, data.OrderId) + sqlResult, err = m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) { + query := fmt.Sprintf("update %s set %s where `id` = ? and version = ? ", m.table, authorizationRowsWithPlaceHolder) + if session != nil { + return session.ExecCtx(ctx, query, newData.OrderId, newData.GrantType, newData.AuthType, newData.UserId, newData.TargetName, newData.TargetIdcard, newData.Status, newData.DeleteTime, newData.DelState, newData.Version, newData.Id, oldVersion) + } + return conn.ExecCtx(ctx, query, newData.OrderId, newData.GrantType, newData.AuthType, newData.UserId, newData.TargetName, newData.TargetIdcard, newData.Status, newData.DeleteTime, newData.DelState, newData.Version, newData.Id, oldVersion) + }, qncAuthorizationIdKey, qncAuthorizationOrderIdKey) + if err != nil { + return err + } + updateCount, err := sqlResult.RowsAffected() + if err != nil { + return err + } + if updateCount == 0 { + return ErrNoRowsUpdate + } + + return nil +} + +func (m *defaultAuthorizationModel) DeleteSoft(ctx context.Context, session sqlx.Session, data *Authorization) error { + data.DelState = globalkey.DelStateYes + data.DeleteTime = sql.NullTime{Time: time.Now(), Valid: true} + if err := m.UpdateWithVersion(ctx, session, data); err != nil { + return errors.Wrapf(errors.New("delete soft failed "), "AuthorizationModel delete err : %+v", err) + } + return nil +} + +func (m *defaultAuthorizationModel) FindSum(ctx context.Context, builder squirrel.SelectBuilder, field string) (float64, error) { + + if len(field) == 0 { + return 0, errors.Wrapf(errors.New("FindSum Least One Field"), "FindSum Least One Field") + } + + builder = builder.Columns("IFNULL(SUM(" + field + "),0)") + + query, values, err := builder.Where("del_state = ?", globalkey.DelStateNo).ToSql() + if err != nil { + return 0, err + } + + var resp float64 + err = m.QueryRowNoCacheCtx(ctx, &resp, query, values...) + switch err { + case nil: + return resp, nil + default: + return 0, err + } +} + +func (m *defaultAuthorizationModel) FindCount(ctx context.Context, builder squirrel.SelectBuilder, field string) (int64, error) { + + if len(field) == 0 { + return 0, errors.Wrapf(errors.New("FindCount Least One Field"), "FindCount Least One Field") + } + + builder = builder.Columns("COUNT(" + field + ")") + + query, values, err := builder.Where("del_state = ?", globalkey.DelStateNo).ToSql() + if err != nil { + return 0, err + } + + var resp int64 + err = m.QueryRowNoCacheCtx(ctx, &resp, query, values...) + switch err { + case nil: + return resp, nil + default: + return 0, err + } +} + +func (m *defaultAuthorizationModel) FindAll(ctx context.Context, builder squirrel.SelectBuilder, orderBy string) ([]*Authorization, error) { + + builder = builder.Columns(authorizationRows) + + if orderBy == "" { + builder = builder.OrderBy("id DESC") + } else { + builder = builder.OrderBy(orderBy) + } + + query, values, err := builder.Where("del_state = ?", globalkey.DelStateNo).ToSql() + if err != nil { + return nil, err + } + + var resp []*Authorization + err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...) + switch err { + case nil: + return resp, nil + default: + return nil, err + } +} + +func (m *defaultAuthorizationModel) FindPageListByPage(ctx context.Context, builder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*Authorization, error) { + + builder = builder.Columns(authorizationRows) + + if orderBy == "" { + builder = builder.OrderBy("id DESC") + } else { + builder = builder.OrderBy(orderBy) + } + + if page < 1 { + page = 1 + } + offset := (page - 1) * pageSize + + query, values, err := builder.Where("del_state = ?", globalkey.DelStateNo).Offset(uint64(offset)).Limit(uint64(pageSize)).ToSql() + if err != nil { + return nil, err + } + + var resp []*Authorization + err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...) + switch err { + case nil: + return resp, nil + default: + return nil, err + } +} + +func (m *defaultAuthorizationModel) FindPageListByPageWithTotal(ctx context.Context, builder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*Authorization, int64, error) { + + total, err := m.FindCount(ctx, builder, "id") + if err != nil { + return nil, 0, err + } + + builder = builder.Columns(authorizationRows) + + if orderBy == "" { + builder = builder.OrderBy("id DESC") + } else { + builder = builder.OrderBy(orderBy) + } + + if page < 1 { + page = 1 + } + offset := (page - 1) * pageSize + + query, values, err := builder.Where("del_state = ?", globalkey.DelStateNo).Offset(uint64(offset)).Limit(uint64(pageSize)).ToSql() + if err != nil { + return nil, total, err + } + + var resp []*Authorization + err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...) + switch err { + case nil: + return resp, total, nil + default: + return nil, total, err + } +} + +func (m *defaultAuthorizationModel) FindPageListByIdDESC(ctx context.Context, builder squirrel.SelectBuilder, preMinId, pageSize int64) ([]*Authorization, error) { + + builder = builder.Columns(authorizationRows) + + if preMinId > 0 { + builder = builder.Where(" id < ? ", preMinId) + } + + query, values, err := builder.Where("del_state = ?", globalkey.DelStateNo).OrderBy("id DESC").Limit(uint64(pageSize)).ToSql() + if err != nil { + return nil, err + } + + var resp []*Authorization + err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...) + switch err { + case nil: + return resp, nil + default: + return nil, err + } +} + +func (m *defaultAuthorizationModel) FindPageListByIdASC(ctx context.Context, builder squirrel.SelectBuilder, preMaxId, pageSize int64) ([]*Authorization, error) { + + builder = builder.Columns(authorizationRows) + + if preMaxId > 0 { + builder = builder.Where(" id > ? ", preMaxId) + } + + query, values, err := builder.Where("del_state = ?", globalkey.DelStateNo).OrderBy("id ASC").Limit(uint64(pageSize)).ToSql() + if err != nil { + return nil, err + } + + var resp []*Authorization + err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...) + switch err { + case nil: + return resp, nil + default: + return nil, err + } +} + +func (m *defaultAuthorizationModel) Trans(ctx context.Context, fn func(ctx context.Context, session sqlx.Session) error) error { + + return m.TransactCtx(ctx, func(ctx context.Context, session sqlx.Session) error { + return fn(ctx, session) + }) + +} + +func (m *defaultAuthorizationModel) SelectBuilder() squirrel.SelectBuilder { + return squirrel.Select().From(m.table) +} +func (m *defaultAuthorizationModel) Delete(ctx context.Context, session sqlx.Session, id int64) error { + data, err := m.FindOne(ctx, id) + if err != nil { + return err + } + + qncAuthorizationIdKey := fmt.Sprintf("%s%v", cacheQncAuthorizationIdPrefix, id) + qncAuthorizationOrderIdKey := fmt.Sprintf("%s%v", cacheQncAuthorizationOrderIdPrefix, data.OrderId) + _, err = m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) { + query := fmt.Sprintf("delete from %s where `id` = ?", m.table) + if session != nil { + return session.ExecCtx(ctx, query, id) + } + return conn.ExecCtx(ctx, query, id) + }, qncAuthorizationIdKey, qncAuthorizationOrderIdKey) + return err +} +func (m *defaultAuthorizationModel) formatPrimary(primary interface{}) string { + return fmt.Sprintf("%s%v", cacheQncAuthorizationIdPrefix, primary) +} +func (m *defaultAuthorizationModel) queryPrimary(ctx context.Context, conn sqlx.SqlConn, v, primary interface{}) error { + query := fmt.Sprintf("select %s from %s where `id` = ? and del_state = ? limit 1", authorizationRows, m.table) + return conn.QueryRowCtx(ctx, v, query, primary, globalkey.DelStateNo) +} + +func (m *defaultAuthorizationModel) tableName() string { + return m.table +} diff --git a/app/user/model/vars.go b/app/user/model/vars.go index 84ef412..cb455aa 100644 --- a/app/user/model/vars.go +++ b/app/user/model/vars.go @@ -46,3 +46,27 @@ const ( QueryStateSuccess = "success" QueryStateProcessing = "processing" ) + +const ( + GrantTypeFace string = "face" +) +const ( + AuthorizationStatusPending = "pending" + AuthorizationStatusSuccess = "success" + AuthorizationStatusFailed = "failed" + AuthorizationStatusExpired = "expired" + AuthorizationStatusRevoked = "revoked" + AuthorizationStatusRejected = "rejected" +) + +const ( + AuthorizationFaceStatusPending = "pending" + AuthorizationFaceStatusSuccess = "success" + AuthorizationFaceStatusFailed = "failed" +) + +const ( + AgentRealNameStatusPending = "pending" + AgentRealNameStatusApproved = "approved" + AgentRealNameStatusRejected = "rejected" +) diff --git a/common/jwt/jwtx_test.go b/common/jwt/jwtx_test.go new file mode 100644 index 0000000..f5debbe --- /dev/null +++ b/common/jwt/jwtx_test.go @@ -0,0 +1,110 @@ +package jwtx + +import ( + "fmt" + "testing" + "time" +) + +func TestGenerateAndParseJwtToken(t *testing.T) { + // 测试参数 + userId := int64(102) + secret := "WUvoIwL-FK0qnlxhvxR9tV6SjfOpeJMpKmY2QvT99lA" + expireTime := int64(2592000) // 1小时过期 + + // 生成token + token, err := GenerateJwtToken(userId, secret, expireTime) + if err != nil { + t.Fatalf("生成JWT令牌失败: %v", err) + } + if token == "" { + t.Fatal("生成的JWT令牌为空") + } + fmt.Println(token) + // 解析token + parsedUserId, err := ParseJwtToken(token, secret) + if err != nil { + t.Fatalf("解析JWT令牌失败: %v", err) + } + + // 验证解析出的userId是否正确 + if parsedUserId != userId { + t.Errorf("解析出的userId不匹配: 期望 %d, 实际 %d", userId, parsedUserId) + } +} + +func TestTokenExpiration(t *testing.T) { + // 测试参数 + userId := int64(10086) + secret := "test_secret_key" + expireTime := int64(1) // 1秒过期 + + // 生成token + token, err := GenerateJwtToken(userId, secret, expireTime) + if err != nil { + t.Fatalf("生成JWT令牌失败: %v", err) + } + + // 等待令牌过期 + time.Sleep(2 * time.Second) + + // 解析已过期token + _, err = ParseJwtToken(token, secret) + if err == nil { + t.Error("期望令牌过期错误,但没有发生错误") + } +} + +func TestInvalidToken(t *testing.T) { + secret := "test_secret_key" + + // 测试无效token + invalidToken := "invalid.token.string" + _, err := ParseJwtToken(invalidToken, secret) + if err == nil { + t.Error("期望无效令牌错误,但没有发生错误") + } + + // 测试密钥不匹配 + userId := int64(10086) + expireTime := int64(3600) + token, _ := GenerateJwtToken(userId, "original_secret", expireTime) + _, err = ParseJwtToken(token, "wrong_secret") + if err == nil { + t.Error("期望密钥不匹配错误,但没有发生错误") + } +} + +func TestUserIdTypes(t *testing.T) { + cases := []struct { + name string + setupFn func() (string, string, int64) + expected int64 + }{ + { + name: "正常int64类型", + setupFn: func() (string, string, int64) { + userId := int64(10086) + secret := "test_secret" + expireTime := int64(3600) + token, _ := GenerateJwtToken(userId, secret, expireTime) + return token, secret, userId + }, + expected: 10086, + }, + // 其他类型在实际场景中通过手动修改token内容测试,这里省略 + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + token, secret, expected := tc.setupFn() + userId, err := ParseJwtToken(token, secret) + if err != nil { + t.Fatalf("解析失败: %v", err) + } + if userId != expected { + t.Errorf("用户ID不匹配: 期望 %d, 实际 %d", expected, userId) + } + }) + } +} diff --git a/deploy/script/README.md b/deploy/script/README.md new file mode 100644 index 0000000..e8d77ab --- /dev/null +++ b/deploy/script/README.md @@ -0,0 +1,163 @@ +# Go-Zero 代码生成工具 + +这是一个用于 Go-Zero 微服务项目的代码生成工具,支持生成数据库模型和 API 代码。 + +## 功能特性 + +- 数据库模型生成:支持生成 Go-Zero 风格的数据库模型代码 +- API 代码生成:支持生成 API 服务相关代码 +- 灵活的表名管理:支持通过命令行指定表名,无需修改配置文件 +- 配置文件支持:通过 config.json 配置数据库连接和表列表 +- 命令行参数:支持通过命令行参数覆盖配置文件设置 + +## 安装 + +确保你的系统已安装 Node.js(10.0+)。 + +```bash +# 安装依赖 +cd deploy/script +npm install +``` + +## 使用方法 + +### 查看配置 + +```bash +npm run config +``` + +### 生成所有配置的表模型 + +```bash +npm run model +``` + +### 生成指定表的模型 + +```bash +# 生成单个表 +npm run model:table agent + +# 生成多个表(逗号分隔,不含空格) +npm run model:tables agent,user,order + +# 或者使用完整命令 +node codegen.js model --tables agent,user,order +``` + +### 排除指定表 + +```bash +# 从配置的表中排除某些表 +npm run model:exclude user,order + +# 或者使用完整命令 +node codegen.js model --exclude user,order +``` + +### 生成所有数据库表的模型 + +```bash +npm run model:all + +# 生成所有表但排除某些表 +node codegen.js model --all --exclude system_table,temp_table +``` + +### 保存表配置 + +```bash +# 生成指定表并保存到配置文件 +npm run model:save agent,user,order -- --save + +# 或者使用完整命令 +node codegen.js model --tables agent,user,order --save +``` + +### 生成 API 代码 + +```bash +npm run api +``` + +### 生成带样式的 API 代码 + +```bash +npm run api:style +``` + +### 查看帮助信息 + +```bash +npm run help +``` + +## 配置文件 + +所有配置都保存在`config.json`文件中,你可以编辑此文件来修改数据库连接信息和要生成的表列表: + +```json +{ + "model": { + "dbUrl": "user:password@tcp(host:port)/database", + "outputDir": "./model", + "templateDir": "../template", + "targetDir": "../../app/user/model", + "tables": ["table1", "table2"], + "disabledTables": ["disabled_table1"] + }, + "api": { + "apiFile": "./app/user/cmd/api/desc/main.api", + "outputDir": "./app/user/cmd/api", + "templateDir": "./deploy/template", + "useStyle": false + } +} +``` + +## 表名管理 + +本工具提供了多种方式来指定需要生成的表: + +1. **配置文件指定**:在`config.json`的`tables`数组中列出要生成的表 +2. **命令行指定单个表**:使用`--table`参数指定单个表 +3. **命令行指定多个表**:使用`--tables`参数指定多个表(逗号分隔) +4. **排除特定表**:使用`--exclude`参数排除特定表 +5. **生成所有表**:使用`--all`参数生成数据库中的所有表 +6. **保存到配置**:使用`--save`参数将当前指定的表保存到配置文件 + +这种灵活的表名管理方式使你可以根据需要随时调整要生成的表,而无需频繁修改配置文件。 + +## 高级用法 + +### 通过命令行参数覆盖配置 + +```bash +# 使用不同的数据库连接 +node codegen.js model --db-url "user:pass@tcp(localhost:3306)/otherdb" + +# 使用不同的API文件 +node codegen.js api --api ./other_api.api + +# 指定输出目录 +node codegen.js api --dir ./output +``` + +### 组合使用参数 + +```bash +# 生成所有表但排除系统表,并使用自定义模板 +node codegen.js model --all --exclude system_log,system_user --template ./my-templates + +# 生成指定表并保存到配置 +node codegen.js model --tables user,order,product --save +``` + +## 脚本文件说明 + +- `codegen.js` - 统一入口脚本 +- `gen_models.js` - 模型生成脚本 +- `gen_api.js` - API 生成脚本 +- `config.json` - 配置文件 diff --git a/deploy/script/codegen.js b/deploy/script/codegen.js new file mode 100644 index 0000000..78cc773 --- /dev/null +++ b/deploy/script/codegen.js @@ -0,0 +1,444 @@ +#!/usr/bin/env node + +/** + * 代码生成统一入口脚本 + * 整合了model生成和api生成功能 + */ + +const { execSync } = require('child_process'); +const fs = require('fs'); +const path = require('path'); +const { generateApi } = require('./gen_api'); + +// 读取配置文件 +const CONFIG_FILE = path.join(__dirname, 'config.json'); +let config = {}; + +try { + if (fs.existsSync(CONFIG_FILE)) { + const configContent = fs.readFileSync(CONFIG_FILE, 'utf8'); + config = JSON.parse(configContent); + console.log('Configuration loaded from config.json'); + } else { + console.log('config.json not found, using default settings'); + } +} catch (error) { + console.error('Error loading configuration:', error.message); + console.log('Using default settings'); +} + +// 默认数据库配置 +const modelConfig = config.model || { + // 数据库连接信息 + dbUrl: 'qnc:5vg67b3UNHu8@tcp(127.0.0.1:21001)/qnc', + // 输出目录 + outputDir: './model', + // 模板目录 + templateDir: '../template', + // 目标目录 + targetDir: '../../app/user/model', + // 表名列表 + tables: [ + 'agent', + 'agent_audit', + 'agent_real_name' + ], + // 禁用的表名列表 + disabledTables: [] +}; + +// 默认API配置 +const apiConfig = config.api || { + // API定义文件路径 + apiFile: './app/user/cmd/api/desc/main.api', + // 输出目录 + outputDir: './app/user/cmd/api', + // 模板目录 + templateDir: './deploy/template', + // 是否使用样式 + useStyle: false +}; + +/** + * 将表名转换为驼峰命名法 + * @param {string} tableName 表名 + * @returns {string} 驼峰命名的表名 + */ +function convertToCamelCase(tableName) { + try { + // 将表名按_分割,并将每个部分首字母大写 + const parts = tableName.split('_'); + let camelCase = ''; + for (const part of parts) { + if (part.length > 0) { + camelCase += part.charAt(0).toUpperCase() + part.slice(1).toLowerCase(); + } + } + return camelCase; + } catch (error) { + console.error(`Error in convertToCamelCase for table: ${tableName}`); + console.error(error.message); + return tableName; // 出错时返回原表名 + } +} + +/** + * 确保目录存在 + * @param {string} dirPath 目录路径 + */ +function ensureDirectoryExists(dirPath) { + if (!fs.existsSync(dirPath)) { + console.log(`Creating directory: ${dirPath}`); + fs.mkdirSync(dirPath, { recursive: true }); + } +} + +/** + * 为单个表生成模型 + * @param {string} table 表名 + * @param {object} config 配置信息 + */ +function generateModelForTable(table, config) { + try { + console.log('========================================='); + console.log(`Processing table: ${table}`); + + // 生成模型 + console.log('Generating model...'); + const command = `goctl model mysql datasource -url="${config.dbUrl}" -table="${table}" -dir="${config.outputDir}" --home="${config.templateDir}" -cache=true --style=goZero`; + console.log(`Running command: ${command}`); + + execSync(command, { stdio: 'inherit' }); + + // 将表名转换为驼峰命名法 + const camelCaseName = convertToCamelCase(table); + console.log(`Table name converted to: ${camelCaseName}`); + + // 定义源文件和目标文件路径 + const sourceModelFile = path.join(config.outputDir, `${camelCaseName}Model.go`); + const sourceModelGenFile = path.join(config.outputDir, `${camelCaseName}Model_gen.go`); + const targetModelFile = path.join(config.targetDir, `${camelCaseName}Model.go`); + const targetModelGenFile = path.join(config.targetDir, `${camelCaseName}Model_gen.go`); + + console.log('Source files:'); + console.log(` - ${sourceModelFile}`); + console.log(` - ${sourceModelGenFile}`); + console.log('Target files:'); + console.log(` - ${targetModelFile}`); + console.log(` - ${targetModelGenFile}`); + + // 检查源文件是否存在并移动 + if (fs.existsSync(sourceModelFile)) { + console.log(`Moving ${sourceModelFile} to ${targetModelFile}`); + fs.copyFileSync(sourceModelFile, targetModelFile); + fs.unlinkSync(sourceModelFile); + } else { + console.log(`WARNING: Source file not found: ${sourceModelFile}`); + } + + if (fs.existsSync(sourceModelGenFile)) { + console.log(`Moving ${sourceModelGenFile} to ${targetModelGenFile}`); + fs.copyFileSync(sourceModelGenFile, targetModelGenFile); + fs.unlinkSync(sourceModelGenFile); + } else { + console.log(`WARNING: Source file not found: ${sourceModelGenFile}`); + } + + console.log(`Processing completed for table: ${table}`); + } catch (error) { + console.error(`ERROR processing table: ${table}`); + console.error(error.message); + console.error(error.stack); + } +} + +/** + * 生成指定表列表的模型 + * @param {string[]} tables 表名列表 + * @param {object} config 配置信息 + */ +function generateModels(tables, config) { + try { + // 确保目录存在 + ensureDirectoryExists(config.outputDir); + ensureDirectoryExists(config.targetDir); + + if (tables.length === 0) { + console.log('No tables specified for generation.'); + return; + } + + // 为每个表生成模型 + for (const table of tables) { + generateModelForTable(table, config); + } + + console.log('========================================='); + console.log('All models generated successfully.'); + } catch (error) { + console.error('ERROR in model generation:'); + console.error(error.message); + console.error(error.stack); + process.exit(1); + } +} + +/** + * 从数据库获取所有表名 + * @param {string} dbUrl 数据库连接URL + * @returns {Promise} 表名列表 + */ +async function getAllTablesFromDB(dbUrl) { + try { + // 解析数据库连接信息 + const dbName = dbUrl.split('/').pop().split('?')[0]; + console.log(`Fetching all tables from database: ${dbName}`); + + // 这里需要实现从数据库获取所有表的逻辑 + // 由于需要依赖额外的库,这里只是示例 + // 实际实现可能需要使用mysql2或其他数据库客户端库 + + // 模拟返回一些表名 + return [ + 'agent', + 'agent_audit', + 'agent_real_name', + 'agent_active_stat', + 'agent_closure', + 'agent_commission', + 'agent_commission_deduction', + 'agent_link', + 'agent_membership_config', + 'agent_membership_recharge_order', + 'agent_membership_user_config', + 'agent_order', + 'agent_platform_deduction', + 'agent_product_config', + 'agent_rewards', + 'agent_wallet', + 'agent_withdrawal', + 'feature', + 'global_notifications', + 'order', + 'product', + 'product_feature', + 'query', + 'user', + 'user_auth', + 'example', + 'authorization', + 'authorization_face' + ]; + } catch (error) { + console.error('Error fetching tables from database:', error.message); + return []; + } +} + +/** + * 显示配置信息 + */ +function showConfig() { + console.log('========================================='); + console.log('Current Configuration:'); + console.log('\nModel Configuration:'); + console.log(' Database URL:', modelConfig.dbUrl); + console.log(' Output Directory:', modelConfig.outputDir); + console.log(' Template Directory:', modelConfig.templateDir); + console.log(' Target Directory:', modelConfig.targetDir); + console.log(' Tables:'); + modelConfig.tables.forEach(table => console.log(` - ${table}`)); + + if (modelConfig.disabledTables && modelConfig.disabledTables.length > 0) { + console.log(' Disabled Tables:'); + modelConfig.disabledTables.forEach(table => console.log(` - ${table}`)); + } + + console.log('\nAPI Configuration:'); + console.log(' API File:', apiConfig.apiFile); + console.log(' Output Directory:', apiConfig.outputDir); + console.log(' Template Directory:', apiConfig.templateDir); + console.log(' Use Style:', apiConfig.useStyle ? 'Yes' : 'No'); + console.log('========================================='); +} + +/** + * 保存配置到文件 + * @param {object} config 配置对象 + */ +function saveConfig(config) { + try { + fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 4), 'utf8'); + console.log('Configuration saved to config.json'); + } catch (error) { + console.error('Error saving configuration:', error.message); + } +} + +/** + * 显示帮助信息 + */ +function showHelp() { + console.log('Usage: node codegen.js [options]'); + console.log('Commands:'); + console.log(' model Generate database models'); + console.log(' api Generate API code'); + console.log(' config Show current configuration'); + console.log(' help Show this help message'); + console.log(''); + console.log('Model Options:'); + console.log(' --table Generate model for specific table'); + console.log(' --tables Generate models for specific tables (comma-separated)'); + console.log(' --exclude Exclude specific tables (comma-separated)'); + console.log(' --all Generate models for all tables in database'); + console.log(' --db-url Database connection URL'); + console.log(' --template Template directory'); + console.log(' --save Save specified tables to config.json'); + console.log(''); + console.log('API Options:'); + console.log(' --api API definition file path'); + console.log(' --dir Output directory'); + console.log(' --template Template directory'); + console.log(' --style Use goZero style'); + console.log(''); + console.log('Configuration:'); + console.log(' Settings are loaded from config.json'); + console.log(' Command line options override config file settings'); + console.log(''); + console.log('Examples:'); + console.log(' node codegen.js model --table agent'); + console.log(' node codegen.js model --tables agent,user,order'); + console.log(' node codegen.js model --exclude user,order'); + console.log(' node codegen.js model --all --exclude system_table'); + console.log(' node codegen.js model --tables agent,user --save'); + process.exit(0); +} + +/** + * 主函数 + */ +async function main() { + // 解析命令行参数 + const args = process.argv.slice(2); + + if (args.length === 0 || args[0] === 'help') { + showHelp(); + } + + const command = args[0]; + + // 显示配置信息 + if (command === 'config') { + showConfig(); + return; + } + + // 处理模型生成命令 + if (command === 'model') { + const config = { ...modelConfig }; + let singleTable = null; + let tablesToGenerate = [...config.tables]; + let excludeTables = []; + let shouldSaveConfig = false; + let generateAllTables = false; + + // 处理其他参数 + for (let i = 1; i < args.length; i++) { + const arg = args[i]; + + if (arg === '--table' && i + 1 < args.length) { + singleTable = args[++i]; + tablesToGenerate = [singleTable]; + } else if (arg === '--tables' && i + 1 < args.length) { + const tableList = args[++i].split(',').map(t => t.trim()).filter(t => t); + tablesToGenerate = tableList; + } else if (arg === '--exclude' && i + 1 < args.length) { + excludeTables = args[++i].split(',').map(t => t.trim()).filter(t => t); + } else if (arg === '--all') { + generateAllTables = true; + } else if (arg === '--db-url' && i + 1 < args.length) { + config.dbUrl = args[++i]; + } else if (arg === '--template' && i + 1 < args.length) { + config.templateDir = args[++i]; + } else if (arg === '--save') { + shouldSaveConfig = true; + } + } + + // 如果指定了--all参数,获取所有表名 + if (generateAllTables) { + tablesToGenerate = await getAllTablesFromDB(config.dbUrl); + } + + // 排除指定的表 + if (excludeTables.length > 0) { + tablesToGenerate = tablesToGenerate.filter(table => !excludeTables.includes(table)); + } + + // 保存配置 + if (shouldSaveConfig) { + const newConfig = { ...config }; + newConfig.tables = tablesToGenerate; + + // 更新配置文件 + if (fs.existsSync(CONFIG_FILE)) { + const existingConfig = JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8')); + existingConfig.model = newConfig; + saveConfig(existingConfig); + } else { + saveConfig({ model: newConfig, api: apiConfig }); + } + } + + // 生成单个表的模型或所有表的模型 + if (singleTable) { + generateModelForTable(singleTable, config); + } else { + generateModels(tablesToGenerate, config); + } + } + // 处理API生成命令 + else if (command === 'api') { + const config = { ...apiConfig }; + + // 处理其他参数 + for (let i = 1; i < args.length; i++) { + const arg = args[i]; + + if (arg === '--api' && i + 1 < args.length) { + config.apiFile = args[++i]; + } else if (arg === '--dir' && i + 1 < args.length) { + config.outputDir = args[++i]; + } else if (arg === '--template' && i + 1 < args.length) { + config.templateDir = args[++i]; + } else if (arg === '--style') { + config.useStyle = true; + } + } + + // 生成API代码 + generateApi(config); + } + else { + console.error(`Unknown command: ${command}`); + showHelp(); + } +} + +// 执行主函数 +if (require.main === module) { + main().catch(err => { + console.error('Error in main execution:', err); + process.exit(1); + }); +} + +// 导出函数,以便其他脚本可以调用 +module.exports = { + generateModels, + generateModelForTable, + generateApi, + showConfig, + saveConfig, + getAllTablesFromDB +}; \ No newline at end of file diff --git a/deploy/script/config.json b/deploy/script/config.json new file mode 100644 index 0000000..1453c0b --- /dev/null +++ b/deploy/script/config.json @@ -0,0 +1,46 @@ +{ + "model": { + "dbUrl": "qnc:5vg67b3UNHu8@tcp(127.0.0.1:21001)/qnc", + "outputDir": "./model", + "templateDir": "../template", + "targetDir": "../../app/user/model", + "tables": [ + "agent", + "agent_audit", + "agent_real_name" + ], + "disabledTables": [ + "agent_active_stat", + "agent_closure", + "agent_commission", + "agent_commission_deduction", + "agent_link", + "agent_membership_config", + "agent_membership_recharge_order", + "agent_membership_user_config", + "agent_order", + "agent_platform_deduction", + "agent_product_config", + "agent_rewards", + "agent_wallet", + "agent_withdrawal", + "feature", + "global_notifications", + "order", + "product", + "product_feature", + "query", + "user", + "user_auth", + "example", + "authorization", + "authorization_face" + ] + }, + "api": { + "apiFile": "./app/user/cmd/api/desc/main.api", + "outputDir": "./app/user/cmd/api", + "templateDir": "./deploy/template", + "useStyle": false + } +} \ No newline at end of file diff --git a/deploy/script/encrypt_mobile.go b/deploy/script/encrypt_mobile.go deleted file mode 100644 index 01ee18f..0000000 --- a/deploy/script/encrypt_mobile.go +++ /dev/null @@ -1,102 +0,0 @@ -package script - -import ( - "fmt" - "log" - "qnc-server/pkg/lzkit/crypto" - - "github.com/zeromicro/go-zero/core/stores/sqlx" -) - -const ( - // 请替换为实际的数据库连接信息 - dbHost = "localhost" - dbPort = "21001" - dbUser = "qnc" - dbPassword = "5vg67b3UNHu8" - dbName = "qnc" - // 请替换为实际的加密密钥 - secretKey = "ff83609b2b24fc73196aac3d3dfb874f" -) - -func RunEncryptMobile() { - fmt.Println("开始加密手机号") - // 连接数据库 - dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local", - dbUser, dbPassword, dbHost, dbPort, dbName) - db := sqlx.NewMysql(dsn) - - // 加密user表的mobile字段 - if err := encryptUserMobile(db); err != nil { - log.Fatalf("加密user表mobile字段失败: %v", err) - } - - // 加密user_auth表的auth_key字段 - if err := encryptUserAuthKey(db); err != nil { - log.Fatalf("加密user_auth表auth_key字段失败: %v", err) - } - - fmt.Println("加密完成!") -} - -func encryptUserMobile(db sqlx.SqlConn) error { - // 查询所有未加密的手机号 - query := "SELECT id, mobile FROM user WHERE mobile IS NOT NULL AND mobile != ''" - var rows []struct { - ID int64 `db:"id"` - Mobile string `db:"mobile"` - } - if err := db.QueryRows(&rows, query); err != nil { - return fmt.Errorf("查询user表失败: %v", err) - } - - // 准备更新语句 - updateStmt := "UPDATE user SET mobile = ? WHERE id = ?" - - // 处理每一行 - for _, row := range rows { - // 加密手机号 - encryptedMobile, err := crypto.EncryptMobile(row.Mobile, secretKey) - if err != nil { - return fmt.Errorf("加密手机号失败: %v", err) - } - - // 更新数据库 - if _, err := db.Exec(updateStmt, encryptedMobile, row.ID); err != nil { - return fmt.Errorf("更新user表失败: %v", err) - } - } - - return nil -} - -func encryptUserAuthKey(db sqlx.SqlConn) error { - // 查询所有需要加密的auth_key - query := "SELECT id, auth_key FROM user_auth WHERE auth_type = 'app_mobile' AND auth_key IS NOT NULL AND auth_key != ''" - var rows []struct { - ID int64 `db:"id"` - AuthKey string `db:"auth_key"` - } - if err := db.QueryRows(&rows, query); err != nil { - return fmt.Errorf("查询user_auth表失败: %v", err) - } - - // 准备更新语句 - updateStmt := "UPDATE user_auth SET auth_key = ? WHERE id = ?" - - // 处理每一行 - for _, row := range rows { - // 加密auth_key - encryptedAuthKey, err := crypto.EncryptMobile(row.AuthKey, secretKey) - if err != nil { - return fmt.Errorf("加密auth_key失败: %v", err) - } - - // 更新数据库 - if _, err := db.Exec(updateStmt, encryptedAuthKey, row.ID); err != nil { - return fmt.Errorf("更新user_auth表失败: %v", err) - } - } - - return nil -} diff --git a/deploy/script/gen_api.js b/deploy/script/gen_api.js new file mode 100644 index 0000000..cc6f837 --- /dev/null +++ b/deploy/script/gen_api.js @@ -0,0 +1,108 @@ +#!/usr/bin/env node + +/** + * API代码生成脚本 + * 功能等同于gen_api.ps1 + */ + +const { execSync } = require('child_process'); +const fs = require('fs'); +const path = require('path'); + +// 配置信息 +const config = { + // API定义文件路径 + apiFile: './app/user/cmd/api/desc/main.api', + // 输出目录 + outputDir: './app/user/cmd/api', + // 模板目录 + templateDir: './deploy/template' +}; + +/** + * 生成API代码 + * @param {object} options 选项 + * @param {string} options.apiFile API定义文件路径 + * @param {string} options.outputDir 输出目录 + * @param {string} options.templateDir 模板目录 + * @param {boolean} options.useStyle 是否使用样式 + */ +function generateApi({ apiFile, outputDir, templateDir, useStyle = false }) { + try { + console.log('========================================='); + console.log('Generating API code...'); + + // 构建命令 + let command = `goctl api go --api ${apiFile} --dir ${outputDir} --home ${templateDir}`; + + // 如果需要使用样式 + if (useStyle) { + command += ' --style=goZero'; + console.log('Using goZero style'); + } + + console.log(`Running command: ${command}`); + + // 执行命令 + execSync(command, { stdio: 'inherit' }); + + console.log('API code generation completed!'); + console.log('========================================='); + } catch (error) { + console.error('ERROR in API generation:'); + console.error(error.message); + console.error(error.stack); + process.exit(1); + } +} + +/** + * 主函数 + */ +function main() { + // 解析命令行参数 + const args = process.argv.slice(2); + const options = { + apiFile: config.apiFile, + outputDir: config.outputDir, + templateDir: config.templateDir, + useStyle: false + }; + + // 处理命令行参数 + for (let i = 0; i < args.length; i++) { + const arg = args[i]; + + if (arg === '--api' && i + 1 < args.length) { + options.apiFile = args[++i]; + } else if (arg === '--dir' && i + 1 < args.length) { + options.outputDir = args[++i]; + } else if (arg === '--template' && i + 1 < args.length) { + options.templateDir = args[++i]; + } else if (arg === '--style') { + options.useStyle = true; + } else if (arg === '--help') { + console.log('Usage: node gen_api.js [options]'); + console.log('Options:'); + console.log(' --api API definition file path'); + console.log(' --dir Output directory'); + console.log(' --template Template directory'); + console.log(' --style Use goZero style'); + console.log(' --help Show this help message'); + process.exit(0); + } + } + + // 生成API代码 + generateApi(options); +} + +// 执行主函数 +if (require.main === module) { + main(); +} + +// 导出函数,以便其他脚本可以调用 +module.exports = { + generateApi +}; \ No newline at end of file diff --git a/deploy/script/gen_models.js b/deploy/script/gen_models.js new file mode 100644 index 0000000..4e7884f --- /dev/null +++ b/deploy/script/gen_models.js @@ -0,0 +1,172 @@ +#!/usr/bin/env node + +/** + * 数据库模型生成脚本 + * 功能等同于gen_models.ps1 + */ + +const { execSync } = require('child_process'); +const fs = require('fs'); +const path = require('path'); + +// 配置信息 +const config = { + // 数据库连接信息 + dbUrl: 'qnc:5vg67b3UNHu8@tcp(127.0.0.1:21001)/qnc', + // 输出目录 + outputDir: './model', + // 模板目录 + templateDir: '../template', + // 目标目录 + targetDir: '../../app/user/model', + // 表名列表 + tables: [ + 'agent', + // 'agent_active_stat', + 'agent_audit', + 'agent_real_name' + // 'agent_closure', + // 'agent_commission', + // 'agent_commission_deduction', + // 'agent_link', + // 'agent_membership_config', + // 'agent_membership_recharge_order', + // 'agent_membership_user_config', + // 'agent_order', + // 'agent_platform_deduction', + // 'agent_product_config', + // 'agent_rewards', + // 'agent_wallet', + // 'agent_withdrawal', + // 'feature', + // 'global_notifications', + // 'order', + // 'product', + // 'product_feature', + // 'query', + // 'user', + // 'user_auth', + // 'example', + // 'authorization', + // 'authorization_face' + ] +}; + +/** + * 将表名转换为驼峰命名法 + * @param {string} tableName 表名 + * @returns {string} 驼峰命名的表名 + */ +function convertToCamelCase(tableName) { + try { + // 将表名按_分割,并将每个部分首字母大写 + const parts = tableName.split('_'); + let camelCase = ''; + for (const part of parts) { + if (part.length > 0) { + camelCase += part.charAt(0).toUpperCase() + part.slice(1).toLowerCase(); + } + } + return camelCase; + } catch (error) { + console.error(`Error in convertToCamelCase for table: ${tableName}`); + console.error(error.message); + return tableName; // 出错时返回原表名 + } +} + +/** + * 确保目录存在 + * @param {string} dirPath 目录路径 + */ +function ensureDirectoryExists(dirPath) { + if (!fs.existsSync(dirPath)) { + console.log(`Creating directory: ${dirPath}`); + fs.mkdirSync(dirPath, { recursive: true }); + } +} + +/** + * 为单个表生成模型 + * @param {string} table 表名 + */ +function generateModelForTable(table) { + try { + console.log('========================================='); + console.log(`Processing table: ${table}`); + + // 生成模型 + console.log('Generating model...'); + const command = `goctl model mysql datasource -url="${config.dbUrl}" -table="${table}" -dir="${config.outputDir}" --home="${config.templateDir}" -cache=true --style=goZero`; + console.log(`Running command: ${command}`); + + execSync(command, { stdio: 'inherit' }); + + // 将表名转换为驼峰命名法 + const camelCaseName = convertToCamelCase(table); + console.log(`Table name converted to: ${camelCaseName}`); + + // 定义源文件和目标文件路径 + const sourceModelFile = path.join(config.outputDir, `${camelCaseName}Model.go`); + const sourceModelGenFile = path.join(config.outputDir, `${camelCaseName}Model_gen.go`); + const targetModelFile = path.join(config.targetDir, `${camelCaseName}Model.go`); + const targetModelGenFile = path.join(config.targetDir, `${camelCaseName}Model_gen.go`); + + console.log('Source files:'); + console.log(` - ${sourceModelFile}`); + console.log(` - ${sourceModelGenFile}`); + console.log('Target files:'); + console.log(` - ${targetModelFile}`); + console.log(` - ${targetModelGenFile}`); + + // 检查源文件是否存在并移动 + if (fs.existsSync(sourceModelFile)) { + console.log(`Moving ${sourceModelFile} to ${targetModelFile}`); + fs.copyFileSync(sourceModelFile, targetModelFile); + fs.unlinkSync(sourceModelFile); + } else { + console.log(`WARNING: Source file not found: ${sourceModelFile}`); + } + + if (fs.existsSync(sourceModelGenFile)) { + console.log(`Moving ${sourceModelGenFile} to ${targetModelGenFile}`); + fs.copyFileSync(sourceModelGenFile, targetModelGenFile); + fs.unlinkSync(sourceModelGenFile); + } else { + console.log(`WARNING: Source file not found: ${sourceModelGenFile}`); + } + + console.log(`Processing completed for table: ${table}`); + } catch (error) { + console.error(`ERROR processing table: ${table}`); + console.error(error.message); + console.error(error.stack); + } +} + +/** + * 主函数 + */ +function main() { + try { + // 确保目录存在 + ensureDirectoryExists(config.outputDir); + ensureDirectoryExists(config.targetDir); + + // 为每个表生成模型 + for (const table of config.tables) { + generateModelForTable(table); + } + + console.log('========================================='); + console.log('Script execution completed.'); + } catch (error) { + console.error('ERROR in main execution:'); + console.error(error.message); + console.error(error.stack); + process.exit(1); + } +} + +// 执行主函数 +main(); \ No newline at end of file diff --git a/deploy/script/gen_models.ps1 b/deploy/script/gen_models.ps1 index ba06e32..705ab7f 100644 --- a/deploy/script/gen_models.ps1 +++ b/deploy/script/gen_models.ps1 @@ -1,21 +1,64 @@ # 设置输出编码为UTF-8 [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 -# 数据库连接信息 - 修改了URL格式 -$DB_URL = "qnc:5vg67b3UNHu8@(127.0.0.1:21001)/qnc" +$OutputEncoding = [System.Text.Encoding]::UTF8 +[Console]::InputEncoding = [System.Text.Encoding]::UTF8 + +# 启用详细输出和错误处理 +$ErrorActionPreference = "Stop" +$VerbosePreference = "Continue" + +# 检查并创建必要的目录 +if (-not (Test-Path "./model")) { + Write-Output "Creating model directory..." + New-Item -ItemType Directory -Path "./model" | Out-Null +} + +if (-not (Test-Path "../../app/user/model")) { + Write-Output "Creating target directory..." + New-Item -ItemType Directory -Path "../../app/user/model" -Force | Out-Null +} + +# 将表名转换为驼峰命名法的函数 +function ConvertToCamelCase { + param ( + [string]$tableName + ) + try { + # 将表名按_分割,并将每个部分首字母大写 + $parts = $tableName -split '_' + $camelCase = "" + foreach ($part in $parts) { + if ($part.Length -gt 0) { + $camelCase += $part.Substring(0, 1).ToUpper() + $part.Substring(1).ToLower() + } + } + return $camelCase + } + catch { + Write-Output "Error in ConvertToCamelCase for table: $tableName" + Write-Output $_.Exception.Message + return $tableName # 出错时返回原表名 + } +} + +# 数据库连接信息 +$DB_URL = "qnc:5vg67b3UNHu8@tcp(127.0.0.1:21001)/qnc" $OUTPUT_DIR = "./model" $TEMPLATE_DIR = "../template" +$TARGET_DIR = "../../app/user/model" -# 表名列表 +# 表名列表 - 每个元素后必须有逗号分隔 $tables = @( - # "agent", + "agent", # "agent_active_stat", - # "agent_audit", + "agent_audit", + "agent_real_name" # "agent_closure", # "agent_commission", # "agent_commission_deduction", # "agent_link", # "agent_membership_config", - # "agent_membership_recharge_order" + # "agent_membership_recharge_order", # "agent_membership_user_config", # "agent_order", # "agent_platform_deduction", @@ -29,12 +72,68 @@ $tables = @( # "product", # "product_feature", # "query", - "user" - # "user_auth" - # "example" + # "user", + # "user_auth", + # "example", + # "authorization", + # "authorization_face" ) # 为每个表生成模型 foreach ($table in $tables) { - goctl model mysql datasource -url="qnc:5vg67b3UNHu8@tcp(127.0.0.1:21001)/qnc" -table="$table" -dir="./model" --home="../template" -cache=true --style=goZero + try { + Write-Output "=========================================" + Write-Output "Processing table: $table" + + # 生成模型 + Write-Output "Generating model..." + $command = "goctl model mysql datasource -url=`"$DB_URL`" -table=`"$table`" -dir=`"$OUTPUT_DIR`" --home=`"$TEMPLATE_DIR`" -cache=true --style=goZero" + Write-Output "Running command: $command" + Invoke-Expression $command + + # 将表名转换为驼峰命名法 + $camelCaseName = ConvertToCamelCase -tableName $table + Write-Output "Table name converted to: $camelCaseName" + + # 定义源文件和目标文件路径 + $sourceModelFile = "$OUTPUT_DIR/${camelCaseName}Model.go" + $sourceModelGenFile = "$OUTPUT_DIR/${camelCaseName}Model_gen.go" + $targetModelFile = "$TARGET_DIR/${camelCaseName}Model.go" + $targetModelGenFile = "$TARGET_DIR/${camelCaseName}Model_gen.go" + + Write-Output "Source files:" + Write-Output " - $sourceModelFile" + Write-Output " - $sourceModelGenFile" + Write-Output "Target files:" + Write-Output " - $targetModelFile" + Write-Output " - $targetModelGenFile" + + # 检查源文件是否存在 + if (-not (Test-Path $sourceModelFile)) { + Write-Output "WARNING: Source file not found: $sourceModelFile" + } + if (-not (Test-Path $sourceModelGenFile)) { + Write-Output "WARNING: Source file not found: $sourceModelGenFile" + } + + # 移动文件 + if (Test-Path $sourceModelFile) { + Write-Output "Moving $sourceModelFile to $targetModelFile" + Move-Item -Path $sourceModelFile -Destination $targetModelFile -Force + } + if (Test-Path $sourceModelGenFile) { + Write-Output "Moving $sourceModelGenFile to $targetModelGenFile" + Move-Item -Path $sourceModelGenFile -Destination $targetModelGenFile -Force + } + + Write-Output "Processing completed for table: $table" + } + catch { + Write-Output "ERROR processing table: $table" + Write-Output $_.Exception.Message + Write-Output $_.ScriptStackTrace + } } + +Write-Output "=========================================" +Write-Output "Script execution completed." diff --git a/deploy/script/package.json b/deploy/script/package.json new file mode 100644 index 0000000..0b38c14 --- /dev/null +++ b/deploy/script/package.json @@ -0,0 +1,26 @@ +{ + "name": "qnc-codegen", + "version": "1.0.0", + "description": "代码生成工具 - Go-Zero微服务项目", + "main": "codegen.js", + "scripts": { + "model": "node codegen.js model", + "model:table": "node codegen.js model --table", + "model:tables": "node codegen.js model --tables", + "model:exclude": "node codegen.js model --exclude", + "model:all": "node codegen.js model --all", + "model:save": "node codegen.js model --tables", + "model:save-exclude": "node codegen.js model --exclude", + "api": "node codegen.js api", + "api:style": "node codegen.js api --style", + "config": "node codegen.js config", + "help": "node codegen.js help" + }, + "keywords": [ + "go-zero", + "codegen", + "microservice" + ], + "author": "", + "license": "ISC" +} \ No newline at end of file diff --git a/deploy/sql/agent_real_name.sql b/deploy/sql/agent_real_name.sql new file mode 100644 index 0000000..e092a58 --- /dev/null +++ b/deploy/sql/agent_real_name.sql @@ -0,0 +1,51 @@ +-- 创建数据库(如果不存在) +CREATE DATABASE IF NOT EXISTS `qnc_server` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; + +-- 使用数据库 +USE `qnc_server`; + +-- 创建代理实名认证表 +CREATE TABLE `agent_real_name` ( + `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID', + `agent_id` bigint NOT NULL COMMENT '代理ID', + `name` varchar(255) NOT NULL COMMENT '实名姓名', + `card_id` varchar(255) NOT NULL COMMENT '身份证号', + `status` enum( + 'pending', + 'approved', + 'rejected' + ) NOT NULL DEFAULT 'pending' COMMENT '认证状态(认证中、通过、拒绝)', + `del_state` tinyint NOT NULL DEFAULT '0' COMMENT '删除状态', + `version` bigint NOT NULL DEFAULT '0' COMMENT '版本号', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `approve_time` datetime DEFAULT NULL COMMENT '认证通过时间', + `reject_time` datetime DEFAULT NULL COMMENT '认证拒绝时间', + `delete_time` datetime DEFAULT NULL COMMENT '删除时间', + PRIMARY KEY (`id`), + UNIQUE KEY `unique_agent_id` (`agent_id`), + KEY `idx_status` (`status`), + KEY `idx_card_id` (`card_id`) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '代理实名认证表'; + +-- 添加表注释 +ALTER TABLE `agent_real_name` COMMENT = '代理实名认证表'; + +-- 添加字段注释 +ALTER TABLE `agent_real_name` +MODIFY COLUMN `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID', +MODIFY COLUMN `agent_id` bigint NOT NULL COMMENT '代理ID', +MODIFY COLUMN `name` varchar(255) NOT NULL COMMENT '实名姓名', +MODIFY COLUMN `card_id` varchar(255) NOT NULL COMMENT '身份证号', +MODIFY COLUMN `status` enum( + 'pending', + 'approved', + 'rejected' +) NOT NULL DEFAULT 'pending' COMMENT '认证状态(认证中、通过、拒绝)', +MODIFY COLUMN `del_state` tinyint NOT NULL DEFAULT '0' COMMENT '删除状态', +MODIFY COLUMN `version` bigint NOT NULL DEFAULT '0' COMMENT '版本号', +MODIFY COLUMN `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', +MODIFY COLUMN `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', +MODIFY COLUMN `approve_time` datetime DEFAULT NULL COMMENT '认证通过时间', +MODIFY COLUMN `reject_time` datetime DEFAULT NULL COMMENT '认证拒绝时间', +MODIFY COLUMN `delete_time` datetime DEFAULT NULL COMMENT '删除时间'; \ No newline at end of file diff --git a/go.mod b/go.mod index 2291141..f29bdac 100644 --- a/go.mod +++ b/go.mod @@ -4,17 +4,22 @@ go 1.22.4 require ( github.com/Masterminds/squirrel v1.5.4 - github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.10 + github.com/alibabacloud-go/cloudauth-20190307/v4 v4.6.0 + github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.11 github.com/alibabacloud-go/dysmsapi-20170525/v3 v3.0.6 github.com/alibabacloud-go/tea v1.2.2 + github.com/alibabacloud-go/tea-rpc v1.1.7 github.com/alibabacloud-go/tea-utils/v2 v2.0.7 + github.com/aliyun/credentials-go v1.4.6 github.com/bytedance/sonic v1.13.0 github.com/cenkalti/backoff/v4 v4.3.0 github.com/go-playground/validator/v10 v10.22.1 github.com/golang-jwt/jwt/v4 v4.5.0 + github.com/google/uuid v1.6.0 github.com/hibiken/asynq v0.25.0 github.com/jinzhu/copier v0.4.0 github.com/pkg/errors v0.9.1 + github.com/redis/go-redis/v9 v9.7.0 github.com/shopspring/decimal v1.4.0 github.com/smartwalle/alipay/v3 v3.2.23 github.com/sony/sonyflake v1.2.0 @@ -30,10 +35,14 @@ require ( github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.5 // indirect github.com/alibabacloud-go/debug v1.0.1 // indirect github.com/alibabacloud-go/endpoint-util v1.1.0 // indirect - github.com/alibabacloud-go/openapi-util v0.1.0 // indirect - github.com/alibabacloud-go/tea-utils v1.3.1 // indirect + github.com/alibabacloud-go/openapi-util v0.1.1 // indirect + github.com/alibabacloud-go/openplatform-20191219/v2 v2.0.1 // indirect + github.com/alibabacloud-go/tea-fileform v1.1.1 // indirect + github.com/alibabacloud-go/tea-oss-sdk v1.1.5 // indirect + github.com/alibabacloud-go/tea-oss-utils v1.1.0 // indirect + github.com/alibabacloud-go/tea-rpc-utils v1.1.1 // indirect + github.com/alibabacloud-go/tea-utils v1.4.5 // indirect github.com/alibabacloud-go/tea-xml v1.1.3 // indirect - github.com/aliyun/credentials-go v1.3.10 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bytedance/sonic/loader v0.2.2 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect @@ -60,7 +69,6 @@ require ( github.com/google/gnostic-models v0.6.8 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/google/gofuzz v1.2.0 // indirect - github.com/google/uuid v1.6.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect @@ -81,14 +89,13 @@ require ( github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.55.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect - github.com/redis/go-redis/v9 v9.7.0 // indirect github.com/robfig/cron/v3 v3.0.1 // indirect - github.com/samber/lo v1.49.1 // indirect github.com/smartwalle/ncrypto v1.0.4 // indirect github.com/smartwalle/ngx v1.0.9 // indirect github.com/smartwalle/nsign v1.0.9 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect github.com/spf13/cast v1.7.0 // indirect + github.com/stretchr/testify v1.10.0 // indirect github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.1 // indirect github.com/tjfoc/gmsm v1.4.1 // indirect diff --git a/go.sum b/go.sum index be6071e..0ed4fa8 100644 --- a/go.sum +++ b/go.sum @@ -13,15 +13,18 @@ github.com/alibabacloud-go/alibabacloud-gateway-pop v0.0.6/go.mod h1:4EUIoxs/do2 github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4/go.mod h1:sCavSAvdzOjul4cEqeVtvlSaSScfNsTQ+46HwlTL1hc= github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.5 h1:zE8vH9C7JiZLNJJQ5OwjU9mSi4T9ef9u3BURT6LCLC8= github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.5/go.mod h1:tWnyE9AjF8J8qqLk645oUmVUnFybApTQWklQmi5tY6g= +github.com/alibabacloud-go/cloudauth-20190307/v4 v4.6.0 h1:msLjhAxXUrGVPCmVIz2SYMdERk4UDlMxaDTFt10GnXE= +github.com/alibabacloud-go/cloudauth-20190307/v4 v4.6.0/go.mod h1:XqPmDB32MyabzEsJBBzcFfRudijqlyyPyKBGRSbErYM= github.com/alibabacloud-go/darabonba-array v0.1.0 h1:vR8s7b1fWAQIjEjWnuF0JiKsCvclSRTfDzZHTYqfufY= github.com/alibabacloud-go/darabonba-array v0.1.0/go.mod h1:BLKxr0brnggqOJPqT09DFJ8g3fsDshapUD3C3aOEFaI= github.com/alibabacloud-go/darabonba-encode-util v0.0.2 h1:1uJGrbsGEVqWcWxrS9MyC2NG0Ax+GpOM5gtupki31XE= github.com/alibabacloud-go/darabonba-encode-util v0.0.2/go.mod h1:JiW9higWHYXm7F4PKuMgEUETNZasrDM6vqVr/Can7H8= github.com/alibabacloud-go/darabonba-map v0.0.2 h1:qvPnGB4+dJbJIxOOfawxzF3hzMnIpjmafa0qOTp6udc= github.com/alibabacloud-go/darabonba-map v0.0.2/go.mod h1:28AJaX8FOE/ym8OUFWga+MtEzBunJwQGceGQlvaPGPc= +github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.0/go.mod h1:5JHVmnHvGzR2wNdgaW1zDLQG8kOC4Uec8ubkMogW7OQ= github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.2/go.mod h1:5JHVmnHvGzR2wNdgaW1zDLQG8kOC4Uec8ubkMogW7OQ= -github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.10 h1:GEYkMApgpKEVDn6z12DcH1EGYpDYRB8JxsazM4Rywak= -github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.10/go.mod h1:26a14FGhZVELuz2cc2AolvW4RHmIO3/HRwsdHhaIPDE= +github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.11 h1:GkVQ9AphMCmgAYakcTpH/OuFz0mQUypO/JiOvo0wgVA= +github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.11/go.mod h1:wHxkgZT1ClZdcwEVP/pDgYK/9HucsnCfMipmJgCz4xY= github.com/alibabacloud-go/darabonba-signature-util v0.0.7 h1:UzCnKvsjPFzApvODDNEYqBHMFt1w98wC7FOo0InLyxg= github.com/alibabacloud-go/darabonba-signature-util v0.0.7/go.mod h1:oUzCYV2fcCH797xKdL6BDH8ADIHlzrtKVjeRtunBNTQ= github.com/alibabacloud-go/darabonba-string v1.0.2 h1:E714wms5ibdzCqGeYJ9JCFywE5nDyvIXIIQbZVFkkqo= @@ -35,19 +38,35 @@ github.com/alibabacloud-go/dysmsapi-20170525/v3 v3.0.6/go.mod h1:UWpcGrWwTbES9QW github.com/alibabacloud-go/endpoint-util v1.1.0 h1:r/4D3VSw888XGaeNpP994zDUaxdgTSHBbVfZlzf6b5Q= github.com/alibabacloud-go/endpoint-util v1.1.0/go.mod h1:O5FuCALmCKs2Ff7JFJMudHs0I5EBgecXXxZRyswlEjE= github.com/alibabacloud-go/openapi-util v0.0.11/go.mod h1:sQuElr4ywwFRlCCberQwKRFhRzIyG4QTP/P4y1CJ6Ws= -github.com/alibabacloud-go/openapi-util v0.1.0 h1:0z75cIULkDrdEhkLWgi9tnLe+KhAFE/r5Pb3312/eAY= github.com/alibabacloud-go/openapi-util v0.1.0/go.mod h1:sQuElr4ywwFRlCCberQwKRFhRzIyG4QTP/P4y1CJ6Ws= +github.com/alibabacloud-go/openapi-util v0.1.1 h1:ujGErJjG8ncRW6XtBBMphzHTvCxn4DjrVw4m04HsS28= +github.com/alibabacloud-go/openapi-util v0.1.1/go.mod h1:/UehBSE2cf1gYT43GV4E+RxTdLRzURImCYY0aRmlXpw= +github.com/alibabacloud-go/openplatform-20191219/v2 v2.0.1 h1:L0TIjr9Qh/SLVc1yPhFkcB9+9SbCNK/jPq4ZKB5zmnc= +github.com/alibabacloud-go/openplatform-20191219/v2 v2.0.1/go.mod h1:EKxBRDLcMzwl4VLF/1WJwlByZZECJawPXUvinKMsTTs= github.com/alibabacloud-go/tea v1.1.0/go.mod h1:IkGyUSX4Ba1V+k4pCtJUc6jDpZLFph9QMy2VUPTwukg= github.com/alibabacloud-go/tea v1.1.7/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeGOFfwYm3N/p4= github.com/alibabacloud-go/tea v1.1.8/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeGOFfwYm3N/p4= +github.com/alibabacloud-go/tea v1.1.10/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeGOFfwYm3N/p4= github.com/alibabacloud-go/tea v1.1.11/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeGOFfwYm3N/p4= github.com/alibabacloud-go/tea v1.1.17/go.mod h1:nXxjm6CIFkBhwW4FQkNrolwbfon8Svy6cujmKFUq98A= github.com/alibabacloud-go/tea v1.1.19/go.mod h1:nXxjm6CIFkBhwW4FQkNrolwbfon8Svy6cujmKFUq98A= github.com/alibabacloud-go/tea v1.1.20/go.mod h1:nXxjm6CIFkBhwW4FQkNrolwbfon8Svy6cujmKFUq98A= github.com/alibabacloud-go/tea v1.2.2 h1:aTsR6Rl3ANWPfqeQugPglfurloyBJY85eFy7Gc1+8oU= github.com/alibabacloud-go/tea v1.2.2/go.mod h1:CF3vOzEMAG+bR4WOql8gc2G9H3EkH3ZLAQdpmpXMgwk= -github.com/alibabacloud-go/tea-utils v1.3.1 h1:iWQeRzRheqCMuiF3+XkfybB3kTgUXkXX+JMrqfLeB2I= +github.com/alibabacloud-go/tea-fileform v1.1.1 h1:1YG6erAP3joQ0XdCXYIotuD7zyOM6qCR49xkp5FZDeU= +github.com/alibabacloud-go/tea-fileform v1.1.1/go.mod h1:ZeCV91o4ISmxidd686f0ebdS5EDHWU+vW+TkjLhrsFE= +github.com/alibabacloud-go/tea-oss-sdk v1.1.5 h1:CFUFcqanvBaoGN/CyTHUZrVNtFZd1WTjem46m0HTTV0= +github.com/alibabacloud-go/tea-oss-sdk v1.1.5/go.mod h1:5fhlKMa/kWRJNgPYRt+5qSg3UidRvNbf9Z2bI8Dp5/s= +github.com/alibabacloud-go/tea-oss-utils v1.1.0 h1:y65crjjcZ2Pbb6UZtC2deuIZHDVTS3IaDWE7M9nVLRc= +github.com/alibabacloud-go/tea-oss-utils v1.1.0/go.mod h1:PFCF12e9yEKyBUIn7X1IrF/pNjvxgkHy0CgxX4+xRuY= +github.com/alibabacloud-go/tea-rpc v1.1.7 h1:txkbgBb5DUBwQKqw9I+JY49eXGvvoAUSU2jnCr7Di+I= +github.com/alibabacloud-go/tea-rpc v1.1.7/go.mod h1:FU//dNbNYv1jSz1BGj6Sc3Co0IrbWsCnPiB/e1YnvgQ= +github.com/alibabacloud-go/tea-rpc-utils v1.1.1 h1:pthm/FnXIqXMNXXJhocvfEkbzUhHD8oWa10BlCHVKMw= +github.com/alibabacloud-go/tea-rpc-utils v1.1.1/go.mod h1:V5HdNi6Xdn0JMpgVhQ19vsFAS51tydr7BqcJtuXH1Yw= github.com/alibabacloud-go/tea-utils v1.3.1/go.mod h1:EI/o33aBfj3hETm4RLiAxF/ThQdSngxrpF8rKUDJjPE= +github.com/alibabacloud-go/tea-utils v1.3.4/go.mod h1:EI/o33aBfj3hETm4RLiAxF/ThQdSngxrpF8rKUDJjPE= +github.com/alibabacloud-go/tea-utils v1.4.5 h1:h0/6Xd2f3bPE4XHTvkpjwxowIwRCJAJOqY6Eq8f3zfA= +github.com/alibabacloud-go/tea-utils v1.4.5/go.mod h1:KNcT0oXlZZxOXINnZBs6YvgOd5aYp9U67G+E3R8fcQw= github.com/alibabacloud-go/tea-utils/v2 v2.0.0/go.mod h1:U5MTY10WwlquGPS34DOeomUGBB0gXbLueiq5Trwu0C4= github.com/alibabacloud-go/tea-utils/v2 v2.0.3/go.mod h1:sj1PbjPodAVTqGTA3olprfeeqqmwD0A5OQz94o9EuXQ= github.com/alibabacloud-go/tea-utils/v2 v2.0.5/go.mod h1:dL6vbUT35E4F4bFTHL845eUloqaerYBYPsdWR2/jhe4= @@ -64,8 +83,9 @@ github.com/alicebob/miniredis/v2 v2.33.0/go.mod h1:MhP4a3EU7aENRi9aO+tHfTBZicLqQ github.com/aliyun/credentials-go v1.1.2/go.mod h1:ozcZaMR5kLM7pwtCMEpVmQ242suV6qTJya2bDq4X1Tw= github.com/aliyun/credentials-go v1.3.1/go.mod h1:8jKYhQuDawt8x2+fusqa1Y6mPxemTsBEN04dgcAcYz0= github.com/aliyun/credentials-go v1.3.6/go.mod h1:1LxUuX7L5YrZUWzBrRyk0SwSdH4OmPrib8NVePL3fxM= -github.com/aliyun/credentials-go v1.3.10 h1:45Xxrae/evfzQL9V10zL3xX31eqgLWEaIdCoPipOEQA= -github.com/aliyun/credentials-go v1.3.10/go.mod h1:Jm6d+xIgwJVLVWT561vy67ZRP4lPTQxMbEYRuT2Ti1U= +github.com/aliyun/credentials-go v1.4.5/go.mod h1:Jm6d+xIgwJVLVWT561vy67ZRP4lPTQxMbEYRuT2Ti1U= +github.com/aliyun/credentials-go v1.4.6 h1:CG8rc/nxCNKfXbZWpWDzI9GjF4Tuu3Es14qT8Y0ClOk= +github.com/aliyun/credentials-go v1.4.6/go.mod h1:Jm6d+xIgwJVLVWT561vy67ZRP4lPTQxMbEYRuT2Ti1U= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= @@ -261,8 +281,6 @@ github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= -github.com/samber/lo v1.49.1 h1:4BIFyVfuQSEpluc7Fua+j1NolZHiEHEpaSEKdsH0tew= -github.com/samber/lo v1.49.1/go.mod h1:dO6KHFzUKXgP8LDhU0oI8d2hekjXnGOu0DB8Jecxd6o= github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= github.com/smartwalle/alipay/v3 v3.2.23 h1:i1VwJeu70EmwpsXXz6GZZnMAtRx5MTfn2dPoql/L3zE= @@ -298,9 +316,9 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= @@ -460,10 +478,9 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= -golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ= golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/pkg/core/aliyun/cloudauth/client.go b/pkg/core/aliyun/cloudauth/client.go new file mode 100644 index 0000000..c4cdb11 --- /dev/null +++ b/pkg/core/aliyun/cloudauth/client.go @@ -0,0 +1,178 @@ +package cloudauth + +import ( + "encoding/json" + "fmt" + + cloudauth "github.com/alibabacloud-go/cloudauth-20190307/v4/client" + openapi "github.com/alibabacloud-go/darabonba-openapi/v2/client" + util "github.com/alibabacloud-go/tea-utils/v2/service" + + "github.com/alibabacloud-go/tea/tea" + "github.com/aliyun/credentials-go/credentials" + "github.com/zeromicro/go-zero/core/logx" +) + +type CloudAuthClient struct { + Client *cloudauth.Client + Config *CloudAuthConfig +} + +type CloudAuthConfig struct { + AccessKeyId string + AccessKeySecret string + Endpoint string + SceneId int64 + ReturnUrl string +} +type InitFaceVerifyResp struct { + CertifyId string + CertifyUrl string +} +type DescribeFaceVerifyResp struct { + Passed bool +} + +// InitFaceVerifyRequest 封装初始化人脸认证所需参数 +type InitFaceVerifyParam struct { + OuterOrderNo string + CertName string + CertNo string + MetaInfo string +} + +// NewCloudAuthClient 创建阿里云人脸认证客户端 +func NewCloudAuthClient(cloudAuthconfig CloudAuthConfig) (*CloudAuthClient, error) { + // 使用AK 初始化Credentials Client。 + credentialsConfig := new(credentials.Config). + // 凭证类型。 + SetType("access_key"). + // 设置为AccessKey ID值。 + SetAccessKeyId(cloudAuthconfig.AccessKeyId). + // 设置为AccessKey Secret值。 + SetAccessKeySecret(cloudAuthconfig.AccessKeySecret) + credentialClient, _err := credentials.NewCredential(credentialsConfig) + if _err != nil { + panic(_err) + } + + ecsConfig := &openapi.Config{} + // 配置云产品服务接入地址(endpoint)。 + ecsConfig.Endpoint = tea.String(cloudAuthconfig.Endpoint) + // 使用Credentials配置凭证。 + ecsConfig.Credential = credentialClient + + // 创建客户端 + client, err := cloudauth.NewClient(ecsConfig) + if err != nil { + panic(fmt.Sprintf("创建阿里云人脸认证客户端失败: %v", err)) + } + + return &CloudAuthClient{ + Client: client, + Config: &CloudAuthConfig{ + AccessKeyId: cloudAuthconfig.AccessKeyId, + AccessKeySecret: cloudAuthconfig.AccessKeySecret, + Endpoint: cloudAuthconfig.Endpoint, + SceneId: cloudAuthconfig.SceneId, + ReturnUrl: cloudAuthconfig.ReturnUrl, + }, + }, nil +} + +// InitFaceVerify 初始化人脸认证 +func (c *CloudAuthClient) InitFaceVerify(param InitFaceVerifyParam) (*InitFaceVerifyResp, error) { + request := &cloudauth.InitFaceVerifyRequest{ + SceneId: tea.Int64(c.Config.SceneId), + OuterOrderNo: tea.String(param.OuterOrderNo), + ProductCode: tea.String("ID_PRO"), + Model: tea.String("LIVENESS"), + CertType: tea.String("IDENTITY_CARD"), + CertName: tea.String(param.CertName), + CertNo: tea.String(param.CertNo), + MetaInfo: tea.String(param.MetaInfo), + ReturnUrl: tea.String(c.Config.ReturnUrl), + } + + runtime := &util.RuntimeOptions{ + ReadTimeout: tea.Int(10000), + ConnectTimeout: tea.Int(5000), + } + + response, err := c.Client.InitFaceVerifyWithOptions(request, runtime) + if err != nil { + if sdkErr, ok := err.(*tea.SDKError); ok { + logx.Errorf("认证初始化失败: %s, 建议: %s", + tea.StringValue(sdkErr.Message), + getRecommendFromError(sdkErr)) + return nil, fmt.Errorf("认证初始化失败: %s", tea.StringValue(sdkErr.Message)) + } + logx.Errorf("认证初始化失败: %v", err) + return nil, fmt.Errorf("认证初始化失败: %v", err) + } + + if tea.StringValue(response.Body.Code) != "200" { + logx.Errorf("认证初始化失败: %s", tea.StringValue(response.Body.Message)) + return nil, fmt.Errorf("认证初始化失败: %s", tea.StringValue(response.Body.Message)) + } + + return &InitFaceVerifyResp{ + CertifyId: tea.StringValue(response.Body.ResultObject.CertifyId), + CertifyUrl: tea.StringValue(response.Body.ResultObject.CertifyUrl), + }, nil +} + +// DescribeFaceVerify 获取认证结果 +func (c *CloudAuthClient) DescribeFaceVerify(certifyId string) (*DescribeFaceVerifyResp, error) { + request := &cloudauth.DescribeFaceVerifyRequest{ + SceneId: tea.Int64(c.Config.SceneId), + CertifyId: tea.String(certifyId), + } + + runtime := &util.RuntimeOptions{ + ReadTimeout: tea.Int(10000), + ConnectTimeout: tea.Int(5000), + } + response, err := c.Client.DescribeFaceVerifyWithOptions(request, runtime) + if err != nil { + logx.Errorf("获取认证结果失败: %v", err) + return nil, fmt.Errorf("获取认证结果失败: %v", err) + } + + if tea.StringValue(response.Body.Code) != "200" { + logx.Errorf("获取认证结果失败: %s", tea.StringValue(response.Body.Message)) + return nil, fmt.Errorf("获取认证结果失败: %s", tea.StringValue(response.Body.Message)) + } + var passed bool + var passedStr = tea.StringValue(response.Body.ResultObject.Passed) + + switch passedStr { + case "T": + passed = true + case "F": + passed = false + default: + passed = false + } + return &DescribeFaceVerifyResp{ + Passed: passed, + }, nil +} + +// 从SDK错误中获取推荐信息 +func getRecommendFromError(err *tea.SDKError) string { + if err == nil || err.Data == nil { + return "" + } + + var data map[string]interface{} + if err := json.Unmarshal([]byte(tea.StringValue(err.Data)), &data); err != nil { + return "" + } + + if recommend, ok := data["Recommend"].(string); ok { + return recommend + } + + return "" +} diff --git a/pkg/lzkit/crypto/crypto_test.go b/pkg/lzkit/crypto/crypto_test.go new file mode 100644 index 0000000..6d8422f --- /dev/null +++ b/pkg/lzkit/crypto/crypto_test.go @@ -0,0 +1,31 @@ +package crypto + +import ( + "encoding/hex" + "fmt" + "testing" +) + +func TestAesEncryptDecrypt(t *testing.T) { + keyHex := "ff83609b2b24fc73196aac3d3dfb874f" + key, decodeErr := hex.DecodeString(keyHex) + if decodeErr != nil { + t.Fatalf("key decode error: %v", decodeErr) + } + + plainText := "45212220000827423X" + + cipherText, err := AesEncrypt([]byte(plainText), key) + if err != nil { + t.Fatalf("AesEncrypt error: %v", err) + } + fmt.Println(cipherText) + decrypted, err := AesDecrypt(cipherText, key) + if err != nil { + t.Fatalf("AesDecrypt error: %v", err) + } + + if string(decrypted) != plainText { + t.Errorf("AesDecrypt result not match, got: %s, want: %s", string(decrypted), plainText) + } +}