feat: 简化技术描述语言(2.7)

This commit is contained in:
dayjoy
2025-08-20 10:28:34 +08:00
parent bd1cf2a5a4
commit 9d0ccd1372

View File

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