feat(cli): 添加命令行敏感操作门禁和数据库管理功能
- 添加 migrate、seed、reset-all 命令行子命令 - 实现管理员密码门禁机制保护敏感操作 - 添加数据库表结构迁移和种子数据初始化功能 - 实现清空并重建全部表的 reset-all 功能 - 重构种子数据只包含系统必需基础数据,移除演示数据 - 添加 RBAC 权限角色初始化的命令行支持 - 配置命令行密码验证和操作日志记录功能
This commit is contained in:
@@ -4,14 +4,21 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"time"
|
||||
|
||||
"bj_power_wms/ent"
|
||||
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
// SeedIfEmpty 首次启动时预置基础数据(区域/账号/演示物料与库存)
|
||||
// SeedIfEmpty 首次启动/重置后预置「系统运行必需的基础主数据」,不含任何演示业务数据。
|
||||
//
|
||||
// 生产环境口径(用户裁决 2026-09-20):种子只允许写入系统跑起来所必需的固定主数据,
|
||||
// 严禁预置演示物料/演示库存/演示账号等假业务数据。具体:
|
||||
// - 区域:库房四大基础分区 Z01~Z04(库存落位的根节点,level=1);
|
||||
// - 管理员账号:admin(初始密码 123456,登录后应立即修改);其余账号由管理员在「账号管理」按需创建、分配角色;
|
||||
// - 接驳台:DOCK01~20(产线,DOCKn↔工位n)+ DOCK21(库房收货),系统预置只读主数据(dock.go 不提供增删改)。
|
||||
//
|
||||
// 权限与角色不在此:由 handler.SeedRBAC 幂等初始化(reset-all/seed 命令与 POST /api/rbac/seed 共用同一函数)。
|
||||
func SeedIfEmpty(client *ent.Client) error {
|
||||
ctx := context.Background()
|
||||
|
||||
@@ -36,106 +43,20 @@ func SeedIfEmpty(client *ent.Client) error {
|
||||
slog.Info("seed: zones created")
|
||||
}
|
||||
|
||||
// 2. 账号(全部统一初始密码 123456)
|
||||
// 2. 管理员账号(唯一预置账号;初始密码 123456,登录后应立即修改)
|
||||
users, _ := client.User.Query().Count(ctx)
|
||||
if users == 0 {
|
||||
adminHash, _ := bcrypt.GenerateFromPassword([]byte("123456"), bcrypt.DefaultCost)
|
||||
opHash, _ := bcrypt.GenerateFromPassword([]byte("123456"), bcrypt.DefaultCost)
|
||||
client.User.Create().
|
||||
SetUsername("admin").
|
||||
SetPassword(string(adminHash)).
|
||||
SetRealName("系统管理员").
|
||||
SetRole("admin").
|
||||
SaveX(ctx)
|
||||
client.User.Create().
|
||||
SetUsername("store1").
|
||||
SetPassword(string(opHash)).
|
||||
SetRealName("一号库房保管员").
|
||||
SetRole("operator").
|
||||
SetDept("库房").
|
||||
SaveX(ctx)
|
||||
client.User.Create().
|
||||
SetUsername("insp1").
|
||||
SetPassword(string(opHash)).
|
||||
SetRealName("质检员一号").
|
||||
SetRole("inspector").
|
||||
SetDept("质检").
|
||||
SaveX(ctx)
|
||||
slog.Info("seed: users created (admin/123456, store1/123456, insp1/123456)")
|
||||
slog.Info("seed: admin user created (admin/123456)")
|
||||
}
|
||||
|
||||
// 3. 演示物料与库存
|
||||
materials, _ := client.Material.Query().Count(ctx)
|
||||
if materials == 0 {
|
||||
mats := []struct {
|
||||
code, name, spec, unit string
|
||||
mode int
|
||||
isBatch, isSerial bool
|
||||
}{
|
||||
{"GJ-BAN-001", "10mm钢板(结构件)", "2000x1000x10", "张", 1, true, false},
|
||||
{"GJ-GANGGUAN-002", "无缝钢管114x8", "114x8x6000", "根", 1, true, false},
|
||||
{"LJ-LUOSI-M16", "M16高强度螺栓", "M16x80 10.9级", "件", 1, true, false},
|
||||
{"JM-ZHOUCHENG-6020", "精密轴承6020", "6020-2RS", "套", 2, false, true},
|
||||
{"JM-DIANJI-750W", "伺服电机750W", "ECMA-C21307", "台", 2, false, true},
|
||||
{"BCP-JIEKOUTI", "接驳体半成品", "-", "件", 2, false, true},
|
||||
}
|
||||
for _, m := range mats {
|
||||
client.Material.Create().
|
||||
SetCode(m.code).
|
||||
SetName(m.name).
|
||||
SetNillableSpec(strPtrNil(m.spec)).
|
||||
SetNillableUnit(strPtrNil(m.unit)).
|
||||
SetManageMode(m.mode).
|
||||
SetIsBatchManaged(m.isBatch).
|
||||
SetIsSerialManaged(m.isSerial).
|
||||
SaveX(ctx)
|
||||
}
|
||||
|
||||
// 演示库存(统一 inventory 表,manage_mode 区分结构件/电气件)
|
||||
today := time.Now().Format("2006-01-02")
|
||||
// 结构件批次(已检合格)
|
||||
client.Inventory.Create().
|
||||
SetManageMode(1).
|
||||
SetMaterialCode("GJ-BAN-001").
|
||||
SetNillableMaterialName(strPtrNil("10mm钢板(结构件)")).
|
||||
SetBatchNo("B2026060100001").
|
||||
SetQuantity(120).SetLockedQty(0).
|
||||
SetNillableProductionDate(strPtrNil("2026-06-01")).
|
||||
SetNillableSupplier(strPtrNil("宝钢供应商")).
|
||||
SetQualityStatus("合格").
|
||||
SetNillableZoneCode(strPtrNil("Z02")).
|
||||
SetStatus("在库").
|
||||
SaveX(ctx)
|
||||
client.Inventory.Create().
|
||||
SetManageMode(1).
|
||||
SetMaterialCode("GJ-GANGGUAN-002").
|
||||
SetNillableMaterialName(strPtrNil("无缝钢管114x8")).
|
||||
SetBatchNo("B2026060200034").
|
||||
SetQuantity(50).SetLockedQty(0).
|
||||
SetNillableProductionDate(strPtrNil(today)).
|
||||
SetNillableSupplier(strPtrNil("天津钢管")).
|
||||
SetQualityStatus("未检").
|
||||
SetNillableZoneCode(strPtrNil("Z01")).
|
||||
SetStatus("在库").
|
||||
SaveX(ctx)
|
||||
|
||||
// 电气件 SN(在库,合格)
|
||||
for i := 1; i <= 5; i++ {
|
||||
client.Inventory.Create().
|
||||
SetManageMode(2).
|
||||
SetMaterialCode("JM-ZHOUCHENG-6020").
|
||||
SetNillableMaterialName(strPtrNil("精密轴承6020")).
|
||||
SetSnCode("SN20260601001" + itoaPad(i)).
|
||||
SetQuantity(1).SetLockedQty(0).
|
||||
SetQualityStatus("合格").
|
||||
SetNillableZoneCode(strPtrNil("Z02")).
|
||||
SetStatus("在库").
|
||||
SaveX(ctx)
|
||||
}
|
||||
slog.Info("seed: demo materials & stock created")
|
||||
}
|
||||
|
||||
// 4. 接驳台主数据 DOCK01~20(产线, DOCKn↔工位n) + DOCK21(库房)
|
||||
// 3. 接驳台主数据 DOCK01~20(产线, DOCKn↔工位n) + DOCK21(库房)
|
||||
docks, _ := client.Dock.Query().Count(ctx)
|
||||
if docks == 0 {
|
||||
for i := 1; i <= 20; i++ {
|
||||
@@ -162,31 +83,3 @@ func SeedIfEmpty(client *ent.Client) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func strPtrNil(s string) *string {
|
||||
if s == "" {
|
||||
return nil
|
||||
}
|
||||
return &s
|
||||
}
|
||||
|
||||
func itoaPad(n int) string {
|
||||
base := "000"
|
||||
return base[:len(base)-lenOfInt(n)] + intToStr(n)
|
||||
}
|
||||
|
||||
func lenOfInt(n int) int {
|
||||
c := 1
|
||||
for n >= 10 {
|
||||
n /= 10
|
||||
c++
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
func intToStr(n int) string {
|
||||
if n < 10 {
|
||||
return string(rune('0' + n))
|
||||
}
|
||||
return intToStr(n/10) + string(rune('0'+n%10))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user