Updated DB schema
This commit is contained in:
parent
36c94a90df
commit
edeb172a6c
Binary file not shown.
|
After Width: | Height: | Size: 467 KiB |
File diff suppressed because one or more lines are too long
250
db/schema.sql
250
db/schema.sql
|
|
@ -7,6 +7,7 @@ CREATE TABLE IF NOT EXISTS users (
|
|||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
name VARCHAR(255) NOT NULL,
|
||||
phone VARCHAR(50) UNIQUE NOT NULL,
|
||||
location_id UUID NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
|
@ -22,29 +23,98 @@ CREATE TABLE IF NOT EXISTS breeds (
|
|||
id SERIAL PRIMARY KEY,
|
||||
species_id INT NOT NULL REFERENCES species(id) ON DELETE RESTRICT,
|
||||
name VARCHAR(100) NOT NULL,
|
||||
description TEXT,
|
||||
description_text TEXT,
|
||||
description_media_url TEXT,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
-- Constraint: Only one type of description should be allowed
|
||||
CONSTRAINT chk_description_type CHECK (
|
||||
(description_text IS NOT NULL AND description_media_url IS NULL)
|
||||
OR (description_text IS NULL AND description_media_url IS NOT NULL)
|
||||
OR (description_text IS NULL AND description_media_url IS NULL)
|
||||
)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS locations (
|
||||
|
||||
-------------------------------------------------------
|
||||
-- ENUM for location type
|
||||
-------------------------------------------------------
|
||||
CREATE TYPE location_type_enum AS ENUM ('farm', 'home', 'other');
|
||||
CREATE TYPE location_source_type_enum AS ENUM (
|
||||
'gps',
|
||||
'manual',
|
||||
'imported',
|
||||
'unknown'
|
||||
);
|
||||
|
||||
|
||||
CREATE TABLE IF NOT EXISTS location_details (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
user_id UUID REFERENCES users(id) ON DELETE SET NULL,
|
||||
is_saved_address BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
location_type VARCHAR(50),
|
||||
location_type location_type_enum NOT NULL,
|
||||
country VARCHAR(100),
|
||||
state VARCHAR(100),
|
||||
district VARCHAR(100),
|
||||
city_village VARCHAR(150),
|
||||
pincode VARCHAR(20),
|
||||
lat DECIMAL(10,7),
|
||||
lng DECIMAL(10,7),
|
||||
source_type VARCHAR(50) NOT NULL DEFAULT 'unknown',
|
||||
source_type location_source_type_enum NOT NULL DEFAULT 'unknown',
|
||||
source_confidence VARCHAR(50) NOT NULL DEFAULT 'medium',
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS user_locations (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
lat DECIMAL(10,7) NOT NULL,
|
||||
lng DECIMAL(10,7) NOT NULL,
|
||||
location_detail_id UUID UNIQUE REFERENCES location_details(id) ON DELETE SET NULL,
|
||||
selected_location BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- Selected location is unique per user, so this trigger ensures that only one location is selected per user
|
||||
CREATE OR REPLACE FUNCTION enforce_single_selected_location()
|
||||
RETURNS trigger AS $$
|
||||
BEGIN
|
||||
-- When changing a location to selected (only act if actually setting to TRUE)
|
||||
IF NEW.selected_location = TRUE THEN
|
||||
-- Unselect all other locations for this user (excluding the current row)
|
||||
UPDATE user_locations
|
||||
SET selected_location = FALSE
|
||||
WHERE user_id = NEW.user_id
|
||||
AND id <> NEW.id
|
||||
AND selected_location = TRUE;
|
||||
END IF;
|
||||
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE TRIGGER trg_single_selected_location
|
||||
BEFORE INSERT OR UPDATE ON user_locations
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION enforce_single_selected_location();
|
||||
|
||||
|
||||
-------------------------------------------------------
|
||||
-- Add FK constraint after both tables exist
|
||||
-------------------------------------------------------
|
||||
ALTER TABLE users
|
||||
ADD CONSTRAINT fk_users_location
|
||||
FOREIGN KEY (location_id)
|
||||
REFERENCES user_locations(id)
|
||||
ON DELETE SET NULL;
|
||||
|
||||
-------------------------------------------------------
|
||||
-- Indexes
|
||||
-------------------------------------------------------
|
||||
CREATE INDEX idx_user_locations_user ON user_locations(user_id);
|
||||
CREATE INDEX idx_user_locations_selected ON user_locations(selected_location);
|
||||
CREATE INDEX idx_location_details_type ON location_details(location_type);
|
||||
CREATE INDEX idx_user_locations_location_detail ON user_locations(location_detail_id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS animals (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
species_id INT NOT NULL REFERENCES species(id) ON DELETE RESTRICT,
|
||||
|
|
@ -64,12 +134,42 @@ CREATE TABLE IF NOT EXISTS animals (
|
|||
ear_tag_no VARCHAR(100),
|
||||
description TEXT,
|
||||
suggested_care TEXT,
|
||||
location_id UUID REFERENCES locations(id) ON DELETE SET NULL,
|
||||
location_id UUID REFERENCES user_locations(id) ON DELETE SET NULL,
|
||||
created_from VARCHAR(50) NOT NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TYPE listing_status_enum AS ENUM (
|
||||
'active',
|
||||
'expired',
|
||||
'hidden',
|
||||
'sold',
|
||||
'deleted'
|
||||
);
|
||||
|
||||
CREATE TYPE listing_score_status_enum AS ENUM (
|
||||
'pending',
|
||||
'calculating',
|
||||
'completed',
|
||||
'failed'
|
||||
);
|
||||
|
||||
CREATE TYPE listing_type_enum AS ENUM (
|
||||
'sale',
|
||||
'stud_service',
|
||||
'adoption',
|
||||
'other'
|
||||
);
|
||||
|
||||
CREATE TYPE seller_type_enum AS ENUM (
|
||||
'owner',
|
||||
'farmer',
|
||||
'broker',
|
||||
'agent',
|
||||
'other'
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS listings (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
seller_id UUID NOT NULL REFERENCES users(id) ON DELETE RESTRICT,
|
||||
|
|
@ -78,15 +178,16 @@ CREATE TABLE IF NOT EXISTS listings (
|
|||
price NUMERIC(12,2) NOT NULL,
|
||||
currency VARCHAR(10) NOT NULL,
|
||||
is_negotiable BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
listing_type VARCHAR(50) NOT NULL,
|
||||
status VARCHAR(50) NOT NULL DEFAULT 'active',
|
||||
listing_type listing_type_enum NOT NULL,
|
||||
status listing_status_enum NOT NULL DEFAULT 'active',
|
||||
listing_score INT NOT NULL DEFAULT 0,
|
||||
views_count INT NOT NULL DEFAULT 0,
|
||||
bookmarks_count INT NOT NULL DEFAULT 0,
|
||||
enquiries_call_count INT NOT NULL DEFAULT 0,
|
||||
enquiries_whatsapp_count INT NOT NULL DEFAULT 0,
|
||||
clicks_count INT NOT NULL DEFAULT 0,
|
||||
listing_score_status VARCHAR(50) NOT NULL DEFAULT 'pending',
|
||||
listing_score_status listing_score_status_enum NOT NULL DEFAULT 'pending',
|
||||
seller_type seller_type_enum NOT NULL DEFAULT 'owner',
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
|
@ -112,3 +213,128 @@ CREATE TABLE IF NOT EXISTS custom_requirements (
|
|||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TYPE message_type_enum AS ENUM ('text', 'media', 'both');
|
||||
CREATE TYPE media_type_enum AS ENUM ('image', 'video', 'audio', 'document');
|
||||
|
||||
CREATE TABLE conversations (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
buyer_id UUID NOT NULL,
|
||||
seller_id UUID NOT NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT NOW(),
|
||||
|
||||
CONSTRAINT fk_conversation_buyer
|
||||
FOREIGN KEY (buyer_id) REFERENCES users(id),
|
||||
|
||||
CONSTRAINT fk_conversation_seller
|
||||
FOREIGN KEY (seller_id) REFERENCES users(id),
|
||||
|
||||
CONSTRAINT unique_buyer_seller UNIQUE (buyer_id, seller_id)
|
||||
);
|
||||
|
||||
CREATE TABLE conversation_media (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
media_type media_type_enum NOT NULL, -- image | video | audio | document
|
||||
media_url TEXT NOT NULL,
|
||||
thumbnail_url TEXT NULL,
|
||||
duration_sec INT NULL, -- audio/video only
|
||||
file_size_kb INT NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE messages (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
conversation_id UUID NOT NULL,
|
||||
sender_id UUID NOT NULL,
|
||||
receiver_id UUID NOT NULL,
|
||||
|
||||
message_type message_type_enum NOT NULL DEFAULT 'text',
|
||||
|
||||
content TEXT NULL, -- nullable for media-only messages
|
||||
media_id UUID UNIQUE, -- FK for media, enforce 1:1 relationship
|
||||
|
||||
is_read BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
read_at TIMESTAMP NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
|
||||
is_deleted BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
|
||||
CONSTRAINT fk_message_conversation
|
||||
FOREIGN KEY (conversation_id) REFERENCES conversations(id)
|
||||
ON DELETE CASCADE,
|
||||
|
||||
CONSTRAINT fk_message_sender
|
||||
FOREIGN KEY (sender_id) REFERENCES users(id),
|
||||
|
||||
CONSTRAINT fk_message_receiver
|
||||
FOREIGN KEY (receiver_id) REFERENCES users(id),
|
||||
|
||||
CONSTRAINT fk_message_media
|
||||
FOREIGN KEY (media_id) REFERENCES conversation_media(id)
|
||||
ON DELETE SET NULL,
|
||||
|
||||
-- Ensures consistency between message_type and media usage
|
||||
CONSTRAINT chk_message_media_relation CHECK (
|
||||
(message_type = 'text' AND media_id IS NULL) OR
|
||||
(message_type = 'media' AND media_id IS NOT NULL AND content IS NULL) OR
|
||||
(message_type = 'both' AND media_id IS NOT NULL AND content IS NOT NULL)
|
||||
)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_conversations_buyer ON conversations(buyer_id);
|
||||
CREATE INDEX idx_conversations_seller ON conversations(seller_id);
|
||||
CREATE INDEX idx_messages_conversation ON messages(conversation_id);
|
||||
CREATE INDEX idx_messages_sender ON messages(sender_id);
|
||||
CREATE INDEX idx_messages_media ON messages(media_id);
|
||||
|
||||
|
||||
CREATE TYPE communication_type_enum AS ENUM ('call', 'missed_call', 'voicemail');
|
||||
CREATE TYPE call_status_enum AS ENUM (
|
||||
'initiated',
|
||||
'ringing',
|
||||
'answered',
|
||||
'completed',
|
||||
'failed',
|
||||
'busy',
|
||||
'no_answer'
|
||||
);
|
||||
|
||||
CREATE TABLE communication_records (
|
||||
communication_id BIGSERIAL PRIMARY KEY,
|
||||
|
||||
conversation_id UUID NOT NULL, -- Conversation this communication belongs to
|
||||
buyer_id UUID NOT NULL, -- Buyer involved
|
||||
seller_id UUID NOT NULL, -- Seller involved
|
||||
request_id UUID NULL, -- Optional: Buy request this communication refers to
|
||||
|
||||
communication_type communication_type_enum NOT NULL,
|
||||
cpaas_call_id VARCHAR(100) NULL, -- Reference ID returned by CPaaS provider
|
||||
|
||||
call_status call_status_enum NOT NULL,
|
||||
|
||||
start_time TIMESTAMP NULL, -- Time CPaaS initiated call
|
||||
end_time TIMESTAMP NULL, -- Time call ended
|
||||
duration_seconds INT DEFAULT 0, -- Duration calculated
|
||||
|
||||
recording_url TEXT NULL, -- Optional CPaaS recording URL
|
||||
metadata JSON NULL, -- Any extra payload
|
||||
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
-- Foreign Keys
|
||||
CONSTRAINT fk_comm_conversation
|
||||
FOREIGN KEY (conversation_id)
|
||||
REFERENCES conversations(id)
|
||||
ON DELETE CASCADE,
|
||||
|
||||
CONSTRAINT fk_comm_buyer
|
||||
FOREIGN KEY (buyer_id)
|
||||
REFERENCES users(id)
|
||||
ON DELETE CASCADE,
|
||||
|
||||
CONSTRAINT fk_comm_seller
|
||||
FOREIGN KEY (seller_id)
|
||||
REFERENCES users(id)
|
||||
ON DELETE CASCADE
|
||||
);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue