Files
Django/users/views/shangjia.py

735 lines
31 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""users.views.shangjia - auto-generated by split script."""
import os
import sys
import json
import re
import time
import uuid
import random
import string
import requests
import hashlib
import traceback
import decimal
import jwt
import ipaddress
import xmltodict
from datetime import datetime, timedelta
from decimal import Decimal
import defusedxml.ElementTree as ET
from django.conf import settings
from django.db import models, transaction, IntegrityError, DatabaseError, connection
from django.db.models import Q, F, Sum, Count, Case, When, IntegerField, Prefetch
from django.core.cache import cache
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.core.exceptions import ObjectDoesNotExist
from django.shortcuts import get_object_or_404
from django.utils import timezone
from django.http import HttpResponse
from django.views import View
from django.views.decorators.csrf import csrf_exempt
from django.contrib.auth.hashers import make_password
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import permissions, status
from rest_framework.permissions import AllowAny, IsAuthenticated
from rest_framework.parsers import MultiPartParser, FormParser, JSONParser
from rest_framework.throttling import AnonRateThrottle
from rest_framework_simplejwt.tokens import RefreshToken
from rest_framework_simplejwt.authentication import JWTAuthentication
from gvsdsdk.fluent import db, func, FQ
from utils.oss_utils import validate_image, upload_to_oss, delete_from_oss
from utils.money import yuan_to_fen
from utils.fadan_utils import check_fadan_qiangdan_eligible
from utils.invitationcode_utils import CreateInvitationCode, VerifyInvitationCode
from users.fadan_fenhong_utils import process_fadan_fenhong
from orders.utils import (
update_daily_payout,
settle_shangjia_order_guanshi_fenhong
)
from backend.utils import (
update_dashou_daily_by_action, update_guanshi_daily_by_action,
update_shangjia_daily, update_zuzhang_daily_by_action,
verify_kefu_permission
)
from rank.utils import get_tag_fee, create_shenhe_jilu, validate_shenheguan
from ..models import (
UserBoss, UserDashou, UserShangjia, UserGuanshi,
UserZuzhang, UserKefu, AdminProfile, OfficialAccountUser,
TixianAutoRecord, TixianShenheJilu, Tixianjilu, Xiugaijilu,
RankingRecord
)
from users.business_models import User
from ..tixian_shenhe_services import (
load_audit_meta_map, refund_balance, 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,
check_dashou_trial_blocks_commission_withdraw,
WX_STATE_FAIL, WX_STATE_SUCCESS, WX_STATE_WAIT, WX_STATE_CANCELING,
)
from ..tixian_shenhe_views import process_audit_collect
from .chufa import DashouPermission
from orders.models import (
Order, PlatformOrderExt, MerchantOrderExt, CommissionRate,
PenaltyRecord, PenaltyEvidenceImage, RefundRecord, PlayerDeliveryImage,
Penalty, PenaltyAppealImage
)
from products.models import (
ShangpinLeixing, Huiyuan, Huiyuangoumai, Gsfenhong, Czjilu
)
from config.models import Qunpeizhi
from backend.models import MerchantDailyStats
from rank.models import (
KaohePayTemp, Chenghao, ShenheJilu,
KaoheCishuFeiyong, YonghuChenghao
)
from users.models import UserShenheguan
from products.utils import update_shangpin_daily_stat
from shop.utils import update_dianpu_daily_stat
from rank.utils import create_shenhe_jilu_from_temp
import logging
logger = logging.getLogger(__name__)
class ShangjiaZhuceView(APIView):
"""
商家注册接口 (通过管理员邀请码)
访问路径: /api/yonghu/shangjiahuce/
请求方法: POST
权限要求: JWT Token认证通过的用户
请求体: { "inviteCode": "管理员邀请码" }
返回字段说明:
- code: 状态码200成功其他失败
- message: 提示信息
- data: 商家信息数据,包含以下字段(与前端商家页面完全一致):
- nicheng: 商家昵称
- sjyue: 商家余额(字符串,保留两位小数)
- fabu: 发布订单总数
- chengjiao: 成交订单总数
- tuikuan: 退款订单总数
- jinridingdan: 今日订单
- jinriliushui: 今日流水(字符串)
- jinyuedingdan: 今月订单
- jinyueliushui: 今月流水(字符串)
- zhuangtai: 商家状态1正常
- dianhua: 商家电话
- wechat: 商家微信
"""
permission_classes = [IsAuthenticated]
def post(self, request):
# 1. 获取并验证邀请码参数
yaoqingma = request.data.get('inviteCode')
if not yaoqingma:
return Response({
'code': 400,
'message': '邀请码不能为空',
'data': None
}, status=status.HTTP_400_BAD_REQUEST)
yaoqingma = yaoqingma.strip()
if len(yaoqingma) > 100:
return Response({
'code': 400,
'message': '邀请码长度超过限制',
'data': None
}, status=status.HTTP_400_BAD_REQUEST)
current_user = request.user
# 2. 检查用户是否已是商家
try:
shangjia_profile = current_user.ShopProfile
# 用户已是商家,直接返回现有信息
return self.fanhuiXianyouShangjiaXinxi(shangjia_profile, current_user)
except UserShangjia.DoesNotExist:
# 用户不是商家,继续注册流程
pass
# 2.5 子客服身份互斥:有效客服不可认证商家
from merchant_ops.services.authz import assert_no_active_staff_when_merchant_register
try:
assert_no_active_staff_when_merchant_register(current_user)
except Exception as e:
from merchant_ops.services.authz import StaffAuthError
if isinstance(e, StaffAuthError):
return Response({'code': 403, 'message': e.msg, 'data': None}, status=status.HTTP_403_FORBIDDEN)
raise
# 3. 根据邀请码查找对应的管理员
try:
# 从管理员表查找邀请码
admin_profile = AdminProfile.query.select_related('user').get(yaoqingma=yaoqingma)
except AdminProfile.DoesNotExist:
return Response({
'code': 404,
'message': '管理员邀请码无效或不存在',
'data': None
}, status=status.HTTP_404_NOT_FOUND)
# 4. 核心:在数据库事务中创建商家身份
try:
with transaction.atomic():
# 4.1 查询老板扩展表获取老板昵称
boss_nickname = '普通商家'
try:
boss_profile = current_user.BossProfile
if boss_profile.nickname and boss_profile.nickname.strip():
boss_nickname = boss_profile.nickname.strip()
except UserBoss.DoesNotExist:
# 老板扩展表不存在,使用默认值
pass
# 4.2 创建商家扩展表
# 使用老板昵称作为商家昵称的基础
shangjia_nicheng = boss_nickname
# 检查昵称是否已存在(安全考虑)
if UserShangjia.query.filter(nicheng=shangjia_nicheng).exists():
# 如果存在,添加随机数确保唯一
shangjia_nicheng = f"{boss_nickname}{random.randint(1000, 9999)}"
# 4.3 创建商家记录
shop_profile = UserShangjia.query.create(
user=current_user,
nicheng=shangjia_nicheng,
zhuangtai=1, # 正常状态
yue=0.00, # 商家余额
fabu=0, # 发布订单总数
tuikuan=0, # 退款订单总数
chengjiao=0, # 成交订单总数
jinridingdan=0, # 今日订单
jinriliushui=0.00, # 今日流水
jinyuedingdan=0, # 今月订单
jinyueliushui=0.00, # 今月流水
dianhua='', # 电话
wechat='', # 微信
)
except Exception as e:
# 事务会自动回滚
error_detail = traceback.format_exc()
#print(f"商家注册错误详情: {error_detail}")
return Response({
'code': 500,
'message': f'商家注册过程中发生系统错误: {str(e)}',
'data': None
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
# 5. 注册成功,返回商家信息
fanhui_data = self.zhuangbeiFanhuiShuju(shop_profile, current_user)
return Response({
'code': 200,
'message': '商家注册成功!',
'data': fanhui_data
})
def fanhuiXianyouShangjiaXinxi(self, shangjia_profile, current_user):
"""处理已是商家的用户,返回其现有信息"""
fanhui_data = self.zhuangbeiFanhuiShuju(shangjia_profile, current_user)
return Response({
'code': 200,
'message': '您已是商家,返回当前信息。',
'data': fanhui_data
})
def zhuangbeiFanhuiShuju(self, shangjia_profile, current_user):
"""
准备返回给前端的商家数据
字段名必须与前端商家页面完全一致
"""
data = {
# 🟢 商家基本信息
'nicheng': shangjia_profile.nicheng, # 商家昵称
'sjyue': format(shangjia_profile.yue, '.2f'), # 商家余额,保留两位小数
'fabu': shangjia_profile.fabu, # 发布订单总数
'chengjiao': shangjia_profile.chengjiao, # 成交订单总数
'tuikuan': shangjia_profile.tuikuan, # 退款订单总数
'jinridingdan': shangjia_profile.jinridingdan, # 今日订单
'jinriliushui': format(shangjia_profile.jinriliushui, '.2f'), # 今日流水
'jinyuedingdan': shangjia_profile.jinyuedingdan, # 今月订单
'jinyueliushui': format(shangjia_profile.jinyueliushui, '.2f'), # 今月流水
'zhuangtai': shangjia_profile.zhuangtai, # 商家状态
'dianhua': shangjia_profile.dianhua or '', # 电话
'wechat': shangjia_profile.wechat or '', # 微信
# 🟢 可能需要的主表信息
'uid': current_user.UserUID, # 用户ID
}
return data
class FaKuanJiFenTongJiView(APIView):
"""
获取罚款与积分处罚统计总览
路径: POST /yonghu/cffktjhq
"""
permission_classes = [IsAuthenticated, DashouPermission]
def post(self, request):
try:
user_main = request.user
yonghuid = user_main.UserUID
# ================= 罚款统计 =================
fadan_stats = Penalty.query.filter(PenalizedUserID=yonghuid).aggregate(
total=Count('id'),
daijiaona=Count('id', filter=Q(Status=1)),
shensuzhong=Count('id', filter=Q(Status=3)),
pingtai_shenhe=Count('id', filter=Q(Status=5)),
yijiaona=Count('id', filter=Q(Status=2)),
yibohui=Count('id', filter=Q(Status=4)),
)
# ================= 积分处罚统计 =================
jifen_stats = PenaltyRecord.query.filter(PlayerID=yonghuid).aggregate(
total=Count('id'),
daichuli=Count('id', filter=Q(ApplyStatus=0) | Q(ApplyStatus=3)),
yichuli=Count('id', filter=Q(ApplyStatus=1) | Q(ApplyStatus=2)),
# 细分
daichuli_0=Count('id', filter=Q(ApplyStatus=0)),
shensuzhong_3=Count('id', filter=Q(ApplyStatus=3)),
yichufa_1=Count('id', filter=Q(ApplyStatus=1)),
yibohui_2=Count('id', filter=Q(ApplyStatus=2)),
)
data = {
"fakuan": {
"total": fadan_stats['total'],
"daijiaona": fadan_stats['daijiaona'],
"shensuzhong": fadan_stats['shensuzhong'],
"pingtai_shenhe": fadan_stats['pingtai_shenhe'],
"yijiaona": fadan_stats['yijiaona'],
"yibohui": fadan_stats['yibohui'],
},
"jifen": {
"total": jifen_stats['total'],
"daichuli": jifen_stats['daichuli'],
"yichuli": jifen_stats['yichuli'],
"daichuli_0": jifen_stats['daichuli_0'],
"shensuzhong_3": jifen_stats['shensuzhong_3'],
"yichufa_1": jifen_stats['yichufa_1'],
"yibohui_2": jifen_stats['yibohui_2'],
}
}
return Response({'code': 0, 'msg': '获取统计成功', 'data': data})
except Exception as e:
logger.error(f"获取统计异常 {yonghuid}: {e}", exc_info=True)
return Response({'code': 99, 'msg': '系统繁忙'}, status=500)
class DaShouFaKuanLieBiaoView(APIView):
"""
打手罚款列表接口
路径POST /yonghu/dsfklbhq
参数:
page: 页码默认1
page_size: 每页条数默认10最大50
zhuangtai: 状态筛选1待缴纳/2已缴纳/3申诉中/4申诉成功0或不传表示全部
sousuo_dingdan_id: 订单号模糊搜索
返回:{
list: [{
id, beichufa_id, guanliandingdan_id, chufaliyou, fakuanjine, zhuangtai,
yingxiang_qiangdan, shensuliyou, bohuiliyou,
zhengju_tupian: [...], // 证据图片yongtu=1
shensu_tupian: [...], // 申诉图片yongtu=2
CreateTime, UpdateTime
}],
has_more, page, page_size
}
"""
permission_classes = [IsAuthenticated, DashouPermission]
def post(self, request):
try:
user_main = request.user
yonghuid = user_main.UserUID
# 获取分页参数
page = int(request.data.get('page', 1))
page_size = int(request.data.get('page_size', 10))
if page_size > 50:
page_size = 50
if page_size < 1:
page_size = 10
# 状态筛选
zhuangtai = request.data.get('zhuangtai')
# 订单号搜索
sousuo_dingdan_id = request.data.get('sousuo_dingdan_id', '').strip()
# 基础查询:当前用户的罚单
qs = Penalty.query.filter(PenalizedUserID=yonghuid)
# 状态筛选
if zhuangtai is not None and int(zhuangtai) in [1, 2, 3, 4, 5]:
qs = qs.filter(Status=int(zhuangtai))
# 订单号模糊搜索
if sousuo_dingdan_id:
qs = qs.filter(RelatedOrderID__icontains=sousuo_dingdan_id)
# 按创建时间倒序
qs = qs.order_by('-CreateTime')
# 分页
paginator = Paginator(qs, page_size)
try:
page_obj = paginator.page(page)
except Exception:
return Response({
'code': 0, 'msg': '获取成功',
'data': {'list': [], 'has_more': False, 'page': page, 'page_size': page_size}
})
records = list(page_obj)
fadan_ids = [r.id for r in records]
# 批量获取图片,按用途分组
zhengju_map = {} # key: fadan_id, value: [url, ...]
shensu_map = {}
if fadan_ids:
tupians = PenaltyAppealImage.query.filter(
Penalty_id__in=fadan_ids
).values('Penalty_id', 'ImageURL', 'Purpose')
for t in tupians:
fid = t['Penalty_id']
url = t['ImageURL']
if t['Purpose'] == 1:
zhengju_map.setdefault(fid, []).append(url)
else:
shensu_map.setdefault(fid, []).append(url)
# 组装返回数据
chufa_list = []
for r in records:
rid = r.id
item = {
'id': rid,
'beichufa_id': r.PenalizedUserID,
'guanliandingdan_id': r.RelatedOrderID or '',
'chufaliyou': r.Reason or '',
'fakuanjine': str(r.FineAmount),
'zhuangtai': r.Status,
'yingxiang_qiangdan': r.AffectsGrabbing,
'shensuliyou': r.AppealReason or '',
'bohuiliyou': r.RejectReason or '',
'zhengju_tupian': zhengju_map.get(rid, []), # 证据图片
'shensu_tupian': shensu_map.get(rid, []), # 申诉图片
'CreateTime': r.CreateTime.strftime('%Y-%m-%d %H:%M:%S') if r.CreateTime else '',
'UpdateTime': r.UpdateTime.strftime('%Y-%m-%d %H:%M:%S') if r.UpdateTime else '',
}
chufa_list.append(item)
has_more = page_obj.has_next()
return Response({
'code': 0,
'msg': '获取成功',
'data': {
'list': chufa_list,
'has_more': has_more,
'page': page,
'page_size': page_size,
}
})
except Exception as e:
logger.error(f"获取罚款列表异常 {yonghuid if 'yonghuid' in locals() else ''}: {e}", exc_info=True)
return Response({'code': 99, 'msg': '系统繁忙'}, status=500)
def _merchant_penalty_auth(user):
"""校验商家身份,返回 (yonghuid, None) 或 (None, error_response)。"""
try:
shangjia_profile = user.ShopProfile
if shangjia_profile.zhuangtai != 1:
return None, Response({
'code': 3, 'msg': '商家账号状态异常', 'data': None
}, status=status.HTTP_403_FORBIDDEN)
except UserShangjia.DoesNotExist:
return None, Response({
'code': 2, 'msg': '用户不是商家身份', 'data': None
}, status=status.HTTP_403_FORBIDDEN)
return user.UserUID, None
def _money_str(val):
if val is None:
return '0.00'
return str(val)
class ShangjiaCufaTongjiView(APIView):
"""
商家端罚款+积分处罚综合统计(含金额汇总)
路径: POST /yonghu/sjcftjhq
"""
permission_classes = [IsAuthenticated]
def post(self, request):
try:
yonghuid, err = _merchant_penalty_auth(request.user)
if err:
return err
fadan_agg = Penalty.query.filter(ApplicantID=yonghuid).aggregate(
total=Count('id'),
total_jine=Sum('FineAmount'),
daijiaona=Count('id', filter=Q(Status=1)),
daijiaona_jine=Sum('FineAmount', filter=Q(Status=1)),
yijiaona=Count('id', filter=Q(Status=2)),
yijiaona_jine=Sum('FineAmount', filter=Q(Status=2)),
shensuzhong=Count('id', filter=Q(Status=3)),
shensuzhong_jine=Sum('FineAmount', filter=Q(Status=3)),
pingtai_shenhe=Count('id', filter=Q(Status=5)),
pingtai_shenhe_jine=Sum('FineAmount', filter=Q(Status=5)),
yibohui=Count('id', filter=Q(Status=4)),
yibohui_jine=Sum('FineAmount', filter=Q(Status=4)),
shijidaozhang_jine=Sum(
'ApplicantBonusAmount',
filter=Q(Status=2, BonusCredited=True),
),
)
jifen_agg = PenaltyRecord.query.filter(ApplicantID=yonghuid).aggregate(
total=Count('id'),
total_jifen=Sum('DeductedPoints'),
daichuli=Count('id', filter=Q(ApplyStatus=0) | Q(ApplyStatus=3)),
daichuli_jifen=Sum(
'DeductedPoints',
filter=Q(ApplyStatus=0) | Q(ApplyStatus=3),
),
yichuli=Count('id', filter=Q(ApplyStatus=1) | Q(ApplyStatus=2)),
yichuli_jifen=Sum(
'DeductedPoints',
filter=Q(ApplyStatus=1) | Q(ApplyStatus=2),
),
daichuli_0=Count('id', filter=Q(ApplyStatus=0)),
shensuzhong_3=Count('id', filter=Q(ApplyStatus=3)),
yichufa_1=Count('id', filter=Q(ApplyStatus=1)),
yibohui_2=Count('id', filter=Q(ApplyStatus=2)),
)
data = {
'fakuan': {
'total': fadan_agg['total'] or 0,
'total_jine': _money_str(fadan_agg['total_jine']),
'daijiaona': fadan_agg['daijiaona'] or 0,
'daijiaona_jine': _money_str(fadan_agg['daijiaona_jine']),
'yijiaona': fadan_agg['yijiaona'] or 0,
'yijiaona_jine': _money_str(fadan_agg['yijiaona_jine']),
'shensuzhong': fadan_agg['shensuzhong'] or 0,
'shensuzhong_jine': _money_str(fadan_agg['shensuzhong_jine']),
'pingtai_shenhe': fadan_agg['pingtai_shenhe'] or 0,
'pingtai_shenhe_jine': _money_str(fadan_agg['pingtai_shenhe_jine']),
'yibohui': fadan_agg['yibohui'] or 0,
'yibohui_jine': _money_str(fadan_agg['yibohui_jine']),
'shijidaozhang_jine': _money_str(fadan_agg['shijidaozhang_jine']),
},
'jifen': {
'total': jifen_agg['total'] or 0,
'total_jifen': jifen_agg['total_jifen'] or 0,
'daichuli': jifen_agg['daichuli'] or 0,
'daichuli_jifen': jifen_agg['daichuli_jifen'] or 0,
'yichuli': jifen_agg['yichuli'] or 0,
'yichuli_jifen': jifen_agg['yichuli_jifen'] or 0,
'daichuli_0': jifen_agg['daichuli_0'] or 0,
'shensuzhong_3': jifen_agg['shensuzhong_3'] or 0,
'yichufa_1': jifen_agg['yichufa_1'] or 0,
'yibohui_2': jifen_agg['yibohui_2'] or 0,
},
}
return Response({'code': 0, 'msg': '获取统计成功', 'data': data})
except Exception as e:
logger.error(f"商家处罚统计异常: {e}", exc_info=True)
return Response({'code': 99, 'msg': '系统繁忙'}, status=500)
class ShangjiaFakuanLieBiaoView(APIView):
"""
商家端金额罚单列表
路径: POST /yonghu/sjfklbhq
"""
permission_classes = [IsAuthenticated]
def post(self, request):
try:
yonghuid, err = _merchant_penalty_auth(request.user)
if err:
return err
page = int(request.data.get('page', 1))
page_size = int(request.data.get('page_size', 10))
if page_size > 50:
page_size = 50
if page_size < 1:
page_size = 10
zhuangtai = request.data.get('zhuangtai')
sousuo_dingdan_id = str(request.data.get('sousuo_dingdan_id', '')).strip()
qs = Penalty.query.filter(ApplicantID=yonghuid)
if zhuangtai is not None and int(zhuangtai) in [1, 2, 3, 4, 5]:
qs = qs.filter(Status=int(zhuangtai))
if sousuo_dingdan_id:
qs = qs.filter(RelatedOrderID__icontains=sousuo_dingdan_id)
qs = qs.order_by('-CreateTime')
paginator = Paginator(qs, page_size)
try:
page_obj = paginator.page(page)
except Exception:
return Response({
'code': 0, 'msg': '获取成功',
'data': {'list': [], 'has_more': False, 'page': page, 'page_size': page_size},
})
records = list(page_obj)
fadan_ids = [r.id for r in records]
staff_ids = list({r.ShopStaffUserID for r in records if r.ShopStaffUserID})
staff_name_map = {}
if staff_ids:
from merchant_ops.models import MerchantStaffMember
for m in MerchantStaffMember.query.filter(
merchant_id=yonghuid, staff_user_id__in=staff_ids
):
staff_name_map[m.staff_user_id] = m.display_name or m.staff_user_id
zhengju_map = {}
shensu_map = {}
if fadan_ids:
tupians = PenaltyAppealImage.query.filter(Penalty_id__in=fadan_ids).values(
'id', 'Penalty_id', 'ImageURL', 'Purpose'
)
for t in tupians:
fid = t['Penalty_id']
img = {'id': t['id'], 'url': t['ImageURL']}
if t['Purpose'] == 1:
zhengju_map.setdefault(fid, []).append(img)
else:
shensu_map.setdefault(fid, []).append(img)
chufa_list = []
for r in records:
rid = r.id
chufa_list.append({
'id': rid,
'beichufa_id': r.PenalizedUserID,
'guanliandingdan_id': r.RelatedOrderID or '',
'chufaliyou': r.Reason or '',
'fakuanjine': str(r.FineAmount),
'zhuangtai': r.Status,
'yingxiang_qiangdan': r.AffectsGrabbing,
'shensuliyou': r.AppealReason or '',
'bohuiliyou': r.RejectReason or '',
'applicant_bonus_amount': str(r.ApplicantBonusAmount or 0),
'applicant_bonus_rate': str(r.ApplicantBonusRate or 0),
'bonus_credited': bool(r.BonusCredited),
'shop_staff_user_id': r.ShopStaffUserID or '',
'staff_display_name': staff_name_map.get(r.ShopStaffUserID or '', ''),
'zhengju_tupian': zhengju_map.get(rid, []),
'shensu_tupian': shensu_map.get(rid, []),
'CreateTime': r.CreateTime.strftime('%Y-%m-%d %H:%M:%S') if r.CreateTime else '',
'UpdateTime': r.UpdateTime.strftime('%Y-%m-%d %H:%M:%S') if r.UpdateTime else '',
})
return Response({
'code': 0,
'msg': '获取成功',
'data': {
'list': chufa_list,
'has_more': page_obj.has_next(),
'page': page,
'page_size': page_size,
},
})
except Exception as e:
logger.error(f"商家罚款列表异常: {e}", exc_info=True)
return Response({'code': 99, 'msg': '系统繁忙'}, status=500)
class FaKuanShenSuView(APIView):
"""
罚款申诉提交/修改接口
路径POST /yonghu/fkss
参数:
fadan_id: 罚单ID
shensu_liyou: 申诉理由
shensu_tupian_urls: 图片相对URL数组可空数组但至少要有理由
逻辑:
- 仅待缴纳状态且无驳回理由的罚单可申诉
- 允许对已有申诉进行修改先删除旧申诉图片yongtu=2再添加新图片
"""
permission_classes = [IsAuthenticated, DashouPermission]
def post(self, request):
try:
user_main = request.user
yonghuid = user_main.UserUID
fadan_id = request.data.get('fadan_id')
shensu_liyou = request.data.get('shensu_liyou', '').strip()
tupian_urls = request.data.get('shensu_tupian_urls', [])
if not fadan_id:
return Response({'code': 400, 'msg': '缺少罚款ID'}, status=400)
if not shensu_liyou:
return Response({'code': 400, 'msg': '请输入申诉理由'}, status=400)
# 查询罚单,确保属于当前打手
try:
fadan = Penalty.query.get(id=fadan_id, PenalizedUserID=yonghuid)
except Penalty.DoesNotExist:
return Response({'code': 404, 'msg': '罚单不存在或无权操作'}, status=404)
# 待缴纳(1)或平台审核中(5)可申诉;平台审核中仅保存申诉内容,不改变状态
from utils.penalty_status import (
PENALTY_PENDING_PAY, PENALTY_APPEALING, PENALTY_PLATFORM_AUDIT,
)
if fadan.Status not in (PENALTY_PENDING_PAY, PENALTY_PLATFORM_AUDIT):
return Response({'code': 400, 'msg': '罚单状态不允许申诉'}, status=400)
if fadan.Status == PENALTY_PENDING_PAY and fadan.RejectReason:
return Response({'code': 400, 'msg': '该罚单申诉已被驳回,不可再次申诉'}, status=400)
if not isinstance(tupian_urls, list):
return Response({'code': 400, 'msg': '申诉图片格式错误'}, status=400)
tupian_urls = [str(u).strip() for u in tupian_urls if str(u).strip()]
if len(tupian_urls) > 9:
return Response({'code': 400, 'msg': '申诉图片最多9张'}, status=400)
if fadan.Status == PENALTY_PENDING_PAY:
fadan.Status = PENALTY_APPEALING
fadan.AppealReason = shensu_liyou
fadan.save()
# 删除旧的申诉图片yongtu=2仅删除打手自己上传的申诉图
PenaltyAppealImage.query.filter(Penalty=fadan, Purpose=2).delete()
# 插入新上传的申诉图片
for url in tupian_urls:
PenaltyAppealImage.query.create(
Penalty=fadan,
PenalizedUserID=yonghuid,
ImageURL=url,
Purpose=2
)
return Response({'code': 0, 'msg': '申诉提交成功'})
except Exception as e:
logger.error(f"罚款申诉异常 {yonghuid if 'yonghuid' in locals() else ''}: {e}", exc_info=True)
return Response({'code': 99, 'msg': '系统繁忙'}, status=500)