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": "玩家名" } } }`