fix: 合并多件档口入库码回写 (#240)
This commit is contained in:
@@ -21,6 +21,7 @@ import (
|
||||
const (
|
||||
MaxInnerCodeUploadBytes = 10 * 1024 * 1024
|
||||
MaxInnerCodeImportRows = 5000
|
||||
MaxInnerCodeValueRunes = 128
|
||||
innerCodeSheetName = "标签入库码映射"
|
||||
)
|
||||
|
||||
@@ -46,7 +47,7 @@ type InnerCodeImportResult struct {
|
||||
CreatedCount int
|
||||
UpdatedCount int
|
||||
RestoredCount int
|
||||
DuplicateRows int
|
||||
MergedRows int
|
||||
}
|
||||
|
||||
// IsInvalidInnerCodeImport 判断错误是否需要用户修正上传文件。
|
||||
@@ -123,6 +124,13 @@ func ImportInnerCodeExcel(db *sql.DB, path, originalFilename, actorUserID string
|
||||
return nil, fmt.Errorf("开始档口入库码导入事务失败: %w", err)
|
||||
}
|
||||
defer tx.Rollback()
|
||||
owners, err := repository.LockInnerCodeCodeOwnersByDate(tx, result.BusinessDate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := validateExistingInnerCodeOwners(result.BusinessDate, owners, rows); err != nil {
|
||||
return nil, fmt.Errorf("%w:%v", ErrInvalidInnerCodeImport, err)
|
||||
}
|
||||
now := model.NowISO()
|
||||
for _, row := range rows {
|
||||
outcome, err := repository.UpsertInnerCodeImportRow(tx, row, now)
|
||||
@@ -156,6 +164,37 @@ type innerCodeParsedRow struct {
|
||||
businessKey string
|
||||
}
|
||||
|
||||
func innerCodeBusinessKey(businessDate, orderNumber, stall, specKey string) string {
|
||||
return strings.Join([]string{businessDate, orderNumber, stall, specKey}, "\x00")
|
||||
}
|
||||
|
||||
func validateExistingInnerCodeOwners(businessDate string, owners []repository.InnerCodeCodeOwner, rows []model.InnerCodeImportRow) error {
|
||||
codeOwners := make(map[string]string)
|
||||
for _, owner := range owners {
|
||||
ownerKey := innerCodeBusinessKey(businessDate, owner.OrderNumber, owner.Stall, owner.SpecKey)
|
||||
for _, code := range strings.Split(owner.InnerCode, ",") {
|
||||
code = strings.TrimSpace(code)
|
||||
if code == "" {
|
||||
continue
|
||||
}
|
||||
if oldKey, exists := codeOwners[code]; exists && oldKey != ownerKey {
|
||||
return fmt.Errorf("数据库中同日入库码 %q 已对应多条记录,请先人工核对", code)
|
||||
}
|
||||
codeOwners[code] = ownerKey
|
||||
}
|
||||
}
|
||||
for _, row := range rows {
|
||||
rowKey := innerCodeBusinessKey(row.BusinessDate, row.OrderNumber, row.Stall, row.SpecKey)
|
||||
for _, code := range strings.Split(row.InnerCode, ",") {
|
||||
if oldKey, exists := codeOwners[code]; exists && oldKey != rowKey {
|
||||
return fmt.Errorf("Excel 第 %d 行的入库码 %q 在该业务日期已属于另一条记录", row.SourceRow, code)
|
||||
}
|
||||
codeOwners[code] = rowKey
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseInnerCodeWorkbook(book *excelize.File, originalFilename, actorUserID string) (*InnerCodeImportResult, []model.InnerCodeImportRow, error) {
|
||||
found := false
|
||||
for _, name := range book.GetSheetList() {
|
||||
@@ -242,7 +281,7 @@ func parseInnerCodeRows(rows *excelize.Rows, originalFilename, actorUserID strin
|
||||
return nil, nil, err
|
||||
}
|
||||
specKey := NormalizeInnerCodeSpecKey(specRaw)
|
||||
key := strings.Join([]string{businessDate, orderNumber, stall, specKey}, "\x00")
|
||||
key := innerCodeBusinessKey(businessDate, orderNumber, stall, specKey)
|
||||
parsed = append(parsed, innerCodeParsedRow{InnerCodeImportRow: model.InnerCodeImportRow{
|
||||
BusinessDate: businessDate, SourceRow: rowNumber, PrintSequence: printSequence,
|
||||
OrderNumber: orderNumber, ShopName: shop, Stall: stall, SpecRaw: specRaw,
|
||||
@@ -278,15 +317,23 @@ func parseInnerCodeRows(rows *excelize.Rows, originalFilename, actorUserID strin
|
||||
}
|
||||
seen[original.businessKey] = true
|
||||
group := groups[original.businessKey]
|
||||
for _, duplicate := range group[1:] {
|
||||
if duplicate.InnerCode != original.InnerCode {
|
||||
return nil, nil, fmt.Errorf("第 %d 行与第 %d 行业务键相同但入库码不同", original.SourceRow, duplicate.SourceRow)
|
||||
row := original.InnerCodeImportRow
|
||||
codes := make([]string, 0, len(group))
|
||||
seenCodes := make(map[string]bool, len(group))
|
||||
for _, member := range group {
|
||||
if !seenCodes[member.InnerCode] {
|
||||
seenCodes[member.InnerCode] = true
|
||||
codes = append(codes, member.InnerCode)
|
||||
}
|
||||
}
|
||||
row := original.InnerCodeImportRow
|
||||
row.InnerCode = strings.Join(codes, ",")
|
||||
if utf8.RuneCountInString(row.InnerCode) > MaxInnerCodeValueRunes {
|
||||
return nil, nil, fmt.Errorf("第 %d 行起的同业务键入库码连接后超过 %d 个字符(共 %d 个不同码)",
|
||||
row.SourceRow, MaxInnerCodeValueRunes, len(codes))
|
||||
}
|
||||
row.SourceDuplicateCount = len(group)
|
||||
unique = append(unique, row)
|
||||
result.DuplicateRows += len(group) - 1
|
||||
result.MergedRows += len(group) - 1
|
||||
}
|
||||
return result, unique, nil
|
||||
}
|
||||
@@ -377,12 +424,20 @@ func innerCodeCell(columns []string, index int) string {
|
||||
}
|
||||
|
||||
func validateInnerCodeFields(row int, order, shop, stall, spec, code string) error {
|
||||
if strings.Contains(code, ",") {
|
||||
return fmt.Errorf("第 %d 行内部档口入库码不能包含英文逗号", row)
|
||||
}
|
||||
for _, character := range code {
|
||||
if character < 32 || character == 127 {
|
||||
return fmt.Errorf("第 %d 行内部档口入库码不能包含控制字符", row)
|
||||
}
|
||||
}
|
||||
for _, field := range []struct {
|
||||
name, value string
|
||||
max int
|
||||
}{
|
||||
{"Shopee订单编号", order, 64}, {"店铺名称", shop, 191}, {"档口及货号", stall, 191},
|
||||
{"规格", spec, 500}, {"内部档口入库码", code, 128},
|
||||
{"规格", spec, 500}, {"内部档口入库码", code, MaxInnerCodeValueRunes},
|
||||
} {
|
||||
if utf8.RuneCountInString(field.value) > field.max {
|
||||
return fmt.Errorf("第 %d 行%s超过 %d 个字符", row, field.name, field.max)
|
||||
|
||||
Reference in New Issue
Block a user