我有一个系统,所有交易金额都以整数(分)存储。 到目前为止,该功能运行良好,已记录了超过 2000 万笔交易。
但是,我最近遇到了一个用例,当转换为美分后,等效整数值变为零,从而导致少量问题
# Models
class Wallet(models.Model):
balance = models.IntegerField()
class WalletTransaction(models.Model):
wallet = models.ForeignKey(Wallet, on_delete=models.CASCADE)
amount = models.IntegerField() # Stored in cents
transaction_type = models.CharField(max_length=50)
# Updating wallet balance and creating transaction
cost_price = 0.0025
cost_price_in_cents = cost_price * 100 # Converts to 0.25 cents
wallet = Wallet.objects.get(id=1)
wallet.balance -= cost_price_in_cents
wallet.save()
WalletTransaction.objects.create(wallet=wallet, amount=cost_price_in_cents)
这里余额被扣除0.25,但在钱包交易历史中它是0。
您应该转换到小数字段。使用当前的解决方案,您可以通过 2 个步骤来完成此操作:
class Wallet(models.Model):
balance = models.IntegerField()
balance_decimal = models.DecimalField(null=True)
class WalletTransaction(models.Model):
wallet = models.ForeignKey(Wallet, on_delete=models.CASCADE)
amount = models.IntegerField() # Stored in cents
amount_decimal = models.DecimalField(null=True)
transaction_type = models.CharField(max_length=50)
并且不要忘记覆盖钱包和交易的逻辑:
cost_price = 0.0025
cost_price_in_cents = cost_price * 100
wallet = Wallet.objects.get(id=1)
wallet.balance -= cost_price_in_cents
wallet.balance_decimal -= cost_price
wallet.save()
WalletTransaction.objects.create(
wallet=wallet,
amount=cost_price_in_cents,
amount_decimal=cost_price
)
from django.db import migrations, transaction
def populate_decimal_fields(apps, schema_editor):
Wallet = apps.get_model('your_app_name', 'Wallet')
WalletTransaction = apps.get_model('your_app_name', 'WalletTransaction')
with transaction.atomic():
for wallet in Wallet.objects.select_for_update():
wallet.balance_decimal = wallet.balance/100
wallet.save(update_fields=["balance_decimal"])
for transaction in WalletTransaction.objects.select_for_update():
transaction.amount_decimal = transaction.amount/ 100
transaction.save(update_fields=["amount_decimal"])
class Migration(migrations.Migration):
dependencies = [
('your_app_name', 'previous_migration_file'),
]
operations = [
migrations.AddField(
model_name='wallet',
name='balance_decimal',
field=models.DecimalField(max_digits=12, decimal_places=2, null=True),
),
migrations.AddField(
model_name='wallettransaction',
name='amount_decimal',
field=models.DecimalField(max_digits=12, decimal_places=2, null=True),
),
migrations.RunPython(populate_decimal_fields),
migrations.AlterField(
model_name='wallet',
name='balance',
field=models.IntegerField(null=True),
),
migrations.AlterField(
model_name='wallettransaction',
name='amount',
field=models.IntegerField(null=True),
),
]
注意:它没有得到很好的优化,因为所有钱包和交易都被 select_for_update 阻止