Compare commits
11 Commits
simple_for
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| cec5e5fc31 | |||
| 2a054c538f | |||
| 35ae4f34fb | |||
|
|
9c1361382c | ||
| 78965dbc9a | |||
| d87b5bd996 | |||
| c6b1214b99 | |||
| fd1c58e8a3 | |||
| 6e02f747ae | |||
| 99b494ef56 | |||
| 6154c93ef6 |
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/.idea
|
||||
0
coding-llms-full.txt
Normal file
0
coding-llms-full.txt
Normal file
144
knowledge-brief.md
Normal file
144
knowledge-brief.md
Normal file
@@ -0,0 +1,144 @@
|
||||
# TOCO平台简介
|
||||
|
||||
## 平台概述
|
||||
|
||||
TOCO是一款专业的软件研发平台,专注于软件设计和核心代码的自动生成。基于DDD(领域驱动设计)、分层设计、CQRS等经典软件工程理论,提供从数据库到API的全栈开发能力,显著提升开发设计和编程效率。
|
||||
|
||||
核心价值在于从软件工程理论出发,提供可视化的软件设计能力,设计结果可直接转换为标准格式的代码,提升编码一致性和效率。主要特性包括可视化设计、模型关联、多人协作、代码生成器等。
|
||||
|
||||
## 核心设计元素
|
||||
|
||||
### 1. 模块(Module)
|
||||
模块是系统领域的具体细分,映射为Java工程中的module,代表系统的叶子子域。
|
||||
- **命名规则**:小写英文字母+下划线(如meeting、user_detail),禁止添加固定后缀
|
||||
- **代码产物**:生成独立的Java Module,采用entrance、service、manager、persist、common分层结构
|
||||
|
||||
### 2. 枚举(Enum)
|
||||
表达常量值集合,可被其他模块使用,可作为字段类型。
|
||||
- **命名规则**:以_enum结尾
|
||||
- **枚举值**:全大写字母+下划线
|
||||
- **代码产物**:在common模块生成Java类,类名以Enum结尾,位于common.enums包路径
|
||||
|
||||
### 3. 值对象(EO)
|
||||
POJO对象结构,可被其他模块使用,可作为实体字段类型。
|
||||
- **命名规则**:以_eo结尾
|
||||
- **字段限制**:只能为基本类型(含List)、EO、Enum
|
||||
- **代码产物**:在persist层生成,类名以Eo结尾,位于persist.eo包路径
|
||||
|
||||
### 4. 实体关系(ER/Entity)
|
||||
实体及其关系,一个实体对应一个数据库表,关系为实体间的外键依赖关系。包含名称、字段、字段类型、主键、索引等属性。是聚合的基础,也是DTO和VO的派生基础。
|
||||
|
||||
### 5. 聚合对象(BO/业务对象)
|
||||
对一组密切关联实体的封装,从聚合根开始,通过实体关系按层级组装其他实体。
|
||||
- **特点**:提供内存一致性视图和数据操作入口
|
||||
- **限制**:只能在单一模块中组合,一个实体只能属于一个聚合对象
|
||||
- **代码产物**:生成BO和BaseBO,BaseBO封装实体属性和关系
|
||||
|
||||
### 6. 数据传输对象(DTO)
|
||||
表达以某个Entity为基础,通过外键关系关联多个Entity的数据结构。
|
||||
- **分类**:BaseDTO(派生自Entity)和普通DTO(派生自BaseDTO)
|
||||
- **字段扩展**:支持正向替换和反向注入
|
||||
- **预定义方法**:根据根Entity的唯一索引自动生成RPC方法
|
||||
- **跨模块使用**:公开DTO的预定义RPC可被其他模块订阅调用
|
||||
|
||||
**获取方式判断原则**:
|
||||
- 主键或唯一索引查询 → 使用预定义方法
|
||||
- 其他复杂查询条件 → 使用读方案
|
||||
|
||||
### 7. 视图对象(VO)
|
||||
基于BaseDTO派生,用于视图层与前端的数据传输。
|
||||
- **用途**:作为HTTP API返回值,不能作为RPC返回值
|
||||
- **继承规则**:DTO字段为基础类型时保持一致,DTO字段时需转换为对应的VO
|
||||
- **转换方法**:自动生成基础convert方法和带数据拼装的convert方法
|
||||
- **根VO vs 子VO**:根VO有uuid标识可被引用,子VO附属于根VO无uuid
|
||||
|
||||
**获取方式判断原则**:
|
||||
- 主键或唯一索引查询 → 预定义方法获取DTO后转换
|
||||
- 其他复杂查询 → 通过读方案获取DTO后转换,或直接获取VO
|
||||
|
||||
### 8. 查询对象(WO)
|
||||
表达以某个Entity为基础,通过外键关系关联多个Entity的查询结构。作为ReadPlan的查询上下文使用,支持数据过滤功能。
|
||||
|
||||
### 9. 读方案(ReadPlan)
|
||||
描述如何基于查询对象从数据库获取DTO和VO列表数据。
|
||||
- **核心能力**:
|
||||
- 根据查询条件返回符合条件的DTO或VO的id列表
|
||||
- 根据字段过滤条件对列表字段数据进行过滤
|
||||
- **排序支持**:默认排序和自定义排序两种方式
|
||||
- **代码产物**:自动生成RPC方法(返回DTO)或Java方法(返回VO)
|
||||
|
||||
### 10. 查询传输对象(QTO)
|
||||
读方案的查询参数结构,每个读方案对应一个QTO。调用方按QTO结构传入查询参数。
|
||||
|
||||
### 11. 写方案(WritePlan)
|
||||
所有数据库写操作都通过写方案实现,每个写方案只能操作一个聚合内部的表。
|
||||
- **操作类型**:CREATE、UPDATE、DELETE、CREATE_ON_DUPLICATE_UPDATE、FULL_MERGE、PARTIAL_MERGE
|
||||
- **代码产物**:生成对应的RPC方法,参数为BTO,返回聚合根主键
|
||||
|
||||
### 12. 业务变更传输对象(BTO)
|
||||
写方案自动生成的参数结构,按照写方案选定的操作实体根据关系形成树形集合。
|
||||
|
||||
### 13. 服务层方法(RPC)
|
||||
按可见性分为公开RPC(可被其他模块订阅)和非公开RPC(仅当前模块使用)。
|
||||
- **参数类型限制**:只能为QTO、BTO、Enum、基本类型
|
||||
- **返回值类型限制**:只能为DTO、Enum、基本类型
|
||||
- **创建方式**:DTO创建自动生成、读方案自动生成、写方案自动生成、自定义RPC
|
||||
|
||||
### 14. 应用程序接口(API)
|
||||
定义对外暴露的HTTP接口。
|
||||
- **参数类型限制**:只能为QTO、BTO、Enum、基本类型
|
||||
- **返回值类型限制**:只能为VO、Enum、基本类型
|
||||
- **返回包装**:框架自动包装返回值(code、message、data)
|
||||
|
||||
### 15. 流程服务(FunctionFlow)
|
||||
针对复杂业务的流程拆解,把业务过程分解成流程节点。
|
||||
- **使用场景**:API/RPC涉及写服务超过3个时推荐使用
|
||||
- **节点类型**:顺序节点、条件节点、选择节点、开始节点
|
||||
- **代码产物**:生成FlowConfig、Service、FlowNode、FlowContext
|
||||
|
||||
### 16. 领域消息 (DomainMessage)
|
||||
可以监听聚合对象实体的创建、删除、更新事件;通过事件驱动的方式实现异步解耦;也是一种跨模块通信的方式;消息驱动的一种实现;
|
||||
|
||||
|
||||
## 最佳实践原则
|
||||
|
||||
### 1. 设计分析结果应用
|
||||
- **细节设计分析**:针对简单需求直接分析读写方案、接口等
|
||||
- **流程拆解设计分析**:针对复杂需求拆解为多个短流程,必须调用createFunctionFlow
|
||||
|
||||
### 2. 写方案创建原则
|
||||
分析出所有写数据场景,按聚合维度分组,每个写场景都需要创建对应的写方案。
|
||||
|
||||
### 3. 接口参数类型选择
|
||||
- **读场景**:参数优先使用QTO
|
||||
- **写场景**:参数优先使用BTO
|
||||
- **严格限制**:DTO、VO不能作为参数;QTO、BTO不能作为返回值
|
||||
|
||||
### 4. 数据获取方式判断
|
||||
严格按查询条件判断,不能根据返回值是否需要数据拼装判断:
|
||||
- 主键或唯一索引 → 预定义方法
|
||||
- 其他复杂条件 → 读方案
|
||||
|
||||
## 代码编写指南
|
||||
|
||||
### 基本原则
|
||||
- **准确性优先**:不产生编译错误,不调用不存在的函数/字段
|
||||
- **单一职责**:Controller做参数校验,Service做业务逻辑
|
||||
- **复用性考虑**:复杂逻辑拆分为多个函数,单函数不超过30行
|
||||
- **循环优化**:避免在循环中执行数据库操作
|
||||
|
||||
### 特殊规则
|
||||
- **异常处理**:统一使用IgnoredException(code, "message")
|
||||
- **BO校验**:业务不变性规则写在BO的聚合校验函数中
|
||||
- **系统代码**:不要修改/** This block is generated by vs **/标注的代码
|
||||
- **代码结构**:使用{}和注释分块提升可读性
|
||||
|
||||
### 注解说明
|
||||
- **@AutoGenerated**:标识自动生成的类和方法
|
||||
- locked=true:不建议修改
|
||||
- uuid:唯一标识,包含|字符表示特殊格式
|
||||
|
||||
## 技术栈
|
||||
- **语言/框架**:Java、SpringBoot
|
||||
- **数据访问**:MyBatis-plus(读)、Hibernate(写)
|
||||
- **分层结构**:entrance、service、manager、persist、common
|
||||
154
llms-full.txt
154
llms-full.txt
@@ -333,8 +333,8 @@ DateTime endTime; //会议结束时间
|
||||
}
|
||||
```
|
||||
- **TOCO中json结构描述:** 在TOCO中,VO使用一个json结构表示,该结构可用于理解VO的含义,或作为创建、更新VO工具的参数。部分字段的含义为:expandList为正向替换,reverseExpandList为反向注入,extendFieldList为来自派生源DTO的字段,customFieldList为自定义字段。expandListList中,foreignKeyInThisEntity为正向替换对应的本表外键字段的名字,voFieldName为正向替换之后给该字段的起的新名字;reverseExpandList中,foreignKeyInOtherEntity为反向注入对应的他表外键字段的名字,voFieldName为反向注入之后给该字段的起的新名字;customFieldList中,uuid为自定义字段特有的UUID,创建DTO的时候不需要填入,因为TOCO会自动为其分配UUID,更新DTO的时候需要传入,用于定位需要更新的自定义字段;typeUuid参数对应类结构的UUID,当type为List且innerType=Enum、Eo时会包含该字段;extendFieldList中,name为继承字段的名称,如果要继承的DTO字段不是DTO类型,则VO中的字段类型将与DTO字段一致,如果DTO中的字段为DTO或List<DTO>类型,由于VO中的字段类型不能为DTO,**必须**将该DTO转换为VO才可作为VO字段使用,所以extendFieldList中会有**一个vo结构**,用来表示该字段DTO类型派生出的VO定义,注意:该VO字段中的VO类型必须派生自继承的DTO字段类型!
|
||||
示例如下:
|
||||
系统中存在meeting_detail_dto
|
||||
示例如下:
|
||||
系统中存在meeting_detail_dto
|
||||
```json
|
||||
{
|
||||
"uuid": "cd55c96b-aa67-bfb2-7614-70b503a8f8bf",
|
||||
@@ -508,7 +508,17 @@ DateTime endTime; //会议结束时间
|
||||
- 建议在Converter中进行代码扩展,不建议修改结构定义文件。其中VO的**自定义字段**由于不直接派生自DTO,所以一般会对应取数逻辑代码。通常如果涉及到数据获取、计算和拼装,批量处理的性能最好,所以自定义字段对应的代码位置**必须**放在Converter的**Map**基础转换方法convertTo${VoName}Map中,批量取数组装,如UserVoConverter.convertToUserVoMap
|
||||
#### **2.8 查询对象(WO)**
|
||||
- **定义与用途:** 在TOCO中,WO表达某个Entity为基本,通过外键关系不断关联多个Entity的数据结构。WO还隐式表达了数据的取数拼装,这种拼装符合外键关系. WO作为ReadPlan的查询上下文使用,所以在创建ReadPlan之前需要先创建WO对象。在理解一个ReadPlan的语义的时候需要以WO作为上下文。
|
||||
- **查询对象案设计元素的表达**
|
||||
如果对应的需求,在返回DTO|VO的时候需要对DTO|VO的列表属性进行过滤,则需要根据对应的DTO|VO的结构定义,扩展出对应的WO对象(需要过滤的字段名需要和DTO|VO的字段名保持一致),例如:
|
||||
查询DTO
|
||||
```java
|
||||
class MeetingDto {
|
||||
String meetingId;
|
||||
String meetingName;
|
||||
List<MeetingAgenda> agendaList;
|
||||
}
|
||||
```
|
||||
需求是需要根据会议名称查询会议列表,并且根据会议议程信息过滤掉部分议程,那在定义查询对象的时候就需要包含议程信息,并且扩展字段的名称要定义为**agendaList**
|
||||
- **查询对象设计元素的表达**
|
||||
- 以json格式表达,json schema 定义如下
|
||||
```json
|
||||
{
|
||||
@@ -546,8 +556,11 @@ DateTime endTime; //会议结束时间
|
||||
}
|
||||
```
|
||||
- **如何创建/生成:**
|
||||
- **关键配置:** Wo中的字段分为三种:a.继承Entity的字段,和Entity的字段类型一样;b.扩展字段,含正向替换和反向注入字段,类型为WO或List<WO>;
|
||||
- **字段扩展方式:**TOCO定义了一个WO组装方法,适用于WO通过外键关系替换/注入对应Entity的信息,对象化表达有外键关系的Entity信息。只要存在外键关系且满足以下条件即可扩展:a.对于正向替换:当前实体存在指向其他实体的外键字段;b.对于反向注入:其他实体存在指向当前实体的外键字段。
|
||||
- **创建思路** 按照需要查询返回的DTO|VO的结构,构建出同构的WO对象(扩展和反向扩展的字段名保持一致),然后根据查询需求和过滤需求对WO进行二次裁剪或和扩展:
|
||||
- 去掉过滤和查询都不需要的扩展
|
||||
- 补全查询或者字段过滤需要扩展
|
||||
- **关键配置:** Wo中的字段分为三种:a.继承Entity的字段,和Entity的字段类型一样;b.扩展字段,含正向替换和反向注入字段,类型为WO或List<WO>;
|
||||
- **字段扩展方式:** TOCO定义了一个WO组装方法,适用于WO通过外键关系替换/注入对应Entity的信息,对象化表达有外键关系的Entity信息。只要存在外键关系且满足以下条件即可扩展:a.对于正向替换:当前实体存在指向其他实体的外键字段;b.对于反向注入:其他实体存在指向当前实体的外键字段。
|
||||
例如:有两个Entity
|
||||
```
|
||||
MeetingRoom{ //会议室
|
||||
@@ -559,8 +572,8 @@ DateTime endTime; //会议结束时间
|
||||
Long roomid; //占用的会议室id,到MeetingRoom的外键,n:1关系
|
||||
Long backupRoomid; //备用的会议室id,到MeetingRoom的外键,n:1关系
|
||||
String title; //会议标题
|
||||
DateTime startTime; //会议开始时间
|
||||
DateTime endTime; //会议结束时间
|
||||
DateTime startTime; //会议开始时间
|
||||
DateTime endTime; //会议结束时间
|
||||
}
|
||||
```
|
||||
其中Meeting和MeetingRoom是n:1关系。即多个会议室会占用同一个会议室。
|
||||
@@ -580,9 +593,9 @@ DateTime endTime; //会议结束时间
|
||||
DateTime endTime; //会议结束时间
|
||||
}
|
||||
```
|
||||
又例如:需要会议和其候选会议室时,将Meeting表中的backupRoomid进行正向替换。
|
||||
又例如:需要会议,且即需要其占用会议室,也需要候选会议室时,将Meeting表中的roomid,backupRoomid都进行正向替换。
|
||||
同时TOCO还定义了“反向注入”这个行为,选定一个表,如果有另外的表到前表有外键,选择和需求相关的具体的外键属性在选定表中增加一个以另外表为根的组合对象(当外键关系是1:1时)或者组合对象的列表(当外键关系是n:1时)。需求是:“获取会议室,和占用它的会议信息”,需要选定MeetingRoom,那么基于另外的表Meeting中存在字段roomid,为到MeetingRoom的n:1关系外键。可以将List<MeetingBaseDto>反向注入到MeetingRoom中,最终生成一个以Meeting为根的组装对象,生成:
|
||||
又例如:需要会议和其候选会议室时,将Meeting表中的backupRoomid进行正向替换。
|
||||
又例如:需要会议,且即需要其占用会议室,也需要候选会议室时,将Meeting表中的roomid,backupRoomid都进行正向替换。
|
||||
同时TOCO还定义了“反向注入”这个行为,选定一个表,如果有另外的表到前表有外键,选择和需求相关的具体的外键属性在选定表中增加一个以另外表为根的组合对象(当外键关系是1:1时)或者组合对象的列表(当外键关系是n:1时)。需求是:“获取会议室,和占用它的会议信息”,需要选定MeetingRoom,那么基于另外的表Meeting中存在字段roomid,为到MeetingRoom的n:1关系外键。可以将List<MeetingBaseDto>反向注入到MeetingRoom中,最终生成一个以Meeting为根的组装对象,生成:
|
||||
```
|
||||
MeetingRoomWithMeetingWo {
|
||||
Long id;// 会议室id
|
||||
@@ -630,12 +643,19 @@ DateTime endTime; //会议结束时间
|
||||
}
|
||||
```
|
||||
#### **2.9 读方案 (ReadPlan)**
|
||||
- **定义与用途:** 在TOCO中,读方案描述了一种数据库查询方案,查询条件为一个面向对象的查询语句,其中可以包含存在外键关系的多个实体的字段(如age=18 and name like #nameLike and school.name like #schoolNameLike,其中以#开头的是自定义参数,会自动生成一个QTO结构,由查询调用方动态传入),结果为符合查询条件的**一种**DTO或VO列表。读方案内部会将该面向对象的查询条件转化为复杂的join sql语句,来实现对数据库的查询,以及返回的复杂DTO和VO数据的组装。读方案可以指定其分页方式(不分页、页码分页、瀑布流分页),以及是否生成计数方法(用于返回符合条件的记录数量)。
|
||||
- **使用建议:** 如果查询条件只有主键,则建议使用预定义的getBy${PrimaryKey}或getBy${PrimaryKey}s方法来获取DTO(参照DTO的**预定义方法**)或VO(参照**复杂嵌套VO获取流程**),不建议使用读方案。非主键查询或多条件查询建议使用读方案。
|
||||
- **关键配置:** 名称(小写字母+下划线,不要以read_plan结尾)、返回结构(DTO/VO,一个读方案**不能**同时返回多种DTO或VO)、查询条件的自然语言描述、是否生成计数方法、排序字段(如果选择不分页,则不需要)
|
||||
- **定义与用途:** 在TOCO中,针对DTO和VO,读方案描述了如何基于查询对象从数据库获取DTO和VO列表数据,主要提供了两个能力
|
||||
- 根据查询条件,返回符合条件的DTO或VO的id列表
|
||||
- 根据字段过滤条件,对DTO和VO的列表字段数据进行过滤
|
||||
- **关键配置:** 名称(小写字母+下划线,不要以read_plan结尾)、返回结构(DTO/VO,一个读方案**不能**同时返回多种DTO或VO)、查询条件的自然语言描述、是否生成计数方法、排序字段(如果选择不分页,则不需要)、过滤字段以及过滤条件。
|
||||
- **与RPC、代码的关系:** 对于每一个返回DTO的读方案,TOCO会为每种分页方式自动生成一个RPC方法,其参数为对应的QTO,返回值为DTO列表;如果选择了生成计数方法,则还会生成一个RPC,参数为QTO,返回值为符合条件的DTO数量。同样,对于每一个返回VO的读方案,TOCO会自动生成一个Java方法,其参数为对应的QTO,返回值为VO列表,方法内部逻辑已经由TOCO完全实现;如果选择了生成计数方法,则还会生成一个count方法,参数为QTO,返回值为符合条件的DTO数量,方法内部逻辑已经由TOCO完全实现
|
||||
- **如何创建/生成:** 在创建读方案时,必须先调用工具创建或选择现有的**一种**DTO或VO作为返回值类型,然后再定义查询条件的自然语言描述(如:根据用户姓名、年龄、学校名称,查询用户列表),分页方式、是否生成计数方法、排序字段等。
|
||||
- 你需要提取需求中的查询部分信息,以输入的查询对象作为查询上下文件,构建一个查询语句
|
||||
- **排序** 对于返回列表数据的排序,排序规则和SQL的排序类似(通过指定字段以及升序|降序,字段可以组合;ReadPlan支持有**两种**方式:
|
||||
- **默认排序**:指定默认排序字段(不需要入参指定排序字段)
|
||||
- **自定义排序**:指定排序字段(需要入参指定排序字段),这种方式通常能很好的满足列表头动态指定排序的需求
|
||||
- **排序字段的来源**:并不是查询字段中的所有字段都能作为排序字段,因为返回的根DTO|VO的id去重列表,所以排序字段只能来源自根WO(不包括列表WO属性: 比如MeetingWO包含了List<MeetingAgendaWO> agendaList属性, 那么agendaList不能作为排序字段),以及根WO扩展出来的非列表属性的WO的字段(比如MeetingWO包含了MeetingRoomWO,那么MeetingRoomWO的属性也能作为排序字段),**注意**:为了唯一确定是给按照那个字段进行排序,排序字段是一个从根节点到当前属性的路径,例如:ADto包含了BDto bDto,BDto具备属性name,如果指定对BDto的name字段排序,则字段路径为:bDto.name
|
||||
- 你需要提取需求中的查询部分信息,以输入的查询对象作为查询上下文件,构建一个查询语句
|
||||
- 如果有列表属性过滤需求(如果没有filter,对每个列表属性都是放回全部数据,例如:MeetingDto的MeetingAgendaDtoList属性,如果不设置MeetingAgendaDtoList的filter,则返回该会议的全部议程信息):你需要提取需求中的过滤部分信息,以输入的查询对象作为查询上下文件,针对可过滤字段(列表属性)分别创建过滤条件, **注意** 过滤条件不能使用列表属性作为查询条件属性(即不能使用contains语法),为了唯一确定是给那个列表属性的指定过滤条件,属性字段是一个从根节点属性到当前属性的路径,例如:ADto包含了List<BDto> bDtoList;BDto包含了List<CDto> cDtoList,如果对cDtoList使用Filter,则字段路径为:bDtoList.cDtoList
|
||||
- 查询语法和过滤语法
|
||||
- 基本语法格式是 属性名 操作符 变量或常量,变量表示了这个数据是外部传入的,用 #变量名为格式;如果是枚举类型常量,需要使用'符号包起来,例如: 男性枚举类型 'MALE'
|
||||
- 对于数值、时间类型属性有:!=, ==, >, <,<=,>=, in,notIn, isNullOrNot 这些操作符
|
||||
- 对于文本类型属性有:like, isNotNull, isNull,!=, ==, in,notIn, isNullOrNot 这些操作符
|
||||
@@ -742,20 +762,35 @@ DateTime endTime; //会议结束时间
|
||||
"supportUnPage":{"type":"boolean", "description":"如果不需要分页,一次性返回部数据,则返回true"},
|
||||
"supportWaterfall":{"type":"boolean","description":"是否需要瀑布流"},
|
||||
"query":{"type":"string","description":"查询语句,符合前述语法定义"},
|
||||
"voOrDtoId":{"type":"string","description":"返回数据对象(VO或DTO)的uuid,创建的时候必须指定,更新的时候不传递"}
|
||||
"voOrDtoId":{"type":"string","description":"返回数据对象(VO或DTO)的uuid,创建的时候必须指定,更新的时候不传递"},
|
||||
"outOrder":{"type":"array","description":"入参排序方式定义",
|
||||
"items":{"type":"object","description":"定义字段排序方式","required":["fieldPath", "direction"],
|
||||
"properties":{"fieldPath": {"type":"string","description":"字段名路径"},"direction": {"type":"string","description":"排序方向,可以是ASC(升序)或者DESC(降序)"}}}
|
||||
},
|
||||
"defaultOrder": {"type":"array","description":"默认排序方式定义",
|
||||
"items": {"type":"object","description":"定义字段排序方式","required":["fieldPath", "direction"],
|
||||
"properties":{"fieldPath": {"type":"string","description":"字段名"},"direction": {"type":"string","description":"排序方向,可以是ASC(升序)或者DESC(降序)"}}}
|
||||
},
|
||||
"filters": {"type": "array", "description":"过滤条件定义",
|
||||
"items": {"type":"object","description":"定义过滤条件","required":["fieldPath", "filter"],
|
||||
"properties":{
|
||||
"fieldPath": {"type":"string","description":"需要过滤的列表字段路径"},
|
||||
"filter": {"type":"object","description":"过滤语法,符合前述的语法定义,不能使用contains语法"}
|
||||
}
|
||||
}
|
||||
},
|
||||
"required":["name","description","query"]
|
||||
}
|
||||
},
|
||||
"required":["qto"]
|
||||
}
|
||||
}
|
||||
```
|
||||
- **举例**
|
||||
- 上下文
|
||||
```java
|
||||
public class meeting_room {
|
||||
storey storey; // 楼层id
|
||||
List<image_storage_info_eo> picture; // 照片
|
||||
String name; // 名称
|
||||
room_type_enum room_type; // 会议室类型
|
||||
List<equipment_enum> equipment; // 设备
|
||||
@@ -764,21 +799,14 @@ DateTime endTime; //会议结束时间
|
||||
String description; // 说明
|
||||
Boolean enable_indicator; // 是否启用
|
||||
String input_code; // 输入码
|
||||
Date created_at; // 创建时间
|
||||
Date updated_at; // 更新时间
|
||||
Long storey_id; // 楼层id
|
||||
Long lock_version; // 乐观锁字段
|
||||
Long contact_staff_id; // 联系员工ID
|
||||
List<meeting> meeting;
|
||||
Long deleted_at; // 删除时间
|
||||
List<meeting> meeting; //注意:该字段可添加过滤条件
|
||||
class storey { // 建筑的楼层
|
||||
location location; // 位置id
|
||||
String name; // 楼层
|
||||
Long id; // 主键
|
||||
Long sort_number; // 排序号
|
||||
Date created_at; // 创建时间
|
||||
Date updated_at; // 更新时间
|
||||
Long deleted_at; // 删除时间
|
||||
Long location_id; // 位置id
|
||||
}
|
||||
class meeting {
|
||||
@@ -789,18 +817,12 @@ DateTime endTime; //会议结束时间
|
||||
Long room_id; // 会议室id
|
||||
String description; // 描述
|
||||
Long create_user_id; // 创建人id
|
||||
Date created_at; // 创建时间
|
||||
Date updated_at; // 更新时间
|
||||
Long deleted_at; // 删除时间
|
||||
}
|
||||
class location { // 位置信息,如楼栋
|
||||
String name; // 名称
|
||||
Long id; // 主键
|
||||
String description; // 描述
|
||||
Long sort_number; // 排序号
|
||||
Date created_at; // 创建时间
|
||||
Date updated_at; // 更新时间
|
||||
Long deleted_at; // 删除时间
|
||||
}
|
||||
enum room_type_enum { //
|
||||
SMALL, //小会议室
|
||||
@@ -816,21 +838,25 @@ DateTime endTime; //会议结束时间
|
||||
}
|
||||
```
|
||||
- 用户需求
|
||||
获取一段时间未被使用的会议室(包含当前会议已选择的会议室),分页返回
|
||||
获取一段时间未被使用的会议室(包含当前会议已选择的会议室),并且根据会议名称会议信息,最后分页返回。
|
||||
- 对应的读方案定义
|
||||
```json
|
||||
{
|
||||
"qto": {
|
||||
"name": "get_unused_meeting_room_list",
|
||||
"description": "获取未使用的会议室(包含当前会议已选择的会议室)",
|
||||
"woId":"759bedd4-4540-4de6-b65d-d44912fb0991",
|
||||
"dtoOrVoId":"e50c02c3-f336-4dc6-88e1-24ffe3dea1e2"
|
||||
"dtoOrVoId":"e50c02c3-f336-4dc6-88e1-24ffe3dea1e2",
|
||||
"generateCountApi": true,
|
||||
"supportPaginate": true,
|
||||
"supportUnPage": false,
|
||||
"supportWaterfall": false,
|
||||
"query": "enable_indicator == true AND ( id == #idIs OR meeting isNull OR NOT ( meeting contains ( start_time <= #meetingEndTime AND end_time >= #meetingStartTime ) ) )"
|
||||
"query": "enable_indicator == true AND ( id == #idIs OR meeting isNull OR NOT ( meeting contains ( start_time <= #meetingEndTime AND end_time >= #meetingStartTime ) ) )",
|
||||
"filters": [
|
||||
{
|
||||
"fieldPath": "meeting",
|
||||
"filter": "title like #meetingTitleLike"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
- **代码产物和修改建议**
|
||||
@@ -873,7 +899,7 @@ DateTime endTime; //会议结束时间
|
||||
- **与API的关系:** QTO通常可作为API的参数,API接收到参数后可直接透传给内部的RPC进行调用
|
||||
#### **2.11 写方案 (WritePlan)**
|
||||
- **定义与用途:** TOCO针对写场景定义了一种写方案,所有对数据库的写操作都只能通过写方案实现。写方案包含了对数据库表的写操作。每个写方案只能操作一个聚合内部的表,同时对一个聚合内表的操作尽量合并至一个写方案中(根据复用性、内聚性等方面具体情况具体分析)。注意写方案可以一次操作聚合内的多张表,如location和storey是同一个聚合,并且location和storey是**1:N**关系,location是父对象(聚合根),storey是子对象,那么我们可以创建一个写方案,同时更新单个聚合根location中的信息,以及操作其下的子表storey**列表**信息,如新增、删除、修改storey等;也可以创建一个写方案单独更新单个子表storey对象信息。
|
||||
- **关键配置:** 名称(小写字母+下划线,不要以write_plan结尾),操作的聚合,聚合内的实体和字段,对每个实体的操作类型:CREATE(创建**单个**实体),UPDATE(更新**单个**实体), DELETE(删除**单个**实体), CREATE_ON_DUPLICATE_UPDATE(创建或者更新**单个**实体),FULL_MERGE( 批量更新**列表**数据,根据传入的列表数据,一条一条执行CREATE_ON_DUPLICATE_UPDATE的操作;并且删除掉老的列表中不在传入列表数据中的部分), PARTIAL_MERGE(批量更新**列表**数据,根据传入的列表数据,一条一条执行)
|
||||
- **关键配置:** 名称(小写字母+下划线,不要以write_plan结尾,注意: 因为一次只能操作一个聚合对象,所以对根跟实体的批量操作命名为batch_xxx是不合适的),操作的聚合,聚合内的实体和字段,对每个实体的操作类型:CREATE(创建**单个**实体),UPDATE(更新**单个**实体), DELETE(删除**单个**实体), CREATE_ON_DUPLICATE_UPDATE(创建或者更新**单个**实体),FULL_MERGE( 批量更新**列表**数据,根据传入的列表数据,一条一条执行CREATE_ON_DUPLICATE_UPDATE的操作;并且删除掉老的列表中不在传入列表数据中的部分), PARTIAL_MERGE(批量更新**列表**数据,根据传入的列表数据,一条一条执行)
|
||||
- **与RPC的关系:** 对于每一个写方案,TOCO会自动生成一个RPC方法,其参数为写方案对应的BTO,返回值为本次操作的聚合根实体的主键值,内部只实现了对当前聚合的数据库操作
|
||||
- **与聚合的关系:** 每个聚合下可以定义多个写方案,但每个写方案只能操作一个聚合内的表,无法同时操作多个聚合内的表
|
||||
- **如何创建/生成:** 创建写方案时需要先选定对应的聚合,以及要操作的聚合内部的实体,然后确定对每个实体的具体操作类型
|
||||
@@ -1226,6 +1252,64 @@ requestParams为请求参数列表,response为返回结构,requestParams中
|
||||
- **例子:**
|
||||
- 用户登录,在UserFlowService中生成一个函数invokeLoginFlow,该函数通过流程框架根据流程定义调用LoginNode,LoginNode中封装了用户登录的逻辑,LoginFlowContext中封装了用户登录的参数和结果。
|
||||
- **修改建议:** 不修改 service 中的函数, 不修改FlowConfig, 可以修改FlowContext, 添加/修改出入参数, 修改FlowNode中的具体业务逻辑。
|
||||
#### **2.16 领域消息(DomainMessage)**
|
||||
- **定义与用途:** TOCO支持领域消息,通过创建和订阅领域消息,可以监听聚合对象的状态变化(创建、删除、更新),TOCO会自动生成消息的发送逻辑
|
||||
- **关键配置:** 名称(小写字母+下划线), 由于消息是全局可见,所以TOCO限制了名称项目级别不能重复
|
||||
- **与聚合的关系:** 每个聚合下可以定义多个领域消息,每次可以监听聚合下面其中一个实体的状态变更
|
||||
- **如何创建/生成:** 创建领域消息,需要先去顶聚合,然后确定需要监听的实体以及监听的变更类型
|
||||
- **领域消息的定义表达**
|
||||
- 以Json表达,Json Schema 定义如下:
|
||||
```json
|
||||
{
|
||||
"type": "object", "required": ["name","description", "bo", "entity", "action","fields"],
|
||||
"properties": {
|
||||
"name": {"type": "string", "description": "消息名称,单词之间使用下划线分割,总称不超过32个字符,一个模块的领域消息名称不能重复"},
|
||||
"description": {"type": "string", "description": "消息描述,不超过128个字符"},
|
||||
"bo": {"type": "string", "description": "聚合名称"},
|
||||
"entity": {"type": "string", "description": "监听的实体名称,必须归属于聚合"},
|
||||
"fields": {"type": "array", "description": "指定消息里返回的entity的字段对应的字段名称","items": {"type": "string", "description": "字段名称"}},
|
||||
"action": {"type": "string", "description": "监听的变更类型,可选值:create、update、delete"},
|
||||
"delayInSeconds": {"type": "integer", "description": "延迟时间,单位为秒, 如果希望消息延迟消费,则设置该字段, 例如: 订单下单后5分钟未支付,触发一个消息,则可以监听订单的创建状态,并且设置延迟时间5分钟,这样消息会在5分钟后被消费"},
|
||||
"uuid": {"type": "string", "description": "消息的uuid,在更新领域消息时传入,创建的时候不传"}
|
||||
}
|
||||
}
|
||||
```
|
||||
- **代码产物和修改建议**
|
||||
- **发送逻辑** 会在相应聚合的实体中通过hibernate的监听器中实现消息的发生逻辑,**不能修改**
|
||||
- **生成产物:**
|
||||
- **生成:** 在manager层生成一个Java类,封装消息体
|
||||
- **命名规则:** 类名为${domainMessageName}Mo
|
||||
- **职责:** 消息内容载体
|
||||
- **类路径:** ```**.manager.mo```
|
||||
- **唯一标识符位置:** 其对应的标识符在类注解@AutoGenerated中指定, uuid规则: ${DomainMessage在TOCO中的uuid}|DMO|DEFINITION
|
||||
- **例子**
|
||||
用户聚合,包含了user实体, 用户实体有 user_id, user_name 字段,那么创建一个领域消息,名称为user_created,描述为用户创建成功,聚合名称为user,实体名称为user,监听的变更类型为create,返回的字段为user_id,user_name。则会生一个类:<code> UserCreatedMo {Long userId;String userName;}</code>;特别的,对于update的监听,生成消息里会包含字段的前值,命名以old结尾, 例如:<code>UserUpdatedMo {String userName ; String userNameOld;}</code>
|
||||
#### **2.17 订阅消息 (SubscribeMessage)**
|
||||
- **定义与用途:** TOCO支持订阅消息, 订阅消息后生成代码会生成消息消费的模板代码, 后续只需在对应的模板代码里填写业务逻辑,而无需关注如何订阅消息的代码逻辑 **注意**在TOCO中,消息订阅按照模块独立订阅:一个消息可以被多个模块订阅,同一个消息在一个模块中只能订阅一次
|
||||
- **订阅消息的定义表达**
|
||||
- 以Json表达,Json Schema 定义如下: 通过msgId或者msgName指定订阅的消息
|
||||
```json
|
||||
{
|
||||
"type": "object", "required": ["moduleName"],
|
||||
"msgId": {"type": "string", "description": "消息id"},
|
||||
"msgName": {"type": "string", "description": "消息名称"},
|
||||
"moduleName": {"type": "string", "description": "模块名称,指定消息被订阅到哪个模块"}
|
||||
}
|
||||
```
|
||||
- **代码产物和修改建议**
|
||||
- **Consumer类**
|
||||
- **命名规则:** 在指定模块的的service层生成一个Java类,封装了消息的消费类入口
|
||||
- **命名规则:** 类名为${messageName}Consumer
|
||||
- **职责:** 消息的消费类入口
|
||||
- **类路径:** ```**.service.mq.consumer```
|
||||
- **MsgHandler函数**
|
||||
- **命名规则:** 在Consumer类中生成一个函数,封装了消息的消费逻辑的函数入口
|
||||
- **命名规则:** 函数名为handleMessage
|
||||
- **职责:** 消息的消费函数入口,该函数以对应的消息作为参数
|
||||
- **唯一标识符位置:** 其对应的标识符在函数@AutoGenerated中指定, uuid规则: ${模块id}_${DomainMessage在TOCO中的uuid}
|
||||
- **修改建议**
|
||||
- handleMessage函数中实现业务逻辑, 如果消费成功,返回ture; 处理失败返回false, 消息会被多次投递
|
||||
- 如果消息要实现幂等消费,则需要在handleMessage函数上添加@Transactional注解,同时在Consumer类中覆盖父函数 @Override boolean useDBIdempotentWithTransactional(){return true;}, 开启幂等机制
|
||||
|
||||
### **3 生成代码产物补充说明**
|
||||
- **3.1.1 支持的语言/框架**
|
||||
|
||||
Reference in New Issue
Block a user