4、分摊引擎架构:分摊计算引擎的模块划分、输入输出接口、调度流程

分摊计算引擎是成本台账系统的核心计算单元,负责将公共成本(如水电费、折旧、管理费)按照预定义的分摊规则精确分配到多个成本对象(项目、部门、产品)。本章从架构视角拆解引擎的模块边界、接口契约与调度机制,为后续接口测试提供结构化的分析基础。

4.1 模块划分与职责

分摊引擎采用分层+微内核设计,核心模块包括以下五个部分:

模块名称 职责描述 关键输出
规则解析器 读取分摊规则配置(固定比例、按工时、按面积、按产量等),校验规则合法性,生成可执行的分摊策略树。 策略树对象、规则校验报告
数据加载器 从成本台账数据库或上游接口拉取待分摊的成本原始数据(成本中心、金额、期间、币种),完成数据清洗与格式转换。 标准化的成本数据集
分摊计算器 执行核心分摊算法:按策略树遍历,逐层计算各成本对象应承担金额,支持多维度(部门+项目+产品)交叉分摊。 分摊明细表、分摊汇总表
结果持久化器 将分摊结果写入目标表(分摊明细表、项目成本台账、财务报表接口表),处理并发写入与事务一致性。 写入确认记录、错误日志
调度控制器 管理分摊任务的触发、排队、重试、超时与监控,对外暴露统一的REST/SOAP接口。 任务状态、执行日志
测试关注点: 规则解析器是错误高发区,尤其当规则配置为“按工时分摊”但工时数据缺失时,引擎应触发降级策略(如均摊)而非直接报错。测试用例需覆盖规则缺失、规则冲突、规则循环引用等异常场景。

4.2 输入输出接口定义

分摊引擎对外暴露三个核心接口,接口契约采用JSON Schema定义,确保上下游系统解耦。

4.2.1 分摊任务提交接口(输入)

POST /api/v1/allocation/task
Content-Type: application/json

{
  "taskId": "ALLOC_20250321_001",
  "period": "2025-03",
  "costCenterIds": ["CC001", "CC002"],
  "ruleGroupId": "RG_DEFAULT",
  "targetObjects": [
    {"type": "project", "ids": ["PJ-A", "PJ-B", "PJ-C"]},
    {"type": "department", "ids": ["DEPT-SALES", "DEPT-RD"]}
  ],
  "options": {
    "dryRun": false,
    "rollbackOnError": true,
    "timeoutSeconds": 300
  }
}

4.2.2 分摊结果查询接口(输出)

GET /api/v1/allocation/result/{taskId}
Response:

{
  "taskId": "ALLOC_20250321_001",
  "status": "COMPLETED",
  "totalCost": 1250000.00,
  "allocatedCost": 1250000.00,
  "details": [
    {
      "sourceCostCenter": "CC001",
      "targetObject": {"type": "project", "id": "PJ-A"},
      "amount": 350000.00,
      "ruleApplied": "按面积分摊",
      "baseValue": 3500.0,
      "unit": "平方米"
    }
  ],
  "errors": []
}

4.2.3 接口对比表

接口 方向 协议 数据格式 幂等性
提交分摊任务 输入 REST POST JSON 是(基于taskId去重)
查询分摊结果 输出 REST GET JSON 是(只读)
回调通知 输出 Webhook POST JSON 是(支持重试)

4.3 调度流程与状态机

分摊引擎的调度流程采用有限状态机模型,确保每个任务的生命周期可追踪、可恢复。下图展示了从任务提交到结果回写的完整调度路径:

分摊引擎调度流程状态机 PENDING DATA_LOADING COMPUTING PERSISTING COMPLETED FAILED ROLLBACK 状态说明: PENDING: 任务排队等待 DATA_LOADING: 加载原始数据 COMPUTING: 执行分摊计算 PERSISTING: 写入结果表 异常路径: FAILED: 不可恢复错误 ROLLBACK: 回滚已写入数据 → 回到 PENDING 重试

4.4 调度时序与重试机制

引擎调度控制器按以下时序执行任务,并内置三级重试策略:

  1. 任务接收: 控制器校验taskId唯一性,若重复则直接返回已有结果(幂等)。
  2. 数据加载: 调用数据加载器,若加载超时(默认30秒)或数据不完整,触发一级重试(最多3次,间隔5秒)。
  3. 分摊计算: 计算器执行策略树,若计算过程中出现除零、空指针等异常,触发二级重试(最多2次,间隔10秒)。
  4. 结果持久化: 写入数据库时若发生死锁或唯一键冲突,触发三级重试(最多5次,间隔1秒,使用指数退避)。
  5. 回调通知: 任务最终状态(COMPLETED/FAILED)通过Webhook通知调用方,通知失败则进入死信队列人工处理。
测试陷阱: 在接口测试中,需验证重试机制的幂等性。例如:同一taskId重复提交时,引擎不应重复计算,而应直接返回第一次的计算结果。若引擎错误地执行了两次分摊,将导致项目成本台账金额翻倍,财务报表合并时出现重大差异。

4.5 架构图:模块间依赖与数据流

下图展示了分摊引擎各模块之间的依赖关系与数据流向,帮助测试人员理解接口调用的上下游依赖:

分摊引擎模块依赖与数据流 上游系统/用户 调度控制器 规则解析器 数据加载器 分摊计算器 结果持久化器 成本台账DB 数据流方向: ① 任务提交 → 调度控制器 ② 调度 → 规则解析 + 数据加载 ③ 计算 → 持久化 → 数据库
架构测试策略: 针对上述模块依赖,接口测试应覆盖以下场景:
• 调度控制器在规则解析器超时(模拟响应延迟 > 5秒)时,是否正确触发重试并最终返回超时错误。
• 数据加载器返回空数据集时,分摊计算器是否直接输出零金额明细,还是抛出异常导致任务失败。
• 结果持久化器在数据库连接断开时,是否触发ROLLBACK状态并回滚已写入的中间数据,避免脏数据残留。