sa2kit 1.6.44 → 1.6.45

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 (267) hide show
  1. package/dist/AliyunOSSProvider-2FARPAQD.js +15 -0
  2. package/dist/{AliyunOSSProvider-HCNGDJL7.js.map → AliyunOSSProvider-2FARPAQD.js.map} +1 -1
  3. package/dist/AliyunOSSProvider-UMVGVBDJ.mjs +9 -0
  4. package/dist/{AliyunOSSProvider-4W47OFEK.mjs.map → AliyunOSSProvider-UMVGVBDJ.mjs.map} +1 -1
  5. package/dist/CollisionBalls-BpHufX3H.d.mts +41 -0
  6. package/dist/CollisionBalls-BpHufX3H.d.ts +41 -0
  7. package/dist/ConfigService-QR67WYNK.mjs +4 -0
  8. package/dist/{ConfigService-V6ZK273Z.mjs.map → ConfigService-QR67WYNK.mjs.map} +1 -1
  9. package/dist/{ConfigService-3DIC6C3Q.js → ConfigService-UYC6ZTCM.js} +3 -3
  10. package/dist/{ConfigService-3DIC6C3Q.js.map → ConfigService-UYC6ZTCM.js.map} +1 -1
  11. package/dist/GenericOrderManager-e4WizpNf.d.mts +28 -0
  12. package/dist/GenericOrderManager-e4WizpNf.d.ts +28 -0
  13. package/dist/LocalStorageProvider-JQF5WK5H.js +15 -0
  14. package/dist/{LocalStorageProvider-PP7MA5OT.js.map → LocalStorageProvider-JQF5WK5H.js.map} +1 -1
  15. package/dist/LocalStorageProvider-PYOHETJV.mjs +9 -0
  16. package/dist/{LocalStorageProvider-3RVPCQB3.mjs.map → LocalStorageProvider-PYOHETJV.mjs.map} +1 -1
  17. package/dist/PMXParser-RLBDO7YK.mjs +4 -0
  18. package/dist/{PMXParser-RNVQL76A.mjs.map → PMXParser-RLBDO7YK.mjs.map} +1 -1
  19. package/dist/{PMXParser-2VTA737I.js → PMXParser-XHNO2KNI.js} +3 -3
  20. package/dist/{PMXParser-2VTA737I.js.map → PMXParser-XHNO2KNI.js.map} +1 -1
  21. package/dist/UniversalFileService-RBV6EN5J.js +15 -0
  22. package/dist/UniversalFileService-RBV6EN5J.js.map +1 -0
  23. package/dist/UniversalFileService-TNYKO6JN.mjs +9 -0
  24. package/dist/UniversalFileService-TNYKO6JN.mjs.map +1 -0
  25. package/dist/analytics/index.js +1 -1
  26. package/dist/analytics/index.mjs +1 -1
  27. package/dist/analytics/server/index.js +1 -1
  28. package/dist/analytics/server/index.mjs +1 -1
  29. package/dist/api/index.js +1 -1
  30. package/dist/api/index.mjs +1 -1
  31. package/dist/audioDetection/index.js +1 -1
  32. package/dist/audioDetection/index.mjs +1 -1
  33. package/dist/auth/client/index.js +1 -1
  34. package/dist/auth/client/index.mjs +1 -1
  35. package/dist/auth/components/index.js +1 -1
  36. package/dist/auth/components/index.mjs +1 -1
  37. package/dist/auth/hooks/index.js +1 -1
  38. package/dist/auth/hooks/index.mjs +1 -1
  39. package/dist/auth/index.js +1 -1
  40. package/dist/auth/index.mjs +1 -1
  41. package/dist/auth/middleware/index.js +1 -1
  42. package/dist/auth/middleware/index.mjs +1 -1
  43. package/dist/auth/routes/index.js +1 -1
  44. package/dist/auth/routes/index.mjs +1 -1
  45. package/dist/auth/schema/index.js +1 -1
  46. package/dist/auth/schema/index.mjs +1 -1
  47. package/dist/auth/services/index.js +1 -1
  48. package/dist/auth/services/index.mjs +1 -1
  49. package/dist/calendar/index.js +12 -12
  50. package/dist/calendar/index.mjs +5 -5
  51. package/dist/calendar/routes/index.js +1 -1
  52. package/dist/calendar/routes/index.mjs +1 -1
  53. package/dist/calendar/server.js +1 -1
  54. package/dist/calendar/server.mjs +1 -1
  55. package/dist/chunk-2PS5PIXV.mjs +443 -0
  56. package/dist/chunk-2PS5PIXV.mjs.map +1 -0
  57. package/dist/chunk-35CXIK5Y.js +277 -0
  58. package/dist/chunk-35CXIK5Y.js.map +1 -0
  59. package/dist/{chunk-GMIUSZXC.mjs → chunk-3JW4X3AC.mjs} +3 -3
  60. package/dist/{chunk-GMIUSZXC.mjs.map → chunk-3JW4X3AC.mjs.map} +1 -1
  61. package/dist/chunk-3M6T5KVD.js +453 -0
  62. package/dist/chunk-3M6T5KVD.js.map +1 -0
  63. package/dist/chunk-3TNR6IMC.js +168 -0
  64. package/dist/chunk-3TNR6IMC.js.map +1 -0
  65. package/dist/chunk-4NFOSCM6.js +34 -0
  66. package/dist/chunk-4NFOSCM6.js.map +1 -0
  67. package/dist/{chunk-3NHAT7D4.mjs → chunk-4VJQZSPU.mjs} +4 -3
  68. package/dist/chunk-4VJQZSPU.mjs.map +1 -0
  69. package/dist/{chunk-SCDDMIF6.js → chunk-4XXIBWCO.js} +66 -66
  70. package/dist/{chunk-SCDDMIF6.js.map → chunk-4XXIBWCO.js.map} +1 -1
  71. package/dist/chunk-6AHYPPUP.js +344 -0
  72. package/dist/chunk-6AHYPPUP.js.map +1 -0
  73. package/dist/{chunk-EGJPS7OL.mjs → chunk-6BJ76BYC.mjs} +3 -3
  74. package/dist/{chunk-EGJPS7OL.mjs.map → chunk-6BJ76BYC.mjs.map} +1 -1
  75. package/dist/chunk-76V7EKBX.mjs +796 -0
  76. package/dist/chunk-76V7EKBX.mjs.map +1 -0
  77. package/dist/chunk-ACLOJXXE.js +195 -0
  78. package/dist/chunk-ACLOJXXE.js.map +1 -0
  79. package/dist/chunk-AEXPAH7Z.mjs +32 -0
  80. package/dist/chunk-AEXPAH7Z.mjs.map +1 -0
  81. package/dist/chunk-CFGX3EKK.js +560 -0
  82. package/dist/chunk-CFGX3EKK.js.map +1 -0
  83. package/dist/chunk-D2HXMGXS.js +46 -0
  84. package/dist/chunk-D2HXMGXS.js.map +1 -0
  85. package/dist/chunk-DM2GUVUH.js +1201 -0
  86. package/dist/chunk-DM2GUVUH.js.map +1 -0
  87. package/dist/{chunk-ZWQJSZEY.js → chunk-DVENFCQY.js} +5 -4
  88. package/dist/chunk-DVENFCQY.js.map +1 -0
  89. package/dist/chunk-EONPKLEJ.mjs +163 -0
  90. package/dist/chunk-EONPKLEJ.mjs.map +1 -0
  91. package/dist/{chunk-L47ZOYHL.js → chunk-EUIXQPPU.js} +4 -4
  92. package/dist/{chunk-L47ZOYHL.js.map → chunk-EUIXQPPU.js.map} +1 -1
  93. package/dist/chunk-K7WNCB4V.mjs +554 -0
  94. package/dist/chunk-K7WNCB4V.mjs.map +1 -0
  95. package/dist/chunk-L4ZYBFB2.mjs +44 -0
  96. package/dist/chunk-L4ZYBFB2.mjs.map +1 -0
  97. package/dist/chunk-M4HGHTIC.js +820 -0
  98. package/dist/chunk-M4HGHTIC.js.map +1 -0
  99. package/dist/{chunk-HHVDOIPV.js → chunk-MZKATHB7.js} +4 -4
  100. package/dist/{chunk-HHVDOIPV.js.map → chunk-MZKATHB7.js.map} +1 -1
  101. package/dist/{chunk-UKT3PLON.mjs → chunk-NXQVTAOP.mjs} +3 -3
  102. package/dist/{chunk-UKT3PLON.mjs.map → chunk-NXQVTAOP.mjs.map} +1 -1
  103. package/dist/chunk-OBIPI4GU.mjs +266 -0
  104. package/dist/chunk-OBIPI4GU.mjs.map +1 -0
  105. package/dist/chunk-PAX4S7QM.mjs +94 -0
  106. package/dist/chunk-PAX4S7QM.mjs.map +1 -0
  107. package/dist/chunk-PXWDQFWV.mjs +192 -0
  108. package/dist/chunk-PXWDQFWV.mjs.map +1 -0
  109. package/dist/chunk-QROLPPXP.mjs +5797 -0
  110. package/dist/chunk-QROLPPXP.mjs.map +1 -0
  111. package/dist/chunk-TGNUEULF.mjs +1158 -0
  112. package/dist/chunk-TGNUEULF.mjs.map +1 -0
  113. package/dist/chunk-VBQFVXOW.mjs +2772 -0
  114. package/dist/chunk-VBQFVXOW.mjs.map +1 -0
  115. package/dist/chunk-VLZ5N6XZ.js +5888 -0
  116. package/dist/chunk-VLZ5N6XZ.js.map +1 -0
  117. package/dist/chunk-VTGPHE4Z.mjs +322 -0
  118. package/dist/chunk-VTGPHE4Z.mjs.map +1 -0
  119. package/dist/chunk-WMJKH4XE.mjs +30 -0
  120. package/dist/{chunk-BJTO5JO5.mjs.map → chunk-WMJKH4XE.mjs.map} +1 -1
  121. package/dist/chunk-XYQMAF7H.js +96 -0
  122. package/dist/chunk-XYQMAF7H.js.map +1 -0
  123. package/dist/chunk-Z3G3IXEF.js +2814 -0
  124. package/dist/chunk-Z3G3IXEF.js.map +1 -0
  125. package/dist/chunk-Z6ZWNWWR.js +35 -0
  126. package/dist/{chunk-DGUM43GV.js.map → chunk-Z6ZWNWWR.js.map} +1 -1
  127. package/dist/components/index.d.mts +378 -0
  128. package/dist/components/index.d.ts +378 -0
  129. package/dist/components/index.js +414 -0
  130. package/dist/components/index.js.map +1 -0
  131. package/dist/components/index.mjs +9 -0
  132. package/dist/components/index.mjs.map +1 -0
  133. package/dist/config/index.js +1 -1
  134. package/dist/config/index.mjs +1 -1
  135. package/dist/config/server/index.js +1 -1
  136. package/dist/config/server/index.mjs +1 -1
  137. package/dist/fileService-O3W6YXCI.mjs +4 -0
  138. package/dist/fileService-O3W6YXCI.mjs.map +1 -0
  139. package/dist/fileService-YUDIYOAS.js +13 -0
  140. package/dist/fileService-YUDIYOAS.js.map +1 -0
  141. package/dist/i18n/index.js +7 -7
  142. package/dist/i18n/index.mjs +1 -1
  143. package/dist/imageCrop/index.js +1 -1
  144. package/dist/imageCrop/index.mjs +1 -1
  145. package/dist/index.d.mts +9 -580
  146. package/dist/index.d.ts +9 -580
  147. package/dist/index.js +297 -956
  148. package/dist/index.js.map +1 -1
  149. package/dist/index.mjs +103 -789
  150. package/dist/index.mjs.map +1 -1
  151. package/dist/logger/index.js +8 -8
  152. package/dist/logger/index.mjs +5 -2
  153. package/dist/mikuFireworks3D/index.js +1 -1
  154. package/dist/mikuFireworks3D/index.mjs +1 -1
  155. package/dist/mikuFireworks3D/server/index.js +1 -1
  156. package/dist/mikuFireworks3D/server/index.mjs +1 -1
  157. package/dist/mikuFusionGame/index.js +5 -5
  158. package/dist/mikuFusionGame/index.mjs +4 -4
  159. package/dist/mmd/admin/index.js +1 -1
  160. package/dist/mmd/admin/index.mjs +1 -1
  161. package/dist/mmd/index.js +2 -2
  162. package/dist/mmd/index.mjs +2 -2
  163. package/dist/mmd/server/index.js +1 -1
  164. package/dist/mmd/server/index.mjs +1 -1
  165. package/dist/music/index.js +1 -1
  166. package/dist/music/index.mjs +1 -1
  167. package/dist/music/server/index.js +1 -1
  168. package/dist/music/server/index.mjs +1 -1
  169. package/dist/navigation/index.d.mts +93 -0
  170. package/dist/navigation/index.d.ts +93 -0
  171. package/dist/navigation/index.js +29 -0
  172. package/dist/navigation/index.js.map +1 -0
  173. package/dist/navigation/index.mjs +4 -0
  174. package/dist/navigation/index.mjs.map +1 -0
  175. package/dist/popupConfig-BznThU1O.d.mts +330 -0
  176. package/dist/popupConfig-BznThU1O.d.ts +330 -0
  177. package/dist/portfolio/index.d.mts +57 -0
  178. package/dist/portfolio/index.d.ts +57 -0
  179. package/dist/portfolio/index.js +35 -0
  180. package/dist/portfolio/index.js.map +1 -0
  181. package/dist/portfolio/index.mjs +10 -0
  182. package/dist/portfolio/index.mjs.map +1 -0
  183. package/dist/request/index.js +1 -1
  184. package/dist/request/index.mjs +1 -1
  185. package/dist/showmasterpiece/index.d.mts +2524 -0
  186. package/dist/showmasterpiece/index.d.ts +2524 -0
  187. package/dist/showmasterpiece/index.js +9681 -0
  188. package/dist/showmasterpiece/index.js.map +1 -0
  189. package/dist/showmasterpiece/index.mjs +9631 -0
  190. package/dist/showmasterpiece/index.mjs.map +1 -0
  191. package/dist/showmasterpiece/migration/index.d.mts +120 -0
  192. package/dist/showmasterpiece/migration/index.d.ts +120 -0
  193. package/dist/showmasterpiece/migration/index.js +595 -0
  194. package/dist/showmasterpiece/migration/index.js.map +1 -0
  195. package/dist/showmasterpiece/migration/index.mjs +589 -0
  196. package/dist/showmasterpiece/migration/index.mjs.map +1 -0
  197. package/dist/showmasterpiece/scripts/index.d.mts +28 -0
  198. package/dist/showmasterpiece/scripts/index.d.ts +28 -0
  199. package/dist/showmasterpiece/scripts/index.js +327 -0
  200. package/dist/showmasterpiece/scripts/index.js.map +1 -0
  201. package/dist/showmasterpiece/scripts/index.mjs +325 -0
  202. package/dist/showmasterpiece/scripts/index.mjs.map +1 -0
  203. package/dist/showmasterpiece/server/index.d.mts +2698 -0
  204. package/dist/showmasterpiece/server/index.d.ts +2698 -0
  205. package/dist/showmasterpiece/server/index.js +179 -0
  206. package/dist/showmasterpiece/server/index.js.map +1 -0
  207. package/dist/showmasterpiece/server/index.mjs +6 -0
  208. package/dist/showmasterpiece/server/index.mjs.map +1 -0
  209. package/dist/storage/index.js +8 -8
  210. package/dist/storage/index.mjs +2 -2
  211. package/dist/testYourself/admin/index.js +1 -1
  212. package/dist/testYourself/admin/index.mjs +1 -1
  213. package/dist/testYourself/index.js +2 -2
  214. package/dist/testYourself/index.mjs +2 -2
  215. package/dist/testYourself/server/index.js +1 -1
  216. package/dist/testYourself/server/index.mjs +1 -1
  217. package/dist/universalExport/index.js +154 -1195
  218. package/dist/universalExport/index.js.map +1 -1
  219. package/dist/universalExport/index.mjs +2 -1157
  220. package/dist/universalExport/index.mjs.map +1 -1
  221. package/dist/universalExport/server/index.js +5 -3
  222. package/dist/universalExport/server/index.js.map +1 -1
  223. package/dist/universalExport/server/index.mjs +4 -2
  224. package/dist/universalExport/server/index.mjs.map +1 -1
  225. package/dist/universalFile/index.js +10 -7
  226. package/dist/universalFile/index.js.map +1 -1
  227. package/dist/universalFile/index.mjs +6 -2
  228. package/dist/universalFile/index.mjs.map +1 -1
  229. package/dist/universalFile/server/index.d.mts +592 -265
  230. package/dist/universalFile/server/index.d.ts +592 -265
  231. package/dist/universalFile/server/index.js +298 -5637
  232. package/dist/universalFile/server/index.js.map +1 -1
  233. package/dist/universalFile/server/index.mjs +8 -5559
  234. package/dist/universalFile/server/index.mjs.map +1 -1
  235. package/dist/utils/index.js +12 -12
  236. package/dist/utils/index.mjs +3 -3
  237. package/package.json +36 -1
  238. package/dist/AliyunOSSProvider-4W47OFEK.mjs +0 -6
  239. package/dist/AliyunOSSProvider-HCNGDJL7.js +0 -15
  240. package/dist/ConfigService-V6ZK273Z.mjs +0 -4
  241. package/dist/LocalStorageProvider-3RVPCQB3.mjs +0 -6
  242. package/dist/LocalStorageProvider-PP7MA5OT.js +0 -15
  243. package/dist/PMXParser-RNVQL76A.mjs +0 -4
  244. package/dist/chunk-25OFOKNF.js +0 -171
  245. package/dist/chunk-25OFOKNF.js.map +0 -1
  246. package/dist/chunk-3DXPQ4YV.mjs +0 -165
  247. package/dist/chunk-3DXPQ4YV.mjs.map +0 -1
  248. package/dist/chunk-3NHAT7D4.mjs.map +0 -1
  249. package/dist/chunk-BJTO5JO5.mjs +0 -10
  250. package/dist/chunk-CIVO4R6N.mjs +0 -37
  251. package/dist/chunk-CIVO4R6N.mjs.map +0 -1
  252. package/dist/chunk-DGUM43GV.js +0 -12
  253. package/dist/chunk-HDMIOOZY.mjs +0 -546
  254. package/dist/chunk-HDMIOOZY.mjs.map +0 -1
  255. package/dist/chunk-HJ6MH7J7.js +0 -552
  256. package/dist/chunk-HJ6MH7J7.js.map +0 -1
  257. package/dist/chunk-KH6RQ4J5.js +0 -28
  258. package/dist/chunk-KH6RQ4J5.js.map +0 -1
  259. package/dist/chunk-Q5EDCKQA.js +0 -336
  260. package/dist/chunk-Q5EDCKQA.js.map +0 -1
  261. package/dist/chunk-YOTQG4NP.mjs +0 -314
  262. package/dist/chunk-YOTQG4NP.mjs.map +0 -1
  263. package/dist/chunk-ZGVB35L2.mjs +0 -25
  264. package/dist/chunk-ZGVB35L2.mjs.map +0 -1
  265. package/dist/chunk-ZRAW3HXA.js +0 -43
  266. package/dist/chunk-ZRAW3HXA.js.map +0 -1
  267. package/dist/chunk-ZWQJSZEY.js.map +0 -1
@@ -0,0 +1,2772 @@
1
+ import { normalizeHomeTabConfig, buildDefaultHomeTabConfig } from './chunk-EONPKLEJ.mjs';
2
+ import { relations, eq, asc, and, sql, count, desc, inArray, like } from 'drizzle-orm';
3
+ import { pgTable, timestamp, varchar, json, boolean, integer, text, serial, index, uuid, jsonb } from 'drizzle-orm/pg-core';
4
+
5
+ var comicUniverseConfigs = pgTable("comic_universe_configs", {
6
+ /** 主键ID */
7
+ id: serial("id").primaryKey(),
8
+ /** 网站名称 */
9
+ siteName: varchar("site_name", { length: 255 }).notNull().default("\u753B\u96C6\u5C55\u89C8"),
10
+ /** 网站描述 */
11
+ siteDescription: text("site_description").default("\u7CBE\u7F8E\u7684\u827A\u672F\u4F5C\u54C1\u5C55\u89C8"),
12
+ /** 首页主标题 */
13
+ heroTitle: varchar("hero_title", { length: 255 }).notNull().default("\u827A\u672F\u753B\u96C6\u5C55\u89C8"),
14
+ /** 首页副标题 */
15
+ heroSubtitle: text("hero_subtitle").default("\u63A2\u7D22\u7CBE\u7F8E\u7684\u827A\u672F\u4F5C\u54C1\uFF0C\u611F\u53D7\u521B\u4F5C\u7684\u9B45\u529B"),
16
+ /** 每页显示的最大画集数量 */
17
+ maxCollectionsPerPage: integer("max_collections_per_page").notNull().default(9),
18
+ /** 是否启用搜索功能 */
19
+ enableSearch: boolean("enable_search").notNull().default(true),
20
+ /** 是否启用分类功能 */
21
+ enableCategories: boolean("enable_categories").notNull().default(true),
22
+ /** 首页分类Tab配置 */
23
+ homeTabConfig: json("home_tab_config").default([]),
24
+ /** 默认分类('all'表示显示所有分类) */
25
+ defaultCategory: varchar("default_category", { length: 100 }).notNull().default("all"),
26
+ /** 主题模式:light(浅色)、dark(深色)、auto(自动) */
27
+ theme: varchar("theme", { length: 20 }).notNull().default("light"),
28
+ /** 界面语言:zh(中文)、en(英文) */
29
+ language: varchar("language", { length: 10 }).notNull().default("zh"),
30
+ /** 创建时间 */
31
+ createdAt: timestamp("created_at").defaultNow().notNull(),
32
+ /** 更新时间 */
33
+ updatedAt: timestamp("updated_at").defaultNow().notNull()
34
+ });
35
+ var comicUniverseCategories = pgTable("comic_universe_categories", {
36
+ /** 主键ID */
37
+ id: serial("id").primaryKey(),
38
+ /** 分类名称(唯一) */
39
+ name: varchar("name", { length: 100 }).notNull().unique(),
40
+ /** 分类描述 */
41
+ description: text("description"),
42
+ /** 显示顺序(数字越小越靠前) */
43
+ displayOrder: integer("display_order").default(0),
44
+ /** 是否启用该分类 */
45
+ isActive: boolean("is_active").notNull().default(true),
46
+ /** 创建时间 */
47
+ createdAt: timestamp("created_at").defaultNow().notNull(),
48
+ /** 更新时间 */
49
+ updatedAt: timestamp("updated_at").defaultNow().notNull()
50
+ }, (table) => ({
51
+ /** 按启用状态查询的索引 */
52
+ isActiveIndex: index("categories_is_active_idx").on(table.isActive),
53
+ /** 按显示顺序排序的索引 */
54
+ displayOrderIndex: index("categories_display_order_idx").on(table.displayOrder)
55
+ }));
56
+ var comicUniverseTags = pgTable("comic_universe_tags", {
57
+ /** 主键ID */
58
+ id: serial("id").primaryKey(),
59
+ /** 标签名称(唯一) */
60
+ name: varchar("name", { length: 50 }).notNull().unique(),
61
+ /** 标签颜色(十六进制色值,用于UI显示) */
62
+ color: varchar("color", { length: 7 }).default("#3b82f6"),
63
+ /** 是否启用该标签 */
64
+ isActive: boolean("is_active").notNull().default(true),
65
+ /** 创建时间 */
66
+ createdAt: timestamp("created_at").defaultNow().notNull()
67
+ }, (table) => ({
68
+ /** 按启用状态查询的索引 */
69
+ isActiveIndex: index("tags_is_active_idx").on(table.isActive)
70
+ }));
71
+ var comicUniverseCollections = pgTable("comic_universe_collections", {
72
+ /** 主键ID */
73
+ id: serial("id").primaryKey(),
74
+ /** 画集标题 */
75
+ title: varchar("title", { length: 255 }).notNull(),
76
+ /** 编号 */
77
+ number: varchar("number", { length: 255 }).notNull(),
78
+ /** 封面图片(支持URL或base64编码) */
79
+ coverImage: text("cover_image").notNull(),
80
+ /** 通用文件服务的封面图片文件ID(新架构) */
81
+ coverImageFileId: uuid("cover_image_file_id"),
82
+ /** 画集描述 */
83
+ description: text("description"),
84
+ /** 分类ID(外键,支持级联删除时设为null) */
85
+ categoryId: integer("category_id").references(() => comicUniverseCategories.id, { onDelete: "set null" }),
86
+ /** 是否已发布(false表示草稿状态) */
87
+ isPublished: boolean("is_published").notNull().default(true),
88
+ /** 发布时间 */
89
+ publishedAt: timestamp("published_at"),
90
+ /** 显示顺序(数字越小越靠前) */
91
+ displayOrder: integer("display_order").default(0),
92
+ /** 画集价格(单位:元,null表示免费或价格待定) */
93
+ price: integer("price"),
94
+ /** 访问次数统计 */
95
+ viewCount: integer("view_count").notNull().default(0),
96
+ /** 创建时间 */
97
+ createdAt: timestamp("created_at").defaultNow().notNull(),
98
+ /** 更新时间 */
99
+ updatedAt: timestamp("updated_at").defaultNow().notNull()
100
+ }, (table) => ({
101
+ /** 按发布状态查询的索引 */
102
+ isPublishedIndex: index("collections_is_published_idx").on(table.isPublished),
103
+ /** 按显示顺序排序的索引 */
104
+ displayOrderIndex: index("collections_display_order_idx").on(table.displayOrder),
105
+ /** 按分类查询的索引 */
106
+ categoryIdIndex: index("collections_category_id_idx").on(table.categoryId),
107
+ /** 已发布画集按顺序排序的复合索引(优化首页查询) */
108
+ publishedOrderIndex: index("collections_published_order_idx").on(table.isPublished, table.displayOrder),
109
+ /** 已发布画集按创建时间排序的复合索引(优化时间线查询) */
110
+ publishedCreatedIndex: index("collections_published_created_idx").on(table.isPublished, table.createdAt),
111
+ /** 封面图片文件ID查询索引(新架构) */
112
+ coverImageFileIdIndex: index("collections_cover_image_file_id_idx").on(table.coverImageFileId)
113
+ }));
114
+ var comicUniverseCollectionTags = pgTable("comic_universe_collection_tags", {
115
+ /** 画集ID(外键,级联删除) */
116
+ collectionId: integer("collection_id").notNull().references(() => comicUniverseCollections.id, { onDelete: "cascade" }),
117
+ /** 标签ID(外键,级联删除) */
118
+ tagId: integer("tag_id").notNull().references(() => comicUniverseTags.id, { onDelete: "cascade" })
119
+ }, (table) => ({
120
+ /** 复合主键,确保同一画集不会重复关联同一标签 */
121
+ pk: { primaryKey: [table.collectionId, table.tagId] },
122
+ /** 按画集查询标签的索引 */
123
+ collectionIdIndex: index("collection_tags_collection_id_idx").on(table.collectionId),
124
+ /** 按标签查询画集的索引 */
125
+ tagIdIndex: index("collection_tags_tag_id_idx").on(table.tagId)
126
+ }));
127
+ var comicUniverseArtworks = pgTable("comic_universe_artworks", {
128
+ /** 主键ID */
129
+ id: serial("id").primaryKey(),
130
+ /** 所属画集ID(外键,级联删除) */
131
+ collectionId: integer("collection_id").notNull().references(() => comicUniverseCollections.id, { onDelete: "cascade" }),
132
+ /** 作品标题 */
133
+ title: varchar("title", { length: 255 }).notNull(),
134
+ /** 编号 */
135
+ number: varchar("number", { length: 255 }).notNull(),
136
+ /** 作品图片(支持URL或base64编码,兼容旧数据) */
137
+ image: text("image"),
138
+ /** 通用文件服务的文件ID(新架构) */
139
+ fileId: uuid("file_id"),
140
+ /** 迁移状态:pending(待迁移), completed(已完成), failed(失败) */
141
+ migrationStatus: varchar("migration_status", { length: 20 }).default("pending"),
142
+ /** 作品描述 */
143
+ description: text("description"),
144
+ /** 创作时间(自由格式,如"2024年春" "明代" 等) */
145
+ createdTime: varchar("created_time", { length: 20 }),
146
+ /** 作品主题(如"山水" "人物" "静物"等) */
147
+ theme: varchar("theme", { length: 255 }),
148
+ /** 作品尺寸(如"120x80cm" "A4" 等) */
149
+ dimensions: varchar("dimensions", { length: 100 }),
150
+ /** 在画集中的页面顺序(从0开始) */
151
+ pageOrder: integer("page_order").notNull().default(0),
152
+ /** 是否启用(false表示已删除或隐藏) */
153
+ isActive: boolean("is_active").notNull().default(true),
154
+ /** 创建时间 */
155
+ createdAt: timestamp("created_at").defaultNow().notNull(),
156
+ /** 更新时间 */
157
+ updatedAt: timestamp("updated_at").defaultNow().notNull()
158
+ }, (table) => ({
159
+ /** 按画集查询作品的索引 */
160
+ collectionIdIndex: index("artworks_collection_id_idx").on(table.collectionId),
161
+ /** 按启用状态查询的索引 */
162
+ isActiveIndex: index("artworks_is_active_idx").on(table.isActive),
163
+ /** 按页面顺序排序的索引 */
164
+ pageOrderIndex: index("artworks_page_order_idx").on(table.pageOrder),
165
+ /** 画集和启用状态的复合索引(优化画集内容查询) */
166
+ collectionActiveIndex: index("artworks_collection_active_idx").on(table.collectionId, table.isActive),
167
+ /** 画集和页面顺序的复合索引(优化排序查询) */
168
+ collectionOrderIndex: index("artworks_collection_order_idx").on(table.collectionId, table.pageOrder),
169
+ /** 画集、启用状态和页面顺序的三元复合索引(优化完整查询) */
170
+ collectionActiveOrderIndex: index("artworks_collection_active_order_idx").on(table.collectionId, table.isActive, table.pageOrder),
171
+ /** 文件ID查询索引(新架构) */
172
+ fileIdIndex: index("artworks_file_id_idx").on(table.fileId),
173
+ /** 迁移状态查询索引 */
174
+ migrationStatusIndex: index("artworks_migration_status_idx").on(table.migrationStatus)
175
+ }));
176
+ var comicUniverseConfigsRelations = relations(comicUniverseConfigs, ({ many }) => ({
177
+ // 配置表通常只有一条记录,不需要关系
178
+ }));
179
+ var comicUniverseCategoriesRelations = relations(comicUniverseCategories, ({ many }) => ({
180
+ /** 该分类下的所有画集 */
181
+ collections: many(comicUniverseCollections)
182
+ }));
183
+ var comicUniverseTagsRelations = relations(comicUniverseTags, ({ many }) => ({
184
+ /** 该标签的所有关联记录 */
185
+ collectionTags: many(comicUniverseCollectionTags)
186
+ }));
187
+ var comicUniverseCollectionsRelations = relations(comicUniverseCollections, ({ one, many }) => ({
188
+ /** 所属分类 */
189
+ category: one(comicUniverseCategories, {
190
+ fields: [comicUniverseCollections.categoryId],
191
+ references: [comicUniverseCategories.id]
192
+ }),
193
+ /** 包含的所有作品页面 */
194
+ artworks: many(comicUniverseArtworks),
195
+ /** 关联的所有标签记录 */
196
+ collectionTags: many(comicUniverseCollectionTags)
197
+ }));
198
+ var comicUniverseCollectionTagsRelations = relations(comicUniverseCollectionTags, ({ one }) => ({
199
+ /** 关联的画集 */
200
+ collection: one(comicUniverseCollections, {
201
+ fields: [comicUniverseCollectionTags.collectionId],
202
+ references: [comicUniverseCollections.id]
203
+ }),
204
+ /** 关联的标签 */
205
+ tag: one(comicUniverseTags, {
206
+ fields: [comicUniverseCollectionTags.tagId],
207
+ references: [comicUniverseTags.id]
208
+ })
209
+ }));
210
+ var comicUniverseArtworksRelations = relations(comicUniverseArtworks, ({ one }) => ({
211
+ /** 所属画集 */
212
+ collection: one(comicUniverseCollections, {
213
+ fields: [comicUniverseArtworks.collectionId],
214
+ references: [comicUniverseCollections.id]
215
+ })
216
+ }));
217
+ var comicUniverseBookings = pgTable("comic_universe_bookings", {
218
+ /** 自增ID(用于内部管理) */
219
+ id: serial("id"),
220
+ /** 预订的画集ID(外键,级联删除) */
221
+ collectionId: integer("collection_id").notNull().references(() => comicUniverseCollections.id, { onDelete: "cascade" }),
222
+ /** 用户QQ号 */
223
+ qqNumber: varchar("qq_number", { length: 20 }).notNull(),
224
+ /** 用户手机号 */
225
+ phoneNumber: varchar("phone_number", { length: 20 }).notNull(),
226
+ /** 预订数量 */
227
+ quantity: integer("quantity").notNull().default(1),
228
+ /** 预订状态:pending(待确认)、confirmed(已确认)、completed(已完成)、cancelled(已取消) */
229
+ status: varchar("status", { length: 20 }).notNull().default("pending"),
230
+ /** 预订备注信息 */
231
+ notes: text("notes"),
232
+ /** 领取方式 */
233
+ pickupMethod: text("pickup_method"),
234
+ /** 管理员处理备注 */
235
+ adminNotes: text("admin_notes"),
236
+ /** 预订时间 */
237
+ createdAt: timestamp("created_at").defaultNow().notNull(),
238
+ /** 更新时间 */
239
+ updatedAt: timestamp("updated_at").defaultNow().notNull(),
240
+ /** 确认时间 */
241
+ confirmedAt: timestamp("confirmed_at"),
242
+ /** 完成时间 */
243
+ completedAt: timestamp("completed_at"),
244
+ /** 取消时间 */
245
+ cancelledAt: timestamp("cancelled_at")
246
+ }, (table) => ({
247
+ // 移除复合主键约束,允许用户对同一画集进行多次预订
248
+ // userCollectionPk: primaryKey({ columns: [table.qqNumber, table.phoneNumber, table.collectionId] }),
249
+ /** 按画集查询预订的索引 */
250
+ collectionIdIndex: index("bookings_collection_id_idx").on(table.collectionId),
251
+ /** 按状态查询的索引 */
252
+ statusIndex: index("bookings_status_idx").on(table.status),
253
+ /** 按QQ号查询的索引 */
254
+ qqNumberIndex: index("bookings_qq_number_idx").on(table.qqNumber),
255
+ /** 按手机号查询的索引 */
256
+ phoneNumberIndex: index("bookings_phone_number_idx").on(table.phoneNumber),
257
+ /** 按创建时间排序的索引 */
258
+ createdAtIndex: index("bookings_created_at_idx").on(table.createdAt),
259
+ /** 画集和状态的复合索引(优化画集预订查询) */
260
+ collectionStatusIndex: index("bookings_collection_status_idx").on(table.collectionId, table.status),
261
+ /** QQ号和状态的复合索引(优化用户预订查询) */
262
+ qqStatusIndex: index("bookings_qq_status_idx").on(table.qqNumber, table.status),
263
+ /** QQ号+手机号的复合索引(优化用户查询) */
264
+ userIndex: index("bookings_user_idx").on(table.qqNumber, table.phoneNumber),
265
+ /** QQ号+手机号+状态的复合索引(优化用户状态查询) */
266
+ userStatusIndex: index("bookings_user_status_idx").on(table.qqNumber, table.phoneNumber, table.status)
267
+ }));
268
+ var comicUniverseBookingsRelations = relations(comicUniverseBookings, ({ one }) => ({
269
+ /** 预订的画集 */
270
+ collection: one(comicUniverseCollections, {
271
+ fields: [comicUniverseBookings.collectionId],
272
+ references: [comicUniverseCollections.id]
273
+ })
274
+ }));
275
+ var popupConfigs = pgTable("popup_configs", {
276
+ /** 配置ID */
277
+ id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
278
+ /** 配置名称 */
279
+ name: text("name").notNull(),
280
+ /** 配置描述 */
281
+ description: text("description"),
282
+ /** 弹窗类型 */
283
+ type: text("type").notNull().default("deadline"),
284
+ // deadline, announcement, etc.
285
+ /** 是否启用 */
286
+ enabled: boolean("enabled").default(false),
287
+ /** 触发条件配置 */
288
+ triggerConfig: jsonb("trigger_config").notNull().$type(),
289
+ /** 弹窗内容配置 */
290
+ contentConfig: jsonb("content_config").notNull().$type(),
291
+ /** 显示设置 */
292
+ displayConfig: jsonb("display_config").$type(),
293
+ /** 是否阻断流程 - true: 阻断提交,false: 仅提醒但允许继续 */
294
+ blockProcess: boolean("block_process").default(false),
295
+ /** 业务模块 */
296
+ businessModule: text("business_module").notNull().default("showmasterpiece"),
297
+ /** 业务场景 */
298
+ businessScene: text("business_scene").notNull().default("cart_checkout"),
299
+ /** 排序权重 */
300
+ sortOrder: text("sort_order").default("0"),
301
+ /** 创建时间 */
302
+ createdAt: timestamp("created_at").defaultNow(),
303
+ /** 更新时间 */
304
+ updatedAt: timestamp("updated_at").defaultNow()
305
+ });
306
+ var popupConfigsRelations = relations(popupConfigs, () => ({}));
307
+ var showmasterConfigCategories = pgTable("showmaster_config_categories", {
308
+ /** 配置分类ID */
309
+ id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
310
+ /** 分类名称(用于代码引用) */
311
+ name: text("name").notNull(),
312
+ /** 显示名称(用于界面显示) */
313
+ displayName: text("display_name").notNull(),
314
+ /** 分类描述 */
315
+ description: text("description"),
316
+ /** 分类图标 */
317
+ icon: text("icon"),
318
+ /** 排序顺序 */
319
+ sortOrder: integer("sort_order").default(0),
320
+ /** 是否激活 */
321
+ isActive: boolean("is_active").default(true),
322
+ /** 创建时间 */
323
+ createdAt: timestamp("created_at").defaultNow(),
324
+ /** 更新时间 */
325
+ updatedAt: timestamp("updated_at").defaultNow()
326
+ });
327
+ var showmasterConfigItems = pgTable("showmaster_config_items", {
328
+ /** 配置项ID */
329
+ id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
330
+ /** 分类ID */
331
+ categoryId: text("category_id").references(() => showmasterConfigCategories.id),
332
+ /** 配置键(必须唯一) */
333
+ key: text("key").notNull().unique(),
334
+ /** 显示名称 */
335
+ displayName: text("display_name").notNull(),
336
+ /** 配置描述 */
337
+ description: text("description"),
338
+ /** 配置值 */
339
+ value: text("value"),
340
+ /** 默认值 */
341
+ defaultValue: text("default_value"),
342
+ /** 配置类型:string, number, boolean, json, password */
343
+ type: text("type").notNull(),
344
+ /** 是否必填 */
345
+ isRequired: boolean("is_required").default(false),
346
+ /** 是否敏感信息 */
347
+ isSensitive: boolean("is_sensitive").default(false),
348
+ /** 验证规则(JSON格式) */
349
+ validation: jsonb("validation"),
350
+ /** 排序顺序 */
351
+ sortOrder: integer("sort_order").default(0),
352
+ /** 是否激活 */
353
+ isActive: boolean("is_active").default(true),
354
+ /** 环境标识(development, production, testing等) */
355
+ environment: text("environment").default("development"),
356
+ /** 创建时间 */
357
+ createdAt: timestamp("created_at").defaultNow(),
358
+ /** 更新时间 */
359
+ updatedAt: timestamp("updated_at").defaultNow()
360
+ });
361
+ var showmasterConfigHistory = pgTable("showmaster_config_history", {
362
+ /** 历史记录ID */
363
+ id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
364
+ /** 配置项ID */
365
+ configItemId: text("config_item_id").references(() => showmasterConfigItems.id),
366
+ /** 旧值 */
367
+ oldValue: text("old_value"),
368
+ /** 新值 */
369
+ newValue: text("new_value"),
370
+ /** 修改人 */
371
+ changedBy: text("changed_by").notNull(),
372
+ /** 修改原因 */
373
+ changeReason: text("change_reason"),
374
+ /** 操作类型:create, update, delete */
375
+ operationType: text("operation_type").notNull(),
376
+ /** 环境标识 */
377
+ environment: text("environment").default("development"),
378
+ /** 创建时间 */
379
+ createdAt: timestamp("created_at").defaultNow()
380
+ });
381
+ var showmasterConfigPermissions = pgTable("showmaster_config_permissions", {
382
+ /** 权限ID */
383
+ id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
384
+ /** 用户ID */
385
+ userId: text("user_id").notNull(),
386
+ /** 分类ID */
387
+ categoryId: text("category_id").references(() => showmasterConfigCategories.id),
388
+ /** 可读权限 */
389
+ canRead: boolean("can_read").default(true),
390
+ /** 可写权限 */
391
+ canWrite: boolean("can_write").default(false),
392
+ /** 可删除权限 */
393
+ canDelete: boolean("can_delete").default(false),
394
+ /** 环境权限(可以访问的环境) */
395
+ allowedEnvironments: jsonb("allowed_environments").$type().default(["development"]),
396
+ /** 创建时间 */
397
+ createdAt: timestamp("created_at").defaultNow(),
398
+ /** 更新时间 */
399
+ updatedAt: timestamp("updated_at").defaultNow()
400
+ });
401
+ var ShowmasterConfigService = class {
402
+ constructor(db2) {
403
+ this.db = db2;
404
+ }
405
+ // ============= 配置分类管理 =============
406
+ /**
407
+ * 获取所有配置分类
408
+ */
409
+ async getAllCategories() {
410
+ console.log("\u{1F3A8} [ShowmasterConfigService] \u83B7\u53D6\u6240\u6709\u914D\u7F6E\u5206\u7C7B");
411
+ return await this.db.select().from(showmasterConfigCategories).where(eq(showmasterConfigCategories.isActive, true)).orderBy(asc(showmasterConfigCategories.sortOrder), asc(showmasterConfigCategories.displayName));
412
+ }
413
+ /**
414
+ * 根据ID获取配置分类
415
+ */
416
+ async getCategoryById(id) {
417
+ console.log("\u{1F3A8} [ShowmasterConfigService] \u83B7\u53D6\u914D\u7F6E\u5206\u7C7B:", id);
418
+ const [category] = await this.db.select().from(showmasterConfigCategories).where(and(
419
+ eq(showmasterConfigCategories.id, id),
420
+ eq(showmasterConfigCategories.isActive, true)
421
+ ));
422
+ return category || null;
423
+ }
424
+ /**
425
+ * 根据名称获取配置分类
426
+ */
427
+ async getCategoryByName(name) {
428
+ console.log("\u{1F3A8} [ShowmasterConfigService] \u6839\u636E\u540D\u79F0\u83B7\u53D6\u914D\u7F6E\u5206\u7C7B:", name);
429
+ const [category] = await this.db.select().from(showmasterConfigCategories).where(and(
430
+ eq(showmasterConfigCategories.name, name),
431
+ eq(showmasterConfigCategories.isActive, true)
432
+ ));
433
+ return category || null;
434
+ }
435
+ /**
436
+ * 创建配置分类
437
+ */
438
+ async createCategory(data) {
439
+ console.log("\u{1F3A8} [ShowmasterConfigService] \u521B\u5EFA\u914D\u7F6E\u5206\u7C7B:", data.name);
440
+ const [category] = await this.db.insert(showmasterConfigCategories).values({
441
+ ...data,
442
+ updatedAt: /* @__PURE__ */ new Date()
443
+ }).returning();
444
+ console.log("\u2705 [ShowmasterConfigService] \u914D\u7F6E\u5206\u7C7B\u521B\u5EFA\u6210\u529F:", category.id);
445
+ return category;
446
+ }
447
+ /**
448
+ * 更新配置分类
449
+ */
450
+ async updateCategory(id, data) {
451
+ console.log("\u{1F3A8} [ShowmasterConfigService] \u66F4\u65B0\u914D\u7F6E\u5206\u7C7B:", id);
452
+ const [category] = await this.db.update(showmasterConfigCategories).set({
453
+ ...data,
454
+ updatedAt: /* @__PURE__ */ new Date()
455
+ }).where(eq(showmasterConfigCategories.id, id)).returning();
456
+ if (!category) {
457
+ throw new Error("\u914D\u7F6E\u5206\u7C7B\u4E0D\u5B58\u5728");
458
+ }
459
+ console.log("\u2705 [ShowmasterConfigService] \u914D\u7F6E\u5206\u7C7B\u66F4\u65B0\u6210\u529F:", category.id);
460
+ return category;
461
+ }
462
+ /**
463
+ * 删除配置分类
464
+ */
465
+ async deleteCategory(id) {
466
+ console.log("\u{1F3A8} [ShowmasterConfigService] \u5220\u9664\u914D\u7F6E\u5206\u7C7B:", id);
467
+ await this.db.update(showmasterConfigCategories).set({
468
+ isActive: false,
469
+ updatedAt: /* @__PURE__ */ new Date()
470
+ }).where(eq(showmasterConfigCategories.id, id));
471
+ console.log("\u2705 [ShowmasterConfigService] \u914D\u7F6E\u5206\u7C7B\u5220\u9664\u6210\u529F:", id);
472
+ }
473
+ // ============= 配置项管理 =============
474
+ /**
475
+ * 获取配置项列表(支持分页和搜索)
476
+ */
477
+ async getConfigItems(params = {}) {
478
+ const {
479
+ categoryId,
480
+ search,
481
+ type,
482
+ environment = "development",
483
+ isActive = true,
484
+ page = 1,
485
+ pageSize = 50
486
+ } = params;
487
+ console.log("\u{1F3A8} [ShowmasterConfigService] \u83B7\u53D6\u914D\u7F6E\u9879\u5217\u8868:", { environment, page, pageSize });
488
+ const conditions = [];
489
+ if (isActive !== void 0) {
490
+ conditions.push(eq(showmasterConfigItems.isActive, isActive));
491
+ }
492
+ if (categoryId) {
493
+ conditions.push(eq(showmasterConfigItems.categoryId, categoryId));
494
+ }
495
+ if (type) {
496
+ conditions.push(eq(showmasterConfigItems.type, type));
497
+ }
498
+ if (environment) {
499
+ conditions.push(eq(showmasterConfigItems.environment, environment));
500
+ }
501
+ if (search) {
502
+ conditions.push(
503
+ sql`(${showmasterConfigItems.key} ILIKE ${"%" + search + "%"} OR
504
+ ${showmasterConfigItems.displayName} ILIKE ${"%" + search + "%"} OR
505
+ ${showmasterConfigItems.description} ILIKE ${"%" + search + "%"})`
506
+ );
507
+ }
508
+ const whereCondition = conditions.length > 0 ? and(...conditions) : void 0;
509
+ const [{ totalCount }] = await this.db.select({ totalCount: count() }).from(showmasterConfigItems).where(whereCondition);
510
+ const items = await this.db.select().from(showmasterConfigItems).where(whereCondition).orderBy(asc(showmasterConfigItems.sortOrder), asc(showmasterConfigItems.displayName)).limit(pageSize).offset((page - 1) * pageSize);
511
+ const totalPages = Math.ceil(totalCount / pageSize);
512
+ console.log(`\u2705 [ShowmasterConfigService] \u627E\u5230 ${items.length}/${totalCount} \u4E2A\u914D\u7F6E\u9879`);
513
+ return {
514
+ items,
515
+ total: totalCount,
516
+ page,
517
+ pageSize,
518
+ totalPages
519
+ };
520
+ }
521
+ /**
522
+ * 根据ID获取配置项
523
+ */
524
+ async getConfigItemById(id) {
525
+ console.log("\u{1F3A8} [ShowmasterConfigService] \u83B7\u53D6\u914D\u7F6E\u9879:", id);
526
+ const [item] = await this.db.select().from(showmasterConfigItems).where(eq(showmasterConfigItems.id, id));
527
+ return item || null;
528
+ }
529
+ /**
530
+ * 根据key获取配置项
531
+ */
532
+ async getConfigItemByKey(key, environment = "development") {
533
+ console.log("\u{1F3A8} [ShowmasterConfigService] \u6839\u636Ekey\u83B7\u53D6\u914D\u7F6E\u9879:", key, "\u73AF\u5883:", environment);
534
+ const [item] = await this.db.select().from(showmasterConfigItems).where(and(
535
+ eq(showmasterConfigItems.key, key),
536
+ eq(showmasterConfigItems.environment, environment),
537
+ eq(showmasterConfigItems.isActive, true)
538
+ ));
539
+ return item || null;
540
+ }
541
+ /**
542
+ * 创建配置项
543
+ */
544
+ async createConfigItem(data) {
545
+ console.log("\u{1F3A8} [ShowmasterConfigService] \u521B\u5EFA\u914D\u7F6E\u9879:", data.key);
546
+ const existingItem = await this.getConfigItemByKey(data.key, data.environment || "development");
547
+ if (existingItem) {
548
+ throw new Error(`\u914D\u7F6E\u9879 ${data.key} \u5728 ${data.environment || "development"} \u73AF\u5883\u4E2D\u5DF2\u5B58\u5728`);
549
+ }
550
+ const [item] = await this.db.insert(showmasterConfigItems).values({
551
+ ...data,
552
+ updatedAt: /* @__PURE__ */ new Date()
553
+ }).returning();
554
+ await this.recordHistory(item.id, null, item.value, "system", "create", item.environment || "development");
555
+ console.log("\u2705 [ShowmasterConfigService] \u914D\u7F6E\u9879\u521B\u5EFA\u6210\u529F:", item.id);
556
+ return item;
557
+ }
558
+ /**
559
+ * 更新配置项
560
+ */
561
+ async updateConfigItem(id, data, changedBy = "system") {
562
+ console.log("\u{1F3A8} [ShowmasterConfigService] \u66F4\u65B0\u914D\u7F6E\u9879:", id);
563
+ const oldItem = await this.getConfigItemById(id);
564
+ if (!oldItem) {
565
+ throw new Error("\u914D\u7F6E\u9879\u4E0D\u5B58\u5728");
566
+ }
567
+ const [item] = await this.db.update(showmasterConfigItems).set({
568
+ ...data,
569
+ updatedAt: /* @__PURE__ */ new Date()
570
+ }).where(eq(showmasterConfigItems.id, id)).returning();
571
+ if (!item) {
572
+ throw new Error("\u914D\u7F6E\u9879\u4E0D\u5B58\u5728");
573
+ }
574
+ if (data.value !== void 0 && data.value !== oldItem.value) {
575
+ await this.recordHistory(item.id, oldItem.value, data.value, changedBy, "update", item.environment || "development");
576
+ }
577
+ console.log("\u2705 [ShowmasterConfigService] \u914D\u7F6E\u9879\u66F4\u65B0\u6210\u529F:", item.id);
578
+ return item;
579
+ }
580
+ /**
581
+ * 删除配置项
582
+ */
583
+ async deleteConfigItem(id, changedBy = "system") {
584
+ console.log("\u{1F3A8} [ShowmasterConfigService] \u5220\u9664\u914D\u7F6E\u9879:", id);
585
+ const item = await this.getConfigItemById(id);
586
+ if (!item) {
587
+ throw new Error("\u914D\u7F6E\u9879\u4E0D\u5B58\u5728");
588
+ }
589
+ await this.recordHistory(item.id, item.value, null, changedBy, "delete", item.environment || "development");
590
+ await this.db.update(showmasterConfigItems).set({
591
+ isActive: false,
592
+ updatedAt: /* @__PURE__ */ new Date()
593
+ }).where(eq(showmasterConfigItems.id, id));
594
+ console.log("\u2705 [ShowmasterConfigService] \u914D\u7F6E\u9879\u5220\u9664\u6210\u529F:", id);
595
+ }
596
+ // ============= 历史记录管理 =============
597
+ /**
598
+ * 记录配置变更历史
599
+ */
600
+ async recordHistory(configItemId, oldValue, newValue, changedBy, operationType, environment) {
601
+ try {
602
+ await this.db.insert(showmasterConfigHistory).values({
603
+ configItemId,
604
+ oldValue,
605
+ newValue,
606
+ changedBy,
607
+ operationType,
608
+ environment
609
+ });
610
+ console.log(`\u{1F4DD} [ShowmasterConfigService] \u5386\u53F2\u8BB0\u5F55\u5DF2\u4FDD\u5B58: ${operationType} for ${configItemId}`);
611
+ } catch (error) {
612
+ console.error("\u274C [ShowmasterConfigService] \u4FDD\u5B58\u5386\u53F2\u8BB0\u5F55\u5931\u8D25:", error);
613
+ }
614
+ }
615
+ /**
616
+ * 获取配置项历史记录
617
+ */
618
+ async getConfigItemHistory(configItemId, limit = 50) {
619
+ console.log("\u{1F3A8} [ShowmasterConfigService] \u83B7\u53D6\u914D\u7F6E\u9879\u5386\u53F2:", configItemId);
620
+ return await this.db.select().from(showmasterConfigHistory).where(eq(showmasterConfigHistory.configItemId, configItemId)).orderBy(desc(showmasterConfigHistory.createdAt)).limit(limit);
621
+ }
622
+ // ============= 工具方法 =============
623
+ /**
624
+ * 获取配置值(带类型转换)
625
+ */
626
+ async getConfigValue(key, environment = "development") {
627
+ const item = await this.getConfigItemByKey(key, environment);
628
+ if (!item || !item.value) {
629
+ return null;
630
+ }
631
+ try {
632
+ switch (item.type) {
633
+ case "number":
634
+ return Number(item.value);
635
+ case "boolean":
636
+ return item.value.toLowerCase() === "true";
637
+ case "json":
638
+ return JSON.parse(item.value);
639
+ default:
640
+ return item.value;
641
+ }
642
+ } catch (error) {
643
+ console.error(`\u274C [ShowmasterConfigService] \u914D\u7F6E\u503C\u7C7B\u578B\u8F6C\u6362\u5931\u8D25: ${key}`, error);
644
+ return item.value;
645
+ }
646
+ }
647
+ /**
648
+ * 设置配置值
649
+ */
650
+ async setConfigValue(key, value, environment = "development", changedBy = "system") {
651
+ console.log("\u{1F3A8} [ShowmasterConfigService] \u8BBE\u7F6E\u914D\u7F6E\u503C:", key, "\u73AF\u5883:", environment);
652
+ const item = await this.getConfigItemByKey(key, environment);
653
+ if (!item) {
654
+ throw new Error(`\u914D\u7F6E\u9879 ${key} \u5728 ${environment} \u73AF\u5883\u4E2D\u4E0D\u5B58\u5728`);
655
+ }
656
+ let stringValue;
657
+ try {
658
+ switch (item.type) {
659
+ case "json":
660
+ stringValue = JSON.stringify(value);
661
+ break;
662
+ default:
663
+ stringValue = String(value);
664
+ break;
665
+ }
666
+ } catch (error) {
667
+ throw new Error(`\u914D\u7F6E\u503C\u8F6C\u6362\u5931\u8D25: ${error}`);
668
+ }
669
+ await this.updateConfigItem(item.id, { value: stringValue }, changedBy);
670
+ }
671
+ /**
672
+ * 初始化默认配置分类
673
+ */
674
+ async initializeDefaultCategories() {
675
+ console.log("\u{1F3A8} [ShowmasterConfigService] \u521D\u59CB\u5316\u9ED8\u8BA4\u914D\u7F6E\u5206\u7C7B");
676
+ const defaultCategories = [
677
+ {
678
+ name: "general",
679
+ displayName: "\u901A\u7528\u914D\u7F6E",
680
+ description: "ShowMasterPieces\u6A21\u5757\u901A\u7528\u914D\u7F6E\u9879",
681
+ icon: "\u2699\uFE0F",
682
+ sortOrder: 0
683
+ },
684
+ {
685
+ name: "display",
686
+ displayName: "\u663E\u793A\u914D\u7F6E",
687
+ description: "\u754C\u9762\u663E\u793A\u76F8\u5173\u914D\u7F6E",
688
+ icon: "\u{1F3A8}",
689
+ sortOrder: 1
690
+ },
691
+ {
692
+ name: "business",
693
+ displayName: "\u4E1A\u52A1\u914D\u7F6E",
694
+ description: "\u4E1A\u52A1\u903B\u8F91\u76F8\u5173\u914D\u7F6E",
695
+ icon: "\u{1F4BC}",
696
+ sortOrder: 2
697
+ },
698
+ {
699
+ name: "integration",
700
+ displayName: "\u96C6\u6210\u914D\u7F6E",
701
+ description: "\u7B2C\u4E09\u65B9\u670D\u52A1\u96C6\u6210\u914D\u7F6E",
702
+ icon: "\u{1F517}",
703
+ sortOrder: 3
704
+ }
705
+ ];
706
+ for (const category of defaultCategories) {
707
+ try {
708
+ const existing = await this.db.select().from(showmasterConfigCategories).where(eq(showmasterConfigCategories.name, category.name));
709
+ if (existing.length === 0) {
710
+ await this.createCategory(category);
711
+ console.log(`\u2705 [ShowmasterConfigService] \u9ED8\u8BA4\u5206\u7C7B\u521B\u5EFA\u6210\u529F: ${category.name}`);
712
+ }
713
+ } catch (error) {
714
+ console.error(`\u274C [ShowmasterConfigService] \u521B\u5EFA\u9ED8\u8BA4\u5206\u7C7B\u5931\u8D25: ${category.name}`, error);
715
+ }
716
+ }
717
+ }
718
+ };
719
+ function createShowmasterConfigService(db2) {
720
+ return new ShowmasterConfigService(db2);
721
+ }
722
+ var PopupConfigService = class {
723
+ constructor(db2) {
724
+ this.db = db2;
725
+ }
726
+ /**
727
+ * 创建弹窗配置
728
+ */
729
+ async createPopupConfig(data) {
730
+ try {
731
+ const [config] = await this.db.insert(popupConfigs).values({
732
+ ...data,
733
+ updatedAt: /* @__PURE__ */ new Date()
734
+ }).returning();
735
+ console.log("\u2705 [PopupConfigService] \u5F39\u7A97\u914D\u7F6E\u521B\u5EFA\u6210\u529F:", config.id);
736
+ return config;
737
+ } catch (error) {
738
+ console.error("\u274C [PopupConfigService] \u521B\u5EFA\u5F39\u7A97\u914D\u7F6E\u5931\u8D25:", error);
739
+ throw new Error("\u521B\u5EFA\u5F39\u7A97\u914D\u7F6E\u5931\u8D25");
740
+ }
741
+ }
742
+ /**
743
+ * 根据ID获取弹窗配置
744
+ */
745
+ async getPopupConfigById(id) {
746
+ try {
747
+ const [config] = await this.db.select().from(popupConfigs).where(eq(popupConfigs.id, id)).limit(1);
748
+ return config || null;
749
+ } catch (error) {
750
+ console.error("\u274C [PopupConfigService] \u83B7\u53D6\u5F39\u7A97\u914D\u7F6E\u5931\u8D25:", error);
751
+ throw new Error("\u83B7\u53D6\u5F39\u7A97\u914D\u7F6E\u5931\u8D25");
752
+ }
753
+ }
754
+ /**
755
+ * 获取指定业务场景的启用弹窗配置
756
+ */
757
+ async getEnabledPopupConfigs(businessModule = "showmasterpiece", businessScene = "cart_checkout") {
758
+ try {
759
+ const conditions = [
760
+ eq(popupConfigs.enabled, true),
761
+ eq(popupConfigs.businessModule, businessModule),
762
+ eq(popupConfigs.businessScene, businessScene)
763
+ ];
764
+ const configs = await this.db.select().from(popupConfigs).where(and(...conditions)).orderBy(desc(popupConfigs.sortOrder), desc(popupConfigs.createdAt));
765
+ console.log(`\u{1F4CA} [PopupConfigService] \u83B7\u53D6\u5230 ${configs.length} \u4E2A\u542F\u7528\u7684\u5F39\u7A97\u914D\u7F6E`);
766
+ return configs;
767
+ } catch (error) {
768
+ console.error("\u274C [PopupConfigService] \u83B7\u53D6\u542F\u7528\u5F39\u7A97\u914D\u7F6E\u5931\u8D25:", error);
769
+ throw new Error("\u83B7\u53D6\u542F\u7528\u5F39\u7A97\u914D\u7F6E\u5931\u8D25");
770
+ }
771
+ }
772
+ /**
773
+ * 获取所有弹窗配置
774
+ */
775
+ async getAllPopupConfigs() {
776
+ try {
777
+ const configs = await this.db.select().from(popupConfigs).orderBy(desc(popupConfigs.createdAt));
778
+ console.log(`\u{1F4CA} [PopupConfigService] \u83B7\u53D6\u5F39\u7A97\u914D\u7F6E: \u8FD4\u56DE${configs.length}\u4E2A\u914D\u7F6E`);
779
+ return configs;
780
+ } catch (error) {
781
+ console.error("\u274C [PopupConfigService] \u83B7\u53D6\u6240\u6709\u5F39\u7A97\u914D\u7F6E\u5931\u8D25:", error);
782
+ throw new Error("\u83B7\u53D6\u6240\u6709\u5F39\u7A97\u914D\u7F6E\u5931\u8D25");
783
+ }
784
+ }
785
+ /**
786
+ * 更新弹窗配置
787
+ */
788
+ async updatePopupConfig(id, data) {
789
+ try {
790
+ const [config] = await this.db.update(popupConfigs).set({
791
+ ...data,
792
+ updatedAt: /* @__PURE__ */ new Date()
793
+ }).where(eq(popupConfigs.id, id)).returning();
794
+ if (!config) {
795
+ throw new Error("\u5F39\u7A97\u914D\u7F6E\u4E0D\u5B58\u5728");
796
+ }
797
+ console.log("\u2705 [PopupConfigService] \u5F39\u7A97\u914D\u7F6E\u66F4\u65B0\u6210\u529F:", config.id);
798
+ return config;
799
+ } catch (error) {
800
+ console.error("\u274C [PopupConfigService] \u66F4\u65B0\u5F39\u7A97\u914D\u7F6E\u5931\u8D25:", error);
801
+ throw new Error("\u66F4\u65B0\u5F39\u7A97\u914D\u7F6E\u5931\u8D25");
802
+ }
803
+ }
804
+ /**
805
+ * 删除弹窗配置
806
+ */
807
+ async deletePopupConfig(id) {
808
+ try {
809
+ const result = await this.db.delete(popupConfigs).where(eq(popupConfigs.id, id));
810
+ console.log("\u2705 [PopupConfigService] \u5F39\u7A97\u914D\u7F6E\u5220\u9664\u6210\u529F:", id);
811
+ return true;
812
+ } catch (error) {
813
+ console.error("\u274C [PopupConfigService] \u5220\u9664\u5F39\u7A97\u914D\u7F6E\u5931\u8D25:", error);
814
+ throw new Error("\u5220\u9664\u5F39\u7A97\u914D\u7F6E\u5931\u8D25");
815
+ }
816
+ }
817
+ /**
818
+ * 切换弹窗配置启用状态
819
+ */
820
+ async togglePopupConfig(id, enabled) {
821
+ try {
822
+ const [config] = await this.db.update(popupConfigs).set({
823
+ enabled,
824
+ updatedAt: /* @__PURE__ */ new Date()
825
+ }).where(eq(popupConfigs.id, id)).returning();
826
+ if (!config) {
827
+ throw new Error("\u5F39\u7A97\u914D\u7F6E\u4E0D\u5B58\u5728");
828
+ }
829
+ console.log(`\u2705 [PopupConfigService] \u5F39\u7A97\u914D\u7F6E${enabled ? "\u542F\u7528" : "\u7981\u7528"}\u6210\u529F:`, config.id);
830
+ return config;
831
+ } catch (error) {
832
+ console.error("\u274C [PopupConfigService] \u5207\u6362\u5F39\u7A97\u914D\u7F6E\u72B6\u6001\u5931\u8D25:", error);
833
+ throw new Error("\u5207\u6362\u5F39\u7A97\u914D\u7F6E\u72B6\u6001\u5931\u8D25");
834
+ }
835
+ }
836
+ /**
837
+ * 检查是否需要显示弹窗
838
+ */
839
+ async shouldShowPopup(businessModule = "showmasterpiece", businessScene = "cart_checkout", currentTime = /* @__PURE__ */ new Date()) {
840
+ try {
841
+ const enabledConfigs = await this.getEnabledPopupConfigs(businessModule, businessScene);
842
+ const triggeredConfigs = [];
843
+ for (const config of enabledConfigs) {
844
+ const { triggerConfig } = config;
845
+ if (triggerConfig.triggerType === "always") {
846
+ triggeredConfigs.push(config);
847
+ continue;
848
+ }
849
+ if (triggerConfig.deadlineTime) {
850
+ const deadlineTime = new Date(triggerConfig.deadlineTime);
851
+ const timeDiff = deadlineTime.getTime() - currentTime.getTime();
852
+ const minutesDiff = Math.floor(timeDiff / (1e3 * 60));
853
+ switch (triggerConfig.triggerType) {
854
+ case "after_deadline":
855
+ if (currentTime > deadlineTime) {
856
+ triggeredConfigs.push(config);
857
+ }
858
+ break;
859
+ case "before_deadline":
860
+ if (triggerConfig.advanceMinutes && minutesDiff <= triggerConfig.advanceMinutes && minutesDiff > 0) {
861
+ triggeredConfigs.push(config);
862
+ }
863
+ break;
864
+ }
865
+ }
866
+ }
867
+ console.log(`\u{1F514} [PopupConfigService] \u68C0\u67E5\u5230 ${triggeredConfigs.length} \u4E2A\u9700\u8981\u663E\u793A\u7684\u5F39\u7A97`);
868
+ return triggeredConfigs;
869
+ } catch (error) {
870
+ console.error("\u274C [PopupConfigService] \u68C0\u67E5\u5F39\u7A97\u663E\u793A\u6761\u4EF6\u5931\u8D25:", error);
871
+ return [];
872
+ }
873
+ }
874
+ };
875
+ function createPopupConfigService(db2) {
876
+ return new PopupConfigService(db2);
877
+ }
878
+ var MasterpiecesConfigDbService = class {
879
+ constructor(db2) {
880
+ this.db = db2;
881
+ }
882
+ async getConfig() {
883
+ const configs = await this.db.select().from(comicUniverseConfigs).limit(1);
884
+ if (configs.length === 0) {
885
+ const defaultConfig = await this.createDefaultConfig();
886
+ return this.mapDbConfigToType(defaultConfig);
887
+ }
888
+ return this.mapDbConfigToType(configs[0]);
889
+ }
890
+ async updateConfig(configData) {
891
+ const configs = await this.db.select().from(comicUniverseConfigs).limit(1);
892
+ if (configs.length === 0) {
893
+ const newConfig = await this.db.insert(comicUniverseConfigs).values({
894
+ siteName: configData.siteName || "\u753B\u96C6\u5C55\u89C8",
895
+ siteDescription: configData.siteDescription || "\u7CBE\u7F8E\u7684\u827A\u672F\u4F5C\u54C1\u5C55\u89C8",
896
+ heroTitle: configData.heroTitle || "\u827A\u672F\u753B\u96C6\u5C55\u89C8",
897
+ heroSubtitle: configData.heroSubtitle || "\u63A2\u7D22\u7CBE\u7F8E\u7684\u827A\u672F\u4F5C\u54C1\uFF0C\u611F\u53D7\u521B\u4F5C\u7684\u9B45\u529B",
898
+ maxCollectionsPerPage: configData.maxCollectionsPerPage || 9,
899
+ enableSearch: configData.enableSearch ?? true,
900
+ enableCategories: configData.enableCategories ?? true,
901
+ homeTabConfig: normalizeHomeTabConfig(configData.homeTabConfig),
902
+ defaultCategory: configData.defaultCategory || "all",
903
+ theme: configData.theme || "light",
904
+ language: configData.language || "zh",
905
+ updatedAt: /* @__PURE__ */ new Date()
906
+ }).returning();
907
+ return this.mapDbConfigToType(newConfig[0]);
908
+ }
909
+ const updateData = { updatedAt: /* @__PURE__ */ new Date() };
910
+ if (configData.siteName !== void 0) updateData.siteName = configData.siteName;
911
+ if (configData.siteDescription !== void 0) updateData.siteDescription = configData.siteDescription;
912
+ if (configData.heroTitle !== void 0) updateData.heroTitle = configData.heroTitle;
913
+ if (configData.heroSubtitle !== void 0) updateData.heroSubtitle = configData.heroSubtitle;
914
+ if (configData.maxCollectionsPerPage !== void 0) updateData.maxCollectionsPerPage = configData.maxCollectionsPerPage;
915
+ if (configData.enableSearch !== void 0) updateData.enableSearch = configData.enableSearch;
916
+ if (configData.enableCategories !== void 0) updateData.enableCategories = configData.enableCategories;
917
+ if (configData.homeTabConfig !== void 0) updateData.homeTabConfig = normalizeHomeTabConfig(configData.homeTabConfig);
918
+ if (configData.defaultCategory !== void 0) updateData.defaultCategory = configData.defaultCategory;
919
+ if (configData.theme !== void 0) updateData.theme = configData.theme;
920
+ if (configData.language !== void 0) updateData.language = configData.language;
921
+ const updatedConfig = await this.db.update(comicUniverseConfigs).set(updateData).where(eq(comicUniverseConfigs.id, configs[0].id)).returning();
922
+ return this.mapDbConfigToType(updatedConfig[0]);
923
+ }
924
+ async resetConfig() {
925
+ await this.db.delete(comicUniverseConfigs);
926
+ const defaultConfig = await this.createDefaultConfig();
927
+ return this.mapDbConfigToType(defaultConfig);
928
+ }
929
+ async createDefaultConfig() {
930
+ const newConfig = await this.db.insert(comicUniverseConfigs).values({
931
+ siteName: "\u753B\u96C6\u5C55\u89C8",
932
+ siteDescription: "\u7CBE\u7F8E\u7684\u827A\u672F\u4F5C\u54C1\u5C55\u89C8",
933
+ heroTitle: "\u827A\u672F\u753B\u96C6\u5C55\u89C8",
934
+ heroSubtitle: "\u63A2\u7D22\u7CBE\u7F8E\u7684\u827A\u672F\u4F5C\u54C1\uFF0C\u611F\u53D7\u521B\u4F5C\u7684\u9B45\u529B",
935
+ maxCollectionsPerPage: 9,
936
+ enableSearch: true,
937
+ enableCategories: true,
938
+ homeTabConfig: buildDefaultHomeTabConfig(),
939
+ defaultCategory: "all",
940
+ theme: "light",
941
+ language: "zh"
942
+ }).returning();
943
+ return newConfig[0];
944
+ }
945
+ mapDbConfigToType(dbConfig) {
946
+ return {
947
+ siteName: dbConfig.siteName,
948
+ siteDescription: dbConfig.siteDescription,
949
+ heroTitle: dbConfig.heroTitle,
950
+ heroSubtitle: dbConfig.heroSubtitle,
951
+ maxCollectionsPerPage: dbConfig.maxCollectionsPerPage,
952
+ enableSearch: dbConfig.enableSearch,
953
+ enableCategories: dbConfig.enableCategories,
954
+ homeTabConfig: normalizeHomeTabConfig(dbConfig.homeTabConfig),
955
+ defaultCategory: dbConfig.defaultCategory,
956
+ theme: dbConfig.theme,
957
+ language: dbConfig.language
958
+ };
959
+ }
960
+ };
961
+ var CategoriesDbService = class {
962
+ constructor(db2) {
963
+ this.db = db2;
964
+ }
965
+ async getCategories() {
966
+ const conditions = [eq(comicUniverseCategories.isActive, true)];
967
+ const categories = await this.db.select().from(comicUniverseCategories).where(and(...conditions)).orderBy(asc(comicUniverseCategories.displayOrder), asc(comicUniverseCategories.name));
968
+ return categories.map((cat) => cat.name);
969
+ }
970
+ async createCategory(name, description) {
971
+ await this.db.insert(comicUniverseCategories).values({ name, description });
972
+ }
973
+ };
974
+ var TagsDbService = class {
975
+ constructor(db2) {
976
+ this.db = db2;
977
+ }
978
+ async getTags() {
979
+ const conditions = [eq(comicUniverseTags.isActive, true)];
980
+ const tags = await this.db.select().from(comicUniverseTags).where(and(...conditions)).orderBy(asc(comicUniverseTags.name));
981
+ return tags.map((tag) => tag.name);
982
+ }
983
+ async createTag(name, color) {
984
+ await this.db.insert(comicUniverseTags).values({
985
+ name,
986
+ color: color || "#3b82f6"
987
+ });
988
+ }
989
+ };
990
+ function createMasterpiecesConfigDbService(db2) {
991
+ return new MasterpiecesConfigDbService(db2);
992
+ }
993
+ function createCategoriesDbService(db2) {
994
+ return new CategoriesDbService(db2);
995
+ }
996
+ function createTagsDbService(db2) {
997
+ return new TagsDbService(db2);
998
+ }
999
+ var db;
1000
+ function initializeShowmasterpieceDb(database, resolver) {
1001
+ db = database;
1002
+ globalThis.__sa2kitShowmasterpieceResolveFileUrl = resolver;
1003
+ }
1004
+ var MasterpiecesConfigDbService2 = class {
1005
+ // 获取配置
1006
+ async getConfig() {
1007
+ const configs = await db.select().from(comicUniverseConfigs).limit(1);
1008
+ if (configs.length === 0) {
1009
+ const defaultConfig = await this.createDefaultConfig();
1010
+ return this.mapDbConfigToType(defaultConfig);
1011
+ }
1012
+ return this.mapDbConfigToType(configs[0]);
1013
+ }
1014
+ // 更新配置
1015
+ async updateConfig(configData) {
1016
+ const configs = await db.select().from(comicUniverseConfigs).limit(1);
1017
+ if (configs.length === 0) {
1018
+ const newConfig = await db.insert(comicUniverseConfigs).values({
1019
+ siteName: configData.siteName || "\u753B\u96C6\u5C55\u89C8",
1020
+ siteDescription: configData.siteDescription || "\u7CBE\u7F8E\u7684\u827A\u672F\u4F5C\u54C1\u5C55\u89C8",
1021
+ heroTitle: configData.heroTitle || "\u827A\u672F\u753B\u96C6\u5C55\u89C8",
1022
+ heroSubtitle: configData.heroSubtitle || "\u63A2\u7D22\u7CBE\u7F8E\u7684\u827A\u672F\u4F5C\u54C1\uFF0C\u611F\u53D7\u521B\u4F5C\u7684\u9B45\u529B",
1023
+ maxCollectionsPerPage: configData.maxCollectionsPerPage || 9,
1024
+ enableSearch: configData.enableSearch ?? true,
1025
+ enableCategories: configData.enableCategories ?? true,
1026
+ homeTabConfig: normalizeHomeTabConfig(configData.homeTabConfig),
1027
+ defaultCategory: configData.defaultCategory || "all",
1028
+ theme: configData.theme || "light",
1029
+ language: configData.language || "zh",
1030
+ updatedAt: /* @__PURE__ */ new Date()
1031
+ }).returning();
1032
+ return this.mapDbConfigToType(newConfig[0]);
1033
+ } else {
1034
+ const updateData = {
1035
+ updatedAt: /* @__PURE__ */ new Date()
1036
+ };
1037
+ if (configData.siteName !== void 0) updateData.siteName = configData.siteName;
1038
+ if (configData.siteDescription !== void 0) updateData.siteDescription = configData.siteDescription;
1039
+ if (configData.heroTitle !== void 0) updateData.heroTitle = configData.heroTitle;
1040
+ if (configData.heroSubtitle !== void 0) updateData.heroSubtitle = configData.heroSubtitle;
1041
+ if (configData.maxCollectionsPerPage !== void 0) updateData.maxCollectionsPerPage = configData.maxCollectionsPerPage;
1042
+ if (configData.enableSearch !== void 0) updateData.enableSearch = configData.enableSearch;
1043
+ if (configData.enableCategories !== void 0) updateData.enableCategories = configData.enableCategories;
1044
+ if (configData.homeTabConfig !== void 0) updateData.homeTabConfig = normalizeHomeTabConfig(configData.homeTabConfig);
1045
+ if (configData.defaultCategory !== void 0) updateData.defaultCategory = configData.defaultCategory;
1046
+ if (configData.theme !== void 0) updateData.theme = configData.theme;
1047
+ if (configData.language !== void 0) updateData.language = configData.language;
1048
+ const updatedConfig = await db.update(comicUniverseConfigs).set(updateData).where(eq(comicUniverseConfigs.id, configs[0].id)).returning();
1049
+ return this.mapDbConfigToType(updatedConfig[0]);
1050
+ }
1051
+ }
1052
+ // 重置为默认配置
1053
+ async resetConfig() {
1054
+ await db.delete(comicUniverseConfigs);
1055
+ const defaultConfig = await this.createDefaultConfig();
1056
+ return this.mapDbConfigToType(defaultConfig);
1057
+ }
1058
+ // 创建默认配置
1059
+ async createDefaultConfig() {
1060
+ const newConfig = await db.insert(comicUniverseConfigs).values({
1061
+ siteName: "\u753B\u96C6\u5C55\u89C8",
1062
+ siteDescription: "\u7CBE\u7F8E\u7684\u827A\u672F\u4F5C\u54C1\u5C55\u89C8",
1063
+ heroTitle: "\u827A\u672F\u753B\u96C6\u5C55\u89C8",
1064
+ heroSubtitle: "\u63A2\u7D22\u7CBE\u7F8E\u7684\u827A\u672F\u4F5C\u54C1\uFF0C\u611F\u53D7\u521B\u4F5C\u7684\u9B45\u529B",
1065
+ maxCollectionsPerPage: 9,
1066
+ enableSearch: true,
1067
+ enableCategories: true,
1068
+ homeTabConfig: buildDefaultHomeTabConfig(),
1069
+ defaultCategory: "all",
1070
+ theme: "light",
1071
+ language: "zh"
1072
+ }).returning();
1073
+ return newConfig[0];
1074
+ }
1075
+ // 映射数据库配置到类型
1076
+ mapDbConfigToType(dbConfig) {
1077
+ return {
1078
+ siteName: dbConfig.siteName,
1079
+ siteDescription: dbConfig.siteDescription,
1080
+ heroTitle: dbConfig.heroTitle,
1081
+ heroSubtitle: dbConfig.heroSubtitle,
1082
+ maxCollectionsPerPage: dbConfig.maxCollectionsPerPage,
1083
+ enableSearch: dbConfig.enableSearch,
1084
+ enableCategories: dbConfig.enableCategories,
1085
+ homeTabConfig: normalizeHomeTabConfig(dbConfig.homeTabConfig),
1086
+ defaultCategory: dbConfig.defaultCategory,
1087
+ theme: dbConfig.theme,
1088
+ language: dbConfig.language
1089
+ };
1090
+ }
1091
+ };
1092
+ var CategoriesDbService2 = class {
1093
+ // 获取所有分类
1094
+ async getCategories() {
1095
+ const conditions = [eq(comicUniverseCategories.isActive, true)];
1096
+ const categories = await db.select().from(comicUniverseCategories).where(and(...conditions)).orderBy(asc(comicUniverseCategories.displayOrder), asc(comicUniverseCategories.name));
1097
+ console.log(`\u{1F4CA} [CategoriesDbService] \u83B7\u53D6\u5206\u7C7B: \u8FD4\u56DE${categories.length}\u4E2A\u5206\u7C7B`);
1098
+ return categories.map((cat) => cat.name);
1099
+ }
1100
+ // 创建分类
1101
+ async createCategory(name, description) {
1102
+ await db.insert(comicUniverseCategories).values({
1103
+ name,
1104
+ description
1105
+ });
1106
+ }
1107
+ };
1108
+ var TagsDbService2 = class {
1109
+ // 获取所有标签
1110
+ async getTags() {
1111
+ const conditions = [eq(comicUniverseTags.isActive, true)];
1112
+ const tags = await db.select().from(comicUniverseTags).where(and(...conditions)).orderBy(asc(comicUniverseTags.name));
1113
+ console.log(`\u{1F4CA} [TagsDbService] \u83B7\u53D6\u6807\u7B7E: \u8FD4\u56DE${tags.length}\u4E2A\u6807\u7B7E`);
1114
+ return tags.map((tag) => tag.name);
1115
+ }
1116
+ // 创建标签
1117
+ async createTag(name, color) {
1118
+ await db.insert(comicUniverseTags).values({
1119
+ name,
1120
+ color: color || "#3b82f6"
1121
+ });
1122
+ }
1123
+ };
1124
+ var CollectionsDbService = class {
1125
+ constructor() {
1126
+ // 缓存配置 - 多层缓存策略
1127
+ this.collectionsCache = null;
1128
+ this.collectionsCacheTime = 0;
1129
+ this.collectionsOverviewCache = null;
1130
+ this.collectionsOverviewCacheTime = 0;
1131
+ // 优化缓存时间配置
1132
+ this.CACHE_DURATION = 10 * 60 * 1e3;
1133
+ // 延长到10分钟
1134
+ this.OVERVIEW_CACHE_DURATION = 15 * 60 * 1e3;
1135
+ // 概览缓存15分钟
1136
+ this.STALE_WHILE_REVALIDATE = 30 * 60 * 1e3;
1137
+ }
1138
+ // 过期后30分钟内可用
1139
+ /**
1140
+ * 获取所有画集 - 优化版本
1141
+ *
1142
+ * 性能问题分析:
1143
+ * 1. 复杂的多表关联查询 - 涉及5个表的数据整合
1144
+ * 2. 作品数据量大 - 包含所有作品的完整信息(特别是图片数据)
1145
+ * 3. N+1查询风险 - 虽然使用并行查询,但仍有优化空间
1146
+ * 4. 缓存策略有限 - 2分钟缓存时间较短,首次访问必须全量查询
1147
+ *
1148
+ * 优化策略:
1149
+ * ✅ 并行查询 - 分类、标签、作品数据并行获取
1150
+ * ✅ 内存缓存 - 减少重复查询
1151
+ * ✅ 字段选择 - 只查询必要字段
1152
+ * ✅ 延长缓存时间 - 从2分钟增加到10分钟
1153
+ * ⚠️ 需要优化 - 数据库索引、分页加载、图片懒加载
1154
+ */
1155
+ async getAllCollections(useCache = true) {
1156
+ if (useCache && this.collectionsCache) {
1157
+ const cacheAge = Date.now() - this.collectionsCacheTime;
1158
+ if (cacheAge < this.CACHE_DURATION) {
1159
+ return this.collectionsCache;
1160
+ }
1161
+ if (cacheAge < this.STALE_WHILE_REVALIDATE) {
1162
+ this.refreshCacheInBackground();
1163
+ return this.collectionsCache;
1164
+ }
1165
+ }
1166
+ try {
1167
+ const result = await this.fetchAllCollectionsFromDb();
1168
+ this.collectionsCache = result;
1169
+ this.collectionsCacheTime = Date.now();
1170
+ return result;
1171
+ } catch (error) {
1172
+ console.error("\u83B7\u53D6\u753B\u96C6\u6570\u636E\u5931\u8D25:", error);
1173
+ if (this.collectionsCache) {
1174
+ console.warn("\u6570\u636E\u5E93\u67E5\u8BE2\u5931\u8D25\uFF0C\u8FD4\u56DE\u8FC7\u671F\u7F13\u5B58\u6570\u636E");
1175
+ return this.collectionsCache;
1176
+ }
1177
+ throw error;
1178
+ }
1179
+ }
1180
+ /**
1181
+ * 获取画集概览 - 不包含作品详情的轻量版本
1182
+ * 适用于首页列表、搜索结果等场景
1183
+ */
1184
+ async getCollectionsOverview() {
1185
+ if (this.collectionsOverviewCache) {
1186
+ const cacheAge = Date.now() - this.collectionsOverviewCacheTime;
1187
+ if (cacheAge < this.OVERVIEW_CACHE_DURATION) {
1188
+ return this.collectionsOverviewCache;
1189
+ }
1190
+ if (cacheAge < this.STALE_WHILE_REVALIDATE) {
1191
+ this.refreshOverviewCacheInBackground();
1192
+ return this.collectionsOverviewCache;
1193
+ }
1194
+ }
1195
+ try {
1196
+ const result = await this.fetchCollectionsOverviewFromDb();
1197
+ this.collectionsOverviewCache = result;
1198
+ this.collectionsOverviewCacheTime = Date.now();
1199
+ return result;
1200
+ } catch (error) {
1201
+ console.error("\u83B7\u53D6\u753B\u96C6\u6982\u89C8\u5931\u8D25:", error);
1202
+ if (this.collectionsOverviewCache) {
1203
+ console.warn("\u6570\u636E\u5E93\u67E5\u8BE2\u5931\u8D25\uFF0C\u8FD4\u56DE\u8FC7\u671F\u7F13\u5B58\u6570\u636E");
1204
+ return this.collectionsOverviewCache;
1205
+ }
1206
+ throw error;
1207
+ }
1208
+ }
1209
+ /**
1210
+ * 后台异步刷新完整缓存
1211
+ */
1212
+ async refreshCacheInBackground() {
1213
+ try {
1214
+ const result = await this.fetchAllCollectionsFromDb();
1215
+ this.collectionsCache = result;
1216
+ this.collectionsCacheTime = Date.now();
1217
+ console.log("\u7F13\u5B58\u5DF2\u5728\u540E\u53F0\u66F4\u65B0");
1218
+ } catch (error) {
1219
+ console.error("\u540E\u53F0\u7F13\u5B58\u66F4\u65B0\u5931\u8D25:", error);
1220
+ }
1221
+ }
1222
+ /**
1223
+ * 后台异步刷新概览缓存
1224
+ */
1225
+ async refreshOverviewCacheInBackground() {
1226
+ try {
1227
+ const result = await this.fetchCollectionsOverviewFromDb();
1228
+ this.collectionsOverviewCache = result;
1229
+ this.collectionsOverviewCacheTime = Date.now();
1230
+ console.log("\u6982\u89C8\u7F13\u5B58\u5DF2\u5728\u540E\u53F0\u66F4\u65B0");
1231
+ } catch (error) {
1232
+ console.error("\u540E\u53F0\u6982\u89C8\u7F13\u5B58\u66F4\u65B0\u5931\u8D25:", error);
1233
+ }
1234
+ }
1235
+ /**
1236
+ * 从数据库获取完整画集数据
1237
+ * 优化版本:不再返回Base64图片数据,只返回fileId和imageUrl
1238
+ */
1239
+ async fetchAllCollectionsFromDb() {
1240
+ try {
1241
+ const collections = await db.select({
1242
+ id: comicUniverseCollections.id,
1243
+ title: comicUniverseCollections.title,
1244
+ number: comicUniverseCollections.number,
1245
+ coverImage: comicUniverseCollections.coverImage,
1246
+ coverImageFileId: comicUniverseCollections.coverImageFileId,
1247
+ // 新增:封面图片文件ID
1248
+ description: comicUniverseCollections.description,
1249
+ isPublished: comicUniverseCollections.isPublished,
1250
+ displayOrder: comicUniverseCollections.displayOrder,
1251
+ price: comicUniverseCollections.price,
1252
+ createdAt: comicUniverseCollections.createdAt,
1253
+ categoryId: comicUniverseCollections.categoryId
1254
+ }).from(comicUniverseCollections).where(eq(comicUniverseCollections.isPublished, true)).orderBy(
1255
+ desc(comicUniverseCollections.displayOrder),
1256
+ desc(comicUniverseCollections.createdAt)
1257
+ );
1258
+ if (collections.length === 0) {
1259
+ return [];
1260
+ }
1261
+ const collectionIds = collections.map((c) => c.id);
1262
+ const [categories, tags, artworks] = await Promise.all([
1263
+ // 获取分类信息
1264
+ db.select({
1265
+ id: comicUniverseCategories.id,
1266
+ name: comicUniverseCategories.name
1267
+ }).from(comicUniverseCategories).where(eq(comicUniverseCategories.isActive, true)),
1268
+ // 获取标签信息
1269
+ db.select({
1270
+ collectionId: comicUniverseCollectionTags.collectionId,
1271
+ tagName: comicUniverseTags.name
1272
+ }).from(comicUniverseCollectionTags).innerJoin(
1273
+ comicUniverseTags,
1274
+ eq(comicUniverseCollectionTags.tagId, comicUniverseTags.id)
1275
+ ).where(
1276
+ and(
1277
+ inArray(comicUniverseCollectionTags.collectionId, collectionIds),
1278
+ eq(comicUniverseTags.isActive, true)
1279
+ )
1280
+ ),
1281
+ // 获取作品数据(只查询fileId,不查询Base64图片)
1282
+ db.select({
1283
+ collectionId: comicUniverseArtworks.collectionId,
1284
+ id: comicUniverseArtworks.id,
1285
+ title: comicUniverseArtworks.title,
1286
+ number: comicUniverseArtworks.number,
1287
+ fileId: comicUniverseArtworks.fileId,
1288
+ // 只查询fileId,不查询Base64图片
1289
+ description: comicUniverseArtworks.description,
1290
+ createdTime: comicUniverseArtworks.createdTime,
1291
+ theme: comicUniverseArtworks.theme,
1292
+ pageOrder: comicUniverseArtworks.pageOrder
1293
+ }).from(comicUniverseArtworks).where(
1294
+ and(
1295
+ inArray(comicUniverseArtworks.collectionId, collectionIds),
1296
+ eq(comicUniverseArtworks.isActive, true)
1297
+ )
1298
+ ).orderBy(asc(comicUniverseArtworks.pageOrder))
1299
+ ]);
1300
+ const categoriesMap = new Map(categories.map((cat) => [cat.id, cat.name]));
1301
+ const tagsMap = /* @__PURE__ */ new Map();
1302
+ tags.forEach((tag) => {
1303
+ if (!tagsMap.has(tag.collectionId)) {
1304
+ tagsMap.set(tag.collectionId, []);
1305
+ }
1306
+ tagsMap.get(tag.collectionId).push(tag.tagName);
1307
+ });
1308
+ const fileIdToUrlMap = /* @__PURE__ */ new Map();
1309
+ const allFileIds = [
1310
+ ...collections.filter((c) => c.coverImageFileId).map((c) => c.coverImageFileId),
1311
+ ...artworks.filter((a) => a.fileId).map((a) => a.fileId)
1312
+ ];
1313
+ if (allFileIds.length > 0) {
1314
+ try {
1315
+ const { getShowMasterpieceFileConfig: getShowMasterpieceFileConfig2 } = await import('./fileService-O3W6YXCI.mjs');
1316
+ const configManager = await getShowMasterpieceFileConfig2();
1317
+ const { UniversalFileService } = await import('./UniversalFileService-TNYKO6JN.mjs');
1318
+ const fileService = new UniversalFileService(configManager.getConfig());
1319
+ await fileService.initialize();
1320
+ const urlPromises = allFileIds.map(async (fileId) => {
1321
+ try {
1322
+ const fileUrl = await fileService.getFileUrl(fileId);
1323
+ return { fileId, url: fileUrl };
1324
+ } catch (error) {
1325
+ console.warn(`\u26A0\uFE0F \u83B7\u53D6\u6587\u4EF6URL\u5931\u8D25: ${fileId}`, error);
1326
+ return { fileId, url: null };
1327
+ }
1328
+ });
1329
+ const urlResults = await Promise.all(urlPromises);
1330
+ urlResults.forEach((result) => {
1331
+ if (result.url) {
1332
+ fileIdToUrlMap.set(result.fileId, result.url);
1333
+ }
1334
+ });
1335
+ } catch (error) {
1336
+ console.warn("\u26A0\uFE0F \u6279\u91CF\u83B7\u53D6\u6587\u4EF6URL\u5931\u8D25:", error);
1337
+ }
1338
+ }
1339
+ const artworksMap = /* @__PURE__ */ new Map();
1340
+ artworks.forEach((artwork) => {
1341
+ if (!artworksMap.has(artwork.collectionId)) {
1342
+ artworksMap.set(artwork.collectionId, []);
1343
+ }
1344
+ let imageUrl;
1345
+ if (artwork.fileId && fileIdToUrlMap.has(artwork.fileId)) {
1346
+ imageUrl = fileIdToUrlMap.get(artwork.fileId);
1347
+ } else {
1348
+ imageUrl = `/api/showmasterpiece/collections/${artwork.collectionId}/artworks/${artwork.id}/image`;
1349
+ }
1350
+ const artworkPage = {
1351
+ id: artwork.id,
1352
+ title: artwork.title || "",
1353
+ number: artwork.number || "",
1354
+ image: imageUrl,
1355
+ // 使用处理后的图片URL
1356
+ fileId: artwork.fileId || void 0,
1357
+ // 添加fileId支持
1358
+ description: artwork.description || "",
1359
+ createdTime: artwork.createdTime || "",
1360
+ theme: artwork.theme || "",
1361
+ pageOrder: artwork.pageOrder || 0
1362
+ // 添加pageOrder字段
1363
+ };
1364
+ artworksMap.get(artwork.collectionId).push(artworkPage);
1365
+ });
1366
+ return collections.map((collection) => {
1367
+ let coverImageUrl;
1368
+ if (collection.coverImageFileId && fileIdToUrlMap.has(collection.coverImageFileId)) {
1369
+ coverImageUrl = fileIdToUrlMap.get(collection.coverImageFileId);
1370
+ console.log(`\u{1F517} [CollectionsDbService] \u4F7F\u7528OSS URL: ${coverImageUrl}`);
1371
+ } else if (collection.coverImage) {
1372
+ coverImageUrl = collection.coverImage;
1373
+ console.log(`\u{1F517} [CollectionsDbService] \u4F7F\u7528\u539F\u59CB\u8DEF\u5F84: ${coverImageUrl}`);
1374
+ } else {
1375
+ coverImageUrl = "";
1376
+ console.log(`\u{1F517} [CollectionsDbService] \u65E0\u5C01\u9762\u56FE\u7247`);
1377
+ }
1378
+ return {
1379
+ id: collection.id,
1380
+ title: collection.title,
1381
+ number: collection.number,
1382
+ coverImage: coverImageUrl,
1383
+ // ✅ 使用处理后的URL
1384
+ coverImageFileId: collection.coverImageFileId || void 0,
1385
+ description: collection.description || "",
1386
+ category: collection.categoryId ? categoriesMap.get(collection.categoryId) || "\u753B\u96C6" : "\u753B\u96C6",
1387
+ tags: tagsMap.get(collection.id) || [],
1388
+ isPublished: collection.isPublished,
1389
+ price: collection.price || void 0,
1390
+ pages: artworksMap.get(collection.id) || []
1391
+ // 🚀 作品数据精简,大幅减少传输量
1392
+ };
1393
+ });
1394
+ } catch (error) {
1395
+ console.error("\u83B7\u53D6\u753B\u96C6\u5217\u8868\u5931\u8D25:", error);
1396
+ throw error;
1397
+ }
1398
+ }
1399
+ /**
1400
+ * 从数据库获取画集概览数据
1401
+ */
1402
+ async fetchCollectionsOverviewFromDb() {
1403
+ try {
1404
+ const collections = await db.select({
1405
+ id: comicUniverseCollections.id,
1406
+ title: comicUniverseCollections.title,
1407
+ number: comicUniverseCollections.number,
1408
+ coverImage: comicUniverseCollections.coverImage,
1409
+ coverImageFileId: comicUniverseCollections.coverImageFileId,
1410
+ // 新增:封面图片文件ID
1411
+ description: comicUniverseCollections.description,
1412
+ isPublished: comicUniverseCollections.isPublished,
1413
+ displayOrder: comicUniverseCollections.displayOrder,
1414
+ price: comicUniverseCollections.price,
1415
+ createdAt: comicUniverseCollections.createdAt,
1416
+ categoryId: comicUniverseCollections.categoryId
1417
+ }).from(comicUniverseCollections).where(eq(comicUniverseCollections.isPublished, true)).orderBy(
1418
+ desc(comicUniverseCollections.displayOrder),
1419
+ desc(comicUniverseCollections.createdAt)
1420
+ );
1421
+ if (collections.length === 0) {
1422
+ return [];
1423
+ }
1424
+ const collectionIds = collections.map((c) => c.id);
1425
+ const [categories, tags, artworkCounts] = await Promise.all([
1426
+ // 获取分类信息
1427
+ db.select({
1428
+ id: comicUniverseCategories.id,
1429
+ name: comicUniverseCategories.name
1430
+ }).from(comicUniverseCategories).where(eq(comicUniverseCategories.isActive, true)),
1431
+ // 获取标签信息
1432
+ db.select({
1433
+ collectionId: comicUniverseCollectionTags.collectionId,
1434
+ tagName: comicUniverseTags.name
1435
+ }).from(comicUniverseCollectionTags).innerJoin(
1436
+ comicUniverseTags,
1437
+ eq(comicUniverseCollectionTags.tagId, comicUniverseTags.id)
1438
+ ).where(
1439
+ and(
1440
+ inArray(comicUniverseCollectionTags.collectionId, collectionIds),
1441
+ eq(comicUniverseTags.isActive, true)
1442
+ )
1443
+ ),
1444
+ // 获取作品数量(而不是具体作品)
1445
+ db.select({
1446
+ collectionId: comicUniverseArtworks.collectionId,
1447
+ count: sql`count(*)`.as("count")
1448
+ }).from(comicUniverseArtworks).where(
1449
+ and(
1450
+ inArray(comicUniverseArtworks.collectionId, collectionIds),
1451
+ eq(comicUniverseArtworks.isActive, true)
1452
+ )
1453
+ ).groupBy(comicUniverseArtworks.collectionId)
1454
+ ]);
1455
+ const categoriesMap = new Map(categories.map((cat) => [cat.id, cat.name]));
1456
+ const tagsMap = /* @__PURE__ */ new Map();
1457
+ tags.forEach((tag) => {
1458
+ if (!tagsMap.has(tag.collectionId)) {
1459
+ tagsMap.set(tag.collectionId, []);
1460
+ }
1461
+ tagsMap.get(tag.collectionId).push(tag.tagName);
1462
+ });
1463
+ const artworkCountsMap = new Map(artworkCounts.map((ac) => [ac.collectionId, ac.count]));
1464
+ const fileIdToUrlMap = /* @__PURE__ */ new Map();
1465
+ const coverImageFileIds = collections.filter((c) => c.coverImageFileId).map((c) => c.coverImageFileId);
1466
+ if (coverImageFileIds.length > 0) {
1467
+ try {
1468
+ const { getShowMasterpieceFileConfig: getShowMasterpieceFileConfig2 } = await import('./fileService-O3W6YXCI.mjs');
1469
+ const configManager = await getShowMasterpieceFileConfig2();
1470
+ const { UniversalFileService } = await import('./UniversalFileService-TNYKO6JN.mjs');
1471
+ const fileService = new UniversalFileService(configManager.getConfig());
1472
+ await fileService.initialize();
1473
+ const urlPromises = coverImageFileIds.map(async (fileId) => {
1474
+ try {
1475
+ const fileUrl = await fileService.getFileUrl(fileId);
1476
+ return { fileId, url: fileUrl };
1477
+ } catch (error) {
1478
+ console.warn(`\u26A0\uFE0F [CollectionsDbService] \u83B7\u53D6\u5C01\u9762\u56FE\u7247URL\u5931\u8D25: ${fileId}`, error);
1479
+ return { fileId, url: null };
1480
+ }
1481
+ });
1482
+ const urlResults = await Promise.all(urlPromises);
1483
+ urlResults.forEach((result) => {
1484
+ if (result.url) {
1485
+ fileIdToUrlMap.set(result.fileId, result.url);
1486
+ }
1487
+ });
1488
+ } catch (error) {
1489
+ console.warn("\u26A0\uFE0F [CollectionsDbService] \u6279\u91CF\u83B7\u53D6\u5C01\u9762\u56FE\u7247URL\u5931\u8D25:", error);
1490
+ }
1491
+ }
1492
+ return collections.map((collection) => {
1493
+ let coverImageUrl;
1494
+ if (collection.coverImageFileId && fileIdToUrlMap.has(collection.coverImageFileId)) {
1495
+ coverImageUrl = fileIdToUrlMap.get(collection.coverImageFileId);
1496
+ } else {
1497
+ coverImageUrl = collection.coverImage || "";
1498
+ }
1499
+ console.log(`\u{1F517} [CollectionsDbService] 1\u5C01\u9762\u56FE\u7247URL: ${coverImageUrl}`);
1500
+ return {
1501
+ id: collection.id,
1502
+ title: collection.title,
1503
+ number: collection.number,
1504
+ coverImage: coverImageUrl,
1505
+ // 使用处理后的封面图片URL
1506
+ coverImageFileId: collection.coverImageFileId || void 0,
1507
+ // 新增:封面图片文件ID
1508
+ description: collection.description || "",
1509
+ category: collection.categoryId ? categoriesMap.get(collection.categoryId) || "\u753B\u96C6" : "\u753B\u96C6",
1510
+ tags: tagsMap.get(collection.id) || [],
1511
+ isPublished: collection.isPublished,
1512
+ price: collection.price || void 0,
1513
+ artworkCount: artworkCountsMap.get(collection.id) || 0
1514
+ };
1515
+ });
1516
+ } catch (error) {
1517
+ console.error("\u83B7\u53D6\u753B\u96C6\u6982\u89C8\u5931\u8D25:", error);
1518
+ throw error;
1519
+ }
1520
+ }
1521
+ // 清除缓存的方法
1522
+ clearCache() {
1523
+ this.collectionsCache = null;
1524
+ this.collectionsCacheTime = 0;
1525
+ this.collectionsOverviewCache = null;
1526
+ this.collectionsOverviewCacheTime = 0;
1527
+ }
1528
+ // 创建画集
1529
+ async createCollection(collectionData) {
1530
+ let categoryId = null;
1531
+ if (collectionData.category) {
1532
+ const existingCategory = await db.select().from(comicUniverseCategories).where(eq(comicUniverseCategories.name, collectionData.category)).limit(1);
1533
+ if (existingCategory.length > 0) {
1534
+ categoryId = existingCategory[0].id;
1535
+ } else {
1536
+ const newCategory = await db.insert(comicUniverseCategories).values({
1537
+ name: collectionData.category
1538
+ }).returning();
1539
+ categoryId = newCategory[0].id;
1540
+ }
1541
+ }
1542
+ const newCollection = await db.insert(comicUniverseCollections).values({
1543
+ title: collectionData.title,
1544
+ number: collectionData.number,
1545
+ coverImage: collectionData.coverImage,
1546
+ coverImageFileId: collectionData.coverImageFileId || null,
1547
+ description: collectionData.description,
1548
+ categoryId,
1549
+ isPublished: collectionData.isPublished,
1550
+ publishedAt: collectionData.isPublished ? /* @__PURE__ */ new Date() : null,
1551
+ price: collectionData.price || null
1552
+ }).returning();
1553
+ if (collectionData.tags && collectionData.tags.length > 0) {
1554
+ await this.updateCollectionTags(newCollection[0].id, collectionData.tags);
1555
+ }
1556
+ this.clearCache();
1557
+ const collections = await this.getAllCollections(false);
1558
+ const filterCollection = collections.filter((c) => c.id === newCollection[0].id);
1559
+ console.log("\u{1F3A8} [createCollection] \u521B\u5EFA\u753B\u96C6\u6210\u529F:", filterCollection);
1560
+ return filterCollection[0];
1561
+ }
1562
+ // 更新画集
1563
+ async updateCollection(id, collectionData) {
1564
+ let categoryId = null;
1565
+ if (collectionData.category) {
1566
+ const existingCategory = await db.select().from(comicUniverseCategories).where(eq(comicUniverseCategories.name, collectionData.category)).limit(1);
1567
+ if (existingCategory.length > 0) {
1568
+ categoryId = existingCategory[0].id;
1569
+ } else {
1570
+ const newCategory = await db.insert(comicUniverseCategories).values({
1571
+ name: collectionData.category
1572
+ }).returning();
1573
+ categoryId = newCategory[0].id;
1574
+ }
1575
+ }
1576
+ await db.update(comicUniverseCollections).set({
1577
+ title: collectionData.title,
1578
+ number: collectionData.number,
1579
+ coverImage: collectionData.coverImage,
1580
+ coverImageFileId: collectionData.coverImageFileId || null,
1581
+ description: collectionData.description,
1582
+ categoryId,
1583
+ isPublished: collectionData.isPublished,
1584
+ publishedAt: collectionData.isPublished ? /* @__PURE__ */ new Date() : null,
1585
+ price: collectionData.price || null,
1586
+ updatedAt: /* @__PURE__ */ new Date()
1587
+ }).where(eq(comicUniverseCollections.id, id));
1588
+ await this.updateCollectionTags(id, collectionData.tags || []);
1589
+ this.clearCache();
1590
+ const collections = await this.getAllCollections(false);
1591
+ return collections.find((c) => c.id === id);
1592
+ }
1593
+ // 删除画集
1594
+ async deleteCollection(id) {
1595
+ const collection = await db.select({
1596
+ id: comicUniverseCollections.id,
1597
+ title: comicUniverseCollections.title
1598
+ }).from(comicUniverseCollections).where(eq(comicUniverseCollections.id, id)).limit(1);
1599
+ if (!collection.length) {
1600
+ throw new Error("\u753B\u96C6\u4E0D\u5B58\u5728");
1601
+ }
1602
+ const collectionData = collection[0];
1603
+ console.log(`\u{1F5D1}\uFE0F [deleteCollection] \u5220\u9664\u753B\u96C6 ID:${id} "${collectionData.title}"`);
1604
+ await db.delete(comicUniverseCollections).where(eq(comicUniverseCollections.id, id));
1605
+ this.clearCache();
1606
+ }
1607
+ // 更新画集标签
1608
+ async updateCollectionTags(collectionId, tagNames) {
1609
+ await db.delete(comicUniverseCollectionTags).where(eq(comicUniverseCollectionTags.collectionId, collectionId));
1610
+ if (tagNames.length === 0) return;
1611
+ const tagIds = [];
1612
+ for (const tagName of tagNames) {
1613
+ const existingTag = await db.select().from(comicUniverseTags).where(eq(comicUniverseTags.name, tagName)).limit(1);
1614
+ if (existingTag.length > 0) {
1615
+ tagIds.push(existingTag[0].id);
1616
+ } else {
1617
+ const newTag = await db.insert(comicUniverseTags).values({
1618
+ name: tagName
1619
+ }).returning();
1620
+ tagIds.push(newTag[0].id);
1621
+ }
1622
+ }
1623
+ const tagRelations = tagIds.map((tagId) => ({
1624
+ collectionId,
1625
+ tagId
1626
+ }));
1627
+ await db.insert(comicUniverseCollectionTags).values(tagRelations);
1628
+ }
1629
+ // 更新画集显示顺序
1630
+ async updateCollectionOrder(collectionOrders) {
1631
+ try {
1632
+ await db.transaction(async (tx) => {
1633
+ for (const { id, displayOrder } of collectionOrders) {
1634
+ await tx.update(comicUniverseCollections).set({
1635
+ displayOrder,
1636
+ updatedAt: /* @__PURE__ */ new Date()
1637
+ }).where(eq(comicUniverseCollections.id, id));
1638
+ }
1639
+ });
1640
+ this.clearCache();
1641
+ } catch (error) {
1642
+ console.error("\u66F4\u65B0\u753B\u96C6\u987A\u5E8F\u5931\u8D25:", error);
1643
+ throw error;
1644
+ }
1645
+ }
1646
+ // 移动画集到指定位置
1647
+ async moveCollection(collectionId, targetOrder) {
1648
+ try {
1649
+ const collections = await db.select({
1650
+ id: comicUniverseCollections.id,
1651
+ displayOrder: comicUniverseCollections.displayOrder,
1652
+ createdAt: comicUniverseCollections.createdAt
1653
+ }).from(comicUniverseCollections).where(eq(comicUniverseCollections.isPublished, true)).orderBy(
1654
+ desc(comicUniverseCollections.displayOrder),
1655
+ desc(comicUniverseCollections.createdAt),
1656
+ desc(comicUniverseCollections.id)
1657
+ );
1658
+ const normalized = await this.normalizeCollectionOrderIfNeeded(collections);
1659
+ const workingCollections = normalized ?? collections;
1660
+ const targetCollection = workingCollections.find((c) => c.id === collectionId);
1661
+ if (!targetCollection) {
1662
+ throw new Error("\u753B\u96C6\u4E0D\u5B58\u5728");
1663
+ }
1664
+ const sortedCollections = workingCollections.filter((c) => c.id !== collectionId);
1665
+ sortedCollections.splice(targetOrder, 0, targetCollection);
1666
+ const updates = sortedCollections.map((collection, index3) => ({
1667
+ id: collection.id,
1668
+ displayOrder: sortedCollections.length - index3
1669
+ // 从高到低排序
1670
+ }));
1671
+ await this.updateCollectionOrder(updates);
1672
+ } catch (error) {
1673
+ console.error("\u79FB\u52A8\u753B\u96C6\u5931\u8D25:", error);
1674
+ throw error;
1675
+ }
1676
+ }
1677
+ // 上移画集
1678
+ async moveCollectionUp(collectionId) {
1679
+ try {
1680
+ console.log("\u{1F4CA} [\u540E\u7AEF\u6392\u5E8F] \u5F00\u59CB\u4E0A\u79FB\u753B\u96C6\u64CD\u4F5C\uFF0CcollectionId:", collectionId);
1681
+ const collections = await db.select({
1682
+ id: comicUniverseCollections.id,
1683
+ displayOrder: comicUniverseCollections.displayOrder,
1684
+ title: comicUniverseCollections.title,
1685
+ createdAt: comicUniverseCollections.createdAt
1686
+ }).from(comicUniverseCollections).where(eq(comicUniverseCollections.isPublished, true)).orderBy(
1687
+ desc(comicUniverseCollections.displayOrder),
1688
+ desc(comicUniverseCollections.createdAt),
1689
+ desc(comicUniverseCollections.id)
1690
+ );
1691
+ const normalized = await this.normalizeCollectionOrderIfNeeded(collections);
1692
+ const workingCollections = normalized ?? collections;
1693
+ console.log("\u{1F4CA} [\u540E\u7AEF\u6392\u5E8F] \u5F53\u524D\u6570\u636E\u5E93\u6392\u5E8F\u72B6\u6001 (\u6309displayOrder\u964D\u5E8F):", {
1694
+ totalCount: workingCollections.length,
1695
+ collections: workingCollections.map((c, i) => ({
1696
+ dbIndex: i,
1697
+ id: c.id,
1698
+ title: c.title,
1699
+ displayOrder: c.displayOrder,
1700
+ note: i === 0 ? "(\u6570\u636E\u5E93\u7B2C\u4E00\u6761/displayOrder\u6700\u5927)" : i === workingCollections.length - 1 ? "(\u6570\u636E\u5E93\u6700\u540E\u6761/displayOrder\u6700\u5C0F)" : ""
1701
+ }))
1702
+ });
1703
+ const currentIndex = workingCollections.findIndex((c) => c.id === collectionId);
1704
+ if (currentIndex === -1) {
1705
+ console.error("\u274C [\u540E\u7AEF\u6392\u5E8F] \u753B\u96C6\u4E0D\u5B58\u5728");
1706
+ throw new Error("\u753B\u96C6\u4E0D\u5B58\u5728");
1707
+ }
1708
+ if (currentIndex === 0) {
1709
+ console.error("\u274C [\u540E\u7AEF\u6392\u5E8F] \u753B\u96C6\u5DF2\u7ECF\u5728\u6700\u9876\u90E8 (displayOrder\u6700\u5927\u503C)");
1710
+ throw new Error("\u753B\u96C6\u5DF2\u7ECF\u5728\u6700\u9876\u90E8");
1711
+ }
1712
+ console.log("\u{1F4CA} [\u540E\u7AEF\u6392\u5E8F] \u4E0A\u79FB\u64CD\u4F5C\u8BE6\u60C5:", {
1713
+ targetCollection: {
1714
+ id: workingCollections[currentIndex].id,
1715
+ title: workingCollections[currentIndex].title,
1716
+ currentDisplayOrder: workingCollections[currentIndex].displayOrder,
1717
+ currentDbIndex: currentIndex
1718
+ },
1719
+ willSwapWith: {
1720
+ id: workingCollections[currentIndex - 1].id,
1721
+ title: workingCollections[currentIndex - 1].title,
1722
+ currentDisplayOrder: workingCollections[currentIndex - 1].displayOrder,
1723
+ currentDbIndex: currentIndex - 1
1724
+ },
1725
+ semantics: "\u4E0A\u79FB=\u4EA4\u6362displayOrder\u503C\uFF0C\u4F7F\u76EE\u6807\u753B\u96C6\u83B7\u5F97\u66F4\u5927\u7684displayOrder\u503C"
1726
+ });
1727
+ const targetIndex = currentIndex - 1;
1728
+ const currentOrder = workingCollections[targetIndex].displayOrder ?? 0;
1729
+ const targetOrder = workingCollections[currentIndex].displayOrder ?? 0;
1730
+ const updates = [
1731
+ { id: workingCollections[currentIndex].id, displayOrder: currentOrder },
1732
+ { id: workingCollections[targetIndex].id, displayOrder: targetOrder }
1733
+ ];
1734
+ console.log("\u{1F4CA} [\u540E\u7AEF\u6392\u5E8F] \u5C06\u6267\u884C\u7684\u66F4\u65B0\u64CD\u4F5C:", {
1735
+ updates,
1736
+ explanation: "\u76EE\u6807\u753B\u96C6\u5C06\u83B7\u5F97\u66F4\u5927\u7684displayOrder\u503C\uFF0C\u4ECE\u800C\u5728\u5217\u8868\u4E2D\u4E0A\u79FB"
1737
+ });
1738
+ await this.updateCollectionOrder(updates);
1739
+ console.log("\u2705 [\u540E\u7AEF\u6392\u5E8F] \u4E0A\u79FB\u64CD\u4F5C\u5B8C\u6210");
1740
+ } catch (error) {
1741
+ console.error("\u274C [\u540E\u7AEF\u6392\u5E8F] \u4E0A\u79FB\u753B\u96C6\u5931\u8D25:", error);
1742
+ throw error;
1743
+ }
1744
+ }
1745
+ // 下移画集
1746
+ async moveCollectionDown(collectionId) {
1747
+ try {
1748
+ console.log("\u{1F4CA} [\u540E\u7AEF\u6392\u5E8F] \u5F00\u59CB\u4E0B\u79FB\u753B\u96C6\u64CD\u4F5C\uFF0CcollectionId:", collectionId);
1749
+ const collections = await db.select({
1750
+ id: comicUniverseCollections.id,
1751
+ displayOrder: comicUniverseCollections.displayOrder,
1752
+ title: comicUniverseCollections.title,
1753
+ createdAt: comicUniverseCollections.createdAt
1754
+ }).from(comicUniverseCollections).where(eq(comicUniverseCollections.isPublished, true)).orderBy(
1755
+ desc(comicUniverseCollections.displayOrder),
1756
+ desc(comicUniverseCollections.createdAt),
1757
+ desc(comicUniverseCollections.id)
1758
+ );
1759
+ const normalized = await this.normalizeCollectionOrderIfNeeded(collections);
1760
+ const workingCollections = normalized ?? collections;
1761
+ console.log("\u{1F4CA} [\u540E\u7AEF\u6392\u5E8F] \u5F53\u524D\u6570\u636E\u5E93\u6392\u5E8F\u72B6\u6001 (\u6309displayOrder\u964D\u5E8F):", {
1762
+ totalCount: workingCollections.length,
1763
+ collections: workingCollections.map((c, i) => ({
1764
+ dbIndex: i,
1765
+ id: c.id,
1766
+ title: c.title,
1767
+ displayOrder: c.displayOrder,
1768
+ note: i === 0 ? "(\u6570\u636E\u5E93\u7B2C\u4E00\u6761/displayOrder\u6700\u5927)" : i === workingCollections.length - 1 ? "(\u6570\u636E\u5E93\u6700\u540E\u6761/displayOrder\u6700\u5C0F)" : ""
1769
+ }))
1770
+ });
1771
+ const currentIndex = workingCollections.findIndex((c) => c.id === collectionId);
1772
+ if (currentIndex === -1) {
1773
+ console.error("\u274C [\u540E\u7AEF\u6392\u5E8F] \u753B\u96C6\u4E0D\u5B58\u5728");
1774
+ throw new Error("\u753B\u96C6\u4E0D\u5B58\u5728");
1775
+ }
1776
+ if (currentIndex === workingCollections.length - 1) {
1777
+ console.error("\u274C [\u540E\u7AEF\u6392\u5E8F] \u753B\u96C6\u5DF2\u7ECF\u5728\u6700\u5E95\u90E8 (displayOrder\u6700\u5C0F\u503C)");
1778
+ throw new Error("\u753B\u96C6\u5DF2\u7ECF\u5728\u6700\u5E95\u90E8");
1779
+ }
1780
+ console.log("\u{1F4CA} [\u540E\u7AEF\u6392\u5E8F] \u4E0B\u79FB\u64CD\u4F5C\u8BE6\u60C5:", {
1781
+ targetCollection: {
1782
+ id: workingCollections[currentIndex].id,
1783
+ title: workingCollections[currentIndex].title,
1784
+ currentDisplayOrder: workingCollections[currentIndex].displayOrder,
1785
+ currentDbIndex: currentIndex
1786
+ },
1787
+ willSwapWith: {
1788
+ id: workingCollections[currentIndex + 1].id,
1789
+ title: workingCollections[currentIndex + 1].title,
1790
+ currentDisplayOrder: workingCollections[currentIndex + 1].displayOrder,
1791
+ currentDbIndex: currentIndex + 1
1792
+ },
1793
+ semantics: "\u4E0B\u79FB=\u4EA4\u6362displayOrder\u503C\uFF0C\u4F7F\u76EE\u6807\u753B\u96C6\u83B7\u5F97\u66F4\u5C0F\u7684displayOrder\u503C"
1794
+ });
1795
+ const targetIndex = currentIndex + 1;
1796
+ const currentOrder = workingCollections[targetIndex].displayOrder ?? 0;
1797
+ const targetOrder = workingCollections[currentIndex].displayOrder ?? 0;
1798
+ const updates = [
1799
+ { id: workingCollections[currentIndex].id, displayOrder: currentOrder },
1800
+ { id: workingCollections[targetIndex].id, displayOrder: targetOrder }
1801
+ ];
1802
+ console.log("\u{1F4CA} [\u540E\u7AEF\u6392\u5E8F] \u5C06\u6267\u884C\u7684\u66F4\u65B0\u64CD\u4F5C:", {
1803
+ updates,
1804
+ explanation: "\u76EE\u6807\u753B\u96C6\u5C06\u83B7\u5F97\u66F4\u5C0F\u7684displayOrder\u503C\uFF0C\u4ECE\u800C\u5728\u5217\u8868\u4E2D\u4E0B\u79FB"
1805
+ });
1806
+ await this.updateCollectionOrder(updates);
1807
+ console.log("\u2705 [\u540E\u7AEF\u6392\u5E8F] \u4E0B\u79FB\u64CD\u4F5C\u5B8C\u6210");
1808
+ } catch (error) {
1809
+ console.error("\u274C [\u540E\u7AEF\u6392\u5E8F] \u4E0B\u79FB\u753B\u96C6\u5931\u8D25:", error);
1810
+ throw error;
1811
+ }
1812
+ }
1813
+ async normalizeCollectionOrderIfNeeded(collections) {
1814
+ if (collections.length <= 1) return null;
1815
+ const orders = collections.map((c) => c.displayOrder ?? 0);
1816
+ const uniqueOrders = new Set(orders);
1817
+ const hasDuplicates = uniqueOrders.size !== orders.length;
1818
+ const hasAllZero = uniqueOrders.size === 1 && orders[0] === 0;
1819
+ if (!hasDuplicates && !hasAllZero) return null;
1820
+ const updates = collections.map((collection, index3) => ({
1821
+ id: collection.id,
1822
+ displayOrder: collections.length - index3
1823
+ }));
1824
+ await this.updateCollectionOrder(updates);
1825
+ return db.select({
1826
+ id: comicUniverseCollections.id,
1827
+ displayOrder: comicUniverseCollections.displayOrder,
1828
+ createdAt: comicUniverseCollections.createdAt
1829
+ }).from(comicUniverseCollections).where(eq(comicUniverseCollections.isPublished, true)).orderBy(
1830
+ desc(comicUniverseCollections.displayOrder),
1831
+ desc(comicUniverseCollections.createdAt),
1832
+ desc(comicUniverseCollections.id)
1833
+ );
1834
+ }
1835
+ };
1836
+ var ArtworksDbService = class {
1837
+ constructor(collectionsService) {
1838
+ this.collectionsService = collectionsService;
1839
+ }
1840
+ // 添加作品到画集
1841
+ async addArtworkToCollection(collectionId, artworkData) {
1842
+ console.log("\u{1F5C3}\uFE0F [\u6570\u636E\u5E93] \u5F00\u59CB\u6DFB\u52A0\u4F5C\u54C1\u5230\u753B\u96C6:", {
1843
+ collectionId,
1844
+ title: artworkData.title,
1845
+ number: artworkData.number
1846
+ });
1847
+ const maxOrder = await db.select({
1848
+ maxOrder: sql`COALESCE(MAX(${comicUniverseArtworks.pageOrder}), -1)`
1849
+ }).from(comicUniverseArtworks).where(eq(comicUniverseArtworks.collectionId, collectionId));
1850
+ const newOrder = (maxOrder[0]?.maxOrder || -1) + 1;
1851
+ console.log("\u{1F4CA} [\u6570\u636E\u5E93] \u8BA1\u7B97\u65B0\u7684\u9875\u9762\u987A\u5E8F:", newOrder);
1852
+ const insertData = {
1853
+ collectionId,
1854
+ title: artworkData.title,
1855
+ number: artworkData.number,
1856
+ description: artworkData.description,
1857
+ createdTime: artworkData.createdTime,
1858
+ theme: artworkData.theme,
1859
+ pageOrder: newOrder
1860
+ };
1861
+ if (!artworkData.fileId) {
1862
+ throw new Error("\u5FC5\u987B\u63D0\u4F9B\u6587\u4EF6ID\uFF0C\u8BF7\u5148\u4E0A\u4F20\u56FE\u7247");
1863
+ }
1864
+ insertData.fileId = artworkData.fileId;
1865
+ insertData.migrationStatus = "completed";
1866
+ console.log("\u{1F4C1} [\u6570\u636E\u5E93] \u4F7F\u7528\u901A\u7528\u6587\u4EF6\u670D\u52A1ID:", artworkData.fileId);
1867
+ const newArtwork = await db.insert(comicUniverseArtworks).values(insertData).returning();
1868
+ console.log("\u2705 [\u6570\u636E\u5E93] \u4F5C\u54C1\u63D2\u5165\u6210\u529F:", {
1869
+ id: newArtwork[0].id,
1870
+ collectionId: newArtwork[0].collectionId,
1871
+ pageOrder: newArtwork[0].pageOrder,
1872
+ title: newArtwork[0].title,
1873
+ fileId: newArtwork[0].fileId,
1874
+ migrationStatus: newArtwork[0].migrationStatus
1875
+ });
1876
+ this.collectionsService.clearCache();
1877
+ console.log("\u{1F9F9} [\u6570\u636E\u5E93] \u7F13\u5B58\u5DF2\u6E05\u9664");
1878
+ const result = {
1879
+ id: newArtwork[0].id,
1880
+ title: newArtwork[0].title,
1881
+ number: newArtwork[0].number,
1882
+ image: newArtwork[0].image || "",
1883
+ fileId: newArtwork[0].fileId || void 0,
1884
+ description: newArtwork[0].description || "",
1885
+ createdTime: newArtwork[0].createdTime || void 0,
1886
+ theme: newArtwork[0].theme || void 0,
1887
+ pageOrder: newArtwork[0].pageOrder || 0
1888
+ };
1889
+ console.log("\u{1F4E4} [\u6570\u636E\u5E93] \u8FD4\u56DE\u4F5C\u54C1\u6570\u636E:", result);
1890
+ return result;
1891
+ }
1892
+ // 更新作品
1893
+ async updateArtwork(collectionId, artworkId, artworkData) {
1894
+ const existingArtwork = await db.select().from(comicUniverseArtworks).where(and(
1895
+ eq(comicUniverseArtworks.id, artworkId),
1896
+ eq(comicUniverseArtworks.collectionId, collectionId)
1897
+ )).limit(1);
1898
+ if (existingArtwork.length === 0) {
1899
+ throw new Error(`\u4F5C\u54C1\u4E0D\u5B58\u5728\u6216\u4E0D\u5C5E\u4E8E\u6307\u5B9A\u753B\u96C6 (\u4F5C\u54C1ID: ${artworkId}, \u753B\u96C6ID: ${collectionId})`);
1900
+ }
1901
+ const updateData = {
1902
+ title: artworkData.title,
1903
+ number: artworkData.number,
1904
+ description: artworkData.description,
1905
+ createdTime: artworkData.createdTime,
1906
+ theme: artworkData.theme,
1907
+ updatedAt: /* @__PURE__ */ new Date()
1908
+ };
1909
+ if (artworkData.fileId) {
1910
+ updateData.fileId = artworkData.fileId;
1911
+ updateData.migrationStatus = "completed";
1912
+ updateData.image = null;
1913
+ console.log("\u{1F4C1} [\u6570\u636E\u5E93] \u66F4\u65B0\u901A\u7528\u6587\u4EF6\u670D\u52A1ID:", artworkData.fileId);
1914
+ }
1915
+ const updatedArtwork = await db.update(comicUniverseArtworks).set(updateData).where(and(
1916
+ eq(comicUniverseArtworks.id, artworkId),
1917
+ eq(comicUniverseArtworks.collectionId, collectionId)
1918
+ )).returning();
1919
+ if (updatedArtwork.length === 0) {
1920
+ throw new Error("\u66F4\u65B0\u4F5C\u54C1\u5931\u8D25\uFF0C\u672A\u8FD4\u56DE\u6570\u636E");
1921
+ }
1922
+ this.collectionsService.clearCache();
1923
+ return {
1924
+ id: updatedArtwork[0].id,
1925
+ title: updatedArtwork[0].title,
1926
+ number: updatedArtwork[0].number,
1927
+ image: updatedArtwork[0].image || "",
1928
+ fileId: updatedArtwork[0].fileId || void 0,
1929
+ description: updatedArtwork[0].description || "",
1930
+ createdTime: updatedArtwork[0].createdTime || "",
1931
+ theme: updatedArtwork[0].theme || "",
1932
+ pageOrder: updatedArtwork[0].pageOrder || 0
1933
+ };
1934
+ }
1935
+ // 删除作品
1936
+ async deleteArtwork(collectionId, artworkId) {
1937
+ const artworkWithCollection = await db.select({
1938
+ artworkId: comicUniverseArtworks.id,
1939
+ artworkTitle: comicUniverseArtworks.title,
1940
+ collectionId: comicUniverseCollections.id,
1941
+ collectionTitle: comicUniverseCollections.title
1942
+ }).from(comicUniverseArtworks).leftJoin(comicUniverseCollections, eq(comicUniverseArtworks.collectionId, comicUniverseCollections.id)).where(and(
1943
+ eq(comicUniverseArtworks.id, artworkId),
1944
+ eq(comicUniverseArtworks.collectionId, collectionId)
1945
+ )).limit(1);
1946
+ if (!artworkWithCollection.length) {
1947
+ throw new Error("\u4F5C\u54C1\u4E0D\u5B58\u5728\u6216\u4E0D\u5C5E\u4E8E\u6307\u5B9A\u753B\u96C6");
1948
+ }
1949
+ const artwork = artworkWithCollection[0];
1950
+ console.log(`\u{1F5D1}\uFE0F [deleteArtwork] \u5220\u9664\u4F5C\u54C1 ID:${artworkId} "${artwork.artworkTitle}" \u4ECE\u753B\u96C6 ID:${collectionId} "${artwork.collectionTitle}"`);
1951
+ await db.delete(comicUniverseArtworks).where(and(
1952
+ eq(comicUniverseArtworks.id, artworkId),
1953
+ eq(comicUniverseArtworks.collectionId, collectionId)
1954
+ ));
1955
+ this.collectionsService.clearCache();
1956
+ }
1957
+ // 更新作品显示顺序
1958
+ async updateArtworkOrder(collectionId, artworkOrders) {
1959
+ try {
1960
+ await db.transaction(async (tx) => {
1961
+ for (const { id, pageOrder } of artworkOrders) {
1962
+ await tx.update(comicUniverseArtworks).set({
1963
+ pageOrder,
1964
+ updatedAt: /* @__PURE__ */ new Date()
1965
+ }).where(and(
1966
+ eq(comicUniverseArtworks.id, id),
1967
+ eq(comicUniverseArtworks.collectionId, collectionId)
1968
+ ));
1969
+ }
1970
+ });
1971
+ this.collectionsService.clearCache();
1972
+ } catch (error) {
1973
+ console.error("\u66F4\u65B0\u4F5C\u54C1\u987A\u5E8F\u5931\u8D25:", error);
1974
+ throw error;
1975
+ }
1976
+ }
1977
+ // 移动作品到指定位置
1978
+ async moveArtwork(collectionId, artworkId, targetOrder) {
1979
+ try {
1980
+ const artworks = await db.select({
1981
+ id: comicUniverseArtworks.id,
1982
+ pageOrder: comicUniverseArtworks.pageOrder
1983
+ }).from(comicUniverseArtworks).where(and(
1984
+ eq(comicUniverseArtworks.collectionId, collectionId),
1985
+ eq(comicUniverseArtworks.isActive, true)
1986
+ )).orderBy(asc(comicUniverseArtworks.pageOrder));
1987
+ const targetArtwork = artworks.find((a) => a.id === artworkId);
1988
+ if (!targetArtwork) {
1989
+ throw new Error("\u4F5C\u54C1\u4E0D\u5B58\u5728");
1990
+ }
1991
+ if (targetOrder < 0 || targetOrder >= artworks.length) {
1992
+ throw new Error("\u76EE\u6807\u4F4D\u7F6E\u65E0\u6548");
1993
+ }
1994
+ const sortedArtworks = artworks.filter((a) => a.id !== artworkId);
1995
+ sortedArtworks.splice(targetOrder, 0, targetArtwork);
1996
+ const updates = sortedArtworks.map((artwork, index3) => ({
1997
+ id: artwork.id,
1998
+ pageOrder: index3
1999
+ }));
2000
+ await this.updateArtworkOrder(collectionId, updates);
2001
+ } catch (error) {
2002
+ console.error("\u79FB\u52A8\u4F5C\u54C1\u5931\u8D25:", error);
2003
+ throw error;
2004
+ }
2005
+ }
2006
+ // 上移作品
2007
+ async moveArtworkUp(collectionId, artworkId) {
2008
+ try {
2009
+ const artworks = await db.select({
2010
+ id: comicUniverseArtworks.id,
2011
+ pageOrder: comicUniverseArtworks.pageOrder
2012
+ }).from(comicUniverseArtworks).where(and(
2013
+ eq(comicUniverseArtworks.collectionId, collectionId),
2014
+ eq(comicUniverseArtworks.isActive, true)
2015
+ )).orderBy(asc(comicUniverseArtworks.pageOrder), asc(comicUniverseArtworks.id));
2016
+ const pageOrders = artworks.map((a) => a.pageOrder);
2017
+ const hasDuplicates = pageOrders.length !== new Set(pageOrders).size;
2018
+ if (hasDuplicates || artworks.some((a) => a.pageOrder === null)) {
2019
+ console.log("\u68C0\u6D4B\u5230\u91CD\u590DpageOrder\uFF0C\u5148\u4FEE\u590D\u987A\u5E8F...");
2020
+ const fixUpdates = artworks.map((artwork, index3) => ({
2021
+ id: artwork.id,
2022
+ pageOrder: index3
2023
+ }));
2024
+ await this.updateArtworkOrder(collectionId, fixUpdates);
2025
+ const fixedArtworks = await db.select({
2026
+ id: comicUniverseArtworks.id,
2027
+ pageOrder: comicUniverseArtworks.pageOrder
2028
+ }).from(comicUniverseArtworks).where(and(
2029
+ eq(comicUniverseArtworks.collectionId, collectionId),
2030
+ eq(comicUniverseArtworks.isActive, true)
2031
+ )).orderBy(asc(comicUniverseArtworks.pageOrder));
2032
+ artworks.splice(0, artworks.length, ...fixedArtworks);
2033
+ }
2034
+ const currentIndex = artworks.findIndex((a) => a.id === artworkId);
2035
+ if (currentIndex === -1) {
2036
+ throw new Error("\u4F5C\u54C1\u4E0D\u5B58\u5728");
2037
+ }
2038
+ if (currentIndex === 0) {
2039
+ throw new Error("\u4F5C\u54C1\u5DF2\u7ECF\u5728\u6700\u524D\u9762");
2040
+ }
2041
+ const targetIndex = currentIndex - 1;
2042
+ const currentOrder = artworks[targetIndex].pageOrder;
2043
+ const targetOrder = artworks[currentIndex].pageOrder;
2044
+ const updates = [
2045
+ { id: artworks[currentIndex].id, pageOrder: currentOrder },
2046
+ { id: artworks[targetIndex].id, pageOrder: targetOrder }
2047
+ ];
2048
+ await this.updateArtworkOrder(collectionId, updates);
2049
+ } catch (error) {
2050
+ console.error("\u4E0A\u79FB\u4F5C\u54C1\u5931\u8D25:", error);
2051
+ throw error;
2052
+ }
2053
+ }
2054
+ // 下移作品
2055
+ async moveArtworkDown(collectionId, artworkId) {
2056
+ try {
2057
+ const artworks = await db.select({
2058
+ id: comicUniverseArtworks.id,
2059
+ pageOrder: comicUniverseArtworks.pageOrder
2060
+ }).from(comicUniverseArtworks).where(and(
2061
+ eq(comicUniverseArtworks.collectionId, collectionId),
2062
+ eq(comicUniverseArtworks.isActive, true)
2063
+ )).orderBy(asc(comicUniverseArtworks.pageOrder), asc(comicUniverseArtworks.id));
2064
+ const pageOrders = artworks.map((a) => a.pageOrder);
2065
+ const hasDuplicates = pageOrders.length !== new Set(pageOrders).size;
2066
+ if (hasDuplicates || artworks.some((a) => a.pageOrder === null)) {
2067
+ console.log("\u68C0\u6D4B\u5230\u91CD\u590DpageOrder\uFF0C\u5148\u4FEE\u590D\u987A\u5E8F...");
2068
+ const fixUpdates = artworks.map((artwork, index3) => ({
2069
+ id: artwork.id,
2070
+ pageOrder: index3
2071
+ }));
2072
+ await this.updateArtworkOrder(collectionId, fixUpdates);
2073
+ const fixedArtworks = await db.select({
2074
+ id: comicUniverseArtworks.id,
2075
+ pageOrder: comicUniverseArtworks.pageOrder
2076
+ }).from(comicUniverseArtworks).where(and(
2077
+ eq(comicUniverseArtworks.collectionId, collectionId),
2078
+ eq(comicUniverseArtworks.isActive, true)
2079
+ )).orderBy(asc(comicUniverseArtworks.pageOrder));
2080
+ artworks.splice(0, artworks.length, ...fixedArtworks);
2081
+ }
2082
+ console.log("\u4E0B\u79FB\u4F5C\u54C1\u540E\u7AEF\u8C03\u8BD5\u4FE1\u606F:", {
2083
+ collectionId,
2084
+ artworkId,
2085
+ totalArtworks: artworks.length,
2086
+ artworkOrders: artworks.map((a) => ({ id: a.id, pageOrder: a.pageOrder }))
2087
+ });
2088
+ const currentIndex = artworks.findIndex((a) => a.id === artworkId);
2089
+ if (currentIndex === -1) {
2090
+ throw new Error("\u4F5C\u54C1\u4E0D\u5B58\u5728");
2091
+ }
2092
+ if (currentIndex === artworks.length - 1) {
2093
+ throw new Error("\u4F5C\u54C1\u5DF2\u7ECF\u5728\u6700\u540E\u9762");
2094
+ }
2095
+ const targetIndex = currentIndex + 1;
2096
+ const currentOrder = artworks[targetIndex].pageOrder;
2097
+ const targetOrder = artworks[currentIndex].pageOrder;
2098
+ console.log("\u4E0B\u79FB\u4EA4\u6362\u4FE1\u606F:", {
2099
+ currentIndex,
2100
+ targetIndex,
2101
+ currentOrder,
2102
+ targetOrder,
2103
+ currentArtworkId: artworks[currentIndex].id,
2104
+ targetArtworkId: artworks[targetIndex].id
2105
+ });
2106
+ const updates = [
2107
+ { id: artworks[currentIndex].id, pageOrder: currentOrder },
2108
+ { id: artworks[targetIndex].id, pageOrder: targetOrder }
2109
+ ];
2110
+ await this.updateArtworkOrder(collectionId, updates);
2111
+ } catch (error) {
2112
+ console.error("\u4E0B\u79FB\u4F5C\u54C1\u5931\u8D25:", error);
2113
+ throw error;
2114
+ }
2115
+ }
2116
+ // 获取指定画集的所有作品(按顺序)
2117
+ async getArtworksByCollection(collectionId) {
2118
+ try {
2119
+ const artworks = await db.select({
2120
+ id: comicUniverseArtworks.id,
2121
+ title: comicUniverseArtworks.title,
2122
+ number: comicUniverseArtworks.number,
2123
+ fileId: comicUniverseArtworks.fileId,
2124
+ // 只查询fileId,不查询Base64图片
2125
+ description: comicUniverseArtworks.description,
2126
+ createdTime: comicUniverseArtworks.createdTime,
2127
+ theme: comicUniverseArtworks.theme,
2128
+ pageOrder: comicUniverseArtworks.pageOrder
2129
+ }).from(comicUniverseArtworks).where(and(
2130
+ eq(comicUniverseArtworks.collectionId, collectionId),
2131
+ eq(comicUniverseArtworks.isActive, true)
2132
+ )).orderBy(asc(comicUniverseArtworks.pageOrder), asc(comicUniverseArtworks.id));
2133
+ const pageOrders = artworks.map((a) => a.pageOrder);
2134
+ const hasDuplicates = pageOrders.length !== new Set(pageOrders).size;
2135
+ if (hasDuplicates || artworks.some((a) => a.pageOrder === null)) {
2136
+ console.log("\u68C0\u6D4B\u5230\u91CD\u590D\u6216\u7A7A\u7684pageOrder\uFF0C\u5F00\u59CB\u4FEE\u590D...");
2137
+ const updates = artworks.map((artwork, index3) => ({
2138
+ id: artwork.id,
2139
+ pageOrder: index3
2140
+ }));
2141
+ await this.updateArtworkOrder(collectionId, updates);
2142
+ const fixedArtworks = await db.select({
2143
+ id: comicUniverseArtworks.id,
2144
+ title: comicUniverseArtworks.title,
2145
+ number: comicUniverseArtworks.number,
2146
+ fileId: comicUniverseArtworks.fileId,
2147
+ // 只查询fileId,不查询Base64图片
2148
+ description: comicUniverseArtworks.description,
2149
+ createdTime: comicUniverseArtworks.createdTime,
2150
+ theme: comicUniverseArtworks.theme,
2151
+ pageOrder: comicUniverseArtworks.pageOrder
2152
+ }).from(comicUniverseArtworks).where(and(
2153
+ eq(comicUniverseArtworks.collectionId, collectionId),
2154
+ eq(comicUniverseArtworks.isActive, true)
2155
+ )).orderBy(asc(comicUniverseArtworks.pageOrder));
2156
+ return await this.buildArtworkPagesWithUrls(fixedArtworks, collectionId);
2157
+ }
2158
+ return await this.buildArtworkPagesWithUrls(artworks, collectionId);
2159
+ } catch (error) {
2160
+ console.error("\u83B7\u53D6\u4F5C\u54C1\u5217\u8868\u5931\u8D25:", error);
2161
+ throw error;
2162
+ }
2163
+ }
2164
+ // 构建作品页面数据,包含正确的图片URL
2165
+ async buildArtworkPagesWithUrls(artworks, collectionId) {
2166
+ const fileIdToUrlMap = /* @__PURE__ */ new Map();
2167
+ const artworksWithFileId = artworks.filter((artwork) => artwork.fileId);
2168
+ if (artworksWithFileId.length > 0) {
2169
+ try {
2170
+ const { getShowMasterpieceFileConfig: getShowMasterpieceFileConfig2 } = await import('./fileService-O3W6YXCI.mjs');
2171
+ const configManager = await getShowMasterpieceFileConfig2();
2172
+ const { UniversalFileService } = await import('./UniversalFileService-TNYKO6JN.mjs');
2173
+ const fileService = new UniversalFileService(configManager.getConfig());
2174
+ await fileService.initialize();
2175
+ const urlPromises = artworksWithFileId.map(async (artwork) => {
2176
+ try {
2177
+ const fileUrl = await fileService.getFileUrl(artwork.fileId);
2178
+ return { fileId: artwork.fileId, url: fileUrl };
2179
+ } catch (error) {
2180
+ console.warn(`\u26A0\uFE0F [ArtworksDbService] \u83B7\u53D6\u6587\u4EF6URL\u5931\u8D25: ${artwork.fileId}`, error);
2181
+ return { fileId: artwork.fileId, url: null };
2182
+ }
2183
+ });
2184
+ const urlResults = await Promise.all(urlPromises);
2185
+ urlResults.forEach((result) => {
2186
+ if (result.url) {
2187
+ fileIdToUrlMap.set(result.fileId, result.url);
2188
+ }
2189
+ });
2190
+ } catch (error) {
2191
+ console.warn("\u26A0\uFE0F [ArtworksDbService] \u6279\u91CF\u83B7\u53D6\u6587\u4EF6URL\u5931\u8D25:", error);
2192
+ }
2193
+ }
2194
+ return artworks.map((artwork) => {
2195
+ let imageUrl;
2196
+ if (artwork.fileId && fileIdToUrlMap.has(artwork.fileId)) {
2197
+ imageUrl = fileIdToUrlMap.get(artwork.fileId);
2198
+ } else {
2199
+ imageUrl = `/api/showmasterpiece/collections/${collectionId}/artworks/${artwork.id}/image`;
2200
+ }
2201
+ return {
2202
+ id: artwork.id,
2203
+ title: artwork.title,
2204
+ number: artwork.number,
2205
+ image: imageUrl,
2206
+ // 使用OSS URL或API路径
2207
+ fileId: artwork.fileId || void 0,
2208
+ description: artwork.description || "",
2209
+ createdTime: artwork.createdTime || "",
2210
+ theme: artwork.theme || "",
2211
+ pageOrder: artwork.pageOrder
2212
+ };
2213
+ });
2214
+ }
2215
+ };
2216
+ var masterpiecesConfigDbService = new MasterpiecesConfigDbService2();
2217
+ var categoriesDbService = new CategoriesDbService2();
2218
+ var tagsDbService = new TagsDbService2();
2219
+ var collectionsDbService = new CollectionsDbService();
2220
+ var artworksDbService = new ArtworksDbService(collectionsDbService);
2221
+ var BookingQueryService = class {
2222
+ constructor(db2) {
2223
+ this.db = db2;
2224
+ }
2225
+ buildAdminConditions(input) {
2226
+ const conditions = [];
2227
+ if (input.qqNumber) {
2228
+ conditions.push(like(comicUniverseBookings.qqNumber, `%${input.qqNumber}%`));
2229
+ }
2230
+ if (input.phoneNumber) {
2231
+ conditions.push(like(comicUniverseBookings.phoneNumber, `%${input.phoneNumber}%`));
2232
+ }
2233
+ if (input.status && input.status !== "all") {
2234
+ conditions.push(eq(comicUniverseBookings.status, input.status));
2235
+ }
2236
+ return conditions;
2237
+ }
2238
+ formatAdminBooking(booking) {
2239
+ return {
2240
+ id: booking.id,
2241
+ collectionId: booking.collectionId,
2242
+ qqNumber: booking.qqNumber,
2243
+ phoneNumber: booking.phoneNumber,
2244
+ quantity: booking.quantity,
2245
+ status: booking.status,
2246
+ notes: booking.notes,
2247
+ pickupMethod: booking.pickupMethod,
2248
+ adminNotes: booking.adminNotes,
2249
+ createdAt: booking.createdAt instanceof Date ? booking.createdAt.toISOString() : booking.createdAt,
2250
+ updatedAt: booking.updatedAt instanceof Date ? booking.updatedAt.toISOString() : booking.updatedAt,
2251
+ confirmedAt: booking.confirmedAt instanceof Date ? booking.confirmedAt.toISOString() : booking.confirmedAt,
2252
+ completedAt: booking.completedAt instanceof Date ? booking.completedAt.toISOString() : booking.completedAt,
2253
+ cancelledAt: booking.cancelledAt instanceof Date ? booking.cancelledAt.toISOString() : booking.cancelledAt,
2254
+ collection: {
2255
+ id: booking.collectionId,
2256
+ title: booking.collectionTitle || "\u672A\u77E5\u753B\u96C6",
2257
+ number: booking.collectionNumber || "\u672A\u77E5\u7F16\u53F7",
2258
+ coverImage: booking.collectionCoverImage || "",
2259
+ price: booking.collectionPrice || 0
2260
+ },
2261
+ totalPrice: (booking.collectionPrice || 0) * booking.quantity
2262
+ };
2263
+ }
2264
+ async getAdminBookings(input = {}) {
2265
+ const {
2266
+ qqNumber,
2267
+ phoneNumber,
2268
+ status,
2269
+ applyFiltersToStats = true
2270
+ } = input;
2271
+ const conditions = this.buildAdminConditions({ qqNumber, phoneNumber, status });
2272
+ const bookings = await this.db.select({
2273
+ id: comicUniverseBookings.id,
2274
+ collectionId: comicUniverseBookings.collectionId,
2275
+ qqNumber: comicUniverseBookings.qqNumber,
2276
+ phoneNumber: comicUniverseBookings.phoneNumber,
2277
+ quantity: comicUniverseBookings.quantity,
2278
+ status: comicUniverseBookings.status,
2279
+ notes: comicUniverseBookings.notes,
2280
+ pickupMethod: comicUniverseBookings.pickupMethod,
2281
+ adminNotes: comicUniverseBookings.adminNotes,
2282
+ createdAt: comicUniverseBookings.createdAt,
2283
+ updatedAt: comicUniverseBookings.updatedAt,
2284
+ confirmedAt: comicUniverseBookings.confirmedAt,
2285
+ completedAt: comicUniverseBookings.completedAt,
2286
+ cancelledAt: comicUniverseBookings.cancelledAt,
2287
+ collectionTitle: comicUniverseCollections.title,
2288
+ collectionNumber: comicUniverseCollections.number,
2289
+ collectionCoverImage: comicUniverseCollections.coverImage,
2290
+ collectionPrice: comicUniverseCollections.price
2291
+ }).from(comicUniverseBookings).leftJoin(comicUniverseCollections, eq(comicUniverseBookings.collectionId, comicUniverseCollections.id)).where(conditions.length > 0 ? and(...conditions) : void 0).orderBy(desc(comicUniverseBookings.createdAt));
2292
+ const baseStatsQuery = this.db.select({
2293
+ totalBookings: sql`count(*)`,
2294
+ pendingBookings: sql`count(*) filter (where ${comicUniverseBookings.status} = 'pending')`,
2295
+ confirmedBookings: sql`count(*) filter (where ${comicUniverseBookings.status} = 'confirmed')`,
2296
+ completedBookings: sql`count(*) filter (where ${comicUniverseBookings.status} = 'completed')`,
2297
+ cancelledBookings: sql`count(*) filter (where ${comicUniverseBookings.status} = 'cancelled')`,
2298
+ totalQuantity: sql`coalesce(sum(${comicUniverseBookings.quantity}), 0)`,
2299
+ totalRevenue: sql`coalesce(sum(${comicUniverseBookings.quantity} * coalesce(${comicUniverseCollections.price}, 0)), 0)`
2300
+ }).from(comicUniverseBookings).leftJoin(comicUniverseCollections, eq(comicUniverseBookings.collectionId, comicUniverseCollections.id));
2301
+ const stats = applyFiltersToStats && conditions.length > 0 ? await baseStatsQuery.where(and(...conditions)) : await baseStatsQuery;
2302
+ return {
2303
+ bookings: bookings.map((booking) => this.formatAdminBooking(booking)),
2304
+ stats: {
2305
+ totalBookings: stats[0]?.totalBookings || 0,
2306
+ pendingBookings: stats[0]?.pendingBookings || 0,
2307
+ confirmedBookings: stats[0]?.confirmedBookings || 0,
2308
+ completedBookings: stats[0]?.completedBookings || 0,
2309
+ cancelledBookings: stats[0]?.cancelledBookings || 0,
2310
+ totalQuantity: stats[0]?.totalQuantity || 0,
2311
+ totalRevenue: stats[0]?.totalRevenue || 0
2312
+ }
2313
+ };
2314
+ }
2315
+ async getBookableCollections(input = {}) {
2316
+ const conditions = [eq(comicUniverseCollections.isPublished, true)];
2317
+ if (input.categoryId) {
2318
+ conditions.push(eq(comicUniverseCollections.categoryId, input.categoryId));
2319
+ }
2320
+ let orderByClause;
2321
+ switch (input.orderBy) {
2322
+ case "createdAt":
2323
+ orderByClause = desc(comicUniverseCollections.createdAt);
2324
+ break;
2325
+ case "title":
2326
+ orderByClause = asc(comicUniverseCollections.title);
2327
+ break;
2328
+ case "displayOrder":
2329
+ default:
2330
+ orderByClause = asc(comicUniverseCollections.displayOrder);
2331
+ break;
2332
+ }
2333
+ const limit = Math.min(100, Math.max(1, input.limit ?? 50));
2334
+ const collections = await this.db.select({
2335
+ id: comicUniverseCollections.id,
2336
+ title: comicUniverseCollections.title,
2337
+ number: comicUniverseCollections.number,
2338
+ coverImage: comicUniverseCollections.coverImage,
2339
+ price: comicUniverseCollections.price,
2340
+ description: comicUniverseCollections.description,
2341
+ displayOrder: comicUniverseCollections.displayOrder,
2342
+ createdAt: comicUniverseCollections.createdAt
2343
+ }).from(comicUniverseCollections).where(and(...conditions)).orderBy(orderByClause).limit(limit);
2344
+ return collections.map((collection) => ({
2345
+ id: collection.id,
2346
+ title: collection.title,
2347
+ number: collection.number,
2348
+ coverImage: collection.coverImage,
2349
+ price: collection.price,
2350
+ description: collection.description,
2351
+ displayOrder: collection.displayOrder,
2352
+ createdAt: collection.createdAt?.toISOString()
2353
+ }));
2354
+ }
2355
+ async exportBookingsCsv() {
2356
+ const bookings = await this.db.select({
2357
+ id: comicUniverseBookings.id,
2358
+ qqNumber: comicUniverseBookings.qqNumber,
2359
+ phoneNumber: comicUniverseBookings.phoneNumber,
2360
+ collectionId: comicUniverseBookings.collectionId,
2361
+ status: comicUniverseBookings.status,
2362
+ quantity: comicUniverseBookings.quantity,
2363
+ notes: comicUniverseBookings.notes,
2364
+ pickupMethod: comicUniverseBookings.pickupMethod,
2365
+ adminNotes: comicUniverseBookings.adminNotes,
2366
+ createdAt: comicUniverseBookings.createdAt,
2367
+ updatedAt: comicUniverseBookings.updatedAt,
2368
+ confirmedAt: comicUniverseBookings.confirmedAt,
2369
+ completedAt: comicUniverseBookings.completedAt,
2370
+ cancelledAt: comicUniverseBookings.cancelledAt,
2371
+ collectionTitle: comicUniverseCollections.title,
2372
+ collectionNumber: comicUniverseCollections.number,
2373
+ collectionPrice: comicUniverseCollections.price
2374
+ }).from(comicUniverseBookings).leftJoin(comicUniverseCollections, eq(comicUniverseBookings.collectionId, comicUniverseCollections.id)).orderBy(comicUniverseBookings.createdAt);
2375
+ const headers = [
2376
+ "\u9884\u8BA2ID",
2377
+ "QQ\u53F7",
2378
+ "\u624B\u673A\u53F7",
2379
+ "\u753B\u96C6ID",
2380
+ "\u753B\u96C6\u6807\u9898",
2381
+ "\u753B\u96C6\u7F16\u53F7",
2382
+ "\u753B\u96C6\u4EF7\u683C",
2383
+ "\u9884\u8BA2\u72B6\u6001",
2384
+ "\u9884\u8BA2\u6570\u91CF",
2385
+ "\u7528\u6237\u5907\u6CE8",
2386
+ "\u9886\u53D6\u65B9\u5F0F",
2387
+ "\u7BA1\u7406\u5458\u5907\u6CE8",
2388
+ "\u521B\u5EFA\u65F6\u95F4",
2389
+ "\u66F4\u65B0\u65F6\u95F4",
2390
+ "\u786E\u8BA4\u65F6\u95F4",
2391
+ "\u5B8C\u6210\u65F6\u95F4",
2392
+ "\u53D6\u6D88\u65F6\u95F4"
2393
+ ];
2394
+ const statusMap = {
2395
+ pending: "\u5F85\u786E\u8BA4",
2396
+ confirmed: "\u5DF2\u786E\u8BA4",
2397
+ completed: "\u5DF2\u5B8C\u6210",
2398
+ cancelled: "\u5DF2\u53D6\u6D88"
2399
+ };
2400
+ const formatTime = (value) => {
2401
+ if (!value) return "";
2402
+ return new Date(value).toLocaleString("zh-CN");
2403
+ };
2404
+ const rows = bookings.map((booking) => [
2405
+ booking.id,
2406
+ booking.qqNumber || "",
2407
+ booking.phoneNumber || "",
2408
+ booking.collectionId,
2409
+ booking.collectionTitle || "",
2410
+ booking.collectionNumber || "",
2411
+ booking.collectionPrice || "",
2412
+ statusMap[booking.status] || booking.status,
2413
+ booking.quantity,
2414
+ booking.notes || "",
2415
+ booking.pickupMethod || "",
2416
+ booking.adminNotes || "",
2417
+ formatTime(booking.createdAt),
2418
+ formatTime(booking.updatedAt),
2419
+ formatTime(booking.confirmedAt),
2420
+ formatTime(booking.completedAt),
2421
+ formatTime(booking.cancelledAt)
2422
+ ]);
2423
+ const BOM = "\uFEFF";
2424
+ return BOM + headers.join(",") + "\n" + rows.map(
2425
+ (row) => row.map((cell) => {
2426
+ const cellStr = String(cell || "");
2427
+ if (cellStr.includes(",") || cellStr.includes('"') || cellStr.includes("\n")) {
2428
+ return `"${cellStr.replace(/"/g, '""')}"`;
2429
+ }
2430
+ return cellStr;
2431
+ }).join(",")
2432
+ ).join("\n");
2433
+ }
2434
+ async getBookingsList(input = {}) {
2435
+ const conditions = [];
2436
+ if (input.collectionId) {
2437
+ conditions.push(eq(comicUniverseBookings.collectionId, input.collectionId));
2438
+ }
2439
+ if (input.qqNumber) {
2440
+ conditions.push(like(comicUniverseBookings.qqNumber, `%${input.qqNumber}%`));
2441
+ }
2442
+ if (input.phoneNumber) {
2443
+ conditions.push(like(comicUniverseBookings.phoneNumber, `%${input.phoneNumber}%`));
2444
+ }
2445
+ if (input.status) {
2446
+ conditions.push(eq(comicUniverseBookings.status, input.status));
2447
+ }
2448
+ const page = Math.max(1, input.page || 1);
2449
+ const limit = Math.min(100, Math.max(1, input.limit || 20));
2450
+ const offset = (page - 1) * limit;
2451
+ const totalResult = await this.db.select({ count: sql`count(*)` }).from(comicUniverseBookings).where(conditions.length > 0 ? and(...conditions) : void 0);
2452
+ const total = totalResult[0]?.count || 0;
2453
+ const bookings = await this.db.select({
2454
+ id: comicUniverseBookings.id,
2455
+ collectionId: comicUniverseBookings.collectionId,
2456
+ qqNumber: comicUniverseBookings.qqNumber,
2457
+ phoneNumber: comicUniverseBookings.phoneNumber,
2458
+ quantity: comicUniverseBookings.quantity,
2459
+ status: comicUniverseBookings.status,
2460
+ notes: comicUniverseBookings.notes,
2461
+ adminNotes: comicUniverseBookings.adminNotes,
2462
+ createdAt: comicUniverseBookings.createdAt,
2463
+ updatedAt: comicUniverseBookings.updatedAt,
2464
+ confirmedAt: comicUniverseBookings.confirmedAt,
2465
+ completedAt: comicUniverseBookings.completedAt,
2466
+ cancelledAt: comicUniverseBookings.cancelledAt,
2467
+ collectionTitle: comicUniverseCollections.title,
2468
+ collectionNumber: comicUniverseCollections.number,
2469
+ collectionCoverImage: comicUniverseCollections.coverImage,
2470
+ collectionPrice: comicUniverseCollections.price
2471
+ }).from(comicUniverseBookings).leftJoin(comicUniverseCollections, eq(comicUniverseBookings.collectionId, comicUniverseCollections.id)).where(conditions.length > 0 ? and(...conditions) : void 0).orderBy(desc(comicUniverseBookings.createdAt)).limit(limit).offset(offset);
2472
+ return {
2473
+ bookings: bookings.map((booking) => ({
2474
+ id: booking.id,
2475
+ collectionId: booking.collectionId,
2476
+ qqNumber: booking.qqNumber,
2477
+ phoneNumber: booking.phoneNumber,
2478
+ quantity: booking.quantity,
2479
+ status: booking.status,
2480
+ notes: booking.notes,
2481
+ adminNotes: booking.adminNotes,
2482
+ createdAt: booking.createdAt?.toISOString(),
2483
+ updatedAt: booking.updatedAt?.toISOString(),
2484
+ confirmedAt: booking.confirmedAt?.toISOString(),
2485
+ completedAt: booking.completedAt?.toISOString(),
2486
+ cancelledAt: booking.cancelledAt?.toISOString(),
2487
+ collection: {
2488
+ id: booking.collectionId,
2489
+ title: booking.collectionTitle,
2490
+ number: booking.collectionNumber,
2491
+ coverImage: booking.collectionCoverImage,
2492
+ price: booking.collectionPrice
2493
+ }
2494
+ })),
2495
+ total,
2496
+ page,
2497
+ limit,
2498
+ totalPages: Math.ceil(total / limit)
2499
+ };
2500
+ }
2501
+ async getBookingById(id) {
2502
+ const bookings = await this.db.select({
2503
+ id: comicUniverseBookings.id,
2504
+ collectionId: comicUniverseBookings.collectionId,
2505
+ qqNumber: comicUniverseBookings.qqNumber,
2506
+ phoneNumber: comicUniverseBookings.phoneNumber,
2507
+ quantity: comicUniverseBookings.quantity,
2508
+ status: comicUniverseBookings.status,
2509
+ notes: comicUniverseBookings.notes,
2510
+ adminNotes: comicUniverseBookings.adminNotes,
2511
+ createdAt: comicUniverseBookings.createdAt,
2512
+ updatedAt: comicUniverseBookings.updatedAt,
2513
+ confirmedAt: comicUniverseBookings.confirmedAt,
2514
+ completedAt: comicUniverseBookings.completedAt,
2515
+ cancelledAt: comicUniverseBookings.cancelledAt,
2516
+ collectionTitle: comicUniverseCollections.title,
2517
+ collectionNumber: comicUniverseCollections.number,
2518
+ collectionCoverImage: comicUniverseCollections.coverImage,
2519
+ collectionPrice: comicUniverseCollections.price,
2520
+ collectionDescription: comicUniverseCollections.description
2521
+ }).from(comicUniverseBookings).leftJoin(comicUniverseCollections, eq(comicUniverseBookings.collectionId, comicUniverseCollections.id)).where(eq(comicUniverseBookings.id, id)).limit(1);
2522
+ if (bookings.length === 0) {
2523
+ return null;
2524
+ }
2525
+ const booking = bookings[0];
2526
+ return {
2527
+ id: booking.id,
2528
+ collectionId: booking.collectionId,
2529
+ qqNumber: booking.qqNumber,
2530
+ phoneNumber: booking.phoneNumber,
2531
+ quantity: booking.quantity,
2532
+ status: booking.status,
2533
+ notes: booking.notes,
2534
+ adminNotes: booking.adminNotes,
2535
+ createdAt: booking.createdAt?.toISOString(),
2536
+ updatedAt: booking.updatedAt?.toISOString(),
2537
+ confirmedAt: booking.confirmedAt?.toISOString(),
2538
+ completedAt: booking.completedAt?.toISOString(),
2539
+ cancelledAt: booking.cancelledAt?.toISOString(),
2540
+ collection: {
2541
+ id: booking.collectionId,
2542
+ title: booking.collectionTitle,
2543
+ number: booking.collectionNumber,
2544
+ coverImage: booking.collectionCoverImage,
2545
+ price: booking.collectionPrice,
2546
+ description: booking.collectionDescription
2547
+ }
2548
+ };
2549
+ }
2550
+ };
2551
+ function createBookingQueryService(db2) {
2552
+ return new BookingQueryService(db2);
2553
+ }
2554
+ var BookingCommandError = class extends Error {
2555
+ constructor(code, message) {
2556
+ super(message);
2557
+ this.code = code;
2558
+ this.name = "BookingCommandError";
2559
+ }
2560
+ };
2561
+ var BookingCommandService = class {
2562
+ constructor(db2) {
2563
+ this.db = db2;
2564
+ }
2565
+ async ensureBookingExists(id) {
2566
+ const existingBooking = await this.db.select({ id: comicUniverseBookings.id }).from(comicUniverseBookings).where(eq(comicUniverseBookings.id, id)).limit(1);
2567
+ if (existingBooking.length === 0) {
2568
+ throw new BookingCommandError("BOOKING_NOT_FOUND", "\u9884\u8BA2\u4E0D\u5B58\u5728");
2569
+ }
2570
+ }
2571
+ async ensureCollectionExists(collectionId) {
2572
+ const collection = await this.db.select({ id: comicUniverseCollections.id }).from(comicUniverseCollections).where(eq(comicUniverseCollections.id, collectionId)).limit(1);
2573
+ if (collection.length === 0) {
2574
+ throw new BookingCommandError("COLLECTION_NOT_FOUND", "\u753B\u96C6\u4E0D\u5B58\u5728");
2575
+ }
2576
+ }
2577
+ validatePhone(phoneNumber) {
2578
+ const phoneRegex = /^1[3-9]\d{9}$/;
2579
+ if (!phoneRegex.test(phoneNumber)) {
2580
+ throw new BookingCommandError("INVALID_PHONE", "\u624B\u673A\u53F7\u683C\u5F0F\u4E0D\u6B63\u786E");
2581
+ }
2582
+ }
2583
+ validateQq(qqNumber) {
2584
+ const qqRegex = /^\d{5,11}$/;
2585
+ if (!qqRegex.test(qqNumber)) {
2586
+ throw new BookingCommandError("INVALID_QQ", "QQ\u53F7\u683C\u5F0F\u4E0D\u6B63\u786E");
2587
+ }
2588
+ }
2589
+ async createBooking(body) {
2590
+ if (!body.collectionId || !body.qqNumber || !body.phoneNumber || !body.quantity) {
2591
+ throw new BookingCommandError("INVALID_PAYLOAD", "\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570\uFF1A\u753B\u96C6ID\u3001QQ\u53F7\u3001\u624B\u673A\u53F7\u3001\u9884\u8BA2\u6570\u91CF");
2592
+ }
2593
+ this.validatePhone(body.phoneNumber);
2594
+ this.validateQq(body.qqNumber);
2595
+ if (body.quantity < 1) {
2596
+ throw new BookingCommandError("INVALID_QUANTITY", "\u9884\u8BA2\u6570\u91CF\u5FC5\u987B\u5927\u4E8E0");
2597
+ }
2598
+ await this.ensureCollectionExists(body.collectionId);
2599
+ const existingBooking = await this.db.select({
2600
+ id: comicUniverseBookings.id,
2601
+ quantity: comicUniverseBookings.quantity,
2602
+ notes: comicUniverseBookings.notes
2603
+ }).from(comicUniverseBookings).where(
2604
+ and(
2605
+ eq(comicUniverseBookings.qqNumber, body.qqNumber),
2606
+ eq(comicUniverseBookings.phoneNumber, body.phoneNumber),
2607
+ eq(comicUniverseBookings.collectionId, body.collectionId)
2608
+ )
2609
+ ).limit(1);
2610
+ let resultBooking;
2611
+ if (existingBooking.length > 0) {
2612
+ const existing = existingBooking[0];
2613
+ const newQuantity = existing.quantity + body.quantity;
2614
+ const combinedNotes = existing.notes ? `${existing.notes}; \u65B0\u589E\u9884\u8BA2: ${body.notes || "\u65E0\u5907\u6CE8"}` : body.notes || null;
2615
+ const [updatedBooking] = await this.db.update(comicUniverseBookings).set({
2616
+ quantity: newQuantity,
2617
+ notes: combinedNotes,
2618
+ updatedAt: /* @__PURE__ */ new Date()
2619
+ }).where(
2620
+ and(
2621
+ eq(comicUniverseBookings.qqNumber, body.qqNumber),
2622
+ eq(comicUniverseBookings.phoneNumber, body.phoneNumber),
2623
+ eq(comicUniverseBookings.collectionId, body.collectionId)
2624
+ )
2625
+ ).returning();
2626
+ resultBooking = updatedBooking;
2627
+ } else {
2628
+ const [newBooking] = await this.db.insert(comicUniverseBookings).values({
2629
+ collectionId: body.collectionId,
2630
+ qqNumber: body.qqNumber,
2631
+ phoneNumber: body.phoneNumber,
2632
+ quantity: body.quantity,
2633
+ notes: body.notes || null,
2634
+ status: "pending"
2635
+ }).returning();
2636
+ resultBooking = newBooking;
2637
+ }
2638
+ return {
2639
+ id: resultBooking.id,
2640
+ collectionId: resultBooking.collectionId,
2641
+ qqNumber: resultBooking.qqNumber,
2642
+ phoneNumber: resultBooking.phoneNumber,
2643
+ quantity: resultBooking.quantity,
2644
+ status: resultBooking.status,
2645
+ notes: resultBooking.notes,
2646
+ createdAt: resultBooking.createdAt instanceof Date ? resultBooking.createdAt.toISOString() : resultBooking.createdAt,
2647
+ updatedAt: resultBooking.updatedAt instanceof Date ? resultBooking.updatedAt.toISOString() : resultBooking.updatedAt
2648
+ };
2649
+ }
2650
+ async batchCreateBookings(body) {
2651
+ const { qqNumber, phoneNumber, items, notes, pickupMethod } = body;
2652
+ if (!qqNumber || !phoneNumber || !Array.isArray(items) || items.length === 0) {
2653
+ throw new BookingCommandError("INVALID_PAYLOAD", "\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570\uFF1AQQ\u53F7\u3001\u624B\u673A\u53F7\u3001\u9884\u8BA2\u9879\u5217\u8868");
2654
+ }
2655
+ this.validateQq(qqNumber);
2656
+ this.validatePhone(phoneNumber);
2657
+ const bookingIds = [];
2658
+ const failures = [];
2659
+ let successCount = 0;
2660
+ let failCount = 0;
2661
+ for (const item of items) {
2662
+ try {
2663
+ if (!item.collectionId || !item.quantity) {
2664
+ failures.push({ collectionId: item.collectionId, reason: "\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570" });
2665
+ failCount++;
2666
+ continue;
2667
+ }
2668
+ if (item.quantity < 1) {
2669
+ failures.push({ collectionId: item.collectionId, reason: "\u9884\u8BA2\u6570\u91CF\u5FC5\u987B\u5927\u4E8E0" });
2670
+ failCount++;
2671
+ continue;
2672
+ }
2673
+ await this.ensureCollectionExists(item.collectionId);
2674
+ const [newBooking] = await this.db.insert(comicUniverseBookings).values({
2675
+ collectionId: item.collectionId,
2676
+ qqNumber,
2677
+ phoneNumber,
2678
+ quantity: item.quantity,
2679
+ notes: notes || null,
2680
+ pickupMethod: pickupMethod || null,
2681
+ status: "pending"
2682
+ }).returning();
2683
+ bookingIds.push(newBooking.id);
2684
+ successCount++;
2685
+ } catch {
2686
+ failures.push({ collectionId: item.collectionId, reason: "\u521B\u5EFA\u9884\u8BA2\u5931\u8D25" });
2687
+ failCount++;
2688
+ }
2689
+ }
2690
+ return {
2691
+ bookingIds,
2692
+ successCount,
2693
+ failCount,
2694
+ failures: failures.length > 0 ? failures : void 0
2695
+ };
2696
+ }
2697
+ async updateBooking(id, body) {
2698
+ await this.ensureBookingExists(id);
2699
+ const updateData = {
2700
+ updatedAt: /* @__PURE__ */ new Date()
2701
+ };
2702
+ if (body.status) {
2703
+ updateData.status = body.status;
2704
+ switch (body.status) {
2705
+ case "confirmed":
2706
+ updateData.confirmedAt = /* @__PURE__ */ new Date();
2707
+ break;
2708
+ case "completed":
2709
+ updateData.completedAt = /* @__PURE__ */ new Date();
2710
+ break;
2711
+ case "cancelled":
2712
+ updateData.cancelledAt = /* @__PURE__ */ new Date();
2713
+ break;
2714
+ }
2715
+ }
2716
+ if (body.adminNotes !== void 0) {
2717
+ updateData.adminNotes = body.adminNotes;
2718
+ }
2719
+ const [updatedBooking] = await this.db.update(comicUniverseBookings).set(updateData).where(eq(comicUniverseBookings.id, id)).returning();
2720
+ return {
2721
+ id: updatedBooking.id,
2722
+ collectionId: updatedBooking.collectionId,
2723
+ qqNumber: updatedBooking.qqNumber,
2724
+ quantity: updatedBooking.quantity,
2725
+ status: updatedBooking.status,
2726
+ notes: updatedBooking.notes,
2727
+ adminNotes: updatedBooking.adminNotes,
2728
+ createdAt: updatedBooking.createdAt?.toISOString(),
2729
+ updatedAt: updatedBooking.updatedAt?.toISOString(),
2730
+ confirmedAt: updatedBooking.confirmedAt?.toISOString(),
2731
+ completedAt: updatedBooking.completedAt?.toISOString(),
2732
+ cancelledAt: updatedBooking.cancelledAt?.toISOString()
2733
+ };
2734
+ }
2735
+ async updateBookingStatus(id, status, adminNotes) {
2736
+ if (!status || !["pending", "confirmed", "completed", "cancelled"].includes(status)) {
2737
+ throw new BookingCommandError("INVALID_STATUS", "\u65E0\u6548\u7684\u9884\u8BA2\u72B6\u6001");
2738
+ }
2739
+ await this.ensureBookingExists(id);
2740
+ const updateData = {
2741
+ status,
2742
+ updatedAt: /* @__PURE__ */ new Date()
2743
+ };
2744
+ if (adminNotes !== void 0) {
2745
+ updateData.adminNotes = adminNotes;
2746
+ }
2747
+ switch (status) {
2748
+ case "confirmed":
2749
+ updateData.confirmedAt = /* @__PURE__ */ new Date();
2750
+ break;
2751
+ case "completed":
2752
+ updateData.completedAt = /* @__PURE__ */ new Date();
2753
+ break;
2754
+ case "cancelled":
2755
+ updateData.cancelledAt = /* @__PURE__ */ new Date();
2756
+ break;
2757
+ }
2758
+ const updatedBookings = await this.db.update(comicUniverseBookings).set(updateData).where(eq(comicUniverseBookings.id, id)).returning();
2759
+ return updatedBookings[0];
2760
+ }
2761
+ async deleteBooking(id) {
2762
+ await this.ensureBookingExists(id);
2763
+ await this.db.delete(comicUniverseBookings).where(eq(comicUniverseBookings.id, id));
2764
+ }
2765
+ };
2766
+ function createBookingCommandService(db2) {
2767
+ return new BookingCommandService(db2);
2768
+ }
2769
+
2770
+ export { BookingCommandError, BookingCommandService, BookingQueryService, CategoriesDbService, MasterpiecesConfigDbService, PopupConfigService, ShowmasterConfigService, TagsDbService, artworksDbService, categoriesDbService, collectionsDbService, comicUniverseArtworks, comicUniverseArtworksRelations, comicUniverseBookings, comicUniverseBookingsRelations, comicUniverseCategories, comicUniverseCategoriesRelations, comicUniverseCollectionTags, comicUniverseCollectionTagsRelations, comicUniverseCollections, comicUniverseCollectionsRelations, comicUniverseConfigs, comicUniverseConfigsRelations, comicUniverseTags, comicUniverseTagsRelations, createBookingCommandService, createBookingQueryService, createCategoriesDbService, createMasterpiecesConfigDbService, createPopupConfigService, createShowmasterConfigService, createTagsDbService, initializeShowmasterpieceDb, masterpiecesConfigDbService, popupConfigs, popupConfigsRelations, showmasterConfigCategories, showmasterConfigHistory, showmasterConfigItems, showmasterConfigPermissions, tagsDbService };
2771
+ //# sourceMappingURL=chunk-VBQFVXOW.mjs.map
2772
+ //# sourceMappingURL=chunk-VBQFVXOW.mjs.map