From bebabce34659cea6a94ade59ba921975c5db2579 Mon Sep 17 00:00:00 2001 From: liangzai <2440983361@qq.com> Date: Mon, 2 Jun 2025 18:21:08 +0800 Subject: [PATCH] add query url share --- app/user/cmd/api/desc/query.api | 29 +++- app/user/cmd/api/etc/main.dev.yaml | 2 + app/user/cmd/api/etc/main.yaml | 2 + app/user/cmd/api/internal/config/config.go | 4 + .../query/querygeneratesharelinkhandler.go | 29 ++++ ...lhandler.go => querysharedetailhandler.go} | 11 +- app/user/cmd/api/internal/handler/routes.go | 18 +- .../internal/logic/query/querydetaillogic.go | 70 -------- .../query/querygeneratesharelinklogic.go | 111 ++++++++++++ .../logic/query/querysharedetaillogic.go | 164 ++++++++++++++++++ .../cmd/api/internal/types/encrypPayload.go | 6 + app/user/cmd/api/internal/types/types.go | 18 ++ common/jwt/jwtx_test.go | 2 +- 13 files changed, 380 insertions(+), 86 deletions(-) create mode 100644 app/user/cmd/api/internal/handler/query/querygeneratesharelinkhandler.go rename app/user/cmd/api/internal/handler/query/{querydetailhandler.go => querysharedetailhandler.go} (73%) delete mode 100644 app/user/cmd/api/internal/logic/query/querydetaillogic.go create mode 100644 app/user/cmd/api/internal/logic/query/querygeneratesharelinklogic.go create mode 100644 app/user/cmd/api/internal/logic/query/querysharedetaillogic.go create mode 100644 app/user/cmd/api/internal/types/encrypPayload.go diff --git a/app/user/cmd/api/desc/query.api b/app/user/cmd/api/desc/query.api index b401c93..62b4f27 100644 --- a/app/user/cmd/api/desc/query.api +++ b/app/user/cmd/api/desc/query.api @@ -116,8 +116,23 @@ service main { @doc "确认查询状态" @handler confirmQueryState post /query/confirm_state (ConfirmQueryStateReq) returns (ConfirmQueryStateResp) + + @doc "生成分享链接" + @handler QueryGenerateShareLink + post /query/generate_share_link (QueryGenerateShareLinkReq) returns (QueryGenerateShareLinkResp) } +type ( + QueryGenerateShareLinkReq { + OrderId *int64 `json:"order_id,optional"` + OrderNo *string `json:"order_no,optional"` + } + QueryGenerateShareLinkResp { + ShareLink string `json:"share_link"` + } +) + + // 获取查询临时订单 type ( QueryProvisionalOrderReq { @@ -208,15 +223,23 @@ service main { post /query/single/test (QuerySingleTestReq) returns (QuerySingleTestResp) @doc "查询详情" - @handler queryDetail - get /query/:id (QueryDetailReq) returns (QueryDetailResp) + @handler queryShareDetail + get /query/share/:id (QueryShareDetailReq) returns (QueryShareDetailResp) @doc "查询示例" @handler queryExample get /query/example (QueryExampleReq) returns (QueryExampleResp) } - +type ( + QueryShareDetailReq { + Id string `path:"id"` + } + QueryShareDetailResp { + Status string `json:"status"` + Query + } +) type QuerySingleTestReq { Params map[string]interface{} `json:"params"` Api string `json:"api"` diff --git a/app/user/cmd/api/etc/main.dev.yaml b/app/user/cmd/api/etc/main.dev.yaml index 76a67d1..a74f4e6 100644 --- a/app/user/cmd/api/etc/main.dev.yaml +++ b/app/user/cmd/api/etc/main.dev.yaml @@ -70,3 +70,5 @@ CloudAuth: Endpoint: "cloudauth.aliyuncs.com" SceneId: 1000013341 ReturnUrl: "https://www.quannengcha.com/authorization/result" +Query: + ShareLinkExpire: 604800 # 7天 = 7 * 24 * 60 * 60 = 604800秒 diff --git a/app/user/cmd/api/etc/main.yaml b/app/user/cmd/api/etc/main.yaml index 8cc94b5..f8094d6 100644 --- a/app/user/cmd/api/etc/main.yaml +++ b/app/user/cmd/api/etc/main.yaml @@ -71,3 +71,5 @@ CloudAuth: Endpoint: "cloudauth.aliyuncs.com" SceneId: 1000013341 ReturnUrl: "https://www.quannengcha.com/authorization/result" +Query: + ShareLinkExpire: 604800 # 7天 = 7 * 24 * 60 * 60 = 604800秒 diff --git a/app/user/cmd/api/internal/config/config.go b/app/user/cmd/api/internal/config/config.go index ec7b9f0..b7f79ee 100644 --- a/app/user/cmd/api/internal/config/config.go +++ b/app/user/cmd/api/internal/config/config.go @@ -21,6 +21,7 @@ type Config struct { SystemConfig SystemConfig WechatH5 WechatH5Config CloudAuth CloudAuthConfig + Query QueryConfig } // JwtAuth 用于 JWT 鉴权配置 @@ -100,3 +101,6 @@ type CloudAuthConfig struct { SceneId int64 ReturnUrl string } +type QueryConfig struct { + ShareLinkExpire int64 +} diff --git a/app/user/cmd/api/internal/handler/query/querygeneratesharelinkhandler.go b/app/user/cmd/api/internal/handler/query/querygeneratesharelinkhandler.go new file mode 100644 index 0000000..54d060b --- /dev/null +++ b/app/user/cmd/api/internal/handler/query/querygeneratesharelinkhandler.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 QueryGenerateShareLinkHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var req types.QueryGenerateShareLinkReq + 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.NewQueryGenerateShareLinkLogic(r.Context(), svcCtx) + resp, err := l.QueryGenerateShareLink(&req) + result.HttpResult(r, w, resp, err) + } +} diff --git a/app/user/cmd/api/internal/handler/query/querydetailhandler.go b/app/user/cmd/api/internal/handler/query/querysharedetailhandler.go similarity index 73% rename from app/user/cmd/api/internal/handler/query/querydetailhandler.go rename to app/user/cmd/api/internal/handler/query/querysharedetailhandler.go index 7539dce..0d61ad9 100644 --- a/app/user/cmd/api/internal/handler/query/querydetailhandler.go +++ b/app/user/cmd/api/internal/handler/query/querysharedetailhandler.go @@ -3,18 +3,17 @@ 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" - - "github.com/zeromicro/go-zero/rest/httpx" ) -func QueryDetailHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { +func QueryShareDetailHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - var req types.QueryDetailReq + var req types.QueryShareDetailReq if err := httpx.Parse(r, &req); err != nil { result.ParamErrorResult(r, w, err) return @@ -23,8 +22,8 @@ func QueryDetailHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { result.ParamValidateErrorResult(r, w, err) return } - l := query.NewQueryDetailLogic(r.Context(), svcCtx) - resp, err := l.QueryDetail(&req) + l := query.NewQueryShareDetailLogic(r.Context(), svcCtx) + resp, err := l.QueryShareDetail(&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 b44c940..ba5da01 100644 --- a/app/user/cmd/api/internal/handler/routes.go +++ b/app/user/cmd/api/internal/handler/routes.go @@ -309,6 +309,12 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) { Path: "/query/confirm_state", Handler: query.ConfirmQueryStateHandler(serverCtx), }, + { + // 生成分享链接 + Method: http.MethodPost, + Path: "/query/generate_share_link", + Handler: query.QueryGenerateShareLinkHandler(serverCtx), + }, { // 查询列表 Method: http.MethodGet, @@ -352,18 +358,18 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) { server.AddRoutes( []rest.Route{ - { - // 查询详情 - Method: http.MethodGet, - Path: "/query/:id", - Handler: query.QueryDetailHandler(serverCtx), - }, { // 查询示例 Method: http.MethodGet, Path: "/query/example", Handler: query.QueryExampleHandler(serverCtx), }, + { + // 查询详情 + Method: http.MethodGet, + Path: "/query/share/:id", + Handler: query.QueryShareDetailHandler(serverCtx), + }, { Method: http.MethodPost, Path: "/query/single/test", diff --git a/app/user/cmd/api/internal/logic/query/querydetaillogic.go b/app/user/cmd/api/internal/logic/query/querydetaillogic.go deleted file mode 100644 index 53b25e9..0000000 --- a/app/user/cmd/api/internal/logic/query/querydetaillogic.go +++ /dev/null @@ -1,70 +0,0 @@ -package query - -import ( - "context" - "encoding/hex" - "encoding/json" - "qnc-server/common/xerr" - "qnc-server/pkg/lzkit/crypto" - "qnc-server/pkg/lzkit/lzUtils" - - "github.com/jinzhu/copier" - "github.com/pkg/errors" - - "qnc-server/app/user/cmd/api/internal/svc" - "qnc-server/app/user/cmd/api/internal/types" - - "github.com/zeromicro/go-zero/core/logx" -) - -type QueryDetailLogic struct { - logx.Logger - ctx context.Context - svcCtx *svc.ServiceContext -} - -func NewQueryDetailLogic(ctx context.Context, svcCtx *svc.ServiceContext) *QueryDetailLogic { - return &QueryDetailLogic{ - Logger: logx.WithContext(ctx), - ctx: ctx, - svcCtx: svcCtx, - } -} - -func (l *QueryDetailLogic) QueryDetail(req *types.QueryDetailReq) (resp *types.QueryDetailResp, err error) { - queryModel, err := l.svcCtx.QueryModel.FindOne(l.ctx, req.Id) - if err != nil { - return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "报告查询, 查找报告错误: %v", err) - } - - 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") - 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", err) - } - if lzUtils.NullStringToString(queryModel.QueryData) != "" { - queryData, decryptErr := crypto.AesDecrypt(lzUtils.NullStringToString(queryModel.QueryData), key) - if decryptErr != nil { - return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "报告查询, 报告结果解密失败, %+v", decryptErr) - } - unmarshalErr := json.Unmarshal(queryData, &query.QueryData) - if unmarshalErr != nil { - return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "报告查询, 报告结构体处理失败, %+v", unmarshalErr) - } - } - err = copier.Copy(&query, queryModel) - if err != nil { - return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "报告查询, 报告结构体复制失败, %v", err) - } - product, err := l.svcCtx.ProductModel.FindOne(l.ctx, queryModel.ProductId) - if err != nil { - return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "报告查询, 获取商品信息失败, %v", err) - } - query.ProductName = product.ProductName - return &types.QueryDetailResp{ - Query: query, - }, nil -} diff --git a/app/user/cmd/api/internal/logic/query/querygeneratesharelinklogic.go b/app/user/cmd/api/internal/logic/query/querygeneratesharelinklogic.go new file mode 100644 index 0000000..93a3e83 --- /dev/null +++ b/app/user/cmd/api/internal/logic/query/querygeneratesharelinklogic.go @@ -0,0 +1,111 @@ +package query + +import ( + "context" + "encoding/hex" + "encoding/json" + "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/ctxdata" + "qnc-server/common/xerr" + "qnc-server/pkg/lzkit/crypto" + + "github.com/pkg/errors" + "github.com/zeromicro/go-zero/core/logx" +) + +type QueryGenerateShareLinkLogic struct { + logx.Logger + ctx context.Context + svcCtx *svc.ServiceContext +} + +func NewQueryGenerateShareLinkLogic(ctx context.Context, svcCtx *svc.ServiceContext) *QueryGenerateShareLinkLogic { + return &QueryGenerateShareLinkLogic{ + Logger: logx.WithContext(ctx), + ctx: ctx, + svcCtx: svcCtx, + } +} + +func (l *QueryGenerateShareLinkLogic) QueryGenerateShareLink(req *types.QueryGenerateShareLinkReq) (resp *types.QueryGenerateShareLinkResp, err error) { + userId, err := ctxdata.GetUidFromCtx(l.ctx) + if err != nil { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成分享链接, 获取用户ID失败: %v", err) + } + + // 检查参数 + if (req.OrderId == nil || *req.OrderId == 0) && (req.OrderNo == nil || *req.OrderNo == "") { + return nil, errors.Wrapf(xerr.NewErrMsg("订单ID和订单号不能同时为空"), "") + } + + var order *model.Order + // 优先使用OrderId查询 + if req.OrderId != nil && *req.OrderId != 0 { + order, err = l.svcCtx.OrderModel.FindOne(l.ctx, *req.OrderId) + if err != nil { + if errors.Is(err, model.ErrNotFound) { + return nil, errors.Wrapf(xerr.NewErrMsg("订单不存在"), "") + } + return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成分享链接, 获取订单失败: %v", err) + } + } else if req.OrderNo != nil && *req.OrderNo != "" { + // 使用OrderNo查询 + order, err = l.svcCtx.OrderModel.FindOneByOrderNo(l.ctx, *req.OrderNo) + if err != nil { + if errors.Is(err, model.ErrNotFound) { + return nil, errors.Wrapf(xerr.NewErrMsg("订单不存在"), "") + } + return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成分享链接, 获取订单失败: %v", err) + } + } else { + return nil, errors.Wrapf(xerr.NewErrMsg("订单ID和订单号不能同时为空"), "") + } + + if order.Status != model.OrderStatusPaid { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成分享链接, 订单未支付") + } + + query, err := l.svcCtx.QueryModel.FindOneByOrderId(l.ctx, order.Id) + if err != nil { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成分享链接, 获取查询失败: %v", err) + } + + if query.QueryState != model.QueryStateSuccess { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成分享链接, 查询未成功") + } + user, err := l.svcCtx.UserModel.FindOne(l.ctx, userId) + if err != nil { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成分享链接, 获取用户失败: %v", err) + } + if user.Inside != 1 { + if order.UserId != userId { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成分享链接, 无权操作此订单") + } + } + + expireAt := time.Now().Add(time.Duration(l.svcCtx.Config.Query.ShareLinkExpire) * time.Second) + payload := types.QueryShareLinkPayload{ + OrderId: order.Id, // 使用查询到的订单ID + ExpireAt: expireAt.Unix(), + } + secretKey := l.svcCtx.Config.Encrypt.SecretKey + key, err := hex.DecodeString(secretKey) + if err != nil { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成分享链接, 解密失败: %v", err) + } + payloadBytes, err := json.Marshal(payload) + if err != nil { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成分享链接, 序列化失败: %v", err) + } + encryptedPayload, err := crypto.AesEncryptURL(payloadBytes, key) + if err != nil { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成分享链接, 加密失败: %v", err) + } + return &types.QueryGenerateShareLinkResp{ + ShareLink: encryptedPayload, + }, nil +} diff --git a/app/user/cmd/api/internal/logic/query/querysharedetaillogic.go b/app/user/cmd/api/internal/logic/query/querysharedetaillogic.go new file mode 100644 index 0000000..eb61b96 --- /dev/null +++ b/app/user/cmd/api/internal/logic/query/querysharedetaillogic.go @@ -0,0 +1,164 @@ +package query + +import ( + "context" + "encoding/hex" + "fmt" + "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/lzkit/crypto" + + "github.com/bytedance/sonic" + "github.com/jinzhu/copier" + "github.com/pkg/errors" + "github.com/zeromicro/go-zero/core/logx" +) + +type QueryShareDetailLogic struct { + logx.Logger + ctx context.Context + svcCtx *svc.ServiceContext +} + +func NewQueryShareDetailLogic(ctx context.Context, svcCtx *svc.ServiceContext) *QueryShareDetailLogic { + return &QueryShareDetailLogic{ + Logger: logx.WithContext(ctx), + ctx: ctx, + svcCtx: svcCtx, + } +} + +func (l *QueryShareDetailLogic) QueryShareDetail(req *types.QueryShareDetailReq) (resp *types.QueryShareDetailResp, err error) { + 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", err) + } + decryptedID, decryptErr := crypto.AesDecryptURL(req.Id, key) + if decryptErr != nil { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "报告查询, 解密数据失败: %v", decryptErr) + } + + var payload types.QueryShareLinkPayload + err = sonic.Unmarshal(decryptedID, &payload) + if err != nil { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "报告查询, 解密数据失败: %v", err) + } + + // 检查分享链接是否过期 + now := time.Now().Unix() + if now > payload.ExpireAt { + return &types.QueryShareDetailResp{ + Status: "expired", + }, nil + } + + // 获取订单信息 + order, err := l.svcCtx.OrderModel.FindOne(l.ctx, payload.OrderId) + if err != nil { + if errors.Is(err, model.ErrNotFound) { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.LOGIC_QUERY_NOT_FOUND), "报告查询, 订单不存在: %v", err) + } + return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "报告查询, 查找报告错误: %v", err) + } + + // 检查订单状态 + if order.Status != "paid" { + return nil, errors.Wrapf(xerr.NewErrMsg("订单未支付,无法查看报告"), "") + } + + // 获取报告信息 + queryModel, err := l.svcCtx.QueryModel.FindOneByOrderId(l.ctx, order.Id) + if err != nil { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "报告查询, 查找报告错误: %v", err) + } + + 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") + + processParamsErr := ProcessQueryParams(queryModel.QueryParams, &query.QueryParams, key) + if processParamsErr != nil { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "报告查询, 报告参数处理失败: %v", processParamsErr) + } + processErr := ProcessQueryData(queryModel.QueryData, &query.QueryData, key) + if processErr != nil { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "报告查询, 报告结果处理失败: %v", processErr) + } + updateFeatureAndProductFeatureErr := l.UpdateFeatureAndProductFeature(queryModel.ProductId, &query.QueryData) + if updateFeatureAndProductFeatureErr != nil { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "报告查询, 报告结果处理失败: %v", updateFeatureAndProductFeatureErr) + } + // 复制报告数据 + err = copier.Copy(&query, queryModel) + if err != nil { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "报告查询, 报告结构体复制失败, %v", err) + } + product, err := l.svcCtx.ProductModel.FindOne(l.ctx, queryModel.ProductId) + if err != nil { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "报告查询, 获取商品信息失败, %v", err) + } + query.ProductName = product.ProductName + return &types.QueryShareDetailResp{ + Status: "success", + Query: query, + }, nil +} + +func (l *QueryShareDetailLogic) UpdateFeatureAndProductFeature(productID int64, target *[]types.QueryItem) error { + // 遍历 target 数组,使用倒序遍历,以便删除元素时不影响索引 + for i := len(*target) - 1; i >= 0; i-- { + queryItem := &(*target)[i] + + // 确保 Data 为 map 类型 + data, ok := queryItem.Data.(map[string]interface{}) + if !ok { + return fmt.Errorf("queryItem.Data 必须是 map[string]interface{} 类型") + } + + // 从 Data 中获取 apiID + apiID, ok := data["apiID"].(string) + if !ok { + return fmt.Errorf("queryItem.Data 中的 apiID 必须是字符串类型") + } + + // 查询 Feature + feature, err := l.svcCtx.FeatureModel.FindOneByApiId(l.ctx, apiID) + if err != nil { + // 如果 Feature 查不到,也要删除当前 QueryItem + *target = append((*target)[:i], (*target)[i+1:]...) + continue + } + + // 查询 ProductFeatureModel + builder := l.svcCtx.ProductFeatureModel.SelectBuilder().Where("product_id = ?", productID) + productFeatures, err := l.svcCtx.ProductFeatureModel.FindAll(l.ctx, builder, "") + if err != nil { + return fmt.Errorf("查询 ProductFeatureModel 错误: %v", err) + } + + // 遍历 productFeatures,找到与 feature.ID 关联且 enable == 1 的项 + var featureData map[string]interface{} + // foundFeature := false + sort := 0 + for _, pf := range productFeatures { + if pf.FeatureId == feature.Id { // 确保和 Feature 关联 + sort = int(pf.Sort) + break // 找到第一个符合条件的就退出循环 + } + } + featureData = map[string]interface{}{ + "featureName": feature.Name, + "sort": sort, + } + + // 更新 queryItem 的 Feature 字段(不是数组) + queryItem.Feature = featureData + } + + return nil +} diff --git a/app/user/cmd/api/internal/types/encrypPayload.go b/app/user/cmd/api/internal/types/encrypPayload.go new file mode 100644 index 0000000..3c6e385 --- /dev/null +++ b/app/user/cmd/api/internal/types/encrypPayload.go @@ -0,0 +1,6 @@ +package types + +type QueryShareLinkPayload struct { + OrderId int64 `json:"order_id"` + ExpireAt int64 `json:"expire_at"` +} diff --git a/app/user/cmd/api/internal/types/types.go b/app/user/cmd/api/internal/types/types.go index 919d640..438bcbe 100644 --- a/app/user/cmd/api/internal/types/types.go +++ b/app/user/cmd/api/internal/types/types.go @@ -424,6 +424,15 @@ type QueryExampleResp struct { Query } +type QueryGenerateShareLinkReq struct { + OrderId *int64 `json:"order_id,optional"` + OrderNo *string `json:"order_no,optional"` +} + +type QueryGenerateShareLinkResp struct { + ShareLink string `json:"share_link"` +} + type QueryItem struct { Feature interface{} `json:"feature"` Data interface{} `json:"data"` // 这里可以是 map 或 具体的 struct @@ -492,6 +501,15 @@ type QueryServiceResp struct { RefreshAfter int64 `json:"refreshAfter"` } +type QueryShareDetailReq struct { + Id string `path:"id"` +} + +type QueryShareDetailResp struct { + Status string `json:"status"` + Query +} + type QuerySingleTestReq struct { Params map[string]interface{} `json:"params"` Api string `json:"api"` diff --git a/common/jwt/jwtx_test.go b/common/jwt/jwtx_test.go index 43d5424..c31ebb4 100644 --- a/common/jwt/jwtx_test.go +++ b/common/jwt/jwtx_test.go @@ -8,7 +8,7 @@ import ( func TestGenerateAndParseJwtToken(t *testing.T) { // 测试参数 - userId := int64(2012) + userId := int64(2043) secret := "WUvoIwL-FK0qnlxhvxR9tV6SjfOpeJMpKmY2QvT99lA" expireTime := int64(2592000) // 1小时过期