diff --git a/knowledge.md b/knowledge.md index 175abfe..2f06918 100644 --- a/knowledge.md +++ b/knowledge.md @@ -1404,73 +1404,84 @@ class CreateUserBto { //对应实体user - 自定义查询时框架不自动生成代码(需要手动编写全部代码) - 各层代码位置严格遵守**3.2 项目结构与导航**,必须有mapper层、service层、DO对象、DTO对象,API返回数据必须有VO对象 -### **3 生成代码产物补充说明** -- **3.1 支持的语言/框架** - Java、SpringBoot、MyBatis-plus(读)、Hibernate(写) -- **3.2 项目结构与导航** - TOCO生成的项目是一个多模块的SpringBoot项目,包括主模块和子模块,它的子模块位于根目录下的/modules目录中,**注意** 获取子模块代码文件路径的时候需要从根目录开始,所以必须从modules节点开始,例如:子模块module1下的的java类路径应该是`modules/module1/src/main/java...` - - |──main_module - │ ├── common # 项目级别公共的基础模块 - │ │ ├──config/ # 中间件配置 - │ │ ├──constants/ # 项目级别常量 - │ │ ├──enums/ # 项目级别枚举 - │ │ ├──redis/ # Redis配置 - │ │ ├──response/ # 返回结果封装 - │ │ └──utils/ # 项目级别utils - └── └── entrance/ # 项目入口 - │ └──AppApplication.java # 项目启动类 - └── modules # 子模块列表 - └── module1/ # 子模块1 - ├── common/ - │ ├──constants/ # 模块常量 - │ ├──utils/ #模块级别utils - │ └──enums/ # 枚举 - ├── entrance/web/src/main/java/com/{project_name}/{module_name}/entrance/web/ - │ ├──controller/ # api 定义 - │ ├──converter/ # 把DTO转化成VO - │ ├──vo/ # VO结构定义 - │ └── query/ # 把读方案返回的数据转成成VO - │ ├── assembler/ # VO的数据填充 - │ ├── collector/ # 读方案返回的id数据展开成完成对象数据 - │ └── executor/ # 调用Service的度方案实现,同时调用collector和assembler,返回最终的VO - ├── manager/src/main/java/com/{project_name}/{module_name}/manager/ - │ ├── bo/ # 聚合对象定义 - │ │ └── base/ # 聚合对象的基类 - │ ├── dto/ # 数据传输对象定义 - │ ├── converter/ # 复杂Dto(非BaseDto)组装 - │ ├── facade/ # 调用其他模块的RPC适配器,包含RpcAdapter,如UserDtoServiceInMeetingRpcAdapter,表示从meeting模块调用user模块中方法 - │ └── impl/ # Dto的查询接口的实现 - ├── persist/src/main/java/com/{project_name}/{module_name}/persist/ - │ ├── eo/ # 值对象(Eo)的结构定义 - │ ├── dos/ # 数据库单表结构的映射 - │ ├── qto/ # 读方案的数据库查询实现 - │ └── mapper/ # MyBatis的Mapper定义 - └── service/src/main/java/com/{project_name}/{module_name}/service/ # BOService(包含某个聚合下所有写方案生成的方法)、 DtoService(包含DTO生成的预定义方法) - ├── bto/ # 写方案入参的定义 - ├── converter/ # 对返回的BaseDto按需进行二次扩展 - ├── query/ # 查询方案的service层入口,调用persist层的查询实现 - └── base/ # 每个BOService对应的基类 - 在一个子模块的内,它的依赖层级为entrance -> service -> manager -> persist, 同时各个层都依赖 common - -- **3.3 标准查找流程** +### **3 代码生成说明** + +**3.1 技术栈** +Java、SpringBoot、MyBatis-plus(读取)、Hibernate(写入) + +**3.2 项目结构** +多模块SpringBoot项目,子模块在`/modules`目录下。子模块路径必须从`modules`开始,如:`modules/module1/src/main/java...` + +``` +项目根目录 +├── main_module +│ ├── common/ # 项目级公共模块 +│ │ ├── config/ # 中间件配置 +│ │ ├── constants/ # 项目常量 +│ │ ├── enums/ # 项目枚举 +│ │ ├── redis/ # Redis配置 +│ │ ├── response/ # 返回结果封装 +│ │ └── utils/ # 项目工具类 +│ └── entrance/ # 项目入口 +│ └── AppApplication.java # 启动类 +└── modules/ # 子模块列表 +└── module1/ # 子模块1 +├── common/ +│ ├── constants/ # 模块常量 +│ ├── utils/ # 模块工具类 +│ └── enums/ # 模块枚举 +├── entrance/web/src/main/java/com/{项目名}/{模块名}/entrance/web/ +│ ├── controller/ # API定义 +│ ├── converter/ # DTO转VO +│ ├── vo/ # VO结构 +│ └── query/ # 读方案转VO +│ ├── assembler/ # VO数据填充 +│ ├── collector/ # ID数据展开为完整对象 +│ └── executor/ # 调用Service实现,返回VO +├── manager/src/main/java/com/{项目名}/{模块名}/manager/ +│ ├── bo/ # 聚合对象定义 +│ │ └── base/ # 聚合对象基类 +│ ├── dto/ # DTO定义 +│ ├── converter/ # 复杂DTO组装 +│ ├── facade/ # 跨模块RPC适配器 +│ └── impl/ # DTO查询实现 +├── persist/src/main/java/com/{项目名}/{模块名}/persist/ +│ ├── eo/ # 值对象定义 +│ ├── dos/ # 数据库表映射 +│ ├── qto/ # 读方案数据库查询 +│ └── mapper/ # MyBatis Mapper +└── service/src/main/java/com/{项目名}/{模块名}/service/ +├── bto/ # 写方案入参定义 +├── converter/ # BaseDto扩展 +├── query/ # 查询方案Service入口 +└── base/ # BOService基类 + +模块依赖层级:entrance -> service -> manager -> persist,各层都依赖common +``` + +**3.3 查找路径** 1. **API查找** → `modules/{模块名}/entrance/web/controller/` 2. **DTO查找** → `modules/{模块名}/manager/dto/` 3. **Service查找** → `modules/{模块名}/service/` -4. **数据层查找** → `modules/{模块名}/persist/ -- **3.4 特殊注解及含义** - TOC自动生成的类和方法会带有@AutoGenerated注解,注解中有2个属性:locked为boolean类型,如果locked=true,则代表该文件或方法不建议修改;uuid为String类型,表示该类或方法的唯一标识,如果uuid中包含|字符,则说明该uuid为特殊格式,由不同类型的数据拼装而成(见**[3.2 设计元素到代码的映射规则及修改建议]**中每种设计元素的代码说明)。 -- **3.5 代码严格分层** - Service层方法不能返回VO,不能调用任何Controller层的方法,如VoQueryExecutor、VoConverter等 +4. **数据层查找** → `modules/{模块名}/persist/` + +**3.4 特殊注解** +自动生成的类和方法有`@AutoGenerated`注解: +- `locked`属性:true表示不建议修改 +- `uuid`属性:唯一标识,包含`|`字符表示特殊格式拼装 + +**3.5 分层规则** +Service层方法不能返回VO,不能调用Controller层方法(如VoQueryExecutor、VoConverter等) ### 4. TOCO 最佳实践 -#### 4.1 接口参数类型和返回值选择(Interface Parameter & Return Type Definition): - 在做TOCO接口(API、RPC)设计时,通常会先判断接口的主要功能是读数据库或写数据库,并分析相关的读写方案以及对应的QTO和BTO。如果是读场景,则参数会**优先使**用相关的QTO;如果是写场景,则参数会**优先**使用相关的BTO,如果BTO和QTO无法满足要求,则可以再增加基本类型或Enum、EO等类型参数。参数类型选择时必须遵循以下要求:a.DTO、VO不能作为参数类型;b.QTO、BTO不能作为返回值类型。 - **重要说明:** - - 本章节描述的是参数选择的**优先级原则**,不是绝对限制 - - API参数的绝对限制请参考2.14章节 - - "优先使用QTO/BTO"意思是在满足规范的前提下,根据场景选择最合适的参数类型 -#### 4.2 在处理先读,后更新的场景的时候,为了避免并发引起的数据脏写,应该充分利用BoService的校验功能。 例如:在账户扣钱的情况,为了避免账户在并发扣除余额不足,在用raw sql实现中,我们一般会 通过 update account set balance = balance - amount where user_id = xxx and blance > amount 的 where 条件保护;在toco中, -应该1、在写方案中使用incr字段。 2、 在boService中,添加类似保护代码 if(userBo.getBalance >= 0) 进行保护;或则通过userBo的业务不变性(聚合校验),添加balance>=0的校验 +#### 4.1 接口参数类型和返回值选择: +设计TOCO接口(API、RPC)时,先判断主要功能是读库还是写库,分析对应的读写方案及QTO、BTO。读场景**优先**使用QTO作参数;写场景**优先**使用BTO作参数。QTO、BTO不满足时,可增加基本类型、Enum、EO参数。必须遵循:a.DTO、VO不能作参数;b.QTO、BTO不能作返回值。 +**重要说明:** +- 本节描述参数选择的**优先级原则**,非绝对限制 +- API参数绝对限制见2.14章节 +- "优先使用QTO/BTO"指在满足规范前提下,根据场景选择最合适的参数类型 + +#### 4.2 并发数据保护最佳实践: +处理先读后更新场景时,为避免并发导致数据脏写,应充分利用BoService校验功能。例如:账户扣钱场景,避免并发扣除导致余额不足,传统raw sql用 `update account set balance = balance - amount where user_id = xxx and balance > amount` 的where条件保护;在TOCO中,应该:1、写方案中使用incr字段。2、在boService中添加保护代码 `if(userBo.getBalance() >= 0)` 或通过userBo业务不变性(聚合校验)添加 `balance>=0` 校验。 -----------------------------------------------------------------------------