feat: 档口入库码支持软删除与重导恢复 (#238)
This commit is contained in:
@@ -2,6 +2,7 @@ package repository
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
@@ -1117,6 +1118,117 @@ func TestMySQLMigrate_V22升级V23且唯一约束生效(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestMySQLMigrate_V23升级V24且软删除字段有效(t *testing.T) {
|
||||
db := openMySQLMigrationTestDB(t)
|
||||
defer db.Close()
|
||||
cleanMySQLTestSchema(t, db)
|
||||
defer cleanMySQLTestSchema(t, db)
|
||||
if err := MigrateMySQL(db); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
mustExec(t, db, `ALTER TABLE syb_inner_code_records DROP FOREIGN KEY fk_inner_code_deleted_by`)
|
||||
mustExec(t, db, `ALTER TABLE syb_inner_code_records DROP INDEX idx_inner_code_deleted`)
|
||||
mustExec(t, db, `ALTER TABLE syb_inner_code_records DROP COLUMN deleted_by_user_id,DROP COLUMN deleted_at`)
|
||||
mustExec(t, db, `DELETE FROM schema_migrations WHERE version=24`)
|
||||
if err := MigrateMySQL(db); err != nil {
|
||||
t.Fatalf("v23 升级 v24 失败: %v", err)
|
||||
}
|
||||
if err := MigrateMySQL(db); err != nil {
|
||||
t.Fatalf("v24 重复迁移失败: %v", err)
|
||||
}
|
||||
if err := checkMySQLV24Shape(db); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpsertInnerCodeImportRow_软删除记录按状态安全恢复(t *testing.T) {
|
||||
db := openMySQLMigrationTestDB(t)
|
||||
defer db.Close()
|
||||
cleanMySQLTestSchema(t, db)
|
||||
defer cleanMySQLTestSchema(t, db)
|
||||
if err := MigrateMySQL(db); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
now := "2026-08-15T03:00:00Z"
|
||||
mustExec(t, db, `INSERT INTO users(user_id,username,password_hash,role,status,password_changed_at,created_at,updated_at)
|
||||
VALUES('restore-user','restore-user','hash','purchaser','active',?,?,?)`, now, now, now)
|
||||
for _, row := range []struct {
|
||||
orderNumber, stall, specKey, innerCode, status string
|
||||
}{
|
||||
{"ORDER-F", "A#1", "黑色,M", "DK-F", "failed"},
|
||||
{"ORDER-U", "B#2", "白色,L", "DK-U", "updated"},
|
||||
{"ORDER-N", "C#3", "杏色,S", "DK-N", "needs_check"},
|
||||
} {
|
||||
mustExec(t, db, `INSERT INTO syb_inner_code_records
|
||||
(business_date,source_row,order_number,stall,spec_raw,spec_key,inner_code,stock_id,status,
|
||||
result_message,created_by_user_id,deleted_at,deleted_by_user_id,created_at,updated_at)
|
||||
VALUES('2026-08-15',2,?,?,?,?,?,99,?,'旧审计','restore-user',?,'restore-user',?,?)`,
|
||||
row.orderNumber, row.stall, row.specKey, row.specKey, row.innerCode, row.status, now, now, now)
|
||||
}
|
||||
|
||||
failedRow := model.InnerCodeImportRow{BusinessDate: "2026-08-15", SourceRow: 8, OrderNumber: "ORDER-F",
|
||||
Stall: "A#1", SpecRaw: "黑色,M", SpecKey: "黑色,M", InnerCode: "DK-F-NEW",
|
||||
SourceDuplicateCount: 1, CreatedByUserID: "restore-user"}
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
outcome, err := UpsertInnerCodeImportRow(tx, failedRow, "2026-08-15T04:00:00Z")
|
||||
if err != nil || outcome != InnerCodeImportRestored {
|
||||
tx.Rollback()
|
||||
t.Fatalf("失败记录恢复不正确 outcome=%s err=%v", outcome, err)
|
||||
}
|
||||
if err := tx.Commit(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var status, innerCode string
|
||||
var stockID sql.NullInt64
|
||||
var deletedAt, message sql.NullString
|
||||
if err := db.QueryRow(`SELECT status,inner_code,stock_id,deleted_at,result_message FROM syb_inner_code_records WHERE order_number='ORDER-F'`).
|
||||
Scan(&status, &innerCode, &stockID, &deletedAt, &message); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if status != "pending" || innerCode != "DK-F-NEW" || stockID.Valid || deletedAt.Valid || message.Valid {
|
||||
t.Fatalf("未完成记录恢复后未重置: status=%s code=%s stock=%+v deleted=%+v message=%+v", status, innerCode, stockID, deletedAt, message)
|
||||
}
|
||||
|
||||
updatedRow := model.InnerCodeImportRow{BusinessDate: "2026-08-15", SourceRow: 9, OrderNumber: "ORDER-U",
|
||||
Stall: "B#2", SpecRaw: "白色,L", SpecKey: "白色,L", InnerCode: "DK-U",
|
||||
SourceDuplicateCount: 1, CreatedByUserID: "restore-user"}
|
||||
tx, err = db.Begin()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
outcome, err = UpsertInnerCodeImportRow(tx, updatedRow, "2026-08-15T04:00:00Z")
|
||||
if err != nil || outcome != InnerCodeImportRestored {
|
||||
tx.Rollback()
|
||||
t.Fatalf("已完成记录恢复不正确 outcome=%s err=%v", outcome, err)
|
||||
}
|
||||
if err := tx.Commit(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := db.QueryRow(`SELECT status,stock_id,deleted_at,result_message FROM syb_inner_code_records WHERE order_number='ORDER-U'`).
|
||||
Scan(&status, &stockID, &deletedAt, &message); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if status != "updated" || !stockID.Valid || deletedAt.Valid || !message.Valid || message.String != "旧审计" {
|
||||
t.Fatalf("已完成记录恢复后未保留审计: status=%s stock=%+v deleted=%+v message=%+v", status, stockID, deletedAt, message)
|
||||
}
|
||||
|
||||
conflictRow := model.InnerCodeImportRow{BusinessDate: "2026-08-15", SourceRow: 10, OrderNumber: "ORDER-N",
|
||||
Stall: "C#3", SpecRaw: "杏色,S", SpecKey: "杏色,S", InnerCode: "DK-N-CHANGED",
|
||||
SourceDuplicateCount: 1, CreatedByUserID: "restore-user"}
|
||||
tx, err = db.Begin()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, err = UpsertInnerCodeImportRow(tx, conflictRow, "2026-08-15T04:00:00Z")
|
||||
tx.Rollback()
|
||||
if !errors.Is(err, ErrInnerCodeRestoreConflict) {
|
||||
t.Fatalf("需核对记录换入库码应拒绝,实际 %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func mustExec(t *testing.T, db *sql.DB, query string, args ...any) {
|
||||
t.Helper()
|
||||
if _, err := db.Exec(query, args...); err != nil {
|
||||
|
||||
Reference in New Issue
Block a user