1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

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

        共 13967字,需瀏覽 28分鐘

         ·

        2022-02-21 21:03

        序言

        我們通過一個系列文章跟大家詳細(xì)展示一個 go-zero 微服務(wù)示例,整個系列分十篇文章,目錄結(jié)構(gòu)如下:

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

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

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

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

        3 用戶服務(wù)(user)

        • 進(jìn)入服務(wù)工作區(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 服務(wù)

        • 創(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 服務(wù)

        • 創(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 服務(wù)

        3.4.1 修改配置文件

        • 修改 user.yaml 配置文件
        $?vim?rpc/etc/user.yaml
        • 修改服務(wù)監(jiān)聽地址,端口號為0.0.0.0:9000,Etcd 服務(wù)配置,Mysql 服務(wù)配置,CacheRedis 服務(wù)配置
        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 服務(wù)配置,CacheRedis 服務(wù)配置的實例化
        $?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
        }
        • 注冊服務(wù)上下文 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
        }

        • 添加用戶注冊邏輯

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

        $?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)?{
        ??//?判斷手機號是否已經(jīng)注冊
        ??_,?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

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

        $?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 服務(wù)

        3.5.1 修改配置文件

        • 修改 user.yaml 配置文件
        $?vim?api/etc/user.yaml
        • 修改服務(wù)地址,端口號為0.0.0.0:8000,Mysql 服務(wù)配置,CacheRedis 服務(wù)配置,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 服務(wù)配置
        $?vim?api/etc/user.yaml
        Name:?User
        Host:?0.0.0.0
        Port:?8000

        ...

        UserRpc:
        ??Etcd:
        ????Hosts:
        ????-?etcd:2379
        ????Key:?user.rpc
        • 添加 user rpc 服務(wù)配置的實例化
        $?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
        }
        • 注冊服務(wù)上下文 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))
        }
        • 添加用戶登錄邏輯

          通過調(diào)用 user rpc 服務(wù)進(jìn)行登錄驗證,登錄成功后,使用用戶信息生成對應(yīng)的 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 服務(wù)

        提示:啟動服務(wù)需要在 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 服務(wù)

        提示:啟動服務(wù)需要在 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 支持我們!



        推薦閱讀


        福利

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


        瀏覽 26
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

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

        手機掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            寡妇一边摸一边爽一边叫床 | 操日本熟妇 | 青青干视频 | ㊣最新国产の精品bt伙计 | 91色色| 黄色片网站免费在线观看 | 性中国xxxxsss3 | 欧美国产日韩激情 | 国产最爽的乱淫视频国语对白1 | 亂伦国产一区二区三 |