122 lines
3.9 KiB
Dart
122 lines
3.9 KiB
Dart
import 'dart:developer';
|
|
import 'dart:math' as math;
|
|
|
|
import 'package:isar/isar.dart';
|
|
import 'package:mnemo_cards_backend/cron/task.dart';
|
|
import 'package:mnemo_cards_common_backend/mnemo_cards_common_backend.dart';
|
|
|
|
import '../main.dart';
|
|
|
|
class GeneratePromocodes with Task {
|
|
final random = math.Random.secure();
|
|
|
|
@override
|
|
String get name => 'generate_promocodes';
|
|
|
|
@override
|
|
Future<void> task() async {
|
|
final campaigns = await isar.txn(() async {
|
|
return await isar.promoCodesCampaignModels
|
|
.filter()
|
|
.statusEqualTo(PromoCodeCampaignModelStatus.created)
|
|
.or()
|
|
.statusEqualTo(PromoCodeCampaignModelStatus.preparing)
|
|
.findAll();
|
|
});
|
|
await Future.wait([
|
|
for (final campaign in campaigns) _generateCampaignPromoCodes(campaign),
|
|
]);
|
|
}
|
|
|
|
static final _letters = 'CDFGJLNQRSUVWXYZ';
|
|
static final _digits = '123456789';
|
|
static final _symbols = _letters + _digits;
|
|
|
|
String get randomSymbol => _symbols[random.nextInt(_symbols.length)];
|
|
|
|
String get randomLetter => _letters[random.nextInt(_letters.length)];
|
|
|
|
String get randomDigit => _digits[random.nextInt(_digits.length)];
|
|
|
|
String _generateCode(String template) => template
|
|
.replaceAllMapped(r'$d', (_) => randomDigit)
|
|
.replaceAllMapped(r'$l', (_) => randomLetter)
|
|
.replaceAllMapped(r'$s', (_) => randomSymbol);
|
|
|
|
Future<void> _generateCampaignPromoCodes(
|
|
PromoCodesCampaignModel campaign,
|
|
) async {
|
|
print('Generating promo codes for campaign ${campaign.id}');
|
|
|
|
try {
|
|
if (campaign.status != PromoCodeCampaignModelStatus.preparing) {
|
|
await isar.writeTxn(() async {
|
|
isar.promoCodesCampaignModels.put(campaign.copyWith(
|
|
status: PromoCodeCampaignModelStatus.preparing));
|
|
});
|
|
}
|
|
final PAGE_INITIAL = 50;
|
|
int page = PAGE_INITIAL;
|
|
int leftToGenerate = campaign.generationSize - campaign.promoCodes.length;
|
|
while (leftToGenerate > 0) {
|
|
final willGenerate = math.min(leftToGenerate, page);
|
|
try {
|
|
await isar.writeTxn(() async {
|
|
final promoCodes = <PromoCodeModel>[];
|
|
for (int i = 0; i < willGenerate; i++) {
|
|
promoCodes.add(
|
|
PromoCodeModel(
|
|
code: _generateCode(campaign.template),
|
|
activations: 0,
|
|
)..campaign.value = campaign,
|
|
);
|
|
}
|
|
await isar.promoCodeModels.putAll(promoCodes);
|
|
await campaign.promoCodes.load();
|
|
campaign.promoCodes.addAll(promoCodes);
|
|
await campaign.promoCodes.save();
|
|
});
|
|
if (page < PAGE_INITIAL) {
|
|
page *= 2;
|
|
}
|
|
} on Object catch (e) {
|
|
if (e is IsarError &&
|
|
page > 1 &&
|
|
e.message.contains('Unique index violated')) {
|
|
page ~/= 2;
|
|
print(
|
|
'UniqueViolationError when generating codes for ${campaign.id}, retrying with page: $page',
|
|
);
|
|
continue;
|
|
} else {
|
|
rethrow;
|
|
}
|
|
}
|
|
leftToGenerate = campaign.generationSize - campaign.promoCodes.length;
|
|
}
|
|
final loadedCampaign =
|
|
await isar.promoCodesCampaignModels.get(campaign.id!);
|
|
if (loadedCampaign == null) {
|
|
log('Campaign not found in db: ${campaign.id}');
|
|
return;
|
|
}
|
|
if (loadedCampaign.promoCodes.length >= campaign.generationSize) {
|
|
await isar.writeTxn(
|
|
() async {
|
|
isar.promoCodesCampaignModels.put(
|
|
loadedCampaign.copyWith(
|
|
status: PromoCodeCampaignModelStatus.ready,
|
|
),
|
|
);
|
|
},
|
|
);
|
|
}
|
|
print('Finished generating promo codes for campaign ${campaign.id}');
|
|
} catch (e, s) {
|
|
print('Error when generating promo codes for ${campaign.id} $e $s');
|
|
}
|
|
}
|
|
|
|
@override
|
|
int get intervalSeconds => 10 * 60;
|
|
}
|