-- ============================================ -- 数据分析实时大屏 - 基础业务表结构(创建版 / Create-only) -- ============================================ -- 用途:创建业务核心表(orders, users, user_sessions, products, merchants 等) -- 特点: -- 1. 不做 DROP/DELETE/TRUNCATE(不清空数据) -- 2. 通过 IF NOT EXISTS + 系统表判断,实现可重复执行 -- 3. 与 analytics_* 表(ANALYTICS_DB_SCHEMA.sql)配套使用 -- ============================================ -- ============================================ -- 1. 表结构创建 -- ============================================ -- 1.1 商家表 CREATE TABLE IF NOT EXISTS merchants ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), shop_name VARCHAR(255) NOT NULL, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); COMMENT ON TABLE public.merchants IS '商家表'; COMMENT ON COLUMN public.merchants.id IS '商家ID'; COMMENT ON COLUMN public.merchants.shop_name IS '店铺名称'; COMMENT ON COLUMN public.merchants.created_at IS '创建时间'; -- 1.2 商品表 CREATE TABLE IF NOT EXISTS products ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), merchant_id UUID REFERENCES merchants(id) ON DELETE SET NULL, name VARCHAR(255) NOT NULL, price DECIMAL(10, 2) NOT NULL, sales INTEGER DEFAULT 0, status INTEGER DEFAULT 1, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); COMMENT ON TABLE public.products IS '商品表'; COMMENT ON COLUMN public.products.id IS '商品ID'; COMMENT ON COLUMN public.products.merchant_id IS '所属商家ID'; COMMENT ON COLUMN public.products.name IS '商品名称'; COMMENT ON COLUMN public.products.price IS '价格'; COMMENT ON COLUMN public.products.sales IS '销量'; COMMENT ON COLUMN public.products.status IS '状态(1:上架, 0:下架)'; COMMENT ON COLUMN public.products.created_at IS '创建时间'; -- 1.3 用户(统计兼容)表 CREATE TABLE IF NOT EXISTS users ( id UUID PRIMARY KEY, phone VARCHAR(20) UNIQUE, email VARCHAR(255), nickname VARCHAR(100), last_login_at TIMESTAMP WITH TIME ZONE, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); COMMENT ON TABLE public.users IS '用户(统计兼容表,不作为权威用户表)'; COMMENT ON COLUMN public.users.id IS '用户ID(建议与 auth.users.id/ak_users.id 对齐)'; COMMENT ON COLUMN public.users.phone IS '手机号(可选)'; COMMENT ON COLUMN public.users.email IS '邮箱(可选)'; COMMENT ON COLUMN public.users.nickname IS '昵称'; COMMENT ON COLUMN public.users.last_login_at IS '最后登录时间'; COMMENT ON COLUMN public.users.created_at IS '创建时间'; COMMENT ON COLUMN public.users.updated_at IS '更新时间'; -- 1.4 订单表 CREATE TABLE IF NOT EXISTS orders ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, merchant_id UUID REFERENCES merchants(id) ON DELETE SET NULL, total_amount DECIMAL(10, 2) NOT NULL DEFAULT 0.00, status INTEGER NOT NULL DEFAULT 0, payment_method VARCHAR(50), created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); COMMENT ON TABLE public.orders IS '订单表'; COMMENT ON COLUMN public.orders.id IS '订单ID'; COMMENT ON COLUMN public.orders.user_id IS '用户ID'; COMMENT ON COLUMN public.orders.merchant_id IS '商家ID'; COMMENT ON COLUMN public.orders.total_amount IS '订单总金额'; COMMENT ON COLUMN public.orders.status IS '订单状态(0:待支付, 1:已支付, 2:已完成, 3:已取消)'; COMMENT ON COLUMN public.orders.payment_method IS '支付方式'; COMMENT ON COLUMN public.orders.created_at IS '创建时间'; COMMENT ON COLUMN public.orders.updated_at IS '更新时间'; -- 1.5 订单商品关联表 CREATE TABLE IF NOT EXISTS order_items ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), order_id UUID NOT NULL REFERENCES orders(id) ON DELETE CASCADE, product_id UUID NOT NULL REFERENCES products(id) ON DELETE RESTRICT, quantity INTEGER NOT NULL DEFAULT 1, price DECIMAL(10, 2) NOT NULL, total_amount DECIMAL(10, 2) NOT NULL DEFAULT 0.00, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); COMMENT ON TABLE public.order_items IS '订单商品关联表'; COMMENT ON COLUMN public.order_items.id IS '主键'; COMMENT ON COLUMN public.order_items.order_id IS '订单ID'; COMMENT ON COLUMN public.order_items.product_id IS '商品ID'; COMMENT ON COLUMN public.order_items.quantity IS '数量'; COMMENT ON COLUMN public.order_items.price IS '单价'; COMMENT ON COLUMN public.order_items.total_amount IS '总价'; COMMENT ON COLUMN public.order_items.created_at IS '创建时间'; -- 1.6 用户会话表 CREATE TABLE IF NOT EXISTS user_sessions ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, session_token VARCHAR(255) UNIQUE, last_active_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), is_active BOOLEAN DEFAULT true, ip_address VARCHAR(45), user_agent TEXT, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); COMMENT ON TABLE public.user_sessions IS '用户会话表(用于在线用户统计)'; COMMENT ON COLUMN public.user_sessions.id IS '会话ID'; COMMENT ON COLUMN public.user_sessions.user_id IS '用户ID'; COMMENT ON COLUMN public.user_sessions.session_token IS '会话Token'; COMMENT ON COLUMN public.user_sessions.last_active_at IS '最后活跃时间'; COMMENT ON COLUMN public.user_sessions.is_active IS '是否活跃'; COMMENT ON COLUMN public.user_sessions.ip_address IS 'IP地址'; COMMENT ON COLUMN public.user_sessions.user_agent IS '用户代理'; COMMENT ON COLUMN public.user_sessions.created_at IS '创建时间'; COMMENT ON COLUMN public.user_sessions.updated_at IS '更新时间'; -- 1.7 访问日志表 CREATE TABLE IF NOT EXISTS page_views ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id UUID REFERENCES users(id) ON DELETE SET NULL, path VARCHAR(255), source VARCHAR(50) DEFAULT 'direct', referrer VARCHAR(255), ip_address VARCHAR(45), user_agent TEXT, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); COMMENT ON TABLE public.page_views IS '访问日志表(用于转化率分析)'; COMMENT ON COLUMN public.page_views.id IS '主键'; COMMENT ON COLUMN public.page_views.user_id IS '用户ID(可空,表示匿名访问)'; COMMENT ON COLUMN public.page_views.path IS '访问路径'; COMMENT ON COLUMN public.page_views.source IS '流量来源(direct/search/social/ad)'; COMMENT ON COLUMN public.page_views.referrer IS '来源页面'; COMMENT ON COLUMN public.page_views.ip_address IS 'IP地址'; COMMENT ON COLUMN public.page_views.user_agent IS '用户代理'; COMMENT ON COLUMN public.page_views.created_at IS '创建时间'; -- ============================================ -- 2. 索引创建 -- ============================================ -- orders CREATE INDEX IF NOT EXISTS idx_orders_created_at ON orders(created_at); CREATE INDEX IF NOT EXISTS idx_orders_status ON orders(status); CREATE INDEX IF NOT EXISTS idx_orders_user_id ON orders(user_id); CREATE INDEX IF NOT EXISTS idx_orders_created_at_status ON orders(created_at, status); -- user_sessions CREATE INDEX IF NOT EXISTS idx_user_sessions_last_active_at ON user_sessions(last_active_at); CREATE INDEX IF NOT EXISTS idx_user_sessions_is_active ON user_sessions(is_active); CREATE INDEX IF NOT EXISTS idx_user_sessions_user_id ON user_sessions(user_id); CREATE INDEX IF NOT EXISTS idx_user_sessions_created_at ON user_sessions(created_at); -- users CREATE INDEX IF NOT EXISTS idx_users_last_login_at ON users(last_login_at); -- order_items CREATE INDEX IF NOT EXISTS idx_order_items_order_id ON order_items(order_id); CREATE INDEX IF NOT EXISTS idx_order_items_product_id ON order_items(product_id); -- page_views CREATE INDEX IF NOT EXISTS idx_page_views_user_id ON page_views(user_id); CREATE INDEX IF NOT EXISTS idx_page_views_created_at ON page_views(created_at); CREATE INDEX IF NOT EXISTS idx_page_views_source ON page_views(source); -- ============================================ -- 3. 触发器函数和触发器 -- ============================================ CREATE OR REPLACE FUNCTION update_updated_at_column() RETURNS TRIGGER AS $$ BEGIN NEW.updated_at = NOW(); RETURN NEW; END; $$ LANGUAGE plpgsql; -- 为需要 updated_at 的表添加触发器 DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_trigger WHERE tgname = 'update_orders_updated_at') THEN EXECUTE 'CREATE TRIGGER update_orders_updated_at BEFORE UPDATE ON public.orders FOR EACH ROW EXECUTE FUNCTION public.update_updated_at_column()'; END IF; IF NOT EXISTS (SELECT 1 FROM pg_trigger WHERE tgname = 'update_user_sessions_updated_at') THEN EXECUTE 'CREATE TRIGGER update_user_sessions_updated_at BEFORE UPDATE ON public.user_sessions FOR EACH ROW EXECUTE FUNCTION public.update_updated_at_column()'; END IF; IF NOT EXISTS (SELECT 1 FROM pg_trigger WHERE tgname = 'update_users_updated_at') THEN EXECUTE 'CREATE TRIGGER update_users_updated_at BEFORE UPDATE ON public.users FOR EACH ROW EXECUTE FUNCTION public.update_updated_at_column()'; END IF; END $$; -- ============================================ -- 4. 行级安全策略(RLS) -- ============================================ -- 启用 RLS ALTER TABLE orders ENABLE ROW LEVEL SECURITY; ALTER TABLE user_sessions ENABLE ROW LEVEL SECURITY; ALTER TABLE users ENABLE ROW LEVEL SECURITY; ALTER TABLE products ENABLE ROW LEVEL SECURITY; ALTER TABLE merchants ENABLE ROW LEVEL SECURITY; ALTER TABLE order_items ENABLE ROW LEVEL SECURITY; ALTER TABLE page_views ENABLE ROW LEVEL SECURITY; -- orders: 用户只能查看和管理自己的订单 DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_policies WHERE schemaname='public' AND tablename='orders' AND policyname='Users can view own orders') THEN EXECUTE 'CREATE POLICY "Users can view own orders" ON public.orders FOR SELECT USING (auth.uid() = user_id)'; END IF; IF NOT EXISTS (SELECT 1 FROM pg_policies WHERE schemaname='public' AND tablename='orders' AND policyname='Users can insert own orders') THEN EXECUTE 'CREATE POLICY "Users can insert own orders" ON public.orders FOR INSERT WITH CHECK (auth.uid() = user_id)'; END IF; IF NOT EXISTS (SELECT 1 FROM pg_policies WHERE schemaname='public' AND tablename='orders' AND policyname='Users can update own orders') THEN EXECUTE 'CREATE POLICY "Users can update own orders" ON public.orders FOR UPDATE USING (auth.uid() = user_id)'; END IF; END $$; -- order_items: 用户只能查看自己订单的商品 DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_policies WHERE schemaname='public' AND tablename='order_items' AND policyname='Users can view own order items') THEN EXECUTE 'CREATE POLICY "Users can view own order items" ON public.order_items FOR SELECT USING (EXISTS (SELECT 1 FROM public.orders WHERE public.orders.id = public.order_items.order_id AND public.orders.user_id = auth.uid()))'; END IF; IF NOT EXISTS (SELECT 1 FROM pg_policies WHERE schemaname='public' AND tablename='order_items' AND policyname='Users can insert own order items') THEN EXECUTE 'CREATE POLICY "Users can insert own order items" ON public.order_items FOR INSERT WITH CHECK (EXISTS (SELECT 1 FROM public.orders WHERE public.orders.id = public.order_items.order_id AND public.orders.user_id = auth.uid()))'; END IF; END $$; -- user_sessions: 用户只能查看和管理自己的会话 DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_policies WHERE schemaname='public' AND tablename='user_sessions' AND policyname='Users can view own sessions') THEN EXECUTE 'CREATE POLICY "Users can view own sessions" ON public.user_sessions FOR SELECT USING (auth.uid() = user_id)'; END IF; IF NOT EXISTS (SELECT 1 FROM pg_policies WHERE schemaname='public' AND tablename='user_sessions' AND policyname='Users can insert own sessions') THEN EXECUTE 'CREATE POLICY "Users can insert own sessions" ON public.user_sessions FOR INSERT WITH CHECK (auth.uid() = user_id)'; END IF; IF NOT EXISTS (SELECT 1 FROM pg_policies WHERE schemaname='public' AND tablename='user_sessions' AND policyname='Users can update own sessions') THEN EXECUTE 'CREATE POLICY "Users can update own sessions" ON public.user_sessions FOR UPDATE USING (auth.uid() = user_id)'; END IF; IF NOT EXISTS (SELECT 1 FROM pg_policies WHERE schemaname='public' AND tablename='user_sessions' AND policyname='Users can delete own sessions') THEN EXECUTE 'CREATE POLICY "Users can delete own sessions" ON public.user_sessions FOR DELETE USING (auth.uid() = user_id)'; END IF; END $$; -- users: 用户只能查看和管理自己的记录 DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_policies WHERE schemaname='public' AND tablename='users' AND policyname='Users can view own profile') THEN EXECUTE 'CREATE POLICY "Users can view own profile" ON public.users FOR SELECT USING (auth.uid() = id)'; END IF; IF NOT EXISTS (SELECT 1 FROM pg_policies WHERE schemaname='public' AND tablename='users' AND policyname='Users can insert own profile') THEN EXECUTE 'CREATE POLICY "Users can insert own profile" ON public.users FOR INSERT WITH CHECK (auth.uid() = id)'; END IF; IF NOT EXISTS (SELECT 1 FROM pg_policies WHERE schemaname='public' AND tablename='users' AND policyname='Users can update own profile') THEN EXECUTE 'CREATE POLICY "Users can update own profile" ON public.users FOR UPDATE USING (auth.uid() = id)'; END IF; END $$; -- products: 任何人可读,认证用户可管理(简化策略) DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_policies WHERE schemaname='public' AND tablename='products' AND policyname='Anyone can view products') THEN EXECUTE 'CREATE POLICY "Anyone can view products" ON public.products FOR SELECT USING (true)'; END IF; IF NOT EXISTS (SELECT 1 FROM pg_policies WHERE schemaname='public' AND tablename='products' AND policyname='Authenticated can manage products') THEN EXECUTE 'CREATE POLICY "Authenticated can manage products" ON public.products FOR ALL USING (auth.role() = ''authenticated'')'; END IF; END $$; -- merchants: 任何人可读,认证用户可管理(简化策略) DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_policies WHERE schemaname='public' AND tablename='merchants' AND policyname='Anyone can view merchants') THEN EXECUTE 'CREATE POLICY "Anyone can view merchants" ON public.merchants FOR SELECT USING (true)'; END IF; IF NOT EXISTS (SELECT 1 FROM pg_policies WHERE schemaname='public' AND tablename='merchants' AND policyname='Authenticated can manage merchants') THEN EXECUTE 'CREATE POLICY "Authenticated can manage merchants" ON public.merchants FOR ALL USING (auth.role() = ''authenticated'')'; END IF; END $$; -- page_views: 任何人可插入,用户只能读自己的记录 DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_policies WHERE schemaname='public' AND tablename='page_views' AND policyname='Anyone can insert page views') THEN EXECUTE 'CREATE POLICY "Anyone can insert page views" ON public.page_views FOR INSERT WITH CHECK (true)'; END IF; IF NOT EXISTS (SELECT 1 FROM pg_policies WHERE schemaname='public' AND tablename='page_views' AND policyname='Users can view own page views') THEN EXECUTE 'CREATE POLICY "Users can view own page views" ON public.page_views FOR SELECT USING (auth.uid() = user_id)'; END IF; END $$;