@@ -13,9 +13,10 @@ type Config struct { | |||
ZhimengDB DBCfg `yaml:"zhimeng_db"` | |||
Log LogCfg `yaml:"log"` | |||
ES ESCfg `yaml:"es"` | |||
Local bool | |||
} | |||
//数据库配置结构体 | |||
// 数据库配置结构体 | |||
type DBCfg struct { | |||
Host string `yaml:"host"` //ip及端口 | |||
Name string `yaml:"name"` //库名 | |||
@@ -28,7 +29,7 @@ type DBCfg struct { | |||
Path string `yaml:"path"` //日志文件存放路径 | |||
} | |||
//日志配置结构体 | |||
// 日志配置结构体 | |||
type LogCfg struct { | |||
AppName string `yaml:"app_name" ` | |||
Level string `yaml:"level"` | |||
@@ -7,7 +7,7 @@ import ( | |||
"gopkg.in/yaml.v2" | |||
) | |||
//配置文件数据,全局变量 | |||
// 配置文件数据,全局变量 | |||
var ( | |||
Debug bool | |||
Prd bool | |||
@@ -17,9 +17,10 @@ var ( | |||
ZhimengDB *DBCfg | |||
Log *LogCfg | |||
ES *ESCfg | |||
Local bool | |||
) | |||
//初始化配置文件,将cfg.yml读入到内存 | |||
// 初始化配置文件,将cfg.yml读入到内存 | |||
func InitCfg() { | |||
//用指定的名称、默认值、使用信息注册一个string类型flag。 | |||
path := flag.String("c", "etc/cfg.yml", "config file") | |||
@@ -47,4 +48,5 @@ func InitCfg() { | |||
Log = &conf.Log | |||
RedisAddr = conf.RedisAddr | |||
SrvAddr = conf.SrvAddr | |||
Local = conf.Local | |||
} |
@@ -0,0 +1,63 @@ | |||
package db | |||
import ( | |||
"applet/app/db/station" | |||
"applet/app/utils/logx" | |||
"github.com/gin-gonic/gin" | |||
"xorm.io/xorm" | |||
) | |||
// 单条记录获取DB | |||
func SysCfgGet(c *gin.Context, key string) string { | |||
res := SysCfgFind(c, key) | |||
if _, ok := res[key]; !ok { | |||
return "" | |||
} | |||
return res[key] | |||
} | |||
// 多条记录获取 | |||
func SysCfgFind(c *gin.Context, keys ...string) map[string]string { | |||
eg := DBs[c.GetString("master_id")] | |||
masterId := c.GetString("master_id") | |||
res := map[string]string{} | |||
//TODO::判断keys长度(大于10个直接查数据库) | |||
if len(keys) > 10 { | |||
cfgList, _ := SysCfgGetAll(eg) | |||
if cfgList == nil { | |||
return nil | |||
} | |||
for _, v := range *cfgList { | |||
res[v.Key] = v.Val | |||
} | |||
} else { | |||
for _, key := range keys { | |||
res[key] = SysCfgGetWithDb(eg, masterId, key) | |||
} | |||
} | |||
return res | |||
} | |||
func SysCfgGetAll(Db *xorm.Engine) (*[]model.SysCfg, error) { | |||
var cfgList []model.SysCfg | |||
if err := Db.Cols("key,val,memo").Find(&cfgList); err != nil { | |||
return nil, logx.Error(err) | |||
} | |||
return &cfgList, nil | |||
} | |||
func SysCfgGetOne(Db *xorm.Engine, key string) (*model.SysCfg, error) { | |||
var cfgList model.SysCfg | |||
if has, err := Db.Where("`key`=?", key).Get(&cfgList); err != nil || has == false { | |||
return nil, logx.Error(err) | |||
} | |||
return &cfgList, nil | |||
} | |||
// 单条记录获取DB | |||
func SysCfgGetWithDb(eg *xorm.Engine, masterId string, HKey string) string { | |||
cfg, err := SysCfgGetOne(eg, HKey) | |||
if err != nil || cfg == nil { | |||
_ = logx.Error(err) | |||
return "" | |||
} | |||
return cfg.Val | |||
} |
@@ -0,0 +1,104 @@ | |||
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,0 +1,194 @@ | |||
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 | |||
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, 22255132).Or("db_master_id=?", 56593133).Find(&m) | |||
//err = Db.Where("deleted_at != ? AND is_dev = '1' AND db_master_id=?", 1, 22255132).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 '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_wap_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 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 masterId + "." + domainBase.V, nil | |||
} |
@@ -0,0 +1,18 @@ | |||
package model | |||
import ( | |||
"time" | |||
) | |||
type DbMapping struct { | |||
DbMasterId string `json:"db_master_id" xorm:"not pk null comment('站长id') VARCHAR(32)"` | |||
DbHost string `json:"db_host" xorm:"not null default '' comment('数据库连接(带port)') VARCHAR(255)"` | |||
DbUsername string `json:"db_username" xorm:"not null default '' comment('数据库用户名') VARCHAR(255)"` | |||
DbPassword string `json:"db_password" xorm:"not null default '' comment('数据库用户名密码') VARCHAR(255)"` | |||
DbName string `json:"db_name" xorm:"not null comment('数据库名') VARCHAR(255)"` | |||
ExternalMysql string `json:"external_mysql" xorm:"not null default '0' comment('是否外部mysql(0是内部,1是外部)') VARCHAR(255)"` | |||
IsDev int `json:"is_dev" xorm:"not null default 0 comment('开发库是1,0是生产库') TINYINT(1)"` | |||
CreatedAt time.Time `json:"created_at" xorm:"not null default CURRENT_TIMESTAMP comment('创建时间') TIMESTAMP"` | |||
UpdatedAt time.Time `json:"updated_at" xorm:"not null default CURRENT_TIMESTAMP comment('更新时间') TIMESTAMP"` | |||
DeletedAt int `json:"deleted_at" xorm:"not null default 0 comment('是否已删除') TINYINT(1)"` | |||
} |
@@ -0,0 +1,8 @@ | |||
package model | |||
type UserAppList struct { | |||
Id int `json:"id" xorm:"int(11) NOT NULL "` | |||
Uuid int64 `json:"uuid" xorm:"int(10) NOT NULL "` | |||
AppId int64 `json:"app_id" xorm:"int(10) NOT NULL "` | |||
SmsPlatform string `json:"sms_platform" xorm:"varchar(255) DEFAULT 'mob' "` | |||
} |
@@ -0,0 +1,7 @@ | |||
package model | |||
type SysCfg struct { | |||
Key string `json:"key" xorm:"not null pk comment('键') VARCHAR(127)"` | |||
Val string `json:"val" xorm:"comment('值') TEXT"` | |||
Memo string `json:"memo" xorm:"not null default '' comment('备注') VARCHAR(255)"` | |||
} |
@@ -1,8 +1,17 @@ | |||
package zhimeng_platform | |||
import ( | |||
"applet/app/db" | |||
"applet/app/e" | |||
sms2 "applet/app/lib/sms" | |||
"applet/app/svc/platform" | |||
"applet/app/utils" | |||
"encoding/json" | |||
"fmt" | |||
"github.com/gin-gonic/gin" | |||
"github.com/syyongx/php2go" | |||
"math/rand" | |||
"time" | |||
) | |||
func WithdrawalIncome(c *gin.Context) { | |||
@@ -23,3 +32,35 @@ func WithdrawalInvoiceImg(c *gin.Context) { | |||
func WithdrawalBindAlipay(c *gin.Context) { | |||
platform.WithdrawalBindAlipay(c) | |||
} | |||
func Sms(c *gin.Context) { | |||
if c.GetString("is_system") == "" { | |||
e.OutErr(c, 400, e.NewErr(400, "请重新在后台进入聚合联盟")) | |||
return | |||
} | |||
if c.GetString("is_system") != "1" { | |||
e.OutErr(c, 400, e.NewErr(400, "请使用超管账号设置")) | |||
return | |||
} | |||
appName := db.SysCfgGet(c, "sms_push_sign") | |||
captcha := createCaptcha() | |||
content := fmt.Sprintf("【%s】验证码:%s", appName, captcha) | |||
templateCode := "" | |||
marshal, _ := json.Marshal(c.Request.Header) | |||
waykeys := "zhimeng_" + c.ClientIP() + "_" + utils.IntToStr(utils.GetApiVersion(c)) + "_" + c.Request.RequestURI + "_" + string(marshal) | |||
postData := map[string]interface{}{ | |||
"content": content, | |||
"mobile": c.GetString("phone"), | |||
"templateCode": templateCode, | |||
"way": php2go.Base64Encode(waykeys), | |||
} | |||
err := sms2.GetSmsConfig(c, "86", postData) | |||
if err != nil { | |||
e.OutErr(c, 400, err.Error()) | |||
return | |||
} | |||
e.OutSuc(c, "success", nil) | |||
return | |||
} | |||
func createCaptcha() string { | |||
return fmt.Sprintf("%05v", rand.New(rand.NewSource(time.Now().UnixNano())).Int31n(1000000)) | |||
} |
@@ -0,0 +1,270 @@ | |||
package mob | |||
import ( | |||
"applet/app/db" | |||
"applet/app/e" | |||
"applet/app/lib/sms" | |||
"applet/app/utils" | |||
"applet/app/utils/logx" | |||
"bytes" | |||
"crypto/cipher" | |||
"crypto/des" | |||
"crypto/md5" | |||
"encoding/base64" | |||
"encoding/hex" | |||
"encoding/json" | |||
"errors" | |||
"fmt" | |||
"io/ioutil" | |||
"net/http" | |||
"sort" | |||
"time" | |||
"github.com/gin-gonic/gin" | |||
"github.com/tidwall/gjson" | |||
) | |||
const base string = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" | |||
// Mob is mob sdk | |||
var Mob *SDK | |||
// MobMap is 每个站长都要有自己的mob 对象 | |||
var MobMap map[string]*SDK | |||
// Init 初始化 | |||
func Init() { | |||
// 后续可能要传请求的上下文来获取对应的配置 | |||
// mob 目前都是我们来管理每个站长的app 所以使用template 库 | |||
//fmt.Println("Mob SDK init ....") | |||
ch := make(chan struct{}) // 只是做信号标志的话 空struct 更省点资源 | |||
MobMap = make(map[string]*SDK) | |||
// 初始化 | |||
for k, e := range db.DBs { | |||
m := db.SysCfgGetWithDb(e, k, "third_app_push_set") | |||
if m == "" { | |||
fmt.Printf("masterid:%s 找不到推送配置", k) | |||
continue | |||
} | |||
key := gjson.Get(m, "mobAppKey").String() | |||
secret := gjson.Get(m, "mobAppSecret").String() | |||
if key == "" || secret == "" { | |||
fmt.Println(k + ":mob no config") | |||
continue | |||
} | |||
// fmt.Println(k, key, secret) | |||
mob := new(SDK) | |||
mob.AppKey = key | |||
mob.AppSecret = secret | |||
MobMap[k] = mob | |||
fmt.Println(k + ":mob config success") | |||
} | |||
go func() { | |||
ch <- struct{}{} | |||
}() | |||
// 定时任务 | |||
go func(MobMap map[string]*SDK, ch chan struct{}) { | |||
<-ch | |||
ticker := time.NewTicker(time.Duration(time.Second * 15)) | |||
//每 15s 一次更新一次mob 配置 | |||
for range ticker.C { | |||
for k, e := range db.DBs { | |||
if err := e.Ping(); err != nil { | |||
logx.Info(err) | |||
continue | |||
} | |||
m := db.SysCfgGetWithDb(e, k, "third_app_push_set") | |||
if m == "" { | |||
fmt.Printf("masterid:%s 找不到推送配置", k) | |||
continue | |||
} | |||
key := gjson.Get(m, "mobAppKey").String() | |||
secret := gjson.Get(m, "mobAppSecret").String() | |||
if key == "" || secret == "" { | |||
fmt.Println(k + ":mob no config") | |||
continue | |||
} | |||
// fmt.Println(k, key, secret) | |||
mob := new(SDK) | |||
mob.AppKey = key | |||
mob.AppSecret = secret | |||
MobMap[k] = mob | |||
// fmt.Println(k + ":mob config success") | |||
} | |||
} | |||
}(MobMap, ch) | |||
} | |||
// GetMobSDK is 获取mob 的sdk | |||
func GetMobSDK(mid string) (*SDK, error) { | |||
return &SDK{AppKey: "30dc33054b635", AppSecret: "396e98c293130c9976fb7428b6b434d6"}, nil | |||
} | |||
// SDK is mob_push 的sdk | |||
type SDK struct { | |||
AppKey string | |||
AppSecret string | |||
} | |||
// MobFreeLogin is 秒验 | |||
func (s *SDK) MobFreeLogin(args map[string]interface{}) (string, error) { | |||
var url string = "http://identify.verify.mob.com/auth/auth/sdkClientFreeLogin" | |||
// https://www.mob.com/wiki/detailed/?wiki=miaoyan_for_fuwuduan_mianmifuwuduanjieru&id=78 | |||
//加appkey | |||
args["appkey"] = s.AppKey | |||
//加签名 | |||
args["sign"] = generateSign(args, s.AppSecret) | |||
b, err := json.Marshal(args) | |||
if err != nil { | |||
return "", logx.Warn(err) | |||
} | |||
// 发送请求 | |||
respBody, err := httpPostBody(url, b) | |||
if err != nil { | |||
return "", logx.Warn(err) | |||
} | |||
// 反序列化 | |||
ret := struct { | |||
Status int `json:"status"` | |||
Error string `json:"error"` | |||
Res interface{} `json:"res"` | |||
}{} | |||
// 要拿 ret 里面 Res 再解密 | |||
if err := json.Unmarshal(respBody, &ret); err != nil { | |||
return "", logx.Warn(err) | |||
} | |||
//fmt.Println(ret) | |||
// ret里面的Res 反序列化为结构体 | |||
res := struct { | |||
IsValid int `json:"isValid"` | |||
Phone string `json:"phone"` | |||
}{} | |||
// 判断是否返回正确 状态码 | |||
if ret.Status == 200 { | |||
decode, _ := base64Decode([]byte(ret.Res.(string))) | |||
decr, _ := desDecrypt(decode, []byte(s.AppSecret)[0:8]) | |||
if err := json.Unmarshal(decr, &res); err != nil { | |||
return "", logx.Warn(err) | |||
} | |||
} | |||
// 有效则拿出res 里的电话号码 | |||
if res.IsValid == 1 { | |||
return res.Phone, nil | |||
} | |||
// Status 不等于200 则返回空 | |||
return "", fmt.Errorf("Mob error , status code %v ", ret.Status) | |||
} | |||
// MobSMS is mob 的短信验证 | |||
func (s *SDK) MobSMS(c *gin.Context, args map[string]interface{}) (bool, error) { | |||
// mob 的短信验证 | |||
// https://www.mob.com/wiki/detailed/?wiki=SMSSDK_for_yanzhengmafuwuduanxiaoyanjiekou&id=23 | |||
url := "https://webapi.sms.mob.com/sms/verify" | |||
//加appkey | |||
args["appkey"] = s.AppKey | |||
fmt.Println(args) | |||
//fmt.Println(args) | |||
// 发送请求 | |||
respBody, err := utils.CurlPost(url, args, nil) | |||
if err != nil { | |||
fmt.Println(err) | |||
return false, logx.Warn(err) | |||
} | |||
fmt.Println("=======================mob") | |||
fmt.Println("mob", string(respBody)) | |||
code := gjson.GetBytes(respBody, "status").Int() | |||
if code == 468 { | |||
return false, errors.New("验证码错误") | |||
} | |||
if code != 200 { | |||
utils.FilePutContents("sms", string(respBody)) | |||
return false, errors.New("验证码错误~") | |||
} | |||
return true, nil | |||
} | |||
func pkcs5UnPadding(origData []byte) []byte { | |||
length := len(origData) | |||
// 去掉最后一个字节 unpadding 次 | |||
unpadding := int(origData[length-1]) | |||
return origData[:(length - unpadding)] | |||
} | |||
func desDecrypt(crypted, key []byte) ([]byte, error) { | |||
block, err := des.NewCipher(key) | |||
if err != nil { | |||
return nil, err | |||
} | |||
blockMode := cipher.NewCBCDecrypter(block, []byte("00000000")) | |||
origData := make([]byte, len(crypted)) | |||
// origData := crypted | |||
blockMode.CryptBlocks(origData, crypted) | |||
origData = pkcs5UnPadding(origData) | |||
// origData = ZeroUnPadding(origData) | |||
return origData, nil | |||
} | |||
func base64Decode(src []byte) ([]byte, error) { | |||
var coder *base64.Encoding | |||
coder = base64.NewEncoding(base) | |||
return coder.DecodeString(string(src)) | |||
} | |||
func httpPostBody(url string, msg []byte) ([]byte, error) { | |||
resp, err := http.Post(url, "application/json;charset=utf-8", bytes.NewBuffer(msg)) | |||
if err != nil { | |||
return []byte(""), err | |||
} | |||
defer resp.Body.Close() | |||
body, err := ioutil.ReadAll(resp.Body) | |||
return body, err | |||
} | |||
func generateSign(request map[string]interface{}, secret string) string { | |||
ret := "" | |||
var keys []string | |||
for k := range request { | |||
keys = append(keys, k) | |||
} | |||
sort.Strings(keys) | |||
for _, k := range keys { | |||
ret = ret + fmt.Sprintf("%v=%v&", k, request[k]) | |||
} | |||
ret = ret[:len(ret)-1] + secret | |||
md5Ctx := md5.New() | |||
md5Ctx.Write([]byte(ret)) | |||
cipherStr := md5Ctx.Sum(nil) | |||
return hex.EncodeToString(cipherStr) | |||
} | |||
func Check(c *gin.Context, phone, zone, validCode string, ok1 bool) (bool, error) { | |||
smsPlatform := sms.GetSmsPlatform(c) | |||
if smsPlatform == "mob" { | |||
mob1, errr := GetMobSDK(c.GetString("master_id")) | |||
if errr != nil { | |||
return false, e.NewErr(400, "mob配置错误") | |||
} | |||
send := map[string]interface{}{ | |||
"phone": phone, | |||
"zone": zone, | |||
"code": validCode, | |||
} | |||
if zone == "" { | |||
send["zone"] = "86" | |||
} | |||
c.Set("not_deduction_doing", "1") | |||
ok, err := mob1.MobSMS(c, send) | |||
fmt.Println(ok) | |||
if err != nil { | |||
fmt.Println(err) | |||
return false, e.NewErr(400, "验证码校验错误") | |||
} | |||
return ok, nil | |||
} | |||
return ok1, nil | |||
} |
@@ -0,0 +1,84 @@ | |||
package sms | |||
import ( | |||
"applet/app/db" | |||
"applet/app/db/model" | |||
"applet/app/lib/zhimeng" | |||
"applet/app/utils" | |||
"applet/app/utils/cache" | |||
"applet/app/utils/logx" | |||
"code.fnuoos.com/go_rely_warehouse/zyos_go_third_party_api.git/sms" | |||
"fmt" | |||
"github.com/tidwall/gjson" | |||
"github.com/gin-gonic/gin" | |||
) | |||
// NewZhimengSMS is 智盟的短信服务 | |||
func NewZhimengSMS(c *gin.Context) *zhimeng.SDK { | |||
sms := new(zhimeng.SDK) | |||
key := db.SysCfgGet(c, "third_zm_sms_key") | |||
secret := db.SysCfgGet(c, "third_zm_sms_secret") | |||
if key == "" || secret == "" { | |||
_ = logx.Warn("短信服务配置错误") | |||
} | |||
sms.Init("send_msg", key, secret) | |||
return sms | |||
} | |||
func GetSmsPlatform(c *gin.Context) string { | |||
var smsPlatform = "ljioe" | |||
key := fmt.Sprintf("%s:sms_platform", c.GetString("master_id")) | |||
smsPlatformTmp, _ := cache.GetString(key) | |||
if smsPlatformTmp == "" { | |||
smsPlatformTmp = GetWebSiteAppSmsPlatform(c.GetString("master_id")) | |||
if smsPlatformTmp != "" { | |||
cache.SetEx(key, smsPlatformTmp, 300) | |||
} | |||
} | |||
if smsPlatformTmp != "" { | |||
smsPlatform = smsPlatformTmp | |||
} | |||
return smsPlatform | |||
} | |||
func GetWebSiteAppSmsPlatform(mid string) string { | |||
obj := new(model.UserAppList) | |||
has, err := db.Db.Where("uuid=? ", mid).Asc("id").Get(obj) | |||
if err != nil || !has { | |||
return "" | |||
} | |||
return utils.AnyToString(obj.SmsPlatform) | |||
} | |||
func GetTplId(c *gin.Context, zone, types string) string { | |||
// 校验短信验证码 | |||
tplId := "" | |||
if zone != "86" { | |||
tplId = db.SysCfgGet(c, "mob_sms_sdk_international_template_id") | |||
} else { | |||
tplId = db.SysCfgGet(c, "mob_sms_sdk_template_id") | |||
} | |||
if c.GetString("app_type") == "o2o" { | |||
tplId = db.SysCfgGet(c, "biz_mob_sms_sdk_template_id") | |||
} | |||
normal := gjson.Get(tplId, types).String() | |||
if normal == "" { | |||
normal = gjson.Get(tplId, "normal").String() | |||
} | |||
return normal | |||
} | |||
func GetSmsConfig(c *gin.Context, zone string, postData map[string]interface{}) error { | |||
postData["is_mob"] = "1" | |||
postData["type"] = "mob" | |||
postData["sms_type"] = "putong" | |||
zone = "86" | |||
postData["templateCode"] = "14373673" | |||
postData["zone"] = zone | |||
postData["smsmsg_key"] = "30dc33054b635" | |||
postData["uid"] = c.GetString("master_id") | |||
err := sms.SmsSendZy(db.Db, postData) | |||
if err != nil { | |||
return err | |||
} | |||
return nil | |||
} |
@@ -0,0 +1,66 @@ | |||
package zhimeng | |||
import ( | |||
"fmt" | |||
"sort" | |||
"strconv" | |||
"time" | |||
"applet/app/utils" | |||
) | |||
var StatusSuc int = 1 | |||
type ZM struct { | |||
AK string | |||
SK string | |||
SMS_AK string | |||
SMS_SK string | |||
} | |||
// 智盟接口, 可以调取京东, 拼多多等 | |||
const ZM_HOST = "http://www.izhim.com/" | |||
var ( | |||
ZM_BASE_URL = ZM_HOST + "?mod=api&act=%s&ctrl=%s" | |||
APP_KEY = "300000001" | |||
SECRET_KEY = "95c347002b2750dbd4b6a03bd4196c18" | |||
SMS_APP_KEY = "300000175" | |||
SMS_SECRET_KEY = "6cf1dcd1820a576ff2cbecbe00d31df2" | |||
) | |||
func Send(act, op string, args map[string]interface{}) ([]byte, error) { | |||
router := fmt.Sprintf(ZM_BASE_URL, act, op) | |||
// args["appkey"] = APP_KEY | |||
args["time"] = strconv.Itoa(int(time.Now().Unix())) | |||
args["sign"] = sign(args, args["secret_key"].(string)) | |||
// b, _ := json.Marshal(args) | |||
// fmt.Println(string(b)) | |||
return utils.CurlPost(router, args, nil) | |||
} | |||
// SMSend is 发送短信用的key 和签名 | |||
func SMSend(act, op, key, secret string, args map[string]interface{}) ([]byte, error) { | |||
router := fmt.Sprintf(ZM_BASE_URL, act, op) | |||
args["appkey"] = key | |||
args["time"] = strconv.Itoa(int(time.Now().Unix())) | |||
args["sign"] = sign(args, secret) | |||
fmt.Println("====短信==", router, args) | |||
return utils.CurlPost(router, args, nil) | |||
} | |||
func sign(m map[string]interface{}, SK string) string { | |||
// key sort | |||
keys := make([]string, 0, len(m)) | |||
for k := range m { | |||
keys = append(keys, k) | |||
} | |||
sort.Strings(keys) | |||
str := "" | |||
for _, k := range keys { | |||
str += k + utils.AnyToString(m[k]) | |||
} | |||
// merge string | |||
str = SK + str + SK + "方诺科技" | |||
return utils.Md5(str) | |||
} |
@@ -0,0 +1,98 @@ | |||
package zhimeng | |||
import ( | |||
"applet/app/utils/logx" | |||
"encoding/json" | |||
"errors" | |||
"fmt" | |||
"github.com/tidwall/gjson" | |||
) | |||
// SDK is zm sdk | |||
type SDK struct { | |||
Action string | |||
operation string | |||
response []byte | |||
SmsKey string | |||
SmsSecret string | |||
data interface{} | |||
err error | |||
} | |||
// ToInterface is data to Interface | |||
func (sdk *SDK) ToInterface() interface{} { | |||
return sdk.data | |||
} | |||
// Init is init action | |||
// In some condition, such as send Sms, need pass sms key and secret after 'action' | |||
func (sdk *SDK) Init(action string, keys ...string) { | |||
sdk.Action = action | |||
//if keys[0] == "" || keys[1] == "" { | |||
// sdk.err = errors.New("智盟短信未配置") | |||
//} | |||
if len(keys) > 1 { | |||
sdk.SmsKey = keys[0] | |||
sdk.SmsSecret = keys[1] | |||
} | |||
} | |||
// SelectFunction is select api with operation | |||
func (sdk *SDK) SelectFunction(operation string) *SDK { | |||
sdk.operation = operation | |||
return sdk | |||
} | |||
// WithSMSArgs is SMS | |||
func (sdk *SDK) WithSMSArgs(args map[string]interface{}) *SDK { | |||
res, err := SMSend(sdk.Action, sdk.operation, sdk.SmsKey, sdk.SmsSecret, args) | |||
if err != nil { | |||
logx.Error(err) | |||
} | |||
sdk.response = res | |||
return sdk | |||
} | |||
// WithArgs is post data to api | |||
func (sdk *SDK) WithArgs(args map[string]interface{}) *SDK { | |||
// args["appkey"] = svc.SysCfgGet(c, md.KEY_CFG_ZM_AK) | |||
// args["secret_key"] = svc.SysCfgGet(c, md.KEY_CFG_ZM_SK) | |||
res, err := Send(sdk.Action, sdk.operation, args) | |||
if err != nil { | |||
logx.Error(err) | |||
} | |||
// for k, v := range args { | |||
// fmt.Printf("%s:%v \n", k, v) | |||
// } | |||
fmt.Println("唯品会请求", args, string(res)) | |||
sdk.response = res | |||
return sdk | |||
} | |||
// Result is response data from api , return interface{} | |||
func (sdk *SDK) Result() (*SDK, error) { | |||
if sdk.err != nil { | |||
return nil, sdk.err | |||
} | |||
tmp := struct { | |||
Msg string `json:"msg"` | |||
Success int `json:"success"` | |||
Data interface{} `json:"data"` | |||
}{} | |||
if err := json.Unmarshal(sdk.response, &tmp); err != nil { | |||
return nil, logx.Error("【Resp】" + string(sdk.response) + ", 【Error】" + err.Error()) | |||
} | |||
if tmp.Success != StatusSuc { | |||
return nil, logx.Error(string(sdk.response)) | |||
} | |||
if gjson.GetBytes(sdk.response, "data").String() == "[]" { | |||
return nil, errors.New("no result") | |||
} | |||
sdk.data = tmp.Data | |||
return sdk, nil | |||
} |
@@ -0,0 +1,70 @@ | |||
package zhimeng | |||
import ( | |||
"applet/app/utils" | |||
"applet/app/utils/logx" | |||
"code.fnuoos.com/go_rely_warehouse/zyos_go_third_party_api.git/sms" | |||
"encoding/json" | |||
"xorm.io/xorm" | |||
) | |||
// 查询数量 | |||
func SmsNum(engine *xorm.Engine, dbName, smsType, appKey, appSecret string) (int, error) { | |||
if smsType == "1" { //新的 | |||
num := sms.SmsNumGetSmsNum(engine, "putong", dbName) | |||
return num, nil | |||
} | |||
params := map[string]interface{}{ | |||
"appkey": appKey, | |||
"secret_key": appSecret, | |||
} | |||
resp, err := Send("send_msg", "msg_num", params) | |||
if err != nil { | |||
return 0, logx.Warn(err) | |||
} | |||
var tmp struct { | |||
Msg string `json:"msg"` | |||
Success int `json:"success"` | |||
Data struct { | |||
Count string `json:"count"` | |||
} `json:"data"` | |||
} | |||
if err = json.Unmarshal(resp, &tmp); err != nil { | |||
return 0, logx.Warn("[resp]: " + string(resp) + ", [err]:" + err.Error()) | |||
} | |||
return utils.StrToInt(tmp.Data.Count), nil | |||
} | |||
func SmsToSend(engine *xorm.Engine, dbName, smsType, appKey, appSecret, content, phone string) (int, error) { | |||
if smsType == "1" { //新的 | |||
args := map[string]interface{}{ | |||
"mobile": phone, | |||
"content": content, | |||
"sms_type": "putong", | |||
"uid": dbName, | |||
} | |||
err := sms.SmsSend(engine, args) | |||
if err != nil { | |||
return 0, err | |||
} | |||
return 1, nil | |||
} | |||
params := map[string]interface{}{ | |||
"appkey": appKey, | |||
"secret_key": appSecret, | |||
"mobile": phone, | |||
"content": content, | |||
} | |||
resp, err := Send("send_msg", "msg_doing", params) | |||
if err != nil { | |||
return 0, logx.Warn(err) | |||
} | |||
var tmp struct { | |||
Msg string `json:"msg"` | |||
Success int `json:"success"` | |||
} | |||
if err = json.Unmarshal(resp, &tmp); err != nil { | |||
return 0, logx.Warn("[resp]: " + string(resp) + ", [err]:" + err.Error()) | |||
} | |||
return tmp.Success, nil | |||
} |
@@ -8,3 +8,11 @@ type LoginReq struct { | |||
type LoginResponse struct { | |||
Token string `json:"token"` | |||
} | |||
type Register struct { | |||
Mobile string `json:"mobile"` | |||
Captcha string `json:"captcha"` | |||
Type string `json:"type"` | |||
Zone string `json:"zone"` | |||
PicCode string `json:"pic_code"` | |||
PicCodeId string `json:"pic_code_id"` | |||
} |
@@ -45,7 +45,16 @@ func AuthJWT(c *gin.Context) { | |||
e.OutErr(c, e.ERR_TOKEN_EXPIRE, errors.New("token 过期已失效")) | |||
return | |||
} | |||
MasterId := claim.MasterId | |||
if strings.Contains(MasterId, "_") { | |||
ex := strings.Split(MasterId, "_") | |||
if len(ex) >= 3 { | |||
MasterId = ex[0] | |||
c.Set("phone", ex[1]) | |||
c.Set("is_system", ex[2]) | |||
} | |||
} | |||
//设置上下文信息 | |||
c.Set("master_id", claim.MasterId) | |||
c.Set("master_id", MasterId) | |||
c.Next() | |||
} |
@@ -11,13 +11,13 @@ import ( | |||
"io/ioutil" | |||
) | |||
//自己实现一个type gin.ResponseWriter interface | |||
// 自己实现一个type gin.ResponseWriter interface | |||
type responseWriter struct { | |||
gin.ResponseWriter | |||
b *bytes.Buffer | |||
} | |||
//重写Write([]byte) (int, error) | |||
// 重写Write([]byte) (int, error) | |||
func (w responseWriter) Write(b []byte) (int, error) { | |||
//向一个bytes.buffer中再写一份数据 | |||
w.b.Write(b) | |||
@@ -99,6 +99,7 @@ func routeZhimeng(r *gin.RouterGroup) { | |||
r.POST("/withdrawal_income", platformHdl.WithdrawalIncome) | |||
r.POST("/withdrawal_bind_alipay", platformHdl.WithdrawalBindAlipay) | |||
r.GET("/sms", platformHdl.Sms) | |||
r.POST("/withdrawal_list", platformHdl.WithdrawalList) | |||
r.POST("/withdrawal_doing", platformHdl.WithdrawalDoing) | |||
r.POST("/withdrawal_output", platformHdl.WithdrawalOutput) | |||
@@ -5,6 +5,7 @@ import ( | |||
"applet/app/db/model" | |||
offical "applet/app/db/official" | |||
"applet/app/e" | |||
"applet/app/lib/mob" | |||
"applet/app/utils" | |||
"applet/app/utils/cache" | |||
"encoding/json" | |||
@@ -321,19 +322,47 @@ func WithdrawalBindAlipay(c *gin.Context) { | |||
e.OutErr(c, 400, e.NewErr(400, "支付宝信息不能为空")) | |||
return | |||
} | |||
masterId, _ := c.Get("master_id") | |||
mid := utils.AnyToString(masterId) | |||
masterDb := db.MasterDb{} | |||
masterDb.Set() | |||
master := masterDb.GetMaster(mid) | |||
master.AlipayName = args["alipay_name"] | |||
master.Alipay = args["alipay"] | |||
update := masterDb.MasterUpdate(master) | |||
if update == false { | |||
e.OutErr(c, 400, e.NewErr(400, "修改失败")) | |||
mob1, errr := mob.GetMobSDK(c.GetString("master_id")) | |||
if errr != nil { | |||
e.OutErr(c, e.ERR_MOB_CONFIG, errr) | |||
return | |||
} | |||
e.OutSuc(c, "success", nil) | |||
if c.GetString("is_system") == "" { | |||
e.OutErr(c, 400, e.NewErr(400, "请重新在后台进入聚合联盟")) | |||
return | |||
} | |||
if c.GetString("is_system") != "1" { | |||
e.OutErr(c, 400, e.NewErr(400, "请使用超管账号设置")) | |||
return | |||
} | |||
send := map[string]interface{}{ | |||
"phone": c.GetString("phone"), | |||
"zone": "86", | |||
"code": args["captcha"], | |||
} | |||
ok, err := mob1.MobSMS(c, send) | |||
if err != nil { | |||
e.OutErr(c, 400, err.Error()) | |||
return | |||
} | |||
ok = true | |||
if ok { | |||
masterId, _ := c.Get("master_id") | |||
mid := utils.AnyToString(masterId) | |||
masterDb := db.MasterDb{} | |||
masterDb.Set() | |||
master := masterDb.GetMaster(mid) | |||
master.AlipayName = args["alipay_name"] | |||
master.Alipay = args["alipay"] | |||
update := masterDb.MasterUpdate(master) | |||
if update == false { | |||
e.OutErr(c, 400, e.NewErr(400, "修改失败")) | |||
return | |||
} | |||
e.OutSuc(c, "success", nil) | |||
return | |||
} | |||
e.OutErr(c, 400, e.NewErr(400, "验证码错误")) | |||
return | |||
} | |||
@@ -4,7 +4,7 @@ go 1.15 | |||
require ( | |||
code.fnuoos.com/go_rely_warehouse/zyos_go_es.git v1.0.1-0.20230707081910-52e70aa52998 | |||
code.fnuoos.com/go_rely_warehouse/zyos_go_third_party_api.git v1.1.21-0.20240628083932-3a853b9ed178 | |||
code.fnuoos.com/go_rely_warehouse/zyos_go_third_party_api.git v1.1.21-0.20240701084733-e197a1ca8795 | |||
github.com/360EntSecGroup-Skylar/excelize v1.4.1 | |||
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5 | |||
github.com/boombuler/barcode v1.0.1 | |||
@@ -15,7 +15,7 @@ import ( | |||
"applet/app/router" | |||
) | |||
//系统初始化 | |||
// 系统初始化 | |||
func init() { | |||
cfg.InitCfg() //配置初始化 | |||
cfg.InitLog() //日志初始化 | |||
@@ -28,6 +28,9 @@ func init() { | |||
if err := db.InitZhimengDB(cfg.ZhimengDB); err != nil { | |||
panic(err) | |||
} | |||
channel := make(chan int, 0) //开辟管道,缓冲为 | |||
go db.InitDBs(channel) | |||
<-channel | |||
} | |||
fmt.Println("init success") | |||