This commit is contained in:
suyl
2021-07-07 10:21:51 +08:00
parent de7ca15b3d
commit 2beca9fbcb
11 changed files with 473 additions and 40 deletions

3
.gitignore vendored
View File

@@ -13,4 +13,5 @@
# Dependency directories (remove the comment below to include it)
# vendor/
*.idea
*.idea
*.log

View File

@@ -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
}
}

View File

@@ -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
}

52
dao/dao.go Normal file
View File

@@ -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
}

31
dao/user_dao.go Normal file
View File

@@ -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
}

View File

@@ -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=LocalMySQL的时区设置
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
}

View File

@@ -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"`
}

View File

@@ -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"`
}

View File

@@ -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)
}
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)
}

View File

@@ -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
}

View File

@@ -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
}
}
//驼峰转下划线
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
}