diff --git a/llms-full.txt b/llms-full.txt index 4dcf84c..7bf2fd5 100644 --- a/llms-full.txt +++ b/llms-full.txt @@ -16,7 +16,7 @@ - **关键属性/配置:** 名称(以_enum结尾),枚举值列表(全大写字母+下划线) - **与其他元素关系:** 枚举可以作为其他对象(Entity、Dto、Vo、Bto、Qto、Eo)的字段类型使用。 - **Enum设计元素的表达:** - - 以Json格式表达,json schema 定义如下 + - 以Json格式表达,json schema 定义如下 ```json { "type": "object", @@ -34,19 +34,19 @@ } ``` * **代码产物和修改建议** - * **生成产物**:在common模块中生成一个Java类 - * **职责:** 表达Enum的数据结构 - * **命名规则**:类名以Enum结尾 - * **类路径:** 位于 ```**.common.enums``` 包路径下 - * **唯一标识符位置:** 其对应的唯一标志在类注解@AutoGenerated中指定 ,uuid规则: ${Enum在TOCO中的uuid}|ENUM|DEFINITION - - **生成代码:** Enum会在common层生成Enum文件,如StatusEnum - - **修改建议:** 不建议修改 + * **生成产物**:在common模块中生成一个Java类 + * **职责:** 表达Enum的数据结构 + * **命名规则**:类名以Enum结尾 + * **类路径:** 位于 ```**.common.enums``` 包路径下 + * **唯一标识符位置:** 其对应的唯一标志在类注解@AutoGenerated中指定 ,uuid规则: ${Enum在TOCO中的uuid}|ENUM|DEFINITION + - **生成代码:** Enum会在common层生成Enum文件,如StatusEnum + - **修改建议:** 不建议修改 #### **2.3 值对象 (Eo)** - **定义与用途:** EO为一种POJO对象结构,可被其他模块使用,可被用来做为实体字段的类型。 - **关键属性/配置:** 名称(以_eo结尾)。EO的字段类型只能为基本类型(含List)、EO、Enum,其他类型不允许。 - **与其他元素关系:** 可以作为其他对象(Entity、Dto、Vo、Bto、Qto)的字段类型使用,同时一个Eo中可以嵌套其他EO作为字段类型。 - **Eo设计元素的表达:** - - 以Json格式表达,Json Schema定义及如下 + - 以Json格式表达,Json Schema定义及如下 ```json { "type":"object", @@ -74,64 +74,64 @@ } ``` * **代码产物和修改建议** - * **生成产物**:在persist层生成结构定义类文,如AddressEo - * **职责:** 表达POJO数据结构 - * **命名规则**:类名以Eo结尾 - * **类路径:** 位于 ```**.persist.eo``` 包路径下 - * **唯一标识符位置:** 其对应的唯一标志在类注解@AutoGenerated中指定 ,uuid规则: ${Eo在TOCO中的uuid}|EO|DEFINITION - - **修改建议:** 不建议修改 + * **生成产物**:在persist层生成结构定义类文,如AddressEo + * **职责:** 表达POJO数据结构 + * **命名规则**:类名以Eo结尾 + * **类路径:** 位于 ```**.persist.eo``` 包路径下 + * **唯一标识符位置:** 其对应的唯一标志在类注解@AutoGenerated中指定 ,uuid规则: ${Eo在TOCO中的uuid}|EO|DEFINITION + - **修改建议:** 不建议修改 #### **2.4 实体关系 (ER / Entity)** - **定义与用途:** 实体及其关系。一个实体一般对应一个数据库表,关系为实体间的外键依赖关系 - **关键属性/配置:** 实体中包含名称、字段、字段类型、主键、索引等,关系分为1:1和1:N关系 - **与其他元素关系:** 实体关系是聚合的基础,也是DTO和VO的派生基础 - **代码产物和修改建议** - - 结构定义 - * **生成产物**:Java类,按照Mybatis-plus的要求生成 - * **职责:** 按照Mybatis-plus的要求生成结构定义类文件 - * **类路径:** 位于 ```**.persist.dos``` 包路径下 - * **唯一标识符位置:** 其对应的唯一标志在类注解@AutoGenerated中指定 ,uuid规则: ${Entity在TOCO中的uuid}|ENTITY|DEFINITION - - Mapper - * **生成产物**:在persist层生成Mybatis-plus的Mapper类 - * **职责:** 提供Mapper给Mybatis-plus框架 - * **命名规则**:类名以Mapper结尾(${entityName}Mapper) - * **类路径:** 位于 ```**.persist.mapper.mybatis``` 包路径下 - * **唯一标识符位置:** 其对应的唯一标志在类注解@AutoGenerated中指定 ,uuid规则: ${Eo在TOCO中的uuid} - - Dao接口 - * **生成产物**:在persist层生成Dao接口 - * **职责:** 提供Entity数据的查询接口,为service层提供数据访问入口 - * **命名规则**:类名以Dao结尾(${entityName}Dao) - * **类路径:** 位于 ```**.persist.mapper``` 包路径下 - * **唯一标识符位置:** 其对应的唯一标志在类注解@AutoGenerated中指定 ,uuid规则: ${Entity在TOCO中的uuid}|ENTITY|IDAO - - Dao实现 - * **生成产物**:在persist层生成Dao接口的实现类文件 - * **职责:** 通过调用Mapper实现实现Dao接口 - * **命名规则**:类名以DaoImpl结尾(${entityName}DaoImpl) - * **类路径:** 位于 ```**.persist.mapper``` 包路径下 - * **唯一标识符位置:** 其对应的唯一标志在类注解@AutoGenerated中指定 ,uuid规则: ${Entity在TOCO中的uuid}|ENTITY|DAO - - **修改建议:** 不建议修改 + - 结构定义 + * **生成产物**:Java类,按照Mybatis-plus的要求生成 + * **职责:** 按照Mybatis-plus的要求生成结构定义类文件 + * **类路径:** 位于 ```**.persist.dos``` 包路径下 + * **唯一标识符位置:** 其对应的唯一标志在类注解@AutoGenerated中指定 ,uuid规则: ${Entity在TOCO中的uuid}|ENTITY|DEFINITION + - Mapper + * **生成产物**:在persist层生成Mybatis-plus的Mapper类 + * **职责:** 提供Mapper给Mybatis-plus框架 + * **命名规则**:类名以Mapper结尾(${entityName}Mapper) + * **类路径:** 位于 ```**.persist.mapper.mybatis``` 包路径下 + * **唯一标识符位置:** 其对应的唯一标志在类注解@AutoGenerated中指定 ,uuid规则: ${Eo在TOCO中的uuid} + - Dao接口 + * **生成产物**:在persist层生成Dao接口 + * **职责:** 提供Entity数据的查询接口,为service层提供数据访问入口 + * **命名规则**:类名以Dao结尾(${entityName}Dao) + * **类路径:** 位于 ```**.persist.mapper``` 包路径下 + * **唯一标识符位置:** 其对应的唯一标志在类注解@AutoGenerated中指定 ,uuid规则: ${Entity在TOCO中的uuid}|ENTITY|IDAO + - Dao实现 + * **生成产物**:在persist层生成Dao接口的实现类文件 + * **职责:** 通过调用Mapper实现实现Dao接口 + * **命名规则**:类名以DaoImpl结尾(${entityName}DaoImpl) + * **类路径:** 位于 ```**.persist.mapper``` 包路径下 + * **唯一标识符位置:** 其对应的唯一标志在类注解@AutoGenerated中指定 ,uuid规则: ${Entity在TOCO中的uuid}|ENTITY|DAO + - **修改建议:** 不建议修改 #### **2.5 聚合对象 (BO/业务对象)** - **定义与用途:** 在TOCO中,聚合对象是对一组密切关联的实体的封装。聚合对象从单一实体开始,这个实体我们称为聚合根,通过实体间关系,不断的顺序将其他实体按层级关系组装进这个聚合对象。聚合对象可以按实体表达为树形结构。聚合对象提供了这组实体的内存一致性视图,提供数据操作入口。由于写操作的内聚性,聚合对象只能在单一模块中组合,而且一个实体只能属于一个对象。同样如果有实体不在任何一个聚合对象中,TOCO将无法提供与之相关的写方法。 - **包含元素:** 聚合对象包括聚合根及其聚合下的其他子实体对象,例如,商品聚合ProductBO中,商品基本信息实体是ProductBO的聚合根,商品SKU实体、商品库存实体是ProductBO的子聚合对象。 - **关键配置:** 名称(${EntityName驼峰}BO结尾,如StaffBO),聚合根实体,聚合子对象实体。每个聚合必须包含一个聚合根 - **与其他元素关系:** 聚合是写方案的基础 - **代码产物和修改建议** - - 综述 - - 业务对象包含多个Entity,通过业务对象的嵌套组合表达了Entity之间的关系,如果一个业务对象包含了子对象,则会生成BO和BaseBO,BaseBO封装实体属性和关系,子类留给业务扩展逻辑; - 如果是叶子节点(不存在子对象)的BO,则直接生成BO类文件,不生成BaseBO类文件 - - BO - * **生成产物**:在Manager层生成聚合对象类文件,符合Hibernate的标准 - * **职责:** 定义聚合对象,多个聚合对象组合成层级结构实现充血模型,支持写链路上的数据变更,监听数据变更,支持数据校验 - * **命名规则**:类名以BO结尾(${entityName}BO) - * **类路径:** 位于 ```**.manager.bo``` 包路径下 - * **唯一标识符位置:** 其对应的唯一标志在类注解@AutoGenerated中指定 ,uuid规则: ${Entity在TOCO中的uuid}|BO|DEFINITION - - **修改建议:** 建议修改BO中的validateAggregate或valid方法。不建议修改检验方法以外的其他代码,如果发现需求中有业务不变性校验,**注意** 上述的校验方法,在写方案内部由框架触发调用,而不是业务代码显式调用 - - BaseBO - * **生成产物**:对于存在子BO的聚合对象,封装不变的代码部分 - * **职责:** 定义聚合对象,多个聚合对象组合成层级结构实现充血模型,支持写链路上的数据变更,监听数据变更,支持数据校验 - * **命名规则**:类名以BO结尾(${entityName}BO) - * **类路径:** 位于 ```**.manager.bo``` 包路径下 - * **唯一标识符位置:** 其对应的唯一标志在类注解@AutoGenerated中指定 ,uuid规则: ${Entity在TOCO中的uuid}|BO|DEFINITION - - **修改建议:** 建议修改BO中的validateAggregate或valid方法,如果发现需求中有业务不变性校验;不建议修改检验方法以外的其他代码 + - 综述 + - 业务对象包含多个Entity,通过业务对象的嵌套组合表达了Entity之间的关系,如果一个业务对象包含了子对象,则会生成BO和BaseBO,BaseBO封装实体属性和关系,子类留给业务扩展逻辑; + 如果是叶子节点(不存在子对象)的BO,则直接生成BO类文件,不生成BaseBO类文件 + - BO + * **生成产物**:在Manager层生成聚合对象类文件,符合Hibernate的标准 + * **职责:** 定义聚合对象,多个聚合对象组合成层级结构实现充血模型,支持写链路上的数据变更,监听数据变更,支持数据校验 + * **命名规则**:类名以BO结尾(${entityName}BO) + * **类路径:** 位于 ```**.manager.bo``` 包路径下 + * **唯一标识符位置:** 其对应的唯一标志在类注解@AutoGenerated中指定 ,uuid规则: ${Entity在TOCO中的uuid}|BO|DEFINITION + - **修改建议:** 建议修改BO中的validateAggregate或valid方法。不建议修改检验方法以外的其他代码,如果发现需求中有业务不变性校验,**注意** 上述的校验方法,在写方案内部由框架触发调用,而不是业务代码显式调用 + - BaseBO + * **生成产物**:对于存在子BO的聚合对象,封装不变的代码部分 + * **职责:** 定义聚合对象,多个聚合对象组合成层级结构实现充血模型,支持写链路上的数据变更,监听数据变更,支持数据校验 + * **命名规则**:类名以BO结尾(${entityName}BO) + * **类路径:** 位于 ```**.manager.bo``` 包路径下 + * **唯一标识符位置:** 其对应的唯一标志在类注解@AutoGenerated中指定 ,uuid规则: ${Entity在TOCO中的uuid}|BO|DEFINITION + - **修改建议:** 建议修改BO中的validateAggregate或valid方法,如果发现需求中有业务不变性校验;不建议修改检验方法以外的其他代码 #### **2.6 数据传输对象 (DTO)** - **定义与用途:** 在TOCO中,DTO表达某个Entity为基本,通过外键关系不断关联多个Entity的数据结构。DTO还隐式表达了数据的取数拼装,这种拼装符合外键关系。往往被当做RPC的返回值、或读方案的返回值使用,不建议把DTO作为入参。注意DTO不能作为HTTP API的返回值。DTO分为BaseDTO和普通DTO,BaseDTO派生自Entity,包含Entity的所有字段,每个Entity有且仅有一个BaseDTO;普通DTO派生自BaseDTO,包含BaseDTO的所有字段,且可以增加扩展字段或自定义字段 - **如何创建/生成:** 对于每个Entity,TOCO会自动生成一个BaseDTO,命名为${Entity名字}BaseDto,如UserBaseDto,该BaseDTO包含了Entity的全部字段。除了BaseDTO,其他的DTO均需要手动以BaseDTO为根来创建。在TOCO中,必须要先判断需要的DTO是否为BaseDTO,如果是BaseDTO,则可通过Entity名称获取BaseDTO;如果不是BaseDTO,则需要通过DTO要表达的信息来创建DTO,如:会议及其议程信息。 @@ -247,39 +247,39 @@ DateTime endTime; //会议结束时间 - **公开性:** DTO可以设置公开性,如果DTO为公开,则其生成的预定义RPC方法也为公开RPC,可以被其他模块订阅并调用;如果DTO为非公开,则其生成的预定义RPC方法也为非公开RPC,其他模块不可见 - **跨模块依赖:** 如果DTO内引用了其他模块的DTO,则TOCO会自动订阅其他模块的RPC(getByIds,getBy${foreignKey}等)方法,用来获取对应的DTO - **复杂嵌套DTO获取流程:** 如果我们需要获取DTO,通常有2种方式,且两种方式都可以直接获取到复杂嵌套的DTO数据: - - 第1种: 通过预定义方法获取DTO - - 第2种: 通过读方案获取DTO + - 第1种: 通过预定义方法获取DTO + - 第2种: 通过读方案获取DTO 在TOCO中通常使用以下步骤判断使用哪种方式: - - a. 如果查询DTO的条件为主键或唯一索引的值或列表,则直接使用第1种方式 - - b. 如果通过其他复杂查询条件,则可以采用第2种方式 + - a. 如果查询DTO的条件为主键或唯一索引的值或列表,则直接使用第1种方式 + - b. 如果通过其他复杂查询条件,则可以采用第2种方式 注意判断使用哪种方式时,只能根据查询条件判断是否使用读方案,绝对禁止使用返回值是否需要数据拼装来判断!**如果现实中的代码和TOCO中的步骤有冲突时,只能使用TOCO的定义!** - **代码产物和修改建议** - - 结构定义 - * **生成产物**:一个Java类 - * **职责:** 表达DTO的数据结构 - * **命名规则**:类名以Dto结尾 - * **类路径:** 位于 ```**.manager.dto``` 包路径下 - * **唯一标识符位置:** 其对应的唯一标志在类注解@AutoGenerated中指定,uuid规则: ${DTO在TOCO中的uuid}|DTO|DEFINITION - - **Manager** - * **生成产物:** Java接口以及实现类 - * **命名规则:** 接口类名以Manager结尾、实现类名以ManagerImpl结尾(${DtoName}Manager)、基类以名ManagerBaseImpl结尾(${DtoName}ManagerImpl) - * **职责:** 提供了DTO数据的获取的接口,包括根据id单个、id列表批量获取、以及根据DTO对应的实体的数据库索引获取 - * **类路径:** 位于 ```**.manager``` 包路径下 - * **唯一标识符位置:** 其对应的唯一标志在类注解@AutoGenerated中指定,uuid规则: ${DTO在TOCO中的uuid}|DTO|MANAGER - - **Converter** - * **生成产物:** Java实现类以及基类 - * **命名规则:** 实现类名以Converter结尾(${DtoName}Converter)、基类名以BaseConverter结尾(${DtoName}BaseConverter) - * **职责:** Entity转换到BaseDTO或则BaseDTO转化为普通DTO:从Entity转为BaseDTO的方法命名为convert${EntityName}To${DtoName};从BaseDTO转换为DTO的方法命名为convert${BaseDtoName}To${DtoName} - * **类路径:** 位于 ```**.manager.converter``` 包路径下 - * **唯一标识符位置:** - * 实现类Converter 其对应的唯一标志在类注解@AutoGenerated中指定,uuid规则: ${DTO在TOCO中的uuid}|DTO|CONVERTER - * 基类BaseConverter 其对应的唯一标志在类注解@AutoGenerated中指定,uuid规则: ${DTO在TOCO中的uuid}|DTO|BASE_CONVERTER - - **例子:** - * 如UserDto、UserDtoManager、UserDtoConverter extends UserDtoBaseConverter、UserDtoService或(名称为${DtoName}Service,内部包含getById,getByIds等方法)。如果Dto为UserBaseDto,则生成的类名为UserBaseDtoService - - **修改建议:** - - 建议在Service与BaseConverter中进行代码扩展,不建议修改结构定义文件和Manager文件。其中DTO的**自定义字段**由于不直接派生自Entity,所以一般会对应取数逻辑代码。通常如果涉及到数据获取、计算和拼装,批量处理的性能最好,所以代码位置**必须**放在BaseConverter中已经自动生成的**列表**转换方法中,批量取数组装,如UserBaseDtoBaseConverter.convertUserToUserBaseDto(List)或UserDtoBaseConverter.convertUserBaseDtoToUserDto(List) + - 结构定义 + * **生成产物**:一个Java类 + * **职责:** 表达DTO的数据结构 + * **命名规则**:类名以Dto结尾 + * **类路径:** 位于 ```**.manager.dto``` 包路径下 + * **唯一标识符位置:** 其对应的唯一标志在类注解@AutoGenerated中指定,uuid规则: ${DTO在TOCO中的uuid}|DTO|DEFINITION + - **Manager** + * **生成产物:** Java接口以及实现类 + * **命名规则:** 接口类名以Manager结尾、实现类名以ManagerImpl结尾(${DtoName}Manager)、基类以名ManagerBaseImpl结尾(${DtoName}ManagerImpl) + * **职责:** 提供了DTO数据的获取的接口,包括根据id单个、id列表批量获取、以及根据DTO对应的实体的数据库索引获取 + * **类路径:** 位于 ```**.manager``` 包路径下 + * **唯一标识符位置:** 其对应的唯一标志在类注解@AutoGenerated中指定,uuid规则: ${DTO在TOCO中的uuid}|DTO|MANAGER + - **Converter** + * **生成产物:** Java实现类以及基类 + * **命名规则:** 实现类名以Converter结尾(${DtoName}Converter)、基类名以BaseConverter结尾(${DtoName}BaseConverter) + * **职责:** Entity转换到BaseDTO或则BaseDTO转化为普通DTO:从Entity转为BaseDTO的方法命名为convert${EntityName}To${DtoName};从BaseDTO转换为DTO的方法命名为convert${BaseDtoName}To${DtoName} + * **类路径:** 位于 ```**.manager.converter``` 包路径下 + * **唯一标识符位置:** + * 实现类Converter 其对应的唯一标志在类注解@AutoGenerated中指定,uuid规则: ${DTO在TOCO中的uuid}|DTO|CONVERTER + * 基类BaseConverter 其对应的唯一标志在类注解@AutoGenerated中指定,uuid规则: ${DTO在TOCO中的uuid}|DTO|BASE_CONVERTER + - **例子:** + * 如UserDto、UserDtoManager、UserDtoConverter extends UserDtoBaseConverter、UserDtoService或(名称为${DtoName}Service,内部包含getById,getByIds等方法)。如果Dto为UserBaseDto,则生成的类名为UserBaseDtoService + - **修改建议:** + - 建议在Service与BaseConverter中进行代码扩展,不建议修改结构定义文件和Manager文件。其中DTO的**自定义字段**由于不直接派生自Entity,所以一般会对应取数逻辑代码。通常如果涉及到数据获取、计算和拼装,批量处理的性能最好,所以代码位置**必须**放在BaseConverter中已经自动生成的**列表**转换方法中,批量取数组装,如UserBaseDtoBaseConverter.convertUserToUserBaseDto(List)或UserDtoBaseConverter.convertUserBaseDtoToUserDto(List) #### **2.7 视图对象 (VO)** - **定义与用途:** 在TOCO中,VO表达某个BaseDTO(如果用户指明派生源,也可使用其他DTO)为派生源,通过外键关系不断关联多个BaseDTO的数据结构。VO用于在视图层与前端之间进行数据传输,往往被当做HTTP API的返回值、或读方案的返回值使用,由服务端返回至前端。注意VO不能作为RPC的返回值。 @@ -488,24 +488,24 @@ DateTime endTime; //会议结束时间 注意判断使用哪种方式时,只能根据查询条件判断是否使用读方案,绝对禁止使用返回值是否需要数据拼装来判断!**如果现实中的代码编写方式和TOCO中的步骤有冲突时,只能使用TOCO的定义!** - **代码产物和修改建议** - - **结构定义** - * **生成产物:** 在controller层生成一个Java类 - * **命名规则:** 类名以Vo结尾 - * **职责:** 表达VO的数据结构 - * **类路径:** 位于 ```**.entrance.web.vo``` 包路径下 - * **唯一标识符位置:** 其对应的唯一标志在类注解@AutoGenerated中指定,uuid规则: ${VO在TOCO中的uuid}|VO|DEFINITION - - **Converter** - * **生成产物:** 在controller层生成一个Java类(**有派生源**的VO才有Converter)和基类 - * **命名规则:** 实现类名以Converter结尾(${VoName}Converter),基类名以BaseConverter结尾(${VoName}BaseConverter) - * **类路径:** 位于 ```**.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 - * **例子:** - * 如UserDetailVo、UserDetailVoConverter(包含convertToUserDetailVo、convertToUserDetailVoList、convertToUserDetailVoMap、convertAndAssembleData、convertAndAssembleDataList方法) - - **修改建议:** - - 建议在Converter中进行代码扩展,不建议修改结构定义文件。其中VO的**自定义字段**由于不直接派生自DTO,所以一般会对应取数逻辑代码。通常如果涉及到数据获取、计算和拼装,批量处理的性能最好,所以自定义字段对应的代码位置**必须**放在Converter的**Map**基础转换方法convertTo${VoName}Map中,批量取数组装,如UserVoConverter.convertToUserVoMap + - **结构定义** + * **生成产物:** 在controller层生成一个Java类 + * **命名规则:** 类名以Vo结尾 + * **职责:** 表达VO的数据结构 + * **类路径:** 位于 ```**.entrance.web.vo``` 包路径下 + * **唯一标识符位置:** 其对应的唯一标志在类注解@AutoGenerated中指定,uuid规则: ${VO在TOCO中的uuid}|VO|DEFINITION + - **Converter** + * **生成产物:** 在controller层生成一个Java类(**有派生源**的VO才有Converter)和基类 + * **命名规则:** 实现类名以Converter结尾(${VoName}Converter),基类名以BaseConverter结尾(${VoName}BaseConverter) + * **类路径:** 位于 ```**.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 + * **例子:** + * 如UserDetailVo、UserDetailVoConverter(包含convertToUserDetailVo、convertToUserDetailVoList、convertToUserDetailVoMap、convertAndAssembleData、convertAndAssembleDataList方法) + - **修改建议:** + - 建议在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的字段名保持一致),例如: @@ -519,50 +519,50 @@ DateTime endTime; //会议结束时间 ``` 需求是需要根据会议名称查询会议列表,并且根据会议议程信息过滤掉部分议程,那在定义查询对象的时候就需要包含议程信息,并且扩展字段的名称要定义为**agendaList** - **查询对象设计元素的表达** - - 以json格式表达,json schema 定义如下 - ```json - { - "type": "object","description": "查询对象定义", "required": ["dtoOrVoId","name","fromEntity"], - "properties": { - "name": {"type": "string", "description": "查询对象名称,用英语表达,单词之间下划线分割,长度补超过32个字符"}, - "uuid": {"type": "string", "description": "查询对象uuid, 在更新的时候必须传递(只有根节点必须传递),创建的时候不传递"}, - "dtoOrVoId":{"type":"string","description":"返回数据对象(VO或DTO)的uuid,创建的时候必须指定,更新的时候不传递"}, - "moduleName": {"type": "string", "description": "查询对象所属模块名称,在创建的时候必须传递"}, - "fromEntity": {"type": "string", "description": "查询对象对应的实体"}, - "expandList": { - "type": "array", "description": "正向扩展列表", - "items": - { - "type": "object","description": "正向扩展定义","required": ["field","wo","fieldName"], - "properties": { - "field": {"type": "string", "description": "指定fromEntity的扩展字段,必须是fromEntity的外键字段"}, - "wo": {"$ref": "#"}, - "fieldName": {"type": "string", "description": "扩展出来的字段名称"} + - 以json格式表达,json schema 定义如下 + ```json + { + "type": "object","description": "查询对象定义", "required": ["dtoOrVoId","name","fromEntity"], + "properties": { + "name": {"type": "string", "description": "查询对象名称,用英语表达,单词之间下划线分割,长度补超过32个字符"}, + "uuid": {"type": "string", "description": "查询对象uuid, 在更新的时候必须传递(只有根节点必须传递),创建的时候不传递"}, + "dtoOrVoId":{"type":"string","description":"返回数据对象(VO或DTO)的uuid,创建的时候必须指定,更新的时候不传递"}, + "moduleName": {"type": "string", "description": "查询对象所属模块名称,在创建的时候必须传递"}, + "fromEntity": {"type": "string", "description": "查询对象对应的实体"}, + "expandList": { + "type": "array", "description": "正向扩展列表", + "items": + { + "type": "object","description": "正向扩展定义","required": ["field","wo","fieldName"], + "properties": { + "field": {"type": "string", "description": "指定fromEntity的扩展字段,必须是fromEntity的外键字段"}, + "wo": {"$ref": "#"}, + "fieldName": {"type": "string", "description": "扩展出来的字段名称"} + } } - } - }, - "reverseExpandList": { - "type": "array", "description": "反向扩展列表", - "items": - { - "type": "object","description": "反向扩展定义","required": ["field","wo","fieldName"], - "properties": { - "field": {"type": "string", "description": "指定fromEntity的扩展字段,必须是fromEntity的外键字段"}, - "wo": {"$ref": "#"}, - "fieldName": {"type": "string", "description": "扩展出来的字段名称"} + }, + "reverseExpandList": { + "type": "array", "description": "反向扩展列表", + "items": + { + "type": "object","description": "反向扩展定义","required": ["field","wo","fieldName"], + "properties": { + "field": {"type": "string", "description": "指定fromEntity的扩展字段,必须是fromEntity的外键字段"}, + "wo": {"$ref": "#"}, + "fieldName": {"type": "string", "description": "扩展出来的字段名称"} + } } } } } - } - ``` + ``` - **如何创建/生成:** - - **创建思路** 按照需要查询返回的DTO|VO的结构,构建出同构的WO对象(扩展和反向扩展的字段名保持一致),然后根据查询需求和过滤需求对WO进行二次裁剪或和扩展: - - 去掉过滤和查询都不需要的扩展 - - 补全查询或者字段过滤需要扩展 - - **关键配置:** Wo中的字段分为三种:a.继承Entity的字段,和Entity的字段类型一样;b.扩展字段,含正向替换和反向注入字段,类型为WO或List; - - **字段扩展方式:** TOCO定义了一个WO组装方法,适用于WO通过外键关系替换/注入对应Entity的信息,对象化表达有外键关系的Entity信息。只要存在外键关系且满足以下条件即可扩展:a.对于正向替换:当前实体存在指向其他实体的外键字段;b.对于反向注入:其他实体存在指向当前实体的外键字段。 - 例如:有两个Entity + - **创建思路** 按照需要查询返回的DTO|VO的结构,构建出同构的WO对象(扩展和反向扩展的字段名保持一致),然后根据查询需求和过滤需求对WO进行二次裁剪或和扩展: + - 去掉过滤和查询都不需要的扩展 + - 补全查询或者字段过滤需要扩展 + - **关键配置:** Wo中的字段分为三种:a.继承Entity的字段,和Entity的字段类型一样;b.扩展字段,含正向替换和反向注入字段,类型为WO或List; + - **字段扩展方式:** TOCO定义了一个WO组装方法,适用于WO通过外键关系替换/注入对应Entity的信息,对象化表达有外键关系的Entity信息。只要存在外键关系且满足以下条件即可扩展:a.对于正向替换:当前实体存在指向其他实体的外键字段;b.对于反向注入:其他实体存在指向当前实体的外键字段。 + 例如:有两个Entity ``` MeetingRoom{ //会议室 Long id;// 会议室id,主键 @@ -646,34 +646,34 @@ DateTime endTime; //会议结束时间 ``` #### **2.9 读方案 (ReadPlan)** - **定义与用途:** 在TOCO中,针对DTO和VO,读方案描述了如何基于查询对象从数据库获取DTO和VO列表数据,主要提供了两个能力 - - 根据查询条件,返回符合条件的DTO或VO的id列表 - - 根据字段过滤条件,对DTO和VO的列表字段数据进行过滤 + - 根据查询条件,返回符合条件的DTO或VO的id列表 + - 根据字段过滤条件,对DTO和VO的列表字段数据进行过滤 - **关键配置:** 名称(小写字母+下划线,不要以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完全实现 - **如何创建/生成:** 在创建读方案时,必须先调用工具创建或选择现有的**一种**DTO或VO作为返回值类型,然后再定义查询条件的自然语言描述(如:根据用户姓名、年龄、学校名称,查询用户列表),分页方式、是否生成计数方法、排序字段等。 - **排序** 对于返回列表数据的排序,排序规则和SQL的排序类似(通过指定字段以及升序|降序,字段可以组合;ReadPlan支持有**两种**方式: - - **默认排序**:指定默认排序字段(不需要入参指定排序字段) - - **自定义排序**:指定排序字段(需要入参指定排序字段),这种方式通常能很好的满足列表头动态指定排序的需求 - - **排序字段的来源**:并不是查询字段中的所有字段都能作为排序字段,因为返回的根DTO|VO的id去重列表,所以排序字段只能来源自根WO(不包括列表WO属性: 比如MeetingWO包含了List agendaList属性, 那么agendaList不能作为排序字段),以及根WO扩展出来的非列表属性的WO的字段(比如MeetingWO包含了MeetingRoomWO,那么MeetingRoomWO的属性也能作为排序字段),**注意**:为了唯一确定是给按照那个字段进行排序,排序字段是一个从根节点到当前属性的路径,例如:ADto包含了BDto bDto,BDto具备属性name,如果指定对BDto的name字段排序,则字段路径为:bDto.name + - **默认排序**:指定默认排序字段(不需要入参指定排序字段) + - **自定义排序**:指定排序字段(需要入参指定排序字段),这种方式通常能很好的满足列表头动态指定排序的需求 + - **排序字段的来源**:并不是查询字段中的所有字段都能作为排序字段,因为返回的根DTO|VO的id去重列表,所以排序字段只能来源自根WO(不包括列表WO属性: 比如MeetingWO包含了List agendaList属性, 那么agendaList不能作为排序字段),以及根WO扩展出来的非列表属性的WO的字段(比如MeetingWO包含了MeetingRoomWO,那么MeetingRoomWO的属性也能作为排序字段),**注意**:为了唯一确定是给按照那个字段进行排序,排序字段是一个从根节点到当前属性的路径,例如:ADto包含了BDto bDto,BDto具备属性name,如果指定对BDto的name字段排序,则字段路径为:bDto.name - 你需要提取需求中的查询部分信息,以输入的查询对象作为查询上下文件,构建一个查询语句 - 如果有列表属性过滤需求(如果没有filter,对每个列表属性都是放回全部数据,例如:MeetingDto的MeetingAgendaDtoList属性,如果不设置MeetingAgendaDtoList的filter,则返回该会议的全部议程信息):你需要提取需求中的过滤部分信息,以输入的查询对象作为查询上下文件,针对可过滤字段(列表属性)分别创建过滤条件, **注意** 过滤条件不能使用列表属性作为查询条件属性(即不能使用contains语法),为了唯一确定是给那个列表属性的指定过滤条件,属性字段是一个从根节点属性到当前属性的路径,例如:ADto包含了List bDtoList;BDto包含了List cDtoList,如果对cDtoList使用Filter,则字段路径为:bDtoList.cDtoList - 查询语法和过滤语法 - - 基本语法格式是 属性名 操作符 变量或常量,变量表示了这个数据是外部传入的,用 #变量名为格式;如果是枚举类型常量,需要使用'符号包起来,例如: 男性枚举类型 'MALE' - - 对于数值、时间类型属性有:!=, ==, >, <,<=,>=, in,notIn, isNullOrNot 这些操作符 - - 对于文本类型属性有:like, isNotNull, isNull,!=, ==, in,notIn, isNullOrNot 这些操作符 - - 对于对象属性或者列表对象属性可以用isNull, isNotNull, isNullOrNot 这些操作符。注意本对象不能直接用这个操作符。 - - 对于对象属性还可以对其子属性进行上述查询 - - 查询变量不能作为条件属性 - - 查询条件之间可以使用AND, OR 进行连接 - - 可以插入括号()对条件进行分组 - - 对于列表对象属性只能使用contains、isNull、isNotNull操作符:如果是wo列表类型可以使用contains(子查询, 子查询的条件属性只能作用在对应的列表类型对象的属性上),表示了该列表属性中需要包含至少一个满足子查询条件的对象; 其他列表类型只能使用isNull或者isNotNull - - 把上述子查询通过and, or, not这三个连接符拼装在一起就可以完成一个查询。请不要使用没提示过的操作符号,连接符。 - - 查询条件中的入参可以在运行时传入或者不传入值,如果不传入值表示该参数相关的条件不起作用;基于这种查询语句实际运行时的动态效果,多个条件联合查询的时候可以优先使用AND - - 使用点号(.)可以访问当前对象的单值对象类型的子属性, 可以多个点号的组合访问嵌套单值对象的属性 - - 查询条件中的属性必须是当前查询对象的属性或单值对象属性或者单值对象的子属性 - - 禁止使用filter语法 - - 禁止使用has语法 - - 语法定义:使用 lezer 定义了如下语法 + - 基本语法格式是 属性名 操作符 变量或常量,变量表示了这个数据是外部传入的,用 #变量名为格式;如果是枚举类型常量,需要使用'符号包起来,例如: 男性枚举类型 'MALE' + - 对于数值、时间类型属性有:!=, ==, >, <,<=,>=, in,notIn, isNullOrNot 这些操作符 + - 对于文本类型属性有:like, isNotNull, isNull,!=, ==, in,notIn, isNullOrNot 这些操作符 + - 对于对象属性或者列表对象属性可以用isNull, isNotNull, isNullOrNot 这些操作符。注意本对象不能直接用这个操作符。 + - 对于对象属性还可以对其子属性进行上述查询 + - 查询变量不能作为条件属性 + - 查询条件之间可以使用AND, OR 进行连接 + - 可以插入括号()对条件进行分组 + - 对于列表对象属性只能使用contains、isNull、isNotNull操作符:如果是wo列表类型可以使用contains(子查询, 子查询的条件属性只能作用在对应的列表类型对象的属性上),表示了该列表属性中需要包含至少一个满足子查询条件的对象; 其他列表类型只能使用isNull或者isNotNull + - 把上述子查询通过and, or, not这三个连接符拼装在一起就可以完成一个查询。请不要使用没提示过的操作符号,连接符。 + - 查询条件中的入参可以在运行时传入或者不传入值,如果不传入值表示该参数相关的条件不起作用;基于这种查询语句实际运行时的动态效果,多个条件联合查询的时候可以优先使用AND + - 使用点号(.)可以访问当前对象的单值对象类型的子属性, 可以多个点号的组合访问嵌套单值对象的属性 + - 查询条件中的属性必须是当前查询对象的属性或单值对象属性或者单值对象的子属性 + - 禁止使用filter语法 + - 禁止使用has语法 + - 语法定义:使用 lezer 定义了如下语法 ``` @top Program { expression? } @skip { spaces | newline | LineComment } @@ -747,7 +747,7 @@ DateTime endTime; //会议结束时间 @detectDelim ``` - **读方案设计元素的表达** - - 以json格式表达,json schema 定义如下 + - 以json格式表达,json schema 定义如下 ```json { "type":"object","description":"读方案定义", @@ -789,111 +789,111 @@ DateTime endTime; //会议结束时间 } ``` - **举例** - - 上下文 - ```java - public class meeting_room { - storey storey; // 楼层id - String name; // 名称 - room_type_enum room_type; // 会议室类型 - List equipment; // 设备 - Long id; // 主键 - Long seat_number; // 座位数 - String description; // 说明 - Boolean enable_indicator; // 是否启用 - String input_code; // 输入码 - Long storey_id; // 楼层id - Long contact_staff_id; // 联系员工ID - List meeting; //注意:该字段可添加过滤条件 - class storey { // 建筑的楼层 - location location; // 位置id - String name; // 楼层 - Long id; // 主键 - Long sort_number; // 排序号 - Long location_id; // 位置id - } - class meeting { - Long id; // 主键 - String title; // 标题 - Date start_time; // 开始时间 - Date end_time; // 结束时间 - Long room_id; // 会议室id - String description; // 描述 - Long create_user_id; // 创建人id - } - class location { // 位置信息,如楼栋 - String name; // 名称 - Long id; // 主键 - String description; // 描述 - Long sort_number; // 排序号 - } - enum room_type_enum { // - SMALL, //小会议室 - MEDIUM, //中会议室 - LARGE - } - enum equipment_enum { // - TV, //电视 - WHITEBOARD, //白板 - PROJECTOR, //投影仪 - VIDEO + - 上下文 + ```java + public class meeting_room { + storey storey; // 楼层id + String name; // 名称 + room_type_enum room_type; // 会议室类型 + List equipment; // 设备 + Long id; // 主键 + Long seat_number; // 座位数 + String description; // 说明 + Boolean enable_indicator; // 是否启用 + String input_code; // 输入码 + Long storey_id; // 楼层id + Long contact_staff_id; // 联系员工ID + List meeting; //注意:该字段可添加过滤条件 + class storey { // 建筑的楼层 + location location; // 位置id + String name; // 楼层 + Long id; // 主键 + Long sort_number; // 排序号 + Long location_id; // 位置id + } + class meeting { + Long id; // 主键 + String title; // 标题 + Date start_time; // 开始时间 + Date end_time; // 结束时间 + Long room_id; // 会议室id + String description; // 描述 + Long create_user_id; // 创建人id + } + class location { // 位置信息,如楼栋 + String name; // 名称 + Long id; // 主键 + String description; // 描述 + Long sort_number; // 排序号 + } + enum room_type_enum { // + SMALL, //小会议室 + MEDIUM, //中会议室 + LARGE + } + enum equipment_enum { // + TV, //电视 + WHITEBOARD, //白板 + PROJECTOR, //投影仪 + VIDEO + } } + ``` + - 用户需求 + 获取一段时间未被使用的会议室(包含当前会议已选择的会议室),并且根据会议名称会议信息,最后分页返回。 + - 对应的读方案定义 + ```json + { + "name": "get_unused_meeting_room_list", + "description": "获取未使用的会议室(包含当前会议已选择的会议室)", + "woId":"759bedd4-4540-4de6-b65d-d44912fb0991", + "dtoOrVoId":"e50c02c3-f336-4dc6-88e1-24ffe3dea1e2", + "generateCountApi": true, + "supportPaginate": true, + "supportUnPage": false, + "supportWaterfall": false, + "query": "enable_indicator == true AND ( id == #idIs OR meeting isNull OR NOT ( meeting contains ( start_time <= #meetingEndTime AND end_time >= #meetingStartTime ) ) )", + "filters": [ + { + "fieldPath": "meeting", + "filter": "title like #meetingTitleLike" + } + ] } - ``` - - 用户需求 - 获取一段时间未被使用的会议室(包含当前会议已选择的会议室),并且根据会议名称会议信息,最后分页返回。 - - 对应的读方案定义 - ```json - { - "name": "get_unused_meeting_room_list", - "description": "获取未使用的会议室(包含当前会议已选择的会议室)", - "woId":"759bedd4-4540-4de6-b65d-d44912fb0991", - "dtoOrVoId":"e50c02c3-f336-4dc6-88e1-24ffe3dea1e2", - "generateCountApi": true, - "supportPaginate": true, - "supportUnPage": false, - "supportWaterfall": false, - "query": "enable_indicator == true AND ( id == #idIs OR meeting isNull OR NOT ( meeting contains ( start_time <= #meetingEndTime AND end_time >= #meetingStartTime ) ) )", - "filters": [ - { - "fieldPath": "meeting", - "filter": "title like #meetingTitleLike" - } - ] - } - ``` + ``` - **代码产物和修改建议** - - **Service** - * **生成产物:** 在Service层生成一个Java类 - * **命名规则:** 类名以QtoService结尾(${ReadPlanName}QtoService) - * **类路径:** 位于 ```**.service.index.entity```包路径下 - * **职责:** 提供查询入口;返回符合查询条件的DTO或VO的id列表(分页、全部、或则瀑布流),以及返回符合条件的总数。 在TOCO的读链路中,数据查询分2步骤,1. 获取符合查询条件的DTO或VO的id列表,2. 根据id列表组装DTO或VO列表。 - * **唯一标识符位置:** 其对应的唯一标志在类注解@AutoGenerated中指定,uuid规则: ${ReadPlan在TOCO中的uuid}|QTO|SERVICE - - **查询传输对象(QTO)** - * **生成产物:** 在Service层生成一个Java类 - * **命名规则:** 类名以Qto结尾(${ReadPlanName}Qto) - * **类路径:** 位于 ```**.persist.qto```包路径下 - * **职责:** 读方案的查询参数结构,通常可作为API的参数,API接收到参数后可直接透传给内部的RPC进行调用 - * **唯一标识符位置:** 其对应的唯一标志在类注解@AutoGenerated中指定,uuid规则: ${ReadPlan在TOCO中的uuid}|QTO|DEFINITION - - **Dao** - - **生成产物:** 在Dao层生成一个Java类 - - **命名规则:** 类名以Dao结尾(${ReadPlanName}Dao) - - **类路径:** 位于 ```**.persist.mapper```包路径下 - - **职责:** 读方案对应的数据库查询方法 - - **唯一标识符位置:** 其对应的唯一标志在类注解@AutoGenerated中指定,uuid规则: ${ReadPlan在TOCO中的uuid}|QTO|DAO - - **QueryExecutor** - - **生成产物** 对于返回**VO**的查询方案,在controller层生成一个Java类 - - **命名规则:** 类名以QueryExecutor结尾(${WritePlanName}QueryExecutor) - - **类路径:** 位于 ```**.entrance.web.query.executor```包路径下 - - **职责:** 把QtoService返回的id数据转化成目标**VO** - - **QueryService** - - **生成产物** 对于返回**DTO**的查询方案,在service层生成一个Java类 - - **命名规则:** 类名以QueryService结尾(${WritePlanName}QueryService) - - **类路径:** 位于 ```**.service.index.entity```包路径下 - - **职责:** 把QtoService返回的id数据转化成目标**DTO** - - **例子:** - * 根据用户名称查询用户列表返回UserDTO,则生成UserNameQto、UserNameQtoService、UserNameQtoDao、UserNameQueryService; UserNameQueryService调用UserNameQtoService,UserNameQtoService调用UserNameQtoDao - - **修改建议:** - - 如果有对结果的数据二次处理,建议在QueryService和QueryExecutor中进行代码扩展,不建议修改QTO文件 + - **Service** + * **生成产物:** 在Service层生成一个Java类 + * **命名规则:** 类名以QtoService结尾(${ReadPlanName}QtoService) + * **类路径:** 位于 ```**.service.index.entity```包路径下 + * **职责:** 提供查询入口;返回符合查询条件的DTO或VO的id列表(分页、全部、或则瀑布流),以及返回符合条件的总数。 在TOCO的读链路中,数据查询分2步骤,1. 获取符合查询条件的DTO或VO的id列表,2. 根据id列表组装DTO或VO列表。 + * **唯一标识符位置:** 其对应的唯一标志在类注解@AutoGenerated中指定,uuid规则: ${ReadPlan在TOCO中的uuid}|QTO|SERVICE + - **查询传输对象(QTO)** + * **生成产物:** 在Service层生成一个Java类 + * **命名规则:** 类名以Qto结尾(${ReadPlanName}Qto) + * **类路径:** 位于 ```**.persist.qto```包路径下 + * **职责:** 读方案的查询参数结构,通常可作为API的参数,API接收到参数后可直接透传给内部的RPC进行调用 + * **唯一标识符位置:** 其对应的唯一标志在类注解@AutoGenerated中指定,uuid规则: ${ReadPlan在TOCO中的uuid}|QTO|DEFINITION + - **Dao** + - **生成产物:** 在Dao层生成一个Java类 + - **命名规则:** 类名以Dao结尾(${ReadPlanName}Dao) + - **类路径:** 位于 ```**.persist.mapper```包路径下 + - **职责:** 读方案对应的数据库查询方法 + - **唯一标识符位置:** 其对应的唯一标志在类注解@AutoGenerated中指定,uuid规则: ${ReadPlan在TOCO中的uuid}|QTO|DAO + - **QueryExecutor** + - **生成产物** 对于返回**VO**的查询方案,在controller层生成一个Java类 + - **命名规则:** 类名以QueryExecutor结尾(${WritePlanName}QueryExecutor) + - **类路径:** 位于 ```**.entrance.web.query.executor```包路径下 + - **职责:** 把QtoService返回的id数据转化成目标**VO** + - **QueryService** + - **生成产物** 对于返回**DTO**的查询方案,在service层生成一个Java类 + - **命名规则:** 类名以QueryService结尾(${WritePlanName}QueryService) + - **类路径:** 位于 ```**.service.index.entity```包路径下 + - **职责:** 把QtoService返回的id数据转化成目标**DTO** + - **例子:** + * 根据用户名称查询用户列表返回UserDTO,则生成UserNameQto、UserNameQtoService、UserNameQtoDao、UserNameQueryService; UserNameQueryService调用UserNameQtoService,UserNameQtoService调用UserNameQtoDao + - **修改建议:** + - 如果有对结果的数据二次处理,建议在QueryService和QueryExecutor中进行代码扩展,不建议修改QTO文件 #### **2.10 查询传输对象(QTO)** - **定义与用途:** 在TOCO中,QTO为读方案的查询参数结构,每个读方案会对应一个QTO,写方案调用方按照QTO的结构向读方案生成的RPC方法传入需要查询的实体字段值,完成对数据库的查询 - **如何创建/生成:** 在创建读方案后,TOCO会自动生成QTO作为该读方案传入的查询参数结构,无需单独创建 @@ -905,69 +905,69 @@ DateTime endTime; //会议结束时间 - **与RPC的关系:** 对于每一个写方案,TOCO会自动生成一个RPC方法,其参数为写方案对应的BTO,返回值为本次操作的聚合根实体的主键值,内部只实现了对当前聚合的数据库操作 - **与聚合的关系:** 每个聚合下可以定义多个写方案,但每个写方案只能操作一个聚合内的表,无法同时操作多个聚合内的表 - **如何创建/生成:** 创建写方案时需要先选定对应的聚合,以及要操作的聚合内部的实体,然后确定对每个实体的具体操作类型 - - 提取需求中的更新部分需求,根据上下文信息构建出一个写更新方案 - - 上下文中输入了备选聚合对象的的定义,每个聚合对象定义了多个实体的嵌套关系 - - 先明确聚合内实体的父子关系层级 - - 根据需求提取出变更相关的名称 - - 根需求提取出变更相关的描述 - - 根据需求选择确定变更范围,选出一个最合适的聚合对象;注意: 最多只能选择一个聚合对象 - - 根据需求在选定的聚合对象按照对象的粒度,选出所需变更对象 - - 根据需求选定变更对象的字段;每个选定的对象至少需要选定一个字段,如果无法确定任何字段,默认选择全部字段, 字段必须明确指定名称,不能类似“所有字段”的模糊表达 - - 每个对象上可以设置对应的一个操作,操作包括:CREATE(创建),UPDATE(更新), DELETE(删除), CREATE_ON_DUPLICATE_UPDATE(创建或者更新),FULL_MERGE( 批量更新列表数据,根据传入的列表数据,一条一条执行CREATE_ON_DUPLICATE_UPDATE的操作;并且删除掉老的列表中不在传入列表数据中的部分), PARTIAL_MERGE(批量更新列表数据,根据传入的列表数据,一条一条执行CREATE_ON_DUPLICATE_UPDATE的操作) - - 批量更新列表数据(FULL_MERGE或者PARTIAL_MERGE),则操作方案需要包含它的父对象 - - FULL_MERGE和PARTIAL_MERGE只能用在列表属性上, 单值属性请使用CREATE_ON_DUPLICATE_UPDATE - - 操作是 UPDATE、DELETE、CREATE_ON_DUPLICATE_UPDATE、FULL_MERGE、PARTIAL_MERGE的时候必须且只能指定一个唯一键(unique_key)(包括主键),用于确定对应的数据记录,主键对应的字段也要包含在fields字段中; - - 选定的对象层级不能跳跃;如果发生不连续,去掉不连续的叶子对象 - - 注意父对象和下一层子对象的操作类型有如下限制,必须严格检查以下规则 - - 如果父对象的操作是DELETE,则下一层子对象不能选择,也不能能设定任何操作 - - 如果父对象是的操作是CREATE,则下一层子对象只能选择CREATE - - 如果父对象是CREATE_ON_DUPLICATE_UPDATE,子对象只能是CREATE或者CREATE_ON_DUPLICATE_UPDATE或者FULL_MERGE或者PARTIAL_MERGE - - 如果父对象是FULL_MERGE,子对象只能是FULL_MERGE或者PARTIAL_MERGE或者是CREATE - - 对于选定的字段列表中的字段,如果字段的类型是Long、BigDecimal、Float 则可以设置为增量更新字段, 把值设置在incrFields属性中,和操作"UPDATE"、"CREATE_ON_DUPLICATE_UPDATE"、"PARTIAL_MERGE"或者"FULL_MERGE"搭配表示对该字段增量更新: 例如A实体的字段count类型是Long, 如果该字段被设置为增量跟新字段,最终的效果是A.count = A.count + ? ; 对于减少字段的值也用该方法表示,可以通过传入负值的入参达到减值的效果 - - **写方案设计元素的表达**: + - 提取需求中的更新部分需求,根据上下文信息构建出一个写更新方案 + - 上下文中输入了备选聚合对象的的定义,每个聚合对象定义了多个实体的嵌套关系 + - 先明确聚合内实体的父子关系层级 + - 根据需求提取出变更相关的名称 + - 根需求提取出变更相关的描述 + - 根据需求选择确定变更范围,选出一个最合适的聚合对象;注意: 最多只能选择一个聚合对象 + - 根据需求在选定的聚合对象按照对象的粒度,选出所需变更对象 + - 根据需求选定变更对象的字段;每个选定的对象至少需要选定一个字段,如果无法确定任何字段,默认选择全部字段, 字段必须明确指定名称,不能类似“所有字段”的模糊表达 + - 每个对象上可以设置对应的一个操作,操作包括:CREATE(创建),UPDATE(更新), DELETE(删除), CREATE_ON_DUPLICATE_UPDATE(创建或者更新),FULL_MERGE( 批量更新列表数据,根据传入的列表数据,一条一条执行CREATE_ON_DUPLICATE_UPDATE的操作;并且删除掉老的列表中不在传入列表数据中的部分), PARTIAL_MERGE(批量更新列表数据,根据传入的列表数据,一条一条执行CREATE_ON_DUPLICATE_UPDATE的操作) + - 批量更新列表数据(FULL_MERGE或者PARTIAL_MERGE),则操作方案需要包含它的父对象 + - FULL_MERGE和PARTIAL_MERGE只能用在列表属性上, 单值属性请使用CREATE_ON_DUPLICATE_UPDATE + - 操作是 UPDATE、DELETE、CREATE_ON_DUPLICATE_UPDATE、FULL_MERGE、PARTIAL_MERGE的时候必须且只能指定一个唯一键(unique_key)(包括主键),用于确定对应的数据记录,主键对应的字段也要包含在fields字段中; + - 选定的对象层级不能跳跃;如果发生不连续,去掉不连续的叶子对象 + - 注意父对象和下一层子对象的操作类型有如下限制,必须严格检查以下规则 + - 如果父对象的操作是DELETE,则下一层子对象不能选择,也不能能设定任何操作 + - 如果父对象是的操作是CREATE,则下一层子对象只能选择CREATE + - 如果父对象是CREATE_ON_DUPLICATE_UPDATE,子对象只能是CREATE或者CREATE_ON_DUPLICATE_UPDATE或者FULL_MERGE或者PARTIAL_MERGE + - 如果父对象是FULL_MERGE,子对象只能是FULL_MERGE或者PARTIAL_MERGE或者是CREATE + - 对于选定的字段列表中的字段,如果字段的类型是Long、BigDecimal、Float 则可以设置为增量更新字段, 把值设置在incrFields属性中,和操作"UPDATE"、"CREATE_ON_DUPLICATE_UPDATE"、"PARTIAL_MERGE"或者"FULL_MERGE"搭配表示对该字段增量更新: 例如A实体的字段count类型是Long, 如果该字段被设置为增量跟新字段,最终的效果是A.count = A.count + ? ; 对于减少字段的值也用该方法表示,可以通过传入负值的入参达到减值的效果 + - **写方案设计元素的表达**: - 以json格式表达,json schema 定义如下: - ```json - { - "type":"object", - "description":"写方案定义", - "properties":{ - "uuid":{"type":"string","description":"写方案设计元素的唯一标识,在创建的时候不传;更新的时候必须传递"}, - "name":{"type":"string","description":"写方案名称,英语表示,单词之间使用下划线连接,长度限制在32个字符之内"}, - "description":{"type":"string","description":"写方案描述,长度限制在256个字符内"}, - "bo": {"type":"string","description":"聚合根的名称,确定了唯一聚合"}, - "operations": { - "type":"array","description":"定义了对写方案涉及到的相关实体的操作,包括操作类型和相关字段", - "items":{ - "type": "object", "description":"定义了对具体一个实体的操作,包括操作类型和相关字段", - "properties":{ - "bo":{"type":"string","description":"具体操作的实体"}, - "action":{"type":"string","description":"对具体实体的操作"}, - "uniqueKey": { - "type":"array", - "description":"唯一键包含的字段列表,该唯一键用于确定数据记录", - "items":{"type":"string","description":"字段名称,组成唯一键的字段名称"} - }, - "fields":{ - "type":"array", - "items":{"type":"string","description":"字段名称,必须来自bo对象"}, - "description":"对选定的Bo的操作的字段列表" - }, - "incrFields":{ - "type":"array", - "items":{ "type":"string", "description":"增量字段名称"}, - "description":"选定的字段中进行增量操作的字段列表" - }, - "required":["bo","action","fields"] - } - } + 以json格式表达,json schema 定义如下: + ```json + { + "type":"object", + "description":"写方案定义", + "properties":{ + "uuid":{"type":"string","description":"写方案设计元素的唯一标识,在创建的时候不传;更新的时候必须传递"}, + "name":{"type":"string","description":"写方案名称,英语表示,单词之间使用下划线连接,长度限制在32个字符之内"}, + "description":{"type":"string","description":"写方案描述,长度限制在256个字符内"}, + "bo": {"type":"string","description":"聚合根的名称,确定了唯一聚合"}, + "operations": { + "type":"array","description":"定义了对写方案涉及到的相关实体的操作,包括操作类型和相关字段", + "items":{ + "type": "object", "description":"定义了对具体一个实体的操作,包括操作类型和相关字段", + "properties":{ + "bo":{"type":"string","description":"具体操作的实体"}, + "action":{"type":"string","description":"对具体实体的操作"}, + "uniqueKey": { + "type":"array", + "description":"唯一键包含的字段列表,该唯一键用于确定数据记录", + "items":{"type":"string","description":"字段名称,组成唯一键的字段名称"} + }, + "fields":{ + "type":"array", + "items":{"type":"string","description":"字段名称,必须来自bo对象"}, + "description":"对选定的Bo的操作的字段列表" + }, + "incrFields":{ + "type":"array", + "items":{ "type":"string", "description":"增量字段名称"}, + "description":"选定的字段中进行增量操作的字段列表" + }, + "required":["bo","action","fields"] + } + } + } + }, + "required":["name","description","operations","bo"] } - }, - "required":["name","description","operations","bo"] - } - ``` + ``` - **例子** - - **上下文:** + - **上下文:** ```java public class meeting {//会议聚合 //唯一键:(id)、(meeting_name) @@ -1000,8 +1000,8 @@ DateTime endTime; //会议结束时间 Long friend_count;//好友数量 } ``` - - **需求:** 创建会议 - - 对应的写方案定义: + - **需求:** 创建会议 + - 对应的写方案定义: ```json { "name": "create_meeting", @@ -1021,26 +1021,26 @@ DateTime endTime; //会议结束时间 } ``` - **代码产物和修改建议** - - **Service:** - * **生成产物:** 在对应的聚合服务BoService里生成一个函数 - * **函数命名规则:** 和写方案同名 - * **职责:** 按需对入参进行转换,调用BaseBOService里的函数完成对聚合对象的操作, 实现对数据库的写操作; - * **BoService的类路径:** ```**.service``` - * **唯一标识符位置:** 其对应的标识符在函数的注解@AutoGenerated中指定, uuid规则: ${WritePlan在TOCO中的uuid} + - **Service:** + * **生成产物:** 在对应的聚合服务BoService里生成一个函数 + * **函数命名规则:** 和写方案同名 + * **职责:** 按需对入参进行转换,调用BaseBOService里的函数完成对聚合对象的操作, 实现对数据库的写操作; + * **BoService的类路径:** ```**.service``` + * **唯一标识符位置:** 其对应的标识符在函数的注解@AutoGenerated中指定, uuid规则: ${WritePlan在TOCO中的uuid} - **BaseBoService** - - **生成产物:** 在对应的聚合的BaseBoService里生成一系列函数,根据入参完成对聚合对象的变更 - - **职责** 增对每个实体的生成增删改的函数,并且根据参数的结构以及聚合的结构,构建嵌套的调用逻辑,完成对一个聚合对象的变更,记录并且返回对应的变更情况 - - **类路径:** ```**.service.base``` + - **生成产物:** 在对应的聚合的BaseBoService里生成一系列函数,根据入参完成对聚合对象的变更 + - **职责** 增对每个实体的生成增删改的函数,并且根据参数的结构以及聚合的结构,构建嵌套的调用逻辑,完成对一个聚合对象的变更,记录并且返回对应的变更情况 + - **类路径:** ```**.service.base``` - **BTO (业务变更传输对象)** - * **生成产物:** 一个Java类,以内部类的方式表示层级结构 - * **命名规则:** 一个Bto结尾(${WritePlanName}Bto,驼峰展示) - * **类路径:** 位于```**.service.bto```包路径下 - * **职责:** 在**TOCO**中,BTO为写方案的参数结构,每个写方案会对应一个BTO,写方案调用方按照BTO的结构向写方案生成的RPC方法传入需要查询的实体字段值,完成对数据库的变更 - * **唯一标识符位置:** 其对应的唯一标识符在类注解@AutoGenerated中指定, uuid规则: ${WritePlan在TOCO中的uuid}|BTO|DEFINITION + * **生成产物:** 一个Java类,以内部类的方式表示层级结构 + * **命名规则:** 一个Bto结尾(${WritePlanName}Bto,驼峰展示) + * **类路径:** 位于```**.service.bto```包路径下 + * **职责:** 在**TOCO**中,BTO为写方案的参数结构,每个写方案会对应一个BTO,写方案调用方按照BTO的结构向写方案生成的RPC方法传入需要查询的实体字段值,完成对数据库的变更 + * **唯一标识符位置:** 其对应的唯一标识符在类注解@AutoGenerated中指定, uuid规则: ${WritePlan在TOCO中的uuid}|BTO|DEFINITION - **例子:** - - 创建用户以及用户设置的写方案create_user_and_setting,生成CreateUserAndSettingBto, 在用户的聚合(UserBO)对应的UserBOService中生成函数createUserAndSetting,该函数调用BaseUserBOService中生成的createUserAndSetting, 其中在BaseUserBOService中还生成了createUser和createSetting的函数, 一起完成了用户的创建和设置创建的逻辑。 + - 创建用户以及用户设置的写方案create_user_and_setting,生成CreateUserAndSettingBto, 在用户的聚合(UserBO)对应的UserBOService中生成函数createUserAndSetting,该函数调用BaseUserBOService中生成的createUserAndSetting, 其中在BaseUserBOService中还生成了createUser和createSetting的函数, 一起完成了用户的创建和设置创建的逻辑。 - **修改建议:** - - 不能修改BaseBoService中的函数,不建议修改BTO文件。建议在BOService中进行手动代码扩展,处理可能被复用的修改前后的逻辑,如修改数据库的前后值对比、或常被复用的校验逻辑(业务不变性校验逻辑除外)、需要经常在一个事务内执行的其他写操作等。 + - 不能修改BaseBoService中的函数,不建议修改BTO文件。建议在BOService中进行手动代码扩展,处理可能被复用的修改前后的逻辑,如修改数据库的前后值对比、或常被复用的校验逻辑(业务不变性校验逻辑除外)、需要经常在一个事务内执行的其他写操作等。 #### **2.12 业务变更传输对象(BTO)** - **定义与用途:** 在TOCO中,BTO为写方案自动生成的参数结构,每个写方案会生成一个BTO。BTO为写方案选定的操作实体根据关系形成的树形集合,最外层为聚合根。写方案调用方按照BTO的结构向写方案生成的RPC方法传入需要操作的实体字段值,完成对数据库的写操作 @@ -1087,8 +1087,8 @@ DateTime endTime; //会议结束时间 结构中一些关键字段描述如下: requestParams为请求参数列表,response为返回结构,requestParams中每个参数和response的结构相同,其中:name为参数名;type为参数类型,参数类型取值范围为Boolean,String,Integer,Long,Float,Double,BigDecimal,Date,ByteArray,Enum,Eo,List,Dto,Qto,Bto,Void,其中参数不能为Void,如果不需要返回值,则type设置为Void;description为描述;typeUuid为参数对应类结构的UUID,当type为Enum、Eo、Dto、Qto、Bto时包含该字段;innerType为List内部类型,当type为List时包含该字段;innerUuid为List内部类结构的UUID,当type为List且innerType为Enum、Eo、Dto、Qto、Bto时包含该字段。 - **代码产物和修改建议** - - **生成代码:** RPC会在service层中生成类文件及实现函数,包含DTO自动生成的RPC如UserDtoService.getById或UserBaseDtoService.getById、读写方案自动生成的RPC如UserDtoQueryService.queryByListQto、UserBOService.createUser、自定义RPC如UserCustomService.customMethod。特别注意公开的RPC才可被其他模块使用,RPC被订阅后会生成RpcAdapter适配器,其他模块通过RpcAdapter才可调用该方法。如Order模块订阅了User模块的UserDtoService.getById,则会在Order模块中生成UserDtoServiceInOrderRpcAdapter.getById方法,Order模块中的代码必须通过@Resource private UserDtoServiceInOrderRpcAdapter userDtoServiceInOrderRpcAdapter;注入适配器后才可进行方法调用 - - **修改建议:** 建议修改RPC方法,不建议修改RPC方法签名、适配器中的内容 + - **生成代码:** RPC会在service层中生成类文件及实现函数,包含DTO自动生成的RPC如UserDtoService.getById或UserBaseDtoService.getById、读写方案自动生成的RPC如UserDtoQueryService.queryByListQto、UserBOService.createUser、自定义RPC如UserCustomService.customMethod。特别注意公开的RPC才可被其他模块使用,RPC被订阅后会生成RpcAdapter适配器,其他模块通过RpcAdapter才可调用该方法。如Order模块订阅了User模块的UserDtoService.getById,则会在Order模块中生成UserDtoServiceInOrderRpcAdapter.getById方法,Order模块中的代码必须通过@Resource private UserDtoServiceInOrderRpcAdapter userDtoServiceInOrderRpcAdapter;注入适配器后才可进行方法调用 + - **修改建议:** 建议修改RPC方法,不建议修改RPC方法签名、适配器中的内容 #### **2.14 应用程序接口 (API)** - **定义与用途:** 在TOCO中,API用于定义对外暴露的HTTP接口 - **如何创建/生成:** API一般为通过TOCO创建,需指定具体的参数和返回值等 @@ -1123,152 +1123,152 @@ requestParams为请求参数列表,response为返回结构,requestParams中 结构中一些关键字段描述如下: requestParams为请求参数列表,response为返回结构,requestParams中每个参数和response的结构相同,其中:name为参数名;type为参数类型,参数类型取值范围为Boolean,String,Integer,Long,Float,Double,BigDecimal,Date,ByteArray,Enum,Eo,List,Vo,Qto,Bto,Void,其中参数不能为Void,如果不需要返回值,则type设置为Void;description为描述;typeUuid为参数对应类结构的UUID,当type为Enum、Eo、Vo、Qto、Bto时包含该字段;innerType为List内部类型,当type为List时包含该字段;innerUuid为List内部类结构的UUID,当type为List且innerType为Enum、Eo、Vo、Qto、Bto时包含该字段。 - **代码产物和修改建议** - - **生成代码:** API会在entrance层生成Controller以及对应的API方法 - - **修改建议:** 建议修改API方法的实现内容,不建议修改API方法签名、URI + - **生成代码:** API会在entrance层生成Controller以及对应的API方法 + - **修改建议:** 建议修改API方法的实现内容,不建议修改API方法签名、URI #### **2.15 流程服务(FunctionFlow)** - **定义与用途:** TOCO针对复杂业务拆解,定义了流程服务,把一个复杂的业务过程,根据业务逻辑的内聚性,合并逻辑功能,把流程分解成流程节点,最终构造出一个类似工作流的逻辑流程;最终实现复杂业务流程分解,提升代码的可维护性。TOCO内嵌了流程引擎,在Function_Flow生成代码后,可以在流程引擎中执行 - **何时使用:** - - 如果一个API/RPC中涉及的写服务超过3个,则推荐使用流程服务 - - 当用户要求使用流程服务 + - 如果一个API/RPC中涉及的写服务超过3个,则推荐使用流程服务 + - 当用户要求使用流程服务 - **节点的封装:** - - 因为TOCO是一个面向数据处理的系统,所以数据内聚为首要考虑因素;一个节点通常围绕一个核心写服务,包括取数的读服务,为写服务的入参进行数据处理和转换,以及该写服务完成后的一些附属功能, 除了条件节点,入参校验,最终数据返回节点,每个节点至少包括一个写服务。 + - 因为TOCO是一个面向数据处理的系统,所以数据内聚为首要考虑因素;一个节点通常围绕一个核心写服务,包括取数的读服务,为写服务的入参进行数据处理和转换,以及该写服务完成后的一些附属功能, 除了条件节点,入参校验,最终数据返回节点,每个节点至少包括一个写服务。 - **关键配置:** 名称(小写字母+下划线),拆解复杂业务逻辑,如果业务流程比较简单,则不需要使用流程服务 - **如何创建/生成:** - - 流程不是逻辑的伪代码,不需要表达全部逻辑细节,相关有内聚性,相似性的功能逻辑需要被封装到一个流程节点内;例如用户注册:创建用户需要,需要创建账号信息,需要创建用户信息,并且发送一个通知消息,这几个功能都属于创建用户相关信息,而且没有逻辑分叉,因此可以内聚在一个逻辑节点内。 - - 流程节点之间的参数和返回值的传递通过一个统一的上下文Context传递 - - 定义了JSON结构,用于表达逻辑流。流程节点分为 “顺序节点”、“条件节点”,“选择节点”,"开始节点“ ,节点之间通过有向边连接,表示逻辑的执行方向;从开始节点开始,可达各个节点。 - - 节点的边的定义如下:顺序节点可以有多条入边,可以有多条出边(一个出边表示下一个执行的节点,多条出边表示几个分支并发执行);条件节点可由多条入边,可以有2条出边,表示条件为True和False两种逻辑分支;选择节点可以有多条入边,可以有多条出边,每条出边表示一种选择路径;开始节点只能有一条出边,不能有入边; - - ”条件节点“只封装条件判断逻辑,返回TRUE或者FALSE;”循环节点“只封装条件判断逻辑,返回TRUE或者FALSE;”选择节点“只封装分支选择逻辑,返回下游分支节点的名称; - - 终止流程:直接抛出异常 - - 一般校验逻辑放在一个“顺序节点"中,如果校验失败,以异常的方式抛出 - - 节点可以复用,同样的功能可以抽取出来封装到一个节点中 - - **流程服务设计元素的表达:** - - 以Json表达,Json Schema 定义如下: - ```json - { - "type": "object","description": "流程服务设计元素", "required": [ "description","name","nodes","edges"], - "properties": - { - "name": { "type": "string", "description": "流程名称,英语描述,单词之间使用下划线分割,总称不超过32个字符" }, - "moduleName": {"type": "string", "description": "模块名称,创建流程时传入,指定流程服务所属的模块"}, - "uuid": {"type": "string", "description": "流程服务设计元素的uuid,创建流程不传入,在更新的时候必须传入"}, - "description": {"type": "string", "description": "流程服务描述, 总长度控制在200个字符内"}, - "nodes": { - "type": "array", "description": "流程服务的节点列表", - "items": { - "type": "object","description": "流程服务节点对象", "required": ["name", "type", "description"], - "properties": { - "name": {"type": "string", "description": "节点名称, 英语描述,单词之间使用下划线分割,总称不超过32个字符"}, - "type": {"type": "string", "description": "节点 类型,可以是 PROCESS_NODE(顺序节点)、SWITCH_NODE(选择节点)、CONDITION_NODE(条件节点),START_NODE(开始节点)"}, - "description": {"type": "string", "description": "描述节点的功能, 英语描述,单词之间使用下划线分割,总称不超过200个字符" } - } - } - }, - "edges": { - "type": "array", "description": "流程服务的边列表", - "items": { - "type": "object","description": "流程服务的边", "required": ["fromNode", "toNode"], - "properties": { - "fromNode": {"type": "string", "description": "边的开始节点名称"}, - "toNode": {"type": "string", "description": "边的结束节点名称"}, - "value": {"type": "boolean", "description": "可选,作为条件节点的出边,true值表示条件匹配的分支,false表示条件不匹配的分支; 作为循环节点的出边,true值表示进入循环,false值表示退出循环"} - } - } - } - } - } - ``` - - 例子:用户注册流程 - ``` - { - "moduleName":"user",//该流程所属的模块 - "name":"user_register",//定义该流程的功能 - "description":"注册用户",//描述该流程的详细功能 - "nodes":[ // 定义该流程包含的节点 + - 流程不是逻辑的伪代码,不需要表达全部逻辑细节,相关有内聚性,相似性的功能逻辑需要被封装到一个流程节点内;例如用户注册:创建用户需要,需要创建账号信息,需要创建用户信息,并且发送一个通知消息,这几个功能都属于创建用户相关信息,而且没有逻辑分叉,因此可以内聚在一个逻辑节点内。 + - 流程节点之间的参数和返回值的传递通过一个统一的上下文Context传递 + - 定义了JSON结构,用于表达逻辑流。流程节点分为 “顺序节点”、“条件节点”,“选择节点”,"开始节点“ ,节点之间通过有向边连接,表示逻辑的执行方向;从开始节点开始,可达各个节点。 + - 节点的边的定义如下:顺序节点可以有多条入边,可以有多条出边(一个出边表示下一个执行的节点,多条出边表示几个分支并发执行);条件节点可由多条入边,可以有2条出边,表示条件为True和False两种逻辑分支;选择节点可以有多条入边,可以有多条出边,每条出边表示一种选择路径;开始节点只能有一条出边,不能有入边; + - ”条件节点“只封装条件判断逻辑,返回TRUE或者FALSE;”循环节点“只封装条件判断逻辑,返回TRUE或者FALSE;”选择节点“只封装分支选择逻辑,返回下游分支节点的名称; + - 终止流程:直接抛出异常 + - 一般校验逻辑放在一个“顺序节点"中,如果校验失败,以异常的方式抛出 + - 节点可以复用,同样的功能可以抽取出来封装到一个节点中 + - **流程服务设计元素的表达:** + - 以Json表达,Json Schema 定义如下: + ```json { - "name":"start", - "type":"START_NODE", - "description": "起始节点" - }, - { - "name":"check_user_registed", - "type":"CONDITION_NODE", - "description":"校验入参合法性;判断昵称是否重名;判断用户是否已注册" - }, - { - "name": "create_user", - "descripiton":"创建用户,发送'用户创建'消息, 给用户发送欢迎通知", - "type": "PROCESS_NODE" - }, - { - "name":"return_user_info", - "descripiton":"返回用户信息,返回推荐给用户可能感兴趣的好友", - "type":"PROCESS_NODE" - } - ], - "edges": [ - { - "fromNode": "start", - "toNode":"check_user_registed" - }, - { - "fromNode":"check_user_registed", - "toNode":"create_user", - "value":false - }, - { - "fromNode":"check_user_registed" - "toNode":"return_user_info", - "value":true - }, - { - "fromNode":"create_user", - "toNode":"return_user_info" - } - ] - } - ``` + "type": "object","description": "流程服务设计元素", "required": [ "description","name","nodes","edges"], + "properties": + { + "name": { "type": "string", "description": "流程名称,英语描述,单词之间使用下划线分割,总称不超过32个字符" }, + "moduleName": {"type": "string", "description": "模块名称,创建流程时传入,指定流程服务所属的模块"}, + "uuid": {"type": "string", "description": "流程服务设计元素的uuid,创建流程不传入,在更新的时候必须传入"}, + "description": {"type": "string", "description": "流程服务描述, 总长度控制在200个字符内"}, + "nodes": { + "type": "array", "description": "流程服务的节点列表", + "items": { + "type": "object","description": "流程服务节点对象", "required": ["name", "type", "description"], + "properties": { + "name": {"type": "string", "description": "节点名称, 英语描述,单词之间使用下划线分割,总称不超过32个字符"}, + "type": {"type": "string", "description": "节点 类型,可以是 PROCESS_NODE(顺序节点)、SWITCH_NODE(选择节点)、CONDITION_NODE(条件节点),START_NODE(开始节点)"}, + "description": {"type": "string", "description": "描述节点的功能, 英语描述,单词之间使用下划线分割,总称不超过200个字符" } + } + } + }, + "edges": { + "type": "array", "description": "流程服务的边列表", + "items": { + "type": "object","description": "流程服务的边", "required": ["fromNode", "toNode"], + "properties": { + "fromNode": {"type": "string", "description": "边的开始节点名称"}, + "toNode": {"type": "string", "description": "边的结束节点名称"}, + "value": {"type": "boolean", "description": "可选,作为条件节点的出边,true值表示条件匹配的分支,false表示条件不匹配的分支; 作为循环节点的出边,true值表示进入循环,false值表示退出循环"} + } + } + } + } + } + ``` + - 例子:用户注册流程 + ``` + { + "moduleName":"user",//该流程所属的模块 + "name":"user_register",//定义该流程的功能 + "description":"注册用户",//描述该流程的详细功能 + "nodes":[ // 定义该流程包含的节点 + { + "name":"start", + "type":"START_NODE", + "description": "起始节点" + }, + { + "name":"check_user_registed", + "type":"CONDITION_NODE", + "description":"校验入参合法性;判断昵称是否重名;判断用户是否已注册" + }, + { + "name": "create_user", + "descripiton":"创建用户,发送'用户创建'消息, 给用户发送欢迎通知", + "type": "PROCESS_NODE" + }, + { + "name":"return_user_info", + "descripiton":"返回用户信息,返回推荐给用户可能感兴趣的好友", + "type":"PROCESS_NODE" + } + ], + "edges": [ + { + "fromNode": "start", + "toNode":"check_user_registed" + }, + { + "fromNode":"check_user_registed", + "toNode":"create_user", + "value":false + }, + { + "fromNode":"check_user_registed" + "toNode":"return_user_info", + "value":true + }, + { + "fromNode":"create_user", + "toNode":"return_user_info" + } + ] + } + ``` - **代码产物和修改建议** - - **FlowConfig** - * **生成产物:** 每个模块在service层的生成一个Java类,负责注册模块下的所有流程到执行器 - * **命名规则:** 类名为${moduleName}FlowConfig - * **职责:** 在应用启动的时候注册模块内的所有的流程服务到执行器 - * **类路径:** ```**.service.flow``` - - **Service** - * **生成产物:** 在service层的以模块名为类名前缀的${moduleName}FlowService中生成一个流程的入口函数 - * **函数命名规则:** 流程名为方法名为后缀:pubic void invoke${functionFlowName}(${functionFlowName}Context context) - * **职责:** 在代码逻辑中,使用该流程需要以该函数作为调用入口 - * **类路径:** ```**.service``` - * **唯一标识符位置:** 其对应的标识符在函数的注解@AutoGenerated中指定, uuid规则: ${FunctionFlow在TOCO中的uuid}|FLOW|METHOD - - **FlowNode** - * **生成产物:** 在service层生成一个Java类, **注意** 每个FunctionFlow的开始节点(StartNode)不生成 - * **类命名规则:** ${nodeName}Node - * **入口函数命名:** pubic void process() - * **职责:** 用于封装内聚性的业务逻辑 - * **类路径:** ```service.flow.node.${functionFlowName}``` - - **FlowContext** - * **函数命名规则:** {nodeName}Node - * **职责:** 作为流程节点之间的参数传递(包括出参和入参),在实现业务逻辑的时候,按需在这个上下文类中添加所需的字段 - * **类路径:** ```**.service.flow.context``` - * **唯一标识符位置:** 其对应的标识符在类注解@AutoGenerated中指定, uuid规则: ${FunctionFlow在TOCO中的uuid}|FLOW|CONTEXT - - **例子:** - - 用户登录,在UserFlowService中生成一个函数invokeLoginFlow,该函数通过流程框架根据流程定义调用LoginNode,LoginNode中封装了用户登录的逻辑,LoginFlowContext中封装了用户登录的参数和结果。 - - **修改建议:** 不修改 service 中的函数, 不修改FlowConfig, 可以修改FlowContext, 添加/修改出入参数, 修改FlowNode中的具体业务逻辑。 + - **FlowConfig** + * **生成产物:** 每个模块在service层的生成一个Java类,负责注册模块下的所有流程到执行器 + * **命名规则:** 类名为${moduleName}FlowConfig + * **职责:** 在应用启动的时候注册模块内的所有的流程服务到执行器 + * **类路径:** ```**.service.flow``` + - **Service** + * **生成产物:** 在service层的以模块名为类名前缀的${moduleName}FlowService中生成一个流程的入口函数 + * **函数命名规则:** 流程名为方法名为后缀:pubic void invoke${functionFlowName}(${functionFlowName}Context context) + * **职责:** 在代码逻辑中,使用该流程需要以该函数作为调用入口 + * **类路径:** ```**.service``` + * **唯一标识符位置:** 其对应的标识符在函数的注解@AutoGenerated中指定, uuid规则: ${FunctionFlow在TOCO中的uuid}|FLOW|METHOD + - **FlowNode** + * **生成产物:** 在service层生成一个Java类, **注意** 每个FunctionFlow的开始节点(StartNode)不生成 + * **类命名规则:** ${nodeName}Node + * **入口函数命名:** pubic void process() + * **职责:** 用于封装内聚性的业务逻辑 + * **类路径:** ```service.flow.node.${functionFlowName}``` + - **FlowContext** + * **函数命名规则:** {nodeName}Node + * **职责:** 作为流程节点之间的参数传递(包括出参和入参),在实现业务逻辑的时候,按需在这个上下文类中添加所需的字段 + * **类路径:** ```**.service.flow.context``` + * **唯一标识符位置:** 其对应的标识符在类注解@AutoGenerated中指定, uuid规则: ${FunctionFlow在TOCO中的uuid}|FLOW|CONTEXT + - **例子:** + - 用户登录,在UserFlowService中生成一个函数invokeLoginFlow,该函数通过流程框架根据流程定义调用LoginNode,LoginNode中封装了用户登录的逻辑,LoginFlowContext中封装了用户登录的参数和结果。 + - **修改建议:** 不修改 service 中的函数, 不修改FlowConfig, 可以修改FlowContext, 添加/修改出入参数, 修改FlowNode中的具体业务逻辑。 ### **3 生成代码产物补充说明** - **3.1.1 支持的语言/框架** Java、SpringBoot、MyBatis-plus(读)、Hibernate(写),具体的项目架构如下 ``` TOCO生成的项目是一个多模块的SpringBoot项目,包括主模块和子模块 -|──main_module -│ ├── common # 项目级别公共的基础模块 -│ │ ├──config/ # 中间件配置 -│ │ ├──constants/ # 项目级别常量 -│ │ ├──enums/ # 项目级别枚举 -│ │ ├──redis/ # Redis配置 -│ │ ├──response/ # 返回结果封装 -│ │ └──utils/ # 项目级别工具类 -└── │── entrance/ + |──main_module + │ ├── common # 项目级别公共的基础模块 + │ │ ├──config/ # 中间件配置 + │ │ ├──constants/ # 项目级别常量 + │ │ ├──enums/ # 项目级别枚举 + │ │ ├──redis/ # Redis配置 + │ │ ├──response/ # 返回结果封装 + │ │ └──utils/ # 项目级别工具类 + └── │── entrance/ │ └──AppApplication.java # 项目启动类 └── modules # 子模块列表 └── module1/ # 子模块1 @@ -1290,17 +1290,16 @@ requestParams为请求参数列表,response为返回结构,requestParams中 │ ├── converter/ # 复杂Dto(非BaseDto)组装 │ ├── facade/ # 调用其他模块的RPC接口 │ └── impl/ # Dto的查询接口的实现 - ├── persist/ + ├── persist/ │ ├── dos/ # 数据库单表结构的映射 │ ├── qto/ # 读方案的数据库查询实现 │ └── mapper/ # MyBatis的Mapper定义 └── service/ # BoService、 DtoService - ├── bto/ # 写方案入参的定义 - ├── converter/ # - ├── query/ # 查询方案的service层入口,调用persist层的查询实现 - └── base/ # 每个BoService对应的基类 - -在一个子模块的内,它的依赖层级为entrance -> service -> manager -> persist, 同时各个层都依赖 common + ├── bto/ # 写方案入参的定义 + ├── converter/ # + ├── query/ # 查询方案的service层入口,调用persist层的查询实现 + └── base/ # 每个BoService对应的基类 + 在一个子模块的内,它的依赖层级为entrance -> service -> manager -> persist, 同时各个层都依赖 common ``` - **3.1.2 特殊注解及含义**