MDrivenDesigner中用OCL读取关联表数据生成采购订单的方案问询
背景
我有一个包含多产品的contracts表,contracts与products为多对多关系,对应关联表Contract-lineitem。在将合同转换为采购订单时,需要读取所有产品的同时,获取Contract-lineitem表中产品表未包含的额外详情。预期执行流程如下:
- 首先创建采购订单
- 遍历合同条目并更新采购订单条目表
- 检查当前合同中的产品是否已存在于新创建的采购订单条目中
- 若不存在则创建该条目并返回新创建的采购订单条目
- 若存在则用正确详情更新采购订单条目中的现有产品并返回更新后的记录
我自行设计了一段实现代码,想请教是否有更优的实现方式:
( vCurrent_Contract.Products->collect(p| po.Products.add(p) -- add products to purchase order ); --iterate over the contract items and update purchase order items vCurrent_Contract.ContractItem->collect(cItem | let poItem = po.PurchaseOrderItem->select(i|i.Products=cItem.Products)->first in if poItem.isNull then ( let nupoItem = PurchaseOrderItem.Create in ( nupoItem.Quantity:=cItem.Quantity; nupoItem.Disc:=cItem.Disc; nupoItem.Subtotal:=cItem.Subtotal; po.PurchaseOrderItem.add(nupoItem); nupoItem --return the newly created purchase order item ) ) else ( poItem.Quantity := cItem.Quantity; poItem.Subtotal:= cItem.Subtotal; poItem.Disc = cItem.Disc; poItem -- return the updated purchase order items ) endif ); po )
优化方案
1. 移除冗余的产品添加步骤
当前代码中先执行vCurrent_Contract.Products->collect(p|po.Products.add(p)),但后续处理ContractItem时已经通过关联产品完成了采购订单条目的创建/更新,这一步属于冗余操作。采购订单与产品的关联应该通过PurchaseOrderItem维护,直接添加产品到po.Products可能导致数据不一致(比如删除采购订单条目后,po.Products中的对应产品可能残留),建议删除该步骤。
2. 构建产品映射提升查询效率
原代码每次遍历ContractItem时,都通过po.PurchaseOrderItem->select(i|i.Products=cItem.Products)->first线性查找对应条目,遍历N个条目就会产生N次线性查询。可以提前将已有的采购订单条目按产品构建映射(比如字典结构),将查询复杂度从O(N)降到O(1):
( let poItemMap = po.PurchaseOrderItem->reduce(map, item | map[item.Products] := item; map) in vCurrent_Contract.ContractItem->collect(cItem | let poItem = poItemMap[cItem.Products] in if poItem.isNull then ( let nupoItem = PurchaseOrderItem.Create in ( nupoItem.Quantity:=cItem.Quantity; nupoItem.Disc:=cItem.Disc; nupoItem.Subtotal:=cItem.Subtotal; po.PurchaseOrderItem.add(nupoItem); poItemMap[cItem.Products] := nupoItem; -- 更新映射,避免后续重复查询 nupoItem ) ) else ( poItem.Quantity := cItem.Quantity; poItem.Subtotal:= cItem.Subtotal; poItem.Disc := cItem.Disc; -- 修复原代码的赋值语法错误 poItem ) endif ); po )
3. 修复语法错误
原代码中poItem.Disc = cItem.Disc是比较操作而非赋值,应改为poItem.Disc := cItem.Disc,否则该行不会生效。
4. 封装为可复用函数
如果该转换逻辑会在多个场景使用,建议将其封装为独立函数(比如CreatePurchaseOrderFromContract(Contract contract)),提升代码的可维护性与复用性。
5. 事务保障数据一致性(可选)
若系统支持事务,建议将采购订单创建、条目更新的全流程包裹在事务中,避免出现采购订单已创建但条目更新失败的半完成状态,确保数据一致性。
内容的提问来源于stack exchange,提问作者Ichama .M. Herbert

