feat: 简化技术描述语言(2.7)
This commit is contained in:
109
knowledge.md
109
knowledge.md
@@ -299,15 +299,15 @@ meeting_with_room_dto无uuid,为待创建DTO。meeting_base_dto和meeting_room
|
||||
- 建议在Service与BaseConverter中扩展代码,不建议修改结构定义文件和Manager文件。DTO的自定义字段不直接派生自Entity,一般对应取数逻辑代码。涉及数据获取、计算和拼装,批量处理性能最好,代码位置必须放在BaseConverter中已自动生成的列表转换方法中,批量取数组装,如UserBaseDtoBaseConverter.convertUserToUserBaseDto(List<User>)或UserDtoBaseConverter.convertUserBaseDtoToUserDto(List<UserBaseDto>)
|
||||
|
||||
#### **2.7 视图对象 (VO)**
|
||||
- **定义与用途:** 在TOCO中,VO表达某个BaseDTO(如果用户指明派生源,也可使用其他DTO)为派生源,通过外键关系不断关联多个BaseDTO的数据结构。VO用于在视图层与前端之间进行数据传输,往往被当做HTTP API的返回值、或读方案的返回值使用,由服务端返回至前端。注意VO不能作为接口的参数,也不能作为RPC的返回值。
|
||||
- **关键配置:** 名称(以Vo结尾,全局唯一)、根Entity、派生源、字段列表。VO中的字段分为三种:a.继承DTO的字段,如果DTO中的字段为基础类型或EO、Enum,则VO中的继承字段和和DTO的字段类型一样,如果DTO中的字段为DTO或List<DTO>类型,则继承字段为字段对应的DTO类型派生出的VO或List<VO>类型;b.扩展字段,含正向替换和反向注入字段,类型为VO或List<VO>;c.自定义字段,类型为基本类型或VO类型。VO中的字段来源于DTO,可以根据页面需要将无用字段进行裁剪,可以根据外键关系扩展其他BaseDto(详见**字段扩展方式**)。如果在派生源中没有合适字段,且明确无法通过外键扩展外部BaseDto的情况下,可增加对应的自定义字段
|
||||
- **继承字段类型转换:** VO只和其派生源VO之间有转换关系。当一个VO派生自某个DTO,且该VO继承了派生源DTO中的DTO类型字段(假设为DTO-A),此时VO中对应的继承字段类型必须为VO(假设为VO-A),且**VO-A也必须派生自DTO-A**,如此才能维持VO和DTO之间的转换关系
|
||||
- **与DTO的区别 (在TOCO语境下): **DTO用于服务层传输,通常作为RPC的返回值,与数据模型更近,复用性较强;VO用于视图层传输,通常作为API的返回值,与UI展示更为接近,可裁剪掉不需要的DTO中的冗余字段,复用性较弱。
|
||||
- **如何创建/生成:** VO通常由某个BaseDTO以及外键关系为基础派生,也可以直接创建和DTO无关、内部全为自定义字段的VO(尽量少用,只为应对某些特殊页面,需要组装一组完全无关的返回数据的场景)。
|
||||
- **根VO和子VO:**TOCO中的VO分为两种:1.根VO,指最外层的VO结构,需要经由TOCO创建,有uuid作为唯一标识,根VO可被其他根VO或子VO引用;2.子VO,某个根VO的内部嵌套VO,通过外键关系关联BaseDTO之后由TOCO自动创建,只附属于某个根VO,只能被这一个根VO引用,且没有uuid。所以在TOCO中,我们需要描述VO要的字段、扩展关系,并通过**创建根VO**的行为使TOCO**自动创建**其子VO,或引用其他根VO,即可完成一个复杂嵌套VO的创建过程,无需单独创建子VO。
|
||||
- **字段扩展方式:**同DTO的字段扩展方式,可以通过任意**一个**派生源BaseDTO,即可经过外键扩展成为复杂嵌套VO。只要存在外键关系且满足以下条件即可扩展:对于正向替换:当前实体存在指向其他实体的外键字段;对于反向注入:其他实体存在指向当前实体的外键字段。
|
||||
例如:有两个BaseDTO
|
||||
<code>
|
||||
- **定义与用途:** VO基于BaseDTO(或指定的其他DTO)派生,通过外键关系关联多个BaseDTO的数据结构。用于视图层与前端数据传输,作为HTTP API返回值或读方案返回值。不能作为接口参数或RPC返回值。
|
||||
- **关键配置:** 名称(以Vo结尾,全局唯一)、根Entity、派生源、字段列表。字段分三种:a.继承DTO字段(基础类型/EO/Enum保持类型不变,DTO类型转为对应VO类型);b.扩展字段(正向替换和反向注入,类型为VO或List<VO>);c.自定义字段(基本类型或VO类型)。可裁剪无用字段,可通过外键关系扩展其他BaseDto。无法扩展时可增加自定义字段。
|
||||
- **继承字段类型转换:** VO只与派生源VO有转换关系。当VO派生自某DTO,且继承了派生源DTO中的DTO类型字段(假设DTO-A),则VO中对应字段类型必须为VO(假设VO-A),且**VO-A必须派生自DTO-A**,以维持转换关系。
|
||||
- **与DTO的区别:** DTO用于服务层传输,通常作RPC返回值,与数据模型更近,复用性强;VO用于视图层传输,通常作API返回值,与UI展示更近,可裁剪冗余字段,复用性弱。
|
||||
- **创建方式:** VO通常基于某个BaseDTO及外键关系派生,也可直接创建与DTO无关的全自定义字段VO(尽量少用,仅用于特殊页面组装无关数据的场景)。
|
||||
- **根VO和子VO:** VO分两种:1.根VO:最外层VO结构,需通过TOCO创建,有uuid唯一标识,可被其他根VO或子VO引用;2.子VO:根VO内部嵌套VO,通过外键关系关联BaseDTO后自动创建,仅附属于一个根VO,无uuid。需描述VO字段和扩展关系,通过**创建根VO**使TOCO**自动创建**子VO或引用其他根VO。
|
||||
- **字段扩展方式:** 同DTO字段扩展方式,通过任意**一个**派生源BaseDTO,经外键扩展成复杂嵌套VO。满足条件即可扩展:正向替换(当前实体有指向其他实体的外键字段);反向注入(其他实体有指向当前实体的外键字段)。
|
||||
例如:两个BaseDTO
|
||||
```
|
||||
MeetingRoomBaseDto{ //会议室
|
||||
Long id;// 会议室id,主键
|
||||
String name;// 会议室名称
|
||||
@@ -320,39 +320,39 @@ meeting_with_room_dto无uuid,为待创建DTO。meeting_base_dto和meeting_room
|
||||
DateTime startTime; //会议开始时间
|
||||
DateTime endTime; //会议结束时间
|
||||
}
|
||||
</code>
|
||||
其中Meeting和MeetingRoom是n:1关系。即多个会议室会占用同一个会议室。
|
||||
通过“正向替换”这个行为,可以组装出如下VO:
|
||||
<code>
|
||||
MeetingWithRoomVo { //根VO,需要通过TOCO创建
|
||||
```
|
||||
Meeting和MeetingRoom是n:1关系。多个会议占用同一个会议室。
|
||||
通过"正向替换"组装出:
|
||||
```
|
||||
MeetingWithRoomVo { //根VO,需通过TOCO创建
|
||||
Long id;// 会议id
|
||||
MeetingRoomVo room { //正向替换该会议用的会议室信息,TOCO自动生成的内部VO,派生自MeetingRoomBaseDto
|
||||
MeetingRoomVo room { //正向替换会议室信息,TOCO自动生成的内部VO,派生自MeetingRoomBaseDto
|
||||
Long id; //会议室ID
|
||||
String name;// 会议室名称
|
||||
}
|
||||
Long backupRoomid; // 不做变化
|
||||
Long backupRoomid; // 不变
|
||||
String title; //会议标题
|
||||
DateTime startTime; //会议开始时间
|
||||
DateTime endTime; //会议结束时间
|
||||
}
|
||||
</code>
|
||||
通过“反向注入”这个行为,可以生成:
|
||||
<code>
|
||||
```
|
||||
通过"反向注入"生成:
|
||||
```
|
||||
MeetingRoomWithMeetingVo{
|
||||
Long id;// 会议室id
|
||||
String name;// 会议室名称
|
||||
List<MeetingVo> meetingList{ //反向注入的用该会议室的会议信息,TOCO自动生成的内部VO,派生自MeetingBaseDto
|
||||
List<MeetingVo> meetingList{ //反向注入使用该会议室的会议信息,TOCO自动生成的内部VO,派生自MeetingBaseDto
|
||||
Long id;// 会议id
|
||||
String title; //会议标题
|
||||
DateTime startTime; //会议开始时间
|
||||
DateTime endTime; //会议结束时间
|
||||
}
|
||||
}
|
||||
</code>
|
||||
- **TOCO中VO的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
|
||||
<code>
|
||||
```
|
||||
- **JSON结构描述:** VO用json结构表示,用于理解含义或作为创建、更新VO工具参数。字段含义:expandList为正向替换,reverseExpandList为反向注入,extendFieldList为来自派生源DTO的字段,customFieldList为自定义字段。expandList中,foreignKeyInThisEntity为本表外键字段名,voFieldName为替换后字段名;reverseExpandList中,foreignKeyInOtherEntity为他表外键字段名,voFieldName为注入后字段名;customFieldList中,uuid为自定义字段UUID(创建时不填,更新时需传入定位);typeUuid为类结构UUID(type为List且innerType=Enum、Eo时包含);extendFieldList中,name为继承字段名,若DTO字段非DTO类型则VO字段类型与DTO一致,若DTO字段为DTO或List<DTO>类型,由于VO字段不能为DTO,**必须**将DTO转换为VO,所以extendFieldList会有**vo结构**表示该字段DTO类型派生出的VO定义,注意:该VO字段中的VO类型必须派生自继承的DTO字段类型!
|
||||
|
||||
示例:系统中存在meeting_detail_dto
|
||||
```
|
||||
{
|
||||
"uuid": "cd55c96b-aa67-bfb2-7614-70b503a8f8bf",
|
||||
"name": "meeting_detail_dto",
|
||||
@@ -371,9 +371,9 @@ meeting_with_room_dto无uuid,为待创建DTO。meeting_base_dto和meeting_room
|
||||
}
|
||||
]
|
||||
}
|
||||
</code>
|
||||
- meeting_with_room_vo由meeting_detail_dto派生,并继承了id和create_user字段
|
||||
<code>
|
||||
```
|
||||
- meeting_with_room_vo由meeting_detail_dto派生,继承id和create_user字段
|
||||
```
|
||||
{
|
||||
"vo": {
|
||||
"uuid": null,
|
||||
@@ -484,45 +484,46 @@ meeting_with_room_dto无uuid,为待创建DTO。meeting_base_dto和meeting_room
|
||||
]
|
||||
}
|
||||
}
|
||||
</code>
|
||||
```
|
||||
|
||||
示例中meeting_with_room_vo为根VO,但没有uuid,为待创建的根VO。meeting_room_with_meetings_vo和meeting_base_vo为meeting_with_room_vo的子VO,无法被其他根VO引用,且没有uuid。另外特别注意:metting_with_room_vo的extendFieldList中create_user结构含有一个vo,是因为在派生源meeting_detail_dto中create_user字段为user_base_dto类型,所以在metting_with_room_vo中,该继承字段类型需要变为由user_base_dto派生出的VO结构,注意metting_with_room_vo中的create_user对应的user_base_vo**必须派生自**DTO字段的user_base_dto类型。
|
||||
meeting_with_room_vo为根VO,无uuid,为待创建根VO。meeting_room_with_meetings_vo和meeting_base_vo为meeting_with_room_vo的子VO,无法被其他根VO引用,无uuid。注意:metting_with_room_vo的extendFieldList中create_user结构含vo,因为派生源meeting_detail_dto中create_user字段为user_base_dto类型,所以metting_with_room_vo中该继承字段类型需变为由user_base_dto派生出的VO结构,注意metting_with_room_vo中create_user对应的user_base_vo**必须派生自**DTO字段的user_base_dto类型。
|
||||
|
||||
- **派生源默认使用BaseDTO:** 除非用户指定了VO的派生源DTO,否则创建VO时只可以用**BaseDTO**为派生源。
|
||||
- **与DTO的转换关系:** 在创建一个**有派生源的**VO后,TOCO会在生成代码时自动生成2种convert方法:1.基础convert方法,从DTO转换为VO,仅转换结构以及基本类型字段的get/set,方法命名为convertTo${VoName}、convertTo${VoName}List、convertTo${VoName}Map,其中**Map转换方法**为底层批量方法,通常也是自定义字段逻辑编写的位置(复用性好,会被其他convert方法调用),单个和列表convert方法都通过**调用Map方法**来实现;2.带数据拼装逻辑的convert方法,内部会**自动**调用基础convert方法从DTO转换为VO并设置基本类型字段数据,然后再根据外键**自动**获取**扩展字段**的数据以拼装最终数据,方法命名为convertAndAssembleData、convertAndAssembleDataList,也就是说这两个方法已经**自动**获取了所有**继承字段**和**扩展字段**的数据)。这2种方法对应的代码会生成在VO对应的Converter类中
|
||||
- **字段数据获取:** 对于继承自DTO的字段、以及扩展字段,TOCO会在convert方法中自动生成数据的获取代码,无需手动实现这两种字段的获取和拼装逻辑。对于自定义字段,则**必须**在最底层的convertTo${VoName}Map方法中实现对应的获取和拼装逻辑,以便于其他convert方法都能够**复用**这段逻辑。**禁止**在其他convert方法中实现自定义字段逻辑,因为这样会导致某些场景下数据拼装不完整。
|
||||
- **跨模块依赖:** 如果VO内存在由其他模块DTO派生出的子VO,则需要订阅其他模块的RPC(getBy${PrimaryKey},getBy${PrimaryKey}s,getBy${ForeignKey}s等)方法,用来获取对应的DTO,然后再转换为子VO
|
||||
- **复杂嵌套VO获取流程:** 如果我们需要获取VO,通常有3种方式,且3种方式都可以直接获取到复杂嵌套的VO数据:
|
||||
- 第1种: 先通过预定义方法获取VO的派生源BaseDTO,再通过convertAndAssembleData或convertAndAssembleDataList方法转换成VO
|
||||
- 第2种: 先通过读方案获取VO的派生源BaseDTO,再通过convertAndAssembleData或convertAndAssembleDataList方法转换成VO
|
||||
- 第3种: 通过读方案直接获取VO
|
||||
- **派生源默认使用BaseDTO:** 除非用户指定VO的派生源DTO,否则创建VO时只可用**BaseDTO**为派生源。
|
||||
- **与DTO的转换关系:** 创建**有派生源**的VO后,TOCO生成代码时自动生成2种convert方法:1.基础convert方法,从DTO转换为VO,仅转换结构及基本类型字段的get/set,方法命名为convertTo${VoName}、convertTo${VoName}List、convertTo${VoName}Map,其中**Map转换方法**为底层批量方法,通常是自定义字段逻辑编写位置(复用性好,被其他convert方法调用),单个和列表convert方法都通过**调用Map方法**实现;2.带数据拼装逻辑的convert方法,内部**自动**调用基础convert方法从DTO转换为VO并设置基本类型字段数据,然后根据外键**自动**获取**扩展字段**数据拼装最终数据,方法命名为convertAndAssembleData、convertAndAssembleDataList,即这两个方法已**自动**获取所有**继承字段**和**扩展字段**数据。这2种方法代码生成在VO对应Converter类中。
|
||||
- **字段数据获取:** 继承自DTO的字段及扩展字段,TOCO在convert方法中自动生成数据获取代码,无需手动实现获取和拼装逻辑。自定义字段则**必须**在最底层convertTo${VoName}Map方法中实现对应获取和拼装逻辑,便于其他convert方法**复用**这段逻辑。**禁止**在其他convert方法中实现自定义字段逻辑,会导致某些场景下数据拼装不完整。
|
||||
- **跨模块依赖:** VO内存在由其他模块DTO派生出的子VO,需订阅其他模块RPC(getBy${PrimaryKey},getBy${PrimaryKey}s,getBy${ForeignKey}s等)方法,获取对应DTO,再转换为子VO。
|
||||
- **复杂嵌套VO获取流程:** 获取VO通常有3种方式,3种方式都可直接获取复杂嵌套VO数据:
|
||||
- 第1种:先通过预定义方法获取VO派生源BaseDTO,再通过convertAndAssembleData或convertAndAssembleDataList方法转换成VO
|
||||
- 第2种:先通过读方案获取VO派生源BaseDTO,再通过convertAndAssembleData或convertAndAssembleDataList方法转换成VO
|
||||
- 第3种:通过读方案直接获取VO
|
||||
|
||||
在TOCO中通常使用以下步骤判断使用哪种方式:
|
||||
- a. 如果查询VO的条件为主键或唯一索引的值或列表,则直接使用第1种方式
|
||||
- b. 如果通过其他复杂查询条件,则可以采用第2种或第3种方式
|
||||
- c. 如果用户上下文中有指定的返回DTO的读方案符合条件,则使用第2种,否则使用第3种
|
||||
TOCO中判断使用方式的步骤:
|
||||
- a. 查询VO条件为主键或唯一索引的值或列表,使用第1种方式
|
||||
- b. 通过其他复杂查询条件,可采用第2种或第3种方式
|
||||
- c. 用户上下文中有指定返回DTO的读方案符合条件,使用第2种,否则使用第3种
|
||||
|
||||
注意判断使用哪种方式时,只能根据查询条件判断是否使用读方案,绝对禁止使用返回值是否需要数据拼装来判断!**如果现实中的代码编写方式和TOCO中的步骤有冲突时,只能使用TOCO的定义!**
|
||||
注意判断使用方式时,只能根据查询条件判断是否使用读方案,绝对禁止使用返回值是否需要数据拼装来判断!**现实代码编写方式和TOCO步骤冲突时,只能使用TOCO定义!**
|
||||
|
||||
- **代码产物和修改建议**
|
||||
- **结构定义**
|
||||
* **生成产物:** 在controller层生成一个Java类
|
||||
* **生成产物:** controller层生成Java类
|
||||
* **命名规则:** 类名以Vo结尾
|
||||
* **职责:** 表达VO的数据结构
|
||||
* **类路径:** 位于 <code>**.entrance.web.vo</code> 包路径下
|
||||
* **唯一标识符位置:** 其对应的唯一标志在类注解@AutoGenerated中指定,uuid规则: ${VO在TOCO中的uuid}|VO|DEFINITION
|
||||
* **职责:** 表达VO数据结构
|
||||
* **类路径:** `**.entrance.web.vo` 包路径下
|
||||
* **唯一标识符位置:** 类注解@AutoGenerated中指定,uuid规则:${VO在TOCO中的uuid}|VO|DEFINITION
|
||||
- **Converter**
|
||||
* **生成产物:** 在controller层生成一个Java类(**有派生源**的VO才有Converter)和基类
|
||||
* **生成产物:** controller层生成Java类(**有派生源**的VO才有Converter)和基类
|
||||
* **命名规则:** 实现类名以Converter结尾(${VoName}Converter),基类名以BaseConverter结尾(${VoName}BaseConverter)
|
||||
* **类路径:** 位于 <code>**.entrance.web.converter</code> 包路径下
|
||||
* **职责:** 把DTO转换成VO;Converter中包含2种convert方法:1.基础convert方法,从DTO转换为VO,仅转换结构,方法命名为convertTo${VoName}、convertTo${VoName}List、convertTo${VoName}Map,其中**Map转换方法**为底层批量方法,单个和列表convert方法都通过**调用Map方法**来实现;2.带数据拼装逻辑的convert方法,内部会调用基础convert方法从DTO转换为VO,然后再根据外键获取拼装最终数据,方法命名为convertAndAssembleData、convertAndAssembleDataList)
|
||||
* **类路径:** `**.entrance.web.converter` 包路径下
|
||||
* **职责:** 把DTO转换成VO;Converter含2种convert方法:1.基础convert方法,从DTO转换为VO,仅转换结构,方法命名为convertTo${VoName}、convertTo${VoName}List、convertTo${VoName}Map,其中**Map转换方法**为底层批量方法,单个和列表convert方法都通过**调用Map方法**实现;2.带数据拼装逻辑的convert方法,内部调用基础convert方法从DTO转换为VO,然后根据外键获取拼装最终数据,方法命名为convertAndAssembleData、convertAndAssembleDataList
|
||||
* **唯一标识符位置:**
|
||||
* 实现类Converter其对应的唯一标志在类注解@AutoGenerated中指定,uuid规则: ${VO在TOCO中的uuid}|VO|CONVERTER
|
||||
* 基类BaseConverter其对应的唯一标志在类注解@AutoGenerated中指定,uuid规则: ${DTO在TOCO中的uuid}|DTO|BASE_CONVERTER
|
||||
* 实现类Converter类注解@AutoGenerated中指定,uuid规则:${VO在TOCO中的uuid}|VO|CONVERTER
|
||||
* 基类BaseConverter类注解@AutoGenerated中指定,uuid规则:${DTO在TOCO中的uuid}|DTO|BASE_CONVERTER
|
||||
* **例子:**
|
||||
* 如UserDetailVo、UserDetailVoConverter(包含convertToUserDetailVo、convertToUserDetailVoList、convertToUserDetailVoMap、convertAndAssembleData、convertAndAssembleDataList方法)
|
||||
* 如UserDetailVo、UserDetailVoConverter(含convertToUserDetailVo、convertToUserDetailVoList、convertToUserDetailVoMap、convertAndAssembleData、convertAndAssembleDataList方法)
|
||||
- **修改建议:**
|
||||
- 建议在Converter中进行代码扩展,不建议修改结构定义文件。其中VO的**自定义字段**由于不直接派生自DTO,所以一般会对应取数逻辑代码。通常如果涉及到数据获取、计算和拼装,批量处理的性能最好,所以自定义字段对应的代码位置**必须**放在Converter的**Map**基础转换方法convertTo${VoName}Map中,批量取数组装,如UserVoConverter.convertToUserVoMap
|
||||
- 建议在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的字段名保持一致),例如:
|
||||
|
||||
Reference in New Issue
Block a user