// 种子数据:服务分类、客户、订单、公告
// 说明:仅用于初始化演示数据,真实环境可替换为数据库迁移/导入
// 服务列表对象结构: key(唯一标识)、name(显示名)、base(基础价,元)、desc(描述)
// svg(svg 字符串)、enabled(是否上架),排序 index
const serviceCatalog = [
{ key: 'express', name: '代拿快递', base: 4, desc: '菜鸟驿站/快递点代取', svg: ``, enabled: true, sort: 0 },
{ key: 'food', name: '代取外卖', base: 4, desc: '食堂/外卖柜代拿', svg: ``, enabled: true, sort: 1 },
{ key: 'buy', name: '帮我买', base: 5, desc: '超市便利店代购', svg: ``, enabled: true, sort: 2 },
{ key: 'send', name: '帮我送', base: 5, desc: '文件物品代送', svg: ``, enabled: true, sort: 3 },
{ key: 'print', name: '打印文印', base: 3, desc: '论文资料打印', svg: ``, enabled: true, sort: 4 },
{ key: 'queue', name: '排队代办', base: 6, desc: '代排队/代办事', svg: ``, enabled: true, sort: 5 },
{ key: 'more', name: '全部服务', base: 0, desc: '查看更多服务', svg: null, enabled: false, sort: 99 } // “全部服务”是占位入口,非真正下单项
]
const notices = [
'新学期上线:跨校区配送,最快 15 分钟送达',
'邀请好友注册,双方各得 5 元无门槛券'
]
// 将服务目录映射为完整的服务对象(与 store.createService 输出结构一致)
// 字段:id / key / name / icon(图标 key) / base / sort / status / desc / createdAt
function buildServices() {
return serviceCatalog.map((s, i) => ({
id: 'S' + String(1001 + i),
key: s.key,
name: s.name,
icon: s.key === 'more' ? 'service' : s.key, // 前端图标库以 key 命名,与服务 key 一致
base: s.base,
sort: s.sort !== undefined ? s.sort : i,
status: s.enabled === false ? 'inactive' : 'active',
desc: s.desc || '',
createdAt: daysAgoISO(45, 8, 0)
}))
}
const rawCustomers = [
{ name: '张伟', phone: '13800010001', campus: '东校区' },
{ name: '李娜', phone: '13800010002', campus: '东校区' },
{ name: '王芳', phone: '13800010003', campus: '西校区' },
{ name: '刘洋', phone: '13800010004', campus: '南校区' },
{ name: '陈静', phone: '13800010005', campus: '东校区' },
{ name: '杨帆', phone: '13800010006', campus: '西校区' },
{ name: '赵磊', phone: '13800010007', campus: '南校区' },
{ name: '周敏', phone: '13800010008', campus: '东校区' },
{ name: '吴强', phone: '13800010009', campus: '西校区' },
{ name: '徐丽', phone: '13800010010', campus: '东校区' },
{ name: '孙浩', phone: '13800010011', campus: '南校区' },
{ name: '马超', phone: '13800010012', campus: '西校区' }
]
const fromPool = ['菜鸟驿站(东门)', '南门外卖取餐柜', '全家便利店', '图文快印中心', '食堂二楼', '京东快递点']
const toPool = ['3 号宿舍楼 512', '图书馆 2 楼自习区', '教学楼 B305', '行政楼 208', '5 号宿舍楼 208', '实验楼 A101']
const notePool = ['两个包裹,帮忙拿一下', '瑞幸咖啡一杯,尽快', '矿泉水两瓶 + 纸巾一包', '双面黑白 20 页', '送到寝室楼下即可', '轻拿轻放,谢谢']
const riders = ['骑手·小林', '骑手·阿强', '骑手·大鹏', '骑手·阿杰']
const goodsPool = ['文件资料', '食品饮料', '生活用品', '数码产品', '其他']
// 状态分布(尽量贴近真实:待接单/配送中/已完成/已取消)
const statusPattern = [
'done', 'done', 'done', 'running', 'waiting', 'done',
'canceled', 'done', 'running', 'done', 'waiting', 'done'
]
const rewardPool = [0, 2, 2, 3, 3, 5, 8]
function daysAgoISO(days, hour = 10, minute = 0) {
const d = new Date()
d.setDate(d.getDate() - days)
d.setHours(hour, minute, 0, 0)
return d.toISOString()
}
function makeOrderId(iso, seq) {
const d = new Date(iso)
const y = d.getFullYear()
const m = String(d.getMonth() + 1).padStart(2, '0')
const day = String(d.getDate()).padStart(2, '0')
return `PT${y}${m}${day}${String(seq).padStart(3, '0')}`
}
// 表白墙种子数据(status: pending 待审核 / approved 已通过 / rejected 已驳回)
export function buildConfessions() {
const now = Date.now()
const min = 60 * 1000
const hr = 60 * min
const day = 24 * hr
const iso = (ms) => new Date(now - ms).toISOString()
return [
{
id: 'CF1001',
to: '图书馆三楼靠窗的女孩',
content: '每天下午都能看见你在那里自习,阳光落在你侧脸的样子真好看。今天终于鼓起勇气写下这段话,希望有机会认识你。',
author: '匿名',
anonymous: true,
theme: 0,
likes: 128,
status: 'approved',
comments: [
{ id: 'CM1001', name: '路人甲', content: '好浪漫,加油冲!', createdAt: iso(10 * min) },
{ id: 'CM1002', name: '同楼层', content: '我好像知道是谁~', createdAt: iso(6 * min) }
],
createdAt: iso(12 * min),
updatedAt: iso(6 * min)
},
{
id: 'CF1002',
to: '',
content: '大学四年很快,谢谢一路陪我熬夜赶due、一起吃食堂二楼麻辣烫的室友们。毕业不散,我们江湖再见!',
author: '阿泽',
anonymous: false,
theme: 2,
likes: 76,
status: 'approved',
comments: [],
createdAt: iso(3 * hr),
updatedAt: iso(3 * hr)
},
{
id: 'CF1003',
to: '篮球场穿7号球衣的男生',
content: '你投三分的样子超帅,可惜我只敢在场边偷偷看。如果你也注意到了那个总来看球的女生,记得对我笑一下。',
author: '匿名',
anonymous: true,
theme: 1,
likes: 203,
status: 'approved',
comments: [
{ id: 'CM1003', name: '球队经理', content: '7号已阅(划掉)', createdAt: iso(20 * hr) }
],
createdAt: iso(1 * day),
updatedAt: iso(20 * hr)
},
{
id: 'CF1004',
to: '高数课代表',
content: '谢谢你每次都耐心给我讲题,其实我很多时候是故意问的……喜欢你,希望期末我们都能拿高分。',
author: '同班的你',
anonymous: false,
theme: 3,
likes: 54,
status: 'approved',
comments: [],
createdAt: iso(2 * day),
updatedAt: iso(2 * day)
},
{
id: 'CF1005',
to: '三食堂打饭的阿姨',
content: '谢谢您每次都给我多打一勺菜,大学最温暖的瞬间之一。(这条留给管理员审核演示)',
author: '匿名',
anonymous: true,
theme: 4,
likes: 0,
status: 'pending',
comments: [],
createdAt: iso(5 * min),
updatedAt: iso(5 * min)
}
]
}
export function buildSeed() {
const services = buildServices()
// 仅用上架中的服务参与派单(排除 more 占位入口)
const activeServices = services.filter((s) => s.status === 'active')
const customers = rawCustomers.map((c, i) => ({
id: 'U' + String(1001 + i),
name: c.name,
phone: c.phone,
campus: c.campus,
balance: Number((50 + i * 13.5).toFixed(2)),
coupons: (i % 4) + 1,
points: 120 + i * 65,
status: i % 7 === 0 ? 'disabled' : 'active',
orderCount: 0,
totalSpend: 0,
createdAt: daysAgoISO(40 - i, 9, (i * 7) % 60)
}))
const orders = []
let seq = 1
for (let i = 0; i < 30; i++) {
const cust = customers[i % customers.length]
const svc = activeServices[i % activeServices.length]
const status = statusPattern[i % statusPattern.length]
const reward = rewardPool[i % rewardPool.length]
const dayOffset = Math.floor(i / 3)
const iso = daysAgoISO(dayOffset, 9 + (i % 10), (i * 11) % 60)
const total = Number((svc.base + reward).toFixed(2))
orders.push({
id: makeOrderId(iso, seq++),
customerId: cust.id,
customerName: cust.name,
customerPhone: cust.phone,
campus: cust.campus,
serviceKey: svc.key,
type: svc.name,
status,
from: fromPool[i % fromPool.length],
to: toPool[i % toPool.length],
goods: goodsPool[i % goodsPool.length],
note: notePool[i % notePool.length],
baseFee: svc.base,
reward,
total,
riderName: status === 'waiting' ? null : riders[i % riders.length],
createdAt: iso,
updatedAt: iso
})
}
// 回填客户的下单数与消费额(仅统计已完成的有效消费)
for (const o of orders) {
const c = customers.find((x) => x.id === o.customerId)
if (!c) continue
c.orderCount += 1
if (o.status === 'done') c.totalSpend = Number((c.totalSpend + o.total).toFixed(2))
}
return {
services,
notices,
customers,
orders,
confessions: buildConfessions(),
seqCounter: seq
}
}