1
0
forked from erp-dev/erp

feat: new module for daily settlement

This commit is contained in:
2026-02-08 11:37:52 +08:00
parent 72597ce81f
commit c564b1af32
15 changed files with 991 additions and 0 deletions

View File

@@ -0,0 +1 @@
"""Settlement management commands"""

View File

@@ -0,0 +1 @@
"""Settlement management commands"""

View File

@@ -0,0 +1,151 @@
"""
手动触发日结任务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}'
)