vivo 互聯(lián)網(wǎng)自研代碼評(píng)審 VCR 落地實(shí)踐
代碼評(píng)審是軟件質(zhì)量保證一種活動(dòng),由一個(gè)或者多個(gè)人對(duì)一個(gè)程序的部分或者全部源代碼進(jìn)閱讀理解。一般來(lái)說(shuō)分為作者和評(píng)審者兩種角色,作者方提供代碼邏輯的介紹和代碼,評(píng)審者則對(duì)提供的代碼基于設(shè)計(jì),功能性和非功能性等方面認(rèn)知進(jìn)行閱讀并提出問(wèn)題。常見(jiàn)的評(píng)審組織形式是有同行評(píng)審(Peer Review)和小組檢查 (Team Inspection)兩種方式。
在代碼評(píng)審中,評(píng)審的目的在通過(guò)代碼的評(píng)審發(fā)現(xiàn)潛在的問(wèn)題,同時(shí)分享和表達(dá)是代碼評(píng)審的重要收獲,我們知道人相同在不同的文化下生產(chǎn)力是不同的,代碼評(píng)審是一個(gè)工具,工具受文化的影響的同時(shí)也影響著文化,最終朝著我們希望的責(zé)任共擔(dān)、持續(xù)改進(jìn)的方向發(fā)展。
一、代碼評(píng)審演進(jìn)
隨著互聯(lián)網(wǎng)的發(fā)展,開(kāi)發(fā)人員也越來(lái)越重視代碼評(píng)審帶來(lái)的代碼的代碼質(zhì)量提高以及代碼評(píng)審間接帶來(lái)的分享及人員備份效果,已經(jīng)不滿足于只是簡(jiǎn)單的發(fā)現(xiàn)當(dāng)前問(wèn)題解決問(wèn)題記錄問(wèn)題,需要滿足從評(píng)審基本跟進(jìn)、評(píng)論管理、評(píng)審報(bào)告以及評(píng)審方式多樣化、評(píng)審與研發(fā)流程相結(jié)合等需求。
① 代碼評(píng)審檢查表:手工定義要檢查項(xiàng),檢查完進(jìn)行打卡標(biāo)記結(jié)果。
② 插件快速評(píng)審導(dǎo)入導(dǎo)出:快速在插件上進(jìn)行評(píng)論,并將評(píng)論結(jié)果導(dǎo)出給被評(píng)審人,被評(píng)審人導(dǎo)入評(píng)審結(jié)果查看,評(píng)審表不可復(fù)用,一旦代碼變更則無(wú)法準(zhǔn)確定位、也無(wú)法再次跟蹤評(píng)審修改結(jié)果。
③ 在線代碼評(píng)審:在線插件或網(wǎng)頁(yè)評(píng)審,提供提交前提交后評(píng)審,可多人評(píng)審策略管控、代碼評(píng)審與需求/缺陷關(guān)聯(lián)管理。
④ 自動(dòng)化代碼評(píng)審:結(jié)合現(xiàn)有的Sonar掃描、安全掃描進(jìn)行對(duì)提交的代碼進(jìn)行自動(dòng)化檢查,使代碼在人工評(píng)審之前已經(jīng)經(jīng)歷一輪自動(dòng)評(píng)審,代碼評(píng)審?fù)ㄟ^(guò)之后可自動(dòng)觸發(fā)構(gòu)建、部署等。
⑤ 智能化代碼評(píng)審:根據(jù)AI大模型,可對(duì)提交代碼進(jìn)行綜合評(píng)價(jià)(編碼標(biāo)準(zhǔn)、可用性、可讀性、可維護(hù)性、安全性、高性能、異常控制、設(shè)計(jì)原則、可擴(kuò)展性、代碼復(fù)雜度等等)并給出相關(guān)測(cè)試建議等,未來(lái)大模型對(duì)代碼評(píng)審還有更大的空間。
二、代碼評(píng)審解決的需求和痛點(diǎn)是什么?
vivo當(dāng)前已經(jīng)有EasyCR評(píng)審工具,那為什么我們還需要繼續(xù)開(kāi)發(fā)調(diào)研代碼評(píng)審工具呢?
我們先看看下面通過(guò)內(nèi)部調(diào)研獲取的信息,看看用戶希望的代碼評(píng)審工具需求和痛點(diǎn)是什么?
針對(duì)當(dāng)前vivo代碼評(píng)審工具我們繼續(xù)升級(jí)補(bǔ)充場(chǎng)景:
- 增加評(píng)審方式:對(duì)原自由評(píng)審方式(主要是提交后進(jìn)行代碼評(píng)審)增加評(píng)審控制方式(提交代碼至倉(cāng)庫(kù)前進(jìn)行代碼評(píng)審、合并時(shí)提交代碼評(píng)審)。
- 支持網(wǎng)頁(yè)/插件:增加網(wǎng)頁(yè)端評(píng)審功能,滿足不同角色進(jìn)行評(píng)審及用戶體驗(yàn)上的優(yōu)化,增強(qiáng)插件版評(píng)審功能。
- 支持研發(fā)流程控制:上線過(guò)程中可作為人工卡點(diǎn)一項(xiàng)檢查項(xiàng)(可通過(guò)代碼是否評(píng)審、代碼評(píng)分、代碼問(wèn)題解決情況等進(jìn)行判斷),通過(guò)線上管理,提高上線質(zhì)量。
- 支持自動(dòng)化檢查:代碼提交前,提交后可進(jìn)行代碼自動(dòng)化檢查,對(duì)代碼進(jìn)行自動(dòng)評(píng)審。
- 增加用戶定制化需求:如評(píng)審權(quán)限、評(píng)審?fù)ㄖ绞健⒃u(píng)審策略多人評(píng)審管理、評(píng)審報(bào)告訂閱等。
當(dāng)前市場(chǎng)上有很多優(yōu)秀的代碼評(píng)審工具,但是很少有評(píng)審工具能滿足所有的場(chǎng)景,角色不同,需要的能力不同,同一個(gè)角色不同團(tuán)隊(duì)使用的方式不同,我們需要一款解決用戶痛癢爽的代碼評(píng)審工具。
三、vivo代碼評(píng)審系統(tǒng)架構(gòu)
四、vivo代碼評(píng)審工具使用流程
在代碼評(píng)審中,CR可以是一次Commit,也可以是一次MergeCommit,那么針對(duì)一次CR我們可以隨時(shí)對(duì)已經(jīng)提交的commit進(jìn)行評(píng)審,也可以在CRpush至代碼庫(kù)之前攔截,同時(shí)也可以在一次合并之前進(jìn)行代碼評(píng)審。
代碼評(píng)審模式:
1. 提交前評(píng)審(Pre-push Code Review)
2. 提交后評(píng)審(Post-push Code Review)
① 合并評(píng)審
② 自由評(píng)審
提交前評(píng)審:VCR基于VCR在提交push至Gitlab代碼倉(cāng)庫(kù)之前,對(duì)代碼進(jìn)行攔截,并進(jìn)行評(píng)審,支持一次評(píng)審請(qǐng)求作為一次評(píng)審,可對(duì)一次一次評(píng)審請(qǐng)求查看所有變更記錄并進(jìn)行評(píng)審追蹤。利用開(kāi)源工具Gerrit,將評(píng)審請(qǐng)求推送至Gerrit中,評(píng)審?fù)ㄟ^(guò)后,將代碼從Gerrit同步至Gitlab倉(cāng)庫(kù)
提交后評(píng)審:
①合并評(píng)審:VCR基于Gitlab 在一次MR的基礎(chǔ)上進(jìn)行代碼評(píng)審。
②自由評(píng)審:針對(duì)用戶當(dāng)前代碼庫(kù)當(dāng)前分支信息或歷史commit進(jìn)行評(píng)審。
五、vivo代碼評(píng)審工具實(shí)施
5.1 確認(rèn)技術(shù)架構(gòu)
提交倉(cāng)庫(kù)前進(jìn)行代碼評(píng)審,我們使用當(dāng)前成熟的代碼評(píng)審Gerrit,實(shí)施過(guò)程中最大的問(wèn)題是用戶如何低成本切換及簡(jiǎn)單評(píng)審的問(wèn)題,對(duì)于當(dāng)前Gerrit評(píng)審工具遇到的問(wèn)題如何解決呢?
1) 我們知道Gerrit評(píng)審工具需要提供給用戶Gerrit代碼庫(kù)地址,并進(jìn)行下載使用,當(dāng)前用戶使用的代碼庫(kù)習(xí)慣不能更改,也是不愿意修改的,那么我們?nèi)绾谓鉀Q呢?
給插件加持,提供用戶黑盒切換至評(píng)審代碼庫(kù),或執(zhí)行一鍵下載代碼庫(kù)功能,底層使用Gerrit與代碼托管庫(kù)同步機(jī)制解決代碼一致性問(wèn)題,用戶在使用代碼庫(kù)時(shí)同原使用方式一致。
2) Git代碼提交,CR為最小單位,CR可作為一次評(píng)審,但還有很多用戶使用的習(xí)慣是一次push作為一次評(píng)審,如何解決用戶一次push為一次評(píng)審呢?
a)需要對(duì)代碼關(guān)系鏈需要進(jìn)行整理,識(shí)別出一次push作為一次評(píng)審記錄,用戶多次追加提交記錄至評(píng)審請(qǐng)求,需要重新識(shí)別出關(guān)系鏈作為原push請(qǐng)求的評(píng)審記錄,Git原生對(duì)代碼變更的情況比較多,我們對(duì)一些場(chǎng)景進(jìn)行分析再特殊處理,不窮舉。
b)可對(duì)最小粒度CR的評(píng)審,也同時(shí)提供一次push請(qǐng)求內(nèi)容進(jìn)行評(píng)審,更方便快捷。
用戶不管是提交前評(píng)審、合并時(shí)評(píng)審,都可能會(huì)產(chǎn)生一次push,多次commit,用戶需要對(duì)最小粒度CR評(píng)審,也需要對(duì)最新變更所有內(nèi)容進(jìn)行評(píng)審。
5.2 插件改造實(shí)施
根據(jù)我們對(duì)用戶的調(diào)研過(guò)程中,用戶對(duì)代碼評(píng)審插件網(wǎng)頁(yè)同時(shí)兼容的要求比較高,針對(duì)idea插件我們?nèi)绾胃脑齑a評(píng)審,這里我們著重對(duì)Gerrit插件改造展開(kāi)說(shuō)明。
步驟1:了解插件框架、配置、打包、運(yùn)行
1)插件框架整體介紹
(圖片來(lái)源于網(wǎng)絡(luò))
- 開(kāi)發(fā)方式:在官網(wǎng)的描述中,創(chuàng)建IDEA插件工程的方式有兩種分別是使用DevKit(IntelliJ Platform Plugin 模版創(chuàng)建)和Gradle構(gòu)建方式,這兩種方式在構(gòu)建項(xiàng)目和打包發(fā)布上有所區(qū)別,同時(shí)官方提供了將Devkit遷移至Gradle的方式。
參考:https://plugins.jetbrains.com/docs/intellij/developing-plugins.html - 框架入口:一個(gè) IDEA 插件開(kāi)發(fā)完,要考慮把它嵌入到哪,比如是從 IDEA 窗體的 Edit、Tools 等進(jìn)入配置還是把窗體嵌入到左、右工具條還是IDEA窗體下的對(duì)話框。
- UI:思考的是窗體需要用到什么語(yǔ)言開(kāi)發(fā),沒(méi)錯(cuò),用的就是 Swing、Awt 的技術(shù)能力。
- API:在 IDEA 插件開(kāi)發(fā)中,一般都是圍繞工程進(jìn)行的,那么基本要從通過(guò) IDEA 插件 JDK 開(kāi)發(fā)能力中獲取到工程信息、類信息、文件信息等。
- 外部功能:這一個(gè)是用于把插件能力與外部系統(tǒng)結(jié)合,比如你是需要把拿到的接口上傳到服務(wù)器,還是從遠(yuǎn)程下載文件等等。
2)Gradle創(chuàng)建
新版通過(guò) New-> Project->IDE Plugin進(jìn)行創(chuàng)建,舊版通過(guò)New Project->Gradle->IntelliJ Platform Plugin進(jìn)行創(chuàng)建。
項(xiàng)目結(jié)構(gòu)如下:
3)配置介紹
plugin.xml
<!DOCTYPE idea-plugin PUBLIC "Plugin/DTD" "http://xxxx">
<idea-plugin>
<!-- 插件唯一id,不能和其他插件項(xiàng)目重復(fù),所以推薦使用包名+插件名com.xxx.xxx的格式
插件不同版本之間不能更改,若沒(méi)有指定,則與name相同 -->
<id> com.your.company.unique.plugin.id </id>
<!-- 插件名稱,別人在官方插件庫(kù)搜索你的插件時(shí)使用的名稱 -->
<name> Plugin display name here </name>
<!-- 插件版本,格式:BRANCH.BUILD.FIX (MAJOR.MINOR.FIX) -->vs
<version>1.0.0</version>
<!-- 供應(yīng)商主頁(yè)和email(不能使用默認(rèn)值,必須修改成自己的)-->
<vendor email="support@yourcompany.com" url="https://www.yourcompany.com">YourCompany</vendor>
<!-- 插件的描述 (不能使用默認(rèn)值,必須修改成自己的。并且需要大于40個(gè)字符)-->
<description><![CDATA[
Enter short description for your plugin here.<br>
<em>most HTML tags may be used</em>
]]></description>
<!-- 插件版本變更信息,使用<![CDATA[ ]]> 來(lái)支持HTML格式;
將展示在 settings | Plugins 對(duì)話框和插件倉(cāng)庫(kù)的Web頁(yè)面 -->
<change-notes><![CDATA[
<p>
<li>1.0.0</li>
<ul>
<li>
1.新增xxx功能 <br/>
2.優(yōu)化xxx功能 <br/>
</li>
</ul>
</p>
]]>
</change-notes>
<!-- please see http://confluence.jetbrains.net/display/IDEADEV/Build+Number+Ranges for description -->
<!-- 插件兼容構(gòu)建的IDE版本, until-build可以不寫(xiě),默認(rèn)到最新版 -->
<idea-version since-build="203.4818.26" until-build="211"/>
<!-- please see http://confluence.jetbrains.net/display/IDEADEV/Plugin+Compatibility+with+IntelliJ+Platform+Products
on how to target different products -->
<!-- 插件依賴,可以依賴模塊或插件 -->
<depends>com.intellij.modules.lang</depends>
<depends>Git4Idea</depends>
<depends optional="true" config-file="plugin-maven.xml">org.jetbrains.idea.maven</depends>
<!—idea第一次打開(kāi), 實(shí)際上就是訂閱了應(yīng)用程序打開(kāi)的事件-->
<application-components>
<component>
<implementation-class>xxxxx</implementation-class>
</component>
</application-components>
<!—打開(kāi)項(xiàng)目 -->
<project-components>
<component>
<implementation-class>
xxxxx
</implementation-class>
</component>
</project-components>
<!-- 插件定義的擴(kuò)展點(diǎn),以供其他插件擴(kuò)展該插件,類似Java的抽象類的功能
如何在https://plugins.jetbrains.com/docs/intellij/plugin-extensions.html -->
<extensionPoints>
</extensionPoints>
<!-- 聲明該插件對(duì)IDEA core或其他插件的擴(kuò)展,Ns是NameSpace的縮寫(xiě) -->
<extensions defaultExtensionNs="com.intellij">
<toolWindow id="代碼評(píng)審" icon="/icons/xx_13x13.png" anchor="bottom" factoryClass="xxx" />
</extensions>
<!-- 編寫(xiě)插件動(dòng)作 https://plugins.jetbrains.com/docs/intellij/plugin-actions.html-->
<actions>
<action id="com.xx.xx.AddCommentAction"
class="com.xx.xx.actions.AddCommentAction"
text="添加評(píng)論"
description="為選中的代碼添加評(píng)論意見(jiàn)"
icon="AllIcons.Actions.StartDebugger">
<!—編輯器右鍵彈出菜單--!>
<add-to-group group-id="EditorPopupMenu" anchor="first"/>
<!--快捷方式--!>
<keyboard-shortcut first-keystroke="alt X" keymap="$default"/> </action>
</action>
</actions>
</idea-plugin>
4)插件運(yùn)行調(diào)試打包安裝
Gradle構(gòu)建方式進(jìn)行調(diào)試打包安裝
運(yùn)行/調(diào)試:runIde 可以選擇Debug模式或者是Run模式
打包
安裝:可以將打的包發(fā)布市場(chǎng)(本地idea配置插件倉(cāng)庫(kù)),從Marketplace搜索插件或者是直接從Settings->plugins->Install->Install Plugin from Disk安裝
步驟2:研究Gerrit插件源碼,搞清楚整理開(kāi)發(fā)流程和模塊
步驟3:基于Gerrit插件規(guī)劃VCR插件模塊,增加clone、branch、mergeRequest、VCR模塊,并對(duì)各組件增強(qiáng)
步驟4:定制原有流程模塊push,自動(dòng)化關(guān)聯(lián)工作項(xiàng)
在使用Git依賴插件之前,先了解一下插件的擴(kuò)展以及擴(kuò)展點(diǎn)(Extensions、Extension Points)。
Intellij 平臺(tái)提供了允許一個(gè)插件與其他插件或者 IDE 交互的 extensions 以及 extension points 的概念。
- Extension Points:如果你想要你的插件可以被其他插件使用,那么你必須在你的插件內(nèi)聲明一個(gè)或多個(gè)擴(kuò)展點(diǎn)(extension points)。每個(gè)擴(kuò)展點(diǎn)定義了允許訪問(wèn)這個(gè)點(diǎn)的類或者接口。
- Extensions:如果你想要你的插件擴(kuò)展其他插件或者 Intellij 平臺(tái),你必須聲明一個(gè)或多個(gè) extensions。
可以在 plugin.xml 中的和塊中定義 extensions 以及 extension points。
plugin.xml
<!--依賴插件包--!>
<depends>Git4Idea</depends>
<!—idea第一次打開(kāi), 實(shí)際上就是訂閱了應(yīng)用程序打開(kāi)的事件-->
<application-components>
<component>
<implementation-class>com.demo.intellij.plugin.vcr.push.VcrPushExtension$Proxy</implementation-class>
</component>
</application-components>
上述我們看到依賴的Git4Idea 包,如果我們想修改原生的的Git,先看下push依賴包中如何實(shí)現(xiàn)的。
Git4Idea(plugin.xml)
<extensions defaultExtensionNs="com.intellij">
<pushSupport implementation="git4idea.push.GitPushSupport"/>
...
</extensions>
intellij-dvcs.jar(plugin.xml)
<extensionPoints>
<extensionPoint name="pushSupport"
interface="com.intellij.dvcs.push.PushSupport"
area="IDEA_PROJECT"
dynamic="true"/>
....
</extensionPoints>
從上述可看到,Git4Idea 的GitPushSupport擴(kuò)展實(shí)現(xiàn)push的功能點(diǎn),接下來(lái)我們主要對(duì)GitPushSupport進(jìn)行javassist字節(jié)碼修改以達(dá)到擴(kuò)展git push組件能力。
擴(kuò)展使用GitPushSupport之前,需要將需要的類進(jìn)行裝載至GitPlugin中,然后再對(duì)GitPushSupport進(jìn)行字節(jié)碼改造,至此對(duì)git Push原生插件頁(yè)進(jìn)行改造。
步驟5:使用樹(shù)狀列表模式,展示一次push請(qǐng)求VCR提交內(nèi)容及多個(gè)CR情況
主要是實(shí)現(xiàn)JTreeTable,對(duì)VCR與CR進(jìn)行管理。
一次評(píng)審請(qǐng)求VCR包含所有CR的提交變更記錄,可針對(duì)該變更記錄進(jìn)行代碼評(píng)審,單個(gè)CR也可以進(jìn)行評(píng)審。
步驟6:展示變更文件視圖及定制評(píng)論展示模塊,精準(zhǔn)定位代碼
代碼評(píng)審主要根據(jù)編輯器獲取代碼行及位置,評(píng)論可精準(zhǔn)定位到代碼行。
1)changeBrowser變更視圖展示VCR變更文件信息
2)雙擊文件,diff視圖展示inline和side-by-side兩種代碼差異
聲明擴(kuò)展,針對(duì)擴(kuò)展類進(jìn)行定制化改造。
plugin.xml
<diff.DiffTool implementatinotallow="com.demo.intellij.plugin.vcr.ui.diff.VcrCommentsDiffTool$Proxy"/>
3)添加代碼塊評(píng)論,定位代碼塊
AddCommentAction.java
public class AddCommentAction extends AnAction implements DumbAware {
public AddCommentAction(String label,
Icon icon,
CommentsDiffTool commentsDiffTool,
Editor editor,
List<CommentInfo> fileComments
....
) {
super(label, null, icon);
}
private CommentInput createComment() {
//獲取用戶選擇代碼位置位置
//行的情況下,默認(rèn)是開(kāi)頭和行結(jié)束 得到光標(biāo)的位置caretModel.getOffset();
/*取到插字光標(biāo)模式對(duì)象 CaretModel caretModel = editor.getCaretModel();
得到光標(biāo)的位置int caretOffset = caretModel.getOffset();
//得到一行開(kāi)始和結(jié)束的地方
int lineNum = document.getLineNumber(caretOffset);
int lineStartOffset = document.getLineStartOffset(lineNum);
int lineEndOffset = document.getLineEndOffset(lineNum);
獲取一行內(nèi)容String lineContent = document.getText(new TextRange(lineStartOffset, lineEndOffset));
*/
Document document = editor.getDocument();
int lineNum = document.getLineNumber(editor.getCaretModel().getOffset()) ;
int lineStartOffset = document.getLineStartOffset(lineNum);
int lineEndOffset = document.getLineEndOffset(lineNum);
String lineContent = document.getText(new TextRange(lineStartOffset, lineEndOffset));
.....
}
}
所有評(píng)論展示列表如何精準(zhǔn)定位代碼
SafeHtmlHistoryComments.java
public class SafeHtmlHistoryComments extends JPanel {
private Iterable<CommentInfo> fileComments;
private List<CommentInfo> commentInfos = new ArrayList<>();
private CommentInfo currentCommentInfo;
private SelectedComment selectedComment;
private SelectedComment operatorSelectedComment;
private Editor editor;
public SafeHtmlHistoryComments(Editor editor,Iterable<CommentInfo> fileComments, Comment selectedComment) {
super(new BorderLayout());
....
HistoryCommentListPanel historyCommentListPanel = new HistoryCommentListPanel(fileComments);
//雙擊table某行觸發(fā)代碼定位
historyCommentListPanel.addTableMouseDoubleHit(new Consumer<CommentInfo>() {
@Override
public void consume(CommentInfo commentInfo) {
codeTextHit(editor,commentInfo);
}
});
}
/**
* 定位代碼
* @param editor
* @param commentInfo
*/
private static void codeTextHit(Editor editor, CommentInfo commentInfo) {
SelectionModel selectionModel = editor.getSelectionModel();
// 優(yōu)化:如果文件修改過(guò)了,則不進(jìn)行選中操作,換為提示
if (null != commentInfo.startIndex && null != commentInfo.endIndex && commentInfo.startIndex != 0 && commentInfo.endIndex != 0) {
editor.getCaretModel().moveToOffset(commentInfo.endIndex);
selectionModel.setSelection(commentInfo.startIndex, commentInfo.endIndex);
} else if (null != commentInfo.line && commentInfo.line != 0) {
int lineNum = commentInfo.line - 1;
editor.getCaretModel().moveToOffset(lineNum);
CharSequence charsSequence = editor.getMarkupModel().getDocument().getCharsSequence();
if(null!=commentInfo.range) {
RangeUtils.Offset offset = RangeUtils.rangeToTextOffset(charsSequence, commentInfo.range);
selectionModel.setSelection(offset.start, offset.end);
}else{
Document document = editor.getDocument();
int lineStartOffset = document.getLineStartOffset(lineNum);
int lineEndOffset = document.getLineEndOffset(lineNum);
selectionModel.setSelection(lineStartOffset, lineEndOffset);
}
}
editor.getScrollingModel().scrollToCaret(ScrollType.MAKE_VISIBLE);
}
....
}
六、未來(lái)展望
6.1 自動(dòng)化代碼評(píng)審
- 代碼提交評(píng)審或代碼合并之前,先自動(dòng)化檢查(Sonar/安全掃描)快速發(fā)現(xiàn)并糾正潛在問(wèn)題,檢查成功后提交評(píng)審。
- 代碼評(píng)審?fù)ㄟ^(guò)之后,結(jié)合流水線,自定義部署構(gòu)建策略,實(shí)現(xiàn)快速迭代。
- 自動(dòng)匯聚測(cè)試報(bào)告,根據(jù)評(píng)審問(wèn)題類型進(jìn)行分類,不斷改進(jìn)Sonar檢查規(guī)則,從而形成良性循環(huán)。
6.2智能化代碼評(píng)審
- 提交代碼評(píng)審之后,通過(guò)AI大模型對(duì)代碼進(jìn)行綜合評(píng)價(jià),并給出建議。
- 通過(guò)智能代碼評(píng)審,產(chǎn)生評(píng)審報(bào)告,并進(jìn)行智能化分析。