package handler import ( "bytes" "fmt" "html/template" "net/http" "strings" "time" "bj_power_mes/common/httpx" "bj_power_mes/internal/logic" "bj_power_mes/internal/svc" ) // ProcessCardHandler GET /api/v1/process-card?sn=xxx // 按 SN 生成可打印流程卡(无公司名、含签名栏、可调样式),返回 text/html。 func ProcessCardHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { sn := strings.TrimSpace(r.URL.Query().Get("sn")) if sn == "" { httpx.BadRequest(w, "缺少 sn 参数") return } data, err := logic.New(svcCtx).Trace(r.Context(), sn) if err != nil { httpx.Fail(w, 2401, err.Error()) return } html, err := renderProcessCard(sn, data) if err != nil { httpx.Fail(w, 2402, "流程卡生成失败:"+err.Error()) return } w.Header().Set("Content-Type", "text/html; charset=utf-8") _, _ = w.Write([]byte(html)) } } type cardTmpl struct { Sn string OrderNo string Product string CreatedAt string Processes []cardProcess Materials []string Operator string Inspector string DoneTime string Result string } type cardProcess struct { Code string Name string Station string Operator string Status string Result string Steps []cardStep } type cardStep struct { Name string Value string OK bool } func renderProcessCard(sn string, data map[string]any) (string, error) { t := cardTmpl{Sn: sn} wp, _ := data["workpiece"].(map[string]any) if wp != nil { t.OrderNo = strAny(wp["orderNo"]) t.Product = strAny(wp["productType"]) if c, ok := wp["createdAt"]; ok { if ts, ok := c.(time.Time); ok { t.CreatedAt = ts.Format("2006-01-02 15:04") } else { t.CreatedAt = fmt.Sprintf("%v", c) } } } procList, _ := data["processTimeline"].([]any) for _, p := range procList { pm, _ := p.(map[string]any) cp := cardProcess{ Code: strAny(pm["processCode"]), Name: strAny(pm["processName"]), Station: strAny(pm["stationNo"]), Operator: strAny(pm["operator"]), Status: strAny(pm["status"]), Result: strAny(pm["result"]), } t.Processes = append(t.Processes, cp) } steps, _ := data["stepData"].([]any) stepByProc := map[string][]cardStep{} for _, s := range steps { sm, _ := s.(map[string]any) key := fmt.Sprintf("%v", sm["processCode"]) ok, _ := sm["isOK"].(bool) stepByProc[key] = append(stepByProc[key], cardStep{ Name: strAny(sm["stepName"]), Value: strAny(sm["valueText"]), OK: ok, }) } for i := range t.Processes { t.Processes[i].Steps = stepByProc[t.Processes[i].Code] } assoc, _ := data["associationTrace"].(map[string]any) if assoc != nil { if b, ok := assoc["batchItems"].([]any); ok { for _, x := range b { t.Materials = append(t.Materials, fmt.Sprintf("%v", x)) } } if s, ok := assoc["serialItems"].([]any); ok { for _, x := range s { t.Materials = append(t.Materials, fmt.Sprintf("%v", x)) } } t.Operator = strAny(assoc["operator"]) } done := "待完工" for _, p := range t.Processes { if p.Result == "NG" { done = "NG" } } t.Result = done const tpl = ` 流程卡 {{.Sn}}

产品流程卡

SN:{{.Sn}} 工单号:{{.OrderNo}} 产品型号:{{.Product}} 登记时间:{{.CreatedAt}} 判定:{{if eq .Result "NG"}}NG{{else}}{{.Result}}{{end}}
{{range .Processes}} {{end}}
工序工序名称工位操作人采集参数结果
工序{{.Code}}{{.Name}}{{.Station}}{{.Operator}} {{if .Steps}}{{range .Steps}}{{.Name}}={{.Value}}({{if .OK}}OK{{else}}NG{{end}}) {{end}}{{else}}-{{end}} {{if eq .Result "NG"}}NG{{else}}OK{{end}}
{{if .Materials}}

关联物料/批次:{{range .Materials}}{{.}} {{end}}

{{end}}
操作人签字:
检验员签字:
` tmpl, err := template.New("card").Parse(tpl) if err != nil { return "", err } var buf bytes.Buffer if err := tmpl.Execute(&buf, t); err != nil { return "", err } return buf.String(), nil } func strAny(v any) string { if v == nil { return "" } return fmt.Sprintf("%v", v) }