VidiDin-Android/app/src/main/java/com/gamedog/vididin/manager/WithdrawManager.kt

514 lines
17 KiB
Kotlin

package com.gamedog.vididin.manager
import com.ama.core.architecture.util.AndroidUtil
import com.ama.core.architecture.util.DateUtil
import com.ama.core.architecture.util.DeviceUtil
import com.ama.core.architecture.util.MD5Util
import com.ama.core.architecture.util.NetUtil
import com.ama.core.architecture.util.SpUtil
import com.ama.core.architecture.util.eventbus.NotifyMan
import com.gamedog.vididin.VidiConst
import com.gamedog.vididin.VididinEvents
import com.gamedog.vididin.beans.req.PayInitReq
import com.gamedog.vididin.beans.req.PayoutCheckReq
import com.gamedog.vididin.beans.resp.WithdrawRecord
import com.gamedog.vididin.core.login.login.AccountManager
import com.gamedog.vididin.features.withdraw.WithDrawActivity.Companion.FINAL_STATE_ONGING
import com.gamedog.vididin.manager.WithdrawManager.Companion.STATE_NEED_WATCH_AD
import com.gamedog.vididin.netbase.NetworkUtil
import com.gamedog.vididin.netbase.Result
import com.vididin.real.money.game.R
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import java.util.concurrent.locks.ReentrantLock
class WithdrawManager private constructor() {
private val backgroundScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
private val mRecordLocker = ReentrantLock()
private val mRecordList: MutableList<WithdrawRecord> by lazy {
SpUtil.instance().getList<WithdrawRecord>(SpUtil.KEY_WITHDRAW_HISTORY_LIST).toMutableList()
}
private val mItemList: MutableList<WithdrawItemBean> by lazy {
val itemList = SpUtil.instance().getList<WithdrawItemBean>(SpUtil.KEY_WITHDRAW_ITEM_LIST).toMutableList()
if (itemList.isEmpty()) {
itemList.addAll(generateItemList())
saveInfos2Sp(itemList)
}
itemList
}
companion object {
const val EACH_SUB_ITEM_CASH_NUM: Float = 1F
// subBean 状态
const val ITEM_STATE_CANNOT_START: Int = 0
const val ITEM_STATE_CAN_START: Int = 1
const val ITEM_STATE_STARTED: Int = 2
// subBean 状态
const val STATE_NEED_WATCH_AD: Int = 0
const val STATE_COULD_WITHDRAW: Int = 1
const val STATE_WITHDRAWING: Int = 2
const val STATE_HAS_WITHDRAWED: Int = 3
// 提现交易状态 提现状态 1:提现中,2:提现成功,3:提现失败
const val TRANSACTION_STATE_ONGOING : Int = 1
const val TRANSACTION_STATE_SUCCESS : Int = 2
const val TRANSACTION_STATE_FAIL : Int = 3
// 提现失败分类(自定义)
const val FAIL_TYPE_UNKNOWN : Int = 1
const val FAIL_TYPE_APP_VERSION_LOW : Int = 2
@Volatile
private var instance: WithdrawManager? = null
fun instance(): WithdrawManager {
return instance ?: synchronized(this) {
instance ?: WithdrawManager().also {
instance = it
}
}
}
}
init {
loopCheckTransactionState()
}
private fun generateItemList(): MutableList<WithdrawItemBean> {
val itemList = mutableListOf<WithdrawItemBean>()
itemList.add(WithdrawItemBean(0, 0.1F, isBigWithDraw = false))
itemList.add(WithdrawItemBean(1, 10F, AndroidUtil.randomInt(50, 70), generateSubItemList(10F)))
itemList.add(WithdrawItemBean(2, 20F, AndroidUtil.randomInt(50, 70), generateSubItemList(20F)))
itemList.add(WithdrawItemBean(3, 50F, AndroidUtil.randomInt(50, 70), generateSubItemList(50F)))
itemList.add(WithdrawItemBean(4, 100F, AndroidUtil.randomInt(50, 70), generateSubItemList(100F)))
itemList.add(WithdrawItemBean(5, 300F, AndroidUtil.randomInt(50, 70), generateSubItemList(300F)))
return itemList
}
private fun generateSubItemList(totalCashInItem: Float): List<WithdrawSubItem> {
val subItemList = mutableListOf<WithdrawSubItem>()
val subItemCount: Int = (totalCashInItem/EACH_SUB_ITEM_CASH_NUM).toInt()
for (i in 0..subItemCount-1) {
val initProgress = AndroidUtil.randomInt(50, 70)
subItemList.add(WithdrawSubItem(i, EACH_SUB_ITEM_CASH_NUM, initProgress, initProgress, 0F))
}
return subItemList
}
private fun saveInfos2Sp(itemList: MutableList<WithdrawItemBean>) {
SpUtil.instance().putList(SpUtil.KEY_WITHDRAW_ITEM_LIST, itemList)
}
fun getItemList(): List<WithdrawItemBean> {
return mItemList
}
fun getItem(itemIndex: Int): WithdrawItemBean {
return mItemList[itemIndex]
}
fun getHasWithdrawSuccessCashCount(): Float {
var count = 0F
for (record in mRecordList) {
if (record.state == TRANSACTION_STATE_SUCCESS) {
count += record.cashNum
}
}
return count
}
fun getHasWithdrawSuccessCashCount(itemIndex: Int): Float {
var count = 0F
for (record in mRecordList) {
if (record.state == TRANSACTION_STATE_SUCCESS && record.itemIndex == itemIndex) {
count += record.cashNum
}
}
return count
}
fun addAdEarnForSubBean(itemIndex: Int, selectedSubIndex: Int, earnMoneyNum: Float) : Boolean {
if (itemIndex >= 0 && itemIndex < mItemList.size) {
try {
val subBean = mItemList[itemIndex].subItemList[selectedSubIndex]
subBean.hasEarnMoneyByAd += earnMoneyNum * 5 // dollar 2 bariz
calculateSubBeanProgress(subBean) //传入 itembean 更新 selectedIndex为下一个
saveInfos2Sp(mItemList)
notifyProgressUpdated(subBean)
return true
} catch (e: Exception) {
e.printStackTrace()
}
}
return false
}
private fun notifyProgressUpdated(subBean: WithdrawSubItem) {
NotifyMan.instance().sendEvent(VididinEvents.EVENT_WITHDRAW_SUB_ITEM_PROGRESS_UPDATED,
NotifyMan.NotifyData(subBean.index))
}
private fun notifySelectedSubBeanChanged(subBean: WithdrawSubItem) {
NotifyMan.instance().sendEvent(VididinEvents.EVENT_WITHDRAW_SELECTED_SUB_ITEM_CHANGED, NotifyMan.NotifyData(subBean.index))
}
private fun notifyItemListChanged() {
NotifyMan.instance().sendEvent(VididinEvents.EVENT_WITHDRAW_ITEM_LIST_CHANGED, null)
}
private fun calculateSubBeanProgress(subBean: WithdrawSubItem) {
val needEarnProgress = 100 - subBean.startProgress
if (subBean.hasEarnMoneyByAd >= subBean.cashTotal) {
subBean.currentProgress = 100
// update state
if (subBean.withdrawState == STATE_NEED_WATCH_AD) {
subBean.withdrawState = STATE_COULD_WITHDRAW
}
} else {
val newProgress = subBean.startProgress + (needEarnProgress * (subBean.hasEarnMoneyByAd / subBean.cashTotal)).toInt()
subBean.currentProgress = if (newProgress >= 100) 99 else newProgress
}
}
fun updateSubBeanState(subBean: WithdrawSubItem, newState: Int) {
subBean.withdrawState = newState
saveInfos2Sp(mItemList)
}
fun startItem(curItem: WithdrawItemBean) {
if (curItem.startMs <= 0L) {
curItem.startMs = DateUtil.getCurTimeMs() - (if (curItem.totalCashNum == 50F) 24 * 3600000 * 4 else 0)
saveInfos2Sp(mItemList)
}
}
fun saveRecordHasNotifyState(recordNo: String) {
val recordBean = getRecord(recordNo)
recordBean?.let {
it.hasShowResultDialog = true
saveRecords2Sp()
}
}
fun saveNewRecord(newRecord: WithdrawRecord) {
try {
mRecordLocker.lock()
mRecordList.add(newRecord)
} finally {
mRecordLocker.unlock()
}
saveRecords2Sp()
adjustAccountCash(newRecord.cashNum * -1)
loopCheckTransactionState()
}
private fun adjustAccountCash(cashNum: Float) {
AccountManager.adjustAccountCash(cashNum)
}
private fun saveRecords2Sp() {
SpUtil.instance().putList(SpUtil.KEY_WITHDRAW_HISTORY_LIST,getClonedRecordList())
}
fun updateRecord(recordNo: String, newState: Int, failType: Int = 0) {
var needSaveSp = false
try {
mRecordLocker.lock()
mRecordList.forEachIndexed { index, record ->
if (record.recordNo == recordNo) {
if (record.state != newState) {
needSaveSp =true
record.state = newState
record.failReason = failType
}
return@forEachIndexed
}
}
} finally {
mRecordLocker.unlock()
}
if (needSaveSp) {
saveRecords2Sp()
}
}
fun getClonedRecordList(): List<WithdrawRecord> {
try {
mRecordLocker.lock()
val clonedList = mutableListOf<WithdrawRecord>()
clonedList.addAll(mRecordList)
return clonedList
} finally {
mRecordLocker.unlock()
}
return emptyList()
}
private fun getRecord(recordNo: String): WithdrawRecord? {
try {
mRecordLocker.lock()
mRecordList.forEachIndexed { index, record ->
if (record.recordNo == recordNo) {
return record
}
}
} finally {
mRecordLocker.unlock()
}
return null
}
private fun loopCheckTransactionState() {
var unCheckCount = 0
try {
mRecordLocker.lock()
mRecordList.forEach { record ->
if (record.state == FINAL_STATE_ONGING) {
unCheckCount++
}
}
} finally {
mRecordLocker.unlock()
}
if (unCheckCount > 0) {
try {
mRecordLocker.lock()
mRecordList.forEachIndexed { index, record ->
if (record.state == FINAL_STATE_ONGING) {
doTransactionCheck(record.recordNo, record.cashNum)
}
}
} finally {
mRecordLocker.unlock()
}
}
}
private fun doTransactionCheck(recordNo: String, cashNum: Float) {
backgroundScope.launch {
performNetworkRequest(recordNo)
}
}
private suspend fun performNetworkRequest(recordNo: String) {
val requestParam = applyInitFields(PayoutCheckReq()).apply {
record_no = recordNo
}
val reqResult = NetworkUtil.callApi {
NetworkUtil.apiservice().withdrawCheck(requestParam)
}
when (reqResult) {
is Result.Loading -> {
}
is Result.Success -> {
val checkResult = reqResult.data.data
var failedType = 0
when (checkResult?.error) {
0 -> {
when (checkResult.status) {
// 提现状态 1:提现中,2:提现成功,3:提现失败
1 -> {
delay(10000)
loopCheckTransactionState()
}
2 -> {
handleTransactionSuccess(recordNo)
}
3 -> {
failedType = FAIL_TYPE_UNKNOWN
}
}
}
1 -> {
failedType = FAIL_TYPE_UNKNOWN
}
2 -> {
failedType = FAIL_TYPE_APP_VERSION_LOW
}
}
if (failedType > 0) {
handleTransactionFailed(recordNo, failedType)
}
}
is Result.Error -> {
}
}
}
private fun handleTransactionSuccess(recordNo: String) {
updateRecord(recordNo, TRANSACTION_STATE_SUCCESS)
notifyWithdrawCheckResult(recordNo)
}
private fun handleTransactionFailed(recordNo: String, failedType: Int) {
updateRecord(recordNo, TRANSACTION_STATE_FAIL, failedType)
val recordBean = WithdrawManager.instance?.getRecord(recordNo)
recordBean?.let {
adjustAccountCash(it.cashNum)
}
notifyWithdrawCheckResult(recordNo)
}
private fun notifyWithdrawCheckResult(recordNo: String) {
val recordBean = getRecord(recordNo)
recordBean.let {
NotifyMan.instance().sendEvent(VididinEvents.EVENT_WITHDRAW_CHECK_RESULT_UPDATED, NotifyMan.NotifyData(it))
}
}
fun <T : PayInitReq> applyInitFields(dataBean: T): T {
dataBean.apply {
platform = "Android"
deviceid = DeviceUtil.generateDeviceId()
version = AndroidUtil.getAppVersionInfo()
ip = NetUtil.getLocalIpAddress()
ts = (System.currentTimeMillis()/1000).toString()
val signOrigin = "${VidiConst.WITHDRAW_MD5KEY}platform=${platform}deviceid=${deviceid}version=${version}ip=${ip}ts=$ts"
sign = MD5Util.md5ForWithDraw(signOrigin)
}
return dataBean
}
fun getFailShowTextRes(failReason: Int): Int {
var failTextRes = R.string.withdraw_normal_fail
when (failReason) {
FAIL_TYPE_APP_VERSION_LOW -> {
failTextRes = R.string.withdraw_fail_version_toolow
}
}
return failTextRes
}
private fun getStartedItemRestCashNum(itemIndex: Int): Float {
if (itemIndex in 0..mItemList.size-1) {
val curItem = mItemList[itemIndex]
if (curItem.hasStarted) {
var hasWithdrawedCash = 0F
curItem.subItemList.forEach { subItem->
if (subItem.withdrawState == STATE_HAS_WITHDRAWED) {
hasWithdrawedCash += subItem.cashTotal
}
}
return curItem.totalCashNum - hasWithdrawedCash
}
}
return 0F
}
private fun getStartedItemRestCashCount(): Float {
var allStartedItemRestCashNum = 0F
mItemList.forEachIndexed { index, item ->
allStartedItemRestCashNum += getStartedItemRestCashNum(index)
}
return allStartedItemRestCashNum
}
fun getItemState(itemIndex: Int): Int {
var returnState = ITEM_STATE_CANNOT_START
AccountManager.getAccount()?.let {
if (itemIndex in 0..mItemList.size-1) {
val curItem = mItemList[itemIndex]
val userCashTotal = it.cashCount
val allStartedItemRestCashNum = getStartedItemRestCashCount()
if (curItem.hasStarted) {
returnState = ITEM_STATE_STARTED
} else if ((userCashTotal - allStartedItemRestCashNum) >= curItem.totalCashNum) {
returnState = ITEM_STATE_CAN_START
}
}
}
return returnState
}
fun getItemProgress(itemIndex: Int): Float {
var itemProgress = 0F
AccountManager.getAccount()?.let {
if (itemIndex in 0..mItemList.size-1) {
val curItem = mItemList[itemIndex]
val userCashTotal = it.cashCount
val restAvailableCashNum = userCashTotal - getStartedItemRestCashCount()
if (curItem.hasStarted || restAvailableCashNum >= curItem.totalCashNum) {
itemProgress = 1F
} else {
itemProgress = restAvailableCashNum / curItem.totalCashNum
}
}
}
return itemProgress
}
fun setItemStarted(itemIndex: Int) {
if (itemIndex in 0..mItemList.size-1) {
val curItem = mItemList[itemIndex]
if (!curItem.hasStarted) {
curItem.hasStarted = true
curItem.startMs = System.currentTimeMillis()
saveInfos2Sp(mItemList)
notifyItemListChanged()
}
}
}
}
data class WithdrawItemBean(
val index: Int,
val totalCashNum: Float,
var totalProgress: Int = 0,
val subItemList: List<WithdrawSubItem> = emptyList(),
var startMs: Long = 0L,
var hasStarted: Boolean = false,
var isBigWithDraw: Boolean = true
)
data class WithdrawSubItem(
val index: Int,
val cashTotal: Float,
val startProgress: Int = 0,
var currentProgress: Int = 0,
var hasEarnMoneyByAd: Float = 0F,
var withdrawState: Int = STATE_NEED_WATCH_AD,
)