331 lines
12 KiB
PL/PgSQL
331 lines
12 KiB
PL/PgSQL
|
|
-- ============================================================
|
||
|
|
-- QZ TOKEN SYSTEM — quiXzoom Token & Payment Schema
|
||
|
|
-- ============================================================
|
||
|
|
-- Komplett schema inklusive zoomers, missions, observations
|
||
|
|
|
||
|
|
-- ============================================================
|
||
|
|
-- ZOOMERS
|
||
|
|
-- ============================================================
|
||
|
|
|
||
|
|
CREATE TABLE zoomers (
|
||
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||
|
|
name VARCHAR(255) NOT NULL,
|
||
|
|
email VARCHAR(255) UNIQUE,
|
||
|
|
phone VARCHAR(50),
|
||
|
|
status VARCHAR(50) DEFAULT 'active',
|
||
|
|
capabilities JSONB DEFAULT '[]',
|
||
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||
|
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||
|
|
);
|
||
|
|
|
||
|
|
-- ============================================================
|
||
|
|
-- AREAS
|
||
|
|
-- ============================================================
|
||
|
|
|
||
|
|
CREATE TABLE areas (
|
||
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||
|
|
name VARCHAR(255) NOT NULL,
|
||
|
|
country VARCHAR(100),
|
||
|
|
city VARCHAR(100),
|
||
|
|
priority FLOAT DEFAULT 0.5,
|
||
|
|
status VARCHAR(50) DEFAULT 'active',
|
||
|
|
metadata JSONB DEFAULT '{}',
|
||
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||
|
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||
|
|
);
|
||
|
|
|
||
|
|
-- ============================================================
|
||
|
|
-- OBSERVATIONS
|
||
|
|
-- ============================================================
|
||
|
|
|
||
|
|
CREATE TABLE observations (
|
||
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||
|
|
observation_id VARCHAR(255) UNIQUE NOT NULL,
|
||
|
|
source_type VARCHAR(50) NOT NULL,
|
||
|
|
source_id VARCHAR(255),
|
||
|
|
zoomer_id UUID REFERENCES zoomers(id),
|
||
|
|
media_type VARCHAR(50),
|
||
|
|
media_url TEXT,
|
||
|
|
status VARCHAR(50) DEFAULT 'pending',
|
||
|
|
validated_by VARCHAR(255),
|
||
|
|
validated_at TIMESTAMP WITH TIME ZONE,
|
||
|
|
ai_analysis JSONB DEFAULT '{}',
|
||
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||
|
|
);
|
||
|
|
|
||
|
|
-- ============================================================
|
||
|
|
-- OBJECTS
|
||
|
|
-- ============================================================
|
||
|
|
|
||
|
|
CREATE TABLE objects (
|
||
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||
|
|
object_id VARCHAR(255) UNIQUE NOT NULL,
|
||
|
|
type VARCHAR(100) NOT NULL,
|
||
|
|
subtype VARCHAR(100),
|
||
|
|
status VARCHAR(50) DEFAULT 'active',
|
||
|
|
verification_status VARCHAR(50) DEFAULT 'unverified',
|
||
|
|
confidence FLOAT DEFAULT 0,
|
||
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||
|
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||
|
|
);
|
||
|
|
|
||
|
|
-- ============================================================
|
||
|
|
-- MISSIONS
|
||
|
|
-- ============================================================
|
||
|
|
|
||
|
|
CREATE TABLE missions (
|
||
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||
|
|
mission_id VARCHAR(255) UNIQUE NOT NULL,
|
||
|
|
type VARCHAR(50) NOT NULL,
|
||
|
|
area_id UUID REFERENCES areas(id),
|
||
|
|
assigned_to UUID REFERENCES zoomers(id),
|
||
|
|
assigned_at TIMESTAMP WITH TIME ZONE,
|
||
|
|
status VARCHAR(50) DEFAULT 'pending',
|
||
|
|
result JSONB DEFAULT '{}',
|
||
|
|
completed_at TIMESTAMP WITH TIME ZONE,
|
||
|
|
priority FLOAT DEFAULT 0.5,
|
||
|
|
instructions JSONB DEFAULT '[]',
|
||
|
|
metadata JSONB DEFAULT '{}',
|
||
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||
|
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||
|
|
);
|
||
|
|
|
||
|
|
-- ============================================================
|
||
|
|
-- ZOOMER WALLETS
|
||
|
|
-- ============================================================
|
||
|
|
|
||
|
|
CREATE TABLE zoomer_wallets (
|
||
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||
|
|
zoomer_id UUID NOT NULL REFERENCES zoomers(id) ON DELETE CASCADE,
|
||
|
|
balance_qz_token DECIMAL(18, 8) DEFAULT 0.0,
|
||
|
|
balance_pending_qz_token DECIMAL(18, 8) DEFAULT 0.0,
|
||
|
|
stripe_connect_account_id VARCHAR(255),
|
||
|
|
stripe_connect_status VARCHAR(50) DEFAULT 'pending',
|
||
|
|
payout_schedule VARCHAR(50) DEFAULT 'weekly_monday',
|
||
|
|
payout_minimum_qz_token DECIMAL(18, 8) DEFAULT 10.0,
|
||
|
|
status VARCHAR(50) DEFAULT 'active',
|
||
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||
|
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||
|
|
UNIQUE(zoomer_id)
|
||
|
|
);
|
||
|
|
|
||
|
|
CREATE INDEX idx_zoomer_wallets_zoomer ON zoomer_wallets(zoomer_id);
|
||
|
|
CREATE INDEX idx_zoomer_wallets_stripe ON zoomer_wallets(stripe_connect_account_id);
|
||
|
|
|
||
|
|
-- ============================================================
|
||
|
|
-- TOKEN TRANSACTIONS (Immutable Ledger)
|
||
|
|
-- ============================================================
|
||
|
|
|
||
|
|
CREATE TABLE token_transactions (
|
||
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||
|
|
tx_hash VARCHAR(255) UNIQUE NOT NULL,
|
||
|
|
tx_type VARCHAR(50) NOT NULL,
|
||
|
|
from_wallet_id UUID REFERENCES zoomer_wallets(id),
|
||
|
|
to_wallet_id UUID REFERENCES zoomer_wallets(id),
|
||
|
|
amount_qz_token DECIMAL(18, 8) NOT NULL,
|
||
|
|
amount_usd DECIMAL(18, 8) NOT NULL,
|
||
|
|
exchange_rate DECIMAL(18, 8) NOT NULL,
|
||
|
|
mission_id UUID REFERENCES missions(id),
|
||
|
|
observation_id UUID REFERENCES observations(id),
|
||
|
|
stripe_payment_intent_id VARCHAR(255),
|
||
|
|
stripe_payout_id VARCHAR(255),
|
||
|
|
description TEXT,
|
||
|
|
metadata JSONB DEFAULT '{}',
|
||
|
|
signature VARCHAR(512),
|
||
|
|
signed_by VARCHAR(255),
|
||
|
|
status VARCHAR(50) DEFAULT 'confirmed',
|
||
|
|
confirmed_at TIMESTAMP WITH TIME ZONE,
|
||
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||
|
|
CONSTRAINT no_negative_amount CHECK (amount_qz_token >= 0)
|
||
|
|
);
|
||
|
|
|
||
|
|
CREATE INDEX idx_token_tx_hash ON token_transactions(tx_hash);
|
||
|
|
CREATE INDEX idx_token_tx_from ON token_transactions(from_wallet_id);
|
||
|
|
CREATE INDEX idx_token_tx_to ON token_transactions(to_wallet_id);
|
||
|
|
CREATE INDEX idx_token_tx_type ON token_transactions(tx_type);
|
||
|
|
CREATE INDEX idx_token_tx_mission ON token_transactions(mission_id);
|
||
|
|
CREATE INDEX idx_token_tx_created ON token_transactions(created_at);
|
||
|
|
|
||
|
|
-- ============================================================
|
||
|
|
-- MISSION PRICING
|
||
|
|
-- ============================================================
|
||
|
|
|
||
|
|
CREATE TABLE mission_pricing (
|
||
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||
|
|
mission_type VARCHAR(50) NOT NULL,
|
||
|
|
base_price_qz_token DECIMAL(18, 8) NOT NULL,
|
||
|
|
base_price_usd DECIMAL(18, 8) NOT NULL,
|
||
|
|
complexity_multiplier DECIMAL(5, 2) DEFAULT 1.0,
|
||
|
|
distance_bonus_qz_token DECIMAL(18, 8) DEFAULT 0.0,
|
||
|
|
time_bonus_qz_token DECIMAL(18, 8) DEFAULT 0.0,
|
||
|
|
quality_bonus_qz_token DECIMAL(18, 8) DEFAULT 0.0,
|
||
|
|
min_price_qz_token DECIMAL(18, 8) DEFAULT 0.5,
|
||
|
|
max_price_qz_token DECIMAL(18, 8) DEFAULT 100.0,
|
||
|
|
country_code VARCHAR(10),
|
||
|
|
city VARCHAR(100),
|
||
|
|
status VARCHAR(50) DEFAULT 'active',
|
||
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||
|
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||
|
|
UNIQUE(mission_type, country_code, city)
|
||
|
|
);
|
||
|
|
|
||
|
|
CREATE INDEX idx_mission_pricing_type ON mission_pricing(mission_type);
|
||
|
|
CREATE INDEX idx_mission_pricing_geo ON mission_pricing(country_code, city);
|
||
|
|
|
||
|
|
-- ============================================================
|
||
|
|
-- PAYOUT SCHEDULES
|
||
|
|
-- ============================================================
|
||
|
|
|
||
|
|
CREATE TABLE payout_schedules (
|
||
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||
|
|
zoomer_id UUID NOT NULL REFERENCES zoomers(id),
|
||
|
|
wallet_id UUID NOT NULL REFERENCES zoomer_wallets(id),
|
||
|
|
day_of_week INTEGER NOT NULL DEFAULT 1,
|
||
|
|
time_of_day TIME NOT NULL DEFAULT '09:00:00',
|
||
|
|
timezone VARCHAR(50) DEFAULT 'UTC',
|
||
|
|
minimum_qz_token DECIMAL(18, 8) DEFAULT 10.0,
|
||
|
|
status VARCHAR(50) DEFAULT 'active',
|
||
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||
|
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||
|
|
UNIQUE(zoomer_id)
|
||
|
|
);
|
||
|
|
|
||
|
|
CREATE INDEX idx_payout_schedules_zoomer ON payout_schedules(zoomer_id);
|
||
|
|
CREATE INDEX idx_payout_schedules_day ON payout_schedules(day_of_week);
|
||
|
|
|
||
|
|
-- ============================================================
|
||
|
|
-- PAYOUT EXECUTIONS
|
||
|
|
-- ============================================================
|
||
|
|
|
||
|
|
CREATE TABLE payout_executions (
|
||
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||
|
|
zoomer_id UUID NOT NULL REFERENCES zoomers(id),
|
||
|
|
wallet_id UUID NOT NULL REFERENCES zoomer_wallets(id),
|
||
|
|
amount_qz_token DECIMAL(18, 8) NOT NULL,
|
||
|
|
amount_usd DECIMAL(18, 8) NOT NULL,
|
||
|
|
exchange_rate DECIMAL(18, 8) NOT NULL,
|
||
|
|
stripe_payout_id VARCHAR(255),
|
||
|
|
stripe_transfer_id VARCHAR(255),
|
||
|
|
status VARCHAR(50) DEFAULT 'pending',
|
||
|
|
error_message TEXT,
|
||
|
|
retry_count INTEGER DEFAULT 0,
|
||
|
|
processed_at TIMESTAMP WITH TIME ZONE,
|
||
|
|
completed_at TIMESTAMP WITH TIME ZONE,
|
||
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||
|
|
CONSTRAINT no_negative_payout CHECK (amount_qz_token > 0)
|
||
|
|
);
|
||
|
|
|
||
|
|
CREATE INDEX idx_payout_exec_zoomer ON payout_executions(zoomer_id);
|
||
|
|
CREATE INDEX idx_payout_exec_status ON payout_executions(status);
|
||
|
|
CREATE INDEX idx_payout_exec_stripe ON payout_executions(stripe_payout_id);
|
||
|
|
|
||
|
|
-- ============================================================
|
||
|
|
-- CUSTOMER TOP-UPS
|
||
|
|
-- ============================================================
|
||
|
|
|
||
|
|
CREATE TABLE customer_topups (
|
||
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||
|
|
customer_id VARCHAR(255) NOT NULL,
|
||
|
|
amount_qz_token DECIMAL(18, 8) NOT NULL,
|
||
|
|
amount_usd DECIMAL(18, 8) NOT NULL,
|
||
|
|
exchange_rate DECIMAL(18, 8) NOT NULL,
|
||
|
|
stripe_payment_intent_id VARCHAR(255),
|
||
|
|
stripe_charge_id VARCHAR(255),
|
||
|
|
status VARCHAR(50) DEFAULT 'pending',
|
||
|
|
metadata JSONB DEFAULT '{}',
|
||
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||
|
|
completed_at TIMESTAMP WITH TIME ZONE
|
||
|
|
);
|
||
|
|
|
||
|
|
CREATE INDEX idx_customer_topups_customer ON customer_topups(customer_id);
|
||
|
|
CREATE INDEX idx_customer_topups_stripe ON customer_topups(stripe_payment_intent_id);
|
||
|
|
|
||
|
|
-- ============================================================
|
||
|
|
-- VIEWS
|
||
|
|
-- ============================================================
|
||
|
|
|
||
|
|
CREATE VIEW zoomer_balance AS
|
||
|
|
SELECT
|
||
|
|
z.id as zoomer_id,
|
||
|
|
z.name as zoomer_name,
|
||
|
|
zw.id as wallet_id,
|
||
|
|
zw.balance_qz_token,
|
||
|
|
zw.balance_pending_qz_token,
|
||
|
|
zw.balance_qz_token + zw.balance_pending_qz_token as total_balance,
|
||
|
|
zw.stripe_connect_status,
|
||
|
|
zw.payout_schedule
|
||
|
|
FROM zoomers z
|
||
|
|
JOIN zoomer_wallets zw ON zw.zoomer_id = z.id;
|
||
|
|
|
||
|
|
CREATE VIEW token_flow_summary AS
|
||
|
|
SELECT
|
||
|
|
DATE_TRUNC('day', created_at) as date,
|
||
|
|
tx_type,
|
||
|
|
COUNT(*) as transaction_count,
|
||
|
|
SUM(amount_qz_token) as total_qz_token,
|
||
|
|
SUM(amount_usd) as total_usd
|
||
|
|
FROM token_transactions
|
||
|
|
WHERE status = 'confirmed'
|
||
|
|
GROUP BY DATE_TRUNC('day', created_at), tx_type
|
||
|
|
ORDER BY date DESC, tx_type;
|
||
|
|
|
||
|
|
-- ============================================================
|
||
|
|
-- TRIGGERS
|
||
|
|
-- ============================================================
|
||
|
|
|
||
|
|
CREATE OR REPLACE FUNCTION update_updated_at_column()
|
||
|
|
RETURNS TRIGGER AS $$
|
||
|
|
BEGIN
|
||
|
|
NEW.updated_at = NOW();
|
||
|
|
RETURN NEW;
|
||
|
|
END;
|
||
|
|
$$ language 'plpgsql';
|
||
|
|
|
||
|
|
CREATE TRIGGER update_zoomers_updated_at BEFORE UPDATE ON zoomers
|
||
|
|
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||
|
|
|
||
|
|
CREATE TRIGGER update_areas_updated_at BEFORE UPDATE ON areas
|
||
|
|
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||
|
|
|
||
|
|
CREATE TRIGGER update_objects_updated_at BEFORE UPDATE ON objects
|
||
|
|
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||
|
|
|
||
|
|
CREATE TRIGGER update_missions_updated_at BEFORE UPDATE ON missions
|
||
|
|
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||
|
|
|
||
|
|
CREATE TRIGGER update_zoomer_wallets_updated_at BEFORE UPDATE ON zoomer_wallets
|
||
|
|
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||
|
|
|
||
|
|
CREATE TRIGGER update_mission_pricing_updated_at BEFORE UPDATE ON mission_pricing
|
||
|
|
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||
|
|
|
||
|
|
CREATE TRIGGER update_payout_schedules_updated_at BEFORE UPDATE ON payout_schedules
|
||
|
|
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||
|
|
|
||
|
|
-- ============================================================
|
||
|
|
-- LEDGER INTEGRITY
|
||
|
|
-- ============================================================
|
||
|
|
|
||
|
|
CREATE OR REPLACE FUNCTION prevent_tx_update()
|
||
|
|
RETURNS TRIGGER AS $$
|
||
|
|
BEGIN
|
||
|
|
IF OLD.status = 'confirmed' THEN
|
||
|
|
RAISE EXCEPTION 'Cannot modify confirmed transaction: %', OLD.tx_hash;
|
||
|
|
END IF;
|
||
|
|
RETURN NEW;
|
||
|
|
END;
|
||
|
|
$$ language 'plpgsql';
|
||
|
|
|
||
|
|
CREATE TRIGGER prevent_confirmed_tx_update BEFORE UPDATE ON token_transactions
|
||
|
|
FOR EACH ROW EXECUTE FUNCTION prevent_tx_update();
|
||
|
|
|
||
|
|
CREATE OR REPLACE FUNCTION prevent_tx_delete()
|
||
|
|
RETURNS TRIGGER AS $$
|
||
|
|
BEGIN
|
||
|
|
RAISE EXCEPTION 'Cannot delete transactions from immutable ledger';
|
||
|
|
END;
|
||
|
|
$$ language 'plpgsql';
|
||
|
|
|
||
|
|
CREATE TRIGGER prevent_tx_deletion BEFORE DELETE ON token_transactions
|
||
|
|
FOR EACH ROW EXECUTE FUNCTION prevent_tx_delete();
|