攻城狮朋友圈:终日乾乾,或跃在渊

做个财务系统

强壮,够用,简单

Created by 史荣久 @trydofor


大众化功能

大厂才需要

以上都跳过 :-)


住店的例子

话说,

3人去住店,

房费,总共30元,

AA制,每人10元。

优惠,

只要25元,退5元。

服务生,退每人1元,

自己私吞2元。

现在,

1元,去哪儿了?

因为,

(10 - 1) x 3 + 2 = 29

那么,哪不对?

咋记?咋查?


财务的问题


账户要强壮

登录账户,财务账户

内部账户,外部账户

账户控制,安全防范

登录账户,通常是实体账户。

财务账户,通常是虚拟账户。

前后两者,是一对多的关系。

内部账户,现金,虚拟币。

外部账户,微信,信用卡。

划定边界,分流隔离。

激活、冻结、安全保护

充值、提现、币型转换

支付、透支、规则限定


例:登录账户

身份类 安全类 控制类 辅助类
姓名 算法 用户状态 创建
邮箱 加盐 安全记录 更新
电话 密文 信息指纹  

ID 姓名 状态 类型
101 批处理 不可登录 系统
202 王老板 需改密码 店主
303 路人甲 未实名制 客人

例:财务账户

ID UID 类别 币种 余额
20201 王老板 现金 CNY 0.00
20202 王老板 虚拟币 CNY 0.00
30301 路人甲 现金 CNY 10.00
30302 路人甲 虚拟币 CNY 0.00

例:余额变化

UID 类别 币种 余额
王老板 现金 CNY +30.00
路人们 现金 CNY -30.00
王老板 现金 CNY -5.00
路人们 现金 CNY +3.00
伙计? 现金 CNY +2.00

记账要够用

单边帐,简单不定够用

复式账,强大但会繁琐

一定的会计分录知识

必须的原则

历史不可变

信息可追溯

借贷必相等

数值的精度

Double的小数位不准

Decimal的舍入规则

平均数的舍入误差

二进制表示小数

X = 1/2 + 1/4 + 1/8 + …

console.log(1.1 + 2.2)    // 3.3000000000000003 > 3.3
console.log(1.0 - 0.42)   // 0.5800000000000001 > 0.58
console.log(17.9 * 100)   // 1789.9999999999998 < 1790
console.log(123.3 / 100)  // 1.2329999999999999 < 1.233

要注意精度和容量

-- 共 10位,小数点4位,整数6位
`BALANCE` DECIMAL(10,4) NOT NULL COMMENT '账户余额',
// false, 不用使用`浮点型`构造函数
new BigDecimal(3.3).equals(new BigDecimal("3.3"))
// 0, 用compareTo 比较数值大小
new BigDecimal(3).compareTo(new BigDecimal("3.0"))
// 4, 除法,要有精度和舍入规则
BigDecimal.TEN.divide(new BigDecimal("3.3"), ROUND_CEILING)

ROUND_X 舍入规则


例:交易流水

ID 订单 类型 币种 金额 付款方 收款方
T1 D1 现金 CNY 30.00 路人们 王老板
T2 D2 现金 CNY 5.00 王老板 路人们

例:交易明细

ID 交易 类型 币种 金额 摘要
R1 T1 现金 CNY 30.00 房间X
R2 T2 现金 CNY 5.00 退款

思考:伙计怎么算?


对账要简单

私吞,别做梦了!

脏数据,有人肉运维!


系统内各种余额=0

-- 所有账户的余额 = 0
SELECT SUM(`余额`) FROM `财务账户`
-- 所有类型的余额 = 0
SELECT SUM(`余额`)`类型` FROM `财务账户` GROUP BY `类型`
-- 所有类型的余额 = 0
SELECT SUM(`余额`)`币种` FROM `财务账户` GROUP BY `币种`

如何满足零和

内部用户,正值表示。

外部账户,负值表示。

无中生有,负值表示。

UID 类别 币种 余额
路人们 现金 CNY 1000.00
支付宝 现金 CNY -994.00
手续费 现金 CNY -6.00

UID 类别 币种 余额
路人们 优惠券 CNY 100.00
双十一 优惠券 CNY -100.00

余额变化=交易净值

-- 单次余额变化 = 单次交易净值
SELECT SUM(`金额`) FROM `交易流水` WHERE `订单`= ?
-- 所有余额变化 = 所有交易净值
SELECT SUM(`金额`) FROM `交易流水`

假设:伙计是手续费

ID 订单 类型 币种 金额 付款方 收款方
T4 D1 现金 CNY 30.00 路人们 王老板
T5 D2 现金 CNY 3.00 王老板 路人们
T6 D2 现金 CNY 2.00 王老板 手续费

假设:伙计被给小费

ID 订单 类型 币种 金额 付款方 收款方
T4 D1 现金 CNY 30.00 路人们 王老板
T5 D2 现金 CNY 5.00 王老板 路人们
T6 D2 现金 CNY 2.00 路人们 小费

如何修正历史数据

如果,原路退款,算一次交易。

ID 订单 修正 币种 金额 付款方 收款方
T4 D1 NULL CNY 30.00 路人们 王老板
T5 D1 T4 CNY -5.00 路人们 王老板

如何修正修正数据

很不幸,忙中出错,错上加错。

ID 订单 修正 币种 金额 付款方 收款方
T4 D1 NULL CNY 30.00 路人们 王老板
T5 D1 T4 CNY -5.00 路人们 王老板
T6 D1 T5 CNY 2.00 路人们 王老板

明细要多明细

余额 - 用户额度最新值

交易 - 订单的资金流向

明细 - 交易的资金构成

交易的物品有关

审计的会计科目


假设路人定了3间房

ID 交易 类型 币种 金额 摘要
R1 T1 现金 CNY 10.00 房间甲
R2 T1 现金 CNY 10.00 房间乙
R3 T1 现金 CNY 10.00 房间丙

一个复杂的例子

付30元3间房,

现金10元,微信20元。

退现金5元。

伙计,不来捣乱了 :)

余额变化-现金

UID 类别 币种 余额
<系统> 现金 CNY -10.00
王老板 现金 CNY +10.00

余额变化-微信

充值过程

UID 类别 币种 余额
<系统> 微信 CNY -18.00
<系统> 微信费 CNY -2.00
客人们 微信 CNY +20.00

付款过程

UID 类别 币种 余额
客人们 微信 CNY -20.00
王老板 微信 CNY +20.00

余额变化-退现金

UID 类别 币种 余额
<系统> 现金 CNY +5.00
王老板 现金 CNY -5.00

流水变化-付款

ID 订单 修正 类别 金额 付款方 收款方
T1 D1 NULL 现金 10.00 路人们 王老板
T2 D1 NULL 微信 20.00 路人们 王老板

流水变化-退款

ID 订单 修正 类别 金额 付款方 收款方
T1 D1 NULL 现金 10.00 路人们 王老板
T2 D1 NULL 微信 20.00 路人们 王老板
T3 D1 T1 现金 -5.00 路人们 王老板

明细变化-付款

ID 交易 类型 币种 金额 摘要
R1 T1 现金 CNY 10.00 房间甲
R2 T2 微信 CNY 10.00 房间乙
R3 T2 微信 CNY 10.00 房间丙

明细变化-退款

ID 交易 类型 币种 金额 摘要
R1 T3 现金 CNY 5.00 退款

Happy Ending

安全第一条,记账要规范。

2019-03-03 @gitunion

Fuck me on GitHub