【全开源】PHP上门洗车+上门家政服务小程序源码上门洗车APP 小程序源码支持二次开发

35 阅读5分钟

2.png

3.png

4.png

5.png

【全开源】PHP上门洗车+家政服务小程序源码:一站式解决方案(含完整部署指南)

一、系统概述

本开源项目基于PHP 8.1+Laravel 9框架开发,采用UniApp实现多端兼容(微信小程序/H5/APP),集成了上门洗车家政服务两大核心业务模块。系统包含用户端、服务端、管理后台三端,支持服务预约、在线支付、服务评价、技师派单等完整业务流程,并提供完整的API接口文档,便于二次开发。

核心功能亮点

  • 双业务支持:洗车服务(标准洗/精致洗/打蜡)与家政服务(保洁/家电清洗/月嫂)无缝切换
  • 智能派单系统:基于LBS地理位置的技师自动匹配
  • 多支付方式:微信支付/支付宝支付/余额支付
  • 实时轨迹追踪:服务人员位置可视化展示
  • 评价管理体系:服务后评价+投诉处理机制

二、源码结构解析

/home-service-system
├── frontend        # UniApp前端源码(跨平台)
│   ├── pages       # 页面组件(按业务模块划分)
│   │   ├── wash    # 洗车服务相关页面
│   │   └── house   # 家政服务相关页面
│   ├── components  # 公共组件
│   └── static     # 静态资源
├── backend         # Laravel后端源码
│   ├── app         # 业务逻辑层
│   │   ├── Models  # 数据模型
│   │   ├── Services# 业务服务
│   │   └── Http    # 控制器与路由
│   ├── database    # 数据库迁移与种子文件
│   └── resources   # 视图与语言包
└── docs            # 开发文档
    ├── api         # 接口文档
    └── deploy      # 部署指南

三、环境配置与部署

3.1 基础环境要求

组件版本要求备注
PHP8.1+需启用pdo_mysql/redis扩展
MySQL8.0+支持JSON字段类型
Redis5.0+用于会话与缓存
Node.js16+前端依赖管理
HBuilderX3.8+UniApp编译工具

3.2 数据库初始化

-- 执行迁移命令(backend目录下)
php artisan migrate

-- 导入基础数据
php artisan db:seed --class=DatabaseSeeder

-- 关键表说明
-- services: 服务类型表(洗车/家政等)
-- service_items: 服务项目表(标准洗/深度保洁等)
-- technicians: 服务人员表
-- orders: 订单主表
-- order_services: 订单服务明细表

3.3 后端服务启动

# 安装PHP依赖
composer install

# 配置.env文件(关键参数)
APP_URL=http://your-domain.com
DB_DATABASE=home_service
REDIS_HOST=127.0.0.1
WECHAT_PAY_KEY=your_wechat_pay_key

# 启动队列监听(用于异步任务)
php artisan queue:work --tries=3

# 启动定时任务(用于订单超时处理)
php artisan schedule:run

3.4 前端编译与发布

// 安装前端依赖
npm install

// 编译微信小程序
hbuilderx frontend --compile MP-WEIXIN

// 配置小程序参数(manifest.json)
"mp-weixin": {
  "appid": "wx1234567890",
  "requiredPrivateInfos": ["getLocation"],
  "permission": {
    "scope.userLocation": {
      "desc": "需要获取您的位置以匹配附近服务"
    }
  }
}

四、核心业务逻辑实现

4.1 服务预约流程(PHP代码示例)

// backend/app/Http/Controllers/OrderController.php
public function createOrder(OrderRequest $request)
{
    $user = auth('api')->user();
    $serviceItems = $request->input('service_items');
    
    DB::transaction(function () use ($user, $serviceItems) {
        // 1. 创建订单主表
        $order = Order::create([
            'user_id' => $user->id,
            'order_no' => 'HS'.date('YmdHis').rand(1000,9999),
            'total_amount' => 0,
            'status' => 'pending_payment'
        ]);
        
        // 2. 添加服务明细(支持多服务组合)
        $total = 0;
        foreach ($serviceItems as $item) {
            $serviceItem = ServiceItem::findOrFail($item['id']);
            $order->services()->attach($serviceItem->id, [
                'quantity' => $item['quantity'],
                'price' => $serviceItem->price,
                'technician_id' => null // 初始未分配
            ]);
            $total += $serviceItem->price * $item['quantity'];
        }
        
        // 3. 更新订单总价
        $order->update(['total_amount' => $total]);
        
        // 4. 生成支付单(微信支付示例)
        $payment = WeChatPay::createPayment([
            'order_no' => $order->order_no,
            'amount' => $total * 100, // 转换为分
            'description' => '上门服务订单'
        ]);
        
        return response()->json([
            'code' => 200,
            'data' => [
                'order' => $order,
                'payment' => $payment
            ]
        ]);
    });
}

4.2 智能派单算法(伪代码)

def assign_technician(order):
    # 1. 获取订单地理位置
    order_location = (order.latitude, order.longitude)
    
    # 2. 筛选可服务技师(状态空闲+技能匹配)
    available_techs = Technician.query()
        .where('status', 'idle')
        .whereHas('skills', function($q) use ($order) {
            $q->whereIn('service_id', $order->service_ids);
        })
        .get()
    
    # 3. 计算距离并排序
    techs_with_distance = []
    for tech in available_techs:
        tech_location = (tech.latitude, tech.longitude)
        distance = haversine(order_location, tech_location) # 哈弗赛恩公式计算距离
        techs_with_distance.append({
            'tech': tech,
            'distance': distance,
            'score': calculate_score(tech, order) # 综合评分算法
        })
    
    # 4. 按评分排序并分配
    sorted_techs = sorted(techs_with_distance, key=lambda x: x['score'], reverse=True)
    if sorted_techs:
        best_tech = sorted_techs[0]['tech']
        best_tech.update(['status' => 'working'])
        order.update(['technician_id' => best_tech.id])
        return best_tech
    return None

4.3 小程序端服务选择页(UniApp实现)

<!-- frontend/pages/service/select.vue -->
<template>
  <view class="container">
    <scroll-view scroll-y class="service-list">
      <view 
        v-for="category in serviceCategories" 
        :key="category.id" 
        class="category-section"
      >
        <view class="category-title">{{ category.name }}</view>
        <view class="service-grid">
          <view 
            v-for="item in category.items" 
            :key="item.id"
            class="service-card"
            @click="selectService(item)"
          >
            <image :src="item.cover" mode="aspectFill"></image>
            <text class="service-name">{{ item.name }}</text>
            <text class="service-price">¥{{ item.price }}</text>
          </view>
        </view>
      </view>
    </scroll-view>
    
    <view class="action-bar">
      <button 
        class="confirm-btn" 
        :disabled="!selectedItems.length"
        @click="goToConfirm"
      >
        确认服务({{ totalPrice }}元)
      </button>
    </view>
  </view>
</template>

<script>
export default {
  data() {
    return {
      serviceCategories: [], // 从API加载的服务分类数据
      selectedItems: []
    }
  },
  computed: {
    totalPrice() {
      return this.selectedItems.reduce((sum, item) => {
        return sum + (item.price * item.quantity || item.price);
      }, 0);
    }
  },
  methods: {
    async loadServices() {
      const res = await uni.request({
        url: '/api/services/categories',
        method: 'GET'
      });
      this.serviceCategories = res.data;
    },
    selectService(item) {
      const existing = this.selectedItems.find(i => i.id === item.id);
      if (existing) {
        uni.showToast({ title: '已选择', icon: 'none' });
      } else {
        this.selectedItems.push({
          ...item,
          quantity: 1
        });
      }
    },
    goToConfirm() {
      uni.navigateTo({
        url: `/pages/order/confirm?items=${JSON.stringify(this.selectedItems)}`
      });
    }
  },
  onLoad() {
    this.loadServices();
  }
}
</script>

五、系统扩展与优化建议

5.1 性能优化方案

  • 数据库优化

    • 订单表按月分表(orders_202301, orders_202302...)
    • 服务人员表按城市分区
    • 使用Redis缓存热门服务数据
  • 接口优化

    • 首页数据采用GraphQL聚合查询
    • 图片使用CDN加速
    • 启用HTTP/2协议

5.2 功能扩展方向

  • 企业版功能

    • 团队管理(家政公司派单系统)
    • 物料库存管理
    • 服务质量监控仪表盘
  • 营销模块

    • 优惠券系统
    • 会员等级体系
    • 拼团/秒杀活动

5.3 第三方服务集成

// 集成示例:高德地图API
public function calculateDistance($origin, $destination)
{
    $client = new \GuzzleHttp\Client();
    $response = $client->get('https://restapi.amap.com/v3/direction/driving', [
        'query' => [
            'key' => env('AMAP_KEY'),
            'origin' => $origin,
            'destination' => $destination,
            'output' => 'JSON'
        ]
    ]);
    $data = json_decode($response->getBody(), true);
    return $data['route']['paths'][0]['distance'] ?? 0;
}

六、常见问题解决方案

6.1 微信支付回调失败

  • 问题原因:服务器未配置公网可访问的回调地址
  • 解决方案
    1. 在微信支付商户平台配置正确的回调域名
    2. 检查Nginx配置是否转发/api/payment/notify路径
    3. 验证签名时使用微信提供的验签工具

6.2 服务人员位置不更新

  • 排查步骤
    1. 检查技师端APP是否开启定位权限
    2. 验证物联网网关是否正常上报数据
    3. 检查Redis中技师位置缓存是否过期

6.3 跨域访问限制

# Nginx跨域配置示例
location /api/ {
    if ($request_method = 'OPTIONS') {
        add_header 'Access-Control-Allow-Origin' '*';
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
        add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization';
        add_header 'Access-Control-Max-Age' 1728000;
        add_header 'Content-Type' 'text/plain; charset=utf-8';
        add_header 'Content-Length' 0;
        return 204;
    }
    
    add_header 'Access-Control-Allow-Origin' '*';
    add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
    add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization';
    proxy_pass http://backend_server;
}

七、二次开发指南

7.1 自定义服务类型

  1. database/seeders/ServiceSeeder.php中添加新服务分类
  2. 创建对应的服务项目数据迁移
  3. 修改前端pages/service/select.vue的分类展示逻辑

7.2 新增支付方式

  1. 创建支付网关抽象类(app/Services/Payment/Gateway.php
  2. 实现具体支付类(如AlipayGateway.php
  3. OrderController中添加支付路由

7.3 修改派单策略

  1. 创建新的派单服务类(app/Services/Order/NewAssignStrategy.php
  2. OrderService.php中配置策略注入
  3. 通过依赖注入切换不同派单算法

结语

本开源系统提供完整的上门服务解决方案,通过模块化设计和清晰的代码结构,支持快速部署和二次开发。系统采用成熟的PHP+Laravel技术栈,结合UniApp的跨平台能力,可有效降低开发成本。建议部署后进行压力测试,并根据实际业务需求调整派单算法和营销策略。开发过程中可参考docs/目录下的详细文档,或通过GitHub Issues提交问题。