Browse Source

update

master
dengbiao 1 month ago
parent
commit
7538b672d3
38 changed files with 640 additions and 5687 deletions
  1. +17
    -4
      README.md
  2. +30
    -33
      app/cfg/cfg_app.go
  3. +13
    -22
      app/cfg/init_cfg.go
  4. +28
    -0
      app/cfg/init_rabbitmq.go
  5. +0
    -3
      app/cfg/init_task.go
  6. +0
    -104
      app/db/dbs.go
  7. +0
    -194
      app/db/dbs_map.go
  8. +10
    -0
      app/hdl/hdl_demo.go
  9. +39
    -0
      app/lib/auth/auth.go
  10. +4
    -8
      app/lib/auth/base.go
  11. +1
    -3
      app/md/app_redis_key.go
  12. +7
    -0
      app/md/md_api_response.go
  13. +0
    -42
      app/md/split_db.go
  14. +20
    -61
      app/mw/mw_auth.go
  15. +0
    -96
      app/mw/mw_auth_jwt.go
  16. +0
    -30
      app/mw/mw_breaker.go
  17. +0
    -17
      app/mw/mw_change_header.go
  18. +0
    -34
      app/mw/mw_check_sign.go
  19. +0
    -22
      app/mw/mw_checker.go
  20. +0
    -93
      app/mw/mw_db.go
  21. +8
    -28
      app/router/router.go
  22. +55
    -0
      app/svc/svc_auth.go
  23. +0
    -11
      app/svc/svc_db.go
  24. +0
    -85
      app/svc/svc_redis_mutex_lock.go
  25. +0
    -175
      app/svc/svc_sys_cfg_get.go
  26. +8
    -4
      app/svc/svc_validate_common.go
  27. +0
    -49
      app/utils/aes.go
  28. +0
    -46
      app/utils/auth.go
  29. +49
    -1646
      docs/docs.go
  30. +33
    -1592
      docs/swagger.json
  31. +27
    -1052
      docs/swagger.yaml
  32. +80
    -31
      go.mod
  33. +90
    -0
      k8s/container.yaml
  34. +90
    -0
      k8s/container_prd.yaml
  35. +0
    -56
      k8s/mall-task-prd.yaml
  36. +0
    -56
      k8s/mall-task.yaml
  37. +0
    -49
      k8s/zyos-mall-deployment_prd.yaml
  38. +31
    -41
      main.go

+ 17
- 4
README.md View File

@@ -12,7 +12,7 @@
- md 结构体

#### 介绍
基于gin的接口小程序
蛋蛋星球后台管理项目

#### 软件架构

@@ -39,10 +39,23 @@

## swagger


```
// 参考:https://segmentfault.com/a/1190000013808421
// 安装命令行
> 参考:https://segmentfault.com/a/1190000013808421

> swagger 命令行工具
go get -u github.com/swaggo/swag/cmd/swag
// 生成

//go版本1.16之前使用该命令
go install github.com/swaggo/swag/cmd/swag
//go版本1.16版本以及之后的版本使用该命令
go install github.com/swaggo/swag/cmd/swag@latest

> swagger 依赖
go get "github.com/swaggo/files"
go get "github.com/swaggo/gin-swagger"


> 生成
swag init
```

+ 30
- 33
app/cfg/cfg_app.go View File

@@ -5,51 +5,41 @@ import (
)

type Config struct {
Debug bool `yaml:"debug"`
Prd bool `yaml:"prd"`
CurlDebug bool `yaml:"curldebug"`
SrvAddr string `yaml:"srv_addr"`
RedisAddr string `yaml:"redis_addr"`
DB DBCfg `yaml:"db"`
Log LogCfg `yaml:"log"`
ArkID ArkIDCfg `yaml:"arkid"`
Admin AdminCfg `yaml:"admin"`
Official OfficialCfg `yaml:"official"`
WxappletFilepath WxappletFilepathCfg `yaml:"wxapplet_filepath"`
Local bool
AppComm AppCommCfg `yaml:"app_comm"`
Debug bool `yaml:"debug"`
Prd bool `yaml:"prd"`
CurlDebug bool `yaml:"curldebug"`
SrvAddr string `yaml:"srv_addr"`
RedisAddr string `yaml:"redis_addr"`
RedisPassword string `yaml:"redis_password"`
DB DBCfg `yaml:"db"`
Log LogCfg `yaml:"log"`
ImBusinessRpc ImBusinessRpcCfg `yaml:"im_business_rpc"`
ImLogicRpc ImLogicRpcCfg `yaml:"im_logic_rpc"`
Local bool
MQ MQCfg `yaml:"mq"`
}

// 公共模块
type AppCommCfg struct {
URL string `yaml:"url"`
type ImBusinessRpcCfg struct {
URL string `yaml:"url"`
PORT string `yaml:"port"`
}

// OfficialCfg is 官网

type OfficialCfg struct {
URL string `yaml:"url"`
}
type WxappletFilepathCfg struct {
URL string `yaml:"url"`
type ImLogicRpcCfg struct {
URL string `yaml:"url"`
PORT string `yaml:"port"`
}

// AdminCfg is 后台接口调用需要
type AdminCfg struct {
URL string `yaml:"url"`
IURL string `yaml:"iurl"`
AesKey string `yaml:"api_aes_key"`
AesIV string `yaml:"api_aes_iv"`
Host string `yaml:"host"`
type AppCommCfg struct {
OuterURL string `yaml:"outer_url"` // 本模块外部地址
}

type ArkIDCfg struct {
Admin string `yaml:"admin"`
AdminPassword string `yaml:"admin_password"`
Url string `yaml:"url`
Url string `yaml:"url"`
}

//数据库配置结构体
// 数据库配置结构体
type DBCfg struct {
Host string `yaml:"host"` //ip及端口
Name string `yaml:"name"` //库名
@@ -62,7 +52,7 @@ type DBCfg struct {
Path string `yaml:"path"` //日志文件存放路径
}

//日志配置结构体
// 日志配置结构体
type LogCfg struct {
AppName string `yaml:"app_name" `
Level string `yaml:"level"`
@@ -76,3 +66,10 @@ type LogCfg struct {
FileMaxSize int `yaml:"file_max_size"`
FileMaxAge int `yaml:"file_max_age"`
}

type MQCfg struct {
Host string `yaml:"host"`
Port string `yaml:"port"`
User string `yaml:"user"`
Pwd string `yaml:"pwd"`
}

+ 13
- 22
app/cfg/init_cfg.go View File

@@ -7,24 +7,20 @@ import (
"gopkg.in/yaml.v2"
)

//配置文件数据,全局变量
// 配置文件数据,全局变量
var (
Debug bool
Prd bool
CurlDebug bool
SrvAddr string
RedisAddr string
DB *DBCfg
Log *LogCfg
ArkID *ArkIDCfg
Admin *AdminCfg
Official *OfficialCfg
WxappletFilepath *WxappletFilepathCfg
Local bool
AppComm *AppCommCfg
Debug bool
Prd bool
SrvAddr string
SmartCanteenPay string
RedisAddr string
RedisPassword string
DB *DBCfg
MQ *MQCfg
Log *LogCfg
)

//初始化配置文件,将cfg.yml读入到内存
// 初始化配置文件,将cfg.yml读入到内存
func InitCfg() {
//用指定的名称、默认值、使用信息注册一个string类型flag。
path := flag.String("c", "etc/cfg.yml", "config file")
@@ -46,15 +42,10 @@ func InitCfg() {
//数据读入内存
Prd = conf.Prd
Debug = conf.Debug
Local = conf.Local
CurlDebug = conf.CurlDebug
DB = &conf.DB
Log = &conf.Log
ArkID = &conf.ArkID
RedisAddr = conf.RedisAddr
RedisPassword = conf.RedisPassword
SrvAddr = conf.SrvAddr
Admin = &conf.Admin
Official = &conf.Official
WxappletFilepath = &conf.WxappletFilepath
AppComm = &conf.AppComm
MQ = &conf.MQ
}

+ 28
- 0
app/cfg/init_rabbitmq.go View File

@@ -0,0 +1,28 @@
package cfg

import (
"code.fnuoos.com/go_rely_warehouse/zyos_go_mq.git/rabbit"
"encoding/json"
"os"
"strings"
"time"
)

func InitMq() {
data, _ := json.Marshal(MQ)
filePutContents("init_rabbit_mq", string(data))
err := rabbit.Init(MQ.Host, MQ.Port, MQ.User, MQ.Pwd)
if err != nil {
filePutContents("init_rabbit_mq", err.Error())
return
}
}

func filePutContents(fileName string, content string) {
fd, _ := os.OpenFile("./tmp/"+fileName+".log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)
fd_time := time.Now().Format("2006-01-02 15:04:05")
fd_content := strings.Join([]string{"[", fd_time, "] ", content, "\n"}, "")
buf := []byte(fd_content)
fd.Write(buf)
fd.Close()
}

+ 0
- 3
app/cfg/init_task.go View File

@@ -28,10 +28,7 @@ func InitTaskCfg() {
Debug = conf.Debug
DB = &conf.DB
Log = &conf.Log
Admin = &conf.Admin
RedisAddr = conf.RedisAddr
Local = conf.Local
AppComm = &conf.AppComm
}

var MemCache mc.Cache


+ 0
- 104
app/db/dbs.go View File

@@ -1,104 +0,0 @@
package db

import (
"fmt"
"os"
"time"

"xorm.io/xorm"
"xorm.io/xorm/log"

"applet/app/cfg"
"applet/app/db/model"
"applet/app/utils/logx"
)

var DBs map[string]*xorm.Engine

// 每个站长都要有自己的syscfg 缓存, 键是站长id,值是缓存名
// var SysCfgMapKey map[string]string

func NewDB(c *cfg.DBCfg) (*xorm.Engine, error) {
db, err := xorm.NewEngine("mysql", fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8mb4", c.User, c.Psw, c.Host, c.Name))
if err != nil {
return nil, err
}
db.SetConnMaxLifetime(c.MaxLifetime * time.Second)
db.SetMaxOpenConns(c.MaxOpenConns)
db.SetMaxIdleConns(c.MaxIdleConns)
err = db.Ping()
if err != nil {
return nil, err
}
if c.ShowLog {
db.ShowSQL(true)
db.Logger().SetLevel(log.LOG_DEBUG)
f, err := os.OpenFile(c.Path, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0777)
if err != nil {
os.RemoveAll(c.Path)
if f, err = os.OpenFile(c.Path, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0777); err != nil {
return nil, err
}
}
logger := log.NewSimpleLogger(f)
logger.ShowSQL(true)
db.SetLogger(logger)
}
return db, nil
}

// InitDBs is 初始化多数据库
func InitDBs(ch chan int) {
// 初始化多数据库
var tables *[]model.DbMapping
InitMapDbs(cfg.DB, cfg.Prd)
ch <- 1
// 每10s 查询一次模板数据库的db mapping 表,如果有新增数据库记录,则添加到 DBs中
ticker := time.NewTicker(time.Duration(time.Second * 120))
for range ticker.C {
if cfg.Prd {
tables = GetAllDatabasePrd() //默认获取全部
} else {
tables = GetAllDatabaseDev() //默认获取全部
}
if tables == nil {
logx.Warn("no database tables data")
continue
}
for _, item := range *tables {
_, ok := DBs[item.DbMasterId]
if !ok {
// 不在db.DBs 则添加进去
dbCfg := cfg.DBCfg{
Name: item.DbName,
ShowLog: cfg.DB.ShowLog,
MaxLifetime: cfg.DB.MaxLifetime,
MaxOpenConns: cfg.DB.MaxOpenConns,
MaxIdleConns: cfg.DB.MaxIdleConns,
Path: fmt.Sprintf(cfg.DB.Path, item.DbName),
}
if item.DbHost != "" && item.DbUsername != "" && item.DbPassword != "" {
dbCfg.Host = item.DbHost
dbCfg.User = item.DbUsername
dbCfg.Psw = item.DbPassword
} else {
dbCfg.Host = cfg.DB.Host
dbCfg.User = cfg.DB.User
dbCfg.Psw = cfg.DB.Psw
}
e, err := NewDB(&dbCfg)
if err != nil || e == nil {
logx.Warnf("db engine can't create, please check config, params[host:%s, user:%s, psw: %s, name: %s], err: %v", dbCfg.Host, dbCfg.User, dbCfg.Psw, dbCfg.Name, err)
} else {
DBs[item.DbMasterId] = e
}
}
// 如果 被禁用则删除
if item.DeletedAt == 1 {
logx.Infof("%s have been removed", item.DbMasterId)
delete(DBs, item.DbMasterId)
}
}
}

}

+ 0
- 194
app/db/dbs_map.go View File

@@ -1,194 +0,0 @@
package db

import (
"errors"
"fmt"

"xorm.io/xorm"

"applet/app/cfg"
"applet/app/db/model"
"applet/app/utils/logx"
)

func MapBaseExists() (bool, error) {
return Db.IsTableExist("db_mapping")
}

func InitMapDbs(c *cfg.DBCfg, prd bool) {
var tables *[]model.DbMapping
exists, err := MapBaseExists()
if !exists || err != nil {
logx.Fatalf("db_mapping not exists : %v", err)
}
// tables := MapAllDatabases(debug)
if prd {
tables = GetAllDatabasePrd() //debug 获取生产
} else {
tables = GetAllDatabaseDev() //debug 获取开发
}

if tables == nil {
logx.Fatal("no database tables data")
}
var e *xorm.Engine
DBs = map[string]*xorm.Engine{}
for _, v := range *tables {
if v.DbName != "" && v.DeletedAt == 0 && v.DbName != c.Name {
dbCfg := cfg.DBCfg{
Name: v.DbName,
ShowLog: c.ShowLog,
MaxLifetime: c.MaxLifetime,
MaxOpenConns: c.MaxOpenConns,
MaxIdleConns: c.MaxIdleConns,
Path: fmt.Sprintf(c.Path, v.DbName),
}
if v.DbHost != "" && v.DbUsername != "" && v.DbPassword != "" {
dbCfg.Host = v.DbHost
dbCfg.User = v.DbUsername
dbCfg.Psw = v.DbPassword
} else {
dbCfg.Host = c.Host
dbCfg.User = c.User
dbCfg.Psw = c.Psw
}
e, err = NewDB(&dbCfg)
if err != nil || e == nil {
logx.Warnf("db engine can't create, please check config, params[host:%s, user:%s, psw: %s, name: %s], err: %v", dbCfg.Host, dbCfg.User, dbCfg.Psw, dbCfg.Name, err)
} else {
DBs[v.DbMasterId] = e
}
}
}
}

func MapAllDatabases(debug bool) *[]model.DbMapping {
sql := "`db_name` != ?"
if debug {
sql = "`db_name` = ?"
}
var m []model.DbMapping
if err := Db.Where(sql, cfg.DB.Name).Find(&m); err != nil || len(m) == 0 {
logx.Warn(err)
return nil
}
return &m
}

// GetAllDatabasePrd is 获取生成库 所有db 除了 deleted_at = 1 的
func GetAllDatabasePrd() *[]model.DbMapping {
var m []model.DbMapping
if err := Db.Where("deleted_at != ? AND is_dev = '0' ", 1).Find(&m); err != nil || len(m) == 0 {
logx.Warn(err)
return nil
}
return &m
}

// GetAllDatabaseDev is 获取开发库 所有db 除了 deleted_at = 1 的
func GetAllDatabaseDev() *[]model.DbMapping {
var m []model.DbMapping
var err error
fmt.Println("cfg.Local is: ", cfg.Local)
if cfg.Local { // 本地调试 加快速度
fmt.Println("notice:LOCAL TEST, only masterId:** 123456 ** available!")
err = Db.Where("deleted_at != ? AND is_dev = '1' AND db_master_id=?", 1, 123456).Find(&m)
} else {
err = Db.Where("deleted_at != ? AND is_dev = '1' ", 1).Find(&m)
}

//err := Db.Where("deleted_at != ? AND is_dev = '1' and db_master_id='123456'", 1).Find(&m)
if err != nil || len(m) == 0 {
logx.Warn(err)
return nil
}
return &m
}

//GetDatabaseByMasterID is 根据站长id 获取对应的的数据库信息
func GetDatabaseByMasterID(Db *xorm.Engine, id string) (*model.DbMapping, error) {
var m model.DbMapping
has, err := Db.Where("db_master_id=?", id).Get(&m)
if !has {
return nil, errors.New("Not Found DB data by " + id)
}
if err != nil {
return nil, err
}
if m.DbHost == "" {
m.DbHost = cfg.DB.Host
m.DbUsername = cfg.DB.User
m.DbPassword = cfg.DB.Psw
}
return &m, nil
}

//SessionGetDatabaseByMasterID is 根据站长id 获取对应的的数据库信息
func SessionGetDatabaseByMasterID(Db *xorm.Session, id string) (*model.DbMapping, error) {
var m model.DbMapping
has, err := Db.Where("db_master_id=?", id).Get(&m)
if !has {
return nil, errors.New("Not Found DB data by " + id)
}
if err != nil {
return nil, err
}
if m.DbHost == "" {
m.DbHost = cfg.DB.Host
m.DbName = cfg.DB.Name
m.DbUsername = cfg.DB.User
m.DbPassword = cfg.DB.Psw
}
return &m, nil
}

// 获取自动任务队列
func MapCrontabCfg(eg *xorm.Engine) *[]model.SysCfg {
var c []model.SysCfg
// 数据库查询如果有下划线会认为是一个任意字符
if err := eg.Where("`key` LIKE 'mall_cron\\_%' AND val != ''").Cols("`key`,`val`").Find(&c); err != nil || len(c) == 0 {
logx.Warn(err)
return nil
}
return &c
}

// 获取官方域名
func GetOfficialDomainInfoByType(Db *xorm.Engine, masterId, key string) (string, error) {
type SysCfg struct {
K string
V string
Memo string
}
var domainBase SysCfg

has, err := Db.Where("k=?", "domain_base").Get(&domainBase)
if err != nil {
return "", err
}
if has == false {
return "", errors.New("can not find key by : domain_base")
}

if key == "wap" {
return "h5." + masterId + "." + domainBase.V, nil
}

if key == "api" {
var apiDomain SysCfg
has, err = Db.Where("k=?", "domain_api_base").Get(&apiDomain)
if err != nil {
return "", err
}
if has == false {
return "", errors.New("can not find key by : domain_api_base")
}
return apiDomain.V, nil
}

if key == "admin" {
return "admin." + masterId + "." + domainBase.V, nil
}
// 默认返回H5的
return "h5." + masterId + "." + domainBase.V, nil
}

+ 10
- 0
app/hdl/hdl_demo.go View File

@@ -6,6 +6,16 @@ import (
"github.com/gin-gonic/gin"
)

// Demo
// @Summary Demo测试
// @Tags Demo
// @Description Demo样例测试
// @Accept json
// @Produce json
// @Param req body interface{} true "用户名密码"
// @Success 200 {object} map[string]interface{} "token"
// @Failure 400 {object} md.Response "具体错误"
// @Router /api/demo [post]
func Demo(c *gin.Context) {
var args interface{}
err := c.ShouldBindJSON(&args)


+ 39
- 0
app/lib/auth/auth.go View File

@@ -0,0 +1,39 @@
package auth

import (
"errors"
"github.com/dgrijalva/jwt-go"
"time"
)

// GenToken 生成JWT
func GenToken(admId int, username string) (string, error) {
// 创建一个我们自己的声明
c := JWTUser{
AdmId: admId,
Username: username,
StandardClaims: jwt.StandardClaims{
ExpiresAt: time.Now().Add(TokenExpireDuration).Unix(), // 过期时间
Issuer: "smart_canteen", // 签发人
},
}
// 使用指定的签名方法创建签名对象
token := jwt.NewWithClaims(jwt.SigningMethodHS256, c)
// 使用指定的secret签名并获得完整的编码后的字符串token
return token.SignedString(Secret)
}

// ParseToken 解析JWT
func ParseToken(tokenString string) (*JWTUser, error) {
// 解析token
token, err := jwt.ParseWithClaims(tokenString, &JWTUser{}, func(token *jwt.Token) (i interface{}, err error) {
return Secret, nil
})
if err != nil {
return nil, err
}
if claims, ok := token.Claims.(*JWTUser); ok && token.Valid { // 校验token
return claims, nil
}
return nil, errors.New("invalid token")
}

+ 4
- 8
app/lib/auth/base.go View File

@@ -7,17 +7,13 @@ import (
)

// TokenExpireDuration is jwt 过期时间
const TokenExpireDuration = time.Hour * 4380
const TokenExpireDuration = time.Hour * 24

var Secret = []byte("zyos")
var Secret = []byte("smart_canteen_admin")

// JWTUser 如果想要保存更多信息,都可以添加到这个结构体中
type JWTUser struct {
UID int `json:"uid"`
Username string `json:"username"`
Phone string `json:"phone"`
AppName string `json:"app_name"`
MiniOpenID string `json:"mini_open_id"` // 小程序的open_id
MiniSK string `json:"mini_session_key"` // 小程序的session_key
AdmId int `json:"adm_id"`
Username string `json:"username"`
jwt.StandardClaims
}

+ 1
- 3
app/md/app_redis_key.go View File

@@ -2,9 +2,7 @@ package md

// 缓存key统一管理, %s格式化为masterId
const (
AppCfgCacheKey = "%s:cfg_cache:%s" // 占位符: masterId, key的第一个字母

UserFinValidUpdateLock = "%s:user_fin_valid_update_lock:%s" // 用户余额更新锁(能拿到锁才能更新余额)
AppCfgCacheKey = "cfg_cache:%s" // 占位符: key的第一个字母

CfgCacheTime = 86400
)

+ 7
- 0
app/md/md_api_response.go View File

@@ -0,0 +1,7 @@
package md

type Response struct {
Code string `json:"code" example:"响应码"`
Data interface{} `json:"data" ` //内容
Msg string `json:"msg" example:"具体错误原因"`
}

+ 0
- 42
app/md/split_db.go View File

@@ -1,42 +0,0 @@
package md

import (
"regexp"

"xorm.io/xorm"
)

type DbInfo struct {
User string
Psw string
Name string
Host string
}

func SplitDbInfo(eg *xorm.Engine) *DbInfo {
if eg == nil {
return &DbInfo{
User: "nil",
Psw: "nil",
Host: "nil",
Name: "nil",
}
}
pattern := `(\w+):(.*)@tcp\(([\w\.\-\:\_]+)\)\/(\w+)`
reg := regexp.MustCompile(pattern).FindStringSubmatch(eg.DataSourceName())

if len(reg) < 5 {
return &DbInfo{
User: "unknown",
Psw: "unknown",
Host: "unknown",
Name: "unknown",
}
}
return &DbInfo{
User: reg[1],
Psw: reg[2],
Host: reg[3],
Name: reg[4],
}
}

+ 20
- 61
app/mw/mw_auth.go View File

@@ -1,72 +1,31 @@
package mw

import (
"errors"

"applet/app/db"
"applet/app/e"
"applet/app/lib/arkid"
"applet/app/md"
"applet/app/utils"

"applet/app/svc"
"errors"
"github.com/gin-gonic/gin"
)

// 检查权限, 签名等等
// Auth 检查权限, 签名等等
func Auth(c *gin.Context) {

for k, v := range c.Request.Header {
c.Set(k, v[0])
}
token, ok := c.Get("Token")
if !ok {
e.OutErr(c, e.ERR_UNAUTHORIZED, errors.New("没有找到token"))
return
}
if token == "" {
e.OutErr(c, e.ERR_UNAUTHORIZED, errors.New("token 不能为空"))
return
}
tokenStr := utils.AnyToString(token)
arkIdSdk := arkid.NewArkID()
var err error
signUser := &md.User{}
arkIdUser := new(arkid.ArkIDUser)
if err = arkIdSdk.SelectFunction("arkid_user_info").
WithArgs(arkid.RequestBody{Token: tokenStr}).
Result(arkIdUser); err != nil {
e.OutErr(c, e.ERR_TOKEN_AUTH, err) //token 不存在
return
}
if arkIdUser.Username == "" {
e.OutErr(c, e.ERR_UNAUTHORIZED, errors.New("Token error"))
return
}
if err = arkIdSdk.SelectFunction("arkid_login").
WithArgs(arkid.RequestBody{Username: arkIdUser.Username, Password: utils.Md5(arkIdUser.Username)}).
Result(arkIdUser); err != nil {
e.OutErr(c, e.ERR_TOKEN_AUTH, err)
return
}
signUser.Ark = arkIdUser
if signUser.Ark == nil {
e.OutErr(c, e.ERR_TOKEN_AUTH, errors.New("无效token"))
return
}
signUser.Info, err = db.UserFindByArkidUserName(db.DBs[c.GetString("mid")], arkIdUser.Username)
if err != nil {
e.OutErr(c, e.ERR_TOKEN_AUTH, err)
return
}
if signUser.Info == nil {
e.OutErr(c, e.ERR_TOKEN_AUTH, errors.New("无效token"))
return
}
signUser.Profile, err = db.UserProfileFindByArkID(db.DBs[c.GetString("mid")], utils.IntToStr(arkIdUser.UserID))
admin, err := svc.CheckUser(c)
if err != nil {
e.OutErr(c, e.ERR_TOKEN_AUTH, err)
return
}
c.Set("user", signUser)
var e1 e.E
switch {
case errors.As(err, &e1):
e.OutErr(c, e1.Code, e1.Error())
return
default:
e.OutErr(c, e.ERR_UNAUTHORIZED, err.Error())
return
}
}
if admin == nil {
e.OutErr(c, e.ERR_NOT_AUTH, "当前管理员信息失效")
return
}
// 将当前请求的username信息保存到请求的上下文c上
c.Set("admin", admin)
c.Next()
}

+ 0
- 96
app/mw/mw_auth_jwt.go View File

@@ -1,96 +0,0 @@
package mw

import (
"applet/app/db"
"applet/app/e"
"applet/app/md"
"applet/app/utils"
"applet/app/utils/cache"
"applet/app/utils/logx"
"errors"
"fmt"
"strings"

"github.com/gin-gonic/gin"
)

// AuthJWT is jwt middleware
func AuthJWT(c *gin.Context) {

authHeader := c.Request.Header.Get("Authorization")
if authHeader == "" {
e.OutErr(c, e.ERR_UNAUTHORIZED, errors.New("token 不能为空"))
return
}

// 按空格分割
parts := strings.SplitN(authHeader, " ", 2)
if !(len(parts) == 2 && parts[0] == "Bearer") {
e.OutErr(c, e.ERR_TOKEN_FORMAT, errors.New("token 格式不对"))
return
}
// parts[1]是token
mc, err := utils.ParseToken(parts[1])
if err != nil {
e.OutErr(c, e.ERR_UNAUTHORIZED, errors.New("token 过期或无效"))
return
}
//fmt.Println(mc.UID)
// 获取user
u, err := db.UserFindByID(db.DBs[c.GetString("mid")], mc.UID)
if err != nil {
e.OutErr(c, e.ERR_DB_ORM, err)
return
}
if u == nil {
e.OutErr(c, e.ERR_UNAUTHORIZED, errors.New("token 过期或无效"))
return
}
// 检验账号是否未激活或被冻结
switch u.State {
case 0:
e.OutErr(c, e.ERR_USER_NO_ACTIVE)
return
case 2:
e.OutErr(c, e.ERR_USER_IS_BAN)
return
}

// 校验是否和缓存的token一致,只能有一个token 是真实有效
key := fmt.Sprintf("%s:token:%s", c.GetString("mid"), u.Username)
//fmt.Println(key)
cjwt, err := cache.GetString(key)
//fmt.Println(cjwt)
if err != nil {
logx.Warn(err)
goto NOCACHE
}
if parts[1] != cjwt {
e.OutErr(c, e.ERR_TOKEN_AUTH, errors.New("token expired"))
return
}
NOCACHE:
// 获取user profile
up, err := db.UserProfileFindByID(db.DBs[c.GetString("mid")], mc.UID)
if err != nil {
e.OutErr(c, e.ERR_DB_ORM, err)
return
}
// 获取user 等级
ul, err := db.UserLevelByID(db.DBs[c.GetString("mid")], u.Level)
if err != nil {
e.OutErr(c, e.ERR_DB_ORM, err)
return
}
user := &md.User{
Info: u,
Profile: up,
Level: ul,
}

// 将当前请求的username信息保存到请求的上下文c上
c.Set("user", user)
// 异步处理 有效会员和新会员
c.Next() // 后续的处理函数可以用过c.Get("user")来获取当前请求的用户信息

}

+ 0
- 30
app/mw/mw_breaker.go View File

@@ -1,30 +0,0 @@
package mw

import (
"errors"
"net/http"
"strconv"

"github.com/afex/hystrix-go/hystrix"
"github.com/gin-gonic/gin"
)

// 熔断器, 此组件需要在gin.Recovery中间之前进行调用, 否则可能会导致panic时候, 无法recovery, 正确顺序如下
//r.Use(BreakerWrapper)
//r.Use(gin.Recovery())
func Breaker(c *gin.Context) {
name := c.Request.Method + "-" + c.Request.RequestURI
hystrix.Do(name, func() error {
c.Next()
statusCode := c.Writer.Status()
if statusCode >= http.StatusInternalServerError {
return errors.New("status code " + strconv.Itoa(statusCode))
}
return nil
}, func(e error) error {
if e == hystrix.ErrCircuitOpen {
c.String(http.StatusAccepted, "请稍后重试") //todo 修改报错方法
}
return e
})
}

+ 0
- 17
app/mw/mw_change_header.go View File

@@ -1,17 +0,0 @@
package mw

import (
"github.com/gin-gonic/gin"
)

// 修改传过来的头部字段
func ChangeHeader(c *gin.Context) {
appvserison := c.GetHeader("AppVersionName")
if appvserison == "" {
appvserison = c.GetHeader("app_version_name")
}
if appvserison != "" {
c.Request.Header.Add("app_version_name", appvserison)
}
c.Next()
}

+ 0
- 34
app/mw/mw_check_sign.go View File

@@ -1,34 +0,0 @@
package mw

import (
"applet/app/e"
"applet/app/utils"
"bytes"
"fmt"
"github.com/gin-gonic/gin"
"io/ioutil"
)

// CheckSign is 中间件 用来检查签名
func CheckSign(c *gin.Context) {

bools := utils.SignCheck(c)
if bools == false {
e.OutErr(c, 400, e.NewErr(400, "签名校验错误,请求失败"))
return
}
c.Next()
}
func CheckBody(c *gin.Context) {
if utils.GetApiVersion(c) > 0 {
body, _ := ioutil.ReadAll(c.Request.Body)
fmt.Println(string(body))
if string(body) != "" {
str := utils.ResultAesDecrypt(c, string(body))
if str != "" {
c.Request.Body = ioutil.NopCloser(bytes.NewBuffer([]byte(str)))
}
}
}
c.Next()
}

+ 0
- 22
app/mw/mw_checker.go View File

@@ -1,22 +0,0 @@
package mw

import (
"strings"

"github.com/gin-gonic/gin"

"applet/app/e"
"applet/app/md"
)

// 检查设备等, 把头部信息下放到hdl可以获取
func Checker(c *gin.Context) {
// 校验平台支持
platform := strings.ToLower(c.GetHeader("Platform"))
//fmt.Println(platform)
if _, ok := md.PlatformList[platform]; !ok {
e.OutErr(c, e.ERR_PLATFORM)
return
}
c.Next()
}

+ 0
- 93
app/mw/mw_db.go View File

@@ -1,93 +0,0 @@
package mw

import (
"applet/app/svc"
"errors"
"fmt"
"github.com/gin-gonic/gin"
"strings"

"applet/app/db"
"applet/app/e"
"applet/app/md"
)

// DB is 中间件 用来检查master_id是否有对应的数据库engine
func DB(c *gin.Context) {
fmt.Println(c.Request.Header)
masterID := c.GetHeader("master_id")
fmt.Println("master_id", masterID)
if masterID == "" {
fmt.Println("not found master_id found MasterId start")
masterID = c.GetHeader("MasterId")
fmt.Println("MasterId", masterID)
// if masterID still emtpy
if masterID == "" {
platform := c.GetHeader("Platform")
if platform == md.PLATFORM_WAP {
// H5 要根据域名去获取mid
hostList := strings.Split(c.Request.Host, ".")
if len(hostList) == 4 && (hostList[2]+"."+hostList[3] == "zhiyingos.com" || hostList[2]+"."+hostList[3] == "izhyin.com") {
// 官方域名
masterID = hostList[1]
} else {
// 自定义域名
masterID = svc.GetWebSiteDomainMasterId(md.PLATFORM_WAP, c.Request.Host)
}
//requestURL := cfg.Official.URL + "/api/user/check"
//fmt.Println(c.Request.Host)
//client := &http.Client{
// Timeout: time.Duration(time.Second * 2),
//}
//data := []byte(fmt.Sprintf(`{"domain":"%s"}`, c.Request.Host))
//body := bytes.NewReader(data)
//request, err := http.NewRequest("POST", requestURL, body)
//if err != nil {
// e.OutErr(c, e.ERR_MASTER_ID, errors.New("not found master_id in DBs"))
// return
//}
//request.Header.Set("Content-Type", "application/json;charset=UTF-8")
//resp, err := client.Do(request.WithContext(context.TODO()))
//if err != nil {
// e.OutErr(c, e.ERR_MASTER_ID, err)
// return
//}
//defer resp.Body.Close()
//respBytes, err := ioutil.ReadAll(resp.Body)
//if err != nil {
// e.OutErr(c, e.ERR_MASTER_ID, err)
// return
//}
//mid := gjson.GetBytes(respBytes, "data.db_master_id").String()
//if mid == "" {
// e.OutErr(c, e.ERR_MASTER_ID, errors.New("not found master_id in DBs"))
// return
//}
//masterID = mid
}
}
}

_, ok := db.DBs[masterID]
if !ok {
e.OutErr(c, e.ERR_MASTER_ID, errors.New("not found master_id in DBs"))
return
}
fmt.Println("master_id", masterID)
c.Set("mid", masterID)
//判断是否有独立域名
domain_wap_base := svc.GetWebSiteDomainInfo(c, "wap")

httpStr := "http://"
if c.GetHeader("Platform") == md.PLATFORM_WX_APPLET || c.GetHeader("Platform") == md.PLATFORM_ALIPAY_APPLET || c.GetHeader("Platform") == md.PLATFORM_BAIDU_APPLET || c.GetHeader("Platform") == md.PLATFORM_TOUTIAO_APPLET || c.GetHeader("Platform") == md.PLATFORM_TIKTOK_APPLET {
httpStr = "https://"
domain_wap_base = strings.Replace(domain_wap_base, "http://", httpStr, 1)
}
c.Set("domain_wap_base", domain_wap_base)
c.Set("http_host", httpStr)

c.Set("h5_api_secret_key", svc.SysCfgGet(c, "h5_api_secret_key"))
c.Set("app_api_secret_key", svc.SysCfgGet(c, "app_api_secret_key"))
c.Set("applet_api_secret_key", svc.SysCfgGet(c, "applet_api_secret_key"))
c.Next()
}

+ 8
- 28
app/router/router.go View File

@@ -6,6 +6,8 @@ import (
"applet/app/mw"
_ "applet/docs"
"github.com/gin-gonic/gin"
swaggerFiles "github.com/swaggo/files"
ginSwagger "github.com/swaggo/gin-swagger"
)

// 初始化路由
@@ -18,15 +20,16 @@ func Init() *gin.Engine {
gin.SetMode(mode)
//创建一个新的启动器
r := gin.New()
r.Use(mw.ChangeHeader)
r.GET("/api/swagger/*any", func(c *gin.Context) {
// r.Use(mw.SwagAuth())
ginSwagger.DisablingWrapHandler(swaggerFiles.Handler, "SWAGGER")(c)
})

// 是否打印访问日志, 在非正式环境都打印
if mode != "release" {
r.Use(gin.Logger())
}
r.Use(gin.Recovery())
// r.Use(mw.Limiter)
//r.LoadHTMLGlob("static/html/*")

r.GET("/favicon.ico", func(c *gin.Context) {
c.Status(204)
@@ -38,36 +41,13 @@ func Init() *gin.Engine {
c.JSON(405, gin.H{"code": 405, "msg": "method not allowed", "data": []struct{}{}})
})
r.Use(mw.Cors)
route(r.Group("/api/v1/mall"))
rInApi(r.Group("/inapi/mall"))
route(r.Group("/api"))
return r
}

func route(r *gin.RouterGroup) {
r.GET("/test", hdl.Demo)

r.Use(mw.DB) // 以下接口需要用到数据库
{
}
r.Use(mw.CheckBody) //body参数转换
r.Use(mw.CheckSign) //签名校验
r.Use(mw.Checker) // 以下接口需要检查Header: platform
{
}

r.Use(mw.AuthJWT) // 以下接口需要JWT验证
{

}
}

func rInApi(r *gin.RouterGroup) {
//TODO::该分组中所有的接口,支持开放平台调用
r.Use(mw.DB) // 以下接口需要用到数据库
{

}
r.Use(mw.AuthJWT) // 以下接口需要JWT验证
r.Use(mw.Auth) // 以下接口需要JWT验证
{

}


+ 55
- 0
app/svc/svc_auth.go View File

@@ -0,0 +1,55 @@
package svc

import (
"applet/app/db"
"applet/app/lib/auth"
"code.fnuoos.com/EggPlanet/egg_models.git/src/implement"
"code.fnuoos.com/EggPlanet/egg_models.git/src/model"
"errors"
"github.com/gin-gonic/gin"
"strings"
"time"
)

func GetUser(c *gin.Context) *model.Admin {
admin, _ := c.Get("admin")
if admin == nil {
return &model.Admin{
AdmId: 0,
Username: "",
Password: "",
State: 0,
IsSuperAdministrator: 0,
IsObserverAdministrator: 0,
Memo: "",
CreateAt: time.Now().Format("2006-01-02 15:04:05"),
UpdateAt: time.Now().Format("2006-01-02 15:04:05"),
}
}
return admin.(*model.Admin)
}

func CheckUser(c *gin.Context) (*model.Admin, error) {
token := c.GetHeader("Authorization")
if token == "" {
return nil, errors.New("token not exist")
}
// 按空格分割
parts := strings.SplitN(token, " ", 2)
if !(len(parts) == 2 && parts[0] == "Bearer") {
return nil, errors.New("token format error")
}
// parts[1]是获取到的tokenString,我们使用之前定义好的解析JWT的函数来解析它
mc, err := auth.ParseToken(parts[1])
if err != nil {
return nil, err
}

// 获取admin
adminDb := implement.NewAdminDb(db.Db)
user, err := adminDb.GetAdmin(mc.AdmId)
if err != nil {
return nil, err
}
return user, nil
}

+ 0
- 11
app/svc/svc_db.go View File

@@ -1,11 +0,0 @@
package svc

import (
"applet/app/db"
"github.com/gin-gonic/gin"
"xorm.io/xorm"
)

func MasterDb(c *gin.Context) *xorm.Engine {
return db.DBs[c.GetString("mid")]
}

+ 0
- 85
app/svc/svc_redis_mutex_lock.go View File

@@ -1,85 +0,0 @@
package svc

import (
"applet/app/md"
"applet/app/utils"
"applet/app/utils/cache"
"errors"
"fmt"
"math/rand"
"reflect"
"time"
)

const redisMutexLockExpTime = 15

// TryGetDistributedLock 分布式锁获取
// requestId 用于标识请求客户端,可以是随机字符串,需确保唯一
func TryGetDistributedLock(lockKey, requestId string, isNegative bool) bool {
if isNegative { // 多次尝试获取
retry := 1
for {
ok, err := cache.Do("SET", lockKey, requestId, "EX", redisMutexLockExpTime, "NX")
// 获取锁成功
if err == nil && ok == "OK" {
return true
}
// 尝试多次没获取成功
if retry > 10 {
return false
}
time.Sleep(time.Millisecond * time.Duration(rand.Intn(1000)))
retry += 1
}
} else { // 只尝试一次
ok, err := cache.Do("SET", lockKey, requestId, "EX", redisMutexLockExpTime, "NX")
// 获取锁成功
if err == nil && ok == "OK" {
return true
}

return false
}
}

// ReleaseDistributedLock 释放锁,通过比较requestId,用于确保客户端只释放自己的锁,使用lua脚本保证操作的原子型
func ReleaseDistributedLock(lockKey, requestId string) (bool, error) {
luaScript := `
if redis.call("get",KEYS[1]) == ARGV[1]
then
return redis.call("del",KEYS[1])
else
return 0
end`

do, err := cache.Do("eval", luaScript, 1, lockKey, requestId)
fmt.Println(reflect.TypeOf(do))
fmt.Println(do)

if utils.AnyToInt64(do) == 1 {
return true, err
} else {
return false, err
}
}

func GetDistributedLockRequestId(prefix string) string {
return prefix + utils.IntToStr(rand.Intn(100000000))
}

// HandleBalanceDistributedLock 处理余额更新时获取锁和释放锁 如果加锁成功,使用语句 ` defer cb() ` 释放锁
func HandleBalanceDistributedLock(masterId, uid, requestIdPrefix string) (cb func(), err error) {
// 获取余额更新锁
balanceLockKey := fmt.Sprintf(md.UserFinValidUpdateLock, masterId, uid)
requestId := GetDistributedLockRequestId(requestIdPrefix)
balanceLockOk := TryGetDistributedLock(balanceLockKey, requestId, true)
if !balanceLockOk {
return nil, errors.New("系统繁忙,请稍后再试")
}

cb = func() {
_, _ = ReleaseDistributedLock(balanceLockKey, requestId)
}

return cb, nil
}

+ 0
- 175
app/svc/svc_sys_cfg_get.go View File

@@ -1,175 +0,0 @@
package svc

import (
"errors"
"github.com/gin-gonic/gin"
"strings"
"xorm.io/xorm"

"applet/app/cfg"
"applet/app/db"
"applet/app/md"
"applet/app/utils"
"applet/app/utils/cache"
)

// 单挑记录获取
func SysCfgGet(c *gin.Context, key string) string {
mid := c.GetString("mid")
eg := db.DBs[mid]
return db.SysCfgGetWithDb(eg, mid, key)
}

// 多条记录获取
func SysCfgFind(c *gin.Context, keys ...string) map[string]string {
var masterId string
if c == nil {
masterId = ""
} else {
masterId = c.GetString("mid")
}
tmp := SysCfgFindComm(masterId, keys...)
return tmp
}
func SysCfgFindComm(masterId string, keys ...string) map[string]string {
var eg *xorm.Engine
if masterId == "" {
eg = db.Db
} else {
eg = db.DBs[masterId]
}
res := map[string]string{}
//TODO::判断keys长度(大于10个直接查数据库)
if len(keys) > 10 {
cfgList, _ := db.SysCfgGetAll(eg)
if cfgList == nil {
return nil
}
for _, v := range *cfgList {
res[v.Key] = v.Val
}
} else {
for _, key := range keys {
res[key] = db.SysCfgGetWithDb(eg, masterId, key)
}
}
return res
}

// 多条记录获取
func EgSysCfgFind(keys ...string) map[string]string {
var e *xorm.Engine
res := map[string]string{}
if len(res) == 0 {
cfgList, _ := db.SysCfgGetAll(e)
if cfgList == nil {
return nil
}
for _, v := range *cfgList {
res[v.Key] = v.Val
}
// 先不设置缓存
// cache.SetJson(md.KEY_SYS_CFG_CACHE, res, 60)
}
if len(keys) == 0 {
return res
}
tmp := map[string]string{}
for _, v := range keys {
if val, ok := res[v]; ok {
tmp[v] = val
} else {
tmp[v] = ""
}
}
return tmp
}

// 清理系统配置信息
func SysCfgCleanCache() {
cache.Del(md.KEY_SYS_CFG_CACHE)
}

// 写入系统设置
func SysCfgSet(c *gin.Context, key, val, memo string) bool {
cfg, err := db.SysCfgGetOne(db.DBs[c.GetString("mid")], key)
if err != nil || cfg == nil {
return db.SysCfgInsert(db.DBs[c.GetString("mid")], key, val, memo)
}
if memo != "" && cfg.Memo != memo {
cfg.Memo = memo
}
SysCfgCleanCache()
return db.SysCfgUpdate(db.DBs[c.GetString("mid")], key, val, cfg.Memo)
}

// 多条记录获取
func SysCfgFindByIds(eg *xorm.Engine, keys ...string) map[string]string {
key := utils.Md5(eg.DataSourceName() + md.KEY_SYS_CFG_CACHE)
res := map[string]string{}
c, ok := cfg.MemCache.Get(key).(map[string]string)
if !ok || len(c) == 0 {
cfgList, _ := db.DbsSysCfgGetAll(eg)
if cfgList == nil {
return nil
}
for _, v := range *cfgList {
res[v.Key] = v.Val
}
cfg.MemCache.Put(key, res, 10)
} else {
res = c
}
if len(keys) == 0 {
return res
}
tmp := map[string]string{}
for _, v := range keys {
if val, ok := res[v]; ok {
tmp[v] = val
} else {
tmp[v] = ""
}
}
return tmp
}

// 支付配置
func SysCfgFindPayment(c *gin.Context) ([]map[string]string, error) {
platform := c.GetHeader("platform")
payCfg := SysCfgFind(c, "pay_wx_pay_img", "pay_ali_pay_img", "pay_balance_img", "pay_type")
if payCfg["pay_wx_pay_img"] == "" || payCfg["pay_ali_pay_img"] == "" || payCfg["pay_balance_img"] == "" || payCfg["pay_type"] == "" {
return nil, errors.New("lack of payment config")
}
payCfg["pay_wx_pay_img"] = ImageFormat(c, payCfg["pay_wx_pay_img"])
payCfg["pay_ali_pay_img"] = ImageFormat(c, payCfg["pay_ali_pay_img"])
payCfg["pay_balance_img"] = ImageFormat(c, payCfg["pay_balance_img"])

var result []map[string]string

if strings.Contains(payCfg["pay_type"], "aliPay") && platform != md.PLATFORM_WX_APPLET {
item := make(map[string]string)
item["pay_channel"] = "alipay"
item["img"] = payCfg["pay_ali_pay_img"]
item["name"] = "支付宝支付"
result = append(result, item)
}

if strings.Contains(payCfg["pay_type"], "wxPay") {
item := make(map[string]string)
item["pay_channel"] = "wx"
item["img"] = payCfg["pay_wx_pay_img"]
item["name"] = "微信支付"
result = append(result, item)
}

if strings.Contains(payCfg["pay_type"], "walletPay") {
item := make(map[string]string)
item["pay_channel"] = "fin"
item["img"] = payCfg["pay_balance_img"]
item["name"] = "余额支付"
result = append(result, item)
}

return result, nil
}

+ 8
- 4
app/svc/svc_validate_common.go View File

@@ -5,17 +5,21 @@ import (
"applet/app/utils"
"applet/app/utils/logx"
"encoding/json"
"errors"
"fmt"
"github.com/go-playground/validator/v10"
)

// HandleValidateErr 通用请求参数错误处理
func HandleValidateErr(err error) error {
switch err.(type) {
case *json.UnmarshalTypeError:
var unmarshalTypeError *json.UnmarshalTypeError
var validationErrors validator.ValidationErrors
switch {
case errors.As(err, &unmarshalTypeError):
return e.NewErr(e.ERR_UNMARSHAL, "参数格式错误")
case validator.ValidationErrors:
errs := err.(validator.ValidationErrors)
case errors.As(err, &validationErrors):
var errs validator.ValidationErrors
errors.As(err, &errs)
transMsgMap := errs.Translate(utils.ValidatorTrans)
transMsgOne := transMsgMap[utils.GetOneKeyOfMapString(transMsgMap)]
return e.NewErr(e.ERR_INVALID_ARGS, transMsgOne)


+ 0
- 49
app/utils/aes.go View File

@@ -1,12 +1,9 @@
package utils

import (
"applet/app/cfg"
"bytes"
"crypto/aes"
"crypto/cipher"
"encoding/base64"
"encoding/json"
"fmt"
)

@@ -124,49 +121,3 @@ func pKCS5Trimming(encrypt []byte) []byte {
padding := encrypt[len(encrypt)-1]
return encrypt[:len(encrypt)-int(padding)]
}

// AesAdminCurlPOST is 与后台接口加密交互
func AesAdminCurlPOST(aesData string, url string) ([]byte, error) {
adminKey := cfg.Admin.AesKey
adminVI := cfg.Admin.AesIV
crypto := AesCrypt{
Key: []byte(adminKey),
Iv: []byte(adminVI),
}

encrypt, err := crypto.Encrypt([]byte(aesData))
if err != nil {
return nil, err
}

// 发送请求到后台
postData := map[string]string{
"postData": base64.StdEncoding.EncodeToString(encrypt),
}
fmt.Println(adminKey)
fmt.Println(adminVI)
fmt.Println("=======ADMIN请求=====")
fmt.Println(postData)
postDataByte, _ := json.Marshal(postData)
rdata, err := CurlPost(url, postDataByte, nil)
fmt.Println(err)

if err != nil {
return nil, err
}
fmt.Println(rdata)

pass, err := base64.StdEncoding.DecodeString(string(rdata))
if err != nil {
return nil, err
}
fmt.Println(pass)

decrypt, err := crypto.Decrypt(pass)
fmt.Println(err)

if err != nil {
return nil, err
}
return decrypt, nil
}

+ 0
- 46
app/utils/auth.go View File

@@ -1,46 +0,0 @@
package utils

import (
"errors"
"time"

"applet/app/lib/auth"

"github.com/dgrijalva/jwt-go"
)

// GenToken 生成JWT
func GenToken(uid int, username, phone, appname, MiniOpenID, MiniSK string) (string, error) {
// 创建一个我们自己的声明
c := auth.JWTUser{
uid,
username,
phone,
appname,
MiniOpenID,
MiniSK,
jwt.StandardClaims{
ExpiresAt: time.Now().Add(auth.TokenExpireDuration).Unix(), // 过期时间
Issuer: "zyos", // 签发人
},
}
// 使用指定的签名方法创建签名对象
token := jwt.NewWithClaims(jwt.SigningMethodHS256, c)
// 使用指定的secret签名并获得完整的编码后的字符串token
return token.SignedString(auth.Secret)
}

// ParseToken 解析JWT
func ParseToken(tokenString string) (*auth.JWTUser, error) {
// 解析token
token, err := jwt.ParseWithClaims(tokenString, &auth.JWTUser{}, func(token *jwt.Token) (i interface{}, err error) {
return auth.Secret, nil
})
if err != nil {
return nil, err
}
if claims, ok := token.Claims.(*auth.JWTUser); ok && token.Valid { // 校验token
return claims, nil
}
return nil, errors.New("invalid token")
}

+ 49
- 1646
docs/docs.go
File diff suppressed because it is too large
View File


+ 33
- 1592
docs/swagger.json
File diff suppressed because it is too large
View File


+ 27
- 1052
docs/swagger.yaml
File diff suppressed because it is too large
View File


+ 80
- 31
go.mod View File

@@ -1,55 +1,104 @@
module applet

go 1.18
go 1.22.0

replace code.fnuoos.com/EggPlanet/egg_models.git => E:/company/Egg/egg_models

require (
code.fnuoos.com/EggPlanet/egg_models.git v0.0.0-20241030143505-e9a8ea068840
code.fnuoos.com/go_rely_warehouse/zyos_go_mq.git v0.0.5
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751
github.com/antchfx/htmlquery v1.3.2 // indirect
github.com/antchfx/xmlquery v1.4.1 // indirect
github.com/boombuler/barcode v1.0.1
github.com/dchest/uniuri v0.0.0-20200228104902-7aecb25e1fe5
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/forgoer/openssl v0.0.0-20201023062029-c3112b0c8700
github.com/gin-contrib/sessions v0.0.3
github.com/gin-gonic/gin v1.6.3
github.com/go-openapi/spec v0.20.3 // indirect
github.com/go-openapi/swag v0.19.15 // indirect
github.com/go-playground/locales v0.13.0
github.com/go-playground/universal-translator v0.17.0
github.com/go-playground/validator/v10 v10.4.2
github.com/gin-gonic/gin v1.9.0
github.com/go-playground/locales v0.14.1
github.com/go-playground/universal-translator v0.18.1
github.com/go-playground/validator/v10 v10.11.2
github.com/go-redis/redis v6.15.9+incompatible
github.com/go-sql-driver/mysql v1.6.0
github.com/gobwas/glob v0.2.3 // indirect
github.com/go-sql-driver/mysql v1.8.1
github.com/gocolly/colly v1.2.0
github.com/golang/protobuf v1.5.2 // indirect
github.com/golang/snappy v0.0.3 // indirect
github.com/gomodule/redigo v2.0.0+incompatible
github.com/iGoogle-ink/gopay v1.5.36
github.com/makiuchi-d/gozxing v0.0.0-20210324052758-57132e828831
github.com/qiniu/api.v7/v7 v7.8.2
github.com/robfig/cron/v3 v3.0.1
github.com/sony/sonyflake v1.0.0
github.com/swaggo/swag v1.8.12
github.com/syyongx/php2go v0.9.8
github.com/tidwall/gjson v1.7.4
go.uber.org/zap v1.16.0
gopkg.in/natefinch/lumberjack.v2 v2.2.1
gopkg.in/yaml.v2 v2.4.0
xorm.io/xorm v1.3.1
)

require (
filippo.io/edwards25519 v1.1.0 // indirect
github.com/KyleBanks/depth v1.2.1 // indirect
github.com/PuerkitoBio/goquery v1.5.1 // indirect
github.com/PuerkitoBio/purell v1.1.1 // indirect
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
github.com/andybalholm/cascadia v1.1.0 // indirect
github.com/antchfx/htmlquery v1.3.2 // indirect
github.com/antchfx/xmlquery v1.4.1 // indirect
github.com/antchfx/xpath v1.3.1 // indirect
github.com/bytedance/sonic v1.8.0 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-openapi/jsonpointer v0.19.5 // indirect
github.com/go-openapi/jsonreference v0.19.6 // indirect
github.com/go-openapi/spec v0.20.4 // indirect
github.com/go-openapi/swag v0.19.15 // indirect
github.com/gobwas/glob v0.2.3 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/gookit/color v1.3.8 // indirect
github.com/gorilla/context v1.1.1 // indirect
github.com/gorilla/securecookie v1.1.1 // indirect
github.com/gorilla/sessions v1.2.1 // indirect
github.com/iGoogle-ink/gopay v1.5.36
github.com/iGoogle-ink/gotil v1.0.20
github.com/json-iterator/go v1.1.10 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/kennygrant/sanitize v1.2.4 // indirect
github.com/klauspost/cpuid/v2 v2.0.9 // indirect
github.com/leodido/go-urn v1.2.1 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/makiuchi-d/gozxing v0.0.0-20210324052758-57132e828831
github.com/pkg/errors v0.9.1
github.com/qiniu/api.v7/v7 v7.8.2
github.com/robfig/cron/v3 v3.0.1
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/onsi/ginkgo v1.15.0 // indirect
github.com/onsi/gomega v1.10.5 // indirect
github.com/pelletier/go-toml/v2 v2.0.6 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d // indirect
github.com/sony/sonyflake v1.0.0
github.com/swaggo/swag v1.7.0
github.com/syyongx/php2go v0.9.4
github.com/streadway/amqp v1.0.0 // indirect
github.com/swaggo/files v1.0.1 // indirect
github.com/swaggo/gin-swagger v1.6.0 // indirect
github.com/syndtr/goleveldb v1.0.0 // indirect
github.com/temoto/robotstxt v1.1.2 // indirect
github.com/tidwall/gjson v1.7.4
github.com/ugorji/go v1.2.5 // indirect
github.com/tidwall/match v1.0.3 // indirect
github.com/tidwall/pretty v1.1.0 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.9 // indirect
go.uber.org/atomic v1.7.0 // indirect
go.uber.org/multierr v1.6.0 // indirect
go.uber.org/zap v1.16.0
golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect
golang.org/x/crypto v0.28.0 // indirect
golang.org/x/lint v0.0.0-20200302205851-738671d3881b // indirect
gopkg.in/natefinch/lumberjack.v2 v2.0.0
gopkg.in/yaml.v2 v2.4.0
honnef.co/go/tools v0.0.1-2020.1.4 // indirect
xorm.io/builder v0.3.9 // indirect
xorm.io/xorm v1.0.7
golang.org/x/net v0.30.0 // indirect
golang.org/x/sync v0.8.0 // indirect
golang.org/x/sys v0.26.0 // indirect
golang.org/x/text v0.19.0 // indirect
golang.org/x/tools v0.26.0 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
google.golang.org/appengine v1.6.1 // indirect
google.golang.org/protobuf v1.28.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
honnef.co/go/tools v0.1.3 // indirect
xorm.io/builder v0.3.11-0.20220531020008-1bd24a7dc978 // indirect
)

+ 90
- 0
k8s/container.yaml View File

@@ -0,0 +1,90 @@
kind: Deployment
apiVersion: apps/v1
# 元数据
metadata:
name: advertisement
namespace: advertisement
labels:
app: advertisement
annotations:
kubesphere.io/creator: dengbiao
kubesphere.io/description: advertisement
# deployment主要部分
spec:
replicas: 1
selector:
matchLabels:
# 名称与上面的labels对应
app: advertisement
template:
metadata:
labels:
# 名称与上面的matchLabels对应
app: advertisement
spec:
# 声明挂载卷(将外部已存在的pvc、config等挂载进来)
volumes:
# 用于时区校正
- name: host-time
hostPath:
path: /etc/localtime
type: ''
# 将前面创建的configMap也挂载进来
- name: advertisement-cfg
configMap:
# 这里的名字就是前面创建的configMap的名字
name: advertisement-cfg
defaultMode: 420
# pvc
- name: advertisement # 在该部署中的名称,后面使用改名称挂载
persistentVolumeClaim:
claimName: advertisement # pvc的名称
# Nginx配置
- name: advertisement-nginx
configMap:
name: advertisement-nginx # 外部configMap的名称
items:
- key: go.conf
path: default.conf
containers:
# 主容器
- name: advertisement-container
# 镜像地址(提前打包好并推送的镜像仓库)
image: 'registry.cn-shenzhen.aliyuncs.com/fnuoos-prd/advertisement:0.0.1'
ports:
- name: ad-1002
# 容器端口号(注意与golang web server启动的端口一致)
containerPort: 1002
protocol: TCP
# 将前面volume声明的需要用到的pvc、config挂载上来
volumeMounts:
- name: host-time
readOnly: true
mountPath: /etc/localtime
- name: advertisement-cfg # 该名字对应前面volumes声明的名字
readOnly: true
# 挂载到容器的哪个路径
mountPath: /var/zyos
imagePullPolicy: Always
# Nginx 容器
- name: nginx
image: 'registry.cn-shenzhen.aliyuncs.com/fnuoos-prd/nginx:latest'
ports:
- name: http-80
containerPort: 80
protocol: TCP
volumeMounts:
# 时区校正
- name: host-time
readOnly: true
mountPath: /etc/localtime
# 存储卷 用于存放前端代码
- name: advertisement # 前面volumes声明的名称
mountPath: /usr/share/nginx/html
- name: advertisement-nginx # Nginx 配置
readOnly: true
mountPath: /etc/nginx/conf.d/default.conf
subPath: default.conf
restartPolicy: Always
terminationGracePeriodSeconds: 30
dnsPolicy: ClusterFirst

+ 90
- 0
k8s/container_prd.yaml View File

@@ -0,0 +1,90 @@
kind: Deployment
apiVersion: apps/v1
# 元数据
metadata:
name: advertisement
namespace: advertisement
labels:
app: advertisement
annotations:
kubesphere.io/creator: dengbiao
kubesphere.io/description: advertisement
# deployment主要部分
spec:
replicas: 1
selector:
matchLabels:
# 名称与上面的labels对应
app: advertisement
template:
metadata:
labels:
# 名称与上面的matchLabels对应
app: advertisement
spec:
# 声明挂载卷(将外部已存在的pvc、config等挂载进来)
volumes:
# 用于时区校正
- name: host-time
hostPath:
path: /etc/localtime
type: ''
# 将前面创建的configMap也挂载进来
- name: advertisement-cfg
configMap:
# 这里的名字就是前面创建的configMap的名字
name: advertisement-cfg
defaultMode: 420
# pvc
- name: advertisement-pvc # 在该部署中的名称,后面使用改名称挂载
persistentVolumeClaim:
claimName: advertisement-pvc # pvc的名称
# Nginx配置
- name: advertisement-nginx
configMap:
name: advertisement-nginx # 外部configMap的名称
items:
- key: go.conf
path: default.conf
containers:
# 主容器
- name: advertisement-container
# 镜像地址(提前打包好并推送的镜像仓库)
image: 'registry.cn-shenzhen.aliyuncs.com/fnuoos-prd/advertisement:0.0.1'
ports:
- name: ad-1002
# 容器端口号(注意与golang web server启动的端口一致)
containerPort: 1002
protocol: TCP
# 将前面volume声明的需要用到的pvc、config挂载上来
volumeMounts:
- name: host-time
readOnly: true
mountPath: /etc/localtime
- name: advertisement-cfg # 该名字对应前面volumes声明的名字
readOnly: true
# 挂载到容器的哪个路径
mountPath: /var/zyos
imagePullPolicy: Always
# Nginx 容器
- name: nginx
image: 'registry.cn-shenzhen.aliyuncs.com/fnuoos-prd/nginx:latest'
ports:
- name: http-80
containerPort: 80
protocol: TCP
volumeMounts:
# 时区校正
- name: host-time
readOnly: true
mountPath: /etc/localtime
# 存储卷 用于存放前端代码
- name: advertisement-pvc # 前面volumes声明的名称
mountPath: /usr/share/nginx/html
- name: advertisement-nginx # Nginx 配置
readOnly: true
mountPath: /etc/nginx/conf.d/default.conf
subPath: default.conf
restartPolicy: Always
terminationGracePeriodSeconds: 30
dnsPolicy: ClusterFirst

+ 0
- 56
k8s/mall-task-prd.yaml View File

@@ -1,56 +0,0 @@
kind: Deployment
apiVersion: apps/v1
metadata:
name: mall-task
namespace: zhios
labels:
app: mall-task
annotations:
kubesphere.io/creator: wuhanqin
kubesphere.io/description: 自营商城go定时任务
spec:
replicas: 1
selector:
matchLabels:
app: mall-task
template:
metadata:
labels:
app: mall-task
spec:
volumes:
- name: host-time
hostPath:
path: /etc/localtime
type: ''
- name: mall-task-cfg1
configMap:
name: zhios-mall-task
items:
- key: task.yml
path: task.yml
defaultMode: 420
containers:
- name: container-mall-task
image: 'registry.cn-shenzhen.aliyuncs.com/fnuoos-prd/zyos-mall-task:0.3'
resources:
limits:
cpu: '1'
memory: 1000Mi
requests:
cpu: 200m
memory: 1000Mi
volumeMounts:
- name: host-time
readOnly: true
mountPath: /etc/localtime
- name: mall-task-cfg1
readOnly: true
mountPath: /var/zyos/task.yml
subPath: task.yml
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
imagePullPolicy: Always
restartPolicy: Always
terminationGracePeriodSeconds: 30
dnsPolicy: ClusterFirst

+ 0
- 56
k8s/mall-task.yaml View File

@@ -1,56 +0,0 @@
kind: Deployment
apiVersion: apps/v1
metadata:
name: mall-task
namespace: dev
labels:
app: mall-task
annotations:
kubesphere.io/creator: wuhanqin
kubesphere.io/description: 自营商城go定时任务
spec:
replicas: 1
selector:
matchLabels:
app: mall-task
template:
metadata:
labels:
app: mall-task
spec:
volumes:
- name: host-time
hostPath:
path: /etc/localtime
type: ''
- name: mall-task-cfg1
configMap:
name: mall-task-cfg
items:
- key: task.yml
path: task.yml
defaultMode: 420
containers:
- name: container-mall-task
image: 'registry.cn-shenzhen.aliyuncs.com/fnuoos-prd/zyos-mall-task:0.1'
resources:
limits:
cpu: '1'
memory: 1000Mi
requests:
cpu: 200m
memory: 1000Mi
volumeMounts:
- name: host-time
readOnly: true
mountPath: /etc/localtime
- name: mall-task-cfg1
readOnly: true
mountPath: /var/zyos/task.yml
subPath: task.yml
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
imagePullPolicy: Always
restartPolicy: Always
terminationGracePeriodSeconds: 30
dnsPolicy: ClusterFirst

+ 0
- 49
k8s/zyos-mall-deployment_prd.yaml View File

@@ -1,49 +0,0 @@
apiVersion: apps/v1
kind: Deployment
metadata:
namespace: zhios
name: zhios-mall
labels:
app: zhios-mall
spec:
replicas: 1
template:
metadata:
name: zhios-mall
labels:
app: zhios-mall
spec:
containers:
- name: zhios-mall-container
image: registry-vpc.cn-shenzhen.aliyuncs.com/fnuoos-prd/zyos-mall:0.1
ports:
- containerPort: 5002
name: 5002tcp
protocol: TCP
resources:
limits:
cpu: "1"
memory: 256Mi
requests:
cpu: 200m
memory: 128Mi
imagePullPolicy: IfNotPresent
restartPolicy: Always
volumes:
- name: host-time
hostPath:
path: /etc/localtime
type: ''
- name: mall-cfg
configMap:
name: zhios-mall-cfg
defaultMode: 420
selector:
matchLabels:
app: zhios-mall

strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 25%
maxSurge: 25%

+ 31
- 41
main.go View File

@@ -1,7 +1,9 @@
package main

import (
"applet/app/db"
"context"
"errors"
"fmt"
"log"
"net/http"
@@ -11,69 +13,57 @@ import (
"time"

"applet/app/cfg"
"applet/app/db"
"applet/app/router"
"applet/app/utils"
)

//系统初始化
// 系统初始化
func init() {
cfg.InitCfg() //配置初始化
cfg.InitLog() //日志初始化
cfg.InitCache() //缓存初始化
if cfg.Debug { //判断是否是debug
if err := db.InitDB(cfg.DB); err != nil { //主数据库初始化
cfg.InitCfg() // 配置初始化
cfg.InitLog() // 日志初始化
cfg.InitCache() // 缓存初始化
cfg.InitMq() // 队列初始化
if cfg.Debug { // 判断是否是debug
if err := db.InitDB(cfg.DB); err != nil { // 主数据库初始化
panic(err)
}
channel := make(chan int, 0) //开辟管道,缓冲为
go db.InitDBs(channel)
<-channel
}
fmt.Println("init success")

}

// @title 智莺生活移动端接口
// @title 蛋蛋星球-管理后台
// @version 1.0
// @description 移动端接口
// @termsOfService 智莺生活后端组
// @contact.name sherlockwhite
// @host localhost:5000
// @description 管理后台接口文档
// @termsOfService http://swagger.io/terms/

// @contact.name dengbiao
// @contact.url http://www.swagger.io/support
// @contact.email 1239118001@qq.com

// @license.name Apache 2.0
// @license.url http://www.apache.org/licenses/LICENSE-2.0.html

// @host localhost:4001
// @securityDefinitions.apikey MasterID
// @in header
// @name MasterID
// @BasePath /
// @BasePath /api
func main() {
// 启动获取所有品牌
//go taoke.GetAllBrand()
r := router.Init() //创建路由
// arkid.Init()
srv := &http.Server{ //设置http服务参数
Addr: cfg.SrvAddr, //指定ip和端口
Handler: r, //指定路由
}
// 读取默认站长的推广位 并写进redis
// master, err := db.UserProfileFindByID(,"1")
r := router.Init() // 创建路由

// if err != nil {
// panic(err)
// }
if cfg.CurlDebug {
utils.CurlDebug = true
srv := &http.Server{ // 设置http服务参数
Addr: cfg.SrvAddr, // 指定ip和端口
Handler: r, // 指定路由
}
//
// if has := cache.SetJson(svc.SysCfgGet(nil, "app_name")+"_default_pid_user", master, 0); has != true {
// panic(errors.New("设置默认pid缓存失败"))
// }
// Initializing the server in a goroutine so that it won't block the graceful shutdown handling below
go func() { //协程启动监听http服务

go func() { // 协程启动监听http服务
fmt.Println("Listening and serving HTTP on " + cfg.SrvAddr)
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
log.Fatalf("listen: %s\n", err)
}
}()
// graceful shutdown
//退出go守护进程
// 退出go守护进程
quit := make(chan os.Signal)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit


Loading…
Cancel
Save