fix: 档口入库码按货运单号直查顺运宝 (#245)
This commit is contained in:
@@ -16,11 +16,18 @@ import (
|
||||
"cmautobuy/admin/syb"
|
||||
)
|
||||
|
||||
// InnerCodeDetailReader 只开放规划所需的顺运宝只读调用,方便无网络测试。
|
||||
// InnerCodeDetailReader 只开放读取已知货运单详情的能力,供规划和回写前核验复用。
|
||||
type InnerCodeDetailReader interface {
|
||||
DetailListByStock(context.Context, []int64) ([]syb.StockDetail, error)
|
||||
}
|
||||
|
||||
// InnerCodePlanReader 是规划阶段需要的顺运宝只读能力。
|
||||
// 货运单定位必须直接查询远端,不能依赖是否已同步进本地 syb_orders。
|
||||
type InnerCodePlanReader interface {
|
||||
InnerCodeDetailReader
|
||||
ListByOrderNumber(context.Context, string) ([]syb.StockRow, error)
|
||||
}
|
||||
|
||||
const innerCodeReadBatchSize = 100
|
||||
|
||||
// InnerCodePlanResult 是一次规划的逐状态统计。
|
||||
@@ -34,7 +41,7 @@ type InnerCodePlanResult struct {
|
||||
|
||||
// PlanInnerCodeRecords 对某个业务日期选中的可重规划记录读取最新远端详情并原子保存计划。
|
||||
// 本函数绝不调用删除或更新顺运宝接口。
|
||||
func PlanInnerCodeRecords(ctx context.Context, db *sql.DB, reader InnerCodeDetailReader, businessDate string, selectedIDs []int64) (*InnerCodePlanResult, error) {
|
||||
func PlanInnerCodeRecords(ctx context.Context, db *sql.DB, reader InnerCodePlanReader, businessDate string, selectedIDs []int64) (*InnerCodePlanResult, error) {
|
||||
if _, err := time.Parse("2006-01-02", businessDate); err != nil {
|
||||
return nil, fmt.Errorf("业务日期格式应为 YYYY-MM-DD")
|
||||
}
|
||||
@@ -61,11 +68,10 @@ func PlanInnerCodeRecords(ctx context.Context, db *sql.DB, reader InnerCodeDetai
|
||||
selectedSet[record.ID] = true
|
||||
}
|
||||
reservedDetails := innerCodeReservedDetails(contextRecords, selectedSet)
|
||||
snapshots, err := repository.ListInnerCodeSybSnapshots(db, orders)
|
||||
stockIDsByOrder, err := readInnerCodeStockIDs(ctx, reader, orders)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stockIDsByOrder := innerCodeStockIDsByOrder(snapshots)
|
||||
requested := uniqueInnerCodeStockIDs(records, stockIDsByOrder)
|
||||
detailsByID, err := readInnerCodeDetails(ctx, reader, requested)
|
||||
if err != nil {
|
||||
@@ -131,38 +137,34 @@ func uniqueInnerCodeOrders(records []model.InnerCodeRecord) []string {
|
||||
return result
|
||||
}
|
||||
|
||||
func innerCodeStockIDsByOrder(snapshots []repository.InnerCodeSybSnapshot) map[string][]int64 {
|
||||
sets := make(map[string]map[int64]bool)
|
||||
for _, snapshot := range snapshots {
|
||||
stockID, ok := innerCodeStockIDFromJSON(snapshot.SybData)
|
||||
if !ok || stockID <= 0 {
|
||||
continue
|
||||
// readInnerCodeStockIDs 对勾选记录的货运单号逐个直查顺运宝。
|
||||
// 零命中和多命中交给逐行规划生成明确状态;网络或协议错误会中止整批,
|
||||
// 避免保存一半新计划、一半旧计划。
|
||||
func readInnerCodeStockIDs(ctx context.Context, reader InnerCodePlanReader, orders []string) (map[string][]int64, error) {
|
||||
result := make(map[string][]int64, len(orders))
|
||||
for _, order := range orders {
|
||||
rows, err := reader.ListByOrderNumber(ctx, order)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("查询顺运宝货运单 %q 失败,本次规划未保存: %w", order, err)
|
||||
}
|
||||
if sets[snapshot.OrderNumber] == nil {
|
||||
sets[snapshot.OrderNumber] = make(map[int64]bool)
|
||||
ids := make([]int64, 0, len(rows))
|
||||
seen := make(map[int64]bool, len(rows))
|
||||
for _, row := range rows {
|
||||
if row.Code != order {
|
||||
return nil, fmt.Errorf("查询顺运宝货运单 %q 返回不一致的 code %q,本次规划未保存", order, row.Code)
|
||||
}
|
||||
if row.ID <= 0 {
|
||||
return nil, fmt.Errorf("查询顺运宝货运单 %q 返回非法 id=%d,本次规划未保存", order, row.ID)
|
||||
}
|
||||
if !seen[row.ID] {
|
||||
seen[row.ID] = true
|
||||
ids = append(ids, row.ID)
|
||||
}
|
||||
}
|
||||
sets[snapshot.OrderNumber][stockID] = true
|
||||
sort.Slice(ids, func(i, j int) bool { return ids[i] < ids[j] })
|
||||
result[order] = ids
|
||||
}
|
||||
result := make(map[string][]int64, len(sets))
|
||||
for order, set := range sets {
|
||||
for id := range set {
|
||||
result[order] = append(result[order], id)
|
||||
}
|
||||
sort.Slice(result[order], func(i, j int) bool { return result[order][i] < result[order][j] })
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func innerCodeStockIDFromJSON(raw string) (int64, bool) {
|
||||
var envelope struct {
|
||||
Stock map[string]any `json:"stock"`
|
||||
}
|
||||
decoder := json.NewDecoder(strings.NewReader(raw))
|
||||
decoder.UseNumber()
|
||||
if err := decoder.Decode(&envelope); err != nil {
|
||||
return 0, false
|
||||
}
|
||||
return innerCodeInt64(envelope.Stock["id"])
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func uniqueInnerCodeStockIDs(records []model.InnerCodeRecord, byOrder map[string][]int64) []int64 {
|
||||
@@ -229,7 +231,7 @@ func planInnerCodeRowsWithReserved(records []model.InnerCodeRecord, stockIDsByOr
|
||||
stockIDs := stockIDsByOrder[record.OrderNumber]
|
||||
if len(stockIDs) == 0 {
|
||||
plan.Status = model.InnerCodeFailed
|
||||
plan.ResultMessage = "本地未找到对应的顺运宝货运单"
|
||||
plan.ResultMessage = "顺运宝未找到货运单"
|
||||
plans = append(plans, plan)
|
||||
continue
|
||||
}
|
||||
@@ -384,18 +386,3 @@ func innerCodeRawText(value any) string {
|
||||
return strings.TrimSpace(fmt.Sprint(typed))
|
||||
}
|
||||
}
|
||||
|
||||
func innerCodeInt64(value any) (int64, bool) {
|
||||
switch typed := value.(type) {
|
||||
case json.Number:
|
||||
result, err := typed.Int64()
|
||||
return result, err == nil
|
||||
case float64:
|
||||
return int64(typed), true
|
||||
case string:
|
||||
result, err := strconv.ParseInt(strings.TrimSpace(typed), 10, 64)
|
||||
return result, err == nil
|
||||
default:
|
||||
return 0, false
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user