--- doc_id: STD-202605-001 feature_id: type: standard title: 医梦 AI 中台工程约束 status: approved owner: 医梦研发团队 created_at: 2026-05-31 updated_at: 2026-05-31 reviewers: [] related_docs: - ODS-202606-001 related_modules: - emoon-openplatform - emoon-ai-agent - emoon-ai-card - emoon-ai-mcp - emoon-ai-device tags: - 工程约束 - 模块边界 - 架构红线 --- # 医梦 AI 中台工程约束 | 项目 | 内容 | | --- | --- | | 文档定位 | 工程结构、模块边界、依赖规则、接口归属、调用方向约束 | | 当前章节 | Maven 工程依赖 + Controller/Service 调用方向 + 接口→模块映射 | | 适用范围 | 现有后端工程、二期 AI Agent / Card / MCP / Device / File、三期计量 / 账务 / 运营 | | 约束目标 | 高内聚、低耦合、可演进、可测试、可独立部署 | | 版本 | v2.1 | | 更新时间 | 2026-05-31 | | 变更说明 | 补充统一入口客户端落位:后端能力仍归 AI 中台模块,前端/移动端另起 `emoon-terminal-client`;明确 AgentRouter、TaskStateService、TongueDiagnosisAdapter、HisMockAdapter 的模块归属;修正 Card 动作不能直连 MCP 的调用方向 | ## 1. Maven 工程依赖规范约束 ### 1.1 结论 当前阶段不建议优先微服务化。医梦 AI 中台仍处于能力快速建设期,团队规模约 3 名后端开发,直接拆微服务会过早引入服务治理、部署、链路追踪、分布式事务、接口版本管理和运维成本。 更合理的方向是: ```text 先做模块化单体,再保留未来微服务拆分边界。 ``` 也就是说,短期仍然保持一个 Maven 多模块工程和少量启动应用,但必须先把 API 契约、领域实现、入口适配、基础设施、第三方系统适配分开。后续如果某个模块需要独立部署,只需要把已经稳定的 Maven 模块搬成服务,而不是重新拆业务逻辑。 本章的核心约束是: ```text 入口层只负责编排入口,不沉淀业务状态。 API 模块只定义契约,不包含实现和重依赖。 领域实现模块只处理自己的业务闭环,不跨模块操作数据表。 计量、账务、合同、运营统计独立成模块,不混入 Agent 调用链。 MCP 只负责工具协议和治理,不承载所有院内系统适配细节。 ``` 本规约的工程取舍基于公开可验证案例做裁剪:东软类智慧医院平台证明“统一入口集成子系统”方向可行,但医梦只做 AI 入口层;华为/医惠类智慧病房证明病房物联网接入是重资产平台能力,因此住院设备只做事件映射;Dify 官方已有 Workflow 版本能力,因此后端只做版本映射和输出治理;科大讯飞类医疗 AI 产品强调嵌入医生流程,因此高风险动作必须通过卡片、工具审计和人工确认。以上案例用于约束边界,不作为照搬大厂平台规模的理由。 ### 1.2 当前工程基线 现有工程大体可以分为以下几层: | 层级 | 当前模块 | 当前职责 | 状态 | | --- | --- | --- | --- | | 启动入口 | `emoon-admin` | 管理后台启动模块 | ✅ 合理 | | 开放入口 | `emoon-openplatform` | 开放平台接口、签名鉴权、SSE | ✅ 已重构:8 个 Mapper 清零,全改用 API Service;旧 ApiSseController 已降级 | | 基础设施 | `emoon-infra/emoon-common-*` | Web、MyBatis、Redis、OSS 等公共能力 | ✅ 稳定 | | API 契约 | `emoon-infra/emoon-modules-api/*` | 系统、知识库、AI Agent 等 API 契约 | ⚠️ DO 类仍在 API 模块(47 条,RuoYi 框架基因),Mapper/ServiceImpl/EngineImpl 已全部迁出 | | 领域实现 | `emoon-infra/emoon-modules/*` | 系统、知识库、聊天、AI 等业务实现 | ✅ Mapper+ServiceImpl 已全部在实现模块,领域边界逐渐清晰 | | 扩展服务 | `emoon-extend/*` | 监控、迁移、文件下载、舌诊等 | ✅ 未变 | 当前最需要收口的不是模块数量,而是依赖方向。 现状风险: | 风险 | 典型表现 | 后果 | | --- | --- | --- | | API 模块过重 | `*-api` 中出现 Mapper、ServiceImpl、EngineImpl、第三方 SDK | 调用方为了一个 DTO 被迫引入实现依赖 | | 入口层过重 | `openplatform` 直接调用 Mapper、EngineFactory、业务表 | 鉴权、会话、计量、账务散落在入口层 | | AI 领域过度集中 | `emoon-mcp-api` 同时包含 Agent、Card、MCP、会话、活动等概念 | 后续二期/三期继续堆功能会形成新巨石 | | 计费逻辑未独立 | 用量、能力值、合同、账单没有独立模块 | 无法支撑真实合同和线上对账 | | 院内适配边界不清 | MCP 工具和 HIS/EMR/LIS/PACS 适配容易混在一起 | 工具治理、院内系统集成和业务编排互相污染 | ### 1.3 目标模块分层 推荐采用以下 Maven 分层。该结构不要求一次性全部改完,但新功能必须按这个方向落位。 ```text emoon-backend ├── emoon-admin # 管理后台启动入口 ├── emoon-openplatform # 开放平台入口适配 ├── emoon-infra │ ├── emoon-common # 通用基础设施 │ ├── emoon-modules-api # 领域 API 契约 │ │ ├── emoon-system-api │ │ ├── emoon-chat-api │ │ ├── emoon-knowledge-api │ │ └── emoon-ai-api # 建议新增 AI API 聚合父模块 │ │ ├── emoon-ai-agent-api │ │ ├── emoon-ai-card-api │ │ ├── emoon-ai-mcp-api │ │ ├── emoon-ai-device-api # 🆕 设备注册中心 API │ │ ├── emoon-ai-file-api # 🆕 文件服务 API │ │ ├── emoon-ai-meter-api │ │ ├── emoon-ai-billing-api │ │ ├── emoon-ai-contract-api │ │ └── emoon-ai-operation-api │ └── emoon-modules # 领域实现 │ ├── emoon-system │ ├── emoon-chat │ ├── emoon-knowledge │ └── emoon-ai # 建议新增 AI 实现聚合父模块 │ ├── emoon-ai-agent │ ├── emoon-ai-card │ ├── emoon-ai-mcp │ ├── emoon-ai-device # 🆕 设备注册中心实现 │ ├── emoon-ai-file # 🆕 文件服务实现 │ ├── emoon-ai-meter │ ├── emoon-ai-billing │ ├── emoon-ai-contract │ └── emoon-ai-operation └── emoon-extend # 外围扩展、任务、迁移、专病能力 ``` #### 1.3.1 目标依赖方向 ```mermaid flowchart TB Admin["emoon-admin"] Open["emoon-openplatform"] Extend["emoon-extend"] SystemApi["emoon-system-api"] KnowledgeApi["emoon-knowledge-api"] ChatApi["emoon-chat-api"] AgentApi["emoon-ai-agent-api"] CardApi["emoon-ai-card-api"] McpApi["emoon-ai-mcp-api"] DeviceApi["emoon-ai-device-api"] FileApi["emoon-ai-file-api"] MeterApi["emoon-ai-meter-api"] BillingApi["emoon-ai-billing-api"] ContractApi["emoon-ai-contract-api"] OperationApi["emoon-ai-operation-api"] Agent["emoon-ai-agent"] Card["emoon-ai-card"] Mcp["emoon-ai-mcp"] Device["emoon-ai-device"] File["emoon-ai-file"] Meter["emoon-ai-meter"] Billing["emoon-ai-billing"] Contract["emoon-ai-contract"] Operation["emoon-ai-operation"] Common["emoon-common-*"] External["Dify / LLM / HIS / EMR / LIS / PACS"] Admin --> SystemApi Admin --> AgentApi Admin --> CardApi Admin --> DeviceApi Admin --> FileApi Admin --> BillingApi Admin --> ContractApi Admin --> OperationApi Open --> SystemApi Open --> AgentApi Open --> CardApi Open --> DeviceApi Open --> FileApi Open --> MeterApi Extend --> AgentApi Extend --> McpApi Extend --> MeterApi Agent --> AgentApi Agent --> CardApi Agent --> McpApi Agent --> DeviceApi Agent --> FileApi Agent --> MeterApi Agent --> ContractApi Agent --> Common Agent --> External Card --> CardApi Card --> AgentApi Card --> Common Mcp --> McpApi Mcp --> SystemApi Mcp --> FileApi Mcp --> Common Mcp --> External Device --> DeviceApi Device --> SystemApi Device --> Common File --> FileApi File --> SystemApi File --> Common Meter --> MeterApi Meter --> ContractApi Meter --> Common Billing --> BillingApi Billing --> MeterApi Billing --> ContractApi Billing --> Common Contract --> ContractApi Contract --> SystemApi Contract --> Common Operation --> OperationApi Operation --> MeterApi Operation --> BillingApi Operation --> ContractApi Operation --> Common KnowledgeApi --> Common ChatApi --> Common SystemApi --> Common ``` 图中的箭头表示允许依赖方向。没有画出的方向默认不允许。 ### 1.4 模块职责定义 #### 1.4.1 入口模块 | 模块 | 允许职责 | 禁止职责 | | --- | --- | --- | | `emoon-admin` | 管理后台入口、菜单权限、运营配置页面、调用领域 API | 直接操作 AI 业务表、直接调用 Dify/LLM、直接扣减能力值 | | `emoon-openplatform` | 对外 API、签名鉴权、租户识别、参数校验、SSE 输出、入口限流 | 保存 AI 会话核心状态、直接查写 Mapper、直接选择引擎实现、直接计费 | | `emoon-extend` | 文件处理、迁移、外围任务、专病扩展、非主链路能力 | 承载 Agent 主链路、承载账务核心状态、绕过领域 API 改业务数据 | 入口模块的原则: ```text 入口模块可以知道“谁来调用、是否有权限、把请求交给谁”, 但不应该知道“AI 会话怎么编排、卡片状态怎么流转、能力值怎么扣”。 ``` #### 1.4.2 API 契约模块 `*-api` 是工程边界的核心。它必须保持轻量,避免把实现细节传染给上游。 允许放入: | 类型 | 示例 | | --- | --- | | DTO / Command / Query | `AgentChatCommand`、`CreateCardInstanceCommand` | | VO / Response | `AgentChatResponse`、`CardRenderPayload` | | Enum / Constant | `EngineType`、`MeterEventStatus` | | Facade Interface | `AgentChatFacade`、`BillingAccountFacade` | | Event Contract | `UsageMeteredEvent`、`CardActionSubmittedEvent` | | SPI Interface | `AgentEngine`、`McpToolInvoker` | 禁止放入: | 禁止内容 | 原因 | | --- | --- | | `Mapper` | API 调用方不应该拿到数据访问能力 | | `ServiceImpl` | API 模块不承载实现 | | `Controller` | API 模块不暴露 Web 入口 | | `EngineImpl` | Dify、DirectLLM、Mock 等实现必须放到实现模块 | | 第三方 SDK 客户端 | 会把重依赖传染给所有调用方 | | MyBatis XML / SQL | 破坏 API 与实现隔离 | | Web/SSE/WebSocket 具体实现 | 入口协议属于应用层或实现层 | #### 1.4.3 AI Agent 模块 | 模块 | 规划职责 | | --- | --- | | `emoon-ai-agent-api` | 定义 AI 调用、会话、流式事件、引擎抽象、编排命令、终端任务状态只读 DTO | | `emoon-ai-agent` | 实现 Agent 主链路、AgentRouter、ConversationService、TaskStateService、DifyAgentEngine、DifyOutputNormalizer、DirectLLM 引擎、Mock 引擎、SSE 事件转换、降级、幂等、Outbox | `emoon-ai-agent` 是 AI 中台二期的主链路模块,但不能变成所有 AI 功能的大杂烩。 允许依赖: ```text emoon-ai-agent-api emoon-ai-card-api emoon-ai-mcp-api emoon-ai-device-api emoon-ai-file-api emoon-ai-meter-api emoon-ai-contract-api emoon-system-api emoon-knowledge-api 必要的 emoon-common-* ``` 禁止依赖: ```text emoon-ai-billing 实现模块 emoon-ai-operation 实现模块 openplatform admin 任意 Controller 层 ``` 关键约束: ```text emoon-ai-agent 可以产生计量事件,但不能直接扣款。 emoon-ai-agent 可以读取合同授权快照,但不能维护合同。 emoon-ai-agent 可以调用 MCP 工具 API,但不能绕过 MCP 治理直连 HIS。 AgentRouter 和 TaskStateService 是 Agent 主链路内部能力,第一期不新增公共 /tasks API。 TaskStateService 必须持久化到 `ai_task_instance`,不能只放 Redis 或 `ai_conversation.ext_json`;Redis 只能作为短期缓存。 DifyOutputNormalizer 是 `DifyAgentEngine` 的内部组件,负责输出结构校验、卡片 schema 校验、风险等级归一和 SSE 事件归一,不新增独立模块。 卡片动作涉及 HIS 写操作时,由 Agent 编排链路调用 Card 做状态校验,再调用 MCP 工具,不允许 Card 自己直连 MCP。 ``` #### 1.4.4 AI Card 模块 | 模块 | 规划职责 | | --- | --- | | `emoon-ai-card-api` | 定义卡片模板、卡片实例、卡片动作、渲染数据、状态事件 | | `emoon-ai-card` | 实现卡片状态机、动作幂等、卡片版本、卡片事件、渲染数据聚合 | 关键约束: ```text emoon-ai-card 只负责“确定性交互状态”,不负责模型推理。 emoon-ai-card 不能直接调用 MCP 工具。 emoon-ai-card 需要调用工具时,必须通过 emoon-ai-agent 编排。 ``` 原因: 如果卡片可以直接调用 MCP,系统会出现两条工具调用链: ```text Agent -> MCP Card -> MCP ``` 这样会导致权限、审计、计量、失败补偿出现双标准。生产环境中必须保持一条主链路。 #### 1.4.5 AI MCP 模块 | 模块 | 规划职责 | | --- | --- | | `emoon-ai-mcp-api` | 定义工具注册、工具调用、工具权限、工具审计、工具结果契约 | | `emoon-ai-mcp` | 实现工具注册表、工具路由、权限校验、限流熔断、工具调用审计、HisMockAdapter、RealHisAdapter、TongueDiagnosisAdapter | `emoon-ai-mcp` 不等于所有院内系统适配。它负责工具协议治理,具体 HIS / EMR / LIS / PACS 的适配可以继续放在当前合适模块中,或后续独立为 `hospital-integration` / `emoon-hospital-adapter`。 关键约束: ```text MCP 管工具治理,不管业务编排。 HIS 适配管系统协议,不管 Agent 对话。 Agent 管流程编排,不管 HIS 细节。 TongueDiagnosisAdapter 在 MVP 阶段作为 MCP 受控工具适配器处理,后续形成独立专精产品线时再拆模块。 HisMockAdapter 必须显式命名和配置,不能把 Mock 结果包装成真实医院交易。 ``` #### 1.4.6 计量模块 | 模块 | 规划职责 | | --- | --- | | `emoon-ai-meter-api` | 定义计量事件、计量状态、幂等键、用量查询接口 | | `emoon-ai-meter` | 实现 Token/次数/分钟/项目/年费等用量归集、状态机、重放、去重 | 计量模块是三期商业闭环的第一层,不是账务模块。 关键约束: ```text 业务模块只提交 usage event。 emoon-ai-meter 负责判断这个 event 是否可信、是否重复、是否可计量。 emoon-ai-billing 再根据计量结果做扣款或结算。 ``` 禁止: ```text emoon-ai-agent 直接写账务流水。 emoon-ai-card 直接扣能力值。 openplatform 直接计算费用。 ``` #### 1.4.7 账务模块 | 模块 | 规划职责 | | --- | --- | | `emoon-ai-billing-api` | 定义账户余额、冻结、扣减、退款、冲正、账单查询契约 | | `emoon-ai-billing` | 实现能力值包预扣款、账务流水、账单、对账、调账、价格快照 | 账务模块必须独立,原因是账务有强一致性要求,而 Agent 调用链有高并发和失败降级要求。两者耦合会导致调用失败、重复扣款、漏扣款和对账困难。 关键约束: ```text 扣款必须基于 pricing snapshot。 账务流水必须可追溯、可冲正、不可物理删除。 同一 meter event 只能入账一次。 ``` #### 1.4.8 合同模块 | 模块 | 规划职责 | | --- | --- | | `emoon-ai-contract-api` | 定义客户合同、能力值包、价格策略、授权范围、有效期 | | `emoon-ai-contract` | 实现合同状态机、套餐绑定、能力包启停、区域能力池规则 | 合同模块不应该放在 `system` 里。`system` 可以提供租户、用户、组织、权限,但合同、套餐、能力值包属于 AI 商业闭环。 #### 1.4.9 运营统计模块 | 模块 | 规划职责 | | --- | --- | | `emoon-ai-operation-api` | 定义统计查询、运营报表、客户看板、异常分析契约 | | `emoon-ai-operation` | 实现统计宽表、报表聚合、医院/科室/医生/智能体维度分析 | 运营统计不能直接拖核心业务表做复杂联表,否则后续会拖慢主链路。 推荐方式: ```text emoon-ai-meter / emoon-ai-billing / emoon-ai-agent 产生事件 Outbox 或定时任务汇总到 operation 宽表 emoon-ai-operation 面向后台和客户报表查询 ``` #### 1.4.10 Device 设备注册模块 | 模块 | 规划职责 | | --- | --- | | `emoon-ai-device-api` | 定义设备注册、心跳、场景配置、事件上报、命令下发、设备查询契约 | | `emoon-ai-device` | 实现 Device Registry、设备身份管理、准入等级校验(A/B/C/D)、心跳监控、Scene Orchestrator、命令轮询与回执、设备事件接入 | 关键约束: ```text 门诊设备(导诊屏、自助机、机器人、诊室屏等)由医梦 Device Registry 管理,包含注册、心跳、场景和基础状态。 住院侧设备类型(nurse_pda / nursing_whiteboard / bedside_screen / ward_robot) 默认 managementMode = mapped_external,由迪耐克/鸿蒙病房体系管理硬件生命周期, 医梦只记录映射关系和 AI 事件互通,不承担住院硬件注册、心跳升级和故障主责。 设备事件只上报业务摘要,不保存患者连续轨迹。 ``` 设备管理模式枚举: | managementMode | 说明 | 典型设备 | |---|---|---| | `emoon_managed` | 医梦统一入口客户端和 Device Registry 管理 | 导诊屏、自助机、门诊机器人、诊室屏 | | `partner_managed` | 合作厂商管理硬件,医梦通过标准接口接入 | 第三方机器人、四诊仪 | | `mapped_external` | 外部体系管理,医梦只做映射和事件互通 | 迪耐克床头屏、护理白板、护士 PDA | 允许依赖: ```text emoon-ai-device-api emoon-system-api(查询医院/科室绑定) 必要的 emoon-common-* ``` 禁止依赖: ```text emoon-ai-agent(设备不编排 AI 流程) emoon-ai-card(设备不管理卡片状态) emoon-ai-billing(设备不计费) emoon-openplatform(设备不反向依赖入口层) ``` #### 1.4.11 File 文件服务模块 | 模块 | 规划职责 | | --- | --- | | `emoon-ai-file-api` | 定义文件上传、元数据查询、删除、安全扫描状态契约 | | `emoon-ai-file` | 实现文件接收、对象存储适配、安全扫描对接、retentionPolicy 管理、幂等去重 | 关键约束: ```text 文件必须先上传获得 fileId,后续 Agent/Card 调用只传 fileId。 不默认返回可公开访问 URL。 舌诊/面诊图片、报告原文按敏感医疗数据治理。 retentionPolicy 不传时,按 purpose 推断默认值: tongue_diagnosis / face_diagnosis → episode report_interpretation → episode medical_record → medical_record audio_transcription → transient other → transient DELETE 必须支持幂等,同一文件重复删除返回首次结果或当前已删除状态。 文件上传幂等键建议:projectId + partnerId + clientFileId。 ``` 允许依赖: ```text emoon-ai-file-api emoon-system-api emoon-common-oss 必要的 emoon-common-* ``` 禁止依赖: ```text emoon-ai-agent emoon-ai-card emoon-ai-mcp emoon-openplatform ``` #### 1.4.12 统一入口客户端工程归属 统一入口客户端不是一个新的后端系统。所有后端能力必须落在 AI 中台现有目标模块中: | 能力 | 归属模块 | 说明 | | --- | --- | --- | | 设备注册、心跳、场景配置、设备事件、命令轮询 | `emoon-ai-device` | 支撑机器人、自助机、导诊屏、诊室屏启动和运维 | | AgentRouter、ConversationService、TaskStateService、DifyAgentEngine、DifyOutputNormalizer | `emoon-ai-agent` | 统一入口客户端只调用 Agent API,不感知内部路由和输出归一 | | 卡片定义、实例、动作幂等、快照 | `emoon-ai-card` | 卡片动作的业务编排由 Agent 主链路承接 | | 文件上传、元数据、删除、访问审计 | `emoon-ai-file` | 舌象、身份证、报告、音频都先拿 `fileId` | | HisMockAdapter、RealHisAdapter、TongueDiagnosisAdapter | `emoon-ai-mcp` | 作为受控工具适配器,不让终端直连 | | 审计、计量、Outbox | `emoon-ai-meter` / `emoon-system` | 记录事实,不参与前端渲染 | 前端和移动端独立为 `emoon-terminal-client` 工程: ```text emoon-terminal-client ├── apps/robot-client ├── apps/kiosk-client ├── apps/guide-screen-client └── packages/api-client / medical-cards / terminal-bridge / terminal-theme / terminal-core ``` 后端仓库只维护 OpenAPI、DTO、业务模块和 Mock/Adapter。不得在 `emoon-backend` 内新增长期维护的终端 Demo 后端、前端页面构建或设备 SDK UI 代码。临时 Demo 只能作为联调样例,且必须通过 OpenPlatform API 调用正式后端能力。 ### 1.5 依赖白名单 #### 1.5.1 API 模块依赖白名单 API 模块允许依赖: | 依赖 | 用途 | | --- | --- | | `emoon-common-core` | 基础实体、通用返回、工具类型 | | `jakarta.validation` | 参数校验注解 | | `jackson` | JSON 序列化注解 | | `lombok` | 简化 DTO | | `mapstruct` 注解 | DTO 转换契约,需谨慎使用 | API 模块禁止依赖: ```text emoon-common-web emoon-common-mybatis emoon-common-sse emoon-common-websocket emoon-common-oss emoon-common-doc emoon-common-excel langchain4j pdfbox tika okhttp spring-boot-starter-web mybatis-plus 任何具体数据库驱动 ``` 例外必须写 ADR,并说明为什么该依赖必须进入 API 层。 #### 1.5.2 实现模块依赖白名单 实现模块可以依赖: ```text 自身 api 其他领域 api 必要的 emoon-common-* 必要的第三方 SDK ``` 实现模块禁止: ```text 依赖入口模块,例如 emoon-openplatform、emoon-admin 依赖其他模块的 impl 包 跨模块注入 Mapper 跨模块直接更新对方业务表 ``` #### 1.5.3 入口模块依赖白名单 入口模块允许依赖: ```text 领域 api emoon-common-web emoon-common-security emoon-common-sse emoon-common-ratelimiter emoon-common-tenant ``` 入口模块禁止: ```text 直接依赖领域实现模块 直接依赖 Mapper 直接调用第三方 AI SDK 直接写核心业务状态表 ``` ### 1.6 禁止依赖关系 以下依赖关系默认禁止: | 禁止方向 | 原因 | | --- | --- | | `*-api` -> `*-impl` | API 层不能依赖实现层 | | `*-api` -> `Mapper` | API 层不能暴露数据访问 | | `openplatform` -> `Mapper` | 入口层不应绕过领域服务 | | `openplatform` -> `AgentEngineFactory` 实现 | 入口层不应选择具体引擎 | | `emoon-ai-card` -> `emoon-ai-mcp` 实现 | 卡片不能绕过 Agent 调工具 | | `emoon-ai-billing` -> `emoon-ai-agent` 实现 | 账务不应反向依赖调用链 | | `emoon-ai-operation` -> 核心表复杂联查 | 统计不能拖慢主链路 | | `system` -> `emoon-ai-billing` 实现 | 系统基础域不能反向依赖商业域 | | `knowledge` -> `emoon-ai-agent` 实现 | 知识库应作为能力被调用,不应控制 Agent | ### 1.7 当前模块迁移建议 #### 1.7.1 `emoon-mcp-api` 用户已确认后续按以下方向拆分: ```text emoon-mcp-api ├── emoon-ai-agent-api ├── emoon-ai-card-api └── emoon-ai-mcp-api ``` 建议迁移归属: | 当前内容 | 目标模块 | 说明 | | --- | --- | --- | | `AgentEngine` / `AgentRequest` / `AgentResponse` | `emoon-ai-agent-api` | 属于引擎契约 | | `AgentEngineFactory` | `emoon-ai-agent` | 属于实现装配,不应在 API | | `MockAgentEngine` | `emoon-ai-agent` | 属于开发/测试实现 | | `AiConversation` | `emoon-ai-agent-api` 或 `emoon-ai-agent` | 如果作为 DO 放实现;如果作为 VO/DTO 放 API | | `AiConversationMapper` | `emoon-ai-agent` | Mapper 必须离开 API | | `AiCardInstance` | `emoon-ai-card-api` 或 `emoon-ai-card` | DTO/VO 放 API,DO/Mapper 放实现 | | `AiCardInstanceMapper` | `emoon-ai-card` | Mapper 必须离开 API | | MCP Tool 契约 | `emoon-ai-mcp-api` | 只放工具调用契约 | | MCP Tool 实现 | `emoon-ai-mcp` | 工具注册、调用、审计实现 | | `HospitalActivity` | 需重新判断 | 如果是医院活动管理,不应归属 MCP,可考虑运营或系统模块 | #### 1.7.2 `emoon-openplatform` 迁移目标: ```text 当前:Controller -> Mapper / EngineFactory / Dify Client / 业务表 目标:Controller -> Facade API -> 领域实现模块 ``` 建议迁移顺序: | 阶段 | 动作 | | --- | --- | | 第一步 | 保留当前接口路径和请求响应,新增 `AgentChatFacade` | | 第二步 | 把会话创建、引擎选择、SSE 事件转换迁入 `emoon-ai-agent` | | 第三步 | `openplatform` 只负责鉴权、签名、限流、参数转换 | | 第四步 | 删除对 `AiConversationMapper`、`AiAgentEngineConfigMapper` 的直接依赖 | #### 1.7.3 `emoon-system-api` 当前 `system-api` 中已有 `AiAgentEngineConfig`,短期可以保留,但要明确边界。 建议: ```text 租户、用户、组织、权限、字典:继续留在 system。 AI 引擎配置、Dify 配置、模型供应商配置:中期迁入 emoon-ai-agent。 合同、套餐、能力值包:不得放入 system。 ``` #### 1.7.4 `emoon-knowledge-api` 知识库模块应该作为 AI Agent 的能力来源,而不是 Agent 的控制者。 建议: ```text knowledge-api 提供知识检索、模板、规则、提示词资产契约。 emoon-ai-agent 可以依赖 knowledge-api。 knowledge 不依赖 emoon-ai-agent 实现。 ``` ### 1.8 二期与三期模块落位 #### 1.8.1 二期功能落位 | 功能 | 应落模块 | 不应落模块 | | --- | --- | --- | | Dify Workflow 调用 | `emoon-ai-agent` | `openplatform`、`emoon-ai-card` | | DirectLLM 调用 | `emoon-ai-agent` | `openplatform` | | Mock 引擎 | `emoon-ai-agent` | `emoon-ai-agent-api` | | SSE 事件协议 | `emoon-ai-agent-api` | 具体 Controller 私有结构 | | SSE 输出实现 | `openplatform` 或 `emoon-ai-agent` 协同 | `emoon-ai-card` | | 卡片实例状态机 | `emoon-ai-card` | `openplatform` | | 卡片动作幂等 | `emoon-ai-card` | 前端、Controller | | MCP 工具注册 | `emoon-ai-mcp` | `emoon-ai-agent` | | MCP 工具权限 | `emoon-ai-mcp` | Dify、前端 | | HIS/EMR 工具适配 | `emoon-ai-mcp` 或后续 hospital adapter | `emoon-ai-card` | | 用量事件生成 | `emoon-ai-agent` / `emoon-ai-card` / `emoon-ai-mcp` | `emoon-ai-billing` | #### 1.8.2 三期功能落位 | 功能 | 应落模块 | 不应落模块 | | --- | --- | --- | | 合同管理 | `emoon-ai-contract` | `system`、`openplatform` | | 能力值包 | `emoon-ai-contract` + `emoon-ai-billing` | `emoon-ai-agent` | | 预扣款 | `emoon-ai-billing` | `openplatform`、`emoon-ai-agent` | | Token/次数/分钟计量 | `emoon-ai-meter` | `emoon-ai-billing` | | 账单生成 | `emoon-ai-billing` | `emoon-ai-operation` | | 账务冲正 | `emoon-ai-billing` | `emoon-ai-meter` | | 医院/科室/医生统计 | `emoon-ai-operation` | 核心链路 Controller | | 合理不限量策略 | `emoon-ai-contract` + `emoon-ai-meter` | `emoon-ai-agent` | | 区域能力池划拨 | `emoon-ai-contract` + `emoon-ai-billing` | `system` | | 客户运营报告 | `emoon-ai-operation` | `emoon-ai-agent` | ### 1.9 事务与一致性边界 复杂 Java 工程最容易写乱的地方不是类名,而是事务边界。以下规则必须统一。 #### 1.9.1 同步事务边界 单个模块内部可以使用本地事务: ```text emoon-ai-card 内部:卡片动作提交 + 卡片状态更新 emoon-ai-meter 内部:计量事件去重 + 状态流转 emoon-ai-billing 内部:账务流水 + 余额更新 ``` 跨模块不要开启大事务。 错误示例: ```text openplatform 一个事务里同时: 写会话 调 Dify 写卡片 写计量 扣能力值 ``` 正确方式: ```text 核心状态先落本模块事务。 跨模块动作通过领域事件或 Facade 调用。 需要最终一致性的地方用 Outbox。 ``` #### 1.9.2 异步一致性边界 推荐事件链路: ```text emoon-ai-agent 调用完成 -> 写 Agent 调用结果 -> 发送 UsageGeneratedEvent -> emoon-ai-meter 计量 -> 发送 MeterEventConfirmed -> emoon-ai-billing 入账 -> emoon-ai-operation 汇总 ``` 计量和账务不得只依赖内存回调,必须可重放。 ### 1.10 包名规范 建议后续新模块统一使用以下包名。 ```text com.emoon.ai.agent com.emoon.ai.card com.emoon.ai.mcp com.emoon.ai.meter com.emoon.ai.billing com.emoon.ai.contract com.emoon.ai.operation ``` API 模块: ```text com.emoon.ai.agent.api com.emoon.ai.agent.api.dto com.emoon.ai.agent.api.enums com.emoon.ai.agent.api.event com.emoon.ai.agent.api.facade ``` 实现模块: ```text com.emoon.ai.agent.application com.emoon.ai.agent.domain com.emoon.ai.agent.infrastructure com.emoon.ai.agent.adapter ``` 说明: | 包 | 职责 | | --- | --- | | `application` | 应用服务、用例编排、事务入口 | | `domain` | 领域对象、领域规则、状态机 | | `infrastructure` | Mapper、Repository、第三方 SDK、消息、Outbox | | `adapter` | 入站/出站适配,例如 Dify、DirectLLM、HIS 工具适配 | ### 1.11 Maven 依赖检查建议 后续建议引入自动化约束,避免规则只停留在文档。 #### 1.11.1 Maven Enforcer 建议用 `maven-enforcer-plugin` 检查: ```text API 模块禁止依赖 spring-boot-starter-web API 模块禁止依赖 mybatis-plus API 模块禁止依赖 okhttp / langchain4j / pdfbox / tika 入口模块禁止依赖领域实现模块 ``` #### 1.11.2 源码扫描式架构测试 当前已新增轻量架构测试,不引入额外 Maven 依赖,只基于源码扫描做增量检查: ```text API 模块不得新增 Mapper / Impl / Controller。 API 模块不得新增 Web / MyBatis / AI SDK / PDF/OCR 等重依赖。 openplatform 不得新增直接 Mapper 依赖。 Controller 不得新增直接 Mapper 依赖。 emoon-ai-card 不得绕过 emoon-ai-agent 直接依赖 emoon-ai-mcp 实现层。 emoon-ai-billing 不得反向依赖 emoon-ai-agent 实现层。 emoon-ai-operation 不得依赖核心链路 infrastructure 做复杂联查。 ``` 历史违规已冻结在 `docs/architecture/legacy/历史架构违规基线.txt`。测试只拦截新增违规,不移动当前业务代码,因此不会影响项目启动。后续完成某个历史文件迁移后,应从 baseline 中删除对应路径,让约束逐步收紧。 ### 1.12 推荐迁移节奏 不要一次性大改 Maven 工程。建议按以下顺序推进: | 阶段 | 目标 | 说明 | | --- | --- | --- | | P0 | 冻结依赖规则 | 新代码先按本章规则落位 | | P1 | 拆分 `emoon-mcp-api` | 拆为 `emoon-ai-agent-api`、`emoon-ai-card-api`、`emoon-ai-mcp-api` | | P2 | 下沉实现类 | Mapper、EngineImpl、Factory 从 API 模块迁入实现模块 | | P3 | 收口 `openplatform` | 改为只依赖 Facade API,不直接操作 Mapper | | P4 | 新增计量模块 | 建立 `emoon-ai-meter-api` / `emoon-ai-meter` | | P5 | 新增合同账务模块 | 建立 `emoon-ai-contract` / `emoon-ai-billing` | | P6 | 新增运营统计模块 | 建立 `emoon-ai-operation`,承接报表和客户运营看板 | | P7 | 引入自动化检查 | 轻量源码扫描式架构测试;后续可再升级 Maven Enforcer / ArchUnit | | P8 | 评估独立部署 | 只对高吞吐或强隔离模块考虑拆服务 | ### 1.13 未来微服务拆分判断标准 满足以下条件时,才考虑把 Maven 模块升级为独立服务: | 判断项 | 拆分信号 | | --- | --- | | 吞吐压力 | 模块资源消耗明显独立,例如语音流、OCR、IoMT 事件 | | 隔离要求 | 医院内网工具调用、敏感数据处理需要独立网络边界 | | 发布频率 | 某模块发布频率远高于主系统,且影响范围可控 | | 团队责任 | 有明确负责人和运维能力 | | 数据边界 | 数据表和事务边界已经稳定 | | 接口稳定 | API 契约至少经历一轮线上验证 | 优先可能独立部署的模块: ```text MCP Tool Server AudioStreamGateway IoMTEventIngestion File/OCR/ASR Worker Operation Report Worker ``` 不建议早期独立部署的模块: ```text emoon-ai-agent emoon-ai-card emoon-ai-meter emoon-ai-billing emoon-ai-contract ``` 原因是这些模块在早期需要频繁联动调整,过早拆服务会降低开发效率。 ### 1.14 工程师落地检查清单 新增一个类或模块前,先回答以下问题: | 问题 | 判断 | | --- | --- | | 这个类是契约还是实现? | 契约放 `*-api`,实现放 `*-modules` | | 是否需要操作数据库? | 需要则不能放 API 模块 | | 是否调用第三方 SDK? | 需要则不能放 API 模块 | | 是否属于入口协议? | Controller、SSE 输出放入口层或 adapter | | 是否属于 Agent 编排? | 放 `emoon-ai-agent` | | 是否属于卡片状态? | 放 `emoon-ai-card` | | 是否属于工具治理? | 放 `emoon-ai-mcp` | | 是否属于用量归集? | 放 `emoon-ai-meter` | | 是否属于扣款结算? | 放 `emoon-ai-billing` | | 是否属于合同套餐? | 放 `emoon-ai-contract` | | 是否属于统计报表? | 放 `emoon-ai-operation` | | 是否要跨模块改表? | 不允许,改为 Facade 或事件 | | 是否为了方便直接注入 Mapper? | 不允许,说明模块边界没设计好 | ### 1.15 本章强制规则摘要 后续开发默认遵守以下规则: ```text R1. API 模块只放契约,不放实现。 R2. Mapper 只能存在于实现模块。 R3. Controller 只能调用 Facade / Application Service,不直接调用 Mapper。 R4. openplatform 只做入口适配,不承载 AI 主业务状态。 R5. emoon-ai-agent 负责 AI 编排,但不负责扣款和报表。 R6. emoon-ai-card 负责卡片状态,但不直接调用 MCP 工具。 R7. emoon-ai-mcp 负责工具治理,但不负责 Agent 流程编排。 R8. emoon-ai-meter 负责计量,不负责余额扣减。 R9. emoon-ai-billing 负责账务,不反向依赖 Agent 实现。 R10. emoon-ai-contract 负责合同和套餐,不放入 system。 R11. emoon-ai-operation 负责统计宽表和报表,不拖慢主链路。 R12. 跨模块一致性优先使用 Outbox / 事件 / 幂等键,不做跨模块大事务。 R13. 新模块必须先明确依赖方向,再写代码。 R14. 任何违反规则的例外必须补 ADR。 ``` 这份约束的目的不是增加开发成本,而是防止 AI 中台在二期、三期叠加后变成无法维护的巨石。只要依赖方向稳定,当前单体工程也可以支撑生产级医疗项目;未来要拆服务时,也有清晰的模块边界可以承接。 ### 1.16 配套执行资产 为了让工程约束在工程师开发 + 技术总监 Review 的模式下真正执行,当前已配套以下资产: | 资产 | 位置 | 使用时机 | 作用 | | --- | --- | --- | --- | | PR 自检模板 | `.github/pull_request_template.md` | 每次提交 PR | 让开发人员在 Review 前先自查模块边界、幂等、计量、账务、安全、测试 | | 任务拆分模板 | `docs/templates/AI中台任务拆分模板.md` | 每个开发任务开始前 | 把需求拆成可执行任务,提前限定允许修改模块、禁止依赖、核心流程和验收标准 | | 模块骨架示例 | `docs/templates/AI中台模块骨架示例.md` | 新增 AI 模块或新功能包时 | 统一 API / application / domain / infrastructure / adapter 的放置规则 | | 架构边界测试 | `emoon-admin/src/test/java/com/emoon/architecture/AiPlatformArchitectureTest.java` | 本地测试和 CI | 冻结历史违规,自动拦截新增 Maven / 架构边界问题 | | 历史违规基线 | `docs/architecture/legacy/历史架构违规基线.txt` | 架构测试运行时 | 记录当前遗留问题,避免一次性搬迁影响项目启动 | 执行要求: ```text 新任务必须先填写任务拆分模板。 新 PR 必须完整填写 PR 自检模板。 新增 AI 模块必须参考模块骨架示例。 新增目标 AI 包后必须运行架构边界测试。 ``` 架构边界测试当前采用“历史基线 + 增量失败”的策略。原因是历史模块中已经存在 `openplatform` 直连 Mapper、`*-api` 混入实现类等问题,如果现在直接全量强约束,会导致测试立即失败,且大规模移动代码会影响启动稳定性。 后续迁移完成后,应逐步收紧基线和扩大检查范围: | 阶段 | 约束范围 | | --- | --- | | 第一阶段 | `com.emoon.ai.*` 新模块 | | 第二阶段 | `com.emoon.mcp.*` 迁移后的 Agent / Card / MCP 模块 | | 第三阶段 | `com.emoon.openplatform.*` 禁止直连 Mapper 和实现层 | | 第四阶段 | 全工程禁止 API 模块依赖实现层和重依赖 | --- ## 2. 接口归属与调用方向约束 > 本章基于 v1.2 对外接口文档(`docs/api/openapi/医梦AI中台开放平台接口-v1.2.openapi.yaml`), > 明确每个接口的 Controller 归属模块和核心 Service 归属模块, > 并定义 Controller → Service 的合法调用方向和禁止调用模式。 ### 2.1 Controller 层强制规则 所有对外 API 的 Controller 统一放在 `emoon-openplatform` 模块的 `controller/v1/` 包下。 Controller 只做四件事: | 允许 | 不允许 | |------|--------| | 解析 HTTP 请求参数和 Header | 直接调用 Mapper | | 调用鉴权拦截器/工具类 | 直接调用 Dify API 或第三方 LLM SDK | | 调用领域 Service(通过 API 接口) | 编写业务逻辑(状态机、计费、HIS 调用) | | 封装统一响应(`R` 或 SSE) | 直接操作数据库表 | 为什么 Controller 不能直接调 Mapper: ```text Controller 只负责 HTTP 协议适配。如果直接调 Mapper,会绕过 Application Service、 状态机、幂等校验、审计、计量和 Outbox。后续排障时无法通过 traceId 还原完整调用链。 ``` ### 2.2 接口 → 模块归属表 下表来自 v1.2 正式联调基准版,每个接口对应**一个 Controller 模块**和**一个核心 Service 实现模块**。 #### P0 接口(联调前必须稳定,21 个) | 方法 | 路径 | Controller 所在模块 | 核心 Service 实现模块 | 当前工程状态 | |------|------|--------------------|---------------------|-------------| | POST | `/auth/token` | emoon-openplatform | emoon-openplatform | 🔴 需新建(Bearer Token 签发) | | GET | `/health` | emoon-openplatform | emoon-openplatform | 🟡 部分存在 | | GET | `/capabilities` | emoon-openplatform | emoon-openplatform + emoon-system | 🔴 需新建 | | POST | `/agent/chat` | emoon-openplatform | emoon-ai-agent | 🟡 需升级鉴权 | | POST | `/agent/chat/stream` | emoon-openplatform | emoon-ai-agent | 🟡 同上 | | POST | `/conversations` | emoon-openplatform | emoon-openplatform | 🟡 路径需 RESTful | | GET | `/conversations` | emoon-openplatform | emoon-openplatform | 🟡 P1,来自旧 conversation/list | | GET | `/conversations/{id}` | emoon-openplatform | emoon-openplatform | 🟡 路径需对齐 | | GET | `/conversations/{id}/messages` | emoon-openplatform | emoon-openplatform | 🔴 需新建 | | POST | `/files/upload` | emoon-openplatform | emoon-ai-file | 🔴 需新建模块 | | GET | `/files/{id}` | emoon-openplatform | emoon-ai-file | 🔴 同上 | | DELETE | `/files/{id}` | emoon-openplatform | emoon-ai-file | 🔴 同上(P1) | | POST | `/devices/register` | emoon-openplatform | emoon-ai-device | 🔴 需新建模块 | | POST | `/devices/{id}/heartbeat` | emoon-openplatform | emoon-ai-device | 🔴 同上 | | GET | `/devices/{id}/scene` | emoon-openplatform | emoon-ai-device | 🔴 同上 | | POST | `/devices/{id}/events` | emoon-openplatform | emoon-ai-device | 🔴 同上 | | GET | `/devices/{id}/commands/poll` | emoon-openplatform | emoon-ai-device | 🔴 同上 | | POST | `/devices/{id}/commands/{cmd}/ack` | emoon-openplatform | emoon-ai-device | 🔴 同上 | | GET | `/cards/{id}` | emoon-openplatform | emoon-ai-card | 🔴 需新建 | | POST | `/cards/{id}/actions/{name}` | emoon-openplatform | emoon-ai-agent + emoon-ai-card | 🟡 路径需对齐;Agent 编排动作,Card 负责状态 | | POST | `/tools/{name}/invoke` | emoon-openplatform | emoon-ai-mcp | 🟡 雏形存在 | | GET | `/usage/summary` | emoon-openplatform | emoon-ai-meter | 🔴 meter 模块空壳 | | GET | `/meter/events` | emoon-openplatform | emoon-ai-meter | 🔴 同上 | #### P1 接口(试点上线增强,14 个) | 方法 | 路径 | Controller 所在模块 | 核心 Service 实现模块 | |------|------|--------------------|---------------------| | GET | `/agents` | emoon-openplatform | emoon-system(查 ai_agent_app) | | GET | `/agents/{agentId}` | emoon-openplatform | emoon-system | | GET | `/devices` | emoon-openplatform | emoon-ai-device | | GET | `/devices/{id}` | emoon-openplatform | emoon-ai-device | | PATCH | `/devices/{id}/scene` | emoon-openplatform | emoon-ai-device | | GET | `/devices/{id}/events` | emoon-openplatform | emoon-ai-device | | POST | `/webhooks` | emoon-openplatform | emoon-openplatform | | GET | `/webhooks` | emoon-openplatform | emoon-openplatform | | DELETE | `/webhooks/{id}` | emoon-openplatform | emoon-openplatform | | POST | `/events/ingest` | emoon-openplatform | emoon-openplatform + emoon-ai-device | | GET | `/tools/catalog` | emoon-openplatform | emoon-ai-mcp | | GET | `/licenses/devices` | emoon-openplatform | emoon-ai-contract + emoon-ai-device | #### P2 接口(远期规划,9 个) | 方法 | 路径 | Controller 所在模块 | 核心 Service 实现模块 | |------|------|--------------------|---------------------| | POST | `/partners` | emoon-openplatform | emoon-openplatform | | GET | `/partners/{id}` | emoon-openplatform | emoon-openplatform | | GET | `/contracts` | emoon-openplatform | emoon-ai-contract | | GET | `/credit/accounts` | emoon-openplatform | emoon-ai-billing | | POST | `/credit/accounts/{id}/freeze` | emoon-ai-billing(内部) | emoon-ai-billing | | POST | `/credit/accounts/{id}/settle` | emoon-ai-billing(内部) | emoon-ai-billing | | GET | `/billing/invoices` | emoon-openplatform | emoon-ai-billing | | GET | `/operation/reports` | emoon-openplatform | emoon-ai-operation | | GET | `/audit/logs` | emoon-openplatform | emoon-system(扩展审计表) | ### 2.3 合法调用方向 ```mermaid flowchart LR subgraph 入口层 OC["emoon-openplatform
Controller v1"] end subgraph 领域实现 AG["emoon-ai-agent"] CD["emoon-ai-card"] MC["emoon-ai-mcp"] DV["emoon-ai-device"] FL["emoon-ai-file"] MT["emoon-ai-meter"] BL["emoon-ai-billing"] CT["emoon-ai-contract"] OP["emoon-ai-operation"] end subgraph 基础域 SY["emoon-system"] KN["emoon-knowledge"] end subgraph 外部 DF["Dify / LLM"] HS["HIS / EMR / LIS / PACS"] end OC --> AG & CD & MC & DV & FL & MT & SY AG --> CD & MC & MT & CT & KN & SY & DF MC --> HS DV --> SY FL --> SY MT --> CT BL --> MT & CT CT --> SY OP --> MT & BL & CT ``` 图中箭头 = 允许调用方向。没有画出的方向默认不允许。关键约束: | 调用方向 | 是否允许 | 原因 | |---------|---------|------| | Controller → Service(通过 API 接口) | ✅ 允许 | 标准调用路径 | | emoon-ai-agent → emoon-ai-mcp | ✅ 允许 | Dify Workflow 工具调用必须通过 MCP | | emoon-ai-agent → emoon-ai-card | ✅ 允许 | Agent 编排链路需要创建卡片、推进卡片动作和写审计 | | emoon-ai-card → emoon-ai-mcp | ❌ 禁止 | 卡片动作触发的 HIS 写操作必须回到 Agent 编排链路,再由 Agent 调 MCP | | emoon-ai-mcp → emoon-ai-agent | ❌ 禁止 | 工具不编排 Agent 流程 | | emoon-ai-card → emoon-ai-agent | ❌ 禁止 | 卡片不发起 Agent 调用 | | emoon-ai-device → emoon-ai-agent | ❌ 禁止 | 设备不编排 AI 流程 | | emoon-ai-billing → emoon-ai-agent | ❌ 禁止 | 账务不反向依赖调用链 | | Controller → Mapper | ❌ 禁止 | 绕过 Service 层和状态机 | | 任意模块 → Dify HTTP 直连 | ❌ 禁止 | 必须经过 AgentEngine | | 前端/设备 → `/tools/{name}/invoke` | ❌ 禁止 | 必须经过卡片动作或 Agent | ### 2.4 禁止的调用模式(红线清单) 以下 6 条违反即拒绝 PR: **F1. Controller 直接调 Mapper** ```java // ❌ 禁止 @RestController public class FooController { @Autowired private AiConversationMapper mapper; // Controller 不应注入 Mapper } ``` 正确做法:Controller → Service 接口(API 模块定义)→ Service 实现(实现模块内调 Mapper)。 **F2. 任意模块直接 import DifyApiClient** ```java // ❌ 禁止:在 emoon-ai-card、emoon-ai-device 等非 Agent 模块中 import com.emoon.openplatform.dify.DifyApiClient; ``` Dify 调用只能出现在 `emoon-ai-agent` 的 `adapter` 包中,通过 `AgentEngine` 接口对外暴露。 **F3. 卡片绕过 Agent 直接调 MCP 工具** ```java // ❌ 禁止:在 emoon-ai-card 中 import com.emoon.ai.mcp.application.HisLockScheduleService; ``` 卡片需要写 HIS 时,必须由 `CardActionController` 调 Agent 编排服务,Agent 调 Card 做状态校验和快照,再由 Agent 调 MCP 完成 HIS 写操作。 不能出现 Card → MCP 的短路调用。 **F4. 前端/设备端直接调 `/tools/{toolName}/invoke`** ```text ❌ 禁止:机器人、自助机、导诊屏厂商的 HTTP Client 直接 POST /api/v1/tools/his.createRegistration/invoke ``` 普通设备厂商只能通过 `/agent/chat/stream`、`/cards/{id}/actions/{name}`、`/files/*` 和 `/devices/*` 接入。 Tool Invoke 仅开放给医梦 MCP Tool Server、HIS/EMR/LIS/PACS 等受控系统适配方。 **F5. Service 层写 HMAC 签名逻辑** ```java // ❌ 禁止:在 Service 实现类中 String sign = SignUtil.hmacSha256(payload, project.getSecretKey()); ``` 鉴权逻辑只能出现在 `emoon-openplatform` 的拦截器或 Controller 层。 Service 层接收的是已验证的 `projectId/tenantId` 上下文。 **F6. 把 Dify API Key 暴露到日志或前端** ```text ❌ 禁止:log.info("Dify key: {}", difyApiKey) ❌ 禁止:响应体中包含 apiKey 字段 ❌ 禁止:前端 JS 中硬编码 Dify Key ``` Dify API Key 只能存在于 `ai_agent_engine_config.config_json` 的加密字段中, 运行时由 Agent 模块解密后使用,不出现在任何日志、响应或前端代码中。 ### 2.5 PR 评审检查清单 每个涉及后端 API 或模块的 PR 必须通过以下检查。此清单同时维护在 `.github/pull_request_template.md` 中。 #### Controller 检查 - [ ] 新 Controller 放在 `emoon-openplatform/controller/v1/` 包下? - [ ] 路径和方法名与 v1.2 YAML 一致? - [ ] 已接入 HMAC 或 Bearer Token 鉴权(非 legacy MD5)? - [ ] 写操作已强制 `X-Emoon-Idempotency-Key`? - [ ] Controller 未直接注入 Mapper? - [ ] Controller 未直接 import DifyApiClient? #### Service 检查 - [ ] Service 接口在 `*-api` 模块、实现在 `*-modules` 模块? - [ ] 新 Service 放在了正确的模块?(参考 §2.2 归属表) - [ ] 没有违反 §2.4 中的任何一条禁止调用模式? - [ ] 涉及 HIS 写操作的,已通过 `emoon-ai-mcp` 工具封装? #### 模块边界检查 - [ ] 没有在 `emoon-openplatform` 里写业务逻辑? - [ ] 新模块经过技术负责人确认?(禁止私自创建顶级模块) - [ ] 没有把 DifyApiClient 暴露给不应见的模块? #### 数据与合规检查 - [ ] 新表有 `project_id` 字段? - [ ] 新表有必要的索引(至少覆盖 project、time、status 三个维度)? - [ ] 日志中不包含身份证、手机号、完整病历、API Key? - [ ] 敏感字段有加密或脱敏? #### 接口文档对齐 - [ ] 新接口已在 v1.2 接口设计文档中定义? - [ ] 请求/响应字段与 YAML schema 一致? - [ ] 返回的 HTTP 状态码与接口文档一致? ### 2.6 工程师落地新接口的速查流程 接手一个新接口时,按以下顺序确认归属: ```text 1. 查 §2.2 接口归属表 → 确定 Controller 放 emoon-openplatform,Service 放哪个模块。 2. 如果接口不在表中 → 先在 PR 描述中讨论归属,不得私自决定。 3. 创建 Controller → 放在 emoon-openplatform/controller/v1 包下。 4. Controller 中只写参数解析 + 鉴权调用 + Service 调用 + 响应封装。 5. Service 接口放在对应 `*-api` 模块,实现放在对应 `*-modules` 模块。 6. 完成后对照 §2.5 检查清单自检。 7. 运行架构边界测试(`mvn -pl emoon-admin -Dtest=AiPlatformArchitectureTest test`)。 ``` ### 2.7 本章强制规则摘要 ```text C1. Controller 统一放在 emoon-openplatform/controller/v1/。 C2. Controller 只做参数解析、鉴权、Service 调用、响应封装。 C3. Controller 禁止注入 Mapper、禁止直接调 Dify、禁止写业务逻辑。 C4. 每个接口的 Service 归属以 §2.2 表为准。 C5. Card → MCP 必须经过 Agent 编排,禁止短路调用。 C6. 前端/设备端禁止直接调 /tools/{name}/invoke。 C7. DifyApiClient 只能被 emoon-ai-agent 的 adapter 包 import。 C8. 鉴权逻辑只能出现在 emoon-openplatform 的拦截器或 Controller。 C9. Dify API Key 不出现在日志、响应、前端代码中。 C10. 新模块必须经过技术负责人确认,禁止私建顶级模块。 ```