add: user module and migration

This commit is contained in:
yystopf 2022-02-17 22:05:07 +08:00
parent 5f7c72b365
commit 6e59e3d3d4
29 changed files with 808 additions and 166 deletions

View File

@ -12,6 +12,8 @@
- [安装](#安装)
- [配置说明](#配置说明)
- [使用说明](#使用说明)
- [启动项目](#启动项目)
- [生成api文档](#生成api文档)
- [相关仓库](#相关仓库)
- [如何贡献](#如何贡献)
- [使用许可](#使用许可)
@ -39,7 +41,7 @@ account-server
## 安装
这个项目使用 [go](https://golang.org/) 、 [swag](https://github.com/swaggo/swag)、[migrate](https://github.com/golang-migrate/migrate)等CLI工具
这个项目使用 [go](https://golang.org/) 、 [swag](https://github.com/swaggo/swag)等CLI工具
请确保你本地安装了它们。
go
@ -54,12 +56,6 @@ $ go get -u github.com/swaggo/swag/cmd/swag
$ mv $GOPATH/bin/swag /usr/local/go/bin
```
migrate安装
```sh
$ go get -u github.com/golang-migrate/migrate/v4/cmd/migrate
$ mv $GOPATH/bin/migrate /usr/local/go/bin
```
## 配置说明
根目录下需要有config.yaml如果没有请进行以下操作
@ -112,16 +108,11 @@ $ ./account-server
# 生成api文档
$ swag init
# 生成migrate数据库迁移
$ migrate create -ext sql -dir migrations -seq create_xxx
```
## 相关仓库
- [Gin](https://github.com/gin-gonic/gin) — Web Framework
- [Gorm](https://github.com/jinzhu/gorm) — ORM
- [Gorm](https://gorm.io/gorm) — ORM
- [Swag](https://github.com/swaggo/swag) - RESTful API Doc
- [Migrate](https://github.com/golang-migrate/migrate) - Database migrations
- [Cron](https://github.com/robfig/cron) - A cron library
## 如何贡献

View File

@ -11,6 +11,8 @@ type App struct {
Version string `mapstructure:"version" json:"version" yaml:"version"`
Domain string `mapstructure:"domain" json:"domain" yaml:"domain"`
PageSize int64 `mapstructure:"page_size" json:"page_size" yaml:"page_size"`
JwtSecret string `mapstructure:"jwt_secret" json:"jwt_secret" yaml:"jwt_secret"`
JwtExpire int64 `mapstructure:"jwt_expire" json:"jwt_expire" yaml:"jwt_expire"`
RunMode string `mapstructure:"run_mode" json:"run_mode" yaml:"run_mode"`
DefaultContextTimeout int64 `mapstructure:"default_context_timeout" json:"default_context_timeout" yaml:"default_context_timeout"`
}

View File

@ -24,6 +24,82 @@ var doc = `{
"host": "{{.Host}}",
"basePath": "{{.BasePath}}",
"paths": {
"/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"
}
}
}
}
},
"/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"
}
}
}
}
},
"/templates": {
"get": {
"description": "一段描述",
@ -217,6 +293,20 @@ var doc = `{
}
},
"definitions": {
"errcode.Error": {
"type": "object",
"properties": {
"code": {
"type": "integer"
},
"data": {
"type": "object"
},
"msg": {
"type": "string"
}
}
},
"model.Template": {
"type": "object",
"properties": {

View File

@ -8,6 +8,82 @@
},
"basePath": "/api",
"paths": {
"/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"
}
}
}
}
},
"/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"
}
}
}
}
},
"/templates": {
"get": {
"description": "一段描述",
@ -201,6 +277,20 @@
}
},
"definitions": {
"errcode.Error": {
"type": "object",
"properties": {
"code": {
"type": "integer"
},
"data": {
"type": "object"
},
"msg": {
"type": "string"
}
}
},
"model.Template": {
"type": "object",
"properties": {

View File

@ -1,5 +1,14 @@
basePath: /api
definitions:
errcode.Error:
properties:
code:
type: integer
data:
type: object
msg:
type: string
type: object
model.Template:
properties:
created_at:
@ -19,6 +28,56 @@ info:
title: AccountServer Api
version: "1.0"
paths:
/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: 用户登录
/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: 用户注册
/templates:
get:
consumes:

View File

@ -8,10 +8,10 @@ package global
import (
"github.com/go-redis/redis"
"github.com/jinzhu/gorm"
"github.com/spf13/viper"
"gitlink.org.cn/Gitlink/account-server/config"
"go.uber.org/zap"
"gorm.io/gorm"
)
var (

28
go.mod
View File

@ -4,33 +4,43 @@ go 1.15
require (
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751
github.com/eddycjy/opentracing-gorm v0.0.0-20200209122056-516a807d2182
github.com/fsnotify/fsnotify v1.4.9
github.com/gin-gonic/gin v1.7.7
github.com/go-playground/locales v0.13.0
github.com/go-playground/universal-translator v0.17.0
github.com/go-playground/validator/v10 v10.6.1
github.com/go-redis/redis v6.15.9+incompatible
github.com/go-sql-driver/mysql v1.5.0
github.com/golang-jwt/jwt v3.2.2+incompatible
github.com/golang-migrate/migrate/v4 v4.15.1
github.com/golang/protobuf v1.5.2
github.com/jinzhu/gorm v1.9.16
github.com/google/go-cmp v0.5.6 // indirect
github.com/jackc/pgx/v4 v4.15.0 // indirect
github.com/json-iterator/go v1.1.10 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible
github.com/lestrrat-go/strftime v1.0.4 // indirect
github.com/lib/pq v1.10.0
github.com/mailru/easyjson v0.7.0 // indirect
github.com/onsi/ginkgo v1.16.4 // indirect
github.com/onsi/gomega v1.13.0 // indirect
github.com/opentracing/opentracing-go v1.2.0 // indirect
github.com/pelletier/go-toml v1.8.1 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/robfig/cron/v3 v3.0.0
github.com/smacker/opentracing-gorm v0.0.0-20181207094635-cd4974441042 // indirect
github.com/spf13/afero v1.2.2 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/spf13/viper v1.7.1
github.com/swaggo/gin-swagger v1.3.0
github.com/swaggo/swag v1.7.0
github.com/viletyy/yolk v1.0.1
go.uber.org/atomic v1.8.0 // indirect
go.uber.org/zap v1.17.0
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.7.0 // indirect
go.uber.org/zap v1.21.0
golang.org/x/crypto v0.0.0-20220214200702-86341886e292 // indirect
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 // indirect
golang.org/x/sys v0.0.0-20211013075003-97ac67df715c // indirect
google.golang.org/genproto v0.0.0-20211013025323-ce878158c4d4 // indirect
google.golang.org/grpc v1.41.0
google.golang.org/protobuf v1.27.1
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
gorm.io/driver/mysql v1.2.3
gorm.io/driver/postgres v1.2.3
gorm.io/gorm v1.22.5
)

View File

@ -9,12 +9,12 @@ package initialize
import (
"fmt"
otgorm "github.com/eddycjy/opentracing-gorm"
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/mysql"
_ "github.com/jinzhu/gorm/dialects/postgres"
"gitlink.org.cn/Gitlink/account-server/global"
"gitlink.org.cn/Gitlink/account-server/internal/model"
"gorm.io/driver/mysql"
"gorm.io/driver/postgres"
"gorm.io/gorm"
gormlogger "gorm.io/gorm/logger"
"moul.io/zapgorm2"
)
func Gorm() *gorm.DB {
@ -29,7 +29,8 @@ func Gorm() *gorm.DB {
}
func GormMysql() *gorm.DB {
db, err := gorm.Open("mysql", fmt.Sprintf("%s:%s@(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local", global.GO_CONFIG.Database.User, global.GO_CONFIG.Database.Password, global.GO_CONFIG.Database.Host, global.GO_CONFIG.Database.Port, global.GO_CONFIG.Database.Name))
dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local", global.GO_CONFIG.Database.User, global.GO_CONFIG.Database.Password, global.GO_CONFIG.Database.Host, global.GO_CONFIG.Database.Port, global.GO_CONFIG.Database.Name)
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
global.GO_LOG.Error(fmt.Sprintf("Mysql Gorm Open Error: %v", err))
}
@ -38,7 +39,8 @@ func GormMysql() *gorm.DB {
}
func GormPostgresql() *gorm.DB {
db, err := gorm.Open("postgres", fmt.Sprintf("host=%s user=%s dbname=%s port=%d sslmode=disable password=%s", global.GO_CONFIG.Database.Host, global.GO_CONFIG.Database.User, global.GO_CONFIG.Database.Name, global.GO_CONFIG.Database.Port, global.GO_CONFIG.Database.Password))
dsn := fmt.Sprintf("host=%s user=%s password=%s dbname=%s port=%d sslmode=disable TimeZone=Asia/Shanghai", global.GO_CONFIG.Database.Host, global.GO_CONFIG.Database.User, global.GO_CONFIG.Database.Password, global.GO_CONFIG.Database.Name, global.GO_CONFIG.Database.Port)
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
if err != nil {
global.GO_LOG.Error(fmt.Sprintf("Postgresql Gorm Open Error: %v", err))
}
@ -47,27 +49,21 @@ func GormPostgresql() *gorm.DB {
}
func GormSet(db *gorm.DB) {
// 设置表前缀
gorm.DefaultTableNameHandler = func(db *gorm.DB, defaultTableName string) string {
return global.GO_CONFIG.Database.TablePrefix + defaultTableName
if global.GO_CONFIG.App.RunMode != "debug" {
logger := zapgorm2.New(global.GO_LOG)
logger.SetAsDefault()
logger.LogLevel = gormlogger.Info
db.Logger = logger
}
// 设置日志
if global.GO_CONFIG.App.RunMode == "debug" {
db.LogMode(true)
sqlDB, err := db.DB()
if err != nil {
global.GO_LOG.Error(fmt.Sprintf("Gorm setting db.DB(): %v ", err))
}
// 设置迁移
db.AutoMigrate(
&model.Template{},
)
// 设置空闲连接池中的最大连接数
db.DB().SetMaxIdleConns(10)
sqlDB.SetConnMaxIdleTime(10)
// 设置打开数据库连接的最大数量
db.DB().SetMaxOpenConns(100)
// 设置链路追踪
otgorm.AddGormCallbacks(db)
sqlDB.SetMaxOpenConns(100)
}

View File

@ -1,67 +0,0 @@
package initialize
import (
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql"
migrate "github.com/golang-migrate/migrate/v4"
migrate_mysql "github.com/golang-migrate/migrate/v4/database/mysql"
migrate_postgres "github.com/golang-migrate/migrate/v4/database/postgres"
_ "github.com/golang-migrate/migrate/v4/source/file"
_ "github.com/lib/pq"
"gitlink.org.cn/Gitlink/account-server/global"
)
func Migrate() {
switch global.GO_CONFIG.Database.Type {
case "mysql":
MigrateSetMysql()
return
case "postgresql":
MigrateSetPostgresql()
return
default:
MigrateSetMysql()
return
}
}
func MigrateSetPostgresql() {
db, err := sql.Open("postgres", fmt.Sprintf("%s:%s@(%s:%d)/%s?sslmode=enable", global.GO_CONFIG.Database.User, global.GO_CONFIG.Database.Password, global.GO_CONFIG.Database.Host, global.GO_CONFIG.Database.Port, global.GO_CONFIG.Database.Name))
if err != nil {
global.GO_LOG.Error(fmt.Sprintf("sql.Open: %v", err))
}
driver, err := migrate_postgres.WithInstance(db, &migrate_postgres.Config{})
if err != nil {
global.GO_LOG.Error(fmt.Sprintf("migrate_postgres.WithInstance: %v", err))
}
m, err := migrate.NewWithDatabaseInstance(
"files:///migrations",
"postgres", driver)
if err != nil {
global.GO_LOG.Error(fmt.Sprintf("migrate.NewWithDatabaseInstance: %v", err))
}
m.Up()
// m.Down()
}
func MigrateSetMysql() {
db, err := sql.Open("mysql", fmt.Sprintf("%s:%s@(%s:%d)/%s?multiStatements=true", global.GO_CONFIG.Database.User, global.GO_CONFIG.Database.Password, global.GO_CONFIG.Database.Host, global.GO_CONFIG.Database.Port, global.GO_CONFIG.Database.Name))
if err != nil {
global.GO_LOG.Fatal(fmt.Sprintf("sql.Open: %v", err))
}
driver, err := migrate_mysql.WithInstance(db, &migrate_mysql.Config{})
if err != nil {
global.GO_LOG.Fatal(fmt.Sprintf("migrate_mysql.WithInstance: %v", err))
}
m, err := migrate.NewWithDatabaseInstance(
"file://migrations",
"migrate", driver)
if err != nil {
global.GO_LOG.Fatal(fmt.Sprintf("migrate.NewWithDatabaseInstance: %v", err))
}
m.Up()
// m.Down()
}

View File

@ -2,12 +2,12 @@ package api
import (
"github.com/gin-gonic/gin"
"github.com/jinzhu/gorm"
"github.com/viletyy/yolk/convert"
"gitlink.org.cn/Gitlink/account-server/global"
"gitlink.org.cn/Gitlink/account-server/internal/service"
"gitlink.org.cn/Gitlink/account-server/pkg/app"
"gitlink.org.cn/Gitlink/account-server/pkg/errcode"
"gorm.io/gorm"
)
type Template struct{}

View File

@ -0,0 +1,78 @@
package api
import (
"github.com/gin-gonic/gin"
"gitlink.org.cn/Gitlink/account-server/global"
"gitlink.org.cn/Gitlink/account-server/internal/service"
"gitlink.org.cn/Gitlink/account-server/pkg/app"
"gitlink.org.cn/Gitlink/account-server/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 /register [post]
func Register(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.ErrorRegisterError)
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 /login [post]
func Login(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.ErrorLoginError)
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{
"user": dbUser,
"token": token,
})
}

View File

@ -6,7 +6,7 @@
*/
package dao
import "github.com/jinzhu/gorm"
import "gorm.io/gorm"
type Dao struct {
Engine *gorm.DB

View File

@ -5,7 +5,7 @@ import (
"gitlink.org.cn/Gitlink/account-server/pkg/app"
)
func (d *Dao) CountTemplate(name string, uuid int) (int, error) {
func (d *Dao) CountTemplate(name string, uuid int) (int64, error) {
template := model.Template{Name: name, Uuid: uuid}
return template.Count(d.Engine)
}

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

@ -0,0 +1,92 @@
package dao
import (
"github.com/viletyy/yolk/crypt"
"gitlink.org.cn/Gitlink/account-server/internal/model"
"gitlink.org.cn/Gitlink/account-server/pkg/app"
)
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) (int64, 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

@ -7,7 +7,7 @@
package model
import (
"github.com/jinzhu/gorm"
"gorm.io/gorm"
)
type Template struct {
@ -17,8 +17,8 @@ type Template struct {
Uuid int `json:"uuid"`
}
func (v Template) Count(db *gorm.DB) (int, error) {
var count int
func (v Template) Count(db *gorm.DB) (int64, error) {
var count int64
if v.Name != "" {
db = db.Where("name = ?", v.Name)
}
@ -50,8 +50,8 @@ func (v Template) List(db *gorm.DB, pageOffset, pageSize int) (vendors []Templat
}
func (v Template) Get(db *gorm.DB) (vendor Template, err error) {
if notFound := db.Where("id = ?", v.ID).First(&vendor).RecordNotFound(); notFound {
return v, gorm.ErrRecordNotFound
if err = db.Where("id = ?", v.ID).First(&vendor).Error; err != nil {
return v, err
}
return vendor, nil

74
internal/model/user.go Normal file
View File

@ -0,0 +1,74 @@
package model
import "gorm.io/gorm"
type User struct {
*Model
Username string `json:"username" gorm:"index:idx_username,unique"`
Password string `json:"-"`
Nickname string `json:"nickname"`
IsAdmin bool `json:"is_admin" gorm:"default: false"`
}
func (u User) Count(db *gorm.DB) (int64, error) {
var count int64
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 err = db.Where("username = ? AND password = ?", u.Username, u.Password).First(&user).Error; err != nil {
return u, err
}
return user, nil
}
func (u User) Get(db *gorm.DB) (user User, err error) {
if err = db.Where("id = ?", u.ID).First(&user).Error; err != nil {
return u, err
}
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.Save(u).Error
return err
}
func (u *User) Delete(db *gorm.DB) error {
return db.Where("id = ?", u.ID).Delete(u).Error
}

View File

@ -39,8 +39,10 @@ func InitRouter() *gin.Engine {
Engine.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
Engine.POST("/api/register", api.Register)
Engine.POST("/api/login", api.Login)
apiRouterGroup := Engine.Group("../api")
// apiRouterGroup.Use(middleware.JWT())
apiRouterGroup.Use(middleware.JWT())
templates := apiRouterGroup.Group("/templates")
template := api.NewTemplate()

View File

@ -9,7 +9,6 @@ package service
import (
"context"
otgorm "github.com/eddycjy/opentracing-gorm"
"gitlink.org.cn/Gitlink/account-server/global"
"gitlink.org.cn/Gitlink/account-server/internal/dao"
)
@ -21,7 +20,7 @@ type Service struct {
func New(ctx context.Context) Service {
svc := Service{Ctx: ctx}
svc.Dao = dao.New(otgorm.WithContext(svc.Ctx, global.GO_DB))
svc.Dao = dao.New(global.GO_DB)
return svc
}

View File

@ -34,7 +34,7 @@ type DeleteTemplateRequest struct {
ID int64 `json:"id" validate:"required,gte=1"`
}
func (svc *Service) CountTemplate(param *CountTemplateRequest) (int, error) {
func (svc *Service) CountTemplate(param *CountTemplateRequest) (int64, error) {
return svc.Dao.CountTemplate(param.Name, param.Uuid)
}

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

@ -0,0 +1,81 @@
package service
import (
"gitlink.org.cn/Gitlink/account-server/internal/model"
"gitlink.org.cn/Gitlink/account-server/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) (int64, 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)
}

12
main.go
View File

@ -6,6 +6,7 @@ import (
"github.com/viletyy/yolk/convert"
"gitlink.org.cn/Gitlink/account-server/global"
"gitlink.org.cn/Gitlink/account-server/initialize"
"gitlink.org.cn/Gitlink/account-server/migrations"
)
// @title AccountServer Api
@ -21,9 +22,14 @@ func main() {
global.GO_DB = initialize.Gorm()
global.GO_REDIS = initialize.Redis()
go initialize.Cron()
go initialize.Migrate()
defer global.GO_DB.Close()
if err := migrations.Migrate(global.GO_DB); err != nil {
global.GO_LOG.Sugar().Fatalf("migrations.Migrate: %v", err)
}
sqlDB, err := global.GO_DB.DB()
if err != nil {
global.GO_LOG.Sugar().Fatalf("global.GO_DB.DB err: %v", err)
}
defer sqlDB.Close()
defer global.GO_REDIS.Close()
flag.StringVar(&grpcPort, "grpc_port", convert.ToString(global.GO_CONFIG.Server.GrpcPort), "启动grpc服务端口号")

View File

@ -1 +0,0 @@
DROP table IF EXISTS templates;

View File

@ -1,10 +0,0 @@
CREATE TABLE `templates` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`created_at` datetime DEFAULT NULL,
`updated_at` datetime DEFAULT NULL,
`deleted_at` datetime DEFAULT NULL,
`name` varchar(255) DEFAULT NULL,
`uuid` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_templates_deleted_at` (`deleted_at`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4;

123
migrations/migration.go Normal file
View File

@ -0,0 +1,123 @@
package migrations
import (
"fmt"
"log"
"os"
"gitlink.org.cn/Gitlink/account-server/global"
"gorm.io/gorm"
)
const minDBVersion = 0
type Migration interface {
Description() string
Migrate(*gorm.DB) error
}
type migration struct {
description string
migrate func(*gorm.DB) error
}
func NewMigration(desc string, fn func(*gorm.DB) error) Migration {
return &migration{desc, fn}
}
func (m *migration) Description() string {
return m.description
}
func (m *migration) Migrate(db *gorm.DB) error {
return m.migrate(db)
}
type Version struct {
ID int64 `gorm:"primary_key"`
Version int64
}
var migrations = []Migration{
NewMigration("add users", addUsers),
}
func GetCurrentDBVersion(db *gorm.DB) (int64, error) {
if err := db.Debug().AutoMigrate(&Version{}); err != nil {
return -1, fmt.Errorf("db.AutoMigrate: %v", err)
}
currentVersion := &Version{ID: 1}
if err := db.Debug().First(currentVersion).Error; err != nil {
return -1, fmt.Errorf("db.First: %v", err)
}
return currentVersion.Version, nil
}
func ExpectedVersion() int64 {
return int64(minDBVersion + len(migrations))
}
func EnsureUpTodate(db *gorm.DB) error {
currentDB, err := GetCurrentDBVersion(db)
if err != nil {
return err
}
if currentDB < 0 {
return fmt.Errorf("Database has not been initialised")
}
if minDBVersion > currentDB {
return fmt.Errorf("DB version %d (<= %d) is too old for auto-migration.", currentDB, minDBVersion)
}
expected := ExpectedVersion()
if currentDB != expected {
return fmt.Errorf(`Current database version %d is not equal to the expected version %d. `, currentDB, expected)
}
return nil
}
func Migrate(db *gorm.DB) error {
if err := db.AutoMigrate(&Version{}); err != nil {
return fmt.Errorf("db.AutoMigrate: %v", err)
}
currentVersion := &Version{ID: 1}
if err := db.First(currentVersion).Error; err != nil {
currentVersion.Version = 0
if err := db.Debug().Create(currentVersion).Error; err != nil {
return fmt.Errorf("db.Create: %v", err)
}
}
v := currentVersion.Version
if minDBVersion > v {
global.GO_LOG.Fatal("Please upgrade the latest code.")
}
if int(v-minDBVersion) > len(migrations) {
msg := fmt.Sprintf("Downgrading database version from '%d' to '%d' is not supported and may result in loss of data integrity.\nIf you really know what you're doing, execute `UPDATE version SET version=%d WHERE id=1;`\n",
v, minDBVersion+len(migrations), minDBVersion+len(migrations))
fmt.Fprint(os.Stderr, msg)
log.Fatal(msg)
return nil
}
for i, m := range migrations[v-minDBVersion:] {
global.GO_LOG.Sugar().Infof("Migration[%d]: %s", v+int64(i), m.Description())
if err := m.Migrate(db); err != nil {
return fmt.Errorf("Migrate: %v", err)
}
currentVersion.Version = v + int64(i) + 1
if err := db.Save(currentVersion).Error; err != nil {
return err
}
}
return nil
}

25
migrations/v1.go Normal file
View File

@ -0,0 +1,25 @@
package migrations
import (
"fmt"
"gitlink.org.cn/Gitlink/account-server/internal/model"
"gorm.io/gorm"
)
func addUsers(db *gorm.DB) (err error) {
type User struct {
*model.Model
Username string `gorm:"index:idx_username,unique"` // 用户名
Password string // 密码
Nickname string // 昵称
IsAdmin bool `gorm:"default: false"` // 是否为管理员
}
if err := db.Debug().AutoMigrate(&User{}); err != nil {
return fmt.Errorf("migrations: create user err: %v", err)
}
return nil
}

View File

@ -16,9 +16,9 @@ type Response struct {
}
type Pager struct {
Page int `json:"page"`
PageSize int `json:"page_size"`
Total int `json:"total"`
Page int `json:"page"`
PageSize int `json:"page_size"`
Total int64 `json:"total"`
}
func NewResponse(ctx *gin.Context) *Response {
@ -38,7 +38,7 @@ func (r *Response) ToResponseErrors(data interface{}) {
r.ToErrorResponse(err)
}
func (r *Response) ToResponseList(list interface{}, total int) {
func (r *Response) ToResponseList(list interface{}, total int64) {
err := errcode.Success
err.WithData(map[string]interface{}{
"list": list,

View File

@ -21,12 +21,12 @@ type Claims struct {
}
func GetJWTSecret() []byte {
return []byte("account-server")
return []byte(global.GO_CONFIG.App.JwtSecret)
}
func GenerateToken(appKey, appSecret string) (string, error) {
nowTime := time.Now()
expireTime := nowTime.Add(time.Duration(12*60*60) * time.Second)
expireTime := nowTime.Add(time.Duration(global.GO_CONFIG.App.JwtExpire) * time.Second)
claims := Claims{
AppKey: crypt.Md5Encode(appKey),

View File

@ -7,10 +7,12 @@
package errcode
var (
ErrorGetListFail = NewError(20101, "获取列表失败")
ErrorGetFail = NewError(20102, "获取数据失败")
ErrorCreateFail = NewError(20103, "创建数据失败")
ErrorUpdateFail = NewError(20104, "更新数据失败")
ErrorDeleteFail = NewError(20105, "删除数据失败")
ErrorCountFail = NewError(20106, "统计数据失败")
ErrorRegisterError = NewError(20010, "注册失败")
ErrorLoginError = NewError(20011, "登录失败")
ErrorGetListFail = NewError(20101, "获取列表失败")
ErrorGetFail = NewError(20102, "获取数据失败")
ErrorCreateFail = NewError(20103, "创建数据失败")
ErrorUpdateFail = NewError(20104, "更新数据失败")
ErrorDeleteFail = NewError(20105, "删除数据失败")
ErrorCountFail = NewError(20106, "统计数据失败")
)

View File

@ -139,19 +139,19 @@ func init() {
func init() { proto.RegisterFile("proto/common.proto", fileDescriptor_1747d3070a2311a0) }
var fileDescriptor_1747d3070a2311a0 = []byte{
// 222 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x3c, 0x8f, 0xc1, 0x4a, 0x03, 0x31,
0x10, 0x86, 0xa9, 0xed, 0xaa, 0x1d, 0x6f, 0xa1, 0x87, 0x55, 0x2f, 0xd2, 0x53, 0x2f, 0x4d, 0xa4,
0x3e, 0x81, 0x82, 0x78, 0x13, 0x59, 0x6f, 0x5e, 0x4a, 0xdc, 0x8e, 0x21, 0xb8, 0xc9, 0x2c, 0x93,
0x28, 0xb4, 0x4f, 0x2f, 0x99, 0xb0, 0x9e, 0x66, 0xbe, 0xfc, 0xe1, 0xcb, 0x1f, 0x50, 0x23, 0x53,
0x26, 0xd3, 0x53, 0x08, 0x14, 0xb5, 0xc0, 0xcd, 0xb5, 0x23, 0x72, 0x03, 0x1a, 0xa1, 0xcf, 0x9f,
0x2f, 0x63, 0xe3, 0xb1, 0x46, 0xeb, 0x57, 0x68, 0xde, 0xac, 0x43, 0x56, 0x0a, 0x16, 0xa3, 0x75,
0xd8, 0xce, 0xee, 0x66, 0x9b, 0x79, 0x27, 0xbb, 0xba, 0x85, 0x65, 0x99, 0xfb, 0xe4, 0x4f, 0xd8,
0x9e, 0x49, 0x70, 0x59, 0x0e, 0xde, 0xfd, 0x09, 0xd5, 0x0a, 0x9a, 0x4c, 0xd9, 0x0e, 0xed, 0x5c,
0x82, 0x0a, 0xeb, 0x3d, 0x34, 0xcf, 0xcc, 0x24, 0xbe, 0x9e, 0x0e, 0xd5, 0xd7, 0x74, 0xb2, 0xab,
0x16, 0x2e, 0x02, 0xa6, 0x54, 0x9e, 0x29, 0xb6, 0x65, 0x37, 0xa1, 0xda, 0xc0, 0xe2, 0x60, 0xb3,
0x15, 0xd7, 0xd5, 0x6e, 0xa5, 0x6b, 0x61, 0x3d, 0x15, 0xd6, 0x8f, 0xf1, 0xd8, 0xc9, 0x8d, 0xa7,
0xdd, 0xc7, 0xbd, 0xf3, 0x79, 0xf0, 0xf1, 0x5b, 0x13, 0x3b, 0xdd, 0x47, 0xf3, 0x52, 0xd1, 0x04,
0xdf, 0x33, 0x6d, 0x13, 0xf2, 0x2f, 0xf2, 0x36, 0xf9, 0x30, 0xfe, 0xff, 0xf8, 0x5c, 0xc6, 0xc3,
0x5f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x82, 0xa0, 0x45, 0x2c, 0x1c, 0x01, 0x00, 0x00,
// 218 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x34, 0x8f, 0x31, 0x4b, 0x83, 0x31,
0x10, 0x86, 0xa9, 0x6d, 0xd4, 0x9e, 0x5b, 0xe8, 0x10, 0x75, 0x91, 0x4e, 0x05, 0x69, 0x02, 0xfa,
0x0b, 0x14, 0xc4, 0x4d, 0x24, 0x6e, 0x2e, 0x25, 0xa6, 0x67, 0x08, 0x7e, 0x5f, 0xae, 0x24, 0xa9,
0xd0, 0xfe, 0x7a, 0xc9, 0xc5, 0x4e, 0x77, 0x4f, 0xde, 0xf0, 0xe4, 0x0d, 0xc8, 0x5d, 0xa6, 0x4a,
0xc6, 0xd3, 0x38, 0x52, 0xd2, 0x0c, 0x37, 0xd7, 0x81, 0x28, 0x0c, 0x68, 0x98, 0xbe, 0xf6, 0xdf,
0xc6, 0xa5, 0x43, 0x8f, 0x96, 0x6f, 0x20, 0xde, 0x5d, 0xc0, 0x2c, 0x25, 0xcc, 0x76, 0x2e, 0xa0,
0x9a, 0xdc, 0x4d, 0x56, 0x53, 0xcb, 0xbb, 0xbc, 0x85, 0x79, 0x9b, 0x9b, 0x12, 0x8f, 0xa8, 0xce,
0x38, 0xb8, 0x6c, 0x07, 0x1f, 0xf1, 0x88, 0x72, 0x01, 0xa2, 0x52, 0x75, 0x83, 0x9a, 0x72, 0xd0,
0x61, 0xb9, 0x01, 0xf1, 0x92, 0x33, 0xb1, 0xcf, 0xd3, 0xb6, 0xfb, 0x84, 0xe5, 0x5d, 0x2a, 0xb8,
0x18, 0xb1, 0x94, 0xf6, 0x4c, 0xb3, 0xcd, 0xed, 0x09, 0xe5, 0x0a, 0x66, 0x5b, 0x57, 0x1d, 0xbb,
0xae, 0x1e, 0x16, 0xba, 0x17, 0xd6, 0xa7, 0xc2, 0xfa, 0x29, 0x1d, 0x2c, 0xdf, 0x78, 0x5e, 0x7f,
0xde, 0x87, 0x58, 0x87, 0x98, 0x7e, 0x34, 0xe5, 0xa0, 0x7d, 0x32, 0xaf, 0x1d, 0x8d, 0xf3, 0x9e,
0xf6, 0xa9, 0xae, 0x0b, 0xe6, 0x5f, 0xcc, 0xff, 0x9f, 0x3d, 0xe7, 0xf1, 0xf8, 0x17, 0x00, 0x00,
0xff, 0xff, 0x7d, 0x94, 0xe6, 0x7f, 0x17, 0x01, 0x00, 0x00,
}