瀏覽代碼

feat: 添加点击统计

SongZihuan 2 年之前
父節點
當前提交
297449543a
共有 12 個文件被更改,包括 192 次插入120 次删除
  1. 1 1
      app/cache.py
  2. 3 0
      app/docx.py
  3. 6 2
      app/index.py
  4. 2 0
      configure/__init__.py
  5. 5 0
      object/archive.py
  6. 5 0
      object/blog.py
  7. 6 0
      sql/__init__.py
  8. 3 2
      sql/cache.py
  9. 41 0
      sql/statistics.py
  10. 30 31
      templates/archive/archive.html
  11. 67 67
      templates/docx/article.html
  12. 23 17
      templates/index/index.html

+ 1 - 1
app/cache.py

@@ -3,7 +3,7 @@ from configure import conf
 
 cache = Cache(config={
         'CACHE_TYPE': 'RedisCache',
-        'CACHE_KEY_PREFIX': 'flask_cache:',
+        'CACHE_KEY_PREFIX': f'{conf["FLASK_CACHE_PREFIX"]}:',
         'CACHE_REDIS_URL': f'redis://{conf["CACHE_REDIS_NAME"]}:{conf["CACHE_REDIS_PASSWD"]}@'
                            f'{conf["CACHE_REDIS_HOST"]}:{conf["CACHE_REDIS_PORT"]}/{conf["CACHE_REDIS_DATABASE"]}'
     })

+ 3 - 0
app/docx.py

@@ -7,6 +7,7 @@ from typing import Optional
 
 import app
 from sql.base import DBBit
+from sql.statistics import add_blog_click, add_archive_click
 from object.blog import BlogArticle
 from object.comment import Comment
 from object.archive import Archive
@@ -136,6 +137,7 @@ def archive_page():
     blog_list = BlogArticle.get_blog_list(archive_id=archive, limit=20, offset=(page - 1) * 20)
     max_page = app.HBlogFlask.get_max_page(BlogArticle.get_blog_count(archive_id=archive), 20)
     page_list = app.HBlogFlask.get_page("docx.archive_page", page, max_page)
+    add_archive_click(archive)
     app.HBlogFlask.print_load_page_log(f"archive-docx list (archive-id: {archive} page: {page})")
     return render_template("docx/docx.html",
                            page=page,
@@ -159,6 +161,7 @@ def __load_article_page(blog_id: int, form: WriteCommentForm,
         view = UpdateBlogForm(article)
     if archive is None:
         archive = UpdateBlogArchiveForm(article)
+    add_blog_click(article.id)
     return render_template("docx/article.html",
                            article=article,
                            cache_str=f":{article.id}",

+ 6 - 2
app/index.py

@@ -5,7 +5,7 @@ from configure import conf
 import app
 from object.blog import BlogArticle
 from object.msg import Message
-
+from sql.statistics import get_hello_click, add_hello_click, get_home_click, add_home_click
 
 index = Blueprint("base", __name__)
 
@@ -14,6 +14,7 @@ index = Blueprint("base", __name__)
 @app.cache.cached(timeout=conf["VIEW_CACHE_EXPIRE"])
 def hello_page():
     app.HBlogFlask.print_load_page_log(f"hello")
+    add_hello_click()
     return render_template("index/hello.html")
 
 
@@ -22,10 +23,13 @@ def index_page():
     blog_list = BlogArticle.get_blog_list(limit=5, offset=0, not_top=True)
     msg_list = Message.get_message_list(limit=6, offset=0, show_secret=False)
     app.HBlogFlask.print_load_page_log(f"index")
+    add_home_click()
     return render_template("index/index.html",
                            blog_list=blog_list,
                            msg_list=msg_list,
-                           show_email=current_user.check_role("ReadUserInfo"))
+                           show_email=current_user.check_role("ReadUserInfo"),
+                           hello_clicks=get_hello_click(),
+                           home_clicks=get_home_click())
 
 
 @index.context_processor

+ 2 - 0
configure/__init__.py

@@ -28,7 +28,9 @@ conf = {
     "CACHE_EXPIRE": 604800,  # 默认七天过期
     "VIEW_CACHE_EXPIRE": 60,  # 视图函数
     "LIST_CACHE_EXPIRE": 5,  # 列表 排行
+    "REDIS_EXPIRE": "statistics",
     "CACHE_PREFIX": "hblog_cache",
+    "FLASK_CACHE_PREFIX": "flask_cache",
     "MAIL_SERVER": "",
     "MAIL_PORT": "",
     "MAIL_USE_TLS": False,

+ 5 - 0
object/archive.py

@@ -8,6 +8,7 @@ from sql.archive import (read_archive,
                          delete_archive,
                          add_blog_to_archive,
                          sub_blog_from_archive)
+from sql.statistics import get_archive_click
 
 
 class _Archive:
@@ -56,6 +57,10 @@ class Archive(_Archive):
     def count(self):
         return sql.blog.get_archive_blog_count(self.id)
 
+    @property
+    def clicks(self):
+        return get_archive_click(self.id)
+
     def is_delete(self):
         return len(self.name) != 0
 

+ 5 - 0
object/blog.py

@@ -12,6 +12,7 @@ from sql.blog import (get_blog_list,
                       delete_blog,
                       set_blog_top,
                       get_user_blog_count)
+from sql.statistics import get_blog_click
 from sql.archive import add_blog_to_archive, sub_blog_from_archive
 from sql.user import get_user_email
 from sql.base import DBBit
@@ -87,6 +88,10 @@ class BlogArticle(_BlogArticle):
     def create_time(self):
         return self.info.create_time
 
+    @property
+    def clicks(self):
+        return get_blog_click(self.id)
+
     @property
     def top(self):
         return self.info.top == DBBit.BIT_1

+ 6 - 0
sql/__init__.py

@@ -14,3 +14,9 @@ cache = redis.RedisDB(host=conf["CACHE_REDIS_HOST"],
                       username=conf["CACHE_REDIS_NAME"],
                       passwd=conf["CACHE_REDIS_PASSWD"],
                       db=conf["CACHE_REDIS_DATABASE"])
+
+redis = redis.RedisDB(host=conf["REDIS_HOST"],
+                      port=conf["REDIS_PORT"],
+                      username=conf["REDIS_NAME"],
+                      passwd=conf["REDIS_PASSWD"],
+                      db=conf["REDIS_DATABASE"])

+ 3 - 2
sql/cache.py

@@ -409,5 +409,6 @@ def restart_clear_cache():
     包括Hblog-Cache和Flask-Cache
     """
 
-    # 删除inject_base, 重新载入conf作为缓存
-    cache.delete(f"flask_cache:inject_base")
+    # 删除全部Flask缓存
+    for i in cache.keys("flask_cache:*"):
+        cache.delete(i)

+ 41 - 0
sql/statistics.py

@@ -0,0 +1,41 @@
+from sql import redis
+from configure import conf
+
+
+CACHE_PREFIX = conf["REDIS_EXPIRE"]
+
+
+def add_hello_click():
+    redis.incr(f"{CACHE_PREFIX}:home", amount=1)
+
+
+def get_hello_click():
+    res = redis.get(f"{CACHE_PREFIX}:home")
+    return res if res else 0
+
+
+def add_home_click():
+    redis.incr(f"{CACHE_PREFIX}:index", amount=1)
+
+
+def get_home_click():
+    res = redis.get(f"{CACHE_PREFIX}:index")
+    return res if res else 0
+
+
+def add_blog_click(blog_id: int):
+    redis.incr(f"{CACHE_PREFIX}:blog:{blog_id}", amount=1)
+
+
+def get_blog_click(blog_id: int):
+    res = redis.get(f"{CACHE_PREFIX}:blog:{blog_id}")
+    return res if res else 0
+
+
+def add_archive_click(archive_id: int):
+    redis.incr(f"{CACHE_PREFIX}:archive:{archive_id}", amount=1)
+
+
+def get_archive_click(archive_id: int):
+    res = redis.get(f"{CACHE_PREFIX}:archive:{archive_id}")
+    return res if res else 0

+ 30 - 31
templates/archive/archive.html

@@ -47,39 +47,38 @@
         </div>
     {% endif %}
 
-    {% cache conf["LIST_CACHE_EXPIRE"], ":archive", ":list" %}
-        <section id="base" class="d-flex mt-3 container justify-content-center flex-wrap">
-            {% for archive in archive_list %}
-                <div class="archive mb-3 mx-2">
-                    <h3> {{ archive.name }} </h3>
-                    <hr>
-                    <p class="archive_describe"> {{ archive.describe }} </p>
-                    <p> 篇数: {{ archive.count }} </p>
-                    <a class="btn btn-primary" href="{{ url_for("docx.archive_page", archive=archive.id, page=1) }}"> 进入 </a>
-                    {% if show_delete %}
-                        <div id="DeleteModal{{archive.id}}" class="modal fade" role="dialog" aria-hidden="true">
-                            <div class="modal-dialog">
-                                <div class="modal-content text-start">
-                                    <div class="modal-header">
-                                        <h4 class="modal-title"> 确认删除归档? </h4>
-                                    </div>
-                                    <div class="modal-body">
-                                        <p> 是否确认删除归档 {{ archive.name }}? </p>
-                                    </div>
-                                    <div class="modal-footer">
-                                        <a class="btn btn-danger"
-                                           href="{{ url_for("archive.delete_archive_page", archive=archive.id) }}"> 删除 </a>
-                                        <button type="button" class="btn btn-outline-dark" data-bs-dismiss="modal"> 取消 </button>
-                                    </div>
+    <section id="base" class="d-flex mt-3 container justify-content-center flex-wrap">
+        {% for archive in archive_list %}
+            <div class="archive mb-3 mx-2">
+                <h3> {{ archive.name }} </h3>
+                <hr>
+                <p class="archive_describe"> {{ archive.describe }} </p>
+                <p> 篇数: {{ archive.count }} </p>
+                <p> 点击量: {{ archive.clicks }} </p>
+                <a class="btn btn-primary" href="{{ url_for("docx.archive_page", archive=archive.id, page=1) }}"> 进入 </a>
+                {% if show_delete %}
+                    <div id="DeleteModal{{archive.id}}" class="modal fade" role="dialog" aria-hidden="true">
+                        <div class="modal-dialog">
+                            <div class="modal-content text-start">
+                                <div class="modal-header">
+                                    <h4 class="modal-title"> 确认删除归档? </h4>
+                                </div>
+                                <div class="modal-body">
+                                    <p> 是否确认删除归档 {{ archive.name }}? </p>
+                                </div>
+                                <div class="modal-footer">
+                                    <a class="btn btn-danger"
+                                       href="{{ url_for("archive.delete_archive_page", archive=archive.id) }}"> 删除 </a>
+                                    <button type="button" class="btn btn-outline-dark" data-bs-dismiss="modal"> 取消 </button>
                                 </div>
                             </div>
                         </div>
+                    </div>
 
-                        <button type="button" class="btn btn-danger"
-                                data-bs-toggle="modal" data-bs-target="#DeleteModal{{archive.id}}"> 删除归档 </button>
-                    {% endif %}
-                </div>
-            {% endfor %}
-        </section>
-    {% endcache %}
+                    <button type="button" class="btn btn-danger"
+                            data-bs-toggle="modal" data-bs-target="#DeleteModal{{archive.id}}"> 删除归档 </button>
+                {% endif %}
+            </div>
+        {% endfor %}
+    </section>
 {% endblock %}

+ 67 - 67
templates/docx/article.html

@@ -13,85 +13,85 @@
     <section id="base" class="container mt-3">
         {% if current_user.check_role("ReadBlog") %}
             {# 检查是否具有读取权限 #}
-            {% cache conf["LIST_CACHE_EXPIRE"], ":blog", ":docx", cache_str %}
-                <div class="row">
-                    <article class="col-12">
-                        <h1> {{ article.title }} <small> {{ article.subtitle }} <small> {{ article.update_time}} / {{ article.create_time}} </small> </small> </h1>
-                        {% for archive in article.archive %}
-                            <span class="badge bg-info"> {{ archive.name }} </span>
+            <div class="row">
+                <article class="col-12">
+                    <h1> {{ article.title }} <small> {{ article.subtitle }} <small> {{ article.update_time}} / {{ article.create_time}} </small> </small> </h1>
+                    <span class="me-1 badge bg-secondary"> 点击量: {{ article.clicks }} </span>
+
+                    {% for archive in article.archive %}
+                        <span class="mx-1 badge bg-info"> {{ archive.name }} </span>
+                    {% endfor %}
+                    <a class="mx-1 badge bg-success" href="{{ url_for('docx.article_down_page', blog=article.id) }}"> 下载 </a>
+                    <hr>
+
+                    <form method="post" action="{{ url_for('docx.update_docx_page') }}" class="was-validated">
+                        {% if can_update %}
+                            {{ view.hidden_tag() }}
+                            {{ view.blog_id() }}
+                        {% endif %}
+                        <div id="markdown-view">
+                            {{ view.content(class="form-control mb-2", style="display:none;") }}
+                        </div>
+                        {% for error in view.content.errors %}
+                            <div class="invalid-feedback"> {{ error }} </div>
                         {% endfor %}
-                        <a href="{{ url_for('docx.article_down_page', blog=article.id) }}"> 下载 </a>
-                        <hr>
 
-                        <form method="post" action="{{ url_for('docx.update_docx_page') }}" class="was-validated">
-                            {% if can_update %}
-                                {{ view.hidden_tag() }}
-                                {{ view.blog_id() }}
-                            {% endif %}
-                            <div id="markdown-view">
-                                {{ view.content(class="form-control mb-2", style="display:none;") }}
+                        {% if can_update %}
+                            <div id="UpdateModal" class="modal fade" role="dialog" aria-hidden="true">
+                                <div class="modal-dialog">
+                                    <div class="modal-content text-start">
+                                        <div class="modal-header">
+                                            <h4 class="modal-title"> 确认更新博文吗? </h4>
+                                        </div>
+                                        <div class="modal-body">
+                                            <p> 是否确认更新博文?请注意校对文本。 </p>
+                                        </div>
+                                        <div class="modal-footer">
+                                            {{ view.submit(class="btn btn-danger", value="确认") }}
+                                            <button type="button" class="btn btn-outline-dark" data-bs-dismiss="modal">取消</button>
+                                        </div>
+                                    </div>
+                                </div>
                             </div>
-                            {% for error in view.content.errors %}
-                                <div class="invalid-feedback"> {{ error }} </div>
-                            {% endfor %}
-
-                            {% if can_update %}
-                                <div id="UpdateModal" class="modal fade" role="dialog" aria-hidden="true">
-                                    <div class="modal-dialog">
-                                        <div class="modal-content text-start">
-                                            <div class="modal-header">
-                                                <h4 class="modal-title"> 确认更新博文吗? </h4>
-                                            </div>
+                        {% endif %}
+                    </form>
+                        {% if can_update %}
+                            <div id="UpdateArchiveModal" class="modal fade" role="dialog" aria-hidden="true">
+                                <div class="modal-dialog">
+                                    <div class="modal-content text-start">
+                                        <div class="modal-header">
+                                            <h4 class="modal-title"> 更新博客归档信息? </h4>
+                                        </div>
+                                        <form method="post" class="was-validated">
                                             <div class="modal-body">
-                                                <p> 是否确认更新博文?请注意校对文本。 </p>
+                                                {{ archive.hidden_tag() }}
+                                                {{ archive.blog_id() }}
+                                                {{ macro.render_select_field(archive.archive) }}
                                             </div>
                                             <div class="modal-footer">
-                                                {{ view.submit(class="btn btn-danger", value="确认") }}
+                                                {{ archive.add(class="btn btn-outline-danger", formaction=url_for("docx.update_archive_page", add='1')) }}
+                                                {{ archive.sub(class="btn btn-outline-danger", formaction=url_for("docx.update_archive_page", add='0')) }}
                                                 <button type="button" class="btn btn-outline-dark" data-bs-dismiss="modal">取消</button>
                                             </div>
-                                        </div>
-                                    </div>
-                                </div>
-                            {% endif %}
-                        </form>
-                            {% if can_update %}
-                                <div id="UpdateArchiveModal" class="modal fade" role="dialog" aria-hidden="true">
-                                    <div class="modal-dialog">
-                                        <div class="modal-content text-start">
-                                            <div class="modal-header">
-                                                <h4 class="modal-title"> 更新博客归档信息? </h4>
-                                            </div>
-                                            <form method="post" class="was-validated">
-                                                <div class="modal-body">
-                                                    {{ archive.hidden_tag() }}
-                                                    {{ archive.blog_id() }}
-                                                    {{ macro.render_select_field(archive.archive) }}
-                                                </div>
-                                                <div class="modal-footer">
-                                                    {{ archive.add(class="btn btn-outline-danger", formaction=url_for("docx.update_archive_page", add='1')) }}
-                                                    {{ archive.sub(class="btn btn-outline-danger", formaction=url_for("docx.update_archive_page", add='0')) }}
-                                                    <button type="button" class="btn btn-outline-dark" data-bs-dismiss="modal">取消</button>
-                                                </div>
-                                            </form>
-                                        </div>
+                                        </form>
                                     </div>
                                 </div>
+                            </div>
 
-                                <div class="text-end mb-2">
-                                    <div class="btn-group">
-                                        <a type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#UpdateModal"> 更新博文 </a>
-                                        {% if article.top %}
-                                            <a class="btn btn-danger" href="{{ url_for("docx.set_blog_top_page", blog=article.id, top='0') }}"> 取消置顶 </a>
-                                        {% else %}
-                                            <a class="btn btn-danger" href="{{ url_for("docx.set_blog_top_page", blog=article.id, top='1') }}"> 置顶文章 </a>
-                                        {% endif %}
-                                        <a type="button" class="btn btn-outline-danger" data-bs-toggle="modal" data-bs-target="#UpdateArchiveModal"> 更新归档 </a>
-                                    </div>
+                            <div class="text-end mb-2">
+                                <div class="btn-group">
+                                    <a type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#UpdateModal"> 更新博文 </a>
+                                    {% if article.top %}
+                                        <a class="btn btn-danger" href="{{ url_for("docx.set_blog_top_page", blog=article.id, top='0') }}"> 取消置顶 </a>
+                                    {% else %}
+                                        <a class="btn btn-danger" href="{{ url_for("docx.set_blog_top_page", blog=article.id, top='1') }}"> 置顶文章 </a>
+                                    {% endif %}
+                                    <a type="button" class="btn btn-outline-danger" data-bs-toggle="modal" data-bs-target="#UpdateArchiveModal"> 更新归档 </a>
                                 </div>
-                            {% endif %}
-                    </article>
-                </div>
-            {% endcache %}
+                            </div>
+                        {% endif %}
+                </article>
+            </div>
         {% endif %}
 
         {% if current_user.check_role("ReadComment") %}

+ 23 - 17
templates/index/index.html

@@ -8,22 +8,28 @@
 {% endblock %}
 
 {% block content %}
-    {% cache conf["VIEW_CACHE_EXPIRE"], ":index" %}
-        <section id="base" class="container mt-3">
-            <div class="row">
-                <article class="col-12">
-                    <div class="introduce mx-lg-2">
-                        {% for info in conf['INTRODUCE'] %}
-                            <h2> {{ info[0] }} </h2>
-                            {{ info[1] | safe }}
-                        {% endfor %}
+    <section id="base" class="container mt-3">
+        <div class="row">
+            <article class="col-12">
+                <div class="introduce mx-lg-2">
+                    {% for info in conf['INTRODUCE'] %}
+                        <h2> {{ info[0] }} </h2>
+                        {{ info[1] | safe }}
+                    {% endfor %}
 
-                        {% for link in conf['INTRODUCE_LINK'] %}
-                            <a class="btn btn-outline-info mb-2" href="{{ conf['INTRODUCE_LINK'][link] }}"> {{ link }} </a>
-                        {% endfor %}
-                    </div>
-                </article>
-            </div>
+                    <hr>
+
+                    <p> 欢迎页点击量: {{ hello_clicks }} </p>
+                    <p> 首页点击量: {{ home_clicks }} </p>
+
+                    {% for link in conf['INTRODUCE_LINK'] %}
+                        <a class="btn btn-outline-info mb-2" href="{{ conf['INTRODUCE_LINK'][link] }}"> {{ link }} </a>
+                    {% endfor %}
+                </div>
+            </article>
+        </div>
+
+        {% cache conf["VIEW_CACHE_EXPIRE"], ":index" %}
             <div class="row">
                 {% if current_user.check_role("ReadBlog") %}  {# 检查是否具有读取权限 #}
                     {% if current_user.check_role("ReadMsg") %}
@@ -85,6 +91,6 @@
                     {% endif %}
                 {% endif %}
             </div>
-        </section>
-    {% endcache %}
+        {% endcache %}
+    </section>
 {% endblock %}