| | |
| | | DictTypeColor //颜色 |
| | | DictTypeSpec //规格 |
| | | ) |
| | | |
| | | type UserType int |
| | | |
| | | const ( |
| | | UserTypeSuper UserType = iota + 1 // 超级管理员 |
| | | UserTypePrimary // 主账户 |
| | | UserTypeSub // 子账户 |
| | | ) |
New file |
| | |
| | | package controllers |
| | | |
| | | import ( |
| | | "github.com/gin-gonic/gin" |
| | | "silkserver/controllers/request" |
| | | "silkserver/extend/code" |
| | | "silkserver/extend/util" |
| | | "silkserver/models" |
| | | ) |
| | | |
| | | type RawSilkStandardController struct { |
| | | } |
| | | |
| | | // SavePriceStandard |
| | | // |
| | | // @Tags 系统设置/生丝定价标准 |
| | | // @Summary 保存生丝定价标准 |
| | | // @Produce application/json |
| | | // @Param object body models.RawSilkPriceStandard true "参数" |
| | | // @Success 200 {object} util.Response "成功" |
| | | // @Router /api-jl/v1/system/savePriceStandard [post] |
| | | func (slf RawSilkStandardController) SavePriceStandard(c *gin.Context) { |
| | | var priceStandard models.RawSilkPriceStandard |
| | | err := c.BindJSON(&priceStandard) |
| | | if err != nil { |
| | | util.ResponseFormat(c, code.RequestParamError, "参数解析失败,数据类型错误") |
| | | return |
| | | } |
| | | if priceStandard.ID > 0 { |
| | | err = models.NewRawSilkPriceStandardSearch().Save(&priceStandard) |
| | | } else { |
| | | err = models.NewRawSilkPriceStandardSearch().Create(&priceStandard) |
| | | } |
| | | if err != nil { |
| | | util.ResponseFormat(c, code.SaveFail, "保存失败") |
| | | return |
| | | } |
| | | util.ResponseFormat(c, code.Success, "保存成功") |
| | | } |
| | | |
| | | // GetPriceStandardList |
| | | // |
| | | // @Tags 系统设置/生丝定价标准 |
| | | // @Summary 获取生丝定价标准 |
| | | // @Produce application/json |
| | | // @Param object body models.RawSilkPriceStandard true "参数" |
| | | // @Success 200 {object} util.Response "成功" |
| | | // @Router /api-jl/v1/system/getPriceStandardList [get] |
| | | func (slf RawSilkStandardController) GetPriceStandardList(c *gin.Context) { |
| | | var param request.GetPriceStandard |
| | | err := c.BindJSON(¶m) |
| | | if err != nil { |
| | | util.ResponseFormat(c, code.RequestParamError, "参数解析失败,数据类型错误") |
| | | return |
| | | } |
| | | list, total, err := models.NewRawSilkPriceStandardSearch().SetPage(param.Page, param.PageSize).Find() |
| | | if err != nil { |
| | | util.ResponseFormat(c, code.SelectError, "查询失败") |
| | | return |
| | | } |
| | | util.ResponseFormatList(c, code.Success, list, int(total)) |
| | | } |
New file |
| | |
| | | package request |
| | | |
| | | type GetPriceStandard struct { |
| | | PageInfo |
| | | KeyWord string `json:"keyWord"` |
| | | } |
| | |
| | | NoTemplateError = &Code{3010, "未配置订单模板"} |
| | | InternalError = &Code{3011, "内部错误"} |
| | | NoProductionRequiredError = &Code{3012, "当前库存满足毛需求量,暂时无需进行生产"} |
| | | SelectError = &Code{3013, "查询失败"} |
| | | ) |
| | |
| | | basic.com/aps/nsqclient.git v0.0.0-20230517072415-37491f4a5d25 |
| | | github.com/dgrijalva/jwt-go v3.2.0+incompatible |
| | | github.com/gin-gonic/gin v1.9.1 |
| | | github.com/golang-jwt/jwt/v4 v4.5.0 |
| | | github.com/nsqio/go-nsq v1.1.0 |
| | | github.com/shopspring/decimal v1.3.1 |
| | | github.com/spf13/viper v1.18.2 |
| | | github.com/swaggo/files v1.0.1 |
| | | github.com/swaggo/gin-swagger v1.6.0 |
| | |
| | | github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= |
| | | github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= |
| | | github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= |
| | | github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= |
| | | github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= |
| | | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= |
| | | github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= |
| | | github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= |
| | |
| | | github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= |
| | | github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= |
| | | github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= |
| | | github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= |
| | | github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= |
| | | github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= |
| | | github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= |
| | | github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= |
| | |
| | | package middleware |
| | | |
| | | import ( |
| | | "errors" |
| | | "fmt" |
| | | "silkserver/conf" |
| | | "strings" |
| | | "time" |
| | | |
| | | jwt "github.com/dgrijalva/jwt-go" |
| | | "github.com/gin-gonic/gin" |
| | | "silkserver/extend/util" |
| | | "silkserver/pkg/contextx" |
| | | "silkserver/pkg/ecode" |
| | | "strings" |
| | | ) |
| | | |
| | | func validateToken(tokenString string) (util.JSON, error) { |
| | | secretKey := []byte(conf.WebConf.JWTSecret) |
| | | |
| | | token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) { |
| | | // Don't forget to validate the alg is what you expect: |
| | | if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { |
| | | return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"]) |
| | | } |
| | | |
| | | return secretKey, nil |
| | | }) |
| | | |
| | | if err != nil { |
| | | return util.JSON{}, err |
| | | } |
| | | |
| | | if !token.Valid { |
| | | return util.JSON{}, errors.New("invalid token") |
| | | } |
| | | |
| | | return token.Claims.(jwt.MapClaims), nil |
| | | } |
| | | |
| | | // JWTMiddleware parses JWT token from cookie and stores data and expires date to the context |
| | | // JWT Token can be passed as cookie, or Authorization header |
| | | func JWTMiddleware() gin.HandlerFunc { |
| | | func JWTAuth() gin.HandlerFunc { |
| | | return func(c *gin.Context) { |
| | | tokenString, err := c.Cookie("token") |
| | | // failed to read cookie |
| | | if err != nil { |
| | | // try reading HTTP Header |
| | | authorization := c.Request.Header.Get("Authorization") |
| | | if authorization == "" { |
| | | c.Next() |
| | | return |
| | | } |
| | | sp := strings.Split(authorization, "Bearer ") |
| | | // invalid token |
| | | if len(sp) < 1 { |
| | | c.Next() |
| | | return |
| | | } |
| | | tokenString = sp[1] |
| | | ctx := new(contextx.Context).SetCtx(c) |
| | | // 我们这里jwt鉴权取头部信息 Authorization 登录时回返回token信息 这里前端需要把token存储到cookie或者本地localStorage中 不过需要跟后端协商过期时间 可以约定刷新令牌或者重新登录 |
| | | token := c.Request.Header.Get("Authorization") |
| | | if token == "" { |
| | | ctx.Fail(ecode.JWTEmpty) |
| | | c.Abort() |
| | | return |
| | | } |
| | | |
| | | tokenData, err := validateToken(tokenString) |
| | | slices := strings.Split(token, " ") |
| | | if len(slices) == 2 { |
| | | token = slices[1] |
| | | } |
| | | j := NewJWT() |
| | | // parseToken 解析token包含的信息 |
| | | claims, err := j.ParseToken(token) |
| | | if err != nil { |
| | | fmt.Println(err.Error()) |
| | | if err == TokenExpired { |
| | | c.Next() |
| | | return |
| | | } |
| | | c.Next() |
| | | return |
| | | } |
| | | |
| | | userParentId := tokenData["parentId"].(string) |
| | | if userParentId == conf.WebConf.NodeId { |
| | | c.Set("parentId", userParentId) |
| | | } else { |
| | | c.Next() |
| | | return |
| | | } |
| | | |
| | | c.Set("token_expire", tokenData["exp"]) |
| | | c.Set("claims", claims) |
| | | c.Next() |
| | | } |
| | | } |
| | | |
| | | func GenerateToken(data interface{}) (string, error) { |
| | | // token is valid for 1 hour |
| | | date := time.Now().Add(time.Hour * 12) |
| | | |
| | | token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{ |
| | | "user": data, |
| | | "exp": date.Unix(), |
| | | }) |
| | | |
| | | secretKey := []byte(conf.WebConf.JWTSecret) |
| | | |
| | | tokenString, err := token.SignedString(secretKey) |
| | | |
| | | return tokenString, err |
| | | } |
New file |
| | |
| | | package middleware |
| | | |
| | | import ( |
| | | "github.com/dgrijalva/jwt-go" |
| | | "silkserver/constvar" |
| | | ) |
| | | |
| | | // Custom claims structure |
| | | type CustomClaims struct { |
| | | BaseClaims |
| | | BufferTime int64 |
| | | jwt.StandardClaims |
| | | } |
| | | |
| | | type BaseClaims struct { |
| | | UserId string |
| | | Username string |
| | | ParentId string |
| | | UserType constvar.UserType |
| | | ModifiedPwd *bool |
| | | } |
New file |
| | |
| | | package middleware |
| | | |
| | | import ( |
| | | "github.com/gin-gonic/gin" |
| | | "silkserver/pkg/contextx" |
| | | "silkserver/pkg/ecode" |
| | | ) |
| | | |
| | | func VerifyResetPwd() gin.HandlerFunc { |
| | | return func(c *gin.Context) { |
| | | ctx := new(contextx.Context).SetCtx(c) |
| | | params, ok := c.Get("claims") |
| | | if !ok { |
| | | c.Abort() |
| | | return |
| | | } |
| | | claims := params.(*CustomClaims) |
| | | if claims.ModifiedPwd == nil { //兼容没有ModifiedPwd值的token |
| | | ctx.Fail(ecode.JWTExpire) |
| | | c.Abort() |
| | | return |
| | | } |
| | | |
| | | if !*claims.ModifiedPwd { |
| | | ctx.Fail(ecode.ResetPwd) |
| | | c.Abort() |
| | | return |
| | | } |
| | | } |
| | | } |
New file |
| | |
| | | package middleware |
| | | |
| | | import ( |
| | | "errors" |
| | | "github.com/gin-gonic/gin" |
| | | "github.com/golang-jwt/jwt/v4" |
| | | ) |
| | | |
| | | type JWT struct { |
| | | SigningKey []byte |
| | | } |
| | | |
| | | var ( |
| | | TokenExpired = errors.New("Token is expired") |
| | | TokenNotValidYet = errors.New("Token not active yet") |
| | | TokenMalformed = errors.New("That's not even a token") |
| | | TokenInvalid = errors.New("Couldn't handle this token:") |
| | | ) |
| | | |
| | | func NewJWT() *JWT { |
| | | return &JWT{ |
| | | []byte("327a9457-899a-481e-8b30-58cc97e5b808"), |
| | | } |
| | | } |
| | | |
| | | // CreateToken 创建一个token |
| | | func (j *JWT) CreateToken(claims CustomClaims) (string, error) { |
| | | token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) |
| | | return token.SignedString(j.SigningKey) |
| | | } |
| | | |
| | | // ParseToken 解析token |
| | | func (j *JWT) ParseToken(tokenString string) (*CustomClaims, error) { |
| | | token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(token *jwt.Token) (i interface{}, e error) { |
| | | return j.SigningKey, nil |
| | | }) |
| | | if err != nil { |
| | | if ve, ok := err.(*jwt.ValidationError); ok { |
| | | if ve.Errors&jwt.ValidationErrorMalformed != 0 { |
| | | return nil, TokenMalformed |
| | | } else if ve.Errors&jwt.ValidationErrorExpired != 0 { |
| | | // Token is expired |
| | | return nil, TokenExpired |
| | | } else if ve.Errors&jwt.ValidationErrorNotValidYet != 0 { |
| | | return nil, TokenNotValidYet |
| | | } else { |
| | | return nil, TokenInvalid |
| | | } |
| | | } |
| | | } |
| | | if token != nil { |
| | | if claims, ok := token.Claims.(*CustomClaims); ok && token.Valid { |
| | | return claims, nil |
| | | } |
| | | return nil, TokenInvalid |
| | | |
| | | } else { |
| | | return nil, TokenInvalid |
| | | } |
| | | } |
| | | |
| | | func GetUserInfo(c *gin.Context) *CustomClaims { |
| | | if claims, exists := c.Get("claims"); exists { |
| | | waitUse := claims.(*CustomClaims) |
| | | return waitUse |
| | | } |
| | | return nil |
| | | } |
New file |
| | |
| | | package models |
| | | |
| | | import ( |
| | | "fmt" |
| | | "github.com/shopspring/decimal" |
| | | "gorm.io/gorm" |
| | | "silkserver/pkg/mysqlx" |
| | | ) |
| | | |
| | | // RawSilkPriceStandard |
| | | type ( |
| | | RawSilkPriceStandard struct { |
| | | gorm.Model |
| | | MarketNumber string `json:"marketNumber" gorm:"type:varchar(255);comment:庄口编号"` |
| | | RawSilkGradeNumber string `json:"rawSilkGradeNumber" gorm:"type:varchar(255);comment:生丝等级编号"` |
| | | PayStandard decimal.Decimal `json:"payStandard" gorm:"type:decimal(20,3);comment:薪酬标准"` |
| | | Unit string `json:"unit" gorm:"type:varchar(100);comment:单位"` |
| | | Notes string `json:"notes" gorm:"type:varchar(255);comment:备注"` |
| | | } |
| | | RawSilkPriceStandardSearch struct { |
| | | RawSilkPriceStandard |
| | | Order string |
| | | PageNum int |
| | | PageSize int |
| | | Orm *gorm.DB |
| | | } |
| | | ) |
| | | |
| | | func (slf RawSilkPriceStandard) TableName() string { |
| | | return "raw_silk_price_standard" |
| | | } |
| | | |
| | | func NewRawSilkPriceStandardSearch() *RawSilkPriceStandardSearch { |
| | | return &RawSilkPriceStandardSearch{Orm: mysqlx.GetDB()} |
| | | } |
| | | |
| | | func (slf *RawSilkPriceStandardSearch) SetOrm(tx *gorm.DB) *RawSilkPriceStandardSearch { |
| | | slf.Orm = tx |
| | | return slf |
| | | } |
| | | |
| | | func (slf *RawSilkPriceStandardSearch) SetPage(page, size int) *RawSilkPriceStandardSearch { |
| | | slf.PageNum, slf.PageSize = page, size |
| | | return slf |
| | | } |
| | | |
| | | func (slf *RawSilkPriceStandardSearch) SetOrder(order string) *RawSilkPriceStandardSearch { |
| | | slf.Order = order |
| | | return slf |
| | | } |
| | | |
| | | func (slf *RawSilkPriceStandardSearch) build() *gorm.DB { |
| | | db := slf.Orm.Table(slf.TableName()) |
| | | |
| | | return db |
| | | } |
| | | |
| | | // Create 单条插入 |
| | | func (slf *RawSilkPriceStandardSearch) Create(record *RawSilkPriceStandard) error { |
| | | db := slf.build() |
| | | err := db.Create(record).Error |
| | | if err != nil { |
| | | return fmt.Errorf("create err: %v, record: %+v", err, record) |
| | | } |
| | | return nil |
| | | } |
| | | |
| | | func (slf *RawSilkPriceStandardSearch) Find() ([]*RawSilkPriceStandard, int64, error) { |
| | | var ( |
| | | records = make([]*RawSilkPriceStandard, 0) |
| | | total int64 |
| | | db = slf.build() |
| | | ) |
| | | |
| | | if err := db.Count(&total).Error; err != nil { |
| | | return records, total, fmt.Errorf("find count err: %v", err) |
| | | } |
| | | if slf.PageNum*slf.PageSize > 0 { |
| | | db = db.Offset((slf.PageNum - 1) * slf.PageSize).Limit(slf.PageSize) |
| | | } |
| | | if err := db.Find(&records).Error; err != nil { |
| | | return records, total, fmt.Errorf("find records err: %v", err) |
| | | } |
| | | |
| | | return records, total, nil |
| | | } |
| | | |
| | | func (slf *RawSilkPriceStandardSearch) Save(record *RawSilkPriceStandard) error { |
| | | var db = slf.build() |
| | | |
| | | if err := db.Save(record).Error; err != nil { |
| | | return fmt.Errorf("save err: %v, record: %+v", err, record) |
| | | } |
| | | |
| | | return nil |
| | | } |
| | |
| | | JWTEmpty = 2013 // JWT为空 |
| | | JWTExpire = 2014 // JWT过期 |
| | | JWTParseErr = 2015 // JWT解析失败 |
| | | ResetPwd = 2036 //账号设置密码 |
| | | ) |
| | |
| | | |
| | | r.StaticFS(conf.LocalConf.StorePath, http.Dir(conf.LocalConf.StorePath)) // 为用户头像和文件提供静态地址 |
| | | r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) |
| | | r.Use(middleware.JWTAuth()) |
| | | r.Use(middleware.VerifyResetPwd()) |
| | | |
| | | urlPrefix := "/api-jl/v1" |
| | | |