diff --git a/app_admin/urls.py b/app_admin/urls.py index aff91dd007017d50695accc05a89824012371b84..a208140cc988003123aef33baafb13aad25289a3 100644 --- a/app_admin/urls.py +++ b/app_admin/urls.py @@ -16,7 +16,9 @@ urlpatterns = [ path('doc_manage/',views.admin_doc,name='doc_manage'), # 文档管理 # 文档历史记录管理及接口 + path('doc_view_log//', views.admin_doc_view_log, name='doc_view_log'), # 文档访问记录页面 path('doc_history_manage//', views.admin_doc_history, name='doc_history_manage'), # 文档历史记录管理 + path('api/doc_view_log//', views.AdminDocViewLog.as_view(), name="api_doc_view_log"), # 文档访问记录接口 path('api/doc_history//', views.AdminDocHistory.as_view(), name="api_doc_history"), # 文档历史记录接口 path('api/doc_history_detail/', views.AdminDocHistoryDetail.as_view(), name="api_doc_history_detail"), # 文档历史记录详情接口 path('doctemp_manage/',views.admin_doctemp,name='doctemp_manage'), # 文档模板管理 diff --git a/app_admin/views.py b/app_admin/views.py index eefa44c4e206b62c5722728f2ee2a61732ff76a8..73cdd69c931be1b99ba46b2d52624dd34dbe32af 100644 --- a/app_admin/views.py +++ b/app_admin/views.py @@ -689,6 +689,43 @@ def admin_doc(request): } return JsonResponse(resp_data) +# 后台管理 - 文档访问记录 +@superuser_only +def admin_doc_view_log(request, id): + doc = Doc.objects.get(id=id) + return render(request, 'app_admin/admin_doc_view_log.html', locals()) + + +# 文档访问记录接口 - 通过文档id +class AdminDocViewLog(APIView): + authentication_classes = [SessionAuthentication, AppMustAuth] + permission_classes = [SuperUserPermission] + + def get_object(self, id): + try: + return Doc.objects.get(id=id) + except ObjectDoesNotExist: + raise Http404 + + # 获取文档的历史记录 + def get(self, request, id): + doc = self.get_object(id=id) + page_num = request.query_params.get('page', 1) + limit = request.query_params.get('limit', 10) + + log_data = ViewRecord.objects.filter(doc=doc).order_by('-view_date', '-view_count') + page = PageNumberPagination() # 实例化一个分页器 + page.page_size = limit + page_logs = page.paginate_queryset(log_data, request, view=self) # 进行分页查询 + serializer = ViewRecordSerializer(page_logs, many=True) # 对分页后的结果进行序列化处理 + resp = { + 'code': 0, + 'data': serializer.data, + 'count': log_data.count() + } + return Response(resp) + + # 后台管理 - 文档管理 - 文档历史管理 @superuser_only def admin_doc_history(request,id): diff --git a/app_api/serializers_app.py b/app_api/serializers_app.py index ec9822fb66dcc7fe5e989fe5d9399d3155abfa16..db87ac30fc883da5648d03af2c5bf90e2d7999d6 100644 --- a/app_api/serializers_app.py +++ b/app_api/serializers_app.py @@ -66,6 +66,13 @@ class DocSerializer(ModelSerializer): return pro_name +# 文档访问记录列化器 +class ViewRecordSerializer(ModelSerializer): + class Meta: + model = ViewRecord + fields = '__all__' + + # 文档历史序列化器 class DocHistorySerializer(ModelSerializer): username = serializers.SerializerMethodField(label="用户名") diff --git a/app_doc/migrations/0039_auto_20210830_0921.py b/app_doc/migrations/0039_auto_20210830_0921.py new file mode 100644 index 0000000000000000000000000000000000000000..85a487c556da2a9c99d752c4f99635c558d68ac0 --- /dev/null +++ b/app_doc/migrations/0039_auto_20210830_0921.py @@ -0,0 +1,38 @@ +# Generated by Django 2.2.24 on 2021-08-30 09:21 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('app_doc', '0038_project_is_top'), + ] + + operations = [ + migrations.AddField( + model_name='doc', + name='last_view_time', + field=models.DateTimeField(blank=True, null=True, verbose_name='最后浏览时间'), + ), + migrations.AddField( + model_name='doc', + name='view_count', + field=models.IntegerField(blank=True, default=0, null=True, verbose_name='浏览次数'), + ), + migrations.CreateModel( + name='ViewRecord', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('ip', models.GenericIPAddressField(blank=True, null=True, verbose_name='IP地址')), + ('view_date', models.DateField(blank=True, null=True, verbose_name='查看日期')), + ('view_count', models.IntegerField(blank=True, default=0, null=True, verbose_name='浏览次数')), + ('doc', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='app_doc.Doc', verbose_name='文档id')), + ], + options={ + 'verbose_name': '访问记录', + 'verbose_name_plural': '访问记录', + }, + ), + ] diff --git a/app_doc/models.py b/app_doc/models.py index a4b7ce1875f29cb517c43e915bd8a2ade3185ac4..382f4211c8c471939656d113062bff4a75ab24cc 100644 --- a/app_doc/models.py +++ b/app_doc/models.py @@ -80,6 +80,8 @@ class Doc(models.Model): editor_mode = models.IntegerField(default=1,verbose_name='编辑器模式') open_children = models.BooleanField(default=False,verbose_name="展开下级目录") show_children = models.BooleanField(verbose_name="显示下级文档",default=False) + view_count = models.IntegerField(verbose_name='浏览次数', null=True, blank=True, default=0) + last_view_time = models.DateTimeField(verbose_name='最后浏览时间', null=True, blank=True) def __str__(self): return self.name @@ -267,4 +269,19 @@ class MyCollect(models.Model): class Meta: verbose_name = '我的收藏' - verbose_name_plural = verbose_name \ No newline at end of file + verbose_name_plural = verbose_name + + +class ViewRecord(models.Model): + # 记录文档访问ip及次数 + doc = models.ForeignKey(Doc, verbose_name='文档id', on_delete=models.CASCADE) + ip = models.GenericIPAddressField(verbose_name='IP地址', null=True, blank=True) + view_date = models.DateField(verbose_name='查看日期', null=True, blank=True) + view_count = models.IntegerField(verbose_name='浏览次数', null=True, blank=True, default=0) + + def __str__(self): + return '文档名:' + self.doc.name + ', 日期:' + str(self.view_date) + ', IP:' + str(self.ip) + ', 访问次数:' + str(self.view_count) + + class Meta: + verbose_name = '访问记录' + verbose_name_plural = verbose_name diff --git a/app_doc/urls.py b/app_doc/urls.py index 47385fa5d49b5f8c53717a73a22ae9a79fb415f7..5a6624a1033b38a4db267d288157f81afc0bd01c 100644 --- a/app_doc/urls.py +++ b/app_doc/urls.py @@ -32,6 +32,7 @@ urlpatterns = [ path('manage_doc/',views.manage_doc,name="manage_doc"), # 管理文档 path('diff_doc/-/',views.diff_doc,name='diff_doc'), # 对比文档历史版本 path('manage_doc_history//',views.manage_doc_history,name='manage_doc_history'), # 管理文档历史版本 + path('doc_view_log//',views.doc_view_log,name='doc_view_log'), # 管理文档访问记录 path('move_doc/', views.move_doc, name='move_doc'), # 移动文档 path('doc_recycle/', views.doc_recycle,name='doc_recycle'), # 文档回收站 path('fast_pub_doc/',views.fast_publish_doc,name='fast_pub_doc'), # 一键发布文档 diff --git a/app_doc/views.py b/app_doc/views.py index 60981e699c0d12c22dfc3c18eb2025a5da79259b..5513cffea4ea142b1b12feb305a4c574174bfde9 100644 --- a/app_doc/views.py +++ b/app_doc/views.py @@ -15,7 +15,7 @@ from rest_framework.views import APIView # 视图 from rest_framework.response import Response # 响应 from rest_framework.pagination import PageNumberPagination # 分页 from rest_framework.authentication import SessionAuthentication # 认证 -from django.db.models import Q +from django.db.models import Q, F from django.db import transaction from django.utils.html import strip_tags from django.utils.translation import gettext_lazy as _ @@ -1015,6 +1015,31 @@ def doc(request,pro_id,doc_id): is_share = False # 获取文集下一级文档 # project_docs = Doc.objects.filter(top_doc=doc.top_doc, parent_doc=0, status=1).order_by('sort') + with transaction.atomic(): + # 记录浏览总次数 + view_count = doc.view_count + doc.view_count = view_count + 1 + last_view_time = doc.last_view_time + doc.last_view_time = datetime.datetime.now() + doc.save() + + # 获取当前ip + if 'HTTP_X_FORWARDED_FOR' in request.META: + client_ip = request.META['HTTP_X_FORWARDED_FOR'] + client_ip = client_ip.split(",")[0] # 真实ip + else: + client_ip = request.META['REMOTE_ADDR'] # 代理ip + this_date = datetime.datetime.now().date() + views = ViewRecord.objects.filter(doc_id=doc.id, ip=client_ip, view_date=this_date) + if views.count() > 0: + ViewRecord.objects.filter(doc_id=doc.id, ip=client_ip, view_date=this_date).update(view_count=F('view_count') + 1) + else: + ViewRecord.objects.create(**{ + 'doc_id': doc.id, + 'ip': client_ip, + 'view_date': this_date, + 'view_count': 1, + }) return render(request,'app_doc/doc.html',locals()) else: return HttpResponse(_('参数错误')) @@ -1553,6 +1578,28 @@ def manage_doc_history(request,doc_id): return JsonResponse({'status':False,'data':_('出现异常')}) +# 管理文档 访问记录 +@login_required() +@require_http_methods(['GET', "POST"]) +def doc_view_log(request, doc_id): + if request.method == 'GET': + try: + doc = Doc.objects.get(id=doc_id, create_user=request.user) + logs = ViewRecord.objects.filter(doc_id=doc_id).order_by('-view_date', '-view_count') + paginator = Paginator(logs, 15) + page = request.GET.get('page', 1) + try: + view_logs = paginator.page(page) + except PageNotAnInteger: + view_logs = paginator.page(1) + except EmptyPage: + view_logs = paginator.page(paginator.num_pages) + return render(request, 'app_doc/manage/manage_doc_view_log.html', locals()) + except Exception as e: + logger.exception(_("管理文档访问记录页面访问出错")) + return render(request, '404.html') + + # 文档回收站 @login_required() @require_http_methods(['GET','POST']) diff --git a/config/config.ini b/config/config.ini index f66296a5b8cbf04accc851217bec9275fb30e0e6..04d30186d5491aca33af67ff1c111a4864851e99 100644 --- a/config/config.ini +++ b/config/config.ini @@ -1,6 +1,6 @@ [site] # True表示开启站点调试模式,False表示关闭站点调试模式 -debug = False +debug = True [database] # engine,指定数据库类型,接受sqlite、mysql、oracle、postgresql @@ -22,6 +22,6 @@ engine = sqlite [selenium] # 在Windows环境下测试或使用,请配置driver = Chrome -# driver = Chrome +driver = Chrome # 如果系统无法正确安装或识别chromedriver,请指定chromedriver在计算机上的绝对路径 # driver_path = driver_path \ No newline at end of file diff --git a/template/app_admin/admin_doc.html b/template/app_admin/admin_doc.html index 8ac536988719b089d16557fe0007ca343ee5dbc5..c40b1793404980bb2ae0ceb612e58e02ec3b3a78 100644 --- a/template/app_admin/admin_doc.html +++ b/template/app_admin/admin_doc.html @@ -73,9 +73,9 @@ {{ d.name }} {{# } }} {{#if (d.editor_mode in [1,2,3]) { }} - + {{# }else if(d.editor_mode == 4){ }} - + {{# } }} {% endverbatim %} @@ -128,7 +128,8 @@ +{% endblock %} + +{% block custom_script %} + +{% endblock %} \ No newline at end of file diff --git a/template/app_doc/doc.html b/template/app_doc/doc.html index ffa8b33253e4f26ac09e33c507d2394a2f43ec12..d29bfd5cca4d47eb27fdb5705e4eda1f0e512405 100644 --- a/template/app_doc/doc.html +++ b/template/app_doc/doc.html @@ -90,6 +90,8 @@ {% trans "分享文档" %} + 浏览次数:{{ view_count }} (上一次浏览时间:{{ last_view_time }}) + {% if request.user.is_authenticated %} {% if is_collect_doc %} diff --git a/template/app_doc/head_base.html b/template/app_doc/head_base.html index aca66788b03da7c72cb2b20388f28b84e17bf374..00c8fbd7ae0e41f438c017997f35758423beacdd 100644 --- a/template/app_doc/head_base.html +++ b/template/app_doc/head_base.html @@ -35,20 +35,20 @@
- + {% trans "新建文档" %}
- + {% trans "新建表格" %}
{% if request.user.is_authenticated %}
- + {% trans "新建文集" %}
diff --git a/template/app_doc/manage/manage_doc.html b/template/app_doc/manage/manage_doc.html index e36606b74a8412a7153508bf9ed2e574a96dd92e..28d42eabfdab72abd942206941041c2fcc71bf5f 100644 --- a/template/app_doc/manage/manage_doc.html +++ b/template/app_doc/manage/manage_doc.html @@ -83,9 +83,9 @@ {{ d.name }} {{# } }} {{#if (d.editor_mode in [1,2,3]) { }} - + {{# }else if(d.editor_mode == 4){ }} - + {{# } }} {% endverbatim %} @@ -137,7 +137,8 @@