ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Flask+uni-app实战:从零搭建班级事务管理微信小程序

Flask+uni-app实战:从零搭建班级事务管理微信小程序 最近帮一个朋友做了个班级事务管理系统顺手把这个项目从零到上线的完整思路和踩坑记录整理了出来。项目本身不算复杂但涉及的技术栈比较典型后端用 Python Flask前端用 uni-app 开发微信小程序做一款面向班委、同学和辅导员的班级事务协同工具。如果你正好在规划类似的校园工具类小程序或者想看看 Flask uni-app 的前后端分离项目实际怎么落地这篇文章应该能帮你省不少时间。这个系统解决的痛点很直接班级日常事务太多请假要私聊、通知要在群里翻记录、班费收支靠 Excel、活动报名靠接龙信息散得乱七八糟。真正动手做的时候发现这些事情完全可以用一个小程序集中管理让每个角色都有自己的操作入口数据一旦沉淀下来后面统计和分析也方便。1. 系统整体设计与技术选型思路1.1 需求拆解班级事务系统到底要管什么开发之前先把需求理清楚。班级事务听起来笼统拆开来看其实就几类通知公告、请假报备、活动报名、班费收支、任务分工。通知公告最简单就是班委发、同学看难点在于已读反馈怎么统计。请假报备涉及审批流程同学提交、班长或辅导员审批状态要可跟踪。活动报名需要统计人数有时还要限制名额、记录报名时间。班费收支要有明细、有分类期末能出个结算表。任务分工主要是值日表、活动分工这类场景。角色上分三类普通同学、班委含班长、团支书、学委等、辅导员或管理员。不同角色的权限差异很大同学只能发起申请和查看自己的事务班委可以发布通知、审批申请、管理班费辅导员则要能看到班里所有事务数据。这套权限模型想清楚之后后面的数据库设计和接口设计就顺了。1.2 为什么选 Flask uni-app 这套组合技术选型纠结过一段时间。当时的候选方案有 Django 原生小程序、Spring Boot 原生小程序、Flask uni-app。最终定了 Flask uni-app理由很实在。Flask 对中小型工具类系统非常友好本身轻量扩展机制灵活加上 SQLAlchemy 做 ORM开发效率很高。这个系统的业务逻辑不算深Flask 完全能抗住没必要上 Django 那样的大而全框架。Spring Boot 和 Java 更是杀鸡用牛刀而且学习成本和部署成本对个人项目来说都偏高。前端选 uni-app 的核心原因是代码复用价值。uni-app 基于 Vue 语法一套代码可以同时编译到微信小程序、H5、App 等多个平台。也就是说如果我后续想出一个网页版给辅导员在电脑上用或者打包一个安卓端 APP这套前端代码基本不用重写只要在提示适配上下点功夫。这对于一个小团队甚至个人开发者来说效率优势太明显了。微信小程序本身有天然的传播优势学生不用单独安装 APP微信里一搜就能用完全符合校园场景的使用习惯。小程序的双端逻辑配合 Flask 的轻量后端整套方案在开发和维护成本之间取了比较平衡的点。1.3 技术栈全景与项目结构规划后端方面核心组合是 Flask 2.x Flask-SQLAlchemy PyMySQL Flask-CORS。数据库选 MySQL 8.0因为学院服务器上已有这套环境用现成的比较稳妥。鉴权这块没有引入复杂的 OAuth 框架用的是微信小程序登录返回的 code 换取 openid再配合自签 token 做接口鉴权后面会详细展开。前端方面uni-app 使用 Vue3 语法版本编译目标设为微信小程序。UI 库选了 uView Plus在小程序环境里表现稳定表单组件、弹窗、消息提示这些常用组件都很齐全省去了自己写一堆基础组件的功夫。部署架构是一台轻量云服务器Ubuntu 22.04上面跑 Nginx 做反向代理后端进程用 Gunicorn 启动数据库用 MySQL 8.0。小程序端要求 HTTPS 接口所以服务器上还配了 SSL 证书。整体目录结构按前后端分开放class-affair-system/ ├── backend/ │ ├── app.py │ ├── config.py │ ├── models.py │ ├── api/ │ │ ├── auth.py │ │ ├── affair.py │ │ ├── notice.py │ │ ├── approve.py │ │ └── user.py │ ├── utils/ │ │ ├── token.py │ │ └── response.py │ └── requirements.txt ├── frontend/ │ ├── pages/ │ │ ├── index/ │ │ ├── publish/ │ │ ├── detail/ │ │ ├── audit/ │ │ └── mine/ │ ├── utils/ │ │ └── request.js │ └── manifest.json └── deploy/ ├── class_system.service ├── nginx.conf └── gunicorn_config.py后端目录按业务模块拆分成 auth、affair、notice、approve、user 几个蓝图前端按页面拆目录整体结构清晰维护起来不费劲。2. Flask 后端核心设计与 API 实现2.1 数据库设计用几张表撑起一套事务流数据库是整个系统的地基这一层设计得不好后面写接口时处处受限。我用了四张核心表外加一张用户表。用户表 user 的字段包含 id、openid、nickname、avatar、real_name、role、class_id、created_at。openid 是微信用户的唯一标识role 字段存角色标识1为普通同学、2为班委、3为辅导员class_id 关联班级表。班级表 class 很简单就是 id、class_name、grade、college。事务表 affair 是系统的核心表字段包括 id、user_id发起人、affair_type事务类型请假、活动报名、班费申请、任务分工等、title、content、attachment_url、class_id、status、created_at、updated_at。status 字段很关键我用 0 表示草稿、1 表示待审批、2 表示已通过、3 表示已驳回。这个状态字段直接决定整个事务流的分支走向。审批表 approval 记录每一次审批操作字段有 id、affair_id、approver_id、action通过或驳回、comment、created_at。之所以单独建表而不是在 affair 表里直接存审批结果是为了保留审批历史。比如一个请假申请被班长驳回后又修改重新提交审批记录就能完整呈现整个流转过程方便后续追溯。通知表 notice 结构也不复杂id、title、content、publisher_id、class_id、publish_time、read_count。已读明细单独用 notice_read 表记录每条通知对应多个已读记录避免重复统计。建表时的几个实际考量点事务表和通知表都冗余存了 class_id而不是通过 user 表间接关联。理由是查询列表时可以直接按班级过滤不用多做一次 join查询性能在小数据量下差异不明显但 SQL 写起来简洁很多。附件字段存的是系统内相对路径而不是完整 URL。这样迁移服务器时不用改数据库配合 Nginx 静态映射就能直接访问。2.2 用户登录与鉴权实现微信小程序的后端登录流程是固定的套路前端调用 wx.login 获取临时 code把 code 传给后端后端拿 code 去微信接口换取 openid 和 session_key然后以后端自己的逻辑创建用户会话。核心代码大致长这样app.route(/api/auth/login, methods[POST]) def login(): data request.get_json() code data.get(code) appid current_app.config[WX_APPID] secret current_app.config[WX_SECRET] resp requests.get( https://api.weixin.qq.com/sns/jscode2session, params{ appid: appid, secret: secret, js_code: code, grant_type: authorization_code } ).json() openid resp.get(openid) if not openid: return jsonify({code: 1, msg: 登录失败无法获取openid}), 500 user User.query.filter_by(openidopenid).first() if not user: default_class Class.query.first() user User( openidopenid, nickname微信用户, role1, class_iddefault_class.id if default_class else None ) db.session.add(user) db.session.commit() token generate_token(user.id, user.role) return jsonify({ code: 0, data: { token: token, user_info: { id: user.id, nickname: user.nickname, avatar: user.avatar, role: user.role, class_id: user.class_id } } })这里有几个细节容易踩坑。微信端换 openid 的接口是https://api.weixin.qq.com/sns/jscode2session用 requests 直接请求时要注意网络超时设置毕竟这是后端依赖第三方服务的关键路径接口超时会导致整个登录流程失败。我在代码里加了超时参数timeout5并且在请求失败时返回友好提示。token 生成用的是 itsdangerous 里的 TimedJSONWebSignatureSerializer设置 7 天有效期。每次请求都从 Header 里取 Authorization 字段解析出 user_id 和 role再用这个用户身份处理请求。这样设计的好处是后端无状态多个进程共享同一套 token 校验逻辑部署多个 Gunicorn worker 时不会有会话不一致的问题。首次登录自动注册是默认行为。用户在小程序里第一次打开就自动创建账号体验上最顺畅不需要先填一堆注册表单。缺点是有大量垃圾账号风险所以我在后续版本里加了完善资料的引导让用户主动补全真实姓名和头像。2.3 核心 API 实现事务发布与审批流转事务发布是班委和同学都用得最多的接口。以请假申请为例前端提交事务类型、标题、内容、附件路径后端做基础校验后插入 affair 表状态默认为待审批。app.route(/api/affair/create, methods[POST]) login_required def create_affair(): user_id g.user_id data request.get_json() affair_type data.get(affair_type) title data.get(title).strip() content data.get(content).strip() attachment_url data.get(attachment_url, ) if not title or not content: return jsonify({code: 1, msg: 标题和内容不能为空}), 400 affair Affair( user_iduser_id, affair_typeaffair_type, titletitle, contentcontent, attachment_urlattachment_url, class_idg.user.class_id, status1 ) db.session.add(affair) db.session.commit() if affair_type in (leave, expense, activity): approval Approval( affair_idaffair.id, approver_idget_approver_for_class(g.user.class_id), statuspending ) db.session.add(approval) db.session.commit() return jsonify({code: 0, data: {id: affair.id}})审批接口的逻辑核心是更新 affair 表状态同时在 approval 表写一条审批记录。为了保证这两步的原子性必须用事务包起来app.route(/api/affair/approve, methods[POST]) login_required app.route(/api/affair/approve, methods[POST]) def approve_affair(): data request.get_json() affair_id data.get(affair_id) action data.get(action) # approve 或 reject comment data.get(comment, ) affair Affair.query.get(affair_id) if not affair: return jsonify({code: 1, msg: 事务不存在}), 404 # 权限校验只有班委和辅导员可以审批 if g.user.role not in (2, 3): return jsonify({code: 1, msg: 无审批权限}), 403 try: affair.status 2 if action approve else 3 approval Approval( affair_idaffair.id, approver_idg.user.id, actionaction, commentcomment ) db.session.add(approval) db.session.commit() except Exception as e: db.session.rollback() return jsonify({code: 1, msg: f审批失败{str(e)}}), 500 return jsonify({code: 0, msg: 操作成功})审批逻辑里最需要注意的点是权限控制。我在装饰器login_required基础上接口内部再做一次角色校验。这样即使未来前端页面入口放出来了后端也能拦住越权请求。权限这块我吃过亏早期版本只在前端隐藏审批按钮结果接口被同学直接调出来发审批请求好在当时数据量小没出乱子后来补上了后端校验才放心。事务列表接口要有分页和状态筛选。我用了一个简单的page、page_size参数配合status参数实现筛选。这里有个设计细节需要注意普通同学只能看到自己发起的申请班委和辅导员可以看到整个班级的申请。这个逻辑在查询时直接用 where 条件区分避免把班级数据透传给普通同学。2.4 文件上传与静态资源处理班级事务里经常要传附件比如请假条的截图、活动海报、票据照片等。文件上传在 Flask 里用 request.files 接收然后保存到服务器指定目录。app.route(/api/upload, methods[POST]) login_required def upload_file(): file request.files.get(file) if not file: return jsonify({code: 1, msg: 未接收到文件}), 400 # 校验文件类型和大小 allowed_ext {png, jpg, jpeg, gif, pdf, doc, docx} ext file.filename.rsplit(., 1)[1].lower() if . in file.filename else if ext not in allowed_ext: return jsonify({code: 1, msg: 不支持的文件类型}), 400 if file.content_length and file.content_length 10 * 1024 * 1024: return jsonify({code: 1, msg: 文件大小不能超过10MB}), 400 filename f{uuid.uuid4().hex}.{ext} upload_dir current_app.config[UPLOAD_DIR] file.save(os.path.join(upload_dir, filename)) return jsonify({ code: 0, data: { url: f/uploads/{filename} } })文件名用 uuid 重新生成而不是直接用用户传递的文件名。这样做的好处是避免文件名冲突也规避了路径穿越攻击的风险。攻击者如果直接把../../etc/passwd作为文件名传过来旧版代码可能会把这个路径拼进保存路径导致安全问题。用 uuid 之后这个风险直接被抹掉了。静态资源的访问开发环境直接用 Flask 的 static 路由映射生产环境则交给 Nginx 处理后端只负责把文件写到磁盘。2.5 CORS 与跨域配置前后端联调头一关前端跑在微信开发者工具里后端跑在本机 5000 端口小程序请求本地接口时会遇到跨域问题。虽然微信小程序不像浏览器那样受同源策略严格限制但在开发者工具里调试时还是会遇到。后端加一层 Flask-CORS 最省心from flask_cors import CORS app Flask(__name__) CORS(app, resources{r/api/*: {origins: *}})生产环境上小程序请求的是 HTTPS 域名由 Nginx 统一反向代理到后端同时 Nginx 处理好 CORS 头后端代码里就可以去掉 CORS 配置减少不必要的暴露面。开发和生产分开处理跨域是前后端分离项目里的常见做法。3. uni-app 小程序前端开发细节3.1 开发工具初始化与 manifest 配置uni-app 项目我用 HBuilderX 创建模板选择默认的 uni-ui 模板Vue3 版本。创建完成后第一件事是配置 manifest.json。微信小程序相关的配置集中在 manifest.json 的mp-weixin节点里。appid 填自己申请的微信小程序 appid没有的话先去微信公众平台注册。{ mp-weixin: { appid: 你的小程序appid, setting: { urlCheck: false, es6: true, minified: true }, usingComponents: true, permission: { scope.userLocation: { desc: 你的位置信息将用于班级活动定位 } } } }开发阶段有个小技巧urlCheck设为 false 可以让开发者工具不校验 request 合法域名方便直接请求本地 IP 或未备案的测试域名。但注意这只是开发阶段的便利设置真机预览和发布时必须改回 true否则会请求失败。如果需要扫码功能在 manifest.json 里加上扫码的权限声明小程序端用uni.scanCode接口唤起扫码。班级场景里扫码常用于活动签到这个功能上线后效果不错。首次在微信开发者工具里打开项目时如果遇到空白页或语法报错多半是编译缓存问题。工具栏里点清除缓存并重新编译基本能解决。3.2 请求封装与全局状态管理小程序端的请求层是开发体验的关键。我封装了一个request.js工具模块统一处理 baseURL、token 注入、响应拦截和错误提示。// utils/request.js const BASE_URL https://api.你的域名.com export function request(options) { return new Promise((resolve, reject) { uni.request({ url: BASE_URL options.url, method: options.method || GET, data: options.data || {}, header: { Content-Type: application/json, Authorization: uni.getStorageSync(token) }, success: (res) { if (res.statusCode 200 res.data.code 0) { resolve(res.data.data) } else if (res.statusCode 401) { // token 过期跳转登录 uni.removeStorageSync(token) uni.navigateTo({ url: /pages/login/login }) reject(new Error(登录已过期)) } else { uni.showToast({ title: res.data.msg || 请求失败, icon: none }) reject(res.data) } }, fail: (err) { uni.showToast({ title: 网络连接异常, icon: none }) reject(err) } }) }) }这个封装里最关键的是 token 统一注入。每个请求都从本地存储里取 token挂到 Authorization 头里后端接口通过装饰器统一校验。这样前端每个接口不用单独写 token 逻辑省了很多重复代码。用户登录态的管理我用了 Vuexuni-app 内置支持。登录成功后把用户信息和 token 同时写入 storage 和 Vuex store页面里直接访问 store 中的用户信息避免每个页面都从 storage 读。页面间传参方面小程序的页面栈机制决定了页面跳转用uni.navigateTo并带 id 参数目标页面的onLoad(options)里拿参数后再调用接口加载详情。这个流程是固定的没什么坑但要注意参数长度限制复杂对象最好序列化后存到全局变量或缓存里。3.3 页面设计与交互实现首页是整个小程序的门面也是用户进来第一个看到的东西我设计成事务列表 分类 tab的布局。顶部是四个 tab全部、请假、活动、班费下面跟着时间倒序的事务列表。列表用的组件是 uni-app 内置的 scroll-view配合 onReachBottom 做触底加载更多。分页参数维护在页面的 data 里每次加载成功页码加一。这个分页交互实现起来不复杂但要注意防重复加载在请求中状态加一个锁避免触底事件连续触发导致重复请求。发布页是操作最频繁的页面。表单包含事务类型选择、标题输入、内容文本域、附件上传四个部分。表单校验用 uni-app 内置的 uni-forms 组件配置 rules 规则后前端就能做基础校验减少无效请求。附件上传部分用 uni.chooseImage 选择图片然后调用 uni.uploadFile 上传到后端uni.chooseImage({ count: 1, sizeType: [compressed], sourceType: [album, camera], success: (res) { const tempFilePath res.tempFilePaths[0] uni.uploadFile({ url: BASE_URL /api/upload, filePath: tempFilePath, name: file, header: { Authorization: uni.getStorageSync(token) }, success: (uploadRes) { const data JSON.parse(uploadRes.data) if (data.code 0) { // 保存返回的路径到表单数据 } } }) } })这里容易踩的坑是 uni.uploadFile 返回的 data 是字符串不是对象必须先 JSON.parse 才能取到 url。另外小程序真机上sourceType里的 camera 才可以调起摄像头这是 H5 和小程序的差异点。审批页是班委使用频率最高的页面。事务详情展示申请人的基本信息、事务内容、附件预览和审批历史。底部是审批操作区通过和驳回两个按钮驳回时可以填写审批意见。整个页面核心就是一个事务详情接口 一个审批接口交互上注意加个 loading 状态防止用户重复点击提交。顶部导航栏在小程序里有个细节就是不同机型状态栏高度不一样。为了避免自定导航时内容顶到状态栏我用了一个公共方法来获取状态栏高度export function getStatusBarHeight() { return new Promise((resolve) { uni.getSystemInfo({ success: (res) { resolve(res.statusBarHeight || 44) } }) }) }然后在页面 mounted 时设置占位 view 的高度这样自定义导航就能适配所有机型不再出现 iPhone 上顶着刘海的问题。3.4 微信小程序端的差异化处理uni-app 说是多端复用但实际开发中还是有不少小程序特有的坑。域名校验是第一个坎。微信小程序要求所有 request 请求的 URL 都必须在小程序后台配置为合法域名否则真机上报错。开发阶段可以关掉 urlCheck但上线前必须把线上域名配好。图片资源也有域名校验。image组件的 src 如果指向未配置的 downloadFile 合法域名图片加载不出来。所以我在 upload 接口返回的路径基础上请求时拼上完整的域名同时在小程序后台把downloadFile 合法域名配置好。分享功能在小程序里比较特殊。微信小程序默认没有分享按钮需要显式调用uni.showShareMenu或配置页面onShareAppMessage生命周期。我做了个班级活动分享功能同学可以分享活动详情页到班级群通过这个实现了一波简单的裂变传播。小程序的生命周期和 H5 有差异onShow在每次从后台切换到前台时都会触发这个时间点适合做数据刷新。我在详情页的 onShow 里重新拉取列表数据保证从详情页返回列表时数据是最新的体验上比用户手动下拉刷新顺滑。4. 部署上线与小程序发布流程4.1 Flask 后端部署到云服务器后端部署看着简单但真到了服务器上环境配置能卡住不少人。我这里用的是 Ubuntu 22.04 云服务器按步骤走完整套流程。先装基础环境sudo apt update sudo apt install -y python3-pip python3-venv nginx mysql-server项目代码拉取到服务器在项目目录下创建虚拟环境并安装依赖cd /var/www/class_system python3 -m venv venv source venv/bin/activate pip install -r requirements.txtrequirements.txt 里最关键的是这几个包Flask2.3.3 Flask-SQLAlchemy3.1.1 PyMySQL1.1.0 Flask-Cors4.0.1 gunicorn21.2.0 itsdangerous2.1.2 requests2.31.0这里有个坑PyMySQL 需要配套安装 cryptography不然连 MySQL 时会报认证错误。用 pip 安装 PyMySQL 之前先确认 cryptography 有没有装不行就一起装pip install PyMySQL cryptography数据库方面先创建 MySQL 库和账号CREATE DATABASE class_system DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE USER class_userlocalhost IDENTIFIED BY 你的密码; GRANT ALL PRIVILEGES ON class_system.* TO class_userlocalhost; FLUSH PRIVILEGES;然后让 Flask 连上数据库。config.py 里配置 SQLALCHEMY_DATABASE_URI注意 MySQL 8.0 默认认证插件是 caching_sha2_password老版本 PyMySQL 连不上要么升级 PyMySQL 到 1.x要么在 MySQL 里把对应用户的认证方式改成 mysql_native_password。4.2 Gunicorn Nginx 配置Flask 自带的开发服务器不适合生产环境直接用 Gunicorn 启动。创建一个 gunicorn_config.pybind 127.0.0.1:5000 workers 3 timeout 60 accesslog /var/log/class_system/access.log errorlog /var/log/class_system/error.log这里 workers 设置为 3是考虑到服务器配置只有 2C4G开太多 worker 反而会抢占资源。如果服务器性能更好可以按 CPU 核数加。启动命令gunicorn -c gunicorn_config.py app:app为了让它常驻后台且开机自启我配了一个 systemd 服务。在/etc/systemd/system/class_system.service里写[Unit] DescriptionClass System Flask App Afternetwork.target [Service] Userwww-data Groupwww-data WorkingDirectory/var/www/class_system ExecStart/var/www/class_system/venv/bin/gunicorn -c /var/www/class_system/gunicorn_config.py app:app Restartalways [Install] WantedBymulti-user.target然后执行sudo systemctl daemon-reload sudo systemctl enable class_system sudo systemctl start class_systemNginx 配置反向代理。因为是给小程序用的接口需要配 HTTPS。这里以域名api.你的域名.com为例server { listen 443 ssl; server_name api.你的域名.com; ssl_certificate /etc/nginx/ssl/你的证书.crt; ssl_certificate_key /etc/nginx/ssl/你的证书.key; ssl_protocols TLSv1.2 TLSv1.3; location / { proxy_pass http://127.0.0.1:5000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } # 静态上传文件直接由 Nginx 服务 location /uploads/ { alias /var/www/class_system/uploads/; } }注意这个location /uploads/配置非常关键。如果不做这个映射上传的附件会走 Flask 接口返回既慢又占后端资源。微信小程序强制要求接口必须是 HTTPS证书我用的免费版申请和部署整个流程大概花了一个小时。证书有效期为三个月记得在到期前自动续期不然小程序突然调不了接口排查半天才发现是证书过期了。4.3 微信小程序注册、提审与发布小程序前端开发完成后HBuilderX 里点发行 - 小程序-微信就会生成编译后的微信小程序代码目录在unpackage/dist/dev/mp-weixin。然后用微信开发者工具打开这个目录上传版本。提审之前有几件事必须核对清楚。第一manifest.json 里的 appid 要和微信公众平台的 appid 保持一致。第二服务器域名在小程序后台配置好request 合法域名和 downloadFile 合法域名都要加。第三小程序分类要选对涉及校园教育类的要选教育类目如果涉及请假等个人事务数据还需要配置隐私保护指引。在微信公众平台里开发管理 - 开发设置 - 服务器域名把线上 API 域名添加进去。这里有个细节小程序域名要求备案且是 HTTPS域名还不能带端口号。隐私协议这块容易被忽略。小程序后台要求填写用户隐私保护指引包括收集哪些用户信息、用途说明等。我们收集了微信昵称和头像作为用户资料还可能需要位置信息活动签到场景这些都要在隐私指引里写明。如果不填小程序审核时会直接被拒。提交审核前最好先在体验版上完整测一遍核心流程登录、发布事务、审批、上传附件、查看列表。体验版和正式版逻辑完全一样只是访问入口不同。我习惯在体验版阶段拉上几个班委同学一起测试他们能发现很多我开发者视角下看不到的问题。审核通过后点击发布小程序就正式上线了。微信审核时间一般在 1-7 天教育类目审核还算顺利第一次提审两天不到就过了。5. 常见问题与排查技巧实录5.1 开发阶段高频问题速查表问题可能原因解决方案Flask 接口返回中文乱码未设置 JSON 编码Flask 返回 JSON 时设置app.config[JSON_AS_ASCII] False小程序请求提示 URL 不在合法域名列表后台未配置 request 合法域名微信公众平台配置服务器域名开发工具临时关掉 urlCheckuni-app 真机预览连不上本地接口手机和电脑不在同一局域网后端启动时绑定0.0.0.0手机和电脑连同一个 Wi-Fi上传附件后前端拿不到图片路径没拼完整域名后端返回相对路径前端拼接 BASE_URL 后再渲染清理了微信开发者工具缓存还是空白编译目标配置问题在 manifest.json 中检查 vue 版本和编译配置跨域问题在开发时也很常见。我早期在本地调试时小程序开发者工具请求http://localhost:5000/api/affair/list一直被 CORS 拦截后来确认是 Flask 端没加 CORS 头。加了 Flask-CORS 扩展后问题直接就解决了。小程序图片上传还有一个常见坑上传到服务器后图片不能立即显示尤其在 H5 端。原因是后端返回的 URL 缺少域名前缀小程序端 image 组件没法解析这个相对路径。解决方式是在响应层统一处理把相对路径转成完整 URL。5.2 生产环境真实踩坑记录第一个印象深刻的坑是附件路径错误。部署到服务器后发现上传的附件总是 404。排查了半天最后发现是 Flask 的UPLOAD_DIR配置的是相对于项目目录的路径但 systemd 启动的 WorkingDirectory 指向的目录不同导致上传文件写到了其他位置而 Nginx 的 alias 映射目录又不对。解决方案是把上传路径和 Nginx 映射路径都改成绝对路径配置文件里写死避免任何相对路径解析带来的不确定性。顺手在代码里加了启动时自动创建 uploads 目录的逻辑防止目录不存在时报错。第二个坑是数据库连接池断连。系统跑了一段时间后凌晨就会出现接口 500 错误报错是Lost connection to MySQL server during query。原因是 MySQL 的 wait_timeout 默认是 8 小时长时间没请求后连接被断开SQLAlchemy 的连接池还持有旧连接。解决方式很简单SQLAlchemy 连接池加两个参数pool_pre_pingTrue和pool_recycle3600。pool_pre_ping每次取连接时先 ping 一下连接断了就重新建立pool_recycle强制一小时回收一次连接防止 MySQL 主动断开。第三个容易忽略的是 token 过期后的无感重登设计。学生用户可能隔了一周再打开小程序token 已过期直接跳登录页重登体验非常差。我后面改成了在请求层拦截 401先静默调一次uni.login获取新 code再走一遍 login 接口自动刷新 token刷新失败才跳转登录页。5.3 排查工具与方法排查问题要有章法盲猜只会浪费时间。我的调试武器库主要这几样。Flask 端先看日志。systemd 服务日志用journalctl -u class_system -f实时查看能看到 Python 完整报错堆栈。开发环境把app.debug True打开出错时浏览器会直接显示详细堆栈信息。SQLAlchemy 还能配置输出 SQL 日志排查数据库层问题时很管用。小程序端调试利器是微信开发者工具的 vConsole可以看到小程序端所有网络请求、控制台日志和本地缓存内容。真机预览时开启调试模式也会出现 vConsole同样可以看完整日志。页面逻辑问题通过打断点观察 data 里的数据变化最直观。接口排查用 curl 最快。部署后可以用类似命令测试线上接口curl -X POST https://api.你的域名.com/api/affair/list \ -H Content-Type: application/json \ -H Authorization: 你的token \ -d {page: 1, page_size: 10, status: 1}这样能区分问题是出在前端还是后端。前端报错但 curl 请求返回正常那就是前端代码逻辑问题curl 也返回 500那就专心排查后端。优化建议里有一个简单有效的数据库加索引。affair 表的 status 和 class_id 字段经常作为查询条件privacy 和权限维度过滤很多加索引后接口响应速度明显提升。还有 user 表的 openid 字段也应该有唯一索引保证一个 openid 对应一个用户。6. 一些个人体会和小建议做完整个项目回头复盘最想给大家提的几条经验是心态和方法上的。第一第一版不要贪大求全。一开始我列了一堆功能班级圈、投票、问卷、签到地图、学期报告。如果真按这个范围做项目周期至少翻两倍。后来砍到只剩通知、事务、审批、个人中心四个主模块整个系统从开发到上线大概用了三周时间核心流程跑通之后用户反馈和真实需求才逐渐浮现出来后续再基于反馈加功能方向比闭门造车准确得多。第二先跑通主流程再回头补细节。我开发时的顺序是登录 - 发布事务 - 审批流转 - 列表展示这条链路通了以后通知模块和班费模块就是加几张表、几个接口的事成本很低。如果一上来就纠结某个页面样式调得美不美主流程迟迟没跑通项目容易陷入无底洞。第三和真实用户一起测。班级事务管理系统这种校园工具类项目最真实的反馈来自同学。体验版阶段我拉了班里几个同学直接用他们在使用过程中提出了很多我完全没想到的需求比如希望申请被驳回时能收到通知、希望班费账单能导出 Excel、希望活动报名后能在详情页看到自己是否已报名。这些建议让系统迭代方向更贴近实际使用场景。后续如果继续迭代这个项目我会考虑加两个方向。一个是数据可视化把班级事务按类型、时间维度做成统计分析页面让辅导员在期末时能直观看到学期情况另一个是消息推送利用小程序的订阅消息功能实现审批状态变更、活动提醒的主动触达把班委从每天追着问进度的状态里解放出来。最后说一个很重要的点技术选型和架构设计只是为了解决实际问题服务不是越复杂越好。这个系统用 Flask uni-app 的组合是因为它匹配当前场景的复杂度学习成本低、开发效率高、部署方便。如果你的项目比这个大得多团队协作复杂那可能需要微服务或者更重量级的框架但这套轻量方案在校园工具类场景里已经足够合格。希望这篇从需求梳理到上线排坑的完整记录能帮你少走一些弯路。
返回列表