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

This commit is contained in:
dayjoy
2025-08-20 10:44:36 +08:00
parent 11eb3f3b37
commit 1dcb2ce6a0

View File

@@ -671,45 +671,45 @@ meeting_with_room_and_agenda_wo示例
```
#### **2.9 读方案 (ReadPlan)**
- **定义与用途:** 在TOCO中针对DTO和VO读方案描述如何基于查询对象从数据库获取DTO和VO列表数据主要提供三个能力
- 根据查询条件返回符合条件的DTO或VO的id列表
- 根据查询条件返回符合条件的DTO或VO的id数量
- 根据字段过滤条件对DTO和VO的列表字段数据进行过滤
- **定义与用途:** 读方案描述如何从数据库获取DTO和VO列表数据提供三个能力
- 根据查询条件返回符合条件的DTO或VO的id列表
- 根据查询条件返回符合条件的DTO或VO的id数量
- 根据字段过滤条件对DTO和VO的列表字段数据进行过滤
- **注意** 不派生自DTO的VO不能创建读方案
- 读方案能力边界
- 读方案的的查询能力是sql的简化实现了部分sql的查询能力支持sql的 count、exists、left join, not、in、like、between、and、or、not 以及 ( ) , 以及排序 order by, 以及分页 limit offset 不能使用group by having 语法,不支持使用其他函数
- 在使用exists 和 left join的时候只能通过外键扩展其中如果外键是1N的时候只能使用exist 如果是11的时候则可以使用 left join 和 exists.
例如: 实体t 和他t1 t1存在列t_id为指向t的外键如果外键关系为1N则 select * from t where exists (select * from t1 where t.id=t1.id) 合法; elect * from t left join t1 on t.id=t1.id 非法
- 在过滤字段的选择上在使用exists的时候在条件中只能选择后表的字段 在使用 left join的时候则可以使用前表和后表字段
例如: 实体 t (id,name) ,实体 t1 (id,name,t_id) t1存在列t_id为指向t的外键, select * from t where exists (select * from t1 where t.id=t1.id and t.name like ) 非法, select * from t left join t1 on t.id=t1.id and t1.name like 合法
select * from t left join t1 on t.id=t1.id where t.name like 合法, select * from t left join t1 on t.id=t1.id where t1.name like 合法
- 在选择使用度方案的时候**必须**符合读方案能力边界,如果需求在读方案能力之外,必须使用 ** [2.16 自定义查询] ** 实现功能
- **特别注意** 对于根据主键获取单个或批量获取DTO或者VO并且不需要对DTO、VO的列表字段数据进行过滤的需求,必须使用**DTO自动生成的预定义方法**,禁止使用读方案
- **关键配置:** 名称(小写字母+下划线,不以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完全实现
- **生成的读方案RPC** 的使用: 读方案生成的RPC属于该写方案所属的模块,其他模块如果需要调用该读方案,需要先订阅该写方案RPC在生成代码后使用adapter调用该读服务;当前模块则可以直接调用该读方案对应Service
- **如何创建/生成:** 在创建读方案时,必须先调用工具创建或选择现有的**一种**DTO或VO作为返回值类型然后定义查询条件自然语言描述(如:根据用户姓名、年龄、学校名称,查询用户列表),分页方式、是否生成计数方法、排序字段等
- **排序** 对于返回列表数据的排序排序规则和SQL的排序类似(通过指定字段以及升序|降序字段可以组合ReadPlan支持有**两种**方式:
- **默认排序**:指定默认排序字段不需入参指定排序字段
- **自定义排序**:指定排序字段(需要入参指定排序字段),这种方式通常能很好的满足列表头动态指定排序需求
- **排序字段来源**并不是查询字段中的所有字段都能作为排序字段因为返回的根DTO|VO的id去重列表所以排序字段只能来源自根WO不包括列表WO属性: 比如MeetingWO包含了List<MeetingAgendaWO> agendaList属性 那么agendaList不能作为排序字段以及根WO扩展出的非列表属性的WO字段比如MeetingWO包含了MeetingRoomWO,那么MeetingRoomWO的属性也能作为排序字段**注意**:为了唯一确定是给按照那个字段进行排序,排序字段是一个从根节点到当前属性的路径例如ADto包含BDto bDtoBDto具备属性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 这些操作符
- isNullOrNot操作符是一个三元操作符,用于根据入参判断过滤某值是否为null。举例user对象有gender字段如果定义了语句 gender isNullOrNot #genderIsNullOrNot, 如果入参 genderIsNullOrNot为true等价于条件 where gender is null 如果入参#genderIsNullOrNot为false等价于 where gender is not null
- 对于对象属性或列表对象属性可以用isNull, isNotNull, isNullOrNot 这些操作符。注意本对象不能直接用这个操作符
- 对于对象属性还可以对其子属性进行上述查询
- **读方案能力边界**
- 支持sql的count、exists、left joinnot、in、like、between、and、or、括号、order bylimitoffset
- 不支持group by、having语法不支持其他函数
- 1:N外键关系只能使用exists1:1外键关系可使用left join和exists
例如实体t和t1t1有t_id外键指向t若为1:N关系则`select * from t where exists (select * from t1 where t.id=t1.id)`合法,`select * from t left join t1 on t.id=t1.id`非法
- exists只能选择后表字段left join可使用前表和后表字段
例如实体t(id,name)实体t1(id,name,t_id)`select * from t where exists (select * from t1 where t.id=t1.id and t.name like ?)`非法,`select * from t left join t1 on t.id=t1.id and t1.name like ?`合法
- 需求超出读方案能力,必须使用自定义查询
- **特别注意** 根据主键获取单个或批量DTO/VO且不需过滤列表字段数据的需求必须使用DTO自动生成的预定义方法禁止使用读方案
- **关键配置:** 名称(小写字母+下划线不以read_plan结尾全局唯一)、返回结构(DTO/VO一个读方案只能返回一种类型)、查询条件自然语言描述、是否生成计数方法、排序字段、过滤字段及过滤条件
- **与RPC、代码的关系:** 返回DTO的读方案自动生成RPC方法参数为QTO返回值为DTO列表返回VO的读方案自动生成Java方法参数为QTO返回值为VO列表方法内部逻辑由TOCO完全实现
- **生成的读方案RPC使用:** 读方案RPC属于对应模块,其他模块需先订阅该RPC用adapter调用当前模块直接调用对应Service
- **创建方式:** 先创建或选择一种DTO或VO作为返回值类型然后定义查询条件自然语言描述分页方式、是否生成计数方法、排序字段等
- **排序** 支持两种方式:
- **默认排序**:指定默认排序字段(不需入参指定排序字段)
- **自定义排序**:指定排序字段(需入参指定排序字段)满足列表头动态排序需求
- **排序字段来源**只能来源自根WO(不包括列表WO属性)以及根WO扩展出的非列表属性的WO字段。排序字段是从根节点到当前属性的路径例如ADto包含BDto bDtoBDto有name属性排序字段路径为bDto.name
- 提取需求中的查询信息,以输入的查询对象作为上下文,构建查询语句
- 如有列表属性过滤需求提取需求中的过滤信息,针对可过滤字段(列表属性)分别创建过滤条件过滤条件不能使用列表属性作为查询条件属性(不能使用contains语法)。属性字段是从根节点属性到当前属性的路径
- **查询语法和过滤语法**
- 基本语法格式属性名 操作符 变量或常量,变量格式为#变量名;枚举类型常量用单引号包围,例如:'MALE'
- 数值、时间类型属性操作符!=, ==, >, <, <=, >=, in, notIn, isNullOrNot
- 文本类型属性操作符like, isNotNull, isNull, !=, ==, in, notIn, isNullOrNot
- isNullOrNot三元操作符根据入参判断过滤某值是否为null。例如:gender isNullOrNot #genderIsNullOrNot,若入参为true等价于where gender is null,若为false等价于where gender is not null
- 对象属性或列表对象属性操作符:isNull, isNotNull, isNullOrNot本对象不能直接用操作符
- 对象属性对其子属性进行上述查询
- 查询变量不能作为条件属性
- 查询条件间可以使用AND, OR 进行连接
- 可插入括号()对条件进行分组
- 对于列表对象属性只能使用contains、isNull、isNotNull操作符如果是wo列表类型可以使用contains(子查询, 子查询的条件属性只能作用在对应的列表类型对象的属性上),表示了该列表属性中需包含至少一个满足子查询条件的对象; 其他列表类型只能使用isNull或isNotNull
- 把上述子查询通过and, or, not这三个连接符拼装在一起就可以完成一个查询。不要使用没提示过的操作符号连接符
- 查询条件中的入参可在运行时传入或不传入值,如果不传入值表示该参数相关条件不起作用;基于这种查询语句实际运行时的动态效果,多条件联合查询的时候可以优先使用AND
- 使用点号(.)可以访问当前对象的单值对象类型子属性, 可以多个点号组合访问嵌套单值对象属性
- 查询条件中的属性必须是当前查询对象的属性或单值对象属性或单值对象子属性
- 查询条件间可用AND, OR连接
- 可插入括号()对条件分组
- 列表对象属性只能使用contains、isNull、isNotNull操作符wo列表类型可用contains(子查询),表示列表属性中需包含至少一个满足子查询条件的对象其他列表类型只能用isNull或isNotNull
- 通过and, or, not连接符拼装子查询完成查询。不要使用未提及的操作符号连接符
- 查询条件中的入参可在运行时传入或不传入值,不传入值表示该参数相关条件不起作用;基于这种动态效果,多条件联合查询优先使用AND
- 使用点号(.)访问当前对象的单值对象类型子属性,可多个点号组合访问嵌套单值对象属性
- 查询条件中的属性必须是当前查询对象的属性或单值对象属性或单值对象子属性
- 禁止使用filter语法
- 禁止使用has语法
- 语法定义:使用 lezer 定义了如下语法
@@ -831,10 +831,10 @@ meeting_with_room_and_agenda_wo示例
- 上下文
<code>
public class meeting_room {
storey storey; // 楼层id
storey storey; // 楼层对象
String name; // 名称
room_type_enum room_type; // 会议室类型
List<equipment_enum> equipment; // 设备
List<equipment_enum> equipment; // 设备列表
Long id; // 主键
Long seat_number; // 座位数
String description; // 说明
@@ -842,10 +842,10 @@ meeting_with_room_and_agenda_wo示例
String input_code; // 输入码
Long storey_id; // 楼层id
Long contact_staff_id; // 联系员工ID
List<meeting> meeting; //注意:该字段可添加过滤条件
class storey { // 建筑的楼层
location location; // 位置id
String name; // 楼层
List<meeting> meeting; // 会议列表,可添加过滤条件
class storey { // 楼层
location location; // 位置对象
String name; // 楼层
Long id; // 主键
Long sort_number; // 排序号
Long location_id; // 位置id
@@ -859,18 +859,18 @@ meeting_with_room_and_agenda_wo示例
String description; // 描述
Long create_user_id; // 创建人id
}
class location { // 位置信息,如楼栋
class location { // 位置,如楼栋
String name; // 名称
Long id; // 主键
String description; // 描述
Long sort_number; // 排序号
}
enum room_type_enum { //
enum room_type_enum {
SMALL, //小会议室
MEDIUM, //中会议室
LARGE
}
enum equipment_enum { //
enum equipment_enum {
TV, //电视
WHITEBOARD, //白板
PROJECTOR, //投影仪
@@ -879,8 +879,8 @@ meeting_with_room_and_agenda_wo示例
}
</code>
- 用户需求
获取一段时间未使用的会议室(包含当前会议已选择的会议室),并且根据会议名称会议信息,最后分页返回。
- 对应的读方案定义
获取一段时间未使用的会议室(包含当前会议已选择的会议室)根据会议标题过滤会议信息,分页返回。
- 读方案定义
<code>
{
"name": "get_unused_meeting_room_list",
@@ -901,45 +901,45 @@ meeting_with_room_and_agenda_wo示例
}
</code>
- **代码产物和修改建议**
- **查询传输对象(QTO)**
* **生成产物:** Service层生成一个Java类
- **QTO查询参数对象**
* **生成产物:** Service层生成Java类
* **命名规则:** 类名以Qto结尾(${ReadPlanName}Qto)
* **类路径:** 位于<code>**.persist.qto</code>包路径
* **职责:** 读方案的查询参数结构,通常可作为API参数,API接收到参数后可直接透传给内部的RPC进行调用
* **唯一标识符位置:** 其对应的唯一标志在类注解@AutoGenerated中指定,uuid规则: ${ReadPlan在TOCO中的uuid}|QTO|DEFINITION
* **内部结构:** QTO内部包含了如下分页相关字段:
* **类路径:** `**.persist.qto`包
* **职责:** 读方案的查询参数结构可作为API参数直接透传给RPC调用
* **唯一标识符位置:** 类注解@AutoGenerated中指定uuid规则: ${ReadPlan的uuid}|QTO|DEFINITION
* **内部结构:** QTO包含分页相关字段:
<code>
private String scrollId; //瀑布流分页时用作游标
private Integer size; //页码分页时用作每页记录的数量
private Integer from; //页码分页时用作开始记录位置
private String scrollId; //瀑布流游标
private Integer size; //每页记录
private Integer from; //开始记录位置
</code>
- **Dao**
- **生成产物:** Dao层生成一个Java类
- **生成产物:** Dao层生成Java类
- **命名规则:** 类名以Dao结尾(${ReadPlanName}Dao)
- **类路径:** 位于 <code>**.persist.mapper</code>包路径
- **类路径:** `**.persist.mapper`包
- **职责:** 读方案对应的数据库查询方法
- **唯一标识符位置:** 其对应的唯一标志在类注解@AutoGenerated中指定,uuid规则: ${ReadPlan在TOCO中的uuid}|QTO|DAO
- **唯一标识符位置:** 类注解@AutoGenerated中指定uuid规则: ${ReadPlan的uuid}|QTO|DAO
- **QueryExecutor**
- **生成产物** 对于返回**VO**的查询方案在controller层生成一个Java类,对于分页查询、不分页返回全量、瀑布流查询、计数都有后独立函数实现
- 分页查询 会生成一个 **Paged函数, 返回VSQueryResult<XxxVo>
- 不分页返回全量, 会生成一个query**函数返回List<XxxVo>
- 瀑布流,会生成一个,**Waterfall函数返回VSQueryResult<XxxVo>
- 查询数量 会生成一个 **Count函数返回Integer
- **生成产物** 返回**VO**的查询方案在controller层生成Java类,包含独立函数:
- 分页查询: **Paged函数返回VSQueryResult<XxxVo>
- 不分页全量: query**函数返回List<XxxVo>
- 瀑布流: **Waterfall函数返回VSQueryResult<XxxVo>
- 查询数量: **Count函数返回Integer
- **命名规则:** 类名以QueryExecutor结尾(${VoName}QueryExecutor)
- **类路径:** 位于<code>**.entrance.web.query.executor</code>包路径
- **职责:** 提供针对VO查询入口,QtoService返回的id数据转化目标**VO**
- **类路径:** `**.entrance.web.query.executor`包
- **职责:** 提供VO查询入口QtoService返回的id数据转化目标**VO**
- **QueryService**
- **生成产物** 对于返回**DTO**的查询方案在service层生成一个Java类,对于分页查询、不分页返回全量、瀑布流查询、计数都有后独立函数实现
- 分页查询 会生成一个 **Paged函数, 返回VSQueryResult<XxxDto>
- 不分页返回全量, 会生成一个query**函数, 返回List<XxxDto>
- 瀑布流,会生成一个,**Waterfall函数, 返回VSQueryResult<XxxDto>
- 查询数量 会生成一个 **Count函数,返回Integer
- **生成产物** 返回**DTO**的查询方案在service层生成Java类,包含独立函数:
- 分页查询: **Paged函数返回VSQueryResult<XxxDto>
- 不分页全量: query**函数返回List<XxxDto>
- 瀑布流: **Waterfall函数返回VSQueryResult<XxxDto>
- 查询数量: **Count函数返回Integer
- **命名规则:** 类名以QueryService结尾(${DtoName}QueryService)
- **类路径:** 位于<code>**.service.index.entity</code>包路径
- **职责:** 提供DTO查询入口QtoService返回的id数据转化目标**DTO**,或返回符合条件的记录数量
- **VSQueryResult说明**
- VSQueryResult为一个写死的全路径为com.vs.es.query.VSQueryResult存在于jar中可以直接使用,禁止创建新的VSQueryResult类!
- VSQueryResult代码结构如下:
- **类路径:** `**.service.index.entity`包
- **职责:** 提供DTO查询入口QtoService返回的id数据转化目标**DTO**,或返回符合条件的记录数量
- **VSQueryResult说明:**
- VSQueryResult是固定全路径为com.vs.es.query.VSQueryResult存在于jar中直接使用禁止创建新的VSQueryResult类!
- VSQueryResult代码结构:
<code>
package com.vs.es.query;
@@ -952,16 +952,16 @@ meeting_with_room_and_agenda_wo示例
private int count;
private List<T> result;
private int from; //页码分页起始条数
private int size; //每页记录数量
private int from; //页码分页起始条数
private int size; //每页记录数量
private String scrollId; //瀑布流查询游标
private boolean hasMore; //瀑布流查询时表示是否有下一页
private boolean hasMore; //瀑布流查询是否有下一页
}
</code>
- **例子:**
* 根据用户名称查询用户列表返回UserDTO生成UserNameQto、UserNameQtoService、UserNameQtoDao、UserNameQueryService; UserNameQueryService调用UserNameQtoServiceUserNameQtoService调用UserNameQtoDao
- **修改建议**
- 如果有对结果数据二次处理建议在QueryService和QueryExecutor中进行代码扩展不建议修改QTO文件
* 根据用户名称查询用户列表返回UserDTO生成UserNameQto、UserNameQtoService、UserNameQtoDao、UserNameQueryService; UserNameQueryService调用UserNameQtoServiceUserNameQtoService调用UserNameQtoDao
- **修改建议:**
- 如结果数据二次处理建议在QueryService和QueryExecutor中扩展代码不建议修改QTO文件
#### **2.10 查询传输对象(QTO)**
- **定义与用途:** 在TOCO中QTO为读方案的查询参数结构每个读方案会对应一个QTO读方案调用方按照QTO的结构向读方案生成的RPC方法传入需要查询的实体字段值完成对数据库的查询