如何构建一个合理的电商订单分账系统

开发前准备

  • 重点:如果你正在开发的或从别人那接手的项目,财务系统或结算逻辑中可以存在负数,那么建议你不用准备分账了,不管你用什么办法(业务调整、将负账转变为逻辑负账、其他…),先把负数从系统中消除,不然硬给一个存在负账的系统上分账,只会带来灾难。
  • 找一家合适的支付机构,(不推荐通联,坑多,他们内部的系统估计是堆屎山)
  • 问清楚以下问题,(说多了都是泪,都是在通联踩的坑)
    1. 会员(在支付机构中开户的用户)是否同时支持商家身份和消费者身份,如果你的系统没分角色,就要考虑怎么让你系统的用户和会员角色建立一对多的映射,消费时取消费者会员角色,收款时取商家会员角色
    2. 分账时人数限制,如果有单笔分账的人数限制,就要考虑怎么让你的订单分账逻辑支持多次分账
    3. 确保公司的对公账户能正常在支付机构提现、充值、转账
    4. 老用户如何迁移到支付机构的用户系统中,如果支持批量导入,先测试一遍,不要对这些支付机构的系统抱有乐观心态
    5. 。。。后面想起来了再加

业务设计

  1. 确保你的系统对每一笔订单都能明确知道相关角色和平台应该得到多少收益,如果这都不能确定,你的系统就不是一个可以正常的电商系统。
  2. 如果订单上剩余的现金(订单实付金额-订单实退金额)小于相关人的收益总和,显然在现金上是不够完成分账的,这时候就要使用代金券功能,支付一笔足额的代金券,补齐每一方的收益(除了平台,多出来的支出本身就是平台自己承担)
  3. 计算公式:
    订单剩余现金 = 订单实付金额 - 订单实退金额 
    # 如果平台收益为负,说明平台在这笔订单上收益为0,还要支出同等的正数金额的代金券去补齐其他三方的收益
    平台收益 = 订单剩余现金 - 三方收益总和(排除平台) 
    实际平台收益 = max(平台收益,0)
    # 如果现金分账比例=1.0,则不需要考虑代金券,说明订单剩余的现金够分账
    现金分账比例 = 订单剩余现金/(实际平台收益 + 三方收益总和) 
    现金分账比例 = min(现金分账比例,1.0) # 分账比例最大就是1.0
    三方现金收益总和 = 三方收益总和 * 现金分账比例
    实际平台收益 = 订单剩余现金 - 三方现金收益总和 # 考虑到小数的精度问题,用减法将剩余的金额归并到平台头上
    代金券支付金额 = 三方代金券收益总和 = 三方收益总和 - 三方现金收益总和
    代金券分账比例 = 代金券支付金额/三方收益总和
    
  4. 实际举例
    订单剩余现金 = 50 - 40 #10
    三方收益总和 = 60
    平台收益 = 订单剩余现金 - 三方收益总和 # 10-60=-50
    实际平台收益 = max(平台收益,0) # 0
    现金分账比例 = 订单剩余现金/(实际平台收益 + 三方收益总和) # 10/(0+60)=0.166
    现金分账比例 = min(现金分账比例,1.0) # 0.166
    三方现金收益总和 = 三方收益总和 * 现金分账比例 # 60*0.166=9.96
    实际平台收益 = 订单剩余现金 - 三方现金收益总和 # 10-9.96=0.04
    代金券支付金额 = 三方代金券收益总和 = 三方收益总和 - 三方现金收益总和 # 60-9.96=50.04
    代金券分账比例 = 代金券支付金额/三方收益总和 # 50.04/60=0.834
    # 0.834 + 0.166=1.0 刚好等于1
    

数据库设计

  1. 分账主单表
CREATE TABLE `order_split` (
	`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键',
	`order_id` BIGINT NOT NULL COMMENT '主订单id',
	`order_no` VARCHAR(100) NOT NULL COMMENT '主订单编号' COLLATE 'utf8mb4_general_ci',
	`receiver_id` BIGINT UNSIGNED NOT NULL COMMENT '收款人',
	`split_no` VARCHAR(100) NOT NULL DEFAULT '' COMMENT '分账编号' COLLATE 'utf8mb4_general_ci',
	`out_split_no` VARCHAR(100) NOT NULL DEFAULT '' COMMENT '外部分账编号' COLLATE 'utf8mb4_general_ci',
	`batch_no` VARCHAR(100) NOT NULL DEFAULT '' COMMENT '批次编号' COLLATE 'utf8mb4_general_ci',
	`amounts` INT UNSIGNED NOT NULL DEFAULT '0' COMMENT '总分账金额',
	`plat_amount` INT UNSIGNED NOT NULL DEFAULT '0' COMMENT '平台分账金额',
	`order_type` TINYINT UNSIGNED NOT NULL DEFAULT '0' COMMENT '订单类型 1消费订单 2代金券订单',
	`split_status` TINYINT UNSIGNED NOT NULL DEFAULT '0' COMMENT '分账状态 0创建 1成功 2失败',
	`call_remark` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '分账|回调备注' COLLATE 'utf8mb4_general_ci',
	`create_time` DATETIME NOT NULL DEFAULT (now()) COMMENT '创建时间',
	`update_time` DATETIME NOT NULL DEFAULT (now()) ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
	PRIMARY KEY (`id`) USING BTREE,
	INDEX `order_id` (`order_id`) USING BTREE
)
COLLATE='utf8mb4_general_ci'
ENGINE=InnoDB
;
  1. 分账明细
CREATE TABLE `order_split_item` (
	`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键',
	`split_id` BIGINT UNSIGNED NOT NULL COMMENT '订单分账id',
	`customer_id` BIGINT UNSIGNED NOT NULL COMMENT '收益收款人',
	`role_type` TINYINT UNSIGNED NOT NULL COMMENT '分账角色类型',
	`amount` INT UNSIGNED NOT NULL DEFAULT '0' COMMENT '分账收益',
	`remark` VARCHAR(255) NOT NULL COMMENT '备注' COLLATE 'utf8mb4_general_ci',
	`create_time` DATETIME NOT NULL DEFAULT (now()) COMMENT '创建时间',
	`update_time` DATETIME NOT NULL DEFAULT (now()) ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
	PRIMARY KEY (`id`) USING BTREE,
	INDEX `split_id` (`split_id`) USING BTREE,
	INDEX `customer_id` (`customer_id`) USING BTREE
)
COLLATE='utf8mb4_general_ci'
ENGINE=InnoDB
;

3.代金券订单表

CREATE TABLE `voucher_order` (
	`id` BIGINT UNSIGNED NOT NULL COMMENT '主键',
	`order_id` BIGINT UNSIGNED NOT NULL COMMENT '主订单id',
	`customer_id` BIGINT UNSIGNED NOT NULL COMMENT '主订单下单用户id',
	`receiver_id` BIGINT UNSIGNED NOT NULL COMMENT '收款人id',
	`order_no` VARCHAR(100) NOT NULL DEFAULT '' COMMENT '流水号' COLLATE 'utf8mb4_general_ci',
	`out_trade_no` VARCHAR(100) NOT NULL DEFAULT '' COMMENT '外部流水号' COLLATE 'utf8mb4_general_ci',
	`payment` INT UNSIGNED NOT NULL,
	`refund` INT UNSIGNED NOT NULL DEFAULT '0',
	`split_ratio` DECIMAL(5,4) NULL DEFAULT NULL COMMENT '代金券占整个分账总额的比例',
	`remark` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '备注' COLLATE 'utf8mb4_general_ci',
	`pay_status` TINYINT UNSIGNED NOT NULL DEFAULT '0' COMMENT '支付状态 0创建 1支付成功 2支付失败' ,
	`create_time` DATETIME NOT NULL DEFAULT (now()) COMMENT '创建时间',
	`update_time` DATETIME NOT NULL DEFAULT (now()) ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
	`pay_time` DATETIME NULL DEFAULT NULL COMMENT '支付时间',
	PRIMARY KEY (`id`) USING BTREE,
	INDEX `order_id` (`order_id`) USING BTREE,
	INDEX `customer_id` (`customer_id`) USING BTREE
)
COLLATE='utf8mb4_general_ci'
ENGINE=InnoDB
;

代码设计

自行实现...
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值