第四章 核心业务表设计(一):组织架构与基础数据表
各位同学,今天我们来聊聊ERP系统里最基础、也最容易被忽视的部分——组织架构与基础数据表。说白了,就是公司、部门、员工、客户、供应商这五张表。
我做了十几年ERP,见过太多项目栽在基础数据上。有的公司上线三个月,发现部门树怎么都查不对;有的客户数据重复率高达30%。嗯,这些坑我都踩过。今天就把我的经验掰开了讲。
4.1 公司表:别小看这个根节点
公司表是整个ERP的根。你想想看,所有业务数据最终都要归属到某个公司实体上。我习惯把公司表设计成支持多组织的,哪怕你现在只有一个公司。
核心字段设计:
company_id:主键,自增或UUIDcompany_code:公司编码,业务上唯一company_name:公司全称parent_company_id:上级公司ID,支持集团架构legal_person:法定代表人tax_id:纳税人识别号status:状态(启用/停用)
CREATE TABLE sys_company (
company_id BIGINT PRIMARY KEY AUTO_INCREMENT,
company_code VARCHAR(32) NOT NULL UNIQUE,
company_name VARCHAR(200) NOT NULL,
parent_company_id BIGINT DEFAULT NULL,
legal_person VARCHAR(50),
tax_id VARCHAR(20),
status TINYINT DEFAULT 1,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_parent (parent_company_id),
INDEX idx_status (status)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
避坑指南:我曾经遇到一个客户,公司编码用了中文。结果在对接税务系统时,编码转换出了大问题。从那以后,我坚持公司编码只用大写字母和数字,长度控制在8位以内。
4.2 部门表:树形结构的经典实现
部门表是组织架构的核心。我见过有人用递归查询,也有人用路径枚举。我个人更推荐闭包表,虽然查询复杂点,但扩展性极好。
为什么会这样?因为部门层级经常变。今天销售部下面有三个组,明天可能就变成五个。闭包表能轻松应对这种变化。
| 字段名 | 类型 | 说明 |
|---|---|---|
| dept_id | BIGINT | 部门ID |
| dept_name | VARCHAR(100) | 部门名称 |
| parent_dept_id | BIGINT | 上级部门ID |
| dept_path | VARCHAR(500) | 路径,如 /1/2/5/ |
| dept_level | TINYINT | 层级深度 |
| company_id | BIGINT | 所属公司 |
-- 查询某个部门下的所有子部门
SELECT * FROM sys_department
WHERE dept_path LIKE CONCAT((SELECT dept_path FROM sys_department WHERE dept_id = ?), '%');
注意:部门表不要用无限级分类的递归查询。我在一个项目里见过,部门只有5层,但递归查询耗时3秒。后来改成路径枚举,直接降到10毫秒。
4.3 员工表:不只是个人信息
员工表是ERP里最活跃的表之一。除了基本信息,还要考虑组织归属、岗位信息、甚至权限继承。
我习惯把员工表拆成两部分:基础信息和组织关系。基础信息放姓名、身份证、手机号这些不变的东西;组织关系放部门、岗位、职级这些经常变的。
关键设计点:
- 员工编号:建议用业务含义编码,比如入职年份+流水号
- 主部门与兼职部门:一个人可能属于多个部门
- 直属上级:用于审批流和汇报关系
- 数据权限:员工能看到哪些公司的数据
CREATE TABLE sys_employee (
emp_id BIGINT PRIMARY KEY AUTO_INCREMENT,
emp_code VARCHAR(32) NOT NULL UNIQUE,
emp_name VARCHAR(50) NOT NULL,
id_card VARCHAR(18),
phone VARCHAR(20),
email VARCHAR(100),
primary_dept_id BIGINT NOT NULL,
position_id BIGINT,
manager_id BIGINT,
hire_date DATE,
status TINYINT DEFAULT 1,
INDEX idx_dept (primary_dept_id),
INDEX idx_manager (manager_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
我的经验:员工表一定要加一个data_scope字段,用来控制数据权限。比如销售只能看自己的客户,经理能看整个部门的。这个字段在后续的报表和查询中会频繁用到。
4.4 客户表:唯一性是个大问题
客户表是ERP里最头疼的表。为什么?因为同一个客户可能被不同业务员录入多次。我见过一个客户在系统里有7条记录,名字就差一个字。
解决这个问题,我建议用统一客户主数据的思路。所有客户必须经过审核才能成为正式客户。同时,建立客户查重机制。
| 字段 | 说明 | 查重规则 |
|---|---|---|
| customer_name | 客户名称 | 模糊匹配 |
| uniform_code | 统一社会信用代码 | 精确匹配 |
| contact_phone | 联系电话 | 精确匹配 |
| registered_address | 注册地址 | 模糊匹配 |
-- 客户查重SQL示例
SELECT customer_id, customer_name, uniform_code
FROM crm_customer
WHERE uniform_code = ?
OR customer_name LIKE CONCAT('%', ?, '%')
OR contact_phone = ?
ORDER BY created_at DESC;
我曾经踩过的坑:有个项目把客户表设计成一个大宽表,所有信息都放一起。结果客户地址变更时,要更新几十个字段。后来我改成客户主表+客户地址子表+客户联系人子表,灵活多了。
4.5 供应商表:比客户更复杂
供应商表的设计逻辑和客户表类似,但多了几个关键点:资质管理、品类管理、采购协议。
我建议供应商表至少包含以下核心信息:
- 基本信息:名称、编码、税号、银行账户
- 资质信息:营业执照、生产许可证、有效期
- 品类信息:供应商能提供哪些物料或服务
- 评级信息:质量等级、交货准时率、价格水平
设计要点:
- 供应商编码建议用字母S开头,和客户编码C区分
- 资质到期提醒:用定时任务扫描,提前30天发通知
- 供应商黑名单:一旦拉黑,所有采购流程自动拦截
CREATE TABLE pur_supplier (
supplier_id BIGINT PRIMARY KEY AUTO_INCREMENT,
supplier_code VARCHAR(32) NOT NULL UNIQUE,
supplier_name VARCHAR(200) NOT NULL,
tax_id VARCHAR(20),
bank_name VARCHAR(100),
bank_account VARCHAR(50),
qualification_level VARCHAR(20), -- A/B/C/D
status TINYINT DEFAULT 1,
blacklist_flag TINYINT DEFAULT 0,
created_at DATETIME,
INDEX idx_blacklist (blacklist_flag),
INDEX idx_status (status)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
个人建议:供应商的银行账户信息一定要单独建表。因为一个供应商可能有多个银行账户,而且账户变更频繁。我见过把银行账户直接放主表的,每次变更都要改主表,太痛苦了。
4.6 基础数据表的通用设计原则
最后,我总结几条通用原则,这些是我在十几个ERP项目里摸爬滚打总结出来的:
| 原则 | 说明 | 示例 |
|---|---|---|
| 编码统一 | 所有基础数据都有唯一编码 | 公司编码、部门编码、员工编码 |
| 状态管理 | 每个表都有状态字段 | 启用/停用/冻结/删除 |
| 时间戳 | 记录创建时间和更新时间 | created_at, updated_at |
| 软删除 | 不要物理删除数据 | is_deleted = 0/1 |
| 索引策略 | 高频查询字段加索引 | 编码、状态、外键 |
嗯,今天就先讲到这里。这五张表看似简单,但设计得好不好,直接决定了后续所有业务模块的稳定性和扩展性。下一章我们会讲单据核心表的设计,到时候你会发现,今天的基础数据表就是所有业务的基石。
记住一句话:基础不牢,地动山摇。我在项目里见过太多因为基础数据设计不合理,导致后期改得痛不欲生的案例。希望你们能少走这些弯路。