Browse Source

refactor: 调整MySQL系统

SongZihuan 2 năm trước cách đây
mục cha
commit
335685cf5e
20 tập tin đã thay đổi với 401 bổ sung298 xóa
  1. 3 4
      app/app.py
  2. 2 2
      app/archive.py
  3. 8 8
      app/auth.py
  4. 17 16
      app/docx.py
  5. 2 2
      app/msg.py
  6. 34 24
      object/archive.py
  7. 65 50
      object/blog.py
  8. 38 21
      object/comment.py
  9. 41 16
      object/msg.py
  10. 73 88
      object/user.py
  11. BIN
      requirements.txt
  12. 14 13
      sql/archive.py
  13. 2 2
      sql/blog.py
  14. 15 4
      sql/comment.py
  15. 23 12
      sql/msg.py
  16. 40 18
      sql/mysql.py
  17. 11 5
      sql/user.py
  18. 7 7
      templates/docx/article.html
  19. 2 2
      templates/index/index.html
  20. 4 4
      templates/msg/msg.html

+ 3 - 4
app/app.py

@@ -1,7 +1,7 @@
 import os
 import sys
 
-from flask import Flask, url_for, request, current_app, g, render_template
+from flask import Flask, url_for, request, current_app, render_template
 from flask_mail import Mail
 from flask_login import LoginManager, current_user
 from flask.logging import default_handler
@@ -12,7 +12,7 @@ import logging
 from bs4 import BeautifulSoup
 
 from configure import conf
-from object.user import AnonymousUser, load_user_by_email
+from object.user import AnonymousUser, User
 
 from .index import index
 from .archive import archive
@@ -59,7 +59,7 @@ class HBlogFlask(Flask):
 
         @self.login_manager.user_loader
         def user_loader(email: str):
-            return load_user_by_email(email)
+            return User(email)
 
         func = {"render_template": render_template, "self": self}
         for i in [400, 401, 403, 404, 405, 408, 410, 413, 414, 423, 500, 501, 502]:
@@ -171,4 +171,3 @@ class HBlogFlask(Flask):
 
 
 Hblog = Union[HBlogFlask, Flask]
-

+ 2 - 2
app/archive.py

@@ -48,7 +48,7 @@ def create_archive_page():
     form: CreateArchiveForm = g.form
     name = form.name.data
     describe = form.describe.data
-    if Archive(name, describe, None).create():
+    if Archive.create(name, describe):
         app.HBlogFlask.print_sys_opt_success_log(f"Create archive {name}")
         flash(f"创建归档 {name} 成功")
     else:
@@ -62,7 +62,7 @@ def create_archive_page():
 @app.role_required("DeleteBlog", "delete archive")
 def delete_archive_page():
     archive_id = int(request.args.get("archive", 1))
-    if Archive(None, None, archive_id).delete():
+    if Archive(archive_id).delete():
         app.HBlogFlask.print_sys_opt_success_log(f"Delete archive {archive_id}")
         flash("归档删除成功")
     else:

+ 8 - 8
app/auth.py

@@ -12,7 +12,7 @@ from wtforms import (EmailField,
 from wtforms.validators import DataRequired, Length, Regexp, EqualTo
 
 import app
-from object.user import User, load_user_by_email
+from object.user import User
 from send_email import send_msg
 
 auth = Blueprint("auth", __name__)
@@ -58,7 +58,7 @@ class RegisterForm(EmailPasswd):
     submit = SubmitField("注册")
 
     def validate_email(self, field):
-        if load_user_by_email(field.data) is not None:
+        if User(field.data) is not None:
             raise ValidationError("邮箱已被注册")
 
 
@@ -82,7 +82,7 @@ class DeleteUserForm(AuthField):
         self.email_user = None
 
     def validate_email(self, field):
-        self.email_user = load_user_by_email(field.data)
+        self.email_user = User(field.data)
         if self.email_user is None:
             raise ValidationError("邮箱用户不存在")
 
@@ -123,7 +123,7 @@ class SetRoleForm(RoleForm):
         self.email_user = None
 
     def validate_email(self, field):
-        self.email_user = load_user_by_email(field.data)
+        self.email_user = User(field.data)
         if self.email_user is None:
             raise ValidationError("邮箱用户不存在")
 
@@ -131,7 +131,7 @@ class SetRoleForm(RoleForm):
 @auth.route('/user/yours')
 @login_required
 def yours_page():
-    msg_count, comment_count, blog_count = current_user.count_info()
+    msg_count, comment_count, blog_count = current_user.count
     app.HBlogFlask.print_load_page_log("user info")
     return render_template("auth/yours.html", msg_count=msg_count, comment_count=comment_count, blog_count=blog_count)
 
@@ -144,7 +144,7 @@ def login_page():
 
     form = LoginForm()
     if form.validate_on_submit():
-        user = load_user_by_email(form.email.data)
+        user = User(form.email.data)
         if user is not None and user.check_passwd(form.passwd.data):
             login_user(user, form.remember.data)
             next_page = request.args.get("next")
@@ -193,12 +193,12 @@ def confirm_page():
         abort(404)
         return
 
-    if load_user_by_email(token[0]) is not None:
+    if User(token[0]) is not None:
         app.HBlogFlask.print_user_opt_fail_log(f"Confirm (bad token)")
         abort(404)
         return
 
-    User(token[0], token[1], None, None).create()
+    User.create(token[0], token[1])
     current_app.logger.info(f"{token[0]} confirm success")
     app.HBlogFlask.print_import_user_opt_success_log(f"confirm {token[0]}")
     flash(f"用户{token[0]}认证完成")

+ 17 - 16
app/docx.py

@@ -7,9 +7,9 @@ from typing import Optional
 
 import app
 from sql.base import DBBit
-from object.blog import BlogArticle, load_blog_by_id
+from object.blog import BlogArticle
 from object.comment import Comment
-from object.archive import load_archive_by_id, Archive
+from object.archive import Archive
 
 docx = Blueprint("docx", __name__)
 
@@ -57,7 +57,7 @@ class UpdateBlogForm(EditorMD):
     def __init__(self, blog: Optional[BlogArticle] = None, **kwargs):
         super().__init__(**kwargs)
         if blog is not None:
-            self.blog_id.data = blog.blog_id
+            self.blog_id.data = blog.id
             self.content.data = blog.content
 
 
@@ -82,7 +82,7 @@ class UpdateBlogArchiveForm(FlaskForm):
             for a in blog.archive:
                 a: Archive
                 self.archive_data.append(a)
-            self.blog_id.data = blog.blog_id
+            self.blog_id.data = blog.id
 
     def validate_archive(self, field):
         for i in field.data:
@@ -145,7 +145,7 @@ def archive_page():
 def __load_article_page(blog_id: int, form: WriteCommentForm,
                         view: Optional[UpdateBlogForm] = None,
                         archive: Optional[UpdateBlogArchiveForm] = None):
-    article = load_blog_by_id(blog_id)
+    article = BlogArticle(blog_id)
     if article is None:
         app.HBlogFlask.print_user_opt_fail_log(f"Load article with error id({blog_id})")
         abort(404)
@@ -175,7 +175,7 @@ def article_page():
 @docx.route('/article/download')
 def article_down_page():
     blog_id = int(request.args.get("blog", 1))
-    article = load_blog_by_id(blog_id)
+    article = BlogArticle(blog_id)
     if article is None:
         app.HBlogFlask.print_user_opt_fail_log(f"Download article with error id({blog_id})")
         abort(404)
@@ -198,11 +198,11 @@ def create_docx_page():
     archive = []
     if -1 not in form.archive.data:
         for i in form.archive.data:
-            i = load_archive_by_id(i)
+            i = Archive(i)
             if i is not None:
                 archive.append(i)
 
-    if BlogArticle(None, current_user, title, subtitle, form.content.data, archive=archive).create():
+    if BlogArticle.create(title, subtitle, form.content.data, archive, current_user):
         app.HBlogFlask.print_sys_opt_success_log("write blog")
         flash(f"博客 {title} 发表成功")
     else:
@@ -214,11 +214,11 @@ def create_docx_page():
 @docx.route('/article/update', methods=["POST"])
 @login_required
 @app.form_required(UpdateBlogForm, "update blog",
-                   lambda form: __load_article_page(form.blog_id.data, WriteCommentForm(), form))
+                   lambda form: __load_article_page(form.id.data, WriteCommentForm(), form))
 @app.role_required("WriteBlog", "write blog")
 def update_docx_page():
     form: UpdateBlogForm = g.form
-    if BlogArticle(form.blog_id.data, None, None, None, None).update(form.content.data):
+    if BlogArticle(form.blog_id.data).update(form.content.data):
         app.HBlogFlask.print_sys_opt_success_log("update blog")
         flash("博文更新成功")
     else:
@@ -234,7 +234,7 @@ def delete_blog_page():
     blog_id = int(request.args.get("blog", -1))
     if blog_id == -1:
         return abort(400)
-    if BlogArticle(blog_id, None, None, None, None).delete():
+    if BlogArticle(blog_id).delete():
         app.HBlogFlask.print_sys_opt_success_log("delete blog")
         flash("博文删除成功")
     else:
@@ -251,7 +251,8 @@ def set_blog_top_page():
     top = request.args.get("top", '0') != '0'
     if blog_id == -1:
         return abort(400)
-    if BlogArticle(blog_id, None, None, None, None).set_top(top):
+    BlogArticle(blog_id).top = top
+    if top:
         app.HBlogFlask.print_sys_opt_success_log(f"set blog top ({top})")
         flash(f"博文{'取消' if not top else ''}置顶成功")
     else:
@@ -263,11 +264,11 @@ def set_blog_top_page():
 @docx.route("/article/set/archive", methods=["POST"])
 @login_required
 @app.form_required(UpdateBlogArchiveForm, "update archive",
-                   lambda form: __load_article_page(form.blog_id.data, WriteCommentForm(), UpdateBlogForm(), form))
+                   lambda form: __load_article_page(form.id.data, WriteCommentForm(), UpdateBlogForm(), form))
 @app.role_required("WriteBlog", "update archive")
 def update_archive_page():
     form: UpdateBlogArchiveForm = g.form
-    article = BlogArticle(form.blog_id.data, None, None, None, None)
+    article = BlogArticle(form.blog_id.data)
     add = request.args.get("add", '0') != '0'
     for i in form.archive.data:
         if add:
@@ -287,7 +288,7 @@ def comment_page():
     blog_id = int(request.args.get("blog", 1))
     form: WriteCommentForm = g.form
     content = form.content.data
-    if Comment(None, blog_id, current_user, content).create():
+    if Comment.create(BlogArticle(blog_id), current_user, content):
         app.HBlogFlask.print_user_opt_success_log("comment")
         flash("评论成功")
     else:
@@ -301,7 +302,7 @@ def comment_page():
 @app.role_required("DeleteComment", "delete comment")
 def delete_comment_page():
     comment_id = int(request.args.get("comment", 1))
-    if Comment(comment_id, None, None, None).delete():
+    if Comment(comment_id).delete():
         app.HBlogFlask.print_sys_opt_success_log("delete comment")
         flash("博文评论成功")
     else:

+ 2 - 2
app/msg.py

@@ -58,7 +58,7 @@ def write_msg_page():
     form: WriteForm = g.form
     content = form.content.data
     secret = form.secret.data
-    if Message(None, current_user, content, secret, None).create():
+    if Message.create(current_user, content, secret):
         app.HBlogFlask.print_user_opt_success_log("write msg")
         flash("留言成功")
     else:
@@ -72,7 +72,7 @@ def write_msg_page():
 @app.role_required("DeleteMsg", "delete msg")
 def delete_msg_page():
     msg_id = int(request.args.get("msg", 1))
-    if Message(msg_id, None, None).delete():
+    if Message(msg_id).delete():
         app.HBlogFlask.print_user_opt_success_log("delete msg")
         flash("留言删除成功")
     else:

+ 34 - 24
object/archive.py

@@ -1,47 +1,57 @@
-from typing import Optional
-
-from sql.archive import (get_archive_name_by_id,
+from sql.archive import (read_archive,
                          create_archive,
                          get_archive_list,
-                         read_archive,
+                         get_blog_archive,
                          delete_archive,
                          add_blog_to_archive,
                          sub_blog_from_archive)
 
 
-def load_archive_by_id(archive_id: int) -> "Optional[Archive]":
-    archive_name, describe = get_archive_name_by_id(archive_id)
-    if archive_id is None:
-        return None
-    return Archive(archive_name, describe, archive_id)
-
-
-class Archive:
-    def __init__(self, name, describe, archive_id):
-        self.name = name
-        self.describe = describe
-        self.archive_id = archive_id
-
+class _Archive:
     @staticmethod
     def get_archive_list():
         return get_archive_list()
 
     @staticmethod
     def get_blog_archive(blog_id: int):
-        archive = read_archive(blog_id)
+        archive = get_blog_archive(blog_id)
         archive_list = []
         for i in archive:
-            archive_list.append(Archive(i[1], i[2], i[0]))
+            archive_list.append(Archive(i[1]))
         return archive_list
 
-    def create(self):
-        return create_archive(self.name, self.describe)
+    @staticmethod
+    def create(name, describe):
+        ret = create_archive(name, describe)
+        if ret is None:
+            return None
+        return Archive(ret)
+
+
+class Archive(_Archive):
+    def __init__(self, archive_id):
+        self.id = archive_id
+
+    @property
+    def info(self):
+        return read_archive(self.id)
+
+    @property
+    def name(self):
+        return self.info[0]
+
+    @property
+    def describe(self):
+        return self.info[1]
+
+    def is_delete(self):
+        return len(self.name) != 0
 
     def delete(self):
-        return delete_archive(self.archive_id)
+        return delete_archive(self.id)
 
     def add_blog(self, blog_id: int):
-        add_blog_to_archive(blog_id, self.archive_id)
+        add_blog_to_archive(blog_id, self.id)
 
     def sub_blog(self, blog_id: int):
-        sub_blog_from_archive(blog_id, self.archive_id)
+        sub_blog_from_archive(blog_id, self.id)

+ 65 - 50
object/blog.py

@@ -1,6 +1,5 @@
-from typing import Optional
+from typing import List
 
-from sql.base import DBBit
 from sql.blog import (get_blog_list,
                       get_blog_count,
                       get_archive_blog_list,
@@ -13,6 +12,7 @@ from sql.blog import (get_blog_list,
                       set_blog_top,
                       get_user_user_count)
 from sql.archive import add_blog_to_archive, sub_blog_from_archive
+from sql.user import get_user_email
 import object.user
 import object.archive
 import object.comment
@@ -22,41 +22,7 @@ class LoadBlogError(Exception):
     pass
 
 
-def load_blog_by_id(blog_id) -> "Optional[BlogArticle]":
-    blog_id = blog_id
-    blog = read_blog(blog_id)
-    if len(blog) == 0:
-        return None
-
-    auth = object.user.load_user_by_id(blog[0])
-    if auth is None:
-        return None
-
-    title = blog[1]
-    subtitle = blog[2]
-    content = blog[3]
-    update_time = blog[4]
-    create_time = blog[5]
-    top = blog[6] == DBBit.BIT_1
-    comment = object.comment.load_comment_list(blog_id)
-    archive = object.archive.Archive.get_blog_archive(blog_id)
-    return BlogArticle(blog_id, auth, title, subtitle, content, update_time, create_time, top, comment, archive)
-
-
-class BlogArticle:
-    def __init__(self, blog_id, auth, title, subtitle, content, update_time=None, create_time=None, top=False,
-                 comment=None, archive=None):
-        self.blog_id = blog_id
-        self.user = auth
-        self.title = title
-        self.subtitle = subtitle
-        self.content = content
-        self.update_time = update_time
-        self.create_time = create_time
-        self.top = top
-        self.comment = [] if comment is None else comment
-        self.archive = [] if archive is None else archive
-
+class _BlogArticle:
     @staticmethod
     def get_blog_list(archive_id=None, limit=None, offset=None, not_top=False):
         if archive_id is None:
@@ -71,27 +37,76 @@ class BlogArticle:
             return get_blog_count()
         if auth is None:
             return get_archive_blog_count(archive_id)
-        return get_user_user_count(auth.get_user_id())
+        return get_user_user_count(auth.id)
+
+    @staticmethod
+    def create(title, subtitle, content, archive: "List[object.archive.Archive]", user: "object.user.User"):
+        return create_blog(user.id, title, subtitle, content, archive)
+
+
+class BlogArticle(_BlogArticle):
+    def __init__(self, blog_id):
+        self.id = blog_id
+
+    @property
+    def info(self):
+        return read_blog(self.id)
+
+    @property
+    def user(self):
+        return object.user.User(get_user_email(self.info[0]))
+
+    @property
+    def title(self):
+        return self.info[1]
+
+    @property
+    def subtitle(self):
+        return self.info[2]
 
-    def create(self):
-        if self.blog_id is not None:  # 只有 blog_id为None时才使用
-            return False
-        return create_blog(self.user.get_user_id(), self.title, self.subtitle, self.content, self.archive)
+    @property
+    def content(self):
+        return self.info[3]
+
+    @property
+    def update_time(self):
+        return self.info[4]
+
+    @property
+    def create_time(self):
+        return self.info[5]
+
+    @property
+    def top(self):
+        return self.info[6]
+
+    @top.setter
+    def top(self, top: bool):
+        set_blog_top(self.id, top)
+
+    @property
+    def comment(self):
+        return object.comment.load_comment_list(self.id)
+
+    @property
+    def archive(self):
+        return object.archive.Archive.get_blog_archive(self.id)
+
+    @property
+    def is_delete(self):
+        return not self.user.is_authenticated and len(self.content) != 0
 
     def delete(self):
-        return delete_blog(self.blog_id)
+        return delete_blog(self.id)
 
     def update(self, content: str):
-        if update_blog(self.blog_id, content):
-            self.content = content
+        if update_blog(self.id, content):
             return True
         return False
 
-    def set_top(self, top: bool):
-        set_blog_top(self.blog_id, top)
-
     def add_to_archive(self, archive_id: int):
-        return add_blog_to_archive(self.blog_id, archive_id)
+        return add_blog_to_archive(self.id, archive_id)
 
     def sub_from_archive(self, archive_id: int):
-        return sub_blog_from_archive(self.blog_id, archive_id)
+        return sub_blog_from_archive(self.id, archive_id)
+

+ 38 - 21
object/comment.py

@@ -1,34 +1,51 @@
-from sql.comment import read_comment, create_comment, get_user_comment_count, delete_comment
+from sql.comment import read_comment_list, create_comment, get_user_comment_count, delete_comment, read_comment
 import object.user
-
-from typing import Optional
+import object.blog
 
 
 def load_comment_list(blog_id: int):
-    comment_list = read_comment(blog_id)
     ret = []
-    for comment in comment_list:
-        ret.append(Comment(comment[0], blog_id, object.user.User(comment[2], None, None, comment[1]), comment[3], comment[4]))
+    for i in read_comment_list(blog_id):
+        ret.append(Comment(i))
     return ret
 
 
-class Comment:
-    def __init__(self, comment_id,
-                 blog_id: Optional[int],
-                 auth: "Optional[object.user.User]",
-                 content: Optional[str], update_time=None):
-        self.comment_id = comment_id
-        self.blog_id = blog_id
-        self.auth = auth
-        self.content = content
-        self.update_time = update_time
-
+class _Comment:
     @staticmethod
     def get_user_comment_count(auth: "object.user"):
-        return get_user_comment_count(auth.get_user_id())
+        return get_user_comment_count(auth.id)
+
+    @staticmethod
+    def create(blog: "object.blog.BlogArticle", auth: "object.user.User", content):
+        return create_comment(blog.id, auth.id, content)
+
+
+class Comment(_Comment):
+    def __init__(self, comment_id):
+        self.id = comment_id
+
+    @property
+    def info(self):
+        return read_comment(self.id)
+
+    @property
+    def blog(self):
+        return object.blog.BlogArticle(self.info[0])
+
+    @property
+    def auth(self):
+        return object.user.User(self.info[1])
+
+    @property
+    def content(self):
+        return self.info[2]
+
+    @property
+    def update_time(self):
+        return self.info[3]
 
-    def create(self):
-        return create_comment(self.blog_id, self.auth.get_user_id(), self.content)
+    def is_delete(self):
+        return not self.auth.is_authenticated and self.blog.is_delete  and len(self.content) != 0
 
     def delete(self):
-        return delete_comment(self.comment_id)
+        return delete_comment(self.id)

+ 41 - 16
object/msg.py

@@ -1,33 +1,58 @@
 from typing import Optional
 
-from sql.msg import read_msg, get_msg_count, create_msg, get_user_msg_count, delete_msg
+from sql.msg import read_msg_list, get_msg_count, create_msg, read_msg, get_user_msg_count, delete_msg
 import object.user
 
 
 def load_message_list(limit: Optional[int] = None, offset: Optional[int] = None, show_secret: bool = False):
-    msg = read_msg(limit=limit, offset=offset, show_secret=show_secret)
     ret = []
-    for i in msg:
-        ret.append(Message(i[0], object.user.User(i[2], None, None, i[1]), i[3], i[5], i[4]))
+    for i in read_msg_list(limit=limit, offset=offset, show_secret=show_secret):
+        ret.append(Message(i))
     return ret
 
 
-class Message:
-    def __init__(self, msg_id, auth: "Optional[object.user.User]", content, secret=False, update_time=None):
-        self.msg_id = msg_id
-        self.auth = auth
-        self.content = content
-        self.secret = secret
-        self.update_time = update_time
-
+class _Message:
     @staticmethod
     def get_msg_count(auth: "object.user" = None):
         if auth is None:
             return get_msg_count()
-        return get_user_msg_count(auth.get_user_id())
+        return get_user_msg_count(auth.id)
+
+    @staticmethod
+    def create(auth: "object.user.User", content, secret: bool = False):
+        ret = create_msg(auth.id, content, secret)
+        if ret is not None:
+            return Message(ret)
+        return None
+
+
+class Message(_Message):
+    def __init__(self, msg_id):
+        self.id = msg_id
+
+    @property
+    def info(self):
+        return read_msg(self.id)
+
+    @property
+    def auth(self):
+        return object.user.User(self.info[0])
+
+    @property
+    def content(self):
+        return self.info[1]
+
+    @property
+    def update_time(self):
+        return self.info[2]
+
+    @property
+    def secret(self):
+        return self.info[3]
 
-    def create(self):
-        return create_msg(self.auth.get_user_id(), self.content, self.secret)
+    @property
+    def is_delete(self):
+        return not self.auth.is_authenticated and len(self.content) != 0
 
     def delete(self):
-        return delete_msg(self.msg_id)
+        return delete_msg(self.id)

+ 73 - 88
object/user.py

@@ -2,13 +2,10 @@ from flask_login import UserMixin, AnonymousUserMixin
 from werkzeug.security import generate_password_hash, check_password_hash
 from itsdangerous import URLSafeTimedSerializer as Serializer
 from itsdangerous.exc import BadData
-from typing import Optional
 
-import sql.user
 from configure import conf
 from sql.user import (read_user,
                       check_role,
-                      get_user_email,
                       create_user,
                       get_role_name,
                       delete_user,
@@ -16,7 +13,8 @@ from sql.user import (read_user,
                       create_role,
                       delete_role,
                       set_user_role,
-                      get_role_list)
+                      get_role_list,
+                      role_authority)
 import object.blog
 import object.comment
 import object.msg
@@ -32,49 +30,71 @@ class AnonymousUser(AnonymousUserMixin):
     def check_role(self, operate: str):
         return check_role(self.role, operate)
 
-    @staticmethod
-    def get_user_id():
+    @property
+    def id(self):
         return 0
 
 
-def load_user_by_email(email: str) -> "Optional[User]":
-    user = read_user(email)
-    if len(user) == 0:
+class _User(UserMixin):
+    @staticmethod
+    def create(email, passwd_hash):
+        if create_user(email, passwd_hash) is not None:
+            return User(email)
         return None
-    passwd_hash = user[0]
-    role = user[1]
-    user_id = user[2]
-    return User(email, passwd_hash, role, user_id)
 
+    @staticmethod
+    def creat_token(email: str, passwd_hash: str):
+        s = Serializer(conf["SECRET_KEY"])
+        return s.dumps({"email": email, "passwd_hash": passwd_hash})
 
-def load_user_by_id(user_id):
-    email = get_user_email(user_id)
-    if email is None:
-        return None
-    return load_user_by_email(email)
+    @staticmethod
+    def load_token(token: str):
+        s = Serializer(conf["SECRET_KEY"])
+        try:
+            token = s.loads(token, max_age=3600)
+            return token['email'], token['passwd_hash']
+        except BadData:
+            return None
+
+    @staticmethod
+    def get_passwd_hash(passwd: str):
+        return generate_password_hash(passwd)
+
+    @staticmethod
+    def create_role(name: str, authority):
+        return create_role(name, authority)
+
+    @staticmethod
+    def delete_role(role_id: int):
+        return delete_role(role_id)
+
+    @staticmethod
+    def get_role_list():
+        return get_role_list()
 
 
-class User(UserMixin):
-    RoleAuthorize = sql.user.role_authority
+class User(_User):
+    RoleAuthorize = role_authority
 
-    def __init__(self, email, passwd_hash, role, user_id):
+    def __init__(self, email):
         self.email = email
-        self.passwd_hash = passwd_hash
-        self.role = role
-        if role is not None:
-            self.role_name = get_role_name(role)
-        else:
-            self.role_name = None
-        self.user_id = user_id
 
-    def count_info(self):
-        msg = object.msg.Message.get_msg_count(self)
-        comment = object.comment.Comment.get_user_comment_count(self)
-        blog = object.blog.BlogArticle.get_blog_count(None, self)
-        return msg, comment, blog
+    def get_id(self):
+        """Flask要求的方法"""
+        return self.email
+
+    @property
+    def is_active(self):
+        """Flask要求的属性, 表示用户是否激活(可登录), HGSSystem没有封禁用户系统, 所有用户都是被激活的"""
+        return self.id != -1
+
+    @property
+    def is_authenticated(self):
+        """Flask要求的属性, 表示登录的凭据是否正确, 这里检查是否能 load_user_by_id"""
+        return self.is_active
 
     @property
-    def s_email(self):
+    def star_email(self):
         if len(self.email) <= 4:
             return f"{self.email[0]}****"
         else:
@@ -82,51 +102,31 @@ class User(UserMixin):
             return email
 
     @property
-    def comment_count(self):
-        return 0
+    def info(self):
+        return read_user(self.email)
 
     @property
-    def blog_count(self):
-        return 0
+    def passwd_hash(self):
+        return self.info[0]
 
     @property
-    def msg_count(self):
-        return 0
+    def role(self):
+        return self.info[1]
 
     @property
-    def is_active(self):
-        """Flask要求的属性, 表示用户是否激活(可登录), HGSSystem没有封禁用户系统, 所有用户都是被激活的"""
-        return True
+    def role_name(self):
+        return get_role_name(self.info[1])
 
     @property
-    def is_authenticated(self):
-        """Flask要求的属性, 表示登录的凭据是否正确, 这里检查是否能 load_user_by_id"""
-        return True
-
-    def get_id(self):
-        """Flask要求的方法"""
-        return self.email
-
-    def get_user_id(self):
-        return self.user_id
+    def id(self):
+        return self.info[2]
 
-    @staticmethod
-    def creat_token(email: str, passwd_hash: str):
-        s = Serializer(conf["SECRET_KEY"])
-        return s.dumps({"email": email, "passwd_hash": passwd_hash})
-
-    @staticmethod
-    def load_token(token: str):
-        s = Serializer(conf["SECRET_KEY"])
-        try:
-            token = s.loads(token, max_age=3600)
-            return token['email'], token['passwd_hash']
-        except BadData:
-            return None
-
-    @staticmethod
-    def get_passwd_hash(passwd: str):
-        return generate_password_hash(passwd)
+    @property
+    def count(self):
+        msg = object.msg.Message.get_msg_count(self)
+        comment = object.comment.Comment.get_user_comment_count(self)
+        blog = object.blog.BlogArticle.get_blog_count(None, self)
+        return msg, comment, blog
 
     def check_passwd(self, passwd: str):
         return check_password_hash(self.passwd_hash, passwd)
@@ -134,26 +134,11 @@ class User(UserMixin):
     def check_role(self, operate: str):
         return check_role(self.role, operate)
 
-    def create(self):
-        return create_user(self.email, self.passwd_hash)
-
     def delete(self):
-        return delete_user(self.user_id)
+        return delete_user(self.id)
 
     def change_passwd(self, passwd):
-        return change_passwd_hash(self.user_id, self.get_passwd_hash(passwd))
-
-    @staticmethod
-    def create_role(name: str, authority):
-        return create_role(name, authority)
-
-    @staticmethod
-    def delete_role(role_id: int):
-        return delete_role(role_id)
+        return change_passwd_hash(self.id, self.get_passwd_hash(passwd))
 
     def set_user_role(self, role_id: int):
-        return set_user_role(role_id, self.user_id)
-
-    @staticmethod
-    def get_role_list():
-        return get_role_list()
+        return set_user_role(role_id, self.id)

BIN
requirements.txt


+ 14 - 13
sql/archive.py

@@ -10,11 +10,20 @@ def create_archive(name: str, describe: str):
                     columns=["Name", "DescribeText"],
                     values=f"'{name}', '{describe}'")
     if cur is None or cur.rowcount == 0:
-        return False
-    return True
+        return None
+    return cur.lastrowid
+
+
+def read_archive(archive_id: int):
+    """ 获取归档 ID """
+    cur = db.search(columns=["Name", "DescribeText"], table="archive",
+                    where=f"ID={archive_id}")
+    if cur is None or cur.rowcount == 0:
+        return ["", ""]
+    return cur.fetchone()
 
 
-def read_archive(blog_id: int):
+def get_blog_archive(blog_id: int):
     """ 获取文章的归档 """
     cur = db.search(columns=["ArchiveID", "ArchiveName", "DescribeText"], table="blog_archive_with_name",
                     where=f"BlogID={blog_id}")
@@ -59,16 +68,8 @@ def get_archive_list(limit: Optional[int] = None, offset: Optional[int] = None):
     """ 获取归档列表 """
     cur = db.search(columns=["ID", "Name", "DescribeText", "Count"], table="archive_with_count",
                     limit=limit,
-                    offset=offset)
+                    offset=offset,
+                    order_by=[("Count", "DESC"), ("Name", "ASC")])
     if cur is None or cur.rowcount == 0:
         return []
     return cur.fetchall()
-
-
-def get_archive_name_by_id(archive_id: int):
-    """ 获取归档 ID """
-    cur = db.search(columns=["Name", "DescribeText"], table="archive",
-                    where=f"ID={archive_id}")
-    if cur is None or cur.rowcount == 0:
-        return None, None
-    return cur.fetchone()

+ 2 - 2
sql/blog.py

@@ -16,7 +16,7 @@ def create_blog(auth_id: int, title: str, subtitle: str, content: str,
         return False
     blog_id = cur.lastrowid
     for archive in archive_list:
-        if not add_blog_to_archive(blog_id, archive.archive_id):
+        if not add_blog_to_archive(blog_id, archive.id):
             return False
     return True
 
@@ -38,7 +38,7 @@ def read_blog(blog_id: int) -> list:
                     table="blog",
                     where=f"ID={blog_id}")
     if cur is None or cur.rowcount == 0:
-        return []
+        return [-1, "", "", "", 0, -1, False]
     return cur.fetchone()
 
 

+ 15 - 4
sql/comment.py

@@ -1,14 +1,15 @@
 from sql import db
 
 
-def read_comment(blog_id: int):
+def read_comment_list(blog_id: int):
     """ 读取文章的 comment """
-    cur = db.search(columns=["CommentID", "Auth", "Email", "Content", "UpdateTime"],
+    cur = db.search(columns=["CommentID"],
                     table="comment_user",
-                    where=f"BlogID={blog_id}")
+                    where=f"BlogID={blog_id}",
+                    order_by=[("UpdateTime", "DESC")])
     if cur is None or cur.rowcount == 0:
         return []
-    return cur.fetchall()
+    return [i[0] for i in cur.fetchall()]
 
 
 def create_comment(blog_id: int, user_id: int, content: str):
@@ -22,6 +23,16 @@ def create_comment(blog_id: int, user_id: int, content: str):
     return True
 
 
+def read_comment(comment_id: int):
+    """ 读取 comment """
+    cur = db.search(columns=["BlogID", "Email", "Content", "UpdateTime"],
+                    table="comment_user",
+                    where=f"CommentID={comment_id}")
+    if cur is None or cur.rowcount == 0:
+        return [-1, "", "", 0]
+    return cur.fetchone()
+
+
 def delete_comment(comment_id):
     """ 删除评论 """
     cur = db.delete(table="comment", where=f"ID={comment_id}")

+ 23 - 12
sql/msg.py

@@ -2,27 +2,38 @@ from sql import db
 from typing import Optional
 
 
-def create_msg(auth: int, content: str, secret: bool = False):
-    content = content.replace("'", "''")
-    cur = db.insert(table="message",
-                    columns=["Auth", "Content", "Secret"],
-                    values=f"{auth}, '{content}', {1 if secret else 0}")
-    return cur is not None and cur.rowcount == 1
-
-
-def read_msg(limit: Optional[int] = None, offset: Optional[int] = None, show_secret: bool = False):
+def read_msg_list(limit: Optional[int] = None, offset: Optional[int] = None, show_secret: bool = False):
     if show_secret:
         where = None
     else:
         where = "Secret=0"
 
-    cur = db.search(columns=["MsgID", "Auth", "Email", "Content", "UpdateTime", "Secret"], table="message_user",
+    cur = db.search(columns=["MsgID"], table="message_user",
                     limit=limit,
                     where=where,
-                    offset=offset)
+                    offset=offset,
+                    order_by=[("UpdateTime", "DESC")])
     if cur is None or cur.rowcount == 0:
         return []
-    return cur.fetchall()
+    return [i[0] for i in cur.fetchall()]
+
+
+def create_msg(auth: int, content: str, secret: bool = False):
+    content = content.replace("'", "''")
+    cur = db.insert(table="message",
+                    columns=["Auth", "Content", "Secret"],
+                    values=f"{auth}, '{content}', {1 if secret else 0}")
+    if cur is None or cur.rowcount != 1:
+        return None
+    return cur.lastrowid
+
+
+def read_msg(msg_id: int):
+    cur = db.search(columns=["Email", "Content", "UpdateTime", "Secret"], table="message_user",
+                    where=f"MsgID={msg_id}")
+    if cur is None or cur.rowcount == 0:
+        return ["", "", 0, False]
+    return cur.fetchone()
 
 
 def delete_msg(msg_id: int):

+ 40 - 18
sql/mysql.py

@@ -5,6 +5,10 @@ from sql.base import Database, DBException, DBCloseException
 from typing import Optional, Union, List, Tuple, Dict
 
 
+class MysqlConnectException(DBCloseException):
+    """Mysql Connect error"""
+
+
 class MysqlDB(Database):
     def __init__(self,
                  host: Optional[str],
@@ -27,19 +31,18 @@ class MysqlDB(Database):
         self.logger.info(f"MySQL({self._name}@{self._host}) connect")
 
     def close(self):
-        if self._cursor is not None:
-            self._cursor.close()
-        if self._db is not None:
-            self._db.close()
-        self._db = None
-        self._cursor = None
-        self._lock = None
-        self.logger.warning(f"MySQL({self._name}@{self._host}) connect close")
+        self._close()
 
     def is_connect(self) -> bool:
         if self._cursor is None or self._db is None:
             return False
-        return True
+
+        try:
+            self._db.ping(False)
+        except Exception:
+            return False
+        else:
+            return True
 
     def get_cursor(self) -> pymysql.cursors.Cursor:
         if self._cursor is None or self._db is None:
@@ -122,30 +125,29 @@ class MysqlDB(Database):
         kw_str = ", ".join(kw_list)
         return self.__done(f"UPDATE {table} SET {kw_str} WHERE {where};", not_commit=not_commit)
 
-    def __search(self, sql) -> Union[None, pymysql.cursors.Cursor]:
-        if self._cursor is None or self._db is None:
-            raise DBCloseException
+    def commit(self):
+        self._commit()
 
+    def __search(self, sql) -> Union[None, pymysql.cursors.Cursor]:
         try:
             self._lock.acquire()  # 上锁
+            self._connect()
             self._cursor.execute(sql)
         except pymysql.MySQLError:
-            self.logger.error(f"MySQL({self._name}@{self._host}) SQL {sql} error", exc_info=True)
+            self.logger.error(f"MySQL({self._name}@{self._host}) SQL {sql} error", exc_info=True, stack_info=True)
             return None
         finally:
             self._lock.release()  # 释放锁
         return self._cursor
 
     def __done(self, sql, not_commit: bool = False) -> Union[None, pymysql.cursors.Cursor]:
-        if self._cursor is None or self._db is None:
-            raise DBCloseException
-
         try:
             self._lock.acquire()
+            self._connect()
             self._cursor.execute(sql)
         except pymysql.MySQLError:
             self._db.rollback()
-            self.logger.error(f"MySQL({self._name}@{self._host}) SQL {sql} error", exc_info=True)
+            self.logger.error(f"MySQL({self._name}@{self._host}) SQL {sql} error", exc_info=True, stack_info=True)
             return None
         finally:
             if not not_commit:
@@ -153,9 +155,29 @@ class MysqlDB(Database):
             self._lock.release()
         return self._cursor
 
-    def commit(self):
+    def _connect(self):
+        if self._db is None:
+            raise MysqlConnectException
+
+        try:
+            self._db.ping(False)
+        except Exception:
+            raise MysqlConnectException
+
+    def _close(self):
+        if self._cursor is not None:
+            self._cursor.close()
+        if self._db is not None:
+            self._db.close()
+        self._db = None
+        self._cursor = None
+        self._lock = None
+        self.logger.warning(f"MySQL({self._name}@{self._host}) connect close")
+
+    def _commit(self):
         try:
             self._lock.acquire()
+            self._connect()
             self._db.commit()
         except pymysql.MySQLError:
             self.logger.error(f"MySQL({self._name}@{self._host}) commit error", exec_info=True)

+ 11 - 5
sql/user.py

@@ -14,21 +14,27 @@ role_authority = ["WriteBlog", "WriteComment", "WriteMsg", "CreateUser",
 def read_user(email: str):
     """ 读取用户 """
     cur = db.search(columns=["PasswdHash", "Role", "ID"], table="user", where=f"Email='{email}'")
-    if cur is None or cur.rowcount == 0:
-        return []
-    assert cur.rowcount == 1
+    if cur is None or cur.rowcount != 1:
+        return ["", -1, -1]
     return cur.fetchone()
 
 
 def create_user(email: str, passwd: str):
     """ 创建用户 """
     email = email.replace("'", "''")
+    if len(email) == 0:
+        return None
+
     cur = db.search(columns=["count(Email)"], table="user")  # 统计个数
     passwd = object.user.User.get_passwd_hash(passwd)
     if cur is None or cur.rowcount == 0 or cur.fetchone()[0] == 0:
-        db.insert(table='user', columns=['Email', 'PasswdHash', 'Role'], values=f"'{email}', '{passwd}', 1")  # 创建为管理员用户
+        # 创建为管理员用户
+        cur = db.insert(table='user', columns=['Email', 'PasswdHash', 'Role'], values=f"'{email}', '{passwd}', 1")
     else:
-        db.insert(table='user', columns=['Email', 'PasswdHash'], values=f"'{email}', '{passwd}'")
+        cur = db.insert(table='user', columns=['Email', 'PasswdHash'], values=f"'{email}', '{passwd}'")
+    if cur is None or cur.rowcount != 1:
+        return None
+    return cur.lastrowid
 
 
 def delete_user(user_id: int):

+ 7 - 7
templates/docx/article.html

@@ -18,7 +18,7 @@
                     {% for archive in article.archive %}
                         <span class="badge badge-info"> {{ archive.name }} </span>
                     {% endfor %}
-                    <a href="{{ url_for('docx.article_down_page', blog=article.blog_id) }}"> 下载 </a>
+                    <a href="{{ url_for('docx.article_down_page', blog=article.id) }}"> 下载 </a>
                     <hr>
 
                     <form method="post" action="{{ url_for('docx.update_docx_page') }}">
@@ -78,9 +78,9 @@
                             <div class="text-right mb-2">
                                 <a type="button" class="btn btn-primary" data-toggle="modal" data-target="#UpdateModal"> 更新博文 </a>
                                 {% if article.top %}
-                                    <a class="btn btn-danger" href="{{ url_for("docx.set_blog_top_page", blog=article.blog_id, top='0') }}"> 取消置顶 </a>
+                                    <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.blog_id, top='1') }}"> 置顶文章 </a>
+                                    <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-toggle="modal" data-target="#UpdateArchiveModal"> 更新归档 </a>
                             </div>
@@ -95,7 +95,7 @@
                     <h1 class="mt-3"> 评论 </h1>
 
                     <section class="col-12 text-right">
-                        <form action="{{ url_for('docx.comment_page', blog=article.blog_id) }}" method="post">
+                        <form action="{{ url_for('docx.comment_page', blog=article.id) }}" method="post">
                             {{ form.hidden_tag() }}
                             {{ form.content(class="form-control mb-2", rows="3") }}
                             {% for error in form.content.errors %}
@@ -127,7 +127,7 @@
                     {% for comment in article.comment %}
 
                         {% if show_delete %}
-                            <div id="DeleteModal{{comment.comment_id}}" class="modal fade" role="dialog" aria-hidden="true">
+                            <div id="DeleteModal{{comment.id }}" class="modal fade" role="dialog" aria-hidden="true">
                                 <div class="modal-dialog">
                                     <div class="modal-content text-left">
                                         <div class="modal-header">
@@ -138,7 +138,7 @@
                                         </div>
                                         <div class="modal-footer">
                                             <a class="btn btn-danger"
-                                               href="{{ url_for("docx.delete_comment_page", comment=comment.comment_id) }}"> 删除 </a>
+                                               href="{{ url_for("docx.delete_comment_page", comment=comment.id) }}"> 删除 </a>
                                             <button type="button" class="btn btn-outline-dark" data-dismiss="modal"> 取消 </button>
                                         </div>
                                     </div>
@@ -157,7 +157,7 @@
 
                                     {% if show_delete %}
                                         <a class="mb-2"
-                                            data-toggle="modal" data-target="#DeleteModal{{comment.comment_id}}"> &times; </a>
+                                            data-toggle="modal" data-target="#DeleteModal{{comment.id }}"> &times; </a>
                                     {% endif %}
 
                                     <br>

+ 2 - 2
templates/index/index.html

@@ -57,7 +57,7 @@
                                 {% if show_email %}  {# 判断是否可读取用户信息 #}
                                     {{ msg.auth.email }}
                                 {% else %}
-                                    {{ msg.auth.s_email }}
+                                    {{ msg.auth.star_email }}
                                 {% endif %}
                                 <br> <small>
                                 {{ msg.update_time }} </small> </p>
@@ -73,7 +73,7 @@
                                 {% if show_email %}  {# 判断是否可读取用户信息 #}
                                     {{ msg.auth.email }}
                                 {% else %}
-                                    {{ msg.auth.s_email }}
+                                    {{ msg.auth.star_email }}
                                 {% endif %}
                                 <br> <small>
                                 {{ msg.update_time }} </small> </p>

+ 4 - 4
templates/msg/msg.html

@@ -47,7 +47,7 @@
                     {% for msg in msg_list %}
                         <div class="msg mr-0">
                             {% if show_delete %}
-                                <div id="DeleteModal{{msg.msg_id}}" class="modal fade" role="dialog" aria-hidden="true">
+                                <div id="DeleteModal{{msg.id }}" class="modal fade" role="dialog" aria-hidden="true">
                                     <div class="modal-dialog">
                                         <div class="modal-content text-left">
                                             <div class="modal-header">
@@ -58,7 +58,7 @@
                                             </div>
                                             <div class="modal-footer">
                                                 <a class="btn btn-danger"
-                                                   href="{{ url_for("msg.delete_msg_page", msg=msg.msg_id) }}"> 删除 </a>
+                                                   href="{{ url_for("msg.delete_msg_page", msg=msg.id) }}"> 删除 </a>
                                                 <button type="button" class="btn btn-outline-dark" data-dismiss="modal"> 取消 </button>
                                             </div>
                                         </div>
@@ -70,12 +70,12 @@
                                 {% if show_email %}  {# 判断是否可读取用户信息 #}
                                     {{ msg.auth.email }}
                                 {% else %}
-                                    {{ msg.auth.s_email }}
+                                    {{ msg.auth.star_email }}
                                 {% endif %}
 
                                 {% if show_delete %}
                                     <a class="mb-2"
-                                        data-toggle="modal" data-target="#DeleteModal{{msg.msg_id}}"> &times; </a>
+                                        data-toggle="modal" data-target="#DeleteModal{{msg.id }}"> &times; </a>
                                 {% endif %}
 
                                 <br>