seed-admin.ts 32 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330
  1. // prisma/mongo/seed-admin.ts
  2. import { PrismaClient, MenuType, AdType } from '@prisma/mongo/client';
  3. import type { Prisma } from '@prisma/mongo/client';
  4. const prisma = new PrismaClient();
  5. type ImgSource = 'LOCAL_ONLY' | 'S3_ONLY' | 'S3_AND_LOCAL' | 'PROVIDER';
  6. async function seedSysConfig(): Promise<void> {
  7. const nowSec = Math.floor(Date.now() / 1000);
  8. // Equivalent to:
  9. // db.sysConfig.updateOne({ _id: -1 }, { $set: {...} }, { upsert: true })
  10. await prisma.$runCommandRaw({
  11. update: 'sysConfig',
  12. updates: [
  13. {
  14. q: { _id: -1 },
  15. u: {
  16. $set: {
  17. game: { apiUrl: 'http://119.28.182.132:83', token: '' },
  18. imchat: { apiUrl: 'http://172.17.0.1:88', token: '' },
  19. mgnt: { apiUrl: 'http://172.17.0.1:83', token: 'nyqFLchjstAR' },
  20. partner: {
  21. baseUrl: 'https://wwapi.hxc1t.com',
  22. signSecret: 'Z3VhbmNpbmV3ZWl4aWFvMTIzNDU2',
  23. md5Key:
  24. '160360904be3dd23bf4f1278a74196efdbf3f9b834ce883ef6ae09eb05c5c652',
  25. itemsLimit: 100,
  26. endpoints: {
  27. orderAdd: '/open/open/order/add',
  28. orderUpdateStatus: '/open/open/order/updateStatus',
  29. chatAdd: '/open/order/chat/send',
  30. },
  31. },
  32. upload: {
  33. s3Enabled: true,
  34. storageStrategy: 'LOCAL_ONLY' as ImgSource,
  35. local: {
  36. rootPath: '/opt/app/node/ww-images',
  37. baseUrl: 'https://man.boxt3yk.com/images',
  38. chatUpload: 'https://man.boxt3yk.com/api/chat/upload',
  39. },
  40. limitsMb: { image: 10, video: 100 },
  41. s3: {
  42. accessKeyId: 'AKIA6GSNGR5PISMIKCJ4',
  43. secretAccessKey: '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',
  44. bucket: 'mybucket-imgs',
  45. region: 'ap-east-1',
  46. endpointUrl: 'https://s3.ap-east-1.amazonaws.com',
  47. imageBaseUrl:
  48. 'https://s3.ap-east-1.amazonaws.com/mybucket-imgs',
  49. },
  50. },
  51. updatedAt: nowSec,
  52. },
  53. },
  54. upsert: true,
  55. multi: false,
  56. },
  57. ],
  58. });
  59. }
  60. // =============================================================================
  61. // MENU SEEDS DATA
  62. // =============================================================================
  63. interface SeedMenu {
  64. legacyId: number;
  65. legacyParentId: number | null;
  66. title: string;
  67. type: MenuType;
  68. name: string;
  69. path: string;
  70. icon: string | null;
  71. componentKey?: string | null;
  72. order: number;
  73. meta?: Prisma.JsonValue;
  74. }
  75. const MENU_SEEDS: SeedMenu[] = [
  76. // ======================
  77. // 营销中心 (Marketing)
  78. // ======================
  79. {
  80. legacyId: 1,
  81. legacyParentId: null,
  82. title: '营销中心',
  83. type: 'DIRECTORY',
  84. name: 'Marketing Management',
  85. path: '/marketing',
  86. icon: 'i-carbon:application-web',
  87. order: 100,
  88. meta: {
  89. title: '营销中心',
  90. i18n: 'route.general.root',
  91. icon: 'i-carbon:application-web',
  92. },
  93. },
  94. {
  95. legacyId: 2,
  96. legacyParentId: 1,
  97. title: '视频管理',
  98. type: 'MENU',
  99. name: 'videoList',
  100. path: '/marketing/video',
  101. icon: 'i-carbon:video',
  102. order: 1,
  103. meta: {
  104. title: '视频管理',
  105. i18n: 'route.general.video.root',
  106. icon: 'i-carbon:video',
  107. },
  108. },
  109. {
  110. legacyId: 3,
  111. legacyParentId: 2,
  112. title: '视频列表',
  113. type: 'SUBMENU',
  114. name: 'videoList',
  115. path: '/marketing/videoList',
  116. icon: null,
  117. componentKey: '@/views/marketing_center/video_mgnt/list.vue',
  118. order: 1,
  119. meta: {
  120. title: '视频列表',
  121. i18n: 'route.general.video.list',
  122. sidebar: false,
  123. breadcrumb: false,
  124. cache: ['videoCreate', 'videoEdit'],
  125. },
  126. },
  127. {
  128. legacyId: 4,
  129. legacyParentId: 2,
  130. title: '新增视频',
  131. type: 'SUBMENU',
  132. name: 'videoCreate',
  133. path: '/marketing/video/detail',
  134. icon: null,
  135. componentKey: '@/views/marketing_center/video_mgnt/detail.vue',
  136. order: 2,
  137. meta: {
  138. title: '新增视频',
  139. i18n: 'route.general.video.create',
  140. sidebar: false,
  141. activeMenu: '/marketing/videoList',
  142. cache: true,
  143. noCache: 'videoList',
  144. },
  145. },
  146. {
  147. legacyId: 5,
  148. legacyParentId: 2,
  149. title: '编辑视频',
  150. type: 'SUBMENU',
  151. name: 'videoEdit',
  152. path: '/marketing/video/detail/:id',
  153. icon: null,
  154. componentKey: '@/views/marketing_center/video_mgnt/detail.vue',
  155. order: 3,
  156. meta: {
  157. title: '编辑视频',
  158. i18n: 'route.general.video.edit',
  159. sidebar: false,
  160. activeMenu: '/marketing/videoList',
  161. cache: true,
  162. noCache: 'videoList',
  163. },
  164. },
  165. {
  166. legacyId: 6,
  167. legacyParentId: 1,
  168. title: '分类管理',
  169. type: 'MENU',
  170. name: 'categoryList',
  171. path: '/marketing/category',
  172. icon: 'i-carbon:category',
  173. order: 2,
  174. meta: {
  175. title: '分类管理',
  176. i18n: 'route.general.category.root',
  177. icon: 'i-carbon:category',
  178. },
  179. },
  180. {
  181. legacyId: 7,
  182. legacyParentId: 6,
  183. title: '分类列表',
  184. type: 'SUBMENU',
  185. name: 'categoryList',
  186. path: '/marketing/categoryList',
  187. icon: null,
  188. componentKey: '@/views/marketing_center/category_mgnt/list.vue',
  189. order: 1,
  190. meta: {
  191. title: '分类列表',
  192. i18n: 'route.general.category.list',
  193. sidebar: false,
  194. breadcrumb: false,
  195. cache: ['categoryCreate', 'categoryEdit'],
  196. },
  197. },
  198. {
  199. legacyId: 8,
  200. legacyParentId: 6,
  201. title: '新增分类',
  202. type: 'SUBMENU',
  203. name: 'categoryCreate',
  204. path: '/marketing/category/detail',
  205. icon: null,
  206. componentKey: '@/views/marketing_center/category_mgnt/detail.vue',
  207. order: 2,
  208. meta: {
  209. title: '新增分类',
  210. i18n: 'route.general.category.create',
  211. sidebar: false,
  212. activeMenu: '/marketing/categoryList',
  213. cache: true,
  214. noCache: 'categoryList',
  215. },
  216. },
  217. {
  218. legacyId: 9,
  219. legacyParentId: 6,
  220. title: '编辑分类',
  221. type: 'SUBMENU',
  222. name: 'categoryEdit',
  223. path: '/marketing/category/detail/:id',
  224. icon: null,
  225. componentKey: '@/views/marketing_center/category_mgnt/detail.vue',
  226. order: 3,
  227. meta: {
  228. title: '编辑分类',
  229. i18n: 'route.general.category.edit',
  230. sidebar: false,
  231. activeMenu: '/marketing/categoryList',
  232. cache: true,
  233. noCache: 'categoryList',
  234. },
  235. },
  236. {
  237. legacyId: 10,
  238. legacyParentId: 1,
  239. title: '标签管理',
  240. type: 'MENU',
  241. name: 'tagList',
  242. path: '/marketing/tag',
  243. icon: 'i-carbon:tag',
  244. order: 3,
  245. meta: {
  246. title: '标签管理',
  247. i18n: 'route.general.tag.root',
  248. icon: 'i-carbon:tag',
  249. },
  250. },
  251. {
  252. legacyId: 11,
  253. legacyParentId: 10,
  254. title: '标签列表',
  255. type: 'SUBMENU',
  256. name: 'tagList',
  257. path: '/marketing/tagList',
  258. icon: null,
  259. componentKey: '@/views/marketing_center/tag_mgnt/list.vue',
  260. order: 1,
  261. meta: {
  262. title: '标签列表',
  263. i18n: 'route.general.tag.list',
  264. sidebar: false,
  265. breadcrumb: false,
  266. cache: ['tagCreate', 'tagEdit'],
  267. },
  268. },
  269. {
  270. legacyId: 12,
  271. legacyParentId: 10,
  272. title: '新增标签',
  273. type: 'SUBMENU',
  274. name: 'tagCreate',
  275. path: '/marketing/tag/detail',
  276. icon: null,
  277. componentKey: '@/views/marketing_center/tag_mgnt/detail.vue',
  278. order: 2,
  279. meta: {
  280. title: '新增标签',
  281. i18n: 'route.general.tag.create',
  282. sidebar: false,
  283. activeMenu: '/marketing/tagList',
  284. cache: true,
  285. noCache: 'tagList',
  286. },
  287. },
  288. {
  289. legacyId: 13,
  290. legacyParentId: 10,
  291. title: '编辑标签',
  292. type: 'SUBMENU',
  293. name: 'tagEdit',
  294. path: '/marketing/tag/detail/:id',
  295. icon: null,
  296. componentKey: '@/views/marketing_center/tag_mgnt/detail.vue',
  297. order: 3,
  298. meta: {
  299. title: '编辑标签',
  300. i18n: 'route.general.tag.edit',
  301. sidebar: false,
  302. activeMenu: '/marketing/tagList',
  303. cache: true,
  304. noCache: 'tagList',
  305. },
  306. },
  307. {
  308. legacyId: 14,
  309. legacyParentId: 1,
  310. title: '广告管理',
  311. type: 'MENU',
  312. name: 'adsList',
  313. path: '/marketing/ads',
  314. icon: 'i-carbon:image-copy',
  315. order: 4,
  316. meta: {
  317. title: '广告管理',
  318. i18n: 'route.general.ads.root',
  319. icon: 'i-carbon:image-copy',
  320. },
  321. },
  322. {
  323. legacyId: 15,
  324. legacyParentId: 14,
  325. title: '广告列表',
  326. type: 'SUBMENU',
  327. name: 'adsList',
  328. path: '/marketing/adsList',
  329. icon: null,
  330. componentKey: '@/views/marketing_center/ads_mgnt/list.vue',
  331. order: 1,
  332. meta: {
  333. title: '广告列表',
  334. i18n: 'route.general.ads.list',
  335. sidebar: false,
  336. breadcrumb: false,
  337. cache: ['adsCreate', 'adsEdit'],
  338. },
  339. },
  340. {
  341. legacyId: 16,
  342. legacyParentId: 14,
  343. title: '新增广告',
  344. type: 'SUBMENU',
  345. name: 'adsCreate',
  346. path: '/marketing/ads/detail',
  347. icon: null,
  348. componentKey: '@/views/marketing_center/ads_mgnt/detail.vue',
  349. order: 2,
  350. meta: {
  351. title: '新增广告',
  352. i18n: 'route.general.ads.create',
  353. sidebar: false,
  354. activeMenu: '/marketing/adsList',
  355. cache: true,
  356. noCache: 'adsList',
  357. },
  358. },
  359. {
  360. legacyId: 17,
  361. legacyParentId: 14,
  362. title: '编辑广告',
  363. type: 'SUBMENU',
  364. name: 'adsEdit',
  365. path: '/marketing/ads/detail/:id',
  366. icon: null,
  367. componentKey: '@/views/marketing_center/ads_mgnt/detail.vue',
  368. order: 3,
  369. meta: {
  370. title: '编辑广告',
  371. i18n: 'route.general.ads.edit',
  372. sidebar: false,
  373. activeMenu: '/marketing/adsList',
  374. cache: true,
  375. noCache: 'adsList',
  376. },
  377. },
  378. {
  379. legacyId: 18,
  380. legacyParentId: 1,
  381. title: '参数管理',
  382. type: 'MENU',
  383. name: 'paramList',
  384. path: '/marketing/param',
  385. icon: 'i-carbon:settings',
  386. order: 5,
  387. meta: {
  388. title: '参数管理',
  389. i18n: 'route.general.param.root',
  390. icon: 'i-carbon:settings',
  391. },
  392. },
  393. {
  394. legacyId: 19,
  395. legacyParentId: 18,
  396. title: '参数列表',
  397. type: 'SUBMENU',
  398. name: 'paramList',
  399. path: '/marketing/paramList',
  400. icon: null,
  401. componentKey: '@/views/marketing_center/param_mgnt/list.vue',
  402. order: 1,
  403. meta: {
  404. title: '参数列表',
  405. i18n: 'route.general.param.list',
  406. sidebar: false,
  407. breadcrumb: false,
  408. cache: ['paramCreate', 'paramEdit'],
  409. },
  410. },
  411. {
  412. legacyId: 20,
  413. legacyParentId: 18,
  414. title: '新增参数',
  415. type: 'SUBMENU',
  416. name: 'paramCreate',
  417. path: '/marketing/param/detail',
  418. icon: null,
  419. componentKey: '@/views/marketing_center/param_mgnt/detail.vue',
  420. order: 2,
  421. meta: {
  422. title: '新增参数',
  423. i18n: 'route.general.param.create',
  424. sidebar: false,
  425. activeMenu: '/marketing/paramList',
  426. cache: true,
  427. noCache: 'paramList',
  428. },
  429. },
  430. {
  431. legacyId: 21,
  432. legacyParentId: 18,
  433. title: '编辑参数',
  434. type: 'SUBMENU',
  435. name: 'paramEdit',
  436. path: '/marketing/param/detail/:id',
  437. icon: null,
  438. componentKey: '@/views/marketing_center/param_mgnt/detail.vue',
  439. order: 3,
  440. meta: {
  441. title: '编辑参数',
  442. i18n: 'route.general.param.edit',
  443. sidebar: false,
  444. activeMenu: '/marketing/paramList',
  445. cache: true,
  446. noCache: 'paramList',
  447. },
  448. },
  449. {
  450. legacyId: 22,
  451. legacyParentId: 1,
  452. title: '渠道管理',
  453. type: 'MENU',
  454. name: 'channelList',
  455. path: '/marketing/channel',
  456. icon: 'i-carbon:network-3',
  457. order: 6,
  458. meta: {
  459. title: '渠道管理',
  460. i18n: 'route.general.channel.root',
  461. icon: 'i-carbon:network-3',
  462. },
  463. },
  464. {
  465. legacyId: 23,
  466. legacyParentId: 22,
  467. title: '渠道列表',
  468. type: 'SUBMENU',
  469. name: 'channelList',
  470. path: '/marketing/channelList',
  471. icon: null,
  472. componentKey: '@/views/marketing_center/channel_mgnt/list.vue',
  473. order: 1,
  474. meta: {
  475. title: '渠道列表',
  476. i18n: 'route.general.channel.list',
  477. sidebar: false,
  478. breadcrumb: false,
  479. cache: ['channelCreate', 'channelEdit'],
  480. },
  481. },
  482. {
  483. legacyId: 24,
  484. legacyParentId: 22,
  485. title: '新增渠道',
  486. type: 'SUBMENU',
  487. name: 'channelCreate',
  488. path: '/marketing/channel/detail',
  489. icon: null,
  490. componentKey: '@/views/marketing_center/channel_mgnt/detail.vue',
  491. order: 2,
  492. meta: {
  493. title: '新增渠道',
  494. i18n: 'route.general.channel.create',
  495. sidebar: false,
  496. activeMenu: '/marketing/channelList',
  497. cache: true,
  498. noCache: 'channelList',
  499. },
  500. },
  501. {
  502. legacyId: 25,
  503. legacyParentId: 22,
  504. title: '编辑渠道',
  505. type: 'SUBMENU',
  506. name: 'channelEdit',
  507. path: '/marketing/channel/detail/:id',
  508. icon: null,
  509. componentKey: '@/views/marketing_center/channel_mgnt/detail.vue',
  510. order: 3,
  511. meta: {
  512. title: '编辑渠道',
  513. i18n: 'route.general.channel.edit',
  514. sidebar: false,
  515. activeMenu: '/marketing/channelList',
  516. cache: true,
  517. noCache: 'channelList',
  518. },
  519. },
  520. // ======================
  521. // 数据中心 (Data Center)
  522. // ======================
  523. {
  524. legacyId: 26,
  525. legacyParentId: null,
  526. title: '数据中心',
  527. type: 'DIRECTORY',
  528. name: 'Data Center',
  529. path: '/datacenter',
  530. icon: 'i-carbon:data-vis-4',
  531. order: 200,
  532. meta: {
  533. title: '数据中心',
  534. i18n: 'route.general.root',
  535. icon: 'i-carbon:data-vis-4',
  536. },
  537. },
  538. {
  539. legacyId: 27,
  540. legacyParentId: 26,
  541. title: 'APP访问记录',
  542. type: 'MENU',
  543. name: 'appAccessList',
  544. path: '/datacenter/appAccessRecord',
  545. icon: 'i-carbon:mobile',
  546. order: 1,
  547. meta: {
  548. title: 'APP访问记录',
  549. i18n: 'route.general.appAccessRecord.root',
  550. icon: 'i-carbon:mobile',
  551. },
  552. },
  553. {
  554. legacyId: 28,
  555. legacyParentId: 27,
  556. title: 'APP访问记录',
  557. type: 'SUBMENU',
  558. name: 'appAccessList',
  559. path: '/datacenter/appAccessList',
  560. icon: null,
  561. componentKey: '@/views/data_center/appAccess_records/list.vue',
  562. order: 1,
  563. meta: {
  564. title: 'APP访问记录',
  565. i18n: 'route.general.appAccessRecord.list',
  566. sidebar: false,
  567. breadcrumb: false,
  568. cache: ['appAccessRecordCreate', 'appAccessRecordEdit'],
  569. },
  570. },
  571. {
  572. legacyId: 29,
  573. legacyParentId: 26,
  574. title: '广告点击记录',
  575. type: 'MENU',
  576. name: 'adsAccessList',
  577. path: '/datacenter/adsAccessRecord',
  578. icon: 'i-carbon:touch-1',
  579. order: 2,
  580. meta: {
  581. title: '广告点击记录',
  582. i18n: 'route.general.adsAccessRecord.root',
  583. icon: 'i-carbon:touch-1',
  584. },
  585. },
  586. {
  587. legacyId: 30,
  588. legacyParentId: 29,
  589. title: '广告点击记录',
  590. type: 'SUBMENU',
  591. name: 'adsAccessList',
  592. path: '/datacenter/adsAccessList',
  593. icon: null,
  594. componentKey: '@/views/data_center/adsAccess_records/list.vue',
  595. order: 1,
  596. meta: {
  597. title: '广告点击记录',
  598. i18n: 'route.general.category.list',
  599. sidebar: false,
  600. breadcrumb: false,
  601. cache: ['adsAccessRecordCreate', 'adsAccessRecordEdit'],
  602. },
  603. },
  604. // ======================
  605. // 统计中心 (Stats Center)
  606. // ======================
  607. {
  608. legacyId: 31,
  609. legacyParentId: null,
  610. title: '统计中心',
  611. type: 'DIRECTORY',
  612. name: 'Statistics Management',
  613. path: '/stats',
  614. icon: 'i-carbon:chart-line',
  615. order: 300,
  616. meta: {
  617. title: '统计中心',
  618. i18n: 'route.general.root',
  619. icon: 'i-carbon:chart-line',
  620. },
  621. },
  622. {
  623. legacyId: 32,
  624. legacyParentId: 31,
  625. title: '每日统计',
  626. type: 'MENU',
  627. name: 'dailyStats',
  628. path: '/stats/dailyStats',
  629. icon: 'i-carbon:calendar',
  630. order: 1,
  631. meta: {
  632. title: '每日统计',
  633. i18n: 'route.general.dailyStats.root',
  634. icon: 'i-carbon:calendar',
  635. },
  636. },
  637. {
  638. legacyId: 33,
  639. legacyParentId: 32,
  640. title: '每日统计',
  641. type: 'SUBMENU',
  642. name: 'dailyStatsList',
  643. path: '/stats/dailyStatsList',
  644. icon: null,
  645. componentKey: '@/views/stats_center/daily_stats/list.vue',
  646. order: 1,
  647. meta: {
  648. title: '每日统计',
  649. i18n: 'route.general.video.list',
  650. sidebar: false,
  651. breadcrumb: false,
  652. cache: ['videoCreate', 'videoEdit'],
  653. },
  654. },
  655. {
  656. legacyId: 34,
  657. legacyParentId: 31,
  658. title: '广告统计',
  659. type: 'MENU',
  660. name: 'adsStatsList',
  661. path: '/stats/adsStats',
  662. icon: 'i-carbon:chart-bar',
  663. order: 2,
  664. meta: {
  665. title: '广告统计',
  666. i18n: 'route.general.adsStats.root',
  667. icon: 'i-carbon:chart-bar',
  668. },
  669. },
  670. {
  671. legacyId: 35,
  672. legacyParentId: 34,
  673. title: '广告统计',
  674. type: 'SUBMENU',
  675. name: 'adsStatsList',
  676. path: '/stats/adsStatsList',
  677. icon: null,
  678. componentKey: '@/views/stats_center/ads_stats/list.vue',
  679. order: 1,
  680. meta: {
  681. title: '广告统计',
  682. i18n: 'route.general.adsStats.list',
  683. sidebar: false,
  684. breadcrumb: false,
  685. cache: ['adsStatsCreate', 'adsStatsEdit'],
  686. },
  687. },
  688. {
  689. legacyId: 36,
  690. legacyParentId: 31,
  691. title: '广告汇总',
  692. type: 'MENU',
  693. name: 'adsStatsSummary',
  694. path: '/stats/adsStatsSummary',
  695. icon: 'i-carbon:chart-pie',
  696. componentKey: '@/views/stats_center/ads_stats_summary/list.vue',
  697. order: 3,
  698. meta: {
  699. title: '广告汇总',
  700. i18n: 'route.general.adsStatsSummary.root',
  701. icon: 'i-carbon:chart-pie',
  702. },
  703. },
  704. {
  705. legacyId: 37,
  706. legacyParentId: 36,
  707. title: '广告汇总',
  708. type: 'SUBMENU',
  709. name: 'adsStatsSummaryList',
  710. path: '/stats/adsStatsSummaryList',
  711. icon: null,
  712. componentKey: '@/views/stats_center/ads_stats_summary/list.vue',
  713. order: 1,
  714. meta: {
  715. title: '广告汇总',
  716. i18n: 'route.general.adsStatsSummary.list',
  717. sidebar: false,
  718. breadcrumb: false,
  719. cache: ['adsStatsSummaryCreate', 'adsStatsSummaryEdit'],
  720. },
  721. },
  722. {
  723. legacyId: 38,
  724. legacyParentId: 31,
  725. title: '渠道统计',
  726. type: 'MENU',
  727. name: 'channelStatsList',
  728. path: '/stats/channelStats',
  729. icon: 'i-carbon:flow',
  730. order: 4,
  731. meta: {
  732. title: '渠道统计',
  733. i18n: 'route.general.channelStats.root',
  734. icon: 'i-carbon:flow',
  735. },
  736. },
  737. {
  738. legacyId: 39,
  739. legacyParentId: 38,
  740. title: '渠道统计',
  741. type: 'SUBMENU',
  742. name: 'channelStatsList',
  743. path: '/stats/channelStatsList',
  744. icon: null,
  745. componentKey: '@/views/stats_center/channel_stats/list.vue',
  746. order: 1,
  747. meta: {
  748. title: '渠道统计',
  749. i18n: 'route.general.channel.list',
  750. sidebar: false,
  751. breadcrumb: false,
  752. cache: ['channelCreate', 'channelEdit'],
  753. },
  754. },
  755. {
  756. legacyId: 40,
  757. legacyParentId: 31,
  758. title: '渠道汇总',
  759. type: 'MENU',
  760. name: 'channelStatsSummaryList',
  761. path: '/stats/channelStatsSummary',
  762. icon: 'i-carbon:report',
  763. order: 5,
  764. meta: {
  765. title: '渠道汇总',
  766. i18n: 'route.general.channelStatsSummary.root',
  767. icon: 'i-carbon:report',
  768. },
  769. },
  770. {
  771. legacyId: 41,
  772. legacyParentId: 40,
  773. title: '渠道汇总',
  774. type: 'SUBMENU',
  775. name: 'channelStatsSummaryList',
  776. path: '/stats/channelStatsSummaryList',
  777. icon: null,
  778. componentKey: '@/views/stats_center/channel_stats_summary/list.vue',
  779. order: 1,
  780. meta: {
  781. title: '参数列表',
  782. i18n: 'route.general.param.list',
  783. sidebar: false,
  784. breadcrumb: false,
  785. cache: ['channelStatsSummaryCreate', 'channelStatsSummaryEdit'],
  786. },
  787. },
  788. // ======================
  789. // 系统管理 (System)
  790. // ======================
  791. {
  792. legacyId: 42,
  793. legacyParentId: null,
  794. title: '账号管理',
  795. type: 'DIRECTORY',
  796. name: 'Sytem Management',
  797. path: '/system',
  798. icon: 'i-carbon:user-admin',
  799. order: 400,
  800. meta: {
  801. title: '账号管理',
  802. i18n: 'route.general.root',
  803. icon: 'i-carbon:user-admin',
  804. },
  805. },
  806. {
  807. legacyId: 43,
  808. legacyParentId: 42,
  809. title: '账号列表',
  810. type: 'MENU',
  811. name: 'userList',
  812. path: '/system/users',
  813. icon: 'i-carbon:user-multiple',
  814. componentKey: '@/views/system_mgnt/users/list.vue',
  815. order: 1,
  816. meta: {
  817. title: '账号列表',
  818. i18n: 'route.general.manager.root',
  819. icon: 'i-carbon:user-multiple',
  820. },
  821. },
  822. {
  823. legacyId: 44,
  824. legacyParentId: 42,
  825. title: '角色列表',
  826. type: 'MENU',
  827. name: 'roleList',
  828. path: '/system/roles',
  829. icon: 'i-carbon:user-role',
  830. order: 2,
  831. meta: {
  832. title: '角色列表',
  833. i18n: 'route.general.role.root',
  834. icon: 'i-carbon:user-role',
  835. },
  836. },
  837. {
  838. legacyId: 45,
  839. legacyParentId: 44,
  840. title: '角色列表',
  841. type: 'SUBMENU',
  842. name: 'roleList',
  843. path: '/system/roleList',
  844. icon: null,
  845. componentKey: '@/views/system_mgnt/roles/list.vue',
  846. order: 1,
  847. meta: {
  848. title: '角色列表',
  849. i18n: 'route.general.role.list',
  850. sidebar: false,
  851. breadcrumb: false,
  852. cache: ['roleCreate', 'roleEdit'],
  853. },
  854. },
  855. {
  856. legacyId: 46,
  857. legacyParentId: 44,
  858. title: '新增角色',
  859. type: 'SUBMENU',
  860. name: 'roleCreate',
  861. path: '/system/roles/detail',
  862. icon: null,
  863. componentKey: '@/views/system_mgnt/roles/detail.vue',
  864. order: 2,
  865. meta: {
  866. title: '新增角色',
  867. i18n: 'route.general.role.create',
  868. sidebar: false,
  869. activeMenu: '/system/roleList',
  870. cache: true,
  871. noCache: 'roleList',
  872. },
  873. },
  874. {
  875. legacyId: 47,
  876. legacyParentId: 44,
  877. title: '编辑角色',
  878. type: 'SUBMENU',
  879. name: 'roleEdit',
  880. path: '/system/roles/detail/:id',
  881. icon: null,
  882. componentKey: '@/views/system_mgnt/roles/detail.vue',
  883. order: 3,
  884. meta: {
  885. title: '编辑角色',
  886. i18n: 'route.general.role.edit',
  887. sidebar: false,
  888. activeMenu: '/system/roleList',
  889. cache: true,
  890. noCache: 'roleList',
  891. },
  892. },
  893. {
  894. legacyId: 48,
  895. legacyParentId: 42,
  896. title: '权限列表',
  897. type: 'MENU',
  898. name: 'menus',
  899. path: '/system/menus',
  900. icon: 'i-carbon:list-boxes',
  901. order: 3,
  902. meta: {
  903. title: '权限列表',
  904. i18n: 'route.general.menu.root',
  905. icon: 'i-carbon:list-boxes',
  906. },
  907. },
  908. {
  909. legacyId: 49,
  910. legacyParentId: 48,
  911. title: '菜单列表',
  912. type: 'SUBMENU',
  913. name: 'menuList',
  914. path: '/system/menuList',
  915. icon: null,
  916. componentKey: '@/views/system_mgnt/menus/list.vue',
  917. order: 1,
  918. meta: {
  919. title: '菜单列表',
  920. i18n: 'route.general.menu.list',
  921. sidebar: false,
  922. breadcrumb: false,
  923. cache: ['menuCreate', 'menuEdit'],
  924. },
  925. },
  926. {
  927. legacyId: 50,
  928. legacyParentId: 48,
  929. title: '新增菜单',
  930. type: 'SUBMENU',
  931. name: 'menuCreate',
  932. path: '/system/menus/detail',
  933. icon: null,
  934. componentKey: '@/views/system_mgnt/menus/detail.vue',
  935. order: 2,
  936. meta: {
  937. title: '新增菜单',
  938. i18n: 'route.general.menu.create',
  939. sidebar: false,
  940. activeMenu: '/system/menuList',
  941. cache: true,
  942. noCache: 'menuList',
  943. },
  944. },
  945. {
  946. legacyId: 51,
  947. legacyParentId: 48,
  948. title: '编辑菜单',
  949. type: 'SUBMENU',
  950. name: 'menuEdit',
  951. path: '/system/menus/detail/:id',
  952. icon: null,
  953. componentKey: '@/views/system_mgnt/menus/detail.vue',
  954. order: 3,
  955. meta: {
  956. title: '编辑菜单',
  957. i18n: 'route.general.menu.edit',
  958. sidebar: false,
  959. activeMenu: '/system/menuList',
  960. cache: true,
  961. noCache: 'menuList',
  962. },
  963. },
  964. {
  965. legacyId: 52,
  966. legacyParentId: 42,
  967. title: 'Redis监控',
  968. type: 'MENU',
  969. name: 'redisMonitor',
  970. path: '/system/redisMonitor',
  971. icon: 'i-carbon:list-boxes',
  972. order: 4,
  973. meta: {
  974. title: 'Redis监控',
  975. i18n: 'route.general.redisMonitor.list',
  976. icon: 'i-carbon:list-boxes',
  977. },
  978. },
  979. {
  980. legacyId: 53,
  981. legacyParentId: 52,
  982. title: 'Redis列表',
  983. type: 'SUBMENU',
  984. name: 'redisList',
  985. path: '/system/redisList',
  986. icon: null,
  987. componentKey: '@/views/system_mgnt/redisMonitor/list.vue',
  988. order: 1,
  989. meta: {
  990. title: 'Redis列表',
  991. i18n: 'route.general.redisMonitor.list',
  992. sidebar: false,
  993. breadcrumb: false,
  994. cache: ['redisCreate', 'redisEdit'],
  995. },
  996. },
  997. ];
  998. // =============================================================================
  999. // HELPER FUNCTIONS
  1000. // =============================================================================
  1001. /**
  1002. * Get default action permissions based on menu type
  1003. */
  1004. function getDefaultPermissions(type: MenuType) {
  1005. switch (type) {
  1006. case 'DIRECTORY':
  1007. return { canView: 1, canCreate: 0, canUpdate: 0, canDelete: 0 };
  1008. case 'MENU':
  1009. case 'SUBMENU':
  1010. return { canView: 1, canCreate: 1, canUpdate: 1, canDelete: 1 };
  1011. case 'BUTTON':
  1012. return { canView: 0, canCreate: 1, canUpdate: 0, canDelete: 0 };
  1013. default:
  1014. return { canView: 0, canCreate: 0, canUpdate: 0, canDelete: 0 };
  1015. }
  1016. }
  1017. // =============================================================================
  1018. // SEED FUNCTIONS
  1019. // =============================================================================
  1020. /**
  1021. * Seed users and roles
  1022. */
  1023. async function seedUsers() {
  1024. console.log('📝 Seeding users and roles...');
  1025. await prisma.$transaction([
  1026. prisma.sysUserRole.deleteMany(),
  1027. prisma.sysUser.deleteMany(),
  1028. prisma.sysRole.deleteMany(),
  1029. ]);
  1030. // Upsert role by unique name
  1031. const roleCreateData: Prisma.SysRoleUncheckedCreateInput = {
  1032. id: '6946c613ea4266475e73d074',
  1033. name: '管理员',
  1034. status: true,
  1035. remark: '管理员专用',
  1036. };
  1037. const role = await prisma.sysRole.upsert({
  1038. where: { name: '管理员' },
  1039. update: { remark: '管理员专用', status: true },
  1040. create: roleCreateData,
  1041. });
  1042. // Upsert admin user by unique username
  1043. const adminUserCreateData: Prisma.SysUserUncheckedCreateInput = {
  1044. username: 'admin',
  1045. password: '$2b$12$iS0UJ1YqSal0N3uwin/OvOABUINAclcZGjHNyGFC7mlwRYTFjGQ26',
  1046. remark: '默认拥有所有菜单权限,不需要配置角色',
  1047. };
  1048. const adminUser = await prisma.sysUser.upsert({
  1049. where: { username: 'admin' },
  1050. // Do not overwrite password on existing user
  1051. update: { remark: '默认拥有所有菜单权限,不需要配置角色' },
  1052. create: adminUserCreateData,
  1053. });
  1054. // Link user and role if not already linked
  1055. const existingLink = await prisma.sysUserRole.findFirst({
  1056. where: { userId: adminUser.id, roleId: role.id },
  1057. });
  1058. if (!existingLink) {
  1059. const userRoleCreateData: Prisma.SysUserRoleUncheckedCreateInput = {
  1060. userId: adminUser.id,
  1061. roleId: role.id,
  1062. };
  1063. await prisma.sysUserRole.create({
  1064. data: userRoleCreateData,
  1065. });
  1066. }
  1067. console.log('✅ Users and roles seeded successfully');
  1068. console.log(` - Admin user: ${adminUser.username}`);
  1069. console.log(` - Role: ${role.name}`);
  1070. }
  1071. /**
  1072. * Seed menus with multi-pass insertion to handle parent-child relationships
  1073. */
  1074. async function seedMenus() {
  1075. console.log('📝 Seeding menus...');
  1076. await prisma.$runCommandRaw({
  1077. delete: 'sys_api_permission',
  1078. deletes: [{ q: {}, limit: 0 }],
  1079. });
  1080. await prisma.$runCommandRaw({
  1081. delete: 'sys_menu',
  1082. deletes: [{ q: {}, limit: 0 }],
  1083. });
  1084. const legacyIdToNewId = new Map<number, string>();
  1085. const inserted = new Set<number>();
  1086. // Multi-pass insertion: insert records whose parent is either null or already inserted
  1087. while (inserted.size < MENU_SEEDS.length) {
  1088. let progress = false;
  1089. for (const seed of MENU_SEEDS) {
  1090. if (inserted.has(seed.legacyId)) {
  1091. continue;
  1092. }
  1093. // If has a parent, but parent not inserted yet → skip this round
  1094. if (
  1095. seed.legacyParentId !== null &&
  1096. !legacyIdToNewId.has(seed.legacyParentId)
  1097. ) {
  1098. continue;
  1099. }
  1100. const parentId =
  1101. seed.legacyParentId === null
  1102. ? null
  1103. : legacyIdToNewId.get(seed.legacyParentId)!;
  1104. const permissions = getDefaultPermissions(seed.type);
  1105. const menuCreateData: Prisma.SysMenuUncheckedCreateInput = {
  1106. parentId,
  1107. title: seed.title,
  1108. status: true,
  1109. type: seed.type,
  1110. order: seed.order,
  1111. frontendAuth: seed.path,
  1112. path: seed.path,
  1113. name: seed.name,
  1114. icon: seed.icon,
  1115. redirect: null,
  1116. componentKey: seed.componentKey ?? null,
  1117. meta: seed.meta ?? undefined,
  1118. ...permissions,
  1119. };
  1120. const created = await prisma.sysMenu.upsert({
  1121. // Use unique index on frontendAuth to identify menu
  1122. where: { frontendAuth: seed.path },
  1123. update: {
  1124. parentId,
  1125. title: seed.title,
  1126. status: true,
  1127. type: seed.type,
  1128. order: seed.order,
  1129. // keep frontendAuth as path
  1130. path: seed.path,
  1131. name: seed.name,
  1132. icon: seed.icon,
  1133. redirect: null,
  1134. componentKey: seed.componentKey ?? null,
  1135. // meta undefined means do not overwrite; if provided, update
  1136. ...(seed.meta !== undefined
  1137. ? { meta: seed.meta as Prisma.InputJsonValue }
  1138. : {}),
  1139. ...permissions,
  1140. },
  1141. create: {
  1142. ...menuCreateData,
  1143. },
  1144. });
  1145. legacyIdToNewId.set(seed.legacyId, created.id);
  1146. inserted.add(seed.legacyId);
  1147. progress = true;
  1148. console.log(
  1149. ` ✓ Menu [${seed.type.padEnd(9)}] ${seed.name.padEnd(25)} → ID: ${created.id}`,
  1150. );
  1151. }
  1152. if (!progress) {
  1153. // Nothing could be inserted in this pass → some parentId is invalid or cyclic
  1154. const pending = MENU_SEEDS.filter((s) => !inserted.has(s.legacyId)).map(
  1155. (s) => ({
  1156. legacyId: s.legacyId,
  1157. legacyParentId: s.legacyParentId,
  1158. name: s.name,
  1159. }),
  1160. );
  1161. console.error('❌ Could not resolve parents for menus:', pending);
  1162. throw new Error(
  1163. 'Menu seeding aborted: unresolved parent relationships. Check legacyParentId values.',
  1164. );
  1165. }
  1166. }
  1167. console.log('✅ Menus seeded successfully');
  1168. console.log(` - Total menus: ${inserted.size}`);
  1169. }
  1170. // =============================================================================
  1171. // MAIN EXECUTION
  1172. // =============================================================================
  1173. async function main() {
  1174. const adsModules = [
  1175. {
  1176. adType: AdType.STARTUP,
  1177. adsModule: '启动页',
  1178. moduleDesc: '启动页(10:21)',
  1179. seq: 1,
  1180. },
  1181. {
  1182. adType: AdType.CAROUSEL,
  1183. adsModule: '轮播',
  1184. moduleDesc: '轮播(2:1)',
  1185. seq: 2,
  1186. },
  1187. {
  1188. adType: AdType.POPUP_ICON,
  1189. adsModule: '弹窗-图标',
  1190. moduleDesc: '弹窗-图标(1:1)',
  1191. seq: 3,
  1192. },
  1193. {
  1194. adType: AdType.POPUP_IMAGE,
  1195. adsModule: '弹窗-图片',
  1196. moduleDesc: '弹窗-图片(2:3)',
  1197. seq: 4,
  1198. },
  1199. {
  1200. adType: AdType.POPUP_OFFICIAL,
  1201. adsModule: '弹窗-官方',
  1202. moduleDesc: '弹窗-官方(2:3)',
  1203. seq: 5,
  1204. },
  1205. {
  1206. adType: AdType.WATERFALL_ICON,
  1207. adsModule: '瀑布流-图标',
  1208. moduleDesc: '瀑布流-图标(1:1)',
  1209. seq: 6,
  1210. },
  1211. {
  1212. adType: AdType.WATERFALL_TEXT,
  1213. adsModule: '瀑布流-文字',
  1214. moduleDesc: '瀑布流-文字',
  1215. seq: 7,
  1216. },
  1217. {
  1218. adType: AdType.WATERFALL_VIDEO,
  1219. adsModule: '瀑布流-视频',
  1220. moduleDesc: '瀑布流-视频(8:5)',
  1221. seq: 8,
  1222. },
  1223. {
  1224. adType: AdType.FLOATING_BOTTOM,
  1225. adsModule: '悬浮-底部',
  1226. moduleDesc: '悬浮-底部(1:1)',
  1227. seq: 9,
  1228. },
  1229. {
  1230. adType: AdType.FLOATING_EDGE,
  1231. adsModule: '悬浮-边缘',
  1232. moduleDesc: '悬浮-边缘(1:1)',
  1233. seq: 10,
  1234. },
  1235. {
  1236. adType: AdType.BANNER,
  1237. adsModule: 'banner',
  1238. moduleDesc: 'banner(4:1)',
  1239. seq: 11,
  1240. },
  1241. {
  1242. adType: AdType.PREROLL,
  1243. adsModule: '片头',
  1244. moduleDesc: '片头(8:5)',
  1245. seq: 12,
  1246. },
  1247. {
  1248. adType: AdType.PAUSE,
  1249. adsModule: '暂停',
  1250. moduleDesc: '暂停(2:1)',
  1251. seq: 13,
  1252. },
  1253. ];
  1254. console.log('🌱 Starting database seeding...\n');
  1255. try {
  1256. console.log('cleaning ads modules...');
  1257. await prisma.adsModule.deleteMany();
  1258. console.log('seeding ads modules...');
  1259. for (const module of adsModules) {
  1260. await prisma.adsModule.create({
  1261. data: module,
  1262. });
  1263. }
  1264. // Seed in order: users first, then menus
  1265. console.log('seeding users...');
  1266. await seedUsers();
  1267. console.log('');
  1268. console.log('seeding menus...');
  1269. await seedMenus();
  1270. console.log('');
  1271. console.log('seeding system config...');
  1272. await seedSysConfig();
  1273. console.log('\n🎉 Database seeding completed successfully!');
  1274. } catch (error) {
  1275. console.error('\n❌ Error during seeding:', error);
  1276. throw error;
  1277. }
  1278. }
  1279. main()
  1280. .then(async () => {
  1281. await prisma.$disconnect();
  1282. })
  1283. .catch(async (e) => {
  1284. console.error(e);
  1285. await prisma.$disconnect();
  1286. process.exit(1);
  1287. });