
家政小程序开发不是"写代码"一件事。它是一条链:需求 → 确认 → 开发 → 上线 → 迭代。这条链上任何一个环节含糊,交付就会变成拉锯。
笔者所在团队在长沙做过几套家政类小程序。技术栈是微信小程序 + Spring Boot 3,业务覆盖下单、派单、结算、评价。踩过的坑很集中:需求口头化、状态流转失控、上线当天才发现类目没开通。
本文给出一套可复用的五阶段交付流程。含环境基线表、功能清单模板、可运行的订单状态机、k6 压测脚本与上线检查清单。读者可照着落地。
一、交付流程总览 📌
阶段划分与交付物
流程透明的前提是里程碑可核对。每个阶段必须有"可看、可签、可测"的东西。
| 阶段 | 参考周期 | 关键交付物 | 验收方式 |
|---|---|---|---|
| 需求沟通 | 3~5 个工作日 | 角色清单、业务流程图、需求条目表 | 客户逐条确认 |
| 功能确认 | 3~7 个工作日 | 交互原型、功能清单、边界说明 | 原型走查会 |
| 开发测试 | 4~8 周 | 可运行版本、测试报告、压测报告 | 冒烟 + 回归用例 |
| 培训上线 | 3~5 个工作日 | 提审材料、操作手册、培训录屏 | 灰度观察 7 天 |
| 售后迭代 | 长期 | 工单记录、迭代排期、月度复盘 | 迭代验收单 |
表中周期是参考值。功能越多,确认阶段越长------这一点要在报价前说清楚。
环境基线
版本必须写死,否则"我本地能跑"会反复发生。
| 组件 | 版本 | 用途 |
|---|---|---|
| 操作系统 | Ubuntu 22.04 LTS | 服务端与构建机 |
| JDK | 17.0.10 LTS | Spring Boot 3.x 最低要求 |
| Spring Boot | 3.2.5 | 后端主框架 |
| MySQL | 8.0.36 | 业务库,字符集 utf8mb4 |
| Redis | 7.2.4 | 登录态、缓存、分布式锁 |
| Nginx | 1.24.0 | 反向代理与灰度分流 |
| Node.js | 20.11.1 LTS | 小程序构建与 CI |
| 微信开发者工具 | 1.06.2405020 | 小程序调试 |
| 小程序基础库 | 3.4.x(兼容下限 2.30.0) | 客户端运行时 |
| Docker | 25.0.3 | 镜像构建 |
| k6 | 0.49.0 | 压测 |
环境验证命令:
bash
java -version && mvn -v | head -n 1
mysql --version && redis-cli -v && nginx -v
node -v && k6 version
二、需求沟通:把口头描述变成可核对的输入
三张清单
家政业务的角色比普通电商多。沟通时按角色铺开,漏项最少。
| 清单 | 内容 | 产出 |
|---|---|---|
| 角色清单 | 用户、服务人员、客服、调度、财务 | 每类角色的权限边界 |
| 流程清单 | 下单 → 派单 → 上门 → 完成 → 结算 | 状态与节点责任 |
| 数据清单 | 客户、地址、服务项目、价格、工时 | 字段与校验规则 |
角色清单建议当面过。远程视频容易漏掉"谁来改价""谁能强制取消"这类权限问题。
把模糊需求写成验收条件
客户原话往往是这样:
"下单后要能改时间,改完要通知阿姨。"
这句话至少藏了四个问题:改几次?距服务开始多久禁止改?改成怎样算合法?通知失败怎么办?
把它写成可执行的验收条件:
gherkin
功能: 用户改约
场景: 服务开始前 2 小时改约
假如 订单状态为 "已接单"
并且 距服务开始时间大于 2 小时
当 用户提交新的服务时间
那么 订单状态回退为 "待接单"
并且 生成一条调度变更记录
并且 向原服务人员推送站内通知
这一步做完,需求沟通就算闭环。后面所有争议都能回到这张表。
三、功能确认:原型、清单与边界
功能清单与优先级
用 MoSCoW 分级,首期只做 Must。Should 与 Could 写进二期,避免无限加需求。
| 端 | 模块 | 功能 | 优先级 |
|---|---|---|---|
| 用户端 | 交易 | 服务分类、预约下单、在线支付、改约取消 | Must |
| 用户端 | 信任 | 服务人员资质、评价与晒单 | Should |
| 服务端 | 履约 | 接单、签到、服务拍照、收入明细 | Must |
| 管理后台 | 调度 | 派单、改派、异常处理 | Must |
| 管理后台 | 经营 | 订单看板、对账结算、优惠券 | Should |
| 管理后台 | 增长 | 拼团、老带新、会员等级 | Could |
订单状态机:把流程写进代码
状态流转失控是家政项目最常见的线上故障。把流转规则集中管理,非法操作直接拒绝。
java
import java.util.*;
/**
* 家政订单状态机(JDK 17 可直接运行)
* 原则:状态流转集中定义,非法流转直接抛异常。
*/
public class OrderStateMachine {
enum State { CREATED, ACCEPTED, ON_SERVICE, FINISHED, CANCELED }
// key -> 可到达的状态集合
private static final Map<State, Set<State>> TRANSFER = new EnumMap<>(State.class);
static {
TRANSFER.put(State.CREATED, EnumSet.of(State.ACCEPTED, State.CANCELED));
// 已接单可回退到待接单:对应"用户改约"
TRANSFER.put(State.ACCEPTED, EnumSet.of(State.CREATED, State.ON_SERVICE, State.CANCELED));
TRANSFER.put(State.ON_SERVICE, EnumSet.of(State.FINISHED));
TRANSFER.put(State.FINISHED, EnumSet.noneOf(State.class));
TRANSFER.put(State.CANCELED, EnumSet.noneOf(State.class));
}
public static State next(State from, State to) {
if (!TRANSFER.get(from).contains(to)) {
throw new IllegalStateException("非法流转: " + from + " -> " + to);
}
return to;
}
public static void main(String[] args) {
State cur = State.CREATED;
cur = next(cur, State.ACCEPTED); // 服务人员接单
cur = next(cur, State.CREATED); // 用户改约,回退待接单
cur = next(cur, State.ACCEPTED); // 再次被接单
cur = next(cur, State.ON_SERVICE); // 开始服务
cur = next(cur, State.FINISHED); // 服务完成,进入结算
System.out.println("最终状态: " + cur);
try {
next(cur, State.ACCEPTED); // 已完成后不能再接单
} catch (IllegalStateException e) {
System.out.println("拦截成功: " + e.getMessage());
}
}
}
边界确认
变更不可怕,可怕的是变更没记录。
| 变更类型 | 处理方式 |
|---|---|
| 文案、样式 | 计入当期,不额外计费 |
| 字段增减 | 评估后计入当期或下期,出书面确认 |
| 新增流程节点 | 单独评估工作量,签变更单 |
四、开发测试:分支策略、联调与压测

分支与提测节奏
| 分支 | 用途 | 合并规则 |
|---|---|---|
| main | 生产可用版本 | 只接受 release 合并 |
| dev | 集成分支 | 每日构建,可部署测试环境 |
| feature/* | 单人功能 | 自测通过后提 PR |
每个 PR 必须附自测说明。没有自测说明的 PR 不进入测试环境。
小程序端请求封装
登录态失效、超时、弱网是家政场景的高频问题。统一封装比散落各处更省事。
javascript
// utils/request.js ------ 统一请求封装:登录态、超时、自动重试
const BASE_URL = 'https://api.example.com';
function request(options, retry = 1) {
return new Promise((resolve, reject) => {
wx.request({
url: BASE_URL + options.url,
method: options.method || 'GET',
data: options.data || {},
timeout: 8000, // 8s 超时,避免用户长时间白屏
header: {
'content-type': 'application/json',
'X-Token': wx.getStorageSync('token') || ''
},
success(res) {
if (res.statusCode === 401) { // 登录态失效:静默重登后重放一次
if (retry > 0) {
return relogin()
.then(() => request(options, retry - 1))
.then(resolve)
.catch(reject);
}
return reject(new Error('AUTH_EXPIRED'));
}
if (res.statusCode >= 500 && retry > 0) { // 服务端抖动,短延迟重试
return setTimeout(() => {
request(options, retry - 1).then(resolve).catch(reject);
}, 300);
}
res.statusCode === 200
? resolve(res.data)
: reject(new Error('HTTP_' + res.statusCode));
},
fail(err) {
retry > 0
? request(options, retry - 1).then(resolve).catch(reject)
: reject(err);
}
});
});
}
function relogin() {
return new Promise((resolve, reject) => {
wx.login({
success: ({ code }) => wx.request({
url: BASE_URL + '/auth/login',
method: 'POST',
data: { code },
success: r => {
wx.setStorageSync('token', r.data.token);
resolve();
},
fail: reject
}),
fail: reject
});
});
}
module.exports = { request };
压测与性能基线
用 k6 做接口压测,并设置门槛值,不达标就视为测试失败。
javascript
// scripts/order_load.js ------ k6 v0.49 压测脚本
import http from 'k6/http';
import { check, sleep } from 'k6';
export const options = {
stages: [
{ duration: '30s', target: 100 }, // 30 秒爬升到 100 并发
{ duration: '60s', target: 100 }, // 稳态压测 1 分钟
{ duration: '10s', target: 0 },
],
thresholds: {
http_req_duration: ['p(95)<300'], // 95 分位响应时间不超过 300ms
http_req_failed: ['rate<0.01'], // 错误率低于 1%
},
};
const TOKEN = __ENV.TOKEN;
export default function () {
const res = http.get(
'https://api.example.com/orders?page=1&size=10',
{ headers: { 'X-Token': TOKEN, 'content-type': 'application/json' } }
);
check(res, { '状态码 200': (r) => r.status === 200 });
sleep(1);
}
测试环境:4C8G 单实例,Spring Boot 3.2.5,MySQL 8.0.36(4C8G),Redis 7.2.4,k6 100 并发、持续 60s。以下为该项目某轮的参考值,仅用于说明优化方向。
| 接口 | 优化前 P95 | 优化后 P95 | 优化手段 |
|---|---|---|---|
| 订单列表 | 620 ms | 180 ms | 分页 + 联合索引 + Redis 缓存 60s |
| 服务人员列表 | 850 ms | 240 ms | 距离预计算落库,不做实时计算 |
| 下单接口 | 520 ms | 210 ms | Redis 预扣名额 + 异步落库 |
| 支付回调 | 90 ms | 85 ms | 幂等表去重 |
真机兼容也要过一遍。至少覆盖 iOS 与 Android 各两台主流机型,以及基础库 2.30.0 的下限版本。
五、培训上线:提审、灰度与回滚 🔧
上线前检查清单
| 检查项 | 判定标准 | 责任人 |
|---|---|---|
| 类目与资质 | 生活服务类目已开通,营业执照与服务协议已上传 | 客户 + 项目经理 |
| 支付 | 商户号绑定成功,回调可公网访问,退款链路实测通过 | 后端 |
| 隐私合规 | 用户隐私保护指引已配置,手机号等字段已声明 | 前端 |
| 域名 | 已备案 + HTTPS,并加入 request 合法域名 | 运维 |
| 数据 | 生产库初始化脚本已执行,测试数据已清理 | 后端 |
| 备份 | 每日全量 + binlog,已演练一次恢复 | 运维 |
清单不过,不提审。这一条能挡掉绝大多数"上线当天出事"。
灰度发布与回滚
先放 10% 流量,观察 60 分钟,再全量。健康检查失败自动回滚。
yaml
# .github/workflows/release.yml ------ 灰度发布与回滚(节选)
name: release
on:
push:
tags: ['v*']
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: 构建镜像
run: docker build -t registry.example.com/housekeeping-api:${{ github.ref_name }} .
- name: 发布新版本
run: kubectl set image deploy/api api=registry.example.com/housekeeping-api:${{ github.ref_name }}
- name: 健康检查,失败即回滚
run: |
sleep 60
curl -sf https://api.example.com/actuator/health || kubectl rollout undo deploy/api
培训安排
软件上线只是起点。没人会用,系统就是摆设。
| 场次 | 对象 | 内容 | 时长 |
|---|---|---|---|
| 第 1 场 | 管理者 + 客服 | 订单看板、异常处理、对账 | 2 h |
| 第 2 场 | 调度 | 派单、改派、改约、超时催办 | 2 h |
| 第 3 场 | 服务人员 | 接单、签到、收入查看 | 1 h |
| 补课 | 未到场人员 | 录屏回放 + 一对一答疑 | 30 min |
培训必须留录屏和操作手册。人员流动是常态,文档能省下大量重复答疑。
六、售后迭代:监控、响应与版本节奏 📈
监控指标与告警阈值
| 指标 | 告警阈值 | 处理动作 |
|---|---|---|
| 接口 P99 | 持续 5 min > 800 ms | 通知值班,排查慢 SQL |
| 5xx 错误率 | 持续 3 min > 1% | 触发回滚预案 |
| 支付回调失败 | 10 min 内 ≥ 3 次 | 人工对账 + 补单 |
| 小程序 JS 错误 | 单版本 > 20 次/日 | 前端当日修复 |
告警要有人接。只有告警没有值班,等于没有监控。
工单响应与迭代节奏
| 等级 | 示例 | 响应时间 | 处理方式 |
|---|---|---|---|
| P0 | 无法支付、下单白屏 | 30 分钟内 | 先回滚或降级,再定位 |
| P1 | 某机型页面错位 | 4 小时内 | 当日排期修复 |
| P2 | 文案、样式优化 | 2 个工作日 | 纳入下个迭代 |
| P3 | 新功能建议 | 5 个工作日 | 评估后进排期 |
迭代按双周一个小版本推进。每月做一次数据复盘,看下单转化、取消率与客诉量。用数据决定下个版本做什么,比拍脑袋可靠。
七、总结:三条可迁移的交付认知
第一,把需求写成验收条件。口头描述只是输入,可执行的场景才是需求。
第二,把业务规则写进代码。状态机、权限、计费规则集中管理,比散落在 if-else 里更容易维护。
第三,把上线当成流程而不是节点。检查清单、灰度、回滚预案、培训文档,缺一项都会在出问题时放大。
结语
本文所有配置与代码可在 Ubuntu 22.04 + JDK 17 + Spring Boot 3.2.5 + MySQL 8.0.36 环境下复现;小程序端依赖基础库 2.30.0 及以上,Node.js 20.11.1 用于构建。压测数据来自本地测试环境,请以自身业务量级重新测量。
你在这类交付里遇到的最大阻力是需求反复,还是上线后的运维响应?欢迎在评论区说说。
