Files
bj_power/bj_power_mes/internal/logic/seed.go
T
SunYF 0ab21b1234 feat&refactor: 完成多模块功能迭代与配置优化
本次提交覆盖多个业务模块的功能完善与体验优化:
1.  **鉴权与配置调整**:
    - 统一JWT滑动续签逻辑,简化Token存储,移除RefreshToken相关冗余代码
    - 调整多项目配置文件中JWT过期时间为3600秒,统一会话闲置窗口
    - 工位配置放开1~12限制,改为仅校验大于0
2.  **术语统一替换**:全链路将"精密件"替换为"电气件",修正物料管理描述
3.  **功能新增**:
    - 新增工位类型、工艺路线与产线点位台账模块
    - 添加工艺PDF预览面板、工位终端代理转发接口
    - 新增操作日志按操作人列表筛选、工位登出日志记录
    - 新增PLC移料指令与产线点位状态管理
4.  **业务流程优化**:
    - 调整BOM物料删除校验逻辑,优化工单备料计算
    - 补充物料图号、检测单号等追溯字段
    - 完善工艺流程图与工位绑定关系说明
    - 优化前端页面文案与交互细节
5.  **代码规范与维护**:
    - 新增通用工具函数与前端静态资源
    - 整理路由权限与中间件逻辑
    - 修复部分接口与配置的不兼容问题
2026-09-10 16:59:15 +08:00

229 lines
10 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package logic
import (
"context"
"strconv"
"bj_power_mes/ent/permission"
"bj_power_mes/ent/role"
"bj_power_mes/ent/station"
"bj_power_mes/ent/user"
"golang.org/x/crypto/bcrypt"
)
// Seed 初始化:超级管理员、默认角色、默认菜单权限、12 道工序与工位派工
func (s *Service) Seed(ctx context.Context) error {
// 1. 默认角色(已存在则合并权限码,保证新按钮权限能落到已有角色上)
roles := []struct{ name, code string }{
{"超级管理员", "SUPER_ADMIN"},
{"生产操作员", "OPERATOR"},
{"质检员", "INSPECTOR"},
}
for _, r := range roles {
codes := []string{"*"}
if r.code == "OPERATOR" {
codes = []string{
"produce.workorder", "produce.dailyplan", "produce.bom", "produce.material", "produce.scan",
"produce.trace", "produce.torque", "produce.plc", "produce.product",
"produce.processflow", "produce.station", "produce.performance", "produce.route",
// 按钮级权限(块2
"produce.workorder:add", "produce.workorder:edit", "produce.workorder:delete",
"produce.workorder:dailyplan", "produce.bom:edit", "produce.material:generate",
"produce.plc:send", "produce.product:add", "produce.product:edit", "produce.product:delete",
"produce.processflow:add", "produce.processflow:edit", "produce.processflow:delete",
"produce.processflow:upload", "produce.station:edit",
"produce.route:add", "produce.route:edit", "produce.route:delete",
}
} else if r.code == "INSPECTOR" {
codes = []string{"produce.trace", "produce.torque", "produce.performance", "sys.eventlog",
"sys.inspect", "sys.inspect:checkin", "sys.inspect:point", "sys.inspect:process",
"sys.inspect:done", "sys.inspect:alarm", "sys.inspect:view"}
}
exist, err := s.ctx.EntClient.Role.Query().Where(role.Code(r.code)).First(ctx)
if err != nil {
_ = s.ctx.EntClient.Role.Create().SetName(r.name).SetCode(r.code).SetPermissionCodes(codes).Exec(ctx)
} else {
_ = s.ctx.EntClient.Role.UpdateOneID(exist.ID).SetPermissionCodes(mergeCodes(exist.PermissionCodes, codes)).Exec(ctx)
}
}
// 2. 超级管理员账号 admin / 123456(对已存在的旧行做对齐,避免残留禁用状态)
hash, _ := bcrypt.GenerateFromPassword([]byte("123456"), bcrypt.DefaultCost)
adminRole, _ := s.ctx.EntClient.Role.Query().Where(role.Code("SUPER_ADMIN")).First(ctx)
roleId := 0
if adminRole != nil {
roleId = adminRole.ID
}
if s.ctx.EntClient.User.Query().Where(user.Username("admin")).ExistX(ctx) {
_ = s.ctx.EntClient.User.Update().
Where(user.Username("admin")).
SetPassword(string(hash)).SetStatus("ENABLED").SetRoleId(roleId).Exec(ctx)
} else {
_ = s.ctx.EntClient.User.Create().
SetUsername("admin").SetPassword(string(hash)).SetName("系统管理员").
SetRoleId(roleId).SetStatus("ENABLED").Exec(ctx)
}
// 3. 默认菜单权限
menus := []struct{ code, name, typ, path string }{
{"produce.workorder", "工单管理", "MENU", "/work-order"},
{"produce.dailyplan", "日排产", "MENU", "/daily-plan"},
{"produce.bom", "物料清单", "MENU", "/bom"},
{"produce.material", "备料单", "MENU", "/material-request"},
{"produce.plc", "工位组合下发", "MENU", "/plc-send"},
{"produce.torque", "拧紧查询", "MENU", "/torque"},
{"produce.processflow", "工艺流程", "MENU", "/process-flow"},
{"produce.station", "关联工位", "MENU", "/station"},
{"produce.route", "工艺路线", "MENU", "/process-route"},
{"produce.performance", "绩效报表", "MENU", "/performance"},
{"produce.scan", "手动报工", "MENU", "/scan"},
{"produce.trace", "工件追溯", "MENU", "/trace"},
{"produce.product", "产品型号", "MENU", "/product-type"},
{"sys.inspect", "巡检终端", "MENU", "/inspect"},
{"sys.eventlog", "操作日志", "MENU", "/event-log"},
// sys.rbac(角色权限管理) 已拆分:账号管理 + 角色管理(对齐 WMS 系统管理菜单)
{"sys.account", "账号管理", "MENU", "/account"},
{"sys.role", "角色管理", "MENU", "/role"},
}
for _, m := range menus {
if s.ctx.EntClient.Permission.Query().Where(permission.Code(m.code)).ExistX(ctx) {
continue
}
_ = s.ctx.EntClient.Permission.Create().
SetCode(m.code).SetName(m.name).SetType(m.typ).SetPath(m.path).Exec(ctx)
}
// 3.1 按钮级权限(块2):前端按钮 + 后端接口 双重校验
// parent = 所属菜单权限码:每个按钮显式声明挂在哪个菜单下,前端据此组装权限树,
// 不再用 code.startsWith(菜单+':') 这类前缀通配推断。
buttons := []struct{ code, name, typ, path, parent string }{
{"produce.workorder:add", "工单-新增", "BUTTON", "", "produce.workorder"},
{"produce.workorder:edit", "工单-编辑", "BUTTON", "", "produce.workorder"},
{"produce.workorder:delete", "工单-删除", "BUTTON", "", "produce.workorder"},
{"produce.workorder:dailyplan", "工单-日排产", "BUTTON", "", "produce.workorder"},
{"produce.bom:edit", "物料清单-维护", "BUTTON", "", "produce.bom"},
{"produce.material:generate", "备料单-生成", "BUTTON", "", "produce.material"},
{"produce.plc:send", "工位组合-下发", "BUTTON", "", "produce.plc"},
{"produce.product:add", "产品型号-新增", "BUTTON", "", "produce.product"},
{"produce.product:edit", "产品型号-编辑", "BUTTON", "", "produce.product"},
{"produce.product:delete", "产品型号-删除", "BUTTON", "", "produce.product"},
{"produce.processflow:add", "工艺流程-新增", "BUTTON", "", "produce.processflow"},
{"produce.processflow:edit", "工艺流程-编辑", "BUTTON", "", "produce.processflow"},
{"produce.processflow:delete", "工艺流程-删除", "BUTTON", "", "produce.processflow"},
{"produce.processflow:upload", "工艺流程-上传图纸", "BUTTON", "", "produce.processflow"},
{"produce.station:edit", "工位-绑定", "BUTTON", "", "produce.station"},
{"produce.route:add", "工艺路线-新增", "BUTTON", "", "produce.route"},
{"produce.route:edit", "工艺路线-编辑", "BUTTON", "", "produce.route"},
{"produce.route:delete", "工艺路线-删除", "BUTTON", "", "produce.route"},
{"sys.account:add", "账号-新增", "BUTTON", "", "sys.account"},
{"sys.account:edit", "账号-编辑", "BUTTON", "", "sys.account"},
{"sys.account:delete", "账号-删除", "BUTTON", "", "sys.account"},
{"sys.role:add", "角色-新增", "BUTTON", "", "sys.role"},
{"sys.role:edit", "角色-编辑", "BUTTON", "", "sys.role"},
{"sys.role:delete", "角色-删除", "BUTTON", "", "sys.role"},
{"sys.role:perm", "角色-权限定义", "BUTTON", "", "sys.role"},
{"sys.inspect:checkin", "巡检-开工签到", "BUTTON", "", "sys.inspect"},
{"sys.inspect:point", "巡检-工位点检", "BUTTON", "", "sys.inspect"},
{"sys.inspect:process", "巡检-过程巡检", "BUTTON", "", "sys.inspect"},
{"sys.inspect:done", "巡检-完工确认", "BUTTON", "", "sys.inspect"},
{"sys.inspect:alarm", "巡检-异常上报", "BUTTON", "", "sys.inspect"},
{"sys.inspect:view", "巡检-记录查看", "BUTTON", "", "sys.inspect"},
}
for _, b := range buttons {
exist, err := s.ctx.EntClient.Permission.Query().Where(permission.Code(b.code)).Only(ctx)
if err == nil && exist != nil {
// 已存在:幂等回填 parentCode(避免旧数据没有父子关系导致权限树漏按钮)
if exist.ParentCode != b.parent {
_ = exist.Update().SetParentCode(b.parent).Exec(ctx)
}
continue
}
_ = s.ctx.EntClient.Permission.Create().
SetCode(b.code).SetName(b.name).SetType(b.typ).SetPath(b.path).
SetParentCode(b.parent).Exec(ctx)
}
// 3.2 旧版权限码迁移(sys.rbac 家族 → sys.account / sys.role,幂等)
migrateLegacySysRbac(ctx, s)
// 4. 初始化工位与默认工艺流程(工位绑定流程,流程承载步骤)
seedFlowsAndStations(ctx, s)
return nil
}
func seedFlowsAndStations(ctx context.Context, s *Service) {
// 初始数据:12 个工位(工位数量以数据库为准,这里只做首次初始化;要 13 个就往 station 表插一行)
for i := 1; i <= 12; i++ {
st, err := s.ctx.EntClient.Station.Query().Where(station.StationNo(i)).First(ctx)
if err != nil {
st, _ = s.ctx.EntClient.Station.Create().
SetStationNo(i).SetName("装配工位" + strconv.Itoa(i)).SetStatus("ENABLED").Save(ctx)
}
if st == nil || st.FlowId > 0 {
continue
}
flow, err := s.ctx.EntClient.ProcessFlow.Create().
SetName("工位" + strconv.Itoa(i) + "_默认").
SetStatus("ACTIVE").SetRemark("默认工艺流程").Save(ctx)
if err != nil {
continue
}
if _, err := s.ctx.EntClient.ProcessStep.Create().
SetFlowId(flow.ID).SetSeq(1).SetName("装配完成").SetCollectType("NONE").Save(ctx); err == nil && i == 3 {
_, _ = s.ctx.EntClient.ProcessStep.Create().
SetFlowId(flow.ID).SetSeq(2).SetName("拧紧扭矩").SetCollectType("AUTO").SetIsTorque(true).Save(ctx)
}
_, _ = s.ctx.EntClient.Station.UpdateOneID(st.ID).SetFlowId(flow.ID).Save(ctx)
}
}
// mergeCodes 合并两份权限码列表(去重、保序)
func mergeCodes(a, b []string) []string {
seen := map[string]bool{}
out := make([]string, 0, len(a)+len(b))
for _, c := range append(append([]string{}, a...), b...) {
if c == "" || seen[c] {
continue
}
seen[c] = true
out = append(out, c)
}
return out
}
// migrateLegacySysRbac 迁移旧 sys.rbac 家族权限码(幂等,可重复执行):
// sys.rbac(角色权限管理) 已拆分为 sys.account(账号管理)+sys.role(角色管理)。
// 对存量角色 permissionCodes 做码替换,并删除已废弃的权限定义行。
func migrateLegacySysRbac(ctx context.Context, s *Service) {
legacyMap := map[string][]string{
"sys.rbac": {"sys.account", "sys.role"},
"sys.rbac:user": {"sys.account:add", "sys.account:edit", "sys.account:delete"},
"sys.rbac:role": {"sys.role:add", "sys.role:edit", "sys.role:delete"},
"sys.rbac:perm": {"sys.role:perm"},
}
// 1) 角色 permissionCodes 替换
roleList, _ := s.ctx.EntClient.Role.Query().All(ctx)
for _, rl := range roleList {
changed := false
out := make([]string, 0, len(rl.PermissionCodes))
for _, c := range rl.PermissionCodes {
if repl, ok := legacyMap[c]; ok {
out = append(out, repl...)
changed = true
continue
}
out = append(out, c)
}
if changed {
_ = s.ctx.EntClient.Role.UpdateOneID(rl.ID).
SetPermissionCodes(mergeCodes(out, nil)).Exec(ctx)
}
}
// 2) 删除旧权限定义行
for oldCode := range legacyMap {
_, _ = s.ctx.EntClient.Permission.Delete().
Where(permission.Code(oldCode)).Exec(ctx)
}
}