869 lines
34 KiB
PL/PgSQL
869 lines
34 KiB
PL/PgSQL
-- =====================================================================================
|
|
-- 商城系统数据库迁移脚本 (PostgreSQL + Supabase)
|
|
-- 用途: 在现有数据库基础上添加商城相关表和功能
|
|
-- 说明: 复用 ak_users 表,新增 ml_ 前缀的商城表
|
|
-- 执行方式: 直接在数据库中执行此脚本
|
|
-- =====================================================================================
|
|
|
|
-- 检查必要的扩展
|
|
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
|
CREATE EXTENSION IF NOT EXISTS "pg_stat_statements";
|
|
CREATE EXTENSION IF NOT EXISTS "btree_gin";
|
|
|
|
-- =====================================================================================
|
|
-- 1. 创建商城用户扩展表
|
|
-- =====================================================================================
|
|
|
|
-- 商城用户档案表
|
|
CREATE TABLE IF NOT EXISTS public.ml_user_profiles (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
user_id UUID UNIQUE NOT NULL REFERENCES public.ak_users(id) ON DELETE CASCADE,
|
|
user_type INTEGER DEFAULT 1 NOT NULL, -- 1:消费者 2:商家 3:配送员 4:客服 5:管理员
|
|
status INTEGER DEFAULT 1 NOT NULL, -- 1:正常 2:冻结 3:注销 4:待审核
|
|
real_name VARCHAR(100), -- 真实姓名
|
|
id_card VARCHAR(32), -- 身份证号
|
|
business_license VARCHAR(100), -- 营业执照号
|
|
credit_score INTEGER DEFAULT 100, -- 信用分数 0-1000
|
|
verification_status INTEGER DEFAULT 0, -- 认证状态 0:未认证 1:已认证 2:认证失败
|
|
verification_data JSONB DEFAULT '{}', -- 认证相关数据
|
|
preferences JSONB DEFAULT '{}', -- 用户偏好设置
|
|
emergency_contact VARCHAR(200), -- 紧急联系人
|
|
service_areas JSONB, -- 服务区域(配送员)
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
|
|
CONSTRAINT chk_ml_user_type CHECK (user_type IN (1,2,3,4,5)),
|
|
CONSTRAINT chk_ml_user_status CHECK (status IN (1,2,3,4)),
|
|
CONSTRAINT chk_ml_verification_status CHECK (verification_status IN (0,1,2)),
|
|
CONSTRAINT chk_ml_credit_score CHECK (credit_score >= 0 AND credit_score <= 1000)
|
|
);
|
|
|
|
COMMENT ON TABLE public.ml_user_profiles IS '商城用户扩展信息表';
|
|
|
|
-- 用户地址表
|
|
CREATE TABLE IF NOT EXISTS public.ml_user_addresses (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
user_id UUID NOT NULL REFERENCES public.ak_users(id) ON DELETE CASCADE,
|
|
receiver_name VARCHAR(100) NOT NULL,
|
|
receiver_phone VARCHAR(32) NOT NULL,
|
|
province VARCHAR(100) NOT NULL,
|
|
city VARCHAR(100) NOT NULL,
|
|
district VARCHAR(100) NOT NULL,
|
|
street VARCHAR(200),
|
|
address_detail TEXT NOT NULL,
|
|
postal_code VARCHAR(16),
|
|
is_default BOOLEAN DEFAULT FALSE,
|
|
label VARCHAR(50), -- home/office/school/other
|
|
latitude DECIMAL(10,7),
|
|
longitude DECIMAL(10,7),
|
|
delivery_instructions TEXT,
|
|
business_hours VARCHAR(100),
|
|
status INTEGER DEFAULT 1, -- 1:正常 2:禁用
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
|
|
CONSTRAINT chk_ml_address_status CHECK (status IN (1,2))
|
|
);
|
|
|
|
COMMENT ON TABLE public.ml_user_addresses IS '用户地址表';
|
|
|
|
-- =====================================================================================
|
|
-- 2. 创建商品相关表
|
|
-- =====================================================================================
|
|
|
|
-- 商品分类表
|
|
CREATE TABLE IF NOT EXISTS public.ml_categories (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
cid SERIAL UNIQUE NOT NULL, -- SEO友好的自增ID
|
|
parent_id UUID REFERENCES public.ml_categories(id),
|
|
name VARCHAR(200) NOT NULL,
|
|
slug VARCHAR(200) UNIQUE,
|
|
description TEXT,
|
|
icon_url TEXT,
|
|
banner_url TEXT,
|
|
sort_order INTEGER DEFAULT 0,
|
|
level INTEGER DEFAULT 1,
|
|
path TEXT[], -- 分类路径
|
|
is_active BOOLEAN DEFAULT TRUE,
|
|
seo_title VARCHAR(200),
|
|
seo_description VARCHAR(500),
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
|
);
|
|
|
|
COMMENT ON TABLE public.ml_categories IS '商品分类表';
|
|
|
|
-- 品牌表
|
|
CREATE TABLE IF NOT EXISTS public.ml_brands (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
cid SERIAL UNIQUE NOT NULL, -- SEO友好的自增ID
|
|
name VARCHAR(200) NOT NULL,
|
|
logo_url TEXT,
|
|
description TEXT,
|
|
website VARCHAR(500),
|
|
is_active BOOLEAN DEFAULT TRUE,
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
|
);
|
|
|
|
COMMENT ON TABLE public.ml_brands IS '品牌表';
|
|
|
|
-- 商品表
|
|
CREATE TABLE IF NOT EXISTS public.ml_products (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
cid SERIAL UNIQUE NOT NULL, -- SEO友好的自增ID
|
|
merchant_id UUID NOT NULL REFERENCES public.ak_users(id),
|
|
category_id UUID NOT NULL REFERENCES public.ml_categories(id),
|
|
brand_id UUID REFERENCES public.ml_brands(id),
|
|
product_code VARCHAR(100) UNIQUE NOT NULL,
|
|
name VARCHAR(500) NOT NULL,
|
|
subtitle VARCHAR(1000),
|
|
description TEXT,
|
|
main_image_url TEXT,
|
|
image_urls JSONB DEFAULT '[]',
|
|
video_urls JSONB DEFAULT '[]',
|
|
|
|
-- 价格信息
|
|
base_price DECIMAL(12,2) NOT NULL CHECK (base_price >= 0),
|
|
market_price DECIMAL(12,2),
|
|
cost_price DECIMAL(12,2),
|
|
|
|
-- 库存信息
|
|
total_stock INTEGER DEFAULT 0 CHECK (total_stock >= 0),
|
|
available_stock INTEGER DEFAULT 0 CHECK (available_stock >= 0),
|
|
min_order_qty INTEGER DEFAULT 1 CHECK (min_order_qty > 0),
|
|
max_order_qty INTEGER,
|
|
|
|
-- 基础属性
|
|
weight DECIMAL(10,3),
|
|
dimensions JSONB, -- {length, width, height}
|
|
|
|
-- 状态
|
|
status INTEGER DEFAULT 1, -- 1:上架 2:下架 3:草稿 4:删除
|
|
is_featured BOOLEAN DEFAULT FALSE,
|
|
is_new BOOLEAN DEFAULT FALSE,
|
|
is_hot BOOLEAN DEFAULT FALSE,
|
|
|
|
-- 统计
|
|
view_count INTEGER DEFAULT 0,
|
|
sale_count INTEGER DEFAULT 0,
|
|
favorite_count INTEGER DEFAULT 0,
|
|
rating_avg DECIMAL(3,2) DEFAULT 0.00 CHECK (rating_avg >= 0 AND rating_avg <= 5),
|
|
rating_count INTEGER DEFAULT 0,
|
|
|
|
-- SEO
|
|
seo_title VARCHAR(200),
|
|
seo_description VARCHAR(500),
|
|
seo_keywords TEXT[],
|
|
slug VARCHAR(200) UNIQUE,
|
|
|
|
-- 其他
|
|
tags TEXT[],
|
|
attributes JSONB DEFAULT '{}',
|
|
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
published_at TIMESTAMP WITH TIME ZONE,
|
|
|
|
CONSTRAINT chk_ml_product_status CHECK (status IN (1,2,3,4))
|
|
);
|
|
|
|
COMMENT ON TABLE public.ml_products IS '商品表';
|
|
|
|
-- 商品SKU表
|
|
CREATE TABLE IF NOT EXISTS public.ml_product_skus (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
product_id UUID NOT NULL REFERENCES public.ml_products(id) ON DELETE CASCADE,
|
|
sku_code VARCHAR(100) UNIQUE NOT NULL,
|
|
specifications JSONB DEFAULT '{}', -- 规格组合
|
|
price DECIMAL(12,2) NOT NULL CHECK (price >= 0),
|
|
market_price DECIMAL(12,2),
|
|
cost_price DECIMAL(12,2),
|
|
stock INTEGER DEFAULT 0 CHECK (stock >= 0),
|
|
warning_stock INTEGER DEFAULT 10, -- 库存预警
|
|
image_url TEXT,
|
|
weight DECIMAL(10,3),
|
|
status INTEGER DEFAULT 1, -- 1:正常 2:禁用
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
|
|
CONSTRAINT chk_ml_sku_status CHECK (status IN (1,2))
|
|
);
|
|
|
|
COMMENT ON TABLE public.ml_product_skus IS '商品SKU表';
|
|
|
|
-- 商品规格表
|
|
CREATE TABLE IF NOT EXISTS public.ml_product_specs (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
product_id UUID NOT NULL REFERENCES public.ml_products(id) ON DELETE CASCADE,
|
|
spec_name VARCHAR(100) NOT NULL, -- 规格名称:颜色、尺寸等
|
|
spec_values JSONB NOT NULL DEFAULT '[]', -- 规格值数组
|
|
sort_order INTEGER DEFAULT 0,
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
|
);
|
|
|
|
COMMENT ON TABLE public.ml_product_specs IS '商品规格表';
|
|
|
|
-- =====================================================================================
|
|
-- 3. 创建店铺相关表
|
|
-- =====================================================================================
|
|
|
|
-- 店铺信息表
|
|
CREATE TABLE IF NOT EXISTS public.ml_shops (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
cid SERIAL UNIQUE NOT NULL, -- SEO友好的自增ID
|
|
merchant_id UUID UNIQUE NOT NULL REFERENCES public.ak_users(id) ON DELETE CASCADE,
|
|
shop_name VARCHAR(200) NOT NULL,
|
|
shop_logo TEXT,
|
|
shop_banner TEXT,
|
|
description TEXT,
|
|
business_license VARCHAR(100),
|
|
contact_name VARCHAR(100),
|
|
contact_phone VARCHAR(32),
|
|
contact_email VARCHAR(200),
|
|
address JSONB, -- 店铺地址信息
|
|
business_hours JSONB, -- 营业时间
|
|
|
|
-- 状态
|
|
status INTEGER DEFAULT 1, -- 1:正常 2:暂停 3:关闭
|
|
|
|
-- 统计
|
|
product_count INTEGER DEFAULT 0,
|
|
order_count INTEGER DEFAULT 0,
|
|
rating_avg DECIMAL(3,2) DEFAULT 0.00,
|
|
rating_count INTEGER DEFAULT 0,
|
|
|
|
-- 认证信息
|
|
verified_at TIMESTAMP WITH TIME ZONE,
|
|
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
|
|
CONSTRAINT chk_ml_shop_status CHECK (status IN (1,2,3))
|
|
);
|
|
|
|
COMMENT ON TABLE public.ml_shops IS '店铺信息表';
|
|
|
|
-- =====================================================================================
|
|
-- 4. 创建订单相关表
|
|
-- =====================================================================================
|
|
|
|
-- 订单表
|
|
CREATE TABLE IF NOT EXISTS public.ml_orders (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
cid SERIAL UNIQUE NOT NULL, -- SEO友好的自增ID
|
|
order_no VARCHAR(50) UNIQUE NOT NULL,
|
|
user_id UUID NOT NULL REFERENCES public.ak_users(id),
|
|
merchant_id UUID NOT NULL REFERENCES public.ak_users(id),
|
|
|
|
-- 金额信息
|
|
product_amount DECIMAL(12,2) NOT NULL DEFAULT 0, -- 商品金额
|
|
discount_amount DECIMAL(12,2) DEFAULT 0, -- 优惠金额
|
|
shipping_fee DECIMAL(12,2) DEFAULT 0, -- 运费
|
|
total_amount DECIMAL(12,2) NOT NULL, -- 总金额
|
|
paid_amount DECIMAL(12,2) DEFAULT 0, -- 已付金额
|
|
|
|
-- 地址信息
|
|
shipping_address JSONB NOT NULL, -- 收货地址
|
|
|
|
-- 状态信息
|
|
order_status INTEGER DEFAULT 1, -- 1:待付款 2:待发货 3:待收货 4:已完成 5:已取消 6:退款中 7:已退款
|
|
payment_status INTEGER DEFAULT 1, -- 1:未付款 2:已付款 3:部分退款 4:全额退款
|
|
shipping_status INTEGER DEFAULT 1, -- 1:未发货 2:已发货 3:运输中 4:已送达
|
|
|
|
-- 时间信息
|
|
paid_at TIMESTAMP WITH TIME ZONE,
|
|
shipped_at TIMESTAMP WITH TIME ZONE,
|
|
delivered_at TIMESTAMP WITH TIME ZONE,
|
|
completed_at TIMESTAMP WITH TIME ZONE,
|
|
|
|
-- 其他信息
|
|
remark TEXT, -- 买家备注
|
|
merchant_memo TEXT, -- 商家备注
|
|
cancel_reason TEXT, -- 取消原因
|
|
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
|
|
CONSTRAINT chk_ml_order_status CHECK (order_status IN (1,2,3,4,5,6,7)),
|
|
CONSTRAINT chk_ml_payment_status CHECK (payment_status IN (1,2,3,4)),
|
|
CONSTRAINT chk_ml_shipping_status CHECK (shipping_status IN (1,2,3,4))
|
|
);
|
|
|
|
COMMENT ON TABLE public.ml_orders IS '订单表';
|
|
|
|
-- 订单商品表
|
|
CREATE TABLE IF NOT EXISTS public.ml_order_items (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
order_id UUID NOT NULL REFERENCES public.ml_orders(id) ON DELETE CASCADE,
|
|
product_id UUID NOT NULL REFERENCES public.ml_products(id),
|
|
sku_id UUID REFERENCES public.ml_product_skus(id),
|
|
product_name VARCHAR(500) NOT NULL,
|
|
sku_name VARCHAR(500),
|
|
specifications JSONB DEFAULT '{}',
|
|
image_url TEXT,
|
|
price DECIMAL(12,2) NOT NULL,
|
|
quantity INTEGER NOT NULL CHECK (quantity > 0),
|
|
total_amount DECIMAL(12,2) NOT NULL,
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
|
);
|
|
|
|
COMMENT ON TABLE public.ml_order_items IS '订单商品表';
|
|
|
|
-- =====================================================================================
|
|
-- 5. 创建购物车和营销相关表
|
|
-- =====================================================================================
|
|
|
|
-- 购物车表
|
|
CREATE TABLE IF NOT EXISTS public.ml_shopping_cart (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
user_id UUID NOT NULL REFERENCES public.ak_users(id) ON DELETE CASCADE,
|
|
product_id UUID NOT NULL REFERENCES public.ml_products(id) ON DELETE CASCADE,
|
|
sku_id UUID REFERENCES public.ml_product_skus(id) ON DELETE CASCADE,
|
|
quantity INTEGER NOT NULL CHECK (quantity > 0),
|
|
selected BOOLEAN DEFAULT TRUE,
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
|
|
UNIQUE(user_id, product_id, sku_id)
|
|
);
|
|
|
|
COMMENT ON TABLE public.ml_shopping_cart IS '购物车表';
|
|
|
|
-- 优惠券模板表
|
|
CREATE TABLE IF NOT EXISTS public.ml_coupon_templates (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
cid SERIAL UNIQUE NOT NULL, -- SEO友好的自增ID
|
|
merchant_id UUID REFERENCES public.ak_users(id), -- NULL表示平台券
|
|
name VARCHAR(200) NOT NULL,
|
|
description TEXT,
|
|
coupon_type INTEGER NOT NULL, -- 1:满减券 2:折扣券 3:免运费券
|
|
discount_type INTEGER NOT NULL, -- 1:固定金额 2:百分比
|
|
discount_value DECIMAL(12,2) NOT NULL, -- 优惠值
|
|
min_order_amount DECIMAL(12,2) DEFAULT 0, -- 最低订单金额
|
|
max_discount_amount DECIMAL(12,2), -- 最大优惠金额
|
|
total_quantity INTEGER, -- 总发放数量
|
|
per_user_limit INTEGER DEFAULT 1, -- 每用户限领数量
|
|
usage_limit INTEGER DEFAULT 1, -- 每张券使用次数限制
|
|
|
|
-- 适用范围
|
|
applicable_products JSONB DEFAULT '[]', -- 适用商品ID数组
|
|
applicable_categories JSONB DEFAULT '[]', -- 适用分类ID数组
|
|
|
|
-- 时间限制
|
|
start_time TIMESTAMP WITH TIME ZONE NOT NULL,
|
|
end_time TIMESTAMP WITH TIME ZONE NOT NULL,
|
|
|
|
status INTEGER DEFAULT 1, -- 1:正常 2:暂停 3:已结束
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
|
|
CONSTRAINT chk_ml_coupon_type CHECK (coupon_type IN (1,2,3)),
|
|
CONSTRAINT chk_ml_discount_type CHECK (discount_type IN (1,2)),
|
|
CONSTRAINT chk_ml_coupon_status CHECK (status IN (1,2,3))
|
|
);
|
|
|
|
COMMENT ON TABLE public.ml_coupon_templates IS '优惠券模板表';
|
|
|
|
-- 用户优惠券表
|
|
CREATE TABLE IF NOT EXISTS public.ml_user_coupons (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
user_id UUID NOT NULL REFERENCES public.ak_users(id) ON DELETE CASCADE,
|
|
template_id UUID NOT NULL REFERENCES public.ml_coupon_templates(id),
|
|
coupon_code VARCHAR(50) UNIQUE NOT NULL,
|
|
status INTEGER DEFAULT 1, -- 1:未使用 2:已使用 3:已过期
|
|
used_at TIMESTAMP WITH TIME ZONE,
|
|
order_id UUID REFERENCES public.ml_orders(id),
|
|
received_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
expire_at TIMESTAMP WITH TIME ZONE NOT NULL,
|
|
|
|
CONSTRAINT chk_ml_user_coupon_status CHECK (status IN (1,2,3))
|
|
);
|
|
|
|
COMMENT ON TABLE public.ml_user_coupons IS '用户优惠券表';
|
|
|
|
-- =====================================================================================
|
|
-- 6. 创建配送和评价相关表
|
|
-- =====================================================================================
|
|
|
|
-- 配送员信息表
|
|
CREATE TABLE IF NOT EXISTS public.ml_delivery_drivers (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
user_id UUID UNIQUE NOT NULL REFERENCES public.ak_users(id) ON DELETE CASCADE,
|
|
real_name VARCHAR(100) NOT NULL,
|
|
id_card VARCHAR(32) NOT NULL,
|
|
driver_license VARCHAR(50),
|
|
vehicle_type INTEGER, -- 1:电动车 2:摩托车 3:汽车
|
|
vehicle_number VARCHAR(20),
|
|
service_areas JSONB DEFAULT '[]', -- 服务区域
|
|
work_status INTEGER DEFAULT 1, -- 1:在线 2:忙碌 3:离线
|
|
current_lat DECIMAL(10,7),
|
|
current_lng DECIMAL(10,7),
|
|
rating_avg DECIMAL(3,2) DEFAULT 0.00,
|
|
rating_count INTEGER DEFAULT 0,
|
|
order_count INTEGER DEFAULT 0,
|
|
status INTEGER DEFAULT 1, -- 1:正常 2:暂停 3:离职
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
|
|
CONSTRAINT chk_ml_driver_vehicle_type CHECK (vehicle_type IN (1,2,3)),
|
|
CONSTRAINT chk_ml_driver_work_status CHECK (work_status IN (1,2,3)),
|
|
CONSTRAINT chk_ml_driver_status CHECK (status IN (1,2,3))
|
|
);
|
|
|
|
COMMENT ON TABLE public.ml_delivery_drivers IS '配送员信息表';
|
|
|
|
-- 配送任务表
|
|
CREATE TABLE IF NOT EXISTS public.ml_delivery_tasks (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
order_id UUID UNIQUE NOT NULL REFERENCES public.ml_orders(id),
|
|
driver_id UUID REFERENCES public.ml_delivery_drivers(id),
|
|
pickup_address JSONB NOT NULL, -- 取货地址
|
|
delivery_address JSONB NOT NULL, -- 配送地址
|
|
distance DECIMAL(8,2), -- 配送距离(km)
|
|
estimated_time INTEGER, -- 预计配送时间(分钟)
|
|
delivery_fee DECIMAL(10,2) NOT NULL DEFAULT 0,
|
|
|
|
status INTEGER DEFAULT 1, -- 1:待接单 2:已接单 3:取货中 4:配送中 5:已送达 6:配送失败
|
|
|
|
-- 时间记录
|
|
assigned_at TIMESTAMP WITH TIME ZONE,
|
|
picked_at TIMESTAMP WITH TIME ZONE,
|
|
delivered_at TIMESTAMP WITH TIME ZONE,
|
|
|
|
-- 其他信息
|
|
delivery_code VARCHAR(10), -- 取货码
|
|
remark TEXT,
|
|
failure_reason TEXT,
|
|
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
|
|
CONSTRAINT chk_ml_delivery_status CHECK (status IN (1,2,3,4,5,6))
|
|
);
|
|
|
|
COMMENT ON TABLE public.ml_delivery_tasks IS '配送任务表';
|
|
|
|
-- 商品评价表
|
|
CREATE TABLE IF NOT EXISTS public.ml_product_reviews (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
order_id UUID NOT NULL REFERENCES public.ml_orders(id),
|
|
order_item_id UUID NOT NULL REFERENCES public.ml_order_items(id),
|
|
user_id UUID NOT NULL REFERENCES public.ak_users(id),
|
|
product_id UUID NOT NULL REFERENCES public.ml_products(id),
|
|
merchant_id UUID NOT NULL REFERENCES public.ak_users(id),
|
|
|
|
rating INTEGER NOT NULL CHECK (rating >= 1 AND rating <= 5),
|
|
content TEXT,
|
|
images JSONB DEFAULT '[]', -- 评价图片
|
|
is_anonymous BOOLEAN DEFAULT FALSE,
|
|
|
|
-- 商家回复
|
|
merchant_reply TEXT,
|
|
merchant_replied_at TIMESTAMP WITH TIME ZONE,
|
|
|
|
status INTEGER DEFAULT 1, -- 1:正常 2:已删除 3:已隐藏
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
|
|
CONSTRAINT chk_ml_review_status CHECK (status IN (1,2,3))
|
|
);
|
|
|
|
COMMENT ON TABLE public.ml_product_reviews IS '商品评价表';
|
|
|
|
-- =====================================================================================
|
|
-- 7. 创建用户行为和系统配置表
|
|
-- =====================================================================================
|
|
|
|
-- 用户收藏表
|
|
CREATE TABLE IF NOT EXISTS public.ml_user_favorites (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
user_id UUID NOT NULL REFERENCES public.ak_users(id) ON DELETE CASCADE,
|
|
target_type INTEGER NOT NULL, -- 1:商品 2:店铺
|
|
target_id UUID NOT NULL,
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
|
|
UNIQUE(user_id, target_type, target_id),
|
|
CONSTRAINT chk_ml_favorite_type CHECK (target_type IN (1,2))
|
|
);
|
|
|
|
COMMENT ON TABLE public.ml_user_favorites IS '用户收藏表';
|
|
|
|
-- 用户浏览历史表
|
|
CREATE TABLE IF NOT EXISTS public.ml_browse_history (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
user_id UUID NOT NULL REFERENCES public.ak_users(id) ON DELETE CASCADE,
|
|
product_id UUID NOT NULL REFERENCES public.ml_products(id) ON DELETE CASCADE,
|
|
browse_duration INTEGER DEFAULT 0, -- 浏览时长(秒)
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
|
|
UNIQUE(user_id, product_id)
|
|
);
|
|
|
|
COMMENT ON TABLE public.ml_browse_history IS '用户浏览历史表';
|
|
|
|
-- 搜索记录表
|
|
CREATE TABLE IF NOT EXISTS public.ml_search_history (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
user_id UUID REFERENCES public.ak_users(id) ON DELETE CASCADE,
|
|
keyword VARCHAR(200) NOT NULL,
|
|
result_count INTEGER DEFAULT 0,
|
|
ip_address INET,
|
|
user_agent TEXT,
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
|
);
|
|
|
|
COMMENT ON TABLE public.ml_search_history IS '搜索记录表';
|
|
|
|
-- 系统配置表
|
|
CREATE TABLE IF NOT EXISTS public.ml_system_configs (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
config_key VARCHAR(100) UNIQUE NOT NULL,
|
|
config_value JSONB,
|
|
description TEXT,
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
|
);
|
|
|
|
COMMENT ON TABLE public.ml_system_configs IS '系统配置表';
|
|
|
|
-- 地区表
|
|
CREATE TABLE IF NOT EXISTS public.ml_regions (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
parent_id UUID REFERENCES public.ml_regions(id),
|
|
name VARCHAR(100) NOT NULL,
|
|
code VARCHAR(20),
|
|
level INTEGER NOT NULL, -- 1:省份 2:城市 3:区县 4:街道
|
|
sort_order INTEGER DEFAULT 0,
|
|
is_active BOOLEAN DEFAULT TRUE,
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
|
);
|
|
|
|
COMMENT ON TABLE public.ml_regions IS '地区表';
|
|
|
|
-- =====================================================================================
|
|
-- 8. 创建索引
|
|
-- =====================================================================================
|
|
|
|
-- 用户扩展表索引
|
|
CREATE INDEX IF NOT EXISTS idx_ml_user_profiles_user_id ON public.ml_user_profiles(user_id);
|
|
CREATE INDEX IF NOT EXISTS idx_ml_user_profiles_type ON public.ml_user_profiles(user_type);
|
|
CREATE INDEX IF NOT EXISTS idx_ml_user_profiles_status ON public.ml_user_profiles(status);
|
|
|
|
-- 分类表索引
|
|
CREATE INDEX IF NOT EXISTS idx_ml_categories_cid ON public.ml_categories(cid);
|
|
CREATE INDEX IF NOT EXISTS idx_ml_categories_parent ON public.ml_categories(parent_id);
|
|
CREATE INDEX IF NOT EXISTS idx_ml_categories_slug ON public.ml_categories(slug);
|
|
CREATE INDEX IF NOT EXISTS idx_ml_categories_level ON public.ml_categories(level, sort_order);
|
|
|
|
-- 品牌表索引
|
|
CREATE INDEX IF NOT EXISTS idx_ml_brands_cid ON public.ml_brands(cid);
|
|
CREATE INDEX IF NOT EXISTS idx_ml_brands_name ON public.ml_brands(name);
|
|
|
|
-- 地址表索引
|
|
CREATE INDEX IF NOT EXISTS idx_ml_user_addresses_user_id ON public.ml_user_addresses(user_id);
|
|
CREATE INDEX IF NOT EXISTS idx_ml_user_addresses_default ON public.ml_user_addresses(user_id, is_default);
|
|
CREATE INDEX IF NOT EXISTS idx_ml_user_addresses_location ON public.ml_user_addresses(city, district);
|
|
|
|
-- 商品表索引
|
|
CREATE INDEX IF NOT EXISTS idx_ml_products_cid ON public.ml_products(cid);
|
|
CREATE INDEX IF NOT EXISTS idx_ml_products_merchant ON public.ml_products(merchant_id, status);
|
|
CREATE INDEX IF NOT EXISTS idx_ml_products_category ON public.ml_products(category_id, status);
|
|
CREATE INDEX IF NOT EXISTS idx_ml_products_status ON public.ml_products(status, created_at DESC);
|
|
CREATE INDEX IF NOT EXISTS idx_ml_products_featured ON public.ml_products(is_featured, status);
|
|
CREATE INDEX IF NOT EXISTS idx_ml_products_price ON public.ml_products(base_price);
|
|
CREATE INDEX IF NOT EXISTS idx_ml_products_rating ON public.ml_products(rating_avg DESC, rating_count DESC);
|
|
CREATE INDEX IF NOT EXISTS idx_ml_products_sale_count ON public.ml_products(sale_count DESC);
|
|
CREATE INDEX IF NOT EXISTS idx_ml_products_tags ON public.ml_products USING GIN(tags);
|
|
CREATE INDEX IF NOT EXISTS idx_ml_products_slug ON public.ml_products(slug);
|
|
|
|
-- 店铺表索引
|
|
CREATE INDEX IF NOT EXISTS idx_ml_shops_cid ON public.ml_shops(cid);
|
|
CREATE INDEX IF NOT EXISTS idx_ml_shops_merchant ON public.ml_shops(merchant_id);
|
|
|
|
-- SKU表索引
|
|
CREATE INDEX IF NOT EXISTS idx_ml_product_skus_product ON public.ml_product_skus(product_id);
|
|
CREATE INDEX IF NOT EXISTS idx_ml_product_skus_code ON public.ml_product_skus(sku_code);
|
|
|
|
-- 订单表索引
|
|
CREATE INDEX IF NOT EXISTS idx_ml_orders_cid ON public.ml_orders(cid);
|
|
CREATE INDEX IF NOT EXISTS idx_ml_orders_user ON public.ml_orders(user_id, created_at DESC);
|
|
CREATE INDEX IF NOT EXISTS idx_ml_orders_merchant ON public.ml_orders(merchant_id, created_at DESC);
|
|
CREATE INDEX IF NOT EXISTS idx_ml_orders_status ON public.ml_orders(order_status, created_at DESC);
|
|
CREATE INDEX IF NOT EXISTS idx_ml_orders_no ON public.ml_orders(order_no);
|
|
|
|
-- 订单商品表索引
|
|
CREATE INDEX IF NOT EXISTS idx_ml_order_items_order ON public.ml_order_items(order_id);
|
|
CREATE INDEX IF NOT EXISTS idx_ml_order_items_product ON public.ml_order_items(product_id);
|
|
|
|
-- 购物车表索引
|
|
CREATE INDEX IF NOT EXISTS idx_ml_shopping_cart_user ON public.ml_shopping_cart(user_id);
|
|
|
|
-- 优惠券模板表索引
|
|
CREATE INDEX IF NOT EXISTS idx_ml_coupon_templates_cid ON public.ml_coupon_templates(cid);
|
|
CREATE INDEX IF NOT EXISTS idx_ml_coupon_templates_merchant ON public.ml_coupon_templates(merchant_id);
|
|
|
|
-- 优惠券表索引
|
|
CREATE INDEX IF NOT EXISTS idx_ml_user_coupons_user ON public.ml_user_coupons(user_id, status);
|
|
CREATE INDEX IF NOT EXISTS idx_ml_user_coupons_code ON public.ml_user_coupons(coupon_code);
|
|
|
|
-- 收藏表索引
|
|
CREATE INDEX IF NOT EXISTS idx_ml_user_favorites_user ON public.ml_user_favorites(user_id, target_type);
|
|
CREATE INDEX IF NOT EXISTS idx_ml_user_favorites_target ON public.ml_user_favorites(target_type, target_id);
|
|
|
|
-- 浏览历史索引
|
|
CREATE INDEX IF NOT EXISTS idx_ml_browse_history_user ON public.ml_browse_history(user_id, created_at DESC);
|
|
CREATE INDEX IF NOT EXISTS idx_ml_browse_history_product ON public.ml_browse_history(product_id);
|
|
|
|
-- =====================================================================================
|
|
-- 9. 创建触发器函数
|
|
-- =====================================================================================
|
|
|
|
-- 自动更新 updated_at 字段的函数
|
|
CREATE OR REPLACE FUNCTION public.update_updated_at_column()
|
|
RETURNS TRIGGER AS $$
|
|
BEGIN
|
|
NEW.updated_at = NOW();
|
|
RETURN NEW;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
-- 创建触发器 (使用 DO 块避免重复创建错误)
|
|
DO $$
|
|
BEGIN
|
|
-- 用户档案更新触发器
|
|
IF NOT EXISTS (SELECT 1 FROM pg_trigger WHERE tgname = 'trigger_ml_user_profiles_updated_at') THEN
|
|
CREATE TRIGGER trigger_ml_user_profiles_updated_at
|
|
BEFORE UPDATE ON public.ml_user_profiles
|
|
FOR EACH ROW EXECUTE FUNCTION public.update_updated_at_column();
|
|
END IF;
|
|
|
|
-- 用户地址更新触发器
|
|
IF NOT EXISTS (SELECT 1 FROM pg_trigger WHERE tgname = 'trigger_ml_user_addresses_updated_at') THEN
|
|
CREATE TRIGGER trigger_ml_user_addresses_updated_at
|
|
BEFORE UPDATE ON public.ml_user_addresses
|
|
FOR EACH ROW EXECUTE FUNCTION public.update_updated_at_column();
|
|
END IF;
|
|
|
|
-- 商品更新触发器
|
|
IF NOT EXISTS (SELECT 1 FROM pg_trigger WHERE tgname = 'trigger_ml_products_updated_at') THEN
|
|
CREATE TRIGGER trigger_ml_products_updated_at
|
|
BEFORE UPDATE ON public.ml_products
|
|
FOR EACH ROW EXECUTE FUNCTION public.update_updated_at_column();
|
|
END IF;
|
|
|
|
-- SKU更新触发器
|
|
IF NOT EXISTS (SELECT 1 FROM pg_trigger WHERE tgname = 'trigger_ml_product_skus_updated_at') THEN
|
|
CREATE TRIGGER trigger_ml_product_skus_updated_at
|
|
BEFORE UPDATE ON public.ml_product_skus
|
|
FOR EACH ROW EXECUTE FUNCTION public.update_updated_at_column();
|
|
END IF;
|
|
|
|
-- 店铺更新触发器
|
|
IF NOT EXISTS (SELECT 1 FROM pg_trigger WHERE tgname = 'trigger_ml_shops_updated_at') THEN
|
|
CREATE TRIGGER trigger_ml_shops_updated_at
|
|
BEFORE UPDATE ON public.ml_shops
|
|
FOR EACH ROW EXECUTE FUNCTION public.update_updated_at_column();
|
|
END IF;
|
|
|
|
-- 订单更新触发器
|
|
IF NOT EXISTS (SELECT 1 FROM pg_trigger WHERE tgname = 'trigger_ml_orders_updated_at') THEN
|
|
CREATE TRIGGER trigger_ml_orders_updated_at
|
|
BEFORE UPDATE ON public.ml_orders
|
|
FOR EACH ROW EXECUTE FUNCTION public.update_updated_at_column();
|
|
END IF;
|
|
|
|
-- 购物车更新触发器
|
|
IF NOT EXISTS (SELECT 1 FROM pg_trigger WHERE tgname = 'trigger_ml_shopping_cart_updated_at') THEN
|
|
CREATE TRIGGER trigger_ml_shopping_cart_updated_at
|
|
BEFORE UPDATE ON public.ml_shopping_cart
|
|
FOR EACH ROW EXECUTE FUNCTION public.update_updated_at_column();
|
|
END IF;
|
|
END $$;
|
|
|
|
-- 确保每个用户只有一个默认地址的触发器
|
|
CREATE OR REPLACE FUNCTION public.ensure_single_default_address()
|
|
RETURNS TRIGGER AS $$
|
|
BEGIN
|
|
IF NEW.is_default = TRUE THEN
|
|
UPDATE public.ml_user_addresses
|
|
SET is_default = FALSE
|
|
WHERE user_id = NEW.user_id AND id != NEW.id;
|
|
END IF;
|
|
RETURN NEW;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
DO $$
|
|
BEGIN
|
|
IF NOT EXISTS (SELECT 1 FROM pg_trigger WHERE tgname = 'trigger_ml_single_default_address') THEN
|
|
CREATE TRIGGER trigger_ml_single_default_address
|
|
BEFORE INSERT OR UPDATE ON public.ml_user_addresses
|
|
FOR EACH ROW EXECUTE FUNCTION public.ensure_single_default_address();
|
|
END IF;
|
|
END $$;
|
|
|
|
-- =====================================================================================
|
|
-- 10. 创建实用函数
|
|
-- =====================================================================================
|
|
|
|
-- 创建订单序列
|
|
CREATE SEQUENCE IF NOT EXISTS public.ml_order_seq START 1;
|
|
|
|
-- 生成订单号的函数
|
|
CREATE OR REPLACE FUNCTION public.generate_order_no()
|
|
RETURNS TEXT AS $$
|
|
DECLARE
|
|
order_no TEXT;
|
|
BEGIN
|
|
order_no := 'ML' || TO_CHAR(NOW(), 'YYYYMMDD') || LPAD(NEXTVAL('ml_order_seq')::TEXT, 6, '0');
|
|
RETURN order_no;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
-- 生成优惠券码的函数
|
|
CREATE OR REPLACE FUNCTION public.generate_coupon_code()
|
|
RETURNS TEXT AS $$
|
|
DECLARE
|
|
code TEXT;
|
|
chars TEXT := 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
|
|
result TEXT := '';
|
|
i INTEGER;
|
|
BEGIN
|
|
FOR i IN 1..8 LOOP
|
|
result := result || substr(chars, (random() * length(chars))::integer + 1, 1);
|
|
END LOOP;
|
|
RETURN 'CP' || result;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
-- 获取用户默认地址
|
|
CREATE OR REPLACE FUNCTION public.get_user_default_address(p_user_id UUID)
|
|
RETURNS TABLE (
|
|
id UUID,
|
|
receiver_name VARCHAR,
|
|
receiver_phone VARCHAR,
|
|
full_address TEXT,
|
|
latitude DECIMAL,
|
|
longitude DECIMAL
|
|
) AS $$
|
|
BEGIN
|
|
RETURN QUERY
|
|
SELECT
|
|
a.id,
|
|
a.receiver_name,
|
|
a.receiver_phone,
|
|
(a.province || ' ' || a.city || ' ' || a.district || ' ' || a.address_detail) as full_address,
|
|
a.latitude,
|
|
a.longitude
|
|
FROM public.ml_user_addresses a
|
|
WHERE a.user_id = p_user_id AND a.is_default = TRUE AND a.status = 1
|
|
LIMIT 1;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
-- 检查用户是否为认证商家
|
|
CREATE OR REPLACE FUNCTION public.is_verified_merchant(p_user_id UUID)
|
|
RETURNS BOOLEAN AS $$
|
|
DECLARE
|
|
result BOOLEAN := FALSE;
|
|
BEGIN
|
|
SELECT (user_type = 2 AND verification_status = 1) INTO result
|
|
FROM public.ml_user_profiles
|
|
WHERE user_id = p_user_id;
|
|
|
|
RETURN COALESCE(result, FALSE);
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
-- =====================================================================================
|
|
-- 11. 创建基础视图
|
|
-- =====================================================================================
|
|
|
|
-- 商城用户完整信息视图
|
|
CREATE OR REPLACE VIEW public.ml_users_view AS
|
|
SELECT
|
|
u.id,
|
|
u.username,
|
|
u.email,
|
|
u.phone,
|
|
u.avatar_url,
|
|
u.gender,
|
|
u.birthday,
|
|
u.bio,
|
|
u.created_at as user_created_at,
|
|
u.updated_at as user_updated_at,
|
|
p.user_type,
|
|
p.status,
|
|
p.real_name,
|
|
p.credit_score,
|
|
p.verification_status,
|
|
p.created_at as profile_created_at,
|
|
p.updated_at as profile_updated_at,
|
|
CASE
|
|
WHEN p.user_type = 1 THEN '消费者'
|
|
WHEN p.user_type = 2 THEN '商家'
|
|
WHEN p.user_type = 3 THEN '配送员'
|
|
WHEN p.user_type = 4 THEN '客服'
|
|
WHEN p.user_type = 5 THEN '管理员'
|
|
ELSE '未知'
|
|
END as user_type_name
|
|
FROM public.ak_users u
|
|
LEFT JOIN public.ml_user_profiles p ON u.id = p.user_id;
|
|
|
|
COMMENT ON VIEW public.ml_users_view IS '商城用户完整信息视图';
|
|
|
|
-- =====================================================================================
|
|
-- 12. 插入初始配置数据
|
|
-- =====================================================================================
|
|
|
|
-- 插入系统配置
|
|
INSERT INTO public.ml_system_configs (config_key, config_value, description) VALUES
|
|
('shipping_fee', '{"default": 10, "free_threshold": 88}', '配送费配置'),
|
|
('platform_commission', '{"rate": 0.05}', '平台佣金配置'),
|
|
('coupon_settings', '{"max_per_user": 10}', '优惠券设置'),
|
|
('order_auto_confirm_days', '7', '订单自动确认天数')
|
|
ON CONFLICT (config_key) DO NOTHING;
|
|
|
|
-- 插入默认分类
|
|
INSERT INTO public.ml_categories (id, name, slug, level, path) VALUES
|
|
(uuid_generate_v4(), '数码电器', 'digital', 1, ARRAY['数码电器']),
|
|
(uuid_generate_v4(), '服装鞋帽', 'fashion', 1, ARRAY['服装鞋帽']),
|
|
(uuid_generate_v4(), '家居用品', 'home', 1, ARRAY['家居用品']),
|
|
(uuid_generate_v4(), '食品饮料', 'food', 1, ARRAY['食品饮料']),
|
|
(uuid_generate_v4(), '美妆护肤', 'beauty', 1, ARRAY['美妆护肤'])
|
|
ON CONFLICT (slug) DO NOTHING;
|
|
|
|
-- 为现有 ak_users 用户创建默认商城档案 (如果不存在)
|
|
INSERT INTO public.ml_user_profiles (user_id, user_type, status)
|
|
SELECT
|
|
id,
|
|
1, -- 默认为消费者
|
|
1 -- 默认状态正常
|
|
FROM public.ak_users
|
|
WHERE id NOT IN (SELECT user_id FROM public.ml_user_profiles WHERE user_id IS NOT NULL);
|
|
|
|
-- =====================================================================================
|
|
-- 13. 完成提示
|
|
-- =====================================================================================
|
|
|
|
DO $$
|
|
BEGIN
|
|
RAISE NOTICE '=======================================================';
|
|
RAISE NOTICE '商城数据库迁移完成!';
|
|
RAISE NOTICE '=======================================================';
|
|
RAISE NOTICE '已创建表数量: 17 张商城表';
|
|
RAISE NOTICE '已创建索引: 30+ 个索引';
|
|
RAISE NOTICE '已创建触发器: 8 个触发器';
|
|
RAISE NOTICE '已创建函数: 6 个函数';
|
|
RAISE NOTICE '已创建视图: 1 个视图';
|
|
RAISE NOTICE '已插入基础配置和分类数据';
|
|
RAISE NOTICE '已为现有用户创建默认商城档案';
|
|
RAISE NOTICE '=======================================================';
|
|
RAISE NOTICE '表名前缀: ml_';
|
|
RAISE NOTICE '复用表: ak_users';
|
|
RAISE NOTICE '兼容: Supabase';
|
|
RAISE NOTICE '=======================================================';
|
|
END $$;
|