瀏覽代碼

新增使用帮助链接helpLinkManage接口

zhuo 3 周之前
父節點
當前提交
f5ac321f9e
共有 2 個文件被更改,包括 425 次插入1 次删除
  1. 423 0
      AdminController/HelpLinkManageController.py
  2. 2 1
      Ansjer/urls.py

+ 423 - 0
AdminController/HelpLinkManageController.py

@@ -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)}")
+

+ 2 - 1
Ansjer/urls.py

@@ -6,7 +6,7 @@ from AdminController import UserManageController, RoleController, MenuController
     ServeManagementController, LogManagementController, DeviceManagementController, VersionManagementController, \
     AiServeController, SurveysManageController, SerialManageController, IcloudManagementController, CampaignController, \
     MessageMangementController, EvaluationActivityController, DataDictManageController, ProductsSchemeManageController, \
-    ProblemEntryManagementController, UIDBurnManageController
+    ProblemEntryManagementController, UIDBurnManageController, HelpLinkManageController
 from AdminController.CloudServiceManage import AgentDeviceController, AgentCustomerController, AgentOrderController
 from Controller import FeedBack, EquipmentOTA, EquipmentInfo, AdminManage, AppInfo, \
     Test, MealManage, DeviceManage, EquipmentStatus, SysManage, DeviceLog, LogAccess, \
@@ -354,6 +354,7 @@ urlpatterns = [
     # 产品问题录入
     re_path('problemEntryManage/(?P<operation>.*)',
             ProblemEntryManagementController.ProblemEntryView.as_view()),
+    re_path('helpLinkManage/(?P<operation>.*)', HelpLinkManageController.HelpLinkManageView.as_view()),
     re_path('uidRurnManage/(?P<operation>.*)', UIDBurnManageController.UIDBurnManageView.as_view()),
     # 后台界面接口 -------------------------------------------------------------------------------------------------------