forked from erp-dev/erp
152 lines
4.8 KiB
Python
152 lines
4.8 KiB
Python
"""
|
||
手动触发日结任务(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}'
|
||
)
|