seed.ts 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. // 种子数据脚本 — 将 Mock 数据写入 Parse Server
  2. import Parse from 'parse/node';
  3. Parse.initialize('prd-test');
  4. Parse.serverURL = 'http://localhost:3000/parse';
  5. Parse.masterKey = 'prd-test-master-key-2026';
  6. const sleep = (ms: number) => new Promise(r => setTimeout(r, ms));
  7. async function seed() {
  8. console.log('=== 开始种入数据 ===\n');
  9. // ===== 1. 用户 =====
  10. const users = [
  11. { id: 'u001', name: '张明华', role: 'sales', department: '销售一部', phone: '138-0000-1234', email: 'zhang@furnishing.com', password: '1234' },
  12. { id: 'u002', name: '李晓燕', role: 'sales', department: '销售一部', phone: '139-0001-2345', email: 'li@furnishing.com', password: '2345' },
  13. { id: 'u003', name: '王建国', role: 'reviewer', department: '审单部', phone: '136-0002-3456', email: 'wang@furnishing.com', password: '3456' },
  14. { id: 'u004', name: '陈美丽', role: 'finance', department: '财务部', phone: '135-0003-4567', email: 'chen@furnishing.com', password: '4567' },
  15. { id: 'u005', name: '刘强', role: 'manager', department: '管理层', phone: '137-0004-5678', email: 'liu@furnishing.com', password: '5678' },
  16. { id: 'u006', name: '赵芳', role: 'sales', department: '销售二部', phone: '132-0005-6789', email: 'zhao@furnishing.com', password: '6789' },
  17. { id: 'u007', name: '系统管理员', role: 'admin', department: '系统管理', phone: '138-0000-0000', email: 'admin@furnishing.com', password: 'admin123' },
  18. ];
  19. const userIdMap: Record<string, string> = {};
  20. for (const u of users) {
  21. try {
  22. const user = new Parse.User();
  23. user.set('username', u.id);
  24. user.set('password', u.password);
  25. user.set('name', u.name);
  26. user.set('role', u.role);
  27. user.set('department', u.department);
  28. user.set('phone', u.phone);
  29. user.set('email', u.email);
  30. const saved = await user.signUp(null);
  31. userIdMap[u.id] = saved.id;
  32. console.log(` User: ${u.name} (${u.id})`);
  33. } catch (e: any) {
  34. if (e.message?.includes('already')) {
  35. console.log(` User skip: ${u.name} (already exists)`);
  36. } else {
  37. console.log(` User FAIL: ${u.name} — ${e.message}`);
  38. }
  39. }
  40. await sleep(200);
  41. }
  42. // ===== 2. 门店 =====
  43. const stores = [
  44. '真北北馆一队:ZB1', '真北北馆二队:ZB2', '真北南馆一队:ZN1', '真北南馆二队:ZN2',
  45. '浦东一队:PD1', '浦东二队:PD2', '徐汇一队:XH1', '徐汇二队:XH2',
  46. '闵行一队:MH1', '闵行二队:MH2', '杨浦一队:YP1', '杨浦二队:YP2',
  47. '长宁一队:CN1', '长宁二队:CN2', '虹口一队:HK1', '虹口二队:HK2',
  48. '松江一队:SJ1', '松江二队:SJ2', '家装渠道部:JZ1',
  49. ];
  50. for (const s of stores) {
  51. const [name, code] = s.split(':');
  52. try {
  53. const SC = Parse.Object.extend('StoreConfig');
  54. const obj = new SC();
  55. obj.set('name', name);
  56. obj.set('code', code);
  57. await obj.save(null, { useMasterKey: true });
  58. } catch (e: any) { console.log(` Store FAIL: ${name} — ${e.message}`); }
  59. await sleep(100);
  60. }
  61. console.log(' Stores: 19 done');
  62. // ===== 3. 产品 =====
  63. const products = [
  64. { name: '定制遮光窗帘', category: '窗帘', subcategory: '遮光帘', baseSpecification: '宽2.5m×高2.8m', unit: '幅', costPrice: 280, salePrice: 580, stock: 48, status: 'active', imageUrl: 'https://images.unsplash.com/photo-1560448204-e02f11c3d0e2?w=400&h=400&fit=crop' },
  65. { name: '北欧风棉麻窗帘', category: '窗帘', subcategory: '棉麻帘', baseSpecification: '宽2m×高2.5m', unit: '幅', costPrice: 180, salePrice: 380, stock: 62, status: 'active', imageUrl: 'https://images.unsplash.com/photo-1513694203232-719a280e022f?w=400&h=400&fit=crop' },
  66. { name: '羊毛混纺地毯', category: '地毯', subcategory: '客厅地毯', baseSpecification: '2m×3m', unit: '张', costPrice: 680, salePrice: 1480, stock: 15, status: 'active', imageUrl: 'https://images.unsplash.com/photo-1600166898405-da9535204843?w=400&h=400&fit=crop' },
  67. { name: '现代简约地毯', category: '地毯', subcategory: '卧室地毯', baseSpecification: '1.5m×2m', unit: '张', costPrice: 320, salePrice: 680, stock: 28, status: 'active', imageUrl: 'https://images.unsplash.com/photo-1555041469-a586c61ea9bc?w=400&h=400&fit=crop' },
  68. { name: '意式真皮沙发', category: '沙发', subcategory: '组合沙发', baseSpecification: '3+2+1人座', unit: '套', costPrice: 8800, salePrice: 18800, stock: 6, status: 'active', imageUrl: 'https://images.unsplash.com/photo-1550254478-ead40cc54513?w=400&h=400&fit=crop' },
  69. { name: '布艺休闲沙发', category: '沙发', subcategory: '单人沙发', baseSpecification: '单人座 85cm', unit: '把', costPrice: 1200, salePrice: 2600, stock: 12, status: 'active', imageUrl: 'https://images.unsplash.com/photo-1540574163026-643ea20ade25?w=400&h=400&fit=crop' },
  70. { name: '四件套床品-云感棉', category: '床品', subcategory: '床上用品', baseSpecification: '1.8m床', unit: '套', costPrice: 380, salePrice: 880, stock: 34, status: 'active', imageUrl: 'https://images.unsplash.com/photo-1522771739844-6a9f6d5f14af?w=400&h=400&fit=crop' },
  71. { name: '法式轻奢台灯', category: '灯具', subcategory: '台灯', baseSpecification: '高45cm', unit: '盏', costPrice: 420, salePrice: 980, stock: 20, status: 'active', imageUrl: 'https://images.unsplash.com/photo-1507473885765-e6ed057f782c?w=400&h=400&fit=crop' },
  72. { name: '欧式花纹壁纸', category: '墙纸', subcategory: '卧室壁纸', baseSpecification: '0.53m×10m/卷', unit: '卷', costPrice: 180, salePrice: 380, stock: 85, status: 'active' },
  73. { name: '抽象装饰画三联', category: '装饰画', subcategory: '挂画', baseSpecification: '60cm×80cm×3幅', unit: '套', costPrice: 280, salePrice: 680, stock: 18, status: 'active' },
  74. { name: '铝合金窗帘轨道', category: '窗帘配件', subcategory: '轨道', baseSpecification: '3m/根', unit: '根', costPrice: 45, salePrice: 120, stock: 120, status: 'active' },
  75. { name: '抱枕套装', category: '配饰', subcategory: '抱枕', baseSpecification: '45cm×45cm 4个', unit: '套', costPrice: 120, salePrice: 280, stock: 45, status: 'active' },
  76. ];
  77. for (const p of products) {
  78. try {
  79. const Product = Parse.Object.extend('Product');
  80. const obj = new Product();
  81. obj.set('name', p.name);
  82. obj.set('category', p.category);
  83. obj.set('subcategory', p.subcategory);
  84. obj.set('baseSpecification', p.baseSpecification);
  85. obj.set('unit', p.unit);
  86. obj.set('costPrice', p.costPrice);
  87. obj.set('salePrice', p.salePrice);
  88. obj.set('stock', p.stock);
  89. obj.set('status', p.status);
  90. if (p.imageUrl) obj.set('imageUrl', p.imageUrl);
  91. await obj.save(null, { useMasterKey: true });
  92. } catch (e: any) { console.log(` Product FAIL: ${p.name} — ${e.message}`); }
  93. await sleep(100);
  94. }
  95. console.log(' Products: 12 done');
  96. // ===== 4. 订单 =====
  97. const orders = [
  98. { orderNo: 'SO2024120001', customerName: '万科·天空之城样板间', customerPhone: '021-88880001', customerAddress: '上海市浦东新区张江路100号', customerType: 'commercial', projectName: '万科天空之城A栋样板间', items: [{ productId: 'p001', productName: '定制遮光窗帘', category: '窗帘', specification: '宽2.5m×高2.8m', quantity: 12, unit: '幅', unitPrice: 580, totalPrice: 6960 }], status: 'pending', totalAmount: 12840, paidAmount: 6420, paymentMethod: '分期付款', isInstallment: true, salesPersonId: 'u001', salesPersonName: '张明华', riskLevel: 'medium', attachments: 3 },
  99. { orderNo: 'SO2024120002', customerName: '林女士', customerPhone: '138-1234-5678', customerAddress: '上海市徐汇区衡山路258号', customerType: 'residential', projectName: '私宅软装全案', items: [{ productId: 'p005', productName: '意式真皮沙发', category: '沙发', specification: '3+2+1人座', quantity: 1, unit: '套', unitPrice: 18800, totalPrice: 18800 }], status: 'approved', totalAmount: 22800, paidAmount: 22800, paymentMethod: '全款', salesPersonId: 'u002', salesPersonName: '李晓燕', reviewHistory: [{ id: 'rh001', action: 'approved', reviewerId: 'u003', reviewerName: '王建国', comment: '客户资质良好,全款支付,通过。', timestamp: '2024-11-30T10:15:00' }], riskLevel: 'low', attachments: 5 },
  100. { orderNo: 'SO2024120003', customerName: '碧桂园·凤凰城', customerPhone: '0755-88880003', customerAddress: '深圳市南山区碧桂园凤凰城', customerType: 'commercial', projectName: '碧桂园凤凰城B区软装', items: [{ productId: 'p002', productName: '北欧风棉麻窗帘', category: '窗帘', specification: '宽2m×高2.5m', quantity: 48, unit: '幅', unitPrice: 380, totalPrice: 18240 }], status: 'rejected', totalAmount: 34560, paidAmount: 0, paymentMethod: '月结', salesPersonId: 'u006', salesPersonName: '赵芳', reviewHistory: [{ id: 'rh002', action: 'rejected', reviewerId: 'u003', reviewerName: '王建国', comment: '月结客户超过授信额度,需要提供担保材料后重新提交。', timestamp: '2024-11-27T16:30:00' }], riskLevel: 'high', attachments: 2, lastRejectedReason: '月结客户超过授信额度' },
  101. { orderNo: 'SO2024120004', customerName: '陈先生', customerPhone: '139-8888-9999', customerAddress: '北京市朝阳区三里屯88号', customerType: 'residential', projectName: '三里屯公寓改造', items: [{ productId: 'p009', productName: '欧式花纹壁纸', category: '墙纸', specification: '0.53m×10m/卷', quantity: 20, unit: '卷', unitPrice: 380, totalPrice: 7600 }], status: 'delivered', totalAmount: 12360, paidAmount: 9270, paymentMethod: '分期付款', isInstallment: true, salesPersonId: 'u001', salesPersonName: '张明华', reviewHistory: [{ id: 'rh003', action: 'approved', reviewerId: 'u003', reviewerName: '王建国', comment: '正常业务,通过。', timestamp: '2024-11-21T09:00:00' }], riskLevel: 'low', attachments: 4 },
  102. { orderNo: 'SO2024120005', customerName: '保利·天汇', customerPhone: '028-88880005', customerAddress: '成都市锦江区保利天汇', customerType: 'commercial', projectName: '保利天汇售楼处软装', items: [{ productId: 'p001', productName: '定制遮光窗帘', category: '窗帘', specification: '宽2.5m×高2.8m', quantity: 20, unit: '幅', unitPrice: 580, totalPrice: 11600 }], status: 'pending', totalAmount: 34600, paidAmount: 0, paymentMethod: '到货付款', salesPersonId: 'u002', salesPersonName: '李晓燕', riskLevel: 'high', attachments: 1 },
  103. { orderNo: 'SO2024120006', customerName: '王女士', customerPhone: '186-6666-7777', customerAddress: '广州市天河区珠江新城168号', customerType: 'residential', projectName: '珠江新城私宅软装', items: [{ productId: 'p005', productName: '意式真皮沙发', category: '沙发', specification: '3+2+1人座', quantity: 1, unit: '套', unitPrice: 18800, totalPrice: 18800 }], status: 'completed', totalAmount: 23440, paidAmount: 23440, paymentMethod: '全款', salesPersonId: 'u001', salesPersonName: '张明华', reviewHistory: [{ id: 'rh004', action: 'approved', reviewerId: 'u003', reviewerName: '王建国', comment: '通过。', timestamp: '2024-11-16T11:00:00' }], riskLevel: 'low', attachments: 6 },
  104. { orderNo: 'SO2024120007', customerName: '龙湖·星悦荟', customerPhone: '023-88880007', customerAddress: '重庆市渝中区龙湖星悦荟', customerType: 'commercial', projectName: '龙湖星悦荟商业空间', items: [{ productId: 'p002', productName: '北欧风棉麻窗帘', category: '窗帘', specification: '宽2m×高2.5m', quantity: 30, unit: '幅', unitPrice: 380, totalPrice: 11400 }], status: 'draft', totalAmount: 16840, paidAmount: 0, paymentMethod: '月结', salesPersonId: 'u001', salesPersonName: '张明华', riskLevel: 'medium', attachments: 0 },
  105. ];
  106. for (const o of orders) {
  107. try {
  108. const Order = Parse.Object.extend('Order');
  109. const obj = new Order();
  110. obj.set('orderNo', o.orderNo);
  111. obj.set('customerName', o.customerName);
  112. obj.set('customerPhone', o.customerPhone);
  113. obj.set('customerAddress', o.customerAddress);
  114. obj.set('customerType', o.customerType);
  115. obj.set('projectName', o.projectName);
  116. obj.set('items', o.items);
  117. obj.set('status', o.status);
  118. obj.set('totalAmount', o.totalAmount);
  119. obj.set('paidAmount', o.paidAmount);
  120. obj.set('paymentMethod', o.paymentMethod);
  121. obj.set('salesPersonId', o.salesPersonId);
  122. obj.set('salesPersonName', o.salesPersonName);
  123. obj.set('riskLevel', o.riskLevel);
  124. obj.set('attachments', o.attachments);
  125. obj.set('isDeleted', false);
  126. if (o.isInstallment) obj.set('isInstallment', true);
  127. if (o.reviewHistory) obj.set('reviewHistory', o.reviewHistory);
  128. if (o.lastRejectedReason) obj.set('lastRejectedReason', o.lastRejectedReason);
  129. await obj.save(null, { useMasterKey: true });
  130. } catch (e: any) { console.log(` Order FAIL: ${o.orderNo} — ${e.message}`); }
  131. await sleep(100);
  132. }
  133. console.log(' Orders: 7 done');
  134. // ===== 5. 财务记录 =====
  135. const finances = [
  136. { orderNo: 'SO2024120002', customerName: '林女士', orderDate: '2024-11-28', dueDate: '2024-11-28', receivable: 22800, received: 22800, difference: 0, status: 'normal', commission: 1368, salesPerson: '李晓燕', verified: true },
  137. { orderNo: 'SO2024120004', customerName: '陈先生', orderDate: '2024-11-20', dueDate: '2024-12-20', receivable: 12360, received: 9270, difference: 3090, status: 'partial', commission: 741.6, salesPerson: '张明华', verified: false },
  138. { orderNo: 'SO2024120006', customerName: '王女士', orderDate: '2024-11-15', dueDate: '2024-11-15', receivable: 23440, received: 23440, difference: 0, status: 'normal', commission: 1406.4, salesPerson: '张明华', verified: true },
  139. { orderNo: 'SO2024110001', customerName: '融创·一号院', orderDate: '2024-11-01', dueDate: '2024-11-30', receivable: 68000, received: 45000, difference: 23000, status: 'overdue', commission: 4080, salesPerson: '赵芳', verified: false },
  140. { orderNo: 'SO2024110002', customerName: '孙先生', orderDate: '2024-11-05', dueDate: '2024-12-05', receivable: 8800, received: 8800, difference: 0, status: 'normal', commission: 528, salesPerson: '李晓燕', verified: true },
  141. ];
  142. for (const f of finances) {
  143. try {
  144. const FR = Parse.Object.extend('FinanceRecord');
  145. const obj = new FR();
  146. obj.set('orderNo', f.orderNo);
  147. obj.set('customerName', f.customerName);
  148. obj.set('orderDate', f.orderDate);
  149. obj.set('dueDate', f.dueDate);
  150. obj.set('receivable', f.receivable);
  151. obj.set('received', f.received);
  152. obj.set('difference', f.difference);
  153. obj.set('status', f.status);
  154. obj.set('commission', f.commission);
  155. obj.set('salesPerson', f.salesPerson);
  156. obj.set('verified', f.verified);
  157. await obj.save(null, { useMasterKey: true });
  158. } catch (e: any) { console.log(` Finance FAIL: ${f.orderNo} — ${e.message}`); }
  159. await sleep(100);
  160. }
  161. console.log(' Finance: 5 done');
  162. // ===== 6. 消息 =====
  163. const messages = [
  164. { type: 'approval', title: '订单待您审核', content: '订单 SO2024120001 已提交,请及时审核。', isRead: false, linkPath: '/approval', relatedId: 'o001', recipientId: 'u003' },
  165. { type: 'approval', title: '订单待您审核', content: '订单 SO2024120005 已提交,请及时审核。', isRead: false, linkPath: '/approval', relatedId: 'o005', recipientId: 'u003' },
  166. { type: 'finance', title: '账款逾期提醒', content: '融创·一号院订单逾期未收款 ¥23,000,请跟进。', isRead: false, linkPath: '/finance', relatedId: 'f004', recipientId: 'u004' },
  167. { type: 'order', title: '订单已审核通过', content: '您的订单 SO2024120002 已通过审核。', isRead: true, linkPath: '/orders', relatedId: 'o002', recipientId: 'u002' },
  168. { type: 'order', title: '订单被打回', content: '订单 SO2024120003 被审核打回。', isRead: true, linkPath: '/orders', relatedId: 'o003', recipientId: 'u006' },
  169. { type: 'system', title: '系统维护通知', content: '系统将于12月10日升级维护。', isRead: true, linkPath: null, relatedId: null, recipientId: null },
  170. ];
  171. for (const m of messages) {
  172. try {
  173. const Msg = Parse.Object.extend('Message');
  174. const obj = new Msg();
  175. obj.set('type', m.type);
  176. obj.set('title', m.title);
  177. obj.set('content', m.content);
  178. obj.set('isRead', m.isRead);
  179. if (m.linkPath) obj.set('linkPath', m.linkPath);
  180. if (m.relatedId) obj.set('relatedId', m.relatedId);
  181. if (m.recipientId) obj.set('recipientId', m.recipientId);
  182. await obj.save(null, { useMasterKey: true });
  183. } catch (e: any) { console.log(` Message FAIL: ${m.title} — ${e.message}`); }
  184. await sleep(100);
  185. }
  186. console.log(' Messages: 6 done');
  187. console.log('\n=== 种入完成 ===');
  188. }
  189. seed().catch(e => { console.error(e); process.exit(1); });