feat: 完成MES工位终端系统全量功能迭代

本次提交包含以下核心变更:
1. 新增按钮级权限控制框架与前端权限校验
2. 新增工位管理、工艺流程、巡检终端等核心业务模块
3. 完善用户体系,支持工位终端专属登录权限
4. 新增文件上传下载、报表查询等配套功能
5. 修复多系统兼容性与交互体验问题
6. 完成所有文档与配置项的规范化更新
This commit is contained in:
SunYF
2026-08-31 08:06:55 +08:00
parent dab03ac0cf
commit 4ec6784629
99 changed files with 17436 additions and 479 deletions
+112 -19
View File
@@ -2,10 +2,13 @@ package logic
import (
"context"
"strconv"
"bj_power_mes/ent/permission"
"bj_power_mes/ent/processflow"
"bj_power_mes/ent/processstep"
"bj_power_mes/ent/role"
"bj_power_mes/ent/station"
"bj_power_mes/ent/stationprocess"
"bj_power_mes/ent/user"
@@ -14,23 +17,37 @@ import (
// Seed 初始化:超级管理员、默认角色、默认菜单权限、12 道工序与工位派工
func (s *Service) Seed(ctx context.Context) error {
// 1. 默认角色
// 1. 默认角色(已存在则合并权限码,保证新按钮权限能落到已有角色上)
roles := []struct{ name, code string }{
{"超级管理员", "SUPER_ADMIN"},
{"生产操作员", "OPERATOR"},
{"质检员", "INSPECTOR"},
}
for _, r := range roles {
if s.ctx.EntClient.Role.Query().Where(role.Code(r.code)).ExistX(ctx) {
continue
}
codes := []string{"*"}
if r.code == "OPERATOR" {
codes = []string{"produce.workorder", "produce.bom", "produce.material", "produce.scan", "produce.trace", "produce.torque", "produce.plc", "produce.product"}
codes = []string{
"produce.workorder", "produce.dailyplan", "produce.bom", "produce.material", "produce.scan",
"produce.trace", "produce.torque", "produce.plc", "produce.product", "produce.step",
"produce.processflow", "produce.station", "produce.performance",
// 按钮级权限(块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",
}
} else if r.code == "INSPECTOR" {
codes = []string{"produce.trace", "produce.torque", "sys.eventlog"}
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)
}
_ = s.ctx.EntClient.Role.Create().SetName(r.name).SetCode(r.code).SetPermissionCodes(codes).Exec(ctx)
}
// 2. 超级管理员账号 admin / 123456(对已存在的旧行做对齐,避免残留禁用状态)
@@ -53,13 +70,19 @@ func (s *Service) Seed(ctx context.Context) error {
// 3. 默认菜单权限
menus := []struct{ code, name, typ, path string }{
{"produce.workorder", "工单管理", "MENU", "/work-order"},
{"produce.bom", "BOM", "MENU", "/bom"},
{"produce.dailyplan", "日排产", "MENU", "/daily-plan"},
{"produce.bom", "物料清单", "MENU", "/bom"},
{"produce.material", "备料单", "MENU", "/material-request"},
{"produce.plc", "PLC工序下发", "MENU", "/plc-send"},
{"produce.torque", "拧紧查询", "MENU", "/torque"},
{"produce.step", "工艺参数", "MENU", "/process-step"},
{"produce.processflow", "工艺流程", "MENU", "/process-flow"},
{"produce.station", "工位管理", "MENU", "/station"},
{"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", "角色权限管理", "MENU", "/rbac"},
}
@@ -71,8 +94,43 @@ func (s *Service) Seed(ctx context.Context) error {
SetCode(m.code).SetName(m.name).SetType(m.typ).SetPath(m.path).Exec(ctx)
}
// 4. 12 道工序步骤模板(每道工序一个"装配完成"步骤 + 拧紧台阶步骤)
seedProcessSteps(ctx, s)
// 3.1 按钮级权限(块2):前端按钮 + 后端接口 双重校验
buttons := []struct{ code, name, typ, path string }{
{"produce.workorder:add", "工单-新增", "BUTTON", ""},
{"produce.workorder:edit", "工单-编辑", "BUTTON", ""},
{"produce.workorder:delete", "工单-删除", "BUTTON", ""},
{"produce.workorder:dailyplan", "工单-日排产", "BUTTON", ""},
{"produce.bom:edit", "物料清单-维护", "BUTTON", ""},
{"produce.material:generate", "备料单-生成", "BUTTON", ""},
{"produce.plc:send", "PLC工序-下发", "BUTTON", ""},
{"produce.product:add", "产品类型-新增", "BUTTON", ""},
{"produce.product:edit", "产品类型-编辑", "BUTTON", ""},
{"produce.product:delete", "产品类型-删除", "BUTTON", ""},
{"produce.processflow:add", "工艺流程-新增", "BUTTON", ""},
{"produce.processflow:edit", "工艺流程-编辑", "BUTTON", ""},
{"produce.processflow:delete", "工艺流程-删除", "BUTTON", ""},
{"produce.processflow:upload", "工艺流程-上传图纸", "BUTTON", ""},
{"produce.station:edit", "工位-绑定", "BUTTON", ""},
{"sys.rbac:user", "权限-用户", "BUTTON", ""},
{"sys.rbac:role", "权限-角色", "BUTTON", ""},
{"sys.rbac:perm", "权限-权限码", "BUTTON", ""},
{"sys.inspect:checkin", "巡检-开工签到", "BUTTON", ""},
{"sys.inspect:point", "巡检-工位点检", "BUTTON", ""},
{"sys.inspect:process", "巡检-过程巡检", "BUTTON", ""},
{"sys.inspect:done", "巡检-完工确认", "BUTTON", ""},
{"sys.inspect:alarm", "巡检-异常上报", "BUTTON", ""},
{"sys.inspect:view", "巡检-记录查看", "BUTTON", ""},
}
for _, b := range buttons {
if s.ctx.EntClient.Permission.Query().Where(permission.Code(b.code)).ExistX(ctx) {
continue
}
_ = s.ctx.EntClient.Permission.Create().
SetCode(b.code).SetName(b.name).SetType(b.typ).SetPath(b.path).Exec(ctx)
}
// 4. 12 道工序默认工艺流程 + 工位绑定(流程挂 process_code,工位绑流程)
seedFlowsAndStations(ctx, s)
// 5. 12 工位 ↔ 工序 派工(工位N 默认做工序N)
for i := 1; i <= 12; i++ {
if s.ctx.EntClient.StationProcess.Query().Where(stationprocess.StationNo(i), stationprocess.ProcessCode(i)).ExistX(ctx) {
@@ -84,17 +142,52 @@ func (s *Service) Seed(ctx context.Context) error {
return nil
}
func seedProcessSteps(ctx context.Context, s *Service) {
func seedFlowsAndStations(ctx context.Context, s *Service) {
// 每个工序编号(1~12)创建默认工艺流程,并为每道工序补默认步骤模板(挂到流程下)
for i := 1; i <= 12; i++ {
cnt, _ := s.ctx.EntClient.ProcessStep.Query().Where(processstep.ProcessCode(i)).Count(ctx)
if cnt > 0 {
continue
flow, err := s.ctx.EntClient.ProcessFlow.Query().
Where(processflow.ProcessCode(i), processflow.Name("工序"+strconv.Itoa(i)+"_默认")).First(ctx)
if err != nil {
flow, _ = s.ctx.EntClient.ProcessFlow.Create().
SetName("工序" + strconv.Itoa(i) + "_默认").
SetProcessCode(i).SetStatus("ACTIVE").SetRemark("默认工艺流程").Save(ctx)
}
_ = s.ctx.EntClient.ProcessStep.Create().
SetProcessCode(i).SetSeq(1).SetName("装配完成").SetCollectType("NONE").Exec(ctx)
if i == 3 { // 示例:工序3 增加一个拧紧采集步骤
_ = s.ctx.EntClient.ProcessStep.Create().
SetProcessCode(3).SetSeq(2).SetName("拧紧扭矩").SetCollectType("AUTO").SetIsTorque(true).Exec(ctx)
cnt, _ := s.ctx.EntClient.ProcessStep.Query().Where(processstep.FlowId(flow.ID)).Count(ctx)
if cnt == 0 {
stepID := 0
if step, err := s.ctx.EntClient.ProcessStep.Create().
SetFlowId(flow.ID).SetProcessCode(i).SetSeq(1).SetName("装配完成").
SetCollectType("NONE").Save(ctx); err == nil {
stepID = step.ID
}
if i == 3 && stepID > 0 {
_, _ = s.ctx.EntClient.ProcessStep.Create().
SetFlowId(flow.ID).SetProcessCode(i).SetSeq(2).SetName("拧紧扭矩").
SetCollectType("AUTO").SetIsTorque(true).Save(ctx)
}
}
// 工位 N 绑定工序 N 的默认流程
st, err := s.ctx.EntClient.Station.Query().Where(station.StationNo(i)).First(ctx)
if err != nil {
_, _ = s.ctx.EntClient.Station.Create().
SetStationNo(i).SetName("装配工位" + strconv.Itoa(i)).
SetFlowId(flow.ID).SetStatus("ENABLED").Save(ctx)
} else if st.FlowId == 0 {
_, _ = 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
}