Files
xk-ai-agent/internal/handler/emr_handler.go
2026-08-14 21:50:48 +08:00

212 lines
5.1 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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
}