初始化器新探索
Sailing
备忘
2019.6 新探索
初始化器使用过程中遇到了一些不方便甚至需要引入体系之外的措施才能解决的问题. 比如: 单据新建/数据转换/复制时的转换器不能共用; 因为初始化器的功能限制, 新建和复制的总体处理过程 不得不实现的不一样等等. 需要找到一个方案简化上述过程, 增强初始化器的通用性, 具体要求如下:
- 所有的初始化器, 都放在后道执行
- 完全新建: new之后再执行初始化 (生成主表, 生成默认费用项);
- 从其他单据新建: 先单据转换, 在初始化 (转换主表, 转换而来的明细项, 转换而来的费用项, 补充生成缺失的默认费用项);
- 复制: 先根据复制选项(复制字段或忽略字段)执行复制, 在执行复制初始化; 复制初始化和新建初始化尽量通用. (复制而来的主表, 复制而来的明细项, 复制而来的费用项, 补充生成缺失的默认费用项);
- 初始化单个?;
- 初始化器支持对列表的先清空, 在生成;
- 支持对列表已有数据的初始化;
- 支持有条件的增加列表项;
解决方案
方案一
- entityScope增加一个选项, 是否直接使用source值作为target值.
- target和source都可以通过context访问, 都可以作为SourceScope的来源.
- listGen增加一个是不是需要先清空的选项.
- 如果列表项中产生出来的项已经在列表中存在了, 那么不加入.
方案路演
完全新建: new之后再执行初始化 (生成主表, 生成默认费用项)
- 根: sourcePath=null, isNew=ture
- source没有, isNew=true, 因此创建一个对象定为target
- 明细(列表中): 不启用
- 费用(列表中)x8: sourcePath=null, isNew=true
- 对每条规则, path没有, isNew=true, 新建一个费用
从其他单据新建: 先单据转换, 在初始化 (转换主表, 转换而来的明细项, 转换而来的费用项, 补充生成缺失的默认费用项)
- 调用的时候, 传入target, target已经是一个基本完整的对象, 可能缺若干费用项目
- 根: sourcePath=target
- source=target, 有分组, 所以会继续进行下去; 此外因为传入了target, 所以肯定不会新建target;
- 明细(列表中): sourcePath=明细, source作为target
- 因为指定了source作为target, 那么对target初始化其实就是对source初始化, 而source其实就是原来的target
方案二
- 分出新建模式, 和target模式.
- 同一套配置, 使用新建模式和target模式时, 高层语义不变.
- 新建模式大致保持现在一样, 但可以去除和target有关部分.
- target模式中, sourcePath不是指定数据上下文, 而是指定初始化目标.
方案路演
完全新建: new之后再执行初始化 (生成主表, 生成默认费用项)
- 新建模式
- 根: sourcePath=null, isNew=ture
- source没有, isNew=true, 因此创建一个对象定为target
- 明细entity in list: sourcePath=null, isNew=false
- 费用entity in list (x8): sourcePath=null, isNew=true, condition=target里面没有对应的费用项
- 对每条规则, path没有, isNew=true, 新建一个费用
从其他单据新建: 先单据转换, 在初始化 (转换主表, 转换而来的明细项, 转换而来的费用项, 补充生成缺失的默认费用项)
- target模式
- 该模式下, sourcePath不起作用, 或者说sourcePath隐式指向target当前节点, isNew的含义变为: a.普通entity, target为空则新建; b.列表中entity, 一条规则产生一条.
- 根: sourcePath=null, isNew=true
- 这里isNew的含义是: 如果target为空则新建, 否则用target
- 明细entity in list: sourcePath=null, isNew=false
- 也就是针对target中每个已有的明细行, 执行项下规则
- 费用entity in list: sourcePath=null, isNew=false
- isNew=false, 仅针对target的每一条明细应用项下规则
- 费用entity in list (x8): sourcePath=null, isNew=true, condition=target里面没有对应的费用项
- isNew=true, 根据sourcePath新建, sourcePath指向的数据没有, 也产生一条.
复制: 先根据复制选项(复制字段或忽略字段)执行复制, 再执行复制初始化; 复制初始化和新建初始化尽量通用. (复制而来的主表, 复制而来的明细项, 复制而来的费用项, 补充生成缺失的默认费用项);
从其他单据读取, 先初始化后, 再执行转换
- 根: sourcePath=:root
- sourcePath指向的对象的size必须和target匹配.
- isNew的原有含义是: 如果sourcePath指向的数据分组为空, 则不应用项下规则;
明细单选择合同新建, 选择了多个合同的多个明细, 生成一个明细单.
分析
有target 无target
"sourcePath=null
isNew=false" 规则不执行 规则不执行
"sourcePath=null
isNew=true" "在原target基础上执行项下规则
规则执行一遍, source为空" "新建一个target, 执行项下规则
规则执行一遍, source为空"
"sourcePath=XXX
isNew=false" "target如果为entity:
source的分组size必须为1, 规则执行一遍, source为该分组;
target如果为list:
source的每个分组执行一遍规则, 并添加到target中;" "target类型如果为entity:
新建一个target. source的分组size必须为1, 规则执行一遍, source为该分组;
target如果为list:
新建一个hashset作为target. source的每个分组执行一遍规则, 并添加到target中;"
"sourcePath=XXX
isNew=true" 同上 同上

模拟:
A1. 根scope: sourcePath=null, isNew=ture; 明细: 不处理; 费用: 多个entityscope, sourcePath=null, isNew=true
执行过程: root的target为空, 于是新建;
A2. 根: sourcePath=:targetRoot; target直接采用原值. 明细: sourcePath=明细, 使用source作为target, (初始化出来的值已经在原集合中, 因此不再加入);
结论
初始化器分两种模式:
- source模式: 保持原来的机制不变;
- target模式: 规则对target迭代
单据新建过程的四种方式, 统一分两步走:
- 完全新建: 首先使用source模式执行新建初始化器; 随后使用target模式执行更新初始化器.
- 复制新建: 首先根据复制选项(复制或忽略), 复制出一个单据; 随后使用target模式执行更新初始化器.
- 选单新建: 首先使用source模式执行单据转换中的初始化器; 随后使用target模式执行更新初始化器.
- 下推新建: 首先使用source模式执行单据转换中的初始化器; 随后对上一步结果中的每个一单据, 分别使用target模式执行更新初始化器.
- excel导入: 首先执行格式转换的初始化器, 然后执行target模式的初始化器.
- 单据添加明细: 首先使用source模式执行单据转换中的对应初始化器; 随后对结果使用target模式执行更新初始化器.