483 lines
13 KiB
Go
483 lines
13 KiB
Go
|
package model
|
|||
|
|
|||
|
import (
|
|||
|
"fmt"
|
|||
|
"go-common/library/ecode"
|
|||
|
"time"
|
|||
|
)
|
|||
|
|
|||
|
/*
|
|||
|
const STREAM_OP_RESULT_SUCCESS = 0 //交易成功
|
|||
|
const STREAM_OP_RESULT_IN_PROGRESS = 1 //交易进行中
|
|||
|
const STREAM_OP_RESULT_FAILED = 2 //交易失败
|
|||
|
const STREAM_OP_RESULT_ROLLBACK_SUCC = 2 //回滚成功
|
|||
|
const STREAM_OP_RESULT_ROLLBACK_IN_PROGRESS = 2 //交易回滚中
|
|||
|
const STREAM_OP_RESULT_ROLLBACK_FAILED = 2 //回滚成功
|
|||
|
|
|||
|
*/
|
|||
|
|
|||
|
const STREAM_OP_RESULT_SUB_SUCC = 1 //扣款成功
|
|||
|
const STREAM_OP_RESULT_ADD_SUCC = 2 //加款成功
|
|||
|
const STREAM_OP_RESULT_SUB_FAILED = -1 //扣款失败
|
|||
|
const STREAM_OP_RESULT_ADD_FAILED = -2 //加款失败
|
|||
|
|
|||
|
const STREAM_OP_REASON_EXECUTE_UNKNOWN = -6 //系统内部逻辑错误: 结果未知,当作失败
|
|||
|
const STREAM_OP_REASON_POST_QUERY_FAILED = -5 //系统内部错误,后置查询失败
|
|||
|
const STREAM_OP_REASON_QUERY_FAILED = -4 //系统内部错误,查询失败
|
|||
|
const STREAM_OP_REASON_LOCK_ERROR = -3 //系统内部错误,获取锁异常
|
|||
|
const STREAM_OP_REASON_EXECUTE_FAILED = -2 //系统内部逻辑错误: 执行失败,连接异常 OR SQL错误 OR 修改要求的条件不满足(有别的进程越过用户锁,对货币数进行了变更)
|
|||
|
const STREAM_OP_REASON_PRE_QUERY_FAILED = -1 //前置查询失败
|
|||
|
const STREAM_OP_REASON_NOT_ENOUGH_COIN = 1
|
|||
|
const STREAM_OP_REASON_LOCK_FAILED = 2
|
|||
|
|
|||
|
const (
|
|||
|
LIVE_PLATFORM_IOS = "ios"
|
|||
|
LIVE_PLATFORM_PC = "pc"
|
|||
|
LIVE_PLATFORM_ANDROID = "android"
|
|||
|
LIVE_PLATFORM_H5 = "h5"
|
|||
|
COIN_TYPE_IOS_GOLD = "iap_gold"
|
|||
|
COIN_TYPE_GOLD = "gold"
|
|||
|
COIN_TYPE_SILVER = "silver"
|
|||
|
COIN_TYPE_METAL = "metal" //主站硬币(主站提供硬币数查询、硬币扣除接口供调用)
|
|||
|
)
|
|||
|
|
|||
|
type RechargeOrPayForm struct {
|
|||
|
Uid int64 `form:"uid" validate:"required"`
|
|||
|
CoinType string `form:"coin_type" validate:"required"`
|
|||
|
CoinNum int64 `form:"coin_num" validate:"required"`
|
|||
|
ExtendTid string `form:"extend_tid" validate:"required"`
|
|||
|
Timestamp int64 `form:"timestamp" validate:"required"`
|
|||
|
TransactionId string `form:"transaction_id" validate:"required"`
|
|||
|
}
|
|||
|
|
|||
|
type ExchangeForm struct {
|
|||
|
Uid int64 `form:"uid" validate:"required"`
|
|||
|
ExtendTid string `form:"extend_tid" validate:"required"`
|
|||
|
Timestamp int64 `form:"timestamp" validate:"required"`
|
|||
|
TransactionId string `form:"transaction_id" validate:"required"`
|
|||
|
SrcCoinType string `form:"src_coin_type" validate:"required"`
|
|||
|
SrcCoinNum int64 `form:"src_coin_num" validate:"required"`
|
|||
|
DestCoinType string `form:"dest_coin_type" validate:"required"`
|
|||
|
DestCoinNum int64 `form:"dest_coin_num" validate:"required"`
|
|||
|
}
|
|||
|
|
|||
|
type RecordCoinStreamForm struct {
|
|||
|
Uid int64 `form:"uid" validate:"required"`
|
|||
|
Data string `form:"data" validate:"required"`
|
|||
|
}
|
|||
|
|
|||
|
type ServiceType int32
|
|||
|
|
|||
|
const (
|
|||
|
PAYTYPE ServiceType = 0
|
|||
|
RECHARGETYPE ServiceType = 1
|
|||
|
EXCHANGETYPE ServiceType = 2
|
|||
|
ROLLBACKTYPE ServiceType = 3
|
|||
|
SysCoinTypeIosGold int32 = 2
|
|||
|
SysCoinTypeGold int32 = 1
|
|||
|
SysCoinTypeSilver int32 = 0
|
|||
|
SysCoinTypeMetal int32 = 3
|
|||
|
)
|
|||
|
|
|||
|
func IsValidServiceType(serviceType int32) bool {
|
|||
|
st := ServiceType(serviceType)
|
|||
|
return st == PAYTYPE ||
|
|||
|
st == RECHARGETYPE ||
|
|||
|
st == EXCHANGETYPE ||
|
|||
|
st == ROLLBACKTYPE
|
|||
|
}
|
|||
|
|
|||
|
var (
|
|||
|
validPlatformMap = map[string]string{LIVE_PLATFORM_ANDROID: LIVE_PLATFORM_ANDROID, LIVE_PLATFORM_H5: LIVE_PLATFORM_H5, LIVE_PLATFORM_PC: LIVE_PLATFORM_PC, LIVE_PLATFORM_IOS: LIVE_PLATFORM_IOS}
|
|||
|
validCoinTypeMap = map[string]int32{COIN_TYPE_IOS_GOLD: SysCoinTypeIosGold, COIN_TYPE_GOLD: SysCoinTypeGold, COIN_TYPE_SILVER: SysCoinTypeSilver, COIN_TYPE_METAL: SysCoinTypeMetal}
|
|||
|
validPlatformNoMap = map[string]int32{LIVE_PLATFORM_PC: 1, LIVE_PLATFORM_ANDROID: 2, LIVE_PLATFORM_IOS: 3, LIVE_PLATFORM_H5: 4}
|
|||
|
)
|
|||
|
|
|||
|
func IsValidCoinType(coinType string) bool {
|
|||
|
_, ok := validCoinTypeMap[coinType]
|
|||
|
return ok
|
|||
|
}
|
|||
|
|
|||
|
func GetCoinTypeNumber(coinType string) int32 {
|
|||
|
n := validCoinTypeMap[coinType]
|
|||
|
return n
|
|||
|
}
|
|||
|
|
|||
|
func IsValidPlatform(platform string) bool {
|
|||
|
_, ok := validPlatformMap[platform]
|
|||
|
return ok
|
|||
|
}
|
|||
|
|
|||
|
func IsPlatformIOS(platform string) bool {
|
|||
|
return platform == LIVE_PLATFORM_IOS
|
|||
|
}
|
|||
|
|
|||
|
func IsLocalCoin(coinTypeNo int32) bool {
|
|||
|
return coinTypeNo != SysCoinTypeMetal
|
|||
|
}
|
|||
|
func GetSysCoinType(coinType string, platform string) string {
|
|||
|
if IsPlatformIOS(platform) && coinType == COIN_TYPE_GOLD {
|
|||
|
coinType = COIN_TYPE_IOS_GOLD
|
|||
|
}
|
|||
|
return coinType
|
|||
|
}
|
|||
|
|
|||
|
func GetSysCoinTypeByNo(coinTypeNo int32) string {
|
|||
|
switch coinTypeNo {
|
|||
|
case SysCoinTypeGold:
|
|||
|
return COIN_TYPE_GOLD
|
|||
|
case SysCoinTypeIosGold:
|
|||
|
return COIN_TYPE_IOS_GOLD
|
|||
|
case SysCoinTypeSilver:
|
|||
|
return COIN_TYPE_SILVER
|
|||
|
case SysCoinTypeMetal:
|
|||
|
return COIN_TYPE_METAL
|
|||
|
default:
|
|||
|
return "not_define"
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
func GetRechargeCnt(coinTypeNo int32) string {
|
|||
|
var rechargeCntField string
|
|||
|
if coinTypeNo == SysCoinTypeSilver {
|
|||
|
rechargeCntField = ""
|
|||
|
} else if coinTypeNo == SysCoinTypeIosGold {
|
|||
|
rechargeCntField = "gold_recharge_cnt"
|
|||
|
} else if coinTypeNo == SysCoinTypeGold {
|
|||
|
rechargeCntField = "gold_recharge_cnt"
|
|||
|
}
|
|||
|
return rechargeCntField
|
|||
|
}
|
|||
|
|
|||
|
func GetPayCnt(coinTypeNo int32) string {
|
|||
|
var cntField string
|
|||
|
if coinTypeNo == SysCoinTypeSilver {
|
|||
|
cntField = "silver_pay_cnt"
|
|||
|
} else if coinTypeNo == SysCoinTypeIosGold {
|
|||
|
cntField = "gold_pay_cnt"
|
|||
|
} else if coinTypeNo == SysCoinTypeGold {
|
|||
|
cntField = "gold_pay_cnt"
|
|||
|
}
|
|||
|
return cntField
|
|||
|
}
|
|||
|
|
|||
|
func GetWalletFormatTime(opTime int64) string {
|
|||
|
tm := time.Unix(opTime, 0)
|
|||
|
date := tm.Format("2006-01-02 15:04:05")
|
|||
|
return date
|
|||
|
}
|
|||
|
|
|||
|
func NewCoinStream(uid int64, tid string, extendTid string, coinType int32, coinNum int64, opType int32, opTime int64, bizCode string, area int64, source string, bizSource string, metadata string) *CoinStreamRecord {
|
|||
|
return &CoinStreamRecord{
|
|||
|
Uid: uid,
|
|||
|
TransactionId: tid,
|
|||
|
ExtendTid: extendTid,
|
|||
|
CoinType: coinType,
|
|||
|
DeltaCoinNum: coinNum,
|
|||
|
OpType: opType,
|
|||
|
OpTime: opTime,
|
|||
|
BizCode: bizCode,
|
|||
|
Area: area,
|
|||
|
Source: source,
|
|||
|
BizSource: bizSource,
|
|||
|
MetaData: metadata,
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
func NewExchangeSteam(uid int64, tid string, srcCoinType int32, srcCoinNum int32, destCoinType int32, destCoinNum int32, opTime int64, status int32) *CoinExchangeRecord {
|
|||
|
return &CoinExchangeRecord{
|
|||
|
Uid: uid,
|
|||
|
TransactionId: tid,
|
|||
|
SrcType: srcCoinType,
|
|||
|
SrcNum: srcCoinNum,
|
|||
|
DestType: destCoinType,
|
|||
|
DestNum: destCoinNum,
|
|||
|
ExchangeTime: opTime,
|
|||
|
Status: status,
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
func (m *CoinStreamRecord) SetOpReason(r int32) {
|
|||
|
m.OpReason = r
|
|||
|
}
|
|||
|
|
|||
|
func GetMelonseedResp(platform string, melonseed *Melonseed) *MelonseedResp {
|
|||
|
gold := getPlatformGold(melonseed.Gold, melonseed.IapGold, platform)
|
|||
|
return &MelonseedResp{
|
|||
|
Silver: fmt.Sprintf("%d", melonseed.Silver),
|
|||
|
Gold: fmt.Sprintf("%d", gold),
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
func GetMelonseedWithMetalResp(platform string, melonseed *Melonseed, metal float64) *MelonseedWithMetalResp {
|
|||
|
gold := getPlatformGold(melonseed.Gold, melonseed.IapGold, platform)
|
|||
|
return &MelonseedWithMetalResp{
|
|||
|
Silver: fmt.Sprintf("%d", melonseed.Silver),
|
|||
|
Gold: fmt.Sprintf("%d", gold),
|
|||
|
Metal: fmt.Sprintf("%.2f", metal),
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
func GetDetailResp(platform string, detail *Detail) *DetailResp {
|
|||
|
gold := getPlatformGold(detail.Gold, detail.IapGold, platform)
|
|||
|
return &DetailResp{
|
|||
|
Silver: fmt.Sprintf("%d", detail.Silver),
|
|||
|
Gold: fmt.Sprintf("%d", gold),
|
|||
|
GoldPayCnt: fmt.Sprintf("%d", detail.GoldPayCnt),
|
|||
|
GoldRechargeCnt: fmt.Sprintf("%d", detail.GoldRechargeCnt),
|
|||
|
SilverPayCnt: fmt.Sprintf("%d", detail.SilverPayCnt),
|
|||
|
CostBase: detail.CostBase,
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
func GetDetailWithMetalResp(platform string, detail *Detail, metal float64) *DetailWithMetalResp {
|
|||
|
gold := getPlatformGold(detail.Gold, detail.IapGold, platform)
|
|||
|
return &DetailWithMetalResp{
|
|||
|
Silver: fmt.Sprintf("%d", detail.Silver),
|
|||
|
Gold: fmt.Sprintf("%d", gold),
|
|||
|
GoldPayCnt: fmt.Sprintf("%d", detail.GoldPayCnt),
|
|||
|
GoldRechargeCnt: fmt.Sprintf("%d", detail.GoldRechargeCnt),
|
|||
|
SilverPayCnt: fmt.Sprintf("%d", detail.SilverPayCnt),
|
|||
|
Metal: fmt.Sprintf("%.2f", metal),
|
|||
|
CostBase: detail.CostBase,
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
func GetTidResp(tid string) *TidResp {
|
|||
|
return &TidResp{TransactionId: tid}
|
|||
|
}
|
|||
|
|
|||
|
func getPlatformGold(normalGold int64, iapGold int64, platform string) int64 {
|
|||
|
gold := normalGold
|
|||
|
if IsPlatformIOS(platform) {
|
|||
|
gold = iapGold
|
|||
|
}
|
|||
|
return gold
|
|||
|
}
|
|||
|
|
|||
|
func IncrMelonseedCoin(userCoins *Melonseed, num int64, coinTypeNo int32) {
|
|||
|
switch coinTypeNo {
|
|||
|
case SysCoinTypeIosGold:
|
|||
|
userCoins.IapGold += num
|
|||
|
case SysCoinTypeGold:
|
|||
|
userCoins.Gold += num
|
|||
|
case SysCoinTypeSilver:
|
|||
|
userCoins.Silver += num
|
|||
|
default:
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
func GetCoinByMelonseed(coinTypeNo int32, userCoin *Melonseed) int64 {
|
|||
|
switch coinTypeNo {
|
|||
|
case SysCoinTypeIosGold:
|
|||
|
return userCoin.IapGold
|
|||
|
case SysCoinTypeGold:
|
|||
|
return userCoin.Gold
|
|||
|
case SysCoinTypeSilver:
|
|||
|
return userCoin.Silver
|
|||
|
default:
|
|||
|
return 0
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
func GetCoinByDetailWithSnapShot(coinTypeNo int32, userCoin *DetailWithSnapShot) int64 {
|
|||
|
switch coinTypeNo {
|
|||
|
case SysCoinTypeIosGold:
|
|||
|
return userCoin.IapGold
|
|||
|
case SysCoinTypeGold:
|
|||
|
return userCoin.Gold
|
|||
|
case SysCoinTypeSilver:
|
|||
|
return userCoin.Silver
|
|||
|
default:
|
|||
|
return 0
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
func CompareCoin(origin interface{}, num int64) bool {
|
|||
|
switch origin.(type) {
|
|||
|
case int64:
|
|||
|
return origin.(int64) >= num
|
|||
|
case float64:
|
|||
|
return int64(origin.(float64)) >= num
|
|||
|
default:
|
|||
|
return false
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// 得到数据库适配的货币数据,由于数据库的org_coin_num delta_coin_num都是整型,但是硬币的类型是浮点数,所以做一下适配
|
|||
|
func GetDbFitCoin(v interface{}) int64 {
|
|||
|
switch v.(type) {
|
|||
|
case int64:
|
|||
|
return v.(int64)
|
|||
|
case float64:
|
|||
|
return int64(v.(float64))
|
|||
|
default:
|
|||
|
return 0
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
func SubCoin(v1 interface{}, v2 interface{}) int64 {
|
|||
|
switch v1.(type) {
|
|||
|
case int64:
|
|||
|
return v1.(int64) - v2.(int64)
|
|||
|
case float64:
|
|||
|
return int64(v1.(float64) - v2.(float64))
|
|||
|
default:
|
|||
|
return 0
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
func AddMoreParam2CoinStream(stream *CoinStreamRecord, bp *BasicParam, platform string) {
|
|||
|
platformNo := GetPlatformNo(platform)
|
|||
|
stream.Platform = platformNo
|
|||
|
stream.Reserved1 = bp.Reason
|
|||
|
stream.Version = bp.Version
|
|||
|
}
|
|||
|
|
|||
|
type CoinStreamFieldInject interface {
|
|||
|
GetExtendTid() string
|
|||
|
GetTimestamp() int64
|
|||
|
GetTransactionId() string
|
|||
|
GetBizCode() string
|
|||
|
GetArea() int64
|
|||
|
GetBizSource() string
|
|||
|
GetSource() string
|
|||
|
GetReason() int64
|
|||
|
GetVersion() int64
|
|||
|
GetMetaData() string
|
|||
|
GetPlatform() string
|
|||
|
GetUid() int64
|
|||
|
}
|
|||
|
|
|||
|
func InjectFieldToCoinStream(stream *CoinStreamRecord, inject CoinStreamFieldInject) {
|
|||
|
stream.ExtendTid = inject.GetExtendTid()
|
|||
|
stream.TransactionId = inject.GetTransactionId()
|
|||
|
stream.OpTime = inject.GetTimestamp()
|
|||
|
|
|||
|
stream.BizCode = inject.GetBizCode()
|
|||
|
stream.Area = inject.GetArea()
|
|||
|
stream.BizSource = inject.GetBizSource()
|
|||
|
stream.MetaData = inject.GetMetaData()
|
|||
|
stream.Source = inject.GetSource()
|
|||
|
|
|||
|
stream.Reserved1 = inject.GetReason()
|
|||
|
stream.Version = inject.GetVersion()
|
|||
|
platformNo := GetPlatformNo(inject.GetPlatform())
|
|||
|
stream.Platform = platformNo
|
|||
|
|
|||
|
stream.Uid = inject.GetUid()
|
|||
|
}
|
|||
|
|
|||
|
func GetPlatformNo(platform string) int32 {
|
|||
|
platformNo, ok := validPlatformNoMap[platform]
|
|||
|
if !ok {
|
|||
|
platformNo = 0
|
|||
|
}
|
|||
|
return platformNo
|
|||
|
}
|
|||
|
|
|||
|
var (
|
|||
|
validRecordCoinStreamItemType = map[string]bool{"recharge": true, "pay": true}
|
|||
|
)
|
|||
|
|
|||
|
func (m *RecordCoinStreamItem) IsValidType() bool {
|
|||
|
_, ok := validRecordCoinStreamItemType[m.Type]
|
|||
|
return ok
|
|||
|
}
|
|||
|
|
|||
|
func (m *RecordCoinStreamItem) IsPayType() bool {
|
|||
|
return m.Type == "pay"
|
|||
|
}
|
|||
|
|
|||
|
func (m *RecordCoinStreamItem) IsRechargeType() bool {
|
|||
|
return m.Type == "recharge"
|
|||
|
}
|
|||
|
|
|||
|
func (m *RecordCoinStreamItem) GetOpType() int32 {
|
|||
|
if m.IsPayType() {
|
|||
|
return int32(PAYTYPE)
|
|||
|
} else {
|
|||
|
return int32(RECHARGETYPE)
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
func (m *RecordCoinStreamItem) GetOpResult() int32 {
|
|||
|
if m.IsPayType() {
|
|||
|
return STREAM_OP_RESULT_SUB_SUCC
|
|||
|
} else {
|
|||
|
return STREAM_OP_RESULT_ADD_SUCC
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
func (m *RecordCoinStreamItem) IsValid() (valid bool) {
|
|||
|
valid = false
|
|||
|
if m.OrgCoinNum < 0 {
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
if !m.IsValidType() {
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
if !IsValidCoinType(m.CoinType) {
|
|||
|
return
|
|||
|
}
|
|||
|
if m.IsPayType() && m.CoinNum >= 0 {
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
if m.IsRechargeType() && m.CoinNum <= 0 {
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
valid = true
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
func GetMelonByDetailWithSnapShot(wallet *DetailWithSnapShot, platform string) (melon *MelonseedResp) {
|
|||
|
gold := wallet.Gold
|
|||
|
if platform == LIVE_PLATFORM_IOS {
|
|||
|
gold = wallet.IapGold
|
|||
|
}
|
|||
|
return &MelonseedResp{
|
|||
|
Silver: fmt.Sprintf("%d", wallet.Silver),
|
|||
|
Gold: fmt.Sprintf("%d", gold),
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
func ModifyCoinInDetailWithSnapShot(wallet *DetailWithSnapShot, sysCoinTypeNo int32, coinNum int64) {
|
|||
|
switch sysCoinTypeNo {
|
|||
|
case SysCoinTypeGold:
|
|||
|
wallet.Gold += coinNum
|
|||
|
case SysCoinTypeIosGold:
|
|||
|
wallet.IapGold += coinNum
|
|||
|
case SysCoinTypeSilver:
|
|||
|
wallet.Silver += coinNum
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// 根据锁的错误设置数据库的reason
|
|||
|
func SetReasonByLockErr(lockErr error, coinStream *CoinStreamRecord) {
|
|||
|
if lockErr == ecode.TargetBlocked {
|
|||
|
coinStream.OpReason = STREAM_OP_REASON_LOCK_FAILED
|
|||
|
} else {
|
|||
|
coinStream.OpReason = STREAM_OP_REASON_LOCK_ERROR
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
func NeedSnapshot(wallet *DetailWithSnapShot, now time.Time) bool {
|
|||
|
lastTime, _ := time.Parse("2006-01-02 15:04:05", wallet.SnapShotTime)
|
|||
|
return now.After(lastTime)
|
|||
|
}
|
|||
|
|
|||
|
func GetTodayTime(now time.Time) time.Time {
|
|||
|
timeStr := now.Format("2006-01-02") + " 00:00:00"
|
|||
|
today, _ := time.Parse("2006-01-02 15:04:05", timeStr)
|
|||
|
return today
|
|||
|
}
|
|||
|
|
|||
|
func TodayNeedSnapShot(wallet *DetailWithSnapShot) bool {
|
|||
|
now := GetTodayTime(time.Now())
|
|||
|
return NeedSnapshot(wallet, now)
|
|||
|
}
|