1. 立项逻辑:兼职平台为什么适合作为毕设,需求边界画在哪
先别急着写代码。很多同学拿到"大学生兼职平台"这个题目,第一反应是去 GitHub 找个开源项目改一改,或者直接照着网上的视频把代码敲一遍。这种做法不是不行,但到了开题答辩和中期检查的时候,老师一问"你是怎么设计的""这个业务表为什么这么建",大概率会卡壳。
我做毕业设计辅导这几年,被问得最多的问题就是:"老师,兼职平台是不是太简单了?""会不会和同学撞题?"我的回答一般是:题目不在新旧,在于你把业务闭环做到多完整。大学生兼职平台这个题,表面上是一个信息发布系统,但它天然带有完整的业务链条——用户注册登录、岗位发布、审核规范、检索匹配、申请录用、打卡结算、评价闭环。这一整条链路做下来,Spring Boot 后端、微信小程序前端、MySQL 设计、安全认证、甚至微信支付都能合理地串起来,正好覆盖毕业设计需要展示的"工作量"和"技术深度"。它看起来不炫,但是能让你在答辩时讲清楚每一个模块存在的理由。
1.1 先想清楚要解决的真实场景
做设计之前,可以先花半天时间想清楚三个问题:谁在用、拿来干什么、什么样的流程走起来不别扭。
兼职平台这个题目,典型的用户画像是两拨人。一拨是想找兼职的大学生,他们的核心诉求是"快速找到离学校近、靠谱、钱不白干"的岗位;另一拨是发布岗位的招聘方,可能是学校周边的奶茶店、快递站、培训机构,也可能是个人发布的家教、跑腿需求。痛点非常明显:信息真假难辨、岗位审核缺失、结账流程没有保障。所以一个好用的兼职平台,不是在做一个"招聘网站玩具版",而是要解决信息撮合和信任保障的问题。
我建议在需求文档里把下面的核心场景写清楚:
- 学生端:浏览岗位、按地区和时薪筛选、收藏岗位、投递简历、查看录用结果、报名签到、对兼职经历评价。
- 招聘端:发布岗位、管理自己发布的岗位、查看投递列表、录用或拒绝学生、确认学生完成兼职后结算。
- 管理端:审核岗位是否合规、处理违规举报和虚假信息、发布系统公告、统计平台数据。
1.2 功能范围裁剪:哪些必须做,哪些可以砍
毕设最怕的不是功能太少,而是范围失控。很多同学一开始列功能清单,恨不得把"秒杀""直播带岗""智能推荐"全塞进去,结果代码写到最后三分之一,实在撑不住,再把功能砍掉,前端页面和后端接口四处漏风,反而更难收场。
我的经验是,把功能拆成"必须做""尽量做""选做"三档:
| 优先级 | 功能模块 | 理由 |
|---|---|---|
| 必须做 | 微信登录、岗位发布、岗位列表/搜索/筛选、投递与录用、个人中心 | 这是业务闭环的地基,缺了任何一个,系统就不完整 |
| 尽量做 | 收藏岗位、岗位审核、公告、站内消息、兼职评价 | 体现业务深度,答辩时能拿出来讲 |
| 选做 | 微信支付结算、签到打卡、数据统计报表、管理员图表 | 有余力再上,能显著提升系统完整度 |
一定要保住支付这个模块,哪怕只是作为"选做"也尽量做。原因后面我会单独讲,微信支付 V3 对接的完整链路,恰恰是很多同学答不上来、却能拉开分差的关键点。
1.3 角色权限与业务规则定义
角色权限这块,建议一开始就理清楚,不要写到一半再补。常见做法是三端角色:STUDENT(学生)、EMPLOYER(招聘方)、ADMIN(管理员)。同一个微信用户,理论上可以同时是学生和招聘方,所以账号表和角色表要能支持多角色,最简单的做法是在用户表上加role字段,学生默认是STUDENT,注册招聘方时再升级为EMPLOYER。如果以后想扩展,再拆成用户-角色中间表也不迟,毕设阶段不用过度设计。
业务规则里有一个很容易被忽略的点:岗位必须经过审核才能进入公开列表。很多同学图省事,发出来就直接展示,结果答辩时老师一句"如果有人在上面发诈骗信息怎么办"就把人问住了。所以哪怕是"先审核后展示"这样一个小规则,也要体现在流程里,它不仅合理,还让整个系统多了一张"审核表"、多了一个管理接口,工作量也有了。
2. 技术选型:"够用且好答辩"的取舍思路
技术选型这个环节,最忌讳的是"啥新用啥"。有些同学习惯把最新版本全往项目里堆,Spring Boot 3.4、JDK 21、Redis 7、Elasticsearch 全都上,结果遇到网上资料对不上的问题,光排错就花了两周。
2.1 Spring Boot 版本:2.7 还是 3.x
很多人纠结这个,我直接给结论:如果你是为了稳定做完毕设,选 Spring Boot 2.7.x + JDK 8/11;如果你学习能力强、愿意折腾,选 Spring Boot 3.x + JDK 17。
为什么这么推荐?因为市面上大量教程、博客、开源项目,仍然基于 Spring Boot 2.x。2.7 是 2.x 的最终版本,资料齐全、生态稳定,MyBatis-Plus 3.5.x 直接兼容,微信支付 SDK 也几乎没有兼容性问题。3.x 虽然性能更好、AOT 等新特性对答辩有加分,但它把javax.*改成了jakarta.*,很多老教程里的 import 语句直接报错。如果一个项目的重点放在业务实现和毕业答辩,而不是前沿技术探索,2.7 是性价比最高的选择。
2.2 小程序端选原生还是 uni-app
这个题目的名称里明确写了"微信小程序",所以前端我的建议很简单:直接用微信小程序原生语法。不要为了炫技上 uni-app,原因有三个。第一,原生小程序不需要额外的编译链,开发者工具打开就能调试,写起来最直接;第二,小程序原生组件和 API(比如wx.requestPayment、wx.login、wx.chooseMedia)本来就是按原生语法设计的,用 uni-app 包一层反而多了语法差异和兼容性坑;第三,答辩时老师看到你用的是 WXML/WXSS/JS 原生结构,会更认可"你真的花时间研究了小程序开发"。
后端接口设计成前后端分离的 RESTful API,小程序端只管调用。这样系统演示的时候,小程序连不上后端,或者后端单独跑在服务器上,都不影响你讲清楚架构。
2.3 数据库、ORM 与辅助组件组合
数据库用 MySQL 8.0 就够了,不使用任何冷门数据库。ORM 我推荐 MyBatis-Plus,因为它比 JPA 更好控制 SQL,也更好向老师解释"这条 SQL 是怎么调的索引"。尤其需要分页、条件查询的地方,MyBatis-Plus 的LambdaQueryWrapper可以省掉大量 XML 配置,代码量直接降三分之一。
其他辅助组件,根据实际需要最小化引入:
- JWT:实现无状态登录凭证,避免传统 Session 在服务端占用内存。
- Hutool:工具库,生成随机数、时间处理、JSON 转换都能用,减少重复代码。
- Lombok:实体类简化。
- 如果有"岗位下架、结算超时"这类定时需求,加一个 Spring 自带的
@Scheduled即可,不用引 Quartz。
Redis 这个点,我要特别说说。很多教程一上来就让你"整合 Redis 做缓存",但如果你对 Redis 的使用只是停留在"引入依赖 + 存一个 List"这种程度,答辩时老师深问缓存一致性,你会很被动。除非你确实设计了缓存刷新策略和数据一致性的方案,否则建议在小项目里先不强行加,把业务闭环和支付做好,已经是稳稳的优良等级。
3. 数据库设计:从业务流程反推表结构与状态机
数据库是整个系统里改动成本最高、答辩时最容易出彩的部分。我的习惯是先画业务流转图,再反推表——不是拿着一个现成的 SQL 文件照着建表,而是先问自己"这个流程里有哪些状态变化",再把状态变化落到字段上。
3.1 核心实体与字段设计
兼职平台的核心实体,建议按下面这张表来设计,后续扩展也方便:
| 表名 | 主要字段 | 作用说明 |
|---|---|---|
user | id、openid、nickname、avatar、phone、role、create_time | 用户主表,openid 做唯一索引,一个微信账号一条记录 |
employer_info | id、user_id、company_name、contact、licence_url、status | 招聘方信息扩展表,发布岗位前先完善资质 |
job | id、publisher_id、title、description、category、salary、salary_unit、province/city/district、address、need_num、deadline、status、view_count | 岗位主表,status 控制上架/下架/审核状态 |
job_apply | id、job_id、student_id、resume_text、status、apply_time | 投递记录表,同时承载录用流程的状态流转 |
favorite | id、user_id、job_id、create_time | 收藏表,唯一索引防止重复收藏 |
job_check | id、job_id、admin_id、check_status、reason、check_time | 岗位审核记录表,方便追踪审核历史和驳回原因 |
order | id、order_no、job_id、student_id、employer_id、amount、status、pay_time、refund_time | 订单/结算表,用于兼职完成后的薪酬结算 |
comment | id、job_id、from_user_id、to_user_id、score、content、create_time | 兼职评价表 |
几个容易忽略的细节:
salary建议用整数存储"元",或者用DECIMAL(10,2),不要用 float,否则涉及金额计算时会出现浮点误差。salary_unit要区分"元/小时、元/天、元/次",这个字段在很多毕设里没有,属于业务完善度上的加分项。- 所有表都建议加上
create_time和update_time,MySQL 8 的DEFAULT CURRENT_TIMESTAMP和ON UPDATE CURRENT_TIMESTAMP可以直接完成。
3.2 订单与结算的状态机设计
job_apply这张表是整个系统里最容易被做坏的地方。很多人把它的状态设计成只有"已申请 / 已通过 / 已拒绝"三个,看起来没问题,但一旦和薪酬确认、订单结算串起来,状态就不够了。
我推荐的状态流转是:
PENDING:学生投递,待招聘方处理。ACCEPTED:招聘方录用。此时岗位的applied_count加 1,如果岗位已招满,自动把岗位状态改成异常或已下架。REJECTED:招聘方拒绝,学生可以继续投其他岗位。WORKING:学生确认到岗/招聘方标记开始兼职(可选,用来展示更完整的闭环)。COMPLETED:兼职完成,这时候才进入结算环节。CANCELED:学生或招聘方取消,取消后要释放岗位名额。
这样设计的好处是,答辩时老师问"你怎么保证一个岗位不会超招、怎么保证薪酬结算只发生一次",你都能用状态机回答得清清楚楚。
3.3 防并发与防重复提交的库表约束
很多毕设项目到了后期,经常出现"同一个学生对同一个岗位投了十份简历""录用之后又重复录用"这种数据脏乱问题。这不是流程设计不出来,而是缺少数据库层面的兜底约束。
这里说两个非常实用的招:
- 投递唯一约束:在
job_apply表上建UNIQUE KEY uk_job_student (job_id, student_id)。这样即使用户在小程序里手快点了两次"立即申请",后端无论如何都不会插入两条相同的记录。 - 订单唯一编号:
order_no直接用时间戳 + 随机数或雪花 ID,同时加唯一索引。生成订单时先查订单再插入,或者在插入时捕获唯一键冲突异常,就能避免同一笔兼职结算被重复创建。
这两个约束虽然只是两行 SQL,但它是"系统能不能经受住并发考验"的最直观证明,放在答辩 PPT 的数据库设计一页里,比写一大堆"高并发架构"更有说服力。
4. 后端实现:登录认证、统一响应与核心业务闭环
后端代码我不打算贴完整项目,那会让你失去自己动手写一遍的机会。这里重点讲三个直接影响系统能否跑通的节点:登录认证怎么写、统一响应怎么做、岗位发布审核闭环如何落地。
4.1 微信登录与 JWT 签发
微信小程序的登录流程,核心就是"用微信给的临时登录凭证换后端认识的身份"。具体来说:
前端调用wx.login()拿到一个code,这个code有效期只有五分钟且只能用一次。小程序把它传给后端,后端拿着code去调用微信的jscode2session接口,换取openid和session_key。openid是用户在咱们平台里的唯一身份标识,session_key用于解密用户信息,但需要注意,现在微信已经推荐使用头像昵称填写能力和手机号快速验证组件,不建议把敏感信息明文存库。
拿到openid后,判断用户表里有没有这个人。没有就注册新用户,有就直接签发 JWT 返回给小程序。后续每次请求,小程序在请求头里带上Authorization: Bearer <token>,后端拦截器统一解析并设置当前用户上下文。
核心代码可以简化成下面这样:
@PostMapping("/wx/login") public Result login(@RequestBody WxLoginDTO dto) { // 1. 向微信服务器换 openid WxSession session = wxService.code2Session(dto.getCode()); // 2. 根据 openid 查用户,不存在则注册 User user = userService.getByOpenid(session.getOpenid()); if (user == null) { user = userService.register(session.getOpenid()); } // 3. 生成 JWT String token = JwtUtil.createToken(user.getId(), user.getRole()); return Result.success(new LoginVO(token, user)); }所谓"登录态"不用自己写 Session 存储,JWT 本身就足够。但要注意 JWT 的过期时间设置,建议 7 天,配合小程序的wx.checkSession来判断是否需要重新登录。
4.2 统一响应体与全局异常处理
统一响应体是一个看似基础、实际却极其重要的环节。前后端分离开发时,如果每个接口返回的结构都不一样,前端调接口时就需要写一堆if (res.code === 0)之类的分支,非常痛苦。
我的习惯是定义一个Result<T>通用类,内部包含code、message、data三个字段,并定义常用的静态方法:
@Data public class Result<T> { private Integer code; private String message; private T data; public static <T> Result<T> success(T data) { Result<T> r = new Result<>(); r.setCode(200); r.setMessage("ok"); r.setData(data); return r; } public static <T> Result<T> error(int code, String message) { Result<T> r = new Result<>(); r.setCode(code); r.setMessage(message); return r; } }配合@RestControllerAdvice做全局异常处理,把"业务异常""参数校验失败""未登录""服务器内部错误"统一拦截。这样小程序端在网络请求封装里只需要判断code === 200,其他情况一律弹 Toast 展示message,开发效率能提升一大截。
4.3 兼职岗位的发布-审核-下架流程
发布流程不要做成"提交后直接进入列表"。标准做法是:
- 招聘方提交岗位,
status = PENDING。 - 管理员在管理端看到待审核列表,通过或驳回;驳回时必须填原因,方便招聘方修改后重新提交。
- 审核通过后,
status = PUBLISHED,学生端列表和详情页才能看到。 - 岗位到期或招满后自动下架;期间招聘方也可以手动下架。
这里的@Scheduled定时任务可以用来做"到期自动下架":
@Scheduled(cron = "0 0 0 * * ?") public void autoOfflineJobs() { jobService.autoOfflineExpiredJobs(LocalDateTime.now()); }@Scheduled在单机部署的毕业设计里完全够用,不需要引入分布式任务调度框架。这也是一个可以在答辩时讲的"技术选型有边界"的意识。
5. 微信小程序端:页面结构、登录态与交互细节
小程序端的核心不在于花哨的 UI,而在于业务闭环能被完整演示。一般我会引导学生按 TabBar 来划分页面:首页、分类/搜索、发布、消息、我的。实在想简化,可以把"消息"合并到"我的"里,保留四个 Tab。
5.1 全局配置与页面划分
打开app.json,先配置好页面路径、窗口样式和 TabBar:
{ "pages": [ "pages/index/index", "pages/category/category", "pages/publish/publish", "pages/message/message", "pages/mine/mine", "pages/job/detail", "pages/apply/apply", "pages/order/order" ], "tabBar": { "color": "#999", "selectedColor": "#07c160", "list": [ { "pagePath": "pages/index/index", "text": "首页" }, { "pagePath": "pages/category/category", "text": "找兼职" }, { "pagePath": "pages/publish/publish", "text": "发布" }, { "pagePath": "pages/message/message", "text": "消息" }, { "pagePath": "pages/mine/mine", "text": "我的" } ] } }注意招聘方发布和普通学生浏览,用的是同一个 TabBar 入口,后端在接口层面根据用户角色做权限控制。这样页面结构更稳定,不用因为角色不同而动态切换 TabBar。
5.2 登录态维护与请求封装
小程序端要封装一个统一的request工具。登录回来后把 token 存到wx.setStorageSync('token', token),每次请求在 header 里带上。如果后端返回 401,则重新执行登录流程。
封装请求工具时,记得处理微信小程序的并发限制和超时:
const request = (url, method = 'GET', data = {}) => { const token = wx.getStorageSync('token') return new Promise((resolve, reject) => { wx.request({ url: `${BASE_URL}${url}`, method, data, header: { 'Content-Type': 'application/json', 'Authorization': token ? `Bearer ${token}` : '' }, timeout: 10000, success: (res) => { if (res.data.code === 200) { resolve(res.data) } else { wx.showToast({ title: res.data.message || '请求失败', icon: 'none' }) reject(res.data) } }, fail: (err) => reject(err) }) }) }这个封装会让后续所有页面的 API 调用都非常简单,一个request('/job/list', 'GET', params)就能完成,不需要每个页面重复写 wx.request 的套路代码。
5.3 软键盘遮挡输入框的处理
这个坑我经常遇到,网上相关内容也很多:在小程序里填写兼职描述或申请理由时,输入框聚焦后弹起软键盘,键盘会直接挡住输入框,导致看不到自己刚输入的内容。
根本原因是,小程序的页面高度默认是满屏的,软键盘弹出时不会自动把输入框顶上去。常规解决方案有两种:
- 在
page.json中设置"disableScroll": false,同时监听bindkeyboardheightchange事件,动态给输入框容器增加一个padding-bottom,高度等于键盘高度。 - 使用
adjust-position相关的配置,但要注意不同机型表现不一致,真机调试时重点验证 iOS 和 Android 两端。
我会在页面上这样处理:
onReady() { wx.onKeyboardHeightChange((res) => { this.setData({ keyboardHeight: res.height }) }) }然后输入框所在容器动态绑定padding-bottom: {{keyboardHeight}}px。这个方案简洁有效,能避免输入内容被键盘完全遮挡。这是一个小而真实的体验优化点,跟那些"整体换框架"的方案比起来,改动量小,效果立竿见影。
5.4 图片上传与服务端白名单配置
发布兼职时,招聘方通常会上传门店照片或资质图片。小程序端用wx.chooseMedia选图,然后调用wx.uploadFile上传到后端。注意wx.uploadFile的 header 中不能直接自定义Content-Type,你需要在 formData 里带 token,或者在后端专门设置一个"允许未登录但仅做上传校验"的接口,我一般选择前者,让上传接口也走统一的鉴权逻辑。
服务端接收图片时,建议不要直接把文件路径落库,而是落一个可访问的 URL 或相对路径。生产环境配置时,要把图片域名加到微信公众平台的"downloadFile 合法域名"里,否则真机上图片无法显示。
这里有个容易被忽略的点:微信开发者工具里的"不校验合法域名"开关只对开发调试有效,真机预览时必须配置合法域名才能正常请求和下载图片。
6. 微信支付 V3 对接的完整链路与踩坑
为什么我要单独把支付拎出来写一节?根据实际辅导情况,"小程序微信支付 v3 对接"是毕业设计里最容易让人连续卡壳三天的环节。很多人把项目做到这一步,后端接口全通了,支付却怎么都调不通。我下面按链路一步步说清楚,尽量帮你避开那些已经趟过的坑。
6.1 技术层面需要哪些密钥与证书
微信支付 V3 和 V2 最大的区别在于:V2 用的是 MD5/HMAC-SHA256 简单签名,V3 用的是SHA256-RSA2048 签名 + AES-256-GCM 回调加密。简化说,V3 更安全,也更麻烦。
你需要在商户平台准备以下东西:
| 配置项 | 说明 |
|---|---|
商户号mchid | 商户平台的商户号 |
| 商户 API 证书私钥 | 下载的apiclient_key.pem,用于请求签名 |
| 商户证书序列号 | 证书的序列号,请求头中Wechatpay-Serial使用 |
| APIv3 密钥 | 32 字节随机字符串,用于回调报文解密 |
| 小程序 AppID | 前端的 appid,下单接口中传入 |
回调通知地址notify_url | 必须是公网可访问的 HTTPS 地址 |
容易搞混的是"商户平台 API 密钥"和"APIv3 密钥"的区别。V3 中的 APIv3 密钥是在商户平台"API 安全"里自己设置的 32 位字符串,不是那个 32 位 APIv2 密钥,也不是证书私钥。
6.2 JSAPI 下单与服务端签名
小程序端使用的支付方式叫JSAPI 支付。流程是:后端先调微信支付的下单接口,拿到一个prepay_id;然后后端自己用prepay_id构造二次签名参数,返回给小程序;小程序再调wx.requestPayment。
后端下单的请求大致如下(关键参数列出来,签名由 SDK 自动处理):
// 以 wechatpay-java 官方 SDK 为例 JSAPIService service = new JSAPIService.Builder() .merchantId(mchId) .privateKey(privateKey) .merchantSerialNumber(serialNo) .apiV3Key(apiV3Key) .build(); String requestBody = buildOrderJson(order); HttpResponse response = service.prepayWithRequestPayment( appId, mchId, orderNo, totalFee, description, notifyUrl, payerOpenid, null);下单接口会返回当前用户的requestPayment所需参数,包括timeStamp、nonceStr、package、signType、paySign。把这些参数原样返回给小程序即可。
需要注意,金额单位是分,比如 50 元下单时要传5000,不是50。这个单位错误是新人报错的高频原因。
6.3 回调验签与幂等处理
支付完成之后,微信服务器会异步回调你的notify_url。这个回调不处理好的话,会出现用户付了钱、但订单状态仍然显示"待支付"的尴尬情况。
回调处理分三步:
- 先校验请求头中的签名信息,确认请求确实来自微信支付。
- 解密回调报文(AES-256-GCM),拿到订单号、支付结果。
- 根据订单号查订单,判断金额是否一致、订单当前状态是否为"待支付";如果是第一次处理,则更新订单状态为"已支付";如果已经处理过,则直接返回成功,避免重复回调导致重复更新。
幂等处理最简单的方式,就是利用order表里的订单状态字段做判断:只有PENDING状态的订单才允许更新为PAID。SQL 可以写成:
UPDATE `order` SET status = 'PAID', pay_time = NOW() WHERE order_no = #{orderNo} AND status = 'PENDING'如果更新的行数为 0,说明订单不存在或已经处理过,直接返回成功。这个写法把幂等逻辑压缩成了一次条件更新。
6.4 最容易翻车的几个细节
根据我自己的排错经验和帮学生排查的情况,支付调不通的常见原因,按出现频率排序大概是:
- 回调地址不是公网 HTTPS。微信要求回调必须走公网 HTTPS,本地
localhost和http都不可能收到回调。解决办法是把后端部署到云服务器,配置好域名证书;如果只是开发联调,可以在云服务器上先部署一版,用真实线上环境做回调验证。仅仅本地启动后端、不想申请域名,是调不通回调的。 - 签名或者序列号对不上。这个报错通常都是证书序列号填错,或者私钥与序列号不匹配。序列号是
apiclient_cert.pem证书文件里的Serial Number,别手动抄,用命令解析确认一下。 - AppID 与商户号没有绑定关系。在商户平台里要把小程序 AppID 绑定到当前商户号,否则下单会提示"appid 与 mchid 不匹配"。
- 商户平台未开通 JSAPI 支付权限。需要在小程序后台关联商户号、签约产品,并保证主体一致或已授权,否则会报
NO_AUTH。
还有一点需要单独提醒:如果小程序因为违规被平台限制支付能力,页面里会提示"由于小程序违规,支付功能暂时无法使用"。这种情况下,项目演示阶段可以让后端保留支付逻辑,前端再做一层模拟渠道,走"模拟支付成功"的开关来演示完整流程。但这里有一条红线:只能用于本地演示或开发测试,绝不能在上线环境绕过微信支付风控。如果确实存在违规记录,应从微信公众平台的申诉渠道处理,而不是在代码层面绕过平台的合规限制。
7. Docker 部署与上线检查清单
很多毕设项目演示的时候,是"后端本机起,小程序开发者工具里开不校验合法域名"。这种演示方式在答辩现场风险极大——一旦网络波动、笔记本故障,整个环节就崩了。稳妥的做法是提前把项目部署到云服务器,答辩时只需要打开小程序,后台服务稳定运行。
7.1 用 docker-compose 编排后端与 MySQL
后端和 MySQL 用 docker-compose 一键启动,是最省心的部署方式。一个简单的docker-compose.yml长这样:
version: "3.8" services: mysql: image: mysql:8.0 container_name: parttime-mysql environment: MYSQL_ROOT_PASSWORD: your_password MYSQL_DATABASE: parttime_job ports: - "3306:3306" volumes: - ./mysql/data:/var/lib/mysql - ./mysql/init:/docker-entrypoint-initdb.d restart: always backend-app: build: . container_name: parttime-backend depends_on: - mysql environment: SPRING_DATASOURCE_URL: jdbc:mysql://mysql:3306/parttime_job?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai SPRING_DATASOURCE_USERNAME: root SPRING_DATASOURCE_PASSWORD: your_password ports: - "8080:8080" restart: always注意,./mysql/init目录下的 SQL 脚本只会在容器第一次创建时自动执行。因此部署前把建表 SQL 和初始数据放进去,是一个很关键的工程化习惯。
7.2 小程序后台的合法域名配置
部署完成后,第一步不要直接拿手机扫码体验,而是要完成小程序的域名白名单配置。在微信公众平台的"开发管理-开发设置-服务器域名"里,把后端的 HTTPS 域名填到request合法域名和uploadFile合法域名里。如果是用wx.downloadFile下载图片,还要在downloadFile合法域名里加上图片资源域名。
需要强调的是,在后端接口未上线 HTTPS 之前,先不要改小程序里的url。先在开发者工具里开"不校验合法域名"做开发调试,等服务器环境完全就绪,再切换到正式域名,避免反复改代码。
7.3 数据库初始化:避免手忙脚乱的现场建表
经常有同学在答辩前一天发现,服务器上的数据库表没建全,后端启动就报Table 'xxx' doesn't exist。网上搜"springboot + mybatis 当表不存在自动建表",大多会用 JPA 的ddl-auto来实现。但如果你用的是 MyBatis-Plus,它默认没有自动建表能力。
我的建议是:不要依赖自动建表,而是建立一套完整的初始化脚本流程。具体做法:
- 把
schema.sql(建表语句)和data.sql(基础数据)放在项目src/main/resources/db/目录。 - 部署时,通过
docker-entrypoint-initdb.d或手动执行一次mysql -u root -p < schema.sql完成初始化。 - 后端 Service 启动时做一个检查:如果核心表不存在,则打一条
WARN日志,提醒你没初始化数据库。
这样处理,比追求"自动建表"更可控。数据库结构是开发环境的沉淀,不应该在生产环境自动生成,这是一个值得在答辩时讲的工程理念。
8. 答辩准备与后续扩展方向
代码写完、部署上线,这只是做完了毕设的一半。答辩环节,老师不会跑完你所有功能,只会挑几个点问,而你要能把这些点的"为什么"讲清楚。
8.1 几个高频问题的准备方向
- "系统怎么保证岗位不会被批量重复申请?" -> 回答
job_apply表唯一索引 + 业务层状态判断。 - "如果用户不点确认完成,兼职的钱怎么结算?" -> 回答订单的自动超时机制,以及结算生成流水之后,再次回调会被幂等过滤。
- "多个用户同时申请同一个岗位,会不会数据错乱?" -> 讲数据库约束 + 事务,不用吹"高并发",诚实表达服务端接口是串行处理即可。
- "小程序端登录态有效期多久?怎么续期?" -> 将 JWT 过期时间和小程序检测会话过期的
wx.checkSession答清楚,最好说明你做了前端 401 拦截自动重新登录。
这些问题都没有标准答案,但如果你能在job_apply唯一索引、order幂等更新、JWT 鉴权、状态机设计这几个点上讲出自己的理解,答辩效果就不会差。
8.2 后续可以弹性扩展的方向
如果还有时间和精力,或者准备把项目作为求职作品集,可以考虑以下扩展方向:
- 消息通知:学生被录用、岗位被驳回时,通过订阅消息通知用户。小程序订阅消息是一次性订阅,需要用户主动授权,这块能体现出对小程序平台规则的熟悉度。
- 信用评分:基于学生完成兼职后的评价和招聘方评分,构建双方信用分。信用分影响岗位排名和是否优先录用,这个方向很适合在"智能推荐"上继续延伸。
- 管理端可视化报表:用 ECharts 或简单的柱状图/饼图展示兼职岗位数量、学生分布、交易规模,让管理员界面更直观。这个功能做出来,非常能打动答辩老师,因为大部分同类项目都没有。
- 数据权限与多租户:如果平台要支持多个学校独立运营,可以在
job表上加school_id字段,实现数据隔离。这个扩展可以证明你考虑过业务进一步发展的问题。
最后再分享一个实际带项目时的习惯:每完成一个模块,就同步更新项目说明文档,把接口调用方式、核心表结构、部署步骤都记下来。这不只是为了给老师看,更是为自己省事——很多时候代码写完了两周,回头再看,连自己都要花不少时间才能回忆起来当时的接口字段含义。有这份文档,答辩前整理材料会轻松很多。
做毕业设计这件事,本质上是一次"从零到一独立交付完整项目"的训练。兼职平台这个题目,技术栈主流、业务清晰、扩展空间大,非常适合用来展示你的开发能力和工程思维。别怕它不够"新",把业务闭环做扎实,把支付链路、状态机、幂等设计这些细节讲透,它就能成为一份拿得出手的毕业设计,也能成为你往后写简历时的一段真实项目经历。