106 lines
3.8 KiB
MySQL
Raw Normal View History

-- Muyu CRM Graph Schema (PostgreSQL)
-- Long-term architecture baseline:
-- PostgreSQL (source of truth) -> CDC/Kafka -> Neo4j (graph analytics)
CREATE SCHEMA IF NOT EXISTS crm;
SET search_path TO crm, public;
-- ==================== Tenant and Identity ====================
CREATE TABLE IF NOT EXISTS tenant (
id BIGSERIAL PRIMARY KEY,
tenant_code VARCHAR(64) NOT NULL UNIQUE,
tenant_name VARCHAR(128) NOT NULL,
status SMALLINT NOT NULL DEFAULT 1,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE TABLE IF NOT EXISTS tenant_user (
id BIGSERIAL PRIMARY KEY,
tenant_id BIGINT NOT NULL REFERENCES tenant(id),
user_id VARCHAR(64) NOT NULL,
role_key VARCHAR(64) NOT NULL DEFAULT 'user',
is_primary BOOLEAN NOT NULL DEFAULT TRUE,
status SMALLINT NOT NULL DEFAULT 1,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
UNIQUE (tenant_id, user_id)
);
CREATE INDEX IF NOT EXISTS idx_tenant_user_user_id ON tenant_user(user_id);
-- ==================== Graph Nodes and Edges ====================
CREATE TABLE IF NOT EXISTS relationship_type (
id BIGSERIAL PRIMARY KEY,
type_code VARCHAR(32) NOT NULL UNIQUE,
type_name VARCHAR(64) NOT NULL,
description TEXT NOT NULL DEFAULT '',
status SMALLINT NOT NULL DEFAULT 1,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE TABLE IF NOT EXISTS tenant_relationship (
id BIGSERIAL PRIMARY KEY,
owner_tenant_id BIGINT NOT NULL REFERENCES tenant(id),
from_tenant_id BIGINT NOT NULL REFERENCES tenant(id),
to_tenant_id BIGINT NOT NULL REFERENCES tenant(id),
relationship_type_id BIGINT NOT NULL REFERENCES relationship_type(id),
status SMALLINT NOT NULL DEFAULT 1,
valid_from TIMESTAMPTZ NOT NULL DEFAULT NOW(),
valid_to TIMESTAMPTZ NULL,
source VARCHAR(32) NOT NULL DEFAULT 'manual',
created_by VARCHAR(64) NOT NULL DEFAULT 'system',
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CHECK (from_tenant_id <> to_tenant_id),
CHECK (valid_to IS NULL OR valid_to > valid_from)
);
CREATE UNIQUE INDEX IF NOT EXISTS uk_tenant_relationship_version
ON tenant_relationship(owner_tenant_id, from_tenant_id, to_tenant_id, relationship_type_id, valid_from);
CREATE INDEX IF NOT EXISTS idx_tenant_relationship_from
ON tenant_relationship(owner_tenant_id, from_tenant_id, status);
CREATE INDEX IF NOT EXISTS idx_tenant_relationship_to
ON tenant_relationship(owner_tenant_id, to_tenant_id, status);
CREATE TABLE IF NOT EXISTS tenant_relationship_history (
id BIGSERIAL PRIMARY KEY,
relationship_id BIGINT NOT NULL REFERENCES tenant_relationship(id),
change_type VARCHAR(32) NOT NULL,
before_json JSONB,
after_json JSONB,
changed_by VARCHAR(64) NOT NULL DEFAULT 'system',
changed_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_tenant_relationship_history_rel
ON tenant_relationship_history(relationship_id, changed_at DESC);
-- ==================== Seed Data ====================
INSERT INTO relationship_type(type_code, type_name, description)
VALUES
('SUPPLY', '供货关系', 'from_tenant 作为 to_tenant 的上游'),
('PURCHASE', '采购关系', 'from_tenant 向 to_tenant 发起采购'),
('OEM', '代工关系', '委托代工上下游关系')
ON CONFLICT (type_code) DO NOTHING;
-- ==================== One-hop Query Examples ====================
-- Upstream (who supplies current tenant):
-- SELECT r.from_tenant_id
-- FROM crm.tenant_relationship r
-- WHERE r.owner_tenant_id = $1
-- AND r.to_tenant_id = $2
-- AND r.status = 1;
--
-- Downstream (who current tenant supplies):
-- SELECT r.to_tenant_id
-- FROM crm.tenant_relationship r
-- WHERE r.owner_tenant_id = $1
-- AND r.from_tenant_id = $2
-- AND r.status = 1;