肝了73小時(shí)的計(jì)算機(jī)設(shè)計(jì)賽交稿了!這3個(gè)設(shè)計(jì)決策讓我徹夜難眠
職業(yè)規(guī)劃項(xiàng)目總結(jié)
此項(xiàng)目是2025中國(guó)大學(xué)生設(shè)計(jì)大賽web開(kāi)發(fā)組的一個(gè)作品。
在最開(kāi)始的時(shí)候,沒(méi)有人在乎這個(gè)比賽,老師提出2個(gè)選題,其中一個(gè)就是這個(gè),然后我回去做了一個(gè)前端的demo,給老師看了一下,沒(méi)有什么反應(yīng)。我就繼續(xù)做我的項(xiàng)目去了,一直做,直到4.9號(hào)才開(kāi)了一個(gè)會(huì)說(shuō)要選擇一個(gè)題目,最終的結(jié)果就是選擇了職業(yè)規(guī)劃,其中一個(gè)人還退了,因?yàn)閳?bào)了其他的組,然后老師A就一直問(wèn)準(zhǔn)備的怎么樣了...我沒(méi)話說(shuō),因?yàn)槲腋揪蜎](méi)有去了解
項(xiàng)目前期
4-9
前期一直都是我在弄,我剛開(kāi)始想的是設(shè)計(jì)一個(gè)數(shù)據(jù)庫(kù)出來(lái)先,我就去設(shè)計(jì)了半天,當(dāng)然之后還是一直在修改一些字段。
這個(gè)項(xiàng)目的主要是依靠AI來(lái)進(jìn)行分析簡(jiǎn)歷之類(lèi)的操作,所以我當(dāng)時(shí)做了有個(gè)簡(jiǎn)單的兩個(gè)接口,也就是登錄和注冊(cè),我就去研究AI了。
4-10
首先我用的是SpringAI,然后我弄了半天都不知道要怎么流式輸出(讓AI輸出的字一點(diǎn)一點(diǎn)的出來(lái)),我又看到了一個(gè)第三方依賴langchain4j,然后在B站看到了一個(gè)視頻,跟著他寫(xiě)完,可以流式輸出了,然后又去弄JSONPATH的問(wèn)題,我記得當(dāng)時(shí)是弄到了2點(diǎn)多,還是有點(diǎn)問(wèn)題,就是JSONPATH的問(wèn)題,第二天早上一起來(lái)我就接著干活,終于算是搞定了
JSONPATH:$.data
## 優(yōu)勢(shì)分析- **教育背景**:清華大學(xué)計(jì)算機(jī)科學(xué)與技術(shù)專(zhuān)業(yè)與目標(biāo)崗位高度匹配- **工作經(jīng)歷**:在字節(jié)跳動(dòng)任職三年,積累豐富實(shí)戰(zhàn)經(jīng)驗(yàn),參與多個(gè)大型項(xiàng)目- **技能描述**:熟悉多種前端框架,如JS中的Node.js, React, Vue.js,以及多種Python框架,如Django, Flask,表明技術(shù)棧廣泛## 缺點(diǎn)分析- **時(shí)間斷層**:2020-2021年未說(shuō)明具體去向- **技能描述不清**熟悉Office應(yīng)改為熟練掌握Office工具,尤其是Excel高級(jí)功能## 面試建議- **STAR法則**:在描述項(xiàng)目經(jīng)歷時(shí),按照情景(Scenario)、任務(wù)(Task)、行動(dòng)(Action)、結(jié)果(Result)的原則進(jìn)行闡述- **崗位關(guān)聯(lián)**:確保所列技能中針對(duì)于招聘要求提及的特定工具或技術(shù)進(jìn)行詳細(xì)描述,結(jié)合具體項(xiàng)目成果強(qiáng)調(diào)對(duì)應(yīng)的技術(shù)能力
代碼實(shí)現(xiàn)(Controller層):
@PostMapping(value = "/send/{resumeId}", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> sendResume(@PathVariable("resumeId") Long resumeId) {
// TODO 之后需要添加消息隊(duì)列給出職位推薦...
return resumeService.analyzeResumeStream(resumeId)
.map(json -> "{\"data\":\"" + json + "\"}")
.timeout(Duration.ofSeconds(30)) // 防止長(zhǎng)時(shí)間無(wú)響應(yīng)
.onErrorResume(e -> {
log.debug("連接中止: " + e.getMessage());
return Flux.just("{\"data\":\"" + "已停止" + "\"}");
});
}
知道怎么調(diào)用AI進(jìn)行流式輸出,我就寫(xiě)了一個(gè)分析簡(jiǎn)歷的接口,Service我就不放出來(lái)了,其實(shí)差不太多,就是保存完成之后保存在數(shù)據(jù)庫(kù)里面了。
還有根據(jù)問(wèn)題答案推薦職業(yè)的接口,差不多就寫(xiě)了這些。
@Override
public Result analyze(List<QuestionDTO> questionDTOList) {
//獲取總分
Question question = getRecord(questionDTOList);
if(question.getAnalyzeData() !=null){
//有數(shù)據(jù),不需要請(qǐng)求ai,直接返回
String analyzeData = question.getAnalyzeData().toString();
RecommendedVO recommendedVO = JSONUtil.toBean(analyzeData, RecommendedVO.class);
return Result.ok(recommendedVO);
}
String jsonStr = JSONUtil.toJsonStr(questionDTOList);
//直接寫(xiě)到消息隊(duì)列中
if(sendStreamMessage(jsonStr,JOB_QUESTION_KEY,question.getScoreTotal())){
return Result.ok();
}else{
return Result.fail("請(qǐng)求失敗");
}
//直接返回
}
4-11
今天我沒(méi)有push到GitHub有點(diǎn)忘記做了啥了,我記得我是寫(xiě)了一個(gè)專(zhuān)業(yè)測(cè)評(píng)的功能,然后提出了幾個(gè)想法,專(zhuān)業(yè)測(cè)評(píng)(職業(yè)測(cè)評(píng)和專(zhuān)業(yè)測(cè)評(píng)以下統(tǒng)稱(chēng)為測(cè)評(píng)功能)就是其中一個(gè),根據(jù)職業(yè)或者專(zhuān)業(yè)生成學(xué)習(xí)路徑的功能我是在設(shè)計(jì)數(shù)據(jù)庫(kù)的時(shí)候我就想好了,我還想到了一個(gè)就是給失業(yè)的人提建議的功能,但是我感覺(jué)有點(diǎn)難以實(shí)現(xiàn)而且不好保存,所以就沒(méi)有寫(xiě)。專(zhuān)業(yè)測(cè)評(píng)的代碼跟職業(yè)測(cè)評(píng)的是差不多的。
4-12
今天我優(yōu)化了一下測(cè)評(píng)功能,最開(kāi)始我寫(xiě)的是直接去請(qǐng)求AI,然后我這個(gè)線程一直在堵塞,等待AI的響應(yīng),最重要的是AI返回的JSON數(shù)據(jù)可能是錯(cuò)誤的,,就導(dǎo)致體驗(yàn)非常的糟糕,想起來(lái)我學(xué)了Redis,而Redis有一個(gè)數(shù)據(jù)類(lèi)型Stream,就是專(zhuān)門(mén)為消息隊(duì)列設(shè)計(jì)的,我就用它來(lái)寫(xiě)消息隊(duì)列(當(dāng)時(shí)我還不會(huì)消息隊(duì)列的中間件),把測(cè)評(píng)的代碼修改一下,就OK了。
我記得當(dāng)天我還我弄了一個(gè)阿里云的API去,剛開(kāi)始有一定的免費(fèi)額度
項(xiàng)目中期
4-13
今天加了一個(gè)查看歷史評(píng)測(cè)的功能,用Redis的ZSet存儲(chǔ),這里還是挺簡(jiǎn)單的,就不多說(shuō)了。
主要是給查看職業(yè)專(zhuān)業(yè)加了一個(gè)緩存,剛開(kāi)始我設(shè)置了1天過(guò)期,我也是個(gè)人才,現(xiàn)在是20分鐘過(guò)期,給簡(jiǎn)歷添加了一個(gè)字段,默認(rèn)簡(jiǎn)歷,為什么加這個(gè)呢,因?yàn)樵诤竺娴纳蓪W(xué)習(xí)路徑需要用。
生成學(xué)習(xí)路徑的接口是最難寫(xiě)的功能了,我覺(jué)得這個(gè)項(xiàng)目里面。首先是兩個(gè)接口,一個(gè)接收職業(yè)id,一個(gè)接收專(zhuān)業(yè)id。生成對(duì)應(yīng)的路徑,因?yàn)橐采婕暗搅薃I我寫(xiě)在消息隊(duì)列里面,就直接返回了。測(cè)試有很多bug
4-14
一直在弄生成學(xué)習(xí)路徑的接口...然后到晚上還是有問(wèn)題...
剛開(kāi)始生成學(xué)習(xí)路徑我是想著,先生成需要的技能,然后用戶點(diǎn)擊技能我再去請(qǐng)求章節(jié),點(diǎn)擊章節(jié)請(qǐng)求小結(jié),首先是代碼實(shí)現(xiàn)起很麻煩,這個(gè)寫(xiě)消息隊(duì)列吧,我都要查看章節(jié),小結(jié)了,你跟我說(shuō)沒(méi)有?不寫(xiě)消息隊(duì)列吧,又太慢了,所以我決定直接生成全部。解析那里的業(yè)務(wù)代碼就變得很復(fù)雜,是我目前寫(xiě)過(guò)最復(fù)雜的業(yè)務(wù)代碼了
4-15
今天終于把生成學(xué)習(xí)路徑的接口搞定了,我就去寫(xiě)了一下回去學(xué)習(xí)路徑的接口,根據(jù)路徑id獲取技能、根據(jù)技能id獲取章節(jié)、根據(jù)章節(jié)id獲取小結(jié),寫(xiě)了一個(gè)完成小結(jié)的接口(具體的邏輯沒(méi)有寫(xiě),只是單純的修改了一下?tīng)顟B(tài))。
4-16
寫(xiě)了一個(gè)分頁(yè)獲取所有專(zhuān)業(yè)、職業(yè)的接口,給專(zhuān)業(yè)、職業(yè)添加了一個(gè)字段,分類(lèi),根據(jù)分類(lèi)獲取,添加模糊查找的功能。添加簡(jiǎn)單的評(píng)論功能,舉報(bào)評(píng)論的功能,不過(guò)這里寫(xiě)的有問(wèn)題,用戶本人發(fā)送的評(píng)論用戶刪除不了,而且用戶可以重復(fù)舉報(bào)一個(gè)評(píng)論,這對(duì)數(shù)據(jù)庫(kù)造成了很大的壓力,還有就是刪除學(xué)習(xí)路徑?jīng)]有進(jìn)行二次判斷,有安全問(wèn)題。
4-17
寫(xiě)了一個(gè)很關(guān)鍵的業(yè)務(wù),就是用戶點(diǎn)擊完成小結(jié),我沒(méi)有去更新學(xué)習(xí)的狀態(tài)以及進(jìn)度,這里我想了很久都不知道要怎么寫(xiě),因?yàn)槲也恢喇?dāng)前小結(jié)是屬于哪個(gè)學(xué)習(xí)路徑的,屬于哪個(gè)技能的。還有就是我不知道這個(gè)學(xué)習(xí)路徑總共有多少個(gè)小結(jié),技能總共有多少個(gè)小結(jié),這樣我也沒(méi)有辦法去計(jì)算他的一個(gè)進(jìn)度。前端只傳遞了一個(gè)小結(jié)的id
nym可以想一想再繼續(xù)看
下面是當(dāng)時(shí)表的結(jié)構(gòu)
leanring_paths
學(xué)習(xí)路線表
分析簡(jiǎn)歷之后,返回分析的內(nèi)容,用戶可以選擇某個(gè)學(xué)習(xí)路線進(jìn)行學(xué)習(xí),選擇之后存儲(chǔ)在
id | bigint | ||
job_id | bigint | 外鍵 | |
majors_id | bigint | 外鍵 | 不能跟job_id同時(shí)存在 |
user_id | bigint | 外鍵 | |
goal_name | varchar(20) | 目標(biāo)名 | |
current_progress | tinyint | 進(jìn)度 | (0-100) |
is_active | boolean | 是否完成 | 0:進(jìn)行中1:已完成 |
why_recommend | varchar(400) | 為啥推薦 | |
advice_and_attention | varchar(300) | 建議和注意事項(xiàng) | |
complete_harvest | varchar((200) | 完成之后的收獲 | |
create_at | date | 創(chuàng)建時(shí)間 | 時(shí)間戳 |
end | date | 完成時(shí)間 |
learning_skill
學(xué)習(xí)某個(gè)技能表
id | BIGINT | 主鍵ID | |
path_id | BIGINT | 學(xué)習(xí)路徑ID | 外鍵 |
step_name | VARCHAR(100) | 步驟名稱(chēng) | 比如:學(xué)習(xí)Java |
step_order | INT | 步驟順序 | |
current_progress | tinyint | 學(xué)習(xí)進(jìn)度 | (0-100) |
status | char(1) | 學(xué)習(xí)狀態(tài) | 0未學(xué) 1學(xué)習(xí)中 2學(xué)完 |
scheduled_time | varchar(20) | 預(yù)計(jì)時(shí)間 | 例如:3-6個(gè)月 |
importance | char(20) | 重要程度 | 核心/重要/擴(kuò)展 |
preknowledge | varchar(200) | 前置知識(shí) | 可為null |
resource | json | 學(xué)習(xí)資源 | |
complete_harvest | varchar(300) | 完成之后的收獲 | |
start_date | DATETIME | 開(kāi)始日期 | 時(shí)間戳 |
complete_date | DATETIME | 完成日期 |
learing_skill_chapter
學(xué)習(xí)某個(gè)技能的具體章節(jié)
id | bigint | 主鍵 | |
skill_id | bigint | 外鍵 | 表learning_skill |
name | varchar(50) | 章節(jié)名稱(chēng) | 比如:"第一章:JavaScript簡(jiǎn)介" |
status | char(1) | 學(xué)習(xí)狀態(tài) | 0未學(xué) 1學(xué)習(xí)中 2學(xué)完 |
complete_harvest | varchar(100) | 完成收獲 | |
description | varchar(200) | 說(shuō)明 | |
chapter_order | int | 順序 |
learning_skill_details
學(xué)習(xí)某個(gè)技能下章節(jié)的的詳細(xì)表格
id | bigint | 主鍵 | |
chapter_id | bigint | 外鍵 | 表learing_skill_chapter |
name | varchar(50) | 學(xué)習(xí)小節(jié)名稱(chēng) | 比如:第一個(gè)Java程序 |
status | char(1) | 學(xué)習(xí)狀態(tài) | 0未學(xué) 1學(xué)習(xí)中 2學(xué)完 |
note | text | 學(xué)習(xí)筆記 | |
description | varchar(200) | 說(shuō)明 | |
nodule_order | int | 順序 |
解決辦法:我當(dāng)時(shí)想的是生成學(xué)習(xí)路徑,保存小結(jié)的時(shí)候在redis里面用list保存一下整個(gè)學(xué)習(xí)路徑的小結(jié)數(shù)以及每個(gè)技能的總小結(jié)數(shù)。寫(xiě)到這里我去看了一下redis,發(fā)現(xiàn)怎么那么多,學(xué)習(xí)路徑才那么點(diǎn),看了一下刪除學(xué)習(xí)路徑的接口發(fā)現(xiàn)忘記刪除緩存了...
先修改一下小結(jié)表的結(jié)構(gòu),添加兩個(gè)字段路徑id,技能id,保存完成之后需要在完成小結(jié)的接口執(zhí)行一系列的業(yè)務(wù)邏輯,首先是需要讓前端傳遞路徑id 以及技能id,因?yàn)橛悬c(diǎn)復(fù)雜,所以我直接寫(xiě)消息隊(duì)列了。
在消息隊(duì)列里面,獲取路徑id,根據(jù)路徑的id獲取總數(shù),獲取當(dāng)前技能的總數(shù),此時(shí)可以有人在想,我直接去數(shù)據(jù)庫(kù)里面查一下不就行了嗎?為啥要把總數(shù)寫(xiě)在redis里面呢?這確實(shí)是可以的,我是因?yàn)楫?dāng)時(shí)我我去沒(méi)有想到,我寫(xiě)著寫(xiě)著才想到的。不過(guò)我這樣寫(xiě)也沒(méi)有什么問(wèn)題,因?yàn)樵谶@里需要執(zhí)行很多更新的操作,對(duì)于數(shù)據(jù)庫(kù)來(lái)說(shuō)其實(shí)壓力挺大的。之后就簡(jiǎn)單了,首先獲取技能已完成的節(jié)點(diǎn)還要路徑已完成的節(jié)點(diǎn),去獲取進(jìn)度即可,這里還需要判斷已完成的節(jié)點(diǎn)是否等于全部節(jié)點(diǎn),如果等于需要更新路徑,技能的狀態(tài)
4-18
優(yōu)化評(píng)論功能,用redis的Set存儲(chǔ)哪個(gè)用戶舉報(bào)了這個(gè)評(píng)論,在舉報(bào)時(shí)判斷一下,防止重復(fù)舉報(bào)。在刪除的相關(guān)接口(刪除簡(jiǎn)歷,刪除學(xué)習(xí)路徑)添加一個(gè)當(dāng)前登錄用戶的判斷。
4-19
修復(fù)了一下學(xué)習(xí)路徑的bug,還有就是修改了一下提示詞,之前的AI,不過(guò)我的目標(biāo)是啥,返回的都是學(xué)習(xí)Java的學(xué)習(xí)路徑,加了一個(gè)mybatis的配置類(lèi),之前沒(méi)有寫(xiě),更新成功還給我返回false,老是返回錯(cuò)誤的結(jié)果,我到今天才解決整個(gè)問(wèn)題。
項(xiàng)目后期
4-20
添加cors配置,添加傳遞用戶默認(rèn)簡(jiǎn)歷的功能(之前老是生成Java的學(xué)習(xí)路徑我就把這個(gè)刪除了,現(xiàn)在又加回來(lái)了)
4-21
添加專(zhuān)門(mén)的測(cè)試接口,用于前端連接后端的測(cè)試,攔截器不攔截測(cè)試接口
4-22
修復(fù)配置類(lèi),不讓服務(wù)器緩存流式
其實(shí)這幾天主要在弄前端和服務(wù)器的問(wèn)題,前端部署了之后是https,然后后端默認(rèn)是http的,師兄就弄這個(gè)問(wèn)題,還要就是因?yàn)楹蠖耸菦](méi)有域名的,導(dǎo)致前端請(qǐng)求時(shí)會(huì)屏蔽這個(gè)請(qǐng)求,就在前端加了一個(gè)測(cè)試的功能
項(xiàng)目總結(jié)
總的來(lái)說(shuō),我對(duì)redis的掌握更好了,還有就是了解了AI的基本調(diào)用方法。項(xiàng)目經(jīng)驗(yàn)+1
放幾張圖片
體驗(yàn)地址:https://marvelous-snickerdoodle-d8a4f2.netlify.app/ 第一次需要同意訪問(wèn)后端!
#比賽##大一##我的失利項(xiàng)目復(fù)盤(pán)#