diff --git a/app/main/api/desc/admin/admin_whitelist.api b/app/main/api/desc/admin/admin_whitelist.api deleted file mode 100644 index 12402fc..0000000 --- a/app/main/api/desc/admin/admin_whitelist.api +++ /dev/null @@ -1,103 +0,0 @@ -syntax = "v1" - -info ( - title: "后台白名单管理服务" - desc: "后台用户模块白名单管理相关接口" - version: "v1" -) - -@server ( - prefix: /api/v1/admin/whitelist - group: admin_whitelist - middleware: AdminAuthInterceptor -) -service main { - // 获取白名单列表 - @handler AdminGetWhitelistList - get /list (AdminGetWhitelistListReq) returns (AdminGetWhitelistListResp) - - // 手动添加白名单 - @handler AdminCreateWhitelist - post /create (AdminCreateWhitelistReq) returns (AdminCreateWhitelistResp) - - // 批量添加白名单 - @handler AdminBatchCreateWhitelist - post /batch-create (AdminBatchCreateWhitelistReq) returns (AdminBatchCreateWhitelistResp) - - // 更新白名单(状态/备注金额) - @handler AdminUpdateWhitelist - put /update/:id (AdminUpdateWhitelistReq) returns (AdminUpdateWhitelistResp) - - // 删除白名单(软删除) - @handler AdminDeleteWhitelist - delete /delete/:id (AdminDeleteWhitelistReq) returns (AdminDeleteWhitelistResp) -} - -type ( - AdminGetWhitelistListReq { - Page int64 `form:"page"` - PageSize int64 `form:"pageSize"` - IdCard *string `form:"id_card,optional"` - FeatureApiId *string `form:"feature_api_id,optional"` - Status *int64 `form:"status,optional"` - } - AdminWhitelistListItem { - Id string `json:"id"` - IdCard string `json:"id_card"` - FeatureId string `json:"feature_id"` - FeatureApiId string `json:"feature_api_id"` - FeatureName string `json:"feature_name"` - UserId string `json:"user_id"` - Amount float64 `json:"amount"` - Status int64 `json:"status"` - StatusText string `json:"status_text"` - CreateTime string `json:"create_time"` - UpdateTime string `json:"update_time"` - } - AdminGetWhitelistListResp { - Total int64 `json:"total"` - Items []AdminWhitelistListItem `json:"items"` - } - AdminCreateWhitelistReq { - IdCard string `json:"id_card"` - FeatureId string `json:"feature_id"` - Amount *float64 `json:"amount,optional"` - Status *int64 `json:"status,optional"` - } - AdminCreateWhitelistResp { - Id string `json:"id"` - } - AdminBatchCreateWhitelistReq { - IdCard string `json:"id_card"` - FeatureIds []string `json:"feature_ids"` - Amount *float64 `json:"amount,optional"` - Status *int64 `json:"status,optional"` - } - AdminBatchCreateWhitelistResultItem { - FeatureId string `json:"feature_id"` - FeatureApiId string `json:"feature_api_id"` - FeatureName string `json:"feature_name"` - Action string `json:"action"` - Message string `json:"message,optional"` - } - AdminBatchCreateWhitelistResp { - SuccessCount int64 `json:"success_count"` - SkipCount int64 `json:"skip_count"` - FailCount int64 `json:"fail_count"` - Items []AdminBatchCreateWhitelistResultItem `json:"items"` - } - AdminUpdateWhitelistReq { - Id string `path:"id"` - Status *int64 `json:"status,optional"` - Amount *float64 `json:"amount,optional"` - } - AdminUpdateWhitelistResp { - Success bool `json:"success"` - } - AdminDeleteWhitelistReq { - Id string `path:"id"` - } - AdminDeleteWhitelistResp { - Success bool `json:"success"` - } -) diff --git a/app/main/api/desc/main.api b/app/main/api/desc/main.api index 42e5d1b..a47aa7e 100644 --- a/app/main/api/desc/main.api +++ b/app/main/api/desc/main.api @@ -24,7 +24,6 @@ import "./admin/platform_user.api" import "./admin/notification.api" import "./admin/admin_product.api" import "./admin/admin_feature.api" -import "./admin/admin_whitelist.api" import "./admin/admin_query_whitelist.api" import "./admin/admin_query.api" import "./admin/admin_agent.api" diff --git a/app/main/api/internal/handler/admin_whitelist/adminbatchcreatewhitelisthandler.go b/app/main/api/internal/handler/admin_whitelist/adminbatchcreatewhitelisthandler.go deleted file mode 100644 index 0c93374..0000000 --- a/app/main/api/internal/handler/admin_whitelist/adminbatchcreatewhitelisthandler.go +++ /dev/null @@ -1,29 +0,0 @@ -package admin_whitelist - -import ( - "net/http" - - "github.com/zeromicro/go-zero/rest/httpx" - "ycc-server/app/main/api/internal/logic/admin_whitelist" - "ycc-server/app/main/api/internal/svc" - "ycc-server/app/main/api/internal/types" - "ycc-server/common/result" - "ycc-server/pkg/lzkit/validator" -) - -func AdminBatchCreateWhitelistHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - var req types.AdminBatchCreateWhitelistReq - 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 := admin_whitelist.NewAdminBatchCreateWhitelistLogic(r.Context(), svcCtx) - resp, err := l.AdminBatchCreateWhitelist(&req) - result.HttpResult(r, w, resp, err) - } -} diff --git a/app/main/api/internal/handler/admin_whitelist/admincreatewhitelisthandler.go b/app/main/api/internal/handler/admin_whitelist/admincreatewhitelisthandler.go deleted file mode 100644 index 128bd47..0000000 --- a/app/main/api/internal/handler/admin_whitelist/admincreatewhitelisthandler.go +++ /dev/null @@ -1,29 +0,0 @@ -package admin_whitelist - -import ( - "net/http" - - "github.com/zeromicro/go-zero/rest/httpx" - "ycc-server/app/main/api/internal/logic/admin_whitelist" - "ycc-server/app/main/api/internal/svc" - "ycc-server/app/main/api/internal/types" - "ycc-server/common/result" - "ycc-server/pkg/lzkit/validator" -) - -func AdminCreateWhitelistHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - var req types.AdminCreateWhitelistReq - 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 := admin_whitelist.NewAdminCreateWhitelistLogic(r.Context(), svcCtx) - resp, err := l.AdminCreateWhitelist(&req) - result.HttpResult(r, w, resp, err) - } -} diff --git a/app/main/api/internal/handler/admin_whitelist/admindeletewhitelisthandler.go b/app/main/api/internal/handler/admin_whitelist/admindeletewhitelisthandler.go deleted file mode 100644 index 6414f7b..0000000 --- a/app/main/api/internal/handler/admin_whitelist/admindeletewhitelisthandler.go +++ /dev/null @@ -1,29 +0,0 @@ -package admin_whitelist - -import ( - "net/http" - - "github.com/zeromicro/go-zero/rest/httpx" - "ycc-server/app/main/api/internal/logic/admin_whitelist" - "ycc-server/app/main/api/internal/svc" - "ycc-server/app/main/api/internal/types" - "ycc-server/common/result" - "ycc-server/pkg/lzkit/validator" -) - -func AdminDeleteWhitelistHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - var req types.AdminDeleteWhitelistReq - 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 := admin_whitelist.NewAdminDeleteWhitelistLogic(r.Context(), svcCtx) - resp, err := l.AdminDeleteWhitelist(&req) - result.HttpResult(r, w, resp, err) - } -} diff --git a/app/main/api/internal/handler/admin_whitelist/admingetwhitelistlisthandler.go b/app/main/api/internal/handler/admin_whitelist/admingetwhitelistlisthandler.go deleted file mode 100644 index ce67eaf..0000000 --- a/app/main/api/internal/handler/admin_whitelist/admingetwhitelistlisthandler.go +++ /dev/null @@ -1,29 +0,0 @@ -package admin_whitelist - -import ( - "net/http" - - "github.com/zeromicro/go-zero/rest/httpx" - "ycc-server/app/main/api/internal/logic/admin_whitelist" - "ycc-server/app/main/api/internal/svc" - "ycc-server/app/main/api/internal/types" - "ycc-server/common/result" - "ycc-server/pkg/lzkit/validator" -) - -func AdminGetWhitelistListHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - var req types.AdminGetWhitelistListReq - 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 := admin_whitelist.NewAdminGetWhitelistListLogic(r.Context(), svcCtx) - resp, err := l.AdminGetWhitelistList(&req) - result.HttpResult(r, w, resp, err) - } -} diff --git a/app/main/api/internal/handler/admin_whitelist/adminupdatewhitelisthandler.go b/app/main/api/internal/handler/admin_whitelist/adminupdatewhitelisthandler.go deleted file mode 100644 index 53bf7fe..0000000 --- a/app/main/api/internal/handler/admin_whitelist/adminupdatewhitelisthandler.go +++ /dev/null @@ -1,29 +0,0 @@ -package admin_whitelist - -import ( - "net/http" - - "github.com/zeromicro/go-zero/rest/httpx" - "ycc-server/app/main/api/internal/logic/admin_whitelist" - "ycc-server/app/main/api/internal/svc" - "ycc-server/app/main/api/internal/types" - "ycc-server/common/result" - "ycc-server/pkg/lzkit/validator" -) - -func AdminUpdateWhitelistHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - var req types.AdminUpdateWhitelistReq - 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 := admin_whitelist.NewAdminUpdateWhitelistLogic(r.Context(), svcCtx) - resp, err := l.AdminUpdateWhitelist(&req) - result.HttpResult(r, w, resp, err) - } -} diff --git a/app/main/api/internal/handler/routes.go b/app/main/api/internal/handler/routes.go index a71f064..4a68740 100644 --- a/app/main/api/internal/handler/routes.go +++ b/app/main/api/internal/handler/routes.go @@ -20,7 +20,6 @@ import ( admin_role "ycc-server/app/main/api/internal/handler/admin_role" admin_role_api "ycc-server/app/main/api/internal/handler/admin_role_api" admin_user "ycc-server/app/main/api/internal/handler/admin_user" - admin_whitelist "ycc-server/app/main/api/internal/handler/admin_whitelist" agent "ycc-server/app/main/api/internal/handler/agent" app "ycc-server/app/main/api/internal/handler/app" auth "ycc-server/app/main/api/internal/handler/auth" @@ -655,40 +654,6 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) { rest.WithPrefix("/api/v1/admin/user"), ) - server.AddRoutes( - rest.WithMiddlewares( - []rest.Middleware{serverCtx.AdminAuthInterceptor}, - []rest.Route{ - { - Method: http.MethodPost, - Path: "/batch-create", - Handler: admin_whitelist.AdminBatchCreateWhitelistHandler(serverCtx), - }, - { - Method: http.MethodPost, - Path: "/create", - Handler: admin_whitelist.AdminCreateWhitelistHandler(serverCtx), - }, - { - Method: http.MethodDelete, - Path: "/delete/:id", - Handler: admin_whitelist.AdminDeleteWhitelistHandler(serverCtx), - }, - { - Method: http.MethodGet, - Path: "/list", - Handler: admin_whitelist.AdminGetWhitelistListHandler(serverCtx), - }, - { - Method: http.MethodPut, - Path: "/update/:id", - Handler: admin_whitelist.AdminUpdateWhitelistHandler(serverCtx), - }, - }..., - ), - rest.WithPrefix("/api/v1/admin/whitelist"), - ) - server.AddRoutes( rest.WithMiddlewares( []rest.Middleware{serverCtx.AdminAuthInterceptor}, diff --git a/app/main/api/internal/logic/admin_query/admindeletequeryfeaturedatalogic.go b/app/main/api/internal/logic/admin_query/admindeletequeryfeaturedatalogic.go index 32d0001..9ad0b01 100644 --- a/app/main/api/internal/logic/admin_query/admindeletequeryfeaturedatalogic.go +++ b/app/main/api/internal/logic/admin_query/admindeletequeryfeaturedatalogic.go @@ -2,10 +2,17 @@ package admin_query import ( "context" + "encoding/hex" + "encoding/json" + "fmt" + "strings" "ycc-server/app/main/api/internal/svc" "ycc-server/app/main/api/internal/types" + "ycc-server/app/main/model" + "ycc-server/common/ctxdata" "ycc-server/common/xerr" + "ycc-server/pkg/lzkit/crypto" "github.com/pkg/errors" "github.com/zeromicro/go-zero/core/logx" @@ -27,7 +34,6 @@ func NewAdminDeleteQueryFeatureDataLogic(ctx context.Context, svcCtx *svc.Servic } func (l *AdminDeleteQueryFeatureDataLogic) AdminDeleteQueryFeatureData(req *types.AdminDeleteQueryFeatureDataReq) (resp *types.AdminDeleteQueryFeatureDataResp, err error) { - // 基本参数校验 if req.QueryId == "" { return nil, errors.Wrapf(xerr.NewErrMsg("QueryId 不能为空"), "") } @@ -35,8 +41,56 @@ func (l *AdminDeleteQueryFeatureDataLogic) AdminDeleteQueryFeatureData(req *type return nil, errors.Wrapf(xerr.NewErrMsg("feature_api_id 不能为空"), "") } - // 使用事务调用 WhitelistService.DeleteFeatureFromQueryData,保持与其它删除逻辑一致 + queryModel, err := l.svcCtx.QueryModel.FindOne(l.ctx, req.QueryId) + if err != nil { + if errors.Is(err, model.ErrNotFound) { + return nil, errors.Wrapf(xerr.NewErrMsg("查询记录不存在"), "") + } + return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询报告记录失败, %v", err) + } + + queryParams, err := decryptQueryParams(l.svcCtx, queryModel) + if err != nil { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取查询参数失败, %v", err) + } + + idCard, _ := queryParams["id_card"].(string) + if idCard == "" { + return nil, errors.Wrapf(xerr.NewErrMsg("查询参数中缺少身份证号,无法添加白名单"), "") + } + + name, _ := queryParams["name"].(string) + if strings.TrimSpace(name) == "" { + name = "*" + } + + adminUserID, err := ctxdata.GetUidFromCtx(l.ctx) + if err != nil { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取管理员信息失败, %v", err) + } + + mainApiId := extractMainApiId(req.FeatureApiId) + feature, err := l.svcCtx.FeatureModel.FindOneByApiId(l.ctx, mainApiId) + if err != nil { + if errors.Is(err, model.ErrNotFound) { + return nil, errors.Wrapf(xerr.NewErrMsg("模块不存在"), "") + } + return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询模块信息失败, %v", err) + } + + remark := fmt.Sprintf("后台删除报告模块自动同步 query_id=%s feature=%s", req.QueryId, feature.ApiId) + l.svcCtx.QueryWhitelistSyncService.TrySync( + l.ctx, + name, + idCard, + []string{feature.ApiId}, + remark, + ) + err = l.svcCtx.QueryModel.Trans(l.ctx, func(ctx context.Context, session sqlx.Session) error { + if wlErr := l.svcCtx.WhitelistService.EnsureFreeWhitelist(ctx, session, idCard, feature, adminUserID, req.QueryId); wlErr != nil { + return wlErr + } return l.svcCtx.WhitelistService.DeleteFeatureFromQueryData(ctx, session, req.QueryId, req.FeatureApiId) }) if err != nil { @@ -46,6 +100,37 @@ func (l *AdminDeleteQueryFeatureDataLogic) AdminDeleteQueryFeatureData(req *type return &types.AdminDeleteQueryFeatureDataResp{ Success: true, - Message: "删除成功(如果原本不存在该模块数据,则视为已删除)", + Message: "删除成功,已同步本地白名单并尽力同步天远查询白名单(若原本不存在该模块数据,则视为已删除)", }, nil } + +func extractMainApiId(featureApiId string) string { + if idx := strings.Index(featureApiId, "_"); idx > 0 { + return featureApiId[:idx] + } + return featureApiId +} + +func decryptQueryParams(svcCtx *svc.ServiceContext, queryModel *model.Query) (map[string]interface{}, error) { + if queryModel.QueryParams == "" { + return nil, errors.New("查询参数为空") + } + + secretKey := svcCtx.Config.Encrypt.SecretKey + key, decodeErr := hex.DecodeString(secretKey) + if decodeErr != nil { + return nil, errors.Wrap(decodeErr, "获取AES密钥失败") + } + + decryptedParams, decryptErr := crypto.AesDecrypt(queryModel.QueryParams, key) + if decryptErr != nil { + return nil, errors.Wrap(decryptErr, "解密查询参数失败") + } + + var params map[string]interface{} + if unmarshalErr := json.Unmarshal(decryptedParams, ¶ms); unmarshalErr != nil { + return nil, errors.Wrap(unmarshalErr, "解析查询参数失败") + } + + return params, nil +} diff --git a/app/main/api/internal/logic/admin_query_whitelist/query_whitelist_helper.go b/app/main/api/internal/logic/admin_query_whitelist/query_whitelist_helper.go index b381b1f..c1c3b46 100644 --- a/app/main/api/internal/logic/admin_query_whitelist/query_whitelist_helper.go +++ b/app/main/api/internal/logic/admin_query_whitelist/query_whitelist_helper.go @@ -53,6 +53,17 @@ func buildQueryWhitelistPayload(name, idCard string, apiCodes []string, remark s return payload } +// SyncQueryWhitelistToTianyuan 将产品编码同步到天远查询白名单(先追加,规则不存在则创建) +func SyncQueryWhitelistToTianyuan( + ctx context.Context, + svcCtx *svc.ServiceContext, + name, idCard string, + apiCodes []string, + remark string, +) error { + return svcCtx.QueryWhitelistSyncService.Sync(ctx, name, idCard, apiCodes, remark) +} + func callQueryWhitelistMgmt( svcCtx *svc.ServiceContext, apiPath string, diff --git a/app/main/api/internal/logic/admin_whitelist/adminbatchcreatewhitelistlogic.go b/app/main/api/internal/logic/admin_whitelist/adminbatchcreatewhitelistlogic.go deleted file mode 100644 index d5b937e..0000000 --- a/app/main/api/internal/logic/admin_whitelist/adminbatchcreatewhitelistlogic.go +++ /dev/null @@ -1,99 +0,0 @@ -package admin_whitelist - -import ( - "context" - - "ycc-server/app/main/api/internal/svc" - "ycc-server/app/main/api/internal/types" - "ycc-server/app/main/model" - "ycc-server/common/ctxdata" - "ycc-server/common/xerr" - - "github.com/pkg/errors" - "github.com/zeromicro/go-zero/core/logx" -) - -type AdminBatchCreateWhitelistLogic struct { - logx.Logger - ctx context.Context - svcCtx *svc.ServiceContext -} - -func NewAdminBatchCreateWhitelistLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminBatchCreateWhitelistLogic { - return &AdminBatchCreateWhitelistLogic{ - Logger: logx.WithContext(ctx), - ctx: ctx, - svcCtx: svcCtx, - } -} - -func (l *AdminBatchCreateWhitelistLogic) AdminBatchCreateWhitelist(req *types.AdminBatchCreateWhitelistReq) (resp *types.AdminBatchCreateWhitelistResp, err error) { - if req.IdCard == "" { - return nil, errors.Wrapf(xerr.NewErrMsg("身份证号不能为空"), "") - } - if len(req.FeatureIds) == 0 { - return nil, errors.Wrapf(xerr.NewErrMsg("请至少选择一个模块"), "") - } - - userID, err := ctxdata.GetUidFromCtx(l.ctx) - if err != nil { - return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取管理员信息失败, %v", err) - } - - amount := 0.0 - if req.Amount != nil { - amount = *req.Amount - } - status, err := parseWhitelistStatus(req.Status) - if err != nil { - return nil, err - } - - resp = &types.AdminBatchCreateWhitelistResp{ - Items: make([]types.AdminBatchCreateWhitelistResultItem, 0, len(req.FeatureIds)), - } - - for _, featureId := range req.FeatureIds { - item := types.AdminBatchCreateWhitelistResultItem{ - FeatureId: featureId, - } - - feature, findErr := l.svcCtx.FeatureModel.FindOne(l.ctx, featureId) - if findErr != nil { - if errors.Is(findErr, model.ErrNotFound) { - item.Action = "failed" - item.Message = "模块不存在" - resp.FailCount++ - } else { - return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询模块失败, %v", findErr) - } - resp.Items = append(resp.Items, item) - continue - } - - result, createErr := createOrReactivateWhitelist(l.ctx, l.svcCtx, userID, req.IdCard, feature, amount, status) - if createErr != nil { - item.FeatureApiId = feature.ApiId - item.FeatureName = feature.Name - item.Action = "failed" - item.Message = createErr.Error() - resp.FailCount++ - resp.Items = append(resp.Items, item) - continue - } - - item.FeatureApiId = result.FeatureApiId - item.FeatureName = result.FeatureName - item.Action = result.Action - item.Message = result.Message - switch result.Action { - case whitelistActionSkipped: - resp.SkipCount++ - case whitelistActionCreated, whitelistActionReactivated: - resp.SuccessCount++ - } - resp.Items = append(resp.Items, item) - } - - return resp, nil -} diff --git a/app/main/api/internal/logic/admin_whitelist/admincreatewhitelistlogic.go b/app/main/api/internal/logic/admin_whitelist/admincreatewhitelistlogic.go deleted file mode 100644 index 66a50ec..0000000 --- a/app/main/api/internal/logic/admin_whitelist/admincreatewhitelistlogic.go +++ /dev/null @@ -1,69 +0,0 @@ -package admin_whitelist - -import ( - "context" - - "ycc-server/app/main/api/internal/svc" - "ycc-server/app/main/api/internal/types" - "ycc-server/app/main/model" - "ycc-server/common/ctxdata" - "ycc-server/common/xerr" - - "github.com/pkg/errors" - "github.com/zeromicro/go-zero/core/logx" -) - -type AdminCreateWhitelistLogic struct { - logx.Logger - ctx context.Context - svcCtx *svc.ServiceContext -} - -func NewAdminCreateWhitelistLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminCreateWhitelistLogic { - return &AdminCreateWhitelistLogic{ - Logger: logx.WithContext(ctx), - ctx: ctx, - svcCtx: svcCtx, - } -} - -func (l *AdminCreateWhitelistLogic) AdminCreateWhitelist(req *types.AdminCreateWhitelistReq) (resp *types.AdminCreateWhitelistResp, err error) { - if req.IdCard == "" { - return nil, errors.Wrapf(xerr.NewErrMsg("身份证号不能为空"), "") - } - if req.FeatureId == "" { - return nil, errors.Wrapf(xerr.NewErrMsg("模块不能为空"), "") - } - - userID, err := ctxdata.GetUidFromCtx(l.ctx) - if err != nil { - return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取管理员信息失败, %v", err) - } - - feature, err := l.svcCtx.FeatureModel.FindOne(l.ctx, req.FeatureId) - if err != nil { - if errors.Is(err, model.ErrNotFound) { - return nil, errors.Wrapf(xerr.NewErrMsg("模块不存在"), "") - } - return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询模块失败, %v", err) - } - - amount := 0.0 - if req.Amount != nil { - amount = *req.Amount - } - status, err := parseWhitelistStatus(req.Status) - if err != nil { - return nil, err - } - - result, err := createOrReactivateWhitelist(l.ctx, l.svcCtx, userID, req.IdCard, feature, amount, status) - if err != nil { - return nil, err - } - if result.Action == whitelistActionSkipped { - return nil, errors.Wrapf(xerr.NewErrMsg("该身份证号已存在生效的白名单记录"), "") - } - - return &types.AdminCreateWhitelistResp{Id: result.Id}, nil -} diff --git a/app/main/api/internal/logic/admin_whitelist/admindeletewhitelistlogic.go b/app/main/api/internal/logic/admin_whitelist/admindeletewhitelistlogic.go deleted file mode 100644 index 86aae08..0000000 --- a/app/main/api/internal/logic/admin_whitelist/admindeletewhitelistlogic.go +++ /dev/null @@ -1,43 +0,0 @@ -package admin_whitelist - -import ( - "context" - - "ycc-server/app/main/api/internal/svc" - "ycc-server/app/main/api/internal/types" - "ycc-server/app/main/model" - "ycc-server/common/xerr" - - "github.com/pkg/errors" - "github.com/zeromicro/go-zero/core/logx" -) - -type AdminDeleteWhitelistLogic struct { - logx.Logger - ctx context.Context - svcCtx *svc.ServiceContext -} - -func NewAdminDeleteWhitelistLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminDeleteWhitelistLogic { - return &AdminDeleteWhitelistLogic{ - Logger: logx.WithContext(ctx), - ctx: ctx, - svcCtx: svcCtx, - } -} - -func (l *AdminDeleteWhitelistLogic) AdminDeleteWhitelist(req *types.AdminDeleteWhitelistReq) (resp *types.AdminDeleteWhitelistResp, err error) { - record, err := l.svcCtx.UserFeatureWhitelistModel.FindOne(l.ctx, req.Id) - if err != nil { - if errors.Is(err, model.ErrNotFound) { - return nil, errors.Wrapf(xerr.NewErrMsg("白名单记录不存在"), "") - } - return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询白名单记录失败, %v", err) - } - - if err = l.svcCtx.UserFeatureWhitelistModel.DeleteSoft(l.ctx, nil, record); err != nil { - return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "删除白名单记录失败, %v", err) - } - - return &types.AdminDeleteWhitelistResp{Success: true}, nil -} diff --git a/app/main/api/internal/logic/admin_whitelist/admingetwhitelistlistlogic.go b/app/main/api/internal/logic/admin_whitelist/admingetwhitelistlistlogic.go deleted file mode 100644 index e768d4c..0000000 --- a/app/main/api/internal/logic/admin_whitelist/admingetwhitelistlistlogic.go +++ /dev/null @@ -1,111 +0,0 @@ -package admin_whitelist - -import ( - "context" - - "ycc-server/app/main/api/internal/svc" - "ycc-server/app/main/api/internal/types" - "ycc-server/app/main/model" - "ycc-server/common/xerr" - - "github.com/Masterminds/squirrel" - "github.com/pkg/errors" - "github.com/zeromicro/go-zero/core/logx" -) - -type AdminGetWhitelistListLogic struct { - logx.Logger - ctx context.Context - svcCtx *svc.ServiceContext -} - -func NewAdminGetWhitelistListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminGetWhitelistListLogic { - return &AdminGetWhitelistListLogic{ - Logger: logx.WithContext(ctx), - ctx: ctx, - svcCtx: svcCtx, - } -} - -func (l *AdminGetWhitelistListLogic) AdminGetWhitelistList(req *types.AdminGetWhitelistListReq) (resp *types.AdminGetWhitelistListResp, err error) { - builder := l.svcCtx.UserFeatureWhitelistModel.SelectBuilder() - - if req.IdCard != nil && *req.IdCard != "" { - builder = builder.Where("id_card LIKE ?", "%"+*req.IdCard+"%") - } - if req.FeatureApiId != nil && *req.FeatureApiId != "" { - builder = builder.Where("feature_api_id LIKE ?", "%"+*req.FeatureApiId+"%") - } - if req.Status != nil { - builder = builder.Where("status = ?", *req.Status) - } - - total, err := l.svcCtx.UserFeatureWhitelistModel.FindCount(l.ctx, builder, "id") - if err != nil { - return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询白名单总数失败, %v", err) - } - - list, err := l.svcCtx.UserFeatureWhitelistModel.FindPageListByPage(l.ctx, builder, req.Page, req.PageSize, "create_time DESC") - if err != nil { - return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询白名单列表失败, %v", err) - } - - featureNameMap := l.buildFeatureNameMap(list) - - items := make([]types.AdminWhitelistListItem, 0, len(list)) - for _, w := range list { - items = append(items, types.AdminWhitelistListItem{ - Id: w.Id, - IdCard: w.IdCard, - FeatureId: w.FeatureId, - FeatureApiId: w.FeatureApiId, - FeatureName: featureNameMap[w.FeatureId], - UserId: w.UserId, - Amount: w.Amount, - Status: w.Status, - StatusText: whitelistStatusText(w.Status), - CreateTime: w.CreateTime.Format("2006-01-02 15:04:05"), - UpdateTime: w.UpdateTime.Format("2006-01-02 15:04:05"), - }) - } - - return &types.AdminGetWhitelistListResp{ - Total: total, - Items: items, - }, nil -} - -func (l *AdminGetWhitelistListLogic) buildFeatureNameMap(list []*model.UserFeatureWhitelist) map[string]string { - featureNameMap := make(map[string]string) - if len(list) == 0 { - return featureNameMap - } - - featureIds := make([]string, 0, len(list)) - seen := make(map[string]struct{}, len(list)) - for _, w := range list { - if _, ok := seen[w.FeatureId]; ok { - continue - } - seen[w.FeatureId] = struct{}{} - featureIds = append(featureIds, w.FeatureId) - } - - featureBuilder := l.svcCtx.FeatureModel.SelectBuilder().Where(squirrel.Eq{"id": featureIds}) - features, err := l.svcCtx.FeatureModel.FindAll(l.ctx, featureBuilder, "") - if err != nil { - l.Errorf("批量查询模块名称失败: %v", err) - return featureNameMap - } - for _, f := range features { - featureNameMap[f.Id] = f.Name - } - return featureNameMap -} - -func whitelistStatusText(status int64) string { - if status == 1 { - return "生效" - } - return "已失效" -} diff --git a/app/main/api/internal/logic/admin_whitelist/adminupdatewhitelistlogic.go b/app/main/api/internal/logic/admin_whitelist/adminupdatewhitelistlogic.go deleted file mode 100644 index 05b00b0..0000000 --- a/app/main/api/internal/logic/admin_whitelist/adminupdatewhitelistlogic.go +++ /dev/null @@ -1,57 +0,0 @@ -package admin_whitelist - -import ( - "context" - - "ycc-server/app/main/api/internal/svc" - "ycc-server/app/main/api/internal/types" - "ycc-server/app/main/model" - "ycc-server/common/xerr" - - "github.com/pkg/errors" - "github.com/zeromicro/go-zero/core/logx" -) - -type AdminUpdateWhitelistLogic struct { - logx.Logger - ctx context.Context - svcCtx *svc.ServiceContext -} - -func NewAdminUpdateWhitelistLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminUpdateWhitelistLogic { - return &AdminUpdateWhitelistLogic{ - Logger: logx.WithContext(ctx), - ctx: ctx, - svcCtx: svcCtx, - } -} - -func (l *AdminUpdateWhitelistLogic) AdminUpdateWhitelist(req *types.AdminUpdateWhitelistReq) (resp *types.AdminUpdateWhitelistResp, err error) { - if req.Status == nil && req.Amount == nil { - return nil, errors.Wrapf(xerr.NewErrMsg("请至少提供一个更新字段"), "") - } - - record, err := l.svcCtx.UserFeatureWhitelistModel.FindOne(l.ctx, req.Id) - if err != nil { - if errors.Is(err, model.ErrNotFound) { - return nil, errors.Wrapf(xerr.NewErrMsg("白名单记录不存在"), "") - } - return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询白名单记录失败, %v", err) - } - - if req.Status != nil { - if *req.Status != 1 && *req.Status != 2 { - return nil, errors.Wrapf(xerr.NewErrMsg("状态值无效,仅支持 1=生效 或 2=已失效"), "") - } - record.Status = *req.Status - } - if req.Amount != nil { - record.Amount = *req.Amount - } - - if err = l.svcCtx.UserFeatureWhitelistModel.UpdateWithVersion(l.ctx, nil, record); err != nil { - return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新白名单记录失败, %v", err) - } - - return &types.AdminUpdateWhitelistResp{Success: true}, nil -} diff --git a/app/main/api/internal/logic/admin_whitelist/whitelist_create_helper.go b/app/main/api/internal/logic/admin_whitelist/whitelist_create_helper.go deleted file mode 100644 index 76083ca..0000000 --- a/app/main/api/internal/logic/admin_whitelist/whitelist_create_helper.go +++ /dev/null @@ -1,102 +0,0 @@ -package admin_whitelist - -import ( - "context" - - "ycc-server/app/main/api/internal/svc" - "ycc-server/app/main/model" - "ycc-server/common/xerr" - - "github.com/google/uuid" - "github.com/pkg/errors" -) - -const ( - whitelistActionCreated = "created" - whitelistActionReactivated = "reactivated" - whitelistActionSkipped = "skipped" -) - -type createWhitelistResult struct { - Id string - FeatureApiId string - FeatureName string - Action string - Message string -} - -func createOrReactivateWhitelist( - ctx context.Context, - svcCtx *svc.ServiceContext, - userID string, - idCard string, - feature *model.Feature, - amount float64, - status int64, -) (*createWhitelistResult, error) { - exists, err := svcCtx.WhitelistService.CheckWhitelistExists(ctx, idCard, feature.Id) - if err != nil { - return nil, err - } - if exists { - return &createWhitelistResult{ - FeatureApiId: feature.ApiId, - FeatureName: feature.Name, - Action: whitelistActionSkipped, - Message: "已存在生效白名单", - }, nil - } - - builder := svcCtx.UserFeatureWhitelistModel.SelectBuilder(). - Where("id_card = ? AND feature_id = ?", idCard, feature.Id) - records, err := svcCtx.UserFeatureWhitelistModel.FindAll(ctx, builder, "") - if err != nil { - return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询白名单记录失败, %v", err) - } - for _, record := range records { - if record.Status == 2 { - record.Status = status - record.Amount = amount - record.UserId = userID - if updateErr := svcCtx.UserFeatureWhitelistModel.UpdateWithVersion(ctx, nil, record); updateErr != nil { - return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新白名单记录失败, %v", updateErr) - } - return &createWhitelistResult{ - Id: record.Id, - FeatureApiId: feature.ApiId, - FeatureName: feature.Name, - Action: whitelistActionReactivated, - }, nil - } - } - - wl := &model.UserFeatureWhitelist{ - Id: uuid.NewString(), - IdCard: idCard, - FeatureId: feature.Id, - FeatureApiId: feature.ApiId, - UserId: userID, - Amount: amount, - Status: status, - } - if _, err = svcCtx.UserFeatureWhitelistModel.Insert(ctx, nil, wl); err != nil { - return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "创建白名单记录失败, %v", err) - } - - return &createWhitelistResult{ - Id: wl.Id, - FeatureApiId: feature.ApiId, - FeatureName: feature.Name, - Action: whitelistActionCreated, - }, nil -} - -func parseWhitelistStatus(status *int64) (int64, error) { - if status == nil { - return 1, nil - } - if *status != 1 && *status != 2 { - return 0, errors.Wrapf(xerr.NewErrMsg("状态值无效,仅支持 1=生效 或 2=已失效"), "") - } - return *status, nil -} diff --git a/app/main/api/internal/logic/agent/offlinefeaturelogic.go b/app/main/api/internal/logic/agent/offlinefeaturelogic.go index 5ab6032..d190767 100644 --- a/app/main/api/internal/logic/agent/offlinefeaturelogic.go +++ b/app/main/api/internal/logic/agent/offlinefeaturelogic.go @@ -4,6 +4,8 @@ import ( "context" "encoding/hex" "encoding/json" + "fmt" + "strings" "ycc-server/app/main/model" "ycc-server/common/ctxdata" "ycc-server/common/xerr" @@ -33,28 +35,32 @@ func NewOfflineFeatureLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Of } func (l *OfflineFeatureLogic) OfflineFeature(req *types.OfflineFeatureReq) (resp *types.OfflineFeatureResp, err error) { - // 1. 验证参数 if err := l.validateRequest(req); err != nil { return nil, err } - // 2. 获取用户ID并验证代理权限(任意等级代理均可下架) userID, err := l.verifyAgent() if err != nil { return nil, err } - // 3. 获取查询记录和身份证号 - queryModel, idCard, err := l.getQueryInfo(req.QueryId) + queryModel, idCard, name, err := l.getQueryInfo(req.QueryId) if err != nil { return nil, err } - _ = queryModel // 避免未使用变量警告 - // 4. 调用 WhitelistService 统一下架处理 + mainApiId := extractMainApiId(req.FeatureApiId) + feature, err := l.svcCtx.FeatureModel.FindOneByApiId(l.ctx, mainApiId) + if err != nil { + if errors.Is(err, model.ErrNotFound) { + return nil, errors.Wrapf(xerr.NewErrMsg("模块不存在"), "") + } + return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询模块信息失败, %v", err) + } + needPay, amount, whitelistCreated, err := l.svcCtx.WhitelistService.ProcessOfflineFeature( l.ctx, - nil, // 不使用事务,因为可能只是检查 + nil, idCard, req.FeatureApiId, userID, @@ -64,14 +70,31 @@ func (l *OfflineFeatureLogic) OfflineFeature(req *types.OfflineFeatureReq) (resp return nil, err } - // 5. 如果已创建白名单,需要删除报告数据 + if needPay { + return &types.OfflineFeatureResp{ + Success: false, + NeedPay: true, + Amount: amount, + }, nil + } + if whitelistCreated { + remark := fmt.Sprintf("代理下架自动同步 query_id=%s feature=%s", req.QueryId, feature.ApiId) + l.svcCtx.QueryWhitelistSyncService.TrySync( + l.ctx, + name, + idCard, + []string{feature.ApiId}, + remark, + ) + if err := l.deleteQueryData(req.QueryId, req.FeatureApiId); err != nil { - // 删除报告数据失败不影响主流程,只记录日志 logx.Errorf("下架模块后删除报告数据失败:查询记录 %s,模块 %s,错误:%v", req.QueryId, req.FeatureApiId, err) } } + _ = queryModel + return &types.OfflineFeatureResp{ Success: whitelistCreated, NeedPay: needPay, @@ -79,7 +102,6 @@ func (l *OfflineFeatureLogic) OfflineFeature(req *types.OfflineFeatureReq) (resp }, nil } -// validateRequest 验证请求参数 func (l *OfflineFeatureLogic) validateRequest(req *types.OfflineFeatureReq) error { if req.QueryId == "" { return errors.Wrapf(xerr.NewErrMsg("查询记录ID不能为空"), "") @@ -90,7 +112,6 @@ func (l *OfflineFeatureLogic) validateRequest(req *types.OfflineFeatureReq) erro return nil } -// verifyAgent 验证是否为代理并返回用户ID(任意等级代理均可下架) func (l *OfflineFeatureLogic) verifyAgent() (string, error) { userID, err := ctxdata.GetUidFromCtx(l.ctx) if err != nil { @@ -108,61 +129,67 @@ func (l *OfflineFeatureLogic) verifyAgent() (string, error) { return userID, nil } -// getQueryInfo 获取查询记录和身份证号 -func (l *OfflineFeatureLogic) getQueryInfo(queryId string) (*model.Query, string, error) { +func (l *OfflineFeatureLogic) getQueryInfo(queryId string) (*model.Query, string, string, error) { queryModel, err := l.svcCtx.QueryModel.FindOne(l.ctx, queryId) if err != nil { if errors.Is(err, model.ErrNotFound) { - return nil, "", errors.Wrapf(xerr.NewErrMsg("查询记录不存在"), "") + return nil, "", "", errors.Wrapf(xerr.NewErrMsg("查询记录不存在"), "") } - return nil, "", errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询报告记录失败, %v", err) + return nil, "", "", errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询报告记录失败, %v", err) } - // 解密 QueryParams 获取 idCard - idCard, err := l.extractIdCardFromQueryParams(queryModel) + idCard, name, err := extractPersonInfoFromQueryParams(l.svcCtx, queryModel) if err != nil { - return nil, "", errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取身份证号失败, %v", err) + return nil, "", "", errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取查询参数失败, %v", err) } - if idCard == "" { - return nil, "", errors.Wrapf(xerr.NewErrMsg("查询参数中缺少身份证号"), "") + return nil, "", "", errors.Wrapf(xerr.NewErrMsg("查询参数中缺少身份证号"), "") } - return queryModel, idCard, nil + return queryModel, idCard, name, nil } -// extractIdCardFromQueryParams 从 QueryParams 中提取身份证号 -func (l *OfflineFeatureLogic) extractIdCardFromQueryParams(queryModel *model.Query) (string, error) { +func extractMainApiId(featureApiId string) string { + if idx := strings.Index(featureApiId, "_"); idx > 0 { + return featureApiId[:idx] + } + return featureApiId +} + +func extractPersonInfoFromQueryParams(svcCtx *svc.ServiceContext, queryModel *model.Query) (idCard, name string, err error) { if queryModel.QueryParams == "" { - return "", errors.New("查询参数为空") + return "", "", errors.New("查询参数为空") } - secretKey := l.svcCtx.Config.Encrypt.SecretKey + secretKey := svcCtx.Config.Encrypt.SecretKey key, decodeErr := hex.DecodeString(secretKey) if decodeErr != nil { - return "", errors.Wrap(decodeErr, "获取AES密钥失败") + return "", "", errors.Wrap(decodeErr, "获取AES密钥失败") } decryptedParams, decryptErr := crypto.AesDecrypt(queryModel.QueryParams, key) if decryptErr != nil { - return "", errors.Wrap(decryptErr, "解密查询参数失败") + return "", "", errors.Wrap(decryptErr, "解密查询参数失败") } var params map[string]interface{} - unmarshalErr := json.Unmarshal(decryptedParams, ¶ms) - if unmarshalErr != nil { - return "", errors.Wrap(unmarshalErr, "解析查询参数失败") + if unmarshalErr := json.Unmarshal(decryptedParams, ¶ms); unmarshalErr != nil { + return "", "", errors.Wrap(unmarshalErr, "解析查询参数失败") } - idCard, ok := params["id_card"].(string) - if !ok || idCard == "" { - return "", errors.New("查询参数中缺少 id_card 或 id_card 为空") + idCardValue, ok := params["id_card"].(string) + if !ok || idCardValue == "" { + return "", "", errors.New("查询参数中缺少 id_card 或 id_card 为空") } - return idCard, nil + nameValue, _ := params["name"].(string) + if strings.TrimSpace(nameValue) == "" { + nameValue = "*" + } + + return idCardValue, nameValue, nil } -// deleteQueryData 删除报告数据 func (l *OfflineFeatureLogic) deleteQueryData(queryId, featureApiId string) error { return l.svcCtx.QueryModel.Trans(l.ctx, func(ctx context.Context, session sqlx.Session) error { return l.svcCtx.WhitelistService.DeleteFeatureFromQueryData(ctx, session, queryId, featureApiId) diff --git a/app/main/api/internal/logic/pay/alipaycallbacklogic.go b/app/main/api/internal/logic/pay/alipaycallbacklogic.go index d331be9..f99d853 100644 --- a/app/main/api/internal/logic/pay/alipaycallbacklogic.go +++ b/app/main/api/internal/logic/pay/alipaycallbacklogic.go @@ -172,6 +172,15 @@ func (l *AlipayCallbackLogic) handleWhitelistOrderPayment(w http.ResponseWriter, return nil } + if whitelistOrder.Status == 2 { + syncPaidWhitelistOrderToTianyuan( + l.ctx, + l.svcCtx, + whitelistOrder, + buildAgentPaidWhitelistSyncRemark(orderNo), + ) + } + // 6. 更新订单和白名单订单 + 创建白名单记录并删除报告数据 err = l.svcCtx.WhitelistOrderModel.Trans(l.ctx, func(ctx context.Context, session sqlx.Session) error { // 6.1 更新订单状态 diff --git a/app/main/api/internal/logic/pay/paymentlogic.go b/app/main/api/internal/logic/pay/paymentlogic.go index 90a33f2..a6be012 100644 --- a/app/main/api/internal/logic/pay/paymentlogic.go +++ b/app/main/api/internal/logic/pay/paymentlogic.go @@ -217,6 +217,13 @@ func (l *PaymentLogic) Payment(req *types.PaymentReq) (resp *types.PaymentResp, } // 更新白名单订单状态为已支付并处理白名单记录 + syncPaidWhitelistOrderToTianyuan( + context.Background(), + l.svcCtx, + whitelistOrder, + buildAgentPaidWhitelistSyncRemark(paymentTypeResp.outTradeNo), + ) + err := l.svcCtx.WhitelistOrderModel.Trans(context.Background(), func(transCtx context.Context, session sqlx.Session) error { // 更新白名单订单状态为已支付 whitelistOrder.Status = 2 // 已支付 diff --git a/app/main/api/internal/logic/pay/wechatpaycallbacklogic.go b/app/main/api/internal/logic/pay/wechatpaycallbacklogic.go index f29dd74..017b418 100644 --- a/app/main/api/internal/logic/pay/wechatpaycallbacklogic.go +++ b/app/main/api/internal/logic/pay/wechatpaycallbacklogic.go @@ -166,6 +166,15 @@ func (l *WechatPayCallbackLogic) handleWhitelistOrderPayment(w http.ResponseWrit return nil } + if whitelistOrder.Status == 2 { + syncPaidWhitelistOrderToTianyuan( + l.ctx, + l.svcCtx, + whitelistOrder, + buildAgentPaidWhitelistSyncRemark(orderNo), + ) + } + // 6. 更新订单和白名单订单 + 创建白名单记录并删除报告数据 err := l.svcCtx.WhitelistOrderModel.Trans(l.ctx, func(ctx context.Context, session sqlx.Session) error { // 6.1 更新订单状态 diff --git a/app/main/api/internal/logic/pay/whitelist_sync_helper.go b/app/main/api/internal/logic/pay/whitelist_sync_helper.go new file mode 100644 index 0000000..402f30e --- /dev/null +++ b/app/main/api/internal/logic/pay/whitelist_sync_helper.go @@ -0,0 +1,28 @@ +package pay + +import ( + "context" + "fmt" + + "ycc-server/app/main/api/internal/svc" + "ycc-server/app/main/model" +) + +func syncPaidWhitelistOrderToTianyuan( + ctx context.Context, + svcCtx *svc.ServiceContext, + whitelistOrder *model.WhitelistOrder, + remark string, +) { + itemBuilder := svcCtx.WhitelistOrderItemModel.SelectBuilder(). + Where("order_id = ?", whitelistOrder.Id) + items, err := svcCtx.WhitelistOrderItemModel.FindAll(ctx, itemBuilder, "") + if err != nil || len(items) == 0 { + return + } + svcCtx.QueryWhitelistSyncService.TrySyncWhitelistOrder(ctx, whitelistOrder, items, remark) +} + +func buildAgentPaidWhitelistSyncRemark(orderNo string) string { + return fmt.Sprintf("代理付费下架自动同步 order_no=%s", orderNo) +} diff --git a/app/main/api/internal/service/querywhitelistsync.go b/app/main/api/internal/service/querywhitelistsync.go new file mode 100644 index 0000000..3454340 --- /dev/null +++ b/app/main/api/internal/service/querywhitelistsync.go @@ -0,0 +1,303 @@ +package service + +import ( + "context" + "database/sql" + "encoding/json" + "regexp" + "strings" + + "ycc-server/app/main/api/internal/config" + tianyuanapi "ycc-server/app/main/api/internal/service/tianyuanapi_sdk" + "ycc-server/app/main/model" + "ycc-server/common/ctxdata" + "ycc-server/common/xerr" + + "github.com/pkg/errors" + "github.com/zeromicro/go-zero/core/logx" +) + +var queryWhitelistIdCardPattern = regexp.MustCompile(`^\d{17}[\dXx]$`) + +const ( + tianyuanQueryWhitelistSuccessCode = 0 + tianyuanQueryWhitelistExistsCode = 1013 + tianyuanQueryWhitelistNotFoundCode = 1014 + tianyuanQueryWhitelistLocalFailCode = -1 +) + +// QueryWhitelistSyncService 天远查询白名单同步服务 +type QueryWhitelistSyncService struct { + config config.Config + client *tianyuanapi.Client + opLogModel model.QueryWhitelistOpLogModel +} + +func NewQueryWhitelistSyncService( + c config.Config, + client *tianyuanapi.Client, + opLogModel model.QueryWhitelistOpLogModel, +) *QueryWhitelistSyncService { + return &QueryWhitelistSyncService{ + config: c, + client: client, + opLogModel: opLogModel, + } +} + +// TrySync 尽力同步天远查询白名单,失败仅记操作日志,不影响主流程 +func (s *QueryWhitelistSyncService) TrySync( + ctx context.Context, + name, idCard string, + apiCodes []string, + remark string, +) { + if err := s.Sync(ctx, name, idCard, apiCodes, remark); err != nil { + logx.WithContext(ctx).Errorf("天远查询白名单同步失败(不影响主流程): %v", err) + } +} + +// TrySyncWhitelistOrder 尽力同步白名单订单中的模块到天远 +func (s *QueryWhitelistSyncService) TrySyncWhitelistOrder( + ctx context.Context, + whitelistOrder *model.WhitelistOrder, + items []*model.WhitelistOrderItem, + remark string, +) { + if whitelistOrder == nil || len(items) == 0 { + return + } + s.TrySync(ctx, "*", whitelistOrder.IdCard, collectWhitelistOrderApiCodes(items), remark) +} + +// Sync 将产品编码同步到天远查询白名单(先追加,规则不存在则创建) +func (s *QueryWhitelistSyncService) Sync( + ctx context.Context, + name, idCard string, + apiCodes []string, + remark string, +) error { + if err := validateQueryWhitelistSyncReq(idCard, apiCodes); err != nil { + s.recordOpLogFailure(ctx, "sync", name, idCard, apiCodes, remark, err.Error(), tianyuanQueryWhitelistLocalFailCode) + return err + } + + appendResp, err := s.callMgmt( + "/api/v1/query-whitelist/entries/append", + name, + idCard, + apiCodes, + remark, + ) + if err != nil { + s.recordOpLogFailure(ctx, "append", name, idCard, apiCodes, remark, err.Error(), tianyuanQueryWhitelistLocalFailCode) + return err + } + if isTianyuanQueryWhitelistSuccess(appendResp.Code) { + s.recordOpLog(ctx, "append", name, idCard, apiCodes, remark, appendResp) + return nil + } + if appendResp.Code != tianyuanQueryWhitelistNotFoundCode { + s.recordOpLog(ctx, "append", name, idCard, apiCodes, remark, appendResp) + return errors.Wrapf(xerr.NewErrMsg(appendResp.Message), "天远查询白名单同步失败(code=%d)", appendResp.Code) + } + + createResp, err := s.callMgmt( + "/api/v1/query-whitelist/entries", + name, + idCard, + apiCodes, + remark, + ) + if err != nil { + s.recordOpLogFailure(ctx, "create", name, idCard, apiCodes, remark, err.Error(), tianyuanQueryWhitelistLocalFailCode) + return err + } + if isTianyuanQueryWhitelistSuccess(createResp.Code) { + s.recordOpLog(ctx, "create", name, idCard, apiCodes, remark, createResp) + return nil + } + if createResp.Code == tianyuanQueryWhitelistExistsCode { + retryResp, retryErr := s.callMgmt( + "/api/v1/query-whitelist/entries/append", + name, + idCard, + apiCodes, + remark, + ) + if retryErr != nil { + s.recordOpLogFailure(ctx, "append", name, idCard, apiCodes, remark, retryErr.Error(), tianyuanQueryWhitelistLocalFailCode) + return retryErr + } + s.recordOpLog(ctx, "append", name, idCard, apiCodes, remark, retryResp) + if isTianyuanQueryWhitelistSuccess(retryResp.Code) { + return nil + } + return errors.Wrapf(xerr.NewErrMsg(retryResp.Message), "天远查询白名单同步失败(code=%d)", retryResp.Code) + } + + s.recordOpLog(ctx, "create", name, idCard, apiCodes, remark, createResp) + return errors.Wrapf(xerr.NewErrMsg(createResp.Message), "天远查询白名单同步失败(code=%d)", createResp.Code) +} + +func collectWhitelistOrderApiCodes(items []*model.WhitelistOrderItem) []string { + codes := make([]string, 0, len(items)) + seen := make(map[string]bool, len(items)) + for _, item := range items { + if item == nil || item.FeatureApiId == "" || seen[item.FeatureApiId] { + continue + } + seen[item.FeatureApiId] = true + codes = append(codes, item.FeatureApiId) + } + return codes +} + +func validateQueryWhitelistSyncReq(idCard string, apiCodes []string) error { + if strings.TrimSpace(idCard) == "" { + return errors.Wrapf(xerr.NewErrMsg("身份证号不能为空"), "") + } + if !queryWhitelistIdCardPattern.MatchString(strings.TrimSpace(idCard)) { + return errors.Wrapf(xerr.NewErrMsg("身份证号格式不正确,需为18位中国大陆身份证号"), "") + } + if len(apiCodes) == 0 { + return errors.Wrapf(xerr.NewErrMsg("请至少选择一个产品编码"), "") + } + for _, code := range apiCodes { + if strings.TrimSpace(code) == "" { + return errors.Wrapf(xerr.NewErrMsg("产品编码不能为空"), "") + } + if code == "*" { + return errors.Wrapf(xerr.NewErrMsg("产品编码不支持通配符 *"), "") + } + } + return nil +} + +func (s *QueryWhitelistSyncService) callMgmt( + apiPath string, + name, idCard string, + apiCodes []string, + remark string, +) (*tianyuanapi.QueryWhitelistMgmtResponse, error) { + mgmtKey := s.config.Tianyuanapi.WhitelistMgmtKey + if mgmtKey == "" { + return nil, errors.Wrapf(xerr.NewErrMsg("查询白名单管理密钥未配置,请在服务端配置 Tianyuanapi.WhitelistMgmtKey"), "") + } + if s.client == nil { + return nil, errors.Wrapf(xerr.NewErrMsg("天远 API 客户端未初始化"), "") + } + + if strings.TrimSpace(name) == "" { + name = "*" + } + + payload := map[string]interface{}{ + "name": name, + "id_card": strings.TrimSpace(idCard), + "api_codes": apiCodes, + } + if strings.TrimSpace(remark) != "" { + payload["remark"] = strings.TrimSpace(remark) + } + + return s.client.CallQueryWhitelistMgmt(apiPath, payload, mgmtKey) +} + +func (s *QueryWhitelistSyncService) recordOpLogFailure( + ctx context.Context, + action, name, idCard string, + apiCodes []string, + remark, message string, + code int64, +) { + s.insertOpLog(ctx, action, name, idCard, apiCodes, remark, code, message, "", nil) +} + +func (s *QueryWhitelistSyncService) recordOpLog( + ctx context.Context, + action, name, idCard string, + apiCodes []string, + remark string, + apiResp *tianyuanapi.QueryWhitelistMgmtResponse, +) { + if apiResp == nil { + return + } + s.insertOpLog(ctx, action, name, idCard, apiCodes, remark, int64(apiResp.Code), apiResp.Message, apiResp.TransactionID, apiResp.Entry) +} + +func (s *QueryWhitelistSyncService) insertOpLog( + ctx context.Context, + action, name, idCard string, + apiCodes []string, + remark string, + tianyuanCode int64, + tianyuanMessage, transactionID string, + entry *tianyuanapi.QueryWhitelistEntry, +) { + if s.opLogModel == nil { + return + } + + operatorUserId, err := ctxdata.GetUidFromCtx(ctx) + if err != nil { + logx.WithContext(ctx).Errorf("记录查询白名单操作日志失败: 获取操作人ID失败, %v", err) + operatorUserId = "" + } + + apiCodesJSON, marshalErr := json.Marshal(apiCodes) + if marshalErr != nil { + logx.WithContext(ctx).Errorf("记录查询白名单操作日志失败: 序列化 api_codes 失败, %v", marshalErr) + return + } + + log := &model.QueryWhitelistOpLog{ + AdminUserId: operatorUserId, + Action: action, + Name: resolveQueryWhitelistName(name), + IdCard: strings.TrimSpace(idCard), + ApiCodes: string(apiCodesJSON), + TianyuanCode: tianyuanCode, + } + + if strings.TrimSpace(remark) != "" { + log.Remark = sql.NullString{String: strings.TrimSpace(remark), Valid: true} + } + if tianyuanMessage != "" { + log.TianyuanMessage = sql.NullString{String: tianyuanMessage, Valid: true} + } + if transactionID != "" { + log.TransactionId = sql.NullString{String: transactionID, Valid: true} + } + if entry != nil { + if entry.IdCardMasked != "" { + log.IdCardMasked = sql.NullString{String: entry.IdCardMasked, Valid: true} + } + if entry.ID != "" { + log.EntryId = sql.NullString{String: entry.ID, Valid: true} + } + if entry.Status != "" { + log.EntryStatus = sql.NullString{String: entry.Status, Valid: true} + } + if len(entry.ApiCodes) > 0 { + entryCodesJSON, _ := json.Marshal(entry.ApiCodes) + log.EntryApiCodes = sql.NullString{String: string(entryCodesJSON), Valid: true} + } + } + + if _, insertErr := s.opLogModel.Insert(ctx, nil, log); insertErr != nil { + logx.WithContext(ctx).Errorf("记录查询白名单操作日志失败: %v", insertErr) + } +} + +func resolveQueryWhitelistName(name string) string { + if strings.TrimSpace(name) == "" { + return "*" + } + return strings.TrimSpace(name) +} + +func isTianyuanQueryWhitelistSuccess(code int) bool { + return code == tianyuanQueryWhitelistSuccessCode +} diff --git a/app/main/api/internal/svc/servicecontext.go b/app/main/api/internal/svc/servicecontext.go index 79afee3..f21f95a 100644 --- a/app/main/api/internal/svc/servicecontext.go +++ b/app/main/api/internal/svc/servicecontext.go @@ -100,7 +100,8 @@ type ServiceContext struct { WechatPayService *service.WechatPayService ApplePayService *service.ApplePayService ApiRequestService *service.ApiRequestService - WhitelistService *service.WhitelistService + WhitelistService *service.WhitelistService + QueryWhitelistSyncService *service.QueryWhitelistSyncService AsynqServer *asynq.Server AsynqService *service.AsynqService VerificationService *service.VerificationService @@ -315,12 +316,13 @@ func NewServiceContext(c config.Config) *ServiceContext { ComplaintAlipayTradeModel: complaintAlipayTradeModel, ComplaintManualModel: complaintManualModel, - // 服务 - AlipayService: alipayService, - WechatPayService: wechatPayService, - ApplePayService: applePayService, - ApiRequestService: apiRequestService, - WhitelistService: whitelistService, + // 服务 + AlipayService: alipayService, + WechatPayService: wechatPayService, + ApplePayService: applePayService, + ApiRequestService: apiRequestService, + WhitelistService: whitelistService, + QueryWhitelistSyncService: service.NewQueryWhitelistSyncService(c, tianyuanapi, queryWhitelistOpLogModel), AsynqServer: asynqServer, AsynqService: asynqService, VerificationService: verificationService, diff --git a/app/main/api/internal/types/types.go b/app/main/api/internal/types/types.go index 2c7756a..7e68e69 100644 --- a/app/main/api/internal/types/types.go +++ b/app/main/api/internal/types/types.go @@ -58,28 +58,6 @@ type AdminAuditWithdrawalResp struct { Success bool `json:"success"` } -type AdminBatchCreateWhitelistReq struct { - IdCard string `json:"id_card"` - FeatureIds []string `json:"feature_ids"` - Amount *float64 `json:"amount,optional"` - Status *int64 `json:"status,optional"` -} - -type AdminBatchCreateWhitelistResp struct { - SuccessCount int64 `json:"success_count"` - SkipCount int64 `json:"skip_count"` - FailCount int64 `json:"fail_count"` - Items []AdminBatchCreateWhitelistResultItem `json:"items"` -} - -type AdminBatchCreateWhitelistResultItem struct { - FeatureId string `json:"feature_id"` - FeatureApiId string `json:"feature_api_id"` - FeatureName string `json:"feature_name"` - Action string `json:"action"` - Message string `json:"message,optional"` -} - type AdminBatchUpdateApiStatusReq struct { Ids []string `json:"ids"` Status int64 `json:"status"` @@ -186,17 +164,6 @@ type AdminCreateUserResp struct { Id string `json:"id"` // 用户ID } -type AdminCreateWhitelistReq struct { - IdCard string `json:"id_card"` - FeatureId string `json:"feature_id"` - Amount *float64 `json:"amount,optional"` - Status *int64 `json:"status,optional"` -} - -type AdminCreateWhitelistResp struct { - Id string `json:"id"` -} - type AdminAppendQueryWhitelistReq struct { Name string `json:"name,optional"` IdCard string `json:"id_card"` @@ -335,14 +302,6 @@ type AdminDeleteUserResp struct { Success bool `json:"success"` // 是否成功 } -type AdminDeleteWhitelistReq struct { - Id string `path:"id"` -} - -type AdminDeleteWhitelistResp struct { - Success bool `json:"success"` -} - type AdminDowngradeAgentReq struct { AgentId string `json:"agent_id"` // 代理ID ToLevel int64 `json:"to_level"` // 目标等级:1=普通,2=黄金,3=钻石(须低于当前等级) @@ -867,19 +826,6 @@ type AdminGetUserListResp struct { Items []AdminUserListItem `json:"items"` // 列表 } -type AdminGetWhitelistListReq struct { - Page int64 `form:"page"` - PageSize int64 `form:"pageSize"` - IdCard *string `form:"id_card,optional"` - FeatureApiId *string `form:"feature_api_id,optional"` - Status *int64 `form:"status,optional"` -} - -type AdminGetWhitelistListResp struct { - Total int64 `json:"total"` - Items []AdminWhitelistListItem `json:"items"` -} - type AdminLoginReq struct { Username string `json:"username" validate:"required"` Password string `json:"password" validate:"required"` @@ -1177,16 +1123,6 @@ type AdminUpdateUserResp struct { Success bool `json:"success"` // 是否成功 } -type AdminUpdateWhitelistReq struct { - Id string `path:"id"` - Status *int64 `json:"status,optional"` - Amount *float64 `json:"amount,optional"` -} - -type AdminUpdateWhitelistResp struct { - Success bool `json:"success"` -} - type AdminUserInfoReq struct { } @@ -1205,20 +1141,6 @@ type AdminUserListItem struct { RoleIds []string `json:"role_ids"` // 关联的角色ID列表 } -type AdminWhitelistListItem struct { - Id string `json:"id"` - IdCard string `json:"id_card"` - FeatureId string `json:"feature_id"` - FeatureApiId string `json:"feature_api_id"` - FeatureName string `json:"feature_name"` - UserId string `json:"user_id"` - Amount float64 `json:"amount"` - Status int64 `json:"status"` - StatusText string `json:"status_text"` - CreateTime string `json:"create_time"` - UpdateTime string `json:"update_time"` -} - type AgentApplyReq struct { Region string `json:"region,optional"` Mobile string `json:"mobile"`