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
|
||
}
|