From f95075e2e4419ee55d0461f550a24eec4655aaf1 Mon Sep 17 00:00:00 2001 From: yml2213 Date: Mon, 3 Aug 2026 14:32:08 +0800 Subject: [PATCH] =?UTF-8?q?data=20=E9=80=8F=E4=BC=A0=E9=97=AD=E7=8E=AF?= =?UTF-8?q?=EF=BC=9A=E5=8F=91=E8=B4=A7=E6=95=B0=E6=8D=AE=E5=9B=9E=E6=98=BE?= =?UTF-8?q?=E3=80=81=E5=9B=9E=E8=B0=83=E5=B8=A6=E5=9B=9E=EF=BC=8C=E5=B9=B6?= =?UTF-8?q?=E9=99=90=E5=88=B6=202KB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 「查询发货数据」响应新增 data(下单透传原样回显,供自建发货页预填/展示) - 回调 payload 带回 data(下单传 → 回调收,商户对接免回查) - 创建订单 data 限制最长 2048 字节 - 文档更新:data 用途(自建页预填/回调/上游查单约定字段)、发货数据与回调示例 - 单测覆盖 data 回显 --- backend/internal/handler/open_v1.go | 5 +++++ backend/internal/service/delivery.go | 3 +++ backend/internal/service/delivery_test.go | 7 +++++++ backend/internal/service/fulfillment.go | 15 ++++++++++++++- frontend/src/openapi/endpoints.ts | 6 ++++-- frontend/src/pages/OpenApiDocs.tsx | 5 +++-- 6 files changed, 36 insertions(+), 5 deletions(-) diff --git a/backend/internal/handler/open_v1.go b/backend/internal/handler/open_v1.go index b8ad76f..43d30e8 100644 --- a/backend/internal/handler/open_v1.go +++ b/backend/internal/handler/open_v1.go @@ -58,6 +58,11 @@ func (h *OpenV1Handler) CreateOrder(c *gin.Context) { } var data interface{} if len(req.Data) > 0 { + if len(req.Data) > 2048 { + openlog.Warn(c, "create_order data_too_large size=%d", len(req.Data)) + response.BadRequest(c, "data 最长 2048 字节") + return + } var decoded interface{} if err := json.Unmarshal(req.Data, &decoded); err != nil { openlog.Warn(c, "create_order bad_data err=%v", err) diff --git a/backend/internal/service/delivery.go b/backend/internal/service/delivery.go index 6f2e2ce..5cdd94d 100644 --- a/backend/internal/service/delivery.go +++ b/backend/internal/service/delivery.go @@ -113,6 +113,7 @@ type DeliveryOrderInfo struct { GameUID string `json:"game_uid,omitempty"` RoleName string `json:"role_name,omitempty"` PayScore int `json:"pay_score,omitempty"` + Data map[string]interface{} `json:"data,omitempty"` Good map[string]interface{} `json:"good,omitempty"` } @@ -558,6 +559,7 @@ func (s *DeliveryService) buildDeliveryState(openOrder *OpenOrderQuery, order *m GameUID: openOrder.GameUID, RoleName: openOrder.RoleName, PayScore: openOrder.PayScore, + Data: requestDataMap(order.RequestData), Good: good, } return info, order, goodID, nil @@ -767,6 +769,7 @@ func (s *DeliveryService) prepareOrder(orderNo string, requireCanShip bool, auth GameUID: openOrder.GameUID, RoleName: openOrder.RoleName, PayScore: openOrder.PayScore, + Data: requestDataMap(order.RequestData), Good: good, } return info, order, goodID, nil diff --git a/backend/internal/service/delivery_test.go b/backend/internal/service/delivery_test.go index e92e6f0..682ae5d 100644 --- a/backend/internal/service/delivery_test.go +++ b/backend/internal/service/delivery_test.go @@ -221,6 +221,10 @@ func TestDeliveryMerchantApiBindAndSubmit(t *testing.T) { APIClientID: 1, ClientOrderNo: "delivery-api-001", SKU: "suit_alan_walker", + RequestData: map[string]interface{}{ + "game_account": "4808146277", + "game_channel": "ios-wechat", + }, }) if err != nil { t.Fatalf("create order: %v", err) @@ -234,6 +238,9 @@ func TestDeliveryMerchantApiBindAndSubmit(t *testing.T) { if !info.CanShip || info.Product == nil || info.Product.Image == "" { t.Fatalf("merchant order should include delivery data: %+v", info) } + if info.Data["game_account"] != "4808146277" || info.Data["game_channel"] != "ios-wechat" { + t.Fatalf("delivery order should echo request data: %+v", info.Data) + } bind, err := deliverySvc.BindForMerchant(merchantID, created.Order.OrderNo, "4808146277") if err != nil { diff --git a/backend/internal/service/fulfillment.go b/backend/internal/service/fulfillment.go index 94c7d4d..4b01b6d 100644 --- a/backend/internal/service/fulfillment.go +++ b/backend/internal/service/fulfillment.go @@ -772,7 +772,7 @@ func CanFulfill(order *model.FulfillmentOrder) (bool, string) { func orderCallbackData(order *model.FulfillmentOrder) map[string]interface{} { canShip, cannotShipReason := CanFulfill(order) - return map[string]interface{}{ + data := map[string]interface{}{ "order_no": order.OrderNo, "client_order_no": order.ClientOrderNo, "product_sku": order.ProductSKU, @@ -788,6 +788,10 @@ func orderCallbackData(order *model.FulfillmentOrder) map[string]interface{} { "provider_order_no": order.ProviderOrderNo, "failure_reason": order.FailureReason, } + if requestData := requestDataMap(order.RequestData); len(requestData) > 0 { + data["data"] = requestData + } + return data } // ----- 仪表盘统计 ----- @@ -1309,6 +1313,15 @@ func resultDataMap(raw string) map[string]interface{} { return m } +// requestDataMap 解析下单透传的 data(RequestData),非法或为空时返回空 map。 +func requestDataMap(raw string) map[string]interface{} { + m := map[string]interface{}{} + if raw != "" && json.Valid([]byte(raw)) { + _ = json.Unmarshal([]byte(raw), &m) + } + return m +} + func resultDataString(raw, key string) string { if v, ok := resultDataMap(raw)[key].(string); ok { return v diff --git a/frontend/src/openapi/endpoints.ts b/frontend/src/openapi/endpoints.ts index 12754aa..b4a53b3 100644 --- a/frontend/src/openapi/endpoints.ts +++ b/frontend/src/openapi/endpoints.ts @@ -94,6 +94,7 @@ const deliveryOrderResponseExample = `{ "game_uid": "4808146277", "role_name": "巫师哈丁12", "pay_score": 360, + "data": { "game_account": "4808146277", "game_channel": "ios-wechat", "role_name": "玩家名" }, "good": { "title": "套装-糯粉咩咩", "image": "https://..." } } }` @@ -196,14 +197,14 @@ export const endpoints: EndpointSpec[] = [ { name: 'sku', type: 'string', required: true, desc: '商品标识,取自商品列表', example: 'suit_pink_sheep' }, { name: 'quantity', type: 'int', desc: '数量,默认 1', example: '1' }, { name: 'buyer_reference', type: 'string', desc: '买家标识/备注', example: 'buyer-001' }, - { name: 'data', type: 'object', desc: '透传给发货流程的业务数据(区服、账号等),原样存储并回显' }, + { name: 'data', type: 'object', desc: '透传业务数据(最长 2048 字节)。自建发货页模式会原样返回给「查询发货数据」用于预填/展示;回调 payload 也会带回;约定字段 game_account/game_channel/role_name 会同步给上游查单' }, ], requestExample: `{ "client_order_no": "shop-10001", "sku": "suit_pink_sheep", "quantity": 1, "buyer_reference": "buyer-001", - "data": { "server": "ios-wechat", "uid": "player-id" } + "data": { "game_account": "4808146277", "game_channel": "ios-wechat", "role_name": "玩家名" } }`, responseExample: `{ "code": 0, @@ -317,6 +318,7 @@ export const endpoints: EndpointSpec[] = [ { name: 'game_uid', type: 'string', desc: 'UID / 角色编号' }, { name: 'role_name', type: 'string', desc: '角色名' }, { name: 'pay_score', type: 'int', desc: '消耗积分' }, + { name: 'data', type: 'object', desc: '下单时透传的业务数据(原样返回,可用于预填玩家编号/展示区服)' }, { name: 'good', type: 'object', desc: '上游商品详情' }, ], notes: [ diff --git a/frontend/src/pages/OpenApiDocs.tsx b/frontend/src/pages/OpenApiDocs.tsx index 87ced60..75a87ee 100644 --- a/frontend/src/pages/OpenApiDocs.tsx +++ b/frontend/src/pages/OpenApiDocs.tsx @@ -490,14 +490,15 @@ const callbackPayloadExample = `{ "client_order_no": "shop-10001", "order_status": "delivered", "can_ship": false, - "product": { "sku": "suit_pink_sheep", "name": "套装-糯粉咩咩" }, + "product_sku": "suit_pink_sheep", "quantity": 1, "amount": 105, "currency": "POINT", "provider_order_no": "PROV-xxxx", "failure_reason": "", "created_at": "2026-08-03T10:00:00+08:00", - "delivered_at": "2026-08-03T10:11:20+08:00" + "delivered_at": "2026-08-03T10:11:20+08:00", + "data": { "game_account": "4808146277", "game_channel": "ios-wechat", "role_name": "玩家名" } } }`