@@ -25,6 +25,7 @@ var ( | |||
Local bool | |||
AppComm *AppCommCfg | |||
WxappletFilepath *WxappletFilepathCfg | |||
ArkID *ArkIDCfg | |||
) | |||
//初始化配置文件,将cfg.yml读入到内存 | |||
@@ -63,4 +64,5 @@ func InitCfg() { | |||
ImBusinessRpc = &conf.ImBusinessRpc | |||
ImLogicRpc = &conf.ImLogicRpc | |||
WxappletFilepath = &conf.WxappletFilepath | |||
ArkID = &conf.ArkID | |||
} |
@@ -2,16 +2,12 @@ package db | |||
import ( | |||
"applet/app/db/model" | |||
"applet/app/e" | |||
"applet/app/utils" | |||
"applet/app/utils/logx" | |||
"errors" | |||
"fmt" | |||
"strconv" | |||
"strings" | |||
"github.com/gin-gonic/gin" | |||
"github.com/tidwall/gjson" | |||
"xorm.io/xorm" | |||
) | |||
@@ -253,281 +249,6 @@ func SysModFindByTemplateIDAndModNameWithIds(Db *xorm.Engine, ids []int, modName | |||
return &m, nil | |||
} | |||
// SysModFindNavIsUsed 查找正在使用的底部导航栏模板 | |||
func SysModFindNavIsUsedByPlatform(c *gin.Context, Db *xorm.Engine, platform string) (*model.SysModule, error) { | |||
var ( | |||
tm model.SysTemplate | |||
m model.SysModule | |||
) | |||
mid := c.GetString("mid") | |||
fmt.Println("===================================app_type", c.GetString("app_type")) | |||
if c.GetString("app_type") != "" && c.GetString("app_type") != "daogou" { | |||
var ( | |||
tempType string | |||
) | |||
switch c.GetString("app_type") { | |||
case "o2o": | |||
tempType = "o2o_store_bottomNav" | |||
} | |||
switch platform { | |||
case "ios": | |||
if has, err := Db.Where("is_use = 1 AND type = ? AND platform = 2 ", tempType). | |||
Cols("id,uid,name,is_use,is_system"). | |||
Get(&tm); err != nil || has == false { | |||
return nil, logx.Warn(err) | |||
} | |||
appVersion := GetCloudBuild(c, platform) | |||
if c.GetHeader("app_version_name") == appVersion && c.GetHeader("app_version_name") != "" { | |||
m, err := GetCloudBundleByVersion(Db, appVersion, 2, 1) | |||
if err != nil { | |||
return nil, logx.Warn(err) | |||
} | |||
tm.Id = int(gjson.Get(m.TemplateDuringAudit, "bottom").Int()) | |||
} | |||
case "android": | |||
has, err := Db.Where("is_use = 1 AND type = ? AND platform = 2 ", tempType).Cols("id,uid,name,is_use,is_system").Get(&tm) | |||
if err != nil || has == false { | |||
return nil, logx.Warn(err) | |||
} | |||
fmt.Println("===================================app_type", tm) | |||
appVersion := GetCloudBuild(c, platform) | |||
fmt.Println("===================================app_type", appVersion) | |||
if appVersion != "" && c.GetHeader("app_version_name") == appVersion { | |||
m, err := GetCloudBundleByVersion(Db, appVersion, 1, 1) | |||
if err != nil { | |||
return nil, logx.Warn(err) | |||
} | |||
fmt.Println("===================================app_type", m) | |||
tm.Id = int(gjson.Get(m.TemplateDuringAudit, "bottom").Int()) | |||
} | |||
case "wx_applet", "wap": | |||
if has, err := Db.Where("is_use = 1 AND type = ? AND platform = 4 ", tempType). | |||
Cols("id,uid,name,is_use,is_system"). | |||
Get(&tm); err != nil || has == false { | |||
return nil, logx.Warn(err) | |||
} | |||
case "baidu_applet": | |||
if has, err := Db.Where("is_use = 1 AND type = ? AND platform = 4 ", tempType). | |||
Cols("id,uid,name,is_use,is_system"). | |||
Get(&tm); err != nil || has == false { | |||
return nil, logx.Warn(err) | |||
} | |||
case "toutiao_applet": | |||
if has, err := Db.Where("is_use = 1 AND type = ? AND platform = 4 ", tempType). | |||
Cols("id,uid,name,is_use,is_system"). | |||
Get(&tm); err != nil || has == false { | |||
return nil, logx.Warn(err) | |||
} | |||
case "alipay_applet": | |||
if has, err := Db.Where("is_use = 1 AND type = ? AND platform = 4 ", tempType). | |||
Cols("id,uid,name,is_use,is_system"). | |||
Get(&tm); err != nil || has == false { | |||
return nil, logx.Warn(err) | |||
} | |||
default: | |||
return &m, errors.New("Platform not support") | |||
} | |||
if has, err := Db.Where("state = 1 AND template_id = ?", tm.Id). | |||
Get(&m); err != nil || has == false { | |||
return nil, logx.Warn(err) | |||
} | |||
fmt.Println("===================================app_type", m) | |||
if tempType == "o2o_store_bottomNav" { | |||
bottomMap := make(map[string]interface{}) | |||
utils.Unserialize([]byte(m.Data), &bottomMap) | |||
list, ok := bottomMap["list"] | |||
if ok { | |||
m.Data = string(utils.MarshalJSONCamelCase2JsonSnakeCase(utils.SerializeStr(list))) | |||
} | |||
} | |||
mm, err := sysModFormat(c, &m) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return mm.(*model.SysModule), nil | |||
} | |||
switch platform { | |||
case "ios": | |||
if has, err := Db.Where("is_use = 1 AND type = 'bottom' AND platform = 2 "). | |||
Cols("id,uid,name,is_use,is_system"). | |||
Get(&tm); err != nil || has == false { | |||
return nil, logx.Warn(err) | |||
} | |||
appVersion := GetCloudBuild(c, platform) | |||
if c.GetHeader("app_version_name") == appVersion && c.GetHeader("app_version_name") != "" { | |||
m, err := GetCloudBundleByVersion(Db, appVersion, 2, 0) | |||
if err != nil { | |||
return nil, logx.Warn(err) | |||
} | |||
tm.Id = int(gjson.Get(m.TemplateDuringAudit, "bottom").Int()) | |||
} | |||
if has, err := Db.Where("state = 1 AND template_id = ? AND mod_name = 'bottom_nav'", tm.Id). | |||
Get(&m); err != nil || has == false { | |||
return nil, logx.Warn(err) | |||
} | |||
mm, err := sysModFormat(c, &m) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return mm.(*model.SysModule), nil | |||
case "android": | |||
has, err := Db.Where("is_use = 1 AND type = 'bottom' AND platform = 2 ").Cols("id,uid,name,is_use,is_system").Get(&tm) | |||
if err != nil || has == false { | |||
return nil, logx.Warn(err) | |||
} | |||
appVersion := GetCloudBuild(c, platform) | |||
if appVersion != "" && c.GetHeader("app_version_name") == appVersion { | |||
m, err := GetCloudBundleByVersion(Db, appVersion, 1, 0) | |||
if err != nil { | |||
return nil, logx.Warn(err) | |||
} | |||
tm.Id = int(gjson.Get(m.TemplateDuringAudit, "bottom").Int()) | |||
} | |||
if has, err := Db.Where("state = 1 AND template_id = ? AND mod_name = 'bottom_nav'", tm.Id). | |||
Get(&m); err != nil || has == false { | |||
return nil, logx.Warn(err) | |||
} | |||
mm, err := sysModFormat(c, &m) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return mm.(*model.SysModule), nil | |||
case "wx_applet", "wap": | |||
wxAppletCfg := GetAppletKey(c, Db) | |||
id := utils.StrToInt(wxAppletCfg["bottom_nav_css_id"]) | |||
if id == 0 { | |||
return nil, e.NewErr(400, "找不到模板配置") | |||
} | |||
if has, err := Db.Where("state = 1 AND template_id = ? AND mod_name = 'bottom_nav'", id). | |||
Get(&m); err != nil || has == false { | |||
return nil, logx.Warn(err) | |||
} | |||
mm, err := sysModFormat(c, &m) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return mm.(*model.SysModule), nil | |||
case "baidu_applet": | |||
if has, err := Db.Where("is_use = 1 AND type = 'bottom' AND platform = 4 "). | |||
Cols("id,uid,name,is_use,is_system"). | |||
Get(&tm); err != nil || has == false { | |||
return nil, logx.Warn(err) | |||
} | |||
appVersion := SysCfgGetWithDb(Db, mid, "baidu_audit_version") | |||
if appVersion != "" && c.GetHeader("app_version_name") == appVersion { | |||
m := SysCfgGetWithDb(Db, mid, "baidu_audit_template") | |||
if m == "" { | |||
return nil, e.NewErr(400, "找不到模板配置") | |||
} | |||
tm.Id = int(gjson.Get(m, "bottom").Int()) | |||
} | |||
if has, err := Db.Where("state = 1 AND template_id = ? AND mod_name = 'bottom_nav'", tm.Id). | |||
Get(&m); err != nil || has == false { | |||
return nil, logx.Warn(err) | |||
} | |||
mm, err := sysModFormat(c, &m) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return mm.(*model.SysModule), nil | |||
case "toutiao_applet": | |||
if has, err := Db.Where("is_use = 1 AND type = 'bottom' AND platform = 4 "). | |||
Cols("id,uid,name,is_use,is_system"). | |||
Get(&tm); err != nil || has == false { | |||
return nil, logx.Warn(err) | |||
} | |||
appVersion := SysCfgGetWithDb(Db, mid, "tt_audit_version") | |||
if appVersion != "" && c.GetHeader("app_version_name") == appVersion { | |||
m := SysCfgGetWithDb(Db, mid, "tt_audit_template") | |||
if m == "" { | |||
return nil, errors.New("找不到模板配置") | |||
} | |||
tm.Id = int(gjson.Get(m, "bottom").Int()) | |||
} | |||
if has, err := Db.Where("state = 1 AND template_id = ? AND mod_name = 'bottom_nav'", tm.Id). | |||
Get(&m); err != nil || has == false { | |||
return nil, logx.Warn(err) | |||
} | |||
mm, err := sysModFormat(c, &m) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return mm.(*model.SysModule), nil | |||
case "alipay_applet": | |||
if has, err := Db.Where("is_use = 1 AND type = 'bottom' AND platform = 4 "). | |||
Cols("id,uid,name,is_use,is_system"). | |||
Get(&tm); err != nil || has == false { | |||
return nil, logx.Warn(err) | |||
} | |||
appVersion := SysCfgGetWithDb(Db, mid, "zfb_audit_version") | |||
if appVersion != "" && c.GetHeader("app_version_name") == appVersion { | |||
m := SysCfgGetWithDb(Db, mid, "zfb_audit_template") | |||
if m == "" { | |||
return nil, errors.New("找不到模板配置") | |||
} | |||
tm.Id = int(gjson.Get(m, "bottom").Int()) | |||
} | |||
if has, err := Db.Where("state = 1 AND template_id = ? AND mod_name = 'bottom_nav'", tm.Id). | |||
Get(&m); err != nil || has == false { | |||
return nil, logx.Warn(err) | |||
} | |||
mm, err := sysModFormat(c, &m) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return mm.(*model.SysModule), nil | |||
default: | |||
return &m, errors.New("Platform not support") | |||
} | |||
} | |||
// O2oBusinessSysModFindNavIsUsedByPlatform O2o商家端小程序查找正在使用的底部导航栏模板 | |||
func O2oBusinessSysModFindNavIsUsedByPlatform(c *gin.Context, Db *xorm.Engine, merchantId string) (*model.O2oBusinessSysModule, error) { | |||
var ( | |||
//tm model.O2oBusinessSysTemplate | |||
m model.O2oBusinessSysModule | |||
//btm model.O2oBusinessSysTemplateAndMerchant | |||
) | |||
kind := "1" | |||
platform := c.GetHeader("platform") | |||
if platform == "wx_applet" { | |||
kind = "bstm.type=2" | |||
} else { | |||
kind = "(bstm.type=1 OR bstm.type=0)" | |||
} | |||
sql := `SELECT * FROM o2o_business_applet_sys_template_and_merchant bstm LEFT JOIN o2o_business_sys_template bst ON bstm.template_id=bst.id WHERE %s` | |||
where := "bstm.merchant_id=" + merchantId + " AND " + kind | |||
sql = fmt.Sprintf(sql, where) | |||
result, err := QueryNativeString(DBs[c.GetString("mid")], sql) | |||
utils.FilePutContents("test123sql", sql) | |||
utils.FilePutContents("test123", utils.SerializeStr(result)) | |||
if len(result) == 0 { | |||
return nil, e.NewErrCode(e.ERR_NO_DATA) | |||
} | |||
if has, err := Db.Where("state = 1 AND template_id = ? AND mod_name = 'bottom'", result[0]["template_id"]). | |||
Get(&m); err != nil || has == false { | |||
return nil, logx.Warn(err) | |||
} | |||
utils.FilePutContents("test1234", utils.SerializeStr(m)) | |||
mm, err := o2oMerchantSysModFormat(c, &m) | |||
utils.FilePutContents("test1235", utils.SerializeStr(mm)) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return mm.(*model.O2oBusinessSysModule), nil | |||
} | |||
// SysModFindBySkipIdentifierAndModName is 根据mod_name和位置 查找对应模块 | |||
func SysModFindBySkipIdentifierAndModName(c *gin.Context, Db *xorm.Engine, name string, modName string) (*model.SysModule, error) { | |||
var m model.SysModule | |||
@@ -667,40 +388,6 @@ func StringByReplace(c *gin.Context, skip string) string { | |||
return skip | |||
} | |||
//公共处理modSkip的链接 首页弹窗 | |||
func SysModSkipByReplace(c *gin.Context, mod *model.SysPopup) *model.SysPopup { | |||
//替换链接的一些参数 | |||
if strings.Contains(mod.Skip, "[replace_APP_URL]") { | |||
mod.Skip = strings.Replace(mod.Skip, "[replace_APP_URL]", c.GetString("domain_wap_base"), -1) | |||
} | |||
if strings.Contains(mod.Skip, "[replace_masterId]") { | |||
mod.Skip = strings.Replace(mod.Skip, "[replace_masterId]", c.GetString("mid"), -1) | |||
} | |||
if strings.Contains(mod.Skip, "[replace_platform]") { | |||
mod.Skip = strings.Replace(mod.Skip, "[replace_platform]", c.GetHeader("Platform"), -1) | |||
} | |||
if strings.Contains(mod.Skip, "优惠卷") { | |||
mod.Skip = strings.Replace(mod.Skip, "优惠卷", "优惠券", -1) | |||
} | |||
if strings.Contains(mod.Skip, "[replace_uid]") { | |||
token := c.GetHeader("Authorization") | |||
// 按空格分割 | |||
parts := strings.SplitN(token, " ", 2) | |||
if len(parts) == 2 && parts[0] == "Bearer" { | |||
// parts[1]是获取到的tokenString,我们使用之前定义好的解析JWT的函数来解析它 | |||
mc, _ := utils.ParseToken(parts[1]) | |||
mod.Skip = strings.Replace(mod.Skip, "[replace_uid]", strconv.Itoa(mc.UID), -1) | |||
} | |||
} | |||
//if strings.Contains(mod.Data, "\"child_category_id") && strings.Contains(mod.Data, "\"category_id") { | |||
// //如果存在这两个字段,要换一下 | |||
// mod.Data = strings.ReplaceAll(mod.Data, "\"category_id", "\"null_category_id") | |||
// mod.Data = strings.ReplaceAll(mod.Data, "\"child_category_id", "\"category_id") | |||
//} | |||
return mod | |||
} | |||
//公共处理modData的链接 | |||
func SysModDataByReplaceSecond(c *gin.Context, mod *model.SysModule) *model.SysModule { | |||
//替换链接的一些参数 | |||
@@ -81,25 +81,6 @@ func GetAllDatabasePrd() *[]model.DbMapping { | |||
logx.Warn(err) | |||
return nil | |||
} | |||
var mm []model.SupplyDbMapping | |||
if err := Db.Where("deleted_at != ? AND is_dev = '0' ", 1).Find(&mm); err != nil || len(mm) == 0 { | |||
logx.Warn(err) | |||
return nil | |||
} | |||
for _, v := range mm { | |||
m = append(m, model.DbMapping{ | |||
DbMasterId: v.DbMasterId, | |||
DbHost: v.DbHost, | |||
DbUsername: v.DbUsername, | |||
DbPassword: v.DbPassword, | |||
DbName: v.DbName, | |||
ExternalMysql: v.ExternalMysql, | |||
IsDev: v.IsDev, | |||
CreatedAt: v.CreatedAt, | |||
UpdatedAt: v.UpdatedAt, | |||
DeletedAt: v.DeletedAt, | |||
}) | |||
} | |||
return &m | |||
} | |||
@@ -0,0 +1,25 @@ | |||
package auth | |||
import ( | |||
"time" | |||
"github.com/dgrijalva/jwt-go" | |||
) | |||
// TokenExpireDuration is jwt 过期时间 | |||
const TokenExpireDuration = time.Hour * 4380 | |||
const RefreshTokenExpireDuration = time.Hour * 4380 | |||
var Secret = []byte("zyos") | |||
// JWTUser 如果想要保存更多信息,都可以添加到这个结构体中 | |||
type JWTUser struct { | |||
UID int `json:"uid"` | |||
Username string `json:"username"` | |||
Phone string `json:"phone"` | |||
AppName string `json:"app_name"` | |||
MiniOpenID string `json:"mini_open_id"` // 小程序的open_id | |||
MiniSK string `json:"mini_session_key"` // 小程序的session_key | |||
jwt.StandardClaims | |||
} |
@@ -0,0 +1,16 @@ | |||
package md | |||
const ( | |||
/* 微信京东小程序 */ | |||
APPLET_JD_ID = "wx91d27dbf599dff74" | |||
APPLET_JD_URL = "pages/union/proxy/proxy?spreadUrl=%s&EA_PTAG=" | |||
/* 唯品会 */ | |||
APPLET_VIP_ID = "wxe9714e742209d35f" | |||
/* 考拉 */ | |||
APPLET_KL_ID = "wx9180a45a676eed94" | |||
APPLET_KL_URL = "package-product/pages/index?dastr=__da_dad3e203_5d016fd6a5b92c00&zkTargetUrl=https%3A%2F%2Fm-goods.kaola.com%2Fproduct%2F{ITEM_ID}.html" | |||
APPLET_KL_ACTIVITY_URL = "package-product/pages/index?dastr=__da_dad3e203_5d016fd6a5b92c00&zkTargetUrl={URL}" | |||
/* 拼多多 */ | |||
APPLET_PDD_ID = "wx32540bd863b27570" | |||
) |
@@ -0,0 +1,8 @@ | |||
package md | |||
type InstantSms struct { | |||
UseNum string `json:"use_num"` | |||
Num string `json:"num"` | |||
LeaveNum string `json:"leave_num"` | |||
EndNum string `json:"end_num"` | |||
} |
@@ -0,0 +1,26 @@ | |||
package md | |||
// 美团 | |||
type OfficialRecommendUrl struct { | |||
Url string // 链接 | |||
WxQrcodeUrl string | |||
WxMiniprogramPath string | |||
AppUrl string | |||
Appid string | |||
Id string //小程序原始id | |||
TaobaoWord string | |||
ClickUrl string | |||
Seq string | |||
Title string | |||
Content string | |||
SuccessContent string | |||
} | |||
type ActivityPlatform struct { | |||
ActivityPlatformId string `json:"activity_id"` | |||
MaterialId string `json:"material_id"` | |||
ActivityPlatformUrl string `json:"url"` | |||
} | |||
type KaolaActivityList struct { | |||
Url string `json:"shareLink"` | |||
} |
@@ -0,0 +1,98 @@ | |||
package md | |||
type OilRequest struct { | |||
ItemName string `json:"item_name"` // 油号 | |||
CityName string `json:"city_name"` // 城市 | |||
Lat string `json:"lat"` // 纬度 | |||
Lng string `json:"lng"` // 经度 | |||
Sort string `json:"sort"` // 排序 | |||
BrandName string `json:"brand_name"` // 品牌 | |||
P string `json:"p"` // 页码 | |||
Mobile string `json:"mobile"` // 手机号 | |||
Id string `json:"id"` // id | |||
CategoryId string `json:"category_id"` //id 多个是逗号隔开 | |||
Pvd string `json:"pvd"` //类型 | |||
IsReturnJh string `json:"is_return_jh"` //是否返回聚合链接 | |||
} | |||
type OilList struct { | |||
Address string `json:"address"` | |||
BrandName string `json:"brand_name"` | |||
CityName string `json:"city_name"` | |||
CostPrice string `json:"cost_price"` | |||
Dis string `json:"dis"` | |||
ID string `json:"id"` | |||
InfoList string `json:"info_list"` | |||
ItemName string `json:"item_name"` | |||
Label []string `json:"label"` | |||
Lat string `json:"lat"` | |||
Lng string `json:"lng"` | |||
Logo string `json:"logo"` | |||
Name string `json:"name"` | |||
OilID string `json:"oil_id"` | |||
StorePrice string `json:"store_price"` | |||
UpdateTime string `json:"update_time"` | |||
VipPrice string `json:"vip_price"` | |||
YhPrice string `json:"yh_price"` | |||
} | |||
type OilListResult struct { | |||
Address string `json:"address"` | |||
CityName string `json:"city_name"` | |||
CostPrice string `json:"cost_price"` | |||
Dis string `json:"dis"` | |||
ID string `json:"id"` | |||
Label []string `json:"label"` | |||
Logo string `json:"logo"` | |||
Name string `json:"name"` | |||
StorePrice string `json:"store_price"` | |||
Lat string `json:"lat"` | |||
Lng string `json:"lng"` | |||
} | |||
type OilDetail struct { | |||
Url string `json:"Url"` | |||
} | |||
type OilCarouselList struct { | |||
Img string `json:"img"` | |||
ImgURL string `json:"img_url"` | |||
Index int `json:"index"` | |||
IsJump string `json:"is_jump"` | |||
IsShow string `json:"is_show"` | |||
Name string `json:"name"` | |||
RequiredLogin string `json:"required_login"` | |||
RequiredTaobaoAuth string `json:"required_taobao_auth"` | |||
SkipIdentifier string `json:"skip_identifier"` | |||
SkipName string `json:"skip_name"` | |||
Data CommModData `json:"data"` | |||
} | |||
type OilStyle struct { | |||
BtnBgColor string `json:"btn_bg_color"` | |||
BtnStrColor string `json:"btn_str_color"` | |||
CarouselList []OilCarouselList `json:"carousel_list"` | |||
HeaderBg struct { | |||
AssistColor string `json:"assist_color"` | |||
MainColor string `json:"main_color"` | |||
MinColor string `json:"min_color"` | |||
} `json:"header_bg"` | |||
NavImg string `json:"nav_img"` | |||
NavImgUrl string `json:"nav_img_url"` | |||
NavStrColor string `json:"nav_str_color"` | |||
PriceStrColor string `json:"price_str_color"` | |||
Title string `json:"title"` | |||
TitleColor string `json:"title_color"` | |||
ItemNameList []struct { | |||
Name string `json:"name"` | |||
Type string `json:"type"` | |||
} `json:"item_name_list"` | |||
BrandNameList []struct { | |||
Name string `json:"name"` | |||
Type string `json:"type"` | |||
} `json:"brand_name_list"` | |||
SortNameList []struct { | |||
Name string `json:"name"` | |||
Type string `json:"type"` | |||
} `json:"sort_name_list"` | |||
} |
@@ -0,0 +1,20 @@ | |||
package md | |||
type GetChargeMoney struct { | |||
PlatformMoney string `json:"platform_money"` | |||
UserMoney string `json:"user_money"` | |||
PhoneChargeDiscount string `json:"phone_charge_discount"` | |||
PhoneChargeAddPrice string `json:"phone_charge_add_price"` | |||
PhoneChargePriceList string `json:"phone_charge_price_list"` | |||
Goods []PhoneGoodsData `json:"goods"` | |||
} | |||
type PhoneGoodsData struct { | |||
Id string `json:"id"` | |||
Capacity string `json:"capacity"` | |||
Price string `json:"price"` | |||
} | |||
type PhoneChargeData struct { | |||
Phone string `json:"phone"` | |||
PhoneInfo string `json:"phone_info"` | |||
List []*PrivilegeCardGoodsNew `json:"list"` | |||
} |
@@ -0,0 +1,222 @@ | |||
package md | |||
const ( | |||
UNPAID = "0" | |||
PAID = "1" | |||
REFUND = "2" | |||
EXPIRE = "3" | |||
CHECKSUCCESS = "4" | |||
) | |||
var ORD_STATE_LIST = map[string]string{ | |||
UNPAID: "未支付", | |||
PAID: "付款成功", | |||
REFUND: "已退款", | |||
EXPIRE: "已失效", | |||
CHECKSUCCESS: "充值成功", | |||
} | |||
type PrivilegeCardGoods struct { | |||
ID string `json:"id"` | |||
GID string `json:"g_id"` | |||
Title string `json:"title"` | |||
OfficialPrice string `json:"official_price"` | |||
PlatPrice string `json:"plat_price"` | |||
Times string `json:"times"` | |||
SpecID string `json:"spec_id"` | |||
SpecImg string `json:"spec_img"` | |||
UpdateTime string `json:"update_time"` | |||
LID string `json:"l_id"` | |||
Type string `json:"type"` | |||
IsDelete string `json:"is_delete"` | |||
TypeID string `json:"type_id"` | |||
IsShowIdnum string `json:"is_show_idnum"` | |||
} | |||
type PrivilegeCardBrand struct { | |||
ID string `json:"id"` | |||
AccountType string `json:"account_type"` | |||
GID string `json:"g_id"` | |||
GoodsLogo string `json:"goods_logo"` | |||
GName string `json:"g_name"` | |||
UpdateTime string `json:"update_time"` | |||
Type string `json:"type"` | |||
Xuzhi string `json:"xuzhi"` | |||
IsDelete string `json:"is_delete"` | |||
TypeID string `json:"type_id"` | |||
IsShowArea string `json:"is_show_area"` | |||
} | |||
//type b struct { | |||
// IsOpen string `json:"is_open"` | |||
// CardName string `json:"card_name"` | |||
// ConditionType string `json:"condition_type"` | |||
// ConditionData `json:"condition_data"` | |||
// BalanceNoticeLimit string `json:"balance_notice_limit"` | |||
// PaymentAvailable []string `json:"payment_available"` | |||
// NewcomersGUIDURL string `json:"newcomers_guid_url"` | |||
//} | |||
type PrivilegeCardCfg struct { | |||
IsOpen string `json:"is_open"` | |||
CardName string `json:"card_name"` | |||
ConditionType string `json:"condition_type"` | |||
ConditionData interface{} `json:"condition_data"` | |||
RegisterCondition struct { | |||
InviteCode struct { | |||
Popup string `json:"popup"` | |||
ShouldInput string `json:"should_input"` | |||
} `json:"invite_code"` | |||
CardKey struct { | |||
Popup string `json:"popup"` | |||
ShouldInput string `json:"should_input"` | |||
} `json:"card_key"` | |||
} `json:"register_condition"` | |||
BalanceNoticeLimit string `json:"balance_notice_limit"` | |||
PaymentAvailable []string `json:"payment_available"` | |||
NewcomersGUIDURL string `json:"newcomers_guid_url"` | |||
} | |||
// PrivilegeCardConditionCfg 2.条件开通 | |||
type PrivilegeCardConditionCfg []struct { | |||
Type string `json:"type"` | |||
Level string `json:"level"` | |||
} | |||
// PrivilegeCardNeedMoneyCfg 3.付费开通 | |||
type PrivilegeCardNeedMoneyCfg struct { | |||
Type string `json:"type"` // 类型:1实体卡 2虚拟卡 | |||
Cost []struct { | |||
DateType string `json:"date_type"` | |||
CostPrice string `json:"cost_price"` | |||
OriginalPrice string `json:"original_price"` | |||
IsOn string `json:"is_on"` | |||
} `json:"cost"` // 价格设置 | |||
GivenData []PrivilegeOpenCardGivenData `json:"given_data"` // 赠送设置 | |||
} | |||
type PrivilegeOpenCardGivenData struct { | |||
Type string `json:"type"` | |||
Data struct { | |||
Level string `json:"level"` | |||
Days string `json:"days"` | |||
IsForever string `json:"is_forever"` | |||
} `json:"data"` | |||
} | |||
type PrivilegeOpenCardCoinCouponData struct { | |||
CouponAmount string `json:"coupon_amount"` | |||
CouponAmountTypeStr string `json:"coupon_amount_type_str"` | |||
CouponAmountOnoff string `json:"coupon_amount_onoff"` | |||
VirtualCoinList []VirtualCoinList `json:"virtual_coin_list"` | |||
VirtualCoinOnoff string `json:"virtual_coin_onoff"` | |||
Days string `json:"days"` | |||
IsForever string `json:"is_forever"` | |||
Level string `json:"level"` | |||
LvOnoff string `json:"lv_onoff"` | |||
IsGiven int `json:"is_given"` | |||
} | |||
type PrivilegeOpenCardLvData struct { | |||
Days string `json:"days"` | |||
IsForever string `json:"is_forever"` | |||
Level string `json:"level"` | |||
LvOnoff string `json:"lv_onoff"` | |||
} | |||
type VirtualCoinList struct { | |||
ID string `json:"id"` | |||
Name string `json:"name"` | |||
Value string `json:"value"` | |||
} | |||
type PrivilegeCardGoodsNew struct { | |||
Id int `json:"id" xorm:"not null pk autoincr INT(11)"` | |||
ZhimengId int `json:"zhimeng_id" xorm:"not null default 0 comment('智盟ID') INT(11)"` | |||
ThirdId int `json:"third_id" xorm:"not null default 0 comment('三方平台ID') INT(11)"` | |||
Name string `json:"name" xorm:"not null default '' comment('商品名称') VARCHAR(255)"` | |||
OldName string `json:"old_name" xorm:"not null default '' comment('商品名称') VARCHAR(255)"` | |||
GoodsImg string `json:"goods_img" xorm:"not null default '' comment('商品图片') VARCHAR(255)"` | |||
BrandId int `json:"brand_id" xorm:"not null default 0 comment('品牌ID') INT(11)"` | |||
Times string `json:"times" xorm:"not null default '' comment('时长') VARCHAR(255)"` | |||
CateId int `json:"cate_id" xorm:"not null default 0 comment('商品分类ID') INT(11)"` | |||
MarkupMode int `json:"markup_mode" xorm:"not null default 0 comment('加价模式 0为利润空间1为进货价') TINYINT(1)"` | |||
MarkupRate int `json:"markup_rate" xorm:"not null default 0 comment('加价比例') DECIMAL(5,2)"` | |||
OfficialPrice string `json:"official_price" xorm:"not null default 0.00 comment('原价') DECIMAL(10,2)"` | |||
PlatformPrice string `json:"platform_price" xorm:"not null default 0.00 comment('进货价') DECIMAL(10,2)"` | |||
FinalPrice string `json:"final_price" xorm:"not null default 0.00 comment('销售价') DECIMAL(10,2)"` | |||
OldFinalPrice string `json:"old_final_price" xorm:"not null default 0.00 comment('销售价') DECIMAL(10,2)"` | |||
Type string `json:"type" xorm:"not null default '' comment('类型') VARCHAR(255)"` | |||
TypeId string `json:"type_id" xorm:"not null default '' comment('类型id') VARCHAR(255)"` | |||
IsShow int `json:"is_show" xorm:"not null default 1 comment('是否显示') TINYINT(1)"` | |||
Sort int `json:"sort" xorm:"default 0 comment('排序') INT(11)"` | |||
CreatedAt int `json:"created_at" xorm:"not null default 0 INT(11)"` | |||
UpdatedAt int `json:"updated_at" xorm:"not null default 0 INT(11)"` | |||
DeletedAt int `json:"deleted_at" xorm:"not null default 0 INT(11)"` | |||
Data string `json:"data" xorm:"not null comment('拓展用') LONGTEXT"` | |||
GoodsSales int `json:"goods_sales" xorm:"not null default 0 INT(11)"` | |||
IsCanBuy string `json:"is_can_buy"` //是否能购买 | |||
IsShowCouponList string `json:"is_show_coupon_list"` //是否展示优惠券弹出 | |||
UserCouponAmount string `json:"user_coupon_amount"` //优惠券额度 | |||
Coupon string `json:"coupon"` // | |||
IsShowIdnum string `json:"is_show_idnum"` | |||
AreaType string `json:"area_type"` | |||
CouponId string `json:"coupon_id"` | |||
PriceStr string `json:"price_str"` | |||
} | |||
type PrivilegeCheckPerm struct { | |||
State bool `json:"state"` | |||
Reason string `json:"reason"` | |||
ConditionType string `json:"condition_type"` | |||
ConditionData interface{} `json:"condition_data"` | |||
BtnStr string `json:"btn_str"` | |||
Skip PrivilegeCheckSkip `json:"skip"` | |||
} | |||
type PrivilegeCheckSkip struct { | |||
Data CommModData `json:"data"` | |||
IsJump string `json:"is_jump"` | |||
Name string `json:"name"` | |||
RequiredLogin string `json:"required_login"` | |||
RequiredTaobaoAuth string `json:"required_taobao_auth"` | |||
SkipIdentifier string `json:"skip_identifier"` | |||
SkipName string `json:"skip_name"` | |||
URL string `json:"url"` | |||
} | |||
type PrivilegeAfterSuccessPopup struct { | |||
BgImg string `json:"bg_img"` | |||
BtnImg string `json:"btn_img"` | |||
Skip SkipData `json:"skip"` | |||
SubTip string `json:"sub_tip"` | |||
Tip string `json:"tip"` | |||
TipType string `json:"tip_type"` | |||
TextTipInfo PrivilegeAfterSuccessPopupText `json:"text_tip_info"` | |||
} | |||
type PrivilegeAfterSuccessPopupText struct { | |||
TopTitle string `json:"title"` | |||
Content string `json:"content"` | |||
SecondContent string `json:"second_content"` | |||
BtnStr string `json:"btn_str"` | |||
} | |||
type PrivilegeCardArea struct { | |||
IsShowDistrict string `json:"is_show_district"` | |||
CountryArea []PrivilegeCardAreaList `json:"country_area"` | |||
SouthArea []PrivilegeCardAreaList `json:"south_area"` | |||
} | |||
type PrivilegeCardAreaList struct { | |||
Id int64 `json:"id"` | |||
Key string `json:"key"` | |||
Value string `json:"value"` | |||
CityList []PrivilegeCardAreaCityList `json:"city_list"` | |||
} | |||
type PrivilegeCardAreaCityList struct { | |||
Id int64 `json:"id"` | |||
Key string `json:"key"` | |||
Value string `json:"value"` | |||
DistrictList []PrivilegeCardAreaDistrictList `json:"district_list"` | |||
} | |||
type PrivilegeCardAreaDistrictList struct { | |||
Id int64 `json:"id"` | |||
Key string `json:"key"` | |||
Value string `json:"value"` | |||
} |
@@ -0,0 +1,11 @@ | |||
package md | |||
// 会员备案信息 | |||
type PublisherInfo struct { | |||
RelationId string // 渠道id | |||
SpecialId string // 会员运营id | |||
ExternalId string // 自定义参数 | |||
Rtag string // 备注 | |||
} |
@@ -0,0 +1,41 @@ | |||
package md | |||
type ExtraData struct { | |||
UnionId string `json:"unionId"` | |||
Tc1 string `json:"tc1"` | |||
Tc2 string `json:"tc2"` | |||
} | |||
// 转链后链接 | |||
type ConvertedUrls struct { | |||
StopStr string `json:"stop_str"` | |||
IsStop string `json:"is_stop"` | |||
IsDown string `json:"is_down"` | |||
Tkl string `json:"tkl"` | |||
ShortTkl string `json:"short_tkl"` | |||
ItemId string `json:"item_id"` | |||
ItemUrl string `json:"item_url"` | |||
NumGoodsId string `json:"num_goods_id"` | |||
URL string `json:"url"` // 短链接 | |||
ShortenURL string `json:"open_app_url"` // 会打开app的长链接 | |||
NoOpenAppURL string `json:"no_open_app_url"` // 不会打开app的长链接 | |||
AppURL string `json:"app_url"` // app 链接 pinduoduo:// | |||
HasCoupon bool `json:"has_coupon"` // 是否有优惠券 | |||
CommissionRate string `json:"commission_rate"` // 利润比例 | |||
ShareURL string `json:"share_url"` // 分享的URL | |||
WeChatTaobaoURL string `json:"wechat_taobao_url"` // 淘宝分享到微信用的url | |||
WeChatMiniURL string `json:"wechat_mini_url"` // 微信小程序转链地址 | |||
WeChatMiniAPPID string `json:"wechat_mini_appid"` // 微信appid | |||
PID string `json:"pid"` // 推广位 | |||
PvdId string `json:"pvd_id"` // 供应商联盟ID | |||
TaoBaoWord string `json:"taobao_word"` // 淘口令TaoBaoWord string `json:"taobao_word"` // 淘口令 | |||
ExtraData ExtraData `json:"extraData"` //考拉用来跟踪用户的 | |||
TbShareId int64 `json:"tb_share_id"` //淘宝活动时的渠道id 组合方式 | |||
IsHasCouponAmount int `json:"is_has_coupon_amount"` //是否有优惠券额度 | |||
ClickUrl string `json:"click_url"` | |||
QrcodeUrl string `json:"qrcode_url"` | |||
Content string `json:"content"` | |||
PvdName string `json:"pvd_name"` | |||
NewPid string `json:"new_pid"` | |||
H5Url string `json:"h5_url"` | |||
} |
@@ -0,0 +1,11 @@ | |||
package md | |||
// 淘口令 | |||
type TaobaoWord struct { | |||
Text string // 文本 | |||
Code string // 短码 | |||
} | |||
type TaobaoWordGid struct { | |||
Gid string // 商品id | |||
} |
@@ -4,7 +4,6 @@ import ( | |||
"applet/app/cfg" | |||
"applet/app/md" | |||
"applet/app/utils/cache" | |||
md2 "applet/consume/md" | |||
"code.fnuoos.com/go_rely_warehouse/zyos_go_mq.git/rabbit" | |||
"fmt" | |||
"github.com/tidwall/gjson" | |||
@@ -233,7 +232,7 @@ func OrderSaveUpdate(eg *xorm.Engine, pvd string, ordData *[]md.OrderInfo, opts | |||
} | |||
eg.Where("id=?", m.Id).Cols("sale,stock").Update(m) | |||
} else if storeOrder != nil && utils.StrToFloat64(storeOrder.DeductPrice) > 0 && storeOrder.DeductState == 1 { | |||
arg := md2.ZhiosGuideStoreOrder{ | |||
arg := md.ZhiosGuideStoreOrder{ | |||
Uid: utils.IntToStr(v.Uid), | |||
Mid: dbName, | |||
Oid: utils.Int64ToStr(v.OrdId), | |||
@@ -0,0 +1,46 @@ | |||
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") | |||
} |
@@ -0,0 +1,421 @@ | |||
package cachesecond | |||
import ( | |||
"errors" | |||
"fmt" | |||
"strconv" | |||
"time" | |||
) | |||
const ( | |||
redisDialTTL = 10 * time.Second | |||
redisReadTTL = 3 * time.Second | |||
redisWriteTTL = 3 * time.Second | |||
redisIdleTTL = 10 * time.Second | |||
redisPoolTTL = 10 * time.Second | |||
redisPoolSize int = 512 | |||
redisMaxIdleConn int = 64 | |||
redisMaxActive int = 512 | |||
) | |||
var ( | |||
ErrNil = errors.New("nil return") | |||
ErrWrongArgsNum = errors.New("args num error") | |||
ErrNegativeInt = errors.New("redis cluster: unexpected value for Uint64") | |||
) | |||
// 以下为提供类型转换 | |||
func Int(reply interface{}, err error) (int, error) { | |||
if err != nil { | |||
return 0, err | |||
} | |||
switch reply := reply.(type) { | |||
case int: | |||
return reply, nil | |||
case int8: | |||
return int(reply), nil | |||
case int16: | |||
return int(reply), nil | |||
case int32: | |||
return int(reply), nil | |||
case int64: | |||
x := int(reply) | |||
if int64(x) != reply { | |||
return 0, strconv.ErrRange | |||
} | |||
return x, nil | |||
case uint: | |||
n := int(reply) | |||
if n < 0 { | |||
return 0, strconv.ErrRange | |||
} | |||
return n, nil | |||
case uint8: | |||
return int(reply), nil | |||
case uint16: | |||
return int(reply), nil | |||
case uint32: | |||
n := int(reply) | |||
if n < 0 { | |||
return 0, strconv.ErrRange | |||
} | |||
return n, nil | |||
case uint64: | |||
n := int(reply) | |||
if n < 0 { | |||
return 0, strconv.ErrRange | |||
} | |||
return n, nil | |||
case []byte: | |||
data := string(reply) | |||
if len(data) == 0 { | |||
return 0, ErrNil | |||
} | |||
n, err := strconv.ParseInt(data, 10, 0) | |||
return int(n), err | |||
case string: | |||
if len(reply) == 0 { | |||
return 0, ErrNil | |||
} | |||
n, err := strconv.ParseInt(reply, 10, 0) | |||
return int(n), err | |||
case nil: | |||
return 0, ErrNil | |||
case error: | |||
return 0, reply | |||
} | |||
return 0, fmt.Errorf("redis cluster: unexpected type for Int, got type %T", reply) | |||
} | |||
func Int64(reply interface{}, err error) (int64, error) { | |||
if err != nil { | |||
return 0, err | |||
} | |||
switch reply := reply.(type) { | |||
case int: | |||
return int64(reply), nil | |||
case int8: | |||
return int64(reply), nil | |||
case int16: | |||
return int64(reply), nil | |||
case int32: | |||
return int64(reply), nil | |||
case int64: | |||
return reply, nil | |||
case uint: | |||
n := int64(reply) | |||
if n < 0 { | |||
return 0, strconv.ErrRange | |||
} | |||
return n, nil | |||
case uint8: | |||
return int64(reply), nil | |||
case uint16: | |||
return int64(reply), nil | |||
case uint32: | |||
return int64(reply), nil | |||
case uint64: | |||
n := int64(reply) | |||
if n < 0 { | |||
return 0, strconv.ErrRange | |||
} | |||
return n, nil | |||
case []byte: | |||
data := string(reply) | |||
if len(data) == 0 { | |||
return 0, ErrNil | |||
} | |||
n, err := strconv.ParseInt(data, 10, 64) | |||
return n, err | |||
case string: | |||
if len(reply) == 0 { | |||
return 0, ErrNil | |||
} | |||
n, err := strconv.ParseInt(reply, 10, 64) | |||
return n, err | |||
case nil: | |||
return 0, ErrNil | |||
case error: | |||
return 0, reply | |||
} | |||
return 0, fmt.Errorf("redis cluster: unexpected type for Int64, got type %T", reply) | |||
} | |||
func Uint64(reply interface{}, err error) (uint64, error) { | |||
if err != nil { | |||
return 0, err | |||
} | |||
switch reply := reply.(type) { | |||
case uint: | |||
return uint64(reply), nil | |||
case uint8: | |||
return uint64(reply), nil | |||
case uint16: | |||
return uint64(reply), nil | |||
case uint32: | |||
return uint64(reply), nil | |||
case uint64: | |||
return reply, nil | |||
case int: | |||
if reply < 0 { | |||
return 0, ErrNegativeInt | |||
} | |||
return uint64(reply), nil | |||
case int8: | |||
if reply < 0 { | |||
return 0, ErrNegativeInt | |||
} | |||
return uint64(reply), nil | |||
case int16: | |||
if reply < 0 { | |||
return 0, ErrNegativeInt | |||
} | |||
return uint64(reply), nil | |||
case int32: | |||
if reply < 0 { | |||
return 0, ErrNegativeInt | |||
} | |||
return uint64(reply), nil | |||
case int64: | |||
if reply < 0 { | |||
return 0, ErrNegativeInt | |||
} | |||
return uint64(reply), nil | |||
case []byte: | |||
data := string(reply) | |||
if len(data) == 0 { | |||
return 0, ErrNil | |||
} | |||
n, err := strconv.ParseUint(data, 10, 64) | |||
return n, err | |||
case string: | |||
if len(reply) == 0 { | |||
return 0, ErrNil | |||
} | |||
n, err := strconv.ParseUint(reply, 10, 64) | |||
return n, err | |||
case nil: | |||
return 0, ErrNil | |||
case error: | |||
return 0, reply | |||
} | |||
return 0, fmt.Errorf("redis cluster: unexpected type for Uint64, got type %T", reply) | |||
} | |||
func Float64(reply interface{}, err error) (float64, error) { | |||
if err != nil { | |||
return 0, err | |||
} | |||
var value float64 | |||
err = nil | |||
switch v := reply.(type) { | |||
case float32: | |||
value = float64(v) | |||
case float64: | |||
value = v | |||
case int: | |||
value = float64(v) | |||
case int8: | |||
value = float64(v) | |||
case int16: | |||
value = float64(v) | |||
case int32: | |||
value = float64(v) | |||
case int64: | |||
value = float64(v) | |||
case uint: | |||
value = float64(v) | |||
case uint8: | |||
value = float64(v) | |||
case uint16: | |||
value = float64(v) | |||
case uint32: | |||
value = float64(v) | |||
case uint64: | |||
value = float64(v) | |||
case []byte: | |||
data := string(v) | |||
if len(data) == 0 { | |||
return 0, ErrNil | |||
} | |||
value, err = strconv.ParseFloat(string(v), 64) | |||
case string: | |||
if len(v) == 0 { | |||
return 0, ErrNil | |||
} | |||
value, err = strconv.ParseFloat(v, 64) | |||
case nil: | |||
err = ErrNil | |||
case error: | |||
err = v | |||
default: | |||
err = fmt.Errorf("redis cluster: unexpected type for Float64, got type %T", v) | |||
} | |||
return value, err | |||
} | |||
func Bool(reply interface{}, err error) (bool, error) { | |||
if err != nil { | |||
return false, err | |||
} | |||
switch reply := reply.(type) { | |||
case bool: | |||
return reply, nil | |||
case int64: | |||
return reply != 0, nil | |||
case []byte: | |||
data := string(reply) | |||
if len(data) == 0 { | |||
return false, ErrNil | |||
} | |||
return strconv.ParseBool(data) | |||
case string: | |||
if len(reply) == 0 { | |||
return false, ErrNil | |||
} | |||
return strconv.ParseBool(reply) | |||
case nil: | |||
return false, ErrNil | |||
case error: | |||
return false, reply | |||
} | |||
return false, fmt.Errorf("redis cluster: unexpected type for Bool, got type %T", reply) | |||
} | |||
func Bytes(reply interface{}, err error) ([]byte, error) { | |||
if err != nil { | |||
return nil, err | |||
} | |||
switch reply := reply.(type) { | |||
case []byte: | |||
if len(reply) == 0 { | |||
return nil, ErrNil | |||
} | |||
return reply, nil | |||
case string: | |||
data := []byte(reply) | |||
if len(data) == 0 { | |||
return nil, ErrNil | |||
} | |||
return data, nil | |||
case nil: | |||
return nil, ErrNil | |||
case error: | |||
return nil, reply | |||
} | |||
return nil, fmt.Errorf("redis cluster: unexpected type for Bytes, got type %T", reply) | |||
} | |||
func String(reply interface{}, err error) (string, error) { | |||
if err != nil { | |||
return "", err | |||
} | |||
value := "" | |||
err = nil | |||
switch v := reply.(type) { | |||
case string: | |||
if len(v) == 0 { | |||
return "", ErrNil | |||
} | |||
value = v | |||
case []byte: | |||
if len(v) == 0 { | |||
return "", ErrNil | |||
} | |||
value = string(v) | |||
case int: | |||
value = strconv.FormatInt(int64(v), 10) | |||
case int8: | |||
value = strconv.FormatInt(int64(v), 10) | |||
case int16: | |||
value = strconv.FormatInt(int64(v), 10) | |||
case int32: | |||
value = strconv.FormatInt(int64(v), 10) | |||
case int64: | |||
value = strconv.FormatInt(v, 10) | |||
case uint: | |||
value = strconv.FormatUint(uint64(v), 10) | |||
case uint8: | |||
value = strconv.FormatUint(uint64(v), 10) | |||
case uint16: | |||
value = strconv.FormatUint(uint64(v), 10) | |||
case uint32: | |||
value = strconv.FormatUint(uint64(v), 10) | |||
case uint64: | |||
value = strconv.FormatUint(v, 10) | |||
case float32: | |||
value = strconv.FormatFloat(float64(v), 'f', -1, 32) | |||
case float64: | |||
value = strconv.FormatFloat(v, 'f', -1, 64) | |||
case bool: | |||
value = strconv.FormatBool(v) | |||
case nil: | |||
err = ErrNil | |||
case error: | |||
err = v | |||
default: | |||
err = fmt.Errorf("redis cluster: unexpected type for String, got type %T", v) | |||
} | |||
return value, err | |||
} | |||
func Strings(reply interface{}, err error) ([]string, error) { | |||
if err != nil { | |||
return nil, err | |||
} | |||
switch reply := reply.(type) { | |||
case []interface{}: | |||
result := make([]string, len(reply)) | |||
for i := range reply { | |||
if reply[i] == nil { | |||
continue | |||
} | |||
switch subReply := reply[i].(type) { | |||
case string: | |||
result[i] = subReply | |||
case []byte: | |||
result[i] = string(subReply) | |||
default: | |||
return nil, fmt.Errorf("redis cluster: unexpected element type for String, got type %T", reply[i]) | |||
} | |||
} | |||
return result, nil | |||
case []string: | |||
return reply, nil | |||
case nil: | |||
return nil, ErrNil | |||
case error: | |||
return nil, reply | |||
} | |||
return nil, fmt.Errorf("redis cluster: unexpected type for Strings, got type %T", reply) | |||
} | |||
func Values(reply interface{}, err error) ([]interface{}, error) { | |||
if err != nil { | |||
return nil, err | |||
} | |||
switch reply := reply.(type) { | |||
case []interface{}: | |||
return reply, nil | |||
case nil: | |||
return nil, ErrNil | |||
case error: | |||
return nil, reply | |||
} | |||
return nil, fmt.Errorf("redis cluster: unexpected type for Values, got type %T", reply) | |||
} |
@@ -0,0 +1,107 @@ | |||
package cache | |||
import ( | |||
"fmt" | |||
"time" | |||
) | |||
var c Cache | |||
type Cache interface { | |||
// get cached value by key. | |||
Get(key string) interface{} | |||
// GetMulti is a batch version of Get. | |||
GetMulti(keys []string) []interface{} | |||
// set cached value with key and expire time. | |||
Put(key string, val interface{}, timeout time.Duration) error | |||
// delete cached value by key. | |||
Delete(key string) error | |||
// increase cached int value by key, as a counter. | |||
Incr(key string) error | |||
// decrease cached int value by key, as a counter. | |||
Decr(key string) error | |||
// check if cached value exists or not. | |||
IsExist(key string) bool | |||
// clear all cache. | |||
ClearAll() error | |||
// start gc routine based on config string settings. | |||
StartAndGC(config string) error | |||
} | |||
// Instance is a function create a new Cache Instance | |||
type Instance func() Cache | |||
var adapters = make(map[string]Instance) | |||
// Register makes a cache adapter available by the adapter name. | |||
// If Register is called twice with the same name or if driver is nil, | |||
// it panics. | |||
func Register(name string, adapter Instance) { | |||
if adapter == nil { | |||
panic("cache: Register adapter is nil") | |||
} | |||
if _, ok := adapters[name]; ok { | |||
panic("cache: Register called twice for adapter " + name) | |||
} | |||
adapters[name] = adapter | |||
} | |||
// NewCache Create a new cache driver by adapter name and config string. | |||
// config need to be correct JSON as string: {"interval":360}. | |||
// it will start gc automatically. | |||
func NewCache(adapterName, config string) (adapter Cache, err error) { | |||
instanceFunc, ok := adapters[adapterName] | |||
if !ok { | |||
err = fmt.Errorf("cache: unknown adapter name %q (forgot to import?)", adapterName) | |||
return | |||
} | |||
adapter = instanceFunc() | |||
err = adapter.StartAndGC(config) | |||
if err != nil { | |||
adapter = nil | |||
} | |||
return | |||
} | |||
func InitCache(adapterName, config string) (err error) { | |||
instanceFunc, ok := adapters[adapterName] | |||
if !ok { | |||
err = fmt.Errorf("cache: unknown adapter name %q (forgot to import?)", adapterName) | |||
return | |||
} | |||
c = instanceFunc() | |||
err = c.StartAndGC(config) | |||
if err != nil { | |||
c = nil | |||
} | |||
return | |||
} | |||
func Get(key string) interface{} { | |||
return c.Get(key) | |||
} | |||
func GetMulti(keys []string) []interface{} { | |||
return c.GetMulti(keys) | |||
} | |||
func Put(key string, val interface{}, ttl time.Duration) error { | |||
return c.Put(key, val, ttl) | |||
} | |||
func Delete(key string) error { | |||
return c.Delete(key) | |||
} | |||
func Incr(key string) error { | |||
return c.Incr(key) | |||
} | |||
func Decr(key string) error { | |||
return c.Decr(key) | |||
} | |||
func IsExist(key string) bool { | |||
return c.IsExist(key) | |||
} | |||
func ClearAll() error { | |||
return c.ClearAll() | |||
} | |||
func StartAndGC(cfg string) error { | |||
return c.StartAndGC(cfg) | |||
} |
@@ -0,0 +1,86 @@ | |||
package cache | |||
import ( | |||
"fmt" | |||
"strconv" | |||
) | |||
// GetString convert interface to string. | |||
func GetString(v interface{}) string { | |||
switch result := v.(type) { | |||
case string: | |||
return result | |||
case []byte: | |||
return string(result) | |||
default: | |||
if v != nil { | |||
return fmt.Sprint(result) | |||
} | |||
} | |||
return "" | |||
} | |||
// GetInt convert interface to int. | |||
func GetInt(v interface{}) int { | |||
switch result := v.(type) { | |||
case int: | |||
return result | |||
case int32: | |||
return int(result) | |||
case int64: | |||
return int(result) | |||
default: | |||
if d := GetString(v); d != "" { | |||
value, _ := strconv.Atoi(d) | |||
return value | |||
} | |||
} | |||
return 0 | |||
} | |||
// GetInt64 convert interface to int64. | |||
func GetInt64(v interface{}) int64 { | |||
switch result := v.(type) { | |||
case int: | |||
return int64(result) | |||
case int32: | |||
return int64(result) | |||
case int64: | |||
return result | |||
default: | |||
if d := GetString(v); d != "" { | |||
value, _ := strconv.ParseInt(d, 10, 64) | |||
return value | |||
} | |||
} | |||
return 0 | |||
} | |||
// GetFloat64 convert interface to float64. | |||
func GetFloat64(v interface{}) float64 { | |||
switch result := v.(type) { | |||
case float64: | |||
return result | |||
default: | |||
if d := GetString(v); d != "" { | |||
value, _ := strconv.ParseFloat(d, 64) | |||
return value | |||
} | |||
} | |||
return 0 | |||
} | |||
// GetBool convert interface to bool. | |||
func GetBool(v interface{}) bool { | |||
switch result := v.(type) { | |||
case bool: | |||
return result | |||
default: | |||
if d := GetString(v); d != "" { | |||
value, _ := strconv.ParseBool(d) | |||
return value | |||
} | |||
} | |||
return false | |||
} |
@@ -0,0 +1,241 @@ | |||
package cache | |||
import ( | |||
"bytes" | |||
"crypto/md5" | |||
"encoding/gob" | |||
"encoding/hex" | |||
"encoding/json" | |||
"fmt" | |||
"io" | |||
"io/ioutil" | |||
"os" | |||
"path/filepath" | |||
"reflect" | |||
"strconv" | |||
"time" | |||
) | |||
// FileCacheItem is basic unit of file cache adapter. | |||
// it contains data and expire time. | |||
type FileCacheItem struct { | |||
Data interface{} | |||
LastAccess time.Time | |||
Expired time.Time | |||
} | |||
// FileCache Config | |||
var ( | |||
FileCachePath = "cache" // cache directory | |||
FileCacheFileSuffix = ".bin" // cache file suffix | |||
FileCacheDirectoryLevel = 2 // cache file deep level if auto generated cache files. | |||
FileCacheEmbedExpiry time.Duration // cache expire time, default is no expire forever. | |||
) | |||
// FileCache is cache adapter for file storage. | |||
type FileCache struct { | |||
CachePath string | |||
FileSuffix string | |||
DirectoryLevel int | |||
EmbedExpiry int | |||
} | |||
// NewFileCache Create new file cache with no config. | |||
// the level and expiry need set in method StartAndGC as config string. | |||
func NewFileCache() Cache { | |||
// return &FileCache{CachePath:FileCachePath, FileSuffix:FileCacheFileSuffix} | |||
return &FileCache{} | |||
} | |||
// StartAndGC will start and begin gc for file cache. | |||
// the config need to be like {CachePath:"/cache","FileSuffix":".bin","DirectoryLevel":2,"EmbedExpiry":0} | |||
func (fc *FileCache) StartAndGC(config string) error { | |||
var cfg map[string]string | |||
json.Unmarshal([]byte(config), &cfg) | |||
if _, ok := cfg["CachePath"]; !ok { | |||
cfg["CachePath"] = FileCachePath | |||
} | |||
if _, ok := cfg["FileSuffix"]; !ok { | |||
cfg["FileSuffix"] = FileCacheFileSuffix | |||
} | |||
if _, ok := cfg["DirectoryLevel"]; !ok { | |||
cfg["DirectoryLevel"] = strconv.Itoa(FileCacheDirectoryLevel) | |||
} | |||
if _, ok := cfg["EmbedExpiry"]; !ok { | |||
cfg["EmbedExpiry"] = strconv.FormatInt(int64(FileCacheEmbedExpiry.Seconds()), 10) | |||
} | |||
fc.CachePath = cfg["CachePath"] | |||
fc.FileSuffix = cfg["FileSuffix"] | |||
fc.DirectoryLevel, _ = strconv.Atoi(cfg["DirectoryLevel"]) | |||
fc.EmbedExpiry, _ = strconv.Atoi(cfg["EmbedExpiry"]) | |||
fc.Init() | |||
return nil | |||
} | |||
// Init will make new dir for file cache if not exist. | |||
func (fc *FileCache) Init() { | |||
if ok, _ := exists(fc.CachePath); !ok { // todo : error handle | |||
_ = os.MkdirAll(fc.CachePath, os.ModePerm) // todo : error handle | |||
} | |||
} | |||
// get cached file name. it's md5 encoded. | |||
func (fc *FileCache) getCacheFileName(key string) string { | |||
m := md5.New() | |||
io.WriteString(m, key) | |||
keyMd5 := hex.EncodeToString(m.Sum(nil)) | |||
cachePath := fc.CachePath | |||
switch fc.DirectoryLevel { | |||
case 2: | |||
cachePath = filepath.Join(cachePath, keyMd5[0:2], keyMd5[2:4]) | |||
case 1: | |||
cachePath = filepath.Join(cachePath, keyMd5[0:2]) | |||
} | |||
if ok, _ := exists(cachePath); !ok { // todo : error handle | |||
_ = os.MkdirAll(cachePath, os.ModePerm) // todo : error handle | |||
} | |||
return filepath.Join(cachePath, fmt.Sprintf("%s%s", keyMd5, fc.FileSuffix)) | |||
} | |||
// Get value from file cache. | |||
// if non-exist or expired, return empty string. | |||
func (fc *FileCache) Get(key string) interface{} { | |||
fileData, err := FileGetContents(fc.getCacheFileName(key)) | |||
if err != nil { | |||
return "" | |||
} | |||
var to FileCacheItem | |||
GobDecode(fileData, &to) | |||
if to.Expired.Before(time.Now()) { | |||
return "" | |||
} | |||
return to.Data | |||
} | |||
// GetMulti gets values from file cache. | |||
// if non-exist or expired, return empty string. | |||
func (fc *FileCache) GetMulti(keys []string) []interface{} { | |||
var rc []interface{} | |||
for _, key := range keys { | |||
rc = append(rc, fc.Get(key)) | |||
} | |||
return rc | |||
} | |||
// Put value into file cache. | |||
// timeout means how long to keep this file, unit of ms. | |||
// if timeout equals FileCacheEmbedExpiry(default is 0), cache this item forever. | |||
func (fc *FileCache) Put(key string, val interface{}, timeout time.Duration) error { | |||
gob.Register(val) | |||
item := FileCacheItem{Data: val} | |||
if timeout == FileCacheEmbedExpiry { | |||
item.Expired = time.Now().Add((86400 * 365 * 10) * time.Second) // ten years | |||
} else { | |||
item.Expired = time.Now().Add(timeout) | |||
} | |||
item.LastAccess = time.Now() | |||
data, err := GobEncode(item) | |||
if err != nil { | |||
return err | |||
} | |||
return FilePutContents(fc.getCacheFileName(key), data) | |||
} | |||
// Delete file cache value. | |||
func (fc *FileCache) Delete(key string) error { | |||
filename := fc.getCacheFileName(key) | |||
if ok, _ := exists(filename); ok { | |||
return os.Remove(filename) | |||
} | |||
return nil | |||
} | |||
// Incr will increase cached int value. | |||
// fc value is saving forever unless Delete. | |||
func (fc *FileCache) Incr(key string) error { | |||
data := fc.Get(key) | |||
var incr int | |||
if reflect.TypeOf(data).Name() != "int" { | |||
incr = 0 | |||
} else { | |||
incr = data.(int) + 1 | |||
} | |||
fc.Put(key, incr, FileCacheEmbedExpiry) | |||
return nil | |||
} | |||
// Decr will decrease cached int value. | |||
func (fc *FileCache) Decr(key string) error { | |||
data := fc.Get(key) | |||
var decr int | |||
if reflect.TypeOf(data).Name() != "int" || data.(int)-1 <= 0 { | |||
decr = 0 | |||
} else { | |||
decr = data.(int) - 1 | |||
} | |||
fc.Put(key, decr, FileCacheEmbedExpiry) | |||
return nil | |||
} | |||
// IsExist check value is exist. | |||
func (fc *FileCache) IsExist(key string) bool { | |||
ret, _ := exists(fc.getCacheFileName(key)) | |||
return ret | |||
} | |||
// ClearAll will clean cached files. | |||
// not implemented. | |||
func (fc *FileCache) ClearAll() error { | |||
return nil | |||
} | |||
// check file exist. | |||
func exists(path string) (bool, error) { | |||
_, err := os.Stat(path) | |||
if err == nil { | |||
return true, nil | |||
} | |||
if os.IsNotExist(err) { | |||
return false, nil | |||
} | |||
return false, err | |||
} | |||
// FileGetContents Get bytes to file. | |||
// if non-exist, create this file. | |||
func FileGetContents(filename string) (data []byte, e error) { | |||
return ioutil.ReadFile(filename) | |||
} | |||
// FilePutContents Put bytes to file. | |||
// if non-exist, create this file. | |||
func FilePutContents(filename string, content []byte) error { | |||
return ioutil.WriteFile(filename, content, os.ModePerm) | |||
} | |||
// GobEncode Gob encodes file cache item. | |||
func GobEncode(data interface{}) ([]byte, error) { | |||
buf := bytes.NewBuffer(nil) | |||
enc := gob.NewEncoder(buf) | |||
err := enc.Encode(data) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return buf.Bytes(), err | |||
} | |||
// GobDecode Gob decodes file cache item. | |||
func GobDecode(data []byte, to *FileCacheItem) error { | |||
buf := bytes.NewBuffer(data) | |||
dec := gob.NewDecoder(buf) | |||
return dec.Decode(&to) | |||
} | |||
func init() { | |||
Register("file", NewFileCache) | |||
} |
@@ -0,0 +1,239 @@ | |||
package cache | |||
import ( | |||
"encoding/json" | |||
"errors" | |||
"sync" | |||
"time" | |||
) | |||
var ( | |||
// DefaultEvery means the clock time of recycling the expired cache items in memory. | |||
DefaultEvery = 60 // 1 minute | |||
) | |||
// MemoryItem store memory cache item. | |||
type MemoryItem struct { | |||
val interface{} | |||
createdTime time.Time | |||
lifespan time.Duration | |||
} | |||
func (mi *MemoryItem) isExpire() bool { | |||
// 0 means forever | |||
if mi.lifespan == 0 { | |||
return false | |||
} | |||
return time.Now().Sub(mi.createdTime) > mi.lifespan | |||
} | |||
// MemoryCache is Memory cache adapter. | |||
// it contains a RW locker for safe map storage. | |||
type MemoryCache struct { | |||
sync.RWMutex | |||
dur time.Duration | |||
items map[string]*MemoryItem | |||
Every int // run an expiration check Every clock time | |||
} | |||
// NewMemoryCache returns a new MemoryCache. | |||
func NewMemoryCache() Cache { | |||
cache := MemoryCache{items: make(map[string]*MemoryItem)} | |||
return &cache | |||
} | |||
// Get cache from memory. | |||
// if non-existed or expired, return nil. | |||
func (bc *MemoryCache) Get(name string) interface{} { | |||
bc.RLock() | |||
defer bc.RUnlock() | |||
if itm, ok := bc.items[name]; ok { | |||
if itm.isExpire() { | |||
return nil | |||
} | |||
return itm.val | |||
} | |||
return nil | |||
} | |||
// GetMulti gets caches from memory. | |||
// if non-existed or expired, return nil. | |||
func (bc *MemoryCache) GetMulti(names []string) []interface{} { | |||
var rc []interface{} | |||
for _, name := range names { | |||
rc = append(rc, bc.Get(name)) | |||
} | |||
return rc | |||
} | |||
// Put cache to memory. | |||
// if lifespan is 0, it will be forever till restart. | |||
func (bc *MemoryCache) Put(name string, value interface{}, lifespan time.Duration) error { | |||
bc.Lock() | |||
defer bc.Unlock() | |||
bc.items[name] = &MemoryItem{ | |||
val: value, | |||
createdTime: time.Now(), | |||
lifespan: lifespan, | |||
} | |||
return nil | |||
} | |||
// Delete cache in memory. | |||
func (bc *MemoryCache) Delete(name string) error { | |||
bc.Lock() | |||
defer bc.Unlock() | |||
if _, ok := bc.items[name]; !ok { | |||
return errors.New("key not exist") | |||
} | |||
delete(bc.items, name) | |||
if _, ok := bc.items[name]; ok { | |||
return errors.New("delete key error") | |||
} | |||
return nil | |||
} | |||
// Incr increase cache counter in memory. | |||
// it supports int,int32,int64,uint,uint32,uint64. | |||
func (bc *MemoryCache) Incr(key string) error { | |||
bc.RLock() | |||
defer bc.RUnlock() | |||
itm, ok := bc.items[key] | |||
if !ok { | |||
return errors.New("key not exist") | |||
} | |||
switch itm.val.(type) { | |||
case int: | |||
itm.val = itm.val.(int) + 1 | |||
case int32: | |||
itm.val = itm.val.(int32) + 1 | |||
case int64: | |||
itm.val = itm.val.(int64) + 1 | |||
case uint: | |||
itm.val = itm.val.(uint) + 1 | |||
case uint32: | |||
itm.val = itm.val.(uint32) + 1 | |||
case uint64: | |||
itm.val = itm.val.(uint64) + 1 | |||
default: | |||
return errors.New("item val is not (u)int (u)int32 (u)int64") | |||
} | |||
return nil | |||
} | |||
// Decr decrease counter in memory. | |||
func (bc *MemoryCache) Decr(key string) error { | |||
bc.RLock() | |||
defer bc.RUnlock() | |||
itm, ok := bc.items[key] | |||
if !ok { | |||
return errors.New("key not exist") | |||
} | |||
switch itm.val.(type) { | |||
case int: | |||
itm.val = itm.val.(int) - 1 | |||
case int64: | |||
itm.val = itm.val.(int64) - 1 | |||
case int32: | |||
itm.val = itm.val.(int32) - 1 | |||
case uint: | |||
if itm.val.(uint) > 0 { | |||
itm.val = itm.val.(uint) - 1 | |||
} else { | |||
return errors.New("item val is less than 0") | |||
} | |||
case uint32: | |||
if itm.val.(uint32) > 0 { | |||
itm.val = itm.val.(uint32) - 1 | |||
} else { | |||
return errors.New("item val is less than 0") | |||
} | |||
case uint64: | |||
if itm.val.(uint64) > 0 { | |||
itm.val = itm.val.(uint64) - 1 | |||
} else { | |||
return errors.New("item val is less than 0") | |||
} | |||
default: | |||
return errors.New("item val is not int int64 int32") | |||
} | |||
return nil | |||
} | |||
// IsExist check cache exist in memory. | |||
func (bc *MemoryCache) IsExist(name string) bool { | |||
bc.RLock() | |||
defer bc.RUnlock() | |||
if v, ok := bc.items[name]; ok { | |||
return !v.isExpire() | |||
} | |||
return false | |||
} | |||
// ClearAll will delete all cache in memory. | |||
func (bc *MemoryCache) ClearAll() error { | |||
bc.Lock() | |||
defer bc.Unlock() | |||
bc.items = make(map[string]*MemoryItem) | |||
return nil | |||
} | |||
// StartAndGC start memory cache. it will check expiration in every clock time. | |||
func (bc *MemoryCache) StartAndGC(config string) error { | |||
var cf map[string]int | |||
json.Unmarshal([]byte(config), &cf) | |||
if _, ok := cf["interval"]; !ok { | |||
cf = make(map[string]int) | |||
cf["interval"] = DefaultEvery | |||
} | |||
dur := time.Duration(cf["interval"]) * time.Second | |||
bc.Every = cf["interval"] | |||
bc.dur = dur | |||
go bc.vacuum() | |||
return nil | |||
} | |||
// check expiration. | |||
func (bc *MemoryCache) vacuum() { | |||
bc.RLock() | |||
every := bc.Every | |||
bc.RUnlock() | |||
if every < 1 { | |||
return | |||
} | |||
for { | |||
<-time.After(bc.dur) | |||
if bc.items == nil { | |||
return | |||
} | |||
if keys := bc.expiredKeys(); len(keys) != 0 { | |||
bc.clearItems(keys) | |||
} | |||
} | |||
} | |||
// expiredKeys returns key list which are expired. | |||
func (bc *MemoryCache) expiredKeys() (keys []string) { | |||
bc.RLock() | |||
defer bc.RUnlock() | |||
for key, itm := range bc.items { | |||
if itm.isExpire() { | |||
keys = append(keys, key) | |||
} | |||
} | |||
return | |||
} | |||
// clearItems removes all the items which key in keys. | |||
func (bc *MemoryCache) clearItems(keys []string) { | |||
bc.Lock() | |||
defer bc.Unlock() | |||
for _, key := range keys { | |||
delete(bc.items, key) | |||
} | |||
} | |||
func init() { | |||
Register("memory", NewMemoryCache) | |||
} |
@@ -0,0 +1,406 @@ | |||
package cachesecond | |||
import ( | |||
"encoding/json" | |||
"errors" | |||
"log" | |||
"strings" | |||
"time" | |||
redigo "github.com/gomodule/redigo/redis" | |||
) | |||
// configuration | |||
type Config struct { | |||
Server string | |||
Password string | |||
MaxIdle int // Maximum number of idle connections in the pool. | |||
// Maximum number of connections allocated by the pool at a given time. | |||
// When zero, there is no limit on the number of connections in the pool. | |||
MaxActive int | |||
// Close connections after remaining idle for this duration. If the value | |||
// is zero, then idle connections are not closed. Applications should set | |||
// the timeout to a value less than the server's timeout. | |||
IdleTimeout time.Duration | |||
// If Wait is true and the pool is at the MaxActive limit, then Get() waits | |||
// for a connection to be returned to the pool before returning. | |||
Wait bool | |||
KeyPrefix string // prefix to all keys; example is "dev environment name" | |||
KeyDelimiter string // delimiter to be used while appending keys; example is ":" | |||
KeyPlaceholder string // placeholder to be parsed using given arguments to obtain a final key; example is "?" | |||
} | |||
var pool *redigo.Pool | |||
var conf *Config | |||
func NewRedis(addr, pwd string) { | |||
if addr == "" { | |||
panic("\nredis connect string cannot be empty\n") | |||
} | |||
pool = &redigo.Pool{ | |||
MaxIdle: redisMaxIdleConn, | |||
IdleTimeout: redisIdleTTL, | |||
MaxActive: redisMaxActive, | |||
// MaxConnLifetime: redisDialTTL, | |||
Wait: true, | |||
Dial: func() (redigo.Conn, error) { | |||
c, err := redigo.Dial("tcp", addr, | |||
redigo.DialConnectTimeout(redisDialTTL), | |||
redigo.DialReadTimeout(redisReadTTL), | |||
redigo.DialWriteTimeout(redisWriteTTL), | |||
) | |||
if err != nil { | |||
log.Println("Redis Dial failed: ", err) | |||
return nil, err | |||
} | |||
if pwd != "" { | |||
c.Send("auth", pwd) | |||
} | |||
return c, err | |||
}, | |||
TestOnBorrow: func(c redigo.Conn, t time.Time) error { | |||
_, err := c.Do("PING") | |||
if err != nil { | |||
log.Println("Unable to ping to redis server:", err) | |||
} | |||
return err | |||
}, | |||
} | |||
conn := pool.Get() | |||
defer conn.Close() | |||
if conn.Err() != nil { | |||
println("\nredis connect " + addr + " error: " + conn.Err().Error()) | |||
} else { | |||
println("\nredis connect " + addr + " success!\n") | |||
} | |||
} | |||
func Do(cmd string, args ...interface{}) (reply interface{}, err error) { | |||
conn := pool.Get() | |||
defer conn.Close() | |||
return conn.Do(cmd, args...) | |||
} | |||
func GetPool() *redigo.Pool { | |||
return pool | |||
} | |||
func ParseKey(key string, vars []string) (string, error) { | |||
arr := strings.Split(key, conf.KeyPlaceholder) | |||
actualKey := "" | |||
if len(arr) != len(vars)+1 { | |||
return "", errors.New("redis/connection.go: Insufficient arguments to parse key") | |||
} else { | |||
for index, val := range arr { | |||
if index == 0 { | |||
actualKey = arr[index] | |||
} else { | |||
actualKey += vars[index-1] + val | |||
} | |||
} | |||
} | |||
return getPrefixedKey(actualKey), nil | |||
} | |||
func getPrefixedKey(key string) string { | |||
return conf.KeyPrefix + conf.KeyDelimiter + key | |||
} | |||
func StripEnvKey(key string) string { | |||
return strings.TrimLeft(key, conf.KeyPrefix+conf.KeyDelimiter) | |||
} | |||
func SplitKey(key string) []string { | |||
return strings.Split(key, conf.KeyDelimiter) | |||
} | |||
func Expire(key string, ttl int) (interface{}, error) { | |||
return Do("EXPIRE", key, ttl) | |||
} | |||
func Persist(key string) (interface{}, error) { | |||
return Do("PERSIST", key) | |||
} | |||
func Del(key string) (interface{}, error) { | |||
return Do("DEL", key) | |||
} | |||
func Set(key string, data interface{}) (interface{}, error) { | |||
// set | |||
return Do("SET", key, data) | |||
} | |||
func SetNX(key string, data interface{}) (interface{}, error) { | |||
return Do("SETNX", key, data) | |||
} | |||
func SetEx(key string, data interface{}, ttl int) (interface{}, error) { | |||
return Do("SETEX", key, ttl, data) | |||
} | |||
func SetJson(key string, data interface{}, ttl int) bool { | |||
c, err := json.Marshal(data) | |||
if err != nil { | |||
return false | |||
} | |||
if ttl < 1 { | |||
_, err = Set(key, c) | |||
} else { | |||
_, err = SetEx(key, c, ttl) | |||
} | |||
if err != nil { | |||
return false | |||
} | |||
return true | |||
} | |||
func GetJson(key string, dst interface{}) error { | |||
b, err := GetBytes(key) | |||
if err != nil { | |||
return err | |||
} | |||
if err = json.Unmarshal(b, dst); err != nil { | |||
return err | |||
} | |||
return nil | |||
} | |||
func Get(key string) (interface{}, error) { | |||
// get | |||
return Do("GET", key) | |||
} | |||
func GetTTL(key string) (time.Duration, error) { | |||
ttl, err := redigo.Int64(Do("TTL", key)) | |||
return time.Duration(ttl) * time.Second, err | |||
} | |||
func GetBytes(key string) ([]byte, error) { | |||
return redigo.Bytes(Do("GET", key)) | |||
} | |||
func GetString(key string) (string, error) { | |||
return redigo.String(Do("GET", key)) | |||
} | |||
func GetStringMap(key string) (map[string]string, error) { | |||
return redigo.StringMap(Do("GET", key)) | |||
} | |||
func GetInt(key string) (int, error) { | |||
return redigo.Int(Do("GET", key)) | |||
} | |||
func GetInt64(key string) (int64, error) { | |||
return redigo.Int64(Do("GET", key)) | |||
} | |||
func GetStringLength(key string) (int, error) { | |||
return redigo.Int(Do("STRLEN", key)) | |||
} | |||
func ZAdd(key string, score float64, data interface{}) (interface{}, error) { | |||
return Do("ZADD", key, score, data) | |||
} | |||
func ZAddNX(key string, score float64, data interface{}) (interface{}, error) { | |||
return Do("ZADD", key, "NX", score, data) | |||
} | |||
func ZRem(key string, data interface{}) (interface{}, error) { | |||
return Do("ZREM", key, data) | |||
} | |||
func ZRange(key string, start int, end int, withScores bool) ([]interface{}, error) { | |||
if withScores { | |||
return redigo.Values(Do("ZRANGE", key, start, end, "WITHSCORES")) | |||
} | |||
return redigo.Values(Do("ZRANGE", key, start, end)) | |||
} | |||
func ZRemRangeByScore(key string, start int64, end int64) ([]interface{}, error) { | |||
return redigo.Values(Do("ZREMRANGEBYSCORE", key, start, end)) | |||
} | |||
func ZCard(setName string) (int64, error) { | |||
return redigo.Int64(Do("ZCARD", setName)) | |||
} | |||
func ZScan(setName string) (int64, error) { | |||
return redigo.Int64(Do("ZCARD", setName)) | |||
} | |||
func SAdd(setName string, data interface{}) (interface{}, error) { | |||
return Do("SADD", setName, data) | |||
} | |||
func SCard(setName string) (int64, error) { | |||
return redigo.Int64(Do("SCARD", setName)) | |||
} | |||
func SIsMember(setName string, data interface{}) (bool, error) { | |||
return redigo.Bool(Do("SISMEMBER", setName, data)) | |||
} | |||
func SMembers(setName string) ([]string, error) { | |||
return redigo.Strings(Do("SMEMBERS", setName)) | |||
} | |||
func SRem(setName string, data interface{}) (interface{}, error) { | |||
return Do("SREM", setName, data) | |||
} | |||
func HSet(key string, HKey string, data interface{}) (interface{}, error) { | |||
return Do("HSET", key, HKey, data) | |||
} | |||
func HGet(key string, HKey string) (interface{}, error) { | |||
return Do("HGET", key, HKey) | |||
} | |||
func HMGet(key string, hashKeys ...string) ([]interface{}, error) { | |||
ret, err := Do("HMGET", key, hashKeys) | |||
if err != nil { | |||
return nil, err | |||
} | |||
reta, ok := ret.([]interface{}) | |||
if !ok { | |||
return nil, errors.New("result not an array") | |||
} | |||
return reta, nil | |||
} | |||
func HMSet(key string, hashKeys []string, vals []interface{}) (interface{}, error) { | |||
if len(hashKeys) == 0 || len(hashKeys) != len(vals) { | |||
var ret interface{} | |||
return ret, errors.New("bad length") | |||
} | |||
input := []interface{}{key} | |||
for i, v := range hashKeys { | |||
input = append(input, v, vals[i]) | |||
} | |||
return Do("HMSET", input...) | |||
} | |||
func HGetString(key string, HKey string) (string, error) { | |||
return redigo.String(Do("HGET", key, HKey)) | |||
} | |||
func HGetFloat(key string, HKey string) (float64, error) { | |||
f, err := redigo.Float64(Do("HGET", key, HKey)) | |||
return f, err | |||
} | |||
func HGetInt(key string, HKey string) (int, error) { | |||
return redigo.Int(Do("HGET", key, HKey)) | |||
} | |||
func HGetInt64(key string, HKey string) (int64, error) { | |||
return redigo.Int64(Do("HGET", key, HKey)) | |||
} | |||
func HGetBool(key string, HKey string) (bool, error) { | |||
return redigo.Bool(Do("HGET", key, HKey)) | |||
} | |||
func HDel(key string, HKey string) (interface{}, error) { | |||
return Do("HDEL", key, HKey) | |||
} | |||
func HGetAll(key string) (map[string]interface{}, error) { | |||
vals, err := redigo.Values(Do("HGETALL", key)) | |||
if err != nil { | |||
return nil, err | |||
} | |||
num := len(vals) / 2 | |||
result := make(map[string]interface{}, num) | |||
for i := 0; i < num; i++ { | |||
key, _ := redigo.String(vals[2*i], nil) | |||
result[key] = vals[2*i+1] | |||
} | |||
return result, nil | |||
} | |||
func FlushAll() bool { | |||
res, _ := redigo.String(Do("FLUSHALL")) | |||
if res == "" { | |||
return false | |||
} | |||
return true | |||
} | |||
// NOTE: Use this in production environment with extreme care. | |||
// Read more here:https://redigo.io/commands/keys | |||
func Keys(pattern string) ([]string, error) { | |||
return redigo.Strings(Do("KEYS", pattern)) | |||
} | |||
func HKeys(key string) ([]string, error) { | |||
return redigo.Strings(Do("HKEYS", key)) | |||
} | |||
func Exists(key string) bool { | |||
count, err := redigo.Int(Do("EXISTS", key)) | |||
if count == 0 || err != nil { | |||
return false | |||
} | |||
return true | |||
} | |||
func Incr(key string) (int64, error) { | |||
return redigo.Int64(Do("INCR", key)) | |||
} | |||
func Decr(key string) (int64, error) { | |||
return redigo.Int64(Do("DECR", key)) | |||
} | |||
func IncrBy(key string, incBy int64) (int64, error) { | |||
return redigo.Int64(Do("INCRBY", key, incBy)) | |||
} | |||
func DecrBy(key string, decrBy int64) (int64, error) { | |||
return redigo.Int64(Do("DECRBY", key)) | |||
} | |||
func IncrByFloat(key string, incBy float64) (float64, error) { | |||
return redigo.Float64(Do("INCRBYFLOAT", key, incBy)) | |||
} | |||
func DecrByFloat(key string, decrBy float64) (float64, error) { | |||
return redigo.Float64(Do("DECRBYFLOAT", key, decrBy)) | |||
} | |||
// use for message queue | |||
func LPush(key string, data interface{}) (interface{}, error) { | |||
// set | |||
return Do("LPUSH", key, data) | |||
} | |||
func LPop(key string) (interface{}, error) { | |||
return Do("LPOP", key) | |||
} | |||
func LPopString(key string) (string, error) { | |||
return redigo.String(Do("LPOP", key)) | |||
} | |||
func LPopFloat(key string) (float64, error) { | |||
f, err := redigo.Float64(Do("LPOP", key)) | |||
return f, err | |||
} | |||
func LPopInt(key string) (int, error) { | |||
return redigo.Int(Do("LPOP", key)) | |||
} | |||
func LPopInt64(key string) (int64, error) { | |||
return redigo.Int64(Do("LPOP", key)) | |||
} | |||
func RPush(key string, data interface{}) (interface{}, error) { | |||
// set | |||
return Do("RPUSH", key, data) | |||
} | |||
func RPop(key string) (interface{}, error) { | |||
return Do("RPOP", key) | |||
} | |||
func RPopString(key string) (string, error) { | |||
return redigo.String(Do("RPOP", key)) | |||
} | |||
func RPopFloat(key string) (float64, error) { | |||
f, err := redigo.Float64(Do("RPOP", key)) | |||
return f, err | |||
} | |||
func RPopInt(key string) (int, error) { | |||
return redigo.Int(Do("RPOP", key)) | |||
} | |||
func RPopInt64(key string) (int64, error) { | |||
return redigo.Int64(Do("RPOP", key)) | |||
} | |||
func Scan(cursor int64, pattern string, count int64) (int64, []string, error) { | |||
var items []string | |||
var newCursor int64 | |||
values, err := redigo.Values(Do("SCAN", cursor, "MATCH", pattern, "COUNT", count)) | |||
if err != nil { | |||
return 0, nil, err | |||
} | |||
values, err = redigo.Scan(values, &newCursor, &items) | |||
if err != nil { | |||
return 0, nil, err | |||
} | |||
return newCursor, items, nil | |||
} |
@@ -0,0 +1,622 @@ | |||
package cachesecond | |||
import ( | |||
"strconv" | |||
"time" | |||
"github.com/go-redis/redis" | |||
) | |||
var pools *redis.ClusterClient | |||
func NewRedisCluster(addrs []string) error { | |||
opt := &redis.ClusterOptions{ | |||
Addrs: addrs, | |||
PoolSize: redisPoolSize, | |||
PoolTimeout: redisPoolTTL, | |||
IdleTimeout: redisIdleTTL, | |||
DialTimeout: redisDialTTL, | |||
ReadTimeout: redisReadTTL, | |||
WriteTimeout: redisWriteTTL, | |||
} | |||
pools = redis.NewClusterClient(opt) | |||
if err := pools.Ping().Err(); err != nil { | |||
return err | |||
} | |||
return nil | |||
} | |||
func RCGet(key string) (interface{}, error) { | |||
res, err := pools.Get(key).Result() | |||
if err != nil { | |||
return nil, convertError(err) | |||
} | |||
return []byte(res), nil | |||
} | |||
func RCSet(key string, value interface{}) error { | |||
err := pools.Set(key, value, 0).Err() | |||
return convertError(err) | |||
} | |||
func RCGetSet(key string, value interface{}) (interface{}, error) { | |||
res, err := pools.GetSet(key, value).Result() | |||
if err != nil { | |||
return nil, convertError(err) | |||
} | |||
return []byte(res), nil | |||
} | |||
func RCSetNx(key string, value interface{}) (int64, error) { | |||
res, err := pools.SetNX(key, value, 0).Result() | |||
if err != nil { | |||
return 0, convertError(err) | |||
} | |||
if res { | |||
return 1, nil | |||
} | |||
return 0, nil | |||
} | |||
func RCSetEx(key string, value interface{}, timeout int64) error { | |||
_, err := pools.Set(key, value, time.Duration(timeout)*time.Second).Result() | |||
if err != nil { | |||
return convertError(err) | |||
} | |||
return nil | |||
} | |||
// nil表示成功,ErrNil表示数据库内已经存在这个key,其他表示数据库发生错误 | |||
func RCSetNxEx(key string, value interface{}, timeout int64) error { | |||
res, err := pools.SetNX(key, value, time.Duration(timeout)*time.Second).Result() | |||
if err != nil { | |||
return convertError(err) | |||
} | |||
if res { | |||
return nil | |||
} | |||
return ErrNil | |||
} | |||
func RCMGet(keys ...string) ([]interface{}, error) { | |||
res, err := pools.MGet(keys...).Result() | |||
return res, convertError(err) | |||
} | |||
// 为确保多个key映射到同一个slot,每个key最好加上hash tag,如:{test} | |||
func RCMSet(kvs map[string]interface{}) error { | |||
pairs := make([]string, 0, len(kvs)*2) | |||
for k, v := range kvs { | |||
val, err := String(v, nil) | |||
if err != nil { | |||
return err | |||
} | |||
pairs = append(pairs, k, val) | |||
} | |||
return convertError(pools.MSet(pairs).Err()) | |||
} | |||
// 为确保多个key映射到同一个slot,每个key最好加上hash tag,如:{test} | |||
func RCMSetNX(kvs map[string]interface{}) (bool, error) { | |||
pairs := make([]string, 0, len(kvs)*2) | |||
for k, v := range kvs { | |||
val, err := String(v, nil) | |||
if err != nil { | |||
return false, err | |||
} | |||
pairs = append(pairs, k, val) | |||
} | |||
res, err := pools.MSetNX(pairs).Result() | |||
return res, convertError(err) | |||
} | |||
func RCExpireAt(key string, timestamp int64) (int64, error) { | |||
res, err := pools.ExpireAt(key, time.Unix(timestamp, 0)).Result() | |||
if err != nil { | |||
return 0, convertError(err) | |||
} | |||
if res { | |||
return 1, nil | |||
} | |||
return 0, nil | |||
} | |||
func RCDel(keys ...string) (int64, error) { | |||
args := make([]interface{}, 0, len(keys)) | |||
for _, key := range keys { | |||
args = append(args, key) | |||
} | |||
res, err := pools.Del(keys...).Result() | |||
if err != nil { | |||
return res, convertError(err) | |||
} | |||
return res, nil | |||
} | |||
func RCIncr(key string) (int64, error) { | |||
res, err := pools.Incr(key).Result() | |||
if err != nil { | |||
return res, convertError(err) | |||
} | |||
return res, nil | |||
} | |||
func RCIncrBy(key string, delta int64) (int64, error) { | |||
res, err := pools.IncrBy(key, delta).Result() | |||
if err != nil { | |||
return res, convertError(err) | |||
} | |||
return res, nil | |||
} | |||
func RCExpire(key string, duration int64) (int64, error) { | |||
res, err := pools.Expire(key, time.Duration(duration)*time.Second).Result() | |||
if err != nil { | |||
return 0, convertError(err) | |||
} | |||
if res { | |||
return 1, nil | |||
} | |||
return 0, nil | |||
} | |||
func RCExists(key string) (bool, error) { | |||
res, err := pools.Exists(key).Result() | |||
if err != nil { | |||
return false, convertError(err) | |||
} | |||
if res > 0 { | |||
return true, nil | |||
} | |||
return false, nil | |||
} | |||
func RCHGet(key string, field string) (interface{}, error) { | |||
res, err := pools.HGet(key, field).Result() | |||
if err != nil { | |||
return nil, convertError(err) | |||
} | |||
return []byte(res), nil | |||
} | |||
func RCHLen(key string) (int64, error) { | |||
res, err := pools.HLen(key).Result() | |||
if err != nil { | |||
return res, convertError(err) | |||
} | |||
return res, nil | |||
} | |||
func RCHSet(key string, field string, val interface{}) error { | |||
value, err := String(val, nil) | |||
if err != nil && err != ErrNil { | |||
return err | |||
} | |||
_, err = pools.HSet(key, field, value).Result() | |||
if err != nil { | |||
return convertError(err) | |||
} | |||
return nil | |||
} | |||
func RCHDel(key string, fields ...string) (int64, error) { | |||
args := make([]interface{}, 0, len(fields)+1) | |||
args = append(args, key) | |||
for _, field := range fields { | |||
args = append(args, field) | |||
} | |||
res, err := pools.HDel(key, fields...).Result() | |||
if err != nil { | |||
return 0, convertError(err) | |||
} | |||
return res, nil | |||
} | |||
func RCHMGet(key string, fields ...string) (interface{}, error) { | |||
args := make([]interface{}, 0, len(fields)+1) | |||
args = append(args, key) | |||
for _, field := range fields { | |||
args = append(args, field) | |||
} | |||
if len(fields) == 0 { | |||
return nil, ErrNil | |||
} | |||
res, err := pools.HMGet(key, fields...).Result() | |||
if err != nil { | |||
return nil, convertError(err) | |||
} | |||
return res, nil | |||
} | |||
func RCHMSet(key string, kvs ...interface{}) error { | |||
if len(kvs) == 0 { | |||
return nil | |||
} | |||
if len(kvs)%2 != 0 { | |||
return ErrWrongArgsNum | |||
} | |||
var err error | |||
v := map[string]interface{}{} // todo change | |||
v["field"], err = String(kvs[0], nil) | |||
if err != nil && err != ErrNil { | |||
return err | |||
} | |||
v["value"], err = String(kvs[1], nil) | |||
if err != nil && err != ErrNil { | |||
return err | |||
} | |||
pairs := make([]string, 0, len(kvs)-2) | |||
if len(kvs) > 2 { | |||
for _, kv := range kvs[2:] { | |||
kvString, err := String(kv, nil) | |||
if err != nil && err != ErrNil { | |||
return err | |||
} | |||
pairs = append(pairs, kvString) | |||
} | |||
} | |||
v["paris"] = pairs | |||
_, err = pools.HMSet(key, v).Result() | |||
if err != nil { | |||
return convertError(err) | |||
} | |||
return nil | |||
} | |||
func RCHKeys(key string) ([]string, error) { | |||
res, err := pools.HKeys(key).Result() | |||
if err != nil { | |||
return res, convertError(err) | |||
} | |||
return res, nil | |||
} | |||
func RCHVals(key string) ([]interface{}, error) { | |||
res, err := pools.HVals(key).Result() | |||
if err != nil { | |||
return nil, convertError(err) | |||
} | |||
rs := make([]interface{}, 0, len(res)) | |||
for _, res := range res { | |||
rs = append(rs, res) | |||
} | |||
return rs, nil | |||
} | |||
func RCHGetAll(key string) (map[string]string, error) { | |||
vals, err := pools.HGetAll(key).Result() | |||
if err != nil { | |||
return nil, convertError(err) | |||
} | |||
return vals, nil | |||
} | |||
func RCHIncrBy(key, field string, delta int64) (int64, error) { | |||
res, err := pools.HIncrBy(key, field, delta).Result() | |||
if err != nil { | |||
return res, convertError(err) | |||
} | |||
return res, nil | |||
} | |||
func RCZAdd(key string, kvs ...interface{}) (int64, error) { | |||
args := make([]interface{}, 0, len(kvs)+1) | |||
args = append(args, key) | |||
args = append(args, kvs...) | |||
if len(kvs) == 0 { | |||
return 0, nil | |||
} | |||
if len(kvs)%2 != 0 { | |||
return 0, ErrWrongArgsNum | |||
} | |||
zs := make([]redis.Z, len(kvs)/2) | |||
for i := 0; i < len(kvs); i += 2 { | |||
idx := i / 2 | |||
score, err := Float64(kvs[i], nil) | |||
if err != nil && err != ErrNil { | |||
return 0, err | |||
} | |||
zs[idx].Score = score | |||
zs[idx].Member = kvs[i+1] | |||
} | |||
res, err := pools.ZAdd(key, zs...).Result() | |||
if err != nil { | |||
return res, convertError(err) | |||
} | |||
return res, nil | |||
} | |||
func RCZRem(key string, members ...string) (int64, error) { | |||
args := make([]interface{}, 0, len(members)) | |||
args = append(args, key) | |||
for _, member := range members { | |||
args = append(args, member) | |||
} | |||
res, err := pools.ZRem(key, members).Result() | |||
if err != nil { | |||
return res, convertError(err) | |||
} | |||
return res, err | |||
} | |||
func RCZRange(key string, min, max int64, withScores bool) (interface{}, error) { | |||
res := make([]interface{}, 0) | |||
if withScores { | |||
zs, err := pools.ZRangeWithScores(key, min, max).Result() | |||
if err != nil { | |||
return nil, convertError(err) | |||
} | |||
for _, z := range zs { | |||
res = append(res, z.Member, strconv.FormatFloat(z.Score, 'f', -1, 64)) | |||
} | |||
} else { | |||
ms, err := pools.ZRange(key, min, max).Result() | |||
if err != nil { | |||
return nil, convertError(err) | |||
} | |||
for _, m := range ms { | |||
res = append(res, m) | |||
} | |||
} | |||
return res, nil | |||
} | |||
func RCZRangeByScoreWithScore(key string, min, max int64) (map[string]int64, error) { | |||
opt := new(redis.ZRangeBy) | |||
opt.Min = strconv.FormatInt(int64(min), 10) | |||
opt.Max = strconv.FormatInt(int64(max), 10) | |||
opt.Count = -1 | |||
opt.Offset = 0 | |||
vals, err := pools.ZRangeByScoreWithScores(key, *opt).Result() | |||
if err != nil { | |||
return nil, convertError(err) | |||
} | |||
res := make(map[string]int64, len(vals)) | |||
for _, val := range vals { | |||
key, err := String(val.Member, nil) | |||
if err != nil && err != ErrNil { | |||
return nil, err | |||
} | |||
res[key] = int64(val.Score) | |||
} | |||
return res, nil | |||
} | |||
func RCLRange(key string, start, stop int64) (interface{}, error) { | |||
res, err := pools.LRange(key, start, stop).Result() | |||
if err != nil { | |||
return nil, convertError(err) | |||
} | |||
return res, nil | |||
} | |||
func RCLSet(key string, index int, value interface{}) error { | |||
err := pools.LSet(key, int64(index), value).Err() | |||
return convertError(err) | |||
} | |||
func RCLLen(key string) (int64, error) { | |||
res, err := pools.LLen(key).Result() | |||
if err != nil { | |||
return res, convertError(err) | |||
} | |||
return res, nil | |||
} | |||
func RCLRem(key string, count int, value interface{}) (int, error) { | |||
val, _ := value.(string) | |||
res, err := pools.LRem(key, int64(count), val).Result() | |||
if err != nil { | |||
return int(res), convertError(err) | |||
} | |||
return int(res), nil | |||
} | |||
func RCTTl(key string) (int64, error) { | |||
duration, err := pools.TTL(key).Result() | |||
if err != nil { | |||
return int64(duration.Seconds()), convertError(err) | |||
} | |||
return int64(duration.Seconds()), nil | |||
} | |||
func RCLPop(key string) (interface{}, error) { | |||
res, err := pools.LPop(key).Result() | |||
if err != nil { | |||
return nil, convertError(err) | |||
} | |||
return res, nil | |||
} | |||
func RCRPop(key string) (interface{}, error) { | |||
res, err := pools.RPop(key).Result() | |||
if err != nil { | |||
return nil, convertError(err) | |||
} | |||
return res, nil | |||
} | |||
func RCBLPop(key string, timeout int) (interface{}, error) { | |||
res, err := pools.BLPop(time.Duration(timeout)*time.Second, key).Result() | |||
if err != nil { | |||
// 兼容redis 2.x | |||
if err == redis.Nil { | |||
return nil, ErrNil | |||
} | |||
return nil, err | |||
} | |||
return res[1], nil | |||
} | |||
func RCBRPop(key string, timeout int) (interface{}, error) { | |||
res, err := pools.BRPop(time.Duration(timeout)*time.Second, key).Result() | |||
if err != nil { | |||
// 兼容redis 2.x | |||
if err == redis.Nil { | |||
return nil, ErrNil | |||
} | |||
return nil, convertError(err) | |||
} | |||
return res[1], nil | |||
} | |||
func RCLPush(key string, value ...interface{}) error { | |||
args := make([]interface{}, 0, len(value)+1) | |||
args = append(args, key) | |||
args = append(args, value...) | |||
vals := make([]string, 0, len(value)) | |||
for _, v := range value { | |||
val, err := String(v, nil) | |||
if err != nil && err != ErrNil { | |||
return err | |||
} | |||
vals = append(vals, val) | |||
} | |||
_, err := pools.LPush(key, vals).Result() // todo ... | |||
if err != nil { | |||
return convertError(err) | |||
} | |||
return nil | |||
} | |||
func RCRPush(key string, value ...interface{}) error { | |||
args := make([]interface{}, 0, len(value)+1) | |||
args = append(args, key) | |||
args = append(args, value...) | |||
vals := make([]string, 0, len(value)) | |||
for _, v := range value { | |||
val, err := String(v, nil) | |||
if err != nil && err != ErrNil { | |||
if err == ErrNil { | |||
continue | |||
} | |||
return err | |||
} | |||
if val == "" { | |||
continue | |||
} | |||
vals = append(vals, val) | |||
} | |||
_, err := pools.RPush(key, vals).Result() // todo ... | |||
if err != nil { | |||
return convertError(err) | |||
} | |||
return nil | |||
} | |||
// 为确保srcKey跟destKey映射到同一个slot,srcKey和destKey需要加上hash tag,如:{test} | |||
func RCBRPopLPush(srcKey string, destKey string, timeout int) (interface{}, error) { | |||
res, err := pools.BRPopLPush(srcKey, destKey, time.Duration(timeout)*time.Second).Result() | |||
if err != nil { | |||
return nil, convertError(err) | |||
} | |||
return res, nil | |||
} | |||
// 为确保srcKey跟destKey映射到同一个slot,srcKey和destKey需要加上hash tag,如:{test} | |||
func RCRPopLPush(srcKey string, destKey string) (interface{}, error) { | |||
res, err := pools.RPopLPush(srcKey, destKey).Result() | |||
if err != nil { | |||
return nil, convertError(err) | |||
} | |||
return res, nil | |||
} | |||
func RCSAdd(key string, members ...interface{}) (int64, error) { | |||
args := make([]interface{}, 0, len(members)+1) | |||
args = append(args, key) | |||
args = append(args, members...) | |||
ms := make([]string, 0, len(members)) | |||
for _, member := range members { | |||
m, err := String(member, nil) | |||
if err != nil && err != ErrNil { | |||
return 0, err | |||
} | |||
ms = append(ms, m) | |||
} | |||
res, err := pools.SAdd(key, ms).Result() // todo ... | |||
if err != nil { | |||
return res, convertError(err) | |||
} | |||
return res, nil | |||
} | |||
func RCSPop(key string) ([]byte, error) { | |||
res, err := pools.SPop(key).Result() | |||
if err != nil { | |||
return nil, convertError(err) | |||
} | |||
return []byte(res), nil | |||
} | |||
func RCSIsMember(key string, member interface{}) (bool, error) { | |||
m, _ := member.(string) | |||
res, err := pools.SIsMember(key, m).Result() | |||
if err != nil { | |||
return res, convertError(err) | |||
} | |||
return res, nil | |||
} | |||
func RCSRem(key string, members ...interface{}) (int64, error) { | |||
args := make([]interface{}, 0, len(members)+1) | |||
args = append(args, key) | |||
args = append(args, members...) | |||
ms := make([]string, 0, len(members)) | |||
for _, member := range members { | |||
m, err := String(member, nil) | |||
if err != nil && err != ErrNil { | |||
return 0, err | |||
} | |||
ms = append(ms, m) | |||
} | |||
res, err := pools.SRem(key, ms).Result() // todo ... | |||
if err != nil { | |||
return res, convertError(err) | |||
} | |||
return res, nil | |||
} | |||
func RCSMembers(key string) ([]string, error) { | |||
res, err := pools.SMembers(key).Result() | |||
if err != nil { | |||
return nil, convertError(err) | |||
} | |||
return res, nil | |||
} | |||
func RCScriptLoad(luaScript string) (interface{}, error) { | |||
res, err := pools.ScriptLoad(luaScript).Result() | |||
if err != nil { | |||
return nil, convertError(err) | |||
} | |||
return res, nil | |||
} | |||
func RCEvalSha(sha1 string, numberKeys int, keysArgs ...interface{}) (interface{}, error) { | |||
vals := make([]interface{}, 0, len(keysArgs)+2) | |||
vals = append(vals, sha1, numberKeys) | |||
vals = append(vals, keysArgs...) | |||
keys := make([]string, 0, numberKeys) | |||
args := make([]string, 0, len(keysArgs)-numberKeys) | |||
for i, value := range keysArgs { | |||
val, err := String(value, nil) | |||
if err != nil && err != ErrNil { | |||
return nil, err | |||
} | |||
if i < numberKeys { | |||
keys = append(keys, val) | |||
} else { | |||
args = append(args, val) | |||
} | |||
} | |||
res, err := pools.EvalSha(sha1, keys, args).Result() | |||
if err != nil { | |||
return nil, convertError(err) | |||
} | |||
return res, nil | |||
} | |||
func RCEval(luaScript string, numberKeys int, keysArgs ...interface{}) (interface{}, error) { | |||
vals := make([]interface{}, 0, len(keysArgs)+2) | |||
vals = append(vals, luaScript, numberKeys) | |||
vals = append(vals, keysArgs...) | |||
keys := make([]string, 0, numberKeys) | |||
args := make([]string, 0, len(keysArgs)-numberKeys) | |||
for i, value := range keysArgs { | |||
val, err := String(value, nil) | |||
if err != nil && err != ErrNil { | |||
return nil, err | |||
} | |||
if i < numberKeys { | |||
keys = append(keys, val) | |||
} else { | |||
args = append(args, val) | |||
} | |||
} | |||
res, err := pools.Eval(luaScript, keys, args).Result() | |||
if err != nil { | |||
return nil, convertError(err) | |||
} | |||
return res, nil | |||
} | |||
func RCGetBit(key string, offset int64) (int64, error) { | |||
res, err := pools.GetBit(key, offset).Result() | |||
if err != nil { | |||
return res, convertError(err) | |||
} | |||
return res, nil | |||
} | |||
func RCSetBit(key string, offset uint32, value int) (int, error) { | |||
res, err := pools.SetBit(key, int64(offset), value).Result() | |||
return int(res), convertError(err) | |||
} | |||
func RCGetClient() *redis.ClusterClient { | |||
return pools | |||
} | |||
func convertError(err error) error { | |||
if err == redis.Nil { | |||
// 为了兼容redis 2.x,这里不返回 ErrNil,ErrNil在调用redis_cluster_reply函数时才返回 | |||
return nil | |||
} | |||
return err | |||
} |
@@ -0,0 +1,324 @@ | |||
package cachesecond | |||
import ( | |||
"errors" | |||
"log" | |||
"strings" | |||
"time" | |||
redigo "github.com/gomodule/redigo/redis" | |||
) | |||
type RedisPool struct { | |||
*redigo.Pool | |||
} | |||
func NewRedisPool(cfg *Config) *RedisPool { | |||
return &RedisPool{&redigo.Pool{ | |||
MaxIdle: cfg.MaxIdle, | |||
IdleTimeout: cfg.IdleTimeout, | |||
MaxActive: cfg.MaxActive, | |||
Wait: cfg.Wait, | |||
Dial: func() (redigo.Conn, error) { | |||
c, err := redigo.Dial("tcp", cfg.Server) | |||
if err != nil { | |||
log.Println("Redis Dial failed: ", err) | |||
return nil, err | |||
} | |||
if cfg.Password != "" { | |||
if _, err := c.Do("AUTH", cfg.Password); err != nil { | |||
c.Close() | |||
log.Println("Redis AUTH failed: ", err) | |||
return nil, err | |||
} | |||
} | |||
return c, err | |||
}, | |||
TestOnBorrow: func(c redigo.Conn, t time.Time) error { | |||
_, err := c.Do("PING") | |||
if err != nil { | |||
log.Println("Unable to ping to redis server:", err) | |||
} | |||
return err | |||
}, | |||
}} | |||
} | |||
func (p *RedisPool) Do(cmd string, args ...interface{}) (reply interface{}, err error) { | |||
conn := pool.Get() | |||
defer conn.Close() | |||
return conn.Do(cmd, args...) | |||
} | |||
func (p *RedisPool) GetPool() *redigo.Pool { | |||
return pool | |||
} | |||
func (p *RedisPool) ParseKey(key string, vars []string) (string, error) { | |||
arr := strings.Split(key, conf.KeyPlaceholder) | |||
actualKey := "" | |||
if len(arr) != len(vars)+1 { | |||
return "", errors.New("redis/connection.go: Insufficient arguments to parse key") | |||
} else { | |||
for index, val := range arr { | |||
if index == 0 { | |||
actualKey = arr[index] | |||
} else { | |||
actualKey += vars[index-1] + val | |||
} | |||
} | |||
} | |||
return getPrefixedKey(actualKey), nil | |||
} | |||
func (p *RedisPool) getPrefixedKey(key string) string { | |||
return conf.KeyPrefix + conf.KeyDelimiter + key | |||
} | |||
func (p *RedisPool) StripEnvKey(key string) string { | |||
return strings.TrimLeft(key, conf.KeyPrefix+conf.KeyDelimiter) | |||
} | |||
func (p *RedisPool) SplitKey(key string) []string { | |||
return strings.Split(key, conf.KeyDelimiter) | |||
} | |||
func (p *RedisPool) Expire(key string, ttl int) (interface{}, error) { | |||
return Do("EXPIRE", key, ttl) | |||
} | |||
func (p *RedisPool) Persist(key string) (interface{}, error) { | |||
return Do("PERSIST", key) | |||
} | |||
func (p *RedisPool) Del(key string) (interface{}, error) { | |||
return Do("DEL", key) | |||
} | |||
func (p *RedisPool) Set(key string, data interface{}) (interface{}, error) { | |||
// set | |||
return Do("SET", key, data) | |||
} | |||
func (p *RedisPool) SetNX(key string, data interface{}) (interface{}, error) { | |||
return Do("SETNX", key, data) | |||
} | |||
func (p *RedisPool) SetEx(key string, data interface{}, ttl int) (interface{}, error) { | |||
return Do("SETEX", key, ttl, data) | |||
} | |||
func (p *RedisPool) Get(key string) (interface{}, error) { | |||
// get | |||
return Do("GET", key) | |||
} | |||
func (p *RedisPool) GetStringMap(key string) (map[string]string, error) { | |||
// get | |||
return redigo.StringMap(Do("GET", key)) | |||
} | |||
func (p *RedisPool) GetTTL(key string) (time.Duration, error) { | |||
ttl, err := redigo.Int64(Do("TTL", key)) | |||
return time.Duration(ttl) * time.Second, err | |||
} | |||
func (p *RedisPool) GetBytes(key string) ([]byte, error) { | |||
return redigo.Bytes(Do("GET", key)) | |||
} | |||
func (p *RedisPool) GetString(key string) (string, error) { | |||
return redigo.String(Do("GET", key)) | |||
} | |||
func (p *RedisPool) GetInt(key string) (int, error) { | |||
return redigo.Int(Do("GET", key)) | |||
} | |||
func (p *RedisPool) GetStringLength(key string) (int, error) { | |||
return redigo.Int(Do("STRLEN", key)) | |||
} | |||
func (p *RedisPool) ZAdd(key string, score float64, data interface{}) (interface{}, error) { | |||
return Do("ZADD", key, score, data) | |||
} | |||
func (p *RedisPool) ZRem(key string, data interface{}) (interface{}, error) { | |||
return Do("ZREM", key, data) | |||
} | |||
func (p *RedisPool) ZRange(key string, start int, end int, withScores bool) ([]interface{}, error) { | |||
if withScores { | |||
return redigo.Values(Do("ZRANGE", key, start, end, "WITHSCORES")) | |||
} | |||
return redigo.Values(Do("ZRANGE", key, start, end)) | |||
} | |||
func (p *RedisPool) SAdd(setName string, data interface{}) (interface{}, error) { | |||
return Do("SADD", setName, data) | |||
} | |||
func (p *RedisPool) SCard(setName string) (int64, error) { | |||
return redigo.Int64(Do("SCARD", setName)) | |||
} | |||
func (p *RedisPool) SIsMember(setName string, data interface{}) (bool, error) { | |||
return redigo.Bool(Do("SISMEMBER", setName, data)) | |||
} | |||
func (p *RedisPool) SMembers(setName string) ([]string, error) { | |||
return redigo.Strings(Do("SMEMBERS", setName)) | |||
} | |||
func (p *RedisPool) SRem(setName string, data interface{}) (interface{}, error) { | |||
return Do("SREM", setName, data) | |||
} | |||
func (p *RedisPool) HSet(key string, HKey string, data interface{}) (interface{}, error) { | |||
return Do("HSET", key, HKey, data) | |||
} | |||
func (p *RedisPool) HGet(key string, HKey string) (interface{}, error) { | |||
return Do("HGET", key, HKey) | |||
} | |||
func (p *RedisPool) HMGet(key string, hashKeys ...string) ([]interface{}, error) { | |||
ret, err := Do("HMGET", key, hashKeys) | |||
if err != nil { | |||
return nil, err | |||
} | |||
reta, ok := ret.([]interface{}) | |||
if !ok { | |||
return nil, errors.New("result not an array") | |||
} | |||
return reta, nil | |||
} | |||
func (p *RedisPool) HMSet(key string, hashKeys []string, vals []interface{}) (interface{}, error) { | |||
if len(hashKeys) == 0 || len(hashKeys) != len(vals) { | |||
var ret interface{} | |||
return ret, errors.New("bad length") | |||
} | |||
input := []interface{}{key} | |||
for i, v := range hashKeys { | |||
input = append(input, v, vals[i]) | |||
} | |||
return Do("HMSET", input...) | |||
} | |||
func (p *RedisPool) HGetString(key string, HKey string) (string, error) { | |||
return redigo.String(Do("HGET", key, HKey)) | |||
} | |||
func (p *RedisPool) HGetFloat(key string, HKey string) (float64, error) { | |||
f, err := redigo.Float64(Do("HGET", key, HKey)) | |||
return float64(f), err | |||
} | |||
func (p *RedisPool) HGetInt(key string, HKey string) (int, error) { | |||
return redigo.Int(Do("HGET", key, HKey)) | |||
} | |||
func (p *RedisPool) HGetInt64(key string, HKey string) (int64, error) { | |||
return redigo.Int64(Do("HGET", key, HKey)) | |||
} | |||
func (p *RedisPool) HGetBool(key string, HKey string) (bool, error) { | |||
return redigo.Bool(Do("HGET", key, HKey)) | |||
} | |||
func (p *RedisPool) HDel(key string, HKey string) (interface{}, error) { | |||
return Do("HDEL", key, HKey) | |||
} | |||
func (p *RedisPool) HGetAll(key string) (map[string]interface{}, error) { | |||
vals, err := redigo.Values(Do("HGETALL", key)) | |||
if err != nil { | |||
return nil, err | |||
} | |||
num := len(vals) / 2 | |||
result := make(map[string]interface{}, num) | |||
for i := 0; i < num; i++ { | |||
key, _ := redigo.String(vals[2*i], nil) | |||
result[key] = vals[2*i+1] | |||
} | |||
return result, nil | |||
} | |||
// NOTE: Use this in production environment with extreme care. | |||
// Read more here:https://redigo.io/commands/keys | |||
func (p *RedisPool) Keys(pattern string) ([]string, error) { | |||
return redigo.Strings(Do("KEYS", pattern)) | |||
} | |||
func (p *RedisPool) HKeys(key string) ([]string, error) { | |||
return redigo.Strings(Do("HKEYS", key)) | |||
} | |||
func (p *RedisPool) Exists(key string) (bool, error) { | |||
count, err := redigo.Int(Do("EXISTS", key)) | |||
if count == 0 { | |||
return false, err | |||
} else { | |||
return true, err | |||
} | |||
} | |||
func (p *RedisPool) Incr(key string) (int64, error) { | |||
return redigo.Int64(Do("INCR", key)) | |||
} | |||
func (p *RedisPool) Decr(key string) (int64, error) { | |||
return redigo.Int64(Do("DECR", key)) | |||
} | |||
func (p *RedisPool) IncrBy(key string, incBy int64) (int64, error) { | |||
return redigo.Int64(Do("INCRBY", key, incBy)) | |||
} | |||
func (p *RedisPool) DecrBy(key string, decrBy int64) (int64, error) { | |||
return redigo.Int64(Do("DECRBY", key)) | |||
} | |||
func (p *RedisPool) IncrByFloat(key string, incBy float64) (float64, error) { | |||
return redigo.Float64(Do("INCRBYFLOAT", key, incBy)) | |||
} | |||
func (p *RedisPool) DecrByFloat(key string, decrBy float64) (float64, error) { | |||
return redigo.Float64(Do("DECRBYFLOAT", key, decrBy)) | |||
} | |||
// use for message queue | |||
func (p *RedisPool) LPush(key string, data interface{}) (interface{}, error) { | |||
// set | |||
return Do("LPUSH", key, data) | |||
} | |||
func (p *RedisPool) LPop(key string) (interface{}, error) { | |||
return Do("LPOP", key) | |||
} | |||
func (p *RedisPool) LPopString(key string) (string, error) { | |||
return redigo.String(Do("LPOP", key)) | |||
} | |||
func (p *RedisPool) LPopFloat(key string) (float64, error) { | |||
f, err := redigo.Float64(Do("LPOP", key)) | |||
return float64(f), err | |||
} | |||
func (p *RedisPool) LPopInt(key string) (int, error) { | |||
return redigo.Int(Do("LPOP", key)) | |||
} | |||
func (p *RedisPool) LPopInt64(key string) (int64, error) { | |||
return redigo.Int64(Do("LPOP", key)) | |||
} | |||
func (p *RedisPool) RPush(key string, data interface{}) (interface{}, error) { | |||
// set | |||
return Do("RPUSH", key, data) | |||
} | |||
func (p *RedisPool) RPop(key string) (interface{}, error) { | |||
return Do("RPOP", key) | |||
} | |||
func (p *RedisPool) RPopString(key string) (string, error) { | |||
return redigo.String(Do("RPOP", key)) | |||
} | |||
func (p *RedisPool) RPopFloat(key string) (float64, error) { | |||
f, err := redigo.Float64(Do("RPOP", key)) | |||
return float64(f), err | |||
} | |||
func (p *RedisPool) RPopInt(key string) (int, error) { | |||
return redigo.Int(Do("RPOP", key)) | |||
} | |||
func (p *RedisPool) RPopInt64(key string) (int64, error) { | |||
return redigo.Int64(Do("RPOP", key)) | |||
} | |||
func (p *RedisPool) Scan(cursor int64, pattern string, count int64) (int64, []string, error) { | |||
var items []string | |||
var newCursor int64 | |||
values, err := redigo.Values(Do("SCAN", cursor, "MATCH", pattern, "COUNT", count)) | |||
if err != nil { | |||
return 0, nil, err | |||
} | |||
values, err = redigo.Scan(values, &newCursor, &items) | |||
if err != nil { | |||
return 0, nil, err | |||
} | |||
return newCursor, items, nil | |||
} |
@@ -0,0 +1,617 @@ | |||
package cachesecond | |||
import ( | |||
"strconv" | |||
"time" | |||
"github.com/go-redis/redis" | |||
) | |||
type RedisClusterPool struct { | |||
client *redis.ClusterClient | |||
} | |||
func NewRedisClusterPool(addrs []string) (*RedisClusterPool, error) { | |||
opt := &redis.ClusterOptions{ | |||
Addrs: addrs, | |||
PoolSize: 512, | |||
PoolTimeout: 10 * time.Second, | |||
IdleTimeout: 10 * time.Second, | |||
DialTimeout: 10 * time.Second, | |||
ReadTimeout: 3 * time.Second, | |||
WriteTimeout: 3 * time.Second, | |||
} | |||
c := redis.NewClusterClient(opt) | |||
if err := c.Ping().Err(); err != nil { | |||
return nil, err | |||
} | |||
return &RedisClusterPool{client: c}, nil | |||
} | |||
func (p *RedisClusterPool) Get(key string) (interface{}, error) { | |||
res, err := p.client.Get(key).Result() | |||
if err != nil { | |||
return nil, convertError(err) | |||
} | |||
return []byte(res), nil | |||
} | |||
func (p *RedisClusterPool) Set(key string, value interface{}) error { | |||
err := p.client.Set(key, value, 0).Err() | |||
return convertError(err) | |||
} | |||
func (p *RedisClusterPool) GetSet(key string, value interface{}) (interface{}, error) { | |||
res, err := p.client.GetSet(key, value).Result() | |||
if err != nil { | |||
return nil, convertError(err) | |||
} | |||
return []byte(res), nil | |||
} | |||
func (p *RedisClusterPool) SetNx(key string, value interface{}) (int64, error) { | |||
res, err := p.client.SetNX(key, value, 0).Result() | |||
if err != nil { | |||
return 0, convertError(err) | |||
} | |||
if res { | |||
return 1, nil | |||
} | |||
return 0, nil | |||
} | |||
func (p *RedisClusterPool) SetEx(key string, value interface{}, timeout int64) error { | |||
_, err := p.client.Set(key, value, time.Duration(timeout)*time.Second).Result() | |||
if err != nil { | |||
return convertError(err) | |||
} | |||
return nil | |||
} | |||
// nil表示成功,ErrNil表示数据库内已经存在这个key,其他表示数据库发生错误 | |||
func (p *RedisClusterPool) SetNxEx(key string, value interface{}, timeout int64) error { | |||
res, err := p.client.SetNX(key, value, time.Duration(timeout)*time.Second).Result() | |||
if err != nil { | |||
return convertError(err) | |||
} | |||
if res { | |||
return nil | |||
} | |||
return ErrNil | |||
} | |||
func (p *RedisClusterPool) MGet(keys ...string) ([]interface{}, error) { | |||
res, err := p.client.MGet(keys...).Result() | |||
return res, convertError(err) | |||
} | |||
// 为确保多个key映射到同一个slot,每个key最好加上hash tag,如:{test} | |||
func (p *RedisClusterPool) MSet(kvs map[string]interface{}) error { | |||
pairs := make([]string, 0, len(kvs)*2) | |||
for k, v := range kvs { | |||
val, err := String(v, nil) | |||
if err != nil { | |||
return err | |||
} | |||
pairs = append(pairs, k, val) | |||
} | |||
return convertError(p.client.MSet(pairs).Err()) | |||
} | |||
// 为确保多个key映射到同一个slot,每个key最好加上hash tag,如:{test} | |||
func (p *RedisClusterPool) MSetNX(kvs map[string]interface{}) (bool, error) { | |||
pairs := make([]string, 0, len(kvs)*2) | |||
for k, v := range kvs { | |||
val, err := String(v, nil) | |||
if err != nil { | |||
return false, err | |||
} | |||
pairs = append(pairs, k, val) | |||
} | |||
res, err := p.client.MSetNX(pairs).Result() | |||
return res, convertError(err) | |||
} | |||
func (p *RedisClusterPool) ExpireAt(key string, timestamp int64) (int64, error) { | |||
res, err := p.client.ExpireAt(key, time.Unix(timestamp, 0)).Result() | |||
if err != nil { | |||
return 0, convertError(err) | |||
} | |||
if res { | |||
return 1, nil | |||
} | |||
return 0, nil | |||
} | |||
func (p *RedisClusterPool) Del(keys ...string) (int64, error) { | |||
args := make([]interface{}, 0, len(keys)) | |||
for _, key := range keys { | |||
args = append(args, key) | |||
} | |||
res, err := p.client.Del(keys...).Result() | |||
if err != nil { | |||
return res, convertError(err) | |||
} | |||
return res, nil | |||
} | |||
func (p *RedisClusterPool) Incr(key string) (int64, error) { | |||
res, err := p.client.Incr(key).Result() | |||
if err != nil { | |||
return res, convertError(err) | |||
} | |||
return res, nil | |||
} | |||
func (p *RedisClusterPool) IncrBy(key string, delta int64) (int64, error) { | |||
res, err := p.client.IncrBy(key, delta).Result() | |||
if err != nil { | |||
return res, convertError(err) | |||
} | |||
return res, nil | |||
} | |||
func (p *RedisClusterPool) Expire(key string, duration int64) (int64, error) { | |||
res, err := p.client.Expire(key, time.Duration(duration)*time.Second).Result() | |||
if err != nil { | |||
return 0, convertError(err) | |||
} | |||
if res { | |||
return 1, nil | |||
} | |||
return 0, nil | |||
} | |||
func (p *RedisClusterPool) Exists(key string) (bool, error) { // todo (bool, error) | |||
res, err := p.client.Exists(key).Result() | |||
if err != nil { | |||
return false, convertError(err) | |||
} | |||
if res > 0 { | |||
return true, nil | |||
} | |||
return false, nil | |||
} | |||
func (p *RedisClusterPool) HGet(key string, field string) (interface{}, error) { | |||
res, err := p.client.HGet(key, field).Result() | |||
if err != nil { | |||
return nil, convertError(err) | |||
} | |||
return []byte(res), nil | |||
} | |||
func (p *RedisClusterPool) HLen(key string) (int64, error) { | |||
res, err := p.client.HLen(key).Result() | |||
if err != nil { | |||
return res, convertError(err) | |||
} | |||
return res, nil | |||
} | |||
func (p *RedisClusterPool) HSet(key string, field string, val interface{}) error { | |||
value, err := String(val, nil) | |||
if err != nil && err != ErrNil { | |||
return err | |||
} | |||
_, err = p.client.HSet(key, field, value).Result() | |||
if err != nil { | |||
return convertError(err) | |||
} | |||
return nil | |||
} | |||
func (p *RedisClusterPool) HDel(key string, fields ...string) (int64, error) { | |||
args := make([]interface{}, 0, len(fields)+1) | |||
args = append(args, key) | |||
for _, field := range fields { | |||
args = append(args, field) | |||
} | |||
res, err := p.client.HDel(key, fields...).Result() | |||
if err != nil { | |||
return 0, convertError(err) | |||
} | |||
return res, nil | |||
} | |||
func (p *RedisClusterPool) HMGet(key string, fields ...string) (interface{}, error) { | |||
args := make([]interface{}, 0, len(fields)+1) | |||
args = append(args, key) | |||
for _, field := range fields { | |||
args = append(args, field) | |||
} | |||
if len(fields) == 0 { | |||
return nil, ErrNil | |||
} | |||
res, err := p.client.HMGet(key, fields...).Result() | |||
if err != nil { | |||
return nil, convertError(err) | |||
} | |||
return res, nil | |||
} | |||
func (p *RedisClusterPool) HMSet(key string, kvs ...interface{}) error { | |||
if len(kvs) == 0 { | |||
return nil | |||
} | |||
if len(kvs)%2 != 0 { | |||
return ErrWrongArgsNum | |||
} | |||
var err error | |||
v := map[string]interface{}{} // todo change | |||
v["field"], err = String(kvs[0], nil) | |||
if err != nil && err != ErrNil { | |||
return err | |||
} | |||
v["value"], err = String(kvs[1], nil) | |||
if err != nil && err != ErrNil { | |||
return err | |||
} | |||
pairs := make([]string, 0, len(kvs)-2) | |||
if len(kvs) > 2 { | |||
for _, kv := range kvs[2:] { | |||
kvString, err := String(kv, nil) | |||
if err != nil && err != ErrNil { | |||
return err | |||
} | |||
pairs = append(pairs, kvString) | |||
} | |||
} | |||
v["paris"] = pairs | |||
_, err = p.client.HMSet(key, v).Result() | |||
if err != nil { | |||
return convertError(err) | |||
} | |||
return nil | |||
} | |||
func (p *RedisClusterPool) HKeys(key string) ([]string, error) { | |||
res, err := p.client.HKeys(key).Result() | |||
if err != nil { | |||
return res, convertError(err) | |||
} | |||
return res, nil | |||
} | |||
func (p *RedisClusterPool) HVals(key string) ([]interface{}, error) { | |||
res, err := p.client.HVals(key).Result() | |||
if err != nil { | |||
return nil, convertError(err) | |||
} | |||
rs := make([]interface{}, 0, len(res)) | |||
for _, res := range res { | |||
rs = append(rs, res) | |||
} | |||
return rs, nil | |||
} | |||
func (p *RedisClusterPool) HGetAll(key string) (map[string]string, error) { | |||
vals, err := p.client.HGetAll(key).Result() | |||
if err != nil { | |||
return nil, convertError(err) | |||
} | |||
return vals, nil | |||
} | |||
func (p *RedisClusterPool) HIncrBy(key, field string, delta int64) (int64, error) { | |||
res, err := p.client.HIncrBy(key, field, delta).Result() | |||
if err != nil { | |||
return res, convertError(err) | |||
} | |||
return res, nil | |||
} | |||
func (p *RedisClusterPool) ZAdd(key string, kvs ...interface{}) (int64, error) { | |||
args := make([]interface{}, 0, len(kvs)+1) | |||
args = append(args, key) | |||
args = append(args, kvs...) | |||
if len(kvs) == 0 { | |||
return 0, nil | |||
} | |||
if len(kvs)%2 != 0 { | |||
return 0, ErrWrongArgsNum | |||
} | |||
zs := make([]redis.Z, len(kvs)/2) | |||
for i := 0; i < len(kvs); i += 2 { | |||
idx := i / 2 | |||
score, err := Float64(kvs[i], nil) | |||
if err != nil && err != ErrNil { | |||
return 0, err | |||
} | |||
zs[idx].Score = score | |||
zs[idx].Member = kvs[i+1] | |||
} | |||
res, err := p.client.ZAdd(key, zs...).Result() | |||
if err != nil { | |||
return res, convertError(err) | |||
} | |||
return res, nil | |||
} | |||
func (p *RedisClusterPool) ZRem(key string, members ...string) (int64, error) { | |||
args := make([]interface{}, 0, len(members)) | |||
args = append(args, key) | |||
for _, member := range members { | |||
args = append(args, member) | |||
} | |||
res, err := p.client.ZRem(key, members).Result() | |||
if err != nil { | |||
return res, convertError(err) | |||
} | |||
return res, err | |||
} | |||
func (p *RedisClusterPool) ZRange(key string, min, max int64, withScores bool) (interface{}, error) { | |||
res := make([]interface{}, 0) | |||
if withScores { | |||
zs, err := p.client.ZRangeWithScores(key, min, max).Result() | |||
if err != nil { | |||
return nil, convertError(err) | |||
} | |||
for _, z := range zs { | |||
res = append(res, z.Member, strconv.FormatFloat(z.Score, 'f', -1, 64)) | |||
} | |||
} else { | |||
ms, err := p.client.ZRange(key, min, max).Result() | |||
if err != nil { | |||
return nil, convertError(err) | |||
} | |||
for _, m := range ms { | |||
res = append(res, m) | |||
} | |||
} | |||
return res, nil | |||
} | |||
func (p *RedisClusterPool) ZRangeByScoreWithScore(key string, min, max int64) (map[string]int64, error) { | |||
opt := new(redis.ZRangeBy) | |||
opt.Min = strconv.FormatInt(int64(min), 10) | |||
opt.Max = strconv.FormatInt(int64(max), 10) | |||
opt.Count = -1 | |||
opt.Offset = 0 | |||
vals, err := p.client.ZRangeByScoreWithScores(key, *opt).Result() | |||
if err != nil { | |||
return nil, convertError(err) | |||
} | |||
res := make(map[string]int64, len(vals)) | |||
for _, val := range vals { | |||
key, err := String(val.Member, nil) | |||
if err != nil && err != ErrNil { | |||
return nil, err | |||
} | |||
res[key] = int64(val.Score) | |||
} | |||
return res, nil | |||
} | |||
func (p *RedisClusterPool) LRange(key string, start, stop int64) (interface{}, error) { | |||
res, err := p.client.LRange(key, start, stop).Result() | |||
if err != nil { | |||
return nil, convertError(err) | |||
} | |||
return res, nil | |||
} | |||
func (p *RedisClusterPool) LSet(key string, index int, value interface{}) error { | |||
err := p.client.LSet(key, int64(index), value).Err() | |||
return convertError(err) | |||
} | |||
func (p *RedisClusterPool) LLen(key string) (int64, error) { | |||
res, err := p.client.LLen(key).Result() | |||
if err != nil { | |||
return res, convertError(err) | |||
} | |||
return res, nil | |||
} | |||
func (p *RedisClusterPool) LRem(key string, count int, value interface{}) (int, error) { | |||
val, _ := value.(string) | |||
res, err := p.client.LRem(key, int64(count), val).Result() | |||
if err != nil { | |||
return int(res), convertError(err) | |||
} | |||
return int(res), nil | |||
} | |||
func (p *RedisClusterPool) TTl(key string) (int64, error) { | |||
duration, err := p.client.TTL(key).Result() | |||
if err != nil { | |||
return int64(duration.Seconds()), convertError(err) | |||
} | |||
return int64(duration.Seconds()), nil | |||
} | |||
func (p *RedisClusterPool) LPop(key string) (interface{}, error) { | |||
res, err := p.client.LPop(key).Result() | |||
if err != nil { | |||
return nil, convertError(err) | |||
} | |||
return res, nil | |||
} | |||
func (p *RedisClusterPool) RPop(key string) (interface{}, error) { | |||
res, err := p.client.RPop(key).Result() | |||
if err != nil { | |||
return nil, convertError(err) | |||
} | |||
return res, nil | |||
} | |||
func (p *RedisClusterPool) BLPop(key string, timeout int) (interface{}, error) { | |||
res, err := p.client.BLPop(time.Duration(timeout)*time.Second, key).Result() | |||
if err != nil { | |||
// 兼容redis 2.x | |||
if err == redis.Nil { | |||
return nil, ErrNil | |||
} | |||
return nil, err | |||
} | |||
return res[1], nil | |||
} | |||
func (p *RedisClusterPool) BRPop(key string, timeout int) (interface{}, error) { | |||
res, err := p.client.BRPop(time.Duration(timeout)*time.Second, key).Result() | |||
if err != nil { | |||
// 兼容redis 2.x | |||
if err == redis.Nil { | |||
return nil, ErrNil | |||
} | |||
return nil, convertError(err) | |||
} | |||
return res[1], nil | |||
} | |||
func (p *RedisClusterPool) LPush(key string, value ...interface{}) error { | |||
args := make([]interface{}, 0, len(value)+1) | |||
args = append(args, key) | |||
args = append(args, value...) | |||
vals := make([]string, 0, len(value)) | |||
for _, v := range value { | |||
val, err := String(v, nil) | |||
if err != nil && err != ErrNil { | |||
return err | |||
} | |||
vals = append(vals, val) | |||
} | |||
_, err := p.client.LPush(key, vals).Result() // todo ... | |||
if err != nil { | |||
return convertError(err) | |||
} | |||
return nil | |||
} | |||
func (p *RedisClusterPool) RPush(key string, value ...interface{}) error { | |||
args := make([]interface{}, 0, len(value)+1) | |||
args = append(args, key) | |||
args = append(args, value...) | |||
vals := make([]string, 0, len(value)) | |||
for _, v := range value { | |||
val, err := String(v, nil) | |||
if err != nil && err != ErrNil { | |||
if err == ErrNil { | |||
continue | |||
} | |||
return err | |||
} | |||
if val == "" { | |||
continue | |||
} | |||
vals = append(vals, val) | |||
} | |||
_, err := p.client.RPush(key, vals).Result() // todo ... | |||
if err != nil { | |||
return convertError(err) | |||
} | |||
return nil | |||
} | |||
// 为确保srcKey跟destKey映射到同一个slot,srcKey和destKey需要加上hash tag,如:{test} | |||
func (p *RedisClusterPool) BRPopLPush(srcKey string, destKey string, timeout int) (interface{}, error) { | |||
res, err := p.client.BRPopLPush(srcKey, destKey, time.Duration(timeout)*time.Second).Result() | |||
if err != nil { | |||
return nil, convertError(err) | |||
} | |||
return res, nil | |||
} | |||
// 为确保srcKey跟destKey映射到同一个slot,srcKey和destKey需要加上hash tag,如:{test} | |||
func (p *RedisClusterPool) RPopLPush(srcKey string, destKey string) (interface{}, error) { | |||
res, err := p.client.RPopLPush(srcKey, destKey).Result() | |||
if err != nil { | |||
return nil, convertError(err) | |||
} | |||
return res, nil | |||
} | |||
func (p *RedisClusterPool) SAdd(key string, members ...interface{}) (int64, error) { | |||
args := make([]interface{}, 0, len(members)+1) | |||
args = append(args, key) | |||
args = append(args, members...) | |||
ms := make([]string, 0, len(members)) | |||
for _, member := range members { | |||
m, err := String(member, nil) | |||
if err != nil && err != ErrNil { | |||
return 0, err | |||
} | |||
ms = append(ms, m) | |||
} | |||
res, err := p.client.SAdd(key, ms).Result() // todo ... | |||
if err != nil { | |||
return res, convertError(err) | |||
} | |||
return res, nil | |||
} | |||
func (p *RedisClusterPool) SPop(key string) ([]byte, error) { | |||
res, err := p.client.SPop(key).Result() | |||
if err != nil { | |||
return nil, convertError(err) | |||
} | |||
return []byte(res), nil | |||
} | |||
func (p *RedisClusterPool) SIsMember(key string, member interface{}) (bool, error) { | |||
m, _ := member.(string) | |||
res, err := p.client.SIsMember(key, m).Result() | |||
if err != nil { | |||
return res, convertError(err) | |||
} | |||
return res, nil | |||
} | |||
func (p *RedisClusterPool) SRem(key string, members ...interface{}) (int64, error) { | |||
args := make([]interface{}, 0, len(members)+1) | |||
args = append(args, key) | |||
args = append(args, members...) | |||
ms := make([]string, 0, len(members)) | |||
for _, member := range members { | |||
m, err := String(member, nil) | |||
if err != nil && err != ErrNil { | |||
return 0, err | |||
} | |||
ms = append(ms, m) | |||
} | |||
res, err := p.client.SRem(key, ms).Result() // todo ... | |||
if err != nil { | |||
return res, convertError(err) | |||
} | |||
return res, nil | |||
} | |||
func (p *RedisClusterPool) SMembers(key string) ([]string, error) { | |||
res, err := p.client.SMembers(key).Result() | |||
if err != nil { | |||
return nil, convertError(err) | |||
} | |||
return res, nil | |||
} | |||
func (p *RedisClusterPool) ScriptLoad(luaScript string) (interface{}, error) { | |||
res, err := p.client.ScriptLoad(luaScript).Result() | |||
if err != nil { | |||
return nil, convertError(err) | |||
} | |||
return res, nil | |||
} | |||
func (p *RedisClusterPool) EvalSha(sha1 string, numberKeys int, keysArgs ...interface{}) (interface{}, error) { | |||
vals := make([]interface{}, 0, len(keysArgs)+2) | |||
vals = append(vals, sha1, numberKeys) | |||
vals = append(vals, keysArgs...) | |||
keys := make([]string, 0, numberKeys) | |||
args := make([]string, 0, len(keysArgs)-numberKeys) | |||
for i, value := range keysArgs { | |||
val, err := String(value, nil) | |||
if err != nil && err != ErrNil { | |||
return nil, err | |||
} | |||
if i < numberKeys { | |||
keys = append(keys, val) | |||
} else { | |||
args = append(args, val) | |||
} | |||
} | |||
res, err := p.client.EvalSha(sha1, keys, args).Result() | |||
if err != nil { | |||
return nil, convertError(err) | |||
} | |||
return res, nil | |||
} | |||
func (p *RedisClusterPool) Eval(luaScript string, numberKeys int, keysArgs ...interface{}) (interface{}, error) { | |||
vals := make([]interface{}, 0, len(keysArgs)+2) | |||
vals = append(vals, luaScript, numberKeys) | |||
vals = append(vals, keysArgs...) | |||
keys := make([]string, 0, numberKeys) | |||
args := make([]string, 0, len(keysArgs)-numberKeys) | |||
for i, value := range keysArgs { | |||
val, err := String(value, nil) | |||
if err != nil && err != ErrNil { | |||
return nil, err | |||
} | |||
if i < numberKeys { | |||
keys = append(keys, val) | |||
} else { | |||
args = append(args, val) | |||
} | |||
} | |||
res, err := p.client.Eval(luaScript, keys, args).Result() | |||
if err != nil { | |||
return nil, convertError(err) | |||
} | |||
return res, nil | |||
} | |||
func (p *RedisClusterPool) GetBit(key string, offset int64) (int64, error) { | |||
res, err := p.client.GetBit(key, offset).Result() | |||
if err != nil { | |||
return res, convertError(err) | |||
} | |||
return res, nil | |||
} | |||
func (p *RedisClusterPool) SetBit(key string, offset uint32, value int) (int, error) { | |||
res, err := p.client.SetBit(key, int64(offset), value).Result() | |||
return int(res), convertError(err) | |||
} | |||
func (p *RedisClusterPool) GetClient() *redis.ClusterClient { | |||
return pools | |||
} |
@@ -3,6 +3,7 @@ package utils | |||
import ( | |||
"bytes" | |||
"encoding/json" | |||
"regexp" | |||
) | |||
func JsonMarshal(interface{}) { | |||
@@ -15,3 +16,22 @@ func JsonDecode(data []byte, v interface{}) error { | |||
d.UseNumber() | |||
return d.Decode(v) | |||
} | |||
// json字符串驼峰命名格式 转为 下划线命名格式 | |||
// c :json字符串 | |||
func MarshalJSONCamelCase2JsonSnakeCase(c string) []byte { | |||
// Regexp definitions | |||
var keyMatchRegex = regexp.MustCompile(`\"(\w+)\":`) | |||
var wordBarrierRegex = regexp.MustCompile(`(\w)([A-Z])`) | |||
marshalled := []byte(c) | |||
converted := keyMatchRegex.ReplaceAllFunc( | |||
marshalled, | |||
func(match []byte) []byte { | |||
return bytes.ToLower(wordBarrierRegex.ReplaceAll( | |||
match, | |||
[]byte(`${1}_${2}`), | |||
)) | |||
}, | |||
) | |||
return converted | |||
} |
@@ -1,172 +0,0 @@ | |||
package consume | |||
import ( | |||
"applet/app/db" | |||
"applet/app/db/model" | |||
"applet/app/utils" | |||
"applet/app/utils/cache" | |||
"applet/app/utils/logx" | |||
"applet/consume/md" | |||
"code.fnuoos.com/go_rely_warehouse/zyos_go_mq.git/rabbit" | |||
"encoding/json" | |||
"errors" | |||
"fmt" | |||
"github.com/cc14514/go-geoip2" | |||
geoip2db "github.com/cc14514/go-geoip2-db" | |||
"github.com/streadway/amqp" | |||
"net" | |||
"strings" | |||
"time" | |||
) | |||
var data []*model.MasterAreaVisitsFlows | |||
func ZhiOsUserVisitIpAddressConsume(queue md.MqQueue) { | |||
fmt.Println(">>>>>>>>>>>>>>>>>>>>>>>>ZhiOsUserVisitIpAddressConsume<<<<<<<<<<<<<<<<<<<<<<<") | |||
ch, err := rabbit.Cfg.Pool.GetChannel() | |||
if err != nil { | |||
logx.Error(err) | |||
return | |||
} | |||
geoIp2db, _ := geoip2db.NewGeoipDbByStatik() | |||
defer geoIp2db.Close() | |||
defer ch.Release() | |||
//1、将自己绑定到交换机上 | |||
ch.Bind(queue.Name, queue.ExchangeName, queue.RoutKey) | |||
//2、取出数据进行消费 | |||
ch.Qos(100) | |||
delivery := ch.Consume(queue.Name, false) | |||
var res amqp.Delivery | |||
var ok bool | |||
for { | |||
res, ok = <-delivery | |||
if ok == true { | |||
fmt.Println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>") | |||
err = handleUserVisitIpAddress(res.Body, geoIp2db) | |||
//_ = res.Reject(false) | |||
_ = res.Ack(true) | |||
if len(data) >= 100 { | |||
db.BatchAddMasterAreaVisitsFlows(db.DataDb, data) | |||
data = []*model.MasterAreaVisitsFlows{} | |||
} | |||
} else { | |||
panic(errors.New("error getting message")) | |||
} | |||
} | |||
fmt.Println("get msg done") | |||
} | |||
func handleUserVisitIpAddress(msg []byte, geoIp2db *geoip2.DBReader) error { | |||
now := time.Now() | |||
today := now.Format("2006-01-02") | |||
var tmpString string | |||
err := json.Unmarshal(msg, &tmpString) | |||
if err != nil { | |||
fmt.Println(err.Error()) | |||
return err | |||
} | |||
var msgStruct md.ZhiOsUserVisitIpAddressMessage | |||
err = json.Unmarshal([]byte(tmpString), &msgStruct) | |||
if err != nil { | |||
fmt.Println(err.Error()) | |||
return err | |||
} | |||
if msgStruct.MasterId == "" { | |||
cacheKeyForUrl := fmt.Sprintf(md.ZhiOsMasterIdForUrlHashMapCacheKey) | |||
get, _ := cache.HGetString(cacheKeyForUrl, msgStruct.Url) | |||
if get == "" { | |||
msgStruct.MasterId = GetWebSiteDomainMasterId(msgStruct.Url) | |||
utils.FilePutContents("GetWebSiteDomainMasterId", utils.SerializeStr(msgStruct)) | |||
cache.HSet(cacheKeyForUrl, msgStruct.Url, msgStruct.MasterId) | |||
cache.Expire(cacheKeyForUrl, md.ZhiOsUserVisitIpAddressHashMapCacheTime) | |||
} else { | |||
msgStruct.MasterId = get | |||
} | |||
} | |||
//1、判断ip是否已统计 | |||
cacheKey := fmt.Sprintf(md.ZhiOsUserVisitIpAddressHashMapCacheKey, msgStruct.MasterId, today) | |||
//get, _ := cache.HGetString(cacheKey, msgStruct.Ip) | |||
//if get == "" { | |||
if true { | |||
//2、分析ip归属地 | |||
countryName, provinceId, cityId, provinceName, cityName := getIpAddress(geoIp2db, msgStruct) | |||
if provinceId != "" || cityId != "" || countryName != "" || provinceName != "" || cityName != "" { | |||
//3、插入`master_area_visits_flows` 表 | |||
if provinceName == "" { | |||
provinceName = msgStruct.Province | |||
} | |||
if cityName == "" { | |||
cityName = msgStruct.City | |||
} | |||
data = append(data, &model.MasterAreaVisitsFlows{ | |||
Ip: msgStruct.Ip, | |||
MasterId: utils.StrToInt(msgStruct.MasterId), | |||
Date: today, | |||
CountryName: countryName, | |||
ProvinceName: provinceName, | |||
ProvinceId: provinceId, | |||
CityName: cityName, | |||
CityId: cityId, | |||
Pvd: msgStruct.Pvd, | |||
CreateAt: now, | |||
UpdateAt: now, | |||
}) | |||
//db.MasterAreaVisitsFlowsInsert(db.DataDb, ) | |||
} | |||
//4、加入到缓存map中 | |||
cache.HSet(cacheKey, msgStruct.Ip, msgStruct.MasterId) | |||
cache.Expire(cacheKey, md.ZhiOsUserVisitIpAddressHashMapCacheTime) | |||
} | |||
return nil | |||
} | |||
func getIpAddress(geoIp2db *geoip2.DBReader, message md.ZhiOsUserVisitIpAddressMessage) (countryName, provinceId, cityId, provinceName, cityName string) { | |||
record, _ := geoIp2db.City(net.ParseIP(message.Ip)) | |||
if record.Country.Names != nil && record.Subdivisions != nil && record.City.Names != nil { | |||
message.Country = record.Country.Names["zh-CN"] | |||
message.Province = record.Subdivisions[0].Names["zh-CN"] | |||
message.City = record.City.Names["zh-CN"] | |||
} | |||
countryName = message.Country | |||
if countryName == "中国" && message.Province != "" { | |||
if message.Province == "闽" { | |||
message.Province = "福建" | |||
} | |||
province, err := db.ProvinceGetOneByName(message.Province) | |||
if err != nil { | |||
return "", "", "", "", "" | |||
} | |||
provinceId = province.Id | |||
provinceName = province.Name | |||
if message.City != "" { | |||
city, err := db.CityGetOneByName(message.City) | |||
if err != nil { | |||
return "", "", "", "", "" | |||
} | |||
cityId = city.Id | |||
cityName = city.Name | |||
} | |||
} | |||
return | |||
} | |||
// 获取指定类型的域名对应的masterId:admin、wap、api | |||
func GetWebSiteDomainMasterId(host string) string { | |||
obj := new(model.UserAppDomain) | |||
has, err := db.Db.Where("domain=?", host).Get(obj) | |||
if err != nil || !has { | |||
hostList := strings.Split(host, ":") | |||
has, err = db.Db.Where("domain=?", hostList[0]).Get(obj) | |||
if err != nil || !has { | |||
return "" | |||
} | |||
} | |||
return utils.AnyToString(obj.Uuid) | |||
} |
@@ -4,6 +4,7 @@ go 1.18 | |||
require ( | |||
code.fnuoos.com/go_rely_warehouse/zyos_go_condition_statistics.git v1.1.2-0.20240222023917-c31b53f7e8cb | |||
code.fnuoos.com/go_rely_warehouse/zyos_go_coupon.git v1.1.2 | |||
code.fnuoos.com/go_rely_warehouse/zyos_go_es.git v1.0.0 | |||
code.fnuoos.com/go_rely_warehouse/zyos_go_mq.git v0.0.4 | |||
code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git v1.9.10-0.20240428041015-bcc1e79f0d28 | |||
@@ -15,6 +16,7 @@ require ( | |||
github.com/cc14514/go-geoip2 v0.0.0-20190105051856-0a1854480a11 | |||
github.com/cc14514/go-geoip2-db v0.0.0-20190106063142-7b6408a9812a | |||
github.com/dchest/uniuri v0.0.0-20200228104902-7aecb25e1fe5 | |||
github.com/dgrijalva/jwt-go v3.2.0+incompatible | |||
github.com/forgoer/openssl v1.2.1 | |||
github.com/gin-contrib/sessions v1.0.0 | |||
github.com/gin-gonic/gin v1.9.1 | |||
@@ -45,13 +47,17 @@ require ( | |||
) | |||
require ( | |||
code.fnuoos.com/go_rely_warehouse/zyos_go_coupon.git v1.1.2 // indirect | |||
code.fnuoos.com/go_rely_warehouse/zyos_go_day_luck_draw.git v1.2.1 // indirect | |||
code.fnuoos.com/go_rely_warehouse/zyos_go_jg_push.git v1.0.5 // indirect | |||
code.fnuoos.com/go_rely_warehouse/zyos_go_o2o_business.git v1.0.9 // indirect | |||
github.com/KyleBanks/depth v1.2.1 // indirect | |||
github.com/PuerkitoBio/goquery v1.9.2 // indirect | |||
github.com/PuerkitoBio/purell v1.1.1 // indirect | |||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect | |||
github.com/andybalholm/cascadia v1.3.2 // indirect | |||
github.com/antchfx/htmlquery v1.3.1 // indirect | |||
github.com/antchfx/xmlquery v1.4.0 // indirect | |||
github.com/antchfx/xpath v1.3.0 // indirect | |||
github.com/bitly/go-simplejson v0.5.0 // indirect | |||
github.com/bytedance/sonic v1.11.3 // indirect | |||
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect | |||
@@ -62,13 +68,17 @@ require ( | |||
github.com/go-openapi/jsonreference v0.19.5 // indirect | |||
github.com/go-openapi/spec v0.20.3 // 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/gocolly/colly v1.2.0 // indirect | |||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect | |||
github.com/golang/protobuf v1.5.3 // indirect | |||
github.com/golang/snappy v0.0.4 // indirect | |||
github.com/gorilla/context v1.1.2 // indirect | |||
github.com/gorilla/securecookie v1.1.2 // indirect | |||
github.com/gorilla/sessions v1.2.2 // indirect | |||
github.com/josharian/intern v1.0.0 // indirect | |||
github.com/kennygrant/sanitize v1.2.4 // indirect | |||
github.com/klauspost/cpuid/v2 v2.2.7 // indirect | |||
github.com/leodido/go-urn v1.4.0 // indirect | |||
github.com/mailru/easyjson v0.7.7 // indirect | |||
@@ -81,7 +91,9 @@ require ( | |||
github.com/pelletier/go-toml/v2 v2.2.0 // indirect | |||
github.com/pkg/errors v0.9.1 // indirect | |||
github.com/rakyll/statik v0.1.7 // indirect | |||
github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d // indirect | |||
github.com/syndtr/goleveldb v1.0.0 // indirect | |||
github.com/temoto/robotstxt v1.1.2 // indirect | |||
github.com/tidwall/match v1.1.1 // indirect | |||
github.com/tidwall/pretty v1.2.0 // indirect | |||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect | |||
@@ -89,14 +101,15 @@ require ( | |||
go.uber.org/atomic v1.7.0 // indirect | |||
go.uber.org/multierr v1.6.0 // indirect | |||
golang.org/x/arch v0.7.0 // indirect | |||
golang.org/x/crypto v0.21.0 // indirect | |||
golang.org/x/crypto v0.22.0 // indirect | |||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b // indirect | |||
golang.org/x/net v0.22.0 // indirect | |||
golang.org/x/net v0.24.0 // indirect | |||
golang.org/x/sync v0.6.0 // indirect | |||
golang.org/x/sys v0.18.0 // indirect | |||
golang.org/x/sys v0.19.0 // indirect | |||
golang.org/x/text v0.14.0 // indirect | |||
golang.org/x/tools v0.6.0 // indirect | |||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect | |||
google.golang.org/appengine v1.4.0 // indirect | |||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 // indirect | |||
gopkg.in/yaml.v3 v3.0.1 // indirect | |||
honnef.co/go/tools v0.0.1-2020.1.4 // indirect | |||
@@ -23,12 +23,6 @@ func init() { | |||
if err := db.InitDB(cfg.DB); err != nil { //主数据库初始化 | |||
panic(err) | |||
} | |||
if err := db.InitDataDB(cfg.DataDB); err != nil { //数据大屏本身库初始化 | |||
panic(err) | |||
} | |||
if err := db.InitImDB(cfg.ImDB); err != nil { //im本身库初始化 | |||
panic(err) | |||
} | |||
channel := make(chan int, 0) //开辟管道,缓冲为 | |||
go db.InitDBs(channel) | |||
<-channel | |||