简介:面向计算机相关专业毕业设计或课程设计人群,提供一套基于微信小程序与Java后端开发的校园兼职系统完整项目。源码涵盖小程序端、后台管理端与业务接口层,配有SQL数据库脚本及说明文档,可帮助快速理解前后端交互、权限控制和兼职信息发布等核心流程。资源包共1254个文件,包括319个PNG图片、228个JS脚本、144个Vue组件、92个WXML页面和105个JSON配置等,压缩后大小约14.79MB,目录结构清晰,适合用于项目实战与二次开发。已有729人学习下载。除主源码外,还包含环境启动脚本、数据库备份文件及文档说明,便于本地部署、调试与演示,能够为课题答辩和功能扩展提供参考基础。
1. 校园兼职系统为什么是毕业设计里的“安全牌”,zip里到底有什么
微信小程序+Java后端做校园兼职系统,是Web方向毕业设计里性价比很高的一个选题。小程序负责展示和交互,Java后端承接业务逻辑与数据读写,MySQL存下全部数据,三部分拼起来就是一个完整可演示的前后端分离项目。这个zip不是一堆零散代码,而是能直接落地的毕业设计交付物:小程序源码、Java后端源码、数据库脚本和说明文档都在压缩包里。适合软件工程、计算机科学、信息管理类专业的学生拿来部署、读代码、改业务,最终变成自己的设计。这类选题在java课程设计案例里出现频率也极高,网上能找到的参考很多,但能一次跑通的少,坑基本都藏在配置和联调环节。这篇笔记按“先立架构、再拆模块、后讲部署排查”的顺序展开,照着走,新手也能在半天内把它跑起来。
2. 先把架构立住:微信小程序、Java后端、MySQL各自在扛什么活
2.1 前端为什么选微信小程序,而不是H5或App
校园兼职的使用场景是“学生刷手机找活、商家发单管人”,最自然的发生地就是微信。小程序不用安装、扫码即用、在校园里传播成本低,这是它相比独立App最明显的优势。从开发角度看,小程序前端语法是WXML+WXSS,结构上接近HTML和CSS,有前端基础就能上手。官方开发者工具自带模拟器和调试器,编译、预览、真机调试都在一个界面里完成,没有原生App那套签名、打包、上架的流程,也不像H5要自己处理不同浏览器的兼容。
小程序相对H5最大的价值是微信登录态。wx.login拿到临时code,后端调微信接口换openid,用户体系就直接和微信账号绑定,不用自己设计手机号加验证码的注册登录流程。这也是近几年毕业设计普遍选小程序而不是H5的原因。如果目标是三端复用,可以去看uniapp方案,但对这套校园兼职系统来说,原生小程序代码更直白,答辩时每一行都能说出处,不用解释跨端框架的黑匣子。
2.2 Java后端为什么是Spring Boot这一套,工程目录怎么认
后端选Java,基本是Spring Boot,理由很实在。Spring Boot把Spring MVC繁琐的XML配置全省了,一个带main方法的启动类就能拉起内嵌Tomcat,产物是单一可执行jar包,部署和演示都省事。生态也成熟,MyBatis-Plus提供几乎全套的单表增删改查,分页插件一行配置就能用,源码里的工作量因此少一大截。微信官方文档里的接口示例大多也是Java,code2Session这类接口用Java调没有理解成本。动手改代码之前,花一两天补一下java基础里的集合、异常和注解用法,再看Spring Boot的请求处理流程,后面改代码会顺手很多。
这类系统的后端工程目录结构高度一致,打开zip里的后端目录,基本长这样:
campus-job-backend/ ├── src/main/java/com/example/job/ │ ├── controller/ # 接口入口:收参数、调Service、返回Result │ ├── service/ # 业务逻辑:状态流转、权限校验都在这层 │ ├── mapper/ # MyBatis-Plus 数据访问接口 │ ├── entity/ # 与数据库表一一对应的实体类 │ ├── config/ # 跨域、拦截器、分页插件配置 │ └── common/ # Result、异常处理等公共类 ├── src/main/resources/ │ ├── application.yml # 数据源、端口等核心配置 │ └── mapper/ # 自定义SQL的XML文件(如果用到) └── pom.xml这个分层结构本身就值得在答辩时讲一遍:controller只做参数接收和结果封装,service放业务规则,mapper只管数据读写。三层各司其职,就是企业里常说的分层思想。你看源码时按这个目录找文件,基本五秒就能定位一个接口的实现位置。
2.3 一次兼职报名的完整数据流
把系统当黑匣子看,用户看到的是“点一下报名按钮,状态变待确认”。实际链路是:wxml里报名按钮绑定bindtap,触发js里的wx.request,把jobId和token通过POST请求发到后端接口;后端Controller收到请求先做鉴权,从token里解析出当前用户,再调Service做业务校验,最后调Mapper写数据库;数据库写入成功后统一返回Result对象,小程序端拿到结果后setData刷新页面。
这条链路在答辩时几乎必问“一条数据是怎么走的”,建议顺着请求路径讲:从哪里发起、在哪一步鉴权的、在哪一步校验名额、失败时错误怎么回传。平时写代码顺手在Controller、Service、Mapper三层入口各打一条日志,真出问题时排查会快很多。这套系统本身就是经典的前后端分离项目实战,把这条数据流讲明白,比背十个技术名词更有说服力。
2.4 架构的边界:这套组合在什么情况下会不够用
要清楚这套方案能做什么、不做什么。它的优势是开发效率高、代码直白、演示效果好;边界在于并发和容错不是它的目标。校园兼职的峰值是中午和晚上课间,报名集中在一个小时内的量也就是几十上百次,单库单表完全扛得住。如果日后要做全校范围的抢单活动,那才需要引入Redis缓存、限流和消息队列,那是另一个层面的事了。毕业设计阶段,把单体应用的分层和状态流转讲透,比堆一个没跑通的微服务架构实用得多。
3. 把校园兼职拆成能答辩的模块:登录、发单、报名、审核
3.1 三种角色与微信登录:code换openid,再换自己的token
用户分三类:学生、商家(发单方)、管理员。学生的操作是浏览兼职、报名、看我的申请;商家发布兼职、查看报名者、录用或拒绝;管理员审核兼职、管理用户、看统计数字。user表里用role字段区分,建议用数字常量并在代码里注释清楚,0代表学生、1代表商家、2代表管理员。权限控制放在Service层做,而不是只在前端隐藏按钮——前端拦截只是为了体验,后端校验才是安全兜底,这个思路答辩时可以直接讲。
登录流程是标准的微信小程序实现:前端wx.login拿到临时code,POST到后端的login接口;后端用code调微信的code2Session接口,换取openid和session_key;拿openid去user表查用户,查到就生成token返回,查不到就自动创建用户再返回token。很多源码里没有注册页,就是因为微信登录天生把注册和登录合在一起。token用JWT生成比较省事,签发时把userId和role放进去,后续请求通过拦截器解析。小程序端拿到token后存到storage,每次wx.request带上Authorization头,嫌麻烦就封装一个request函数统一处理。
3.2 兼职信息流:列表分页、关键词搜索、详情展示
主页面是兼职列表,常见实现是顶部职位分类tab或搜索框,下方滚动分页加载。接口设计为GET /api/job/list,参数带pageNum、pageSize、keyword、categoryId、status,返回结果里除了列表还有total,小程序端根据total判断还能不能继续上拉加载。keyword用数据库LIKE即可,毕业设计不需要上ElasticSearch。分类来自job_category表,一般固定写死几个常用分类就够了:餐饮、家教、跑腿、促销、其他。
详情页展示兼职的完整信息:标题、分类、薪资、单位、工作时长、地点、招聘人数、已报名人数、发布人、发布时间、描述。页面底部固定报名按钮,点击前先判断是否登录、兼职是否处于招募中、当前用户是否已报名,前端先拦一遍。真正的校验还是要在后端再做一次,列表和详情字段同名时前端可以直接复用一套字段映射,减少联调时字段对不上的问题。
3.3 报名与状态流转:一张apply表撑起整套状态机
报名是这套系统的核心业务,数据落在apply表。字段至少包含id、job_id、user_id、status、create_time、update_time。status取值:0待确认、1已录用、2已拒绝、3已取消、4已完成。流程是:学生报名写一条待确认记录;商家在报名列表里操作,录用或拒绝;学生在录用前可以取消;录用并工作完成后标记已完成。这个状态机如果讲清楚,答辩能加不少分,因为很多人的设计只有两个状态,一被追问就露怯。
并发超招是常见翻车点。人数校验不能在Service里先查后改,两个用户同时查到剩余名额为1,就都会通过校验,最后实际报名人数超了。常见做法是条件更新,把校验和扣减合并成一条原子SQL,update影响行数为0就说明满了。同时apply表上加job_id、user_id的联合唯一索引,防止同一用户重复报名。这两个措施是这套系统里最值得拿出来讲的点,既简单又实用。
3.4 管理端:审核、统计、用户管理都在做什么
管理端常见做法是直接做在小程序里,管理员账号登录后多出一组页面。管理员看到待审核兼职列表,点通过或驳回,通过后兼职才会在学生端的列表里出现。统计页用几条COUNT和GROUP BY就能做:总用户数、兼职总数、报名总数、近七天报名趋势。这些查询直接在Mapper里写SQL注解就行,不需要单独报表组件。管理员账号用SQL直接insert,或者在user表里手动改role字段,源码里通常已经有初始化SQL。
审核流程如果觉得“发布即上架”更简单,也可以砍掉审核环节,把管理员改成只做统计和下架。但保留审核有一个好处:演示时可以多展示一个业务动作,而且能回答“平台怎么保证兼职信息的可信度”这类问题。多数毕业设计源码会保留审核,因为工作量不大,但答辩时能讲的点多一个。
3.5 小程序端页面清单与目录约定
这套源码的小程序端目录结构,基本不出以下几种页面:pages/index兼职列表、pages/detail兼职详情、pages/publish发布兼职、pages/my个人中心、pages/apply我的报名、pages/manage管理入口。前端页面和接口的对应关系,翻一遍app.js里的globalData.baseUrl,再对照pages下每个js文件里的wx.request,半小时就能把整个项目接口地图画出来。
页面如果用了自定义导航栏,顶部导航栏高度在不同机型上差异很大,别写死成固定px。可靠做法是拿胶囊按钮的位置信息往上推算状态栏高度,或者直接用官方提供的胶囊信息做适配。这套系统作为完整的微信小程序项目实例,页面多但结构统一,照着其中一个页面改分类、改字段,其他页面照抄即可。
4. 让数据库和后端先跑起来:建表SQL、接口约定与启动配置
4.1 先建库建表:四张核心表怎么设计
打开zip里的sql脚本,核心就是四张表:用户表、分类表、兼职表、报名表。表结构质量直接决定后面写代码顺不顺。下面这份SQL是这类系统最常见的设计,字段命名尽量用英文,和实体类保持一致,避免在代码和SQL之间来回翻译:
CREATE DATABASE IF NOT EXISTS campus_job DEFAULT CHARACTER SET utf8mb4; USE campus_job; CREATE TABLE `t_user` ( `id` INT NOT NULL AUTO_INCREMENT, `openid` VARCHAR(64) NOT NULL COMMENT '微信openid', `nickname` VARCHAR(50) DEFAULT '' COMMENT '昵称', `avatar` VARCHAR(255) DEFAULT '' COMMENT '头像地址', `phone` VARCHAR(20) DEFAULT '' COMMENT '联系电话', `role` TINYINT NOT NULL DEFAULT 0 COMMENT '0学生 1商家 2管理员', `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_openid` (`openid`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表'; CREATE TABLE `t_job_category` ( `id` INT NOT NULL AUTO_INCREMENT, `name` VARCHAR(20) NOT NULL COMMENT '分类名', `sort` INT DEFAULT 0 COMMENT '排序权重', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='兼职分类表'; CREATE TABLE `t_job` ( `id` INT NOT NULL AUTO_INCREMENT, `title` VARCHAR(100) NOT NULL COMMENT '兼职标题', `description` TEXT COMMENT '兼职描述', `category_id` INT NOT NULL COMMENT '所属分类', `salary` DECIMAL(10,2) NOT NULL COMMENT '薪资', `salary_unit` VARCHAR(10) DEFAULT '时薪' COMMENT '时薪/日薪/月薪', `address` VARCHAR(200) DEFAULT '' COMMENT '工作地点', `headcount` INT NOT NULL DEFAULT 1 COMMENT '招聘人数', `applied` INT NOT NULL DEFAULT 0 COMMENT '已报名人数', `status` TINYINT NOT NULL DEFAULT 0 COMMENT '0待审核 1招募中 2已满 3已结束 4已下线', `publisher_id` INT NOT NULL COMMENT '发布人', `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `idx_status` (`status`), KEY `idx_publisher` (`publisher_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='兼职信息表'; CREATE TABLE `t_apply` ( `id` INT NOT NULL AUTO_INCREMENT, `job_id` INT NOT NULL COMMENT '兼职ID', `user_id` INT NOT NULL COMMENT '报名人ID', `status` TINYINT NOT NULL DEFAULT 0 COMMENT '0待确认 1已录用 2已拒绝 3已取消 4已完成', `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP, `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_job_user` (`job_id`, `user_id`), KEY `idx_user` (`user_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='报名表';字段设计里的几个关键点:t_user的openid加唯一约束,因为微信登录以openid为身份依据,重复了会出大问题;t_job的salary用DECIMAL存金额,float精度不够;headcount和applied两个字段配合实现名额控制;t_apply的uk_job_user联合唯一索引是防止重复报名的最后一道保障,业务代码漏判时数据库还能兜底。create_time全部用默认值,代码里不手动传时间。
4.2 统一返回体和接口清单:前端拿数据的第一约定
后端所有接口返回统一结构,前端才好写公共处理逻辑。Result类封装code、message、data三个字段,接口正常返回200,业务失败返回约定错误码。前端request函数里先判断code再处理业务,HTTP状态码只管连通性,不承载业务语义——这是前后端分离项目的基本约定,答辩时被问到“接口怎么设计的”能答出这一层是加分项。
这类源码的接口清单基本是固定的,核心接口如下:
| 功能 | 方法 | 路径 | 说明 |
|---|---|---|---|
| 登录 | POST | /api/login | 参数为code,返回token和用户信息 |
| 兼职分页列表 | GET | /api/job/list | 参数pageNum/pageSize/keyword/categoryId/status |
| 兼职详情 | GET | /api/job/{id} | 返回完整兼职信息加当前用户报名状态 |
| 发布兼职 | POST | /api/job/publish | 商家身份,参数为兼职信息JSON |
| 报名兼职 | POST | /api/job/apply | 参数jobId,报名当前登录用户 |
| 处理报名 | POST | /api/job/handle | 商家操作,参数applyId和action |
| 我的报名 | GET | /api/apply/list | 当前用户的所有报名记录 |
| 审核兼职 | POST | /api/admin/audit | 管理员操作,参数jobId和action |
接口路径用restful风格命名,资源用名词,动作交给HTTP方法。多看几遍这个清单,对照着后端controller目录找代码,整个系统的脉络就清楚了。
4.3 Controller写法与分页:一个能直接改的骨架
Controller保持薄,只做参数接收和返回封装,业务细节放Service里。下面这段骨架代码在处理分页和动态条件查询时很典型:
@RestController @RequestMapping("/api/job") public class JobController { @Autowired private JobService jobService; @GetMapping("/list") public Result<PageResult<JobVO>> list( @RequestParam(defaultValue = "1") int pageNum, @RequestParam(defaultValue = "10") int pageSize, @RequestParam(required = false) String keyword, @RequestParam(required = false) Integer categoryId, @RequestParam(required = false) Integer status) { return Result.ok(jobService.pageList(pageNum, pageSize, keyword, categoryId, status)); } @GetMapping("/{id}") public Result<JobVO> detail(@PathVariable Long id) { return Result.ok(jobService.getDetail(id)); } @PostMapping("/publish") public Result<Void> publish(@RequestBody JobDTO dto) { jobService.publish(dto); return Result.ok(null); } @PostMapping("/apply") public Result<Void> apply(@RequestBody ApplyDTO dto) { jobService.apply(dto.getJobId()); return Result.ok(null); } }几个参数细节说明:@RequestParam加了defaultValue="1",前端不传pageNum时不会报错;keyword和categoryId用required=false,前端可以不传,Service里判断为空就跳过这个条件;@RequestBody接收JSON对象,前端wx.request里要设置content-type为application/json并传JSON字符串。Service返回的PageResult包含list和total两个字段,total用来给前端判断是否还有下一页。MyBatis-Plus里new Page(pageNum, pageSize),配合分页插件会自动生成COUNT语句,这是这类源码里最通用的分页写法。
4.4 启动配置:三个最常写错的地方
配置里最常出问题的三个点:数据源URL参数、跨域配置、拦截器放行。缺一个,系统就跑不顺畅。
server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/campus_job?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai username: root password: yourpassword driver-class-name: com.mysql.cj.jdbc.Driver mybatis-plus: configuration: map-underscore-to-camel-case: trueURL里的useUnicode和characterEncoding缺一个,中文就乱码;serverTimezone缺了,时间差八小时。这两个是经典坑,后面排查章节会具体展开。数据库连接池用HikariCP默认配置即可,这是Spring Boot内置的连接池,不需要额外调参。
跨域配置同样是必写项,小程序端域名和后端端口不同,浏览器和安全策略会拦截跨域请求。常见做法是加一个CorsFilter:
@Configuration public class CorsConfig { @Bean public CorsFilter corsFilter() { CorsConfiguration config = new CorsConfiguration(); config.addAllowedOriginPattern("*"); config.addAllowedHeader("*"); config.addAllowedMethod("*"); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", config); return new CorsFilter(source); } }开发期放开所有来源没问题,但答辩时如果被问安全,能说出“开发期放开、上线收紧”就是加分回答。拦截器里要放行login接口和兼职列表这种公开接口,其他接口校验Authorization头里的token。漏放行会导致登录接口本身被拦截,一登录就401,排查时最容易懵。
4.5 启动顺序:先底座、再后端、最后前端
整套系统启动顺序有讲究,从上到下一步一步来:
# 1. 导入数据库 mysql -u root -p campus_job < campus_job.sql # 2. 启动后端 cd campus-job-backend mvn spring-boot:run # 看到 Started ... 就是启动成功,先测 http://localhost:8080/api/job/list # 3. 微信开发者工具导入小程序目录 # 修改 app.js 里的 baseUrl 为 http://localhost:8080 # 右上角 详情 -> 本地设置 -> 勾选"不校验合法域名"顺序不能乱。先确认数据库有表有数据,再启后端,最后联调前端。如果后端日志报数据库连不上,优先检查yml里的账号密码和库名;如果前端报网络错误,优先检查baseUrl和合法域名校验。按这个顺序排查,十分钟内能定位大多数问题。zip里的说明文档一般会把JDK版本和Maven版本写清楚,先看说明再动手,能省很多环境上的折腾。
提示:前端联调时建议先用Apifox或Postman把全部接口跑一遍,确认入参出参符合预期,再切到小程序端联调。接口层的黑匣子先打开,页面报错时就能直接判断是前端问题还是后端问题。
5. 避坑与排查:这套系统最常见的五个翻车点
这一章写在本地跑通和站上演示台之间。下面五个问题是我反复见到的典型翻车点,按“现象、原因、解决”三段式记录,方便直接对照。每一条的解决方式都经过实践验证,照着改就能恢复。
5.1 模拟器一切正常,真机预览所有请求全失败
现象:开发者工具里列表加载正常、登录没问题,用手机扫码预览后,页面空白或弹出“url不在合法域名列表中”。这是真机预览环节第一个翻车点,很多人以为是代码问题,其实和后端没关系。
原因:微信在真机上强制校验wx.request的合法域名,localhost本来就不在正式域名名单里;开发者工具默认跳过该校验,所以两边表现不一致。本质是微信的安全策略,不是代码bug。
解决:演示用开发者工具时,在“详情-本地设置”里勾选“不校验合法域名”即可;真机演示需要在小程序后台配置request合法域名,但那要求域名是HTTPS且经过ICP备案,校园网环境往往不具备。最省事的做法是手机打开小程序时开启“调试”模式,绕过域名校验;答辩演示直接用开发者工具的模拟器,提前把窗口布置好。这个坑踩过一次就有教训了,最好的办法是提前确定演示设备。
5.2 中文变问号:JDBC连接串少了两个参数
现象:数据库表里用SQL查中文正常,但接口返回给小程序的数据里,中文全部显示为??。也有人是反向的:接口返回正常,但管理端写入的数据进了库就变成问号。
原因:JDBC连接串缺了编码参数,Java和MySQL之间以错误字符集传输,utf8mb4的数据被当成latin1处理。MySQL的character_set_server如果是utf8mb4,但连接层面没指定,驱动就按默认字符集往返。
解决:在application.yml的url里补上useUnicode=true和characterEncoding=utf8,两个参数一起写,重启后端。建库时用default charset utf8mb4。如果数据库里已经存进了问号数据,别想着转换,那批数据已经废了,删掉重新导入SQL脚本。顺手检查characterEncoding参数拼写的血泪经验:不是charEncoding,少一个缩写都不生效。
5.3 后端时间比本地慢八个小时
现象:新发布的兼职创建时间显示成前一天,或报名记录的update_time对不上本地时间。排查时发现数据库里存的时间本身就是错的。
原因:默认时区是UTC,北京时间是UTC+8,JDBC连接串没指定时区,驱动按服务器默认时区读写,整体偏移了八小时。MySQL驱动8.0及以上版本对时区更敏感,不配时区甚至直接报错。
解决:url末尾加serverTimezone=Asia/Shanghai,重启后端再测试。连接串已经加了这个参数还不对,检查MySQL的default-time-zone参数和操作系统时区。日期字段展示时,建议直接用后端返回的字符串,前端不要再toLocaleString转一遍,很容易转出双重偏移。演示时如果时间不对,被老师看到会影响印象分,这条务必提前验。
5.4 报名超招:先查后改的并发漏洞怎么补
现象:兼职招聘5人,结果有6个人报名成功,最后一个人名额是硬塞进去的。单机演示不容易复现,但并发请求一多就翻车。
原因:Service里先SELECT查剩余名额,再UPDATE写入报名数据,两步之间其他请求插进来,读到的都是同一个旧值。这就是典型的先查后改并发问题,代码看着逻辑对,实际并发下不堪一击。
解决:把校验和扣减合并成一条原子SQL,让数据库自己判断名额:
-- 报名时优先扣减名额,满足条件才更新成功 UPDATE t_job SET applied = applied + 1 WHERE id = #{jobId} AND applied < headcount AND status = 1;如果这条SQL影响行数为0,说明兼职已满或状态不允许,直接返回业务错误。MyBatis-Plus里我一般用@Update注解写在Mapper接口上,避免单独建XML。配合apply表的(job_id, user_id)联合唯一索引,同一用户重复报名在数据库层直接报错,业务代码再捕获异常提示“请勿重复报名”。这两个改动都是几行的事,但能让系统经得起追问。
5.5 图片上传成功却404:路径映射与存储约定
现象:上传接口调用成功,图片路径也写进了数据库,但小程序端img标签加载时404。控制台看到请求的是一个本地磁盘路径,前端根本访问不到。
原因:图片保存到了后端服务器的本地磁盘目录,但后端没有配置静态资源映射,或者前端拼的URL前缀和后端不一致。源码里常见的通病是把完整本地绝对路径存进数据库,换一台电脑部署,路径全裂,图片全挂。
解决:后端配置静态资源映射,把本地存储目录映射成/upload/**访问路径:
@Configuration public class WebConfig implements WebMvcConfigurer { @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/upload/**") .addResourceHandler("file:" + uploadDir + "/"); } }存入数据库时只存相对路径,比如/upload/avatar/xxx.jpg,前端展示用baseUrl加相对路径拼接。千万别把完整本地绝对路径存库,这不光是404问题,换环境部署时全系统图片一起崩。如果老师要求部署到服务器,这个设计能省掉一半的搬迁时间。
6. 从能跑到能讲:六分钟演示脚本与三个加分项
6.1 六分钟演示脚本
演示不要临场发挥,按固定脚本走。建议准备两个账号:一个学生、一个商家,提前在小程序里登录好。六分钟的时间分配可以参考:
| 时间 | 演示环节 | 讲解要点 |
|---|---|---|
| 第1分钟 | 项目介绍 | 说清小程序、Java后端、MySQL三个组成部分 |
| 第2分钟 | 登录流程 | 进入页面自动登录,讲code换openid的过程 |
| 第3分钟 | 浏览与报名 | 兼职列表、详情、报名,讲状态如何流转 |
| 第4分钟 | 商家操作 | 切换商家账号,发布兼职、查看报名、录用 |
| 第5分钟 | 管理端 | 审核兼职,展示统计数据 |
| 第6分钟 | 数据库验证 | 现场查一条apply记录,对应到代码位置 |
6.2 答辩前自检清单
- 后端能启动、数据库能连、核心接口在Apifox里全部测过一遍
- 小程序能从登录走到报名闭环,中途无报错
- 能不看代码说出一条报名记录的状态流转路径
- 能在数据库里用SQL查出刚产生的报名记录
- 两个角色账号密码都写在纸上,放在手边
6.3 三个低成本加分项
如果时间充裕,可以加三个成本不高但出效果的功能。第一是订阅消息,报名被录用或拒绝后通过微信订阅消息通知学生,两周内能做完,演示时很显眼。第二是定时任务,写一个带@Scheduled注解的方法,每天凌晨把已过期的兼职状态自动改成“已结束”,一个方法就够,但能答出“系统自动维护数据”这个点。第三是统计图表,管理端引入ECharts组件绘制近七天报名人数柱状图,数据来源就是一条GROUP BY日期的SQL,几分钟就能接好。
我自己的习惯是先用Apifox把所有接口过一遍,再按上面的脚本完整演练两遍,每个环节的页面截图留好。截图比直播演示更保险,万一现场网络波动,还能切到截图继续讲。上面这些坑我基本都踩过,提前补好比现场补救轻松得多,希望帮到你。
本文还有配套的精品资源,点击获取