db.ts 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. import pg from 'pg'
  2. const { Pool } = pg
  3. export const pool = new Pool({
  4. connectionString: process.env.DATABASE_URL || 'postgresql://xinghen:xinghen@localhost:5432/xinghen'
  5. })
  6. export const mapUser = (r: any) => ({
  7. id: r.id,
  8. phone: r.phone,
  9. role: r.role || 'USER',
  10. organization: r.organization || '',
  11. wechat: r.wechat || '',
  12. contactName: r.contact_name || '',
  13. bio: r.bio || '',
  14. pointsBalance: Number(r.points_balance) || 0,
  15. createdAt: r.created_at,
  16. updatedAt: r.updated_at
  17. })
  18. export const mapAsset = (r: any) => ({
  19. id: r.id,
  20. courseId: r.course_id,
  21. episodeId: r.episode_id || null,
  22. originalName: r.original_name,
  23. size: Number(r.size),
  24. mimeType: r.mime_type,
  25. url: `/uploads/${r.stored_name}`,
  26. createdAt: r.created_at
  27. })
  28. export const mapInstructor = (r: any) => ({
  29. id: r.id,
  30. name: r.name,
  31. organization: r.organization || '',
  32. introduction: r.introduction || '',
  33. imageUrl: r.image_urls?.[0] || (r.image_stored_name ? `/uploads/${r.image_stored_name}` : null),
  34. imageUrls: r.image_urls?.length ? r.image_urls : (r.image_stored_name ? [`/uploads/${r.image_stored_name}`] : [])
  35. })
  36. export const mapDeliverable = (r: any) => ({
  37. id: r.id,
  38. courseId: r.course_id,
  39. episodeId: r.episode_id || null,
  40. originalName: r.original_name,
  41. size: Number(r.size),
  42. mimeType: r.mime_type,
  43. url: `/uploads/${r.stored_name}`,
  44. createdAt: r.created_at
  45. })
  46. export const mapEpisode = (r: any, assets: any[] = [], deliverables: any[] = []) => ({
  47. id: r.id,
  48. courseId: r.course_id,
  49. episodeNumber: Number(r.episode_number) || 1,
  50. title: r.title,
  51. summary: r.summary || '',
  52. lectureNotes: r.lecture_notes || '',
  53. createdAt: r.created_at,
  54. updatedAt: r.updated_at,
  55. assets: assets.map(mapAsset),
  56. deliverables: deliverables.map(mapDeliverable)
  57. })
  58. export const mapCourse = (
  59. r: any,
  60. assets: any[] = [],
  61. instructors: any[] = [],
  62. deliverables: any[] = [],
  63. creatorUser: any = null,
  64. episodes: any[] = []
  65. ) => {
  66. const mappedAssets = assets.map(mapAsset)
  67. const mappedDeliverables = deliverables.map(mapDeliverable)
  68. // 为每个分集关联专属的 assets 与 deliverables
  69. const mappedEpisodes = episodes.map((epRow) => {
  70. const epAssets = assets.filter((a) => a.episode_id === epRow.id)
  71. const epDeliverables = deliverables.filter((d) => d.episode_id === epRow.id)
  72. return mapEpisode(epRow, epAssets, epDeliverables)
  73. })
  74. return {
  75. id: r.id,
  76. userId: r.user_id,
  77. name: r.name,
  78. category: r.category || '',
  79. description: r.description || '',
  80. status: r.status,
  81. productionNotes: r.production_notes || '',
  82. estimatedMinutes: Number(r.estimated_minutes) || 1,
  83. estimatedPoints: Number(r.estimated_points) || 1000,
  84. actualPoints: r.actual_points == null ? null : Number(r.actual_points),
  85. pointsCharged: Boolean(r.points_charged),
  86. progressEvents: (r.progress_events || []).map((event: any) => ({
  87. id: event.id,
  88. actorType: event.actor_type,
  89. eventType: event.event_type,
  90. title: event.title,
  91. description: event.description || '',
  92. metadata: event.metadata || {},
  93. createdAt: event.created_at
  94. })),
  95. createdAt: r.created_at,
  96. submittedAt: r.submitted_at || null,
  97. completedAt: r.completed_at || null,
  98. assets: mappedAssets,
  99. episodes: mappedEpisodes,
  100. instructors: instructors.map(mapInstructor),
  101. deliverables: mappedDeliverables,
  102. creator: creatorUser ? mapUser(creatorUser) : (r.user_phone ? {
  103. id: r.user_id,
  104. phone: r.user_phone,
  105. role: r.user_role || 'USER',
  106. organization: r.user_organization || '',
  107. wechat: r.user_wechat || '',
  108. contactName: r.user_contact_name || '',
  109. bio: r.user_bio || '',
  110. createdAt: r.user_created_at
  111. } : undefined)
  112. }
  113. }