fix: 统一顺运宝单条采集客户端选择 (#174)

This commit is contained in:
chengma
2026-08-12 09:56:15 +08:00
parent 6127551a7b
commit f693d2a6fc
5 changed files with 98 additions and 4 deletions
+8 -2
View File
@@ -207,8 +207,13 @@ func (h *Handler) SybDetail(c *gin.Context) {
fail(c, http.StatusNotFound, "顺运宝货运单明细不存在。")
return
}
assignableClients, err := service.ListAssignableClients(h.db, currentUser(c), h.onlineThreshold)
if err != nil {
fail(c, http.StatusInternalServerError, "读取可分配客户端失败,数据没有被改动。")
return
}
c.HTML(http.StatusOK, "syb/detail_modal", gin.H{
"D": detail, "CSRFToken": csrfToken(c),
"D": detail, "CSRFToken": csrfToken(c), "AssignableClients": assignableClients,
"Keyword": c.Query("order_no"), "ShopFilter": c.Query("shop"), "StageFilter": c.Query("stage"),
"CurrentPage": service.ParsePage(c.Query("page")),
})
@@ -603,7 +608,8 @@ func (h *Handler) SybAssociatePdd(c *gin.Context) {
// SybCollectPdd 为货运单当前关联的 PDD 商品创建采集任务。
func (h *Handler) SybCollectPdd(c *gin.Context) {
result, err := service.CreateSybPddCollectTaskForUser(h.db, currentUser(c), c.PostForm("syb_id"))
result, err := service.CreateSybPddCollectTasksForUser(
h.db, currentUser(c), []string{c.PostForm("syb_id")}, c.PostForm("client_id"))
if err != nil {
h.sybRedirect(c, "采集任务未创建:"+err.Error())
return
+37
View File
@@ -143,6 +143,15 @@ func TestCreateSybPddCollectTasksForUser_同一PDD商品去重(t *testing.T) {
if err := db.QueryRow(`SELECT COUNT(*) FROM tasks WHERE task_type='collect'`).Scan(&taskCount); err != nil || taskCount != 1 {
t.Fatalf("同一 PDD 商品应只建一条任务,count=%d err=%v", taskCount, err)
}
var status model.TaskStatus
var assignedClient sql.NullString
if err := db.QueryRow(`SELECT status,assigned_client FROM tasks WHERE task_type='collect'`).
Scan(&status, &assignedClient); err != nil {
t.Fatal(err)
}
if status != model.TaskPending || assignedClient.Valid {
t.Fatalf("不指定客户端时应为无主待领,status=%q client=%+v", status, assignedClient)
}
var sourceCount int
if err := db.QueryRow(`SELECT COUNT(*) FROM task_syb_sources`).Scan(&sourceCount); err != nil || sourceCount != 2 {
t.Fatalf("去重任务应保留两条顺运宝来源,count=%d err=%v", sourceCount, err)
@@ -158,6 +167,34 @@ func TestCreateSybPddCollectTasksForUser_同一PDD商品去重(t *testing.T) {
}
}
func TestCreateSybPddCollectTasksForUser_指定可见客户端(t *testing.T) {
db := newTestDB(t)
admin, _, _ := insertTaskUsers(t, db)
if err := RegisterClient(db, model.Client{ClientID: "client-chosen", Name: "仓库手机"}, true); err != nil {
t.Fatal(err)
}
seedShopeeProduct(t, db, "1001", "商品一")
if _, err := AssociateShopeePdd(db, "1001", pddURLA, false); err != nil {
t.Fatal(err)
}
seedSybCollectOrder(t, db, "SYB-CHOSEN", "1001")
result, err := CreateSybPddCollectTasksForUser(
db, admin, []string{"SYB-CHOSEN"}, "client-chosen")
if err != nil || result.Created != 1 {
t.Fatalf("指定客户端创建结果=%+v err=%v", result, err)
}
var status model.TaskStatus
var assignedClient string
if err := db.QueryRow(`SELECT status,assigned_client FROM tasks WHERE task_type='collect' AND pdd_goods_id='737116531267'`).
Scan(&status, &assignedClient); err != nil {
t.Fatal(err)
}
if status != model.TaskAssigned || assignedClient != "client-chosen" {
t.Fatalf("指定客户端任务状态=%q client=%q", status, assignedClient)
}
}
func TestCreateSybPddCollectTasksForUser_无任务的采集中状态可恢复(t *testing.T) {
db := newTestDB(t)
admin, _, _ := insertTaskUsers(t, db)
+35
View File
@@ -6,6 +6,7 @@ import (
"strings"
"testing"
"cmautobuy/admin/model"
"cmautobuy/admin/service"
)
@@ -87,3 +88,37 @@ func TestSybListTemplate_区分标题图片与阶段动作(t *testing.T) {
t.Error("顺运宝列表不应继续使用含义模糊的整行双击入口")
}
}
func TestSybDetailTemplate_单条采集可选执行客户端(t *testing.T) {
tmpl, err := template.ParseFS(templateFS, "templates/*/*.html")
if err != nil {
t.Fatal(err)
}
data := map[string]any{
"D": &service.SybProcessingDetail{
SybID: "SYB-1", OrderNo: "ORDER-1", Title: "待采集商品",
PddGoodsID: "737116531267", CanCollect: true,
},
"CSRFToken": "test-csrf",
"AssignableClients": []service.ClientView{{
Client: model.Client{ClientID: "CLIENT-1", Name: "仓库手机"}, Status: "在线",
}},
}
var output bytes.Buffer
if err := tmpl.ExecuteTemplate(&output, "syb/detail_modal", data); err != nil {
t.Fatalf("渲染顺运宝详情失败: %v", err)
}
body := output.String()
for _, want := range []string{
`action="/syb/collect-pdd"`,
`label for="syb-detail-collect-client"`,
`select id="syb-detail-collect-client" name="client_id"`,
`option value="">不指定(任意客户端可领取)`,
`option value="CLIENT-1">仓库手机(CLIENT-1,在线)`,
`指定后任务只等待该客户端领取`,
} {
if !strings.Contains(body, want) {
t.Errorf("顺运宝详情缺少 %q", want)
}
}
}
+12 -2
View File
@@ -39,7 +39,7 @@
</form>
{{if .PddGoodsID}}
<form method="post" action="/syb/collect-pdd" class="inline">
<form method="post" action="/syb/collect-pdd" class="form-stack">
<input type="hidden" name="csrf_token" value="{{$.CSRFToken}}">
<input type="hidden" name="syb_id" value="{{.SybID}}">
<input type="hidden" name="order_no" value="{{$.Keyword}}">
@@ -47,7 +47,17 @@
<input type="hidden" name="stage" value="{{$.StageFilter}}">
<input type="hidden" name="page" value="{{$.CurrentPage}}">
{{if .CanCollect}}
<button type="submit">{{if or (eq .CollectStatus "failed") (eq .Stage "pdd_collecting_stale")}}重新创建采集任务{{else}}创建采集任务{{end}}</button>
<div class="field">
<label for="syb-detail-collect-client">执行客户端</label>
<select id="syb-detail-collect-client" name="client_id">
<option value="">不指定(任意客户端可领取)</option>
{{range $.AssignableClients}}
<option value="{{.ClientID}}">{{.Name}}({{.ClientID}},{{.Status}})</option>
{{end}}
</select>
<small>通常保持不指定即可;指定后任务只等待该客户端领取。</small>
</div>
<div><button type="submit">{{if or (eq .CollectStatus "failed") (eq .Stage "pdd_collecting_stale")}}重新创建采集任务{{else}}创建采集任务{{end}}</button></div>
{{else}}
<span class="hint">{{if eq .CollectStatus "collecting"}}采集任务正在执行,请稍后刷新。{{else}}PDD 数据已采集,下一步匹配采购规格。{{end}}</span>
{{end}}
+6
View File
@@ -561,6 +561,12 @@ PDD 采集中(超时)行可用于
标准商品链接并按 `goods_id` 复用已有档案;换成其他商品需要明确确认。待采集或采集失败时显示
“创建采集任务”或“重新创建采集任务”,采集中和已采集状态只显示说明。
单条创建和重新创建采集任务时,按钮上方显示有可见标签的“执行客户端”
下拉框,只列出当前账号可见的 Client,并显示名称、Client ID 和在线状态。
默认为“不指定(任意客户端可领取)”;指定后只等待该 Client 领取。持续
帮助文字必须说清两种选择的后果,不依赖 placeholder 或颜色。该入口与列表
顶部批量创建共用同一套服务端阶段、客户端可见范围和重复任务校验。
```text
┌──────────────────────────────────────────────────┐
│ 规格匹配 · 订单 SO-20260806-001 │