seed.ts 27 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120
  1. // prisma/mysql/seed.ts
  2. import { PrismaClient, MenuType } from '@prisma/mysql/client';
  3. import type { Prisma } from '@prisma/mysql/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. component_key?: 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. component_key: '@/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. component_key: '@/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. component_key: '@/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. component_key: '@/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. component_key: '@/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. component_key: '@/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. component_key: '@/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. component_key: '@/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. component_key: '@/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. component_key: '@/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. component_key: '@/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. component_key: '@/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. component_key: '@/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. component_key: '@/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. component_key: '@/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. component_key: '@/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. component_key: '@/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. component_key: '@/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. component_key: '@/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. component_key: '@/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. component_key: '@/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. component_key: '@/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. component_key: '@/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. component_key: '@/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. component_key: '@/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. component_key: '@/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. component_key: '@/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. component_key: '@/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. component_key: '@/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. component_key: '@/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. component_key: '@/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. component_key: '@/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. component_key: '@/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. // Upsert role by unique name
  938. const role = await prisma.role.upsert({
  939. where: { name: '管理员' },
  940. update: { remark: '管理员专用' },
  941. create: {
  942. name: '管理员',
  943. remark: '管理员专用',
  944. },
  945. });
  946. // Upsert admin user by unique username
  947. const adminUser = await prisma.user.upsert({
  948. where: { username: 'admin' },
  949. // Do not overwrite password on existing user
  950. update: { remark: '默认拥有所有菜单权限,不需要配置角色' },
  951. create: {
  952. username: 'admin',
  953. password: '$2b$12$iS0UJ1YqSal0N3uwin/OvOABUINAclcZGjHNyGFC7mlwRYTFjGQ26',
  954. remark: '默认拥有所有菜单权限,不需要配置角色',
  955. },
  956. });
  957. // Link user and role if not already linked
  958. const existingLink = await prisma.userRole.findFirst({
  959. where: { userId: adminUser.id, roleId: role.id },
  960. });
  961. if (!existingLink) {
  962. await prisma.userRole.create({
  963. data: {
  964. userId: adminUser.id,
  965. roleId: role.id,
  966. },
  967. });
  968. }
  969. console.log('✅ Users and roles seeded successfully');
  970. console.log(` - Admin user: ${adminUser.username}`);
  971. console.log(` - Role: ${role.name}`);
  972. }
  973. /**
  974. * Seed menus with multi-pass insertion to handle parent-child relationships
  975. */
  976. async function seedMenus() {
  977. console.log('📝 Seeding menus...');
  978. const legacyIdToNewId = new Map<number, number>();
  979. const inserted = new Set<number>();
  980. // Multi-pass insertion: insert records whose parent is either null or already inserted
  981. while (inserted.size < MENU_SEEDS.length) {
  982. let progress = false;
  983. for (const seed of MENU_SEEDS) {
  984. if (inserted.has(seed.legacyId)) {
  985. continue;
  986. }
  987. // If has a parent, but parent not inserted yet → skip this round
  988. if (
  989. seed.legacyParentId !== null &&
  990. !legacyIdToNewId.has(seed.legacyParentId)
  991. ) {
  992. continue;
  993. }
  994. const parentId =
  995. seed.legacyParentId === null
  996. ? null
  997. : legacyIdToNewId.get(seed.legacyParentId)!;
  998. const permissions = getDefaultPermissions(seed.type);
  999. const created = await prisma.menu.upsert({
  1000. // Use unique index on frontendAuth to identify menu
  1001. where: { frontendAuth: seed.path },
  1002. update: {
  1003. parentId,
  1004. title: seed.title,
  1005. status: true,
  1006. type: seed.type,
  1007. order: seed.order,
  1008. // keep frontendAuth as path
  1009. path: seed.path,
  1010. name: seed.name,
  1011. icon: seed.icon,
  1012. redirect: null,
  1013. component_key: seed.component_key ?? null,
  1014. // meta undefined means do not overwrite; if provided, update
  1015. ...(seed.meta !== undefined
  1016. ? { meta: seed.meta as Prisma.InputJsonValue }
  1017. : {}),
  1018. ...permissions,
  1019. },
  1020. create: {
  1021. parentId,
  1022. title: seed.title,
  1023. status: true,
  1024. type: seed.type,
  1025. order: seed.order,
  1026. // Use path as frontendAuth for RBAC tracking
  1027. frontendAuth: seed.path,
  1028. path: seed.path,
  1029. name: seed.name,
  1030. icon: seed.icon,
  1031. redirect: null,
  1032. component_key: seed.component_key ?? null,
  1033. meta: seed.meta ?? undefined,
  1034. // Set default permissions based on type
  1035. ...permissions,
  1036. },
  1037. });
  1038. legacyIdToNewId.set(seed.legacyId, created.id);
  1039. inserted.add(seed.legacyId);
  1040. progress = true;
  1041. console.log(
  1042. ` ✓ Menu [${seed.type.padEnd(9)}] ${seed.name.padEnd(25)} → ID: ${created.id}`,
  1043. );
  1044. }
  1045. if (!progress) {
  1046. // Nothing could be inserted in this pass → some parentId is invalid or cyclic
  1047. const pending = MENU_SEEDS.filter((s) => !inserted.has(s.legacyId)).map(
  1048. (s) => ({
  1049. legacyId: s.legacyId,
  1050. legacyParentId: s.legacyParentId,
  1051. name: s.name,
  1052. }),
  1053. );
  1054. console.error('❌ Could not resolve parents for menus:', pending);
  1055. throw new Error(
  1056. 'Menu seeding aborted: unresolved parent relationships. Check legacyParentId values.',
  1057. );
  1058. }
  1059. }
  1060. console.log('✅ Menus seeded successfully');
  1061. console.log(` - Total menus: ${inserted.size}`);
  1062. }
  1063. // =============================================================================
  1064. // MAIN EXECUTION
  1065. // =============================================================================
  1066. async function main() {
  1067. console.log('🌱 Starting database seeding...\n');
  1068. try {
  1069. // Seed in order: users first, then menus
  1070. await seedUsers();
  1071. console.log('');
  1072. await seedMenus();
  1073. console.log('\n🎉 Database seeding completed successfully!');
  1074. } catch (error) {
  1075. console.error('\n❌ Error during seeding:', error);
  1076. throw error;
  1077. }
  1078. }
  1079. main()
  1080. .then(async () => {
  1081. await prisma.$disconnect();
  1082. })
  1083. .catch(async (e) => {
  1084. console.error(e);
  1085. await prisma.$disconnect();
  1086. process.exit(1);
  1087. });