毒瘤還是銀彈--低代碼與傳統研發模式案例對比

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"低代碼技術最近火了,火到什麼程度?火到行業大佬親自下場打口水仗的地步。大佬們言之鑿鑿,喫瓜羣衆則看得心驚肉跳,低代碼這個坑到底要不要跳,剛伸出去的腳又罵罵咧咧的縮了回來。恰巧最近看到一篇","attrs":{}},{"type":"link","attrs":{"href":"https://tech.meituan.com/2020/03/19/design-pattern-practice-in-marketing.html","title":null,"type":null},"content":[{"type":"text","text":"設計模式在外賣營銷業務中的實踐","attrs":{}}]},{"type":"text","text":",文章1萬5千字,閱讀需要半小時,裏面非常詳細的介紹瞭如何基於設計模式等技術構建一套營銷系統。我將用這個案例演示利用低代碼工具如何完成同樣的功能,從而讓讀者對基於低代碼的研發過程與效率差異有一個感性的認識,從而獲得自己的結論。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" ","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://tech.meituan.com/2020/03/19/design-pattern-practice-in-marketing.html","title":null,"type":null},"content":[{"type":"text","text":"設計模式在外賣營銷業務中的實踐","attrs":{}}]},{"type":"text","text":"介紹了外賣營銷業務裏面的三個子業務,以低代碼的視角來看,第一個與第三個屬於一類問題,因此本文會以第一個和第二個舉例,第三個省略。每個業務先介紹需求,業務模型和原做法,再介紹低代碼的做法。最後是關於傳統方式與低代碼方式的對比總結。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"邀請下單需求","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"“邀請下單”是外賣用戶邀請其他用戶下單後給予獎勵的平臺。即用戶A邀請用戶B,並且用戶B下單後,給予用戶A一定的現金獎勵(以下簡稱返獎)。返獎會有多個計算策略。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" ","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"業務模型","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" ","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/2f/2fc3b5eb4a20c7cd8c58ef9010d2b3dc.jpeg","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"對於滿足返獎條件的用戶,根據新老用戶分類和邀新效果分爲普通獎勵,梯度獎和多種返獎機制。計算完獎勵金額以後,需更新用戶的獎金信息,並進行結算服務。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"基於工廠與策略模式的原做法","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"將返獎規則抽象爲返獎策略,將不同用戶類型對應的不同返獎方案,實現爲不同的返獎策略。返獎策略作爲值對象通過工廠模式根據用戶類型生成。下面是對應工廠模式和策略模式的設計。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/cf/cfdfa0fd31e272bff54478cd7ffec999.jpeg","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/cf/cfdfa0fd31e272bff54478cd7ffec999.jpeg","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"原文給出的部分示例源碼,貼在這裏做參考。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"策略與工廠定義:","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/10/1078c05818189713b5222fa1c00e2cd0.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上下文如何使用策略:","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/6a/6a7891ab261074c8ca3b00cbc2c4e899.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"注:這裏的insertRewardAndSettlement的調用寫成了定義,應該是原文作者筆誤。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"整體使用流程:","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/8e/8ebac036cc02230414c199945a241c37.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"基於流程處理框架的低代碼做法","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"原需求是一個標準的處理流程,可以用面向流程處理的低代碼框架。用低代碼框架x-series中的流程圖組件xunit來實現的話。只需先照着業務模型畫一遍流程圖,再爲可視化單元關聯業務代碼就行。我們再複習一遍原始需求:","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/2f/2fc3b5eb4a20c7cd8c58ef9010d2b3dc.jpeg","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"首先用xunit創建對應流程圖,這裏還補充了需求中沒有明確說明的不能返獎流程:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/a0/a059bfdcc4d47dc261057d2a74b275b1.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"雖然已經很清晰,但還是簡要介紹一下處理流程:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"1.      系統首先判斷是否要返獎,返獎走可以返獎分支,不能返獎走遺憾通知分支;","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2.      返獎分支根據用戶類型進行判斷,新老用戶根據類型對應到不同的處理分支後,統一進行更新獎金並結算。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" ","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"接下來爲各個節點提供具體代碼。Xunit框架預定義了一套面向流程處理的函數式接口, 並會保證執行順序和畫出來的流程圖完全一致,研發人員只需要提供整個返獎邏輯中最核心的分支判斷和返獎處理邏輯即可。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"爲方便理解,先提供返獎流程處理的上下文","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/0e/0e916e3529621a2ac44990b735e641ec.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"判斷能否返獎,實現Validator接口","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/a0/a00caa39566e673a9b179820261cc440.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"根據用戶類型選擇具體返獎處理分支,實現Locator接口","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/d2/d273cf28b9e4b0f0aa427725d28da990.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"每個用戶類型對應的返獎處理,以新用戶普通返獎爲例,其他用戶類型類似,實現Processor接口:","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/19/19cff6a614b2fcedfa9ee3199b2c4af7.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"更新獎金並結算處理略。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"調用這個模塊的代碼如下:","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/01/01d1a2e1666a8951c7a9016686ea3504.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"返獎流程需求","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"業務模型","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/a6/a62105106c45c8a87f642b4b3f69dff0.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"基於狀態機模式的原做法","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通用狀態機的類圖:","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/1e/1eb685f8817fcbd73af72d05245b4778.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"原文給出的狀態機代碼太長了,而且和返獎業務緊密耦合在一起了,爲節約篇幅這裏我就不再重複。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"基於狀態機的低代碼做法","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"既然原需求是個狀態機,那麼我們就用狀態機低代碼框架來實現。用低代碼框架x-series中的狀態機組件xstate照着業務模型直接畫個狀態機就是我們要做的全部工作:","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/91/9178b77b94a54b8e84acdde976b5eb4d.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Xstate會基於返獎流程的業務狀態機模型和事件來自動處理狀態變遷。如果狀態變遷沒有其他額外的檢測條件,無需提供任何代碼。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"返獎流程狀態機的使用方法如下:","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/17/1709d2073e728245896ff330ca3452ad.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"看到這裏,有沒有一種開發也可以這麼玩的感覺?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" ","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在總結低代碼優勢之前,我們先聊聊:","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"軟件研發的現實","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"學習門檻高","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"原文簡要介紹了傳統的系統設計思想以及做法,涉及到的工作有業務抽象識別,模式選擇,數據類型定義,接口設計,接口實現與邏輯整合等等。毫無疑問是好方法,但問題是概念抽象本質上是一種反直覺的思維方式,一個工程師需要大量的學習與練習才能熟練掌握。即使你懂了,你的隊友也懂嗎?這直接導致下一個問題。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"缺少合格人員","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"雖然很殘酷,但工作中能搞定面向對象設計的人都很少。我不否認有的優秀團隊裏面每個人都很強,但這是豪華配置,不是每個公司都玩得起的。絕大多數研發乾的其實就是將業務邏輯翻譯爲代碼,根本談不上設計。那種寫的又臭又長的程序我相信每位都看過,而這又導致下一個問題。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"可理解性與可維護性差","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可理解性與可維護性是個一體兩面的概念。如果把代碼比作樹葉,那麼系統就是森林。由於大多數情況下,除了代碼,我們缺乏清晰,準確,一致的設計文檔。只有花大量的時間去讀源碼,可以想象用這種方式去理解系統全貌會有多困難。一個無法難以理解的系統自然難以維護。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"低代碼的優勢","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"學習門檻低","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"從上面我給出的低代碼示例可以看出,低代碼框架一般來說會提供可視化界面,並且產出物會和需求原貌高度一致。由於有視覺引導,學習起來非常簡單,就像手機應用一般,基本上按直覺或常識操作即可。用好低代碼/無代碼工具的關鍵是要搞清楚業務需求到底可以套用那種類型的模型,以及對應這種模型有哪些可用的低代碼框架。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"可理解性好","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"好到什麼程度?由於與需求原貌高度一致,即使是產品經理可以非常容易的理解並驗證研發人員給出的設計是否正確。更不用談程序員了。與此相反,基於傳統方法產出的類圖,時序圖等設計文檔,即使有,產品經理也不大可能看得懂,自然也無法有效評審。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"可維護性好","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可視化模型讓研發人員理解系統的難度大大降低,即使很長時間不碰系統,再熟悉起來所花的時間也很少。而且在必要的場景下,這些可視化模型還能直接關聯到代碼,這一點對於系統的維護尤其有利,可以極大的減輕理解系統所耗費的心智負擔。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"其他優勢","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"由於低代碼框架本身會提供底層或編織層面的能力,開發系統僅需提供特定接口的業務邏輯,開發相同功能所需的代碼量遠遠小於傳統方式。良好的接口設計和較低代碼量也方便了研發人員和測試人員開展單元測試和白盒測試。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"除此以外,由於可視化模型本身具備的自解釋性,使其天然具備文檔屬性,從而可以大幅減少手工編寫文檔的工作量。比較成熟的低代碼框架,例如x-series,都會直接基於模型來運行系統,無需經歷基於模型生成代碼這一步,也就不存在模型與實際運行代碼不一致的情況,消除了文檔更新維護成本。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"關於低代碼的思考","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"低代碼並不是要否認傳統的系統設計與開發方式。傳統方式提供了一套完美的方法論,學好了當然有用。但僅僅依靠傳統方式,而沒有現代化的工具輔助的話是遠遠不夠的。否則軟件危機也不會一直拖到今天都沒有消失。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"跟傳統方式相比,低代碼/無代碼的學習難度和研發效率在特定領域裏具備明顯優勢,有利於大規模推廣。兩者的區別有點類似於弓與弩的區別,普通人想要通過練習成爲弓箭高手很難,但弩的話,掌握起來容易很多。在古代,官府對民間的態度是禁弩不禁弓的,因爲弓難以掌握。但當今社會,我們不應該讓學習門檻成爲提升效率的阻礙,而低代碼就是軟件工程之弩,就是銀彈。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我也不認爲低代碼會導致能力較弱的程序員被替代,道理很簡單。如果低代碼都能讓普通人具備開發能力,那麼受過專門訓練的程序員就更不在話下。而那些能力本來就很強的,用低代碼/無代碼只會如虎添翼,因爲他們終於可以把精力集中在真正值得突破的技術難點,而不是重複性的基礎建設上,這纔是高手的正確用法。低代碼能讓高級程序員變爲超級程序員,讓普通程序員變爲高效程序員。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"不要認爲用低代碼就拉低了逼格,因爲簡化纔是真正複雜的事。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" ","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"參考文檔:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://github.com/hejiehui/xross-tools-installer","title":null,"type":null},"content":[{"type":"text","text":"開源低代碼框架x-series","attrs":{}}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://xie.infoq.cn/article/cf8e8026568e39cdb09b1f4e2","title":null,"type":null},"content":[{"type":"text","text":"關於軟件設計的錯誤假設","attrs":{}}]}]}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章