refactor: 重构产线工位与备料流程,移除冗余字段
1. 移除工单、工件、日排产中的冗余工位组合/接驳台字段 2. 新增工位接驳台编码字段与唯一约束,关联WMS接驳台主数据 3. 重构日排产为工位产量分配模式,替代原接驳台列表 4. 新增备料单工位级字段与分批出库支持 5. 移除定时同步可生产数量,改为WMS实时拉取接口 6. 过滤操作日志,排除登录/退出相关日志 7. 调整仪表盘工序展示逻辑为今日派工路线 8. 新增接驳台维护菜单与基础数据
This commit is contained in:
@@ -0,0 +1,184 @@
|
||||
package logic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"bj_power_mes/ent"
|
||||
"bj_power_mes/ent/stationprocess"
|
||||
)
|
||||
|
||||
// TodayStr 当前日期 yyyy-MM-dd(派工/路由默认生效日)
|
||||
func TodayStr() string {
|
||||
return time.Now().Format("2006-01-02")
|
||||
}
|
||||
|
||||
// StationProcessReq 工位↔工序 派工请求(按 effectDate+shift 可换;同工位同天同班次唯一)
|
||||
type StationProcessReq struct {
|
||||
StationNo int `json:"stationNo"`
|
||||
ProcessCode int `json:"processCode"` // 1~12 为工序编号;0=解除派工(该工位不参与任何工序)
|
||||
EffectDate string `json:"effectDate"` // yyyy-MM-dd
|
||||
Shift string `json:"shift"` // 班次(默认空串=单班)
|
||||
Operator string `json:"operator"`
|
||||
}
|
||||
|
||||
// UpsertStationProcess 覆盖式保存单条工位派工。
|
||||
// 先按「其余既有行 + 本次新行」做连续性/单调不降校验,不合法整体回退(避免半截写入)。
|
||||
// processCode=0 表示解除该工位派工(仅删不插)。
|
||||
func (s *Service) UpsertStationProcess(ctx context.Context, req StationProcessReq) error {
|
||||
if req.StationNo <= 0 {
|
||||
return errors.New("工位号必填")
|
||||
}
|
||||
if req.EffectDate == "" {
|
||||
return errors.New("生效日期必填")
|
||||
}
|
||||
// 拟生效集合:取同一 生效日+班次 的既有行,剔除本工位旧行,再并入本次新行
|
||||
rows, err := s.ctx.EntClient.StationProcess.Query().
|
||||
Where(stationprocess.EffectDate(req.EffectDate), stationprocess.Shift(req.Shift)).
|
||||
Order(ent.Asc(stationprocess.FieldStationNo)).All(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
merged := make([]*ent.StationProcess, 0, len(rows)+1)
|
||||
for _, r := range rows {
|
||||
if r.StationNo == req.StationNo {
|
||||
continue
|
||||
}
|
||||
merged = append(merged, r)
|
||||
}
|
||||
if req.ProcessCode > 0 {
|
||||
merged = append(merged, &ent.StationProcess{
|
||||
StationNo: req.StationNo,
|
||||
ProcessCode: req.ProcessCode,
|
||||
EffectDate: req.EffectDate,
|
||||
Shift: req.Shift,
|
||||
})
|
||||
}
|
||||
// 连续性校验:已派工工位(processCode>0)沿工位号必须单调不降、不可穿插。
|
||||
// 未派工(processCode=0)工位视为中性、不参与校验(可穿插在已派工工位之间)。
|
||||
if err := ValidateStationProcessContinuity(merged); err != nil {
|
||||
return err
|
||||
}
|
||||
// 覆盖式写入:删旧 + 插新(唯一索引兜底防重复)
|
||||
if _, err := s.ctx.EntClient.StationProcess.Delete().
|
||||
Where(stationprocess.EffectDate(req.EffectDate), stationprocess.Shift(req.Shift), stationprocess.StationNo(req.StationNo)).
|
||||
Exec(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
if req.ProcessCode > 0 {
|
||||
if _, err := s.ctx.EntClient.StationProcess.Create().
|
||||
SetStationNo(req.StationNo).SetProcessCode(req.ProcessCode).
|
||||
SetEffectDate(req.EffectDate).SetShift(req.Shift).SetOperator(req.Operator).Save(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
s.ctx.EventLog.Write(ctx, "station.process.save", req.EffectDate, req.Operator, "station_process", req.EffectDate,
|
||||
"保存工位派工", map[string]any{"stationNo": req.StationNo, "processCode": req.ProcessCode, "shift": req.Shift})
|
||||
} else {
|
||||
s.ctx.EventLog.Write(ctx, "station.process.clear", req.EffectDate, req.Operator, "station_process", req.EffectDate,
|
||||
"解除工位派工", map[string]any{"stationNo": req.StationNo, "shift": req.Shift})
|
||||
}
|
||||
s.notifyDashboard()
|
||||
return nil
|
||||
}
|
||||
|
||||
// ListStationProcess 查询某 生效日+班次 的全部派工(按工位号升序)
|
||||
func (s *Service) ListStationProcess(ctx context.Context, effectDate, shift string) ([]*ent.StationProcess, error) {
|
||||
if effectDate == "" {
|
||||
effectDate = TodayStr()
|
||||
}
|
||||
return s.ctx.EntClient.StationProcess.Query().
|
||||
Where(stationprocess.EffectDate(effectDate), stationprocess.Shift(shift)).
|
||||
Order(ent.Asc(stationprocess.FieldStationNo)).All(ctx)
|
||||
}
|
||||
|
||||
// ValidateStationProcessContinuity 校验工位派工连续性:
|
||||
// 已派工工位(processCode>0)按工位号升序必须单调不降(不可出现 1→2→1 的穿插/回退)。
|
||||
// processCode=0(未派工)工位中性、不参与校验。
|
||||
func ValidateStationProcessContinuity(rows []*ent.StationProcess) error {
|
||||
type pair struct {
|
||||
stationNo int
|
||||
processCode int
|
||||
}
|
||||
ps := make([]pair, 0, len(rows))
|
||||
for _, r := range rows {
|
||||
if r.ProcessCode > 0 {
|
||||
ps = append(ps, pair{r.StationNo, r.ProcessCode})
|
||||
}
|
||||
}
|
||||
sort.Slice(ps, func(i, j int) bool { return ps[i].stationNo < ps[j].stationNo })
|
||||
for i := 1; i < len(ps); i++ {
|
||||
if ps[i].processCode < ps[i-1].processCode {
|
||||
return errors.New("工位派工不合法:工序编号必须沿工位号单调不降、不可穿插(如 3/5/7 号位同为工序1 合法,但 4 号位不得穿插其他工序)")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RouteStations 派生某 生效日+班次 的工位路线(按工位号升序的工位号列表)。
|
||||
func RouteStations(ctx context.Context, client *ent.Client, effectDate, shift string) ([]int, error) {
|
||||
rows, err := client.StationProcess.Query().
|
||||
Where(stationprocess.EffectDate(effectDate), stationprocess.Shift(shift)).
|
||||
Order(ent.Asc(stationprocess.FieldStationNo)).All(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := make([]int, 0, len(rows))
|
||||
for _, r := range rows {
|
||||
if r.ProcessCode > 0 {
|
||||
out = append(out, r.StationNo)
|
||||
}
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// RouteStationProcessCodeMap 派生 工位号 → 工序编号 映射(仅含已派工工位)。
|
||||
func RouteStationProcessCodeMap(ctx context.Context, client *ent.Client, effectDate, shift string) (map[int]int, error) {
|
||||
rows, err := client.StationProcess.Query().
|
||||
Where(stationprocess.EffectDate(effectDate), stationprocess.Shift(shift)).All(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m := map[int]int{}
|
||||
for _, r := range rows {
|
||||
if r.ProcessCode > 0 {
|
||||
m[r.StationNo] = r.ProcessCode
|
||||
}
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// RouteProcessCodes 派生某 生效日+班次 的工序编号集合(去重、升序)。
|
||||
func RouteProcessCodes(ctx context.Context, client *ent.Client, effectDate, shift string) ([]int, error) {
|
||||
rows, err := client.StationProcess.Query().
|
||||
Where(stationprocess.EffectDate(effectDate), stationprocess.Shift(shift)).All(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
seen := map[int]bool{}
|
||||
out := []int{}
|
||||
for _, r := range rows {
|
||||
if r.ProcessCode > 0 && !seen[r.ProcessCode] {
|
||||
seen[r.ProcessCode] = true
|
||||
out = append(out, r.ProcessCode)
|
||||
}
|
||||
}
|
||||
sort.Ints(out)
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// RouteStationsStr 派生工位路线字符串(逗号分隔,供追溯/看板展示)。
|
||||
func RouteStationsStr(ctx context.Context, client *ent.Client, effectDate, shift string) (string, error) {
|
||||
rs, err := RouteStations(ctx, client, effectDate, shift)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
parts := make([]string, len(rs))
|
||||
for i, n := range rs {
|
||||
parts[i] = strconv.Itoa(n)
|
||||
}
|
||||
return strings.Join(parts, ","), nil
|
||||
}
|
||||
Reference in New Issue
Block a user