家政小程序开发从0到上线:五阶段交付流程与验收清单

家政小程序开发不是"写代码"一件事。它是一条链:需求 → 确认 → 开发 → 上线 → 迭代。这条链上任何一个环节含糊,交付就会变成拉锯。

笔者所在团队在长沙做过几套家政类小程序。技术栈是微信小程序 + 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 用于构建。压测数据来自本地测试环境,请以自身业务量级重新测量。

你在这类交付里遇到的最大阻力是需求反复,还是上线后的运维响应?欢迎在评论区说说。

相关推荐
伞伞悦读3 小时前
【第38期】Python 模块与包详解:import、from、模块搜索路径、包结构和 __init__
开发语言·python
只睡四小时3 小时前
Canvas 弹道联机实战:700 行 + 固定时间步长
python·websocket·html5·游戏开发·canvas
奇思妙想聪明勤奋的小羊4 小时前
DeepAgents第5章:子Agent 与上下文隔离—让 Agent学会委派
人工智能·python·学习·语言模型
lpfasd1234 小时前
2026年第38周GitHub趋势周报
python·科技·github
IZero074 小时前
Jev 与 Laya
python·语言模型
2601_962218615 小时前
万象生鲜系统称重自动多退少补算法解决生鲜非标品痛点
大数据·数据库·人工智能·python·算法
Jasmin Tin Wei5 小时前
ai辅助逆向
python
醇氧5 小时前
Python`__pycache__` 被 Git 跟踪的排查与解决
python·django