feat: 提现驳回可选返还手续费与入账冻结(单笔/批量)
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -68,7 +68,8 @@ from ..models import (
|
||||
)
|
||||
from users.business_models import User
|
||||
from ..tixian_shenhe_services import (
|
||||
load_audit_meta_map, refund_balance, sync_audit_and_jilu_status,
|
||||
load_audit_meta_map, refund_balance, refund_withdraw_reject,
|
||||
estimate_manual_shouxufei, sync_audit_and_jilu_status,
|
||||
mark_transfer_success, release_collect_quota_for_record,
|
||||
close_audit_wechat_failed, query_wechat_transfer_bill,
|
||||
check_shijidaozhang_within_limit,
|
||||
@@ -337,8 +338,9 @@ class KefuWithdrawListView(APIView):
|
||||
for item in records:
|
||||
shenhe_jilu_id = getattr(item, 'shenhe_jilu_id', None) if shenhe_field_ok else None
|
||||
shenhe_danhao = (getattr(item, 'shenhe_danhao', None) or '') if shenhe_field_ok else ''
|
||||
meta = audit_meta_map.get(shenhe_jilu_id, {}) if shenhe_jilu_id else {}
|
||||
if shenhe_jilu_id and not shenhe_danhao:
|
||||
shenhe_danhao = audit_meta_map.get(shenhe_jilu_id, {}).get('shenhe_danhao', '')
|
||||
shenhe_danhao = meta.get('shenhe_danhao', '')
|
||||
list_data.append({
|
||||
'id': item.id,
|
||||
'shenhe_jilu_id': shenhe_jilu_id,
|
||||
@@ -351,6 +353,8 @@ class KefuWithdrawListView(APIView):
|
||||
'zhifu': item.zhifu or '',
|
||||
'skzhanghao': item.skzhanghao or '',
|
||||
'jine': str(item.jine) if item.jine is not None else '0.00',
|
||||
'shouxufei': meta.get('shouxufei', '0.00') if shenhe_jilu_id else '0.00',
|
||||
'shijidaozhang': meta.get('shijidaozhang') or (str(item.jine) if item.jine is not None else '0.00'),
|
||||
'zhuangtai': item.zhuangtai,
|
||||
'fangshi': item.fangshi,
|
||||
'dakuan_mode': self._resolve_row_dakuan_mode(item, audit_mode_map, shenhe_field_ok),
|
||||
@@ -532,13 +536,32 @@ class KefuWithdrawActionView(APIView):
|
||||
单条:{ phone, action, tixian_id, yonghuid, leixing, reason? }
|
||||
批量:{ phone, action, batch_list: [{tixian_id, yonghuid, leixing}, ...], reason? }
|
||||
|
||||
自动打款(dakuan_mode=2):同意→6待收款;拒绝→5+驳回原因+退还可到账金额(shijidaozhang),手续费不退,不更新平台收支/每日统计
|
||||
驳回(action=2)可选(默认 false,与现网一致):
|
||||
refund_fee: 是否退还手续费
|
||||
apply_entry_freeze: 退回余额是否走入账冻结(按俱乐部配置;押金/商家无效)
|
||||
|
||||
自动打款(dakuan_mode=2):同意→6待收款;拒绝→5+驳回原因+退款,不更新平台收支/每日统计
|
||||
手动打款(dakuan_mode=1):原逻辑不变
|
||||
状态要求:Tixianjilu.zhuangtai=1 且 TixianShenheJilu.zhuangtai=1(审核中),其他状态一律拒绝
|
||||
"""
|
||||
permission_classes = [IsAuthenticated]
|
||||
parser_classes = [JSONParser]
|
||||
|
||||
@staticmethod
|
||||
def _parse_bool(raw, default=False):
|
||||
if raw is None:
|
||||
return bool(default)
|
||||
if isinstance(raw, bool):
|
||||
return raw
|
||||
if isinstance(raw, (int, float)):
|
||||
return bool(raw)
|
||||
s = str(raw).strip().lower()
|
||||
if s in ('1', 'true', 'yes', 'y', 'on'):
|
||||
return True
|
||||
if s in ('0', 'false', 'no', 'n', 'off', ''):
|
||||
return False
|
||||
return bool(default)
|
||||
|
||||
def _verify_kefu(self, request, phone):
|
||||
"""与 menu-access / verify_kefu_permission 对齐:按后台账号放行,不因 UserType 推断失败而 401。"""
|
||||
from jituan.services.admin_context import is_kefu_backend_account, is_system_super_admin
|
||||
@@ -598,7 +621,8 @@ class KefuWithdrawActionView(APIView):
|
||||
return None, Response({'code': 400, 'msg': '参数格式错误'}, status=status.HTTP_400_BAD_REQUEST)
|
||||
return [item], None
|
||||
|
||||
def _process_auto_item(self, request, item, action, reason, kefu_user):
|
||||
def _process_auto_item(self, request, item, action, reason, kefu_user,
|
||||
refund_fee=False, apply_entry_freeze=False):
|
||||
"""自动打款单条处理(须在 transaction.atomic 内调用)"""
|
||||
tixian_id = item['tixian_id']
|
||||
req_yonghuid = item['yonghuid']
|
||||
@@ -651,6 +675,7 @@ class KefuWithdrawActionView(APIView):
|
||||
bhliyou=limit_msg,
|
||||
bo_hui_ren_id=kefu_user.UserUID,
|
||||
)
|
||||
# 超限自动驳回:保持现网(不退手续费、不走冻结)
|
||||
refund_balance(user, audit.leixing, audit.shijidaozhang)
|
||||
tixian.shenheid = kefu_user.UserUID
|
||||
tixian.bhliyou = limit_msg
|
||||
@@ -665,13 +690,22 @@ class KefuWithdrawActionView(APIView):
|
||||
bhliyou=reason,
|
||||
bo_hui_ren_id=kefu_user.UserUID,
|
||||
)
|
||||
# 退还可到账金额,申请时扣的 shenqing_jine 中的手续费(shouxufei)不退
|
||||
refund_balance(user, audit.leixing, audit.shijidaozhang)
|
||||
refund_withdraw_reject(
|
||||
user,
|
||||
audit.leixing,
|
||||
shijidaozhang=audit.shijidaozhang,
|
||||
shouxufei=audit.shouxufei,
|
||||
refund_fee=refund_fee,
|
||||
apply_entry_freeze=apply_entry_freeze,
|
||||
biz_id=f'withdraw_reject:auto:{tixian_id}',
|
||||
freeze_reason=f'提现驳回退款 #{tixian_id}',
|
||||
)
|
||||
tixian.shenheid = kefu_user.UserUID
|
||||
tixian.bhliyou = reason
|
||||
tixian.save(update_fields=['shenheid', 'bhliyou', 'UpdateTime'])
|
||||
|
||||
def _process_manual_item(self, request, item, action, reason, phone):
|
||||
def _process_manual_item(self, request, item, action, reason, phone,
|
||||
refund_fee=False, apply_entry_freeze=False):
|
||||
"""手动打款单条处理(原逻辑,须在 transaction.atomic 内调用)"""
|
||||
tixian_id = item['tixian_id']
|
||||
try:
|
||||
@@ -713,8 +747,18 @@ class KefuWithdrawActionView(APIView):
|
||||
except Exception as e:
|
||||
logger.error(f'更新收支记录失败: {e}')
|
||||
else:
|
||||
fee = estimate_manual_shouxufei(user, tixian.leixing, tixian.jine) if refund_fee else Decimal('0')
|
||||
try:
|
||||
refund_balance(user, tixian.leixing, tixian.jine)
|
||||
refund_withdraw_reject(
|
||||
user,
|
||||
tixian.leixing,
|
||||
shijidaozhang=tixian.jine,
|
||||
shouxufei=fee,
|
||||
refund_fee=refund_fee,
|
||||
apply_entry_freeze=apply_entry_freeze,
|
||||
biz_id=f'withdraw_reject:manual:{tixian_id}',
|
||||
freeze_reason=f'提现驳回退款 #{tixian_id}',
|
||||
)
|
||||
except ValueError as e:
|
||||
raise ValueError(str(e))
|
||||
tixian.zhuangtai = 3
|
||||
@@ -730,6 +774,8 @@ class KefuWithdrawActionView(APIView):
|
||||
phone = request.data.get('phone', '').strip()
|
||||
action_raw = request.data.get('action')
|
||||
reason = request.data.get('reason', '').strip()
|
||||
refund_fee = self._parse_bool(request.data.get('refund_fee'), False)
|
||||
apply_entry_freeze = self._parse_bool(request.data.get('apply_entry_freeze'), False)
|
||||
|
||||
try:
|
||||
action = int(action_raw)
|
||||
@@ -773,11 +819,19 @@ class KefuWithdrawActionView(APIView):
|
||||
is_auto = bool(getattr(tixian_probe, 'shenhe_jilu_id', None))
|
||||
|
||||
if is_auto:
|
||||
self._process_auto_item(request, item, action, reason, kefu_user)
|
||||
self._process_auto_item(
|
||||
request, item, action, reason, kefu_user,
|
||||
refund_fee=refund_fee if action == 2 else False,
|
||||
apply_entry_freeze=apply_entry_freeze if action == 2 else False,
|
||||
)
|
||||
else:
|
||||
if is_batch:
|
||||
raise ValueError(f'提现记录{tixian_id}:批量操作仅支持自动打款记录')
|
||||
self._process_manual_item(request, item, action, reason, phone)
|
||||
self._process_manual_item(
|
||||
request, item, action, reason, phone,
|
||||
refund_fee=refund_fee if action == 2 else False,
|
||||
apply_entry_freeze=apply_entry_freeze if action == 2 else False,
|
||||
)
|
||||
|
||||
except ValueError as e:
|
||||
return Response({'code': 400, 'msg': str(e)}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
Reference in New Issue
Block a user