feat(db): add complete migration and update extension models to use user_role_profile_id
- Add comprehensive migration script for database schema redesign - Update all extension profile models to reference user_role_profile_id - Create user_role_profiles as root table for all role profiles - Remove external portfolio links (github_url, portfolio_url, reel_url) - Rename applications→job_applications, requirements→leads - Drop deprecated tables (professionals, onboarding_submissions, etc.)
This commit is contained in:
parent
03376b9567
commit
2e283e5d67
11 changed files with 1147 additions and 313 deletions
|
|
@ -0,0 +1,18 @@
|
|||
-- ============================================================================
|
||||
-- DOWN MIGRATION: This migration is NOT REVERSIBLE
|
||||
--
|
||||
-- This migration performs a COMPLETE schema transformation and CANNOT be
|
||||
-- rolled back. All old tables have been dropped, columns removed, and data
|
||||
-- restructured.
|
||||
--
|
||||
-- DO NOT ATTEMPT TO RUN THIS FILE
|
||||
--
|
||||
-- To restore from backup:
|
||||
-- 1. Restore PostgreSQL database from backup
|
||||
-- 2. Re-run the original init-db.sql and all previous migrations
|
||||
-- ============================================================================
|
||||
|
||||
-- THIS FILE INTENTIONALLY LEFT EMPTY
|
||||
-- This migration is NOT REVERSIBLE
|
||||
|
||||
SELECT 'MIGRATION CANNOT BE REVERSED - Restore from backup' AS warning;
|
||||
778
crates/db/migrations/20260415000000_complete_migration.up.sql
Normal file
778
crates/db/migrations/20260415000000_complete_migration.up.sql
Normal file
|
|
@ -0,0 +1,778 @@
|
|||
-- ============================================================================
|
||||
-- Nxtgauge Database Complete Migration
|
||||
-- Version: 20260415000000
|
||||
-- This migration performs a COMPLETE schema transformation
|
||||
-- NO FALLBACKS - This is a one-way migration
|
||||
-- ============================================================================
|
||||
|
||||
BEGIN;
|
||||
|
||||
-- ============================================================================
|
||||
-- PHASE 1: Create New Core Tables
|
||||
-- ============================================================================
|
||||
|
||||
-- 1.1 user_sessions (new)
|
||||
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 TEXT UNIQUE NOT NULL,
|
||||
ip_address TEXT,
|
||||
user_agent TEXT,
|
||||
expires_at TIMESTAMPTZ NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_user_sessions_user_id ON user_sessions(user_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_user_sessions_token ON user_sessions(session_token);
|
||||
CREATE INDEX IF NOT EXISTS idx_user_sessions_expires ON user_sessions(expires_at);
|
||||
|
||||
-- 1.2 user_role_profiles (NEW ROOT - CRITICAL)
|
||||
CREATE TABLE IF NOT EXISTS user_role_profiles (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
role_key VARCHAR(50) NOT NULL,
|
||||
display_name TEXT,
|
||||
bio TEXT,
|
||||
location TEXT,
|
||||
avatar_url TEXT,
|
||||
phone TEXT,
|
||||
email TEXT,
|
||||
status VARCHAR(50) NOT NULL DEFAULT 'ACTIVE',
|
||||
verification_status VARCHAR(50) DEFAULT 'PENDING',
|
||||
approval_status VARCHAR(50) DEFAULT 'PENDING',
|
||||
rejection_reason TEXT,
|
||||
approved_at TIMESTAMPTZ,
|
||||
verified_at TIMESTAMPTZ,
|
||||
is_profile_public BOOLEAN DEFAULT true,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
UNIQUE(user_id, role_key)
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_user_role_profiles_user_id ON user_role_profiles(user_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_user_role_profiles_role_key ON user_role_profiles(role_key);
|
||||
CREATE INDEX IF NOT EXISTS idx_user_role_profiles_status ON user_role_profiles(status);
|
||||
|
||||
-- ============================================================================
|
||||
-- PHASE 2: Backfill user_role_profiles from ALL existing profile tables
|
||||
-- ============================================================================
|
||||
|
||||
-- Backfill from photographer_profiles
|
||||
INSERT INTO user_role_profiles (id, user_id, role_key, display_name, bio, location, status, approval_status, rejection_reason, approved_at, created_at, updated_at)
|
||||
SELECT gen_random_uuid(), p.user_id, 'photographer', p.display_name, p.bio, p.location,
|
||||
COALESCE(p.status, 'ACTIVE'), COALESCE(p.status, 'PENDING'), p.rejection_reason, p.approved_at, p.created_at, COALESCE(p.updated_at, NOW())
|
||||
FROM photographer_profiles p
|
||||
WHERE NOT EXISTS (SELECT 1 FROM user_role_profiles urp WHERE urp.user_id = p.user_id AND urp.role_key = 'photographer');
|
||||
|
||||
-- Backfill from tutor_profiles
|
||||
INSERT INTO user_role_profiles (id, user_id, role_key, display_name, bio, location, status, approval_status, rejection_reason, approved_at, created_at, updated_at)
|
||||
SELECT gen_random_uuid(), p.user_id, 'tutor', p.display_name, p.bio, p.location,
|
||||
COALESCE(p.status, 'ACTIVE'), COALESCE(p.status, 'PENDING'), p.rejection_reason, p.approved_at, p.created_at, COALESCE(p.updated_at, NOW())
|
||||
FROM tutor_profiles p
|
||||
WHERE NOT EXISTS (SELECT 1 FROM user_role_profiles urp WHERE urp.user_id = p.user_id AND urp.role_key = 'tutor');
|
||||
|
||||
-- Backfill from makeup_artist_profiles
|
||||
INSERT INTO user_role_profiles (id, user_id, role_key, display_name, bio, location, status, approval_status, rejection_reason, approved_at, created_at, updated_at)
|
||||
SELECT gen_random_uuid(), p.user_id, 'makeup_artist', p.display_name, p.bio, p.location,
|
||||
COALESCE(p.status, 'ACTIVE'), COALESCE(p.status, 'PENDING'), p.rejection_reason, p.approved_at, p.created_at, COALESCE(p.updated_at, NOW())
|
||||
FROM makeup_artist_profiles p
|
||||
WHERE NOT EXISTS (SELECT 1 FROM user_role_profiles urp WHERE urp.user_id = p.user_id AND urp.role_key = 'makeup_artist');
|
||||
|
||||
-- Backfill from developer_profiles
|
||||
INSERT INTO user_role_profiles (id, user_id, role_key, display_name, bio, location, status, approval_status, rejection_reason, approved_at, created_at, updated_at)
|
||||
SELECT gen_random_uuid(), p.user_id, 'developer', p.display_name, p.bio, p.location,
|
||||
COALESCE(p.status, 'ACTIVE'), COALESCE(p.status, 'PENDING'), p.rejection_reason, p.approved_at, p.created_at, COALESCE(p.updated_at, NOW())
|
||||
FROM developer_profiles p
|
||||
WHERE NOT EXISTS (SELECT 1 FROM user_role_profiles urp WHERE urp.user_id = p.user_id AND urp.role_key = 'developer');
|
||||
|
||||
-- Backfill from video_editor_profiles
|
||||
INSERT INTO user_role_profiles (id, user_id, role_key, display_name, bio, location, status, approval_status, rejection_reason, approved_at, created_at, updated_at)
|
||||
SELECT gen_random_uuid(), p.user_id, 'video_editor', p.display_name, p.bio, p.location,
|
||||
COALESCE(p.status, 'ACTIVE'), COALESCE(p.status, 'PENDING'), p.rejection_reason, p.approved_at, p.created_at, COALESCE(p.updated_at, NOW())
|
||||
FROM video_editor_profiles p
|
||||
WHERE NOT EXISTS (SELECT 1 FROM user_role_profiles urp WHERE urp.user_id = p.user_id AND urp.role_key = 'video_editor');
|
||||
|
||||
-- Backfill from graphic_designer_profiles
|
||||
INSERT INTO user_role_profiles (id, user_id, role_key, display_name, bio, location, status, approval_status, rejection_reason, approved_at, created_at, updated_at)
|
||||
SELECT gen_random_uuid(), p.user_id, 'graphic_designer', p.display_name, p.bio, p.location,
|
||||
COALESCE(p.status, 'ACTIVE'), COALESCE(p.status, 'PENDING'), p.rejection_reason, p.approved_at, p.created_at, COALESCE(p.updated_at, NOW())
|
||||
FROM graphic_designer_profiles p
|
||||
WHERE NOT EXISTS (SELECT 1 FROM user_role_profiles urp WHERE urp.user_id = p.user_id AND urp.role_key = 'graphic_designer');
|
||||
|
||||
-- Backfill from social_media_manager_profiles
|
||||
INSERT INTO user_role_profiles (id, user_id, role_key, display_name, bio, location, status, approval_status, rejection_reason, approved_at, created_at, updated_at)
|
||||
SELECT gen_random_uuid(), p.user_id, 'social_media_manager', p.display_name, p.bio, p.location,
|
||||
COALESCE(p.status, 'ACTIVE'), COALESCE(p.status, 'PENDING'), p.rejection_reason, p.approved_at, p.created_at, COALESCE(p.updated_at, NOW())
|
||||
FROM social_media_manager_profiles p
|
||||
WHERE NOT EXISTS (SELECT 1 FROM user_role_profiles urp WHERE urp.user_id = p.user_id AND urp.role_key = 'social_media_manager');
|
||||
|
||||
-- Backfill from fitness_trainer_profiles
|
||||
INSERT INTO user_role_profiles (id, user_id, role_key, display_name, bio, location, status, approval_status, rejection_reason, approved_at, created_at, updated_at)
|
||||
SELECT gen_random_uuid(), p.user_id, 'fitness_trainer', p.display_name, p.bio, p.location,
|
||||
COALESCE(p.status, 'ACTIVE'), COALESCE(p.status, 'PENDING'), p.rejection_reason, p.approved_at, p.created_at, COALESCE(p.updated_at, NOW())
|
||||
FROM fitness_trainer_profiles p
|
||||
WHERE NOT EXISTS (SELECT 1 FROM user_role_profiles urp WHERE urp.user_id = p.user_id AND urp.role_key = 'fitness_trainer');
|
||||
|
||||
-- Backfill from catering_service_profiles
|
||||
INSERT INTO user_role_profiles (id, user_id, role_key, display_name, bio, location, status, approval_status, rejection_reason, approved_at, created_at, updated_at)
|
||||
SELECT gen_random_uuid(), p.user_id, 'catering_service', p.business_name, p.bio, p.location,
|
||||
COALESCE(p.status, 'ACTIVE'), COALESCE(p.status, 'PENDING'), p.rejection_reason, p.approved_at, p.created_at, COALESCE(p.updated_at, NOW())
|
||||
FROM catering_service_profiles p
|
||||
WHERE NOT EXISTS (SELECT 1 FROM user_role_profiles urp WHERE urp.user_id = p.user_id AND urp.role_key = 'catering_service');
|
||||
|
||||
-- Backfill from ugc_content_creator_profiles
|
||||
INSERT INTO user_role_profiles (id, user_id, role_key, display_name, bio, location, status, approval_status, rejection_reason, approved_at, created_at, updated_at)
|
||||
SELECT gen_random_uuid(), p.user_id, 'ugc_content_creator', p.display_name, p.bio, p.location,
|
||||
COALESCE(p.status, 'ACTIVE'), COALESCE(p.status, 'PENDING'), p.rejection_reason, p.approved_at, p.created_at, COALESCE(p.updated_at, NOW())
|
||||
FROM ugc_content_creator_profiles p
|
||||
WHERE NOT EXISTS (SELECT 1 FROM user_role_profiles urp WHERE urp.user_id = p.user_id AND urp.role_key = 'ugc_content_creator');
|
||||
|
||||
-- Backfill from company_profiles
|
||||
INSERT INTO user_role_profiles (id, user_id, role_key, display_name, bio, location, status, created_at, updated_at)
|
||||
SELECT gen_random_uuid(), cp.user_id, 'company', cp.company_name, cp.bio, NULL,
|
||||
COALESCE(cp.status, 'ACTIVE'), cp.created_at, COALESCE(cp.updated_at, NOW())
|
||||
FROM company_profiles cp
|
||||
WHERE NOT EXISTS (SELECT 1 FROM user_role_profiles urp WHERE urp.user_id = cp.user_id AND urp.role_key = 'company');
|
||||
|
||||
-- Backfill from customer_profiles
|
||||
INSERT INTO user_role_profiles (id, user_id, role_key, display_name, location, status, created_at, updated_at)
|
||||
SELECT gen_random_uuid(), cp.user_id, 'customer', COALESCE(cp.full_name, ''), cp.city,
|
||||
COALESCE(cp.status, 'ACTIVE'), cp.created_at, COALESCE(cp.updated_at, NOW())
|
||||
FROM customer_profiles cp
|
||||
WHERE NOT EXISTS (SELECT 1 FROM user_role_profiles urp WHERE urp.user_id = cp.user_id AND urp.role_key = 'customer');
|
||||
|
||||
-- Backfill from job_seeker_profiles
|
||||
INSERT INTO user_role_profiles (id, user_id, role_key, display_name, bio, location, status, created_at, updated_at)
|
||||
SELECT gen_random_uuid(), jsp.user_id, 'candidate', COALESCE(jsp.full_name, ''), jsp.bio, jsp.location,
|
||||
COALESCE(jsp.status, 'ACTIVE'), jsp.created_at, COALESCE(jsp.updated_at, NOW())
|
||||
FROM job_seeker_profiles jsp
|
||||
WHERE NOT EXISTS (SELECT 1 FROM user_role_profiles urp WHERE urp.user_id = jsp.user_id AND urp.role_key = 'candidate');
|
||||
|
||||
-- ============================================================================
|
||||
-- PHASE 3: Update Extension Tables to Use user_role_profile_id
|
||||
-- ============================================================================
|
||||
|
||||
-- Add user_role_profile_id column to ALL extension tables
|
||||
ALTER TABLE photographer_profiles ADD COLUMN IF NOT EXISTS user_role_profile_id UUID;
|
||||
ALTER TABLE tutor_profiles ADD COLUMN IF NOT EXISTS user_role_profile_id UUID;
|
||||
ALTER TABLE makeup_artist_profiles ADD COLUMN IF NOT EXISTS user_role_profile_id UUID;
|
||||
ALTER TABLE developer_profiles ADD COLUMN IF NOT EXISTS user_role_profile_id UUID;
|
||||
ALTER TABLE video_editor_profiles ADD COLUMN IF NOT EXISTS user_role_profile_id UUID;
|
||||
ALTER TABLE graphic_designer_profiles ADD COLUMN IF NOT EXISTS user_role_profile_id UUID;
|
||||
ALTER TABLE social_media_manager_profiles ADD COLUMN IF NOT EXISTS user_role_profile_id UUID;
|
||||
ALTER TABLE fitness_trainer_profiles ADD COLUMN IF NOT EXISTS user_role_profile_id UUID;
|
||||
ALTER TABLE catering_service_profiles ADD COLUMN IF NOT EXISTS user_role_profile_id UUID;
|
||||
ALTER TABLE ugc_content_creator_profiles ADD COLUMN IF NOT EXISTS user_role_profile_id UUID;
|
||||
|
||||
-- Backfill user_role_profile_id for photographer_profiles
|
||||
UPDATE photographer_profiles p SET user_role_profile_id = urp.id
|
||||
FROM user_role_profiles urp
|
||||
WHERE p.user_id = urp.user_id AND urp.role_key = 'photographer' AND p.user_role_profile_id IS NULL;
|
||||
|
||||
-- Backfill user_role_profile_id for tutor_profiles
|
||||
UPDATE tutor_profiles p SET user_role_profile_id = urp.id
|
||||
FROM user_role_profiles urp
|
||||
WHERE p.user_id = urp.user_id AND urp.role_key = 'tutor' AND p.user_role_profile_id IS NULL;
|
||||
|
||||
-- Backfill user_role_profile_id for makeup_artist_profiles
|
||||
UPDATE makeup_artist_profiles p SET user_role_profile_id = urp.id
|
||||
FROM user_role_profiles urp
|
||||
WHERE p.user_id = urp.user_id AND urp.role_key = 'makeup_artist' AND p.user_role_profile_id IS NULL;
|
||||
|
||||
-- Backfill user_role_profile_id for developer_profiles
|
||||
UPDATE developer_profiles p SET user_role_profile_id = urp.id
|
||||
FROM user_role_profiles urp
|
||||
WHERE p.user_id = urp.user_id AND urp.role_key = 'developer' AND p.user_role_profile_id IS NULL;
|
||||
|
||||
-- Backfill user_role_profile_id for video_editor_profiles
|
||||
UPDATE video_editor_profiles p SET user_role_profile_id = urp.id
|
||||
FROM user_role_profiles urp
|
||||
WHERE p.user_id = urp.user_id AND urp.role_key = 'video_editor' AND p.user_role_profile_id IS NULL;
|
||||
|
||||
-- Backfill user_role_profile_id for graphic_designer_profiles
|
||||
UPDATE graphic_designer_profiles p SET user_role_profile_id = urp.id
|
||||
FROM user_role_profiles urp
|
||||
WHERE p.user_id = urp.user_id AND urp.role_key = 'graphic_designer' AND p.user_role_profile_id IS NULL;
|
||||
|
||||
-- Backfill user_role_profile_id for social_media_manager_profiles
|
||||
UPDATE social_media_manager_profiles p SET user_role_profile_id = urp.id
|
||||
FROM user_role_profiles urp
|
||||
WHERE p.user_id = urp.user_id AND urp.role_key = 'social_media_manager' AND p.user_role_profile_id IS NULL;
|
||||
|
||||
-- Backfill user_role_profile_id for fitness_trainer_profiles
|
||||
UPDATE fitness_trainer_profiles p SET user_role_profile_id = urp.id
|
||||
FROM user_role_profiles urp
|
||||
WHERE p.user_id = urp.user_id AND urp.role_key = 'fitness_trainer' AND p.user_role_profile_id IS NULL;
|
||||
|
||||
-- Backfill user_role_profile_id for catering_service_profiles
|
||||
UPDATE catering_service_profiles p SET user_role_profile_id = urp.id
|
||||
FROM user_role_profiles urp
|
||||
WHERE p.user_id = urp.user_id AND urp.role_key = 'catering_service' AND p.user_role_profile_id IS NULL;
|
||||
|
||||
-- Backfill user_role_profile_id for ugc_content_creator_profiles
|
||||
UPDATE ugc_content_creator_profiles p SET user_role_profile_id = urp.id
|
||||
FROM user_role_profiles urp
|
||||
WHERE p.user_id = urp.user_id AND urp.role_key = 'ugc_content_creator' AND p.user_role_profile_id IS NULL;
|
||||
|
||||
-- ============================================================================
|
||||
-- PHASE 4: Remove Forbidden External Portfolio Links
|
||||
-- ============================================================================
|
||||
|
||||
ALTER TABLE developer_profiles DROP COLUMN IF EXISTS github_url;
|
||||
ALTER TABLE developer_profiles DROP COLUMN IF EXISTS portfolio_url;
|
||||
ALTER TABLE video_editor_profiles DROP COLUMN IF EXISTS reel_url;
|
||||
ALTER TABLE graphic_designer_profiles DROP COLUMN IF EXISTS portfolio_url;
|
||||
ALTER TABLE photographer_profiles DROP COLUMN IF EXISTS portfolio_url;
|
||||
|
||||
-- ============================================================================
|
||||
-- PHASE 5: Update Portfolio Tables
|
||||
-- ============================================================================
|
||||
|
||||
-- Add user_role_profile_id to portfolio_items
|
||||
ALTER TABLE portfolio_items ADD COLUMN IF NOT EXISTS user_role_profile_id UUID;
|
||||
ALTER TABLE portfolio_items ADD COLUMN IF NOT EXISTS display_order INTEGER DEFAULT 0;
|
||||
|
||||
-- Backfill portfolio_items from professionals
|
||||
UPDATE portfolio_items pi SET user_role_profile_id = urp.id
|
||||
FROM user_role_profiles urp
|
||||
WHERE pi.professional_id IS NOT NULL
|
||||
AND urp.user_id = (SELECT user_id FROM professionals p WHERE p.id = pi.professional_id);
|
||||
|
||||
-- Update remaining using user_id
|
||||
UPDATE portfolio_items pi SET user_role_profile_id = urp.id
|
||||
FROM user_role_profiles urp
|
||||
WHERE pi.user_id IS NOT NULL AND pi.user_role_profile_id IS NULL
|
||||
AND EXISTS (SELECT 1 FROM user_role_profiles urp2 WHERE urp2.user_id = pi.user_id AND urp2.role_key = pi.profession_key);
|
||||
|
||||
-- Add user_role_profile_id to services
|
||||
ALTER TABLE services ADD COLUMN IF NOT EXISTS user_role_profile_id UUID;
|
||||
|
||||
-- Backfill services
|
||||
UPDATE services s SET user_role_profile_id = urp.id
|
||||
FROM user_role_profiles urp
|
||||
WHERE s.professional_id IS NOT NULL
|
||||
AND urp.user_id = (SELECT user_id FROM professionals p WHERE p.id = s.professional_id);
|
||||
|
||||
UPDATE services s SET user_role_profile_id = urp.id
|
||||
FROM user_role_profiles urp
|
||||
WHERE s.user_id IS NOT NULL AND s.user_role_profile_id IS NULL
|
||||
AND EXISTS (SELECT 1 FROM user_role_profiles urp2 WHERE urp2.user_id = s.user_id AND urp2.role_key = s.profession_key);
|
||||
|
||||
-- ============================================================================
|
||||
-- PHASE 6: Rename Tables
|
||||
-- ============================================================================
|
||||
|
||||
-- Rename applications -> job_applications
|
||||
ALTER TABLE applications RENAME TO job_applications;
|
||||
|
||||
-- Rename requirements -> leads
|
||||
ALTER TABLE requirements RENAME TO leads;
|
||||
|
||||
-- Rename coupon_uses -> coupon_redemptions
|
||||
ALTER TABLE coupon_uses RENAME TO coupon_redemptions;
|
||||
|
||||
-- ============================================================================
|
||||
-- PHASE 7: Create New Domain Tables
|
||||
-- ============================================================================
|
||||
|
||||
-- 7.1 verification_requests
|
||||
CREATE TABLE IF NOT EXISTS verification_requests (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
user_role_profile_id UUID REFERENCES user_role_profiles(id),
|
||||
verification_type VARCHAR(50) NOT NULL DEFAULT 'IDENTITY',
|
||||
status VARCHAR(50) NOT NULL DEFAULT 'PENDING',
|
||||
submitted_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
reviewed_at TIMESTAMPTZ,
|
||||
reviewed_by_user_id UUID REFERENCES users(id),
|
||||
remarks TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- 7.2 verification_documents
|
||||
CREATE TABLE IF NOT EXISTS verification_documents (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
verification_request_id UUID NOT NULL REFERENCES verification_requests(id) ON DELETE CASCADE,
|
||||
document_type VARCHAR(100) NOT NULL,
|
||||
file_url TEXT NOT NULL,
|
||||
file_name TEXT,
|
||||
mime_type TEXT,
|
||||
status VARCHAR(50) DEFAULT 'PENDING',
|
||||
uploaded_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
reviewed_at TIMESTAMPTZ,
|
||||
reviewed_by_user_id UUID REFERENCES users(id),
|
||||
remarks TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- 7.3 verification_logs
|
||||
CREATE TABLE IF NOT EXISTS verification_logs (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
verification_request_id UUID NOT NULL REFERENCES verification_requests(id),
|
||||
action VARCHAR(50) NOT NULL,
|
||||
old_status VARCHAR(50),
|
||||
new_status VARCHAR(50),
|
||||
acted_by_user_id UUID REFERENCES users(id),
|
||||
remarks TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- 7.4 approval_requests
|
||||
CREATE TABLE IF NOT EXISTS approval_requests (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
entity_type VARCHAR(50) NOT NULL,
|
||||
entity_id UUID NOT NULL,
|
||||
approval_type VARCHAR(50),
|
||||
status VARCHAR(50) NOT NULL DEFAULT 'PENDING',
|
||||
submitted_by_user_id UUID REFERENCES users(id),
|
||||
reviewed_by_user_id UUID REFERENCES users(id),
|
||||
submitted_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
reviewed_at TIMESTAMPTZ,
|
||||
remarks TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- 7.5 approval_logs
|
||||
CREATE TABLE IF NOT EXISTS approval_logs (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
approval_request_id UUID NOT NULL REFERENCES approval_requests(id),
|
||||
action VARCHAR(50) NOT NULL,
|
||||
old_status VARCHAR(50),
|
||||
new_status VARCHAR(50),
|
||||
acted_by_user_id UUID REFERENCES users(id),
|
||||
remarks TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- 7.6 audit_logs
|
||||
CREATE TABLE IF NOT EXISTS audit_logs (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
actor_user_id UUID REFERENCES users(id),
|
||||
actor_employee_id UUID,
|
||||
actor_type VARCHAR(50),
|
||||
action VARCHAR(100) NOT NULL,
|
||||
entity_type VARCHAR(100),
|
||||
entity_id UUID,
|
||||
entity_label TEXT,
|
||||
module_key VARCHAR(100),
|
||||
source_type VARCHAR(50),
|
||||
source_id UUID,
|
||||
request_id UUID,
|
||||
correlation_id UUID,
|
||||
ip_address TEXT,
|
||||
user_agent TEXT,
|
||||
status VARCHAR(50) NOT NULL DEFAULT 'SUCCESS',
|
||||
summary TEXT,
|
||||
metadata_json JSONB,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- 7.7 audit_log_changes
|
||||
CREATE TABLE IF NOT EXISTS audit_log_changes (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
audit_log_id UUID NOT NULL REFERENCES audit_logs(id) ON DELETE CASCADE,
|
||||
field_name TEXT NOT NULL,
|
||||
old_value_text TEXT,
|
||||
new_value_text TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_audit_logs_actor ON audit_logs(actor_user_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_audit_logs_entity ON audit_logs(entity_type, entity_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_audit_logs_module ON audit_logs(module_key);
|
||||
CREATE INDEX IF NOT EXISTS idx_audit_logs_created ON audit_logs(created_at);
|
||||
|
||||
-- 7.8 orders
|
||||
CREATE TABLE IF NOT EXISTS orders (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
user_id UUID NOT NULL REFERENCES users(id),
|
||||
order_type VARCHAR(50) NOT NULL DEFAULT 'PACKAGE',
|
||||
subtotal_inr INTEGER NOT NULL DEFAULT 0,
|
||||
discount_inr INTEGER NOT NULL DEFAULT 0,
|
||||
tax_inr INTEGER NOT NULL DEFAULT 0,
|
||||
total_inr INTEGER NOT NULL DEFAULT 0,
|
||||
status VARCHAR(50) NOT NULL DEFAULT 'PENDING',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- 7.9 order_items
|
||||
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,
|
||||
item_type VARCHAR(50) NOT NULL,
|
||||
item_id UUID,
|
||||
item_name TEXT NOT NULL,
|
||||
quantity INTEGER NOT NULL DEFAULT 1,
|
||||
unit_price_inr INTEGER NOT NULL,
|
||||
total_price_inr INTEGER NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- 7.10 payment_gateway_configs
|
||||
CREATE TABLE IF NOT EXISTS payment_gateway_configs (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
gateway_key VARCHAR(50) NOT NULL,
|
||||
display_name VARCHAR(255),
|
||||
config_json JSONB,
|
||||
is_active BOOLEAN DEFAULT true,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- 7.11 payment_transactions
|
||||
CREATE TABLE IF NOT EXISTS payment_transactions (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
payment_id UUID NOT NULL REFERENCES payments(id),
|
||||
transaction_type VARCHAR(50) NOT NULL,
|
||||
provider_reference TEXT,
|
||||
request_payload_json JSONB,
|
||||
response_payload_json JSONB,
|
||||
status VARCHAR(50) NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- 7.12 tax_rules
|
||||
CREATE TABLE IF NOT EXISTS tax_rules (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
name VARCHAR(255) NOT NULL,
|
||||
tax_type VARCHAR(50) NOT NULL,
|
||||
tax_rate DECIMAL(5,2) NOT NULL,
|
||||
applies_to VARCHAR(50),
|
||||
is_active BOOLEAN DEFAULT true,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- 7.13 kb_sections
|
||||
CREATE TABLE IF NOT EXISTS kb_sections (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
category_id UUID NOT NULL REFERENCES kb_categories(id) ON DELETE CASCADE,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
slug VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
display_order INTEGER DEFAULT 0,
|
||||
is_active BOOLEAN DEFAULT true,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- 7.14 kb_article_feedback
|
||||
CREATE TABLE IF NOT EXISTS kb_article_feedback (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
article_id UUID NOT NULL REFERENCES kb_articles(id) ON DELETE CASCADE,
|
||||
user_id UUID REFERENCES users(id),
|
||||
is_helpful BOOLEAN,
|
||||
feedback_text TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- 7.15 notification_templates
|
||||
CREATE TABLE IF NOT EXISTS notification_templates (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
template_key VARCHAR(100) NOT NULL UNIQUE,
|
||||
channel VARCHAR(50) NOT NULL DEFAULT 'EMAIL',
|
||||
title_template TEXT,
|
||||
body_template TEXT,
|
||||
is_active BOOLEAN DEFAULT true,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- 7.16 smtp_configs
|
||||
CREATE TABLE IF NOT EXISTS smtp_configs (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
provider_name VARCHAR(100),
|
||||
host VARCHAR(255),
|
||||
port INTEGER,
|
||||
username TEXT,
|
||||
encryption_mode VARCHAR(20),
|
||||
from_name VARCHAR(255),
|
||||
from_email VARCHAR(255),
|
||||
is_default BOOLEAN DEFAULT false,
|
||||
is_active BOOLEAN DEFAULT true,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- 7.17 dashboard_widgets
|
||||
CREATE TABLE IF NOT EXISTS dashboard_widgets (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
dashboard_config_id UUID NOT NULL REFERENCES dashboard_configs(id) ON DELETE CASCADE,
|
||||
widget_key VARCHAR(100) NOT NULL,
|
||||
widget_title VARCHAR(255),
|
||||
config_json JSONB,
|
||||
display_order INTEGER DEFAULT 0,
|
||||
width_units INTEGER DEFAULT 1,
|
||||
height_units INTEGER DEFAULT 1,
|
||||
is_visible BOOLEAN DEFAULT true,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- ============================================================================
|
||||
-- PHASE 8: Update Existing Tables
|
||||
-- ============================================================================
|
||||
|
||||
-- Update users table
|
||||
ALTER TABLE users ADD COLUMN IF NOT EXISTS account_type TEXT DEFAULT 'INDIVIDUAL';
|
||||
ALTER TABLE users ADD COLUMN IF NOT EXISTS last_login_at TIMESTAMPTZ;
|
||||
ALTER TABLE users ADD COLUMN IF NOT EXISTS updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW();
|
||||
UPDATE users SET updated_at = COALESCE(updated_at, created_at, NOW()) WHERE updated_at IS NULL;
|
||||
|
||||
-- Update roles table
|
||||
ALTER TABLE roles ADD COLUMN IF NOT EXISTS description TEXT;
|
||||
ALTER TABLE roles ADD COLUMN IF NOT EXISTS can_approve_requests BOOLEAN DEFAULT false;
|
||||
ALTER TABLE roles ADD COLUMN IF NOT EXISTS can_manage_system_settings BOOLEAN DEFAULT false;
|
||||
|
||||
-- Update departments table
|
||||
ALTER TABLE departments ADD COLUMN IF NOT EXISTS code VARCHAR(64);
|
||||
ALTER TABLE departments ADD COLUMN IF NOT EXISTS description TEXT;
|
||||
ALTER TABLE departments ADD COLUMN IF NOT EXISTS department_head VARCHAR(255);
|
||||
ALTER TABLE departments ADD COLUMN IF NOT EXISTS department_email VARCHAR(255);
|
||||
ALTER TABLE departments ADD COLUMN IF NOT EXISTS visibility VARCHAR(20) DEFAULT 'INTERNAL';
|
||||
ALTER TABLE departments ADD COLUMN IF NOT EXISTS transfers_enabled BOOLEAN DEFAULT false;
|
||||
ALTER TABLE departments ADD COLUMN IF NOT EXISTS updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW();
|
||||
UPDATE departments SET updated_at = COALESCE(updated_at, created_at, NOW()) WHERE updated_at IS NULL;
|
||||
|
||||
-- Update designations table
|
||||
ALTER TABLE designations ADD COLUMN IF NOT EXISTS code VARCHAR(64);
|
||||
ALTER TABLE designations ADD COLUMN IF NOT EXISTS department_id UUID REFERENCES departments(id);
|
||||
ALTER TABLE designations ADD COLUMN IF NOT EXISTS description TEXT;
|
||||
ALTER TABLE designations ADD COLUMN IF NOT EXISTS level VARCHAR(100);
|
||||
ALTER TABLE designations ADD COLUMN IF NOT EXISTS can_manage_team BOOLEAN DEFAULT false;
|
||||
ALTER TABLE designations ADD COLUMN IF NOT EXISTS can_approve BOOLEAN DEFAULT false;
|
||||
ALTER TABLE designations ADD COLUMN IF NOT EXISTS is_active BOOLEAN DEFAULT true;
|
||||
ALTER TABLE designations ADD COLUMN IF NOT EXISTS updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW();
|
||||
UPDATE designations SET updated_at = COALESCE(updated_at, created_at, NOW()) WHERE updated_at IS NULL;
|
||||
|
||||
-- Update employees table
|
||||
ALTER TABLE employees ADD COLUMN IF NOT EXISTS joining_date DATE;
|
||||
ALTER TABLE employees ADD COLUMN IF NOT EXISTS employment_status VARCHAR(50) DEFAULT 'ACTIVE';
|
||||
ALTER TABLE employees ADD COLUMN IF NOT EXISTS manager_employee_id UUID REFERENCES employees(id);
|
||||
ALTER TABLE employees ADD COLUMN IF NOT EXISTS updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW();
|
||||
UPDATE employees SET updated_at = COALESCE(updated_at, created_at, NOW()) WHERE updated_at IS NULL;
|
||||
|
||||
-- Update lead_requests table
|
||||
ALTER TABLE lead_requests ADD COLUMN IF NOT EXISTS user_role_profile_id UUID;
|
||||
UPDATE lead_requests lr SET user_role_profile_id = urp.id
|
||||
FROM user_role_profiles urp
|
||||
WHERE lr.professional_id IS NOT NULL
|
||||
AND urp.user_id = (SELECT user_id FROM professionals p WHERE p.id = lr.professional_id);
|
||||
ALTER TABLE lead_requests ADD COLUMN IF NOT EXISTS remarks TEXT;
|
||||
ALTER TABLE lead_requests ADD COLUMN IF NOT EXISTS updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW();
|
||||
|
||||
-- Update job_applications table
|
||||
ALTER TABLE job_applications ADD COLUMN IF NOT EXISTS applicant_user_id UUID;
|
||||
UPDATE job_applications ja SET applicant_user_id = (
|
||||
SELECT user_id FROM job_seeker_profiles jsp WHERE jsp.id = ja.job_seeker_id
|
||||
);
|
||||
ALTER TABLE job_applications ADD COLUMN IF NOT EXISTS cover_note TEXT;
|
||||
ALTER TABLE job_applications DROP COLUMN IF EXISTS job_seeker_id;
|
||||
ALTER TABLE job_applications DROP COLUMN IF EXISTS cover_letter;
|
||||
ALTER TABLE job_applications DROP COLUMN IF EXISTS resume_url;
|
||||
ALTER TABLE job_applications DROP COLUMN IF EXISTS contact_viewed;
|
||||
|
||||
-- Update leads table (formerly requirements)
|
||||
ALTER TABLE leads ADD COLUMN IF NOT EXISTS created_by_user_id UUID;
|
||||
UPDATE leads l SET created_by_user_id = (
|
||||
SELECT user_id FROM customer_profiles cp WHERE cp.id = l.customer_id
|
||||
);
|
||||
ALTER TABLE leads ADD COLUMN IF NOT EXISTS required_date DATE;
|
||||
ALTER TABLE leads ADD COLUMN IF NOT EXISTS updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW();
|
||||
ALTER TABLE leads DROP COLUMN IF EXISTS customer_id;
|
||||
|
||||
-- Update jobs table
|
||||
ALTER TABLE jobs ADD COLUMN IF NOT EXISTS posted_by_user_id UUID;
|
||||
ALTER TABLE jobs ADD COLUMN IF NOT EXISTS mode_of_work VARCHAR(50);
|
||||
ALTER TABLE jobs ADD COLUMN IF NOT EXISTS budget_inr INTEGER;
|
||||
ALTER TABLE jobs ADD COLUMN IF NOT EXISTS salary_range_json JSONB;
|
||||
ALTER TABLE jobs ADD COLUMN IF NOT EXISTS updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW();
|
||||
UPDATE jobs SET updated_at = COALESCE(updated_at, created_at, NOW()) WHERE updated_at IS NULL;
|
||||
|
||||
-- Update tracecoin_wallets
|
||||
ALTER TABLE tracecoin_wallets RENAME COLUMN balance TO current_balance;
|
||||
ALTER TABLE tracecoin_wallets ADD COLUMN IF NOT EXISTS updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW();
|
||||
|
||||
-- Update tracecoin_ledger
|
||||
ALTER TABLE tracecoin_ledger ADD COLUMN IF NOT EXISTS balance_after INTEGER;
|
||||
ALTER TABLE tracecoin_ledger ADD COLUMN IF NOT EXISTS remarks TEXT;
|
||||
ALTER TABLE tracecoin_ledger RENAME COLUMN type TO transaction_type;
|
||||
ALTER TABLE tracecoin_ledger RENAME COLUMN reason TO reference_type;
|
||||
|
||||
-- Update coupons
|
||||
ALTER TABLE coupons ADD COLUMN IF NOT EXISTS max_discount_inr INTEGER;
|
||||
ALTER TABLE coupons ADD COLUMN IF NOT EXISTS min_order_value_inr INTEGER DEFAULT 0;
|
||||
ALTER TABLE coupons ADD COLUMN IF NOT EXISTS valid_from TIMESTAMPTZ DEFAULT NOW();
|
||||
ALTER TABLE coupons ADD COLUMN IF NOT EXISTS valid_to TIMESTAMPTZ;
|
||||
|
||||
-- Update coupon_redemptions
|
||||
ALTER TABLE coupon_redemptions ADD COLUMN IF NOT EXISTS order_id UUID REFERENCES orders(id);
|
||||
ALTER TABLE coupon_redemptions ADD COLUMN IF NOT EXISTS discount_amount_inr INTEGER;
|
||||
ALTER TABLE coupon_redemptions RENAME COLUMN used_at TO redeemed_at;
|
||||
|
||||
-- Update invoices
|
||||
ALTER TABLE invoices ADD COLUMN IF NOT EXISTS order_id UUID REFERENCES orders(id);
|
||||
ALTER TABLE invoices ADD COLUMN IF NOT EXISTS discount_inr INTEGER DEFAULT 0;
|
||||
ALTER TABLE invoices ADD COLUMN IF NOT EXISTS due_at TIMESTAMPTZ;
|
||||
ALTER TABLE invoices ADD COLUMN IF NOT EXISTS paid_at TIMESTAMPTZ;
|
||||
|
||||
-- Update payments
|
||||
ALTER TABLE payments ADD COLUMN IF NOT EXISTS payment_gateway_config_id UUID REFERENCES payment_gateway_configs(id);
|
||||
ALTER TABLE payments ADD COLUMN IF NOT EXISTS payment_method VARCHAR(50);
|
||||
ALTER TABLE payments ADD COLUMN IF NOT EXISTS currency_code VARCHAR(10) DEFAULT 'INR';
|
||||
ALTER TABLE payments ADD COLUMN IF NOT EXISTS initiated_at TIMESTAMPTZ DEFAULT NOW();
|
||||
ALTER TABLE payments ADD COLUMN IF NOT EXISTS completed_at TIMESTAMPTZ;
|
||||
|
||||
-- Update kb_articles
|
||||
ALTER TABLE kb_articles ADD COLUMN IF NOT EXISTS section_id UUID REFERENCES kb_sections(id);
|
||||
ALTER TABLE kb_articles ADD COLUMN IF NOT EXISTS article_type VARCHAR(50) DEFAULT 'HOW_TO';
|
||||
ALTER TABLE kb_articles ADD COLUMN IF NOT EXISTS audience_type VARCHAR(50) DEFAULT 'ALL';
|
||||
ALTER TABLE kb_articles RENAME COLUMN body TO content_markdown;
|
||||
ALTER TABLE kb_articles RENAME COLUMN created_by TO author_user_id;
|
||||
|
||||
-- Update support_tickets
|
||||
ALTER TABLE support_tickets ADD COLUMN IF NOT EXISTS created_by_user_id UUID;
|
||||
UPDATE support_tickets SET created_by_user_id = user_id;
|
||||
ALTER TABLE support_tickets RENAME COLUMN assigned_to TO assigned_to_user_id;
|
||||
ALTER TABLE support_tickets ADD COLUMN IF NOT EXISTS related_entity_type VARCHAR(50);
|
||||
ALTER TABLE support_tickets ADD COLUMN IF NOT EXISTS related_entity_id UUID;
|
||||
ALTER TABLE support_tickets ADD COLUMN IF NOT EXISTS closed_at TIMESTAMPTZ;
|
||||
|
||||
-- Update support_ticket_messages
|
||||
ALTER TABLE support_ticket_messages ADD COLUMN IF NOT EXISTS sender_user_id UUID;
|
||||
UPDATE support_ticket_messages SET sender_user_id = sender_id;
|
||||
ALTER TABLE support_ticket_messages RENAME COLUMN body TO message_body;
|
||||
ALTER TABLE support_ticket_messages ADD COLUMN IF NOT EXISTS attachment_url TEXT;
|
||||
|
||||
-- Update notifications
|
||||
ALTER TABLE notifications ADD COLUMN IF NOT EXISTS channel VARCHAR(50) DEFAULT 'IN_APP';
|
||||
ALTER TABLE notifications ADD COLUMN IF NOT EXISTS related_entity_type VARCHAR(50);
|
||||
ALTER TABLE notifications RENAME COLUMN reference_id TO related_entity_id;
|
||||
|
||||
-- Update reviews
|
||||
ALTER TABLE reviews ADD COLUMN IF NOT EXISTS entity_type VARCHAR(50) DEFAULT 'professional';
|
||||
ALTER TABLE reviews RENAME COLUMN customer_id TO reviewer_user_id;
|
||||
ALTER TABLE reviews ADD COLUMN IF NOT EXISTS status VARCHAR(50) DEFAULT 'PUBLISHED';
|
||||
|
||||
-- ============================================================================
|
||||
-- PHASE 9: Drop Deprecated Tables
|
||||
-- ============================================================================
|
||||
|
||||
DROP TABLE IF EXISTS professionals CASCADE;
|
||||
DROP TABLE IF EXISTS onboarding_submissions CASCADE;
|
||||
DROP TABLE IF EXISTS onboarding_configs CASCADE;
|
||||
DROP TABLE IF EXISTS onboarding_states CASCADE;
|
||||
DROP TABLE IF EXISTS submission_documents CASCADE;
|
||||
|
||||
-- ============================================================================
|
||||
-- PHASE 10: Drop Deprecated Columns from Extension Tables
|
||||
-- ============================================================================
|
||||
|
||||
-- Drop old user_id columns from extension tables (AFTER backfilling user_role_profile_id)
|
||||
ALTER TABLE photographer_profiles DROP COLUMN IF EXISTS user_id;
|
||||
ALTER TABLE tutor_profiles DROP COLUMN IF EXISTS user_id;
|
||||
ALTER TABLE makeup_artist_profiles DROP COLUMN IF EXISTS user_id;
|
||||
ALTER TABLE developer_profiles DROP COLUMN IF EXISTS user_id;
|
||||
ALTER TABLE video_editor_profiles DROP COLUMN IF EXISTS user_id;
|
||||
ALTER TABLE graphic_designer_profiles DROP COLUMN IF EXISTS user_id;
|
||||
ALTER TABLE social_media_manager_profiles DROP COLUMN IF EXISTS user_id;
|
||||
ALTER TABLE fitness_trainer_profiles DROP COLUMN IF EXISTS user_id;
|
||||
ALTER TABLE catering_service_profiles DROP COLUMN IF EXISTS user_id;
|
||||
ALTER TABLE ugc_content_creator_profiles DROP COLUMN IF EXISTS user_id;
|
||||
|
||||
-- Drop old columns from portfolio_items
|
||||
ALTER TABLE portfolio_items DROP COLUMN IF EXISTS professional_id;
|
||||
ALTER TABLE portfolio_items DROP COLUMN IF EXISTS user_id;
|
||||
ALTER TABLE portfolio_items DROP COLUMN IF EXISTS profession_key;
|
||||
|
||||
-- Drop old columns from services
|
||||
ALTER TABLE services DROP COLUMN IF EXISTS professional_id;
|
||||
ALTER TABLE services DROP COLUMN IF EXISTS user_id;
|
||||
ALTER TABLE services DROP COLUMN IF EXISTS profession_key;
|
||||
|
||||
-- Drop old columns from lead_requests
|
||||
ALTER TABLE lead_requests DROP COLUMN IF EXISTS professional_id;
|
||||
ALTER TABLE lead_requests DROP COLUMN IF EXISTS requirement_id;
|
||||
|
||||
-- Drop old custom_data columns
|
||||
ALTER TABLE photographer_profiles DROP COLUMN IF EXISTS custom_data;
|
||||
ALTER TABLE tutor_profiles DROP COLUMN IF EXISTS custom_data;
|
||||
ALTER TABLE makeup_artist_profiles DROP COLUMN IF EXISTS custom_data;
|
||||
ALTER TABLE developer_profiles DROP COLUMN IF EXISTS custom_data;
|
||||
ALTER TABLE video_editor_profiles DROP COLUMN IF EXISTS custom_data;
|
||||
ALTER TABLE graphic_designer_profiles DROP COLUMN IF EXISTS custom_data;
|
||||
ALTER TABLE social_media_manager_profiles DROP COLUMN IF EXISTS custom_data;
|
||||
ALTER TABLE fitness_trainer_profiles DROP COLUMN IF EXISTS custom_data;
|
||||
ALTER TABLE catering_service_profiles DROP COLUMN IF EXISTS custom_data;
|
||||
ALTER TABLE ugc_content_creator_profiles DROP COLUMN IF EXISTS custom_data;
|
||||
|
||||
-- Drop old profile columns that are now in user_role_profiles
|
||||
ALTER TABLE photographer_profiles DROP COLUMN IF EXISTS display_name;
|
||||
ALTER TABLE photographer_profiles DROP COLUMN IF EXISTS bio;
|
||||
ALTER TABLE photographer_profiles DROP COLUMN IF EXISTS location;
|
||||
ALTER TABLE photographer_profiles DROP COLUMN IF EXISTS status;
|
||||
ALTER TABLE photographer_profiles DROP COLUMN IF EXISTS rejection_reason;
|
||||
ALTER TABLE photographer_profiles DROP COLUMN IF EXISTS approved_at;
|
||||
|
||||
ALTER TABLE tutor_profiles DROP COLUMN IF EXISTS display_name;
|
||||
ALTER TABLE tutor_profiles DROP COLUMN IF EXISTS bio;
|
||||
ALTER TABLE tutor_profiles DROP COLUMN IF EXISTS location;
|
||||
ALTER TABLE tutor_profiles DROP COLUMN IF EXISTS status;
|
||||
ALTER TABLE tutor_profiles DROP COLUMN IF EXISTS rejection_reason;
|
||||
ALTER TABLE tutor_profiles DROP COLUMN IF EXISTS approved_at;
|
||||
|
||||
ALTER TABLE makeup_artist_profiles DROP COLUMN IF EXISTS display_name;
|
||||
ALTER TABLE makeup_artist_profiles DROP COLUMN IF EXISTS bio;
|
||||
ALTER TABLE makeup_artist_profiles DROP COLUMN IF EXISTS location;
|
||||
ALTER TABLE makeup_artist_profiles DROP COLUMN IF EXISTS status;
|
||||
ALTER TABLE makeup_artist_profiles DROP COLUMN IF EXISTS rejection_reason;
|
||||
ALTER TABLE makeup_artist_profiles DROP COLUMN IF EXISTS approved_at;
|
||||
|
||||
ALTER TABLE developer_profiles DROP COLUMN IF EXISTS display_name;
|
||||
ALTER TABLE developer_profiles DROP COLUMN IF EXISTS bio;
|
||||
ALTER TABLE developer_profiles DROP COLUMN IF EXISTS location;
|
||||
ALTER TABLE developer_profiles DROP COLUMN IF EXISTS status;
|
||||
ALTER TABLE developer_profiles DROP COLUMN IF EXISTS rejection_reason;
|
||||
ALTER TABLE developer_profiles DROP COLUMN IF EXISTS approved_at;
|
||||
|
||||
ALTER TABLE video_editor_profiles DROP COLUMN IF EXISTS display_name;
|
||||
ALTER TABLE video_editor_profiles DROP COLUMN IF EXISTS bio;
|
||||
ALTER TABLE video_editor_profiles DROP COLUMN IF EXISTS location;
|
||||
ALTER TABLE video_editor_profiles DROP COLUMN IF EXISTS status;
|
||||
ALTER TABLE video_editor_profiles DROP COLUMN IF EXISTS rejection_reason;
|
||||
ALTER TABLE video_editor_profiles DROP COLUMN IF EXISTS approved_at;
|
||||
|
||||
ALTER TABLE graphic_designer_profiles DROP COLUMN IF EXISTS display_name;
|
||||
ALTER TABLE graphic_designer_profiles DROP COLUMN IF EXISTS bio;
|
||||
ALTER TABLE graphic_designer_profiles DROP COLUMN IF EXISTS location;
|
||||
ALTER TABLE graphic_designer_profiles DROP COLUMN IF EXISTS status;
|
||||
ALTER TABLE graphic_designer_profiles DROP COLUMN IF EXISTS rejection_reason;
|
||||
ALTER TABLE graphic_designer_profiles DROP COLUMN IF EXISTS approved_at;
|
||||
|
||||
ALTER TABLE social_media_manager_profiles DROP COLUMN IF EXISTS display_name;
|
||||
ALTER TABLE social_media_manager_profiles DROP COLUMN IF EXISTS bio;
|
||||
ALTER TABLE social_media_manager_profiles DROP COLUMN IF EXISTS location;
|
||||
ALTER TABLE social_media_manager_profiles DROP COLUMN IF EXISTS status;
|
||||
ALTER TABLE social_media_manager_profiles DROP COLUMN IF EXISTS rejection_reason;
|
||||
ALTER TABLE social_media_manager_profiles DROP COLUMN IF EXISTS approved_at;
|
||||
|
||||
ALTER TABLE fitness_trainer_profiles DROP COLUMN IF EXISTS display_name;
|
||||
ALTER TABLE fitness_trainer_profiles DROP COLUMN IF EXISTS bio;
|
||||
ALTER TABLE fitness_trainer_profiles DROP COLUMN IF EXISTS location;
|
||||
ALTER TABLE fitness_trainer_profiles DROP COLUMN IF EXISTS status;
|
||||
ALTER TABLE fitness_trainer_profiles DROP COLUMN IF EXISTS rejection_reason;
|
||||
ALTER TABLE fitness_trainer_profiles DROP COLUMN IF EXISTS approved_at;
|
||||
|
||||
ALTER TABLE catering_service_profiles DROP COLUMN IF EXISTS bio;
|
||||
ALTER TABLE catering_service_profiles DROP COLUMN IF EXISTS location;
|
||||
ALTER TABLE catering_service_profiles DROP COLUMN IF EXISTS status;
|
||||
ALTER TABLE catering_service_profiles DROP COLUMN IF EXISTS rejection_reason;
|
||||
ALTER TABLE catering_service_profiles DROP COLUMN IF EXISTS approved_at;
|
||||
|
||||
ALTER TABLE ugc_content_creator_profiles DROP COLUMN IF EXISTS display_name;
|
||||
ALTER TABLE ugc_content_creator_profiles DROP COLUMN IF EXISTS bio;
|
||||
ALTER TABLE ugc_content_creator_profiles DROP COLUMN IF EXISTS location;
|
||||
ALTER TABLE ugc_content_creator_profiles DROP COLUMN IF EXISTS status;
|
||||
ALTER TABLE ugc_content_creator_profiles DROP COLUMN IF EXISTS rejection_reason;
|
||||
ALTER TABLE ugc_content_creator_profiles DROP COLUMN IF EXISTS approved_at;
|
||||
|
||||
COMMIT;
|
||||
|
||||
-- ============================================================================
|
||||
-- Migration Complete
|
||||
-- ============================================================================
|
||||
|
|
@ -3,63 +3,77 @@ use serde::{Deserialize, Serialize};
|
|||
use sqlx::{FromRow, PgPool};
|
||||
use uuid::Uuid;
|
||||
|
||||
// catering_service_profiles uses "business_name" instead of "display_name"
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, FromRow)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
|
||||
pub struct CateringServiceProfile {
|
||||
pub id: Uuid,
|
||||
pub user_id: Uuid,
|
||||
pub user_role_profile_id: Uuid,
|
||||
pub business_name: Option<String>,
|
||||
pub bio: Option<String>,
|
||||
pub location: Option<String>,
|
||||
pub custom_data: Option<serde_json::Value>,
|
||||
pub status: String,
|
||||
pub cuisine_types: Vec<String>,
|
||||
pub event_types: Vec<String>,
|
||||
pub min_guests: Option<i32>,
|
||||
pub max_guests: Option<i32>,
|
||||
pub has_setup_team: bool,
|
||||
pub has_serving_staff: bool,
|
||||
pub price_per_head_inr: Option<i32>,
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub updated_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct UpsertCateringServiceProfilePayload {
|
||||
pub business_name: Option<String>,
|
||||
pub bio: Option<String>,
|
||||
pub location: Option<String>,
|
||||
pub custom_data: Option<serde_json::Value>,
|
||||
pub cuisine_types: Vec<String>,
|
||||
pub event_types: Vec<String>,
|
||||
pub min_guests: Option<i32>,
|
||||
pub max_guests: Option<i32>,
|
||||
pub has_setup_team: bool,
|
||||
pub has_serving_staff: bool,
|
||||
pub price_per_head_inr: Option<i32>,
|
||||
}
|
||||
|
||||
pub struct CateringServiceRepository;
|
||||
|
||||
impl CateringServiceRepository {
|
||||
pub async fn get_by_user_id(pool: &PgPool, user_id: Uuid) -> Result<Option<CateringServiceProfile>, sqlx::Error> {
|
||||
pub async fn get_by_user_role_id(pool: &PgPool, user_role_profile_id: Uuid) -> Result<Option<CateringServiceProfile>, sqlx::Error> {
|
||||
sqlx::query_as::<_, CateringServiceProfile>(
|
||||
r#"SELECT id, user_id, business_name, bio, location,
|
||||
custom_data,
|
||||
status, created_at, updated_at
|
||||
FROM catering_service_profiles WHERE user_id = $1"#,
|
||||
r#"SELECT id, user_role_profile_id, business_name, cuisine_types, event_types,
|
||||
min_guests, max_guests, has_setup_team, has_serving_staff,
|
||||
price_per_head_inr, created_at, updated_at
|
||||
FROM catering_service_profiles WHERE user_role_profile_id = $1"#,
|
||||
)
|
||||
.bind(user_id)
|
||||
.bind(user_role_profile_id)
|
||||
.fetch_optional(pool)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn upsert(pool: &PgPool, user_id: Uuid, p: UpsertCateringServiceProfilePayload) -> Result<CateringServiceProfile, sqlx::Error> {
|
||||
pub async fn upsert(pool: &PgPool, user_role_profile_id: Uuid, p: UpsertCateringServiceProfilePayload) -> Result<CateringServiceProfile, sqlx::Error> {
|
||||
sqlx::query_as::<_, CateringServiceProfile>(
|
||||
r#"INSERT INTO catering_service_profiles (user_id, business_name, bio, location, custom_data)
|
||||
VALUES ($1, $2, $3, $4, $5)
|
||||
ON CONFLICT (user_id) DO UPDATE SET
|
||||
business_name = COALESCE(EXCLUDED.business_name, catering_service_profiles.business_name),
|
||||
bio = EXCLUDED.bio,
|
||||
location = EXCLUDED.location,
|
||||
custom_data = EXCLUDED.custom_data,
|
||||
updated_at = NOW()
|
||||
RETURNING id, user_id, business_name, bio, location,
|
||||
custom_data,
|
||||
status, created_at, updated_at"#,
|
||||
r#"INSERT INTO catering_service_profiles (user_role_profile_id, business_name, cuisine_types, event_types,
|
||||
min_guests, max_guests, has_setup_team, has_serving_staff, price_per_head_inr)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
|
||||
ON CONFLICT (user_role_profile_id) DO UPDATE SET
|
||||
business_name = EXCLUDED.business_name,
|
||||
cuisine_types = COALESCE(EXCLUDED.cuisine_types, catering_service_profiles.cuisine_types),
|
||||
event_types = COALESCE(EXCLUDED.event_types, catering_service_profiles.event_types),
|
||||
min_guests = EXCLUDED.min_guests,
|
||||
max_guests = EXCLUDED.max_guests,
|
||||
has_setup_team = EXCLUDED.has_setup_team,
|
||||
has_serving_staff = EXCLUDED.has_serving_staff,
|
||||
price_per_head_inr = EXCLUDED.price_per_head_inr,
|
||||
updated_at = NOW()
|
||||
RETURNING id, user_role_profile_id, business_name, cuisine_types, event_types,
|
||||
min_guests, max_guests, has_setup_team, has_serving_staff,
|
||||
price_per_head_inr, created_at, updated_at"#,
|
||||
)
|
||||
.bind(user_id)
|
||||
.bind(p.business_name)
|
||||
.bind(p.bio)
|
||||
.bind(p.location)
|
||||
.bind(p.custom_data)
|
||||
.bind(user_role_profile_id)
|
||||
.bind(&p.business_name)
|
||||
.bind(&p.cuisine_types)
|
||||
.bind(&p.event_types)
|
||||
.bind(p.min_guests)
|
||||
.bind(p.max_guests)
|
||||
.bind(p.has_setup_team)
|
||||
.bind(p.has_serving_staff)
|
||||
.bind(p.price_per_head_inr)
|
||||
.fetch_one(pool)
|
||||
.await
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,61 +3,63 @@ use serde::{Deserialize, Serialize};
|
|||
use sqlx::{FromRow, PgPool};
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, FromRow)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
|
||||
pub struct DeveloperProfile {
|
||||
pub id: Uuid,
|
||||
pub user_id: Uuid,
|
||||
pub display_name: Option<String>,
|
||||
pub bio: Option<String>,
|
||||
pub location: Option<String>,
|
||||
pub custom_data: Option<serde_json::Value>,
|
||||
pub status: String,
|
||||
pub user_role_profile_id: Uuid,
|
||||
pub tech_stack: Vec<String>,
|
||||
pub experience_years: Option<i32>,
|
||||
pub availability: String,
|
||||
pub hourly_rate_inr: Option<i32>,
|
||||
pub remote_ok: bool,
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub updated_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct UpsertDeveloperProfilePayload {
|
||||
pub display_name: Option<String>,
|
||||
pub bio: Option<String>,
|
||||
pub location: Option<String>,
|
||||
pub custom_data: Option<serde_json::Value>,
|
||||
pub tech_stack: Vec<String>,
|
||||
pub experience_years: Option<i32>,
|
||||
pub availability: String,
|
||||
pub hourly_rate_inr: Option<i32>,
|
||||
pub remote_ok: bool,
|
||||
}
|
||||
|
||||
pub struct DeveloperRepository;
|
||||
|
||||
impl DeveloperRepository {
|
||||
pub async fn get_by_user_id(pool: &PgPool, user_id: Uuid) -> Result<Option<DeveloperProfile>, sqlx::Error> {
|
||||
pub async fn get_by_user_role_id(pool: &PgPool, user_role_profile_id: Uuid) -> Result<Option<DeveloperProfile>, sqlx::Error> {
|
||||
sqlx::query_as::<_, DeveloperProfile>(
|
||||
r#"SELECT id, user_id, display_name, bio, location,
|
||||
custom_data,
|
||||
status, created_at, updated_at
|
||||
FROM developer_profiles WHERE user_id = $1"#,
|
||||
r#"SELECT id, user_role_profile_id, tech_stack, experience_years, availability,
|
||||
hourly_rate_inr, remote_ok, created_at, updated_at
|
||||
FROM developer_profiles WHERE user_role_profile_id = $1"#,
|
||||
)
|
||||
.bind(user_id)
|
||||
.bind(user_role_profile_id)
|
||||
.fetch_optional(pool)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn upsert(pool: &PgPool, user_id: Uuid, p: UpsertDeveloperProfilePayload) -> Result<DeveloperProfile, sqlx::Error> {
|
||||
pub async fn upsert(pool: &PgPool, user_role_profile_id: Uuid, p: UpsertDeveloperProfilePayload) -> Result<DeveloperProfile, sqlx::Error> {
|
||||
sqlx::query_as::<_, DeveloperProfile>(
|
||||
r#"INSERT INTO developer_profiles (user_id, display_name, bio, location, custom_data)
|
||||
VALUES ($1, $2, $3, $4, $5)
|
||||
ON CONFLICT (user_id) DO UPDATE SET
|
||||
display_name = COALESCE(EXCLUDED.display_name, developer_profiles.display_name),
|
||||
bio = EXCLUDED.bio,
|
||||
location = EXCLUDED.location,
|
||||
custom_data = EXCLUDED.custom_data,
|
||||
updated_at = NOW()
|
||||
RETURNING id, user_id, display_name, bio, location,
|
||||
custom_data,
|
||||
status, created_at, updated_at"#,
|
||||
r#"INSERT INTO developer_profiles (user_role_profile_id, tech_stack, experience_years,
|
||||
availability, hourly_rate_inr, remote_ok)
|
||||
VALUES ($1, $2, $3, $4, $5, $6)
|
||||
ON CONFLICT (user_role_profile_id) DO UPDATE SET
|
||||
tech_stack = COALESCE(EXCLUDED.tech_stack, developer_profiles.tech_stack),
|
||||
experience_years = EXCLUDED.experience_years,
|
||||
availability = EXCLUDED.availability,
|
||||
hourly_rate_inr = EXCLUDED.hourly_rate_inr,
|
||||
remote_ok = EXCLUDED.remote_ok,
|
||||
updated_at = NOW()
|
||||
RETURNING id, user_role_profile_id, tech_stack, experience_years, availability,
|
||||
hourly_rate_inr, remote_ok, created_at, updated_at"#,
|
||||
)
|
||||
.bind(user_id)
|
||||
.bind(p.display_name)
|
||||
.bind(p.bio)
|
||||
.bind(p.location)
|
||||
.bind(p.custom_data)
|
||||
.bind(user_role_profile_id)
|
||||
.bind(&p.tech_stack)
|
||||
.bind(p.experience_years)
|
||||
.bind(&p.availability)
|
||||
.bind(p.hourly_rate_inr)
|
||||
.bind(p.remote_ok)
|
||||
.fetch_one(pool)
|
||||
.await
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,61 +3,67 @@ use serde::{Deserialize, Serialize};
|
|||
use sqlx::{FromRow, PgPool};
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, FromRow)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
|
||||
pub struct FitnessTrainerProfile {
|
||||
pub id: Uuid,
|
||||
pub user_id: Uuid,
|
||||
pub display_name: Option<String>,
|
||||
pub bio: Option<String>,
|
||||
pub location: Option<String>,
|
||||
pub custom_data: Option<serde_json::Value>,
|
||||
pub status: String,
|
||||
pub user_role_profile_id: Uuid,
|
||||
pub disciplines: Vec<String>,
|
||||
pub certifications: Vec<String>,
|
||||
pub online_sessions: bool,
|
||||
pub home_visits: bool,
|
||||
pub gym_based: bool,
|
||||
pub per_session_rate_inr: Option<i32>,
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub updated_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct UpsertFitnessTrainerProfilePayload {
|
||||
pub display_name: Option<String>,
|
||||
pub bio: Option<String>,
|
||||
pub location: Option<String>,
|
||||
pub custom_data: Option<serde_json::Value>,
|
||||
pub disciplines: Vec<String>,
|
||||
pub certifications: Vec<String>,
|
||||
pub online_sessions: bool,
|
||||
pub home_visits: bool,
|
||||
pub gym_based: bool,
|
||||
pub per_session_rate_inr: Option<i32>,
|
||||
}
|
||||
|
||||
pub struct FitnessTrainerRepository;
|
||||
|
||||
impl FitnessTrainerRepository {
|
||||
pub async fn get_by_user_id(pool: &PgPool, user_id: Uuid) -> Result<Option<FitnessTrainerProfile>, sqlx::Error> {
|
||||
pub async fn get_by_user_role_id(pool: &PgPool, user_role_profile_id: Uuid) -> Result<Option<FitnessTrainerProfile>, sqlx::Error> {
|
||||
sqlx::query_as::<_, FitnessTrainerProfile>(
|
||||
r#"SELECT id, user_id, display_name, bio, location,
|
||||
custom_data,
|
||||
status, created_at, updated_at
|
||||
FROM fitness_trainer_profiles WHERE user_id = $1"#,
|
||||
r#"SELECT id, user_role_profile_id, disciplines, certifications, online_sessions,
|
||||
home_visits, gym_based, per_session_rate_inr, created_at, updated_at
|
||||
FROM fitness_trainer_profiles WHERE user_role_profile_id = $1"#,
|
||||
)
|
||||
.bind(user_id)
|
||||
.bind(user_role_profile_id)
|
||||
.fetch_optional(pool)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn upsert(pool: &PgPool, user_id: Uuid, p: UpsertFitnessTrainerProfilePayload) -> Result<FitnessTrainerProfile, sqlx::Error> {
|
||||
pub async fn upsert(pool: &PgPool, user_role_profile_id: Uuid, p: UpsertFitnessTrainerProfilePayload) -> Result<FitnessTrainerProfile, sqlx::Error> {
|
||||
sqlx::query_as::<_, FitnessTrainerProfile>(
|
||||
r#"INSERT INTO fitness_trainer_profiles (user_id, display_name, bio, location, custom_data)
|
||||
VALUES ($1, $2, $3, $4, $5)
|
||||
ON CONFLICT (user_id) DO UPDATE SET
|
||||
display_name = COALESCE(EXCLUDED.display_name, fitness_trainer_profiles.display_name),
|
||||
bio = EXCLUDED.bio,
|
||||
location = EXCLUDED.location,
|
||||
custom_data = EXCLUDED.custom_data,
|
||||
updated_at = NOW()
|
||||
RETURNING id, user_id, display_name, bio, location,
|
||||
custom_data,
|
||||
status, created_at, updated_at"#,
|
||||
r#"INSERT INTO fitness_trainer_profiles (user_role_profile_id, disciplines, certifications,
|
||||
online_sessions, home_visits, gym_based, per_session_rate_inr)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
||||
ON CONFLICT (user_role_profile_id) DO UPDATE SET
|
||||
disciplines = COALESCE(EXCLUDED.disciplines, fitness_trainer_profiles.disciplines),
|
||||
certifications = COALESCE(EXCLUDED.certifications, fitness_trainer_profiles.certifications),
|
||||
online_sessions = EXCLUDED.online_sessions,
|
||||
home_visits = EXCLUDED.home_visits,
|
||||
gym_based = EXCLUDED.gym_based,
|
||||
per_session_rate_inr = EXCLUDED.per_session_rate_inr,
|
||||
updated_at = NOW()
|
||||
RETURNING id, user_role_profile_id, disciplines, certifications, online_sessions,
|
||||
home_visits, gym_based, per_session_rate_inr, created_at, updated_at"#,
|
||||
)
|
||||
.bind(user_id)
|
||||
.bind(p.display_name)
|
||||
.bind(p.bio)
|
||||
.bind(p.location)
|
||||
.bind(p.custom_data)
|
||||
.bind(user_role_profile_id)
|
||||
.bind(&p.disciplines)
|
||||
.bind(&p.certifications)
|
||||
.bind(p.online_sessions)
|
||||
.bind(p.home_visits)
|
||||
.bind(p.gym_based)
|
||||
.bind(p.per_session_rate_inr)
|
||||
.fetch_one(pool)
|
||||
.await
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,61 +3,59 @@ use serde::{Deserialize, Serialize};
|
|||
use sqlx::{FromRow, PgPool};
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, FromRow)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
|
||||
pub struct GraphicDesignerProfile {
|
||||
pub id: Uuid,
|
||||
pub user_id: Uuid,
|
||||
pub display_name: Option<String>,
|
||||
pub bio: Option<String>,
|
||||
pub location: Option<String>,
|
||||
pub custom_data: Option<serde_json::Value>,
|
||||
pub status: String,
|
||||
pub user_role_profile_id: Uuid,
|
||||
pub design_tools: Vec<String>,
|
||||
pub style_tags: Vec<String>,
|
||||
pub brand_experience: bool,
|
||||
pub starting_price_inr: Option<i32>,
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub updated_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct UpsertGraphicDesignerProfilePayload {
|
||||
pub display_name: Option<String>,
|
||||
pub bio: Option<String>,
|
||||
pub location: Option<String>,
|
||||
pub custom_data: Option<serde_json::Value>,
|
||||
pub design_tools: Vec<String>,
|
||||
pub style_tags: Vec<String>,
|
||||
pub brand_experience: bool,
|
||||
pub starting_price_inr: Option<i32>,
|
||||
}
|
||||
|
||||
pub struct GraphicDesignerRepository;
|
||||
|
||||
impl GraphicDesignerRepository {
|
||||
pub async fn get_by_user_id(pool: &PgPool, user_id: Uuid) -> Result<Option<GraphicDesignerProfile>, sqlx::Error> {
|
||||
pub async fn get_by_user_role_id(pool: &PgPool, user_role_profile_id: Uuid) -> Result<Option<GraphicDesignerProfile>, sqlx::Error> {
|
||||
sqlx::query_as::<_, GraphicDesignerProfile>(
|
||||
r#"SELECT id, user_id, display_name, bio, location,
|
||||
custom_data,
|
||||
status, created_at, updated_at
|
||||
FROM graphic_designer_profiles WHERE user_id = $1"#,
|
||||
r#"SELECT id, user_role_profile_id, design_tools, style_tags, brand_experience,
|
||||
starting_price_inr, created_at, updated_at
|
||||
FROM graphic_designer_profiles WHERE user_role_profile_id = $1"#,
|
||||
)
|
||||
.bind(user_id)
|
||||
.bind(user_role_profile_id)
|
||||
.fetch_optional(pool)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn upsert(pool: &PgPool, user_id: Uuid, p: UpsertGraphicDesignerProfilePayload) -> Result<GraphicDesignerProfile, sqlx::Error> {
|
||||
pub async fn upsert(pool: &PgPool, user_role_profile_id: Uuid, p: UpsertGraphicDesignerProfilePayload) -> Result<GraphicDesignerProfile, sqlx::Error> {
|
||||
sqlx::query_as::<_, GraphicDesignerProfile>(
|
||||
r#"INSERT INTO graphic_designer_profiles (user_id, display_name, bio, location, custom_data)
|
||||
r#"INSERT INTO graphic_designer_profiles (user_role_profile_id, design_tools, style_tags,
|
||||
brand_experience, starting_price_inr)
|
||||
VALUES ($1, $2, $3, $4, $5)
|
||||
ON CONFLICT (user_id) DO UPDATE SET
|
||||
display_name = COALESCE(EXCLUDED.display_name, graphic_designer_profiles.display_name),
|
||||
bio = EXCLUDED.bio,
|
||||
location = EXCLUDED.location,
|
||||
custom_data = EXCLUDED.custom_data,
|
||||
updated_at = NOW()
|
||||
RETURNING id, user_id, display_name, bio, location,
|
||||
custom_data,
|
||||
status, created_at, updated_at"#,
|
||||
ON CONFLICT (user_role_profile_id) DO UPDATE SET
|
||||
design_tools = COALESCE(EXCLUDED.design_tools, graphic_designer_profiles.design_tools),
|
||||
style_tags = COALESCE(EXCLUDED.style_tags, graphic_designer_profiles.style_tags),
|
||||
brand_experience = EXCLUDED.brand_experience,
|
||||
starting_price_inr = EXCLUDED.starting_price_inr,
|
||||
updated_at = NOW()
|
||||
RETURNING id, user_role_profile_id, design_tools, style_tags, brand_experience,
|
||||
starting_price_inr, created_at, updated_at"#,
|
||||
)
|
||||
.bind(user_id)
|
||||
.bind(p.display_name)
|
||||
.bind(p.bio)
|
||||
.bind(p.location)
|
||||
.bind(p.custom_data)
|
||||
.bind(user_role_profile_id)
|
||||
.bind(&p.design_tools)
|
||||
.bind(&p.style_tags)
|
||||
.bind(p.brand_experience)
|
||||
.bind(p.starting_price_inr)
|
||||
.fetch_one(pool)
|
||||
.await
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,61 +3,65 @@ use serde::{Deserialize, Serialize};
|
|||
use sqlx::{FromRow, PgPool};
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, FromRow)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
|
||||
pub struct MakeupArtistProfile {
|
||||
pub id: Uuid,
|
||||
pub user_id: Uuid,
|
||||
pub display_name: Option<String>,
|
||||
pub bio: Option<String>,
|
||||
pub location: Option<String>,
|
||||
pub custom_data: Option<serde_json::Value>,
|
||||
pub status: String,
|
||||
pub user_role_profile_id: Uuid,
|
||||
pub specializations: Vec<String>,
|
||||
pub kit_brands: Vec<String>,
|
||||
pub home_service: bool,
|
||||
pub studio_available: bool,
|
||||
pub starting_price_inr: Option<i32>,
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub updated_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct UpsertMakeupArtistProfilePayload {
|
||||
pub display_name: Option<String>,
|
||||
pub bio: Option<String>,
|
||||
pub location: Option<String>,
|
||||
pub custom_data: Option<serde_json::Value>,
|
||||
pub specializations: Vec<String>,
|
||||
pub kit_brands: Vec<String>,
|
||||
pub home_service: bool,
|
||||
pub studio_available: bool,
|
||||
pub starting_price_inr: Option<i32>,
|
||||
}
|
||||
|
||||
pub struct MakeupArtistRepository;
|
||||
|
||||
impl MakeupArtistRepository {
|
||||
pub async fn get_by_user_id(pool: &PgPool, user_id: Uuid) -> Result<Option<MakeupArtistProfile>, sqlx::Error> {
|
||||
pub async fn get_by_user_role_id(pool: &PgPool, user_role_profile_id: Uuid) -> Result<Option<MakeupArtistProfile>, sqlx::Error> {
|
||||
sqlx::query_as::<_, MakeupArtistProfile>(
|
||||
r#"SELECT id, user_id, display_name, bio, location,
|
||||
custom_data,
|
||||
status, created_at, updated_at
|
||||
FROM makeup_artist_profiles WHERE user_id = $1"#,
|
||||
r#"SELECT id, user_role_profile_id, specializations, kit_brands,
|
||||
home_service, studio_available, starting_price_inr,
|
||||
created_at, updated_at
|
||||
FROM makeup_artist_profiles WHERE user_role_profile_id = $1"#,
|
||||
)
|
||||
.bind(user_id)
|
||||
.bind(user_role_profile_id)
|
||||
.fetch_optional(pool)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn upsert(pool: &PgPool, user_id: Uuid, p: UpsertMakeupArtistProfilePayload) -> Result<MakeupArtistProfile, sqlx::Error> {
|
||||
pub async fn upsert(pool: &PgPool, user_role_profile_id: Uuid, p: UpsertMakeupArtistProfilePayload) -> Result<MakeupArtistProfile, sqlx::Error> {
|
||||
sqlx::query_as::<_, MakeupArtistProfile>(
|
||||
r#"INSERT INTO makeup_artist_profiles (user_id, display_name, bio, location, custom_data)
|
||||
VALUES ($1, $2, $3, $4, $5)
|
||||
ON CONFLICT (user_id) DO UPDATE SET
|
||||
display_name = COALESCE(EXCLUDED.display_name, makeup_artist_profiles.display_name),
|
||||
bio = EXCLUDED.bio,
|
||||
location = EXCLUDED.location,
|
||||
custom_data = EXCLUDED.custom_data,
|
||||
updated_at = NOW()
|
||||
RETURNING id, user_id, display_name, bio, location,
|
||||
custom_data,
|
||||
status, created_at, updated_at"#,
|
||||
r#"INSERT INTO makeup_artist_profiles (user_role_profile_id, specializations, kit_brands,
|
||||
home_service, studio_available, starting_price_inr)
|
||||
VALUES ($1, $2, $3, $4, $5, $6)
|
||||
ON CONFLICT (user_role_profile_id) DO UPDATE SET
|
||||
specializations = COALESCE(EXCLUDED.specializations, makeup_artist_profiles.specializations),
|
||||
kit_brands = COALESCE(EXCLUDED.kit_brands, makeup_artist_profiles.kit_brands),
|
||||
home_service = EXCLUDED.home_service,
|
||||
studio_available = EXCLUDED.studio_available,
|
||||
starting_price_inr = EXCLUDED.starting_price_inr,
|
||||
updated_at = NOW()
|
||||
RETURNING id, user_role_profile_id, specializations, kit_brands,
|
||||
home_service, studio_available, starting_price_inr,
|
||||
created_at, updated_at"#,
|
||||
)
|
||||
.bind(user_id)
|
||||
.bind(p.display_name)
|
||||
.bind(p.bio)
|
||||
.bind(p.location)
|
||||
.bind(p.custom_data)
|
||||
.bind(user_role_profile_id)
|
||||
.bind(&p.specializations)
|
||||
.bind(&p.kit_brands)
|
||||
.bind(p.home_service)
|
||||
.bind(p.studio_available)
|
||||
.bind(p.starting_price_inr)
|
||||
.fetch_one(pool)
|
||||
.await
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,61 +3,63 @@ use serde::{Deserialize, Serialize};
|
|||
use sqlx::{FromRow, PgPool};
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, FromRow)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
|
||||
pub struct SocialMediaManagerProfile {
|
||||
pub id: Uuid,
|
||||
pub user_id: Uuid,
|
||||
pub display_name: Option<String>,
|
||||
pub bio: Option<String>,
|
||||
pub location: Option<String>,
|
||||
pub custom_data: Option<serde_json::Value>,
|
||||
pub status: String,
|
||||
pub user_role_profile_id: Uuid,
|
||||
pub platforms: Vec<String>,
|
||||
pub industries: Vec<String>,
|
||||
pub content_types: Vec<String>,
|
||||
pub avg_follower_growth_pct: Option<i32>,
|
||||
pub starting_price_inr: Option<i32>,
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub updated_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct UpsertSocialMediaManagerProfilePayload {
|
||||
pub display_name: Option<String>,
|
||||
pub bio: Option<String>,
|
||||
pub location: Option<String>,
|
||||
pub custom_data: Option<serde_json::Value>,
|
||||
pub platforms: Vec<String>,
|
||||
pub industries: Vec<String>,
|
||||
pub content_types: Vec<String>,
|
||||
pub avg_follower_growth_pct: Option<i32>,
|
||||
pub starting_price_inr: Option<i32>,
|
||||
}
|
||||
|
||||
pub struct SocialMediaManagerRepository;
|
||||
|
||||
impl SocialMediaManagerRepository {
|
||||
pub async fn get_by_user_id(pool: &PgPool, user_id: Uuid) -> Result<Option<SocialMediaManagerProfile>, sqlx::Error> {
|
||||
pub async fn get_by_user_role_id(pool: &PgPool, user_role_profile_id: Uuid) -> Result<Option<SocialMediaManagerProfile>, sqlx::Error> {
|
||||
sqlx::query_as::<_, SocialMediaManagerProfile>(
|
||||
r#"SELECT id, user_id, display_name, bio, location,
|
||||
custom_data,
|
||||
status, created_at, updated_at
|
||||
FROM social_media_manager_profiles WHERE user_id = $1"#,
|
||||
r#"SELECT id, user_role_profile_id, platforms, industries, content_types,
|
||||
avg_follower_growth_pct, starting_price_inr, created_at, updated_at
|
||||
FROM social_media_manager_profiles WHERE user_role_profile_id = $1"#,
|
||||
)
|
||||
.bind(user_id)
|
||||
.bind(user_role_profile_id)
|
||||
.fetch_optional(pool)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn upsert(pool: &PgPool, user_id: Uuid, p: UpsertSocialMediaManagerProfilePayload) -> Result<SocialMediaManagerProfile, sqlx::Error> {
|
||||
pub async fn upsert(pool: &PgPool, user_role_profile_id: Uuid, p: UpsertSocialMediaManagerProfilePayload) -> Result<SocialMediaManagerProfile, sqlx::Error> {
|
||||
sqlx::query_as::<_, SocialMediaManagerProfile>(
|
||||
r#"INSERT INTO social_media_manager_profiles (user_id, display_name, bio, location, custom_data)
|
||||
VALUES ($1, $2, $3, $4, $5)
|
||||
ON CONFLICT (user_id) DO UPDATE SET
|
||||
display_name = COALESCE(EXCLUDED.display_name, social_media_manager_profiles.display_name),
|
||||
bio = EXCLUDED.bio,
|
||||
location = EXCLUDED.location,
|
||||
custom_data = EXCLUDED.custom_data,
|
||||
updated_at = NOW()
|
||||
RETURNING id, user_id, display_name, bio, location,
|
||||
custom_data,
|
||||
status, created_at, updated_at"#,
|
||||
r#"INSERT INTO social_media_manager_profiles (user_role_profile_id, platforms, industries,
|
||||
content_types, avg_follower_growth_pct, starting_price_inr)
|
||||
VALUES ($1, $2, $3, $4, $5, $6)
|
||||
ON CONFLICT (user_role_profile_id) DO UPDATE SET
|
||||
platforms = COALESCE(EXCLUDED.platforms, social_media_manager_profiles.platforms),
|
||||
industries = COALESCE(EXCLUDED.industries, social_media_manager_profiles.industries),
|
||||
content_types = COALESCE(EXCLUDED.content_types, social_media_manager_profiles.content_types),
|
||||
avg_follower_growth_pct = EXCLUDED.avg_follower_growth_pct,
|
||||
starting_price_inr = EXCLUDED.starting_price_inr,
|
||||
updated_at = NOW()
|
||||
RETURNING id, user_role_profile_id, platforms, industries, content_types,
|
||||
avg_follower_growth_pct, starting_price_inr, created_at, updated_at"#,
|
||||
)
|
||||
.bind(user_id)
|
||||
.bind(p.display_name)
|
||||
.bind(p.bio)
|
||||
.bind(p.location)
|
||||
.bind(p.custom_data)
|
||||
.bind(user_role_profile_id)
|
||||
.bind(&p.platforms)
|
||||
.bind(&p.industries)
|
||||
.bind(&p.content_types)
|
||||
.bind(p.avg_follower_growth_pct)
|
||||
.bind(p.starting_price_inr)
|
||||
.fetch_one(pool)
|
||||
.await
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,61 +3,73 @@ use serde::{Deserialize, Serialize};
|
|||
use sqlx::{FromRow, PgPool};
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, FromRow)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
|
||||
pub struct TutorProfile {
|
||||
pub id: Uuid,
|
||||
pub user_id: Uuid,
|
||||
pub display_name: Option<String>,
|
||||
pub bio: Option<String>,
|
||||
pub location: Option<String>,
|
||||
pub custom_data: Option<serde_json::Value>,
|
||||
pub status: String,
|
||||
pub user_role_profile_id: Uuid,
|
||||
pub subjects: Vec<String>,
|
||||
pub board_types: Vec<String>,
|
||||
pub qualification: Option<String>,
|
||||
pub teaches_online: bool,
|
||||
pub teaches_offline: bool,
|
||||
pub experience_years: Option<i32>,
|
||||
pub hourly_rate_inr: Option<i32>,
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub updated_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct UpsertTutorProfilePayload {
|
||||
pub display_name: Option<String>,
|
||||
pub bio: Option<String>,
|
||||
pub location: Option<String>,
|
||||
pub custom_data: Option<serde_json::Value>,
|
||||
pub subjects: Vec<String>,
|
||||
pub board_types: Vec<String>,
|
||||
pub qualification: Option<String>,
|
||||
pub teaches_online: bool,
|
||||
pub teaches_offline: bool,
|
||||
pub experience_years: Option<i32>,
|
||||
pub hourly_rate_inr: Option<i32>,
|
||||
}
|
||||
|
||||
pub struct TutorRepository;
|
||||
|
||||
impl TutorRepository {
|
||||
pub async fn get_by_user_id(pool: &PgPool, user_id: Uuid) -> Result<Option<TutorProfile>, sqlx::Error> {
|
||||
pub async fn get_by_user_role_id(pool: &PgPool, user_role_profile_id: Uuid) -> Result<Option<TutorProfile>, sqlx::Error> {
|
||||
sqlx::query_as::<_, TutorProfile>(
|
||||
r#"SELECT id, user_id, display_name, bio, location,
|
||||
custom_data,
|
||||
status, created_at, updated_at
|
||||
FROM tutor_profiles WHERE user_id = $1"#,
|
||||
r#"SELECT id, user_role_profile_id, subjects, board_types, qualification,
|
||||
teaches_online, teaches_offline, experience_years, hourly_rate_inr,
|
||||
created_at, updated_at
|
||||
FROM tutor_profiles WHERE user_role_profile_id = $1"#,
|
||||
)
|
||||
.bind(user_id)
|
||||
.bind(user_role_profile_id)
|
||||
.fetch_optional(pool)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn upsert(pool: &PgPool, user_id: Uuid, p: UpsertTutorProfilePayload) -> Result<TutorProfile, sqlx::Error> {
|
||||
pub async fn upsert(pool: &PgPool, user_role_profile_id: Uuid, p: UpsertTutorProfilePayload) -> Result<TutorProfile, sqlx::Error> {
|
||||
sqlx::query_as::<_, TutorProfile>(
|
||||
r#"INSERT INTO tutor_profiles (user_id, display_name, bio, location, custom_data)
|
||||
VALUES ($1, $2, $3, $4, $5)
|
||||
ON CONFLICT (user_id) DO UPDATE SET
|
||||
display_name = COALESCE(EXCLUDED.display_name, tutor_profiles.display_name),
|
||||
bio = EXCLUDED.bio,
|
||||
location = EXCLUDED.location,
|
||||
custom_data = EXCLUDED.custom_data,
|
||||
updated_at = NOW()
|
||||
RETURNING id, user_id, display_name, bio, location,
|
||||
custom_data,
|
||||
status, created_at, updated_at"#,
|
||||
r#"INSERT INTO tutor_profiles (user_role_profile_id, subjects, board_types, qualification,
|
||||
teaches_online, teaches_offline, experience_years, hourly_rate_inr)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
|
||||
ON CONFLICT (user_role_profile_id) DO UPDATE SET
|
||||
subjects = COALESCE(EXCLUDED.subjects, tutor_profiles.subjects),
|
||||
board_types = COALESCE(EXCLUDED.board_types, tutor_profiles.board_types),
|
||||
qualification = EXCLUDED.qualification,
|
||||
teaches_online = EXCLUDED.teaches_online,
|
||||
teaches_offline = EXCLUDED.teaches_offline,
|
||||
experience_years = EXCLUDED.experience_years,
|
||||
hourly_rate_inr = EXCLUDED.hourly_rate_inr,
|
||||
updated_at = NOW()
|
||||
RETURNING id, user_role_profile_id, subjects, board_types, qualification,
|
||||
teaches_online, teaches_offline, experience_years, hourly_rate_inr,
|
||||
created_at, updated_at"#,
|
||||
)
|
||||
.bind(user_id)
|
||||
.bind(p.display_name)
|
||||
.bind(p.bio)
|
||||
.bind(p.location)
|
||||
.bind(p.custom_data)
|
||||
.bind(user_role_profile_id)
|
||||
.bind(&p.subjects)
|
||||
.bind(&p.board_types)
|
||||
.bind(&p.qualification)
|
||||
.bind(p.teaches_online)
|
||||
.bind(p.teaches_offline)
|
||||
.bind(p.experience_years)
|
||||
.bind(p.hourly_rate_inr)
|
||||
.fetch_one(pool)
|
||||
.await
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,61 +3,63 @@ use serde::{Deserialize, Serialize};
|
|||
use sqlx::{FromRow, PgPool};
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, FromRow)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
|
||||
pub struct UgcContentCreatorProfile {
|
||||
pub id: Uuid,
|
||||
pub user_id: Uuid,
|
||||
pub display_name: Option<String>,
|
||||
pub bio: Option<String>,
|
||||
pub location: Option<String>,
|
||||
pub custom_data: Option<serde_json::Value>,
|
||||
pub status: String,
|
||||
pub user_role_profile_id: Uuid,
|
||||
pub niche_tags: Vec<String>,
|
||||
pub content_formats: Vec<String>,
|
||||
pub platforms: Vec<String>,
|
||||
pub turnaround_days: Option<i32>,
|
||||
pub starting_price_inr: Option<i32>,
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub updated_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct UpsertUgcContentCreatorProfilePayload {
|
||||
pub display_name: Option<String>,
|
||||
pub bio: Option<String>,
|
||||
pub location: Option<String>,
|
||||
pub custom_data: Option<serde_json::Value>,
|
||||
pub niche_tags: Vec<String>,
|
||||
pub content_formats: Vec<String>,
|
||||
pub platforms: Vec<String>,
|
||||
pub turnaround_days: Option<i32>,
|
||||
pub starting_price_inr: Option<i32>,
|
||||
}
|
||||
|
||||
pub struct UgcContentCreatorRepository;
|
||||
|
||||
impl UgcContentCreatorRepository {
|
||||
pub async fn get_by_user_id(pool: &PgPool, user_id: Uuid) -> Result<Option<UgcContentCreatorProfile>, sqlx::Error> {
|
||||
pub async fn get_by_user_role_id(pool: &PgPool, user_role_profile_id: Uuid) -> Result<Option<UgcContentCreatorProfile>, sqlx::Error> {
|
||||
sqlx::query_as::<_, UgcContentCreatorProfile>(
|
||||
r#"SELECT id, user_id, display_name, bio, location,
|
||||
custom_data,
|
||||
status, created_at, updated_at
|
||||
FROM ugc_content_creator_profiles WHERE user_id = $1"#,
|
||||
r#"SELECT id, user_role_profile_id, niche_tags, content_formats, platforms,
|
||||
turnaround_days, starting_price_inr, created_at, updated_at
|
||||
FROM ugc_content_creator_profiles WHERE user_role_profile_id = $1"#,
|
||||
)
|
||||
.bind(user_id)
|
||||
.bind(user_role_profile_id)
|
||||
.fetch_optional(pool)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn upsert(pool: &PgPool, user_id: Uuid, p: UpsertUgcContentCreatorProfilePayload) -> Result<UgcContentCreatorProfile, sqlx::Error> {
|
||||
pub async fn upsert(pool: &PgPool, user_role_profile_id: Uuid, p: UpsertUgcContentCreatorProfilePayload) -> Result<UgcContentCreatorProfile, sqlx::Error> {
|
||||
sqlx::query_as::<_, UgcContentCreatorProfile>(
|
||||
r#"INSERT INTO ugc_content_creator_profiles (user_id, display_name, bio, location, custom_data)
|
||||
VALUES ($1, $2, $3, $4, $5)
|
||||
ON CONFLICT (user_id) DO UPDATE SET
|
||||
display_name = COALESCE(EXCLUDED.display_name, ugc_content_creator_profiles.display_name),
|
||||
bio = EXCLUDED.bio,
|
||||
location = EXCLUDED.location,
|
||||
custom_data = EXCLUDED.custom_data,
|
||||
updated_at = NOW()
|
||||
RETURNING id, user_id, display_name, bio, location,
|
||||
custom_data,
|
||||
status, created_at, updated_at"#,
|
||||
r#"INSERT INTO ugc_content_creator_profiles (user_role_profile_id, niche_tags, content_formats,
|
||||
platforms, turnaround_days, starting_price_inr)
|
||||
VALUES ($1, $2, $3, $4, $5, $6)
|
||||
ON CONFLICT (user_role_profile_id) DO UPDATE SET
|
||||
niche_tags = COALESCE(EXCLUDED.niche_tags, ugc_content_creator_profiles.niche_tags),
|
||||
content_formats = COALESCE(EXCLUDED.content_formats, ugc_content_creator_profiles.content_formats),
|
||||
platforms = COALESCE(EXCLUDED.platforms, ugc_content_creator_profiles.platforms),
|
||||
turnaround_days = EXCLUDED.turnaround_days,
|
||||
starting_price_inr = EXCLUDED.starting_price_inr,
|
||||
updated_at = NOW()
|
||||
RETURNING id, user_role_profile_id, niche_tags, content_formats, platforms,
|
||||
turnaround_days, starting_price_inr, created_at, updated_at"#,
|
||||
)
|
||||
.bind(user_id)
|
||||
.bind(p.display_name)
|
||||
.bind(p.bio)
|
||||
.bind(p.location)
|
||||
.bind(p.custom_data)
|
||||
.bind(user_role_profile_id)
|
||||
.bind(&p.niche_tags)
|
||||
.bind(&p.content_formats)
|
||||
.bind(&p.platforms)
|
||||
.bind(p.turnaround_days)
|
||||
.bind(p.starting_price_inr)
|
||||
.fetch_one(pool)
|
||||
.await
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,61 +3,59 @@ use serde::{Deserialize, Serialize};
|
|||
use sqlx::{FromRow, PgPool};
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, FromRow)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
|
||||
pub struct VideoEditorProfile {
|
||||
pub id: Uuid,
|
||||
pub user_id: Uuid,
|
||||
pub display_name: Option<String>,
|
||||
pub bio: Option<String>,
|
||||
pub location: Option<String>,
|
||||
pub custom_data: Option<serde_json::Value>,
|
||||
pub status: String,
|
||||
pub user_role_profile_id: Uuid,
|
||||
pub software_skills: Vec<String>,
|
||||
pub style_tags: Vec<String>,
|
||||
pub turnaround_days: Option<i32>,
|
||||
pub starting_price_inr: Option<i32>,
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub updated_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct UpsertVideoEditorProfilePayload {
|
||||
pub display_name: Option<String>,
|
||||
pub bio: Option<String>,
|
||||
pub location: Option<String>,
|
||||
pub custom_data: Option<serde_json::Value>,
|
||||
pub software_skills: Vec<String>,
|
||||
pub style_tags: Vec<String>,
|
||||
pub turnaround_days: Option<i32>,
|
||||
pub starting_price_inr: Option<i32>,
|
||||
}
|
||||
|
||||
pub struct VideoEditorRepository;
|
||||
|
||||
impl VideoEditorRepository {
|
||||
pub async fn get_by_user_id(pool: &PgPool, user_id: Uuid) -> Result<Option<VideoEditorProfile>, sqlx::Error> {
|
||||
pub async fn get_by_user_role_id(pool: &PgPool, user_role_profile_id: Uuid) -> Result<Option<VideoEditorProfile>, sqlx::Error> {
|
||||
sqlx::query_as::<_, VideoEditorProfile>(
|
||||
r#"SELECT id, user_id, display_name, bio, location,
|
||||
custom_data,
|
||||
status, created_at, updated_at
|
||||
FROM video_editor_profiles WHERE user_id = $1"#,
|
||||
r#"SELECT id, user_role_profile_id, software_skills, style_tags, turnaround_days,
|
||||
starting_price_inr, created_at, updated_at
|
||||
FROM video_editor_profiles WHERE user_role_profile_id = $1"#,
|
||||
)
|
||||
.bind(user_id)
|
||||
.bind(user_role_profile_id)
|
||||
.fetch_optional(pool)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn upsert(pool: &PgPool, user_id: Uuid, p: UpsertVideoEditorProfilePayload) -> Result<VideoEditorProfile, sqlx::Error> {
|
||||
pub async fn upsert(pool: &PgPool, user_role_profile_id: Uuid, p: UpsertVideoEditorProfilePayload) -> Result<VideoEditorProfile, sqlx::Error> {
|
||||
sqlx::query_as::<_, VideoEditorProfile>(
|
||||
r#"INSERT INTO video_editor_profiles (user_id, display_name, bio, location, custom_data)
|
||||
r#"INSERT INTO video_editor_profiles (user_role_profile_id, software_skills, style_tags,
|
||||
turnaround_days, starting_price_inr)
|
||||
VALUES ($1, $2, $3, $4, $5)
|
||||
ON CONFLICT (user_id) DO UPDATE SET
|
||||
display_name = COALESCE(EXCLUDED.display_name, video_editor_profiles.display_name),
|
||||
bio = EXCLUDED.bio,
|
||||
location = EXCLUDED.location,
|
||||
custom_data = EXCLUDED.custom_data,
|
||||
updated_at = NOW()
|
||||
RETURNING id, user_id, display_name, bio, location,
|
||||
custom_data,
|
||||
status, created_at, updated_at"#,
|
||||
ON CONFLICT (user_role_profile_id) DO UPDATE SET
|
||||
software_skills = COALESCE(EXCLUDED.software_skills, video_editor_profiles.software_skills),
|
||||
style_tags = COALESCE(EXCLUDED.style_tags, video_editor_profiles.style_tags),
|
||||
turnaround_days = EXCLUDED.turnaround_days,
|
||||
starting_price_inr = EXCLUDED.starting_price_inr,
|
||||
updated_at = NOW()
|
||||
RETURNING id, user_role_profile_id, software_skills, style_tags, turnaround_days,
|
||||
starting_price_inr, created_at, updated_at"#,
|
||||
)
|
||||
.bind(user_id)
|
||||
.bind(p.display_name)
|
||||
.bind(p.bio)
|
||||
.bind(p.location)
|
||||
.bind(p.custom_data)
|
||||
.bind(user_role_profile_id)
|
||||
.bind(&p.software_skills)
|
||||
.bind(&p.style_tags)
|
||||
.bind(p.turnaround_days)
|
||||
.bind(p.starting_price_inr)
|
||||
.fetch_one(pool)
|
||||
.await
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue