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:
@@ -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
|
||||
}
|
||||
Reference in New Issue
Block a user