Commit da795257 authored by yuguo's avatar yuguo

fix

parent 79589e01
...@@ -56,7 +56,8 @@ ...@@ -56,7 +56,8 @@
"Bash(sed:*)", "Bash(sed:*)",
"Bash(docker-compose ps:*)", "Bash(docker-compose ps:*)",
"Bash(cat web/src/app/\\\\\\(main\\\\\\)/admin/agents/page.tsx)", "Bash(cat web/src/app/\\\\\\(main\\\\\\)/admin/agents/page.tsx)",
"Bash(awk NR==68,NR==376:*)" "Bash(awk NR==68,NR==376:*)",
"Bash(LANG=en_US.UTF-8 sed:*)"
] ]
} }
} }
This diff is collapsed.
...@@ -11,6 +11,8 @@ import ( ...@@ -11,6 +11,8 @@ import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/google/uuid" "github.com/google/uuid"
"context"
internalagent "internet-hospital/internal/agent" internalagent "internet-hospital/internal/agent"
"internet-hospital/internal/model" "internet-hospital/internal/model"
"internet-hospital/internal/service/knowledgesvc" "internet-hospital/internal/service/knowledgesvc"
...@@ -25,6 +27,7 @@ import ( ...@@ -25,6 +27,7 @@ import (
"internet-hospital/internal/service/chronic" "internet-hospital/internal/service/chronic"
"internet-hospital/internal/service/health" "internet-hospital/internal/service/health"
"internet-hospital/internal/service/user" "internet-hospital/internal/service/user"
"internet-hospital/pkg/agent/tools"
"internet-hospital/pkg/ai" "internet-hospital/pkg/ai"
"internet-hospital/pkg/config" "internet-hospital/pkg/config"
"internet-hospital/pkg/database" "internet-hospital/pkg/database"
...@@ -202,6 +205,69 @@ func main() { ...@@ -202,6 +205,69 @@ func main() {
// 注入跨包回调(AgentCallFn / WorkflowTriggerFn) // 注入跨包回调(AgentCallFn / WorkflowTriggerFn)
internalagent.WireCallbacks() internalagent.WireCallbacks()
// v17: 注入问诊业务回调(避免 internal/agent ↔ internal/service 循环引用)
tools.CreateConsultFn = func(ctx context.Context, patientID, doctorID, consultType, chiefComplaint, medicalHistory string) (string, string, error) {
consultSvc := consult.NewService()
resp, err := consultSvc.CreateConsult(ctx, patientID, &consult.CreateConsultRequest{
DoctorID: doctorID,
Type: consultType,
ChiefComplaint: chiefComplaint,
MedicalHistory: medicalHistory,
})
if err != nil {
return "", "", err
}
return resp.ID, resp.SerialNumber, nil
}
tools.AcceptConsultFn = func(ctx context.Context, consultID, doctorUserID string) error {
dpSvc := doctorportal.NewService()
_, err := dpSvc.AcceptConsult(ctx, consultID, doctorUserID)
return err
}
log.Println("[Main] 问诊业务回调注入完成")
// v17 第2批: 注入慢病/续方/健康指标/排班回调
tools.CreateChronicRecordFn = func(ctx context.Context, userID, diseaseName, hospital, doctorName, currentMeds, notes string) (string, error) {
chronicSvc := chronic.NewService()
rec, err := chronicSvc.CreateChronicRecord(ctx, userID, &chronic.ChronicRecordReq{
DiseaseName: diseaseName, Hospital: hospital,
DoctorName: doctorName, CurrentMeds: currentMeds, Notes: notes,
})
if err != nil {
return "", err
}
return rec.ID, nil
}
tools.CreateRenewalFn = func(ctx context.Context, userID, chronicID, diseaseName, reason string, medicines []string) (string, error) {
chronicSvc := chronic.NewService()
rec, err := chronicSvc.CreateRenewal(ctx, userID, &chronic.RenewalReq{
ChronicID: chronicID, DiseaseName: diseaseName,
Medicines: medicines, Reason: reason,
})
if err != nil {
return "", err
}
return rec.ID, nil
}
tools.RecordHealthMetricFn = func(ctx context.Context, userID, metricType string, value1, value2 float64, unit, notes string) (string, error) {
chronicSvc := chronic.NewService()
rec, err := chronicSvc.CreateMetric(ctx, userID, &chronic.MetricReq{
MetricType: metricType, Value1: value1, Value2: value2,
Unit: unit, Notes: notes,
})
if err != nil {
return "", err
}
return rec.ID, nil
}
tools.CreateScheduleFn = func(ctx context.Context, doctorUserID, date, startTime, endTime string, maxCount int) error {
dpSvc := doctorportal.NewService()
return dpSvc.CreateSchedule(ctx, doctorUserID, []doctorportal.ScheduleSlotReq{
{Date: date, StartTime: startTime, EndTime: endTime, MaxCount: maxCount},
})
}
log.Println("[Main] 第2批业务回调注入完成")
// 设置 Gin 模式 // 设置 Gin 模式
gin.SetMode(cfg.Server.Mode) gin.SetMode(cfg.Server.Mode)
......
...@@ -16,6 +16,21 @@ func defaultAgentDefinitions() []model.AgentDefinition { ...@@ -16,6 +16,21 @@ func defaultAgentDefinitions() []model.AgentDefinition {
"search_medical_knowledge", "query_drug", "search_medical_knowledge", "query_drug",
"query_medical_record", "generate_follow_up_plan", "query_medical_record", "generate_follow_up_plan",
"send_notification", "navigate_page", "send_notification", "navigate_page",
// v17: 问诊管理
"query_consultation_list", "query_consultation_detail", "create_consultation",
// v17: 处方查询
"query_prescription_list", "query_prescription_detail",
// v17: 患者信息
"query_patient_profile", "query_health_metrics", "query_lab_reports",
// v17: 医生/科室
"query_doctor_list", "query_doctor_detail", "query_department_list",
// v17: 慢病管理
"query_chronic_records", "create_chronic_record",
"query_renewal_requests", "create_renewal_request",
// v17: 健康指标写入 + 排班查询
"record_health_metric", "query_doctor_schedule",
// v17: 支付订单查询
"query_order_list", "query_order_detail",
}) })
// 医生通用智能体 — 合并 diagnosis + prescription + follow_up 能力 // 医生通用智能体 — 合并 diagnosis + prescription + follow_up 能力
...@@ -23,6 +38,19 @@ func defaultAgentDefinitions() []model.AgentDefinition { ...@@ -23,6 +38,19 @@ func defaultAgentDefinitions() []model.AgentDefinition {
"query_medical_record", "query_symptom_knowledge", "search_medical_knowledge", "query_medical_record", "query_symptom_knowledge", "search_medical_knowledge",
"query_drug", "check_drug_interaction", "check_contraindication", "calculate_dosage", "query_drug", "check_drug_interaction", "check_contraindication", "calculate_dosage",
"generate_follow_up_plan", "send_notification", "navigate_page", "generate_follow_up_plan", "send_notification", "navigate_page",
// v17: 问诊管理
"query_consultation_list", "query_consultation_detail",
"accept_consultation", "query_waiting_queue",
// v17: 处方管理
"query_prescription_list", "query_prescription_detail", "search_medicine_catalog",
// v17: 患者信息
"query_patient_profile", "query_health_metrics", "query_lab_reports",
// v17: 慢病续方审批
"query_renewal_requests",
// v17: 排班管理
"query_doctor_schedule", "create_doctor_schedule",
// v17: 收入统计
"query_income_stats", "query_income_records",
}) })
// 管理员通用智能体 — 合并 admin_assistant + general 管理能力 // 管理员通用智能体 — 合并 admin_assistant + general 管理能力
...@@ -31,6 +59,14 @@ func defaultAgentDefinitions() []model.AgentDefinition { ...@@ -31,6 +59,14 @@ func defaultAgentDefinitions() []model.AgentDefinition {
"trigger_workflow", "request_human_review", "trigger_workflow", "request_human_review",
"list_knowledge_collections", "send_notification", "list_knowledge_collections", "send_notification",
"query_drug", "search_medical_knowledge", "navigate_page", "query_drug", "search_medical_knowledge", "navigate_page",
// v17: 业务数据查询
"query_consultation_list", "query_consultation_detail",
"query_prescription_list", "query_prescription_detail",
"generate_tool",
// v17: 管理统计 + 用户管理 + 系统日志 + 订单
"query_dashboard_stats", "query_dashboard_trend",
"query_user_list", "query_system_logs",
"query_order_list", "query_order_detail",
}) })
return []model.AgentDefinition{ return []model.AgentDefinition{
......
...@@ -6,6 +6,7 @@ import ( ...@@ -6,6 +6,7 @@ import (
"log" "log"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/google/uuid"
"internet-hospital/internal/model" "internet-hospital/internal/model"
"internet-hospital/pkg/agent" "internet-hospital/pkg/agent"
...@@ -31,6 +32,7 @@ func NewHandler() *Handler { ...@@ -31,6 +32,7 @@ func NewHandler() *Handler {
// RegisterRoutes 用户侧路由(全角色可用,仅需 JWT) // RegisterRoutes 用户侧路由(全角色可用,仅需 JWT)
func (h *Handler) RegisterRoutes(r gin.IRouter) { func (h *Handler) RegisterRoutes(r gin.IRouter) {
g := r.Group("/agent") g := r.Group("/agent")
g.POST("/sessions", h.CreateSession)
g.POST("/:agent_id/chat", h.Chat) g.POST("/:agent_id/chat", h.Chat)
g.POST("/:agent_id/chat/stream", h.ChatStream) g.POST("/:agent_id/chat/stream", h.ChatStream)
g.GET("/sessions", h.ListSessions) g.GET("/sessions", h.ListSessions)
...@@ -139,6 +141,39 @@ func (h *Handler) ListAgents(c *gin.Context) { ...@@ -139,6 +141,39 @@ func (h *Handler) ListAgents(c *gin.Context) {
response.Success(c, h.svc.ListAgents()) response.Success(c, h.svc.ListAgents())
} }
// CreateSession 创建新会话,返回 session_id
func (h *Handler) CreateSession(c *gin.Context) {
var req struct {
AgentID string `json:"agent_id" binding:"required"`
}
if err := c.ShouldBindJSON(&req); err != nil {
response.BadRequest(c, err.Error())
return
}
userID, _ := c.Get("user_id")
uid, _ := userID.(string)
role, _ := c.Get("role")
userRole, _ := role.(string)
sessionID := uuid.New().String()
session := model.AgentSession{
SessionID: sessionID,
AgentID: req.AgentID,
UserID: uid,
History: "[]",
Context: "null",
PageContext: "null",
EntityContext: "null",
Status: "active",
UserRole: userRole,
}
if err := database.GetDB().Create(&session).Error; err != nil {
response.Error(c, 500, "创建会话失败: "+err.Error())
return
}
response.Success(c, gin.H{"session_id": sessionID})
}
// ListSessions 获取用户的 Agent 会话列表 // ListSessions 获取用户的 Agent 会话列表
func (h *Handler) ListSessions(c *gin.Context) { func (h *Handler) ListSessions(c *gin.Context) {
userID, _ := c.Get("user_id") userID, _ := c.Get("user_id")
...@@ -303,14 +338,15 @@ func (h *Handler) GetDefinition(c *gin.Context) { ...@@ -303,14 +338,15 @@ func (h *Handler) GetDefinition(c *gin.Context) {
// CreateDefinition 创建新 Agent // CreateDefinition 创建新 Agent
func (h *Handler) CreateDefinition(c *gin.Context) { func (h *Handler) CreateDefinition(c *gin.Context) {
var req struct { var req struct {
AgentID string `json:"agent_id" binding:"required"` AgentID string `json:"agent_id" binding:"required"`
Name string `json:"name" binding:"required"` Name string `json:"name" binding:"required"`
Description string `json:"description"` Description string `json:"description"`
Category string `json:"category"` Category string `json:"category"`
SystemPrompt string `json:"system_prompt"` SystemPrompt string `json:"system_prompt"`
Tools []string `json:"tools"` PromptTemplateID *uint `json:"prompt_template_id"`
Skills []string `json:"skills"` Tools []string `json:"tools"`
MaxIterations int `json:"max_iterations"` Skills []string `json:"skills"`
MaxIterations int `json:"max_iterations"`
} }
if err := c.ShouldBindJSON(&req); err != nil { if err := c.ShouldBindJSON(&req); err != nil {
response.BadRequest(c, err.Error()) response.BadRequest(c, err.Error())
...@@ -322,15 +358,16 @@ func (h *Handler) CreateDefinition(c *gin.Context) { ...@@ -322,15 +358,16 @@ func (h *Handler) CreateDefinition(c *gin.Context) {
req.MaxIterations = 10 req.MaxIterations = 10
} }
def := model.AgentDefinition{ def := model.AgentDefinition{
AgentID: req.AgentID, AgentID: req.AgentID,
Name: req.Name, Name: req.Name,
Description: req.Description, Description: req.Description,
Category: req.Category, Category: req.Category,
SystemPrompt: req.SystemPrompt, SystemPrompt: req.SystemPrompt,
Tools: string(toolsJSON), PromptTemplateID: req.PromptTemplateID,
Skills: string(skillsJSON), Tools: string(toolsJSON),
MaxIterations: req.MaxIterations, Skills: string(skillsJSON),
Status: "active", MaxIterations: req.MaxIterations,
Status: "active",
} }
if err := database.GetDB().Create(&def).Error; err != nil { if err := database.GetDB().Create(&def).Error; err != nil {
response.Error(c, 500, err.Error()) response.Error(c, 500, err.Error())
...@@ -344,14 +381,16 @@ func (h *Handler) CreateDefinition(c *gin.Context) { ...@@ -344,14 +381,16 @@ func (h *Handler) CreateDefinition(c *gin.Context) {
func (h *Handler) UpdateDefinition(c *gin.Context) { func (h *Handler) UpdateDefinition(c *gin.Context) {
agentID := c.Param("agent_id") agentID := c.Param("agent_id")
var req struct { var req struct {
Name string `json:"name"` Name string `json:"name"`
Description string `json:"description"` Description string `json:"description"`
Category string `json:"category"` Category string `json:"category"`
SystemPrompt string `json:"system_prompt"` SystemPrompt string `json:"system_prompt"`
Tools []string `json:"tools"` PromptTemplateID *uint `json:"prompt_template_id"`
Skills []string `json:"skills"` ClearTemplate bool `json:"clear_template"`
MaxIterations int `json:"max_iterations"` Tools []string `json:"tools"`
Status string `json:"status"` Skills []string `json:"skills"`
MaxIterations int `json:"max_iterations"`
Status string `json:"status"`
} }
if err := c.ShouldBindJSON(&req); err != nil { if err := c.ShouldBindJSON(&req); err != nil {
response.BadRequest(c, err.Error()) response.BadRequest(c, err.Error())
...@@ -378,6 +417,11 @@ func (h *Handler) UpdateDefinition(c *gin.Context) { ...@@ -378,6 +417,11 @@ func (h *Handler) UpdateDefinition(c *gin.Context) {
if req.SystemPrompt != "" { if req.SystemPrompt != "" {
updates["system_prompt"] = req.SystemPrompt updates["system_prompt"] = req.SystemPrompt
} }
if req.PromptTemplateID != nil {
updates["prompt_template_id"] = *req.PromptTemplateID
} else if req.ClearTemplate {
updates["prompt_template_id"] = nil
}
if req.Tools != nil { if req.Tools != nil {
toolsJSON, _ := json.Marshal(req.Tools) toolsJSON, _ := json.Marshal(req.Tools)
updates["tools"] = string(toolsJSON) updates["tools"] = string(toolsJSON)
......
...@@ -62,6 +62,55 @@ func InitTools() { ...@@ -62,6 +62,55 @@ func InitTools() {
// v15: 热代码 Tool 生成器 // v15: 热代码 Tool 生成器
r.Register(&tools.CodeGenTool{}) r.Register(&tools.CodeGenTool{})
// v17: 第1批业务工具 — 问诊管理
r.Register(&tools.ConsultationListTool{DB: db})
r.Register(&tools.ConsultationDetailTool{DB: db})
r.Register(&tools.CreateConsultationTool{})
r.Register(&tools.AcceptConsultationTool{})
r.Register(&tools.WaitingQueueTool{DB: db})
// v17: 第1批业务工具 — 处方管理
r.Register(&tools.PrescriptionListTool{DB: db})
r.Register(&tools.PrescriptionDetailTool{DB: db})
r.Register(&tools.MedicineCatalogTool{DB: db})
// v17: 第1批业务工具 — 患者信息
r.Register(&tools.PatientProfileTool{DB: db})
r.Register(&tools.HealthMetricsTool{DB: db})
r.Register(&tools.LabReportsTool{DB: db})
// v17: 第2批业务工具 — 医生/科室
r.Register(&tools.DoctorListTool{DB: db})
r.Register(&tools.DoctorDetailTool{DB: db})
r.Register(&tools.DepartmentListTool{DB: db})
// v17: 第2批业务工具 — 慢病管理
r.Register(&tools.ChronicRecordsTool{DB: db})
r.Register(&tools.CreateChronicRecordTool{})
r.Register(&tools.RenewalRequestsTool{DB: db})
r.Register(&tools.CreateRenewalTool{})
// v17: 第2批业务工具 — 健康指标写入
r.Register(&tools.RecordHealthMetricTool{})
// v17: 第2批业务工具 — 排班管理
r.Register(&tools.DoctorScheduleTool{DB: db})
r.Register(&tools.CreateDoctorScheduleTool{})
// v17: 第3批业务工具 — 支付订单
r.Register(&tools.OrderListTool{DB: db})
r.Register(&tools.OrderDetailTool{DB: db})
// v17: 第3批业务工具 — 医生收入
r.Register(&tools.IncomeStatsTool{DB: db})
r.Register(&tools.IncomeRecordsTool{DB: db})
// v17: 第3批业务工具 — 管理统计
r.Register(&tools.DashboardStatsTool{DB: db})
r.Register(&tools.DashboardTrendTool{DB: db})
r.Register(&tools.UserListTool{DB: db})
r.Register(&tools.SystemLogsTool{DB: db})
// v15: 从数据库加载动态 SQL 工具 // v15: 从数据库加载动态 SQL 工具
loadDynamicSQLTools(r, db) loadDynamicSQLTools(r, db)
...@@ -75,8 +124,17 @@ func InitTools() { ...@@ -75,8 +124,17 @@ func InitTools() {
log.Println("[InitTools] ToolMonitor & ToolSelector 初始化完成") log.Println("[InitTools] ToolMonitor & ToolSelector 初始化完成")
// v16: 初始化智能工具推荐器(使用pgvector) // v16: 初始化智能工具推荐器(使用pgvector)
agent.InitToolRecommender(db, embedder) recommender := agent.InitToolRecommender(db, embedder)
log.Println("[InitTools] ToolRecommender 初始化完成") log.Println("[InitTools] ToolRecommender 初始化完成")
// v17: 自动同步工具向量索引(异步,不阻塞启动)
if recommender != nil && embedder != nil {
go func() {
if err := recommender.IndexToolEmbeddings(context.Background()); err != nil {
log.Printf("[InitTools] 工具向量索引构建失败(embedder可能未配置API key): %v", err)
}
}()
}
} }
// WireCallbacks 注入跨包回调(在 InitTools 和 GetService 初始化完成后调用) // WireCallbacks 注入跨包回调(在 InitTools 和 GetService 初始化完成后调用)
...@@ -155,6 +213,45 @@ func syncToolsToDB(r *agent.ToolRegistry) { ...@@ -155,6 +213,45 @@ func syncToolsToDB(r *agent.ToolRegistry) {
"navigate_page": "navigation", "navigate_page": "navigation",
// v15: 热代码生成 // v15: 热代码生成
"generate_tool": "code_gen", "generate_tool": "code_gen",
// v17: 问诊管理
"query_consultation_list": "consult",
"query_consultation_detail": "consult",
"create_consultation": "consult",
"accept_consultation": "consult",
"query_waiting_queue": "consult",
// v17: 处方管理
"query_prescription_list": "prescription",
"query_prescription_detail": "prescription",
"search_medicine_catalog": "pharmacy",
// v17: 患者信息
"query_patient_profile": "patient",
"query_health_metrics": "health",
"query_lab_reports": "health",
// v17: 医生/科室
"query_doctor_list": "doctor",
"query_doctor_detail": "doctor",
"query_department_list": "department",
// v17: 慢病管理
"query_chronic_records": "chronic",
"create_chronic_record": "chronic",
"query_renewal_requests": "chronic",
"create_renewal_request": "chronic",
// v17: 健康指标写入
"record_health_metric": "health",
// v17: 排班管理
"query_doctor_schedule": "schedule",
"create_doctor_schedule": "schedule",
// v17: 支付订单
"query_order_list": "payment",
"query_order_detail": "payment",
// v17: 医生收入
"query_income_stats": "income",
"query_income_records": "income",
// v17: 管理统计
"query_dashboard_stats": "dashboard",
"query_dashboard_trend": "dashboard",
"query_user_list": "user_manage",
"query_system_logs": "system",
} }
for name, tool := range r.All() { for name, tool := range r.All() {
...@@ -252,6 +349,21 @@ func buildToolKeywords(name, description, category string) string { ...@@ -252,6 +349,21 @@ func buildToolKeywords(name, description, category string) string {
"通知", "提醒", "随访", "复诊", "安全", "禁忌", "通知", "提醒", "随访", "复诊", "安全", "禁忌",
"相互作用", "剂量", "用量", "计算", "表达式", "相互作用", "剂量", "用量", "计算", "表达式",
"患者", "医生", "管理", "查询", "患者", "医生", "管理", "查询",
// v17: 新增业务关键词
"问诊", "就诊", "接诊", "挂号", "预约", "主诉",
"等候", "队列", "排队",
"档案", "信息", "资料", "健康",
"指标", "血压", "血糖", "心率", "体温",
"化验", "解读", "目录", "库存",
// v17: 第2批业务关键词
"慢病", "慢性", "续方", "续药", "审批",
"排班", "时段", "预约",
"科室", "专长", "评分", "在线",
// v17: 第3批业务关键词
"订单", "支付", "付款", "退款", "收入", "余额",
"提现", "账单", "收益", "分成",
"仪表盘", "统计", "趋势", "运营",
"用户", "日志", "操作", "系统",
} { } {
if strings.Contains(description, kw) { if strings.Contains(description, kw) {
descKeywords[kw] = true descKeywords[kw] = true
...@@ -299,6 +411,45 @@ func initToolSelector(r *agent.ToolRegistry) { ...@@ -299,6 +411,45 @@ func initToolSelector(r *agent.ToolRegistry) {
"navigate_page": "navigation", "navigate_page": "navigation",
// v15: 热代码生成 // v15: 热代码生成
"generate_tool": "code_gen", "generate_tool": "code_gen",
// v17: 问诊管理
"query_consultation_list": "consult",
"query_consultation_detail": "consult",
"create_consultation": "consult",
"accept_consultation": "consult",
"query_waiting_queue": "consult",
// v17: 处方管理
"query_prescription_list": "prescription",
"query_prescription_detail": "prescription",
"search_medicine_catalog": "pharmacy",
// v17: 患者信息
"query_patient_profile": "patient",
"query_health_metrics": "health",
"query_lab_reports": "health",
// v17: 医生/科室
"query_doctor_list": "doctor",
"query_doctor_detail": "doctor",
"query_department_list": "department",
// v17: 慢病管理
"query_chronic_records": "chronic",
"create_chronic_record": "chronic",
"query_renewal_requests": "chronic",
"create_renewal_request": "chronic",
// v17: 健康指标写入
"record_health_metric": "health",
// v17: 排班管理
"query_doctor_schedule": "schedule",
"create_doctor_schedule": "schedule",
// v17: 支付订单
"query_order_list": "payment",
"query_order_detail": "payment",
// v17: 医生收入
"query_income_stats": "income",
"query_income_records": "income",
// v17: 管理统计
"query_dashboard_stats": "dashboard",
"query_dashboard_trend": "dashboard",
"query_user_list": "user_manage",
"query_system_logs": "system",
} }
for name, tool := range r.All() { for name, tool := range r.All() {
......
This diff is collapsed.
This diff is collapsed.
...@@ -28,15 +28,15 @@ func (ChronicRecord) TableName() string { return "chronic_records" } ...@@ -28,15 +28,15 @@ func (ChronicRecord) TableName() string { return "chronic_records" }
type RenewalRequest struct { type RenewalRequest struct {
ID string `gorm:"type:uuid;primaryKey" json:"id"` ID string `gorm:"type:uuid;primaryKey" json:"id"`
UserID string `gorm:"type:uuid;index;not null" json:"user_id"` UserID string `gorm:"type:uuid;index;not null" json:"user_id"`
ChronicID string `gorm:"type:uuid;index" json:"chronic_id"` ChronicID *string `gorm:"type:uuid;index" json:"chronic_id"`
DiseaseName string `gorm:"type:varchar(100)" json:"disease_name"` DiseaseName string `gorm:"type:varchar(100)" json:"disease_name"`
Medicines string `gorm:"type:text" json:"medicines"` // JSON array Medicines string `gorm:"type:text" json:"medicines"` // JSON array
Reason string `gorm:"type:text" json:"reason"` Reason string `gorm:"type:text" json:"reason"`
Status string `gorm:"type:varchar(20);default:'pending'" json:"status"` // pending|approved|rejected Status string `gorm:"type:varchar(20);default:'pending'" json:"status"` // pending|approved|rejected
DoctorID string `gorm:"type:uuid" json:"doctor_id"` DoctorID *string `gorm:"type:uuid" json:"doctor_id"`
DoctorName string `gorm:"type:varchar(50)" json:"doctor_name"` DoctorName string `gorm:"type:varchar(50)" json:"doctor_name"`
DoctorNote string `gorm:"type:text" json:"doctor_note"` DoctorNote string `gorm:"type:text" json:"doctor_note"`
PrescriptionID string `gorm:"type:uuid" json:"prescription_id"` PrescriptionID *string `gorm:"type:uuid" json:"prescription_id"`
AIAdvice string `gorm:"type:text" json:"ai_advice"` AIAdvice string `gorm:"type:text" json:"ai_advice"`
CreatedAt time.Time `json:"created_at"` CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"` UpdatedAt time.Time `json:"updated_at"`
......
...@@ -61,7 +61,7 @@ func (h *Handler) CreateRecord(c *gin.Context) { ...@@ -61,7 +61,7 @@ func (h *Handler) CreateRecord(c *gin.Context) {
} }
r, err := h.service.CreateChronicRecord(c.Request.Context(), userID.(string), &req) r, err := h.service.CreateChronicRecord(c.Request.Context(), userID.(string), &req)
if err != nil { if err != nil {
response.Error(c, 500, "创建慢病档案失败") response.Error(c, 500, "创建慢病档案失败: "+err.Error())
return return
} }
response.Success(c, r) response.Success(c, r)
...@@ -110,7 +110,7 @@ func (h *Handler) CreateRenewal(c *gin.Context) { ...@@ -110,7 +110,7 @@ func (h *Handler) CreateRenewal(c *gin.Context) {
} }
r, err := h.service.CreateRenewal(c.Request.Context(), userID.(string), &req) r, err := h.service.CreateRenewal(c.Request.Context(), userID.(string), &req)
if err != nil { if err != nil {
response.Error(c, 500, "创建续方申请失败") response.Error(c, 500, "创建续方申请失败: "+err.Error())
return return
} }
response.Success(c, r) response.Success(c, r)
......
...@@ -93,9 +93,13 @@ func (s *Service) ListRenewals(ctx context.Context, userID string) ([]model.Rene ...@@ -93,9 +93,13 @@ func (s *Service) ListRenewals(ctx context.Context, userID string) ([]model.Rene
func (s *Service) CreateRenewal(ctx context.Context, userID string, req *RenewalReq) (*model.RenewalRequest, error) { func (s *Service) CreateRenewal(ctx context.Context, userID string, req *RenewalReq) (*model.RenewalRequest, error) {
medsJSON, _ := json.Marshal(req.Medicines) medsJSON, _ := json.Marshal(req.Medicines)
var chronicID *string
if req.ChronicID != "" {
chronicID = &req.ChronicID
}
r := &model.RenewalRequest{ r := &model.RenewalRequest{
ID: uuid.New().String(), UserID: userID, ID: uuid.New().String(), UserID: userID,
ChronicID: req.ChronicID, DiseaseName: req.DiseaseName, ChronicID: chronicID, DiseaseName: req.DiseaseName,
Medicines: string(medsJSON), Reason: req.Reason, Status: "pending", Medicines: string(medsJSON), Reason: req.Reason, Status: "pending",
} }
if err := s.db.WithContext(ctx).Create(r).Error; err != nil { if err := s.db.WithContext(ctx).Create(r).Error; err != nil {
......
package tools
import (
"context"
"fmt"
"internet-hospital/pkg/agent"
)
// CreateChronicRecordFn 创建慢病记录回调,由 main.go 注入
var CreateChronicRecordFn func(ctx context.Context, userID, diseaseName, hospital, doctorName, currentMeds, notes string) (string, error)
// CreateChronicRecordTool 创建慢病档案
type CreateChronicRecordTool struct{}
func (t *CreateChronicRecordTool) Name() string { return "create_chronic_record" }
func (t *CreateChronicRecordTool) Description() string {
return "患者创建慢病档案记录,记录慢性疾病的诊断信息和当前用药情况"
}
func (t *CreateChronicRecordTool) Parameters() []agent.ToolParameter {
return []agent.ToolParameter{
{Name: "disease_name", Type: "string", Description: "疾病名称(如高血压、糖尿病)", Required: true},
{Name: "hospital", Type: "string", Description: "确诊医院(可选)", Required: false},
{Name: "doctor_name", Type: "string", Description: "确诊医生(可选)", Required: false},
{Name: "current_meds", Type: "string", Description: "当前用药情况(可选)", Required: false},
{Name: "notes", Type: "string", Description: "备注(可选)", Required: false},
}
}
func (t *CreateChronicRecordTool) Execute(ctx context.Context, params map[string]interface{}) (interface{}, error) {
userRole, _ := ctx.Value(agent.ContextKeyUserRole).(string)
userID, _ := ctx.Value(agent.ContextKeyUserID).(string)
if userRole != "patient" {
return nil, fmt.Errorf("仅患者可创建慢病记录")
}
if userID == "" {
return nil, fmt.Errorf("未获取到用户信息")
}
diseaseName, ok := params["disease_name"].(string)
if !ok || diseaseName == "" {
return nil, fmt.Errorf("disease_name 必填")
}
hospital, _ := params["hospital"].(string)
doctorName, _ := params["doctor_name"].(string)
currentMeds, _ := params["current_meds"].(string)
notes, _ := params["notes"].(string)
if CreateChronicRecordFn == nil {
return nil, fmt.Errorf("慢病服务未初始化")
}
recordID, err := CreateChronicRecordFn(ctx, userID, diseaseName, hospital, doctorName, currentMeds, notes)
if err != nil {
return nil, fmt.Errorf("创建慢病记录失败: %v", err)
}
return map[string]interface{}{
"record_id": recordID,
"disease_name": diseaseName,
"message": "慢病档案已创建",
}, nil
}
package tools
import (
"context"
"fmt"
"internet-hospital/pkg/agent"
"gorm.io/gorm"
)
// ChronicRecordsTool 查询慢病记录
type ChronicRecordsTool struct {
DB *gorm.DB
}
func (t *ChronicRecordsTool) Name() string { return "query_chronic_records" }
func (t *ChronicRecordsTool) Description() string {
return "查询患者的慢病档案记录(疾病名称、确诊日期、当前用药、控制状态)"
}
func (t *ChronicRecordsTool) Parameters() []agent.ToolParameter {
return []agent.ToolParameter{
{Name: "patient_id", Type: "string", Description: "患者ID(医生使用,患者无需传入)", Required: false},
}
}
func (t *ChronicRecordsTool) Execute(ctx context.Context, params map[string]interface{}) (interface{}, error) {
userID, _ := ctx.Value(agent.ContextKeyUserID).(string)
userRole, _ := ctx.Value(agent.ContextKeyUserRole).(string)
targetID := userID
if pid, ok := params["patient_id"].(string); ok && pid != "" {
if userRole == "patient" && pid != userID {
return nil, fmt.Errorf("患者只能查看自己的慢病记录")
}
targetID = pid
}
if targetID == "" {
return nil, fmt.Errorf("未获取到用户信息")
}
var results []map[string]interface{}
rows, err := t.DB.WithContext(ctx).Raw(`
SELECT id, disease_name, diagnosis_date, hospital, doctor_name,
current_meds, control_status, notes, created_at
FROM chronic_records
WHERE user_id = ? AND deleted_at IS NULL
ORDER BY created_at DESC`, targetID).Rows()
if err != nil {
return map[string]interface{}{"records": []interface{}{}, "total": 0}, nil
}
defer rows.Close()
cols, _ := rows.Columns()
for rows.Next() {
row := make(map[string]interface{})
vals := make([]interface{}, len(cols))
ptrs := make([]interface{}, len(cols))
for i := range vals {
ptrs[i] = &vals[i]
}
if err := rows.Scan(ptrs...); err == nil {
for i, col := range cols {
row[col] = vals[i]
}
results = append(results, row)
}
}
return map[string]interface{}{
"records": results,
"total": len(results),
}, nil
}
package tools
import (
"context"
"fmt"
"internet-hospital/pkg/agent"
)
// AcceptConsultFn 接诊回调,由 WireCallbacks 注入
var AcceptConsultFn func(ctx context.Context, consultID, doctorUserID string) error
// AcceptConsultationTool 医生接诊
type AcceptConsultationTool struct{}
func (t *AcceptConsultationTool) Name() string { return "accept_consultation" }
func (t *AcceptConsultationTool) Description() string {
return "医生接受一个等候中的问诊,将问诊状态从pending变为in_progress"
}
func (t *AcceptConsultationTool) Parameters() []agent.ToolParameter {
return []agent.ToolParameter{
{Name: "consultation_id", Type: "string", Description: "要接诊的问诊ID", Required: true},
}
}
func (t *AcceptConsultationTool) Execute(ctx context.Context, params map[string]interface{}) (interface{}, error) {
userRole, _ := ctx.Value(agent.ContextKeyUserRole).(string)
userID, _ := ctx.Value(agent.ContextKeyUserID).(string)
if userRole != "doctor" {
return nil, fmt.Errorf("仅医生可接诊")
}
consultID, ok := params["consultation_id"].(string)
if !ok || consultID == "" {
return nil, fmt.Errorf("consultation_id 必填")
}
if AcceptConsultFn == nil {
return nil, fmt.Errorf("接诊服务未初始化")
}
if err := AcceptConsultFn(ctx, consultID, userID); err != nil {
return nil, fmt.Errorf("接诊失败: %v", err)
}
return map[string]interface{}{
"consultation_id": consultID,
"status": "in_progress",
"message": "接诊成功,问诊已开始",
}, nil
}
package tools
import (
"context"
"fmt"
"internet-hospital/pkg/agent"
)
// CreateConsultFn 创建问诊回调,由 WireCallbacks 注入
// 参数: ctx, patientID, doctorID, consultType, chiefComplaint, medicalHistory
// 返回: 问诊ID, 流水号, error
var CreateConsultFn func(ctx context.Context, patientID, doctorID, consultType, chiefComplaint, medicalHistory string) (string, string, error)
// CreateConsultationTool 创建问诊(患者发起)
type CreateConsultationTool struct{}
func (t *CreateConsultationTool) Name() string { return "create_consultation" }
func (t *CreateConsultationTool) Description() string {
return "患者创建新的问诊,需要指定医生ID、问诊类型和主诉。创建成功后返回问诊ID和流水号"
}
func (t *CreateConsultationTool) Parameters() []agent.ToolParameter {
return []agent.ToolParameter{
{Name: "doctor_id", Type: "string", Description: "接诊医生ID(从 query_doctor_list 获取)", Required: true},
{Name: "type", Type: "string", Description: "问诊类型", Required: true, Enum: []string{"text", "video"}},
{Name: "chief_complaint", Type: "string", Description: "主诉(患者症状描述)", Required: true},
{Name: "medical_history", Type: "string", Description: "既往病史(可选)", Required: false},
}
}
func (t *CreateConsultationTool) Execute(ctx context.Context, params map[string]interface{}) (interface{}, error) {
userRole, _ := ctx.Value(agent.ContextKeyUserRole).(string)
userID, _ := ctx.Value(agent.ContextKeyUserID).(string)
if userRole != "patient" {
return nil, fmt.Errorf("仅患者可创建问诊")
}
if userID == "" {
return nil, fmt.Errorf("未获取到用户信息")
}
doctorID, ok := params["doctor_id"].(string)
if !ok || doctorID == "" {
return nil, fmt.Errorf("doctor_id 必填")
}
consultType, ok := params["type"].(string)
if !ok || (consultType != "text" && consultType != "video") {
return nil, fmt.Errorf("type 必须为 text 或 video")
}
chiefComplaint, ok := params["chief_complaint"].(string)
if !ok || chiefComplaint == "" {
return nil, fmt.Errorf("chief_complaint 必填")
}
medicalHistory, _ := params["medical_history"].(string)
if CreateConsultFn == nil {
return nil, fmt.Errorf("问诊服务未初始化")
}
consultID, serialNumber, err := CreateConsultFn(ctx, userID, doctorID, consultType, chiefComplaint, medicalHistory)
if err != nil {
return nil, fmt.Errorf("创建问诊失败: %v", err)
}
return map[string]interface{}{
"consultation_id": consultID,
"serial_number": serialNumber,
"status": "pending",
"message": "问诊已创建,等待医生接诊",
}, nil
}
package tools
import (
"context"
"fmt"
"internet-hospital/pkg/agent"
"gorm.io/gorm"
)
// ConsultationDetailTool 查询问诊详情
type ConsultationDetailTool struct {
DB *gorm.DB
}
func (t *ConsultationDetailTool) Name() string { return "query_consultation_detail" }
func (t *ConsultationDetailTool) Description() string {
return "查询问诊详情,包括患者信息、医生信息、主诉、诊断、消息记录,支持UUID或流水号(C开头)查询"
}
func (t *ConsultationDetailTool) Parameters() []agent.ToolParameter {
return []agent.ToolParameter{
{Name: "consultation_id", Type: "string", Description: "问诊ID(UUID)或流水号(C开头,如C20260305-0001)", Required: true},
{Name: "include_messages", Type: "boolean", Description: "是否包含消息记录,默认true", Required: false},
}
}
func (t *ConsultationDetailTool) Execute(ctx context.Context, params map[string]interface{}) (interface{}, error) {
userID, _ := ctx.Value(agent.ContextKeyUserID).(string)
userRole, _ := ctx.Value(agent.ContextKeyUserRole).(string)
consultID, ok := params["consultation_id"].(string)
if !ok || consultID == "" {
return nil, fmt.Errorf("consultation_id 必填")
}
includeMessages := true
if v, ok := params["include_messages"].(bool); ok {
includeMessages = v
}
// 支持流水号查询
resolvedID := consultID
if len(consultID) > 0 && consultID[0] == 'C' {
var id string
if err := t.DB.WithContext(ctx).Raw("SELECT id FROM consultations WHERE serial_number = ?", consultID).Scan(&id).Error; err != nil || id == "" {
return nil, fmt.Errorf("流水号 %s 对应的问诊不存在", consultID)
}
resolvedID = id
}
// 查询问诊详情
var detail map[string]interface{}
detailRows, err := t.DB.WithContext(ctx).Raw(`
SELECT c.id, c.serial_number, c.patient_id, c.doctor_id, c.type, c.status,
c.chief_complaint, c.medical_history, c.diagnosis, c.summary,
c.started_at, c.ended_at, c.created_at, c.satisfaction_score,
d.name as doctor_name, d.title as doctor_title, dep.name as department_name,
u.real_name as patient_name, u.phone as patient_phone
FROM consultations c
LEFT JOIN doctors d ON c.doctor_id = d.id
LEFT JOIN departments dep ON d.department_id = dep.id
LEFT JOIN users u ON c.patient_id = u.id
WHERE c.id = ? AND c.deleted_at IS NULL`, resolvedID).Rows()
if err != nil {
return nil, fmt.Errorf("问诊不存在: %s", consultID)
}
defer detailRows.Close()
cols, _ := detailRows.Columns()
if !detailRows.Next() {
return nil, fmt.Errorf("问诊不存在: %s", consultID)
}
detail = make(map[string]interface{})
vals := make([]interface{}, len(cols))
ptrs := make([]interface{}, len(cols))
for i := range vals {
ptrs[i] = &vals[i]
}
if err := detailRows.Scan(ptrs...); err != nil {
return nil, fmt.Errorf("问诊不存在: %s", consultID)
}
for i, col := range cols {
detail[col] = vals[i]
}
// 权限校验:患者只能查自己的,医生只能查自己接诊的
if userRole == "patient" && detail["patient_id"] != userID {
return nil, fmt.Errorf("无权查看该问诊记录")
}
if userRole == "doctor" {
var doctorID string
t.DB.WithContext(ctx).Raw("SELECT id FROM doctors WHERE user_id = ?", userID).Scan(&doctorID)
if detail["doctor_id"] != doctorID {
return nil, fmt.Errorf("无权查看该问诊记录")
}
}
// 可选:查询消息记录
if includeMessages {
var messages []map[string]interface{}
msgRows, err := t.DB.WithContext(ctx).Raw(`
SELECT id, sender_type, content, content_type, created_at
FROM consult_messages
WHERE consult_id = ? AND deleted_at IS NULL
ORDER BY created_at ASC LIMIT 50`, resolvedID).Rows()
if err == nil {
defer msgRows.Close()
msgCols, _ := msgRows.Columns()
for msgRows.Next() {
msg := make(map[string]interface{})
mVals := make([]interface{}, len(msgCols))
mPtrs := make([]interface{}, len(msgCols))
for i := range mVals {
mPtrs[i] = &mVals[i]
}
if err := msgRows.Scan(mPtrs...); err == nil {
for i, col := range msgCols {
msg[col] = mVals[i]
}
messages = append(messages, msg)
}
}
}
detail["messages"] = messages
detail["message_count"] = len(messages)
}
return detail, nil
}
package tools
import (
"context"
"fmt"
"internet-hospital/pkg/agent"
"gorm.io/gorm"
)
// ConsultationListTool 查询问诊列表
type ConsultationListTool struct {
DB *gorm.DB
}
func (t *ConsultationListTool) Name() string { return "query_consultation_list" }
func (t *ConsultationListTool) Description() string {
return "查询问诊列表:患者查自己的问诊记录,医生查自己接诊的问诊记录,支持按状态过滤"
}
func (t *ConsultationListTool) Parameters() []agent.ToolParameter {
return []agent.ToolParameter{
{Name: "status", Type: "string", Description: "问诊状态过滤(可选):pending/in_progress/completed/cancelled", Required: false, Enum: []string{"pending", "in_progress", "completed", "cancelled"}},
{Name: "limit", Type: "number", Description: "返回记录数量,默认10,最大50", Required: false},
}
}
func (t *ConsultationListTool) Execute(ctx context.Context, params map[string]interface{}) (interface{}, error) {
userID, _ := ctx.Value(agent.ContextKeyUserID).(string)
userRole, _ := ctx.Value(agent.ContextKeyUserRole).(string)
if userID == "" {
return nil, fmt.Errorf("未获取到用户信息")
}
limit := 10
if v, ok := params["limit"].(float64); ok && v > 0 {
limit = int(v)
if limit > 50 {
limit = 50
}
}
status, _ := params["status"].(string)
// 根据角色决定查询条件
var roleField string
switch userRole {
case "doctor":
roleField = "doctor_id"
var doctorID string
if err := t.DB.WithContext(ctx).Raw("SELECT id FROM doctors WHERE user_id = ?", userID).Scan(&doctorID).Error; err != nil || doctorID == "" {
return map[string]interface{}{"consultations": []interface{}{}, "total": 0}, nil
}
userID = doctorID
case "admin":
roleField = ""
default:
roleField = "patient_id"
}
query := "SELECT c.id, c.serial_number, c.chief_complaint, c.status, c.type, c.created_at, c.started_at, c.ended_at, " +
"d.name as doctor_name, dep.name as department_name, u.real_name as patient_name " +
"FROM consultations c " +
"LEFT JOIN doctors d ON c.doctor_id = d.id " +
"LEFT JOIN departments dep ON d.department_id = dep.id " +
"LEFT JOIN users u ON c.patient_id = u.id " +
"WHERE c.deleted_at IS NULL"
args := []interface{}{}
if roleField != "" {
query += " AND c." + roleField + " = ?"
args = append(args, userID)
}
if status != "" {
query += " AND c.status = ?"
args = append(args, status)
}
query += " ORDER BY c.created_at DESC LIMIT ?"
args = append(args, limit)
var results []map[string]interface{}
rows, err := t.DB.WithContext(ctx).Raw(query, args...).Rows()
if err != nil {
return map[string]interface{}{"consultations": []interface{}{}, "total": 0}, nil
}
defer rows.Close()
cols, _ := rows.Columns()
for rows.Next() {
row := make(map[string]interface{})
vals := make([]interface{}, len(cols))
ptrs := make([]interface{}, len(cols))
for i := range vals {
ptrs[i] = &vals[i]
}
if err := rows.Scan(ptrs...); err == nil {
for i, col := range cols {
row[col] = vals[i]
}
results = append(results, row)
}
}
return map[string]interface{}{
"consultations": results,
"total": len(results),
"role": userRole,
}, nil
}
package tools
import (
"context"
"fmt"
"time"
"internet-hospital/pkg/agent"
"gorm.io/gorm"
)
// DashboardStatsTool 查询管理端仪表盘统计数据
type DashboardStatsTool struct {
DB *gorm.DB
}
func (t *DashboardStatsTool) Name() string { return "query_dashboard_stats" }
func (t *DashboardStatsTool) Description() string {
return "查询管理端仪表盘统计:总用户数、总医生数、总问诊数、今日问诊、待审核医生、今日/本月收入,仅管理员可用"
}
func (t *DashboardStatsTool) Parameters() []agent.ToolParameter {
return []agent.ToolParameter{}
}
func (t *DashboardStatsTool) Execute(ctx context.Context, params map[string]interface{}) (interface{}, error) {
userRole, _ := ctx.Value(agent.ContextKeyUserRole).(string)
if userRole != "admin" {
return nil, fmt.Errorf("仅管理员可查看仪表盘数据")
}
today := time.Now().Format("2006-01-02")
monthStart := time.Now().Format("2006-01") + "-01"
stats := map[string]interface{}{}
var count int64
// 总用户数
t.DB.WithContext(ctx).Raw("SELECT COUNT(*) FROM users WHERE deleted_at IS NULL").Scan(&count)
stats["total_users"] = count
// 总医生数
t.DB.WithContext(ctx).Raw("SELECT COUNT(*) FROM users WHERE role = 'doctor' AND deleted_at IS NULL").Scan(&count)
stats["total_doctors"] = count
// 总问诊数
t.DB.WithContext(ctx).Raw("SELECT COUNT(*) FROM consultations WHERE deleted_at IS NULL").Scan(&count)
stats["total_consultations"] = count
// 今日问诊数
t.DB.WithContext(ctx).Raw("SELECT COUNT(*) FROM consultations WHERE DATE(created_at) = ? AND deleted_at IS NULL", today).Scan(&count)
stats["today_consultations"] = count
// 待审核医生数
t.DB.WithContext(ctx).Raw("SELECT COUNT(*) FROM doctor_reviews WHERE status = 'pending'").Scan(&count)
stats["pending_doctor_reviews"] = count
// 今日收入(已支付订单)
var todayRevenue int64
t.DB.WithContext(ctx).Raw(
"SELECT COALESCE(SUM(amount), 0) FROM payment_orders WHERE DATE(paid_at) = ? AND status = 'paid' AND deleted_at IS NULL",
today,
).Scan(&todayRevenue)
stats["revenue_today"] = todayRevenue
// 本月收入
var monthRevenue int64
t.DB.WithContext(ctx).Raw(
"SELECT COALESCE(SUM(amount), 0) FROM payment_orders WHERE paid_at >= ? AND status = 'paid' AND deleted_at IS NULL",
monthStart,
).Scan(&monthRevenue)
stats["revenue_month"] = monthRevenue
// 总处方数
t.DB.WithContext(ctx).Raw("SELECT COUNT(*) FROM prescriptions WHERE deleted_at IS NULL").Scan(&count)
stats["total_prescriptions"] = count
return stats, nil
}
package tools
import (
"context"
"fmt"
"time"
"internet-hospital/pkg/agent"
"gorm.io/gorm"
)
// DashboardTrendTool 查询管理端运营趋势数据
type DashboardTrendTool struct {
DB *gorm.DB
}
func (t *DashboardTrendTool) Name() string { return "query_dashboard_trend" }
func (t *DashboardTrendTool) Description() string {
return "查询最近7天运营趋势:每日问诊量、完成量、收入,仅管理员可用"
}
func (t *DashboardTrendTool) Parameters() []agent.ToolParameter {
return []agent.ToolParameter{
{Name: "days", Type: "number", Description: "查询天数,默认7,最大30", Required: false},
}
}
func (t *DashboardTrendTool) Execute(ctx context.Context, params map[string]interface{}) (interface{}, error) {
userRole, _ := ctx.Value(agent.ContextKeyUserRole).(string)
if userRole != "admin" {
return nil, fmt.Errorf("仅管理员可查看运营趋势")
}
days := 7
if v, ok := params["days"].(float64); ok && v > 0 {
days = int(v)
if days > 30 {
days = 30
}
}
startDate := time.Now().AddDate(0, 0, -(days - 1)).Format("2006-01-02")
rows, err := t.DB.WithContext(ctx).Raw(`
SELECT
DATE(created_at)::text AS date,
COUNT(*) AS consult_count,
SUM(CASE WHEN status = 'completed' THEN 1 ELSE 0 END) AS completed_count
FROM consultations
WHERE DATE(created_at) >= ? AND deleted_at IS NULL
GROUP BY DATE(created_at)
ORDER BY date
`, startDate).Rows()
if err != nil {
return map[string]interface{}{"trend": []interface{}{}}, nil
}
defer rows.Close()
var results []map[string]interface{}
for rows.Next() {
var date string
var consultCount, completedCount int
if err := rows.Scan(&date, &consultCount, &completedCount); err == nil {
results = append(results, map[string]interface{}{
"date": date,
"consult_count": consultCount,
"completed_count": completedCount,
})
}
}
if results == nil {
results = []map[string]interface{}{}
}
return map[string]interface{}{
"trend": results,
"days": days,
}, nil
}
package tools
import (
"context"
"internet-hospital/pkg/agent"
"gorm.io/gorm"
)
// DepartmentListTool 查询科室列表
type DepartmentListTool struct {
DB *gorm.DB
}
func (t *DepartmentListTool) Name() string { return "query_department_list" }
func (t *DepartmentListTool) Description() string {
return "查询所有科室列表(含层级关系),为推荐科室后查询具体医生提供数据支持"
}
func (t *DepartmentListTool) Parameters() []agent.ToolParameter {
return []agent.ToolParameter{}
}
func (t *DepartmentListTool) Execute(ctx context.Context, params map[string]interface{}) (interface{}, error) {
var results []map[string]interface{}
rows, err := t.DB.WithContext(ctx).Raw(`
SELECT id, name, icon, parent_id, sort_order
FROM departments
WHERE deleted_at IS NULL
ORDER BY sort_order ASC, name ASC`).Rows()
if err != nil {
return map[string]interface{}{"departments": []interface{}{}, "total": 0}, nil
}
defer rows.Close()
cols, _ := rows.Columns()
for rows.Next() {
row := make(map[string]interface{})
vals := make([]interface{}, len(cols))
ptrs := make([]interface{}, len(cols))
for i := range vals {
ptrs[i] = &vals[i]
}
if err := rows.Scan(ptrs...); err == nil {
for i, col := range cols {
row[col] = vals[i]
}
results = append(results, row)
}
}
return map[string]interface{}{
"departments": results,
"total": len(results),
}, nil
}
package tools
import (
"context"
"fmt"
"time"
"internet-hospital/pkg/agent"
"gorm.io/gorm"
)
// DoctorDetailTool 查询医生详情+排班
type DoctorDetailTool struct {
DB *gorm.DB
}
func (t *DoctorDetailTool) Name() string { return "query_doctor_detail" }
func (t *DoctorDetailTool) Description() string {
return "查询医生详情信息和近7天排班,包括简介、专长、评分、价格、可预约时段"
}
func (t *DoctorDetailTool) Parameters() []agent.ToolParameter {
return []agent.ToolParameter{
{Name: "doctor_id", Type: "string", Description: "医生ID", Required: true},
}
}
func (t *DoctorDetailTool) Execute(ctx context.Context, params map[string]interface{}) (interface{}, error) {
doctorID, ok := params["doctor_id"].(string)
if !ok || doctorID == "" {
return nil, fmt.Errorf("doctor_id 必填")
}
// 查询医生详情
detailRows, err := t.DB.WithContext(ctx).Raw(`
SELECT d.id, d.name, d.title, d.hospital, d.specialties, d.introduction,
d.rating, d.consult_count, d.price, d.is_online, d.avatar,
dep.name as department_name
FROM doctors d
LEFT JOIN departments dep ON d.department_id = dep.id
WHERE d.id = ? AND d.deleted_at IS NULL`, doctorID).Rows()
if err != nil {
return nil, fmt.Errorf("医生不存在: %s", doctorID)
}
defer detailRows.Close()
cols, _ := detailRows.Columns()
if !detailRows.Next() {
return nil, fmt.Errorf("医生不存在: %s", doctorID)
}
detail := make(map[string]interface{})
vals := make([]interface{}, len(cols))
ptrs := make([]interface{}, len(cols))
for i := range vals {
ptrs[i] = &vals[i]
}
if err := detailRows.Scan(ptrs...); err != nil {
return nil, fmt.Errorf("医生不存在: %s", doctorID)
}
for i, col := range cols {
detail[col] = vals[i]
}
// 查询近7天排班
today := time.Now().Format("2006-01-02")
endDate := time.Now().AddDate(0, 0, 7).Format("2006-01-02")
var schedules []map[string]interface{}
schedRows, err := t.DB.WithContext(ctx).Raw(`
SELECT id, date, start_time, end_time, max_count, remaining
FROM doctor_schedules
WHERE doctor_id = ? AND date >= ? AND date <= ?
ORDER BY date ASC, start_time ASC`, doctorID, today, endDate).Rows()
if err == nil {
defer schedRows.Close()
sCols, _ := schedRows.Columns()
for schedRows.Next() {
sched := make(map[string]interface{})
sVals := make([]interface{}, len(sCols))
sPtrs := make([]interface{}, len(sCols))
for i := range sVals {
sPtrs[i] = &sVals[i]
}
if err := schedRows.Scan(sPtrs...); err == nil {
for i, col := range sCols {
sched[col] = sVals[i]
}
schedules = append(schedules, sched)
}
}
}
detail["schedules"] = schedules
return detail, nil
}
package tools
import (
"context"
"fmt"
"internet-hospital/pkg/agent"
"gorm.io/gorm"
)
// DoctorListTool 搜索医生列表
type DoctorListTool struct {
DB *gorm.DB
}
func (t *DoctorListTool) Name() string { return "query_doctor_list" }
func (t *DoctorListTool) Description() string {
return "搜索医生列表,支持按科室、关键词筛选,按评分/问诊量/价格排序,返回医生基本信息和在线状态"
}
func (t *DoctorListTool) Parameters() []agent.ToolParameter {
return []agent.ToolParameter{
{Name: "department_id", Type: "string", Description: "科室ID过滤(可选)", Required: false},
{Name: "keyword", Type: "string", Description: "医生姓名关键词(可选)", Required: false},
{Name: "sort_by", Type: "string", Description: "排序方式,默认按评分", Required: false, Enum: []string{"rating", "consult_count", "price"}},
{Name: "limit", Type: "number", Description: "返回数量,默认10", Required: false},
}
}
func (t *DoctorListTool) Execute(ctx context.Context, params map[string]interface{}) (interface{}, error) {
userRole, _ := ctx.Value(agent.ContextKeyUserRole).(string)
if userRole != "patient" && userRole != "admin" {
return nil, fmt.Errorf("仅患者和管理员可搜索医生")
}
limit := 10
if v, ok := params["limit"].(float64); ok && v > 0 {
limit = int(v)
if limit > 30 {
limit = 30
}
}
departmentID, _ := params["department_id"].(string)
keyword, _ := params["keyword"].(string)
sortBy, _ := params["sort_by"].(string)
query := "SELECT d.id, d.name, d.title, d.hospital, d.specialties, d.rating, " +
"d.consult_count, d.price, d.is_online, d.avatar, " +
"dep.name as department_name " +
"FROM doctors d " +
"LEFT JOIN departments dep ON d.department_id = dep.id " +
"WHERE d.status = 'approved' AND d.deleted_at IS NULL"
args := []interface{}{}
if departmentID != "" {
query += " AND d.department_id = ?"
args = append(args, departmentID)
}
if keyword != "" {
query += " AND d.name ILIKE ?"
args = append(args, "%"+keyword+"%")
}
switch sortBy {
case "consult_count":
query += " ORDER BY d.consult_count DESC"
case "price":
query += " ORDER BY d.price ASC"
default:
query += " ORDER BY d.rating DESC"
}
query += " LIMIT ?"
args = append(args, limit)
var results []map[string]interface{}
rows, err := t.DB.WithContext(ctx).Raw(query, args...).Rows()
if err != nil {
return map[string]interface{}{"doctors": []interface{}{}, "total": 0}, nil
}
defer rows.Close()
cols, _ := rows.Columns()
for rows.Next() {
row := make(map[string]interface{})
vals := make([]interface{}, len(cols))
ptrs := make([]interface{}, len(cols))
for i := range vals {
ptrs[i] = &vals[i]
}
if err := rows.Scan(ptrs...); err == nil {
for i, col := range cols {
row[col] = vals[i]
}
results = append(results, row)
}
}
return map[string]interface{}{
"doctors": results,
"total": len(results),
}, nil
}
package tools
import (
"context"
"fmt"
"time"
"internet-hospital/pkg/agent"
"gorm.io/gorm"
)
// DoctorScheduleTool 查询医生排班
type DoctorScheduleTool struct {
DB *gorm.DB
}
func (t *DoctorScheduleTool) Name() string { return "query_doctor_schedule" }
func (t *DoctorScheduleTool) Description() string {
return "查询医生排班信息,患者用于预约,医生用于查看自己的排班"
}
func (t *DoctorScheduleTool) Parameters() []agent.ToolParameter {
return []agent.ToolParameter{
{Name: "doctor_id", Type: "string", Description: "医生ID(患者必填,医生可不填查自己的)", Required: false},
{Name: "days", Type: "number", Description: "查询未来天数,默认7天", Required: false},
}
}
func (t *DoctorScheduleTool) Execute(ctx context.Context, params map[string]interface{}) (interface{}, error) {
userID, _ := ctx.Value(agent.ContextKeyUserID).(string)
userRole, _ := ctx.Value(agent.ContextKeyUserRole).(string)
doctorID, _ := params["doctor_id"].(string)
// 医生查自己排班时无需传 doctor_id
if doctorID == "" && userRole == "doctor" {
var id string
t.DB.WithContext(ctx).Raw("SELECT id FROM doctors WHERE user_id = ?", userID).Scan(&id)
doctorID = id
}
if doctorID == "" {
return nil, fmt.Errorf("doctor_id 必填")
}
days := 7
if v, ok := params["days"].(float64); ok && v > 0 {
days = int(v)
if days > 30 {
days = 30
}
}
today := time.Now().Format("2006-01-02")
endDate := time.Now().AddDate(0, 0, days).Format("2006-01-02")
var results []map[string]interface{}
rows, err := t.DB.WithContext(ctx).Raw(`
SELECT id, date, start_time, end_time, max_count, remaining
FROM doctor_schedules
WHERE doctor_id = ? AND date >= ? AND date <= ?
ORDER BY date ASC, start_time ASC`, doctorID, today, endDate).Rows()
if err != nil {
return map[string]interface{}{"schedules": []interface{}{}, "total": 0}, nil
}
defer rows.Close()
cols, _ := rows.Columns()
for rows.Next() {
row := make(map[string]interface{})
vals := make([]interface{}, len(cols))
ptrs := make([]interface{}, len(cols))
for i := range vals {
ptrs[i] = &vals[i]
}
if err := rows.Scan(ptrs...); err == nil {
for i, col := range cols {
row[col] = vals[i]
}
results = append(results, row)
}
}
return map[string]interface{}{
"schedules": results,
"total": len(results),
"doctor_id": doctorID,
}, nil
}
package tools
import (
"context"
"fmt"
"internet-hospital/pkg/agent"
)
// CreateScheduleFn 创建排班回调,由 main.go 注入
// 参数: ctx, doctorUserID, date, startTime, endTime, maxCount
var CreateScheduleFn func(ctx context.Context, doctorUserID, date, startTime, endTime string, maxCount int) error
// CreateDoctorScheduleTool 医生创建排班
type CreateDoctorScheduleTool struct{}
func (t *CreateDoctorScheduleTool) Name() string { return "create_doctor_schedule" }
func (t *CreateDoctorScheduleTool) Description() string {
return "医生创建排班时段,设置日期、时间段和最大接诊人数"
}
func (t *CreateDoctorScheduleTool) Parameters() []agent.ToolParameter {
return []agent.ToolParameter{
{Name: "date", Type: "string", Description: "排班日期(格式:2006-01-02)", Required: true},
{Name: "start_time", Type: "string", Description: "开始时间(格式:09:00)", Required: true},
{Name: "end_time", Type: "string", Description: "结束时间(格式:12:00)", Required: true},
{Name: "max_count", Type: "number", Description: "最大接诊人数", Required: true},
}
}
func (t *CreateDoctorScheduleTool) Execute(ctx context.Context, params map[string]interface{}) (interface{}, error) {
userRole, _ := ctx.Value(agent.ContextKeyUserRole).(string)
userID, _ := ctx.Value(agent.ContextKeyUserID).(string)
if userRole != "doctor" {
return nil, fmt.Errorf("仅医生可创建排班")
}
if userID == "" {
return nil, fmt.Errorf("未获取到用户信息")
}
date, ok := params["date"].(string)
if !ok || date == "" {
return nil, fmt.Errorf("date 必填")
}
startTime, ok := params["start_time"].(string)
if !ok || startTime == "" {
return nil, fmt.Errorf("start_time 必填")
}
endTime, ok := params["end_time"].(string)
if !ok || endTime == "" {
return nil, fmt.Errorf("end_time 必填")
}
maxCount := 0
if v, ok := params["max_count"].(float64); ok {
maxCount = int(v)
}
if maxCount <= 0 {
return nil, fmt.Errorf("max_count 必须大于0")
}
if CreateScheduleFn == nil {
return nil, fmt.Errorf("排班服务未初始化")
}
if err := CreateScheduleFn(ctx, userID, date, startTime, endTime, maxCount); err != nil {
return nil, fmt.Errorf("创建排班失败: %v", err)
}
return map[string]interface{}{
"date": date,
"start_time": startTime,
"end_time": endTime,
"max_count": maxCount,
"message": "排班已创建",
}, nil
}
package tools
import (
"context"
"fmt"
"internet-hospital/pkg/agent"
)
// RecordHealthMetricFn 记录健康指标回调,由 main.go 注入
var RecordHealthMetricFn func(ctx context.Context, userID, metricType string, value1, value2 float64, unit, notes string) (string, error)
// RecordHealthMetricTool 记录健康指标
type RecordHealthMetricTool struct{}
func (t *RecordHealthMetricTool) Name() string { return "record_health_metric" }
func (t *RecordHealthMetricTool) Description() string {
return "患者记录健康指标(血压、血糖、心率、体温),异常值会自动触发健康告警"
}
func (t *RecordHealthMetricTool) Parameters() []agent.ToolParameter {
return []agent.ToolParameter{
{Name: "metric_type", Type: "string", Description: "指标类型", Required: true, Enum: []string{"blood_pressure", "blood_glucose", "heart_rate", "body_temperature"}},
{Name: "value1", Type: "number", Description: "主值(血压为收缩压,血糖/心率/体温为测量值)", Required: true},
{Name: "value2", Type: "number", Description: "副值(血压为舒张压,其他类型可不填)", Required: false},
{Name: "unit", Type: "string", Description: "单位(可选,如mmHg、mmol/L、bpm、℃)", Required: false},
{Name: "notes", Type: "string", Description: "备注(可选,如饭前/饭后、运动后等)", Required: false},
}
}
func (t *RecordHealthMetricTool) Execute(ctx context.Context, params map[string]interface{}) (interface{}, error) {
userRole, _ := ctx.Value(agent.ContextKeyUserRole).(string)
userID, _ := ctx.Value(agent.ContextKeyUserID).(string)
if userRole != "patient" {
return nil, fmt.Errorf("仅患者可记录健康指标")
}
if userID == "" {
return nil, fmt.Errorf("未获取到用户信息")
}
metricType, ok := params["metric_type"].(string)
if !ok || metricType == "" {
return nil, fmt.Errorf("metric_type 必填")
}
value1, ok := params["value1"].(float64)
if !ok {
return nil, fmt.Errorf("value1 必填")
}
var value2 float64
if v, ok := params["value2"].(float64); ok {
value2 = v
}
unit, _ := params["unit"].(string)
notes, _ := params["notes"].(string)
// 默认单位
if unit == "" {
switch metricType {
case "blood_pressure":
unit = "mmHg"
case "blood_glucose":
unit = "mmol/L"
case "heart_rate":
unit = "bpm"
case "body_temperature":
unit = "℃"
}
}
if RecordHealthMetricFn == nil {
return nil, fmt.Errorf("健康指标服务未初始化")
}
metricID, err := RecordHealthMetricFn(ctx, userID, metricType, value1, value2, unit, notes)
if err != nil {
return nil, fmt.Errorf("记录健康指标失败: %v", err)
}
return map[string]interface{}{
"metric_id": metricID,
"metric_type": metricType,
"value1": value1,
"value2": value2,
"unit": unit,
"message": "健康指标已记录",
}, nil
}
package tools
import (
"context"
"fmt"
"internet-hospital/pkg/agent"
"gorm.io/gorm"
)
// HealthMetricsTool 查询健康指标记录
type HealthMetricsTool struct {
DB *gorm.DB
}
func (t *HealthMetricsTool) Name() string { return "query_health_metrics" }
func (t *HealthMetricsTool) Description() string {
return "查询患者健康指标记录(血压、血糖、心率、体温),支持类型过滤,按时间倒序返回"
}
func (t *HealthMetricsTool) Parameters() []agent.ToolParameter {
return []agent.ToolParameter{
{Name: "patient_id", Type: "string", Description: "患者ID(医生使用,患者无需传入)", Required: false},
{Name: "metric_type", Type: "string", Description: "指标类型过滤(可选)", Required: false, Enum: []string{"blood_pressure", "blood_glucose", "heart_rate", "body_temperature"}},
{Name: "limit", Type: "number", Description: "返回数量,默认20", Required: false},
}
}
func (t *HealthMetricsTool) Execute(ctx context.Context, params map[string]interface{}) (interface{}, error) {
userID, _ := ctx.Value(agent.ContextKeyUserID).(string)
userRole, _ := ctx.Value(agent.ContextKeyUserRole).(string)
targetID := userID
if pid, ok := params["patient_id"].(string); ok && pid != "" {
if userRole == "patient" && pid != userID {
return nil, fmt.Errorf("患者只能查看自己的健康指标")
}
targetID = pid
}
if targetID == "" {
return nil, fmt.Errorf("未获取到用户信息")
}
limit := 20
if v, ok := params["limit"].(float64); ok && v > 0 {
limit = int(v)
if limit > 100 {
limit = 100
}
}
metricType, _ := params["metric_type"].(string)
query := "SELECT id, metric_type, value1, value2, unit, notes, recorded_at, created_at " +
"FROM health_metrics WHERE user_id = ?"
args := []interface{}{targetID}
if metricType != "" {
query += " AND metric_type = ?"
args = append(args, metricType)
}
query += " ORDER BY recorded_at DESC LIMIT ?"
args = append(args, limit)
var results []map[string]interface{}
rows, err := t.DB.WithContext(ctx).Raw(query, args...).Rows()
if err != nil {
return map[string]interface{}{"metrics": []interface{}{}, "total": 0}, nil
}
defer rows.Close()
cols, _ := rows.Columns()
for rows.Next() {
row := make(map[string]interface{})
vals := make([]interface{}, len(cols))
ptrs := make([]interface{}, len(cols))
for i := range vals {
ptrs[i] = &vals[i]
}
if err := rows.Scan(ptrs...); err == nil {
for i, col := range cols {
row[col] = vals[i]
}
results = append(results, row)
}
}
return map[string]interface{}{
"metrics": results,
"total": len(results),
}, nil
}
package tools
import (
"context"
"fmt"
"internet-hospital/pkg/agent"
"gorm.io/gorm"
)
// IncomeRecordsTool 查询医生收入明细
type IncomeRecordsTool struct {
DB *gorm.DB
}
func (t *IncomeRecordsTool) Name() string { return "query_income_records" }
func (t *IncomeRecordsTool) Description() string {
return "查询医生收入明细记录,包含收入类型、金额、患者姓名、状态,支持日期过滤"
}
func (t *IncomeRecordsTool) Parameters() []agent.ToolParameter {
return []agent.ToolParameter{
{Name: "start_date", Type: "string", Description: "开始日期(可选,格式YYYY-MM-DD)", Required: false},
{Name: "end_date", Type: "string", Description: "结束日期(可选,格式YYYY-MM-DD)", Required: false},
{Name: "limit", Type: "number", Description: "返回数量,默认20,最大100", Required: false},
}
}
func (t *IncomeRecordsTool) Execute(ctx context.Context, params map[string]interface{}) (interface{}, error) {
userID, _ := ctx.Value(agent.ContextKeyUserID).(string)
userRole, _ := ctx.Value(agent.ContextKeyUserRole).(string)
if userID == "" {
return nil, fmt.Errorf("未获取到用户信息")
}
if userRole != "doctor" {
return nil, fmt.Errorf("仅医生可查看收入明细")
}
// 查询医生ID
var doctorID string
t.DB.WithContext(ctx).Raw("SELECT id FROM doctors WHERE user_id = ?", userID).Scan(&doctorID)
if doctorID == "" {
return nil, fmt.Errorf("医生信息不存在")
}
limit := 20
if v, ok := params["limit"].(float64); ok && v > 0 {
limit = int(v)
if limit > 100 {
limit = 100
}
}
startDate, _ := params["start_date"].(string)
endDate, _ := params["end_date"].(string)
query := "SELECT id, consult_id, income_type, amount, status, patient_name, settled_at, created_at " +
"FROM doctor_incomes WHERE doctor_id = ?"
args := []interface{}{doctorID}
if startDate != "" {
query += " AND created_at >= ?"
args = append(args, startDate)
}
if endDate != "" {
query += " AND created_at <= ?"
args = append(args, endDate+" 23:59:59")
}
query += " ORDER BY created_at DESC LIMIT ?"
args = append(args, limit)
var results []map[string]interface{}
rows, err := t.DB.WithContext(ctx).Raw(query, args...).Rows()
if err != nil {
return map[string]interface{}{"records": []interface{}{}, "total": 0}, nil
}
defer rows.Close()
cols, _ := rows.Columns()
for rows.Next() {
row := make(map[string]interface{})
vals := make([]interface{}, len(cols))
ptrs := make([]interface{}, len(cols))
for i := range vals {
ptrs[i] = &vals[i]
}
if err := rows.Scan(ptrs...); err == nil {
for i, col := range cols {
row[col] = vals[i]
}
results = append(results, row)
}
}
return map[string]interface{}{
"records": results,
"total": len(results),
}, nil
}
package tools
import (
"context"
"fmt"
"time"
"internet-hospital/pkg/agent"
"gorm.io/gorm"
)
// IncomeStatsTool 查询医生收入统计
type IncomeStatsTool struct {
DB *gorm.DB
}
func (t *IncomeStatsTool) Name() string { return "query_income_stats" }
func (t *IncomeStatsTool) Description() string {
return "查询医生收入统计:可提现余额、本月收入、本月问诊量,仅医生可用"
}
func (t *IncomeStatsTool) Parameters() []agent.ToolParameter {
return []agent.ToolParameter{}
}
func (t *IncomeStatsTool) Execute(ctx context.Context, params map[string]interface{}) (interface{}, error) {
userID, _ := ctx.Value(agent.ContextKeyUserID).(string)
userRole, _ := ctx.Value(agent.ContextKeyUserRole).(string)
if userID == "" {
return nil, fmt.Errorf("未获取到用户信息")
}
if userRole != "doctor" {
return nil, fmt.Errorf("仅医生可查看收入统计")
}
// 查询医生ID
var doctorID string
t.DB.WithContext(ctx).Raw("SELECT id FROM doctors WHERE user_id = ?", userID).Scan(&doctorID)
if doctorID == "" {
return nil, fmt.Errorf("医生信息不存在")
}
var totalBalance int64
var monthIncome int64
var monthConsults int64
// 可提现余额(已结算未提现)
t.DB.WithContext(ctx).Raw(
"SELECT COALESCE(SUM(amount), 0) FROM doctor_incomes WHERE doctor_id = ? AND status = 'settled'",
doctorID,
).Scan(&totalBalance)
// 本月收入
startOfMonth := time.Now().Format("2006-01") + "-01"
t.DB.WithContext(ctx).Raw(
"SELECT COALESCE(SUM(amount), 0) FROM doctor_incomes WHERE doctor_id = ? AND created_at >= ?",
doctorID, startOfMonth,
).Scan(&monthIncome)
// 本月问诊量
t.DB.WithContext(ctx).Raw(
"SELECT COUNT(*) FROM doctor_incomes WHERE doctor_id = ? AND created_at >= ?",
doctorID, startOfMonth,
).Scan(&monthConsults)
return map[string]interface{}{
"total_balance": totalBalance,
"month_income": monthIncome,
"month_consults": monthConsults,
}, nil
}
package tools
import (
"context"
"fmt"
"internet-hospital/pkg/agent"
"gorm.io/gorm"
)
// LabReportsTool 查询检验报告
type LabReportsTool struct {
DB *gorm.DB
}
func (t *LabReportsTool) Name() string { return "query_lab_reports" }
func (t *LabReportsTool) Description() string {
return "查询患者的检验报告列表,包括AI解读结果"
}
func (t *LabReportsTool) Parameters() []agent.ToolParameter {
return []agent.ToolParameter{
{Name: "patient_id", Type: "string", Description: "患者ID(医生使用,患者无需传入)", Required: false},
{Name: "limit", Type: "number", Description: "返回数量,默认10", Required: false},
}
}
func (t *LabReportsTool) Execute(ctx context.Context, params map[string]interface{}) (interface{}, error) {
userID, _ := ctx.Value(agent.ContextKeyUserID).(string)
userRole, _ := ctx.Value(agent.ContextKeyUserRole).(string)
targetID := userID
if pid, ok := params["patient_id"].(string); ok && pid != "" {
if userRole == "patient" && pid != userID {
return nil, fmt.Errorf("患者只能查看自己的检验报告")
}
targetID = pid
}
if targetID == "" {
return nil, fmt.Errorf("未获取到用户信息")
}
limit := 10
if v, ok := params["limit"].(float64); ok && v > 0 {
limit = int(v)
if limit > 30 {
limit = 30
}
}
var results []map[string]interface{}
rows, err := t.DB.WithContext(ctx).Raw(`
SELECT id, title, report_date, file_url, file_type, category,
ai_interpret, created_at
FROM lab_reports
WHERE user_id = ? AND deleted_at IS NULL
ORDER BY report_date DESC, created_at DESC LIMIT ?`, targetID, limit).Rows()
if err != nil {
return map[string]interface{}{"reports": []interface{}{}, "total": 0}, nil
}
defer rows.Close()
cols, _ := rows.Columns()
for rows.Next() {
row := make(map[string]interface{})
vals := make([]interface{}, len(cols))
ptrs := make([]interface{}, len(cols))
for i := range vals {
ptrs[i] = &vals[i]
}
if err := rows.Scan(ptrs...); err == nil {
for i, col := range cols {
row[col] = vals[i]
}
results = append(results, row)
}
}
return map[string]interface{}{
"reports": results,
"total": len(results),
}, nil
}
package tools
import (
"context"
"fmt"
"internet-hospital/pkg/agent"
"gorm.io/gorm"
)
// MedicineCatalogTool 搜索药品目录
type MedicineCatalogTool struct {
DB *gorm.DB
}
func (t *MedicineCatalogTool) Name() string { return "search_medicine_catalog" }
func (t *MedicineCatalogTool) Description() string {
return "搜索药品目录,查询药品名称、规格、库存、价格等信息,为开方提供数据支持"
}
func (t *MedicineCatalogTool) Parameters() []agent.ToolParameter {
return []agent.ToolParameter{
{Name: "keyword", Type: "string", Description: "药品名称或关键词", Required: true},
{Name: "category", Type: "string", Description: "药品分类过滤(可选)", Required: false},
{Name: "limit", Type: "number", Description: "返回数量,默认10", Required: false},
}
}
func (t *MedicineCatalogTool) Execute(ctx context.Context, params map[string]interface{}) (interface{}, error) {
userRole, _ := ctx.Value(agent.ContextKeyUserRole).(string)
if userRole != "doctor" && userRole != "admin" {
return nil, fmt.Errorf("仅医生和管理员可搜索药品目录")
}
keyword, ok := params["keyword"].(string)
if !ok || keyword == "" {
return nil, fmt.Errorf("keyword 必填")
}
limit := 10
if v, ok := params["limit"].(float64); ok && v > 0 {
limit = int(v)
if limit > 30 {
limit = 30
}
}
category, _ := params["category"].(string)
query := "SELECT id, name, generic_name, specification, manufacturer, " +
"unit, price, stock, category, status, usage_method, contraindication " +
"FROM medicines " +
"WHERE (name ILIKE ? OR generic_name ILIKE ?) AND status = 'active'"
searchPattern := "%" + keyword + "%"
args := []interface{}{searchPattern, searchPattern}
if category != "" {
query += " AND category = ?"
args = append(args, category)
}
query += " ORDER BY name ASC LIMIT ?"
args = append(args, limit)
var results []map[string]interface{}
rows, err := t.DB.WithContext(ctx).Raw(query, args...).Rows()
if err != nil {
return map[string]interface{}{"medicines": []interface{}{}, "total": 0}, nil
}
defer rows.Close()
cols, _ := rows.Columns()
for rows.Next() {
row := make(map[string]interface{})
vals := make([]interface{}, len(cols))
ptrs := make([]interface{}, len(cols))
for i := range vals {
ptrs[i] = &vals[i]
}
if err := rows.Scan(ptrs...); err == nil {
for i, col := range cols {
row[col] = vals[i]
}
results = append(results, row)
}
}
return map[string]interface{}{
"medicines": results,
"total": len(results),
}, nil
}
package tools
import (
"context"
"fmt"
"internet-hospital/pkg/agent"
"gorm.io/gorm"
)
// OrderDetailTool 查询支付订单详情
type OrderDetailTool struct {
DB *gorm.DB
}
func (t *OrderDetailTool) Name() string { return "query_order_detail" }
func (t *OrderDetailTool) Description() string {
return "查询支付订单详情,包含订单号、金额、状态、支付方式、关联业务等完整信息"
}
func (t *OrderDetailTool) Parameters() []agent.ToolParameter {
return []agent.ToolParameter{
{Name: "order_id", Type: "string", Description: "订单ID", Required: true},
}
}
func (t *OrderDetailTool) Execute(ctx context.Context, params map[string]interface{}) (interface{}, error) {
userID, _ := ctx.Value(agent.ContextKeyUserID).(string)
userRole, _ := ctx.Value(agent.ContextKeyUserRole).(string)
if userID == "" {
return nil, fmt.Errorf("未获取到用户信息")
}
orderID, _ := params["order_id"].(string)
if orderID == "" {
return nil, fmt.Errorf("请提供订单ID")
}
query := "SELECT id, order_no, user_id, order_type, related_id, amount, status, " +
"payment_method, transaction_id, paid_at, refunded_at, expire_at, remark, created_at " +
"FROM payment_orders WHERE id = ? AND deleted_at IS NULL"
args := []interface{}{orderID}
// 患者只能查自己的订单
if userRole == "patient" {
query += " AND user_id = ?"
args = append(args, userID)
}
var result map[string]interface{}
row := t.DB.WithContext(ctx).Raw(query, args...)
rows, err := row.Rows()
if err != nil {
return nil, fmt.Errorf("查询订单失败")
}
defer rows.Close()
cols, _ := rows.Columns()
if rows.Next() {
result = make(map[string]interface{})
vals := make([]interface{}, len(cols))
ptrs := make([]interface{}, len(cols))
for i := range vals {
ptrs[i] = &vals[i]
}
if err := rows.Scan(ptrs...); err == nil {
for i, col := range cols {
result[col] = vals[i]
}
}
}
if result == nil {
return nil, fmt.Errorf("订单不存在")
}
return result, nil
}
package tools
import (
"context"
"fmt"
"internet-hospital/pkg/agent"
"gorm.io/gorm"
)
// OrderListTool 查询支付订单列表
type OrderListTool struct {
DB *gorm.DB
}
func (t *OrderListTool) Name() string { return "query_order_list" }
func (t *OrderListTool) Description() string {
return "查询支付订单列表:患者查自己的订单,管理员查所有订单,支持按状态和类型过滤"
}
func (t *OrderListTool) Parameters() []agent.ToolParameter {
return []agent.ToolParameter{
{Name: "status", Type: "string", Description: "订单状态过滤(可选)", Required: false, Enum: []string{"pending", "paid", "refunded", "cancelled", "completed"}},
{Name: "order_type", Type: "string", Description: "订单类型过滤(可选)", Required: false, Enum: []string{"consult", "prescription", "pharmacy"}},
{Name: "limit", Type: "number", Description: "返回数量,默认10,最大50", Required: false},
}
}
func (t *OrderListTool) Execute(ctx context.Context, params map[string]interface{}) (interface{}, error) {
userID, _ := ctx.Value(agent.ContextKeyUserID).(string)
userRole, _ := ctx.Value(agent.ContextKeyUserRole).(string)
if userID == "" {
return nil, fmt.Errorf("未获取到用户信息")
}
limit := 10
if v, ok := params["limit"].(float64); ok && v > 0 {
limit = int(v)
if limit > 50 {
limit = 50
}
}
status, _ := params["status"].(string)
orderType, _ := params["order_type"].(string)
query := "SELECT id, order_no, order_type, related_id, amount, status, payment_method, paid_at, created_at " +
"FROM payment_orders WHERE deleted_at IS NULL"
args := []interface{}{}
switch userRole {
case "patient":
query += " AND user_id = ?"
args = append(args, userID)
case "admin":
// 管理员查所有
default:
return nil, fmt.Errorf("无权限查询订单")
}
if status != "" {
query += " AND status = ?"
args = append(args, status)
}
if orderType != "" {
query += " AND order_type = ?"
args = append(args, orderType)
}
query += " ORDER BY created_at DESC LIMIT ?"
args = append(args, limit)
var results []map[string]interface{}
rows, err := t.DB.WithContext(ctx).Raw(query, args...).Rows()
if err != nil {
return map[string]interface{}{"orders": []interface{}{}, "total": 0}, nil
}
defer rows.Close()
cols, _ := rows.Columns()
for rows.Next() {
row := make(map[string]interface{})
vals := make([]interface{}, len(cols))
ptrs := make([]interface{}, len(cols))
for i := range vals {
ptrs[i] = &vals[i]
}
if err := rows.Scan(ptrs...); err == nil {
for i, col := range cols {
row[col] = vals[i]
}
results = append(results, row)
}
}
return map[string]interface{}{
"orders": results,
"total": len(results),
}, nil
}
package tools
import (
"context"
"fmt"
"internet-hospital/pkg/agent"
"gorm.io/gorm"
)
// PatientProfileTool 查询患者健康档案
type PatientProfileTool struct {
DB *gorm.DB
}
func (t *PatientProfileTool) Name() string { return "query_patient_profile" }
func (t *PatientProfileTool) Description() string {
return "查询患者健康档案(基本信息、过敏史、病史、保险信息),患者查自己的,医生按patient_id查"
}
func (t *PatientProfileTool) Parameters() []agent.ToolParameter {
return []agent.ToolParameter{
{Name: "patient_id", Type: "string", Description: "患者ID(医生/管理员使用,患者无需传入自动查自己的)", Required: false},
}
}
func (t *PatientProfileTool) Execute(ctx context.Context, params map[string]interface{}) (interface{}, error) {
userID, _ := ctx.Value(agent.ContextKeyUserID).(string)
userRole, _ := ctx.Value(agent.ContextKeyUserRole).(string)
targetID := userID
if pid, ok := params["patient_id"].(string); ok && pid != "" {
if userRole == "patient" && pid != userID {
return nil, fmt.Errorf("患者只能查看自己的健康档案")
}
targetID = pid
}
if targetID == "" {
return nil, fmt.Errorf("未获取到用户信息")
}
// 查询用户基本信息
userInfo := make(map[string]interface{})
uRows, err := t.DB.WithContext(ctx).Raw(`
SELECT id, real_name, phone, gender, age, avatar, role, status, created_at
FROM users WHERE id = ?`, targetID).Rows()
if err != nil {
return nil, fmt.Errorf("用户不存在")
}
defer uRows.Close()
if uRows.Next() {
uCols, _ := uRows.Columns()
uVals := make([]interface{}, len(uCols))
uPtrs := make([]interface{}, len(uCols))
for i := range uVals {
uPtrs[i] = &uVals[i]
}
if err := uRows.Scan(uPtrs...); err == nil {
for i, col := range uCols {
userInfo[col] = uVals[i]
}
}
} else {
return nil, fmt.Errorf("用户不存在")
}
// 查询健康档案
profile := make(map[string]interface{})
pRows, err := t.DB.WithContext(ctx).Raw(`
SELECT gender, birth_date, medical_history, allergy_history,
emergency_contact, insurance_type, insurance_no
FROM patient_profiles WHERE user_id = ?`, targetID).Rows()
if err == nil {
defer pRows.Close()
if pRows.Next() {
pCols, _ := pRows.Columns()
pVals := make([]interface{}, len(pCols))
pPtrs := make([]interface{}, len(pCols))
for i := range pVals {
pPtrs[i] = &pVals[i]
}
if err := pRows.Scan(pPtrs...); err == nil {
for i, col := range pCols {
profile[col] = pVals[i]
}
}
}
}
// 查询最近问诊数
var consultCount int64
t.DB.WithContext(ctx).Raw("SELECT COUNT(*) FROM consultations WHERE patient_id = ? AND deleted_at IS NULL", targetID).Scan(&consultCount)
return map[string]interface{}{
"user": userInfo,
"health_profile": profile,
"consult_count": consultCount,
}, nil
}
package tools
import (
"context"
"fmt"
"internet-hospital/pkg/agent"
"gorm.io/gorm"
)
// PrescriptionDetailTool 查询处方详情
type PrescriptionDetailTool struct {
DB *gorm.DB
}
func (t *PrescriptionDetailTool) Name() string { return "query_prescription_detail" }
func (t *PrescriptionDetailTool) Description() string {
return "查询处方详情,包括药品明细、用法用量、状态和费用,支持处方ID或处方编号(RX开头)查询"
}
func (t *PrescriptionDetailTool) Parameters() []agent.ToolParameter {
return []agent.ToolParameter{
{Name: "prescription_id", Type: "string", Description: "处方ID(UUID)或处方编号(RX开头)", Required: true},
}
}
func (t *PrescriptionDetailTool) Execute(ctx context.Context, params map[string]interface{}) (interface{}, error) {
userID, _ := ctx.Value(agent.ContextKeyUserID).(string)
userRole, _ := ctx.Value(agent.ContextKeyUserRole).(string)
prescriptionID, ok := params["prescription_id"].(string)
if !ok || prescriptionID == "" {
return nil, fmt.Errorf("prescription_id 必填")
}
// 支持处方编号查询
resolvedID := prescriptionID
if len(prescriptionID) >= 2 && prescriptionID[:2] == "RX" {
var id string
if err := t.DB.WithContext(ctx).Raw("SELECT id FROM prescriptions WHERE prescription_no = ?", prescriptionID).Scan(&id).Error; err != nil || id == "" {
return nil, fmt.Errorf("处方编号 %s 不存在", prescriptionID)
}
resolvedID = id
}
// 查询处方主记录
detailRows, err := t.DB.WithContext(ctx).Raw(`
SELECT p.id, p.prescription_no, p.consult_id, p.patient_id, p.patient_name,
p.patient_gender, p.patient_age, p.diagnosis, p.allergy_history,
p.remark, p.total_amount, p.status, p.created_at,
d.name as doctor_name, d.title as doctor_title, dep.name as department_name
FROM prescriptions p
LEFT JOIN doctors d ON p.doctor_id = d.id
LEFT JOIN departments dep ON d.department_id = dep.id
WHERE p.id = ? AND p.deleted_at IS NULL`, resolvedID).Rows()
if err != nil {
return nil, fmt.Errorf("处方不存在: %s", prescriptionID)
}
defer detailRows.Close()
cols, _ := detailRows.Columns()
if !detailRows.Next() {
return nil, fmt.Errorf("处方不存在: %s", prescriptionID)
}
detail := make(map[string]interface{})
vals := make([]interface{}, len(cols))
ptrs := make([]interface{}, len(cols))
for i := range vals {
ptrs[i] = &vals[i]
}
if err := detailRows.Scan(ptrs...); err != nil {
return nil, fmt.Errorf("处方不存在: %s", prescriptionID)
}
for i, col := range cols {
detail[col] = vals[i]
}
// 权限校验
if userRole == "patient" && detail["patient_id"] != userID {
return nil, fmt.Errorf("无权查看该处方")
}
// 查询药品明细
var items []map[string]interface{}
itemRows, err := t.DB.WithContext(ctx).Raw(`
SELECT medicine_name, specification, usage, dosage, frequency,
days, quantity, unit, price, note
FROM prescription_items
WHERE prescription_id = ?
ORDER BY created_at ASC`, resolvedID).Rows()
if err == nil {
defer itemRows.Close()
itemCols, _ := itemRows.Columns()
for itemRows.Next() {
item := make(map[string]interface{})
iVals := make([]interface{}, len(itemCols))
iPtrs := make([]interface{}, len(itemCols))
for i := range iVals {
iPtrs[i] = &iVals[i]
}
if err := itemRows.Scan(iPtrs...); err == nil {
for i, col := range itemCols {
item[col] = iVals[i]
}
items = append(items, item)
}
}
}
detail["items"] = items
detail["drug_count"] = len(items)
return detail, nil
}
package tools
import (
"context"
"fmt"
"internet-hospital/pkg/agent"
"gorm.io/gorm"
)
// PrescriptionListTool 查询处方列表
type PrescriptionListTool struct {
DB *gorm.DB
}
func (t *PrescriptionListTool) Name() string { return "query_prescription_list" }
func (t *PrescriptionListTool) Description() string {
return "查询处方列表:患者查自己的处方,医生查自己开的处方,支持按状态过滤"
}
func (t *PrescriptionListTool) Parameters() []agent.ToolParameter {
return []agent.ToolParameter{
{Name: "status", Type: "string", Description: "处方状态过滤(可选):pending/signed/dispensed/completed/cancelled", Required: false},
{Name: "limit", Type: "number", Description: "返回数量,默认10,最大50", Required: false},
}
}
func (t *PrescriptionListTool) Execute(ctx context.Context, params map[string]interface{}) (interface{}, error) {
userID, _ := ctx.Value(agent.ContextKeyUserID).(string)
userRole, _ := ctx.Value(agent.ContextKeyUserRole).(string)
if userID == "" {
return nil, fmt.Errorf("未获取到用户信息")
}
limit := 10
if v, ok := params["limit"].(float64); ok && v > 0 {
limit = int(v)
if limit > 50 {
limit = 50
}
}
status, _ := params["status"].(string)
query := "SELECT p.id, p.prescription_no, p.patient_name, p.diagnosis, " +
"p.total_amount, p.status, p.created_at, " +
"d.name as doctor_name, dep.name as department_name, " +
"(SELECT COUNT(*) FROM prescription_items pi WHERE pi.prescription_id = p.id) as drug_count " +
"FROM prescriptions p " +
"LEFT JOIN doctors d ON p.doctor_id = d.id " +
"LEFT JOIN departments dep ON d.department_id = dep.id " +
"WHERE p.deleted_at IS NULL"
args := []interface{}{}
switch userRole {
case "patient":
query += " AND p.patient_id = ?"
args = append(args, userID)
case "doctor":
var doctorID string
t.DB.WithContext(ctx).Raw("SELECT id FROM doctors WHERE user_id = ?", userID).Scan(&doctorID)
if doctorID == "" {
return map[string]interface{}{"prescriptions": []interface{}{}, "total": 0}, nil
}
query += " AND p.doctor_id = ?"
args = append(args, doctorID)
case "admin":
// 管理员查所有
default:
return nil, fmt.Errorf("无权限查询处方")
}
if status != "" {
query += " AND p.status = ?"
args = append(args, status)
}
query += " ORDER BY p.created_at DESC LIMIT ?"
args = append(args, limit)
var results []map[string]interface{}
rows, err := t.DB.WithContext(ctx).Raw(query, args...).Rows()
if err != nil {
return map[string]interface{}{"prescriptions": []interface{}{}, "total": 0}, nil
}
defer rows.Close()
cols, _ := rows.Columns()
for rows.Next() {
row := make(map[string]interface{})
vals := make([]interface{}, len(cols))
ptrs := make([]interface{}, len(cols))
for i := range vals {
ptrs[i] = &vals[i]
}
if err := rows.Scan(ptrs...); err == nil {
for i, col := range cols {
row[col] = vals[i]
}
results = append(results, row)
}
}
return map[string]interface{}{
"prescriptions": results,
"total": len(results),
}, nil
}
package tools
import (
"context"
"fmt"
"internet-hospital/pkg/agent"
)
// CreateRenewalFn 创建续方申请回调,由 main.go 注入
var CreateRenewalFn func(ctx context.Context, userID, chronicID, diseaseName, reason string, medicines []string) (string, error)
// CreateRenewalTool 患者创建续方申请
type CreateRenewalTool struct{}
func (t *CreateRenewalTool) Name() string { return "create_renewal_request" }
func (t *CreateRenewalTool) Description() string {
return "患者发起续方申请,需要指定疾病名称和续方药品列表,提交后等待医生审批"
}
func (t *CreateRenewalTool) Parameters() []agent.ToolParameter {
return []agent.ToolParameter{
{Name: "disease_name", Type: "string", Description: "疾病名称", Required: true},
{Name: "medicines", Type: "array", Description: "续方药品名称列表", Required: true},
{Name: "chronic_id", Type: "string", Description: "关联的慢病记录ID(可选)", Required: false},
{Name: "reason", Type: "string", Description: "续方原因(可选)", Required: false},
}
}
func (t *CreateRenewalTool) Execute(ctx context.Context, params map[string]interface{}) (interface{}, error) {
userRole, _ := ctx.Value(agent.ContextKeyUserRole).(string)
userID, _ := ctx.Value(agent.ContextKeyUserID).(string)
if userRole != "patient" {
return nil, fmt.Errorf("仅患者可发起续方申请")
}
if userID == "" {
return nil, fmt.Errorf("未获取到用户信息")
}
diseaseName, ok := params["disease_name"].(string)
if !ok || diseaseName == "" {
return nil, fmt.Errorf("disease_name 必填")
}
// 解析药品列表
var medicines []string
if arr, ok := params["medicines"].([]interface{}); ok {
for _, v := range arr {
if s, ok := v.(string); ok {
medicines = append(medicines, s)
}
}
}
if len(medicines) == 0 {
return nil, fmt.Errorf("medicines 必填且至少包含一种药品")
}
chronicID, _ := params["chronic_id"].(string)
reason, _ := params["reason"].(string)
if CreateRenewalFn == nil {
return nil, fmt.Errorf("续方服务未初始化")
}
renewalID, err := CreateRenewalFn(ctx, userID, chronicID, diseaseName, reason, medicines)
if err != nil {
return nil, fmt.Errorf("创建续方申请失败: %v", err)
}
return map[string]interface{}{
"renewal_id": renewalID,
"disease_name": diseaseName,
"status": "pending",
"message": "续方申请已提交,等待医生审批",
}, nil
}
package tools
import (
"context"
"fmt"
"internet-hospital/pkg/agent"
"gorm.io/gorm"
)
// RenewalRequestsTool 查询续方申请列表
type RenewalRequestsTool struct {
DB *gorm.DB
}
func (t *RenewalRequestsTool) Name() string { return "query_renewal_requests" }
func (t *RenewalRequestsTool) Description() string {
return "查询续方申请列表:患者查自己的续方申请(含状态和AI建议),医生查待审批的续方申请"
}
func (t *RenewalRequestsTool) Parameters() []agent.ToolParameter {
return []agent.ToolParameter{
{Name: "status", Type: "string", Description: "状态过滤(可选):pending/approved/rejected", Required: false, Enum: []string{"pending", "approved", "rejected"}},
}
}
func (t *RenewalRequestsTool) Execute(ctx context.Context, params map[string]interface{}) (interface{}, error) {
userID, _ := ctx.Value(agent.ContextKeyUserID).(string)
userRole, _ := ctx.Value(agent.ContextKeyUserRole).(string)
if userID == "" {
return nil, fmt.Errorf("未获取到用户信息")
}
status, _ := params["status"].(string)
query := "SELECT r.id, r.disease_name, r.medicines, r.reason, r.status, " +
"r.doctor_note, r.ai_advice, r.created_at, " +
"u.real_name as patient_name " +
"FROM renewal_requests r " +
"LEFT JOIN users u ON r.user_id = u.id " +
"WHERE r.deleted_at IS NULL"
args := []interface{}{}
switch userRole {
case "patient":
query += " AND r.user_id = ?"
args = append(args, userID)
case "doctor":
// 医生查所有待审批的
if status == "" {
status = "pending"
}
case "admin":
// 管理员查所有
default:
return nil, fmt.Errorf("无权限查询续方申请")
}
if status != "" {
query += " AND r.status = ?"
args = append(args, status)
}
query += " ORDER BY r.created_at DESC LIMIT 30"
var results []map[string]interface{}
rows, err := t.DB.WithContext(ctx).Raw(query, args...).Rows()
if err != nil {
return map[string]interface{}{"renewals": []interface{}{}, "total": 0}, nil
}
defer rows.Close()
cols, _ := rows.Columns()
for rows.Next() {
row := make(map[string]interface{})
vals := make([]interface{}, len(cols))
ptrs := make([]interface{}, len(cols))
for i := range vals {
ptrs[i] = &vals[i]
}
if err := rows.Scan(ptrs...); err == nil {
for i, col := range cols {
row[col] = vals[i]
}
results = append(results, row)
}
}
return map[string]interface{}{
"renewals": results,
"total": len(results),
}, nil
}
package tools
import (
"context"
"fmt"
"internet-hospital/pkg/agent"
"gorm.io/gorm"
)
// SystemLogsTool 查询系统操作日志(管理员专用)
type SystemLogsTool struct {
DB *gorm.DB
}
func (t *SystemLogsTool) Name() string { return "query_system_logs" }
func (t *SystemLogsTool) Description() string {
return "查询系统操作日志,支持按操作类型、资源过滤,仅管理员可用"
}
func (t *SystemLogsTool) Parameters() []agent.ToolParameter {
return []agent.ToolParameter{
{Name: "action", Type: "string", Description: "操作类型过滤(可选,如login/create/update/delete)", Required: false},
{Name: "resource", Type: "string", Description: "资源类型过滤(可选,如user/doctor/prescription)", Required: false},
{Name: "limit", Type: "number", Description: "返回数量,默认20,最大100", Required: false},
}
}
func (t *SystemLogsTool) Execute(ctx context.Context, params map[string]interface{}) (interface{}, error) {
userRole, _ := ctx.Value(agent.ContextKeyUserRole).(string)
if userRole != "admin" {
return nil, fmt.Errorf("仅管理员可查看系统日志")
}
limit := 20
if v, ok := params["limit"].(float64); ok && v > 0 {
limit = int(v)
if limit > 100 {
limit = 100
}
}
action, _ := params["action"].(string)
resource, _ := params["resource"].(string)
query := "SELECT sl.id, sl.user_id, sl.action, sl.resource, sl.detail, sl.ip, sl.created_at, " +
"u.real_name as user_name " +
"FROM system_logs sl LEFT JOIN users u ON sl.user_id = u.id WHERE 1=1"
args := []interface{}{}
if action != "" {
query += " AND sl.action = ?"
args = append(args, action)
}
if resource != "" {
query += " AND sl.resource LIKE ?"
args = append(args, "%"+resource+"%")
}
query += " ORDER BY sl.created_at DESC LIMIT ?"
args = append(args, limit)
var results []map[string]interface{}
rows, err := t.DB.WithContext(ctx).Raw(query, args...).Rows()
if err != nil {
return map[string]interface{}{"logs": []interface{}{}, "total": 0}, nil
}
defer rows.Close()
cols, _ := rows.Columns()
for rows.Next() {
row := make(map[string]interface{})
vals := make([]interface{}, len(cols))
ptrs := make([]interface{}, len(cols))
for i := range vals {
ptrs[i] = &vals[i]
}
if err := rows.Scan(ptrs...); err == nil {
for i, col := range cols {
row[col] = vals[i]
}
results = append(results, row)
}
}
return map[string]interface{}{
"logs": results,
"total": len(results),
}, nil
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment