投稿文章,作者:一縷殤流化隱半邊冰霜(@halfrost) 前言 眾所周知,,現(xiàn)在App的競(jìng)爭(zhēng)已經(jīng)到了用戶體驗(yàn)為王,,質(zhì)量為上的白熱化階段,。用戶們都是很挑剔的,。如果一個(gè)公司的推廣團(tuán)隊(duì)好不容易砸了重金推廣了一個(gè)APP,,好不容易有了一些用戶,,由于一次線上的bug導(dǎo)致一批的用戶在使用中紛紛出現(xiàn)閃退bug,,輕則,很可能前期推廣砸的錢都白費(fèi)了,,重則,,口碑不好,未來(lái)也提升不起用戶量來(lái)了,。靜下心來(lái)分析一下問(wèn)題的原因,,無(wú)外乎就是質(zhì)量沒(méi)有過(guò)關(guān)就上線了。除去主觀的一些因素,,很大部分的客觀因素我覺(jué)得可以被我們防范的,。根據(jù)大神們提出的一套開發(fā)規(guī)范建議,CI + FDD,,就可以幫助我們極大程度的解決客觀因素,。本文接下來(lái)主要討論 Continuous Integration 持續(xù)集成(簡(jiǎn)稱CI) 目錄 1.為什么我們需要持續(xù)集成 2.持續(xù)化集成工具——Jenkins 3.iOS自動(dòng)化打包命令——xcodebuild + xcrun 和 fastlane - gym 命令 4.打包完成自動(dòng)化上傳 fir / 蒲公英 第三方平臺(tái) 5.完整的持續(xù)集成流程 6.Jenkins + Docker 一、為什么我們需要持續(xù)集成 談到為什么需要的問(wèn)題,,我們就需要從什么是來(lái)說(shuō)起,。那什么是持續(xù)集成呢。 持續(xù)集成是一種軟件開發(fā)實(shí)踐:許多團(tuán)隊(duì)頻繁地集成他們的工作,,每位成員通常進(jìn)行日常集成,,進(jìn)而每天會(huì)有多種集成。每個(gè)集成會(huì)由自動(dòng)的構(gòu)建(包括測(cè)試)來(lái)盡可能快地檢測(cè)錯(cuò)誤,。許多團(tuán)隊(duì)發(fā)現(xiàn)這種方法可以顯著的減少集成問(wèn)題并且可以使團(tuán)隊(duì)開發(fā)更加快捷,。 CI是一種開發(fā)實(shí)踐。實(shí)踐應(yīng)該包含3個(gè)基本模塊,,一個(gè)可以自動(dòng)構(gòu)建的過(guò)程,,自動(dòng)編譯代碼,可以自動(dòng)分發(fā),,部署和測(cè)試,。一個(gè)代碼倉(cāng)庫(kù),SVN或者Git,。最后一個(gè)是一個(gè)持續(xù)集成的服務(wù)器,。通過(guò)持續(xù)集成,可以讓我們通過(guò)自動(dòng)化等手段高頻率地去獲取產(chǎn)品反饋并響應(yīng)反饋的過(guò)程。 那么持續(xù)集成能給我們帶來(lái)些什么好處呢,?這里推薦一篇文章,,文章中把Continuous integration (CI) and test-driven development (TDD)分成了12個(gè)步驟。然而帶來(lái)的好處成倍增加,,有24點(diǎn)好處,。 我來(lái)說(shuō)說(shuō)用了CI以后帶來(lái)的一些深有體會(huì)的優(yōu)點(diǎn)。 1. 縮減開發(fā)周期,,快速迭代版本 每個(gè)版本開始都會(huì)估算好開發(fā)周期,,但是總會(huì)因?yàn)楦鞣N事情而延期。這其中包括了一些客觀因素,。由于產(chǎn)品線增多,,迭代速度越來(lái)越快,給測(cè)試帶來(lái)的壓力也越來(lái)越大,。如果測(cè)試都在開發(fā)完全開發(fā)完成之后再來(lái)測(cè)試,,那就會(huì)影響很長(zhǎng)一段時(shí)間。這時(shí)候由于集成晚就會(huì)嚴(yán)重拖慢項(xiàng)目節(jié)奏,。如果能盡早的持續(xù)集成,,盡快進(jìn)入上圖的12步驟的迭代環(huán)中,就可以盡早的暴露出問(wèn)題,,提早解決,,盡量在規(guī)定時(shí)間內(nèi)完成任務(wù)。 2. 自動(dòng)化流水線操作帶來(lái)的高效 其實(shí)打包對(duì)于開發(fā)人員來(lái)說(shuō)是一件很耗時(shí),,而且沒(méi)有很大技術(shù)含量的工作,。如果開發(fā)人員一多,相互改的代碼沖突的幾率就越大,,加上沒(méi)有產(chǎn)線管理機(jī)制,,代碼倉(cāng)庫(kù)的代碼質(zhì)量很難保證。團(tuán)隊(duì)里面會(huì)花一些時(shí)間來(lái)解決沖突,,解決完了沖突還需要自己手動(dòng)打包,。這個(gè)時(shí)候如果證書又不對(duì),又要耽誤好長(zhǎng)時(shí)間,。這些時(shí)間其實(shí)可以用持續(xù)集成來(lái)節(jié)約起來(lái)的,。一天兩天看著不多,但是按照年的單位來(lái)計(jì)算,,可以節(jié)約很多時(shí)間,! 3. 隨時(shí)可部署 有了持續(xù)集成以后,我們可以以天為單位來(lái)打包,,這種高頻率的集成帶來(lái)的最大的優(yōu)點(diǎn)就是可以隨時(shí)部署上線。這樣就不會(huì)導(dǎo)致快要上線,到處是漏洞,,到處是bug,,手忙腳亂弄完以后還不能部署,嚴(yán)重影響上線時(shí)間,。 4. 極大程度避免低級(jí)錯(cuò)誤 我們可以犯錯(cuò)誤,,但是犯低級(jí)錯(cuò)誤就很不應(yīng)該。這里指的低級(jí)錯(cuò)誤包括以下幾點(diǎn):編譯錯(cuò)誤,,安裝問(wèn)題,,接口問(wèn)題,性能問(wèn)題,。 以天為單位的持續(xù)集成,,可以很快發(fā)現(xiàn)編譯問(wèn)題,自動(dòng)打包直接無(wú)法通過(guò),。打完包以后,,測(cè)試掃碼無(wú)法安裝,這種問(wèn)題也會(huì)立即被暴露出來(lái),。接口問(wèn)題和性能問(wèn)題就有自動(dòng)化測(cè)試腳本來(lái)發(fā)現(xiàn),。這些低級(jí)問(wèn)題由持續(xù)集成來(lái)暴露展現(xiàn)出來(lái),提醒我們避免低級(jí)錯(cuò)誤,。 二,、持續(xù)化集成工具——Jenkins Jenkins 是一個(gè)開源項(xiàng)目,提供了一種易于使用的持續(xù)集成系統(tǒng),,使開發(fā)者從繁雜的集成中解脫出來(lái),,專注于更為重要的業(yè)務(wù)邏輯實(shí)現(xiàn)上。同時(shí) Jenkins 能實(shí)施監(jiān)控集成中存在的錯(cuò)誤,,提供詳細(xì)的日志文件和提醒功能,,還能用圖表的形式形象地展示項(xiàng)目構(gòu)建的趨勢(shì)和穩(wěn)定性。 根據(jù)官方定義,,Jenkins有以下的用途:
關(guān)于這4點(diǎn),,實(shí)際使用中還是比較方便的: 1.構(gòu)建項(xiàng)目自動(dòng)化打包可以省去開發(fā)人員好多時(shí)間,重要的是,,Jenkins為我們維護(hù)了一套高質(zhì)量可用的代碼,,而且保證了一個(gè)純凈的環(huán)境。我們經(jīng)常會(huì)出現(xiàn)由于本地配置出錯(cuò)而導(dǎo)致打包失敗的情況?,F(xiàn)在Jenkins就是一個(gè)公平的評(píng)判者,,它無(wú)法正確的編譯出ipa,那就是有編譯錯(cuò)誤或者配置問(wèn)題,。開發(fā)人員沒(méi)必要去爭(zhēng)論本地是可以運(yùn)行的,,拉取了誰(shuí)誰(shuí)誰(shuí)的代碼以后就不能運(yùn)行了,。共同維護(hù)Jenkins的正常編譯,因?yàn)镴enkins的編譯環(huán)境比我們本地簡(jiǎn)單的多,,它是最純凈無(wú)污染的編譯環(huán)境,。開發(fā)者就只用專注于編碼。這是給開發(fā)者帶來(lái)的便利,。 2.這個(gè)可以用來(lái)自動(dòng)化測(cè)試,。在本地生成大批的測(cè)試用例。每天利用服務(wù)器不斷的跑這些用例,。每天每個(gè)接口都跑一遍,。看上去沒(méi)必要,,但是實(shí)際上今天運(yùn)行正常的系統(tǒng),,很可能由于今天的代碼改動(dòng),明天就出現(xiàn)問(wèn)題了,。有了Jenkins可以以天為單位的進(jìn)行回歸測(cè)試,,代碼只要有改動(dòng),Jenkins就把所有的回歸測(cè)試的用例全部都跑一遍,。在項(xiàng)目工期緊張的情況下,,很多情況測(cè)試都不是很重視回歸測(cè)試,畢竟很可能測(cè)一遍之后是徒勞的“無(wú)用功”,。然而由于回歸測(cè)試不及時(shí),,就導(dǎo)致到最后發(fā)版的時(shí)候系統(tǒng)不可用了,這時(shí)候回頭查找原因是比較耗時(shí)的,,查看提交記錄,,看到上百條提交記錄,排查起來(lái)也是頭疼的事情,。以天為單位的回歸測(cè)試能立即發(fā)現(xiàn)問(wèn)題,。測(cè)試人員每天可以專注按單元測(cè)試,一周手動(dòng)一次回歸測(cè)試,。這是給測(cè)試者帶來(lái)的便利,。 3.這個(gè)是靜態(tài)代碼分析,可以檢測(cè)出很多代碼的問(wèn)題,,比如潛在的內(nèi)存泄露的問(wèn)題,。由于Jenkins所在環(huán)境的純凈,還是可以發(fā)現(xiàn)一些我們本地復(fù)雜環(huán)境無(wú)法發(fā)現(xiàn)的問(wèn)題,,進(jìn)一步的提高代碼質(zhì)量,。這是給質(zhì)檢帶來(lái)的便利。 4.隨時(shí)部署,。Jenkins在打包完成之后可以設(shè)定之后的操作,,這個(gè)時(shí)候往往就是提交app到跑測(cè)試用例的系統(tǒng),,或者部署到內(nèi)測(cè)平臺(tái)生成二維碼。部署中不能安裝等一些低級(jí)問(wèn)題隨之立即暴露,。測(cè)試人員也只需要掃一下二維碼即可安裝,,很方便,。這也算是給測(cè)試帶來(lái)的便利,。 以下的例子以2016-07-24 22:35的Weekly Release 2.15的版本為例。 我們來(lái)開始安裝Jenkins,。從官網(wǎng)https:/// 上下載最新的pkg安裝包,。 也可以下載jenkins.war, 然后運(yùn)行Java -jar jenkins.war,進(jìn)行安裝,。 安裝完成之后,,Safari可能會(huì)自動(dòng)打開,如果沒(méi)有自動(dòng)打開,,打開瀏覽器,,輸入http://localhost:8080 這個(gè)時(shí)候可能會(huì)報(bào)一個(gè)錯(cuò)誤。如果出現(xiàn)了這面的問(wèn)題,。出現(xiàn)這個(gè)問(wèn)題的原因就是Java環(huán)境有問(wèn)題,,重新Java環(huán)境即可。 這個(gè)時(shí)候如果你重啟電腦會(huì)發(fā)現(xiàn)Jenkins給你新增了一個(gè)用戶,,名字就叫Jenkins,,不過(guò)這個(gè)時(shí)候你不知道密碼。你可能會(huì)去試密碼,,肯定是是不對(duì)的,,因?yàn)槌跏济艽a很復(fù)雜。這個(gè)時(shí)候正確做法是打開http://localhost:8080 會(huì)出現(xiàn)下圖的重設(shè)初始密碼的界面,。 按照提示,,找到/Users/Shared/Jenkins/Home/ 這個(gè)目錄下,這個(gè)目錄雖然是共享目錄,,但是有權(quán)限的,,非Jenkins用戶/secrets/目錄是沒(méi)有讀寫權(quán)限的。 打開initialAdminPassword文件,,復(fù)制出密碼,,就可以填到網(wǎng)頁(yè)上去重置密碼了。如下圖 一路安裝過(guò)來(lái),,輸入用戶名,,密碼,郵件這些,,就算安裝完成了,。 還是繼續(xù)登錄localhost:8080 ,,選擇“系統(tǒng)管理”——“管理插件”,我們要先安裝一些輔助插件,。 安裝GitLab插件 因?yàn)槲覀冇玫氖荊itLab來(lái)管理源代碼,,Jenkins本身并沒(méi)有自帶GitLab插件,所以我們需要依次選擇 系統(tǒng)管理->管理插件,,在“可選插件”中選中“GitLab Plugin”和“Gitlab Hook Plugin”這兩項(xiàng),,然后安裝。 安裝Xcode插件 同安裝GitLab插件的步驟一樣,,我們依次選擇系統(tǒng)管理->管理插件,,在“可選插件”中選中“Xcode integration”安裝。 安裝完了這個(gè),,我們就可以配置一個(gè)構(gòu)建項(xiàng)目了,。 點(diǎn)擊新建好的項(xiàng)目,進(jìn)來(lái)配置一下General參數(shù),。 這里可以設(shè)置包的保留天數(shù)還有天數(shù),。 接著設(shè)置源碼管理。 由于現(xiàn)在我用到的是GitLab,,先配置SSH Key,,在Jenkins的證書管理中添加SSH。在Jenkins管理頁(yè)面,,選擇“Credentials”,,然后選擇“Global credentials (unrestricted)”,點(diǎn)擊“Add Credentials”,,如下圖所示,,我們填寫自己的SSH信息,然后點(diǎn)擊“Save”,,這樣就把SSH添加到Jenkins的全局域中去了,。 如果正常的配置正確的話,是不會(huì)出現(xiàn)下圖中的那段紅色的警告,。如果有下圖的提示,,就說(shuō)明Jenkins還沒(méi)有連通GitLab或者SVN,那就請(qǐng)?jiān)贆z查SSH Key是否配置正確,。 構(gòu)建觸發(fā)器設(shè)置這里是設(shè)置自動(dòng)化測(cè)試的地方,。這里涉及的內(nèi)容很多,暫時(shí)我也沒(méi)有深入研究,,這里暫時(shí)先不設(shè)置,。有自動(dòng)化測(cè)試需求的可以好好研究研究這里的設(shè)置。 不過(guò)這里有兩個(gè)配置還是需要是配置的 Poll SCM (poll source code management) 輪詢?cè)创a管理 需要設(shè)置源碼的路徑才能起到輪詢的效果,。一般設(shè)置為類似結(jié)果: 0/5 每5分鐘輪詢一次 Build periodically (定時(shí)build) 一般設(shè)置為類似: 00 20 * 每天 20點(diǎn)執(zhí)行定時(shí)build ,。當(dāng)然兩者的設(shè)置都是一樣可以通用的,。 格式是這樣的 分鐘(0-59) 小時(shí)(0-23) 日期(1-31) 月(1-12) 周幾(0-7,0和7都是周日)(更加詳細(xì)的設(shè)置看這里) 構(gòu)建環(huán)境設(shè)置 iOS打包需要簽名文件和證書,所以這部分我們勾選“Keychains and Code Signing Identities”和“Mobile Provisioning Profiles”,。 這里我們又需要用到Jenkins的插件,,在系統(tǒng)管理頁(yè)面,選擇“Keychains and Provisioning Profiles Management”,。 進(jìn)入Keychains and Provisioning Profiles Management頁(yè)面,,點(diǎn)擊“瀏覽”按鈕,,分別上傳自己的keychain和證書,。上傳成功后,,我們?cè)贋閗eychain指明簽名文件的名稱,。點(diǎn)擊“Add Code Signing Identity”,,最后添加成功后如下圖所示: 注意:我第一次導(dǎo)入證書和Provisioning Profiles文件,,就遇到了一點(diǎn)小“坑”,,我當(dāng)時(shí)以為是需要證書,,但是這里需要的Keychain,,并不是cer證書文件。這個(gè)Keychain其實(shí)在/Users/管理員用戶名/Library/keychains/login.keychain,當(dāng)把這個(gè)Keychain設(shè)置好了之后,,Jenkins會(huì)把這個(gè)Keychain拷貝到/Users/Shared/Jenkins/Library/keychains這里,,(Library是隱藏文件)。Provisioning Profiles文件也直接拷貝到/Users/Shared/Jenkins/Library/MobileDevice文件目錄下,。 這樣Adhoc證書和簽名文件就在Jenkins中配置好了,,接下來(lái)我們只需要在item設(shè)置中指定相關(guān)文件即可。 回到我們新建的item,,找到構(gòu)建環(huán)境,,按下圖選好自己的相關(guān)證書和簽名文件。 接下來(lái)在進(jìn)行構(gòu)建的設(shè)置 我們這里選擇執(zhí)行一段打包腳本,。腳本在下一章節(jié)詳細(xì)的講解,。 構(gòu)建后操作 這里我們選擇Execute a set of scripts,這里也是一個(gè)腳本,,這個(gè)腳本用來(lái)上傳自動(dòng)打包好的ipa文件,。腳本在第四章節(jié)有詳細(xì)的講解。 至此,,我們的Jenkins設(shè)置就全部完成了,。點(diǎn)擊構(gòu)建,就會(huì)開始構(gòu)建項(xiàng)目了,。 構(gòu)建一次,,各個(gè)顏色代表的意義如下: 天氣的晴雨表代表了項(xiàng)目的質(zhì)量,這也是Jenkins的一個(gè)特色,。 如果構(gòu)建失敗了,,可以去查看Console Output可以查看log日志,。 三、iOS自動(dòng)化打包命令——xcodebuild + xcrun 和 fastlane - gym 命令 在日常開發(fā)中,,打包是最后上線不可缺少的環(huán)節(jié),,如果需要把工程打包成 ipa 文件,通常的做法就是在 Xcode 里點(diǎn)擊 「Product -> Archive」,,當(dāng)整個(gè)工程 archive 后,,然后在自動(dòng)彈出的 「Organizer」 中進(jìn)行選擇,根據(jù)需要導(dǎo)出 ad hoc,,enterprise 類型的 ipa 包,。雖然Xcode已經(jīng)可以很完美的做到打包的事情,但是還是需要我們手動(dòng)點(diǎn)擊5,,6下,。加上我們現(xiàn)在需要持續(xù)集成,用打包命令自動(dòng)化執(zhí)行就順其自然的需要了,。 1. xcodebuild + xcrun命令 Xcode為我們開發(fā)者提供了一套構(gòu)建打包的命令,,就是xcodebuild 和xcrun命令。xcodebuild把我們指定的項(xiàng)目打包成.app文件,,xcrun將指定的.app文件轉(zhuǎn)換為對(duì)應(yīng)的.ipa文件,。 具體的文檔如下:xcodebuild官方文檔、xcrun官方文檔
上面10個(gè)命令最主要的還是前3個(gè),。 接下來(lái)來(lái)說(shuō)明一下參數(shù): -project -workspace:這兩個(gè)對(duì)應(yīng)的就是項(xiàng)目的名字,。如果有多個(gè)工程,這里又沒(méi)有指定,,則默認(rèn)為第一個(gè)工程,。 -target:打包對(duì)應(yīng)的targets,如果沒(méi)有指定這默認(rèn)第一個(gè),。 -configuration:如果沒(méi)有修改這個(gè)配置,,默認(rèn)就是Debug和Release這兩個(gè)版本,沒(méi)有指定默認(rèn)為Release版本,。 -buildsetting=value ...:使用此命令去修改工程的配置,。 -scheme:指定打包的scheme。 上面這些是最最基本的命令,。 上面10個(gè)命令的第一個(gè)和第二個(gè)里面的參數(shù),,其中 -target 和 -configuration 參數(shù)可以使用 xcodebuild -list 獲得,-sdk 參數(shù)可由 xcodebuild -showsdks 獲得,,[buildsetting=value ...] 用來(lái)覆蓋工程中已有的配置,。可覆蓋的參數(shù)參考官方文檔:Xcode Build Setting Reference。
上面第3個(gè)命令就是專門用來(lái)打帶有Cocopods的項(xiàng)目,,因?yàn)檫@個(gè)時(shí)候項(xiàng)目工程文件不再是xcodeproj了,,而是變成了xcworkspace了。 再來(lái)說(shuō)說(shuō)xcrun命令,。
參數(shù)不多,,使用方法也很簡(jiǎn)單,xcrun -sdk iphoneos -v PackageApplication + 上述一些參數(shù),。 參數(shù)都了解之后,,我們就來(lái)看看該如何用了。下面這個(gè)是使用了xcodebuild + xcrun命令寫的自動(dòng)化打包腳本
2. gym 命令 說(shuō)到gym,,就要先說(shuō)一下fastlane,。 fastlane是一套自動(dòng)化打包的工具集,用 Ruby 寫的,,用于 iOS 和 Android 的自動(dòng)化打包和發(fā)布等工作,。gym是其中的打包命令。 fastlane的官網(wǎng)看這里, fastlane的github看這里,。 要想使用gym,,先要安裝fastlane。
整個(gè)發(fā)布過(guò)程可以用fastlane描述成下面這樣
Ps:這里可能大家還會(huì)聽過(guò)一個(gè)命令叫 xctool xctool是官方xcodebuild命令的一個(gè)增強(qiáng)實(shí)現(xiàn),,輸出的內(nèi)容比xcodebuild直觀可讀得多。通過(guò)brew即可安裝,。
使用gym自動(dòng)化打包,,腳本如下:
四,、打包完成自動(dòng)化上傳 fir / 蒲公英 第三方平臺(tái) 要上傳到 fir / 蒲公英 第三方平臺(tái),都需要注冊(cè)一個(gè)賬號(hào),,獲得token,,之后才能進(jìn)行腳本化操作。 1. 自動(dòng)化上傳fir 安裝fir-clifir的命令行工具 需要先裝好ruby再執(zhí)行
2.自動(dòng)化上傳蒲公英
五,、完整的持續(xù)集成流程 經(jīng)過(guò)上面的持續(xù)化集成,,現(xiàn)在我們就擁有了如下完整持續(xù)集成的流程 六、Jenkins + Docker 關(guān)于Jenkins的部署,,其實(shí)是分以下兩種: 單節(jié)點(diǎn)(Master)部署 這種部署適用于大多數(shù)項(xiàng)目,,其構(gòu)建任務(wù)較輕,數(shù)量較少,單個(gè)節(jié)點(diǎn)就足以滿足日常開發(fā)所需,。 多節(jié)點(diǎn)(Master-Slave)部署 通常規(guī)模較大,,代碼提交頻繁(意味著構(gòu)建頻繁),自動(dòng)化測(cè)試壓力較大的項(xiàng)目都會(huì)采取這種部署結(jié)構(gòu),。在這種部署結(jié)構(gòu)下,,Master通常只充當(dāng)管理者的角色,負(fù)責(zé)任務(wù)的調(diào)度,,slave節(jié)點(diǎn)的管理,,任務(wù)狀態(tài)的收集等工作,而具體的構(gòu)建任務(wù)則會(huì)分配給slave節(jié)點(diǎn),。一個(gè)Master節(jié)點(diǎn)理論上可以管理的slave節(jié)點(diǎn)數(shù)是沒(méi)有上限的,,但通常隨著數(shù)量的增加,其性能以及穩(wěn)定性就會(huì)有不同程度的下降,,具體的影響則因Master硬件性能的高低而不同,。 但是多節(jié)點(diǎn)部署又會(huì)有一些缺陷,當(dāng)測(cè)試用例變得海量以后,,會(huì)造成一些問(wèn)題,,于是有人設(shè)計(jì)出了下面這種部署結(jié)構(gòu),Jenkins + Docker 由于筆者現(xiàn)在的項(xiàng)目還處于單節(jié)點(diǎn)(Master)部署,,關(guān)于多節(jié)點(diǎn)(Master-Slave)部署也沒(méi)有實(shí)踐經(jīng)驗(yàn),,改進(jìn)版本的Docker更是沒(méi)有接觸過(guò),但是如果有這種海量測(cè)試用例,,高壓力的大量復(fù)雜的回歸測(cè)試的需求的,,那推薦大家看這篇文章。 最后 以上就是我關(guān)于Jenkins持續(xù)集成的一次實(shí)踐經(jīng)驗(yàn),。分享給大家,,如果里面有什么錯(cuò)誤,歡迎大家多多指教,。 |
|
來(lái)自: acronym > 《IT技術(shù)》