refactor: 重构产线工位与备料流程,移除冗余字段

1. 移除工单、工件、日排产中的冗余工位组合/接驳台字段
2. 新增工位接驳台编码字段与唯一约束,关联WMS接驳台主数据
3. 重构日排产为工位产量分配模式,替代原接驳台列表
4. 新增备料单工位级字段与分批出库支持
5. 移除定时同步可生产数量,改为WMS实时拉取接口
6. 过滤操作日志,排除登录/退出相关日志
7. 调整仪表盘工序展示逻辑为今日派工路线
8. 新增接驳台维护菜单与基础数据
This commit is contained in:
SunYF
2026-09-19 07:50:21 +08:00
parent 543655d441
commit 37f10d3a13
156 changed files with 4489 additions and 1285 deletions
+31 -73
View File
@@ -4,12 +4,11 @@ import (
"context"
"errors"
"sort"
"strconv"
"strings"
"time"
"bj_power_mes/ent"
"bj_power_mes/ent/dailyplan"
"bj_power_mes/ent/station"
"bj_power_mes/ent/workorder"
"bj_power_mes/ent/workpiece"
)
@@ -23,13 +22,13 @@ func parseDate(v string) (time.Time, error) {
}
type DailyPlanReq struct {
Id int `json:"id"`
OrderNo string `json:"orderNo"`
PlanDate string `json:"planDate"`
PlanQty int `json:"planQty"`
CompletedQty int `json:"completedQty"`
Status string `json:"status"`
DockCodes []string `json:"dockCodes"`
Id int `json:"id"`
OrderNo string `json:"orderNo"`
PlanDate string `json:"planDate"`
PlanQty int `json:"planQty"`
CompletedQty int `json:"completedQty"`
Status string `json:"status"`
StationPlanQty map[string]int `json:"stationPlanQty"` // 工位号(字符串键)→计划产量分配;缺省按路线均分
// 批量排产(新建时与 PlanDate 二选一):日期范围 [start,end] + 分摊模式
PlanRange []string `json:"planRange"` // [start,end] yyyy-MM-dd,批量生成多条日排产
Mode string `json:"mode"` // AVG(平均分摊)/FIXED(固定日产);单条保存时忽略
@@ -60,6 +59,10 @@ func (s *Service) SaveDailyPlan(ctx context.Context, req DailyPlanReq, operator
if req.PlanDate == "" {
return errors.New("排产日期必填")
}
spq := req.StationPlanQty
if spq == nil {
spq = map[string]int{}
}
cur, err := s.ctx.EntClient.DailyPlan.Query().
Where(dailyplan.OrderNo(req.OrderNo)).All(ctx)
if err != nil {
@@ -101,24 +104,19 @@ func (s *Service) SaveDailyPlan(ctx context.Context, req DailyPlanReq, operator
status = exist.Status
}
_, err = s.ctx.EntClient.DailyPlan.UpdateOneID(exist.ID).
SetOrderNo(req.OrderNo).SetPlanDate(req.PlanDate).SetPlanQty(req.PlanQty).SetStatus(status).
SetDockCodes(dockCodes(req.DockCodes)).Save(ctx)
SetOrderNo(req.OrderNo).SetPlanDate(req.PlanDate).SetPlanQty(req.PlanQty).SetStatus(status).SetStationPlanQty(spq).Save(ctx)
if err != nil {
return err
}
} else {
_, err = s.ctx.EntClient.DailyPlan.Create().
SetOrderNo(req.OrderNo).SetPlanDate(req.PlanDate).SetPlanQty(req.PlanQty).
SetStatus(status).SetOperator(operator).SetDockCodes(dockCodes(req.DockCodes)).Save(ctx)
SetStatus(status).SetStationPlanQty(spq).SetOperator(operator).Save(ctx)
if err != nil {
return err
}
}
// 记录该工单本次使用的接驳台到工单 context,供下次排产默认推荐
if len(dockCodes(req.DockCodes)) > 0 {
s.rememberOrderDocks(ctx, req.OrderNo, dockCodes(req.DockCodes))
}
s.ctx.EventLog.Write(ctx, "daily.plan.save", req.OrderNo, operator, "daily_plan", req.OrderNo, "保存日排产", map[string]any{"planDate": req.PlanDate, "planQty": req.PlanQty, "dockCodes": req.DockCodes})
s.ctx.EventLog.Write(ctx, "daily.plan.save", req.OrderNo, operator, "daily_plan", req.OrderNo, "保存日排产", map[string]any{"planDate": req.PlanDate, "planQty": req.PlanQty})
s.notifyDashboard()
return nil
}
@@ -188,82 +186,42 @@ func (s *Service) saveDailyPlanBatch(ctx context.Context, req DailyPlanReq, oper
continue
}
_, err = s.ctx.EntClient.DailyPlan.UpdateOneID(existOne.ID).
SetPlanQty(q).SetStatus("PENDING").SetOperator(operator).Save(ctx)
SetPlanQty(q).SetStatus("PENDING").SetStationPlanQty(map[string]int{}).SetOperator(operator).Save(ctx)
} else {
_, err = s.ctx.EntClient.DailyPlan.Create().
SetOrderNo(req.OrderNo).SetPlanDate(ds).SetPlanQty(q).
SetStatus("PENDING").SetOperator(operator).SetDockCodes(dockCodes(req.DockCodes)).Save(ctx)
SetStatus("PENDING").SetStationPlanQty(map[string]int{}).SetOperator(operator).Save(ctx)
}
if err != nil {
return err
}
}
if len(dockCodes(req.DockCodes)) > 0 {
s.rememberOrderDocks(ctx, req.OrderNo, dockCodes(req.DockCodes))
}
s.ctx.EventLog.Write(ctx, "daily.plan.batch", req.OrderNo, operator, "daily_plan", req.OrderNo,
"日排产批量生成", map[string]any{"days": days, "total": sum, "mode": req.Mode, "quota": quota})
s.notifyDashboard()
return nil
}
// dockCodes 规范化接驳台列表:仅保留 DOCK01..DOCK20 / DOCK21,去重、保持顺序
func dockCodes(codes []string) []string {
seen := map[string]bool{}
out := []string{}
for _, c := range codes {
c = strings.TrimSpace(c)
if !validDockCode(c) || seen[c] {
continue
}
seen[c] = true
out = append(out, c)
}
return out
}
// validDockCode DOCK01..DOCK21
func validDockCode(c string) bool {
if len(c) != 6 || !strings.HasPrefix(c, "DOCK") {
return false
}
n, err := strconv.Atoi(c[4:])
return err == nil && n >= 1 && n <= 21
}
// rememberOrderDocks 把工单「上次使用的接驳台」写入 work_order.context,供下次排产默认推荐
func (s *Service) rememberOrderDocks(ctx context.Context, orderNo string, docks []string) {
if len(docks) == 0 {
return
}
wo, err := s.ctx.EntClient.WorkOrder.Query().Where(workorder.WorkOrderNo(orderNo)).Only(ctx)
if err != nil {
return
}
c := wo.Context
if c == nil {
c = map[string]any{}
}
c["lastDockCodes"] = docks
_ = s.ctx.EntClient.WorkOrder.UpdateOneID(wo.ID).SetContext(c).Exec(ctx)
}
// SuggestedDockCodes 查某工单「上次使用的接驳台」,供排产默认推荐
// SuggestedDockCodes 查今日工位派工(station_process)路线覆盖的接驳台编码(DOCKxx),供排产/备料默认推荐。
// 路线由工位派工定义,接驳台由各派工工位引用(station.dockCode),因此可直接由路线派生。
func (s *Service) SuggestedDockCodes(ctx context.Context, orderNo string) []string {
wo, err := s.ctx.EntClient.WorkOrder.Query().Where(workorder.WorkOrderNo(orderNo)).Only(ctx)
route, err := RouteStations(ctx, s.ctx.EntClient, TodayStr(), "")
if err != nil || len(route) == 0 {
return nil
}
sts, err := s.ctx.EntClient.Station.Query().Where(station.StationNoIn(route...)).All(ctx)
if err != nil {
return nil
}
if v, ok := wo.Context["lastDockCodes"].([]any); ok {
out := []string{}
for _, x := range v {
if s2, ok := x.(string); ok {
out = append(out, s2)
}
seen := map[string]bool{}
out := []string{}
for _, st := range sts {
if st.DockCode != "" && !seen[st.DockCode] {
seen[st.DockCode] = true
out = append(out, st.DockCode)
}
return out
}
return nil
return out
}
// syncPlansByOrderStatus 工单状态联动日排产: