Как предотвратить дублирование вставки базы данных при обработке нескольких регулярных платежей с разной частотой в AndrAndroid

Форум для тех, кто программирует под Android
Ответить
Anonymous
 Как предотвратить дублирование вставки базы данных при обработке нескольких регулярных платежей с разной частотой в Andr

Сообщение Anonymous »

Я хочу добавить для пользователя возможность обычной оплаты. Когда пользователь вставляет платеж в базу данных и устанавливает частоту «Ежедневно» или любой другой вариант, исходя из количества оставшихся платежей, данные должны добавляться в соответствии с выбранной периодичностью. После этого следует обновить оставшиеся платежи. Если пользователь удаляет какой-либо из ежедневных платежей, система должна добавить платеж снова через график регулярных платежей.
PaymentReceiver

Код: Выделить всё

class PaymentReceiver : BroadcastReceiver() {

companion object {
private val lastExecutionTimes = ConcurrentHashMap()
}

override fun onReceive(context: Context, intent: Intent) {
val title = intent.getStringExtra("TITLE")
val message = intent.getStringExtra("MESSAGE")
val paymentId = intent.getIntExtra("paymentId", 0)
val frequency = intent.getStringExtra("FREQUENCY") ?: return
val currentTime = System.currentTimeMillis()

val userViewModel = UserViewModel(Application.getContext())
val paymentManager =
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager

val soundUri = Uri.parse("android.resource://${context.packageName}/raw/notification")

val payment = runBlocking {
userViewModel.getRegularPaymentById(paymentId.toLong())
}

if (payment == null) {
Log.e("PaymentReceiver", "Payment not found for ID: $paymentId")
return
}

if (lastExecutionTimes[paymentId]?.let { currentTime - it < 5000 } == true) {
Log.d("PaymentReceiver", "Duplicate execution avoided for paymentId: $paymentId")
return
}
lastExecutionTimes[paymentId] = currentTime

if (payment?.remainingPayments!! = Build.VERSION_CODES.O) {
val channel = NotificationChannel(
"PAYMENT_CHANNEL",
"Payments",
NotificationManager.IMPORTANCE_HIGH
).apply {
setSound(soundUri, null)
enableVibration(true)
lockscreenVisibility = NotificationCompat.VISIBILITY_PUBLIC
}
paymentManager.createNotificationChannel(channel)
}

val notificationIntent = Intent(context, SplashActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
}

val pendingIntent = PendingIntent.getActivity(
context,
paymentId,
notificationIntent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
)

val builder = NotificationCompat.Builder(context, "PAYMENT_CHANNEL")
.setSmallIcon(R.mipmap.ic_logo)
.setLargeIcon(BitmapFactory.decodeResource(context.resources, R.mipmap.ic_logo))
.setContentTitle(title)
.setContentText(message)
.setSound(soundUri)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setAutoCancel(true)
.setContentIntent(pendingIntent)

paymentManager.notify(paymentId, builder.build())

if (payment?.remainingPayments!! >  0) {
scheduleNextReminder(context, title, message, paymentId, frequency, userViewModel)
}

}

private fun scheduleNextReminder(
context: Context,
title: String?,
message: String?,
paymentId: Int,
frequency: String,
userViewModel: UserViewModel
) {
val nextAlarmTime = calculateNextDueDate(System.currentTimeMillis(), frequency)

val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
val newIntent = Intent(context, PaymentReceiver::class.java).apply {
putExtra("TITLE", title)
putExtra("MESSAGE", message)
putExtra("paymentId", paymentId)
putExtra("FREQUENCY", frequency)
}

val pendingIntent = PendingIntent.getBroadcast(
context,
paymentId,
newIntent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
)

alarmManager.cancel(pendingIntent)

alarmManager.setExact(
AlarmManager.RTC_WAKEUP,
nextAlarmTime,
pendingIntent
)
Log.d("Receiver", "onReceive: *************")
userViewModel.processRegularPayments()
}

private val currentDate: Long get() = resetTimeToZero(System.currentTimeMillis())

private fun calculateNextDueDate(startDate: Long, frequency: String): Long {
val calendar = Calendar.getInstance().apply {
timeInMillis = startDate
}
if (startDate == currentDate) {
return currentDate
}

when (frequency) {
"Daily" -> calendar.add(Calendar.DAY_OF_YEAR, 1)
"Weekly" -> calendar.add(Calendar.WEEK_OF_YEAR, 1)
"Every two weeks" -> calendar.add(Calendar.WEEK_OF_YEAR, 2)
"Monthly" -> calendar.add(Calendar.MONTH, 1)
"Every two months" -> calendar.add(Calendar.MONTH, 2)
"Every three months" -> calendar.add(Calendar.MONTH, 3)
"Every four months" -> calendar.add(Calendar.MONTH, 4)
"Every six months" -> calendar.add(Calendar.MONTH, 6)
"Every year" ->  calendar.add(Calendar.YEAR, 1)
}

return Utils.resetTimeToZero(calendar.timeInMillis)
}

private fun cancelAlarm(context: Context, paymentId: Int) {
val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
val intent = Intent(context, PaymentReceiver::class.java)
val pendingIntent = PendingIntent.getBroadcast(
context,
paymentId,
intent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
)
alarmManager.cancel(pendingIntent)
Log.d("PaymentReceiver", "Alarm canceled for paymentId: $paymentId")
}

}
UserViewModel

Код: Выделить всё

class UserViewModel(application: Context) : ViewModel() {

val repository: UserRepository
private val mutex = Mutex()
private var isProcessingPayments = false

init {
val userDao = UserDatabase.getUserDatabase(application).userDao()
repository = UserRepository(userDao)
}

fun getRegularPaymentById(id: Long): RegularPayment? {
return repository.getRegularPaymentById(id)
}

fun getRegularPayments() {
viewModelScope.launch {
_getRegularPayments.value = repository.getAllRegularPayments()
}
}

fun insertOrUpdateRegularPayment(regularPayment: RegularPayment) {
viewModelScope.launch(Dispatchers.IO) {
val paymentId = repository.insertOrUpdateRegularPayment(regularPayment)
regularPayment.id = paymentId
setPayment(Application.getContext(), regularPayment)
}
}

fun setPayment(context: Context, regularPayment: RegularPayment) {

Log.d("UserView", "setPayment: ${regularPayment.id}")

val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
val intent = Intent(context, PaymentReceiver::class.java).apply {
putExtra("paymentId", regularPayment.id.toInt())
putExtra("TITLE", "Payment Added")
putExtra(
"MESSAGE",
"Added payment: ${regularPayment.paymentName} of ${regularPayment.amount}"
)
putExtra("FREQUENCY", regularPayment.frequency)
}

val pendingIntent = PendingIntent.getBroadcast(
context,
regularPayment.id.toInt(),
intent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
)

if (regularPayment.remainingPayments 
addPaymentToUserData(payment)
}

}
} finally {
isProcessingPayments = false
}
}
}

private fun shouldAddPayment(payment: RegularPayment): Boolean {
val dueDate = calculateNextDueDate(payment.startDate, payment.frequency)
return payment.remainingPayments > 0 &&  dueDate  calendar.add(Calendar.DAY_OF_YEAR, 1)
"Weekly" -> calendar.add(Calendar.WEEK_OF_YEAR, 1)
"Every two weeks" -> calendar.add(Calendar.WEEK_OF_YEAR, 2)
"Monthly" -> calendar.add(Calendar.MONTH, 1)
"Every two months" -> calendar.add(Calendar.MONTH, 2)
"Every three months" -> calendar.add(Calendar.MONTH, 3)
"Every four months" -> calendar.add(Calendar.MONTH, 4)
"Every six months" -> calendar.add(Calendar.MONTH, 6)
"Every year" -> calendar.add(Calendar.YEAR, 1)
}
return resetTimeToZero(calendar.timeInMillis)
}
ДАО

Код: Выделить всё

   @Upsert
suspend fun insertOrUpdateRegularPayment(regularPayment: RegularPayment):Long

@Query("SELECT * FROM regular_payment")
suspend fun getAllRegularPayments(): List

@Query("SELECT * FROM regular_payment WHERE id = :id")
fun getRegularPaymentById(id:Long): RegularPayment?

@Query("UPDATE regular_payment SET remainingPayments = :remaining WHERE id = :id")
suspend fun updateRemainingPayments(id: Long, remaining: Int)

@Query("""
SELECT
(SELECT COUNT(*) FROM user_data WHERE regularPaymentID = :paymentId AND expenseName = :paymentName AND date = :date) AS addedCount,
(SELECT COUNT(*) FROM deleted_payment WHERE paymentId = :paymentId AND deleteDate = :date) AS deletedCount
""")
fun isPaymentProcessable(paymentId: Long, paymentName: String, date: Long): ProcessableResult
  • Я пытался справиться с использованием isProcessingPayments в качестве флага.
  • Попробуйте отменить AlarmManager и запустить новый каждый раз.


Подробнее здесь: https://stackoverflow.com/questions/793 ... ular-payme
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «Android»