diff --git a/db/db_block_star_chain.go b/db/db_block_star_chain.go new file mode 100644 index 0000000..a424a13 --- /dev/null +++ b/db/db_block_star_chain.go @@ -0,0 +1,153 @@ +package db + +import ( + "code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/db/model" + zhios_order_relate_utils "code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/utils" + zhios_order_relate_logx "code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/utils/logx" + "errors" + "fmt" + "reflect" + "xorm.io/xorm" +) + +// BatchSelectBlockStarChains 批量查询数据 TODO::和下面的方法重复了,建议采用下面的 `BlockStarChainFindByParams` 方法 +func BatchSelectBlockStarChains(Db *xorm.Engine, params map[string]interface{}) (*[]model.BlockStarChain, error) { + var BlockStarChainData []model.BlockStarChain + if err := Db.In(zhios_order_relate_utils.AnyToString(params["key"]), params["value"]). + Find(&BlockStarChainData); err != nil { + return nil, zhios_order_relate_logx.Warn(err) + } + return &BlockStarChainData, nil +} + +// BlockStarChainInsert 插入单条数据 +func BlockStarChainInsert(Db *xorm.Engine, BlockStarChain *model.BlockStarChain) (int, error) { + _, err := Db.InsertOne(BlockStarChain) + if err != nil { + return 0, err + } + return BlockStarChain.Id, nil +} + +// BatchAddBlockStarChains 批量新增数据 +func BatchAddBlockStarChains(Db *xorm.Engine, BlockStarChainData []*model.BlockStarChain) (int64, error) { + affected, err := Db.Insert(BlockStarChainData) + if err != nil { + return 0, err + } + return affected, nil +} + +func GetBlockStarChainCount(Db *xorm.Engine) int { + var BlockStarChain model.BlockStarChain + session := Db.Where("") + count, err := session.Count(&BlockStarChain) + if err != nil { + return 0 + } + return int(count) +} + +// BlockStarChainDelete 删除记录 +func BlockStarChainDelete(Db *xorm.Engine, id interface{}) (int64, error) { + if reflect.TypeOf(id).Kind() == reflect.Slice { + return Db.In("id", id).Delete(model.BlockStarChain{}) + } else { + return Db.Where("id = ?", id).Delete(model.BlockStarChain{}) + } +} + +// BlockStarChainUpdate 更新记录 +func BlockStarChainUpdate(session *xorm.Session, id interface{}, BlockStarChain *model.BlockStarChain, forceColums ...string) (int64, error) { + var ( + affected int64 + err error + ) + if forceColums != nil { + affected, err = session.Where("id=?", id).Cols(forceColums...).Update(BlockStarChain) + } else { + affected, err = session.Where("id=?", id).Update(BlockStarChain) + } + if err != nil { + return 0, err + } + return affected, nil +} + +// BlockStarChainGetOneByParams 通过传入的参数查询数据(单条) +func BlockStarChainGetOneByParams(session *xorm.Session, params map[string]interface{}) (*model.BlockStarChain, error) { + var m model.BlockStarChain + var query = fmt.Sprintf("%s =?", params["key"]) + if has, err := session.Where(query, params["value"]).Get(&m); err != nil || has == false { + return nil, zhios_order_relate_logx.Error(err) + } + return &m, nil +} + +// BlockStarChainFindByParams 通过传入的参数查询数据(多条) +func BlockStarChainFindByParams(Db *xorm.Engine, params map[string]interface{}) (*[]model.BlockStarChain, error) { + var m []model.BlockStarChain + if params["value"] == nil { + return nil, errors.New("参数有误") + } + if params["key"] == nil { + //查询全部数据 + err := Db.Find(&m) + if err != nil { + return nil, zhios_order_relate_logx.Error(err) + } + return &m, nil + } else { + if reflect.TypeOf(params["value"]).Kind() == reflect.Slice { + //指定In查询 + if err := Db.In(zhios_order_relate_utils.AnyToString(params["key"]), params["value"]).Find(&m); err != nil { + return nil, zhios_order_relate_logx.Warn(err) + } + return &m, nil + } else { + var query = fmt.Sprintf("%s =?", params["key"]) + err := Db.Where(query, params["value"]).Find(&m) + if err != nil { + return nil, zhios_order_relate_logx.Error(err) + } + return &m, nil + } + + } +} + +func BlockStarChainFindByParamsByPage(Db *xorm.Engine, params map[string]interface{}, page, pageSize int) (*[]model.BlockStarChain, error) { + var m []model.BlockStarChain + if params["value"] == nil { + return nil, errors.New("参数有误") + } + if page == 0 && pageSize == 0 { + page = 1 + pageSize = 10 + } + + if params["key"] == nil { + //查询全部数据 + err := Db.Limit(pageSize, (page-1)*pageSize).Find(&m) + if err != nil { + return nil, zhios_order_relate_logx.Error(err) + } + return &m, nil + } else { + if reflect.TypeOf(params["value"]).Kind() == reflect.Slice { + //指定In查询 + if err := Db.In(zhios_order_relate_utils.AnyToString(params["key"]), params["value"]).Limit(pageSize, (page-1)*pageSize).Find(&m); err != nil { + return nil, zhios_order_relate_logx.Warn(err) + } + return &m, nil + } else { + var query = fmt.Sprintf("%s =?", params["key"]) + err := Db.Where(query, params["value"]).Limit(pageSize, (page-1)*pageSize).Find(&m) + if err != nil { + return nil, zhios_order_relate_logx.Error(err) + } + return &m, nil + } + + } +} diff --git a/db/db_block_star_chain_flow.go b/db/db_block_star_chain_flow.go new file mode 100644 index 0000000..b2d5e7f --- /dev/null +++ b/db/db_block_star_chain_flow.go @@ -0,0 +1,153 @@ +package db + +import ( + "code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/db/model" + zhios_order_relate_utils "code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/utils" + zhios_order_relate_logx "code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/utils/logx" + "errors" + "fmt" + "reflect" + "xorm.io/xorm" +) + +// BatchSelectBlockStarChainFlows 批量查询数据 TODO::和下面的方法重复了,建议采用下面的 `BlockStarChainFlowFindByParams` 方法 +func BatchSelectBlockStarChainFlows(Db *xorm.Engine, params map[string]interface{}) (*[]model.BlockStarChainFlow, error) { + var BlockStarChainFlowData []model.BlockStarChainFlow + if err := Db.In(zhios_order_relate_utils.AnyToString(params["key"]), params["value"]). + Find(&BlockStarChainFlowData); err != nil { + return nil, zhios_order_relate_logx.Warn(err) + } + return &BlockStarChainFlowData, nil +} + +// BlockStarChainFlowInsert 插入单条数据 +func BlockStarChainFlowInsert(session *xorm.Session, BlockStarChainFlow *model.BlockStarChainFlow) (int64, error) { + _, err := session.InsertOne(BlockStarChainFlow) + if err != nil { + return 0, err + } + return BlockStarChainFlow.Id, nil +} + +// BatchAddBlockStarChainFlows 批量新增数据 +func BatchAddBlockStarChainFlows(Db *xorm.Engine, BlockStarChainFlowData []*model.BlockStarChainFlow) (int64, error) { + affected, err := Db.Insert(BlockStarChainFlowData) + if err != nil { + return 0, err + } + return affected, nil +} + +func GetBlockStarChainFlowCount(Db *xorm.Engine) int { + var BlockStarChainFlow model.BlockStarChainFlow + session := Db.Where("") + count, err := session.Count(&BlockStarChainFlow) + if err != nil { + return 0 + } + return int(count) +} + +// BlockStarChainFlowDelete 删除记录 +func BlockStarChainFlowDelete(Db *xorm.Engine, id interface{}) (int64, error) { + if reflect.TypeOf(id).Kind() == reflect.Slice { + return Db.In("id", id).Delete(model.BlockStarChainFlow{}) + } else { + return Db.Where("id = ?", id).Delete(model.BlockStarChainFlow{}) + } +} + +// BlockStarChainFlowUpdate 更新记录 +func BlockStarChainFlowUpdate(session *xorm.Session, id interface{}, BlockStarChainFlow *model.BlockStarChainFlow, forceColums ...string) (int64, error) { + var ( + affected int64 + err error + ) + if forceColums != nil { + affected, err = session.Where("id=?", id).Cols(forceColums...).Update(BlockStarChainFlow) + } else { + affected, err = session.Where("id=?", id).Update(BlockStarChainFlow) + } + if err != nil { + return 0, err + } + return affected, nil +} + +// BlockStarChainFlowGetOneByParams 通过传入的参数查询数据(单条) +func BlockStarChainFlowGetOneByParams(Db *xorm.Engine, params map[string]interface{}) (*model.BlockStarChainFlow, error) { + var m model.BlockStarChainFlow + var query = fmt.Sprintf("%s =?", params["key"]) + if has, err := Db.Where(query, params["value"]).Get(&m); err != nil || has == false { + return nil, zhios_order_relate_logx.Error(err) + } + return &m, nil +} + +// BlockStarChainFlowFindByParams 通过传入的参数查询数据(多条) +func BlockStarChainFlowFindByParams(Db *xorm.Engine, params map[string]interface{}) (*[]model.BlockStarChainFlow, error) { + var m []model.BlockStarChainFlow + if params["value"] == nil { + return nil, errors.New("参数有误") + } + if params["key"] == nil { + //查询全部数据 + err := Db.Find(&m) + if err != nil { + return nil, zhios_order_relate_logx.Error(err) + } + return &m, nil + } else { + if reflect.TypeOf(params["value"]).Kind() == reflect.Slice { + //指定In查询 + if err := Db.In(zhios_order_relate_utils.AnyToString(params["key"]), params["value"]).Find(&m); err != nil { + return nil, zhios_order_relate_logx.Warn(err) + } + return &m, nil + } else { + var query = fmt.Sprintf("%s =?", params["key"]) + err := Db.Where(query, params["value"]).Find(&m) + if err != nil { + return nil, zhios_order_relate_logx.Error(err) + } + return &m, nil + } + + } +} + +func BlockStarChainFlowFindByParamsByPage(Db *xorm.Engine, params map[string]interface{}, page, pageSize int) (*[]model.BlockStarChainFlow, error) { + var m []model.BlockStarChainFlow + if params["value"] == nil { + return nil, errors.New("参数有误") + } + if page == 0 && pageSize == 0 { + page = 1 + pageSize = 10 + } + + if params["key"] == nil { + //查询全部数据 + err := Db.Limit(pageSize, (page-1)*pageSize).Find(&m) + if err != nil { + return nil, zhios_order_relate_logx.Error(err) + } + return &m, nil + } else { + if reflect.TypeOf(params["value"]).Kind() == reflect.Slice { + //指定In查询 + if err := Db.In(zhios_order_relate_utils.AnyToString(params["key"]), params["value"]).Limit(pageSize, (page-1)*pageSize).Find(&m); err != nil { + return nil, zhios_order_relate_logx.Warn(err) + } + return &m, nil + } else { + var query = fmt.Sprintf("%s =?", params["key"]) + err := Db.Where(query, params["value"]).Limit(pageSize, (page-1)*pageSize).Find(&m) + if err != nil { + return nil, zhios_order_relate_logx.Error(err) + } + return &m, nil + } + + } +} diff --git a/db/db_block_star_chain_settlement_records.go b/db/db_block_star_chain_settlement_records.go new file mode 100644 index 0000000..2267aca --- /dev/null +++ b/db/db_block_star_chain_settlement_records.go @@ -0,0 +1,153 @@ +package db + +import ( + "code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/db/model" + zhios_order_relate_utils "code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/utils" + zhios_order_relate_logx "code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/utils/logx" + "errors" + "fmt" + "reflect" + "xorm.io/xorm" +) + +// BatchSelectBlockStarChainSettlementRecordss 批量查询数据 TODO::和下面的方法重复了,建议采用下面的 `BlockStarChainSettlementRecordsFindByParams` 方法 +func BatchSelectBlockStarChainSettlementRecordss(Db *xorm.Engine, params map[string]interface{}) (*[]model.BlockStarChainSettlementRecords, error) { + var BlockStarChainSettlementRecordsData []model.BlockStarChainSettlementRecords + if err := Db.In(zhios_order_relate_utils.AnyToString(params["key"]), params["value"]). + Find(&BlockStarChainSettlementRecordsData); err != nil { + return nil, zhios_order_relate_logx.Warn(err) + } + return &BlockStarChainSettlementRecordsData, nil +} + +// BlockStarChainSettlementRecordsInsert 插入单条数据 +func BlockStarChainSettlementRecordsInsert(session *xorm.Session, BlockStarChainSettlementRecords *model.BlockStarChainSettlementRecords) (int64, error) { + _, err := session.InsertOne(BlockStarChainSettlementRecords) + if err != nil { + return 0, err + } + return BlockStarChainSettlementRecords.Id, nil +} + +// BatchAddBlockStarChainSettlementRecordss 批量新增数据 +func BatchAddBlockStarChainSettlementRecordss(Db *xorm.Engine, BlockStarChainSettlementRecordsData []*model.BlockStarChainSettlementRecords) (int64, error) { + affected, err := Db.Insert(BlockStarChainSettlementRecordsData) + if err != nil { + return 0, err + } + return affected, nil +} + +func GetBlockStarChainSettlementRecordsCount(Db *xorm.Engine) int { + var BlockStarChainSettlementRecords model.BlockStarChainSettlementRecords + session := Db.Where("") + count, err := session.Count(&BlockStarChainSettlementRecords) + if err != nil { + return 0 + } + return int(count) +} + +// BlockStarChainSettlementRecordsDelete 删除记录 +func BlockStarChainSettlementRecordsDelete(Db *xorm.Engine, id interface{}) (int64, error) { + if reflect.TypeOf(id).Kind() == reflect.Slice { + return Db.In("id", id).Delete(model.BlockStarChainSettlementRecords{}) + } else { + return Db.Where("id = ?", id).Delete(model.BlockStarChainSettlementRecords{}) + } +} + +// BlockStarChainSettlementRecordsUpdate 更新记录 +func BlockStarChainSettlementRecordsUpdate(session *xorm.Session, id interface{}, BlockStarChainSettlementRecords *model.BlockStarChainSettlementRecords, forceColums ...string) (int64, error) { + var ( + affected int64 + err error + ) + if forceColums != nil { + affected, err = session.Where("id=?", id).Cols(forceColums...).Update(BlockStarChainSettlementRecords) + } else { + affected, err = session.Where("id=?", id).Update(BlockStarChainSettlementRecords) + } + if err != nil { + return 0, err + } + return affected, nil +} + +// BlockStarChainSettlementRecordsGetOneByParams 通过传入的参数查询数据(单条) +func BlockStarChainSettlementRecordsGetOneByParams(Db *xorm.Engine, params map[string]interface{}) (*model.BlockStarChainSettlementRecords, error) { + var m model.BlockStarChainSettlementRecords + var query = fmt.Sprintf("%s =?", params["key"]) + if has, err := Db.Where(query, params["value"]).Get(&m); err != nil || has == false { + return nil, zhios_order_relate_logx.Error(err) + } + return &m, nil +} + +// BlockStarChainSettlementRecordsFindByParams 通过传入的参数查询数据(多条) +func BlockStarChainSettlementRecordsFindByParams(Db *xorm.Engine, params map[string]interface{}) (*[]model.BlockStarChainSettlementRecords, error) { + var m []model.BlockStarChainSettlementRecords + if params["value"] == nil { + return nil, errors.New("参数有误") + } + if params["key"] == nil { + //查询全部数据 + err := Db.Find(&m) + if err != nil { + return nil, zhios_order_relate_logx.Error(err) + } + return &m, nil + } else { + if reflect.TypeOf(params["value"]).Kind() == reflect.Slice { + //指定In查询 + if err := Db.In(zhios_order_relate_utils.AnyToString(params["key"]), params["value"]).Find(&m); err != nil { + return nil, zhios_order_relate_logx.Warn(err) + } + return &m, nil + } else { + var query = fmt.Sprintf("%s =?", params["key"]) + err := Db.Where(query, params["value"]).Find(&m) + if err != nil { + return nil, zhios_order_relate_logx.Error(err) + } + return &m, nil + } + + } +} + +func BlockStarChainSettlementRecordsFindByParamsByPage(Db *xorm.Engine, params map[string]interface{}, page, pageSize int) (*[]model.BlockStarChainSettlementRecords, error) { + var m []model.BlockStarChainSettlementRecords + if params["value"] == nil { + return nil, errors.New("参数有误") + } + if page == 0 && pageSize == 0 { + page = 1 + pageSize = 10 + } + + if params["key"] == nil { + //查询全部数据 + err := Db.Limit(pageSize, (page-1)*pageSize).Find(&m) + if err != nil { + return nil, zhios_order_relate_logx.Error(err) + } + return &m, nil + } else { + if reflect.TypeOf(params["value"]).Kind() == reflect.Slice { + //指定In查询 + if err := Db.In(zhios_order_relate_utils.AnyToString(params["key"]), params["value"]).Limit(pageSize, (page-1)*pageSize).Find(&m); err != nil { + return nil, zhios_order_relate_logx.Warn(err) + } + return &m, nil + } else { + var query = fmt.Sprintf("%s =?", params["key"]) + err := Db.Where(query, params["value"]).Limit(pageSize, (page-1)*pageSize).Find(&m) + if err != nil { + return nil, zhios_order_relate_logx.Error(err) + } + return &m, nil + } + + } +} diff --git a/db/db_user_virtual_amount.go b/db/db_user_virtual_amount.go new file mode 100644 index 0000000..05c1893 --- /dev/null +++ b/db/db_user_virtual_amount.go @@ -0,0 +1,173 @@ +package db + +import ( + "code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/db/model" + zhios_order_relate_utils "code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/utils" + zhios_order_relate_logx "code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/utils/logx" + "errors" + "fmt" + "reflect" + "xorm.io/xorm" +) + +// BatchSelectUserVirtualAmounts 批量查询数据 TODO::和下面的方法重复了,建议采用下面的 `UserVirtualAmountFindByParams` 方法 +func BatchSelectUserVirtualAmounts(Db *xorm.Engine, params map[string]interface{}) (*[]model.UserVirtualAmount, error) { + var UserVirtualAmountData []model.UserVirtualAmount + if err := Db.In(zhios_order_relate_utils.AnyToString(params["key"]), params["value"]). + Find(&UserVirtualAmountData); err != nil { + return nil, zhios_order_relate_logx.Warn(err) + } + return &UserVirtualAmountData, nil +} + +// UserVirtualAmountInsert 插入单条数据 +func UserVirtualAmountInsert(Db *xorm.Engine, UserVirtualAmount *model.UserVirtualAmount) (int64, error) { + _, err := Db.InsertOne(UserVirtualAmount) + if err != nil { + return 0, err + } + return UserVirtualAmount.Id, nil +} + +// BatchAddUserVirtualAmounts 批量新增数据 +func BatchAddUserVirtualAmounts(Db *xorm.Engine, UserVirtualAmountData []*model.UserVirtualAmount) (int64, error) { + affected, err := Db.Insert(UserVirtualAmountData) + if err != nil { + return 0, err + } + return affected, nil +} + +func GetUserVirtualAmountCount(Db *xorm.Engine) int { + var UserVirtualAmount model.UserVirtualAmount + session := Db.Where("") + count, err := session.Count(&UserVirtualAmount) + if err != nil { + return 0 + } + return int(count) +} + +// UserVirtualAmountDelete 删除记录 +func UserVirtualAmountDelete(Db *xorm.Engine, id interface{}) (int64, error) { + if reflect.TypeOf(id).Kind() == reflect.Slice { + return Db.In("id", id).Delete(model.UserVirtualAmount{}) + } else { + return Db.Where("id = ?", id).Delete(model.UserVirtualAmount{}) + } +} + +// UserVirtualAmountUpdate 更新记录 +func UserVirtualAmountUpdate(session *xorm.Session, id interface{}, UserVirtualAmount *model.UserVirtualAmount, forceColums ...string) (int64, error) { + var ( + affected int64 + err error + ) + if forceColums != nil { + affected, err = session.Where("id=?", id).Cols(forceColums...).Update(UserVirtualAmount) + } else { + affected, err = session.Where("id=?", id).Update(UserVirtualAmount) + } + if err != nil { + return 0, err + } + return affected, nil +} + +func GetUserVirtualWalletWithSession(session *xorm.Session, uid, coinId int) (*model.UserVirtualAmount, error) { + var UserVirtualWallet model.UserVirtualAmount + get, err := session.Where("uid = ? AND coin_id = ?", uid, coinId).Get(&UserVirtualWallet) + if err != nil { + return nil, err + } + if get { + return &UserVirtualWallet, nil + } else { + UserVirtualWallet.Amount = "0" + UserVirtualWallet.CoinId = coinId + UserVirtualWallet.Uid = uid + one, err := session.InsertOne(&UserVirtualWallet) + if err != nil || one == 0 { + return nil, err + } + } + return nil, errors.New("获取用户虚拟币钱包失败") +} + +// UserVirtualAmountGetOneByParams 通过传入的参数查询数据(单条) +func UserVirtualAmountGetOneByParams(Db *xorm.Engine, params map[string]interface{}) (*model.UserVirtualAmount, error) { + var m model.UserVirtualAmount + var query = fmt.Sprintf("%s =?", params["key"]) + if has, err := Db.Where(query, params["value"]).Get(&m); err != nil || has == false { + return nil, zhios_order_relate_logx.Error(err) + } + return &m, nil +} + +// UserVirtualAmountFindByParams 通过传入的参数查询数据(多条) +func UserVirtualAmountFindByParams(Db *xorm.Engine, params map[string]interface{}) (*[]model.UserVirtualAmount, error) { + var m []model.UserVirtualAmount + if params["value"] == nil { + return nil, errors.New("参数有误") + } + if params["key"] == nil { + //查询全部数据 + err := Db.Find(&m) + if err != nil { + return nil, zhios_order_relate_logx.Error(err) + } + return &m, nil + } else { + if reflect.TypeOf(params["value"]).Kind() == reflect.Slice { + //指定In查询 + if err := Db.In(zhios_order_relate_utils.AnyToString(params["key"]), params["value"]).Find(&m); err != nil { + return nil, zhios_order_relate_logx.Warn(err) + } + return &m, nil + } else { + var query = fmt.Sprintf("%s =?", params["key"]) + err := Db.Where(query, params["value"]).Find(&m) + if err != nil { + return nil, zhios_order_relate_logx.Error(err) + } + return &m, nil + } + + } +} + +func UserVirtualAmountFindByParamsByPage(Db *xorm.Engine, params map[string]interface{}, page, pageSize int) (*[]model.UserVirtualAmount, error) { + var m []model.UserVirtualAmount + if params["value"] == nil { + return nil, errors.New("参数有误") + } + if page == 0 && pageSize == 0 { + page = 1 + pageSize = 10 + } + + if params["key"] == nil { + //查询全部数据 + err := Db.Limit(pageSize, (page-1)*pageSize).Find(&m) + if err != nil { + return nil, zhios_order_relate_logx.Error(err) + } + return &m, nil + } else { + if reflect.TypeOf(params["value"]).Kind() == reflect.Slice { + //指定In查询 + if err := Db.In(zhios_order_relate_utils.AnyToString(params["key"]), params["value"]).Limit(pageSize, (page-1)*pageSize).Find(&m); err != nil { + return nil, zhios_order_relate_logx.Warn(err) + } + return &m, nil + } else { + var query = fmt.Sprintf("%s =?", params["key"]) + err := Db.Where(query, params["value"]).Limit(pageSize, (page-1)*pageSize).Find(&m) + if err != nil { + return nil, zhios_order_relate_logx.Error(err) + } + return &m, nil + } + + } +} diff --git a/db/db_user_virtual_coin_flow.go b/db/db_user_virtual_coin_flow.go new file mode 100644 index 0000000..3714309 --- /dev/null +++ b/db/db_user_virtual_coin_flow.go @@ -0,0 +1,153 @@ +package db + +import ( + "code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/db/model" + zhios_order_relate_utils "code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/utils" + zhios_order_relate_logx "code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/utils/logx" + "errors" + "fmt" + "reflect" + "xorm.io/xorm" +) + +// BatchSelectUserVirtualCoinFlows 批量查询数据 TODO::和下面的方法重复了,建议采用下面的 `UserVirtualCoinFlowFindByParams` 方法 +func BatchSelectUserVirtualCoinFlows(Db *xorm.Engine, params map[string]interface{}) (*[]model.UserVirtualCoinFlow, error) { + var UserVirtualCoinFlowData []model.UserVirtualCoinFlow + if err := Db.In(zhios_order_relate_utils.AnyToString(params["key"]), params["value"]). + Find(&UserVirtualCoinFlowData); err != nil { + return nil, zhios_order_relate_logx.Warn(err) + } + return &UserVirtualCoinFlowData, nil +} + +// UserVirtualCoinFlowInsert 插入单条数据 +func UserVirtualCoinFlowInsert(session *xorm.Session, UserVirtualCoinFlow *model.UserVirtualCoinFlow) (int64, error) { + _, err := session.InsertOne(UserVirtualCoinFlow) + if err != nil { + return 0, err + } + return UserVirtualCoinFlow.Id, nil +} + +// BatchAddUserVirtualCoinFlows 批量新增数据 +func BatchAddUserVirtualCoinFlows(Db *xorm.Engine, UserVirtualCoinFlowData []*model.UserVirtualCoinFlow) (int64, error) { + affected, err := Db.Insert(UserVirtualCoinFlowData) + if err != nil { + return 0, err + } + return affected, nil +} + +func GetUserVirtualCoinFlowCount(Db *xorm.Engine) int { + var UserVirtualCoinFlow model.UserVirtualCoinFlow + session := Db.Where("") + count, err := session.Count(&UserVirtualCoinFlow) + if err != nil { + return 0 + } + return int(count) +} + +// UserVirtualCoinFlowDelete 删除记录 +func UserVirtualCoinFlowDelete(Db *xorm.Engine, id interface{}) (int64, error) { + if reflect.TypeOf(id).Kind() == reflect.Slice { + return Db.In("id", id).Delete(model.UserVirtualCoinFlow{}) + } else { + return Db.Where("id = ?", id).Delete(model.UserVirtualCoinFlow{}) + } +} + +// UserVirtualCoinFlowUpdate 更新记录 +func UserVirtualCoinFlowUpdate(session *xorm.Session, id interface{}, UserVirtualCoinFlow *model.UserVirtualCoinFlow, forceColums ...string) (int64, error) { + var ( + affected int64 + err error + ) + if forceColums != nil { + affected, err = session.Where("id=?", id).Cols(forceColums...).Update(UserVirtualCoinFlow) + } else { + affected, err = session.Where("id=?", id).Update(UserVirtualCoinFlow) + } + if err != nil { + return 0, err + } + return affected, nil +} + +// UserVirtualCoinFlowGetOneByParams 通过传入的参数查询数据(单条) +func UserVirtualCoinFlowGetOneByParams(Db *xorm.Engine, params map[string]interface{}) (*model.UserVirtualCoinFlow, error) { + var m model.UserVirtualCoinFlow + var query = fmt.Sprintf("%s =?", params["key"]) + if has, err := Db.Where(query, params["value"]).Get(&m); err != nil || has == false { + return nil, zhios_order_relate_logx.Error(err) + } + return &m, nil +} + +// UserVirtualCoinFlowFindByParams 通过传入的参数查询数据(多条) +func UserVirtualCoinFlowFindByParams(Db *xorm.Engine, params map[string]interface{}) (*[]model.UserVirtualCoinFlow, error) { + var m []model.UserVirtualCoinFlow + if params["value"] == nil { + return nil, errors.New("参数有误") + } + if params["key"] == nil { + //查询全部数据 + err := Db.Find(&m) + if err != nil { + return nil, zhios_order_relate_logx.Error(err) + } + return &m, nil + } else { + if reflect.TypeOf(params["value"]).Kind() == reflect.Slice { + //指定In查询 + if err := Db.In(zhios_order_relate_utils.AnyToString(params["key"]), params["value"]).Find(&m); err != nil { + return nil, zhios_order_relate_logx.Warn(err) + } + return &m, nil + } else { + var query = fmt.Sprintf("%s =?", params["key"]) + err := Db.Where(query, params["value"]).Find(&m) + if err != nil { + return nil, zhios_order_relate_logx.Error(err) + } + return &m, nil + } + + } +} + +func UserVirtualCoinFlowFindByParamsByPage(Db *xorm.Engine, params map[string]interface{}, page, pageSize int) (*[]model.UserVirtualCoinFlow, error) { + var m []model.UserVirtualCoinFlow + if params["value"] == nil { + return nil, errors.New("参数有误") + } + if page == 0 && pageSize == 0 { + page = 1 + pageSize = 10 + } + + if params["key"] == nil { + //查询全部数据 + err := Db.Limit(pageSize, (page-1)*pageSize).Find(&m) + if err != nil { + return nil, zhios_order_relate_logx.Error(err) + } + return &m, nil + } else { + if reflect.TypeOf(params["value"]).Kind() == reflect.Slice { + //指定In查询 + if err := Db.In(zhios_order_relate_utils.AnyToString(params["key"]), params["value"]).Limit(pageSize, (page-1)*pageSize).Find(&m); err != nil { + return nil, zhios_order_relate_logx.Warn(err) + } + return &m, nil + } else { + var query = fmt.Sprintf("%s =?", params["key"]) + err := Db.Where(query, params["value"]).Limit(pageSize, (page-1)*pageSize).Find(&m) + if err != nil { + return nil, zhios_order_relate_logx.Error(err) + } + return &m, nil + } + + } +} diff --git a/db/model/block_star_chain.go b/db/model/block_star_chain.go new file mode 100644 index 0000000..c98f9b8 --- /dev/null +++ b/db/model/block_star_chain.go @@ -0,0 +1,33 @@ +package model + +import ( + "time" +) + +type BlockStarChain struct { + Id int `json:"id" xorm:"not null pk autoincr comment('主键id') INT(11)"` + IsUse int `json:"is_use" xorm:"not null default 0 comment('是否开启(否:0;是:1)') TINYINT(1)"` + Coin1 int `json:"coin_1" xorm:"not null default 0 comment('coinId_1(类似于区块币)') INT(11)"` + Coin2 int `json:"coin_2" xorm:"not null default 0 comment('coinId_2(类似于静态贡献值)') INT(11)"` + Coin3 int `json:"coin_3" xorm:"not null default 0 comment('coinId_3(类似于动态贡献值)') INT(11)"` + InitialEverydayPublishCoin string `json:"initial_everyday_publish_coin" xorm:"not null default 0.0000000000 comment('初始每日区块币发行数量') DECIMAL(28,10)"` + NowEverydayPublishCoin string `json:"now_everyday_publish_coin" xorm:"not null default 0.0000000000 comment('当前每日区块币发行数量') DECIMAL(28,10)"` + TodayPublishCoin string `json:"today_publish_coin" xorm:"not null default 0.0000000000 comment('今日区块币发行数量(若为0,则按当前每日区块币发行数量)') DECIMAL(28,10)"` + EveryThirtyDaysReduceRate string `json:"every_thirty_days_reduce_rate" xorm:"not null default 5.00 comment('每三十日递减发行百分比') DECIMAL(5,2)"` + TotalNowCoin string `json:"total_now_coin" xorm:"not null default 0.0000000000 comment('当前区块币总量') DECIMAL(28,10)"` + TotalPublishCoin string `json:"total_publish_coin" xorm:"not null default 0.0000000000 comment('累计区块币发行总量') DECIMAL(28,10)"` + TotalDestroyCoin string `json:"total_destroy_coin" xorm:"not null default 0.0000000000 comment('累计区块币销毁总量') DECIMAL(28,10)"` + TotalRemainderCoin string `json:"total_remainder_coin" xorm:"not null default 0.0000000000 comment('累计区块币剩余总量') DECIMAL(28,10)"` + InitialCoinTotal string `json:"initial_coin_total" xorm:"not null default 0.0000000000 comment('初始区块币总量') DECIMAL(28,10)"` + PlatformGuidePriceForCoin string `json:"platform_guide_price_for_coin" xorm:"not null default 0.0000000000 comment('平台区块币指导价') DECIMAL(22,10)"` + StartAt string `json:"start_at" xorm:"not null default '' comment('起始时间(0000-00)') CHAR(50)"` + PlatformBusinessDiscountRate string `json:"platform_business_discount_rate" xorm:"not null default 0.00 comment('平台商家让利百分比') DECIMAL(5,2)"` + ConsumeReturnContributionRate string `json:"consume_return_contribution_rate" xorm:"not null default 100.00 comment('消费返贡献值比例') DECIMAL(5,2)"` + PublishCoinDynamicRate string `json:"publish_coin_dynamic_rate" xorm:"not null default 45.00 comment('区块币发行动态占比') DECIMAL(5,2)"` + PublishCoinStaticRate string `json:"publish_coin_static_rate" xorm:"not null default 45.00 comment('区块币发行静态占比') DECIMAL(5,2)"` + PublishCoinOperationCenterRate string `json:"publish_coin_operation_center_rate" xorm:"not null default 3.00 comment('区块币发行运营中心占比') DECIMAL(5,2)"` + PublishCoinOperationOtherRate string `json:"publish_coin_other_rate" xorm:"not null default 7.00 comment('区块币发行其他占比') DECIMAL(5,2)"` + RewardSettings string `json:"reward_settings" xorm:"comment('奖励设置') TEXT"` + CreateAt time.Time `json:"create_at" xorm:"not null default 'CURRENT_TIMESTAMP' comment('创建时间') TIMESTAMP"` + UpdateAt time.Time `json:"update_at" xorm:"default 'CURRENT_TIMESTAMP' comment('更新时间') TIMESTAMP"` +} diff --git a/db/model/block_star_chain_flow.go b/db/model/block_star_chain_flow.go new file mode 100644 index 0000000..a86e1e8 --- /dev/null +++ b/db/model/block_star_chain_flow.go @@ -0,0 +1,23 @@ +package model + +import ( + "time" +) + +type BlockStarChainFlow struct { + Id int64 `json:"id" xorm:"pk autoincr BIGINT(20)"` + CoinId int `json:"coin_id" xorm:"not null comment('虚拟币id') INT(11)"` + Direction int `json:"direction" xorm:"not null default 1 comment('方向:1收入 2支出') TINYINT(255)"` + Kind int `json:"kind" xorm:"not null default 1 comment('种类(1:系统定时发放 2:系统销毁 3:静态未分配完销毁 4:动态未分配完销毁 4:运营中心未分配完销毁 5:运营中心未分配完销毁 6:打赏销毁 7:打赏未分配完销毁 7:自营拼团抽奖销毁 8:红包转余额销毁 9:转增手续费销毁)') TINYINT(1)"` + Title string `json:"title" xorm:"not null default '' comment('标题') VARCHAR(255)"` + Amount string `json:"amount" xorm:"not null comment('变更数量') DECIMAL(28,10)"` + BeforeTotalNowCoin string `json:"before_total_now_coin" xorm:"not null default 0.0000000000 comment('变更前-区块币总量') DECIMAL(28,10)"` + AfterTotalNowCoin string `json:"after_total_now_coin" xorm:"not null default 0.0000000000 comment('变更后-区块币总量') DECIMAL(28,10)"` + BeforeTotalRemainderCoin string `json:"before_total_remainder_coin" xorm:"not null default 0.0000000000 comment('变更前-累计区块币剩余总量') DECIMAL(28,10)"` + AfterTotalRemainderCoin string `json:"after_total_remainder_coin" xorm:"not null default 0.0000000000 comment('变更后-累计区块币剩余总量') DECIMAL(28,10)"` + BeforeTotalPublishCoin string `json:"before_total_publish_coin" xorm:"default 0.0000000000 comment('变更前-累计区块币发行总量') DECIMAL(28,10)"` + AfterTotalPublishCoin string `json:"after_total_publish_coin" xorm:"default 0.0000000000 comment('变更后-累计区块币发行总量') DECIMAL(28,10)"` + BeforeTotalDestroyCoin string `json:"before_total_destroy_coin" xorm:"default 0.0000000000 comment('变更前-累计区块币销毁总量') DECIMAL(28,10)"` + AfterTotalDestroyCoin string `json:"after_total_destroy_coin" xorm:"default 0.0000000000 comment('变更后-累计区块币销毁总量') DECIMAL(28,10)"` + CreateTime time.Time `json:"create_time" xorm:"default 'CURRENT_TIMESTAMP' comment('创建时间') DATETIME"` +} diff --git a/db/model/block_star_chain_settlement_records.go b/db/model/block_star_chain_settlement_records.go new file mode 100644 index 0000000..7c0b800 --- /dev/null +++ b/db/model/block_star_chain_settlement_records.go @@ -0,0 +1,23 @@ +package model + +import ( + "time" +) + +type BlockStarChainSettlementRecords struct { + Id int64 `json:"id" xorm:"pk autoincr BIGINT(20)"` + CoinId int `json:"coin_id" xorm:"not null comment('虚拟币id') INT(11)"` + TodayPublishCoin string `json:"today_publish_coin" xorm:"not null default 0.0000000000 comment('今日区块币发行总数量') DECIMAL(28,10)"` + TodayDestroyCoinForSystem string `json:"today_destroy_coin_for_system" xorm:"not null default 0.0000000000 comment('今日销毁区块币数量-系统') DECIMAL(28,10)"` + TodayPublishCoinForStatic string `json:"today_publish_coin_for_static" xorm:"not null default 0.0000000000 comment('今日区块币发行数量-静态区') DECIMAL(28,10)"` + TodayDestroyCoinForStatic string `json:"today_destroy_coin_for_static" xorm:"not null default 0.0000000000 comment('今日销毁区块币数量-静态区') DECIMAL(28,10)"` + TodayPublishCoinForDynamic string `json:"today_publish_coin_for_dynamic" xorm:"not null default 0.0000000000 comment('今日区块币发行数量-动态区') DECIMAL(28,10)"` + TodayDestroyCoinForDynamic string `json:"today_destroy_coin_for_dynamic" xorm:"not null default 0.0000000000 comment('今日销毁区块币数量-动态区') DECIMAL(28,10)"` + TodayPublishCoinForOperationCenter string `json:"today_publish_coin_for_operation_center" xorm:"not null default 0.0000000000 comment('今日区块币发行数量-运营中心') DECIMAL(28,10)"` + TodayDestroyCoinForOperationCenter string `json:"today_destroy_coin_for_operation_center" xorm:"not null default 0.0000000000 comment('今日销毁区块币数量-运营中心') DECIMAL(28,10)"` + TodayPublishCoinForOther string `json:"today_publish_coin_for_other" xorm:"not null default 0.0000000000 comment('今日区块币发行数量-其他') DECIMAL(28,10)"` + TodayDestroyCoinForOther string `json:"today_destroy_coin_for_other" xorm:"not null default 0.0000000000 comment('今日销毁区块币数量-其他') DECIMAL(28,10)"` + Date string `json:"date" xorm:"not null default '' comment('日期(0000-00)') VARCHAR(50)"` + CreateAt time.Time `json:"create_at" xorm:"not null default 'CURRENT_TIMESTAMP' comment('创建时间') DATETIME"` + UpdateAt time.Time `json:"update_at" xorm:"not null default 'CURRENT_TIMESTAMP' DATETIME"` +} diff --git a/db/model/user_virtual_amount.go b/db/model/user_virtual_amount.go new file mode 100644 index 0000000..a7e8710 --- /dev/null +++ b/db/model/user_virtual_amount.go @@ -0,0 +1,9 @@ +package model + +type UserVirtualAmount struct { + Id int64 `json:"id" xorm:"pk autoincr BIGINT(20)"` + Uid int `json:"uid" xorm:"index INT(11)"` + CoinId int `json:"coin_id" xorm:"INT(11)"` + Amount string `json:"amount" xorm:"DECIMAL(16,6)"` + FreezeAmount string `json:"freeze_amount" xorm:"DECIMAL(16,6)"` +} diff --git a/db/model/user_virtual_coin_flow.go b/db/model/user_virtual_coin_flow.go new file mode 100644 index 0000000..2d48b87 --- /dev/null +++ b/db/model/user_virtual_coin_flow.go @@ -0,0 +1,21 @@ +package model + +import ( + "time" +) + +type UserVirtualCoinFlow struct { + Id int64 `json:"id" xorm:"pk autoincr BIGINT(20)"` + Uid int `json:"uid" xorm:"not null comment('用户id') index INT(11)"` + CoinId int `json:"coin_id" xorm:"not null comment('虚拟币id') INT(11)"` + Direction int `json:"direction" xorm:"not null comment('方向:1收入 2支出') TINYINT(255)"` + Title string `json:"title" xorm:"comment('标题') VARCHAR(255)"` + OrdId string `json:"ord_id" xorm:"comment('相关的订单id') VARCHAR(255)"` + Amout string `json:"amout" xorm:"not null comment('变更数量') DECIMAL(16,6)"` + BeforeAmout string `json:"before_amout" xorm:"not null comment('变更前数量') DECIMAL(16,6)"` + AfterAmout string `json:"after_amout" xorm:"not null comment('变更后数量') DECIMAL(16,6)"` + SysFee string `json:"sys_fee" xorm:"not null default 0.000000 comment('手续费') DECIMAL(16,6)"` + CreateTime time.Time `json:"create_time" xorm:"created default 'CURRENT_TIMESTAMP' comment('创建时间') DATETIME"` + TransferType int `json:"transfer_type" xorm:"comment('转账类型:1全球分红,2管理员修改,3消费,4退回,5虚拟币兑换') TINYINT(100)"` + CoinIdTo int `json:"coin_id_to" xorm:"not null default 0 comment('兑换时目标币种id') INT(11)"` +} diff --git a/enum/block_star_chain.go b/enum/block_star_chain.go new file mode 100644 index 0000000..c10eafd --- /dev/null +++ b/enum/block_star_chain.go @@ -0,0 +1,47 @@ +package enum + +// 退款状态 +type BlockStarChainFlowKind int + +const ( + SystemTimingIssue BlockStarChainFlowKind = iota + SystemDestroy + StaticUnallocatedAndDestroy + DynamicallyUnallocatedAndDestroy + OperationCenterUnallocatedAndDestroy + OtherUnallocatedAndDestroy + RewardDestroy + RewardUnallocatedAndDestroy + GroupLotteryAndDestroy + RedEnvelopeTransferBalanceAndDestroy + TransferFeeAndDestroy +) + +func (kind BlockStarChainFlowKind) String() string { + switch kind { + case SystemTimingIssue: + return "系统定时发放" + case SystemDestroy: + return "系统销毁" + case StaticUnallocatedAndDestroy: + return "静态未分配完销毁" + case DynamicallyUnallocatedAndDestroy: + return "动态未分配完销毁" + case OperationCenterUnallocatedAndDestroy: + return "运营中心未分配完销毁" + case OtherUnallocatedAndDestroy: + return "其他未分配完销毁" + case RewardDestroy: + return "打赏销毁" + case RewardUnallocatedAndDestroy: + return "打赏未分配完销毁" + case GroupLotteryAndDestroy: + return "自营拼团抽奖销毁" + case RedEnvelopeTransferBalanceAndDestroy: + return "红包转余额销毁" + case TransferFeeAndDestroy: + return "转增手续费销毁" + default: + return "未知状态" + } +} diff --git a/go.mod b/go.mod index 1edd5d9..507ef3b 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,8 @@ module code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git go 1.15 require ( + github.com/go-redis/redis v6.15.9+incompatible + github.com/gomodule/redigo v1.8.9 // indirect github.com/jinzhu/copier v0.3.5 github.com/syyongx/php2go v0.9.6 go.uber.org/zap v1.13.0 diff --git a/go.sum b/go.sum index a5ad7bb..cdaefe8 100644 --- a/go.sum +++ b/go.sum @@ -71,6 +71,7 @@ github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgO github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= @@ -96,6 +97,8 @@ github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8l github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/gomodule/redigo v1.8.9 h1:Sl3u+2BI/kk+VEatbj0scLdrFhjPmbxOc1myhDP41ws= +github.com/gomodule/redigo v1.8.9/go.mod h1:7ArFNvsTjH8GMMzB4uy1snslv2BwmginuMs06a1uzZE= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= diff --git a/md/block_star_chain.go b/md/block_star_chain.go new file mode 100644 index 0000000..4c68463 --- /dev/null +++ b/md/block_star_chain.go @@ -0,0 +1,37 @@ +package md + +const ( + FlowDirectionIncome = 1 //流水 - 收入 + FlowDirectionExpenditure = 2 //流水 - 支出 +) + +const ( + RedisDataBase = 2 //TODO::区块星链的缓存统一存入redis 2 号库 + UserVirtualAmountRedisKey = "%s:user_virtual_amount:%d:user:%d" +) + +const ( + StaticAreaDistributionTitleForUserVirtualCoinFlow = "区块星链-静态区发放" + DynamicAreaDistributionTitleForUserVirtualCoinFlow = "区块星链-动态区发放" + StaticIssueAndDestroyStaticContributionTitleForUserVirtualCoinFlow = "区块星链-静态发放(销毁静态贡献值)" + DynamicIssueAndDestroyStaticContributionTitleForUserVirtualCoinFlow = "区块星链-动态发放(销毁静态贡献值)" +) +const ( + StaticAreaDistributionTransferTypeForUserVirtualCoinFlow = 100 + DynamicAreaDistributionTransferTypeForUserVirtualCoinFlow = 101 + StaticIssueAndDestroyStaticContributionTransferTypeForUserVirtualCoinFlow = 102 + DynamicIssueAndDestroyStaticContributionTransferTypeForUserVirtualCoinFlow = 103 +) + +const DealUserCoinRequestIdPrefix = "%s:block_star_chain_deal_user_coin:%d:uid:%d" + +type DealUserCoinReq struct { + Kind string `json:"kind"` + Mid string `json:"mid"` + Title string `json:"title"` + TransferType int `json:"transfer_type"` + OrdId string `json:"ord_id"` + CoinId int `json:"coin_id"` + Uid int `json:"uid"` + Amount float64 `json:"amount"` +} diff --git a/rule/block_star_chain_settlement.go b/rule/block_star_chain_settlement.go new file mode 100644 index 0000000..23168ff --- /dev/null +++ b/rule/block_star_chain_settlement.go @@ -0,0 +1,547 @@ +package rule + +import ( + "code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/db" + "code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/db/model" + "code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/enum" + "code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/md" + "code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/svc" + zhios_order_relate_utils "code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/utils" + "code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/utils/cache" + zhios_order_relate_logx "code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/utils/logx" + "errors" + "fmt" + "github.com/shopspring/decimal" + "strconv" + "time" + "xorm.io/xorm" +) + +func Init(redisAddr string) (err error) { + if redisAddr != "" { + cache.NewRedis(redisAddr) + } + _, err = cache.SelectDb(md.RedisDataBase) + return +} + +// DailySettlementBlockStarChain 每日结算“区块星链” +func DailySettlementBlockStarChain(engine *xorm.Engine, mid string) (err error) { + session := engine.NewSession() + defer func() { + session.Close() + if err := recover(); err != nil { + _ = zhios_order_relate_logx.Error(err) + } + }() + session.Begin() + now := time.Now() + + //1、查找 `block_star_chain` 基础设置 + blockStarChain, err := db.BlockStarChainGetOneByParams(session, map[string]interface{}{ + "key": "is_use", + "value": 1, + }) + if err != nil { + _ = session.Rollback() + return + } + //initialCoinTotal := zhios_order_relate_utils.StrToFloat64(blockStarChain.InitialCoinTotal) //初始区块币总量 + todayPublishCoin := zhios_order_relate_utils.StrToFloat64(blockStarChain.TodayPublishCoin) //今日区块币发行数量(若为0,则按当前每日区块币发行数量) + nowEverydayPublishCoin := zhios_order_relate_utils.StrToFloat64(blockStarChain.NowEverydayPublishCoin) //当前每日区块币发行数量 + var publishCoin = todayPublishCoin + + //2、判断今日是否有系统销毁 + var destroyCoinForSystem string + if todayPublishCoin != 0 && todayPublishCoin != nowEverydayPublishCoin { + destroyCoinForSystem = decimal.NewFromFloat(nowEverydayPublishCoin).Sub(decimal.NewFromFloat(todayPublishCoin)).String() + err := DealDestroyCoin(session, int(enum.SystemDestroy), zhios_order_relate_utils.StrToFloat64(destroyCoinForSystem), enum.SystemDestroy.String(), blockStarChain) + if err != nil { + _ = session.Rollback() + return + } + publishCoin = nowEverydayPublishCoin + } + + //3、进行系统区块币发放 + err = dealIssueCoin(session, int(enum.SystemDestroy), publishCoin, enum.SystemTimingIssue.String(), blockStarChain) + if err != nil { + _ = session.Rollback() + return + } + + //4、进行静态区-区块币统计分配 + var staticAreaCoinNums float64 //静态区分配区块币总数 + publishCoinStaticRate, _ := decimal.NewFromString(blockStarChain.PublishCoinStaticRate) //区块币发行静态占比 + publishCoinStaticRate = publishCoinStaticRate.Div(decimal.NewFromFloat(100)) + staticAreaCoinNums = zhios_order_relate_utils.StrToFloat64(decimal.NewFromFloat(publishCoin).Mul(publishCoinStaticRate).String()) + err, destroyCoinForStatic := statisticsAndDistributeCoinForStatic(session, mid, staticAreaCoinNums, *blockStarChain) + if err != nil { + _ = session.Rollback() + return + } + + //5、进行动态区-区块币统计分配 + var dynamicAreaCoinNums float64 //动态区分配区块币总数 + publishCoinDynamicRate, _ := decimal.NewFromString(blockStarChain.PublishCoinDynamicRate) //区块币发行动态占比 + publishCoinDynamicRate = publishCoinDynamicRate.Div(decimal.NewFromFloat(100)) + dynamicAreaCoinNums = zhios_order_relate_utils.StrToFloat64(decimal.NewFromFloat(publishCoin).Mul(publishCoinDynamicRate).String()) + err, destroyCoinForDynamic := statisticsAndDistributeCoinForDynamic(session, mid, dynamicAreaCoinNums, *blockStarChain) + if err != nil { + _ = session.Rollback() + return + } + + //6、进行运营中心-区块币统计 TODO::未完成分配 + var operationCenterCoinNums float64 //动态区分配区块币总数 + publishCoinOperationCenterRate, _ := decimal.NewFromString(blockStarChain.PublishCoinOperationCenterRate) //区块币发行动态占比 + publishCoinOperationCenterRate = publishCoinOperationCenterRate.Div(decimal.NewFromFloat(100)) + operationCenterCoinNums = zhios_order_relate_utils.StrToFloat64(decimal.NewFromFloat(publishCoin).Mul(publishCoinOperationCenterRate).String()) + + //7、进行其他-区块币统计 TODO::未完成分配 + var otherCoinNums float64 //动态区分配区块币总数 + publishCoinOperationOtherRate, _ := decimal.NewFromString(blockStarChain.PublishCoinOperationOtherRate) //区块币发行动态占比 + publishCoinOperationOtherRate = publishCoinOperationOtherRate.Div(decimal.NewFromFloat(100)) + otherCoinNums = zhios_order_relate_utils.StrToFloat64(decimal.NewFromFloat(publishCoin).Mul(publishCoinOperationOtherRate).String()) + + //8、插入 block_star_chain_settlement_records 记录 + var blockStarChainSettlementRecords = model.BlockStarChainSettlementRecords{ + CoinId: blockStarChain.Coin1, + TodayPublishCoin: zhios_order_relate_utils.Float64ToStr(publishCoin), + TodayDestroyCoinForSystem: destroyCoinForSystem, + TodayPublishCoinForStatic: zhios_order_relate_utils.Float64ToStr(staticAreaCoinNums), + TodayDestroyCoinForStatic: zhios_order_relate_utils.Float64ToStr(destroyCoinForStatic), + TodayPublishCoinForDynamic: zhios_order_relate_utils.Float64ToStr(dynamicAreaCoinNums), + TodayDestroyCoinForDynamic: zhios_order_relate_utils.Float64ToStr(destroyCoinForDynamic), + TodayPublishCoinForOperationCenter: zhios_order_relate_utils.Float64ToStr(operationCenterCoinNums), + TodayDestroyCoinForOperationCenter: "", + TodayPublishCoinForOther: zhios_order_relate_utils.Float64ToStr(otherCoinNums), + TodayDestroyCoinForOther: "", + Date: now.Format("2006-01"), + CreateAt: now, + UpdateAt: now, + } + _, err = db.BlockStarChainSettlementRecordsInsert(session, &blockStarChainSettlementRecords) + if err != nil { + _ = session.Rollback() + return + } + + //6、计算当前每日区块币应发行数量 + err = calcNowEverydayPublishCoin(session, *blockStarChain) + if err != nil { + _ = session.Rollback() + return + } + return nil +} + +/* + 统计分配区块币-静态区 + TODO:: 公式【 个人贡献值/全网贡献值x2100枚=每天获取的惠积分 】 +*/ +func statisticsAndDistributeCoinForStatic(session *xorm.Session, mid string, publishCoin float64, chain model.BlockStarChain) (err error, unassignedTotalCoinValue float64) { + publishCoinValue := decimal.NewFromFloat(publishCoin) //静态区发行区块币数量 + platformGuidePriceForCoinValue := decimal.NewFromFloat(zhios_order_relate_utils.StrToFloat64(chain.PlatformGuidePriceForCoin)) //今日平台区块币指导价 + var unassignedTotalCoin = decimal.NewFromFloat(0) //未分配完的区块币 + var userVirtualAmount model.UserVirtualAmount + var userVirtualAmounts []model.UserVirtualAmount + //1、统计出静态区总贡献值 + sumStatic, err := session.Table("user_virtual_amount").Where("coin_id =?", chain.Coin2).Sum(&userVirtualAmount, "amount") + if err != nil { + return + } + sumStaticValue := decimal.NewFromFloat(sumStatic) + + //2、查询出所有拥有静态贡献值的用户 + err = session.Table("user_virtual_amount").Where("coin_id =?", chain.Coin2).And("amount > 0").Find(&userVirtualAmounts) + if err != nil { + return + } + + for _, item := range userVirtualAmounts { + amount := decimal.NewFromFloat(zhios_order_relate_utils.StrToFloat64(item.Amount)) //用户贡献值余额 + getCoin := amount.Div(sumStaticValue).Mul(publishCoinValue) //得到的区块币 + needDestroyContribution := getCoin.Mul(platformGuidePriceForCoinValue) //需销毁贡献值 + //3.1判断静态贡献值是否足够 + if needDestroyContribution.GreaterThan(amount) { + //TODO::公式【得到的区块币 - ((需销毁贡献值 - 用户贡献值余额) / 今日平台区块币指导价)】 + tempCoin := (needDestroyContribution.Sub(amount)).Div(platformGuidePriceForCoinValue) + getCoin = getCoin.Sub(tempCoin) + unassignedTotalCoin.Add(tempCoin) + needDestroyContribution = amount + } + + //3.2给相应用户加上分配到的虚拟币 + err := DealUserCoin(session, md.DealUserCoinReq{ + Kind: "add", + Mid: mid, + Title: md.StaticAreaDistributionTitleForUserVirtualCoinFlow, + TransferType: md.StaticAreaDistributionTransferTypeForUserVirtualCoinFlow, + OrdId: "", + CoinId: chain.Coin1, + Uid: item.Uid, + Amount: zhios_order_relate_utils.StrToFloat64(getCoin.String()), + }) + if err != nil { + return + } + + //3.3给相应用户扣除 "静态发放(销毁静态贡献值)" + err = DealUserCoin(session, md.DealUserCoinReq{ + Kind: "sub", + Mid: mid, + Title: md.StaticIssueAndDestroyStaticContributionTitleForUserVirtualCoinFlow, + TransferType: md.StaticIssueAndDestroyStaticContributionTransferTypeForUserVirtualCoinFlow, + OrdId: "", + CoinId: chain.Coin2, + Uid: item.Uid, + Amount: zhios_order_relate_utils.StrToFloat64(getCoin.String()), + }) + if err != nil { + return + } + } + + //4、处理未分配完的区块币-静态区 + unassignedTotalCoinValue = zhios_order_relate_utils.StrToFloat64(unassignedTotalCoin.String()) + if unassignedTotalCoinValue > 0 { + err := DealDestroyCoin(session, int(enum.StaticUnallocatedAndDestroy), unassignedTotalCoinValue, enum.StaticUnallocatedAndDestroy.String(), &chain) + if err != nil { + return + } + } + return +} + +/* + 统计分配区块币-动态区 +*/ +func statisticsAndDistributeCoinForDynamic(session *xorm.Session, mid string, publishCoin float64, chain model.BlockStarChain) (err error, unassignedTotalCoinValue float64) { + publishCoinValue := decimal.NewFromFloat(publishCoin) //动态区发行区块币数量 + platformGuidePriceForCoinValue := decimal.NewFromFloat(zhios_order_relate_utils.StrToFloat64(chain.PlatformGuidePriceForCoin)) //今日平台区块币指导价 + var unassignedTotalCoin = decimal.NewFromFloat(0) //未分配完的区块币 + var userVirtualAmount model.UserVirtualAmount + var userVirtualAmounts []model.UserVirtualAmount + //1、统计出动态区总贡献值 + sumStatic, err := session.Table("user_virtual_amount").Where("coin_id =?", chain.Coin3).Sum(&userVirtualAmount, "amount") + if err != nil { + return + } + sumStaticValue := decimal.NewFromFloat(sumStatic) + + //2、查询出所有拥有动态贡献值的用户 + err = session.Table("user_virtual_amount").Where("coin_id =?", chain.Coin3).And("amount > 0").Find(&userVirtualAmounts) + if err != nil { + return + } + + //3、循环处理每个用户的数据(增加虚拟币,扣除静态贡献值) + for _, item := range userVirtualAmounts { + amount := decimal.NewFromFloat(zhios_order_relate_utils.StrToFloat64(item.Amount)) //用户贡献值余额 + getCoin := amount.Div(sumStaticValue).Mul(publishCoinValue) //得到的区块币 + needDestroyContribution := getCoin.Mul(platformGuidePriceForCoinValue) //需销毁贡献值 + //3.1判断静态贡献值是否足够 + if needDestroyContribution.GreaterThan(amount) { + //TODO::公式【得到的区块币 - ((需销毁贡献值 - 用户贡献值余额) / 今日平台区块币指导价)】 + tempCoin := (needDestroyContribution.Sub(amount)).Div(platformGuidePriceForCoinValue) + getCoin = getCoin.Sub(tempCoin) + unassignedTotalCoin.Add(tempCoin) + needDestroyContribution = amount + } + + //3.2给相应用户加上分配到的虚拟币 + err := DealUserCoin(session, md.DealUserCoinReq{ + Kind: "add", + Mid: mid, + Title: md.DynamicAreaDistributionTitleForUserVirtualCoinFlow, + TransferType: md.DynamicAreaDistributionTransferTypeForUserVirtualCoinFlow, + OrdId: "", + CoinId: chain.Coin1, + Uid: item.Uid, + Amount: zhios_order_relate_utils.StrToFloat64(getCoin.String()), + }) + if err != nil { + return + } + + //3.3给相应用户扣除 "动态发放(销毁静态贡献值)" + err = DealUserCoin(session, md.DealUserCoinReq{ + Kind: "sub", + Mid: mid, + Title: md.DynamicIssueAndDestroyStaticContributionTitleForUserVirtualCoinFlow, + TransferType: md.DynamicIssueAndDestroyStaticContributionTransferTypeForUserVirtualCoinFlow, + OrdId: "", + CoinId: chain.Coin2, + Uid: item.Uid, + Amount: zhios_order_relate_utils.StrToFloat64(getCoin.String()), + }) + if err != nil { + return + } + } + + //4、处理未分配完的区块币-动态区 + unassignedTotalCoinValue = zhios_order_relate_utils.StrToFloat64(unassignedTotalCoin.String()) + if unassignedTotalCoinValue > 0 { + err := DealDestroyCoin(session, int(enum.DynamicallyUnallocatedAndDestroy), unassignedTotalCoinValue, enum.DynamicallyUnallocatedAndDestroy.String(), &chain) + if err != nil { + return + } + } + return +} + +//计算当前每日区块币应发行数量 +func calcNowEverydayPublishCoin(session *xorm.Session, chain model.BlockStarChain) error { + now := time.Now() + startAt := zhios_order_relate_utils.String2Time(chain.StartAt) //起始时间 + diffDays := zhios_order_relate_utils.GetDiffDays(now, startAt) //相差天数 + remainder := diffDays % 30 + if remainder == 0 { + everyThirtyDaysReduceRate := zhios_order_relate_utils.StrToFloat64(chain.EveryThirtyDaysReduceRate) / 100 //每三十日递减发行百分比 + nowEverydayPublishCoin := zhios_order_relate_utils.StrToFloat64(chain.NowEverydayPublishCoin) //当前每日区块币发行数量 + chain.NowEverydayPublishCoin = zhios_order_relate_utils.Float64ToStrPrec10(nowEverydayPublishCoin * (1 - everyThirtyDaysReduceRate)) + updateAffected, err := db.BlockStarChainUpdate(session, chain.Id, &chain, "now_everyday_publish_coin") + if err != nil { + return err + } + if updateAffected == 0 { + err = errors.New("更新 block_star_chain 的 now_everyday_publish_coin 记录失败") + return err + } + } + return nil +} + +//处理虚拟币 - 发放 +func dealIssueCoin(session *xorm.Session, kind int, amount float64, title string, chain *model.BlockStarChain) error { + now := time.Now() + var blockStarChainFlow model.BlockStarChainFlow + blockStarChainFlow.CoinId = chain.Coin1 + blockStarChainFlow.Direction = md.FlowDirectionIncome + blockStarChainFlow.Kind = kind + blockStarChainFlow.Title = title + blockStarChainFlow.CreateTime = now + switch kind { + case int(enum.SystemTimingIssue): + blockStarChainFlow.BeforeTotalNowCoin = chain.TotalNowCoin + blockStarChainFlow.AfterTotalNowCoin = zhios_order_relate_utils.Float64ToStrPrec10(zhios_order_relate_utils.StrToFloat64(chain.TotalRemainderCoin) + amount) + blockStarChainFlow.BeforeTotalRemainderCoin = chain.TotalRemainderCoin + blockStarChainFlow.AfterTotalRemainderCoin = zhios_order_relate_utils.Float64ToStrPrec10(zhios_order_relate_utils.StrToFloat64(chain.TotalRemainderCoin) - amount) + blockStarChainFlow.BeforeTotalPublishCoin = chain.TotalPublishCoin + blockStarChainFlow.AfterTotalPublishCoin = zhios_order_relate_utils.Float64ToStrPrec10(zhios_order_relate_utils.StrToFloat64(chain.TotalRemainderCoin) + amount) + blockStarChainFlow.BeforeTotalDestroyCoin = chain.TotalDestroyCoin + blockStarChainFlow.AfterTotalDestroyCoin = chain.TotalDestroyCoin + break + } + chain.TotalNowCoin = blockStarChainFlow.AfterTotalNowCoin + chain.TotalRemainderCoin = blockStarChainFlow.AfterTotalRemainderCoin + chain.TotalPublishCoin = blockStarChainFlow.AfterTotalPublishCoin + chain.TotalDestroyCoin = blockStarChainFlow.AfterTotalDestroyCoin + + //更新 `block_star_chain` 表 + updateAffected, err := db.BlockStarChainUpdate(session, chain.Id, chain) + if err != nil { + return err + } + if updateAffected == 0 { + err = errors.New("更新 block_star_chain 记录失败") + return err + } + + //插入 `block_star_chain_flow` 记录 + _, err = db.BlockStarChainFlowInsert(session, &blockStarChainFlow) + if err != nil { + return err + } + return nil +} + +//DealDestroyCoin 处理虚拟币 - 销毁 +func DealDestroyCoin(session *xorm.Session, kind int, amount float64, title string, chain *model.BlockStarChain) error { + now := time.Now() + var blockStarChainFlow model.BlockStarChainFlow + blockStarChainFlow.CoinId = chain.Coin1 + blockStarChainFlow.Direction = md.FlowDirectionExpenditure + blockStarChainFlow.Kind = kind + blockStarChainFlow.Title = title + blockStarChainFlow.CreateTime = now + switch kind { + case int(enum.SystemDestroy): + blockStarChainFlow.BeforeTotalNowCoin = chain.TotalNowCoin + blockStarChainFlow.AfterTotalNowCoin = chain.TotalNowCoin + blockStarChainFlow.BeforeTotalRemainderCoin = chain.TotalRemainderCoin + blockStarChainFlow.AfterTotalRemainderCoin = zhios_order_relate_utils.Float64ToStrPrec10(zhios_order_relate_utils.StrToFloat64(chain.TotalRemainderCoin) - amount) + blockStarChainFlow.BeforeTotalPublishCoin = chain.TotalPublishCoin + blockStarChainFlow.AfterTotalPublishCoin = chain.TotalPublishCoin + blockStarChainFlow.BeforeTotalDestroyCoin = chain.TotalDestroyCoin + blockStarChainFlow.AfterTotalDestroyCoin = zhios_order_relate_utils.Float64ToStrPrec10(zhios_order_relate_utils.StrToFloat64(chain.TotalRemainderCoin) + amount) + break + case int(enum.StaticUnallocatedAndDestroy): + blockStarChainFlow.BeforeTotalNowCoin = chain.TotalNowCoin + blockStarChainFlow.AfterTotalNowCoin = zhios_order_relate_utils.Float64ToStrPrec10(zhios_order_relate_utils.StrToFloat64(chain.TotalNowCoin) - amount) + blockStarChainFlow.BeforeTotalRemainderCoin = chain.TotalRemainderCoin + blockStarChainFlow.AfterTotalRemainderCoin = chain.TotalRemainderCoin + blockStarChainFlow.BeforeTotalPublishCoin = chain.TotalPublishCoin + blockStarChainFlow.AfterTotalPublishCoin = chain.TotalPublishCoin + blockStarChainFlow.BeforeTotalDestroyCoin = chain.TotalDestroyCoin + blockStarChainFlow.AfterTotalDestroyCoin = zhios_order_relate_utils.Float64ToStrPrec10(zhios_order_relate_utils.StrToFloat64(chain.TotalRemainderCoin) + amount) + break + case int(enum.DynamicallyUnallocatedAndDestroy): + blockStarChainFlow.BeforeTotalNowCoin = chain.TotalNowCoin + blockStarChainFlow.AfterTotalNowCoin = zhios_order_relate_utils.Float64ToStrPrec10(zhios_order_relate_utils.StrToFloat64(chain.TotalNowCoin) - amount) + blockStarChainFlow.BeforeTotalRemainderCoin = chain.TotalRemainderCoin + blockStarChainFlow.AfterTotalRemainderCoin = chain.TotalRemainderCoin + blockStarChainFlow.BeforeTotalPublishCoin = chain.TotalPublishCoin + blockStarChainFlow.AfterTotalPublishCoin = chain.TotalPublishCoin + blockStarChainFlow.BeforeTotalDestroyCoin = chain.TotalDestroyCoin + blockStarChainFlow.AfterTotalDestroyCoin = zhios_order_relate_utils.Float64ToStrPrec10(zhios_order_relate_utils.StrToFloat64(chain.TotalRemainderCoin) + amount) + break + case int(enum.OperationCenterUnallocatedAndDestroy): + blockStarChainFlow.BeforeTotalNowCoin = chain.TotalNowCoin + blockStarChainFlow.AfterTotalNowCoin = zhios_order_relate_utils.Float64ToStrPrec10(zhios_order_relate_utils.StrToFloat64(chain.TotalNowCoin) - amount) + blockStarChainFlow.BeforeTotalRemainderCoin = chain.TotalRemainderCoin + blockStarChainFlow.AfterTotalRemainderCoin = chain.TotalRemainderCoin + blockStarChainFlow.BeforeTotalPublishCoin = chain.TotalPublishCoin + blockStarChainFlow.AfterTotalPublishCoin = chain.TotalPublishCoin + blockStarChainFlow.BeforeTotalDestroyCoin = chain.TotalDestroyCoin + blockStarChainFlow.AfterTotalDestroyCoin = zhios_order_relate_utils.Float64ToStrPrec10(zhios_order_relate_utils.StrToFloat64(chain.TotalRemainderCoin) + amount) + break + case int(enum.OtherUnallocatedAndDestroy): + blockStarChainFlow.BeforeTotalNowCoin = chain.TotalNowCoin + blockStarChainFlow.AfterTotalNowCoin = zhios_order_relate_utils.Float64ToStrPrec10(zhios_order_relate_utils.StrToFloat64(chain.TotalNowCoin) - amount) + blockStarChainFlow.BeforeTotalRemainderCoin = chain.TotalRemainderCoin + blockStarChainFlow.AfterTotalRemainderCoin = chain.TotalRemainderCoin + blockStarChainFlow.BeforeTotalPublishCoin = chain.TotalPublishCoin + blockStarChainFlow.AfterTotalPublishCoin = chain.TotalPublishCoin + blockStarChainFlow.BeforeTotalDestroyCoin = chain.TotalDestroyCoin + blockStarChainFlow.AfterTotalDestroyCoin = zhios_order_relate_utils.Float64ToStrPrec10(zhios_order_relate_utils.StrToFloat64(chain.TotalRemainderCoin) + amount) + break + case int(enum.RewardDestroy): + blockStarChainFlow.BeforeTotalNowCoin = chain.TotalNowCoin + blockStarChainFlow.AfterTotalNowCoin = zhios_order_relate_utils.Float64ToStrPrec10(zhios_order_relate_utils.StrToFloat64(chain.TotalNowCoin) - amount) + blockStarChainFlow.BeforeTotalRemainderCoin = chain.TotalRemainderCoin + blockStarChainFlow.AfterTotalRemainderCoin = chain.TotalRemainderCoin + blockStarChainFlow.BeforeTotalPublishCoin = chain.TotalPublishCoin + blockStarChainFlow.AfterTotalPublishCoin = chain.TotalPublishCoin + blockStarChainFlow.BeforeTotalDestroyCoin = chain.TotalDestroyCoin + blockStarChainFlow.AfterTotalDestroyCoin = zhios_order_relate_utils.Float64ToStrPrec10(zhios_order_relate_utils.StrToFloat64(chain.TotalRemainderCoin) + amount) + break + case int(enum.RewardUnallocatedAndDestroy): + blockStarChainFlow.BeforeTotalNowCoin = chain.TotalNowCoin + blockStarChainFlow.AfterTotalNowCoin = zhios_order_relate_utils.Float64ToStrPrec10(zhios_order_relate_utils.StrToFloat64(chain.TotalNowCoin) - amount) + blockStarChainFlow.BeforeTotalRemainderCoin = chain.TotalRemainderCoin + blockStarChainFlow.AfterTotalRemainderCoin = chain.TotalRemainderCoin + blockStarChainFlow.BeforeTotalPublishCoin = chain.TotalPublishCoin + blockStarChainFlow.AfterTotalPublishCoin = chain.TotalPublishCoin + blockStarChainFlow.BeforeTotalDestroyCoin = chain.TotalDestroyCoin + blockStarChainFlow.AfterTotalDestroyCoin = zhios_order_relate_utils.Float64ToStrPrec10(zhios_order_relate_utils.StrToFloat64(chain.TotalRemainderCoin) + amount) + break + case int(enum.GroupLotteryAndDestroy): + blockStarChainFlow.BeforeTotalNowCoin = chain.TotalNowCoin + blockStarChainFlow.AfterTotalNowCoin = zhios_order_relate_utils.Float64ToStrPrec10(zhios_order_relate_utils.StrToFloat64(chain.TotalNowCoin) - amount) + blockStarChainFlow.BeforeTotalRemainderCoin = chain.TotalRemainderCoin + blockStarChainFlow.AfterTotalRemainderCoin = chain.TotalRemainderCoin + blockStarChainFlow.BeforeTotalPublishCoin = chain.TotalPublishCoin + blockStarChainFlow.AfterTotalPublishCoin = chain.TotalPublishCoin + blockStarChainFlow.BeforeTotalDestroyCoin = chain.TotalDestroyCoin + blockStarChainFlow.AfterTotalDestroyCoin = zhios_order_relate_utils.Float64ToStrPrec10(zhios_order_relate_utils.StrToFloat64(chain.TotalRemainderCoin) + amount) + break + case int(enum.RedEnvelopeTransferBalanceAndDestroy): + blockStarChainFlow.BeforeTotalNowCoin = chain.TotalNowCoin + blockStarChainFlow.AfterTotalNowCoin = zhios_order_relate_utils.Float64ToStrPrec10(zhios_order_relate_utils.StrToFloat64(chain.TotalNowCoin) - amount) + blockStarChainFlow.BeforeTotalRemainderCoin = chain.TotalRemainderCoin + blockStarChainFlow.AfterTotalRemainderCoin = chain.TotalRemainderCoin + blockStarChainFlow.BeforeTotalPublishCoin = chain.TotalPublishCoin + blockStarChainFlow.AfterTotalPublishCoin = chain.TotalPublishCoin + blockStarChainFlow.BeforeTotalDestroyCoin = chain.TotalDestroyCoin + blockStarChainFlow.AfterTotalDestroyCoin = zhios_order_relate_utils.Float64ToStrPrec10(zhios_order_relate_utils.StrToFloat64(chain.TotalRemainderCoin) + amount) + break + case int(enum.GroupLotteryAndDestroy): + blockStarChainFlow.BeforeTotalNowCoin = chain.TotalNowCoin + blockStarChainFlow.AfterTotalNowCoin = zhios_order_relate_utils.Float64ToStrPrec10(zhios_order_relate_utils.StrToFloat64(chain.TotalNowCoin) - amount) + blockStarChainFlow.BeforeTotalRemainderCoin = chain.TotalRemainderCoin + blockStarChainFlow.AfterTotalRemainderCoin = chain.TotalRemainderCoin + blockStarChainFlow.BeforeTotalPublishCoin = chain.TotalPublishCoin + blockStarChainFlow.AfterTotalPublishCoin = chain.TotalPublishCoin + blockStarChainFlow.BeforeTotalDestroyCoin = chain.TotalDestroyCoin + blockStarChainFlow.AfterTotalDestroyCoin = zhios_order_relate_utils.Float64ToStrPrec10(zhios_order_relate_utils.StrToFloat64(chain.TotalRemainderCoin) + amount) + break + } + chain.TotalNowCoin = blockStarChainFlow.AfterTotalNowCoin + chain.TotalRemainderCoin = blockStarChainFlow.AfterTotalRemainderCoin + chain.TotalPublishCoin = blockStarChainFlow.AfterTotalPublishCoin + chain.TotalDestroyCoin = blockStarChainFlow.AfterTotalDestroyCoin + //更新 `block_star_chain` 表 + updateAffected, err := db.BlockStarChainUpdate(session, chain.Id, chain) + if err != nil { + return err + } + if updateAffected == 0 { + err = errors.New("更新 block_star_chain 记录失败") + return err + } + + //插入 `block_star_chain_flow` 记录 + _, err = db.BlockStarChainFlowInsert(session, &blockStarChainFlow) + if err != nil { + return err + } + return nil +} + +// DealUserCoin 处理给用户虚拟币积分 +func DealUserCoin(session *xorm.Session, req md.DealUserCoinReq) (err error) { + //1、分布式锁阻拦 + requestIdPrefix := fmt.Sprintf(md.DealUserCoinRequestIdPrefix, req.Mid, req.CoinId, req.Uid) + cb, err := svc.HandleDistributedLock(req.Mid, strconv.Itoa(req.Uid), requestIdPrefix) + if err != nil { + return err + } + if cb != nil { + defer cb() // 释放锁 + } + + //2、计算&&组装数据 + now := time.Now() + coinAmount, err := svc.GetUserCoinAmount(session, req.Mid, req.CoinId, req.Uid) + if err != nil { + return + } + coinAmountValue := decimal.NewFromFloat(zhios_order_relate_utils.StrToFloat64(coinAmount)) + amountValue := decimal.NewFromFloat(req.Amount) + + var userVirtualCoinFlow model.UserVirtualCoinFlow + userVirtualCoinFlow.CoinId = req.CoinId + userVirtualCoinFlow.Title = req.Title + userVirtualCoinFlow.TransferType = req.TransferType + userVirtualCoinFlow.Uid = req.Uid + userVirtualCoinFlow.Amout = amountValue.String() + userVirtualCoinFlow.CreateTime = now + + if req.Kind == "add" { + userVirtualCoinFlow.Direction = 1 + userVirtualCoinFlow.AfterAmout = coinAmountValue.Add(amountValue).String() + } else if req.Kind == "sub" { + userVirtualCoinFlow.Direction = 2 + userVirtualCoinFlow.AfterAmout = coinAmountValue.Sub(amountValue).String() + } else { + err = errors.New("错误的kind类型") + return + } + + //3、插入 `user_virtual_coin_flow` 记录 + _, err = db.UserVirtualCoinFlowInsert(session, &userVirtualCoinFlow) + if err != nil { + return err + } + + //4、修改 `user_virtual_amount`的amount值 && 及缓存 + err = svc.SetCacheUserVirtualAmount(session, req.Mid, userVirtualCoinFlow.AfterAmout, req.CoinId, req.Uid, true) + if err != nil { + return err + } + + return nil +} diff --git a/svc/svc_block_star_chain_settlement.go b/svc/svc_block_star_chain_settlement.go new file mode 100644 index 0000000..514be4a --- /dev/null +++ b/svc/svc_block_star_chain_settlement.go @@ -0,0 +1,60 @@ +package svc + +import ( + "code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/db" + "code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/db/model" + "code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/md" + zhios_order_relate_utils "code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/utils" + "code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/utils/cache" + "fmt" + "xorm.io/xorm" +) + +// GetUserCoinAmount 获取用户虚拟积分余额 +func GetUserCoinAmount(session *xorm.Session, masterId string, coinId, uid int) (amount string, err error) { + redisKey := fmt.Sprintf(md.UserVirtualAmountRedisKey, masterId, coinId, uid) + amount, err = cache.GetString(redisKey) + if err != nil { + if err.Error() == "redigo: nil returned" { + if err != nil { + return amount, err + } + userVirtualAmount, err := db.GetUserVirtualWalletWithSession(session, uid, coinId) + if err != nil { + return amount, err + } + if userVirtualAmount == nil { + amount = "0" + } else { + amount = userVirtualAmount.Amount + } + //将获取到的余额值缓存至redis + _ = SetCacheUserVirtualAmount(session, masterId, amount, coinId, uid, false) + return amount, nil + } + return amount, err + } + return amount, nil +} + +// SetCacheUserVirtualAmount 设置缓存的用户虚拟币积分余额 +func SetCacheUserVirtualAmount(session *xorm.Session, masterId, amount string, coinId, uid int, isUpdateDb bool) error { + redisKey := fmt.Sprintf(md.UserVirtualAmountRedisKey, masterId, coinId, uid) + if isUpdateDb { + _, err := session.Where("uid=?", uid).And("coin_id=?", coinId).Update(model.UserVirtualAmount{ + Uid: uid, + CoinId: coinId, + Amount: amount, + }) + if err != nil { + return err + } + } + //_, err := cache.Set(redisKey, int64(utils.StrToFloat64(amount))) + //TODO::默认缓存1小时 (先调整为 10 min) + _, err := cache.SetEx(redisKey, int64(zhios_order_relate_utils.StrToFloat64(amount)), 60*60*0.1) + if err != nil { + return err + } + return nil +} diff --git a/svc/svc_redis_mutex_lock.go b/svc/svc_redis_mutex_lock.go new file mode 100644 index 0000000..f9e714b --- /dev/null +++ b/svc/svc_redis_mutex_lock.go @@ -0,0 +1,84 @@ +package svc + +import ( + "code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/md" + zhios_order_relate_utils "code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/utils" + "code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/utils/cache" + "errors" + "fmt" + "math/rand" + "reflect" + "time" +) + +const redisMutexLockExpTime = 15 + +// TryGetDistributedLock 分布式锁获取 +// requestId 用于标识请求客户端,可以是随机字符串,需确保唯一 +func TryGetDistributedLock(lockKey, requestId string, isNegative bool) bool { + if isNegative { // 多次尝试获取 + retry := 1 + for { + ok, err := cache.Do("SET", lockKey, requestId, "EX", redisMutexLockExpTime, "NX") + // 获取锁成功 + if err == nil && ok == "OK" { + return true + } + // 尝试多次没获取成功 + if retry > 10 { + return false + } + time.Sleep(time.Millisecond * time.Duration(rand.Intn(1000))) + retry += 1 + } + } else { // 只尝试一次 + ok, err := cache.Do("SET", lockKey, requestId, "EX", redisMutexLockExpTime, "NX") + // 获取锁成功 + if err == nil && ok == "OK" { + return true + } + + return false + } +} + +// ReleaseDistributedLock 释放锁,通过比较requestId,用于确保客户端只释放自己的锁,使用lua脚本保证操作的原子型 +func ReleaseDistributedLock(lockKey, requestId string) (bool, error) { + luaScript := ` + if redis.call("get",KEYS[1]) == ARGV[1] + then + return redis.call("del",KEYS[1]) + else + return 0 + end` + + do, err := cache.Do("eval", luaScript, 1, lockKey, requestId) + fmt.Println(reflect.TypeOf(do)) + fmt.Println(do) + if zhios_order_relate_utils.AnyToInt64(do) == 1 { + return true, err + } else { + return false, err + } +} + +func GetDistributedLockRequestId(prefix string) string { + return prefix + zhios_order_relate_utils.IntToStr(rand.Intn(100000000)) +} + +// HandleDistributedLock 处理余额更新时获取锁和释放锁 如果加锁成功,使用语句 ` defer cb() ` 释放锁 +func HandleDistributedLock(masterId, uid, requestIdPrefix string) (cb func(), err error) { + // 获取余额更新锁 + balanceLockKey := fmt.Sprintf(md.UserFinValidUpdateLock, masterId, uid) + requestId := GetDistributedLockRequestId(requestIdPrefix) + balanceLockOk := TryGetDistributedLock(balanceLockKey, requestId, true) + if !balanceLockOk { + return nil, errors.New("系统繁忙,请稍后再试") + } + + cb = func() { + _, _ = ReleaseDistributedLock(balanceLockKey, requestId) + } + + return cb, nil +} diff --git a/utils/cache/base.go b/utils/cache/base.go new file mode 100644 index 0000000..64648dd --- /dev/null +++ b/utils/cache/base.go @@ -0,0 +1,421 @@ +package cache + +import ( + "errors" + "fmt" + "strconv" + "time" +) + +const ( + redisDialTTL = 10 * time.Second + redisReadTTL = 3 * time.Second + redisWriteTTL = 3 * time.Second + redisIdleTTL = 10 * time.Second + redisPoolTTL = 10 * time.Second + redisPoolSize int = 512 + redisMaxIdleConn int = 64 + redisMaxActive int = 512 +) + +var ( + ErrNil = errors.New("nil return") + ErrWrongArgsNum = errors.New("args num error") + ErrNegativeInt = errors.New("redis cluster: unexpected value for Uint64") +) + +// 以下为提供类型转换 + +func Int(reply interface{}, err error) (int, error) { + if err != nil { + return 0, err + } + switch reply := reply.(type) { + case int: + return reply, nil + case int8: + return int(reply), nil + case int16: + return int(reply), nil + case int32: + return int(reply), nil + case int64: + x := int(reply) + if int64(x) != reply { + return 0, strconv.ErrRange + } + return x, nil + case uint: + n := int(reply) + if n < 0 { + return 0, strconv.ErrRange + } + return n, nil + case uint8: + return int(reply), nil + case uint16: + return int(reply), nil + case uint32: + n := int(reply) + if n < 0 { + return 0, strconv.ErrRange + } + return n, nil + case uint64: + n := int(reply) + if n < 0 { + return 0, strconv.ErrRange + } + return n, nil + case []byte: + data := string(reply) + if len(data) == 0 { + return 0, ErrNil + } + + n, err := strconv.ParseInt(data, 10, 0) + return int(n), err + case string: + if len(reply) == 0 { + return 0, ErrNil + } + + n, err := strconv.ParseInt(reply, 10, 0) + return int(n), err + case nil: + return 0, ErrNil + case error: + return 0, reply + } + return 0, fmt.Errorf("redis cluster: unexpected type for Int, got type %T", reply) +} + +func Int64(reply interface{}, err error) (int64, error) { + if err != nil { + return 0, err + } + switch reply := reply.(type) { + case int: + return int64(reply), nil + case int8: + return int64(reply), nil + case int16: + return int64(reply), nil + case int32: + return int64(reply), nil + case int64: + return reply, nil + case uint: + n := int64(reply) + if n < 0 { + return 0, strconv.ErrRange + } + return n, nil + case uint8: + return int64(reply), nil + case uint16: + return int64(reply), nil + case uint32: + return int64(reply), nil + case uint64: + n := int64(reply) + if n < 0 { + return 0, strconv.ErrRange + } + return n, nil + case []byte: + data := string(reply) + if len(data) == 0 { + return 0, ErrNil + } + + n, err := strconv.ParseInt(data, 10, 64) + return n, err + case string: + if len(reply) == 0 { + return 0, ErrNil + } + + n, err := strconv.ParseInt(reply, 10, 64) + return n, err + case nil: + return 0, ErrNil + case error: + return 0, reply + } + return 0, fmt.Errorf("redis cluster: unexpected type for Int64, got type %T", reply) +} + +func Uint64(reply interface{}, err error) (uint64, error) { + if err != nil { + return 0, err + } + switch reply := reply.(type) { + case uint: + return uint64(reply), nil + case uint8: + return uint64(reply), nil + case uint16: + return uint64(reply), nil + case uint32: + return uint64(reply), nil + case uint64: + return reply, nil + case int: + if reply < 0 { + return 0, ErrNegativeInt + } + return uint64(reply), nil + case int8: + if reply < 0 { + return 0, ErrNegativeInt + } + return uint64(reply), nil + case int16: + if reply < 0 { + return 0, ErrNegativeInt + } + return uint64(reply), nil + case int32: + if reply < 0 { + return 0, ErrNegativeInt + } + return uint64(reply), nil + case int64: + if reply < 0 { + return 0, ErrNegativeInt + } + return uint64(reply), nil + case []byte: + data := string(reply) + if len(data) == 0 { + return 0, ErrNil + } + + n, err := strconv.ParseUint(data, 10, 64) + return n, err + case string: + if len(reply) == 0 { + return 0, ErrNil + } + + n, err := strconv.ParseUint(reply, 10, 64) + return n, err + case nil: + return 0, ErrNil + case error: + return 0, reply + } + return 0, fmt.Errorf("redis cluster: unexpected type for Uint64, got type %T", reply) +} + +func Float64(reply interface{}, err error) (float64, error) { + if err != nil { + return 0, err + } + + var value float64 + err = nil + switch v := reply.(type) { + case float32: + value = float64(v) + case float64: + value = v + case int: + value = float64(v) + case int8: + value = float64(v) + case int16: + value = float64(v) + case int32: + value = float64(v) + case int64: + value = float64(v) + case uint: + value = float64(v) + case uint8: + value = float64(v) + case uint16: + value = float64(v) + case uint32: + value = float64(v) + case uint64: + value = float64(v) + case []byte: + data := string(v) + if len(data) == 0 { + return 0, ErrNil + } + value, err = strconv.ParseFloat(string(v), 64) + case string: + if len(v) == 0 { + return 0, ErrNil + } + value, err = strconv.ParseFloat(v, 64) + case nil: + err = ErrNil + case error: + err = v + default: + err = fmt.Errorf("redis cluster: unexpected type for Float64, got type %T", v) + } + + return value, err +} + +func Bool(reply interface{}, err error) (bool, error) { + if err != nil { + return false, err + } + switch reply := reply.(type) { + case bool: + return reply, nil + case int64: + return reply != 0, nil + case []byte: + data := string(reply) + if len(data) == 0 { + return false, ErrNil + } + + return strconv.ParseBool(data) + case string: + if len(reply) == 0 { + return false, ErrNil + } + + return strconv.ParseBool(reply) + case nil: + return false, ErrNil + case error: + return false, reply + } + return false, fmt.Errorf("redis cluster: unexpected type for Bool, got type %T", reply) +} + +func Bytes(reply interface{}, err error) ([]byte, error) { + if err != nil { + return nil, err + } + switch reply := reply.(type) { + case []byte: + if len(reply) == 0 { + return nil, ErrNil + } + return reply, nil + case string: + data := []byte(reply) + if len(data) == 0 { + return nil, ErrNil + } + return data, nil + case nil: + return nil, ErrNil + case error: + return nil, reply + } + return nil, fmt.Errorf("redis cluster: unexpected type for Bytes, got type %T", reply) +} + +func String(reply interface{}, err error) (string, error) { + if err != nil { + return "", err + } + + value := "" + err = nil + switch v := reply.(type) { + case string: + if len(v) == 0 { + return "", ErrNil + } + + value = v + case []byte: + if len(v) == 0 { + return "", ErrNil + } + + value = string(v) + case int: + value = strconv.FormatInt(int64(v), 10) + case int8: + value = strconv.FormatInt(int64(v), 10) + case int16: + value = strconv.FormatInt(int64(v), 10) + case int32: + value = strconv.FormatInt(int64(v), 10) + case int64: + value = strconv.FormatInt(v, 10) + case uint: + value = strconv.FormatUint(uint64(v), 10) + case uint8: + value = strconv.FormatUint(uint64(v), 10) + case uint16: + value = strconv.FormatUint(uint64(v), 10) + case uint32: + value = strconv.FormatUint(uint64(v), 10) + case uint64: + value = strconv.FormatUint(v, 10) + case float32: + value = strconv.FormatFloat(float64(v), 'f', -1, 32) + case float64: + value = strconv.FormatFloat(v, 'f', -1, 64) + case bool: + value = strconv.FormatBool(v) + case nil: + err = ErrNil + case error: + err = v + default: + err = fmt.Errorf("redis cluster: unexpected type for String, got type %T", v) + } + + return value, err +} + +func Strings(reply interface{}, err error) ([]string, error) { + if err != nil { + return nil, err + } + switch reply := reply.(type) { + case []interface{}: + result := make([]string, len(reply)) + for i := range reply { + if reply[i] == nil { + continue + } + switch subReply := reply[i].(type) { + case string: + result[i] = subReply + case []byte: + result[i] = string(subReply) + default: + return nil, fmt.Errorf("redis cluster: unexpected element type for String, got type %T", reply[i]) + } + } + return result, nil + case []string: + return reply, nil + case nil: + return nil, ErrNil + case error: + return nil, reply + } + return nil, fmt.Errorf("redis cluster: unexpected type for Strings, got type %T", reply) +} + +func Values(reply interface{}, err error) ([]interface{}, error) { + if err != nil { + return nil, err + } + switch reply := reply.(type) { + case []interface{}: + return reply, nil + case nil: + return nil, ErrNil + case error: + return nil, reply + } + return nil, fmt.Errorf("redis cluster: unexpected type for Values, got type %T", reply) +} diff --git a/utils/cache/redis.go b/utils/cache/redis.go new file mode 100644 index 0000000..55d60a5 --- /dev/null +++ b/utils/cache/redis.go @@ -0,0 +1,413 @@ +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), + //redigo.DialDatabase(RedisDataBase), + ) + 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 SelectDb(db int) (interface{}, error) { + return Do("SELECT", db) +} + +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, decrBy)) +} + +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 +} + +func LPushMax(key string, data ...interface{}) (interface{}, error) { + // set + return Do("LPUSH", key, data) +} diff --git a/utils/cache/redis_cluster.go b/utils/cache/redis_cluster.go new file mode 100644 index 0000000..901f30c --- /dev/null +++ b/utils/cache/redis_cluster.go @@ -0,0 +1,622 @@ +package cache + +import ( + "strconv" + "time" + + "github.com/go-redis/redis" +) + +var pools *redis.ClusterClient + +func NewRedisCluster(addrs []string) error { + opt := &redis.ClusterOptions{ + Addrs: addrs, + PoolSize: redisPoolSize, + PoolTimeout: redisPoolTTL, + IdleTimeout: redisIdleTTL, + DialTimeout: redisDialTTL, + ReadTimeout: redisReadTTL, + WriteTimeout: redisWriteTTL, + } + pools = redis.NewClusterClient(opt) + if err := pools.Ping().Err(); err != nil { + return err + } + return nil +} + +func RCGet(key string) (interface{}, error) { + res, err := pools.Get(key).Result() + if err != nil { + return nil, convertError(err) + } + return []byte(res), nil +} +func RCSet(key string, value interface{}) error { + err := pools.Set(key, value, 0).Err() + return convertError(err) +} +func RCGetSet(key string, value interface{}) (interface{}, error) { + res, err := pools.GetSet(key, value).Result() + if err != nil { + return nil, convertError(err) + } + return []byte(res), nil +} +func RCSetNx(key string, value interface{}) (int64, error) { + res, err := pools.SetNX(key, value, 0).Result() + if err != nil { + return 0, convertError(err) + } + if res { + return 1, nil + } + return 0, nil +} +func RCSetEx(key string, value interface{}, timeout int64) error { + _, err := pools.Set(key, value, time.Duration(timeout)*time.Second).Result() + if err != nil { + return convertError(err) + } + return nil +} + +// nil表示成功,ErrNil表示数据库内已经存在这个key,其他表示数据库发生错误 +func RCSetNxEx(key string, value interface{}, timeout int64) error { + res, err := pools.SetNX(key, value, time.Duration(timeout)*time.Second).Result() + if err != nil { + return convertError(err) + } + if res { + return nil + } + return ErrNil +} +func RCMGet(keys ...string) ([]interface{}, error) { + res, err := pools.MGet(keys...).Result() + return res, convertError(err) +} + +// 为确保多个key映射到同一个slot,每个key最好加上hash tag,如:{test} +func RCMSet(kvs map[string]interface{}) error { + pairs := make([]string, 0, len(kvs)*2) + for k, v := range kvs { + val, err := String(v, nil) + if err != nil { + return err + } + pairs = append(pairs, k, val) + } + return convertError(pools.MSet(pairs).Err()) +} + +// 为确保多个key映射到同一个slot,每个key最好加上hash tag,如:{test} +func RCMSetNX(kvs map[string]interface{}) (bool, error) { + pairs := make([]string, 0, len(kvs)*2) + for k, v := range kvs { + val, err := String(v, nil) + if err != nil { + return false, err + } + pairs = append(pairs, k, val) + } + res, err := pools.MSetNX(pairs).Result() + return res, convertError(err) +} +func RCExpireAt(key string, timestamp int64) (int64, error) { + res, err := pools.ExpireAt(key, time.Unix(timestamp, 0)).Result() + if err != nil { + return 0, convertError(err) + } + if res { + return 1, nil + } + return 0, nil +} +func RCDel(keys ...string) (int64, error) { + args := make([]interface{}, 0, len(keys)) + for _, key := range keys { + args = append(args, key) + } + res, err := pools.Del(keys...).Result() + if err != nil { + return res, convertError(err) + } + return res, nil +} +func RCIncr(key string) (int64, error) { + res, err := pools.Incr(key).Result() + if err != nil { + return res, convertError(err) + } + return res, nil +} +func RCIncrBy(key string, delta int64) (int64, error) { + res, err := pools.IncrBy(key, delta).Result() + if err != nil { + return res, convertError(err) + } + return res, nil +} +func RCExpire(key string, duration int64) (int64, error) { + res, err := pools.Expire(key, time.Duration(duration)*time.Second).Result() + if err != nil { + return 0, convertError(err) + } + if res { + return 1, nil + } + return 0, nil +} +func RCExists(key string) (bool, error) { + res, err := pools.Exists(key).Result() + if err != nil { + return false, convertError(err) + } + if res > 0 { + return true, nil + } + return false, nil +} +func RCHGet(key string, field string) (interface{}, error) { + res, err := pools.HGet(key, field).Result() + if err != nil { + return nil, convertError(err) + } + return []byte(res), nil +} +func RCHLen(key string) (int64, error) { + res, err := pools.HLen(key).Result() + if err != nil { + return res, convertError(err) + } + return res, nil +} +func RCHSet(key string, field string, val interface{}) error { + value, err := String(val, nil) + if err != nil && err != ErrNil { + return err + } + _, err = pools.HSet(key, field, value).Result() + if err != nil { + return convertError(err) + } + return nil +} +func RCHDel(key string, fields ...string) (int64, error) { + args := make([]interface{}, 0, len(fields)+1) + args = append(args, key) + for _, field := range fields { + args = append(args, field) + } + res, err := pools.HDel(key, fields...).Result() + if err != nil { + return 0, convertError(err) + } + return res, nil +} + +func RCHMGet(key string, fields ...string) (interface{}, error) { + args := make([]interface{}, 0, len(fields)+1) + args = append(args, key) + for _, field := range fields { + args = append(args, field) + } + if len(fields) == 0 { + return nil, ErrNil + } + res, err := pools.HMGet(key, fields...).Result() + if err != nil { + return nil, convertError(err) + } + return res, nil +} +func RCHMSet(key string, kvs ...interface{}) error { + if len(kvs) == 0 { + return nil + } + if len(kvs)%2 != 0 { + return ErrWrongArgsNum + } + var err error + v := map[string]interface{}{} // todo change + v["field"], err = String(kvs[0], nil) + if err != nil && err != ErrNil { + return err + } + v["value"], err = String(kvs[1], nil) + if err != nil && err != ErrNil { + return err + } + pairs := make([]string, 0, len(kvs)-2) + if len(kvs) > 2 { + for _, kv := range kvs[2:] { + kvString, err := String(kv, nil) + if err != nil && err != ErrNil { + return err + } + pairs = append(pairs, kvString) + } + } + v["paris"] = pairs + _, err = pools.HMSet(key, v).Result() + if err != nil { + return convertError(err) + } + return nil +} + +func RCHKeys(key string) ([]string, error) { + res, err := pools.HKeys(key).Result() + if err != nil { + return res, convertError(err) + } + return res, nil +} +func RCHVals(key string) ([]interface{}, error) { + res, err := pools.HVals(key).Result() + if err != nil { + return nil, convertError(err) + } + rs := make([]interface{}, 0, len(res)) + for _, res := range res { + rs = append(rs, res) + } + return rs, nil +} +func RCHGetAll(key string) (map[string]string, error) { + vals, err := pools.HGetAll(key).Result() + if err != nil { + return nil, convertError(err) + } + return vals, nil +} +func RCHIncrBy(key, field string, delta int64) (int64, error) { + res, err := pools.HIncrBy(key, field, delta).Result() + if err != nil { + return res, convertError(err) + } + return res, nil +} +func RCZAdd(key string, kvs ...interface{}) (int64, error) { + args := make([]interface{}, 0, len(kvs)+1) + args = append(args, key) + args = append(args, kvs...) + if len(kvs) == 0 { + return 0, nil + } + if len(kvs)%2 != 0 { + return 0, ErrWrongArgsNum + } + zs := make([]redis.Z, len(kvs)/2) + for i := 0; i < len(kvs); i += 2 { + idx := i / 2 + score, err := Float64(kvs[i], nil) + if err != nil && err != ErrNil { + return 0, err + } + zs[idx].Score = score + zs[idx].Member = kvs[i+1] + } + res, err := pools.ZAdd(key, zs...).Result() + if err != nil { + return res, convertError(err) + } + return res, nil +} +func RCZRem(key string, members ...string) (int64, error) { + args := make([]interface{}, 0, len(members)) + args = append(args, key) + for _, member := range members { + args = append(args, member) + } + res, err := pools.ZRem(key, members).Result() + if err != nil { + return res, convertError(err) + } + return res, err +} + +func RCZRange(key string, min, max int64, withScores bool) (interface{}, error) { + res := make([]interface{}, 0) + if withScores { + zs, err := pools.ZRangeWithScores(key, min, max).Result() + if err != nil { + return nil, convertError(err) + } + for _, z := range zs { + res = append(res, z.Member, strconv.FormatFloat(z.Score, 'f', -1, 64)) + } + } else { + ms, err := pools.ZRange(key, min, max).Result() + if err != nil { + return nil, convertError(err) + } + for _, m := range ms { + res = append(res, m) + } + } + return res, nil +} +func RCZRangeByScoreWithScore(key string, min, max int64) (map[string]int64, error) { + opt := new(redis.ZRangeBy) + opt.Min = strconv.FormatInt(int64(min), 10) + opt.Max = strconv.FormatInt(int64(max), 10) + opt.Count = -1 + opt.Offset = 0 + vals, err := pools.ZRangeByScoreWithScores(key, *opt).Result() + if err != nil { + return nil, convertError(err) + } + res := make(map[string]int64, len(vals)) + for _, val := range vals { + key, err := String(val.Member, nil) + if err != nil && err != ErrNil { + return nil, err + } + res[key] = int64(val.Score) + } + return res, nil +} +func RCLRange(key string, start, stop int64) (interface{}, error) { + res, err := pools.LRange(key, start, stop).Result() + if err != nil { + return nil, convertError(err) + } + return res, nil +} +func RCLSet(key string, index int, value interface{}) error { + err := pools.LSet(key, int64(index), value).Err() + return convertError(err) +} +func RCLLen(key string) (int64, error) { + res, err := pools.LLen(key).Result() + if err != nil { + return res, convertError(err) + } + return res, nil +} +func RCLRem(key string, count int, value interface{}) (int, error) { + val, _ := value.(string) + res, err := pools.LRem(key, int64(count), val).Result() + if err != nil { + return int(res), convertError(err) + } + return int(res), nil +} +func RCTTl(key string) (int64, error) { + duration, err := pools.TTL(key).Result() + if err != nil { + return int64(duration.Seconds()), convertError(err) + } + return int64(duration.Seconds()), nil +} +func RCLPop(key string) (interface{}, error) { + res, err := pools.LPop(key).Result() + if err != nil { + return nil, convertError(err) + } + return res, nil +} +func RCRPop(key string) (interface{}, error) { + res, err := pools.RPop(key).Result() + if err != nil { + return nil, convertError(err) + } + return res, nil +} +func RCBLPop(key string, timeout int) (interface{}, error) { + res, err := pools.BLPop(time.Duration(timeout)*time.Second, key).Result() + if err != nil { + // 兼容redis 2.x + if err == redis.Nil { + return nil, ErrNil + } + return nil, err + } + return res[1], nil +} +func RCBRPop(key string, timeout int) (interface{}, error) { + res, err := pools.BRPop(time.Duration(timeout)*time.Second, key).Result() + if err != nil { + // 兼容redis 2.x + if err == redis.Nil { + return nil, ErrNil + } + return nil, convertError(err) + } + return res[1], nil +} +func RCLPush(key string, value ...interface{}) error { + args := make([]interface{}, 0, len(value)+1) + args = append(args, key) + args = append(args, value...) + vals := make([]string, 0, len(value)) + for _, v := range value { + val, err := String(v, nil) + if err != nil && err != ErrNil { + return err + } + vals = append(vals, val) + } + _, err := pools.LPush(key, vals).Result() // todo ... + if err != nil { + return convertError(err) + } + return nil +} +func RCRPush(key string, value ...interface{}) error { + args := make([]interface{}, 0, len(value)+1) + args = append(args, key) + args = append(args, value...) + vals := make([]string, 0, len(value)) + for _, v := range value { + val, err := String(v, nil) + if err != nil && err != ErrNil { + if err == ErrNil { + continue + } + return err + } + if val == "" { + continue + } + vals = append(vals, val) + } + _, err := pools.RPush(key, vals).Result() // todo ... + if err != nil { + return convertError(err) + } + return nil +} + +// 为确保srcKey跟destKey映射到同一个slot,srcKey和destKey需要加上hash tag,如:{test} +func RCBRPopLPush(srcKey string, destKey string, timeout int) (interface{}, error) { + res, err := pools.BRPopLPush(srcKey, destKey, time.Duration(timeout)*time.Second).Result() + if err != nil { + return nil, convertError(err) + } + return res, nil +} + +// 为确保srcKey跟destKey映射到同一个slot,srcKey和destKey需要加上hash tag,如:{test} +func RCRPopLPush(srcKey string, destKey string) (interface{}, error) { + res, err := pools.RPopLPush(srcKey, destKey).Result() + if err != nil { + return nil, convertError(err) + } + return res, nil +} +func RCSAdd(key string, members ...interface{}) (int64, error) { + args := make([]interface{}, 0, len(members)+1) + args = append(args, key) + args = append(args, members...) + ms := make([]string, 0, len(members)) + for _, member := range members { + m, err := String(member, nil) + if err != nil && err != ErrNil { + return 0, err + } + ms = append(ms, m) + } + res, err := pools.SAdd(key, ms).Result() // todo ... + if err != nil { + return res, convertError(err) + } + return res, nil +} +func RCSPop(key string) ([]byte, error) { + res, err := pools.SPop(key).Result() + if err != nil { + return nil, convertError(err) + } + return []byte(res), nil +} +func RCSIsMember(key string, member interface{}) (bool, error) { + m, _ := member.(string) + res, err := pools.SIsMember(key, m).Result() + if err != nil { + return res, convertError(err) + } + return res, nil +} +func RCSRem(key string, members ...interface{}) (int64, error) { + args := make([]interface{}, 0, len(members)+1) + args = append(args, key) + args = append(args, members...) + ms := make([]string, 0, len(members)) + for _, member := range members { + m, err := String(member, nil) + if err != nil && err != ErrNil { + return 0, err + } + ms = append(ms, m) + } + res, err := pools.SRem(key, ms).Result() // todo ... + if err != nil { + return res, convertError(err) + } + return res, nil +} +func RCSMembers(key string) ([]string, error) { + res, err := pools.SMembers(key).Result() + if err != nil { + return nil, convertError(err) + } + return res, nil +} +func RCScriptLoad(luaScript string) (interface{}, error) { + res, err := pools.ScriptLoad(luaScript).Result() + if err != nil { + return nil, convertError(err) + } + return res, nil +} +func RCEvalSha(sha1 string, numberKeys int, keysArgs ...interface{}) (interface{}, error) { + vals := make([]interface{}, 0, len(keysArgs)+2) + vals = append(vals, sha1, numberKeys) + vals = append(vals, keysArgs...) + keys := make([]string, 0, numberKeys) + args := make([]string, 0, len(keysArgs)-numberKeys) + for i, value := range keysArgs { + val, err := String(value, nil) + if err != nil && err != ErrNil { + return nil, err + } + if i < numberKeys { + keys = append(keys, val) + } else { + args = append(args, val) + } + } + res, err := pools.EvalSha(sha1, keys, args).Result() + if err != nil { + return nil, convertError(err) + } + return res, nil +} +func RCEval(luaScript string, numberKeys int, keysArgs ...interface{}) (interface{}, error) { + vals := make([]interface{}, 0, len(keysArgs)+2) + vals = append(vals, luaScript, numberKeys) + vals = append(vals, keysArgs...) + keys := make([]string, 0, numberKeys) + args := make([]string, 0, len(keysArgs)-numberKeys) + for i, value := range keysArgs { + val, err := String(value, nil) + if err != nil && err != ErrNil { + return nil, err + } + if i < numberKeys { + keys = append(keys, val) + } else { + args = append(args, val) + } + } + res, err := pools.Eval(luaScript, keys, args).Result() + if err != nil { + return nil, convertError(err) + } + return res, nil +} +func RCGetBit(key string, offset int64) (int64, error) { + res, err := pools.GetBit(key, offset).Result() + if err != nil { + return res, convertError(err) + } + return res, nil +} +func RCSetBit(key string, offset uint32, value int) (int, error) { + res, err := pools.SetBit(key, int64(offset), value).Result() + return int(res), convertError(err) +} +func RCGetClient() *redis.ClusterClient { + return pools +} +func convertError(err error) error { + if err == redis.Nil { + // 为了兼容redis 2.x,这里不返回 ErrNil,ErrNil在调用redis_cluster_reply函数时才返回 + return nil + } + return err +} diff --git a/utils/cache/redis_pool.go b/utils/cache/redis_pool.go new file mode 100644 index 0000000..ca38b3f --- /dev/null +++ b/utils/cache/redis_pool.go @@ -0,0 +1,324 @@ +package cache + +import ( + "errors" + "log" + "strings" + "time" + + redigo "github.com/gomodule/redigo/redis" +) + +type RedisPool struct { + *redigo.Pool +} + +func NewRedisPool(cfg *Config) *RedisPool { + return &RedisPool{&redigo.Pool{ + MaxIdle: cfg.MaxIdle, + IdleTimeout: cfg.IdleTimeout, + MaxActive: cfg.MaxActive, + Wait: cfg.Wait, + Dial: func() (redigo.Conn, error) { + c, err := redigo.Dial("tcp", cfg.Server) + if err != nil { + log.Println("Redis Dial failed: ", err) + return nil, err + } + if cfg.Password != "" { + if _, err := c.Do("AUTH", cfg.Password); err != nil { + c.Close() + log.Println("Redis AUTH failed: ", err) + return nil, err + } + } + return c, err + }, + TestOnBorrow: func(c redigo.Conn, t time.Time) error { + _, err := c.Do("PING") + if err != nil { + log.Println("Unable to ping to redis server:", err) + } + return err + }, + }} +} + +func (p *RedisPool) Do(cmd string, args ...interface{}) (reply interface{}, err error) { + conn := pool.Get() + defer conn.Close() + return conn.Do(cmd, args...) +} + +func (p *RedisPool) GetPool() *redigo.Pool { + return pool +} + +func (p *RedisPool) ParseKey(key string, vars []string) (string, error) { + arr := strings.Split(key, conf.KeyPlaceholder) + actualKey := "" + if len(arr) != len(vars)+1 { + return "", errors.New("redis/connection.go: Insufficient arguments to parse key") + } else { + for index, val := range arr { + if index == 0 { + actualKey = arr[index] + } else { + actualKey += vars[index-1] + val + } + } + } + return getPrefixedKey(actualKey), nil +} + +func (p *RedisPool) getPrefixedKey(key string) string { + return conf.KeyPrefix + conf.KeyDelimiter + key +} +func (p *RedisPool) StripEnvKey(key string) string { + return strings.TrimLeft(key, conf.KeyPrefix+conf.KeyDelimiter) +} +func (p *RedisPool) SplitKey(key string) []string { + return strings.Split(key, conf.KeyDelimiter) +} +func (p *RedisPool) Expire(key string, ttl int) (interface{}, error) { + return Do("EXPIRE", key, ttl) +} +func (p *RedisPool) Persist(key string) (interface{}, error) { + return Do("PERSIST", key) +} + +func (p *RedisPool) Del(key string) (interface{}, error) { + return Do("DEL", key) +} +func (p *RedisPool) Set(key string, data interface{}) (interface{}, error) { + // set + return Do("SET", key, data) +} +func (p *RedisPool) SetNX(key string, data interface{}) (interface{}, error) { + return Do("SETNX", key, data) +} +func (p *RedisPool) SetEx(key string, data interface{}, ttl int) (interface{}, error) { + return Do("SETEX", key, ttl, data) +} +func (p *RedisPool) Get(key string) (interface{}, error) { + // get + return Do("GET", key) +} +func (p *RedisPool) GetStringMap(key string) (map[string]string, error) { + // get + return redigo.StringMap(Do("GET", key)) +} + +func (p *RedisPool) GetTTL(key string) (time.Duration, error) { + ttl, err := redigo.Int64(Do("TTL", key)) + return time.Duration(ttl) * time.Second, err +} +func (p *RedisPool) GetBytes(key string) ([]byte, error) { + return redigo.Bytes(Do("GET", key)) +} +func (p *RedisPool) GetString(key string) (string, error) { + return redigo.String(Do("GET", key)) +} +func (p *RedisPool) GetInt(key string) (int, error) { + return redigo.Int(Do("GET", key)) +} +func (p *RedisPool) GetStringLength(key string) (int, error) { + return redigo.Int(Do("STRLEN", key)) +} +func (p *RedisPool) ZAdd(key string, score float64, data interface{}) (interface{}, error) { + return Do("ZADD", key, score, data) +} +func (p *RedisPool) ZRem(key string, data interface{}) (interface{}, error) { + return Do("ZREM", key, data) +} +func (p *RedisPool) ZRange(key string, start int, end int, withScores bool) ([]interface{}, error) { + if withScores { + return redigo.Values(Do("ZRANGE", key, start, end, "WITHSCORES")) + } + return redigo.Values(Do("ZRANGE", key, start, end)) +} +func (p *RedisPool) SAdd(setName string, data interface{}) (interface{}, error) { + return Do("SADD", setName, data) +} +func (p *RedisPool) SCard(setName string) (int64, error) { + return redigo.Int64(Do("SCARD", setName)) +} +func (p *RedisPool) SIsMember(setName string, data interface{}) (bool, error) { + return redigo.Bool(Do("SISMEMBER", setName, data)) +} +func (p *RedisPool) SMembers(setName string) ([]string, error) { + return redigo.Strings(Do("SMEMBERS", setName)) +} +func (p *RedisPool) SRem(setName string, data interface{}) (interface{}, error) { + return Do("SREM", setName, data) +} +func (p *RedisPool) HSet(key string, HKey string, data interface{}) (interface{}, error) { + return Do("HSET", key, HKey, data) +} + +func (p *RedisPool) HGet(key string, HKey string) (interface{}, error) { + return Do("HGET", key, HKey) +} + +func (p *RedisPool) HMGet(key string, hashKeys ...string) ([]interface{}, error) { + ret, err := Do("HMGET", key, hashKeys) + if err != nil { + return nil, err + } + reta, ok := ret.([]interface{}) + if !ok { + return nil, errors.New("result not an array") + } + return reta, nil +} + +func (p *RedisPool) HMSet(key string, hashKeys []string, vals []interface{}) (interface{}, error) { + if len(hashKeys) == 0 || len(hashKeys) != len(vals) { + var ret interface{} + return ret, errors.New("bad length") + } + input := []interface{}{key} + for i, v := range hashKeys { + input = append(input, v, vals[i]) + } + return Do("HMSET", input...) +} + +func (p *RedisPool) HGetString(key string, HKey string) (string, error) { + return redigo.String(Do("HGET", key, HKey)) +} +func (p *RedisPool) HGetFloat(key string, HKey string) (float64, error) { + f, err := redigo.Float64(Do("HGET", key, HKey)) + return float64(f), err +} +func (p *RedisPool) HGetInt(key string, HKey string) (int, error) { + return redigo.Int(Do("HGET", key, HKey)) +} +func (p *RedisPool) HGetInt64(key string, HKey string) (int64, error) { + return redigo.Int64(Do("HGET", key, HKey)) +} +func (p *RedisPool) HGetBool(key string, HKey string) (bool, error) { + return redigo.Bool(Do("HGET", key, HKey)) +} +func (p *RedisPool) HDel(key string, HKey string) (interface{}, error) { + return Do("HDEL", key, HKey) +} +func (p *RedisPool) HGetAll(key string) (map[string]interface{}, error) { + vals, err := redigo.Values(Do("HGETALL", key)) + if err != nil { + return nil, err + } + num := len(vals) / 2 + result := make(map[string]interface{}, num) + for i := 0; i < num; i++ { + key, _ := redigo.String(vals[2*i], nil) + result[key] = vals[2*i+1] + } + return result, nil +} + +// NOTE: Use this in production environment with extreme care. +// Read more here:https://redigo.io/commands/keys +func (p *RedisPool) Keys(pattern string) ([]string, error) { + return redigo.Strings(Do("KEYS", pattern)) +} + +func (p *RedisPool) HKeys(key string) ([]string, error) { + return redigo.Strings(Do("HKEYS", key)) +} + +func (p *RedisPool) Exists(key string) (bool, error) { + count, err := redigo.Int(Do("EXISTS", key)) + if count == 0 { + return false, err + } else { + return true, err + } +} + +func (p *RedisPool) Incr(key string) (int64, error) { + return redigo.Int64(Do("INCR", key)) +} + +func (p *RedisPool) Decr(key string) (int64, error) { + return redigo.Int64(Do("DECR", key)) +} + +func (p *RedisPool) IncrBy(key string, incBy int64) (int64, error) { + return redigo.Int64(Do("INCRBY", key, incBy)) +} + +func (p *RedisPool) DecrBy(key string, decrBy int64) (int64, error) { + return redigo.Int64(Do("DECRBY", key)) +} + +func (p *RedisPool) IncrByFloat(key string, incBy float64) (float64, error) { + return redigo.Float64(Do("INCRBYFLOAT", key, incBy)) +} + +func (p *RedisPool) DecrByFloat(key string, decrBy float64) (float64, error) { + return redigo.Float64(Do("DECRBYFLOAT", key, decrBy)) +} + +// use for message queue +func (p *RedisPool) LPush(key string, data interface{}) (interface{}, error) { + // set + return Do("LPUSH", key, data) +} + +func (p *RedisPool) LPop(key string) (interface{}, error) { + return Do("LPOP", key) +} + +func (p *RedisPool) LPopString(key string) (string, error) { + return redigo.String(Do("LPOP", key)) +} +func (p *RedisPool) LPopFloat(key string) (float64, error) { + f, err := redigo.Float64(Do("LPOP", key)) + return float64(f), err +} +func (p *RedisPool) LPopInt(key string) (int, error) { + return redigo.Int(Do("LPOP", key)) +} +func (p *RedisPool) LPopInt64(key string) (int64, error) { + return redigo.Int64(Do("LPOP", key)) +} + +func (p *RedisPool) RPush(key string, data interface{}) (interface{}, error) { + // set + return Do("RPUSH", key, data) +} + +func (p *RedisPool) RPop(key string) (interface{}, error) { + return Do("RPOP", key) +} + +func (p *RedisPool) RPopString(key string) (string, error) { + return redigo.String(Do("RPOP", key)) +} +func (p *RedisPool) RPopFloat(key string) (float64, error) { + f, err := redigo.Float64(Do("RPOP", key)) + return float64(f), err +} +func (p *RedisPool) RPopInt(key string) (int, error) { + return redigo.Int(Do("RPOP", key)) +} +func (p *RedisPool) RPopInt64(key string) (int64, error) { + return redigo.Int64(Do("RPOP", key)) +} + +func (p *RedisPool) Scan(cursor int64, pattern string, count int64) (int64, []string, error) { + var items []string + var newCursor int64 + + values, err := redigo.Values(Do("SCAN", cursor, "MATCH", pattern, "COUNT", count)) + if err != nil { + return 0, nil, err + } + values, err = redigo.Scan(values, &newCursor, &items) + if err != nil { + return 0, nil, err + } + + return newCursor, items, nil +} diff --git a/utils/cache/redis_pool_cluster.go b/utils/cache/redis_pool_cluster.go new file mode 100644 index 0000000..cd1911b --- /dev/null +++ b/utils/cache/redis_pool_cluster.go @@ -0,0 +1,617 @@ +package cache + +import ( + "strconv" + "time" + + "github.com/go-redis/redis" +) + +type RedisClusterPool struct { + client *redis.ClusterClient +} + +func NewRedisClusterPool(addrs []string) (*RedisClusterPool, error) { + opt := &redis.ClusterOptions{ + Addrs: addrs, + PoolSize: 512, + PoolTimeout: 10 * time.Second, + IdleTimeout: 10 * time.Second, + DialTimeout: 10 * time.Second, + ReadTimeout: 3 * time.Second, + WriteTimeout: 3 * time.Second, + } + c := redis.NewClusterClient(opt) + if err := c.Ping().Err(); err != nil { + return nil, err + } + return &RedisClusterPool{client: c}, nil +} + +func (p *RedisClusterPool) Get(key string) (interface{}, error) { + res, err := p.client.Get(key).Result() + if err != nil { + return nil, convertError(err) + } + return []byte(res), nil +} +func (p *RedisClusterPool) Set(key string, value interface{}) error { + err := p.client.Set(key, value, 0).Err() + return convertError(err) +} +func (p *RedisClusterPool) GetSet(key string, value interface{}) (interface{}, error) { + res, err := p.client.GetSet(key, value).Result() + if err != nil { + return nil, convertError(err) + } + return []byte(res), nil +} +func (p *RedisClusterPool) SetNx(key string, value interface{}) (int64, error) { + res, err := p.client.SetNX(key, value, 0).Result() + if err != nil { + return 0, convertError(err) + } + if res { + return 1, nil + } + return 0, nil +} +func (p *RedisClusterPool) SetEx(key string, value interface{}, timeout int64) error { + _, err := p.client.Set(key, value, time.Duration(timeout)*time.Second).Result() + if err != nil { + return convertError(err) + } + return nil +} + +// nil表示成功,ErrNil表示数据库内已经存在这个key,其他表示数据库发生错误 +func (p *RedisClusterPool) SetNxEx(key string, value interface{}, timeout int64) error { + res, err := p.client.SetNX(key, value, time.Duration(timeout)*time.Second).Result() + if err != nil { + return convertError(err) + } + if res { + return nil + } + return ErrNil +} +func (p *RedisClusterPool) MGet(keys ...string) ([]interface{}, error) { + res, err := p.client.MGet(keys...).Result() + return res, convertError(err) +} + +// 为确保多个key映射到同一个slot,每个key最好加上hash tag,如:{test} +func (p *RedisClusterPool) MSet(kvs map[string]interface{}) error { + pairs := make([]string, 0, len(kvs)*2) + for k, v := range kvs { + val, err := String(v, nil) + if err != nil { + return err + } + pairs = append(pairs, k, val) + } + return convertError(p.client.MSet(pairs).Err()) +} + +// 为确保多个key映射到同一个slot,每个key最好加上hash tag,如:{test} +func (p *RedisClusterPool) MSetNX(kvs map[string]interface{}) (bool, error) { + pairs := make([]string, 0, len(kvs)*2) + for k, v := range kvs { + val, err := String(v, nil) + if err != nil { + return false, err + } + pairs = append(pairs, k, val) + } + res, err := p.client.MSetNX(pairs).Result() + return res, convertError(err) +} +func (p *RedisClusterPool) ExpireAt(key string, timestamp int64) (int64, error) { + res, err := p.client.ExpireAt(key, time.Unix(timestamp, 0)).Result() + if err != nil { + return 0, convertError(err) + } + if res { + return 1, nil + } + return 0, nil +} +func (p *RedisClusterPool) Del(keys ...string) (int64, error) { + args := make([]interface{}, 0, len(keys)) + for _, key := range keys { + args = append(args, key) + } + res, err := p.client.Del(keys...).Result() + if err != nil { + return res, convertError(err) + } + return res, nil +} +func (p *RedisClusterPool) Incr(key string) (int64, error) { + res, err := p.client.Incr(key).Result() + if err != nil { + return res, convertError(err) + } + return res, nil +} +func (p *RedisClusterPool) IncrBy(key string, delta int64) (int64, error) { + res, err := p.client.IncrBy(key, delta).Result() + if err != nil { + return res, convertError(err) + } + return res, nil +} +func (p *RedisClusterPool) Expire(key string, duration int64) (int64, error) { + res, err := p.client.Expire(key, time.Duration(duration)*time.Second).Result() + if err != nil { + return 0, convertError(err) + } + if res { + return 1, nil + } + return 0, nil +} +func (p *RedisClusterPool) Exists(key string) (bool, error) { // todo (bool, error) + res, err := p.client.Exists(key).Result() + if err != nil { + return false, convertError(err) + } + if res > 0 { + return true, nil + } + return false, nil +} +func (p *RedisClusterPool) HGet(key string, field string) (interface{}, error) { + res, err := p.client.HGet(key, field).Result() + if err != nil { + return nil, convertError(err) + } + return []byte(res), nil +} +func (p *RedisClusterPool) HLen(key string) (int64, error) { + res, err := p.client.HLen(key).Result() + if err != nil { + return res, convertError(err) + } + return res, nil +} +func (p *RedisClusterPool) HSet(key string, field string, val interface{}) error { + value, err := String(val, nil) + if err != nil && err != ErrNil { + return err + } + _, err = p.client.HSet(key, field, value).Result() + if err != nil { + return convertError(err) + } + return nil +} +func (p *RedisClusterPool) HDel(key string, fields ...string) (int64, error) { + args := make([]interface{}, 0, len(fields)+1) + args = append(args, key) + for _, field := range fields { + args = append(args, field) + } + res, err := p.client.HDel(key, fields...).Result() + if err != nil { + return 0, convertError(err) + } + return res, nil +} + +func (p *RedisClusterPool) HMGet(key string, fields ...string) (interface{}, error) { + args := make([]interface{}, 0, len(fields)+1) + args = append(args, key) + for _, field := range fields { + args = append(args, field) + } + if len(fields) == 0 { + return nil, ErrNil + } + res, err := p.client.HMGet(key, fields...).Result() + if err != nil { + return nil, convertError(err) + } + return res, nil +} +func (p *RedisClusterPool) HMSet(key string, kvs ...interface{}) error { + if len(kvs) == 0 { + return nil + } + if len(kvs)%2 != 0 { + return ErrWrongArgsNum + } + var err error + v := map[string]interface{}{} // todo change + v["field"], err = String(kvs[0], nil) + if err != nil && err != ErrNil { + return err + } + v["value"], err = String(kvs[1], nil) + if err != nil && err != ErrNil { + return err + } + pairs := make([]string, 0, len(kvs)-2) + if len(kvs) > 2 { + for _, kv := range kvs[2:] { + kvString, err := String(kv, nil) + if err != nil && err != ErrNil { + return err + } + pairs = append(pairs, kvString) + } + } + v["paris"] = pairs + _, err = p.client.HMSet(key, v).Result() + if err != nil { + return convertError(err) + } + return nil +} + +func (p *RedisClusterPool) HKeys(key string) ([]string, error) { + res, err := p.client.HKeys(key).Result() + if err != nil { + return res, convertError(err) + } + return res, nil +} +func (p *RedisClusterPool) HVals(key string) ([]interface{}, error) { + res, err := p.client.HVals(key).Result() + if err != nil { + return nil, convertError(err) + } + rs := make([]interface{}, 0, len(res)) + for _, res := range res { + rs = append(rs, res) + } + return rs, nil +} +func (p *RedisClusterPool) HGetAll(key string) (map[string]string, error) { + vals, err := p.client.HGetAll(key).Result() + if err != nil { + return nil, convertError(err) + } + return vals, nil +} +func (p *RedisClusterPool) HIncrBy(key, field string, delta int64) (int64, error) { + res, err := p.client.HIncrBy(key, field, delta).Result() + if err != nil { + return res, convertError(err) + } + return res, nil +} +func (p *RedisClusterPool) ZAdd(key string, kvs ...interface{}) (int64, error) { + args := make([]interface{}, 0, len(kvs)+1) + args = append(args, key) + args = append(args, kvs...) + if len(kvs) == 0 { + return 0, nil + } + if len(kvs)%2 != 0 { + return 0, ErrWrongArgsNum + } + zs := make([]redis.Z, len(kvs)/2) + for i := 0; i < len(kvs); i += 2 { + idx := i / 2 + score, err := Float64(kvs[i], nil) + if err != nil && err != ErrNil { + return 0, err + } + zs[idx].Score = score + zs[idx].Member = kvs[i+1] + } + res, err := p.client.ZAdd(key, zs...).Result() + if err != nil { + return res, convertError(err) + } + return res, nil +} +func (p *RedisClusterPool) ZRem(key string, members ...string) (int64, error) { + args := make([]interface{}, 0, len(members)) + args = append(args, key) + for _, member := range members { + args = append(args, member) + } + res, err := p.client.ZRem(key, members).Result() + if err != nil { + return res, convertError(err) + } + return res, err +} + +func (p *RedisClusterPool) ZRange(key string, min, max int64, withScores bool) (interface{}, error) { + res := make([]interface{}, 0) + if withScores { + zs, err := p.client.ZRangeWithScores(key, min, max).Result() + if err != nil { + return nil, convertError(err) + } + for _, z := range zs { + res = append(res, z.Member, strconv.FormatFloat(z.Score, 'f', -1, 64)) + } + } else { + ms, err := p.client.ZRange(key, min, max).Result() + if err != nil { + return nil, convertError(err) + } + for _, m := range ms { + res = append(res, m) + } + } + return res, nil +} +func (p *RedisClusterPool) ZRangeByScoreWithScore(key string, min, max int64) (map[string]int64, error) { + opt := new(redis.ZRangeBy) + opt.Min = strconv.FormatInt(int64(min), 10) + opt.Max = strconv.FormatInt(int64(max), 10) + opt.Count = -1 + opt.Offset = 0 + vals, err := p.client.ZRangeByScoreWithScores(key, *opt).Result() + if err != nil { + return nil, convertError(err) + } + res := make(map[string]int64, len(vals)) + for _, val := range vals { + key, err := String(val.Member, nil) + if err != nil && err != ErrNil { + return nil, err + } + res[key] = int64(val.Score) + } + return res, nil +} +func (p *RedisClusterPool) LRange(key string, start, stop int64) (interface{}, error) { + res, err := p.client.LRange(key, start, stop).Result() + if err != nil { + return nil, convertError(err) + } + return res, nil +} +func (p *RedisClusterPool) LSet(key string, index int, value interface{}) error { + err := p.client.LSet(key, int64(index), value).Err() + return convertError(err) +} +func (p *RedisClusterPool) LLen(key string) (int64, error) { + res, err := p.client.LLen(key).Result() + if err != nil { + return res, convertError(err) + } + return res, nil +} +func (p *RedisClusterPool) LRem(key string, count int, value interface{}) (int, error) { + val, _ := value.(string) + res, err := p.client.LRem(key, int64(count), val).Result() + if err != nil { + return int(res), convertError(err) + } + return int(res), nil +} +func (p *RedisClusterPool) TTl(key string) (int64, error) { + duration, err := p.client.TTL(key).Result() + if err != nil { + return int64(duration.Seconds()), convertError(err) + } + return int64(duration.Seconds()), nil +} +func (p *RedisClusterPool) LPop(key string) (interface{}, error) { + res, err := p.client.LPop(key).Result() + if err != nil { + return nil, convertError(err) + } + return res, nil +} +func (p *RedisClusterPool) RPop(key string) (interface{}, error) { + res, err := p.client.RPop(key).Result() + if err != nil { + return nil, convertError(err) + } + return res, nil +} +func (p *RedisClusterPool) BLPop(key string, timeout int) (interface{}, error) { + res, err := p.client.BLPop(time.Duration(timeout)*time.Second, key).Result() + if err != nil { + // 兼容redis 2.x + if err == redis.Nil { + return nil, ErrNil + } + return nil, err + } + return res[1], nil +} +func (p *RedisClusterPool) BRPop(key string, timeout int) (interface{}, error) { + res, err := p.client.BRPop(time.Duration(timeout)*time.Second, key).Result() + if err != nil { + // 兼容redis 2.x + if err == redis.Nil { + return nil, ErrNil + } + return nil, convertError(err) + } + return res[1], nil +} +func (p *RedisClusterPool) LPush(key string, value ...interface{}) error { + args := make([]interface{}, 0, len(value)+1) + args = append(args, key) + args = append(args, value...) + vals := make([]string, 0, len(value)) + for _, v := range value { + val, err := String(v, nil) + if err != nil && err != ErrNil { + return err + } + vals = append(vals, val) + } + _, err := p.client.LPush(key, vals).Result() // todo ... + if err != nil { + return convertError(err) + } + return nil +} +func (p *RedisClusterPool) RPush(key string, value ...interface{}) error { + args := make([]interface{}, 0, len(value)+1) + args = append(args, key) + args = append(args, value...) + vals := make([]string, 0, len(value)) + for _, v := range value { + val, err := String(v, nil) + if err != nil && err != ErrNil { + if err == ErrNil { + continue + } + return err + } + if val == "" { + continue + } + vals = append(vals, val) + } + _, err := p.client.RPush(key, vals).Result() // todo ... + if err != nil { + return convertError(err) + } + return nil +} + +// 为确保srcKey跟destKey映射到同一个slot,srcKey和destKey需要加上hash tag,如:{test} +func (p *RedisClusterPool) BRPopLPush(srcKey string, destKey string, timeout int) (interface{}, error) { + res, err := p.client.BRPopLPush(srcKey, destKey, time.Duration(timeout)*time.Second).Result() + if err != nil { + return nil, convertError(err) + } + return res, nil +} + +// 为确保srcKey跟destKey映射到同一个slot,srcKey和destKey需要加上hash tag,如:{test} +func (p *RedisClusterPool) RPopLPush(srcKey string, destKey string) (interface{}, error) { + res, err := p.client.RPopLPush(srcKey, destKey).Result() + if err != nil { + return nil, convertError(err) + } + return res, nil +} +func (p *RedisClusterPool) SAdd(key string, members ...interface{}) (int64, error) { + args := make([]interface{}, 0, len(members)+1) + args = append(args, key) + args = append(args, members...) + ms := make([]string, 0, len(members)) + for _, member := range members { + m, err := String(member, nil) + if err != nil && err != ErrNil { + return 0, err + } + ms = append(ms, m) + } + res, err := p.client.SAdd(key, ms).Result() // todo ... + if err != nil { + return res, convertError(err) + } + return res, nil +} +func (p *RedisClusterPool) SPop(key string) ([]byte, error) { + res, err := p.client.SPop(key).Result() + if err != nil { + return nil, convertError(err) + } + return []byte(res), nil +} +func (p *RedisClusterPool) SIsMember(key string, member interface{}) (bool, error) { + m, _ := member.(string) + res, err := p.client.SIsMember(key, m).Result() + if err != nil { + return res, convertError(err) + } + return res, nil +} +func (p *RedisClusterPool) SRem(key string, members ...interface{}) (int64, error) { + args := make([]interface{}, 0, len(members)+1) + args = append(args, key) + args = append(args, members...) + ms := make([]string, 0, len(members)) + for _, member := range members { + m, err := String(member, nil) + if err != nil && err != ErrNil { + return 0, err + } + ms = append(ms, m) + } + res, err := p.client.SRem(key, ms).Result() // todo ... + if err != nil { + return res, convertError(err) + } + return res, nil +} +func (p *RedisClusterPool) SMembers(key string) ([]string, error) { + res, err := p.client.SMembers(key).Result() + if err != nil { + return nil, convertError(err) + } + return res, nil +} +func (p *RedisClusterPool) ScriptLoad(luaScript string) (interface{}, error) { + res, err := p.client.ScriptLoad(luaScript).Result() + if err != nil { + return nil, convertError(err) + } + return res, nil +} +func (p *RedisClusterPool) EvalSha(sha1 string, numberKeys int, keysArgs ...interface{}) (interface{}, error) { + vals := make([]interface{}, 0, len(keysArgs)+2) + vals = append(vals, sha1, numberKeys) + vals = append(vals, keysArgs...) + keys := make([]string, 0, numberKeys) + args := make([]string, 0, len(keysArgs)-numberKeys) + for i, value := range keysArgs { + val, err := String(value, nil) + if err != nil && err != ErrNil { + return nil, err + } + if i < numberKeys { + keys = append(keys, val) + } else { + args = append(args, val) + } + } + res, err := p.client.EvalSha(sha1, keys, args).Result() + if err != nil { + return nil, convertError(err) + } + return res, nil +} +func (p *RedisClusterPool) Eval(luaScript string, numberKeys int, keysArgs ...interface{}) (interface{}, error) { + vals := make([]interface{}, 0, len(keysArgs)+2) + vals = append(vals, luaScript, numberKeys) + vals = append(vals, keysArgs...) + keys := make([]string, 0, numberKeys) + args := make([]string, 0, len(keysArgs)-numberKeys) + for i, value := range keysArgs { + val, err := String(value, nil) + if err != nil && err != ErrNil { + return nil, err + } + if i < numberKeys { + keys = append(keys, val) + } else { + args = append(args, val) + } + } + res, err := p.client.Eval(luaScript, keys, args).Result() + if err != nil { + return nil, convertError(err) + } + return res, nil +} +func (p *RedisClusterPool) GetBit(key string, offset int64) (int64, error) { + res, err := p.client.GetBit(key, offset).Result() + if err != nil { + return res, convertError(err) + } + return res, nil +} +func (p *RedisClusterPool) SetBit(key string, offset uint32, value int) (int, error) { + res, err := p.client.SetBit(key, int64(offset), value).Result() + return int(res), convertError(err) +} +func (p *RedisClusterPool) GetClient() *redis.ClusterClient { + return pools +} diff --git a/utils/convert.go b/utils/convert.go index 28caca4..62cb219 100644 --- a/utils/convert.go +++ b/utils/convert.go @@ -280,6 +280,9 @@ func Float64ToStrPrec1(f float64) string { func Float64ToStrPrec4(f float64) string { return strconv.FormatFloat(f, 'f', 4, 64) } +func Float64ToStrPrec10(f float64) string { + return strconv.FormatFloat(f, 'f', 10, 64) +} func Float64ToStrPrec6(f float64) string { return strconv.FormatFloat(f, 'f', 6, 64) diff --git a/utils/time2s.go b/utils/time2s.go new file mode 100644 index 0000000..6d3b1a9 --- /dev/null +++ b/utils/time2s.go @@ -0,0 +1,29 @@ +package zhios_order_relate_utils + +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 +} + +//GetDiffDays 获取两个时间相差的天数,0表同一天,正数表t1>t2,负数表t1