refactor: 重构工艺工序相关命名与数据模型
1. 全局替换"工序"为"工艺"统一术语,包括页面文案、枚举、注释
2. 重构工单与工件工艺数据模型:
- 新增工艺组合表flow_material作为备料/齐套唯一源头
- 新增工位状态表station_state支持自主停单/恢复接单
- 移除station_process派工表,改用工单工艺组合作为路线唯一源头
- 替换processCode为flowId作为工艺关联标识
- 重构工件当前进度字段为currentStationNo
3. 删除冗余的静态备份资源文件
4. 调整物料选择组件默认启用仅显示激活物料
5. 优化工单创建/编辑逻辑,新增工艺组合校验与保存
This commit is contained in:
@@ -80,19 +80,19 @@ type cardTorque struct {
|
||||
}
|
||||
|
||||
type cardTmpl struct {
|
||||
Sn string
|
||||
OrderNo string
|
||||
Product string
|
||||
Quantity int
|
||||
CreatedAt string
|
||||
Processes []cardProcess
|
||||
Materials []string
|
||||
Operator string
|
||||
Inspector string
|
||||
DoneTime string
|
||||
Result string
|
||||
NeedTorque bool
|
||||
TorqueRows []cardTorque
|
||||
Sn string
|
||||
OrderNo string
|
||||
Product string
|
||||
Quantity int
|
||||
CreatedAt string
|
||||
Processes []cardProcess
|
||||
Materials []string
|
||||
Operator string
|
||||
Inspector string
|
||||
DoneTime string
|
||||
Result string
|
||||
NeedTorque bool
|
||||
TorqueRows []cardTorque
|
||||
}
|
||||
|
||||
// buildCard 汇总流程卡所需全部数据(ent 实体直查),并计算缺失项。
|
||||
@@ -132,23 +132,27 @@ func buildCard(ctx context.Context, svcCtx *svc.ServiceContext, sn string) (*car
|
||||
missing = append(missing, "产品数量")
|
||||
}
|
||||
|
||||
// 工序时间线(操作人 + 完成时间)
|
||||
// 工艺时间线(操作人 + 完成时间)
|
||||
procs, _ := client.WorkpieceProcess.Query().
|
||||
Where(workpieceprocess.Sn(sn)).Order(workpieceprocess.ByProcessCode()).All(ctx)
|
||||
Where(workpieceprocess.Sn(sn)).Order(ent.Asc(workpieceprocess.FieldFlowId), ent.Asc(workpieceprocess.FieldID)).All(ctx)
|
||||
for _, p := range procs {
|
||||
doneAt := ""
|
||||
if p.EndedAt != nil {
|
||||
doneAt = p.EndedAt.Format("2006-01-02 15:04")
|
||||
}
|
||||
flowLabel := p.ProcessName
|
||||
if flowLabel == "" {
|
||||
flowLabel = "工艺" + itoaCard(p.FlowId)
|
||||
}
|
||||
if p.Operator == "" {
|
||||
missing = append(missing, fmt.Sprintf("工序%s报工记录(缺操作人)", itoaCard(p.ProcessCode)))
|
||||
missing = append(missing, fmt.Sprintf("工艺「%s」报工记录(缺操作人)", flowLabel))
|
||||
}
|
||||
if p.EndedAt == nil {
|
||||
missing = append(missing, fmt.Sprintf("工序%s报工记录(缺完成时间)", itoaCard(p.ProcessCode)))
|
||||
missing = append(missing, fmt.Sprintf("工艺「%s」报工记录(缺完成时间)", flowLabel))
|
||||
}
|
||||
cp := cardProcess{
|
||||
ID: p.ID,
|
||||
Code: fmt.Sprintf("%d", p.ProcessCode),
|
||||
Code: p.StationNo,
|
||||
Name: p.ProcessName,
|
||||
Station: p.StationNo,
|
||||
Operator: p.Operator,
|
||||
@@ -162,7 +166,7 @@ func buildCard(ctx context.Context, svcCtx *svc.ServiceContext, sn string) (*car
|
||||
t.Processes = append(t.Processes, cp)
|
||||
}
|
||||
if len(procs) == 0 {
|
||||
missing = append(missing, "工序报工记录")
|
||||
missing = append(missing, "报工记录")
|
||||
}
|
||||
|
||||
// 步骤考核数据按工序实绩 ID 归组,回填到对应工序行的"采集参数"
|
||||
@@ -206,15 +210,22 @@ func buildCard(ctx context.Context, svcCtx *svc.ServiceContext, sn string) (*car
|
||||
return t, missing, nil
|
||||
}
|
||||
|
||||
// hasTorqueStep 判断该工件加工路径上是否存在"拧紧采集"步骤(经 工位→流程→步骤 链)
|
||||
// 路径由今日工位派工(station_process)派生
|
||||
// hasTorqueStep 判断该工件加工路径上是否存在"拧紧采集"步骤(经 工单工艺组合→工位→流程→步骤 链)
|
||||
// 路线 = 工单工艺组合(唯一路线源头)
|
||||
func hasTorqueStep(ctx context.Context, client *ent.Client, sn string, wp *ent.Workpiece) bool {
|
||||
route, err := logic.RouteStations(ctx, client, logic.TodayStr(), "")
|
||||
if err != nil || len(route) == 0 {
|
||||
if wp.WorkOrderId <= 0 {
|
||||
return false
|
||||
}
|
||||
wo, err := client.WorkOrder.Get(ctx, wp.WorkOrderId)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
items, _ := logic.RouteStationsFromWO(ctx, client, wo)
|
||||
if len(items) == 0 {
|
||||
return false
|
||||
}
|
||||
stations, err := client.Station.Query().
|
||||
Where(station.StationNoIn(route...), station.FlowIdGT(0)).All(ctx)
|
||||
Where(station.StationNoIn(logic.RouteStationsOfWO(items)...), station.FlowIdGT(0)).All(ctx)
|
||||
if err != nil || len(stations) == 0 {
|
||||
return false
|
||||
}
|
||||
@@ -269,13 +280,13 @@ func renderProcessCard(t *cardTmpl) (string, error) {
|
||||
<span>判定:<b>{{if eq .Result "NG"}}<span class="ng">NG</span>{{else}}{{.Result}}{{end}}</b></span>
|
||||
</div>
|
||||
{{if .Processes}}
|
||||
<h2>工序流转记录</h2>
|
||||
<h2>工艺流转记录</h2>
|
||||
<table>
|
||||
<thead><tr><th>工序</th><th>工序名称</th><th>工位</th><th>操作人</th><th>完成时间</th><th>采集参数</th><th>结果</th></tr></thead>
|
||||
<thead><tr><th>工位号</th><th>工艺名称</th><th>工位</th><th>操作人</th><th>完成时间</th><th>采集参数</th><th>结果</th></tr></thead>
|
||||
<tbody>
|
||||
{{range .Processes}}
|
||||
<tr>
|
||||
<td>工序{{.Code}}</td><td>{{.Name}}</td><td>{{.Station}}</td><td>{{.Operator}}</td><td>{{.DoneTime}}</td>
|
||||
<td>{{.Code}}</td><td>{{.Name}}</td><td>{{.Station}}</td><td>{{.Operator}}</td><td>{{.DoneTime}}</td>
|
||||
<td>{{if .Steps}}{{range .Steps}}{{.Name}}={{.Value}}({{if .OK}}<span class="ok">OK</span>{{else}}<span class="ng">NG</span>{{end}}) {{end}}{{else}}-{{end}}</td>
|
||||
<td>{{if eq .Result "NG"}}<span class="ng">NG</span>{{else}}OK{{end}}</td>
|
||||
</tr>
|
||||
@@ -283,7 +294,7 @@ func renderProcessCard(t *cardTmpl) (string, error) {
|
||||
</tbody>
|
||||
</table>
|
||||
{{else}}
|
||||
<p style="color:#b45309;">暂无工序报工记录。</p>
|
||||
<p style="color:#b45309;">暂无报工记录。</p>
|
||||
{{end}}
|
||||
{{if .NeedTorque}}
|
||||
<h2>拧紧数据{{if not .TorqueRows}}(无数据){{end}}</h2>
|
||||
|
||||
@@ -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}}
|
||||
|
||||
@@ -13,12 +13,12 @@ func itoa(n int) string { return strconv.Itoa(n) }
|
||||
|
||||
// BindReq 绑定提交请求
|
||||
type BindReq struct {
|
||||
Sn string `json:"sn"`
|
||||
OrderNo string `json:"orderNo"`
|
||||
ProcessCode int `json:"processCode"`
|
||||
StationNo int `json:"stationNo"`
|
||||
Operator string `json:"operator"`
|
||||
Items []logic.BindItemReq `json:"items"`
|
||||
Sn string `json:"sn"`
|
||||
OrderNo string `json:"orderNo"`
|
||||
FlowId int `json:"flowId"` // 报工/绑定工艺(唯一路线源头=工单工艺组合)
|
||||
StationNo int `json:"stationNo"`
|
||||
Operator string `json:"operator"`
|
||||
Items []logic.BindItemReq `json:"items"`
|
||||
}
|
||||
|
||||
func doBind(svcCtx *svc.ServiceContext, r *http.Request) (any, error) {
|
||||
@@ -26,7 +26,7 @@ func doBind(svcCtx *svc.ServiceContext, r *http.Request) (any, error) {
|
||||
if err := httpx.ParseJSON(r, &req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if req.Sn == "" || req.ProcessCode <= 0 {
|
||||
if req.Sn == "" || req.FlowId <= 0 {
|
||||
return nil, nil
|
||||
}
|
||||
op := operator(r, req.Operator)
|
||||
@@ -34,7 +34,7 @@ func doBind(svcCtx *svc.ServiceContext, r *http.Request) (any, error) {
|
||||
if req.StationNo > 0 {
|
||||
stationNo = itoa(req.StationNo)
|
||||
}
|
||||
return logic.New(svcCtx).BindWorkpiece(r.Context(), req.Sn, req.OrderNo, req.ProcessCode, stationNo, op, req.Items)
|
||||
return logic.New(svcCtx).BindWorkpiece(r.Context(), req.Sn, req.OrderNo, req.FlowId, stationNo, op, req.Items)
|
||||
}
|
||||
|
||||
// BindInternalHandler POST /api/internal/workpiece/binds —— 工位终端扫码绑料
|
||||
@@ -46,7 +46,7 @@ func BindInternalHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return
|
||||
}
|
||||
if data == nil {
|
||||
httpx.BadRequest(w, "sn / processCode 必填")
|
||||
httpx.BadRequest(w, "sn / flowId 必填")
|
||||
return
|
||||
}
|
||||
httpx.Ok(w, data)
|
||||
@@ -55,16 +55,16 @@ func BindInternalHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
|
||||
func doUnbind(svcCtx *svc.ServiceContext, r *http.Request) error {
|
||||
var req struct {
|
||||
Sn string `json:"sn"`
|
||||
ProcessCode int `json:"processCode"`
|
||||
Sn string `json:"sn"`
|
||||
FlowId int `json:"flowId"`
|
||||
MaterialCode string `json:"materialCode"`
|
||||
BindValue string `json:"bindValue"`
|
||||
Operator string `json:"operator"`
|
||||
BindValue string `json:"bindValue"`
|
||||
Operator string `json:"operator"`
|
||||
}
|
||||
if err := httpx.ParseJSON(r, &req); err != nil {
|
||||
return err
|
||||
}
|
||||
return logic.New(svcCtx).RemoveBind(r.Context(), req.Sn, req.ProcessCode, req.MaterialCode, req.BindValue, operator(r, req.Operator))
|
||||
return logic.New(svcCtx).RemoveBind(r.Context(), req.Sn, req.FlowId, req.MaterialCode, req.BindValue, operator(r, req.Operator))
|
||||
}
|
||||
|
||||
// UnbindInternalHandler POST /api/internal/workpiece/binds/remove
|
||||
@@ -80,17 +80,17 @@ func UnbindInternalHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
|
||||
// ---------- 装机绑定:应绑清单/齐套状态查询 ----------
|
||||
|
||||
// bindPanelFromReq 通用查询逻辑:按 sn+processCode 返回绑定面板(应绑物料+已绑情况)
|
||||
// bindPanelFromReq 通用查询逻辑:按 sn+flowId 返回绑定面板(应绑物料+已绑情况)
|
||||
func bindPanelFromReq(svcCtx *svc.ServiceContext, r *http.Request) (any, error) {
|
||||
sn := r.URL.Query().Get("sn")
|
||||
processCode := atoiDefault(r.URL.Query().Get("processCode"), 0)
|
||||
if sn == "" || processCode <= 0 {
|
||||
flowId := atoiDefault(r.URL.Query().Get("flowId"), 0)
|
||||
if sn == "" || flowId <= 0 {
|
||||
return nil, nil
|
||||
}
|
||||
return logic.New(svcCtx).StationBindPanelData(r.Context(), sn, processCode)
|
||||
return logic.New(svcCtx).StationBindPanelData(r.Context(), sn, flowId)
|
||||
}
|
||||
|
||||
// BindPanelInternalHandler GET /api/internal/workpiece/bind-panel?sn=&processCode=
|
||||
// BindPanelInternalHandler GET /api/internal/workpiece/bind-panel?sn=&flowId=
|
||||
// 工位终端(12 个触控一体机)扫码工件后拉取绑定面板,驱动工人扫码绑料。
|
||||
func BindPanelInternalHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -100,7 +100,7 @@ func BindPanelInternalHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return
|
||||
}
|
||||
if data == nil {
|
||||
httpx.BadRequest(w, "缺少 sn / processCode 参数")
|
||||
httpx.BadRequest(w, "缺少 sn / flowId 参数")
|
||||
return
|
||||
}
|
||||
httpx.Ok(w, data)
|
||||
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
// ---------- BOM ----------
|
||||
|
||||
// ImportBomHandler POST /bom/import 产品物料清单(BOM) Excel 导入。
|
||||
// 模板列:产品编号|物料清单名称|物料编码|名称|规格|单位|单台用量|损耗率|装配工位|相关标准|备注
|
||||
// 模板列:产品编号|物料清单名称|物料编码|名称|规格|单位|单台用量|损耗率|相关标准|备注(原「装配工位」列忽略)
|
||||
func ImportBomHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
if err := r.ParseMultipartForm(svcCtx.Config.Upload.MaxMB << 20); err != nil {
|
||||
@@ -59,12 +59,6 @@ func ImportBomHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
if bomName == "" {
|
||||
bomName = "默认"
|
||||
}
|
||||
pc := 0
|
||||
if v := trimCell(row, 8); v != "" {
|
||||
if n, e := strconv.Atoi(v); e == nil {
|
||||
pc = n
|
||||
}
|
||||
}
|
||||
item := logic.BomItemReq{
|
||||
ProductCode: productCode,
|
||||
BomName: bomName,
|
||||
@@ -73,7 +67,6 @@ func ImportBomHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
Spec: trimCell(row, 4),
|
||||
Unit: trimCell(row, 5),
|
||||
ManageMode: "2",
|
||||
ProcessCode: &pc,
|
||||
UnitQty: parseFloat(trimCell(row, 6)),
|
||||
LossRate: parseFloat(trimCell(row, 7)),
|
||||
RelatedStandard: trimCell(row, 9),
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
package production
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"bj_power_mes/common/httpx"
|
||||
"bj_power_mes/internal/logic"
|
||||
"bj_power_mes/internal/svc"
|
||||
)
|
||||
|
||||
// ListFlowMaterialsHandler GET /api/v1/process-flows/:id/materials —— 查某工艺的物料清单(flow_material)
|
||||
func ListFlowMaterialsHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
flowId := pathID(r)
|
||||
data, err := logic.New(svcCtx).ListFlowMaterials(r.Context(), flowId)
|
||||
if err != nil {
|
||||
httpx.Fail(w, 2113, err.Error())
|
||||
return
|
||||
}
|
||||
httpx.Ok(w, data)
|
||||
}
|
||||
}
|
||||
|
||||
// SaveFlowMaterialsHandler POST /api/v1/process-flows/:id/materials —— 整表覆盖保存工艺物料清单
|
||||
// body: {items: [{materialCode, materialName, spec, unit, manageMode, unitQty, lossRate}]}
|
||||
func SaveFlowMaterialsHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
flowId := pathID(r)
|
||||
var req struct {
|
||||
Items []logic.FlowMaterialReq `json:"items"`
|
||||
}
|
||||
if err := httpx.ParseJSON(r, &req); err != nil {
|
||||
httpx.BadRequest(w, "请求体解析失败")
|
||||
return
|
||||
}
|
||||
if err := logic.New(svcCtx).SaveFlowMaterials(r.Context(), flowId, req.Items, operator(r, "")); err != nil {
|
||||
httpx.Fail(w, 2114, err.Error())
|
||||
return
|
||||
}
|
||||
httpx.OkMessage(w, "工艺物料清单已保存", nil)
|
||||
}
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
package production
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"bj_power_mes/common/httpx"
|
||||
"bj_power_mes/internal/logic"
|
||||
"bj_power_mes/internal/svc"
|
||||
)
|
||||
|
||||
// ---------- 工位派工(工位↔工序,替代原工序组合串) ----------
|
||||
|
||||
// ListStationProcessHandler GET /station-process 查询某日班次全部工位派工(按工位号升序)
|
||||
func ListStationProcessHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
q := r.URL.Query()
|
||||
data, err := logic.New(svcCtx).ListStationProcess(r.Context(), q.Get("effectDate"), q.Get("shift"))
|
||||
if err != nil {
|
||||
httpx.Fail(w, 3301, err.Error())
|
||||
return
|
||||
}
|
||||
httpx.Ok(w, data)
|
||||
}
|
||||
}
|
||||
|
||||
// SaveStationProcessHandler POST /station-process/save 保存单条工位派工
|
||||
// 校验:同工位同天同班次唯一;已派工工位沿工位号必须单调不降、不可穿插。processCode=0 表示解除派工。
|
||||
func SaveStationProcessHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req logic.StationProcessReq
|
||||
if err := httpx.ParseJSON(r, &req); err != nil {
|
||||
httpx.BadRequest(w, "请求体解析失败")
|
||||
return
|
||||
}
|
||||
if req.Operator == "" {
|
||||
req.Operator = operator(r, "")
|
||||
}
|
||||
if err := logic.New(svcCtx).UpsertStationProcess(r.Context(), req); err != nil {
|
||||
httpx.Fail(w, 3302, err.Error())
|
||||
return
|
||||
}
|
||||
httpx.OkMessage(w, "保存成功", nil)
|
||||
}
|
||||
}
|
||||
|
||||
// RouteStationProcessHandler GET /station-process/route 派生路线(工位号列表/工序映射/串)
|
||||
// 供流程卡、PLC 下发、备料单等依赖"今日路线"的模块读取,不再手填组合串。
|
||||
func RouteStationProcessHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
q := r.URL.Query()
|
||||
ctx := r.Context()
|
||||
stations, err := logic.RouteStations(ctx, svcCtx.EntClient, q.Get("effectDate"), q.Get("shift"))
|
||||
if err != nil {
|
||||
httpx.Fail(w, 3303, err.Error())
|
||||
return
|
||||
}
|
||||
procMap, err := logic.RouteStationProcessCodeMap(ctx, svcCtx.EntClient, q.Get("effectDate"), q.Get("shift"))
|
||||
if err != nil {
|
||||
httpx.Fail(w, 3303, err.Error())
|
||||
return
|
||||
}
|
||||
str, _ := logic.RouteStationsStr(ctx, svcCtx.EntClient, q.Get("effectDate"), q.Get("shift"))
|
||||
httpx.Ok(w, map[string]any{
|
||||
"stations": stations,
|
||||
"processByStation": procMap,
|
||||
"routeStr": str,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package production
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"bj_power_mes/common/httpx"
|
||||
"bj_power_mes/internal/logic"
|
||||
"bj_power_mes/internal/svc"
|
||||
)
|
||||
|
||||
// StationPauseInternalHandler POST /api/internal/station/pause —— 工位停单/恢复(工人自主,只认当天)
|
||||
// body: {stationNo, paused, reason, operator}
|
||||
func StationPauseInternalHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req struct {
|
||||
StationNo int `json:"stationNo"`
|
||||
Paused bool `json:"paused"`
|
||||
Reason string `json:"reason"`
|
||||
Operator string `json:"operator"`
|
||||
}
|
||||
if err := httpx.ParseJSON(r, &req); err != nil {
|
||||
httpx.BadRequest(w, "请求体解析失败")
|
||||
return
|
||||
}
|
||||
if err := logic.New(svcCtx).SetStationPause(r.Context(), req.StationNo, req.Paused, req.Reason, operator(r, req.Operator)); err != nil {
|
||||
httpx.Fail(w, 2115, err.Error())
|
||||
return
|
||||
}
|
||||
httpx.OkMessage(w, "已更新工位停单状态", nil)
|
||||
}
|
||||
}
|
||||
|
||||
// StationStateInternalHandler GET /api/internal/station/state?date= —— 查询停单状态(date 空取当天)
|
||||
// 响应: {list: [{id, stationNo, date, paused, reason, operator, createdAt, updatedAt}]}
|
||||
func StationStateInternalHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
data, err := logic.New(svcCtx).ListStationStates(r.Context(), r.URL.Query().Get("date"))
|
||||
if err != nil {
|
||||
httpx.Fail(w, 2116, err.Error())
|
||||
return
|
||||
}
|
||||
httpx.Ok(w, map[string]any{"list": data})
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,7 @@ func pathID(r *http.Request) int {
|
||||
|
||||
func ListProductTypesHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
data, _, err := logic.New(svcCtx).ListProductTypes(r.Context())
|
||||
data, _, err := logic.New(svcCtx).ListProductTypes(r.Context(), r.URL.Query().Get("isActive"))
|
||||
if err != nil {
|
||||
httpx.Fail(w, 3001, err.Error())
|
||||
return
|
||||
@@ -232,7 +232,7 @@ func ListDailyPlansHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
orderNo := r.URL.Query().Get("orderNo")
|
||||
planDate := r.URL.Query().Get("planDate")
|
||||
data, err := logic.New(svcCtx).ListDailyPlans(r.Context(), orderNo, planDate)
|
||||
data, err := logic.New(svcCtx).ListDailyPlansWithCombination(r.Context(), orderNo, planDate)
|
||||
if err != nil {
|
||||
httpx.Fail(w, 3009, err.Error())
|
||||
return
|
||||
|
||||
@@ -77,6 +77,13 @@ func RegisterProductionRoutes(server *rest.Server, serverCtx *svc.ServiceContext
|
||||
server.AddRoute(rest.Route{
|
||||
Method: http.MethodGet, Path: "/api/internal/files", Handler: internal(FileDownloadInternalHandler(serverCtx)),
|
||||
})
|
||||
// ---------- 工位停单(只认当天;工人自主停/恢复,上位机据此不下发流转指令) ----------
|
||||
server.AddRoute(rest.Route{
|
||||
Method: http.MethodPost, Path: "/api/internal/station/pause", Handler: production.StationPauseInternalHandler(serverCtx),
|
||||
})
|
||||
server.AddRoute(rest.Route{
|
||||
Method: http.MethodGet, Path: "/api/internal/station/state", Handler: production.StationStateInternalHandler(serverCtx),
|
||||
})
|
||||
// ---------- 工位终端 0/13 上下线 + 在制品(第五步 5.2/5.4)----------
|
||||
server.AddRoute(rest.Route{
|
||||
Method: http.MethodGet, Path: "/api/internal/workpieces/in-process", Handler: internal(production.InProcessInternalHandler(serverCtx)),
|
||||
@@ -265,10 +272,9 @@ func RegisterProductionRoutes(server *rest.Server, serverCtx *svc.ServiceContext
|
||||
// 产品型号级全景流程卡(D2):按型号聚合 工位组合→工艺流程/步骤→物料清单→检验卡项
|
||||
{Method: http.MethodGet, Path: "/process-card/model", Handler: ModelProcessCardHandler(serverCtx)},
|
||||
|
||||
// ---------- 工位派工(工位↔工序,替代原工序组合串) ----------
|
||||
{Method: http.MethodGet, Path: "/station-process", Handler: production.ListStationProcessHandler(serverCtx)},
|
||||
{Method: http.MethodPost, Path: "/station-process/save", Handler: production.SaveStationProcessHandler(serverCtx)},
|
||||
{Method: http.MethodGet, Path: "/station-process/route", Handler: production.RouteStationProcessHandler(serverCtx)},
|
||||
// ---------- 工艺物料清单(flow_material,一份工艺定义一份清单) ----------
|
||||
{Method: http.MethodGet, Path: "/process-flows/:id/materials", Handler: production.ListFlowMaterialsHandler(serverCtx)},
|
||||
{Method: http.MethodPost, Path: "/process-flows/:id/materials", Handler: production.SaveFlowMaterialsHandler(serverCtx)},
|
||||
|
||||
// ---------- 排产支撑视图(§A2 / §D1):可生产套数 + 支撑天数 + 最先告罄短板物料 ----------
|
||||
{Method: http.MethodGet, Path: "/production/producible-support", Handler: production.ProducibleSupportHandler(serverCtx)},
|
||||
|
||||
@@ -229,8 +229,8 @@ func PerformanceExportHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
{"按工位", "byStation", []string{"工位", "完成数", "合格", "不合格", "累计作业时长(秒)", "平均作业时长(秒)", "效率(件/小时)"}, func(r *logic.WorkloadRow) []any {
|
||||
return []any{r.StationNo, r.DoneCount, r.OkCount, r.NgCount, r.TotalDurationSec, r.AvgDurationSec, r.Efficiency}
|
||||
}},
|
||||
{"明细", "detail", []string{"操作人", "工位", "日期", "工序", "工序名", "完成数", "合格", "不合格", "累计作业时长(秒)", "平均作业时长(秒)", "效率(件/小时)"}, func(r *logic.WorkloadRow) []any {
|
||||
return []any{r.Operator, r.StationNo, r.Date, r.ProcessCode, r.ProcessName, r.DoneCount, r.OkCount, r.NgCount, r.TotalDurationSec, r.AvgDurationSec, r.Efficiency}
|
||||
{"明细", "detail", []string{"操作人", "工位", "日期", "工艺ID", "工艺名", "完成数", "合格", "不合格", "累计作业时长(秒)", "平均作业时长(秒)", "效率(件/小时)"}, func(r *logic.WorkloadRow) []any {
|
||||
return []any{r.Operator, r.StationNo, r.Date, r.FlowId, r.ProcessName, r.DoneCount, r.OkCount, r.NgCount, r.TotalDurationSec, r.AvgDurationSec, r.Efficiency}
|
||||
}},
|
||||
}
|
||||
for i, vw := range views {
|
||||
|
||||
Reference in New Issue
Block a user