在数字经济蓬勃发展的今天,传统家政行业面临着数字化转型的迫切需求。一个功能完善的家政小程序不仅能提升服务效率,还能增强客户体验。本文提供的开源无加密家政小程序解决方案,具备多端适配能力(微信小程序、H5、APP),集成了完整的支付对接功能,为企业快速进入市场提供了可靠的技术基础。
源码:j.yunzes.top/er
本指南将手把手教您从零开始搭建一个完整的家政服务平台,所有代码均开源透明,可根据业务需求自由定制。
一、项目架构与技术选型
1.1技术栈说明
//package.json核心依赖示例
{
"dependencies":{
//前端框架
" uni-helper/uni-ui":"^1.4.7",//多端UI组件库
"uni-simple-router":"^2.0.7",//路由管理
"pinia":"^2.1.6",//状态管理
//网络请求
"luch-request":"^3.1.1",//增强型请求库
//工具库
"dayjs":"^1.11.10",//时间处理
"uuid":"^9.0.0",//唯一标识生成
//支付相关
"wechat-miniprogram-pay":"^2.0.0",//微信支付SDK
}
}
1.2项目目录结构

homemaking-miniprogram/ ├──api/#接口管理 │├──index.js#接口统一导出 │├──order.js#订单相关接口 │└──payment.js#支付相关接口 ├──components/#公共组件 │├──service-card/#服务卡片组件 │├──time-picker/#时间选择器 │└──address-picker/#地址选择器 ├──pages/#页面文件 │├──index/#首页 │├──service/#服务列表页 │├──order/#订单页 │└──user/#用户中心 ├──store/#状态管理 │├──index.js#store主文件 │└──modules/#模块store ├──static/#静态资源 └──utils/#工具函数 ├──request.js#请求封装 ├──payment.js#支付工具 └──validate.js#表单验证
二、数据库设计核心表结构
2.1服务项目表(services)
CREATE TABLE`services`( `id`int(11)NOT NULL AUTO_INCREMENT, `name`varchar(100)NOT NULL COMMENT'服务名称', `category_id`int(11)NOT NULL COMMENT'分类ID', `price`decimal(10,2)NOT NULL COMMENT'基础价格', `unit`varchar(20)DEFAULT'次'COMMENT'单位', `duration`int(11)DEFAULT 60 COMMENT'预计时长(分钟)', `cover_image`varchar(255)DEFAULT NULL COMMENT'封面图片', `description`text COMMENT'服务描述', `is_recommend`tinyint(1)DEFAULT 0 COMMENT'是否推荐', `sort_order`int(11)DEFAULT 0 COMMENT'排序', `status`tinyint(1)DEFAULT 1 COMMENT'状态1启用0禁用', `created_at`timestamp NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY(`id`) )ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='服务项目表';
2.2订单表(orders)

CREATE TABLE`orders`( `order_id`varchar(32)NOT NULL COMMENT'订单号', `user_id`int(11)NOT NULL COMMENT'用户ID', `service_id`int(11)NOT NULL COMMENT'服务ID', `service_count`int(11)DEFAULT 1 COMMENT'服务数量', `total_amount`decimal(10,2)NOT NULL COMMENT'订单总额', `pay_amount`decimal(10,2)NOT NULL COMMENT'实付金额', `coupon_amount`decimal(10,2)DEFAULT'0.00'COMMENT'优惠金额', `contact_name`varchar(50)NOT NULL COMMENT'联系人', `contact_phone`varchar(20)NOT NULL COMMENT'联系电话', `service_address`text NOT NULL COMMENT'服务地址', `service_time`datetime NOT NULL COMMENT'预约时间', `remark`varchar(500)DEFAULT NULL COMMENT'备注', `order_status`tinyint(4)DEFAULT 0 COMMENT'0待支付1已支付2已接单3服务中4已完成5已取消', `pay_status`tinyint(1)DEFAULT 0 COMMENT'支付状态0未支付1已支付', `pay_time`datetime DEFAULT NULL COMMENT'支付时间', `pay_type`varchar(20)DEFAULT NULL COMMENT'支付方式', `transaction_id`varchar(50)DEFAULT NULL COMMENT'支付交易号', `created_at`timestamp NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY(`order_id`), KEY`idx_user_id`(`user_id`), KEY`idx_created_at`(`created_at`) )ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单表';
三、核心功能实现代码
3.1服务预约功能实现
<!--pages/service/detail.vue服务详情页-->
<template>
<view class="service-detail">
<!--服务头部-->
<view class="service-header">
<image:src="serviceInfo.cover_image"mode="widthFix"></image>
<view class="service-info">
<view class="title">{{serviceInfo.name}}</view>
<view class="price">
<text class="current-price">¥{{serviceInfo.price}}</text>
<text class="original-price"v-if="serviceInfo.original_price">
¥{{serviceInfo.original_price}}
</text>
</view>
</view>
</view>
<!--服务详情-->
<view class="service-content">
<view class="section">
<view class="section-title">服务描述</view>
<view class="description">{{serviceInfo.description}}</view>
</view>
<!--时间选择-->
<view class="section">
<view class="section-title">选择服务时间</view>
<view class="time-picker">
<picker
mode="date"
:value="selectedDate"
change="onDateChange"
:start="today"
>
<view class="picker-item">
{{selectedDate||'选择日期'}}
</view>
</picker>
<picker
mode="time"
:value="selectedTime"
change="onTimeChange"
>
<view class="picker-item">
{{selectedTime||'选择时间'}}
</view>
</picker>
</view>
</view>
<!--立即预约按钮-->
<view class="fixed-bottom">
<button
class="reserve-btn"
click="handleReserve"
:disabled="!selectedDate||!selectedTime"
>
立即预约
</button>
</view>
</view>
</view>
</template>
<script setup>
import{ref,onMounted}from'vue'
import{getServiceDetail,createOrder}from' /api/service'
const serviceInfo=ref({})
const selectedDate=ref('')
const selectedTime=ref('')
const today=ref(new Date().toISOString().split('T')[0])
//获取服务详情
const fetchServiceDetail=async(id)=>{
try{
const res=await getServiceDetail(id)
serviceInfo.value=res.data
}catch(error){
uni.showToast({title:'获取服务详情失败',icon:'none'})
}
}
//处理预约
const handleReserve=async()=>{
if(!selectedDate.value||!selectedTime.value){
uni.showToast({title:'请选择服务时间',icon:'none'})
return
}
const orderData={
service_id:serviceInfo.value.id,
service_time:`${selectedDate.value}${selectedTime.value}:00`,
remark:''
}
try{
const res=await createOrder(orderData)
uni.navigateTo({
url:`/pages/order/confirm?id=${res.data.order_id}`
})
}catch(error){
uni.showToast({title:'创建订单失败',icon:'none'})
}
}
onMounted(()=>{
const{id}=uni.getCurrentInstance().proxy.$route.query
fetchServiceDetail(id)
})
</script>
3.2多端适配配置
//manifest.json多端配置文件
{
"name":"家政服务平台",
"appid":"",
"description":"一站式家政服务解决方案",
"versionName":"1.0.0",
"versionCode":"100",
/*小程序特有配置*/
"mp-weixin":{
"appid":"your-wechat-appid",
"setting":{
"urlCheck":false,
"es6":true,
"postcss":true
},
"permission":{
"scope.userLocation":{
"desc":"您的位置信息将用于获取附近服务"
}
},
"requiredPrivateInfos":["getLocation"]
},
/*H5特有配置*/
"h5":{
"title":"家政服务平台",
"router":{
"mode":"hash"
},
"template":"template.h5.html",
"optimization":{
"treeShaking":{
"enable":true
}
}
},
/*APP特有配置*/
"app-plus":{
"distribute":{
"android":{
"permissions":[
"<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>"
]
},
"ios":{}
}
},
/*通用配置*/
"uniStatistics":{
"enable":true
}
}
四、微信支付对接完整实现
4.1支付工具类封装

//utils/payment.js
import{request}from'./request'
class PaymentService{
constructor(){
this.payment=null
//初始化支付方式
this.initPayment()
}
//初始化支付方式
initPayment(){
//微信小程序环境
if(uni.getSystemInfoSync().platform==='mp-weixin'){
this.payment=this.wechatPayment
}
//H5环境
else if(typeof window!=='undefined'){
this.payment=this.h5Payment
}
//APP环境
else{
this.payment=this.appPayment
}
}
//微信小程序支付
async wechatPayment(orderId,successCallback,failCallback){
try{
//1.获取支付参数
const params=await this.getPaymentParams(orderId)
//2.调起微信支付
const res=await wx.requestPayment({
timeStamp:params.timeStamp,
nonceStr:params.nonceStr,
package:params.package,
signType:params.signType,
paySign:params.paySign,
success:(res)=>{
successCallback&&successCallback(res)
this.paymentSuccess(orderId)
},
fail:(err)=>{
failCallback&&failCallback(err)
uni.showToast({title:'支付失败',icon:'none'})
}
})
return res
}catch(error){
console.error('支付失败:',error)
uni.showToast({title:'支付参数获取失败',icon:'none'})
}
}
//获取支付参数
async getPaymentParams(orderId){
const res=await request({
url:'/api/payment/wechat/miniprogram',
method:'POST',
data:{
order_id:orderId,
openid:uni.getStorageSync('openid')
}
})
if(res.code===200){
return res.data
}else{
throw new Error(res.message)
}
}
//支付成功处理
async paymentSuccess(orderId){
try{
//更新订单状态
await request({
url:'/api/payment/success',
method:'POST',
data:{order_id:orderId}
})
//跳转到支付成功页面
uni.redirectTo({
url:`/pages/order/success?order_id=${orderId}`
})
}catch(error){
console.error('支付成功处理失败:',error)
}
}
//检查支付状态
async checkPaymentStatus(orderId){
const res=await request({
url:'/api/order/status',
method:'GET',
data:{order_id:orderId}
})
return res.data
}
}
export default new PaymentService()
4.2后端支付接口示例
//server/controllers/payment.js
const crypto=require('crypto')
const wxpay=require('../utils/wxpay')
class PaymentController{
//生成小程序支付参数
async generateMiniProgramPayParams(ctx){
const{order_id,openid}=ctx.request.body
//1.验证订单
const order=await OrderModel.findById(order_id)
if(!order){
ctx.throw(404,'订单不存在')
}
if(order.pay_status===1){
ctx.throw(400,'订单已支付')
}
//2.生成统一下单参数
const params={
appid:config.wx.appId,//小程序ID
mch_id:config.wx.mchId,//商户号
nonce_str:crypto.randomBytes(16).toString('hex'),//随机字符串
body:'家政服务-'+order.service_name,//商品描述
out_trade_no:order.order_id,//商户订单号
total_fee:Math.round(order.pay_amount*100),//金额(分)
spbill_create_ip:ctx.request.ip,//终端IP
notify_url:config.wx.notifyUrl,//通知地址
trade_type:'JSAPI',//交易类型
openid:openid//用户openid
}
//3.生成签名
params.sign=this.generateSign(params,config.wx.mcKey)
//4.调用微信统一下单接口
const result=await wxpay.unifiedOrder(params)
//5.返回小程序支付所需参数
const payParams={
timeStamp:Math.floor(Date.now()/1000).toString(),
nonceStr:crypto.randomBytes(16).toString('hex'),
package:`prepay_id=${result.prepay_id}`,
signType:'MD5'
}
//6.生成支付签名
const signStr=`appId=${config.wx.appId}&nonceStr=${
payParams.nonceStr
}&package=${payParams.package}&signType=${
payParams.signType
}&timeStamp=${payParams.timeStamp}&key=${config.wx.mKey}`
payParams.paySign=crypto
.createHash('md5')
.update(signStr)
.digest('hex')
.toUpperCase()
ctx.body={
code:200,
data:payParams
}
}
//支付结果通知回调
async paymentNotify(ctx){
const xmlData=ctx.request.body
//验证签名
if(!this.verifySign(xmlData)){
ctx.body=this.generateXmlResult('FAIL','签名失败')
return
}
const{out_trade_no,transaction_id,result_code}=xmlData
if(result_code==='SUCCESS'){
//更新订单状态
await OrderModel.updateOne(
{order_id:out_trade_no},
{
$set:{
pay_status:1,
order_status:1,
pay_time:new Date(),
transaction_id:transaction_id
}
}
)
//记录支付日志
await PaymentLogModel.create({
order_id:out_trade_no,
transaction_id:transaction_id,
amount:xmlData.total_fee/100,
pay_type:'wechat',
status:1
})
}
ctx.body=this.generateXmlResult('SUCCESS','OK')
}
//生成签名
generateSign(params,key){
const sortedParams=Object.keys(params)
.filter(k=>params[k]&&k!=='sign')
.sort()
.map(k=>`${k}=${params[k]}`)
.join('&')
const signStr=`${sortedParams}&key=${key}`
return crypto
.createHash('md5')
.update(signStr,'utf8')
.digest('hex')
.toUpperCase()
}
}
五、后台管理系统核心模块
5.1单子管理组件

<!--admin/pages/order/list.vue-->
<template>
<div class="order-management">
<!--搜索筛选-->
<el-card class="search-card">
<el-form:inline="true":model="searchForm">
<el-form-item label="订单号">
<el-input v-model="searchForm.order_id"placeholder="输入订单号"clearable/>
</el-form-item>
<el-form-item label="订单状态">
<el-select v-model="searchForm.order_status"clearable>
<el-option label="全部"value=""/>
<el-option label="待支付":value="0"/>
<el-option label="已支付":value="1"/>
<el-option label="已接单":value="2"/>
<el-option label="已完成":value="4"/>
<el-option label="已取消":value="5"/>
</el-select>
</el-form-item>
<el-form-item label="下单时间">
<el-date-picker
v-model="dateRange"
type="daterange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="YYYY-MM-DD"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" click="handleSearch">搜索</el-button>
<el-button click="handleReset">重置</el-button>
</el-form-item>
</el-form>
</el-card>
<!--订单列表-->
<el-card>
<el-table:data="orderList"v-loading="loading">
<el-table-column prop="order_id"label="订单号"width="200"/>
<el-table-column prop="service_name"label="服务项目"/>
<el-table-column prop="total_amount"label="订单金额"width="100">
<template#default="{row}">
¥{{row.total_amount}}
</template>
</el-table-column>
<el-table-column prop="contact_name"label="联系人"width="100"/>
<el-table-column prop="contact_phone"label="联系电话"width="120"/>
<el-table-column prop="service_time"label="预约时间"width="180">
<template#default="{row}">
{{formatTime(row.service_time)}}
</template>
</el-table-column>
<el-table-column prop="order_status"label="订单状态"width="100">
<template#default="{row}">
<el-tag:type="getStatusType(row.order_status)">
{{getStatusText(row.order_status)}}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作"width="200"fixed="right">
<template#default="{row}">
<el-button
size="small"
click="handleViewDetail(row.order_id)"
>
详情
</el-button>
<el-button
v-if="row.order_status===1"
size="small"
type="success"
click="handleAcceptOrder(row.order_id)"
>
接单
</el-button>
<el-button
v-if="row.order_status===2"
size="small"
type="primary"
click="handleStartService(row.order_id)"
>
开始服务
</el-button>
<el-button
v-if="row.order_status===3"
size="small"
type="warning"
click="handleCompleteOrder(row.order_id)"
>
完成
</el-button>
</template>
</el-table-column>
</el-table>
<!--分页-->
<el-pagination
v-model:current-page="pagination.current"
v-model:page-size="pagination.size"
:total="pagination.total"
:page-sizes="[10,20,50,100]"
size-change="handleSizeChange"
current-change="handleCurrentChange"
layout="total,sizes,prev,pager,next,jumper"
/>
</el-card>
</div>
</template>
<script setup>
import{ref,reactive,onMounted}from'vue'
import{getOrderList,updateOrderStatus}from' /api/admin/order'
import{formatTime}from' /utils/date'
const loading=ref(false)
const orderList=ref([])
const dateRange=ref([])
const searchForm=reactive({
order_id:'',
order_status:'',
start_date:'',
end_date:''
})
const pagination=reactive({
current:1,
size:10,
total:0
})
//获取订单列表
const fetchOrderList=async()=>{
loading.value=true
try{
const params={
...searchForm,
page:pagination.current,
page_size:pagination.size
}
if(dateRange.value.length===2){
params.start_date=dateRange.value[0]
params.end_date=dateRange.value[1]
}
const res=await getOrderList(params)
orderList.value=res.data.list
pagination.total=res.data.total
}catch(error){
console.error('获取订单列表失败:',error)
}finally{
loading.value=false
}
}
//接单处理
const handleAcceptOrder=async(orderId)=>{
try{
await updateOrderStatus({
order_id:orderId,
status:2
})
ElMessage.success('接单成功')
fetchOrderList()
}catch(error){
ElMessage.error('操作失败')
}
}
onMounted(()=>{
fetchOrderList()
})
</script>
六、部署与配置指南
6.1环境要求与安装
#1.环境要求
#Node.js>=16.0.0
#MySQL>=5.7
#Redis>=6.0
#2.克隆项目
git clone https://github.com/your-repo/homemaking-miniprogram.git
cd homemaking-miniprogram
#3.安装依赖
npm install
#4.数据库配置
#导入数据库文件
mysql-u root-p<database/schema.sql
#5.配置文件修改
#复制配置文件
cp.env.example.env
#编辑配置文件
vim.env
6.2环境变量配置
#.env配置文件
NODE_ENV=production
PORT=3000
#数据库配置
DB_HOST=localhost
DB_PORT=3306
DB_USER=root
DB_PASSWORD=your_password
DB_NAME=homemaking_db
#Redis配置
REDIS_HOST=127.0.0.1
REDIS_PORT=6379
REDIS_PASSWORD=
REDIS_DB=0
#微信小程序配置
WX_APPID=your_wechat_appid
WX_APPSECRET=your_wechat_appsecret
WX_MCHID=your_mch_id
WX_MCKEY=your_mch_key
WX_NOTIFY_URL=https://your-domain.com/api/payment/notify
#JWT配置
JWT_SECRET=your_jwt_secret_key
JWT_EXPIRES_IN=7d
#文件上传配置
UPLOAD_PATH=./uploads
MAX_FILE_SIZE=5mb
ALLOWED_EXTENSIONS=jpg,jpeg,png,gif
6.3 PM2进程管理配置

//ecosystem.config.js
module.exports={
apps:[{
name:'homemaking-api',
script:'./server/index.js',
instances:'max',
exec_mode:'cluster',
env:{
NODE_ENV:'production',
PORT:3000
},
error_file:'./logs/err.log',
out_file:'./logs/out.log',
log_file:'./logs/combined.log',
time:true,
merge_logs:true,
max_memory_restart:'1G',
watch:false,
ignore_watch:[
'node_modules',
'logs',
'uploads'
],
env_production:{
NODE_ENV:'production'
}
}]
}
启动命令:
#启动生产环境
pm2 start ecosystem.config.js--env production
#查看进程状态
pm2 status
#查看日志
pm2 logs homemaking-api
七、二次开发指南
7.1添加新的服务分类
1.在数据库中添加分类数据:
INSERT INTO service_categories(name,icon,sort_order,status)
VALUES
('深度保洁','icon-clean',1,1),
('家电维修','icon-repair',2,1),
('保姆月嫂','icon-nanny',3,1);
2.在前端添加分类配置:
//utils/constants.js
export const SERVICE_CATEGORIES=[
{id:1,name:'日常保洁',icon:'icon-cleaning'},
{id:2,name:'深度保洁',icon:'icon-clean'},
{id:3,name:'家电维修',icon:'icon-repair'},
{id:4,name:'保姆月嫂',icon:'icon-nanny'}
]
7.2自定义支付方式
//扩展支付方式
//utils/payment/extension.js
export class PaymentFactory{
constructor(){
this.paymentMethods={
'wechat':WechatPayment,
'alipay':AlipayPayment,
'balance':BalancePayment
}
}
//注册新的支付方式
registerPaymentMethod(name,handler){
this.paymentMethods[name]=handler
}
//获取支付处理器
getPaymentHandler(type){
const Handler=this.paymentMethods[type]
if(!Handler){
throw new Error(`不支持的支付方式:${type}`)
}
return new Handler()
}
}
本文详细介绍了一站式家政小程序的完整搭建流程,从项目架构设计到核心功能实现,再到支付对接和多端适配,提供了完整的开源无加密解决方案。本系统具有以下特点:
1.模块化设计:代码结构清晰,易于维护和扩展
2.多端兼容:一套代码,多端运行(小程序、H5、APP)
3.支付完整:集成微信支付,支持多种支付场景
4.安全可靠:采用JWT认证、参数验证、SQL防注入
5.高性能:支持Redis缓存、数据库优化
本指南涵盖了从零开始搭建家政小程序的全过程,您可以根据实际业务需求进行调整和扩展。如果在实施过程中遇到问题,欢迎在项目Issues中提出,我们将持续更新和维护这个开源项目。
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/70048804/viewspace-3105231/ ,如需转载,请注明出处,否则将追究法律责任。