212 lines
5.1 KiB
Go
212 lines
5.1 KiB
Go
|
|
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
|
|||
|
|
}
|