[ADD]为数据添加redis缓存

This commit is contained in:
viletyy 2020-09-28 20:24:06 +08:00
parent 4181eb96d7
commit 17ce95596b
16 changed files with 543 additions and 144 deletions

View File

@ -27,3 +27,10 @@ Host = 0.0.0.0
Port = 5432
Name = shop
TablePrefix = shop_
[redis]
Host = 127.0.0.1:6379
Password =
MaxIdle = 30
MaxActive = 30
IdleTimeout = 200

View File

@ -13,7 +13,7 @@ func main() {
c := cron.New()
c.AddFunc("* * * * * *", func() {
log.Println("Run models.CleanEmployee...")
models.CleaanAllEmployee()
models.CleanAllEmployee()
})
c.Start()

3
go.mod
View File

@ -11,10 +11,11 @@ require (
github.com/go-openapi/spec v0.19.9 // indirect
github.com/go-openapi/swag v0.19.9 // indirect
github.com/go-playground/validator/v10 v10.3.0 // indirect
github.com/gomodule/redigo v2.0.0+incompatible
github.com/jinzhu/gorm v1.9.16
github.com/lib/pq v1.2.0 // indirect
github.com/mailru/easyjson v0.7.6 // indirect
github.com/robfig/cron v1.2.0 // indirect
github.com/robfig/cron v1.2.0
github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337 // indirect
github.com/swaggo/gin-swagger v1.2.0
github.com/swaggo/swag v1.6.7

View File

@ -4,6 +4,7 @@ import (
"fmt"
"github.com/gin-gonic/gin"
"github.com/go-pripro/shop/models"
"github.com/go-pripro/shop/pkg/gredis"
"github.com/go-pripro/shop/pkg/logging"
"github.com/go-pripro/shop/pkg/setting"
"github.com/go-pripro/shop/routers"
@ -15,6 +16,7 @@ func init() {
setting.Setup()
models.Setup()
logging.Setup()
gredis.Setup()
}
func main() {

View File

@ -1,7 +1,10 @@
package models
import "github.com/jinzhu/gorm"
type Employee struct {
Model
AvatarUrl string `json:"avatar_url"`
Username string `json:"username"`
Password string `json:"password"`
@ -20,15 +23,18 @@ func CheckEmployee(username, password string) bool {
return false
}
func ExistEmployeeByID(id int) bool {
func ExistEmployeeByID(id int) (bool, error) {
var employee Employee
db.Select("id").Where("id = ?", id).First(&employee)
if employee.ID > 0 {
return true
err := db.Select("id").Where("id = ? AND deleted_on = ?", id, 0).First(&employee).Error
if err != nil && err != gorm.ErrRecordNotFound {
return false, err
}
return false
if employee.ID > 0 {
return true, nil
}
return false, nil
}
func ExistEmployeeByUsername(username string) bool {
@ -40,40 +46,71 @@ func ExistEmployeeByUsername(username string) bool {
return false
}
func GetEmployeeTotal(maps interface{}) (count int) {
db.Model(&Employee{}).Where(maps).Count(&count)
func GetEmployeeTotal(maps interface{}) (int, error) {
var count int
if err := db.Model(&Employee{}).Where(maps).Count(&count).Error; err != nil {
return 0, err
}
return
return count, nil
}
func GetEmployees(pageNum int, pageSize int, maps interface{}) (employees []Employee) {
db.Where(maps).Offset(pageNum).Limit(pageSize).Find(&employees)
func GetEmployees(pageNum int, pageSize int, maps interface{}) ([]*Employee, error) {
var employees []*Employee
err := db.Where(maps).Offset(pageNum).Limit(pageSize).Find(&employees).Error
if err != nil && err != gorm.ErrRecordNotFound {
return nil, err
}
return
return employees, nil
}
func GetEmployee(id int) (employee Employee) {
db.Where("id = ?", id).First(&employee)
return
func GetEmployee(id int) (*Employee, error) {
var employee Employee
err := db.Where("id = ? AND deleted_on = ? ", id, 0).First(&employee).Error
if err != nil && err != gorm.ErrRecordNotFound {
return nil, err
}
err = db.Model(&employee).Error
if err != nil && err != gorm.ErrRecordNotFound {
return nil, err
}
return &employee, nil
}
func EditEmployee(id int, data interface{}) bool {
db.Model(&Employee{}).Where("id = ?", id).Updates(data)
return true
func EditEmployee(id int, data interface{}) error {
if err := db.Model(&Employee{}).Where("id = ? AND deleted_on = ?", id, 0).Updates(data).Error; err != nil {
return err
}
return nil
}
func AddArticle(data map[string]interface{}) bool {
db.Create(&Employee{
func AddEmployee(data map[string]interface{}) error {
employee := Employee{
AvatarUrl: data["avatar_url"].(string),
Username: data["username"].(string),
Password: data["password"].(string),
Department: data["department"].(string),
Position: data["position"].(string),
})
return true
}
if err := db.Create(&employee).Error; err != nil {
return err
}
return nil
}
func CleaanAllEmployee() bool {
func DeleteEmployee(id int) error {
if err := db.Where("id = ?", id).Delete(Employee{}).Error; err != nil {
return err
}
return nil
}
func CleanAllEmployee() bool {
db.Unscoped().Where("deleted_on != ?", 0).Delete(&Employee{})
return true

View File

@ -15,7 +15,7 @@ type Model struct {
ID int `gorm:"primary_key" json:"id"`
CreatedOn int `json:"created_on"`
ModifiedOn int `json:"modified_on"`
DeleteOn int `json:"deleted_on"`
DeletedOn int `json:"deleted_on"`
}
func Setup() {
@ -82,13 +82,13 @@ func deleteCallback(scope *gorm.Scope) {
extraOption = fmt.Sprint(str)
}
deleteOnField, hasDeleteOnField := scope.FieldByName("DeleteOn")
deletedOnField, hasDeletedOnField := scope.FieldByName("DeletedOn")
if !scope.Search.Unscoped && hasDeleteOnField {
if !scope.Search.Unscoped && hasDeletedOnField {
scope.Raw(fmt.Sprintf(
"update %v SET %v=%v%v%v",
scope.QuotedTableName(),
scope.Quote(deleteOnField.DBName),
scope.Quote(deletedOnField.DBName),
scope.AddToVars(time.Now().Unix()),
addExtraSpaceIfExist(scope.CombinedConditionSql()),
addExtraSpaceIfExist(extraOption),

View File

@ -1 +1,14 @@
package app
import (
"github.com/astaxie/beego/validation"
"github.com/go-pripro/shop/pkg/logging"
)
func MarkErrors(errors []*validation.Error) {
for _, err := range errors {
logging.Info(err.Key, err.Message)
}
return
}

View File

@ -1 +1,20 @@
package app
import (
"github.com/gin-gonic/gin"
"github.com/go-pripro/shop/pkg/e"
)
type Gin struct {
C *gin.Context
}
func (g *Gin) Response(httpCode, errCode int, data interface{}) {
g.C.JSON(httpCode, gin.H{
"code" : errCode,
"msg" : e.GetMsg(errCode),
"data" : data,
})
return
}

View File

@ -1 +1,5 @@
package e
const (
CacheEmployee = "Employee"
)

View File

@ -12,6 +12,9 @@ const (
ErrorUploadCheckImageFail = 10006
ErrorUploadCheckImageFormat = 10007
ErrorExistEmployee = 20001
ErrorNotExistEmployee = 20002
ErrorExistEmployee = 20001
ErrorNotExistEmployee = 20002
ErrorCheckExistEmployeeFail = 20003
ErrorGetEmployeeFail = 20004
ErrorCreatedEmployeeFail = 20005
)

View File

@ -13,6 +13,9 @@ var MsgFlags = map[int]string{
ErrorUploadCheckImageFormat: "校验图片错误,图片格式或大小有问题",
ErrorExistEmployee: "已存在该员工用户名",
ErrorNotExistEmployee: "该员工不存在",
ErrorCheckExistEmployeeFail: "该员工信息缓存失败",
ErrorGetEmployeeFail: "获取员工信息缓存失败",
ErrorCreatedEmployeeFail: "创建员工失败,入数据库",
}
func GetMsg(code int) string {

View File

@ -1 +1,100 @@
package gredis
import (
"encoding/json"
"github.com/go-pripro/shop/pkg/setting"
"github.com/gomodule/redigo/redis"
"time"
)
var RedisConn *redis.Pool
func Setup() error {
RedisConn = &redis.Pool{
Dial: func() (redis.Conn, error) {
c, err := redis.Dial("tcp", setting.RedisSetting.Host)
if err != nil {
return nil, err
}
if setting.RedisSetting.Password != "" {
if _, err := c.Do("AUTH", setting.RedisSetting.Password); err != nil {
c.Close()
return nil, err
}
}
return c, err
},
TestOnBorrow: func(c redis.Conn, t time.Time) error {
_, err := c.Do("PING")
return err
},
MaxIdle: setting.RedisSetting.MaxIdle, // 最大空闲连接数
MaxActive: setting.RedisSetting.MaxActive, // 在给定时间内,允许分配的最大连接数
IdleTimeout: setting.RedisSetting.IdleTimeout, // 在给定时间内将会保持空闲状态,若到达时间限制则关闭连接
}
return nil
}
func Set(key string, data interface{}, time int) error {
conn := RedisConn.Get() //在连接池中获取一个活跃连接
defer conn.Close()
value, err := json.Marshal(data)
if err != nil {
return err
}
_, err = conn.Do("SET", key, value) //向redis服务器发送命令并返回收到的答复
if err != nil {
return err
}
_, err = conn.Do("EXPIRE", key, time)
if err != nil {
return err
}
return nil
}
func Exists(key string) bool {
conn := RedisConn.Get()
defer conn.Close()
exists, err := redis.Bool(conn.Do("EXISTS", key)) // 将命令返回转为布尔值
if err != nil {
return false
}
return exists
}
func Get(key string) ([]byte, error) {
conn := RedisConn.Get()
defer conn.Close()
reply, err := redis.Bytes(conn.Do("GET", key)) // 将命令返回转为Bytes
if err != nil {
return nil, err
}
return reply, nil
}
func Delete(key string) (bool, error) {
conn := RedisConn.Get()
defer conn.Close()
return redis.Bool(conn.Do("DEL", key))
}
func LikeDeletes(key string) error {
conn := RedisConn.Get()
defer conn.Close()
keys, err := redis.Strings(conn.Do("KEYS", "*"+key+"*")) // 将命令返回转为[]string
if err != nil {
return err
}
for _, key := range keys {
_, err = Delete(key)
if err != nil {
return err
}
}
return nil
}

View File

@ -45,6 +45,16 @@ type Database struct {
var DatabaseSetting = &Database{}
type Redis struct {
Host string
Password string
MaxIdle int
MaxActive int
IdleTimeout time.Duration
}
var RedisSetting = &Redis{}
func Setup() {
Cfg, err := ini.Load("conf/app.ini")
if err != nil {
@ -68,4 +78,11 @@ func Setup() {
if err != nil {
log.Fatalf("Cfg.MapTo DatabaseSetting err: %v", err)
}
err = Cfg.Section("redis").MapTo(RedisSetting)
if err != nil {
log.Fatalf("Cfg.MapTo DatabaseSetting err: %v", err)
}
RedisSetting.IdleTimeout *= time.Second
}

View File

@ -4,92 +4,15 @@ import (
"github.com/astaxie/beego/validation"
"github.com/gin-gonic/gin"
"github.com/go-pripro/shop/models"
"github.com/go-pripro/shop/pkg/app"
"github.com/go-pripro/shop/pkg/e"
"github.com/go-pripro/shop/pkg/logging"
"github.com/go-pripro/shop/pkg/setting"
"github.com/go-pripro/shop/pkg/util"
"github.com/go-pripro/shop/service/employee_service"
"github.com/unknwon/com"
"net/http"
)
// @Summary 获取单个员工
// @Tags employees
// @Description
// @Accept json
// @Produce json
// @Param Authorization header string true "auth by /admin/login"
// @Param id path int true "ID"
// @Success 200 {string} json "{"code": 200, "data": {}, "msg": "ok"}"
// @Router /admin/v1/employees/{id} [GET]
func GetEmployee(c *gin.Context) {
id := com.StrTo(c.Param("id")).MustInt()
valid := validation.Validation{}
valid.Min(id, 1, "id").Message("ID必须大于0")
code := e.InvalidParams
var data interface{}
if !valid.HasErrors() {
if models.ExistEmployeeByID(id) {
data = models.GetEmployee(id)
code = e.SUCCESS
} else {
code = e.ErrorNotExistEmployee
}
} else {
for _, err := range valid.Errors {
logging.Info("err.key: %s, err.message: %s", err.Key, err.Message)
}
}
c.JSON(http.StatusOK, gin.H{
"code" : code,
"msg" : e.GetMsg(code),
"data" : data,
})
}
// @Summary 获取员工列表
// @Tags employees
// @Description
// @Accept json
// @Produce json
// @Param Authorization header string true "auth by /admin/login"
// @Param department query string false "Department"
// @Param position query string false "Position"
// @Param state query int false "State"
// @Success 200 {string} json "{"code": 200, "data": {}, "msg":"ok"}"
// @Router /admin/v1/employees [get]
func GetEmployees(c *gin.Context) {
data := make(map[string]interface{})
maps := make(map[string]interface{})
valid := validation.Validation{}
var state int = -1
if arg := c.Query("state"); arg != "" {
state = com.StrTo(arg).MustInt()
maps["state"] = state
valid.Range(state, 0, 1, "state").Message("状态只允许0或1")
}
code := e.InvalidParams
if ! valid.HasErrors() {
code = e.SUCCESS
data["lists"] = models.GetEmployees(util.GetPage(c), setting.AppSetting.PageSize, maps)
data["total"] = models.GetEmployeeTotal(maps)
} else {
for _, err := range valid.Errors {
logging.Info(err.Key, err.Message)
//log.Printf("err.key: %s, err.message: %s", err.Key, err.Message)
}
}
c.JSON(http.StatusOK, gin.H{
"code" : code,
"msg" : e.GetMsg(code),
"data" : data,
})
}
// @Summary 新增员工
// @Tags employees
// @Description
@ -104,6 +27,7 @@ func GetEmployees(c *gin.Context) {
// @Success 200 {string} json "{"code": 200, "data":{}, "msg":"ok"}"
// @Router /admin/v1/employees [post]
func AddEmployee(c *gin.Context) {
appG := app.Gin{C: c}
username := c.PostForm("username")
password := c.PostForm("password")
department := c.PostForm("department")
@ -116,32 +40,127 @@ func AddEmployee(c *gin.Context) {
valid.Required(department, "department").Message("部门不能为空")
valid.Required(position, "position").Message("职位不能为空")
if valid.HasErrors() {
app.MarkErrors(valid.Errors)
appG.Response(http.StatusOK, e.InvalidParams, nil)
return
}
if models.ExistEmployeeByUsername(username) {
appG.Response(http.StatusInternalServerError, e.ErrorExistEmployee, nil)
}
employeeService := employee_service.Employee{
AvatarUrl: avatarUrl,
Username: username,
Password: password,
Department: department,
Position: position,
}
if err := employeeService.Add(); err == nil {
appG.Response(http.StatusOK, e.SUCCESS, employeeService)
} else {
appG.Response(http.StatusOK, e.ErrorCreatedEmployeeFail, employeeService)
}
}
// @Summary 获取单个员工
// @Tags employees
// @Description
// @Accept json
// @Produce json
// @Param Authorization header string true "auth by /admin/login"
// @Param id path int true "ID"
// @Success 200 {string} json "{"code": 200, "data": {}, "msg": "ok"}"
// @Router /admin/v1/employees/{id} [GET]
func GetEmployee(c *gin.Context) {
appG := app.Gin{C: c}
id := com.StrTo(c.Param("id")).MustInt()
valid := validation.Validation{}
valid.Min(id, 1, "id")
// 验证参数
if valid.HasErrors() {
app.MarkErrors(valid.Errors)
appG.Response(http.StatusOK, e.InvalidParams, nil)
return
}
// 调用service对象存储数据
employeeService := employee_service.Employee{
ID: id,
}
// 员工不存在的情况
exists, err := employeeService.ExistByID()
if err != nil {
appG.Response(http.StatusInternalServerError, e.ErrorCheckExistEmployeeFail, nil)
}
if !exists {
appG.Response(http.StatusOK, e.ErrorNotExistEmployee, nil)
return
}
if employee, err := employeeService.Get(); err != nil{
// 获取员工信息的过程中Get()出错
appG.Response(http.StatusInternalServerError, e.ErrorGetEmployeeFail, employee)
} else {
appG.Response(http.StatusOK, e.SUCCESS, employee)
}
}
// @Summary 获取员工列表
// @Tags employees
// @Description
// @Accept json
// @Produce json
// @Param Authorization header string true "auth by /admin/login"
// @Param department query string false "Department"
// @Param position query string false "Position"
// @Param state query int false "State"
// @Success 200 {string} json "{"code": 200, "data": {}, "msg":"ok"}"
// @Router /admin/v1/employees [get]
func GetEmployees(c *gin.Context) {
appG := app.Gin{C: c}
valid := validation.Validation{}
var state int = -1
if arg := c.Query("state"); arg != "" {
state = com.StrTo(arg).MustInt()
valid.Range(state, 0, 1, "state").Message("状态只允许0或1")
}
if valid.HasErrors() {
app.MarkErrors(valid.Errors)
appG.Response(http.StatusBadRequest, e.InvalidParams, nil)
}
employeeService := employee_service.Employee{
State: state,
PageNum: util.GetPage(c),
PageSize: setting.AppSetting.PageSize,
}
total, err := employeeService.Count()
if err != nil {
appG.Response(http.StatusInternalServerError, e.InvalidParams, nil)
return
}
employees, err := employeeService.GetAll()
if err != nil {
appG.Response(http.StatusInternalServerError, e.InvalidParams, nil)
return
}
data := make(map[string]interface{})
data["lists"] = employees
data["total"] = total
code := e.InvalidParams
if !valid.HasErrors() {
if !models.ExistEmployeeByUsername(username) {
data["username"] = username
data["password"] = password
data["department"] = department
data["position"] = position
data["avatar_url"] = avatarUrl
code = e.SUCCESS
} else {
code = e.ErrorExistEmployee
}
}
if code == 200 {
models.AddArticle(data)
}
c.JSON(http.StatusOK, gin.H{
"code" : code,
"msg" : e.GetMsg(code),
"data" : data,
})
appG.Response(http.StatusOK, e.SUCCESS, data)
}
// @Summary 更新员工
@ -159,7 +178,7 @@ func AddEmployee(c *gin.Context) {
// @Param state formData string false "员工状态"
// @Success 200 {string} json "{"code": 200, "data":{}, "msg":"ok"}"
// @Router /admin/v1/employees/{id} [put]
func EditEmployee(c *gin.Context) {
func EditEmployee(c *gin.Context) {
id := com.StrTo(c.Param("id")).MustInt()
avatarUrl := c.PostForm("avatar_url")
username := c.PostForm("username")
@ -169,16 +188,16 @@ func EditEmployee(c *gin.Context) {
state := com.StrTo(c.PostForm("state")).MustInt()
valid := validation.Validation{}
valid.Min(id,1, "id").Message("必须是有效的员工id")
valid.Min(id, 1, "id").Message("必须是有效的员工id")
valid.Range(state, 0, 1, "state").Message("状态只允许0或1")
data := make(map[string]interface{})
code := e.InvalidParams
if ! valid.HasErrors() {
if models.ExistEmployeeByID(id) {
if !valid.HasErrors() {
if _, err := models.ExistEmployeeByID(id); err == nil {
code = e.SUCCESS
if avatarUrl != "" {
if avatarUrl != "" {
data["avatar_url"] = avatarUrl
}
if username != "" {
@ -202,13 +221,13 @@ func EditEmployee(c *gin.Context) {
}
if code == 200 {
models.EditEmployee(id, data)
models.EditEmployee(id, data)
}
c.JSON(http.StatusOK, gin.H{
"code" : code,
"msg" : e.GetMsg(code),
"code": code,
"msg": e.GetMsg(code),
"data": data,
})
}
}

View File

@ -1 +1,44 @@
package cache_service
import (
"github.com/go-pripro/shop/pkg/e"
"strconv"
"strings"
)
type Employee struct {
ID int
State int
PageNum int
PageSize int
}
func (employee *Employee) GetEmployeeKey() string {
return e.CacheEmployee + "_" + strconv.Itoa(employee.ID)
}
func (employee *Employee) GetEmployeesKey() string {
keys := []string{
e.CacheEmployee,
"List",
}
if employee.ID > 0 {
keys = append(keys, strconv.Itoa(employee.ID))
}
if employee.State >= 0 {
keys = append(keys, strconv.Itoa(employee.State))
}
if employee.PageNum > 0 {
keys = append(keys, strconv.Itoa(employee.PageNum))
}
if employee.PageSize > 0 {
keys = append(keys, strconv.Itoa(employee.PageSize))
}
return strings.Join(keys, "_")
}

View File

@ -1 +1,133 @@
package employee_service
import (
"encoding/json"
"errors"
"github.com/go-pripro/shop/models"
"github.com/go-pripro/shop/pkg/gredis"
"github.com/go-pripro/shop/pkg/logging"
"github.com/go-pripro/shop/service/cache_service"
)
type Employee struct {
ID int
CreatedOn int
ModifiedOn int
DeletedOn int
AvatarUrl string
Username string
Password string
Department string
Position string
State int
PageNum int
PageSize int
}
func (e *Employee) Add() error {
employee := map[string]interface{}{
"avatar_url": e.AvatarUrl,
"username": e.Username,
"password": e.Password,
"department": e.Department,
"position": e.Position,
"state": e.State,
}
if err := models.AddEmployee(employee); err != nil {
return err
}
return nil
}
func (e *Employee) Edit() error {
return models.EditEmployee(e.ID, map[string]interface{}{
"avatar_url": e.AvatarUrl,
"username": e.Username,
"password": e.Password,
"department": e.Department,
"position": e.Position,
"state": e.State,
})
}
func (e *Employee) Get() (*models.Employee, error) {
var cacheEmployee *models.Employee
cache := cache_service.Employee{
ID: e.ID,
}
key := cache.GetEmployeeKey()
if gredis.Exists(key) {
data, err := gredis.Get(key)
if err != nil {
logging.Info(err)
return cacheEmployee, errors.New("从缓存中获取员工信息失败")
} else {
json.Unmarshal(data, &cacheEmployee)
return cacheEmployee, nil
}
}
if employee, err := models.GetEmployee(e.ID); err != nil {
return nil, err
} else {
gredis.Set(key, employee, 3600)
//cacheEmployee.ID = employee.ID
return cacheEmployee, nil
}
}
func (e *Employee) GetAll() ([]*models.Employee, error) {
var (
employees, cacheEmployees []*models.Employee
)
cache := cache_service.Employee{
State: e.State,
PageNum: e.PageNum,
PageSize: e.PageSize,
}
key := cache.GetEmployeesKey()
if gredis.Exists(key) {
data, err := gredis.Get(key)
if err != nil {
logging.Info(err)
} else {
json.Unmarshal(data, &cacheEmployees)
return cacheEmployees, nil
}
}
employees, err := models.GetEmployees(e.PageNum, e.PageSize, e.getMaps())
if err != nil {
return nil, err
}
gredis.Set(key, employees, 3600)
return employees, nil
}
func (a *Employee) Delete() error {
return models.DeleteEmployee(a.ID)
}
func (e *Employee) ExistByID() (bool, error) {
return models.ExistEmployeeByID(e.ID)
}
func (e *Employee) Count() (int, error) {
return models.GetEmployeeTotal(e.getMaps())
}
func (e *Employee) getMaps() map[string]interface{} {
maps := make(map[string]interface{})
maps["deleted_on"] = 0
if e.State != -1 {
maps["state"] = e.State
}
return maps
}