更新 knowledge.md
This commit is contained in:
422
knowledge.md
422
knowledge.md
@@ -717,78 +717,78 @@ meeting_with_room_and_agenda_wo示例:
|
|||||||
- 禁止使用filter语法
|
- 禁止使用filter语法
|
||||||
- 禁止使用has语法
|
- 禁止使用has语法
|
||||||
- 语法定义:使用 lezer 定义了如下语法
|
- 语法定义:使用 lezer 定义了如下语法
|
||||||
<code>
|
<code>
|
||||||
@top Program { expression? }
|
@top Program { expression? }
|
||||||
@skip { spaces | newline | LineComment }
|
@skip { spaces | newline | LineComment }
|
||||||
@precedence {
|
@precedence {
|
||||||
member,
|
member,
|
||||||
and @left,
|
and @left,
|
||||||
or @left
|
or @left
|
||||||
}
|
}
|
||||||
kw<term> { @specialize[@name={term}]<identifier, term> }
|
kw<term> { @specialize[@name={term}]<identifier, term> }
|
||||||
boolean { @specialize[@name=Boolean]<identifier, "true" | "false"> }
|
boolean { @specialize[@name=Boolean]<identifier, "true" | "false"> }
|
||||||
@skip {} {
|
@skip {} {
|
||||||
String[isolate] {
|
String[isolate] {
|
||||||
'"' (stringContentDouble | Escape)* ('"' | "\n") |
|
'"' (stringContentDouble | Escape)* ('"' | "\n") |
|
||||||
"'" (stringContentSingle | Escape)* ("'" | "\n")
|
"'" (stringContentSingle | Escape)* ("'" | "\n")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
commaSep<content> {
|
commaSep<content> {
|
||||||
content ("," content)*
|
content ("," content)*
|
||||||
}
|
}
|
||||||
List {
|
List {
|
||||||
"[" commaSep<Value> ~destructure "]"
|
"[" commaSep<Value> ~destructure "]"
|
||||||
}
|
}
|
||||||
Value { String | Number | List | boolean }
|
Value { String | Number | List | boolean }
|
||||||
Field { identifier ~arrow }
|
Field { identifier ~arrow }
|
||||||
Member { Field !member "." (Member | Field) }
|
Member { Field !member "." (Member | Field) }
|
||||||
Input { "#" identifier ~arrow }
|
Input { "#" identifier ~arrow }
|
||||||
expression {
|
expression {
|
||||||
TupleExpression | BinaryExpression | NotExpression | ParenthesizedExpression | ListExpression
|
TupleExpression | BinaryExpression | NotExpression | ParenthesizedExpression | ListExpression
|
||||||
}
|
}
|
||||||
TupleExpression { TwoTupleExpression | ThreeTupleExpression }
|
TupleExpression { TwoTupleExpression | ThreeTupleExpression }
|
||||||
TwoTupleExpression { (Field | Member) TwoOperator }
|
TwoTupleExpression { (Field | Member) TwoOperator }
|
||||||
ThreeTupleExpression { (Field | Member) ThreeOperator (Input | Value) }
|
ThreeTupleExpression { (Field | Member) ThreeOperator (Input | Value) }
|
||||||
ListExpression { (Field | Member) ListOperator ParenthesizedExpression }
|
ListExpression { (Field | Member) ListOperator ParenthesizedExpression }
|
||||||
BinaryExpression {
|
BinaryExpression {
|
||||||
expression !and (kw<'AND'> | kw<'and'>) expression |
|
expression !and (kw<'AND'> | kw<'and'>) expression |
|
||||||
expression !or (kw<'OR'> | kw<'or'>) expression
|
expression !or (kw<'OR'> | kw<'or'>) expression
|
||||||
}
|
}
|
||||||
NotExpression { (kw<'NOT'> | kw<'not'>) ParenthesizedExpression }
|
NotExpression { (kw<'NOT'> | kw<'not'>) ParenthesizedExpression }
|
||||||
ParenthesizedExpression { "(" expression ")" }
|
ParenthesizedExpression { "(" expression ")" }
|
||||||
@tokens {
|
@tokens {
|
||||||
spaces[@export] { $[\u0009 \u000b\u00a0\u1680\u2000-\u200a\u202f\u205f\u3000\ufeff]+ }
|
spaces[@export] { $[\u0009 \u000b\u00a0\u1680\u2000-\u200a\u202f\u205f\u3000\ufeff]+ }
|
||||||
newline[@export] { $[\r\n\u2028\u2029] }
|
newline[@export] { $[\r\n\u2028\u2029] }
|
||||||
identifierChar { @asciiLetter | $[_$\u{a1}-\u{10ffff}] }
|
identifierChar { @asciiLetter | $[_$\u{a1}-\u{10ffff}] }
|
||||||
word { identifierChar (identifierChar | @digit)* }
|
word { identifierChar (identifierChar | @digit)* }
|
||||||
identifier { word }
|
identifier { word }
|
||||||
hex { @digit | $[a-fA-F] }
|
hex { @digit | $[a-fA-F] }
|
||||||
stringContentSingle { ![\\\n']+ }
|
stringContentSingle { ![\\\n']+ }
|
||||||
stringContentDouble { ![\\\n"]+ }
|
stringContentDouble { ![\\\n"]+ }
|
||||||
@precedence { spaces, newline, identifier }
|
@precedence { spaces, newline, identifier }
|
||||||
Escape {
|
Escape {
|
||||||
"\\" ("x" hex hex | "u" ("{" hex+ "}" | hex hex hex hex) | ![xu])
|
"\\" ("x" hex hex | "u" ("{" hex+ "}" | hex hex hex hex) | ![xu])
|
||||||
}
|
}
|
||||||
Number {
|
Number {
|
||||||
(@digit ("_" | @digit)* ("." ("_" | @digit)*)? | "." @digit ("_" | @digit)*)
|
(@digit ("_" | @digit)* ("." ("_" | @digit)*)? | "." @digit ("_" | @digit)*)
|
||||||
(("e" | "E") ("+" | "-")? ("_" | @digit)+)? |
|
(("e" | "E") ("+" | "-")? ("_" | @digit)+)? |
|
||||||
@digit ("_" | @digit)* "n" |
|
@digit ("_" | @digit)* "n" |
|
||||||
"0x" (hex | "_")+ "n"? |
|
"0x" (hex | "_")+ "n"? |
|
||||||
"0b" $[01_]+ "n"? |
|
"0b" $[01_]+ "n"? |
|
||||||
"0o" $[0-7_]+ "n"?
|
"0o" $[0-7_]+ "n"?
|
||||||
}
|
}
|
||||||
|
|
||||||
@precedence { Number "." }
|
@precedence { Number "." }
|
||||||
ThreeOperator { "in" | "notIn" | "!=" | "==" | ">" | ">=" | "<" | "<=" | "isNullOrNot" | "like" | "has" }
|
ThreeOperator { "in" | "notIn" | "!=" | "==" | ">" | ">=" | "<" | "<=" | "isNullOrNot" | "like" | "has" }
|
||||||
TwoOperator { "isNull" | "isNotNull" }
|
TwoOperator { "isNull" | "isNotNull" }
|
||||||
ListOperator { "contains" | "filter" }
|
ListOperator { "contains" | "filter" }
|
||||||
LineComment[isolate] { "//" ![\n]* }
|
LineComment[isolate] { "//" ![\n]* }
|
||||||
"(" ")"
|
"(" ")"
|
||||||
"[" "]"
|
"[" "]"
|
||||||
"."
|
"."
|
||||||
}
|
}
|
||||||
@detectDelim
|
@detectDelim
|
||||||
</code>
|
</code>
|
||||||
- **读方案设计元素的表达**
|
- **读方案设计元素的表达**
|
||||||
- 以json格式表达,json schema 定义如下
|
- 以json格式表达,json schema 定义如下
|
||||||
```json
|
```json
|
||||||
@@ -835,73 +835,73 @@ meeting_with_room_and_agenda_wo示例:
|
|||||||
- 上下文
|
- 上下文
|
||||||
<code>
|
<code>
|
||||||
public class meeting_room {
|
public class meeting_room {
|
||||||
storey storey; // 楼层对象
|
storey storey; // 楼层对象
|
||||||
String name; // 名称
|
String name; // 名称
|
||||||
room_type_enum room_type; // 会议室类型
|
room_type_enum room_type; // 会议室类型
|
||||||
List<equipment_enum> equipment; // 设备列表
|
List<equipment_enum> equipment; // 设备列表
|
||||||
Long id; // 主键
|
Long id; // 主键
|
||||||
Long seat_number; // 座位数
|
Long seat_number; // 座位数
|
||||||
String description; // 说明
|
String description; // 说明
|
||||||
Boolean enable_indicator; // 是否启用
|
Boolean enable_indicator; // 是否启用
|
||||||
String input_code; // 输入码
|
String input_code; // 输入码
|
||||||
Long storey_id; // 楼层id
|
Long storey_id; // 楼层id
|
||||||
Long contact_staff_id; // 联系员工ID
|
Long contact_staff_id; // 联系员工ID
|
||||||
List<meeting> meeting; // 会议列表,可添加过滤条件
|
List<meeting> meeting; // 会议列表,可添加过滤条件
|
||||||
class storey { // 楼层
|
class storey { // 楼层
|
||||||
location location; // 位置对象
|
location location; // 位置对象
|
||||||
String name; // 楼层名
|
String name; // 楼层名
|
||||||
Long id; // 主键
|
Long id; // 主键
|
||||||
Long sort_number; // 排序号
|
Long sort_number; // 排序号
|
||||||
Long location_id; // 位置id
|
Long location_id; // 位置id
|
||||||
}
|
}
|
||||||
class meeting {
|
class meeting {
|
||||||
Long id; // 主键
|
Long id; // 主键
|
||||||
String title; // 标题
|
String title; // 标题
|
||||||
Date start_time; // 开始时间
|
Date start_time; // 开始时间
|
||||||
Date end_time; // 结束时间
|
Date end_time; // 结束时间
|
||||||
Long room_id; // 会议室id
|
Long room_id; // 会议室id
|
||||||
String description; // 描述
|
String description; // 描述
|
||||||
Long create_user_id; // 创建人id
|
Long create_user_id; // 创建人id
|
||||||
}
|
}
|
||||||
class location { // 位置,如楼栋
|
class location { // 位置,如楼栋
|
||||||
String name; // 名称
|
String name; // 名称
|
||||||
Long id; // 主键
|
Long id; // 主键
|
||||||
String description; // 描述
|
String description; // 描述
|
||||||
Long sort_number; // 排序号
|
Long sort_number; // 排序号
|
||||||
}
|
}
|
||||||
enum room_type_enum {
|
enum room_type_enum {
|
||||||
SMALL, //小会议室
|
SMALL, //小会议室
|
||||||
MEDIUM, //中会议室
|
MEDIUM, //中会议室
|
||||||
LARGE
|
LARGE
|
||||||
}
|
}
|
||||||
enum equipment_enum {
|
enum equipment_enum {
|
||||||
TV, //电视
|
TV, //电视
|
||||||
WHITEBOARD, //白板
|
WHITEBOARD, //白板
|
||||||
PROJECTOR, //投影仪
|
PROJECTOR, //投影仪
|
||||||
VIDEO
|
VIDEO
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</code>
|
</code>
|
||||||
- 用户需求
|
- 用户需求
|
||||||
获取一段时间未使用的会议室(包含当前会议已选择的会议室),根据会议标题过滤会议信息,分页返回。
|
获取一段时间未使用的会议室(包含当前会议已选择的会议室),根据会议标题过滤会议信息,分页返回。
|
||||||
- 读方案定义
|
- 读方案定义
|
||||||
<code>
|
<code>
|
||||||
{
|
{
|
||||||
"name": "get_unused_meeting_room_list",
|
"name": "get_unused_meeting_room_list",
|
||||||
"description": "获取未使用的会议室(包含当前会议已选择的会议室)",
|
"description": "获取未使用的会议室(包含当前会议已选择的会议室)",
|
||||||
"woId":"759bedd4-4540-4de6-b65d-d44912fb0991",
|
"woId":"759bedd4-4540-4de6-b65d-d44912fb0991",
|
||||||
"dtoOrVoId":"e50c02c3-f336-4dc6-88e1-24ffe3dea1e2",
|
"dtoOrVoId":"e50c02c3-f336-4dc6-88e1-24ffe3dea1e2",
|
||||||
"generateCountApi": true,
|
"generateCountApi": true,
|
||||||
"supportPaginate": true,
|
"supportPaginate": true,
|
||||||
"supportUnPage": false,
|
"supportUnPage": false,
|
||||||
"supportWaterfall": 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": [
|
"filters": [
|
||||||
{
|
{
|
||||||
"fieldPath": "meeting",
|
"fieldPath": "meeting",
|
||||||
"filter": "title like #meetingTitleLike"
|
"filter": "title like #meetingTitleLike"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
</code>
|
</code>
|
||||||
- **代码产物和修改建议**
|
- **代码产物和修改建议**
|
||||||
@@ -912,11 +912,11 @@ meeting_with_room_and_agenda_wo示例:
|
|||||||
* **职责:** 读方案的查询参数结构,可作为API参数,直接透传给RPC调用
|
* **职责:** 读方案的查询参数结构,可作为API参数,直接透传给RPC调用
|
||||||
* **唯一标识符位置:** 类注解@AutoGenerated中指定,uuid规则: ${ReadPlan的uuid}|QTO|DEFINITION
|
* **唯一标识符位置:** 类注解@AutoGenerated中指定,uuid规则: ${ReadPlan的uuid}|QTO|DEFINITION
|
||||||
* **内部结构:** QTO包含分页相关字段:
|
* **内部结构:** QTO包含分页相关字段:
|
||||||
<code>
|
<code>
|
||||||
private String scrollId; //瀑布流游标
|
private String scrollId; //瀑布流游标
|
||||||
private Integer size; //每页记录数
|
private Integer size; //每页记录数
|
||||||
private Integer from; //开始记录位置
|
private Integer from; //开始记录位置
|
||||||
</code>
|
</code>
|
||||||
- **Dao**
|
- **Dao**
|
||||||
- **生成产物:** Dao层生成Java类
|
- **生成产物:** Dao层生成Java类
|
||||||
- **命名规则:** 类名以Dao结尾(${ReadPlanName}Dao)
|
- **命名规则:** 类名以Dao结尾(${ReadPlanName}Dao)
|
||||||
@@ -926,44 +926,44 @@ meeting_with_room_and_agenda_wo示例:
|
|||||||
- **QueryExecutor**
|
- **QueryExecutor**
|
||||||
- **生成条件** 如果一个读方案返回的是VO结构,则会生成QueryExecutor
|
- **生成条件** 如果一个读方案返回的是VO结构,则会生成QueryExecutor
|
||||||
- **生成产物** 返回**VO**的查询方案,在entrance层生成Java类,包含独立函数:
|
- **生成产物** 返回**VO**的查询方案,在entrance层生成Java类,包含独立函数:
|
||||||
- 分页查询: **Paged函数,返回VSQueryResult<XxxVo>
|
- 分页查询: **Paged函数,返回VSQueryResult<XxxVo>
|
||||||
- 不分页全量: query**函数,返回List<XxxVo>
|
- 不分页全量: query**函数,返回List<XxxVo>
|
||||||
- 瀑布流: **Waterfall函数,返回VSQueryResult<XxxVo>
|
- 瀑布流: **Waterfall函数,返回VSQueryResult<XxxVo>
|
||||||
- 查询数量: **Count函数,返回Integer
|
- 查询数量: **Count函数,返回Integer
|
||||||
- **命名规则:** 类名以QueryExecutor结尾(${VoName}QueryExecutor)
|
- **命名规则:** 类名以QueryExecutor结尾(${VoName}QueryExecutor)
|
||||||
- **类路径:** `**.entrance.web.query.executor`包下
|
- **类路径:** `**.entrance.web.query.executor`包下
|
||||||
- **职责:** 提供VO查询入口,将QtoService返回的id数据转化为目标**VO**
|
- **职责:** 提供VO查询入口,将QtoService返回的id数据转化为目标**VO**
|
||||||
- **QueryService**
|
- **QueryService**
|
||||||
- **生成条件** 如果一个读方案返回的是DTO结构,则会生成QueryService
|
- **生成条件** 如果一个读方案返回的是DTO结构,则会生成QueryService
|
||||||
- **生成产物** 返回**DTO**的查询方案,在service层生成Java类,包含独立函数:
|
- **生成产物** 返回**DTO**的查询方案,在service层生成Java类,包含独立函数:
|
||||||
- 分页查询: **Paged函数,返回VSQueryResult<XxxDto>
|
- 分页查询: **Paged函数,返回VSQueryResult<XxxDto>
|
||||||
- 不分页全量: query**函数,返回List<XxxDto>
|
- 不分页全量: query**函数,返回List<XxxDto>
|
||||||
- 瀑布流: **Waterfall函数,返回VSQueryResult<XxxDto>
|
- 瀑布流: **Waterfall函数,返回VSQueryResult<XxxDto>
|
||||||
- 查询数量: **Count函数,返回Integer
|
- 查询数量: **Count函数,返回Integer
|
||||||
- **命名规则:** 类名以QueryService结尾(${DtoName}QueryService)
|
- **命名规则:** 类名以QueryService结尾(${DtoName}QueryService)
|
||||||
- **类路径:** `**.service.index.entity`包下
|
- **类路径:** `**.service.index.entity`包下
|
||||||
- **职责:** 提供DTO查询入口,将QtoService返回的id数据转化为目标**DTO**,或返回符合条件的记录数量
|
- **职责:** 提供DTO查询入口,将QtoService返回的id数据转化为目标**DTO**,或返回符合条件的记录数量
|
||||||
- **VSQueryResult说明:**
|
- **VSQueryResult说明:**
|
||||||
- VSQueryResult是固定类,全路径为com.vs.es.query.VSQueryResult,存在于jar中,直接使用,禁止创建新的VSQueryResult类!
|
- VSQueryResult是固定类,全路径为com.vs.es.query.VSQueryResult,存在于jar中,直接使用,禁止创建新的VSQueryResult类!
|
||||||
- VSQueryResult代码结构:
|
- VSQueryResult代码结构:
|
||||||
<code>
|
<code>
|
||||||
package com.vs.es.query;
|
package com.vs.es.query;
|
||||||
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
public class VSQueryResult<T> { //T为DTO或VO类型
|
public class VSQueryResult<T> { //T为DTO或VO类型
|
||||||
private int count;
|
private int count;
|
||||||
private List<T> result;
|
private List<T> result;
|
||||||
|
|
||||||
private int from; //页码分页起始条数
|
private int from; //页码分页起始条数
|
||||||
private int size; //每页记录数量
|
private int size; //每页记录数量
|
||||||
private String scrollId; //瀑布流查询游标
|
private String scrollId; //瀑布流查询游标
|
||||||
private boolean hasMore; //瀑布流查询是否有下一页
|
private boolean hasMore; //瀑布流查询是否有下一页
|
||||||
}
|
}
|
||||||
</code>
|
</code>
|
||||||
- **例子:**
|
- **例子:**
|
||||||
* 根据用户名称查询用户列表返回UserDTO,生成UserNameQto、UserNameQtoService、UserNameQtoDao、UserNameQueryService; UserNameQueryService调用UserNameQtoService,UserNameQtoService调用UserNameQtoDao
|
* 根据用户名称查询用户列表返回UserDTO,生成UserNameQto、UserNameQtoService、UserNameQtoDao、UserNameQueryService; UserNameQueryService调用UserNameQtoService,UserNameQtoService调用UserNameQtoDao
|
||||||
- **修改建议:**
|
- **修改建议:**
|
||||||
@@ -978,12 +978,12 @@ meeting_with_room_and_agenda_wo示例:
|
|||||||
#### **2.11 写方案 (WritePlan,单聚合操作)**
|
#### **2.11 写方案 (WritePlan,单聚合操作)**
|
||||||
- **定义与用途:** 写方案是数据库写操作的唯一方式,每个写方案只能变更一个聚合的数据。写方案可以一次操作聚合内的多张表。例如:location(父对象/聚合根)和storey(子对象)是1:N关系,可创建写方案同时更新location信息和操作storey列表(新增/删除/修改);也可创建写方案单独更新单个storey对象。
|
- **定义与用途:** 写方案是数据库写操作的唯一方式,每个写方案只能变更一个聚合的数据。写方案可以一次操作聚合内的多张表。例如:location(父对象/聚合根)和storey(子对象)是1:N关系,可创建写方案同时更新location信息和操作storey列表(新增/删除/修改);也可创建写方案单独更新单个storey对象。
|
||||||
- **关键配置:** 名称(小写+下划线,不以write_plan结尾,全局唯一),操作的聚合,聚合内的实体和字段,每个实体的操作类型:
|
- **关键配置:** 名称(小写+下划线,不以write_plan结尾,全局唯一),操作的聚合,聚合内的实体和字段,每个实体的操作类型:
|
||||||
- CREATE:创建单个实体
|
- CREATE:创建单个实体
|
||||||
- UPDATE:更新单个实体
|
- UPDATE:更新单个实体
|
||||||
- DELETE:删除单个实体
|
- DELETE:删除单个实体
|
||||||
- CREATE_ON_DUPLICATE_UPDATE:创建或更新单个实体
|
- CREATE_ON_DUPLICATE_UPDATE:创建或更新单个实体
|
||||||
- FULL_MERGE:批量更新列表数据,执行CREATE_ON_DUPLICATE_UPDATE操作,删除不在传入列表中的旧数据
|
- FULL_MERGE:批量更新列表数据,执行CREATE_ON_DUPLICATE_UPDATE操作,删除不在传入列表中的旧数据
|
||||||
- PARTIAL_MERGE:批量更新列表数据,执行CREATE_ON_DUPLICATE_UPDATE操作
|
- PARTIAL_MERGE:批量更新列表数据,执行CREATE_ON_DUPLICATE_UPDATE操作
|
||||||
- **与RPC关系:** 每个写方案自动生成一个RPC方法,参数为对应BTO,返回值为聚合根实体主键值,只实现当前聚合的数据库操作。
|
- **与RPC关系:** 每个写方案自动生成一个RPC方法,参数为对应BTO,返回值为聚合根实体主键值,只实现当前聚合的数据库操作。
|
||||||
- **生成RPC使用:** 写方案RPC属于所属模块,其他模块需先订阅该RPC,用adapter调用;当前模块直接调用对应Service。
|
- **生成RPC使用:** 写方案RPC属于所属模块,其他模块需先订阅该RPC,用adapter调用;当前模块直接调用对应Service。
|
||||||
- **与聚合关系:** 每个聚合可定义多个写方案,但每个写方案只能操作一个聚合内的表。
|
- **与聚合关系:** 每个聚合可定义多个写方案,但每个写方案只能操作一个聚合内的表。
|
||||||
@@ -997,13 +997,13 @@ meeting_with_room_and_agenda_wo示例:
|
|||||||
- 选定变更对象的字段(每个对象至少选一个字段,无法确定时默认选全部,字段必须明确指定名称)
|
- 选定变更对象的字段(每个对象至少选一个字段,无法确定时默认选全部,字段必须明确指定名称)
|
||||||
- 为每个对象设置操作类型
|
- 为每个对象设置操作类型
|
||||||
- 列表属性可批量操作:
|
- 列表属性可批量操作:
|
||||||
- 批量更新列表(FULL_MERGE/PARTIAL_MERGE):需包含父对象
|
- 批量更新列表(FULL_MERGE/PARTIAL_MERGE):需包含父对象
|
||||||
- 批量删除列表:需包含父对象,父对象设为UPDATE
|
- 批量删除列表:需包含父对象,父对象设为UPDATE
|
||||||
- 批量创建列表:需包含父对象,父对象设为UPDATE或CREATE
|
- 批量创建列表:需包含父对象,父对象设为UPDATE或CREATE
|
||||||
- 示例:meeting对象有List<meeting_agenda> agendaList,批量创建agenda需同时设置meeting和meeting_agenda对象,meeting_agenda设为CREATE;只设置meeting_agenda的CREATE则一次只能创建一条数据
|
- 示例:meeting对象有List<meeting_agenda> agendaList,批量创建agenda需同时设置meeting和meeting_agenda对象,meeting_agenda设为CREATE;只设置meeting_agenda的CREATE则一次只能创建一条数据
|
||||||
- FULL_MERGE和PARTIAL_MERGE只用于列表属性,单值属性用CREATE_ON_DUPLICATE_UPDATE
|
- FULL_MERGE和PARTIAL_MERGE只用于列表属性,单值属性用CREATE_ON_DUPLICATE_UPDATE
|
||||||
- UPDATE/DELETE/CREATE_ON_DUPLICATE_UPDATE/FULL_MERGE/PARTIAL_MERGE操作必须指定一个唯一键(包括主键),用于确定数据记录,主键字段也要包含在fields中
|
- UPDATE/DELETE/CREATE_ON_DUPLICATE_UPDATE/FULL_MERGE/PARTIAL_MERGE操作必须指定一个唯一键(包括主键),用于确定数据记录,主键字段也要包含在fields中
|
||||||
- 唯一键:实体中的唯一索引,由一个或多个字段组成
|
- 唯一键:实体中的唯一索引,由一个或多个字段组成
|
||||||
- 选定对象层级不能跳跃,发生不连续时去掉不连续的叶子对象
|
- 选定对象层级不能跳跃,发生不连续时去掉不连续的叶子对象
|
||||||
- 父对象和子对象操作类型限制:
|
- 父对象和子对象操作类型限制:
|
||||||
- 父对象DELETE:子对象不能选择任何操作
|
- 父对象DELETE:子对象不能选择任何操作
|
||||||
@@ -1012,8 +1012,8 @@ meeting_with_room_and_agenda_wo示例:
|
|||||||
- 父对象FULL_MERGE:子对象只能FULL_MERGE/PARTIAL_MERGE/CREATE
|
- 父对象FULL_MERGE:子对象只能FULL_MERGE/PARTIAL_MERGE/CREATE
|
||||||
- 增量更新字段:Long/BigDecimal/Float/Integer类型字段可设为增量更新,设置在incrFields中,配合UPDATE/CREATE_ON_DUPLICATE_UPDATE/PARTIAL_MERGE/FULL_MERGE使用。例如:A实体count字段设为增量更新,效果是A.count = A.count + ?,传入负值可减值
|
- 增量更新字段:Long/BigDecimal/Float/Integer类型字段可设为增量更新,设置在incrFields中,配合UPDATE/CREATE_ON_DUPLICATE_UPDATE/PARTIAL_MERGE/FULL_MERGE使用。例如:A实体count字段设为增量更新,效果是A.count = A.count + ?,传入负值可减值
|
||||||
- 字段选择:
|
- 字段选择:
|
||||||
- 按需选择字段
|
- 按需选择字段
|
||||||
- 当CREATE操作的时候,如果主键不又入参指定,不勾选主键字段,主键字段的值由框架分配,如果勾选了主键,但是不传递参数值,会造成运行时异常
|
- 当CREATE操作的时候,如果主键不又入参指定,不勾选主键字段,主键字段的值由框架分配,如果勾选了主键,但是不传递参数值,会造成运行时异常
|
||||||
- **写方案设计元素表达:**
|
- **写方案设计元素表达:**
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
@@ -1411,10 +1411,10 @@ class CreateUserBto { //对应实体user
|
|||||||
- **定义与用途:** TOCO支持领域消息,通过创建和订阅领域消息,可以监听聚合对象的状态变化(创建、删除、更新),TOCO会自动生成消息的发送逻辑
|
- **定义与用途:** TOCO支持领域消息,通过创建和订阅领域消息,可以监听聚合对象的状态变化(创建、删除、更新),TOCO会自动生成消息的发送逻辑
|
||||||
- **关键配置:** 名称(小写字母+下划线), 由于消息是全局可见,所以TOCO限制了名称项目级别不能重复; **注意** 在监听状态变化的时候不要命名为某个字段的变更,比如 order_payed, 也不要按照业务语义命名,例如order_payed_complete,因为实际上是order表的任意变更都会受到消息,因此命名为order_changed或order_updated更准确的表达了消息的触发语义; 同理对于create事件,一般命名为 xxx_created;对于delete时间,一般命名为 xxx_deleted
|
- **关键配置:** 名称(小写字母+下划线), 由于消息是全局可见,所以TOCO限制了名称项目级别不能重复; **注意** 在监听状态变化的时候不要命名为某个字段的变更,比如 order_payed, 也不要按照业务语义命名,例如order_payed_complete,因为实际上是order表的任意变更都会受到消息,因此命名为order_changed或order_updated更准确的表达了消息的触发语义; 同理对于create事件,一般命名为 xxx_created;对于delete时间,一般命名为 xxx_deleted
|
||||||
- **与聚合的关系:** 每个聚合下可以定义多个领域消息,每次可以监听聚合下面其中一个实体的状态变更
|
- **与聚合的关系:** 每个聚合下可以定义多个领域消息,每次可以监听聚合下面其中一个实体的状态变更
|
||||||
- **如何创建/生成:** 创建领域消息,需要先去顶聚合,然后确定需要监听的实体以及监听的变更类型
|
- **如何创建/生成:** 创建领域消息,需要先确定聚合,然后确定需要监听的实体以及监听的变更类型
|
||||||
- **最佳实践** 如果只是字段区别,应该修改现有的领域消息,增加相应的字段即可,而不是新创建一个领域消息;
|
- **最佳实践** 如果只是字段区别,应该修改现有的领域消息,增加相应的字段即可,而不是新创建一个领域消息;
|
||||||
- **领域消息的定义表达**
|
- **领域消息的定义表达**
|
||||||
- 以Json表达,Json Schema 定义如下:
|
- 以Json表达,Json Schema 定义如下:
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"type": "object", "required": ["name","description", "bo", "entity", "action","fields"],
|
"type": "object", "required": ["name","description", "bo", "entity", "action","fields"],
|
||||||
@@ -1431,20 +1431,66 @@ class CreateUserBto { //对应实体user
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
- **代码产物和修改建议**
|
- **代码产物和修改建议**
|
||||||
- **发送逻辑** 会在相应聚合的实体中通过hibernate的监听器中实现消息的发生逻辑,**不能修改**
|
- **发送逻辑** 会在相应聚合的实体中通过hibernate的监听器中实现消息的发生逻辑,**不能修改**
|
||||||
- **生成产物:**
|
- **生成产物:**
|
||||||
- **消息载体:** 在manager层生成一个Java类,封装消息体
|
- **消息载体:** 在manager层生成一个Java类,封装消息体
|
||||||
- **命名规则:** 类名为${domainMessageName}Mo
|
- **命名规则:** 类名为${domainMessageName}Mo
|
||||||
- **职责:** 消息内容载体
|
- **职责:** 消息内容载体
|
||||||
- **类路径:** ```**.manager.mo```
|
- **类路径:** ```**.manager.mo```
|
||||||
- **唯一标识符位置:** 其对应的标识符在类注解@AutoGenerated中指定, uuid规则: ${DomainMessage在TOCO中的uuid}|DMO|DEFINITION
|
- **唯一标识符位置:** 其对应的标识符在类注解@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>
|
用户聚合,包含了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.18 普通消息 (CommonMessage)**
|
||||||
|
- **定义与用途:** TOCO支持普通消息,普通消息可以自由定义消息内容。定义消息后,TOCO可以生成消息体,以及消息的发送模板;只需调用这个发送函数,即可发送消息; 消息队列的创建配置等工作TOCO自动完成
|
||||||
|
- **关键配置:** 名称(小写字母+下划线), 由于消息是全局可见,所以TOCO限制了名称项目级别不能重复;
|
||||||
|
- **领域消息的定义表达**
|
||||||
|
- 以Json表达,Json Schema 定义如下:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type":"object",
|
||||||
|
"properties": {
|
||||||
|
"name":{ "type": "string", "description": "消息名称,单词之间使用下划线分割,总称不超过32个字符,一个模块的领域消息名称不能重复"},
|
||||||
|
"description": { "type": "string","description": "消息描述,不超过128个字符"},
|
||||||
|
"uuid":{ "type": "string", "description": "唯一标识符,创建时为空,更新时必填"},
|
||||||
|
"moduleName":{ "type": "string", "description": "所属模块,创建时必填,更新时可空"},
|
||||||
|
"delay": {"type": "boolean","description": "是否是延迟消息,如果是延时消息,在生成的的发送消息函数里添加延迟时间参数"},
|
||||||
|
"transactional": {"type": "boolean","description": "是否是事务消息,如果是事务消息,消息的发送和外层事务保持一致"},
|
||||||
|
"fieldList":{
|
||||||
|
"type":"array", "description": "消息属性列表",
|
||||||
|
"items":{
|
||||||
|
"type": "object",
|
||||||
|
"properties":{
|
||||||
|
"name": { "type": "string","description": "字段名称,英文下划线分割,不超过32字符"},
|
||||||
|
"uuid":{ "type": "string","description": "字段类型为Enum或Eo时必填,对应的Enum或Eo标识符" } ,
|
||||||
|
"type":{ "type": "string","description": "字段类型:String,Integer,Long,Float,Double,Boolean,Date,Eo,Enum,BigDecimal,List" },
|
||||||
|
"innerUuid":{"type": "string", "description": "当innerType为Eo或Enum时,对应的标识符"},
|
||||||
|
"innerType": { "type": "string", "description": "List元素类型:String,Integer,Long,Float,Double,Boolean,Date,Eo,Enum,BigDecimal"}
|
||||||
|
},
|
||||||
|
"required":[ "name","type"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required":["name","description"]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
**代码产物和修改建议**
|
||||||
|
- **消息载体类:** 在manager层生成一个Java类,封装消息体
|
||||||
|
- **命名规则:** 类名为${commonMessageName}Mo
|
||||||
|
- **职责:** 消息内容载体
|
||||||
|
- **类路径:** ```**.manager.mo```
|
||||||
|
- **唯一标识符位置:** 其对应的标识符在类注解@AutoGenerated中指定, uuid规则: ${CommonMessage在TOCO中的uuid}|MO|DEFINITION
|
||||||
|
- **发送类** 在service层生成一个Java类,封装消息发送逻辑
|
||||||
|
- **命名规则:** 类型名为 ${commonMessageName}MoMessageSender
|
||||||
|
- **职责** 封装发送函数,作为消息发送的入口
|
||||||
|
- **类路径:** ```**.service.mq.producer```
|
||||||
|
- **唯一标识符位置:** 其对应的标识符在类注解@AutoGenerated中指定, uuid规则: ${CommonMessage在TOCO中的uuid}|MO|PRODUCER
|
||||||
|
- **例子**
|
||||||
|
- 在用户注册成功后,需要给用户发一封邮件,可以定义个普通消息,名称为:user_registered, 包含了字段: long user_id, String nick_name, 生成代码后,会生成 类:<code> UserRegisteredMo {Long userId;String nickName;}</code> 和一个发送类 <code>UserRegisteredMoMessageSender {void send(UserRegisteredMo message);}</code>
|
||||||
|
|
||||||
#### **2.18 订阅消息 (SubscribeMessage)**
|
#### **2.19 订阅消息 (SubscribeMessage)**
|
||||||
- **定义与用途:** TOCO支持订阅消息, 订阅消息后生成代码会生成消息消费的模板代码, 后续只需在对应的模板代码里填写业务逻辑,而无需关注如何订阅消息的代码逻辑 **注意**在TOCO中,消息订阅按照模块独立订阅:一个消息可以被多个模块订阅,同一个消息在一个模块中只能订阅一次
|
- **定义与用途:** TOCO支持订阅消息(普通消息和领域消息), 订阅消息后生成代码会生成消息消费的模板代码, 后续只需在对应的模板代码里填写业务逻辑,而无需关注如何订阅消息的代码逻辑 **注意**在TOCO中,消息订阅按照模块独立订阅:一个消息可以被多个模块订阅,同一个消息在一个模块中只能订阅一次
|
||||||
- **订阅消息的定义表达**
|
- **订阅消息的定义表达**
|
||||||
- 以Json表达,Json Schema 定义如下: 通过msgId或者msgName指定订阅的消息
|
- 以Json表达,Json Schema 定义如下: 通过msgId或者msgName指定订阅的消息
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"type": "object", "required": ["moduleName"],
|
"type": "object", "required": ["moduleName"],
|
||||||
@@ -1455,18 +1501,18 @@ class CreateUserBto { //对应实体user
|
|||||||
```
|
```
|
||||||
- **代码产物和修改建议**
|
- **代码产物和修改建议**
|
||||||
- **Consumer类**
|
- **Consumer类**
|
||||||
- **命名规则:** 在指定模块的的service层生成一个Java类,封装了消息的消费类入口
|
- **命名规则:** 在指定模块的的service层生成一个Java类,封装了消息的消费类入口
|
||||||
- **命名规则:** 类名为${messageName}Consumer
|
- **命名规则:** 类名为${messageName}Consumer
|
||||||
- **职责:** 消息的消费类入口
|
- **职责:** 消息的消费类入口
|
||||||
- **类路径:** ```**.service.mq.consumer```
|
- **类路径:** ```**.service.mq.consumer```
|
||||||
- **MsgHandler函数**
|
- **MsgHandler函数**
|
||||||
- **命名规则:** 在Consumer类中生成一个函数,封装了消息的消费逻辑的函数入口
|
- **命名规则:** 在Consumer类中生成一个函数,封装了消息的消费逻辑的函数入口
|
||||||
- **命名规则:** 函数名为handleMessage
|
- **命名规则:** 函数名为handleMessage
|
||||||
- **职责:** 消息的消费函数入口,该函数以对应的消息作为参数
|
- **职责:** 消息的消费函数入口,该函数以对应的消息作为参数
|
||||||
- **唯一标识符位置:** 其对应的标识符在函数@AutoGenerated中指定, uuid规则: ${模块id}_${DomainMessage在TOCO中的uuid}
|
- **唯一标识符位置:** 其对应的标识符在函数@AutoGenerated中指定, uuid规则: ${模块id}_${DomainMessage在TOCO中的uuid}
|
||||||
- **修改建议**
|
- **修改建议**
|
||||||
- handleMessage函数中实现业务逻辑, 如果消费成功,返回ture; 处理失败返回false, 消息会被多次投递
|
- handleMessage函数中实现业务逻辑, 如果消费成功,返回ture; 处理失败返回false, 消息会被多次投递
|
||||||
- 如果消息要实现幂等消费,则需要在handleMessage函数上添加@Transactional注解,同时在Consumer类中覆盖父函数 @Override boolean useDBIdempotentWithTransactional(){return true;}, 开启幂等机制; 对于监听实体状态变化的事件,因为实体的任何字段变更都会触发事件,因此在消费消息的时候有些情况需要根据具体的字段值进行过滤, 例如:监听订单支付状态, 创建的消息叫做 order_changed_mo,包含了status字段,那么仅当status_older!=payed && status=payed 为true的时候表示是订单支付完成的消息。
|
- 如果消息要实现幂等消费,则需要在handleMessage函数上添加@Transactional注解,同时在Consumer类中覆盖父函数 @Override boolean useDBIdempotentWithTransactional(){return true;}, 开启幂等机制; 对于监听实体状态变化的事件,因为实体的任何字段变更都会触发事件,因此在消费消息的时候有些情况需要根据具体的字段值进行过滤, 例如:监听订单支付状态, 创建的消息叫做 order_changed_mo,包含了status字段,那么仅当status_older!=payed && status=payed 为true的时候表示是订单支付完成的消息。
|
||||||
|
|
||||||
|
|
||||||
### **3 代码生成说明**
|
### **3 代码生成说明**
|
||||||
|
|||||||
Reference in New Issue
Block a user