🎤 省钱兄 JAVA 同城自助KTV无人KTV线上预约系统 — 微信公众号+小程序+H5+APP + 物联网 全链路逻辑

2026年最新版,基于 Spring Cloud Alibaba + UniApp + MQTT + 树莓派边缘计算 的四端一体全渠道方案


🏗️ 一、系统总体架构(物联网四层架构)


┌─────────────────────────────────────────────────────────┐
│                    📱 多端接入层 (UniApp)                  │
│    微信公众号  │  微信小程序  │  H5  │  APP (Android/iOS)  │
├─────────────────────────────────────────────────────────┤
│                  🖥️ 云端业务层 (Spring Cloud)              │
│  用户服务 │ 订单服务 │ 设备服务 │ 支付服务 │ 营销服务 │ 消息服务 │
├─────────────────────────────────────────────────────────┤
│                 🔌 通信中间件层                            │
│        WebSocket(Netty) │ MQTT(EMQX) │ Kafka │ Redis    │
├─────────────────────────────────────────────────────────┤
│                 📡 边缘计算层 (树莓派/RK3588)               │
│    本地决策引擎 │ GPIO控制 │ MQTT客户端 │ 离线缓存         │
└─────────────────────────────────────────────────────────┘
层级 技术选型 核心职责
多端前端 UniApp(Vue3) + uView UI 四端代码复用率 90%+
API网关 Spring Cloud Gateway 统一鉴权、限流、路由
微服务 Nacos + Sentinel + Seata 注册发现、熔断、分布式事务
数据库 MySQL(分库分表) + Redis + MongoDB 结构化/缓存/日志
实时通信 Netty WebSocket + EMQX MQTT 状态推送、设备控制
消息队列 RocketMQ / Kafka 异步解耦、削峰填谷
边缘节点 树莓派4B + Java OpenJDK + RXTX 本地控制、断网自治

📡 二、物联网结合核心逻辑(重点)

🔄 完整业务流转时序图


用户              云端服务器              EMQX MQTT           树莓派(包厢)
 │                   │                      │                   │
 │── 扫码/选房 ────▶│                      │                   │
 │                   │── 检查设备状态 ─────▶│                   │
 │                   │◀── 返回空闲 ────────│                   │
 │── 创建订单 ─────▶│                      │                   │
 │                   │── 支付回调 ─────────▶│                   │
 │                   │                      │── 开门指令 ──────▶│
 │                   │                      │                   │── ✅ 开电磁锁
 │                   │                      │◀── 状态上报 ─────│
 │                   │◀── WebSocket推送 ───│                   │
 │◀── "已开门" ─────│                      │                   │
 │                   │                      │                   │
 │── 扫码开门 ─────▶│                      │                   │
 │                   │── 验证二维码 ───────▶│                   │
 │                   │                      │── 开门指令 ──────▶│
 │                   │                      │                   │── ✅ 开锁+开音响
 │                   │                      │◀── 状态上报 ─────│
 │◀── "开始唱歌" ──│                      │                   │
 │                   │                      │                   │
 │── 结束唱歌 ─────▶│                      │                   │
 │                   │── 计时结算 ─────────▶│                   │
 │                   │                      │── 关设备指令 ────▶│
 │                   │                      │                   │── ✅ 关音响+关灯
 │                   │                      │◀── 状态上报 ─────│
 │◀── 订单完成 ────│                      │                   │

🔐 核心物联网代码

1️⃣ MQTT 主题设计

ktv/device/{deviceSn}/command     → 云端下发控制指令
ktv/device/{deviceSn}/status      → 设备状态上报
ktv/device/{deviceSn}/heartbeat   → 心跳保活
ktv/order/{orderNo}/sync          → 订单状态同步
2️⃣ 云端发送控制指令

java

@Service
public class MqttControlService {

    @Autowired
    private MqttGateway mqttGateway;

    /**
     * 发送开门指令到包厢设备
     */
    public void sendOpenDoor(String deviceSn, String orderNo) {
        MqttMessage message = new MqttMessage(
            JSON.toJSONString(Map.of(
                "cmd", "OPEN_DOOR",
                "orderNo", orderNo,
                "timestamp", System.currentTimeMillis()
            )).getBytes()
        );
        mqttGateway.publish("ktv/device/" + deviceSn + "/command", message);
    }

    /**
     * 发送关门指令
     */
    public void sendCloseDoor(String deviceSn) {
        mqttGateway.publish("ktv/device/" + deviceSn + "/command",
            "{\"cmd\":\"CLOSE_DOOR\"}".getBytes());
    }

    /**
     * 发送灯光控制
     */
    public void sendLightControl(String deviceSn, String mode) {
        mqttGateway.publish("ktv/device/" + deviceSn + "/command",
            JSON.toJSONString(Map.of("cmd","LIGHT","mode",mode)).getBytes());
    }
}
3️⃣ 树莓派边缘节点(Java)

java

@Component
public class DeviceController {

    private final GpioController gpioController;
    private final WebSocketService wsService;

    /**
     * 订阅 MQTT 命令主题
     */
    @MqttSubscribe("ktv/+/command")
    public void handleCommand(String topic, String payload) {
        JSONObject cmd = JSON.parseObject(payload);
        String roomId = topic.split("/")[1];
        String action = cmd.getString("cmd");

        switch (action) {
            case "OPEN_DOOR":
                gpioController.unlockDoor(roomId);      // 电磁锁开门
                wsService.pushStatus(roomId, "门已开启");
                break;

            case "CLOSE_DOOR":
                gpioController.lockDoor(roomId);
                wsService.pushStatus(roomId, "门已关闭");
                break;

            case "LIGHT":
                String mode = cmd.getString("mode");
                gpioController.setLight(roomId, mode);   // 灯光模式
                break;

            case "START_SING":
                gpioController.powerOnAudio(roomId);     // 开启音响
                break;

            case "STOP_SING":
                gpioController.powerOffAudio(roomId);
                break;
        }
    }

    /**
     * 心跳保活(每30秒)
     */
    @Scheduled(fixedRate = 30000)
    public void heartbeat() {
        mqttClient.publish("ktv/device/" + deviceSn + "/heartbeat",
            "{\"status\":\"online\",\"uptime\":" + uptime + "}".getBytes());
    }

    /**
     * 离线模式:断网时本地验证二维码开门
     */
    public boolean verifyQrCodeOffline(String qrCode) {
        // 本地缓存最近24小时有效订单
        List<String> cachedOrders = localCache.getValidOrders();
        return cachedOrders.contains(qrCode);
    }
}
4️⃣ MQTT 配置(EMQX + Spring Boot)

yaml

# application.yml
mqtt:
  broker-url: tcp://emqx-server:1883
  client-id: ktv-cloud-server
  username: ${MQTT_USER}
  password: ${MQTT_PASS}
  default-topic: ktv/device/+/status
  qos: 1  # 至少一次

java

@Configuration
public class MqttConfig {

    @Bean
    public MqttPahoClientFactory mqttClientFactory() {
        DefaultMqttPahoClientFactory factory = new DefaultMqttPahoClientFactory();
        MqttConnectOptions options = new MqttConnectOptions();
        options.setServerURIs(new String[]{"tcp://emqx:1883"});
        options.setUserName("admin");
        options.setPassword("password".toCharArray());
        options.setCleanSession(true);
        options.setAutomaticReconnect(true);
        options.setConnectionTimeout(30);
        options.setKeepAliveInterval(60);
        factory.setConnectionOptions(options);
        return factory;
    }
}

📱 三、四端统一前端逻辑(UniApp)

🔑 多端统一登录(JWT + OAuth2.0)


javascript

// utils/auth.js
const BASE_URL = 'https://api.ktv-system.com'

// 统一登录(适配四端)
export const unifiedLogin = () => {
  // #ifdef MP-WEIXIN
  return wx.login().then(res => {
    return request('/auth/wechat-login', 'POST', { code: res.code })
  })
  // #endif

  // #ifdef H5
  return request('/auth/h5-login', 'POST', { phone: '', smsCode: '' })
  // #endif

  // #ifdef APP-PLUS
  return uni.login().then(res => {
    return request('/auth/app-login', 'POST', { token: res.authResult.access_token })
  })
  // #endif
}

// 统一支付
export const unifiedPay = (orderId) => {
  // #ifdef MP-WEIXIN
  return wx.requestPayment({
    timeStamp, nonceStr, package: `prepay_id=${prepayId}`,
    signType: 'MD5', paySign
  })
  // #endif

  // #ifdef H5
  window.location.href = payUrl
  // #endif

  // #ifdef APP-PLUS
  return uni.requestPayment({ provider: 'wxpay', orderInfo })
  // #endif
}

🎤 用户端核心页面


vue

<!-- pages/index/index.vue -->
<template>
  <view class="ktv-app">
    <!-- 地图选点 -->
    <map 
      :latitude="lat" 
      :longitude="lng" 
      :markers="markers"
      @tap="selectRoom"
      class="map-container"
    />

    <!-- 包厢列表 -->
    <view class="room-list">
      <view 
        v-for="room in rooms" 
        :key="room.id" 
        class="room-card"
        @tap="goBooking(room)"
      >
        <image :src="room.image" class="room-img" />
        <view class="room-info">
          <text class="room-name">{{ room.roomNumber }}</text>
          <text class="room-type">{{ room.typeName }}</text>
          <view class="room-status" :class="room.statusClass">
            {{ room.statusText }}
          </view>
        </view>
        <view class="room-price">
          <text class="price">¥{{ room.hourlyPrice }}</text>
          <text class="unit">/小时</text>
        </view>
      </view>
    </view>

    <!-- 动态定价提示 -->
    <view class="pricing-tip" v-if="isPeak">
      🔥 当前为高峰时段,价格上浮50%
    </view>
  </view>
</template>

<script>
export default {
  data() {
    return {
      lat: 34.26,
      lng: 108.94,
      rooms: [],
      markers: []
    }
  },
  computed: {
    isPeak() {
      const hour = new Date().getHours()
      return (hour >= 18 && hour <= 23) || (hour >= 10 && hour <= 12)
    }
  },
  onLoad() {
    this.loadNearbyRooms()
    this.getLocation()
  },
  methods: {
    async loadNearbyRooms() {
      const res = await this.$api.getNearbyRooms({
        lat: this.lat,
        lng: this.lng,
        radius: 5000
      })
      this.rooms = res.data.map(r => ({
        ...r,
        statusText: r.status === 1 ? '空闲' : r.status === 2 ? '使用中' : '维护中',
        statusClass: r.status === 1 ? 'online' : r.status === 2 ? 'busy' : 'offline'
      }))
      this.markers = this.rooms.map(r => ({
        id: r.id,
        latitude: r.lat,
        longitude: r.lng,
        title: r.roomNumber,
        iconPath: r.status === 1 ? '/static/green.png' : '/static/red.png'
      }))
    },
    goBooking(room) {
      uni.navigateTo({
        url: `/pages/booking/booking?roomId=${room.id}`
      })
    }
  }
}
</script>

📋 预约下单页


vue

<!-- pages/booking/booking.vue -->
<template>
  <view class="booking-page">
    <!-- 包厢信息 -->
    <view class="device-card">
      <image :src="room.image" />
      <view class="info">
        <text class="name">{{ room.roomNumber }} - {{ room.typeName }}</text>
        <text class="addr">{{ room.address }}</text>
        <view class="status" :class="room.statusClass">
          <view class="dot"></view>
          {{ room.statusText }}
        </view>
      </view>
    </view>

    <!-- 套餐选择(动态定价) -->
    <view class="section">
      <view class="title">选择套餐</view>
      <view 
        v-for="pkg in packages" 
        :key="pkg.id"
        :class="['pkg-item', { active: selectedPkg === pkg.id }]"
        @tap="selectPkg(pkg)"
      >
        <text class="pkg-name">{{ pkg.name }}</text>
        <text class="pkg-desc">{{ pkg.description }}</text>
        <text class="pkg-price">¥{{ pkg.price }}</text>
        <text class="pkg-duration">{{ pkg.duration }}分钟</text>
      </view>
    </view>

    <!-- 时段选择 -->
    <view class="section">
      <view class="title">选择时段</view>
      <scroll-view scroll-x class="time-slots">
        <view 
          v-for="slot in timeSlots" 
          :key="slot.id"
          :class="['slot', { disabled: !slot.available, active: selectedSlot === slot.id }]"
          @tap="selectSlot(slot)"
        >
          {{ slot.time }}
          <text v-if="!slot.available" class="sold">已约满</text>
        </view>
      </scroll-view>
    </view>

    <!-- 底部支付 -->
    <view class="bottom-bar">
      <view class="total">
        合计:<text class="amount">¥{{ totalPrice }}</text>
      </view>
      <button class="pay-btn" @tap="submitOrder">
        立即支付 ¥{{ totalPrice }}
      </button>
    </view>
  </view>
</template>

<script>
export default {
  data() {
    return {
      room: {},
      packages: [
        { id: 1, name: '欢唱套餐', price: 68, duration: 120, description: '小包厢' },
        { id: 2, name: '畅唱套餐', price: 128, duration: 180, description: '中包厢' },
        { id: 3, name: '豪唱套餐', price: 228, duration: 240, description: '大包厢+果盘' }
      ],
      selectedPkg: 1,
      selectedSlot: null,
      timeSlots: [],
      totalPrice: 68
    }
  },
  onLoad(options) {
    this.roomId = options.roomId
    this.loadRoomInfo()
    this.loadTimeSlots()
  },
  methods: {
    async submitOrder() {
      // 1. 创建订单(分布式锁防超卖)
      const order = await this.$api.createOrder({
        roomId: this.roomId,
        packageId: this.selectedPkg,
        timeSlot: this.selectedSlot
      })

      // 2. 统一支付
      await this.$util.unifiedPay(order.orderNo)

      // 3. 支付成功 → MQTT开门指令
      this.$api.paySuccess(order.orderNo)

      // 4. 跳转到唱歌控制页
      uni.redirectTo({
        url: `/pages/sing/sing?orderId=${order.orderNo}`
      })
    }
  }
}
</script>

🎮 四、唱歌控制页(物联网实时交互)


vue

<!-- pages/sing/sing.vue -->
<template>
  <view class="sing-page">
    <view class="header">
      <text class="title">🎤 {{ room.roomNumber }}</text>
      <text class="timer">剩余 {{ remainTime }} 分钟</text>
    </view>

    <!-- 实时状态(WebSocket推送) -->
    <view class="status-bar">
      <view class="stat-item">
        <text class="icon">🔊</text>
        <text>{{ volume }}%</text>
      </view>
      <view class="stat-item">
        <text class="icon">💡</text>
        <text>{{ lightMode }}</text>
      </view>
      <view class="stat-item">
        <text class="icon">🌡️</text>
        <text>{{ temperature }}°C</text>
      </view>
    </view>

    <!-- 控制面板 -->
    <view class="controls">
      <view class="ctrl-btn" @tap="toggleMic">
        <text :class="{ active: micOn }">🎙️ 麦克风</text>
      </view>
      <view class="ctrl-btn" @tap="toggleLight">
        <text :class="{ active: lightOn }">💡 灯光</text>
      </view>
      <view class="ctrl-btn" @tap="callService">
        <text>📞 呼叫服务</text>
      </view>
    </view>

    <!-- 点歌列表 -->
    <view class="song-list">
      <view v-for="song in songs" :key="song.id" class="song-item" @tap="playSong(song)">
        <text class="song-name">{{ song.name }}</text>
        <text class="song-singer">{{ song.singer }}</text>
      </view>
    </view>

    <!-- 结束按钮 -->
    <button class="end-btn" @tap="endSing">结束唱歌</button>
  </view>
</template>

<script>
export default {
  data() {
    return {
      orderId: '',
      room: {},
      remainTime: 0,
      volume: 80,
      lightMode: '彩色',
      micOn: true,
      lightOn: true,
      songs: []
    }
  },
  onLoad(options) {
    this.orderId = options.orderId
    this.connectWebSocket()    // WebSocket实时状态
    this.startTimer()
  },
  methods: {
    // WebSocket 实时接收设备状态
    connectWebSocket() {
      this.ws = uni.connectSocket({
        url: `wss://api.ktv-system.com/ws/order/${this.orderId}`
      })
      this.ws.onMessage((res) => {
        const data = JSON.parse(res.data)
        if (data.type === 'device_status') {
          this.volume = data.volume
          this.lightMode = data.lightMode
          this.remainTime = data.remainTime
        }
      })
    },

    // 发送控制指令到设备(MQTT)
    async toggleMic() {
      this.micOn = !this.micOn
      await this.$api.sendDeviceCommand({
        orderId: this.orderId,
        command: this.micOn ? 'MIC_ON' : 'MIC_OFF'
      })
    },

    async toggleLight() {
      this.lightOn = !this.lightOn
      await this.$api.sendDeviceCommand({
        orderId: this.orderId,
        command: this.lightOn ? 'LIGHT_ON' : 'LIGHT_OFF'
      })
    },

    // 结束唱歌 → 结算
    async endSing() {
      uni.showModal({
        title: '确认结束',
        content: '确定要结束本次唱歌吗?',
        success: async (res) => {
          if (res.confirm) {
            await this.$api.endOrder(this.orderId)
            // MQTT关设备指令
            await this.$api.sendDeviceCommand({
              orderId: this.orderId,
              command: 'STOP_ALL'
            })
            uni.redirectTo({ url: '/pages/my/my' })
          }
        }
      })
    }
  }
}
</script>

🗄️ 五、核心数据库设计


sql

-- KTV包厢表
CREATE TABLE `ktv_room` (
  `id` BIGINT PRIMARY KEY AUTO_INCREMENT,
  `room_number` VARCHAR(20) NOT NULL COMMENT '包间编号 K01',
  `room_type` TINYINT DEFAULT 1 COMMENT '1小 2中 3大',
  `hourly_price` DECIMAL(10,2) DEFAULT 68.00 COMMENT '每小时价格',
  `peak_price_multiplier` DECIMAL(3,2) DEFAULT 1.5 COMMENT '高峰倍率',
  `device_sn` VARCHAR(64) COMMENT '设备序列号',
  `latitude` DECIMAL(10,7),
  `longitude` DECIMAL(10,7),
  `address` VARCHAR(255),
  `current_status` TINYINT DEFAULT 1 COMMENT '0离线 1空闲 2使用中 3维护',
  `created_time` DATETIME DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- 预约订单表
CREATE TABLE `booking_order` (
  `id` BIGINT PRIMARY KEY AUTO_INCREMENT,
  `order_no` VARCHAR(32) UNIQUE NOT NULL COMMENT 'CW20260513001',
  `user_id` BIGINT NOT NULL,
  `room_id` BIGINT NOT NULL,
  `package_id` TINYINT,
  `start_time` DATETIME NOT NULL,
  `end_time` DATETIME NOT NULL,
  `amount` DECIMAL(10,2) NOT NULL,
  `pay_method` TINYINT DEFAULT 1 COMMENT '1微信 2余额',
  `status` TINYINT DEFAULT 0 COMMENT '0待支付 1已支付 2唱歌中 3已完成 4已取消',
  `pay_time` DATETIME,
  `qr_code` VARCHAR(255) COMMENT '开门二维码',
  `created_time` DATETIME DEFAULT CURRENT_TIMESTAMP,
  INDEX `idx_user_time` (`user_id`, `start_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- 智能设备表
CREATE TABLE `smart_device` (
  `id` BIGINT PRIMARY KEY AUTO_INCREMENT,
  `device_sn` VARCHAR(64) UNIQUE NOT NULL,
  `room_id` BIGINT NOT NULL,
  `device_type` TINYINT DEFAULT 1 COMMENT '1门锁 2音响 3灯光',
  `device_status` TINYINT DEFAULT 1 COMMENT '0离线 1在线',
  `last_heartbeat` DATETIME,
  `firmware_version` VARCHAR(20),
  `created_time` DATETIME DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- 设备控制日志
CREATE TABLE `device_command_log` (
  `id` BIGINT PRIMARY KEY AUTO_INCREMENT,
  `order_no` VARCHAR(32),
  `device_sn` VARCHAR(64),
  `command` VARCHAR(32) NOT NULL,
  `status` TINYINT DEFAULT 0 COMMENT '0下发中 1已执行 2失败',
  `created_time` DATETIME DEFAULT CURRENT_TIMESTAMP,
  INDEX `idx_order` (`order_no`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

🔧 六、后端核心服务代码

1️⃣ 预约服务(Redisson分布式锁防超卖)


java

@Service
@Slf4j
public class BookingService {

    @Autowired
    private RedissonClient redissonClient;

    @Autowired
    private BookingOrderMapper orderMapper;

    @GlobalTransactional(name = "ktv-create-booking")
    public BookingOrder createBooking(BookingRequest req) {
        String lockKey = "room_lock:" + req.getRoomId();
        RLock lock = redissonClient.getLock(lockKey);

        try {
            // 10秒内获取锁,自动续期
            if (!lock.tryLock(10, 30, TimeUnit.SECONDS)) {
                throw new BookingException("预约太火爆,请稍后重试");
            }

            // 验证可用性
            RoomAvailability avail = checkAvailability(req.getRoomId(), req.getStartTime());
            if (!avail.isAvailable()) {
                throw new BookingException("该时段已被预约");
            }

            // 动态定价
            BigDecimal amount = pricingService.calculate(req.getStartTime(), req.getRoomType());

            // 创建订单
            BookingOrder order = new BookingOrder();
            order.setOrderNo(generateOrderNo());
            order.setUserId(req.getUserId());
            order.setRoomId(req.getRoomId());
            order.setAmount(amount);
            order.setStatus(BookingStatus.PENDING_PAYMENT);
            orderMapper.insert(order);

            log.info("订单创建成功: {}", order.getOrderNo());
            return order;

        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new BookingException("预约失败");
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    /**
     * 支付成功后 → MQTT开门 + 生成二维码
     */
    @Transactional
    public void onPaySuccess(String orderNo) {
        BookingOrder order = orderMapper.selectByOrderNo(orderNo);
        order.setStatus(BookingStatus.CONFIRMED);
        order.setPayTime(LocalDateTime.now());
        orderMapper.updateById(order);

        // 1. 生成开门二维码
        String qrCode = QrCodeUtil.generate(orderNo);
        order.setQrCode(qrCode);
        orderMapper.updateById(order);

        // 2. MQTT下发开门指令
        Room room = roomMapper.selectById(order.getRoomId());
        mqttControlService.sendOpenDoor(room.getDeviceSn(), orderNo);

        // 3. WebSocket推送用户
        webSocketService.push(order.getUserId(), 
            JSON.toJSONString(Map.of("type","door_opened","orderNo",orderNo)));

        log.info("支付成功,已开门: {}", orderNo);
    }
}

2️⃣ 动态定价引擎


java

@Service
public class PricingService {

    public BigDecimal calculatePrice(LocalDateTime slotTime, int roomType, boolean isVip) {
        // 基础价格
        BigDecimal base = roomType == 1 ? new BigDecimal("68")
                       : roomType == 2 ? new BigDecimal("128")
                       : new BigDecimal("228");

        int hour = slotTime.getHour();

        // 🔥 高峰时段加价 (18:00-23:00, 10:00-12:00)
        if ((hour >= 18 && hour <= 23) || (hour >= 10 && hour <= 12)) {
            base = base.multiply(new BigDecimal("1.5"));
        }

        // 👑 VIP折扣
        if (isVip) {
            base = base.multiply(new BigDecimal("0.85"));
        }

        // 📊 供需定价:空闲率<30%时提价20%
        double occupancy = getOccupancyRate(slotTime);
        if (occupancy < 0.3) {
            base = base.multiply(new BigDecimal("1.2"));
        }

        return base.setScale(2, RoundingMode.HALF_UP);
    }

    private double getOccupancyRate(LocalDateTime time) {
        // 从Redis获取当前时段预约率
        String key = "occupancy:" + time.toLocalDate();
        return redisTemplate.opsForZSet().score("room_booking", "current") != null
            ? 0.6 : 0.4;
    }
}

3️⃣ 支付回调 → 触发设备控制(Seata分布式事务)


java

@Service
@Slf4j
public class PayCallbackService {

    @Autowired
    private BookingService bookingService;

    @Autowired
    private MqttControlService mqttControl;

    /**
     * 微信支付回调
     */
    public String wechatNotify(String xmlData) {
        Map<String, String> result = WxPayApi.parseNotify(xmlData);

        if ("SUCCESS".equals(result.get("result_code"))) {
            String orderNo = result.get("out_trade_no");

            // Seata全局事务:支付成功 → 更新订单 → MQTT开门
            try {
                bookingService.onPaySuccess(orderNo);
            } catch (Exception e) {
                log.error("支付回调处理失败: {}", orderNo, e);
                return "<xml><return_code>FAIL</return_code></xml>";
            }
        }
        return "<xml><return_code>SUCCESS</return_code></xml>";
    }
}

📊 七、管理后台核心功能(Vue3 + Element Plus)

模块 功能
📊 数据驾驶舱 今日营收、核销率、设备在线率、会员转化率
🏠 包厢管理 增删改查、状态监控、价格配置、图片上传
📋 订单管理 实时订单列表、强制结束、退款处理
📡 设备监控 MQTT在线状态、心跳检测、远程重启、固件升级
👥 用户管理 会员等级、积分管理、消费记录
🎯 营销中心 优惠券发放、战队裂变、积分商城、动态定价规则
📱 全域验券 聚合美团/抖音/快手券码,Redis原子核销

vue

<!-- 管理后台 - 设备监控 -->
<template>
  <el-card>
    <template #header>
      <span>📡 设备实时监控</span>
    </template>
    <el-table :data="devices" stripe>
      <el-table-column prop="deviceSn" label="设备SN" />
      <el-table-column prop="roomNumber" label="包厢" />
      <el-table-column label="状态">
        <template #default="{ row }">
          <el-tag :type="row.status === 1 ? 'success' : 'danger'">
            {{ row.status === 1 ? '在线' : '离线' }}
          </el-tag>
        </template>
      </el-table-column>
      <el-table-column prop="lastHeartbeat" label="最后心跳" />
      <el-table-column label="操作">
        <template #default="{ row }">
          <el-button size="small" @click="remoteRestart(row)">远程重启</el-button>
          <el-button size="small" type="danger" @click="forceOffline(row)">强制下线</el-button>
        </template>
      </el-table-column>
    </el-table>
  </el-card>
</template>

🔒 八、安全体系

层面 措施
🔐 认证 JWT + OAuth2.0,四端统一Token,支持微信/手机号登录
🛡️ 传输 全站HTTPS + SSL/TLS,MQTT over TLS
🔑 支付 Token化 + RSA加密,支付服务独立部署隔离
📱 指令 AES加密MQTT控制指令,设备双向认证
📋 审计 ELK日志收集,操作日志全记录
👤 权限 RBAC角色模型,商家/管理员/用户分级

💰 九、省钱兄源码报价参考

版本 内容 价格
🥉 基础版 微信小程序 + H5,核心预约+支付 ¥4,888
🥈 专业版 小程序 + H5 + APP + 公众号,完整物联网 ¥9,888
🥇 旗舰版 四端全含 + 管理后台 + 源码交付 + 部署服务 ¥15,888

📞 联系:西安省钱兄网络科技有限公司 | 马晓东  | 📱 13895585204(微信同号)


✅ 核心亮点总结

能力 实现方式
🌐 四端一体 UniApp 一套代码 → 微信/H5/APP/公众号
📡 物联网控制 MQTT协议 + 树莓派边缘计算,响应<500ms
🔄 实时通信 Netty WebSocket 状态推送 + MQTT设备上报
💸 动态定价 高峰/VIP/供需 三维定价引擎
🛡️ 防超卖 Redisson分布式锁 + Seata分布式事务
🔌 离线自治 树莓派本地缓存,断网可扫码开门
📊 数据驱动 用户画像 + 智能排房 + 精准营销

这套系统是2026年同城无人KTV赛道的标杆级解决方案,从预约→支付→开门→唱歌→结算→数据分析,全链路物联网闭环,真正实现"无人值守、智能运营"!🎤🔥

更多推荐