import { tool } from "@langchain/core/tools"; import z from "zod"; import mongoose from "mongoose"; import { DBModel } from "../../../../models/index.js"; import { genPinyin, genPinyinFL } from "../../../../models/schema/health_profile.js"; const PROFILE_KEYS = ["name", "mobile", "sex", "birth", "idnumber"]; const LOCATION_KEYS = ["country", "province", "city", "district", "address"]; const HEALTH_KEYS = ["height", "weight", "bloodType", "remark"]; const profileSchema = z .object({ name: z.string().optional().describe("患者姓名"), mobile: z.string().optional().describe("患者电话"), sex: z.enum(["male", "female"]).optional().describe("性别"), birth: z.string().optional().describe("出生年月(YYYY-MM-DD)"), idnumber: z.string().optional().describe("证件号(身份证/护照等)"), }) .optional() .describe("患者基本信息"); const locationSchema = z .object({ country: z.string().optional().describe("国家(国外用户填国家,国内默认中国)"), province: z.string().optional().describe("省"), city: z.string().optional().describe("市"), district: z.string().optional().describe("区/县"), address: z.string().optional().describe("详细地址"), }) .optional() .describe("所在地信息"); const healthSchema = z .object({ height: z.number().optional().describe("身高(cm)"), weight: z.number().optional().describe("体重(kg)"), bloodType: z.string().optional().describe("血型"), remark: z.string().optional().describe("备注"), }) .optional() .describe("健康信息"); const healthProfileSetTool = tool( async ({ action, profileId, userId, profile, location, health }) => { try { if (action === "create") { if (!profile || (!profile.name && !profile.mobile)) { return { success: false, error: "创建健康档案至少需要提供患者姓名或电话" }; } const docData = { profile: { ...profile }, location: { ...location }, health: { ...health }, meta: { createtime: new Date(), updatetime: new Date() }, }; if (userId) { if (!mongoose.Types.ObjectId.isValid(userId)) { return { success: false, error: `Invalid user ID: ${userId}` }; } docData.userId = new mongoose.Types.ObjectId(userId); } const doc = await DBModel.HealthProfile.create(docData); return { success: true, data: doc }; } if (action === "update") { if (!profileId) { return { success: false, error: "更新健康档案必须提供 profileId(可先用 health_profile_query 查询定位)" }; } if (!mongoose.Types.ObjectId.isValid(profileId)) { return { success: false, error: `Invalid profile ID: ${profileId}` }; } const update = {}; if (userId) { if (!mongoose.Types.ObjectId.isValid(userId)) { return { success: false, error: `Invalid user ID: ${userId}` }; } update.userId = new mongoose.Types.ObjectId(userId); } if (profile) { for (const key of PROFILE_KEYS) { if (profile[key] !== undefined) update[`profile.${key}`] = profile[key]; } // 更新姓名时同步拼音字段(findByIdAndUpdate 不触发 pre("save")) if (profile.name !== undefined) { update["profile.pinyin"] = genPinyin(profile.name); update["profile.pinyinFL"] = genPinyinFL(profile.name); } } if (location) { for (const key of LOCATION_KEYS) { if (location[key] !== undefined) update[`location.${key}`] = location[key]; } } if (health) { for (const key of HEALTH_KEYS) { if (health[key] !== undefined) update[`health.${key}`] = health[key]; } } if (Object.keys(update).length === 0) { return { success: false, error: "未提供任何要更新的字段" }; } update["meta.updatetime"] = new Date(); const updated = await DBModel.HealthProfile.findByIdAndUpdate( profileId, { $set: update }, { new: true } ); if (!updated) { return { success: false, error: `未找到 ID 为 ${profileId} 的健康档案` }; } return { success: true, data: updated }; } return { success: false, error: "未知的 action,可选值:create / update" }; } catch (error) { return { success: false, error: error.message }; } }, { name: "health_profile_set", description: "创建或更新患者健康档案。create 新建档案(至少提供患者姓名或电话);update 按档案 ID 局部更新,仅修改提供的字段。更新前建议先用 health_profile_query 查询现有内容。", schema: z.object({ action: z.enum(["create", "update"]).describe("操作类型:create 新建 / update 更新"), profileId: z.string().optional().describe("健康档案 ID(_id),update 时必填"), userId: z.string().optional().describe("所属用户ID(可选,用于关联小程序用户)"), profile: profileSchema, location: locationSchema, health: healthSchema, }), } ); export { healthProfileSetTool };