From debe30a374032b468d1f6ea22eed88bd7bbdf4e8 Mon Sep 17 00:00:00 2001 From: huangjuajun <102564160@qq.com> Date: Fri, 24 Jun 2022 17:54:32 +0800 Subject: [PATCH] =?UTF-8?q?add=20reverse=20for=20v1.0.0=20=E5=88=86?= =?UTF-8?q?=E4=BD=A3=E8=A7=84=E5=88=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/.gitignore | 8 + .idea/modules.xml | 8 + .idea/vcs.xml | 6 + .idea/zyos_go_order_relate_rule.iml | 9 + db/db_sys_cfg.go | 57 +++ db/db_user.go | 17 + db/db_user_level.go | 27 ++ db/db_virtual_coin.go | 26 ++ db/dbs_plan_commission.go | 29 ++ db/dbs_plan_reward.go | 31 ++ db/dbs_user.go | 29 ++ db/model/plan_commission.go | 15 + db/model/plan_reward.go | 20 + db/model/sys_cfg.go | 7 + db/model/user.go | 29 ++ db/model/user_level.go | 21 + db/model/user_profile.go | 91 ++++ db/model/user_relate.go | 13 + db/model/virtual_coin.go | 17 + db/model/virtual_coin_relate.go | 12 + go.mod | 13 + go.sum | 662 +++++++++++++++++++++++++++ lib/comm_plan/all.go | 683 ++++++++++++++++++++++++++++ lib/comm_plan/init.go | 155 +++++++ lib/comm_plan/self.go | 185 ++++++++ md/app_redis_key.go | 19 + md/cfg_key.go | 92 ++++ md/commission_parameter.go | 28 ++ md/provider.go | 143 ++++++ md/user_info.go | 39 ++ rule/get_plan_cfg.go | 180 ++++++++ rule/relate_commission.go | 58 +++ rule/reward_commission.go | 401 ++++++++++++++++ rule/user_relative_network.go | 46 ++ utils/cache/base.go | 421 +++++++++++++++++ utils/cache/redis.go | 403 ++++++++++++++++ utils/cache/redis_cluster.go | 622 +++++++++++++++++++++++++ utils/cache/redis_pool.go | 324 +++++++++++++ utils/cache/redis_pool_cluster.go | 617 +++++++++++++++++++++++++ utils/convert.go | 366 +++++++++++++++ utils/curl.go | 209 +++++++++ utils/file.go | 22 + utils/format.go | 59 +++ utils/logx/log.go | 245 ++++++++++ utils/logx/output.go | 105 +++++ utils/logx/sugar.go | 192 ++++++++ utils/serialize.go | 23 + utils/string.go | 203 +++++++++ 48 files changed, 6987 insertions(+) create mode 100644 .idea/.gitignore create mode 100644 .idea/modules.xml create mode 100644 .idea/vcs.xml create mode 100644 .idea/zyos_go_order_relate_rule.iml create mode 100644 db/db_sys_cfg.go create mode 100644 db/db_user.go create mode 100644 db/db_user_level.go create mode 100644 db/db_virtual_coin.go create mode 100644 db/dbs_plan_commission.go create mode 100644 db/dbs_plan_reward.go create mode 100644 db/dbs_user.go create mode 100644 db/model/plan_commission.go create mode 100644 db/model/plan_reward.go create mode 100644 db/model/sys_cfg.go create mode 100644 db/model/user.go create mode 100644 db/model/user_level.go create mode 100644 db/model/user_profile.go create mode 100644 db/model/user_relate.go create mode 100644 db/model/virtual_coin.go create mode 100644 db/model/virtual_coin_relate.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 lib/comm_plan/all.go create mode 100644 lib/comm_plan/init.go create mode 100644 lib/comm_plan/self.go create mode 100644 md/app_redis_key.go create mode 100644 md/cfg_key.go create mode 100644 md/commission_parameter.go create mode 100644 md/provider.go create mode 100644 md/user_info.go create mode 100644 rule/get_plan_cfg.go create mode 100644 rule/relate_commission.go create mode 100644 rule/reward_commission.go create mode 100644 rule/user_relative_network.go create mode 100644 utils/cache/base.go create mode 100644 utils/cache/redis.go create mode 100644 utils/cache/redis_cluster.go create mode 100644 utils/cache/redis_pool.go create mode 100644 utils/cache/redis_pool_cluster.go create mode 100644 utils/convert.go create mode 100644 utils/curl.go create mode 100644 utils/file.go create mode 100644 utils/format.go create mode 100644 utils/logx/log.go create mode 100644 utils/logx/output.go create mode 100644 utils/logx/sugar.go create mode 100644 utils/serialize.go create mode 100644 utils/string.go 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 +}