lawless/database/migrations/001_init_schema.up.sql

971 行
48 KiB
SQL

此文件含有模棱两可的 Unicode 字符

此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。

-- TDD-04 数据库表结构初始化
-- 适用 PostgreSQL 16 + pg_partman分区扩展可选
-- 命名约定:小写下划线,主键 uuid,金额 numeric(20,4),JSONB 存半结构化数据
CREATE EXTENSION IF NOT EXISTS pgcrypto;
--------------------------------------------------------------------------------
-- 0. 开发参考表(种族、境界、货币静态配置)
--------------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS race_templates (
id VARCHAR(32) PRIMARY KEY,
name VARCHAR(64) NOT NULL,
category VARCHAR(16) NOT NULL DEFAULT 'common', -- common / rare
can_create BOOLEAN NOT NULL DEFAULT true,
description TEXT,
created_at TIMESTAMPTZ DEFAULT NOW()
);
--------------------------------------------------------------------------------
-- 1. 账号与角色
--------------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS players (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
nakama_user_id UUID UNIQUE,
platform VARCHAR(32),
device_id_hash VARCHAR(128) UNIQUE,
status VARCHAR(16) NOT NULL DEFAULT 'active', -- active / banned / deleted
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE TABLE IF NOT EXISTS characters (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
player_id UUID NOT NULL REFERENCES players(id) ON DELETE CASCADE,
name VARCHAR(64) UNIQUE NOT NULL,
race_id VARCHAR(32) NOT NULL,
birth_race_id VARCHAR(32) NOT NULL,
birth_world_tier SMALLINT NOT NULL DEFAULT 1,
world_tier SMALLINT NOT NULL DEFAULT 1,
realm_tier SMALLINT NOT NULL DEFAULT 1,
minor_realm SMALLINT NOT NULL DEFAULT 1,
realm_status VARCHAR(16) NOT NULL DEFAULT 'normal', -- normal / tribulation_pending / breakthrough_ready
level INT NOT NULL DEFAULT 1,
exp BIGINT NOT NULL DEFAULT 0,
power BIGINT NOT NULL DEFAULT 0,
status VARCHAR(16) NOT NULL DEFAULT 'active', -- active / dead / sealed / deleted
base_stats JSONB NOT NULL DEFAULT '{}',
battle_stats JSONB NOT NULL DEFAULT '{}',
san_current SMALLINT NOT NULL DEFAULT 100,
san_max SMALLINT NOT NULL DEFAULT 100,
crime_score INT NOT NULL DEFAULT 0,
heavenly_value INT NOT NULL DEFAULT 0,
karma_value INT NOT NULL DEFAULT 0,
reputation_score INT NOT NULL DEFAULT 0,
mercenary_score INT NOT NULL DEFAULT 0,
hunter_prestige INT NOT NULL DEFAULT 0,
last_online_at TIMESTAMPTZ,
daily_reset_at TIMESTAMPTZ,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX idx_characters_player ON characters(player_id);
CREATE INDEX idx_characters_realm_rank ON characters(world_tier, realm_tier, power DESC);
CREATE INDEX idx_characters_realm_minor ON characters(realm_tier, minor_realm, power DESC);
CREATE INDEX idx_characters_last_online ON characters(last_online_at);
CREATE INDEX idx_characters_status ON characters(status);
CREATE TABLE IF NOT EXISTS character_race_states (
character_id UUID PRIMARY KEY REFERENCES characters(id) ON DELETE CASCADE,
main_race_id VARCHAR(32) NOT NULL,
sub_branch_id VARCHAR(32),
bloodline_data JSONB NOT NULL DEFAULT '{}',
is_rare_race BOOLEAN NOT NULL DEFAULT false,
rebirth_count SMALLINT NOT NULL DEFAULT 0,
conversion_cooldown_until TIMESTAMPTZ,
race_talents JSONB NOT NULL DEFAULT '{}',
hidden_talents JSONB NOT NULL DEFAULT '{}',
updated_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE TABLE IF NOT EXISTS race_currency_wallets (
character_id UUID NOT NULL REFERENCES characters(id) ON DELETE CASCADE,
race_currency_code VARCHAR(32) NOT NULL,
amount NUMERIC(20,4) NOT NULL DEFAULT 0,
total_earned NUMERIC(20,4) NOT NULL DEFAULT 0,
updated_at TIMESTAMPTZ DEFAULT NOW(),
PRIMARY KEY (character_id, race_currency_code)
);
--------------------------------------------------------------------------------
-- 2. 境界
--------------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS realms (
tier SMALLINT PRIMARY KEY,
minor_realm_max SMALLINT NOT NULL DEFAULT 3,
name VARCHAR(64) NOT NULL,
world_tier SMALLINT NOT NULL,
main_currency_code VARCHAR(32) NOT NULL,
is_tribulation_required BOOLEAN NOT NULL DEFAULT true,
base_success_rate NUMERIC(5,4) NOT NULL DEFAULT 0.5,
attr_growth_template JSONB NOT NULL DEFAULT '{}'
);
CREATE TABLE IF NOT EXISTS character_realms (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
character_id UUID NOT NULL REFERENCES characters(id) ON DELETE CASCADE,
realm_tier SMALLINT NOT NULL,
max_minor_reached SMALLINT NOT NULL DEFAULT 1,
exp_in_tier BIGINT NOT NULL DEFAULT 0,
stats_snapshot JSONB NOT NULL DEFAULT '{}',
is_current BOOLEAN NOT NULL DEFAULT false,
updated_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX idx_character_realms_character ON character_realms(character_id);
CREATE INDEX idx_character_realms_current ON character_realms(character_id, is_current);
CREATE TABLE IF NOT EXISTS realm_breakthrough_records (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
character_id UUID NOT NULL REFERENCES characters(id) ON DELETE CASCADE,
from_realm_tier SMALLINT NOT NULL,
to_realm_tier SMALLINT NOT NULL,
from_minor_realm SMALLINT NOT NULL,
to_minor_realm SMALLINT NOT NULL,
is_success BOOLEAN NOT NULL,
is_break_world_barrier BOOLEAN NOT NULL DEFAULT false,
source_world_tier SMALLINT NOT NULL,
target_world_tier SMALLINT NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX idx_realm_breakthrough_character ON realm_breakthrough_records(character_id);
CREATE INDEX idx_realm_breakthrough_created ON realm_breakthrough_records(created_at);
--------------------------------------------------------------------------------
-- 3. 功法与技能
--------------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS manuals (
id VARCHAR(64) PRIMARY KEY,
name VARCHAR(128) NOT NULL,
category VARCHAR(32) NOT NULL, -- universal / profession / racial / unique
domain VARCHAR(32) NOT NULL,
element VARCHAR(16) NOT NULL DEFAULT 'none',
alignment VARCHAR(16) NOT NULL DEFAULT 'neutral',
max_layers SMALLINT NOT NULL DEFAULT 1,
required_race VARCHAR(32)[],
required_profession VARCHAR(32)[],
base_attr JSONB NOT NULL DEFAULT '{}',
skill_unlock_layers JSONB NOT NULL DEFAULT '[]',
version INT NOT NULL DEFAULT 1
);
CREATE TABLE IF NOT EXISTS character_manuals (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
character_id UUID NOT NULL REFERENCES characters(id) ON DELETE CASCADE,
manual_id VARCHAR(64) NOT NULL REFERENCES manuals(id),
instance_data JSONB NOT NULL DEFAULT '{}',
is_buffing BOOLEAN NOT NULL DEFAULT false,
buff_data JSONB NOT NULL DEFAULT '{}',
source_tag VARCHAR(32) NOT NULL DEFAULT 'ORIGINAL',
can_copy_to_jade_slip BOOLEAN NOT NULL DEFAULT false,
copy_count_left SMALLINT NOT NULL DEFAULT 0,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX idx_character_manuals_character ON character_manuals(character_id);
CREATE INDEX idx_character_manuals_buffing ON character_manuals(character_id, is_buffing);
CREATE INDEX idx_character_manuals_manual ON character_manuals(manual_id);
CREATE INDEX idx_character_manuals_instance_gin ON character_manuals USING GIN (instance_data jsonb_path_ops);
CREATE TABLE IF NOT EXISTS skills (
id VARCHAR(64) PRIMARY KEY,
name VARCHAR(128) NOT NULL,
category VARCHAR(32) NOT NULL,
domain VARCHAR(32) NOT NULL,
archetype VARCHAR(32) NOT NULL,
element VARCHAR(16) NOT NULL DEFAULT 'none',
alignment VARCHAR(16) NOT NULL DEFAULT 'neutral',
damage_type VARCHAR(16) NOT NULL DEFAULT 'physical',
scaling_attr VARCHAR(8),
is_eldritch BOOLEAN NOT NULL DEFAULT false,
san_cost SMALLINT NOT NULL DEFAULT 0,
san_gate JSONB NOT NULL DEFAULT '{}',
san_scaling NUMERIC(6,4) NOT NULL DEFAULT 0,
requirements JSONB NOT NULL DEFAULT '{}',
continuous_cost JSONB NOT NULL DEFAULT '{}',
flow VARCHAR(32) NOT NULL DEFAULT 'transcript_jade_ok',
base_template JSONB NOT NULL DEFAULT '{}',
version INT NOT NULL DEFAULT 1
);
CREATE TABLE IF NOT EXISTS character_skills (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
character_id UUID NOT NULL REFERENCES characters(id) ON DELETE CASCADE,
skill_id VARCHAR(64) NOT NULL REFERENCES skills(id),
custom_name VARCHAR(128),
instance_data JSONB NOT NULL DEFAULT '{}',
source_tag VARCHAR(32) NOT NULL DEFAULT 'ORIGINAL',
proficiency INT NOT NULL DEFAULT 0,
is_unique BOOLEAN NOT NULL DEFAULT false,
unique_holder_until TIMESTAMPTZ,
lineage JSONB NOT NULL DEFAULT '{}',
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX idx_character_skills_character ON character_skills(character_id);
CREATE INDEX idx_character_skills_unique ON character_skills(character_id, is_unique);
CREATE INDEX idx_character_skills_skill ON character_skills(skill_id);
CREATE INDEX idx_character_skills_instance_gin ON character_skills USING GIN (instance_data jsonb_path_ops);
--------------------------------------------------------------------------------
-- 4. 物品、装备与经济
--------------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS items (
id VARCHAR(64) PRIMARY KEY,
name VARCHAR(128) NOT NULL,
category VARCHAR(32) NOT NULL,
sub_category VARCHAR(32),
stackable BOOLEAN NOT NULL DEFAULT false,
max_stack INT NOT NULL DEFAULT 1,
bind_type VARCHAR(16) NOT NULL DEFAULT 'none',
world_tier SMALLINT NOT NULL DEFAULT 1,
base_attr_template JSONB NOT NULL DEFAULT '{}',
random_affix_pool JSONB NOT NULL DEFAULT '{}',
script_id VARCHAR(64),
version INT NOT NULL DEFAULT 1
);
CREATE TABLE IF NOT EXISTS inventories (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
character_id UUID NOT NULL REFERENCES characters(id) ON DELETE CASCADE,
item_id VARCHAR(64) NOT NULL REFERENCES items(id),
slot_type VARCHAR(16) NOT NULL DEFAULT 'bag', -- bag / warehouse / mail / auction_escrow
quantity INT NOT NULL DEFAULT 1,
instance_data JSONB NOT NULL DEFAULT '{}',
is_stolen BOOLEAN NOT NULL DEFAULT false,
blood_debt_mark BOOLEAN NOT NULL DEFAULT false,
can_trade BOOLEAN NOT NULL DEFAULT true,
acquired_at TIMESTAMPTZ DEFAULT NOW(),
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX idx_inventories_character_slot ON inventories(character_id, slot_type);
CREATE INDEX idx_inventories_item ON inventories(item_id);
CREATE INDEX idx_inventories_instance_gin ON inventories USING GIN (instance_data jsonb_path_ops);
CREATE TABLE IF NOT EXISTS equipments (
character_id UUID NOT NULL REFERENCES characters(id) ON DELETE CASCADE,
slot_name VARCHAR(32) NOT NULL, -- weapon / head / body / hand / foot / accessory1 / accessory2 / life_item
inventory_id UUID REFERENCES inventories(id) ON DELETE SET NULL,
updated_at TIMESTAMPTZ DEFAULT NOW(),
PRIMARY KEY (character_id, slot_name)
);
CREATE INDEX idx_equipments_inventory ON equipments(inventory_id);
CREATE TABLE IF NOT EXISTS currencies (
code VARCHAR(32) PRIMARY KEY,
name VARCHAR(64) NOT NULL,
world_tier SMALLINT NOT NULL DEFAULT 1,
is_premium BOOLEAN NOT NULL DEFAULT false,
exchange_rules JSONB NOT NULL DEFAULT '{}'
);
CREATE TABLE IF NOT EXISTS currency_balances (
character_id UUID NOT NULL REFERENCES characters(id) ON DELETE CASCADE,
currency_code VARCHAR(32) NOT NULL REFERENCES currencies(code),
amount NUMERIC(20,4) NOT NULL DEFAULT 0,
total_earned NUMERIC(20,4) NOT NULL DEFAULT 0,
total_spent NUMERIC(20,4) NOT NULL DEFAULT 0,
updated_at TIMESTAMPTZ DEFAULT NOW(),
PRIMARY KEY (character_id, currency_code)
);
-- 经济审计日志(建议按 created_at 月分区,保留 12 个月)
CREATE TABLE IF NOT EXISTS economy_audit_logs (
id BIGSERIAL PRIMARY KEY,
character_id UUID REFERENCES characters(id) ON DELETE SET NULL,
entity_type VARCHAR(16) NOT NULL, -- character / guild / system
entity_id UUID NOT NULL,
currency_code VARCHAR(32) NOT NULL,
flow_type VARCHAR(16) NOT NULL, -- faucet / sink / transfer
reason_code VARCHAR(64) NOT NULL,
amount NUMERIC(20,4) NOT NULL,
balance_after NUMERIC(20,4),
related_id UUID,
world_tier SMALLINT NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX idx_economy_audit_character_reason ON economy_audit_logs(character_id, reason_code, created_at);
CREATE INDEX idx_economy_audit_entity ON economy_audit_logs(entity_id, created_at);
CREATE INDEX idx_economy_audit_currency ON economy_audit_logs(currency_code, created_at);
CREATE INDEX idx_economy_audit_flow ON economy_audit_logs(flow_type, created_at);
CREATE INDEX idx_economy_audit_world_tier ON economy_audit_logs(world_tier, created_at);
CREATE INDEX idx_economy_audit_created ON economy_audit_logs(created_at);
COMMENT ON TABLE economy_audit_logs IS '大表建议:按 created_at 月分区,保留 12 个月(可用 pg_partman';
--------------------------------------------------------------------------------
-- 5. 组织(门派/帮派/家族/自建宗门)
--------------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS guilds (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name VARCHAR(128) UNIQUE NOT NULL,
org_type VARCHAR(16) NOT NULL, -- system_sect / player_sect / guild / family
world_tier SMALLINT NOT NULL DEFAULT 1,
region_id UUID,
zone_id UUID,
leader_id UUID NOT NULL REFERENCES characters(id) ON DELETE CASCADE,
level SMALLINT NOT NULL DEFAULT 1,
reputation INT NOT NULL DEFAULT 0,
member_limit INT NOT NULL DEFAULT 50,
tax_rate NUMERIC(5,4) NOT NULL DEFAULT 0.20,
diplomacy_policy JSONB NOT NULL DEFAULT '{}',
building_levels JSONB NOT NULL DEFAULT '{}',
funds JSONB NOT NULL DEFAULT '{}',
status VARCHAR(16) NOT NULL DEFAULT 'active', -- active / dissolved / blacklisted
created_at TIMESTAMPTZ DEFAULT NOW(),
dissolved_at TIMESTAMPTZ
);
CREATE INDEX idx_guilds_type_status ON guilds(org_type, status);
CREATE INDEX idx_guilds_world_tier ON guilds(world_tier, status);
CREATE INDEX idx_guilds_leader ON guilds(leader_id);
CREATE TABLE IF NOT EXISTS guild_members (
guild_id UUID NOT NULL REFERENCES guilds(id) ON DELETE CASCADE,
character_id UUID NOT NULL REFERENCES characters(id) ON DELETE CASCADE,
role VARCHAR(32) NOT NULL DEFAULT 'member', -- leader / vice / elder / elder_council / member / disciple_manager
joined_at TIMESTAMPTZ DEFAULT NOW(),
contribution JSONB NOT NULL DEFAULT '{}',
daily_quota JSONB NOT NULL DEFAULT '{}',
updated_at TIMESTAMPTZ DEFAULT NOW(),
PRIMARY KEY (guild_id, character_id)
);
CREATE INDEX idx_guild_members_character ON guild_members(character_id);
CREATE TABLE IF NOT EXISTS guild_territories (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
guild_id UUID NOT NULL REFERENCES guilds(id) ON DELETE CASCADE,
territory_type VARCHAR(32) NOT NULL,
level SMALLINT NOT NULL DEFAULT 1,
upgrade_progress JSONB NOT NULL DEFAULT '{}',
params JSONB NOT NULL DEFAULT '{}',
updated_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX idx_guild_territories_guild ON guild_territories(guild_id, territory_type);
CREATE TABLE IF NOT EXISTS guild_diplomacy (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
guild_a_id UUID NOT NULL REFERENCES guilds(id) ON DELETE CASCADE,
guild_b_id UUID NOT NULL REFERENCES guilds(id) ON DELETE CASCADE,
relation VARCHAR(16) NOT NULL, -- allied / at_war / trade / betrayed_cooldown
started_at TIMESTAMPTZ DEFAULT NOW(),
ended_at TIMESTAMPTZ
);
CREATE INDEX idx_guild_diplomacy_a ON guild_diplomacy(guild_a_id, relation);
CREATE INDEX idx_guild_diplomacy_b ON guild_diplomacy(guild_b_id, relation);
CREATE TABLE IF NOT EXISTS guild_warehouses (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
guild_id UUID NOT NULL REFERENCES guilds(id) ON DELETE CASCADE,
inventory_id UUID NOT NULL REFERENCES inventories(id) ON DELETE CASCADE,
deposited_by UUID NOT NULL REFERENCES characters(id) ON DELETE CASCADE,
deposit_type VARCHAR(16) NOT NULL DEFAULT 'common', -- common / tribute / black_eat
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX idx_guild_warehouses_guild ON guild_warehouses(guild_id);
--------------------------------------------------------------------------------
-- 6. 市场、拍卖与情报
--------------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS market_orders (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
seller_id UUID NOT NULL REFERENCES characters(id) ON DELETE CASCADE,
seller_guild_id UUID REFERENCES guilds(id) ON DELETE SET NULL,
item_id VARCHAR(64) NOT NULL REFERENCES items(id),
inventory_id UUID NOT NULL REFERENCES inventories(id) ON DELETE CASCADE,
currency_code VARCHAR(32) NOT NULL REFERENCES currencies(code),
unit_price NUMERIC(20,4) NOT NULL,
quantity INT NOT NULL,
total_price NUMERIC(20,4) NOT NULL,
tax_rate NUMERIC(5,4) NOT NULL DEFAULT 0.05,
status VARCHAR(16) NOT NULL DEFAULT 'active', -- active / sold / cancelled / expired
world_tier SMALLINT NOT NULL DEFAULT 1,
listed_at TIMESTAMPTZ DEFAULT NOW(),
expired_at TIMESTAMPTZ,
sold_at TIMESTAMPTZ,
buyer_id UUID REFERENCES characters(id) ON DELETE SET NULL
);
CREATE INDEX idx_market_orders_search ON market_orders(item_id, status, world_tier);
CREATE INDEX idx_market_orders_seller ON market_orders(seller_id, status);
CREATE INDEX idx_market_orders_listed ON market_orders(listed_at);
CREATE INDEX idx_market_orders_status ON market_orders(status, expired_at);
COMMENT ON TABLE market_orders IS '大表建议:按 listed_at 月分区,保留 6 个月';
CREATE TABLE IF NOT EXISTS auctions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
seller_id UUID NOT NULL REFERENCES characters(id) ON DELETE CASCADE,
guild_id UUID REFERENCES guilds(id) ON DELETE SET NULL,
auction_type VARCHAR(16) NOT NULL, -- official / organization
item_id VARCHAR(64) NOT NULL REFERENCES items(id),
inventory_id UUID NOT NULL REFERENCES inventories(id) ON DELETE CASCADE,
category VARCHAR(32) NOT NULL, -- rare_bloodline / rare_manual / jade_slip / secret_material / artifact / material
currency_code VARCHAR(32) NOT NULL REFERENCES currencies(code),
start_price NUMERIC(20,4) NOT NULL,
reserve_price NUMERIC(20,4),
min_increment_rate NUMERIC(5,4) NOT NULL DEFAULT 0.05,
deposit_rate NUMERIC(5,4) NOT NULL DEFAULT 0.05,
tax_rate NUMERIC(5,4) NOT NULL DEFAULT 0.05,
status VARCHAR(16) NOT NULL DEFAULT 'preparing', -- preparing / active / extended / sold / expired / robbed
started_at TIMESTAMPTZ,
ended_at TIMESTAMPTZ,
is_anonymous BOOLEAN NOT NULL DEFAULT false,
access_type VARCHAR(16) NOT NULL DEFAULT 'public',
risk_type VARCHAR(16) NOT NULL DEFAULT 'none', -- rare_robbable / none
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX idx_auctions_list ON auctions(status, auction_type, ended_at);
CREATE INDEX idx_auctions_seller ON auctions(seller_id);
CREATE INDEX idx_auctions_started ON auctions(started_at);
CREATE INDEX idx_auctions_guild ON auctions(guild_id, status);
CREATE TABLE IF NOT EXISTS auction_bids (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
auction_id UUID NOT NULL REFERENCES auctions(id) ON DELETE CASCADE,
bidder_id UUID NOT NULL REFERENCES characters(id) ON DELETE CASCADE,
amount NUMERIC(20,4) NOT NULL,
deposit_paid NUMERIC(20,4) NOT NULL DEFAULT 0,
is_auto_bid BOOLEAN NOT NULL DEFAULT false,
auto_max_amount NUMERIC(20,4),
bid_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX idx_auction_bids_auction ON auction_bids(auction_id, bid_at);
CREATE INDEX idx_auction_bids_bidder ON auction_bids(bidder_id, bid_at);
COMMENT ON TABLE auction_bids IS '大表建议:与 auctions 对齐,按 bid_at 月分区,保留 6 个月';
CREATE TABLE IF NOT EXISTS auction_blacklist_records (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
guild_id UUID NOT NULL REFERENCES guilds(id) ON DELETE CASCADE,
auction_id UUID NOT NULL REFERENCES auctions(id) ON DELETE CASCADE,
is_exposed BOOLEAN NOT NULL DEFAULT false,
exposed_at TIMESTAMPTZ,
exposure_type VARCHAR(16), -- report / arbitration / heaven_sense
punishment_level SMALLINT,
reputation_delta INT,
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX idx_auction_blacklist_guild ON auction_blacklist_records(guild_id);
CREATE TABLE IF NOT EXISTS intelligence_orders (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
seller_id UUID NOT NULL REFERENCES characters(id) ON DELETE CASCADE,
buyer_id UUID REFERENCES characters(id) ON DELETE SET NULL,
intel_type VARCHAR(32) NOT NULL, -- resource / event / location / bounty_clue / instance_entry / unique_skill_clue
target_id UUID,
target_world_tier SMALLINT NOT NULL DEFAULT 1,
content_summary VARCHAR(256),
detail_data JSONB NOT NULL DEFAULT '{}',
currency_code VARCHAR(32) NOT NULL REFERENCES currencies(code),
price NUMERIC(20,4) NOT NULL,
purchase_count SMALLINT NOT NULL DEFAULT 0,
max_purchase SMALLINT NOT NULL DEFAULT 1,
status VARCHAR(16) NOT NULL DEFAULT 'active', -- active / sold_out / expired
cooldown_until TIMESTAMPTZ,
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX idx_intelligence_type ON intelligence_orders(intel_type, status);
CREATE INDEX idx_intelligence_target_tier ON intelligence_orders(target_world_tier, status);
CREATE INDEX idx_intelligence_seller ON intelligence_orders(seller_id);
CREATE INDEX idx_intelligence_created ON intelligence_orders(created_at);
--------------------------------------------------------------------------------
-- 7. 弟子系统
--------------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS disciples (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
owner_id UUID NOT NULL REFERENCES characters(id) ON DELETE CASCADE,
guild_id UUID REFERENCES guilds(id) ON DELETE SET NULL,
name VARCHAR(64) NOT NULL,
race_id VARCHAR(32) NOT NULL,
quality VARCHAR(16) NOT NULL DEFAULT 'common', -- common / fine / excellent / perfect / immortal
profession VARCHAR(32),
stats JSONB NOT NULL DEFAULT '{}',
skills JSONB NOT NULL DEFAULT '{}',
equipment JSONB NOT NULL DEFAULT '{}',
daily_task_quota_min INT NOT NULL DEFAULT 120,
death_rate_modifier NUMERIC(5,4) NOT NULL DEFAULT 0,
status VARCHAR(16) NOT NULL DEFAULT 'active', -- active / dead / dispatched / insurance_pending
tombstone_data JSONB NOT NULL DEFAULT '{}',
created_at TIMESTAMPTZ DEFAULT NOW(),
died_at TIMESTAMPTZ
);
CREATE INDEX idx_disciples_owner ON disciples(owner_id, status);
CREATE INDEX idx_disciples_guild ON disciples(guild_id);
CREATE TABLE IF NOT EXISTS disciple_missions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
disciple_id UUID NOT NULL REFERENCES disciples(id) ON DELETE CASCADE,
owner_id UUID NOT NULL REFERENCES characters(id) ON DELETE CASCADE,
contract_id UUID, -- FK deferred to contracts creation below
mission_type VARCHAR(16) NOT NULL, -- mercenary / gathering / sect_proxy
difficulty SMALLINT NOT NULL DEFAULT 1,
success_rate NUMERIC(5,4) NOT NULL DEFAULT 0,
death_rate NUMERIC(5,4) NOT NULL DEFAULT 0,
insurance_item_id UUID REFERENCES inventories(id) ON DELETE SET NULL,
result VARCHAR(16) NOT NULL DEFAULT 'pending', -- pending / success / fail / death
rewards JSONB NOT NULL DEFAULT '{}',
started_at TIMESTAMPTZ DEFAULT NOW(),
ended_at TIMESTAMPTZ
);
CREATE INDEX idx_disciple_missions_disciple ON disciple_missions(disciple_id, result);
CREATE INDEX idx_disciple_missions_owner ON disciple_missions(owner_id, started_at);
--------------------------------------------------------------------------------
-- 8. 悬赏、追杀令与佣兵委托
--------------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS contracts (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
contract_type VARCHAR(32) NOT NULL, -- mercenary / limited_time / bounty
publisher_id UUID NOT NULL REFERENCES characters(id) ON DELETE CASCADE,
publisher_guild_id UUID REFERENCES guilds(id) ON DELETE SET NULL,
difficulty SMALLINT NOT NULL DEFAULT 1,
currency_code VARCHAR(32) NOT NULL REFERENCES currencies(code),
base_reward NUMERIC(20,4) NOT NULL,
max_participants SMALLINT NOT NULL DEFAULT 1,
status VARCHAR(16) NOT NULL DEFAULT 'active', -- active / accepted / completed / failed / expired / cancelled
valid_until TIMESTAMPTZ NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW(),
completed_at TIMESTAMPTZ
);
CREATE INDEX idx_contracts_publisher ON contracts(publisher_id, contract_type, status);
CREATE INDEX idx_contracts_status_until ON contracts(status, valid_until);
CREATE TABLE IF NOT EXISTS bounties (
contract_id UUID PRIMARY KEY REFERENCES contracts(id) ON DELETE CASCADE,
bounty_type VARCHAR(16) NOT NULL, -- official_bounty / private_bounty / manhunt
target_id UUID REFERENCES characters(id) ON DELETE CASCADE,
target_world_tier SMALLINT NOT NULL DEFAULT 1,
reward_amount NUMERIC(20,4) NOT NULL,
fee_amount NUMERIC(20,4) NOT NULL DEFAULT 0,
is_anonymous BOOLEAN NOT NULL DEFAULT false,
report_count_today SMALLINT NOT NULL DEFAULT 0,
trigger_probability NUMERIC(5,4) NOT NULL DEFAULT 0,
clues JSONB NOT NULL DEFAULT '{}',
item_inventory_id UUID REFERENCES inventories(id) ON DELETE SET NULL
);
CREATE INDEX idx_bounties_target ON bounties(target_id, bounty_type, target_world_tier);
CREATE INDEX idx_bounties_type ON bounties(bounty_type, target_world_tier);
CREATE TABLE IF NOT EXISTS contract_participants (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
contract_id UUID NOT NULL REFERENCES contracts(id) ON DELETE CASCADE,
character_id UUID NOT NULL REFERENCES characters(id) ON DELETE CASCADE,
participant_type VARCHAR(16) NOT NULL, -- hunter / disciple_proxy / party_member
disciple_id UUID REFERENCES disciples(id) ON DELETE SET NULL,
status VARCHAR(16) NOT NULL DEFAULT 'accepted', -- accepted / active / completed / failed
accepted_at TIMESTAMPTZ DEFAULT NOW(),
completed_at TIMESTAMPTZ
);
CREATE INDEX idx_contract_participants_contract ON contract_participants(contract_id, status);
CREATE INDEX idx_contract_participants_character ON contract_participants(character_id, status);
CREATE TABLE IF NOT EXISTS manhunt_records (
bounty_id UUID PRIMARY KEY REFERENCES bounties(contract_id) ON DELETE CASCADE,
original_owner_id UUID NOT NULL REFERENCES characters(id) ON DELETE CASCADE,
current_holder_id UUID NOT NULL REFERENCES characters(id) ON DELETE CASCADE,
window_end_at TIMESTAMPTZ NOT NULL,
outcome VARCHAR(16) NOT NULL DEFAULT 'pending', -- pending / returned / black_ate / failed
hunter_choice VARCHAR(16),
compensation_paid NUMERIC(20,4) DEFAULT 0,
chase_right_until TIMESTAMPTZ,
created_at TIMESTAMPTZ DEFAULT NOW()
);
--------------------------------------------------------------------------------
-- 9. 世界地图、区域、副本与遗迹
--------------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS world_layers (
tier SMALLINT PRIMARY KEY,
name VARCHAR(64) NOT NULL,
display_layer SMALLINT NOT NULL DEFAULT 0,
realm_tier SMALLINT NOT NULL DEFAULT 1,
is_pvp_enabled BOOLEAN NOT NULL DEFAULT false,
cross_tier_rules JSONB NOT NULL DEFAULT '{}'
);
CREATE TABLE IF NOT EXISTS maps (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
world_tier SMALLINT NOT NULL REFERENCES world_layers(tier),
name VARCHAR(128) NOT NULL,
map_type VARCHAR(16) NOT NULL DEFAULT 'world', -- world / event / instance / ruin
seed BIGINT,
layout_data JSONB NOT NULL DEFAULT '{}',
is_active BOOLEAN NOT NULL DEFAULT true,
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX idx_maps_world_tier ON maps(world_tier, map_type, is_active);
CREATE TABLE IF NOT EXISTS regions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
map_id UUID NOT NULL REFERENCES maps(id) ON DELETE CASCADE,
world_tier SMALLINT NOT NULL DEFAULT 1,
name VARCHAR(128) NOT NULL,
theme VARCHAR(32),
is_safe_zone BOOLEAN NOT NULL DEFAULT false,
pvp_rules JSONB NOT NULL DEFAULT '{}'
);
CREATE INDEX idx_regions_map ON regions(map_id, is_safe_zone);
CREATE INDEX idx_regions_world_tier ON regions(world_tier);
CREATE TABLE IF NOT EXISTS zones (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
region_id UUID NOT NULL REFERENCES regions(id) ON DELETE CASCADE,
zone_type VARCHAR(16) NOT NULL, -- stronghold / wild / secret / dungeon / ruins
name VARCHAR(128) NOT NULL,
terrain VARCHAR(32),
weather VARCHAR(32),
resource_density SMALLINT NOT NULL DEFAULT 1,
danger_level SMALLINT NOT NULL DEFAULT 1,
rarity VARCHAR(16) NOT NULL DEFAULT 'normal',
seed BIGINT,
generated_data JSONB NOT NULL DEFAULT '{}',
refreshed_at TIMESTAMPTZ DEFAULT NOW(),
expires_at TIMESTAMPTZ
);
CREATE INDEX idx_zones_region ON zones(region_id, zone_type);
CREATE INDEX idx_zones_expires ON zones(expires_at);
CREATE TABLE IF NOT EXISTS zone_explorations (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
zone_id UUID NOT NULL REFERENCES zones(id) ON DELETE CASCADE,
character_id UUID NOT NULL REFERENCES characters(id) ON DELETE CASCADE,
fog_state JSONB NOT NULL DEFAULT '{}',
exploration_count INT NOT NULL DEFAULT 0,
last_entered_at TIMESTAMPTZ,
UNIQUE (zone_id, character_id)
);
CREATE INDEX idx_zone_explorations_zone ON zone_explorations(zone_id);
CREATE INDEX idx_zone_explorations_character ON zone_explorations(character_id);
--------------------------------------------------------------------------------
-- 10. 战斗与战报
--------------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS battles (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
battle_type VARCHAR(16) NOT NULL, -- expedition_pve / dungeon_pve / pvp / gvg / bounty / manhunt
world_tier SMALLINT NOT NULL DEFAULT 1,
realm_tier SMALLINT NOT NULL DEFAULT 1,
game_timestamp TIMESTAMPTZ DEFAULT NOW(),
attacker_id UUID REFERENCES characters(id) ON DELETE SET NULL,
defender_id UUID REFERENCES characters(id) ON DELETE SET NULL,
party_a UUID[] NOT NULL DEFAULT '{}',
party_b UUID[] NOT NULL DEFAULT '{}',
status VARCHAR(16) NOT NULL DEFAULT 'in_progress', -- in_progress / completed
result_summary JSONB NOT NULL DEFAULT '{}',
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX idx_battles_attacker ON battles(attacker_id, created_at);
CREATE INDEX idx_battles_defender ON battles(defender_id, created_at);
CREATE INDEX idx_battles_type_status ON battles(battle_type, status);
CREATE INDEX idx_battles_realm ON battles(world_tier, realm_tier, created_at);
CREATE TABLE IF NOT EXISTS battle_logs (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
battle_id UUID UNIQUE NOT NULL REFERENCES battles(id) ON DELETE CASCADE,
report JSONB NOT NULL DEFAULT '{}',
special_events JSONB NOT NULL DEFAULT '[]',
drops JSONB NOT NULL DEFAULT '{}',
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX idx_battle_logs_created ON battle_logs(created_at);
CREATE INDEX idx_battle_logs_report_gin ON battle_logs USING GIN (report jsonb_path_ops);
COMMENT ON TABLE battle_logs IS '大表建议:按 created_at 周分区,保留 8 周(可用 pg_partman';
CREATE TABLE IF NOT EXISTS player_kills (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
killer_id UUID NOT NULL REFERENCES characters(id) ON DELETE CASCADE,
victim_id UUID NOT NULL REFERENCES characters(id) ON DELETE CASCADE,
battle_id UUID REFERENCES battles(id) ON DELETE SET NULL,
world_tier SMALLINT NOT NULL DEFAULT 1,
is_malice BOOLEAN NOT NULL DEFAULT false,
karma_delta INT NOT NULL DEFAULT 0,
crime_delta INT NOT NULL DEFAULT 0,
loot_taken JSONB NOT NULL DEFAULT '{}',
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX idx_player_kills_killer ON player_kills(killer_id);
CREATE INDEX idx_player_kills_victim ON player_kills(victim_id);
CREATE INDEX idx_player_kills_world ON player_kills(world_tier, created_at);
--------------------------------------------------------------------------------
-- 11. 副本、遗迹(依赖 battles
--------------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS instances (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
zone_id UUID NOT NULL REFERENCES zones(id) ON DELETE CASCADE,
instance_type VARCHAR(16) NOT NULL, -- permanent / random / ruin / event
theme VARCHAR(32),
world_tier SMALLINT NOT NULL DEFAULT 1,
recommended_realm_tier SMALLINT NOT NULL DEFAULT 1,
generated_layout JSONB NOT NULL DEFAULT '{}',
affixes JSONB NOT NULL DEFAULT '[]',
max_parties SMALLINT NOT NULL DEFAULT 1,
expires_at TIMESTAMPTZ,
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX idx_instances_world_tier ON instances(world_tier, instance_type, expires_at);
CREATE INDEX idx_instances_zone ON instances(zone_id);
CREATE TABLE IF NOT EXISTS instance_runs (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
instance_id UUID NOT NULL REFERENCES instances(id) ON DELETE CASCADE,
party_leader_id UUID NOT NULL REFERENCES characters(id) ON DELETE CASCADE,
party_members UUID[] NOT NULL DEFAULT '{}',
difficulty_coefficient NUMERIC(6,4) NOT NULL DEFAULT 1.0,
status VARCHAR(16) NOT NULL DEFAULT 'in_progress', -- in_progress / completed / failed / abandoned
battle_id UUID REFERENCES battles(id) ON DELETE SET NULL,
consumed_keys SMALLINT NOT NULL DEFAULT 0,
started_at TIMESTAMPTZ DEFAULT NOW(),
ended_at TIMESTAMPTZ
);
CREATE INDEX idx_instance_runs_instance ON instance_runs(instance_id, status);
CREATE INDEX idx_instance_runs_leader ON instance_runs(party_leader_id, started_at);
CREATE TABLE IF NOT EXISTS instance_loot (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
run_id UUID NOT NULL REFERENCES instance_runs(id) ON DELETE CASCADE,
character_id UUID NOT NULL REFERENCES characters(id) ON DELETE CASCADE,
item_id VARCHAR(64) NOT NULL REFERENCES items(id),
inventory_id UUID REFERENCES inventories(id) ON DELETE SET NULL,
currency_code VARCHAR(32) REFERENCES currencies(code),
currency_amount NUMERIC(20,4) DEFAULT 0,
is_resonance_copy BOOLEAN NOT NULL DEFAULT false,
completeness NUMERIC(5,4) DEFAULT 1.0,
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX idx_instance_loot_run ON instance_loot(run_id);
CREATE INDEX idx_instance_loot_character ON instance_loot(character_id);
CREATE TABLE IF NOT EXISTS world_ruin_records (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
source_character_id UUID NOT NULL REFERENCES characters(id) ON DELETE CASCADE,
source_world_tier SMALLINT NOT NULL,
region_id UUID NOT NULL REFERENCES regions(id) ON DELETE CASCADE,
zone_id UUID NOT NULL REFERENCES zones(id) ON DELETE CASCADE,
instance_id UUID REFERENCES instances(id) ON DELETE SET NULL,
richness_score NUMERIC(8,2) NOT NULL DEFAULT 0,
snapshot_data JSONB NOT NULL DEFAULT '{}',
total_extractable NUMERIC(6,4) NOT NULL DEFAULT 0,
extracted_ratio NUMERIC(6,4) NOT NULL DEFAULT 0,
status VARCHAR(16) NOT NULL DEFAULT 'active',
opened_at TIMESTAMPTZ DEFAULT NOW(),
expires_at TIMESTAMPTZ
);
CREATE INDEX idx_world_ruin_source ON world_ruin_records(source_character_id);
CREATE INDEX idx_world_ruin_region ON world_ruin_records(region_id, status);
CREATE INDEX idx_world_ruin_expires ON world_ruin_records(status, expires_at);
--------------------------------------------------------------------------------
-- 12. 渡劫记录
--------------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS tribulation_records (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
character_id UUID NOT NULL REFERENCES characters(id) ON DELETE CASCADE,
realm_tier SMALLINT NOT NULL,
minor_realm SMALLINT NOT NULL,
tribulation_type VARCHAR(16) NOT NULL, -- normal / heart_devil / qi_deviation / chaos / heti
base_success_rate NUMERIC(5,4) NOT NULL,
modified_success_rate NUMERIC(5,4) NOT NULL,
helper_ids UUID[] NOT NULL DEFAULT '{}',
result VARCHAR(16) NOT NULL, -- success / fail / backlash / death
penalties JSONB NOT NULL DEFAULT '{}',
drops_on_success JSONB NOT NULL DEFAULT '[]',
san_snapshot SMALLINT NOT NULL DEFAULT 100,
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX idx_tribulation_character ON tribulation_records(character_id, created_at);
CREATE INDEX idx_tribulation_realm ON tribulation_records(realm_tier, created_at);
--------------------------------------------------------------------------------
-- 13. 社交关系
--------------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS social_relations (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
character_a_id UUID NOT NULL REFERENCES characters(id) ON DELETE CASCADE,
character_b_id UUID NOT NULL REFERENCES characters(id) ON DELETE CASCADE,
relation_type VARCHAR(16) NOT NULL, -- friend / companion / master / apprentice / enemy
intimacy INT NOT NULL DEFAULT 0,
status VARCHAR(16) NOT NULL DEFAULT 'active', -- active / cooldown / dissolved
cooldown_until TIMESTAMPTZ,
created_at TIMESTAMPTZ DEFAULT NOW(),
dissolved_at TIMESTAMPTZ,
UNIQUE (character_a_id, character_b_id, relation_type)
);
CREATE INDEX idx_social_relations_a ON social_relations(character_a_id, relation_type, status);
CREATE INDEX idx_social_relations_b ON social_relations(character_b_id, relation_type, status);
CREATE TABLE IF NOT EXISTS lovers (
relation_id UUID PRIMARY KEY REFERENCES social_relations(id) ON DELETE CASCADE,
resonance INT NOT NULL DEFAULT 0,
shared_residence_id UUID REFERENCES guild_territories(id) ON DELETE SET NULL,
combined_skill_id UUID REFERENCES character_skills(id) ON DELETE SET NULL,
vow_path VARCHAR(16),
teleport_cooldown_until TIMESTAMPTZ,
mutual_benefits JSONB NOT NULL DEFAULT '{}',
updated_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE TABLE IF NOT EXISTS sworn_brothers (
relation_id UUID PRIMARY KEY REFERENCES social_relations(id) ON DELETE CASCADE,
group_id UUID UNIQUE NOT NULL DEFAULT gen_random_uuid(),
member_order SMALLINT NOT NULL DEFAULT 0,
shared_mission_slots JSONB NOT NULL DEFAULT '{}',
group_buffs JSONB NOT NULL DEFAULT '{}',
flag_zone_id UUID REFERENCES zones(id) ON DELETE SET NULL,
updated_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE TABLE IF NOT EXISTS reputation_records (
id BIGSERIAL PRIMARY KEY,
character_id UUID NOT NULL REFERENCES characters(id) ON DELETE CASCADE,
source_id UUID NOT NULL REFERENCES characters(id) ON DELETE CASCADE,
source_type VARCHAR(16) NOT NULL, -- trade / mercenary / master / party / report
delta INT NOT NULL DEFAULT 0,
tag VARCHAR(32),
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX idx_reputation_character ON reputation_records(character_id, created_at);
CREATE INDEX idx_reputation_source ON reputation_records(source_id, created_at);
COMMENT ON TABLE reputation_records IS '大表建议:按 created_at 月分区,保留 6 个月';
--------------------------------------------------------------------------------
-- 14. 配置与审计
--------------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS nacos_configs (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
config_key VARCHAR(128) UNIQUE NOT NULL,
namespace VARCHAR(64) NOT NULL,
value JSONB NOT NULL DEFAULT '{}',
version INT NOT NULL DEFAULT 1,
is_grayscale BOOLEAN NOT NULL DEFAULT false,
grayscale_ratio NUMERIC(5,4) NOT NULL DEFAULT 0,
effective_from TIMESTAMPTZ,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX idx_nacos_configs_namespace ON nacos_configs(namespace, config_key);
CREATE TABLE IF NOT EXISTS daily_reset_tracking (
character_id UUID NOT NULL REFERENCES characters(id) ON DELETE CASCADE,
reset_date DATE NOT NULL,
report_count_used SMALLINT NOT NULL DEFAULT 0,
instance_count_used SMALLINT NOT NULL DEFAULT 0,
ruin_count_used SMALLINT NOT NULL DEFAULT 0,
double_cultivation_minutes SMALLINT NOT NULL DEFAULT 0,
fishing_games SMALLINT NOT NULL DEFAULT 0,
updated_at TIMESTAMPTZ DEFAULT NOW(),
PRIMARY KEY (character_id, reset_date)
);
--------------------------------------------------------------------------------
-- 14. 延迟外键约束(解决建表顺序依赖)
--------------------------------------------------------------------------------
ALTER TABLE guilds ADD CONSTRAINT fk_guilds_region
FOREIGN KEY (region_id) REFERENCES regions(id) ON DELETE SET NULL;
ALTER TABLE guilds ADD CONSTRAINT fk_guilds_zone
FOREIGN KEY (zone_id) REFERENCES zones(id) ON DELETE SET NULL;
ALTER TABLE disciple_missions ADD CONSTRAINT fk_disciple_missions_contract
FOREIGN KEY (contract_id) REFERENCES contracts(id) ON DELETE SET NULL;
--------------------------------------------------------------------------------
-- 15. 参考数据 seed
--------------------------------------------------------------------------------
INSERT INTO race_templates (id, name, category, can_create, description) VALUES
('human', '人族', 'common', false, '默认不可创建,可通过转化获得'),
('tiger_yao', '虎妖', 'common', true, '妖族分支'),
('wolf_yao', '狼妖', 'common', true, '妖族分支'),
('elf_light', '光精灵', 'common', true, '精灵族分支'),
('elf_dark', '暗精灵', 'common', true, '精灵族分支'),
('demon_blood', '魔血族', 'common', true, '魔族分支'),
('ghost_ethereal', '幽魂族', 'common', true, '鬼族分支'),
('deep_one', '深潜裔', 'rare', true, '稀有血统,多分支进化'),
('dragon_blood', '龙血族', 'rare', true, '稀有种族'),
('chaos_heritage', '混沌裔', 'rare', true, '稀有种族'),
('giant', '巨人族', 'rare', true, '稀有种族')
ON CONFLICT (id) DO NOTHING;
INSERT INTO realms (tier, minor_realm_max, name, world_tier, main_currency_code, is_tribulation_required, base_success_rate, attr_growth_template) VALUES
(1, 3, '炼气期', 1, 'copper', false, 1.0, '{"hp":10,"atk":2,"def":1}'),
(2, 3, '筑基期', 2, 'copper', true, 0.55, '{"hp":20,"atk":4,"def":2}'),
(3, 3, '金丹期', 3, 'spirit_stone_low', true, 0.50, '{"hp":35,"atk":7,"def":4}'),
(4, 3, '元婴期', 4, 'soul_crystal', true, 0.45, '{"hp":55,"atk":11,"def":6}'),
(5, 3, '化神期', 5, 'immortal_crystal', true, 0.40, '{"hp":80,"atk":16,"def":9}'),
(6, 3, '合体期', 6, 'chaos_crystal', true, 0.35, '{"hp":110,"atk":22,"def":12}')
ON CONFLICT (tier) DO NOTHING;
INSERT INTO world_layers (tier, name, display_layer, realm_tier, is_pvp_enabled, cross_tier_rules) VALUES
(1, '种族出生地', 0, 1, false, '{}'),
(2, '洪荒主陆', 1, 2, true, '{}'),
(3, '洪荒腹地', 2, 3, true, '{}'),
(4, '太古秘境', 3, 4, true, '{}'),
(5, '混沌之渊·化神域', 4, 5, true, '{}'),
(6, '混沌之渊·合体域', 5, 6, true, '{}')
ON CONFLICT (tier) DO NOTHING;
INSERT INTO currencies (code, name, world_tier, is_premium, exchange_rules) VALUES
('copper', '铜钱', 1, false, '{}'),
('silver', '银两', 2, false, '{}'),
('spirit_stone_low', '下品灵石', 3, false, '{}'),
('spirit_stone_mid', '中品灵石', 3, false, '{}'),
('soul_crystal', '魂晶', 4, false, '{}'),
('immortal_crystal', '仙晶', 5, false, '{}'),
('chaos_crystal', '混沌灵石', 6, false, '{}'),
('purple_gas', '鸿蒙紫气', 1, true, '{}')
ON CONFLICT (code) DO NOTHING;
--------------------------------------------------------------------------------
-- 自检提示(可选):
-- psql -U postgres -d honghuang -f database/migrations/001_init_schema.up.sql
--------------------------------------------------------------------------------