132 lines
4.4 KiB
Go
132 lines
4.4 KiB
Go
package service
|
||
|
||
import (
|
||
"database/sql"
|
||
"errors"
|
||
"fmt"
|
||
"strings"
|
||
|
||
"cmautobuy/admin/repository"
|
||
)
|
||
|
||
// ErrPddAssociationReplaceRequired 表示当前蝦皮商品已经关联了另一个 PDD 商品。
|
||
var ErrPddAssociationReplaceRequired = errors.New("更换 PDD 商品需要明确确认")
|
||
|
||
const canonicalPddGoodsURL = "https://mobile.yangkeduo.com/goods.html?goods_id="
|
||
|
||
// normalizeSybPddReference 只在顺运宝详情入口兼容纯商品 ID。
|
||
// 其他入口仍沿用完整 PDD 链接校验,避免无意扩大已经确认的输入范围。
|
||
func normalizeSybPddReference(raw string) (string, error) {
|
||
value := strings.TrimSpace(raw)
|
||
if value == "" {
|
||
return "", fmt.Errorf("%w: PDD 商品链接或商品 ID 不能为空", ErrBadPddURL)
|
||
}
|
||
if isGoodsID(value) {
|
||
return canonicalPddGoodsURL + value, nil
|
||
}
|
||
if isASCIIDigits(value) {
|
||
return "", fmt.Errorf("%w: PDD 商品 ID 必须是 6~24 位纯数字", ErrBadPddURL)
|
||
}
|
||
if !strings.Contains(value, "://") {
|
||
return "", fmt.Errorf("%w: 请输入带 goods_id 的完整拼多多链接,或 6~24 位纯数字商品 ID", ErrBadPddURL)
|
||
}
|
||
return value, nil
|
||
}
|
||
|
||
func isASCIIDigits(value string) bool {
|
||
for _, char := range value {
|
||
if char < '0' || char > '9' {
|
||
return false
|
||
}
|
||
}
|
||
return true
|
||
}
|
||
|
||
// AssociateShopeePdd 把蝦皮商品关联到 PDD 商品档案。
|
||
// 解析、建档/复活和关联更新在同一事务内完成,任一步失败都不留下半成品。
|
||
func AssociateShopeePdd(db *sql.DB, shopeeGoodsID, rawURL string, confirmReplace bool) (string, error) {
|
||
shopeeGoodsID = strings.TrimSpace(shopeeGoodsID)
|
||
rawURL = strings.TrimSpace(rawURL)
|
||
pddGoodsID, err := ParsePddGoodsID(rawURL)
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
|
||
tx, err := db.Begin()
|
||
if err != nil {
|
||
return "", fmt.Errorf("开始关联 PDD 商品事务失败: %w", err)
|
||
}
|
||
defer tx.Rollback()
|
||
|
||
product, err := repository.GetShopeeProductByGoodsID(tx, shopeeGoodsID)
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
if product == nil {
|
||
return "", fmt.Errorf("蝦皮商品 %s 不存在", shopeeGoodsID)
|
||
}
|
||
if product.PddGoodsID != "" && product.PddGoodsID != pddGoodsID && !confirmReplace {
|
||
return "", fmt.Errorf("%w:当前是 %s,新链接解析为 %s",
|
||
ErrPddAssociationReplaceRequired, product.PddGoodsID, pddGoodsID)
|
||
}
|
||
if _, err := repository.EnsurePddProduct(tx, pddGoodsID, rawURL); err != nil {
|
||
return "", err
|
||
}
|
||
if err := repository.UpdateShopeePddLink(tx, shopeeGoodsID, pddGoodsID, rawURL); err != nil {
|
||
return "", err
|
||
}
|
||
if err := tx.Commit(); err != nil {
|
||
return "", fmt.Errorf("提交 PDD 商品关联失败: %w", err)
|
||
}
|
||
return pddGoodsID, nil
|
||
}
|
||
|
||
// CreateShopeePddCollectTask 按蝦皮商品当前关联创建采集任务,避免相信表单里的 PDD ID。
|
||
func CreateShopeePddCollectTask(db *sql.DB, shopeeGoodsID string) (CollectTaskResult, error) {
|
||
product, err := repository.GetShopeeProductByGoodsID(db, strings.TrimSpace(shopeeGoodsID))
|
||
if err != nil {
|
||
return CollectTaskResult{}, err
|
||
}
|
||
if product == nil {
|
||
return CollectTaskResult{}, fmt.Errorf("蝦皮商品不存在")
|
||
}
|
||
if product.PddGoodsID == "" {
|
||
return CollectTaskResult{}, fmt.Errorf("请先关联 PDD 商品")
|
||
}
|
||
return CreatePddCollectTasks(db, []string{product.PddGoodsID})
|
||
}
|
||
|
||
// AssociateSybPdd 从顺运宝明细解析出可信的蝦皮商品,再复用统一关联逻辑。
|
||
func AssociateSybPdd(db *sql.DB, sybID, rawURL string, confirmReplace bool) (string, error) {
|
||
context, err := repository.GetSybOrderContext(db, strings.TrimSpace(sybID))
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
if context == nil {
|
||
return "", fmt.Errorf("顺运宝明细不存在")
|
||
}
|
||
if context.Order.ShopeeGoodsID == "" {
|
||
return "", fmt.Errorf("顺运宝未提供蝦皮商品 ID")
|
||
}
|
||
if context.Order.SpecKey == "" {
|
||
return "", fmt.Errorf("顺运宝未提供规格,不能进入采购流程")
|
||
}
|
||
normalizedURL, err := normalizeSybPddReference(rawURL)
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
return AssociateShopeePdd(db, context.Order.ShopeeGoodsID, normalizedURL, confirmReplace)
|
||
}
|
||
|
||
// CreateSybPddCollectTask 按顺运宝明细当前关联创建采集任务。
|
||
func CreateSybPddCollectTask(db *sql.DB, sybID string) (CollectTaskResult, error) {
|
||
context, err := repository.GetSybOrderContext(db, strings.TrimSpace(sybID))
|
||
if err != nil {
|
||
return CollectTaskResult{}, err
|
||
}
|
||
if context == nil {
|
||
return CollectTaskResult{}, fmt.Errorf("顺运宝明细不存在")
|
||
}
|
||
return CreateShopeePddCollectTask(db, context.Order.ShopeeGoodsID)
|
||
}
|