fix: rebuild MES as compilable go-zero+ent backend (renamed bj_power_mes), restore 3 workstation projects from pristine original, align naming; all Go projects go build clean

This commit is contained in:
SunYF
2026-08-27 10:56:41 +08:00
parent 380715f8a9
commit 371b494c54
523 changed files with 67923 additions and 24758 deletions
@@ -0,0 +1,49 @@
package reports
import (
"context"
"time"
"bj_power_mes/ent"
"bj_power_mes/internal/eventbus"
"bj_power_mes/internal/svc"
"bj_power_mes/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type AckAlarmLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// 确认报警
func NewAckAlarmLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AckAlarmLogic {
return &AckAlarmLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *AckAlarmLogic) AckAlarm(req *types.AckAlarmReq) (resp *types.AckAlarmReply, err error) {
now := time.Now()
a, err := l.svcCtx.EntClient.Alarm.UpdateOneID(req.Id).
SetResolved(true).
SetResolvedAt(now).
Save(l.ctx)
if ent.IsNotFound(err) {
return nil, err
} else if err != nil {
return nil, err
}
l.svcCtx.EventBus.Publish(l.ctx, eventbus.NewAlarmAckedEvent(a.ID))
return &types.AckAlarmReply{
Id: a.ID,
Resolved: true,
ResolvedAt: now.UnixMilli(),
}, nil
}
@@ -0,0 +1,60 @@
package reports
import (
"context"
"encoding/json"
"bj_power_mes/ent"
"bj_power_mes/ent/eventlog"
"bj_power_mes/internal/svc"
"bj_power_mes/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type GetLatestEventLogsLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewGetLatestEventLogsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetLatestEventLogsLogic {
return &GetLatestEventLogsLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *GetLatestEventLogsLogic) GetLatestEventLogs(req *types.LatestEventLogReq) (resp []types.EventLog, err error) {
logs, err := l.svcCtx.EntClient.EventLog.Query().
Order(ent.Desc(eventlog.FieldCreatedAt)).
Limit(req.Limit).
All(l.ctx)
if err != nil {
return nil, err
}
result := make([]types.EventLog, 0, len(logs))
for _, lg := range logs {
payload, _ := json.Marshal(lg.Payload)
result = append(result, types.EventLog{
Id: lg.ID,
EventType: lg.EventType,
SourceId: lg.SourceId,
EntityType: lg.EntityType,
EntityId: lg.EntityId,
EntityVersion: lg.EntityVersion,
Description: lg.Description,
Payload: string(payload),
CreatedAt: lg.CreatedAt.Format("2006-01-02 15:04:05"),
JobId: lg.JobId,
WorkpieceNo: lg.WorkpieceNo,
EquipmentId: lg.EquipmentId,
EquipmentName: lg.EquipmentName,
TempSlotNo: lg.TempSlotNo,
})
}
return result, nil
}
@@ -0,0 +1,202 @@
package reports
import (
"context"
"fmt"
"math"
"time"
"bj_power_mes/internal/svc"
"bj_power_mes/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type ProductionReportLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewProductionReportLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ProductionReportLogic {
return &ProductionReportLogic{Logger: logx.WithContext(ctx), ctx: ctx, svcCtx: svcCtx}
}
func (l *ProductionReportLogic) ProductionReport(req *types.ProductionReportReq) (*types.ProductionReportReply, error) {
startMonth, endMonth := parseMonthRange(req.StartTime, req.EndTime)
granularity := req.Granularity
if granularity != "day" {
granularity = "month"
}
timeFormat := "YYYY-MM"
if granularity == "day" {
timeFormat = "YYYY-MM-DD"
}
trend, totalCompleted, totalScrapped, err := l.queryTrend(startMonth, endMonth, timeFormat)
if err != nil {
return nil, err
}
qualRate := calcQualRate(totalCompleted, totalScrapped)
planRate, err := l.queryPlanAchievement(startMonth, endMonth)
if err != nil {
return nil, err
}
return &types.ProductionReportReply{
Summary: types.ProductionReportSummary{
TotalCompleted: totalCompleted,
TotalScrapped: totalScrapped,
QualificationRate: qualRate,
PlanAchievementRate: planRate,
},
Trend: trend,
}, nil
}
type trendRow struct {
Period string
Category string
Completed int
Scrapped int
}
func (l *ProductionReportLogic) queryTrend(startMonth, endMonth, timeFormat string) ([]types.ProductionTrendItem, int, int, error) {
query := fmt.Sprintf(`
SELECT to_char(j.updated_at, '%s') AS period,
pt.category,
COUNT(*) FILTER (WHERE j.status = 'COMPLETED') AS completed,
COUNT(*) FILTER (WHERE j.status = 'SCRAPPED') AS scrapped
FROM job j
JOIN product_type pt ON j.product_type_id = pt.id
WHERE j.updated_at IS NOT NULL
AND j.status IN ('COMPLETED', 'SCRAPPED')
AND to_char(j.updated_at, 'YYYY-MM') >= $1
AND to_char(j.updated_at, 'YYYY-MM') <= $2
GROUP BY period, pt.category
ORDER BY period, pt.category`, timeFormat)
rows, err := l.svcCtx.DB.QueryContext(l.ctx, query, startMonth, endMonth)
if err != nil {
return nil, 0, 0, fmt.Errorf("query trend: %w", err)
}
defer rows.Close()
var rawRows []trendRow
totalCompleted := 0
totalScrapped := 0
for rows.Next() {
var r trendRow
if err := rows.Scan(&r.Period, &r.Category, &r.Completed, &r.Scrapped); err != nil {
return nil, 0, 0, fmt.Errorf("scan trend: %w", err)
}
rawRows = append(rawRows, r)
totalCompleted += r.Completed
totalScrapped += r.Scrapped
}
// 按 period 分组
periodMap := make(map[string][]types.CategoryStat)
for _, r := range rawRows {
periodMap[r.Period] = append(periodMap[r.Period], types.CategoryStat{
Category: r.Category,
Completed: r.Completed,
Scrapped: r.Scrapped,
QualificationRate: calcQualRate(r.Completed, r.Scrapped),
})
}
// fillCategories 确保每个 period 都有 3 个 category 条目
fillCategories := func(period string, existing []types.CategoryStat) types.ProductionTrendItem {
existMap := make(map[string]types.CategoryStat, len(existing))
for _, c := range existing {
existMap[c.Category] = c
}
var cats []types.CategoryStat
for _, cat := range []string{"A6VM107", "A6VM160", "A6VM200"} {
if s, ok := existMap[cat]; ok {
cats = append(cats, s)
} else {
cats = append(cats, types.CategoryStat{Category: cat})
}
}
return types.ProductionTrendItem{Period: period, Categories: cats}
}
var trend []types.ProductionTrendItem
current, _ := time.Parse("2006-01", startMonth)
end, _ := time.Parse("2006-01", endMonth)
if timeFormat == "YYYY-MM-DD" {
// 日粒度:只填充有数据的日期,去重
seen := make(map[string]bool)
for _, r := range rawRows {
if seen[r.Period] {
continue
}
seen[r.Period] = true
trend = append(trend, fillCategories(r.Period, periodMap[r.Period]))
}
} else {
// 月粒度:逐月遍历,补零
for !current.After(end) {
month := current.Format("2006-01")
trend = append(trend, fillCategories(month, periodMap[month]))
current = current.AddDate(0, 1, 0)
}
}
return trend, totalCompleted, totalScrapped, nil
}
func (l *ProductionReportLogic) queryPlanAchievement(startMonth, endMonth string) (float64, error) {
query := `
SELECT COALESCE(SUM(quantity), 0), COALESCE(SUM(finished_num), 0)
FROM work_order
WHERE status = 'COMPLETED'
AND completed_at IS NOT NULL
AND to_char(completed_at, 'YYYY-MM') >= $1
AND to_char(completed_at, 'YYYY-MM') <= $2`
var totalQty, totalFinished int64
err := l.svcCtx.DB.QueryRowContext(l.ctx, query, startMonth, endMonth).Scan(&totalQty, &totalFinished)
if err != nil {
return 0, fmt.Errorf("query plan achievement: %w", err)
}
if totalQty == 0 {
return 0, nil
}
return math.Round(float64(totalFinished)/float64(totalQty)*10000) / 10000, nil
}
func calcQualRate(completed, scrapped int) float64 {
total := completed + scrapped
if total == 0 {
return 0
}
return math.Round(float64(completed)/float64(total)*10000) / 10000
}
func parseMonthRange(startStr, endStr string) (string, string) {
now := time.Now()
defaultEnd := now.Format("2006-01")
defaultStart := now.AddDate(0, -11, 0).Format("2006-01")
start := defaultStart
end := defaultEnd
if startStr != "" {
if t, err := time.Parse("2006-01", startStr); err == nil {
start = t.Format("2006-01")
}
}
if endStr != "" {
if t, err := time.Parse("2006-01", endStr); err == nil {
end = t.Format("2006-01")
}
}
return start, end
}
@@ -0,0 +1,95 @@
package reports
import (
"context"
"fmt"
"time"
"bj_power_mes/constants"
"bj_power_mes/ent"
"bj_power_mes/ent/alarm"
"bj_power_mes/internal/svc"
"bj_power_mes/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type QueryAlarmsLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// 查询报警信息
func NewQueryAlarmsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *QueryAlarmsLogic {
return &QueryAlarmsLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *QueryAlarmsLogic) QueryAlarms(req *types.AlarmQueryParams) (resp *types.QueryAlarmsReply, err error) {
q := l.svcCtx.EntClient.Alarm.Query()
if req.Level != "" {
q.Where(alarm.LevelEQ(constants.AlarmLevel(req.Level)))
}
if req.EquipmentId > 0 {
q.Where(alarm.EquipmentIdEQ(req.EquipmentId))
}
if req.Resolved != nil {
q.Where(alarm.ResolvedEQ(*req.Resolved))
}
if req.StartTime != nil {
q.Where(alarm.CreatedAtGTE(time.UnixMilli(*req.StartTime)))
}
if req.EndTime != nil {
q.Where(alarm.CreatedAtLTE(time.UnixMilli(*req.EndTime)))
}
total, err := q.Count(l.ctx)
if err != nil {
return nil, fmt.Errorf("count alarms: %w", err)
}
q.Order(ent.Desc(alarm.FieldCreatedAt))
offset := (req.Page - 1) * req.Limit
records, err := q.Offset(offset).Limit(req.Limit).All(l.ctx)
if err != nil {
return nil, fmt.Errorf("query alarms: %w", err)
}
items := make([]types.Alarm, len(records))
for i, a := range records {
item := types.Alarm{
Id: a.ID,
AlarmCode: a.AlarmCode,
AlarmMessage: a.AlarmMessage,
Level: string(a.Level),
Source: a.Source,
Resolved: a.Resolved,
}
if a.EquipmentId != nil {
item.EquipmentId = *a.EquipmentId
}
if a.JobId != nil {
item.JobId = *a.JobId
}
if a.ResolvedAt != nil {
item.ResolvedAt = a.ResolvedAt.Format("2006-01-02T15:04:05Z07:00")
}
item.CreatedAt = a.CreatedAt.Format("2006-01-02T15:04:05Z07:00")
items[i] = item
}
return &types.QueryAlarmsReply{
PageReply: types.PageReply{
Page: req.Page,
Limit: req.Limit,
Total: total,
},
Data: items,
}, nil
}
@@ -0,0 +1,100 @@
package reports
import (
"context"
"encoding/json"
"time"
"bj_power_mes/ent"
enteventlog "bj_power_mes/ent/eventlog"
"bj_power_mes/ent/predicate"
"bj_power_mes/internal/svc"
"bj_power_mes/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type QueryEventLogsLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewQueryEventLogsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *QueryEventLogsLogic {
return &QueryEventLogsLogic{Logger: logx.WithContext(ctx), ctx: ctx, svcCtx: svcCtx}
}
func (l *QueryEventLogsLogic) QueryEventLogs(req *types.QueryEventLogsReq) (resp *types.QueryEventLogsReply, err error) {
var conditions []predicate.EventLog
if req.EventType != "" {
conditions = append(conditions, enteventlog.EventType(req.EventType))
}
if req.EntityType != "" {
conditions = append(conditions, enteventlog.EntityType(req.EntityType))
}
if req.StartTime != "" {
if t, parseErr := time.ParseInLocation("2006-01-02", req.StartTime, time.Local); parseErr == nil {
conditions = append(conditions, enteventlog.CreatedAtGTE(t))
}
}
if req.EndTime != "" {
if t, parseErr := time.ParseInLocation("2006-01-02", req.EndTime, time.Local); parseErr == nil {
conditions = append(conditions, enteventlog.CreatedAtLTE(t.AddDate(0, 0, 1)))
}
}
if req.WorkpieceNo != "" {
conditions = append(conditions, enteventlog.WorkpieceNoContains(req.WorkpieceNo))
}
if req.JobId > 0 {
conditions = append(conditions, enteventlog.JobId(req.JobId))
}
q := l.svcCtx.EntClient.EventLog.Query().Where(conditions...)
total, err := q.Count(l.ctx)
if err != nil {
return nil, err
}
offset := (req.Page - 1) * req.Limit
logs, err := q.
Order(ent.Desc(enteventlog.FieldCreatedAt)).
Offset(offset).
Limit(req.Limit).
All(l.ctx)
if err != nil {
return nil, err
}
data := make([]types.EventLog, 0, len(logs))
for _, lg := range logs {
var payloadStr string
if lg.Payload != nil {
if b, err := json.Marshal(lg.Payload); err == nil {
payloadStr = string(b)
}
}
data = append(data, types.EventLog{
Id: lg.ID,
EventType: lg.EventType,
SourceId: lg.SourceId,
EntityType: lg.EntityType,
EntityId: lg.EntityId,
EntityVersion: int64(lg.EntityVersion),
Description: lg.Description,
Payload: payloadStr,
CreatedAt: lg.CreatedAt.Format(time.RFC3339),
JobId: int(lg.JobId),
WorkpieceNo: lg.WorkpieceNo,
EquipmentId: int(lg.EquipmentId),
EquipmentName: lg.EquipmentName,
TempSlotNo: int(lg.TempSlotNo),
})
}
return &types.QueryEventLogsReply{
PageReply: types.PageReply{Page: req.Page, Limit: req.Limit, Total: total},
Data: data,
}, nil
}