seed-admin.ts 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206
  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. 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://mgnt.cqf.wang/images',
  38. chatUpload: 'https://mgnt.cqf.wang/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. // =============================================================================
  966. // HELPER FUNCTIONS
  967. // =============================================================================
  968. /**
  969. * Get default action permissions based on menu type
  970. */
  971. function getDefaultPermissions(type: MenuType) {
  972. switch (type) {
  973. case 'DIRECTORY':
  974. return { canView: 1, canCreate: 0, canUpdate: 0, canDelete: 0 };
  975. case 'MENU':
  976. case 'SUBMENU':
  977. return { canView: 1, canCreate: 1, canUpdate: 1, canDelete: 1 };
  978. case 'BUTTON':
  979. return { canView: 0, canCreate: 1, canUpdate: 0, canDelete: 0 };
  980. default:
  981. return { canView: 0, canCreate: 0, canUpdate: 0, canDelete: 0 };
  982. }
  983. }
  984. // =============================================================================
  985. // SEED FUNCTIONS
  986. // =============================================================================
  987. /**
  988. * Seed users and roles
  989. */
  990. async function seedUsers() {
  991. console.log('📝 Seeding users and roles...');
  992. await prisma.$transaction([
  993. prisma.sysUserRole.deleteMany(),
  994. prisma.sysUser.deleteMany(),
  995. prisma.sysRole.deleteMany(),
  996. ]);
  997. // Upsert role by unique name
  998. const roleCreateData: Prisma.SysRoleUncheckedCreateInput = {
  999. id: '6946c613ea4266475e73d074',
  1000. name: '管理员',
  1001. status: true,
  1002. remark: '管理员专用',
  1003. };
  1004. const role = await prisma.sysRole.upsert({
  1005. where: { name: '管理员' },
  1006. update: { remark: '管理员专用', status: true },
  1007. create: roleCreateData,
  1008. });
  1009. // Upsert admin user by unique username
  1010. const adminUserCreateData: Prisma.SysUserUncheckedCreateInput = {
  1011. username: 'admin',
  1012. password: '$2b$12$iS0UJ1YqSal0N3uwin/OvOABUINAclcZGjHNyGFC7mlwRYTFjGQ26',
  1013. remark: '默认拥有所有菜单权限,不需要配置角色',
  1014. };
  1015. const adminUser = await prisma.sysUser.upsert({
  1016. where: { username: 'admin' },
  1017. // Do not overwrite password on existing user
  1018. update: { remark: '默认拥有所有菜单权限,不需要配置角色' },
  1019. create: adminUserCreateData,
  1020. });
  1021. // Link user and role if not already linked
  1022. const existingLink = await prisma.sysUserRole.findFirst({
  1023. where: { userId: adminUser.id, roleId: role.id },
  1024. });
  1025. if (!existingLink) {
  1026. const userRoleCreateData: Prisma.SysUserRoleUncheckedCreateInput = {
  1027. userId: adminUser.id,
  1028. roleId: role.id,
  1029. };
  1030. await prisma.sysUserRole.create({
  1031. data: userRoleCreateData,
  1032. });
  1033. }
  1034. console.log('✅ Users and roles seeded successfully');
  1035. console.log(` - Admin user: ${adminUser.username}`);
  1036. console.log(` - Role: ${role.name}`);
  1037. }
  1038. /**
  1039. * Seed menus with multi-pass insertion to handle parent-child relationships
  1040. */
  1041. async function seedMenus() {
  1042. console.log('📝 Seeding menus...');
  1043. await prisma.$runCommandRaw({
  1044. delete: 'sys_api_permission',
  1045. deletes: [{ q: {}, limit: 0 }],
  1046. });
  1047. await prisma.$runCommandRaw({
  1048. delete: 'sys_menu',
  1049. deletes: [{ q: {}, limit: 0 }],
  1050. });
  1051. const legacyIdToNewId = new Map<number, string>();
  1052. const inserted = new Set<number>();
  1053. // Multi-pass insertion: insert records whose parent is either null or already inserted
  1054. while (inserted.size < MENU_SEEDS.length) {
  1055. let progress = false;
  1056. for (const seed of MENU_SEEDS) {
  1057. if (inserted.has(seed.legacyId)) {
  1058. continue;
  1059. }
  1060. // If has a parent, but parent not inserted yet → skip this round
  1061. if (
  1062. seed.legacyParentId !== null &&
  1063. !legacyIdToNewId.has(seed.legacyParentId)
  1064. ) {
  1065. continue;
  1066. }
  1067. const parentId =
  1068. seed.legacyParentId === null
  1069. ? null
  1070. : legacyIdToNewId.get(seed.legacyParentId)!;
  1071. const permissions = getDefaultPermissions(seed.type);
  1072. const menuCreateData: Prisma.SysMenuUncheckedCreateInput = {
  1073. parentId,
  1074. title: seed.title,
  1075. status: true,
  1076. type: seed.type,
  1077. order: seed.order,
  1078. frontendAuth: seed.path,
  1079. path: seed.path,
  1080. name: seed.name,
  1081. icon: seed.icon,
  1082. redirect: null,
  1083. componentKey: seed.componentKey ?? null,
  1084. meta: seed.meta ?? undefined,
  1085. ...permissions,
  1086. };
  1087. const created = await prisma.sysMenu.upsert({
  1088. // Use unique index on frontendAuth to identify menu
  1089. where: { frontendAuth: seed.path },
  1090. update: {
  1091. parentId,
  1092. title: seed.title,
  1093. status: true,
  1094. type: seed.type,
  1095. order: seed.order,
  1096. // keep frontendAuth as path
  1097. path: seed.path,
  1098. name: seed.name,
  1099. icon: seed.icon,
  1100. redirect: null,
  1101. componentKey: seed.componentKey ?? null,
  1102. // meta undefined means do not overwrite; if provided, update
  1103. ...(seed.meta !== undefined
  1104. ? { meta: seed.meta as Prisma.InputJsonValue }
  1105. : {}),
  1106. ...permissions,
  1107. },
  1108. create: {
  1109. ...menuCreateData,
  1110. },
  1111. });
  1112. legacyIdToNewId.set(seed.legacyId, created.id);
  1113. inserted.add(seed.legacyId);
  1114. progress = true;
  1115. console.log(
  1116. ` ✓ Menu [${seed.type.padEnd(9)}] ${seed.name.padEnd(25)} → ID: ${created.id}`,
  1117. );
  1118. }
  1119. if (!progress) {
  1120. // Nothing could be inserted in this pass → some parentId is invalid or cyclic
  1121. const pending = MENU_SEEDS.filter((s) => !inserted.has(s.legacyId)).map(
  1122. (s) => ({
  1123. legacyId: s.legacyId,
  1124. legacyParentId: s.legacyParentId,
  1125. name: s.name,
  1126. }),
  1127. );
  1128. console.error('❌ Could not resolve parents for menus:', pending);
  1129. throw new Error(
  1130. 'Menu seeding aborted: unresolved parent relationships. Check legacyParentId values.',
  1131. );
  1132. }
  1133. }
  1134. console.log('✅ Menus seeded successfully');
  1135. console.log(` - Total menus: ${inserted.size}`);
  1136. }
  1137. // =============================================================================
  1138. // MAIN EXECUTION
  1139. // =============================================================================
  1140. async function main() {
  1141. console.log('🌱 Starting database seeding...\n');
  1142. try {
  1143. // Seed in order: users first, then menus
  1144. console.log('seeding users...');
  1145. await seedUsers();
  1146. console.log('');
  1147. console.log('seeding menus...');
  1148. await seedMenus();
  1149. console.log('');
  1150. console.log('seeding system config...');
  1151. await seedSysConfig();
  1152. console.log('\n🎉 Database seeding completed successfully!');
  1153. } catch (error) {
  1154. console.error('\n❌ Error during seeding:', error);
  1155. throw error;
  1156. }
  1157. }
  1158. main()
  1159. .then(async () => {
  1160. await prisma.$disconnect();
  1161. })
  1162. .catch(async (e) => {
  1163. console.error(e);
  1164. await prisma.$disconnect();
  1165. process.exit(1);
  1166. });