123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423 |
- """
- @File : HelpLinkManageController.py
- @Time : 2025/7/30 8:52
- @Author : Zhuo
- @Email : zhuojiaxuan@zosi.life
- @Software: PyCharm
- """
- import csv
- import datetime
- import json
- import time
- from io import StringIO
- from django.core.paginator import Paginator, EmptyPage
- from django.db import transaction, IntegrityError
- from django.db.models import Q, Case, When
- from django.http import QueryDict, HttpResponse
- from django.views import View
- from Ansjer.config import LOGGER
- from Model.models import HelpLink
- from Object.Enums.RedisKeyConstant import RedisKeyConstant
- from Object.RedisObject import RedisObject
- from Object.ResponseObject import ResponseObject
- from Object.TokenObject import TokenObject
- class HelpLinkManageView(View):
- def get(self, request, *args, **kwargs):
- request.encoding = 'utf-8'
- operation = kwargs.get('operation')
- return self.validation(request.GET, request, operation)
- def post(self, request, *args, **kwargs):
- request.encoding = 'utf-8'
- operation = kwargs.get('operation')
- return self.validation(request.POST, request, operation)
- def delete(self, request, *args, **kwargs):
- request.encoding = 'utf-8'
- operation = kwargs.get('operation')
- delete = QueryDict(request.body)
- if not delete:
- delete = request.GET
- return self.validation(delete, request, operation)
- def put(self, request, *args, **kwargs):
- request.encoding = 'utf-8'
- operation = kwargs.get('operation')
- put = QueryDict(request.body)
- return self.validation(put, request, operation)
- def validation(self, request_dict, request, operation):
- """请求验证路由"""
- # 初始化响应对象
- language = request_dict.get('language', 'en')
- response = ResponseObject(language, 'pc')
- # Token验证
- try:
- tko = TokenObject(
- request.META.get('HTTP_AUTHORIZATION'),
- returntpye='pc')
- if tko.code != 0:
- return response.json(tko.code)
- response.lang = tko.lang
- user_id = tko.userID
- except Exception as e:
- LOGGER.error(f"Token验证失败: {str(e)}")
- return response.json(444)
- # 操作路由映射
- operation_handlers = {
- 'queryList': self.query_list, # 查询
- 'add': self.add_help_link, # 添加
- 'edit': self.edit_help_link, # 编辑
- 'delete': self.delete_help_link, # 删除
- 'getByDeviceType': self.get_by_device_type, # 根据设备类型获取方案
- 'batchDelete': self.batch_delete, # 批量删除
- 'export': self.export_help_links, # 导出帮助链接
- }
- handler = operation_handlers.get(operation)
- if not handler:
- return response.json(444, 'operation')
- try:
- return handler(user_id, request_dict, response)
- except Exception as e:
- LOGGER.error(f"操作{operation}执行异常:{repr(e)}")
- return response.json(500, "服务器内部错误")
- def query_list(self,user_id, request_dict, response):
- """查询帮助链接列表"""
- # 参数处理与验证
- try:
- page = int(request_dict.get('page', 1))
- page_size = min(int(request_dict.get('pageSize', 10)), 100) # 限制最大分页大小
- except ValueError:
- return response.json(444, "分页参数错误")
- # 构建查询条件
- query = Q()
- # 特殊字段处理
- if device_type := request_dict.get('deviceType'):
- query &= Q(device_type=int(device_type))
- if lang := request_dict.get('lang'):
- query &= Q(lang=lang)
- if title := request_dict.get('title'):
- query &= Q(title__icontains=title)
- if url := request_dict.get('url'):
- query &= Q(url__icontains=url)
- # 使用分页器
- queryset = HelpLink.objects.filter(query).order_by('-id')
- paginator = Paginator(queryset, page_size)
- try:
- page_obj = paginator.page(page)
- except EmptyPage:
- return response.json(444, "页码超出范围")
- # 序列化数据
- data = [self._help_link_to_dict(help_link) for help_link in page_obj]
- return response.json(0, {
- 'list': data,
- 'total': paginator.count,
- 'currentPage': page_obj.number,
- 'totalPages': paginator.num_pages
- })
- def add_help_link(self, user_id, request_dict, response):
- """新增帮助链接"""
- try:
- # 构造帮助链接数据
- help_link_data = {
- 'device_type': int(request_dict.get('deviceType', 0)),
- 'lang': request_dict.get('lang'),
- 'url': request_dict.get('url', ''),
- 'title': request_dict.get('title', ''),
- 'description': request_dict.get('description', ''),
- 'is_active' : request_dict.get('isActive', 1),
- 'created_time': int(time.time()),
- 'updated_time': int(time.time()),
- }
- help_link = HelpLink.objects.create(**help_link_data)
- return response.json(0, {
- 'id': help_link.id
- })
- except IntegrityError as e:
- LOGGER.error(f"用户 {user_id} 创建帮助链接时发生唯一性冲突: {str(e)}")
- return response.json(173, "数据已存在")
- except ValueError as e:
- return response.json(444, "参数类型错误")
- except Exception as e:
- LOGGER.exception(f"用户 {user_id} 添加帮助链接异常:{repr(e)}")
- return response.json(500, "添加失败")
- def edit_help_link(self, user_id, request_dict, response):
- """编辑帮助链接"""
- help_link_id = request_dict.get('helpLinkId',None)
- device_type = request_dict.get('deviceType', None)
- lang = request_dict.get('lang', None)
- url = request_dict.get('url', None)
- title = request_dict.get('title', None)
- description = request_dict.get('description', None)
- is_active = request_dict.get('isActive', None)
- if not all([help_link_id, device_type, lang, url]):
- return response.json(444)
- try:
- now_time = int(time.time())
- help_link_qs = HelpLink.objects.filter(id=help_link_id).values('device_type', 'lang')
- if not help_link_qs.exists():
- return response.json(173)
- # 保存修改前的信息,用于清除缓存
- old_device_type = help_link_qs[0]['device_type']
- old_lang = help_link_qs[0]['lang']
- # 更新数据
- update_data = {
- 'device_type': int(device_type),
- 'lang': lang,
- 'url': url,
- 'title': title,
- 'description': description,
- 'updated_time': now_time
- }
- # 只有当is_active参数存在时才更新该字段
- if is_active is not None:
- update_data['is_active'] = bool(is_active)
- HelpLink.objects.filter(id=help_link_id).update(**update_data)
- # 清除相关缓存
- self._clear_help_link_cache(old_device_type, old_lang)
- # 如果设备类型或语言发生变化,也需要清除新缓存
- if int(device_type) != old_device_type or lang != old_lang:
- self._clear_help_link_cache(int(device_type), lang)
- return response.json(0)
- except Exception as e:
- LOGGER.exception(f"用户 {user_id} 编辑帮助链接异常: {repr(e)}")
- return response.json(500, f'error_line:{e.__traceback__.tb_lineno}, error_msg:{repr(e)}')
- def delete_help_link(self, user_id, request_dict, response):
- """删除帮助链接"""
- # 参数校验
- if 'id' not in request_dict:
- return response.json(444, "缺少链接ID")
- try:
- # 先获取帮助链接信息,用于清除缓存
- help_link = HelpLink.objects.get(id=request_dict['id'])
- device_type = help_link.device_type
- lang = help_link.lang
- # 直接删除记录
- deleted_count, _ = HelpLink.objects.filter(id=request_dict['id']).delete()
- if deleted_count == 0:
- return response.json(173, "帮助链接不存在")
- # 清除相关缓存
- self._clear_help_link_cache(device_type, lang)
- # 记录操作日志
- LOGGER.info(f"用户 {user_id} 删除了帮助链接,ID: {request_dict['id']}")
- return response.json(0)
- except HelpLink.DoesNotExist:
- return response.json(173, "帮助链接不存在")
- except ValueError:
- return response.json(500, "链接ID格式错误")
- def get_by_device_type(self,user_id, request_dict, response):
- """根据设备类型和语言获取帮助链接"""
- try:
- device_type = int(request_dict.get('deviceType',0))
- lang = request_dict.get('lang')
- except(ValueError, TypeError):
- return response.json(444, '参数错误')
- try:
- # 构建缓存键
- cache_key = RedisKeyConstant.HELP_LINK_TYPE.value + f'{device_type}:{lang}'
- redis = RedisObject("help_link")
- # 先尝试从缓存获取
- cached_data = redis.get_data(cache_key)
- if cached_data:
- cached_data = json.loads(cached_data)
- return response.json(0, cached_data)
- # 优化数据库查询 - 单次查询获取结果
- help_link = HelpLink.objects.filter(
- Q(device_type=device_type) | Q(device_type=-1),
- lang=lang,
- is_active=True
- ).order_by(
- # 优先匹配指定设备类型,其次匹配通用类型(-1)
- Case(
- When(device_type=device_type, then=0),
- default=1,
- )
- ).first()
- if not help_link:
- return response.json(173)
- # 构建返回数据
- data = {
- 'url': help_link.url,
- 'title': help_link.title,
- 'description': help_link.description
- }
- # 设置缓存,过期时间30天
- try:
- redis.set_data(cache_key, json.dumps(data), RedisKeyConstant.EXPIRE_TIME_30_DAYS.value)
- except Exception as e:
- LOGGER.error(f"设置Redis缓存出错: {repr(e)}")
- # 缓存失败不影响主流程
- return response.json(0, data)
- except Exception as e:
- LOGGER.error(f"查询帮助链接出错: {repr(e)}")
- return response.json(500)
- def batch_delete(self, user_id, request_dict, response):
- """批量删除帮助链接"""
- try:
- ids = request_dict.get('ids')
- if not ids:
- return response.json(444, "参数错误")
- # 解析ID列表
- if isinstance(ids, str):
- ids = json.loads(ids)
- if not isinstance(ids, list) or not ids:
- return response.json(444, "ID列表格式错误")
- # 查询要删除的帮助链接信息,并收集缓存清理信息
- help_links = HelpLink.objects.filter(id__in=ids)
- if not help_links.exists():
- return response.json(173, "没有找到可删除的帮助链接")
- deleted_links_info = list(help_links.values('device_type', 'lang'))
- # 批量删除
- deleted_count, _ = help_links.delete()
- # 清除相关缓存
- for link_info in deleted_links_info:
- self._clear_help_link_cache(link_info['device_type'], link_info['lang'])
- # 记录操作日志
- LOGGER.info(f"用户 {user_id} 批量删除了 {deleted_count} 条帮助链接,ID列表: {ids}")
- return response.json(0, f"成功删除{deleted_count}条记录")
- except json.JSONDecodeError:
- return response.json(444, "ID列表格式错误")
- except Exception as e:
- LOGGER.error(f"用户 {user_id} 批量删除帮助链接失败: {str(e)}")
- return response.json(500, "批量删除失败")
- def export_help_links(self, user_id, request_dict, response):
- """导出帮助链接为CSV格式"""
- try:
- # 获取所有帮助链接
- queryset = HelpLink.objects.filter(is_active=True).order_by('-id')
- # 创建CSV内容
- output = StringIO()
- writer = csv.writer(output)
- # 写入表头
- writer.writerow(['ID', '设备类型', '语言', '链接标题', 'URL', '描述', '是否启用', '创建时间', '更新时间'])
- # 写入每一行数据
- for link in queryset:
- # 确保时间戳是整数
- created_ts = int(link.created_time) if link.created_time else None
- updated_ts = int(link.updated_time) if link.updated_time else None
- created_time = datetime.datetime.fromtimestamp(created_ts).strftime(
- '%Y-%m-%d %H:%M:%S') if created_ts is not None else ''
- updated_time = datetime.datetime.fromtimestamp(updated_ts).strftime(
- '%Y-%m-%d %H:%M:%S') if updated_ts is not None else ''
- writer.writerow([
- link.id,
- link.device_type,
- link.lang,
- link.title,
- link.url,
- link.description,
- '是' if link.is_active else '否',
- created_time,
- updated_time
- ])
- LOGGER.info(f"用户 {user_id} 导出了 {queryset.count()} 条帮助链接")
- # 准备响应
- csv_content = output.getvalue()
- output.close()
- # 创建HTTP响应
- response_http = HttpResponse(
- csv_content,
- content_type='text/csv',
- headers={
- 'Content-Disposition': 'attachment; filename="help_links_export.csv"'
- }
- )
- return response_http
- except Exception as e:
- LOGGER.error(f"用户 {user_id} 导出帮助链接失败: {str(e)}")
- return ResponseObject('cn', 'pc').json(500, "导出失败")
- def _help_link_to_dict(self, help_link):
- """帮助链接对象序列化"""
- return {
- 'id': help_link.id,
- 'deviceType': help_link.device_type,
- 'lang': help_link.lang,
- 'url': help_link.url,
- 'title': help_link.title,
- 'description': help_link.description,
- 'isActive': help_link.is_active,
- 'createdTime': help_link.created_time,
- 'updatedTime': help_link.updated_time,
- }
- def _clear_help_link_cache(self, device_type, lang):
- """
- 清除帮助链接缓存
- """
- try:
- cache_key = RedisKeyConstant.HELP_LINK_TYPE.value + f'{device_type}:{lang}'
- redis = RedisObject("help_link")
- redis.del_data(cache_key)
- # 同时清除通用类型(-1)的缓存
- cache_key_general = f"help_link:-1:{lang}"
- redis.del_data(cache_key_general)
- except Exception as e:
- LOGGER.error(f"清除帮助链接缓存失败: {repr(e)}")
|