diff --git a/knowledge.md b/knowledge.md index 588240d..7698240 100644 --- a/knowledge.md +++ b/knowledge.md @@ -962,77 +962,80 @@ meeting_with_room_and_agenda_wo示例: * 根据用户名称查询用户列表返回UserDTO,生成UserNameQto、UserNameQtoService、UserNameQtoDao、UserNameQueryService; UserNameQueryService调用UserNameQtoService,UserNameQtoService调用UserNameQtoDao - **修改建议:** - 如有结果数据二次处理,建议在QueryService和QueryExecutor中扩展代码,不建议修改QTO文件 - + #### **2.10 查询传输对象(QTO)** -- **定义与用途:** 在TOCO中,QTO为读方案的查询参数结构,每个读方案会对应一个QTO,读方案调用方按照QTO的结构向读方案生成的RPC方法传入需要查询的实体字段值,完成对数据库的查询 -- **如何创建/生成:** 在创建读方案后,TOCO会自动生成QTO作为该读方案传入的查询参数结构,无需单独创建 -- **关键配置:** 名称(${ReadPlanNameQto},驼峰展示),查询字段列表(如idIs,nameLike, schoolNameLike等) -- **与API的关系:** QTO通常可作为API的参数,API接收到参数后可直接透传给内部的RPC进行调用,注意QTO只用作读操作的参数,**禁止用作写参数结构** +- **定义与用途:** QTO是读方案的查询参数结构。每个读方案对应一个QTO。调用方使用QTO结构传入查询字段值,完成数据库查询 +- **如何创建/生成:** 创建读方案后,TOCO自动生成对应的QTO。无需单独创建 +- **关键配置:** 名称(${ReadPlanNameQto},驼峰命名),查询字段列表(如idIs,nameLike,schoolNameLike等) +- **与API的关系:** QTO可作为API参数。API接收参数后直接透传给内部RPC调用。QTO只用于读操作参数,**禁止用于写参数结构** #### **2.11 写方案 (WritePlan,单聚合操作)** -- **定义与用途:** TOCO针对写场景定义了一种写方案,所有对数据库的写操作都只能通过写方案实现(一个写方案只可以变更一个聚合的数据,无法同时操作多个聚合!)。写方案包含了对数据库表的写操作。每个写方案只能操作一个聚合内部的表,同时对一个聚合内表的操作尽量合并至一个写方案中(根据复用性、内聚性等方面具体情况具体分析)。注意写方案可以一次操作聚合内的多张表,如location和storey是同一个聚合,并且location和storey是**1:N**关系,location是父对象(聚合根),storey是子对象,那么我们可以创建一个写方案,同时更新单个聚合根location中的信息,以及操作其下的子表storey**列表**信息,如新增、删除、修改storey等;也可以创建一个写方案单独更新单个子表storey对象信息。 -- **关键配置:** 名称(小写字母+下划线,不要以write_plan结尾,全局唯一),操作的聚合,聚合内的实体和字段,对每个实体的操作类型:CREATE(创建**单个**实体),UPDATE(更新**单个**实体), DELETE(删除**单个**实体), CREATE_ON_DUPLICATE_UPDATE(创建或者更新**单个**实体),FULL_MERGE( 批量更新**列表**数据,根据传入的列表数据,一条一条执行CREATE_ON_DUPLICATE_UPDATE的操作;并且删除掉老的列表中不在传入列表数据中的部分), PARTIAL_MERGE(批量更新**列表**数据,根据传入的列表数据,一条一条执行) -- **与RPC的关系:** 对于每一个写方案,TOCO会自动生成一个RPC方法,其参数为写方案对应的BTO,返回值为本次操作的聚合根实体的主键值,内部只实现了对当前聚合的数据库操作 -- **生成的写方案RPC** 的使用: 写方案生成的RPC属于该写方案所属的模块,其他模块如果需要调用该写方案,需要先订阅该写方案RPC,在生成代码后使用adapter调用该读服务;当前模块则可以直接调用该写方案对应的Service -- **与聚合的关系:** 每个聚合下可以定义多个写方案,但每个写方案只能操作一个聚合内的表,无法同时操作多个聚合内的表 -- **如何创建/生成:** 创建写方案时需要先选定对应的聚合,以及要操作的聚合内部的实体,然后确定对每个实体的具体操作类型 - - 提取需求中的更新部分需求,根据上下文信息构建出一个写更新方案 - - 上下文中输入了备选聚合对象的的定义,每个聚合对象定义了多个实体的嵌套关系 - - 先明确聚合内实体的父子关系层级 - - 根据需求提取出变更相关的名称 - - 根需求提取出变更相关的描述 - - 根据需求选择确定变更范围,选出一个最合适的聚合对象;注意: 最多只能选择一个聚合对象 - - 根据需求在选定的聚合对象按照对象的粒度,选出所需变更对象 - - 根据需求选定变更对象的字段;每个选定的对象至少需要选定一个字段,如果无法确定任何字段,默认选择全部字段, 字段必须明确指定名称,不能类似“所有字段”的模糊表达 - - 每个对象上可以设置对应的一个操作,操作包括:CREATE(创建),UPDATE(更新), DELETE(删除), CREATE_ON_DUPLICATE_UPDATE(创建或者更新),FULL_MERGE( 批量更新列表数据,根据传入的列表数据,一条一条执行CREATE_ON_DUPLICATE_UPDATE的操作;并且删除掉老的列表中不在传入列表数据中的部分), PARTIAL_MERGE(批量更新列表数据,根据传入的列表数据,一条一条执行CREATE_ON_DUPLICATE_UPDATE的操作) - - 列表属性可以批量操作 - - 批量更新列表数据(FULL_MERGE或者PARTIAL_MERGE),则操作方案需要包含它的父对象 - - 批量删除列表数据,则操作方案需要包含父对象,父对象的操作设置为UPDATE - - 批量创建列表数据,则操作方案需要包含父对象,父对象的操作设置为UPDATE、或者是CREATE(当父对象也需要一起创建的时候) - - 举例:meeting对象有List agdendaList属性,如果需要批量创建agenda,则需要同时设置metting对象和meeting_agenda对象,并且对meeting_agenda设置create操作;这是会生成一个Bto参数,其中包含了 List agendaList的属性,用于批量传递参数;反正,如果只设置了meeting_agenda的create操作,生成的Bto参数,只包含了meeting_agenda实体的属性,表示一次只能创建一条数据; 同理对于UPDATE、DELETE也是类似的规则 - - FULL_MERGE和PARTIAL_MERGE只能用在列表属性上, 单值属性请使用CREATE_ON_DUPLICATE_UPDATE - - 操作是 UPDATE、DELETE、CREATE_ON_DUPLICATE_UPDATE、FULL_MERGE、PARTIAL_MERGE的时候必须且只能指定一个唯一键(unique_key)(包括主键),用于确定对应的数据记录,主键对应的字段也要包含在fields字段中; - - 唯一键(unique_key)的具体解释:在实体中,存在普通索引和唯一索引,这里的唯一键值的是唯一索引,由一个或多个字段组成 - - 选定的对象层级不能跳跃;如果发生不连续,去掉不连续的叶子对象 - - 注意父对象和下一层子对象的操作类型有如下限制,必须严格检查以下规则 - - 如果父对象的操作是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 、Integer 则可以设置为增量更新字段, 把值设置在incrFields属性中,和操作"UPDATE"、"CREATE_ON_DUPLICATE_UPDATE"、"PARTIAL_MERGE"或者"FULL_MERGE"搭配表示对该字段增量更新: 例如A实体的字段count类型是Long, 如果该字段被设置为增量跟新字段,最终的效果是A.count = A.count + ? ; 对于减少字段的值也用该方法表示,可以通过传入负值的入参达到减值的效果 - - **写方案设计元素的表达**: - - 以json格式表达,json schema 定义如下: +- **定义与用途:** 写方案是数据库写操作的唯一方式,每个写方案只能变更一个聚合的数据。写方案可以一次操作聚合内的多张表。例如:location(父对象/聚合根)和storey(子对象)是1:N关系,可创建写方案同时更新location信息和操作storey列表(新增/删除/修改);也可创建写方案单独更新单个storey对象。 +- **关键配置:** 名称(小写+下划线,不以write_plan结尾,全局唯一),操作的聚合,聚合内的实体和字段,每个实体的操作类型: + - CREATE:创建单个实体 + - UPDATE:更新单个实体 + - DELETE:删除单个实体 + - CREATE_ON_DUPLICATE_UPDATE:创建或更新单个实体 + - FULL_MERGE:批量更新列表数据,执行CREATE_ON_DUPLICATE_UPDATE操作,删除不在传入列表中的旧数据 + - PARTIAL_MERGE:批量更新列表数据,执行CREATE_ON_DUPLICATE_UPDATE操作 +- **与RPC关系:** 每个写方案自动生成一个RPC方法,参数为对应BTO,返回值为聚合根实体主键值,只实现当前聚合的数据库操作。 +- **生成RPC使用:** 写方案RPC属于所属模块,其他模块需先订阅该RPC,用adapter调用;当前模块直接调用对应Service。 +- **与聚合关系:** 每个聚合可定义多个写方案,但每个写方案只能操作一个聚合内的表。 +- **创建方式:** 先选定聚合和要操作的实体,再确定每个实体的操作类型 + - 提取需求中的更新需求,根据上下文构建写方案 + - 输入备选聚合对象定义,每个聚合定义多个实体的嵌套关系 + - 明确聚合内实体的父子层级关系 + - 提取需求相关的变更名称和描述 + - 选择最合适的聚合对象(最多只能选一个) + - 按对象粒度选出需变更对象 + - 选定变更对象的字段(每个对象至少选一个字段,无法确定时默认选全部,字段必须明确指定名称) + - 为每个对象设置操作类型 + - 列表属性可批量操作: + - 批量更新列表(FULL_MERGE/PARTIAL_MERGE):需包含父对象 + - 批量删除列表:需包含父对象,父对象设为UPDATE + - 批量创建列表:需包含父对象,父对象设为UPDATE或CREATE + - 示例:meeting对象有List agendaList,批量创建agenda需同时设置meeting和meeting_agenda对象,meeting_agenda设为CREATE;只设置meeting_agenda的CREATE则一次只能创建一条数据 + - FULL_MERGE和PARTIAL_MERGE只用于列表属性,单值属性用CREATE_ON_DUPLICATE_UPDATE + - UPDATE/DELETE/CREATE_ON_DUPLICATE_UPDATE/FULL_MERGE/PARTIAL_MERGE操作必须指定一个唯一键(包括主键),用于确定数据记录,主键字段也要包含在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/Integer类型字段可设为增量更新,设置在incrFields中,配合UPDATE/CREATE_ON_DUPLICATE_UPDATE/PARTIAL_MERGE/FULL_MERGE使用。例如:A实体count字段设为增量更新,效果是A.count = A.count + ?,传入负值可减值 + - **写方案设计元素表达:** ```json { "type":"object", "description":"写方案定义", "properties":{ - "uuid":{"type":"string","description":"写方案设计元素的唯一标识,在创建的时候不传;更新的时候必须传递"}, - "name":{"type":"string","description":"写方案名称,英语表示,单词之间使用下划线连接,长度限制在32个字符之内"}, - "description":{"type":"string","description":"写方案描述,长度限制在256个字符内"}, - "bo": {"type":"string","description":"聚合根的名称,确定了唯一聚合"}, + "uuid":{"type":"string","description":"写方案唯一标识,创建时不传,更新时必传"}, + "name":{"type":"string","description":"写方案名称,英文+下划线,32字符内"}, + "description":{"type":"string","description":"写方案描述,256字符内"}, + "bo": {"type":"string","description":"聚合根名称"}, "operations": { - "type":"array","description":"定义了对写方案涉及到的相关实体的操作,包括操作类型和相关字段", + "type":"array","description":"实体操作定义,包括操作类型和字段", "items":{ - "type": "object", "description":"定义了对具体一个实体的操作,包括操作类型和相关字段", + "type": "object", "description":"具体实体操作定义", "properties":{ - "bo":{"type":"string","description":"具体操作的实体"}, - "action":{"type":"string","description":"对具体实体的操作"}, + "bo":{"type":"string","description":"操作的实体"}, + "action":{"type":"string","description":"实体操作类型"}, "uniqueKey": { "type":"array", - "description":"唯一键包含的字段列表,该唯一键用于确定数据记录", - "items":{"type":"string","description":"字段名称,组成唯一键的字段名称"} + "description":"唯一键字段列表,用于确定数据记录", + "items":{"type":"string","description":"字段名称"} }, "fields":{ "type":"array", "items":{"type":"string","description":"字段名称,必须来自bo对象"}, - "description":"对选定的Bo的操作的字段列表" + "description":"操作字段列表" }, "incrFields":{ "type":"array", "items":{ "type":"string", "description":"增量字段名称"}, - "description":"选定的字段中进行增量操作的字段列表" + "description":"增量操作字段列表" }, "required":["bo","action","fields"] } @@ -1042,12 +1045,12 @@ meeting_with_room_and_agenda_wo示例: "required":["name","description","operations","bo"] } ``` -- **例子** - - **上下文:** - +- **示例:** + - **上下文:** + ```java public class meeting {//会议聚合 //唯一键:(id)、(meeting_name) - Long id; //主键id + Long id; //主键 String meeting_name; //会议名称 Date start_time; //开始时间 Date end_time;//结束时间 @@ -1075,10 +1078,10 @@ meeting_with_room_and_agenda_wo示例: String nickname;//昵称 Long friend_count;//好友数量 } - - - **需求:** 创建会议 - - 对应的写方案定义: - + ``` + - **需求:** 创建会议 + - **对应写方案定义:** + ```json { "name": "create_meeting", "description": "创建会议", @@ -1095,280 +1098,277 @@ meeting_with_room_and_agenda_wo示例: } ] } - -- **代码产物和修改建议** - - **BOService:** - * **生成产物:** 在对应的聚合服务BOService里生成一个函数 - * **函数命名规则:** 和写方案同名 - * **职责:** 按需对入参进行转换,调用BaseBOService里的函数完成对聚合对象的操作, 实现对数据库的写操作; - * **返回值:** 操作的聚合根实体记录的主键字段的值 - * **BOService的类路径:** **.service - * **唯一标识符位置:** 其对应的标识符在函数的注解@AutoGenerated中指定, uuid规则: ${WritePlan在TOCO中的uuid} -- **BaseBOService** - - **生成产物:** 在对应的聚合的BaseBOService里生成一系列函数,根据入参完成对聚合对象的变更 - - **职责** 增对每个实体的生成增删改的函数,并且根据参数的结构以及聚合的结构,构建嵌套的调用逻辑,完成对一个聚合对象的变更,记录并且返回对应的变更情况 - - **类路径:** **.service.base - - **禁止** 修改该类 -- **BTO (业务变更传输对象)** - * **生成产物:** 一个Java类,以内部类的方式表示层级结构, **注意** BTO只能由写方案生成,不能凭空创建 - * **命名规则:** 一个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的函数, 一起完成了用户的创建和设置创建的逻辑。 + ``` + +- **代码产物和修改建议:** + - **BOService:** + * **生成产物:** 在聚合服务BOService中生成一个函数 + * **函数命名:** 与写方案同名 + * **职责:** 转换入参,调用BaseBOService函数完成聚合对象操作,实现数据库写操作 + * **返回值:** 聚合根实体记录主键值 + * **类路径:** `**.service` + * **唯一标识:** 函数注解@AutoGenerated中指定,uuid规则:${WritePlan的uuid} +- **BaseBOService:** + - **生成产物:** 在聚合BaseBOService中生成系列函数,根据入参完成聚合对象变更 + - **职责:** 为每个实体生成增删改函数,根据参数结构和聚合结构构建嵌套调用逻辑,完成聚合对象变更,记录并返回变更情况 + - **类路径:** `**.service.base` + - **禁止:** 修改该类 +- **BTO (业务变更传输对象):** + * **生成产物:** Java类,以内部类方式表示层级结构,注意BTO只能由写方案生成,不能凭空创建 + * **命名规则:** 以Bto结尾(${WritePlanName}Bto,驼峰命名) + * **禁止:** 修改该类 + * **类路径:** `**.service.bto`包下 + * **职责:** 写方案的参数结构,每个写方案对应一个BTO,调用方按BTO结构向写方案RPC方法传入实体字段值,完成数据库变更 + * **唯一标识:** 类注解@AutoGenerated中指定,uuid规则:${WritePlan的uuid}|BTO|DEFINITION +- **示例:** + - 创建用户及设置的写方案create_user_and_setting,生成CreateUserAndSettingBto,在UserBOService中生成函数createUserAndSetting,该函数调用BaseUserBOService中生成的createUserAndSetting,其中BaseUserBOService还生成createUser和createSetting函数,一起完成用户创建和设置创建逻辑。 - **修改建议:** - - 不能修改BaseBOService中的函数,不建议修改BTO文件。建议在BOService中进行手动代码扩展,处理可能被复用的修改前后的逻辑,如修改数据库的前后值对比、或常被复用的校验逻辑(业务不变性校验逻辑除外)、需要经常在一个事务内执行的其他写操作等。 - - 父类函数会返回一个BoResult类,此类记录了各个Bto和Bo实例的对应关系,以及各个Bto实例的操作结果, 可以通过以下接口获取。 例如:在创建用户的写方案中,BoService里的入参为CreateUserBto 例如:在创建用户的写方案中,BoService里的入参为CreateUserBto,用户id由数据库生成,如果需要返回创建用户id,那么通过boResult.getAddedResult(createUserBto).getBo().getId()可以返回新建的用户id - + - 不能修改BaseBOService函数,不建议修改BTO文件。建议在BOService中扩展代码,处理可能被复用的前后逻辑,如数据库前后值对比、常被复用的校验逻辑(业务不变性校验除外)、需要在一个事务内执行的其他写操作等。 + - 父类函数返回BoResult类,记录各Bto和Bo实例对应关系及Bto实例操作结果,可通过以下接口获取。例如:创建用户的写方案中,BoService入参为CreateUserBto,用户id由数据库生成,需要返回创建用户id时,通过boResult.getAddedResult(createUserBto).getBo().getId()返回新建用户id + ```java /** * 获取更新成功的bto结果 - * - * @return */ public UpdatedBto getUpdatedResult(final Object bto) /** - * 获取成功插入的Bto的结果 - * @param btoObj - * @return + * 获取成功插入的Bto结果 */ public AddedBto getAddedResult(final Object btoObj) /** * 获取Bto对应的删除结果 - * - * @param btoObj - * @return */ public DeletedBto getDeletedResult(final Object btoObj) - - + ``` + ```java public class UpdatedBto { - //Bto 入参 - private Bto bto; - //Bto 对应的Entity,前项 - private Entity entity; - //bo,后项 - private BO bo; - } - - - //记录删除记录的情况 +//Bto入参 +private Bto bto; +//Bto对应的Entity,前项 +private Entity entity; +//bo,后项 +private BO bo; +} + ``` + ```java + //记录删除记录情况 @Setter @Getter public class DeletedBto { - //Bto 入参 - private Bto bto; - //Bto 对应的Entity,前项 - private Entity entity; +//Bto入参 +private Bto bto; +//Bto对应的Entity,前项 +private Entity entity; } - - + ``` + ```java //记录Bto创建的前后值 @Setter @Getter public class AddedBto { - //Bto 入参 - private Bto bto; - //bo 后项 - private BO bo; +//Bto入参 +private Bto bto; +//bo后项 +private BO bo; } - + ``` - #### **2.12 业务变更传输对象(BTO)** - - **定义与用途:** 在TOCO中,BTO为写方案自动生成的参数结构,每个写方案会生成一个BTO。BTO为写方案选定的操作实体根据关系形成的树形集合,最外层为聚合根。写方案调用方按照BTO的结构向写方案生成的RPC方法传入需要操作的实体字段值,完成对数据库的写操作 - - **如何创建/生成:** BTO只能由写方案自动创建,不能单独新建任何BTO。在创建写方案后,TOCO会自动生成一个BTO作为该写方案传入的参数结构。 - - **关键配置:** 名称(${WritePlanName}Bto,驼峰展示),嵌套的树形实体和字段列表,BTO内部的字段全部都来自Entity。以下为一个示例: - - class CreateUserBto { //对应实体user - Long id; //来自于user.id - String name; //来自于user.name - List pictureList; - class PictureBto { //对应实体picture - String url; //来自于picture.url +#### **2.12 业务变更传输对象(BTO)** +- **定义与用途:** 在TOCO中,BTO为写方案自动生成的参数结构,每个写方案会生成一个BTO。BTO为写方案选定的操作实体根据关系形成的树形集合,最外层为聚合根。写方案调用方按照BTO的结构向写方案生成的RPC方法传入需要操作的实体字段值,完成对数据库的写操作 +- **如何创建/生成:** BTO只能由写方案自动创建,不能单独新建任何BTO。在创建写方案后,TOCO会自动生成一个BTO作为该写方案传入的参数结构。 +- **关键配置:** 名称(${WritePlanName}Bto,驼峰展示),嵌套的树形实体和字段列表,BTO内部的字段全部都来自Entity。以下为一个示例: + + class CreateUserBto { //对应实体user + Long id; //来自于user.id + String name; //来自于user.name + List pictureList; + class PictureBto { //对应实体picture + String url; //来自于picture.url + } + } + +- **与API的关系:** BTO通常可作为API的参数,API接收到参数后可直接透传给内部的RPC进行调用,注意BTO只用作写操作的参数,**禁止用作查询参数结构** +- **复杂场景处理:** 对于涉及多个写方案的复杂API,可以: + a. 如果接口有一个主要的写场景,则创建一个主要的写方案,让其自动生成的BTO作为API参数,然后再增加其他基本类型的参数或EO + b. 使用基本类型参数或EO作为参数,在接口执行流程中将基本类型参数转换为各个写方案自动生成的BTO,再调用多个写方案 + c. 如果流程过于复杂,优先考虑使用基本类型参数或EO + +#### **2.13 服务层方法 (RPC)** +- **定义与用途:** 在TOCO中,RPC为服务层的方法。RPC按照可见性可以分为两种,一种是公开RPC,可以被其他模块订阅,订阅后可以通过RPC适配器进行调用;另一种是非公开RPC,只能被当前模块调用。非公开RPC可以被公开,从而被其他模块订阅并调用 +- **如何创建/生成:** RPC有4种创建方式:a.DTO创建后会自动创建RPC,RPC的公开性与DTO的公开性保持一致;b.返回DTO的读方案会根据分页情况、以及是否生成计数函数的配置自动生成非公开的RPC c.写方案创建后会自动生成非公开的RPC,返回值为操作的聚合根实体记录的主键字段的值 d.如果上述三种RPC无法满足需求,则可以通过TOCO创建自定义RPC完整指定功能,需指定具体的参数和返回值以及公开性等。 +- **优先复用:** 当用户需要创建一个RPC时,如果用户有明确要求创建的方式,则按照用户的要求来创建。如果没有明确要求,则通常先判断是否可以通过创建读方案、写方案、DTO来使TOCO自动创建出对应的RPC,最后再考虑通过TOCO创建自定义RPC +- **自定义RPC和代码中手写方法的关系:** 二者都可以通过手动的方式实现一个服务层的方法,应用场景的区别在于:如果一个方法需要被其他模块订阅,则通常使用自定义RPC;如果一个方法只是某个API私有调用,不需要给外部模块开放,则可以使用代码手写方法 +- **关键配置:** 类名(驼峰,首字母大写,以Service结尾)、是否公开、方法名(驼峰,首字母小写)、请求参数、返回值。注意如果一个RPC是分页查询且参数为Qto类型,则Qto中已经包含了分页所需的from、size、scrollId等属性,无需额外为RPC增加类似参数。 +- **TOCO中RPC的存储:** 注意RPC在TOCO中只存储了方法签名,不存储内部的执行流程逻辑,如果需要了解其内部的实现逻辑,则需要通过阅读RPC对应的代码。 +- **参数类型:** RPC的参数**只能**为QTO、BTO、Enum、基本类型,可为单值或列表。注意如果是对象类型,则优先使用QTO、BTO作为参数,**禁止使用**VO和自定义结构如Object +- **返回值类型:** RPC的返回值**只能**为DTO、Enum、基本类型,可为单值或列表,**禁止使用**VO、QTO、BTO、自定义结构如Object作为返回值。注意如果是对象类型,则优先使用DTO作为返回值 +- **TOCO中json结构描述:** 在TOCO中,DTO使用一个json结构表示,示例如下: +```json +{ + "methodName": "saveUsers", + "className": "UserSaveService", + "requestParams":[ + { + "name": "saveUserBtoList", + "description": "批量保存用户参数", + "type": "List", + "innerType": "Bto", + "innerUuid": "dbvvc4d4-0063-442f-abd7-vrfded656988" + } + ], + "response": { + "type": "Boolean" + } +} +``` +结构中一些关键字段描述如下: +requestParams为请求参数列表,response为返回结构,requestParams中每个参数和response的结构相同,其中:name为参数名;type为参数类型,参数类型取值范围为Boolean,String,Integer,Long,Float,Double,BigDecimal,Date,ByteArray,Enum,Eo,Dto,Qto,Bto,List,PageResult,Void,其中参数不能为Void和PageResult,如果不需要返回值则type设置为Void,如果返回值为分页查询的结果则type设置为PageResult,且innerType必为Dto,对应代码中的VSQueryResult;description为描述;typeUuid为参数对应类结构的UUID,当type为Enum、Eo、Dto时传入该对象的uuid,当type为Qto时传入对应读方案的uuid、当type为Bto时传入对应写方案的uuid;innerType为List内部类型,当type为List或PageResult时包含该字段;innerUuid为List内部类结构的UUID,当type为List或PageResult且innerType为Enum、Eo、Dto时传入该对象的uuid,当innerType为Qto时传入对应读方案的uuid、当innerType为Bto时传入对应写方案的uuid。 + - **生成代码:** RPC会在service层中生成类文件及实现函数,包含DTO自动生成的RPC如UserDtoService.getById(主键为id)或StaffBaseDtoService.getByStaffId(主键为staff_id)、读写方案自动生成的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创建,需指定具体的参数和返回值等。 +- **TOCO中API的存储:** 注意API在TOCO中只存储了其URI和方法签名,不存储内部的执行流程逻辑,如果需要了解其内部的实现逻辑,则需要通过阅读API对应的代码。 +- **关键配置:** uri(加粗展示,一般为/api/${moduleName}/xxx,如/api/user/create,全局唯一。如果用户有特殊命名规则的话以用户要求为准)、类名(以Controller结尾)、方法名(驼峰,首字母小写)、请求参数、返回值。注意如果一个API是分页查询且参数为Qto类型,则Qto中已经包含了分页所需的from、size、scrollId等属性,无需额外为API增加类似参数。 +- **参数类型:** API的参数**只能**为读方案自动生成的QTO、写方案自动生成的BTO、EO、Enum、基本类型,可为单值或列表。注意如果是对象类型,则优先使用读方案自动生成的QTO、写方案自动生成的BTO作为参数。**禁止使用**DTO和自定义结构如Object作为参数 +- **返回值类型:** TOCO的API运行在自己的Java脚手架中,脚手架会自动对API的返回值做一层对象包装(code、message、data)。所以在TOCO中,API的返回值无需考虑返回码和错误信息,只需考虑返回的数据本身。TOCO中API的返回值**只能**为VO、Enum、基本类型,可为单值或列表,**禁止使用**DTO、QTO、BTO、自定义结构如Object作为返回值。注意如果是对象类型,则优先使用VO作为返回值。 +- **TOCO中json结构描述:** 在TOCO中,API使用一个json结构表示,示例如下: +```json +{ + "methodName": "getUserMeetingList", + "className": "MeetingQueryController", + "usageScenario": "xxx", + "requestParams":[ + { + "name": "getMeetingListByUserId74Qto", + "description": "查询参数", + "type": "Qto", + "uuid": "6351a4d4-0063-442f-abd7-a2df6d656988" + }, + { + "name": "test", + "description": "是否为测试请求", + "type": "Boolean" + } + ], + "response": { + "type": "List", + "innerType": "Vo", + "innerUuid": "1d58352b-5333-4509-aec2-1abc3fac9122" + } +} +``` +结构中一些关键字段描述如下: +requestParams为请求参数列表,response为返回结构,requestParams中每个参数和response的结构相同,其中:name为参数名;type为参数类型,参数类型取值范围为Boolean,String,Integer,Long,Float,Double,BigDecimal,Date,ByteArray,Enum,Eo,List,PageResult,Vo,Qto,Bto,Void,其中参数不能为Void和PageResult,如果不需要返回值则type设置为Void,如果返回值为分页查询的结果则type设置为PageResult,且innerType必为Vo,对应代码中的VSQueryResult;description为描述;typeUuid为参数对应类结构的UUID,当type为Enum、Eo、Vo时传入该对象的uuid,当type为Qto时传入对应读方案的uuid、当type为Bto时传入对应写方案的uuid;innerType为List内部类型,当type为List时包含该字段;innerUuid为List或PageResult内部类结构的UUID,当type为List或PageResult且innerType为Enum、Eo、Vo时传入该对象的uuid,当innerType为Qto时传入对应读方案的uuid、当innerType为Bto时传入对应写方案的uuid。 +- **代码产物和修改建议** + - **生成代码:** API会在entrance层生成Controller以及对应的API方法 + - **修改建议:** 建议修改API方法的实现内容,禁止直接修改API方法签名、URI,**注意**:如需修改API定义(名称、出入参数、URL)需要通过修改API设计元素实现 + +#### **2.15 流程服务(FunctionFlow)** +- **定义与用途:** TOCO针对复杂业务拆解,定义了流程服务,把一个复杂的业务过程,根据业务逻辑的内聚性,合并逻辑功能,把流程分解成流程节点,最终构造出一个类似工作流的逻辑流程;最终实现复杂业务流程分解,提升代码的可维护性。TOCO内嵌了流程引擎,在Function_Flow生成代码后,可以在流程引擎中执行 +- **何时使用:** + - 如果一个API/RPC中涉及的写服务超过3个,则**必须**使用流程服务 + - 当用户要求使用流程服务 +- **节点的封装:** + - 因为TOCO是一个面向数据处理的系统,所以数据内聚为首要考虑因素;一个节点通常围绕一个核心写服务,包括取数的读服务,为写服务的入参进行数据处理和转换,以及该写服务完成后的一些附属功能, 除了条件节点,入参校验,最终数据返回节点,每个节点至少包括一个写服务。 +- **关键配置:** 名称(小写字母+下划线),拆解复杂业务逻辑,如果业务流程比较简单,则不需要使用流程服务 +- **如何创建/生成:** + - 流程不是逻辑的伪代码,不需要表达全部逻辑细节,相关有内聚性,相似性的功能逻辑需要被封装到一个流程节点内;例如用户注册:创建用户需要,需要创建账号信息,需要创建用户信息,并且发送一个通知消息,这几个功能都属于创建用户相关信息,而且没有逻辑分叉,因此可以内聚在一个逻辑节点内。 + - 流程节点之间的参数和返回值的传递通过一个统一的上下文Context传递(在写代码的时候按需修改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值表示退出循环"} } - - - **与API的关系:** BTO通常可作为API的参数,API接收到参数后可直接透传给内部的RPC进行调用,注意BTO只用作写操作的参数,**禁止用作查询参数结构** - - **复杂场景处理:** 对于涉及多个写方案的复杂API,可以: - a. 如果接口有一个主要的写场景,则创建一个主要的写方案,让其自动生成的BTO作为API参数,然后再增加其他基本类型的参数或EO - b. 使用基本类型参数或EO作为参数,在接口执行流程中将基本类型参数转换为各个写方案自动生成的BTO,再调用多个写方案 - c. 如果流程过于复杂,优先考虑使用基本类型参数或EO - #### **2.13 服务层方法 (RPC)** - - **定义与用途:** 在TOCO中,RPC为服务层的方法。RPC按照可见性可以分为两种,一种是公开RPC,可以被其他模块订阅,订阅后可以通过RPC适配器进行调用;另一种是非公开RPC,只能被当前模块调用。非公开RPC可以被公开,从而被其他模块订阅并调用 - - **如何创建/生成:** RPC有4种创建方式:a.DTO创建后会自动创建RPC,RPC的公开性与DTO的公开性保持一致;b.返回DTO的读方案会根据分页情况、以及是否生成计数函数的配置自动生成非公开的RPC c.写方案创建后会自动生成非公开的RPC,返回值为操作的聚合根实体记录的主键字段的值 d.如果上述三种RPC无法满足需求,则可以通过TOCO创建自定义RPC完整指定功能,需指定具体的参数和返回值以及公开性等。 - - **优先复用:** 当用户需要创建一个RPC时,如果用户有明确要求创建的方式,则按照用户的要求来创建。如果没有明确要求,则通常先判断是否可以通过创建读方案、写方案、DTO来使TOCO自动创建出对应的RPC,最后再考虑通过TOCO创建自定义RPC - - **自定义RPC和代码中手写方法的关系:** 二者都可以通过手动的方式实现一个服务层的方法,应用场景的区别在于:如果一个方法需要被其他模块订阅,则通常使用自定义RPC;如果一个方法只是某个API私有调用,不需要给外部模块开放,则可以使用代码手写方法 - - **关键配置:** 类名(驼峰,首字母大写,以Service结尾)、是否公开、方法名(驼峰,首字母小写)、请求参数、返回值。注意如果一个RPC是分页查询且参数为Qto类型,则Qto中已经包含了分页所需的from、size、scrollId等属性,无需额外为RPC增加类似参数。 - - **TOCO中RPC的存储:** 注意RPC在TOCO中只存储了方法签名,不存储内部的执行流程逻辑,如果需要了解其内部的实现逻辑,则需要通过阅读RPC对应的代码。 - - **参数类型:** RPC的参数**只能**为QTO、BTO、Enum、基本类型,可为单值或列表。注意如果是对象类型,则优先使用QTO、BTO作为参数,**禁止使用**VO和自定义结构如Object - - **返回值类型:** RPC的返回值**只能**为DTO、Enum、基本类型,可为单值或列表,**禁止使用**VO、QTO、BTO、自定义结构如Object作为返回值。注意如果是对象类型,则优先使用DTO作为返回值 - - **TOCO中json结构描述:** 在TOCO中,DTO使用一个json结构表示,示例如下: - ```json - { - "methodName": "saveUsers", - "className": "UserSaveService", - "requestParams":[ - { - "name": "saveUserBtoList", - "description": "批量保存用户参数", - "type": "List", - "innerType": "Bto", - "innerUuid": "dbvvc4d4-0063-442f-abd7-vrfded656988" - } - ], - "response": { - "type": "Boolean" - } - } - ``` - 结构中一些关键字段描述如下: - requestParams为请求参数列表,response为返回结构,requestParams中每个参数和response的结构相同,其中:name为参数名;type为参数类型,参数类型取值范围为Boolean,String,Integer,Long,Float,Double,BigDecimal,Date,ByteArray,Enum,Eo,Dto,Qto,Bto,List,PageResult,Void,其中参数不能为Void和PageResult,如果不需要返回值则type设置为Void,如果返回值为分页查询的结果则type设置为PageResult,且innerType必为Dto,对应代码中的VSQueryResult;description为描述;typeUuid为参数对应类结构的UUID,当type为Enum、Eo、Dto时传入该对象的uuid,当type为Qto时传入对应读方案的uuid、当type为Bto时传入对应写方案的uuid;innerType为List内部类型,当type为List或PageResult时包含该字段;innerUuid为List内部类结构的UUID,当type为List或PageResult且innerType为Enum、Eo、Dto时传入该对象的uuid,当innerType为Qto时传入对应读方案的uuid、当innerType为Bto时传入对应写方案的uuid。 - - **生成代码:** RPC会在service层中生成类文件及实现函数,包含DTO自动生成的RPC如UserDtoService.getById(主键为id)或StaffBaseDtoService.getByStaffId(主键为staff_id)、读写方案自动生成的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创建,需指定具体的参数和返回值等。 - - **TOCO中API的存储:** 注意API在TOCO中只存储了其URI和方法签名,不存储内部的执行流程逻辑,如果需要了解其内部的实现逻辑,则需要通过阅读API对应的代码。 - - **关键配置:** uri(加粗展示,一般为/api/${moduleName}/xxx,如/api/user/create,全局唯一。如果用户有特殊命名规则的话以用户要求为准)、类名(以Controller结尾)、方法名(驼峰,首字母小写)、请求参数、返回值。注意如果一个API是分页查询且参数为Qto类型,则Qto中已经包含了分页所需的from、size、scrollId等属性,无需额外为API增加类似参数。 - - **参数类型:** API的参数**只能**为读方案自动生成的QTO、写方案自动生成的BTO、EO、Enum、基本类型,可为单值或列表。注意如果是对象类型,则优先使用读方案自动生成的QTO、写方案自动生成的BTO作为参数。**禁止使用**DTO和自定义结构如Object作为参数 - - **返回值类型:** TOCO的API运行在自己的Java脚手架中,脚手架会自动对API的返回值做一层对象包装(code、message、data)。所以在TOCO中,API的返回值无需考虑返回码和错误信息,只需考虑返回的数据本身。TOCO中API的返回值**只能**为VO、Enum、基本类型,可为单值或列表,**禁止使用**DTO、QTO、BTO、自定义结构如Object作为返回值。注意如果是对象类型,则优先使用VO作为返回值。 - - **TOCO中json结构描述:** 在TOCO中,API使用一个json结构表示,示例如下: - ```json - { - "methodName": "getUserMeetingList", - "className": "MeetingQueryController", - "usageScenario": "xxx", - "requestParams":[ - { - "name": "getMeetingListByUserId74Qto", - "description": "查询参数", - "type": "Qto", - "uuid": "6351a4d4-0063-442f-abd7-a2df6d656988" - }, - { - "name": "test", - "description": "是否为测试请求", - "type": "Boolean" - } - ], - "response": { - "type": "List", - "innerType": "Vo", - "innerUuid": "1d58352b-5333-4509-aec2-1abc3fac9122" - } - } - ``` - 结构中一些关键字段描述如下: - requestParams为请求参数列表,response为返回结构,requestParams中每个参数和response的结构相同,其中:name为参数名;type为参数类型,参数类型取值范围为Boolean,String,Integer,Long,Float,Double,BigDecimal,Date,ByteArray,Enum,Eo,List,PageResult,Vo,Qto,Bto,Void,其中参数不能为Void和PageResult,如果不需要返回值则type设置为Void,如果返回值为分页查询的结果则type设置为PageResult,且innerType必为Vo,对应代码中的VSQueryResult;description为描述;typeUuid为参数对应类结构的UUID,当type为Enum、Eo、Vo时传入该对象的uuid,当type为Qto时传入对应读方案的uuid、当type为Bto时传入对应写方案的uuid;innerType为List内部类型,当type为List时包含该字段;innerUuid为List或PageResult内部类结构的UUID,当type为List或PageResult且innerType为Enum、Eo、Vo时传入该对象的uuid,当innerType为Qto时传入对应读方案的uuid、当innerType为Bto时传入对应写方案的uuid。 - - **代码产物和修改建议** - - **生成代码:** API会在entrance层生成Controller以及对应的API方法 - - **修改建议:** 建议修改API方法的实现内容,禁止直接修改API方法签名、URI,**注意**:如需修改API定义(名称、出入参数、URL)需要通过修改API设计元素实现 - #### **2.15 流程服务(FunctionFlow)** - - **定义与用途:** TOCO针对复杂业务拆解,定义了流程服务,把一个复杂的业务过程,根据业务逻辑的内聚性,合并逻辑功能,把流程分解成流程节点,最终构造出一个类似工作流的逻辑流程;最终实现复杂业务流程分解,提升代码的可维护性。TOCO内嵌了流程引擎,在Function_Flow生成代码后,可以在流程引擎中执行 - - **何时使用:** - - 如果一个API/RPC中涉及的写服务超过3个,则**必须**使用流程服务 - - 当用户要求使用流程服务 - - **节点的封装:** - - 因为TOCO是一个面向数据处理的系统,所以数据内聚为首要考虑因素;一个节点通常围绕一个核心写服务,包括取数的读服务,为写服务的入参进行数据处理和转换,以及该写服务完成后的一些附属功能, 除了条件节点,入参校验,最终数据返回节点,每个节点至少包括一个写服务。 - - **关键配置:** 名称(小写字母+下划线),拆解复杂业务逻辑,如果业务流程比较简单,则不需要使用流程服务 - - **如何创建/生成:** - - 流程不是逻辑的伪代码,不需要表达全部逻辑细节,相关有内聚性,相似性的功能逻辑需要被封装到一个流程节点内;例如用户注册:创建用户需要,需要创建账号信息,需要创建用户信息,并且发送一个通知消息,这几个功能都属于创建用户相关信息,而且没有逻辑分叉,因此可以内聚在一个逻辑节点内。 - - 流程节点之间的参数和返回值的传递通过一个统一的上下文Context传递(在写代码的时候按需修改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":[ // 定义该流程包含的节点 - { - "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" - } - ] + } + } + } } - + ``` + - 例子:用户注册流程 + + { + "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类,负责注册模块下的所有流程到执行器 @@ -1396,8 +1396,7 @@ meeting_with_room_and_agenda_wo示例: - 用户登录,在UserFlowService中生成一个函数invokeLoginFlow,该函数通过流程框架根据流程定义调用LoginNode,LoginNode中封装了用户登录的逻辑,LoginFlowContext中封装了用户登录的参数和结果。 - **修改建议:** 不修改 service 中的函数, 不修改FlowConfig, 可以修改FlowContext, 添加/修改出入参数, 修改FlowNode中的具体业务逻辑。 - -#### **2.12 自定义查询** +#### **2.16 自定义查询** - 在读方案无法满足需求的情况下,可以使用自定义查询 - 自定查询使用复杂的sql实现业务功能 - 自定义查询的数据访问层使用MyBatis、MyBatisPlus实现