""" 手动触发日结任务(management command) 用法: python manage.py run_daily_settlement 选项: --merchant-id: 仅处理指定商户ID --date: 指定结算日期(YYYY-MM-DD),默认为昨天 --sync: 同步执行(阻塞),默认为异步(通过 Celery) """ from datetime import datetime from django.core.management.base import BaseCommand, CommandError from django.db import transaction from django.utils import timezone from settlement.models import DailySettlementConfig class Command(BaseCommand): help = '手动触发日结任务(支持指定商户、日期、同步/异步执行)' def add_arguments(self, parser): parser.add_argument( '--merchant-id', type=int, help='仅处理指定商户ID', ) parser.add_argument( '--date', type=str, help='指定结算日期(YYYY-MM-DD),默认为昨天', ) parser.add_argument( '--sync', action='store_true', help='同步执行(阻塞),默认为异步(通过 Celery)', ) def handle(self, *args, **options): from settlement.tasks import run_daily_settlement, run_merchant_daily_settlement merchant_id = options.get('merchant_id') date_str = options.get('date') sync_mode = bool(options['sync']) # 解析日期 if date_str: try: date_obj = datetime.strptime(date_str, '%Y-%m-%d').date() except ValueError as e: raise CommandError(f'--date 必须为 YYYY-MM-DD 格式: {e}') else: date_obj = timezone.localdate() - timezone.timedelta(days=1) self.stdout.write(f'[日结] 开始执行: 日期={date_obj}, 同步模式={sync_mode}') if merchant_id: # 处理单个商户 self._handle_single_merchant( merchant_id=merchant_id, date_obj=date_obj, sync_mode=sync_mode ) else: # 处理所有商户 self._handle_all_merchants( date_obj=date_obj, sync_mode=sync_mode ) def _handle_single_merchant(self, merchant_id: int, date_obj, sync_mode: bool): """处理单个商户的日结""" from settlement.tasks import run_merchant_daily_settlement try: config = DailySettlementConfig.objects.select_related('merchant').get( merchant_id=merchant_id ) except DailySettlementConfig.DoesNotExist: self.stderr.write( self.style.ERROR( f'商户 {merchant_id} 未配置日结,跳过' ) ) return if not config.settlement_modules: self.stdout.write( f'商户 {config.merchant.name} (ID={merchant_id}) 未配置统计模块,跳过' ) return self.stdout.write( f'处理商户: {config.merchant.name} (ID={merchant_id}), ' f'模块={config.settlement_modules}' ) if sync_mode: # 同步执行 result = run_merchant_daily_settlement( merchant_id=merchant_id, settlement_date=str(date_obj) ) self.stdout.write( self.style.SUCCESS( f'商户 {merchant_id} 日结完成: {result}' ) ) else: # 异步执行(通过 Celery) task = run_merchant_daily_settlement.delay( merchant_id=merchant_id, settlement_date=str(date_obj) ) self.stdout.write( f'商户 {merchant_id} 日结任务已提交: task_id={task.id}' ) def _handle_all_merchants(self, date_obj, sync_mode: bool): """处理所有商户的日结""" from settlement.tasks import run_daily_settlement configs = DailySettlementConfig.objects.filter( settlement_modules__len__gt=0 ).select_related('merchant') if not configs.exists(): self.stdout.write('没有配置日结的商户,跳过') return self.stdout.write( f'共 {configs.count()} 个商户需要执行日结' ) if sync_mode: # 同步执行(逐个商户) for config in configs: self._handle_single_merchant( merchant_id=config.merchant.id, date_obj=date_obj, sync_mode=True ) else: # 异步执行(通过 Celery) task = run_daily_settlement.delay() self.stdout.write( f'所有商户日结任务已提交: task_id={task.id}' )