一直從事互聯網電商開發三年多的時間了,回頭想一想卻對整個業務流程不是很瞭解,說出去非常慚愧。可是身處互聯網電商的環境中,或多或少接觸了其中的各個業務,其次周邊還有不少從事電商的同事和朋友,這都是資源。因而,我決定和個人同事、盆友們、甚至還有大家去梳理整個流程並分享出來,談不上結果要作的多麼好,至少在每個咱們有能力去作好的地方,必定會細緻入微。php
除此以外,同時爲了知足咱們自身在工做中可能得不到的技術知足感,咱們在作整個系統設計的過程當中,會去使用咱們最想用的技術棧。技術棧這一點咱們藉助docker去實現,因此最終的結果:一方面咱們掌握了業務的東西,另外一方面又獲得了技術上的知足感,兩者兼得。git
最後,出於時間的考慮,咱們提出了一個想法Do design No code。**【只設計不碼碼】**這句話的意思:最終咱們設計出來整個系統的數據模型,接口文檔,甚至交互過程,以及環境部署等,可是最後咱們卻不寫代碼。是吧?若是這樣了寫代碼還有什麼意義。固然,也不全是這樣,出於時間的考慮固然也會用代碼實現出來的,說不定最後正是對面的你去實現的。github
其次,這些內容確定有考慮不全面或者在上規模的業務中存在更復雜的地方,歡迎指出,咱們也但願學習和分享您的經驗。sql
今天,咱們開始第一部分用戶體系的設計。本文分爲以下四大模塊:docker
當你第一次接觸和用戶相關的互聯網產品時,或者曾今在我眼裏。用戶體系無非就是「登陸」和「註冊」,「修改用戶信息」這些,等。簡單來作的話,無非咱們須要一張表去記錄用戶的身份信息:註冊時(insert操做),往表裏插入一個數據;登陸時(select&update操做),經過用戶標識(手機號、郵箱等)判斷用戶的密碼是否正確;修改用戶信息(select&update操做),就是直接update這個uid的用戶信息(頭像、暱稱等)。json
這樣設計的確沒什麼問題,很簡單不是麼。可是隨着業務的發展,一方面咱們須要提供統一的用戶管理(高內聚),又要提升系統的可擴展性,因此我想呈現出來的是我理解的一個基本用戶體系應該有的東西。後端
首先咱們對原有的用戶表進行再一次的抽象(抽離用戶註冊、登陸依賴的字段、第三方登錄) -> 帳戶表,爲何這麼作?隨着業務的發展,之前只維護一個產品,也許某一天又開發新的產品,這樣咱們就能夠統一的維護咱們公司全部產品的註冊登陸邏輯,不一樣的產品只維護該產品和用戶相關的信息便可(具體依賴產品形態)。以下圖所示:bash
上圖中,還提到了第三方登錄/員工表/後臺權限管理,這些都是一些用戶體系基本必備的結構。架構
第三方登陸:第三方也是登錄方式的一種,咱們也把它抽象到帳戶的一部分,如上圖所示。其次,關於第三方登陸這裏存在一個交互方式設計存在的問題,後面交互設計時會提到。框架
員工:由於上面咱們抽離了帳戶表,因此內部的管理系統後臺也能夠統一的使用帳戶表的登陸邏輯,這樣全公司在帳號這個事情上達到了真正的高內聚。
提到了員工,咱們的內部各類系統後臺確定涉及各類的權限管理,因此這裏提到了簡單的RBAC(基於角色的權限控制),具體的邏輯數據模型設計會提到。
隨着業務產品形態的愈來愈複雜,在設計架構的時候,咱們須要分析其中的變與不變:
最終的結果,咱們把原有的用戶拆成了帳戶和用戶,同時咱們也要在這裏明確這兩個概念的區別:
最終的架構圖以下:
對應上面的架構,咱們很容易設計出咱們的數據模型(這裏假設咱們目前只有一個對C端的應用):
帳戶 -> 1.帳戶表
用戶 -> 2.用戶表
員工 -> 3.員工表
複製代碼
除了上面三張表外,還須要咱們的R(role)B(base)A(access)C(control)權限管理,RBAC基於角色的權限管理你們應該很熟悉,這裏我就不詳細說了,簡單的RBAC首先須要:
4.系統菜單表(菜單即權限),系統的uri路徑
5.權限表(菜單即權限),具體的權限就是訪問系統的菜單
6.角色表,一個角色具備哪些權限
7.員工和角色的關聯表,一個員工屬於哪一個角色
複製代碼
好了一個簡單的RBAC涉及的表基本羅列出來了,可是在個人工做經歷中你們實現的權限管理每每只針對某個系統,這樣對於衆多的系統後臺來講就是亂、重複造輪子、權限管理效率低。因此我在上面的架構設計中把權限做爲了一個服務爲全系統提供基礎服務能力。而達到這個目的的結果我只須要再增長一張表:
8.後臺管理系統表, 登記全部的後臺管理系統(這樣經過系統id和系統資源uri的id就能夠全局構成惟一性,單純的uri存在重複的可能性,用uri不用url的緣由是域名存在變更的可能性)
複製代碼
最後咱們的用戶體系應該基本就上面8張表。咦,貌似漏掉了第三方登錄,咱們加上吧,很簡單以下:
9. 第三方用戶登錄表,記錄不一樣第三方的用戶標示
複製代碼
最最後就是上面的9張表了,具體的表結構和sql以下:
帳戶模型
-- 帳戶模型
CREATE TABLE `account_user` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '帳號id',
`email` varchar(30) NOT NULL DEFAULT '' COMMENT '郵箱',
`phone` varchar(15) NOT NULL DEFAULT '' COMMENT '手機號',
`username` varchar(30) NOT NULL DEFAULT '' COMMENT '用戶名',
`password` varchar(32) NOT NULL DEFAULT '' COMMENT '密碼',
`create_at` int(11) NOT NULL DEFAULT '0' COMMENT '建立時間',
`create_ip_at` varchar(12) NOT NULL DEFAULT '' COMMENT '建立ip',
`last_login_at` int(11) NOT NULL DEFAULT '0' COMMENT '最後一次登錄時間',
`last_login_ip_at` varchar(12) NOT NULL DEFAULT '' COMMENT '最後一次登錄ip',
`login_times` int(11) NOT NULL DEFAULT '0' COMMENT '登陸次數',
`status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '狀態 1:enable, 0:disable, -1:deleted',
PRIMARY KEY (`id`),
KEY `idx_email` (`email`),
KEY `idx_phone` (`phone`),
KEY `idx_username` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='帳戶';
複製代碼
-- 第三方帳戶
CREATE TABLE `account_platform` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增id',
`uid` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '帳號id',
`platform_id` varchar(60) NOT NULL DEFAULT '' COMMENT '平臺id',
`platform_token` varchar(60) NOT NULL DEFAULT '' COMMENT '平臺access_token',
`type` tinyint(1) NOT NULL DEFAULT '0' COMMENT '平臺類型 0:未知,1:facebook,2:google,3:wechat,4:qq,5:weibo,6:twitter',
`nickname` varchar(60) NOT NULL DEFAULT '' COMMENT '暱稱',
`avatar` varchar(255) NOT NULL DEFAULT '' COMMENT '頭像',
`create_at` int(11) NOT NULL DEFAULT '0' COMMENT '建立時間',
`update_at` int(11) NOT NULL DEFAULT '0' COMMENT '更新時間',
PRIMARY KEY (`id`),
KEY `idx_uid` (`uid`),
KEY `idx_platform_id` (`platform_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='第三方用戶信息';
複製代碼
用戶模型
-- 用戶模型
CREATE TABLE `skr_member` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '用戶id',
`uid` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '帳號id',
`nickname` varchar(30) NOT NULL DEFAULT '' COMMENT '暱稱',
`avatar` varchar(255) NOT NULL DEFAULT '' COMMENT '頭像(相對路徑)',
`gender` enum('male','female','unknow') NOT NULL DEFAULT 'unknow' COMMENT '性別',
`role` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '角色 0:普通用戶 1:vip',
`create_at` int(11) NOT NULL DEFAULT '0' COMMENT '建立時間',
`update_at` int(11) NOT NULL DEFAULT '0' COMMENT '更新時間',
PRIMARY KEY (`id`),
KEY `idx_uid` (`uid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='帳戶信息';
複製代碼
員工模型
-- 員工表
CREATE TABLE `staff_info` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '員工id',
`uid` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '帳號id',
`email` varchar(30) NOT NULL DEFAULT '' COMMENT '員工郵箱',
`phone` varchar(15) NOT NULL DEFAULT '' COMMENT '員工手機號',
`name` varchar(30) NOT NULL DEFAULT '' COMMENT '員工姓名',
`nickname` varchar(30) NOT NULL DEFAULT '' COMMENT '員工暱稱',
`avatar` varchar(255) NOT NULL DEFAULT '' COMMENT '員工頭像(相對路徑)',
`gender` enum('male','female','unknow') NOT NULL DEFAULT 'unknow' COMMENT '員工性別',
`create_at` int(11) NOT NULL DEFAULT '0' COMMENT '建立時間',
`update_at` int(11) NOT NULL DEFAULT '0' COMMENT '更新時間',
PRIMARY KEY (`id`),
KEY `idx_uid` (`uid`),
KEY `idx_email` (`email`),
KEY `idx_phone` (`phone`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='員工信息(這裏列了大概的信息,多的能夠垂直拆表)';
複製代碼
系統權限管理模型
-- 權限管理: 系統map
CREATE TABLE `auth_ms` (
`id` smallint(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增id',
`ms_name` varchar(255) NOT NULL DEFAULT '0' COMMENT '系統名稱',
`ms_desc` varchar(255) NOT NULL DEFAULT '0' COMMENT '系統描述',
`ms_domain` varchar(255) NOT NULL DEFAULT '0' COMMENT '系統域名',
`create_at` int(11) NOT NULL DEFAULT '0' COMMENT '建立時間',
`create_by` int(11) NOT NULL DEFAULT '0' COMMENT '建立人staff_id',
`update_at` int(11) NOT NULL DEFAULT '0' COMMENT '更新時間',
`update_by` int(11) NOT NULL DEFAULT '0' COMMENT '修改人staff_id',
`status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '狀態 1:enable, 0:disable, -1:deleted',
PRIMARY KEY (`id`),
KEY `idx_domain` (`domain`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='系統map(登記目前存在的後臺系統信息)';
-- 權限管理: 系統menu
CREATE TABLE `auth_ms_menu` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增id',
`ms_id` smallint(11) unsigned NOT NULL DEFAULT '0' COMMENT '系統id',
`parent_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '父菜單id',
`menu_name` varchar(255) NOT NULL DEFAULT '0' COMMENT '菜單名稱',
`menu_desc` varchar(255) NOT NULL DEFAULT '0' COMMENT '菜單描述',
`menu_uri` varchar(255) NOT NULL DEFAULT '0' COMMENT '菜單uri',
`create_at` int(11) NOT NULL DEFAULT '0' COMMENT '建立時間',
`is_show` enum('yes','no') NOT NULL DEFAULT 'no' COMMENT '是否展現菜單',
`create_by` int(11) NOT NULL DEFAULT '0' COMMENT '建立人staff_id',
`update_at` int(11) NOT NULL DEFAULT '0' COMMENT '更新時間',
`update_by` int(11) NOT NULL DEFAULT '0' COMMENT '修改人staff_id',
`status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '狀態 1:enable, 0:disable, -1:deleted',
PRIMARY KEY (`id`),
KEY `idx_ms_id` (`ms_id`),
KEY `idx_parent_id` (`parent_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='系統menu';
-- 權限管理: 系統權限
CREATE TABLE `auth_item` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增id',
`ms_id` tinyint(11) unsigned NOT NULL DEFAULT '0' COMMENT '系統id',
`menu_id` varchar(255) NOT NULL DEFAULT '0' COMMENT '頁面/接口uri',
`create_at` int(11) NOT NULL DEFAULT '0' COMMENT '建立時間',
`create_by` int(11) NOT NULL DEFAULT '0' COMMENT '建立人staff_id',
`update_at` int(11) NOT NULL DEFAULT '0' COMMENT '更新時間',
`update_by` int(11) NOT NULL DEFAULT '0' COMMENT '修改人staff_id',
`status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '狀態 1:enable, 0:disable, -1:deleted',
PRIMARY KEY (`id`),
KEY `idx_ms_menu` (`ms_id`, `menu_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='系統權限';
-- 權限管理: 系統權限(權限的各個集合)
CREATE TABLE `auth_role` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增id',
`name` varchar(255) NOT NULL DEFAULT '0' COMMENT '角色名稱',
`desc` varchar(255) NOT NULL DEFAULT '0' COMMENT '角色描述',
`auth_item_set` text COMMENT '權限集合 多個值,號隔開',
`create_at` int(11) NOT NULL DEFAULT '0' COMMENT '建立時間',
`create_by` int(11) NOT NULL DEFAULT '0' COMMENT '建立人staff_id',
`update_at` int(11) NOT NULL DEFAULT '0' COMMENT '更新時間',
`update_by` int(11) NOT NULL DEFAULT '0' COMMENT '修改人staff_id',
`status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '狀態 1:enable, 0:disable, -1:deleted',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='員工角色';
-- 權限管理: 角色與員工關係
CREATE TABLE `auth_role_staff` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增id',
`staff_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '員工id',
`role_set` text COMMENT '角色集合 多個值,號隔開',
`create_at` int(11) NOT NULL DEFAULT '0' COMMENT '建立時間',
`create_by` int(11) NOT NULL DEFAULT '0' COMMENT '建立人staff_id',
`update_at` int(11) NOT NULL DEFAULT '0' COMMENT '更新時間',
`update_by` int(11) NOT NULL DEFAULT '0' COMMENT '修改人staff_id',
`status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '狀態 1:enable, 0:disable, -1:deleted',
PRIMARY KEY (`id`),
KEY `idx_staff_id` (`staff_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='權限角色與員工關係';
複製代碼
友情提示:一大波圖片即將到來,此處圖片較多,不清楚的可點擊大圖查看
註冊成功以後存在至少兩種交互方式:
具體交互流程以下:
快捷登陸的流程基本和上面一致只是驗證密碼換成了驗證驗證碼。
第三方登陸的交互其實存在這樣的問題:
由於我發現有些PM爲了提升用戶使用的簡單快捷性,每每第三方登錄成功後會直接產生uid,而不進行帳號的綁定。這樣以後在再進行帳號綁定就涉及帳號合併的問題,很麻煩(若是有錢包等)。若是咱們一開始就進行綁定操做,這樣將來帳號的關係就清晰明瞭便於維護,第三方登錄其實就至關於普通帳號的別名。 最後這個事情作不作的結果就是,帳戶表account_user和第三方用戶信息表account_platform是的一對多仍是一對一的關係。
這個還好說,通常來講綁定的選擇基本是正確的。最後具體的流程圖以下:
交互界面圖以下:
首先,咱們的後臺管理系統須要個響亮的稱號,想了一會之前公司用過apollo,因而我準備用mars但忽然冒出來個earth,地球萬物之根,恰好咱們這又是個全業務的基礎服務管理系統,哈哈就這樣吧~ Earth System
Earth System的權限管理功能主要分爲如下四部分:
具體交互以下:
1.註冊接口
請求參數:
字段 | 類型 | 是否必傳 | 描述 |
---|---|---|---|
username | string | 非必傳 | 用戶帳號 |
string | email/phone二者擇一 | 用戶郵箱 | |
phone | string | email/phone二者擇一 | 用戶手機號 |
code | int | 必傳 | 驗證碼 |
交互方式一(跳轉到登錄頁面)響應內容:
{
"code": "200",
"msg": "OK",
"result": []
}
複製代碼
交互方式二(跳轉到首頁頁面)響應內容:
{
"code": "200",
"msg": "OK",
"result": {
"s_token": "string, 用戶會話標示",
"s_token_expire": "string, 用戶會話標示過時時間,0不過時",
"username": "string, 用戶名",
"nickname": "string, 用戶暱稱",
"avatar": "string, 用戶頭像",
"gender": "string, 用戶性別,male:男,female:女,other:未知",
}
}
複製代碼
2.登陸接口
請求參數:
字段 | 類型 | 是否必傳 | 描述 |
---|---|---|---|
username | string | username/email/phone三者擇一 | 用戶帳號 |
string | username/email/phone三者擇一 | 用戶郵箱 | |
phone | string | username/email/phone三者擇一 | 用戶手機號 |
password | string | 必傳 | 密碼 |
響應內容:
{
"code": "200",
"result": {
"s_token": "string, 用戶會話標示",
"s_token_expire": "string, 用戶會話標示過時時間,0不過時",
"nickname": "string, 用戶暱稱",
"username": "string, 用戶名",
"avatar": "string, 用戶頭像",
"gender": "string, 用戶性別,male:男,female:女,other:未知",
}
}
複製代碼
3.快捷登陸接口
請求參數:
字段 | 類型 | 是否必傳 | 描述 |
---|---|---|---|
string | email/phone二者擇一 | 用戶郵箱 | |
phone | string | email/phone二者擇一 | 用戶手機號 |
code | int | 必傳 | 驗證碼 |
響應內容:
{
"code": "200",
"result": {
"s_token": "string, 用戶會話標示",
"s_token_expire": "string, 用戶會話標示過時時間,0不過時",
"nickname": "string, 用戶暱稱",
"username": "string, 用戶名",
"avatar": "string, 用戶頭像",
"gender": "string, 用戶性別,male:男,female:女,other:未知",
}
}
複製代碼
4.第三方登陸接口
請求參數:
字段 | 類型 | 是否必傳 | 描述 |
---|---|---|---|
type | string | 必傳 | 平臺類型 1:facebook,2:google,3:wechat,4:qq,5:weibo,6:twitter |
platform_id | string | 必傳 | 第三方平臺用戶ID |
platform_token | string | 必傳 | 第三方平臺令牌 |
響應內容:
{
"code": "200",
"result": {
"s_token": "string, 用戶會話標示",
"s_token_expire": "string, 用戶會話標示過時時間,0不過時",
"username": "string, 用戶名",
"nickname": "string, 用戶暱稱",
"avatar": "string, 用戶頭像",
"gender": "string, 用戶性別,male:男,female:女,other:未知",
}
}
複製代碼
5.用戶信息修改接口
請求參數:
字段 | 類型 | 是否必傳 | 描述 |
---|---|---|---|
username | string | 非必傳 | 用戶帳號 |
nickname | string | 非必傳 | 暱稱 |
avatar | string | 非必傳 | 頭像url |
gender | string | 非必傳 | 用戶性別,male:男,female:女,other:未知 |
響應內容:
{
"code": "200",
"result": {
"username": "string, 用戶名",
"nickname": "string, 用戶暱稱",
"avatar": "string, 用戶頭像",
"gender": "string, 用戶性別,male:男,female:女,other:未知",
}
}
複製代碼
6.用戶登陸狀態校驗
請求參數:
字段 | 類型 | 是否必傳 | 描述 |
---|---|---|---|
s_token | string | 必傳 | 用戶會話標示 |
響應內容:
{
"code": "200",
"result": {
"s_token_expire": "string, 用戶會話標示過時時間,0不過時, -1登錄失效",
}
}
複製代碼
帳戶服務:
請求參數:
字段 | 類型 | 是否必傳 | 描述 |
---|---|---|---|
username | string | 非必傳 | 用戶帳號 |
string | email/phone二者擇一 | 用戶郵箱 | |
phone | string | email/phone二者擇一 | 用戶手機號 |
交互方式一(跳轉到登錄頁面)響應內容:
{
"code": "200",
"msg": "OK",
"result": {
"uid": "string, 帳戶ID"
}
}
複製代碼
請求參數:
字段 | 類型 | 是否必傳 | 描述 |
---|---|---|---|
username | string | 非必傳 | 用戶帳號 |
string | email/phone二者擇一 | 用戶郵箱 | |
phone | string | email/phone二者擇一 | 用戶手機號 |
password | string | 必傳 | 密碼 |
響應內容:
{
"code": "200",
"msg": "OK",
"result": {
"uid": "string, 帳戶ID"
}
}
複製代碼
請求參數:
字段 | 類型 | 是否必傳 | 描述 |
---|---|---|---|
type | string | 必傳 | 平臺類型 1:facebook,2:google,3:wechat,4:qq,5:weibo,6:twitter |
platform_id | string | 必傳 | 第三方平臺用戶ID |
platform_token | string | 必傳 | 第三方平臺令牌 |
響應內容:
{
"code": "200",
"result": {
"uid": "string, 帳戶ID",
"nickname": "string, 用戶暱稱",
"avatar": "string, 用戶頭像",
}
}
複製代碼
權限服務
請求參數:
字段 | 類型 | 是否必傳 | 描述 |
---|---|---|---|
ms_id | string | 必傳 | 系統ID |
響應內容:
{
"code": "200",
"msg": "OK",
"result": {
"ms_name": "string, 系統名稱",
"ms_desc": "string, 系統描述",
"ms_domain": "string, 系統域名",
"list": [
{
"parent_id": "string, 父菜單ID",
"menu_id": "string, 菜單ID",
"menu_name": "string, 菜單ID",
"menu_desc": "string, 菜單描述",
"menu_uri": "string, 菜單uri",
"child" : [
{
"parent_id": "string, 父菜單ID",
"menu_id": "string, 菜單ID",
"menu_name": "string, 菜單ID",
"menu_desc": "string, 菜單描述",
"menu_uri": "string, 菜單uri",
"child" : []
}
]
}
]
}
}
複製代碼
請求參數:
字段 | 類型 | 是否必傳 | 描述 |
---|---|---|---|
menu_id | string | 必傳 | 菜單ID |
響應內容:
{
"code": "200",
"msg": "OK",
"result": []
}
複製代碼
若是有寫的不對或者不完善的地方,但願你們多多評論,互相學習互相進步~
項目地址: github.com/skr-shop/ma…
排名不分前後,字典序
暱稱 | 簡介 | 我的博客 |
---|---|---|
AStraw | 研究生創業者, 現於小米科技海外商城組從事商城後端研發工做 | -------- |
大愚Dayu | 國內大多人使用的PHP第三方支付聚合項目Payment做者,創過業,現於小米科技海外商城組從事商城後端研發工做 | 大愚Talk |
lwhcv | 曾就任於百度/融360, 現於小米科技海外商城組從事商城後端研發工做 | -------- |
TIGERB | PHP框架EasyPHP做者,擁有A/B/C輪電商創業公司工做經驗,現於小米科技海外商城組從事商城後端研發工做 | TIGERB.cn |