|
|
@@ -0,0 +1,529 @@
|
|
|
+package com.etotem.cfc.controller.mcp;
|
|
|
+
|
|
|
+import com.alibaba.fastjson.JSON;
|
|
|
+import com.etotem.cfc.common.Result;
|
|
|
+import com.etotem.cfc.dto.ProductListQueryDTO;
|
|
|
+import com.etotem.cfc.entity.Article;
|
|
|
+import com.etotem.cfc.entity.HealthReportDraft;
|
|
|
+import com.etotem.cfc.service.*;
|
|
|
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+import org.springframework.stereotype.Component;
|
|
|
+
|
|
|
+import javax.annotation.Resource;
|
|
|
+import java.util.*;
|
|
|
+
|
|
|
+/**
|
|
|
+ * MCP 工具注册中心。
|
|
|
+ * 所有 MCP 工具的逻辑实现在此。
|
|
|
+ */
|
|
|
+@Slf4j
|
|
|
+@Component
|
|
|
+public class McpToolRegistry {
|
|
|
+
|
|
|
+ // ---- 服务依赖 ----
|
|
|
+ @Resource
|
|
|
+ private FamilyMemberService familyMemberService;
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private TaskService taskService;
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private HealthReportDraftService healthReportDraftService;
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private AiGateway aiGateway;
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private ActivityService activityService;
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private ArticleService articleService;
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private ProductService productService;
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private UserService userService;
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private WishService wishService;
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private EnergyService energyService;
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private ContactService contactService;
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private FamilyContextService familyContextService;
|
|
|
+
|
|
|
+ // ----------------------------------------------------------------
|
|
|
+ // tools/list
|
|
|
+ // ----------------------------------------------------------------
|
|
|
+
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
+ public Map<String, Object> listTools() {
|
|
|
+ List<Map<String, Object>> tools = new ArrayList<>();
|
|
|
+ tools.add(toolDef("list_family_members",
|
|
|
+ "获取指定用户所属家庭的成员列表,含基本信息(昵称/头像/角色标签)和五维能量概览。返回按辈分排序的列表。",
|
|
|
+ toolSchema(
|
|
|
+ required("userId", "string", "用户 ID(登录账号 userId,非 memberId)"),
|
|
|
+ optional("page", "integer", "页码,默认 1"),
|
|
|
+ optional("pageSize", "integer", "每页数量,默认 20")
|
|
|
+ )
|
|
|
+ ));
|
|
|
+ tools.add(toolDef("get_today_tasks",
|
|
|
+ "获取指定家庭成员今日的任务列表。仅返回 status=in_progress 或 pending 的任务,不含已完成任务。",
|
|
|
+ toolSchema(
|
|
|
+ required("memberId", "string", "家庭成员 ID(family_members.id)"),
|
|
|
+ optional("dimensionCode", "string", "按维度过滤,如 '身体'/'心智'/'行动'/'财富'/'心理'")
|
|
|
+ )
|
|
|
+ ));
|
|
|
+ tools.add(toolDef("get_health_report_status",
|
|
|
+ "查询健康报告采集状态。支持查询草稿解析进度,以及已入库报告的类型和评分摘要。",
|
|
|
+ toolSchema(
|
|
|
+ required("draftId", "string", "草稿 ID(health_report_drafts.id)")
|
|
|
+ )
|
|
|
+ ));
|
|
|
+ tools.add(toolDef("send_ai_message",
|
|
|
+ "向 AI 助手发送消息并获取回复。自动注入家庭上下文(家庭成员/任务/能量等),支持多轮对话。",
|
|
|
+ toolSchema(
|
|
|
+ required("query", "string", "用户问题/指令文本"),
|
|
|
+ optional("conversationId", "string", "对话 ID;不传则创建新对话"),
|
|
|
+ optional("context", "string", "额外上下文 JSON 字符串,覆盖自动注入的上下文")
|
|
|
+ )
|
|
|
+ ));
|
|
|
+ tools.add(toolDef("search_activities",
|
|
|
+ "搜索已发布的活动列表,按创建时间倒序。支持按维度、关键词过滤,支持分页。",
|
|
|
+ toolSchema(
|
|
|
+ optional("dimensionCode", "string", "维度代码过滤,如 '身'/'智'/'行'/'富'/'心'"),
|
|
|
+ optional("keyword", "string", "关键词搜索(标题/描述)"),
|
|
|
+ optional("page", "integer", "页码,默认 1"),
|
|
|
+ optional("pageSize", "integer", "每页数量,默认 10")
|
|
|
+ )
|
|
|
+ ));
|
|
|
+ tools.add(toolDef("search_articles",
|
|
|
+ "搜索已发布文章列表,按精选排序后按发布时间倒序。支持按分类、维度、关键词过滤,支持分页。",
|
|
|
+ toolSchema(
|
|
|
+ optional("categoryId", "string", "文章分类 ID,不传则搜索全部"),
|
|
|
+ optional("keyword", "string", "关键词搜索(标题/摘要/标签)"),
|
|
|
+ optional("dimensionCode", "string", "维度代码过滤"),
|
|
|
+ optional("page", "integer", "页码,默认 1"),
|
|
|
+ optional("pageSize", "integer", "每页数量,默认 10")
|
|
|
+ )
|
|
|
+ ));
|
|
|
+ tools.add(toolDef("search_products",
|
|
|
+ "搜索已上架商品列表,按排序后按创建时间倒序。支持按品类、关键词、成长档案关联过滤,支持分页。",
|
|
|
+ toolSchema(
|
|
|
+ optional("productType", "string", "商品品类过滤"),
|
|
|
+ optional("keyword", "string", "关键词搜索(名称/描述)"),
|
|
|
+ optional("growthRelated", "boolean", "true=仅返回成长档案关联商品"),
|
|
|
+ optional("page", "integer", "页码,默认 1"),
|
|
|
+ optional("pageSize", "integer", "每页数量,默认 10")
|
|
|
+ )
|
|
|
+ ));
|
|
|
+ tools.add(toolDef("get_user_profile",
|
|
|
+ "获取用户基本信息:用户 ID、昵称、手机号、头像、角色、当前家庭 ID 与名称、会员等级。",
|
|
|
+ toolSchema(
|
|
|
+ required("userId", "string", "用户 ID")
|
|
|
+ )
|
|
|
+ ));
|
|
|
+ tools.add(toolDef("get_my_pending_tasks",
|
|
|
+ "获取当前用户的待办任务列表(含审核中、待完成、即将到期)。按优先级和截止时间排序。",
|
|
|
+ toolSchema(
|
|
|
+ required("userId", "string", "用户 ID"),
|
|
|
+ optional("limit", "integer", "返回数量上限,默认 20")
|
|
|
+ )
|
|
|
+ ));
|
|
|
+ tools.add(toolDef("get_my_wishes_pending",
|
|
|
+ "获取当前用户的待审批心愿列表。包含心愿名称、状态(待审批/已通过/已拒绝)、积分等。",
|
|
|
+ toolSchema(
|
|
|
+ required("userId", "string", "用户 ID")
|
|
|
+ )
|
|
|
+ ));
|
|
|
+ tools.add(toolDef("get_energy_overview",
|
|
|
+ "获取指定家庭成员的五维能量概览:身/智/富/行/心五个维度的当前能量值和百分比排名。",
|
|
|
+ toolSchema(
|
|
|
+ required("memberId", "string", "家庭成员 ID"),
|
|
|
+ optional("date", "string", "查询日期,格式 yyyy-MM-dd,默认今天")
|
|
|
+ )
|
|
|
+ ));
|
|
|
+ tools.add(toolDef("get_family_summary",
|
|
|
+ "获取家庭整体概览:家庭成员数、今日待办任务数、本周活动数、总能量值排名。用于快速了解家庭状态。",
|
|
|
+ toolSchema(
|
|
|
+ required("userId", "string", "用户 ID(家庭管理员)")
|
|
|
+ )
|
|
|
+ ));
|
|
|
+ tools.add(toolDef("search_contact",
|
|
|
+ "根据昵称或手机号搜索联系人,返回匹配联系人的 ID、昵称、头像、最近互动时间。用于添加联系人推荐。",
|
|
|
+ toolSchema(
|
|
|
+ required("keyword", "string", "搜索关键词(昵称/手机号)"),
|
|
|
+ optional("limit", "integer", "返回数量上限,默认 10")
|
|
|
+ )
|
|
|
+ ));
|
|
|
+
|
|
|
+ Map<String, Object> resp = new LinkedHashMap<>();
|
|
|
+ resp.put("tools", tools);
|
|
|
+ return resp;
|
|
|
+ }
|
|
|
+
|
|
|
+ // ----------------------------------------------------------------
|
|
|
+ // tools/call
|
|
|
+ // ----------------------------------------------------------------
|
|
|
+
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
+ public Object callTool(Map<String, Object> params) {
|
|
|
+ String toolName = getString(params, "name");
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
+ Map<String, Object> args = (Map<String, Object>) params.get("arguments");
|
|
|
+ if (args == null) args = Collections.emptyMap();
|
|
|
+
|
|
|
+ switch (toolName) {
|
|
|
+ case "list_family_members": return handleListMembers(args);
|
|
|
+ case "get_today_tasks": return handleTodayTasks(args);
|
|
|
+ case "get_health_report_status": return handleHealthReportStatus(args);
|
|
|
+ case "send_ai_message": return handleAiChat(args);
|
|
|
+ case "search_activities": return handleSearchActivities(args);
|
|
|
+ case "search_articles": return handleSearchArticles(args);
|
|
|
+ case "search_products": return handleSearchProducts(args);
|
|
|
+ case "get_user_profile": return handleUserProfile(args);
|
|
|
+ case "get_my_pending_tasks": return handleMyPendingTasks(args);
|
|
|
+ case "get_my_wishes_pending": return handleMyWishesPending(args);
|
|
|
+ case "get_energy_overview": return handleEnergyOverview(args);
|
|
|
+ case "get_family_summary": return handleFamilySummary(args);
|
|
|
+ case "search_contact": return handleSearchContact(args);
|
|
|
+ default:
|
|
|
+ throw new IllegalArgumentException("Unknown tool: " + toolName);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // ----------------------------------------------------------------
|
|
|
+ // 各工具 handler
|
|
|
+ // ----------------------------------------------------------------
|
|
|
+
|
|
|
+ private Object handleListMembers(Map<String, Object> args) {
|
|
|
+ Long userId = parseLongArg(args, "userId");
|
|
|
+ int page = parseIntArg(args, "page", 1);
|
|
|
+ int pageSize = parseIntArg(args, "pageSize", 20);
|
|
|
+
|
|
|
+ List<?> all = familyMemberService.listMembers(userId);
|
|
|
+ int total = all.size();
|
|
|
+ int from = Math.min((page - 1) * pageSize, total);
|
|
|
+ int to = Math.min(from + pageSize, total);
|
|
|
+ List<?> pageList = all.subList(from, to);
|
|
|
+
|
|
|
+ Map<String, Object> result = new LinkedHashMap<>();
|
|
|
+ result.put("members", pageList);
|
|
|
+ result.put("total", total);
|
|
|
+ result.put("page", page);
|
|
|
+ result.put("pageSize", pageSize);
|
|
|
+ return wrapContent("获取到 " + total + " 名家庭成员", result);
|
|
|
+ }
|
|
|
+
|
|
|
+ private Object handleTodayTasks(Map<String, Object> args) {
|
|
|
+ Long memberId = parseLongArg(args, "memberId");
|
|
|
+ String dim = getStringArg(args, "dimensionCode");
|
|
|
+ if (dim != null) dim = dim.toLowerCase();
|
|
|
+
|
|
|
+ List<?> tasks;
|
|
|
+ if (dim != null && !dim.isEmpty()) {
|
|
|
+ tasks = taskService.getTodayTasks(memberId, dim);
|
|
|
+ } else {
|
|
|
+ tasks = taskService.getTodayTasks(memberId);
|
|
|
+ }
|
|
|
+
|
|
|
+ return wrapContent("获取到 " + tasks.size() + " 个今日任务", tasks);
|
|
|
+ }
|
|
|
+
|
|
|
+ private Object handleHealthReportStatus(Map<String, Object> args) {
|
|
|
+ Long draftId = parseLongArg(args, "draftId");
|
|
|
+ if (draftId == null) throw new IllegalArgumentException("draftId 必填");
|
|
|
+
|
|
|
+ HealthReportDraft draft = healthReportDraftService.getDraftById(draftId);
|
|
|
+ if (draft == null) {
|
|
|
+ return wrapContent("草稿不存在", null);
|
|
|
+ }
|
|
|
+
|
|
|
+ Map<String, Object> info = new LinkedHashMap<>();
|
|
|
+ info.put("draftId", draft.getId());
|
|
|
+ info.put("status", draft.getParseStatus());
|
|
|
+ info.put("parseError", draft.getParseError());
|
|
|
+ info.put("reportType", draft.getReportType());
|
|
|
+ info.put("createdAt", draft.getCreatedAt());
|
|
|
+ info.put("updatedAt", draft.getUpdatedAt());
|
|
|
+ return wrapContent("草稿 [" + draft.getId() + "] 状态:" + draft.getParseStatus(), info);
|
|
|
+ }
|
|
|
+
|
|
|
+ private Object handleAiChat(Map<String, Object> args) {
|
|
|
+ String query = getStringArg(args, "query");
|
|
|
+ if (query == null || query.isEmpty()) throw new IllegalArgumentException("query 必填");
|
|
|
+ String conversationId = getStringArg(args, "conversationId");
|
|
|
+ String contextStr = getStringArg(args, "context");
|
|
|
+ Map<String, Object> context = contextStr != null ? JSON.parseObject(contextStr) : null;
|
|
|
+
|
|
|
+ Map<String, Object> result = aiGateway.chat(query, 0L, conversationId, context);
|
|
|
+ if (result == null) {
|
|
|
+ return wrapContent("AI 服务暂时不可用,请稍后重试", null);
|
|
|
+ }
|
|
|
+
|
|
|
+ String answer = (String) result.get("answer");
|
|
|
+ String convId = (String) result.get("conversationId");
|
|
|
+ Map<String, Object> data = new LinkedHashMap<>();
|
|
|
+ data.put("answer", answer);
|
|
|
+ data.put("conversationId", convId);
|
|
|
+ Object tasks = result.get("tasks");
|
|
|
+ if (tasks != null) data.put("tasks", tasks);
|
|
|
+ return wrapContent(answer, data);
|
|
|
+ }
|
|
|
+
|
|
|
+ private Object handleSearchActivities(Map<String, Object> args) {
|
|
|
+ String dim = getStringArg(args, "dimensionCode");
|
|
|
+ if (dim != null) dim = dim.toLowerCase();
|
|
|
+ int page = parseIntArg(args, "page", 1);
|
|
|
+ int pageSize = parseIntArg(args, "pageSize", 10);
|
|
|
+
|
|
|
+ Result<Map<String, Object>> result = activityService.list(dim, page, pageSize, 0L);
|
|
|
+ if (result == null || result.getCode() != 200) {
|
|
|
+ return wrapContent("查询活动失败", null);
|
|
|
+ }
|
|
|
+
|
|
|
+ Map<String, Object> data = result.getData();
|
|
|
+ List<?> records = (List<?>) data.get("records");
|
|
|
+ long total = toLong(data.get("total"));
|
|
|
+ return wrapContent("查询到 " + total + " 条活动记录", records);
|
|
|
+ }
|
|
|
+
|
|
|
+ private Object handleSearchArticles(Map<String, Object> args) {
|
|
|
+ Long categoryId = parseLongArg(args, "categoryId");
|
|
|
+ String keyword = getStringArg(args, "keyword");
|
|
|
+ String dim = getStringArg(args, "dimensionCode");
|
|
|
+ if (dim != null) dim = dim.toLowerCase();
|
|
|
+ int page = parseIntArg(args, "page", 1);
|
|
|
+ int pageSize = parseIntArg(args, "pageSize", 10);
|
|
|
+
|
|
|
+ Page<Article> pageResult =
|
|
|
+ articleService.getPublicList(categoryId, keyword, dim, page, pageSize, 0L, null, null);
|
|
|
+ List<?> records = pageResult.getRecords();
|
|
|
+ long total = pageResult.getTotal();
|
|
|
+ return wrapContent("查询到 " + total + " 篇文章", records);
|
|
|
+ }
|
|
|
+
|
|
|
+ private Object handleSearchProducts(Map<String, Object> args) {
|
|
|
+ String productType = getStringArg(args, "productType");
|
|
|
+ String keyword = getStringArg(args, "keyword");
|
|
|
+ Boolean growthRelated = getBoolArg(args, "growthRelated");
|
|
|
+ int page = parseIntArg(args, "page", 1);
|
|
|
+ int pageSize = parseIntArg(args, "pageSize", 10);
|
|
|
+
|
|
|
+ ProductListQueryDTO query = new ProductListQueryDTO();
|
|
|
+ query.setPage(page);
|
|
|
+ query.setSize(pageSize);
|
|
|
+ query.setProductType(productType);
|
|
|
+ query.setKeyword(keyword);
|
|
|
+ query.setGrowthRelated(growthRelated);
|
|
|
+
|
|
|
+ Result<Map<String, Object>> result = productService.list(query, 0L, null);
|
|
|
+ if (result == null || result.getCode() != 200) {
|
|
|
+ return wrapContent("查询商品失败", null);
|
|
|
+ }
|
|
|
+
|
|
|
+ Map<String, Object> data = result.getData();
|
|
|
+ List<?> records = (List<?>) data.get("records");
|
|
|
+ long total = toLong(data.get("total"));
|
|
|
+ return wrapContent("查询到 " + total + " 件商品", records);
|
|
|
+ }
|
|
|
+
|
|
|
+ private Object handleUserProfile(Map<String, Object> args) {
|
|
|
+ Long userId = parseLongArg(args, "userId");
|
|
|
+ if (userId == null) throw new IllegalArgumentException("userId 必填");
|
|
|
+
|
|
|
+ com.etotem.cfc.entity.User user = userService.getUserInfo(userId);
|
|
|
+ if (user == null) {
|
|
|
+ return wrapContent("用户不存在", null);
|
|
|
+ }
|
|
|
+
|
|
|
+ Map<String, Object> profile = new LinkedHashMap<>();
|
|
|
+ profile.put("userId", user.getId());
|
|
|
+ profile.put("nickname", user.getNickname());
|
|
|
+ profile.put("phone", user.getPhone());
|
|
|
+ profile.put("avatar", user.getAvatar());
|
|
|
+ profile.put("role", user.getRole());
|
|
|
+ profile.put("familyId", user.getFamilyId());
|
|
|
+ profile.put("createdAt", user.getCreatedAt());
|
|
|
+ profile.put("updatedAt", user.getUpdatedAt());
|
|
|
+ return wrapContent("用户信息", profile);
|
|
|
+ }
|
|
|
+
|
|
|
+ private Object handleMyPendingTasks(Map<String, Object> args) {
|
|
|
+ Long memberId = parseLongArg(args, "memberId");
|
|
|
+ if (memberId == null) throw new IllegalArgumentException("memberId 必填(可传 userId 同值)");
|
|
|
+ List<?> tasks = taskService.getMyPendingTasks(memberId);
|
|
|
+ return wrapContent("获取到 " + tasks.size() + " 个待办任务", tasks);
|
|
|
+ }
|
|
|
+
|
|
|
+ private Object handleMyWishesPending(Map<String, Object> args) {
|
|
|
+ Long userId = parseLongArg(args, "userId");
|
|
|
+ if (userId == null) throw new IllegalArgumentException("userId 必填");
|
|
|
+ // 通过 userId 获取其 familyId,再查家庭待审批心愿
|
|
|
+ com.etotem.cfc.entity.User user = userService.getUserInfo(userId);
|
|
|
+ if (user == null || user.getFamilyId() == null) {
|
|
|
+ return wrapContent("用户不属于任何家庭", Collections.emptyList());
|
|
|
+ }
|
|
|
+ List<?> wishes = wishService.getParentPendingWishes(user.getFamilyId());
|
|
|
+ return wrapContent("获取到 " + wishes.size() + " 个待审批心愿", wishes);
|
|
|
+ }
|
|
|
+
|
|
|
+ private Object handleEnergyOverview(Map<String, Object> args) {
|
|
|
+ Long memberId = parseLongArg(args, "memberId");
|
|
|
+ if (memberId == null) throw new IllegalArgumentException("memberId 必填");
|
|
|
+
|
|
|
+ Map<String, Object> overview = energyService.getOverview(memberId);
|
|
|
+ return wrapContent("五维能量概览", overview);
|
|
|
+ }
|
|
|
+
|
|
|
+ private Object handleFamilySummary(Map<String, Object> args) {
|
|
|
+ Long userId = parseLongArg(args, "userId");
|
|
|
+ if (userId == null) throw new IllegalArgumentException("userId 必填");
|
|
|
+
|
|
|
+ Map<String, Object> summary = familyContextService.buildContext(userId);
|
|
|
+ return wrapContent("家庭概览", summary);
|
|
|
+ }
|
|
|
+
|
|
|
+ private Object handleSearchContact(Map<String, Object> args) {
|
|
|
+ Long userId = parseLongArg(args, "userId");
|
|
|
+ if (userId == null) throw new IllegalArgumentException("userId 必填");
|
|
|
+
|
|
|
+ com.etotem.cfc.common.Result<java.util.List<com.etotem.cfc.dto.ContactDTO>> result = contactService.list(userId);
|
|
|
+ List<?> contacts = (result != null && result.getCode() == 200) ? result.getData() : Collections.emptyList();
|
|
|
+ return wrapContent("获取到 " + contacts.size() + " 个联系人", contacts);
|
|
|
+ }
|
|
|
+
|
|
|
+ // ----------------------------------------------------------------
|
|
|
+ // 工具定义辅助方法
|
|
|
+ // ----------------------------------------------------------------
|
|
|
+
|
|
|
+ private Map<String, Object> toolDef(String name, String desc, Map<String, Object> schema) {
|
|
|
+ Map<String, Object> tool = new LinkedHashMap<>();
|
|
|
+ tool.put("name", name);
|
|
|
+ tool.put("description", desc);
|
|
|
+ tool.put("inputSchema", schema);
|
|
|
+ return tool;
|
|
|
+ }
|
|
|
+
|
|
|
+ private Map<String, Object> toolSchema(Map<String, Object>... properties) {
|
|
|
+ Map<String, Object> schema = new LinkedHashMap<>();
|
|
|
+ schema.put("type", "object");
|
|
|
+ schema.put("properties", joinProperties(properties));
|
|
|
+ schema.put("required", extractRequired(properties));
|
|
|
+ return schema;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static Map<String, Object> required(String name, String type, String description) {
|
|
|
+ Map<String, Object> p = new LinkedHashMap<>();
|
|
|
+ p.put("name", name);
|
|
|
+ p.put("type", type);
|
|
|
+ p.put("description", description);
|
|
|
+ return p;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static Map<String, Object> optional(String name, String type, String description) {
|
|
|
+ Map<String, Object> p = new LinkedHashMap<>();
|
|
|
+ p.put("name", name);
|
|
|
+ p.put("type", type);
|
|
|
+ p.put("description", description);
|
|
|
+ return p;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static Map<String, Object> joinProperties(Map<String, Object>[] properties) {
|
|
|
+ Map<String, Object> result = new LinkedHashMap<>();
|
|
|
+ for (Map<String, Object> p : properties) {
|
|
|
+ String key = (String) p.get("name");
|
|
|
+ if (key != null) result.put(key, p);
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static List<String> extractRequired(Map<String, Object>[] properties) {
|
|
|
+ List<String> required = new ArrayList<>();
|
|
|
+ for (Map<String, Object> p : properties) {
|
|
|
+ String desc = (String) p.get("description");
|
|
|
+ if (desc != null && desc.startsWith("必填")) {
|
|
|
+ required.add((String) p.get("name"));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return required;
|
|
|
+ }
|
|
|
+
|
|
|
+ // ----------------------------------------------------------------
|
|
|
+ // 响应封装
|
|
|
+ // ----------------------------------------------------------------
|
|
|
+
|
|
|
+ private Map<String, Object> wrapContent(String summaryText, Object data) {
|
|
|
+ Map<String, Object> contentItem = new LinkedHashMap<>();
|
|
|
+ contentItem.put("type", "text");
|
|
|
+ contentItem.put("text", summaryText);
|
|
|
+ List<Map<String, Object>> content = new ArrayList<>();
|
|
|
+ content.add(contentItem);
|
|
|
+ Map<String, Object> result = new LinkedHashMap<>();
|
|
|
+ result.put("content", content);
|
|
|
+ if (data != null) {
|
|
|
+ result.put("data", data);
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ // ----------------------------------------------------------------
|
|
|
+ // 参数解析工具
|
|
|
+ // ----------------------------------------------------------------
|
|
|
+
|
|
|
+ private Long parseLongArg(Map<String, Object> args, String key) {
|
|
|
+ Object v = args.get(key);
|
|
|
+ if (v == null) return null;
|
|
|
+ if (v instanceof Number) return ((Number) v).longValue();
|
|
|
+ try { return Long.parseLong(v.toString()); } catch (NumberFormatException e) {
|
|
|
+ throw new IllegalArgumentException(key + " 必须为数字");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private int parseIntArg(Map<String, Object> args, String key, int defaultVal) {
|
|
|
+ Object v = args.get(key);
|
|
|
+ if (v == null) return defaultVal;
|
|
|
+ if (v instanceof Number) return ((Number) v).intValue();
|
|
|
+ try { return Integer.parseInt(v.toString()); } catch (NumberFormatException e) {
|
|
|
+ return defaultVal;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private boolean getBoolArg(Map<String, Object> args, String key) {
|
|
|
+ Object v = args.get(key);
|
|
|
+ if (v == null) return false;
|
|
|
+ if (v instanceof Boolean) return (Boolean) v;
|
|
|
+ return Boolean.parseBoolean(v.toString());
|
|
|
+ }
|
|
|
+
|
|
|
+ private String getStringArg(Map<String, Object> args, String key) {
|
|
|
+ Object v = args.get(key);
|
|
|
+ return v != null ? v.toString() : null;
|
|
|
+ }
|
|
|
+
|
|
|
+ private String getString(Map<String, Object> map, String key) {
|
|
|
+ Object v = map.get(key);
|
|
|
+ return v != null ? v.toString() : null;
|
|
|
+ }
|
|
|
+
|
|
|
+ private long toLong(Object v) {
|
|
|
+ if (v == null) return 0L;
|
|
|
+ if (v instanceof Number) return ((Number) v).longValue();
|
|
|
+ try { return Long.parseLong(v.toString()); } catch (NumberFormatException e) { return 0L; }
|
|
|
+ }
|
|
|
+
|
|
|
+ private Date parseDate(String s) {
|
|
|
+ try {
|
|
|
+ java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy-MM-dd");
|
|
|
+ return sdf.parse(s);
|
|
|
+ } catch (Exception e) {
|
|
|
+ return new Date();
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|