refactor: 重构工艺工序相关命名与数据模型

1.  全局替换"工序"为"工艺"统一术语,包括页面文案、枚举、注释
2.  重构工单与工件工艺数据模型:
    - 新增工艺组合表flow_material作为备料/齐套唯一源头
    - 新增工位状态表station_state支持自主停单/恢复接单
    - 移除station_process派工表,改用工单工艺组合作为路线唯一源头
    - 替换processCode为flowId作为工艺关联标识
    - 重构工件当前进度字段为currentStationNo
3.  删除冗余的静态备份资源文件
4.  调整物料选择组件默认启用仅显示激活物料
5.  优化工单创建/编辑逻辑,新增工艺组合校验与保存
This commit is contained in:
SunYF
2026-09-22 11:32:29 +08:00
parent 932700ca65
commit d609ff8a9e
185 changed files with 8623 additions and 4501 deletions
@@ -21,8 +21,8 @@ import (
"bj_power_mes/ent/workorder"
"bj_power_mes/ent/workpieceprocess"
"bj_power_mes/internal/logic"
"bj_power_mes/internal/wmsclient"
"bj_power_mes/internal/svc"
"bj_power_mes/internal/wmsclient"
)
// ModelProcessCardHandler GET /api/v1/process-card/model?productTypeId=&productCode=[&format=json]
@@ -31,7 +31,7 @@ import (
// 该型号的完整制造定义,用于工艺交底/审核/客户查阅:
//
// 产品型号 → 工位组合 → 每工位工艺流程与步骤(含采集方式/检测点/拧紧/考核标准/工艺图纸)
// → 物料清单(按 BOM 分组,含装配工位/单台用量/损耗/需求总量)
// → 物料清单(按 BOM 分组,含工艺清单/单台用量/损耗/需求总量)
// → 检验卡项(所有"检测确认"步骤汇总为检验项清单)
// → 各环节时间(工单计划时间 + 该型号历史实绩的首末作业时间)
//
@@ -116,7 +116,7 @@ type modelBomItem struct {
Spec string
Unit string
ManageMode string
StationNo string
FlowNames string // 使用该物料的工艺清单(经 flow_material 映射)
UnitQty string
LossRate string
RequiredQty string
@@ -158,37 +158,40 @@ func buildModelCard(ctx context.Context, svcCtx *svc.ServiceContext, productType
return nil, fmt.Errorf("产品型号不存在")
}
// 2) 产品最近工单(仅取工单号/计划时间用于卡头展示;路线不再存工单)
// 2) 产品最近工单(卡头展示 + 工艺组合路线唯一来源)
var refWO *ent.WorkOrder
if productCode != "" {
if wo, err := client.WorkOrder.Query().
refWO, _ = client.WorkOrder.Query().
Where(workorder.ProductCode(productCode)).
Order(ent.Desc(workorder.FieldID)).First(ctx); err == nil && wo != nil {
card.OrderNo = wo.WorkOrderNo
if wo.PlanStart != nil {
card.PlanStart = wo.PlanStart.Format("2006-01-02 15:04")
}
if wo.PlanEnd != nil {
card.PlanEnd = wo.PlanEnd.Format("2006-01-02 15:04")
}
Order(ent.Desc(workorder.FieldID)).First(ctx)
}
if refWO != nil {
card.OrderNo = refWO.WorkOrderNo
if refWO.PlanStart != nil {
card.PlanStart = refWO.PlanStart.Format("2006-01-02 15:04")
}
if refWO.PlanEnd != nil {
card.PlanEnd = refWO.PlanEnd.Format("2006-01-02 15:04")
}
}
// 3) 工位组合:由「今日工位派工(station_process)」派生;未派工则展示全产线已配置工艺流的工位
seq, _ := logic.RouteStations(ctx, client, logic.TodayStr(), "")
if len(seq) == 0 {
// 3) 工位组合:唯一路线源头 = 最近工单的工艺组合;未配置则展示全产线已配置工艺的工位
var seq []int
if items, _ := logic.RouteStationsFromWO(ctx, client, refWO); len(items) > 0 {
seq = logic.RouteStationsOfWO(items)
card.SeqSource = "取自最近工单的工艺组合(工单 " + refWO.WorkOrderNo + ")"
} else {
sts, _ := client.Station.Query().Where(station.FlowIdGT(0)).Order(ent.Asc(station.FieldStationNo)).All(ctx)
for _, st := range sts {
seq = append(seq, st.StationNo)
}
card.SeqSource = "今日未派工,按全产线已配置工位顺序展示"
} else {
card.SeqSource = "取自今日工位派工路线"
card.SeqSource = "未配置工艺组合,按全产线已配置工位顺序展示"
}
sort.Ints(seq)
card.ProcessSeq = joinInts(seq)
card.StationCount = len(seq)
// 3) 工位 + 工艺流程 + 步骤 + 考核标准
// 4) 工位 + 工艺流程 + 步骤 + 考核标准
sts, _ := client.Station.Query().Where(station.StationNoIn(seq...)).All(ctx)
stationByName := map[int]*ent.Station{}
flowIds := []int{}
@@ -227,8 +230,9 @@ func buildModelCard(ctx context.Context, svcCtx *svc.ServiceContext, productType
for _, no := range seq {
st := stationByName[no]
ms := modelStation{StationNo: no, StationName: fmt.Sprintf("工位%d", no), RefTime: refTimes[no]}
ms := modelStation{StationNo: no, StationName: fmt.Sprintf("工位%d", no)}
if st != nil {
ms.RefTime = refTimes[st.FlowId]
if st.Name != "" {
ms.StationName = st.Name
}
@@ -263,8 +267,22 @@ func buildModelCard(ctx context.Context, svcCtx *svc.ServiceContext, productType
card.Stations = append(card.Stations, ms)
}
// 4) 物料清单(按 BOM 名称分组)
// 5) 物料清单(按 BOM 名称分组;工艺清单经 flow_material + process_flow 映射)
if productCode != "" {
// 物料编码 → 使用该物料的工艺名列表(工艺物料清单是装配绑定的唯一依据)
flowNames := map[string][]string{}
if fms, e := client.FlowMaterial.Query().All(ctx); e == nil {
flowAll, _ := client.ProcessFlow.Query().All(ctx)
nameById := map[int]string{}
for _, f := range flowAll {
nameById[f.ID] = f.Name
}
for _, fm := range fms {
if n := nameById[fm.FlowId]; n != "" {
flowNames[fm.MaterialCode] = append(flowNames[fm.MaterialCode], n)
}
}
}
items, _ := client.BomItem.Query().
Where(bomitem.ProductCode(productCode)).
Order(ent.Asc(bomitem.FieldBomName), ent.Asc(bomitem.FieldMaterialCode)).All(ctx)
@@ -280,9 +298,9 @@ func buildModelCard(ctx context.Context, svcCtx *svc.ServiceContext, productType
gi = len(card.BomGroups) - 1
groupIdx[name] = gi
}
stationNo := "-"
if it.ProcessCode > 0 {
stationNo = fmt.Sprintf("工位%d", it.ProcessCode)
fnames := "-"
if ns := flowNames[it.MaterialCode]; len(ns) > 0 {
fnames = strings.Join(ns, "、")
}
card.BomGroups[gi].Items = append(card.BomGroups[gi].Items, modelBomItem{
MaterialCode: it.MaterialCode,
@@ -290,7 +308,7 @@ func buildModelCard(ctx context.Context, svcCtx *svc.ServiceContext, productType
Spec: it.Spec,
Unit: it.Unit,
ManageMode: manageModeCN(it.ManageMode),
StationNo: stationNo,
FlowNames: fnames,
UnitQty: f2s(it.UnitQty),
LossRate: f2s(it.LossRate),
RequiredQty: f2s(it.RequiredQty),
@@ -303,7 +321,7 @@ func buildModelCard(ctx context.Context, svcCtx *svc.ServiceContext, productType
return card, nil
}
// stationRefTimes 该产品型号历史实绩按工位聚合的首末作业时间(无实绩则返回空 map)
// stationRefTimes 该产品型号历史实绩按工艺聚合的首末作业时间(无实绩则返回空 map,键=工艺ID)
func stationRefTimes(ctx context.Context, client *ent.Client, productCode string) map[int]string {
out := map[int]string{}
if productCode == "" {
@@ -326,10 +344,10 @@ func stationRefTimes(ctx context.Context, client *ent.Client, productCode string
}
m := map[int]*span{}
for _, p := range procs {
s := m[p.ProcessCode]
s := m[p.FlowId]
if s == nil {
s = &span{}
m[p.ProcessCode] = s
m[p.FlowId] = s
}
if p.StartedAt != nil {
t := p.StartedAt.Unix()
@@ -346,15 +364,15 @@ func stationRefTimes(ctx context.Context, client *ent.Client, productCode string
s.seen = true
}
}
for code, s := range m {
for flowId, s := range m {
if !s.seen {
continue
}
if s.last == 0 {
out[code] = tsText(s.first) + " 起(未完成)"
out[flowId] = tsText(s.first) + " 起(未完成)"
continue
}
out[code] = tsText(s.first) + " → " + tsText(s.last)
out[flowId] = tsText(s.first) + " → " + tsText(s.last)
}
return out
}
@@ -511,7 +529,7 @@ func renderModelCard(c *modelCard) (string, error) {
{{if .Steps}}
<table>
<thead>
<tr><th colspan="6" class="st-name">工位{{.StationNo}} {{.StationName}} · {{if .FlowName}}{{.FlowName}}{{else}}未绑定流程{{end}}</th></tr>
<tr><th colspan="6" class="st-name">工位{{.StationNo}} {{.StationName}} · {{if .FlowName}}{{.FlowName}}{{else}}未绑定工艺{{end}}</th></tr>
<tr><th style="width:64px;">步骤号</th><th style="width:26%;">步骤名称</th><th>采集方式</th><th>检测确认</th><th>拧紧采集</th><th>考核标准</th></tr>
</thead>
<tbody>
@@ -534,13 +552,13 @@ func renderModelCard(c *modelCard) (string, error) {
<table>
<thead>
<tr><th colspan="9" class="st-name">BOM:{{.BomName}}</th></tr>
<tr><th>图号</th><th>物料名称</th><th>规格</th><th>单位</th><th>管理方式</th><th>装配工位</th><th>单台用量</th><th>损耗率(%)</th><th>需求总量</th></tr>
<tr><th>图号</th><th>物料名称</th><th>规格</th><th>单位</th><th>管理方式</th><th>工艺清单</th><th>单台用量</th><th>损耗率(%)</th><th>需求总量</th></tr>
</thead>
<tbody>
{{range .Items}}
<tr>
<td>{{.MaterialCode}}</td><td class="left">{{.MaterialName}}</td><td class="left">{{.Spec}}</td>
<td>{{.Unit}}</td><td>{{.ManageMode}}</td><td>{{.StationNo}}</td>
<td>{{.Unit}}</td><td>{{.ManageMode}}</td><td>{{.FlowNames}}</td>
<td>{{.UnitQty}}</td><td>{{.LossRate}}</td><td>{{.RequiredQty}}</td>
</tr>
{{end}}