领域驱动设计 · DDD 实战 · 设备全生命周期 · AI Agent · 2026-08
设备管理是船舶PMS系统的基石——所有保养计划、工单、备件关联都围绕设备卡片展开。本文记录我们如何用 Coze AI Agent,以 DDD 为方法论,将设备管理的需求规格转化为一份可交付开发团队的详细设计文档。
| 22 个功能点 | 4 个聚合根 | 13 张核心表 | 双驱动保养机制 |
|---|
1. 设备管理为什么难
设备管理看似是"设备信息的CRUD",实际上它是整个PMS系统的数据锚点。难点不在字段多少,而在它与其他模块的耦合密度:
- 设备计数器驱动保养计划的到期判断(时间+计数器双驱动)
- 设备部件关联备件库存(一个设备由哪些部件组成,每个部件需要什么备件)
- 应急设备需要独立的检查周期和合规记录
- 设备故障报告需要与工单形成闭环
- CWBT国标编码体系需要与设备类型映射
设备卡片 ──→ 计数器读数 ──→ 到期判断 ──→ 保养工作项目 ──→ 工单 │ │ ├──→ 部件结构 ──→ 备件关联 ├──→ 应急设备 ──→ 定期检查 └──→ 故障报告 ──→ 维修闭环需求文档把这些功能点列清楚了,但要变成开发团队可以直接编码的详细设计,需要完成领域建模、状态机设计、数据设计和集成设计。
2. 领域建模:识别四个聚合根
我们用 DDD 的聚合识别原则——“删除它时,什么必须一起删除”——从实体关系中识别聚合边界。
2.1 设备信息聚合(Device)
设备卡片是整个系统的核心主数据,包含设备基本属性、层级关系和技术参数。
聚合根:Device(设备)
包含实体:设备参数(DeviceParam)、设备备件关联(DevicePartsMaterial)
聚合边界:设备的基本属性和参数随设备生命周期管理;删除设备时,其参数和备件关联一并删除。
关键设计判断:设备的层级结构(ParentId/ParentDevice/DeviceLevel)是同一聚合内的自引用,不是跨聚合引用。子设备不能脱离父设备独立存在于结构树中,但子设备本身又是独立的聚合根——这是一个典型的"聚合内持有其他聚合根ID引用"的模式。
2.2 计数器聚合(DeviceCounter)
计数器是触发计划性保养的核心机制,它有独立的生命周期和变更历史。
聚合根:DeviceCounter(设备计数器)
包含实体:计数器变更日志(DeviceCounterLog)
聚合边界:计数器当前值和变更日志必须强一致——每次读数更新必须同时写入日志,不允许只更新值不记日志。
这是一个关键的聚合边界决策。计数器和设备是1:1关系,但我们没有把计数器放入设备聚合内部,原因是:
- 计数器的更新频率远高于设备信息变更
- 计数器有独立的修正审批流程
- 将高频更新的计数器与低频变更的设备信息分离,可以降低并发冲突
2.3 保养工作项目聚合(DeviceJob)
设备上的每个保养工作项目有独立的计划周期、审批状态和执行历史。
聚合根:DeviceJob(保养工作项目)
聚合边界:工作项目的计划参数、周期设置、审批信息和上次/下次执行日期在同一个事务中维护。
2.4 应急设备聚合(EmergencyEquipment)
应急设备(救生、消防等)有独立的检查周期和合规要求,与普通设备管理逻辑不同。
聚合根:EmergencyEquipment(应急设备)
包含实体:应急检查记录(EmergencyCheck)
聚合边界:应急设备的检查历史是设备定义的一部分,删除设备定义时检查记录一并归档。
此外,设备故障报告(DeviceFault)作为独立聚合根存在,它通过 CardId 与工单核关联,形成故障-维修闭环。
3. 核心机制:时间与计数器双驱动
设备管理最有领域特色的设计是双驱动保养触发机制。一个保养工作项目的到期判断,不是简单的"上次完成日期+周期",而是同时支持时间驱动和计数器驱动:
代码中体现这一机制的关键字段:
| 字段 | 含义 | 设计作用 |
|---|---|---|
Period+Unit | 周期值+周期单位 | 定义保养间隔(小时/天/月/次) |
LastDoneDate | 上次完成日期 | 时间驱动基准 |
LastDoneCounter | 上次完成时计数器读数 | 计数器驱动基准 |
NextPlanDate | 下次计划日期 | 时间驱动目标 |
SpanLeft/SpanRight | 提前/延后宽限期(天) | 允许的执行窗口 |
PlanType | 计划类型 | 区分时间驱动/计数器驱动/混合驱动 |
AI 辅助发现的设计细节:HourPerDay字段出现在计数器实体上,表明系统支持将运行小时按日均运行时间折算为日历天数,从而实现计数器驱动与时间驱动的统一计算。这是一个典型的"代码中藏着的业务知识"——如果只看需求文档的功能列表,很难发现这个折算逻辑。
4. 状态机设计:从代码字段还原业务流转
设备卡片需要审批后生效,保养工作项目也有审批流程。AI 通过分析 Status 字段、Controller 名称(Approve)和审批字段(ApproveMan/ApproveDate/ApproveMemo),推导出两个状态机。
4.1 设备卡片状态机
4.2 计数器变更流程
计数器有三种变更场景,每种场景的业务规则不同:
| 场景 | 触发方式 | 是否需要审批 | 是否写日志 | 修正原因 |
|---|---|---|---|---|
| 日常读数录入 | 船员手工录入 | 否 | 是 | 不需要 |
| 读数修正 | 发现录入错误 | 是 | 是 | 必填 |
| 依赖设备联动 | 关联设备计数器更新 | 否 | 是 | 自动标注 |
ReadWay字段标识读数来源方式,GrouplogId将同一次批量录入的多条日志关联在一起——这些字段细节帮助我们设计出更精确的应用服务接口。
5. 数据设计:13张表的字段级映射
详细设计的数据设计部分,要求每个字段都来自代码实体,不允许编造。AI Agent 逐行读取了以下实体文件:
device.cs(28个字段)device_counter.cs(9个字段)device_counter_log.cs(8个字段)device_job.cs(35个字段)mat_emergencyequip.cs(10个字段)mat_emergencycheck.cs(11个字段)device_parts_material.cs(4个字段)device_param.cs(6个字段)dic_device_sys.cs(5个字段)dic_device_part.cs(10个字段)dic_cwbt2devtype.cs(3个字段)hkmw_device_fault.cs(24个字段,变体独有)dic_mat_counter.cs(9个字段)
对每张表,设计文档给出了:字段名、类型、是否必填、默认值、业务含义和设计说明。例如设备表的关键字段设计:
| 字段 | 类型 | 说明 | 设计要点 |
|---|---|---|---|
| DeviceId | string | 设备唯一标识 | 聚合根ID,船舶内唯一 |
| ShipId | string | 船舶标识 | 多租户隔离键 |
| ParentId | string | 父设备ID | 自引用,构建设备层级树 |
| DeviceLevel | int | 设备层级 | 1=系统级, 2=设备级, 3=部件级 |
| IfKey | int | 是否关键设备 | 关键设备保养要求更严格 |
| PmsDevice | int | 是否纳入PMS | 未纳入的设备不生成保养计划 |
| SpareFlag | int? | 备件标识 | 标识该设备是否可作为备件管理 |
| Status | int | 设备状态 | 关联状态机 |
多客户变体处理:hkmw_device_fault是 HKMW 变体独有的故障报告实体。设计文档中通过变体标记明确标注,在标准产品中为可选模块。
6. CWBT 国标集成
CWBT(船舶维修保养体系)是中国船舶行业的国家标准。设备管理需要将企业内部的设备类型与 CWBT 编码体系映射。
设计中,DicCwbt2devtype作为一个值对象(映射关系),通过TypeCode(设备类型编码)和CwbtCode(CWBT编码)建立多对多映射。这个映射关系的设计要点是:
- 映射是多对多的:一个设备类型可能对应多个CWBT编码,一个CWBT编码也可能覆盖多个设备类型
- 映射由岸端统一维护,同步到船端
- 设备部件字典(DicDevicePart)也持有 CwbtCode,支持按部件维度的CWBT追溯
7. 跨域集成
设备管理作为PMS的基础数据域,与多个其他上下文有集成关系:
每个集成点都设计了防腐层(ACL)接口,确保设备上下文的内部模型变化不直接影响下游。例如,保养上下文需要的计数器读数信息通过ICounterReadingProvider接口获取,而不是直接访问设备聚合的内部状态。
8. AI 做了什么,人做了什么
AI 完成的工作(约80%)
- 实体字段提取:逐文件读取13个C#实体类,提取全部162个字段
- 聚合边界识别:分析实体间的引用关系和生命周期,提出4个聚合根的划分方案
- 状态机推导:从Status字段、审批字段和Controller命名推导出状态流转图
- 时序图生成:根据Controller调用链和实体关系绘制核心流程时序
- 字段设计表:为每个字段生成类型、约束、业务含义的说明
- 应用服务接口:基于用例生成Command/Query和DTO定义
- 仓储接口:为每个聚合根生成仓储接口和查询规格
- ADR记录:将关键设计决策结构化记录
人需要决策的部分(约20%)
- 计数器是否独立聚合:AI 提出了方案,但最终需要人确认高频更新分离的策略
- 设备层级的聚合处理:自引用结构是放聚合内还是跨聚合引用,需要根据事务一致性要求判断
- 双驱动折算逻辑:
HourPerDay的业务含义需要领域专家确认 - 变体差异的范围:哪些HKMW独有功能应该纳入标准设计,需要产品决策
- CWBT映射的维护策略:多对多映射的数据维护责任和同步方向
9. 经验总结
9.1 代码是最好的需求补充
需求文档说"支持计数器管理",但代码中的HourPerDay、GrouplogId、DependDeviceId三个字段揭示了三个需求文档未提及的业务能力:日均折算、批量分组、设备依赖联动。AI 读取代码的价值不仅是获取字段,更是发现字段背后隐藏的业务规则。
9.2 变体实体需要显式标记
多客户变体系统中,某些实体只在特定变体中存在(如hkmw_device_fault)。设计文档需要明确标注变体归属,避免将变体特有逻辑误纳入标准设计。
9.3 DDD 的聚合划分需要迭代
AI 第一次生成的方案把计数器放在设备聚合内部。我们通过分析更新频率和并发冲突,将计数器独立为单独聚合。这种迭代是正常的——聚合边界不是一次性确定的,需要根据业务场景和技术约束反复权衡。
9.4 状态机必须从代码验证
需求文档只说"设备卡片需审批后生效",但审批有几级、驳回后能否重提、停用后能否重启——这些细节只能从代码字段和Controller逻辑中确认。AI 在这方面的表现令人惊喜:它通过分析ApproveMan/ApproveDate/ApproveMemo的组合模式,准确推导出了审批流程。