Files
bj_power/bj_power_mes/internal/logic/dashboard_front.go
T
SunYF b6799c9de4 feat: 完成产线与仓储系统多模块迭代升级
本次迭代覆盖MES与WMS核心业务:
1. 新增接驳台托盘传感器读取与AGV对接能力
2. 完善工单排产、备料流程与权限体系拆分
3. 优化看板接口与前端路由、样式
4. 新增操作日志、库存盘点与角色保护逻辑
5. 修复代理地址、BOM保存等已知问题
2026-09-04 14:18:53 +08:00

242 lines
7.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package logic
import (
"context"
"strings"
"time"
)
/*
看板前端契约层(bj_power_dashboard/src/types.ts
MES 内部保留一套“富”聚合(DashboardSnapshot:质量/报警/绩效/AGV 等,供后续扩展),
而看板对外只暴露甲方约定的正面指标(见 types.ts 注释):
- 不直接下发合格率/不良/报警等负面字段
- 当前订单卡片:合同 / 负责人 / 工期 / 完成度 / 可追溯
- 设备一律以“在线台数”等正面口径
此文件把富快照映射为前端 JSON,字段名与 types.ts 一一对应。
*/
// FrontToolState 单把工具(扫码枪 / 拧紧枪)
type FrontToolState struct {
Status string `json:"status"` // online / offline / busy / alarm
CurrentSn string `json:"currentSn,omitempty"`
ProcessedCount int `json:"processedCount"`
}
// FrontStation 工位(前端 Station
type FrontStation struct {
ID int `json:"id"`
Name string `json:"name"`
Status string `json:"status"` // running / idle / alarm / offline
ScanGun FrontToolState `json:"scanGun"`
TighteningGun FrontToolState `json:"tighteningGun"`
CurrentSn string `json:"currentSn"`
CurrentOperator string `json:"currentOperator"`
}
// FrontProduction 产能总览(前端 ProductionOverview
type FrontProduction struct {
OutputToday int `json:"outputToday"`
TargetToday int `json:"targetToday"`
PassCount int `json:"passCount"` // 一次通过件数(替代合格率)
TorqueCount int `json:"torqueCount"`
InLine int `json:"inLine"`
DeviceOnline int `json:"deviceOnline"`
DeviceTotal int `json:"deviceTotal"`
StationSummary StationSummary `json:"stationSummary"`
}
// FrontMovement 出入库动态(实时事件流)
type FrontMovement struct {
ID string `json:"id"`
Time string `json:"time"`
Type string `json:"type"` // in / out
Material string `json:"material"`
Qty int `json:"qty"`
Operator string `json:"operator"`
}
// FrontWarehouse 仓储动态(前端 WarehouseOverview,数据源 WMS 8890
type FrontWarehouse struct {
TotalStock int `json:"totalStock"`
MaterialTypes int `json:"materialTypes"`
InboundToday int `json:"inboundToday"`
OutboundToday int `json:"outboundToday"`
Movements []FrontMovement `json:"movements"`
}
// FrontProgress 当前订单卡片(前端 ProductionProgress
type FrontProgress struct {
OrderNo string `json:"orderNo"`
ContractNo string `json:"contractNo"`
ProductName string `json:"productName"`
Owner string `json:"owner"`
PlanStart string `json:"planStart"`
PlanEnd string `json:"planEnd"`
RemainDays int `json:"remainDays"`
TotalQty int `json:"totalQty"`
DoneQty int `json:"doneQty"`
Progress float64 `json:"progress"`
ProcessDone int `json:"processDone"`
ProcessTotal int `json:"processTotal"`
CurrentProcess string `json:"currentProcess"`
Status string `json:"status"`
TraceStepCount int `json:"traceStepCount"`
Traceable bool `json:"traceable"`
}
// FrontTrends 近 7 日产量趋势(前端 TrendData{ production: [...] }
type FrontTrends struct {
Production []TrendPoint `json:"production"`
}
// FrontDashboard 前端全量快照(与 types.ts DashboardData 对齐;isDemo 由前端标注,不回传)
type FrontDashboard struct {
UpdatedAt string `json:"updatedAt"`
Production FrontProduction `json:"production"`
Warehouse FrontWarehouse `json:"warehouse"`
Equipment []FrontStation `json:"equipment"`
Progress FrontProgress `json:"progress"`
Traces []TraceItem `json:"traces"`
Trends FrontTrends `json:"trends"`
}
// DashboardFrontSnapshot 看板前端快照:富聚合 → 前端契约映射,走同一缓存(60s + 防击穿)。
func (s *Service) DashboardFrontSnapshot(ctx context.Context) (*FrontDashboard, error) {
return cachedTyped(ctx, s, "dashboard:front", dashboardTTL, func() (*FrontDashboard, error) {
rich, err := s.DashboardSnapshot(ctx)
if err != nil {
return nil, err
}
return mapFrontDashboard(rich), nil
})
}
// mapFrontDashboard 富快照 → 前端契约。
// 展示口径按甲方要求:负面字段(合格率/不良数/报警)一律不出现。
func mapFrontDashboard(r *DashboardSnapshot) *FrontDashboard {
f := &FrontDashboard{UpdatedAt: r.UpdatedAt}
// -------- production / equipment --------
onlineDevices := 0
summary := StationSummary{}
equipment := make([]FrontStation, 0, len(r.Equipment))
for _, st := range r.Equipment {
tool := "online"
switch st.Status {
case "running":
tool = "busy"
summary.Running++
case "alarm":
tool = "alarm"
summary.Alarm++
case "offline":
tool = "offline"
summary.Offline++
default:
summary.Idle++
}
if st.Status != "offline" {
onlineDevices += 2
}
equipment = append(equipment, FrontStation{
ID: st.StationNo,
Name: st.Name,
Status: st.Status,
ScanGun: FrontToolState{Status: tool, ProcessedCount: st.DoneToday},
TighteningGun: FrontToolState{Status: tool, CurrentSn: st.CurrentSn, ProcessedCount: st.DoneToday},
CurrentSn: st.CurrentSn,
CurrentOperator: st.CurrentOperator,
})
}
f.Equipment = equipment
f.Production = FrontProduction{
OutputToday: r.Production.OutputToday,
TargetToday: r.Production.TargetToday,
PassCount: r.Production.QualityOk,
TorqueCount: r.Production.TorqueTotal,
InLine: r.Production.InLine,
DeviceOnline: onlineDevices,
DeviceTotal: len(r.Equipment) * 2,
StationSummary: summary,
}
// -------- warehouse --------
f.Warehouse = FrontWarehouse{
TotalStock: r.Warehouse.TotalQty,
MaterialTypes: r.Warehouse.MaterialTypes,
InboundToday: r.Warehouse.InToday,
OutboundToday: r.Warehouse.OutToday,
Movements: []FrontMovement{},
}
// -------- 当前订单卡片 --------
pr := FrontProgress{ProcessTotal: 12, Status: "待排产"}
if c := r.Production.CurrentOrder; c != nil {
pr = FrontProgress{
OrderNo: c.OrderNo,
ProductName: c.ProductName,
TotalQty: c.TotalQty,
DoneQty: c.DoneQty,
Progress: c.Progress,
Status: c.Status,
CurrentProcess: c.CurrentProcess,
PlanStart: shortDate(c.PlanStart),
PlanEnd: shortDate(c.PlanEnd),
RemainDays: remainDays(c.PlanEnd),
ProcessTotal: 12,
}
}
// 追溯:取最近完工工件,汇总工序实绩条数 → “全程可追溯”
stepCount := 0
allOk := true
for _, t := range r.Trace.Recent {
stepCount += t.StepCount
if t.NgCount > 0 {
allOk = false
}
}
pr.TraceStepCount = stepCount
pr.Traceable = stepCount > 0 && allOk
f.Progress = pr
// -------- traces / trends --------
f.Traces = r.Trace.Recent
if f.Traces == nil {
f.Traces = []TraceItem{}
}
f.Trends = FrontTrends{Production: r.Trends}
if f.Trends.Production == nil {
f.Trends.Production = []TrendPoint{}
}
return f
}
// shortDate "2006-01-02 15:04:05" → "2006-01-02"
func shortDate(s string) string {
if len(s) >= 10 {
return s[:10]
}
return strings.TrimSpace(s)
}
// remainDays 距交期剩余天数:按自然日差(负数 = 已超期)。
// 工期为空的订单返回 0,前端显示“—”即可。
func remainDays(planEnd string) int {
layout := dashboardTimeLayout
if len(planEnd) == 10 {
layout = "2006-01-02"
}
t, err := time.ParseInLocation(layout, strings.TrimSpace(planEnd), time.Local)
if err != nil {
return 0
}
// 取日期差(跨过当天 0 点即视为到期)
now := time.Now()
start := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location())
end := time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, t.Location())
return int(end.Sub(start).Hours() / 24)
}