diff --git a/app/cfg/init_cfg.go b/app/cfg/init_cfg.go index 5e16535..a678345 100644 --- a/app/cfg/init_cfg.go +++ b/app/cfg/init_cfg.go @@ -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 } diff --git a/app/db/db_sys_mod.go b/app/db/db_sys_mod.go index 0003b2b..6602d75 100644 --- a/app/db/db_sys_mod.go +++ b/app/db/db_sys_mod.go @@ -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 { //替换链接的一些参数 diff --git a/app/db/dbs_map.go b/app/db/dbs_map.go index ac82dc3..0576b18 100644 --- a/app/db/dbs_map.go +++ b/app/db/dbs_map.go @@ -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 } diff --git a/app/lib/auth/base.go b/app/lib/auth/base.go new file mode 100644 index 0000000..a886c2e --- /dev/null +++ b/app/lib/auth/base.go @@ -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 +} diff --git a/app/md/applet.go b/app/md/applet.go new file mode 100644 index 0000000..ee9e925 --- /dev/null +++ b/app/md/applet.go @@ -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" +) diff --git a/app/md/instant_sms.go b/app/md/instant_sms.go new file mode 100644 index 0000000..b6aa76c --- /dev/null +++ b/app/md/instant_sms.go @@ -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"` +} diff --git a/app/md/official_recommend_list.go b/app/md/official_recommend_list.go new file mode 100644 index 0000000..568543a --- /dev/null +++ b/app/md/official_recommend_list.go @@ -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"` +} diff --git a/app/md/oil.go b/app/md/oil.go new file mode 100644 index 0000000..97c4673 --- /dev/null +++ b/app/md/oil.go @@ -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"` +} diff --git a/app/md/phone_charge.go b/app/md/phone_charge.go new file mode 100644 index 0000000..5391fd6 --- /dev/null +++ b/app/md/phone_charge.go @@ -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"` +} diff --git a/app/md/privilege_card.go b/app/md/privilege_card.go new file mode 100644 index 0000000..f4f4d90 --- /dev/null +++ b/app/md/privilege_card.go @@ -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"` +} diff --git a/app/md/publisher_info.go b/app/md/publisher_info.go new file mode 100644 index 0000000..8dc73ba --- /dev/null +++ b/app/md/publisher_info.go @@ -0,0 +1,11 @@ +package md + +// 会员备案信息 + +type PublisherInfo struct { + RelationId string // 渠道id + SpecialId string // 会员运营id + ExternalId string // 自定义参数 + Rtag string // 备注 + +} diff --git a/app/md/url.go b/app/md/url.go new file mode 100644 index 0000000..1f89265 --- /dev/null +++ b/app/md/url.go @@ -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"` +} diff --git a/app/md/word.go b/app/md/word.go new file mode 100644 index 0000000..b5ddc4c --- /dev/null +++ b/app/md/word.go @@ -0,0 +1,11 @@ +package md + +// 淘口令 + +type TaobaoWord struct { + Text string // 文本 + Code string // 短码 +} +type TaobaoWordGid struct { + Gid string // 商品id +} diff --git a/app/svc/svc_order_track_save_update.go b/app/svc/svc_order_track_save_update.go index e6b25dc..794e64b 100644 --- a/app/svc/svc_order_track_save_update.go +++ b/app/svc/svc_order_track_save_update.go @@ -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), diff --git a/app/utils/auth.go b/app/utils/auth.go new file mode 100644 index 0000000..d7bd9ae --- /dev/null +++ b/app/utils/auth.go @@ -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") +} diff --git a/app/utils/cachesecond/base.go b/app/utils/cachesecond/base.go new file mode 100644 index 0000000..126c4d3 --- /dev/null +++ b/app/utils/cachesecond/base.go @@ -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) +} diff --git a/app/utils/cachesecond/cache/cache.go b/app/utils/cachesecond/cache/cache.go new file mode 100644 index 0000000..e43c5f0 --- /dev/null +++ b/app/utils/cachesecond/cache/cache.go @@ -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) +} diff --git a/app/utils/cachesecond/cache/conv.go b/app/utils/cachesecond/cache/conv.go new file mode 100644 index 0000000..6b700ae --- /dev/null +++ b/app/utils/cachesecond/cache/conv.go @@ -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 +} diff --git a/app/utils/cachesecond/cache/file.go b/app/utils/cachesecond/cache/file.go new file mode 100644 index 0000000..5c4e366 --- /dev/null +++ b/app/utils/cachesecond/cache/file.go @@ -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) +} diff --git a/app/utils/cachesecond/cache/memory.go b/app/utils/cachesecond/cache/memory.go new file mode 100644 index 0000000..0cc5015 --- /dev/null +++ b/app/utils/cachesecond/cache/memory.go @@ -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) +} diff --git a/app/utils/cachesecond/redis.go b/app/utils/cachesecond/redis.go new file mode 100644 index 0000000..99c5247 --- /dev/null +++ b/app/utils/cachesecond/redis.go @@ -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 +} diff --git a/app/utils/cachesecond/redis_cluster.go b/app/utils/cachesecond/redis_cluster.go new file mode 100644 index 0000000..c86bf55 --- /dev/null +++ b/app/utils/cachesecond/redis_cluster.go @@ -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 +} diff --git a/app/utils/cachesecond/redis_pool.go b/app/utils/cachesecond/redis_pool.go new file mode 100644 index 0000000..501aa27 --- /dev/null +++ b/app/utils/cachesecond/redis_pool.go @@ -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 +} diff --git a/app/utils/cachesecond/redis_pool_cluster.go b/app/utils/cachesecond/redis_pool_cluster.go new file mode 100644 index 0000000..30ea8ac --- /dev/null +++ b/app/utils/cachesecond/redis_pool_cluster.go @@ -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 +} diff --git a/app/utils/json.go b/app/utils/json.go index 998bcec..2905833 100644 --- a/app/utils/json.go +++ b/app/utils/json.go @@ -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 +} diff --git a/consume/zhios_user_visit_ip_address_consume.go b/consume/zhios_user_visit_ip_address_consume.go deleted file mode 100644 index c050907..0000000 --- a/consume/zhios_user_visit_ip_address_consume.go +++ /dev/null @@ -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) -} diff --git a/go.mod b/go.mod index 9bd348e..b05e745 100644 --- a/go.mod +++ b/go.mod @@ -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 diff --git a/main.go b/main.go index 08535b1..d02a917 100644 --- a/main.go +++ b/main.go @@ -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