commit debe30a374032b468d1f6ea22eed88bd7bbdf4e8
Author: huangjuajun <102564160@qq.com>
Date: Fri Jun 24 17:54:32 2022 +0800
add reverse for v1.0.0 分佣规则
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..35410ca
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,8 @@
+# 默认忽略的文件
+/shelf/
+/workspace.xml
+# 基于编辑器的 HTTP 客户端请求
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..aa214a6
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..94a25f7
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/zyos_go_order_relate_rule.iml b/.idea/zyos_go_order_relate_rule.iml
new file mode 100644
index 0000000..5e764c4
--- /dev/null
+++ b/.idea/zyos_go_order_relate_rule.iml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/db/db_sys_cfg.go b/db/db_sys_cfg.go
new file mode 100644
index 0000000..c5cab2c
--- /dev/null
+++ b/db/db_sys_cfg.go
@@ -0,0 +1,57 @@
+package db
+
+import (
+ "code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/db/model"
+ "code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/md"
+ "code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/utils/cache"
+ zhios_order_relate_logx "code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/utils/logx"
+ "fmt"
+ "xorm.io/xorm"
+)
+
+// 系统配置get
+func SysCfgGetAll(Db *xorm.Engine) (*[]model.SysCfg, error) {
+ var cfgList []model.SysCfg
+ if err := Db.Cols("key,val,memo").Find(&cfgList); err != nil {
+ return nil, zhios_order_relate_logx.Error(err)
+ }
+ return &cfgList, nil
+}
+
+// 获取一条记录
+func SysCfgGetOne(Db *xorm.Engine, key string) (*model.SysCfg, error) {
+ var cfgList model.SysCfg
+ if has, err := Db.Where("`key`=?", key).Get(&cfgList); err != nil || has == false {
+ return nil, zhios_order_relate_logx.Error(err)
+ }
+ return &cfgList, nil
+}
+
+// 多条记录获取DB
+func SysCfgFindWithDb(eg *xorm.Engine, masterId string, keys ...string) map[string]string {
+ res := map[string]string{}
+ cacheKey := fmt.Sprintf(md.AppCfgCacheKey, masterId)
+ err := cache.GetJson(cacheKey, &res)
+ if err != nil || len(res) == 0 {
+ cfgList, _ := SysCfgGetAll(eg)
+ if cfgList == nil {
+ return nil
+ }
+ for _, v := range *cfgList {
+ res[v.Key] = v.Val
+ }
+ cache.SetJson(cacheKey, res, md.CfgCacheTime)
+ }
+ if len(keys) == 0 {
+ return res
+ }
+ tmp := map[string]string{}
+ for _, v := range keys {
+ if val, ok := res[v]; ok {
+ tmp[v] = val
+ } else {
+ tmp[v] = ""
+ }
+ }
+ return tmp
+}
diff --git a/db/db_user.go b/db/db_user.go
new file mode 100644
index 0000000..14360b6
--- /dev/null
+++ b/db/db_user.go
@@ -0,0 +1,17 @@
+package db
+
+import (
+ "code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/db/model"
+ zhios_order_relate_logx "code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/utils/logx"
+ "xorm.io/xorm"
+)
+
+// UserFindByID is find user byid
+func UserFindByID(Db *xorm.Engine, id interface{}) (*model.User, error) {
+ var m model.User
+ if has, err := Db.Where("uid = ?", id).
+ Get(&m); err != nil || has == false {
+ return nil, zhios_order_relate_logx.Warn(err)
+ }
+ return &m, nil
+}
diff --git a/db/db_user_level.go b/db/db_user_level.go
new file mode 100644
index 0000000..6990057
--- /dev/null
+++ b/db/db_user_level.go
@@ -0,0 +1,27 @@
+package db
+
+import (
+ "code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/db/model"
+ zhios_order_relate_logx "code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/utils/logx"
+ "xorm.io/xorm"
+)
+
+//UserLevelInIDescByWeight is In 查询获取 权重最低 对应等级
+func UserLevelInIDescByWeightLow(Db *xorm.Engine) ([]*model.UserLevel, error) {
+ var ms []*model.UserLevel
+ if err := Db.Asc("level_weight").Limit(1).Find(&ms); err != nil {
+ return nil, err
+ }
+ return ms, nil
+
+}
+
+// UserLevlEgAll is 获取所有开启等级并且升序返回
+func UserLevlEgAll(Db *xorm.Engine) ([]*model.UserLevel, error) {
+ var m []*model.UserLevel
+ err := Db.Where("is_use = ?", 1).Asc("level_weight").Find(&m)
+ if err != nil {
+ return nil, zhios_order_relate_logx.Warn(err)
+ }
+ return m, nil
+}
diff --git a/db/db_virtual_coin.go b/db/db_virtual_coin.go
new file mode 100644
index 0000000..13fcc13
--- /dev/null
+++ b/db/db_virtual_coin.go
@@ -0,0 +1,26 @@
+package db
+
+import (
+ "code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/db/model"
+ "code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/md"
+ "code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/utils/cache"
+ "fmt"
+ "xorm.io/xorm"
+)
+
+// VirtualCoinListInUse 查询正在使用中的虚拟币
+func VirtualCoinListInUse(Db *xorm.Engine, masterId string) ([]*model.VirtualCoin, error) {
+ var m []*model.VirtualCoin
+ cacheKey := fmt.Sprintf(md.VirtualCoinCfgCacheKey, masterId)
+
+ err := cache.GetJson(cacheKey, &m)
+ if err != nil || len(m) == 0 {
+ err := Db.Where("is_use=1").Find(&m)
+ if err != nil {
+ return nil, err
+ }
+ cache.SetJson(cacheKey, m, md.CfgCacheTime)
+ }
+
+ return m, nil
+}
diff --git a/db/dbs_plan_commission.go b/db/dbs_plan_commission.go
new file mode 100644
index 0000000..ce9d8bf
--- /dev/null
+++ b/db/dbs_plan_commission.go
@@ -0,0 +1,29 @@
+package db
+
+import (
+ "code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/db/model"
+ zhios_order_relate_logx "code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/utils/logx"
+ "xorm.io/xorm"
+)
+
+func DbsPlanCommissionById(eg *xorm.Engine, id int) (*model.PlanCommission, error) {
+ var m model.PlanCommission
+ if isGet, err := eg.Where("id = ?", id).Get(&m); err != nil || !isGet {
+ return nil, zhios_order_relate_logx.Warn(err)
+ }
+ return &m, nil
+}
+
+func DbsPlanCommissionByIds(eg *xorm.Engine, ids ...int) []*model.PlanCommission {
+ var m []*model.PlanCommission
+ var err error
+ if len(ids) > 0 {
+ err = eg.In("id", ids).Find(&m)
+ } else {
+ err = eg.Find(&m)
+ }
+ if err != nil {
+ return nil
+ }
+ return m
+}
diff --git a/db/dbs_plan_reward.go b/db/dbs_plan_reward.go
new file mode 100644
index 0000000..4d40c4f
--- /dev/null
+++ b/db/dbs_plan_reward.go
@@ -0,0 +1,31 @@
+package db
+
+import (
+ "code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/db/model"
+ zhios_order_relate_logx "code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/utils/logx"
+ "xorm.io/xorm"
+
+)
+
+func DbsPlanRewardByPvd(eg *xorm.Engine, pvd string) (*model.PlanReward, error) {
+ m := &model.PlanReward{}
+ if isGet, err := eg.Where("pvd = ?", pvd).Get(m); err != nil || !isGet {
+ return nil, zhios_order_relate_logx.Warn(err)
+ }
+
+ return m, nil
+}
+
+func DbsPlanRewardByPvds(eg *xorm.Engine, pvds ...string) ([]*model.PlanReward, error) {
+ var m []*model.PlanReward
+ var err error
+ if len(pvds) > 0 {
+ err = eg.In("pvd", pvds).Find(&m)
+ } else {
+ err = eg.Find(&m)
+ }
+ if err != nil {
+ return nil, err
+ }
+ return m, nil
+}
diff --git a/db/dbs_user.go b/db/dbs_user.go
new file mode 100644
index 0000000..7b2237b
--- /dev/null
+++ b/db/dbs_user.go
@@ -0,0 +1,29 @@
+package db
+
+import (
+ "code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/db/model"
+ zhios_order_relate_logx "code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/utils/logx"
+ "xorm.io/xorm"
+)
+
+func DbsUserFindByIds(eg *xorm.Engine, uid []int) (*[]model.User, error) {
+ var users []model.User
+ if err := eg.In("uid", uid).Asc("level").Find(&users); err != nil {
+ return nil, zhios_order_relate_logx.Error(err)
+ }
+ if len(users) == 0 {
+ return nil, nil
+ }
+ return &users, nil
+}
+
+func DbsUserRelate(eg *xorm.Engine, uid int) (*[]model.UserRelate, error) {
+ var userRelate []model.UserRelate
+ if err := eg.Where("uid = ?", uid).Asc("level").Find(&userRelate); err != nil {
+ return nil, zhios_order_relate_logx.Error(err)
+ }
+ if len(userRelate) == 0 {
+ return nil, nil
+ }
+ return &userRelate, nil
+}
diff --git a/db/model/plan_commission.go b/db/model/plan_commission.go
new file mode 100644
index 0000000..627d5bb
--- /dev/null
+++ b/db/model/plan_commission.go
@@ -0,0 +1,15 @@
+package model
+
+import "time"
+
+type PlanCommission struct {
+ Id int `json:"id" xorm:"not null pk autoincr comment('分佣方案ID,现在只允许1,2') INT(10)"`
+ PlanName string `json:"plan_name" xorm:"not null default '' comment('方案名称') VARCHAR(64)"`
+ Memo string `json:"memo" xorm:"not null default '' comment('备注') VARCHAR(256)"`
+ Mode string `json:"mode" xorm:"not null default '' comment('模式,lv_all级差按总佣金,lv_self级差按自购,lv_subsidy级差按补贴佣金') VARCHAR(16)"`
+ Data string `json:"data" xorm:"comment('lv级别id,subsidy_mode:0按佣金计算,1按利润计算,self_rate自购比例,team_rate团队分成比例,peer_rate同级分成比例,subsidy_enable:0不开启,1:开启,subsidy_share_rate分享补贴比例,subsidy_self_reta自购补贴比例,payMode默认0(默认内部支出)') TEXT"`
+ UpdateAt time.Time `json:"update_at" xorm:"default 'CURRENT_TIMESTAMP' TIMESTAMP"`
+ CommissionMode string `json:"commission_mode" xorm:"default 'bili' comment('佣金返佣方式 比例 bili 固定金额 money') VARCHAR(16)"`
+ IntegralMode string `json:"integral_mode" xorm:"default 'bili' comment('积分返佣方式 比例 bili 固定金额 money') VARCHAR(16)"`
+ BlockIconsMode string `json:"block_icons_mode" xorm:"default 'bili' comment('区块币返佣方式 比例 bili 固定金额 money') VARCHAR(16)"`
+}
diff --git a/db/model/plan_reward.go b/db/model/plan_reward.go
new file mode 100644
index 0000000..17243c7
--- /dev/null
+++ b/db/model/plan_reward.go
@@ -0,0 +1,20 @@
+package model
+
+type PlanReward struct {
+ Id int `json:"id" xorm:"not null pk autoincr INT(10)"`
+ Pvd string `json:"pvd" xorm:"not null comment('供应商') unique VARCHAR(255)"`
+ PvdRate float32 `json:"pvd_rate" xorm:"not null default 0.0000 comment('供应商抽成比例') FLOAT(6,4)"`
+ SysRate float32 `json:"sys_rate" xorm:"not null default 0.0000 comment('平台抽成比例') FLOAT(6,4)"`
+ RegionRate float32 `json:"region_rate" xorm:"not null default 0.0000 comment('区域代理抽成比例') FLOAT(6,4)"`
+ GlobalRate float32 `json:"global_rate" xorm:"not null default 0.0000 comment('全球分红抽成比例') FLOAT(6,4)"`
+ SettleMode int `json:"settle_mode" xorm:"not null default 1 comment('0.手动方案,1.自动方案') TINYINT(1)"`
+ PlanCommissionId int `json:"plan_commission_id" xorm:"not null default 0 comment('佣金方案0未设置,>0对应方案') TINYINT(3)"`
+ PlanSettleId int `json:"plan_settle_id" xorm:"not null default 0 comment('结算方案0未设置,>0对应方案') TINYINT(3)"`
+ State int `json:"state" xorm:"not null default 1 comment('0关闭,1开启') TINYINT(1)"`
+ SubsidyRate float32 `json:"subsidy_rate" xorm:"not null default 0.0000 comment('待删除字段') FLOAT(6,4)"`
+ Source int `json:"source" xorm:"not null default 1 comment('佣金来源:1联盟佣金 2补贴金额') TINYINT(1)"`
+ AppType int `json:"app_type" xorm:"not null default 1 comment('所属应用:1导购 2自营 4O2O') TINYINT(3)"`
+ MerchantRate float32 `json:"merchant_rate" xorm:"not null default 0.0000 comment('o2o商家抽成比例') FLOAT(6,4)"`
+ PushHandRate float32 `json:"push_hand_rate" xorm:"not null default 0.0000 comment('o2o推手抽成比例') FLOAT(6,4)"`
+ IntegralOpen int `json:"integral_open" xorm:"not null default 0 comment('积分抽成') TINYINT(1)"`
+}
diff --git a/db/model/sys_cfg.go b/db/model/sys_cfg.go
new file mode 100644
index 0000000..22d906b
--- /dev/null
+++ b/db/model/sys_cfg.go
@@ -0,0 +1,7 @@
+package model
+
+type SysCfg struct {
+ Key string `json:"key" xorm:"not null pk comment('键') VARCHAR(127)"`
+ Val string `json:"val" xorm:"comment('值') TEXT"`
+ Memo string `json:"memo" xorm:"not null default '' comment('备注') VARCHAR(255)"`
+}
diff --git a/db/model/user.go b/db/model/user.go
new file mode 100644
index 0000000..5a3707d
--- /dev/null
+++ b/db/model/user.go
@@ -0,0 +1,29 @@
+package model
+
+import (
+ "time"
+)
+
+type User struct {
+ Uid int `json:"uid" xorm:"not null pk autoincr comment('主键ID') INT(10)"`
+ Username string `json:"username" xorm:"not null default '' comment('用户名') index VARCHAR(50)"`
+ Password string `json:"password" xorm:"not null default '' comment('密码') CHAR(32)"`
+ Email string `json:"email" xorm:"not null default '' comment('邮箱') VARCHAR(128)"`
+ Phone string `json:"phone" xorm:"not null default '' comment('联系电话') VARCHAR(20)"`
+ Nickname string `json:"nickname" xorm:"not null default '' comment('昵称') VARCHAR(20)"`
+ Level int `json:"level" xorm:"not null default 0 comment('用户等级id') INT(11)"`
+ InviteTotal int `json:"invite_total" xorm:"not null default 0 comment('直推邀请总人数') INT(11)"`
+ LevelArriveAt time.Time `json:"level_arrive_at" xorm:"not null default CURRENT_TIMESTAMP comment('到达该等级的时间') TIMESTAMP"`
+ LevelExpireAt time.Time `json:"level_expire_at" xorm:"not null default CURRENT_TIMESTAMP comment('该等级过期时间') TIMESTAMP"`
+ CreateAt time.Time `json:"create_at" xorm:"created not null default CURRENT_TIMESTAMP comment('创建时间') TIMESTAMP"`
+ UpdateAt time.Time `json:"update_at" xorm:"updated default CURRENT_TIMESTAMP comment('最后修改资料时间') TIMESTAMP"`
+ LastLoginAt time.Time `json:"last_login_at" xorm:"default CURRENT_TIMESTAMP comment('最近登录时间') TIMESTAMP"`
+ DeleteAt int `json:"delete_at" xorm:"not null default 0 comment('是否删除;0未删除;1已删除') TINYINT(1)"`
+ State int `json:"state" xorm:"not null default 1 comment('0未激活,1正常,2冻结,3删除') TINYINT(1)"`
+ LastLoginIp string `json:"last_login_ip" xorm:"not null default '' comment('最后登录IP') VARCHAR(64)"`
+ RegisterIp string `json:"register_ip" xorm:"not null default '' comment('注册IP') VARCHAR(64)"`
+ Zone string `json:"zone" xorm:"not null default '86' comment('区号') VARCHAR(100)"`
+ SalePhone string `json:"sale_phone" xorm:"not null default '' comment('') VARCHAR(100)"`
+ IsFake int `json:"is_fake" xorm:"not null default 0 comment('0真实 1虚拟') TINYINT(1)"`
+ IsMarketer int `json:"is_marketer" xorm:"not null default 0 comment('是否市商 0否 1是') TINYINT(1)"`
+}
diff --git a/db/model/user_level.go b/db/model/user_level.go
new file mode 100644
index 0000000..11e11be
--- /dev/null
+++ b/db/model/user_level.go
@@ -0,0 +1,21 @@
+package model
+
+import (
+ "time"
+)
+
+type UserLevel struct {
+ Id int `json:"id" xorm:"not null pk autoincr comment('等级id') INT(11)"`
+ BenefitIds string `json:"benefit_ids" xorm:"comment('该等级拥有的权益id【json】') TEXT"`
+ LevelName string `json:"level_name" xorm:"not null default '' comment('等级名称') VARCHAR(255)"`
+ LevelWeight int `json:"level_weight" xorm:"not null default 0 comment('等级权重') INT(11)"`
+ LevelUpdateCondition int `json:"level_update_condition" xorm:"not null default 2 comment('2是条件升级,1是无条件升级') TINYINT(1)"`
+ AutoAudit int `json:"auto_audit" xorm:"not null default 0 comment('(自动审核)0关闭,1开启') TINYINT(1)"`
+ AutoUpdate int `json:"auto_update" xorm:"not null default 0 comment('(自动升级)0关闭,1开启') TINYINT(1)"`
+ LevelDate int `json:"level_date" xorm:"default 0 comment('会员有效期(0永久有效,单位月)') INT(11)"`
+ IsUse int `json:"is_use" xorm:"not null default 1 comment('是否开启(0否,1是)') TINYINT(1)"`
+ ChoosableNum int `json:"choosable_num" xorm:"default 0 comment('可选任务数量(当is_must_task为0时生效)') INT(6)"`
+ Memo string `json:"memo" xorm:"default '' comment('备注') VARCHAR(255)"`
+ CssSet string `json:"css_set" xorm:"TEXT"`
+ CreateAt time.Time `json:"create_at" xorm:"not null default CURRENT_TIMESTAMP TIMESTAMP"`
+}
diff --git a/db/model/user_profile.go b/db/model/user_profile.go
new file mode 100644
index 0000000..3bd7fe1
--- /dev/null
+++ b/db/model/user_profile.go
@@ -0,0 +1,91 @@
+package model
+
+import (
+ "time"
+)
+
+type UserProfile struct {
+ Uid int `json:"uid" xorm:"not null pk comment('关联userID') INT(20)"`
+ ArkidUid int `json:"arkid_uid" xorm:"not null default 0 comment('Arkid 用户ID') INT(20)"`
+ ParentUid int `json:"parent_uid" xorm:"not null default 0 comment('上级ID') INT(20)"`
+ ArkidToken string `json:"arkid_token" xorm:"not null default '' comment('token') VARCHAR(2000)"`
+ AvatarUrl string `json:"avatar_url" xorm:"not null default '' comment('头像URL') VARCHAR(2000)"`
+ CustomInviteCode string `json:"custom_invite_code" xorm:"not null default '' comment('邀请码(自定义)') VARCHAR(16)"`
+ InviteCode string `json:"invite_code" xorm:"not null default '' comment('邀请码(系统)') VARCHAR(16)"`
+ Gender int `json:"gender" xorm:"not null default 2 comment('性别0女,1男,2未知') TINYINT(1)"`
+ Birthday int `json:"birthday" xorm:"not null default 0 comment('出生日期') INT(10)"`
+ AccWxId string `json:"acc_wx_id" xorm:"not null default '' comment('账户_微信id') VARCHAR(50)"`
+ AccWxOpenid string `json:"acc_wx_openid" xorm:"not null default '' comment('账户_微信openid') VARCHAR(80)"`
+ AccTaobaoNickname string `json:"acc_taobao_nickname" xorm:"not null default '' comment('淘宝昵称') VARCHAR(50)"`
+ AccTaobaoAuthTime int64 `json:"acc_taobao_auth_time" xorm:"not null default 0 comment('淘宝授权备案时间') BIGINT(11)"`
+ AccTaobaoShareId int64 `json:"acc_taobao_share_id" xorm:"not null default 0 comment('淘宝分享relationId,') index BIGINT(12)"`
+ AccTaobaoSelfId int64 `json:"acc_taobao_self_id" xorm:"not null default 0 comment('淘宝自购specialId') index BIGINT(12)"`
+ AccJdSelfId string `json:"acc_jd_self_id" xorm:"not null default '' comment('京东自购ID') index VARCHAR(50)"`
+ AccJdShareId string `json:"acc_jd_share_id" xorm:"not null default '' comment('京东分享ID') index VARCHAR(50)"`
+ AccJdFreeId string `json:"acc_jd_free_id" xorm:"not null default '' comment('京东新人免单ID') VARCHAR(50)"`
+ AccSuningSelfId string `json:"acc_suning_self_id" xorm:"not null default '' comment('苏宁自购ID') index VARCHAR(50)"`
+ AccSuningShareId string `json:"acc_suning_share_id" xorm:"not null default '' comment('苏宁分享ID') index VARCHAR(50)"`
+ AccSuningFreeId string `json:"acc_suning_free_id" xorm:"not null default '' comment('苏宁新人免单ID') VARCHAR(50)"`
+ AccPddSelfId string `json:"acc_pdd_self_id" xorm:"not null default '' comment('拼多多自购ID') index VARCHAR(50)"`
+ AccPddShareId string `json:"acc_pdd_share_id" xorm:"not null default '' comment('拼多多分享ID') index VARCHAR(50)"`
+ AccPddFreeId string `json:"acc_pdd_free_id" xorm:"not null default '' comment('拼多多新人免单ID') VARCHAR(50)"`
+ AccPddBind int `json:"acc_pdd_bind" xorm:"not null default 0 comment('拼多多是否授权绑定') TINYINT(1)"`
+ AccVipSelfId string `json:"acc_vip_self_id" xorm:"not null default '' comment('唯品会自购ID') index VARCHAR(50)"`
+ AccVipShareId string `json:"acc_vip_share_id" xorm:"not null default '' comment('唯品会分享ID') index VARCHAR(50)"`
+ AccVipFreeId string `json:"acc_vip_free_id" xorm:"not null default '' comment('唯品会新人免单ID') VARCHAR(50)"`
+ AccKaolaSelfId string `json:"acc_kaola_self_id" xorm:"not null default '' comment('考拉自购ID') index VARCHAR(50)"`
+ AccKaolaShareId string `json:"acc_kaola_share_id" xorm:"not null default '' comment('考拉分享ID') index VARCHAR(50)"`
+ AccKaolaFreeId string `json:"acc_kaola_free_id" xorm:"not null default '' comment('考拉新人免单ID') VARCHAR(50)"`
+ AccDuomaiShareId int64 `json:"acc_duomai_share_id" xorm:"not null pk default 0 comment('多麦联盟分享ID') BIGINT(12)"`
+ AccAlipay string `json:"acc_alipay" xorm:"not null default '' comment('支付宝账号') VARCHAR(50)"`
+ AccAlipayRealName string `json:"acc_alipay_real_name" xorm:"not null default '' comment('支付宝账号真实姓名') VARCHAR(50)"`
+ CertTime int `json:"cert_time" xorm:"not null default 0 comment('认证时间') INT(10)"`
+ CertName string `json:"cert_name" xorm:"not null default '' comment('证件上名字,也是真实姓名') VARCHAR(50)"`
+ CertNum string `json:"cert_num" xorm:"not null default '' comment('证件号码') VARCHAR(50)"`
+ CertState int `json:"cert_state" xorm:"not null default 0 comment('认证状态(0为未认证,1为认证中,2为已认证,3为认证失败)') TINYINT(1)"`
+ FinCommission string `json:"fin_commission" xorm:"not null default 0.0000 comment('累计佣金') DECIMAL(10,4)"`
+ FinValid string `json:"fin_valid" xorm:"not null default 0.0000 comment('可用余额,fin=>finance财务') DECIMAL(10,4)"`
+ FinInvalid string `json:"fin_invalid" xorm:"not null default 0.0000 comment('不可用余额,冻结余额') DECIMAL(10,4)"`
+ FinSelfOrderCount int `json:"fin_self_order_count" xorm:"not null default 0 comment('自购订单数,包括未完成') INT(11)"`
+ FinSelfOrderCountDone int `json:"fin_self_order_count_done" xorm:"not null default 0 comment('自购已完成订单') INT(11)"`
+ FinSelfRebate float32 `json:"fin_self_rebate" xorm:"not null default 0.000000 comment('累积自购获得返利金额') FLOAT(14,6)"`
+ FinTotal float32 `json:"fin_total" xorm:"not null default 0.000000 comment('累计总收益') FLOAT(14,6)"`
+ Lat float32 `json:"lat" xorm:"not null default 0.000000 comment('纬度') FLOAT(15,6)"`
+ Lng float32 `json:"lng" xorm:"not null default 0.000000 comment('经度') FLOAT(15,6)"`
+ Memo string `json:"memo" xorm:"not null default '' comment('用户简述备注') VARCHAR(2048)"`
+ IsNew int `json:"is_new" xorm:"not null default 1 comment('是否是新用户') TINYINT(1)"`
+ IsVerify int `json:"is_verify" xorm:"not null default 0 comment('是否有效会员') TINYINT(1)"`
+ IsOrdered int `json:"is_ordered" xorm:"not null default 0 comment('是否已完成首单(0否,1是)') TINYINT(1)"`
+ FromWay string `json:"from_way" xorm:"not null default '' comment('注册来源:
+no_captcha_phone:免验证码手机号注册;
+manual_phone:手动手机验证码注册;
+wx:微信授权;
+wx_mp:小程序授权;
+wx_pub:公众号授权;
+wx_bind_phone:微信注册绑定手机号;
+admin:管理员添加;taobao_bind_phone:淘宝注册绑定手机号,apple_bind_phone:苹果注册绑定手机号') VARCHAR(16)"`
+ HidOrder int `json:"hid_order" xorm:"not null default 0 comment('隐藏订单') TINYINT(3)"`
+ HidContact int `json:"hid_contact" xorm:"not null default 0 comment('隐藏联系方式') TINYINT(4)"`
+ NewMsgNotice int `json:"new_msg_notice" xorm:"not null default 1 comment('新消息通知') TINYINT(1)"`
+ WxAccount string `json:"wx_account" xorm:"not null default '' comment('微信号') VARCHAR(100)"`
+ WxQrcode string `json:"wx_qrcode" xorm:"not null default '' comment('微信二维码') VARCHAR(100)"`
+ ThirdPartyTaobaoOid string `json:"third_party_taobao_oid" xorm:"not null default '' comment('淘宝第三方登录openID') VARCHAR(100)"`
+ ThirdPartyTaobaoSid string `json:"third_party_taobao_sid" xorm:"not null default '' comment('淘宝第三方登录sID') VARCHAR(255)"`
+ ThirdPartyTaobaoAcctoken string `json:"third_party_taobao_acctoken" xorm:"not null default '' comment('淘宝第三方登录topaccesstoken') VARCHAR(100)"`
+ ThirdPartyTaobaoAuthcode string `json:"third_party_taobao_authcode" xorm:"not null default '' comment('淘宝第三方登录topAuthCode') VARCHAR(100)"`
+ ThirdPartyAppleToken string `json:"third_party_apple_token" xorm:"not null default '' comment('苹果第三方登录token') VARCHAR(1024)"`
+ ThirdPartyQqAccessToken string `json:"third_party_qq_access_token" xorm:"not null default '' comment('QQ第三方登录access_token') VARCHAR(255)"`
+ ThirdPartyQqExpiresIn string `json:"third_party_qq_expires_in" xorm:"not null default '' comment('QQ第三方登录expires_in(剩余时长)') VARCHAR(255)"`
+ ThirdPartyQqOpenid string `json:"third_party_qq_openid" xorm:"not null default '' comment('QQ第三方登陆openid(不变,用于认证)') VARCHAR(255)"`
+ ThirdPartyQqUnionid string `json:"third_party_qq_unionid" xorm:"not null default '' comment('QQ第三方登陆unionid') VARCHAR(255)"`
+ ThirdPartyWechatExpiresIn string `json:"third_party_wechat_expires_in" xorm:"not null default '' comment('微信第三方登录expires_in(剩余时长)') VARCHAR(255)"`
+ ThirdPartyWechatOpenid string `json:"third_party_wechat_openid" xorm:"not null default '' comment('微信第三方登陆openid(不变,用于认证)') VARCHAR(255)"`
+ ThirdPartyWechatUnionid string `json:"third_party_wechat_unionid" xorm:"not null default '' comment('微信第三方登陆unionid') VARCHAR(255)"`
+ ThirdPartyWechatMiniOpenid string `json:"third_party_wechat_mini_openid" xorm:"not null default '' comment('微信小程序登录open_id') VARCHAR(255)"`
+ ThirdPartyWechatH5Openid string `json:"third_party_wechat_h5_openid" xorm:"not null default '' comment('微信H5登录open_id') VARCHAR(255)"`
+ FreeRemainTime int `json:"free_remain_time" xorm:"not null default 0 comment('免单剩余次数') INT(11)"`
+ FreeCumulativeTime int `json:"free_cumulative_time" xorm:"not null default 0 comment('免单累计次数') INT(11)"`
+ IsDelete int `json:"is_delete" xorm:"not null default 0 comment('是否已删除') TINYINT(1)"`
+ UpdateAt time.Time `json:"update_at" xorm:"updated not null default CURRENT_TIMESTAMP comment('更新时间') TIMESTAMP"`
+ SignDay int `json:"sign_day" xorm:"not null default 0 comment('签到天数') INT(11)"`
+}
diff --git a/db/model/user_relate.go b/db/model/user_relate.go
new file mode 100644
index 0000000..375562f
--- /dev/null
+++ b/db/model/user_relate.go
@@ -0,0 +1,13 @@
+package model
+
+import (
+ "time"
+)
+
+type UserRelate struct {
+ Id int64 `json:"id" xorm:"pk autoincr comment('主键') BIGINT(10)"`
+ ParentUid int `json:"parent_uid" xorm:"not null default 0 comment('上级会员ID') unique(idx_union_u_p_id) INT(20)"`
+ Uid int `json:"uid" xorm:"not null default 0 comment('关联UserID') unique(idx_union_u_p_id) INT(20)"`
+ Level int `json:"level" xorm:"not null default 1 comment('推广等级(1直属,大于1非直属)') INT(10)"`
+ InviteTime time.Time `json:"invite_time" xorm:"not null default CURRENT_TIMESTAMP comment('邀请时间') TIMESTAMP"`
+}
diff --git a/db/model/virtual_coin.go b/db/model/virtual_coin.go
new file mode 100644
index 0000000..3baaa55
--- /dev/null
+++ b/db/model/virtual_coin.go
@@ -0,0 +1,17 @@
+package model
+
+type VirtualCoin struct {
+ Id int `json:"id" xorm:"not null pk autoincr INT(11)"`
+ Name string `json:"name" xorm:"not null default '' comment('名称') VARCHAR(255)"`
+ ExchangeRatio string `json:"exchange_ratio" xorm:"not null comment('兑换比例(与金额)') DECIMAL(5,2)"`
+ IsUse int `json:"is_use" xorm:"comment('是否开启:0否 1是') TINYINT(1)"`
+ CanExchange string `json:"can_exchange" xorm:"comment('能兑换的虚拟币id和手续费列表json') VARCHAR(255)"`
+ CanExchangeMoney int `json:"can_exchange_money" xorm:"not null default 0 comment('现金能否兑换:0否 1是') TINYINT(1)"`
+ IsBlock int `json:"is_block" xorm:"not null default 0 comment('是否区块币:0否 1是') TINYINT(1)"`
+ FunctionType string `json:"function_type" xorm:"comment('功能类型') VARCHAR(255)"`
+ CanCny int `json:"can_cny" xorm:"not null default 0 comment('是否能兑换余额:0否 1是') TINYINT(1)"`
+ CanTransfer int `json:"can_transfer" xorm:"not null default 0 comment('是否能支持转账:0否 1是') TINYINT(1)"`
+ CanBackout int `json:"can_backout" xorm:"not null default 0 comment('是否能支持转账撤回:0否 1是') TINYINT(1)"`
+ LimitLevelTransfer string `json:"limit_level_transfer" xorm:"default '' comment('能支持转账的用户等级') VARCHAR(600)"`
+ LimitLevelBackout string `json:"limit_level_backout" xorm:"comment('能支持撤回的用户等级') VARCHAR(600)"`
+}
diff --git a/db/model/virtual_coin_relate.go b/db/model/virtual_coin_relate.go
new file mode 100644
index 0000000..a629564
--- /dev/null
+++ b/db/model/virtual_coin_relate.go
@@ -0,0 +1,12 @@
+package model
+
+type VirtualCoinRelate struct {
+ Id int64 `json:"id" xorm:"pk autoincr BIGINT(20)"`
+ Oid int64 `json:"oid" xorm:"not null default 0 comment('订单号') index unique(IDX_ORD) BIGINT(20)"`
+ Uid int `json:"uid" xorm:"not null default 0 comment('用户ID') unique(IDX_ORD) index INT(10)"`
+ CoinId int `json:"coin_id" xorm:"comment('虚拟币id') INT(11)"`
+ Amount string `json:"amount" xorm:"not null default 0.000000 comment('数量') DECIMAL(16,6)"`
+ Pvd string `json:"pvd" xorm:"not null default '' comment('供应商taobao,jd,pdd,vip,suning,kaola,mall_goods,group_buy') index VARCHAR(255)"`
+ CreateAt int `json:"create_at" xorm:"not null default 0 comment('订单创建时间') index INT(10)"`
+ Level int `json:"level" xorm:"not null default 0 comment('0自购 1直推 大于1:间推') INT(10)"`
+}
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..0048340
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,13 @@
+module code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git
+
+go 1.15
+
+require (
+ github.com/go-redis/redis v6.15.9+incompatible
+ github.com/gomodule/redigo/redis v0.0.1
+ github.com/syyongx/php2go v0.9.6
+ go.uber.org/zap v1.13.0
+ golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f
+ gopkg.in/natefinch/lumberjack.v2 v2.0.0
+ xorm.io/xorm v1.3.1
+)
diff --git a/go.sum b/go.sum
new file mode 100644
index 0000000..5c80a99
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,662 @@
+cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:lSA0F4e9A2NcQSqGqTOXqu2aRi/XEQxDCBwM8yJtE6s=
+gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:EXuID2Zs0pAQhH8yz+DNjUbjppKQzKFAn28TMYPB6IU=
+gitee.com/travelliu/dm v1.8.11192/go.mod h1:DHTzyhCrM843x9VdKVbZ+GKXGRbKM2sJ4LxihRxShkE=
+github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
+github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
+github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
+github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
+github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
+github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
+github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
+github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
+github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
+github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
+github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
+github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
+github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
+github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
+github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
+github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
+github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
+github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
+github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
+github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
+github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
+github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
+github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
+github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
+github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
+github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
+github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE=
+github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
+github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
+github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
+github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
+github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
+github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
+github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
+github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
+github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
+github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/denisenkom/go-mssqldb v0.10.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
+github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
+github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
+github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
+github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
+github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
+github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
+github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
+github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
+github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g=
+github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
+github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
+github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4=
+github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
+github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
+github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
+github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
+github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
+github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
+github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o=
+github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
+github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
+github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
+github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg=
+github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
+github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
+github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
+github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
+github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
+github.com/goccy/go-json v0.8.1 h1:4/Wjm0JIJaTDm8K1KcGrLHJoa8EsJ13YWeX+6Kfq6uI=
+github.com/goccy/go-json v0.8.1/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
+github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
+github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
+github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
+github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
+github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
+github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
+github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
+github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
+github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/gomodule/redigo v1.8.8 h1:f6cXq6RRfiyrOJEV7p3JhLDlmawGBVBBP1MggY8Mo4E=
+github.com/gomodule/redigo/redis v0.0.1 h1:tQQSZyg4O0N0Dh2hli1pOrRdj+WHl1xf3w/x7olDgu0=
+github.com/gomodule/redigo/redis v0.0.1/go.mod h1:QhGMo2EGfdSsmrYDENZq12/Y23fRP6X6nyFZ4TGSUvM=
+github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
+github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
+github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.5.3 h1:x95R7cp+rSeeqAMI2knLtQ0DKlaBhv2NrtrOvafPHRo=
+github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
+github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
+github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
+github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
+github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
+github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
+github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
+github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
+github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
+github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
+github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE=
+github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
+github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
+github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
+github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
+github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
+github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
+github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
+github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
+github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
+github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
+github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
+github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
+github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
+github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
+github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
+github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
+github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
+github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
+github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
+github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
+github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
+github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg=
+github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
+github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
+github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
+github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
+github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
+github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA=
+github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE=
+github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s=
+github.com/jackc/pgconn v1.4.0/go.mod h1:Y2O3ZDF0q4mMacyWV3AstPJpeHXWGEetiFttmq5lahk=
+github.com/jackc/pgconn v1.5.0/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI=
+github.com/jackc/pgconn v1.5.1-0.20200601181101-fa742c524853/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI=
+github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o=
+github.com/jackc/pgconn v1.8.1/go.mod h1:JV6m6b6jhjdmzchES0drzCcYcAHS1OPD5xu3OZ/lE2g=
+github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY=
+github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8=
+github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE=
+github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c=
+github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
+github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78=
+github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA=
+github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg=
+github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
+github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
+github.com/jackc/pgproto3/v2 v2.0.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
+github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
+github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
+github.com/jackc/pgservicefile v0.0.0-20200307190119-3430c5407db8/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
+github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
+github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg=
+github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc=
+github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw=
+github.com/jackc/pgtype v1.2.0/go.mod h1:5m2OfMh1wTK7x+Fk952IDmI4nw3nPrvtQdM0ZT4WpC0=
+github.com/jackc/pgtype v1.3.1-0.20200510190516-8cd94a14c75a/go.mod h1:vaogEUkALtxZMCH411K+tKzNpwzCKU+AnPzBKZ+I+Po=
+github.com/jackc/pgtype v1.3.1-0.20200606141011-f6355165a91c/go.mod h1:cvk9Bgu/VzJ9/lxTO5R5sf80p0DiucVtN7ZxvaC4GmQ=
+github.com/jackc/pgtype v1.7.0/go.mod h1:ZnHF+rMePVqDKaOfJVI4Q8IVvAQMryDlDkZnKOI75BE=
+github.com/jackc/pgtype v1.8.0/go.mod h1:PqDKcEBtllAtk/2p6z6SHdXW5UB+MhE75tUol2OKexE=
+github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y=
+github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM=
+github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc=
+github.com/jackc/pgx/v4 v4.5.0/go.mod h1:EpAKPLdnTorwmPUUsqrPxy5fphV18j9q3wrfRXgo+kA=
+github.com/jackc/pgx/v4 v4.6.1-0.20200510190926-94ba730bb1e9/go.mod h1:t3/cdRQl6fOLDxqtlyhe9UWgfIi9R8+8v8GKV5TRA/o=
+github.com/jackc/pgx/v4 v4.6.1-0.20200606145419-4e5062306904/go.mod h1:ZDaNWkt9sW1JMiNn0kdYBaLelIhw7Pg4qd+Vk6tw7Hg=
+github.com/jackc/pgx/v4 v4.11.0/go.mod h1:i62xJgdrtVDsnL3U8ekyrQXEwGNTRoG7/8r+CIdYfcc=
+github.com/jackc/pgx/v4 v4.12.0/go.mod h1:fE547h6VulLPA3kySjfnSG/e2D861g/50JlVUa/ub60=
+github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
+github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
+github.com/jackc/puddle v1.1.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
+github.com/jackc/puddle v1.1.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
+github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
+github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
+github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
+github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
+github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
+github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
+github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
+github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
+github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
+github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
+github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
+github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
+github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
+github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
+github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
+github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
+github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
+github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
+github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
+github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
+github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
+github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
+github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
+github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
+github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
+github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
+github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
+github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
+github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
+github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
+github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
+github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
+github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
+github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
+github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
+github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
+github.com/mattn/go-sqlite3 v1.14.9 h1:10HX2Td0ocZpYEjhilsuo6WWtUqttj2Kb0KtD86/KYA=
+github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
+github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
+github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
+github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
+github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
+github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
+github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
+github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
+github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
+github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
+github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
+github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg=
+github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU=
+github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k=
+github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w=
+github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
+github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
+github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
+github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs=
+github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
+github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
+github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs=
+github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU=
+github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
+github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
+github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis=
+github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74=
+github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
+github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
+github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA=
+github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
+github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
+github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
+github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM=
+github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
+github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
+github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac=
+github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
+github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
+github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
+github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
+github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
+github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
+github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
+github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og=
+github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
+github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
+github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
+github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
+github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA=
+github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
+github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
+github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
+github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
+github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
+github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk=
+github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
+github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
+github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
+github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
+github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
+github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
+github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
+github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
+github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
+github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
+github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
+github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
+github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
+github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
+github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
+github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
+github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
+github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
+github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
+github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
+github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
+github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
+github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
+github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
+github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
+github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE=
+github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
+github.com/syyongx/php2go v0.9.6 h1:NFJlFwV5SKonYE7e/EtAcVxaJAVX9piRLpapw23HOQ8=
+github.com/syyongx/php2go v0.9.6/go.mod h1:meN2eIhhUoxOd2nMxbpe8g6cFPXI5O9/UAAuz7oDdzw=
+github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
+github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
+github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
+github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
+github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
+github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
+go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
+go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
+go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
+go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
+go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
+go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
+go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
+go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
+go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk=
+go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
+go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
+go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
+go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A=
+go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
+go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4=
+go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
+go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
+go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
+go.uber.org/zap v1.13.0 h1:nR6NoDBgAf67s68NhaXbsojM+2gxp3S1hWkHDl27pVU=
+go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
+golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
+golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
+golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
+golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
+golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=
+golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
+golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
+golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
+golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
+golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw=
+golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f h1:Ax0t5p6N38Ga0dThY21weqDEyz2oklo4IvDkpigvkD8=
+golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210902050250-f475640dd07b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac h1:oN6lz7iLW/YC7un8pq+9bOLyXrprv2+DKfkJY+2LJJw=
+golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
+golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
+golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78 h1:M8tBwCtWD/cZV9DZpFYRUgaymAYAr+aIUTWzDaM3uPs=
+golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
+google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
+google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
+google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
+google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
+google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM=
+google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
+google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
+google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
+gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
+gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
+gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
+gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
+gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
+gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
+gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
+gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
+gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
+gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
+gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
+gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
+gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
+honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
+lukechampine.com/uint128 v1.1.1 h1:pnxCASz787iMf+02ssImqk6OLt+Z5QHMoZyUXR4z6JU=
+lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
+modernc.org/cc/v3 v3.33.6/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
+modernc.org/cc/v3 v3.33.9/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
+modernc.org/cc/v3 v3.33.11/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
+modernc.org/cc/v3 v3.34.0/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
+modernc.org/cc/v3 v3.35.0/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
+modernc.org/cc/v3 v3.35.4/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
+modernc.org/cc/v3 v3.35.5/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
+modernc.org/cc/v3 v3.35.7/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
+modernc.org/cc/v3 v3.35.8/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
+modernc.org/cc/v3 v3.35.10/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
+modernc.org/cc/v3 v3.35.15/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
+modernc.org/cc/v3 v3.35.16/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
+modernc.org/cc/v3 v3.35.17/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
+modernc.org/cc/v3 v3.35.18 h1:rMZhRcWrba0y3nVmdiQ7kxAgOOSq2m2f2VzjHLgEs6U=
+modernc.org/cc/v3 v3.35.18/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
+modernc.org/ccgo/v3 v3.9.5/go.mod h1:umuo2EP2oDSBnD3ckjaVUXMrmeAw8C8OSICVa0iFf60=
+modernc.org/ccgo/v3 v3.10.0/go.mod h1:c0yBmkRFi7uW4J7fwx/JiijwOjeAeR2NoSaRVFPmjMw=
+modernc.org/ccgo/v3 v3.11.0/go.mod h1:dGNposbDp9TOZ/1KBxghxtUp/bzErD0/0QW4hhSaBMI=
+modernc.org/ccgo/v3 v3.11.1/go.mod h1:lWHxfsn13L3f7hgGsGlU28D9eUOf6y3ZYHKoPaKU0ag=
+modernc.org/ccgo/v3 v3.11.3/go.mod h1:0oHunRBMBiXOKdaglfMlRPBALQqsfrCKXgw9okQ3GEw=
+modernc.org/ccgo/v3 v3.12.4/go.mod h1:Bk+m6m2tsooJchP/Yk5ji56cClmN6R1cqc9o/YtbgBQ=
+modernc.org/ccgo/v3 v3.12.6/go.mod h1:0Ji3ruvpFPpz+yu+1m0wk68pdr/LENABhTrDkMDWH6c=
+modernc.org/ccgo/v3 v3.12.8/go.mod h1:Hq9keM4ZfjCDuDXxaHptpv9N24JhgBZmUG5q60iLgUo=
+modernc.org/ccgo/v3 v3.12.11/go.mod h1:0jVcmyDwDKDGWbcrzQ+xwJjbhZruHtouiBEvDfoIsdg=
+modernc.org/ccgo/v3 v3.12.14/go.mod h1:GhTu1k0YCpJSuWwtRAEHAol5W7g1/RRfS4/9hc9vF5I=
+modernc.org/ccgo/v3 v3.12.18/go.mod h1:jvg/xVdWWmZACSgOiAhpWpwHWylbJaSzayCqNOJKIhs=
+modernc.org/ccgo/v3 v3.12.20/go.mod h1:aKEdssiu7gVgSy/jjMastnv/q6wWGRbszbheXgWRHc8=
+modernc.org/ccgo/v3 v3.12.21/go.mod h1:ydgg2tEprnyMn159ZO/N4pLBqpL7NOkJ88GT5zNU2dE=
+modernc.org/ccgo/v3 v3.12.22/go.mod h1:nyDVFMmMWhMsgQw+5JH6B6o4MnZ+UQNw1pp52XYFPRk=
+modernc.org/ccgo/v3 v3.12.25/go.mod h1:UaLyWI26TwyIT4+ZFNjkyTbsPsY3plAEB6E7L/vZV3w=
+modernc.org/ccgo/v3 v3.12.29/go.mod h1:FXVjG7YLf9FetsS2OOYcwNhcdOLGt8S9bQ48+OP75cE=
+modernc.org/ccgo/v3 v3.12.36/go.mod h1:uP3/Fiezp/Ga8onfvMLpREq+KUjUmYMxXPO8tETHtA8=
+modernc.org/ccgo/v3 v3.12.38/go.mod h1:93O0G7baRST1vNj4wnZ49b1kLxt0xCW5Hsa2qRaZPqc=
+modernc.org/ccgo/v3 v3.12.43/go.mod h1:k+DqGXd3o7W+inNujK15S5ZYuPoWYLpF5PYougCmthU=
+modernc.org/ccgo/v3 v3.12.46/go.mod h1:UZe6EvMSqOxaJ4sznY7b23/k13R8XNlyWsO5bAmSgOE=
+modernc.org/ccgo/v3 v3.12.47/go.mod h1:m8d6p0zNps187fhBwzY/ii6gxfjob1VxWb919Nk1HUk=
+modernc.org/ccgo/v3 v3.12.50/go.mod h1:bu9YIwtg+HXQxBhsRDE+cJjQRuINuT9PUK4orOco/JI=
+modernc.org/ccgo/v3 v3.12.51/go.mod h1:gaIIlx4YpmGO2bLye04/yeblmvWEmE4BBBls4aJXFiE=
+modernc.org/ccgo/v3 v3.12.53/go.mod h1:8xWGGTFkdFEWBEsUmi+DBjwu/WLy3SSOrqEmKUjMeEg=
+modernc.org/ccgo/v3 v3.12.54/go.mod h1:yANKFTm9llTFVX1FqNKHE0aMcQb1fuPJx6p8AcUx+74=
+modernc.org/ccgo/v3 v3.12.55/go.mod h1:rsXiIyJi9psOwiBkplOaHye5L4MOOaCjHg1Fxkj7IeU=
+modernc.org/ccgo/v3 v3.12.56/go.mod h1:ljeFks3faDseCkr60JMpeDb2GSO3TKAmrzm7q9YOcMU=
+modernc.org/ccgo/v3 v3.12.57/go.mod h1:hNSF4DNVgBl8wYHpMvPqQWDQx8luqxDnNGCMM4NFNMc=
+modernc.org/ccgo/v3 v3.12.60/go.mod h1:k/Nn0zdO1xHVWjPYVshDeWKqbRWIfif5dtsIOCUVMqM=
+modernc.org/ccgo/v3 v3.12.65/go.mod h1:D6hQtKxPNZiY6wDBtehSGKFKmyXn53F8nGTpH+POmS4=
+modernc.org/ccgo/v3 v3.12.66/go.mod h1:jUuxlCFZTUZLMV08s7B1ekHX5+LIAurKTTaugUr/EhQ=
+modernc.org/ccgo/v3 v3.12.67/go.mod h1:Bll3KwKvGROizP2Xj17GEGOTrlvB1XcVaBrC90ORO84=
+modernc.org/ccgo/v3 v3.12.73/go.mod h1:hngkB+nUUqzOf3iqsM48Gf1FZhY599qzVg1iX+BT3cQ=
+modernc.org/ccgo/v3 v3.12.81/go.mod h1:p2A1duHoBBg1mFtYvnhAnQyI6vL0uw5PGYLSIgF6rYY=
+modernc.org/ccgo/v3 v3.12.82 h1:wudcnJyjLj1aQQCXF3IM9Gz2X6UNjw+afIghzdtn0v8=
+modernc.org/ccgo/v3 v3.12.82/go.mod h1:ApbflUfa5BKadjHynCficldU1ghjen84tuM5jRynB7w=
+modernc.org/ccorpus v1.11.1 h1:K0qPfpVG1MJh5BYazccnmhywH4zHuOgJXgbjzyp6dWA=
+modernc.org/ccorpus v1.11.1/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ=
+modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM=
+modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM=
+modernc.org/libc v1.9.8/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w=
+modernc.org/libc v1.9.11/go.mod h1:NyF3tsA5ArIjJ83XB0JlqhjTabTCHm9aX4XMPHyQn0Q=
+modernc.org/libc v1.11.0/go.mod h1:2lOfPmj7cz+g1MrPNmX65QCzVxgNq2C5o0jdLY2gAYg=
+modernc.org/libc v1.11.2/go.mod h1:ioIyrl3ETkugDO3SGZ+6EOKvlP3zSOycUETe4XM4n8M=
+modernc.org/libc v1.11.5/go.mod h1:k3HDCP95A6U111Q5TmG3nAyUcp3kR5YFZTeDS9v8vSU=
+modernc.org/libc v1.11.6/go.mod h1:ddqmzR6p5i4jIGK1d/EiSw97LBcE3dK24QEwCFvgNgE=
+modernc.org/libc v1.11.11/go.mod h1:lXEp9QOOk4qAYOtL3BmMve99S5Owz7Qyowzvg6LiZso=
+modernc.org/libc v1.11.13/go.mod h1:ZYawJWlXIzXy2Pzghaf7YfM8OKacP3eZQI81PDLFdY8=
+modernc.org/libc v1.11.16/go.mod h1:+DJquzYi+DMRUtWI1YNxrlQO6TcA5+dRRiq8HWBWRC8=
+modernc.org/libc v1.11.19/go.mod h1:e0dgEame6mkydy19KKaVPBeEnyJB4LGNb0bBH1EtQ3I=
+modernc.org/libc v1.11.24/go.mod h1:FOSzE0UwookyT1TtCJrRkvsOrX2k38HoInhw+cSCUGk=
+modernc.org/libc v1.11.26/go.mod h1:SFjnYi9OSd2W7f4ct622o/PAYqk7KHv6GS8NZULIjKY=
+modernc.org/libc v1.11.27/go.mod h1:zmWm6kcFXt/jpzeCgfvUNswM0qke8qVwxqZrnddlDiE=
+modernc.org/libc v1.11.28/go.mod h1:Ii4V0fTFcbq3qrv3CNn+OGHAvzqMBvC7dBNyC4vHZlg=
+modernc.org/libc v1.11.31/go.mod h1:FpBncUkEAtopRNJj8aRo29qUiyx5AvAlAxzlx9GNaVM=
+modernc.org/libc v1.11.34/go.mod h1:+Tzc4hnb1iaX/SKAutJmfzES6awxfU1BPvrrJO0pYLg=
+modernc.org/libc v1.11.37/go.mod h1:dCQebOwoO1046yTrfUE5nX1f3YpGZQKNcITUYWlrAWo=
+modernc.org/libc v1.11.39/go.mod h1:mV8lJMo2S5A31uD0k1cMu7vrJbSA3J3waQJxpV4iqx8=
+modernc.org/libc v1.11.42/go.mod h1:yzrLDU+sSjLE+D4bIhS7q1L5UwXDOw99PLSX0BlZvSQ=
+modernc.org/libc v1.11.44/go.mod h1:KFq33jsma7F5WXiYelU8quMJasCCTnHK0mkri4yPHgA=
+modernc.org/libc v1.11.45/go.mod h1:Y192orvfVQQYFzCNsn+Xt0Hxt4DiO4USpLNXBlXg/tM=
+modernc.org/libc v1.11.47/go.mod h1:tPkE4PzCTW27E6AIKIR5IwHAQKCAtudEIeAV1/SiyBg=
+modernc.org/libc v1.11.49/go.mod h1:9JrJuK5WTtoTWIFQ7QjX2Mb/bagYdZdscI3xrvHbXjE=
+modernc.org/libc v1.11.51/go.mod h1:R9I8u9TS+meaWLdbfQhq2kFknTW0O3aw3kEMqDDxMaM=
+modernc.org/libc v1.11.53/go.mod h1:5ip5vWYPAoMulkQ5XlSJTy12Sz5U6blOQiYasilVPsU=
+modernc.org/libc v1.11.54/go.mod h1:S/FVnskbzVUrjfBqlGFIPA5m7UwB3n9fojHhCNfSsnw=
+modernc.org/libc v1.11.55/go.mod h1:j2A5YBRm6HjNkoSs/fzZrSxCuwWqcMYTDPLNx0URn3M=
+modernc.org/libc v1.11.56/go.mod h1:pakHkg5JdMLt2OgRadpPOTnyRXm/uzu+Yyg/LSLdi18=
+modernc.org/libc v1.11.58/go.mod h1:ns94Rxv0OWyoQrDqMFfWwka2BcaF6/61CqJRK9LP7S8=
+modernc.org/libc v1.11.70/go.mod h1:DUOmMYe+IvKi9n6Mycyx3DbjfzSKrdr/0Vgt3j7P5gw=
+modernc.org/libc v1.11.71/go.mod h1:DUOmMYe+IvKi9n6Mycyx3DbjfzSKrdr/0Vgt3j7P5gw=
+modernc.org/libc v1.11.75/go.mod h1:dGRVugT6edz361wmD9gk6ax1AbDSe0x5vji0dGJiPT0=
+modernc.org/libc v1.11.82/go.mod h1:NF+Ek1BOl2jeC7lw3a7Jj5PWyHPwWD4aq3wVKxqV1fI=
+modernc.org/libc v1.11.86/go.mod h1:ePuYgoQLmvxdNT06RpGnaDKJmDNEkV7ZPKI2jnsvZoE=
+modernc.org/libc v1.11.87 h1:PzIzOqtlzMDDcCzJ5cUP6h/Ku6Fa9iyflP2ccTY64aE=
+modernc.org/libc v1.11.87/go.mod h1:Qvd5iXTeLhI5PS0XSyqMY99282y+3euapQFxM7jYnpY=
+modernc.org/mathutil v1.1.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
+modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
+modernc.org/mathutil v1.4.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
+modernc.org/mathutil v1.4.1 h1:ij3fYGe8zBF4Vu+g0oT7mB06r8sqGWKuJu1yXeR4by8=
+modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
+modernc.org/memory v1.0.4/go.mod h1:nV2OApxradM3/OVbs2/0OsP6nPfakXpi50C7dcoHXlc=
+modernc.org/memory v1.0.5 h1:XRch8trV7GgvTec2i7jc33YlUI0RKVDBvZ5eZ5m8y14=
+modernc.org/memory v1.0.5/go.mod h1:B7OYswTRnfGg+4tDH1t1OeUNnsy2viGTdME4tzd+IjM=
+modernc.org/opt v0.1.1 h1:/0RX92k9vwVeDXj+Xn23DKp2VJubL7k8qNffND6qn3A=
+modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
+modernc.org/sqlite v1.14.2 h1:ohsW2+e+Qe2To1W6GNezzKGwjXwSax6R+CrhRxVaFbE=
+modernc.org/sqlite v1.14.2/go.mod h1:yqfn85u8wVOE6ub5UT8VI9JjhrwBUUCNyTACN0h6Sx8=
+modernc.org/strutil v1.1.1 h1:xv+J1BXY3Opl2ALrBwyfEikFAj8pmqcpnfmuwUwcozs=
+modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw=
+modernc.org/tcl v1.8.13 h1:V0sTNBw0Re86PvXZxuCub3oO9WrSTqALgrwNZNvLFGw=
+modernc.org/tcl v1.8.13/go.mod h1:V+q/Ef0IJaNUSECieLU4o+8IScapxnMyFV6i/7uQlAY=
+modernc.org/token v1.0.0 h1:a0jaWiNMDhDUtqOj09wvjWWAqd3q7WpBulmL9H2egsk=
+modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
+modernc.org/z v1.2.19 h1:BGyRFWhDVn5LFS5OcX4Yd/MlpRTOc7hOPTdcIpCiUao=
+modernc.org/z v1.2.19/go.mod h1:+ZpP0pc4zz97eukOzW3xagV/lS82IpPN9NGG5pNF9vY=
+sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
+sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=
+xorm.io/builder v0.3.11-0.20220531020008-1bd24a7dc978 h1:bvLlAPW1ZMTWA32LuZMBEGHAUOcATZjzHcotf3SWweM=
+xorm.io/builder v0.3.11-0.20220531020008-1bd24a7dc978/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE=
+xorm.io/xorm v1.3.1 h1:z5egKrDoOLqZFhMjcGF4FBHiTmE5/feQoHclfhNidfM=
+xorm.io/xorm v1.3.1/go.mod h1:9NbjqdnjX6eyjRRhh01GHm64r6N9shTb/8Ak3YRt8Nw=
diff --git a/lib/comm_plan/all.go b/lib/comm_plan/all.go
new file mode 100644
index 0000000..88732b0
--- /dev/null
+++ b/lib/comm_plan/all.go
@@ -0,0 +1,683 @@
+package comm_plan
+
+import (
+ zhios_order_relate_utils "code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/utils"
+ zhios_order_relate_logx "code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/utils/logx"
+ "fmt"
+)
+
+//佣金 积分 区块币计算
+func CalReturnAmountAndRatio(level, ownbuyReturnType, peerNum int, userType string, fee, integralFee float64, opt *PlanOpt) (commission, commissionRatio float64, amountList, ratioList []*VirtualCoinCommission) {
+ // 佣金的比例兼容旧系统 比例独立出来的 所以这样算
+ commissionRatio = getCommissionRatio(userType, level, peerNum, opt.UserRate)
+ commission = fee * commissionRatio
+
+ // 新版支持多种虚拟币 支持的种类id保存在ReturnType id=0代表现金佣金 其他为虚拟币
+ if opt.UserRate[level].ReturnType != nil { //返佣类型
+
+ for _, coinId := range opt.UserRate[level].ReturnType {
+ newFee := integralFee
+ if coinId == "0" {
+ newFee = fee
+ }
+ ratio := getVirtualCoinRatio(userType, level, peerNum, opt.UserRate, coinId)
+ amount := getCoinAmount(ratio, zhios_order_relate_utils.StrToInt(coinId), newFee, opt.VirtualCoinMoneyRatioList)
+ amountList = append(amountList, &VirtualCoinCommission{
+ Cid: coinId,
+ Val: amount,
+ })
+ ratioList = append(ratioList, &VirtualCoinCommission{
+ Cid: coinId,
+ Val: zhios_order_relate_utils.AnyToFloat64(ratio),
+ })
+ }
+ }
+
+ if ownbuyReturnType == 1 { //自购不返利
+ commission = 0
+ for i := range amountList {
+ amountList[i].Val = 0
+ }
+ }
+ commission = zhios_order_relate_utils.FloatFormat(commission, 6)
+
+ for i, coin := range amountList {
+ amountList[i].Val = zhios_order_relate_utils.FloatFormat(coin.Val, 6)
+ }
+
+ return commission, commissionRatio, amountList, ratioList
+}
+
+//佣金 积分 区块币计算
+func CalReturnAmountAndRatioToWinery(level int, fee, integralFee float64, opt *PlanOpt) (commission, commissionRatio float64, amountList, ratioList []*VirtualCoinCommission) {
+ if opt.UserRate[level].ReturnType != nil { //返佣类型
+ for _, coinId := range opt.UserRate[level].ReturnType {
+ newFee := integralFee
+ if coinId == "0" {
+ newFee = fee
+ }
+ ratio := zhios_order_relate_utils.Float64ToStrByPrec(zhios_order_relate_utils.StrToFloat64(opt.UserRate[level].BuyDeliverList[coinId])/100, 4)
+ amount := getCoinAmount(ratio, zhios_order_relate_utils.StrToInt(coinId), newFee, opt.VirtualCoinMoneyRatioList)
+ if coinId == "0" {
+ commission = amount
+ commissionRatio = zhios_order_relate_utils.StrToFloat64(ratio)
+ }
+ amountList = append(amountList, &VirtualCoinCommission{
+ Cid: coinId,
+ Val: amount,
+ })
+ ratioList = append(ratioList, &VirtualCoinCommission{
+ Cid: coinId,
+ Val: zhios_order_relate_utils.AnyToFloat64(ratio),
+ })
+ }
+ }
+
+ return commission, commissionRatio, amountList, ratioList
+}
+
+// 按总佣金的比例进行划分计算
+func CalcAll(opt *PlanOpt, totalAmt, integralTotalAmt float64, userList *LvUser, pvd string, sysFee float64, integralSysFee float64) error {
+ grade := opt.UserRate
+ if len(grade) == 0 {
+ return zhios_order_relate_logx.Warn("level grade is not set")
+ }
+
+ //查出用户自购佣金
+ commission, commissionRatio, amountList, ratioList := CalReturnAmountAndRatio(userList.Lv, userList.OwnbuyReturnType, 0, "own", totalAmt, integralTotalAmt, opt)
+ userList.Profit = commission // 另外出来的佣金 兼容旧的
+ userList.ProfitList = amountList // 各币种分佣
+ userList.SubsidyFee = 0
+ ratioListMap := convertList2Map(ratioList)
+
+ for k, v := range userList.ProfitList {
+ userList.ProfitList[k].Val = ratioListMap[v.Cid] * v.Val
+ }
+ // 各种币换算出总的额度
+ totalAmtList := make([]*VirtualCoinCommission, 0)
+ for coinId, rate := range opt.VirtualCoinMoneyRatioList {
+ var amount float64
+ if coinId == 0 {
+ amount = totalAmt
+ } else {
+ amount = integralTotalAmt * zhios_order_relate_utils.AnyToFloat64(rate)
+ }
+ totalAmtList = append(totalAmtList, &VirtualCoinCommission{
+ Cid: zhios_order_relate_utils.AnyToString(coinId),
+ Val: amount,
+ })
+ }
+
+ var (
+ node = userList
+ maxLv = node.Lv // 当前等级
+ maxLevelWeight = node.LevelWeight // 当前权重
+ peerNum = 0 // 存在同级数
+ peerRate float64 = 0 // 同级累计比例
+ peerRateList = make([]*VirtualCoinCommission, 0) // 各虚拟币同级累计
+ restAmtList = make([]*VirtualCoinCommission, 0) // 各虚拟币剩余额度
+ accumulateRatioList = make([]*VirtualCoinCommission, 0) // 各虚拟币累计比例
+ restAmt = totalAmt - userList.Profit // 剩余比例
+ totalCommissionRatio = commissionRatio // 累计佣金比例
+ )
+ // 计算剩余额度
+ restAmtList, _ = CalVirtualCommissionMinus(totalAmtList, amountList)
+ // 累计比例
+ accumulateRatioList = ratioList
+ restAmt = zhios_order_relate_utils.FloatFormat(restAmt, 6)
+
+Loop:
+ for node.ParentUser != nil { //查找上级用户
+ node.ParentUser.Profit = 0
+
+ //佣金补贴奖励
+ subsidyFee, subsidyRatio, isOnlySubsidyFee, subsidyFeeList, subsidyRatioList := subsidyFeeDo(opt, totalAmt, integralTotalAmt, node.ParentUser, userList.NewLv, pvd, sysFee, integralSysFee)
+ node.ParentUser.SubsidyFee = subsidyFee
+ node.ParentUser.SubsidyFeeList = subsidyFeeList // 各币种补贴
+
+ // 如果父级比当前级别低, 跳过
+ // 同级奖, 如果父级别与当前级别一致,并且设置了对应比例
+ count := len(grade[maxLv].PeerRate)
+ if grade[maxLv].PeerRateList != nil {
+ count = len(grade[maxLv].PeerRateList)
+ }
+ var isBreak bool
+ zeroList := make(map[string]struct{})
+ // 同级奖
+ if node.ParentUser.LevelWeight == maxLevelWeight && count > peerNum {
+ //同级奖励比例
+ commission, commissionRatio, amountList, ratioList := CalReturnAmountAndRatio(maxLv, userList.OwnbuyReturnType, peerNum, "same_lv", totalAmt, integralTotalAmt, opt)
+ //佣金 (lv, isOnlySubsidy int, restAmt, profit, peerRate, totalRatio, restRatio, subsidyFee, subsidyBili float64, opt *PlanOpt)
+ node.ParentUser.Profit, restAmt, totalCommissionRatio, peerRate, node.ParentUser.SubsidyFee, isBreak = sameMoney(node.Lv, isOnlySubsidyFee, restAmt, commission, peerRate, totalCommissionRatio, commissionRatio, node.ParentUser.SubsidyFee, subsidyRatio, opt)
+ node.ParentUser.ProfitList, restAmtList, accumulateRatioList, peerRateList, node.ParentUser.SubsidyFeeList, zeroList = sameMoneyV2(node.Lv, isOnlySubsidyFee, totalAmtList, restAmtList, amountList, peerRateList, accumulateRatioList, ratioList, node.ParentUser.SubsidyFeeList, subsidyRatioList, opt)
+
+ // 全部都没得分了
+ if isBreak && len(zeroList) == len(opt.UserRate[maxLv].ReturnType) {
+ break Loop
+ }
+ peerNum++
+ } else if node.ParentUser.LevelWeight > maxLevelWeight {
+ if _, ok := grade[node.Lv]; !ok {
+ return zhios_order_relate_logx.Warn("level grade node.Lv is not set")
+ }
+ if _, ok := grade[node.ParentUser.Lv]; !ok {
+ return zhios_order_relate_logx.Warn("level grade node.ParentUser.Lv is not set")
+ }
+ commission, _, amountList, teamRatioList := CalReturnAmountAndRatio(node.ParentUser.Lv, userList.OwnbuyReturnType, peerNum, "team", totalAmt, integralTotalAmt, opt)
+ //佣金
+ node.ParentUser.Profit = commission
+ node.ParentUser.Profit, restAmt, totalCommissionRatio, node.ParentUser.SubsidyFee, isBreak = teamDiffMoney(node.ParentUser.Profit, grade[node.Lv].PayMode, isOnlySubsidyFee, totalAmt, restAmt, grade[node.ParentUser.Lv].TeamRate, totalCommissionRatio, peerRate, node.ParentUser.SubsidyFee, subsidyRatio)
+ //积分
+ node.ParentUser.ProfitList = amountList
+ // profitList []*VirtualCoinCommission, payMode, isOnlySubsidy int, totalAmtList, restAmtList, teamRatioList, totalRatioList, peerRateList, subsidyFeeList, subsidyRatioList []*VirtualCoinCommission
+ node.ParentUser.ProfitList, restAmtList, accumulateRatioList, node.ParentUser.SubsidyFeeList, zeroList = teamDiffMoneyV2(node.ParentUser.ProfitList, grade[node.Lv].PayMode, isOnlySubsidyFee, totalAmtList, restAmtList, teamRatioList, accumulateRatioList, peerRateList, subsidyFeeList, subsidyRatioList)
+
+ // 没得分了 就结束
+ if isBreak && len(zeroList) == len(opt.UserRate[maxLv].ReturnType) {
+ break Loop
+ }
+ // 等级往上升则置0
+ maxLevelWeight, maxLv, peerRate, peerRateList, peerNum = node.ParentUser.LevelWeight, node.ParentUser.Lv, 0, nil, 0
+ }
+ //如果是酒庄制度,要看这里处理 额外的补贴
+ subsidyFee, subsidyRatio, isOnlySubsidyFee, subsidyFeeList, subsidyRatioList = subsidyFeeDo(opt, totalAmt, integralTotalAmt, node.ParentUser, userList.NewLv, pvd, sysFee, integralSysFee)
+ node.ParentUser.SubsidyFee = subsidyFee
+ node.ParentUser.SubsidyFeeList = subsidyFeeList // 各币种补贴
+ node.Profit = zhios_order_relate_utils.StrToFloat64(fmt.Sprintf("%.4f", node.Profit))
+ node = node.ParentUser
+ }
+
+ return nil
+}
+
+//公共处理同级计算 (只计算佣金 旧版本用)
+func sameMoney(lv, isOnlySubsidy int, restAmt, profit, peerRate, totalRatio, restRatio, subsidyFee, subsidyBili float64, opt *PlanOpt) (float64, float64, float64, float64, float64, bool) {
+ //如果不够扣了,并且是比例返利就跳过
+ if restAmt < profit {
+ return 0, restAmt, totalRatio, peerRate, subsidyFee, true
+ }
+
+ //极差返利
+ if opt.UserRate[lv].PayMode == 0 && isOnlySubsidy == 0 {
+ restAmt -= profit // 剩余可分
+ restAmt = zhios_order_relate_utils.FloatFormat(restAmt, 6)
+ peerRate += restRatio
+ totalRatio += restRatio
+ } else if isOnlySubsidy == 1 { //如果只返补贴 当成是极差的一部分 所以要扣 不是额外的
+ profit = 0
+ if opt.UserRate[lv].PayMode == 0 {
+ if restAmt < subsidyFee {
+ subsidyFee = 0
+ return profit, restAmt, totalRatio, subsidyFee, peerRate, true
+ }
+ restAmt -= subsidyFee // 剩余可分
+ restAmt = zhios_order_relate_utils.FloatFormat(restAmt, 6)
+ totalRatio += zhios_order_relate_utils.FloatFormat(subsidyBili, 6)
+ }
+ }
+ return profit, restAmt, totalRatio, peerRate, subsidyFee, false
+}
+
+func sameMoneyV2(lv, isOnlySubsidy int, totalAmtList, restAmtList, profitList, peerRateList, totalRatioList, restRatioList, subsidyFeeList, subsidyRatioList []*VirtualCoinCommission, opt *PlanOpt) ([]*VirtualCoinCommission, []*VirtualCoinCommission, []*VirtualCoinCommission, []*VirtualCoinCommission, []*VirtualCoinCommission, map[string]struct{}) {
+
+ restAmtMap := convertList2Map(restAmtList)
+ totalAmtMap := convertList2Map(totalAmtList)
+ //profitMap := convertList2Map(profitList)
+ restRatioMap := convertList2Map(restRatioList)
+ totalRatioMap := convertList2Map(totalRatioList)
+ peerRateMap := convertList2Map(peerRateList)
+ subsidyMap := convertList2Map(subsidyFeeList)
+ subsidyRatioMap := convertList2Map(subsidyRatioList)
+
+ zeroList := make(map[string]struct{})
+
+ newProfitList := make([]*VirtualCoinCommission, 0)
+ newRestAmtList := make([]*VirtualCoinCommission, 0)
+ newTotalRatioList := make([]*VirtualCoinCommission, 0)
+ newPeerRateList := make([]*VirtualCoinCommission, 0)
+ newSubsidyFeeList := subsidyFeeList
+
+ //极差返利
+ if opt.UserRate[lv].PayMode == 0 && isOnlySubsidy == 0 {
+ for _, coin := range profitList {
+ profitOne := &VirtualCoinCommission{}
+ profitOne.Cid = coin.Cid
+ profitOne.Val = totalAmtMap[coin.Cid] * restRatioMap[coin.Cid]
+ // 不够扣了 设为0
+ if restAmtMap[coin.Cid] < profitOne.Val {
+ profitOne.Val = 0
+ zeroList[coin.Cid] = struct{}{}
+ }
+ // 分佣
+ newProfitList = append(newProfitList, profitOne)
+
+ // 剩余
+ restAmtMap[coin.Cid] -= profitOne.Val
+
+ // 累计比例
+ totalRatioMap[coin.Cid] += restRatioMap[coin.Cid]
+
+ // 同级累计比例
+ if _, ok := peerRateMap[coin.Cid]; !ok {
+ peerRateMap[coin.Cid] = 0
+ }
+ peerRateMap[coin.Cid] += restRatioMap[coin.Cid]
+ }
+ } else if isOnlySubsidy == 1 {
+ newSubsidyFeeList = make([]*VirtualCoinCommission, 0)
+ for _, coin := range profitList {
+ profitOne := &VirtualCoinCommission{}
+ subsidyFeeOne := &VirtualCoinCommission{}
+
+ profitOne.Cid = coin.Cid
+ profitOne.Val = 0
+ newProfitList = append(newProfitList, profitOne)
+
+ if opt.UserRate[lv].PayMode == 0 {
+ subsidyFeeOne.Cid = coin.Cid
+ subsidyFeeOne.Val = subsidyMap[coin.Cid]
+ if restAmtMap[coin.Cid] < subsidyMap[coin.Cid] {
+ subsidyFeeOne.Val = 0
+ zeroList[coin.Cid] = struct{}{}
+ }
+ newSubsidyFeeList = append(newSubsidyFeeList, subsidyFeeOne)
+ }
+
+ // 剩余额度
+ restAmtMap[coin.Cid] -= subsidyFeeOne.Val
+
+ // 累计比例
+ totalRatioMap[coin.Cid] += subsidyRatioMap[coin.Cid]
+
+ // 同级累计比例
+ if _, ok := peerRateMap[coin.Cid]; !ok {
+ peerRateMap[coin.Cid] = 0
+ }
+ peerRateMap[coin.Cid] += restRatioMap[coin.Cid]
+ }
+ }
+
+ newTotalRatioList = convertMap2List(totalRatioMap)
+ newPeerRateList = convertMap2List(peerRateMap)
+ newRestAmtList = convertMap2List(restAmtMap)
+
+ return newProfitList, newRestAmtList, newTotalRatioList, newPeerRateList, newSubsidyFeeList, zeroList
+}
+
+//公共处理下团队-上一层 (用于旧版的制度 只有佣金时)
+func teamDiffMoney(profit float64, payMode, isOnlySubsidy int, totalAmt, restAmt, teamRatio, totalRatio, peerRate, subsidyFee, subsidyRatio float64) (float64, float64, float64, float64, bool) {
+ // 如果是团队内部支出团队比例大于同级累计比例 或站长支出
+ if payMode == 1 || teamRatio > peerRate {
+ teamRatio = zhios_order_relate_utils.FloatFormat(teamRatio-totalRatio, 6)
+ }
+ //极差返利
+ if isOnlySubsidy == 0 {
+ totalRatio += teamRatio
+ //出现负数跳过
+ if teamRatio <= 0 {
+ profit = 0
+ return profit, restAmt, totalRatio, subsidyFee, true
+ }
+ profit = zhios_order_relate_utils.FloatFormat(teamRatio*totalAmt, 6)
+ if restAmt < profit {
+ profit = 0
+ return profit, restAmt, totalRatio, subsidyFee, true
+ }
+ restAmt -= profit // 剩余可分
+
+ } else if isOnlySubsidy == 1 { //如果只返补贴 当成是极差的一部分 所以要扣 不是额外的
+ totalRatio += zhios_order_relate_utils.FloatFormat(subsidyRatio, 6)
+ profit = 0
+ if restAmt < subsidyFee {
+ profit = 0
+ subsidyFee = 0
+ return profit, restAmt, totalRatio, subsidyFee, true
+ }
+ restAmt -= subsidyFee // 剩余可分
+ }
+ restAmt = zhios_order_relate_utils.FloatFormat(restAmt, 6)
+ return profit, restAmt, totalRatio, subsidyFee, false
+}
+
+// 处理多虚拟币团队的
+func teamDiffMoneyV2(profitList []*VirtualCoinCommission, payMode, isOnlySubsidy int, totalAmtList, restAmtList, teamRatioList, totalRatioList, peerRateList, subsidyFeeList, subsidyRatioList []*VirtualCoinCommission) (newProfitList, newRestAmtList, newTotalRatioList, newSubsidyFeeList []*VirtualCoinCommission, zeroList map[string]struct{}) {
+ restAmtMap := convertList2Map(restAmtList)
+ totalAmtMap := convertList2Map(totalAmtList)
+ profitMap := convertList2Map(profitList)
+ totalRatioMap := convertList2Map(totalRatioList)
+ peerRateMap := convertList2Map(peerRateList)
+ subsidyFeeMap := convertList2Map(subsidyFeeList)
+ subsidyRatioMap := convertList2Map(subsidyRatioList)
+ teamRatioMap := convertList2Map(teamRatioList)
+
+ zeroList = make(map[string]struct{})
+ newProfitList = make([]*VirtualCoinCommission, 0)
+
+ for _, coin := range profitList {
+ // 如果是团队内部支出团队比例大于同级累计比例 或站长支出
+ if payMode == 1 || teamRatioMap[coin.Cid] > peerRateMap[coin.Cid] {
+ teamRatioMap[coin.Cid] = zhios_order_relate_utils.FloatFormat(teamRatioMap[coin.Cid]-totalRatioMap[coin.Cid], 6)
+ }
+
+ if isOnlySubsidy == 0 {
+ totalRatioMap[coin.Cid] += teamRatioMap[coin.Cid]
+
+ profitOne := &VirtualCoinCommission{}
+ profitOne.Cid = coin.Cid
+ profitOne.Val = zhios_order_relate_utils.FloatFormat(totalAmtMap[coin.Cid]*teamRatioMap[coin.Cid], 6)
+ // 剩余不足或比例小于0
+ if teamRatioMap[coin.Cid] < 0 || restAmtMap[coin.Cid] < profitOne.Val {
+ zeroList[coin.Cid] = struct{}{}
+ profitOne.Val = 0
+ }
+ newProfitList = append(newProfitList, profitOne)
+
+ restAmtMap[coin.Cid] -= profitOne.Val
+ } else if isOnlySubsidy == 1 { //如果只返补贴 当成是极差的一部分 所以要扣 不是额外的
+ totalRatioMap[coin.Cid] += zhios_order_relate_utils.FloatFormat(subsidyRatioMap[coin.Cid], 6)
+ profitMap[coin.Cid] = 0
+ if restAmtMap[coin.Cid] < subsidyFeeMap[coin.Cid] {
+ subsidyFeeMap[coin.Cid] = 0
+ }
+ restAmtMap[coin.Cid] -= subsidyFeeMap[coin.Cid]
+ }
+ }
+ newTotalRatioList = convertMap2List(totalRatioMap)
+ newRestAmtList = convertMap2List(restAmtMap)
+ newSubsidyFeeList = convertMap2List(subsidyFeeMap)
+
+ return newProfitList, newRestAmtList, newTotalRatioList, newSubsidyFeeList, zeroList
+}
+
+//补贴金额计算
+// types 支持的返佣类型
+func subsidyFeeDo(opt *PlanOpt, totalAmt, integralTotalAmt float64, lvuser *LvUser, newLv int, pvd string, sysFee, integralSysFee float64) (subsidyFee, subsidyRatio float64, isOnlySubsidyFee int, subsidyFeeList, subsidyRatioList []*VirtualCoinCommission) {
+ grade := opt.UserRate
+ lv := lvuser.Lv
+ if grade[lv].UserSubsidyType == "winery" { //酒庄模式 换一下计算基数
+ commission := lvuser.Profit
+ amountList := lvuser.ProfitList
+ var baseMoney = commission
+ if zhios_order_relate_utils.StrToInt(grade[lv].UserSubsidyBaseCoinId) > 0 {
+ for _, v := range amountList {
+ if v.Cid == grade[lv].UserSubsidyBaseCoinId {
+ baseMoney = v.Val
+ }
+ }
+ }
+ totalAmt = baseMoney
+ integralTotalAmt = baseMoney
+ }
+ subsidyFee, subsidyRatio, isOnlySubsidyFee, subsidyFeeList, subsidyRatioList = commSubsidy(opt, totalAmt, integralTotalAmt, lvuser, newLv, pvd, sysFee, integralSysFee, grade)
+ return subsidyFee, subsidyRatio, isOnlySubsidyFee, subsidyFeeList, subsidyRatioList
+}
+func commSubsidy(opt *PlanOpt, totalAmt, integralTotalAmt float64, lvuser *LvUser, newLv int, pvd string, sysFee, integralSysFee float64, grade map[int]*LvGrade) (subsidyFee, subsidyRatio float64, isOnlySubsidyFee int, subsidyFeeList, subsidyRatioList []*VirtualCoinCommission) {
+ lv := lvuser.Lv
+ //会员费分佣只有直推的奖励
+ pvdBool := zhios_order_relate_utils.InArr(pvd, []string{"user_level_up", "mall_goods_user_lv"})
+ if opt.IsCanRunSubsidy == 1 { //后台推演用
+ if zhios_order_relate_utils.InArr(grade[lv].UserSubsidyType, []string{"buy_goods", "winery"}) {
+ pvdBool = false
+ } else {
+ pvdBool = true
+ }
+ }
+ if pvdBool && lvuser.Diff != 1 {
+ return 0, 0, 0, nil, nil
+ }
+ if _, ok := grade[lv]; !ok {
+ return 0, 0, 0, nil, nil
+ }
+ if grade[lv].UserSubsidyType == "" {
+ grade[lv].UserSubsidyType = "up_lv"
+ }
+ // 各等级 各虚拟币补贴设置
+ userLvUpSubsidyList := grade[lv].UserLvUpSubsidyList
+ if userLvUpSubsidyList == nil {
+ return 0, 0, 0, nil, nil
+ }
+ //只有额外补贴跟 分销补贴按钮都开启才有的分
+ if grade[lv].SubsidyEnable == 1 && grade[lv].UserLvUpSubsidyEnable == 1 {
+ //判断有没有开启 如果不是推荐会员模式
+ if pvdBool && grade[lv].UserSubsidyType != "up_lv" { // 分享会员补贴
+ return 0, 0, 0, nil, nil
+ }
+ //如果不是购买商品模式 跳过
+ if pvdBool == false && zhios_order_relate_utils.InArr(grade[lv].UserSubsidyType, []string{"buy_goods", "winery"}) { // 购买商品补贴
+ return 0, 0, 0, nil, nil
+ }
+ //处理每个条件的返利
+ if grade[lv].UserLvUpSubsidyList != nil {
+ fmt.Println(grade[lv].UserLvUpSubsidyList)
+ for k, v1 := range grade[lv].UserLvUpSubsidyList {
+ v, ok := v1.(map[string]interface{})
+
+ if ok {
+ //如果不相等并且是会员升级的没的返
+ if newLv != int(zhios_order_relate_utils.AnyToInt64(v["lv"])) && pvdBool {
+ continue
+ }
+ //如果层级不是当前层级 且不是会员升级
+ if pvdBool == false && k+1 != lvuser.Diff {
+ continue
+ }
+ if pvdBool == false { //如果不是会员升级 newLv=旧的等级
+ newLv = lv
+ }
+ //如果没开启与补贴共存 只能拿这个奖励
+ if int(zhios_order_relate_utils.AnyToInt64(v["is_use"])) != 1 {
+ isOnlySubsidyFee = 1
+ }
+
+ modeList := grade[lv].SubsidyModeList // 每一种币按比例还是固定金额
+ if modeList == nil {
+ return 0, 0, 0, nil, nil
+ }
+ subsidyReturnType := grade[lv].SubsidyReturnType // 补贴支持的虚拟币
+ if subsidyReturnType == nil {
+ return 0, 0, 0, nil, nil
+ }
+
+ for _, coinId := range subsidyReturnType {
+ ratio := GetLvUpSubsidyVirtualCoinRatio(lv, newLv, lvuser.Diff, grade, coinId)
+ mode := modeList[coinId]
+ if mode != "bili" && mode != "money" {
+ continue
+ }
+ modeStr := mode.(string)
+ subsidyMode := grade[lv].SubsidyMode
+ newAmt := integralTotalAmt
+ newSysFee := integralSysFee
+ var moneyRate = ""
+ if coinId == "0" {
+ newAmt = totalAmt
+ newSysFee = sysFee
+ } else if mode == "bili" {
+ rateList := opt.VirtualCoinMoneyRatioList
+ moneyRates, ok := rateList[zhios_order_relate_utils.StrToInt(coinId)]
+ if ok {
+ moneyRate = moneyRates
+ }
+ }
+ amount, subsidyRatio := GetSubsidyVirtualCoinAmount(ratio, moneyRate, modeStr, newAmt, newSysFee, subsidyMode)
+ subsidyFeeList = append(subsidyFeeList, &VirtualCoinCommission{
+ Cid: coinId,
+ Val: amount,
+ })
+ subsidyRatioList = append(subsidyRatioList, &VirtualCoinCommission{
+ Cid: coinId,
+ Val: subsidyRatio,
+ })
+ }
+ }
+ }
+ }
+ }
+ subsidyFee = zhios_order_relate_utils.FloatFormat(subsidyFee, 6)
+ for i, coin := range subsidyFeeList {
+ subsidyFeeList[i].Val = zhios_order_relate_utils.FloatFormat(subsidyFeeList[i].Val, 6)
+ if coin.Cid == "0" {
+ subsidyFee += coin.Val // 添加上额外补贴到外部的补贴金额
+ }
+ }
+ return subsidyFee, subsidyRatio, isOnlySubsidyFee, subsidyFeeList, subsidyRatioList
+
+}
+
+// 获取佣金比例
+func getCommissionRatio(typ string, level, peerNum int, grade map[int]*LvGrade) (ratio float64) {
+ switch typ {
+ case "team":
+ ratio = grade[level].TeamRate
+ case "same_lv":
+ if len(grade[level].PeerRate) == 0 {
+ ratio = 0
+ } else {
+ ratio = grade[level].PeerRate[peerNum]
+ }
+ default:
+ ratio = grade[level].SelfRate
+ }
+ return
+}
+
+// 获取佣金、虚拟币比例 0=佣金
+func getVirtualCoinRatio(typ string, level, peerNum int, grade map[int]*LvGrade, coinId string) (ratio string) {
+ ok := false
+ switch typ {
+ case "team":
+ ratio, ok = grade[level].TeamRateList[coinId]
+ case "same_lv":
+ ratio, ok = grade[level].PeerRateList[peerNum][coinId]
+ default:
+ ratio, ok = grade[level].SelfRateList[coinId]
+ }
+ if !ok {
+ ratio = "0"
+ }
+ return
+}
+
+// GetLvUpSubsidyVirtualCoinRatio 获取各币种分销补贴比例
+func GetLvUpSubsidyVirtualCoinRatio(level, newLevel, diff int, grade map[int]*LvGrade, coinId string) (ratio string) {
+ gradeCfg, ok := grade[level]
+ if !ok || gradeCfg == nil {
+ return "0"
+ }
+ levelSubsidyCfg := gradeCfg.UserLvUpSubsidyList
+ if levelSubsidyCfg == nil {
+ return "0"
+ }
+ // 找出对应等级的配置
+ var levelRateListArr = make([]interface{}, 0)
+ for oneKey, oneLevel := range levelSubsidyCfg {
+ oneLevelMap, ok := oneLevel.(map[string]interface{})
+ if !ok || oneLevelMap == nil {
+ continue
+ }
+ if oneLevelMap["lv"] == "" && diff == 0 {
+ continue
+ }
+ if oneLevelMap["lv"] == zhios_order_relate_utils.AnyToString(newLevel) || (oneKey+1 == diff && diff > 0) {
+ //可能不是这个类型
+ jsons, ok := oneLevelMap["rate_list"].([]interface{})
+ if ok {
+ levelRateListArr = jsons
+ }
+
+ }
+ }
+ if len(levelRateListArr) == 0 {
+ return "0"
+ }
+ // 各币种的补贴比例
+ coinIdInt := zhios_order_relate_utils.StrToInt(coinId)
+ ratio = levelRateListArr[coinIdInt].(string)
+ if zhios_order_relate_utils.StrToFloat64(ratio) == 0 {
+ return "0"
+ }
+
+ return ratio
+}
+
+// GetSubsidyVirtualCoinAmount 获取各币种补贴值
+// ratio 比例
+// typ 模式 bili money
+func GetSubsidyVirtualCoinAmount(ratio, moneyRate, typ string, fee, sysFee float64, subsidyMode int) (amount, subsidyRatio float64) {
+ ratioF := zhios_order_relate_utils.AnyToFloat64(ratio)
+ if typ == "money" {
+ amount = ratioF
+ subsidyRatio = ratioF / fee
+ } else {
+ subsidyRatio = ratioF / 100
+ if zhios_order_relate_utils.StrToFloat64(moneyRate) > 0 {
+ sysFee = sysFee * zhios_order_relate_utils.StrToFloat64(moneyRate)
+ }
+ if zhios_order_relate_utils.StrToFloat64(moneyRate) > 0 {
+ fee = fee * zhios_order_relate_utils.StrToFloat64(moneyRate)
+ }
+ if subsidyMode == 1 { // 按利润
+ amount = sysFee * subsidyRatio
+ } else { // 按佣金
+ amount = fee * subsidyRatio
+ }
+ }
+ return
+}
+
+// 计算佣金、虚拟币额度
+func getCoinAmount(ratio string, coinId int, fee float64, rateList map[int]string) (amount float64) {
+ moneyRate, ok := rateList[coinId]
+ if !ok {
+ amount = 0.00
+ return
+ }
+ if coinId == 0 { // 金额
+ amount = fee * zhios_order_relate_utils.StrToFloat64(ratio)
+ } else { // 虚拟币 需要将金额按设置的比例兑换成虚拟币 这里不乘比例了 会影响后面的极差
+ //amount = fee * zhios_order_relate_utils.AnyToFloat64(moneyRate) * zhios_order_relate_utils.StrToFloat64(ratio)
+ amount = fee * zhios_order_relate_utils.AnyToFloat64(moneyRate)
+ }
+ return
+}
+
+// 计算各币种数量扣除
+func CalVirtualCommissionMinus(a, b []*VirtualCoinCommission) (c []*VirtualCoinCommission, zeroList map[string]struct{}) {
+ var amount float64
+ zeroList = make(map[string]struct{})
+ for _, coinA := range a {
+ for _, coinB := range b {
+ if coinA.Cid == coinB.Cid {
+ amount = coinA.Val - coinB.Val
+ if amount < 0 {
+ zeroList[coinA.Cid] = struct{}{}
+ amount = 0
+ }
+ c = append(c, &VirtualCoinCommission{
+ Cid: coinA.Cid,
+ Val: amount,
+ })
+ }
+ }
+ }
+ return c, zeroList
+}
+
+func convertList2Map(a []*VirtualCoinCommission) (b map[string]float64) {
+ b = make(map[string]float64)
+ for _, i := range a {
+ b[i.Cid] = i.Val
+ }
+ return b
+}
+
+func convertMap2List(a map[string]float64) (b []*VirtualCoinCommission) {
+ for cid, val := range a {
+ b = append(b, &VirtualCoinCommission{
+ Cid: cid,
+ Val: val,
+ })
+ }
+
+ return b
+}
diff --git a/lib/comm_plan/init.go b/lib/comm_plan/init.go
new file mode 100644
index 0000000..4dbd299
--- /dev/null
+++ b/lib/comm_plan/init.go
@@ -0,0 +1,155 @@
+package comm_plan
+
+var Fn = map[string]func(opt *PlanOpt, totalAmt, integralTotalAmt float64, userList *LvUser, pvd string, sysFee float64, integralSysFee float64) error{
+ "lv_all": CalcAll,
+ "lv_self": CalcSelf,
+ "lv_subsidy": CalcAll,
+ "lv_price": CalcAll,
+ "lv_winery": CalcAll,
+}
+
+type PlanOpt struct {
+ IntegralOpen int
+ PlanCommissionId int
+ IsCanRunSubsidy int //用于后台判断要不要走补贴
+ Pvd string // 供应商
+ Mode string // 分佣方案
+ CommissionMode string // 佣金返佣方式
+ //IntegralMode string // 积分返佣方式
+ //BlockIconsMode string // 区块币返佣方式
+ SysRate float64 // 系统占佣比例
+ PvdRate float64 // 供应商占佣比例
+ RegionRate float64 // 区域代理占佣比例
+ GlobalRate float64 // 全球分红占佣比例
+ MerchantRate float64 //商家占佣比例
+ PushHandRate float64 //推手占佣比例
+ //IntegralBili float64 // 积分兑换比例
+ //BlockIconsBili float64 // 区块币兑换比例
+ UserRate map[int]*LvGrade // 供应商对应的等级比例
+ VirtualCoinMoneyRatioList map[int]string // 兑换现金比例列表
+}
+
+// 级差结构
+type LvGrade struct {
+ Lv int `json:"lv"` // 级别
+ SubsidyMode int `json:"subsidy_mode"` // 补贴计算方式, 0按佣金计算,1按平台利润计算
+ SubsidyEnable int `json:"subsidy_enable"` // 是否开启补贴计算方式 0关闭, 1开启
+ SubsidySelfRate float64 `json:"subsidy_self_rate"` // 自购补贴比例
+ SubsidyShareRate float64 `json:"subsidy_share_rate"` // 分享补贴比例
+ PayMode int `json:"pay_mode"` // 0团队内部支出, 1平台系统支出
+ SelfRate float64 `json:"self_rate"` // 自购比例
+ TeamRate float64 `json:"team_rate"` // 团队分成比例
+ PeerRate []float64 `json:"peer_rate"` // 同级分成比例
+ UserLvUpSubsidyEnable int `json:"user_lv_up_subsidy_enable"` //分销补贴开关
+ UserLvUpSubsidyMode int `json:"user_lv_up_subsidy_mode"` //补贴方式 0比例 1固定金额
+ UserSubsidyType string `json:"user_subsidy_type"` // 补贴模式 up_lv 推荐会员补贴 buy_goods 购买商品
+ UserSubsidyBaseCoinId string `json:"user_subsidy_base_coin_id"` // 补贴计算基数
+ UserLvUpSubsidyList []interface{} `json:"user_lv_up_subsidy_list"` //会员费分销补贴相应方式的列表
+ PeerRateList []map[string]string `json:"peer_rate_list"` //同级比例
+ BuyDeliverList map[string]string `json:"buy_deliver_list"` //酒庄制度
+ ReturnType []string `json:"return_type"` //返利类型
+ SelfRateList map[string]string `json:"self_rate_list"` // 自购比例
+ TeamRateList map[string]string `json:"team_rate_list"` // 团队最高比例
+ SubsidyModeList map[string]interface{} `json:"subsidy_mode_list"` // 各币种返佣模式:bili:比例 money:固定金额
+ //SubsidyBlockIconsMode string `json:"subsidy_block_icons_mode"` //分销 区块币返利类型 bili 比例 money 固定金额
+ //SubsidyCommissionMode string `json:"subsidy_commission_mode"` //分销 佣金返利类型 bili 比例 money 固定金额
+ //SubsidyIntegralMode string `json:"subsidy_integral_mode"` //分销 积分返利类型 bili 比例 money 固定金额
+ SubsidyReturnType []string `json:"subsidy_return_type"` //分销 返利类型
+ SubsidyOwnBiliList []map[string]string `json:"subsidy_own_bili_list"`
+}
+type UserLvUpSubsidyList struct {
+ Lv int `json:"lv"` // 等级
+ //Bili float64 `json:"bili"` // 比例
+ IsUse int `json:"is_use"` // 是否共存
+ //BlockIcons float64 `json:"block_icons"` //区块币
+ Commission float64 `json:"commission"` //佣金 (保留兼容旧版,对应在RateList里面的key为‘0’的值)
+ //Integral float64 `json:"integral"` //积分
+ RateList map[string]interface{} // 各币种分佣设置
+}
+type SettleCommissionToGuide struct {
+ Profit float64 `json:"profit"`
+ PvdFee float64 `json:"pvd_fee"`
+ SysFee float64 `json:"sys_fee"`
+ SubsidyFee float64 `json:"subsidy_fee"`
+ LvUser *LvUser `json:"lv_user"`
+}
+type SettleCommissionToAdminRes struct {
+ DistriCommission string `json:"distriCommission"`
+ PaymentCommission string `json:"paymentCommission"`
+ PvdCommission float64 `json:"pvdCommission"`
+ SubsidyCommission float64 `json:"subsidyCommission"`
+ Relation []*SettleCommissionToAdminLv `json:"relation"`
+ RemainCommission string `json:"remainCommission"`
+ SysCommission float64 `json:"sysCommission"`
+ TotalCommission string `json:"totalCommission"`
+}
+type SettleCommissionToAdminLv struct {
+ AmountCommission string `json:"amountCommission"`
+ Commission string `json:"commission"`
+ CommissionRate string `json:"commissionRate"`
+ GenKey string `json:"genKey"`
+ IsBuyer string `json:"isBuyer"`
+ LvID string `json:"lvId"`
+ LvName string `json:"lvName"`
+ Name string `json:"name"`
+ SubsidyCommission string `json:"subsidyCommission"`
+ SubsidyRate string `json:"subsidyRate"`
+ TierName string `json:"tierName"`
+ Weight string `json:"weight"`
+}
+type SettleCommissionToAdmin struct {
+ Commission string `json:"commission"`
+ ID int64 `json:"id"`
+ RelationList []SettleCommissionToRelationList `json:"relationList"`
+}
+type SettleCommissionToRelationList struct {
+ GenKey string `json:"genKey"`
+ IsBuyer string `json:"isBuyer"`
+ LvID string `json:"lvId"`
+ Name string `json:"name"`
+}
+type LvUser struct {
+ Uid int // 用户ID
+ Lv int // 等级
+ NewLv int // 升级后等级 针对会员费分佣
+ LevelWeight int // 权重
+ Profit float64 // 利润(金额)
+ SubsidyFee float64 // 补贴(金额)
+ //IntegralProfit float64 // 积分利润
+ //IntegralSubsidyFee float64 // 积分补贴
+ //BlockIconsProfit float64 // 区块币利润
+ //BlockIconsSubsidyFee float64 // 区块币补贴
+ ProfitList []*VirtualCoinCommission // 各币种对用的数量
+ SubsidyFeeList []*VirtualCoinCommission // 各币种对应的补贴数量
+ OwnSubsidyFeeList map[string]float64 // 各币种对应的补贴数量
+ OwnbuyReturnType int //0有返利 1没有返利
+ Diff int // 与当前用户级别差
+ OldDiff int // 旧的与当前用户级别差
+ ParentUser *LvUser // 父用户
+}
+
+// 虚拟币分佣结构体
+type VirtualCoinCommission struct {
+ Cid string `json:"cid"` // 虚拟币id
+ Val float64 `json:"val"` // 数量
+}
+
+type VirtualCoinRateList struct {
+ Cid string `json:"cid"` // 虚拟币id
+ Val string `json:"val"` // 现金兑换比例
+}
+
+// 初始化级差模式
+func Init(g map[int]*LvGrade) map[int]*LvGrade {
+ var MinLv int = -1 // 最低级别
+ for k, v := range g {
+ if MinLv == -1 {
+ MinLv = v.Lv
+ g[k].TeamRate = v.SelfRate
+ } else if v.Lv < MinLv {
+ MinLv = v.Lv
+ g[k].TeamRate = v.SelfRate
+ }
+ }
+ return g
+}
diff --git a/lib/comm_plan/self.go b/lib/comm_plan/self.go
new file mode 100644
index 0000000..a321032
--- /dev/null
+++ b/lib/comm_plan/self.go
@@ -0,0 +1,185 @@
+package comm_plan
+
+import (
+ zhios_order_relate_utils "code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/utils"
+ zhios_order_relate_logx "code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/utils/logx"
+ "fmt"
+)
+
+// 按自购佣金进行计算
+func CalcSelf(opt *PlanOpt, totalAmt float64, integralTotalAmt float64, userList *LvUser, pvd string, sysFee float64, integralSysFee float64) error {
+ grade := opt.UserRate
+ if len(grade) == 0 {
+ return zhios_order_relate_logx.Warn("level grade is not set")
+ }
+
+ //查出用户自购佣金
+ commission, commissionRatio, amountList, ratioList := CalReturnAmountAndRatio(userList.Lv, userList.OwnbuyReturnType, 0, "own", totalAmt, integralTotalAmt, opt)
+ userList.Profit = commission // 另外出来的佣金 兼容旧的
+ userList.ProfitList = amountList // 各币种分佣
+ userList.SubsidyFee = 0
+ ratioListMap := convertList2Map(ratioList)
+
+ for k, v := range userList.ProfitList {
+ userList.ProfitList[k].Val = ratioListMap[v.Cid] * v.Val
+ }
+ // 各种币换算出总的额度
+ totalAmtList := make([]*VirtualCoinCommission, 0)
+ for coinId, rate := range opt.VirtualCoinMoneyRatioList {
+ var amount float64
+ if coinId == 0 {
+ amount = totalAmt
+ } else {
+ amount = totalAmt * zhios_order_relate_utils.AnyToFloat64(rate)
+ }
+ totalAmtList = append(totalAmtList, &VirtualCoinCommission{
+ Cid: zhios_order_relate_utils.AnyToString(coinId),
+ Val: amount,
+ })
+ }
+
+ var (
+ node = userList
+ maxLv = node.Lv // 当前等级
+ maxLevelWeight = node.LevelWeight // 当前权重
+ peerNum = 0 // 存在同级数
+ peerRate float64 = 0 // 同级累计比例
+ peerRateList = make([]*VirtualCoinCommission, 0) // 各虚拟币同级累计
+ restAmtList = make([]*VirtualCoinCommission, 0) // 各虚拟币剩余额度
+ accumulateRatioList = make([]*VirtualCoinCommission, 0) // 各虚拟币累计比例
+ //integralPeerRate float64 = 0 // 同级累计比例
+ //blockIconsPeerRate float64 = 0 // 同级累计比例
+ restAmt = totalAmt - userList.Profit // 剩余比例
+ //integralRestAmt = totalAmt - userList.IntegralProfit // 积分剩余比例
+ //blockIconsRestAmt = totalAmt - userList.BlockIconsProfit // 区块币剩余比例
+ totalCommissionRatio = commissionRatio // 累计佣金比例
+ //integralBili = integralLeaveBili // 积分累计佣金比例
+ //blockIconsBili = blockIconsLeaveBili // 区块币累计佣金比例
+ )
+ // 计算剩余额度
+ restAmtList, _ = CalVirtualCommissionMinus(totalAmtList, amountList)
+ // 累计比例
+ accumulateRatioList = ratioList
+
+ // 以用户自购为基数
+ totalAmt = userList.Profit
+ totalAmtList = amountList
+
+Loop:
+ for node.ParentUser != nil { //查找上级用户
+ node.ParentUser.Profit = 0
+
+ //佣金补贴奖励
+ subsidyFee, subsidyRatio, isOnlySubsidyFee, subsidyFeeList, subsidyRatioList := subsidyFeeDo(opt, totalAmt, integralTotalAmt, node.ParentUser, userList.NewLv, pvd, sysFee, integralSysFee)
+ node.ParentUser.SubsidyFee = subsidyFee
+ node.ParentUser.SubsidyFeeList = subsidyFeeList // 各币种补贴
+
+ // 如果父级比当前级别低, 跳过
+ // 同级奖, 如果父级别与当前级别一致,并且设置了对应比例
+ count := len(grade[maxLv].PeerRate)
+ if grade[maxLv].PeerRateList != nil {
+ count = len(grade[maxLv].PeerRateList)
+ }
+ var isBreak bool
+ zeroList := make(map[string]struct{})
+ // 同级奖
+ if node.ParentUser.LevelWeight == maxLevelWeight && count > peerNum {
+ //同级奖励比例
+ commission, commissionRatio, amountList, ratioList := CalReturnAmountAndRatio(maxLv, userList.OwnbuyReturnType, peerNum, "same_lv", totalAmt, integralTotalAmt, opt)
+ //佣金 (lv, isOnlySubsidy int, restAmt, profit, peerRate, totalRatio, restRatio, subsidyFee, subsidyBili float64, opt *PlanOpt)
+ node.ParentUser.Profit, restAmt, totalCommissionRatio, peerRate, node.ParentUser.SubsidyFee, isBreak = sameMoney(node.Lv, isOnlySubsidyFee, restAmt, commission, peerRate, totalCommissionRatio, commissionRatio, node.ParentUser.SubsidyFee, subsidyRatio, opt)
+ // 虚拟币 newProfitList, newRestAmtList, newTotalRatioList, newPeerRateList, newSubsidyFeeList, zeroList
+ node.ParentUser.ProfitList, restAmtList, accumulateRatioList, peerRateList, node.ParentUser.SubsidyFeeList, zeroList = sameMoneyV2(node.Lv, isOnlySubsidyFee, totalAmtList, restAmtList, amountList, peerRateList, accumulateRatioList, ratioList, node.ParentUser.SubsidyFeeList, subsidyRatioList, opt)
+
+ // 全部都没得分了
+ if isBreak && len(zeroList) == len(opt.UserRate[maxLv].ReturnType) {
+ break Loop
+ }
+ peerNum++
+ } else if node.ParentUser.LevelWeight > maxLevelWeight {
+ if _, ok := grade[node.Lv]; !ok {
+ return zhios_order_relate_logx.Warn("level grade node.Lv is not set")
+ }
+ if _, ok := grade[node.ParentUser.Lv]; !ok {
+ return zhios_order_relate_logx.Warn("level grade node.ParentUser.Lv is not set")
+ }
+ commission, commissionRatio, amountList, teamRatioList := CalReturnAmountAndRatio(node.ParentUser.Lv, userList.OwnbuyReturnType, peerNum, "team", totalAmt, integralTotalAmt, opt)
+ //佣金
+ node.ParentUser.Profit = commission
+ node.ParentUser.Profit, restAmt, totalCommissionRatio, node.ParentUser.SubsidyFee, isBreak = teamDiffMoney(node.ParentUser.Profit, grade[node.Lv].PayMode, isOnlySubsidyFee, totalAmt, restAmt, grade[node.ParentUser.Lv].TeamRate, commissionRatio, peerRate, node.ParentUser.SubsidyFee, subsidyRatio)
+ //积分
+ node.ParentUser.ProfitList = amountList
+ // profitList []*VirtualCoinCommission, payMode, isOnlySubsidy int, totalAmtList, restAmtList, teamRatioList, totalRatioList, peerRateList, subsidyFeeList, subsidyRatioList []*VirtualCoinCommission
+ node.ParentUser.ProfitList, restAmtList, accumulateRatioList, node.ParentUser.SubsidyFeeList, zeroList = teamDiffMoneyV2(node.ParentUser.ProfitList, grade[node.Lv].PayMode, isOnlySubsidyFee, totalAmtList, restAmtList, teamRatioList, accumulateRatioList, peerRateList, subsidyFeeList, subsidyRatioList)
+
+ // 没得分了 就结束
+ if isBreak && len(zeroList) == len(opt.UserRate[maxLv].ReturnType) {
+ break Loop
+ }
+ // 等级往上升则置0
+ maxLevelWeight, maxLv, peerRate, peerRateList, peerNum = node.ParentUser.LevelWeight, node.ParentUser.Lv, 0, nil, 0
+ }
+ node.Profit = zhios_order_relate_utils.StrToFloat64(fmt.Sprintf("%.4f", node.Profit))
+
+ node = node.ParentUser
+
+ }
+
+ return nil
+}
+
+/*func Test() {
+ g := map[int]*LvGrade{
+ 0: {
+ Lv: 0,
+ SelfRate: 0.4,
+ TeamRate: 0,
+ PeerRate: []float64{0.05, 0.03, 0.02},
+ },
+ 1: {Lv: 1,
+ SelfRate: 0.5,
+ TeamRate: 0.6,
+ PeerRate: []float64{0.1, 0.05, 0.03},
+ },
+ 2: {
+ Lv: 2,
+ SelfRate: 0.6,
+ TeamRate: 0.7,
+ PeerRate: []float64{},
+ },
+ }
+ Init(g)
+ var money float64 = 100
+ u := []int{0, 0, 1, 1, 2}
+ var uList *LvUser
+ var node *LvUser
+ for k, v := range u {
+ if uList == nil {
+ uList = &LvUser{
+ Uid: k,
+ Lv: v,
+ }
+ node = uList
+ } else {
+ if node != nil {
+ node.ParentUser = &LvUser{
+ Uid: k,
+ Lv: v,
+ }
+ node = node.ParentUser
+ }
+ }
+ }
+ fmt.Println(u, node, uList)
+
+ CalcSelf(g, money, uList)
+ fmt.Println("final:", uList)
+ for uList.ParentUser != nil {
+ fmt.Printf("%#v\n", uList)
+ uList = uList.ParentUser
+ if uList.ParentUser == nil {
+ fmt.Printf("%#v", uList)
+ }
+ }
+}
+*/
diff --git a/md/app_redis_key.go b/md/app_redis_key.go
new file mode 100644
index 0000000..d7d5f33
--- /dev/null
+++ b/md/app_redis_key.go
@@ -0,0 +1,19 @@
+package md
+
+// 缓存key统一管理, %s格式化为masterId
+const (
+ AppCfgCacheKey = "%s:cfg_cache:%s" // 占位符: masterId, key的第一个字母
+ VirtualCoinCfgCacheKey = "%s:virtual_coin_cfg"
+ PlanRewardCfgCacheKey = "%s:plan_reward_cfg"
+ UnionSetCacheCfg = "%s:union_set_cfg:%s" // 联盟设置缓存key
+
+ UserFinValidUpdateLock = "%s:user_fin_valid_update_lock:%s" // 用户余额更新锁(能拿到锁才能更新余额)
+
+ WithdrawApplyQueueListKey = "withdraw_apply_queue" // 提现队列
+
+ TplBottomNavRedisKey = "%s:tpl_nav_bottom_key:%s" // master_id platform
+
+ SysModByIdRedisKey = "%s:sys_mod_tpl_by_id:%s"
+
+ CfgCacheTime = 86400
+)
diff --git a/md/cfg_key.go b/md/cfg_key.go
new file mode 100644
index 0000000..e8377fd
--- /dev/null
+++ b/md/cfg_key.go
@@ -0,0 +1,92 @@
+package md
+
+// 获取用户的缓存key
+const (
+ KEY_SYS_CFG_CACHE = "sys_cfg_cache"
+
+ // 文件缓存的key
+ KEY_CFG_FILE_PVD = "file_provider" // 文件供应商
+ KEY_CFG_FILE_BUCKET = "file_bucket"
+ KEY_CFG_FILE_REGION = "file_bucket_region"
+ KEY_CFG_FILE_HOST = "file_bucket_host"
+ KEY_CFG_FILE_SCHEME = "file_bucket_scheme"
+ KEY_CFG_FILE_AK = "file_access_key"
+ KEY_CFG_FILE_SK = "file_secret_key"
+ KEY_CFG_FILE_MAX_SIZE = "file_user_upload_max_size"
+ KEY_CFG_FILE_EXT = "file_ext"
+ KEY_CFG_FILE_AVATAR_THUMBNAIL = "file_avatar_thumbnail" // 默认头像缩略图参数,宽高120px,格式webp.
+ // 智盟
+ KEY_CFG_ZM_JD_SITE_ID = "third_zm_jd_site_id" // 智盟京东联盟id
+ KEY_CFG_ZM_WEB_ID = "third_zm_web_id" // 智盟网站ID
+ KEY_CFG_ZM_AK = "third_zm_app_key"
+ KEY_CFG_ZM_SK = "third_zm_app_secret"
+ KEY_CFG_ZM_SMS_AK = "third_zm_sms_ak"
+ KEY_CFG_ZM_SMS_SK = "third_zm_sms_sk"
+ KEY_CFG_APP_NAME = "app_name"
+
+ KEY_CFG_WHITELIST = "api_cfg_whitelist" // API允许的访问的设置白名单
+
+ // 淘宝
+ KEY_CFG_TB_AUTH_AK = "third_taobao_auth_ak"
+ KEY_CFG_TB_AUTH_SK = "third_taobao_auth_sk"
+ KEY_CFG_TB_INVITER_CODE = "third_taobao_auth_inviter_code"
+ KEY_CFG_TB_AK = "third_taobao_ak"
+ KEY_CFG_TB_SK = "third_taobao_sk"
+ KEY_CFG_TB_PID = "third_taobao_pid" // 淘宝推广ID,如:mm_123_456_789,123是联盟ID,456是site_id,789是adzone_id
+ KEY_CFG_TB_SID = "third_taobao_sid" // 淘宝session id ,又称access_token
+
+ // 苏宁
+ KEY_CFG_SN_AK = "third_suning_ak"
+ KEY_CFG_SN_SK = "third_suning_sk"
+
+ KEY_CFG_JD_AK = ""
+ KEY_CFG_JD_SK = ""
+
+ KEY_CFG_KL_AK = "third_kaola_ak"
+ KEY_CFG_KL_SK = "third_kaola_sk"
+
+ KEY_CFG_VIP_AK = ""
+ KEY_CFG_VIP_SK = ""
+
+ // 自动任务配置
+ KEY_CFG_CRON_TB = "cron_order_taobao"
+ KEY_CFG_CRON_JD = "cron_order_jd"
+ KEY_CFG_CRON_PDD = "cron_order_pdd"
+ KEY_CFG_CRON_SN = "cron_order_suning"
+ KEY_CFG_CRON_VIP = "cron_order_vip"
+ KEY_CFG_CRON_KL = "cron_order_kaola"
+ KEY_CFG_CRON_DUOMAI = "cron_order_duomai"
+ KEY_CFG_CRON_HIS = "cron_order_his" // 迁移到历史订单
+ KEY_CFG_CRON_SETTLE = "cron_order_settle" // 迁移到历史订单
+ KEY_CFG_CRON_PUBLISHER = "cron_taobao_publisher" // 跟踪淘宝备案信息绑定会员运营id 针对小程序
+ KEY_CFG_CRON_MEITUAN = "cron_order_meituan" //美团
+ KEY_CFG_CRON_OILSTATION = "cron_order_oilstation" //加油
+ KEY_CFG_CRON_KFC = "cron_order_kfc" //肯德基
+ KEY_CFG_CRON_CINEMA = "cron_order_cinema" //电影票
+ KEY_CFG_CRON_OilRequest = "cron_order_oilrequest" //加入主动请求抓单
+ KEY_CFG_CRON_AGOTB = "cron_order_agotaobao" //n天前的淘宝订单
+ KEY_CFG_CRON_CREDIT_CARD = "cron_order_credit_card"
+ KEY_CFG_CRON_ORDER_STAT = "cron_order_stat" // 订单统计任务
+ KEY_CFG_CRON_CARD_UPDATE = "cron_card_update" // 权益卡更新
+ KEY_CFG_CRON_USER_LV_UP_SETTLE = "cron_user_lv_up_settle" //会员费订单结算
+ KEY_CFG_CRON_PRIVILEGE_CARD_SETTLE = "cron_privilege_card_settle" //权益卡订单结算
+ KEY_CFG_CRON_CARD_RETURN = "cron_card_return" //权益卡退款
+ KEY_CFG_CRON_PUBLISHER_RELATION = "cron_taobao_publisher_relation" //获取淘宝渠道
+ KEY_CFG_CRON_DTKBRAND = "cron_dtk_brand" //大淘客品牌信息
+ KEY_CFG_CRON_PUBLISHER_RELATION_BIND = "cron_taobao_publisher_relation_bind" //获取淘宝渠道绑定
+
+ // 自动任务运行时设置
+ KEY_CFG_CRON_TIME_TB = "crontab_order_time_taobao"
+ KEY_CFG_CRON_TIME_JD = "crontab_order_time_jd"
+ KEY_CFG_CRON_TIME_PDD = "crontab_order_time_pdd"
+ KEY_CFG_CRON_TIME_SN = "crontab_order_time_suning"
+ KEY_CFG_CRON_TIME_VIP = "crontab_order_time_vip"
+ KEY_CFG_CRON_TIME_KL = "crontab_order_time_kaola"
+ KEY_CFG_CRON_TIME_DUOMAI = "crontab_order_time_duomai"
+ KEY_CFG_CRON_TIME_PUBLISHER = "crontab_taobao_time_publisher" // 跟踪淘宝备案信息绑定会员运营id 针对小程序
+ KEY_CFG_CRON_TIME_MEITUAN = "crontab_order_time_meituan" //美团
+ KEY_CFG_CRON_TIME_OILSTATION = "crontab_order_time_oilstation" //加油
+ KEY_CFG_CRON_TIME_KFC = "crontab_order_time_kfc" //肯德基
+ KEY_CFG_CRON_TIME_CINEMA = "crontab_order_time_cinema" //电影票
+
+)
diff --git a/md/commission_parameter.go b/md/commission_parameter.go
new file mode 100644
index 0000000..c58da41
--- /dev/null
+++ b/md/commission_parameter.go
@@ -0,0 +1,28 @@
+package md
+
+// MoreDetailResponse is for response detail
+type CommissionParam struct {
+ OldPrice string `json:"old_price"`
+ GoodsPrice string `json:"goods_price"`
+ Commission string `json:"commission"`
+ CommissionRate string `json:"commission_rate"`
+ CouponPrice string `json:"coupon_price"`
+ WlGoodsPrice string `json:"wl_goods_price"` //卷后价
+ LowerPrice string `json:"lower_price"`
+ LowestCouponPrice string `json:"lowestCouponPrice"`
+ MinGroupPrice string `json:"min_group_price"`
+ PaidPrice string `json:"paid_price"`
+}
+type CommissionFirstParam struct {
+ CommissionParam CommissionParam `json:"commission_param"`
+ Uid string `json:"uid"`
+ IsShare int `json:"is_share"`
+ OldLv string `json:"old_lv"` //升级礼包读取的是升级前的等级
+ NewLv string `json:"new_lv"` //升级礼包读取的是升级后的等级
+ Provider string `json:"provider"`
+ IsAllLevelReturn int `json:"is_all_level_return"` // 是否返回所有层级
+ GoodsId string `json:"goods_id,omitempty"` // 用于标记是哪个商品的
+ OwnbuyReturnType int `json:"ownbuy_return_type"` //自购是否返利 0返利 1不返利
+ Oid string `json:"oid"`
+ ShowLevel string `json:"show_level"`
+}
diff --git a/md/provider.go b/md/provider.go
new file mode 100644
index 0000000..98d18f4
--- /dev/null
+++ b/md/provider.go
@@ -0,0 +1,143 @@
+package md
+
+const (
+ PVD_TB = "taobao"
+ PVD_JD = "jd"
+ PVD_SN = "suning"
+ PVD_VIP = "vip"
+ PVD_PDD = "pdd"
+ PVD_KL = "kaola"
+ PVD_TM = "tmall"
+ PVD_DTK = "dataoke"
+ PVD_HDK = "haodanku"
+ PVD_JTT = "jingtuitui"
+ // 特殊活动免单方案
+ PVD_FREE = "free"
+ PVD_MEITUAN = "meituan"
+ PVD_OILSTATION = "oil"
+ PVD_KFC = "kfc"
+ PVD_CINEMA = "cinema"
+ PVD_NEW_CINEMA = "new_cinema" //新电影票
+ PVD_CREDIT_CARD = "credit_card"
+ PVD_CARD_REFUND = "card_refund" //权益卡退款
+ PVD_CARD = "privilege_card" //权益卡
+ PVD_USER_LV_UP = "userlvup" //会员费用
+ PVD_OPEN_CARD = "privilege_open_card" // 权益卡开卡
+ PVD_DUOMAI = "duomai" // 多麦
+ PVD_MALL_GOODS = "mall_goods" // 自营商品订单返佣
+ PVD_MALL_GOODS_USER_LV = "mall_goods_user_lv" // 自营商品升级礼包订单返佣
+ PVD_GROUP_BUY = "mall_group_buy" // 拼团未中奖返佣
+ PVD_REGIONAL_AGENT_PAY = "regional_agent_pay" // 区域代理升级付款
+ PVD_GUIDE = "GUIDE" //导购
+ PVD_SELF_MALL = "SELF_MALL" //自营
+ PVD_O2O = "O2O" //O2O
+ PVD_COMMON = "COMMON" //区域代理通用网点
+ PVD_ORDINARY = "ordinary" //通用
+
+)
+
+var PVD_LIST = map[string]string{
+ PVD_TB: "淘宝",
+ PVD_JD: "京东",
+ PVD_SN: "苏宁",
+ PVD_VIP: "唯品会",
+ PVD_PDD: "拼多多",
+ PVD_KL: "考拉",
+ PVD_CREDIT_CARD: "信用卡",
+ PVD_TM: "天猫",
+ PVD_USER_LV_UP: "会员升级",
+ PVD_CARD_REFUND: "权益卡退款",
+ PVD_CARD: "权益卡",
+ PVD_MEITUAN: "美团",
+ PVD_OILSTATION: "加油",
+ PVD_KFC: "肯德基",
+ PVD_CINEMA: "电影票",
+ "ele": "饿了么",
+ "user_lv_up": "会员升级",
+ PVD_REGIONAL_AGENT_PAY: "区域代理升级",
+ PVD_GUIDE: "导购", //导购
+ PVD_SELF_MALL: "自营", //自营
+ PVD_O2O: "O2O", //O2O
+ PVD_COMMON: "区域代理通用网点",
+ PVD_ORDINARY: "通用", //通用
+ "CARD": "权益卡",
+ "OIL": "加油",
+}
+
+var PVD_LIST_ICON = map[string]string{
+ PVD_TB: "provider-square-icon-taobao.png",
+ PVD_JD: "provider-square-icon-jd.png",
+ PVD_SN: "provider-square-icon-suning.png",
+ PVD_VIP: "provider-square-icon-vip.png",
+ PVD_PDD: "provider-square-icon-pdd.png",
+ PVD_KL: "provider-square-icon-kaola.png",
+}
+
+var ZHIMENG_CFG_LIST = []string{KEY_CFG_ZM_AK, KEY_CFG_ZM_SK, KEY_CFG_ZM_WEB_ID}
+
+var PVD_CFG_LIST = map[string][]string{
+ PVD_TB: {
+ "third_taobao_sid",
+ "third_taobao_web_ak",
+ "third_taobao_web_sk",
+ "third_taobao_svc_ak",
+ "third_taobao_svc_sk",
+ "third_taobao_svc_sid",
+ // 推广位
+ "third_taobao_share_inviter_code",
+ "third_taobao_share_pid_android",
+ "third_taobao_share_pid_ios",
+ "third_taobao_share_pid_web",
+ // 推广位
+ "third_taobao_self_inviter_code",
+ "third_taobao_self_pid_android",
+ "third_taobao_self_pid_ios",
+ "third_taobao_self_pid_web",
+ },
+ PVD_JD: {KEY_CFG_ZM_AK, KEY_CFG_ZM_SK, KEY_CFG_ZM_SMS_AK, KEY_CFG_ZM_SMS_SK, KEY_CFG_ZM_JD_SITE_ID, KEY_CFG_ZM_WEB_ID},
+ PVD_VIP: {KEY_CFG_ZM_AK, KEY_CFG_ZM_SK, KEY_CFG_ZM_SMS_AK, KEY_CFG_ZM_SMS_SK, KEY_CFG_ZM_JD_SITE_ID, KEY_CFG_ZM_WEB_ID},
+ PVD_PDD: {KEY_CFG_ZM_AK, KEY_CFG_ZM_SK, KEY_CFG_ZM_SMS_AK, KEY_CFG_ZM_SMS_SK, KEY_CFG_ZM_JD_SITE_ID, KEY_CFG_ZM_WEB_ID},
+ PVD_SN: {KEY_CFG_SN_AK, KEY_CFG_SN_SK},
+ PVD_KL: {KEY_CFG_KL_AK, KEY_CFG_KL_SK},
+ PVD_MEITUAN: {KEY_CFG_ZM_AK, KEY_CFG_ZM_SK, KEY_CFG_ZM_SMS_AK, KEY_CFG_ZM_SMS_SK, KEY_CFG_ZM_WEB_ID},
+ PVD_OILSTATION: {KEY_CFG_ZM_AK, KEY_CFG_ZM_SK, KEY_CFG_ZM_SMS_AK, KEY_CFG_ZM_SMS_SK, KEY_CFG_ZM_WEB_ID},
+ PVD_KFC: {KEY_CFG_ZM_AK, KEY_CFG_ZM_SK, KEY_CFG_ZM_SMS_AK, KEY_CFG_ZM_SMS_SK, KEY_CFG_ZM_WEB_ID},
+ PVD_CINEMA: {KEY_CFG_ZM_AK, KEY_CFG_ZM_SK, KEY_CFG_ZM_SMS_AK, KEY_CFG_ZM_SMS_SK, KEY_CFG_ZM_WEB_ID},
+}
+
+var COIN_TRANSFER_TYPE = map[int]string{
+ 1: "全球分红",
+ 2: "管理员修改",
+ 3: "消费",
+ 4: "退回",
+ 5: "虚拟币兑换",
+ 6: "余额充值",
+}
+var COMM_TASK_TYPE_NAME = map[int]string{
+ 1: "邀请好友",
+ 2: "分享商品",
+ 3: "观看视频广告",
+ 4: "购买商品",
+ 5: "淘宝授权",
+ 6: "微信绑定",
+ 7: "成为XX等级会员",
+ 8: "填写邀请码",
+ 9: "绑定手机号",
+}
+var COMM_TASK_TYPE_ONE = []string{
+ "5", "6", "7", "8", "9",
+}
+var COMM_TASK_TYPE_MORE = []string{
+ "1", "2", "3", "4",
+}
+var VIRTUAL_COIN_FUNCTION_TYPE = map[string]string{
+ "block": "block",
+ "capital_pool": "capital_pool",
+ "super_group": "super_group",
+}
+
+var VIRTUAL_COIN_FUNCTION_TYPE_NAME = map[string]string{
+ "block": "区块币",
+ "capital_pool": "全球分红",
+ "super_group": "超级拼团",
+}
diff --git a/md/user_info.go b/md/user_info.go
new file mode 100644
index 0000000..af6cbc9
--- /dev/null
+++ b/md/user_info.go
@@ -0,0 +1,39 @@
+package md
+
+import (
+ "code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/db/model"
+)
+
+type UserInfoResponse struct {
+ Avatar string `json:"avatar"`
+ NickName string `json:"nickname"`
+ Gender string `json:"gender"`
+ Birthday string `json:"birthday"`
+ RegisterTime string `json:"register_time"`
+ FileBucketURL string `json:"file_bucket_url"`
+ FileFormat string `json:"file_format"`
+ IsNoChange string `json:"is_no_change"`
+ IsUpLoadWx string `json:"is_upload_wx"`
+}
+
+type User struct {
+ Info *model.User
+ Profile *model.UserProfile
+ Level *model.UserLevel
+ Tags []string
+}
+
+type UserRelation struct {
+ Uid int
+ CurUid int
+ Diff int // 与当前用户级别差
+ Level int // 用户当前等级
+ OldDiff int // 旧的级别
+}
+
+type UserTree struct {
+ Uid int `json:"uid"`
+ Nickname string `json:"nickname"`
+ Phone string `json:"phone"`
+ Children []*UserTree `json:"children"`
+}
diff --git a/rule/get_plan_cfg.go b/rule/get_plan_cfg.go
new file mode 100644
index 0000000..52d2977
--- /dev/null
+++ b/rule/get_plan_cfg.go
@@ -0,0 +1,180 @@
+package rule
+
+import (
+ "code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/db"
+ "code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/db/model"
+ "code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/lib/comm_plan"
+ "code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/md"
+ zhios_order_relate_utils "code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/utils"
+ zhios_order_relate_logx "code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/utils/logx"
+ "encoding/json"
+ "fmt"
+ "strings"
+ "xorm.io/xorm"
+)
+
+var pvdCfgList = map[string]struct {
+ AK string
+ SK string
+ Sid string
+}{
+ md.PVD_TB: {"third_taobao_svc_ak", "third_taobao_svc_sk", "third_taobao_web_sid"},
+ md.PVD_KL: {md.KEY_CFG_KL_AK, md.KEY_CFG_KL_SK, ""},
+ md.PVD_SN: {md.KEY_CFG_SN_AK, md.KEY_CFG_SN_SK, ""},
+ md.PVD_JD: {md.KEY_CFG_ZM_AK, md.KEY_CFG_ZM_SK, md.KEY_CFG_ZM_WEB_ID},
+ md.PVD_PDD: {md.KEY_CFG_ZM_AK, md.KEY_CFG_ZM_SK, md.KEY_CFG_ZM_WEB_ID},
+ md.PVD_VIP: {md.KEY_CFG_ZM_AK, md.KEY_CFG_ZM_SK, md.KEY_CFG_ZM_WEB_ID},
+}
+
+func GetPlanCfg(eg *xorm.Engine, pvd, masterId string) (*comm_plan.PlanOpt, error) {
+ opt := &comm_plan.PlanOpt{}
+ // 根据供应商
+ rewardOpt, err := db.DbsPlanRewardByPvd(eg, pvd)
+ if err != nil {
+ return nil, err
+ }
+ if rewardOpt == nil {
+ return nil, zhios_order_relate_logx.Warn("找不到方案记录")
+ }
+ if rewardOpt.State == 0 {
+ return nil, zhios_order_relate_logx.Warn("抽成方案未开启")
+ }
+ if rewardOpt.PlanCommissionId == 0 {
+ return nil, zhios_order_relate_logx.Warn("抽成方案未设置佣金方案id")
+ }
+ fmt.Println("抽成设置:", zhios_order_relate_utils.SerializeStr(rewardOpt))
+ fmt.Println("commission id:", rewardOpt.PlanCommissionId)
+ commissionOpt, err := db.DbsPlanCommissionById(eg, rewardOpt.PlanCommissionId)
+ if err != nil || commissionOpt == nil || commissionOpt.Id == 0 {
+ return nil, err
+ }
+ if _, ok := comm_plan.Fn[commissionOpt.Mode]; !ok {
+ return nil, zhios_order_relate_logx.Warn("分佣模式不存在")
+ }
+ opt.Pvd = pvd
+ opt.Mode = commissionOpt.Mode
+ opt.IntegralOpen = rewardOpt.IntegralOpen
+ opt.SysRate = float64(int64(rewardOpt.SysRate*1e4)) / 1e4
+ opt.PvdRate = float64(int64(rewardOpt.PvdRate*1e4)) / 1e4
+ opt.RegionRate = float64(int64(rewardOpt.RegionRate*1e4)) / 1e4
+ opt.GlobalRate = float64(int64(rewardOpt.GlobalRate*1e4)) / 1e4
+ opt.PushHandRate = float64(int64(rewardOpt.PushHandRate*1e4)) / 1e4
+ opt.MerchantRate = float64(int64(rewardOpt.MerchantRate*1e4)) / 1e4
+ opt.PlanCommissionId = rewardOpt.PlanCommissionId
+
+ // 兑换现金比例
+ virtualCoinMoneyRate, err := GetVirtualCoinMoneyRateList(eg, masterId)
+ if err != nil || virtualCoinMoneyRate == nil {
+ opt.VirtualCoinMoneyRatioList = nil
+ } else {
+ opt.VirtualCoinMoneyRatioList = virtualCoinMoneyRate
+ }
+
+ if opt.SysRate >= 1 || opt.PvdRate >= 1 || opt.RegionRate >= 1 || opt.GlobalRate >= 1 || opt.PvdRate+opt.SysRate+opt.RegionRate+opt.GlobalRate >= 1 {
+ return nil, zhios_order_relate_logx.Warn("分佣方案数据设置总额不能大于1")
+ }
+ var subsidyTmp map[int]*comm_plan.LvGrade
+ commissionOpt.Data = strings.ReplaceAll(commissionOpt.Data, "\"bili\":0", "\"bili\":\"0\"")
+ if strings.Contains(commissionOpt.Data, "\"subsidy_mode_list\":[") { //兼容旧的方案
+ tmp := strings.Split(commissionOpt.Data, "\"subsidy_mode_list\":[")
+ if len(tmp) > 0 {
+ tmp1 := strings.Split(tmp[1], "]")
+ if len(tmp1) > 0 {
+ str := "\"subsidy_mode_list\":[" + tmp1[0] + "]"
+ ex := strings.Split(tmp1[0], ",")
+ bili := "bili"
+ if len(ex) > 0 {
+ bili = strings.ReplaceAll(ex[0], "\"", "")
+ }
+ commissionOpt.Data = strings.ReplaceAll(commissionOpt.Data, str, "\"subsidy_mode_list\":{\"0\":\""+bili+"\"}")
+ }
+ }
+ }
+ if err := json.Unmarshal([]byte(commissionOpt.Data), &subsidyTmp); err != nil {
+ return nil, zhios_order_relate_logx.Warn(fmt.Sprintf("%s:分佣方案数据设置错误", masterId))
+ }
+ opt.UserRate = subsidyTmp
+
+ return opt, nil
+}
+
+// 获取所有供应商对应的分佣方案, 包括免单,一元购
+func PlanOpts(eg *xorm.Engine) map[string]*comm_plan.PlanOpt {
+ // 获取所有佣金方案
+ allCommissionPlan := db.DbsPlanCommissionByIds(eg)
+ if allCommissionPlan == nil {
+ return nil
+ }
+ commissionPlans := map[int]*model.PlanCommission{}
+ for _, v := range allCommissionPlan {
+ v.Data = strings.ReplaceAll(v.Data, "\"bili\":0", "\"bili\":\"0\"")
+ commissionPlans[v.Id] = v
+ }
+
+ // 获取所有抽成方案的计划
+ allRewardPlan, err := db.DbsPlanRewardByPvds(eg)
+ if err != nil || allRewardPlan == nil {
+ return nil
+ }
+
+ opts := map[string]*comm_plan.PlanOpt{}
+ for _, v := range allRewardPlan {
+ if _, ok := commissionPlans[v.PlanCommissionId]; ok && v.State == 1 && v.PlanCommissionId > 0 {
+ var subsidyTmp map[int]*comm_plan.LvGrade
+ if err := json.Unmarshal([]byte(commissionPlans[v.PlanCommissionId].Data), &subsidyTmp); err != nil {
+ zhios_order_relate_logx.Warn("分佣方案数据设置错误 :", commissionPlans[v.PlanCommissionId])
+ continue
+ }
+ opts[v.Pvd] = &comm_plan.PlanOpt{
+ Pvd: v.Pvd,
+ PlanCommissionId: v.PlanCommissionId,
+ Mode: commissionPlans[v.PlanCommissionId].Mode,
+ CommissionMode: commissionPlans[v.PlanCommissionId].CommissionMode,
+ SysRate: float64(v.SysRate),
+ PvdRate: float64(v.PvdRate),
+ RegionRate: float64(v.RegionRate),
+ GlobalRate: float64(v.GlobalRate),
+ UserRate: subsidyTmp,
+ }
+
+ }
+ }
+ fmt.Println(opts)
+ if len(opts) == 0 {
+ return nil
+ }
+ return opts
+}
+func GetCreditCardRate(user *md.User, opts map[string]*comm_plan.PlanOpt) string {
+ opt, ok := opts[md.PVD_CREDIT_CARD]
+ if opts == nil || !ok {
+ // 方案为空写定45
+ return "45"
+ }
+ userRate, ok1 := opt.UserRate[user.Info.Level]
+ if !ok1 {
+ return "35"
+ }
+ return zhios_order_relate_utils.AnyToString(((1 - opt.SysRate - opt.PvdRate) * userRate.SelfRate) * 100)
+}
+
+func GetVirtualCoinMoneyRateList(eg *xorm.Engine, masterId string) (map[int]string, error) {
+ m, err := db.VirtualCoinListInUse(eg, masterId)
+ if err != nil {
+ _ = zhios_order_relate_logx.Error(err)
+ return nil, err
+ }
+ if len(m) == 0 {
+ return nil, nil
+ }
+
+ result := make(map[int]string, 0)
+
+ result[0] = "1" // 把现金也添加进去 比例1:1
+
+ for _, coin := range m {
+ result[coin.Id] = coin.ExchangeRatio
+ }
+
+ return result, nil
+}
diff --git a/rule/relate_commission.go b/rule/relate_commission.go
new file mode 100644
index 0000000..f9baeba
--- /dev/null
+++ b/rule/relate_commission.go
@@ -0,0 +1,58 @@
+package rule
+
+import (
+ "code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/lib/comm_plan"
+ "code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/md"
+ "context"
+ "errors"
+ "golang.org/x/sync/errgroup"
+ "sync"
+ "xorm.io/xorm"
+)
+
+// BatchGetCommission 批量分佣
+func BatchGetCommission(eg *xorm.Engine, dbName string, commissionParamList []*md.CommissionFirstParam) (map[string]*comm_plan.LvUser, error) {
+ var (
+ isShare = false
+ isAllLevelReturn = false
+ )
+ goodsId2lvUser := make(map[string]*comm_plan.LvUser, len(commissionParamList))
+ if len(commissionParamList) == 0 {
+ return goodsId2lvUser, errors.New("参数错误")
+ }
+
+ group, _ := errgroup.WithContext(context.Background())
+ var mu sync.Mutex
+
+ for _, param := range commissionParamList {
+ param := param // 为下面的闭包创建局部变量
+ group.Go(func() error {
+ if param.IsShare != 0 {
+ isShare = true
+ }
+
+ if param.IsAllLevelReturn != 0 {
+ isAllLevelReturn = true
+ }
+
+ if param.GoodsId == "" {
+ return errors.New("商品ID缺失")
+ }
+
+ _, _, _, _, lvUser, err := GetRewardCommission(eg, ¶m.CommissionParam, isShare, param.Uid, param.Provider, dbName, isAllLevelReturn, map[string]string{})
+ if err != nil {
+ return err
+ }
+ mu.Lock()
+ goodsId2lvUser[param.GoodsId] = lvUser
+ mu.Unlock()
+
+ return nil
+ })
+ }
+ if err := group.Wait(); err != nil {
+ return goodsId2lvUser, errors.New("处理错误")
+ }
+
+ return goodsId2lvUser, nil
+}
diff --git a/rule/reward_commission.go b/rule/reward_commission.go
new file mode 100644
index 0000000..1ebf3fd
--- /dev/null
+++ b/rule/reward_commission.go
@@ -0,0 +1,401 @@
+package rule
+
+import (
+ "code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/db"
+ "code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/lib/comm_plan"
+ "code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/md"
+ zhios_order_relate_utils "code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/utils"
+ "errors"
+ "fmt"
+ "strings"
+ "xorm.io/xorm"
+)
+
+// getRewardCommission is 获取制度后的佣金
+// 返回:单个佣金、层级佣金、错误
+func GetRewardCommission(engine *xorm.Engine, rmd *md.CommissionParam, isShare bool, userId, provider, masterId string, returnAllLevel bool, extraData map[string]string) (float64, float64, float64, float64, *comm_plan.LvUser, error) {
+ virCfg := db.SysCfgFindWithDb(engine, masterId, "virtual_coin_rebate_type")
+ var virType = "price"
+ if virCfg["virtual_coin_rebate_type"] != "" {
+ virType = virCfg["virtual_coin_rebate_type"]
+ }
+ if virType == "commission" {
+ rmd.GoodsPrice = rmd.Commission
+ }
+ var (
+ err error
+ uid = 0
+ level = 0
+ newLevel = 0
+ )
+ user, _ := db.UserFindByID(engine, userId)
+ if user != nil {
+ uid = user.Uid
+ level = user.Level
+ newLevel = user.Level
+ }
+ // 获取抽成方案
+ newProvider := provider
+ if newProvider == md.PVD_TM { //抽成方案只有淘宝的要替换回来
+ newProvider = md.PVD_TB
+ }
+ if newProvider == "userlvup" {
+ newProvider = "user_level_up"
+ }
+ if newProvider == md.PVD_MALL_GOODS_USER_LV { //升级礼包按升级前等级计算佣金
+ level = zhios_order_relate_utils.StrToInt(extraData["old_lv"])
+ newLevel = zhios_order_relate_utils.StrToInt(extraData["new_lv"])
+ }
+ if extraData["show_level"] != "" {
+ level = zhios_order_relate_utils.StrToInt(extraData["show_level"])
+ }
+ ownbuyReturnType := zhios_order_relate_utils.StrToInt(extraData["ownbuy_return_type"])
+ cfg, err := GetPlanCfg(engine, newProvider, masterId)
+ if err != nil {
+ return 0, 0, 0, 0, nil, err
+ }
+ if cfg == nil {
+ return 0, 0, 0, 0, nil, errors.New("分佣方案未设置")
+ }
+
+ var userRelationship *[]md.UserRelation
+ if returnAllLevel {
+ userRelationship, err = UserRelativeNetwork(engine, uid)
+ } else {
+ userRelationship = nil
+ }
+ // 获取全部佣金
+ com, price := getCommission(rmd, provider)
+ fmt.Println(com)
+ fmt.Println(price)
+ comf := zhios_order_relate_utils.StrToFloat64(com)
+ if zhios_order_relate_utils.InArr(cfg.Mode, []string{"lv_price", "lv_winery"}) && zhios_order_relate_utils.StrToFloat64(rmd.OldPrice) > 0 { //价格为基数
+ comf = zhios_order_relate_utils.StrToFloat64(rmd.OldPrice)
+ }
+ // userRelationship == nil 是只返回第一层 即用户自己的
+ pvdFee, sysFee, subsidyFee, ulink, err := CalcCommission(uid, level, 0, ownbuyReturnType, comf, zhios_order_relate_utils.StrToFloat64(price), isShare, cfg, userRelationship, newProvider, newLevel, engine)
+ if err != nil {
+ return 0, 0, 0, 0, nil, nil
+ }
+ if ulink == nil {
+ return 0, 0, 0, 0, nil, nil
+ }
+ return ulink.Profit, pvdFee, sysFee, subsidyFee, ulink, nil
+}
+
+func getCommission(d *md.CommissionParam, provider string) (string, string) {
+
+ switch provider {
+ case md.PVD_TB, md.PVD_TM:
+ if zhios_order_relate_utils.StrToFloat64(d.GoodsPrice) == 0 {
+ //如果价格没有直接返回佣金
+ return d.Commission, d.PaidPrice
+ }
+ currentPrice := currentPrice(d, provider)
+ currentPriceF := zhios_order_relate_utils.StrToFloat64(currentPrice)
+ commissionRateF := zhios_order_relate_utils.StrToFloat64(d.CommissionRate) / 100
+ com := currentPriceF * commissionRateF
+ return zhios_order_relate_utils.Float64ToStr(com), currentPrice
+ case md.PVD_JD:
+ if zhios_order_relate_utils.StrToFloat64(d.GoodsPrice) == 0 {
+ //如果价格没有直接返回佣金
+ return d.Commission, d.PaidPrice
+ }
+ currentPrice := currentPrice(d, provider)
+ currentPriceF := zhios_order_relate_utils.StrToFloat64(currentPrice)
+ commissionRateF := zhios_order_relate_utils.StrToFloat64(d.CommissionRate) / 100
+ fmt.Println("当前劵后价格:", currentPriceF, "佣金比例:", commissionRateF)
+ com := currentPriceF * commissionRateF
+ return zhios_order_relate_utils.Float64ToStr(com), currentPrice
+ case md.PVD_PDD:
+ if zhios_order_relate_utils.StrToFloat64(d.GoodsPrice) == 0 {
+ //如果价格没有直接返回佣金
+ return d.Commission, d.PaidPrice
+ }
+ currentPrice := currentPrice(d, provider)
+ currentPriceF := zhios_order_relate_utils.StrToFloat64(currentPrice)
+ commissionRateF := zhios_order_relate_utils.StrToFloat64(d.Commission) / 100
+ com := currentPriceF * commissionRateF
+ return zhios_order_relate_utils.Float64ToStr(com), currentPrice
+ case md.PVD_VIP:
+ if zhios_order_relate_utils.StrToFloat64(d.GoodsPrice) == 0 {
+ //如果价格没有直接返回佣金
+ return d.Commission, d.PaidPrice
+ }
+ currentPrice := currentPrice(d, provider)
+ currentPriceF := zhios_order_relate_utils.StrToFloat64(currentPrice)
+ commissionRateF := zhios_order_relate_utils.StrToFloat64(d.CommissionRate) / 100
+ com := currentPriceF * commissionRateF
+ return zhios_order_relate_utils.Float64ToStr(com), currentPrice
+ case md.PVD_SN:
+ if zhios_order_relate_utils.StrToFloat64(d.GoodsPrice) == 0 {
+ //如果价格没有直接返回佣金
+ return d.Commission, d.PaidPrice
+ }
+ currentPrice := currentPrice(d, provider)
+ currentPriceF := zhios_order_relate_utils.StrToFloat64(currentPrice)
+ commissionRateF := zhios_order_relate_utils.StrToFloat64(d.CommissionRate) / 100
+ fmt.Println("当前劵后价格:", currentPriceF, "佣金比例:", commissionRateF)
+ com := currentPriceF * commissionRateF
+ return zhios_order_relate_utils.Float64ToStr(com), currentPrice
+ case md.PVD_KL:
+ if zhios_order_relate_utils.StrToFloat64(d.GoodsPrice) == 0 {
+ //如果价格没有直接返回佣金
+ return d.Commission, d.PaidPrice
+ }
+ currentPrice := currentPrice(d, provider)
+ currentPriceF := zhios_order_relate_utils.StrToFloat64(currentPrice)
+ commissionRateF := zhios_order_relate_utils.StrToFloat64(d.CommissionRate) // TODO 先不除于100
+ com := currentPriceF * commissionRateF
+ return zhios_order_relate_utils.Float64ToStr(com), currentPrice
+ default:
+ if zhios_order_relate_utils.StrToFloat64(d.GoodsPrice) == 0 {
+ //如果价格没有直接返回佣金
+ return d.Commission, d.Commission
+ }
+ return d.Commission, d.GoodsPrice // 直接传过来就是总佣金了
+ }
+}
+
+func currentPrice(m *md.CommissionParam, pvd string) string {
+ switch pvd {
+
+ case md.PVD_TB, md.PVD_TM:
+ if m.CouponPrice != "" {
+ price := zhios_order_relate_utils.StrToFloat64(m.WlGoodsPrice) - zhios_order_relate_utils.StrToFloat64(m.CouponPrice)
+ return zhios_order_relate_utils.Float64ToStr(price)
+ }
+ return m.WlGoodsPrice
+ case md.PVD_JD:
+ //fmt.Println(m.LowestCouponPrice, m.LowerPrice, m.WlGoodsPrice)
+ if m.LowestCouponPrice == "" {
+ if m.CouponPrice != "" {
+ price := zhios_order_relate_utils.StrToFloat64(m.WlGoodsPrice) - zhios_order_relate_utils.StrToFloat64(m.CouponPrice)
+ return zhios_order_relate_utils.Float64ToStr(price)
+ }
+ return m.LowerPrice
+ }
+ return m.LowestCouponPrice
+ case md.PVD_PDD:
+ if m.CouponPrice != "" {
+ price := zhios_order_relate_utils.StrToFloat64(m.MinGroupPrice) - zhios_order_relate_utils.StrToFloat64(m.CouponPrice)
+ return zhios_order_relate_utils.Float64ToStr(price)
+ }
+ return m.MinGroupPrice
+ case md.PVD_VIP:
+ if m.CouponPrice != "" {
+ price := zhios_order_relate_utils.StrToFloat64(m.GoodsPrice) - zhios_order_relate_utils.StrToFloat64(m.CouponPrice)
+ return zhios_order_relate_utils.Float64ToStr(price)
+ }
+ return m.GoodsPrice
+ case md.PVD_SN:
+ if m.CouponPrice != "" {
+ price := zhios_order_relate_utils.StrToFloat64(m.WlGoodsPrice) - zhios_order_relate_utils.StrToFloat64(m.CouponPrice)
+ if price < 0 {
+ return m.WlGoodsPrice
+ }
+ return zhios_order_relate_utils.Float64ToStr(price)
+ }
+ return m.WlGoodsPrice
+ case md.PVD_KL:
+ if m.CouponPrice != "" {
+ price := zhios_order_relate_utils.StrToFloat64(m.WlGoodsPrice) - zhios_order_relate_utils.StrToFloat64(m.CouponPrice)
+ return zhios_order_relate_utils.Float64ToStr(price)
+ }
+ return m.WlGoodsPrice
+ default:
+ return ""
+ }
+}
+func CommFee(fee float64, opt *comm_plan.PlanOpt, types string) (float64, float64, float64) {
+ if types == "integral" && opt.IntegralOpen == 0 || opt.Mode == "lv_winery" { //积分抽成后台没开启不用扣
+ return fee, 0, 0
+ }
+ pvdFee := fee * opt.PvdRate // 供应商联盟比例
+ sysFee := fee * opt.SysRate // 平台比例
+ regionFee := fee * opt.RegionRate // 区域代理比例
+ globalFee := fee * opt.GlobalRate // 全球分红比例
+ pushHandFee := fee * opt.PushHandRate
+ merchantFee := fee * opt.MerchantRate
+ // 剩余可分配的佣金
+ fee = float64(int64(fee*1e4)-int64(pvdFee*1e4)-int64(sysFee*1e4)-int64(regionFee*1e4)-int64(globalFee*1e4)-int64(pushHandFee*1e4)-int64(merchantFee*1e4)) / 1e4
+ return fee, pvdFee, sysFee
+}
+
+// 根据用户计算对应的手续费
+//ownbuyReturnType 0返利 1不返利 (免单商品是淘礼金商品的时候用的)
+//pvd 只是为了判断是会员升级的订单 可不传
+func CalcCommission(uid, level, oldDiff, ownbuyReturnType int, fee, integralFee float64, isShare bool, opt *comm_plan.PlanOpt, userRelationShip *[]md.UserRelation, pvd string, newLevel int, eg *xorm.Engine) (pvdFee, sysFee, subsidyFee float64, lvUser *comm_plan.LvUser, err error) {
+ //佣金扣除抽成后
+ fee, pvdFee, sysFee = CommFee(fee, opt, "commission")
+ //积分扣除抽成后
+ var integralSysFee float64 = 0
+ integralFee, _, integralSysFee = CommFee(integralFee, opt, "integral")
+ // 计算自购补贴比例
+ subsidyFee = 0
+ //如果没登录,要找出权重最低的那个
+ if uid == 0 {
+ lvList, err := db.UserLevelInIDescByWeightLow(eg)
+ if err != nil {
+ fmt.Println(err)
+ }
+ if lvList != nil {
+ for _, v := range lvList {
+ level = v.Id
+ }
+ }
+ }
+ // 判断当前用户等级是否有分佣设置
+ if _, ok := opt.UserRate[level]; !ok {
+ return 0, 0, 0, nil, errors.New("opt.UserRate[level] is nil uid=" + zhios_order_relate_utils.IntToStr(uid))
+ }
+
+ // 金额的补贴
+ subsidyFeeList := getOwnSubsidy(opt, level, isShare, fee, sysFee, integralFee)
+ subsidyFee1, ok := subsidyFeeList["0"]
+ if ok {
+ subsidyFee = subsidyFee1
+ }
+ // 获取用户的关系网
+ //userRelationShip, err := UserRelativeNetwork(eg, uid)
+ // 如果获取用户关系失败或者佣金方案不存在, 那么只计算当前用户的佣金, 平台佣金, 以及供应商方的佣金
+ if _, ok := comm_plan.Fn[opt.Mode]; !ok || userRelationShip == nil || len(*userRelationShip) == 0 {
+ // 获得自购的
+ commission, _, amountList, ratioList := comm_plan.CalReturnAmountAndRatio(level, ownbuyReturnType, 0, "own", fee, integralFee, opt)
+ if opt.Mode == "lv_winery" {
+ commission, _, amountList, ratioList = comm_plan.CalReturnAmountAndRatioToWinery(level, fee, integralFee, opt)
+ }
+ ratioListMap := convertList2Map(ratioList)
+ for k, v := range amountList {
+ amountList[k].Val = ratioListMap[v.Cid] * v.Val
+ }
+ //重新计算佣金
+ return pvdFee, sysFee, subsidyFee, &comm_plan.LvUser{OwnSubsidyFeeList: subsidyFeeList, Uid: uid, Lv: level, OldDiff: oldDiff, Profit: commission, ProfitList: amountList, Diff: 0}, nil
+ }
+
+ //查出所有等级替换权重
+ levelList, _ := db.UserLevlEgAll(eg)
+ levelWeight := 0
+ for _, v1 := range levelList {
+ if v1.Id == level {
+ levelWeight = v1.LevelWeight
+ }
+ }
+ lvUser = &comm_plan.LvUser{OwnSubsidyFeeList: subsidyFeeList, Uid: uid, Lv: level, Diff: 0, OldDiff: oldDiff, NewLv: newLevel, LevelWeight: levelWeight, OwnbuyReturnType: ownbuyReturnType}
+ var node = lvUser
+ //关系链处理
+ for _, v := range *userRelationShip {
+ for _, v1 := range levelList {
+ levelId := v.Level
+ if v1.Id == levelId {
+ levelWeight = v1.LevelWeight
+ }
+ }
+ node.ParentUser = &comm_plan.LvUser{Uid: v.Uid, Lv: v.Level, Diff: v.Diff, OldDiff: v.OldDiff, LevelWeight: levelWeight}
+ node = node.ParentUser
+ }
+ if err := comm_plan.Fn[opt.Mode](opt, fee, integralFee, lvUser, pvd, sysFee, integralSysFee); err != nil {
+ fmt.Println("方案有问题1")
+ return 0, 0, 0, nil, err
+ }
+ if opt.Mode == "lv_winery" {
+ commission, _, amountList, ratioList := comm_plan.CalReturnAmountAndRatioToWinery(level, fee, integralFee, opt)
+ lvUser.Profit = commission // 另外出来的佣金 兼容旧的
+ lvUser.ProfitList = amountList // 各币种分佣
+ lvUser.SubsidyFee = 0
+ ratioListMap := convertList2Map(ratioList)
+ for k, v := range lvUser.ProfitList {
+ lvUser.ProfitList[k].Val = ratioListMap[v.Cid] * v.Val
+ }
+ }
+ return pvdFee, sysFee, subsidyFee, lvUser, nil
+}
+func getOwnSubsidy(opt *comm_plan.PlanOpt, level int, isShare bool, fee, sysFee, integralFee float64) map[string]float64 {
+ var res = make(map[string]float64)
+ if opt.UserRate[level].SubsidyEnable != 1 {
+ return res
+ }
+ if len(opt.UserRate[level].SubsidyOwnBiliList) == 0 {
+ var tmp = map[string]string{
+ "coin_id": "0",
+ "self_bili": zhios_order_relate_utils.Float64ToStrByPrec(opt.UserRate[level].SubsidySelfRate*100, 6),
+ "share_bili": zhios_order_relate_utils.Float64ToStrByPrec(opt.UserRate[level].SubsidyShareRate*100, 6),
+ }
+ opt.UserRate[level].SubsidyOwnBiliList = append(opt.UserRate[level].SubsidyOwnBiliList, tmp)
+ }
+ for _, v := range opt.UserRate[level].SubsidyOwnBiliList {
+ var newFee = fee
+ if v["coin_id"] != "0" { //积分的
+ newFee = integralFee
+ }
+ if v["coin_id"] == "0" && opt.UserRate[level].SubsidyMode == 1 { // 按佣金计算补贴
+ newFee = sysFee
+ }
+ var subsidyFee float64 = 0
+ subsidyFee = newFee * (zhios_order_relate_utils.StrToFloat64(v["self_bili"]) / 100)
+ if isShare {
+ subsidyFee = newFee * (zhios_order_relate_utils.StrToFloat64(v["share_bili"]) / 100)
+ }
+ subsidyFee = zhios_order_relate_utils.FloatFormat(subsidyFee, 6)
+ res[v["coin_id"]] = subsidyFee
+ }
+ return res
+}
+
+func getBili(eg *xorm.Engine, types string) float64 {
+ //读取佣金与 区块币比例
+ biliList, _ := db.SysCfgGetOne(eg, types)
+ var bili float64 = 1
+ if biliList != nil && biliList.Val != "" {
+ list := strings.Split(biliList.Val, ":")
+ if len(list) == 2 {
+ bili = zhios_order_relate_utils.StrToFloat64(list[0]) / zhios_order_relate_utils.StrToFloat64(list[1])
+ bili = zhios_order_relate_utils.FloatFormat(bili, 6)
+ }
+ }
+ return bili
+}
+//
+////分佣记录写入
+//func OrderRelateInsert(eg *xorm.Engine, userId int, oid int64, pvd string, createTime int, CommissionParam *md.CommissionParam, masterId string) {
+// _, _, _, _, lvUser, _ := GetRewardCommission(eg, CommissionParam, false, zhios_order_relate_utils.IntToStr(userId), pvd, masterId, true, map[string]string{})
+// fmt.Println(lvUser)
+// if lvUser == nil {
+// return
+// }
+// level := 0
+// profit := zhios_order_relate_utils.FloatFormat(lvUser.Profit+lvUser.SubsidyFee, 6)
+// //更新订单表的用户佣金
+// if pvd == "regional_agent_pay" {
+// _, _ = db.RegionalAgentOrdUpdate(eg, map[string]interface{}{
+// "key": "ord_id",
+// "value": oid,
+// }, &model2.RegionalAgentUserOrd{UserCommission: zhios_order_relate_utils.Float64ToStr(profit)})
+// }
+// if pvd == "userlvup" {
+// _, _ = levelDb.UserLevelUpgradeOrdUpdate(eg, map[string]interface{}{
+// "key": "ord_id",
+// "value": oid,
+// }, &levelModel.UserLevelUpgradeOrd{UserCommission: zhios_order_relate_utils.Float64ToStr(profit)})
+// }
+// data := []*model.OrdListRelate{{Oid: oid, Uid: lvUser.Uid, Amount: profit, Pvd: pvd, CreateAt: createTime, Level: level}}
+// for lvUser.ParentUser != nil {
+// lvUser = lvUser.ParentUser
+// fmt.Println(lvUser)
+// level = level + 1
+// profit = zhios_order_relate_utils.FloatFormat(lvUser.Profit+lvUser.SubsidyFee, 6)
+// data = append(data, &model.OrdListRelate{Oid: oid, Uid: lvUser.Uid, Amount: profit, Pvd: pvd, CreateAt: createTime, Level: level})
+// }
+// err := commDb.DbInsertBatch(eg, data)
+// if err != nil {
+// return
+// }
+// return
+//}
+func convertList2Map(a []*comm_plan.VirtualCoinCommission) (b map[string]float64) {
+ b = make(map[string]float64)
+ for _, i := range a {
+ b[i.Cid] = i.Val
+ }
+ return b
+}
diff --git a/rule/user_relative_network.go b/rule/user_relative_network.go
new file mode 100644
index 0000000..0604034
--- /dev/null
+++ b/rule/user_relative_network.go
@@ -0,0 +1,46 @@
+package rule
+
+import (
+ "code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/db"
+ "code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/md"
+ "xorm.io/xorm"
+)
+
+// 获取用户关系等级, 如果返回nil则没有上级用户
+func UserRelativeNetwork(eg *xorm.Engine, uid int) (*[]md.UserRelation, error) {
+ parent, err := db.DbsUserRelate(eg, uid)
+ if err != nil || parent == nil {
+ return nil, err
+ }
+ var uids []int
+ var userRelation []md.UserRelation
+ for _, v := range *parent {
+ uids = append(uids, v.ParentUid)
+ userRelation = append(userRelation, md.UserRelation{
+ Uid: v.ParentUid,
+ CurUid: uid,
+ Diff: v.Level,
+ Level: 0,
+ })
+ }
+ relateUsers, err := db.DbsUserFindByIds(eg, uids)
+ if err != nil {
+ return nil, err
+ }
+ if relateUsers == nil {
+ return nil, nil
+ }
+ tmp := map[int]int{}
+ for _, v := range *relateUsers {
+ tmp[v.Uid] = v.Level
+ }
+ for k, v := range userRelation {
+ if val, ok := tmp[v.Uid]; ok {
+ userRelation[k].Level = val
+ }
+ }
+ if len(userRelation) == 0 {
+ return nil, nil
+ }
+ return &userRelation, nil
+}
diff --git a/utils/cache/base.go b/utils/cache/base.go
new file mode 100644
index 0000000..64648dd
--- /dev/null
+++ b/utils/cache/base.go
@@ -0,0 +1,421 @@
+package cache
+
+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/utils/cache/redis.go b/utils/cache/redis.go
new file mode 100644
index 0000000..4e5f047
--- /dev/null
+++ b/utils/cache/redis.go
@@ -0,0 +1,403 @@
+package cache
+
+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 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
+ }
+ 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/utils/cache/redis_cluster.go b/utils/cache/redis_cluster.go
new file mode 100644
index 0000000..901f30c
--- /dev/null
+++ b/utils/cache/redis_cluster.go
@@ -0,0 +1,622 @@
+package cache
+
+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/utils/cache/redis_pool.go b/utils/cache/redis_pool.go
new file mode 100644
index 0000000..ca38b3f
--- /dev/null
+++ b/utils/cache/redis_pool.go
@@ -0,0 +1,324 @@
+package cache
+
+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/utils/cache/redis_pool_cluster.go b/utils/cache/redis_pool_cluster.go
new file mode 100644
index 0000000..cd1911b
--- /dev/null
+++ b/utils/cache/redis_pool_cluster.go
@@ -0,0 +1,617 @@
+package cache
+
+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/utils/convert.go b/utils/convert.go
new file mode 100644
index 0000000..44a9bb5
--- /dev/null
+++ b/utils/convert.go
@@ -0,0 +1,366 @@
+package zhios_order_relate_utils
+
+import (
+ "encoding/binary"
+ "encoding/json"
+ "fmt"
+ "math"
+ "strconv"
+ "strings"
+)
+
+func ToString(raw interface{}, e error) (res string) {
+ if e != nil {
+ return ""
+ }
+ return AnyToString(raw)
+}
+
+func ToInt64(raw interface{}, e error) int64 {
+ if e != nil {
+ return 0
+ }
+ return AnyToInt64(raw)
+}
+
+func AnyToBool(raw interface{}) bool {
+ switch i := raw.(type) {
+ case float32, float64, int, int64, uint, uint8, uint16, uint32, uint64, int8, int16, int32:
+ return i != 0
+ case []byte:
+ return i != nil
+ case string:
+ if i == "false" {
+ return false
+ }
+ return i != ""
+ case error:
+ return false
+ case nil:
+ return true
+ }
+ val := fmt.Sprint(raw)
+ val = strings.TrimLeft(val, "&")
+ if strings.TrimLeft(val, "{}") == "" {
+ return false
+ }
+ if strings.TrimLeft(val, "[]") == "" {
+ return false
+ }
+ // ptr type
+ b, err := json.Marshal(raw)
+ if err != nil {
+ return false
+ }
+ if strings.TrimLeft(string(b), "\"\"") == "" {
+ return false
+ }
+ if strings.TrimLeft(string(b), "{}") == "" {
+ return false
+ }
+ return true
+}
+
+func AnyToInt64(raw interface{}) int64 {
+ switch i := raw.(type) {
+ case string:
+ res, _ := strconv.ParseInt(i, 10, 64)
+ return res
+ case []byte:
+ return BytesToInt64(i)
+ case int:
+ return int64(i)
+ case int64:
+ return i
+ case uint:
+ return int64(i)
+ case uint8:
+ return int64(i)
+ case uint16:
+ return int64(i)
+ case uint32:
+ return int64(i)
+ case uint64:
+ return int64(i)
+ case int8:
+ return int64(i)
+ case int16:
+ return int64(i)
+ case int32:
+ return int64(i)
+ case float32:
+ return int64(i)
+ case float64:
+ return int64(i)
+ case error:
+ return 0
+ case bool:
+ if i {
+ return 1
+ }
+ return 0
+ }
+ return 0
+}
+
+func AnyToString(raw interface{}) string {
+ switch i := raw.(type) {
+ case []byte:
+ return string(i)
+ case int:
+ return strconv.FormatInt(int64(i), 10)
+ case int64:
+ return strconv.FormatInt(i, 10)
+ case float32:
+ return Float64ToStr(float64(i))
+ case float64:
+ return Float64ToStr(i)
+ case uint:
+ return strconv.FormatInt(int64(i), 10)
+ case uint8:
+ return strconv.FormatInt(int64(i), 10)
+ case uint16:
+ return strconv.FormatInt(int64(i), 10)
+ case uint32:
+ return strconv.FormatInt(int64(i), 10)
+ case uint64:
+ return strconv.FormatInt(int64(i), 10)
+ case int8:
+ return strconv.FormatInt(int64(i), 10)
+ case int16:
+ return strconv.FormatInt(int64(i), 10)
+ case int32:
+ return strconv.FormatInt(int64(i), 10)
+ case string:
+ return i
+ case error:
+ return i.Error()
+ case bool:
+ return strconv.FormatBool(i)
+ }
+ return fmt.Sprintf("%#v", raw)
+}
+
+func AnyToFloat64(raw interface{}) float64 {
+ switch i := raw.(type) {
+ case []byte:
+ f, _ := strconv.ParseFloat(string(i), 64)
+ return f
+ case int:
+ return float64(i)
+ case int64:
+ return float64(i)
+ case float32:
+ return float64(i)
+ case float64:
+ return i
+ case uint:
+ return float64(i)
+ case uint8:
+ return float64(i)
+ case uint16:
+ return float64(i)
+ case uint32:
+ return float64(i)
+ case uint64:
+ return float64(i)
+ case int8:
+ return float64(i)
+ case int16:
+ return float64(i)
+ case int32:
+ return float64(i)
+ case string:
+ f, _ := strconv.ParseFloat(i, 64)
+ return f
+ case bool:
+ if i {
+ return 1
+ }
+ }
+ return 0
+}
+
+func ToByte(raw interface{}, e error) []byte {
+ if e != nil {
+ return []byte{}
+ }
+ switch i := raw.(type) {
+ case string:
+ return []byte(i)
+ case int:
+ return Int64ToBytes(int64(i))
+ case int64:
+ return Int64ToBytes(i)
+ case float32:
+ return Float32ToByte(i)
+ case float64:
+ return Float64ToByte(i)
+ case uint:
+ return Int64ToBytes(int64(i))
+ case uint8:
+ return Int64ToBytes(int64(i))
+ case uint16:
+ return Int64ToBytes(int64(i))
+ case uint32:
+ return Int64ToBytes(int64(i))
+ case uint64:
+ return Int64ToBytes(int64(i))
+ case int8:
+ return Int64ToBytes(int64(i))
+ case int16:
+ return Int64ToBytes(int64(i))
+ case int32:
+ return Int64ToBytes(int64(i))
+ case []byte:
+ return i
+ case error:
+ return []byte(i.Error())
+ case bool:
+ if i {
+ return []byte("true")
+ }
+ return []byte("false")
+ }
+ return []byte(fmt.Sprintf("%#v", raw))
+}
+
+func Int64ToBytes(i int64) []byte {
+ var buf = make([]byte, 8)
+ binary.BigEndian.PutUint64(buf, uint64(i))
+ return buf
+}
+
+func BytesToInt64(buf []byte) int64 {
+ return int64(binary.BigEndian.Uint64(buf))
+}
+
+func StrToInt(s string) int {
+ res, _ := strconv.Atoi(s)
+ return res
+}
+
+func StrToInt64(s string) int64 {
+ res, _ := strconv.ParseInt(s, 10, 64)
+ return res
+}
+
+func Float32ToByte(float float32) []byte {
+ bits := math.Float32bits(float)
+ bytes := make([]byte, 4)
+ binary.LittleEndian.PutUint32(bytes, bits)
+
+ return bytes
+}
+
+func ByteToFloat32(bytes []byte) float32 {
+ bits := binary.LittleEndian.Uint32(bytes)
+ return math.Float32frombits(bits)
+}
+
+func Float64ToByte(float float64) []byte {
+ bits := math.Float64bits(float)
+ bytes := make([]byte, 8)
+ binary.LittleEndian.PutUint64(bytes, bits)
+ return bytes
+}
+
+func ByteToFloat64(bytes []byte) float64 {
+ bits := binary.LittleEndian.Uint64(bytes)
+ return math.Float64frombits(bits)
+}
+
+func Float64ToStr(f float64) string {
+ return strconv.FormatFloat(f, 'f', 2, 64)
+}
+func Float64ToStrPrec1(f float64) string {
+ return strconv.FormatFloat(f, 'f', 1, 64)
+}
+func Float64ToStrByPrec(f float64, prec int) string {
+ return strconv.FormatFloat(f, 'f', prec, 64)
+}
+
+func Float32ToStr(f float32) string {
+ return Float64ToStr(float64(f))
+}
+
+func StrToFloat64(s string) float64 {
+ res, err := strconv.ParseFloat(s, 64)
+ if err != nil {
+ return 0
+ }
+ return res
+}
+func StrToFormat(s string, prec int) string {
+ ex := strings.Split(s, ".")
+ if len(ex) == 2 {
+ if StrToFloat64(ex[1]) == 0 { //小数点后面为空就是不要小数点了
+ return ex[0]
+ }
+ //看取多少位
+ str := ex[1]
+ str1 := str
+ if prec < len(str) {
+ str1 = str[0:prec]
+ } else {
+ for i := 0; i < prec-len(str); i++ {
+ str1 += "0"
+ }
+ }
+ if prec > 0 {
+ return ex[0] + "." + str1
+ } else {
+ return ex[0]
+ }
+ }
+ return s
+}
+
+func StrToFloat32(s string) float32 {
+ res, err := strconv.ParseFloat(s, 32)
+ if err != nil {
+ return 0
+ }
+ return float32(res)
+}
+
+func StrToBool(s string) bool {
+ b, _ := strconv.ParseBool(s)
+ return b
+}
+
+func BoolToStr(b bool) string {
+ if b {
+ return "true"
+ }
+ return "false"
+}
+
+func FloatToInt64(f float64) int64 {
+ return int64(f)
+}
+
+func IntToStr(i int) string {
+ return strconv.Itoa(i)
+}
+
+func Int64ToStr(i int64) string {
+ return strconv.FormatInt(i, 10)
+}
+
+func IntToFloat64(i int) float64 {
+ s := strconv.Itoa(i)
+ res, err := strconv.ParseFloat(s, 64)
+ if err != nil {
+ return 0
+ }
+ return res
+}
+func Int64ToFloat64(i int64) float64 {
+ s := strconv.FormatInt(i, 10)
+ res, err := strconv.ParseFloat(s, 64)
+ if err != nil {
+ return 0
+ }
+ return res
+}
diff --git a/utils/curl.go b/utils/curl.go
new file mode 100644
index 0000000..cfaffde
--- /dev/null
+++ b/utils/curl.go
@@ -0,0 +1,209 @@
+package zhios_order_relate_utils
+
+import (
+ "bytes"
+ "crypto/tls"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "net/http"
+ "net/url"
+ "sort"
+ "strings"
+ "time"
+)
+
+var CurlDebug bool
+
+func CurlGet(router string, header map[string]string) ([]byte, error) {
+ return curl(http.MethodGet, router, nil, header)
+}
+func CurlGetJson(router string, body interface{}, header map[string]string) ([]byte, error) {
+ return curl_new(http.MethodGet, router, body, header)
+}
+
+// 只支持form 与json 提交, 请留意body的类型, 支持string, []byte, map[string]string
+func CurlPost(router string, body interface{}, header map[string]string) ([]byte, error) {
+ return curl(http.MethodPost, router, body, header)
+}
+
+func CurlPut(router string, body interface{}, header map[string]string) ([]byte, error) {
+ return curl(http.MethodPut, router, body, header)
+}
+
+// 只支持form 与json 提交, 请留意body的类型, 支持string, []byte, map[string]string
+func CurlPatch(router string, body interface{}, header map[string]string) ([]byte, error) {
+ return curl(http.MethodPatch, router, body, header)
+}
+
+// CurlDelete is curl delete
+func CurlDelete(router string, body interface{}, header map[string]string) ([]byte, error) {
+ return curl(http.MethodDelete, router, body, header)
+}
+
+func curl(method, router string, body interface{}, header map[string]string) ([]byte, error) {
+ var reqBody io.Reader
+ contentType := "application/json"
+ switch v := body.(type) {
+ case string:
+ reqBody = strings.NewReader(v)
+ case []byte:
+ reqBody = bytes.NewReader(v)
+ case map[string]string:
+ val := url.Values{}
+ for k, v := range v {
+ val.Set(k, v)
+ }
+ reqBody = strings.NewReader(val.Encode())
+ contentType = "application/x-www-form-urlencoded"
+ case map[string]interface{}:
+ val := url.Values{}
+ for k, v := range v {
+ val.Set(k, v.(string))
+ }
+ reqBody = strings.NewReader(val.Encode())
+ contentType = "application/x-www-form-urlencoded"
+ }
+ if header == nil {
+ header = map[string]string{"Content-Type": contentType}
+ }
+ if _, ok := header["Content-Type"]; !ok {
+ header["Content-Type"] = contentType
+ }
+ resp, er := CurlReq(method, router, reqBody, header)
+ if er != nil {
+ return nil, er
+ }
+ res, err := ioutil.ReadAll(resp.Body)
+ if CurlDebug {
+ blob := SerializeStr(body)
+ if contentType != "application/json" {
+ blob = HttpBuild(body)
+ }
+ fmt.Printf("\n\n=====================\n[url]: %s\n[time]: %s\n[method]: %s\n[content-type]: %v\n[req_header]: %s\n[req_body]: %#v\n[resp_err]: %v\n[resp_header]: %v\n[resp_body]: %v\n=====================\n\n",
+ router,
+ time.Now().Format("2006-01-02 15:04:05.000"),
+ method,
+ contentType,
+ HttpBuildQuery(header),
+ blob,
+ err,
+ SerializeStr(resp.Header),
+ string(res),
+ )
+ }
+ resp.Body.Close()
+ return res, err
+}
+
+func curl_new(method, router string, body interface{}, header map[string]string) ([]byte, error) {
+ var reqBody io.Reader
+ contentType := "application/json"
+
+ if header == nil {
+ header = map[string]string{"Content-Type": contentType}
+ }
+ if _, ok := header["Content-Type"]; !ok {
+ header["Content-Type"] = contentType
+ }
+ resp, er := CurlReq(method, router, reqBody, header)
+ if er != nil {
+ return nil, er
+ }
+ res, err := ioutil.ReadAll(resp.Body)
+ if CurlDebug {
+ blob := SerializeStr(body)
+ if contentType != "application/json" {
+ blob = HttpBuild(body)
+ }
+ fmt.Printf("\n\n=====================\n[url]: %s\n[time]: %s\n[method]: %s\n[content-type]: %v\n[req_header]: %s\n[req_body]: %#v\n[resp_err]: %v\n[resp_header]: %v\n[resp_body]: %v\n=====================\n\n",
+ router,
+ time.Now().Format("2006-01-02 15:04:05.000"),
+ method,
+ contentType,
+ HttpBuildQuery(header),
+ blob,
+ err,
+ SerializeStr(resp.Header),
+ string(res),
+ )
+ }
+ resp.Body.Close()
+ return res, err
+}
+
+func CurlReq(method, router string, reqBody io.Reader, header map[string]string) (*http.Response, error) {
+ req, _ := http.NewRequest(method, router, reqBody)
+ if header != nil {
+ for k, v := range header {
+ req.Header.Set(k, v)
+ }
+ }
+ // 绕过github等可能因为特征码返回503问题
+ // https://www.imwzk.com/posts/2021-03-14-why-i-always-get-503-with-golang/
+ defaultCipherSuites := []uint16{0xc02f, 0xc030, 0xc02b, 0xc02c, 0xcca8, 0xcca9, 0xc013, 0xc009,
+ 0xc014, 0xc00a, 0x009c, 0x009d, 0x002f, 0x0035, 0xc012, 0x000a}
+ client := &http.Client{
+ Transport: &http.Transport{
+ TLSClientConfig: &tls.Config{
+ InsecureSkipVerify: true,
+ CipherSuites: append(defaultCipherSuites[8:], defaultCipherSuites[:8]...),
+ },
+ },
+ // 获取301重定向
+ CheckRedirect: func(req *http.Request, via []*http.Request) error {
+ return http.ErrUseLastResponse
+ },
+ }
+ return client.Do(req)
+}
+
+// 组建get请求参数,sortAsc true为小到大,false为大到小,nil不排序 a=123&b=321
+func HttpBuildQuery(args map[string]string, sortAsc ...bool) string {
+ str := ""
+ if len(args) == 0 {
+ return str
+ }
+ if len(sortAsc) > 0 {
+ keys := make([]string, 0, len(args))
+ for k := range args {
+ keys = append(keys, k)
+ }
+ if sortAsc[0] {
+ sort.Strings(keys)
+ } else {
+ sort.Sort(sort.Reverse(sort.StringSlice(keys)))
+ }
+ for _, k := range keys {
+ str += "&" + k + "=" + args[k]
+ }
+ } else {
+ for k, v := range args {
+ str += "&" + k + "=" + v
+ }
+ }
+ return str[1:]
+}
+
+func HttpBuild(body interface{}, sortAsc ...bool) string {
+ params := map[string]string{}
+ if args, ok := body.(map[string]interface{}); ok {
+ for k, v := range args {
+ params[k] = AnyToString(v)
+ }
+ return HttpBuildQuery(params, sortAsc...)
+ }
+ if args, ok := body.(map[string]string); ok {
+ for k, v := range args {
+ params[k] = AnyToString(v)
+ }
+ return HttpBuildQuery(params, sortAsc...)
+ }
+ if args, ok := body.(map[string]int); ok {
+ for k, v := range args {
+ params[k] = AnyToString(v)
+ }
+ return HttpBuildQuery(params, sortAsc...)
+ }
+ return AnyToString(body)
+}
diff --git a/utils/file.go b/utils/file.go
new file mode 100644
index 0000000..f5b4c92
--- /dev/null
+++ b/utils/file.go
@@ -0,0 +1,22 @@
+package zhios_order_relate_utils
+
+import (
+ "os"
+ "path"
+ "strings"
+ "time"
+)
+
+// 获取文件后缀
+func FileExt(fname string) string {
+ return strings.ToLower(strings.TrimLeft(path.Ext(fname), "."))
+}
+
+func FilePutContents(fileName string, content string) {
+ fd, _ := os.OpenFile("./tmp/"+fileName+".logs", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)
+ fd_time := time.Now().Format("2006-01-02 15:04:05")
+ fd_content := strings.Join([]string{"[", fd_time, "] ", content, "\n"}, "")
+ buf := []byte(fd_content)
+ fd.Write(buf)
+ fd.Close()
+}
diff --git a/utils/format.go b/utils/format.go
new file mode 100644
index 0000000..dbe8d4f
--- /dev/null
+++ b/utils/format.go
@@ -0,0 +1,59 @@
+package zhios_order_relate_utils
+
+import (
+ "math"
+)
+
+func CouponFormat(data string) string {
+ switch data {
+ case "0.00", "0", "":
+ return ""
+ default:
+ return Int64ToStr(FloatToInt64(StrToFloat64(data)))
+ }
+}
+func CommissionFormat(data string) string {
+ if data != "" && data != "0" {
+ return data
+ }
+
+ return ""
+}
+
+func HideString(src string, hLen int) string {
+ str := []rune(src)
+ if hLen == 0 {
+ hLen = 4
+ }
+ hideStr := ""
+ for i := 0; i < hLen; i++ {
+ hideStr += "*"
+ }
+ hideLen := len(str) / 2
+ showLen := len(str) - hideLen
+ if hideLen == 0 || showLen == 0 {
+ return hideStr
+ }
+ subLen := showLen / 2
+ if subLen == 0 {
+ return string(str[:showLen]) + hideStr
+ }
+ s := string(str[:subLen])
+ s += hideStr
+ s += string(str[len(str)-subLen:])
+ return s
+}
+
+//SaleCountFormat is 格式化销量
+func SaleCountFormat(s string) string {
+ return s + "已售"
+}
+
+// 小数格式化
+func FloatFormat(f float64, i int) float64 {
+ if i > 14 {
+ return f
+ }
+ p := math.Pow10(i)
+ return float64(int64((f+0.000000000000009)*p)) / p
+}
diff --git a/utils/logx/log.go b/utils/logx/log.go
new file mode 100644
index 0000000..586e4ab
--- /dev/null
+++ b/utils/logx/log.go
@@ -0,0 +1,245 @@
+package zhios_order_relate_logx
+
+import (
+ "os"
+ "strings"
+ "time"
+
+ "go.uber.org/zap"
+ "go.uber.org/zap/zapcore"
+)
+
+type LogConfig struct {
+ AppName string `yaml:"app_name" json:"app_name" toml:"app_name"`
+ Level string `yaml:"level" json:"level" toml:"level"`
+ StacktraceLevel string `yaml:"stacktrace_level" json:"stacktrace_level" toml:"stacktrace_level"`
+ IsStdOut bool `yaml:"is_stdout" json:"is_stdout" toml:"is_stdout"`
+ TimeFormat string `yaml:"time_format" json:"time_format" toml:"time_format"` // second, milli, nano, standard, iso,
+ Encoding string `yaml:"encoding" json:"encoding" toml:"encoding"` // console, json
+ Skip int `yaml:"skip" json:"skip" toml:"skip"`
+
+ IsFileOut bool `yaml:"is_file_out" json:"is_file_out" toml:"is_file_out"`
+ FileDir string `yaml:"file_dir" json:"file_dir" toml:"file_dir"`
+ FileName string `yaml:"file_name" json:"file_name" toml:"file_name"`
+ FileMaxSize int `yaml:"file_max_size" json:"file_max_size" toml:"file_max_size"`
+ FileMaxAge int `yaml:"file_max_age" json:"file_max_age" toml:"file_max_age"`
+}
+
+var (
+ l *LogX = defaultLogger()
+ conf *LogConfig
+)
+
+// default logger setting
+func defaultLogger() *LogX {
+ conf = &LogConfig{
+ Level: "debug",
+ StacktraceLevel: "error",
+ IsStdOut: true,
+ TimeFormat: "standard",
+ Encoding: "console",
+ Skip: 2,
+ }
+ writers := []zapcore.WriteSyncer{os.Stdout}
+ lg, lv := newZapLogger(setLogLevel(conf.Level), setLogLevel(conf.StacktraceLevel), conf.Encoding, conf.TimeFormat, conf.Skip, zapcore.NewMultiWriteSyncer(writers...))
+ zap.RedirectStdLog(lg)
+ return &LogX{logger: lg, atomLevel: lv}
+}
+
+// initial standard log, if you don't init, it will use default logger setting
+func InitDefaultLogger(cfg *LogConfig) {
+ var writers []zapcore.WriteSyncer
+ if cfg.IsStdOut || (!cfg.IsStdOut && !cfg.IsFileOut) {
+ writers = append(writers, os.Stdout)
+ }
+ if cfg.IsFileOut {
+ writers = append(writers, NewRollingFile(cfg.FileDir, cfg.FileName, cfg.FileMaxSize, cfg.FileMaxAge))
+ }
+
+ lg, lv := newZapLogger(setLogLevel(cfg.Level), setLogLevel(cfg.StacktraceLevel), cfg.Encoding, cfg.TimeFormat, cfg.Skip, zapcore.NewMultiWriteSyncer(writers...))
+ zap.RedirectStdLog(lg)
+ if cfg.AppName != "" {
+ lg = lg.With(zap.String("app", cfg.AppName)) // 加上应用名称
+ }
+ l = &LogX{logger: lg, atomLevel: lv}
+}
+
+// create a new logger
+func NewLogger(cfg *LogConfig) *LogX {
+ var writers []zapcore.WriteSyncer
+ if cfg.IsStdOut || (!cfg.IsStdOut && !cfg.IsFileOut) {
+ writers = append(writers, os.Stdout)
+ }
+ if cfg.IsFileOut {
+ writers = append(writers, NewRollingFile(cfg.FileDir, cfg.FileName, cfg.FileMaxSize, cfg.FileMaxAge))
+ }
+
+ lg, lv := newZapLogger(setLogLevel(cfg.Level), setLogLevel(cfg.StacktraceLevel), cfg.Encoding, cfg.TimeFormat, cfg.Skip, zapcore.NewMultiWriteSyncer(writers...))
+ zap.RedirectStdLog(lg)
+ if cfg.AppName != "" {
+ lg = lg.With(zap.String("app", cfg.AppName)) // 加上应用名称
+ }
+ return &LogX{logger: lg, atomLevel: lv}
+}
+
+// create a new zaplog logger
+func newZapLogger(level, stacktrace zapcore.Level, encoding, timeType string, skip int, output zapcore.WriteSyncer) (*zap.Logger, *zap.AtomicLevel) {
+ encCfg := zapcore.EncoderConfig{
+ TimeKey: "T",
+ LevelKey: "L",
+ NameKey: "N",
+ CallerKey: "C",
+ MessageKey: "M",
+ StacktraceKey: "S",
+ LineEnding: zapcore.DefaultLineEnding,
+ EncodeCaller: zapcore.ShortCallerEncoder,
+ EncodeDuration: zapcore.NanosDurationEncoder,
+ EncodeLevel: zapcore.LowercaseLevelEncoder,
+ }
+ setTimeFormat(timeType, &encCfg) // set time type
+ atmLvl := zap.NewAtomicLevel() // set level
+ atmLvl.SetLevel(level)
+ encoder := zapcore.NewJSONEncoder(encCfg) // 确定encoder格式
+ if encoding == "console" {
+ encoder = zapcore.NewConsoleEncoder(encCfg)
+ }
+ return zap.New(zapcore.NewCore(encoder, output, atmLvl), zap.AddCaller(), zap.AddStacktrace(stacktrace), zap.AddCallerSkip(skip)), &atmLvl
+}
+
+// set log level
+func setLogLevel(lvl string) zapcore.Level {
+ switch strings.ToLower(lvl) {
+ case "panic":
+ return zapcore.PanicLevel
+ case "fatal":
+ return zapcore.FatalLevel
+ case "error":
+ return zapcore.ErrorLevel
+ case "warn", "warning":
+ return zapcore.WarnLevel
+ case "info":
+ return zapcore.InfoLevel
+ default:
+ return zapcore.DebugLevel
+ }
+}
+
+// set time format
+func setTimeFormat(timeType string, z *zapcore.EncoderConfig) {
+ switch strings.ToLower(timeType) {
+ case "iso": // iso8601 standard
+ z.EncodeTime = zapcore.ISO8601TimeEncoder
+ case "sec": // only for unix second, without millisecond
+ z.EncodeTime = func(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
+ enc.AppendInt64(t.Unix())
+ }
+ case "second": // unix second, with millisecond
+ z.EncodeTime = zapcore.EpochTimeEncoder
+ case "milli", "millisecond": // millisecond
+ z.EncodeTime = zapcore.EpochMillisTimeEncoder
+ case "nano", "nanosecond": // nanosecond
+ z.EncodeTime = zapcore.EpochNanosTimeEncoder
+ default: // standard format
+ z.EncodeTime = func(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
+ enc.AppendString(t.Format("2006-01-02 15:04:05.000"))
+ }
+ }
+}
+
+func GetLevel() string {
+ switch l.atomLevel.Level() {
+ case zapcore.PanicLevel:
+ return "panic"
+ case zapcore.FatalLevel:
+ return "fatal"
+ case zapcore.ErrorLevel:
+ return "error"
+ case zapcore.WarnLevel:
+ return "warn"
+ case zapcore.InfoLevel:
+ return "info"
+ default:
+ return "debug"
+ }
+}
+
+func SetLevel(lvl string) {
+ l.atomLevel.SetLevel(setLogLevel(lvl))
+}
+
+// temporary add call skip
+func AddCallerSkip(skip int) *LogX {
+ l.logger.WithOptions(zap.AddCallerSkip(skip))
+ return l
+}
+
+// permanent add call skip
+func AddDepth(skip int) *LogX {
+ l.logger = l.logger.WithOptions(zap.AddCallerSkip(skip))
+ return l
+}
+
+// permanent add options
+func AddOptions(opts ...zap.Option) *LogX {
+ l.logger = l.logger.WithOptions(opts...)
+ return l
+}
+
+func AddField(k string, v interface{}) {
+ l.logger.With(zap.Any(k, v))
+}
+
+func AddFields(fields map[string]interface{}) *LogX {
+ for k, v := range fields {
+ l.logger.With(zap.Any(k, v))
+ }
+ return l
+}
+
+// Normal log
+func Debug(e interface{}, args ...interface{}) error {
+ return l.Debug(e, args...)
+}
+func Info(e interface{}, args ...interface{}) error {
+ return l.Info(e, args...)
+}
+func Warn(e interface{}, args ...interface{}) error {
+ return l.Warn(e, args...)
+}
+func Error(e interface{}, args ...interface{}) error {
+ return l.Error(e, args...)
+}
+func Panic(e interface{}, args ...interface{}) error {
+ return l.Panic(e, args...)
+}
+func Fatal(e interface{}, args ...interface{}) error {
+ return l.Fatal(e, args...)
+}
+
+// Format logs
+func Debugf(format string, args ...interface{}) error {
+ return l.Debugf(format, args...)
+}
+func Infof(format string, args ...interface{}) error {
+ return l.Infof(format, args...)
+}
+func Warnf(format string, args ...interface{}) error {
+ return l.Warnf(format, args...)
+}
+func Errorf(format string, args ...interface{}) error {
+ return l.Errorf(format, args...)
+}
+func Panicf(format string, args ...interface{}) error {
+ return l.Panicf(format, args...)
+}
+func Fatalf(format string, args ...interface{}) error {
+ return l.Fatalf(format, args...)
+}
+
+func formatFieldMap(m FieldMap) []Field {
+ var res []Field
+ for k, v := range m {
+ res = append(res, zap.Any(k, v))
+ }
+ return res
+}
diff --git a/utils/logx/output.go b/utils/logx/output.go
new file mode 100644
index 0000000..1f41c04
--- /dev/null
+++ b/utils/logx/output.go
@@ -0,0 +1,105 @@
+package zhios_order_relate_logx
+
+import (
+ "bytes"
+ "io"
+ "os"
+ "path/filepath"
+ "time"
+
+ "gopkg.in/natefinch/lumberjack.v2"
+)
+
+// output interface
+type WriteSyncer interface {
+ io.Writer
+ Sync() error
+}
+
+// split writer
+func NewRollingFile(dir, filename string, maxSize, MaxAge int) WriteSyncer {
+ s, err := os.Stat(dir)
+ if err != nil || !s.IsDir() {
+ os.RemoveAll(dir)
+ if err := os.MkdirAll(dir, 0766); err != nil {
+ panic(err)
+ }
+ }
+ return newLumberjackWriteSyncer(&lumberjack.Logger{
+ Filename: filepath.Join(dir, filename),
+ MaxSize: maxSize, // megabytes, MB
+ MaxAge: MaxAge, // days
+ LocalTime: true,
+ Compress: false,
+ })
+}
+
+type lumberjackWriteSyncer struct {
+ *lumberjack.Logger
+ buf *bytes.Buffer
+ logChan chan []byte
+ closeChan chan interface{}
+ maxSize int
+}
+
+func newLumberjackWriteSyncer(l *lumberjack.Logger) *lumberjackWriteSyncer {
+ ws := &lumberjackWriteSyncer{
+ Logger: l,
+ buf: bytes.NewBuffer([]byte{}),
+ logChan: make(chan []byte, 5000),
+ closeChan: make(chan interface{}),
+ maxSize: 1024,
+ }
+ go ws.run()
+ return ws
+}
+
+func (l *lumberjackWriteSyncer) run() {
+ ticker := time.NewTicker(1 * time.Second)
+
+ for {
+ select {
+ case <-ticker.C:
+ if l.buf.Len() > 0 {
+ l.sync()
+ }
+ case bs := <-l.logChan:
+ _, err := l.buf.Write(bs)
+ if err != nil {
+ continue
+ }
+ if l.buf.Len() > l.maxSize {
+ l.sync()
+ }
+ case <-l.closeChan:
+ l.sync()
+ return
+ }
+ }
+}
+
+func (l *lumberjackWriteSyncer) Stop() {
+ close(l.closeChan)
+}
+
+func (l *lumberjackWriteSyncer) Write(bs []byte) (int, error) {
+ b := make([]byte, len(bs))
+ for i, c := range bs {
+ b[i] = c
+ }
+ l.logChan <- b
+ return 0, nil
+}
+
+func (l *lumberjackWriteSyncer) Sync() error {
+ return nil
+}
+
+func (l *lumberjackWriteSyncer) sync() error {
+ defer l.buf.Reset()
+ _, err := l.Logger.Write(l.buf.Bytes())
+ if err != nil {
+ return err
+ }
+ return nil
+}
diff --git a/utils/logx/sugar.go b/utils/logx/sugar.go
new file mode 100644
index 0000000..5beae07
--- /dev/null
+++ b/utils/logx/sugar.go
@@ -0,0 +1,192 @@
+package zhios_order_relate_logx
+
+import (
+ "errors"
+ "fmt"
+ "strconv"
+
+ "go.uber.org/zap"
+)
+
+type LogX struct {
+ logger *zap.Logger
+ atomLevel *zap.AtomicLevel
+}
+
+type Field = zap.Field
+type FieldMap map[string]interface{}
+
+// 判断其他类型--start
+func getFields(msg string, format bool, args ...interface{}) (string, []Field) {
+ var str []interface{}
+ var fields []zap.Field
+ if len(args) > 0 {
+ for _, v := range args {
+ if f, ok := v.(Field); ok {
+ fields = append(fields, f)
+ } else if f, ok := v.(FieldMap); ok {
+ fields = append(fields, formatFieldMap(f)...)
+ } else {
+ str = append(str, AnyToString(v))
+ }
+ }
+ if format {
+ return fmt.Sprintf(msg, str...), fields
+ }
+ str = append([]interface{}{msg}, str...)
+ return fmt.Sprintln(str...), fields
+ }
+ return msg, []Field{}
+}
+
+func (l *LogX) Debug(s interface{}, args ...interface{}) error {
+ es, e := checkErr(s)
+ if es != "" {
+ msg, field := getFields(es, false, args...)
+ l.logger.Debug(msg, field...)
+ }
+ return e
+}
+func (l *LogX) Info(s interface{}, args ...interface{}) error {
+ es, e := checkErr(s)
+ if es != "" {
+ msg, field := getFields(es, false, args...)
+ l.logger.Info(msg, field...)
+ }
+ return e
+}
+func (l *LogX) Warn(s interface{}, args ...interface{}) error {
+ es, e := checkErr(s)
+ if es != "" {
+ msg, field := getFields(es, false, args...)
+ l.logger.Warn(msg, field...)
+ }
+ return e
+}
+func (l *LogX) Error(s interface{}, args ...interface{}) error {
+ es, e := checkErr(s)
+ if es != "" {
+ msg, field := getFields(es, false, args...)
+ l.logger.Error(msg, field...)
+ }
+ return e
+}
+func (l *LogX) DPanic(s interface{}, args ...interface{}) error {
+ es, e := checkErr(s)
+ if es != "" {
+ msg, field := getFields(es, false, args...)
+ l.logger.DPanic(msg, field...)
+ }
+ return e
+}
+func (l *LogX) Panic(s interface{}, args ...interface{}) error {
+ es, e := checkErr(s)
+ if es != "" {
+ msg, field := getFields(es, false, args...)
+ l.logger.Panic(msg, field...)
+ }
+ return e
+}
+func (l *LogX) Fatal(s interface{}, args ...interface{}) error {
+ es, e := checkErr(s)
+ if es != "" {
+ msg, field := getFields(es, false, args...)
+ l.logger.Fatal(msg, field...)
+ }
+ return e
+}
+
+func checkErr(s interface{}) (string, error) {
+ switch e := s.(type) {
+ case error:
+ return e.Error(), e
+ case string:
+ return e, errors.New(e)
+ case []byte:
+ return string(e), nil
+ default:
+ return "", nil
+ }
+}
+
+func (l *LogX) LogError(err error) error {
+ return l.Error(err.Error())
+}
+
+func (l *LogX) Debugf(msg string, args ...interface{}) error {
+ s, f := getFields(msg, true, args...)
+ l.logger.Debug(s, f...)
+ return errors.New(s)
+}
+
+func (l *LogX) Infof(msg string, args ...interface{}) error {
+ s, f := getFields(msg, true, args...)
+ l.logger.Info(s, f...)
+ return errors.New(s)
+}
+
+func (l *LogX) Warnf(msg string, args ...interface{}) error {
+ s, f := getFields(msg, true, args...)
+ l.logger.Warn(s, f...)
+ return errors.New(s)
+}
+
+func (l *LogX) Errorf(msg string, args ...interface{}) error {
+ s, f := getFields(msg, true, args...)
+ l.logger.Error(s, f...)
+ return errors.New(s)
+}
+
+func (l *LogX) DPanicf(msg string, args ...interface{}) error {
+ s, f := getFields(msg, true, args...)
+ l.logger.DPanic(s, f...)
+ return errors.New(s)
+}
+
+func (l *LogX) Panicf(msg string, args ...interface{}) error {
+ s, f := getFields(msg, true, args...)
+ l.logger.Panic(s, f...)
+ return errors.New(s)
+}
+
+func (l *LogX) Fatalf(msg string, args ...interface{}) error {
+ s, f := getFields(msg, true, args...)
+ l.logger.Fatal(s, f...)
+ return errors.New(s)
+}
+
+func AnyToString(raw interface{}) string {
+ switch i := raw.(type) {
+ case []byte:
+ return string(i)
+ case int:
+ return strconv.FormatInt(int64(i), 10)
+ case int64:
+ return strconv.FormatInt(i, 10)
+ case float32:
+ return strconv.FormatFloat(float64(i), 'f', 2, 64)
+ case float64:
+ return strconv.FormatFloat(i, 'f', 2, 64)
+ case uint:
+ return strconv.FormatInt(int64(i), 10)
+ case uint8:
+ return strconv.FormatInt(int64(i), 10)
+ case uint16:
+ return strconv.FormatInt(int64(i), 10)
+ case uint32:
+ return strconv.FormatInt(int64(i), 10)
+ case uint64:
+ return strconv.FormatInt(int64(i), 10)
+ case int8:
+ return strconv.FormatInt(int64(i), 10)
+ case int16:
+ return strconv.FormatInt(int64(i), 10)
+ case int32:
+ return strconv.FormatInt(int64(i), 10)
+ case string:
+ return i
+ case error:
+ return i.Error()
+ }
+ return fmt.Sprintf("%#v", raw)
+}
diff --git a/utils/serialize.go b/utils/serialize.go
new file mode 100644
index 0000000..dd0150b
--- /dev/null
+++ b/utils/serialize.go
@@ -0,0 +1,23 @@
+package zhios_order_relate_utils
+
+import (
+ "encoding/json"
+)
+
+func Serialize(data interface{}) []byte {
+ res, err := json.Marshal(data)
+ if err != nil {
+ return []byte{}
+ }
+ return res
+}
+
+func Unserialize(b []byte, dst interface{}) {
+ if err := json.Unmarshal(b, dst); err != nil {
+ dst = nil
+ }
+}
+
+func SerializeStr(data interface{}, arg ...interface{}) string {
+ return string(Serialize(data))
+}
diff --git a/utils/string.go b/utils/string.go
new file mode 100644
index 0000000..e68c51c
--- /dev/null
+++ b/utils/string.go
@@ -0,0 +1,203 @@
+package zhios_order_relate_utils
+
+import (
+ "fmt"
+ "github.com/syyongx/php2go"
+ "math/rand"
+ "reflect"
+ "sort"
+ "strings"
+ "time"
+)
+
+func Implode(glue string, args ...interface{}) string {
+ data := make([]string, len(args))
+ for i, s := range args {
+ data[i] = fmt.Sprint(s)
+ }
+ return strings.Join(data, glue)
+}
+
+//字符串是否在数组里
+func InArr(target string, str_array []string) bool {
+ for _, element := range str_array {
+ if target == element {
+ return true
+ }
+ }
+ return false
+}
+func InArrToInt(target int, str_array []int) bool {
+ for _, element := range str_array {
+ if target == element {
+ return true
+ }
+ }
+ return false
+}
+
+//把数组的值放到key里
+func ArrayColumn(array interface{}, key string) (result map[string]interface{}, err error) {
+ result = make(map[string]interface{})
+ t := reflect.TypeOf(array)
+ v := reflect.ValueOf(array)
+ if t.Kind() != reflect.Slice {
+ return nil, nil
+ }
+ if v.Len() == 0 {
+ return nil, nil
+ }
+ for i := 0; i < v.Len(); i++ {
+ indexv := v.Index(i)
+ if indexv.Type().Kind() != reflect.Struct {
+ return nil, nil
+ }
+ mapKeyInterface := indexv.FieldByName(key)
+ if mapKeyInterface.Kind() == reflect.Invalid {
+ return nil, nil
+ }
+ mapKeyString, err := InterfaceToString(mapKeyInterface.Interface())
+ if err != nil {
+ return nil, err
+ }
+ result[mapKeyString] = indexv.Interface()
+ }
+ return result, err
+}
+
+//转string
+func InterfaceToString(v interface{}) (result string, err error) {
+ switch reflect.TypeOf(v).Kind() {
+ case reflect.Int64, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32:
+ result = fmt.Sprintf("%v", v)
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ result = fmt.Sprintf("%v", v)
+ case reflect.String:
+ result = v.(string)
+ default:
+ err = nil
+ }
+ return result, err
+}
+
+func HideTrueName(name string) string {
+ res := "**"
+ if name != "" {
+ runs := []rune(name)
+ leng := len(runs)
+ if leng <= 3 {
+ res = string(runs[0:1]) + res
+ } else if leng < 5 {
+ res = string(runs[0:2]) + res
+ } else if leng < 10 {
+ res = string(runs[0:2]) + "***" + string(runs[leng-2:leng])
+ } else if leng < 16 {
+ res = string(runs[0:3]) + "****" + string(runs[leng-3:leng])
+ } else {
+ res = string(runs[0:4]) + "*****" + string(runs[leng-4:leng])
+ }
+ }
+ return res
+}
+
+func GetQueryParam(uri string) map[string]string {
+ //根据问号分割路由还是query参数
+ uriList := strings.Split(uri, "?")
+ var query = make(map[string]string, 0)
+ //有参数才处理
+ if len(uriList) == 2 {
+ //分割query参数
+ var queryList = strings.Split(uriList[1], "&")
+ if len(queryList) > 0 {
+ //key value 分别赋值
+ for _, v := range queryList {
+ var valueList = strings.Split(v, "=")
+ if len(valueList) == 2 {
+ value, _ := php2go.URLDecode(valueList[1])
+ if value == "" {
+ value = valueList[1]
+ }
+ query[valueList[0]] = value
+ }
+ }
+ }
+ }
+ return query
+}
+
+//JoinStringsInASCII 按照规则,参数名ASCII码从小到大排序后拼接
+//data 待拼接的数据
+//sep 连接符
+//onlyValues 是否只包含参数值,true则不包含参数名,否则参数名和参数值均有
+//includeEmpty 是否包含空值,true则包含空值,否则不包含,注意此参数不影响参数名的存在
+//exceptKeys 被排除的参数名,不参与排序及拼接
+func JoinStringsInASCII(data map[string]string, sep string, onlyValues, includeEmpty bool, exceptKeys ...string) string {
+ var list []string
+ var keyList []string
+ m := make(map[string]int)
+ if len(exceptKeys) > 0 {
+ for _, except := range exceptKeys {
+ m[except] = 1
+ }
+ }
+ for k := range data {
+ if _, ok := m[k]; ok {
+ continue
+ }
+ value := data[k]
+ if !includeEmpty && value == "" {
+ continue
+ }
+ if onlyValues {
+ keyList = append(keyList, k)
+ } else {
+ list = append(list, fmt.Sprintf("%s=%s", k, value))
+ }
+ }
+ if onlyValues {
+ sort.Strings(keyList)
+ for _, v := range keyList {
+ list = append(list, AnyToString(data[v]))
+ }
+ } else {
+ sort.Strings(list)
+ }
+ return strings.Join(list, sep)
+}
+
+//x的y次方
+func RandPow(l int) string {
+ var i = "1"
+ for j := 0; j < l; j++ {
+ i += "0"
+ }
+ k := StrToInt64(i)
+ n := rand.New(rand.NewSource(time.Now().UnixNano())).Int63n(k)
+ ls := "%0" + IntToStr(l) + "v"
+ str := fmt.Sprintf(ls, n)
+ //min := int(math.Pow10(l - 1))
+ //max := int(math.Pow10(l) - 1)
+ return str
+}
+
+//根据显示长度截取字符串
+func ShowSubstr(s string, l int) string {
+ if len(s) <= l {
+ return s
+ }
+ ss, sl, rl, rs := "", 0, 0, []rune(s)
+ for _, r := range rs {
+ rint := int(r)
+ if rint < 128 {
+ rl = 1
+ } else {
+ rl = 2
+ }
+ if sl+rl > l {
+ break
+ }
+ sl += rl
+ ss += string(r)
+ }
+ return ss
+}