-- ===================================================================================== -- 商城系统数据库迁移脚本 (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 $$;