feat: PDD 批量采集支持可选客户端 (#74)
This commit is contained in:
+27
-3
@@ -558,7 +558,7 @@ func (r CollectTaskResult) Skipped() int {
|
||||
return r.SkippedCollecting + r.SkippedCollected + r.SkippedDeleted
|
||||
}
|
||||
|
||||
// CreatePddCollectTasks 为勾选的 PDD 商品创建采集任务。
|
||||
// CreatePddCollectTasks 为勾选的 PDD 商品创建不指定客户端的采集任务。
|
||||
//
|
||||
// `[必须]` **不指定客户端**(assigned_client 为 NULL,status 为 pending),
|
||||
// 谁领到就在领取时标记谁,见 #17。采集是纯读取操作,哪台机器跑都一样,
|
||||
@@ -576,6 +576,20 @@ func (r CollectTaskResult) Skipped() int {
|
||||
// 返回的跳过分类必须显示给操作员。静默跳过的话,
|
||||
// 操作员会以为任务建好了,等半天没动静也不知道为什么。
|
||||
func CreatePddCollectTasks(db *sql.DB, goodsIDs []string) (CollectTaskResult, error) {
|
||||
return createPddCollectTasks(db, goodsIDs, "", "")
|
||||
}
|
||||
|
||||
// CreatePddCollectTasksForUser 为 PDD 批量页面创建可选客户端的采集任务。
|
||||
// 即使浏览器伪造 clientID,也必须重新按当前登录账号校验可见范围。
|
||||
func CreatePddCollectTasksForUser(db *sql.DB, actor *model.User, goodsIDs []string, clientID string) (CollectTaskResult, error) {
|
||||
visibleUserID, err := visibleClientUserID(actor)
|
||||
if err != nil {
|
||||
return CollectTaskResult{}, err
|
||||
}
|
||||
return createPddCollectTasks(db, goodsIDs, strings.TrimSpace(clientID), visibleUserID)
|
||||
}
|
||||
|
||||
func createPddCollectTasks(db *sql.DB, goodsIDs []string, clientID, visibleUserID string) (CollectTaskResult, error) {
|
||||
var result CollectTaskResult
|
||||
|
||||
goodsIDs = dedupe(goodsIDs)
|
||||
@@ -589,6 +603,16 @@ func CreatePddCollectTasks(db *sql.DB, goodsIDs []string) (CollectTaskResult, er
|
||||
}
|
||||
defer tx.Rollback() // 已提交的事务再 Rollback 是空操作,安全
|
||||
|
||||
if clientID != "" {
|
||||
visible, err := repository.ClientVisibleToUser(tx, clientID, visibleUserID)
|
||||
if err != nil {
|
||||
return CollectTaskResult{}, err
|
||||
}
|
||||
if !visible {
|
||||
return CollectTaskResult{}, invalidInput("所选客户端不存在或不在当前账号可见范围")
|
||||
}
|
||||
}
|
||||
|
||||
// 跳过原因里"正在采集"的那些,各自还要等多久才超时可重试;
|
||||
// 取其中最快的一个,给操作员一个"下一步该等多久"的具体数字。
|
||||
var (
|
||||
@@ -632,8 +656,8 @@ func CreatePddCollectTasks(db *sql.DB, goodsIDs []string) (CollectTaskResult, er
|
||||
continue
|
||||
}
|
||||
|
||||
if err := repository.InsertCollectTask(
|
||||
tx, newCollectTaskID(), p.GoodsID, p.URL); err != nil {
|
||||
if err := repository.InsertCollectTaskForClient(
|
||||
tx, newCollectTaskID(), p.GoodsID, p.URL, clientID); err != nil {
|
||||
return CollectTaskResult{}, err
|
||||
}
|
||||
result.Created++
|
||||
|
||||
@@ -597,8 +597,8 @@ func collectTaskOf(t *testing.T, db *sql.DB, goodsID string) (status, assigned,
|
||||
return status, a.String, url, true
|
||||
}
|
||||
|
||||
// `[必须]` 采集任务不指定客户端。采集是纯读取,哪台机器跑都一样,
|
||||
// 指定了反而会在那台机器关着的时候干等。
|
||||
// `[必须]` 默认不指定客户端。常规采集哪台机器跑都一样,
|
||||
// 保持无主待领可以避免某台机器关着时一直等待。
|
||||
func TestCreatePddCollectTasks_不指定客户端(t *testing.T) {
|
||||
db := newTestDB(t)
|
||||
createProduct(t, db, "737116531267")
|
||||
@@ -651,6 +651,75 @@ func TestCreatePddCollectTasks_建出的任务能被任意客户端领走(t *tes
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreatePddCollectTasksForUser_指定客户端时只等待该客户端领取(t *testing.T) {
|
||||
db := newTestDB(t)
|
||||
admin := prepareAdminUser(t, db, time.Date(2026, 8, 10, 1, 0, 0, 0, time.UTC))
|
||||
RegisterClient(db, model.Client{ClientID: "client-chosen", Name: "指定机器"}, true)
|
||||
RegisterClient(db, model.Client{ClientID: "client-other", Name: "其他机器"}, true)
|
||||
createProduct(t, db, "737116531267")
|
||||
|
||||
result, err := CreatePddCollectTasksForUser(db, admin, []string{"737116531267"}, "client-chosen")
|
||||
if err != nil || result.Created != 1 {
|
||||
t.Fatalf("指定客户端建任务失败: result=%+v err=%v", result, err)
|
||||
}
|
||||
status, assigned, _, ok := collectTaskOf(t, db, "737116531267")
|
||||
if !ok || status != "assigned" || assigned != "client-chosen" {
|
||||
t.Fatalf("任务初始状态或客户端不对: status=%q assigned=%q ok=%t", status, assigned, ok)
|
||||
}
|
||||
otherTask, err := ClaimNextTask(db, "client-other", []string{"collect"})
|
||||
if err != nil || otherTask != nil {
|
||||
t.Fatalf("其他客户端不应领到指定任务: task=%+v err=%v", otherTask, err)
|
||||
}
|
||||
chosenTask, err := ClaimNextTask(db, "client-chosen", []string{"collect"})
|
||||
if err != nil || chosenTask == nil {
|
||||
t.Fatalf("指定客户端应该能领取任务: task=%+v err=%v", chosenTask, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreatePddCollectTasksForUser_采购员不能指定别人客户端且整批回滚(t *testing.T) {
|
||||
db := newTestDB(t)
|
||||
admin, buyerA, buyerB := prepareClientAssignmentUsers(t, db)
|
||||
RegisterClient(db, model.Client{ClientID: "client-b", Name: "B 的机器"}, true)
|
||||
if _, _, err := AssignClient(db, admin, "client-b", buyerB.UserID,
|
||||
time.Date(2026, 8, 10, 1, 0, 0, 0, time.UTC)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
createProduct(t, db, "737116531267")
|
||||
createProduct(t, db, "937122477375")
|
||||
|
||||
_, err := CreatePddCollectTasksForUser(db, buyerA,
|
||||
[]string{"737116531267", "937122477375"}, "client-b")
|
||||
if !IsValidationError(err) {
|
||||
t.Fatalf("不可见客户端应返回表单错误,实际 %v", err)
|
||||
}
|
||||
var taskCount int
|
||||
if err := db.QueryRow(`SELECT COUNT(*) FROM tasks WHERE task_type = 'collect'`).Scan(&taskCount); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if taskCount != 0 {
|
||||
t.Fatalf("拒绝后不应产生部分任务,实际 %d", taskCount)
|
||||
}
|
||||
for _, goodsID := range []string{"737116531267", "937122477375"} {
|
||||
product, _ := repository.GetPddProductByGoodsID(db, goodsID)
|
||||
if product.CollectStatus != model.CollectPending {
|
||||
t.Errorf("拒绝后商品 %s 状态不应改变,实际 %s", goodsID, product.CollectStatus)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreatePddCollectTasksForUser_不指定时保持无主待领(t *testing.T) {
|
||||
db := newTestDB(t)
|
||||
admin := prepareAdminUser(t, db, time.Date(2026, 8, 10, 1, 0, 0, 0, time.UTC))
|
||||
createProduct(t, db, "737116531267")
|
||||
if _, err := CreatePddCollectTasksForUser(db, admin, []string{"737116531267"}, ""); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
status, assigned, _, _ := collectTaskOf(t, db, "737116531267")
|
||||
if status != "pending" || assigned != "" {
|
||||
t.Errorf("默认应保持无主待领,status=%q assigned=%q", status, assigned)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreatePddCollectTasks_按商品去重(t *testing.T) {
|
||||
db := newTestDB(t)
|
||||
createProduct(t, db, "737116531267")
|
||||
|
||||
Reference in New Issue
Block a user