feat: 档口入库码支持软删除与重导恢复 (#238)
This commit is contained in:
@@ -20,7 +20,7 @@ import (
|
||||
"cmautobuy/admin/spec"
|
||||
)
|
||||
|
||||
const mysqlSchemaVersion = 23
|
||||
const mysqlSchemaVersion = 24
|
||||
|
||||
// OpenMySQL 打开生产 MySQL 8 数据库。错误信息绝不包含完整 DSN 或密码。
|
||||
func OpenMySQL(cfg config.DatabaseConfig) (*sql.DB, error) {
|
||||
@@ -700,10 +700,56 @@ func MigrateMySQL(db *sql.DB) error {
|
||||
if _, err := db.Exec(`INSERT INTO schema_migrations (version, applied_at) VALUES (?, ?)`, 23, time.Now().UTC().Format(time.RFC3339Nano)); err != nil {
|
||||
return fmt.Errorf("记录 MySQL schema v23 失败: %w", err)
|
||||
}
|
||||
current = 23
|
||||
}
|
||||
if current < 24 {
|
||||
if err := migrateMySQLV24(db); err != nil {
|
||||
return fmt.Errorf("执行 MySQL schema v24 失败: %w", err)
|
||||
}
|
||||
if err := checkMySQLV24Shape(db); err != nil {
|
||||
return fmt.Errorf("MySQL schema v24 自检失败,未记录版本: %w", err)
|
||||
}
|
||||
if _, err := db.Exec(`INSERT INTO schema_migrations (version, applied_at) VALUES (?, ?)`, 24, time.Now().UTC().Format(time.RFC3339Nano)); err != nil {
|
||||
return fmt.Errorf("记录 MySQL schema v24 失败: %w", err)
|
||||
}
|
||||
}
|
||||
return CheckMySQLSchema(db)
|
||||
}
|
||||
|
||||
// migrateMySQLV24 为档口入库码增加可恢复软删除审计。
|
||||
func migrateMySQLV24(db *sql.DB) error {
|
||||
for _, column := range []struct{ name, ddl string }{
|
||||
{"deleted_at", `ALTER TABLE syb_inner_code_records ADD COLUMN deleted_at VARCHAR(35) NULL AFTER applied_at`},
|
||||
{"deleted_by_user_id", `ALTER TABLE syb_inner_code_records ADD COLUMN deleted_by_user_id VARCHAR(191) COLLATE utf8mb4_bin NULL AFTER deleted_at`},
|
||||
} {
|
||||
exists, err := mysqlColumnExists(db, "syb_inner_code_records", column.name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !exists {
|
||||
if _, err := db.Exec(column.ddl); err != nil {
|
||||
return fmt.Errorf("增加 syb_inner_code_records.%s 失败: %w", column.name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
if exists, err := mysqlIndexExists(db, "syb_inner_code_records", "idx_inner_code_deleted"); err != nil {
|
||||
return err
|
||||
} else if !exists {
|
||||
if _, err := db.Exec(`ALTER TABLE syb_inner_code_records ADD INDEX idx_inner_code_deleted (deleted_at,business_date,status,id)`); err != nil {
|
||||
return fmt.Errorf("增加档口入库码软删除索引失败: %w", err)
|
||||
}
|
||||
}
|
||||
if exists, err := mysqlConstraintExists(db, "syb_inner_code_records", "fk_inner_code_deleted_by"); err != nil {
|
||||
return err
|
||||
} else if !exists {
|
||||
if _, err := db.Exec(`ALTER TABLE syb_inner_code_records ADD CONSTRAINT fk_inner_code_deleted_by
|
||||
FOREIGN KEY (deleted_by_user_id) REFERENCES users(user_id) ON DELETE SET NULL`); err != nil {
|
||||
return fmt.Errorf("增加档口入库码删除人外键失败: %w", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// migrateMySQLV23 建立档口入库码最小闭环使用的唯一业务表。
|
||||
// Excel 原文件、批次和每次尝试不会另建表保存。
|
||||
func migrateMySQLV23(db *sql.DB) error {
|
||||
@@ -2132,7 +2178,35 @@ func CheckMySQLSchema(db *sql.DB) error {
|
||||
if err := checkMySQLV22Shape(db); err != nil {
|
||||
return err
|
||||
}
|
||||
return checkMySQLV23Shape(db)
|
||||
if err := checkMySQLV23Shape(db); err != nil {
|
||||
return err
|
||||
}
|
||||
return checkMySQLV24Shape(db)
|
||||
}
|
||||
|
||||
func checkMySQLV24Shape(db *sql.DB) error {
|
||||
for _, name := range []string{"deleted_at", "deleted_by_user_id"} {
|
||||
exists, err := mysqlColumnExists(db, "syb_inner_code_records", name)
|
||||
if err != nil || !exists {
|
||||
return fmt.Errorf("档口入库码软删除字段 %s 缺失: %v", name, err)
|
||||
}
|
||||
}
|
||||
for _, item := range []struct{ kind, name string }{
|
||||
{"index", "idx_inner_code_deleted"},
|
||||
{"constraint", "fk_inner_code_deleted_by"},
|
||||
} {
|
||||
var exists bool
|
||||
var err error
|
||||
if item.kind == "index" {
|
||||
exists, err = mysqlIndexExists(db, "syb_inner_code_records", item.name)
|
||||
} else {
|
||||
exists, err = mysqlConstraintExists(db, "syb_inner_code_records", item.name)
|
||||
}
|
||||
if err != nil || !exists {
|
||||
return fmt.Errorf("档口入库码软删除%s %s 缺失: %v", item.kind, item.name, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkMySQLV23Shape(db *sql.DB) error {
|
||||
|
||||
Reference in New Issue
Block a user