- 后端 internal/service 按职责拆分: fulfillment.go(1397→527)拆出 wallet/timeout/data/order/query/dashboard/shipnotify delivery.go(1124→801)拆出 upstream/link/state/helpers merchant.go(855→251)拆出 member/product/api_client/catalog/helpers - 前端 MerchantCenter.tsx(1327→606)拆出 merchantCenterTabs/merchantCenterUtils - docker-compose backend 透传 CORS_ALLOWED_ORIGINS - CORS 白名单实现(config/router/README/.env.example 配套) - 修复 gofmt 与文件尾部多余空行
158 lines
3.5 KiB
Go
158 lines
3.5 KiB
Go
package service
|
||
|
||
import (
|
||
"encoding/json"
|
||
"strconv"
|
||
"strings"
|
||
"time"
|
||
|
||
"affiliate_dash/internal/pkg/timeutil"
|
||
)
|
||
|
||
// buildShipNotifyResultData 把推送结果与游戏字段合并进 ResultData JSON。
|
||
func buildShipNotifyResultData(existing string, in ShipNotifyInput, shippedAt *time.Time) string {
|
||
m := map[string]interface{}{}
|
||
if existing != "" && json.Valid([]byte(existing)) {
|
||
_ = json.Unmarshal([]byte(existing), &m)
|
||
}
|
||
m["ship_status"] = in.ShipStatus
|
||
if in.ProviderOrderNo != "" {
|
||
m["provider_order_no"] = in.ProviderOrderNo
|
||
}
|
||
if in.ShipStatus == "success" {
|
||
delete(m, "fail_reason")
|
||
} else if in.FailReason != "" {
|
||
m["fail_reason"] = in.FailReason
|
||
}
|
||
if shippedAt != nil {
|
||
m["shipped_at"] = timeutil.FormatAPITime(*shippedAt)
|
||
}
|
||
if in.GameChannel != nil {
|
||
m["game_channel"] = *in.GameChannel
|
||
}
|
||
if in.GameUID != nil {
|
||
m["game_uid"] = *in.GameUID
|
||
}
|
||
if in.RoleName != nil {
|
||
m["role_name"] = *in.RoleName
|
||
}
|
||
if in.PayScore != nil {
|
||
m["pay_score"] = *in.PayScore
|
||
}
|
||
raw, err := json.Marshal(m)
|
||
if err != nil {
|
||
return existing
|
||
}
|
||
return string(raw)
|
||
}
|
||
|
||
// mergeResultData 保留已有 JSON 字段,仅覆盖或新增 patch 中的字段,避免各发货阶段互相清空上下文。
|
||
func mergeResultData(existing string, patch map[string]interface{}) string {
|
||
if len(patch) == 0 {
|
||
return existing
|
||
}
|
||
m := map[string]interface{}{}
|
||
if existing != "" && json.Valid([]byte(existing)) {
|
||
_ = json.Unmarshal([]byte(existing), &m)
|
||
}
|
||
for k, v := range patch {
|
||
m[k] = v
|
||
}
|
||
raw, err := json.Marshal(m)
|
||
if err != nil {
|
||
return existing
|
||
}
|
||
return string(raw)
|
||
}
|
||
|
||
func resultDataMap(raw string) map[string]interface{} {
|
||
m := map[string]interface{}{}
|
||
if raw != "" && json.Valid([]byte(raw)) {
|
||
_ = json.Unmarshal([]byte(raw), &m)
|
||
}
|
||
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
|
||
}
|
||
|
||
// requestDataString 读取下单透传 data 中的字符串字段。
|
||
func requestDataString(raw, key string) string {
|
||
if v, ok := requestDataMap(raw)[key].(string); ok {
|
||
return strings.TrimSpace(v)
|
||
}
|
||
return ""
|
||
}
|
||
|
||
func resultDataString(raw, key string) string {
|
||
if v, ok := resultDataMap(raw)[key].(string); ok {
|
||
return v
|
||
}
|
||
return ""
|
||
}
|
||
|
||
func resultDataNumber(raw, key string) int64 {
|
||
switch v := resultDataMap(raw)[key].(type) {
|
||
case float64:
|
||
return int64(v)
|
||
case int64:
|
||
return v
|
||
case int:
|
||
return int64(v)
|
||
case string:
|
||
if n, err := strconv.ParseInt(v, 10, 64); err == nil {
|
||
return n
|
||
}
|
||
}
|
||
return 0
|
||
}
|
||
|
||
// extractGameFields 从 JSON 文本中还原游戏相关字段(仅填充当前为空的字段)。
|
||
func extractGameFields(raw string, out *OpenOrderQuery) {
|
||
if raw == "" || !json.Valid([]byte(raw)) {
|
||
return
|
||
}
|
||
var m map[string]interface{}
|
||
if err := json.Unmarshal([]byte(raw), &m); err != nil {
|
||
return
|
||
}
|
||
if out.GameChannel == "" {
|
||
if v, ok := m["game_channel"].(string); ok {
|
||
out.GameChannel = v
|
||
}
|
||
}
|
||
if out.GameUID == "" {
|
||
if v, ok := m["game_uid"].(string); ok {
|
||
out.GameUID = v
|
||
}
|
||
}
|
||
if out.RoleName == "" {
|
||
if v, ok := m["role_name"].(string); ok {
|
||
out.RoleName = v
|
||
}
|
||
}
|
||
if out.PayScore == 0 {
|
||
if v, ok := toInt(m["pay_score"]); ok {
|
||
out.PayScore = v
|
||
}
|
||
}
|
||
}
|
||
|
||
func toInt(v interface{}) (int, bool) {
|
||
switch n := v.(type) {
|
||
case float64:
|
||
return int(n), true
|
||
case int:
|
||
return n, true
|
||
case int64:
|
||
return int(n), true
|
||
}
|
||
return 0, false
|
||
}
|