@@ -21,7 +21,7 @@ func InitMapDbs(c *cfg.DBCfg, prd bool) { | |||||
logx.Fatalf("db_mapping not exists : %v", err) | logx.Fatalf("db_mapping not exists : %v", err) | ||||
} | } | ||||
// tables := MapAllDatabases(debug) | // tables := MapAllDatabases(debug) | ||||
if prd { | |||||
if true { | |||||
tables = GetAllDatabasePrd() //debug 获取生产 | tables = GetAllDatabasePrd() //debug 获取生产 | ||||
} else { | } else { | ||||
tables = GetAllDatabaseDev() //debug 获取开发 | tables = GetAllDatabaseDev() //debug 获取开发 | ||||
@@ -17,54 +17,54 @@ func Init() { | |||||
// 增加消费任务队列 | // 增加消费任务队列 | ||||
func initConsumes() { | func initConsumes() { | ||||
jobs[consumeMd.ZhiosUserUpLvFunName] = ZhiosUserUpLv | |||||
jobs[consumeMd.CanalGuideOrderByUserUpLvConsume] = CanalGuideOrderByUserUpLvConsume | |||||
jobs[consumeMd.ZhiosOrderFreeFunName] = ZhiosOrderFree | |||||
jobs[consumeMd.ZhiosOrderTotalFunName] = ZhiosOrderTotal | |||||
jobs[consumeMd.ZhiosOrderTotalSecondFunName] = ZhiosOrderTotalSecond | |||||
//jobs[consumeMd.ZhiosUserUpLvFunName] = ZhiosUserUpLv | |||||
//jobs[consumeMd.CanalGuideOrderByUserUpLvConsume] = CanalGuideOrderByUserUpLvConsume | |||||
//jobs[consumeMd.ZhiosOrderFreeFunName] = ZhiosOrderFree | |||||
//jobs[consumeMd.ZhiosOrderTotalFunName] = ZhiosOrderTotal | |||||
//jobs[consumeMd.ZhiosOrderTotalSecondFunName] = ZhiosOrderTotalSecond | |||||
//// | |||||
//jobs[consumeMd.ZhiosOrderSettleTotalFunName] = ZhiosSettleTotal | |||||
//jobs[consumeMd.ZhiosOrderHjyFunName] = ZhiosOrderHjy | |||||
//jobs[consumeMd.ZhiosOrderBuckleFunName] = ZhiosOrderBuckle | |||||
//// | |||||
//jobs[consumeMd.ZhiosSupplierAfterOrderFunName] = ZhiosSupplierAfterOrder | |||||
//jobs[consumeMd.ZhiosGuideStoreOrderFunName] = ZhiosGuideStoreOrder | |||||
// | // | ||||
jobs[consumeMd.ZhiosOrderSettleTotalFunName] = ZhiosSettleTotal | |||||
jobs[consumeMd.ZhiosOrderHjyFunName] = ZhiosOrderHjy | |||||
jobs[consumeMd.ZhiosOrderBuckleFunName] = ZhiosOrderBuckle | |||||
////jobs[consumeMd.ZhiosAppreciationDevFunName] = ZhiosAppreciation | |||||
// | // | ||||
jobs[consumeMd.ZhiosSupplierAfterOrderFunName] = ZhiosSupplierAfterOrder | |||||
jobs[consumeMd.ZhiosGuideStoreOrderFunName] = ZhiosGuideStoreOrder | |||||
//jobs[consumeMd.ZhiosAppreciationDevFunName] = ZhiosAppreciation | |||||
jobs[consumeMd.ZhiosAppreciationFunName] = ZhiosAppreciation | |||||
jobs[consumeMd.ZhiosValidUserFunName] = ZhiosValidUser | |||||
//jobs[consumeMd.ZhiosAcquisitionConditionDevFunName] = ZhiosAcquisitionCondition | |||||
jobs[consumeMd.ZhiosAcquisitionConditionFunName] = ZhiosAcquisitionCondition | |||||
jobs[consumeMd.CanalOrderConsumeFunName] = CanalOrderConsume | |||||
jobs[consumeMd.CanalGuideOrderConsumeFunName] = CanalGuideOrderConsume | |||||
jobs[consumeMd.ZhiOsUserVisitIpAddressConsumeFunName] = ZhiOsUserVisitIpAddressConsume | |||||
jobs[consumeMd.DouShenUserRegisterConsumeForOfficialFunName] = DouShenUserRegisterConsumeForOfficial | |||||
jobs[consumeMd.DouShenUserRegisterConsumeForOperationCenterFunName] = DouShenUserRegisterConsumeForOperationCenter | |||||
jobs[consumeMd.DouShenUserRegisterConsumeForMyRecommenderFunName] = DouShenUserRegisterConsumeForMyRecommender | |||||
jobs[consumeMd.DouShenUserRegisterConsumeForMyFansFunName] = DouShenUserRegisterConsumeForMyFans | |||||
jobs[consumeMd.DouShenUserRegisterConsumeForUserRegisterUpLvFunName] = DouShenUserRegisterConsumeForUserRegisterUpLv | |||||
jobs[consumeMd.ZhiosFastReturnOrderPayFunName] = ZhiosFastReturnOrderPay | |||||
jobs[consumeMd.ZhiosFastReturnOrderSuccessFunName] = ZhiosFastReturnOrderSuccess | |||||
jobs[consumeMd.ZhiosFastReturnOrderRefundFunName] = ZhiosFastReturnOrderRefund | |||||
jobs[consumeMd.ZhiosFastReturnOrderRefundSecondFunName] = ZhiosFastReturnOrderRefundSecond | |||||
//jobs[consumeMd.CanalMallOrdForYouMiShangFunName] = CanalMallOrdForYouMiShang | |||||
jobs[consumeMd.YoumishangExchangeStoreFunName] = YoumishangExchangeStore | |||||
jobs[consumeMd.ZhiosRechargeOrderFailFunName] = ZhiosRechargeOrderFail | |||||
jobs[consumeMd.CloudIssuanceAsyncMLoginFunName] = CloudIssuanceAsyncMLoginConsume | |||||
jobs[consumeMd.ZhiosTikTokUpdateFunName] = ZhiosTikTokUpdate | |||||
jobs[consumeMd.ZhiosTikTokAllUpdateFunName] = ZhiosTikTokAllUpdate | |||||
jobs[consumeMd.ZhiosCapitalPoolOrderTotalFunName] = ZhiosCapitalPoolOrderTotal | |||||
jobs[consumeMd.ZhiosExpressOrderFail] = ZhiosExpressOrderFail | |||||
jobs[consumeMd.ZhiosWithdrawReward] = ZhiosWithdrawReward | |||||
//jobs[consumeMd.ZhiosAppreciationFunName] = ZhiosAppreciation | |||||
//jobs[consumeMd.ZhiosValidUserFunName] = ZhiosValidUser | |||||
// | |||||
////jobs[consumeMd.ZhiosAcquisitionConditionDevFunName] = ZhiosAcquisitionCondition | |||||
// | |||||
//jobs[consumeMd.ZhiosAcquisitionConditionFunName] = ZhiosAcquisitionCondition | |||||
//jobs[consumeMd.CanalOrderConsumeFunName] = CanalOrderConsume | |||||
//jobs[consumeMd.CanalGuideOrderConsumeFunName] = CanalGuideOrderConsume | |||||
//jobs[consumeMd.ZhiOsUserVisitIpAddressConsumeFunName] = ZhiOsUserVisitIpAddressConsume | |||||
// | |||||
//jobs[consumeMd.DouShenUserRegisterConsumeForOfficialFunName] = DouShenUserRegisterConsumeForOfficial | |||||
//jobs[consumeMd.DouShenUserRegisterConsumeForOperationCenterFunName] = DouShenUserRegisterConsumeForOperationCenter | |||||
//jobs[consumeMd.DouShenUserRegisterConsumeForMyRecommenderFunName] = DouShenUserRegisterConsumeForMyRecommender | |||||
//jobs[consumeMd.DouShenUserRegisterConsumeForMyFansFunName] = DouShenUserRegisterConsumeForMyFans | |||||
//jobs[consumeMd.DouShenUserRegisterConsumeForUserRegisterUpLvFunName] = DouShenUserRegisterConsumeForUserRegisterUpLv | |||||
// | |||||
//jobs[consumeMd.ZhiosFastReturnOrderPayFunName] = ZhiosFastReturnOrderPay | |||||
//jobs[consumeMd.ZhiosFastReturnOrderSuccessFunName] = ZhiosFastReturnOrderSuccess | |||||
//jobs[consumeMd.ZhiosFastReturnOrderRefundFunName] = ZhiosFastReturnOrderRefund | |||||
//jobs[consumeMd.ZhiosFastReturnOrderRefundSecondFunName] = ZhiosFastReturnOrderRefundSecond | |||||
// | |||||
////jobs[consumeMd.CanalMallOrdForYouMiShangFunName] = CanalMallOrdForYouMiShang | |||||
//jobs[consumeMd.YoumishangExchangeStoreFunName] = YoumishangExchangeStore | |||||
// | |||||
//jobs[consumeMd.ZhiosRechargeOrderFailFunName] = ZhiosRechargeOrderFail | |||||
// | |||||
//jobs[consumeMd.CloudIssuanceAsyncMLoginFunName] = CloudIssuanceAsyncMLoginConsume | |||||
//jobs[consumeMd.ZhiosTikTokUpdateFunName] = ZhiosTikTokUpdate | |||||
//jobs[consumeMd.ZhiosTikTokAllUpdateFunName] = ZhiosTikTokAllUpdate | |||||
// | |||||
//jobs[consumeMd.ZhiosCapitalPoolOrderTotalFunName] = ZhiosCapitalPoolOrderTotal | |||||
//jobs[consumeMd.ZhiosExpressOrderFail] = ZhiosExpressOrderFail | |||||
//jobs[consumeMd.ZhiosWithdrawReward] = ZhiosWithdrawReward | |||||
//jobs[consumeMd.ZhiosRechargeOrderFailDevFunName] = ZhiosRechargeOrderFailDev | //jobs[consumeMd.ZhiosRechargeOrderFailDevFunName] = ZhiosRechargeOrderFailDev | ||||
@@ -74,6 +74,9 @@ func initConsumes() { | |||||
//////////////////////////////////////// V2 ///////////////////////////////////////////////////// | //////////////////////////////////////// V2 ///////////////////////////////////////////////////// | ||||
//jobs[consumeMd.SupplyCloudChainFenxiaoNewChangeFunName] = SupplyCloudChainFenxiaoNewChangeConsume | //jobs[consumeMd.SupplyCloudChainFenxiaoNewChangeFunName] = SupplyCloudChainFenxiaoNewChangeConsume | ||||
//////////////////////////////////////// V3 ///////////////////////////////////////////////////// | |||||
jobs[consumeMd.MallAddSupplyGoodsFunName] = MallAddSupplyGoodsConsume | |||||
} | } | ||||
func Run() { | func Run() { | ||||
@@ -0,0 +1,88 @@ | |||||
package consume | |||||
import ( | |||||
svc2 "applet/app/svc" | |||||
"applet/app/utils/logx" | |||||
"applet/consume/md" | |||||
md3 "applet/mall/md" | |||||
"applet/mall/svc" | |||||
"code.fnuoos.com/go_rely_warehouse/zyos_go_mq.git/rabbit" | |||||
"encoding/json" | |||||
"errors" | |||||
"fmt" | |||||
"github.com/gin-gonic/gin" | |||||
"github.com/jinzhu/copier" | |||||
"github.com/streadway/amqp" | |||||
"strings" | |||||
) | |||||
func MallAddSupplyGoodsConsume(queue md.MqQueue) { | |||||
fmt.Println(">>>>>>>>>>>>>>>>>>>>>>>>") | |||||
ch, err := rabbit.Cfg.Pool.GetChannel() | |||||
if err != nil { | |||||
logx.Error(err) | |||||
return | |||||
} | |||||
defer ch.Release() | |||||
//2、取出数据进行消费 | |||||
ch.Qos(1) | |||||
delivery := ch.Consume(queue.Name, false) | |||||
var res amqp.Delivery | |||||
var ok bool | |||||
for { | |||||
res, ok = <-delivery | |||||
if ok == true { | |||||
fmt.Println(">>>>>>>>>>>>>>>>MallAddSupplyGoodsConsume<<<<<<<<<<<<<<<<<<<<<<<<<") | |||||
err = handleMallAddSupplyGoodsConsume(res.Body) | |||||
if err != nil { | |||||
fmt.Println("*****************err*************************", err) | |||||
} | |||||
_ = res.Ack(true) | |||||
} else { | |||||
panic(errors.New("error getting message")) | |||||
} | |||||
} | |||||
fmt.Println("get msg done") | |||||
} | |||||
func handleMallAddSupplyGoodsConsume(msgData []byte) error { | |||||
//解析mq中queue的数据结构体 | |||||
var pushStuct struct { | |||||
Args struct { | |||||
GoodsInfo []*md3.OfficialGoods `json:"goods_info"` | |||||
md3.OfficialGoodsConditions | |||||
} `json:"args"` | |||||
Mid string `json:"mid"` | |||||
} | |||||
err := json.Unmarshal(msgData, &pushStuct) | |||||
if err != nil { | |||||
panic(err) | |||||
} | |||||
//设置masterId | |||||
var c = &gin.Context{} | |||||
c.Set("mid", pushStuct.Mid) | |||||
var t struct { | |||||
GoodsInfo []*md3.OfficialGoods `json:"goods_info"` | |||||
md3.OfficialGoodsConditions | |||||
} | |||||
t = pushStuct.Args | |||||
var conditions md3.OfficialGoodsConditions | |||||
conditions.CategoryId = t.CategoryId | |||||
conditions.AddPriceBase = t.AddPriceBase | |||||
conditions.AddPriceNum = t.AddPriceNum | |||||
conditions.AddPriceType = t.AddPriceType | |||||
conditions.NumType = t.NumType | |||||
engine := svc2.MasterDb(c) | |||||
for _, goods := range t.GoodsInfo { | |||||
var b2cGoods md3.OfficialGoods | |||||
copier.Copy(&b2cGoods, goods) | |||||
ex := strings.Split(b2cGoods.Price, "-") | |||||
b2cGoods.Price = ex[0] | |||||
err1 := svc.AddOfficialGoods(c, engine, &b2cGoods, &conditions) | |||||
if err1 != nil { | |||||
return err1 | |||||
} | |||||
} | |||||
return nil | |||||
} |
@@ -21,7 +21,7 @@ var RabbitMqQueueKeyList = []*MqQueue{ | |||||
ConsumeFunName: "CloudIssuanceAsyncMLoginConsume", | ConsumeFunName: "CloudIssuanceAsyncMLoginConsume", | ||||
}, | }, | ||||
{ | { | ||||
ExchangeName: "", | |||||
ExchangeName: "zhios.cloud.issuance.msg.callback.exchange", | |||||
Name: "cloud_issuance_msg_call_back", | Name: "cloud_issuance_msg_call_back", | ||||
Type: DirectQueueType, | Type: DirectQueueType, | ||||
IsPersistent: false, | IsPersistent: false, | ||||
@@ -30,7 +30,7 @@ var RabbitMqQueueKeyList = []*MqQueue{ | |||||
ConsumeFunName: "CloudIssuanceMsgCallBackConsume", | ConsumeFunName: "CloudIssuanceMsgCallBackConsume", | ||||
}, | }, | ||||
{ | { | ||||
ExchangeName: "zhios.cloud.issuance.msg.callback.exchange", | |||||
ExchangeName: "zhios.cloud_chain.fenxiao.newChange.exchange", | |||||
Name: "cloud_chain_fenxiao_newChange", | Name: "cloud_chain_fenxiao_newChange", | ||||
Type: FanOutQueueType, | Type: FanOutQueueType, | ||||
IsPersistent: false, | IsPersistent: false, | ||||
@@ -38,6 +38,15 @@ var RabbitMqQueueKeyList = []*MqQueue{ | |||||
BindKey: "", | BindKey: "", | ||||
ConsumeFunName: "SupplyCloudChainFenxiaoNewChangeConsume", | ConsumeFunName: "SupplyCloudChainFenxiaoNewChangeConsume", | ||||
}, | }, | ||||
{ | |||||
ExchangeName: "zhios.addSupplyGoods.exchange", | |||||
Name: "mall_add_supply_goods", | |||||
Type: FanOutQueueType, | |||||
IsPersistent: false, | |||||
RoutKey: "", | |||||
BindKey: "", | |||||
ConsumeFunName: "MallAddSupplyGoodsConsume", | |||||
}, | |||||
{ | { | ||||
ExchangeName: "canal.topic", | ExchangeName: "canal.topic", | ||||
Name: "canal_order", | Name: "canal_order", | ||||
@@ -413,4 +422,5 @@ const ( | |||||
ZhiosGuideStoreOrderFunName = "ZhiosGuideStoreOrder" | ZhiosGuideStoreOrderFunName = "ZhiosGuideStoreOrder" | ||||
ZhiosAcquisitionConditionDevFunName = "ZhiosAcquisitionConditionDev" | ZhiosAcquisitionConditionDevFunName = "ZhiosAcquisitionConditionDev" | ||||
SupplyCloudChainFenxiaoNewChangeFunName = "SupplyCloudChainFenxiaoNewChangeConsume" | SupplyCloudChainFenxiaoNewChangeFunName = "SupplyCloudChainFenxiaoNewChangeConsume" | ||||
MallAddSupplyGoodsFunName = "MallAddSupplyGoodsConsume" | |||||
) | ) |
@@ -44,6 +44,7 @@ require ( | |||||
) | ) | ||||
require ( | require ( | ||||
github.com/360EntSecGroup-Skylar/excelize v1.4.1 // indirect | |||||
github.com/KyleBanks/depth v1.2.1 // indirect | github.com/KyleBanks/depth v1.2.1 // indirect | ||||
github.com/PuerkitoBio/purell v1.1.1 // indirect | github.com/PuerkitoBio/purell v1.1.1 // indirect | ||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect | github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect | ||||
@@ -66,6 +67,7 @@ require ( | |||||
github.com/mingrammer/commonregex v1.0.1 // indirect | github.com/mingrammer/commonregex v1.0.1 // indirect | ||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect | ||||
github.com/modern-go/reflect2 v1.0.2 // indirect | github.com/modern-go/reflect2 v1.0.2 // indirect | ||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect | |||||
github.com/mvdan/xurls v1.1.0 // indirect | github.com/mvdan/xurls v1.1.0 // indirect | ||||
github.com/nilorg/sdk v0.0.0-20221104025912-4b6ccb7004d8 // indirect | github.com/nilorg/sdk v0.0.0-20221104025912-4b6ccb7004d8 // indirect | ||||
github.com/olivere/elastic/v7 v7.0.32 // indirect | github.com/olivere/elastic/v7 v7.0.32 // indirect | ||||
@@ -0,0 +1,84 @@ | |||||
package md | |||||
// 获取用户的缓存key | |||||
const ( | |||||
KEY_SYS_CFG_CACHE = "sys_cfg_cache" | |||||
KEY_CFG_CACHE = "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_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_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_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" //电影票 | |||||
) |
@@ -0,0 +1,11 @@ | |||||
package md | |||||
const ( | |||||
//http://supply-chain-admin.izhyin.com | |||||
SupplierDomain = "supplier_domain" | |||||
//eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6OCwiYWNjb3VudCI6IjE4MjI5Nzc1MzE2IiwicGhvbmUiOiIxODIyOTc3NTMxNiIsIm1pZCI6IjEyMzQ1NiIsImlzcyI6Inp5b3Nfc3VwcGx5X2NoYWluX3VuaXF1ZV9rZXkifQ.CuiPv1vmdPXJlieJOa4ykCoBuWWA9TT8n8qgSP9ZJ5o | |||||
SupplierUniqueKey = "supplier_unique_key" | |||||
OrderFailureByNotPaying = "b2c_order_failure_by_not_paying" | |||||
OrderFinish = "b2c_order_finish" | |||||
BizB2cSmartyCustomerService = "biz_b2c_smarty_customer_service" | |||||
) |
@@ -0,0 +1,64 @@ | |||||
package md | |||||
import "time" | |||||
type OfficialGoods struct { | |||||
ID string `json:"id"` | |||||
Price string `json:"price"` | |||||
LinePrice string `json:"line_price"` | |||||
Weight string `json:"weight"` | |||||
Profit string `json:"profit"` | |||||
CreateTime string `json:"create_time"` | |||||
UpdateTime string `json:"update_time"` | |||||
Stock int `json:"stock"` | |||||
SkuList []*OfficialSku `json:"skuList"` | |||||
SaleCount int `json:"sale_count"` | |||||
Title string `json:"title"` | |||||
Image string `json:"image"` | |||||
StateText string `json:"state_text"` | |||||
SaleState int `json:"sale_state"` | |||||
SaleStartTime string `json:"sale_start_time"` | |||||
StateCode int `json:"state_code"` | |||||
MerchantID int `json:"merchant_id"` | |||||
MerchantName string `json:"merchant_name"` | |||||
IsGoodsWarehouse string `json:"is_goods_warehouse"` | |||||
AuditReason string `json:"audit_reason"` | |||||
CategoryID string `json:"category_id"` | |||||
GoodsCode string `json:"goods_code"` | |||||
} | |||||
type OfficialSku struct { | |||||
SkuID int `json:"sku_id"` | |||||
GoodsID int `json:"goods_id"` | |||||
Price string `json:"price"` | |||||
CreateTime time.Time `json:"create_time"` | |||||
UpdateTime time.Time `json:"update_time"` | |||||
Weight string `json:"weight"` | |||||
Stock int `json:"stock"` | |||||
Indexes string `json:"indexes"` | |||||
Sku string `json:"sku"` | |||||
SaleCount int `json:"sale_count"` | |||||
LinePrice string `json:"line_price"` | |||||
SkuCode string `json:"sku_code"` | |||||
CloudChainSkuImgUrl string `json:"cloud_chain_sku_img_url"` | |||||
} | |||||
type OfficialGoodsConditions struct { | |||||
PlatformType string `json:"platform_type"` //加入平台 | |||||
CategoryId string `json:"category_id"` | |||||
AddPriceType string `json:"add_price_type"` //1按金额增加2按比例增加 | |||||
AddPriceBase string `json:"add_price_base"` //加价基数 1按进货价2按利润空间 | |||||
AddPriceNum string `json:"add_price_num"` //类型是1就是直接加值,如果是2则是百分比 | |||||
NumType string `json:"num_type"` //价格数值设置 1小数点后两位2整数 | |||||
Ids []string `json:"ids"` //编辑商品id组 | |||||
IsSkuAddPrice string `json:"is_sku_add_price"` | |||||
SkusConditions []*OfficialSkuConditions `json:"skus_conditions"` | |||||
} | |||||
type OfficialSkuConditions struct { | |||||
AddPriceType string `json:"add_price_type"` //1按金额增加2按比例增加 | |||||
AddPriceBase string `json:"add_price_base"` //加价基数 1按进货价2按利润空间 | |||||
AddPriceNum string `json:"add_price_num"` //类型是1就是直接加值,如果是2则是百分比 | |||||
NumType string `json:"num_type"` //价格数值设置 1小数点后两位2整数 | |||||
SkuCode string `json:"sku_code"` | |||||
} |
@@ -0,0 +1,115 @@ | |||||
package md | |||||
import ( | |||||
"applet/supply/md" | |||||
) | |||||
type SupplierGoodsDetail struct { | |||||
BaseGoods BaseGoods `json:"base_goods"` | |||||
//SelectData SelectData `json:"select_data"`\ | |||||
MallLogo string `json:"mall_logo"` //店铺Logo | |||||
MallName string `json:"mall_name"` //店铺名称 | |||||
Skus []*Sku `json:"sku"` | |||||
ShippingData MallShippingTemplateListRespForCamel `json:"shipping_data"` | |||||
} | |||||
type MallShippingTemplateDataForCamel struct { | |||||
Regions []string `json:"region" label:"省/市/区(县)" label:"省市区的id"` | |||||
AdditionalAmount string `json:"additionalAmount" label:"续件/续重"` | |||||
AdditionalFee string `json:"additionalFee" label:"续费"` | |||||
FirstAmount string `json:"firstAmount" label:"首件/首重"` | |||||
FirstFee string `json:"firstFee" label:"运费"` | |||||
} | |||||
// | |||||
type MallShippingTemplateListRespForCamel struct { | |||||
Id int `json:"id" label:"not null pk autoincr INT(11)"` | |||||
Name string `json:"name" label:"模板名称" binding:"required"` | |||||
RegionRules md.RegionRule `json:"regionRules"` | |||||
Data []MallShippingTemplateDataForCamel `json:"data" label:"运费数据" binding:"required"` | |||||
CalculateType int `json:"calculateType" label:"计费方式:1按重量 2按件数" binding:"required"` | |||||
CreateTime string `json:"createTime"` | |||||
} | |||||
type BaseGoods struct { | |||||
AuditReason string `json:"audit_reason"` | |||||
CategoryID int `json:"category_id"` | |||||
CategoryName string `json:"category_name"` | |||||
CustomProperty []interface{} `json:"custom_property"` | |||||
Detail []string `json:"detail"` | |||||
DetailURL []string `json:"detail_url"` | |||||
GoodsID int64 `json:"goods_id"` | |||||
GoodsType int `json:"goods_type"` | |||||
ImageList []string `json:"image_list"` | |||||
ImageListURL []string `json:"image_list_url"` | |||||
IsSpeImageInDetail int `json:"is_spe_image_in_detail"` | |||||
IsSpeImageOn int `json:"is_spe_image_on"` | |||||
MerchantID int `json:"merchant_id"` | |||||
Price string `json:"price"` | |||||
LinePrice string `json:"line_price"` | |||||
ProfitRate string `json:"profit_rate"` | |||||
SaleCount int `json:"sale_count"` | |||||
SaleStartTime string `json:"sale_start_time"` | |||||
SaleState int `json:"sale_state"` | |||||
SaleStateZh string `json:"sale_state_zh"` | |||||
Service []int `json:"service"` | |||||
ShippingFee string `json:"shipping_fee"` | |||||
ShippingFeeType int `json:"shipping_fee_type"` | |||||
ShippingTimeCustom string `json:"shipping_time_custom"` | |||||
ShippingTimeType int `json:"shipping_time_type"` | |||||
ShippingTplID int `json:"shipping_tpl_id"` | |||||
ShippingType int `json:"shipping_type"` | |||||
Spe []*Spe `json:"spe"` | |||||
SpeImages []string `json:"spe_images"` | |||||
SpeImagesURL []string `json:"spe_images_url"` | |||||
Stock int `json:"stock"` | |||||
Title string `json:"title"` | |||||
UpdateTime string `json:"update_time"` | |||||
GoodsCode string `json:"goods_code"` | |||||
} | |||||
type Spe struct { | |||||
Name string `json:"name"` | |||||
Values []string `json:"values"` | |||||
} | |||||
type SelectData struct { | |||||
CustomCategory []interface{} `json:"custom_category"` | |||||
GoodsTypeList []*SelectDataInsideGoodsTypeList `json:"goods_type_list"` | |||||
ServiceList []*SelectDataInsideServiceList `json:"service_list"` | |||||
ShippingTemplateList []*SelectDataInsideShippingTemplateList `json:"shipping_template_list"` | |||||
} | |||||
type SelectDataInsideGoodsTypeList struct { | |||||
Desc string `json:"desc"` | |||||
Name string `json:"name"` | |||||
Type int `json:"type"` | |||||
} | |||||
type SelectDataInsideServiceList struct { | |||||
Label string `json:"label"` | |||||
Value int `json:"value"` | |||||
} | |||||
type SelectDataInsideShippingTemplateList struct { | |||||
ShippingTemplateID int `json:"shipping_template_id"` | |||||
Name string `json:"name"` | |||||
} | |||||
type Sku struct { | |||||
SkuId int64 `json:"sku_id"` | |||||
GoodsID int64 `json:"goods_id"` | |||||
LinePrice string `json:"line_price"` | |||||
Price string `json:"price"` | |||||
Sku []*SkuInsideSku `json:"sku"` | |||||
Stock int `json:"stock"` | |||||
Weight string `json:"weight"` | |||||
SkuCode string `json:"sku_code"` | |||||
Indexes string `json:"indexes"` | |||||
SaleCount int `json:"sale_count"` | |||||
CloudChainSkuImgUrl string `json:"cloud_chain_sku_img_url"` | |||||
} | |||||
type SkuInsideSku struct { | |||||
Name string `json:"name"` | |||||
Value string `json:"value"` | |||||
} |
@@ -0,0 +1,93 @@ | |||||
package svc | |||||
import ( | |||||
"applet/app/db" | |||||
"applet/app/utils" | |||||
supplyMd "applet/mall/curl_supply/md" | |||||
"applet/mall/db/model" | |||||
"applet/supply/svc" | |||||
"bytes" | |||||
"encoding/json" | |||||
"errors" | |||||
"fmt" | |||||
"github.com/gin-gonic/gin" | |||||
"strconv" | |||||
"strings" | |||||
) | |||||
func CommGet(doMain, uniqueKey string) (map[string]string, error) { | |||||
if find := strings.Contains(doMain, "http://"); find { | |||||
doMain = strings.Replace(doMain, "http://", "", 1) | |||||
} | |||||
if find := strings.Contains(doMain, "https://"); find { | |||||
doMain = strings.Replace(doMain, "https://", "", 1) | |||||
} | |||||
var dm model.SupplyUserAppDomain | |||||
get, err := db.Db.Where("domain=?", doMain).Get(&dm) | |||||
if err != nil || !get { | |||||
utils.FilePutContents("CurlSupplyDomain", doMain) | |||||
return nil, errors.New("该域名未授权") | |||||
} | |||||
header := map[string]string{ | |||||
"Content-Type": "application/json", | |||||
"Authorization": uniqueKey, | |||||
"master_id": strconv.Itoa(dm.Uuid), | |||||
} | |||||
return header, nil | |||||
} | |||||
func CurlSupplyGoodsDetails(c *gin.Context, supplierGoodsId string) (*supplyMd.SupplierGoodsDetail, error) { | |||||
var ( | |||||
goodsDetail supplyMd.SupplierGoodsDetail | |||||
err error | |||||
) | |||||
doMain := svc.SysCfgGet(c, supplyMd.SupplierDomain) | |||||
url := doMain + "/api/open/goods/detail?id=%s" | |||||
url = fmt.Sprintf(url, supplierGoodsId) | |||||
uniqueKey := svc.SysCfgGet(c, supplyMd.SupplierUniqueKey) | |||||
if doMain == "" || uniqueKey == "" { | |||||
err = errors.New("请前往供应链平台获取domain和uniqueKey,完成供应链设置") | |||||
return nil, err | |||||
} | |||||
header, err := CommGet(doMain, uniqueKey) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
bytes1, err := utils.CurlGet(url, header) | |||||
if err != nil { | |||||
err = errors.New("请前往供应链平台获取domain和uniqueKey,完成供应链设置") | |||||
return nil, err | |||||
} | |||||
//resp := make(map[string]interface{}) | |||||
//utils.Unserialize(bytes1, &resp) | |||||
//fmt.Println("$$$$$$$$$$$$$$->:" + utils.SerializeStr(resp) + ":<-$$$$$$$$$$$$$$") | |||||
respJson := make(map[string]interface{}) | |||||
d := json.NewDecoder(bytes.NewReader(bytes1)) | |||||
d.UseNumber() | |||||
err = d.Decode(&respJson) | |||||
if err != nil { | |||||
fmt.Println(err) | |||||
} | |||||
fmt.Println("222222222222->:" + utils.SerializeStr(respJson) + ":<-22222222") | |||||
_, ok := respJson["data"] | |||||
if ok { | |||||
utils.Unserialize([]byte(utils.SerializeStr(respJson["data"])), &goodsDetail) | |||||
} else { | |||||
msg, ok1 := respJson["msg"] | |||||
if ok1 { | |||||
err = errors.New(utils.AnyToString(msg)) | |||||
} else { | |||||
err = errors.New("获取供应链商品信息失败,请前往供应链检查商品信息1!") | |||||
} | |||||
return nil, err | |||||
} | |||||
if goodsDetail.BaseGoods.GoodsID > 0 { | |||||
return &goodsDetail, nil | |||||
} | |||||
msg, ok1 := respJson["msg"] | |||||
if ok1 { | |||||
err = errors.New(utils.AnyToString(msg)) | |||||
} else { | |||||
err = errors.New("获取供应链商品信息失败,请前往供应链检查商品信息2!") | |||||
} | |||||
return nil, err | |||||
} |
@@ -0,0 +1,30 @@ | |||||
package db | |||||
import ( | |||||
"applet/mall/db/model" | |||||
"xorm.io/xorm" | |||||
) | |||||
func AddOrUpdateMallSkuAddPriceRule(session *xorm.Session, addPriceRule, supplyGoodsSkuCode string) error { | |||||
var rule model.MallSkuAddPriceRule | |||||
get, err := session.Where("supply_goods_sku_code = ?", supplyGoodsSkuCode).Get(&rule) | |||||
if err != nil { | |||||
return err | |||||
} | |||||
if get { | |||||
rule.AddPriceRule = addPriceRule | |||||
_, err := session.ID(rule.Id).Update(&rule) | |||||
if err != nil { | |||||
return err | |||||
} | |||||
return nil | |||||
} else { | |||||
rule.AddPriceRule = addPriceRule | |||||
rule.SupplyGoodsSkuCode = supplyGoodsSkuCode | |||||
one, err := session.InsertOne(&rule) | |||||
if err != nil || one == 0 { | |||||
return err | |||||
} | |||||
return nil | |||||
} | |||||
} |
@@ -0,0 +1,72 @@ | |||||
package db | |||||
import ( | |||||
"applet/mall/db/model" | |||||
"xorm.io/xorm" | |||||
"applet/app/utils/logx" | |||||
) | |||||
// 系统配置get | |||||
func DbsSysCfgGetAll(eg *xorm.Engine) (*[]model.SysCfg, error) { | |||||
var cfgList []model.SysCfg | |||||
if err := eg.Cols("`key`,`val`").Find(&cfgList); err != nil { | |||||
return nil, logx.Error(err) | |||||
} | |||||
return &cfgList, nil | |||||
} | |||||
// 获取一条记录 | |||||
func DbsSysCfgGet(eg *xorm.Engine, key string) (*model.SysCfg, error) { | |||||
var cfgList model.SysCfg | |||||
if has, err := eg.Where("`key`=?", key).Get(&cfgList); err != nil || has == false { | |||||
return nil, logx.Error(err) | |||||
} | |||||
return &cfgList, nil | |||||
} | |||||
func NewDbsSysCfgGet(eg *xorm.Engine, key string) (*model.SysCfg, error) { | |||||
var cfgList model.SysCfg | |||||
if _, err := eg.Where("`key`=?", key).Get(&cfgList); err != nil { | |||||
return nil, logx.Error(err) | |||||
} | |||||
return &cfgList, nil | |||||
} | |||||
//写入或更新 | |||||
func DbsSysCfgInsertANDUPDATE(eg *xorm.Engine, key, val string) bool { | |||||
latest, err := DbsSysCfgGet(eg, key) | |||||
if err != nil { | |||||
return false | |||||
} | |||||
if latest == nil { | |||||
cfg := model.SysCfg{Key: key, Val: val} | |||||
has, _ := eg.Insert(cfg) | |||||
if has > 0 { | |||||
return true | |||||
} | |||||
return false | |||||
} | |||||
bools := DbsSysCfgUpdate(eg, key, val) | |||||
return bools | |||||
} | |||||
func DbsSysCfgInsert(eg *xorm.Engine, key, val string) bool { | |||||
cfg := model.SysCfg{Key: key, Val: val} | |||||
_, err := eg.Where("`key`=?", key).Cols("val,memo").Update(&cfg) | |||||
if err != nil { | |||||
logx.Error(err) | |||||
return false | |||||
} | |||||
return true | |||||
} | |||||
func DbsSysCfgUpdate(eg *xorm.Engine, key, val string) bool { | |||||
cfg := model.SysCfg{Key: key, Val: val} | |||||
_, err := eg.Where("`key`=?", key).Cols("val").Update(&cfg) | |||||
if err != nil { | |||||
logx.Error(err) | |||||
return false | |||||
} | |||||
return true | |||||
} |
@@ -0,0 +1,92 @@ | |||||
package model | |||||
import ( | |||||
"time" | |||||
) | |||||
type MallGoods struct { | |||||
GoodsId int `json:"goods_id" xorm:"not null pk autoincr comment('商品id') INT(11)"` | |||||
GoodsType int `json:"goods_type" xorm:"not null default 1 comment('商品类型:1实物商品') TINYINT(1)"` | |||||
Title string `json:"title" xorm:"not null default '' comment('商品标题') VARCHAR(1024)"` | |||||
CategoryId int `json:"category_id" xorm:"not null default 0 comment('类目ID') INT(11)"` | |||||
ShareText string `json:"share_text" xorm:"not null default '' comment('分享描述') VARCHAR(2048)"` | |||||
ImageList string `json:"image_list" xorm:"comment('商品图json') TEXT"` | |||||
CommissionRate string `json:"commission_rate" xorm:"not null default 0.0000 comment('佣金比例') DECIMAL(6,4)"` | |||||
IcbcCommissionRate string `json:"icbc_commission_rate" xorm:"not null default 0.0000 comment('佣金比例') DECIMAL(6,4)"` | |||||
IntegralRate string `json:"integral_rate" xorm:"not null default 0.0000 comment('积分分佣比例') DECIMAL(6,4)"` | |||||
Price string `json:"price" xorm:"not null default 0.00 comment('价格') DECIMAL(12,2)"` | |||||
BuyNeedDeductBili string `json:"buy_need_deduct_bili" xorm:"not null default 0.00 comment('抵扣券最大比例') DECIMAL(12,2)"` | |||||
LinePrice string `json:"line_price" xorm:"not null default 0.00 comment('划线价') DECIMAL(12,2)"` | |||||
VipDiscountType int `json:"vip_discount_type" xorm:"not null default 1 comment('会员优惠方式:1打折 2减价 3指定价格') TINYINT(3)"` | |||||
VipDiscountPrice string `json:"vip_discount_price" xorm:"comment('会员优惠价信息') TEXT"` | |||||
Stock int `json:"stock" xorm:"not null default 0 comment('库存') INT(255)"` | |||||
Weight string `json:"weight" xorm:"not null default 0.0000 comment('重量') DECIMAL(12,4)"` | |||||
ShippingType int `json:"shipping_type" xorm:"not null default 1 comment('配送方式:1快递') TINYINT(1)"` | |||||
ShippingFeeType int `json:"shipping_fee_type" xorm:"not null default 1 comment('运费计费方式:1统一运费 2运费模板') TINYINT(1)"` | |||||
ShippingTplId int `json:"shipping_tpl_id" xorm:"not null default 0 comment('运费模板id') INT(11)"` | |||||
ShippingFee string `json:"shipping_fee" xorm:"not null default 0.00 comment('统一运费的值') DECIMAL(12,2)"` | |||||
SaleState int `json:"sale_state" xorm:"not null default 1 comment('销售状态:1上架(立即销售)2定时销售 3放入仓库') TINYINT(1)"` | |||||
SaleStartTime time.Time `json:"sale_start_time" xorm:"comment('定时开售时间') DATETIME"` | |||||
IsLimitBuy int `json:"is_limit_buy" xorm:"not null default 0 comment('是否限购:0否 1是') TINYINT(1)"` | |||||
LimitBuyData string `json:"limit_buy_data" xorm:"not null default '' comment('限购数据:type:1:终身限购n件 2:按周期x限购y件') VARCHAR(5012)"` | |||||
CreateTime time.Time `json:"create_time" xorm:"created not null default 'CURRENT_TIMESTAMP' comment('创建时间') DATETIME"` | |||||
UpdateTime time.Time `json:"update_time" xorm:"updated not null default 'CURRENT_TIMESTAMP' comment('更新时间') DATETIME"` | |||||
OfficialProperty string `json:"official_property" xorm:"not null comment('官方参数json') TEXT"` | |||||
CustomProperty string `json:"custom_property" xorm:"not null comment('自定义参数json') TEXT"` | |||||
Spe string `json:"spe" xorm:"not null default '' comment('所有规格属性json') VARCHAR(5012)"` | |||||
SpeImages string `json:"spe_images" xorm:"not null default '' comment('第一组规格值对应的图片json') VARCHAR(1024)"` | |||||
Detail string `json:"detail" xorm:"comment('商品详情') TEXT"` | |||||
VipDiscountSwitch int `json:"vip_discount_switch" xorm:"not null default 0 comment('是否开启会员价:0关闭 1开启') TINYINT(1)"` | |||||
Sort int `json:"sort" xorm:"not null default 0 comment('序号') INT(11)"` | |||||
ActivityType int `json:"activity_type" xorm:"not null default 0 comment('参与的活动:0未参加活动 1拼团活动 2秒杀活动 3砍价活动 4超级拼团 5会员升级礼包') TINYINT(3)"` | |||||
SaleCount int `json:"sale_count" xorm:"not null default 0 comment('销量') INT(11)"` | |||||
IsSpeImageOn int `json:"is_spe_image_on" xorm:"not null default 0 comment('是否开启规格图片:0否 1是') TINYINT(1)"` | |||||
IsSpeImageInDetail int `json:"is_spe_image_in_detail" xorm:"not null default 0 comment('规格图是否显示在详情页') TINYINT(1)"` | |||||
DeleteAt time.Time `json:"delete_at" xorm:"comment('商品删除字段,不为null时,表示删除') DATETIME"` | |||||
Service string `json:"service" xorm:"comment('支持的服务json,id数组') VARCHAR(512)"` | |||||
IsVirtualCoin int `json:"is_virtual_coin" xorm:"comment('是否支持虚拟币') TINYINT(1)"` | |||||
VirtualCoinId int `json:"virtual_coin_id" xorm:"comment('支持支付的虚拟币id') INT(11)"` | |||||
RepurchaseDiscount string `json:"repurchase_discount" xorm:"default 0.00 comment('复购折扣') DECIMAL(12,2)"` | |||||
UserLevel int `json:"user_level" xorm:"default 0 comment('升级等级') INT(11)"` | |||||
UserLevelDay int `json:"user_level_day" xorm:"default 0 comment('升级等级天数 0为永久') INT(11)"` | |||||
IsHasRepurchase int `json:"is_has_repurchase" xorm:"default 0 comment('是否有复购') INT(1)"` | |||||
RepurchaseType int `json:"repurchase_type" xorm:"default 0 comment('复购类型 0购买相同商品为复购 1购买商品赠送相同会员等级为复购') INT(11)"` | |||||
IsHasUserLevel int `json:"is_has_user_level" xorm:"default 0 comment('是否有会员权益') INT(1)"` | |||||
UserLevelGivenStatus int `json:"user_level_given_status" xorm:"default 0 comment('会员礼包赠送权益订单状态') INT(11)"` | |||||
UserLevelUpTimeType int `json:"user_level_up_time_type" xorm:"default 0 comment('升级礼包升级方式 0过期时间延长 1当前时间延长') INT(11)"` | |||||
UserLevelCanRefund int `json:"user_level_can_refund" xorm:"default 0 comment('会员礼包是否能退货') INT(1)"` | |||||
SeckillStock int `json:"seckill_stock" xorm:"default 0 comment('秒杀库存(总)') INT(11)"` | |||||
VideoUrl string `json:"video_url" xorm:"not null default '' comment('视频链接') VARCHAR(255)"` | |||||
VideoHeight string `json:"video_height" xorm:"not null default '' comment('视频高度') VARCHAR(100)"` | |||||
GoodsSourceUrl string `json:"goods_source_url" xorm:"not null default '' comment('商品来源链接') TEXT"` | |||||
SalesStoreName string `json:"sales_store_name" xorm:"not null default '' comment('经销商名称') VARCHAR(100)"` | |||||
Pvd int `json:"pvd" xorm:"default 1 comment('来源:1:商家自己发布,2:来自于供应商') TINYINT(1)"` | |||||
SupplierGoodsId int64 `json:"supplier_goods_id" xorm:"comment('供应商的商品id') INT(11)"` | |||||
SupplierMerchantId int `json:"supplier_merchant_id" xorm:"comment('供应商id') INT(11)"` | |||||
SupplierMerchantName string `json:"supplier_merchant_name" xorm:"comment('供应商名称') VARCHAR(255)"` | |||||
SupplierCategoryId int `json:"supplier_category_id" xorm:"comment('供应商分类id') INT(11)"` | |||||
SupplierGoodsUpdateTime string `json:"supplier_goods_update_time" xorm:"default '' VARCHAR(100)"` | |||||
OriginalPrice string `json:"original_price" xorm:"comment('原价(指站长库的价格)') DECIMAL(12,4)"` | |||||
AddPrice string `json:"add_price" xorm:"comment('加价(指在原价上加多少钱)') DECIMAL(10,2)"` | |||||
SupplierMerchantAvatar string `json:"supplier_merchant_avatar" xorm:"comment('供应链商家头像') VARCHAR(255)"` | |||||
AddPriceRule string `json:"add_price_rule" xorm:"comment('加价规则json') TEXT"` | |||||
SupplierGoodsCode string `json:"supplier_goods_code" xorm:"comment('供应商的商品编号') VARCHAR(255)"` | |||||
Msg string `json:"msg" xorm:"comment('商品的错误信息或其他内容') TEXT"` | |||||
RewardGuessCardNum int `json:"reward_guess_card_num" xorm:"default 0 comment('奖励拆猜卡数量') INT(11)"` | |||||
LimitJoinType string `json:"limit_join_type" xorm:"not null default '' comment('条件限制json') VARCHAR(1024)"` | |||||
LevelType string `json:"level_type" xorm:"not null default '' comment('等级限制json') VARCHAR(1024)"` | |||||
AgentRule string `json:"agent_rule" xorm:"not null default '' comment('等级限制json') VARCHAR(5012)"` | |||||
IntegralRule string `json:"integral_rule" xorm:"not null default '' comment('等级限制json') VARCHAR(500)"` | |||||
IntegralPayType int `json:"integral_pay_type" xorm:"default 0 comment('积分支付方式 0固定积分金额 1积分不够余额付') INT(1)"` | |||||
IsVirtualGoods int `json:"is_virtual_goods" xorm:"default 0 comment('') INT(1)"` | |||||
RewardVirtualCoinId int `json:"reward_virtual_coin_id" xorm:"default 0 comment('') INT(11)"` | |||||
RewardVirtualUserLv string `json:"reward_virtual_user_lv" xorm:"not null comment('自定义参数json') TEXT"` | |||||
BaseCommission string `json:"base_commission" xorm:"comment('积分兑换模式基数') DECIMAL(12,4)"` | |||||
PlatformCostPrice string `json:"platform_cost_price" xorm:"comment('平台成本价') DECIMAL(12,4)"` | |||||
GroupRuleData string `json:"group_rule_data" xorm:" comment('拼团数据 json') TEXT"` | |||||
BelongCity string `json:"belong_city" xorm:"not null default '' comment('所属城市 大陆mainland 澳门 macao') VARCHAR(255)"` | |||||
VirtualGoodsInfo string `json:"virtual_goods_info" xorm:"not null default '' comment('') VARCHAR(1024)"` | |||||
ConsignForSaleInfo string `json:"consign_for_sale_info" xorm:"not null default '' comment('') VARCHAR(1024)"` | |||||
ConsignForSaleAddCart int `json:"consign_for_sale_add_cart" xorm:"default 0 comment('') INT(11)"` | |||||
IsJuShuiTan int `json:"is_ju_shui_tan" xorm:"default 0 comment('') INT(11)"` | |||||
} |
@@ -0,0 +1,62 @@ | |||||
package model | |||||
import ( | |||||
"time" | |||||
) | |||||
type MallOfficialGoods struct { | |||||
GoodsId int `json:"goods_id" xorm:"not null pk autoincr comment('商品id') INT(11)"` | |||||
GoodsType int `json:"goods_type" xorm:"not null default 1 comment('商品类型:1实物商品') TINYINT(1)"` | |||||
Title string `json:"title" xorm:"not null default '' comment('商品标题') VARCHAR(1024)"` | |||||
CategoryId int `json:"category_id" xorm:"not null default 0 comment('类目ID') INT(11)"` | |||||
ShareText string `json:"share_text" xorm:"not null default '' comment('分享描述') VARCHAR(2048)"` | |||||
ImageList string `json:"image_list" xorm:"comment('商品图json') TEXT"` | |||||
Price string `json:"price" xorm:"not null default 0.00 comment('价格(original_price+add_price)站长实收数值') DECIMAL(12,2)"` | |||||
Discount string `json:"discount" xorm:"default 10.00 comment('折扣,为10无折扣') DECIMAL(6,2)"` | |||||
DiscountPrice string `json:"discount_price" xorm:"not null default 0.00 comment('折扣价') DECIMAL(12,2)"` | |||||
VipDiscountType int `json:"vip_discount_type" xorm:"not null default 1 comment('会员优惠方式:0原价 1会员 2折扣') TINYINT(3)"` | |||||
VipPrice string `json:"vip_price" xorm:"comment('会员价,会员等级对应的价格值json:格式为:[{"level_id":1,"value":10}]') TEXT"` | |||||
Stock int `json:"stock" xorm:"not null default 0 comment('库存') INT(255)"` | |||||
Weight string `json:"weight" xorm:"not null default 0.0000 comment('重量') DECIMAL(12,4)"` | |||||
ShippingType int `json:"shipping_type" xorm:"not null default 1 comment('配送方式:1快递') TINYINT(1)"` | |||||
ShippingFeeType int `json:"shipping_fee_type" xorm:"not null default 1 comment('运费计费方式:1统一运费 2运费模板') TINYINT(1)"` | |||||
ShippingTplId int `json:"shipping_tpl_id" xorm:"not null default 0 comment('运费模板id') INT(11)"` | |||||
ShippingFee string `json:"shipping_fee" xorm:"not null default 0.00 comment('统一运费的值') DECIMAL(12,2)"` | |||||
SaleState int `json:"sale_state" xorm:"not null default 1 comment('销售状态:1上架(立即销售)2定时销售 3放入仓库') TINYINT(1)"` | |||||
SaleStartTime time.Time `json:"sale_start_time" xorm:"comment('定时开售时间') DATETIME"` | |||||
IsLimitBuy int `json:"is_limit_buy" xorm:"not null default 0 comment('是否限购:0否 1是') TINYINT(1)"` | |||||
LimitBuyData string `json:"limit_buy_data" xorm:"not null default '' comment('限购数据:type:1:终身限购n件 2:按周期x限购y件') VARCHAR(5012)"` | |||||
CreateTime time.Time `json:"create_time" xorm:"not null default 'CURRENT_TIMESTAMP' comment('创建时间') DATETIME"` | |||||
UpdateTime time.Time `json:"update_time" xorm:"not null default 'CURRENT_TIMESTAMP' comment('更新时间') DATETIME"` | |||||
OfficialProperty string `json:"official_property" xorm:"not null comment('官方参数json') TEXT"` | |||||
CustomProperty string `json:"custom_property" xorm:"not null comment('自定义参数json') TEXT"` | |||||
Spe string `json:"spe" xorm:"not null default '' comment('所有规格属性json') VARCHAR(5012)"` | |||||
SpeImages string `json:"spe_images" xorm:"not null default '' comment('第一组规格值对应的图片json') VARCHAR(1024)"` | |||||
Detail string `json:"detail" xorm:"comment('商品详情') TEXT"` | |||||
Sort int `json:"sort" xorm:"not null default 0 comment('序号') INT(11)"` | |||||
ActivityType int `json:"activity_type" xorm:"not null default 0 comment('参与的活动:0未参加活动 1拼团活动 2秒杀活动 3砍价活动 4超级拼团') TINYINT(3)"` | |||||
SaleCount int `json:"sale_count" xorm:"not null default 0 comment('销量') INT(11)"` | |||||
IsSpeImageOn int `json:"is_spe_image_on" xorm:"not null default 0 comment('是否开启规格图片:0否 1是') TINYINT(1)"` | |||||
IsSpeImageInDetail int `json:"is_spe_image_in_detail" xorm:"not null default 0 comment('规格图是否显示在详情页') TINYINT(1)"` | |||||
DeleteAt time.Time `json:"delete_at" xorm:"comment('商品删除字段,不为null时,表示删除') DATETIME"` | |||||
Service string `json:"service" xorm:"comment('支持的服务json,id数组') VARCHAR(512)"` | |||||
IsVirtualCoin int `json:"is_virtual_coin" xorm:"comment('是否支持虚拟币') TINYINT(1)"` | |||||
VirtualCoinId int `json:"virtual_coin_id" xorm:"comment('支持支付的虚拟币id') INT(11)"` | |||||
StoreId int `json:"store_id" xorm:"not null comment('店铺id') INT(11)"` | |||||
IsSingleSku int `json:"is_single_sku" xorm:"not null default 1 comment('是否单规格,0:否,1是') TINYINT(1)"` | |||||
CommissionRate string `json:"commission_rate" xorm:"not null default 0.0000 comment('佣金比例') DECIMAL(6,4)"` | |||||
AuditState int `json:"audit_state" xorm:"default 1 comment('审核状态:1未审核 2通过 3失败') TINYINT(1)"` | |||||
AuditRemark string `json:"audit_remark" xorm:"comment('审核备注') VARCHAR(255)"` | |||||
StockDeductionWay int `json:"stock_deduction_way" xorm:"default 1 comment('库存减扣方式 1:拍下减库存,') TINYINT(1)"` | |||||
IsReturnWithoutReason int `json:"is_return_without_reason" xorm:"default 0 comment('是否7天无理由退货(0否,1是)') TINYINT(1)"` | |||||
IsManufactorDelivery int `json:"is_manufactor_delivery" xorm:"default 0 comment('是否厂家配送(0:否,1是)') TINYINT(1)"` | |||||
IsOfficialFidelity int `json:"is_official_fidelity" xorm:"default 0 comment('是官方保真(0:否,1:是)') TINYINT(1)"` | |||||
Pvd int `json:"pvd" xorm:"comment('来源:1:商家自己发布,2:来自于供应商') TINYINT(1)"` | |||||
SupplierGoodsId int `json:"supplier_goods_id" xorm:"comment('供应商的商品id') INT(11)"` | |||||
SupplierMerchantId int `json:"supplier_merchant_id" xorm:"comment('供应商id') INT(11)"` | |||||
SupplierMerchantName string `json:"supplier_merchant_name" xorm:"comment('供应商名称') VARCHAR(255)"` | |||||
SupplierCategoryId int `json:"supplier_category_id" xorm:"comment('供应商分类id') INT(11)"` | |||||
OriginalPrice string `json:"original_price" xorm:"comment('原价(指供应链的价格)') DECIMAL(12,4)"` | |||||
AddPrice string `json:"add_price" xorm:"comment('加价(指在原价上加多少钱)') DECIMAL(10,2)"` | |||||
LinePrice string `json:"line_price" xorm:"comment('指导价(指供应链的价格)') DECIMAL(12,4)"` | |||||
} |
@@ -0,0 +1,31 @@ | |||||
package model | |||||
import ( | |||||
"time" | |||||
) | |||||
type MallOfficialSku struct { | |||||
SkuId int64 `json:"sku_id" xorm:"not null pk autoincr BIGINT(20)"` | |||||
GoodsId int `json:"goods_id" xorm:"not null comment('商品id') INT(11)"` | |||||
Price string `json:"price" xorm:"not null default 0.00 comment('价格(original_price+add_price)站长实收数值') DECIMAL(12,2)"` | |||||
CreateTime time.Time `json:"create_time" xorm:"default 'CURRENT_TIMESTAMP' comment('创建时间') DATETIME"` | |||||
UpdateTime time.Time `json:"update_time" xorm:"default 'CURRENT_TIMESTAMP' comment('更新时间') DATETIME"` | |||||
VipPrice string `json:"vip_price" xorm:"comment('会员价,会员等级对应的价格值json:格式为:') VARCHAR(5012)"` | |||||
Weight string `json:"weight" xorm:"not null default 0.0000 comment('重量') DECIMAL(12,4)"` | |||||
Stock int `json:"stock" xorm:"not null default 0 comment('库存') INT(11)"` | |||||
Indexes string `json:"indexes" xorm:"not null default '' comment('规格值组合的下标') VARCHAR(100)"` | |||||
Sku string `json:"sku" xorm:"not null comment('规格组合json') VARCHAR(2048)"` | |||||
SaleCount int `json:"sale_count" xorm:"not null default 0 comment('销量') INT(11)"` | |||||
Sort int `json:"sort" xorm:"not null default 0 comment('排序') INT(11)"` | |||||
ActivityPrice string `json:"activity_price" xorm:"not null default 0.00 comment('活动价格') DECIMAL(12,2)"` | |||||
VirtualCoinPrice string `json:"virtual_coin_price" xorm:"comment('虚拟币购买价(剩余部分按比例折算成虚拟币)') DECIMAL(12,2)"` | |||||
VirtualCoinVipPrice string `json:"virtual_coin_vip_price" xorm:"comment('虚拟币支付会员折扣设置') VARCHAR(5012)"` | |||||
Discount string `json:"discount" xorm:"default 10.00 comment('折扣,为10无折扣') DECIMAL(6,2)"` | |||||
DiscountPrice string `json:"discount_price" xorm:"default 0.00 comment('折扣价') DECIMAL(12,2)"` | |||||
VipDiscountType int `json:"vip_discount_type" xorm:"default 0 comment('会员优惠方式:0原价 1会员 2折扣') INT(11)"` | |||||
OriginalPrice string `json:"original_price" xorm:"comment('原价(指供应链的价格)') DECIMAL(12,4)"` | |||||
AddPrice string `json:"add_price" xorm:"comment('加价(指在原价上加多少钱)') DECIMAL(10,2)"` | |||||
SupplierGoodsId int `json:"supplier_goods_id" xorm:"comment('供应商的商品id') INT(11)"` | |||||
SupplierSkuId int `json:"supplier_sku_id" xorm:"comment('供应商的skuid') INT(11)"` | |||||
LinePrice string `json:"line_price" xorm:"comment('指导价(指供应链的价格)') DECIMAL(12,4)"` | |||||
} |
@@ -0,0 +1,37 @@ | |||||
package model | |||||
import ( | |||||
"time" | |||||
) | |||||
type MallSku struct { | |||||
SkuId int64 `json:"sku_id" xorm:"not null pk autoincr BIGINT(20)"` | |||||
GoodsId int `json:"goods_id" xorm:"not null comment('商品id') INT(11)"` | |||||
Price string `json:"price" xorm:"not null default 0.00 comment('价格') DECIMAL(12,2)"` | |||||
CreateTime time.Time `json:"create_time" xorm:"created default 'CURRENT_TIMESTAMP' comment('创建时间') DATETIME"` | |||||
UpdateTime time.Time `json:"update_time" xorm:"updated default 'CURRENT_TIMESTAMP' comment('更新时间') DATETIME"` | |||||
VipPrice string `json:"vip_price" xorm:"comment('会员等级对应的价格值json') VARCHAR(5012)"` | |||||
Weight string `json:"weight" xorm:"not null default 0.0000 comment('重量') DECIMAL(12,4)"` | |||||
Stock int `json:"stock" xorm:"not null default 0 comment('库存') INT(11)"` | |||||
SeckillStock int `json:"seckill_stock" xorm:"default 0 comment('秒杀库存') INT(11)"` | |||||
SkuCode string `json:"sku_code" xorm:"not null default '' comment('规格值组合的下标') VARCHAR(100)"` | |||||
Indexes string `json:"indexes" xorm:"not null default '' comment('规格值组合的下标') VARCHAR(100)"` | |||||
Sku string `json:"sku" xorm:"not null comment('规格组合json') VARCHAR(2048)"` | |||||
SaleCount int `json:"sale_count" xorm:"not null default 0 comment('销量') INT(11)"` | |||||
Sort int `json:"sort" xorm:"not null default 0 comment('排序') INT(11)"` | |||||
ActivityPrice string `json:"activity_price" xorm:"not null default 0.00 comment('活动价格') DECIMAL(12,2)"` | |||||
VirtualCoinPrice string `json:"virtual_coin_price" xorm:"comment('虚拟币购买价(剩余部分按比例折算成虚拟币)') DECIMAL(12,2)"` | |||||
VirtualCoinVipPrice string `json:"virtual_coin_vip_price" xorm:"comment('虚拟币支付会员折扣设置') VARCHAR(5012)"` | |||||
UserLevel int `json:"user_level" xorm:"not null default 0 comment('升级等级') INT(11)"` | |||||
UserLevelDay int `json:"user_level_day" xorm:"not null default 0 comment('升级等级天数 0为永久') INT(11)"` | |||||
IsHasRepurchase int `json:"is_has_repurchase" xorm:"not null default 0 comment('是否有复购') INT(1)"` | |||||
RepurchaseType int `json:"repurchase_type" xorm:"not null default 0 comment('复购类型 0购买相同商品为复购 1购买商品赠送相同会员等级为复购') INT(11)"` | |||||
RepurchaseDiscount string `json:"repurchase_discount" xorm:"comment('虚拟币购买价(复购折扣)') DECIMAL(12,2)"` | |||||
SupplierGoodsId int64 `json:"supplier_goods_id" xorm:"comment('供应商的商品id') INT(11)"` | |||||
SupplierSkuId int64 `json:"supplier_sku_id" xorm:"comment('供应商的skuid') INT(11)"` | |||||
OriginalPrice string `json:"original_price" xorm:"comment('原价(指站长库的价格)') DECIMAL(12,4)"` | |||||
AddPrice string `json:"add_price" xorm:"comment('加价(指在原价上加多少钱)') DECIMAL(10,2)"` | |||||
SupplierSkuCode string `json:"supplier_sku_code" xorm:"comment('供应商的SKU编号') VARCHAR(255)"` | |||||
CloudChainSkuImgUrl string `json:"cloud_chain_sku_img_url" xorm:"comment('云链的产品SKU图片url') VARCHAR(255)"` | |||||
RewardCoinAmount string `json:"reward_coin_amount" xorm:"comment('') DECIMAL(12,4)"` | |||||
} |
@@ -0,0 +1,13 @@ | |||||
package model | |||||
import ( | |||||
"time" | |||||
) | |||||
type MallSkuAddPriceRule struct { | |||||
Id int64 `json:"id" xorm:"pk autoincr BIGINT(22)"` | |||||
SupplyGoodsSkuCode string `json:"supply_goods_sku_code" xorm:"not null comment('供应链good-sku编码(唯一)') unique VARCHAR(600)"` | |||||
AddPriceRule string `json:"add_price_rule" xorm:"not null default '' comment('加价规则') VARCHAR(600)"` | |||||
CreateTime time.Time `json:"create_time" xorm:"created not null default 'CURRENT_TIMESTAMP' comment('创建时间') DATETIME"` | |||||
UpdateTime time.Time `json:"update_time" xorm:"updated not null default 'CURRENT_TIMESTAMP' comment('更新时间') DATETIME"` | |||||
} |
@@ -0,0 +1,8 @@ | |||||
package model | |||||
type SupplyUserAppDomain struct { | |||||
Domain string `json:"domain" xorm:"not null pk comment('绑定域名') VARCHAR(100)"` | |||||
Uuid int `json:"uuid" xorm:"not null comment('对应APP ID编号') index unique(IDX_UUID_TYPE) INT(10)"` | |||||
Type string `json:"type" xorm:"not null comment('api接口域名,wap.h5域名,admin管理后台') unique(IDX_UUID_TYPE) ENUM('admin','api','pc','wap')"` | |||||
IsSsl int `json:"is_ssl" xorm:"not null default 0 comment('是否开启ssl:0否;1是') TINYINT(255)"` | |||||
} |
@@ -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)"` | |||||
} |
@@ -0,0 +1,69 @@ | |||||
package md | |||||
import "time" | |||||
type OfficialGoods struct { | |||||
ID string `json:"id"` | |||||
Price string `json:"price"` | |||||
LinePrice string `json:"line_price"` | |||||
Weight string `json:"weight"` | |||||
Profit string `json:"profit"` | |||||
CreateTime string `json:"create_time"` | |||||
UpdateTime string `json:"update_time"` | |||||
Stock int `json:"stock"` | |||||
SkuList []*OfficialSku `json:"skuList"` | |||||
SaleCount int `json:"sale_count"` | |||||
Title string `json:"title"` | |||||
Image string `json:"image"` | |||||
StateText string `json:"state_text"` | |||||
SaleState int `json:"sale_state"` | |||||
SaleStartTime string `json:"sale_start_time"` | |||||
StateCode int `json:"state_code"` | |||||
MerchantID int `json:"merchant_id"` | |||||
MerchantName string `json:"merchant_name"` | |||||
IsGoodsWarehouse string `json:"is_goods_warehouse"` | |||||
AuditReason string `json:"audit_reason"` | |||||
CategoryID string `json:"category_id"` | |||||
GoodsCode string `json:"goods_code"` | |||||
} | |||||
type OfficialSku struct { | |||||
SkuID int64 `json:"sku_id"` | |||||
GoodsID int64 `json:"goods_id"` | |||||
Price string `json:"price"` | |||||
CreateTime time.Time `json:"create_time"` | |||||
UpdateTime time.Time `json:"update_time"` | |||||
Weight string `json:"weight"` | |||||
Stock int `json:"stock"` | |||||
Indexes string `json:"indexes"` | |||||
Sku string `json:"sku"` | |||||
SaleCount int `json:"sale_count"` | |||||
LinePrice string `json:"line_price"` | |||||
SkuCode string `json:"sku_code"` | |||||
CloudChainSkuImgUrl string `json:"cloud_chain_sku_img_url"` | |||||
} | |||||
type OfficialGoodsConditions struct { //自营加价结构体 | |||||
Title string `json:"title"` | |||||
CategoryId string `json:"category_id"` | |||||
AddPriceType string `json:"add_price_type"` //1按金额增加2按比例增加 | |||||
AddPriceBase string `json:"add_price_base"` //加价基数 1按进货价2按利润空间 | |||||
AddPriceNum string `json:"add_price_num"` //类型是1就是直接加值,如果是2则是百分比 | |||||
NumType string `json:"num_type"` //价格数值设置 1小数点后两位2整数 | |||||
Ids []string `json:"ids"` //编辑商品id组 | |||||
IsSkuAddPrice string `json:"is_sku_add_price"` | |||||
SkusConditions []*OfficialSkuConditions `json:"skus_conditions"` | |||||
} | |||||
type OfficialSkuConditions struct { | |||||
AddPriceType string `json:"add_price_type"` //1按金额增加2按比例增加 | |||||
AddPriceBase string `json:"add_price_base"` //加价基数 1按进货价2按利润空间 | |||||
AddPriceNum string `json:"add_price_num"` //类型是1就是直接加值,如果是2则是百分比 | |||||
NumType string `json:"num_type"` //价格数值设置 1小数点后两位2整数 | |||||
SkuCode string `json:"sku_code"` | |||||
} | |||||
type Conditions struct { | |||||
AddPriceType string `json:"add_price_type"` //1按金额增加2按比例增加 | |||||
AddPriceBase string `json:"add_price_base"` //加价基数 1按进货价2按利润空间 | |||||
AddPriceNum string `json:"add_price_num"` //类型是1就是直接加值,如果是2则是百分比 | |||||
NumType string `json:"num_type"` //价格数值设置 1小数点后两位2整数 | |||||
} |
@@ -0,0 +1,192 @@ | |||||
package svc | |||||
import ( | |||||
"applet/app/utils" | |||||
"applet/app/utils/logx" | |||||
supplySvc "applet/mall/curl_supply/svc" | |||||
"applet/mall/db" | |||||
"applet/mall/db/model" | |||||
"applet/mall/md" | |||||
utils2 "applet/mall/utils" | |||||
"errors" | |||||
"github.com/gin-gonic/gin" | |||||
"xorm.io/xorm" | |||||
) | |||||
func AddOfficialGoods(c *gin.Context, engine *xorm.Engine, goods *md.OfficialGoods, conditions *md.OfficialGoodsConditions) error { | |||||
exist, err2 := engine.Where("supplier_goods_id = ?", goods.ID).Exist(new(model.MallGoods)) | |||||
if exist { | |||||
return errors.New("该商品已存在无需重复拉取") | |||||
} | |||||
details, err2 := supplySvc.CurlSupplyGoodsDetails(c, goods.ID) | |||||
if err2 != nil { | |||||
return err2 | |||||
} | |||||
if details.BaseGoods.GoodsID <= 0 { | |||||
return errors.New("错误的供应链商品id") | |||||
} | |||||
if details.BaseGoods.MerchantID <= 0 { | |||||
return errors.New("错误的供应链站长id") | |||||
} | |||||
for _, sku := range details.Skus { | |||||
if sku.GoodsID <= 0 || sku.SkuId <= 0 { | |||||
return errors.New("错误的供应链商品SkuId") | |||||
} | |||||
} | |||||
if details.BaseGoods.SaleState != 1 { | |||||
return errors.New("该商品不是销售中状态!") | |||||
} | |||||
session := engine.NewSession() | |||||
defer session.Close() | |||||
session.Begin() | |||||
var officialGoods model.MallGoods | |||||
officialGoods.CategoryId = utils.StrToInt(conditions.CategoryId) | |||||
officialGoods.Title = details.BaseGoods.Title | |||||
//officialGoods.AuditState = 2 | |||||
//imgStringSlice := make([]string, 0) | |||||
//imgStringSlice = append(imgStringSlice, goods.Image) | |||||
officialGoods.AddPriceRule = utils.SerializeStr(conditions) | |||||
var goodsConditions md.Conditions | |||||
goodsConditions.AddPriceBase = conditions.AddPriceBase | |||||
goodsConditions.AddPriceNum = conditions.AddPriceNum | |||||
goodsConditions.AddPriceType = conditions.AddPriceType | |||||
goodsConditions.NumType = conditions.NumType | |||||
officialGoods.ImageList = utils.SerializeStr(details.BaseGoods.ImageListURL) | |||||
officialGoods.GoodsType = details.BaseGoods.GoodsType | |||||
if details.BaseGoods.DetailURL == nil { | |||||
details.BaseGoods.DetailURL = make([]string, 0) | |||||
} | |||||
officialGoods.Detail = utils.SerializeStr(details.BaseGoods.DetailURL) | |||||
officialGoods.LinePrice = goods.LinePrice | |||||
officialGoods.SupplierMerchantId = details.BaseGoods.MerchantID | |||||
officialGoods.SupplierGoodsId = details.BaseGoods.GoodsID | |||||
officialGoods.SupplierCategoryId = details.BaseGoods.CategoryID | |||||
officialGoods.SupplierMerchantName = details.MallName | |||||
officialGoods.AddPrice = calculateAddPriceNum(goods.Price, goods.LinePrice, &goodsConditions) | |||||
officialGoods.SaleState = details.BaseGoods.SaleState | |||||
officialGoods.Stock = details.BaseGoods.Stock | |||||
officialGoods.Pvd = 2 | |||||
officialGoods.Weight = goods.Weight | |||||
officialGoods.SaleCount = details.BaseGoods.SaleCount | |||||
officialGoods.OriginalPrice = details.BaseGoods.Price | |||||
officialGoods.Price = utils.AnyToString(utils.StrToFloat64(officialGoods.AddPrice) + utils.StrToFloat64(officialGoods.OriginalPrice)) | |||||
officialGoods.Spe = utils.SerializeStr(details.BaseGoods.Spe) | |||||
officialGoods.SpeImages = utils.SerializeStr(details.BaseGoods.SpeImagesURL) | |||||
officialGoods.IsSpeImageInDetail = details.BaseGoods.IsSpeImageInDetail | |||||
officialGoods.IsSpeImageOn = details.BaseGoods.IsSpeImageOn | |||||
officialGoods.SaleStartTime = utils2.String2Time(details.BaseGoods.SaleStartTime) | |||||
officialGoods.VipDiscountType = 1 | |||||
officialGoods.VipDiscountPrice = "0" | |||||
officialGoods.SupplierGoodsCode = details.BaseGoods.GoodsCode | |||||
officialGoods.CustomProperty = utils.SerializeStr(details.BaseGoods.CustomProperty) | |||||
//todo 是否需要同步运费模板!! | |||||
officialGoods.ShippingFee = details.BaseGoods.ShippingFee | |||||
officialGoods.ShippingFeeType = details.BaseGoods.ShippingFeeType | |||||
officialGoods.ShippingTplId = details.BaseGoods.ShippingTplID | |||||
officialGoods.ShippingType = details.BaseGoods.ShippingType | |||||
officialGoods.SupplierMerchantAvatar = details.MallLogo | |||||
_, err := session.InsertOne(&officialGoods) | |||||
if err != nil { | |||||
session.Rollback() | |||||
return err | |||||
} | |||||
if officialGoods.GoodsId == 0 { | |||||
session.Rollback() | |||||
return errors.New("数据库操作失败!") | |||||
} | |||||
skuConditionsMap := make(map[string]*md.OfficialSkuConditions) | |||||
for _, condition := range conditions.SkusConditions { | |||||
skuConditionsMap[condition.SkuCode] = condition | |||||
} | |||||
for _, sku := range goods.SkuList { | |||||
var officialSku model.MallSku | |||||
officialSku.GoodsId = officialGoods.GoodsId | |||||
officialSku.SaleCount = sku.SaleCount | |||||
officialSku.Sku = sku.Sku | |||||
officialSku.SupplierGoodsId = sku.GoodsID | |||||
officialSku.SupplierSkuId = sku.SkuID | |||||
officialSku.Indexes = sku.Indexes | |||||
officialSku.Weight = sku.Weight | |||||
officialSku.Stock = sku.Stock | |||||
var skuConditions md.Conditions | |||||
skuConditions.AddPriceBase = conditions.AddPriceBase | |||||
skuConditions.AddPriceNum = conditions.AddPriceNum | |||||
skuConditions.AddPriceType = conditions.AddPriceType | |||||
skuConditions.NumType = conditions.NumType | |||||
if conditions.IsSkuAddPrice == "1" { | |||||
officialSkuConditions, ok := skuConditionsMap[sku.SkuCode] | |||||
if !ok { | |||||
session.Rollback() | |||||
return errors.New("SKU编码对应的加价信息获取失败或SKU编码不一致,无法加价!") | |||||
} | |||||
skuConditions.AddPriceBase = officialSkuConditions.AddPriceBase | |||||
skuConditions.AddPriceNum = officialSkuConditions.AddPriceNum | |||||
skuConditions.AddPriceType = officialSkuConditions.AddPriceType | |||||
skuConditions.NumType = officialSkuConditions.NumType | |||||
} | |||||
officialSku.OriginalPrice = sku.Price | |||||
officialSku.AddPrice = calculateAddPriceNum(sku.Price, sku.LinePrice, &skuConditions) | |||||
officialSku.Price = utils.AnyToString(utils.StrToFloat64(officialSku.AddPrice) + utils.StrToFloat64(officialSku.OriginalPrice)) | |||||
officialSku.SupplierSkuId = sku.SkuID | |||||
officialSku.SupplierGoodsId = sku.GoodsID | |||||
officialSku.SupplierSkuCode = sku.SkuCode | |||||
officialSku.CloudChainSkuImgUrl = sku.CloudChainSkuImgUrl | |||||
one, err := session.InsertOne(&officialSku) | |||||
if err != nil || one == 0 { | |||||
session.Rollback() | |||||
return err | |||||
} | |||||
err = db.AddOrUpdateMallSkuAddPriceRule(session, utils.SerializeStr(&skuConditions), details.BaseGoods.GoodsCode+"-"+sku.SkuCode) | |||||
if err != nil { | |||||
logx.Error(err) | |||||
session.Rollback() | |||||
return err | |||||
} | |||||
} | |||||
err = session.Commit() | |||||
if err != nil { | |||||
session.Rollback() | |||||
return err | |||||
} | |||||
return nil | |||||
} | |||||
func calculateAddPriceNum(originalPrice string, linePrice string, conditions *md.Conditions) string { | |||||
addPrice := "0.0" | |||||
if conditions.AddPriceType == "1" { | |||||
if conditions.AddPriceBase == "1" { | |||||
addPrice = utils.AnyToString(utils.StrToFloat64(conditions.AddPriceNum)) | |||||
} else if conditions.AddPriceBase == "2" { | |||||
profits := utils.StrToFloat64(linePrice) - utils.StrToFloat64(originalPrice) | |||||
if profits < 0 { | |||||
profits = utils.StrToFloat64(originalPrice) - utils.StrToFloat64(linePrice) | |||||
} | |||||
if profits < 0 { | |||||
profits = 0 | |||||
} | |||||
addPrice = utils.AnyToString(profits + utils.StrToFloat64(conditions.AddPriceNum)) | |||||
} | |||||
} else if conditions.AddPriceType == "2" { | |||||
if conditions.AddPriceBase == "1" { | |||||
addPriceRate := utils.StrToFloat64(conditions.AddPriceNum) / 100 | |||||
addPrice = utils.AnyToString(utils.StrToFloat64(originalPrice) * addPriceRate) | |||||
} else if conditions.AddPriceBase == "2" { | |||||
addPriceRate := utils.StrToFloat64(conditions.AddPriceNum) / 100 | |||||
profits := utils.StrToFloat64(linePrice) - utils.StrToFloat64(originalPrice) | |||||
if profits < 0 { | |||||
profits = utils.StrToFloat64(originalPrice) - utils.StrToFloat64(linePrice) | |||||
} | |||||
if profits < 0 { | |||||
profits = 0 | |||||
} | |||||
addPrice = utils.AnyToString(profits * addPriceRate) | |||||
} | |||||
} | |||||
return addPrice | |||||
} |
@@ -0,0 +1,107 @@ | |||||
package svc | |||||
import ( | |||||
"applet/mall/curl_supply/md" | |||||
db2 "applet/supply/db" | |||||
"github.com/gin-gonic/gin" | |||||
"xorm.io/xorm" | |||||
"applet/app/cfg" | |||||
"applet/app/db" | |||||
"applet/app/utils" | |||||
"applet/app/utils/cache" | |||||
) | |||||
// 单挑记录获取 | |||||
func SysCfgGet(c *gin.Context, key string) string { | |||||
res := SysCfgFind(c, key) | |||||
if _, ok := res[key]; !ok { | |||||
return "" | |||||
} | |||||
return res[key] | |||||
} | |||||
// 多条记录获取 | |||||
func SysCfgFind(c *gin.Context, keys ...string) map[string]string { | |||||
var e *xorm.Engine | |||||
if c == nil { | |||||
e = db.Db | |||||
} else { | |||||
e = db.DBs[c.GetString("mid")] | |||||
} | |||||
res := map[string]string{} | |||||
err := cache.GetJson(md.KEY_SYS_CFG_CACHE, &res) | |||||
if err != nil || len(res) == 0 { | |||||
cfgList, _ := db.SysCfgGetAll(e) | |||||
if cfgList == nil { | |||||
return nil | |||||
} | |||||
for _, v := range *cfgList { | |||||
res[v.Key] = v.Val | |||||
} | |||||
// 先不设置缓存 | |||||
// cache.SetJson(md.KEY_SYS_CFG_CACHE, res, 60) | |||||
} | |||||
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 | |||||
} | |||||
// 清理系统配置信息 | |||||
func SysCfgCleanCache() { | |||||
cache.Del(md.KEY_SYS_CFG_CACHE) | |||||
} | |||||
// 写入系统设置 | |||||
func SysCfgSet(c *gin.Context, key, val, memo string) bool { | |||||
cfg, err := db.SysCfgGetOne(db.DBs[c.GetString("mid")], key) | |||||
if err != nil || cfg == nil { | |||||
return db.SysCfgInsert(db.DBs[c.GetString("mid")], key, val, memo) | |||||
} | |||||
if memo != "" && cfg.Memo != memo { | |||||
cfg.Memo = memo | |||||
} | |||||
SysCfgCleanCache() | |||||
return db.SysCfgUpdate(db.DBs[c.GetString("mid")], key, val, cfg.Memo) | |||||
} | |||||
// 多条记录获取 | |||||
func SysCfgFindByIds(eg *xorm.Engine, keys ...string) map[string]string { | |||||
key := utils.Md5(eg.DataSourceName() + md.KEY_SYS_CFG_CACHE) | |||||
res := map[string]string{} | |||||
c, ok := cfg.MemCache.Get(key).(map[string]string) | |||||
if !ok || len(c) == 0 { | |||||
cfgList, _ := db2.DbsSysCfgGetAll(eg) | |||||
if cfgList == nil { | |||||
return nil | |||||
} | |||||
for _, v := range *cfgList { | |||||
res[v.Key] = v.Val | |||||
} | |||||
cfg.MemCache.Put(key, res, 10) | |||||
} else { | |||||
res = c | |||||
} | |||||
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 | |||||
} |
@@ -0,0 +1,131 @@ | |||||
package tool | |||||
import ( | |||||
"bytes" | |||||
"encoding/json" | |||||
"log" | |||||
"regexp" | |||||
"strconv" | |||||
"strings" | |||||
"unicode" | |||||
) | |||||
/*************************************** 下划线json ***************************************/ | |||||
type JsonSnakeCase struct { | |||||
Value interface{} | |||||
} | |||||
func MarshalJSON(marshalJson []byte) []byte { | |||||
// Regexp definitions | |||||
var keyMatchRegex = regexp.MustCompile(`\"(\w+)\":`) | |||||
var wordBarrierRegex = regexp.MustCompile(`(\w)([A-Z])`) | |||||
converted := keyMatchRegex.ReplaceAllFunc( | |||||
marshalJson, | |||||
func(match []byte) []byte { | |||||
return bytes.ToLower(wordBarrierRegex.ReplaceAll( | |||||
match, | |||||
[]byte(`${1}_${2}`), | |||||
)) | |||||
}, | |||||
) | |||||
return converted | |||||
} | |||||
/*************************************** 驼峰json ***************************************/ | |||||
type JsonCamelCase struct { | |||||
Value interface{} | |||||
} | |||||
func (c JsonCamelCase) MarshalJSON() ([]byte, error) { | |||||
var keyMatchRegex = regexp.MustCompile(`\"(\w+)\":`) | |||||
marshalled, err := json.Marshal(c.Value) | |||||
converted := keyMatchRegex.ReplaceAllFunc( | |||||
marshalled, | |||||
func(match []byte) []byte { | |||||
matchStr := string(match) | |||||
key := matchStr[1 : len(matchStr)-2] | |||||
resKey := Lcfirst(Case2Camel(key)) | |||||
return []byte(`"` + resKey + `":`) | |||||
}, | |||||
) | |||||
return converted, err | |||||
} | |||||
/*************************************** 其他方法 ***************************************/ | |||||
// 驼峰式写法转为下划线写法 | |||||
func Camel2Case(name string) string { | |||||
buffer := NewBuffer() | |||||
for i, r := range name { | |||||
if unicode.IsUpper(r) { | |||||
if i != 0 { | |||||
buffer.Append('_') | |||||
} | |||||
buffer.Append(unicode.ToLower(r)) | |||||
} else { | |||||
buffer.Append(r) | |||||
} | |||||
} | |||||
return buffer.String() | |||||
} | |||||
// 下划线写法转为驼峰写法 | |||||
func Case2Camel(name string) string { | |||||
name = strings.Replace(name, "_", " ", -1) | |||||
name = strings.Title(name) | |||||
return strings.Replace(name, " ", "", -1) | |||||
} | |||||
// 首字母大写 | |||||
func Ucfirst(str string) string { | |||||
for i, v := range str { | |||||
return string(unicode.ToUpper(v)) + str[i+1:] | |||||
} | |||||
return "" | |||||
} | |||||
// 首字母小写 | |||||
func Lcfirst(str string) string { | |||||
for i, v := range str { | |||||
return string(unicode.ToLower(v)) + str[i+1:] | |||||
} | |||||
return "" | |||||
} | |||||
// 内嵌bytes.Buffer,支持连写 | |||||
type Buffer struct { | |||||
*bytes.Buffer | |||||
} | |||||
func NewBuffer() *Buffer { | |||||
return &Buffer{Buffer: new(bytes.Buffer)} | |||||
} | |||||
func (b *Buffer) Append(i interface{}) *Buffer { | |||||
switch val := i.(type) { | |||||
case int: | |||||
b.append(strconv.Itoa(val)) | |||||
case int64: | |||||
b.append(strconv.FormatInt(val, 10)) | |||||
case uint: | |||||
b.append(strconv.FormatUint(uint64(val), 10)) | |||||
case uint64: | |||||
b.append(strconv.FormatUint(val, 10)) | |||||
case string: | |||||
b.append(val) | |||||
case []byte: | |||||
b.Write(val) | |||||
case rune: | |||||
b.WriteRune(val) | |||||
} | |||||
return b | |||||
} | |||||
func (b *Buffer) append(s string) *Buffer { | |||||
defer func() { | |||||
if err := recover(); err != nil { | |||||
log.Println("*****内存不够了!******") | |||||
} | |||||
}() | |||||
b.WriteString(s) | |||||
return b | |||||
} |
@@ -0,0 +1,21 @@ | |||||
package tool | |||||
import "time" | |||||
func Time2String(date time.Time, format string) string { | |||||
if format == "" { | |||||
format = "2006-01-02 15:04:05" | |||||
} | |||||
timeS := date.Format(format) | |||||
if timeS == "0001-01-01 00:00:00" { | |||||
return "" | |||||
} | |||||
return timeS | |||||
} | |||||
func String2Time(timeStr string) time.Time { | |||||
toTime, err := time.ParseInLocation("2006-01-02 15:04:05", timeStr, time.Local) | |||||
if err != nil { | |||||
return time.Now() | |||||
} | |||||
return toTime | |||||
} |
@@ -0,0 +1,163 @@ | |||||
package utils | |||||
import ( | |||||
"applet/app/cfg" | |||||
"bytes" | |||||
"crypto/aes" | |||||
"crypto/cipher" | |||||
"encoding/base64" | |||||
"encoding/json" | |||||
"fmt" | |||||
) | |||||
func AesEncrypt(rawData, key []byte) ([]byte, error) { | |||||
block, err := aes.NewCipher(key) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
blockSize := block.BlockSize() | |||||
rawData = PKCS5Padding(rawData, blockSize) | |||||
// rawData = ZeroPadding(rawData, block.BlockSize()) | |||||
blockMode := cipher.NewCBCEncrypter(block, key[:blockSize]) | |||||
encrypted := make([]byte, len(rawData)) | |||||
// 根据CryptBlocks方法的说明,如下方式初始化encrypted也可以 | |||||
// encrypted := rawData | |||||
blockMode.CryptBlocks(encrypted, rawData) | |||||
return encrypted, nil | |||||
} | |||||
func AesDecrypt(encrypted, key []byte) ([]byte, error) { | |||||
block, err := aes.NewCipher(key) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
blockSize := block.BlockSize() | |||||
blockMode := cipher.NewCBCDecrypter(block, key[:blockSize]) | |||||
rawData := make([]byte, len(encrypted)) | |||||
// rawData := encrypted | |||||
blockMode.CryptBlocks(rawData, encrypted) | |||||
rawData = PKCS5UnPadding(rawData) | |||||
// rawData = ZeroUnPadding(rawData) | |||||
return rawData, nil | |||||
} | |||||
func ZeroPadding(cipherText []byte, blockSize int) []byte { | |||||
padding := blockSize - len(cipherText)%blockSize | |||||
padText := bytes.Repeat([]byte{0}, padding) | |||||
return append(cipherText, padText...) | |||||
} | |||||
func ZeroUnPadding(rawData []byte) []byte { | |||||
length := len(rawData) | |||||
unPadding := int(rawData[length-1]) | |||||
return rawData[:(length - unPadding)] | |||||
} | |||||
func PKCS5Padding(cipherText []byte, blockSize int) []byte { | |||||
padding := blockSize - len(cipherText)%blockSize | |||||
padText := bytes.Repeat([]byte{byte(padding)}, padding) | |||||
return append(cipherText, padText...) | |||||
} | |||||
func PKCS5UnPadding(rawData []byte) []byte { | |||||
length := len(rawData) | |||||
// 去掉最后一个字节 unPadding 次 | |||||
unPadding := int(rawData[length-1]) | |||||
return rawData[:(length - unPadding)] | |||||
} | |||||
// 填充0 | |||||
func zeroFill(key *string) { | |||||
l := len(*key) | |||||
if l != 16 && l != 24 && l != 32 { | |||||
if l < 16 { | |||||
*key = *key + fmt.Sprintf("%0*d", 16-l, 0) | |||||
} else if l < 24 { | |||||
*key = *key + fmt.Sprintf("%0*d", 24-l, 0) | |||||
} else if l < 32 { | |||||
*key = *key + fmt.Sprintf("%0*d", 32-l, 0) | |||||
} else { | |||||
*key = string([]byte(*key)[:32]) | |||||
} | |||||
} | |||||
} | |||||
type AesCrypt struct { | |||||
Key []byte | |||||
Iv []byte | |||||
} | |||||
func (a *AesCrypt) Encrypt(data []byte) ([]byte, error) { | |||||
aesBlockEncrypt, err := aes.NewCipher(a.Key) | |||||
if err != nil { | |||||
println(err.Error()) | |||||
return nil, err | |||||
} | |||||
content := pKCS5Padding(data, aesBlockEncrypt.BlockSize()) | |||||
cipherBytes := make([]byte, len(content)) | |||||
aesEncrypt := cipher.NewCBCEncrypter(aesBlockEncrypt, a.Iv) | |||||
aesEncrypt.CryptBlocks(cipherBytes, content) | |||||
return cipherBytes, nil | |||||
} | |||||
func (a *AesCrypt) Decrypt(src []byte) (data []byte, err error) { | |||||
decrypted := make([]byte, len(src)) | |||||
var aesBlockDecrypt cipher.Block | |||||
aesBlockDecrypt, err = aes.NewCipher(a.Key) | |||||
if err != nil { | |||||
println(err.Error()) | |||||
return nil, err | |||||
} | |||||
aesDecrypt := cipher.NewCBCDecrypter(aesBlockDecrypt, a.Iv) | |||||
aesDecrypt.CryptBlocks(decrypted, src) | |||||
return pKCS5Trimming(decrypted), nil | |||||
} | |||||
func pKCS5Padding(cipherText []byte, blockSize int) []byte { | |||||
padding := blockSize - len(cipherText)%blockSize | |||||
padText := bytes.Repeat([]byte{byte(padding)}, padding) | |||||
return append(cipherText, padText...) | |||||
} | |||||
func pKCS5Trimming(encrypt []byte) []byte { | |||||
padding := encrypt[len(encrypt)-1] | |||||
return encrypt[:len(encrypt)-int(padding)] | |||||
} | |||||
// AesAdminCurlPOST is 与后台接口加密交互 | |||||
func AesAdminCurlPOST(aesData string, url string) ([]byte, error) { | |||||
adminKey := cfg.Admin.AesKey | |||||
adminVI := cfg.Admin.AesIV | |||||
crypto := AesCrypt{ | |||||
Key: []byte(adminKey), | |||||
Iv: []byte(adminVI), | |||||
} | |||||
encrypt, err := crypto.Encrypt([]byte(aesData)) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
// 发送请求到后台 | |||||
postData := map[string]string{ | |||||
"postData": base64.StdEncoding.EncodeToString(encrypt), | |||||
} | |||||
postDataByte, _ := json.Marshal(postData) | |||||
rdata, err := CurlPost(url, postDataByte, nil) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
pass, err := base64.StdEncoding.DecodeString(string(rdata)) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
decrypt, err := crypto.Decrypt(pass) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
return decrypt, nil | |||||
} |
@@ -0,0 +1,108 @@ | |||||
package utils | |||||
import ( | |||||
"encoding/base64" | |||||
"fmt" | |||||
"io/ioutil" | |||||
"net/http" | |||||
) | |||||
const ( | |||||
Base64Std = iota | |||||
Base64Url | |||||
Base64RawStd | |||||
Base64RawUrl | |||||
) | |||||
func Base64StdEncode(str interface{}) string { | |||||
return Base64Encode(str, Base64Std) | |||||
} | |||||
func Base64StdDecode(str interface{}) string { | |||||
return Base64Decode(str, Base64Std) | |||||
} | |||||
func Base64UrlEncode(str interface{}) string { | |||||
return Base64Encode(str, Base64Url) | |||||
} | |||||
func Base64UrlDecode(str interface{}) string { | |||||
return Base64Decode(str, Base64Url) | |||||
} | |||||
func Base64RawStdEncode(str interface{}) string { | |||||
return Base64Encode(str, Base64RawStd) | |||||
} | |||||
func Base64RawStdDecode(str interface{}) string { | |||||
return Base64Decode(str, Base64RawStd) | |||||
} | |||||
func Base64RawUrlEncode(str interface{}) string { | |||||
return Base64Encode(str, Base64RawUrl) | |||||
} | |||||
func Base64RawUrlDecode(str interface{}) string { | |||||
return Base64Decode(str, Base64RawUrl) | |||||
} | |||||
func Base64Encode(str interface{}, encode int) string { | |||||
newEncode := base64Encode(encode) | |||||
if newEncode == nil { | |||||
return "" | |||||
} | |||||
switch v := str.(type) { | |||||
case string: | |||||
return newEncode.EncodeToString([]byte(v)) | |||||
case []byte: | |||||
return newEncode.EncodeToString(v) | |||||
} | |||||
return newEncode.EncodeToString([]byte(fmt.Sprint(str))) | |||||
} | |||||
func Base64Decode(str interface{}, encode int) string { | |||||
var err error | |||||
var b []byte | |||||
newEncode := base64Encode(encode) | |||||
if newEncode == nil { | |||||
return "" | |||||
} | |||||
switch v := str.(type) { | |||||
case string: | |||||
b, err = newEncode.DecodeString(v) | |||||
case []byte: | |||||
b, err = newEncode.DecodeString(string(v)) | |||||
default: | |||||
return "" | |||||
} | |||||
if err != nil { | |||||
return "" | |||||
} | |||||
return string(b) | |||||
} | |||||
func base64Encode(encode int) *base64.Encoding { | |||||
switch encode { | |||||
case Base64Std: | |||||
return base64.StdEncoding | |||||
case Base64Url: | |||||
return base64.URLEncoding | |||||
case Base64RawStd: | |||||
return base64.RawStdEncoding | |||||
case Base64RawUrl: | |||||
return base64.RawURLEncoding | |||||
default: | |||||
return nil | |||||
} | |||||
} | |||||
func GetBase64FromUrlForImage(url string) (string, error) { | |||||
res, err := http.Get(url) | |||||
if err != nil { | |||||
return "", err | |||||
} | |||||
defer res.Body.Close() | |||||
data, _ := ioutil.ReadAll(res.Body) | |||||
imageBase64 := base64.StdEncoding.EncodeToString(data) | |||||
return imageBase64, nil | |||||
} |
@@ -0,0 +1,26 @@ | |||||
package utils | |||||
import "reflect" | |||||
// 检验一个值是否为空 | |||||
func Empty(val interface{}) bool { | |||||
v := reflect.ValueOf(val) | |||||
switch v.Kind() { | |||||
case reflect.String, reflect.Array: | |||||
return v.Len() == 0 | |||||
case reflect.Map, reflect.Slice: | |||||
return v.Len() == 0 || v.IsNil() | |||||
case reflect.Bool: | |||||
return !v.Bool() | |||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: | |||||
return v.Int() == 0 | |||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: | |||||
return v.Uint() == 0 | |||||
case reflect.Float32, reflect.Float64: | |||||
return v.Float() == 0 | |||||
case reflect.Interface, reflect.Ptr: | |||||
return v.IsNil() | |||||
} | |||||
return reflect.DeepEqual(val, reflect.Zero(v.Type()).Interface()) | |||||
} |
@@ -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) | |||||
} |
@@ -0,0 +1,107 @@ | |||||
package cache | |||||
import ( | |||||
"fmt" | |||||
"time" | |||||
) | |||||
var c Cache | |||||
type Cache interface { | |||||
// get cached value by key. | |||||
Get(key string) interface{} | |||||
// GetMulti is a batch version of Get. | |||||
GetMulti(keys []string) []interface{} | |||||
// set cached value with key and expire time. | |||||
Put(key string, val interface{}, timeout time.Duration) error | |||||
// delete cached value by key. | |||||
Delete(key string) error | |||||
// increase cached int value by key, as a counter. | |||||
Incr(key string) error | |||||
// decrease cached int value by key, as a counter. | |||||
Decr(key string) error | |||||
// check if cached value exists or not. | |||||
IsExist(key string) bool | |||||
// clear all cache. | |||||
ClearAll() error | |||||
// start gc routine based on config string settings. | |||||
StartAndGC(config string) error | |||||
} | |||||
// Instance is a function create a new Cache Instance | |||||
type Instance func() Cache | |||||
var adapters = make(map[string]Instance) | |||||
// Register makes a cache adapter available by the adapter name. | |||||
// If Register is called twice with the same name or if driver is nil, | |||||
// it panics. | |||||
func Register(name string, adapter Instance) { | |||||
if adapter == nil { | |||||
panic("cache: Register adapter is nil") | |||||
} | |||||
if _, ok := adapters[name]; ok { | |||||
panic("cache: Register called twice for adapter " + name) | |||||
} | |||||
adapters[name] = adapter | |||||
} | |||||
// NewCache Create a new cache driver by adapter name and config string. | |||||
// config need to be correct JSON as string: {"interval":360}. | |||||
// it will start gc automatically. | |||||
func NewCache(adapterName, config string) (adapter Cache, err error) { | |||||
instanceFunc, ok := adapters[adapterName] | |||||
if !ok { | |||||
err = fmt.Errorf("cache: unknown adapter name %q (forgot to import?)", adapterName) | |||||
return | |||||
} | |||||
adapter = instanceFunc() | |||||
err = adapter.StartAndGC(config) | |||||
if err != nil { | |||||
adapter = nil | |||||
} | |||||
return | |||||
} | |||||
func InitCache(adapterName, config string) (err error) { | |||||
instanceFunc, ok := adapters[adapterName] | |||||
if !ok { | |||||
err = fmt.Errorf("cache: unknown adapter name %q (forgot to import?)", adapterName) | |||||
return | |||||
} | |||||
c = instanceFunc() | |||||
err = c.StartAndGC(config) | |||||
if err != nil { | |||||
c = nil | |||||
} | |||||
return | |||||
} | |||||
func Get(key string) interface{} { | |||||
return c.Get(key) | |||||
} | |||||
func GetMulti(keys []string) []interface{} { | |||||
return c.GetMulti(keys) | |||||
} | |||||
func Put(key string, val interface{}, ttl time.Duration) error { | |||||
return c.Put(key, val, ttl) | |||||
} | |||||
func Delete(key string) error { | |||||
return c.Delete(key) | |||||
} | |||||
func Incr(key string) error { | |||||
return c.Incr(key) | |||||
} | |||||
func Decr(key string) error { | |||||
return c.Decr(key) | |||||
} | |||||
func IsExist(key string) bool { | |||||
return c.IsExist(key) | |||||
} | |||||
func ClearAll() error { | |||||
return c.ClearAll() | |||||
} | |||||
func StartAndGC(cfg string) error { | |||||
return c.StartAndGC(cfg) | |||||
} |
@@ -0,0 +1,86 @@ | |||||
package cache | |||||
import ( | |||||
"fmt" | |||||
"strconv" | |||||
) | |||||
// GetString convert interface to string. | |||||
func GetString(v interface{}) string { | |||||
switch result := v.(type) { | |||||
case string: | |||||
return result | |||||
case []byte: | |||||
return string(result) | |||||
default: | |||||
if v != nil { | |||||
return fmt.Sprint(result) | |||||
} | |||||
} | |||||
return "" | |||||
} | |||||
// GetInt convert interface to int. | |||||
func GetInt(v interface{}) int { | |||||
switch result := v.(type) { | |||||
case int: | |||||
return result | |||||
case int32: | |||||
return int(result) | |||||
case int64: | |||||
return int(result) | |||||
default: | |||||
if d := GetString(v); d != "" { | |||||
value, _ := strconv.Atoi(d) | |||||
return value | |||||
} | |||||
} | |||||
return 0 | |||||
} | |||||
// GetInt64 convert interface to int64. | |||||
func GetInt64(v interface{}) int64 { | |||||
switch result := v.(type) { | |||||
case int: | |||||
return int64(result) | |||||
case int32: | |||||
return int64(result) | |||||
case int64: | |||||
return result | |||||
default: | |||||
if d := GetString(v); d != "" { | |||||
value, _ := strconv.ParseInt(d, 10, 64) | |||||
return value | |||||
} | |||||
} | |||||
return 0 | |||||
} | |||||
// GetFloat64 convert interface to float64. | |||||
func GetFloat64(v interface{}) float64 { | |||||
switch result := v.(type) { | |||||
case float64: | |||||
return result | |||||
default: | |||||
if d := GetString(v); d != "" { | |||||
value, _ := strconv.ParseFloat(d, 64) | |||||
return value | |||||
} | |||||
} | |||||
return 0 | |||||
} | |||||
// GetBool convert interface to bool. | |||||
func GetBool(v interface{}) bool { | |||||
switch result := v.(type) { | |||||
case bool: | |||||
return result | |||||
default: | |||||
if d := GetString(v); d != "" { | |||||
value, _ := strconv.ParseBool(d) | |||||
return value | |||||
} | |||||
} | |||||
return false | |||||
} |
@@ -0,0 +1,241 @@ | |||||
package cache | |||||
import ( | |||||
"bytes" | |||||
"crypto/md5" | |||||
"encoding/gob" | |||||
"encoding/hex" | |||||
"encoding/json" | |||||
"fmt" | |||||
"io" | |||||
"io/ioutil" | |||||
"os" | |||||
"path/filepath" | |||||
"reflect" | |||||
"strconv" | |||||
"time" | |||||
) | |||||
// FileCacheItem is basic unit of file cache adapter. | |||||
// it contains data and expire time. | |||||
type FileCacheItem struct { | |||||
Data interface{} | |||||
LastAccess time.Time | |||||
Expired time.Time | |||||
} | |||||
// FileCache Config | |||||
var ( | |||||
FileCachePath = "cache" // cache directory | |||||
FileCacheFileSuffix = ".bin" // cache file suffix | |||||
FileCacheDirectoryLevel = 2 // cache file deep level if auto generated cache files. | |||||
FileCacheEmbedExpiry time.Duration // cache expire time, default is no expire forever. | |||||
) | |||||
// FileCache is cache adapter for file storage. | |||||
type FileCache struct { | |||||
CachePath string | |||||
FileSuffix string | |||||
DirectoryLevel int | |||||
EmbedExpiry int | |||||
} | |||||
// NewFileCache Create new file cache with no config. | |||||
// the level and expiry need set in method StartAndGC as config string. | |||||
func NewFileCache() Cache { | |||||
// return &FileCache{CachePath:FileCachePath, FileSuffix:FileCacheFileSuffix} | |||||
return &FileCache{} | |||||
} | |||||
// StartAndGC will start and begin gc for file cache. | |||||
// the config need to be like {CachePath:"/cache","FileSuffix":".bin","DirectoryLevel":2,"EmbedExpiry":0} | |||||
func (fc *FileCache) StartAndGC(config string) error { | |||||
var cfg map[string]string | |||||
json.Unmarshal([]byte(config), &cfg) | |||||
if _, ok := cfg["CachePath"]; !ok { | |||||
cfg["CachePath"] = FileCachePath | |||||
} | |||||
if _, ok := cfg["FileSuffix"]; !ok { | |||||
cfg["FileSuffix"] = FileCacheFileSuffix | |||||
} | |||||
if _, ok := cfg["DirectoryLevel"]; !ok { | |||||
cfg["DirectoryLevel"] = strconv.Itoa(FileCacheDirectoryLevel) | |||||
} | |||||
if _, ok := cfg["EmbedExpiry"]; !ok { | |||||
cfg["EmbedExpiry"] = strconv.FormatInt(int64(FileCacheEmbedExpiry.Seconds()), 10) | |||||
} | |||||
fc.CachePath = cfg["CachePath"] | |||||
fc.FileSuffix = cfg["FileSuffix"] | |||||
fc.DirectoryLevel, _ = strconv.Atoi(cfg["DirectoryLevel"]) | |||||
fc.EmbedExpiry, _ = strconv.Atoi(cfg["EmbedExpiry"]) | |||||
fc.Init() | |||||
return nil | |||||
} | |||||
// Init will make new dir for file cache if not exist. | |||||
func (fc *FileCache) Init() { | |||||
if ok, _ := exists(fc.CachePath); !ok { // todo : error handle | |||||
_ = os.MkdirAll(fc.CachePath, os.ModePerm) // todo : error handle | |||||
} | |||||
} | |||||
// get cached file name. it's md5 encoded. | |||||
func (fc *FileCache) getCacheFileName(key string) string { | |||||
m := md5.New() | |||||
io.WriteString(m, key) | |||||
keyMd5 := hex.EncodeToString(m.Sum(nil)) | |||||
cachePath := fc.CachePath | |||||
switch fc.DirectoryLevel { | |||||
case 2: | |||||
cachePath = filepath.Join(cachePath, keyMd5[0:2], keyMd5[2:4]) | |||||
case 1: | |||||
cachePath = filepath.Join(cachePath, keyMd5[0:2]) | |||||
} | |||||
if ok, _ := exists(cachePath); !ok { // todo : error handle | |||||
_ = os.MkdirAll(cachePath, os.ModePerm) // todo : error handle | |||||
} | |||||
return filepath.Join(cachePath, fmt.Sprintf("%s%s", keyMd5, fc.FileSuffix)) | |||||
} | |||||
// Get value from file cache. | |||||
// if non-exist or expired, return empty string. | |||||
func (fc *FileCache) Get(key string) interface{} { | |||||
fileData, err := FileGetContents(fc.getCacheFileName(key)) | |||||
if err != nil { | |||||
return "" | |||||
} | |||||
var to FileCacheItem | |||||
GobDecode(fileData, &to) | |||||
if to.Expired.Before(time.Now()) { | |||||
return "" | |||||
} | |||||
return to.Data | |||||
} | |||||
// GetMulti gets values from file cache. | |||||
// if non-exist or expired, return empty string. | |||||
func (fc *FileCache) GetMulti(keys []string) []interface{} { | |||||
var rc []interface{} | |||||
for _, key := range keys { | |||||
rc = append(rc, fc.Get(key)) | |||||
} | |||||
return rc | |||||
} | |||||
// Put value into file cache. | |||||
// timeout means how long to keep this file, unit of ms. | |||||
// if timeout equals FileCacheEmbedExpiry(default is 0), cache this item forever. | |||||
func (fc *FileCache) Put(key string, val interface{}, timeout time.Duration) error { | |||||
gob.Register(val) | |||||
item := FileCacheItem{Data: val} | |||||
if timeout == FileCacheEmbedExpiry { | |||||
item.Expired = time.Now().Add((86400 * 365 * 10) * time.Second) // ten years | |||||
} else { | |||||
item.Expired = time.Now().Add(timeout) | |||||
} | |||||
item.LastAccess = time.Now() | |||||
data, err := GobEncode(item) | |||||
if err != nil { | |||||
return err | |||||
} | |||||
return FilePutContents(fc.getCacheFileName(key), data) | |||||
} | |||||
// Delete file cache value. | |||||
func (fc *FileCache) Delete(key string) error { | |||||
filename := fc.getCacheFileName(key) | |||||
if ok, _ := exists(filename); ok { | |||||
return os.Remove(filename) | |||||
} | |||||
return nil | |||||
} | |||||
// Incr will increase cached int value. | |||||
// fc value is saving forever unless Delete. | |||||
func (fc *FileCache) Incr(key string) error { | |||||
data := fc.Get(key) | |||||
var incr int | |||||
if reflect.TypeOf(data).Name() != "int" { | |||||
incr = 0 | |||||
} else { | |||||
incr = data.(int) + 1 | |||||
} | |||||
fc.Put(key, incr, FileCacheEmbedExpiry) | |||||
return nil | |||||
} | |||||
// Decr will decrease cached int value. | |||||
func (fc *FileCache) Decr(key string) error { | |||||
data := fc.Get(key) | |||||
var decr int | |||||
if reflect.TypeOf(data).Name() != "int" || data.(int)-1 <= 0 { | |||||
decr = 0 | |||||
} else { | |||||
decr = data.(int) - 1 | |||||
} | |||||
fc.Put(key, decr, FileCacheEmbedExpiry) | |||||
return nil | |||||
} | |||||
// IsExist check value is exist. | |||||
func (fc *FileCache) IsExist(key string) bool { | |||||
ret, _ := exists(fc.getCacheFileName(key)) | |||||
return ret | |||||
} | |||||
// ClearAll will clean cached files. | |||||
// not implemented. | |||||
func (fc *FileCache) ClearAll() error { | |||||
return nil | |||||
} | |||||
// check file exist. | |||||
func exists(path string) (bool, error) { | |||||
_, err := os.Stat(path) | |||||
if err == nil { | |||||
return true, nil | |||||
} | |||||
if os.IsNotExist(err) { | |||||
return false, nil | |||||
} | |||||
return false, err | |||||
} | |||||
// FileGetContents Get bytes to file. | |||||
// if non-exist, create this file. | |||||
func FileGetContents(filename string) (data []byte, e error) { | |||||
return ioutil.ReadFile(filename) | |||||
} | |||||
// FilePutContents Put bytes to file. | |||||
// if non-exist, create this file. | |||||
func FilePutContents(filename string, content []byte) error { | |||||
return ioutil.WriteFile(filename, content, os.ModePerm) | |||||
} | |||||
// GobEncode Gob encodes file cache item. | |||||
func GobEncode(data interface{}) ([]byte, error) { | |||||
buf := bytes.NewBuffer(nil) | |||||
enc := gob.NewEncoder(buf) | |||||
err := enc.Encode(data) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
return buf.Bytes(), err | |||||
} | |||||
// GobDecode Gob decodes file cache item. | |||||
func GobDecode(data []byte, to *FileCacheItem) error { | |||||
buf := bytes.NewBuffer(data) | |||||
dec := gob.NewDecoder(buf) | |||||
return dec.Decode(&to) | |||||
} | |||||
func init() { | |||||
Register("file", NewFileCache) | |||||
} |
@@ -0,0 +1,239 @@ | |||||
package cache | |||||
import ( | |||||
"encoding/json" | |||||
"errors" | |||||
"sync" | |||||
"time" | |||||
) | |||||
var ( | |||||
// DefaultEvery means the clock time of recycling the expired cache items in memory. | |||||
DefaultEvery = 60 // 1 minute | |||||
) | |||||
// MemoryItem store memory cache item. | |||||
type MemoryItem struct { | |||||
val interface{} | |||||
createdTime time.Time | |||||
lifespan time.Duration | |||||
} | |||||
func (mi *MemoryItem) isExpire() bool { | |||||
// 0 means forever | |||||
if mi.lifespan == 0 { | |||||
return false | |||||
} | |||||
return time.Now().Sub(mi.createdTime) > mi.lifespan | |||||
} | |||||
// MemoryCache is Memory cache adapter. | |||||
// it contains a RW locker for safe map storage. | |||||
type MemoryCache struct { | |||||
sync.RWMutex | |||||
dur time.Duration | |||||
items map[string]*MemoryItem | |||||
Every int // run an expiration check Every clock time | |||||
} | |||||
// NewMemoryCache returns a new MemoryCache. | |||||
func NewMemoryCache() Cache { | |||||
cache := MemoryCache{items: make(map[string]*MemoryItem)} | |||||
return &cache | |||||
} | |||||
// Get cache from memory. | |||||
// if non-existed or expired, return nil. | |||||
func (bc *MemoryCache) Get(name string) interface{} { | |||||
bc.RLock() | |||||
defer bc.RUnlock() | |||||
if itm, ok := bc.items[name]; ok { | |||||
if itm.isExpire() { | |||||
return nil | |||||
} | |||||
return itm.val | |||||
} | |||||
return nil | |||||
} | |||||
// GetMulti gets caches from memory. | |||||
// if non-existed or expired, return nil. | |||||
func (bc *MemoryCache) GetMulti(names []string) []interface{} { | |||||
var rc []interface{} | |||||
for _, name := range names { | |||||
rc = append(rc, bc.Get(name)) | |||||
} | |||||
return rc | |||||
} | |||||
// Put cache to memory. | |||||
// if lifespan is 0, it will be forever till restart. | |||||
func (bc *MemoryCache) Put(name string, value interface{}, lifespan time.Duration) error { | |||||
bc.Lock() | |||||
defer bc.Unlock() | |||||
bc.items[name] = &MemoryItem{ | |||||
val: value, | |||||
createdTime: time.Now(), | |||||
lifespan: lifespan, | |||||
} | |||||
return nil | |||||
} | |||||
// Delete cache in memory. | |||||
func (bc *MemoryCache) Delete(name string) error { | |||||
bc.Lock() | |||||
defer bc.Unlock() | |||||
if _, ok := bc.items[name]; !ok { | |||||
return errors.New("key not exist") | |||||
} | |||||
delete(bc.items, name) | |||||
if _, ok := bc.items[name]; ok { | |||||
return errors.New("delete key error") | |||||
} | |||||
return nil | |||||
} | |||||
// Incr increase cache counter in memory. | |||||
// it supports int,int32,int64,uint,uint32,uint64. | |||||
func (bc *MemoryCache) Incr(key string) error { | |||||
bc.RLock() | |||||
defer bc.RUnlock() | |||||
itm, ok := bc.items[key] | |||||
if !ok { | |||||
return errors.New("key not exist") | |||||
} | |||||
switch itm.val.(type) { | |||||
case int: | |||||
itm.val = itm.val.(int) + 1 | |||||
case int32: | |||||
itm.val = itm.val.(int32) + 1 | |||||
case int64: | |||||
itm.val = itm.val.(int64) + 1 | |||||
case uint: | |||||
itm.val = itm.val.(uint) + 1 | |||||
case uint32: | |||||
itm.val = itm.val.(uint32) + 1 | |||||
case uint64: | |||||
itm.val = itm.val.(uint64) + 1 | |||||
default: | |||||
return errors.New("item val is not (u)int (u)int32 (u)int64") | |||||
} | |||||
return nil | |||||
} | |||||
// Decr decrease counter in memory. | |||||
func (bc *MemoryCache) Decr(key string) error { | |||||
bc.RLock() | |||||
defer bc.RUnlock() | |||||
itm, ok := bc.items[key] | |||||
if !ok { | |||||
return errors.New("key not exist") | |||||
} | |||||
switch itm.val.(type) { | |||||
case int: | |||||
itm.val = itm.val.(int) - 1 | |||||
case int64: | |||||
itm.val = itm.val.(int64) - 1 | |||||
case int32: | |||||
itm.val = itm.val.(int32) - 1 | |||||
case uint: | |||||
if itm.val.(uint) > 0 { | |||||
itm.val = itm.val.(uint) - 1 | |||||
} else { | |||||
return errors.New("item val is less than 0") | |||||
} | |||||
case uint32: | |||||
if itm.val.(uint32) > 0 { | |||||
itm.val = itm.val.(uint32) - 1 | |||||
} else { | |||||
return errors.New("item val is less than 0") | |||||
} | |||||
case uint64: | |||||
if itm.val.(uint64) > 0 { | |||||
itm.val = itm.val.(uint64) - 1 | |||||
} else { | |||||
return errors.New("item val is less than 0") | |||||
} | |||||
default: | |||||
return errors.New("item val is not int int64 int32") | |||||
} | |||||
return nil | |||||
} | |||||
// IsExist check cache exist in memory. | |||||
func (bc *MemoryCache) IsExist(name string) bool { | |||||
bc.RLock() | |||||
defer bc.RUnlock() | |||||
if v, ok := bc.items[name]; ok { | |||||
return !v.isExpire() | |||||
} | |||||
return false | |||||
} | |||||
// ClearAll will delete all cache in memory. | |||||
func (bc *MemoryCache) ClearAll() error { | |||||
bc.Lock() | |||||
defer bc.Unlock() | |||||
bc.items = make(map[string]*MemoryItem) | |||||
return nil | |||||
} | |||||
// StartAndGC start memory cache. it will check expiration in every clock time. | |||||
func (bc *MemoryCache) StartAndGC(config string) error { | |||||
var cf map[string]int | |||||
json.Unmarshal([]byte(config), &cf) | |||||
if _, ok := cf["interval"]; !ok { | |||||
cf = make(map[string]int) | |||||
cf["interval"] = DefaultEvery | |||||
} | |||||
dur := time.Duration(cf["interval"]) * time.Second | |||||
bc.Every = cf["interval"] | |||||
bc.dur = dur | |||||
go bc.vacuum() | |||||
return nil | |||||
} | |||||
// check expiration. | |||||
func (bc *MemoryCache) vacuum() { | |||||
bc.RLock() | |||||
every := bc.Every | |||||
bc.RUnlock() | |||||
if every < 1 { | |||||
return | |||||
} | |||||
for { | |||||
<-time.After(bc.dur) | |||||
if bc.items == nil { | |||||
return | |||||
} | |||||
if keys := bc.expiredKeys(); len(keys) != 0 { | |||||
bc.clearItems(keys) | |||||
} | |||||
} | |||||
} | |||||
// expiredKeys returns key list which are expired. | |||||
func (bc *MemoryCache) expiredKeys() (keys []string) { | |||||
bc.RLock() | |||||
defer bc.RUnlock() | |||||
for key, itm := range bc.items { | |||||
if itm.isExpire() { | |||||
keys = append(keys, key) | |||||
} | |||||
} | |||||
return | |||||
} | |||||
// clearItems removes all the items which key in keys. | |||||
func (bc *MemoryCache) clearItems(keys []string) { | |||||
bc.Lock() | |||||
defer bc.Unlock() | |||||
for _, key := range keys { | |||||
delete(bc.items, key) | |||||
} | |||||
} | |||||
func init() { | |||||
Register("memory", NewMemoryCache) | |||||
} |
@@ -0,0 +1,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 | |||||
} |
@@ -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 | |||||
} |
@@ -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 | |||||
} |
@@ -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 | |||||
} |
@@ -0,0 +1,372 @@ | |||||
package utils | |||||
import ( | |||||
"encoding/binary" | |||||
"encoding/json" | |||||
"fmt" | |||||
"github.com/gin-gonic/gin" | |||||
"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 Float64ToStrByPrec(f float64, prec int) string { | |||||
return strconv.FormatFloat(f, 'f', prec, 64) | |||||
} | |||||
func Float64ToStrPrec4(f float64) string { | |||||
return strconv.FormatFloat(f, 'f', 4, 64) | |||||
} | |||||
func StrToFormat(c *gin.Context, s string, prec int) string { | |||||
ex := strings.Split(s, ".") | |||||
if len(ex) == 2 { | |||||
if StrToFloat64(ex[1]) == 0 && c.GetString("is_show_point") != "1" { //小数点后面为空就是不要小数点了 | |||||
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 Truncate(num string, prec int) string { | |||||
if num == "" { | |||||
return "" | |||||
} | |||||
if prec >= len(num) { | |||||
return num | |||||
} | |||||
newn := strings.Split(num, ".") | |||||
if len(newn) < 2 || prec >= len(newn[1]) { | |||||
return num | |||||
} | |||||
return newn[0] + "." + newn[1][:prec] | |||||
} | |||||
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 Float64ToStrPrec6(f float64) string { | |||||
return strconv.FormatFloat(f, 'f', 6, 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 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) | |||||
} |
@@ -0,0 +1,19 @@ | |||||
package utils | |||||
import ( | |||||
"crypto/md5" | |||||
"encoding/base64" | |||||
"fmt" | |||||
) | |||||
func GetMd5(raw []byte) string { | |||||
h := md5.New() | |||||
h.Write(raw) | |||||
return fmt.Sprintf("%x", h.Sum(nil)) | |||||
} | |||||
func GetBase64Md5(raw []byte) string { | |||||
h := md5.New() | |||||
h.Write(raw) | |||||
return base64.StdEncoding.EncodeToString(h.Sum(nil)) | |||||
} |
@@ -0,0 +1,170 @@ | |||||
package 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) | |||||
} | |||||
// 只支持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 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) | |||||
} |
@@ -0,0 +1,25 @@ | |||||
package utils | |||||
import ( | |||||
"fmt" | |||||
"os" | |||||
"strconv" | |||||
"time" | |||||
) | |||||
func Debug(args ...interface{}) { | |||||
s := "" | |||||
l := len(args) | |||||
if l < 1 { | |||||
fmt.Println("please input some data") | |||||
os.Exit(0) | |||||
} | |||||
i := 1 | |||||
for _, v := range args { | |||||
s += fmt.Sprintf("【"+strconv.Itoa(i)+"】: %#v\n", v) | |||||
i++ | |||||
} | |||||
s = "******************** 【DEBUG - " + time.Now().Format("2006-01-02 15:04:05") + "】 ********************\n" + s + "******************** 【DEBUG - END】 ********************\n" | |||||
fmt.Println(s) | |||||
os.Exit(0) | |||||
} |
@@ -0,0 +1,37 @@ | |||||
package utils | |||||
func RemoveDuplicateString(elms []string) []string { | |||||
res := make([]string, 0, len(elms)) | |||||
temp := map[string]struct{}{} | |||||
for _, item := range elms { | |||||
if _, ok := temp[item]; !ok { | |||||
temp[item] = struct{}{} | |||||
res = append(res, item) | |||||
} | |||||
} | |||||
return res | |||||
} | |||||
func RemoveDuplicateInt(elms []int) []int { | |||||
res := make([]int, 0, len(elms)) | |||||
temp := map[int]struct{}{} | |||||
for _, item := range elms { | |||||
if _, ok := temp[item]; !ok { | |||||
temp[item] = struct{}{} | |||||
res = append(res, item) | |||||
} | |||||
} | |||||
return res | |||||
} | |||||
func RemoveDuplicateInt64(elms []int64) []int64 { | |||||
res := make([]int64, 0, len(elms)) | |||||
temp := map[int64]struct{}{} | |||||
for _, item := range elms { | |||||
if _, ok := temp[item]; !ok { | |||||
temp[item] = struct{}{} | |||||
res = append(res, item) | |||||
} | |||||
} | |||||
return res | |||||
} |
@@ -0,0 +1,22 @@ | |||||
package 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+".log", 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() | |||||
} |
@@ -0,0 +1,29 @@ | |||||
package utils | |||||
import "os" | |||||
// 判断所给路径文件、文件夹是否存在 | |||||
func Exists(path string) bool { | |||||
_, err := os.Stat(path) //os.Stat获取文件信息 | |||||
if err != nil { | |||||
if os.IsExist(err) { | |||||
return true | |||||
} | |||||
return false | |||||
} | |||||
return true | |||||
} | |||||
// 判断所给路径是否为文件夹 | |||||
func IsDir(path string) bool { | |||||
s, err := os.Stat(path) | |||||
if err != nil { | |||||
return false | |||||
} | |||||
return s.IsDir() | |||||
} | |||||
// 判断所给路径是否为文件 | |||||
func IsFile(path string) bool { | |||||
return !IsDir(path) | |||||
} |
@@ -0,0 +1,59 @@ | |||||
package 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 | |||||
} |
@@ -0,0 +1,161 @@ | |||||
package utils | |||||
import ( | |||||
"bytes" | |||||
"encoding/json" | |||||
"log" | |||||
"regexp" | |||||
"strconv" | |||||
"strings" | |||||
"unicode" | |||||
) | |||||
func JsonMarshal(interface{}) { | |||||
} | |||||
// 不科学计数法 | |||||
func JsonDecode(data []byte, v interface{}) error { | |||||
d := json.NewDecoder(bytes.NewReader(data)) | |||||
d.UseNumber() | |||||
return d.Decode(v) | |||||
} | |||||
/*************************************** 下划线json ***************************************/ | |||||
type JsonSnakeCase struct { | |||||
Value interface{} | |||||
} | |||||
func (c JsonSnakeCase) MarshalJSON() ([]byte, error) { | |||||
// Regexp definitions | |||||
var keyMatchRegex = regexp.MustCompile(`\"(\w+)\":`) | |||||
var wordBarrierRegex = regexp.MustCompile(`(\w)([A-Z])`) | |||||
marshalled, err := json.Marshal(c.Value) | |||||
converted := keyMatchRegex.ReplaceAllFunc( | |||||
marshalled, | |||||
func(match []byte) []byte { | |||||
return bytes.ToLower(wordBarrierRegex.ReplaceAll( | |||||
match, | |||||
[]byte(`${1}_${2}`), | |||||
)) | |||||
}, | |||||
) | |||||
return converted, err | |||||
} | |||||
/*************************************** 驼峰json ***************************************/ | |||||
type JsonCamelCase struct { | |||||
Value interface{} | |||||
} | |||||
func (c JsonCamelCase) MarshalJSON() ([]byte, error) { | |||||
var keyMatchRegex = regexp.MustCompile(`\"(\w+)\":`) | |||||
marshalled, err := json.Marshal(c.Value) | |||||
converted := keyMatchRegex.ReplaceAllFunc( | |||||
marshalled, | |||||
func(match []byte) []byte { | |||||
matchStr := string(match) | |||||
key := matchStr[1 : len(matchStr)-2] | |||||
resKey := Lcfirst(Case2Camel(key)) | |||||
return []byte(`"` + resKey + `":`) | |||||
}, | |||||
) | |||||
return converted, err | |||||
} | |||||
// 驼峰式写法转为下划线写法 | |||||
func Camel2Case(name string) string { | |||||
buffer := NewBuffer() | |||||
for i, r := range name { | |||||
if unicode.IsUpper(r) { | |||||
if i != 0 { | |||||
buffer.Append('_') | |||||
} | |||||
buffer.Append(unicode.ToLower(r)) | |||||
} else { | |||||
buffer.Append(r) | |||||
} | |||||
} | |||||
return buffer.String() | |||||
} | |||||
// 下划线写法转为驼峰写法 | |||||
func Case2Camel(name string) string { | |||||
name = strings.Replace(name, "_", " ", -1) | |||||
name = strings.Title(name) | |||||
return strings.Replace(name, " ", "", -1) | |||||
} | |||||
// 首字母大写 | |||||
func Ucfirst(str string) string { | |||||
for i, v := range str { | |||||
return string(unicode.ToUpper(v)) + str[i+1:] | |||||
} | |||||
return "" | |||||
} | |||||
// 首字母小写 | |||||
func Lcfirst(str string) string { | |||||
for i, v := range str { | |||||
return string(unicode.ToLower(v)) + str[i+1:] | |||||
} | |||||
return "" | |||||
} | |||||
// 内嵌bytes.Buffer,支持连写 | |||||
type Buffer struct { | |||||
*bytes.Buffer | |||||
} | |||||
func NewBuffer() *Buffer { | |||||
return &Buffer{Buffer: new(bytes.Buffer)} | |||||
} | |||||
func (b *Buffer) Append(i interface{}) *Buffer { | |||||
switch val := i.(type) { | |||||
case int: | |||||
b.append(strconv.Itoa(val)) | |||||
case int64: | |||||
b.append(strconv.FormatInt(val, 10)) | |||||
case uint: | |||||
b.append(strconv.FormatUint(uint64(val), 10)) | |||||
case uint64: | |||||
b.append(strconv.FormatUint(val, 10)) | |||||
case string: | |||||
b.append(val) | |||||
case []byte: | |||||
b.Write(val) | |||||
case rune: | |||||
b.WriteRune(val) | |||||
} | |||||
return b | |||||
} | |||||
func (b *Buffer) append(s string) *Buffer { | |||||
defer func() { | |||||
if err := recover(); err != nil { | |||||
log.Println("*****内存不够了!******") | |||||
} | |||||
}() | |||||
b.WriteString(s) | |||||
return b | |||||
} | |||||
// json字符串驼峰命名格式 转为 下划线命名格式 | |||||
// c :json字符串 | |||||
func MarshalJSONCamelCase2JsonSnakeCase(c string) []byte { | |||||
// Regexp definitions | |||||
var keyMatchRegex = regexp.MustCompile(`\"(\w+)\":`) | |||||
var wordBarrierRegex = regexp.MustCompile(`(\w)([A-Z])`) | |||||
marshalled := []byte(c) | |||||
converted := keyMatchRegex.ReplaceAllFunc( | |||||
marshalled, | |||||
func(match []byte) []byte { | |||||
return bytes.ToLower(wordBarrierRegex.ReplaceAll( | |||||
match, | |||||
[]byte(`${1}_${2}`), | |||||
)) | |||||
}, | |||||
) | |||||
return converted | |||||
} |
@@ -0,0 +1,31 @@ | |||||
package utils | |||||
import "time" | |||||
// 自定义时间类型 用于解决json解析错误问题 | |||||
type LocalTime time.Time | |||||
const TimeFormat = "2006-01-02 15:04:05" | |||||
// 实现 json解析(反序列化)接口,调用c.ShouldBindJSON()方法时,ShouldBindJSON()方法会调用 | |||||
func (t *LocalTime) UnmarshalJSON(data []byte) (err error) { | |||||
// 空值不进行解析 | |||||
if len(data) == 2 { | |||||
*t = LocalTime(time.Time{}) | |||||
return | |||||
} | |||||
// 指定解析的格式 | |||||
now, err := time.Parse(`"`+TimeFormat+`"`, string(data)) | |||||
*t = LocalTime(now) | |||||
return | |||||
} | |||||
// 实现 json(序列化)接口 | |||||
func (t LocalTime) MarshalJSON() ([]byte, error) { | |||||
b := make([]byte, 0, len(TimeFormat)+2) | |||||
b = append(b, '"') | |||||
b = time.Time(t).AppendFormat(b, TimeFormat) | |||||
b = append(b, '"') | |||||
return b, nil | |||||
} |
@@ -0,0 +1,245 @@ | |||||
package 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 | |||||
} |
@@ -0,0 +1,105 @@ | |||||
package 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 | |||||
} |
@@ -0,0 +1,192 @@ | |||||
package 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) | |||||
} |
@@ -0,0 +1,55 @@ | |||||
package utils | |||||
// MapStringKeys 取出map的key | |||||
func MapStringKeys(collection *map[string]struct{}) []interface{} { | |||||
result := make([]interface{}, 0, len(*collection)) | |||||
for key := range *collection { | |||||
result = append(result, key) | |||||
} | |||||
return result | |||||
} | |||||
// GetOneKeyOfMapString 取出Map的一个key | |||||
func GetOneKeyOfMapString(collection map[string]string) string { | |||||
for k := range collection { | |||||
return k | |||||
} | |||||
return "" | |||||
} | |||||
// sources源数组,num拆分份数,size每份的大小 | |||||
func SplitArray(sources []string, num, pageSize int64) [][]string { | |||||
max := int64(len(sources)) | |||||
if max < num { | |||||
return nil | |||||
} | |||||
var segmens = make([][]string, 0) | |||||
quantity := pageSize | |||||
end := int64(0) | |||||
for i := int64(1); i <= num; i++ { | |||||
qu := i * quantity | |||||
if i != num { | |||||
segmens = append(segmens, sources[i-1+end:qu]) | |||||
} else { | |||||
segmens = append(segmens, sources[i-1+end:]) | |||||
} | |||||
end = qu - i | |||||
} | |||||
return segmens | |||||
} | |||||
// sourceslen源数组长度,pageSize页数据量 | |||||
// 获取拆分份数 | |||||
func SplitArrayCnt(sourceslen, pageSize int) int { | |||||
if sourceslen < pageSize { | |||||
return 1 | |||||
} | |||||
s := sourceslen / pageSize | |||||
y := sourceslen % pageSize | |||||
if y > 0 { | |||||
return s + 1 | |||||
} else { | |||||
return s | |||||
} | |||||
} |
@@ -0,0 +1,341 @@ | |||||
package utils | |||||
import ( | |||||
"encoding/json" | |||||
"fmt" | |||||
"reflect" | |||||
"strconv" | |||||
"strings" | |||||
) | |||||
func Map2Struct(vals map[string]interface{}, dst interface{}) (err error) { | |||||
return Map2StructByTag(vals, dst, "json") | |||||
} | |||||
func Map2StructByTag(vals map[string]interface{}, dst interface{}, structTag string) (err error) { | |||||
defer func() { | |||||
e := recover() | |||||
if e != nil { | |||||
if v, ok := e.(error); ok { | |||||
err = fmt.Errorf("Panic: %v", v.Error()) | |||||
} else { | |||||
err = fmt.Errorf("Panic: %v", e) | |||||
} | |||||
} | |||||
}() | |||||
pt := reflect.TypeOf(dst) | |||||
pv := reflect.ValueOf(dst) | |||||
if pv.Kind() != reflect.Ptr || pv.Elem().Kind() != reflect.Struct { | |||||
return fmt.Errorf("not a pointer of struct") | |||||
} | |||||
var f reflect.StructField | |||||
var ft reflect.Type | |||||
var fv reflect.Value | |||||
for i := 0; i < pt.Elem().NumField(); i++ { | |||||
f = pt.Elem().Field(i) | |||||
fv = pv.Elem().Field(i) | |||||
ft = f.Type | |||||
if f.Anonymous || !fv.CanSet() { | |||||
continue | |||||
} | |||||
tag := f.Tag.Get(structTag) | |||||
name, option := parseTag(tag) | |||||
if name == "-" { | |||||
continue | |||||
} | |||||
if name == "" { | |||||
name = strings.ToLower(f.Name) | |||||
} | |||||
val, ok := vals[name] | |||||
if !ok { | |||||
if option == "required" { | |||||
return fmt.Errorf("'%v' not found", name) | |||||
} | |||||
if len(option) != 0 { | |||||
val = option // default value | |||||
} else { | |||||
//fv.Set(reflect.Zero(ft)) // TODO set zero value or just ignore it? | |||||
continue | |||||
} | |||||
} | |||||
// convert or set value to field | |||||
vv := reflect.ValueOf(val) | |||||
vt := reflect.TypeOf(val) | |||||
if vt.Kind() != reflect.String { | |||||
// try to assign and convert | |||||
if vt.AssignableTo(ft) { | |||||
fv.Set(vv) | |||||
continue | |||||
} | |||||
if vt.ConvertibleTo(ft) { | |||||
fv.Set(vv.Convert(ft)) | |||||
continue | |||||
} | |||||
return fmt.Errorf("value type not match: field=%v(%v) value=%v(%v)", f.Name, ft.Kind(), val, vt.Kind()) | |||||
} | |||||
s := strings.TrimSpace(vv.String()) | |||||
if len(s) == 0 && option == "required" { | |||||
return fmt.Errorf("value of required argument can't not be empty") | |||||
} | |||||
fk := ft.Kind() | |||||
// convert string to value | |||||
if fk == reflect.Ptr && ft.Elem().Kind() == reflect.String { | |||||
fv.Set(reflect.ValueOf(&s)) | |||||
continue | |||||
} | |||||
if fk == reflect.Ptr || fk == reflect.Struct { | |||||
err = convertJsonValue(s, name, fv) | |||||
} else if fk == reflect.Slice { | |||||
err = convertSlice(s, f.Name, ft, fv) | |||||
} else { | |||||
err = convertValue(fk, s, f.Name, fv) | |||||
} | |||||
if err != nil { | |||||
return err | |||||
} | |||||
continue | |||||
} | |||||
return nil | |||||
} | |||||
func Struct2Map(s interface{}) map[string]interface{} { | |||||
return Struct2MapByTag(s, "json") | |||||
} | |||||
func Struct2MapByTag(s interface{}, tagName string) map[string]interface{} { | |||||
t := reflect.TypeOf(s) | |||||
v := reflect.ValueOf(s) | |||||
if v.Kind() == reflect.Ptr && v.Elem().Kind() == reflect.Struct { | |||||
t = t.Elem() | |||||
v = v.Elem() | |||||
} | |||||
if v.Kind() != reflect.Struct { | |||||
return nil | |||||
} | |||||
m := make(map[string]interface{}) | |||||
for i := 0; i < t.NumField(); i++ { | |||||
fv := v.Field(i) | |||||
ft := t.Field(i) | |||||
if !fv.CanInterface() { | |||||
continue | |||||
} | |||||
if ft.PkgPath != "" { // unexported | |||||
continue | |||||
} | |||||
var name string | |||||
var option string | |||||
tag := ft.Tag.Get(tagName) | |||||
if tag != "" { | |||||
ts := strings.Split(tag, ",") | |||||
if len(ts) == 1 { | |||||
name = ts[0] | |||||
} else if len(ts) > 1 { | |||||
name = ts[0] | |||||
option = ts[1] | |||||
} | |||||
if name == "-" { | |||||
continue // skip this field | |||||
} | |||||
if name == "" { | |||||
name = strings.ToLower(ft.Name) | |||||
} | |||||
if option == "omitempty" { | |||||
if isEmpty(&fv) { | |||||
continue // skip empty field | |||||
} | |||||
} | |||||
} else { | |||||
name = strings.ToLower(ft.Name) | |||||
} | |||||
if ft.Anonymous && fv.Kind() == reflect.Ptr && fv.IsNil() { | |||||
continue | |||||
} | |||||
if (ft.Anonymous && fv.Kind() == reflect.Struct) || | |||||
(ft.Anonymous && fv.Kind() == reflect.Ptr && fv.Elem().Kind() == reflect.Struct) { | |||||
// embedded struct | |||||
embedded := Struct2MapByTag(fv.Interface(), tagName) | |||||
for embName, embValue := range embedded { | |||||
m[embName] = embValue | |||||
} | |||||
} else if option == "string" { | |||||
kind := fv.Kind() | |||||
if kind == reflect.Int || kind == reflect.Int8 || kind == reflect.Int16 || kind == reflect.Int32 || kind == reflect.Int64 { | |||||
m[name] = strconv.FormatInt(fv.Int(), 10) | |||||
} else if kind == reflect.Uint || kind == reflect.Uint8 || kind == reflect.Uint16 || kind == reflect.Uint32 || kind == reflect.Uint64 { | |||||
m[name] = strconv.FormatUint(fv.Uint(), 10) | |||||
} else if kind == reflect.Float32 || kind == reflect.Float64 { | |||||
m[name] = strconv.FormatFloat(fv.Float(), 'f', 2, 64) | |||||
} else { | |||||
m[name] = fv.Interface() | |||||
} | |||||
} else { | |||||
m[name] = fv.Interface() | |||||
} | |||||
} | |||||
return m | |||||
} | |||||
func isEmpty(v *reflect.Value) bool { | |||||
k := v.Kind() | |||||
if k == reflect.Bool { | |||||
return v.Bool() == false | |||||
} else if reflect.Int < k && k < reflect.Int64 { | |||||
return v.Int() == 0 | |||||
} else if reflect.Uint < k && k < reflect.Uintptr { | |||||
return v.Uint() == 0 | |||||
} else if k == reflect.Float32 || k == reflect.Float64 { | |||||
return v.Float() == 0 | |||||
} else if k == reflect.Array || k == reflect.Map || k == reflect.Slice || k == reflect.String { | |||||
return v.Len() == 0 | |||||
} else if k == reflect.Interface || k == reflect.Ptr { | |||||
return v.IsNil() | |||||
} | |||||
return false | |||||
} | |||||
func convertSlice(s string, name string, ft reflect.Type, fv reflect.Value) error { | |||||
var err error | |||||
et := ft.Elem() | |||||
if et.Kind() == reflect.Ptr || et.Kind() == reflect.Struct { | |||||
return convertJsonValue(s, name, fv) | |||||
} | |||||
ss := strings.Split(s, ",") | |||||
if len(s) == 0 || len(ss) == 0 { | |||||
return nil | |||||
} | |||||
fs := reflect.MakeSlice(ft, 0, len(ss)) | |||||
for _, si := range ss { | |||||
ev := reflect.New(et).Elem() | |||||
err = convertValue(et.Kind(), si, name, ev) | |||||
if err != nil { | |||||
return err | |||||
} | |||||
fs = reflect.Append(fs, ev) | |||||
} | |||||
fv.Set(fs) | |||||
return nil | |||||
} | |||||
func convertJsonValue(s string, name string, fv reflect.Value) error { | |||||
var err error | |||||
d := StringToSlice(s) | |||||
if fv.Kind() == reflect.Ptr { | |||||
if fv.IsNil() { | |||||
fv.Set(reflect.New(fv.Type().Elem())) | |||||
} | |||||
} else { | |||||
fv = fv.Addr() | |||||
} | |||||
err = json.Unmarshal(d, fv.Interface()) | |||||
if err != nil { | |||||
return fmt.Errorf("invalid json '%v': %v, %v", name, err.Error(), s) | |||||
} | |||||
return nil | |||||
} | |||||
func convertValue(kind reflect.Kind, s string, name string, fv reflect.Value) error { | |||||
if !fv.CanAddr() { | |||||
return fmt.Errorf("can not addr: %v", name) | |||||
} | |||||
if kind == reflect.String { | |||||
fv.SetString(s) | |||||
return nil | |||||
} | |||||
if kind == reflect.Bool { | |||||
switch s { | |||||
case "true": | |||||
fv.SetBool(true) | |||||
case "false": | |||||
fv.SetBool(false) | |||||
case "1": | |||||
fv.SetBool(true) | |||||
case "0": | |||||
fv.SetBool(false) | |||||
default: | |||||
return fmt.Errorf("invalid bool: %v value=%v", name, s) | |||||
} | |||||
return nil | |||||
} | |||||
if reflect.Int <= kind && kind <= reflect.Int64 { | |||||
i, err := strconv.ParseInt(s, 10, 64) | |||||
if err != nil { | |||||
return fmt.Errorf("invalid int: %v value=%v", name, s) | |||||
} | |||||
fv.SetInt(i) | |||||
} else if reflect.Uint <= kind && kind <= reflect.Uint64 { | |||||
i, err := strconv.ParseUint(s, 10, 64) | |||||
if err != nil { | |||||
return fmt.Errorf("invalid int: %v value=%v", name, s) | |||||
} | |||||
fv.SetUint(i) | |||||
} else if reflect.Float32 == kind || kind == reflect.Float64 { | |||||
i, err := strconv.ParseFloat(s, 64) | |||||
if err != nil { | |||||
return fmt.Errorf("invalid float: %v value=%v", name, s) | |||||
} | |||||
fv.SetFloat(i) | |||||
} else { | |||||
// not support or just ignore it? | |||||
// return fmt.Errorf("type not support: field=%v(%v) value=%v(%v)", name, ft.Kind(), val, vt.Kind()) | |||||
} | |||||
return nil | |||||
} | |||||
func parseTag(tag string) (string, string) { | |||||
tags := strings.Split(tag, ",") | |||||
if len(tags) <= 0 { | |||||
return "", "" | |||||
} | |||||
if len(tags) == 1 { | |||||
return tags[0], "" | |||||
} | |||||
return tags[0], tags[1] | |||||
} |
@@ -0,0 +1,12 @@ | |||||
package utils | |||||
import ( | |||||
"crypto/md5" | |||||
"encoding/hex" | |||||
) | |||||
func Md5(str string) string { | |||||
h := md5.New() | |||||
h.Write([]byte(str)) | |||||
return hex.EncodeToString(h.Sum(nil)) | |||||
} |
@@ -0,0 +1,33 @@ | |||||
package qrcode | |||||
import ( | |||||
"image" | |||||
_ "image/jpeg" | |||||
_ "image/png" | |||||
"os" | |||||
"github.com/makiuchi-d/gozxing" | |||||
"github.com/makiuchi-d/gozxing/qrcode" | |||||
) | |||||
func DecodeFile(fi string) (string, error) { | |||||
file, err := os.Open(fi) | |||||
if err != nil { | |||||
return "", err | |||||
} | |||||
img, _, err := image.Decode(file) | |||||
if err != nil { | |||||
return "", err | |||||
} | |||||
// prepare BinaryBitmap | |||||
bmp, err := gozxing.NewBinaryBitmapFromImage(img) | |||||
if err != nil { | |||||
return "", err | |||||
} | |||||
// decode image | |||||
result, err := qrcode.NewQRCodeReader().Decode(bmp, nil) | |||||
if err != nil { | |||||
return "", err | |||||
} | |||||
return result.String(), nil | |||||
} |
@@ -0,0 +1,43 @@ | |||||
package qrcode | |||||
// 生成登录二维码图片, 方便在网页上显示 | |||||
import ( | |||||
"bytes" | |||||
"encoding/base64" | |||||
"image/jpeg" | |||||
"image/png" | |||||
"github.com/boombuler/barcode" | |||||
"github.com/boombuler/barcode/qr" | |||||
) | |||||
func GetJPGBase64(content string, edges ...int) string { | |||||
edgeLen := 300 | |||||
if len(edges) > 0 && edges[0] > 100 && edges[0] < 2000 { | |||||
edgeLen = edges[0] | |||||
} | |||||
img, _ := qr.Encode(content, qr.L, qr.Unicode) | |||||
img, _ = barcode.Scale(img, edgeLen, edgeLen) | |||||
emptyBuff := bytes.NewBuffer(nil) // 开辟一个新的空buff缓冲区 | |||||
jpeg.Encode(emptyBuff, img, nil) | |||||
dist := make([]byte, 50000) // 开辟存储空间 | |||||
base64.StdEncoding.Encode(dist, emptyBuff.Bytes()) // buff转成base64 | |||||
return "data:image/png;base64," + string(dist) // 输出图片base64(type = []byte) | |||||
} | |||||
func GetPNGBase64(content string, edges ...int) string { | |||||
edgeLen := 300 | |||||
if len(edges) > 0 && edges[0] > 100 && edges[0] < 2000 { | |||||
edgeLen = edges[0] | |||||
} | |||||
img, _ := qr.Encode(content, qr.L, qr.Unicode) | |||||
img, _ = barcode.Scale(img, edgeLen, edgeLen) | |||||
emptyBuff := bytes.NewBuffer(nil) // 开辟一个新的空buff缓冲区 | |||||
png.Encode(emptyBuff, img) | |||||
dist := make([]byte, 50000) // 开辟存储空间 | |||||
base64.StdEncoding.Encode(dist, emptyBuff.Bytes()) // buff转成base64 | |||||
return string(dist) // 输出图片base64(type = []byte) | |||||
} |
@@ -0,0 +1,85 @@ | |||||
package qrcode | |||||
// 生成登录二维码图片 | |||||
import ( | |||||
"errors" | |||||
"image" | |||||
"image/jpeg" | |||||
"image/png" | |||||
"os" | |||||
"path/filepath" | |||||
"strings" | |||||
"github.com/boombuler/barcode" | |||||
"github.com/boombuler/barcode/qr" | |||||
) | |||||
func SaveJpegFile(filePath, content string, edges ...int) error { | |||||
edgeLen := 300 | |||||
if len(edges) > 0 && edges[0] > 100 && edges[0] < 2000 { | |||||
edgeLen = edges[0] | |||||
} | |||||
img, _ := qr.Encode(content, qr.L, qr.Unicode) | |||||
img, _ = barcode.Scale(img, edgeLen, edgeLen) | |||||
return writeFile(filePath, img, "jpg") | |||||
} | |||||
func SavePngFile(filePath, content string, edges ...int) error { | |||||
edgeLen := 300 | |||||
if len(edges) > 0 && edges[0] > 100 && edges[0] < 2000 { | |||||
edgeLen = edges[0] | |||||
} | |||||
img, _ := qr.Encode(content, qr.L, qr.Unicode) | |||||
img, _ = barcode.Scale(img, edgeLen, edgeLen) | |||||
return writeFile(filePath, img, "png") | |||||
} | |||||
func writeFile(filePath string, img image.Image, format string) error { | |||||
if err := createDir(filePath); err != nil { | |||||
return err | |||||
} | |||||
file, err := os.Create(filePath) | |||||
defer file.Close() | |||||
if err != nil { | |||||
return err | |||||
} | |||||
switch strings.ToLower(format) { | |||||
case "png": | |||||
err = png.Encode(file, img) | |||||
break | |||||
case "jpg": | |||||
err = jpeg.Encode(file, img, nil) | |||||
default: | |||||
return errors.New("format not accept") | |||||
} | |||||
if err != nil { | |||||
return err | |||||
} | |||||
return nil | |||||
} | |||||
func createDir(filePath string) error { | |||||
var err error | |||||
// filePath, _ = filepath.Abs(filePath) | |||||
dirPath := filepath.Dir(filePath) | |||||
dirInfo, err := os.Stat(dirPath) | |||||
if err != nil { | |||||
if !os.IsExist(err) { | |||||
err = os.MkdirAll(dirPath, 0777) | |||||
if err != nil { | |||||
return err | |||||
} | |||||
} else { | |||||
return err | |||||
} | |||||
} else { | |||||
if dirInfo.IsDir() { | |||||
return nil | |||||
} | |||||
return errors.New("directory is a file") | |||||
} | |||||
return nil | |||||
} |
@@ -0,0 +1,39 @@ | |||||
package qrcode | |||||
import ( | |||||
"bytes" | |||||
"image/jpeg" | |||||
"image/png" | |||||
"net/http" | |||||
"github.com/boombuler/barcode" | |||||
"github.com/boombuler/barcode/qr" | |||||
) | |||||
func WritePng(w http.ResponseWriter, content string, edges ...int) error { | |||||
edgeLen := 300 | |||||
if len(edges) > 0 && edges[0] > 100 && edges[0] < 2000 { | |||||
edgeLen = edges[0] | |||||
} | |||||
img, _ := qr.Encode(content, qr.L, qr.Unicode) | |||||
img, _ = barcode.Scale(img, edgeLen, edgeLen) | |||||
buff := bytes.NewBuffer(nil) | |||||
png.Encode(buff, img) | |||||
w.Header().Set("Content-Type", "image/png") | |||||
_, err := w.Write(buff.Bytes()) | |||||
return err | |||||
} | |||||
func WriteJpg(w http.ResponseWriter, content string, edges ...int) error { | |||||
edgeLen := 300 | |||||
if len(edges) > 0 && edges[0] > 100 && edges[0] < 2000 { | |||||
edgeLen = edges[0] | |||||
} | |||||
img, _ := qr.Encode(content, qr.L, qr.Unicode) | |||||
img, _ = barcode.Scale(img, edgeLen, edgeLen) | |||||
buff := bytes.NewBuffer(nil) | |||||
jpeg.Encode(buff, img, nil) | |||||
w.Header().Set("Content-Type", "image/jpg") | |||||
_, err := w.Write(buff.Bytes()) | |||||
return err | |||||
} |
@@ -0,0 +1,41 @@ | |||||
package utils | |||||
import ( | |||||
crand "crypto/rand" | |||||
"fmt" | |||||
"github.com/syyongx/php2go" | |||||
"math" | |||||
"math/big" | |||||
"math/rand" | |||||
"time" | |||||
) | |||||
func RandString(l int, c ...string) string { | |||||
var ( | |||||
chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" | |||||
str string | |||||
num *big.Int | |||||
) | |||||
if len(c) > 0 { | |||||
chars = c[0] | |||||
} | |||||
chrLen := int64(len(chars)) | |||||
for len(str) < l { | |||||
num, _ = crand.Int(crand.Reader, big.NewInt(chrLen)) | |||||
str += string(chars[num.Int64()]) | |||||
} | |||||
return str | |||||
} | |||||
//x的y次方 | |||||
func RandPow(l int) int { | |||||
min := int(math.Pow10(l - 1)) | |||||
max := int(math.Pow10(l) - 1) | |||||
return php2go.Rand(min, max) | |||||
} | |||||
func RandNum() string { | |||||
seed := time.Now().UnixNano() + rand.Int63() | |||||
return fmt.Sprintf("%05v", rand.New(rand.NewSource(seed)).Int31n(1000000)) | |||||
} |
@@ -0,0 +1,36 @@ | |||||
package utils | |||||
import ( | |||||
"applet/app/cfg" | |||||
"applet/app/utils/cache" | |||||
"fmt" | |||||
"github.com/gin-gonic/gin" | |||||
) | |||||
func ClearRedis(c *gin.Context) { | |||||
var str = []string{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"} | |||||
key := fmt.Sprintf("%s:cfg_cache", c.GetString("mid")) | |||||
cache.Del(key) | |||||
key2 := fmt.Sprintf("%s:virtual_coin_cfg", c.GetString("mid")) | |||||
cache.Del(key2) | |||||
key3 := fmt.Sprintf("%s:virtual_coin_cfg_comm", c.GetString("mid")) | |||||
cache.Del(key3) | |||||
for _, v := range str { | |||||
key1 := fmt.Sprintf("%s:cfg_cache:%s", c.GetString("mid"), v) | |||||
cache.Del(key1) | |||||
} | |||||
if cfg.Prd == false { | |||||
key := fmt.Sprintf("%s:cfg_cache", "22255132") | |||||
cache.Del(key) | |||||
key2 := fmt.Sprintf("%s:virtual_coin_cfg", "22255132") | |||||
cache.Del(key2) | |||||
key3 := fmt.Sprintf("%s:virtual_coin_cfg_comm", "22255132") | |||||
cache.Del(key3) | |||||
for _, v := range str { | |||||
key1 := fmt.Sprintf("%s:cfg_cache:%s", "22255132", v) | |||||
cache.Del(key1) | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,170 @@ | |||||
package utils | |||||
import ( | |||||
"bytes" | |||||
"crypto/rand" | |||||
"crypto/rsa" | |||||
"crypto/x509" | |||||
"encoding/pem" | |||||
"errors" | |||||
"fmt" | |||||
"io/ioutil" | |||||
"log" | |||||
"os" | |||||
) | |||||
// 生成私钥文件 TODO 未指定路径 | |||||
func RsaKeyGen(bits int) error { | |||||
privateKey, err := rsa.GenerateKey(rand.Reader, bits) | |||||
if err != nil { | |||||
return err | |||||
} | |||||
derStream := x509.MarshalPKCS1PrivateKey(privateKey) | |||||
block := &pem.Block{ | |||||
Type: "RSA PRIVATE KEY", | |||||
Bytes: derStream, | |||||
} | |||||
priFile, err := os.Create("private.pem") | |||||
if err != nil { | |||||
return err | |||||
} | |||||
err = pem.Encode(priFile, block) | |||||
priFile.Close() | |||||
if err != nil { | |||||
return err | |||||
} | |||||
// 生成公钥文件 | |||||
publicKey := &privateKey.PublicKey | |||||
derPkix, err := x509.MarshalPKIXPublicKey(publicKey) | |||||
if err != nil { | |||||
return err | |||||
} | |||||
block = &pem.Block{ | |||||
Type: "PUBLIC KEY", | |||||
Bytes: derPkix, | |||||
} | |||||
pubFile, err := os.Create("public.pem") | |||||
if err != nil { | |||||
return err | |||||
} | |||||
err = pem.Encode(pubFile, block) | |||||
pubFile.Close() | |||||
if err != nil { | |||||
return err | |||||
} | |||||
return nil | |||||
} | |||||
// 生成私钥文件, 返回 privateKey , publicKey, error | |||||
func RsaKeyGenText(bits int) (string, string, error) { // bits 字节位 1024/2048 | |||||
privateKey, err := rsa.GenerateKey(rand.Reader, bits) | |||||
if err != nil { | |||||
return "", "", err | |||||
} | |||||
derStream := x509.MarshalPKCS1PrivateKey(privateKey) | |||||
block := &pem.Block{ | |||||
Type: "RSA PRIVATE KEY", | |||||
Bytes: derStream, | |||||
} | |||||
priBuff := bytes.NewBuffer(nil) | |||||
err = pem.Encode(priBuff, block) | |||||
if err != nil { | |||||
return "", "", err | |||||
} | |||||
// 生成公钥文件 | |||||
publicKey := &privateKey.PublicKey | |||||
derPkix, err := x509.MarshalPKIXPublicKey(publicKey) | |||||
if err != nil { | |||||
return "", "", err | |||||
} | |||||
block = &pem.Block{ | |||||
Type: "PUBLIC KEY", | |||||
Bytes: derPkix, | |||||
} | |||||
pubBuff := bytes.NewBuffer(nil) | |||||
err = pem.Encode(pubBuff, block) | |||||
if err != nil { | |||||
return "", "", err | |||||
} | |||||
return priBuff.String(), pubBuff.String(), nil | |||||
} | |||||
// 加密 | |||||
func RsaEncrypt(rawData, publicKey []byte) ([]byte, error) { | |||||
block, _ := pem.Decode(publicKey) | |||||
if block == nil { | |||||
return nil, errors.New("public key error") | |||||
} | |||||
pubInterface, err := x509.ParsePKIXPublicKey(block.Bytes) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
pub := pubInterface.(*rsa.PublicKey) | |||||
return rsa.EncryptPKCS1v15(rand.Reader, pub, rawData) | |||||
} | |||||
// 公钥加密 | |||||
func RsaEncrypts(data, keyBytes []byte) []byte { | |||||
//解密pem格式的公钥 | |||||
block, _ := pem.Decode(keyBytes) | |||||
if block == nil { | |||||
panic(errors.New("public key error")) | |||||
} | |||||
// 解析公钥 | |||||
pubInterface, err := x509.ParsePKIXPublicKey(block.Bytes) | |||||
if err != nil { | |||||
panic(err) | |||||
} | |||||
// 类型断言 | |||||
pub := pubInterface.(*rsa.PublicKey) | |||||
//加密 | |||||
ciphertext, err := rsa.EncryptPKCS1v15(rand.Reader, pub, data) | |||||
if err != nil { | |||||
panic(err) | |||||
} | |||||
return ciphertext | |||||
} | |||||
// 解密 | |||||
func RsaDecrypt(cipherText, privateKey []byte) ([]byte, error) { | |||||
block, _ := pem.Decode(privateKey) | |||||
if block == nil { | |||||
return nil, errors.New("private key error") | |||||
} | |||||
priv, err := x509.ParsePKCS1PrivateKey(block.Bytes) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
return rsa.DecryptPKCS1v15(rand.Reader, priv, cipherText) | |||||
} | |||||
// 从证书获取公钥 | |||||
func OpensslPemGetPublic(pathOrString string) (interface{}, error) { | |||||
var certPem []byte | |||||
var err error | |||||
if IsFile(pathOrString) && Exists(pathOrString) { | |||||
certPem, err = ioutil.ReadFile(pathOrString) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
if string(certPem) == "" { | |||||
return nil, errors.New("empty pem file") | |||||
} | |||||
} else { | |||||
if pathOrString == "" { | |||||
return nil, errors.New("empty pem string") | |||||
} | |||||
certPem = StringToSlice(pathOrString) | |||||
} | |||||
block, rest := pem.Decode(certPem) | |||||
if block == nil || block.Type != "PUBLIC KEY" { | |||||
//log.Fatal("failed to decode PEM block containing public key") | |||||
return nil, errors.New("failed to decode PEM block containing public key") | |||||
} | |||||
pub, err := x509.ParsePKIXPublicKey(block.Bytes) | |||||
if err != nil { | |||||
log.Fatal(err) | |||||
} | |||||
fmt.Printf("Got a %T, with remaining data: %q", pub, rest) | |||||
return pub, nil | |||||
} |
@@ -0,0 +1,23 @@ | |||||
package 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)) | |||||
} |
@@ -0,0 +1,48 @@ | |||||
package utils | |||||
import ( | |||||
"math/rand" | |||||
"time" | |||||
) | |||||
// 打乱随机字符串 | |||||
func ShuffleString(s *string) { | |||||
if len(*s) > 1 { | |||||
b := []byte(*s) | |||||
rand.Seed(time.Now().UnixNano()) | |||||
rand.Shuffle(len(b), func(x, y int) { | |||||
b[x], b[y] = b[y], b[x] | |||||
}) | |||||
*s = string(b) | |||||
} | |||||
} | |||||
// 打乱随机slice | |||||
func ShuffleSliceBytes(b []byte) { | |||||
if len(b) > 1 { | |||||
rand.Seed(time.Now().UnixNano()) | |||||
rand.Shuffle(len(b), func(x, y int) { | |||||
b[x], b[y] = b[y], b[x] | |||||
}) | |||||
} | |||||
} | |||||
// 打乱slice int | |||||
func ShuffleSliceInt(i []int) { | |||||
if len(i) > 1 { | |||||
rand.Seed(time.Now().UnixNano()) | |||||
rand.Shuffle(len(i), func(x, y int) { | |||||
i[x], i[y] = i[y], i[x] | |||||
}) | |||||
} | |||||
} | |||||
// 打乱slice interface | |||||
func ShuffleSliceInterface(i []interface{}) { | |||||
if len(i) > 1 { | |||||
rand.Seed(time.Now().UnixNano()) | |||||
rand.Shuffle(len(i), func(x, y int) { | |||||
i[x], i[y] = i[y], i[x] | |||||
}) | |||||
} | |||||
} |
@@ -0,0 +1,70 @@ | |||||
package utils | |||||
// ContainsString is 字符串是否包含在字符串切片里 | |||||
func ContainsString(array []string, val string) (index int) { | |||||
index = -1 | |||||
for i := 0; i < len(array); i++ { | |||||
if array[i] == val { | |||||
index = i | |||||
return | |||||
} | |||||
} | |||||
return | |||||
} | |||||
//ArrayDiff 模拟PHP array_diff函数 | |||||
func ArrayDiff(array1 []interface{}, othersParams ...[]interface{}) ([]interface{}, error) { | |||||
if len(array1) == 0 { | |||||
return []interface{}{}, nil | |||||
} | |||||
if len(array1) > 0 && len(othersParams) == 0 { | |||||
return array1, nil | |||||
} | |||||
var tmp = make(map[interface{}]int, len(array1)) | |||||
for _, v := range array1 { | |||||
tmp[v] = 1 | |||||
} | |||||
for _, param := range othersParams { | |||||
for _, arg := range param { | |||||
if tmp[arg] != 0 { | |||||
tmp[arg]++ | |||||
} | |||||
} | |||||
} | |||||
var res = make([]interface{}, 0, len(tmp)) | |||||
for k, v := range tmp { | |||||
if v == 1 { | |||||
res = append(res, k) | |||||
} | |||||
} | |||||
return res, nil | |||||
} | |||||
//ArrayIntersect 模拟PHP array_intersect函数 | |||||
func ArrayIntersect(array1 []interface{}, othersParams ...[]interface{}) ([]interface{}, error) { | |||||
if len(array1) == 0 { | |||||
return []interface{}{}, nil | |||||
} | |||||
if len(array1) > 0 && len(othersParams) == 0 { | |||||
return array1, nil | |||||
} | |||||
var tmp = make(map[interface{}]int, len(array1)) | |||||
for _, v := range array1 { | |||||
tmp[v] = 1 | |||||
} | |||||
for _, param := range othersParams { | |||||
for _, arg := range param { | |||||
if tmp[arg] != 0 { | |||||
tmp[arg]++ | |||||
} | |||||
} | |||||
} | |||||
var res = make([]interface{}, 0, len(tmp)) | |||||
for k, v := range tmp { | |||||
if v > 1 { | |||||
res = append(res, k) | |||||
} | |||||
} | |||||
return res, nil | |||||
} |
@@ -0,0 +1,47 @@ | |||||
package utils | |||||
import ( | |||||
"fmt" | |||||
"reflect" | |||||
"strings" | |||||
"unsafe" | |||||
) | |||||
// string与slice互转,零copy省内存 | |||||
// zero copy to change slice to string | |||||
func Slice2String(b []byte) (s string) { | |||||
pBytes := (*reflect.SliceHeader)(unsafe.Pointer(&b)) | |||||
pString := (*reflect.StringHeader)(unsafe.Pointer(&s)) | |||||
pString.Data = pBytes.Data | |||||
pString.Len = pBytes.Len | |||||
return | |||||
} | |||||
// no copy to change string to slice | |||||
func StringToSlice(s string) (b []byte) { | |||||
pBytes := (*reflect.SliceHeader)(unsafe.Pointer(&b)) | |||||
pString := (*reflect.StringHeader)(unsafe.Pointer(&s)) | |||||
pBytes.Data = pString.Data | |||||
pBytes.Len = pString.Len | |||||
pBytes.Cap = pString.Len | |||||
return | |||||
} | |||||
// 任意slice合并 | |||||
func SliceJoin(sep string, elems ...interface{}) string { | |||||
l := len(elems) | |||||
if l == 0 { | |||||
return "" | |||||
} | |||||
if l == 1 { | |||||
s := fmt.Sprint(elems[0]) | |||||
sLen := len(s) - 1 | |||||
if s[0] == '[' && s[sLen] == ']' { | |||||
return strings.Replace(s[1:sLen], " ", sep, -1) | |||||
} | |||||
return s | |||||
} | |||||
sep = strings.Replace(fmt.Sprint(elems), " ", sep, -1) | |||||
return sep[1 : len(sep)-1] | |||||
} |
@@ -0,0 +1,96 @@ | |||||
package utils | |||||
import ( | |||||
"fmt" | |||||
"reflect" | |||||
"strings" | |||||
) | |||||
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 ImplodeByKey(glue string, args ...interface{}) string { | |||||
data := make([]string, len(args)) | |||||
for i, _ := range args { | |||||
data[i] = fmt.Sprint(i) | |||||
} | |||||
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 | |||||
} | |||||
//把数组的值放到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 | |||||
} |
@@ -0,0 +1,181 @@ | |||||
package utils | |||||
import ( | |||||
"errors" | |||||
"fmt" | |||||
"strconv" | |||||
"strings" | |||||
"time" | |||||
) | |||||
func StrToTime(s string) (int64, error) { | |||||
// delete all not int characters | |||||
if s == "" { | |||||
return time.Now().Unix(), nil | |||||
} | |||||
r := make([]rune, 14) | |||||
l := 0 | |||||
// 过滤除数字以外的字符 | |||||
for _, v := range s { | |||||
if '0' <= v && v <= '9' { | |||||
r[l] = v | |||||
l++ | |||||
if l == 14 { | |||||
break | |||||
} | |||||
} | |||||
} | |||||
for l < 14 { | |||||
r[l] = '0' // 补0 | |||||
l++ | |||||
} | |||||
t, err := time.Parse("20060102150405", string(r)) | |||||
if err != nil { | |||||
return 0, err | |||||
} | |||||
return t.Unix(), nil | |||||
} | |||||
func Time2String(date time.Time, format string) string { | |||||
if format == "" { | |||||
format = "2006-01-02 15:04:05" | |||||
} | |||||
timeS := date.Format(format) | |||||
if timeS == "0001-01-01 00:00:00" || strings.Contains(timeS, "0001-01-01") { | |||||
return "" | |||||
} | |||||
return timeS | |||||
} | |||||
func String2Time(timeStr string) time.Time { | |||||
toTime, err := time.ParseInLocation("2006-01-02 15:04:05", timeStr, time.Local) | |||||
if err != nil { | |||||
return time.Now() | |||||
} | |||||
return toTime | |||||
} | |||||
func TimeToStr(unixSecTime interface{}, layout ...string) string { | |||||
i := AnyToInt64(unixSecTime) | |||||
if i == 0 { | |||||
return "" | |||||
} | |||||
f := "2006-01-02 15:04:05" | |||||
if len(layout) > 0 { | |||||
f = layout[0] | |||||
} | |||||
return time.Unix(i, 0).Format(f) | |||||
} | |||||
func FormatNanoUnix() string { | |||||
return strings.Replace(time.Now().Format("20060102150405.0000000"), ".", "", 1) | |||||
} | |||||
func TimeParse(format, src string) (time.Time, error) { | |||||
return time.ParseInLocation(format, src, time.Local) | |||||
} | |||||
func TimeParseStd(src string) time.Time { | |||||
t, _ := TimeParse("2006-01-02 15:04:05", src) | |||||
return t | |||||
} | |||||
func TimeStdParseUnix(src string) int64 { | |||||
t, err := TimeParse("2006-01-02 15:04:05", src) | |||||
if err != nil { | |||||
return 0 | |||||
} | |||||
return t.Unix() | |||||
} | |||||
// 获取一个当前时间 时间间隔 时间戳 | |||||
func GetTimeInterval(unit string, amount int) (startTime, endTime int64) { | |||||
t := time.Now() | |||||
nowTime := t.Unix() | |||||
tmpTime := int64(0) | |||||
switch unit { | |||||
case "years": | |||||
tmpTime = time.Date(t.Year()+amount, t.Month(), t.Day(), t.Hour(), 0, 0, 0, t.Location()).Unix() | |||||
case "months": | |||||
tmpTime = time.Date(t.Year(), t.Month()+time.Month(amount), t.Day(), t.Hour(), 0, 0, 0, t.Location()).Unix() | |||||
case "days": | |||||
tmpTime = time.Date(t.Year(), t.Month(), t.Day()+amount, t.Hour(), 0, 0, 0, t.Location()).Unix() | |||||
case "hours": | |||||
tmpTime = time.Date(t.Year(), t.Month(), t.Day(), t.Hour()+amount, 0, 0, 0, t.Location()).Unix() | |||||
} | |||||
if amount > 0 { | |||||
startTime = nowTime | |||||
endTime = tmpTime | |||||
} else { | |||||
startTime = tmpTime | |||||
endTime = nowTime | |||||
} | |||||
return | |||||
} | |||||
// 时分秒字符串转时间戳,传入示例:8:40 or 8:40:10 | |||||
func HmsToUnix(str string) (int64, error) { | |||||
t := time.Now() | |||||
arr := strings.Split(str, ":") | |||||
if len(arr) < 2 { | |||||
return 0, errors.New("Time format error") | |||||
} | |||||
h, _ := strconv.Atoi(arr[0]) | |||||
m, _ := strconv.Atoi(arr[1]) | |||||
s := 0 | |||||
if len(arr) == 3 { | |||||
s, _ = strconv.Atoi(arr[3]) | |||||
} | |||||
formatted1 := fmt.Sprintf("%d%02d%02d%02d%02d%02d", t.Year(), t.Month(), t.Day(), h, m, s) | |||||
res, err := time.ParseInLocation("20060102150405", formatted1, time.Local) | |||||
if err != nil { | |||||
return 0, err | |||||
} else { | |||||
return res.Unix(), nil | |||||
} | |||||
} | |||||
// 获取特定时间范围 | |||||
func GetTimeRange(s string) map[string]int64 { | |||||
t := time.Now() | |||||
var stime, etime time.Time | |||||
switch s { | |||||
case "today": | |||||
stime = time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, t.Location()) | |||||
// 明天 0点 | |||||
etime = time.Date(t.Year(), t.Month(), t.Day()+1, 0, 0, 0, 0, t.Location()) | |||||
case "this_month": | |||||
stime = time.Date(t.Year(), t.Month(), 0, 0, 0, 0, 0, t.Location()) | |||||
// 这个月的最后一天 | |||||
etime = time.Date(t.Year(), t.Month()+1, 0, 0, 0, 0, 0, t.Location()) | |||||
case "last_month": | |||||
stime = time.Date(t.Year(), t.Month()-1, 0, 0, 0, 0, 0, t.Location()) | |||||
// 上月的最后一天 | |||||
etime = time.Date(t.Year(), t.Month(), 0, 0, 0, 0, 0, t.Location()) | |||||
} | |||||
return map[string]int64{ | |||||
"start": stime.Unix(), | |||||
"end": etime.Unix(), | |||||
} | |||||
} | |||||
// GetFirstDateOfMonth 获取传入的时间所在月份的第一天,即某月第一天的0点。如传入time.Now(), 返回当前月份的第一天0点时间。 | |||||
func GetFirstDateOfMonth(d time.Time) time.Time { | |||||
d = d.AddDate(0, 0, -d.Day()+1) | |||||
return GetZeroTime(d) | |||||
} | |||||
// GetLastDateOfMonth 获取传入的时间所在月份的最后一天,即某月最后一天的0点。如传入time.Now(), 返回当前月份的最后一天0点时间。 | |||||
func GetLastDateOfMonth(d time.Time) time.Time { | |||||
return GetFirstDateOfMonth(d).AddDate(0, 1, -1) | |||||
} | |||||
// GetZeroTime 获取某一天的0点时间 | |||||
func GetZeroTime(d time.Time) time.Time { | |||||
return time.Date(d.Year(), d.Month(), d.Day(), 0, 0, 0, 0, d.Location()) | |||||
} | |||||
// GetZeroTime 获取某一天的0点时间的时间戳 | |||||
func GetZeroTimeUnix(d time.Time) int64 { | |||||
return time.Date(d.Year(), d.Month(), d.Day(), 0, 0, 0, 0, d.Location()).Unix() | |||||
} |
@@ -0,0 +1,61 @@ | |||||
package utils | |||||
import ( | |||||
"fmt" | |||||
"math/rand" | |||||
"time" | |||||
) | |||||
const ( | |||||
KC_RAND_KIND_NUM = 0 // 纯数字 | |||||
KC_RAND_KIND_LOWER = 1 // 小写字母 | |||||
KC_RAND_KIND_UPPER = 2 // 大写字母 | |||||
KC_RAND_KIND_ALL = 3 // 数字、大小写字母 | |||||
) | |||||
func newUUID() *[16]byte { | |||||
u := &[16]byte{} | |||||
rand.Read(u[:16]) | |||||
u[8] = (u[8] | 0x80) & 0xBf | |||||
u[6] = (u[6] | 0x40) & 0x4f | |||||
return u | |||||
} | |||||
func UUIDString() string { | |||||
u := newUUID() | |||||
return fmt.Sprintf("%x-%x-%x-%x-%x", u[:4], u[4:6], u[6:8], u[8:10], u[10:]) | |||||
} | |||||
func UUIDHexString() string { | |||||
u := newUUID() | |||||
return fmt.Sprintf("%x%x%x%x%x", u[:4], u[4:6], u[6:8], u[8:10], u[10:]) | |||||
} | |||||
func UUIDBinString() string { | |||||
u := newUUID() | |||||
return fmt.Sprintf("%s", [16]byte(*u)) | |||||
} | |||||
func Krand(size int, kind int) []byte { | |||||
ikind, kinds, result := kind, [][]int{[]int{10, 48}, []int{26, 97}, []int{26, 65}}, make([]byte, size) | |||||
isAll := kind > 2 || kind < 0 | |||||
rand.Seed(time.Now().UnixNano()) | |||||
for i := 0; i < size; i++ { | |||||
if isAll { // random ikind | |||||
ikind = rand.Intn(3) | |||||
} | |||||
scope, base := kinds[ikind][0], kinds[ikind][1] | |||||
result[i] = uint8(base + rand.Intn(scope)) | |||||
} | |||||
return result | |||||
} | |||||
// OrderUUID is only num for uuid | |||||
func OrderUUID(uid int) string { | |||||
ustr := IntToStr(uid) | |||||
tstr := Int64ToStr(time.Now().Unix()) | |||||
ulen := len(ustr) | |||||
tlen := len(tstr) | |||||
rlen := 18 - ulen - tlen | |||||
krb := Krand(rlen, KC_RAND_KIND_NUM) | |||||
return ustr + tstr + string(krb) | |||||
} |
@@ -0,0 +1,55 @@ | |||||
package utils | |||||
import ( | |||||
"fmt" | |||||
"github.com/gin-gonic/gin/binding" | |||||
"github.com/go-playground/locales/en" | |||||
"github.com/go-playground/locales/zh" | |||||
ut "github.com/go-playground/universal-translator" | |||||
"github.com/go-playground/validator/v10" | |||||
enTranslations "github.com/go-playground/validator/v10/translations/en" | |||||
chTranslations "github.com/go-playground/validator/v10/translations/zh" | |||||
"reflect" | |||||
) | |||||
var ValidatorTrans ut.Translator | |||||
// ValidatorTransInit 验证器错误信息翻译初始化 | |||||
// local 通常取决于 http 请求头的 'Accept-Language' | |||||
func ValidatorTransInit(local string) (err error) { | |||||
if v, ok := binding.Validator.Engine().(*validator.Validate); ok { | |||||
zhT := zh.New() //chinese | |||||
enT := en.New() //english | |||||
uni := ut.New(enT, zhT, enT) | |||||
var o bool | |||||
ValidatorTrans, o = uni.GetTranslator(local) | |||||
if !o { | |||||
return fmt.Errorf("uni.GetTranslator(%s) failed", local) | |||||
} | |||||
// 注册一个方法,从自定义标签label中获取值(用在把字段名映射为中文) | |||||
v.RegisterTagNameFunc(func(field reflect.StructField) string { | |||||
label := field.Tag.Get("label") | |||||
if label == "" { | |||||
return field.Name | |||||
} | |||||
return label | |||||
}) | |||||
// 注册翻译器 | |||||
switch local { | |||||
case "en": | |||||
err = enTranslations.RegisterDefaultTranslations(v, ValidatorTrans) | |||||
case "zh": | |||||
err = chTranslations.RegisterDefaultTranslations(v, ValidatorTrans) | |||||
default: | |||||
err = enTranslations.RegisterDefaultTranslations(v, ValidatorTrans) | |||||
} | |||||
return | |||||
} | |||||
return | |||||
} | |||||
// ValidatorTransInitZh 验证器错误信息翻译为中文初始化 | |||||
func ValidatorTransInitZh() (err error) { | |||||
return ValidatorTransInit("zh") | |||||
} |