diff --git a/business/urls.py b/business/urls.py index f106fe5..3c5d461 100644 --- a/business/urls.py +++ b/business/urls.py @@ -1,7 +1,7 @@ from django.urls import path from .models import Schedule -from .views import registration,registrationDetail,Project,Projectquerytype,ProjectDetail,EditProject,BidRegistration,BidDetail,registrationList,caseManagement,caseManagementDetail,CaseAttachmentUpload,CaseAttachmentUpdate,Uploadinvoice,InvoiceDetail,Log,LogDetail,accumulate,preFilingLinkedCases,Application,ApplicationDetail,WarehousingRegistration,WarehousingDetail,PlatformRegistration,PlatformDetail,EditPlatformDetail,DeletePlatformDetail,bulletin,BulletinDetail,EditBulletin,deleteBulletin,Lawyersdocuments,LawyersdocumentsDetail,LwaDetail,CreateSchedule,DeleteSchedule,ScheduleDetail,handleSchedule,AddRermission,DisplayRermission,DeleteRermission,EditRermission,addRole,DeleteRole,EditRole,displayRole,modifypermissions,getRolePermissions,DeleteRegistration,EditRegistration,DeleteProject,EditBid,DeleteBid,EditCase,DeleteCase,EditApplication,DeleteApplication,EditWarehousing,DeleteWarehousing,EditLawyerFlie,EditSchedule,TransferCase,CaseChangeRequestCreate,CaseChangeRequestList,CaseChangeRequestDetail,ProjectDropdownList,CaseDropdownList,ConflictSearch,CreateCaseTag,CaseTagList,CaseTagDetail,EditCaseTag,DeleteCaseTag,CaseTagDropdownList,CaseListByTag,SetCaseTags,PropagandaEit,addSystem,SystemList,eitSystem,deleteSystem +from .views import registration,registrationDetail,Project,Projectquerytype,ProjectDetail,EditProject,BidRegistration,BidDetail,registrationList,caseManagement,caseManagementDetail,CaseAttachmentUpload,CaseAttachmentUpdate,Uploadinvoice,InvoiceDetail,Log,LogDetail,accumulate,preFilingLinkedCases,Application,ApplicationDetail,WarehousingRegistration,WarehousingDetail,PlatformRegistration,PlatformDetail,EditPlatformDetail,DeletePlatformDetail,bulletin,BulletinDetail,EditBulletin,deleteBulletin,Lawyersdocuments,LawyersdocumentsDetail,LwaDetail,CreateSchedule,DeleteSchedule,ScheduleDetail,handleSchedule,AddRermission,DisplayRermission,DeleteRermission,EditRermission,addRole,DeleteRole,EditRole,displayRole,modifypermissions,getRolePermissions,DeleteRegistration,EditRegistration,DeleteProject,EditBid,DeleteBid,EditCase,DeleteCase,EditApplication,DeleteApplication,EditWarehousing,DeleteWarehousing,EditLawyerFlie,EditSchedule,TransferCase,CaseChangeRequestCreate,CaseChangeRequestList,CaseChangeRequestDetail,ProjectDropdownList,CaseDropdownList,ConflictSearch,CreateCaseTag,CaseTagList,CaseTagDetail,EditCaseTag,DeleteCaseTag,CaseTagDropdownList,CaseListByTag,SetCaseTags,PropagandaEit,addSystem,SystemList,eitSystem,deleteSystem,ExportCaseLogExcel urlpatterns = [ path('register',registration.as_view(),name='register'), @@ -85,5 +85,7 @@ urlpatterns = [ path('addSystem',addSystem.as_view(),name='addSystem'), path('SystemList',SystemList.as_view(),name='SystemList'), path('eitSystem',eitSystem.as_view(),name='eitSystem'), - path('deleteSystem',deleteSystem.as_view(),name='deleteSystem') + path('deleteSystem',deleteSystem.as_view(),name='deleteSystem'), + # 案件日志导出Excel + path('export-case-log-excel',ExportCaseLogExcel.as_view(),name='export-case-log-excel'), ] \ No newline at end of file diff --git a/business/views.py b/business/views.py index 747ea03..1dd5dd9 100644 --- a/business/views.py +++ b/business/views.py @@ -5149,3 +5149,154 @@ class deleteSystem(APIView): except System.DoesNotExist: return Response({'status': 'error', 'message': '系统公告不存在', 'code': 1}, status=status.HTTP_404_NOT_FOUND) + +class ExportCaseLogExcel(APIView): + def post(self, request, *args, **kwargs): + """ + 导出案件日志到Excel + 必填参数:case_id(案件ID) + 可选参数:start_time(开始时间)、end_time(结束时间) + 返回:Excel文件下载 + """ + import pandas as pd + from django.http import HttpResponse + from io import BytesIO + + case_id = request.data.get('case_id') # 案件ID(必填) + start_time = request.data.get('start_time') # 开始时间(可选) + end_time = request.data.get('end_time') # 结束时间(可选) + + if not case_id: + return Response({'status': 'error', 'message': '缺少参数case_id(案件ID)', 'code': 1}, status=status.HTTP_400_BAD_REQUEST) + + # 获取案件信息 + try: + case = Case.objects.get(id=case_id, is_deleted=False) + except Case.DoesNotExist: + return Response({'status': 'error', 'message': '案件不存在', 'code': 1}, status=status.HTTP_404_NOT_FOUND) + + # 构建查询条件 + Q_obj = Q(is_deleted=False, case_id=case_id) + + # 时间范围筛选(可选) + if start_time: + Q_obj &= Q(times__gte=start_time) + if end_time: + Q_obj &= Q(times__lte=end_time) + + # 查询日志数据 + logs = Caselog.objects.filter(Q_obj).order_by('-times', '-id') + + if not logs.exists(): + return Response({'status': 'error', 'message': '没有找到案件日志数据', 'code': 1}, status=status.HTTP_404_NOT_FOUND) + + # 解析案件负责人信息 + responsiblefor_info = "" + if case.responsiblefor: + try: + resp_data = json.loads(case.responsiblefor) if isinstance(case.responsiblefor, str) else case.responsiblefor + if isinstance(resp_data, dict): + parts = [] + if resp_data.get('responsible_person'): + parts.append(f"承办人:{resp_data.get('responsible_person')}") + if resp_data.get('main_lawyer'): + parts.append(f"主办律师:{resp_data.get('main_lawyer')}") + if resp_data.get('assist_lawyer'): + parts.append(f"协办律师:{resp_data.get('assist_lawyer')}") + if resp_data.get('trainee_lawyer'): + parts.append(f"实习律师:{resp_data.get('trainee_lawyer')}") + if resp_data.get('secretary'): + parts.append(f"秘书/助理:{resp_data.get('secretary')}") + responsiblefor_info = ";".join(parts) if parts else str(case.responsiblefor) + else: + responsiblefor_info = str(case.responsiblefor) + except (json.JSONDecodeError, TypeError): + responsiblefor_info = str(case.responsiblefor) if case.responsiblefor else "" + + # 获取案件标签 + tags_str = "" + try: + tags = case.tags.filter(is_deleted=False).values_list('name', flat=True) + tags_str = "、".join(tags) if tags else "" + except: + tags_str = "" + + # 构建Excel数据 + excel_data = [] + for log in logs: + # 解析附件信息 + file_info = "" + if log.file: + try: + files = json.loads(log.file) if isinstance(log.file, str) else log.file + if isinstance(files, list): + file_names = [] + for f in files: + if isinstance(f, dict): + file_names.append(f.get('name', f.get('url', '未知文件'))) + elif isinstance(f, str): + file_names.append(f) + file_info = ";".join(file_names) if file_names else "" + else: + file_info = str(files) + except (json.JSONDecodeError, TypeError): + file_info = str(log.file) if log.file else "" + + excel_data.append({ + '日志ID': log.id, + '案件ID': case_id, + '合同编号': case.contract_no or '', + '项目类型': case.project_type or '', + '客户名称': case.client_name or '', + '相对方名称': case.party_name or '', + '项目简述': case.project_description or '', + '负责人信息': responsiblefor_info, + '收费情况': case.charge or '', + '立案时间': case.times or '', + '案件状态': case.state or '', + '案件标签': tags_str, + '已开票金额': case.invoice_status or '0', + '已收款金额': case.paymentcollection or '0', + '日志内容': log.content or '', + '记录时间': log.times or '', + '记录人': log.username or '', + '附件信息': file_info, + }) + + # 创建DataFrame + df = pd.DataFrame(excel_data) + + # 导出Excel + output = BytesIO() + with pd.ExcelWriter(output, engine='openpyxl') as writer: + df.to_excel(writer, index=False, sheet_name='案件日志') + + # 调整列宽 + worksheet = writer.sheets['案件日志'] + for idx, col in enumerate(df.columns): + max_length = max( + df[col].astype(str).map(len).max() if len(df) > 0 else 0, + len(str(col)) + ) + # 限制最大列宽为50 + adjusted_width = min(max_length + 2, 50) + worksheet.column_dimensions[chr(65 + idx) if idx < 26 else 'A' + chr(65 + idx - 26)].width = adjusted_width + + output.seek(0) + + # 生成文件名 + contract_no = case.contract_no or f'案件{case_id}' + filename = f'{contract_no}_案件日志_{datetime.now().strftime("%Y%m%d_%H%M%S")}.xlsx' + + # 返回Excel文件 + response = HttpResponse( + output.read(), + content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' + ) + # 处理中文文件名 + from urllib.parse import quote + response['Content-Disposition'] = f'attachment; filename*=UTF-8\'\'{quote(filename)}' + response['Access-Control-Expose-Headers'] = 'Content-Disposition' + + return response +