add: user module

This commit is contained in:
viletyy 2021-07-09 14:48:22 +08:00 committed by 来自村里的小螃蟹
parent bf7c262d78
commit 57f071922e
18 changed files with 2092 additions and 50 deletions

View File

@ -1,7 +1,7 @@
<!--
* @Date: 2021-03-21 19:54:57
* @LastEditors: viletyy
* @LastEditTime: 2021-06-15 00:19:24
* @LastEditTime: 2021-07-09 14:48:06
* @FilePath: /potato/README.md
-->
# Potato
@ -10,13 +10,13 @@
Go项目脚手架
基于gin、gorm、zap的脚手架
基于gin、gorm、zap、cron的脚手架
本脚手架包含以下内容:
1. mvc结构。
2. swagger接口文档。
3. 配置、数据库、redis、日志、工具库封装。
3. 配置、数据库、redis、日志、工具库、后台任务封装。
4. 单点登陆(jwt)。
## 内容列表
@ -40,6 +40,7 @@ potato
├── internal(内部模块)
│   ├── controller(控制器层,用于存放控制器)
│   ├── dao(数据访问层所有与数据相关等操作都会在dao层进行)
│   ├── job(后台任务)
│   ├── middleware(HTTP中间件)
│   ├── model(模型层用于存放model对象)
│   ├── routers(路由相关逻辑处理)
@ -82,8 +83,11 @@ jaegertracing/all-in-one:1.16
## 使用说明
```sh
# 启动项目
$ go build -o potato main.go
$ ./potato
# 生成api文档
$ swag init
```
## 相关仓库
@ -91,6 +95,7 @@ $ ./potato
- [Gin](https://github.com/gin-gonic/gin) — Web Framework
- [Gorm](https://github.com/jinzhu/gorm) — ORM
- [Swag](https://github.com/swaggo/swag) - RESTful API Doc
- [Cron](https://github.com/robfig/cron) - A cron library
## 如何贡献

View File

@ -59,6 +59,82 @@ var doc = `{
}
}
},
"/user/login": {
"post": {
"consumes": [
"multipart/form-data"
],
"produces": [
"application/json"
],
"summary": "用户登录",
"parameters": [
{
"type": "string",
"description": "用户名",
"name": "username",
"in": "formData",
"required": true
},
{
"type": "string",
"description": "密码",
"name": "password",
"in": "formData",
"required": true
}
],
"responses": {
"200": {
"description": "请求成功",
"schema": {
"$ref": "#/definitions/errcode.Error"
}
}
}
}
},
"/user/register": {
"post": {
"consumes": [
"multipart/form-data"
],
"produces": [
"application/json"
],
"summary": "用户注册",
"parameters": [
{
"type": "string",
"description": "用户名",
"name": "username",
"in": "formData",
"required": true
},
{
"type": "string",
"description": "密码",
"name": "password",
"in": "formData",
"required": true
},
{
"type": "string",
"description": "昵称",
"name": "nickname",
"in": "formData"
}
],
"responses": {
"200": {
"description": "请求成功",
"schema": {
"$ref": "#/definitions/errcode.Error"
}
}
}
}
},
"/v1/upload": {
"post": {
"consumes": [
@ -101,6 +177,248 @@ var doc = `{
}
}
},
"/v1/users": {
"get": {
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"users"
],
"summary": "用户列表",
"parameters": [
{
"type": "string",
"description": "auth token",
"name": "token",
"in": "header",
"required": true
},
{
"type": "integer",
"description": "页码",
"name": "page",
"in": "query"
},
{
"type": "integer",
"description": "每页数量",
"name": "page_size",
"in": "query"
},
{
"maxLength": 30,
"type": "string",
"description": "用户名",
"name": "username",
"in": "query"
},
{
"maxLength": 30,
"type": "string",
"description": "昵称",
"name": "nickname",
"in": "query"
}
],
"responses": {
"200": {
"description": "请求成功",
"schema": {
"$ref": "#/definitions/model.User"
}
}
}
},
"post": {
"consumes": [
"multipart/form-data"
],
"produces": [
"application/json"
],
"tags": [
"users"
],
"summary": "新增用户",
"parameters": [
{
"type": "string",
"description": "auth token",
"name": "token",
"in": "header",
"required": true
},
{
"maxLength": 30,
"minLength": 1,
"type": "string",
"description": "用户名",
"name": "username",
"in": "formData",
"required": true
},
{
"maxLength": 18,
"minLength": 1,
"type": "string",
"description": "密码",
"name": "password",
"in": "formData",
"required": true
},
{
"maxLength": 30,
"type": "string",
"description": "昵称",
"name": "nickname",
"in": "formData"
}
],
"responses": {
"200": {
"description": "请求成功",
"schema": {
"$ref": "#/definitions/model.User"
}
}
}
}
},
"/v1/users/{id}": {
"get": {
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"users"
],
"summary": "用户",
"parameters": [
{
"type": "string",
"description": "auth token",
"name": "token",
"in": "header",
"required": true
},
{
"type": "integer",
"description": "用户 ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "请求成功",
"schema": {
"$ref": "#/definitions/model.User"
}
}
}
},
"delete": {
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"users"
],
"summary": "删除用户",
"parameters": [
{
"type": "string",
"description": "auth token",
"name": "token",
"in": "header",
"required": true
},
{
"type": "integer",
"description": "用户ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "请求成功",
"schema": {
"$ref": "#/definitions/model.User"
}
}
}
},
"patch": {
"consumes": [
"multipart/form-data"
],
"produces": [
"application/json"
],
"tags": [
"users"
],
"summary": "更新用户",
"parameters": [
{
"type": "string",
"description": "auth token",
"name": "token",
"in": "header",
"required": true
},
{
"type": "integer",
"description": "用户ID",
"name": "id",
"in": "path",
"required": true
},
{
"maxLength": 30,
"type": "string",
"description": "用户名",
"name": "username",
"in": "formData"
},
{
"maxLength": 18,
"type": "string",
"description": "密码",
"name": "password",
"in": "formData"
},
{
"maxLength": 30,
"type": "string",
"description": "昵称",
"name": "nickname",
"in": "formData"
}
],
"responses": {
"200": {
"description": "请求成功",
"schema": {
"$ref": "#/definitions/model.User"
}
}
}
}
},
"/v1/vendors": {
"get": {
"consumes": [
@ -328,7 +646,18 @@ var doc = `{
},
"definitions": {
"basic.Vendor": {
"type": "object"
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"name": {
"type": "string"
},
"uuid": {
"type": "integer"
}
}
},
"errcode.Error": {
"type": "object",
@ -343,6 +672,29 @@ var doc = `{
"type": "string"
}
}
},
"model.User": {
"type": "object",
"properties": {
"created_at": {
"type": "string"
},
"id": {
"type": "integer"
},
"is_admin": {
"type": "boolean"
},
"nickname": {
"type": "string"
},
"updated_at": {
"type": "string"
},
"username": {
"type": "string"
}
}
}
}
}`

View File

@ -43,6 +43,82 @@
}
}
},
"/user/login": {
"post": {
"consumes": [
"multipart/form-data"
],
"produces": [
"application/json"
],
"summary": "用户登录",
"parameters": [
{
"type": "string",
"description": "用户名",
"name": "username",
"in": "formData",
"required": true
},
{
"type": "string",
"description": "密码",
"name": "password",
"in": "formData",
"required": true
}
],
"responses": {
"200": {
"description": "请求成功",
"schema": {
"$ref": "#/definitions/errcode.Error"
}
}
}
}
},
"/user/register": {
"post": {
"consumes": [
"multipart/form-data"
],
"produces": [
"application/json"
],
"summary": "用户注册",
"parameters": [
{
"type": "string",
"description": "用户名",
"name": "username",
"in": "formData",
"required": true
},
{
"type": "string",
"description": "密码",
"name": "password",
"in": "formData",
"required": true
},
{
"type": "string",
"description": "昵称",
"name": "nickname",
"in": "formData"
}
],
"responses": {
"200": {
"description": "请求成功",
"schema": {
"$ref": "#/definitions/errcode.Error"
}
}
}
}
},
"/v1/upload": {
"post": {
"consumes": [
@ -85,6 +161,248 @@
}
}
},
"/v1/users": {
"get": {
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"users"
],
"summary": "用户列表",
"parameters": [
{
"type": "string",
"description": "auth token",
"name": "token",
"in": "header",
"required": true
},
{
"type": "integer",
"description": "页码",
"name": "page",
"in": "query"
},
{
"type": "integer",
"description": "每页数量",
"name": "page_size",
"in": "query"
},
{
"maxLength": 30,
"type": "string",
"description": "用户名",
"name": "username",
"in": "query"
},
{
"maxLength": 30,
"type": "string",
"description": "昵称",
"name": "nickname",
"in": "query"
}
],
"responses": {
"200": {
"description": "请求成功",
"schema": {
"$ref": "#/definitions/model.User"
}
}
}
},
"post": {
"consumes": [
"multipart/form-data"
],
"produces": [
"application/json"
],
"tags": [
"users"
],
"summary": "新增用户",
"parameters": [
{
"type": "string",
"description": "auth token",
"name": "token",
"in": "header",
"required": true
},
{
"maxLength": 30,
"minLength": 1,
"type": "string",
"description": "用户名",
"name": "username",
"in": "formData",
"required": true
},
{
"maxLength": 18,
"minLength": 1,
"type": "string",
"description": "密码",
"name": "password",
"in": "formData",
"required": true
},
{
"maxLength": 30,
"type": "string",
"description": "昵称",
"name": "nickname",
"in": "formData"
}
],
"responses": {
"200": {
"description": "请求成功",
"schema": {
"$ref": "#/definitions/model.User"
}
}
}
}
},
"/v1/users/{id}": {
"get": {
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"users"
],
"summary": "用户",
"parameters": [
{
"type": "string",
"description": "auth token",
"name": "token",
"in": "header",
"required": true
},
{
"type": "integer",
"description": "用户 ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "请求成功",
"schema": {
"$ref": "#/definitions/model.User"
}
}
}
},
"delete": {
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"users"
],
"summary": "删除用户",
"parameters": [
{
"type": "string",
"description": "auth token",
"name": "token",
"in": "header",
"required": true
},
{
"type": "integer",
"description": "用户ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "请求成功",
"schema": {
"$ref": "#/definitions/model.User"
}
}
}
},
"patch": {
"consumes": [
"multipart/form-data"
],
"produces": [
"application/json"
],
"tags": [
"users"
],
"summary": "更新用户",
"parameters": [
{
"type": "string",
"description": "auth token",
"name": "token",
"in": "header",
"required": true
},
{
"type": "integer",
"description": "用户ID",
"name": "id",
"in": "path",
"required": true
},
{
"maxLength": 30,
"type": "string",
"description": "用户名",
"name": "username",
"in": "formData"
},
{
"maxLength": 18,
"type": "string",
"description": "密码",
"name": "password",
"in": "formData"
},
{
"maxLength": 30,
"type": "string",
"description": "昵称",
"name": "nickname",
"in": "formData"
}
],
"responses": {
"200": {
"description": "请求成功",
"schema": {
"$ref": "#/definitions/model.User"
}
}
}
}
},
"/v1/vendors": {
"get": {
"consumes": [
@ -312,7 +630,18 @@
},
"definitions": {
"basic.Vendor": {
"type": "object"
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"name": {
"type": "string"
},
"uuid": {
"type": "integer"
}
}
},
"errcode.Error": {
"type": "object",
@ -327,6 +656,29 @@
"type": "string"
}
}
},
"model.User": {
"type": "object",
"properties": {
"created_at": {
"type": "string"
},
"id": {
"type": "integer"
},
"is_admin": {
"type": "boolean"
},
"nickname": {
"type": "string"
},
"updated_at": {
"type": "string"
},
"username": {
"type": "string"
}
}
}
}
}

View File

@ -1,6 +1,13 @@
basePath: /api
definitions:
basic.Vendor:
properties:
id:
type: integer
name:
type: string
uuid:
type: integer
type: object
errcode.Error:
properties:
@ -11,6 +18,21 @@ definitions:
msg:
type: string
type: object
model.User:
properties:
created_at:
type: string
id:
type: integer
is_admin:
type: boolean
nickname:
type: string
updated_at:
type: string
username:
type: string
type: object
info:
contact: {}
description: This is a potato use golang
@ -40,6 +62,56 @@ paths:
schema:
$ref: '#/definitions/errcode.Error'
summary: 鉴权验证
/user/login:
post:
consumes:
- multipart/form-data
parameters:
- description: 用户名
in: formData
name: username
required: true
type: string
- description: 密码
in: formData
name: password
required: true
type: string
produces:
- application/json
responses:
"200":
description: 请求成功
schema:
$ref: '#/definitions/errcode.Error'
summary: 用户登录
/user/register:
post:
consumes:
- multipart/form-data
parameters:
- description: 用户名
in: formData
name: username
required: true
type: string
- description: 密码
in: formData
name: password
required: true
type: string
- description: 昵称
in: formData
name: nickname
type: string
produces:
- application/json
responses:
"200":
description: 请求成功
schema:
$ref: '#/definitions/errcode.Error'
summary: 用户注册
/v1/upload:
post:
consumes:
@ -68,6 +140,170 @@ paths:
schema:
$ref: '#/definitions/errcode.Error'
summary: 上传文件
/v1/users:
get:
consumes:
- application/json
parameters:
- description: auth token
in: header
name: token
required: true
type: string
- description: 页码
in: query
name: page
type: integer
- description: 每页数量
in: query
name: page_size
type: integer
- description: 用户名
in: query
maxLength: 30
name: username
type: string
- description: 昵称
in: query
maxLength: 30
name: nickname
type: string
produces:
- application/json
responses:
"200":
description: 请求成功
schema:
$ref: '#/definitions/model.User'
summary: 用户列表
tags:
- users
post:
consumes:
- multipart/form-data
parameters:
- description: auth token
in: header
name: token
required: true
type: string
- description: 用户名
in: formData
maxLength: 30
minLength: 1
name: username
required: true
type: string
- description: 密码
in: formData
maxLength: 18
minLength: 1
name: password
required: true
type: string
- description: 昵称
in: formData
maxLength: 30
name: nickname
type: string
produces:
- application/json
responses:
"200":
description: 请求成功
schema:
$ref: '#/definitions/model.User'
summary: 新增用户
tags:
- users
/v1/users/{id}:
delete:
consumes:
- application/json
parameters:
- description: auth token
in: header
name: token
required: true
type: string
- description: 用户ID
in: path
name: id
required: true
type: integer
produces:
- application/json
responses:
"200":
description: 请求成功
schema:
$ref: '#/definitions/model.User'
summary: 删除用户
tags:
- users
get:
consumes:
- application/json
parameters:
- description: auth token
in: header
name: token
required: true
type: string
- description: 用户 ID
in: path
name: id
required: true
type: integer
produces:
- application/json
responses:
"200":
description: 请求成功
schema:
$ref: '#/definitions/model.User'
summary: 用户
tags:
- users
patch:
consumes:
- multipart/form-data
parameters:
- description: auth token
in: header
name: token
required: true
type: string
- description: 用户ID
in: path
name: id
required: true
type: integer
- description: 用户名
in: formData
maxLength: 30
name: username
type: string
- description: 密码
in: formData
maxLength: 18
name: password
type: string
- description: 昵称
in: formData
maxLength: 30
name: nickname
type: string
produces:
- application/json
responses:
"200":
description: 请求成功
schema:
$ref: '#/definitions/model.User'
summary: 更新用户
tags:
- users
/v1/vendors:
get:
consumes:

1
go.mod
View File

@ -22,7 +22,6 @@ require (
github.com/opentracing/opentracing-go v1.2.0
github.com/robfig/cron/v3 v3.0.0
github.com/smacker/opentracing-gorm v0.0.0-20181207094635-cd4974441042 // indirect
github.com/soheilhy/cmux v0.1.5
github.com/spf13/viper v1.7.1
github.com/swaggo/gin-swagger v1.3.0
github.com/swaggo/swag v1.7.0

View File

@ -1,7 +1,7 @@
/*
* @Date: 2021-03-22 10:12:38
* @LastEditors: viletyy
* @LastEditTime: 2021-06-14 21:11:29
* @LastEditTime: 2021-07-09 14:27:45
* @FilePath: /potato/initialize/gorm.go
*/
package initialize
@ -60,8 +60,9 @@ func GormSet(db *gorm.DB) {
// 设置迁移
db.AutoMigrate(
basic.Vendor{},
model.Auth{},
&basic.Vendor{},
&model.User{},
&model.Auth{},
)
// 设置空闲连接池中的最大连接数

View File

@ -0,0 +1,85 @@
/*
* @Date: 2021-07-09 14:44:50
* @LastEditors: viletyy
* @LastEditTime: 2021-07-09 14:45:52
* @FilePath: /potato/internal/controller/api/user.go
*/
package api
import (
"github.com/gin-gonic/gin"
"github.com/viletyy/potato/global"
"github.com/viletyy/potato/internal/service"
"github.com/viletyy/potato/pkg/app"
"github.com/viletyy/potato/pkg/errcode"
)
// @Summary 用户注册
// @Description
// @Accept mpfd
// @Produce json
// @Param username formData string true "用户名"
// @Param password formData string true "密码"
// @Param nickname formData string false "昵称"
// @Success 200 {object} errcode.Error "请求成功"
// @Router /user/register [post]
func UserRegister(c *gin.Context) {
param := service.UserRegisterRequest{}
response := app.NewResponse(c)
valid, errs := app.BindAndValid(c, &param)
if !valid {
global.GO_LOG.Sugar().Errorf("app.BindAndValid errs: %v", errs)
response.ToResponseErrors(errs.Errors())
return
}
svc := service.New(c.Request.Context())
dbUser, err := svc.RegisterUser(&param)
if err != nil {
global.GO_LOG.Sugar().Errorf("svc.RegisterUser err: %v", err)
response.ToErrorResponse(errcode.ErrorRegisterUserError)
return
}
response.ToResponse(dbUser)
}
// @Summary 用户登录
// @Description
// @Accept mpfd
// @Produce json
// @Param username formData string true "用户名"
// @Param password formData string true "密码"
// @Success 200 {object} errcode.Error "请求成功"
// @Router /user/login [post]
func UserLogin(c *gin.Context) {
param := service.UserLoginRequest{}
response := app.NewResponse(c)
valid, errs := app.BindAndValid(c, &param)
if !valid {
global.GO_LOG.Sugar().Errorf("app.BindAndValid errs: %v", errs)
response.ToResponseErrors(errs.Errors())
return
}
svc := service.New(c.Request.Context())
dbUser, err := svc.LoginUser(&param)
if err != nil {
global.GO_LOG.Sugar().Errorf("svc.LoginUser err: %v", err)
response.ToErrorResponse(errcode.ErrorLoginUserError)
return
}
token, err := app.GenerateToken(dbUser.Username, dbUser.Password)
if err != nil {
global.GO_LOG.Sugar().Errorf("app.GenerateToken err: %v", err)
response.ToErrorResponse(errcode.UnauthorizedTokenGenerate)
return
}
response.ToResponse(gin.H{
"username": dbUser.Username,
"is_admin": dbUser.IsAdmin,
"token": token,
})
}

View File

@ -1,7 +1,214 @@
/*
* @Date: 2021-03-21 19:54:57
* @LastEditors: viletyy
* @LastEditTime: 2021-06-10 19:02:24
* @LastEditTime: 2021-07-09 14:32:27
* @FilePath: /potato/internal/controller/api/v1/user.go
*/
package v1
import (
"github.com/gin-gonic/gin"
"github.com/jinzhu/gorm"
"github.com/viletyy/potato/global"
"github.com/viletyy/potato/internal/service"
"github.com/viletyy/potato/pkg/app"
"github.com/viletyy/potato/pkg/errcode"
"github.com/viletyy/yolk/convert"
)
type User struct{}
func NewUser() User {
return User{}
}
// @Summary 用户列表
// @Tags users
// @Description
// @Accept json
// @Produce json
// @Param token header string true "auth token"
// @Param page query int false "页码"
// @Param page_size query int false "每页数量"
// @Param username query string false "用户名" maxlength(30)
// @Param nickname query string false "昵称" maxlength(30)
// @Success 200 {object} model.User "请求成功"
// @Router /v1/users [get]
func (user User) List(c *gin.Context) {
param := service.UserListRequest{}
response := app.NewResponse(c)
valid, errs := app.BindAndValid(c, &param)
if !valid {
global.GO_LOG.Sugar().Errorf("app.BindAndValid errs: %v", errs)
response.ToResponseErrors(errs.Errors())
return
}
svc := service.New(c.Request.Context())
paper := app.Pager{Page: app.GetPage(c), PageSize: app.GetPageSize(c)}
total, err := svc.CountUser(&service.CountUserRequest{Username: param.Username, Nickname: param.Nickname})
if err != nil {
global.GO_LOG.Sugar().Errorf("svc.CountUser err: %v", err)
response.ToErrorResponse(errcode.ErrorCountUserFail)
return
}
users, err := svc.GetUserList(&param, &paper)
if err != nil {
global.GO_LOG.Sugar().Errorf("svc.GetUserList err: %v", err)
response.ToErrorResponse(errcode.ErrorGetUserListFail)
return
}
response.ToResponseList(users, total)
}
// @Summary 用户
// @Tags users
// @Description
// @Accept json
// @Produce json
// @Param token header string true "auth token"
// @Param id path int true "用户 ID"
// @Success 200 {object} model.User "请求成功"
// @Router /v1/users/{id} [get]
func (user User) Get(c *gin.Context) {
userID, err := convert.StrTo(c.Param("id")).Int64()
response := app.NewResponse(c)
if err != nil {
global.GO_LOG.Sugar().Errorf("convert.StrTo err: %v", err)
response.ToErrorResponse(errcode.InvalidParams)
return
}
param := service.UserRequest{ID: userID}
valid, errs := app.BindAndValid(c, &param)
if !valid {
global.GO_LOG.Sugar().Errorf("app.BindAndValid errs: %v", errs)
response.ToResponseErrors(errs.Errors())
return
}
svc := service.New(c.Request.Context())
dbUser, err := svc.GetUser(&param)
if err != nil {
global.GO_LOG.Sugar().Errorf("svc.GetUser err: %v", err)
response.ToErrorResponse(errcode.ErrorGetUserFail)
return
}
response.ToResponse(dbUser)
}
// @Summary 新增用户
// @Tags users
// @Description
// @Accept mpfd
// @Produce json
// @Param token header string true "auth token"
// @Param username formData string true "用户名" minlength(1) maxlength(30)
// @Param password formData string true "密码" minlength(1) maxlength(18)
// @param nickname formData string false "昵称" maxlength(30)
// @Success 200 {object} model.User "请求成功"
// @Router /v1/users [post]
func (user User) Create(c *gin.Context) {
param := service.CreateUserRequest{}
response := app.NewResponse(c)
valid, errs := app.BindAndValid(c, &param)
if !valid {
global.GO_LOG.Sugar().Errorf("app.BindAndValid errs: %v", errs)
response.ToResponseErrors(errs.Errors())
return
}
svc := service.New(c.Request.Context())
dbUser, err := svc.CreateUser(&param)
if err != nil {
global.GO_LOG.Sugar().Errorf("svc.CreateUser err: %v", err)
response.ToErrorResponse(errcode.ErrorCreateUserFail)
return
}
response.ToResponse(dbUser)
}
// @Summary 更新用户
// @Tags users
// @Description
// @Accept mpfd
// @Produce json
// @Param token header string true "auth token"
// @Param id path int true "用户ID"
// @Param username formData string false "用户名" maxlength(30)
// @Param password formData string false "密码" maxlength(18)
// @param nickname formData string false "昵称" maxlength(30)
// @Success 200 {object} model.User "请求成功"
// @Router /v1/users/{id} [patch]
func (user User) Update(c *gin.Context) {
userID, err := convert.StrTo(c.Param("id")).Int64()
response := app.NewResponse(c)
if err != nil {
global.GO_LOG.Sugar().Errorf("convert.StrTo err: %v", err)
response.ToErrorResponse(errcode.InvalidParams)
return
}
param := service.UpdateUserRequest{ID: userID}
valid, errs := app.BindAndValid(c, &param)
if !valid {
global.GO_LOG.Sugar().Errorf("app.BindAndValid errs: %v", errs)
response.ToResponseErrors(errs)
return
}
svc := service.New(c.Request.Context())
dbUser, err := svc.UpdateUser(&param)
if err != nil {
if err == gorm.ErrRecordNotFound {
global.GO_LOG.Sugar().Errorf("svc.UpdateUser err: %v", err)
response.ToErrorResponse(errcode.ErrorGetUserFail)
return
} else {
global.GO_LOG.Sugar().Errorf("svc.UpdateUser err: %v", err)
response.ToErrorResponse(errcode.ErrorUpdateUserFail)
return
}
}
response.ToResponse(dbUser)
}
// @Summary 删除用户
// @Tags users
// @Description
// @Accept json
// @Produce json
// @Param token header string true "auth token"
// @Param id path int true "用户ID"
// @Success 200 {object} model.User "请求成功"
// @Router /v1/users/{id} [delete]
func (user User) Delete(c *gin.Context) {
userID, err := convert.StrTo(c.Param("id")).Int64()
response := app.NewResponse(c)
if err != nil {
global.GO_LOG.Sugar().Errorf("convert.StrTo err: %v", err)
response.ToErrorResponse(errcode.InvalidParams)
return
}
param := service.DeleteUserRequest{ID: userID}
svc := service.New(c.Request.Context())
dbUser, err := svc.DeleteUser(&param)
if err != nil {
if err == gorm.ErrRecordNotFound {
global.GO_LOG.Sugar().Errorf("svc.DeleteUser err: %v", err)
response.ToErrorResponse(errcode.ErrorGetUserFail)
return
} else {
global.GO_LOG.Sugar().Errorf("svc.DeleteUser err: %v", err)
response.ToErrorResponse(errcode.ErrorDeleteUserFail)
return
}
}
errcode.Success.Data = dbUser
response.ToErrorResponse(errcode.Success)
}

98
internal/dao/user.go Normal file
View File

@ -0,0 +1,98 @@
/*
* @Date: 2021-07-09 14:30:19
* @LastEditors: viletyy
* @LastEditTime: 2021-07-09 14:30:40
* @FilePath: /potato/internal/dao/user.go
*/
package dao
import (
"github.com/viletyy/potato/internal/model"
"github.com/viletyy/potato/pkg/app"
"github.com/viletyy/yolk/crypt"
)
func (d *Dao) RegisterUser(username string, password string, nickname string) (model.User, error) {
user := model.User{
Username: username,
Password: crypt.Md5Encode(password),
Nickname: nickname,
}
return user, user.Create(d.Engine)
}
func (d *Dao) LoginUser(username string, password string) (model.User, error) {
user := model.User{
Username: username,
Password: crypt.Md5Encode(password),
}
return user.GetByUsernameAndPassword(d.Engine)
}
func (d *Dao) CountUser(username, nickname string) (int, error) {
vendor := model.User{Username: username, Nickname: nickname}
return vendor.Count(d.Engine)
}
func (d *Dao) GetUserList(username, nickname string, page, pageSize int) ([]model.User, error) {
user := model.User{Username: username, Nickname: nickname}
pageOffset := app.GetPageOffset(page, pageSize)
return user.List(d.Engine, pageOffset, pageSize)
}
func (d *Dao) GetUser(id int64) (model.User, error) {
user := model.User{
Model: &model.Model{ID: id},
}
return user.Get(d.Engine)
}
func (d *Dao) CreateUser(username, password, nickname string) (model.User, error) {
user := model.User{
Username: username,
Password: crypt.Md5Encode(password),
Nickname: nickname,
}
return user, user.Create(d.Engine)
}
func (d *Dao) UpdateUser(id int64, username, password, nickname string) (model.User, error) {
user := model.User{
Username: username,
Nickname: nickname,
Model: &model.Model{ID: id},
}
dbUser, err := user.Get(d.Engine)
if password == "" {
user.Password = dbUser.Password
} else {
user.Password = crypt.Md5Encode(password)
}
if err != nil {
return user, err
}
return dbUser, user.Update(d.Engine)
}
func (d *Dao) DeleteUser(id int64) (model.User, error) {
user := model.User{
Model: &model.Model{ID: id},
}
dbUser, err := user.Get(d.Engine)
if err != nil {
return user, err
}
return dbUser, dbUser.Delete(d.Engine)
}

View File

@ -1,11 +1,13 @@
/*
* @Date: 2021-03-21 19:54:57
* @LastEditors: viletyy
* @LastEditTime: 2021-06-11 10:38:24
* @LastEditTime: 2021-07-09 14:31:04
* @FilePath: /potato/internal/model/user.go
*/
package model
import "github.com/jinzhu/gorm"
type User struct {
*Model
@ -14,3 +16,65 @@ type User struct {
Nickname string `json:"nickname"`
IsAdmin bool `json:"is_admin" gorm:"default: false"`
}
func (u User) Count(db *gorm.DB) (int, error) {
var count int
if u.Username != "" {
db = db.Where("username = ?", u.Username)
}
if u.Nickname != "" {
db = db.Where("nickname = ?", u.Nickname)
}
if err := db.Model(&u).Count(&count).Error; err != nil {
return 0, err
}
return count, nil
}
func (u User) List(db *gorm.DB, pageOffset, pageSize int) (users []User, err error) {
if pageOffset >= 0 && pageSize > 0 {
db = db.Offset(pageOffset).Limit(pageSize)
}
if u.Username != "" {
db = db.Where("name = ?", u.Username)
}
if u.Nickname != "" {
db = db.Where("nickname = ?", u.Nickname)
}
if err = db.Find(&users).Error; err != nil {
return nil, err
}
return
}
func (u User) GetByUsernameAndPassword(db *gorm.DB) (user User, err error) {
if notFound := db.Where("username = ? AND password = ?", u.Username, u.Password).First(&user).RecordNotFound(); notFound {
return u, gorm.ErrRecordNotFound
}
return user, nil
}
func (u User) Get(db *gorm.DB) (user User, err error) {
if notFound := db.Where("id = ?", u.ID).First(&user).RecordNotFound(); notFound {
return u, gorm.ErrRecordNotFound
}
return user, nil
}
func (u *User) Create(db *gorm.DB) error {
return db.Create(u).Error
}
func (u *User) Update(db *gorm.DB) error {
err := db.Model(&User{}).Update(u).Error
return err
}
func (u *User) Delete(db *gorm.DB) error {
return db.Where("id = ?", u.ID).Delete(u).Error
}

View File

@ -1,7 +1,7 @@
/*
* @Date: 2021-03-21 19:54:57
* @LastEditors: viletyy
* @LastEditTime: 2021-06-14 23:32:23
* @LastEditTime: 2021-07-09 14:44:37
* @FilePath: /potato/internal/routers/router.go
*/
package routers
@ -52,6 +52,8 @@ func InitRouter() *gin.Engine {
Engine.StaticFS("/static", http.Dir(global.GO_CONFIG.App.UploadSavePath))
Engine.POST("/api/auth", api.GetAuth)
Engine.POST("/api/user/register", api.UserRegister)
Engine.POST("/api/user/login", api.UserLogin)
Engine.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
v1RouterGroup := Engine.Group("../api/v1")
@ -59,6 +61,16 @@ func InitRouter() *gin.Engine {
upload := v1.NewUpload()
v1RouterGroup.POST("/upload", upload.Create)
users := v1RouterGroup.Group("/users")
user := v1.NewUser()
{
users.GET("", user.List)
users.POST("", user.Create)
users.GET("/:id", user.Get)
users.PATCH("/:id", user.Update)
users.DELETE("/:id", user.Delete)
}
V1InitBasicRouter()
return Engine

88
internal/service/user.go Normal file
View File

@ -0,0 +1,88 @@
/*
* @Date: 2021-07-09 14:29:44
* @LastEditors: viletyy
* @LastEditTime: 2021-07-09 14:30:11
* @FilePath: /potato/internal/service/user.go
*/
package service
import (
"github.com/viletyy/potato/internal/model"
"github.com/viletyy/potato/pkg/app"
)
type UserRegisterRequest struct {
Username string `form:"username" json:"username" validate:"max=30"`
Password string `form:"password" json:"password" validate:"max=18"`
Nickname string `form:"nickname" json:"nickname" validate:"max=30"`
}
type UserLoginRequest struct {
Username string `form:"username" json:"username" validate:"max=30"`
Password string `form:"password" json:"password" validate:"max=18"`
}
type UserListRequest struct {
Username string `form:"username" json:"username" validate:"max=30"`
Nickname string `form:"nickname" json:"nickname" validate:"max=30"`
}
type CountUserRequest struct {
Username string `form:"username" json:"username" validate:"max=30"`
Password string `form:"password" json:"password" validate:"max=18"`
Nickname string `form:"nickname" json:"nickname" validate:"max=30"`
}
type UserRequest struct {
ID int64 `form:"id" json:"id" validate:"required,gte=1"`
}
type CreateUserRequest struct {
Username string `form:"username" json:"username" validate:"required,max=30"`
Password string `form:"password" json:"password" validate:"required,max=18"`
Nickname string `form:"nickname" json:"nickname" validate:"max=30"`
}
type UpdateUserRequest struct {
ID int64 `form:"id" json:"id" validate:"required,gte=1"`
Username string `form:"username" json:"username" validate:"max=30"`
Password string `form:"password" json:"password" validate:"max=18"`
Nickname string `form:"nickname" json:"nickname" validate:"max=30"`
}
type DeleteUserRequest struct {
ID int64 `form:"id" json:"id" validate:"required,gte=1"`
}
func (svc *Service) RegisterUser(param *UserRegisterRequest) (model.User, error) {
return svc.Dao.RegisterUser(param.Username, param.Password, param.Nickname)
}
func (svc *Service) LoginUser(param *UserLoginRequest) (model.User, error) {
return svc.Dao.LoginUser(param.Username, param.Password)
}
func (svc *Service) CountUser(param *CountUserRequest) (int, error) {
return svc.Dao.CountUser(param.Username, param.Nickname)
}
func (svc *Service) GetUserList(param *UserListRequest, pager *app.Pager) ([]model.User, error) {
return svc.Dao.GetUserList(param.Username, param.Nickname, pager.Page, pager.PageSize)
}
func (svc *Service) GetUser(param *UserRequest) (model.User, error) {
return svc.Dao.GetUser(param.ID)
}
func (svc *Service) CreateUser(param *CreateUserRequest) (model.User, error) {
return svc.Dao.CreateUser(param.Username, param.Password, param.Nickname)
}
func (svc *Service) UpdateUser(param *UpdateUserRequest) (model.User, error) {
return svc.Dao.UpdateUser(param.ID, param.Username, param.Password, param.Nickname)
}
func (svc *Service) DeleteUser(param *DeleteUserRequest) (model.User, error) {
return svc.Dao.DeleteUser(param.ID)
}

53
main.go
View File

@ -1,16 +1,14 @@
/*
* @Date: 2021-03-21 19:54:57
* @LastEditors: viletyy
* @LastEditTime: 2021-07-08 13:59:06
* @LastEditTime: 2021-07-09 14:37:35
* @FilePath: /potato/main.go
*/
package main
import (
"flag"
"net"
"github.com/soheilhy/cmux"
"github.com/viletyy/potato/global"
"github.com/viletyy/potato/initialize"
"github.com/viletyy/yolk/convert"
@ -20,12 +18,8 @@ import (
// @version 1.0
// @description This is a potato use golang
// @BasePath /api
var port string
func runTcpServer(port string) (net.Listener, error) {
return net.Listen("tcp", ":"+port)
}
var grpcPort string
var httpPort string
func main() {
global.GO_VP = initialize.Viper()
@ -33,35 +27,32 @@ func main() {
global.GO_DB = initialize.Gorm()
global.GO_REDIS = initialize.Redis()
global.GO_TRACER = initialize.Tracer()
initialize.Cron()
go initialize.Cron()
defer global.GO_DB.Close()
defer global.GO_REDIS.Close()
flag.StringVar(&port, "port", convert.ToString(global.GO_CONFIG.Server.Port), "启动端口号")
flag.StringVar(&grpcPort, "grpc_port", convert.ToString(global.GO_CONFIG.Server.GrpcPort), "启动grpc服务端口号")
flag.StringVar(&httpPort, "http_port", convert.ToString(global.GO_CONFIG.Server.HttpPort), "启动http服务端口号")
flag.Parse()
l, err := runTcpServer(port)
if err != nil {
global.GO_LOG.Sugar().Fatalf("Run Tcp Server err: %v", err)
}
m := cmux.New(l)
grpcL := m.MatchWithWriters(
cmux.HTTP2MatchHeaderFieldSendSettings(
"content-type",
"application/grpc",
),
)
httpL := m.Match(cmux.HTTP1Fast())
errs := make(chan error)
go func() {
err := initialize.RunHttpServer(httpPort)
if err != nil {
errs <- err
}
}()
grpcS := initialize.RunGrpcServer()
httpS := initialize.RunServer(port)
go grpcS.Serve(grpcL)
go httpS.Serve(httpL)
err = m.Serve()
if err != nil {
go func() {
err := initialize.RunGrpcServer(grpcPort)
if err != nil {
errs <- err
}
}()
select {
case err := <-errs:
global.GO_LOG.Sugar().Fatalf("Run Server err: %v", err)
}
}

View File

@ -1,17 +1,25 @@
/*
* @Date: 2021-06-10 23:09:09
* @LastEditors: viletyy
* @LastEditTime: 2021-06-11 17:25:58
* @LastEditTime: 2021-07-09 14:32:17
* @FilePath: /potato/pkg/errcode/module_code.go
*/
package errcode
var (
ErrorUploadFileFail = NewError(20001, "上传文件失败")
ErrorGetVendorListFail = NewError(20101, "获取系统厂商列表失败")
ErrorGetVendorFail = NewError(20102, "获取系统厂商失败")
ErrorCreateVendorFail = NewError(20103, "创建系统厂商失败")
ErrorUpdateVendorFail = NewError(20104, "更新系统厂商失败")
ErrorDeleteVendorFail = NewError(20105, "删除系统厂商失败")
ErrorCountVendorFail = NewError(20106, "统计系统厂商失败")
ErrorRegisterUserError = NewError(20010, "用户注册失败")
ErrorLoginUserError = NewError(20011, "用户登录失败")
ErrorGetUserListFail = NewError(20101, "获取用户列表失败")
ErrorGetUserFail = NewError(20102, "获取用户失败")
ErrorCreateUserFail = NewError(20103, "创建用户失败")
ErrorUpdateUserFail = NewError(20104, "更新用户失败")
ErrorDeleteUserFail = NewError(20105, "删除用户失败")
ErrorCountUserFail = NewError(20106, "统计用户失败")
ErrorGetVendorListFail = NewError(20201, "获取系统厂商列表失败")
ErrorGetVendorFail = NewError(20202, "获取系统厂商失败")
ErrorCreateVendorFail = NewError(20203, "创建系统厂商失败")
ErrorUpdateVendorFail = NewError(20204, "更新系统厂商失败")
ErrorDeleteVendorFail = NewError(20205, "删除系统厂商失败")
ErrorCountVendorFail = NewError(20206, "统计系统厂商失败")
)

357
proto/user.pb.go Normal file
View File

@ -0,0 +1,357 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.26.0
// protoc v3.17.1
// source: proto/user.proto
package proto
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type GetUserListRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Username string `protobuf:"bytes,1,opt,name=username,proto3" json:"username,omitempty"`
Nickname string `protobuf:"bytes,2,opt,name=nickname,proto3" json:"nickname,omitempty"`
Page int64 `protobuf:"varint,3,opt,name=page,proto3" json:"page,omitempty"`
PageSize int64 `protobuf:"varint,4,opt,name=page_size,json=pageSize,proto3" json:"page_size,omitempty"`
}
func (x *GetUserListRequest) Reset() {
*x = GetUserListRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_proto_user_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *GetUserListRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetUserListRequest) ProtoMessage() {}
func (x *GetUserListRequest) ProtoReflect() protoreflect.Message {
mi := &file_proto_user_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use GetUserListRequest.ProtoReflect.Descriptor instead.
func (*GetUserListRequest) Descriptor() ([]byte, []int) {
return file_proto_user_proto_rawDescGZIP(), []int{0}
}
func (x *GetUserListRequest) GetUsername() string {
if x != nil {
return x.Username
}
return ""
}
func (x *GetUserListRequest) GetNickname() string {
if x != nil {
return x.Nickname
}
return ""
}
func (x *GetUserListRequest) GetPage() int64 {
if x != nil {
return x.Page
}
return 0
}
func (x *GetUserListRequest) GetPageSize() int64 {
if x != nil {
return x.PageSize
}
return 0
}
type User struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
Username string `protobuf:"bytes,2,opt,name=username,proto3" json:"username,omitempty"`
Password string `protobuf:"bytes,3,opt,name=password,proto3" json:"password,omitempty"`
Nickname string `protobuf:"bytes,4,opt,name=nickname,proto3" json:"nickname,omitempty"`
IsAdmin bool `protobuf:"varint,5,opt,name=is_admin,json=isAdmin,proto3" json:"is_admin,omitempty"`
}
func (x *User) Reset() {
*x = User{}
if protoimpl.UnsafeEnabled {
mi := &file_proto_user_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *User) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*User) ProtoMessage() {}
func (x *User) ProtoReflect() protoreflect.Message {
mi := &file_proto_user_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use User.ProtoReflect.Descriptor instead.
func (*User) Descriptor() ([]byte, []int) {
return file_proto_user_proto_rawDescGZIP(), []int{1}
}
func (x *User) GetId() int64 {
if x != nil {
return x.Id
}
return 0
}
func (x *User) GetUsername() string {
if x != nil {
return x.Username
}
return ""
}
func (x *User) GetPassword() string {
if x != nil {
return x.Password
}
return ""
}
func (x *User) GetNickname() string {
if x != nil {
return x.Nickname
}
return ""
}
func (x *User) GetIsAdmin() bool {
if x != nil {
return x.IsAdmin
}
return false
}
type GetUserListReply struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
List []*User `protobuf:"bytes,1,rep,name=list,proto3" json:"list,omitempty"`
Pager *Pager `protobuf:"bytes,2,opt,name=pager,proto3" json:"pager,omitempty"`
}
func (x *GetUserListReply) Reset() {
*x = GetUserListReply{}
if protoimpl.UnsafeEnabled {
mi := &file_proto_user_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *GetUserListReply) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetUserListReply) ProtoMessage() {}
func (x *GetUserListReply) ProtoReflect() protoreflect.Message {
mi := &file_proto_user_proto_msgTypes[2]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use GetUserListReply.ProtoReflect.Descriptor instead.
func (*GetUserListReply) Descriptor() ([]byte, []int) {
return file_proto_user_proto_rawDescGZIP(), []int{2}
}
func (x *GetUserListReply) GetList() []*User {
if x != nil {
return x.List
}
return nil
}
func (x *GetUserListReply) GetPager() *Pager {
if x != nil {
return x.Pager
}
return nil
}
var File_proto_user_proto protoreflect.FileDescriptor
var file_proto_user_proto_rawDesc = []byte{
0x0a, 0x10, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x1a, 0x12, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e,
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x7d, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65,
0x72, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08,
0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08,
0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x6e, 0x69, 0x63, 0x6b,
0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6e, 0x69, 0x63, 0x6b,
0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01,
0x28, 0x03, 0x52, 0x04, 0x70, 0x61, 0x67, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x61, 0x67, 0x65,
0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x70, 0x61, 0x67,
0x65, 0x53, 0x69, 0x7a, 0x65, 0x22, 0x85, 0x01, 0x0a, 0x04, 0x55, 0x73, 0x65, 0x72, 0x12, 0x0e,
0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1a,
0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61,
0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61,
0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x6e, 0x69, 0x63, 0x6b, 0x6e, 0x61,
0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6e, 0x69, 0x63, 0x6b, 0x6e, 0x61,
0x6d, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x69, 0x73, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x18, 0x05,
0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x69, 0x73, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x22, 0x4b, 0x0a,
0x10, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x70, 0x6c,
0x79, 0x12, 0x19, 0x0a, 0x04, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32,
0x05, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x04, 0x6c, 0x69, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x05,
0x70, 0x61, 0x67, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x06, 0x2e, 0x50, 0x61,
0x67, 0x65, 0x72, 0x52, 0x05, 0x70, 0x61, 0x67, 0x65, 0x72, 0x32, 0x46, 0x0a, 0x0b, 0x55, 0x73,
0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x37, 0x0a, 0x0b, 0x47, 0x65, 0x74,
0x55, 0x73, 0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x13, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73,
0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x11, 0x2e,
0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79,
0x22, 0x00, 0x42, 0x21, 0x5a, 0x1f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d,
0x2f, 0x76, 0x69, 0x6c, 0x65, 0x74, 0x79, 0x79, 0x2f, 0x70, 0x6f, 0x74, 0x61, 0x74, 0x6f, 0x2f,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_proto_user_proto_rawDescOnce sync.Once
file_proto_user_proto_rawDescData = file_proto_user_proto_rawDesc
)
func file_proto_user_proto_rawDescGZIP() []byte {
file_proto_user_proto_rawDescOnce.Do(func() {
file_proto_user_proto_rawDescData = protoimpl.X.CompressGZIP(file_proto_user_proto_rawDescData)
})
return file_proto_user_proto_rawDescData
}
var file_proto_user_proto_msgTypes = make([]protoimpl.MessageInfo, 3)
var file_proto_user_proto_goTypes = []interface{}{
(*GetUserListRequest)(nil), // 0: GetUserListRequest
(*User)(nil), // 1: User
(*GetUserListReply)(nil), // 2: GetUserListReply
(*Pager)(nil), // 3: Pager
}
var file_proto_user_proto_depIdxs = []int32{
1, // 0: GetUserListReply.list:type_name -> User
3, // 1: GetUserListReply.pager:type_name -> Pager
0, // 2: UserService.GetUserList:input_type -> GetUserListRequest
2, // 3: UserService.GetUserList:output_type -> GetUserListReply
3, // [3:4] is the sub-list for method output_type
2, // [2:3] is the sub-list for method input_type
2, // [2:2] is the sub-list for extension type_name
2, // [2:2] is the sub-list for extension extendee
0, // [0:2] is the sub-list for field type_name
}
func init() { file_proto_user_proto_init() }
func file_proto_user_proto_init() {
if File_proto_user_proto != nil {
return
}
file_proto_common_proto_init()
if !protoimpl.UnsafeEnabled {
file_proto_user_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*GetUserListRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_proto_user_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*User); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_proto_user_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*GetUserListReply); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_proto_user_proto_rawDesc,
NumEnums: 0,
NumMessages: 3,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_proto_user_proto_goTypes,
DependencyIndexes: file_proto_user_proto_depIdxs,
MessageInfos: file_proto_user_proto_msgTypes,
}.Build()
File_proto_user_proto = out.File
file_proto_user_proto_rawDesc = nil
file_proto_user_proto_goTypes = nil
file_proto_user_proto_depIdxs = nil
}

29
proto/user.proto Normal file
View File

@ -0,0 +1,29 @@
syntax = "proto3";
option go_package= "github.com/viletyy/potato/proto";
import "proto/common.proto";
service UserService {
rpc GetUserList(GetUserListRequest) returns (GetUserListReply) {}
}
message GetUserListRequest {
string username = 1;
string nickname = 2;
int64 page = 3;
int64 page_size = 4;
}
message User {
int64 id = 1;
string username = 2;
string password = 3;
string nickname = 4;
bool is_admin = 5;
}
message GetUserListReply {
repeated User list = 1;
Pager pager = 2;
}

101
proto/user_grpc.pb.go Normal file
View File

@ -0,0 +1,101 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
package proto
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.32.0 or later.
const _ = grpc.SupportPackageIsVersion7
// UserServiceClient is the client API for UserService service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type UserServiceClient interface {
GetUserList(ctx context.Context, in *GetUserListRequest, opts ...grpc.CallOption) (*GetUserListReply, error)
}
type userServiceClient struct {
cc grpc.ClientConnInterface
}
func NewUserServiceClient(cc grpc.ClientConnInterface) UserServiceClient {
return &userServiceClient{cc}
}
func (c *userServiceClient) GetUserList(ctx context.Context, in *GetUserListRequest, opts ...grpc.CallOption) (*GetUserListReply, error) {
out := new(GetUserListReply)
err := c.cc.Invoke(ctx, "/UserService/GetUserList", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// UserServiceServer is the server API for UserService service.
// All implementations must embed UnimplementedUserServiceServer
// for forward compatibility
type UserServiceServer interface {
GetUserList(context.Context, *GetUserListRequest) (*GetUserListReply, error)
mustEmbedUnimplementedUserServiceServer()
}
// UnimplementedUserServiceServer must be embedded to have forward compatible implementations.
type UnimplementedUserServiceServer struct {
}
func (UnimplementedUserServiceServer) GetUserList(context.Context, *GetUserListRequest) (*GetUserListReply, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetUserList not implemented")
}
func (UnimplementedUserServiceServer) mustEmbedUnimplementedUserServiceServer() {}
// UnsafeUserServiceServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to UserServiceServer will
// result in compilation errors.
type UnsafeUserServiceServer interface {
mustEmbedUnimplementedUserServiceServer()
}
func RegisterUserServiceServer(s grpc.ServiceRegistrar, srv UserServiceServer) {
s.RegisterService(&UserService_ServiceDesc, srv)
}
func _UserService_GetUserList_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetUserListRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(UserServiceServer).GetUserList(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/UserService/GetUserList",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(UserServiceServer).GetUserList(ctx, req.(*GetUserListRequest))
}
return interceptor(ctx, in, info, handler)
}
// UserService_ServiceDesc is the grpc.ServiceDesc for UserService service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var UserService_ServiceDesc = grpc.ServiceDesc{
ServiceName: "UserService",
HandlerType: (*UserServiceServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "GetUserList",
Handler: _UserService_GetUserList_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "proto/user.proto",
}

57
server/user.go Normal file
View File

@ -0,0 +1,57 @@
/*
* @Date: 2021-07-09 14:33:26
* @LastEditors: viletyy
* @LastEditTime: 2021-07-09 14:34:16
* @FilePath: /potato/server/user.go
*/
package server
import (
"context"
"encoding/json"
"github.com/viletyy/potato/internal/service"
"github.com/viletyy/potato/pkg/app"
"github.com/viletyy/potato/pkg/errcode/rpc"
pb "github.com/viletyy/potato/proto"
)
type UserServer struct {
pb.UnimplementedUserServiceServer
}
func NewVendorServer() *UserServer {
return &UserServer{}
}
func (t *UserServer) GetUserList(ctx context.Context, r *pb.GetUserListRequest) (*pb.GetUserListReply, error) {
svc := service.New(ctx)
dbUserList, err := svc.Dao.GetUserList(r.GetUsername(), r.GetNickname(), int(r.GetPage()), int(r.GetPageSize()))
if err != nil {
return nil, rpc.ToRpcError(rpc.RpcErrorGetVendorListFail)
}
total, err := svc.Dao.CountUser(r.GetUsername(), r.GetNickname())
if err != nil {
return nil, rpc.ToRpcError(rpc.RpcErrorCountVendorFail)
}
data := map[string]interface{}{
"list": dbUserList,
"pager": app.Pager{
Page: int(r.GetPage()),
PageSize: int(r.GetPageSize()),
Total: total,
},
}
byteData, err := json.Marshal(data)
if err != nil {
return nil, rpc.ToRpcError(rpc.RpcInvalidParams)
}
userList := pb.GetUserListReply{}
err = json.Unmarshal(byteData, &userList)
if err != nil {
return nil, rpc.ToRpcError(rpc.RpcInvalidParams)
}
return &userList, nil
}