Files
jyls_django/User/views.py
2026-01-20 16:10:15 +08:00

2322 lines
107 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.

from django.contrib.auth.models import Permission
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
import json
import ast
from .models import User, Approval, Department, OperationLog, Team
from business.models import permission
from finance.models import Income, Accounts, Payment, Reimbursement, BonusChange
from finance.models import Invoice
from business.models import ProjectRegistration, Case, SealApplication, PreFiling, Bid, CaseChangeRequest
import datetime
from utility.utility import flies
from django.contrib.sessions.backends.db import SessionStore
from django.db.models import Count, Q
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from collections import defaultdict
from .utils import (
is_department_id,
log_operation,
get_approvers_from_record,
is_finance_personincharge,
get_finance_personincharge_candidates,
)
class CreateUserView(APIView):
def post(self, request, *args, **kwargs):
"""人事管理-人员添加"""
role = request.data.get('role')
username = request.data.get('username') # 姓名
account = request.data.get('account') # 账号
password = request.data.get('password') # 密码
nation = request.data.get('nation') # 民族
IdCard = request.data.get('IdCard') # 身份证
department = request.data.get('department') # 归属部门
mobilePhone = request.data.get('mobilePhone') # 手机号
position = request.data.get('position') # 岗位
team = request.data.get('team') # 所属团队
Dateofjoining = request.data.get('Dateofjoining') # 入职时间
Confirmationtime = request.data.get('Confirmationtime') # 转正时间
Practicingcertificatetime = request.data.get('Practicingcertificatetime') # 执业证时间
AcademicResume = request.FILES.getlist('AcademicResume') # 学业简历
academic = request.data.get('academic') # 学历
contract = request.FILES.getlist('contract') # 合同
ApplicationForm = request.FILES.getlist('ApplicationForm') # 入职申请表
salary = request.data.get('salary') # 工资标准
# 详细的参数验证,提供更明确的错误信息
missing_params = []
if not username:
missing_params.append('username(姓名)')
if not account:
missing_params.append('account(账号)')
if not password:
missing_params.append('password(密码)')
if not IdCard:
missing_params.append('IdCard(身份证)')
if not department:
missing_params.append('department(归属部门)')
if not position:
missing_params.append('position(岗位)')
if not nation:
missing_params.append('nation(民族)')
if not mobilePhone:
missing_params.append('mobilePhone(手机号)')
if not team:
missing_params.append('team(所属团队)')
if not Dateofjoining:
missing_params.append('Dateofjoining(入职时间)')
if not academic:
missing_params.append('academic(学历)')
if not contract or (isinstance(contract, list) and len(contract) == 0):
missing_params.append('contract(合同文件)')
if missing_params:
return Response({
'status': 'error',
'message': f'缺少参数: {", ".join(missing_params)}',
'code': 1
}, status=status.HTTP_400_BAD_REQUEST)
# 检查用户名是否已被未软删除的用户使用(允许软删除后重新注册同名用户)
if User.objects.filter(username=username, is_deleted=False).exists():
return Response({'status': 'error', 'message': '用户名已存在,不能重复', 'code': 1},
status=status.HTTP_400_BAD_REQUEST)
AcademicResume_url = flies(AcademicResume)
ApplicationForm_url = flies(ApplicationForm)
contract_url = flies(contract)
# 日期格式验证和解析
try:
Dateofjoining = datetime.datetime.strptime(Dateofjoining, "%Y-%m-%d")
except ValueError:
return Response({'status': 'error', 'message': '入职时间格式错误应为YYYY-MM-DD格式', 'code': 1},
status=status.HTTP_400_BAD_REQUEST)
# 处理转正时间,支持 'null' 字符串
if Confirmationtime and Confirmationtime not in ['null', 'None', 'NULL', 'NONE', '']:
try:
Confirmationtime = datetime.datetime.strptime(Confirmationtime, "%Y-%m-%d")
except ValueError:
return Response({'status': 'error', 'message': '转正时间格式错误应为YYYY-MM-DD格式', 'code': 1},
status=status.HTTP_400_BAD_REQUEST)
else:
Confirmationtime = None
# 处理执业证时间,支持 'null' 字符串
if Practicingcertificatetime and Practicingcertificatetime not in ['null', 'None', 'NULL', 'NONE', '']:
try:
# 支持两种格式YYYY-MM精确到月和 YYYY-MM-DD精确到日
if len(Practicingcertificatetime) == 7 and Practicingcertificatetime.count('-') == 1:
# YYYY-MM 格式,转换为该月的第一天
Practicingcertificatetime = datetime.datetime.strptime(Practicingcertificatetime + "-01", "%Y-%m-%d")
else:
# YYYY-MM-DD 格式
Practicingcertificatetime = datetime.datetime.strptime(Practicingcertificatetime, "%Y-%m-%d")
except ValueError:
return Response({
'status': 'error',
'message': '执业证时间格式错误应为YYYY-MM精确到月或YYYY-MM-DD精确到日格式',
'code': 1
}, status=status.HTTP_400_BAD_REQUEST)
else:
Practicingcertificatetime = None
# 解析角色和部门ID列表
try:
if role:
role_list = ast.literal_eval(role) if isinstance(role, str) else role
if not isinstance(role_list, list):
role_list = [role_list] if role_list else []
else:
role_list = []
except (ValueError, SyntaxError) as e:
return Response({'status': 'error', 'message': f'角色参数格式错误: {str(e)}', 'code': 1},
status=status.HTTP_400_BAD_REQUEST)
try:
if department:
department_id = ast.literal_eval(department) if isinstance(department, str) else department
if not isinstance(department_id, list):
department_id = [department_id] if department_id else []
else:
department_id = []
except (ValueError, SyntaxError) as e:
return Response({'status': 'error', 'message': f'部门参数格式错误: {str(e)}', 'code': 1},
status=status.HTTP_400_BAD_REQUEST)
# 创建用户,添加异常处理
try:
user = User.objects.create(
username=username,
account=account,
password=password,
ethnicity=nation,
card=IdCard,
mobilePhone=mobilePhone,
position=position,
team=team,
Dateofjoining=Dateofjoining,
Confirmationtime=Confirmationtime,
Practicingcertificatetime=Practicingcertificatetime,
AcademicResume=json.dumps(AcademicResume_url),
academic=academic,
contract=json.dumps(contract_url),
ApplicationForm=json.dumps(ApplicationForm_url),
state="待登记",
salary=salary
)
# 添加角色和部门关联
if role_list:
user.role.add(*role_list)
if department_id:
user.department.add(*department_id)
# 记录操作日志
new_data = {
'id': user.id,
'username': user.username,
'account': user.account,
'position': user.position,
'team': user.team,
'state': user.state
}
log_operation(
request=request,
operation_type='CREATE',
module='User',
action='新增用户',
target_type='User',
target_id=user.id,
target_name=user.username,
new_data=new_data,
remark=f'新增用户:{user.username}(账号:{user.account}'
)
return Response({'message': '添加人员成功', 'code': 0}, status=status.HTTP_200_OK)
except Exception as e:
# 捕获数据库操作异常
error_msg = str(e)
if 'Duplicate entry' in error_msg or 'UNIQUE constraint' in error_msg:
if 'username' in error_msg:
return Response({'status': 'error', 'message': '用户名已存在,不能重复', 'code': 1},
status=status.HTTP_400_BAD_REQUEST)
else:
return Response({'status': 'error', 'message': '数据已存在,请检查唯一性约束', 'code': 1},
status=status.HTTP_400_BAD_REQUEST)
else:
return Response({'status': 'error', 'message': f'创建用户失败: {error_msg}', 'code': 1},
status=status.HTTP_500_INTERNAL_SERVER_ERROR)
class EditorialStaffView(APIView):
def post(self, request, *args, **kwargs):
"""修改人员信息"""
id = request.data.get('id')
username = request.data.get('username') # 姓名
account = request.data.get('account') # 账号
password = request.data.get('password') # 密码
nation = request.data.get('nation') # 民族
IdCard = request.data.get('IdCard') # 身份证
department = request.data.get('department') # 归属部门
mobilePhone = request.data.get('mobilePhone') # 手机号
position = request.data.get('position') # 岗位
team = request.data.get('team') # 所属团队
Dateofjoining = request.data.get('Dateofjoining') # 入职时间
Confirmationtime = request.data.get('Confirmationtime') # 转正时间
Practicingcertificatetime = request.data.get('Practicingcertificatetime') # 执业证时间
AcademicResume = request.FILES.getlist('AcademicResume') # 学业简历
academic = request.data.get('academic') # 学历
contract = request.FILES.getlist('contract') # 合同
ApplicationForm = request.FILES.getlist('ApplicationForm') # 入职申请表
salary = request.data.get('salary') # 工资标准
# 密码改为可选参数,其他参数仍为必填
if not all([username, account, IdCard, department, position, nation, mobilePhone, team, Dateofjoining,
academic]):
return Response({'status': 'error', 'message': '缺少参数', 'code': 1}, status=status.HTTP_400_BAD_REQUEST)
user = User.objects.get(id=id, is_deleted=False)
# 检查用户名是否已被其他用户使用(排除当前用户)
if username and User.objects.filter(username=username, is_deleted=False).exclude(id=id).exists():
return Response({'status': 'error', 'message': '用户名已存在,不能重复', 'code': 1},
status=status.HTTP_400_BAD_REQUEST)
if AcademicResume:
AcademicResume_url = flies(AcademicResume)
user.AcademicResume = json.dumps(AcademicResume_url)
if ApplicationForm:
ApplicationForm_url = flies(ApplicationForm)
user.ApplicationForm = json.dumps(ApplicationForm_url)
if contract:
contract_url = flies(contract)
user.contract = json.dumps(contract_url)
def parse_date_str(date_str, allow_none=False):
"""解析日期字符串,处理各种空值情况
Args:
date_str: 日期字符串
allow_none: 是否允许返回 None用于可选字段
Returns:
日期字符串YYYY-MM-DD格式或 None如果 allow_none=True 且输入为空)
Raises:
ValueError: 如果 allow_none=False 且输入为空或无效
"""
# 处理 None、空字符串、'null'、'None' 等情况
if not date_str or date_str in ['null', 'None', 'NULL', 'NONE', '']:
if allow_none:
return None # 可选字段返回 None
raise ValueError('日期不能为空')
# 处理无效日期格式
if date_str == "0000-00-00":
if allow_none:
return None
raise ValueError('日期格式无效')
try:
return datetime.datetime.strptime(date_str, "%Y-%m-%d").strftime('%Y-%m-%d')
except ValueError:
# 如果解析失败,根据 allow_none 决定返回值
if allow_none:
return None
raise ValueError('日期格式错误应为YYYY-MM-DD格式')
def parse_practicing_certificate_time(date_str):
"""解析执业证时间支持YYYY-MM精确到月和YYYY-MM-DD精确到日格式"""
# 处理 None、空字符串、'null'、'None' 等情况
if not date_str or date_str in ['null', 'None', 'NULL', 'NONE', '']:
return None # 可选字段返回 None
# 处理无效日期格式
if date_str == "0000-00-00":
return None
try:
# 支持两种格式YYYY-MM精确到月和 YYYY-MM-DD精确到日
if len(date_str) == 7 and date_str.count('-') == 1:
# YYYY-MM 格式,转换为该月的第一天
return datetime.datetime.strptime(date_str + "-01", "%Y-%m-%d").strftime('%Y-%m-%d')
else:
# YYYY-MM-DD 格式
return datetime.datetime.strptime(date_str, "%Y-%m-%d").strftime('%Y-%m-%d')
except ValueError:
raise ValueError('执业证时间格式错误应为YYYY-MM精确到月或YYYY-MM-DD精确到日格式')
# 入职时间是必填字段,不允许为空
try:
Dateofjoining = parse_date_str(Dateofjoining, allow_none=False)
except ValueError as e:
return Response({'status': 'error', 'message': str(e), 'code': 1},
status=status.HTTP_400_BAD_REQUEST)
# 转正时间是可选字段,允许为 None
Confirmationtime = parse_date_str(Confirmationtime, allow_none=True)
try:
# 执业证时间是可选字段,允许为 None
Practicingcertificatetime = parse_practicing_certificate_time(Practicingcertificatetime)
except ValueError as e:
return Response({'status': 'error', 'message': str(e), 'code': 1},
status=status.HTTP_400_BAD_REQUEST)
user.Confirmationtime = Confirmationtime
user.Practicingcertificatetime = Practicingcertificatetime
user.Dateofjoining = Dateofjoining
user.username = username
user.account = account
# 密码修改权限控制:只有超级管理员才能通过此接口修改密码
# 普通用户修改密码请使用专门的 /user/change-password 接口
if password and password.strip():
# 获取当前登录用户
token = request.META.get('token')
try:
current_user = User.objects.get(token=token, is_deleted=False)
is_admin = (current_user.username == 'admin' or current_user.account == 'admin')
if is_admin:
# 超级管理员可以修改任何用户的密码
user.password = password
else:
# 非超级管理员不能通过此接口修改密码
return Response({
'status': 'error',
'message': '非超级管理员不能通过此接口修改密码,请使用密码修改接口',
'code': 1
}, status=status.HTTP_403_FORBIDDEN)
except User.DoesNotExist:
return Response({
'status': 'error',
'message': '用户不存在或已被删除',
'code': 1
}, status=status.HTTP_401_UNAUTHORIZED)
user.ethnicity = nation
user.card = IdCard
user.mobilePhone = mobilePhone
user.position = position
user.team = team
user.academic = academic
user.salary = salary
# 处理 ManyToMany 字段 department
if department:
try:
# 尝试解析 JSON 格式的字符串,如 "[1,2]" 或 "1,2"
if isinstance(department, str):
# 如果是 "1,2" 格式,先转换为列表格式
if ',' in department and not department.startswith('['):
department_id = [int(x.strip()) for x in department.split(',')]
else:
department_id = ast.literal_eval(department)
else:
department_id = department
# 确保 department_id 是列表格式ManyToMany 字段的 set 方法需要可迭代对象)
if not isinstance(department_id, (list, tuple)):
department_id = [department_id]
user.department.set(department_id)
except (ValueError, SyntaxError):
# 如果解析失败尝试作为单个ID处理
try:
user.department.set([int(department)])
except (ValueError, TypeError):
pass
# 处理 ManyToMany 字段 role如果有传入
role = request.data.get('role')
if role:
role_id = ast.literal_eval(role) if isinstance(role, str) else role
# 确保 role_id 是列表格式ManyToMany 字段的 set 方法需要可迭代对象)
if not isinstance(role_id, (list, tuple)):
role_id = [role_id]
user.role.set(role_id)
# 记录操作前的数据
old_data = {
'id': user.id,
'username': user.username,
'account': user.account,
'position': user.position,
'team': user.team,
'state': user.state,
'salary': user.salary
}
user.save()
# 记录操作后的数据
new_data = {
'id': user.id,
'username': user.username,
'account': user.account,
'position': user.position,
'team': user.team,
'state': user.state,
'salary': user.salary
}
# 记录操作日志
log_operation(
request=request,
operation_type='UPDATE',
module='User',
action='编辑用户信息',
target_type='User',
target_id=user.id,
target_name=user.username,
old_data=old_data,
new_data=new_data,
remark=f'编辑用户信息:{user.username}(账号:{user.account}'
)
return Response({'message': '修改成功', 'code': 0}, status=status.HTTP_200_OK)
class LoginView(APIView):
"""
登录页面
"""
def post(self, request):
token = request.META.get('token')
username = request.data.get('username')
password = request.data.get('password')
if not all([username, password]):
return Response({'message': '缺少参数', 'code': 1}, status=status.HTTP_400_BAD_REQUEST)
try:
user = User.objects.get(account=username, is_deleted=False)
# 只有"在职"状态的用户才能登录,新创建的用户需要完成财务登记审批流程
if user.state != '在职':
if user.state == '待登记':
return Response(
{'message': '您的账号尚未完成财务登记,请联系财务部门完成入职财务登记审批。', 'code': 1},
status=status.HTTP_400_BAD_REQUEST)
else:
return Response({'message': '你的账号已经封存。', 'code': 1}, status=status.HTTP_400_BAD_REQUEST)
# 比较密码
if user.password == password or user.token == token:
# if '123456' == password or user.token == token:
# 更新 encryption 字段到数据库
user.token = token
user.save()
# 记录登录日志
log_operation(
request=request,
operation_type='LOGIN',
module='User',
action='用户登录',
target_type='User',
target_id=user.id,
target_name=user.username,
remark=f'用户登录:{user.username}(账号:{user.account}'
)
# 创建会话
session = SessionStore()
session.create()
session['user_id'] = user.id
session.save()
# 置 sessionid 到响应的 cookie 中
response = Response({
'id': user.id,
'message': '登录成功',
'code': 0
}, status=status.HTTP_200_OK)
response.set_cookie(key='sessionid', value=session.session_key, httponly=True)
return response
else:
return Response({'message': '密码错误', 'code': 1}, status=status.HTTP_400_BAD_REQUEST)
except User.DoesNotExist:
return Response({'status': 'error', 'message': '用户不存在', 'code': 1}, status=status.HTTP_400_BAD_REQUEST)
class PersonnelDetailsView(APIView):
"""展示人员信息"""
def post(self, request, *args, **kwargs):
token = request.META.get('token')
user = User.objects.prefetch_related('role', 'department').get(token=token, is_deleted=False)
# 如果是admin用户直接返回最高权限标识
if user.username == 'admin' or user.account == 'admin':
permission_data = ['*:*:*']
else:
# 非admin用户从数据库查询权限
permissionId = list(user.role.values("permissionId"))
permission_ids_raw = [item['permissionId'] for item in permissionId if item['permissionId']]
permission_ids = []
# 解析权限ID支持多种格式
for perm_str in permission_ids_raw:
if not perm_str:
continue
try:
# 处理逗号分隔的字符串格式(如 "1,2,3"
if ',' in perm_str and not perm_str.strip().startswith('['):
perm_list = [int(x.strip()) for x in perm_str.split(',') if x.strip()]
permission_ids.extend(perm_list)
else:
# 处理 JSON 列表格式(如 "[1,2,3]")或单个数字字符串(如 "1"
parsed = ast.literal_eval(perm_str)
if isinstance(parsed, list):
permission_ids.extend(parsed)
elif isinstance(parsed, int):
# 单个数字的情况
permission_ids.append(parsed)
except (ValueError, SyntaxError):
# 如果解析失败跳过这个权限ID
continue
permissions = permission.objects.filter(id__in=permission_ids)
permission_data = []
for per in permissions:
permission_data.append(per.permission_logo)
data = {
'id': user.id,
'username': user.username,
'account': user.account,
"ethnicity": user.ethnicity,
"card": user.card,
"mobilePhone": user.mobilePhone,
"position": user.position,
"team": user.team,
"Dateofjoining": user.Dateofjoining,
"Confirmationtime": user.Confirmationtime,
'Practicingcertificatetime': user.Practicingcertificatetime,
"Dateofdeparture": user.Dateofdeparture,
"AcademicResume": user.AcademicResume,
"academic": user.academic,
"contract": user.contract,
"ApplicationForm": user.ApplicationForm,
"state": user.state,
# 角色数据
"role": list(user.role.values('id', 'RoleName', "permissionId")), # 假设Role模型有name字段
# 如果需要部门数据
"department": list(user.department.values('id', 'username')),
"permission_data": permission_data,
}
return Response({'message': '详细人员信息展示成功', "data": data, 'code': 0}, status=status.HTTP_200_OK)
class DepartmentView(APIView):
def post(self, request, *args, **kwargs):
"""部门列表"""
name = request.data.get('name')
Q_obj = Q()
if name:
Q_obj &= Q(username__icontains=name)
deps = Department.objects.filter(Q_obj, is_deleted=False)
data = []
for dep in deps:
data.append({
'id': dep.id,
'name': dep.username,
})
return Response({'message': '展示成功', "data": data, 'code': 0}, status=status.HTTP_200_OK)
class PersonnelListView(APIView):
def post(self, request, *args, **kwargs):
"""
人员列表
:param request:
:param args:
:param kwargs:
:return:
"""
page = request.data.get('page')
per_page = request.data.get('per_page')
username = request.data.get('username')
department = request.data.get('department')
if not all([page, per_page]):
return Response({'status': 'error', 'message': '缺少参数', 'code': 1}, status=status.HTTP_400_BAD_REQUEST)
Q_obj = Q(is_deleted=False) # 只查询未软删除的用户
if username:
Q_obj &= Q(username__icontains=username)
if department:
Q_obj &= Q(department__username__icontains=department)
users = User.objects.prefetch_related('department', 'role').filter(Q_obj).order_by('-id')
total = len(users)
paginator = Paginator(users, per_page)
try:
user_agents_page = paginator.page(page)
except PageNotAnInteger:
user_agents_page = paginator.page(1)
except EmptyPage:
user_agents_page = paginator.page(paginator.num_pages)
data = []
for info in user_agents_page.object_list:
data.append({
'id': info.id,
"username": info.username, # 姓名
"account": info.account, # 账号
"nation": info.ethnicity, # 名族
"IdCard": info.card, # 身份证
"mobilePhone": info.mobilePhone, # 手机号
"department": list(info.department.values('id', 'username')),
"role": list(info.role.values('id', 'RoleName', 'permissionId')), # 角色信息
"position": info.position, # 岗位
"team": info.team, # 所属团队
"Dateofjoining": info.Dateofjoining, # 入职时间
"Confirmationtime": info.Confirmationtime, # # 转正时间
'Practicingcertificatetime': info.Practicingcertificatetime, # 执业证时间
"Dateofdeparture": info.Dateofdeparture, # 离职时间
"AcademicResume": info.AcademicResume, # 学业简历
"academic": info.academic, # 学历信息
"contract": info.contract, # 合同
"ApplicationForm": info.ApplicationForm, # 入职申请表
"salary": info.salary, # 工资
"state": info.state, # 状态
})
return Response({'message': '查看成功', "total": total, 'data': data, 'code': 0}, status=status.HTTP_200_OK)
class AddDepartment(APIView):
def post(self, request, *args, **kwargs):
name = request.data.get('name')
if not all([name]):
return Response({'status': 'error', 'message': '缺少参数', 'code': 1}, status=status.HTTP_400_BAD_REQUEST)
dep = Department.objects.filter(username=name, is_deleted=False).first()
if dep:
return Response({'message': '添加失败,部门存在', 'code': 0}, status=status.HTTP_400_BAD_REQUEST)
Department.objects.create(username=name)
return Response({'message': '添加部门成功', 'code': 0}, status=status.HTTP_200_OK)
class DeleteDepartment(APIView):
def post(self, request, *args, **kwargs):
id = request.data.get('id')
if not all([id]):
return Response({'status': 'error', 'message': '缺少参数', 'code': 1}, status=status.HTTP_400_BAD_REQUEST)
try:
dep = Department.objects.get(id=id, is_deleted=False)
except Department.DoesNotExist:
return Response({'status': 'error', 'message': '部门不存在', 'code': 1}, status=status.HTTP_404_NOT_FOUND)
department = User.objects.filter(department=dep, is_deleted=False)
if department.exists():
return Response({'status': 'error', 'message': '删除失败,该部门还存在人员,请及时转移', 'code': 1},
status=status.HTTP_400_BAD_REQUEST)
# 记录操作前的数据
old_data = {
'id': dep.id,
'username': dep.username
}
# 软删除:更新 is_deleted 字段
dep.is_deleted = True
dep.save()
# 记录操作日志
log_operation(
request=request,
operation_type='DELETE',
module='User',
action='删除部门',
target_type='Department',
target_id=dep.id,
target_name=dep.username,
old_data=old_data,
remark=f'删除部门:{dep.username}'
)
return Response({'message': '删除成功', 'code': 0}, status=status.HTTP_200_OK)
class Personlist(APIView):
"""
部门列表展示
"""
def post(self, request, *args, **kwargs):
users = Department.objects.filter(is_deleted=False).order_by('id')
page = request.data.get('page')
per_page = request.data.get('per_page')
total = len(users)
paginator = Paginator(users, per_page)
try:
user_agents_page = paginator.page(page)
except PageNotAnInteger:
user_agents_page = paginator.page(1)
except EmptyPage:
user_agents_page = paginator.page(paginator.num_pages)
department_dict = []
for user in user_agents_page.object_list:
department_dict.append({
'id': user.id,
"name": user.username, # 修改为 name与前端期望的字段名一致
"department": user.username, # 同时保留 username 字段以兼容
})
return Response({'message': '展示成功', "total": total, 'data': department_dict, 'code': 0},
status=status.HTTP_200_OK)
class roxyExhibition(APIView):
def post(self, request, *args, **kwargs):
"""
代办展示
:param request:
:param args:
:param kwargs:
:return:
"""
page = request.data.get('page')
per_page = request.data.get('per_page')
token = request.META.get('token')
if not all([page, per_page]):
return Response({'status': 'error', 'message': '缺少参数', 'code': 1}, status=status.HTTP_400_BAD_REQUEST)
user = User.objects.prefetch_related('department').get(token=token)
# 获取用户所属的所有部门ID转为字符串因为personincharge存储的是字符串
user_department_ids = list(user.department.values_list('id', flat=True))
user_department_ids_str = [str(did) for did in user_department_ids]
# 获取用户的部门名称列表
user_departments = user.department.values_list('username', flat=True)
user_department_names = list(user_departments)
# 判断用户是否是财务部人员:只通过部门名称判断
is_finance_user = '财务部' in user_department_names
finance_personincharge_candidates = get_finance_personincharge_candidates()
# 构建查询条件:
# personincharge字段统一规则
# - 纯数字字符串 = 部门ID该部门下所有人员都能看到
# - 非纯数字字符串 = 审批员用户名(只有该审批员能看到)
# - "财务" = 财务部门(只有财务部人员能看到)
# 状态:审核中 或 已抄送财务(财务能看到)
# 重要多人团队审核时只有当前审核人personincharge等于当前用户名才能看到审核记录
query = Q(state__in=["审核中", "已抄送财务"])
# 部门匹配personincharge字段是纯数字字符串且匹配用户所属部门
department_query = Q()
if user_department_ids_str:
# 匹配personincharge字段等于用户所属的任一部门ID纯数字字符串
department_query = Q(personincharge__in=user_department_ids_str)
# 审批员匹配personincharge字段是用户名非纯数字字符串
# 重要只匹配当前审核人确保第一个审核人审核完后personincharge被更新为第二个审核人时第一个审核人不再看到
person_query = Q(personincharge=user.username)
# 财务匹配personincharge字段是"财务",且状态为"已抄送财务"
# 重要:只有财务部人员才能看到财务审核的审批记录
finance_query = Q()
if is_finance_user:
finance_query = Q(personincharge__in=finance_personincharge_candidates, state="已抄送财务")
# 组合查询:部门匹配 OR 审批员匹配 OR 财务匹配(仅财务部人员)
# 如果用户有部门,使用部门匹配;否则只使用审批员匹配
# 注意:审批员匹配必须精确匹配当前用户名,确保多人团队审核时,只有当前审核人能看到
if user_department_ids_str:
query &= (department_query | person_query | finance_query)
else:
# 如果用户没有部门,匹配审批员或财务(仅财务部人员)
query &= (person_query | finance_query)
# 添加调试日志
import logging
logger = logging.getLogger(__name__)
logger.info(f"roxyExhibition: 用户={user.username}, 部门IDs={user_department_ids_str}, 部门名称={user_department_names}")
logger.info(f"roxyExhibition: 是否是财务部人员={is_finance_user} (仅通过部门名称判断)")
logger.info(
f"roxyExhibition: 查询条件 - personincharge={user.username} "
f"OR personincharge in 财务标识{finance_personincharge_candidates}(已抄送财务,仅财务部) "
f"OR personincharge IN {user_department_ids_str}"
)
approvals = Approval.objects.filter(query, is_deleted=False).order_by('-id')
# 记录查询结果
logger.info(f"roxyExhibition: 查询到 {approvals.count()} 条审批记录")
for approval in approvals[:5]: # 只记录前5条
logger.info(f"roxyExhibition: 审批记录 ID={approval.id}, type={approval.type}, personincharge={approval.personincharge}, state={approval.state}, content={approval.content[:50]}")
# 过滤掉关联的业务记录已被删除的审批记录
# 获取所有已删除的案件ID转为字符串因为Approval.user_id是CharField
deleted_case_ids = Case.objects.filter(is_deleted=True).values_list('id', flat=True)
deleted_case_ids_str = [str(cid) for cid in deleted_case_ids]
# 获取所有已删除的变更申请ID
deleted_change_request_ids = CaseChangeRequest.objects.filter(is_deleted=True).values_list('id', flat=True)
deleted_change_request_ids_str = [str(crid) for crid in deleted_change_request_ids]
# 获取所有已删除的投标登记ID
deleted_bid_ids = Bid.objects.filter(is_deleted=True).values_list('id', flat=True)
deleted_bid_ids_str = [str(bid) for bid in deleted_bid_ids]
# 获取所有已删除的立项登记ID
deleted_project_ids = ProjectRegistration.objects.filter(is_deleted=True).values_list('id', flat=True)
deleted_project_ids_str = [str(pid) for pid in deleted_project_ids]
# 获取所有已删除的收入确认ID
deleted_income_ids = Income.objects.filter(is_deleted=True).values_list('id', flat=True)
deleted_income_ids_str = [str(iid) for iid in deleted_income_ids]
# 过滤已删除的开票记录
from finance.models import Invoice
deleted_invoice_ids = Invoice.objects.filter(is_deleted=True).values_list('id', flat=True)
deleted_invoice_ids_str = [str(iid) for iid in deleted_invoice_ids]
# 获取所有已删除的调账申请ID
deleted_accounts_ids = Accounts.objects.filter(is_deleted=True).values_list('id', flat=True)
deleted_accounts_ids_str = [str(aid) for aid in deleted_accounts_ids]
# 获取所有已删除的付款申请ID
deleted_payment_ids = Payment.objects.filter(is_deleted=True).values_list('id', flat=True)
deleted_payment_ids_str = [str(pid) for pid in deleted_payment_ids]
# 获取所有已删除的报销申请ID
deleted_reimbursement_ids = Reimbursement.objects.filter(is_deleted=True).values_list('id', flat=True)
deleted_reimbursement_ids_str = [str(rid) for rid in deleted_reimbursement_ids]
# 获取所有已删除的工资/奖金变更ID
deleted_bonus_change_ids = BonusChange.objects.filter(is_deleted=True).values_list('id', flat=True)
deleted_bonus_change_ids_str = [str(bid) for bid in deleted_bonus_change_ids]
# 获取所有已删除的申请用印ID
deleted_seal_application_ids = SealApplication.objects.filter(is_deleted=True).values_list('id', flat=True)
deleted_seal_application_ids_str = [str(sid) for sid in deleted_seal_application_ids]
# 获取所有已删除的用户ID用于入职财务登记和离职财务登记
deleted_user_ids = User.objects.filter(is_deleted=True).values_list('id', flat=True)
deleted_user_ids_str = [str(uid) for uid in deleted_user_ids]
# 获取所有已删除的待办ID
from business.models import Schedule
deleted_schedule_ids = Schedule.objects.filter(is_deleted=True).values_list('id', flat=True)
deleted_schedule_ids_str = [str(sid) for sid in deleted_schedule_ids]
# 排除关联的业务记录已被删除的审批记录
exclude_conditions = Q()
# 排除 type="案件管理" 且关联的案件已被删除的审批记录
if deleted_case_ids_str:
exclude_conditions |= Q(type="案件管理") & Q(user_id__in=deleted_case_ids_str)
# 排除 type="案件变更" 且关联的变更申请已被删除的审批记录
if deleted_change_request_ids_str:
exclude_conditions |= Q(type="案件变更") & Q(user_id__in=deleted_change_request_ids_str)
# 排除 type="投标登记" 且关联的投标登记已被删除的审批记录
if deleted_bid_ids_str:
exclude_conditions |= Q(type="投标登记") & Q(user_id__in=deleted_bid_ids_str)
# 排除 type="立项登记" 且关联的立项登记已被删除的审批记录
if deleted_project_ids_str:
exclude_conditions |= Q(type="立项登记") & Q(user_id__in=deleted_project_ids_str)
# 排除 type="收入确认" 且关联的收入确认已被删除的审批记录
if deleted_income_ids_str:
exclude_conditions |= Q(type="收入确认") & Q(user_id__in=deleted_income_ids_str)
# 过滤已删除的开票记录
if deleted_invoice_ids_str:
exclude_conditions |= Q(type="开票") & Q(user_id__in=deleted_invoice_ids_str)
# 排除 type="调账申请" 且关联的调账申请已被删除的审批记录
if deleted_accounts_ids_str:
exclude_conditions |= Q(type="调账申请") & Q(user_id__in=deleted_accounts_ids_str)
# 排除 type="付款申请" 且关联的付款申请已被删除的审批记录
if deleted_payment_ids_str:
exclude_conditions |= Q(type="付款申请") & Q(user_id__in=deleted_payment_ids_str)
# 排除 type="报销申请" 且关联的报销申请已被删除的审批记录
if deleted_reimbursement_ids_str:
exclude_conditions |= Q(type="报销申请") & Q(user_id__in=deleted_reimbursement_ids_str)
# 排除 type="工资/奖金变更" 且关联的工资/奖金变更已被删除的审批记录
if deleted_bonus_change_ids_str:
exclude_conditions |= Q(type="工资/奖金变更") & Q(user_id__in=deleted_bonus_change_ids_str)
# 排除 type="申请用印" 且关联的申请用印已被删除的审批记录
if deleted_seal_application_ids_str:
exclude_conditions |= Q(type="申请用印") & Q(user_id__in=deleted_seal_application_ids_str)
# 排除 type="入职财务登记" 且关联的用户已被删除的审批记录
if deleted_user_ids_str:
exclude_conditions |= Q(type="入职财务登记") & Q(user_id__in=deleted_user_ids_str)
# 排除 type="离职财务登记" 且关联的用户已被删除的审批记录
if deleted_user_ids_str:
exclude_conditions |= Q(type="离职财务登记") & Q(user_id__in=deleted_user_ids_str)
# 排除 type="待办" 且关联的待办已被删除的审批记录
if deleted_schedule_ids_str:
exclude_conditions |= Q(type="待办") & Q(user_id__in=deleted_schedule_ids_str)
if exclude_conditions:
approvals = approvals.exclude(exclude_conditions)
# 调试信息(生产环境可以注释掉)
# print(f"用户: {user.username}, 部门IDs: {user_department_ids_str}")
# print(f"查询到的审批数量: {approvals.count()}")
total = len(approvals)
paginator = Paginator(approvals, per_page)
try:
user_agents_page = paginator.page(page)
except PageNotAnInteger:
user_agents_page = paginator.page(1)
except EmptyPage:
user_agents_page = paginator.page(paginator.num_pages)
data = []
for info in user_agents_page.object_list:
title = info.title
content = info.content
# 如果是案件管理类型的审批,添加委托人名字和相对方名字以便区分
if info.type == "案件管理":
try:
case_id = int(info.user_id)
case = Case.objects.filter(id=case_id, is_deleted=False).select_related('project').first()
if case:
# 从Case模型的client_name和party_name字段获取信息
# 或者从关联的project获取
client_name = case.client_name or ""
party_name = case.party_name or ""
# 如果Case中没有尝试从project获取
if not client_name and case.project:
client_name = case.project.client_info or ""
if not party_name and case.project:
party_name = case.project.party_info or ""
# 提取名字(如果包含其他信息,只取名字部分)
# client_name和party_name可能是完整信息如"张三身份证号xxx"
# 尝试提取名字部分
client_str = client_name.split('')[0].split(',')[0].strip() if client_name else '未知'
party_str = party_name.split('')[0].split(',')[0].strip() if party_name else '未知'
# 添加到title中格式案件管理信息提交委托人XXX vs 相对方XXX
title = f"案件管理信息提交(委托人:{client_str} vs 相对方:{party_str}"
# 也更新content添加案件信息
if "提交了一份案件信息" in content:
content = f"{content} - 委托人:{client_str},相对方:{party_str}"
except (ValueError, TypeError, AttributeError):
# 如果解析失败保持原有title和content
pass
# 获取业务记录的当前状态
business_state = None
is_approved = False
try:
if info.type == "收入确认":
try:
income = Income.objects.get(id=info.user_id, is_deleted=False)
business_state = income.state
is_approved = (income.state == "已通过")
except Income.DoesNotExist:
pass
elif info.type == "开票":
try:
invoice = Invoice.objects.get(id=info.user_id, is_deleted=False)
business_state = invoice.state
is_approved = (invoice.state == "已通过")
except Invoice.DoesNotExist:
pass
elif info.type == "付款申请":
try:
payment = Payment.objects.get(id=info.user_id, is_deleted=False)
business_state = payment.state
is_approved = (payment.state == "已通过")
except Payment.DoesNotExist:
pass
elif info.type == "报销申请":
try:
reimbursement = Reimbursement.objects.get(id=info.user_id, is_deleted=False)
business_state = reimbursement.state
is_approved = (reimbursement.state == "已完成")
except Reimbursement.DoesNotExist:
pass
elif info.type == "案件管理":
try:
case_id = int(info.user_id)
case = Case.objects.filter(id=case_id, is_deleted=False).first()
if case:
business_state = case.state
is_approved = (case.state == "已通过")
except (ValueError, TypeError, AttributeError):
pass
elif info.type == "入职财务登记" or info.type == "离职财务登记":
try:
user_id = int(info.user_id)
user = User.objects.filter(id=user_id, is_deleted=False).first()
if user:
business_state = user.state
is_approved = (user.state == "在职") if info.type == "入职财务登记" else (user.state == "已离职")
except (ValueError, TypeError, AttributeError):
pass
# 可以根据需要添加其他类型
except Exception as e:
import logging
logger = logging.getLogger(__name__)
logger.error(f"获取业务记录状态失败 (type={info.type}, user_id={info.user_id}): {str(e)}")
# 计算优化后的状态:审批中、待查看、已完成
# 状态判断逻辑:
# 1. 如果业务记录已通过is_approved=True则状态为"已完成"
# 2. 如果审批记录状态为"已抄送财务",则状态为"待查看"(给财务部用的)
# 3. 其他情况(如"审核中"、"未通过"等),则状态为"审批中"
approval_status = "审批中" # 默认状态
if is_approved:
approval_status = "已完成"
elif info.state == "已抄送财务":
approval_status = "待查看"
elif info.state == "审核中":
approval_status = "审批中"
elif info.state == "已通过":
# 如果审批记录是"已通过"但业务记录还没通过,可能是数据不一致,保持"审批中"
approval_status = "审批中"
elif info.state == "未通过":
approval_status = "审批中" # 未通过也可以继续审批流程
itme = {
'id': info.id,
"title": title,
"content": content,
"times": info.times,
"completeTiem": info.completeTiem,
"personincharge": info.personincharge,
"state": info.state, # 审批记录状态(原始状态)
"type": info.type,
"business_state": business_state, # 业务记录当前状态
"is_approved": is_approved, # 是否已经审核完全通过
"status": approval_status, # 优化后的状态:审批中、待查看、已完成
}
# 如果是离职财务登记类型从content中解析结算工资
if info.type == "离职财务登记":
settlement_salary = None
if content and "结算工资:" in content:
import re
# 匹配格式结算工资xxx元 或 结算工资:待审批人指定
match = re.search(r'结算工资:([^]*)', content)
if match:
salary_str = match.group(1).strip()
# 如果包含"元"字,提取数字部分
if '' in salary_str:
salary_match = re.search(r'(\d+(?:\.\d+)?)', salary_str)
if salary_match:
settlement_salary = salary_match.group(1)
elif salary_str == "待审批人指定":
settlement_salary = None
else:
# 尝试直接提取数字(处理其他格式)
salary_match = re.search(r'(\d+(?:\.\d+)?)', salary_str)
if salary_match:
settlement_salary = salary_match.group(1)
itme["settlement_salary"] = settlement_salary
# 如果是立项登记类型,检索冲突信息并添加到返回字段
if info.type == "立项登记":
try:
from business.views import search_related_records
project_id = int(info.user_id)
project = ProjectRegistration.objects.filter(id=project_id, is_deleted=False).first()
if project and project.client_info and project.party_info:
# 检索冲突记录
conflict_records = search_related_records(
project.client_info,
project.party_info,
exclude_project_id=project_id
)
# 添加三个冲突字段
itme["prefiling_conflicts"] = conflict_records.get('prefiling_conflicts', [])
itme["project_conflicts"] = conflict_records.get('project_conflicts', [])
itme["bid_conflicts"] = conflict_records.get('bid_conflicts', [])
else:
# 如果没有找到项目或缺少信息,返回空数组
itme["prefiling_conflicts"] = []
itme["project_conflicts"] = []
itme["bid_conflicts"] = []
except (ValueError, TypeError, AttributeError, ImportError):
# 如果解析失败,返回空数组
itme["prefiling_conflicts"] = []
itme["project_conflicts"] = []
itme["bid_conflicts"] = []
data.append(itme)
return Response({'message': '展示成功', "total": total, 'data': data, 'code': 0}, status=status.HTTP_200_OK)
class approvalProcessing(APIView):
def post(self, request, *args, **kwargs):
"""
消除代办
财务查看时只需要传type和id不需要传state默认为查看通过
:param request:
:param args:
:param kwargs:
:return:
"""
state = request.data.get('state')
type = request.data.get('type')
id = request.data.get('id')
if not all([type, id]):
return Response({'status': 'error', 'message': '缺少参数type或id', 'code': 1}, status=status.HTTP_400_BAD_REQUEST)
try:
approval = Approval.objects.get(id=id, is_deleted=False)
except Approval.DoesNotExist:
return Response({'status': 'error', 'message': '审批记录不存在或已被删除', 'code': 1}, status=status.HTTP_404_NOT_FOUND)
# 财务查看逻辑如果只传了type和id没有传state且当前是财务审核则默认为查看通过
from User.utils import is_finance_personincharge
is_finance_view = False
if not state and is_finance_personincharge(approval.personincharge) and approval.state == "已抄送财务":
state = "已通过" # 财务查看默认通过
is_finance_view = True
if type == "入职财务登记":
try:
user = User.objects.get(id=approval.user_id, is_deleted=False)
except User.DoesNotExist:
return Response({'status': 'error', 'message': '用户记录不存在或已被删除', 'code': 1}, status=status.HTTP_404_NOT_FOUND)
# 检查当前是否已经是财务审核
if is_finance_personincharge(approval.personincharge) and approval.state == "已抄送财务":
# 财务部审核逻辑如果只传了type和id不传state则默认为"已通过"
if not state:
state = "已通过"
if state == "已通过":
approval.state = "已通过"
user.state = "在职"
else:
approval.state = "未通过"
user.state = "异常"
approval.save(update_fields=['state'])
user.save(update_fields=['state'])
return Response({'message': '处理成功', 'code': 0}, status=status.HTTP_200_OK)
# 使用统一的审核流程处理函数
# 非财务查看时state参数是必填的
if not state:
return Response({'status': 'error', 'message': '缺少参数state审核状态已通过/未通过)', 'code': 1}, status=status.HTTP_400_BAD_REQUEST)
from User.utils import process_approval_flow
import logging
logger = logging.getLogger(__name__)
current_approver = approval.personincharge
logger.info(f"approvalProcessing-入职财务登记: 审批ID={approval.id}, 当前审核人={current_approver}, 审核状态={state}, 审批内容={approval.content}")
is_completed, error = process_approval_flow(
approval=approval,
business_record=user, # User模型没有approvers_order字段但可以更新state
current_approver=current_approver,
state=state,
approval_type="入职财务登记",
final_state_map={"已通过": "在职", "未通过": "异常"}
)
# 刷新审批记录,查看更新后的状态
approval.refresh_from_db()
logger.info(f"approvalProcessing-入职财务登记: 审核后 - personincharge={approval.personincharge}, state={approval.state}")
if error:
return Response({'status': 'error', 'message': error, 'code': 1}, status=status.HTTP_400_BAD_REQUEST)
# 如果审核不通过,更新用户状态
if state == "未通过":
user.state = "异常"
user.save(update_fields=['state'])
return Response({'message': '处理成功', 'code': 0}, status=status.HTTP_200_OK)
if type == "开票":
from finance.models import Invoice
try:
invoice = Invoice.objects.get(id=approval.user_id, is_deleted=False)
except Invoice.DoesNotExist:
return Response({'status': 'error', 'message': '开票记录不存在或已被删除', 'code': 1}, status=status.HTTP_404_NOT_FOUND)
# 检查当前是否已经是财务审核
if is_finance_personincharge(approval.personincharge) and approval.state == "已抄送财务":
# 财务部审核逻辑如果只传了type和id不传state则默认为"已通过"
if not state:
state = "已通过"
if state == "已通过":
approval.state = "已通过"
invoice.state = "已通过"
else:
approval.state = "未通过"
invoice.state = "未通过"
invoice.save(update_fields=['state'])
approval.save(update_fields=['state'])
return Response({'message': '处理成功', 'code': 0}, status=status.HTTP_200_OK)
# 使用统一的审核流程处理函数
# 非财务查看时state参数是必填的
if not state:
return Response({'status': 'error', 'message': '缺少参数state审核状态已通过/未通过)', 'code': 1}, status=status.HTTP_400_BAD_REQUEST)
from User.utils import process_approval_flow
current_approver = approval.personincharge
is_completed, error = process_approval_flow(
approval=approval,
business_record=invoice,
current_approver=current_approver,
state=state,
approval_type="开票",
final_state_map={"已通过": "已通过", "未通过": "未通过"}
)
if error:
return Response({'status': 'error', 'message': error, 'code': 1}, status=status.HTTP_400_BAD_REQUEST)
return Response({'message': '处理成功', 'code': 0}, status=status.HTTP_200_OK)
if type == "收入确认":
try:
income = Income.objects.get(id=approval.user_id, is_deleted=False)
except Income.DoesNotExist:
return Response({'status': 'error', 'message': '收入确认记录不存在或已被删除', 'code': 1}, status=status.HTTP_404_NOT_FOUND)
# 检查当前是否已经是财务审核
if is_finance_personincharge(approval.personincharge) and approval.state == "已抄送财务":
# 财务部只是查看,不需要审核,状态已经是"已通过"
# 如果财务标记为未通过,则更新状态
if state == "未通过":
approval.state = "未通过"
income.state = "未通过"
income.save(update_fields=['state'])
approval.save(update_fields=['state'])
return Response({'message': '处理成功', 'code': 0}, status=status.HTTP_200_OK)
else:
# 财务查看通过默认或不传state状态保持"已通过"(已经是"已通过"
# 如果审批记录状态不是"已通过",更新为"已通过"(表示财务已查看)
if approval.state != "已通过":
approval.state = "已通过"
approval.save(update_fields=['state'])
return Response({'message': '处理成功', 'code': 0}, status=status.HTTP_200_OK)
# 负责人填写收入分配
allocate = request.data.get('allocate') # 收入分配(负责人必须填写)
# 如果当前状态是"待负责人填写分配",负责人必须填写收入分配
if income.state == "待负责人填写分配":
if not allocate:
return Response({
'status': 'error',
'message': '请填写收入分配',
'code': 1
}, status=status.HTTP_400_BAD_REQUEST)
# 更新收入分配
income.allocate = allocate
# 负责人填写收入分配后,抄送财务时就已经审核通过,财务只是查看
income.state = "已通过"
# 更新审批内容,添加分配信息
if "收入分配:待负责人指定" in approval.content:
approval.content = approval.content.replace("收入分配:待负责人指定", f"收入分配:{allocate}")
else:
approval.content = approval.content + f",收入分配:{allocate}"
income.save(update_fields=['allocate', 'state'])
# 负责人填写收入分配后,抄送给财务负责人
from User.utils import get_finance_personincharge_value
finance_personincharge = get_finance_personincharge_value()
# 更新审批记录,抄送给财务(状态已经是"已通过"
approval.personincharge = finance_personincharge
approval.state = "已抄送财务"
approval.content = approval.content + ",已抄送财务部"
approval.save(update_fields=['personincharge', 'state', 'content'])
return Response({
'message': '收入分配已提交,已抄送给财务',
'code': 0,
'data': {
'allocate': allocate,
'state': income.state
}
}, status=status.HTTP_200_OK)
# 兼容旧流程:如果负责人未填写分配,但审批人指定了分配方案
if allocate:
income.allocate = allocate
# 更新审批内容,添加分配信息
if "收入分配:待审批人指定" in approval.content:
approval.content = approval.content.replace("收入分配:待审批人指定", f"收入分配:{allocate}")
elif "收入分配:待负责人指定" in approval.content:
approval.content = approval.content.replace("收入分配:待负责人指定", f"收入分配:{allocate}")
else:
approval.content = approval.content + f",收入分配:{allocate}"
income.save(update_fields=['allocate'])
# 使用统一的审核流程处理函数(兼容旧流程)
# 非财务查看时state参数是必填的
if not state:
return Response({'status': 'error', 'message': '缺少参数state审核状态已通过/未通过)', 'code': 1}, status=status.HTTP_400_BAD_REQUEST)
from User.utils import process_approval_flow
current_approver = approval.personincharge
is_completed, error = process_approval_flow(
approval=approval,
business_record=income,
current_approver=current_approver,
state=state,
approval_type="收入确认",
final_state_map={"已通过": "已通过", "未通过": "未通过"}
)
if error:
return Response({'status': 'error', 'message': error, 'code': 1}, status=status.HTTP_400_BAD_REQUEST)
return Response({'message': '处理成功', 'code': 0}, status=status.HTTP_200_OK)
if type == "调账申请":
try:
account = Accounts.objects.get(id=approval.user_id, is_deleted=False)
except Accounts.DoesNotExist:
return Response({'status': 'error', 'message': '调账申请记录不存在或已被删除', 'code': 1}, status=status.HTTP_404_NOT_FOUND)
# 调账申请不需要审核流程,直接抄送财务查看即可
# 检查当前是否已经是财务审核
if is_finance_personincharge(approval.personincharge) and approval.state == "已抄送财务":
# 财务部只是查看,不需要审核,状态已经是"已通过"
# 如果财务标记为未通过,则更新状态
if state == "未通过":
approval.state = "未通过"
account.state = "未通过"
account.save(update_fields=['state'])
approval.save(update_fields=['state'])
return Response({'message': '处理成功', 'code': 0}, status=status.HTTP_200_OK)
else:
# 财务查看通过默认或不传state状态保持"已通过"(已经是"已通过"
# 如果审批记录状态不是"已通过",更新为"已通过"(表示财务已查看)
if approval.state != "已通过":
approval.state = "已通过"
approval.save(update_fields=['state'])
return Response({'message': '处理成功', 'code': 0}, status=status.HTTP_200_OK)
# 其他情况(兼容旧数据)
return Response({'message': '调账申请不需要审核,已直接抄送财务', 'code': 0}, status=status.HTTP_200_OK)
if type == "付款申请":
try:
payment = Payment.objects.get(id=approval.user_id, is_deleted=False)
except Payment.DoesNotExist:
return Response({'status': 'error', 'message': '付款申请记录不存在或已被删除', 'code': 1}, status=status.HTTP_404_NOT_FOUND)
# 检查当前是否已经是财务审核
if is_finance_personincharge(approval.personincharge) and approval.state == "已抄送财务":
# 财务部审核逻辑如果只传了type和id不传state则默认为"已通过"
if not state:
state = "已通过"
if state == "已通过":
approval.state = "已通过"
payment.state = "已通过"
else:
approval.state = "未通过"
payment.state = "未通过"
payment.save(update_fields=['state'])
approval.save(update_fields=['state'])
return Response({'message': '处理成功', 'code': 0}, status=status.HTTP_200_OK)
# 使用统一的审核流程处理函数(与离职逻辑一样)
# 非财务查看时state参数是必填的
if not state:
return Response({'status': 'error', 'message': '缺少参数state审核状态已通过/未通过)', 'code': 1}, status=status.HTTP_400_BAD_REQUEST)
from User.utils import process_approval_flow
current_approver = approval.personincharge
is_completed, error = process_approval_flow(
approval=approval,
business_record=payment,
current_approver=current_approver,
state=state,
approval_type="付款申请",
final_state_map={"已通过": "已通过", "未通过": "未通过"}
)
if error:
return Response({'status': 'error', 'message': error, 'code': 1}, status=status.HTTP_400_BAD_REQUEST)
return Response({'message': '处理成功', 'code': 0}, status=status.HTTP_200_OK)
if type == "报销申请":
try:
reimbursement = Reimbursement.objects.get(id=approval.user_id, is_deleted=False)
except Reimbursement.DoesNotExist:
return Response({'status': 'error', 'message': '报销申请记录不存在或已被删除', 'code': 1}, status=status.HTTP_404_NOT_FOUND)
# 使用统一的审核流程处理函数
from User.utils import process_approval_flow
current_approver = approval.personincharge
is_completed, error = process_approval_flow(
approval=approval,
business_record=reimbursement,
current_approver=current_approver,
state=state,
approval_type="报销申请",
final_state_map={"已通过": "已完成", "未通过": "未通过"}
)
if error:
return Response({'status': 'error', 'message': error, 'code': 1}, status=status.HTTP_400_BAD_REQUEST)
return Response({'message': '处理成功', 'code': 0}, status=status.HTTP_200_OK)
if type == "工资/奖金变更":
try:
bonus = BonusChange.objects.get(id=approval.user_id, is_deleted=False)
except BonusChange.DoesNotExist:
return Response({'status': 'error', 'message': '工资/奖金变更记录不存在或已被删除', 'code': 1}, status=status.HTTP_404_NOT_FOUND)
# 使用统一的审核流程处理函数
from User.utils import process_approval_flow
current_approver = approval.personincharge
is_completed, error = process_approval_flow(
approval=approval,
business_record=bonus,
current_approver=current_approver,
state=state,
approval_type="工资/奖金变更",
final_state_map={"已通过": "已通过", "未通过": "未通过"}
)
if error:
return Response({'status': 'error', 'message': error, 'code': 1}, status=status.HTTP_400_BAD_REQUEST)
return Response({'message': '处理成功', 'code': 0}, status=status.HTTP_200_OK)
if type == "立项登记":
try:
project = ProjectRegistration.objects.get(id=approval.user_id, is_deleted=False)
except ProjectRegistration.DoesNotExist:
return Response({'status': 'error', 'message': '立项登记记录不存在或已被删除', 'code': 1}, status=status.HTTP_404_NOT_FOUND)
# 使用统一的审核流程处理函数
from User.utils import process_approval_flow
current_approver = approval.personincharge
is_completed, error = process_approval_flow(
approval=approval,
business_record=project,
current_approver=current_approver,
state=state,
approval_type="立项登记",
final_state_map={"已通过": "已通过", "未通过": "未通过"}
)
if error:
return Response({'status': 'error', 'message': error, 'code': 1}, status=status.HTTP_400_BAD_REQUEST)
return Response({'message': '处理成功', 'code': 0}, status=status.HTTP_200_OK)
if type == "投标登记":
try:
bid = Bid.objects.get(id=approval.user_id, is_deleted=False)
except Bid.DoesNotExist:
return Response({'status': 'error', 'message': '投标登记记录不存在或已被删除', 'code': 1}, status=status.HTTP_404_NOT_FOUND)
# 使用统一的审核流程处理函数
from User.utils import process_approval_flow
current_approver = approval.personincharge
is_completed, error = process_approval_flow(
approval=approval,
business_record=bid,
current_approver=current_approver,
state=state,
approval_type="投标登记",
final_state_map={"已通过": "已通过", "未通过": "未通过"}
)
if error:
return Response({'status': 'error', 'message': error, 'code': 1}, status=status.HTTP_400_BAD_REQUEST)
return Response({'message': '处理成功', 'code': 0}, status=status.HTTP_200_OK)
if type == "案件管理":
try:
approval = Approval.objects.get(id=id, is_deleted=False)
except Approval.DoesNotExist:
return Response({'status': 'error', 'message': '审批记录不存在或已被删除', 'code': 1}, status=status.HTTP_404_NOT_FOUND)
try:
case = Case.objects.get(id=approval.user_id, is_deleted=False)
except Case.DoesNotExist:
return Response({'status': 'error', 'message': '案件记录不存在或已被删除', 'code': 1}, status=status.HTTP_404_NOT_FOUND)
# 检查当前是否已经是财务审核
if is_finance_personincharge(approval.personincharge) and approval.state == "已抄送财务":
# 财务部审核逻辑:财务部只需要一个人审核完即可完成
if state == "已通过":
approval.state = "已通过"
case.state = "已通过"
else:
approval.state = "未通过"
case.state = "未通过"
approval.save(update_fields=['state'])
case.save(update_fields=['state'])
return Response({'message': '处理成功', 'code': 0}, status=status.HTTP_200_OK)
# 使用统一的审核流程处理函数
from User.utils import process_approval_flow
current_approver = approval.personincharge
is_completed, error = process_approval_flow(
approval=approval,
business_record=case, # Case模型有approvers_order字段
current_approver=current_approver,
state=state,
approval_type="案件管理",
final_state_map={"已通过": "已通过", "未通过": "未通过"}
)
if error:
return Response({'status': 'error', 'message': error, 'code': 1}, status=status.HTTP_400_BAD_REQUEST)
return Response({'message': '处理成功', 'code': 0}, status=status.HTTP_200_OK)
if type == "案件变更":
try:
approval = Approval.objects.get(id=id, is_deleted=False)
except Approval.DoesNotExist:
return Response({'status': 'error', 'message': '审批记录不存在或已被删除', 'code': 1}, status=status.HTTP_404_NOT_FOUND)
try:
change_request = CaseChangeRequest.objects.get(id=approval.user_id, is_deleted=False)
except CaseChangeRequest.DoesNotExist:
return Response({'status': 'error', 'message': '变更申请记录不存在或已被删除', 'code': 1}, status=status.HTTP_404_NOT_FOUND)
current_approver = approval.personincharge
if state == "未通过":
approval.state = "未通过"
change_request.state = "未通过"
approval.save(update_fields=['state'])
change_request.save(update_fields=['state'])
return Response({'message': '处理成功', 'code': 0}, status=status.HTTP_200_OK)
approvers_list = get_approvers_from_record(change_request, approval=approval)
if not approvers_list:
approval.state = "已通过"
change_request.state = "已通过"
approval.save(update_fields=['state'])
change_request.save(update_fields=['state'])
return Response({'message': '处理成功', 'code': 0}, status=status.HTTP_200_OK)
try:
current_index = approvers_list.index(current_approver)
except ValueError:
approval.state = "已通过"
change_request.state = "已通过"
approval.save(update_fields=['state'])
change_request.save(update_fields=['state'])
return Response({'message': '处理成功', 'code': 0}, status=status.HTTP_200_OK)
if current_index < len(approvers_list) - 1:
next_approver = approvers_list[current_index + 1]
approval.personincharge = next_approver
approval.state = "审核中"
if "当前审批人:" in approval.content:
approval.content = approval.content.replace(
f"当前审批人:{current_approver}",
f"当前审批人:{next_approver}"
)
approval.save(update_fields=['state', 'personincharge', 'content'])
return Response({'message': '处理成功', 'code': 0}, status=status.HTTP_200_OK)
approval.state = "已通过"
change_request.state = "已通过"
approval.save(update_fields=['state'])
change_request.save(update_fields=['state'])
return Response({'message': '处理成功', 'code': 0}, status=status.HTTP_200_OK)
if type == "申请用印":
try:
seal_app = SealApplication.objects.get(id=approval.user_id, is_deleted=False)
except SealApplication.DoesNotExist:
return Response({'status': 'error', 'message': '用印申请记录不存在或已被删除', 'code': 1}, status=status.HTTP_404_NOT_FOUND)
# 检查当前是否已经是财务审核
if is_finance_personincharge(approval.personincharge) and approval.state == "已抄送财务":
# 财务部审核逻辑:财务部只需要一个人审核完即可完成
if state == "已通过":
approval.state = "已通过"
seal_app.state = "已通过"
else:
approval.state = "未通过"
seal_app.state = "未通过"
approval.save(update_fields=['state'])
seal_app.save(update_fields=['state'])
return Response({'message': '处理成功', 'code': 0}, status=status.HTTP_200_OK)
# 使用统一的审核流程处理函数
from User.utils import process_approval_flow
current_approver = approval.personincharge
is_completed, error = process_approval_flow(
approval=approval,
business_record=seal_app, # SealApplication模型有approvers_order字段
current_approver=current_approver,
state=state,
approval_type="申请用印",
final_state_map={"已通过": "已通过", "未通过": "未通过"}
)
if error:
return Response({'status': 'error', 'message': error, 'code': 1}, status=status.HTTP_400_BAD_REQUEST)
return Response({'message': '处理成功', 'code': 0}, status=status.HTTP_200_OK)
if type == "离职财务登记":
try:
user = User.objects.get(id=approval.user_id, is_deleted=False)
except User.DoesNotExist:
return Response({'status': 'error', 'message': '用户记录不存在或已被删除', 'code': 1}, status=status.HTTP_404_NOT_FOUND)
# 接收离职工资参数(可选,审批人可以填写)
settlement_salary = request.data.get('settlement_salary')
# 如果审批人填写了离职工资更新审批记录的content字段
if settlement_salary:
# 更新审批内容中的结算工资信息
content = approval.content
# 替换或添加结算工资信息
if "结算工资:" in content:
# 使用正则表达式替换结算工资部分
import re
content = re.sub(r'结算工资:[^]*', f'结算工资:{settlement_salary}', content)
else:
# 如果content中没有结算工资信息添加到末尾
content = content + f",结算工资:{settlement_salary}"
approval.content = content
approval.save(update_fields=['content'])
# 检查当前是否已经是财务审核
if is_finance_personincharge(approval.personincharge) and approval.state == "已抄送财务":
# 财务部审核逻辑:财务部只需要一个人审核完即可完成
if state == "已通过":
approval.state = "已通过"
# 用户状态已经在离职登记时设置为"已离职",这里不需要再次修改
else:
approval.state = "未通过"
# 如果审批未通过,恢复用户状态为"在职"
user.state = "在职"
user.Dateofdeparture = None
user.save(update_fields=['state', 'Dateofdeparture'])
approval.save(update_fields=['state'])
return Response({'message': '处理成功', 'code': 0}, status=status.HTTP_200_OK)
# 使用统一的审核流程处理函数(会同步离职工资信息到下一个审批人)
from User.utils import process_approval_flow
current_approver = approval.personincharge
is_completed, error = process_approval_flow(
approval=approval,
business_record=user, # User模型没有approvers_order字段但可以更新state
current_approver=current_approver,
state=state,
approval_type="离职财务登记",
final_state_map={"已通过": "已离职", "未通过": "在职"}
)
if error:
return Response({'status': 'error', 'message': error, 'code': 1}, status=status.HTTP_400_BAD_REQUEST)
# 如果审核不通过,恢复用户状态为"在职"
if state == "未通过":
user.state = "在职"
user.Dateofdeparture = None
user.save(update_fields=['state', 'Dateofdeparture'])
return Response({'message': '处理成功', 'code': 0}, status=status.HTTP_200_OK)
if type == "待办":
try:
from business.models import Schedule
schedule = Schedule.objects.get(id=approval.user_id, is_deleted=False)
except Schedule.DoesNotExist:
return Response({'status': 'error', 'message': '待办记录不存在或已被删除', 'code': 1}, status=status.HTTP_404_NOT_FOUND)
# 使用统一的审核流程处理函数
from User.utils import process_approval_flow
import logging
logger = logging.getLogger(__name__)
current_approver = approval.personincharge
logger.info(f"approvalProcessing-待办: 审批ID={approval.id}, 待办ID={schedule.id}, 当前审核人={current_approver}, 审核状态={state}, 审批内容={approval.content}")
is_completed, error = process_approval_flow(
approval=approval,
business_record=schedule,
current_approver=current_approver,
state=state,
approval_type="待办",
final_state_map={"已通过": "已完成", "未通过": "未通过"}
)
# 刷新审批记录,查看更新后的状态
approval.refresh_from_db()
schedule.refresh_from_db()
logger.info(f"approvalProcessing-待办: 审核后 - personincharge={approval.personincharge}, state={approval.state}, 待办状态={schedule.state}")
if error:
return Response({'status': 'error', 'message': error, 'code': 1}, status=status.HTTP_400_BAD_REQUEST)
return Response({'message': '处理成功', 'code': 0}, status=status.HTTP_200_OK)
return Response({'message': '处理成功', 'code': 0}, status=status.HTTP_200_OK)
class personneldisplay(APIView):
def get(self, request, *args, **kwargs):
"""
人员列表
:param request:
:param args:
:param kwargs:
:return:
"""
users = User.objects.filter(is_deleted=False)
data = []
for user in users:
itme = {
'id': user.id,
"username": user.username,
'position': user.position,
}
data.append(itme)
return Response({'message': '展示成功', "data": data, 'code': 0}, status=status.HTTP_200_OK)
class DeleteUser(APIView):
def post(self, request, *args, **kwargs):
"""
删除用户
:param request:
:param args:
:param kwargs:
:return:
"""
id = request.data.get('id')
if not id:
return Response({'status': 'error', 'message': '缺少参数', 'code': 1}, status=status.HTTP_400_BAD_REQUEST)
try:
user = User.objects.get(id=id, is_deleted=False)
except User.DoesNotExist:
return Response({'status': 'error', 'message': '用户不存在', 'code': 1}, status=status.HTTP_404_NOT_FOUND)
# 记录操作前的数据
old_data = {
'id': user.id,
'username': user.username,
'account': user.account,
'state': user.state,
'position': user.position,
'team': user.team
}
# 软删除:更新 is_deleted 字段
user.is_deleted = True
user.save()
# 记录操作日志
log_operation(
request=request,
operation_type='DELETE',
module='User',
action='删除用户',
target_type='User',
target_id=user.id,
target_name=user.username,
old_data=old_data,
remark=f'删除用户:{user.username}(账号:{user.account}'
)
return Response({'message': '删除成功', 'code': 0}, status=status.HTTP_200_OK)
class OperationLogView(APIView):
"""操作日志查询接口"""
def post(self, request, *args, **kwargs):
"""
查询操作日志
支持按操作人、操作类型、模块、时间范围等条件查询
"""
page = request.data.get('page', 1)
per_page = request.data.get('per_page', 20)
operator = request.data.get('operator') # 操作人
operation_type = request.data.get('operation_type') # 操作类型
module = request.data.get('module') # 模块
target_type = request.data.get('target_type') # 目标类型
start_time = request.data.get('start_time') # 开始时间
end_time = request.data.get('end_time') # 结束时间
# 构建查询条件
query = Q()
if operator:
query &= Q(operator__icontains=operator)
if operation_type:
query &= Q(operation_type=operation_type)
if module:
query &= Q(module=module)
if target_type:
query &= Q(target_type=target_type)
if start_time:
try:
start_datetime = datetime.datetime.strptime(start_time, "%Y-%m-%d")
query &= Q(create_time__gte=start_datetime)
except:
pass
if end_time:
try:
end_datetime = datetime.datetime.strptime(end_time, "%Y-%m-%d")
# 结束时间包含当天,所以加一天
end_datetime = end_datetime + datetime.timedelta(days=1)
query &= Q(create_time__lt=end_datetime)
except:
pass
# 查询日志
logs = OperationLog.objects.filter(query).order_by('-create_time')
total = logs.count()
# 分页
paginator = Paginator(logs, per_page)
try:
logs_page = paginator.page(page)
except PageNotAnInteger:
logs_page = paginator.page(1)
except EmptyPage:
logs_page = paginator.page(paginator.num_pages)
# 格式化数据
data = []
for log in logs_page.object_list:
data.append({
'id': log.id,
'operator': log.operator,
'operator_id': log.operator_id,
'operation_type': log.operation_type,
'module': log.module,
'action': log.action,
'target_type': log.target_type,
'target_id': log.target_id,
'target_name': log.target_name,
'ip_address': log.ip_address,
'request_path': log.request_path,
'remark': log.remark,
'create_time': log.create_time.strftime('%Y-%m-%d %H:%M:%S'),
'old_data': json.loads(log.old_data) if log.old_data else None,
'new_data': json.loads(log.new_data) if log.new_data else None,
})
return Response({
'message': '查询成功',
'total': total,
'data': data,
'code': 0
}, status=status.HTTP_200_OK)
# ========== 团队管理接口 ==========
class TeamView(APIView):
"""团队列表(不分页,用于下拉选择)"""
def post(self, request, *args, **kwargs):
name = request.data.get('name', '')
Q_obj = Q()
if name:
Q_obj &= Q(name__icontains=name)
teams = Team.objects.filter(Q_obj, is_deleted=False).order_by('id')
data = []
for team in teams:
data.append({
'id': team.id,
'name': team.name,
'team_type': team.team_type,
'team_type_display': team.get_team_type_display(),
'description': team.description,
})
return Response({'message': '展示成功', "data": data, 'code': 0}, status=status.HTTP_200_OK)
class TeamListView(APIView):
"""团队列表(分页)"""
def post(self, request, *args, **kwargs):
page = request.data.get('page', 1)
per_page = request.data.get('per_page', 10)
name = request.data.get('name', '')
Q_obj = Q()
if name:
Q_obj &= Q(name__icontains=name)
teams = Team.objects.filter(Q_obj, is_deleted=False).order_by('-id')
total = teams.count()
paginator = Paginator(teams, per_page)
try:
teams_page = paginator.page(page)
except PageNotAnInteger:
teams_page = paginator.page(1)
except EmptyPage:
teams_page = paginator.page(paginator.num_pages)
data = []
for team in teams_page.object_list:
# 统计该团队下的人员数量
user_count = User.objects.filter(team=team.name, is_deleted=False).count()
data.append({
'id': team.id,
'name': team.name,
'team_type': team.team_type,
'team_type_display': team.get_team_type_display(),
'description': team.description,
'user_count': user_count,
})
return Response({
'message': '查询成功',
'total': total,
'data': data,
'code': 0
}, status=status.HTTP_200_OK)
class AddTeam(APIView):
"""新增团队"""
def post(self, request, *args, **kwargs):
name = request.data.get('name')
team_type = request.data.get('team_type', 'team') # 默认为团队类型
description = request.data.get('description', '')
if not name:
return Response({'status': 'error', 'message': '团队名称不能为空', 'code': 1},
status=status.HTTP_400_BAD_REQUEST)
# 验证团队类型
if team_type not in ['personal', 'team']:
return Response({'status': 'error', 'message': '团队类型无效必须是personal个人团队或team团队', 'code': 1},
status=status.HTTP_400_BAD_REQUEST)
# 检查团队名称是否已存在
existing_team = Team.objects.filter(name=name, is_deleted=False).first()
if existing_team:
return Response({'status': 'error', 'message': '团队名称已存在', 'code': 1},
status=status.HTTP_400_BAD_REQUEST)
# 创建团队
team = Team.objects.create(name=name, team_type=team_type, description=description)
# 记录操作日志
new_data = {
'id': team.id,
'name': team.name,
'team_type': team.team_type,
'description': team.description
}
log_operation(
request=request,
operation_type='CREATE',
module='User',
action='新增团队',
target_type='Team',
target_id=team.id,
target_name=team.name,
new_data=new_data,
remark=f'新增团队:{team.name}(类型:{team.get_team_type_display()}'
)
return Response({'message': '添加团队成功', 'code': 0}, status=status.HTTP_200_OK)
class EditTeam(APIView):
"""编辑团队"""
def post(self, request, *args, **kwargs):
id = request.data.get('id')
name = request.data.get('name')
team_type = request.data.get('team_type')
description = request.data.get('description', '')
if not all([id, name]):
return Response({'status': 'error', 'message': '缺少参数', 'code': 1},
status=status.HTTP_400_BAD_REQUEST)
try:
team = Team.objects.get(id=id, is_deleted=False)
except Team.DoesNotExist:
return Response({'status': 'error', 'message': '团队不存在', 'code': 1},
status=status.HTTP_404_NOT_FOUND)
# 验证团队类型
if team_type and team_type not in ['personal', 'team']:
return Response({'status': 'error', 'message': '团队类型无效必须是personal个人团队或team团队', 'code': 1},
status=status.HTTP_400_BAD_REQUEST)
# 检查新名称是否与其他团队冲突
if name != team.name:
existing_team = Team.objects.filter(name=name, is_deleted=False).exclude(id=id).first()
if existing_team:
return Response({'status': 'error', 'message': '团队名称已存在', 'code': 1},
status=status.HTTP_400_BAD_REQUEST)
# 记录操作前的数据
old_data = {
'id': team.id,
'name': team.name,
'team_type': team.team_type,
'description': team.description
}
# 更新团队信息
team.name = name
if team_type:
team.team_type = team_type
team.description = description
team.save()
# 记录操作后的数据
new_data = {
'id': team.id,
'name': team.name,
'team_type': team.team_type,
'description': team.description
}
# 记录操作日志
log_operation(
request=request,
operation_type='UPDATE',
module='User',
action='编辑团队',
target_type='Team',
target_id=team.id,
target_name=team.name,
old_data=old_data,
new_data=new_data,
remark=f'编辑团队:{team.name}(类型:{team.get_team_type_display()}'
)
return Response({'message': '修改成功', 'code': 0}, status=status.HTTP_200_OK)
class DeleteTeam(APIView):
"""删除团队"""
def post(self, request, *args, **kwargs):
id = request.data.get('id')
if not id:
return Response({'status': 'error', 'message': '缺少参数', 'code': 1},
status=status.HTTP_400_BAD_REQUEST)
try:
team = Team.objects.get(id=id, is_deleted=False)
except Team.DoesNotExist:
return Response({'status': 'error', 'message': '团队不存在', 'code': 1},
status=status.HTTP_404_NOT_FOUND)
# 检查是否有用户属于该团队
user_count = User.objects.filter(team=team.name, is_deleted=False).count()
if user_count > 0:
return Response({
'status': 'error',
'message': f'删除失败,该团队下还有{user_count}名人员,请先转移人员',
'code': 1
}, status=status.HTTP_400_BAD_REQUEST)
# 记录操作前的数据
old_data = {
'id': team.id,
'name': team.name,
'team_type': team.team_type,
'description': team.description
}
# 软删除:更新 is_deleted 字段
team.is_deleted = True
team.save()
# 记录操作日志
log_operation(
request=request,
operation_type='DELETE',
module='User',
action='删除团队',
target_type='Team',
target_id=team.id,
target_name=team.name,
old_data=old_data,
remark=f'删除团队:{team.name}'
)
return Response({'message': '删除成功', 'code': 0}, status=status.HTTP_200_OK)
class ApprovalStatusCheck(APIView):
"""查询待办是否已经审核完全通过"""
def post(self, request, *args, **kwargs):
"""
查询待办的审核状态,判断是否已经审核完全通过
:param request:
:param args:
:param kwargs:
:return:
"""
type = request.data.get('type')
id = request.data.get('id')
if not all([type, id]):
return Response({'status': 'error', 'message': '缺少参数type或id', 'code': 1}, status=status.HTTP_400_BAD_REQUEST)
try:
approval = Approval.objects.get(id=id, is_deleted=False)
except Approval.DoesNotExist:
return Response({'status': 'error', 'message': '审批记录不存在或已被删除', 'code': 1}, status=status.HTTP_404_NOT_FOUND)
# 获取业务记录状态
business_state = None
is_approved = False
try:
if type == "收入确认":
from finance.models import Income
try:
income = Income.objects.get(id=approval.user_id, is_deleted=False)
business_state = income.state
is_approved = (income.state == "已通过")
except Income.DoesNotExist:
pass
elif type == "开票":
from finance.models import Invoice
try:
invoice = Invoice.objects.get(id=approval.user_id, is_deleted=False)
business_state = invoice.state
is_approved = (invoice.state == "已通过")
except Invoice.DoesNotExist:
pass
elif type == "付款申请":
from finance.models import Payment
try:
payment = Payment.objects.get(id=approval.user_id, is_deleted=False)
business_state = payment.state
is_approved = (payment.state == "已通过")
except Payment.DoesNotExist:
pass
elif type == "报销申请":
from finance.models import Reimbursement
try:
reimbursement = Reimbursement.objects.get(id=approval.user_id, is_deleted=False)
business_state = reimbursement.state
is_approved = (reimbursement.state == "已完成")
except Reimbursement.DoesNotExist:
pass
elif type == "案件管理":
from business.models import Case
try:
case = Case.objects.get(id=approval.user_id, is_deleted=False)
business_state = case.state
is_approved = (case.state == "已通过")
except Case.DoesNotExist:
pass
# 可以根据需要添加其他类型
except Exception as e:
import logging
logger = logging.getLogger(__name__)
logger.error(f"查询业务记录状态失败: {str(e)}")
return Response({
'message': '查询成功',
'code': 0,
'data': {
'approval_id': approval.id,
'approval_state': approval.state,
'business_state': business_state,
'is_approved': is_approved, # 是否已经审核完全通过
'type': type
}
}, status=status.HTTP_200_OK)
class ChangePasswordView(APIView):
"""修改密码接口"""
def post(self, request, *args, **kwargs):
"""
修改密码
权限规则:
- 超级管理员admin可以修改任何用户的密码不需要原密码
- 非超级管理员:只能修改自己的密码(需要验证原密码)
"""
token = request.META.get('token')
user_id = request.data.get('user_id') # 要修改密码的用户ID可选超级管理员使用
old_password = request.data.get('old_password') # 原密码(非超级管理员必填)
new_password = request.data.get('new_password') # 新密码
# 参数验证
if not new_password:
return Response({'status': 'error', 'message': '新密码不能为空', 'code': 1},
status=status.HTTP_400_BAD_REQUEST)
# 获取当前登录用户
try:
current_user = User.objects.get(token=token, is_deleted=False)
except User.DoesNotExist:
return Response({'status': 'error', 'message': '用户不存在或已被删除', 'code': 1},
status=status.HTTP_401_UNAUTHORIZED)
# 判断是否为超级管理员
is_admin = (current_user.username == 'admin' or current_user.account == 'admin')
# 确定要修改密码的目标用户
if is_admin:
# 超级管理员可以修改任何用户的密码
if user_id:
try:
target_user = User.objects.get(id=user_id, is_deleted=False)
except User.DoesNotExist:
return Response({'status': 'error', 'message': '目标用户不存在或已被删除', 'code': 1},
status=status.HTTP_404_NOT_FOUND)
else:
# 如果没有指定user_id默认修改自己的密码
target_user = current_user
# 超级管理员修改密码不需要原密码
else:
# 非超级管理员只能修改自己的密码
target_user = current_user
# 必须提供原密码
if not old_password:
return Response({'status': 'error', 'message': '原密码不能为空', 'code': 1},
status=status.HTTP_400_BAD_REQUEST)
# 验证原密码
if target_user.password != old_password:
return Response({'status': 'error', 'message': '原密码错误', 'code': 1},
status=status.HTTP_400_BAD_REQUEST)
# 记录操作前的数据(不包含密码)
old_data = {
'id': target_user.id,
'username': target_user.username,
'account': target_user.account
}
# 更新密码
target_user.password = new_password
target_user.save(update_fields=['password'])
# 记录操作后的数据
new_data = {
'id': target_user.id,
'username': target_user.username,
'account': target_user.account
}
# 记录操作日志
action_desc = '修改密码' if target_user.id == current_user.id else f'修改用户{target_user.username}的密码'
log_operation(
request=request,
operation_type='UPDATE',
module='User',
action=action_desc,
target_type='User',
target_id=target_user.id,
target_name=target_user.username,
old_data=old_data,
new_data=new_data,
remark=f'{current_user.username} {action_desc}'
)
return Response({'message': '密码修改成功', 'code': 0}, status=status.HTTP_200_OK)