一個練習專案,好玩的bbs-python-tornado

河北大学-徐小波發表於2024-09-02

程式碼:

import os.path
import tornado.httpserver
import tornado.web
import tornado.options
import tornado.ioloop
from tornado.options import define, options
import MySQLdb
import json
import hashlib
import random
import math
import os
from datetime import datetime

class DateEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, datetime):
            return obj.strftime("%Y-%m-%d %H:%M:%S")
        else:
            return json.JSONEncoder.default(self, obj)
        
class BaseHandler:
    conn = None
    cursor = None
    secretKey = 'saacac3423@21212'
    pagesize = 20
    
    def __init__(self):
        self.conn = MySQLdb.Connection('127.0.0.1', 'root', '123456', 'my_bbs')
        self.cursor = self.conn.cursor(cursorclass = MySQLdb.cursors.DictCursor)
        
    def __del__(self):
        self.cursor.close()
        self.conn.close()
 
    def getloginuserinfo(self, sessionId):
        try:
            sessionIdHead = self.get_secure_cookie("sessionId")
        except:
            sessionIdHead = ''
        
        if sessionIdHead is not None and sessionIdHead != '':
            sessionId = sessionIdHead
            
        sql = "select id,username,nickname,addTime,sessionId from user where sessionId='%s'" % sessionId
        self.cursor.execute(sql)
        data = self.cursor.fetchone()
        if data is None:
            data = {'id' : 0, 'username' : '', 'nickname' : '', 'addTime' : '', 'sessionId' : ''}
        
        return data
        
    def response(self, code, msg, data):
        if code != 0:
            result = {'code' : code, 'msg' : msg, 'data' : None}
        else:
            result = {'code' : 0, 'msg' : '', 'data' : data}
            
        result = json.dumps(result, cls = DateEncoder, ensure_ascii = False)
            
        return result
    
    def error(self, code, msg):
        self.write(self.response(code, msg, None))
    
    def success(self, data = {}):
        self.write(self.response(0, '', data))
    
class IndexHandler(tornado.web.RequestHandler, BaseHandler):
    def get(self):
        self.write("此站介面使用python.tornado實現,<a href='api.html' target='_blank'>介面列表</a>")

class RegisterHandler(tornado.web.RequestHandler, BaseHandler):
    def get(self):
        username = self.get_argument("username", "")
        password = self.get_argument("password", "")
        nickname = self.get_argument("nickname", "")
        sql = "select id,username,nickname,addTime from user where username='%s'" % username
        self.cursor.execute(sql)
        data = self.cursor.fetchone()
        if data != None:
            self.error(1, '使用者名稱已經存在')
            return True

        try:
            passwordMd5 = hashlib.md5(password.encode(encoding='utf-8')).hexdigest()
            sql = "insert into user(username, password, nickname) value('%s', '%s', '%s')" % (username, passwordMd5, nickname)
            self.cursor.execute(sql)
            self.conn.commit()
            insertId = self.cursor.lastrowid
            self.success(insertId)
        except MySQLdb.Error as e:
            self.conn.rollback()
            self.error(1, '註冊失敗')
            
class LoginHandler(tornado.web.RequestHandler, BaseHandler):
    def get(self):
        username = self.get_argument("username", "")
        password = self.get_argument("password", "")
        passwordMd5 = hashlib.md5(password.encode(encoding='utf-8')).hexdigest()
        sql = "select id,username,nickname,addTime from user where username='%s' and password='%s'" % (username, passwordMd5)
        self.cursor.execute(sql)
        data = self.cursor.fetchone()
        if data == None:
            self.error(1, '使用者名稱或者密碼錯誤')
            return True

        tmpSessionId = self.secretKey + str(data['id']) + str(data['addTime'])
        tmpSessionId = hashlib.md5(tmpSessionId.encode(encoding='utf-8')).hexdigest()
        try:
            sql = "update user set sessionId='%s' where id=%s" % (tmpSessionId, data['id'])
            self.cursor.execute(sql)
            self.conn.commit()
            data['sessionId'] = tmpSessionId
            self.success(data)
        except MySQLdb.Error as e:
            self.conn.rollback()
            self.error(1, '儲存會話id失敗')
            
class LogoutHandler(tornado.web.RequestHandler, BaseHandler):
    def get(self):
        sessionId = self.get_argument("sessionId", "")
        data = super().getloginuserinfo(sessionId)
        
        if data == None:
            self.success(None)
            return True
        
        if data['sessionId'] == '':
            self.success(data)
            return True

        try:
            sql = "update user set sessionId='' where sessionId='%s'" % sessionId
            self.cursor.execute(sql)
            self.conn.commit()
            data['sessionId'] = ''
            self.success(data)
        except MySQLdb.Error as e:
            self.conn.rollback()
            self.error(1, '刪除會話id失敗')
        
class GetuserinfoHandler(tornado.web.RequestHandler, BaseHandler):
    def get(self):
        sessionId = self.get_argument("sessionId", "")
        userinfo = super().getloginuserinfo(sessionId)
        self.success(userinfo)
        
class PostlistHandler(tornado.web.RequestHandler, BaseHandler):
    def get(self):
        page = self.get_argument("page", "1")
        keyword = self.get_argument("keyword", "")
        page = int(page)
        if page <= 0:
            page = 1
        addsql = " isDel=0 "
        if keyword is not None and keyword != '':
            addsql = " isDel=0 and title like '%"+keyword+"%' "
            
        start = (page - 1) * self.pagesize
        
        sql1 = "select count(1) as count from content where %s" % addsql
        self.cursor.execute(sql1)
        countdata = self.cursor.fetchone()
        totalpage = math.ceil(countdata['count'] / float(self.pagesize))
        
        data = []
        if totalpage > 0:
            sql2 = "select id,title,userId,userNickename,replyNum,updateTime from content where %s order by updateTime desc limit %s,%s" % (addsql, start, self.pagesize)
            self.cursor.execute(sql2)
            data = self.cursor.fetchall()
        
        self.success({'totalpage' : totalpage, 'data' : data})
        
class PostdetailHandler(tornado.web.RequestHandler, BaseHandler):
    def get(self):
        id = self.get_argument("id", "0")
        sql = "select id,title,content,userId,userNickename,replyNum,updateTime from content where isDel=0 and id=%s" % id
        self.cursor.execute(sql)
        data = self.cursor.fetchone()
        
        self.success(data)
        
class PostaddHandler(tornado.web.RequestHandler, BaseHandler):
    def get(self):
        title = self.get_argument("title", "")
        content = self.get_argument("content", "")
        sessionId = self.get_argument("sessionId", "")
        userinfo = super().getloginuserinfo(sessionId)
        userId = userinfo['id']
        userNickename = userinfo['nickname']
        
        if userId <= 0:
            self.error(1, '請先登入')
            return True

        try:
            sql = "insert into content(title, content, userId, userNickename) value('%s', '%s', %s, '%s')" % (title, content, userId, userNickename)
            self.cursor.execute(sql)
            self.conn.commit()
            insertId = self.cursor.lastrowid
            self.success(insertId)
        except MySQLdb.Error as e:
            self.conn.rollback()
            self.error(1, '發帖失敗')
        
class PosteditHandler(tornado.web.RequestHandler, BaseHandler):
    def get(self):
        id = self.get_argument("id", "0")
        title = self.get_argument("title", "")
        content = self.get_argument("content", "")
        sessionId = self.get_argument("sessionId", "")
        userinfo = super().getloginuserinfo(sessionId)
        userId = userinfo['id']
        userNickename = userinfo['nickname']
        
        if userId <= 0:
            self.error(1, '請先登入')
            return True

        try:
            sql = "update content set title='%s',content='%s',userId=%s,userNickename='%s' where id=%s and userId=%s" % (title, content, userId, userNickename, id, userId)
            self.cursor.execute(sql)
            self.conn.commit()
            self.success(None)
        except MySQLdb.Error as e:
            self.conn.rollback()
            self.error(1, '編輯帖子失敗')
        
class PostdeleteHandler(tornado.web.RequestHandler, BaseHandler):
    def get(self):
        id = self.get_argument("id", "0")
        sessionId = self.get_argument("sessionId", "")
        userinfo = super().getloginuserinfo(sessionId)
        userId = userinfo['id']
        userNickename = userinfo['nickname']
        
        if userId <= 0:
            self.error(1, '請先登入')
            return True

        try:
            sql = "update content set isDel=1 where id=%s and userId=%s" % (id, userId)
            self.cursor.execute(sql)
            self.conn.commit()
            self.success(None)
        except MySQLdb.Error as e:
            self.conn.rollback()
            self.error(1, '刪除帖子失敗')
        
class ReplylistHandler(tornado.web.RequestHandler, BaseHandler):
    def get(self):
        page = self.get_argument("page", "1")
        contentId = self.get_argument("contentId", "0")
        page = int(page)
        if page <= 0:
            page = 1
        start = (page - 1) * self.pagesize
        
        sql1 = "select count(1) as count from reply where isDel=0 and contentId=%s" % contentId
        self.cursor.execute(sql1)
        countdata = self.cursor.fetchone()
        totalpage = math.ceil(countdata['count'] / float(self.pagesize))
        
        data = []
        if totalpage > 0:
            sql2 = "select id,content,replyUserId,replyUserNickename,addTime from reply where isDel=0 and contentId=%s order by id asc limit %s,%s" % (contentId, start, self.pagesize)
            self.cursor.execute(sql2)
            data = self.cursor.fetchall()
        
        self.success({'totalpage' : totalpage, 'data' : data})
        
class ReplydetailHandler(tornado.web.RequestHandler, BaseHandler):
    def get(self):
        id = self.get_argument("id", "0")
        sql = "select id,content,replyUserId,replyUserNickename,addTime from reply where isDel=0 and id=%s" % id
        self.cursor.execute(sql)
        data = self.cursor.fetchone()
        
        self.success(data)
        
class ReplyaddHandler(tornado.web.RequestHandler, BaseHandler):
    def get(self):
        contentId = self.get_argument("contentId", "0")
        content = self.get_argument("content", "")
        sessionId = self.get_argument("sessionId", "")
        userinfo = super().getloginuserinfo(sessionId)
        userId = userinfo['id']
        userNickename = userinfo['nickname']
        
        if userId <= 0:
            self.error(1, '請先登入')
            return True

        try:
            sql2 = "update content set replyNum=replyNum+1 where id=%s" % contentId
            self.cursor.execute(sql2)
            sql1 = "insert into reply(contentId, content, replyUserId, replyUserNickename) value(%s, '%s', %s, '%s')" % (contentId, content, userId, userNickename)
            self.cursor.execute(sql1)
            
            self.conn.commit()
            insertId = self.cursor.lastrowid
            self.success(insertId)
        except MySQLdb.Error as e:
            self.conn.rollback()
            self.error(1, '回覆失敗')
        
class ReplyeditHandler(tornado.web.RequestHandler, BaseHandler):
    def get(self):
        id = self.get_argument("id", "0")
        content = self.get_argument("content", "")
        sessionId = self.get_argument("sessionId", "")
        userinfo = super().getloginuserinfo(sessionId)
        userId = userinfo['id']
        userNickename = userinfo['nickname']
        
        if userId <= 0:
            self.error(1, '請先登入')
            return True

        try:
            sql = "update reply set content='%s',replyUserId=%s,replyUserNickename='%s' where id=%s and replyUserId=%s" % (content, userId, userNickename, id, userId)
            self.cursor.execute(sql)
            self.conn.commit()
            self.success(None)
        except MySQLdb.Error as e:
            self.conn.rollback()
            self.error(1, '編輯回覆失敗')
        
class ReplydeleteHandler(tornado.web.RequestHandler, BaseHandler):
    def get(self):
        id = self.get_argument("id", "0")
        sessionId = self.get_argument("sessionId", "")
        userinfo = super().getloginuserinfo(sessionId)
        userId = userinfo['id']
        userNickename = userinfo['nickname']
        
        if userId <= 0:
            self.error(1, '請先登入')
            return True

        sql = "select id,content,replyUserId,replyUserNickename,addTime,contentId from reply where isDel=0 and id=%s" % id
        self.cursor.execute(sql)
        contentdata = self.cursor.fetchone()
        
        if contentdata is None:
            self.error(1, '回覆不存在')
            return True

        try:
            sql2 = "update content set replyNum=replyNum-1 where id=%s" % contentdata['contentId']
            self.cursor.execute(sql2)
            sql1 = "update reply set isDel=1 where id=%s and replyUserId=%s" % (id, userId)
            self.cursor.execute(sql1)
            
            self.conn.commit()
            self.success(None)
        except MySQLdb.Error as e:
            self.conn.rollback()
            self.error(1, '刪除回覆失敗')

if __name__ == "__main__":
    port = 1088
    define("port", default=port, help="run on the given port", type = int)
    print("python.tornado Server is running on port %d\n" % port)
    
    tornado.options.parse_command_line()
    app = tornado.web.Application(
        handlers=[
            (r"/", IndexHandler),
            (r"/user/register", RegisterHandler),
            (r"/user/login", LoginHandler),
            (r"/user/logout", LogoutHandler),
            (r"/user/getuserinfo", GetuserinfoHandler),
            (r"/post/list", PostlistHandler),
            (r"/post/detail", PostdetailHandler),
            (r"/post/add", PostaddHandler),
            (r"/post/edit", PosteditHandler),
            (r"/post/delete", PostdeleteHandler),
            (r"/reply/list", ReplylistHandler),
            (r"/reply/detail", ReplydetailHandler),
            (r"/reply/add", ReplyaddHandler),
            (r"/reply/edit", ReplyeditHandler),
            (r"/reply/delete", ReplydeleteHandler),
        ],
        cookie_secret="bZJc2sWbQLKos6GkHn/VB9oXwQt8S0R0kRvJ5/xJ89E=",
        xsrf_cookies=True,
        login_url="/xsrf",
        template_path=os.path.join(os.path.dirname(__file__), "templates"),
        static_path=os.path.join(os.path.dirname(__file__), "static"),
        debug=True
    )
    http_server = tornado.httpserver.HTTPServer(app)
    http_server.listen(options.port)
    tornado.ioloop.IOLoop.instance().start()

輸出:

D:\workspace\studys\study_pys\pc_app\dist>D:\software\Python310\python.exe D:\workspace\studys\study_bbs\start_web_tornado.py
python.tornado Server is running on port 1088

相關文章