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 by lazy { SpUtil.instance().getList(SpUtil.KEY_WITHDRAW_HISTORY_LIST).toMutableList() } private val mItemList: MutableList by lazy { val itemList = SpUtil.instance().getList(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 { val itemList = mutableListOf() 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 { val subItemList = mutableListOf() 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) { SpUtil.instance().putList(SpUtil.KEY_WITHDRAW_ITEM_LIST, itemList) } fun getItemList(): List { 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 { try { mRecordLocker.lock() val clonedList = mutableListOf() 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 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 = 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, )