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:
| 项目 | 内容 |
|---|---|
| 文档定位 | 工程结构、模块边界、依赖规则、接口归属、调用方向约束 |
| 当前章节 | Maven 工程依赖 + Controller/Service 调用方向 + 接口→模块映射 |
| 适用范围 | 现有后端工程、二期 AI Agent / Card / MCP / Device / File、三期计量 / 账务 / 运营 |
| 约束目标 | 高内聚、低耦合、可演进、可测试、可独立部署 |
| 版本 | v2.1 |
| 更新时间 | 2026-05-31 |
| 变更说明 | 补充统一入口客户端落位:后端能力仍归 AI 中台模块,前端/移动端另起 emoon-terminal-client;明确 AgentRouter、TaskStateService、TongueDiagnosisAdapter、HisMockAdapter 的模块归属;修正 Card 动作不能直连 MCP 的调用方向 |
当前阶段不建议优先微服务化。医梦 AI 中台仍处于能力快速建设期,团队规模约 3 名后端开发,直接拆微服务会过早引入服务治理、部署、链路追踪、分布式事务、接口版本管理和运维成本。
更合理的方向是:
先做模块化单体,再保留未来微服务拆分边界。
也就是说,短期仍然保持一个 Maven 多模块工程和少量启动应用,但必须先把 API 契约、领域实现、入口适配、基础设施、第三方系统适配分开。后续如果某个模块需要独立部署,只需要把已经稳定的 Maven 模块搬成服务,而不是重新拆业务逻辑。
本章的核心约束是:
入口层只负责编排入口,不沉淀业务状态。
API 模块只定义契约,不包含实现和重依赖。
领域实现模块只处理自己的业务闭环,不跨模块操作数据表。
计量、账务、合同、运营统计独立成模块,不混入 Agent 调用链。
MCP 只负责工具协议和治理,不承载所有院内系统适配细节。
本规约的工程取舍基于公开可验证案例做裁剪:东软类智慧医院平台证明“统一入口集成子系统”方向可行,但医梦只做 AI 入口层;华为/医惠类智慧病房证明病房物联网接入是重资产平台能力,因此住院设备只做事件映射;Dify 官方已有 Workflow 版本能力,因此后端只做版本映射和输出治理;科大讯飞类医疗 AI 产品强调嵌入医生流程,因此高风险动作必须通过卡片、工具审计和人工确认。以上案例用于约束边界,不作为照搬大厂平台规模的理由。
现有工程大体可以分为以下几层:
| 层级 | 当前模块 | 当前职责 | 状态 |
|---|---|---|---|
| 启动入口 | 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 适配容易混在一起 | 工具治理、院内系统集成和业务编排互相污染 |
推荐采用以下 Maven 分层。该结构不要求一次性全部改完,但新功能必须按这个方向落位。
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 # 外围扩展、任务、迁移、专病能力
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
图中的箭头表示允许依赖方向。没有画出的方向默认不允许。
| 模块 | 允许职责 | 禁止职责 |
|---|---|---|
emoon-admin |
管理后台入口、菜单权限、运营配置页面、调用领域 API | 直接操作 AI 业务表、直接调用 Dify/LLM、直接扣减能力值 |
emoon-openplatform |
对外 API、签名鉴权、租户识别、参数校验、SSE 输出、入口限流 | 保存 AI 会话核心状态、直接查写 Mapper、直接选择引擎实现、直接计费 |
emoon-extend |
文件处理、迁移、外围任务、专病扩展、非主链路能力 | 承载 Agent 主链路、承载账务核心状态、绕过领域 API 改业务数据 |
入口模块的原则:
入口模块可以知道“谁来调用、是否有权限、把请求交给谁”,
但不应该知道“AI 会话怎么编排、卡片状态怎么流转、能力值怎么扣”。
*-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 具体实现 | 入口协议属于应用层或实现层 |
| 模块 | 规划职责 |
|---|---|
emoon-ai-agent-api |
定义 AI 调用、会话、流式事件、引擎抽象、编排命令、终端任务状态只读 DTO |
emoon-ai-agent |
实现 Agent 主链路、AgentRouter、ConversationService、TaskStateService、DifyAgentEngine、DifyOutputNormalizer、DirectLLM 引擎、Mock 引擎、SSE 事件转换、降级、幂等、Outbox |
emoon-ai-agent 是 AI 中台二期的主链路模块,但不能变成所有 AI 功能的大杂烩。
允许依赖:
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-*
禁止依赖:
emoon-ai-billing 实现模块
emoon-ai-operation 实现模块
openplatform
admin
任意 Controller 层
关键约束:
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。
| 模块 | 规划职责 |
|---|---|
emoon-ai-card-api |
定义卡片模板、卡片实例、卡片动作、渲染数据、状态事件 |
emoon-ai-card |
实现卡片状态机、动作幂等、卡片版本、卡片事件、渲染数据聚合 |
关键约束:
emoon-ai-card 只负责“确定性交互状态”,不负责模型推理。
emoon-ai-card 不能直接调用 MCP 工具。
emoon-ai-card 需要调用工具时,必须通过 emoon-ai-agent 编排。
原因:
如果卡片可以直接调用 MCP,系统会出现两条工具调用链:
Agent -> MCP
Card -> MCP
这样会导致权限、审计、计量、失败补偿出现双标准。生产环境中必须保持一条主链路。
| 模块 | 规划职责 |
|---|---|
emoon-ai-mcp-api |
定义工具注册、工具调用、工具权限、工具审计、工具结果契约 |
emoon-ai-mcp |
实现工具注册表、工具路由、权限校验、限流熔断、工具调用审计、HisMockAdapter、RealHisAdapter、TongueDiagnosisAdapter |
emoon-ai-mcp 不等于所有院内系统适配。它负责工具协议治理,具体 HIS / EMR / LIS / PACS 的适配可以继续放在当前合适模块中,或后续独立为 hospital-integration / emoon-hospital-adapter。
关键约束:
MCP 管工具治理,不管业务编排。
HIS 适配管系统协议,不管 Agent 对话。
Agent 管流程编排,不管 HIS 细节。
TongueDiagnosisAdapter 在 MVP 阶段作为 MCP 受控工具适配器处理,后续形成独立专精产品线时再拆模块。
HisMockAdapter 必须显式命名和配置,不能把 Mock 结果包装成真实医院交易。
| 模块 | 规划职责 |
|---|---|
emoon-ai-meter-api |
定义计量事件、计量状态、幂等键、用量查询接口 |
emoon-ai-meter |
实现 Token/次数/分钟/项目/年费等用量归集、状态机、重放、去重 |
计量模块是三期商业闭环的第一层,不是账务模块。
关键约束:
业务模块只提交 usage event。
emoon-ai-meter 负责判断这个 event 是否可信、是否重复、是否可计量。
emoon-ai-billing 再根据计量结果做扣款或结算。
禁止:
emoon-ai-agent 直接写账务流水。
emoon-ai-card 直接扣能力值。
openplatform 直接计算费用。
| 模块 | 规划职责 |
|---|---|
emoon-ai-billing-api |
定义账户余额、冻结、扣减、退款、冲正、账单查询契约 |
emoon-ai-billing |
实现能力值包预扣款、账务流水、账单、对账、调账、价格快照 |
账务模块必须独立,原因是账务有强一致性要求,而 Agent 调用链有高并发和失败降级要求。两者耦合会导致调用失败、重复扣款、漏扣款和对账困难。
关键约束:
扣款必须基于 pricing snapshot。
账务流水必须可追溯、可冲正、不可物理删除。
同一 meter event 只能入账一次。
| 模块 | 规划职责 |
|---|---|
emoon-ai-contract-api |
定义客户合同、能力值包、价格策略、授权范围、有效期 |
emoon-ai-contract |
实现合同状态机、套餐绑定、能力包启停、区域能力池规则 |
合同模块不应该放在 system 里。system 可以提供租户、用户、组织、权限,但合同、套餐、能力值包属于 AI 商业闭环。
| 模块 | 规划职责 |
|---|---|
emoon-ai-operation-api |
定义统计查询、运营报表、客户看板、异常分析契约 |
emoon-ai-operation |
实现统计宽表、报表聚合、医院/科室/医生/智能体维度分析 |
运营统计不能直接拖核心业务表做复杂联表,否则后续会拖慢主链路。
推荐方式:
emoon-ai-meter / emoon-ai-billing / emoon-ai-agent 产生事件
Outbox 或定时任务汇总到 operation 宽表
emoon-ai-operation 面向后台和客户报表查询
| 模块 | 规划职责 |
|---|---|
emoon-ai-device-api |
定义设备注册、心跳、场景配置、事件上报、命令下发、设备查询契约 |
emoon-ai-device |
实现 Device Registry、设备身份管理、准入等级校验(A/B/C/D)、心跳监控、Scene Orchestrator、命令轮询与回执、设备事件接入 |
关键约束:
门诊设备(导诊屏、自助机、机器人、诊室屏等)由医梦 Device Registry 管理,包含注册、心跳、场景和基础状态。
住院侧设备类型(nurse_pda / nursing_whiteboard / bedside_screen / ward_robot)
默认 managementMode = mapped_external,由迪耐克/鸿蒙病房体系管理硬件生命周期,
医梦只记录映射关系和 AI 事件互通,不承担住院硬件注册、心跳升级和故障主责。
设备事件只上报业务摘要,不保存患者连续轨迹。
设备管理模式枚举:
| managementMode | 说明 | 典型设备 |
|---|---|---|
emoon_managed |
医梦统一入口客户端和 Device Registry 管理 | 导诊屏、自助机、门诊机器人、诊室屏 |
partner_managed |
合作厂商管理硬件,医梦通过标准接口接入 | 第三方机器人、四诊仪 |
mapped_external |
外部体系管理,医梦只做映射和事件互通 | 迪耐克床头屏、护理白板、护士 PDA |
允许依赖:
emoon-ai-device-api
emoon-system-api(查询医院/科室绑定)
必要的 emoon-common-*
禁止依赖:
emoon-ai-agent(设备不编排 AI 流程)
emoon-ai-card(设备不管理卡片状态)
emoon-ai-billing(设备不计费)
emoon-openplatform(设备不反向依赖入口层)
| 模块 | 规划职责 |
|---|---|
emoon-ai-file-api |
定义文件上传、元数据查询、删除、安全扫描状态契约 |
emoon-ai-file |
实现文件接收、对象存储适配、安全扫描对接、retentionPolicy 管理、幂等去重 |
关键约束:
文件必须先上传获得 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。
允许依赖:
emoon-ai-file-api
emoon-system-api
emoon-common-oss
必要的 emoon-common-*
禁止依赖:
emoon-ai-agent
emoon-ai-card
emoon-ai-mcp
emoon-openplatform
统一入口客户端不是一个新的后端系统。所有后端能力必须落在 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 工程:
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 调用正式后端能力。
API 模块允许依赖:
| 依赖 | 用途 |
|---|---|
emoon-common-core |
基础实体、通用返回、工具类型 |
jakarta.validation |
参数校验注解 |
jackson |
JSON 序列化注解 |
lombok |
简化 DTO |
mapstruct 注解 |
DTO 转换契约,需谨慎使用 |
API 模块禁止依赖:
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 层。
实现模块可以依赖:
自身 api
其他领域 api
必要的 emoon-common-*
必要的第三方 SDK
实现模块禁止:
依赖入口模块,例如 emoon-openplatform、emoon-admin
依赖其他模块的 impl 包
跨模块注入 Mapper
跨模块直接更新对方业务表
入口模块允许依赖:
领域 api
emoon-common-web
emoon-common-security
emoon-common-sse
emoon-common-ratelimiter
emoon-common-tenant
入口模块禁止:
直接依赖领域实现模块
直接依赖 Mapper
直接调用第三方 AI SDK
直接写核心业务状态表
以下依赖关系默认禁止:
| 禁止方向 | 原因 |
|---|---|
*-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 |
emoon-mcp-api用户已确认后续按以下方向拆分:
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,可考虑运营或系统模块 |
emoon-openplatform迁移目标:
当前:Controller -> Mapper / EngineFactory / Dify Client / 业务表
目标:Controller -> Facade API -> 领域实现模块
建议迁移顺序:
| 阶段 | 动作 |
|---|---|
| 第一步 | 保留当前接口路径和请求响应,新增 AgentChatFacade |
| 第二步 | 把会话创建、引擎选择、SSE 事件转换迁入 emoon-ai-agent |
| 第三步 | openplatform 只负责鉴权、签名、限流、参数转换 |
| 第四步 | 删除对 AiConversationMapper、AiAgentEngineConfigMapper 的直接依赖 |
emoon-system-api当前 system-api 中已有 AiAgentEngineConfig,短期可以保留,但要明确边界。
建议:
租户、用户、组织、权限、字典:继续留在 system。
AI 引擎配置、Dify 配置、模型供应商配置:中期迁入 emoon-ai-agent。
合同、套餐、能力值包:不得放入 system。
emoon-knowledge-api知识库模块应该作为 AI Agent 的能力来源,而不是 Agent 的控制者。
建议:
knowledge-api 提供知识检索、模板、规则、提示词资产契约。
emoon-ai-agent 可以依赖 knowledge-api。
knowledge 不依赖 emoon-ai-agent 实现。
| 功能 | 应落模块 | 不应落模块 |
|---|---|---|
| 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 |
| 功能 | 应落模块 | 不应落模块 |
|---|---|---|
| 合同管理 | 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 |
复杂 Java 工程最容易写乱的地方不是类名,而是事务边界。以下规则必须统一。
单个模块内部可以使用本地事务:
emoon-ai-card 内部:卡片动作提交 + 卡片状态更新
emoon-ai-meter 内部:计量事件去重 + 状态流转
emoon-ai-billing 内部:账务流水 + 余额更新
跨模块不要开启大事务。
错误示例:
openplatform 一个事务里同时:
写会话
调 Dify
写卡片
写计量
扣能力值
正确方式:
核心状态先落本模块事务。
跨模块动作通过领域事件或 Facade 调用。
需要最终一致性的地方用 Outbox。
推荐事件链路:
emoon-ai-agent 调用完成
-> 写 Agent 调用结果
-> 发送 UsageGeneratedEvent
-> emoon-ai-meter 计量
-> 发送 MeterEventConfirmed
-> emoon-ai-billing 入账
-> emoon-ai-operation 汇总
计量和账务不得只依赖内存回调,必须可重放。
建议后续新模块统一使用以下包名。
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 模块:
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
实现模块:
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 工具适配 |
后续建议引入自动化约束,避免规则只停留在文档。
建议用 maven-enforcer-plugin 检查:
API 模块禁止依赖 spring-boot-starter-web
API 模块禁止依赖 mybatis-plus
API 模块禁止依赖 okhttp / langchain4j / pdfbox / tika
入口模块禁止依赖领域实现模块
当前已新增轻量架构测试,不引入额外 Maven 依赖,只基于源码扫描做增量检查:
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 中删除对应路径,让约束逐步收紧。
不要一次性大改 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 | 评估独立部署 | 只对高吞吐或强隔离模块考虑拆服务 |
满足以下条件时,才考虑把 Maven 模块升级为独立服务:
| 判断项 | 拆分信号 |
|---|---|
| 吞吐压力 | 模块资源消耗明显独立,例如语音流、OCR、IoMT 事件 |
| 隔离要求 | 医院内网工具调用、敏感数据处理需要独立网络边界 |
| 发布频率 | 某模块发布频率远高于主系统,且影响范围可控 |
| 团队责任 | 有明确负责人和运维能力 |
| 数据边界 | 数据表和事务边界已经稳定 |
| 接口稳定 | API 契约至少经历一轮线上验证 |
优先可能独立部署的模块:
MCP Tool Server
AudioStreamGateway
IoMTEventIngestion
File/OCR/ASR Worker
Operation Report Worker
不建议早期独立部署的模块:
emoon-ai-agent
emoon-ai-card
emoon-ai-meter
emoon-ai-billing
emoon-ai-contract
原因是这些模块在早期需要频繁联动调整,过早拆服务会降低开发效率。
新增一个类或模块前,先回答以下问题:
| 问题 | 判断 |
|---|---|
| 这个类是契约还是实现? | 契约放 *-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? | 不允许,说明模块边界没设计好 |
后续开发默认遵守以下规则:
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 中台在二期、三期叠加后变成无法维护的巨石。只要依赖方向稳定,当前单体工程也可以支撑生产级医疗项目;未来要拆服务时,也有清晰的模块边界可以承接。
为了让工程约束在工程师开发 + 技术总监 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 |
架构测试运行时 | 记录当前遗留问题,避免一次性搬迁影响项目启动 |
执行要求:
新任务必须先填写任务拆分模板。
新 PR 必须完整填写 PR 自检模板。
新增 AI 模块必须参考模块骨架示例。
新增目标 AI 包后必须运行架构边界测试。
架构边界测试当前采用“历史基线 + 增量失败”的策略。原因是历史模块中已经存在 openplatform 直连 Mapper、*-api 混入实现类等问题,如果现在直接全量强约束,会导致测试立即失败,且大规模移动代码会影响启动稳定性。
后续迁移完成后,应逐步收紧基线和扩大检查范围:
| 阶段 | 约束范围 |
|---|---|
| 第一阶段 | com.emoon.ai.* 新模块 |
| 第二阶段 | com.emoon.mcp.* 迁移后的 Agent / Card / MCP 模块 |
| 第三阶段 | com.emoon.openplatform.* 禁止直连 Mapper 和实现层 |
| 第四阶段 | 全工程禁止 API 模块依赖实现层和重依赖 |
本章基于 v1.2 对外接口文档(
docs/api/openapi/医梦AI中台开放平台接口-v1.2.openapi.yaml), 明确每个接口的 Controller 归属模块和核心 Service 归属模块, 并定义 Controller → Service 的合法调用方向和禁止调用模式。
所有对外 API 的 Controller 统一放在 emoon-openplatform 模块的 controller/v1/ 包下。
Controller 只做四件事:
| 允许 | 不允许 |
|---|---|
| 解析 HTTP 请求参数和 Header | 直接调用 Mapper |
| 调用鉴权拦截器/工具类 | 直接调用 Dify API 或第三方 LLM SDK |
| 调用领域 Service(通过 API 接口) | 编写业务逻辑(状态机、计费、HIS 调用) |
封装统一响应(R<T> 或 SSE) |
直接操作数据库表 |
为什么 Controller 不能直接调 Mapper:
Controller 只负责 HTTP 协议适配。如果直接调 Mapper,会绕过 Application Service、
状态机、幂等校验、审计、计量和 Outbox。后续排障时无法通过 traceId 还原完整调用链。
下表来自 v1.2 正式联调基准版,每个接口对应一个 Controller 模块和一个核心 Service 实现模块。
| 方法 | 路径 | 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 | 🔴 同上 |
| 方法 | 路径 | 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 |
| 方法 | 路径 | 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(扩展审计表) |
flowchart LR
subgraph 入口层
OC["emoon-openplatform<br/>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 |
以下 6 条违反即拒绝 PR:
F1. Controller 直接调 Mapper
// ❌ 禁止
@RestController
public class FooController {
@Autowired
private AiConversationMapper mapper; // Controller 不应注入 Mapper
}
正确做法:Controller → Service 接口(API 模块定义)→ Service 实现(实现模块内调 Mapper)。
F2. 任意模块直接 import DifyApiClient
// ❌ 禁止:在 emoon-ai-card、emoon-ai-device 等非 Agent 模块中
import com.emoon.openplatform.dify.DifyApiClient;
Dify 调用只能出现在 emoon-ai-agent 的 adapter 包中,通过 AgentEngine 接口对外暴露。
F3. 卡片绕过 Agent 直接调 MCP 工具
// ❌ 禁止:在 emoon-ai-card 中
import com.emoon.ai.mcp.application.HisLockScheduleService;
卡片需要写 HIS 时,必须由 CardActionController 调 Agent 编排服务,Agent 调 Card 做状态校验和快照,再由 Agent 调 MCP 完成 HIS 写操作。
不能出现 Card → MCP 的短路调用。
F4. 前端/设备端直接调 /tools/{toolName}/invoke
❌ 禁止:机器人、自助机、导诊屏厂商的 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 签名逻辑
// ❌ 禁止:在 Service 实现类中
String sign = SignUtil.hmacSha256(payload, project.getSecretKey());
鉴权逻辑只能出现在 emoon-openplatform 的拦截器或 Controller 层。
Service 层接收的是已验证的 projectId/tenantId 上下文。
F6. 把 Dify API Key 暴露到日志或前端
❌ 禁止:log.info("Dify key: {}", difyApiKey)
❌ 禁止:响应体中包含 apiKey 字段
❌ 禁止:前端 JS 中硬编码 Dify Key
Dify API Key 只能存在于 ai_agent_engine_config.config_json 的加密字段中,
运行时由 Agent 模块解密后使用,不出现在任何日志、响应或前端代码中。
每个涉及后端 API 或模块的 PR 必须通过以下检查。此清单同时维护在 .github/pull_request_template.md 中。
emoon-openplatform/controller/v1/ 包下?X-Emoon-Idempotency-Key?*-api 模块、实现在 *-modules 模块?emoon-ai-mcp 工具封装?emoon-openplatform 里写业务逻辑?project_id 字段?接手一个新接口时,按以下顺序确认归属:
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`)。
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. 新模块必须经过技术负责人确认,禁止私建顶级模块。