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

基于uni-app的校园综合服务平台开发实战

闪递校园:基于uni-app的校园综合服务平台开发实战

作为一名全栈开发者,我用6个月时间开发了这款校园综合服务平台——闪递校园。本文将详细分享项目从0到1的开发经验,包括技术选型、核心功能实现、踩坑记录以及性能优化等方面的干货内容。

📝 项目背景

在这里插入图片描述

为什么要做这个项目?

在大学期间,我发现校园里存在诸多痛点:

  • 🏃‍♂️ 跑腿需求旺盛:取快递、买外卖、代购等需求量大,但缺乏规范化平台
  • 💬 社交圈子固化:同学之间交流局限,缺乏破冰工具
  • 🎫 信息不对称:演出票务、二手交易信息分散
  • 💰 支付体验差:校园服务多采用现金支付,体验落后

于是我决定开发一款集跑腿服务、社交娱乐、校园电商于一体的综合性平台。

技术选型思考

技术栈选择理由
前端框架uni-app + Vue 2.x一套代码多端运行,降低开发成本
UI组件库TuniaoUI组件丰富,校园风格契合
状态管理Vuex用户信息、订单状态等需要全局管理
地图服务腾讯地图API国内定位精准,校园场景适配好
支付系统微信支付 + 余额支付覆盖主流支付场景
实时通讯WebSocket订单状态、聊天消息实时性要求高

🏗️ 项目架构设计

整体架构图

核心模块
跑腿服务
社交圈子
支付系统
实时通讯
用户层
UI组件层
业务逻辑层
数据管理层
网络请求层
后端API

目录结构设计

reWxSchool/
├── pages/              # 主包页面
│   ├── index/         # 首页模块
│   ├── hall/          # 接单大厅
│   ├── circle/        # 校园圈子
│   └── user/          # 用户中心
├── pagesA/            # 分包A - 核心业务
│   ├── order/         # 订单管理
│   ├── chat/          # 聊天系统
│   ├── makeFrend/     # 盲盒交友
│   └── withdraw/      # 钱包系统
├── pagesB/            # 分包B - 扩展功能
│   ├── movie/         # 电影票务
│   ├── secondhand/    # 二手市场
│   └── mbti/          # 性格测试
├── components/        # 公共组件
├── store/            # Vuex状态管理
└── util/             # 工具函数

💡 核心功能实现

1. 复杂支付系统的设计与实现

支付系统是整个平台的核心,我设计了一套完整的支付流程:

1.1 支付组件封装
// components/payBox/payBox.vue
export default {data() {return {paymentInProgress: false,      // 支付进行中标志isProcessing: false,           // 处理中标志currentOrderId: null,          // 当前订单IDpaymentSessionId: null,        // 支付会话IDretryCount: 0,                 // 重试次数paymentTimeout: null           // 支付超时定时器}},methods: {// 核心支付流程async processPayment() {if (this.paymentInProgress) {this.showToast('支付进行中,请稍候');return;}try {this.paymentInProgress = true;this.isProcessing = true;this.paymentStartTime = Date.now();this.retryCount++;// 设置支付超时(5分钟)this.setPaymentTimeout();// 保存订单(如果还没有订单ID)let orderResult;if (!this.currentOrderId) {orderResult = await this.saveOrder();this.currentOrderId = orderResult.id || orderResult.payOrderId;} else {// 使用现有订单ID,避免重复创建订单orderResult = { payOrderId: this.currentOrderId, id: this.currentOrderId };}// 发起支付await this.initiatePayment(orderResult.payOrderId, orderResult.id);} catch (error) {this.handlePaymentError(error);} finally {this.clearPaymentTimeout();// 延迟重置状态,防止快速重复点击setTimeout(() => {this.isProcessing = false;this.paymentInProgress = false;}, 1000);}},// 微信支付处理async wechatPayment(payOrderId, orderId) {try {const response = await this.$apiHttp.transactions({ payOrderId });if (response.code !== 0) {throw new Error('交易初始化失败');}await this.requestWechatPayment(response.data, orderId);} catch (error) {console.log('支付失败', orderId);}},// 余额支付处理async balancePayment(payOrderId, orderId) {try {const response = await this.$apiHttp.balancePayment({ payOrderId });if (response.code === 0) {this.handlePaymentSuccess(orderId);} else {this.handlePaymentFailure(response.msg, orderId);}} catch (error) {this.handlePaymentFailure('余额支付失败', orderId);}}}
}
1.2 防重复支付机制

为了防止用户快速点击导致重复扣费,我设计了多重保护机制:

// 多重锁定机制
confirmBalancePayment() {// 防止快速点击确认框if (this.isProcessing || this.paymentInProgress) {return;}uni.showModal({title: '确认支付',content: '是否使用余额免密支付?',success: (res) => {if (res.confirm) {// 再次检查状态,防止确认框期间状态变化if (!this.isProcessing && !this.paymentInProgress) {this.processPayment();}}},});
}

2. 实时通讯系统实现

2.1 WebSocket连接管理
// pagesA/chatUser/chat.vue
export default {data() {return {socket: null,wsUrl: "wss://school.bitsai.top/school-api/websocket",connectionStatus: 'disconnected',token: "",reconnectAttempts: 0,maxReconnectAttempts: 5}},methods: {// WebSocket连接connectWebSocket() {// 检查token是否存在if (!this.token) {console.error('Token不存在,无法建立WebSocket连接');this.showErrorMessage('登录信息无效,请重新登录');return;}this.socket = uni.connectSocket({url: this.wsUrl,header: {'token': this.token},success: () => console.log('WebSocket connection established'),fail: (error) => {console.error('WebSocket connection failed', error);this.showErrorMessage('WebSocket连接失败,请稍后重试');}});// 处理连接打开this.socket.onOpen(() => {console.log('WebSocket connection opened successfully');this.connectionStatus = 'connected';this.reconnectAttempts = 0; // 重置重连次数});// 处理接收消息this.socket.onMessage((message) => {let data = JSON.parse(message.data);if (data.type === 0) {this.handleReceivedMessage(data.msg);}});// 处理连接关闭,自动重连(指数退避)this.socket.onClose((event) => {if (event.code !== 1000) {this.connectionStatus = 'reconnecting';// 确保token存在时才重连if (this.token && this.reconnectAttempts < this.maxReconnectAttempts) {setTimeout(() => {this.reconnectAttempts++;this.connectWebSocket();}, this.getReconnectDelay());} else {this.connectionStatus = 'disconnected';}} else {this.connectionStatus = 'disconnected';}});},// 指数退避重连延迟getReconnectDelay() {return Math.min(1000 * Math.pow(2, this.reconnectAttempts), 30000);},// 消息发送handleSend() {if (this.chatMsg && !/^\s+$/.test(this.chatMsg)) {let messageObj = {botContent: "",userContent: this.chatMsg,isload: true,iserr: false,sendTime: new Date().toISOString()};this.msgList.push(messageObj);this.shouldScrollToBottom = true;if (this.socket && this.socket.readyState === 1) {const message = {type: 0,msg: this.chatMsg,data: "Some data",userId: this.receiverId};this.socket.send({data: JSON.stringify(message),success: () => {console.log('Message sent successfully');this.updateMessageStatus('success');},fail: (error) => {console.error('Failed to send message', error);this.updateMessageStatus('error');}});} else {console.error('WebSocket is not open');this.updateMessageStatus('error');this.showErrorMessage('无法发送消息,请稍后重试');}this.chatMsg = ''; // 清空输入框}}}
}

3. 地理位置服务集成

3.1 腾讯地图API封装
// pagesB/utils/power.js
export default {methods: {// 获取用户定位信息Monitor() {var QQMapWX = require('@/comne/qqmap-wx-jssdk.js');let qqmapsdk = new QQMapWX({key: ''});var _this = this;uni.getLocation({type: 'gcj02',success(res) {var latitude = res.latitude;var longitude = res.longitude;// 小程序环境使用腾讯地图SDK// #ifdef MP-WEIXINqqmapsdk.reverseGeocoder({location: {latitude: latitude,longitude: longitude},success: function(res) {console.log(res.result);uni.setStorageSync('LocalHost', res.result);_this.city = res.result.ad_info.city;}});// #endif// H5环境使用HTTP API// #ifdef H5_this.getH5Loca(latitude, longitude);// #endif},fail(err) {console.log(err);uni.showModal({title: '提示',content: '请先授权位置信息',success: function(res) {if (res.cancel == false && res.confirm) {uni.openSetting({success: function(data) {_this.Monitor();}});} else {_this.Monitor();}}});}});},// H5环境地理编码getH5Loca(latitude, longitude) {let prm = {location: {latitude: latitude,longitude: longitude},key: '',output: 'jsonp',get_poi: 0,coord_type: 5};let url = `https://apis.map.qq.com/ws/geocoder/v1/?location=${latitude},${longitude}`;this.$jsonp(url, prm).then(res => {uni.setStorageSync('LocalHost', res.result);this.city = res.result.ad_info.city;});}}
}

4. AI智能客服集成

// util/request/api.js
coQuanziApi: (data) => {return new Promise((resolve, reject) => {uni.request({url: 'https://api.coze.cn/open_api/v2/chat',method: 'POST',header: {'Authorization': '','Connection': 'keep-alive','Host': 'api.coze.cn','Accept': '*/*','Content-Type': 'application/json',},data: {"conversation_id": data.userId,"bot_id": "","user": data.userId,"stream": false,"query": data.content,},success: (res) => {resolve(res);},fail: (err) => {console.error(err);reject(err);}});});
}

🎯 创新功能设计

1. 盲盒交友系统

这是我最引以为豪的创新功能,通过匿名投递和随机抽取的方式,为校园社交带来新的玩法:

核心逻辑:
  1. 投放纸条:用户可以匿名投放个人信息或心情到盲盒中
  2. 随机抽取:其他用户可以随机抽取纸条,如果感兴趣可以发起聊天
  3. 地理限制:仅限同校学生参与,确保社交的真实性
  4. 隐私保护:初期完全匿名,建立信任后才可以选择公开身份
// 盲盒相关API
// 投放纸条
luckyNoteSave: (data) => {return postRequest('luckyNote/save', data)
},// 抽取纸条
luckydrawNote: (data) => {return postRequest('luckyNote/drawNote', data)
},// 查看历史
noteHistoryPage: (data) => {return getRequest('luckyNote/noteHistoryPage', data)
}

2. 智能跑腿订单系统

订单状态流转:
骑手接单
骑手取货
骑手送达
用户确认
用户取消
待接单
待取货
配送中
待确认
已完成
已取消
核心API设计:
// 订单相关API
orderSave: (data) => postRequest('order/save', data),          // 创建订单
orderTaking: (data) => postRequest('order/taking', data),      // 骑手接单
orderDelivery: (data) => postRequest('order/delivery', data),  // 骑手取货
orderArrive: (data) => postRequest('order/arrive', data),      // 骑手送达
orderConfirmArrive: (data) => postRequest('order/confirmArrive', data), // 用户确认

🚀 性能优化实践

1. 分包加载策略

通过合理的分包设计,将首屏加载时间从3.2s优化到1.8s:

// pages.json
{"pages": [// 主包 - 核心页面{"path": "pages/index/index"},{"path": "pages/hall/index"},{"path": "pages/circle/circle"},{"path": "pages/user/index"}],"subPackages": [{"root": "pagesA",  // 分包A - 核心业务功能"pages": [{"path": "order/index"},{"path": "chat/chat"},{"path": "makeFrend/makeFrend"}]},{"root": "pagesB",  // 分包B - 扩展功能"pages": [{"path": "movie/index"},{"path": "secondhand/index"},{"path": "mbti/mbti"}]}]
}

2. 请求优化

API管理器设计:
// util/request/api.js
const apiManager = {// 用户相关login: (data) => postRequest('api/login', data),getUserData: () => getRequest('user/data'),// 订单相关orderList: (data) => getRequest('order/pageList', data),orderSave: (data) => postRequest('order/save', data),// 支付相关transactions: (data) => getRequest('pay/transactions', data),balancePayment: (data) => getRequest('pay/balancePayment', data),// 聊天相关getChatList: (data) => getRequest('chat/getChatList', data),chatPageList: (data) => getRequest('chat/message/pageList', data)
}

3. 状态管理优化

// store/index.js
const store = new Vuex.Store({state: {// 持久化用户信息vuex_user: lifeData.vuex_user ? lifeData.vuex_user : {name: '图鸟'},// 导航栏配置vuex_custom_nav_bar: true,vuex_status_bar_height: 0,vuex_custom_bar_height: 0,},mutations: {// 通用状态更新$tStore(state, payload) {let nameArr = payload.name.split('.');let saveKey = '';let len = nameArr.length;if (len >= 2) {// 支持多层级状态更新let obj = state[nameArr[0]];for (let i = 1; i < len - 1; i++) {obj = obj[nameArr[i]];}obj[nameArr[len - 1]] = payload.value;saveKey = nameArr[0];} else {state[payload.name] = payload.value;saveKey = payload.name;}// 自动持久化到本地存储saveLifeData(saveKey, state[saveKey]);}}
})

🐛 踩坑记录与解决方案

1. 微信支付回调问题

问题: 微信支付成功后,有时候回调不及时,导致订单状态更新延迟。

解决方案:

// 增加支付状态轮询机制
requestWechatPayment(paymentData, orderId) {return new Promise((resolve, reject) => {uni.requestPayment({...paymentData,success: () => {// 支付成功后启动状态轮询this.startPaymentPolling(orderId);this.handlePaymentSuccess(orderId);},fail: (error) => {this.handlePaymentFailure('微信支付失败', orderId);}});});
},// 支付状态轮询
startPaymentPolling(orderId) {const pollInterval = setInterval(async () => {try {const result = await this.$apiHttp.orderDetail(orderId);if (result.data.payStatus === 'PAID') {clearInterval(pollInterval);this.updateOrderStatus('paid');}} catch (error) {console.error('轮询支付状态失败', error);}}, 2000);// 30秒后停止轮询setTimeout(() => clearInterval(pollInterval), 30000);
}

2. WebSocket断线重连问题

问题: 用户切换应用或网络波动时,WebSocket连接容易断开,重连策略不够完善。

解决方案:

// 改进的重连机制
connectWebSocket() {// ... 连接逻辑this.socket.onClose((event) => {if (event.code !== 1000 && this.reconnectAttempts < this.maxReconnectAttempts) {this.connectionStatus = 'reconnecting';// 指数退避策略const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 30000);setTimeout(() => {this.reconnectAttempts++;console.log(`${this.reconnectAttempts}次重连尝试`);this.connectWebSocket();}, delay);} else {this.connectionStatus = 'disconnected';if (this.reconnectAttempts >= this.maxReconnectAttempts) {this.showErrorMessage('连接失败次数过多,请检查网络后手动重试');}}});
}

3. 地理位置权限问题

问题: 用户首次使用时拒绝地理位置权限,导致功能异常。

解决方案:

Monitor() {uni.getLocation({type: 'gcj02',success(res) {// 定位成功处理},fail(err) {console.log(err);// 优雅的权限引导uni.showModal({title: '定位权限',content: '为了给您提供更好的校园服务,需要获取您的位置信息',confirmText: '去设置',cancelText: '手动选择',success: function(res) {if (res.confirm) {// 引导用户去设置页面uni.openSetting({success: function(data) {if (data.authSetting['scope.userLocation']) {_this.Monitor(); // 重新获取定位}}});} else {// 提供手动选择城市的备选方案uni.navigateTo({url: '/pagesA/school/school'});}}});}});
}

🎉 结语

闪递校园这个项目让我对全栈开发有了更深入的理解,也让我意识到技术服务于业务、业务服务于用户的重要性。虽然项目还有很多可以优化的地方,但它已经成为我技术成长路上的一个重要里程碑。

希望这篇文章能够对正在学习 uni-app 开发或者想要做校园服务类项目的同学有所帮助。如果你有任何问题或建议,欢迎在评论区交流讨论!

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

相关文章:

  • uni-app支持单多选、搜索、查询、限制能否点击组件
  • 掌握CRISPE框架:结构化提示词设计的终极指南
  • 【溜冰场轮滑计时计费扣次软件有哪些?】分享常见的几款软件,佳易王软件系列#软件功能解析操作教程
  • Tiny RDM:一个现代化轻量级的Redis桌面客户端
  • 盟接之桥说制造:浅谈本分和做正确的事情
  • 前端微前端架构深度实践:从单体应用到微前端的完整架构解决方案
  • 携程旅行 web 验证码 分析
  • GET、POST、添加、编辑
  • python爬虫之selenium库进阶(小白五分钟从入门到精通)
  • 日语学习-日语知识点小记-构建基础-JLPT-N3阶段(23):文法+单词第7回5+考え方3
  • 为什么要使用RocketMQ半消息
  • 使用C#语言 基于FTP协议进行文件夹上传下载
  • 【Android】Span富文本简介
  • 苹果 Safari 地址栏可能被超大光标视觉欺骗
  • 阿里云OSS架构示意图与流程
  • AR眼镜在警务安防的应用方案
  • 前沿科技竞速:脑机接口、AI芯片与半导体生态上的新突破
  • 线性回归中梯度下降与正规方程以及拟合问题与正则化
  • 【职业】算法与数据结构专题
  • 【Flink】DataStream API (二)
  • 收藏!VSCode 开发者工具快捷键大全
  • 计算机毕设推荐:基于python的农产品价格数据分析与预测的可视化系统的设计与实现 基于Python农产品管理系统【源码+文档+调试】
  • 基于单片机汽车防盗系统/汽车安全防丢系统
  • 企业级主流日志系统架构对比ELKK Stack -Grafana Stack
  • 解决「图片导出功能需要 Chromium 浏览器支持,但未找到」的完整方案
  • Promise:异步编程的优雅解决方案
  • elemen ui Table表格中添加图片
  • qData 数据中台【开源版】发布 1.0.4 版本,全面升级数据清洗与资产管理能力
  • Spring Security(第六篇):结营篇 —— 完整源码与后续进阶路线 [特殊字符]
  • Day20 API