Files
crm_project/server/database/schema.sql
T
hankin 423baff73b v0.1.0: CRM/ERP 系统内测版本 - 安全加固完成
- Docker bridge 网络隔离(8000 端口封死)
- Gunicorn 4 Worker 多进程
- Alembic 数据库迁移基线
- 日志轮转 20m×3
- JWT 密钥 + DB 密码 + CORS 收紧
- 3-2-1 备份链路(NAS + R740-B 冷备)
- 连接池 pool_pre_ping + pool_recycle=3600
2026-03-16 07:31:37 +00:00

540 lines
28 KiB
PL/PgSQL
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
-- ============================================================
-- 润滑油行业 B2B ERP/CRM 综合系统 - PostgreSQL 数据库建模
-- 技术栈: Python (FastAPI) + PostgreSQL
-- 生成时间: 2026-02-27
-- ============================================================
-- 启用 uuid-ossp 扩展(用于 UUID 主键生成)
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
-- ============================================================
-- 通用函数:自动更新 updated_at 触发器
-- ============================================================
CREATE OR REPLACE FUNCTION update_updated_at_column()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = CURRENT_TIMESTAMP;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
-- ************************************************************
-- 1. RBAC 权限域
-- ************************************************************
-- ============================================================
-- 1.1 部门树表 sys_departments
-- ============================================================
CREATE TABLE sys_departments (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
parent_id UUID REFERENCES sys_departments(id),
name VARCHAR(100) NOT NULL,
sort_order INT DEFAULT 0,
status SMALLINT DEFAULT 1, -- 1:启用 0:停用
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
is_deleted BOOLEAN DEFAULT FALSE
);
COMMENT ON TABLE sys_departments IS '部门树表';
COMMENT ON COLUMN sys_departments.id IS '部门主键 UUID';
COMMENT ON COLUMN sys_departments.parent_id IS '父级部门IDNULL表示顶级';
COMMENT ON COLUMN sys_departments.name IS '部门名称';
COMMENT ON COLUMN sys_departments.sort_order IS '排序序号';
COMMENT ON COLUMN sys_departments.status IS '状态 1:启用 0:停用';
CREATE INDEX idx_dept_parent ON sys_departments(parent_id) WHERE is_deleted = FALSE;
CREATE TRIGGER trg_dept_updated
BEFORE UPDATE ON sys_departments
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
-- ============================================================
-- 1.2 角色表 sys_roles
-- ============================================================
CREATE TABLE sys_roles (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
role_name VARCHAR(50) NOT NULL UNIQUE,
data_scope VARCHAR(20) NOT NULL DEFAULT 'self', -- all / dept_and_sub / self
menu_keys JSONB DEFAULT '[]'::JSONB, -- 菜单与按钮权限键集合
description VARCHAR(255),
status SMALLINT DEFAULT 1,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
is_deleted BOOLEAN DEFAULT FALSE
);
COMMENT ON TABLE sys_roles IS '角色表';
COMMENT ON COLUMN sys_roles.id IS '角色主键 UUID';
COMMENT ON COLUMN sys_roles.role_name IS '角色名称';
COMMENT ON COLUMN sys_roles.data_scope IS '数据权限范围: all=全部 / dept_and_sub=本部门及下属 / self=仅本人';
COMMENT ON COLUMN sys_roles.menu_keys IS '拥有的菜单/按钮权限键列表 (JSONB数组)';
CREATE TRIGGER trg_role_updated
BEFORE UPDATE ON sys_roles
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
-- ============================================================
-- 1.3 员工/账号表 sys_users
-- ============================================================
CREATE TABLE sys_users (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
dept_id UUID REFERENCES sys_departments(id),
role_id UUID REFERENCES sys_roles(id),
username VARCHAR(50) NOT NULL UNIQUE,
password_hash VARCHAR(255) NOT NULL,
real_name VARCHAR(50),
phone VARCHAR(20),
email VARCHAR(100),
avatar_url VARCHAR(500),
status SMALLINT DEFAULT 1, -- 1:在职 0:离职
last_login_at TIMESTAMP,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
is_deleted BOOLEAN DEFAULT FALSE
);
COMMENT ON TABLE sys_users IS '员工/账号表';
COMMENT ON COLUMN sys_users.id IS '用户主键 UUID';
COMMENT ON COLUMN sys_users.dept_id IS '所属部门ID';
COMMENT ON COLUMN sys_users.role_id IS '所属角色ID';
COMMENT ON COLUMN sys_users.username IS '登录账号';
COMMENT ON COLUMN sys_users.password_hash IS '密码哈希值';
COMMENT ON COLUMN sys_users.real_name IS '真实姓名';
COMMENT ON COLUMN sys_users.status IS '状态 1:在职 0:离职';
CREATE INDEX idx_user_dept ON sys_users(dept_id) WHERE is_deleted = FALSE;
CREATE INDEX idx_user_role ON sys_users(role_id) WHERE is_deleted = FALSE;
CREATE TRIGGER trg_user_updated
BEFORE UPDATE ON sys_users
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
-- ************************************************************
-- 2. CRM 客户域
-- ************************************************************
-- ============================================================
-- 2.1 客户主表 crm_customers
-- ============================================================
CREATE TABLE crm_customers (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
name VARCHAR(200) NOT NULL,
level CHAR(1) DEFAULT 'C', -- A / B / C 三级
industry VARCHAR(100),
contact VARCHAR(50), -- 联系人姓名
phone VARCHAR(30),
email VARCHAR(100),
address TEXT,
ai_score NUMERIC(5,2) DEFAULT 0, -- AI 客情健康度评分 0~100
owner_id UUID REFERENCES sys_users(id), -- 负责销售
status SMALLINT DEFAULT 1, -- 1:活跃 0:冻结
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
is_deleted BOOLEAN DEFAULT FALSE
);
COMMENT ON TABLE crm_customers IS '客户主表';
COMMENT ON COLUMN crm_customers.id IS '客户主键 UUID';
COMMENT ON COLUMN crm_customers.name IS '客户/公司名称';
COMMENT ON COLUMN crm_customers.level IS '客户等级 A/B/C';
COMMENT ON COLUMN crm_customers.ai_score IS 'AI客情健康度评分 0~100';
COMMENT ON COLUMN crm_customers.owner_id IS '负责销售ID';
CREATE INDEX idx_cust_level ON crm_customers(level) WHERE is_deleted = FALSE;
CREATE INDEX idx_cust_owner ON crm_customers(owner_id) WHERE is_deleted = FALSE;
CREATE INDEX idx_cust_name ON crm_customers(name) WHERE is_deleted = FALSE;
CREATE TRIGGER trg_cust_updated
BEFORE UPDATE ON crm_customers
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
-- ============================================================
-- 2.2 客户跟进日志表 crm_follow_up_logs
-- ============================================================
CREATE TABLE crm_follow_up_logs (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
customer_id UUID NOT NULL REFERENCES crm_customers(id),
salesperson_id UUID NOT NULL REFERENCES sys_users(id),
content TEXT NOT NULL,
emotion_type VARCHAR(20), -- positive / neutral / negative
next_visit_time TIMESTAMP,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
is_deleted BOOLEAN DEFAULT FALSE
);
COMMENT ON TABLE crm_follow_up_logs IS '客户跟进日志表(AI简报数据源)';
COMMENT ON COLUMN crm_follow_up_logs.customer_id IS '关联客户ID';
COMMENT ON COLUMN crm_follow_up_logs.salesperson_id IS '操作销售人员ID';
COMMENT ON COLUMN crm_follow_up_logs.content IS '跟进内容';
COMMENT ON COLUMN crm_follow_up_logs.emotion_type IS '情感标记: positive/neutral/negative';
COMMENT ON COLUMN crm_follow_up_logs.next_visit_time IS '计划下次拜访时间';
CREATE INDEX idx_follow_cust ON crm_follow_up_logs(customer_id) WHERE is_deleted = FALSE;
CREATE INDEX idx_follow_sales ON crm_follow_up_logs(salesperson_id) WHERE is_deleted = FALSE;
CREATE TRIGGER trg_follow_updated
BEFORE UPDATE ON crm_follow_up_logs
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
-- ************************************************************
-- 3. ERP 供应链域
-- ************************************************************
-- ============================================================
-- 3.1 产品分类树 erp_product_categories
-- ============================================================
CREATE TABLE erp_product_categories (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
parent_id UUID REFERENCES erp_product_categories(id),
name VARCHAR(100) NOT NULL,
sort_order INT DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
is_deleted BOOLEAN DEFAULT FALSE
);
COMMENT ON TABLE erp_product_categories IS '产品分类树表(左树结构支撑)';
COMMENT ON COLUMN erp_product_categories.parent_id IS '父级分类IDNULL为顶级分类';
CREATE INDEX idx_pcat_parent ON erp_product_categories(parent_id) WHERE is_deleted = FALSE;
CREATE TRIGGER trg_pcat_updated
BEFORE UPDATE ON erp_product_categories
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
-- ============================================================
-- 3.2 产品 SKU 表 erp_product_skus
-- ============================================================
CREATE TABLE erp_product_skus (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
category_id UUID REFERENCES erp_product_categories(id),
sku_code VARCHAR(50) NOT NULL UNIQUE,
name VARCHAR(200) NOT NULL,
spec VARCHAR(100), -- 规格,如 200L/桶
standard_price NUMERIC(12,2) NOT NULL DEFAULT 0,
stock_qty NUMERIC(12,2) NOT NULL DEFAULT 0,
warning_threshold NUMERIC(12,2) DEFAULT 0, -- 库存预警阈值
unit VARCHAR(20) DEFAULT '',
status SMALLINT DEFAULT 1, -- 1:在售 0:停售
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
is_deleted BOOLEAN DEFAULT FALSE
);
COMMENT ON TABLE erp_product_skus IS '产品SKU主档表';
COMMENT ON COLUMN erp_product_skus.sku_code IS 'SKU编号,全局唯一';
COMMENT ON COLUMN erp_product_skus.spec IS '包装规格,如 200L/桶、18L/桶';
COMMENT ON COLUMN erp_product_skus.standard_price IS '标准售价';
COMMENT ON COLUMN erp_product_skus.stock_qty IS '当前库存数量';
COMMENT ON COLUMN erp_product_skus.warning_threshold IS '库存预警阈值,低于此值触发预警';
CREATE INDEX idx_sku_category ON erp_product_skus(category_id) WHERE is_deleted = FALSE;
CREATE INDEX idx_sku_code ON erp_product_skus(sku_code) WHERE is_deleted = FALSE;
CREATE TRIGGER trg_sku_updated
BEFORE UPDATE ON erp_product_skus
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
-- ============================================================
-- 3.3 出入库流水表 erp_inventory_flows
-- ============================================================
CREATE TABLE erp_inventory_flows (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
sku_id UUID NOT NULL REFERENCES erp_product_skus(id),
change_qty NUMERIC(12,2) NOT NULL, -- 正数=入库,负数=出库
reason VARCHAR(50) NOT NULL, -- purchase/shipment/loss/adjust
remark TEXT,
operator_id UUID REFERENCES sys_users(id),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
is_deleted BOOLEAN DEFAULT FALSE
);
COMMENT ON TABLE erp_inventory_flows IS '出入库流水账表';
COMMENT ON COLUMN erp_inventory_flows.sku_id IS '关联SKU ID';
COMMENT ON COLUMN erp_inventory_flows.change_qty IS '变动数量:正=入库 负=出库';
COMMENT ON COLUMN erp_inventory_flows.reason IS '变动原因: purchase/shipment/loss/adjust';
COMMENT ON COLUMN erp_inventory_flows.operator_id IS '操作人ID';
CREATE INDEX idx_invflow_sku ON erp_inventory_flows(sku_id) WHERE is_deleted = FALSE;
CREATE INDEX idx_invflow_operator ON erp_inventory_flows(operator_id) WHERE is_deleted = FALSE;
CREATE INDEX idx_invflow_time ON erp_inventory_flows(created_at) WHERE is_deleted = FALSE;
CREATE TRIGGER trg_invflow_updated
BEFORE UPDATE ON erp_inventory_flows
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
-- ************************************************************
-- 4. ERP 交易域
-- ************************************************************
-- ============================================================
-- 4.1 订单主表 erp_orders
-- ============================================================
CREATE TABLE erp_orders (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
order_no VARCHAR(30) NOT NULL UNIQUE, -- 业务单号,如 ORD-20260227-001
customer_id UUID NOT NULL REFERENCES crm_customers(id),
salesperson_id UUID REFERENCES sys_users(id),
total_amount NUMERIC(14,2) NOT NULL DEFAULT 0,
shipping_state VARCHAR(20) NOT NULL DEFAULT 'pending', -- pending / partial / shipped
payment_state VARCHAR(20) NOT NULL DEFAULT 'unpaid', -- unpaid / partial / cleared
paid_amount NUMERIC(14,2) DEFAULT 0,
remark TEXT,
order_date DATE DEFAULT CURRENT_DATE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
is_deleted BOOLEAN DEFAULT FALSE
);
COMMENT ON TABLE erp_orders IS '订单主表';
COMMENT ON COLUMN erp_orders.order_no IS '业务订单号';
COMMENT ON COLUMN erp_orders.customer_id IS '下单客户ID';
COMMENT ON COLUMN erp_orders.salesperson_id IS '负责销售ID';
COMMENT ON COLUMN erp_orders.total_amount IS '订单总金额';
COMMENT ON COLUMN erp_orders.shipping_state IS '发货状态: pending/partial/shipped';
COMMENT ON COLUMN erp_orders.payment_state IS '付款状态: unpaid/partial/cleared';
COMMENT ON COLUMN erp_orders.paid_amount IS '已付金额';
CREATE INDEX idx_order_no ON erp_orders(order_no) WHERE is_deleted = FALSE;
CREATE INDEX idx_order_cust ON erp_orders(customer_id) WHERE is_deleted = FALSE;
CREATE INDEX idx_order_sales ON erp_orders(salesperson_id) WHERE is_deleted = FALSE;
CREATE INDEX idx_order_date ON erp_orders(order_date) WHERE is_deleted = FALSE;
CREATE TRIGGER trg_order_updated
BEFORE UPDATE ON erp_orders
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
-- ============================================================
-- 4.2 订单明细表 erp_order_items
-- ============================================================
CREATE TABLE erp_order_items (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
order_id UUID NOT NULL REFERENCES erp_orders(id),
sku_id UUID NOT NULL REFERENCES erp_product_skus(id),
qty NUMERIC(12,2) NOT NULL,
unit_price NUMERIC(12,2) NOT NULL, -- 本次成交单价(可能是专属价)
sub_total NUMERIC(14,2) NOT NULL,
shipped_qty NUMERIC(12,2) DEFAULT 0, -- 已发货累计量(用于分批发货扣减)
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
is_deleted BOOLEAN DEFAULT FALSE
);
COMMENT ON TABLE erp_order_items IS '订单明细行表';
COMMENT ON COLUMN erp_order_items.order_id IS '归属订单ID';
COMMENT ON COLUMN erp_order_items.sku_id IS '关联SKU ID';
COMMENT ON COLUMN erp_order_items.qty IS '下单数量';
COMMENT ON COLUMN erp_order_items.unit_price IS '成交单价(可能为客户专属价)';
COMMENT ON COLUMN erp_order_items.sub_total IS '小计金额 = qty * unit_price';
COMMENT ON COLUMN erp_order_items.shipped_qty IS '已发货累计数量,用于分批发货进度追踪';
CREATE INDEX idx_oitem_order ON erp_order_items(order_id) WHERE is_deleted = FALSE;
CREATE INDEX idx_oitem_sku ON erp_order_items(sku_id) WHERE is_deleted = FALSE;
CREATE TRIGGER trg_oitem_updated
BEFORE UPDATE ON erp_order_items
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
-- ************************************************************
-- 5. ERP 物流域
-- ************************************************************
-- ============================================================
-- 5.1 发货主单表 erp_shipping_records
-- ============================================================
CREATE TABLE erp_shipping_records (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
shipping_no VARCHAR(30) NOT NULL UNIQUE, -- 发货单号 SHP-xxx
order_id UUID NOT NULL REFERENCES erp_orders(id),
carrier VARCHAR(100), -- 承运方,如德邦
tracking_no VARCHAR(100), -- 物流追踪号
status VARCHAR(20) NOT NULL DEFAULT 'transit', -- transit / delivered
ship_date DATE DEFAULT CURRENT_DATE,
remark TEXT,
operator_id UUID REFERENCES sys_users(id),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
is_deleted BOOLEAN DEFAULT FALSE
);
COMMENT ON TABLE erp_shipping_records IS '发货主单表';
COMMENT ON COLUMN erp_shipping_records.shipping_no IS '发货单号';
COMMENT ON COLUMN erp_shipping_records.order_id IS '关联订单ID';
COMMENT ON COLUMN erp_shipping_records.carrier IS '承运方';
COMMENT ON COLUMN erp_shipping_records.tracking_no IS '物流追踪号';
COMMENT ON COLUMN erp_shipping_records.status IS '物流状态: transit=运输中 / delivered=已签收';
CREATE INDEX idx_ship_order ON erp_shipping_records(order_id) WHERE is_deleted = FALSE;
CREATE INDEX idx_ship_no ON erp_shipping_records(shipping_no) WHERE is_deleted = FALSE;
CREATE TRIGGER trg_ship_updated
BEFORE UPDATE ON erp_shipping_records
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
-- ============================================================
-- 5.2 发货明细表 erp_shipping_items
-- ============================================================
CREATE TABLE erp_shipping_items (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
shipping_id UUID NOT NULL REFERENCES erp_shipping_records(id),
order_item_id UUID NOT NULL REFERENCES erp_order_items(id),
sku_id UUID NOT NULL REFERENCES erp_product_skus(id),
shipped_qty NUMERIC(12,2) NOT NULL, -- 本次发货数量
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
is_deleted BOOLEAN DEFAULT FALSE
);
COMMENT ON TABLE erp_shipping_items IS '发货明细行表(承接分批发货扣减)';
COMMENT ON COLUMN erp_shipping_items.shipping_id IS '归属发货单ID';
COMMENT ON COLUMN erp_shipping_items.order_item_id IS '关联订单明细行ID(用于回写已发货量)';
COMMENT ON COLUMN erp_shipping_items.sku_id IS '关联SKU ID';
COMMENT ON COLUMN erp_shipping_items.shipped_qty IS '本次实际发货数量';
CREATE INDEX idx_sitem_shipping ON erp_shipping_items(shipping_id) WHERE is_deleted = FALSE;
CREATE INDEX idx_sitem_oitem ON erp_shipping_items(order_item_id) WHERE is_deleted = FALSE;
CREATE TRIGGER trg_sitem_updated
BEFORE UPDATE ON erp_shipping_items
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
-- ************************************************************
-- 6. 财务票据域
-- ************************************************************
-- ============================================================
-- 6.1 统一发票池表 fin_invoice_pool
-- ============================================================
CREATE TABLE fin_invoice_pool (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
uploader_id UUID REFERENCES sys_users(id),
file_url VARCHAR(500),
merchant_name VARCHAR(200),
amount NUMERIC(14,2) NOT NULL DEFAULT 0,
invoice_date DATE,
type VARCHAR(30) NOT NULL DEFAULT 'expense', -- customer=客户发票 / expense=报销发票
ai_extracted_data JSONB DEFAULT '{}'::JSONB, -- OCR + AI 解析的原始结构化数据
is_used BOOLEAN DEFAULT FALSE, -- 是否已被报销单引用
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
is_deleted BOOLEAN DEFAULT FALSE -- 软删除=作废
);
COMMENT ON TABLE fin_invoice_pool IS '统一发票池表(上传即OCR提取)';
COMMENT ON COLUMN fin_invoice_pool.uploader_id IS '上传人ID';
COMMENT ON COLUMN fin_invoice_pool.file_url IS '原始票据文件URL';
COMMENT ON COLUMN fin_invoice_pool.merchant_name IS '商户/开票方名称';
COMMENT ON COLUMN fin_invoice_pool.amount IS '票面金额';
COMMENT ON COLUMN fin_invoice_pool.type IS '票据类型: customer=客户发票 / expense=报销发票';
COMMENT ON COLUMN fin_invoice_pool.ai_extracted_data IS 'AI/OCR提取的结构化数据 (JSONB)';
COMMENT ON COLUMN fin_invoice_pool.is_used IS '是否已被报销单引用';
COMMENT ON COLUMN fin_invoice_pool.is_deleted IS '软删除标识(作废,防物理篡改)';
CREATE INDEX idx_inv_uploader ON fin_invoice_pool(uploader_id) WHERE is_deleted = FALSE;
CREATE INDEX idx_inv_type ON fin_invoice_pool(type) WHERE is_deleted = FALSE;
CREATE INDEX idx_inv_date ON fin_invoice_pool(invoice_date) WHERE is_deleted = FALSE;
CREATE TRIGGER trg_inv_updated
BEFORE UPDATE ON fin_invoice_pool
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
-- ============================================================
-- 6.2 报销单主表 fin_expense_records
-- ============================================================
CREATE TABLE fin_expense_records (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
system_no VARCHAR(30) NOT NULL UNIQUE, -- 系统单号 EXP-xxx
applicant_id UUID NOT NULL REFERENCES sys_users(id),
total_amount NUMERIC(14,2) NOT NULL DEFAULT 0,
status VARCHAR(20) NOT NULL DEFAULT 'draft', -- draft/pending/approved/rejected
remark TEXT,
approved_by UUID REFERENCES sys_users(id),
approved_at TIMESTAMP,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
is_deleted BOOLEAN DEFAULT FALSE
);
COMMENT ON TABLE fin_expense_records IS '报销单主表';
COMMENT ON COLUMN fin_expense_records.system_no IS '系统报销单号';
COMMENT ON COLUMN fin_expense_records.applicant_id IS '申请人ID';
COMMENT ON COLUMN fin_expense_records.total_amount IS '报销总金额';
COMMENT ON COLUMN fin_expense_records.status IS '报销状态: draft/pending/approved/rejected';
COMMENT ON COLUMN fin_expense_records.approved_by IS '审批人ID';
CREATE INDEX idx_exp_applicant ON fin_expense_records(applicant_id) WHERE is_deleted = FALSE;
CREATE INDEX idx_exp_status ON fin_expense_records(status) WHERE is_deleted = FALSE;
CREATE INDEX idx_exp_no ON fin_expense_records(system_no) WHERE is_deleted = FALSE;
CREATE TRIGGER trg_exp_updated
BEFORE UPDATE ON fin_expense_records
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
-- ============================================================
-- 6.3 报销明细行表 fin_expense_details
-- ============================================================
CREATE TABLE fin_expense_details (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
expense_id UUID NOT NULL REFERENCES fin_expense_records(id),
invoice_id UUID REFERENCES fin_invoice_pool(id),
expense_desc VARCHAR(500), -- 费用说明
original_type VARCHAR(50), -- 原始费用类型: fuel/entertainment/travel/office
offset_type VARCHAR(50), -- 冲顶类型(二级类型转换后)
amount NUMERIC(14,2) NOT NULL DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
is_deleted BOOLEAN DEFAULT FALSE
);
COMMENT ON TABLE fin_expense_details IS '报销明细行表';
COMMENT ON COLUMN fin_expense_details.expense_id IS '归属报销单ID';
COMMENT ON COLUMN fin_expense_details.invoice_id IS '关联发票ID';
COMMENT ON COLUMN fin_expense_details.expense_desc IS '费用说明描述';
COMMENT ON COLUMN fin_expense_details.original_type IS '原始费用类型: fuel/entertainment/travel/office';
COMMENT ON COLUMN fin_expense_details.offset_type IS '冲顶类型(二级票据类型转换后的类型)';
COMMENT ON COLUMN fin_expense_details.amount IS '本行金额';
CREATE INDEX idx_expd_expense ON fin_expense_details(expense_id) WHERE is_deleted = FALSE;
CREATE INDEX idx_expd_invoice ON fin_expense_details(invoice_id) WHERE is_deleted = FALSE;
CREATE TRIGGER trg_expd_updated
BEFORE UPDATE ON fin_expense_details
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
-- ============================================================
-- 完成:输出建表结果摘要
-- ============================================================
-- 共创建 13 张业务表:
-- RBAC 权限域: sys_departments, sys_roles, sys_users
-- CRM 客户域: crm_customers, crm_follow_up_logs
-- ERP 供应链域: erp_product_categories, erp_product_skus, erp_inventory_flows
-- ERP 交易域: erp_orders, erp_order_items
-- ERP 物流域: erp_shipping_records, erp_shipping_items
-- 财务票据域: fin_invoice_pool, fin_expense_records, fin_expense_details
--
-- 全局规范落地:
-- ✓ 主键统一 UUID (uuid_generate_v4)
-- ✓ 每表必带 created_at / updated_at / is_deleted 审计三件套
-- ✓ updated_at 自动触发器 (update_updated_at_column)
-- ✓ 灵活字段采用 JSONB (menu_keys, ai_extracted_data)
-- ✓ 外键关联完整,部分索引 (WHERE is_deleted=FALSE) 优化查询