0001_init.sql 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. BEGIN;
  2. CREATE EXTENSION IF NOT EXISTS pgcrypto;
  3. CREATE OR REPLACE FUNCTION set_updated_at()
  4. RETURNS TRIGGER AS $$
  5. BEGIN
  6. NEW.updated_at = NOW();
  7. RETURN NEW;
  8. END;
  9. $$ LANGUAGE plpgsql;
  10. CREATE TABLE tenants (
  11. id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  12. tenant_code TEXT NOT NULL UNIQUE,
  13. name TEXT NOT NULL,
  14. status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('draft', 'active', 'disabled', 'archived')),
  15. theme_jsonb JSONB NOT NULL DEFAULT '{}'::jsonb,
  16. settings_jsonb JSONB NOT NULL DEFAULT '{}'::jsonb,
  17. created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  18. updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
  19. );
  20. CREATE TRIGGER tenants_set_updated_at
  21. BEFORE UPDATE ON tenants
  22. FOR EACH ROW EXECUTE FUNCTION set_updated_at();
  23. CREATE TABLE entry_channels (
  24. id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  25. tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
  26. channel_code TEXT NOT NULL,
  27. channel_type TEXT NOT NULL CHECK (channel_type IN ('app', 'wechat_mini', 'wechat_oa', 'h5', 'qr')),
  28. platform_app_id TEXT,
  29. display_name TEXT NOT NULL,
  30. status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('draft', 'active', 'disabled', 'archived')),
  31. is_default BOOLEAN NOT NULL DEFAULT FALSE,
  32. config_jsonb JSONB NOT NULL DEFAULT '{}'::jsonb,
  33. created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  34. updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  35. UNIQUE (tenant_id, channel_code)
  36. );
  37. CREATE INDEX entry_channels_tenant_id_idx ON entry_channels(tenant_id);
  38. CREATE INDEX entry_channels_platform_app_id_idx ON entry_channels(platform_app_id);
  39. CREATE TRIGGER entry_channels_set_updated_at
  40. BEFORE UPDATE ON entry_channels
  41. FOR EACH ROW EXECUTE FUNCTION set_updated_at();
  42. CREATE TABLE users (
  43. id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  44. user_public_id TEXT NOT NULL UNIQUE,
  45. default_tenant_id UUID REFERENCES tenants(id) ON DELETE SET NULL,
  46. status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'disabled', 'deleted')),
  47. nickname TEXT,
  48. avatar_url TEXT,
  49. last_login_at TIMESTAMPTZ,
  50. created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  51. updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
  52. );
  53. CREATE INDEX users_default_tenant_id_idx ON users(default_tenant_id);
  54. CREATE TRIGGER users_set_updated_at
  55. BEFORE UPDATE ON users
  56. FOR EACH ROW EXECUTE FUNCTION set_updated_at();
  57. CREATE TABLE login_identities (
  58. id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  59. user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
  60. identity_type TEXT NOT NULL CHECK (identity_type IN ('mobile', 'wechat_mini_openid', 'wechat_oa_openid', 'wechat_unionid')),
  61. provider TEXT NOT NULL,
  62. provider_subject TEXT NOT NULL,
  63. country_code TEXT,
  64. mobile TEXT,
  65. status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'disabled', 'deleted')),
  66. profile_jsonb JSONB NOT NULL DEFAULT '{}'::jsonb,
  67. created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  68. updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  69. UNIQUE (provider, provider_subject)
  70. );
  71. CREATE INDEX login_identities_user_id_idx ON login_identities(user_id);
  72. CREATE INDEX login_identities_mobile_idx ON login_identities(country_code, mobile);
  73. CREATE TRIGGER login_identities_set_updated_at
  74. BEFORE UPDATE ON login_identities
  75. FOR EACH ROW EXECUTE FUNCTION set_updated_at();
  76. CREATE TABLE auth_sms_codes (
  77. id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  78. scene TEXT NOT NULL,
  79. country_code TEXT NOT NULL,
  80. mobile TEXT NOT NULL,
  81. client_type TEXT NOT NULL CHECK (client_type IN ('app', 'wechat')),
  82. device_key TEXT NOT NULL,
  83. code_hash TEXT NOT NULL,
  84. provider_payload_jsonb JSONB NOT NULL DEFAULT '{}'::jsonb,
  85. expires_at TIMESTAMPTZ NOT NULL,
  86. cooldown_until TIMESTAMPTZ NOT NULL,
  87. consumed_at TIMESTAMPTZ,
  88. created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
  89. );
  90. CREATE INDEX auth_sms_codes_lookup_idx
  91. ON auth_sms_codes(country_code, mobile, client_type, scene, created_at DESC);
  92. CREATE TABLE auth_refresh_tokens (
  93. id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  94. user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
  95. client_type TEXT NOT NULL CHECK (client_type IN ('app', 'wechat')),
  96. device_key TEXT,
  97. token_hash TEXT NOT NULL UNIQUE,
  98. issued_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  99. expires_at TIMESTAMPTZ NOT NULL,
  100. revoked_at TIMESTAMPTZ,
  101. replaced_by_token_id UUID REFERENCES auth_refresh_tokens(id) ON DELETE SET NULL
  102. );
  103. CREATE INDEX auth_refresh_tokens_user_id_idx ON auth_refresh_tokens(user_id);
  104. CREATE INDEX auth_refresh_tokens_expires_at_idx ON auth_refresh_tokens(expires_at);
  105. COMMIT;