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

541 lines
18 KiB
Kotlin
Raw Normal View History

2025-12-12 07:27:30 +00:00
package com.gamedog.vididin.manager
import com.ama.core.architecture.util.AndroidUtil
2025-12-15 04:37:51 +00:00
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
2025-12-12 07:27:30 +00:00
import com.ama.core.architecture.util.SpUtil
2025-12-15 02:47:58 +00:00
import com.ama.core.architecture.util.eventbus.NotifyMan
import com.gamedog.vididin.VidiConst
2025-12-15 02:47:58 +00:00
import com.gamedog.vididin.VididinEvents
2025-12-25 02:42:51 +00:00
import com.gamedog.vididin.beans.RECORD_CASH_MINUS_WITHDRAW_SUCCESS
2025-12-24 08:31:38 +00:00
import com.gamedog.vididin.beans.RECORD_CASH_PLUS_WITHDRAW_FAIL
2025-12-25 02:42:51 +00:00
import com.gamedog.vididin.beans.RECORD_CASH_PLUS_WITHDRAW_ONGOING
2025-12-24 08:31:38 +00:00
import com.gamedog.vididin.beans.RecordCash
import com.gamedog.vididin.beans.req.PayInitReq
import com.gamedog.vididin.beans.req.PayoutCheckReq
import com.gamedog.vididin.beans.resp.WithdrawRecord
2025-12-15 09:30:16 +00:00
import com.gamedog.vididin.core.login.login.AccountManager
2025-12-15 03:39:03 +00:00
import com.gamedog.vididin.manager.WithdrawManager.Companion.STATE_NEED_WATCH_AD
import com.gamedog.vididin.netbase.NetworkUtil
import com.gamedog.vididin.netbase.Result
2025-12-15 08:42:35 +00:00
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
2025-12-12 07:27:30 +00:00
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()
}
2025-12-12 07:27:30 +00:00
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())
2025-12-15 02:47:58 +00:00
saveInfos2Sp(itemList)
2025-12-12 07:27:30 +00:00
}
itemList
}
companion object {
const val EACH_SUB_ITEM_CASH_NUM: Float = 1F
2025-12-22 09:00:22 +00:00
// subBean 状态
2025-12-15 03:39:03 +00:00
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
2025-12-25 08:24:33 +00:00
// 提现交易状态 提现状态 0: 未启动 1:提现中,2:提现成功,3:提现失败
const val TRANSACTION_STATE_UNSTART : Int = 0
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
2025-12-15 03:39:03 +00:00
2025-12-12 07:27:30 +00:00
@Volatile
private var instance: WithdrawManager? = null
fun instance(): WithdrawManager {
return instance ?: synchronized(this) {
instance ?: WithdrawManager().also {
instance = it
}
}
}
}
init {
loopCheckTransactionState()
}
2025-12-12 07:27:30 +00:00
private fun generateItemList(): MutableList<WithdrawItemBean> {
val itemList = mutableListOf<WithdrawItemBean>()
2025-12-22 09:00:22 +00:00
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)))
2025-12-12 07:27:30 +00:00
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) {
2025-12-15 02:47:58 +00:00
val initProgress = AndroidUtil.randomInt(50, 70)
subItemList.add(WithdrawSubItem(i, EACH_SUB_ITEM_CASH_NUM, initProgress, initProgress, 0F))
2025-12-12 07:27:30 +00:00
}
return subItemList
}
2025-12-15 02:47:58 +00:00
private fun saveInfos2Sp(itemList: MutableList<WithdrawItemBean>) {
2025-12-12 07:27:30 +00:00
SpUtil.instance().putList(SpUtil.KEY_WITHDRAW_ITEM_LIST, itemList)
}
fun getItemList(): List<WithdrawItemBean> {
return mItemList
}
2025-12-12 09:23:21 +00:00
fun getItem(itemIndex: Int): WithdrawItemBean {
return mItemList[itemIndex]
2025-12-12 07:27:30 +00:00
}
2025-12-15 02:47:58 +00:00
2025-12-22 06:32:16 +00:00
fun getHasWithdrawSuccessCashCount(): Float {
var count = 0F
for (record in mRecordList) {
if (record.state == TRANSACTION_STATE_SUCCESS) {
count += record.cashNum
}
}
return count
}
2025-12-22 06:51:32 +00:00
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
}
2025-12-15 02:47:58 +00:00
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)
}
2025-12-15 02:47:58 +00:00
private fun calculateSubBeanProgress(subBean: WithdrawSubItem) {
val needEarnProgress = 100 - subBean.startAdProgress
2025-12-15 02:47:58 +00:00
if (subBean.hasEarnMoneyByAd >= subBean.cashTotal) {
subBean.currentAdProgress = 100
2025-12-15 03:39:03 +00:00
// update state
if (subBean.withdrawState == STATE_NEED_WATCH_AD) {
subBean.withdrawState = STATE_COULD_WITHDRAW
}
2025-12-15 02:47:58 +00:00
} else {
val newProgress = subBean.startAdProgress + (needEarnProgress * (subBean.hasEarnMoneyByAd / subBean.cashTotal)).toInt()
subBean.currentAdProgress = if (newProgress >= 100) 100 else newProgress
2025-12-15 02:47:58 +00:00
}
2025-12-12 07:27:30 +00:00
}
2025-12-15 03:39:03 +00:00
fun updateSubBeanState(subBean: WithdrawSubItem, newState: Int) {
subBean.withdrawState = newState
saveInfos2Sp(mItemList)
}
2025-12-15 04:37:51 +00:00
fun startItem(curItem: WithdrawItemBean) {
if (curItem.startMs <= 0L) {
2025-12-15 05:44:00 +00:00
curItem.startMs = DateUtil.getCurTimeMs() - (if (curItem.totalCashNum == 50F) 24 * 3600000 * 4 else 0)
2025-12-15 04:37:51 +00:00
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()
2025-12-25 02:42:51 +00:00
val cashNum = newRecord.cashNum * -1
AccountManager.adjustCash(cashNum, RecordCash(
RECORD_CASH_PLUS_WITHDRAW_ONGOING, cashNum.toDouble(), true).apply { uuid = newRecord.recordNo }
)
loopCheckTransactionState()
}
2025-12-15 09:30:16 +00:00
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
if (newState == STATE_HAS_WITHDRAWED) {
checkIfItemFinishAndReset(record.itemIndex)
}
}
return@forEachIndexed
}
}
} finally {
mRecordLocker.unlock()
}
if (needSaveSp) {
saveRecords2Sp()
}
}
private fun checkIfItemFinishAndReset(itemIndex: Int) {
var needReset = false
if (itemIndex == 0) {
needReset = true
} else {
var allSubItemFinish = true
mItemList[itemIndex].subItemList.forEach {
if (it.withdrawState != STATE_HAS_WITHDRAWED) {
allSubItemFinish = false
return@forEach
}
}
needReset = allSubItemFinish
}
if (needReset) {
resetItem(itemIndex)
}
}
private fun resetItem(itemIndex: Int) {
val needResetItem = mItemList[itemIndex]
needResetItem.apply {
totalProgress = 0
startMs = 0L
hasStarted = false
subItemList.forEach { subItem ->
subItem.apply {
currentAdProgress = startAdProgress
hasEarnMoneyByAd = 0F
withdrawState = STATE_NEED_WATCH_AD
}
}
}
notifyItemListChanged()
}
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 ->
2025-12-25 11:35:34 +00:00
if (record.state == TRANSACTION_STATE_ONGOING) {
unCheckCount++
}
}
} finally {
mRecordLocker.unlock()
}
if (unCheckCount > 0) {
try {
mRecordLocker.lock()
mRecordList.forEachIndexed { index, record ->
2025-12-25 11:35:34 +00:00
if (record.state == TRANSACTION_STATE_ONGOING) {
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)
2025-12-24 08:31:38 +00:00
val recordBean = WithdrawManager.instance?.getRecord(recordNo)
recordBean?.let {
2025-12-25 02:42:51 +00:00
RecordsManager.instance().updateCashRecord(RecordCash(
RECORD_CASH_MINUS_WITHDRAW_SUCCESS, it.cashNum.toDouble(), true).apply { uuid = recordNo })
2025-12-24 08:31:38 +00:00
}
}
private fun handleTransactionFailed(recordNo: String, failedType: Int) {
updateRecord(recordNo, TRANSACTION_STATE_FAIL, failedType)
2025-12-15 09:30:16 +00:00
val recordBean = WithdrawManager.instance?.getRecord(recordNo)
recordBean?.let {
2025-12-25 02:42:51 +00:00
AccountManager.adjustCash(it.cashNum,
RecordCash(RECORD_CASH_PLUS_WITHDRAW_FAIL, it.cashNum.toDouble(), false).apply { uuid = recordNo })
2025-12-15 09:30:16 +00:00
}
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
}
2025-12-15 08:42:35 +00:00
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
}
2025-12-22 09:00:22 +00:00
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 getItemProgress(itemIndex: Int): Float {
var itemProgress = 0F
if (itemIndex in 0..mItemList.size-1) {
val curItem = mItemList[itemIndex]
val userCashTotal = AccountManager.getCash()
val restAvailableCashNum = userCashTotal - getStartedItemRestCashCount()
2025-12-22 09:00:22 +00:00
if (curItem.hasStarted || restAvailableCashNum >= curItem.totalCashNum) {
itemProgress = 1F
} else {
itemProgress = restAvailableCashNum / curItem.totalCashNum
2025-12-22 09:00:22 +00:00
}
}
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()
}
}
}
2025-12-12 07:27:30 +00:00
}
data class WithdrawItemBean(
val index: Int,
val totalCashNum: Float,
var totalProgress: Int = 0,
2025-12-15 02:47:58 +00:00
val subItemList: List<WithdrawSubItem> = emptyList(),
2025-12-15 04:37:51 +00:00
var startMs: Long = 0L,
2025-12-22 09:00:22 +00:00
var hasStarted: Boolean = false,
var isBigWithDraw: Boolean = true
2025-12-12 07:27:30 +00:00
)
data class WithdrawSubItem(
val index: Int,
2025-12-15 02:47:58 +00:00
val cashTotal: Float,
val startAdProgress: Int = 0,
var currentAdProgress: Int = 0,
2025-12-15 02:47:58 +00:00
var hasEarnMoneyByAd: Float = 0F,
2025-12-15 03:39:03 +00:00
var withdrawState: Int = STATE_NEED_WATCH_AD,
2025-12-12 07:27:30 +00:00
)