150 lines
4.6 KiB
Python
150 lines
4.6 KiB
Python
"""各俱乐部全局收支流水(szjilu)统一记账。"""
|
||
import logging
|
||
from decimal import Decimal
|
||
|
||
from django.db import IntegrityError, transaction
|
||
from django.db.utils import OperationalError, ProgrammingError
|
||
|
||
from config.models import PlatformIncomeLog, Szjilu
|
||
from jituan.constants import CLUB_ID_DEFAULT
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
_ZERO = Decimal('0.00')
|
||
_SZJILU_DEFAULTS = {
|
||
'TotalIncome': _ZERO,
|
||
'TotalFlow': _ZERO,
|
||
'TotalExpense': _ZERO,
|
||
'DailyExpense': _ZERO,
|
||
'DailyFlow': _ZERO,
|
||
}
|
||
|
||
_PLATFORM_LOG_ERRORS = (ProgrammingError, OperationalError)
|
||
|
||
|
||
def normalize_szjilu_club_id(club_id):
|
||
return (club_id or '').strip() or CLUB_ID_DEFAULT
|
||
|
||
|
||
def _get_szjilu_for_update(club_id):
|
||
cid = normalize_szjilu_club_id(club_id)
|
||
szjilu, _ = Szjilu.objects.select_for_update().get_or_create(
|
||
club_id=cid,
|
||
defaults=dict(_SZJILU_DEFAULTS),
|
||
)
|
||
return szjilu
|
||
|
||
|
||
def apply_szjilu_income(amount, club_id=None):
|
||
"""平台收入:总收益、总流水、今日流水增加。"""
|
||
jine = Decimal(str(amount))
|
||
with transaction.atomic():
|
||
szjilu = _get_szjilu_for_update(club_id)
|
||
szjilu.TotalIncome += jine
|
||
szjilu.TotalFlow += jine
|
||
szjilu.DailyFlow += jine
|
||
szjilu.save()
|
||
|
||
|
||
def _apply_income_stats(amount, club_id):
|
||
"""写入 daily_income_stat + szjilu(核心统计,必须成功)。"""
|
||
from orders.utils import update_daily_income
|
||
|
||
cid = normalize_szjilu_club_id(club_id)
|
||
jine = Decimal(str(amount))
|
||
update_daily_income(jine, cid)
|
||
apply_szjilu_income(jine, cid)
|
||
|
||
|
||
def record_wechat_income_once(amount, club_id=None, biz_ref=None):
|
||
"""
|
||
微信入账:同一 biz_ref 只记一次 daily_income_stat + szjilu。
|
||
返回 True=本次新入账;False=已记过(重复回调)。
|
||
若 platform_income_log 表不可用,仍记入收入(与改造前一致,不能因幂等表故障丢统计)。
|
||
"""
|
||
ref = (biz_ref or '').strip()
|
||
if not ref:
|
||
raise ValueError('biz_ref 不能为空')
|
||
cid = normalize_szjilu_club_id(club_id)
|
||
jine = Decimal(str(amount))
|
||
|
||
try:
|
||
with transaction.atomic():
|
||
PlatformIncomeLog.objects.create(
|
||
biz_ref=ref,
|
||
club_id=cid,
|
||
amount=jine,
|
||
)
|
||
except IntegrityError:
|
||
logger.info('平台入账已存在,跳过重复记账 biz_ref=%s', ref)
|
||
return False
|
||
except _PLATFORM_LOG_ERRORS as exc:
|
||
logger.warning(
|
||
'platform_income_log 不可用(%s),跳过幂等直接记入收入 biz_ref=%s',
|
||
exc, ref,
|
||
)
|
||
_apply_income_stats(jine, cid)
|
||
return True
|
||
except Exception as exc:
|
||
logger.error(
|
||
'platform_income_log 写入异常,仍记入收入 biz_ref=%s err=%s',
|
||
ref, exc, exc_info=True,
|
||
)
|
||
_apply_income_stats(jine, cid)
|
||
return True
|
||
|
||
try:
|
||
_apply_income_stats(jine, cid)
|
||
except Exception:
|
||
try:
|
||
PlatformIncomeLog.objects.filter(biz_ref=ref).delete()
|
||
except Exception:
|
||
pass
|
||
raise
|
||
logger.info('平台入账记账成功 biz_ref=%s club=%s amount=%s', ref, cid, jine)
|
||
return True
|
||
|
||
|
||
def apply_szjilu_expense(amount, club_id=None):
|
||
"""平台出款/支出:总收益减少,总支出、今日支出增加。"""
|
||
jine = Decimal(str(amount))
|
||
with transaction.atomic():
|
||
szjilu = _get_szjilu_for_update(club_id)
|
||
szjilu.TotalIncome -= jine
|
||
szjilu.TotalExpense += jine
|
||
szjilu.DailyExpense += jine
|
||
szjilu.save()
|
||
|
||
|
||
def get_szjilu_snapshot(club_id=None):
|
||
"""读取某俱乐部收支汇总;无记录时返回零值。"""
|
||
cid = normalize_szjilu_club_id(club_id)
|
||
row = Szjilu.query.filter(club_id=cid).first()
|
||
if not row:
|
||
return {
|
||
'zongliushui': 0.0,
|
||
'zongshouyi': 0.0,
|
||
'zongzhichu': 0.0,
|
||
'jrls': 0.0,
|
||
'jrzc': 0.0,
|
||
}
|
||
return {
|
||
'zongliushui': float(row.TotalFlow or _ZERO),
|
||
'zongshouyi': float(row.TotalIncome or _ZERO),
|
||
'zongzhichu': float(row.TotalExpense or _ZERO),
|
||
'jrls': float(row.DailyFlow or _ZERO),
|
||
'jrzc': float(row.DailyExpense or _ZERO),
|
||
}
|
||
|
||
|
||
def reset_all_daily_szjilu():
|
||
"""每日清零:所有俱乐部的今日流水/今日支出。"""
|
||
updated = 0
|
||
with transaction.atomic():
|
||
for szjilu in Szjilu.objects.select_for_update().all():
|
||
szjilu.DailyExpense = _ZERO
|
||
szjilu.DailyFlow = _ZERO
|
||
szjilu.save(update_fields=['DailyExpense', 'DailyFlow', 'UpdateTime'])
|
||
updated += 1
|
||
return updated
|