seed-admin.ts 27 KB

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