本地生活服务系统:模块边界与结算字段怎么拆

地级市综合运营商做本地生活服务系统 ,技术评审里除了问「有哪些模块」,更该问「模块边界怎么划、结算字段放哪张表」。若外卖、跑腿、同城团购各维护独立结算导出逻辑,财务对账就要人工拼;加模块时结算规则又要重写一遍,专项定制预算很快烧在返工上。

下文从模块边界、结算表结构、Service 伪代码说明「统一结算中枢 + 业态扩展字段」怎么拆。示例为教学示意,以光合同城当期交付为准。

痛点:结算返工根因往往是字段散落各模块

早期拼装方案常见结构:

  • 外卖模块自带 wm_settle_detail,跑腿模块自带 errand_settle_detail
  • 各模块独立导出 CSV,列名、精度、状态枚举各不一致
  • 加第三业态时,财务要求的核对列又要重新开发

后果很直观:周会前要对结算,运营从三个后台各导一份表再拼;改一条商务规则,要在多个系统各改一遍。本地生活服务系统 若走统一后台一体化,架构上应先收敛结算写路径与导出配置,再谈 UI 有多少菜单。

地级市评审时可先问:结算明细是否全业态共用主表?导出列是否可配置?加模块时结算规则是否可继承?答不上来,后面多半要反复花钱改字段。

模块边界:订单中枢、结算中枢、业态插件

text 复制代码
local-life-service/
├── apps/
│   ├── user-app/
│   ├── merchant-app/
│   ├── rider-app/
│   └── admin-console/             # 统一运营后台
├── order-hub/                     # 订单中枢(唯一写入口)
│   ├── command-api/
│   ├── state-machine/
│   └── read-model/
├── settlement-hub/                # 结算中枢(唯一写入口)
│   ├── settle-command/
│   ├── export-config/
│   ├── reconcile-read/
│   └── field-mapping.yaml
├── biz-plugins/
│   ├── takeout/
│   ├── errand/
│   └── group-buy/
├── mid-shared/
│   ├── user-master/
│   ├── merchant-master/
│   └── marketing-core/
└── ops/
    ├── settle-export-columns.yaml
    └── biz-settle-rules.yaml

边界原则:

  1. order-hub 管订单状态,settlement-hub 管结算明细与导出,业态插件禁止直连 UPDATE 结算主表。
  2. biz-plugin 只提供业态扩展字段与合法状态分支,结算通用字段在 settlement-hub 统一维护。
  3. Admin 控制台结算导出走 export-config,列定义来自 YAML,而非各模块硬编码。

结算主表:通用字段 + 业态扩展

sql 复制代码
-- 结算明细主表:全业态共用
CREATE TABLE st_settle_detail (
  id                BIGINT PRIMARY KEY AUTO_INCREMENT,
  order_id          BIGINT NOT NULL,
  order_no          VARCHAR(32) NOT NULL,
  biz_type          VARCHAR(16) NOT NULL COMMENT 'takeout|errand|group_buy|...',
  merchant_id       BIGINT NOT NULL,
  user_id           BIGINT NOT NULL,
  city_code         VARCHAR(12) NOT NULL,
  order_amount      DECIMAL(12,2) NOT NULL COMMENT '订单原价',
  discount_amount   DECIMAL(12,2) NOT NULL DEFAULT 0 COMMENT '优惠抵扣',
  platform_fee      DECIMAL(12,2) NOT NULL DEFAULT 0 COMMENT '平台服务费(客户自定规则)',
  merchant_income   DECIMAL(12,2) NOT NULL COMMENT '商家应得',
  rider_fee         DECIMAL(12,2) NULL COMMENT '配送费(如有)',
  settle_status     VARCHAR(16) NOT NULL COMMENT 'pending|confirmed|paid|disputed',
  settle_batch_no   VARCHAR(32) NULL COMMENT '结算批次号',
  created_at        DATETIME NOT NULL,
  updated_at        DATETIME NOT NULL,
  INDEX idx_merchant_batch (merchant_id, settle_batch_no),
  INDEX idx_biz_status (biz_type, settle_status)
) COMMENT='本地生活统一结算明细';

-- 外卖结算扩展
CREATE TABLE st_settle_takeout_ext (
  settle_id         BIGINT PRIMARY KEY,
  delivery_subsidy  DECIMAL(12,2) NULL COMMENT '配送补贴',
  packaging_fee     DECIMAL(12,2) NULL,
  CONSTRAINT fk_takeout_settle FOREIGN KEY (settle_id) REFERENCES st_settle_detail(id)
);

-- 跑腿结算扩展
CREATE TABLE st_settle_errand_ext (
  settle_id         BIGINT PRIMARY KEY,
  distance_km       DECIMAL(8,2) NULL,
  weight_surcharge  DECIMAL(12,2) NULL,
  CONSTRAINT fk_errand_settle FOREIGN KEY (settle_id) REFERENCES st_settle_detail(id)
);

-- 导出配置:列定义可配置,避免各模块硬编码
CREATE TABLE st_export_column_def (
  id                INT PRIMARY KEY AUTO_INCREMENT,
  column_key        VARCHAR(32) NOT NULL UNIQUE,
  column_label      VARCHAR(64) NOT NULL,
  source_table      VARCHAR(64) NOT NULL COMMENT 'st_settle_detail|ext',
  source_field      VARCHAR(64) NOT NULL,
  sort_order        INT NOT NULL DEFAULT 0,
  enabled           TINYINT NOT NULL DEFAULT 1
);

设计约束:

  1. merchant_incomeplatform_fee 等通用字段只在 st_settle_detail 维护,禁止各模块复制结算表。
  2. 业态差异放 st_settle_*_ext,导出时 JOIN 主表与扩展表。
  3. 商务结算规则由客户确定;系统提供可配置核对字段与导出项,不作收益承诺。

导出列配置:YAML 驱动,少返工

yaml 复制代码
# ops/settle-export-columns.yaml(示意)
default_export:
  columns:
    - key: order_no
      label: 订单号
      source: st_settle_detail.order_no
    - key: biz_type
      label: 业态
      source: st_settle_detail.biz_type
    - key: merchant_name
      label: 商家名称
      source: md_merchant.name
      join: merchant_id
    - key: order_amount
      label: 订单金额
      source: st_settle_detail.order_amount
    - key: discount_amount
      label: 优惠抵扣
      source: st_settle_detail.discount_amount
    - key: platform_fee
      label: 平台服务费
      source: st_settle_detail.platform_fee
    - key: merchant_income
      label: 商家应得
      source: st_settle_detail.merchant_income
    - key: settle_status
      label: 结算状态
      source: st_settle_detail.settle_status
    - key: settle_batch_no
      label: 结算批次
      source: st_settle_detail.settle_batch_no

takeout_extra:
  - key: delivery_subsidy
    label: 配送补贴
    source: st_settle_takeout_ext.delivery_subsidy
    when_biz: takeout

errand_extra:
  - key: distance_km
    label: 配送公里
    source: st_settle_errand_ext.distance_km
    when_biz: errand

财务临时要求加一列核对项时,优先在 YAML 和 st_export_column_def 配置,而非各模块改硬编码导出逻辑。这是减少本地生活服务系统后期返工的关键。

业态结算规则:分支在配置,不在代码复制

yaml 复制代码
# ops/biz-settle-rules.yaml(示意)
common:
  platform_fee_mode: percent          # percent|fixed|tiered(客户配置)
  platform_fee_rate: 0.05             # 示意值,客户自定
  merchant_income_formula: "order_amount - discount_amount - platform_fee"

takeout:
  inherit: common
  extra_fields:
    - packaging_fee
    - delivery_subsidy
  rider_fee_source: order_ext.rider_fee

errand:
  inherit: common
  extra_fields:
    - distance_km
    - weight_surcharge
  rider_fee_source: calculated_by_distance

group_buy:
  inherit: common
  extra_fields:
    - verify_code
    - verify_time

加第三业态时,只需在 YAML 增加 inherit: common 与扩展字段清单,结算主表结构不变。

Service 伪代码:结算唯一写入口

java 复制代码
@Service
public class SettlementCommandService {

    public SettleDetail confirmSettle(ConfirmSettleCmd cmd, Operator op) {
        Order order = orderRepo.findById(cmd.getOrderId());
        SettleRule rule = ruleLoader.load(order.getBizType());

        SettleDetail detail = new SettleDetail();
        detail.setOrderId(order.getId());
        detail.setOrderNo(order.getOrderNo());
        detail.setBizType(order.getBizType());
        detail.setMerchantId(order.getMerchantId());
        detail.setOrderAmount(order.getPayAmount());
        detail.setDiscountAmount(calcDiscount(order));
        detail.setPlatformFee(calcPlatformFee(order, rule));
        detail.setMerchantIncome(calcMerchantIncome(order, rule));
        detail.setSettleStatus("pending");

        settleRepo.save(detail);
        saveBizExtIfNeeded(detail, order, rule);

        auditLog.append("settle_confirmed", detail.getId(), op);
        return detail;
    }

    private BigDecimal calcPlatformFee(Order order, SettleRule rule) {
        if ("percent".equals(rule.getPlatformFeeMode())) {
            return order.getPayAmount()
                .multiply(rule.getPlatformFeeRate())
                .setScale(2, RoundingMode.HALF_UP);
        }
        // fixed / tiered 分支略
        return BigDecimal.ZERO;
    }
}
java 复制代码
@Service
public class SettlementExportService {

    public ExportFile exportByBatch(String batchNo, ExportProfile profile) {
        List<ColumnDef> columns = columnDefRepo.load(profile);
        List<Map<String, Object>> rows = reconcileRead.query(batchNo, columns);
        return csvWriter.write(rows, columns);
    }
}

所有结算明细生成只经 SettlementCommandService;导出只经 SettlementExportService,Admin 控制台不得各模块各自写导出 SQL。

与统一后台的关系

结算中枢与订单中枢、用户核心资料同属统一后台层:

  • 统一后台: 结算导出、批次确认、争议标记在同一 Admin 入口,按 biz_type 筛选。
  • 数据互通: 结算明细关联统一 order_idmerchant_iduser_id,禁止各模块独立结算副本。
  • 能力共享: 新增导出列、调整核对字段,在 settlement-hub/export-config 一处变更,各业态同步生效。

光合同城国内综合形态走统一后台一体化路线:内置多业务模块,模块数据互通、后台统一管理。成品可直接部署;支持私有化源码与按需定制,商务结算规则由客户自行确定。

验收清单(技术评审可用)

  1. 结算主表是否全业态共用?禁止各模块复制 settle_detail
  2. 导出列是否 YAML 可配置?财务临时加列是否不必改各模块代码?
  3. 加第三业态时,结算规则是否 inherit: common 即可?
  4. 结算写路径是否唯一?禁止业态插件直连 UPDATE 结算表。
  5. 统一 Admin 是否一处导出,而非每个业态各导一份再拼?

本地生活服务系统里,模块边界与结算字段怎么拆,决定了后期是改配置还是反复返工。先把结算中枢和导出配置收敛,再按节奏叠业态插件,比每个模块各存各的结算逻辑,往往更稳。

相关推荐
IT_陈寒15 分钟前
Vue的嵌套组件竟然吃掉了我的事件?
前端·人工智能·后端
默_笙16 分钟前
🏠 「LLM Notes」:我用 Next.js + Redis 给自己造了个笔记博客
前端·javascript
风骏时光牛马21 分钟前
AI开发平台异常指标实时监控告警
前端
roman_日积跬步-终至千里35 分钟前
【数据治理(6)】DataOps 不是调度工具,而是让数据产品长期可信的运行机制
java·服务器·网络
来了就未晚36 分钟前
Python基础 学习代码存储与命名规范
开发语言·python·学习
xierui1231231 小时前
OpenAI 拟停止向 Cursor供模:如何设计不绑供应商的AIAgent架构
人工智能·系统架构·软件工程
落魄大学生之流水线上谋生计1 小时前
LangGraph 基本用法指南
java·windows·python·langchain
布莱克6051 小时前
数组详解:定义、作用、应用场景及与链表的区别(C/C++ 代码讲解)
c语言·开发语言·c++·数组
2601_962128411 小时前
SpringBoot篇(缓存层)
java·spring boot·缓存