mall数据库文件
This commit is contained in:
110
mall_sql/schemas/ak_contents_product_extension.sql
Normal file
110
mall_sql/schemas/ak_contents_product_extension.sql
Normal file
@@ -0,0 +1,110 @@
|
||||
-- ===================================================================
|
||||
-- 基于ak_contents的商品扩展方案(不推荐但可行)
|
||||
-- ===================================================================
|
||||
|
||||
-- 1. 为ak_contents添加商品相关字段
|
||||
DO $$
|
||||
BEGIN
|
||||
-- 添加内容类型字段区分资讯和商品
|
||||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='ak_contents' AND column_name='content_type') THEN
|
||||
ALTER TABLE public.ak_contents ADD COLUMN content_type VARCHAR(50) DEFAULT 'article';
|
||||
COMMENT ON COLUMN public.ak_contents.content_type IS '内容类型:article=资讯,product=商品';
|
||||
END IF;
|
||||
|
||||
-- 添加商品特有字段
|
||||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='ak_contents' AND column_name='product_code') THEN
|
||||
ALTER TABLE public.ak_contents ADD COLUMN product_code VARCHAR(50);
|
||||
COMMENT ON COLUMN public.ak_contents.product_code IS '商品编码(仅商品类型使用)';
|
||||
END IF;
|
||||
|
||||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='ak_contents' AND column_name='base_price') THEN
|
||||
ALTER TABLE public.ak_contents ADD COLUMN base_price DECIMAL(12,2);
|
||||
COMMENT ON COLUMN public.ak_contents.base_price IS '商品基础价格(仅商品类型使用)';
|
||||
END IF;
|
||||
|
||||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='ak_contents' AND column_name='stock_quantity') THEN
|
||||
ALTER TABLE public.ak_contents ADD COLUMN stock_quantity INTEGER DEFAULT 0;
|
||||
COMMENT ON COLUMN public.ak_contents.stock_quantity IS '库存数量(仅商品类型使用)';
|
||||
END IF;
|
||||
|
||||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='ak_contents' AND column_name='merchant_id') THEN
|
||||
ALTER TABLE public.ak_contents ADD COLUMN merchant_id UUID;
|
||||
COMMENT ON COLUMN public.ak_contents.merchant_id IS '商家ID(仅商品类型使用)';
|
||||
END IF;
|
||||
END
|
||||
$$;
|
||||
|
||||
-- 2. 创建商品特有的附加表
|
||||
|
||||
-- 商品SKU表
|
||||
CREATE TABLE IF NOT EXISTS public.ak_product_skus (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
content_id UUID NOT NULL REFERENCES public.ak_contents(id) ON DELETE CASCADE,
|
||||
sku_code VARCHAR(100) UNIQUE NOT NULL,
|
||||
specification_values JSONB NOT NULL DEFAULT '{}',
|
||||
price DECIMAL(12,2) NOT NULL,
|
||||
stock_quantity INTEGER DEFAULT 0,
|
||||
is_default BOOLEAN DEFAULT false,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- 商品规格表
|
||||
CREATE TABLE IF NOT EXISTS public.ak_product_specifications (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
content_id UUID NOT NULL REFERENCES public.ak_contents(id) ON DELETE CASCADE,
|
||||
spec_name VARCHAR(100) NOT NULL,
|
||||
spec_values TEXT[] NOT NULL,
|
||||
is_required BOOLEAN DEFAULT false,
|
||||
sort_order INTEGER DEFAULT 0,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- 商品属性表
|
||||
CREATE TABLE IF NOT EXISTS public.ak_product_attributes (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
content_id UUID NOT NULL REFERENCES public.ak_contents(id) ON DELETE CASCADE,
|
||||
attribute_name VARCHAR(200) NOT NULL,
|
||||
attribute_value TEXT NOT NULL,
|
||||
attribute_group VARCHAR(100),
|
||||
sort_order INTEGER DEFAULT 0,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- 3. 创建专门的商品视图
|
||||
CREATE OR REPLACE VIEW public.vw_products AS
|
||||
SELECT
|
||||
id,
|
||||
title as product_name,
|
||||
content as product_description,
|
||||
product_code,
|
||||
base_price,
|
||||
stock_quantity,
|
||||
merchant_id,
|
||||
category_id,
|
||||
status,
|
||||
tags,
|
||||
published_at as listed_at,
|
||||
created_at,
|
||||
updated_at,
|
||||
-- 排除资讯相关字段
|
||||
NULL as ai_analysis_result,
|
||||
NULL as translation_status
|
||||
FROM public.ak_contents
|
||||
WHERE content_type = 'product';
|
||||
|
||||
-- 4. 添加约束和索引
|
||||
ALTER TABLE public.ak_contents ADD CONSTRAINT chk_product_code_unique
|
||||
CHECK (content_type != 'product' OR product_code IS NOT NULL);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_ak_contents_product_type ON public.ak_contents(content_type) WHERE content_type = 'product';
|
||||
CREATE INDEX IF NOT EXISTS idx_ak_contents_product_code ON public.ak_contents(product_code) WHERE content_type = 'product';
|
||||
CREATE INDEX IF NOT EXISTS idx_ak_contents_merchant ON public.ak_contents(merchant_id) WHERE content_type = 'product';
|
||||
|
||||
-- 注意事项和限制
|
||||
COMMENT ON VIEW public.vw_products IS '
|
||||
使用ak_contents作为商品表的注意事项:
|
||||
1. 必须设置content_type=product来区分商品和资讯
|
||||
2. 许多ak_contents的字段对商品无意义(如AI翻译相关)
|
||||
3. 查询性能可能受影响,建议添加分区
|
||||
4. 推荐使用独立的商品表设计以获得更好的架构
|
||||
';
|
||||
1377
mall_sql/schemas/complete_mall_database.sql
Normal file
1377
mall_sql/schemas/complete_mall_database.sql
Normal file
File diff suppressed because it is too large
Load Diff
666
mall_sql/schemas/mall_seo_security.sql
Normal file
666
mall_sql/schemas/mall_seo_security.sql
Normal file
@@ -0,0 +1,666 @@
|
||||
-- =====================================================================================
|
||||
-- 商城系统 SEO 优化和安全策略脚本
|
||||
-- 用途: 为商城系统添加 SEO 优化函数和 RLS 安全策略
|
||||
-- 前置条件: 需要先执行 mall_migration.sql
|
||||
-- =====================================================================================
|
||||
|
||||
-- =====================================================================================
|
||||
-- 1. SEO 优化相关函数
|
||||
-- =====================================================================================
|
||||
|
||||
-- 根据 cid 获取商品信息 (SEO 友好)
|
||||
CREATE OR REPLACE FUNCTION public.get_product_by_cid(p_cid INTEGER)
|
||||
RETURNS TABLE (
|
||||
id UUID,
|
||||
cid INTEGER,
|
||||
name VARCHAR,
|
||||
slug VARCHAR,
|
||||
description TEXT,
|
||||
main_image_url TEXT,
|
||||
base_price DECIMAL,
|
||||
rating_avg DECIMAL,
|
||||
sale_count INTEGER,
|
||||
category_name VARCHAR,
|
||||
brand_name VARCHAR,
|
||||
shop_name VARCHAR
|
||||
) AS $$
|
||||
BEGIN
|
||||
RETURN QUERY
|
||||
SELECT
|
||||
p.id,
|
||||
p.cid,
|
||||
p.name,
|
||||
p.slug,
|
||||
p.description,
|
||||
p.main_image_url,
|
||||
p.base_price,
|
||||
p.rating_avg,
|
||||
p.sale_count,
|
||||
c.name as category_name,
|
||||
b.name as brand_name,
|
||||
s.shop_name
|
||||
FROM public.ml_products p
|
||||
LEFT JOIN public.ml_categories c ON p.category_id = c.id
|
||||
LEFT JOIN public.ml_brands b ON p.brand_id = b.id
|
||||
LEFT JOIN public.ml_shops s ON p.merchant_id = s.merchant_id
|
||||
WHERE p.cid = p_cid AND p.status = 1;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
-- 根据 cid 获取分类信息 (SEO 友好)
|
||||
CREATE OR REPLACE FUNCTION public.get_category_by_cid(p_cid INTEGER)
|
||||
RETURNS TABLE (
|
||||
id UUID,
|
||||
cid INTEGER,
|
||||
name VARCHAR,
|
||||
slug VARCHAR,
|
||||
description TEXT,
|
||||
icon_url TEXT,
|
||||
path TEXT[]
|
||||
) AS $$
|
||||
BEGIN
|
||||
RETURN QUERY
|
||||
SELECT
|
||||
c.id,
|
||||
c.cid,
|
||||
c.name,
|
||||
c.slug,
|
||||
c.description,
|
||||
c.icon_url,
|
||||
c.path
|
||||
FROM public.ml_categories c
|
||||
WHERE c.cid = p_cid AND c.is_active = TRUE;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
-- 根据 cid 获取品牌信息 (SEO 友好)
|
||||
CREATE OR REPLACE FUNCTION public.get_brand_by_cid(p_cid INTEGER)
|
||||
RETURNS TABLE (
|
||||
id UUID,
|
||||
cid INTEGER,
|
||||
name VARCHAR,
|
||||
logo_url TEXT,
|
||||
description TEXT
|
||||
) AS $$
|
||||
BEGIN
|
||||
RETURN QUERY
|
||||
SELECT
|
||||
b.id,
|
||||
b.cid,
|
||||
b.name,
|
||||
b.logo_url,
|
||||
b.description
|
||||
FROM public.ml_brands b
|
||||
WHERE b.cid = p_cid AND b.is_active = TRUE;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
-- 根据 cid 获取店铺信息 (SEO 友好)
|
||||
CREATE OR REPLACE FUNCTION public.get_shop_by_cid(p_cid INTEGER)
|
||||
RETURNS TABLE (
|
||||
id UUID,
|
||||
cid INTEGER,
|
||||
shop_name VARCHAR,
|
||||
description TEXT,
|
||||
shop_logo TEXT,
|
||||
rating_avg DECIMAL,
|
||||
product_count INTEGER
|
||||
) AS $$
|
||||
BEGIN
|
||||
RETURN QUERY
|
||||
SELECT
|
||||
s.id,
|
||||
s.cid,
|
||||
s.shop_name,
|
||||
s.description,
|
||||
s.shop_logo,
|
||||
s.rating_avg,
|
||||
s.product_count
|
||||
FROM public.ml_shops s
|
||||
WHERE s.cid = p_cid AND s.status = 1;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
-- 生成 SEO 友好的 URL 路径
|
||||
CREATE OR REPLACE FUNCTION public.generate_seo_url(
|
||||
p_type VARCHAR, -- 'product', 'category', 'brand', 'shop'
|
||||
p_cid INTEGER,
|
||||
p_slug VARCHAR DEFAULT NULL
|
||||
)
|
||||
RETURNS TEXT AS $$
|
||||
DECLARE
|
||||
url_path TEXT;
|
||||
BEGIN
|
||||
CASE p_type
|
||||
WHEN 'product' THEN
|
||||
url_path := '/product/' || p_cid;
|
||||
IF p_slug IS NOT NULL THEN
|
||||
url_path := url_path || '/' || p_slug;
|
||||
END IF;
|
||||
WHEN 'category' THEN
|
||||
url_path := '/category/' || p_cid;
|
||||
IF p_slug IS NOT NULL THEN
|
||||
url_path := url_path || '/' || p_slug;
|
||||
END IF;
|
||||
WHEN 'brand' THEN
|
||||
url_path := '/brand/' || p_cid;
|
||||
IF p_slug IS NOT NULL THEN
|
||||
url_path := url_path || '/' || p_slug;
|
||||
END IF;
|
||||
WHEN 'shop' THEN
|
||||
url_path := '/shop/' || p_cid;
|
||||
IF p_slug IS NOT NULL THEN
|
||||
url_path := url_path || '/' || p_slug;
|
||||
END IF;
|
||||
ELSE
|
||||
url_path := '/' || p_type || '/' || p_cid;
|
||||
END CASE;
|
||||
|
||||
RETURN url_path;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
-- 批量更新 slug 字段(用于现有数据)
|
||||
CREATE OR REPLACE FUNCTION public.update_seo_slugs()
|
||||
RETURNS VOID AS $$
|
||||
BEGIN
|
||||
-- 更新商品 slug
|
||||
UPDATE public.ml_products
|
||||
SET slug = LOWER(REGEXP_REPLACE(name, '[^a-zA-Z0-9\u4e00-\u9fa5]+', '-', 'g'))
|
||||
WHERE slug IS NULL OR slug = '';
|
||||
|
||||
-- 更新分类 slug
|
||||
UPDATE public.ml_categories
|
||||
SET slug = LOWER(REGEXP_REPLACE(name, '[^a-zA-Z0-9\u4e00-\u9fa5]+', '-', 'g'))
|
||||
WHERE slug IS NULL OR slug = '';
|
||||
|
||||
RAISE NOTICE 'SEO slugs updated successfully';
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
-- =====================================================================================
|
||||
-- 2. 商业逻辑函数
|
||||
-- =====================================================================================
|
||||
|
||||
-- 计算购物车总金额
|
||||
CREATE OR REPLACE FUNCTION public.calculate_cart_total(p_user_id UUID)
|
||||
RETURNS DECIMAL AS $$
|
||||
DECLARE
|
||||
total_amount DECIMAL := 0;
|
||||
BEGIN
|
||||
SELECT COALESCE(SUM(
|
||||
CASE
|
||||
WHEN s.id IS NOT NULL THEN s.price * c.quantity
|
||||
ELSE p.base_price * c.quantity
|
||||
END
|
||||
), 0) INTO total_amount
|
||||
FROM public.ml_shopping_cart c
|
||||
LEFT JOIN public.ml_product_skus s ON c.sku_id = s.id
|
||||
LEFT JOIN public.ml_products p ON c.product_id = p.id
|
||||
WHERE c.user_id = p_user_id
|
||||
AND c.selected = TRUE
|
||||
AND p.status = 1
|
||||
AND (s.id IS NULL OR s.status = 1);
|
||||
|
||||
RETURN total_amount;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
-- 获取商品可用库存
|
||||
CREATE OR REPLACE FUNCTION public.get_product_available_stock(p_product_id UUID, p_sku_id UUID DEFAULT NULL)
|
||||
RETURNS INTEGER AS $$
|
||||
DECLARE
|
||||
stock_count INTEGER := 0;
|
||||
BEGIN
|
||||
IF p_sku_id IS NOT NULL THEN
|
||||
-- 获取特定SKU库存
|
||||
SELECT COALESCE(stock, 0) INTO stock_count
|
||||
FROM public.ml_product_skus
|
||||
WHERE id = p_sku_id AND product_id = p_product_id AND status = 1;
|
||||
ELSE
|
||||
-- 获取商品总库存
|
||||
SELECT COALESCE(available_stock, 0) INTO stock_count
|
||||
FROM public.ml_products
|
||||
WHERE id = p_product_id AND status = 1;
|
||||
END IF;
|
||||
|
||||
RETURN stock_count;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
-- 商品库存更新触发器函数
|
||||
CREATE OR REPLACE FUNCTION public.update_product_stock()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
-- 更新商品总库存
|
||||
IF TG_OP = 'DELETE' THEN
|
||||
UPDATE public.ml_products
|
||||
SET
|
||||
total_stock = (
|
||||
SELECT COALESCE(SUM(stock), 0)
|
||||
FROM public.ml_product_skus
|
||||
WHERE product_id = OLD.product_id AND status = 1
|
||||
),
|
||||
available_stock = (
|
||||
SELECT COALESCE(SUM(stock), 0)
|
||||
FROM public.ml_product_skus
|
||||
WHERE product_id = OLD.product_id AND status = 1
|
||||
)
|
||||
WHERE id = OLD.product_id;
|
||||
RETURN OLD;
|
||||
ELSE
|
||||
UPDATE public.ml_products
|
||||
SET
|
||||
total_stock = (
|
||||
SELECT COALESCE(SUM(stock), 0)
|
||||
FROM public.ml_product_skus
|
||||
WHERE product_id = NEW.product_id AND status = 1
|
||||
),
|
||||
available_stock = (
|
||||
SELECT COALESCE(SUM(stock), 0)
|
||||
FROM public.ml_product_skus
|
||||
WHERE product_id = NEW.product_id AND status = 1
|
||||
)
|
||||
WHERE id = NEW.product_id;
|
||||
RETURN NEW;
|
||||
END IF;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
-- 订单状态变更时的处理
|
||||
CREATE OR REPLACE FUNCTION public.handle_order_status_change()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
-- 如果订单状态变为已付款
|
||||
IF NEW.order_status = 2 AND (OLD.order_status IS NULL OR OLD.order_status = 1) THEN
|
||||
NEW.paid_at = NOW();
|
||||
END IF;
|
||||
|
||||
-- 如果订单状态变为已发货
|
||||
IF NEW.order_status = 3 AND OLD.order_status = 2 THEN
|
||||
NEW.shipped_at = NOW();
|
||||
END IF;
|
||||
|
||||
-- 如果订单状态变为已完成
|
||||
IF NEW.order_status = 4 AND OLD.order_status = 3 THEN
|
||||
NEW.delivered_at = NOW();
|
||||
NEW.completed_at = NOW();
|
||||
|
||||
-- 更新商品销量
|
||||
UPDATE public.ml_products
|
||||
SET sale_count = sale_count + (
|
||||
SELECT SUM(quantity)
|
||||
FROM public.ml_order_items
|
||||
WHERE order_id = NEW.id
|
||||
)
|
||||
WHERE id IN (
|
||||
SELECT product_id
|
||||
FROM public.ml_order_items
|
||||
WHERE order_id = NEW.id
|
||||
);
|
||||
END IF;
|
||||
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
-- 创建库存更新触发器
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_trigger WHERE tgname = 'trigger_ml_update_product_stock') THEN
|
||||
CREATE TRIGGER trigger_ml_update_product_stock
|
||||
AFTER INSERT OR UPDATE OR DELETE ON public.ml_product_skus
|
||||
FOR EACH ROW EXECUTE FUNCTION public.update_product_stock();
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- 创建订单状态变更触发器
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_trigger WHERE tgname = 'trigger_ml_order_status_change') THEN
|
||||
CREATE TRIGGER trigger_ml_order_status_change
|
||||
BEFORE UPDATE ON public.ml_orders
|
||||
FOR EACH ROW EXECUTE FUNCTION public.handle_order_status_change();
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- =====================================================================================
|
||||
-- 3. 创建详细视图
|
||||
-- =====================================================================================
|
||||
|
||||
-- 商品详情视图
|
||||
CREATE OR REPLACE VIEW public.ml_products_detail_view AS
|
||||
SELECT
|
||||
p.*,
|
||||
c.cid as category_cid,
|
||||
c.name as category_name,
|
||||
c.path as category_path,
|
||||
b.cid as brand_cid,
|
||||
b.name as brand_name,
|
||||
s.cid as shop_cid,
|
||||
s.shop_name,
|
||||
u.username as merchant_name,
|
||||
CASE
|
||||
WHEN p.status = 1 THEN '上架'
|
||||
WHEN p.status = 2 THEN '下架'
|
||||
WHEN p.status = 3 THEN '草稿'
|
||||
WHEN p.status = 4 THEN '删除'
|
||||
ELSE '未知'
|
||||
END as status_name
|
||||
FROM public.ml_products p
|
||||
LEFT JOIN public.ml_categories c ON p.category_id = c.id
|
||||
LEFT JOIN public.ml_brands b ON p.brand_id = b.id
|
||||
LEFT JOIN public.ml_shops s ON p.merchant_id = s.merchant_id
|
||||
LEFT JOIN public.ak_users u ON p.merchant_id = u.id;
|
||||
|
||||
COMMENT ON VIEW public.ml_products_detail_view IS '商品详情视图';
|
||||
|
||||
-- 订单详情视图
|
||||
CREATE OR REPLACE VIEW public.ml_orders_detail_view AS
|
||||
SELECT
|
||||
o.*,
|
||||
u.username as customer_name,
|
||||
u.phone as customer_phone,
|
||||
m.username as merchant_name,
|
||||
s.shop_name,
|
||||
CASE
|
||||
WHEN o.order_status = 1 THEN '待付款'
|
||||
WHEN o.order_status = 2 THEN '待发货'
|
||||
WHEN o.order_status = 3 THEN '待收货'
|
||||
WHEN o.order_status = 4 THEN '已完成'
|
||||
WHEN o.order_status = 5 THEN '已取消'
|
||||
WHEN o.order_status = 6 THEN '退款中'
|
||||
WHEN o.order_status = 7 THEN '已退款'
|
||||
ELSE '未知'
|
||||
END as order_status_name,
|
||||
CASE
|
||||
WHEN o.payment_status = 1 THEN '未付款'
|
||||
WHEN o.payment_status = 2 THEN '已付款'
|
||||
WHEN o.payment_status = 3 THEN '部分退款'
|
||||
WHEN o.payment_status = 4 THEN '全额退款'
|
||||
ELSE '未知'
|
||||
END as payment_status_name
|
||||
FROM public.ml_orders o
|
||||
LEFT JOIN public.ak_users u ON o.user_id = u.id
|
||||
LEFT JOIN public.ak_users m ON o.merchant_id = m.id
|
||||
LEFT JOIN public.ml_shops s ON o.merchant_id = s.merchant_id;
|
||||
|
||||
COMMENT ON VIEW public.ml_orders_detail_view IS '订单详情视图';
|
||||
|
||||
-- =====================================================================================
|
||||
-- 4. RLS (Row Level Security) 策略
|
||||
-- =====================================================================================
|
||||
|
||||
-- 启用 RLS
|
||||
ALTER TABLE public.ml_user_profiles ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE public.ml_user_addresses ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE public.ml_shopping_cart ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE public.ml_user_favorites ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE public.ml_browse_history ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE public.ml_user_coupons ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE public.ml_orders ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE public.ml_products ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
-- 用户档案策略:用户只能访问自己的数据
|
||||
DO $$
|
||||
BEGIN
|
||||
-- 删除可能存在的策略
|
||||
DROP POLICY IF EXISTS ml_user_profiles_select_policy ON public.ml_user_profiles;
|
||||
DROP POLICY IF EXISTS ml_user_profiles_insert_policy ON public.ml_user_profiles;
|
||||
DROP POLICY IF EXISTS ml_user_profiles_update_policy ON public.ml_user_profiles;
|
||||
DROP POLICY IF EXISTS ml_user_profiles_delete_policy ON public.ml_user_profiles;
|
||||
|
||||
-- 创建新策略
|
||||
CREATE POLICY ml_user_profiles_select_policy ON public.ml_user_profiles
|
||||
FOR SELECT USING (
|
||||
auth.uid() = (SELECT auth_id FROM public.ak_users WHERE id = user_id)
|
||||
);
|
||||
|
||||
CREATE POLICY ml_user_profiles_insert_policy ON public.ml_user_profiles
|
||||
FOR INSERT WITH CHECK (
|
||||
auth.uid() = (SELECT auth_id FROM public.ak_users WHERE id = user_id)
|
||||
);
|
||||
|
||||
CREATE POLICY ml_user_profiles_update_policy ON public.ml_user_profiles
|
||||
FOR UPDATE USING (
|
||||
auth.uid() = (SELECT auth_id FROM public.ak_users WHERE id = user_id)
|
||||
);
|
||||
|
||||
CREATE POLICY ml_user_profiles_delete_policy ON public.ml_user_profiles
|
||||
FOR DELETE USING (
|
||||
auth.uid() = (SELECT auth_id FROM public.ak_users WHERE id = user_id)
|
||||
);
|
||||
END $$;
|
||||
|
||||
-- 用户地址策略
|
||||
DO $$
|
||||
BEGIN
|
||||
DROP POLICY IF EXISTS ml_user_addresses_select_policy ON public.ml_user_addresses;
|
||||
DROP POLICY IF EXISTS ml_user_addresses_insert_policy ON public.ml_user_addresses;
|
||||
DROP POLICY IF EXISTS ml_user_addresses_update_policy ON public.ml_user_addresses;
|
||||
DROP POLICY IF EXISTS ml_user_addresses_delete_policy ON public.ml_user_addresses;
|
||||
|
||||
CREATE POLICY ml_user_addresses_select_policy ON public.ml_user_addresses
|
||||
FOR SELECT USING (
|
||||
auth.uid() = (SELECT auth_id FROM public.ak_users WHERE id = user_id)
|
||||
);
|
||||
|
||||
CREATE POLICY ml_user_addresses_insert_policy ON public.ml_user_addresses
|
||||
FOR INSERT WITH CHECK (
|
||||
auth.uid() = (SELECT auth_id FROM public.ak_users WHERE id = user_id)
|
||||
);
|
||||
|
||||
CREATE POLICY ml_user_addresses_update_policy ON public.ml_user_addresses
|
||||
FOR UPDATE USING (
|
||||
auth.uid() = (SELECT auth_id FROM public.ak_users WHERE id = user_id)
|
||||
);
|
||||
|
||||
CREATE POLICY ml_user_addresses_delete_policy ON public.ml_user_addresses
|
||||
FOR DELETE USING (
|
||||
auth.uid() = (SELECT auth_id FROM public.ak_users WHERE id = user_id)
|
||||
);
|
||||
END $$;
|
||||
|
||||
-- 购物车策略
|
||||
DO $$
|
||||
BEGIN
|
||||
DROP POLICY IF EXISTS ml_shopping_cart_select_policy ON public.ml_shopping_cart;
|
||||
DROP POLICY IF EXISTS ml_shopping_cart_insert_policy ON public.ml_shopping_cart;
|
||||
DROP POLICY IF EXISTS ml_shopping_cart_update_policy ON public.ml_shopping_cart;
|
||||
DROP POLICY IF EXISTS ml_shopping_cart_delete_policy ON public.ml_shopping_cart;
|
||||
|
||||
CREATE POLICY ml_shopping_cart_select_policy ON public.ml_shopping_cart
|
||||
FOR SELECT USING (
|
||||
auth.uid() = (SELECT auth_id FROM public.ak_users WHERE id = user_id)
|
||||
);
|
||||
|
||||
CREATE POLICY ml_shopping_cart_insert_policy ON public.ml_shopping_cart
|
||||
FOR INSERT WITH CHECK (
|
||||
auth.uid() = (SELECT auth_id FROM public.ak_users WHERE id = user_id)
|
||||
);
|
||||
|
||||
CREATE POLICY ml_shopping_cart_update_policy ON public.ml_shopping_cart
|
||||
FOR UPDATE USING (
|
||||
auth.uid() = (SELECT auth_id FROM public.ak_users WHERE id = user_id)
|
||||
);
|
||||
|
||||
CREATE POLICY ml_shopping_cart_delete_policy ON public.ml_shopping_cart
|
||||
FOR DELETE USING (
|
||||
auth.uid() = (SELECT auth_id FROM public.ak_users WHERE id = user_id)
|
||||
);
|
||||
END $$;
|
||||
|
||||
-- 订单策略:用户可以查看自己的订单,商家可以查看自己店铺的订单
|
||||
DO $$
|
||||
BEGIN
|
||||
DROP POLICY IF EXISTS ml_orders_select_policy ON public.ml_orders;
|
||||
DROP POLICY IF EXISTS ml_orders_insert_policy ON public.ml_orders;
|
||||
DROP POLICY IF EXISTS ml_orders_update_policy ON public.ml_orders;
|
||||
DROP POLICY IF EXISTS ml_orders_delete_policy ON public.ml_orders;
|
||||
|
||||
CREATE POLICY ml_orders_select_policy ON public.ml_orders
|
||||
FOR SELECT USING (
|
||||
auth.uid() IN (
|
||||
SELECT auth_id FROM public.ak_users WHERE id IN (user_id, merchant_id)
|
||||
)
|
||||
);
|
||||
|
||||
CREATE POLICY ml_orders_insert_policy ON public.ml_orders
|
||||
FOR INSERT WITH CHECK (
|
||||
auth.uid() IN (
|
||||
SELECT auth_id FROM public.ak_users WHERE id IN (user_id, merchant_id)
|
||||
)
|
||||
);
|
||||
|
||||
CREATE POLICY ml_orders_update_policy ON public.ml_orders
|
||||
FOR UPDATE USING (
|
||||
auth.uid() IN (
|
||||
SELECT auth_id FROM public.ak_users WHERE id IN (user_id, merchant_id)
|
||||
)
|
||||
);
|
||||
|
||||
CREATE POLICY ml_orders_delete_policy ON public.ml_orders
|
||||
FOR DELETE USING (
|
||||
auth.uid() IN (
|
||||
SELECT auth_id FROM public.ak_users WHERE id IN (user_id, merchant_id)
|
||||
)
|
||||
);
|
||||
END $$;
|
||||
|
||||
-- 商品策略:所有人可以查看上架商品,商家只能管理自己的商品
|
||||
DO $$
|
||||
BEGIN
|
||||
DROP POLICY IF EXISTS ml_products_select_policy ON public.ml_products;
|
||||
DROP POLICY IF EXISTS ml_products_insert_policy ON public.ml_products;
|
||||
DROP POLICY IF EXISTS ml_products_update_policy ON public.ml_products;
|
||||
DROP POLICY IF EXISTS ml_products_delete_policy ON public.ml_products;
|
||||
|
||||
CREATE POLICY ml_products_select_policy ON public.ml_products
|
||||
FOR SELECT USING (status = 1);
|
||||
|
||||
CREATE POLICY ml_products_insert_policy ON public.ml_products
|
||||
FOR INSERT WITH CHECK (
|
||||
auth.uid() = (SELECT auth_id FROM public.ak_users WHERE id = merchant_id)
|
||||
);
|
||||
|
||||
CREATE POLICY ml_products_update_policy ON public.ml_products
|
||||
FOR UPDATE USING (
|
||||
auth.uid() = (SELECT auth_id FROM public.ak_users WHERE id = merchant_id)
|
||||
);
|
||||
|
||||
CREATE POLICY ml_products_delete_policy ON public.ml_products
|
||||
FOR DELETE USING (
|
||||
auth.uid() = (SELECT auth_id FROM public.ak_users WHERE id = merchant_id)
|
||||
);
|
||||
END $$;
|
||||
|
||||
-- 收藏策略
|
||||
DO $$
|
||||
BEGIN
|
||||
DROP POLICY IF EXISTS ml_user_favorites_select_policy ON public.ml_user_favorites;
|
||||
DROP POLICY IF EXISTS ml_user_favorites_insert_policy ON public.ml_user_favorites;
|
||||
DROP POLICY IF EXISTS ml_user_favorites_update_policy ON public.ml_user_favorites;
|
||||
DROP POLICY IF EXISTS ml_user_favorites_delete_policy ON public.ml_user_favorites;
|
||||
|
||||
CREATE POLICY ml_user_favorites_select_policy ON public.ml_user_favorites
|
||||
FOR SELECT USING (
|
||||
auth.uid() = (SELECT auth_id FROM public.ak_users WHERE id = user_id)
|
||||
);
|
||||
|
||||
CREATE POLICY ml_user_favorites_insert_policy ON public.ml_user_favorites
|
||||
FOR INSERT WITH CHECK (
|
||||
auth.uid() = (SELECT auth_id FROM public.ak_users WHERE id = user_id)
|
||||
);
|
||||
|
||||
CREATE POLICY ml_user_favorites_update_policy ON public.ml_user_favorites
|
||||
FOR UPDATE USING (
|
||||
auth.uid() = (SELECT auth_id FROM public.ak_users WHERE id = user_id)
|
||||
);
|
||||
|
||||
CREATE POLICY ml_user_favorites_delete_policy ON public.ml_user_favorites
|
||||
FOR DELETE USING (
|
||||
auth.uid() = (SELECT auth_id FROM public.ak_users WHERE id = user_id)
|
||||
);
|
||||
END $$;
|
||||
|
||||
-- 浏览历史策略
|
||||
DO $$
|
||||
BEGIN
|
||||
DROP POLICY IF EXISTS ml_browse_history_select_policy ON public.ml_browse_history;
|
||||
DROP POLICY IF EXISTS ml_browse_history_insert_policy ON public.ml_browse_history;
|
||||
DROP POLICY IF EXISTS ml_browse_history_update_policy ON public.ml_browse_history;
|
||||
DROP POLICY IF EXISTS ml_browse_history_delete_policy ON public.ml_browse_history;
|
||||
|
||||
CREATE POLICY ml_browse_history_select_policy ON public.ml_browse_history
|
||||
FOR SELECT USING (
|
||||
auth.uid() = (SELECT auth_id FROM public.ak_users WHERE id = user_id)
|
||||
);
|
||||
|
||||
CREATE POLICY ml_browse_history_insert_policy ON public.ml_browse_history
|
||||
FOR INSERT WITH CHECK (
|
||||
auth.uid() = (SELECT auth_id FROM public.ak_users WHERE id = user_id)
|
||||
);
|
||||
|
||||
CREATE POLICY ml_browse_history_update_policy ON public.ml_browse_history
|
||||
FOR UPDATE USING (
|
||||
auth.uid() = (SELECT auth_id FROM public.ak_users WHERE id = user_id)
|
||||
);
|
||||
|
||||
CREATE POLICY ml_browse_history_delete_policy ON public.ml_browse_history
|
||||
FOR DELETE USING (
|
||||
auth.uid() = (SELECT auth_id FROM public.ak_users WHERE id = user_id)
|
||||
);
|
||||
END $$;
|
||||
|
||||
-- 优惠券策略
|
||||
DO $$
|
||||
BEGIN
|
||||
DROP POLICY IF EXISTS ml_user_coupons_select_policy ON public.ml_user_coupons;
|
||||
DROP POLICY IF EXISTS ml_user_coupons_insert_policy ON public.ml_user_coupons;
|
||||
DROP POLICY IF EXISTS ml_user_coupons_update_policy ON public.ml_user_coupons;
|
||||
DROP POLICY IF EXISTS ml_user_coupons_delete_policy ON public.ml_user_coupons;
|
||||
|
||||
CREATE POLICY ml_user_coupons_select_policy ON public.ml_user_coupons
|
||||
FOR SELECT USING (
|
||||
auth.uid() = (SELECT auth_id FROM public.ak_users WHERE id = user_id)
|
||||
);
|
||||
|
||||
CREATE POLICY ml_user_coupons_insert_policy ON public.ml_user_coupons
|
||||
FOR INSERT WITH CHECK (
|
||||
auth.uid() = (SELECT auth_id FROM public.ak_users WHERE id = user_id)
|
||||
);
|
||||
|
||||
CREATE POLICY ml_user_coupons_update_policy ON public.ml_user_coupons
|
||||
FOR UPDATE USING (
|
||||
auth.uid() = (SELECT auth_id FROM public.ak_users WHERE id = user_id)
|
||||
);
|
||||
|
||||
CREATE POLICY ml_user_coupons_delete_policy ON public.ml_user_coupons
|
||||
FOR DELETE USING (
|
||||
auth.uid() = (SELECT auth_id FROM public.ak_users WHERE id = user_id)
|
||||
);
|
||||
END $$;
|
||||
|
||||
-- =====================================================================================
|
||||
-- 5. 完成提示
|
||||
-- =====================================================================================
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
RAISE NOTICE '=======================================================';
|
||||
RAISE NOTICE 'SEO 优化和安全策略配置完成!';
|
||||
RAISE NOTICE '=======================================================';
|
||||
RAISE NOTICE '已创建 SEO 函数: 6 个';
|
||||
RAISE NOTICE '已创建业务函数: 4 个';
|
||||
RAISE NOTICE '已创建详细视图: 2 个';
|
||||
RAISE NOTICE '已配置 RLS 策略: 8 个表';
|
||||
RAISE NOTICE '已创建库存和订单触发器';
|
||||
RAISE NOTICE '=======================================================';
|
||||
RAISE NOTICE '功能说明:';
|
||||
RAISE NOTICE '- SEO 友好的 URL 生成';
|
||||
RAISE NOTICE '- CID 基础的数据查询';
|
||||
RAISE NOTICE '- 自动库存管理';
|
||||
RAISE NOTICE '- 订单状态自动更新';
|
||||
RAISE NOTICE '- 用户数据安全隔离';
|
||||
RAISE NOTICE '=======================================================';
|
||||
END $$;
|
||||
452
mall_sql/schemas/product_database.sql
Normal file
452
mall_sql/schemas/product_database.sql
Normal file
@@ -0,0 +1,452 @@
|
||||
-- ===================================================================
|
||||
-- 电商商城商品管理数据库设计
|
||||
-- 基于PostgreSQL,兼容现有ak_contents资讯系统
|
||||
-- ===================================================================
|
||||
|
||||
-- ===================================================================
|
||||
-- 1. 商品核心表
|
||||
-- ===================================================================
|
||||
|
||||
-- 商品基础信息表
|
||||
CREATE TABLE IF NOT EXISTS public.mall_products (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
product_code VARCHAR(50) UNIQUE NOT NULL, -- 商品编码
|
||||
name VARCHAR(500) NOT NULL, -- 商品名称
|
||||
subtitle VARCHAR(1000), -- 副标题/卖点
|
||||
description TEXT, -- 商品描述
|
||||
|
||||
-- 商家信息
|
||||
merchant_id UUID NOT NULL REFERENCES public.ak_users(id),
|
||||
brand_id UUID REFERENCES public.mall_brands(id),
|
||||
|
||||
-- 分类信息
|
||||
category_id UUID NOT NULL REFERENCES public.mall_categories(id),
|
||||
category_path TEXT[], -- 分类路径,便于查询
|
||||
|
||||
-- 基础属性
|
||||
weight DECIMAL(10,3), -- 重量(kg)
|
||||
dimensions JSONB, -- 尺寸信息 {长,宽,高}
|
||||
|
||||
-- 价格信息
|
||||
base_price DECIMAL(12,2) NOT NULL, -- 基础价格
|
||||
market_price DECIMAL(12,2), -- 市场价
|
||||
cost_price DECIMAL(12,2), -- 成本价
|
||||
|
||||
-- 库存信息
|
||||
stock_quantity INTEGER DEFAULT 0, -- 总库存
|
||||
available_quantity INTEGER DEFAULT 0, -- 可用库存
|
||||
reserved_quantity INTEGER DEFAULT 0, -- 预留库存
|
||||
min_order_quantity INTEGER DEFAULT 1, -- 最小起订量
|
||||
max_order_quantity INTEGER, -- 最大限购量
|
||||
|
||||
-- 状态信息
|
||||
status VARCHAR(20) DEFAULT 'draft', -- 状态:draft/active/inactive/deleted
|
||||
is_featured BOOLEAN DEFAULT false, -- 是否精选
|
||||
is_new BOOLEAN DEFAULT false, -- 是否新品
|
||||
is_hot BOOLEAN DEFAULT false, -- 是否热卖
|
||||
is_on_sale BOOLEAN DEFAULT false, -- 是否促销
|
||||
|
||||
-- 多媒体
|
||||
main_image_url TEXT, -- 主图
|
||||
image_urls TEXT[], -- 图片URL数组
|
||||
video_urls TEXT[], -- 视频URL数组
|
||||
|
||||
-- SEO相关
|
||||
seo_title VARCHAR(200), -- SEO标题
|
||||
seo_description VARCHAR(500), -- SEO描述
|
||||
seo_keywords TEXT[], -- SEO关键词
|
||||
slug VARCHAR(200) UNIQUE, -- URL友好标识
|
||||
|
||||
-- 销售统计
|
||||
view_count INTEGER DEFAULT 0, -- 浏览次数
|
||||
sale_count INTEGER DEFAULT 0, -- 销售数量
|
||||
favorite_count INTEGER DEFAULT 0, -- 收藏次数
|
||||
rating_average DECIMAL(3,2) DEFAULT 0, -- 平均评分
|
||||
rating_count INTEGER DEFAULT 0, -- 评分次数
|
||||
|
||||
-- 时间信息
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
published_at TIMESTAMP WITH TIME ZONE, -- 上架时间
|
||||
sale_start_at TIMESTAMP WITH TIME ZONE, -- 开售时间
|
||||
sale_end_at TIMESTAMP WITH TIME ZONE, -- 停售时间
|
||||
|
||||
-- 额外信息
|
||||
tags TEXT[], -- 标签
|
||||
attributes JSONB DEFAULT '{}', -- 自定义属性
|
||||
notes TEXT, -- 内部备注
|
||||
|
||||
-- 约束
|
||||
CONSTRAINT chk_price_positive CHECK (base_price >= 0),
|
||||
CONSTRAINT chk_stock_non_negative CHECK (stock_quantity >= 0),
|
||||
CONSTRAINT chk_available_stock CHECK (available_quantity >= 0),
|
||||
CONSTRAINT chk_reserved_stock CHECK (reserved_quantity >= 0),
|
||||
CONSTRAINT chk_rating_range CHECK (rating_average >= 0 AND rating_average <= 5)
|
||||
);
|
||||
|
||||
-- 商品表索引
|
||||
CREATE INDEX IF NOT EXISTS idx_mall_products_merchant ON public.mall_products(merchant_id, status);
|
||||
CREATE INDEX IF NOT EXISTS idx_mall_products_category ON public.mall_products(category_id, status);
|
||||
CREATE INDEX IF NOT EXISTS idx_mall_products_status ON public.mall_products(status, published_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_mall_products_featured ON public.mall_products(is_featured, published_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_mall_products_price ON public.mall_products(base_price, status);
|
||||
CREATE INDEX IF NOT EXISTS idx_mall_products_sale_count ON public.mall_products(sale_count DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_mall_products_rating ON public.mall_products(rating_average DESC, rating_count DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_mall_products_code ON public.mall_products(product_code);
|
||||
CREATE INDEX IF NOT EXISTS idx_mall_products_slug ON public.mall_products(slug);
|
||||
CREATE INDEX IF NOT EXISTS idx_mall_products_tags ON public.mall_products USING GIN(tags);
|
||||
CREATE INDEX IF NOT EXISTS idx_mall_products_category_path ON public.mall_products USING GIN(category_path);
|
||||
|
||||
COMMENT ON TABLE public.mall_products IS '商品基础信息表';
|
||||
|
||||
-- ===================================================================
|
||||
-- 2. 商品SKU表
|
||||
-- ===================================================================
|
||||
|
||||
-- 商品SKU表
|
||||
CREATE TABLE IF NOT EXISTS public.mall_product_skus (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
product_id UUID NOT NULL REFERENCES public.mall_products(id) ON DELETE CASCADE,
|
||||
sku_code VARCHAR(100) UNIQUE NOT NULL, -- SKU编码
|
||||
|
||||
-- 规格信息
|
||||
specification_values JSONB NOT NULL DEFAULT '{}', -- 规格值 {"颜色":"红色","尺寸":"L"}
|
||||
specification_text VARCHAR(500), -- 规格描述文本
|
||||
|
||||
-- 价格库存
|
||||
price DECIMAL(12,2) NOT NULL, -- SKU价格
|
||||
cost_price DECIMAL(12,2), -- SKU成本价
|
||||
stock_quantity INTEGER DEFAULT 0, -- SKU库存
|
||||
available_quantity INTEGER DEFAULT 0, -- SKU可用库存
|
||||
reserved_quantity INTEGER DEFAULT 0, -- SKU预留库存
|
||||
|
||||
-- SKU属性
|
||||
weight DECIMAL(10,3), -- SKU重量
|
||||
barcode VARCHAR(50), -- 条形码
|
||||
image_url TEXT, -- SKU图片
|
||||
|
||||
-- 状态
|
||||
is_active BOOLEAN DEFAULT true, -- 是否启用
|
||||
is_default BOOLEAN DEFAULT false, -- 是否默认SKU
|
||||
|
||||
-- 销售统计
|
||||
sale_count INTEGER DEFAULT 0, -- 销售数量
|
||||
|
||||
-- 时间
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
|
||||
-- 约束
|
||||
CONSTRAINT chk_sku_price_positive CHECK (price >= 0),
|
||||
CONSTRAINT chk_sku_stock_non_negative CHECK (stock_quantity >= 0)
|
||||
);
|
||||
|
||||
-- SKU表索引
|
||||
CREATE INDEX IF NOT EXISTS idx_mall_product_skus_product ON public.mall_product_skus(product_id, is_active);
|
||||
CREATE INDEX IF NOT EXISTS idx_mall_product_skus_code ON public.mall_product_skus(sku_code);
|
||||
CREATE INDEX IF NOT EXISTS idx_mall_product_skus_barcode ON public.mall_product_skus(barcode);
|
||||
CREATE INDEX IF NOT EXISTS idx_mall_product_skus_default ON public.mall_product_skus(product_id, is_default);
|
||||
CREATE INDEX IF NOT EXISTS idx_mall_product_skus_spec ON public.mall_product_skus USING GIN(specification_values);
|
||||
|
||||
COMMENT ON TABLE public.mall_product_skus IS '商品SKU表';
|
||||
|
||||
-- ===================================================================
|
||||
-- 3. 商品分类表
|
||||
-- ===================================================================
|
||||
|
||||
-- 商品分类表
|
||||
CREATE TABLE IF NOT EXISTS public.mall_categories (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
name VARCHAR(200) NOT NULL, -- 分类名称
|
||||
slug VARCHAR(200) UNIQUE, -- URL友好标识
|
||||
description TEXT, -- 分类描述
|
||||
|
||||
-- 层级关系
|
||||
parent_id UUID REFERENCES public.mall_categories(id),
|
||||
level INTEGER DEFAULT 0, -- 层级:0=顶级
|
||||
path TEXT, -- 路径:/1/2/3
|
||||
sort_order INTEGER DEFAULT 0, -- 排序
|
||||
|
||||
-- 显示信息
|
||||
icon_url TEXT, -- 分类图标
|
||||
banner_url TEXT, -- 分类横幅
|
||||
|
||||
-- 状态
|
||||
is_active BOOLEAN DEFAULT true, -- 是否启用
|
||||
is_featured BOOLEAN DEFAULT false, -- 是否精选
|
||||
|
||||
-- 统计
|
||||
product_count INTEGER DEFAULT 0, -- 商品数量
|
||||
|
||||
-- SEO
|
||||
seo_title VARCHAR(200),
|
||||
seo_description VARCHAR(500),
|
||||
seo_keywords TEXT[],
|
||||
|
||||
-- 时间
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
|
||||
-- 自定义属性
|
||||
attributes JSONB DEFAULT '{}'
|
||||
);
|
||||
|
||||
-- 分类表索引
|
||||
CREATE INDEX IF NOT EXISTS idx_mall_categories_parent ON public.mall_categories(parent_id, sort_order);
|
||||
CREATE INDEX IF NOT EXISTS idx_mall_categories_level ON public.mall_categories(level, sort_order);
|
||||
CREATE INDEX IF NOT EXISTS idx_mall_categories_active ON public.mall_categories(is_active, sort_order);
|
||||
CREATE INDEX IF NOT EXISTS idx_mall_categories_featured ON public.mall_categories(is_featured, sort_order);
|
||||
CREATE INDEX IF NOT EXISTS idx_mall_categories_slug ON public.mall_categories(slug);
|
||||
|
||||
COMMENT ON TABLE public.mall_categories IS '商品分类表';
|
||||
|
||||
-- ===================================================================
|
||||
-- 4. 商品品牌表
|
||||
-- ===================================================================
|
||||
|
||||
-- 商品品牌表
|
||||
CREATE TABLE IF NOT EXISTS public.mall_brands (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
name VARCHAR(200) NOT NULL UNIQUE, -- 品牌名称
|
||||
english_name VARCHAR(200), -- 英文名称
|
||||
slug VARCHAR(200) UNIQUE, -- URL友好标识
|
||||
description TEXT, -- 品牌描述
|
||||
|
||||
-- 品牌信息
|
||||
logo_url TEXT, -- 品牌Logo
|
||||
banner_url TEXT, -- 品牌横幅
|
||||
website_url TEXT, -- 官网地址
|
||||
origin_country VARCHAR(100), -- 品牌原产国
|
||||
founded_year INTEGER, -- 创立年份
|
||||
|
||||
-- 状态
|
||||
is_active BOOLEAN DEFAULT true, -- 是否启用
|
||||
is_featured BOOLEAN DEFAULT false, -- 是否精选
|
||||
|
||||
-- 统计
|
||||
product_count INTEGER DEFAULT 0, -- 商品数量
|
||||
|
||||
-- SEO
|
||||
seo_title VARCHAR(200),
|
||||
seo_description VARCHAR(500),
|
||||
seo_keywords TEXT[],
|
||||
|
||||
-- 时间
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
|
||||
-- 排序
|
||||
sort_order INTEGER DEFAULT 0
|
||||
);
|
||||
|
||||
-- 品牌表索引
|
||||
CREATE INDEX IF NOT EXISTS idx_mall_brands_active ON public.mall_brands(is_active, sort_order);
|
||||
CREATE INDEX IF NOT EXISTS idx_mall_brands_featured ON public.mall_brands(is_featured, sort_order);
|
||||
CREATE INDEX IF NOT EXISTS idx_mall_brands_slug ON public.mall_brands(slug);
|
||||
|
||||
COMMENT ON TABLE public.mall_brands IS '商品品牌表';
|
||||
|
||||
-- ===================================================================
|
||||
-- 5. 商品规格相关表
|
||||
-- ===================================================================
|
||||
|
||||
-- 规格名表(如:颜色、尺寸、款式等)
|
||||
CREATE TABLE IF NOT EXISTS public.mall_specifications (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
name VARCHAR(100) NOT NULL, -- 规格名称:颜色、尺寸等
|
||||
slug VARCHAR(100) UNIQUE, -- URL友好标识
|
||||
type VARCHAR(50) DEFAULT 'select', -- 类型:select/input/color/image
|
||||
sort_order INTEGER DEFAULT 0, -- 排序
|
||||
is_required BOOLEAN DEFAULT false, -- 是否必选
|
||||
is_active BOOLEAN DEFAULT true, -- 是否启用
|
||||
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- 规格值表(如:红色、蓝色、L、XL等)
|
||||
CREATE TABLE IF NOT EXISTS public.mall_specification_values (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
specification_id UUID NOT NULL REFERENCES public.mall_specifications(id) ON DELETE CASCADE,
|
||||
value VARCHAR(200) NOT NULL, -- 规格值:红色、L等
|
||||
color_code VARCHAR(20), -- 颜色代码(仅颜色规格)
|
||||
image_url TEXT, -- 规格值图片
|
||||
sort_order INTEGER DEFAULT 0, -- 排序
|
||||
is_active BOOLEAN DEFAULT true, -- 是否启用
|
||||
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
|
||||
UNIQUE(specification_id, value)
|
||||
);
|
||||
|
||||
-- 商品规格关联表
|
||||
CREATE TABLE IF NOT EXISTS public.mall_product_specifications (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
product_id UUID NOT NULL REFERENCES public.mall_products(id) ON DELETE CASCADE,
|
||||
specification_id UUID NOT NULL REFERENCES public.mall_specifications(id) ON DELETE CASCADE,
|
||||
is_required BOOLEAN DEFAULT false, -- 该商品的该规格是否必选
|
||||
sort_order INTEGER DEFAULT 0, -- 在该商品中的排序
|
||||
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
|
||||
UNIQUE(product_id, specification_id)
|
||||
);
|
||||
|
||||
-- 索引
|
||||
CREATE INDEX IF NOT EXISTS idx_mall_specifications_active ON public.mall_specifications(is_active, sort_order);
|
||||
CREATE INDEX IF NOT EXISTS idx_mall_specification_values_spec ON public.mall_specification_values(specification_id, is_active, sort_order);
|
||||
CREATE INDEX IF NOT EXISTS idx_mall_product_specifications_product ON public.mall_product_specifications(product_id, sort_order);
|
||||
|
||||
-- ===================================================================
|
||||
-- 6. 商品详情相关表
|
||||
-- ===================================================================
|
||||
|
||||
-- 商品详情内容表(富文本、图文混排)
|
||||
CREATE TABLE IF NOT EXISTS public.mall_product_details (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
product_id UUID NOT NULL REFERENCES public.mall_products(id) ON DELETE CASCADE,
|
||||
|
||||
-- 详情内容
|
||||
detail_type VARCHAR(50) DEFAULT 'rich_text', -- 类型:rich_text/markdown/html
|
||||
content TEXT, -- 详情内容
|
||||
images TEXT[], -- 详情图片
|
||||
|
||||
-- 显示控制
|
||||
section_title VARCHAR(200), -- 区块标题
|
||||
sort_order INTEGER DEFAULT 0, -- 排序
|
||||
is_active BOOLEAN DEFAULT true, -- 是否显示
|
||||
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- 商品参数表
|
||||
CREATE TABLE IF NOT EXISTS public.mall_product_attributes (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
product_id UUID NOT NULL REFERENCES public.mall_products(id) ON DELETE CASCADE,
|
||||
|
||||
-- 参数信息
|
||||
attribute_name VARCHAR(200) NOT NULL, -- 参数名称
|
||||
attribute_value TEXT NOT NULL, -- 参数值
|
||||
attribute_group VARCHAR(100), -- 参数分组
|
||||
|
||||
-- 显示控制
|
||||
sort_order INTEGER DEFAULT 0, -- 排序
|
||||
is_key_attribute BOOLEAN DEFAULT false, -- 是否关键参数
|
||||
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
|
||||
UNIQUE(product_id, attribute_name)
|
||||
);
|
||||
|
||||
-- 索引
|
||||
CREATE INDEX IF NOT EXISTS idx_mall_product_details_product ON public.mall_product_details(product_id, sort_order);
|
||||
CREATE INDEX IF NOT EXISTS idx_mall_product_attributes_product ON public.mall_product_attributes(product_id, attribute_group, sort_order);
|
||||
|
||||
-- ===================================================================
|
||||
-- 7. 视图和函数
|
||||
-- ===================================================================
|
||||
|
||||
-- 商品列表视图(包含完整信息)
|
||||
CREATE OR REPLACE VIEW public.vw_mall_products_full AS
|
||||
SELECT
|
||||
p.*,
|
||||
c.name as category_name,
|
||||
c.path as category_full_path,
|
||||
b.name as brand_name,
|
||||
b.logo_url as brand_logo_url,
|
||||
|
||||
-- SKU汇总信息
|
||||
(SELECT MIN(price) FROM public.mall_product_skus WHERE product_id = p.id AND is_active = true) as min_price,
|
||||
(SELECT MAX(price) FROM public.mall_product_skus WHERE product_id = p.id AND is_active = true) as max_price,
|
||||
(SELECT SUM(stock_quantity) FROM public.mall_product_skus WHERE product_id = p.id AND is_active = true) as total_stock,
|
||||
|
||||
-- 默认SKU信息
|
||||
default_sku.id as default_sku_id,
|
||||
default_sku.sku_code as default_sku_code,
|
||||
default_sku.price as default_price,
|
||||
default_sku.stock_quantity as default_stock
|
||||
|
||||
FROM public.mall_products p
|
||||
LEFT JOIN public.mall_categories c ON p.category_id = c.id
|
||||
LEFT JOIN public.mall_brands b ON p.brand_id = b.id
|
||||
LEFT JOIN public.mall_product_skus default_sku ON p.id = default_sku.product_id AND default_sku.is_default = true
|
||||
WHERE p.status != 'deleted';
|
||||
|
||||
COMMENT ON VIEW public.vw_mall_products_full IS '商品完整信息视图';
|
||||
|
||||
-- ===================================================================
|
||||
-- 8. 触发器(维护统计数据)
|
||||
-- ===================================================================
|
||||
|
||||
-- 更新商品SKU统计的触发器函数
|
||||
CREATE OR REPLACE FUNCTION public.update_product_sku_stats()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
-- 更新商品的库存统计
|
||||
UPDATE public.mall_products
|
||||
SET
|
||||
stock_quantity = (
|
||||
SELECT COALESCE(SUM(stock_quantity), 0)
|
||||
FROM public.mall_product_skus
|
||||
WHERE product_id = COALESCE(NEW.product_id, OLD.product_id) AND is_active = true
|
||||
),
|
||||
available_quantity = (
|
||||
SELECT COALESCE(SUM(available_quantity), 0)
|
||||
FROM public.mall_product_skus
|
||||
WHERE product_id = COALESCE(NEW.product_id, OLD.product_id) AND is_active = true
|
||||
),
|
||||
updated_at = NOW()
|
||||
WHERE id = COALESCE(NEW.product_id, OLD.product_id);
|
||||
|
||||
RETURN COALESCE(NEW, OLD);
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
-- 创建触发器
|
||||
DO $$
|
||||
BEGIN
|
||||
DROP TRIGGER IF EXISTS trigger_update_product_sku_stats ON public.mall_product_skus;
|
||||
CREATE TRIGGER trigger_update_product_sku_stats
|
||||
AFTER INSERT OR UPDATE OR DELETE ON public.mall_product_skus
|
||||
FOR EACH ROW EXECUTE FUNCTION public.update_product_sku_stats();
|
||||
END
|
||||
$$;
|
||||
|
||||
-- ===================================================================
|
||||
-- 9. 初始化数据
|
||||
-- ===================================================================
|
||||
|
||||
-- 插入基础商品分类
|
||||
INSERT INTO public.mall_categories (name, slug, level, sort_order) VALUES
|
||||
('服装鞋包', 'fashion', 0, 1),
|
||||
('数码家电', 'electronics', 0, 2),
|
||||
('食品生鲜', 'food', 0, 3),
|
||||
('家居日用', 'home', 0, 4),
|
||||
('美妆护肤', 'beauty', 0, 5),
|
||||
('运动户外', 'sports', 0, 6),
|
||||
('图书文娱', 'books', 0, 7),
|
||||
('医药保健', 'health', 0, 8)
|
||||
ON CONFLICT (slug) DO NOTHING;
|
||||
|
||||
-- 插入基础规格
|
||||
INSERT INTO public.mall_specifications (name, slug, type, sort_order) VALUES
|
||||
('颜色', 'color', 'color', 1),
|
||||
('尺寸', 'size', 'select', 2),
|
||||
('款式', 'style', 'select', 3),
|
||||
('容量', 'capacity', 'select', 4),
|
||||
('材质', 'material', 'select', 5)
|
||||
ON CONFLICT (slug) DO NOTHING;
|
||||
|
||||
-- 输出完成信息
|
||||
DO $$
|
||||
BEGIN
|
||||
RAISE NOTICE '商品管理数据库结构创建完成!';
|
||||
RAISE NOTICE '已创建以下核心表:';
|
||||
RAISE NOTICE '- mall_products: 商品基础信息';
|
||||
RAISE NOTICE '- mall_product_skus: 商品SKU';
|
||||
RAISE NOTICE '- mall_categories: 商品分类';
|
||||
RAISE NOTICE '- mall_brands: 商品品牌';
|
||||
RAISE NOTICE '- mall_specifications: 商品规格';
|
||||
RAISE NOTICE '可以开始添加商品数据了!';
|
||||
END
|
||||
$$;
|
||||
Reference in New Issue
Block a user