mirror of
https://github.com/pezkuwichain/pwap.git
synced 2026-04-22 21:47:56 +00:00
df58d26893
- Add merchant tier system (Lite/Super/Diamond) with tier badges - Add advanced order filters (token, fiat, payment method, amount range) - Add merchant dashboard with stats, ads management, tier upgrade - Add fraud prevention system with risk scoring and trade limits - Rename migrations to timestamp format for Supabase CLI compatibility - Add new migrations: phase2_phase3_tables, fraud_prevention, merchant_system
567 lines
18 KiB
PL/PgSQL
567 lines
18 KiB
PL/PgSQL
-- =====================================================
|
|
-- P2P FIAT SYSTEM - PHASE 2 & 3 ADDITIONAL TABLES
|
|
-- Messages, Ratings, Notifications, Evidence, Fraud Reports
|
|
-- =====================================================
|
|
|
|
-- =====================================================
|
|
-- P2P MESSAGES TABLE (Phase 2 - Chat System)
|
|
-- =====================================================
|
|
CREATE TABLE IF NOT EXISTS public.p2p_messages (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
trade_id UUID NOT NULL REFERENCES public.p2p_fiat_trades(id) ON DELETE CASCADE,
|
|
sender_id UUID NOT NULL REFERENCES auth.users(id),
|
|
|
|
-- Message content
|
|
message TEXT NOT NULL CHECK (LENGTH(message) > 0 AND LENGTH(message) <= 2000),
|
|
message_type VARCHAR(20) DEFAULT 'text' CHECK (message_type IN ('text', 'image', 'system')),
|
|
attachment_url TEXT, -- Supabase Storage URL
|
|
|
|
-- Read status
|
|
is_read BOOLEAN DEFAULT FALSE,
|
|
read_at TIMESTAMPTZ,
|
|
|
|
-- Metadata
|
|
metadata JSONB DEFAULT '{}',
|
|
|
|
-- Timestamps
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ DEFAULT NOW()
|
|
);
|
|
|
|
-- Indexes for p2p_messages
|
|
CREATE INDEX idx_p2p_messages_trade ON public.p2p_messages(trade_id, created_at DESC);
|
|
CREATE INDEX idx_p2p_messages_sender ON public.p2p_messages(sender_id);
|
|
CREATE INDEX idx_p2p_messages_unread ON public.p2p_messages(trade_id, is_read) WHERE is_read = false;
|
|
|
|
-- Enable RLS
|
|
ALTER TABLE public.p2p_messages ENABLE ROW LEVEL SECURITY;
|
|
|
|
-- Policy: Only trade participants can read/write messages
|
|
CREATE POLICY "p2p_messages_trade_participants" ON public.p2p_messages
|
|
FOR ALL USING (
|
|
EXISTS (
|
|
SELECT 1 FROM public.p2p_fiat_trades t
|
|
WHERE t.id = trade_id
|
|
AND (t.seller_id = auth.uid() OR t.buyer_id = auth.uid())
|
|
)
|
|
);
|
|
|
|
-- =====================================================
|
|
-- P2P RATINGS TABLE (Phase 2 - Trust System)
|
|
-- =====================================================
|
|
CREATE TABLE IF NOT EXISTS public.p2p_ratings (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
trade_id UUID NOT NULL REFERENCES public.p2p_fiat_trades(id) ON DELETE CASCADE,
|
|
rater_id UUID NOT NULL REFERENCES auth.users(id),
|
|
rated_id UUID NOT NULL REFERENCES auth.users(id),
|
|
|
|
-- Rating details
|
|
rating INT NOT NULL CHECK (rating BETWEEN 1 AND 5),
|
|
review TEXT CHECK (LENGTH(review) <= 500),
|
|
|
|
-- Rating aspects (optional breakdown)
|
|
communication_rating INT CHECK (communication_rating BETWEEN 1 AND 5),
|
|
speed_rating INT CHECK (speed_rating BETWEEN 1 AND 5),
|
|
|
|
-- Timestamps
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
|
|
-- Constraints
|
|
CONSTRAINT unique_rating_per_trade UNIQUE(trade_id, rater_id),
|
|
CONSTRAINT cannot_rate_self CHECK (rater_id != rated_id)
|
|
);
|
|
|
|
-- Indexes for p2p_ratings
|
|
CREATE INDEX idx_p2p_ratings_trade ON public.p2p_ratings(trade_id);
|
|
CREATE INDEX idx_p2p_ratings_rated ON public.p2p_ratings(rated_id, created_at DESC);
|
|
CREATE INDEX idx_p2p_ratings_rater ON public.p2p_ratings(rater_id);
|
|
|
|
-- Enable RLS
|
|
ALTER TABLE public.p2p_ratings ENABLE ROW LEVEL SECURITY;
|
|
|
|
-- Policy: Public read (for reputation display)
|
|
CREATE POLICY "p2p_ratings_public_read" ON public.p2p_ratings
|
|
FOR SELECT USING (true);
|
|
|
|
-- Policy: Only trade participants can insert ratings
|
|
CREATE POLICY "p2p_ratings_trade_participants_insert" ON public.p2p_ratings
|
|
FOR INSERT WITH CHECK (
|
|
rater_id = auth.uid() AND
|
|
EXISTS (
|
|
SELECT 1 FROM public.p2p_fiat_trades t
|
|
WHERE t.id = trade_id
|
|
AND t.status = 'completed'
|
|
AND (t.seller_id = auth.uid() OR t.buyer_id = auth.uid())
|
|
)
|
|
);
|
|
|
|
-- =====================================================
|
|
-- P2P NOTIFICATIONS TABLE (Phase 2 - Real-time Alerts)
|
|
-- =====================================================
|
|
CREATE TABLE IF NOT EXISTS public.p2p_notifications (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
|
|
|
|
-- Notification content
|
|
type VARCHAR(50) NOT NULL CHECK (type IN (
|
|
'new_order', 'payment_sent', 'payment_confirmed', 'trade_cancelled',
|
|
'dispute_opened', 'dispute_resolved', 'new_message', 'rating_received',
|
|
'offer_matched', 'trade_reminder', 'system'
|
|
)),
|
|
title TEXT NOT NULL,
|
|
message TEXT,
|
|
|
|
-- Reference
|
|
reference_type VARCHAR(20) CHECK (reference_type IN ('trade', 'offer', 'dispute', 'message')),
|
|
reference_id UUID,
|
|
|
|
-- Status
|
|
is_read BOOLEAN DEFAULT FALSE,
|
|
read_at TIMESTAMPTZ,
|
|
|
|
-- Action URL (frontend route)
|
|
action_url TEXT,
|
|
|
|
-- Metadata
|
|
metadata JSONB DEFAULT '{}',
|
|
|
|
-- Timestamps
|
|
created_at TIMESTAMPTZ DEFAULT NOW()
|
|
);
|
|
|
|
-- Indexes for p2p_notifications
|
|
CREATE INDEX idx_p2p_notifications_user ON public.p2p_notifications(user_id, created_at DESC);
|
|
CREATE INDEX idx_p2p_notifications_unread ON public.p2p_notifications(user_id, is_read) WHERE is_read = false;
|
|
CREATE INDEX idx_p2p_notifications_type ON public.p2p_notifications(user_id, type);
|
|
|
|
-- Enable RLS
|
|
ALTER TABLE public.p2p_notifications ENABLE ROW LEVEL SECURITY;
|
|
|
|
-- Policy: Users can only see their own notifications
|
|
CREATE POLICY "p2p_notifications_own" ON public.p2p_notifications
|
|
FOR ALL USING (user_id = auth.uid());
|
|
|
|
-- =====================================================
|
|
-- P2P DISPUTE EVIDENCE TABLE (Phase 3 - Dispute System)
|
|
-- =====================================================
|
|
CREATE TABLE IF NOT EXISTS public.p2p_dispute_evidence (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
dispute_id UUID NOT NULL REFERENCES public.p2p_fiat_disputes(id) ON DELETE CASCADE,
|
|
uploaded_by UUID NOT NULL REFERENCES auth.users(id),
|
|
|
|
-- Evidence details
|
|
evidence_type VARCHAR(30) NOT NULL CHECK (evidence_type IN (
|
|
'screenshot', 'receipt', 'bank_statement', 'chat_log',
|
|
'transaction_proof', 'identity_doc', 'other'
|
|
)),
|
|
file_url TEXT NOT NULL,
|
|
file_name TEXT,
|
|
file_size INT, -- bytes
|
|
mime_type TEXT,
|
|
|
|
-- Description
|
|
description TEXT CHECK (LENGTH(description) <= 1000),
|
|
|
|
-- Admin review
|
|
reviewed_by UUID REFERENCES auth.users(id),
|
|
reviewed_at TIMESTAMPTZ,
|
|
review_notes TEXT,
|
|
is_valid BOOLEAN,
|
|
|
|
-- Timestamps
|
|
created_at TIMESTAMPTZ DEFAULT NOW()
|
|
);
|
|
|
|
-- Indexes for p2p_dispute_evidence
|
|
CREATE INDEX idx_p2p_evidence_dispute ON public.p2p_dispute_evidence(dispute_id, created_at);
|
|
CREATE INDEX idx_p2p_evidence_uploader ON public.p2p_dispute_evidence(uploaded_by);
|
|
|
|
-- Enable RLS
|
|
ALTER TABLE public.p2p_dispute_evidence ENABLE ROW LEVEL SECURITY;
|
|
|
|
-- Policy: Dispute parties can view evidence
|
|
CREATE POLICY "p2p_evidence_parties_read" ON public.p2p_dispute_evidence
|
|
FOR SELECT USING (
|
|
EXISTS (
|
|
SELECT 1 FROM public.p2p_fiat_disputes d
|
|
JOIN public.p2p_fiat_trades t ON t.id = d.trade_id
|
|
WHERE d.id = dispute_id
|
|
AND (t.seller_id = auth.uid() OR t.buyer_id = auth.uid() OR d.opened_by = auth.uid())
|
|
)
|
|
);
|
|
|
|
-- Policy: Dispute parties can upload evidence
|
|
CREATE POLICY "p2p_evidence_parties_insert" ON public.p2p_dispute_evidence
|
|
FOR INSERT WITH CHECK (
|
|
uploaded_by = auth.uid() AND
|
|
EXISTS (
|
|
SELECT 1 FROM public.p2p_fiat_disputes d
|
|
JOIN public.p2p_fiat_trades t ON t.id = d.trade_id
|
|
WHERE d.id = dispute_id
|
|
AND d.status IN ('open', 'under_review')
|
|
AND (t.seller_id = auth.uid() OR t.buyer_id = auth.uid())
|
|
)
|
|
);
|
|
|
|
-- Policy: Admins can view all evidence
|
|
CREATE POLICY "p2p_evidence_admin_read" ON public.p2p_dispute_evidence
|
|
FOR SELECT USING (
|
|
EXISTS (
|
|
SELECT 1 FROM public.admin_roles
|
|
WHERE user_id = auth.uid() AND role IN ('moderator', 'admin')
|
|
)
|
|
);
|
|
|
|
-- Policy: Admins can update evidence (for review)
|
|
CREATE POLICY "p2p_evidence_admin_update" ON public.p2p_dispute_evidence
|
|
FOR UPDATE USING (
|
|
EXISTS (
|
|
SELECT 1 FROM public.admin_roles
|
|
WHERE user_id = auth.uid() AND role IN ('moderator', 'admin')
|
|
)
|
|
);
|
|
|
|
-- =====================================================
|
|
-- P2P FRAUD REPORTS TABLE (Phase 3 - Security)
|
|
-- =====================================================
|
|
CREATE TABLE IF NOT EXISTS public.p2p_fraud_reports (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
reporter_id UUID NOT NULL REFERENCES auth.users(id),
|
|
reported_user_id UUID NOT NULL REFERENCES auth.users(id),
|
|
|
|
-- Report details
|
|
trade_id UUID REFERENCES public.p2p_fiat_trades(id),
|
|
reason VARCHAR(50) NOT NULL CHECK (reason IN (
|
|
'fake_payment', 'fake_proof', 'scam_attempt', 'harassment',
|
|
'money_laundering', 'identity_fraud', 'multiple_accounts', 'other'
|
|
)),
|
|
description TEXT NOT NULL CHECK (LENGTH(description) >= 20 AND LENGTH(description) <= 2000),
|
|
evidence_urls TEXT[] DEFAULT '{}',
|
|
|
|
-- Investigation
|
|
status VARCHAR(20) DEFAULT 'pending' CHECK (status IN (
|
|
'pending', 'investigating', 'confirmed', 'dismissed', 'escalated'
|
|
)),
|
|
assigned_to UUID REFERENCES auth.users(id),
|
|
assigned_at TIMESTAMPTZ,
|
|
|
|
-- Resolution
|
|
resolution TEXT,
|
|
resolution_notes TEXT,
|
|
resolved_by UUID REFERENCES auth.users(id),
|
|
resolved_at TIMESTAMPTZ,
|
|
|
|
-- Action taken
|
|
action_taken VARCHAR(30) CHECK (action_taken IN (
|
|
'warning_issued', 'temporary_ban', 'permanent_ban',
|
|
'trade_restricted', 'no_action', 'referred_to_authorities'
|
|
)),
|
|
|
|
-- Timestamps
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
|
|
|
-- Constraints
|
|
CONSTRAINT cannot_report_self CHECK (reporter_id != reported_user_id)
|
|
);
|
|
|
|
-- Indexes for p2p_fraud_reports
|
|
CREATE INDEX idx_p2p_fraud_reporter ON public.p2p_fraud_reports(reporter_id);
|
|
CREATE INDEX idx_p2p_fraud_reported ON public.p2p_fraud_reports(reported_user_id);
|
|
CREATE INDEX idx_p2p_fraud_status ON public.p2p_fraud_reports(status) WHERE status IN ('pending', 'investigating');
|
|
CREATE INDEX idx_p2p_fraud_trade ON public.p2p_fraud_reports(trade_id) WHERE trade_id IS NOT NULL;
|
|
|
|
-- Enable RLS
|
|
ALTER TABLE public.p2p_fraud_reports ENABLE ROW LEVEL SECURITY;
|
|
|
|
-- Policy: Users can view their own reports
|
|
CREATE POLICY "p2p_fraud_own_reports" ON public.p2p_fraud_reports
|
|
FOR SELECT USING (reporter_id = auth.uid());
|
|
|
|
-- Policy: Users can create reports
|
|
CREATE POLICY "p2p_fraud_create" ON public.p2p_fraud_reports
|
|
FOR INSERT WITH CHECK (reporter_id = auth.uid());
|
|
|
|
-- Policy: Admins can view all reports
|
|
CREATE POLICY "p2p_fraud_admin_read" ON public.p2p_fraud_reports
|
|
FOR SELECT USING (
|
|
EXISTS (
|
|
SELECT 1 FROM public.admin_roles
|
|
WHERE user_id = auth.uid() AND role IN ('moderator', 'admin')
|
|
)
|
|
);
|
|
|
|
-- Policy: Admins can update reports
|
|
CREATE POLICY "p2p_fraud_admin_update" ON public.p2p_fraud_reports
|
|
FOR UPDATE USING (
|
|
EXISTS (
|
|
SELECT 1 FROM public.admin_roles
|
|
WHERE user_id = auth.uid() AND role IN ('moderator', 'admin')
|
|
)
|
|
);
|
|
|
|
-- =====================================================
|
|
-- TRIGGERS FOR UPDATED_AT
|
|
-- =====================================================
|
|
CREATE TRIGGER update_p2p_messages_updated_at BEFORE UPDATE ON public.p2p_messages
|
|
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
|
|
|
CREATE TRIGGER update_p2p_fraud_reports_updated_at BEFORE UPDATE ON public.p2p_fraud_reports
|
|
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
|
|
|
-- =====================================================
|
|
-- ENABLE REALTIME FOR NEW TABLES
|
|
-- =====================================================
|
|
ALTER PUBLICATION supabase_realtime ADD TABLE public.p2p_messages;
|
|
ALTER PUBLICATION supabase_realtime ADD TABLE public.p2p_notifications;
|
|
|
|
-- =====================================================
|
|
-- NOTIFICATION TRIGGER FUNCTIONS
|
|
-- =====================================================
|
|
|
|
-- Function to create notification
|
|
CREATE OR REPLACE FUNCTION public.create_p2p_notification(
|
|
p_user_id UUID,
|
|
p_type TEXT,
|
|
p_title TEXT,
|
|
p_message TEXT DEFAULT NULL,
|
|
p_reference_type TEXT DEFAULT NULL,
|
|
p_reference_id UUID DEFAULT NULL,
|
|
p_action_url TEXT DEFAULT NULL,
|
|
p_metadata JSONB DEFAULT '{}'
|
|
) RETURNS UUID AS $$
|
|
DECLARE
|
|
v_notification_id UUID;
|
|
BEGIN
|
|
INSERT INTO public.p2p_notifications (
|
|
user_id, type, title, message,
|
|
reference_type, reference_id, action_url, metadata
|
|
) VALUES (
|
|
p_user_id, p_type, p_title, p_message,
|
|
p_reference_type, p_reference_id, p_action_url, p_metadata
|
|
)
|
|
RETURNING id INTO v_notification_id;
|
|
|
|
RETURN v_notification_id;
|
|
END;
|
|
$$ LANGUAGE plpgsql SECURITY DEFINER;
|
|
|
|
-- Trigger: Notify on new trade
|
|
CREATE OR REPLACE FUNCTION notify_on_new_trade()
|
|
RETURNS TRIGGER AS $$
|
|
DECLARE
|
|
v_offer RECORD;
|
|
BEGIN
|
|
-- Get offer details
|
|
SELECT * INTO v_offer FROM public.p2p_fiat_offers WHERE id = NEW.offer_id;
|
|
|
|
-- Notify seller about new order
|
|
PERFORM public.create_p2p_notification(
|
|
NEW.seller_id,
|
|
'new_order',
|
|
'New P2P Order',
|
|
format('Someone wants to buy %s %s', NEW.crypto_amount, v_offer.token),
|
|
'trade',
|
|
NEW.id,
|
|
format('/p2p/trade/%s', NEW.id)
|
|
);
|
|
|
|
RETURN NEW;
|
|
END;
|
|
$$ LANGUAGE plpgsql SECURITY DEFINER;
|
|
|
|
CREATE TRIGGER trigger_notify_new_trade
|
|
AFTER INSERT ON public.p2p_fiat_trades
|
|
FOR EACH ROW EXECUTE FUNCTION notify_on_new_trade();
|
|
|
|
-- Trigger: Notify on payment sent
|
|
CREATE OR REPLACE FUNCTION notify_on_payment_sent()
|
|
RETURNS TRIGGER AS $$
|
|
BEGIN
|
|
IF OLD.status = 'pending' AND NEW.status = 'payment_sent' THEN
|
|
PERFORM public.create_p2p_notification(
|
|
NEW.seller_id,
|
|
'payment_sent',
|
|
'Payment Marked as Sent',
|
|
'The buyer has marked the payment as sent. Please verify and release.',
|
|
'trade',
|
|
NEW.id,
|
|
format('/p2p/trade/%s', NEW.id)
|
|
);
|
|
END IF;
|
|
|
|
RETURN NEW;
|
|
END;
|
|
$$ LANGUAGE plpgsql SECURITY DEFINER;
|
|
|
|
CREATE TRIGGER trigger_notify_payment_sent
|
|
AFTER UPDATE ON public.p2p_fiat_trades
|
|
FOR EACH ROW EXECUTE FUNCTION notify_on_payment_sent();
|
|
|
|
-- Trigger: Notify on trade completed
|
|
CREATE OR REPLACE FUNCTION notify_on_trade_completed()
|
|
RETURNS TRIGGER AS $$
|
|
BEGIN
|
|
IF OLD.status != 'completed' AND NEW.status = 'completed' THEN
|
|
-- Notify buyer
|
|
PERFORM public.create_p2p_notification(
|
|
NEW.buyer_id,
|
|
'payment_confirmed',
|
|
'Trade Completed!',
|
|
'The seller has released the crypto. Check your wallet.',
|
|
'trade',
|
|
NEW.id,
|
|
format('/p2p/trade/%s', NEW.id)
|
|
);
|
|
END IF;
|
|
|
|
RETURN NEW;
|
|
END;
|
|
$$ LANGUAGE plpgsql SECURITY DEFINER;
|
|
|
|
CREATE TRIGGER trigger_notify_trade_completed
|
|
AFTER UPDATE ON public.p2p_fiat_trades
|
|
FOR EACH ROW EXECUTE FUNCTION notify_on_trade_completed();
|
|
|
|
-- Trigger: Notify on dispute opened
|
|
CREATE OR REPLACE FUNCTION notify_on_dispute_opened()
|
|
RETURNS TRIGGER AS $$
|
|
DECLARE
|
|
v_trade RECORD;
|
|
v_other_party UUID;
|
|
BEGIN
|
|
-- Get trade details
|
|
SELECT * INTO v_trade FROM public.p2p_fiat_trades WHERE id = NEW.trade_id;
|
|
|
|
-- Determine other party
|
|
IF NEW.opened_by = v_trade.seller_id THEN
|
|
v_other_party := v_trade.buyer_id;
|
|
ELSE
|
|
v_other_party := v_trade.seller_id;
|
|
END IF;
|
|
|
|
-- Notify the other party
|
|
PERFORM public.create_p2p_notification(
|
|
v_other_party,
|
|
'dispute_opened',
|
|
'Dispute Opened',
|
|
'A dispute has been opened for your trade. Please provide evidence.',
|
|
'dispute',
|
|
NEW.id,
|
|
format('/p2p/dispute/%s', NEW.id)
|
|
);
|
|
|
|
RETURN NEW;
|
|
END;
|
|
$$ LANGUAGE plpgsql SECURITY DEFINER;
|
|
|
|
CREATE TRIGGER trigger_notify_dispute_opened
|
|
AFTER INSERT ON public.p2p_fiat_disputes
|
|
FOR EACH ROW EXECUTE FUNCTION notify_on_dispute_opened();
|
|
|
|
-- Trigger: Notify on new message
|
|
CREATE OR REPLACE FUNCTION notify_on_new_message()
|
|
RETURNS TRIGGER AS $$
|
|
DECLARE
|
|
v_trade RECORD;
|
|
v_recipient_id UUID;
|
|
BEGIN
|
|
-- Skip system messages
|
|
IF NEW.message_type = 'system' THEN
|
|
RETURN NEW;
|
|
END IF;
|
|
|
|
-- Get trade details
|
|
SELECT * INTO v_trade FROM public.p2p_fiat_trades WHERE id = NEW.trade_id;
|
|
|
|
-- Determine recipient
|
|
IF NEW.sender_id = v_trade.seller_id THEN
|
|
v_recipient_id := v_trade.buyer_id;
|
|
ELSE
|
|
v_recipient_id := v_trade.seller_id;
|
|
END IF;
|
|
|
|
-- Create notification
|
|
PERFORM public.create_p2p_notification(
|
|
v_recipient_id,
|
|
'new_message',
|
|
'New Message',
|
|
LEFT(NEW.message, 100),
|
|
'trade',
|
|
NEW.trade_id,
|
|
format('/p2p/trade/%s', NEW.trade_id)
|
|
);
|
|
|
|
RETURN NEW;
|
|
END;
|
|
$$ LANGUAGE plpgsql SECURITY DEFINER;
|
|
|
|
CREATE TRIGGER trigger_notify_new_message
|
|
AFTER INSERT ON public.p2p_messages
|
|
FOR EACH ROW EXECUTE FUNCTION notify_on_new_message();
|
|
|
|
-- =====================================================
|
|
-- RATING HELPER FUNCTION
|
|
-- =====================================================
|
|
CREATE OR REPLACE FUNCTION public.update_user_rating_stats(p_user_id UUID)
|
|
RETURNS void AS $$
|
|
DECLARE
|
|
v_avg_rating NUMERIC;
|
|
v_total_ratings INT;
|
|
BEGIN
|
|
-- Calculate average rating
|
|
SELECT
|
|
AVG(rating)::NUMERIC(3,2),
|
|
COUNT(*)
|
|
INTO v_avg_rating, v_total_ratings
|
|
FROM public.p2p_ratings
|
|
WHERE rated_id = p_user_id;
|
|
|
|
-- Update reputation with rating info
|
|
UPDATE public.p2p_reputation
|
|
SET
|
|
updated_at = NOW()
|
|
-- Note: You could add avg_rating column to p2p_reputation if needed
|
|
WHERE user_id = p_user_id;
|
|
END;
|
|
$$ LANGUAGE plpgsql SECURITY DEFINER;
|
|
|
|
-- Trigger: Update stats after rating
|
|
CREATE OR REPLACE FUNCTION update_rating_stats_trigger()
|
|
RETURNS TRIGGER AS $$
|
|
BEGIN
|
|
PERFORM public.update_user_rating_stats(NEW.rated_id);
|
|
RETURN NEW;
|
|
END;
|
|
$$ LANGUAGE plpgsql SECURITY DEFINER;
|
|
|
|
CREATE TRIGGER trigger_update_rating_stats
|
|
AFTER INSERT ON public.p2p_ratings
|
|
FOR EACH ROW EXECUTE FUNCTION update_rating_stats_trigger();
|
|
|
|
-- =====================================================
|
|
-- GRANT EXECUTE PERMISSIONS
|
|
-- =====================================================
|
|
GRANT EXECUTE ON FUNCTION public.create_p2p_notification TO authenticated;
|
|
GRANT EXECUTE ON FUNCTION public.update_user_rating_stats TO authenticated;
|
|
|
|
-- =====================================================
|
|
-- ADD INDEXES FOR PERFORMANCE
|
|
-- =====================================================
|
|
|
|
-- Index for finding user's average rating quickly
|
|
CREATE INDEX idx_p2p_ratings_avg ON public.p2p_ratings(rated_id, rating);
|
|
|
|
-- Index for unread notification count
|
|
CREATE INDEX idx_p2p_notifications_unread_count ON public.p2p_notifications(user_id)
|
|
WHERE is_read = false;
|
|
|
|
-- =====================================================
|
|
-- COMMENTS FOR DOCUMENTATION
|
|
-- =====================================================
|
|
COMMENT ON TABLE public.p2p_messages IS 'Real-time chat messages between trade participants';
|
|
COMMENT ON TABLE public.p2p_ratings IS 'Post-trade ratings and reviews';
|
|
COMMENT ON TABLE public.p2p_notifications IS 'User notifications for P2P trading events';
|
|
COMMENT ON TABLE public.p2p_dispute_evidence IS 'Evidence files uploaded during disputes';
|
|
COMMENT ON TABLE public.p2p_fraud_reports IS 'Fraud reports submitted by users';
|