@@ -12,7 +12,7 @@ | |||||
- md 结构体 | - md 结构体 | ||||
#### 介绍 | #### 介绍 | ||||
基于gin的接口小程序 | |||||
蛋蛋星球后台管理项目 | |||||
#### 软件架构 | #### 软件架构 | ||||
@@ -39,10 +39,23 @@ | |||||
## swagger | ## swagger | ||||
``` | ``` | ||||
// 参考:https://segmentfault.com/a/1190000013808421 | |||||
// 安装命令行 | |||||
> 参考:https://segmentfault.com/a/1190000013808421 | |||||
> swagger 命令行工具 | |||||
go get -u github.com/swaggo/swag/cmd/swag | 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 | swag init | ||||
``` | ``` |
@@ -5,51 +5,41 @@ import ( | |||||
) | ) | ||||
type Config struct { | 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 { | type ArkIDCfg struct { | ||||
Admin string `yaml:"admin"` | Admin string `yaml:"admin"` | ||||
AdminPassword string `yaml:"admin_password"` | AdminPassword string `yaml:"admin_password"` | ||||
Url string `yaml:"url` | |||||
Url string `yaml:"url"` | |||||
} | } | ||||
//数据库配置结构体 | |||||
// 数据库配置结构体 | |||||
type DBCfg struct { | type DBCfg struct { | ||||
Host string `yaml:"host"` //ip及端口 | Host string `yaml:"host"` //ip及端口 | ||||
Name string `yaml:"name"` //库名 | Name string `yaml:"name"` //库名 | ||||
@@ -62,7 +52,7 @@ type DBCfg struct { | |||||
Path string `yaml:"path"` //日志文件存放路径 | Path string `yaml:"path"` //日志文件存放路径 | ||||
} | } | ||||
//日志配置结构体 | |||||
// 日志配置结构体 | |||||
type LogCfg struct { | type LogCfg struct { | ||||
AppName string `yaml:"app_name" ` | AppName string `yaml:"app_name" ` | ||||
Level string `yaml:"level"` | Level string `yaml:"level"` | ||||
@@ -76,3 +66,10 @@ type LogCfg struct { | |||||
FileMaxSize int `yaml:"file_max_size"` | FileMaxSize int `yaml:"file_max_size"` | ||||
FileMaxAge int `yaml:"file_max_age"` | 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"` | |||||
} |
@@ -7,24 +7,20 @@ import ( | |||||
"gopkg.in/yaml.v2" | "gopkg.in/yaml.v2" | ||||
) | ) | ||||
//配置文件数据,全局变量 | |||||
// 配置文件数据,全局变量 | |||||
var ( | 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() { | func InitCfg() { | ||||
//用指定的名称、默认值、使用信息注册一个string类型flag。 | //用指定的名称、默认值、使用信息注册一个string类型flag。 | ||||
path := flag.String("c", "etc/cfg.yml", "config file") | path := flag.String("c", "etc/cfg.yml", "config file") | ||||
@@ -46,15 +42,10 @@ func InitCfg() { | |||||
//数据读入内存 | //数据读入内存 | ||||
Prd = conf.Prd | Prd = conf.Prd | ||||
Debug = conf.Debug | Debug = conf.Debug | ||||
Local = conf.Local | |||||
CurlDebug = conf.CurlDebug | |||||
DB = &conf.DB | DB = &conf.DB | ||||
Log = &conf.Log | Log = &conf.Log | ||||
ArkID = &conf.ArkID | |||||
RedisAddr = conf.RedisAddr | RedisAddr = conf.RedisAddr | ||||
RedisPassword = conf.RedisPassword | |||||
SrvAddr = conf.SrvAddr | SrvAddr = conf.SrvAddr | ||||
Admin = &conf.Admin | |||||
Official = &conf.Official | |||||
WxappletFilepath = &conf.WxappletFilepath | |||||
AppComm = &conf.AppComm | |||||
MQ = &conf.MQ | |||||
} | } |
@@ -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() | |||||
} |
@@ -28,10 +28,7 @@ func InitTaskCfg() { | |||||
Debug = conf.Debug | Debug = conf.Debug | ||||
DB = &conf.DB | DB = &conf.DB | ||||
Log = &conf.Log | Log = &conf.Log | ||||
Admin = &conf.Admin | |||||
RedisAddr = conf.RedisAddr | RedisAddr = conf.RedisAddr | ||||
Local = conf.Local | |||||
AppComm = &conf.AppComm | |||||
} | } | ||||
var MemCache mc.Cache | var MemCache mc.Cache | ||||
@@ -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) | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -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 | |||||
} |
@@ -6,6 +6,16 @@ import ( | |||||
"github.com/gin-gonic/gin" | "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) { | func Demo(c *gin.Context) { | ||||
var args interface{} | var args interface{} | ||||
err := c.ShouldBindJSON(&args) | err := c.ShouldBindJSON(&args) | ||||
@@ -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") | |||||
} |
@@ -7,17 +7,13 @@ import ( | |||||
) | ) | ||||
// TokenExpireDuration is jwt 过期时间 | // TokenExpireDuration is jwt 过期时间 | ||||
const TokenExpireDuration = time.Hour * 4380 | |||||
const TokenExpireDuration = time.Hour * 24 | |||||
var Secret = []byte("zyos") | |||||
var Secret = []byte("smart_canteen_admin") | |||||
// JWTUser 如果想要保存更多信息,都可以添加到这个结构体中 | // JWTUser 如果想要保存更多信息,都可以添加到这个结构体中 | ||||
type JWTUser struct { | 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 | jwt.StandardClaims | ||||
} | } |
@@ -2,9 +2,7 @@ package md | |||||
// 缓存key统一管理, %s格式化为masterId | // 缓存key统一管理, %s格式化为masterId | ||||
const ( | const ( | ||||
AppCfgCacheKey = "%s:cfg_cache:%s" // 占位符: masterId, key的第一个字母 | |||||
UserFinValidUpdateLock = "%s:user_fin_valid_update_lock:%s" // 用户余额更新锁(能拿到锁才能更新余额) | |||||
AppCfgCacheKey = "cfg_cache:%s" // 占位符: key的第一个字母 | |||||
CfgCacheTime = 86400 | CfgCacheTime = 86400 | ||||
) | ) |
@@ -0,0 +1,7 @@ | |||||
package md | |||||
type Response struct { | |||||
Code string `json:"code" example:"响应码"` | |||||
Data interface{} `json:"data" ` //内容 | |||||
Msg string `json:"msg" example:"具体错误原因"` | |||||
} |
@@ -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], | |||||
} | |||||
} |
@@ -1,72 +1,31 @@ | |||||
package mw | package mw | ||||
import ( | import ( | ||||
"errors" | |||||
"applet/app/db" | |||||
"applet/app/e" | "applet/app/e" | ||||
"applet/app/lib/arkid" | |||||
"applet/app/md" | |||||
"applet/app/utils" | |||||
"applet/app/svc" | |||||
"errors" | |||||
"github.com/gin-gonic/gin" | "github.com/gin-gonic/gin" | ||||
) | ) | ||||
// 检查权限, 签名等等 | |||||
// Auth 检查权限, 签名等等 | |||||
func Auth(c *gin.Context) { | 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 { | 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() | c.Next() | ||||
} | } |
@@ -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")来获取当前请求的用户信息 | |||||
} |
@@ -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 | |||||
}) | |||||
} |
@@ -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() | |||||
} |
@@ -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() | |||||
} |
@@ -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() | |||||
} |
@@ -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() | |||||
} |
@@ -6,6 +6,8 @@ import ( | |||||
"applet/app/mw" | "applet/app/mw" | ||||
_ "applet/docs" | _ "applet/docs" | ||||
"github.com/gin-gonic/gin" | "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) | gin.SetMode(mode) | ||||
//创建一个新的启动器 | //创建一个新的启动器 | ||||
r := gin.New() | 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" { | if mode != "release" { | ||||
r.Use(gin.Logger()) | r.Use(gin.Logger()) | ||||
} | } | ||||
r.Use(gin.Recovery()) | r.Use(gin.Recovery()) | ||||
// r.Use(mw.Limiter) | |||||
//r.LoadHTMLGlob("static/html/*") | |||||
r.GET("/favicon.ico", func(c *gin.Context) { | r.GET("/favicon.ico", func(c *gin.Context) { | ||||
c.Status(204) | c.Status(204) | ||||
@@ -38,36 +41,13 @@ func Init() *gin.Engine { | |||||
c.JSON(405, gin.H{"code": 405, "msg": "method not allowed", "data": []struct{}{}}) | c.JSON(405, gin.H{"code": 405, "msg": "method not allowed", "data": []struct{}{}}) | ||||
}) | }) | ||||
r.Use(mw.Cors) | r.Use(mw.Cors) | ||||
route(r.Group("/api/v1/mall")) | |||||
rInApi(r.Group("/inapi/mall")) | |||||
route(r.Group("/api")) | |||||
return r | return r | ||||
} | } | ||||
func route(r *gin.RouterGroup) { | func route(r *gin.RouterGroup) { | ||||
r.GET("/test", hdl.Demo) | 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验证 | |||||
{ | { | ||||
} | } | ||||
@@ -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 | |||||
} |
@@ -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")] | |||||
} |
@@ -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 | |||||
} |
@@ -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 | |||||
} |
@@ -5,17 +5,21 @@ import ( | |||||
"applet/app/utils" | "applet/app/utils" | ||||
"applet/app/utils/logx" | "applet/app/utils/logx" | ||||
"encoding/json" | "encoding/json" | ||||
"errors" | |||||
"fmt" | "fmt" | ||||
"github.com/go-playground/validator/v10" | "github.com/go-playground/validator/v10" | ||||
) | ) | ||||
// HandleValidateErr 通用请求参数错误处理 | // HandleValidateErr 通用请求参数错误处理 | ||||
func HandleValidateErr(err error) error { | 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, "参数格式错误") | 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) | transMsgMap := errs.Translate(utils.ValidatorTrans) | ||||
transMsgOne := transMsgMap[utils.GetOneKeyOfMapString(transMsgMap)] | transMsgOne := transMsgMap[utils.GetOneKeyOfMapString(transMsgMap)] | ||||
return e.NewErr(e.ERR_INVALID_ARGS, transMsgOne) | return e.NewErr(e.ERR_INVALID_ARGS, transMsgOne) | ||||
@@ -1,12 +1,9 @@ | |||||
package utils | package utils | ||||
import ( | import ( | ||||
"applet/app/cfg" | |||||
"bytes" | "bytes" | ||||
"crypto/aes" | "crypto/aes" | ||||
"crypto/cipher" | "crypto/cipher" | ||||
"encoding/base64" | |||||
"encoding/json" | |||||
"fmt" | "fmt" | ||||
) | ) | ||||
@@ -124,49 +121,3 @@ func pKCS5Trimming(encrypt []byte) []byte { | |||||
padding := encrypt[len(encrypt)-1] | padding := encrypt[len(encrypt)-1] | ||||
return encrypt[:len(encrypt)-int(padding)] | 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 | |||||
} |
@@ -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") | |||||
} |
@@ -1,55 +1,104 @@ | |||||
module applet | module applet | ||||
go 1.18 | |||||
go 1.22.0 | |||||
replace code.fnuoos.com/EggPlanet/egg_models.git => E:/company/Egg/egg_models | |||||
require ( | 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/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5 | ||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 | 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/boombuler/barcode v1.0.1 | ||||
github.com/dchest/uniuri v0.0.0-20200228104902-7aecb25e1fe5 | github.com/dchest/uniuri v0.0.0-20200228104902-7aecb25e1fe5 | ||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible | github.com/dgrijalva/jwt-go v3.2.0+incompatible | ||||
github.com/forgoer/openssl v0.0.0-20201023062029-c3112b0c8700 | github.com/forgoer/openssl v0.0.0-20201023062029-c3112b0c8700 | ||||
github.com/gin-contrib/sessions v0.0.3 | 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-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/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/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/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/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/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/leodido/go-urn v1.2.1 // indirect | ||||
github.com/mailru/easyjson v0.7.7 // 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/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/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/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 | 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 | |||||
) | ) |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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% |
@@ -1,7 +1,9 @@ | |||||
package main | package main | ||||
import ( | import ( | ||||
"applet/app/db" | |||||
"context" | "context" | ||||
"errors" | |||||
"fmt" | "fmt" | ||||
"log" | "log" | ||||
"net/http" | "net/http" | ||||
@@ -11,69 +13,57 @@ import ( | |||||
"time" | "time" | ||||
"applet/app/cfg" | "applet/app/cfg" | ||||
"applet/app/db" | |||||
"applet/app/router" | "applet/app/router" | ||||
"applet/app/utils" | |||||
) | ) | ||||
//系统初始化 | |||||
// 系统初始化 | |||||
func init() { | 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) | panic(err) | ||||
} | } | ||||
channel := make(chan int, 0) //开辟管道,缓冲为 | |||||
go db.InitDBs(channel) | |||||
<-channel | |||||
} | } | ||||
fmt.Println("init success") | fmt.Println("init success") | ||||
} | } | ||||
// @title 智莺生活移动端接口 | |||||
// @title 蛋蛋星球-管理后台 | |||||
// @version 1.0 | // @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 | // @securityDefinitions.apikey MasterID | ||||
// @in header | // @in header | ||||
// @name MasterID | // @name MasterID | ||||
// @BasePath / | |||||
// @BasePath /api | |||||
func main() { | 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) | 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) | log.Fatalf("listen: %s\n", err) | ||||
} | } | ||||
}() | }() | ||||
// graceful shutdown | |||||
//退出go守护进程 | |||||
// 退出go守护进程 | |||||
quit := make(chan os.Signal) | quit := make(chan os.Signal) | ||||
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) | signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) | ||||
<-quit | <-quit | ||||