仿优酷项目
项目三:仿优酷系统
一:项目需求
管理员
-
1 注册
-
2 登录
-
3 上传视频
-
4 删除视频
-
5 发布公告
用户
-
1 注册
-
2 登录
-
3 冲会员
-
4 查看视频
-
5 下载免费视频
-
6 下载收费视频
-
7 查看下载记录
-
8 查看公告
二:项目理解
1.什么是ORM:
对象-关系映射(Object/Relation Mapping,简称ORM)
2.为什么要有ORM
我们需要将数据存到数据库中,但是又需要通过数据来调用一些属性方法,通过rom我们能解决将数据转换成对象进行操作,操作完再将对象转换成数据存到数据库中。
理论上我们需要提前建立好数据库表,但是建立表我们依赖于数据,数据有哪些字段我们的表就需要建立哪些字段。因此建立表之前我们需要建立一个models层,models里面有user类,我们就建user表,几个类就建立几个表,表名就是通过类名来的。表有两个属性,一个是表名,一个是字段,表名就是对应的类名,类中的对象通过__class__
就能获得类,再__name__
就能获得类名,这样同时也能获得表名,类中的属性就相当于表中的字段,类的对象也包含了数据,就相当于表的记录。
演示映射关系:
类<------->表
对象<----.>记录
属性<---->字段
问题1:如何通过类来创建一张有字段的表?什么时候建立一张表?又如何将对象与记录之间进行相互转换(增删改查)
因此需要封装一套orm,对象和记录经过orm就能相互转换
当我们产生一个模型类的时候
问题2:orm框架的增删改成操作的是数据字典还是模型类对象?
是模型类对象
问题3:在设计orm时要用到哪些知识点?
1元类控制类的创建
2.面向对象三大特性:封装、继承、多态
3.自定义异常
4.re模块
问题4:实现带连接池的Mysql工具类
1.工具类之类方法
2.time模块
3.多线程
4.连接池概念
三:具体项目
客户端:
start.py
1 import sys 2 import os 3 4 sys.path.append(os.path.dirname(__file__)) 5 6 from core import src 7 8 if __name__ == '__main__': 9 src.run()
1 import socket 2 3 # socket客户端函数 4 def get_client(): 5 client = socket.socket() 6 client.connect( 7 ('127.0.0.1', 9981) 8 ) 9 return client
1 import json 2 import struct 3 import os 4 import hashlib 5 6 # 定义发送与接收数据的公共方法 7 def send_and_back(send_dic,client, file=None): 8 # 先序列化,得到json数据,并转成bytes数据 9 json_bytes = json.dumps(send_dic).encode('utf-8') 10 # 给bytes数据制作报头 11 headers = struct.pack('i', len(json_bytes)) 12 # 将打包好的数据发送给服务端 13 # 先发送报头 14 client.send(headers) 15 # 再发送真实数据(bytes数据) 16 client.send(json_bytes) 17 18 # 注意,若上传电影时调用此功能打开文件上传数据 19 if file: 20 # 以bytes模式打开电影文件,循环传入 21 with open(file, 'rb') as f: 22 for line in f: 23 client.send(line) 24 25 # 接收服务端返回的数据 26 # 先接收报头 27 headers = client.recv(4) 28 29 # 解压报头,获取真实数据的真实长度 30 # struct.unpack得到一个元组 31 bytes_len = struct.unpack('i', headers)[0] 32 33 34 35 # 通过长度接收真实数据并解码 36 json_bytes_data = client.recv(bytes_len).decode('utf-8') 37 38 # 反序列化得到真实数据字典 39 back_dic = json.loads(json_bytes_data) 40 41 # 将数据返回出去 42 return back_dic 43 44 45 # 定义一个获取md5值的方法 46 def get_movie_md5(movie_path): 47 # 获取电影大小 48 movie_size = os.path.getsize(movie_path) 49 50 # 指定位置截取字段生成MD5值 51 index_list = [0, movie_size//3, (movie_size//3)*2, movie_size-10] 52 53 # 获取md5加密对象 54 md5_obj = hashlib.md5() 55 56 # 先打开电影获取真实数据 57 with open(movie_path, 'rb') as f: 58 for line in index_list: 59 # 移动光标到指定位置 60 f.seek(line) 61 # 每个位置获取10个bytes数据 62 data = f.read(10) 63 # 每10个bytes 加一次密 64 md5_obj.update(data) 65 66 return md5_obj.hexdigest()
1 ''' 2 管理员视图 3 ''' 4 from lib import common 5 from conf import settings 6 import os 7 8 user_info = { 9 'cookies': None 10 } 11 12 13 # 管理员注册功能 14 def register(client): 15 while True: 16 # 输入用户名和密码 17 username = input('请输入用户名: ').strip() 18 password = input('请输入密码: ').strip() 19 re_password = input('请确认密码: ').strip() 20 # 校验两次密码是否相等 21 if password == re_password: 22 # 组织客户端的注册数据 23 send_dic = { 24 'username': username, 25 'password': password, 26 'user_type': 'admin', 27 'func_type': 'register' 28 } 29 # 将组织好的字典发送到服务端并接收返回结果 30 back_dic = common.send_and_back(send_dic, client) 31 32 # 判断服务端返回来的字典信息内是否有注册成功,并打印消息 33 print('运行测试34') 34 if back_dic.get('flag'): 35 print(back_dic.get('msg')) 36 break 37 else: 38 print(back_dic.get('msg')) 39 40 41 # 管理员登陆功能 42 def login(client): 43 while True: 44 username = input('请输入用户名: ').strip() 45 password = input('请输入密码: ').strip() 46 # 1.组织数据发送给服务端 47 send_dic = { 48 'username': username, 49 'password': password, 50 'func_type': 'login' 51 } 52 # 接收服务端返回的消息 53 back_dic = common.send_and_back(send_dic, client) 54 if back_dic.get('flag'): 55 print(back_dic.get('msg')) 56 user_info['cookies'] = back_dic.get('session') 57 break 58 59 else: 60 print(back_dic.get('msg')) 61 62 63 # 管理员上传电影 64 def upload_movie(client): 65 while True: 66 # 获取电影文件夹位置 67 upload_movie_dir = settings.UPLOAD_MOVIE_DIR 68 # 打印目录所有的电影文件,让用户选择 69 movie_list = os.listdir(upload_movie_dir) 70 71 # 若没有可上传的电影 72 if not movie_list: 73 print('没有可上传的电影!!!') 74 break 75 # 获取电影编码和名字 76 for index, movie_name in enumerate(movie_list): 77 print(index, movie_name) 78 print('选择q可以退出') 79 choice = input('请输入上传电影编号: ').strip() 80 if choice == 'q': 81 break 82 if not choice.isdigit(): 83 continue 84 choice = int(choice) 85 if choice not in range(len(movie_list)): 86 continue 87 # 获取到电影名 88 movie_name = movie_list[choice] 89 # 获取真实的电影数据 90 movie_path = os.path.join(upload_movie_dir, movie_name) 91 # 组织需要上传的电影信息发送给服务端 92 # 获取电影大小 93 movie_size = os.path.getsize(movie_path) 94 # 获取电影md5值,发送给服务端,校验电影是否存在 95 movie_md5 = common.get_movie_md5(movie_path) 96 # 发送检测电影是否存在数据给服务端 97 send_dic = { 98 'func_type': 'check_movie', 99 'movie_md5': movie_md5, 100 'cookies': user_info.get('cookies') 101 } 102 # 获取返回的消息 103 back_dic = common.send_and_back(send_dic, client) 104 105 # 校验back_dic中返回的消息,若电影不存在则发送上传电影功能申请 106 if back_dic.get('flag'): 107 # 管理员设置上传的电影是否免费 108 print('电影收费选择y,免费选择n') 109 choice2 = input('请确认电影是否收费: ').strip() 110 is_free = 1 111 # 若输入Y,将is_free修改成0 112 if choice2 == 'y': 113 is_free = 0 114 send_dic = { 115 'func_type': 'upload_movie', 116 'movie_name': movie_name, 117 'movie_size': movie_size, 118 'movie_md5': movie_md5, 119 'cookies':user_info.get('cookies'), 120 'is_free': is_free 121 } 122 # 将信息发送过去 123 back_dic2 = common.send_and_back(send_dic, client, file=movie_path) 124 if back_dic2.get('flag'): 125 print(back_dic2.get('msg')) 126 break 127 else: 128 print(back_dic.get('msg')) 129 130 131 # 管理员删除电影 132 def delete_movie(client): 133 while True: 134 # 1.先去服务端获取可删除的电影(注意携带cookies) 135 send_dic = { 136 'func_type': 'get_movie_list', 137 'cookies': user_info.get('cookies'), 138 'movie_type': 'all' 139 } 140 # 获取返回的消息 141 back_dic = common.send_and_back(send_dic, client) 142 # 若没有电影则退出 143 if not back_dic.get('flag'): 144 print(back_dic.get('msg')) 145 break 146 # 若有则打印并选择可删除的电影 147 movie_list = back_dic.get('movie_list') 148 for index, movie_name_id in enumerate(movie_list): 149 print(index, movie_name_id) 150 choice = input('请需要删除的电影编号: ').strip() 151 if choice == 'q': 152 break 153 if not choice.isdigit(): 154 continue 155 choice = int(choice) 156 157 if choice not in range(len(movie_list)): 158 continue 159 # 3.发送删除的请求给服务端,让服务端修改电影的is_delete字段即可 160 # movie_name_id ---> [电影名字, 电影id] 161 movie_name_id = movie_list[choice] 162 # 制作信息 163 send_dic = { 164 'func_type': 'delete_movie', 165 'cookies': user_info.get('cookies'), 166 'movie_id': movie_name_id[1] # 传入唯一的电影id 167 } 168 # 获取返回的结果 169 back_dic2 = common.send_and_back(send_dic, client) 170 if back_dic2.get('flag'): 171 print(back_dic2.get('msg')) 172 break 173 174 # 管理员发布公告 175 def send_notice(client): 176 177 # 1.让管理员输入 公告标题与内容 178 title = input('请输入公告标题:').strip() 179 180 # 2.发送数据给服务端保存公告即可 181 content = input('请输入公告内容:').strip() 182 183 send_dic = { 184 'cookies': user_info.get('cookies'), 185 'title': title, 186 'content': content, 187 'func_type': 'send_notice' 188 } 189 190 back_dic = common.send_and_back(send_dic, client) 191 print(back_dic.get('msg')) 192 193 194 195 196 197 func_dic = { 198 '1': register, 199 '2': login, 200 '3': upload_movie, 201 '4': delete_movie, 202 '5': send_notice, 203 } 204 205 def admin_view(client): 206 while True: 207 print(''' 208 1.注册 209 2.登陆 210 3.上传视频 211 4.删除视频 212 5.发布公告 213 q.退出 214 ''') 215 choice = input('请输入功能编号: ').strip() 216 if choice == 'q': 217 break 218 if choice not in func_dic: 219 continue 220 func_dic.get(choice)(client)
1 from tcp_client import socket_client 2 from core import admin 3 from core import user 4 5 6 func_dic = { 7 '1': admin.admin_view, 8 '2': user.user_view 9 } 10 11 # 总视图run函数 12 def run(): 13 print('Client is running...') 14 # 获得client套接字 15 client = socket_client.get_client() 16 while True: 17 print(''' 18 1.管理员视图 19 2.普通用户视图 20 q.退出 21 ''') 22 23 # 开始 输入选择功能编号 24 choice = input('请输入功能编号: ').strip() 25 if choice == 'q': 26 break 27 if choice not in func_dic: 28 continue 29 func_dic.get(choice)(client) 30 31 # 关闭通道 32 client.close()
1 ''' 2 用户视图 3 ''' 4 from lib import common 5 import time 6 import os 7 from conf import settings 8 9 user_info = { 10 'cookies': None, 11 'is_vip': 0 # 默认登录前不是VIP 12 } 13 14 15 # 普通用户注册视图(和管理员一样) 16 def register(client): 17 while True: 18 username = input('请输入用户名: ').strip() 19 password = input('请输入密码: ').strip() 20 re_password = input('请确认密码: ').strip() 21 if password == re_password: 22 # 一 组织客户端的注册数据 23 send_dic = { 24 'username': username, 25 'password': password, 26 'user_type': 'user', 27 'func_type': 'register' 28 } 29 30 back_dic = common.send_and_back(send_dic, client) 31 32 # 判断服务端返回来的字典是否注册成功,并打印消息 33 if back_dic.get('flag'): 34 print(back_dic.get('msg')) 35 break 36 37 else: 38 print(back_dic.get('msg')) 39 40 41 # 普通用户登录 42 def login(client): 43 while True: 44 username = input('请输入用户名: ').strip() 45 password = input('请输入密码: ').strip() 46 47 # 1.组织数据发送给服务端 48 send_dic = { 49 'username': username, 50 'password': password, 51 'func_type': 'login' 52 } 53 54 # 调用commin中的send_and_back与服务端交互 55 back_dic = common.send_and_back(send_dic, client) 56 57 if back_dic.get('flag'): 58 print(back_dic.get('msg')) 59 # 修改cookies值 60 user_info['cookies'] = back_dic.get('session') 61 # 修改is_vip值 62 is_vip = back_dic.get('is_vip') 63 if is_vip: 64 user_info['is_vip'] = is_vip 65 break 66 67 else: 68 print(back_dic.get('msg')) 69 70 71 # 普通用户充会员 72 def buy_vip(client): 73 while True: 74 # 判断当前用户是不是会员 75 if not user_info.get('is_vip'): 76 choice = input('请输入y 或者 n 确认是否购买价值5000元的黑金会员: ').strip() 77 if choice not in ['y', 'n']: 78 print('输入错误') 79 break 80 if choice == 'y': 81 # 制作消息字典给服务端修改数据is_vip字段 82 send_dic = { 83 'func_type': 'buy_vip', 84 'cookies': user_info.get('cookies') 85 } 86 back_dic = common.send_and_back(send_dic, client) 87 if back_dic.get('flag'): 88 print(back_dic.get('msg')) 89 print('请重新登陆跟新数据') 90 break 91 else: 92 print(back_dic.get('msg')) 93 break 94 else: 95 print('请努力赚钱,欢迎下次光临') 96 break 97 else: 98 print('您已经是尊贵的黑金会员了!!!') 99 break 100 101 102 # 普通用户查看电影 103 def check_movies(client): 104 # 1.先去服务端获取可删除的电影 105 send_dic = { 106 'func_type': 'get_movie_list', 107 'movie_type': 'all', 108 'cookies': user_info.get('cookies') 109 } 110 111 back_dic = common.send_and_back(send_dic, client) 112 # 直接打印消息就可以 113 if back_dic.get('flag'): 114 print(back_dic.get('movie_list')) 115 116 117 # 普通用户下载免费电影 118 def download_free_movie(client): 119 while True: 120 # 先去服务端获取所有免费的电影 121 send_dic = { 122 'func_type': 'get_movie_list', 123 'movie_type': 'free', 124 'cookies': user_info.get('cookies') 125 } 126 127 # 获取返回结果 128 back_dic = common.send_and_back(send_dic, client) 129 130 # 若服务端没有电影 131 if not back_dic.get('flag'): 132 print(back_dic.get('msg')) 133 break 134 # 获取电影列表 135 movie_list = back_dic.get('movie_list') 136 # 若服务端有电影但是没有免费电影 137 if not movie_list: 138 print('没有可下载的免费电影') 139 break 140 141 # 否则打印所有的免费电影并让用户选择 142 for index, name_id_type in enumerate(movie_list): 143 print(index, name_id_type) 144 145 # 输入需要下载的电影编号 146 choice = input('请输入下载电影的编号: ').strip() 147 if not choice.isdigit(): 148 print('请输入正确编号!!') 149 continue 150 151 choice = int(choice) 152 153 if choice not in range(len(movie_list)): 154 print('请输入正确编号!!') 155 continue 156 157 movie_name, movie_id, movie_type = movie_list[choice] 158 # 可查看一下打印的内容 159 print(movie_name, movie_id, movie_type) 160 161 # 选择成功后,制作下载免费电影的请求给服务端 162 send_dic2 = { 163 'func_type': 'download_movie', 164 'movie_name': movie_name, 165 'movie_id': movie_id, 166 'cookies': user_info.get('cookies') 167 } 168 back_dic2 = common.send_and_back(send_dic2, client) 169 170 # 免费电影可设置广告时间,模拟广告等待 171 wait_time = back_dic2.get('wait_time') 172 if wait_time: 173 print('广告时间:{你想学编程吗?你想月入百万吗?你想迎娶白富美吗,欢迎来上海老男孩全球IT技术培训集团') 174 time.sleep(wait_time) 175 176 # 接收服务端返回来的下载电影字典 177 movie_size = back_dic2.get('movie_size') 178 179 # 接收电影真实数据,并保存到download_movie_dir中 180 movie_path = os.path.join( 181 settings.DOWNLOAD_MOVIE_DIR, movie_name 182 ) 183 # 开始正式接收电影 184 recv_data = 0 185 with open(movie_path, 'wb') as f: 186 while recv_data < movie_size: 187 data = client.recv(1024) 188 f.write(data) 189 recv_data += len(data) 190 print('电影下载成功') 191 break 192 193 194 # 普通用户下载收费电影 195 def download_buy_movie(client): 196 while True: 197 # 先去服务端获取所有收费的电影 198 send_dic = { 199 'func_type': 'get_movie_list', 200 'movie_type': 'pay', 201 'cookies': user_info.get('cookies') 202 } 203 # 获取返回结果 204 back_dic = common.send_and_back(send_dic, client) 205 206 # 若服务端没有电影 207 if not back_dic.get('flag'): 208 print(back_dic.get('msg')) 209 break 210 # 获取电影列表 211 movie_list = back_dic.get('movie_list') 212 # 若服务端有电影但是没有免费电影 213 if not movie_list: 214 print('没有可下载的收费电影') 215 break 216 217 # 否则打印所有的收费电影并让用户选择 218 for index, name_id_type in enumerate(movie_list): 219 print(index, name_id_type) 220 221 # 设置电影收费项目,缴费才能选择电影 222 print('收费电影:普通用户100元一部,尊贵的黑金用户50元一部,确认购买请按"y","退出任意键') 223 choice_buy = input('请输入: ').strip() 224 if choice_buy == 'y': 225 if user_info.get('is_vip'): 226 print('尊贵的会员恭喜您付费成功!') 227 else: 228 print('恭喜您付费成功') 229 230 print('现在请开始选则您喜欢的电影') 231 else: 232 print('欢迎下次光临') 233 break 234 # 输入需要下载的电影编号 235 choice = input('请输入下载电影的编号: ').strip() 236 if not choice.isdigit(): 237 print('请输入正确编号!!') 238 continue 239 240 choice = int(choice) 241 242 if choice not in range(len(movie_list)): 243 print('请输入正确编号!!') 244 continue 245 movie_name, movie_id, movie_type = movie_list[choice] 246 # 可查看一下打印的内容 247 print(movie_name, movie_id, movie_type) 248 249 # 选择成功后,制作下载收费电影的请求给服务端 250 send_dic2 = { 251 'func_type': 'download_movie', 252 'movie_name': movie_name, 253 'movie_id': movie_id, 254 'cookies': user_info.get('cookies') 255 } 256 back_dic2 = common.send_and_back(send_dic2, client) 257 258 # 收费电影可不设置广告时间 259 # 接收服务端返回来的下载电影字典 260 movie_size = back_dic2.get('movie_size') 261 # 接收电影真实数据,并保存到download_movie_dir中 262 movie_path = os.path.join( 263 settings.DOWNLOAD_MOVIE_DIR, movie_name 264 ) 265 # 开始正式接收电影 266 recv_data = 0 267 with open(movie_path, 'wb') as f: 268 while recv_data < movie_size: 269 data = client.recv(1024) 270 f.write(data) 271 recv_data += len(data) 272 print('恭喜您下载电影成功') 273 break 274 275 276 # 普通用户查看下载记录 277 def check_movie_record(client): 278 # 制作想要查看下载记录的信息 279 send_dic = { 280 'func_type': 'check_movie_record', 281 'cookies': user_info.get('cookies') 282 } 283 back_dic = common.send_and_back(send_dic, client) 284 print(back_dic) 285 286 if back_dic.get('flag'): 287 print(back_dic.get('download_movie_list')) 288 else: 289 print(back_dic.get('msg')) 290 291 292 # 普通用户查看公告 293 def check_notice(client): 294 # 制作发送给服务端查看公告的请求 295 send_dic = { 296 'func_type': 'check_notice', 297 'cookies': user_info.get('cookies') 298 } 299 # 接收服务端信息 300 back_dic = common.send_and_back(send_dic, client) 301 302 if back_dic.get('flag'): 303 print(back_dic.get('notice_list')) 304 else: 305 print(back_dic.get('msg')) 306 307 308 func_dic = { 309 '1': register, 310 '2': login, 311 '3': buy_vip, 312 '4': check_movies, 313 '5': download_free_movie, 314 '6': download_buy_movie, 315 '7': check_movie_record, 316 '8': check_notice 317 } 318 319 320 def user_view(client): 321 while True: 322 print(''' 323 1 注册 324 2 登录 325 3 冲会员 326 4 查看视频 327 5 下载免费视频 328 6 下载收费视频 329 7 查看下载记录 330 8 查看公告 331 q.退出 332 ''') 333 334 choice = input('请输入功能编号:').strip() 335 if choice == 'q': 336 break 337 338 if choice not in func_dic: 339 continue 340 341 func_dic.get(choice)(client)
1 import os 2 3 BASE_PATH = os.path.dirname(os.path.dirname(__file__)) 4 5 UPLOAD_MOVIE_DIR = os.path.join( 6 BASE_PATH, 'upload_movie_dir' 7 ) 8 9 DOWNLOAD_MOVIE_DIR = os.path.join( 10 BASE_PATH, 'download_movie_dir' 11 )
1 import sys 2 import os 3 sys.path.append( 4 os.path.dirname(__file__) 5 ) 6 7 from tcp_server import socket_server 8 if __name__ == '__main__': 9 print('Server in running...') 10 11 socket_server.run()
1 import socket 2 from concurrent.futures import ThreadPoolExecutor 3 from db import user_data 4 5 from interface import common_interface 6 7 from interface import admin_interface 8 from interface import user_interface 9 from threading import Lock 10 import struct 11 import json 12 13 14 pool = ThreadPoolExecutor(50) 15 mutex = Lock() 16 17 # 程序接口字典 18 19 func_dic = { 20 # 注册接口 21 'register': common_interface.register_interface, 22 # 登陆接口 23 'login': common_interface.login_interface, 24 # 检测电影接口 25 'check_movie': admin_interface.check_movie_interface, 26 # 上传电影接口 27 'upload_movie': admin_interface.upload_movie_interface, 28 # 获取未删除电影接口 29 'get_movie_list': common_interface.get_movie_list_interface, 30 # 删除电影接口 31 'delete_movie': admin_interface.delete_movie_interface, 32 # 发布公告接口 33 'send_notice': admin_interface.send_notice_interface, 34 # 购买会员接口 35 'buy_vip': user_interface.buy_vip_interface, 36 # 下载电影接口 37 'download_movie': common_interface.download_free_movie_interface, 38 # 查看下载记录接口 39 'check_movie_record': user_interface.check_movie_record_interface, 40 # 查看公告接口 41 'check_notice': user_interface.check_notice_interface 42 } 43 44 45 # 定义任务函数 46 def working(conn, addr): 47 48 while True: 49 # 获取报错信息并返回 50 try: 51 # 1.先接收报头 52 headers = conn.recv(4) 53 # 解压报头,获取真实数据长度 54 # struct.unpack得到一个元组 55 bytes_len = struct.unpack('i', headers)[0] 56 # 根据真实长度获取真实数据(json数据) 57 json_bytes_data = conn.recv(bytes_len).decode('utf-8') 58 # 反序列化得到真实数据字典 59 back_dic = json.loads(json_bytes_data) 60 # 保证每个客户端发过来都是唯一,将addr添加到客户端发送过来的字典中 61 back_dic['addr'] = str(addr) 62 # 获得客户端发送过来的执行程序的类型 63 func_type = back_dic.get('func_type') 64 # 查看此程序类型是否在程序接口字典中(建立一个程序接口字典) 65 if func_type in func_dic: 66 # 如果有执行此接口,并将客户端传来的信息字典和conn传过去 67 func_dic.get(func_type)(back_dic, conn) 68 69 70 # 捕获错误信息给e 71 except Exception as e: 72 print(e) 73 # 上锁 74 mutex.acquire() 75 # 清除当前客户端存放在服务端的session值 76 user_data.user_online.pop(str(addr)) 77 # 解锁 78 mutex.release() 79 break 80 conn.close() 81 82 83 # 实现并发 84 def run(): 85 # 获取socket套接字 86 server = socket.socket() 87 # 绑定ip和端口 88 server.bind(('127.0.0.1', 9981)) 89 # 设置半连接池 90 server.listen(50) 91 92 while True: 93 # 链接获取通道和地址 94 conn, addr = server.accept() 95 # 异步提交working任务(开启五十个线程) 96 pool.submit(working, conn, addr)
1 import pymysql 2 from orm_control.mysql_pool import POOL 3 4 5 # MySQL连接类,链接数据库 6 class MySQL: 7 __instance = None 8 # 使用单例模式 9 @classmethod 10 def singleton(cls): 11 if not cls.__instance: 12 cls.__instance = cls() 13 14 return cls.__instance 15 16 # 实例化MySQL类时,获取数据库链接对象,获取游标对象 17 def __init__(self): 18 # 通过数据库连接池获取数据库链接对象 19 self.mysql_client = POOL.connection() 20 # 获取数据库游标对象并将结果以列表套字典的形式返回 21 self.cursor = self.mysql_client.cursor( 22 pymysql.cursors.DictCursor 23 ) 24 25 26 # 自定义查询方法 27 def select(self, sql, args=None): 28 # 提交sql语句 29 self.cursor.execute(sql, args) 30 # 获取返回的结果 31 res = self.cursor.fetchall() 32 return res 33 34 # 自定义提交sql语句方法,比如:insert,undate 35 def execute(self, sql, args): 36 # 提交sql语句 37 try: 38 self.cursor.execute(sql, args) 39 except Exception as e: 40 print(e) 41 42 # 关闭数据库及游标 43 def close(self): 44 self.cursor.close() 45 # 再关闭数据库连接 46 self.mysql_client.close()
1 from DBUtils.PooledDB import PooledDB 2 import pymysql 3 ''' 4 数据库连接池 5 ''' 6 POOL = PooledDB( 7 creator=pymysql, # 使用链接数据库的模块 8 maxconnections=6, # 连接池允许的最大连接数,0和None表示不限制连接数 9 mincached=2, # 初始化时,链接池中至少创建的空闲的链接,0表示不创建 10 maxcached=5, # 链接池中最多闲置的链接,0和None不限制 11 maxshared=3, 12 # 链接池中最多共享的链接数量,0和None表示全部共享。 13 # PS: 无用,因为pymysql和MySQLdb等模块的 threadsafety都为1,所有值无论设置为多少,_maxcached永远为0,所以永远是所有链接都共享。 14 blocking=True, # 连接池中如果没有可用连接后,是否阻塞等待。True,等待;False,不等待然后报错 15 maxusage=None, # 一个链接最多被重复使用的次数,None表示无限制 16 setsession=[], # 开始会话前执行的命令列表。如:["set datestyle to ...", "set time zone ..."] 17 ping=0, 18 # ping MySQL服务端,检查是否服务可用。# 如:0 = None = never, 1 = default = whenever it is requested, 2 = when a cursor is created, 4 = when a query is executed, 7 = always 19 host='127.0.0.1', 20 port=3306, 21 user='root', 22 password='960617', 23 database='orm_demo', 24 charset='utf8', 25 autocommit=True 26 )
1 ''' 2 ORM: 对象关系映射 3 将对象 映射成 数据表中的一条条记录 4 5 类 ----> 表名 6 对象 ----> 记录 7 对象.属性 ----> 字段 8 ''' 9 from orm_control.mysql_control import MySQL 10 11 12 # 建立一个字段类型的父类 13 class Field: 14 def __init__(self, name, 15 column_type, 16 primary_key, 17 default): 18 self.name = name 19 self.column_type = column_type 20 self.primary_key = primary_key 21 self.default = default 22 23 24 # 字段类型为字符串的 25 class StringField(Field): 26 def __init__(self, name, 27 column_type='varchar(64)', 28 primary_key=False, 29 default=None): 30 super().__init__(name, column_type, primary_key, default) 31 32 33 # 字段类型为整型的 34 class IntegerField(Field): 35 def __init__(self, name, column_type='int', 36 primary_key=False, 37 default=0): 38 super().__init__(name, column_type, primary_key, default) 39 40 41 # 自定义元类,控制表类的创建 42 # 1.保证一张表必须要有一个表名, 43 # 2,一张表只能有一个主键, 44 # 3.将所有的'字段名'与'字段对象'添加到一个独立的字典中 45 # 以key(字段名):value(字段对象),添加到类的名称空间中,方便后期使用 46 47 class OrmMetaClass(type): 48 49 def __new__(cls, class_name, class_bases, class_attr): 50 # 参数为继承元类的类名,该类的父类,该类的名称空间 51 # 将Models类过滤掉 52 if class_name == 'Models': 53 # 将models类的类名,基类和民称空间原路返回 54 return type.__new__(cls, class_name, class_bases, class_attr) 55 56 # 获取table表名,若自定义则获取,没有则默认使用类名 57 # dict.get(key) ---> key若有则返回对应的值,若没有则返回默认值 class_name就是默认值 58 # 将类名当作表名 59 table_name = class_attr.get('table_name', class_name) 60 # 先设置默认主键值 61 primary_key = None 62 # 设置存放字段名与字段对象的字典 63 mappings = {} 64 65 # 保证一张表只能有一个主键 66 # 循环遍历类的名称空间 67 for k, v in class_attr.items(): 68 # 将字段以外的属性过滤 69 # 判断当前的V是否是字段对象 70 if isinstance(v, Field): 71 # 如果是则添加到字典中 72 mappings[k] = v 73 74 # 注意:字典被迭代时不能修改其属性 75 # class_attr.pop(k) 76 # 判断字段对象如果有主键,则为primary_key变量赋值 77 if v.primary_key: 78 # 再次判断primary_key是否有值,如果有则证明主键已有,抛出异常 79 if primary_key: 80 raise TypeError('一张表只能有一个主键') 81 # 赋值 82 primary_key = v.name 83 # 过滤掉类名称空间中重复的字段属性 84 for key in mappings.keys(): 85 class_attr.pop(key) 86 87 # 检测一遍是否有主键 88 89 if not primary_key: 90 raise TypeError('一张表必须要有一个主键') 91 # 给类的名称空间添加表名,主键和属性 92 class_attr['table_name'] = table_name 93 class_attr['primary_key'] = primary_key 94 class_attr['mappings'] = mappings 95 # 将类的名称空间返回出去 96 return type.__new__(cls, class_name, class_bases, class_attr) 97 98 99 # 定义一个类继承字典类,并且定义获取属性的方法 100 101 class Models(dict, metaclass=OrmMetaClass): 102 103 # 对象.属性,属性没有时触发 104 def __getattr__(self, item): 105 return self.get(item) 106 107 # 对象.属性 = 属性值时触发 108 def __setattr__(self, key, value): 109 self[key] = value 110 111 # 查询数据的方法(定义成类方法) 112 @classmethod 113 def select_data(cls, **kwargs): 114 # 定义一个MySQL类 115 # 获取链接数据库类的对象 116 mysql_obj = MySQL() 117 filed_value = None 118 119 # 若kwargs为False代表没有查询条件 120 if not kwargs: 121 # 查所有数据 122 sql = f'select * from {cls.table_name}' 123 else: 124 # 根据条件查找 125 # 获取字段名 126 filed_name = list(kwargs.keys())[0] 127 print(filed_name) 128 129 # 获取字段值 130 filed_value = kwargs.get(filed_name) 131 132 # 根据条件查询 133 # select * from 表名 where 字段名=字段值; 134 sql = f'select * from {cls.table_name} where {filed_name}=?' 135 sql = sql.replace('?', '%s') 136 res = mysql_obj.select(sql, filed_value) # res若为空则已注册 137 # 返回查询结果 138 return [cls(**r) for r in res] 139 140 # 插入数据 141 def insert_data(self): 142 mysql_obj = MySQL() 143 144 # 存放字段名的列表 145 filed_names = [] # [字段名, 字段名, 字段名, ] 146 147 # 存放字段值的列表 148 filed_values = [] # [字段值, 字段值,字段值,] 149 150 # 设置一个替换值的列表 151 replace_list = [] # [?, ?] 152 153 # 获取民粹空间中所有的数据 154 for k, v in self.mappings.items(): 155 # 获取字段名,追加到列表中 156 filed_names.append(v.name) 157 res = self.get(v.name, v.default) 158 # print(f'{v.name}', res) 159 filed_values.append( 160 # 反射: 根据字符串操作对象中的属性或方法 161 res 162 ) 163 replace_list.append('?') 164 # # sql = f'insert into {self.table_name}({",".join(filed_names)}) values({",".join(replace_list)})' 165 # 组织sql语句 166 sql = 'insert into %s(%s) values(%s)' % ( 167 self.table_name, ",".join(filed_names), ",".join(replace_list) 168 ) 169 sql = sql.replace('?', '%s') 170 # 调用mysql语句执行方法 171 mysql_obj.execute(sql, filed_values) 172 173 # 跟新数据 174 def update_data(self): 175 mysql_obj = MySQL() 176 # 主键值 177 pk = None 178 # 存放字段名的列表 179 filed_names = [] # [字段名, 字段名, 字段名, ] 180 181 # 存放字段值的列表 182 filed_values = [] # [字段值, 字段值,字段值,] 183 184 for k, v in self.mappings.items(): 185 # 判断mappings中哪一个字段是主键 186 if v.primary_key: 187 # 获取主键的值 188 pk = self.get(v.name) 189 190 else: 191 # 添加字段名 192 filed_names.append(v.name + '=?') 193 194 # 添加字段值 195 filed_values.append( 196 self.get(v.name, v.default) 197 ) 198 # sql: update User set 字段名=字段值, 字段名=字段值 199 # sql: update User set username=?, password=? where pk=1; 200 sql = 'update %s set %s where %s=%s' % ( 201 self.table_name, 202 ','.join(filed_names), 203 self.primary_key, 204 pk 205 ) 206 sql = sql.replace('?', '%s') 207 mysql_obj.execute(sql, filed_values)
1 import struct 2 import json 3 import hashlib 4 import uuid 5 from db.user_data import user_online 6 from threading import Lock 7 8 mutex = Lock() 9 10 11 # 服务端发送消息给客户端的公共方法 12 def send_data(back_dic, conn, file=None): 13 14 # 1序列化得到json的bytes数据 15 bytes_data = json.dumps(back_dic).encode('utf-8') 16 17 18 19 20 # 制作报头 21 headers = struct.pack('i', len(bytes_data)) 22 23 24 25 26 # 先发送报头 27 28 conn.send(headers) 29 30 31 # 再发送真实数据 32 conn.send(bytes_data) 33 34 if file: 35 # 循环发送 36 with open(file, 'rb') as f: 37 for line in f: 38 conn.send(line) 39 40 41 # 密码加密 42 def get_md5(pwd): 43 md5_obj = hashlib.md5() 44 md5_obj.update(pwd.encode('utf-8')) 45 # 加盐 46 salt = '你个丑比还想盗劳资的信息' 47 md5_obj.update(salt.encode('utf-8')) 48 return md5_obj.hexdigest() 49 50 51 # 产生随机的session字符串 52 def get_session(): 53 uuid_str = str(uuid.uuid4()) 54 session_val = get_md5(uuid_str) 55 return session_val 56 57 58 # 登陆认证装饰器 59 def login_auth(func): 60 def inner(*args, **kwargs): # args = (back_dic, conn) 61 # 校验用户是否登陆:校验session是否一致 62 # back_dic.get('cookies') 63 client_session = args[0].get('cookies') 64 print(client_session) 65 addr = args[0].get('addr') 66 # 上锁: 67 mutex.acquire() 68 # 获取服务端中存储的addr信息 69 session_id_list = user_online.get(addr) 70 # 解锁 71 mutex.release() 72 # 判断是否存在信息 73 if session_id_list: 74 print('测试63',session_id_list[0]) 75 # 判断是否相等 76 if client_session == session_id_list[0]: 77 # 若相等则代表已登录,则将用户id添加到客户端发送过来的字典中 78 args[0]['user_id'] = session_id_list[1] 79 # 运行主函数 80 res = func(*args, **kwargs) 81 return res 82 else: 83 send_dic = { 84 'flag': False, 'msg': '携带的session值错误,没有执行权限' 85 } 86 send_data(send_dic, args[1]) 87 88 return inner
admin_interface.py
common_interface.py
user_interface
1 from orm_control.orm import Models 2 3 from orm_control.orm import IntegerField 4 5 from orm_control.orm import StringField 6 7 # 用户表类: id、用户名、密码、用户类型、是否为VIP、注册时间 字段 8 class User(Models): 9 u_id = IntegerField(name='u_id', primary_key=True) # u_id = integerField_obj 10 username = StringField(name='username') 11 password = StringField(name='password') 12 user_type = StringField(name='user_type') # admin, user 13 # 0: 默认不是vip, 1: 是vip 14 is_vip = IntegerField(name='is_vip') 15 register_time = StringField(name='register_time') 16 17 18 # 电影表类: id、电影名字、电影大小、电影md5值、电影是否免费、电影是否删除、上传时间、上传用户的id 19 class Movie(Models): 20 m_id = IntegerField(name='m_id', primary_key=True) 21 movie_name = StringField(name='movie_name') 22 movie_size = StringField(name='movie_size') 23 movie_md5 = StringField(name='movie_md5') 24 # 0: 收费 1:免费 25 is_free = IntegerField(name='is_free') 26 # 0:未删除 1: 已删除 27 is_delete = IntegerField(name='is_delete') 28 upload_time = StringField(name='upload_time') 29 user_id = IntegerField(name='user_id') 30 31 # 公告表类: id、公告标题, 公告内容、发布时间、发布用户id 32 class Notice(Models): 33 n_id = IntegerField(name='n_id', primary_key=True) 34 title = StringField(name='title') 35 content = StringField(name='content') 36 create_time = StringField(name='create_time') 37 user_id = IntegerField(name='user_id') 38 39 40 # 下载记录表: id、下载电影的id、下载用户的id、下载时间 41 class DownloadRecord(Models): 42 d_id = IntegerField(name='d_id', primary_key=True) 43 movie_id = IntegerField(name='movie_id') 44 user_id = IntegerField(name='user_id') 45 download_time = StringField(name='download_time')
1 user_data.py 2 user_online = {}
1 import os 2 3 BASE_PATH = os.path.dirname( 4 os.path.dirname(__file__) 5 ) 6 7 MOVIE_DIR = os.path.join(BASE_PATH, 'movie_dir')