校园求职微信小程序系统开发:Spring Boot全栈实践

发布时间:2026/9/7 19:33:03
校园求职微信小程序系统开发:Spring Boot全栈实践 1. 项目背景与需求拆解1.1 “校园求职”为什么值得单独做一个系统做这个项目之前我一直在想一个事市面上招聘平台已经够多了Boss直聘、智联、前程无忧为什么还要搞一个“校园版”后来真正调研了一圈学校里的场景发现校园求职和社招完全是两码事。校园求职的核心矛盾不是“信息不够多”而是“信息太杂、匹配太差、反馈太慢”。企业来学校开宣讲会学生提前一个小时排队进场听完HR念PPT再散场现场收的纸质简历堆成小山后续全靠HR手动筛。另一边学生海投一百份简历可能只有两三个回复学校就业指导中心想跟进每个学生的求职进度也缺少一个抓手。用微信小程序来承载这个系统逻辑非常顺。学生群体没有装独立App的习惯但微信是每天睁眼闭眼都要开的。一个小程序点开即用不需要下载安装用完也不用卸载。校园求职本身是低频但强需求的行为——大三找实习、大四校招、毕业两年内可能还有春招秋招把这些场景收进小程序里使用成本几乎为零。这个项目适合谁拿去做参考三类人一类是计算机相关专业的学生拿来做毕业设计或者课程设计技术栈覆盖前后端全流程工作量足够展示能力第二类是想搞校园创业或者接外包的开发者这套需求模型可以直接改造成商业项目的初始版本第三类是学校就业部门的信息化建设人员用这个东西当需求底稿拿去和软件公司对接心里更有数。1.2 核心用户的真实痛点不只是“找工作”我把用户拆成三类角色学生、企业HR、系统管理员学校就业中心。每类角色的痛点是完全不同的。学生侧最大的痛点是信息过载和投递无反馈。他们需要的是快速筛选出“专业对口、年级匹配、地点可接受”的岗位投完简历能知道企业有没有看过、有没有进面试流程而不是投完石沉大海。企业HR侧的痛点则是简历筛选效率太低。校园招聘收到的简历量大且格式五花八门PDF、Word、图片各种格式混在一起HR需要花大量时间做格式整理。如果系统能提供统一格式的在线简历并且按岗位、按专业做初步筛选标记效率能提升一大截。学校管理员侧关注的是数据汇总和流程监管。学生签约情况怎么样哪些专业的就业率偏低哪些企业常年进校招聘但实际签约率不高这些数据如果靠Excel来回传统计一次要半个月。系统里只需要做几个报表页面数据实时呈现。明确了这三类角色的痛点后续的设计就不会跑偏。我不打算做一个功能大而全、但每个功能都用不上的“花架子”而是把精力集中在招聘信息流转这条主线上企业发职位、学生投简历、HR筛选沟通、学校看数据。1.3 为什么用微信小程序而不是网页或者App这个问题几乎每次汇报都会被问到。我的回答永远是看用户习惯。校园场景里的学生手机里装几十个App很正常但真正每天打开的可能就那么几个。指望学生为了找一个实习岗位专门下载一个App并且注册完善简历——这个路径太长流失率极高。网页版其实也不是不行但校园求职有一个天然场景老师往班级群里转发一条招聘信息学生点开是网页链接的话操作路径是“打开链接→注册→完善简历→投递”每一步都在流失。小程序的价值在于“微信生态内闭环”。老师转发的可以是一个小程序卡片学生点开直接进入系统第一次使用用微信手机号快捷登录简历填过一次就存在云端企业HR收到的面试邀约会通过微信服务通知推送到学生手机上。这个路径短到让用户没有流失的机会。另外微信小程序自带社交传播属性。好的岗位信息学生可以直接转发给同学企业宣讲会信息可以在群里快速扩散这种裂变效果是独立App和网页做不到的。技术层面小程序开发本身用的是类Vue语法的框架前端门槛低对后端技术没有任何限制只要提供HTTP接口什么语言都行。我用的就是Java Spring Boot在后面章节会详细讲技术选型的考量。2. 系统架构与技术选型思路2.1 整体架构一个小程序项目应该分几层校园求职系统看起来不是一个很大的工程但如果一上来就不分层所有代码堆在一起写后面加功能、改Bug会非常痛苦。我设计的分层思路是这样的用户表现层小程序端、统一接入层网关/拦截器、业务处理层服务模块、数据存储层MySQLRedis。各层之间单向依赖不允许跨层调用。表现层就是微信小程序本身负责页面渲染和用户交互。这里面有一个原则小程序端不直接操作数据库所有数据都通过HTTP请求后端接口获取。业务处理层按照模块拆分用户模块、招聘模块、简历模块、消息模块、统计模块。每个模块内部按Controller-Service-Mapper三层处理Controller只做参数接收和结果封装Service写核心业务逻辑Mapper跟数据库打交道。Redis在整个系统里承担三件事登录态token缓存、热门职位列表缓存、验证码临时存储。MySQL承载全部持久化数据。这种分层结构的好处是任何一个模块出问题定位范围会被限制住。比如用户反馈简历保存失败我只需要去查简历模块的Service层日志不用去招聘模块翻代码。联调阶段第三方接口出问题比如微信登录凭证校验也可以快速隔离开。2.2 后端为什么选Spring Boot有哪些不可替代的优势Java和Spring Boot在整个后端领域的生态地位不用我多说。校园求职系统涉及的典型Web开发场景——RESTful接口、拦截器、参数校验、数据库事务、定时任务——Spring Boot都有成熟的解决方案而且是开箱即用型的不需要从零手写。选择Spring Boot还有一个很重要的原因兼容性和资料丰富度。这个项目学生搞的毕业设计普遍需要用到Java技术栈如果你去接外包客户公司不管用什么技术Spring Boot这套东西拿过去基本都能跑。出了问题搜索引擎一搜一大堆答案不会卡在一个小众框架的文档上。核心依赖版本我建议这样配Spring Boot 2.7.x稳定踩坑少MyBatis-Plus 3.5.x省去写大量XMLMySQL 8.0以上Redis 6.x。Java环境用JDK 8或11都行Spring Boot 2.7系列向下兼容两种。用MyBatis-Plus而非原生MyBatis的原因很现实单表CRUD操作占了这个小项目的60%以上手写所有SQL浪费时间又不安全。MyBatis-Plus的BaseMapper提供了通用的增删改查方法复杂查询只需要写少量的自定义SQL或者用LambdaQueryWrapper代码量能砍一半。提示Spring Boot 3.x已经发布但如果你用的是JDK 8还是老实选2.7.x别给自己找麻烦。2.3 数据库选型和核心表设计思路MySQL做持久化存储Redis做缓存。这个组合打校园级别的并发绰绰有余。数据库命名我用的是校园求职系统的拼音首字母简写简写加模块名的形式比如表job_recruit招聘职位表、job_resume简历表、job_delivery投递记录表。字段命名统一用下划线风格加注释不加注释的字段后续维护就是灾难。各表关系不复杂用户表里用role字段区分学生、企业HR、管理员三种角色。企业表存公司的基本信息HR表关联到企业。职位表关联企业表简历表关联学生用户表投递记录表同时关联职位表和简历表。后面第3章会展开讲每张表的字段明细和设计动机。3. 数据库模型与核心表结构详解3.1 用户、企业、简历三张基础表的字段设计用户表sys_user字段名类型说明idbigint主键openidvarchar(64)微信openid唯一nicknamevarchar(50)微信昵称avatar_urlvarchar(255)头像地址phonevarchar(20)手机号roletinyint1学生 2企业HR 3管理员statustinyint1正常 0禁用create_timedatetime注册时间Openid是小程序登录体系里的关键字段每个微信用户对同一个小程序有唯一的openid。这个字段做唯一索引第一次登录时写入以后靠它识别老用户。企业表ent_company字段包括企业名称、统一社会信用代码、行业类型、企业规模1-20人、20-99人、100-499人、500人以上、融资阶段初创型、成长型、成熟型等、办公地址、企业LOGO、企业简介、营业执照图片。这个表在注册审核时需要管理员校验所以多一个audit_status字段0待审核、1通过、2驳回。简历表resume_info简历表是学生端最核心的表。字段包含基本信息姓名、性别、出生日期、手机、邮箱、教育经历学校、专业、学历、毕业年份、求职意向期望岗位、期望城市、期望薪资、技能标签、项目经历、实习经历、自我评价。一个学生可以维护多份简历比如针对不同类型岗位做不同版本所以用user_id关联不唯一。3.2 招聘职位与投递记录业务主链路的表结构招聘职位表job_recruit字段名类型说明idbigint主键company_idbigint关联企业表job_titlevarchar(100)职位名称job_categoryvarchar(50)职位分类技术/产品/运营/设计/市场/其他job_typetinyint1全职 2实习 3兼职work_cityvarchar(50)工作城市salary_minint薪资下限单位Ksalary_maxint薪资上限单位Keducation_reqvarchar(20)学历要求recruit_numint招聘人数job_desctext职位描述is_hottinyint是否热门推荐is_expiredtinyint是否已过期create_timedatetime发布时间职位列表是学生最常刷的页面查询条件多城市、职位类别、职位类型、学历要求、薪资区间排序方式也有时间和薪资两种。这块查询用LambdaQueryWrapper动态拼接条件不需要写复杂SQL。投递记录表job_delivery投递记录是整个系统里面最需要认真设计的一张表因为它关联学生、职位、简历三方的状态。字段包括id、resume_id投的是哪份简历、job_id投的什么职位、student_id、company_id、status1待查看、2已查看未处理、3已通过、4已拒绝、create_time。加company_id做一个反规范化冗余是为了避免每次查投递列表都要join企业表虽然MySQL join性能不差但能省就省。初始状态是1投递成功HR查看后在后台操作变为已通过或已拒绝学生端实时看到更新结果。3.3 收藏、消息通知与系统管理辅助表收藏功能做了一张独立的job_favorite表存student_id、job_id和收藏时间。为什么要单独建表而不是在职位表里加一个收藏数因为收藏数可以靠统计记录得出但用户查看“我收藏了哪些职位”需要可逆反查单独建表最清晰。消息通知表user_message记录三类消息投递状态变动、面试邀约、系统公告。字段包括user_id接收人、message_type、content、is_read、create_time。首页做一个红点提醒功能靠查未读消息数。管理员后台还涉及一个登录日志表login_log和操作日志表oper_log记录谁在什么时间干了什么。这个小项目不做复杂的审计系统两张表够用。4. 后端核心接口设计与登录态方案4.1 微信登录流程从code到openid再到token小程序的登录认证流程本质上是由微信官方介入的三方认证。核心逻辑是小程序端调用wx.login()拿到临时凭证code。将code发送到后端自定义接口/api/login。后端拿code AppID AppSecret请求微信官方接口https://api.weixin.qq.com/sns/jscode2session。微信返回openid和session_keysession_key是解密用户手机号等敏感信息的密钥一般不做持久化。后端查库确认openid是否已注册未注册则静默创建用户。生成自定义token用UUID或者JWT存入Redis设置有效期为7天。返回token和用户信息给小程序端。这里有几个细节值得注意。第一小程序端的AppSecret绝对不能放在前端所有人用小程序的代码包可以反编译拿到只要AppSecret泄露别人就可以冒充你的小程序做任何操作。AppSecret只能保存在后端服务器。第二自己生成的token而不是直接用openid作为凭证好处是有过期机制可以主动踢人下线而且不暴露微信内部标识。第三Redis里token过期时间和小程序端的storage清理逻辑要做配合不能出现服务端已过期但前端还拿旧token请求的情况。4.2 拦截器与统一状态码规范整个后端的接口风格是RESTful 统一返回格式。所有正常返回的数据结构是{ code: 200, message: success, data: {} }错误情况返回类似结构code为非200message为具体错误描述。前端请求封装层收到响应后统一判断code实现全局的错误提示不需要每个页面都写一遍wx.showToast。登录态校验通过一个拦截器完成。拦截器会放行白名单接口登录接口、获取职位列表、职位详情等公开接口其余接口要求请求头中携带token字段。校验流程从Header取token。查询Rediskey不存在说明未登录或已过期直接返回401。校验通过后从Redis中取出用户信息放入ThreadLocal。Service层直接通过UserContext.getUserId()获取当前用户不需要每个方法都传userId参数。这样做的好处是后续开发新接口时只需要关注业务逻辑本身登录态的问题拦截器已统一处理不用在每个Controller里重复写获取用户信息的代码。4.3 职位列表分页与条件筛选的后端实现逻辑职位列表页是学生最常用的功能性能和体验直接决定整个系统的好感度。接口定义为GET /api/jobs?page1size10citycategorytypesalarykeyword。后端处理分两步第一步用LambdaQueryWrapper拼接查询条件第二步用MyBatis-Plus的Page对象做分页。LambdaQueryWrapperJobRecruit wrapper new LambdaQueryWrapper(); wrapper.eq(JobRecruit::getIsExpired, 0) // 只查未过期职位 .eq(StringUtils.hasText(city), JobRecruit::getWorkCity, city) .eq(StringUtils.hasText(category), JobRecruit::getJobCategory, category) .eq(type ! null, JobRecruit::getJobType, type) .ge(salaryMin ! null, JobRecruit::getSalaryMin, salaryMin) .like(StringUtils.hasText(keyword), JobRecruit::getJobTitle, keyword) .orderByDesc(JobRecruit::getIsHot) .orderByDesc(JobRecruit::getCreateTime); PageJobRecruit page new Page(page, size); jobRecruitMapper.selectPage(page, wrapper);对薪资的处理做了一点小优化前端传递薪资下限后端用salary_min 入参查询保证学生的预期薪资范围不会因为比实际略高而被过滤掉。比如学生填了8K系统会匹配薪资下限不高于8K的职位而不是薪资下限正好8K的职位这样结果更合理。4.4 简历投递的防重复与事务控制投递接口作为核心写操作需要考虑两件事防止重复投递和保持数据一致性。防重复的逻辑很简单投递前先查job_delivery表里是否存在同一学生同一职位的记录。这里直接给表加一个唯一索引(student_id, job_id)最可靠代码里即使有并发问题数据库索引也能兜底。数据一致性涉及两个层面的写操作插入投递记录同时更新职位表里的投递数delivery_count 1。这两个操作必须在一个事务里否则会出现投递记录插入成功但统计没加上去的脏数据。Spring的Transactional注解就能实现。注意事务不是越多越好只有写操作才需要。查询接口加事务只会白白增加数据库连接占用。4.5 消息推送服务微信订阅消息的接入要点在微信公众平台开通订阅消息功能选择模板。后端在用户投递成功时手动触发一次订阅消息授权请求小程序端通过wx.requestSubscribeMessage让用户确认。用户授权后后端拿到用户openid推送面试结果通知。订阅消息的坑在于一次性订阅消息用户每授权一次只能推送一条。如果希望反复推送需要在每次推送前重新引导用户授权。这个场景下我做了个折中方案投递成功时引导学生订阅“投递结果通知”HR操作后推送如果用户没有授权则只在站内消息里展示结果。5. 小程序前端页面设计与关键实现5.1 页面架构底部Tab和功能页划分小程序端采用四个底部Tab首页、职位、消息、我的。首页展示轮播图、热门职位推荐、快捷分类入口职位页是筛选列表消息页是通知中心我的页面包含个人信息、我的简历、投递记录、收藏夹等子功能。页面跳转关系上小程序原生导航搭配自定义组件。有一个地方值得说明职位详情页、企业详情页、简历编辑页这三个高频页面都用了navigationStyle: custom自定义导航栏为了适配不同手机的顶部状态栏高度用了微信官方API查询状态栏高度后做动态占位。这个细节很烦但很值——不做的话iPhone X和普通安卓机上页面顶部会错位。5.2 封装request请求统一处理登录过期和错误提示小程序原生的wx.request能力太少直接裸用会导致每个页面都要写一堆重复代码。我的做法是封装一个request.js工具模块const request (url, method, data) { const token wx.getStorageSync(token) return new Promise((resolve, reject) { wx.request({ url: BASE_URL url, method, data, header: { Content-Type: application/json, token: token }, success: (res) { if (res.data.code 200) { resolve(res.data.data) } else if (res.data.code 401) { // 登录过期清理缓存并跳转登录页 wx.removeStorageSync(token) wx.navigateTo({ url: /pages/login/login }) } else { wx.showToast({ title: res.data.message, icon: none }) reject(res.data) } }, fail: (err) { wx.showToast({ title: 网络异常, icon: none }) reject(err) } }) }) }封装之后页面里调接口只需要一句const list await request(/api/jobs, GET, params)错误处理全部走统一分支代码清爽很多。注意如果页面里用了wx.showLoading封装层每次请求前最好也统一处理避免接口快了loading没消失的闪烁问题。5.3 职位列表页下拉刷新、触底加载、防抖搜索职位列表页在交互上的核心诉求是信息密度大、筛选便捷、滑动流畅。列表用scroll-view或页面自带滚动实现为了性能我选择了页面滚动加上onReachBottom触底加载。数据分页大小是10条一页每一次触底请求下一页追加进数组。下拉刷新用微信原生的enablePullDownRefresh重置页面从第一页加载。搜索框使用bindinput监听输入但要加防抖。因为小程序每次输入都会触发重新渲染如果用户连续输入“前端开发”四个字每敲一个字都发一次请求用户体验和服务器压力都是灾难。我写了一个300ms的定时器用户停止输入后才发起请求。筛选条件组件用了van-dropdown-menuVant Weapp支持城市、职位类别、薪资范围三个维度联动筛选。筛选条件变化时重置分页再次搜索。5.4 简历编辑表单校验和实时保存简历编辑页是所有页面里逻辑最复杂的因为字段多、类型多。表单组件分成基础信息、教育经历、求职意向、项目经历四个区块。教育经历和项目经历是动态列表用户可以新增多条记录。表单校验在提交时统一触发。例如手机号11位校验、邮箱格式校验、期望起薪不能大于期望顶薪。校验失败会在对应字段下面红字提示并且滚动定位到第一个错误字段。关于保存策略我做了自动保存用户点击“保存”才提交整个表单离开页面时如果检测到未保存内容弹窗提示。这比实时保存更容易控制不会频繁触发接口也不容易产生数据错乱。5.5 权限控制不同角色看到不同的菜单和页面小程序前端的权限控制依赖登录时后端返回的角色信息前端根据role字段判断渲染内容。学生端能看职位、投简历、管理个人简历、收消息。HR端看不到职位浏览和投递功能取而代之的是“我的招聘”模块——管理职位发布、查看收到的简历列表、处理投递状态。管理员端有用户管理和数据统计入口。页面路由的权限拦截有两种层级一种是通过TabBar入口控制不同角色登录后加载不同的TabBar配置另一种是页面级别的拦截在每个页面的onLoad里检查角色不匹配就重定向回首页。这样即便用户通过链接强行打开无权限页面也会被拦回来。6. 从开发到上线的完整流程与踩坑实录6.1 本地开发环境的搭建顺序开发这个项目需要准备的东西按顺序列一下JDK 8 Maven 3.6用于后端编译打包。MySQL 8.0本地建库建用户导入SQL脚本。Redis 6.xWindows下用Memurai或者Docker跑RedisLinux直接用redis-server。HBuilderX或者微信开发者工具用于小程序端开发和调试。一个测试用的AppID。个人开发者可以申请测试号开发阶段完全够用如果想要完整的登录、支付功能还是需要注册企业主体小程序。后端启动之前需要修改application.yml里的数据库账号密码、Redis地址、微信AppID和AppSecret。然后跑mvn spring-boot:run启动服务连上微信开发者工具把本地请求地址改成http://localhost:8080。这里的坑微信开发者工具默认不允许访问HTTP非安全域名需要勾选“不校验合法域名”选项开发期可以偷懒上线前必须配合法域名。6.2 后端接口的单元测试与Postman联调开发阶段我习惯用Postman先测接口再和小程序联调。接口写完先跑一遍冒烟测试参数正常、参数缺失、参数非法、未登录访问四种场景各过一遍。不要写完了就扔给前端前端连完返回来一堆Bug来回沟通成本很高。写单元测试的时候Spring Boot自带的SpringBootTest配MockMvc就够用不需要引入太复杂的测试框架。核心测试点集中在登录接口、职位查询分页、简历投递防重复、消息推送这几条链路上。6.3 真机预览与上线发布时必须注意的配置从开发者工具到真机预览再到正式上线有三个配置项最容易出问题域名合法性问题。生产环境的小程序只能请求HTTPS域名而且域名必须先在小程序管理后台配置请求白名单。我见过好几个项目在这个环节卡了一两天——接口明明通了真机上一请求就报“域名不合法”。解决办法是提前把域名备案、申请SSL证书、配置好Nginx反向代理然后在微信公众平台-开发管理-服务器域名里添加。HTTPS证书更新。如果忘了续期用户打开小程序会发现所有请求全部失败页面空白。这个事儿遇到一次就有阴影了我现在会设置证书到期前一个月的监控提醒。版本审核。每次更新上线都需要提交微信审核审核周期从几小时到几天不等。如果项目里涉及招聘类内容审核较严格需要提前准备好营业执照、保证信息真实性。我的经验是凡是涉及用户生成内容企业发布职位信息的都要在后台加一个审核机制审核上线前一定要明确说明平台有内容管理能力否则有可能被驳回。6.4 数据库备份与定时任务生产环境的数据库备份是必须做的。MySQL可以用mysqldump配合Linux的crontab做每日全量备份保留最近7天备份文件。另外职位过期处理和后端数据统计可以用Spring Boot的Scheduled定时任务。每天晚上系统自动把超过截止日期的职位标记为is_expired 1并统计各个专业、各个岗位类型的投递量数据放入统计表。这样管理员后台看数据报表的时候不用实时跑全量统计接口响应速度会快很多。7. 系统测试与常见问题排查速查表7.1 轮测试的场景覆盖清单测试不能只测正常路径我把测试用例按下面维度拆分功能测试登录、职位浏览、筛选搜索、投递流程、简历编辑保存、收藏、消息通知、HR审核、管理员统计。每个环节都要覆盖正常流程和异常流程。兼容性测试微信开发者工具、Android真机、iOS真机各跑一遍。重点看自定义导航栏在不同屏幕上的适配富文本内容在两种系统里的渲染差异。性能测试模拟100个学生同时访问职位列表接口观察接口响应时间。MyBatis-Plus的分页查询在这个量级不会有问题主要是看Redis缓存有没有兜住热门数据。安全测试越权访问、SQL注入、XSS注入。后端统一做了参数校验和拦截器但还是要测一下普通用户能不能通过模拟请求访问HR接口。7.2 高频问题速查表问题现象可能原因解决方案微信登录拉不起AppID错误或未配置检查小程序后台AppID和代码里配置是否一致手机号快捷登录失败未绑定手机号或接口权限不足检查是否在公众平台申请手机号快速验证组件权限图片上传后加载不出来图片域名未加入白名单在公众平台配置downloadFile合法域名职位列表是空的后端服务未启动或数据库没有数据用Postman直接请求接口排查投递提示网络异常请求超时或接口报错查看后端日志定位异常堆栈订阅消息推送不成功用户未授权订阅检查消息模板ID配置和授权流程数据统计报表不正确定时任务未运行检查Scheduled运行日志7.3 想清楚用户的增长路径再动手开发前不妨先走一遍用户旅程地图学生小王在班级群里看到老师转发的“校园求职”小程序卡片点开小程序微信登录后看到首页推荐了3个技术岗位他点击其中一个前端开发岗位看到职位描述很匹配顺手完善了简历模板一键投递投完收到一条订阅消息确认“投递成功”。两天后收到通知“您的简历已被查看”又过了两天收到面试邀约。这条旅程走通项目就成功了一半。剩下的一半是HR端能不能顺畅地看简历、筛简历、发消息和学校端能不能看到完整数据。8. 实测总结与后续扩展建议磨了大半个学期这个系统总算是从需求文档变成了一个能跑通全流程的小程序。拿着真机从学生注册、完善简历、看到职位、投递、HR处理、收到通知完整走了一遍心里的石头终于落地。回过头来看最值得分享的心得是校园里做的系统功能不在多在于核心流程没有断点。做之前先想清楚学生、HR、管理员各自的工作流是怎么走的把流程打通比做十个花哨页面有价值得多。按现在的结构后续还有好几个方向可以扩展一是加上AI简历诊断。学生上传简历后系统基于预设的规则库关键词匹配、内容完整度、项目经历描述质量自动评分并给出修改建议这个功能在校园职前教育场景很受欢迎。二是增加宣讲会模块。将宣讲会信息、在线报名、座位预约统一管理方便学生提前锁定名额。三是基于学生浏览和投递行为做职位推荐。用简单的协同过滤算法给用户打上偏好标签再和职位标签做匹配推荐准确度过得去也够写进论文里了。四是数据看板增强。管理员端生成就业质量报告包括各专业就业率、平均薪资、企业满意度等指标导出PDF直接拿去给学校领导汇报。这个项目做到了现在我自己最大的收获倒不是技术上的而是真正体会到了“从用户需求出发”这件事有多重要。你以为学生要的是一个功能齐全的招聘平台其实他们要的可能只是“别让我错过一条适合我的信息”。想通了这一点很多设计决策就变得简单了。