feat(rock): 关联业务工单升级为双单号清晰展示(系统工单号 + 创建商户单号)
1. 后端批量反查商户单号:在 RockService.ListTokens 中支持批量反查关联订单创建时由调用方传入的 ClientOrderNo(商户单号),并拓展支持通过商户单号进行全局关键词模糊搜索; 2. 管理后台双单号排版升级:在 Rock 管理表格中将「关联业务工单」升级为双行呈现——第一行高亮展示内部统一系统工单号 (FO2026...),第二行辅助展示外部电商/商户原始单号 (如 test4 / 拼多多单号),两行均支持一键独立复制; 3. 单元测试全覆盖:补充商户单号自动挂载关联与按商户单号模糊检索的自动化单测,确保对单溯源精准无误。
This commit is contained in:
@@ -56,6 +56,7 @@ type RockToken struct {
|
||||
Region string `gorm:"size:64;not null;default:'Q/V可用'" json:"region"`
|
||||
Status string `gorm:"size:32;not null;default:'available';index" json:"status"`
|
||||
AssignedOrderNo string `gorm:"size:128;index" json:"assigned_order_no"`
|
||||
ClientOrderNo string `gorm:"-" json:"client_order_no,omitempty"` // 关联的创建订单商户单号
|
||||
ExpectedUID string `gorm:"size:64" json:"expected_uid"`
|
||||
ActualRoleName string `gorm:"size:128" json:"actual_role_name"`
|
||||
ActualRoleID string `gorm:"size:64" json:"actual_role_id"`
|
||||
|
||||
@@ -459,4 +459,41 @@ func TestRockService_StatsAndPurchase(t *testing.T) {
|
||||
if total != 2 || len(list) != 2 {
|
||||
t.Fatalf("期望查询到 2 条,实际为 total=%d len=%d", total, len(list))
|
||||
}
|
||||
|
||||
// 6. 测试关联履约订单与创建订单商户单号反查
|
||||
orderNo := "FO_TEST_ROCK_999"
|
||||
clientOrderNo := "SHOP_ORDER_PDD_888"
|
||||
_ = db.Create(&model.FulfillmentOrder{
|
||||
OrderNo: orderNo,
|
||||
ClientOrderNo: clientOrderNo,
|
||||
ProductSKU: "rock_black_tshirt",
|
||||
ProductName: "黑色ROCK长袖T恤",
|
||||
}).Error
|
||||
_ = db.Model(&model.RockToken{}).Where("id = ?", list[0].ID).Update("assigned_order_no", orderNo).Error
|
||||
|
||||
listWithClient, _, err := rockSvc.ListTokens(context.Background(), 1, 10, "", "")
|
||||
if err != nil {
|
||||
t.Fatalf("list tokens with client: %v", err)
|
||||
}
|
||||
found := false
|
||||
for _, tk := range listWithClient {
|
||||
if tk.ID == list[0].ID {
|
||||
found = true
|
||||
if tk.ClientOrderNo != clientOrderNo {
|
||||
t.Fatalf("期望商户单号为 %s,实际为 %s", clientOrderNo, tk.ClientOrderNo)
|
||||
}
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Fatalf("未找到对应的 Token 记录")
|
||||
}
|
||||
|
||||
// 7. 测试按商户单号关键词搜索
|
||||
searchList, searchTotal, err := rockSvc.ListTokens(context.Background(), 1, 10, "", "PDD_888")
|
||||
if err != nil {
|
||||
t.Fatalf("search tokens by client_order_no: %v", err)
|
||||
}
|
||||
if searchTotal != 1 || len(searchList) != 1 {
|
||||
t.Fatalf("期望按商户单号搜索出 1 条,实际为 total=%d len=%d", searchTotal, len(searchList))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -643,8 +643,8 @@ func (s *RockService) ListTokens(ctx context.Context, page, pageSize int, status
|
||||
}
|
||||
if strings.TrimSpace(search) != "" {
|
||||
term := "%" + strings.TrimSpace(search) + "%"
|
||||
db = db.Where("assigned_order_no LIKE ? OR expected_uid LIKE ? OR actual_role_name LIKE ? OR actual_role_id LIKE ? OR redeem_code LIKE ?",
|
||||
term, term, term, term, term)
|
||||
db = db.Where("assigned_order_no LIKE ? OR expected_uid LIKE ? OR actual_role_name LIKE ? OR actual_role_id LIKE ? OR redeem_code LIKE ? OR assigned_order_no IN (SELECT order_no FROM fulfillment_orders WHERE client_order_no LIKE ?)",
|
||||
term, term, term, term, term, term)
|
||||
}
|
||||
|
||||
var total int64
|
||||
@@ -661,6 +661,34 @@ func (s *RockService) ListTokens(ctx context.Context, page, pageSize int, status
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
// 批量反查关联订单的商户单号(创建订单时的 ClientOrderNo)
|
||||
orderNos := make([]string, 0, len(tokens))
|
||||
for _, t := range tokens {
|
||||
if t.AssignedOrderNo != "" {
|
||||
orderNos = append(orderNos, t.AssignedOrderNo)
|
||||
}
|
||||
}
|
||||
if len(orderNos) > 0 {
|
||||
var orders []struct {
|
||||
OrderNo string `gorm:"column:order_no"`
|
||||
ClientOrderNo string `gorm:"column:client_order_no"`
|
||||
}
|
||||
if scanErr := s.db.WithContext(ctx).Table("fulfillment_orders").
|
||||
Select("order_no, client_order_no").
|
||||
Where("order_no IN ?", orderNos).
|
||||
Scan(&orders).Error; scanErr == nil {
|
||||
clientMap := make(map[string]string, len(orders))
|
||||
for _, o := range orders {
|
||||
clientMap[o.OrderNo] = o.ClientOrderNo
|
||||
}
|
||||
for i := range tokens {
|
||||
if cNo, ok := clientMap[tokens[i].AssignedOrderNo]; ok && cNo != "" {
|
||||
tokens[i].ClientOrderNo = cNo
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return tokens, total, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -301,6 +301,7 @@ export interface RockTokenItem {
|
||||
region: string
|
||||
status: string
|
||||
assigned_order_no?: string
|
||||
client_order_no?: string
|
||||
expected_uid?: string
|
||||
actual_role_name?: string
|
||||
actual_role_id?: string
|
||||
|
||||
@@ -357,15 +357,41 @@ export default function PlatformRock() {
|
||||
{
|
||||
title: '关联业务工单',
|
||||
dataIndex: 'assigned_order_no',
|
||||
width: 190,
|
||||
render: (orderNo) =>
|
||||
orderNo ? (
|
||||
<Text code copyable={{ text: orderNo }} style={{ fontWeight: 600, color: '#1d4ed8' }}>
|
||||
{orderNo}
|
||||
</Text>
|
||||
) : (
|
||||
<Text type="secondary">-</Text>
|
||||
),
|
||||
width: 220,
|
||||
render: (orderNo, record) => {
|
||||
if (!orderNo) {
|
||||
return <Text type="secondary">-</Text>
|
||||
}
|
||||
return (
|
||||
<Space direction="vertical" size={3}>
|
||||
{/* 系统履约工单号 */}
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 4 }}>
|
||||
<Text
|
||||
code
|
||||
copyable={{ text: orderNo }}
|
||||
style={{ fontWeight: 600, color: '#1d4ed8', fontSize: 12 }}
|
||||
title="系统履约工单号"
|
||||
>
|
||||
{orderNo}
|
||||
</Text>
|
||||
</div>
|
||||
{/* 创建订单时的商户单号 */}
|
||||
{record.client_order_no ? (
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 4, fontSize: 12 }}>
|
||||
<span style={{ color: '#64748b' }}>商户:</span>
|
||||
<Text
|
||||
copyable={{ text: record.client_order_no }}
|
||||
type="secondary"
|
||||
style={{ fontFamily: 'monospace', fontSize: 12 }}
|
||||
title="创建订单时的商户单号 (ClientOrderNo)"
|
||||
>
|
||||
{record.client_order_no}
|
||||
</Text>
|
||||
</div>
|
||||
) : null}
|
||||
</Space>
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '预期 UID vs 扫码角色 (风控门禁)',
|
||||
|
||||
Reference in New Issue
Block a user