第四章 核心业务表设计(一):组织架构与基础数据表

各位同学,今天我们来聊聊ERP系统里最基础、也最容易被忽视的部分——组织架构与基础数据表。说白了,就是公司、部门、员工、客户、供应商这五张表。

我做了十几年ERP,见过太多项目栽在基础数据上。有的公司上线三个月,发现部门树怎么都查不对;有的客户数据重复率高达30%。嗯,这些坑我都踩过。今天就把我的经验掰开了讲。

4.1 公司表:别小看这个根节点

公司表是整个ERP的根。你想想看,所有业务数据最终都要归属到某个公司实体上。我习惯把公司表设计成支持多组织的,哪怕你现在只有一个公司。

核心字段设计:

  • company_id:主键,自增或UUID
  • company_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 供应商表:比客户更复杂

供应商表的设计逻辑和客户表类似,但多了几个关键点:资质管理、品类管理、采购协议。

我建议供应商表至少包含以下核心信息:

  • 基本信息:名称、编码、税号、银行账户
  • 资质信息:营业执照、生产许可证、有效期
  • 品类信息:供应商能提供哪些物料或服务
  • 评级信息:质量等级、交货准时率、价格水平

设计要点:

  1. 供应商编码建议用字母S开头,和客户编码C区分
  2. 资质到期提醒:用定时任务扫描,提前30天发通知
  3. 供应商黑名单:一旦拉黑,所有采购流程自动拦截
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
索引策略 高频查询字段加索引 编码、状态、外键

嗯,今天就先讲到这里。这五张表看似简单,但设计得好不好,直接决定了后续所有业务模块的稳定性和扩展性。下一章我们会讲单据核心表的设计,到时候你会发现,今天的基础数据表就是所有业务的基石。

记住一句话:基础不牢,地动山摇。我在项目里见过太多因为基础数据设计不合理,导致后期改得痛不欲生的案例。希望你们能少走这些弯路。