fix: 统一顺运宝单条采集客户端选择 (#174)
This commit is contained in:
@@ -207,8 +207,13 @@ func (h *Handler) SybDetail(c *gin.Context) {
|
|||||||
fail(c, http.StatusNotFound, "顺运宝货运单明细不存在。")
|
fail(c, http.StatusNotFound, "顺运宝货运单明细不存在。")
|
||||||
return
|
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{
|
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"),
|
"Keyword": c.Query("order_no"), "ShopFilter": c.Query("shop"), "StageFilter": c.Query("stage"),
|
||||||
"CurrentPage": service.ParsePage(c.Query("page")),
|
"CurrentPage": service.ParsePage(c.Query("page")),
|
||||||
})
|
})
|
||||||
@@ -603,7 +608,8 @@ func (h *Handler) SybAssociatePdd(c *gin.Context) {
|
|||||||
|
|
||||||
// SybCollectPdd 为货运单当前关联的 PDD 商品创建采集任务。
|
// SybCollectPdd 为货运单当前关联的 PDD 商品创建采集任务。
|
||||||
func (h *Handler) SybCollectPdd(c *gin.Context) {
|
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 {
|
if err != nil {
|
||||||
h.sybRedirect(c, "采集任务未创建:"+err.Error())
|
h.sybRedirect(c, "采集任务未创建:"+err.Error())
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -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 {
|
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)
|
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
|
var sourceCount int
|
||||||
if err := db.QueryRow(`SELECT COUNT(*) FROM task_syb_sources`).Scan(&sourceCount); err != nil || sourceCount != 2 {
|
if err := db.QueryRow(`SELECT COUNT(*) FROM task_syb_sources`).Scan(&sourceCount); err != nil || sourceCount != 2 {
|
||||||
t.Fatalf("去重任务应保留两条顺运宝来源,count=%d err=%v", sourceCount, err)
|
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) {
|
func TestCreateSybPddCollectTasksForUser_无任务的采集中状态可恢复(t *testing.T) {
|
||||||
db := newTestDB(t)
|
db := newTestDB(t)
|
||||||
admin, _, _ := insertTaskUsers(t, db)
|
admin, _, _ := insertTaskUsers(t, db)
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"cmautobuy/admin/model"
|
||||||
"cmautobuy/admin/service"
|
"cmautobuy/admin/service"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -87,3 +88,37 @@ func TestSybListTemplate_区分标题图片与阶段动作(t *testing.T) {
|
|||||||
t.Error("顺运宝列表不应继续使用含义模糊的整行双击入口")
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -39,7 +39,7 @@
|
|||||||
</form>
|
</form>
|
||||||
|
|
||||||
{{if .PddGoodsID}}
|
{{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="csrf_token" value="{{$.CSRFToken}}">
|
||||||
<input type="hidden" name="syb_id" value="{{.SybID}}">
|
<input type="hidden" name="syb_id" value="{{.SybID}}">
|
||||||
<input type="hidden" name="order_no" value="{{$.Keyword}}">
|
<input type="hidden" name="order_no" value="{{$.Keyword}}">
|
||||||
@@ -47,7 +47,17 @@
|
|||||||
<input type="hidden" name="stage" value="{{$.StageFilter}}">
|
<input type="hidden" name="stage" value="{{$.StageFilter}}">
|
||||||
<input type="hidden" name="page" value="{{$.CurrentPage}}">
|
<input type="hidden" name="page" value="{{$.CurrentPage}}">
|
||||||
{{if .CanCollect}}
|
{{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}}
|
{{else}}
|
||||||
<span class="hint">{{if eq .CollectStatus "collecting"}}采集任务正在执行,请稍后刷新。{{else}}PDD 数据已采集,下一步匹配采购规格。{{end}}</span>
|
<span class="hint">{{if eq .CollectStatus "collecting"}}采集任务正在执行,请稍后刷新。{{else}}PDD 数据已采集,下一步匹配采购规格。{{end}}</span>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|||||||
@@ -561,6 +561,12 @@ PDD 采集中(超时)行可用于
|
|||||||
标准商品链接并按 `goods_id` 复用已有档案;换成其他商品需要明确确认。待采集或采集失败时显示
|
标准商品链接并按 `goods_id` 复用已有档案;换成其他商品需要明确确认。待采集或采集失败时显示
|
||||||
“创建采集任务”或“重新创建采集任务”,采集中和已采集状态只显示说明。
|
“创建采集任务”或“重新创建采集任务”,采集中和已采集状态只显示说明。
|
||||||
|
|
||||||
|
单条创建和重新创建采集任务时,按钮上方显示有可见标签的“执行客户端”
|
||||||
|
下拉框,只列出当前账号可见的 Client,并显示名称、Client ID 和在线状态。
|
||||||
|
默认为“不指定(任意客户端可领取)”;指定后只等待该 Client 领取。持续
|
||||||
|
帮助文字必须说清两种选择的后果,不依赖 placeholder 或颜色。该入口与列表
|
||||||
|
顶部批量创建共用同一套服务端阶段、客户端可见范围和重复任务校验。
|
||||||
|
|
||||||
```text
|
```text
|
||||||
┌──────────────────────────────────────────────────┐
|
┌──────────────────────────────────────────────────┐
|
||||||
│ 规格匹配 · 订单 SO-20260806-001 │
|
│ 规格匹配 · 订单 SO-20260806-001 │
|
||||||
|
|||||||
Reference in New Issue
Block a user