Files

212 lines
5.1 KiB
Go
Raw Permalink Normal View History

2026-08-14 21:50:48 +08:00
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
}