ITPub博客

首页 > 应用开发 > IT综合 > 一站式家政小程序源码快速搭建指南|多端适配+支付对接+开源无加密

一站式家政小程序源码快速搭建指南|多端适配+支付对接+开源无加密

原创 IT综合 作者:lpesdx 时间:2025-12-15 14:32:23 0 删除 编辑

  在数字经济蓬勃发展的今天,传统家政行业面临着数字化转型的迫切需求。一个功能完善的家政小程序不仅能提升服务效率,还能增强客户体验。本文提供的开源无加密家政小程序解决方案,具备多端适配能力(微信小程序、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/ ,如需转载,请注明出处,否则将追究法律责任。

上一篇: 没有了~
下一篇: 没有了~
请登录后发表评论 登录
全部评论

注册时间:2025-12-15

  • 博文量
    1
  • 访问量
    1353