肝了73小时的计算机设计赛交稿了!这3个设计决策让我彻夜难眠
职业规划项目总结
此项目是2025中国大学生设计大赛web开发组的一个作品。
在最开始的时候,没有人在乎这个比赛,老师提出2个选题,其中一个就是这个,然后我回去做了一个前端的demo,给老师看了一下,没有什么反应。我就继续做我的项目去了,一直做,直到4.9号才开了一个会说要选择一个题目,最终的结果就是选择了职业规划,其中一个人还退了,因为报了其他的组,然后老师A就一直问准备的怎么样了...我没话说,因为我根本就没有去了解
项目前期
4-9
前期一直都是我在弄,我刚开始想的是设计一个数据库出来先,我就去设计了半天,当然之后还是一直在修改一些字段。
这个项目的主要是依靠AI来进行分析简历之类的操作,所以我当时做了有个简单的两个接口,也就是登录和注册,我就去研究AI了。
4-10
首先我用的是SpringAI,然后我弄了半天都不知道要怎么流式输出(让AI输出的字一点一点的出来),我又看到了一个第三方依赖langchain4j,然后在B站看到了一个视频,跟着他写完,可以流式输出了,然后又去弄JSONPATH的问题,我记得当时是弄到了2点多,还是有点问题,就是JSONPATH的问题,第二天早上一起来我就接着干活,终于算是搞定了
JSONPATH:$.data
## 优势分析- **教育背景**:清华大学计算机科学与技术专业与目标岗位高度匹配- **工作经历**:在字节跳动任职三年,积累丰富实战经验,参与多个大型项目- **技能描述**:熟悉多种前端框架,如JS中的Node.js, React, Vue.js,以及多种Python框架,如Django, Flask,表明技术栈广泛## 缺点分析- **时间断层**:2020-2021年未说明具体去向- **技能描述不清**熟悉Office应改为熟练掌握Office工具,尤其是Excel高级功能## 面试建议- **STAR法则**:在描述项目经历时,按照情景(Scenario)、任务(Task)、行动(Action)、结果(Result)的原则进行阐述- **岗位关联**:确保所列技能中针对于招聘要求提及的特定工具或技术进行详细描述,结合具体项目成果强调对应的技术能力
代码实现(Controller层):
@PostMapping(value = "/send/{resumeId}", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> sendResume(@PathVariable("resumeId") Long resumeId) {
// TODO 之后需要添加消息队列给出职位推荐...
return resumeService.analyzeResumeStream(resumeId)
.map(json -> "{\"data\":\"" + json + "\"}")
.timeout(Duration.ofSeconds(30)) // 防止长时间无响应
.onErrorResume(e -> {
log.debug("连接中止: " + e.getMessage());
return Flux.just("{\"data\":\"" + "已停止" + "\"}");
});
}
知道怎么调用AI进行流式输出,我就写了一个分析简历的接口,Service我就不放出来了,其实差不太多,就是保存完成之后保存在数据库里面了。
还有根据问题答案推荐职业的接口,差不多就写了这些。
@Override
public Result analyze(List<QuestionDTO> questionDTOList) {
//获取总分
Question question = getRecord(questionDTOList);
if(question.getAnalyzeData() !=null){
//有数据,不需要请求ai,直接返回
String analyzeData = question.getAnalyzeData().toString();
RecommendedVO recommendedVO = JSONUtil.toBean(analyzeData, RecommendedVO.class);
return Result.ok(recommendedVO);
}
String jsonStr = JSONUtil.toJsonStr(questionDTOList);
//直接写到消息队列中
if(sendStreamMessage(jsonStr,JOB_QUESTION_KEY,question.getScoreTotal())){
return Result.ok();
}else{
return Result.fail("请求失败");
}
//直接返回
}
4-11
今天我没有push到GitHub有点忘记做了啥了,我记得我是写了一个专业测评的功能,然后提出了几个想法,专业测评(职业测评和专业测评以下统称为测评功能)就是其中一个,根据职业或者专业生成学习路径的功能我是在设计数据库的时候我就想好了,我还想到了一个就是给失业的人提建议的功能,但是我感觉有点难以实现而且不好保存,所以就没有写。专业测评的代码跟职业测评的是差不多的。
4-12
今天我优化了一下测评功能,最开始我写的是直接去请求AI,然后我这个线程一直在堵塞,等待AI的响应,最重要的是AI返回的JSON数据可能是错误的,,就导致体验非常的糟糕,想起来我学了Redis,而Redis有一个数据类型Stream,就是专门为消息队列设计的,我就用它来写消息队列(当时我还不会消息队列的中间件),把测评的代码修改一下,就OK了。
我记得当天我还我弄了一个阿里云的API去,刚开始有一定的免费额度
项目中期
4-13
今天加了一个查看历史评测的功能,用Redis的ZSet存储,这里还是挺简单的,就不多说了。
主要是给查看职业专业加了一个缓存,刚开始我设置了1天过期,我也是个人才,现在是20分钟过期,给简历添加了一个字段,默认简历,为什么加这个呢,因为在后面的生成学习路径需要用。
生成学习路径的接口是最难写的功能了,我觉得这个项目里面。首先是两个接口,一个接收职业id,一个接收专业id。生成对应的路径,因为也涉及到了AI我写在消息队列里面,就直接返回了。测试有很多bug
4-14
一直在弄生成学习路径的接口...然后到晚上还是有问题...
刚开始生成学习路径我是想着,先生成需要的技能,然后用户点击技能我再去请求章节,点击章节请求小结,首先是代码实现起很麻烦,这个写消息队列吧,我都要查看章节,小结了,你跟我说没有?不写消息队列吧,又太慢了,所以我决定直接生成全部。解析那里的业务代码就变得很复杂,是我目前写过最复杂的业务代码了
4-15
今天终于把生成学习路径的接口搞定了,我就去写了一下回去学习路径的接口,根据路径id获取技能、根据技能id获取章节、根据章节id获取小结,写了一个完成小结的接口(具体的逻辑没有写,只是单纯的修改了一下状态)。
4-16
写了一个分页获取所有专业、职业的接口,给专业、职业添加了一个字段,分类,根据分类获取,添加模糊查找的功能。添加简单的评论功能,举报评论的功能,不过这里写的有问题,用户本人发送的评论用户删除不了,而且用户可以重复举报一个评论,这对数据库造成了很大的压力,还有就是删除学习路径没有进行二次判断,有安全问题。
4-17
写了一个很关键的业务,就是用户点击完成小结,我没有去更新学习的状态以及进度,这里我想了很久都不知道要怎么写,因为我不知道当前小结是属于哪个学习路径的,属于哪个技能的。还有就是我不知道这个学习路径总共有多少个小结,技能总共有多少个小结,这样我也没有办法去计算他的一个进度。前端只传递了一个小结的id
nym可以想一想再继续看
下面是当时表的结构
leanring_paths
学习路线表
分析简历之后,返回分析的内容,用户可以选择某个学习路线进行学习,选择之后存储在
id | bigint | ||
job_id | bigint | 外键 | |
majors_id | bigint | 外键 | 不能跟job_id同时存在 |
user_id | bigint | 外键 | |
goal_name | varchar(20) | 目标名 | |
current_progress | tinyint | 进度 | (0-100) |
is_active | boolean | 是否完成 | 0:进行中1:已完成 |
why_recommend | varchar(400) | 为啥推荐 | |
advice_and_attention | varchar(300) | 建议和注意事项 | |
complete_harvest | varchar((200) | 完成之后的收获 | |
create_at | date | 创建时间 | 时间戳 |
end | date | 完成时间 |
learning_skill
学习某个技能表
id | BIGINT | 主键ID | |
path_id | BIGINT | 学习路径ID | 外键 |
step_name | VARCHAR(100) | 步骤名称 | 比如:学习Java |
step_order | INT | 步骤顺序 | |
current_progress | tinyint | 学习进度 | (0-100) |
status | char(1) | 学习状态 | 0未学 1学习中 2学完 |
scheduled_time | varchar(20) | 预计时间 | 例如:3-6个月 |
importance | char(20) | 重要程度 | 核心/重要/扩展 |
preknowledge | varchar(200) | 前置知识 | 可为null |
resource | json | 学习资源 | |
complete_harvest | varchar(300) | 完成之后的收获 | |
start_date | DATETIME | 开始日期 | 时间戳 |
complete_date | DATETIME | 完成日期 |
learing_skill_chapter
学习某个技能的具体章节
id | bigint | 主键 | |
skill_id | bigint | 外键 | 表learning_skill |
name | varchar(50) | 章节名称 | 比如:"第一章:JavaScript简介" |
status | char(1) | 学习状态 | 0未学 1学习中 2学完 |
complete_harvest | varchar(100) | 完成收获 | |
description | varchar(200) | 说明 | |
chapter_order | int | 顺序 |
learning_skill_details
学习某个技能下章节的的详细表格
id | bigint | 主键 | |
chapter_id | bigint | 外键 | 表learing_skill_chapter |
name | varchar(50) | 学习小节名称 | 比如:第一个Java程序 |
status | char(1) | 学习状态 | 0未学 1学习中 2学完 |
note | text | 学习笔记 | |
description | varchar(200) | 说明 | |
nodule_order | int | 顺序 |
解决办法:我当时想的是生成学习路径,保存小结的时候在redis里面用list保存一下整个学习路径的小结数以及每个技能的总小结数。写到这里我去看了一下redis,发现怎么那么多,学习路径才那么点,看了一下删除学习路径的接口发现忘记删除缓存了...
先修改一下小结表的结构,添加两个字段路径id,技能id,保存完成之后需要在完成小结的接口执行一系列的业务逻辑,首先是需要让前端传递路径id 以及技能id,因为有点复杂,所以我直接写消息队列了。
在消息队列里面,获取路径id,根据路径的id获取总数,获取当前技能的总数,此时可以有人在想,我直接去数据库里面查一下不就行了吗?为啥要把总数写在redis里面呢?这确实是可以的,我是因为当时我我去没有想到,我写着写着才想到的。不过我这样写也没有什么问题,因为在这里需要执行很多更新的操作,对于数据库来说其实压力挺大的。之后就简单了,首先获取技能已完成的节点还要路径已完成的节点,去获取进度即可,这里还需要判断已完成的节点是否等于全部节点,如果等于需要更新路径,技能的状态
4-18
优化评论功能,用redis的Set存储哪个用户举报了这个评论,在举报时判断一下,防止重复举报。在删除的相关接口(删除简历,删除学习路径)添加一个当前登录用户的判断。
4-19
修复了一下学习路径的bug,还有就是修改了一下提示词,之前的AI,不过我的目标是啥,返回的都是学习Java的学习路径,加了一个mybatis的配置类,之前没有写,更新成功还给我返回false,老是返回错误的结果,我到今天才解决整个问题。
项目后期
4-20
添加cors配置,添加传递用户默认简历的功能(之前老是生成Java的学习路径我就把这个删除了,现在又加回来了)
4-21
添加专门的测试接口,用于前端连接后端的测试,拦截器不拦截测试接口
4-22
修复配置类,不让服务器缓存流式
其实这几天主要在弄前端和服务器的问题,前端部署了之后是https,然后后端默认是http的,师兄就弄这个问题,还要就是因为后端是没有域名的,导致前端请求时会屏蔽这个请求,就在前端加了一个测试的功能
项目总结
总的来说,我对redis的掌握更好了,还有就是了解了AI的基本调用方法。项目经验+1
放几张图片
体验地址:https://marvelous-snickerdoodle-d8a4f2.netlify.app/ 第一次需要同意访问后端!
#比赛##大一##我的失利项目复盘#