package logic import ( "context" "encoding/json" "errors" "strings" "time" "bj_power_mes/ent" "bj_power_mes/ent/inspectionrecord" ) // FlexStr 兼容客户端把数字字段传成 number 或 string(前端 stationNo 用数字, // 标准 JSON 反序列化进 string 会报错 → 400 请求体解析失败) type FlexStr string func (f *FlexStr) UnmarshalJSON(b []byte) error { s := strings.TrimSpace(string(b)) s = strings.Trim(s, `"`) if s == "null" { s = "" } *f = FlexStr(s) return nil } // InspectionReq 巡检记录请求(PAD 巡检终端,块8) type InspectionReq struct { Category string `json:"category"` // CHECKIN/POINT/PROCESS/DONE/ALARM StationNo FlexStr `json:"stationNo"` Shift string `json:"shift"` // 早/中/晚 OrderNo string `json:"orderNo"` // 工单号 Sn string `json:"sn"` // 工件SN Result string `json:"result"` // OK/NG Items []string `json:"items"` // 点检项/异常类型 Photo string `json:"photo"` // 拍照文件名 Remark string `json:"remark"` // 文字描述/签字 Operator string `json:"operator"` // 操作人 } // CreateInspection 提交巡检记录;ALARM 类型同时触发看板报警(SSE + 清缓存) func (s *Service) CreateInspection(ctx context.Context, req InspectionReq, operator string) error { if req.Category == "" { return errors.New("记录类型必填") } category := req.Category if !oneOf(category, "CHECKIN", "POINT", "PROCESS", "DONE", "ALARM") { return errors.New("记录类型不合法") } result := req.Result if result == "" { result = "OK" } if req.Operator == "" { req.Operator = operator } _, err := s.ctx.EntClient.InspectionRecord.Create(). SetCategory(category). SetStationNo(string(req.StationNo)). SetShift(req.Shift). SetOrderNo(req.OrderNo). SetSn(req.Sn). SetResult(result). SetItems(req.Items). SetPhoto(req.Photo). SetRemark(req.Remark). SetOperator(req.Operator). Save(ctx) if err != nil { return err } s.ctx.EventLog.Write(ctx, "inspection."+category, req.OrderNo, req.Operator, "inspection_record", req.Sn, "巡检终端提交", map[string]any{"category": category, "stationNo": string(req.StationNo), "result": result}) if category == "ALARM" { // 触发看板报警:SSE 推送 + 使 Redis 看板报警缓存失效 alarmJSON, _ := json.Marshal(map[string]any{ "level": "CRITICAL", "type": "inspection_alarm", "message": "巡检异常上报:" + req.Remark, "station": string(req.StationNo), "time": time.Now().Format("2006-01-02 15:04:05"), }) if s.ctx.SSE != nil { s.ctx.SSE.Publish("dashboard.alarm", string(alarmJSON)) } if s.ctx.RedisClient != nil { _, _ = s.ctx.RedisClient.Del("dashboard:alarms") } } s.notifyDashboard() return nil } // ListInspections 查询巡检记录(不分页,供内部/导出用,封顶 1000) func (s *Service) ListInspections(ctx context.Context, category, operator, from, to string) ([]*ent.InspectionRecord, error) { rows, _, err := s.ListInspectionsPaged(ctx, category, operator, from, to, "", "", 1, 1000) return rows, err } // ListInspectionsPaged 分页查询巡检记录,返回 {rows, total};排序 id desc 最新置顶 // orderNo/stationNo:2026-09-08 补充筛选(大小写不敏感) func (s *Service) ListInspectionsPaged(ctx context.Context, category, operator, from, to, orderNo, stationNo string, page, pageSize int) ([]*ent.InspectionRecord, int, error) { q := s.ctx.EntClient.InspectionRecord.Query() if category != "" { q = q.Where(inspectionrecord.Category(category)) } if orderNo != "" { q = q.Where(inspectionrecord.OrderNoEqualFold(orderNo)) } if stationNo != "" { q = q.Where(inspectionrecord.StationNoEqualFold(stationNo)) } if operator != "" { q = q.Where(inspectionrecord.Operator(operator)) } if from != "" { if f, err := time.Parse("2006-01-02", from); err == nil { q = q.Where(inspectionrecord.CreatedAtGTE(f)) } } if to != "" { if t, err := time.Parse("2006-01-02", to); err == nil { q = q.Where(inspectionrecord.CreatedAtLT(t.Add(24 * time.Hour))) } } total, err := q.Count(ctx) if err != nil { return nil, 0, err } rows, err := q.Order(ent.Desc(inspectionrecord.FieldID)). Offset((page - 1) * pageSize).Limit(pageSize).All(ctx) return rows, total, err } func oneOf(v string, items ...string) bool { for _, it := range items { if v == it { return true } } return false }