领域驱动设计 用DDD重构会计凭证生成(下)( 十 )

<>();// 会计凭证主信息String pushStatus;List monitorDocumentPOList = new ArrayList();for (AdAccountDocumentPO accountDoc : documentPOList) {List accountDocInfoList = accountDoc.getAdAccountDocumentInfoPOList();if (CollectionUtils.isEmpty(accountDocInfoList)) {log.error("会计凭证主信息[ID = {}]下的会计凭证信息为空 , 跳过不进行处理!", accountDoc.getId());continue;}// 会计凭证信息for (AdAccountDocumentInfoPO accountDocInfo : accountDocInfoList) {//采购申请单 , 关联交易 , 应付凭证00 , CS00001成本中心 , 只生产凭证 , 设置发送失败if("3".equals(accountDoc.getBillTypeKind()) && "0".equals(accountDoc.getIsRelatedParty()) && "00".equals(accountDocInfo.getVoucherType())){int num =documentInfoService.queryCostNum(accountDoc.getBillNo(),accountDocInfo.getId());if(num>0){// 更新凭证信息发送状态 , 发送失败updateVoucherPushStatus(accountDocInfo.getId(), null, "关联交易不固定成本中心 , 需要手动修改成本中心", PushStatusEnum.PUSH_FAIL.getStatus(), operatorId);log.info("采购付款单{}的应付凭证id为{} , 有特殊成本中心CS00001",accountDocInfo.getBillNo(),accountDocInfo.getId());continue;}}try {this.pushVoucher(accountDoc, accountDocInfo, errorMessageList, operatorId, operatorName);} catch (Exception ex) {errorMessageList.add("发送凭证失败 , 异常信息:" + ex.getMessage());}}// 更新主凭证推送状态pushStatus = updateMainVoucherPushStatus(accountDoc, operatorId);//发送失败加入预警列表if (monitorFlag && PushStatusEnum.PUSH_FAIL.getStatus().equals(pushStatus)) {monitorDocumentPOList.add(accountDoc);}}if (!CollectionUtils.isEmpty(monitorDocumentPOList)) {try {pushFailedVoucherLogic.sendPushFailedVoucherToMonitor(monitorDocumentPOList.get(0));} catch (Exception e) {log.error("sendPushFailedVoucherToMonitor Exception", e);}}//推送失败预警 , 逐条发送monitorService.monitor(monitorDocumentPOList);if (!CollectionUtils.isEmpty(errorMessageList)) {return ResponseBuilder.FAILURE.setErrorMsg(StringUtils.join(errorMessageList, "\n"));}return ResponseBuilder.ok();}...}
以上是典型的 , 甚至可以说是融入了每个血液的 贫血模型+事务脚本形式的代码 , 但这并不是说 贫血模式+事务脚本形式的代码本身有问题 , 如果业务足够简单 , 比如单纯的CRUD , 那么这种形式的代码足以胜任 , 但如果业务复杂 , 且需跟随业务的变更不断更新代码 , 那么这种形式的代码就可能将系统送上一条持续累积技术债的不归路 。
重构后的凭证生成功能被转移到了门面微服务中 , 如此便不再需要之前复杂的数据组织逻辑和一次需传送大量数据的RPC , 重构后的凭证生成方法只需传入单据ID即可 , 通过定义凭证创建成功和失败两个领域事件将原有代码中耦合在一起的 , 且明显不符合SRP的凭证生成和推送两个功能进行了解耦 , 凭证推送功能则由负责异步(虽然该功能很重要 , 但这不能成为它与凭证创建紧密绑定的理由 , 所以不应让其对凭证的创建有任何影响)执行 。
@AllArgsConstructor@Servicepublic class VoucherService {private final BillCrudService billCrudService;private final EventPublisher eventPublisher;/*** 生成标准凭证, 即正向付款成功后所应生成的凭证** @param billId 单据ID*/public void createStandardVoucher(Long billId) {try {List voucherIds = VoucherCreator.getInstance(billCrudService.get(billId), voucherKindEnum).createVoucher();if (!voucherIds.isEmpty()) {eventPublisher.publish(new VoucherCreatedEvent(billId, voucherIds));}} catch (CreateVoucherException e) {eventPublisher.publish(new VoucherCreateFailedEvent(billId, e.getMessage()));} catch (Exception e) {log.error("非业务异常", e);throw new RuntimeException("非业务异常", e);}}...}