redis, updatediff

This commit is contained in:
suyl
2021-07-07 15:24:12 +08:00
parent 2beca9fbcb
commit acb48212a1
7 changed files with 346 additions and 19 deletions

View File

@@ -5,6 +5,7 @@ import (
"fmt"
"git.rosy.net.cn/jx-print/globals"
"git.rosy.net.cn/jx-print/model"
putils "git.rosy.net.cn/jx-print/utils"
"github.com/dchest/captcha"
"github.com/gin-contrib/sessions"
"github.com/gin-gonic/gin"
@@ -42,9 +43,10 @@ func callFunc(c *gin.Context, worker func() (retVal interface{}, errCode string,
} else {
token = cookie.Value
}
if token != "token" {
err = fmt.Errorf("token过期,请重新登录!")
if user := putils.GetKet(token); user == nil {
err = fmt.Errorf("token过期或无效,请重新登录!")
callBack.Desc = err.Error()
callBack.Code = model.ErrCodeToken
c.JSON(http.StatusOK, callBack)
return false
}

View File

@@ -43,7 +43,7 @@ func GetUsers(c *gin.Context) {
return
}
type RegisterUserParam struct {
type UserParam struct {
Name string `json:"name" form:"name" binding:"required"` //用户名
Password string `json:"password" form:"password" binding:"required"` //密码md5后的
Code string `json:"code" form:"code" binding:"required"` //验证码
@@ -53,7 +53,7 @@ type RegisterUserParam struct {
func RegisterUser(c *gin.Context) {
var (
err error
user = &RegisterUserParam{}
user = &UserParam{}
)
globals.SugarLogger.Debugf("Begin API :%s params: %v ip: %s", c.Request.URL, c.Params, c.ClientIP())
if err = c.Bind(&user); err != nil {
@@ -81,17 +81,11 @@ func RegisterUser(c *gin.Context) {
return
}
type LoginParam struct {
Name string `json:"name" form:"name" binding:"required"` //用户名
Password string `json:"password" form:"password" binding:"required"` //密码md5后的
Code string `json:"code" form:"code" binding:"required"` //验证码
}
//登录 POST
func Login(c *gin.Context) {
var (
err error
user = &LoginParam{}
user = &UserParam{}
)
globals.SugarLogger.Debugf("Begin API :%s params: %v ip: %s", c.Request.URL, c.Params, c.ClientIP())
if err = c.Bind(&user); err != nil {
@@ -111,7 +105,85 @@ func Login(c *gin.Context) {
return
}
if !callFunc(c, func() (retVal interface{}, errCode string, err error) {
retVal, err = services.Login(c, user.Name, user.Password, user.Code)
retVal, err = services.Login(c, user.Name, user.Password)
return retVal, "", err
}) {
return
}
return
}
//自动登录获取token POST
func GetTokenInfo(c *gin.Context) {
var (
err error
user = &struct {
Token string `json:"token" form:"token"`
}{}
)
globals.SugarLogger.Debugf("Begin API :%s params: %v ip: %s", c.Request.URL, c.Params, c.ClientIP())
if err = c.Bind(&user); err != nil {
c.JSON(http.StatusOK, &CallBack{
Code: model.ErrCodeNormal,
Desc: err.Error(),
})
globals.SugarLogger.Debugf("End API :%s error:%v:", c.Request.URL, err)
return
}
if !callFunc(c, func() (retVal interface{}, errCode string, err error) {
retVal, err = services.GetTokenInfo(c, user.Token)
return retVal, "", err
}) {
return
}
return
}
//登出删token POST
func Logout(c *gin.Context) {
var (
err error
user = &struct {
Token string `json:"token" form:"token"`
}{}
)
globals.SugarLogger.Debugf("Begin API :%s params: %v ip: %s", c.Request.URL, c.Params, c.ClientIP())
if err = c.Bind(&user); err != nil {
c.JSON(http.StatusOK, &CallBack{
Code: model.ErrCodeNormal,
Desc: err.Error(),
})
globals.SugarLogger.Debugf("End API :%s error:%v:", c.Request.URL, err)
return
}
if !callFunc(c, func() (retVal interface{}, errCode string, err error) {
err = services.Logout(c, user.Token)
return retVal, "", err
}) {
return
}
return
}
//更新用户信息 POST
func UpdateUser(c *gin.Context) {
var (
err error
user = &struct {
Payload string `json:"payload" form:"payload"` //user 的json格式数据
}{}
)
globals.SugarLogger.Debugf("Begin API :%s params: %v ip: %s", c.Request.URL, c.Params, c.ClientIP())
if err = c.Bind(&user); err != nil {
c.JSON(http.StatusOK, &CallBack{
Code: model.ErrCodeNormal,
Desc: err.Error(),
})
globals.SugarLogger.Debugf("End API :%s error:%v:", c.Request.URL, err)
return
}
if !callFunc(c, func() (retVal interface{}, errCode string, err error) {
err = services.UpdateUser(c, user.Payload)
return retVal, "", err
}) {
return

View File

@@ -1,6 +1,7 @@
package dao
import (
"fmt"
putils "git.rosy.net.cn/jx-print/utils"
"github.com/jmoiron/sqlx"
"reflect"
@@ -13,16 +14,17 @@ func Insert(db *sqlx.DB, obj interface{}) (err error) {
var (
value = reflect.ValueOf(obj)
stype = reflect.TypeOf(obj)
sname = stype.Name()
sql, values = strings.Builder{}, strings.Builder{}
sqlParams = []interface{}{}
direct reflect.Value
)
if stype.Kind() != reflect.Struct {
direct = reflect.Indirect(value)
stype = stype.Elem()
} else {
direct = value
}
sname := stype.Name()
sql.WriteString("INSERT INTO ")
for i := 0; i < stype.NumField()-1; i++ {
if stype.Field(i).Type.String() == "*time.Time" {
@@ -50,3 +52,108 @@ func Insert(db *sqlx.DB, obj interface{}) (err error) {
_, err = db.DB.Exec(sql.String(), sqlParams...)
return err
}
func Update(db *sqlx.DB, obj interface{}, fields ...string) (err error) {
var (
value = reflect.ValueOf(obj)
stype = reflect.TypeOf(obj)
sql = strings.Builder{}
sqlParams = []interface{}{}
direct reflect.Value
fieldsMap = make(map[string]string)
)
if stype.Kind() != reflect.Struct {
direct = reflect.Indirect(value)
stype = stype.Elem()
} else {
direct = value
}
sname := stype.Name()
sql.WriteString("UPDATE ")
sql.WriteString(putils.UnMarshalHr(sname) + " SET ")
fieldsStr := []string{}
for _, v := range fields {
fieldsStr = append(fieldsStr, v+"=?")
fieldsMap[v] = v
}
sql.WriteString(strings.Join(fieldsStr, ","))
sql.WriteString(" WHERE id = ?")
for i := 0; i < stype.NumField()-1; i++ {
if fieldsMap[stype.Field(i).Tag.Get("json")] != "" {
if stype.Field(i).Type.String() == "*time.Time" {
if direct.Field(i).Interface().(*time.Time) != nil {
sqlParams = append(sqlParams, direct.Field(i).Interface())
}
} else {
if !direct.Field(i).IsZero() {
sqlParams = append(sqlParams, direct.Field(i).Interface())
}
}
}
}
if direct.Field(0).Int() == 0 {
return err
} else {
sqlParams = append(sqlParams, direct.Field(0).Int())
}
_, err = db.DB.Exec(sql.String(), sqlParams...)
return err
}
//更新两个结构体中不同的字段
//obj是作为参数obj2是原本的要更新的
func UpdateDiff(db *sqlx.DB, obj interface{}, obj2 interface{}) (err error) {
var (
value = reflect.ValueOf(obj)
stype = reflect.TypeOf(obj)
value2 = reflect.ValueOf(obj2)
stype2 = reflect.TypeOf(obj2)
sql = strings.Builder{}
sqlParams = []interface{}{}
fieldMap1 = make(map[string]interface{})
fieldMap2 = make(map[string]interface{})
fields = make(map[string]interface{})
)
if stype.Kind() != reflect.Struct {
stype = stype.Elem()
value = reflect.Indirect(value)
}
if stype2.Kind() != reflect.Struct {
stype2 = stype2.Elem()
value2 = reflect.Indirect(value2)
}
sname := stype.Name()
sname2 := stype2.Name()
if sname != sname2 {
return fmt.Errorf("请传入两个类型相同的结构体!")
}
for i := 1; i < stype.NumField()-1; i++ {
fieldMap1[stype.Field(i).Tag.Get("json")] = value.Field(i).Interface()
}
for i := 1; i < stype2.NumField()-1; i++ {
fieldMap2[stype2.Field(i).Tag.Get("json")] = value2.Field(i).Interface()
}
for k, v := range fieldMap1 {
if fieldMap2[k] != nil {
if fieldMap2[k] != v {
fields[k] = v
}
}
}
sql.WriteString("UPDATE ")
sql.WriteString(putils.UnMarshalHr(sname) + " SET ")
fieldsStr := []string{}
for k, v := range fields {
fieldsStr = append(fieldsStr, k+"=?")
sqlParams = append(sqlParams, v)
}
sql.WriteString(strings.Join(fieldsStr, ","))
sql.WriteString(" WHERE id = ?")
if value2.Field(0).Int() == 0 {
return err
} else {
sqlParams = append(sqlParams, value2.Field(0).Int())
}
_, err = db.DB.Exec(sql.String(), sqlParams...)
return err
}

View File

@@ -29,3 +29,17 @@ func GetUsers(db *sqlx.DB, userID, name, mobile string) (users []*model.User, er
}
return users, err
}
func GetUserForLogin(db *sqlx.DB, name, password string) (user *model.User, err error) {
var users []*model.User
sql := `
SELECT *
FROM user
WHERE name = ? AND password = ?
`
sqlParams := []interface{}{name, password}
if err = db.Select(&users, sql, sqlParams...); err == nil {
return users[0], err
}
return user, err
}

View File

@@ -10,11 +10,14 @@ func Init(r *gin.Engine) {
//user
user := v2.Group("/user")
user.GET("/getUsers", controllers.GetUsers)
user.GET("/getTokenInfo", controllers.GetTokenInfo)
user.GET("/logout", controllers.Logout)
user.GET("/updateUser", controllers.UpdateUser)
//v1是不需要token的
v1 := r.Group("v1")
userw := v1.Group("/user")
user.GET("/login", controllers.Login)
userw.GET("/login", controllers.Login)
userw.GET("/refreshCode", controllers.RefreshCode)
userw.GET("/register", controllers.RegisterUser)
}

View File

@@ -2,15 +2,24 @@ package services
import (
"crypto/md5"
"encoding/json"
"fmt"
"git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/jx-print/dao"
"git.rosy.net.cn/jx-print/globals"
"git.rosy.net.cn/jx-print/model"
putils "git.rosy.net.cn/jx-print/utils"
"github.com/gin-gonic/gin"
"strings"
"time"
)
const (
TokenHeader = "TOKEN"
TokenVer = "V2"
TokenTypeSep = "."
)
func GetUsers(c *gin.Context, name, mobile, userID string) (users []*model.User, err error) {
return dao.GetUsers(globals.GetDB(), userID, name, mobile)
}
@@ -18,7 +27,7 @@ func GetUsers(c *gin.Context, name, mobile, userID string) (users []*model.User,
func RegisterUser(c *gin.Context, name, password string) (err error) {
var (
db = globals.GetDB()
user = model.User{}
user = &model.User{}
now = time.Now()
)
if users, _ := dao.GetUsers(db, "", name, ""); len(users) > 0 {
@@ -34,10 +43,84 @@ func RegisterUser(c *gin.Context, name, password string) (err error) {
return err
}
func Login(c *gin.Context, name, password, code string) (user *model.User, err error) {
//var (
// db = globals.GetDB()
//)
type LoginResult struct {
model.User
Token string `json:"token"` //token
}
func Login(c *gin.Context, name, password string) (loginResult *LoginResult, err error) {
var (
db = globals.GetDB()
now = time.Now()
user = &model.User{}
token string
)
loginResult = &LoginResult{}
if users, _ := dao.GetUsers(db, "", name, ""); len(users) == 0 {
return loginResult, fmt.Errorf("用户名不存在!")
}
if user, err = dao.GetUserForLogin(db, name, fmt.Sprintf("%x", md5.Sum([]byte(model.RegisterKey+password)))); err != nil {
return loginResult, err
} else if user == nil {
return loginResult, fmt.Errorf("密码错误!")
}
loginResult.User = *user
//创建token
token, err = setToken(user)
loginResult.Token = token
//更新登录时间和ip
user.LastLoginAt = &now
user.LastLoginIP = c.ClientIP()
err = dao.Update(db, user, "last_login_at", "last_login_ip")
return loginResult, err
}
func setToken(user *model.User) (token string, err error) {
token = createToken(user)
err = putils.SetKey(token, user, putils.DefTokenDuration)
return token, err
}
func createToken(user *model.User) (token string) {
return strings.Join([]string{
TokenHeader,
TokenVer,
user.UserID,
time.Now().Format("20060102-150405"),
utils.GetUUID(),
user.Name,
}, TokenTypeSep)
}
func GetTokenInfo(c *gin.Context, token string) (user *model.User, err error) {
result := putils.GetKet(token)
if user, ok := result.(*model.User); !ok {
return user, err
}
return user, err
}
func Logout(c *gin.Context, token string) (err error) {
return putils.DelKey(token)
}
func UpdateUser(c *gin.Context, payload string) (err error) {
var (
db = globals.GetDB()
userp = &model.User{}
user = &model.User{}
)
if err = json.Unmarshal([]byte(payload), &userp); err != nil {
return err
}
if userp.ID == 0 && userp.UserID == "" {
return fmt.Errorf("id 和 user_id 至少传一个!")
}
if users, err := dao.GetUsers(db, userp.UserID, "", ""); err != nil {
return err
} else {
user = users[0]
}
err = dao.UpdateDiff(db, userp, user)
return err
}

46
utils/redis.go Normal file
View File

@@ -0,0 +1,46 @@
package utils
import (
"git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/jx-print/globals"
"github.com/go-redis/redis"
"time"
)
const (
DefTokenDuration = time.Hour * 24 * 7
)
var (
client *redis.Client
)
func init() {
globals.SugarLogger.Debugf("redis init..")
client = redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "",
})
pong, err := client.Ping().Result()
globals.SugarLogger.Debugf("redis pong %v, err: %v", pong, err)
}
func SetKey(key string, value interface{}, expiration time.Duration) error {
strValue := string(utils.MustMarshal(value))
return client.Set(key, strValue, expiration).Err()
}
func DelKey(key string) error {
return client.Del(key).Err()
}
func GetKet(key string) interface{} {
result, err := client.Get(key).Result()
if err == nil {
var retVal interface{}
if err = utils.UnmarshalUseNumber([]byte(result), &retVal); err == nil {
return retVal
}
}
return nil
}