423baff73b
- Docker bridge 网络隔离(8000 端口封死) - Gunicorn 4 Worker 多进程 - Alembic 数据库迁移基线 - 日志轮转 20m×3 - JWT 密钥 + DB 密码 + CORS 收紧 - 3-2-1 备份链路(NAS + R740-B 冷备) - 连接池 pool_pre_ping + pool_recycle=3600
540 lines
28 KiB
PL/PgSQL
540 lines
28 KiB
PL/PgSQL
-- ============================================================
|
||
-- 润滑油行业 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 '父级部门ID,NULL表示顶级';
|
||
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 '父级分类ID,NULL为顶级分类';
|
||
|
||
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) 优化查询
|