JAVA无人共享24小时自助洗车扫码洗车系统源码微信小程序的代码片段
·
🚗 JAVA 无人共享24小时自助洗车扫码系统 - 微信小程序代码片段
📁 项目结构
├── backend (Java SpringBoot)
│ └── src/main/java/com/carwash/
├── miniprogram (微信小程序)
│ ├── pages/
│ │ ├── index/ # 首页
│ │ ├── scan/ # 扫码页面
│ │ ├── order/ # 订单页
│ │ └── my/ # 我的
│ └── utils/
🔧 一、微信小程序前端代码
1️⃣ 首页 pages/index/index.wxml
xml
<view class="container">
<!-- 顶部Banner -->
<swiper class="banner" autoplay circular indicator-dots>
<swiper-item wx:for="{{banners}}" wx:key="id">
<image src="{{item.image}}" mode="aspectFill"></image>
</swiper-item>
</swiper>
<!-- 附近洗车点 -->
<view class="section">
<view class="section-title">附近洗车点</view>
<view class="store-list">
<view
class="store-item"
wx:for="{{stores}}"
wx:key="id"
bindtap="goToStore"
data-id="{{item.id}}"
>
<image src="{{item.image}}" class="store-img"></image>
<view class="store-info">
<text class="store-name">{{item.name}}</text>
<text class="store-distance">距离{{item.distance}}km</text>
<view class="store-status {{item.status == 1 ? 'online' : 'offline'}}">
{{item.status == 1 ? '营业中' : '已打烊'}}
</view>
</view>
<view class="store-price">
<text class="price">¥{{item.price}}</text>
<text class="unit">/次</text>
</view>
</view>
</view>
</view>
<!-- 扫码洗车按钮 -->
<view class="scan-btn" bindtap="scanCode">
<text class="iconfont icon-scan"></text>
<text>扫码洗车</text>
</view>
</view>
2️⃣ 首页逻辑 pages/index/index.js
javascript
const app = getApp()
const api = require('../../utils/api')
Page({
data: {
banners: [],
stores: [],
latitude: 0,
longitude: 0
},
onLoad() {
this.getLocation()
this.getStores()
this.getBanners()
},
// 获取位置
getLocation() {
wx.getLocation({
type: 'gcj02',
success: (res) => {
this.setData({
latitude: res.latitude,
longitude: res.longitude
})
}
})
},
// 获取附近洗车点
async getStores() {
const res = await api.getNearbyStores({
lat: this.data.latitude,
lng: this.data.longitude
})
this.setData({ stores: res.data })
},
// 获取轮播图
async getBanners() {
const res = await api.getBanners()
this.setData({ banners: res.data })
},
// 扫码洗车
scanCode() {
wx.scanCode({
onlyFromCamera: false,
scanType: ['qrCode'],
success: async (res) => {
// res.result 是二维码内容,包含设备ID
const deviceId = res.result.replace('wash:', '')
wx.navigateTo({
url: `/pages/order/order?deviceId=${deviceId}`
})
}
})
},
goToStore(e) {
const id = e.currentTarget.dataset.id
wx.navigateTo({
url: `/pages/order/order?storeId=${id}`
})
}
})
3️⃣ 扫码洗车/下单页 pages/order/order.wxml
xml
<view class="order-page">
<!-- 设备信息 -->
<view class="device-card" wx:if="{{deviceInfo}}">
<image src="{{deviceInfo.image}}" class="device-img"></image>
<view class="device-info">
<text class="device-name">{{deviceInfo.name}}</text>
<text class="device-addr">{{deviceInfo.address}}</text>
<view class="device-status">
<view class="status-dot {{deviceInfo.online ? 'green' : 'red'}}"></view>
<text>{{deviceInfo.online ? '设备在线' : '设备离线'}}</text>
</view>
</view>
</view>
<!-- 洗车套餐选择 -->
<view class="section">
<view class="section-title">选择洗车套餐</view>
<view class="package-list">
<view
class="package-item {{selectedPackage == item.id ? 'active' : ''}}"
wx:for="{{packages}}"
wx:key="id"
bindtap="selectPackage"
data-id="{{item.id}}"
>
<view class="pkg-name">{{item.name}}</view>
<view class="pkg-desc">{{item.description}}</view>
<view class="pkg-price">
<text class="currency">¥</text>
<text class="amount">{{item.price}}</text>
</view>
<view class="pkg-duration">约{{item.duration}}分钟</view>
</view>
</view>
</view>
<!-- 支付方式 -->
<view class="section">
<view class="section-title">支付方式</view>
<view class="pay-methods">
<view
class="pay-item {{payMethod == 'wechat' ? 'active' : ''}}"
bindtap="selectPay"
data-method="wechat"
>
<image src="/images/wechat-pay.png"></image>
<text>微信支付</text>
</view>
<view
class="pay-item {{payMethod == 'balance' ? 'active' : ''}}"
bindtap="selectPay"
data-method="balance"
>
<image src="/images/wallet.png"></image>
<text>余额支付 (¥{{userBalance}})</text>
</view>
</view>
</view>
<!-- 底部支付按钮 -->
<view class="bottom-bar">
<view class="total">
<text>合计:</text>
<text class="total-price">¥{{totalPrice}}</text>
</view>
<button class="pay-btn" bindtap="submitOrder">立即支付</button>
</view>
</view>
4️⃣ 下单逻辑 pages/order/order.js
javascript
const api = require('../../utils/api')
Page({
data: {
deviceId: '',
storeId: '',
deviceInfo: null,
packages: [
{ id: 1, name: '标准洗车', description: '外观清洗+简单内饰', price: 10, duration: 15 },
{ id: 2, name: '精致洗车', description: '全车清洗+内饰清洁', price: 20, duration: 25 },
{ id: 3, name: '豪华洗车', description: '全车精洗+打蜡', price: 35, duration: 40 }
],
selectedPackage: 1,
payMethod: 'wechat',
userBalance: 0,
totalPrice: 10
},
onLoad(options) {
if (options.deviceId) {
this.setData({ deviceId: options.deviceId })
this.getDeviceInfo(options.deviceId)
}
if (options.storeId) {
this.setData({ storeId: options.storeId })
this.getDeviceInfoByStore(options.storeId)
}
this.getUserBalance()
},
// 获取设备信息
async getDeviceInfo(deviceId) {
const res = await api.getDeviceInfo(deviceId)
this.setData({ deviceInfo: res.data })
},
// 获取用户余额
async getUserBalance() {
const res = await api.getUserBalance()
this.setData({ userBalance: res.data.balance })
},
// 选择套餐
selectPackage(e) {
const id = e.currentTarget.dataset.id
const pkg = this.data.packages.find(p => p.id === id)
this.setData({
selectedPackage: id,
totalPrice: pkg.price
})
},
// 选择支付方式
selectPay(e) {
this.setData({ payMethod: e.currentTarget.dataset.method })
},
// 提交订单
async submitOrder() {
wx.showLoading({ title: '支付中...' })
try {
// 1. 创建订单
const orderRes = await api.createOrder({
deviceId: this.data.deviceId,
packageId: this.data.selectedPackage,
payMethod: this.data.payMethod
})
const orderId = orderRes.data.orderId
// 2. 调用微信支付
if (this.data.payMethod === 'wechat') {
const payRes = await api.getWxPayParams(orderId)
wx.requestPayment({
timeStamp: payRes.data.timeStamp,
nonceStr: payRes.data.nonceStr,
package: payRes.data.package,
signType: payRes.data.signType,
paySign: payRes.data.paySign,
success: () => {
wx.showToast({ title: '支付成功', icon: 'success' })
// 跳转到洗车控制页
wx.redirectTo({
url: `/pages/wash/wash?orderId=${orderId}&deviceId=${this.data.deviceId}`
})
},
fail: () => {
wx.showToast({ title: '支付取消', icon: 'none' })
}
})
} else {
// 余额支付
await api.payByBalance(orderId)
wx.showToast({ title: '支付成功', icon: 'success' })
wx.redirectTo({
url: `/pages/wash/wash?orderId=${orderId}&deviceId=${this.data.deviceId}`
})
}
} catch (err) {
wx.showToast({ title: err.message || '下单失败', icon: 'none' })
} finally {
wx.hideLoading()
}
}
})
5️⃣ 洗车控制页 pages/wash/wash.wxml
xml
<view class="wash-page">
<view class="wash-header">
<text class="wash-title">洗车进行中</text>
<text class="wash-timer">剩余时间:{{remainTime}}s</text>
</view>
<!-- 洗车控制面板 -->
<view class="control-panel">
<view class="control-item" bindtap="toggleWash" data-type="wash">
<view class="control-icon wash-icon {{isWashing ? 'active' : ''}}">
🚿
</view>
<text>{{isWashing ? '清洗中' : '未开始'}}</text>
</view>
<view class="control-item" bindtap="toggleWash" data-type="foam">
<view class="control-icon foam-icon {{isFoam ? 'active' : ''}}">
🧴
</view>
<text>泡沫</text>
</view>
<view class="control-item" bindtap="toggleWash" data-type="wax">
<view class="control-icon wax-icon {{isWax ? 'active' : ''}}">
✨
</view>
<text>打蜡</text>
</view>
<view class="control-item" bindtap="toggleWash" data-type="dry">
<view class="control-icon dry-icon {{isDry ? 'active' : ''}}">
💨
</view>
<text>吹干</text>
</view>
</view>
<!-- 进度条 -->
<view class="progress-bar">
<view class="progress-fill" style="width: {{progress}}%"></view>
</view>
<!-- 底部操作 -->
<view class="bottom-actions">
<button class="stop-btn" bindtap="stopWash">结束洗车</button>
<button class="call-btn" bindtap="callService">联系客服</button>
</view>
</view>
6️⃣ 洗车控制逻辑 pages/wash/wash.js
javascript
const api = require('../../utils/api')
Page({
data: {
orderId: '',
deviceId: '',
isWashing: false,
isFoam: false,
isWax: false,
isDry: false,
remainTime: 0,
progress: 0,
timer: null
},
onLoad(options) {
this.setData({
orderId: options.orderId,
deviceId: options.deviceId
})
this.startTimer()
this.listenDeviceStatus()
},
onUnload() {
if (this.data.timer) {
clearInterval(this.data.timer)
}
this.stopListening()
},
// 倒计时
startTimer() {
let time = 900 // 15分钟
this.setData({ remainTime: time })
this.data.timer = setInterval(() => {
time--
this.setData({
remainTime: time,
progress: ((900 - time) / 900) * 100
})
if (time <= 0) {
clearInterval(this.data.timer)
this.autoStop()
}
}, 1000)
},
// 切换洗车模式
async toggleWash(e) {
const type = e.currentTarget.dataset.type
const keyMap = {
wash: 'isWashing',
foam: 'isFoam',
wax: 'isWax',
dry: 'isDry'
}
const newVal = !this.data[keyMap[type]]
this.setData({ [keyMap[type]]: newVal })
// 发送指令到设备
await api.sendDeviceCommand({
deviceId: this.data.deviceId,
command: type,
status: newVal ? 1 : 0
})
wx.showToast({
title: newVal ? `${type}已开启` : `${type}已关闭`,
icon: 'none'
})
},
// 监听设备状态(WebSocket)
listenDeviceStatus() {
this.wsTask = wx.connectSocket({
url: `wss://your-domain.com/ws/device/${this.data.deviceId}`,
success: () => {
wx.onSocketMessage((res) => {
const data = JSON.parse(res.data)
if (data.type === 'status') {
this.setData({
isWashing: data.washing,
isFoam: data.foam,
isWax: data.wax,
isDry: data.dry
})
}
})
}
})
},
stopListening() {
if (this.wsTask) {
wx.closeSocket()
}
},
// 结束洗车
async stopWash() {
wx.showModal({
title: '确认结束',
content: '确定要结束洗车吗?',
success: async (res) => {
if (res.confirm) {
await api.stopWash({
orderId: this.data.orderId,
deviceId: this.data.deviceId
})
clearInterval(this.data.timer)
wx.redirectTo({ url: '/pages/my/my' })
}
}
})
},
autoStop() {
this.stopWash()
},
callService() {
wx.makePhoneCall({
phoneNumber: '400-888-8888'
})
}
})
☕ 二、Java SpringBoot 后端核心代码
1️⃣ 实体类 Device.java
java
@Data
@Entity
@Table(name = "t_device")
public class Device {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String deviceCode; // 设备编号
private String deviceName; // 设备名称
private String qrCode; // 二维码内容
private Double latitude; // 纬度
private Double longitude; // 经度
private String address; // 地址
private Integer status; // 0-离线 1-在线 2-故障
private Integer deviceType; // 1-自助洗车机
@CreatedDate
private LocalDateTime createTime;
@UpdateTimestamp
private LocalDateTime updateTime;
}
2️⃣ 订单实体 Order.java
java
@Data
@Entity
@Table(name = "t_order")
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String orderNo; // 订单号
private Long userId; // 用户ID(openid)
private Long deviceId; // 设备ID
private Integer packageId; // 套餐ID
private BigDecimal amount; // 金额
private Integer payMethod; // 1-微信 2-余额
private Integer status; // 0-待支付 1-已支付 2-洗车中 3-已完成 4-已取消
private LocalDateTime payTime; // 支付时间
private LocalDateTime startTime; // 开始洗车时间
private LocalDateTime endTime; // 结束时间
@CreatedDate
private LocalDateTime createTime;
}
3️⃣ 订单Controller OrderController.java
java
@RestController
@RequestMapping("/api/order")
@CrossOrigin
public class OrderController {
@Autowired
private OrderService orderService;
@Autowired
private WxPayService wxPayService;
// 创建订单
@PostMapping("/create")
public Result createOrder(@RequestBody OrderDTO dto) {
String orderNo = "CW" + System.currentTimeMillis();
Order order = orderService.createOrder(orderNo, dto);
return Result.success(order);
}
// 获取微信支付参数
@PostMapping("/pay/{orderId}")
public Result getWxPayParams(@PathVariable Long orderId) {
Map<String, String> payParams = wxPayService.createPay(orderId);
return Result.success(payParams);
}
// 支付回调
@PostMapping("/notify")
public String payNotify(@RequestBody String xmlData) {
Map<String, String> result = wxPayService.parseNotify(xmlData);
if ("SUCCESS".equals(result.get("result_code"))) {
String orderNo = result.get("out_trade_no");
orderService.updateStatus(orderNo, 1); // 已支付
// 发送WebSocket消息通知设备开始
deviceService.sendCommand(orderNo, "START");
}
return "<xml><return_code><![CDATA[SUCCESS]]></return_code></xml>";
}
// 结束洗车
@PostMapping("/stop")
public Result stopWash(@RequestBody StopWashDTO dto) {
orderService.endOrder(dto.getOrderId(), dto.getDeviceId());
return Result.success("洗车已结束");
}
}
4️⃣ 微信支付服务 WxPayService.java
java
@Service
public class WxPayService {
@Value("${wx.pay.appId}")
private String appId;
@Value("${wx.pay.mchId}")
private String mchId;
@Value("${wx.pay.apiKey}")
private String apiKey;
@Value("${wx.pay.notifyUrl}")
private String notifyUrl;
public Map<String, String> createPay(Long orderId) {
Order order = orderService.getByOrderNo(
orderService.getOrderNo(orderId)
);
WxPayUnifiedOrderRequest request = new WxPayUnifiedOrderRequest();
request.setBody("自助洗车-" + order.getPackageId());
request.setOut_trade_no(order.getOrderNo());
request.setTotal_fee(order.getAmount().multiply(new BigDecimal(100)).intValue());
request.setSpbill_create_ip("127.0.0.1");
request.setNotify_url(notifyUrl);
request.setTrade_type("JSAPI");
request.setOpenid(order.getUserOpenid());
WxPayUnifiedOrderResult result = WxPayApi.unifiedOrder(request, apiKey);
// 返回小程序支付参数
Map<String, String> params = new HashMap<>();
params.put("appId", appId);
params.put("timeStamp", String.valueOf(System.currentTimeMillis() / 1000));
params.put("nonceStr", UUID.randomUUID().toString().replace("-", ""));
params.put("package", "prepay_id=" + result.getPrepayId());
params.put("signType", "MD5");
String sign = WxPayApi.paySign(params, apiKey);
params.put("paySign", sign);
return params;
}
}
5️⃣ 设备控制WebSocket DeviceWebSocket.java
java
@ServerEndpoint("/ws/device/{deviceId}")
@Component
public class DeviceWebSocket {
private static ConcurrentHashMap<String, Session> sessions = new ConcurrentHashMap<>();
@OnOpen
public void onOpen(Session session, @PathParam("deviceId") String deviceId) {
sessions.put(deviceId, session);
System.out.println("设备连接: " + deviceId);
}
@OnClose
public void onClose(@PathParam("deviceId") String deviceId) {
sessions.remove(deviceId);
}
// 发送控制指令到设备
public static void sendCommand(String deviceId, String command) {
Session session = sessions.get(deviceId);
if (session != null && session.isOpen()) {
try {
session.getBasicRemote().sendText(
JSON.toJSONString(Map.of("cmd", command))
);
} catch (IOException e) {
e.printStackTrace();
}
}
}
@OnMessage
public void onMessage(String message, @PathParam("deviceId") String deviceId) {
// 接收设备上报状态
JSONObject data = JSON.parseObject(message);
// 更新设备状态到数据库
deviceService.updateStatus(deviceId, data.getInteger("status"));
}
}
🔌 三、API工具类 utils/api.js
javascript
const BASE_URL = 'https://your-domain.com/api'
const request = (url, method, data) => {
return new Promise((resolve, reject) => {
wx.request({
url: BASE_URL + url,
method,
data,
header: {
'Authorization': 'Bearer ' + wx.getStorageSync('token'),
'Content-Type': 'application/json'
},
success: (res) => {
if (res.data.code === 200) {
resolve(res.data)
} else {
reject(new Error(res.data.message))
}
},
fail: reject
})
})
}
module.exports = {
getNearbyStores: (params) => request('/store/nearby', 'GET', params),
getBanners: () => request('/banner/list', 'GET'),
getDeviceInfo: (id) => request(`/device/${id}`, 'GET'),
createOrder: (data) => request('/order/create', 'POST', data),
getWxPayParams: (orderId) => request(`/order/pay/${orderId}`, 'POST'),
getUserBalance: () => request('/user/balance', 'GET'),
sendDeviceCommand: (data) => request('/device/command', 'POST', data),
stopWash: (data) => request('/order/stop', 'POST', data)
}
📊 系统架构图
┌─────────────┐ ┌──────────────┐ ┌─────────────┐
│ 微信小程序 │────▶│ SpringBoot │────▶│ MySQL │
│ (前端) │ │ (后端API) │ │ (数据库) │
└─────────────┘ └──────┬───────┘ └─────────────┘
│
┌────────────┼────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ 微信支付 │ │ WebSocket│ │ MQTT协议 │
│ │ │ 设备通信 │ │ IoT设备 │
└──────────┘ └──────────┘ └──────────┘
✅ 核心功能清单
| 功能 | 说明 |
|---|---|
| 🔐 扫码识别设备 | 微信扫一扫 → 解析设备ID |
| 💳 微信支付 | JSAPI支付,余额支付 |
| 🚿 远程控制 | WebSocket实时控制洗车机 |
| 📍 LBS定位 | 查找附近洗车点 |
| 📊 订单管理 | 订单状态全流程跟踪 |
| 💰 余额充值 | 微信充值/消费记录 |
| 🔔 消息通知 | 支付成功/洗车完成推送 |
💡 提示:实际部署需要配置 微信支付商户号、SSL证书、MQTT Broker(如EMQX) 对接IoT洗车设备硬件。需要完整项目可以进一步沟通!
更多推荐
所有评论(0)