进行了完整的视图拆分,同步了 merchant_ops 的修改
This commit is contained in:
128
users/views/__init__.py
Normal file
128
users/views/__init__.py
Normal file
@@ -0,0 +1,128 @@
|
||||
"""users.views package aggregate entry - re-export all submodule symbols.
|
||||
|
||||
Preserves external `from users.views import X` for users/urls.py and jituan/urls.py.
|
||||
"""
|
||||
|
||||
from .auth import (
|
||||
DashouZhuceView,
|
||||
GuanshiRegisterView,
|
||||
GuanshiYaoqingDashouListView,
|
||||
WechatLoginAndDashouRegisterView,
|
||||
WechatLoginAndGuanshiRegisterView,
|
||||
WechatMiniProgramLoginView,
|
||||
WeixinOfficialCallbackView,
|
||||
ZuzhangXinxiView,
|
||||
ZuzhangZhuceView,
|
||||
)
|
||||
|
||||
from .user_info import (
|
||||
BossBiaoshiView,
|
||||
DashouGengxinView,
|
||||
DashouJianquanView,
|
||||
DashouXinxiAPIView,
|
||||
DashouZiliaoHuoquView,
|
||||
GuanshiXinxiView,
|
||||
HuoQuYaoQingRenView,
|
||||
LaobanJianquanView,
|
||||
ShangJiaXinXiView,
|
||||
ShangjiaNichengGengxinView,
|
||||
UserInfoUpdateView,
|
||||
YaoqingmaView,
|
||||
ZaixianZhuangtaiAPIView,
|
||||
huoquKehuduanIP,
|
||||
huoquYonghuDaili,
|
||||
)
|
||||
|
||||
from .shangjia import (
|
||||
DaShouFaKuanLieBiaoView,
|
||||
FaKuanJiFenTongJiView,
|
||||
FaKuanShenSuView,
|
||||
ShangjiaCufaTongjiView,
|
||||
ShangjiaFakuanLieBiaoView,
|
||||
ShangjiaZhuceView,
|
||||
)
|
||||
|
||||
from .tixian import (
|
||||
FORBIDDEN_STATUS_FOR_WITHDRAW,
|
||||
GuanshiContactView,
|
||||
ORDER_STATUS_COMPLETED,
|
||||
ORDER_STATUS_FAIL,
|
||||
ORDER_STATUS_REFUNDED,
|
||||
ShoukuanXinxiShangchuanView,
|
||||
TixianAssetView,
|
||||
TixianCallbackV3View,
|
||||
TixianJiluHuoquViewV2,
|
||||
TixianQueRenAutoView,
|
||||
TixianShenqingV3View,
|
||||
TixianShenqingView,
|
||||
TixianXinxiHuoquView,
|
||||
YonghuTixianShenheXiugaiView,
|
||||
generate_tixian_id,
|
||||
)
|
||||
|
||||
from .admin import (
|
||||
AdGetOrderList,
|
||||
AdGetOrderTypes,
|
||||
AdGuanLiYongHu,
|
||||
AdKfglView,
|
||||
AdKftjView,
|
||||
AdKfxgView,
|
||||
AdTongYiChuFa,
|
||||
AdYaoQingDaShou,
|
||||
AdcjxgView,
|
||||
AdckyhxqView,
|
||||
AddtxshView,
|
||||
AdminFinancialDataView,
|
||||
AdminLoginView,
|
||||
AdtixianqkView,
|
||||
CfGuanLi,
|
||||
PaihangbangGuanliQueryView,
|
||||
PaihangbangRiqiliebiaoView,
|
||||
admin_required,
|
||||
)
|
||||
|
||||
from .chufa import (
|
||||
ChufaJiluHuoquView,
|
||||
DashouPermission,
|
||||
DashouShensuView,
|
||||
ShangjiaChufaJiluHuoquView,
|
||||
)
|
||||
|
||||
from .kefu import (
|
||||
KefuCancelDesignationView,
|
||||
KefuChangeDashouView,
|
||||
KefuForceCompleteView,
|
||||
KefuGetDashouDetailView,
|
||||
KefuGetDashouListView,
|
||||
KefuGetOrderDetailView,
|
||||
KefuGetOrderListView,
|
||||
KefuGetOrderTypesView,
|
||||
KefuGetShangjiaOrderListView,
|
||||
KefuLoginView,
|
||||
KefuMerchantRefundView,
|
||||
KefuPlatformRefundView,
|
||||
KefuPunishView,
|
||||
KefuPunishmentActionView,
|
||||
KefuPunishmentDetailView,
|
||||
KefuPunishmentListView,
|
||||
KefuRecoverOrderView,
|
||||
KefuRejectRefundView,
|
||||
KefuRejectSettlementView,
|
||||
KefuStatsView,
|
||||
KefuTransferHallView,
|
||||
KefuUpdateDashouView,
|
||||
KefuWithdrawActionView,
|
||||
KefuWithdrawDetailView,
|
||||
KefuWithdrawListView,
|
||||
)
|
||||
|
||||
from .pay import (
|
||||
FaKuanHuitiaoView,
|
||||
FaKuanPayFailView,
|
||||
FaKuanPayPollView,
|
||||
FaKuanPayView,
|
||||
KaohePayCallbackView,
|
||||
KaohePayFailView,
|
||||
KaohePayPollView,
|
||||
KaohePayView,
|
||||
)
|
||||
2890
users/views/admin.py
Normal file
2890
users/views/admin.py
Normal file
File diff suppressed because it is too large
Load Diff
1936
users/views/auth.py
Normal file
1936
users/views/auth.py
Normal file
File diff suppressed because it is too large
Load Diff
830
users/views/chufa.py
Normal file
830
users/views/chufa.py
Normal file
@@ -0,0 +1,830 @@
|
||||
"""users.views.chufa - 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 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 DashouPermission:
|
||||
"""
|
||||
打手权限验证类
|
||||
确保用户是打手且账号状态正常
|
||||
"""
|
||||
|
||||
def has_permission(self, request, view):
|
||||
user = request.user
|
||||
|
||||
# 检查用户是否已认证
|
||||
if not user.is_authenticated:
|
||||
return False
|
||||
|
||||
try:
|
||||
# 通过反向关系获取打手扩展信息
|
||||
dashou_profile = user.DashouProfile
|
||||
|
||||
# 检查账号状态是否为1(正常)
|
||||
if dashou_profile.zhanghaozhuangtai != 1:
|
||||
return False
|
||||
|
||||
return True
|
||||
except UserDashou.DoesNotExist:
|
||||
return False
|
||||
except Exception as e:
|
||||
logger.error(f"打手权限验证异常: {e}")
|
||||
return False
|
||||
|
||||
|
||||
class ChufaJiluHuoquView(APIView):
|
||||
"""
|
||||
打手获取处罚记录接口
|
||||
路径: /yonghu/dshqcfjl
|
||||
方法: POST
|
||||
权限: JWT Token + 打手权限验证
|
||||
"""
|
||||
permission_classes = [IsAuthenticated, DashouPermission]
|
||||
|
||||
def post(self, request):
|
||||
try:
|
||||
# 获取当前用户信息
|
||||
user_main = request.user
|
||||
yonghuid = user_main.UserUID
|
||||
|
||||
# 通过反向关系获取打手扩展信息
|
||||
try:
|
||||
dashou_profile = user_main.DashouProfile
|
||||
# 再次确认账号状态
|
||||
if dashou_profile.zhanghaozhuangtai != 1:
|
||||
return Response({
|
||||
'code': 3,
|
||||
'msg': '账号已被封禁,无法查看处罚记录',
|
||||
'data': None
|
||||
}, status=status.HTTP_403_FORBIDDEN)
|
||||
except UserDashou.DoesNotExist:
|
||||
return Response({
|
||||
'code': 2,
|
||||
'msg': '用户不是打手身份',
|
||||
'data': None
|
||||
}, status=status.HTTP_403_FORBIDDEN)
|
||||
|
||||
# 判断请求类型:统计信息 or 记录列表
|
||||
qingqiu_tongji = request.data.get('qingqiu_tongji', False)
|
||||
|
||||
if qingqiu_tongji:
|
||||
# 返回统计信息
|
||||
return self.get_tongji_info(yonghuid)
|
||||
else:
|
||||
# 返回处罚记录列表
|
||||
return self.get_chufa_list(yonghuid, request.data)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"获取处罚记录异常 - 用户ID: {yonghuid}, 错误: {str(e)}", exc_info=True)
|
||||
return Response({
|
||||
'code': 99,
|
||||
'msg': '系统繁忙,请稍后重试',
|
||||
'data': None
|
||||
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
|
||||
def get_tongji_info(self, yonghuid):
|
||||
"""
|
||||
获取统计信息(总记录、待处理、已处理)
|
||||
"""
|
||||
try:
|
||||
# 使用单个查询获取所有统计信息
|
||||
stats = PenaltyRecord.query.filter(PlayerID=yonghuid).aggregate(
|
||||
# 总记录数
|
||||
zongshu=Count('id'),
|
||||
|
||||
# 待处理数:状态为0(待处罚)和3(申诉中)
|
||||
daichuli=Count(
|
||||
Case(
|
||||
When(Q(ApplyStatus=0) | Q(ApplyStatus=3), then=1),
|
||||
output_field=IntegerField()
|
||||
)
|
||||
),
|
||||
|
||||
# 已处理数:状态为1(已处罚)和2(已驳回)
|
||||
yichuli=Count(
|
||||
Case(
|
||||
When(Q(ApplyStatus=1) | Q(ApplyStatus=2), then=1),
|
||||
output_field=IntegerField()
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
# 如果查询结果为None,则设置为0
|
||||
tongji_data = {
|
||||
'zongshu': stats.get('zongshu', 0) or 0,
|
||||
'daichuli': stats.get('daichuli', 0) or 0,
|
||||
'yichuli': stats.get('yichuli', 0) or 0
|
||||
}
|
||||
|
||||
return Response({
|
||||
'code': 0,
|
||||
'msg': '获取统计信息成功',
|
||||
'data': tongji_data
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"获取统计信息异常 - 用户ID: {yonghuid}, 错误: {str(e)}")
|
||||
return Response({
|
||||
'code': 1,
|
||||
'msg': '获取统计信息失败',
|
||||
'data': None
|
||||
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
|
||||
def get_chufa_list(self, yonghuid, request_data):
|
||||
"""
|
||||
获取处罚记录列表(高性能批量查询)
|
||||
🔴 只修改图片查询逻辑,字段保持原样
|
||||
"""
|
||||
try:
|
||||
# 获取分页参数
|
||||
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', 0)
|
||||
|
||||
# 🔴 构建查询条件 - 打手端根据PlayerID查询
|
||||
queryset = PenaltyRecord.query.filter(PlayerID=yonghuid)
|
||||
|
||||
if zhuangtai == 0:
|
||||
# 待处理:状态为0(待处罚)和3(申诉中)
|
||||
queryset = queryset.filter(Q(ApplyStatus=0) | Q(ApplyStatus=3))
|
||||
elif zhuangtai == 1:
|
||||
# 已处理:状态为1(已处罚)和2(已驳回)
|
||||
queryset = queryset.filter(Q(ApplyStatus=1) | Q(ApplyStatus=2))
|
||||
else:
|
||||
# 默认返回所有状态
|
||||
pass
|
||||
|
||||
# 按创建时间倒序排列(最新在前)
|
||||
queryset = queryset.order_by('-CreateTime')
|
||||
|
||||
# 使用游标分页,避免传统分页的性能问题
|
||||
paginator = Paginator(queryset, page_size)
|
||||
|
||||
try:
|
||||
page_obj = paginator.page(page)
|
||||
except:
|
||||
# 页码超出范围,返回空列表
|
||||
return Response({
|
||||
'code': 0,
|
||||
'msg': '获取成功',
|
||||
'data': {
|
||||
'list': [],
|
||||
'has_more': False,
|
||||
'page': page,
|
||||
'page_size': page_size,
|
||||
'current_count': 0
|
||||
}
|
||||
})
|
||||
|
||||
# 获取当前页的记录
|
||||
chufa_records = list(page_obj)
|
||||
|
||||
if not chufa_records:
|
||||
# 没有数据直接返回
|
||||
return Response({
|
||||
'code': 0,
|
||||
'msg': '获取成功',
|
||||
'data': {
|
||||
'list': [],
|
||||
'has_more': False,
|
||||
'page': page,
|
||||
'page_size': page_size,
|
||||
'current_count': 0
|
||||
}
|
||||
})
|
||||
|
||||
# 🔴【核心修复】收集所有需要查询图片的组合
|
||||
dingdan_ids = []
|
||||
shangjia_ids = []
|
||||
|
||||
for record in chufa_records:
|
||||
if record.OrderID:
|
||||
dingdan_ids.append(record.OrderID)
|
||||
if record.ApplicantID: # 商家ID
|
||||
shangjia_ids.append(record.ApplicantID)
|
||||
|
||||
# 去重
|
||||
dingdan_ids = list(set(dingdan_ids))
|
||||
shangjia_ids = list(set(shangjia_ids))
|
||||
|
||||
# 🔴【核心修复】分别查询两种图片(避免if-elif逻辑错误)
|
||||
# 1. 查询证据图片(商家上传的)
|
||||
zhengju_mapping = {}
|
||||
if dingdan_ids and shangjia_ids:
|
||||
# 构建Q对象:OrderID在列表中 AND UserID在商家ID列表中
|
||||
zhengju_q = Q(OrderID__in=dingdan_ids, UserID__in=shangjia_ids)
|
||||
zhengju_queryset = PenaltyEvidenceImage.query.filter(zhengju_q).values('OrderID', 'UserID', 'ImageURL')
|
||||
|
||||
for item in zhengju_queryset:
|
||||
key = f"{item['OrderID']}_{item['UserID']}"
|
||||
if key not in zhengju_mapping:
|
||||
zhengju_mapping[key] = []
|
||||
if item['ImageURL']:
|
||||
zhengju_mapping[key].append(item['ImageURL'])
|
||||
|
||||
# 2. 查询申诉图片(打手上传的)
|
||||
shensu_mapping = {}
|
||||
if dingdan_ids:
|
||||
# 构建Q对象:OrderID在列表中 AND UserID=当前打手ID
|
||||
shensu_q = Q(OrderID__in=dingdan_ids, UserID=yonghuid)
|
||||
shensu_queryset = PenaltyEvidenceImage.query.filter(shensu_q).values('OrderID', 'UserID', 'ImageURL')
|
||||
|
||||
for item in shensu_queryset:
|
||||
key = f"{item['OrderID']}_{item['UserID']}"
|
||||
if key not in shensu_mapping:
|
||||
shensu_mapping[key] = []
|
||||
if item['ImageURL']:
|
||||
shensu_mapping[key].append(item['ImageURL'])
|
||||
|
||||
# 🔴【核心修复】组装返回数据 - 字段保持原样!
|
||||
chufa_list = []
|
||||
for record in chufa_records:
|
||||
dingdan_id = record.OrderID
|
||||
shangjia_id = record.ApplicantID
|
||||
|
||||
# 获取证据图片(商家上传的)
|
||||
zhengju_tupian = []
|
||||
if dingdan_id and shangjia_id:
|
||||
key = f"{dingdan_id}_{shangjia_id}"
|
||||
zhengju_tupian = zhengju_mapping.get(key, [])
|
||||
|
||||
# 获取申诉图片(打手上传的)
|
||||
shensu_tupian = []
|
||||
if dingdan_id:
|
||||
key = f"{dingdan_id}_{yonghuid}"
|
||||
shensu_tupian = shensu_mapping.get(key, [])
|
||||
|
||||
# 🔴【字段保持原样!】与前端完全对应!
|
||||
chufa_data = {
|
||||
'id': record.id,
|
||||
'dingdan_id': record.OrderID or '',
|
||||
'qingqiuid': record.ApplicantID or '', # 商家ID
|
||||
'cfliyou': record.PenaltyReason or '',
|
||||
'sqzhuangtai': record.ApplyStatus or 0,
|
||||
'ssliyou': record.AppealReason or '',
|
||||
'jifen': record.DeductedPoints or 0,
|
||||
'CreateTime': record.CreateTime.strftime('%Y-%m-%d %H:%M:%S') if record.CreateTime else '',
|
||||
'UpdateTime': record.UpdateTime.strftime('%Y-%m-%d %H:%M:%S') if record.UpdateTime else '',
|
||||
'zhengju_tupian': zhengju_tupian, # 商家上传的证据图片
|
||||
'shensu_tupian': shensu_tupian, # 打手上传的申诉图片
|
||||
}
|
||||
chufa_list.append(chufa_data)
|
||||
|
||||
# 判断是否还有更多数据
|
||||
has_more = page_obj.has_next()
|
||||
|
||||
# 构建响应数据
|
||||
response_data = {
|
||||
'list': chufa_list,
|
||||
'has_more': has_more,
|
||||
'page': page,
|
||||
'page_size': page_size,
|
||||
'current_count': len(chufa_list)
|
||||
}
|
||||
|
||||
return Response({
|
||||
'code': 0,
|
||||
'msg': '获取成功',
|
||||
'data': response_data
|
||||
})
|
||||
|
||||
except ValueError as e:
|
||||
logger.error(f"参数错误 - 用户ID: {yonghuid}, 错误: {str(e)}")
|
||||
return Response({
|
||||
'code': 4,
|
||||
'msg': '参数格式错误',
|
||||
'data': None
|
||||
}, status=status.HTTP_400_BAD_REQUEST)
|
||||
except Exception as e:
|
||||
logger.error(f"获取处罚记录列表异常 - 用户ID: {yonghuid}, 错误: {str(e)}", exc_info=True)
|
||||
return Response({
|
||||
'code': 5,
|
||||
'msg': '获取记录列表失败',
|
||||
'data': None
|
||||
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
|
||||
|
||||
class DashouShensuView(APIView):
|
||||
"""
|
||||
打手申诉接口
|
||||
路径: /yonghu/dscfss
|
||||
方法: POST
|
||||
权限: JWT Token + 打手权限验证
|
||||
功能: 打手对处罚进行申诉
|
||||
"""
|
||||
permission_classes = [IsAuthenticated, DashouPermission]
|
||||
|
||||
def post(self, request):
|
||||
try:
|
||||
# 获取当前用户信息
|
||||
user_main = request.user
|
||||
yonghuid = user_main.UserUID
|
||||
|
||||
# 🔴🆕【关键】验证打手账号状态
|
||||
try:
|
||||
dashou_profile = user_main.DashouProfile
|
||||
if dashou_profile.zhanghaozhuangtai != 1:
|
||||
return Response({
|
||||
'code': 3,
|
||||
'msg': '账号已被封禁,无法申诉',
|
||||
'data': None
|
||||
}, status=status.HTTP_403_FORBIDDEN)
|
||||
except UserDashou.DoesNotExist:
|
||||
return Response({
|
||||
'code': 2,
|
||||
'msg': '用户不是打手身份',
|
||||
'data': None
|
||||
}, status=status.HTTP_403_FORBIDDEN)
|
||||
|
||||
# 🔴🆕【关键】获取请求参数
|
||||
chufa_id = request.data.get('chufa_id') # 处罚记录ID
|
||||
shensu_liyou = request.data.get('shensu_liyou', '').strip() # 申诉理由
|
||||
shensu_tupian_urls = request.data.get('shensu_tupian_urls', []) # 申诉图片URL数组
|
||||
|
||||
# 验证必填字段
|
||||
if not chufa_id:
|
||||
return Response({
|
||||
'code': 6,
|
||||
'msg': '处罚记录ID不能为空',
|
||||
'data': None
|
||||
}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
if not shensu_liyou:
|
||||
return Response({
|
||||
'code': 7,
|
||||
'msg': '申诉理由不能为空',
|
||||
'data': None
|
||||
}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
# 验证申诉理由长度
|
||||
if len(shensu_liyou) > 500:
|
||||
return Response({
|
||||
'code': 8,
|
||||
'msg': '申诉理由不能超过500字',
|
||||
'data': None
|
||||
}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
# 验证图片数量
|
||||
if len(shensu_tupian_urls) > 9:
|
||||
return Response({
|
||||
'code': 9,
|
||||
'msg': '最多只能上传9张图片',
|
||||
'data': None
|
||||
}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
# 🔴🆕【关键】使用事务确保数据一致性
|
||||
|
||||
with transaction.atomic():
|
||||
# 1. 查询处罚记录
|
||||
try:
|
||||
chufa_record = PenaltyRecord.objects.select_for_update().get(
|
||||
id=chufa_id,
|
||||
PlayerID=yonghuid # 确保只能申诉自己的处罚记录
|
||||
)
|
||||
except PenaltyRecord.DoesNotExist:
|
||||
return Response({
|
||||
'code': 10,
|
||||
'msg': '处罚记录不存在或无权申诉',
|
||||
'data': None
|
||||
}, status=status.HTTP_404_NOT_FOUND)
|
||||
|
||||
# 2. 验证处罚记录状态(必须是待处理状态0)
|
||||
if chufa_record.ApplyStatus != 0:
|
||||
return Response({
|
||||
'code': 11,
|
||||
'msg': '该处罚记录已处理,无法申诉',
|
||||
'data': None
|
||||
}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
# 3. 验证订单是否存在(可选,根据需求决定)
|
||||
# 这里可以根据OrderID去查询订单表,但根据需求描述,我们已经有了处罚记录
|
||||
# 处罚记录中包含了OrderID,如果OrderID不存在,说明订单可能被删除
|
||||
# 但根据业务逻辑,有处罚记录就应该有订单,所以这里可以跳过这个检查
|
||||
|
||||
# 4. 检查是否已有申诉记录(防止重复申诉)
|
||||
if chufa_record.AppealReason or chufa_record.ApplyStatus == 3:
|
||||
# 检查是否已经有申诉图片
|
||||
existing_shensu_tupian = PenaltyEvidenceImage.query.filter(
|
||||
OrderID=chufa_record.OrderID,
|
||||
UserID=yonghuid
|
||||
).exists()
|
||||
|
||||
if existing_shensu_tupian:
|
||||
return Response({
|
||||
'code': 12,
|
||||
'msg': '您已经提交过申诉,请等待处理结果',
|
||||
'data': None
|
||||
}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
# 5. 更新处罚记录
|
||||
chufa_record.AppealReason = shensu_liyou
|
||||
chufa_record.ApplyStatus = 3 # 状态改为申诉中
|
||||
chufa_record.save()
|
||||
|
||||
# 6. 保存申诉图片(如果有)
|
||||
saved_tupian_urls = []
|
||||
if shensu_tupian_urls:
|
||||
for tupian_url in shensu_tupian_urls:
|
||||
if tupian_url: # 确保URL不为空
|
||||
chufa_tupian = PenaltyEvidenceImage.query.create(
|
||||
OrderID=chufa_record.OrderID,
|
||||
UserID=yonghuid,
|
||||
ImageURL=tupian_url
|
||||
)
|
||||
saved_tupian_urls.append(tupian_url)
|
||||
|
||||
# 7. 构建返回数据
|
||||
response_data = {
|
||||
'chufa_id': chufa_record.id,
|
||||
'dingdan_id': chufa_record.OrderID or '',
|
||||
'shensu_liyou': shensu_liyou,
|
||||
'shensu_tupian_urls': saved_tupian_urls,
|
||||
'sqzhuangtai': chufa_record.ApplyStatus,
|
||||
'UpdateTime': chufa_record.UpdateTime.strftime(
|
||||
'%Y-%m-%d %H:%M:%S') if chufa_record.UpdateTime else ''
|
||||
}
|
||||
|
||||
# 记录操作日志
|
||||
logger.info(f"打手申诉成功 - 用户ID: {yonghuid}, 处罚ID: {chufa_id}, 订单ID: {chufa_record.OrderID}")
|
||||
|
||||
return Response({
|
||||
'code': 0,
|
||||
'msg': '申诉提交成功',
|
||||
'data': response_data
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"打手申诉异常 - 用户ID: {yonghuid}, 错误: {str(e)}", exc_info=True)
|
||||
return Response({
|
||||
'code': 99,
|
||||
'msg': '系统繁忙,请稍后重试',
|
||||
'data': None
|
||||
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
|
||||
|
||||
class ShangjiaChufaJiluHuoquView(APIView):
|
||||
"""
|
||||
商家获取处罚记录接口
|
||||
路径: /yonghu/sjcfjlhq
|
||||
方法: POST
|
||||
权限: JWT Token + 商家权限验证
|
||||
"""
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
def post(self, request):
|
||||
try:
|
||||
# 获取当前用户信息
|
||||
user_main = request.user
|
||||
yonghuid = user_main.UserUID
|
||||
|
||||
# 通过反向关系获取商家扩展信息
|
||||
try:
|
||||
shangjia_profile = user_main.ShopProfile
|
||||
# 再次确认账号状态
|
||||
if shangjia_profile.zhuangtai != 1:
|
||||
return Response({
|
||||
'code': 3,
|
||||
'msg': '商家账号状态异常,无法查看处罚记录',
|
||||
'data': None
|
||||
}, status=status.HTTP_403_FORBIDDEN)
|
||||
except UserShangjia.DoesNotExist:
|
||||
return Response({
|
||||
'code': 2,
|
||||
'msg': '用户不是商家身份',
|
||||
'data': None
|
||||
}, status=status.HTTP_403_FORBIDDEN)
|
||||
|
||||
# 判断请求类型
|
||||
qingqiu_tongji = request.data.get('qingqiu_tongji', False)
|
||||
|
||||
if qingqiu_tongji:
|
||||
return self.get_tongji_info(yonghuid)
|
||||
else:
|
||||
return self.get_chufa_list(yonghuid, request.data)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"商家获取处罚记录异常 - 用户ID: {yonghuid}, 错误: {str(e)}", exc_info=True)
|
||||
return Response({
|
||||
'code': 99,
|
||||
'msg': '系统繁忙,请稍后重试',
|
||||
'data': None
|
||||
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
|
||||
def get_tongji_info(self, yonghuid):
|
||||
"""
|
||||
获取统计信息
|
||||
"""
|
||||
try:
|
||||
# 🔴 查询条件:ApplicantID = 当前商家ID
|
||||
stats = PenaltyRecord.query.filter(ApplicantID=yonghuid).aggregate(
|
||||
zongshu=Count('id'),
|
||||
daichuli=Count(
|
||||
Case(
|
||||
When(Q(ApplyStatus=0) | Q(ApplyStatus=3), then=1),
|
||||
output_field=IntegerField()
|
||||
)
|
||||
),
|
||||
yichuli=Count(
|
||||
Case(
|
||||
When(Q(ApplyStatus=1) | Q(ApplyStatus=2), then=1),
|
||||
output_field=IntegerField()
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
tongji_data = {
|
||||
'zongshu': stats.get('zongshu', 0) or 0,
|
||||
'daichuli': stats.get('daichuli', 0) or 0,
|
||||
'yichuli': stats.get('yichuli', 0) or 0
|
||||
}
|
||||
|
||||
return Response({
|
||||
'code': 0,
|
||||
'msg': '获取统计信息成功',
|
||||
'data': tongji_data
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"商家获取统计信息异常 - 用户ID: {yonghuid}, 错误: {str(e)}")
|
||||
return Response({
|
||||
'code': 1,
|
||||
'msg': '获取统计信息失败',
|
||||
'data': None
|
||||
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
|
||||
def get_chufa_list(self, yonghuid, request_data):
|
||||
"""
|
||||
获取处罚记录列表(商家端)
|
||||
🔴 只修改图片查询逻辑,字段保持原样
|
||||
"""
|
||||
try:
|
||||
# 获取分页参数
|
||||
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', 0)
|
||||
|
||||
# 🔴 查询条件:ApplicantID = 当前商家ID
|
||||
queryset = PenaltyRecord.query.filter(ApplicantID=yonghuid)
|
||||
|
||||
if zhuangtai == 0:
|
||||
queryset = queryset.filter(Q(ApplyStatus=0) | Q(ApplyStatus=3))
|
||||
elif zhuangtai == 1:
|
||||
queryset = queryset.filter(Q(ApplyStatus=1) | Q(ApplyStatus=2))
|
||||
else:
|
||||
pass
|
||||
|
||||
# 按创建时间倒序排列
|
||||
queryset = queryset.order_by('-CreateTime')
|
||||
|
||||
# 分页逻辑
|
||||
paginator = Paginator(queryset, page_size)
|
||||
|
||||
try:
|
||||
page_obj = paginator.page(page)
|
||||
except:
|
||||
return Response({
|
||||
'code': 0,
|
||||
'msg': '获取成功',
|
||||
'data': {
|
||||
'list': [],
|
||||
'has_more': False,
|
||||
'page': page,
|
||||
'page_size': page_size,
|
||||
'current_count': 0
|
||||
}
|
||||
})
|
||||
|
||||
chufa_records = list(page_obj)
|
||||
|
||||
if not chufa_records:
|
||||
return Response({
|
||||
'code': 0,
|
||||
'msg': '获取成功',
|
||||
'data': {
|
||||
'list': [],
|
||||
'has_more': False,
|
||||
'page': page,
|
||||
'page_size': page_size,
|
||||
'current_count': 0
|
||||
}
|
||||
})
|
||||
|
||||
# 🔴【核心修复】收集所有需要查询图片的组合
|
||||
dingdan_ids = []
|
||||
dashou_ids = []
|
||||
|
||||
for record in chufa_records:
|
||||
if record.OrderID:
|
||||
dingdan_ids.append(record.OrderID)
|
||||
if record.PlayerID: # 打手ID
|
||||
dashou_ids.append(record.PlayerID)
|
||||
|
||||
# 去重
|
||||
dingdan_ids = list(set(dingdan_ids))
|
||||
dashou_ids = list(set(dashou_ids))
|
||||
|
||||
# 🔴【核心修复】分别查询两种图片(避免if-elif逻辑错误)
|
||||
# 1. 查询证据图片(商家自己上传的)
|
||||
zhengju_mapping = {}
|
||||
if dingdan_ids:
|
||||
# 构建Q对象:OrderID在列表中 AND UserID=当前商家ID
|
||||
zhengju_q = Q(OrderID__in=dingdan_ids, UserID=yonghuid)
|
||||
zhengju_queryset = PenaltyEvidenceImage.query.filter(zhengju_q).values('OrderID', 'UserID', 'ImageURL')
|
||||
|
||||
for item in zhengju_queryset:
|
||||
key = f"{item['OrderID']}_{item['UserID']}"
|
||||
if key not in zhengju_mapping:
|
||||
zhengju_mapping[key] = []
|
||||
if item['ImageURL']:
|
||||
zhengju_mapping[key].append(item['ImageURL'])
|
||||
|
||||
# 2. 查询申诉图片(打手上传的)
|
||||
shensu_mapping = {}
|
||||
if dingdan_ids and dashou_ids:
|
||||
# 构建Q对象:OrderID在列表中 AND UserID在打手ID列表中
|
||||
shensu_q = Q(OrderID__in=dingdan_ids, UserID__in=dashou_ids)
|
||||
shensu_queryset = PenaltyEvidenceImage.query.filter(shensu_q).values('OrderID', 'UserID', 'ImageURL')
|
||||
|
||||
for item in shensu_queryset:
|
||||
key = f"{item['OrderID']}_{item['UserID']}"
|
||||
if key not in shensu_mapping:
|
||||
shensu_mapping[key] = []
|
||||
if item['ImageURL']:
|
||||
shensu_mapping[key].append(item['ImageURL'])
|
||||
|
||||
# 🔴【核心修复】组装返回数据 - 字段保持原样!
|
||||
chufa_list = []
|
||||
for record in chufa_records:
|
||||
dingdan_id = record.OrderID
|
||||
dashou_id = record.PlayerID
|
||||
|
||||
# 获取证据图片(商家自己上传的)
|
||||
zhengju_tupian = []
|
||||
if dingdan_id:
|
||||
key = f"{dingdan_id}_{yonghuid}"
|
||||
zhengju_tupian = zhengju_mapping.get(key, [])
|
||||
|
||||
# 获取申诉图片(打手上传的)
|
||||
shensu_tupian = []
|
||||
if dingdan_id and dashou_id:
|
||||
key = f"{dingdan_id}_{dashou_id}"
|
||||
shensu_tupian = shensu_mapping.get(key, [])
|
||||
|
||||
# 🔴【字段保持原样!】与前端完全对应!
|
||||
chufa_data = {
|
||||
'id': record.id,
|
||||
'dingdan_id': record.OrderID or '',
|
||||
'qingqiuid': record.PlayerID or '', # 打手ID
|
||||
'cfliyou': record.PenaltyReason or '',
|
||||
'sqzhuangtai': record.ApplyStatus or 0,
|
||||
'ssliyou': record.AppealReason or '',
|
||||
'jifen': record.DeductedPoints or 0,
|
||||
'CreateTime': record.CreateTime.strftime('%Y-%m-%d %H:%M:%S') if record.CreateTime else '',
|
||||
'UpdateTime': record.UpdateTime.strftime('%Y-%m-%d %H:%M:%S') if record.UpdateTime else '',
|
||||
'zhengju_tupian': zhengju_tupian, # 商家自己上传的证据图片
|
||||
'shensu_tupian': shensu_tupian, # 打手上传的申诉图片
|
||||
}
|
||||
chufa_list.append(chufa_data)
|
||||
|
||||
# 判断是否还有更多数据
|
||||
has_more = page_obj.has_next()
|
||||
|
||||
response_data = {
|
||||
'list': chufa_list,
|
||||
'has_more': has_more,
|
||||
'page': page,
|
||||
'page_size': page_size,
|
||||
'current_count': len(chufa_list)
|
||||
}
|
||||
|
||||
return Response({
|
||||
'code': 0,
|
||||
'msg': '获取成功',
|
||||
'data': response_data
|
||||
})
|
||||
|
||||
except ValueError as e:
|
||||
logger.error(f"商家端参数错误 - 用户ID: {yonghuid}, 错误: {str(e)}")
|
||||
return Response({
|
||||
'code': 4,
|
||||
'msg': '参数格式错误',
|
||||
'data': None
|
||||
}, status=status.HTTP_400_BAD_REQUEST)
|
||||
except Exception as e:
|
||||
logger.error(f"商家获取处罚记录列表异常 - 用户ID: {yonghuid}, 错误: {str(e)}", exc_info=True)
|
||||
return Response({
|
||||
'code': 5,
|
||||
'msg': '获取记录列表失败',
|
||||
'data': None
|
||||
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
3794
users/views/kefu.py
Normal file
3794
users/views/kefu.py
Normal file
File diff suppressed because it is too large
Load Diff
1073
users/views/pay.py
Normal file
1073
users/views/pay.py
Normal file
File diff suppressed because it is too large
Load Diff
734
users/views/shangjia.py
Normal file
734
users/views/shangjia.py
Normal file
@@ -0,0 +1,734 @@
|
||||
"""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)
|
||||
1418
users/views/tixian.py
Normal file
1418
users/views/tixian.py
Normal file
File diff suppressed because it is too large
Load Diff
1112
users/views/user_info.py
Normal file
1112
users/views/user_info.py
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user