<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          帶你十天輕松搞定 Go 微服務系列(三)

          共 13967字,需瀏覽 28分鐘

           ·

          2022-02-21 21:03

          序言

          我們通過一個系列文章跟大家詳細展示一個 go-zero 微服務示例,整個系列分十篇文章,目錄結構如下:

          1. 環(huán)境搭建:帶你十天輕松搞定 Go 微服務系列(一)
          2. 服務拆分:帶你十天輕松搞定 Go 微服務系列(二)
          3. 用戶服務(本文)
          4. 產品服務
          5. 訂單服務
          6. 支付服務
          7. RPC 服務 Auth 驗證
          8. 服務監(jiān)控
          9. 鏈路追蹤
          10. 分布式事務

          期望通過本系列帶你在本機利用 Docker 環(huán)境利用 go-zero 快速開發(fā)一個商城系統(tǒng),讓你快速上手微服務。

          完整示例代碼:https://github.com/nivin-studio/go-zero-mall

          首先,我們來更新一下上篇文章中的服務拆分圖片,由于微信公眾號手機和電腦端不同步,導致美化的圖片沒有跟大家見面,特此補上,如圖:

          3 用戶服務(user)

          • 進入服務工作區(qū)
          $?cd?mall/service/user

          3.1 生成 user model 模型

          • 創(chuàng)建 sql 文件
          $?vim?model/user.sql
          • 編寫 sql 文件
          CREATE?TABLE?`user`?(
          ??`id`?bigint?unsigned?NOT?NULL?AUTO_INCREMENT,
          ??`name`?varchar(255)??NOT?NULL?DEFAULT?''?COMMENT?'用戶姓名',
          ??`gender`?tinyint(3)?unsigned?NOT?NULL?DEFAULT?'0'?COMMENT?'用戶性別',
          ??`mobile`?varchar(255)??NOT?NULL?DEFAULT?''?COMMENT?'用戶電話',
          ??`password`?varchar(255)??NOT?NULL?DEFAULT?''?COMMENT?'用戶密碼',
          ??`create_time`?timestamp?NULL?DEFAULT?CURRENT_TIMESTAMP,
          ??`update_time`?timestamp?NULL?DEFAULT?CURRENT_TIMESTAMP?ON?UPDATE?CURRENT_TIMESTAMP,
          ??PRIMARY?KEY?(`id`),
          ??UNIQUE?KEY?`idx_mobile_unique`?(`mobile`)
          )?ENGINE=InnoDB??DEFAULT?CHARSET=utf8mb4;
          • 運行模板生成命令
          $?goctl?model?mysql?ddl?-src?./model/user.sql?-dir?./model?-c

          3.2 生成 user api 服務

          • 創(chuàng)建 api 文件
          $?vim?api/user.api
          • 編寫 api 文件
          type?(
          ??//?用戶登錄
          ??LoginRequest?{
          ????Mobile???string?`json:"mobile"`
          ????Password?string?`json:"password"`
          ??}
          ??LoginResponse?{
          ????AccessToken??string?`json:"accessToken"`
          ????AccessExpire?int64??`json:"accessExpire"`
          ??}
          ??//?用戶登錄

          ??//?用戶注冊
          ??RegisterRequest?{
          ????Name?????string?`json:"name"`
          ????Gender???int64??`json:"gender"`
          ????Mobile???string?`json:"mobile"`
          ????Password?string?`json:"password"`
          ??}
          ??RegisterResponse?{
          ????Id?????int64??`json:"id"`
          ????Name???string?`json:"name"`
          ????Gender?int64??`json:"gender"`
          ????Mobile?string?`json:"mobile"`
          ??}
          ??//?用戶注冊

          ??//?用戶信息
          ??UserInfoResponse?{
          ????Id?????int64??`json:"id"`
          ????Name???string?`json:"name"`
          ????Gender?int64??`json:"gender"`
          ????Mobile?string?`json:"mobile"`
          ??}
          ??//?用戶信息
          )

          service?User?{
          ??@handler?Login
          ??post?/api/user/login(LoginRequest)?returns?(LoginResponse)
          ??
          ??@handler?Register
          ??post?/api/user/register(RegisterRequest)?returns?(RegisterResponse)
          }

          @server(
          ??jwt:?Auth
          )
          service?User?{
          ??@handler?UserInfo
          ??post?/api/user/userinfo()?returns?(UserInfoResponse)
          }
          • 運行模板生成命令
          $?goctl?api?go?-api?./api/user.api?-dir?./api

          3.3 生成 user rpc 服務

          • 創(chuàng)建 proto 文件
          $?vim?rpc/user.proto
          • 編寫 proto 文件
          syntax = "proto3";

          package userclient;

          option go_package = "user";

          // 用戶登錄
          message LoginRequest {
          string Mobile = 1;
          string Password = 2;
          }
          message LoginResponse {
          int64 Id = 1;
          string Name = 2;
          int64 Gender = 3;
          string Mobile = 4;
          }
          // 用戶登錄

          // 用戶注冊
          message RegisterRequest {
          string Name = 1;
          int64 Gender = 2;
          string Mobile = 3;
          string Password = 4;
          }
          message RegisterResponse {
          int64 Id = 1;
          string Name = 2;
          int64 Gender = 3;
          string Mobile = 4;
          }
          // 用戶注冊

          // 用戶信息
          message UserInfoRequest {
          int64 Id = 1;
          }
          message UserInfoResponse {
          int64 Id = 1;
          string Name = 2;
          int64 Gender = 3;
          string Mobile = 4;
          }
          // 用戶信息

          service User {
          rpc Login(LoginRequest) returns(LoginResponse);
          rpc Register(RegisterRequest) returns(RegisterResponse);
          rpc UserInfo(UserInfoRequest) returns(UserInfoResponse);
          }
          • 運行模板生成命令
          $?goctl?rpc?proto?-src?./rpc/user.proto?-dir?./rpc
          • 添加下載依賴包

            回到 mall 項目根目錄執(zhí)行如下命令:

          $?go?mod?tidy

          3.4 編寫 user rpc 服務

          3.4.1 修改配置文件

          • 修改 user.yaml 配置文件
          $?vim?rpc/etc/user.yaml
          • 修改服務監(jiān)聽地址,端口號為0.0.0.0:9000,Etcd 服務配置,Mysql 服務配置,CacheRedis 服務配置
          Name:?user.rpc
          ListenOn:?0.0.0.0:9000

          Etcd:
          ??Hosts:
          ??-?etcd:2379
          ??Key:?user.rpc

          Mysql:
          ??DataSource:?root:123456@tcp(mysql:3306)/mall?charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai

          CacheRedis:
          -?Host:?redis:6379
          ??Type:?node
          ??Pass:

          3.4.2 添加 user model 依賴

          • 添加 Mysql 服務配置,CacheRedis 服務配置的實例化
          $?vim?rpc/internal/config/config.go
          package?config

          import?(
          ??"github.com/tal-tech/go-zero/core/stores/cache"
          ??"github.com/tal-tech/go-zero/zrpc"
          )

          type?Config?struct?{
          ??zrpc.RpcServerConf

          ??Mysql?struct?{
          ????DataSource?string
          ??}
          ??
          ??CacheRedis?cache.CacheConf
          }
          • 注冊服務上下文 user model 的依賴
          $?vim?rpc/internal/svc/servicecontext.go
          package?svc

          import?(
          ??"mall/service/user/model"
          ??"mall/service/user/rpc/internal/config"

          ??"github.com/tal-tech/go-zero/core/stores/sqlx"
          )

          type?ServiceContext?struct?{
          ??Config?config.Config
          ????
          ??UserModel?model.UserModel
          }

          func?NewServiceContext(c?config.Config)?*ServiceContext?{
          ??conn?:=?sqlx.NewMysql(c.Mysql.DataSource)
          ??return?&ServiceContext{
          ????Config:????c,
          ????UserModel:?model.NewUserModel(conn,?c.CacheRedis),
          ??}
          }

          3.4.3 添加用戶注冊邏輯 Register

          • 添加密碼加密工具

            在根目錄 common 新建 crypt 工具庫,此工具方法主要用于密碼的加密處理。

          $?vim?common/cryptx/crypt.go
          package?cryptx

          import?(
          ??"fmt"

          ??"golang.org/x/crypto/scrypt"
          )

          func?PasswordEncrypt(salt,?password?string)?string?{
          ??dk,?_?:=?scrypt.Key([]byte(password),?[]byte(salt),?32768,?8,?1,?32)
          ??return?fmt.Sprintf("%x",?string(dk))
          }
          • 添加密碼加密 Salt 配置
          $?vim?rpc/etc/user.yaml
          Name:?user.rpc
          ListenOn:?0.0.0.0:9000

          ...

          Salt:?HWVOFkGgPTryzICwd7qnJaZR9KQ2i8xe
          $?vim?rpc/internal/config/config.go
          package?config

          import?(
          ??"github.com/tal-tech/go-zero/core/stores/cache"
          ??"github.com/tal-tech/go-zero/zrpc"
          )

          type?Config?struct?{
          ??...
          ??Salt?string
          }

          • 添加用戶注冊邏輯

            用戶注冊流程,先判斷注冊手機號是否已經被注冊,手機號未被注冊,將用戶信息寫入數(shù)據(jù)庫,用戶密碼需要進行加密存儲。

          $?vim?rpc/internal/logic/registerlogic.go
          package?logic

          import?(
          ??"context"

          ??"mall/common/cryptx"
          ??"mall/service/user/model"
          ??"mall/service/user/rpc/internal/svc"
          ??"mall/service/user/rpc/user"

          ??"github.com/tal-tech/go-zero/core/logx"
          ??"google.golang.org/grpc/status"
          )

          type?RegisterLogic?struct?{
          ??ctx????context.Context
          ??svcCtx?*svc.ServiceContext
          ??logx.Logger
          }

          func?NewRegisterLogic(ctx?context.Context,?svcCtx?*svc.ServiceContext)?*RegisterLogic?{
          ??return?&RegisterLogic{
          ????ctx:????ctx,
          ????svcCtx:?svcCtx,
          ????Logger:?logx.WithContext(ctx),
          ??}
          }

          func?(l?*RegisterLogic)?Register(in?*user.RegisterRequest)?(*user.RegisterResponse,?error)?{
          ??//?判斷手機號是否已經注冊
          ??_,?err?:=?l.svcCtx.UserModel.FindOneByMobile(in.Mobile)
          ??if?err?==?nil?{
          ????return?nil,?status.Error(100,?"該用戶已存在")
          ??}
          ??if?err?==?model.ErrNotFound?{
          ????newUser?:=?model.User{
          ??????Name:?????in.Name,
          ??????Gender:???in.Gender,
          ??????Mobile:???in.Mobile,
          ??????Password:?cryptx.PasswordEncrypt(l.svcCtx.Config.Salt,?in.Password),
          ????}

          ????res,?err?:=?l.svcCtx.UserModel.Insert(&newUser)
          ????if?err?!=?nil?{
          ??????return?nil,?status.Error(500,?err.Error())
          ????}

          ????newUser.Id,?err?=?res.LastInsertId()
          ????if?err?!=?nil?{
          ??????return?nil,?status.Error(500,?err.Error())
          ????}

          ????return?&user.RegisterResponse{
          ??????Id:?????newUser.Id,
          ??????Name:???newUser.Name,
          ??????Gender:?newUser.Gender,
          ??????Mobile:?newUser.Mobile,
          ????},?nil

          ??}

          ??return?nil,?status.Error(500,?err.Error())
          }

          3.4.4 添加用戶登錄邏輯 Login

          用戶登錄流程,通過手機號查詢判斷用戶是否是注冊用戶,如果是注冊用戶,需要將用戶輸入的密碼進行加密與數(shù)據(jù)庫中用戶加密密碼進行對比驗證。

          $?vim?rpc/internal/logic/loginlogic.go
          package?logic

          import?(
          ??"context"

          ??"mall/common/cryptx"
          ??"mall/service/user/model"
          ??"mall/service/user/rpc/internal/svc"
          ??"mall/service/user/rpc/user"

          ??"github.com/tal-tech/go-zero/core/logx"
          ??"google.golang.org/grpc/status"
          )

          type?LoginLogic?struct?{
          ??ctx????context.Context
          ??svcCtx?*svc.ServiceContext
          ??logx.Logger
          }

          func?NewLoginLogic(ctx?context.Context,?svcCtx?*svc.ServiceContext)?*LoginLogic?{
          ??return?&LoginLogic{
          ????ctx:????ctx,
          ????svcCtx:?svcCtx,
          ????Logger:?logx.WithContext(ctx),
          ??}
          }

          func?(l?*LoginLogic)?Login(in?*user.LoginRequest)?(*user.LoginResponse,?error)?{
          ??//?查詢用戶是否存在
          ??res,?err?:=?l.svcCtx.UserModel.FindOneByMobile(in.Mobile)
          ??if?err?!=?nil?{
          ????if?err?==?model.ErrNotFound?{
          ??????return?nil,?status.Error(100,?"用戶不存在")
          ????}
          ????return?nil,?status.Error(500,?err.Error())
          ??}

          ??//?判斷密碼是否正確
          ??password?:=?cryptx.PasswordEncrypt(l.svcCtx.Config.Salt,?in.Password)
          ??if?password?!=?res.Password?{
          ????return?nil,?status.Error(100,?"密碼錯誤")
          ??}

          ??return?&user.LoginResponse{
          ????Id:?????res.Id,
          ????Name:???res.Name,
          ????Gender:?res.Gender,
          ????Mobile:?res.Mobile,
          ??},?nil
          }

          3.4.5 添加用戶信息邏輯 UserInfo

          $?vim?rpc/internal/logic/userinfologic.go
          package?logic

          import?(
          ??"context"

          ??"mall/service/user/model"
          ??"mall/service/user/rpc/internal/svc"
          ??"mall/service/user/rpc/user"

          ??"github.com/tal-tech/go-zero/core/logx"
          ??"google.golang.org/grpc/status"
          )

          type?UserInfoLogic?struct?{
          ??ctx????context.Context
          ??svcCtx?*svc.ServiceContext
          ??logx.Logger
          }

          func?NewUserInfoLogic(ctx?context.Context,?svcCtx?*svc.ServiceContext)?*UserInfoLogic?{
          ??return?&UserInfoLogic{
          ????ctx:????ctx,
          ????svcCtx:?svcCtx,
          ????Logger:?logx.WithContext(ctx),
          ??}
          }

          func?(l?*UserInfoLogic)?UserInfo(in?*user.UserInfoRequest)?(*user.UserInfoResponse,?error)?{
          ??//?查詢用戶是否存在
          ??res,?err?:=?l.svcCtx.UserModel.FindOne(in.Id)
          ??if?err?!=?nil?{
          ????if?err?==?model.ErrNotFound?{
          ??????return?nil,?status.Error(100,?"用戶不存在")
          ????}
          ????return?nil,?status.Error(500,?err.Error())
          ??}

          ??return?&user.UserInfoResponse{
          ????Id:?????res.Id,
          ????Name:???res.Name,
          ????Gender:?res.Gender,
          ????Mobile:?res.Mobile,
          ??},?nil
          }

          3.5 編寫 user api 服務

          3.5.1 修改配置文件

          • 修改 user.yaml 配置文件
          $?vim?api/etc/user.yaml
          • 修改服務地址,端口號為0.0.0.0:8000,Mysql 服務配置,CacheRedis 服務配置,Auth 驗證配置
          Name:?User
          Host:?0.0.0.0
          Port:?8000

          Mysql:
          ??DataSource:?root:123456@tcp(mysql:3306)/mall?charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai

          CacheRedis:
          -?Host:?redis:6379
          ??Pass:
          ??Type:?node

          Auth:
          ??AccessSecret:?uOvKLmVfztaXGpNYd4Z0I1SiT7MweJhl
          ??AccessExpire:?86400

          3.5.2 添加 user rpc 依賴

          • 添加 user rpc 服務配置
          $?vim?api/etc/user.yaml
          Name:?User
          Host:?0.0.0.0
          Port:?8000

          ...

          UserRpc:
          ??Etcd:
          ????Hosts:
          ????-?etcd:2379
          ????Key:?user.rpc
          • 添加 user rpc 服務配置的實例化
          $?vim?api/internal/config/config.go
          package?config

          import?(
          ??"github.com/tal-tech/go-zero/rest"
          ??"github.com/tal-tech/go-zero/zrpc"
          )

          type?Config?struct?{
          ??rest.RestConf

          ??Auth?struct?{
          ????AccessSecret?string
          ????AccessExpire?int64
          ??}

          ??UserRpc?zrpc.RpcClientConf
          }
          • 注冊服務上下文 user rpc 的依賴
          $?vim?api/internal/svc/servicecontext.go
          package?svc

          import?(
          ??"mall/service/user/api/internal/config"
          ??"mall/service/user/rpc/userclient"

          ??"github.com/tal-tech/go-zero/zrpc"
          )

          type?ServiceContext?struct?{
          ??Config?config.Config
          ????
          ??UserRpc?userclient.User
          }

          func?NewServiceContext(c?config.Config)?*ServiceContext?{
          ??return?&ServiceContext{
          ????Config:??c,
          ????UserRpc:?userclient.NewUser(zrpc.MustNewClient(c.UserRpc)),
          ??}
          }

          3.5.3 ?添加用戶注冊邏輯 Register

          $?vim?api/internal/logic/registerlogic.go
          package?logic

          import?(
          ??"context"

          ??"mall/service/user/api/internal/svc"
          ??"mall/service/user/api/internal/types"
          ??"mall/service/user/rpc/userclient"

          ??"github.com/tal-tech/go-zero/core/logx"
          )

          type?RegisterLogic?struct?{
          ??logx.Logger
          ??ctx????context.Context
          ??svcCtx?*svc.ServiceContext
          }

          func?NewRegisterLogic(ctx?context.Context,?svcCtx?*svc.ServiceContext)?RegisterLogic?{
          ??return?RegisterLogic{
          ????Logger:?logx.WithContext(ctx),
          ????ctx:????ctx,
          ????svcCtx:?svcCtx,
          ??}
          }

          func?(l?*RegisterLogic)?Register(req?types.RegisterRequest)?(resp?*types.RegisterResponse,?err?error)?{
          ??res,?err?:=?l.svcCtx.UserRpc.Register(l.ctx,?&userclient.RegisterRequest{
          ????Name:?????req.Name,
          ????Gender:???req.Gender,
          ????Mobile:???req.Mobile,
          ????Password:?req.Password,
          ??})
          ??if?err?!=?nil?{
          ????return?nil,?err
          ??}

          ??return?&types.RegisterResponse{
          ????Id:?????res.Id,
          ????Name:???res.Name,
          ????Gender:?res.Gender,
          ????Mobile:?res.Mobile,
          ??},?nil
          }

          3.5.4 ?添加用戶登錄邏輯 Login

          • 添加 JWT 工具

            在根目錄 common 新建 jwtx 工具庫,用于生成用戶 token

          $?vim?common/jwtx/jwt.go
          package?jwtx

          import?"github.com/golang-jwt/jwt"

          func?GetToken(secretKey?string,?iat,?seconds,?uid?int64)?(string,?error)?{
          ??claims?:=?make(jwt.MapClaims)
          ??claims["exp"]?=?iat?+?seconds
          ??claims["iat"]?=?iat
          ??claims["uid"]?=?uid
          ??token?:=?jwt.New(jwt.SigningMethodHS256)
          ??token.Claims?=?claims
          ??return?token.SignedString([]byte(secretKey))
          }
          • 添加用戶登錄邏輯

            通過調用 user rpc 服務進行登錄驗證,登錄成功后,使用用戶信息生成對應的 token 以及 token 的有效期。

          $?vim?api/internal/logic/loginlogic.go
          package?logic

          import?(
          ??"context"
          ??"time"

          ??"mall/common/jwtx"
          ??"mall/service/user/api/internal/svc"
          ??"mall/service/user/api/internal/types"
          ??"mall/service/user/rpc/userclient"

          ??"github.com/tal-tech/go-zero/core/logx"
          )

          type?LoginLogic?struct?{
          ??logx.Logger
          ??ctx????context.Context
          ??svcCtx?*svc.ServiceContext
          }

          func?NewLoginLogic(ctx?context.Context,?svcCtx?*svc.ServiceContext)?LoginLogic?{
          ??return?LoginLogic{
          ????Logger:?logx.WithContext(ctx),
          ????ctx:????ctx,
          ????svcCtx:?svcCtx,
          ??}
          }

          func?(l?*LoginLogic)?Login(req?types.LoginRequest)?(resp?*types.LoginResponse,?err?error)?{
          ??res,?err?:=?l.svcCtx.UserRpc.Login(l.ctx,?&userclient.LoginRequest{
          ????Mobile:???req.Mobile,
          ????Password:?req.Password,
          ??})
          ??if?err?!=?nil?{
          ????return?nil,?err
          ??}

          ??now?:=?time.Now().Unix()
          ??accessExpire?:=?l.svcCtx.Config.Auth.AccessExpire

          ??accessToken,?err?:=?jwtx.GetToken(l.svcCtx.Config.Auth.AccessSecret,?now,?accessExpire,?res.Id)
          ??if?err?!=?nil?{
          ????return?nil,?err
          ??}

          ??return?&types.LoginResponse{
          ????AccessToken:??accessToken,
          ????AccessExpire:?now?+?accessExpire,
          ??},?nil
          }

          3.5.5 ?添加用戶信息邏輯 UserInfo

          $?vim?api/internal/logic/userinfologic.go
          package?logic

          import?(
          ??"context"
          ??"encoding/json"

          ??"mall/service/user/api/internal/svc"
          ??"mall/service/user/api/internal/types"
          ??"mall/service/user/rpc/userclient"

          ??"github.com/tal-tech/go-zero/core/logx"
          )

          type?UserInfoLogic?struct?{
          ??logx.Logger
          ??ctx????context.Context
          ??svcCtx?*svc.ServiceContext
          }

          func?NewUserInfoLogic(ctx?context.Context,?svcCtx?*svc.ServiceContext)?UserInfoLogic?{
          ??return?UserInfoLogic{
          ????Logger:?logx.WithContext(ctx),
          ????ctx:????ctx,
          ????svcCtx:?svcCtx,
          ??}
          }

          func?(l?*UserInfoLogic)?UserInfo()?(resp?*types.UserInfoResponse,?err?error)?{
          ??uid,?_?:=?l.ctx.Value("uid").(json.Number).Int64()
          ??res,?err?:=?l.svcCtx.UserRpc.UserInfo(l.ctx,?&userclient.UserInfoRequest{
          ????Id:?uid,
          ??})
          ??if?err?!=?nil?{
          ????return?nil,?err
          ??}

          ??return?&types.UserInfoResponse{
          ????Id:?????res.Id,
          ????Name:???res.Name,
          ????Gender:?res.Gender,
          ????Mobile:?res.Mobile,
          ??},?nil
          }

          通過 l.ctx.Value("uid") 可獲取 jwt token 中自定義的參數(shù)

          3.6 啟動 user rpc 服務

          提示:啟動服務需要在 golang 容器中啟動

          $?cd?mall/service/user/rpc
          $?go?run?user.go?-f?etc/user.yaml
          Starting?rpc?server?at?127.0.0.1:9000...

          3.7 啟動 user api 服務

          提示:啟動服務需要在 golang 容器中啟動

          $?cd?mall/service/user/api
          $?go?run?user.go?-f?etc/user.yaml
          Starting?server?at?0.0.0.0:8000...

          項目地址

          https://github.com/zeromicro/go-zero

          歡迎使用 go-zerostar 支持我們!



          推薦閱讀


          福利

          我為大家整理了一份從入門到進階的Go學習資料禮包,包含學習建議:入門看什么,進階看什么。關注公眾號 「polarisxu」,回復?ebook?獲取;還可以回復「進群」,和數(shù)萬 Gopher 交流學習。


          瀏覽 26
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  九色免费网站 | 亚洲成人篇在线观看无码 | 青娱乐青青草在线观看 | 亚洲 精品一区二区三区 | 加勒比综合色 |