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

663 lines
23 KiB
Kotlin
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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.statisticreporter.StatisticUtil
import com.gamedog.vididin.VidiConst
import com.gamedog.vididin.VididinEvents
import com.gamedog.vididin.beans.RECORD_CASH_MINUS_WITHDRAW_SUCCESS
import com.gamedog.vididin.beans.RECORD_CASH_PLUS_WITHDRAW_FAIL
import com.gamedog.vididin.beans.RECORD_CASH_PLUS_WITHDRAW_ONGOING
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
import com.gamedog.vididin.core.login.login.AccountManager
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.viddin.videos.free.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: Double = 1.0
//----------------------- FROM pagsmile.proto ------------------------------
// init接口 status
// 1可提现 2:条件未达成 3:已提现, 4:禁止提现, 5:提现中
const val INIT_OK = 1
const val ERROR_NOT_REACH_CONDITION = 2
const val ERROR_ALREADY_WITHDRAWED = 3
const val ERROR_FORBID = 4
const val ERROR_WITHDRAWING = 5
// init接口 服务器 error 字段
// 0成功1失败2签名验证失败3客户端版本过低4 ts长度错误
const val ERROR_FAILED_UNKNOW = 1 + 10
const val ERROR_APP_VERSION_LOW = 3 + 10
/* payout接口 错误码,
0成功1失败2签名验证失败3客户端版本过低4uuid错误5所在地国家或地区不在提现限制内6提现金额不符对应的产品id7提现产品id不对8达到提现金额限制9提现次数超过限制10今日没有提现机会11提现账号达到次数限制12身份审核条件不满足不能提现13巴西提现参数 document_type 错误,
14巴西提现参数 document_id 错误15 巴西提现参数 AccountType 错误16 巴西提现参数 Name 错误17巴西提现参数 Account 和 DocumentId 不同18巴西提现参数account_type为CPF时 对应的 account 错误19巴西提现参数account_type为CNPJ时 对应的 account 错误20巴西提现参数 account_type 错误,
21巴西提现参数 document_type 错误22巴西提现参数account_type为CPF时 对应的 document_id 错误23巴西提现参数account_type为CNPJ时 对应的 document_id 错误24 ts长度错误25 没提0.1就提现其它的
*/
const val ERROR_PAYOUT_UNKNOW = 1 + 20
const val ERROR_PAYOUT_APP_VERSION_LOW = 3 + 20
const val ERROR_PAYOUT_COUNTRY_RESTRICTION = 5 + 20
const val ERROR_PAYOUT_REACH_TOTAL_LIMIT = 8 + 20
const val ERROR_PAYOUT_REACH_TIMES_LIMIT = 9 + 20
const val ERROR_PAYOUT_TODAY_NO_CHANCE = 10 + 20
const val ERROR_PAYOUT_ACCOUNT_REACH_TIMES_LIMIT = 11 + 20
const val ERROR_PAYOUT_ACCOUNT_INVALID = 18 + 20
const val ERROR_PAYOUT_USER_IDENTITY_LIMIT = 12 + 20
const val ERROR_PAYOUT_MUST_WITHDRAW01_FIRST = 25 + 20
// 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
// 提现交易状态 提现状态 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
@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.1, isBigWithDraw = false))
itemList.add(WithdrawItemBean(1, 10.0, AndroidUtil.randomInt(50, 70), generateSubItemList(10.0)))
itemList.add(WithdrawItemBean(2, 20.0, AndroidUtil.randomInt(50, 70), generateSubItemList(20.0)))
itemList.add(WithdrawItemBean(3, 50.0, AndroidUtil.randomInt(50, 70), generateSubItemList(50.0)))
itemList.add(WithdrawItemBean(4, 100.0, AndroidUtil.randomInt(50, 70), generateSubItemList(100.0)))
itemList.add(WithdrawItemBean(5, 300.0, AndroidUtil.randomInt(50, 70), generateSubItemList(300.0)))
return itemList
}
private fun generateSubItemList(totalCashInItem: Double): 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, 0.0))
}
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(): Double {
var count = 0.0
for (record in mRecordList) {
if (record.state == TRANSACTION_STATE_SUCCESS) {
count += record.cashNum
}
}
// extra withdraw count from zeroBuy
count += ZeroManager.instance().getZeroHasWithdrawSuccessCashCount()
return count
}
fun getHasWithdrawSuccessCashCount(itemIndex: Int): Double {
var count = 0.0
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: Double) : 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.startAdProgress
if (subBean.hasEarnMoneyByAd >= subBean.cashTotal) {
subBean.currentAdProgress = 100
// update state
if (subBean.withdrawState == STATE_NEED_WATCH_AD) {
subBean.withdrawState = STATE_COULD_WITHDRAW
}
} else {
val newProgress = subBean.startAdProgress + (needEarnProgress * (subBean.hasEarnMoneyByAd / subBean.cashTotal)).toInt()
subBean.currentAdProgress = if (newProgress >= 100) 100 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 == 50.0) 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()
val cashNum = newRecord.cashNum * -1
AccountManager.adjustCash(cashNum, RecordCash(
RECORD_CASH_PLUS_WITHDRAW_ONGOING, cashNum.toDouble(), true).apply { uuid = newRecord.recordNo }
)
loopCheckTransactionState()
}
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 = 0.0
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 ->
if (record.state == TRANSACTION_STATE_ONGOING) {
unCheckCount++
}
}
} finally {
mRecordLocker.unlock()
}
if (unCheckCount > 0) {
try {
mRecordLocker.lock()
mRecordList.forEachIndexed { index, record ->
if (record.state == TRANSACTION_STATE_ONGOING) {
doTransactionCheck(record.recordNo, record.cashNum)
}
}
} finally {
mRecordLocker.unlock()
}
}
}
private fun doTransactionCheck(recordNo: String, cashNum: Double) {
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 = ERROR_FAILED_UNKNOW
when (checkResult?.error) {
0 -> {
when (checkResult.status) {
// 提现状态 1:提现中,2:提现成功,3:提现失败
1 -> {
delay(10000)
loopCheckTransactionState()
}
2 -> {
handleTransactionSuccess(recordNo)
}
3 -> {
failedType = ERROR_FAILED_UNKNOW
}
}
}
1 -> {
failedType = ERROR_FAILED_UNKNOW
}
3 -> {
failedType = ERROR_APP_VERSION_LOW
}
}
if (failedType > 0) {
handleTransactionFailed(recordNo, failedType)
}
}
is Result.Error -> {
}
}
}
private fun handleTransactionSuccess(recordNo: String) {
updateRecord(recordNo, TRANSACTION_STATE_SUCCESS)
notifyWithdrawCheckResult(recordNo)
val recordBean = getRecord(recordNo)
recordBean?.let {
RecordsManager.instance().updateCashRecord(RecordCash(
RECORD_CASH_MINUS_WITHDRAW_SUCCESS, it.cashNum.toDouble(), true).apply { uuid = recordNo })
updateFirstWithdraw01Task(it)
sendWithdrawResultStatistic(it.cashNum, true, 0)
}
}
private fun updateFirstWithdraw01Task(recordBean: WithdrawRecord) {
if (recordBean.cashNum == 0.1 && !TaskManager.instance().newbieFirstWithdrawStatus().getStatusBean().hasClaimReward) {
TaskManager.instance().newbieFirstWithdrawStatus().claimReward()
}
}
private fun handleTransactionFailed(recordNo: String, failedType: Int) {
updateRecord(recordNo, TRANSACTION_STATE_FAIL, failedType)
val recordBean = WithdrawManager.instance?.getRecord(recordNo)
recordBean?.let {
AccountManager.adjustCash(it.cashNum,
RecordCash(RECORD_CASH_PLUS_WITHDRAW_FAIL, it.cashNum.toDouble(), false, failedType).apply { uuid = recordNo })
sendWithdrawResultStatistic(it.cashNum, false, failedType)
}
notifyWithdrawCheckResult(recordNo)
}
private fun sendWithdrawResultStatistic(cashNum: Double, isSuccess: Boolean, failType: Int) {
StatisticUtil.reportEvents(StatisticUtil.KEY_Withdrawal_Reason,
mapOf("Reason_Type" to (if (isSuccess) "Success" else "Fail"),
"Fail_Reason" to failType.toString() + ": " + getFailHintStrRes(failType),
"Withdrawal_Position" to cashNum,
"Withdrawal_Day" to 1))
}
fun getFailHintStrRes(failType: Int) : Int {
var failTextRes = R.string.withdraw_normal_fail
when (failType) {
ERROR_NOT_REACH_CONDITION -> {
failTextRes = R.string.withdraw_fail_not_reach_condition
}
ERROR_ALREADY_WITHDRAWED -> {
failTextRes = R.string.withdraw_fail_already_withdraw
}
ERROR_FORBID -> {
failTextRes = R.string.withdraw_fail_forbidden
}
ERROR_WITHDRAWING -> {
failTextRes = R.string.withdraw_fail_withdrawing
}
ERROR_FAILED_UNKNOW -> {
failTextRes = R.string.withdraw_normal_fail
}
ERROR_APP_VERSION_LOW -> {
failTextRes = R.string.withdraw_fail_version_toolow
}
ERROR_PAYOUT_UNKNOW -> {
failTextRes = R.string.withdraw_normal_fail
}
ERROR_PAYOUT_APP_VERSION_LOW -> {
failTextRes = R.string.withdraw_fail_version_toolow
}
ERROR_PAYOUT_COUNTRY_RESTRICTION -> {
failTextRes = R.string.withdraw_fail_country_restriction
}
ERROR_PAYOUT_REACH_TOTAL_LIMIT -> {
failTextRes = R.string.withdraw_fail_reach_amount_limit
}
ERROR_PAYOUT_REACH_TIMES_LIMIT -> {
failTextRes = R.string.withdraw_fail_reach_times_limit
}
ERROR_PAYOUT_TODAY_NO_CHANCE -> {
failTextRes = R.string.withdraw_fail_today_no_chance
}
ERROR_PAYOUT_ACCOUNT_REACH_TIMES_LIMIT -> {
failTextRes = R.string.withdraw_account_times_limit
}
ERROR_PAYOUT_ACCOUNT_INVALID -> {
failTextRes = R.string.withdraw_account_invalid
}
ERROR_PAYOUT_USER_IDENTITY_LIMIT -> {
failTextRes = R.string.withdraw_fail_user_identity_limit
}
ERROR_PAYOUT_MUST_WITHDRAW01_FIRST -> {
failTextRes = R.string.withdraw_fail_must_withdraw_01_first
}
}
return failTextRes
}
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
}
private fun getStartedItemRestCashNum(itemIndex: Int): Double {
if (itemIndex in 0..mItemList.size-1) {
val curItem = mItemList[itemIndex]
if (curItem.hasStarted) {
var hasWithdrawedCash = 0.0
curItem.subItemList.forEach { subItem->
if (subItem.withdrawState == STATE_HAS_WITHDRAWED) {
hasWithdrawedCash += subItem.cashTotal
}
}
return curItem.totalCashNum - hasWithdrawedCash
}
}
return 0.0
}
private fun getStartedItemRestCashCount(): Double {
var allStartedItemRestCashNum = 0.0
mItemList.forEachIndexed { index, item ->
if (!(TaskManager.instance().newbieFirstWithdrawStatus().getStatusBean().hasClaimReward && index == 0)) {
allStartedItemRestCashNum += getStartedItemRestCashNum(index)
}
}
return allStartedItemRestCashNum
}
fun getItemProgress(itemIndex: Int): Double {
var itemProgress = 0.0
if (itemIndex in 0..mItemList.size-1) {
val curItem = mItemList[itemIndex]
val userCashTotal = AccountManager.getCash()
var restAvailableCashNum = userCashTotal - getStartedItemRestCashCount()
if (restAvailableCashNum < 0.0) {
restAvailableCashNum = 0.0
}
if (curItem.hasStarted || restAvailableCashNum >= curItem.totalCashNum) {
itemProgress = 1.0
} 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: Double,
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: Double,
val startAdProgress: Int = 0,
var currentAdProgress: Int = 0,
var hasEarnMoneyByAd: Double = 0.0,
var withdrawState: Int = STATE_NEED_WATCH_AD,
)