微信小程序版 · 云开发配置 / 数据库初始化 / 云函数模板 / 4 周冲刺 / 审核清单 | → MVP-FS 飞书版
TL 1 人 4 周完成开发+审核+上线,详见 MVP-WX 实施方案 §7
需求确认 + UI 原型
云环境搭建
数据库初始化
框架搭建
客户端核心功能
下单/报价/支付
验收/NPS
L1→L8 跑通
师傅端打卡拍照
管理端派单看板
异常监控
L6 全流程
AI 云函数
全流程联调
提交审核
上线首单
微信云开发 CloudBase:零运维、免域名、免 SSL
mp.weixin.qq.com → 注册 → 服务号/小程序 → 企业认证(¥300/年)→ 获取 AppID
微信开发者工具 → 打开小程序项目 → 云开发 → 创建环境 weavely-mvp
基础版(免费额度,日均<30单够用)或 专业版(¥40/月,额度更高)
weavely-mvp/ ├── miniprogram/ # 小程序前端代码 │ ├── pages/ │ │ ├── customer/ # 客户端页面(8页) │ │ ├── worker/ # 师傅端页面(6页) │ │ └── admin/ # 管理端页面(10页) │ ├── components/ # 公共组件 │ ├── utils/ # 工具函数 │ └── app.js # 入口(角色切换逻辑) ├── cloudfunctions/ # 云函数 │ ├── createOrder/ # L1 创建订单 │ ├── generateQuote/ # L3 生成报价 │ ├── assignWorker/ # L4 派单 │ ├── workerCheckin/ # L6 打卡 │ ├── uploadPhotos/ # L6 照片上传 │ ├── acceptOrder/ # L7 验收 │ ├── npsSurvey/ # L8 NPS推送 │ └── weeklyReport/ # 周报生成 └── project.config.json # 项目配置
// app.js - 三端角色切换
App({
globalData: {
role: null, // 'customer' | 'worker' | 'admin'
openid: null,
userInfo: null
},
onLaunch() {
// 微信登录获取 openid
wx.cloud.callFunction({ name: 'login' }).then(res => {
this.globalData.openid = res.result.openid
// 根据openid查询用户角色(首次进入选择角色)
this.checkRole()
})
},
checkRole() {
const db = wx.cloud.database()
db.collection('users').where({ openid: this.globalData.openid }).get().then(res => {
if (res.data.length > 0) {
this.globalData.role = res.data[0].role
this.gotoHome()
} else {
// 首次进入,跳转角色选择页
wx.redirectTo({ url: '/pages/role-select/role-select' })
}
})
}
})
6 个集合,字段与 MVP-FS 完全一致(snake_case),可互导互迁
| 集合名 | 对应 FS 表 | 字段数 | 主键 | 说明 |
|---|---|---|---|---|
orders | CPT-Lite | 40 | order_id | 订单跟踪(8阶段状态机) |
quotes | QSV-Lite | 15 | quote_id | 报价记录 |
workers | WPL-Lite | 8 | worker_id | 师傅台账 |
customers | CPL-Lite | 10 | customer_id | 客户台账 |
services | SCL-Lite | 7 | category_id | 品类费率 |
buildings | BPL-Lite | 8 | building_id | 楼宇系数 |
// cloudfunctions/initSeed/index.js - 种子数据初始化
const cloud = require('wx-server-sdk')
cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV })
const db = cloud.database()
exports.main = async (event, context) => {
// 师傅种子数据(≥3)
const workers = [
{ worker_id: 'WK-001', name: '陈师傅', contact: '+852-91234567', level: 'L2',
skill_categories: ['衣柜','橱柜'], total_orders: 0, avg_rating: 5.0, status: '在职' },
{ worker_id: 'WK-002', name: '李师傅', contact: '+852-98765432', level: 'L3',
skill_categories: ['床','餐桌','书柜'], total_orders: 0, avg_rating: 5.0, status: '在职' },
{ worker_id: 'WK-003', name: '王师傅', contact: '+852-95558888', level: 'L1',
skill_categories: ['衣柜','床'], total_orders: 0, avg_rating: 5.0, status: '在职' }
]
// 品类费率(≥5)
const services = [
{ category_id: 'CAT-01', name: '衣柜', sub_category: 'PAX/定制', base_fee: 600,
base_work_hours: 3.0, cap_price: 3000, skill_required: 'L1+' },
{ category_id: 'CAT-02', name: '橱柜', sub_category: '整体/定制', base_fee: 800,
base_work_hours: 4.0, cap_price: 5000, skill_required: 'L2+' },
{ category_id: 'CAT-03', name: '床', sub_category: '框架/储物', base_fee: 400,
base_work_hours: 1.5, cap_price: 1500, skill_required: 'L1+' },
{ category_id: 'CAT-04', name: '餐桌', sub_category: '标准/伸缩', base_fee: 300,
base_work_hours: 1.0, cap_price: 1000, skill_required: 'L1+' },
{ category_id: 'CAT-05', name: '书柜', sub_category: '墙挂/落地', base_fee: 500,
base_work_hours: 2.5, cap_price: 2000, skill_required: 'L2+' }
]
// 楼宇系数(≥5)
const buildings = [
{ building_id: 'BLD-001', estate_name: '沙田第一城', address: '沙田', building_type: '私人住宅',
floor_factor: 1.0, has_elevator: true, parking_note: '有访客车位', access_note: '' },
{ building_id: 'BLD-002', estate_name: '太古城', address: '鲗鱼涌', building_type: '私人屋苑',
floor_factor: 1.2, has_elevator: true, parking_note: '需预约', access_note: '大件走货梯' },
{ building_id: 'BLD-003', estate_name: '公屋-彩虹邨', address: '黄大仙', building_type: '公屋',
floor_factor: 1.5, has_elevator: true, parking_note: '无车位', access_note: '走后楼梯' },
{ building_id: 'BLD-004', estate_name: '海怡半岛', address: '鸭脷洲', building_type: '私人屋苑',
floor_factor: 1.1, has_elevator: true, parking_note: '有访客车位', access_note: '' },
{ building_id: 'BLD-005', estate_name: '村屋-锦田', address: '元朗', building_type: '村屋',
floor_factor: 0.9, has_elevator: false, parking_note: '门口停车', access_note: '楼梯搬运' }
]
// 批量插入
for (const w of workers) await db.collection('workers').add({ data: w })
for (const s of services) await db.collection('services').add({ data: s })
for (const b of buildings) await db.collection('buildings').add({ data: b })
return { success: true, workers: workers.length, services: services.length, buildings: buildings.length }
}
cloudfunctions/initSeed → 上传并部署 → 在云开发控制台调用即可完成种子数据初始化。
MVP 阶段三端合一(一个小程序+角色切换),共 ~24 页
| 页面 | 阶段 |
|---|---|
| 首页/扫码入口 | L1 |
| 下单表单 | L1→L2 |
| 报价查看 | L3→L4 |
| 支付页 | L4 |
| 订单进度 | L4→L7 |
| 验收签字 | L7 |
| NPS评价 | L8 |
| 历史订单 | — |
| 页面 | 阶段 |
|---|---|
| 接单列表 | L4 |
| 订单详情 | L4→L6 |
| 打卡页(3按钮) | L6 |
| 拍照页 | L6 |
| 工时记录 | L6 |
| 异常上报 | L6 |
工作台 / 需求确认(L2) / 报价审核(L3) / 派单管理(L4) / 配送协调(L5) / 异常监控 / 回访管理(L8) / 数据看板 / 师傅管理 / 客户管理
8 个云函数,复制即可部署
// cloudfunctions/createOrder/index.js
const cloud = require('wx-server-sdk')
cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV })
const db = cloud.database()
exports.main = async (event, context) => {
const { OPENID } = cloud.getWXContext()
const today = new Date().toISOString().slice(0,10).replace(/-/g,'')
// 生成订单编号 ORD-YYYYMMDD-NNN
const count = await db.collection('orders').count()
const seq = String(count.total + 1).padStart(3, '0')
const order_id = `ORD-${today}-${seq}`
// AI 提炼需求(如有自由文本)
let lead_demand = event.lead_demand || ''
if (event.raw_text && event.raw_text.length > 20) {
try {
const ai = await cloud.callFunction({ name: 'aiExtract', data: { text: event.raw_text } })
lead_demand = ai.result.extracted || lead_demand
} catch (e) { console.log('AI降级,使用原始文本') }
}
const order = {
order_id,
current_stage: 'L1',
order_status: '进行中',
created_at: db.serverDate(),
updated_at: db.serverDate(),
lead_source: event.lead_source || 'SCENE01线上',
customer_name: event.customer_name,
customer_contact: event.customer_contact,
lead_demand,
lead_time: db.serverDate(),
_openid: OPENID,
_ai_generated: lead_demand !== event.lead_demand
}
const res = await db.collection('orders').add({ data: order })
// 发送订阅消息通知 OL
await cloud.callFunction({ name: 'notifyAdmin', data: { order_id, type: 'new_order' } })
return { success: true, order_id, _id: res._id }
}
// cloudfunctions/generateQuote/index.js
const cloud = require('wx-server-sdk')
cloud.init()
const db = cloud.database()
exports.main = async (event, context) => {
const { order_id } = event
const order = (await db.collection('orders').where({ order_id }).get()).data[0]
// 查主数据
const service = (await db.collection('services').where({ name: order.category }).get()).data[0]
const building = order.building_id
? (await db.collection('buildings').where({ building_id: order.building_id }).get()).data[0]
: { floor_factor: 1.0 }
const worker = order.assigned_worker_id
? (await db.collection('workers').where({ worker_id: order.assigned_worker_id }).get()).data[0]
: { level: 'L1' }
// 系数映射
const levelFactor = { 'L1': 1.0, 'L2': 1.2, 'L3': 1.4 }[worker.level] || 1.0
// QSV-Lite 报价公式
const base_fee = service.base_fee
const complexity_factor = event.complexity_factor || 1.0
const building_factor = building.floor_factor
const worker_level_factor = levelFactor
const extra_work_fee = event.extra_work_fee || 0
const discount = event.discount || 0
const cap_price = service.cap_price
const subtotal = base_fee * complexity_factor * building_factor * worker_level_factor + extra_work_fee
const before_cap = subtotal - discount
const final_amount = Math.min(before_cap, cap_price)
const cap_triggered = before_cap > cap_price
// AI 报价建议(可选)
let ai_suggestion = null
try {
const ai = await cloud.callFunction({
name: 'aiQuoteAdvice',
data: { order_id, final_amount, category: order.category, history: true }
})
ai_suggestion = ai.result
} catch (e) { console.log('AI报价建议降级') }
// 生成报价编号
const today = new Date().toISOString().slice(0,10).replace(/-/g,'')
const count = await db.collection('quotes').count()
const seq = String(count.total + 1).padStart(3, '0')
const quote_id = `QTE-${today}-${seq}`
const quote = {
quote_id, order_id, category: order.category,
base_fee, complexity_factor, building_factor, worker_level_factor,
extra_work_fee, subtotal, discount, cap_price, final_amount,
quote_detail_json: JSON.stringify({ cap_triggered, ai_suggestion }),
quote_time: db.serverDate(),
quote_status: '待确认'
}
await db.collection('quotes').add({ data: quote })
// 更新订单阶段
await db.collection('orders').where({ order_id }).update({
data: { current_stage: 'L3', quote_id, quote_amount: final_amount, updated_at: db.serverDate() }
})
return { success: true, quote_id, final_amount, cap_triggered, ai_suggestion }
}
// cloudfunctions/workerCheckin/index.js
const cloud = require('wx-server-sdk')
cloud.init()
const db = cloud.database()
exports.main = async (event, context) => {
const { order_id, action, gps } = event
// action: 'depart' | 'arrive' | 'complete'
const fieldMap = {
depart: 'worker_depart_time',
arrive: 'worker_arrive_time',
complete: 'work_complete_time'
}
const update = {
updated_at: db.serverDate(),
[fieldMap[action]]: db.serverDate()
}
if (gps) update.gps_location = gps
// 完工时自动计算工时
if (action === 'complete') {
const order = (await db.collection('orders').where({ order_id }).get()).data[0]
if (order.worker_arrive_time) {
const arrive = new Date(order.worker_arrive_time)
const complete = new Date()
update.actual_work_hours = Math.round((complete - arrive) / 3600000 * 10) / 10 // 1位小数
}
}
await db.collection('orders').where({ order_id }).update({ data: update })
// 推送通知给客户
await cloud.callFunction({ name: 'notifyCustomer', data: { order_id, action } })
return { success: true, action, work_hours: update.actual_work_hours }
}
// 客户端调起支付
wx.cloud.callFunction({
name: 'createPayment',
data: { order_id, amount: final_amount }
}).then(res => {
const payment = res.result.payment
wx.requestPayment({
...payment,
success: () => {
// 支付成功,云函数已收到回调,更新 payment_status
wx.showToast({ title: '支付成功' })
wx.navigateTo({ url: '/pages/customer/progress?order_id=' + order_id })
},
fail: (err) => {
console.log('支付失败', err)
}
})
})
// cloudfunctions/createPayment/index.js - 云函数创建支付订单
const cloud = require('wx-server-sdk')
cloud.init()
const db = cloud.database()
exports.main = async (event, context) => {
const { order_id, amount } = event
const res = await cloud.cloudPay.unifiedOrder({
body: `WEAVELY安装服务-${order_id}`,
outTradeNo: order_id,
spbillCreateIp: '127.0.0.1',
subMchId: '你的子商户号',
totalFee: Math.round(amount * 100), // 分
envId: cloud.DYNAMIC_CURRENT_ENV,
functionName: 'paymentCallback' // 支付回调云函数
})
// 记录支付订单号
await db.collection('orders').where({ order_id }).update({
data: { payment_transaction_id: res.payment.outTradeNo }
})
return { payment: res.payment }
}
数据模型完全一致,字段名 snake_case 直接映射,零成本互导
// cloudfunctions/importFromCSV/index.js
// 接收飞书导出的CSV JSON,批量导入
const cloud = require('wx-server-sdk')
cloud.init()
const db = cloud.database()
exports.main = async (event, context) => {
const { collection, rows } = event
// collection: 'orders' | 'workers' | ...
// rows: [{ order_id: '...', current_stage: '...', ... }, ...]
let success = 0, fail = 0
for (const row of rows) {
try {
await db.collection(collection).add({ data: row })
success++
} catch (e) { fail++ }
}
return { success, fail, total: rows.length }
}
// cloudfunctions/exportToCSV/index.js
const cloud = require('wx-server-sdk')
cloud.init()
const db = cloud.database()
exports.main = async (event, context) => {
const { collection } = event
const res = await db.collection(collection).limit(1000).get()
// 转CSV
const headers = Object.keys(res.data[0] || {})
const csv = [
headers.join(','),
...res.data.map(row =>
headers.map(h => JSON.stringify(row[h] ?? '')).join(',')
)
].join('\n')
return { csv, count: res.data.length }
}
scripts/mvp-data-checker.py 校验数据质量,脚本对 FS 和 WX 通用(字段定义一致)。
提交微信审核前逐项确认,避免被拒延误上线
客户下单 → OL 确认 → 报价 → 支付 → 派单 → 配送 → 师傅打卡拍照 → 验收签字 → NPS
需求超能力转介 / 配送延误 PAUSE / 验收整改回退 L6 / NPS≤6 低分跟进
需求提炼 / 报价建议 / 师傅推荐 / 周报生成,AI 不可用时降级正常
沙箱环境支付成功 → 回调更新 payment_status → 订单阶段自动流转 L4
新订单通知 OL / 阶段变更通知客户 / NPS 推送 / 超时预警
通用协作规范见 MVP-FS §4,以下为 WX 特有
| 场景 | WX 做法 | 责任人 |
|---|---|---|
| Bug 反馈 | 小程序 → 截图 → 飞书群 @TL,30min 内响应 | 发现者/TL |
| 云函数部署 | TL 独占,禁止 OL 直接改云函数 | TL |
| 数据库变更 | 加字段 TL 确认即可;删字段/改字段需 PO 确认 | TL/PO |
| 小程序版本发布 | 体验版 → 审核 → 正式版,版本号 V1.0.x | TL |
| AI 提示词调整 | DT 维护提示词库,版本管理 | DT/TL |
| 数据备份 | 每周五导出 CSV → 飞书多维表同步(双备份) | TL/OL |