功能點算法及在軟件測試中的應用


——划分邏輯事務

在前一篇文章我們講到,“邏輯事務”是統計功能點指數的最小單元,所以進行科學的划分,對統計的正確性非常重要。另外,划分邏輯事務其實也是一個需求分解的過程,測試工程師可以通過這個過程來分析項目需求,讓需求分析工作更加標准化,同時也降低溝通成本,大家圍繞邏輯事務進行討論。

邏輯事務一般描述的是用戶的行為,所以命名一般都是動賓結構,例如“注冊淘寶會員”、“查看寶貝的詳情”。也有少數的邏輯事務是由一些定時程序產生的,例如“同步用戶的最新信息”。有的項目會在需求文檔里面專門描述一些“業務規則”,注意,規則不是邏輯事務,規則一定是依附與某個邏輯事務的,例如“不准注冊同名的會員”這個規則其實是屬於“注冊會員”這個邏輯事務。

在以數據庫為基礎的WEB應用中,邏輯事務一定是對某項數據進行的增刪改查操作,也就是我們常說的CRUDL的其中之一。CRUDL分別代表:

  • Create 創建新數據(注冊會員)
  • Read 讀取數據的信息(查看寶貝)
  • Update 更新數據(保存收貨地址)
  • Delete 刪除數據(清空購物車)
  • List 列出一批數據(各種DataGrid)

下面我們對這5種邏輯事務分別進行分析,並且結合具體的案例來看看划分的規則。如果邏輯事務划分正確,后面的計算出現誤差就不會太大。

Create

每個WEB應用程序,都是從創建數據開始的,比如“發布新寶貝”、“注冊新會員”,創建數據會在數據表中新增記錄。創建數據一般由用戶在頁面上點擊按鈕觸發,也可能是在請求一個URL的時候觸發。

有時候,用戶在一個頁面點擊“新建”,會同時新建兩個數據對象,比如注冊淘寶會員,會同時創建一個支付寶帳號,這里不能算做2個邏輯事務,而只有1個,這個邏輯事務的“實體”是2個。

Read

讀取數據的邏輯事務基本都叫“查看XXX的詳情”,WEB程序會根據主鍵,把某個數據對象select出來,把各個字段的值顯示在頁面上。在分析Read類邏輯事務時,要對頁面進行分塊分類,因為現在很多WEB頁面,都不是單純顯示一個數據,而是用i_f_r_a_m_e的方式,把相關對象都顯示出來,這里每個對象都是一個獨立的邏輯事務。

注意,在一些列表頁面,比如“我購買過的商品”,是用數據列表的方式,展示出很多商品,這不屬於Read類邏輯事務,而是屬於List,后面會講到。Read類一般都是展示單體。

有些Read類邏輯事務會出現,不同身份的用戶查看同一數據對象時,有不同的輸出,比如VIP用戶查看商品時有價格優惠。這里不能算作2個邏輯事務:“普通用戶查看商品”“VIP查看商品”,而應該算1個。“用戶的角色”只是一個輸入DET。不過,如果普通用戶和VIP用戶查看商品,完全是兩個不同的頁面(View不同),那就要算2個邏輯事務了。

Update

這類邏輯事務是對已經存在的數據進行更新,一般叫做“保存XXX信息”,不過在某些場景,Update邏輯事務有很多其他的命名,例如“為訂單付款”,實際上是更新了訂單對象的狀態,因此歸於Update類。

在一些信息的保存頁面,例如“保存我的收貨地址”頁面,用戶需要先打開某個收貨地址的詳情頁面,然后再進行保存,那么這個詳情頁面,要算作一個Read類邏輯事務,用戶點擊按鈕保存,算作一個Update類邏輯事務。

Delete

刪除類邏輯事務出現的不多,一般都是用戶點擊“刪除”按鈕,把一個或者幾個數據對象刪除。老規矩,如果用戶一次點擊,刪除一個對象的同時,又級聯刪除了這個對象相關的另一個對象的話,只算作一個邏輯事務,實體是2個。刪除時一般都會彈出一個對話框,讓用戶確認,這個確認不算邏輯事務。

List

此類邏輯事務最常出現的形態是DataGrid數據表格,例如上文說的“列出我購買過的商品”。這個控件在WEB應用程序中被使用的非常多,它用一種格式在一個頁面展示出多個數據對象,並且把這些對象的關鍵屬性(名稱、類型)顯示出來。除了DataGrid,樹型控件也是一個List類的邏輯事務。此外,頁面中展示對象的下拉菜單、RadioButton,也要作為單獨的邏輯事務來計算,不過前提示,它們顯示的是數據對象,如果是“男/女”這樣的RadioButton,不是邏輯事務,而購買商品時,選擇的“收貨地址列表”則是邏輯事務。

有一些DataGrid控件,支持用戶直接在表格里修改數據,這里的修改功能要單獨作為Update類邏輯事務計算,與上文有一點不同的是,不需要另算Read類邏輯事務了。

到這里我們對這5類邏輯事務都分析清楚了,大家划分邏輯事務時,還要明確一個原則,每個邏輯事務的實體個數、輸入DET個數、輸出DET個數都不能是0,否則就不算邏輯事務。例如,有的頁面上設計一個按鈕,用戶點擊后,跳轉到另一個頁面,這種單純的跳轉,不是邏輯事務。邏輯事務都是對數據對象的操作。大部分情況下,數據對象都在數據庫中,也有一些情況,數據對象是文件對象,比如上傳圖片,圖片本身就是實體對象。

以上所列出的,都是常見的邏輯事務案例,在真實項目中,還可能會遇到一些其他的情況,大家只要根據邏輯事務的判定原則,進行推理,就可以很好的把邏輯事務划分清楚了。

最后,要說一下測試用例設計。非常相似的,測試用例也是圍繞邏輯事務來設計的。在組織用例分類時,基本遵循“項目-功能模塊-邏輯事務”這種目錄結構。每個邏輯事務的用例,都擁有類似的前置條件、操作步驟、校驗方法,所以組織在一起設計,是非常科學的。

 

 

 

 

Q:邏輯事務分解對於那種“增刪改查”類型的功能點比較適用,但是流程類的功能點,就不合適了吧?
A:就拿交易流程來說好了,我們在設計交易流程的TC時,是把下單、付款、發貨、確認等操作,分別進行設計的,這些操作,其實都是單獨的邏輯事務,實際上,開發在設計程序的時候,也是分開做的,不可能全寫在一個函數里面。

Q:我發現有的邏輯事務,比如點擊一個按鈕以后,程序既做了查,又做了改,按照你Part2里的分類,是不是應該算兩個邏輯事務。
A:這個怪我沒說清楚,這應該是算一個邏輯事務,他可以同時包含多個CRUD的action。

Q:我們以前設計TC很多都是基於一個頁面的,現在按照這種方式,一個頁面會分解成多個邏輯事務,這樣感覺會比較零散。
A:測試設計和測試執行是有區別的,測試設計的目標是把被測系統分析清楚,並不是編寫出完整的執行腳本,實際上在測試執行過程中,有經驗的測試工程師是會把幾個邏輯事務放在一起測,效率極高。

 

我們仍然會分別對CRUDL這五類邏輯事務進行分析,因為他們的輸入和輸出的特點,各有不同。不過,對於“實體”的計算,各類邏輯事務的計算方法相同,所以先單獨講一下實體。

淘寶系統里存在下面這些實體:會員、寶貝、交易記錄、寶貝類目、評價記錄、店鋪等等。實體的英文原名是Entity Type,也就是我們平時討論中經常出現的“對象”,經常接觸代碼的工程師會在代碼里發現很多“xxxDO”,比如UserDO、OrderDO,這些和實體是完全對應的。另外還有一個簡單的辦法來識別實體,就是看數據庫的表,一般一個實體肯定至少對應一張表,比如會員這個實體在數據庫里,必然有一張users表。

分析一個邏輯事務有哪些實體,是分析的第一步,也是最重要的一步。實體越多,開發和測試的復雜度越高。從MkII算法里也能看出,實體的系數是1.66,遠高於輸入和輸出。另外,分析實體可以幫助測試工程師搞清楚,這個事務的范圍,對事務有一個全局的概念。分析完后,測試工程師一般會這么說:“哦,這個事務跟這幾個對象有關!”在新人學習和測試設計評審中,實體的分析也能起到非常大的幫助作用。

下面開始分類講CRUDL的輸入和輸出:

 

 

Create

注冊會員、發布新寶貝,這都屬於Create類型邏輯事務,這類事務一般都有一個內容較多的“表單”,里面有一些輸入框、checkbox、radiobutton和一個確認提交按鈕,這里的每個控件,都要計算為1個輸入DET。需要注意,radio控件一般會有多個選項,不能算多個輸入,而只能算一個;提交按鈕也要算1個輸入。除了這些頁面控件類輸入,還有一類輸入,是“隱含”的。比如賣家在發布新寶貝時,賣家自己的userid就是一個輸入,因為在發布成功后,這個寶貝會擁有賣家的userid,只是這個id並不需要賣家填寫,而是放在系統緩存里。雖然是隱含輸入,卻也參與了邏輯運算,因此要計數。這是Create的兩類輸入。

Create事務的輸出一般會有以下一些信息,“注冊成功”、“此會員名已被人注冊”、“密碼太短”。當用戶試圖執行這個事務,系統給用戶所有可能的提示信息,都要記為一個輸出。有些輸出跟某個輸入,有直接的邏輯關系,比如“此會員名已被人注冊”只跟“會員名”輸入框里所填的信息有關,有的輸出,則是由好幾個輸入組合在一起,才能出現,分析的時候,需要弄明白,不過這點區別不影響計數。

如果把邏輯事務看成一個“函數”,那么輸入就可以抽象為函數的輸入參數,而輸出就是函數的所有可能性的返回值,以及函數拋出的各種異常。后面幾類邏輯事務,也可以抽象為這種定義,大家后面自己推理試一試。

Read

Read類事務是讀取一個對象的詳細信息,比如我們查看一個寶貝的詳細信息。它的輸入個數一般比較少,其中最基本的,就是這個對象的主鍵id,比如寶貝的id。如果不同類型的用戶查看寶貝時,會有不同的顯示信息,那么用戶的userid和用戶類型這2個要記為輸入。如果寶貝的某些屬性會影響顯示,比如鞋城寶貝會有個圖標,那么輸入也要+1。其實如果業務邏輯復雜,輸入也不少。我們可以把這些輸入抽象的稱為“影響對象顯示的一些屬性”。

一般來說Read的輸出都比較多,這個對象能顯示在頁面上的屬性,都要記為輸出,比如“寶貝名稱”、“價格”、“顏色”等等。除了文字類,圖片也是輸出,比如寶貝的縮略圖和表示寶貝屬性的一些小圖標,都算。另外,有的圖片和Label有鏈接,這里的鏈接要單獨算輸出,因為一個純文本,肯定沒有一個附帶http鏈接的文本信息量多。

Update

Update事務的輸入和輸出數量可多可少,關鍵看系統Update的規模,比如“編輯寶貝”跟“發布新寶貝”相比,輸入輸出的數量都非常多。像“修改我的登錄密碼”這樣的,數量就非常少了。Update事務的輸入輸出識別,與Create類非常相似,因為大部分Update事務也是表單提交的方式。

這里需要注意的是,系統中會存在一些“不起眼”的Update事務,分析時千萬別漏了。比如大部分會員有多個收貨地址,然后在列表中有一個鼠標懸停出現的“設置為默認收獲地址”的按鈕,當用戶點擊的時候,只是修改收獲地址的一個屬性,非常的隱蔽。這也是一個邏輯事務,它的輸入是用戶id,收貨地址id,輸出只有1個,就是點擊按鈕后,系統提示修改成功,非常簡單,但是不要遺漏。

Delete

Delete類事務的分析相對簡單一些,多數刪除功能,都是直接對數據進行刪除,因此輸入一般就是數據主鍵id這1個。不過,有一些數據在刪除前,需要先做一些邏輯判斷,看看能不能刪,這樣輸入就多了,相應的實體也會增加。比如,“已經發布的寶貝不能刪除”,那么“寶貝發布狀態”就是1個輸入,“已經有交易的寶貝不能刪除”,那么實體就不僅僅是寶貝,還有交易記錄,輸入也要增加“交易記錄id”;如果規則更復雜“有未完成交易的寶貝不能被刪除”,那么輸入還要增加“交易狀態”,依此類推。

List

List事務是一個重點,最后講,它的輸入輸出計算比較復雜,而且多變,所以大家不僅要理解我下面講的東西,還要能自己推理,分析自己實際遇到的各種List。List事務實質上跟Read很像,不同在於Read只看1個對象,List要看多個。首先看最常出現的List事務:DataGrid,這種控件一般是一個二維表格,它的輸入與Read類似,比如我的訂單列表的輸入是會員userid,我的已買到訂單列表還要增加訂單類型這個輸入,我的近3個月已買到訂單再加訂單時間,等等。有些列表上方,會有查詢功能,比如按照名稱查詢,這些查詢項會影響列表的顯示數據,因此是輸入。大家如果想象一下這個列表對應的sql語句,就好理解了,where后面跟的都是輸入。

DataGrid的輸出比較直觀,每一列就是一個輸出,對應sql語句里的select后面的字段,注意,有些列顯示的不僅僅是文字,還有圖片,顏色,這些都要單獨計數。

對於DataGrid來說最糾結的要屬“翻頁”和“排序”功能,這究竟是算輸入還是輸出呢?經過推理我們發現,翻頁功能對顯示的數據是有影響的,比如我翻到第二頁(假設每頁10個數據對象),很多程序會控制從數據庫里讀取的數據,只取出第11到20的數據,也是在sql的where后面加條件,所以,翻頁屬於輸入的計數,流行的翻頁一般是“上一頁”“下一頁”這樣的按鈕或者是直接輸入數字翻頁,只要出現1種,輸入+2,要是兩種都有,+4。再說說排序,排序對應的sql標記是order by,不是select也不是where,比較另類,究竟算哪邊合適呢?在MkII的相關資料里也沒有找到答案。通過比較發現order的操作與where更接近一些,相似度更大,都是影響數據的檢索范圍,因此我們把排序也認定為輸入。DataGrid如果有1列提供排序功能,那么輸入+1,多列的話,依此類推。

前文曾經提到過,顯示數據對象的radio控件,也是List類的事務,它的輸入,就是顯示這些數據的條件。比如買家購買商品時,有個收貨地址的radio控件,因為是“我的”地址,所以userid是1個輸入。輸出就要看這個radio展示了數據對象的哪些屬性,有幾個算幾個。比如收貨地址的姓名、省、市、區、郵編,這些都是不同的字段,但是在radio里全部拼在一起,要算5個輸出。

還有一類控件使用也較多,就是樹形控件,這也是List類的邏輯事務。輸入算法就不說了,同上,重點說輸出。樹形控件里的每個節點,一般都是一個數據對象,節點會有名稱、顏色、加粗、小圖標這么幾種顯示元素,用來表示數據對象的屬性,這些都是輸出,單獨計數。樹形控件有特殊的展開、折疊功能,這個不太好分類,我們直接規定,輸出+4,因為在折疊上我們需要投入一點測試成本。如果一棵樹里展示了兩種數據對象,別忘了實體要+1。

List的例子我們就講3個,大家還有可能遇到各種五花八門的List邏輯事務,只要掌握這個原則:輸入會影響顯示數據的范圍,而輸出是展示數據的各個屬性。大家只要掌握規律,細心推理,遇到問題方可迎刃而解。

 

 

功能點分析法應用在軟件測試中,它最核心的價值究竟是什么呢?

讓我們先看看軟件開發,這是跟測試離得最近的工作類型。開發工程師工作大致可以分為“設計”和“編碼”兩步。設計一般是使用UML語言,借助類似於Rose這樣的工具,繪制一些UC圖、類圖、ER圖等等。這些設計圖決定了最終的編碼該如何實施。另外,當其他的開發工程師需要了解這個project時(例如評審),ta會先看設計文檔,從設計文檔中掌握開發思路,然后再閱讀代碼,了解細節。

由於UML語言中,包含了大量的約定和規則,因此開發工程師只需要花費較少的工作量,就能表達出充足的信息。而閱讀UML設計文檔的其他人,也能很快從UML設計中了解設計人員的思路。試想一下,如果讓讀者直接看代碼,需要花費多少倍的時間,才能達到相同的目的。這就是設計模型的價值,不僅幫助設計人員自己整理思路,也幫助其他讀者快速交流信息。

對於軟件測試來說,也有“設計”和“編碼(實施)”兩個階段的工作。設計是我們設計測試用例的過程,也就是我們考慮如何做;實施就是我們執行測試的過程,有時是手工執行,有時是寫腳本讓計算機執行。因此,測試用例是我們的“設計文檔”,是我們交流測試方法,評審測試方案的核心。但是只依靠測試用例,我們感覺存在很多問題:

  • 測試用例數量多,難以閱讀
  • 測試用例結構五花八門,風格迥異,不同團隊間不好交流
  • 測試用例很難清楚表達需求邏輯,每次用例評審,要花費大量時間,講解需求邏輯
  • 測試用例描寫的是測試細節,較難看出測試的思路和重點

在這種情況下,我們需要一種測試設計模型,用來解決上面那些問題。事實上,測試設計模型不是唯一的,我們允許團隊中使用各種設計模型來設計測試用例。以前我們曾經用UML來設計,這是一種設計模型。不過UML開發工程師用起來合適,我們測試用就不是特別合適,畢竟它的優勢,是描述程序的開發實現。另外,設計模型和測試用例模式,應該是成對出現的,也就是說,用什么樣的設計模型,就應該有合適的用例模式與之對應。一成不變的用例模式,其實是不存在的,它必須要緊跟設計模型。

這就是我們選擇功能點分析算法的最主要目的:尋找一種新的設計模型,改善我們的測試用例設計過程,精簡測試文檔(因為模型可以包含很多信息),讓測試團隊用一種相同的設計模型進行工作,減少溝通成本,更好的支撐我們的業務測試。

現在我們面對的,是互聯網軟件產品,這一類軟件的特點,不同於傳統的應用軟件,互聯網應用軟件多使用BS結構,MVC的開發模型,有龐大的數據庫作為支撐,需求和用戶界面多變,市場競爭激烈,等等。在這種條件下,測試工程師往往沒有太多時間設計用例,而是要快速的面對大量新需求和需求變更,第一時間找出程序的bug,這才是王道。

下面,我們講一下,怎么樣使用功能點分析的方法,來設計測試用例。

如part2所說,我們拿到一個project,首先需要把它拆分成邏輯事務,然后針對每個邏輯事務,討論使用何種測試方法。有些事務屬於核心事務,必須要重點測試,要設計完整的用例,有些事務只需編寫一個簡單的check list,就足夠指導測試執行了,有些事務甚至根本不用寫任何文檔,測試工程師拿到手也知道該怎么測試。在這里我不想再回答“一個完全不懂的測試新人,看不到用例,該怎么測試?”這樣的問題。測試新人正式上崗之前,必須接受測試技術培訓,和project的業務培訓。如果是跨團隊合作(其實這種場景很少),那么PTM也要出面先做一些測試方案的講解,絕對不能把測試用例直接扔給其他工程師。

這里我們推薦使用freemind或者xmind這樣的思維導圖軟件,來做功能點分析。root node一般是project的名稱,比如購物車。然后下級node是各個模塊的名稱,然后就是邏輯事務的名稱。本文選了一個邏輯事務作為案例:買家在寶貝詳情頁面點擊購買。通過對這個事務的功能點分析,再推導出相應的測試用例。事實上,淘寶測試團隊的twork小組,正在開發通過freemind圖,自動生成測試用例的功能,所以在下面的講解中,我會不斷比較,freemind圖和最終生成的用例。

首先在邏輯事務的node下創建:輸入、輸出、實體3個node,先列出所有的實體。實體對用例設計並沒有什么影響,只是告訴讀者,這個事務跟哪些對象有關,這樣可以清楚的界定用例范圍。如下圖:

“01點擊立即購買”是我們今天要講的事務,02~06也是事務,但是今天不會講到。使用twork把這個設計圖導入以后,將會產生對應的目錄結構,注意,一直到邏輯事務這一層,都和設計圖相同,再往下,會根據設計的不同有所變化,而並不產生“輸入”“輸出”這樣的測試集。如下圖:

下面重點要講輸入,這和測試用例的設計有很大關系。這個事務的輸入比較多,不過我們如果分類來看,就會比較清楚。首先看最上面那3個實體的主鍵id,這3個輸入是必須要參與程序的邏輯運算的,但是與測試用例無關。如下圖:

有一類輸入,比如買家狀態,會有很多枚舉值,這些枚舉值會產生非常優先的判定,比如說,一個被處罰的買家,是不能購買寶貝的。這一個條件就可以直接產生一個確定的結果,這些結果,一般是用頁面文字的方式告知用戶,所以要算作“輸出”。注意:輸出的項,不一定都在“輸出”這個node下面,而是有很大一部分,會掛在輸入項的下面,表示和輸入的邏輯關系,這種關系也是設計圖中的重要信息,如下圖:

在導入twork以后,會在邏輯事務的測試集下,產生一個叫做“買家狀態”的測試集,這些枚舉值將變成輸入條件,而后面的輸出結果,將變成期望結果。如下圖:

 

還有一種輸入項,比如頁面表單的輸入框,會產生一堆輸出:“不能為空”“不能超過20字符”等等,在設計圖中,我們可以把這一堆輸出,直接掛在輸入項下面,這樣,也會產生一組用例,也就是我們常說的頁面校驗。

上面所說的,是輸出和輸入緊密關聯的情況,產生的用例比較簡單。除此以外還會出現更復雜的情況,當多個輸入組合在一起的時候,才會產生一定的輸出。這時,就需要把這些有邏輯關系的輸入組織起來,在設計圖里單獨建一個node,注意這個node上不要標記Input,因為它不是一個輸入項,而只是一個分組。真正的輸入項在下面。如下圖:

根據這一部分的設計,會生成一組比較復雜的用例,每個輸入項,會成為一列,這里有4個,就是4列,另外再加1列“期望結果”。這是twork中一種新的用例編寫模式,叫做測試數據驅動模式(TD驅動),看起來眼熟,其實就是“判定表”,我們以前用Excel寫用例的時候,就是這么寫的,現在在twork中,更進了一步,用戶可以隨意定義每個測試集的列,而每一行,也作為一個用例對象,保存在數據庫里。如下圖:

需要說明的是,這種復雜的組合,程序是無法自動生成用例的,因為要完全排列組合的話,用例太多,不靠譜,而且具體的組合情況,跟需求有很強的關系,程序更是難以了解。程序能做的是,生成用例表格結構,同時創建一些空白的用例,然后我們自己在里面填一下值就可以了,寫用例速度快,而且用例非常直觀。

大家注意到了,在設計圖中,輸入、輸出、實體我們都用不同的標記給標識出來,這樣導入twork時,程序便會自動算出每個邏輯事務的功能點指數分值,非常方便,所以文章開頭說,這個指數計算,只是一個副產品。

通過上面的分析過程我們可以看出,功能點分析圖與測試用例之間,存在非常緊密的邏輯關系,之前幾篇文章我們也講到,功能點分析是一種非常好的分解分析需求的手段。通過這張分析圖,讀者可以迅速了解設計者的思路,以及了解每個邏輯事務大致的邏輯。這時如果需要看細節,可以進入twork,很快找到這個邏輯事務的測試集,並查看下面的用例。

上面的例子,列舉的是Create類的邏輯事務,以及里面兩種最常見的輸入組合。Update類和Delete類事務,跟這個差不多,這里不再細講。它們的共同點在於,輸入一般較多,並且存在一些邏輯組合,而輸出,相對比較簡單。

至於Read類和List類事務,在設計圖會有一些區別,這兩種邏輯事務輸入相對較少,而輸出項很多,它們主要的測試重點在於,校驗頁面展示,比如“查看寶貝信息”,輸出項可能會有30個以上,這時,使用check list的方式會比較方便,並不用編寫復雜的用例,只需說明,需要校驗哪些點即可。如果Read類事務也有復雜的輸入,比如查看寶貝信息會有:寶貝類型、寶貝類目、寶貝消保類型這些輸入,那么就參考剛才Create類的方式即可。

總之,設計用例重點關注業務邏輯,對於展示類的事務,盡量用簡單的方式完成測試設計,至於有些一看到頁面,就知道應該怎么測試的事務,即使不寫用例,我覺得也問題不大。只要在設計圖中把這個事務的輸入輸出實體都標識清楚,我相信測試工程師就可以很好的完成測試工作。即使交給另一位工程師,只要他也了解這種設計模式,那么也可以測得很好。

功能點算法以及在軟件測試中的應用的系列文章,到這里總共編寫了4章,我們需要告一段落了。這4章主要講了功能點算法的概念,以及基本的應用,后面我們會實踐一段時間,等積累了足夠的經驗,我們再繼續這個話題,一起期待Part5回歸吧。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM