校园兼职系统在每年的毕业设计和课程设计里都是常客,我前后帮人参考过好几个类似的题目,自己也动手用 Flask + Vue 完整实现了一套,期间在 PyCharm 里一路从数据库设计写到前端页面联调,还把 Django 拉出来对比了一轮理清技术选型思路。这套系统既要管学生端找兼职、投递简历,也要管企业端发岗位、筛人选,后台还要有个运营窗口处理纠纷和审核信息,写下来之后发现里面值得拆解的细节不少。
这篇博文我就围绕这个“基于 Flask 的大学生校园兼职系统”展开,把从需求拆解、技术选型、架构设计、核心模块实现,到前后端联调和写代码时反复踩坑的整个过程,尽量完整记录下来。目标读者是正在做类似项目的人,无论你是刚起步的初学者还是已经写过几个图省事的课程设计、想进一步提升工程质量的开发者,里面提到的方案取舍、代码片段和排查思路,都应该可以直接拿来用。
1. 项目整体设计与需求拆解
1.1 这套系统的核心痛点是什么
校园兼职场景看着简单,真实跑起来的需求比想象中要碎得多。很多学校里的兼职信息还停留在微信群、QQ 群转发阶段,一个兼职信息发出来,群里几十个人同时回复报名,管理员根本没法判断谁靠谱谁不靠谱;发布信息的商家也说不清楚自己到底收到了多少份报名,后续要不要人、人满了没有都没法同步。这种信息不对称带来的结果是:学生找兼职靠运气,商家招人靠吼,学校就业指导部门想插手管理却没有数据支撑。
我设计这套系统时把核心痛点归纳为三个:第一是信息结构化,兼职岗位必须按照类别、薪资、时间、地点、结算方式这些字段规范化发布,不能是模糊的一段话;第二是流程闭环,从岗位发布、学生报名、商家确认、兼职完成到互评结算,每个环节都要有状态流转记录;第三是权限隔离,学生、企业管理员、平台运营三种角色,看到的内容和能执行的操作必须是完全不同的。
这三个痛点直接决定了系统不可能只做一个简单的“信息展示平台”,必须有用户体系、审核机制和状态管理。这也是为什么后端不能只用几个页面糊弄过去——它需要的是完整的业务逻辑支撑。
1.2 角色权限与业务闭环设计
系统最终划分了三种角色:
- 学生端(普通用户)
- 企业端(兼职发布方,可以是商家店员账号也可以是公司HR账号)
- 平台运营端(学校兼职服务中心或第三方运营人员)
学生端的核心操作是浏览岗位、收藏岗位、投递报名、查看录用结果、确认完工、评价企业;企业端的核心操作是发布兼职、审核报名学生、标记到岗和完工、结算记录、回复学生评价;运营端则是审核兼职信息是否合规、处理学生和企业之间的纠纷申诉、统计平台数据。
一张典型的岗位生命周期图可以这样理解:企业发布岗位 → 运营审核通过 → 岗位上线展示 → 学生投递简历 → 企业筛选并发送录取通知 → 学生确认接受 → 按要求到岗 → 完工后企业结算 → 双方互评。这里的每一个箭头,在数据库里都对应一条状态记录的变更,在代码里都对应一个后端接口的调用。
我建议在动手写代码之前,先把这条闭环画在纸上。我见过太多人一上来就建表,结果写着写着发现“岗位被下架了,已经投递的学生怎么办”这种边界问题完全没想清楚,回头改表结构才是最痛苦的事。
1.3 为什么这个项目适合作为完整演示项目
从学习和演示的角度看,这个题目覆盖的技术面非常全面:涉及用户认证、文件上传、富文本内容(岗位描述)、状态机流转、列表分页搜索、前后端跨域交互、多角色权限控制。任何一个模块拿出来都足够单独写一篇技术笔记。而它们组合在一起又不会复杂到一个人做不完,是个“跳一跳够得着”的工程。
我当时做的时候,给它的定位就是“一个可以写进简历、能现场演示完整业务流程的系统”,所以特意在模块划分上尽量逼近真实产品的结构,而不是那种做了一个登录框加点增删改查就交付的作业题。
2. 技术选型分析:Flask 与 Django 的取舍
2.1 标题里同时出现 Flask 和 Django,到底怎么选
很多人在做这类系统时都会陷入 Flask 和 Django 的纠结中。这两种框架我都写过实际项目,个人感受是:Django 像一个“全家桶”,自带 Admin 后台、ORM、认证体系、表单校验,只要你按它的路子来,很多功能不需要自己造轮子;Flask 像一个“工具箱”,核心很轻,路由和请求处理起步极其简单,但用户认证、数据库迁移、表单验证这些都不内置,需要自己集成第三方扩展。
对于校园兼职系统这种规模的项目,Flask 的灵活度优势非常明显。它的核心并不复杂,路由、请求、响应这些基础概念半天就能熟悉,适合把注意力集中在业务逻辑和数据处理上。前面提到的三个角色权限、岗位状态流转,本质上就是几十个接口的编写和组织,Flask 的蓝图(Blueprint)机制配合 RESTful 风格能写得很清晰。
而 Django 的优势在快速搭建后台管理界面这一点上确实很诱人——如果你需要运营端大量依赖表单单页,Django Admin 直接配置出来就能用。但代价是你必须接受它的路由映射、ORM 查询方式、中间件机制等一整套约定,项目规模越大越省事,项目规模中等偏小时反而觉得束手束脚。兼职系统里很多自定义的交互逻辑,在 Django 的框架约束下需要绕不少弯。
在这个项目里我最终选择了 Flask。核心原因是前后端分离架构下,Django Admin 的优势基本发挥不出来——前端是 Vue 独立渲染页面,后端只需要提供 JSON 接口,Flask 的轻量直接可以让接口代码层层分明,调试也直观。当然这不是说 Django 不行,如果你打算带着 Django Admin 一起做后台管理,不走前后端分离路线,选 Django 也完全合理。
2.2 Flask + Vue 前后端分离的优势
选择 Vue 作为前端框架,最直接的原因是组件化开发和数据驱动视图这两点,对这件事帮助太大了。兼职列表页有筛选栏、卡片列表、分页器、收藏按钮,这些如果用原生 JavaScript 写,DOM 操作会非常繁琐;用 Vue 之后,每条兼职记录的数据结构绑定到组件上,状态的改变自动反映到界面,代码量至少减少一半以上。
前后端分离意味着 Flask 后端只负责提供 RESTful API,Vue 前端通过 HTTP 请求调用接口拿到 JSON 数据并渲染。这么做的好处是开发时可以前后端并行,比如后端接口还没写好时,前端可以用 Mock 数据先调页面;调试时也可以通过浏览器开发者工具直接查看接口返回的数据。部署时前端静态文件由 Nginx 之类的服务器托管,后端以服务方式运行,互不干扰。
对于大学生兼职系统,这种架构还有一个隐藏优势:未来如果要出小程序端或者移动 App 端,后端接口基本不需要改动,只需要新做一个客户端去消费这些 API 就行。学校场景下一旦项目验收完,后续大概率会有人想“加个微信小程序版”,到时候你就知道前后端分离的好处了。
2.3 PyCharm 开发环境的配置心得
整个项目我是在 PyCharm 里写的。很多人以为 PyCharm 打开即用,其实有两个配置步骤非常影响体验。第一是虚拟环境的设置,建议每个项目单独创建虚拟环境,在 PyCharm 的 Settings → Project → Python Interpreter 里选择新建的虚拟环境路径,这样依赖包不会污染全局 Python 环境。第二是把 Flask 的运行模板配置好,在 Run Configuration 里指定 script path 指向入口文件,设置环境变量 FLASK_ENV=development 来开启调试模式,这样点击运行按钮就可以直接启动开发服务器,不用每次都在命令行敲命令。
有一点特别提醒:PyCharm 的社区版和专业版在 Flask 项目上的体验差距其实不大,社区版足够应付。真正值得花时间的是熟悉它的调试器——在接口代码里打上断点,用 Debug 模式启动服务,前端请求过来时你可以清晰地看到每个变量的变化,排查问题效率比加 print 日志高一个量级。
3. 系统架构与数据库设计
3.1 前后端交互的接口风格约定
整个系统的后端我完全采用 RESTful 风格来设计接口。资源用名词表示,操作由 HTTP 方法表达:/api/jobs 对应兼职列表的 GET 请求,/api/jobs/{id} 对应单个岗位的获取或修改,/api/jobs/{id}/apply 对应投递申请的动作。这样设计的好处是接口路径清晰,前端开发时通过方法名就能猜出请求意义,不用反复确认文档。
接口的返回格式也做了统一封装。每一个接口都返回固定的 JSON 结构:
{ "code": 200, "message": "success", "data": {} }code 为 200 时表示业务成功,data 里放具体数据;code 非 200 时表示业务异常,message 里给出给用户看的提示信息。HTTP 状态码我也会规范区分,401 表示未登录,403 表示无权限,404 表示资源不存在,500 表示服务器内部错误。这样前端拦截器可以根据状态码统一跳转登录页或弹出错误提示,不用每个接口单独处理。
3.2 数据库表结构设计拆解
数据库我用的是 MySQL,建了 6 张核心表:用户表、岗位表、报名表、收藏表、评价表和公告表。每一张表都有状态字段来支撑业务流程。
用户表字段包括 openid(或账号密码登录凭证)、角色类型、昵称、头像、联系方式、学校或公司名称、创建时间。角色字段我用 int 类型存储,0 表示学生,1 表示企业,2 表示运营,方便以后扩展更多角色。头像字段存的是文件路径字符串,实际的图片文件走文件上传接口保存到服务器目录。
岗位表是整个系统的核心,字段包括发布者ID、标题、类别、薪资数额、薪资单位、工作地点、开始时间、结束时间、招聘人数、已报名人数、岗位描述文本、状态字段、审核意见、浏览量、上下架时间。这里的薪资设计我特意拆成“数额”和“单位”两个字段,是因为兼职有两种常见的计薪方式:按小时计费和按天计费,拆开之后前端展示和后端统计都更灵活。
报名表是连接学生和岗位的“中间表”,包含岗位ID、学生ID、报名附言、当前状态(待审核、已通过、已拒绝、已完成、已取消)、企业回复内容、操作时间记录。这个表的设计决定了整个流程闭环能不能跑通,所以我把它独立出来而不是作为岗位表里的一个字段,就是为了后续可以做状态流转的追溯。
3.3 状态机设计:兼职流程的核心逻辑
岗位的状态我用一个字段表示,取值包括草稿、待审核、已发布、已下架、已关闭。报名记录的状态取值包括待审核、已通过、已拒绝、已完成、已取消。为了避免代码里到处散落魔法数字,我定义了一个常量类统一管理:
class JobStatus: DRAFT = 0 PENDING = 1 PUBLISHED = 2 OFFLINE = 3 CLOSED = 4 class ApplyStatus: PENDING = 0 ACCEPTED = 1 REJECTED = 2 FINISHED = 3 CANCELLED = 4每次状态变更,我都在接口层写清楚“从什么状态到什么状态是允许的”。比如只有已发布的岗位才能被学生投递,只有待审核的报名记录才能被企业通过或拒绝。状态判断写不严谨,会导致很多数据异常问题——比如学生投递了一个已下架的岗位,或者企业对一条已经取消的报名记录做了确认操作。
这里我给出的经验是:不要只在某个接口里做单点判断,最好做一个独立的状态校验函数,所有状态流转都走这个函数。因为多人协作或后续扩展时,很容易在某个新接口里遗漏校验逻辑。
4. 核心功能模块的真实现场
4.1 用户认证与扩展登录态
用户认证我采用的是基于 Token 的方式。用户登录成功后,后端生成一个 Token 字符串返回给前端,前端在请求头里带上这个 Token,后端通过装饰器校验用户身份。登录的密码存储用的是哈希方案,不保存明文。
Token 的生成我并没有引入复杂的扩展库,而是用了 Python 内置模块配合签名机制实现。思路是:将用户ID和过期时间戳拼成一段字符串,使用一个只有服务器知道的密钥做签名,校验时验证签名是否一致、过期时间是否已超过当前时间。四十行代码就能实现一个够用的登录态方案,虽然和专业的 JWT 库相比功能少一些,但对这个项目来说足够清晰易懂。
核心校验装饰器包含两部分逻辑:
def login_required(func): @wraps(func) def wrapper(*args, **kwargs): token = request.headers.get('Authorization', '').replace('Bearer ', '') user_id = verify_token(token) if not user_id: return jsonify(code=401, message='请先登录') g.user_id = user_id g.user = find_user(user_id) return func(*args, **kwargs) return wrapper使用装饰器时,在需要登录才能访问的接口函数上加一行@login_required,函数内部可以通过g.user拿到当前登录用户的完整信息,非常方便。权限控制的粒度我会再写一个role_required装饰器包在外面,实现角色隔离。
4.2 兼职岗位的发布与审核流程
兼职岗位发布不是一个简单的插入数据操作。企业提交岗位信息后,系统需要做两项前置检查:一是必填字段是否完整,二是岗位内容是否包含违禁词或敏感表述。检查通过后,岗位的状态置为待审核,运营端首页会出现一条待办记录。
运营的审核接口逻辑也不复杂:读取岗位的完整信息,结合用户举报和系统预检结果,决定通过或驳回。驳回时必须要填写驳回原因,这个原因会展示给企业端,方便企业修改后重新提交。这里就体现出状态设计的好处:岗位如果一度被驳回,只需把状态改回草稿,修改后再次提交即可,不需要删除重建。
在这一环节我遇到过最多的问题是:企业提交的兼职薪资和岗位类别对不上,比如标着“轻松日结”但薪资高得异常,这种明显超出合理范围的岗位应该直接进人工复核。我在预检阶段除了违禁词匹配,还会加一个简单的薪资范围合理性判断,超过阈值就打上高风险标签。
4.3 学生报名与录取闭环
学生浏览岗位列表后,看到有兴趣的岗位可以点“立即报名”,弹出一个小表单填写报名附言。报名接口要做的第一件事是判断岗位状态是否允许报名:岗位必须处于已发布状态,招聘人数必须大于已报名人数,学生不能重复报名同一岗位。
报名成功之后,企业的待办列表里就会出现这条记录,企业可以查看学生简历和个人信息,然后选择“录取”或“拒绝”。录取时系统会给学生生成一条消息通知,拒绝时也必须附带原因。学生端收到录取通知后,可以选择“确认到岗”或“放弃机会”。只有双方确认到位之后,岗位才能进入“完成”相关的流程。
这套双确认的机制是现实业务里很常见但设计时容易忽略的细节。如果只允许企业单方面确认“已完成”,很容易出现学生实际没到岗但企业点了完成的情况。双确认虽然增加了一次接口交互,但能有效避免纠纷,权衡下来非常值得。
4.4 收藏与搜索功能的实现
收藏功能的实现相对简单,但有一处细节值得注意:收藏按钮的状态需要前后端同步。前端在渲染岗位列表时,如果已经知道当前用户收藏了哪些岗位,就会把按钮置为“已收藏”状态。我的做法是:在岗位列表接口返回数据时,额外返回一个is_favored字段,值为 true 或 false,这个字段是由后端根据当前用户 ID 和岗位 ID 在收藏表里查询得到的。
搜索功能我在字段选择上做了取舍,重点支持岗位标题、岗位类别和工作地点的模糊搜索,而没有对岗位描述做全文检索,原因是岗位描述是长文本,模糊查询效率很低,而且在演示阶段没必要引入搜索引擎组件。但我在设计时就留了扩展考虑:岗位描述字段建立全文索引,后续数据量上来可以直接切换为全文检索而不用改表结构。
4.5 文件上传与静态资源处理
用户头像、岗位配图这些都需要文件上传能力。Flask 端的实现比较直接:接收前端传来的文件对象,校验文件类型和大小,重命名后保存到指定目录,然后把文件的访问 URL 返回给前端。文件类型我用扩展名白名单控制,只允许 jpg、png、webp 这些常用图片格式,大小限制在 5MB 以内。
一个很容易忽略的问题是文件保存的目录权限。开发环境下把上传目录放在项目内部,但部署到服务器后可能会出现 Python 进程没有写入权限的情况,导致接口报错。我当时的做法是在项目根目录建 uploads 文件夹,并在配置里开启UPLOAD_FOLDER,同时把该目录的读权限配好。更规范的做法是直接用对象存储,校园项目的演示环境下可以先不上这么重的依赖。
5. 前后端联调与关键环节实录
5.1 Flask 蓝图与项目目录组织
项目不是单个 py 文件,而是用 Flask 的蓝图机制按模块拆分。
project/ ├── app.py ├── extend.py ├── models/ ├── apis/ │ ├── auth_api.py │ ├── job_api.py │ ├── apply_api.py │ ├── admin_api.py │ └── upload_api.py ├── utils/ └── static/app.py 是入口文件,负责创建 Flask 实例、加载配置、注册蓝图。extend.py 放数据库连接等扩展的单例对象。apis 目录下的每个文件对应一个业务模块,文件内部用蓝图对象定义路由,通过app.register_blueprint()注册到主应用。
这种组织方式最大的好处是,每个模块的路由前缀可以统一:认证接口统一以/api/auth/开头,兼职接口统一以/api/jobs/开头,报名相关接口统一以/api/apply/开头。前端对接时一眼就能看清每个请求对应哪个模块,调试效率很高。
5.2 前端路由与页面设计
Vue 前端我采用的是 Vue Router 单页应用模式。页面路由包括登录注册页、兼职列表页、岗位详情页、企业发布页、学生个人中心、企业工作台、运营后台。路由守卫在跳转时判断用户是否登录,以及当前用户的角色是否能访问目标页面:
router.beforeEach((to, from, next) => { const token = localStorage.getItem('token') if (to.meta.requiresAuth && !token) { next('/login') } else if (to.meta.role && !checkRole(to.meta.role)) { next('/403') } else { next() } })页面之间,兼职列表页和岗位详情页是信息量最大的两个视图。列表页的筛选条件、分页和排序都通过 URL 参数传给后端;岗位详情页除了展示岗位详细信息,还要根据当前用户的角色显示不同的操作按钮——学生看到“我要报名”,企业看到“编辑岗位”和“查看报名列表”。这个动态按钮的逻辑是 Vue 里根据不同角色控制组件显示隐藏,很典型。
5.3 跨域问题的一次完整排查
前后端分离开发时,跨域问题是绕不开的坑。我在本地调试时,前端跑在http://localhost:8080,后端跑在http://localhost:5000,两个端口不同,浏览器默认就会拦截前端向后端发起的 AJAX 请求。第一次遇到这个问题的时候,我一度以为接口写错了,排查了半天才发现是跨域。
解决方法是后端加入 CORS 支持,允许前端的请求跨域访问。可以使用 Flask-CORS 扩展,配置简单:
from flask_cors import CORS CORS(app, resources={r"/api/*": {"origins": "http://localhost:8080"}})这里我特意把允许的源写成了前端的地址,而不是用*通配符。虽然通配符更省事,但生产环境里放开所有来源会有安全隐患,任何其他网站都能调用你的接口。养成写具体源的习惯,安全性会好很多。
5.4 数据库连接与 ORM 映射的配置
数据库配置我建议直接写在入口文件或独立配置类中。连接 MySQL 时,对比过几种方案之后,我用的连接串格式是这样的:
mysql+pymysql://username:password@localhost:3306/campus_job?charset=utf8mb4字符集一定不要用 utf8,要用 utf8mb4,否则中文可能正常,但一些特殊符号和 emoji 字符会报编码错误。ORM 模型定义时,每个表对应一个 Python 类,字段是类属性,类型用 Column 定义。定义好模型后有一步特别容易忽略:需要在模型类下方通过 ORM 的映射关系把模型和数据库表连接起来,然后在入口文件里调用建表相关函数才能创建表。
我在开发过程中被这个问题卡过一次,印象很深:模型定义得整整齐齐,启动服务也不报错,但数据库里就是查不到表。原因就是只定义了模型,没有执行建表操作。这个坑在 Flask 的 ORM 配置里非常常见。
5.5 从零搭建前端环境的注意事项
前端项目我基于 Vue CLI 创建。创建完成后,需要先安装路由插件、状态管理插件和 HTTP 请求库。这里我踩过一个版本兼容的坑:如果安装了最新版的 Vue CLI,生成的默认项目结构和旧教程里的结构会有很多差异,照着老文章抄代码很容易对不上,建议创建项目后先看一版官方文档再动手。
HTTP 请求封装我写了一个统一模块,所有请求都会自动带上 Token、统一处理错误状态码。这里面的核心是对响应拦截器的设置:只要后端返回 code 非 200,就直接弹出错误提示,并处理 401 跳转登录页。这样前端每个页面里不需要重复写错误处理代码,体验会好很多。
6. 常见问题排查与避坑指南
6.1 登录状态失效和 Token 过期
前后端分离下最容易遇到的一个情况是:页面还在,但 Token 过期了,用户点击某个操作按钮就突然跳到登录页。这是预期行为,但关键是跳转前需要把本地的用户状态清干净,否则会出现“明明没登录,但刷新页面后还能看到部分用户信息”的假象。
我在前端路由失活时做了一层处理:Token 过期后,除了跳转登录页,还会清除本地存储的所有用户缓存,并重置全局的 Vuex 状态。这样用户可以干净地重新登录,不会出现莫名其妙的页面残留。后端方面,Token 过期时间我设为 7 天,足够覆盖学生正常的使用频率,同时也不会长期暴露接口。
6.2 并发报名导致的数量超限
兼职岗位有人数限制,但如果在高并发的瞬间同时有多个学生报名同一个岗位,程序读取“已报名人数”时都是同一个旧值,然后各自加一写回,人数就会出现超卖。这在真实平台上绝对是严重事故,类似订票的超卖问题。
我采用的解决方案是在数据库层面做加法更新,而不是先查询后更新。具体的 SQL 逻辑是“一条 UPDATE 语句同时增加报名人数并判断是否已满”,但这要求在 SQL 里做原子判断。或者更简单一点,直接在岗位表加一个人数剩余字段,用条件更新来控制数量。篇幅有限我不展开完整 SQL,但这个思路值得在写代码前想清楚,因为顺序上如果先做“查人数”再“写回”,一定会留坑。
6.3 文件上传后无法访问的用户权限陷阱
文件上传后,访问静态文件这个动作也会被后端的登录校验拦截。有一次我以为文件上传成功了,但前端加载图片 404,排查后发现是配置了全局的登录拦截器,把静态文件的请求也拦住了。解决办法是给静态文件访问路径设置白名单,不走登录检测。这个细节说大不大,但卡住的时候确实很耽误时间。
6.4 前后端字段不一致导致的数据异常
前端用驼峰命名(jobTitle、publishTime),后端用下划线命名(job_title、publish_time),这个在前后端分离项目里是最常见的联调障碍之一。我当时就经历过前端页面表格里所有字段全是空的,检查发现接口明明返回了数据,但字段名对不上。
解决方式我推荐统一规范:后端接口返回时统一转换为驼峰命名的 JSON 字段。后端的模型定义用下划线命名只是内部风格,在序列化返回数据时做一层映射,这样前端开发时不需要记两种命名规则。这个映射可以在每个模型定义类里加一个 to_dict 方法统一处理。
6.5 部署阶段容易忽略的三个配置项
项目开发完成后部署上线,有三件事我几乎每次都要提前确认。第一是 Debug 模式必须关闭,开启 Debug 的 Flask 服务在生产环境会暴露详细的错误堆栈,非常危险,而且会导致代码改动后服务自动重启,浪费资源。第二是数据库连接串里的地址要改成生产数据库的地址,不能还是本地 localhost。第三是前端构建之后,静态文件的路径配置必须是正确的绝对路径,否则 Vue 打包出来的页面加载不到 JS 和 CSS 文件,白屏一片。
7. 身体踩过坑才记得住的几个小技巧
写到这里,我想分享几个通过反复踩坑才沉淀下来的经验,可以让你的项目顺利很多。
兼职系统的消息通知模块,我是直接在报名状态变更时同步生成一条站内信记录,而不是等用户刷新页面再去拉取。用一张消息表和报名表做关联,轮询接口返回未读消息数,前端右上角就有动态小铃铛。当时觉得这个功能只是锦上添花,但演示的时候,面试官或导师对这类实时反馈的印象分提升是很明显的。
在 PyCharm 里调试 Flask 还有一个实用的技巧:前端发起请求时,在接口函数开头打一个断点,然后用 Debug 模式启动,你就能在调试器里看到请求包含的所有参数和请求头。排查一个很隐蔽的参数丢失问题,用这种方法一看便知,远比自己加日志猜测高效。
关于数据库设计,我在建表阶段就建立了一个习惯:每张表都包含 create_time 和 update_time 两个时间字段,并把 update_time 设置为每次更新自动变更。当时觉得多此一举,后来排查数据问题、统计一段时间内的数据变化时,才知道这个习惯有多救命。如果你现在刚开始建表,强烈建议把这两个字段加上。
最后想到的一个价值也很高的建议是:在 Git 里做一次“初始可用版本”的提交,把所有功能跑通后的代码打个标签。之后无论怎么重构、加新功能,随时可以对比回退,也可以从这个可用版本拉分支去尝试其他方案。我那个后来加小程序端的尝试,就是从初始版本拉出来的分支,全程没有影响主项目的稳定性。
这套系统从需求梳理到代码落地,前后大概不到三周的时间。节奏上,前面大概花了两天做需求拆解和表结构设计,中间十三四天写后端和前端的内容,最后几天集中处理联调问题、打磨页面细节。如果让我重新做一遍,我一定会在需求拆解阶段花更多时间把状态流转和边界情况理得更细,因为那些“当时没想清楚”的问题,后来几乎都在联调和测试阶段排着队找上门来。希望这篇记录里踩过的坑能帮你省下这部分时间。