From 2beca9fbcb19827a7fd8a531a3105eadda81902c Mon Sep 17 00:00:00 2001 From: suyl <770236076@qq.com> Date: Wed, 7 Jul 2021 10:21:51 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B3=A8=E5=86=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 +- controllers/controller.go | 90 +++++++++++++++++++++++++- controllers/user_controller.go | 114 ++++++++++++++++++++++++++++++++- dao/dao.go | 52 +++++++++++++++ dao/user_dao.go | 31 +++++++++ globals/globals.go | 22 ++++--- model/model.go | 32 +++++++-- model/user.go | 30 +++++++++ routers/router.go | 13 +++- services/user.go | 41 ++++++++++++ utils/utils.go | 85 +++++++++++++++++++----- 11 files changed, 473 insertions(+), 40 deletions(-) create mode 100644 dao/dao.go create mode 100644 dao/user_dao.go diff --git a/.gitignore b/.gitignore index ee685b9..e2df9bf 100644 --- a/.gitignore +++ b/.gitignore @@ -13,4 +13,5 @@ # Dependency directories (remove the comment below to include it) # vendor/ -*.idea \ No newline at end of file +*.idea +*.log \ No newline at end of file diff --git a/controllers/controller.go b/controllers/controller.go index a50ec26..527e32e 100644 --- a/controllers/controller.go +++ b/controllers/controller.go @@ -1,7 +1,93 @@ package controllers -import "git.rosy.net.cn/jx-print/globals" +import ( + "encoding/json" + "fmt" + "git.rosy.net.cn/jx-print/globals" + "git.rosy.net.cn/jx-print/model" + "github.com/dchest/captcha" + "github.com/gin-contrib/sessions" + "github.com/gin-gonic/gin" + "net/http" + "strings" +) -func init() { +type CallBack struct { + Data string `json:"data"` + Code string `json:"code"` + Desc string `json:"desc"` +} + +func init() { globals.SugarLogger.Debugf("test init ...") } + +func callFunc(c *gin.Context, worker func() (retVal interface{}, errCode string, err error)) bool { + var ( + err error + callBack = &CallBack{ + Code: model.ErrCodeNormal, + } + token string + cookie *http.Cookie + result interface{} + ) + //验证token + //v2下的接口需要验证,v1不需要 + if !strings.Contains(c.Request.URL.String(), "v1") { + if cookie, err = c.Request.Cookie("token"); err != nil { + callBack.Desc = err.Error() + c.JSON(http.StatusOK, callBack) + return false + } else { + token = cookie.Value + } + if token != "token" { + err = fmt.Errorf("token 已过期,请重新登录!") + callBack.Desc = err.Error() + c.JSON(http.StatusOK, callBack) + return false + } + } + if result, _, err = worker(); err == nil { + if result != nil { + if data, err := json.Marshal(&result); err == nil { + callBack.Code = model.ErrCodeSuccess + callBack.Data = string(data) + c.JSON(http.StatusOK, callBack) + globals.SugarLogger.Debugf("End API :%s success", c.Request.URL) + return true + } else { + err = fmt.Errorf("data error!") + callBack.Desc = err.Error() + c.JSON(http.StatusOK, callBack) + return false + } + } else { + callBack.Code = model.ErrCodeSuccess + c.JSON(http.StatusOK, callBack) + globals.SugarLogger.Debugf("End API :%s success", c.Request.URL) + return true + } + } else { + callBack.Desc = err.Error() + c.JSON(http.StatusOK, callBack) + globals.SugarLogger.Debugf("End API :%s error:%v", c.Request.URL, err) + } + return false +} + +func captchaVerify(c *gin.Context, code string) bool { + session := sessions.Default(c) + if captchaId := session.Get(c.ClientIP() + model.SessionKey); captchaId != nil { + session.Delete(c.ClientIP() + model.SessionKey) + _ = session.Save() + if captcha.VerifyString(captchaId.(string), code) { + return true + } else { + return false + } + } else { + return false + } +} diff --git a/controllers/user_controller.go b/controllers/user_controller.go index 4a27dcc..b49275e 100644 --- a/controllers/user_controller.go +++ b/controllers/user_controller.go @@ -1,10 +1,120 @@ package controllers import ( + "git.rosy.net.cn/jx-print/globals" + "git.rosy.net.cn/jx-print/model" + "git.rosy.net.cn/jx-print/services" "git.rosy.net.cn/jx-print/utils" "github.com/gin-gonic/gin" + "net/http" ) -func RefreshCode(c *gin.Context){ - utils.Captcha(c,4) +//刷新验证码 POST +func RefreshCode(c *gin.Context) { + utils.Captcha(c, 4) +} + +type GetUsersParam struct { + Name string `json:"name" uri:"name"` //用户名 + UserID string `json:"user_id" uri:"user_id"` //用户名 + Mobile string `json:"mobile" uri:"mobile"` //用户名 +} + +//查询用户 GET +func GetUsers(c *gin.Context) { + var ( + err error + user = &GetUsersParam{} + ) + globals.SugarLogger.Debugf("Begin API :%s params: %v ip: %s", c.Request.URL, c.Params, c.ClientIP()) + if err = c.ShouldBindUri(&user); err != nil { + c.JSON(http.StatusOK, &CallBack{ + Code: model.ErrCodeNormal, + Desc: err.Error(), + }) + return + } + if !callFunc(c, func() (retVal interface{}, errCode string, err error) { + retVal, err = services.GetUsers(c, user.UserID, user.Name, user.Mobile) + return retVal, "", err + }) { + return + } + return +} + +type RegisterUserParam 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 RegisterUser(c *gin.Context) { + var ( + err error + user = &RegisterUserParam{} + ) + 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 !captchaVerify(c, user.Code) { + c.JSON(http.StatusOK, &CallBack{ + Code: model.ErrCodeNormal, + Desc: "验证码错误!", + }) + 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.RegisterUser(c, user.Name, user.Password) + return retVal, "", err + }) { + return + } + 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{} + ) + 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 !captchaVerify(c, user.Code) { + c.JSON(http.StatusOK, &CallBack{ + Code: model.ErrCodeNormal, + Desc: "验证码错误!", + }) + 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.Login(c, user.Name, user.Password, user.Code) + return retVal, "", err + }) { + return + } + return } diff --git a/dao/dao.go b/dao/dao.go new file mode 100644 index 0000000..b7ccde4 --- /dev/null +++ b/dao/dao.go @@ -0,0 +1,52 @@ +package dao + +import ( + putils "git.rosy.net.cn/jx-print/utils" + "github.com/jmoiron/sqlx" + "reflect" + "strings" + + "time" +) + +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) + } else { + direct = value + } + sql.WriteString("INSERT INTO ") + for i := 0; i < stype.NumField()-1; i++ { + if stype.Field(i).Type.String() == "*time.Time" { + if direct.Field(i).Interface().(*time.Time) != nil { + values.WriteString(stype.Field(i).Tag.Get("json") + ",") + sqlParams = append(sqlParams, direct.Field(i).Interface()) + } + } else { + if !direct.Field(i).IsZero() { + values.WriteString(stype.Field(i).Tag.Get("json") + ",") + sqlParams = append(sqlParams, direct.Field(i).Interface()) + } + } + } + sql.WriteString(putils.UnMarshalHr(sname) + "(") + sql.WriteString(values.String()[:len(values.String())-1]) + sql.WriteString(") VALUES(") + for i := 0; i < len(strings.Split(values.String(), ","))-1; i++ { + if i != 0 { + sql.WriteString(",") + } + sql.WriteString("?") + } + sql.WriteString(")") + _, err = db.DB.Exec(sql.String(), sqlParams...) + return err +} diff --git a/dao/user_dao.go b/dao/user_dao.go new file mode 100644 index 0000000..dfed479 --- /dev/null +++ b/dao/user_dao.go @@ -0,0 +1,31 @@ +package dao + +import ( + "git.rosy.net.cn/jx-print/model" + "github.com/jmoiron/sqlx" +) + +func GetUsers(db *sqlx.DB, userID, name, mobile string) (users []*model.User, err error) { + var sqlParams []interface{} + sql := ` + SELECT * + FROM user + WHERE 1 = 1 + ` + if name != "" { + sql += " AND name LIKE ?" + sqlParams = append(sqlParams, "%"+name+"%") + } + if userID != "" { + sql += " AND user_id = ?" + sqlParams = append(sqlParams, userID) + } + if mobile != "" { + sql += " AND mobile = ?" + sqlParams = append(sqlParams, mobile) + } + if err = db.Select(&users, sql, sqlParams...); err == nil { + return users, err + } + return users, err +} diff --git a/globals/globals.go b/globals/globals.go index 5126fb4..1f4d523 100644 --- a/globals/globals.go +++ b/globals/globals.go @@ -1,23 +1,27 @@ package globals import ( - "database/sql" + _ "github.com/go-sql-driver/mysql" + "github.com/jmoiron/sqlx" "go.uber.org/zap" ) var ( SugarLogger *zap.SugaredLogger - db *sql.DB - ) + db *sqlx.DB + err error +) func init() { logger, _ := zap.NewDevelopment() SugarLogger = logger.Sugar() + sqlStr :="root:WebServer@1@tcp(127.0.0.1:3306)/api?charset=utf8mb4&loc=Local&parseTime=true" + db, err = sqlx.Connect("mysql", sqlStr) + if err != nil { + SugarLogger.Debugf("加载数据库失败!err :%v",err) + } +} - //parseTime:时间格式转换(查询结果为时间时,是否自动解析为时间); - - // loc=Local:MySQL的时区设置 - - sqlStr := "root:123456@tcp(127.0.0.1:3306)/testdb?charset=utf8&parseTime=true&loc=Local" - db, _ = sql.Open("mysql", sqlStr) +func GetDB()*sqlx.DB{ + return db } \ No newline at end of file diff --git a/model/model.go b/model/model.go index 6cbb397..784208e 100644 --- a/model/model.go +++ b/model/model.go @@ -1,15 +1,33 @@ package model -import "time" +import ( + "time" +) const ( - SessionKey = "jxCode" + SessionKey = "jxCode" + RegisterKey = "jxRegister" +) + +const ( + ErrCodeSuccess = "0" + ErrCodeNormal = "-1" + + ErrCodeToken = "-1000" +) + +const ( + FieldDeletedAt = "deleted_at" + FieldCreatedAt = "created_at" + FieldUpdatedAt = "updated_at" + FieldLastOperator = "last_operator" + FieldID = "id" ) type ModelIDCULD struct { - ID int `json:"id"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - LastOperator string `json:"last_operator"` - DeletedAt time.Time `json:"deleted_at"` + ID int `json:"id" db:"id"` + CreatedAt *time.Time `json:"created_at" db:"created_at"` + UpdatedAt *time.Time `json:"updated_at" db:"updated_at"` + LastOperator string `json:"last_operator" db:"last_operator"` + DeletedAt *time.Time `json:"deleted_at" db:"deleted_at"` } diff --git a/model/user.go b/model/user.go index 868de98..82ef290 100644 --- a/model/user.go +++ b/model/user.go @@ -1,2 +1,32 @@ package model +import ( + "time" +) + +type User struct { + ID int `json:"id" db:"id"` + CreatedAt *time.Time `json:"created_at" db:"created_at"` + UpdatedAt *time.Time `json:"updated_at" db:"updated_at"` + LastOperator string `json:"last_operator" db:"last_operator"` + DeletedAt *time.Time `json:"deleted_at" db:"deleted_at"` + UserID string `json:"user_id" db:"user_id"` // 内部唯一标识 + Password string `json:"password"` //密码 + Name string `json:"name"` // 外部显示标识(当前可以重复) + Mobile string `json:"mobile"` + Email string `json:"email"` + Avatar string `json:"avatar"` // 头像 + Status int8 `json:"status"` + Type int8 `json:"type"` // 用户类型 + Company string `json:"company"` //公司名称 + CityCode int `json:"city_code" db:"city_code"` + DistrictCode int `json:"district_code" db:"district_code"` + Address string `json:"address"` + + IDCardNo string `json:"id_card_no" db:"id_card_no"` // 身份证号 + Remark string `json:"remark"` + + LastLoginAt *time.Time `json:"last_login_at" db:"last_login_at"` + LastLoginIP string `json:"last_login_ip" db:"last_login_ip"` + LastLoginType string `json:"last_login_type" db:"last_login_type"` +} diff --git a/routers/router.go b/routers/router.go index cb780b6..7514e34 100644 --- a/routers/router.go +++ b/routers/router.go @@ -5,9 +5,16 @@ import ( "github.com/gin-gonic/gin" ) -func Init(r *gin.Engine){ +func Init(r *gin.Engine) { v2 := r.Group("/v2") //user user := v2.Group("/user") - user.POST("/refreshCode",controllers.RefreshCode) -} \ No newline at end of file + user.GET("/getUsers", controllers.GetUsers) + + //v1是不需要token的 + v1 := r.Group("v1") + userw := v1.Group("/user") + user.GET("/login", controllers.Login) + userw.GET("/refreshCode", controllers.RefreshCode) + userw.GET("/register", controllers.RegisterUser) +} diff --git a/services/user.go b/services/user.go index ec025b1..e048a8a 100644 --- a/services/user.go +++ b/services/user.go @@ -1,2 +1,43 @@ package services +import ( + "crypto/md5" + "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" + "github.com/gin-gonic/gin" + "time" +) + +func GetUsers(c *gin.Context, name, mobile, userID string) (users []*model.User, err error) { + return dao.GetUsers(globals.GetDB(), userID, name, mobile) +} + +func RegisterUser(c *gin.Context, name, password string) (err error) { + var ( + db = globals.GetDB() + user = model.User{} + now = time.Now() + ) + if users, _ := dao.GetUsers(db, "", name, ""); len(users) > 0 { + return fmt.Errorf("用户名重复!") + } + user.UserID = utils.GetUUID() + user.Name = name + user.Password = fmt.Sprintf("%x", md5.Sum([]byte(model.RegisterKey+password))) + user.CreatedAt = &now + user.UpdatedAt = &now + user.DeletedAt = &utils.DefaultTimeValue + err = dao.Insert(db, user) + return err +} + +func Login(c *gin.Context, name, password, code string) (user *model.User, err error) { + //var ( + // db = globals.GetDB() + //) + + return user, err +} diff --git a/utils/utils.go b/utils/utils.go index d4a217a..667e8ea 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -7,9 +7,25 @@ import ( "github.com/gin-contrib/sessions" "github.com/gin-gonic/gin" "net/http" + "strings" "time" ) +var commonInitialisms = []string{"ACL", "API", "ASCII", "CPU", "CSS", "DNS", "EOF", "GUID", "HTML", "HTTP", "HTTPS", "ID", "IP", "JSON", "LHS", "QPS", "RAM", "RHS", "RPC", "SLA", "SMTP", "SQL", "SSH", "TCP", "TLS", "TTL", "UDP", "UI", "UID", "UUID", "URI", "URL", "UTF8", "VM", "XML", "XMPP", "XSRF", "XSS"} +var commonInitialismsReplacer *strings.Replacer +var uncommonInitialismsReplacer *strings.Replacer + +func init() { + var commonInitialismsForReplacer []string + var uncommonInitialismsForReplacer []string + for _, initialism := range commonInitialisms { + commonInitialismsForReplacer = append(commonInitialismsForReplacer, initialism, strings.Title(strings.ToLower(initialism))) + uncommonInitialismsForReplacer = append(uncommonInitialismsForReplacer, strings.Title(strings.ToLower(initialism)), initialism) + } + commonInitialismsReplacer = strings.NewReplacer(commonInitialismsForReplacer...) + uncommonInitialismsReplacer = strings.NewReplacer(uncommonInitialismsForReplacer...) +} + func Captcha(c *gin.Context, length ...int) { l := captcha.DefaultLen w, h := 107, 36 @@ -24,25 +40,11 @@ func Captcha(c *gin.Context, length ...int) { } captchaId := captcha.NewLen(l) session := sessions.Default(c) - session.Set(model.SessionKey, captchaId) + session.Set(c.ClientIP()+model.SessionKey, captchaId) _ = session.Save() _ = Serve(c.Writer, c.Request, captchaId, ".png", "zh", false, w, h) } -func CaptchaVerify(c *gin.Context, code string) bool { - session := sessions.Default(c) - if captchaId := session.Get(model.SessionKey); captchaId != nil { - session.Delete(model.SessionKey) - _ = session.Save() - if captcha.VerifyString(captchaId.(string), code) { - return true - } else { - return false - } - } else { - return false - } -} func Serve(w http.ResponseWriter, r *http.Request, id, ext, lang string, download bool, width, height int) error { w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate") w.Header().Set("Pragma", "no-cache") @@ -65,4 +67,55 @@ func Serve(w http.ResponseWriter, r *http.Request, id, ext, lang string, downloa } http.ServeContent(w, r, id+ext, time.Time{}, bytes.NewReader(content.Bytes())) return nil -} \ No newline at end of file +} + +//驼峰转下划线 +func UnMarshalHr(name string) string { + const ( + lower = false + upper = true + ) + + if name == "" { + return "" + } + + var ( + value = commonInitialismsReplacer.Replace(name) + buf = bytes.NewBufferString("") + lastCase, currCase, nextCase, nextNumber bool + ) + + for i, v := range value[:len(value)-1] { + nextCase = bool(value[i+1] >= 'A' && value[i+1] <= 'Z') + nextNumber = bool(value[i+1] >= '0' && value[i+1] <= '9') + + if i > 0 { + if currCase == upper { + if lastCase == upper && (nextCase == upper || nextNumber == upper) { + buf.WriteRune(v) + } else { + if value[i-1] != '_' && value[i+1] != '_' { + buf.WriteRune('_') + } + buf.WriteRune(v) + } + } else { + buf.WriteRune(v) + if i == len(value)-2 && (nextCase == upper && nextNumber == lower) { + buf.WriteRune('_') + } + } + } else { + currCase = upper + buf.WriteRune(v) + } + lastCase = currCase + currCase = nextCase + } + + buf.WriteByte(value[len(value)-1]) + + s := strings.ToLower(buf.String()) + return s +}