HermanGinCasbinKafkaMysqlRedisZapCobraGrom
application -------------------------------------------------
constants ------------------------------------------------
controllers ----------------------------------------------
models ---------------------------------------------------
repositories ---------------------------------------------
services -------------------------------------------------
validates ------------------------------------------------
request.go -----------------------------------------------
response.go ----------------------------------------------
cmd ---------------------------------------------------------
config ------------------------------------------------------
database ----------------------------------------------------
migrations -----------------------------------------------
seeders --------------------------------------------------
jobs --------------------------------------------------------
kernel ------------------------------------------------------
app ------------------------------------------------------
casbin --------------------------------------------------- Casbin
cobra ----------------------------------------------------
core -----------------------------------------------------
kafka ---------------------------------------------------- kafka
log ------------------------------------------------------
mysql ---------------------------------------------------- Mysql
redis ---------------------------------------------------- Redis
servers --------------------------------------------------
runtime -----------------------------------------------------
logs -----------------------------------------------------
storages ----------------------------------------------------
tests -------------------------------------------------------
.air.toml --------------------------------------------------- Air
.gitignore -------------------------------------------------- gitignore
go.mod ------------------------------------------------------ go.mod
go.sum ------------------------------------------------------ go.sum
config.yaml.debug -------------------------------------------
config.yaml.test --------------------------------------------
config.yaml.release -----------------------------------------
Dockerfile -------------------------------------------------- Dodcker
docker-compose.yaml ----------------------------------------- Dodcker
LICENSE -----------------------------------------------------
Makefile ---------------------------------------------------- Makefile
main.go -----------------------------------------------------
README.md --------------------------------------------------- Readme
user``user_login
1_init.down.sql``1_init.up.sql
1initdownuptest.css``test_user.css
json
type Users struct {
Id uint `json:"id" gorm:"primary_key"`
User string `json:"user"`
Password string `json:"password"`
Nickname string `json:"nickname"`
Sex string `json:"sex"`
Age int `json:"age"`
Region string `json:"region"`
Phone string `json:"phone"`
Email string `json:"email"`
Introduction string `json:"introduction"`
Status string `json:"status"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
DeletedAt *time.Time `json:"deletedAt" sql:"index"`
}
Success``TokenNotExit
user``user_role
user_id``user_name
pk_user_id``uk_user_name``idx_user_age
pk+
pk_mainfk++
fk_sub_main3config.yaml.debug``config.yaml.test``config.yaml.release``config.yaml.debug``config.yaml
cp config.yaml.debug config.yaml
MysqlRedisMySQLRedis
#
mysql:
# IP
host: 127.0.0.1
#
port: 3306
#
user: root
#
password: root
#
dbname: herman
#
max_open_conn: 100
# max_open_conn
max_idle_conn: 10
# Redis
redis:
# IP
host: 127.0.0.1
#
port: 6380
#
username:
#
password:
# 0
db: 0
#
pool_size: 100
Go
go mod download
1
go build -o herman . # herman
herman server --host=0.0.0.0 --port=8000 --migrate=true # hostportmigtate
2
go run main.go server --host=0.0.0.0 --port=8000 --migrate=true # hostport
3
****Go 1.16
go install github.com/cosmtrek/air@latest #
.air.toml
air init
# [Air](https://github.com/cosmtrek/air) TOML
#
# . `tmp_dir` `root`
root = "."
tmp_dir = "tmp"
[build]
# shell `make`
cmd = "go build -o ./tmp/herman.exe ."
# `cmd`
bin = "tmp\\herman.exe server"
# .
include_ext = ["go", "tpl", "tmpl", "html"]
#
exclude_dir = ["assets", "tmp", "vendor", "frontend/node_modules"]
#
include_dir = []
#
exclude_file = []
#
delay = 1000 # ms
#
stop_on_error = true
# air`tmp_dir`
log = "air_errors.log"
[log]
#
time = true
[color]
#
main = "magenta"
watcher = "cyan"
build = "yellow"
runner = "green"
[misc]
# tmp
clean_on_exit = true
air
HermanHTTP
main.go
Hermanmain.go
GolangHermanRedisMySQLCasbin/kernel/core/Container.go
package core
import (
"github.com/casbin/casbin/v2"
"github.com/gin-gonic/gin"
"github.com/go-redis/redis/v8"
"go.uber.org/zap"
"gorm.io/gorm"
)
var (
Engine *gin.Engine
Log *zap.SugaredLogger
Db *gorm.DB
Redis *redis.Client
Casbin *casbin.CachedEnforcer
)
********/middlewares
// ServerHandler
// @return gin.HandlerFunc
func ServerHandler() gin.HandlerFunc {
return func(ctx *gin.Context) {
Reload() //
ctx.Next()
Close() //
}
}
GinAPI
func NewServer(host string, port uint) {
// gin
gin.SetMode(app.Config.Mode)
// gin
engine := gin.New()
//
engine.Use(log.GinLogger()).Use(middlewares.CatchError()).Use(middlewares.ServerHandler())
//
core.Engine = routers.InitRouter(engine)
//
Run(host, port)
}
// Jwt
// @return gin.HandlerFunc
func Jwt(guard string) gin.HandlerFunc {
return func(ctx *gin.Context) {
if VerifyRoute(ctx.Request.URL.Path, ctx.Request.Method, MiddlewareConstant.ExcludeRoute) {
return
}
claims := utils.JwtVerify(ctx, guard)
switch guard {
case "user", "mobile": //
//
ctx.Set("user", repositories.User().GetUserInfo(claims.Uid))
case "admin": //
ctx.Set("admin", repositories.Admin().GetAdminInfo(claims.Uid))
case "merchant": //
default:
panic(MiddlewareConstant.GuardError)
}
ctx.Next()
}
}
//
adminRouter := api.Group("/admin", middlewares.Jwt("admin"), middlewares.CheckPermission())
{
admin.Router(adminRouter)
}
cobracmd``/kernel/cobra/cobra.go
// HermanVersionCmd herman
var (
HermanVersionCmd = &cobra.Command{
Use: "version",
Short: "Get herman version",
Example: "herman version",
SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error {
fmt.Printf(`Herman version: %v`, color.GreenString(app.Version))
return nil
},
}
)
// rootCmd
var rootCmd = &cobra.Command{Use: "herman"}
//
func init() {
//
cobra.OnInitialize(app.InitConfig, servers.ZapLogs, func() {
if command.IsMigrate {
//
_ = command.Migrate("up")
}
//
if !command.MigrationStatus {
middlewares.Reload()
}
})
//
rootCmd.AddCommand(command.HermanVersionCmd)
}
1
herman version # Herman version: 1.0.0
2
herman migrate --direction=up --number=1 # 1
// init
// @return void
func init() {
//
MigrationCmd.Flags().BoolVarP(&MigrationStatus, "status", "s", false, "Database migration status")
// updown
MigrationCmd.Flags().StringVarP(&direction, "direction", "d", "up", "Database migration")
// Error: Dirty database version XX.
MigrationCmd.Flags().UintVarP(&version, "version", "v", 0, "Database version")
// 1herman -d down -n 1
MigrationCmd.Flags().UintVarP(&number, "number", "n", 0, "Database migration steps")
}
3JWT
herman jwt:secret
4
herman server --host=0.0.0.0 --port=8000 --migrate=true #
herman server # 8000
cobrahttps://cobra.dev/
kafkajobs
// SendSms
// @param string topic
// @return void
func SendSms(topic string) {
var data map[string]interface{}
//
kafkaConsumer := ExecConsumer(topic)
for {
//
message := <-kafkaConsumer.MessageQueue
// JSONmap
if err := json.Unmarshal(message, &data); err != nil {
app.Log.Errorf("Consumer sms json data failed, err:%v", err)
}
execSend(data)
}
}
jobs.Dispatch(data,jobs.SendSms)
for {
//
message := <-kafkaConsumer.MessageQueue
// JSONmap
if err := json.Unmarshal(message, &data); err != nil {
app.Log.Errorf("Consumer sms json data failed, err:%v", err)
}
execSend(data)
}
Dispatchdatatimetime
var data map[string]interface{}
data["topic"] = "sms_send"
data["time"] = time.Now().Add(time.Second * 60) // 60
jobs.Dispatch(data,jobs.SendSms)
Redis/kernel/core/container.go
//
ctx := context.Background()
key
val, err := core.Redis.Set(ctx, "key", 1)
fmt.Println(val)
key
get := core.Redis.Get(ctx, "key").Result()
fmt.Println(get.Val(), get.Err())
key
core.Redis.Set(ctx, "key", 1, time.Minute*30)
Redishttps://redis.uptrace.dev/zh/guide/
ZapHerman
//
app.Log.info(data)
//
app.Log.infoln(data)
//
app.Log.Debug(data)
//
app.Log.Error(data)
//
app.Log.Errorln(data)
//
app.Log.Fatal(data)
APIhttps://pkg.go.dev/go.uber.org/zap
/app/utils
// Factory
// @return factory
func Factory() (factory *CaptchaService.CaptchaServiceFactory) { //
//
factory = CaptchaService.NewCaptchaServiceFactory(
CaptchaConfig.BuildConfig(app.Config.Captcha.CacheType,
app.Config.Captcha.ResourcePath,
&CaptchaConfig.WatermarkConfig{
Text: app.Config.Captcha.Text,
},
nil, nil, app.Config.Captcha.CacheExpireSec))
//
factory.RegisterCache(Constant.MemCacheKey, CaptchaService.NewMemCacheService(CaptchaConstant.CacheMaxNumber))
// redis
factory.RegisterCache(Constant.RedisCacheKey, CaptchaService.NewConfigRedisCacheService([]string{fmt.Sprintf("%s:%d",
app.Config.Redis.Host,
app.Config.Redis.Port,
)},
app.Config.Redis.UserName,
app.Config.Redis.Password,
false,
app.Config.Redis.Db,
))
//
factory.RegisterService(Constant.ClickWordCaptcha, CaptchaService.NewClickWordCaptchaService(factory))
//
factory.RegisterService(Constant.BlockPuzzleCaptcha, CaptchaService.NewBlockPuzzleCaptchaService(factory))
return factory
}
CasbinRBAC, ABACACLRBACGORM/kernel/casbin/casbin.go
Casbin/kernel/core/container.go
success, _ := core.Casbin.Enforce(info.User, ctx.Request.URL.Path, ctx.Request.Method)
https://casbin.org/zh/docs/category/the-basics
config.yaml``config
app.Config
MySQL
app.Config.Mysql
config.yaml
viper.Get("app")
Gin/routers/router.go
func InitRouter(rootEngine *gin.Engine) *gin.Engine {
//
rootEngine.GET("/", func(context *gin.Context) {
response := app.Request{Context: context}
response.Success(app.D(map[string]interface{}{
"message": "Welcome to Herman!",
}))
})
//
api := rootEngine.Group(app.Config.AppPrefix)
//
api.GET("/captcha", CaptchaController.GetCaptcha)
//
api.POST("/captcha/check", CaptchaController.CheckCaptcha)
//
userRouter := api.Group("/user", middlewares.Jwt("user"))
{
mobile.Router(userRouter)
}
//
adminRouter := api.Group("/admin", middlewares.Jwt("admin"), middlewares.CheckPermission())
{
admin.Router(adminRouter)
}
return rootEngine
}
// AddAdmin
// @param *gin.Context ctx
// @return void
func AddAdmin(ctx *gin.Context) {
context := app.Request{Context: ctx} //
data := context.Params() //
AdminService.Add(AdminValidate.Add.Check(data)) //
context.Json(nil) //
}
// Add
var Add = validates.Validates{Validate: AddValidate{}}
// AddValidate
type AddValidate struct {
User string `json:"user" validate:"required,min=5,max=15" label:""`
Password string `json:"password" validate:"required,min=6,max=15" label:""`
Roles []role.Roles `json:"roles" validate:"required" label:""`
Photo string `json:"photo" validate:"omitempty,url,max=255" label:""`
Name string `json:"name" validate:"omitempty,max=20" label:""`
Card string `json:"card" validate:"omitempty,max=20" label:""`
Sex uint8 `json:"sex" validate:"required,oneof=1 2 3" label:""`
Age uint8 `json:"age" validate:"required,min=0,max=120" label:""`
Region string `json:"region" validate:"omitempty,max=255" label:""`
Phone string `json:"phone" validate:"omitempty,len=11" label:""`
Email string `json:"email" validate:"omitempty,email" label:""`
Introduction string `json:"introduction" validate:"omitempty" label:""`
State uint8 `json:"state" validate:"required,oneof=1 2" label:""`
Sort uint `json:"sort" validate:"omitempty" label:""`
}
check
// Check
// @param map[string]interface{} data
// @return void
func (base Validates) Check(data map[string]interface{}) (toMap map[string]interface{}) {
// map
if err := mapstructure.WeakDecode(data, &base.Validate); err != nil {
panic(constants.MapToStruct)
}
if err := Validate(base.Validate); err != nil {
panic(err.Error())
}
toMap, err := utils.ToMap(base.Validate, "json")
if err != nil {
panic(constants.StructToMap)
}
return toMap
}
// Login
// @param *gin.Context ctx
// @return void
func Login(ctx *gin.Context) {
context := app.Request{Context: ctx}
data := context.Params()
context.Json(AdminService.Login(AdminValidate.Login(data)), AdminConstant.LoginSuccess)
}
// CaptchaLoginValidate
type CaptchaLoginValidate struct {
User string `json:"user" validate:"required,min=5,max=15" label:""`
Password string `json:"password" validate:"required,min=6,max=15" label:""`
CaptchaType int `json:"captchaType" validate:"required,numeric,oneof=1 2" label:""`
Token string `json:"token" validate:"required" label:"Token"`
PointJson string `json:"pointJson" validate:"required" label:"PointJson"`
}
// ExcludeCaptchaLoginValidate
type ExcludeCaptchaLoginValidate struct {
User string `json:"user" validate:"required,min=5,max=15" label:""`
Password string `json:"password" validate:"required,min=6,max=15" label:""`
}
// Login
// @param map[string]interface{} data
// @return toMap
func Login(data map[string]interface{}) (toMap map[string]interface{}) {
//
if !app.Config.Captcha.Switch {
return excludeCaptchaLogin(data)
}
return captchaLogin(data)
}
// captchaLogin
// @param map[string]interface{} data
// @return toMap
func captchaLogin(data map[string]interface{}) (toMap map[string]interface{}) {
var login CaptchaLoginValidate
// map
if err := mapstructure.WeakDecode(data, &login); err != nil {
panic(constants.MapToStruct)
}
if err := validates.Validate(login); err != nil {
panic(err.Error())
}
//
err := utils.Factory().GetService(fmt.Sprintf("%s", data["captchaType"])).Verification(fmt.Sprintf("%s", data["token"]),
fmt.Sprintf("%s", data["PointJson"]))
if err != nil {
panic(CaptchaConstant.CheckCaptchaError)
}
toMap, err = utils.ToMap(&login, "json")
if err != nil {
panic(constants.StructToMap)
}
return toMap
}
// excludeCaptchaLogin
// @param map[string]interface{} data
// @return toMap
func excludeCaptchaLogin(data map[string]interface{}) (toMap map[string]interface{}) {
var login ExcludeCaptchaLoginValidate
// map
if err := mapstructure.WeakDecode(data, &login); err != nil {
panic(constants.MapToStruct)
}
if err := validates.Validate(login); err != nil {
panic(err.Error())
}
toMap, err := utils.ToMap(&login, "json")
if err != nil {
panic(constants.StructToMap)
}
return toMap
}
2
err := core.Db().Transaction(func(tx *gorm.DB) error {
// casbin
_, _ = casbin.InitEnforcer(casbin.GetAdminPolicy(), tx)
// Key
if isExist, _ := repositories.Role(tx).KeyIsExist(data["role"].(string)); isExist {
return errors.New(RoleConstant.KeyExist)
}
roles := data["roles"]
rules := data["rules"]
delete(data, "roles")
delete(data, "rules")
//
roleInfo, err := repositories.Role(tx).Insert(data)
if err != nil {
return errors.New(RoleConstant.AddFail)
}
//
if err := AddPolicies(roles.([]role.Roles), rules.([]role.Rules), roleInfo); err != nil {
return err
}
return nil
})
CasbinCasbinDb
_, _ = casbin.InitEnforcer(casbin.GetAdminPolicy(), tx)
roleInfo, err := repositories.Role(tx).Insert(data)
if err != nil {
return errors.New(RoleConstant.AddFail)
}
ServiceModelModel****************************
package repositories
import (
"github.com/herman-hang/herman/app/constants"
"github.com/herman-hang/herman/app/utils"
"github.com/mitchellh/mapstructure"
"gorm.io/gorm"
)
// BaseRepository
type BaseRepository struct {
Model interface{}
Db *gorm.DB
}
// PageInfo
type PageInfo struct {
Page int64 `json:"page"` //
PageSize int64 `json:"pageSize"` //
Keywords string `json:"keywords"` //
}
// Insert
// @param map[string]interface{} data
// @return toMap err
func (base *BaseRepository) Insert(data map[string]interface{}) (toMap map[string]interface{}, err error) {
// IDID
data["id"] = constants.InitId
if err := mapstructure.WeakDecode(data, base.Model); err != nil {
return nil, err
}
if err := base.Db.Create(base.Model).Error; err != nil {
return nil, err
}
//
tempStruct := base.Model
toMap, err = utils.ToMap(tempStruct, "json")
if err != nil {
return nil, err
}
return toMap, nil
}
// Find
// @param map[string]interface{} condition
// @param []string fields
// @return data err
func (base *BaseRepository) Find(condition map[string]interface{}, fields ...[]string) (info map[string]interface{}, err error) {
data := make(map[string]interface{})
info = make(map[string]interface{})
if len(fields) > 0 {
if err := base.Db.Model(&base.Model).Where(condition).Select(fields[0]).Find(&data).Error; err != nil {
return nil, err
}
} else {
if err := base.Db.Model(&base.Model).Where(condition).Find(&data).Error; err != nil {
return nil, err
}
}
if len(data) > 0 {
for k, v := range data {
//
info[utils.UnderscoreToLowerCamelCase(k)] = v
}
}
return info, nil
}
// Update
// @param []uint ids
// @param map[string]interface{} attributes
// @return error
func (base *BaseRepository) Update(ids []uint, data map[string]interface{}) error {
var attributes = make(map[string]interface{})
//
for k, v := range data {
k := utils.ToSnakeCase(k)
attributes[k] = v
}
if err := base.Db.Model(&base.Model).Where("id IN (?)", ids).Updates(attributes).Error; err != nil {
return err
}
return nil
}
// Delete
// @param []uint ids ID
// @return error
func (base *BaseRepository) Delete(ids []uint) error {
if err := base.Db.Delete(&base.Model, ids).Error; err != nil {
return err
}
return nil
}
// IsExist
// @param map[string]interface{} condition
// @return bool bool
func (base *BaseRepository) IsExist(condition map[string]interface{}) bool {
data := make(map[string]interface{})
err := base.Db.Model(&base.Model).Where(condition).Find(&data).Error
if err != nil && len(data) > constants.LengthByZero {
return true
}
return false
}
// GetList
// @param string query
// @param []string fields
// @param string order
// @param map[string]interface{} pageInfo
// @return list total pageNum err
func (base *BaseRepository) GetList(query string, fields []string, order string, pageInfo ...map[string]interface{}) (data map[string]interface{}, err error) {
var (
page PageInfo
total int64
pageNum int64
list []map[string]interface{}
)
if len(pageInfo) > 0 {
if err := mapstructure.WeakDecode(pageInfo[0], &page); err != nil {
panic(constants.MapToStruct)
}
}
//
base.Db.Model(&base.Model).Count(&total)
//
if page.PageSize != 0 && total%page.PageSize != 0 {
pageNum = total / page.PageSize
pageNum++
}
// query = fmt.Sprintf(" dns like '%%%s' ", createDbnameInfo.DNS)
err = base.Db.Model(&base.Model).
Select(fields).
Where(query).
Order(order).
Limit(int(page.PageSize)).
Offset(int((page.Page - 1) * page.PageSize)).
Find(&list).Error
if err != nil {
return nil, err
}
data = map[string]interface{}{
"list": list, //
"total": total, //
"pageNum": pageNum, //
"pageSize": page.PageSize, //
"page": page.Page, //
}
return data, nil
}
// GetAllData
// @param []string fields
// @return list err
func (base *BaseRepository) GetAllData(fields []string) (data []map[string]interface{}, err error) {
if len(fields) > 0 {
if err := base.Db.Model(&base.Model).Select(fields).Find(&data).Error; err != nil {
return nil, err
}
} else {
if err := base.Db.Model(&base.Model).Find(&data).Error; err != nil {
return nil, err
}
}
return data, nil
}
ModelBaseRepository
package repositories
import (
AdminConstant "github.com/herman-hang/herman/app/constants/admin"
"github.com/herman-hang/herman/app/models"
"github.com/herman-hang/herman/kernel/core"
"gorm.io/gorm"
)
// AdminRepository
type AdminRepository struct {
BaseRepository
}
// Admin
// @param *gorm.DB tx
// @return AdminRepository
func Admin(tx ...*gorm.DB) *AdminRepository {
if len(tx) > 0 && tx[0] != nil {
return &AdminRepository{BaseRepository{Model: new(models.Admin), Db: tx[0]}}
}
return &AdminRepository{BaseRepository{Model: new(models.Admin), Db: core.Db}}
}
// GetAdminInfo
// @param interface{} attributes iduser
// @return admin
func (u AdminRepository) GetAdminInfo(attributes interface{}) (admin *models.Admin) {
var err error
switch attributes.(type) {
case uint:
err = core.Db.Where("id = ?", attributes).Find(&admin).Error
case string:
err = core.Db.Where("user = ?", attributes).Find(&admin).Error
}
if err != nil {
panic(AdminConstant.GetAdminInfoFail)
}
return admin
}
// AdminRepository
type AdminRepository struct {
BaseRepository
}
// Admin
// @param *gorm.DB tx
// @return AdminRepository
func Admin(tx ...*gorm.DB) *AdminRepository {
if len(tx) > 0 && tx[0] != nil {
return &AdminRepository{BaseRepository{Model: new(models.Admin), Db: tx[0]}}
}
return &AdminRepository{BaseRepository{Model: new(models.Admin), Db: core.Db}}
}
Service
//
admin := repositories.Admin().GetAdminInfo(fmt.Sprintf("%s", data["user"]))
repositories.Admin()
GORMGORMhttps://gorm.io/zh_CN/docs/
jsongormcolumn
package models
import (
"gorm.io/gorm"
"time"
)
// Admin
type Admin struct {
Id uint `json:"id" gorm:"column:id;primary_key;comment:ID"`
User string `json:"user" gorm:"column:user;comment:"`
Password string `json:"password" gorm:"column:password;comment:"`
Photo string `json:"photo" gorm:"column:photo;comment:"`
Name string `json:"name" gorm:"column:name;comment:"`
Card string `json:"card" gorm:"column:card;comment:"`
Sex uint8 `json:"sex" gorm:"column:sex;default:3;comment:(1,23)"`
Age uint8 `json:"age" gorm:"column:age;default:0;comment:"`
Region string `json:"region" gorm:"column:region;comment:"`
Phone string `json:"phone" gorm:"column:phone;comment:"`
Email string `json:"email" gorm:"column:email;comment:"`
Introduction string `json:"introduction" gorm:"column:introduction;comment:"`
State uint8 `json:"state" gorm:"column:state;default:2;comment:(1,2)"`
Sort uint `json:"sort" gorm:"column:sort;default:0;comment:"`
LoginOutIp string `json:"loginOutIp" gorm:"column:login_out_ip;comment:IP"`
LoginTotal uint `json:"loginTotal" gorm:"column:login_total;default:0;comment:"`
LoginOutAt time.Time `json:"loginOutAt" gorm:"column:login_out_at;default:1970-01-01 00:00:00;comment:"`
CreatedAt time.Time `json:"createdAt" gorm:"column:created_at;comment:"`
UpdatedAt time.Time `json:"updatedAt" gorm:"column:updated_at;comment:"`
DeletedAt gorm.DeletedAt `json:"deletedAt" gorm:"column:deleted_at;index;comment:"`
}
// TableName
func (Admin) TableName() string {
return "admin"
}
TableName()string
/app/response.go
package app
import (
"fmt"
"github.com/herman-hang/herman/app/constants"
"github.com/herman-hang/herman/app/utils"
"net/http"
)
// Response
type Response struct {
HttpCode int `json:"-"`
Code int `json:"code"`
Message string `json:"message"`
Data interface{} `json:"data"`
}
// Option
type Option func(*Response)
// C JSON
// @param int code
// @return Option
func C(code int) Option {
return func(this *Response) {
this.Code = code
}
}
// M
// @param string message
// @return Option
func M(message string) Option {
return func(this *Response) {
this.Message = message
}
}
// D
// @param interface{} data
// @return Option
func D(data interface{}) Option {
return func(this *Response) {
this.Data = data
}
}
// H HTTP
// @param int HttpCode HTTP200500
// @return Option
func H(HttpCode int) Option {
return func(this *Response) {
this.HttpCode = HttpCode
}
}
// Success
// @param *Gin g
// @param Option opts CMDH
func (r *Request) Success(opts ...Option) {
defaultResponse := &Response{
HttpCode: http.StatusOK,
Code: http.StatusOK,
Message: constants.Success,
Data: nil,
}
// opts
for _, o := range opts {
o(defaultResponse)
}
// http
r.Context.JSON(defaultResponse.HttpCode, defaultResponse)
return
}
// Json
// @param interface{} data
// @param args messagecode
func (r *Request) Json(data interface{}, args ...interface{}) {
var jsonString []byte
// json
camelJson, _ := utils.CamelJSON(data)
switch len(args) {
case 0:
jsonString = []byte(fmt.Sprintf(`{"code":%d,"message":"%s","data":%s}`, http.StatusOK, constants.Success, camelJson))
case 1:
jsonString = []byte(fmt.Sprintf(`{"code":%d,"message":"%s","data":%s}`, http.StatusOK, args[0], camelJson))
case 2:
jsonString = []byte(fmt.Sprintf(`{"code":%d,"message":"%s","data":%s}`, args[1], args[0], camelJson))
}
// http
r.Context.Data(http.StatusOK, "application/json", jsonString)
}
json2
//
rootEngine.GET("/", func(context *gin.Context) {
response := app.Request{Context: context}
response.Success(app.D(map[string]interface{}{
"message": "Welcome to Herman!",
}))
})
response.Success()
4app.D()``response.Success()``app.H()``app.C()``app.M()
Json
func Login(ctx *gin.Context) {
context := app.Request{Context: ctx}
data := context.Params()
context.Json(AdminService.Login(AdminValidate.Login(data)), AdminConstant.LoginSuccess)
}
context.Json()
datamessagecode
/tests/base.go``tests
HTTP/tests/base.go
// AdminLogin
// @return void
func (s *SuiteCase) AdminLogin() {
var (
response app.Response
loginUri = s.AppPrefix + "/admin/login"
)
// mapjson
_, _, w := s.Request("POST", loginUri, map[string]interface{}{
"user": "admin",
"password": "123456",
})
// jsonstruct
_ = json.Unmarshal(w.Body.Bytes(), &response)
s.Authorization = response.Data.(string)
}
SetupSuite()
// SetupSuite
// @return void
func (s *SuiteCase) SetupSuite() {
app.InitConfig()
servers.ZapLogs()
middlewares.Reload()
gin.SetMode(app.Config.Mode)
e := gin.Default()
e.Use(middlewares.CatchError())
core.Engine = routers.InitRouter(e)
s.AppPrefix = app.Config.AppPrefix
switch s.Guard {
case "admin":
s.AdminLogin()
default:
panic(MiddlewareConstant.GuardError)
}
}
// TestAdminTestSuite
// @return void
func TestAdminTestSuite(t *testing.T) {
suite.Run(t, &AdminTestSuite{SuiteCase: test.SuiteCase{Guard: "admin"}})
}
Guard: "admin"
package admin
import (
"fmt"
"github.com/brianvoe/gofakeit/v6"
"github.com/herman-hang/herman/app/repositories"
"github.com/herman-hang/herman/kernel/core/test"
"github.com/herman-hang/herman/database/seeders/admin"
"github.com/herman-hang/herman/database/seeders/role"
"github.com/stretchr/testify/suite"
"testing"
)
//
type AdminTestSuite struct {
test.SuiteCase
}
var (
AdminLoginUri = "/admin/login" // URI
AdminUri = "/admin/admins" // URI
)
// TestLogin
// @return void
func (base *AdminTestSuite) TestLogin() {
base.Assert([]test.Case{
{
Method: "POST",
Uri: base.AppPrefix + AdminLoginUri,
Params: map[string]interface{}{"user": "admin", "password": "123456"},
Code: 200,
Message: "",
},
})
}
// TestAddAdmin
// @return void
func (base *AdminTestSuite) TestAddAdmin() {
roleInfo, _ := repositories.Role().Insert(role.Role())
adminInfo := admin.Admin()
adminInfo["roles"] = []map[string]interface{}{
{
"name": roleInfo["name"].(string),
"role": roleInfo["role"].(string),
},
}
base.Assert([]test.Case{
{
Method: "POST",
Uri: base.AppPrefix + AdminUri,
Params: adminInfo,
Code: 200,
Message: "",
},
})
}
// TestModifyAdmin
// @return void
func (base *AdminTestSuite) TestModifyAdmin() {
roleInfo, _ := repositories.Role().Insert(role.Role())
adminInfo := admin.Admin()
adminInfo["roles"] = []map[string]interface{}{
{
"name": roleInfo["name"].(string),
"role": roleInfo["role"].(string),
},
}
info, _ := repositories.Admin().Insert(adminInfo)
base.Assert([]test.Case{
{
Method: "PUT",
Uri: base.AppPrefix + AdminUri,
Params: map[string]interface{}{
"id": info["id"],
"user": gofakeit.Username(),
"password": gofakeit.Password(false, false, true, false, false, 10),
"photo": gofakeit.ImageURL(100, 100),
"roles": adminInfo["roles"],
"name": gofakeit.Name(),
"card": "450981200008272525",
"sex": gofakeit.RandomInt([]int{1, 2, 3}),
"age": gofakeit.Number(18, 60),
"region": gofakeit.Country(),
"phone": "18888888888",
"email": gofakeit.Email(),
"introduction": gofakeit.Sentence(10),
"state": gofakeit.RandomInt([]int{1, 2}),
"sort": gofakeit.Number(1, 100),
},
Code: 200,
Message: "",
},
})
}
// TestDeleteAdmin ID
// @return void
func (base *AdminTestSuite) TestFindAdmin() {
roleInfo, _ := repositories.Role().Insert(role.Role())
adminInfo := admin.Admin()
adminInfo["roles"] = []map[string]interface{}{
{
"name": roleInfo["name"].(string),
"role": roleInfo["role"].(string),
},
}
info, _ := repositories.Admin().Insert(adminInfo)
base.Assert([]test.Case{
{
Method: "GET",
Uri: base.AppPrefix + AdminUri + "/" + fmt.Sprintf("%d", info["id"]),
Params: nil,
Code: 200,
Message: "",
},
})
}
// TestGetAdminList
// @return void
func (base *AdminTestSuite) TestRemoveAdmin() {
roleInfo, _ := repositories.Role().Insert(role.Role())
adminInfo := admin.Admin()
adminInfo["roles"] = []map[string]interface{}{
{
"name": roleInfo["name"].(string),
"role": roleInfo["role"].(string),
},
}
info, _ := repositories.Admin().Insert(adminInfo)
base.Assert([]test.Case{
{
Method: "DELETE",
Uri: base.AppPrefix + AdminUri,
Params: map[string]interface{}{
"id": []uint{info["id"].(uint)},
},
Code: 200,
Message: "",
},
})
}
// TestGetAdminList
// @return void
func (base *AdminTestSuite) TestListAdmin() {
roleInfo, _ := repositories.Role().Insert(role.Role())
adminInfo := admin.Admin()
adminInfo["roles"] = []map[string]interface{}{
{
"name": roleInfo["name"].(string),
"role": roleInfo["role"].(string),
},
}
_, _ = repositories.Admin().Insert(adminInfo)
base.Assert([]test.Case{
{
Method: "GET",
Uri: base.AppPrefix + AdminUri,
Params: map[string]interface{}{"page": 1, "pageSize": 2, "keywords": ""},
Code: 200,
Message: "",
IsList: true,
Fields: []string{
"id",
"user",
"photo",
"sort",
"state",
"phone",
"email",
"name",
"card",
"introduction",
"sex",
"age",
"region",
"createdAt",
},
},
})
}
// TestAdminTestSuite
// @return void
func TestAdminTestSuite(t *testing.T) {
suite.Run(t, &AdminTestSuite{SuiteCase: test.SuiteCase{Guard: "admin"}})
}
/database/migrations``__.sql``1_init.up.sql``1_init.down.sql
(DDL)
herman migrate --status=true --direction=up
herman migrate -s true
herman migrate -s true -d up
herman migrate --status=true --direction=down
herman migrate -s true -d down
herman migrate --status=true --direction=force --version=1 # 1
herman migrate -s true -d force -v 1 # 1
herman migrate --status=true --direction=up --number=1
herman migrate -s true -d up -n 1
herman migrate --status=true --direction=down --number=1
herman migrate -s true -d down -n 1
herman migrate --status=true --direction=drop
herman migrate -s true -d drop
/database/seeders``brianvoe/gofakeit
package admin
import (
"github.com/brianvoe/gofakeit/v6"
)
// Admin
func Admin() map[string]interface{} {
return map[string]interface{}{
"user": gofakeit.Username(),
"password": gofakeit.Password(false, false, true, false, false, 10),
"photo": gofakeit.ImageURL(100, 100),
"name": gofakeit.Name(),
"card": "450981200008272525",
"sex": gofakeit.RandomInt([]int{1, 2, 3}),
"age": gofakeit.Number(18, 60),
"region": gofakeit.Country(),
"phone": "18888888888",
"email": gofakeit.Email(),
"introduction": gofakeit.Sentence(10),
"state": gofakeit.RandomInt([]int{1, 2}),
"sort": gofakeit.Number(1, 100),
}
}
https://github.com/brianvoe/gofakeit
12Herman
// WatermarkConfig
type WatermarkConfig struct {
FontSize int //
Color color.RGBA // rgba
Text string //
}
type BlockPuzzleConfig struct {
Offset int //
}
type ClickWordConfig struct {
FontSize int //
FontNum int //
}
type Config struct {
Watermark *WatermarkConfig
ClickWord *ClickWordConfig
BlockPuzzle *BlockPuzzleConfig
CacheType string //
CacheExpireSec int
}
func NewConfig() *Config {
return &Config{
CacheType: "redis", //
Watermark: &WatermarkConfig{
FontSize: 12,
Color: color.RGBA{R: 255, G: 255, B: 255, A: 255},
Text: "",
},
ClickWord: &ClickWordConfig{
FontSize: 25,
FontNum: 5,
},
BlockPuzzle: &BlockPuzzleConfig{Offset: 10},
CacheExpireSec: 2 * 60, //
}
}
package main
import (
config2 "github.com/TestsLing/aj-captcha-go/config"
"github.com/TestsLing/aj-captcha-go/service"
"github.com/TestsLing/aj-captcha-go/const"
"github.com/gin-gonic/gin"
)
//
type clientParams struct {
Token string `json:"token"`
PointJson string `json:"pointJson"`
CaptchaType string `json:"captchaType"`
}
//
var config = config2.NewConfig()
//
var factory = service.NewCaptchaServiceFactory(config)
func main() {
// CacheType key
factory.RegisterCache(constant.MemCacheKey, service.NewMemCacheService(20)) // 20
//
factory.RegisterService(constant.ClickWordCaptcha, service.NewClickWordCaptchaService(factory))
factory.RegisterService(constant.BlockPuzzleCaptcha, service.NewBlockPuzzleCaptchaService(factory))
//Default
r := gin.Default()
r.GET("/ping", func(c *gin.Context) {
//json
c.JSON(200, gin.H{
"message": "pong",
})
})
r.GET("/captcha/get", func(c *gin.Context) {
//
data := factory.GetService(config2.BlockPuzzleCaptcha).Get()
//json
c.JSON(200, data)
})
r.Run("0.0.0.0:888") // listen and serve on 0.0.0.0:888
}
https://ajcaptcha.beliefteam.cn/captcha-doc/
Apache License Version 2.0 see http://www.apache.org/licenses/LICENSE-2.0.html