本地生活服务系统:成品模块和定制接口怎么划界

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

下文从模块边界、结算表结构、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 是否一处导出,而非每个业态各导一份再拼?

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

相关推荐
2603_9651481114 分钟前
家居百货蓝海:API挖掘高复购率生活小商品
大数据·服务器·人工智能·python·生活
萧瑟余晖22 分钟前
Java深入解析篇三十三之密封类与接口(Sealed Classes)详解
java·开发语言
2501_9304724425 分钟前
踩坑|CodeBuddy权限配置:AI误删文件、乱跑命令、.env泄露怎么防
人工智能·ai编程
杨杨杨大侠3 小时前
从 Token 到蒸馏:一步步理解大模型如何工作
aigc·openai·ai编程
小淮AI3 小时前
从“刷题”到“追问”:AI课堂正在重塑哪些学习旧习惯?
人工智能·学习
cui_ruicheng4 小时前
LangChain 应用开发(十四):Agent 上下文与记忆机制
服务器·人工智能·python·langchain
平头哥~4 小时前
Day 07 _ 那条 1px 的线,为什么在手机上忽粗忽细
前端·css·样式·学习资料
论迹复利4 小时前
FreeRTOS 在 RISC-V 上是如何“点火“的 —— 从 main() 到第一个任务的完整链路
java·开发语言·risc-v
小江的记录本4 小时前
【CSS】CSS 动画:transition、animation、transform、CSS3 新特性(附《思维导图》)
前端·css·安全·spring·前端框架·css3·动画