|
@@ -0,0 +1,423 @@
|
|
|
+"""
|
|
|
+@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)}")
|
|
|
+
|