news 2026/5/8 16:59:35

ERP数据库设计规范:财务模块实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
ERP数据库设计规范:财务模块实战

一、表设计的核心原则

ERP系统的数据库设计,和普通业务系统不同。它需要支撑业务高频操作、确保数据一致性、支持复杂查询。以下是财务模块设计的几个关键点。

1. 凭证表的设计

CREATE TABLE fm_voucher ( id BIGINT PRIMARY KEY, voucher_no VARCHAR(30) NOT NULL, -- 凭证号 accounting_period VARCHAR(6) NOT NULL, -- 会计期间,格式:YYYYMM voucher_date DATE NOT NULL, attachment_count INT DEFAULT 0, -- 附件张数 poster_id BIGINT, poster_time DATETIME, poster_ip VARCHAR(50), make_id BIGINT, make_time DATETIME, source_module VARCHAR(20), -- 来源模块:GL/AR/AP/INV source_id BIGINT, -- 来源单据ID is_reversed BIT DEFAULT 0, -- 是否被冲销 reverse_id BIGINT, -- 冲销凭证ID approve_status VARCHAR(10), -- 审核状态 approve_id BIGINT, approve_time DATETIME, is_deleted BIT DEFAULT 0, ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP );

注意几个字段:

source_module和source_id:业务系统生成的凭证必须记录来源,否则业务单据和凭证之间的联查就会断裂。is_reversed和reverse_id:冲销凭证和被冲销凭证之间要建立双向关联,否则查历史数据时容易遗漏。accounting_period用VARCHAR而非INT:这样可以支持跨年查询,SQL写法也更直观。

2. 分录表的设计

凭证表和分录表是一对多关系。

CREATE TABLE fm_voucher_entry ( id BIGINT PRIMARY KEY, voucher_id BIGINT NOT NULL, entry_no INT NOT NULL, -- 分录序号 account_id BIGINT NOT NULL, -- 科目ID account_code VARCHAR(20) NOT NULL, -- 科目编码,冗余存储加速查询 direction VARCHAR(4), -- 借贷方向:DR/CR amount DECIMAL(18,2) NOT NULL, -- 金额,精确到分 quantity DECIMAL(18,6), -- 数量,制造业需要 unit_price DECIMAL(18,6), -- 单价 currency_code VARCHAR(3), -- 币种,默认CNY exchange_rate DECIMAL(18,6) DEFAULT 1,-- 汇率 secondary_customer_id BIGINT, -- 辅助核算:客户 supplier_id BIGINT, -- 辅助核算:供应商 department_id BIGINT, -- 辅助核算:部门 person_id BIGINT, -- 辅助核算:个人 project_id BIGINT, -- 辅助核算:项目 cash_flow_id BIGINT, -- 现金流量项目 memo VARCHAR(200), -- 摘要 is_deleted BIT DEFAULT 0 );

金额字段用DECIMAL(18,2),适合绝大多数企业的最大金额范围(999万亿级别)。如果企业有特殊需求(如金融行业),可以改为DECIMAL(24,4)。

辅助核算字段的设计:财务系统必须支持多维辅助核算。常见的做法是把每个辅助核算方向做成独立字段,而不是用JSON或EAV模式。独立字段的优势是:查询性能好、索引支持好、报表工具兼容性好。

3. 会计期间的设计

CREATE TABLE bd_accounting_period ( org_id BIGINT, period_year INT NOT NULL, period_month INT NOT NULL, start_date DATE NOT NULL, end_date DATE NOT NULL, period_status VARCHAR(10), -- Open/Closed/Frozen is_current_period BIT DEFAULT 0, allow_post_before_date DATETIME, -- 允许在什么时候之前制单 PRIMARY KEY (org_id, period_year, period_month) );

会计期间的”锁定期”很关键。月末结账后,历史期间的凭证要锁定,禁止修改。allow_post_before_date字段可以精确控制制单的时间权限,比简单的”已结账/未结账”两态更灵活。

二、事务处理中的并发控制

1. 凭证编号的并发问题

多用户同时制单时,凭证号不能重复。

错误做法:

危险:可能产生重复编号 INSERT INTO fm_voucher (voucher_no, ...) VALUES ((SELECT MAX(voucher_no)+1 FROM fm_voucher), ...);

正确做法:使用数据库的序列或锁机制。

方法一:序列(MySQL 8.0+) voucher_no = CONCAT('JZ', NEXT VALUE FOR seq_voucher_no); 方法二:表锁 START TRANSACTION; SELECT MAX(voucher_no) FROM fm_voucher WHERE accounting_period = ? FOR UPDATE; 生成新编号 INSERT INTO fm_voucher ...; COMMIT;

2. 余额表更新的原子性

科目余额表(account_balance)是财务系统最热的表,每次记账都要更新。如果并发控制不好,会导致余额数据不一致。

UPDATE ac_account_balance SET period_amount_dr = period_amount_dr + ?, period_amount_cr = period_amount_cr + ?, year_amount_dr = year_amount_dr + ?, year_amount_cr = year_amount_cr + ?, ts = NOW() WHERE account_id = ? AND period_year = ? AND period_month = ? AND direction = ?; -- 如果影响行数为0,说明该科目期间余额记录不存在,需要插入 INSERT INTO ac_account_balance (...) SELECT ... WHERE NOT EXISTS ( SELECT 1 FROM ac_account_balance WHERE ... );

这种方式比先查后改更高效,也能避免TOCTOU(检查-使用-检查)竞争条件。如果企业并发量特别大,可以考虑在应用层做余额队列:所有记账请求先入队,队列消费者顺序处理,避免数据库层面的锁竞争。

三、数据归档与性能维护

财务数据有一个特点:越老的数据访问频率越低,但必须保留。

按会计年度做数据归档是个好策略:

  1. 每年12月结账后,将历史年度数据迁移到归档库。

  2. 归档库和现行库分开,现行库保持在合理数据量内。

  3. 报表查询时,现行库和归档库分别查询后合并结果。

归档存储过程示例 CREATE PROCEDURE sp_archive_financial_data(IN p_year INT) BEGIN 1. 迁移凭证 INSERT INTO fm_voucher_archive SELECT * FROM fm_voucher WHERE LEFT(accounting_period, 4) = CAST(p_year AS CHAR); 2. 迁移分录 INSERT INTO fm_voucher_entry_archive SELECT e.* FROM fm_voucher_entry e JOIN fm_voucher_archive v ON e.voucher_id = v.id WHERE v.accounting_period LIKE CONCAT(p_year, '%'); 3. 迁移余额 INSERT INTO ac_account_balance_archive SELECT * FROM ac_account_balance WHERE period_year < p_year; 4. 删除现行库数据 DELETE FROM fm_voucher_entry WHERE voucher_id IN (SELECT id FROM fm_voucher WHERE LEFT(accounting_period, 4) = CAST(p_year AS CHAR)); DELETE FROM fm_voucher WHERE LEFT(accounting_period, 4) = CAST(p_year AS CHAR); DELETE FROM ac_account_balance WHERE period_year < p_year; END;

归档前务必检查:

  • 当年数据是否已全部结账

  • 归档后的查询接口是否需要修改

  • 归档库的备份策略是否完善

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/8 16:58:55

力扣56合并区间

以数组 intervals 表示若干个区间的集合&#xff0c;其中单个区间为 intervals[i] [starti, endi] 。请你合并所有重叠的区间&#xff0c;并返回 一个不重叠的区间数组&#xff0c;该数组需恰好覆盖输入中的所有区间 。 class Solution(object):def merge(self, intervals):&q…

作者头像 李华
网站建设 2026/5/8 16:58:41

观察Taotoken按Token计费模式下的月度账单明细

&#x1f680; 告别海外账号与网络限制&#xff01;稳定直连全球优质大模型&#xff0c;限时半价接入中。 &#x1f449; 点击领取海量免费额度 观察Taotoken按Token计费模式下的月度账单明细 对于个人开发者或小型团队而言&#xff0c;在使用大模型API时&#xff0c;成本控制…

作者头像 李华
网站建设 2026/5/8 16:57:49

美军加速AI军事化有可能被反噬

根据“国防科技新闻”4月29日公开报道&#xff0c;五角大楼首席数字与人工智能办公室证实&#xff0c;美军已通过“GenAI.mil”平台部署了超过10万个AI智能体&#xff08;AI Agents&#xff09;。这些“数字化士兵”全天候运行在作战数据平台上&#xff0c;旨在将情报分析周期从…

作者头像 李华
网站建设 2026/5/8 16:57:43

如何通过curl命令直接测试taotoken的api接口稳定性与响应

&#x1f680; 告别海外账号与网络限制&#xff01;稳定直连全球优质大模型&#xff0c;限时半价接入中。 &#x1f449; 点击领取海量免费额度 如何通过curl命令直接测试taotoken的api接口稳定性与响应 在开发或调试过程中&#xff0c;有时我们需要绕过SDK&#xff0c;直接与…

作者头像 李华