package handler import ( "net/http" "strconv" "time" "tcm-agent/internal/agent" "tcm-agent/internal/model/entity" "github.com/gin-gonic/gin" ) // ======================================================================== // 病历 HTTP 接口处理器 // ======================================================================== // 职责链: // HTTP 请求 → 参数校验 → 调用 Agent → 持久化 → 响应封装 // ======================================================================== // EMRHandler 病历接口处理器 type EMRHandler struct { agentRunner *agent.Runner emrAgent *agent.EMRGenerator } // NewEMRHandler 创建病历处理器 // // 参数: // // runner - Agent 引擎 // scene - 场景名(对应 config.yaml 中 routes 的 key) // 为空则使用默认值 "emr-generator" func NewEMRHandler(runner *agent.Runner, scene string) *EMRHandler { return &EMRHandler{ agentRunner: runner, emrAgent: agent.NewEMRGenerator(runner, scene), } } // GenerateRequest 生成病历请求体 type GenerateRequest struct { PatientID string `json:"patient_id" binding:"required"` ChiefComplaint string `json:"chief_complaint" binding:"required"` HistoryNotes string `json:"history_notes"` Allergies []string `json:"allergies"` PastIllness []string `json:"past_illness"` } // Generate 根据主诉+病史生成病历 // // POST /api/v1/emr/generate // // 完整生命周期: // 1. 参数校验 // 2. Agent 感知 → 规划 → 检索 → 工具 → 反思 → 输出 // 3. 规则引擎质控 // 4. 持久化到数据库 // 5. 返回结构化响应 func (h *EMRHandler) Generate(c *gin.Context) { // ===== ① 参数校验 ===== var req GenerateRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{ "error": "参数错误", "detail": err.Error(), "example": `{"patient_id":"P001","chief_complaint":"反复头晕3个月","history_notes":"...","allergies":[],"past_illness":[]}`, }) return } doctorID := c.GetString("user_id") // ===== ②~⑤ 调用 Agent ===== agentReq := &agent.EMRRequest{ PatientID: req.PatientID, ChiefComplaint: req.ChiefComplaint, HistoryNotes: req.HistoryNotes, Allergies: req.Allergies, PastIllness: req.PastIllness, } resp, err := h.emrAgent.Generate(c.Request.Context(), agentReq) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{ "error": "病历生成失败", "detail": err.Error(), }) return } // ===== ⑥ 持久化 ===== emr := &entity.EMR{ PatientID: req.PatientID, DoctorID: doctorID, Draft: resp.Draft, Structured: resp.Structured, Status: resp.Status, SessionID: resp.SessionID, CreatedAt: time.Now(), UpdatedAt: time.Now(), } // dao.EMR.Create(emr) // 实际项目取消注释 // ===== ⑦ 返回响应 ===== c.JSON(http.StatusOK, gin.H{ "code": 200, "message": "病历生成成功", "data": gin.H{ "session_id": resp.SessionID, "draft": resp.Draft, "structured": resp.Structured, "issues": resp.Issues, "status": resp.Status, "emr_id": emr.ID, "next_action": getNextAction(resp.Status), }, }) } // KnowledgeQA 病历书写规范问答 // // POST /api/v1/emr/qa func (h *EMRHandler) KnowledgeQA(c *gin.Context) { var req struct { Question string `json:"question" binding:"required"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(400, gin.H{"error": "问题不能为空"}) return } answer, err := h.agentRunner.MaxKB().Chat(c.Request.Context(), req.Question) if err != nil { c.JSON(500, gin.H{"error": "知识库查询失败", "detail": err.Error()}) return } c.JSON(200, gin.H{ "code": 200, "data": gin.H{ "question": req.Question, "answer": answer, "source": "MaxKB 知识库", }, }) } // GetByID 查询病历详情 // // GET /api/v1/emr/:id func (h *EMRHandler) GetByID(c *gin.Context) { idStr := c.Param("id") id, _ := strconv.ParseInt(idStr, 10, 64) c.JSON(200, gin.H{ "code": 200, "data": gin.H{ "id": id, "note": "实际项目从数据库查询", }, }) } // Update 更新病历(医生人工修改后保存) // // PUT /api/v1/emr/:id func (h *EMRHandler) Update(c *gin.Context) { idStr := c.Param("id") id, _ := strconv.ParseInt(idStr, 10, 64) var req struct { Draft string `json:"draft"` IsFinal bool `json:"is_final"` } c.ShouldBindJSON(&req) userID := c.GetString("user_id") logAudit("emr_update", userID, idStr, req.Draft) c.JSON(200, gin.H{ "code": 200, "message": "病历已更新", "data": gin.H{ "id": id, "is_final": req.IsFinal, "updated_by": userID, }, }) } // getNextAction 根据状态给出下一步建议 func getNextAction(status string) string { switch status { case "success": return "病历生成完成,请医生审核确认" case "need_revision": return "病历存在质控问题,请查看 issues 列表并修改" default: return "请检查输入信息是否完整" } } // logAudit 审计日志 func logAudit(action, userID, targetID, detail string) { // 实际项目:写入审计表 _ = action _ = userID _ = targetID _ = detail }