schema.sql 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. CREATE TABLE IF NOT EXISTS users (
  2. id UUID PRIMARY KEY,
  3. phone VARCHAR(20) UNIQUE NOT NULL,
  4. password_hash TEXT NOT NULL,
  5. role VARCHAR(20) NOT NULL DEFAULT 'USER',
  6. organization VARCHAR(120) NOT NULL DEFAULT '',
  7. wechat VARCHAR(80) NOT NULL DEFAULT '',
  8. contact_name VARCHAR(80) NOT NULL DEFAULT '',
  9. bio VARCHAR(500) NOT NULL DEFAULT '',
  10. created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  11. updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
  12. );
  13. ALTER TABLE users ADD COLUMN IF NOT EXISTS role VARCHAR(20) NOT NULL DEFAULT 'USER';
  14. CREATE TABLE IF NOT EXISTS sessions (
  15. token_hash CHAR(64) PRIMARY KEY,
  16. user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
  17. expires_at TIMESTAMPTZ NOT NULL,
  18. created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
  19. );
  20. CREATE INDEX IF NOT EXISTS idx_sessions_user ON sessions(user_id);
  21. CREATE TABLE IF NOT EXISTS courses (
  22. id UUID PRIMARY KEY,
  23. user_id UUID REFERENCES users(id) ON DELETE CASCADE,
  24. name VARCHAR(100) NOT NULL,
  25. category VARCHAR(50) NOT NULL DEFAULT '',
  26. audience VARCHAR(100) NOT NULL DEFAULT '',
  27. description VARCHAR(500) NOT NULL DEFAULT '',
  28. status VARCHAR(30) NOT NULL DEFAULT 'DRAFT' CHECK (status IN ('DRAFT', 'WAITING_PRODUCTION', 'IN_PRODUCTION', 'COMPLETED', 'REJECTED')),
  29. production_notes TEXT NOT NULL DEFAULT '',
  30. ppt_original_name VARCHAR(255),
  31. ppt_stored_name VARCHAR(255),
  32. ppt_size BIGINT,
  33. ppt_mime_type VARCHAR(150),
  34. created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  35. updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  36. submitted_at TIMESTAMPTZ,
  37. completed_at TIMESTAMPTZ
  38. );
  39. ALTER TABLE courses ADD COLUMN IF NOT EXISTS user_id UUID REFERENCES users(id) ON DELETE CASCADE;
  40. ALTER TABLE courses ADD COLUMN IF NOT EXISTS production_notes TEXT NOT NULL DEFAULT '';
  41. ALTER TABLE courses ADD COLUMN IF NOT EXISTS completed_at TIMESTAMPTZ;
  42. DO $$
  43. BEGIN
  44. ALTER TABLE courses DROP CONSTRAINT IF EXISTS courses_status_check;
  45. ALTER TABLE courses ADD CONSTRAINT courses_status_check CHECK (status IN ('DRAFT', 'WAITING_PRODUCTION', 'IN_PRODUCTION', 'COMPLETED', 'REJECTED'));
  46. EXCEPTION
  47. WHEN OTHERS THEN NULL;
  48. END $$;
  49. CREATE INDEX IF NOT EXISTS idx_courses_user_created ON courses(user_id, created_at DESC);
  50. CREATE INDEX IF NOT EXISTS idx_courses_status_created ON courses(status, created_at DESC);
  51. -- 课程分集表(Episodes)
  52. CREATE TABLE IF NOT EXISTS episodes (
  53. id UUID PRIMARY KEY,
  54. course_id UUID NOT NULL REFERENCES courses(id) ON DELETE CASCADE,
  55. episode_number INT NOT NULL DEFAULT 1,
  56. title VARCHAR(200) NOT NULL,
  57. summary VARCHAR(500) NOT NULL DEFAULT '',
  58. lecture_notes TEXT NOT NULL DEFAULT '',
  59. created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  60. updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
  61. );
  62. CREATE INDEX IF NOT EXISTS idx_episodes_course ON episodes(course_id, episode_number ASC);
  63. CREATE TABLE IF NOT EXISTS course_assets (
  64. id UUID PRIMARY KEY,
  65. course_id UUID NOT NULL REFERENCES courses(id) ON DELETE CASCADE,
  66. episode_id UUID REFERENCES episodes(id) ON DELETE CASCADE,
  67. original_name VARCHAR(255) NOT NULL,
  68. stored_name VARCHAR(255) NOT NULL,
  69. size BIGINT NOT NULL,
  70. mime_type VARCHAR(150) NOT NULL DEFAULT 'application/octet-stream',
  71. created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
  72. );
  73. ALTER TABLE course_assets ADD COLUMN IF NOT EXISTS episode_id UUID REFERENCES episodes(id) ON DELETE CASCADE;
  74. CREATE INDEX IF NOT EXISTS idx_assets_course ON course_assets(course_id, created_at);
  75. CREATE INDEX IF NOT EXISTS idx_assets_episode ON course_assets(episode_id, created_at);
  76. CREATE TABLE IF NOT EXISTS instructors (
  77. id UUID PRIMARY KEY,
  78. course_id UUID NOT NULL REFERENCES courses(id) ON DELETE CASCADE,
  79. name VARCHAR(80) NOT NULL,
  80. organization VARCHAR(120) NOT NULL DEFAULT '',
  81. introduction VARCHAR(1000) NOT NULL DEFAULT '',
  82. image_original_name VARCHAR(255),
  83. image_stored_name VARCHAR(255),
  84. image_mime_type VARCHAR(100),
  85. created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
  86. );
  87. CREATE INDEX IF NOT EXISTS idx_instructors_course ON instructors(course_id, created_at);
  88. CREATE TABLE IF NOT EXISTS course_deliverables (
  89. id UUID PRIMARY KEY,
  90. course_id UUID NOT NULL REFERENCES courses(id) ON DELETE CASCADE,
  91. episode_id UUID REFERENCES episodes(id) ON DELETE SET NULL,
  92. original_name VARCHAR(255) NOT NULL,
  93. stored_name VARCHAR(255) NOT NULL,
  94. size BIGINT NOT NULL,
  95. mime_type VARCHAR(150) NOT NULL DEFAULT 'application/octet-stream',
  96. created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
  97. );
  98. ALTER TABLE course_deliverables ADD COLUMN IF NOT EXISTS episode_id UUID REFERENCES episodes(id) ON DELETE SET NULL;
  99. CREATE INDEX IF NOT EXISTS idx_deliverables_course ON course_deliverables(course_id, created_at);
  100. CREATE INDEX IF NOT EXISTS idx_deliverables_episode ON course_deliverables(episode_id, created_at);