当前位置: 首页 > news >正文

JAVA无人共享台球杆台球柜系统球杆柜租赁系统源码支持微信小程序

JAVA无人共享台球杆台球柜系统:智能体育器材租赁解决方案(支持微信小程序)

在共享经济3.0时代和体育产业数字化浪潮的双重驱动下,JAVA无人共享台球杆台球柜系统正成为体育器材租赁行业数字化转型的重要技术基础设施。根据国家体育总局数据显示,2023年中国台球运动人口已突破8000万,年复合增长率达15%,而专业台球器材的购置成本高昂,成为制约运动普及的重要因素。这套球杆柜租赁系统源码通过物联网技术和智能管理平台,有效解决了传统台球器材租赁存在的管理效率低、器材损耗大、用户体验差等结构性痛点。

从技术经济学角度分析,该系统采用SpringBoot+MyBatisPlus+MySQL的微服务分布式架构,实现了设备状态实时监控和租赁业务的高并发处理。基于Uniapp框架构建的微信小程序客户端,使运营方能够快速触达微信生态的10亿用户,极大降低了用户获取成本。管理后台采用Vue+ElementUI技术栈,为运营商提供了智能化的设备管理和数据分析能力。

行业前景方面,随着智能物联网技术的成熟和共享经济模式的深化,体育器材共享市场正朝着智能化、网络化、标准化方向发展。艾瑞咨询研究报告指出,智能共享器材租赁系统的应用可使设备利用率提升50%以上,运营成本降低40%。这套JAVA无人共享台球杆台球柜系统不仅实现了租赁流程的数字化重构,更通过智能调度算法和预防性维护机制为运营商提供了科学的设备管理和维护策略,将成为体育器材共享行业标准化发展的重要技术支撑。

系统技术架构深度解析 分布式租赁服务架构设计

本系统采用基于SpringBoot的微服务架构,结合MyBatisPlusMySQL构建高可用的租赁服务集群。以下是核心业务架构代码示例:

// 台球杆设备实体类
@Entity
@Table(name = "billiards_cue")
public class BilliardsCue {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;@Column(name = "cue_sn", unique = true, nullable = false)private String cueSn; // 球杆唯一编号@Column(name = "cue_brand")private String cueBrand; // 球杆品牌@Column(name = "cue_model")private String cueModel; // 球杆型号@Column(name = "cue_weight")private BigDecimal cueWeight; // 球杆重量@Column(name = "cue_tip_size")private BigDecimal cueTipSize; // 杆头尺寸@Column(name = "current_status")private Integer currentStatus; // 当前状态:0-空闲 1-租赁中 2-维护中@Column(name = "cabinet_id")private Long cabinetId; // 所属柜子ID@Column(name = "slot_position")private Integer slotPosition; // 槽位位置@Column(name = "rental_count")private Integer rentalCount = 0; // 租赁次数@Column(name = "last_maintenance_date")private LocalDate lastMaintenanceDate; // 最后维护日期@Column(name = "battery_level")private Integer batteryLevel; // 电量(智能球杆)@Column(name = "create_time")private LocalDateTime createTime;
}// 智能柜实体
@Entity
@Table(name = "smart_cabinet")
public class SmartCabinet {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;@Column(name = "cabinet_sn", unique = true, nullable = false)private String cabinetSn; // 柜子序列号@Column(name = "cabinet_name")private String cabinetName; // 柜子名称@Column(name = "location")private String location; // 安装位置@Column(name = "geo_location")private String geoLocation; // 地理坐标@Column(name = "total_slots")private Integer totalSlots; // 总槽位数@Column(name = "available_slots")private Integer availableSlots; // 可用槽位@Column(name = "cabinet_status")private Integer cabinetStatus; // 柜子状态@Column(name = "network_status")private Integer networkStatus; // 网络状态@Column(name = "power_status")private Integer powerStatus; // 电源状态@Column(name = "last_heartbeat")private LocalDateTime lastHeartbeat; // 最后心跳时间
}// 智能调度服务
@Service
public class CueDispatchService {@Autowiredprivate BilliardsCueMapper cueMapper;@Autowiredprivate SmartCabinetMapper cabinetMapper;@Autowiredprivate RedisTemplate<String, Object> redisTemplate;/*** 智能推荐可用球杆*/public List<CueRecommendationDTO> recommendCues(Long userId, String userLocation) {// 1. 获取附近可用的智能柜List<SmartCabinet> nearbyCabinets = findNearbyCabinets(userLocation, 5000); // 5公里范围内// 2. 获取用户偏好UserPreference preference = getUserPreference(userId);// 3. 多维度推荐排序return nearbyCabinets.stream().flatMap(cabinet -> getAvailableCues(cabinet.getId()).stream()).map(cue -> {RecommendationScore score = calculateRecommendationScore(cue, preference);return new CueRecommendationDTO(cue, score);}).sorted(Comparator.comparing(CueRecommendationDTO::getScore).reversed()).collect(Collectors.toList());}/*** 计算球杆推荐分数*/private RecommendationScore calculateRecommendationScore(BilliardsCue cue, UserPreference preference) {RecommendationScore score = new RecommendationScore();// 设备状态分数(40%权重)score.setDeviceStatusScore(calculateDeviceStatusScore(cue));// 用户偏好匹配度(30%权重)score.setPreferenceMatchScore(calculatePreferenceMatchScore(cue, preference));// 设备使用频率(20%权重)score.setUsageScore(calculateUsageScore(cue));// 维护状态(10%权重)score.setMaintenanceScore(calculateMaintenanceScore(cue));return score;}/*** 计算设备状态分数*/private double calculateDeviceStatusScore(BilliardsCue cue) {double score = 0;// 基础状态分数if (cue.getCurrentStatus() == 0) {score += 60; // 空闲状态}// 电量分数(智能球杆)if (cue.getBatteryLevel() != null) {score += cue.getBatteryLevel() * 0.4;}// 使用次数分数(适度使用的最优)if (cue.getRentalCount() < 100) {score += 20;} else if (cue.getRentalCount() < 500) {score += 15;} else {score += 5;}return score;}
}// 推荐评分模型
@Data
class RecommendationScore {private double deviceStatusScore;     // 设备状态分数private double preferenceMatchScore;  // 偏好匹配度private double usageScore;            // 使用频率分数private double maintenanceScore;      // 维护状态分数public double getScore() {return deviceStatusScore * 0.4 + preferenceMatchScore * 0.3 + usageScore * 0.2 + maintenanceScore * 0.1;}
}
租赁订单状态机引擎
// 租赁订单实体
@Entity
@Table(name = "cue_rental_order")
public class CueRentalOrder {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;@Column(name = "order_no", unique = true)private String orderNo; // 订单编号@Column(name = "user_id")private Long userId; // 用户ID@Column(name = "cue_id")private Long cueId; // 球杆ID@Column(name = "cabinet_id")private Long cabinetId; // 柜子ID@Column(name = "rental_start_time")private LocalDateTime rentalStartTime; // 租赁开始时间@Column(name = "rental_end_time")private LocalDateTime rentalEndTime; // 租赁结束时间@Column(name = "actual_return_time")private LocalDateTime actualReturnTime; // 实际归还时间@Column(name = "rental_duration")private Integer rentalDuration; // 租赁时长(分钟)@Column(name = "total_amount")private BigDecimal totalAmount; // 订单总额@Column(name = "payment_status")private Integer paymentStatus; // 支付状态@Column(name = "order_status")private Integer orderStatus; // 订单状态@Column(name = "create_time")private LocalDateTime createTime;
}// 租赁订单服务
@Service
@Transactional
public class CueRentalService {@Autowiredprivate CueRentalOrderMapper orderMapper;@Autowiredprivate BilliardsCueMapper cueMapper;@Autowiredprivate SmartCabinetMapper cabinetMapper;@Autowiredprivate IotDeviceService iotDeviceService;@Autowiredprivate NotificationService notificationService;/*** 创建租赁订单*/public CueRentalOrder createRentalOrder(RentalOrderDTO orderDTO) {// 1. 验证设备可用性BilliardsCue cue = cueMapper.selectById(orderDTO.getCueId());if (cue.getCurrentStatus() != 0) {throw new RentalException("球杆暂不可用");}// 2. 创建订单记录CueRentalOrder order = new CueRentalOrder();BeanUtils.copyProperties(orderDTO, order);order.setOrderNo(generateOrderNo());order.setOrderStatus(1); // 待取杆order.setPaymentStatus(1); // 待支付order.setCreateTime(LocalDateTime.now());orderMapper.insert(order);// 3. 更新球杆状态cue.setCurrentStatus(1); // 租赁中cueMapper.updateById(cue);// 4. 控制智能柜开锁boolean openResult = iotDeviceService.openCabinetSlot(orderDTO.getCabinetId(), cue.getSlotPosition());if (!openResult) {throw new RentalException("设备开锁失败,请重试");}// 5. 发送订单创建通知notificationService.sendRentalStartNotification(order);return order;}/*** 归还球杆处理*/public boolean returnCue(String orderNo, Long cabinetId, Integer slotPosition) {CueRentalOrder order = getOrderByNo(orderNo);// 状态机验证if (order.getOrderStatus() != 2) {throw new RentalException("订单状态异常");}// 1. 更新球杆状态和位置BilliardsCue cue = cueMapper.selectById(order.getCueId());cue.setCurrentStatus(0); // 空闲cue.setCabinetId(cabinetId);cue.setSlotPosition(slotPosition);cue.setRentalCount(cue.getRentalCount() + 1);cueMapper.updateById(cue);// 2. 更新订单状态order.setOrderStatus(3); // 已完成order.setActualReturnTime(LocalDateTime.now());order.setRentalDuration(calculateRentalDuration(order.getRentalStartTime(), order.getActualReturnTime()));orderMapper.updateById(order);// 3. 计算费用calculateRentalFee(order);// 4. 发送归还成功通知notificationService.sendReturnSuccessNotification(order);return true;}/*** 计算租赁时长(分钟)*/private Integer calculateRentalDuration(LocalDateTime startTime, LocalDateTime endTime) {return (int) Duration.between(startTime, endTime).toMinutes();}/*** 计算租赁费用*/private void calculateRentalFee(CueRentalOrder order) {// 基础计费规则:首小时X元,之后每半小时Y元BigDecimal basePrice = new BigDecimal("10.00"); // 首小时价格BigDecimal unitPrice = new BigDecimal("5.00");  // 后续半小时价格int totalMinutes = order.getRentalDuration();int baseMinutes = 60; // 首小时int unitMinutes = 30; // 计费单元if (totalMinutes <= baseMinutes) {order.setTotalAmount(basePrice);} else {int extraMinutes = totalMinutes - baseMinutes;int extraUnits = (int) Math.ceil((double) extraMinutes / unitMinutes);order.setTotalAmount(basePrice.add(unitPrice.multiply(BigDecimal.valueOf(extraUnits))));}orderMapper.updateById(order);}private String generateOrderNo() {return "RO" + System.currentTimeMillis() + RandomUtil.randomNumbers(6);}
}
微信小程序Uniapp实现

用户端采用Uniapp开发,支持微信小程序。以下是核心租赁功能页面实现:

<template><view class="cue-rental-container"><!-- 顶部定位和搜索 --><view class="header-section"><view class="location-selector" @tap="chooseLocation"><image src="/static/location-icon.png" class="location-icon"></image><text class="location-text">{{ currentLocation }}</text><image src="/static/arrow-down.png" class="arrow-icon"></image></view><view class="search-box" @tap="goToSearch"><image src="/static/search-icon.png" class="search-icon"></image><text class="search-placeholder">搜索台球厅或位置</text></view></view><!-- 附近智能柜 --><view class="nearby-cabinets-section"><view class="section-title">附近智能柜</view><view class="cabinet-list"><view v-for="cabinet in nearbyCabinets" :key="cabinet.id" class="cabinet-card" @tap="viewCabinetDetail(cabinet)"><view class="cabinet-header"><image src="/static/cabinet-icon.png" class="cabinet-icon"></image><view class="cabinet-info"><text class="cabinet-name">{{ cabinet.cabinetName }}</text><view class="cabinet-status"><view class="status-indicator" :class="getStatusClass(cabinet.cabinetStatus)"></view><text class="status-text">{{ getStatusText(cabinet.cabinetStatus) }}</text></view></view><view class="distance-badge"><text>{{ cabinet.distance }}m</text></view></view><view class="cabinet-details"><view class="detail-item"><image src="/static/location-small.png" class="detail-icon"></image><text class="detail-text">{{ cabinet.location }}</text></view><view class="detail-item"><image src="/static/cue-icon.png" class="detail-icon"></image><text class="detail-text">可用球杆: {{ cabinet.availableSlots }}/{{ cabinet.totalSlots }}</text></view><view class="detail-item"><image src="/static/price-icon.png" class="detail-icon"></image><text class="detail-text">首小时¥10,之后每半小时¥5</text></view></view><view class="cabinet-actions"><button class="nav-btn" @tap.stop="openNavigation(cabinet)"><image src="/static/navigation.png" class="btn-icon"></image>导航</button><button class="rent-btn" @tap.stop="quickRent(cabinet)">立即租杆</button></view></view></view></view><!-- 智能柜详情模态框 --><uni-popup ref="cabinetPopup" type="bottom"><view class="cabinet-detail-modal"><view class="modal-header"><text class="modal-title">{{ selectedCabinet.cabinetName }}</text><text class="close-btn" @tap="closeCabinetModal">×</text></view><view class="cabinet-status-overview"><view class="status-card"><text class="status-value">{{ selectedCabinet.availableSlots }}</text><text class="status-label">可用球杆</text></view><view class="status-card"><text class="status-value">{{ selectedCabinet.totalSlots - selectedCabinet.availableSlots }}</text><text class="status-label">已租出</text></view><view class="status-card"><text class="status-value">{{ getUptime(selectedCabinet.lastHeartbeat) }}</text><text class="status-label">运行状态</text></view></view><!-- 球杆列表 --><view class="cue-list-section"><text class="section-title">可用球杆</text><scroll-view class="cue-list" scroll-y><view v-for="cue in availableCues" :key="cue.id" class="cue-item" :class="{recommended: cue.isRecommended}"><view class="cue-info"><image src="/static/cue.png" class="cue-icon"></image><view class="cue-details"><text class="cue-brand">{{ cue.cueBrand }} {{ cue.cueModel }}</text><view class="cue-specs"><text class="spec-item">重量: {{ cue.cueWeight }}oz</text><text class="spec-item">杆头: {{ cue.cueTipSize }}mm</text><text class="spec-item">租赁: {{ cue.rentalCount }}次</text></view><view class="cue-status"><text class="status-tag" :class="getCueStatusClass(cue)">{{ getCueStatusText(cue) }}</text><text class="battery-level" v-if="cue.batteryLevel">电量: {{ cue.batteryLevel }}%</text></view></view></view><button class="select-btn" @tap="selectCue(cue)" :disabled="cue.currentStatus !== 0">{{ cue.currentStatus === 0 ? '选择' : '不可用' }}</button></view></scroll-view></view><button class="confirm-rent-btn" @tap="confirmRental" :disabled="!selectedCue">确认租赁</button></view></uni-popup><!-- 租赁确认模态框 --><uni-popup ref="confirmPopup" type="center"><view class="confirm-rental-modal"><text class="modal-title">确认租赁</text><view class="rental-info"><view class="info-item"><text class="info-label">球杆信息:</text><text class="info-value">{{ selectedCue.cueBrand }} {{ selectedCue.cueModel }}</text></view><view class="info-item"><text class="info-label">取杆位置:</text><text class="info-value">{{ selectedCabinet.location }}</text></view><view class="info-item"><text class="info-label">计费规则:</text><text class="info-value">首小时¥10,之后每半小时¥5</text></view></view><view class="rental-agreement"><checkbox-group @change="onAgreementChange"><label class="agreement-item"><checkbox :checked="agreementChecked" /><text class="agreement-text">我已阅读并同意《球杆租赁协议》</text></label></checkbox-group></view><view class="modal-actions"><button class="cancel-btn" @tap="cancelRental">取消</button><button class="confirm-btn" @tap="createRentalOrder" :disabled="!agreementChecked">确认租赁</button></view></view></uni-popup><!-- 租赁中状态面板 --><view class="renting-panel" v-if="currentRental"><view class="panel-header"><text class="panel-title">租赁中</text><text class="rental-duration">{{ formatDuration(currentRental.duration) }}</text></view><view class="rental-info"><text class="info-text">球杆: {{ currentRental.cueInfo }}</text><text class="info-text">位置: {{ currentRental.cabinetLocation }}</text><text class="info-text">当前费用: ¥{{ currentRental.currentFee }}</text></view><view class="action-buttons"><button class="extend-btn" @tap="extendRental">续租</button><button class="return-btn" @tap="showReturnGuide">归还</button></view></view><!-- 底部导航 --><view class="tabbar"><view class="tabbar-item" :class="{active: currentTab === 0}" @tap="switchTab(0)"><image :src="currentTab === 0 ? '/static/home-active.png' : '/static/home.png'" class="tab-icon"></image><text class="tab-text">首页</text></view><view class="tabbar-item" :class="{active: currentTab === 1}" @tap="switchTab(1)"><image :src="currentTab === 1 ? '/static/order-active.png' : '/static/order.png'" class="tab-icon"></image><text class="tab-text">订单</text></view><view class="tabbar-item" :class="{active: currentTab === 2}" @tap="switchTab(2)"><image :src="currentTab === 2 ? '/static/profile-active.png' : '/static/profile.png'" class="tab-icon"></image><text class="tab-text">我的</text></view></view></view>
</template><script>
export default {data() {return {currentTab: 0,currentLocation: '定位中...',nearbyCabinets: [],selectedCabinet: {},availableCues: [],selectedCue: null,currentRental: null,agreementChecked: false,// 租赁计时器rentalTimer: null,rentalStartTime: null}},computed: {canRent() {return this.selectedCue && this.agreementChecked}},onLoad() {this.getCurrentLocation()this.loadNearbyCabinets()this.checkCurrentRental()},onUnload() {this.clearRentalTimer()},methods: {// 获取当前位置async getCurrentLocation() {try {const location = await this.getUserLocation()const address = await this.reverseGeocode(location.latitude, location.longitude)this.currentLocation = address} catch (error) {this.currentLocation = '定位失败'}},// 加载附近智能柜async loadNearbyCabinets() {try {const location = await this.getUserLocation()const params = {latitude: location.latitude,longitude: location.longitude,radius: 5000}const res = await this.$http.get('/api/cabinet/nearby', { params })if (res.code === 200) {this.nearbyCabinets = res.data}} catch (error) {uni.showToast({title: '加载失败',icon: 'none'})}},// 查看柜子详情async viewCabinetDetail(cabinet) {this.selectedCabinet = cabinettry {const res = await this.$http.get(`/api/cabinet/${cabinet.id}/cues`)if (res.code === 200) {this.availableCues = res.datathis.$refs.cabinetPopup.open()}} catch (error) {uni.showToast({title: '加载球杆信息失败',icon: 'none'})}},// 选择球杆selectCue(cue) {this.selectedCue = cue},// 确认租赁confirmRental() {if (!this.selectedCue) {uni.showToast({title: '请选择球杆',icon: 'none'})return}this.$refs.confirmPopup.open()},// 创建租赁订单async createRentalOrder() {try {const orderData = {cueId: this.selectedCue.id,cabinetId: this.selectedCabinet.id}const res = await this.$http.post('/api/rental/create', orderData)if (res.code === 200) {uni.showToast({title: '租赁成功',icon: 'success'})this.closeAllModals()this.startRentalTimer(res.data)}} catch (error) {uni.showToast({title: error.message || '租赁失败',icon: 'none'})}},// 开始租赁计时startRentalTimer(rentalOrder) {this.currentRental = {id: rentalOrder.id,cueInfo: `${this.selectedCue.cueBrand} ${this.selectedCue.cueModel}`,cabinetLocation: this.selectedCabinet.location,startTime: new Date(),duration: 0,currentFee: '0.00'}this.rentalStartTime = new Date()this.rentalTimer = setInterval(() => {this.updateRentalInfo()}, 60000) // 每分钟更新一次},// 更新租赁信息updateRentalInfo() {const now = new Date()const duration = Math.floor((now - this.rentalStartTime) / 60000) // 分钟this.currentRental.duration = durationthis.currentRental.currentFee = this.calculateCurrentFee(duration)},// 计算当前费用calculateCurrentFee(duration) {const basePrice = 10.00 // 首小时const unitPrice = 5.00  // 每半小时if (duration <= 60) {return basePrice.toFixed(2)} else {const extraUnits = Math.ceil((duration - 60) / 30)return (basePrice + unitPrice * extraUnits).toFixed(2)}},// 归还球杆async showReturnGuide() {uni.showModal({title: '归还球杆',content: '请将球杆放回任意智能柜的空闲槽位,系统将自动结束计费',confirmText: '确认归还',success: async (res) => {if (res.confirm) {await this.returnCue()}}})},// 执行归还操作async returnCue() {try {const res = await this.$http.post('/api/rental/return', {orderId: this.currentRental.id})if (res.code === 200) {uni.showToast({title: '归还成功',icon: 'success'})this.clearRentalTimer()this.currentRental = nullthis.loadNearbyCabinets() // 刷新柜子状态}} catch (error) {uni.showToast({title: '归还失败',icon: 'none'})}},// 检查当前是否有租赁中的订单async checkCurrentRental() {try {const res = await this.$http.get('/api/rental/current')if (res.code === 200 && res.data) {this.startRentalTimer(res.data)}} catch (error) {console.error('检查当前租赁失败', error)}},// 清理计时器clearRentalTimer() {if (this.rentalTimer) {clearInterval(this.rentalTimer)this.rentalTimer = null}},// 关闭所有模态框closeAllModals() {this.$refs.cabinetPopup.close()this.$refs.confirmPopup.close()},// 获取状态样式getStatusClass(status) {const classMap = {1: 'online',2: 'offline',3: 'maintenance'}return classMap[status] || 'offline'},// 获取状态文本getStatusText(status) {const textMap = {1: '正常',2: '离线',3: '维护中'}return textMap[status] || '未知'},// 格式化时长formatDuration(minutes) {const hours = Math.floor(minutes / 60)const mins = minutes % 60return `${hours.toString().padStart(2, '0')}:${mins.toString().padStart(2, '0')}`}}
}
</script><style scoped>
.cue-rental-container {background: #f5f7fa;min-height: 100vh;padding-bottom: 120rpx;
}.header-section {display: flex;align-items: center;padding: 30rpx;background: white;
}.cabinet-card {background: white;margin: 20rpx;border-radius: 20rpx;padding: 30rpx;box-shadow: 0 4rpx 20rpx rgba(0,0,0,0.08);
}.cabinet-header {display: flex;align-items: center;margin-bottom: 20rpx;
}.status-indicator {width: 20rpx;height: 20rpx;border-radius: 50%;margin-right: 10rpx;
}.status-indicator.online {background: #52c41a;
}.status-indicator.offline {background: #ff4d4f;
}.status-indicator.maintenance {background: #faad14;
}.cabinet-actions {display: flex;justify-content: space-between;margin-top: 30rpx;
}.nav-btn, .rent-btn {flex: 1;margin: 0 10rpx;border-radius: 10rpx;padding: 20rpx;
}.rent-btn {background: #1890ff;color: white;
}.renting-panel {position: fixed;bottom: 120rpx;left: 20rpx;right: 20rpx;background: white;border-radius: 20rpx;padding: 30rpx;box-shadow: 0 -4rpx 20rpx rgba(0,0,0,0.1);
}.confirm-rental-modal {background: white;border-radius: 20rpx;padding: 40rpx;margin: 40rpx;
}.agreement-item {display: flex;align-items: center;margin: 20rpx 0;
}.tabbar {position: fixed;bottom: 0;left: 0;right: 0;background: white;display: flex;padding: 20rpx 0;border-top: 1rpx solid #eee;
}.tabbar-item {flex: 1;display: flex;flex-direction: column;align-items: center;
}
</style>
管理后台Vue+ElementUI实现

管理后台采用Vue+ElementUI构建,提供完善的设备管理和运营分析功能:

<template><div class="cue-management-container"><!-- 数据概览 --><el-row :gutter="20" class="stats-row"><el-col :span="6"><el-card class="stats-card"><div class="stats-content"><div class="stats-number">{{ stats.totalCabinets }}</div><div class="stats-label">总柜数</div></div></el-card></el-col><el-col :span="6"><el-card class="stats-card"><div class="stats-content"><div class="stats-number">{{ stats.totalCues }}</div><div class="stats-label">总球杆数</div></div></el-card></el-col><el-col :span="6"><el-card class="stats-card"><div class="stats-content"><div class="stats-number">{{ stats.todayRentals }}</div><div class="stats-label">今日租赁</div></div></el-card></el-col><el-col :span="6"><el-card class="stats-card"><div class="stats-content"><div class="stats-number">¥{{ stats.todayRevenue }}</div><div class="stats-label">今日收入</div></div></el-card></el-col></el-row><!-- 智能柜管理 --><el-card class="table-card"><template #header><div class="card-header"><span>智能柜管理</span><el-button type="primary" @click="addCabinet">新增柜子</el-button></div></template><!-- 筛选条件 --><el-form :model="queryParams" inline><el-form-item label="柜子状态"><el-select v-model="queryParams.cabinetStatus" placeholder="请选择状态"><el-option label="全部" value=""></el-option><el-option label="正常" value="1"></el-option><el-option label="离线" value="2"></el-option><el-option label="维护中" value="3"></el-option></el-select></el-form-item><el-form-item label="网络状态"><el-select v-model="queryParams.networkStatus" placeholder="请选择状态"><el-option label="全部" value=""></el-option><el-option label="在线" value="1"></el-option><el-option label="离线" value="2"></el-option></el-select></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-table :data="cabinetList" v-loading="loading"><el-table-column prop="id" label="ID" width="80" /><el-table-column prop="cabinetName" label="柜子名称" width="150" /><el-table-column prop="cabinetSn" label="序列号" width="180" /><el-table-column prop="location" label="安装位置" width="200" /><el-table-column prop="availableSlots" label="可用槽位" width="120"><template #default="scope">{{ scope.row.availableSlots }}/{{ scope.row.totalSlots }}</template></el-table-column><el-table-column prop="cabinetStatus" label="柜子状态" width="120"><template #default="scope"><el-tag :type="getCabinetStatusTagType(scope.row.cabinetStatus)">{{ getCabinetStatusText(scope.row.cabinetStatus) }}</el-tag></template></el-table-column><el-table-column prop="networkStatus" label="网络状态" width="120"><template #default="scope"><el-tag :type="scope.row.networkStatus === 1 ? 'success' : 'danger'">{{ scope.row.networkStatus === 1 ? '在线' : '离线' }}</el-tag></template></el-table-column><el-table-column prop="lastHeartbeat" label="最后心跳" width="180" /><el-table-column label="操作" width="200" fixed="right"><template #default="scope"><el-button size="small" @click="viewCabinetDetail(scope.row)">详情</el-button><el-button size="small" @click="editCabinet(scope.row)">编辑</el-button><el-button size="small" type="warning" @click="maintainCabinet(scope.row)">维护</el-button></template></el-table-column></el-table><!-- 分页 --><el-pagination@size-change="handleSizeChange"@current-change="handleCurrentChange":current-page="pagination.current":page-sizes="[10, 20, 50]":page-size="pagination.size"layout="total, sizes, prev, pager, next, jumper":total="pagination.total"></el-pagination></el-card><!-- 球杆管理 --><el-card class="cue-management-card"><template #header><span>球杆管理</span></template><el-table :data="cueList" v-loading="cueLoading"><el-table-column prop="cueSn" label="球杆编号" width="150" /><el-table-column prop="cueBrand" label="品牌" width="120" /><el-table-column prop="cueModel" label="型号" width="120" /><el-table-column prop="currentStatus" label="当前状态" width="120"><template #default="scope"><el-tag :type="getCueStatusTagType(scope.row.currentStatus)">{{ getCueStatusText(scope.row.currentStatus) }}</el-tag></template></el-table-column><el-table-column prop="cabinetName" label="所在柜子" width="150" /><el-table-column prop="rentalCount" label="租赁次数" width="100" /><el-table-column prop="lastMaintenanceDate" label="最后维护" width="120" /><el-table-column prop="batteryLevel" label="电量" width="100"><template #default="scope"><el-progress v-if="scope.row.batteryLevel" :percentage="scope.row.batteryLevel" :show-text="false"></el-progress><span v-else>-</span></template></el-table-column><el-table-column label="操作" width="150"><template #default="scope"><el-button size="small" @click="maintainCue(scope.row)">维护</el-button><el-button size="small" type="danger" @click="retireCue(scope.row)">退役</el-button></template></el-table-column></el-table></el-card></div>
</template><script>
import { getCabinets, getDashboardStats, getCues } from '@/api/cueManagement'export default {name: 'CueManagement',data() {return {stats: {totalCabinets: 0,totalCues: 0,todayRentals: 0,todayRevenue: 0},cabinetList: [],cueList: [],loading: false,cueLoading: false,queryParams: {cabinetStatus: '',networkStatus: ''},pagination: {current: 1,size: 10,total: 0}}},mounted() {this.loadDashboardData()this.loadCabinetList()this.loadCueList()},methods: {async loadDashboardData() {try {const res = await getDashboardStats()if (res.code === 200) {this.stats = res.data}} catch (error) {this.$message.error('加载数据失败')}},async loadCabinetList() {this.loading = truetry {const params = {...this.queryParams,page: this.pagination.current,size: this.pagination.size}const res = await getCabinets(params)if (res.code === 200) {this.cabinetList = res.data.recordsthis.pagination.total = res.data.total}} catch (error) {this.$message.error('加载柜子列表失败')} finally {this.loading = false}},async loadCueList() {this.cueLoading = truetry {const res = await getCues()if (res.code === 200) {this.cueList = res.data}} catch (error) {this.$message.error('加载球杆列表失败')} finally {this.cueLoading = false}},getCabinetStatusTagType(status) {const map = {1: 'success',  // 正常2: 'danger',   // 离线3: 'warning'   // 维护中}return map[status] || 'info'},getCabinetStatusText(status) {const map = {1: '正常',2: '离线',3: '维护中'}return map[status] || '未知'},getCueStatusTagType(status) {const map = {0: 'success',  // 空闲1: 'primary',  // 租赁中2: 'warning'   // 维护中}return map[status] || 'info'},getCueStatusText(status) {const map = {0: '空闲',1: '租赁中',2: '维护中'}return map[status] || '未知'},handleSearch() {this.pagination.current = 1this.loadCabinetList()},handleReset() {this.queryParams = {cabinetStatus: '',networkStatus: ''}this.handleSearch()},handleSizeChange(size) {this.pagination.size = sizethis.loadCabinetList()},handleCurrentChange(current) {this.pagination.current = currentthis.loadCabinetList()},addCabinet() {this.$router.push('/cabinet/add')},viewCabinetDetail(cabinet) {this.$router.push(`/cabinet/detail/${cabinet.id}`)},editCabinet(cabinet) {this.$router.push(`/cabinet/edit/${cabinet.id}`)}}
}
</script><style scoped>
.cue-management-container {padding: 20px;background: #f5f7fa;
}.stats-card {text-align: center;
}.stats-number {font-size: 32px;font-weight: bold;color: #1890ff;margin-bottom: 8px;
}.stats-label {color: #666;font-size: 14px;
}.table-card {margin-top: 20px;
}.card-header {display: flex;justify-content: space-between;align-items: center;
}.cue-management-card {margin-top: 20px;
}
</style>
数据库设计核心表结构
-- 智能柜表
CREATE TABLE `smart_cabinet` (`id` bigint(20) NOT NULL AUTO_INCREMENT,`cabinet_sn` varchar(50) NOT NULL COMMENT '柜子序列号',`cabinet_name` varchar(100) NOT NULL COMMENT '柜子名称',`location` varchar(255) NOT NULL COMMENT '安装位置',`geo_location` varchar(100) NOT NULL COMMENT '地理坐标',`total_slots` int(11) NOT NULL COMMENT '总槽位数',`available_slots` int(11) NOT NULL COMMENT '可用槽位',`cabinet_status` tinyint(1) NOT NULL COMMENT '柜子状态',`network_status` tinyint(1) NOT NULL COMMENT '网络状态',`power_status` tinyint(1) NOT NULL COMMENT '电源状态',`last_heartbeat` datetime DEFAULT NULL COMMENT '最后心跳时间',`create_time` datetime DEFAULT CURRENT_TIMESTAMP,`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,PRIMARY KEY (`id`),UNIQUE KEY `uk_cabinet_sn` (`cabinet_sn`),KEY `idx_location` (`geo_location`),KEY `idx_status` (`cabinet_status`),KEY `idx_heartbeat` (`last_heartbeat`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='智能柜表';-- 台球杆表
CREATE TABLE `billiards_cue` (`id` bigint(20) NOT NULL AUTO_INCREMENT,`cue_sn` varchar(50) NOT NULL COMMENT '球杆唯一编号',`cue_brand` varchar(50) NOT NULL COMMENT '球杆品牌',`cue_model` varchar(50) NOT NULL COMMENT '球杆型号',`cue_weight` decimal(5,2) NOT NULL COMMENT '球杆重量',`cue_tip_size` decimal(3,1) NOT NULL COMMENT '杆头尺寸',`current_status` tinyint(1) NOT NULL COMMENT '当前状态',`cabinet_id` bigint(20) DEFAULT NULL COMMENT '所属柜子ID',`slot_position` int(11) DEFAULT NULL COMMENT '槽位位置',`rental_count` int(11) DEFAULT '0' COMMENT '租赁次数',`last_maintenance_date` date DEFAULT NULL COMMENT '最后维护日期',`battery_level` int(11) DEFAULT NULL COMMENT '电量',`create_time` datetime DEFAULT CURRENT_TIMESTAMP,`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,PRIMARY KEY (`id`),UNIQUE KEY `uk_cue_sn` (`cue_sn`),KEY `idx_cabinet` (`cabinet_id`),KEY `idx_status` (`current_status`),KEY `idx_maintenance` (`last_maintenance_date`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='台球杆表';-- 租赁订单表
CREATE TABLE `cue_rental_order` (`id` bigint(20) NOT NULL AUTO_INCREMENT,`order_no` varchar(32) NOT NULL COMMENT '订单编号',`user_id` bigint(20) NOT NULL COMMENT '用户ID',`cue_id` bigint(20) NOT NULL COMMENT '球杆ID',`cabinet_id` bigint(20) NOT NULL COMMENT '柜子ID',`rental_start_time` datetime NOT NULL COMMENT '租赁开始时间',`rental_end_time` datetime DEFAULT NULL COMMENT '租赁结束时间',`actual_return_time` datetime DEFAULT NULL COMMENT '实际归还时间',`rental_duration` int(11) DEFAULT NULL COMMENT '租赁时长',`total_amount` decimal(10,2) DEFAULT NULL COMMENT '订单总额',`payment_status` tinyint(1) NOT NULL COMMENT '支付状态',`order_status` tinyint(1) NOT NULL COMMENT '订单状态',`create_time` datetime DEFAULT CURRENT_TIMESTAMP,`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,PRIMARY KEY (`id`),UNIQUE KEY `uk_order_no` (`order_no`),KEY `idx_user_id` (`user_id`),KEY `idx_cue_id` (`cue_id`),KEY `idx_cabinet_id` (`cabinet_id`),KEY `idx_rental_time` (`rental_start_time`),KEY `idx_order_status` (`order_status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='租赁订单表';-- 设备维护记录表
CREATE TABLE `device_maintenance` (`id` bigint(20) NOT NULL AUTO_INCREMENT,`device_type` tinyint(1) NOT NULL COMMENT '设备类型:1-柜子 2-球杆',`device_id` bigint(20) NOT NULL COMMENT '设备ID',`maintenance_type` tinyint(1) NOT NULL COMMENT '维护类型',`maintenance_desc` text COMMENT '维护描述',`maintenance_date` date NOT NULL COMMENT '维护日期',`maintenance_person` varchar(50) NOT NULL COMMENT '维护人员',`create_time` datetime DEFAULT CURRENT_TIMESTAMP,PRIMARY KEY (`id`),KEY `idx_device` (`device_type`, `device_id`),KEY `idx_maintenance_date` (`maintenance_date`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='设备维护记录表';

这套基于JAVA无人共享台球杆台球柜系统的完整解决方案,通过现代化的物联网技术和智能管理平台,为体育器材共享行业提供了全方位的数字化转型升级路径。系统采用SpringBoot+MyBatisPlus+MySQL构建高可用分布式服务集群,使用Uniapp框架实现微信小程序客户端,配合Vue+ElementUI的管理后台,形成了完整的技术生态闭环。

从技术实现角度看,该系统创新性地引入了智能调度算法、设备状态监控、预防性维护机制等先进技术理念,确保了共享租赁系统在复杂使用场景下的稳定性和用户体验。对于体育器材共享运营商而言,这套系统不仅实现了租赁业务的数字化管理,更重要的是通过数据分析和智能算法为设备优化配置和维护策略提供了科学依据。

随着体育产业数字化进程的加速和共享经济模式的深化,智能体育器材共享正在成为新的市场增长点。这套JAVA无人共享台球杆台球柜系统源码以其先进的技术架构、完善的功能设计和良好的可扩展性,将成为体育器材共享行业标准化发展的重要技术支撑。未来,随着5G、人工智能等新技术的深度融合,该系统还将持续演进,为体育器材共享行业的创新发展提供更强大的技术动力。

http://www.dtcms.com/a/507471.html

相关文章:

  • c 网站开发程序员网站建设 收费明细
  • dockerfile中CMD和ENTRYPOINT指令
  • 用服务器自建一套无界白板 + 文档协作平台 —— Affine
  • docker可视化面板portainer忘记密码的解决方案
  • Spring 4.1新特性:深度优化与生态整合
  • 湛江网站建设外包网站开发公司报价单模板
  • 画册设计公司网站优书网注册
  • 中国风手机网站模板html自动播放视频
  • 汇编与底层编程笔记
  • 酒店网站建设的需求分析报告做外贸网站赚钱吗
  • 物联网异构设备协同运维中的服务依赖动态解析与容错机制
  • 技术人互助:城市级充电系统(Java 微服务)的落地细节,含 demo 和设备适配经验
  • 如何在GitHub上查看自己提过的Issues
  • 【element-ui】面包屑导航
  • 如何设计一个高端网站简洁大方大气erp系统有哪些功能模块
  • BearPi小熊派 鸿蒙开发入门笔记(2)
  • 《信息系统项目管理师》2017 年下案例分析题及解析
  • 一、新建一个SpringBoot3项目
  • 外贸英文网站开发网站要用什么软件做
  • 凡科网站可以做seo优化周口网站设计
  • 笔记本触摸板无法使用怎么办 5种方法快速恢复
  • C#高级:数据库中使用SQL作分组处理5(Rank() 排名函数)
  • 51单片机基础-外部中断INT
  • dz网站恢复数据库阻止网站查到访问者ip
  • 【机器学习05】神经网络、模型表示、前向传播、TensorFlow实现
  • 视频教做家常菜的网站wordpress招商平台
  • 三角洲行动 游戏特色 巨 椰 云手机
  • 正则表达式魔法:用Python Re模块驯服文本数据的艺术
  • 《算法每日一题(1)--- 连续因子》
  • 019数据结构之栈——算法备赛