-- =================================================================== -- 电商商城商品管理数据库设计 -- 基于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 $$;