llm-content-creator 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (438) hide show
  1. package/README.md +309 -0
  2. package/dist/application/workflow/SyncExecutor.d.ts +75 -0
  3. package/dist/application/workflow/SyncExecutor.d.ts.map +1 -0
  4. package/dist/application/workflow/SyncExecutor.js +370 -0
  5. package/dist/application/workflow/SyncExecutor.js.map +1 -0
  6. package/dist/application/workflow/types.d.ts +46 -0
  7. package/dist/application/workflow/types.d.ts.map +1 -0
  8. package/dist/application/workflow/types.js +7 -0
  9. package/dist/application/workflow/types.js.map +1 -0
  10. package/dist/config/index.d.ts +173 -0
  11. package/dist/config/index.d.ts.map +1 -0
  12. package/dist/config/index.js +288 -0
  13. package/dist/config/index.js.map +1 -0
  14. package/dist/domain/entities/QualityCheck.d.ts +181 -0
  15. package/dist/domain/entities/QualityCheck.d.ts.map +1 -0
  16. package/dist/domain/entities/QualityCheck.js +39 -0
  17. package/dist/domain/entities/QualityCheck.js.map +1 -0
  18. package/dist/domain/entities/Result.d.ts +103 -0
  19. package/dist/domain/entities/Result.d.ts.map +1 -0
  20. package/dist/domain/entities/Result.js +15 -0
  21. package/dist/domain/entities/Result.js.map +1 -0
  22. package/dist/domain/entities/Task.d.ts +130 -0
  23. package/dist/domain/entities/Task.d.ts.map +1 -0
  24. package/dist/domain/entities/Task.js +64 -0
  25. package/dist/domain/entities/Task.js.map +1 -0
  26. package/dist/domain/entities/TaskStep.d.ts +160 -0
  27. package/dist/domain/entities/TaskStep.d.ts.map +1 -0
  28. package/dist/domain/entities/TaskStep.js +30 -0
  29. package/dist/domain/entities/TaskStep.js.map +1 -0
  30. package/dist/domain/entities/TokenUsage.d.ts +70 -0
  31. package/dist/domain/entities/TokenUsage.d.ts.map +1 -0
  32. package/dist/domain/entities/TokenUsage.js +42 -0
  33. package/dist/domain/entities/TokenUsage.js.map +1 -0
  34. package/dist/domain/entities/index.d.ts +11 -0
  35. package/dist/domain/entities/index.d.ts.map +1 -0
  36. package/dist/domain/entities/index.js +16 -0
  37. package/dist/domain/entities/index.js.map +1 -0
  38. package/dist/domain/repositories/QualityCheckRepository.d.ts +49 -0
  39. package/dist/domain/repositories/QualityCheckRepository.d.ts.map +1 -0
  40. package/dist/domain/repositories/QualityCheckRepository.js +5 -0
  41. package/dist/domain/repositories/QualityCheckRepository.js.map +1 -0
  42. package/dist/domain/repositories/ResultRepository.d.ts +43 -0
  43. package/dist/domain/repositories/ResultRepository.d.ts.map +1 -0
  44. package/dist/domain/repositories/ResultRepository.js +5 -0
  45. package/dist/domain/repositories/ResultRepository.js.map +1 -0
  46. package/dist/domain/repositories/TaskRepository.d.ts +240 -0
  47. package/dist/domain/repositories/TaskRepository.d.ts.map +1 -0
  48. package/dist/domain/repositories/TaskRepository.js +7 -0
  49. package/dist/domain/repositories/TaskRepository.js.map +1 -0
  50. package/dist/domain/workflow/CheckpointManager.d.ts +94 -0
  51. package/dist/domain/workflow/CheckpointManager.d.ts.map +1 -0
  52. package/dist/domain/workflow/CheckpointManager.js +224 -0
  53. package/dist/domain/workflow/CheckpointManager.js.map +1 -0
  54. package/dist/domain/workflow/ContentCreatorGraph.d.ts +17 -0
  55. package/dist/domain/workflow/ContentCreatorGraph.d.ts.map +1 -0
  56. package/dist/domain/workflow/ContentCreatorGraph.js +381 -0
  57. package/dist/domain/workflow/ContentCreatorGraph.js.map +1 -0
  58. package/dist/domain/workflow/State.d.ts +172 -0
  59. package/dist/domain/workflow/State.d.ts.map +1 -0
  60. package/dist/domain/workflow/State.js +184 -0
  61. package/dist/domain/workflow/State.js.map +1 -0
  62. package/dist/domain/workflow/index.d.ts +11 -0
  63. package/dist/domain/workflow/index.d.ts.map +1 -0
  64. package/dist/domain/workflow/index.js +15 -0
  65. package/dist/domain/workflow/index.js.map +1 -0
  66. package/dist/domain/workflow/nodes/BaseNode.d.ts +134 -0
  67. package/dist/domain/workflow/nodes/BaseNode.d.ts.map +1 -0
  68. package/dist/domain/workflow/nodes/BaseNode.js +253 -0
  69. package/dist/domain/workflow/nodes/BaseNode.js.map +1 -0
  70. package/dist/domain/workflow/nodes/CheckImageNode.d.ts +43 -0
  71. package/dist/domain/workflow/nodes/CheckImageNode.d.ts.map +1 -0
  72. package/dist/domain/workflow/nodes/CheckImageNode.js +254 -0
  73. package/dist/domain/workflow/nodes/CheckImageNode.js.map +1 -0
  74. package/dist/domain/workflow/nodes/CheckTextNode.d.ts +66 -0
  75. package/dist/domain/workflow/nodes/CheckTextNode.d.ts.map +1 -0
  76. package/dist/domain/workflow/nodes/CheckTextNode.js +530 -0
  77. package/dist/domain/workflow/nodes/CheckTextNode.js.map +1 -0
  78. package/dist/domain/workflow/nodes/GenerateImageNode.d.ts +44 -0
  79. package/dist/domain/workflow/nodes/GenerateImageNode.d.ts.map +1 -0
  80. package/dist/domain/workflow/nodes/GenerateImageNode.js +272 -0
  81. package/dist/domain/workflow/nodes/GenerateImageNode.js.map +1 -0
  82. package/dist/domain/workflow/nodes/OrganizeNode.d.ts +49 -0
  83. package/dist/domain/workflow/nodes/OrganizeNode.d.ts.map +1 -0
  84. package/dist/domain/workflow/nodes/OrganizeNode.js +241 -0
  85. package/dist/domain/workflow/nodes/OrganizeNode.js.map +1 -0
  86. package/dist/domain/workflow/nodes/SearchNode.d.ts +48 -0
  87. package/dist/domain/workflow/nodes/SearchNode.d.ts.map +1 -0
  88. package/dist/domain/workflow/nodes/SearchNode.js +151 -0
  89. package/dist/domain/workflow/nodes/SearchNode.js.map +1 -0
  90. package/dist/domain/workflow/nodes/WriteNode.d.ts +68 -0
  91. package/dist/domain/workflow/nodes/WriteNode.d.ts.map +1 -0
  92. package/dist/domain/workflow/nodes/WriteNode.js +431 -0
  93. package/dist/domain/workflow/nodes/WriteNode.js.map +1 -0
  94. package/dist/domain/workflow/nodes/config/index.js +287 -0
  95. package/dist/domain/workflow/nodes/domain/entities/Task.js +68 -0
  96. package/dist/domain/workflow/nodes/domain/workflow/State.js +200 -0
  97. package/dist/domain/workflow/nodes/domain/workflow/nodes/BaseNode.js +328 -0
  98. package/dist/domain/workflow/nodes/domain/workflow/nodes/CheckTextNode.js +500 -0
  99. package/dist/domain/workflow/nodes/index.d.ts +13 -0
  100. package/dist/domain/workflow/nodes/index.d.ts.map +1 -0
  101. package/dist/domain/workflow/nodes/index.js +13 -0
  102. package/dist/domain/workflow/nodes/index.js.map +1 -0
  103. package/dist/domain/workflow/nodes/infrastructure/logging/logger.js +275 -0
  104. package/dist/domain/workflow/nodes/services/llm/EnhancedLLMService.js +559 -0
  105. package/dist/index.d.ts +24 -0
  106. package/dist/index.d.ts.map +1 -0
  107. package/dist/index.js +34 -0
  108. package/dist/index.js.map +1 -0
  109. package/dist/infrastructure/cache/CacheService.d.ts +139 -0
  110. package/dist/infrastructure/cache/CacheService.d.ts.map +1 -0
  111. package/dist/infrastructure/cache/CacheService.js +419 -0
  112. package/dist/infrastructure/cache/CacheService.js.map +1 -0
  113. package/dist/infrastructure/cache/index.d.ts +5 -0
  114. package/dist/infrastructure/cache/index.d.ts.map +1 -0
  115. package/dist/infrastructure/cache/index.js +6 -0
  116. package/dist/infrastructure/cache/index.js.map +1 -0
  117. package/dist/infrastructure/database/BaseRepository.d.ts +98 -0
  118. package/dist/infrastructure/database/BaseRepository.d.ts.map +1 -0
  119. package/dist/infrastructure/database/BaseRepository.js +178 -0
  120. package/dist/infrastructure/database/BaseRepository.js.map +1 -0
  121. package/dist/infrastructure/database/MemoryTaskRepository.d.ts +77 -0
  122. package/dist/infrastructure/database/MemoryTaskRepository.d.ts.map +1 -0
  123. package/dist/infrastructure/database/MemoryTaskRepository.js +309 -0
  124. package/dist/infrastructure/database/MemoryTaskRepository.js.map +1 -0
  125. package/dist/infrastructure/database/PostgresQualityCheckRepository.d.ts +36 -0
  126. package/dist/infrastructure/database/PostgresQualityCheckRepository.d.ts.map +1 -0
  127. package/dist/infrastructure/database/PostgresQualityCheckRepository.js +89 -0
  128. package/dist/infrastructure/database/PostgresQualityCheckRepository.js.map +1 -0
  129. package/dist/infrastructure/database/PostgresTaskRepository.d.ts +94 -0
  130. package/dist/infrastructure/database/PostgresTaskRepository.d.ts.map +1 -0
  131. package/dist/infrastructure/database/PostgresTaskRepository.js +364 -0
  132. package/dist/infrastructure/database/PostgresTaskRepository.js.map +1 -0
  133. package/dist/infrastructure/database/ResultRepository.d.ts +41 -0
  134. package/dist/infrastructure/database/ResultRepository.d.ts.map +1 -0
  135. package/dist/infrastructure/database/ResultRepository.js +86 -0
  136. package/dist/infrastructure/database/ResultRepository.js.map +1 -0
  137. package/dist/infrastructure/database/SQLiteTaskRepository.d.ts +101 -0
  138. package/dist/infrastructure/database/SQLiteTaskRepository.d.ts.map +1 -0
  139. package/dist/infrastructure/database/SQLiteTaskRepository.js +548 -0
  140. package/dist/infrastructure/database/SQLiteTaskRepository.js.map +1 -0
  141. package/dist/infrastructure/database/index.d.ts +32 -0
  142. package/dist/infrastructure/database/index.d.ts.map +1 -0
  143. package/dist/infrastructure/database/index.js +72 -0
  144. package/dist/infrastructure/database/index.js.map +1 -0
  145. package/dist/infrastructure/logging/logger.d.ts +69 -0
  146. package/dist/infrastructure/logging/logger.d.ts.map +1 -0
  147. package/dist/infrastructure/logging/logger.js +169 -0
  148. package/dist/infrastructure/logging/logger.js.map +1 -0
  149. package/dist/infrastructure/monitoring/LoggingService.d.ts +109 -0
  150. package/dist/infrastructure/monitoring/LoggingService.d.ts.map +1 -0
  151. package/dist/infrastructure/monitoring/LoggingService.js +198 -0
  152. package/dist/infrastructure/monitoring/LoggingService.js.map +1 -0
  153. package/dist/infrastructure/monitoring/MetricsService.d.ts +112 -0
  154. package/dist/infrastructure/monitoring/MetricsService.d.ts.map +1 -0
  155. package/dist/infrastructure/monitoring/MetricsService.js +362 -0
  156. package/dist/infrastructure/monitoring/MetricsService.js.map +1 -0
  157. package/dist/infrastructure/monitoring/SentryService.d.ts +108 -0
  158. package/dist/infrastructure/monitoring/SentryService.d.ts.map +1 -0
  159. package/dist/infrastructure/monitoring/SentryService.js +282 -0
  160. package/dist/infrastructure/monitoring/SentryService.js.map +1 -0
  161. package/dist/infrastructure/monitoring/index.d.ts +7 -0
  162. package/dist/infrastructure/monitoring/index.d.ts.map +1 -0
  163. package/dist/infrastructure/monitoring/index.js +10 -0
  164. package/dist/infrastructure/monitoring/index.js.map +1 -0
  165. package/dist/infrastructure/queue/TaskQueue.d.ts +110 -0
  166. package/dist/infrastructure/queue/TaskQueue.d.ts.map +1 -0
  167. package/dist/infrastructure/queue/TaskQueue.js +363 -0
  168. package/dist/infrastructure/queue/TaskQueue.js.map +1 -0
  169. package/dist/infrastructure/queue/index.d.ts +5 -0
  170. package/dist/infrastructure/queue/index.d.ts.map +1 -0
  171. package/dist/infrastructure/queue/index.js +5 -0
  172. package/dist/infrastructure/queue/index.js.map +1 -0
  173. package/dist/infrastructure/redis/connection.d.ts +61 -0
  174. package/dist/infrastructure/redis/connection.d.ts.map +1 -0
  175. package/dist/infrastructure/redis/connection.js +184 -0
  176. package/dist/infrastructure/redis/connection.js.map +1 -0
  177. package/dist/infrastructure/redis/index.d.ts +5 -0
  178. package/dist/infrastructure/redis/index.d.ts.map +1 -0
  179. package/dist/infrastructure/redis/index.js +5 -0
  180. package/dist/infrastructure/redis/index.js.map +1 -0
  181. package/dist/infrastructure/security/ApiKeyService.d.ts +103 -0
  182. package/dist/infrastructure/security/ApiKeyService.d.ts.map +1 -0
  183. package/dist/infrastructure/security/ApiKeyService.js +250 -0
  184. package/dist/infrastructure/security/ApiKeyService.js.map +1 -0
  185. package/dist/infrastructure/security/QuotaService.d.ts +87 -0
  186. package/dist/infrastructure/security/QuotaService.d.ts.map +1 -0
  187. package/dist/infrastructure/security/QuotaService.js +303 -0
  188. package/dist/infrastructure/security/QuotaService.js.map +1 -0
  189. package/dist/infrastructure/security/RateLimiter.d.ts +104 -0
  190. package/dist/infrastructure/security/RateLimiter.d.ts.map +1 -0
  191. package/dist/infrastructure/security/RateLimiter.js +331 -0
  192. package/dist/infrastructure/security/RateLimiter.js.map +1 -0
  193. package/dist/infrastructure/security/index.d.ts +7 -0
  194. package/dist/infrastructure/security/index.d.ts.map +1 -0
  195. package/dist/infrastructure/security/index.js +10 -0
  196. package/dist/infrastructure/security/index.js.map +1 -0
  197. package/dist/monitoring/index.d.ts +5 -0
  198. package/dist/monitoring/index.d.ts.map +1 -0
  199. package/dist/monitoring/index.js +5 -0
  200. package/dist/monitoring/index.js.map +1 -0
  201. package/dist/monitoring/server.d.ts +14 -0
  202. package/dist/monitoring/server.d.ts.map +1 -0
  203. package/dist/monitoring/server.js +99 -0
  204. package/dist/monitoring/server.js.map +1 -0
  205. package/dist/presentation/cli/commands/cancel.d.ts +8 -0
  206. package/dist/presentation/cli/commands/cancel.d.ts.map +1 -0
  207. package/dist/presentation/cli/commands/cancel.js +57 -0
  208. package/dist/presentation/cli/commands/cancel.js.map +1 -0
  209. package/dist/presentation/cli/commands/create.d.ts +8 -0
  210. package/dist/presentation/cli/commands/create.d.ts.map +1 -0
  211. package/dist/presentation/cli/commands/create.js +368 -0
  212. package/dist/presentation/cli/commands/create.js.map +1 -0
  213. package/dist/presentation/cli/commands/result.d.ts +8 -0
  214. package/dist/presentation/cli/commands/result.d.ts.map +1 -0
  215. package/dist/presentation/cli/commands/result.js +121 -0
  216. package/dist/presentation/cli/commands/result.js.map +1 -0
  217. package/dist/presentation/cli/commands/status.d.ts +8 -0
  218. package/dist/presentation/cli/commands/status.d.ts.map +1 -0
  219. package/dist/presentation/cli/commands/status.js +92 -0
  220. package/dist/presentation/cli/commands/status.js.map +1 -0
  221. package/dist/presentation/cli/index.d.ts +8 -0
  222. package/dist/presentation/cli/index.d.ts.map +1 -0
  223. package/dist/presentation/cli/index.js +32 -0
  224. package/dist/presentation/cli/index.js.map +1 -0
  225. package/dist/presentation/cli/utils/cleanup.d.ts +14 -0
  226. package/dist/presentation/cli/utils/cleanup.d.ts.map +1 -0
  227. package/dist/presentation/cli/utils/cleanup.js +62 -0
  228. package/dist/presentation/cli/utils/cleanup.js.map +1 -0
  229. package/dist/presentation/cli/utils/formatter.d.ts +28 -0
  230. package/dist/presentation/cli/utils/formatter.d.ts.map +1 -0
  231. package/dist/presentation/cli/utils/formatter.js +68 -0
  232. package/dist/presentation/cli/utils/formatter.js.map +1 -0
  233. package/dist/presentation/cli.d.ts +7 -0
  234. package/dist/presentation/cli.d.ts.map +1 -0
  235. package/dist/presentation/cli.js +8 -0
  236. package/dist/presentation/cli.js.map +1 -0
  237. package/dist/presentation/monitor-cli.d.ts +8 -0
  238. package/dist/presentation/monitor-cli.d.ts.map +1 -0
  239. package/dist/presentation/monitor-cli.js +44 -0
  240. package/dist/presentation/monitor-cli.js.map +1 -0
  241. package/dist/presentation/worker-cli.d.ts +8 -0
  242. package/dist/presentation/worker-cli.d.ts.map +1 -0
  243. package/dist/presentation/worker-cli.js +51 -0
  244. package/dist/presentation/worker-cli.js.map +1 -0
  245. package/dist/schedulers/TaskScheduler.d.ts +99 -0
  246. package/dist/schedulers/TaskScheduler.d.ts.map +1 -0
  247. package/dist/schedulers/TaskScheduler.js +233 -0
  248. package/dist/schedulers/TaskScheduler.js.map +1 -0
  249. package/dist/schedulers/index.d.ts +5 -0
  250. package/dist/schedulers/index.d.ts.map +1 -0
  251. package/dist/schedulers/index.js +5 -0
  252. package/dist/schedulers/index.js.map +1 -0
  253. package/dist/services/image/ImageService.d.ts +68 -0
  254. package/dist/services/image/ImageService.d.ts.map +1 -0
  255. package/dist/services/image/ImageService.js +166 -0
  256. package/dist/services/image/ImageService.js.map +1 -0
  257. package/dist/services/index.d.ts +8 -0
  258. package/dist/services/index.d.ts.map +1 -0
  259. package/dist/services/index.js +12 -0
  260. package/dist/services/index.js.map +1 -0
  261. package/dist/services/llm/EnhancedLLMService.d.ts +148 -0
  262. package/dist/services/llm/EnhancedLLMService.d.ts.map +1 -0
  263. package/dist/services/llm/EnhancedLLMService.js +425 -0
  264. package/dist/services/llm/EnhancedLLMService.js.map +1 -0
  265. package/dist/services/llm/LLMService.d.ts +103 -0
  266. package/dist/services/llm/LLMService.d.ts.map +1 -0
  267. package/dist/services/llm/LLMService.js +212 -0
  268. package/dist/services/llm/LLMService.js.map +1 -0
  269. package/dist/services/quality/HardRuleChecker.d.ts +143 -0
  270. package/dist/services/quality/HardRuleChecker.d.ts.map +1 -0
  271. package/dist/services/quality/HardRuleChecker.js +353 -0
  272. package/dist/services/quality/HardRuleChecker.js.map +1 -0
  273. package/dist/services/quality/LLMEvaluator.d.ts +105 -0
  274. package/dist/services/quality/LLMEvaluator.d.ts.map +1 -0
  275. package/dist/services/quality/LLMEvaluator.js +312 -0
  276. package/dist/services/quality/LLMEvaluator.js.map +1 -0
  277. package/dist/services/quality/QualityCheckService.d.ts +112 -0
  278. package/dist/services/quality/QualityCheckService.d.ts.map +1 -0
  279. package/dist/services/quality/QualityCheckService.js +342 -0
  280. package/dist/services/quality/QualityCheckService.js.map +1 -0
  281. package/dist/services/quality/QualityService.d.ts +75 -0
  282. package/dist/services/quality/QualityService.d.ts.map +1 -0
  283. package/dist/services/quality/QualityService.js +360 -0
  284. package/dist/services/quality/QualityService.js.map +1 -0
  285. package/dist/services/quality/index.d.ts +7 -0
  286. package/dist/services/quality/index.d.ts.map +1 -0
  287. package/dist/services/quality/index.js +10 -0
  288. package/dist/services/quality/index.js.map +1 -0
  289. package/dist/services/search/SearchService.d.ts +79 -0
  290. package/dist/services/search/SearchService.d.ts.map +1 -0
  291. package/dist/services/search/SearchService.js +193 -0
  292. package/dist/services/search/SearchService.js.map +1 -0
  293. package/dist/workers/TaskWorker.d.ts +61 -0
  294. package/dist/workers/TaskWorker.d.ts.map +1 -0
  295. package/dist/workers/TaskWorker.js +256 -0
  296. package/dist/workers/TaskWorker.js.map +1 -0
  297. package/dist/workers/index.d.ts +5 -0
  298. package/dist/workers/index.d.ts.map +1 -0
  299. package/dist/workers/index.js +5 -0
  300. package/dist/workers/index.js.map +1 -0
  301. package/docs/DOCUMENTATION-ANALYSIS.md +190 -0
  302. package/docs/README.md +145 -0
  303. package/docs/SOURCE-CODE-ANALYSIS.md +1107 -0
  304. package/docs/architecture-complete.md +5524 -0
  305. package/docs/archive/implementation/implementation-analysis/README.md +244 -0
  306. package/docs/archive/implementation/implementation-analysis/implementation-analysis-context.md +483 -0
  307. package/docs/archive/implementation/implementation-analysis/implementation-analysis-plan.md +1242 -0
  308. package/docs/archive/implementation/implementation-analysis/implementation-analysis-tasks.md +777 -0
  309. package/docs/archive/phases/phase-1/phase-1-completion-summary.md +284 -0
  310. package/docs/archive/phases/phase-1/phase-1-implementation-guide.md +1380 -0
  311. package/docs/archive/phases/phase-2/phase-2a/phase-2a-completion-summary.md +443 -0
  312. package/docs/archive/phases/phase-2/phase-2b/phase-2b-completion-report.md +430 -0
  313. package/docs/archive/phases/phase-2/phase-2b/phase-2b-completion-summary.md +592 -0
  314. package/docs/archive/phases/phase-2/phase-2b/phase-2b-final-summary.md +371 -0
  315. package/docs/archive/phases/phase-2/phase-2b/phase-2b-preparation-complete.md +343 -0
  316. package/docs/archive/phases/phase-2/phase-2b/phase-2b-preparation.md +945 -0
  317. package/docs/archive/phases/phase-2/phase-2b/phase-2b-progress-update.md +366 -0
  318. package/docs/archive/phases/phase-3/phase-3-completion-summary.md +354 -0
  319. package/docs/archive/phases/phase-3/phase-3-development-plan.md +878 -0
  320. package/docs/archive/phases/phase-3/phase-3-quick-start.md +324 -0
  321. package/docs/archive/phases/phase-4/phase-4-completion-summary.md +708 -0
  322. package/docs/archive/phases/phase-4/phase-4-development-plan.md +740 -0
  323. package/docs/archive/phases/phase-4/phase-4-quick-start.md +632 -0
  324. package/docs/archive/phases/phase-4/phase-4-session-3-security-testing.md +484 -0
  325. package/docs/archive/phases/phase-4/phase-4-session-4-unit-tests.md +550 -0
  326. package/docs/archive/phases/phase-4/phase-4-session-5-security-tests.md +564 -0
  327. package/docs/archive/phases/phase-4/phase-4-session-6-cache-integration.md +456 -0
  328. package/docs/archive/phases/phase-4/phase-4-session-7-test-fixes.md +348 -0
  329. package/docs/archive/phases/phase-4/phase-4-session-8-taskqueue-fixes.md +323 -0
  330. package/docs/archive/phases/phase-4/phase-4-session-summary-continued.md +373 -0
  331. package/docs/archive/phases/phase-4/phase-4-session-summary.md +595 -0
  332. package/docs/archive/reports/progress-reports/PHASE_0_PROGRESS.md +242 -0
  333. package/docs/archive/reports/progress-reports/PHASE_0_SUMMARY.md +262 -0
  334. package/docs/archive/reports/progress-reports/PHASE_1_2_ISSUES.md +399 -0
  335. package/docs/archive/reports/progress-reports/PHASE_1_PROGRESS.md +388 -0
  336. package/docs/archive/reports/progress-reports/PHASE_3_PREPARATION.md +574 -0
  337. package/docs/archive/reports/progress-reports/current-progress-update.md +294 -0
  338. package/docs/archive/reports/progress-reports/final-summary.md +215 -0
  339. package/docs/archive/reports/progress-reports/implementation-summary.md +287 -0
  340. package/docs/archive/reports/progress-reports/project-progress-report.md +440 -0
  341. package/docs/archive/reports/progress-reports/project-progress.md +386 -0
  342. package/docs/archive/reports/test-reports/TEST-COVERAGE-REPORT.md +441 -0
  343. package/docs/archive/reports/test-reports/e2e-test-report.md +293 -0
  344. package/docs/archive/reports/test-reports/final-test-report.md +367 -0
  345. package/docs/archive/reports/test-reports/real-env-test-report.md +391 -0
  346. package/docs/archive/reports/test-reports/test-completion-summary.md +356 -0
  347. package/docs/archive/reports/test-reports/test-report.md +371 -0
  348. package/docs/archive/sessions/session-2-summary.md +429 -0
  349. package/docs/archive/sessions/session-3-summary.md +395 -0
  350. package/docs/archive/sessions/session-summary.md +370 -0
  351. package/docs/config-system-update.md +239 -0
  352. package/docs/database-refactoring-PLAN.md +199 -0
  353. package/docs/database-refactoring-SUMMARY.md +384 -0
  354. package/docs/quality-check-architecture.md +1030 -0
  355. package/docs/quick-start.md +388 -0
  356. package/docs/references/bullmq-quick-reference.md +525 -0
  357. package/docs/references/monitoring-optimization-guide.md +871 -0
  358. package/docs/references/performance-optimization-guide.md +933 -0
  359. package/docs/storage-guide.md +612 -0
  360. package/docs/test-implementation-PLAN.md +223 -0
  361. package/docs/test-implementation-SUMMARY.md +194 -0
  362. package/docs/user-guide.md +719 -0
  363. package/docs/workflow-architecture.md +549 -0
  364. package/package.json +126 -0
  365. package/src/application/workflow/SyncExecutor.ts +444 -0
  366. package/src/application/workflow/types.ts +57 -0
  367. package/src/config/index.ts +352 -0
  368. package/src/domain/entities/QualityCheck.ts +202 -0
  369. package/src/domain/entities/Result.ts +130 -0
  370. package/src/domain/entities/Task.ts +178 -0
  371. package/src/domain/entities/TaskStep.ts +188 -0
  372. package/src/domain/entities/TokenUsage.ts +119 -0
  373. package/src/domain/entities/index.ts +20 -0
  374. package/src/domain/repositories/QualityCheckRepository.ts +52 -0
  375. package/src/domain/repositories/ResultRepository.ts +47 -0
  376. package/src/domain/repositories/TaskRepository.ts +271 -0
  377. package/src/domain/workflow/CheckpointManager.ts +283 -0
  378. package/src/domain/workflow/ContentCreatorGraph.ts +446 -0
  379. package/src/domain/workflow/State.ts +321 -0
  380. package/src/domain/workflow/index.ts +18 -0
  381. package/src/domain/workflow/nodes/BaseNode.ts +325 -0
  382. package/src/domain/workflow/nodes/CheckImageNode.ts +325 -0
  383. package/src/domain/workflow/nodes/CheckTextNode.ts +709 -0
  384. package/src/domain/workflow/nodes/GenerateImageNode.ts +342 -0
  385. package/src/domain/workflow/nodes/OrganizeNode.ts +304 -0
  386. package/src/domain/workflow/nodes/SearchNode.ts +192 -0
  387. package/src/domain/workflow/nodes/WriteNode.ts +505 -0
  388. package/src/domain/workflow/nodes/index.ts +13 -0
  389. package/src/index.ts +43 -0
  390. package/src/infrastructure/cache/CacheService.ts +483 -0
  391. package/src/infrastructure/cache/index.ts +6 -0
  392. package/src/infrastructure/database/BaseRepository.ts +214 -0
  393. package/src/infrastructure/database/MemoryTaskRepository.ts +377 -0
  394. package/src/infrastructure/database/PostgresQualityCheckRepository.ts +115 -0
  395. package/src/infrastructure/database/PostgresTaskRepository.ts +424 -0
  396. package/src/infrastructure/database/ResultRepository.ts +113 -0
  397. package/src/infrastructure/database/SQLiteTaskRepository.ts +651 -0
  398. package/src/infrastructure/database/index.ts +83 -0
  399. package/src/infrastructure/logging/logger.ts +231 -0
  400. package/src/infrastructure/monitoring/LoggingService.ts +292 -0
  401. package/src/infrastructure/monitoring/MetricsService.ts +468 -0
  402. package/src/infrastructure/monitoring/SentryService.ts +345 -0
  403. package/src/infrastructure/monitoring/index.ts +12 -0
  404. package/src/infrastructure/queue/TaskQueue.ts +429 -0
  405. package/src/infrastructure/queue/index.ts +5 -0
  406. package/src/infrastructure/redis/connection.ts +215 -0
  407. package/src/infrastructure/redis/index.ts +5 -0
  408. package/src/infrastructure/security/ApiKeyService.ts +340 -0
  409. package/src/infrastructure/security/QuotaService.ts +411 -0
  410. package/src/infrastructure/security/RateLimiter.ts +417 -0
  411. package/src/infrastructure/security/index.ts +12 -0
  412. package/src/monitoring/index.ts +5 -0
  413. package/src/monitoring/server.ts +109 -0
  414. package/src/presentation/cli/commands/cancel.ts +64 -0
  415. package/src/presentation/cli/commands/create.ts +400 -0
  416. package/src/presentation/cli/commands/result.ts +136 -0
  417. package/src/presentation/cli/commands/status.ts +102 -0
  418. package/src/presentation/cli/index.ts +39 -0
  419. package/src/presentation/cli/utils/cleanup.ts +65 -0
  420. package/src/presentation/cli/utils/formatter.ts +74 -0
  421. package/src/presentation/cli.ts +8 -0
  422. package/src/presentation/monitor-cli.ts +52 -0
  423. package/src/presentation/worker-cli.ts +62 -0
  424. package/src/schedulers/TaskScheduler.ts +314 -0
  425. package/src/schedulers/index.ts +11 -0
  426. package/src/services/image/ImageService.ts +221 -0
  427. package/src/services/index.ts +15 -0
  428. package/src/services/llm/EnhancedLLMService.ts +596 -0
  429. package/src/services/llm/LLMService.ts +310 -0
  430. package/src/services/quality/HardRuleChecker.ts +509 -0
  431. package/src/services/quality/LLMEvaluator.ts +400 -0
  432. package/src/services/quality/QualityCheckService.ts +473 -0
  433. package/src/services/quality/QualityService.ts +445 -0
  434. package/src/services/quality/index.ts +12 -0
  435. package/src/services/search/SearchService.ts +266 -0
  436. package/src/types/global.d.ts +17 -0
  437. package/src/workers/TaskWorker.ts +320 -0
  438. package/src/workers/index.ts +5 -0
@@ -0,0 +1,1380 @@
1
+ # 阶段 1 实施指南:核心数据层与基础架构
2
+
3
+ **项目**: Content Creator (写作 Agent)
4
+ **阶段**: 1 - 核心数据层与基础架构
5
+ **工期**: 5-7 天
6
+ **状态**: ⏳ 进行中
7
+
8
+ ---
9
+
10
+ ## 📋 阶段概述
11
+
12
+ ### 目标
13
+ 实现完整的数据访问层,包括领域模型、数据库表结构和 Repository 层,为后续工作流引擎提供坚实的数据基础。
14
+
15
+ ### 时间分配
16
+ - 领域模型完善: **1 天**
17
+ - 数据库迁移脚本: **1 天** (简化版,不含分区)
18
+ - Repository 实现: **2 天**
19
+ - 数据访问测试: **1.5 天**
20
+ - 性能优化: **0.5 天**
21
+
22
+ ### 验收标准
23
+ - ✅ 可以创建任务记录
24
+ - ✅ 可以查询和更新任务状态
25
+ - ✅ 数据库迁移可重复执行
26
+ - ✅ 测试覆盖率 > 80%
27
+ - ✅ 并发测试通过(乐观锁验证)
28
+
29
+ ---
30
+
31
+ ## 📦 领域模型设计
32
+
33
+ ### 1.1 核心实体
34
+
35
+ #### **Task (任务)**
36
+
37
+ ```typescript
38
+ // src/domain/entities/Task.ts
39
+
40
+ export enum TaskStatus {
41
+ PENDING = 'pending', // 等待执行
42
+ RUNNING = 'running', // 执行中
43
+ COMPLETED = 'completed', // 完成
44
+ FAILED = 'failed' // 失败
45
+ }
46
+
47
+ export enum ExecutionMode {
48
+ SYNC = 'sync', // 同步执行
49
+ ASYNC = 'async' // 异步执行
50
+ }
51
+
52
+ export interface Task {
53
+ // 基础信息
54
+ taskId: string; // UUID
55
+ userId?: string; // 用户 ID (外键,可选)
56
+ mode: ExecutionMode; // 执行模式
57
+ topic: string; // 选题方向
58
+ requirements: string; // 写作要求
59
+
60
+ // 硬性约束
61
+ hardConstraints: {
62
+ minWords?: number;
63
+ maxWords?: number;
64
+ keywords?: string[];
65
+ };
66
+
67
+ // 状态字段
68
+ status: TaskStatus;
69
+ currentStep: string; // 当前步骤
70
+ workerId?: string; // Worker ID(多 Worker 抢占)
71
+
72
+ // 重试计数
73
+ textRetryCount: number; // 文本质检重试次数
74
+ imageRetryCount: number; // 配图质检重试次数
75
+
76
+ // 乐观锁(并发控制)
77
+ version: number; // 版本号
78
+
79
+ // 时间戳
80
+ createdAt: Date;
81
+ startedAt?: Date;
82
+ completedAt?: Date;
83
+ updatedAt: Date;
84
+ deletedAt?: Date; // 软删除
85
+
86
+ // 错误信息
87
+ errorMessage?: string;
88
+
89
+ // State 快照(崩溃恢复)
90
+ stateSnapshot?: object; // LangGraph State 序列化
91
+
92
+ // 幂等性
93
+ idempotencyKey?: string; // 幂等键(防止重复提交)
94
+ }
95
+ ```
96
+
97
+ #### **TaskStep (执行步骤)**
98
+
99
+ ```typescript
100
+ // src/domain/entities/TaskStep.ts
101
+
102
+ export enum StepName {
103
+ SEARCH = 'search',
104
+ ORGANIZE = 'organize',
105
+ WRITE = 'write',
106
+ CHECK_TEXT = 'check_text',
107
+ GENERATE_IMAGE = 'generate_image',
108
+ CHECK_IMAGE = 'check_image'
109
+ }
110
+
111
+ export enum StepStatus {
112
+ PENDING = 'pending',
113
+ RUNNING = 'running',
114
+ COMPLETED = 'completed',
115
+ FAILED = 'failed',
116
+ SKIPPED = 'skipped'
117
+ }
118
+
119
+ export interface TaskStep {
120
+ id: number;
121
+ taskId: string;
122
+ stepName: StepName;
123
+ status: StepStatus;
124
+ attempt: number; // 尝试次数(用于重试)
125
+
126
+ // 输入输出
127
+ inputData?: object; // 输入数据 (JSON)
128
+ outputData?: object; // 输出数据 (JSON)
129
+
130
+ // 性能指标
131
+ startedAt?: Date;
132
+ completedAt?: Date;
133
+ durationMs?: number; // 执行耗时(毫秒)
134
+
135
+ // 错误信息
136
+ errorMessage?: string;
137
+ }
138
+ ```
139
+
140
+ #### **QualityCheck (质检结果)**
141
+
142
+ ```typescript
143
+ // src/domain/entities/QualityCheck.ts
144
+
145
+ export enum CheckType {
146
+ TEXT = 'text',
147
+ IMAGE = 'image'
148
+ }
149
+
150
+ export interface QualityCheck {
151
+ id: number;
152
+ taskId: string;
153
+ checkType: CheckType;
154
+
155
+ // 评分
156
+ score: number; // 1-10 分
157
+ passed: boolean;
158
+ hardConstraintsPassed: boolean;
159
+
160
+ // 详情
161
+ details: {
162
+ // 硬规则检查结果
163
+ hardRules?: {
164
+ wordCount: { passed: boolean; wordCount: number };
165
+ keywords: { passed: boolean; found: string[] };
166
+ structure: { passed: boolean; checks: object };
167
+ };
168
+
169
+ // 软评分(LLM)
170
+ softScores?: {
171
+ relevance: { score: number; reason: string };
172
+ coherence: { score: number; reason: string };
173
+ completeness: { score: number; reason: string };
174
+ readability: { score: number; reason: string };
175
+ };
176
+
177
+ // 文本质检
178
+ wordCount?: number;
179
+ keywordsFound?: string[];
180
+ structureCheck?: {
181
+ hasTitle: boolean;
182
+ hasIntro: boolean;
183
+ hasBody: boolean;
184
+ hasConclusion: boolean;
185
+ };
186
+
187
+ // 配图质检
188
+ relevanceScore?: number;
189
+ aestheticScore?: number;
190
+ promptMatch?: number;
191
+ };
192
+
193
+ // 改进建议
194
+ fixSuggestions?: string[];
195
+
196
+ // 元数据
197
+ rubricVersion?: string;
198
+ modelName?: string;
199
+ promptHash?: string;
200
+
201
+ checkedAt: Date;
202
+ }
203
+ ```
204
+
205
+ #### **Result (生成结果)**
206
+
207
+ ```typescript
208
+ // src/domain/entities/Result.ts
209
+
210
+ export enum ResultType {
211
+ ARTICLE = 'article',
212
+ IMAGE = 'image'
213
+ }
214
+
215
+ export interface Result {
216
+ id: number;
217
+ taskId: string;
218
+ resultType: ResultType;
219
+ content?: string; // 文章内容 (Markdown)
220
+ filePath?: string; // 文件路径
221
+
222
+ metadata: {
223
+ // 文章元数据
224
+ wordCount?: number;
225
+ title?: string;
226
+ keywords?: string[];
227
+
228
+ // 配图元数据
229
+ prompt?: string;
230
+ url?: string;
231
+ width?: number;
232
+ height?: number;
233
+
234
+ // 来源引用
235
+ sources?: Array<{
236
+ url: string;
237
+ title: string;
238
+ snippet: string;
239
+ }>;
240
+ };
241
+
242
+ createdAt: Date;
243
+ }
244
+ ```
245
+
246
+ #### **TokenUsage (Token 使用记录)**
247
+
248
+ ```typescript
249
+ // src/domain/entities/TokenUsage.ts
250
+
251
+ export interface TokenUsage {
252
+ id: number;
253
+ taskId: string;
254
+ traceId: string; // 链路追踪 ID
255
+ stepName: string; // 步骤名称
256
+ apiName: string; // API 名称 (deepseek, doubao)
257
+ modelName: string; // 模型名称
258
+
259
+ // Token 统计
260
+ tokensIn: number; // 输入 token
261
+ tokensOut: number; // 输出 token
262
+ totalTokens: number; // 总 token
263
+
264
+ // 成本计算
265
+ costPer1kTokensIn: number;
266
+ costPer1kTokensOut: number;
267
+ totalCost: number;
268
+
269
+ // 元数据
270
+ metadata?: {
271
+ temperature?: number;
272
+ maxTokens?: number;
273
+ duration?: number;
274
+ };
275
+
276
+ createdAt: Date;
277
+ }
278
+ ```
279
+
280
+ ---
281
+
282
+ ## 🗄️ 数据库表结构设计
283
+
284
+ ### 2.1 数据库迁移脚本
285
+
286
+ **注意**: 根据实施计划修订版,阶段 1 使用简化版设计,**不含分区策略**。
287
+
288
+ #### **迁移脚本位置**
289
+ ```
290
+ migrations/
291
+ └── 001_create_initial_tables.sql
292
+ ```
293
+
294
+ #### **核心表结构**
295
+
296
+ ##### **1. tasks 表**
297
+
298
+ ```sql
299
+ -- 任务主表
300
+ CREATE TABLE IF NOT EXISTS tasks (
301
+ id SERIAL PRIMARY KEY,
302
+ task_id TEXT UNIQUE NOT NULL,
303
+ user_id TEXT, -- 用户 ID(外键,可选)
304
+
305
+ -- 执行模式和需求
306
+ mode TEXT NOT NULL CHECK (mode IN ('sync', 'async')),
307
+ topic TEXT NOT NULL,
308
+ requirements TEXT NOT NULL,
309
+
310
+ -- 硬性约束 (JSON)
311
+ hard_constraints JSONB,
312
+
313
+ -- 状态字段
314
+ status TEXT NOT NULL DEFAULT 'pending' CHECK (status IN ('pending', 'running', 'completed', 'failed')),
315
+ current_step TEXT,
316
+ worker_id TEXT,
317
+
318
+ -- 重试计数
319
+ text_retry_count INTEGER NOT NULL DEFAULT 0,
320
+ image_retry_count INTEGER NOT NULL DEFAULT 0,
321
+
322
+ -- 乐观锁
323
+ version INTEGER NOT NULL DEFAULT 1,
324
+
325
+ -- 时间戳
326
+ created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
327
+ started_at TIMESTAMP,
328
+ completed_at TIMESTAMP,
329
+ updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
330
+ deleted_at TIMESTAMP,
331
+
332
+ -- 错误和快照
333
+ error_message TEXT,
334
+ state_snapshot JSONB,
335
+
336
+ -- 幂等性
337
+ idempotency_key TEXT UNIQUE
338
+ );
339
+
340
+ -- 索引
341
+ CREATE INDEX idx_tasks_user_id ON tasks(user_id);
342
+ CREATE INDEX idx_tasks_status ON tasks(status);
343
+ CREATE INDEX idx_tasks_created_at ON tasks(created_at DESC);
344
+ CREATE INDEX idx_tasks_idempotency_key ON tasks(idempotency_key) WHERE idempotency_key IS NOT NULL;
345
+
346
+ -- 自动更新 updated_at 触发器
347
+ CREATE OR REPLACE FUNCTION update_updated_at_column()
348
+ RETURNS TRIGGER AS $$
349
+ BEGIN
350
+ NEW.updated_at = CURRENT_TIMESTAMP;
351
+ RETURN NEW;
352
+ END;
353
+ $$ language 'plpgsql';
354
+
355
+ CREATE TRIGGER update_tasks_updated_at
356
+ BEFORE UPDATE ON tasks
357
+ FOR EACH ROW
358
+ EXECUTE FUNCTION update_updated_at_column();
359
+ ```
360
+
361
+ ##### **2. task_steps 表**
362
+
363
+ ```sql
364
+ -- 执行步骤记录表
365
+ CREATE TABLE IF NOT EXISTS task_steps (
366
+ id SERIAL PRIMARY KEY,
367
+ task_id TEXT NOT NULL,
368
+ step_name TEXT NOT NULL CHECK (step_name IN (
369
+ 'search', 'organize', 'write', 'check_text',
370
+ 'generate_image', 'check_image'
371
+ )),
372
+ status TEXT NOT NULL DEFAULT 'pending' CHECK (status IN (
373
+ 'pending', 'running', 'completed', 'failed', 'skipped'
374
+ )),
375
+
376
+ -- 尝试次数
377
+ attempt INTEGER NOT NULL DEFAULT 1,
378
+
379
+ -- 输入输出 (JSON)
380
+ input_data JSONB,
381
+ output_data JSONB,
382
+
383
+ -- 性能指标
384
+ started_at TIMESTAMP,
385
+ completed_at TIMESTAMP,
386
+ duration_ms INTEGER,
387
+
388
+ -- 错误信息
389
+ error_message TEXT,
390
+
391
+ -- 时间戳
392
+ created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
393
+
394
+ -- 外键约束
395
+ FOREIGN KEY (task_id) REFERENCES tasks(task_id) ON DELETE CASCADE
396
+ );
397
+
398
+ -- 索引
399
+ CREATE INDEX idx_task_steps_task_id ON task_steps(task_id);
400
+ CREATE INDEX idx_task_steps_step_name ON task_steps(step_name);
401
+ CREATE INDEX idx_task_steps_status ON task_steps(status);
402
+ ```
403
+
404
+ ##### **3. quality_checks 表**
405
+
406
+ ```sql
407
+ -- 质检结果表
408
+ CREATE TABLE IF NOT EXISTS quality_checks (
409
+ id SERIAL PRIMARY KEY,
410
+ task_id TEXT NOT NULL,
411
+ check_type TEXT NOT NULL CHECK (check_type IN ('text', 'image')),
412
+
413
+ -- 评分
414
+ score NUMERIC(3, 2) NOT NULL CHECK (score >= 1 AND score <= 10),
415
+ passed BOOLEAN NOT NULL,
416
+ hard_constraints_passed BOOLEAN NOT NULL,
417
+
418
+ -- 详情 (JSON)
419
+ details JSONB NOT NULL,
420
+
421
+ -- 改进建议
422
+ fix_suggestions TEXT[],
423
+
424
+ -- 元数据
425
+ rubric_version TEXT,
426
+ model_name TEXT,
427
+ prompt_hash TEXT,
428
+
429
+ -- 时间戳
430
+ checked_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
431
+
432
+ -- 外键约束
433
+ FOREIGN KEY (task_id) REFERENCES tasks(task_id) ON DELETE CASCADE
434
+ );
435
+
436
+ -- 索引
437
+ CREATE INDEX idx_quality_checks_task_id ON quality_checks(task_id);
438
+ CREATE INDEX idx_quality_checks_check_type ON quality_checks(check_type);
439
+ CREATE INDEX idx_quality_checks_passed ON quality_checks(passed);
440
+ ```
441
+
442
+ ##### **4. results 表**
443
+
444
+ ```sql
445
+ -- 生成结果表
446
+ CREATE TABLE IF NOT EXISTS results (
447
+ id SERIAL PRIMARY KEY,
448
+ task_id TEXT NOT NULL,
449
+ result_type TEXT NOT NULL CHECK (result_type IN ('article', 'image')),
450
+
451
+ -- 内容
452
+ content TEXT,
453
+ file_path TEXT,
454
+
455
+ -- 元数据 (JSON)
456
+ metadata JSONB NOT NULL,
457
+
458
+ -- 时间戳
459
+ created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
460
+
461
+ -- 外键约束
462
+ FOREIGN KEY (task_id) REFERENCES tasks(task_id) ON DELETE CASCADE,
463
+
464
+ -- 约束:一个任务只能有一种类型的结果
465
+ UNIQUE (task_id, result_type)
466
+ );
467
+
468
+ -- 索引
469
+ CREATE INDEX idx_results_task_id ON results(task_id);
470
+ CREATE INDEX idx_results_result_type ON results(result_type);
471
+ ```
472
+
473
+ ##### **5. token_usage 表**
474
+
475
+ ```sql
476
+ -- Token 使用记录表
477
+ CREATE TABLE IF NOT EXISTS token_usage (
478
+ id SERIAL PRIMARY KEY,
479
+ task_id TEXT NOT NULL,
480
+ trace_id TEXT NOT NULL,
481
+ step_name TEXT NOT NULL,
482
+ api_name TEXT NOT NULL,
483
+ model_name TEXT NOT NULL,
484
+
485
+ -- Token 统计
486
+ tokens_in INTEGER NOT NULL,
487
+ tokens_out INTEGER NOT NULL,
488
+ total_tokens INTEGER NOT NULL,
489
+
490
+ -- 成本计算
491
+ cost_per_1k_tokens_in NUMERIC(10, 6) NOT NULL,
492
+ cost_per_1k_tokens_out NUMERIC(10, 6) NOT NULL,
493
+ total_cost NUMERIC(10, 6) NOT NULL,
494
+
495
+ -- 元数据 (JSON)
496
+ metadata JSONB,
497
+
498
+ -- 时间戳
499
+ created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
500
+
501
+ -- 外键约束
502
+ FOREIGN KEY (task_id) REFERENCES tasks(task_id) ON DELETE CASCADE
503
+ );
504
+
505
+ -- 索引
506
+ CREATE INDEX idx_token_usage_task_id ON token_usage(task_id);
507
+ CREATE INDEX idx_token_usage_trace_id ON token_usage(trace_id);
508
+ CREATE INDEX idx_token_usage_api_name ON token_usage(api_name);
509
+ CREATE INDEX idx_token_usage_created_at ON token_usage(created_at DESC);
510
+ ```
511
+
512
+ ##### **6. users 表 (可选)**
513
+
514
+ ```sql
515
+ -- 用户表(如果需要用户系统)
516
+ CREATE TABLE IF NOT EXISTS users (
517
+ id SERIAL PRIMARY KEY,
518
+ user_id TEXT UNIQUE NOT NULL,
519
+ email TEXT UNIQUE,
520
+ name TEXT,
521
+
522
+ -- 配额管理
523
+ quota_daily INTEGER NOT NULL DEFAULT 10,
524
+ quota_used_today INTEGER NOT NULL DEFAULT 0,
525
+
526
+ -- 状态
527
+ status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'suspended', 'deleted')),
528
+
529
+ -- 时间戳
530
+ created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
531
+ updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
532
+ deleted_at TIMESTAMP,
533
+
534
+ -- 元数据
535
+ metadata JSONB
536
+ );
537
+
538
+ -- 索引
539
+ CREATE INDEX idx_users_user_id ON users(user_id);
540
+ CREATE INDEX idx_users_email ON users(email) WHERE email IS NOT NULL;
541
+ CREATE INDEX idx_users_status ON users(status);
542
+
543
+ -- 自动更新 updated_at
544
+ CREATE TRIGGER update_users_updated_at
545
+ BEFORE UPDATE ON users
546
+ FOR EACH ROW
547
+ EXECUTE FUNCTION update_updated_at_column();
548
+ ```
549
+
550
+ ---
551
+
552
+ ## 🔧 Repository 实现
553
+
554
+ ### 3.1 Repository 基类
555
+
556
+ ```typescript
557
+ // src/infrastructure/database/BaseRepository.ts
558
+
559
+ import { Pool, PoolClient, QueryResult } from 'pg';
560
+ import { config } from '../../config';
561
+
562
+ /**
563
+ * Repository 基类
564
+ * 提供通用的数据库操作和事务管理
565
+ */
566
+ export abstract class BaseRepository {
567
+ protected pool: Pool;
568
+
569
+ constructor(pool?: Pool) {
570
+ this.pool = pool || new Pool({
571
+ host: config.database.host,
572
+ port: config.database.port,
573
+ database: config.database.name,
574
+ user: config.database.user,
575
+ password: config.database.password,
576
+ max: 20, // 连接池大小
577
+ idleTimeoutMillis: 30000,
578
+ connectionTimeoutMillis: 2000,
579
+ });
580
+ }
581
+
582
+ /**
583
+ * 执行查询
584
+ */
585
+ protected async query<T>(text: string, params?: any[]): Promise<QueryResult<T>> {
586
+ const start = Date.now();
587
+ try {
588
+ const res = await this.pool.query<T>(text, params);
589
+ const duration = Date.now() - start;
590
+ console.log('Executed query', { text, duration, rows: res.rowCount });
591
+ return res;
592
+ } catch (error) {
593
+ console.error('Query error', { text, error });
594
+ throw error;
595
+ }
596
+ }
597
+
598
+ /**
599
+ * 获取数据库连接(用于事务)
600
+ */
601
+ protected async getConnection(): Promise<PoolClient> {
602
+ return await this.pool.connect();
603
+ }
604
+
605
+ /**
606
+ * 执行事务
607
+ */
608
+ protected async transaction<T>(
609
+ callback: (client: PoolClient) => Promise<T>
610
+ ): Promise<T> {
611
+ const client = await this.getConnection();
612
+ try {
613
+ await client.query('BEGIN');
614
+ const result = await callback(client);
615
+ await client.query('COMMIT');
616
+ return result;
617
+ } catch (error) {
618
+ await client.query('ROLLBACK');
619
+ throw error;
620
+ } finally {
621
+ client.release();
622
+ }
623
+ }
624
+
625
+ /**
626
+ * 关闭连接池
627
+ */
628
+ async close(): Promise<void> {
629
+ await this.pool.end();
630
+ }
631
+ }
632
+ ```
633
+
634
+ ### 3.2 TaskRepository 接口
635
+
636
+ ```typescript
637
+ // src/domain/repositories/TaskRepository.ts
638
+
639
+ import { Task, TaskStatus, ExecutionMode } from '../entities/Task';
640
+
641
+ export interface CreateTaskInput {
642
+ userId?: string;
643
+ mode: ExecutionMode;
644
+ topic: string;
645
+ requirements: string;
646
+ hardConstraints?: {
647
+ minWords?: number;
648
+ maxWords?: number;
649
+ keywords?: string[];
650
+ };
651
+ idempotencyKey?: string;
652
+ }
653
+
654
+ export interface TaskRepository {
655
+ /**
656
+ * 创建任务
657
+ */
658
+ create(input: CreateTaskInput): Promise<Task>;
659
+
660
+ /**
661
+ * 根据 taskId 查询任务
662
+ */
663
+ findById(taskId: string): Promise<Task | null>;
664
+
665
+ /**
666
+ * 根据 userId 查询任务列表
667
+ */
668
+ findByUserId(userId: string, limit?: number, offset?: number): Promise<Task[]>;
669
+
670
+ /**
671
+ * 更新任务状态
672
+ */
673
+ updateStatus(taskId: string, status: TaskStatus, version: number): Promise<boolean>;
674
+
675
+ /**
676
+ * Worker 抢占任务(乐观锁)
677
+ */
678
+ claimTask(taskId: string, workerId: string, version: number): Promise<boolean>;
679
+
680
+ /**
681
+ * 更新当前步骤
682
+ */
683
+ updateCurrentStep(taskId: string, step: string, version: number): Promise<boolean>;
684
+
685
+ /**
686
+ * 增加重试计数
687
+ */
688
+ incrementRetryCount(
689
+ taskId: string,
690
+ type: 'text' | 'image',
691
+ version: number
692
+ ): Promise<boolean>;
693
+
694
+ /**
695
+ * 保存 State 快照
696
+ */
697
+ saveStateSnapshot(taskId: string, snapshot: object, version: number): Promise<boolean>;
698
+
699
+ /**
700
+ * 标记任务完成
701
+ */
702
+ markAsCompleted(taskId: string, version: number): Promise<boolean>;
703
+
704
+ /**
705
+ * 标记任务失败
706
+ */
707
+ markAsFailed(taskId: string, errorMessage: string, version: number): Promise<boolean>;
708
+
709
+ /**
710
+ * 软删除任务
711
+ */
712
+ softDelete(taskId: string): Promise<boolean>;
713
+ }
714
+ ```
715
+
716
+ ### 3.3 TaskRepository 实现
717
+
718
+ ```typescript
719
+ // src/infrastructure/database/PostgresTaskRepository.ts
720
+
721
+ import { BaseRepository } from './BaseRepository';
722
+ import { TaskRepository, CreateTaskInput } from '../../domain/repositories/TaskRepository';
723
+ import { Task, TaskStatus, ExecutionMode } from '../../domain/entities/Task';
724
+ import { v4 as uuidv4 } from 'uuid';
725
+
726
+ export class PostgresTaskRepository extends BaseRepository implements TaskRepository {
727
+
728
+ /**
729
+ * 创建任务
730
+ */
731
+ async create(input: CreateTaskInput): Promise<Task> {
732
+ const taskId = input.idempotencyKey || uuidv4();
733
+
734
+ const query = `
735
+ INSERT INTO tasks (
736
+ task_id, user_id, mode, topic, requirements,
737
+ hard_constraints, idempotency_key
738
+ ) VALUES ($1, $2, $3, $4, $5, $6, $7)
739
+ RETURNING *
740
+ `;
741
+
742
+ const values = [
743
+ taskId,
744
+ input.userId || null,
745
+ input.mode,
746
+ input.topic,
747
+ input.requirements,
748
+ JSON.stringify(input.hardConstraints || {}),
749
+ input.idempotencyKey || null,
750
+ ];
751
+
752
+ const result = await this.query<any>(query, values);
753
+ return this.mapToTask(result.rows[0]);
754
+ }
755
+
756
+ /**
757
+ * 根据 taskId 查询任务
758
+ */
759
+ async findById(taskId: string): Promise<Task | null> {
760
+ const query = 'SELECT * FROM tasks WHERE task_id = $1 AND deleted_at IS NULL';
761
+ const result = await this.query<any>(query, [taskId]);
762
+
763
+ if (result.rows.length === 0) {
764
+ return null;
765
+ }
766
+
767
+ return this.mapToTask(result.rows[0]);
768
+ }
769
+
770
+ /**
771
+ * 根据 userId 查询任务列表
772
+ */
773
+ async findByUserId(userId: string, limit = 10, offset = 0): Promise<Task[]> {
774
+ const query = `
775
+ SELECT * FROM tasks
776
+ WHERE user_id = $1 AND deleted_at IS NULL
777
+ ORDER BY created_at DESC
778
+ LIMIT $2 OFFSET $3
779
+ `;
780
+
781
+ const result = await this.query<any>(query, [userId, limit, offset]);
782
+ return result.rows.map(row => this.mapToTask(row));
783
+ }
784
+
785
+ /**
786
+ * 更新任务状态(带乐观锁)
787
+ */
788
+ async updateStatus(taskId: string, status: TaskStatus, version: number): Promise<boolean> {
789
+ const query = `
790
+ UPDATE tasks
791
+ SET status = $1,
792
+ version = version + 1,
793
+ updated_at = CURRENT_TIMESTAMP
794
+ WHERE task_id = $2 AND version = $3
795
+ RETURNING version
796
+ `;
797
+
798
+ const result = await this.query(query, [status, taskId, version]);
799
+ return result.rowCount === 1;
800
+ }
801
+
802
+ /**
803
+ * Worker 抢占任务(乐观锁)
804
+ */
805
+ async claimTask(taskId: string, workerId: string, version: number): Promise<boolean> {
806
+ const query = `
807
+ UPDATE tasks
808
+ SET worker_id = $1,
809
+ status = 'running',
810
+ started_at = CURRENT_TIMESTAMP,
811
+ version = version + 1
812
+ WHERE task_id = $2
813
+ AND version = $3
814
+ AND status = 'pending'
815
+ RETURNING version
816
+ `;
817
+
818
+ const result = await this.query(query, [workerId, taskId, version]);
819
+ return result.rowCount === 1;
820
+ }
821
+
822
+ /**
823
+ * 更新当前步骤
824
+ */
825
+ async updateCurrentStep(taskId: string, step: string, version: number): Promise<boolean> {
826
+ const query = `
827
+ UPDATE tasks
828
+ SET current_step = $1,
829
+ version = version + 1
830
+ WHERE task_id = $2 AND version = $3
831
+ RETURNING version
832
+ `;
833
+
834
+ const result = await this.query(query, [step, taskId, version]);
835
+ return result.rowCount === 1;
836
+ }
837
+
838
+ /**
839
+ * 增加重试计数
840
+ */
841
+ async incrementRetryCount(
842
+ taskId: string,
843
+ type: 'text' | 'image',
844
+ version: number
845
+ ): Promise<boolean> {
846
+ const column = type === 'text' ? 'text_retry_count' : 'image_retry_count';
847
+
848
+ const query = `
849
+ UPDATE tasks
850
+ SET ${column} = ${column} + 1,
851
+ version = version + 1
852
+ WHERE task_id = $1 AND version = $2
853
+ RETURNING version
854
+ `;
855
+
856
+ const result = await this.query(query, [taskId, version]);
857
+ return result.rowCount === 1;
858
+ }
859
+
860
+ /**
861
+ * 保存 State 快照
862
+ */
863
+ async saveStateSnapshot(taskId: string, snapshot: object, version: number): Promise<boolean> {
864
+ const query = `
865
+ UPDATE tasks
866
+ SET state_snapshot = $1,
867
+ version = version + 1
868
+ WHERE task_id = $2 AND version = $3
869
+ RETURNING version
870
+ `;
871
+
872
+ const result = await this.query(query, [JSON.stringify(snapshot), taskId, version]);
873
+ return result.rowCount === 1;
874
+ }
875
+
876
+ /**
877
+ * 标记任务完成
878
+ */
879
+ async markAsCompleted(taskId: string, version: number): Promise<boolean> {
880
+ const query = `
881
+ UPDATE tasks
882
+ SET status = 'completed',
883
+ completed_at = CURRENT_TIMESTAMP,
884
+ version = version + 1
885
+ WHERE task_id = $1 AND version = $2
886
+ RETURNING version
887
+ `;
888
+
889
+ const result = await this.query(query, [taskId, version]);
890
+ return result.rowCount === 1;
891
+ }
892
+
893
+ /**
894
+ * 标记任务失败
895
+ */
896
+ async markAsFailed(taskId: string, errorMessage: string, version: number): Promise<boolean> {
897
+ const query = `
898
+ UPDATE tasks
899
+ SET status = 'failed',
900
+ error_message = $1,
901
+ completed_at = CURRENT_TIMESTAMP,
902
+ version = version + 1
903
+ WHERE task_id = $2 AND version = $3
904
+ RETURNING version
905
+ `;
906
+
907
+ const result = await this.query(query, [errorMessage, taskId, version]);
908
+ return result.rowCount === 1;
909
+ }
910
+
911
+ /**
912
+ * 软删除任务
913
+ */
914
+ async softDelete(taskId: string): Promise<boolean> {
915
+ const query = `
916
+ UPDATE tasks
917
+ SET deleted_at = CURRENT_TIMESTAMP
918
+ WHERE task_id = $1 AND deleted_at IS NULL
919
+ RETURNING task_id
920
+ `;
921
+
922
+ const result = await this.query(query, [taskId]);
923
+ return result.rowCount === 1;
924
+ }
925
+
926
+ /**
927
+ * 映射数据库行到 Task 实体
928
+ */
929
+ private mapToTask(row: any): Task {
930
+ return {
931
+ taskId: row.task_id,
932
+ userId: row.user_id || undefined,
933
+ mode: row.mode as ExecutionMode,
934
+ topic: row.topic,
935
+ requirements: row.requirements,
936
+ hardConstraints: row.hard_constraints || {},
937
+ status: row.status as TaskStatus,
938
+ currentStep: row.current_step || '',
939
+ workerId: row.worker_id || undefined,
940
+ textRetryCount: row.text_retry_count,
941
+ imageRetryCount: row.image_retry_count,
942
+ version: row.version,
943
+ createdAt: new Date(row.created_at),
944
+ startedAt: row.started_at ? new Date(row.started_at) : undefined,
945
+ completedAt: row.completed_at ? new Date(row.completed_at) : undefined,
946
+ updatedAt: new Date(row.updated_at),
947
+ deletedAt: row.deleted_at ? new Date(row.deleted_at) : undefined,
948
+ errorMessage: row.error_message || undefined,
949
+ stateSnapshot: row.state_snapshot || undefined,
950
+ idempotencyKey: row.idempotency_key || undefined,
951
+ };
952
+ }
953
+ }
954
+ ```
955
+
956
+ ---
957
+
958
+ ## 🧪 测试策略
959
+
960
+ ### 4.1 测试框架配置
961
+
962
+ ```typescript
963
+ // tests/setup.ts
964
+
965
+ import { Pool } from 'pg';
966
+ import { config } from '../src/config';
967
+
968
+ let testPool: Pool;
969
+
970
+ export async function setupTestDatabase() {
971
+ testPool = new Pool({
972
+ host: config.database.host,
973
+ port: config.database.port,
974
+ database: config.database.name + '_test', // 测试数据库
975
+ user: config.database.user,
976
+ password: config.database.password,
977
+ });
978
+
979
+ // 清空测试表
980
+ await testPool.query('TRUNCATE TABLE token_usage, results, quality_checks, task_steps, tasks CASCADE');
981
+
982
+ return testPool;
983
+ }
984
+
985
+ export async function teardownTestDatabase() {
986
+ await testPool?.end();
987
+ }
988
+
989
+ export { testPool };
990
+ ```
991
+
992
+ ### 4.2 Repository 单元测试
993
+
994
+ ```typescript
995
+ // tests/unit/repositories/TaskRepository.test.ts
996
+
997
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
998
+ import { PostgresTaskRepository } from '../../../src/infrastructure/database/PostgresTaskRepository';
999
+ import { setupTestDatabase, teardownTestDatabase } from '../../setup';
1000
+ import { TaskStatus, ExecutionMode } from '../../../src/domain/entities/Task';
1001
+
1002
+ describe('PostgresTaskRepository', () => {
1003
+ let repository: PostgresTaskRepository;
1004
+ let pool: any;
1005
+
1006
+ beforeEach(async () => {
1007
+ pool = await setupTestDatabase();
1008
+ repository = new PostgresTaskRepository(pool);
1009
+ });
1010
+
1011
+ afterEach(async () => {
1012
+ await teardownTestDatabase();
1013
+ });
1014
+
1015
+ describe('create', () => {
1016
+ it('应该成功创建任务', async () => {
1017
+ const input = {
1018
+ mode: ExecutionMode.SYNC,
1019
+ topic: 'AI 技术发展',
1020
+ requirements: '写一篇关于 AI 技术发展的文章',
1021
+ hardConstraints: {
1022
+ minWords: 500,
1023
+ maxWords: 1000,
1024
+ keywords: ['AI', '技术', '发展'],
1025
+ },
1026
+ };
1027
+
1028
+ const task = await repository.create(input);
1029
+
1030
+ expect(task).toBeDefined();
1031
+ expect(task.taskId).toBeDefined();
1032
+ expect(task.status).toBe(TaskStatus.PENDING);
1033
+ expect(task.version).toBe(1);
1034
+ expect(task.topic).toBe(input.topic);
1035
+ });
1036
+
1037
+ it('应该支持幂等键', async () => {
1038
+ const input = {
1039
+ mode: ExecutionMode.SYNC,
1040
+ topic: '测试选题',
1041
+ requirements: '测试要求',
1042
+ idempotencyKey: 'test-key-123',
1043
+ };
1044
+
1045
+ const task1 = await repository.create(input);
1046
+ const task2 = await repository.create(input);
1047
+
1048
+ expect(task1.taskId).toBe(task2.taskId);
1049
+ expect(task1.taskId).toBe('test-key-123');
1050
+ });
1051
+ });
1052
+
1053
+ describe('findById', () => {
1054
+ it('应该正确查询任务', async () => {
1055
+ const input = {
1056
+ mode: ExecutionMode.ASYNC,
1057
+ topic: '测试选题',
1058
+ requirements: '测试要求',
1059
+ };
1060
+
1061
+ const created = await repository.create(input);
1062
+ const found = await repository.findById(created.taskId);
1063
+
1064
+ expect(found).toBeDefined();
1065
+ expect(found?.taskId).toBe(created.taskId);
1066
+ expect(found?.status).toBe(created.status);
1067
+ });
1068
+
1069
+ it('查询不存在的任务应该返回 null', async () => {
1070
+ const found = await repository.findById('non-existent-id');
1071
+ expect(found).toBeNull();
1072
+ });
1073
+ });
1074
+
1075
+ describe('updateStatus', () => {
1076
+ it('应该正确更新任务状态', async () => {
1077
+ const task = await repository.create({
1078
+ mode: ExecutionMode.SYNC,
1079
+ topic: '测试',
1080
+ requirements: '测试',
1081
+ });
1082
+
1083
+ const updated = await repository.updateStatus(task.taskId, TaskStatus.RUNNING, task.version);
1084
+
1085
+ expect(updated).toBe(true);
1086
+
1087
+ const found = await repository.findById(task.taskId);
1088
+ expect(found?.status).toBe(TaskStatus.RUNNING);
1089
+ expect(found?.version).toBe(2);
1090
+ });
1091
+
1092
+ it('乐观锁应该生效', async () => {
1093
+ const task = await repository.create({
1094
+ mode: ExecutionMode.SYNC,
1095
+ topic: '测试',
1096
+ requirements: '测试',
1097
+ });
1098
+
1099
+ // 使用错误的版本号
1100
+ const updated = await repository.updateStatus(task.taskId, TaskStatus.RUNNING, 999);
1101
+
1102
+ expect(updated).toBe(false);
1103
+ });
1104
+ });
1105
+
1106
+ describe('claimTask', () => {
1107
+ it('Worker 应该能成功抢占任务', async () => {
1108
+ const task = await repository.create({
1109
+ mode: ExecutionMode.ASYNC,
1110
+ topic: '测试',
1111
+ requirements: '测试',
1112
+ });
1113
+
1114
+ const claimed = await repository.claimTask(task.taskId, 'worker-1', task.version);
1115
+
1116
+ expect(claimed).toBe(true);
1117
+
1118
+ const found = await repository.findById(task.taskId);
1119
+ expect(found?.workerId).toBe('worker-1');
1120
+ expect(found?.status).toBe(TaskStatus.RUNNING);
1121
+ });
1122
+
1123
+ it('多个 Worker 抢占同一任务应该只有一个成功', async () => {
1124
+ const task = await repository.create({
1125
+ mode: ExecutionMode.ASYNC,
1126
+ topic: '测试',
1127
+ requirements: '测试',
1128
+ });
1129
+
1130
+ // 模拟并发抢占
1131
+ const [claim1, claim2, claim3] = await Promise.all([
1132
+ repository.claimTask(task.taskId, 'worker-1', task.version),
1133
+ repository.claimTask(task.taskId, 'worker-2', task.version),
1134
+ repository.claimTask(task.taskId, 'worker-3', task.version),
1135
+ ]);
1136
+
1137
+ // 应该只有一个成功
1138
+ const successCount = [claim1, claim2, claim3].filter(c => c).length;
1139
+ expect(successCount).toBe(1);
1140
+
1141
+ const found = await repository.findById(task.taskId);
1142
+ expect(found?.workerId).toBeDefined();
1143
+ expect(found?.status).toBe(TaskStatus.RUNNING);
1144
+ });
1145
+ });
1146
+ });
1147
+ ```
1148
+
1149
+ ### 4.3 并发测试
1150
+
1151
+ ```typescript
1152
+ // tests/integration/concurrency.test.ts
1153
+
1154
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
1155
+ import { PostgresTaskRepository } from '../../../src/infrastructure/database/PostgresTaskRepository';
1156
+ import { setupTestDatabase, teardownTestDatabase } from '../../setup';
1157
+ import { ExecutionMode } from '../../../src/domain/entities/Task';
1158
+
1159
+ describe('并发测试: 乐观锁验证', () => {
1160
+ let repository: PostgresTaskRepository;
1161
+ let pool: any;
1162
+
1163
+ beforeEach(async () => {
1164
+ pool = await setupTestDatabase();
1165
+ repository = new PostgresTaskRepository(pool);
1166
+ });
1167
+
1168
+ afterEach(async () => {
1169
+ await teardownTestDatabase();
1170
+ });
1171
+
1172
+ it('多个 Worker 同时更新同一任务应该只有一个成功', async () => {
1173
+ const task = await repository.create({
1174
+ mode: ExecutionMode.ASYNC,
1175
+ topic: '测试',
1176
+ requirements: '测试',
1177
+ });
1178
+
1179
+ // 模拟 10 个并发更新
1180
+ const updatePromises = Array.from({ length: 10 }, (_, i) =>
1181
+ repository.updateStatus(task.taskId, 'running' as any, task.version)
1182
+ );
1183
+
1184
+ const results = await Promise.all(updatePromises);
1185
+ const successCount = results.filter(r => r).length;
1186
+
1187
+ // 只有一个应该成功
1188
+ expect(successCount).toBe(1);
1189
+
1190
+ // 验证最终状态
1191
+ const finalTask = await repository.findById(task.taskId);
1192
+ expect(finalTask?.version).toBe(2); // 只增加了一次
1193
+ });
1194
+
1195
+ it('并发抢占任务测试', async () => {
1196
+ const task = await repository.create({
1197
+ mode: ExecutionMode.ASYNC,
1198
+ topic: '测试',
1199
+ requirements: '测试',
1200
+ });
1201
+
1202
+ // 5 个 Worker 同时抢占
1203
+ const workers = Array.from({ length: 5 }, (_, i) => `worker-${i + 1}`);
1204
+ const claimPromises = workers.map(workerId =>
1205
+ repository.claimTask(task.taskId, workerId, task.version)
1206
+ );
1207
+
1208
+ const results = await Promise.all(claimPromises);
1209
+ const successCount = results.filter(r => r).length;
1210
+
1211
+ // 只有一个抢占成功
1212
+ expect(successCount).toBe(1);
1213
+
1214
+ // 验证任务被哪个 Worker 抢占
1215
+ const finalTask = await repository.findById(task.taskId);
1216
+ expect(workers).toContain(finalTask?.workerId);
1217
+ expect(finalTask?.status).toBe('running');
1218
+ });
1219
+ });
1220
+ ```
1221
+
1222
+ ---
1223
+
1224
+ ## 📊 任务清单
1225
+
1226
+ ### 按优先级排序
1227
+
1228
+ #### P0 - 阻塞性任务
1229
+
1230
+ - [ ] **任务 1.1**: 完善领域模型实体类 (1 天)
1231
+ - [ ] 1.1.1: 完善 `Task.ts` 实体类
1232
+ - [ ] 1.1.2: 完善 `TaskStep.ts` 实体类
1233
+ - [ ] 1.1.3: 完善 `QualityCheck.ts` 实体类
1234
+ - [ ] 1.1.4: 完善 `Result.ts` 实体类
1235
+ - [ ] 1.1.5: 完善 `TokenUsage.ts` 实体类
1236
+ - [ ] 1.1.6: 添加 Zod 验证 Schema
1237
+
1238
+ - [ ] **任务 1.2**: 创建数据库迁移脚本 (1 天)
1239
+ - [ ] 1.2.1: 创建 `migrations/001_create_initial_tables.sql`
1240
+ - [ ] 1.2.2: 创建 `tasks` 表和索引
1241
+ - [ ] 1.2.3: 创建 `task_steps` 表和索引
1242
+ - [ ] 1.2.4: 创建 `quality_checks` 表和索引
1243
+ - [ ] 1.2.5: 创建 `results` 表和索引
1244
+ - [ ] 1.2.6: 创建 `token_usage` 表和索引
1245
+ - [ ] 1.2.7: 创建 `users` 表和索引
1246
+ - [ ] 1.2.8: 添加更新时间触发器函数
1247
+ - [ ] 1.2.9: 测试迁移脚本可重复执行
1248
+
1249
+ - [ ] **任务 1.3**: 实现 Repository 基类 (0.5 天)
1250
+ - [ ] 1.3.1: 创建 `BaseRepository.ts`
1251
+ - [ ] 1.3.2: 实现连接池管理
1252
+ - [ ] 1.3.3: 实现 `query()` 方法
1253
+ - [ ] 1.3.4: 实现 `transaction()` 方法
1254
+
1255
+ - [ ] **任务 1.4**: 实现 TaskRepository (1.5 天)
1256
+ - [ ] 1.4.1: 定义 `TaskRepository` 接口
1257
+ - [ ] 1.4.2: 实现 `create()` 方法
1258
+ - [ ] 1.4.3: 实现 `findById()` 方法
1259
+ - [ ] 1.4.4: 实现 `findByUserId()` 方法
1260
+ - [ ] 1.4.5: 实现 `updateStatus()` 方法(乐观锁)
1261
+ - [ ] 1.4.6: 实现 `claimTask()` 方法(抢占机制)
1262
+ - [ ] 1.4.7: 实现其他更新方法
1263
+ - [ ] 1.4.8: 添加数据映射方法
1264
+
1265
+ #### P1 - 高优先级任务
1266
+
1267
+ - [ ] **任务 1.5**: 编写单元测试 (1.5 天)
1268
+ - [ ] 1.5.1: 配置 Vitest 测试环境
1269
+ - [ ] 1.5.2: 创建测试数据库设置
1270
+ - [ ] 1.5.3: 编写 `create()` 测试
1271
+ - [ ] 1.5.4: 编写 `findById()` 测试
1272
+ - [ ] 1.5.5: 编写 `updateStatus()` 测试
1273
+ - [ ] 1.5.6: 编写 `claimTask()` 测试
1274
+ - [ ] 1.5.7: 编写乐观锁验证测试
1275
+
1276
+ - [ ] **任务 1.6**: 并发测试 (0.5 天)
1277
+ - [ ] 1.6.1: 编写并发更新测试
1278
+ - [ ] 1.6.2: 编写并发抢占测试
1279
+ - [ ] 1.6.3: 验证乐观锁正确性
1280
+
1281
+ #### P2 - 中优先级任务
1282
+
1283
+ - [ ] **任务 1.7**: 性能优化 (0.5 天)
1284
+ - [ ] 1.7.1: 优化数据库索引
1285
+ - [ ] 1.7.2: 配置连接池参数
1286
+ - [ ] 1.7.3: 添加查询性能监控
1287
+ - [ ] 1.7.4: 编写性能基准测试
1288
+
1289
+ - [ ] **任务 1.8**: 文档完善 (0.5 天)
1290
+ - [ ] 1.8.1: 编写 API 文档
1291
+ - [ ] 1.8.2: 添加使用示例
1292
+ - [ ] 1.8.3: 更新 README
1293
+
1294
+ ---
1295
+
1296
+ ## 🚀 快速开始
1297
+
1298
+ ### 1. 安装依赖
1299
+
1300
+ ```bash
1301
+ pnpm install
1302
+ ```
1303
+
1304
+ ### 2. 配置数据库连接
1305
+
1306
+ 编辑 `.env` 文件:
1307
+
1308
+ ```env
1309
+ # Database
1310
+ DATABASE_HOST=localhost
1311
+ DATABASE_PORT=5432
1312
+ DATABASE_NAME=content_creator
1313
+ DATABASE_USER=postgres
1314
+ DATABASE_PASSWORD=your_password
1315
+ ```
1316
+
1317
+ ### 3. 运行数据库迁移
1318
+
1319
+ ```bash
1320
+ # 创建数据库
1321
+ createdb content_creator
1322
+
1323
+ # 运行迁移脚本
1324
+ psql -U postgres -d content_creator -f migrations/001_create_initial_tables.sql
1325
+ ```
1326
+
1327
+ ### 4. 运行测试
1328
+
1329
+ ```bash
1330
+ # 运行所有测试
1331
+ pnpm test
1332
+
1333
+ # 运行单元测试
1334
+ pnpm test:unit
1335
+
1336
+ # 运行并发测试
1337
+ pnpm test:concurrency
1338
+
1339
+ # 查看测试覆盖率
1340
+ pnpm test:coverage
1341
+ ```
1342
+
1343
+ ---
1344
+
1345
+ ## ⚠️ 注意事项
1346
+
1347
+ ### 开发注意事项
1348
+
1349
+ 1. **乐观锁使用**: 所有更新操作必须传入 `version` 参数
1350
+ 2. **幂等键**: 使用幂等键防止重复提交任务
1351
+ 3. **软删除**: 删除操作使用软删除,不物理删除数据
1352
+ 4. **JSON 字段**: 约束条件、快照等使用 JSONB 存储
1353
+
1354
+ ### 测试注意事项
1355
+
1356
+ 1. **测试隔离**: 每个测试前清空测试表
1357
+ 2. **并发测试**: 使用真实的并发操作验证乐观锁
1358
+ 3. **覆盖率**: 确保测试覆盖率 > 80%
1359
+ 4. **边界条件**: 测试空值、并发冲突等边界情况
1360
+
1361
+ ### 性能注意事项
1362
+
1363
+ 1. **连接池**: 合理配置连接池大小(默认 20)
1364
+ 2. **索引**: 为常用查询字段添加索引
1365
+ 3. **事务**: 复杂操作使用事务保证一致性
1366
+ 4. **监控**: 添加查询性能监控
1367
+
1368
+ ---
1369
+
1370
+ ## 📚 相关资源
1371
+
1372
+ - [完整架构文档](./architecture-complete.md)
1373
+ - [实施战略规划](./implementation-analysis-plan.md)
1374
+ - [Backend 开发规范](../.claude/skills/backend-dev-guidelines/SKILL.md)
1375
+
1376
+ ---
1377
+
1378
+ **文档版本**: 1.0
1379
+ **创建日期**: 2025-01-18
1380
+ **最后更新**: 2025-01-18