consumer模块完成95%,在和商家端对接聊天购物闭环
This commit is contained in:
29
sql/create_shop_follows.sql
Normal file
29
sql/create_shop_follows.sql
Normal file
@@ -0,0 +1,29 @@
|
||||
-- create_shop_follows.sql
|
||||
-- Run this in Supabase SQL Editor
|
||||
|
||||
CREATE TABLE IF NOT EXISTS public.ml_shop_follows (
|
||||
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
|
||||
user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
|
||||
shop_id UUID NOT NULL REFERENCES public.ml_shops(id) ON DELETE CASCADE, -- Assuming ml_shops.id is UUID
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()) NOT NULL,
|
||||
UNIQUE(user_id, shop_id)
|
||||
);
|
||||
|
||||
-- Enable RLS
|
||||
ALTER TABLE public.ml_shop_follows ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
-- Policies
|
||||
CREATE POLICY "Users can view their own follows"
|
||||
ON public.ml_shop_follows FOR SELECT
|
||||
USING (auth.uid() = user_id);
|
||||
|
||||
CREATE POLICY "Users can create their own follows"
|
||||
ON public.ml_shop_follows FOR INSERT
|
||||
WITH CHECK (auth.uid() = user_id);
|
||||
|
||||
CREATE POLICY "Users can delete their own follows"
|
||||
ON public.ml_shop_follows FOR DELETE
|
||||
USING (auth.uid() = user_id);
|
||||
|
||||
-- Optional: View count on shops
|
||||
-- You might want to add follower_count to ml_shops and use triggers to update it.
|
||||
27
sql/fix_chat_rls.sql
Normal file
27
sql/fix_chat_rls.sql
Normal file
@@ -0,0 +1,27 @@
|
||||
-- Enable RLS permissions for Chat Messages
|
||||
-- Previously only SELECT was allowed, blocking USERS from sending messages (INSERT)
|
||||
|
||||
-- 1. Policy for INSERT (Sending messages)
|
||||
-- User can insert if they are the sender (linked via ak_users)
|
||||
DROP POLICY IF EXISTS ml_chat_messages_insert_policy ON public.ml_chat_messages;
|
||||
CREATE POLICY ml_chat_messages_insert_policy ON public.ml_chat_messages
|
||||
FOR INSERT WITH CHECK (
|
||||
auth.uid() IN (
|
||||
SELECT auth_id FROM public.ak_users WHERE id = sender_id
|
||||
)
|
||||
);
|
||||
|
||||
-- 2. Policy for UPDATE (Marking as read)
|
||||
-- Sender or Receiver can update (e.g. mark as read)
|
||||
DROP POLICY IF EXISTS ml_chat_messages_update_policy ON public.ml_chat_messages;
|
||||
CREATE POLICY ml_chat_messages_update_policy ON public.ml_chat_messages
|
||||
FOR UPDATE USING (
|
||||
auth.uid() IN (
|
||||
SELECT auth_id FROM public.ak_users WHERE id IN (sender_id, receiver_id)
|
||||
)
|
||||
);
|
||||
|
||||
-- 3. Ensure SELECT policy is also correct (existing one is complex, this is a simpler backup if needed)
|
||||
-- (We trust the existing select policy if it exists, but making sure)
|
||||
-- The existing policy:
|
||||
-- auth.uid() IN (SELECT auth_id FROM ak_users WHERE id IN (sender_id, receiver_id))
|
||||
52
sql/fix_shop_product_counts.sql
Normal file
52
sql/fix_shop_product_counts.sql
Normal file
@@ -0,0 +1,52 @@
|
||||
-- 1. Immediate Fix: Update all shop counts based on existing products
|
||||
-- Recalculate based on merchant_id link
|
||||
UPDATE public.ml_shops s
|
||||
SET product_count = (
|
||||
SELECT count(*)
|
||||
FROM public.ml_products p
|
||||
WHERE p.merchant_id = s.merchant_id
|
||||
AND p.status = 1
|
||||
);
|
||||
|
||||
-- 2. Permanent Fix: Create a Trigger to keep it updated automatically
|
||||
-- This ensures when you add/remove products in the future, the shop count updates
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.update_shop_product_count_trigger()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
-- Update count for the merchant associated with the NEW product (on Insert/Update)
|
||||
IF (TG_OP = 'INSERT' OR TG_OP = 'UPDATE') THEN
|
||||
UPDATE public.ml_shops
|
||||
SET product_count = (
|
||||
SELECT count(*)
|
||||
FROM public.ml_products
|
||||
WHERE merchant_id = NEW.merchant_id
|
||||
AND status = 1
|
||||
)
|
||||
WHERE merchant_id = NEW.merchant_id;
|
||||
END IF;
|
||||
|
||||
-- Update count for the merchant associated with the OLD product (on Delete/Update)
|
||||
IF (TG_OP = 'DELETE' OR (TG_OP = 'UPDATE' AND OLD.merchant_id IS DISTINCT FROM NEW.merchant_id)) THEN
|
||||
UPDATE public.ml_shops
|
||||
SET product_count = (
|
||||
SELECT count(*)
|
||||
FROM public.ml_products
|
||||
WHERE merchant_id = OLD.merchant_id
|
||||
AND status = 1
|
||||
)
|
||||
WHERE merchant_id = OLD.merchant_id;
|
||||
END IF;
|
||||
|
||||
RETURN NULL;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql SECURITY DEFINER;
|
||||
|
||||
-- Drop trigger if exists to avoid error on multiple runs
|
||||
DROP TRIGGER IF EXISTS on_product_change_update_shop_count ON public.ml_products;
|
||||
|
||||
-- Create the trigger
|
||||
CREATE TRIGGER on_product_change_update_shop_count
|
||||
AFTER INSERT OR UPDATE OR DELETE ON public.ml_products
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION public.update_shop_product_count_trigger();
|
||||
34
sql/fix_shop_visibility.sql
Normal file
34
sql/fix_shop_visibility.sql
Normal file
@@ -0,0 +1,34 @@
|
||||
-- Fix permissions and RLS for ml_shops to make it searchable
|
||||
-- Run this in Supabase SQL Editor
|
||||
|
||||
-- 1. Grant access to postgres/anon/authenticated roles (Standard Supabase setup)
|
||||
GRANT SELECT ON public.ml_shops TO anon, authenticated, service_role;
|
||||
|
||||
-- 2. Enable RLS
|
||||
ALTER TABLE public.ml_shops ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
-- 3. Create policies
|
||||
-- Drop existing potential policies to avoid conflicts
|
||||
DROP POLICY IF EXISTS "Public can view active shops" ON public.ml_shops;
|
||||
DROP POLICY IF EXISTS "Merchants can view own shop" ON public.ml_shops;
|
||||
|
||||
-- Policy: Everyone can see active shops
|
||||
CREATE POLICY "Public can view active shops"
|
||||
ON public.ml_shops
|
||||
FOR SELECT
|
||||
USING (status = 1);
|
||||
|
||||
-- Policy: Merchants can see and edit their own shop
|
||||
CREATE POLICY "Merchants can view own shop"
|
||||
ON public.ml_shops
|
||||
FOR ALL
|
||||
USING (auth.uid() = merchant_id);
|
||||
|
||||
-- 4. Update product_count for the test shop (and others)
|
||||
-- This ensures the sorting works as expected
|
||||
UPDATE public.ml_shops s
|
||||
SET product_count = (
|
||||
SELECT count(*)
|
||||
FROM public.ml_products p
|
||||
WHERE p.merchant_id = s.merchant_id AND p.status = 1
|
||||
);
|
||||
128
sql/seed_test2_products.sql
Normal file
128
sql/seed_test2_products.sql
Normal file
@@ -0,0 +1,128 @@
|
||||
-- Seed data for test2@mall.com
|
||||
-- Adds a shop, products, and SKUs for this user.
|
||||
|
||||
DO $$
|
||||
DECLARE
|
||||
v_user_email TEXT := 'test2@mall.com';
|
||||
v_user_id UUID;
|
||||
v_category_id UUID;
|
||||
v_product_id UUID;
|
||||
v_shop_id UUID;
|
||||
BEGIN
|
||||
-- 1. Get User ID from auth.users
|
||||
SELECT id INTO v_user_id FROM auth.users WHERE email = v_user_email LIMIT 1;
|
||||
|
||||
IF v_user_id IS NULL THEN
|
||||
RAISE EXCEPTION 'User % not found in auth.users', v_user_email;
|
||||
END IF;
|
||||
|
||||
-- 2. Ensure user exists in public.ak_users (if table exists and is required for FK)
|
||||
-- Try to insert if not exists (upsert-like behavior relies on ID match)
|
||||
-- Assuming ak_users is just a profile table using the same ID
|
||||
INSERT INTO public.ak_users (id, email, username, nickname, role)
|
||||
VALUES (v_user_id, v_user_email, 'test2', 'Test User 2', 'merchant')
|
||||
ON CONFLICT (id) DO NOTHING;
|
||||
|
||||
-- 3. Ensure Shop exists
|
||||
INSERT INTO public.ml_shops (merchant_id, shop_name, status)
|
||||
VALUES (v_user_id, 'Test Shop 2 Official', 1)
|
||||
ON CONFLICT (merchant_id) DO UPDATE SET shop_name = 'Test Shop 2 Official'
|
||||
RETURNING id INTO v_shop_id;
|
||||
|
||||
RAISE NOTICE 'Shop ID: %', v_shop_id;
|
||||
|
||||
-- 4. Get a Category (Use an existing one or create one)
|
||||
SELECT id INTO v_category_id FROM public.ml_categories LIMIT 1;
|
||||
|
||||
IF v_category_id IS NULL THEN
|
||||
-- Create a dummy category if none exists
|
||||
INSERT INTO public.ml_categories (name, level, sort_order)
|
||||
VALUES ('Electronics', 1, 1)
|
||||
RETURNING id INTO v_category_id;
|
||||
END IF;
|
||||
|
||||
RAISE NOTICE 'Category ID: %', v_category_id;
|
||||
|
||||
-- 5. Insert Product 1: Wireless Headset (No attributes, simple product)
|
||||
-- Note: product_code must be unique. Using random suffix or timestamp.
|
||||
INSERT INTO public.ml_products (
|
||||
merchant_id,
|
||||
category_id,
|
||||
product_code,
|
||||
name,
|
||||
subtitle,
|
||||
main_image_url,
|
||||
base_price,
|
||||
total_stock,
|
||||
status,
|
||||
attributes
|
||||
)
|
||||
VALUES (
|
||||
v_user_id,
|
||||
v_category_id,
|
||||
'TEST_P_001_' || floor(random() * 1000)::text,
|
||||
'Premium Wireless Headset',
|
||||
'Noise cancelling, 20h battery life',
|
||||
'https://picsum.photos/400/400?random=101',
|
||||
199.00,
|
||||
100,
|
||||
1, -- On Sale
|
||||
'{"Brand": "AudioTech", "Model": "WH-1000"}'::jsonb
|
||||
)
|
||||
RETURNING id INTO v_product_id;
|
||||
|
||||
-- Insert SKUs for Product 1 (Black/White)
|
||||
INSERT INTO public.ml_product_skus (product_id, sku_code, specifications, price, stock, image_url)
|
||||
VALUES
|
||||
(v_product_id, 'SKU_001_BLK_' || floor(random()*1000), '{"Color": "Black"}', 199.00, 50, 'https://picsum.photos/400/400?random=102'),
|
||||
(v_product_id, 'SKU_001_WHT_' || floor(random()*1000), '{"Color": "White"}', 199.00, 50, 'https://picsum.photos/400/400?random=103');
|
||||
|
||||
|
||||
-- 6. Insert Product 2: Mechanical Keyboard (Complex attributes)
|
||||
INSERT INTO public.ml_products (
|
||||
merchant_id, category_id, product_code, name, subtitle, main_image_url, base_price, total_stock, status, attributes
|
||||
)
|
||||
VALUES (
|
||||
v_user_id,
|
||||
v_category_id,
|
||||
'TEST_P_002_' || floor(random() * 1000)::text,
|
||||
'RGB Mechanical Keyboard',
|
||||
'Blue switches, hot-swappable',
|
||||
'https://picsum.photos/400/400?random=201',
|
||||
89.00,
|
||||
200,
|
||||
1,
|
||||
'{"Brand": "KeyMaster", "Material": "Aluminum", "Layout": "75%"}'::jsonb
|
||||
)
|
||||
RETURNING id INTO v_product_id;
|
||||
|
||||
-- Insert SKUs for Product 2
|
||||
INSERT INTO public.ml_product_skus (product_id, sku_code, specifications, price, stock)
|
||||
VALUES
|
||||
(v_product_id, 'SKU_002_BLU_' || floor(random()*1000), '{"Switch": "Blue", "Color": "Black"}', 89.00, 100),
|
||||
(v_product_id, 'SKU_002_RED_' || floor(random()*1000), '{"Switch": "Red", "Color": "White"}', 99.00, 100);
|
||||
|
||||
-- 7. Insert Product 3: Simple Mouse (Minimal attributes)
|
||||
INSERT INTO public.ml_products (
|
||||
merchant_id, category_id, product_code, name, subtitle, main_image_url, base_price, total_stock, status, attributes
|
||||
)
|
||||
VALUES (
|
||||
v_user_id,
|
||||
v_category_id,
|
||||
'TEST_P_003_' || floor(random() * 1000)::text,
|
||||
'Ergonomic Mouse',
|
||||
'Vertical design for health',
|
||||
'https://picsum.photos/400/400?random=301',
|
||||
29.99,
|
||||
500,
|
||||
1,
|
||||
'{"Brand": "ErgoLife"}'::jsonb
|
||||
)
|
||||
RETURNING id INTO v_product_id;
|
||||
|
||||
-- Insert Default SKU
|
||||
INSERT INTO public.ml_product_skus (product_id, sku_code, specifications, price, stock)
|
||||
VALUES
|
||||
(v_product_id, 'SKU_003_STD_' || floor(random()*1000), '{}', 29.99, 500);
|
||||
|
||||
END $$;
|
||||
Reference in New Issue
Block a user