开发环境是Ubuntu系统下python3.5,数据库为mysql,与数据库交互的包为pymysql。
先写一个动态资源服务器,详情见
动态资源服务器初级
然后更新应用程序框架用装饰器实现(模拟FLASK框架),再设计一个类shsp,类功能实现与数据库交互并返回想要的结果
之后是代码实现。
数据库设计完然后写入要展示的商品数据,这里不展示。已打包上传
中间的网页内容具体不显示,具体已打包上传
import gevent
from gevent import monkey
import sys
import socket
import chardet
'''这个程序实现的是一个动态资源服务器'''
gevent.monkey.patch_all() # 给所有耗时操作打补丁
class Server(object):
def __init__(self, port, app):
'''初始化,实现逻辑'''
self.app = app
# 创建服务器的套接字
self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server_socket.setsockopt(
socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.server_socket.bind(('', port))
self.response_data = None # 这个是响应报文的数据(除了响应体之外)
def start(self):
# 服务器开始运行
self.server_socket.listen()
while True:
handler_socket, address = self.server_socket.accept()
# handler_prc = threading.Thread(
# target=self.handler_client, args=(handler_socket,))
# handler_prc.start()
gv = gevent.spawn(self.handler_client, handler_socket)
gv.join()
def start_response(self, status, header_list):
'''
这个函数实现的是拼接响应报文,遵循的还是响应报文的格式,请求动态资源调用
:param status: 状态码,例如'200 ok'
:param header_list: 列表 例如[(server,wsgisever),(name,xiaoming)]
:return:
HTTP/1.1 状态码 说明\r\n
Headername1:header_value\r\n
Headername2:header_value\r\n
Headername3:header_value\r\n
\r\n
Response_body 响应体,就是数据,网页、图片、文本
'''
response_header_firstline = 'HTTP/1.1 %s\r\n' % status
response_header = ''
# 这是键值 直接拆包
for header_key, header_value in header_list:
response_header += ('%s:%s\r\n' % (header_key, header_value))
self.response_data = response_header_firstline + response_header + '\r\n'
def handler_client(self, handler_socket):
# 人工客服处理客户端的请求
data = handler_socket.recv(1024).decode('utf-8') # 字节流文件--字符串
request_datas_list = data.split('\r\n')
for obj in request_datas_list:
print(obj)
# todo:开始拼接的响应报文
# todo:截取请求的路径,获取请求的内容,根据路径不同返回不同数据
first_line = request_datas_list[0]
# ret = re.search('GET (/.*?) HTTP/1.1', first_line) #这个是正则
# 获取第一行,然后路径
firstline_list = first_line.split(' ')
# 判断是否有ret值,如果没有,说明是一个非法请求
if not firstline_list:
handler_socket.close()
return
# 尝试没有就给他一个默认值
try:
request_path = firstline_list[1]
except BaseException:
request_path = '/'
# request_path = ret.group(1)
print('**********************')
'''
HTTP/1.1 状态码 说明\r\n
Headername1:header_value\r\n
Headername2:header_value\r\n
Headername3:header_value\r\n
\r\n
Response_body 响应体,就是数据,网页、图片、文本
'''
environ = {'path': request_path}
if request_path.endswith('.py'):
# 交给动态资源处理框架取处理
print('此时进入动态资源处理')
user_info = request_datas_list[len(request_datas_list)-1]
responsebody = self.app(environ, self.start_response, user_info)
data = self.response_data + responsebody
handler_socket.send(data.encode('UTF-8'))
handler_socket.close()
else:
# 这里交给静态资源处理
if request_path == '/':
status_code = '200 ok'
responsebody = 'hello world!'
# 暂时不写静态
# elif request_path == '/index.html':
# status_code = '200 ok'
# with open('index.html') as f:
# response_data = f.read()
# responsebody = response_data
# elif request_path == '/login.html':
# status_code = '200 ok'
# with open('login.html') as f:
# responsebody = f.read()%('这是登录页面', '''
#
# ''')
else:
status_code = '404 not found'
with open('err.html') as f:
responsebody = f.read()
responseHeader = 'HTTP/1.1' + status_code + ' 123\r\n'
responseHeader += '\r\n'
# 判断responsebody是否是二进制文件
# 需要知道发送数据大小,已经发送了多少
try:
chardet.detect(responsebody)
sent_count = 0 # 已经发送数据大小
reponse = responseHeader.encode('utf-8') + responsebody
while sent_count < len(reponse):
# send_size = int(sent_count + len(reponse) / 2) # 一次发送数据大小加上当前位置
# 现在发送的数据大小
now_size = handler_socket.send(
reponse[sent_count:])
sent_count += now_size
# handler_socket.send(
# responseHeader.encode('utf-8') + responsebody)
except BaseException:
reponse = responseHeader + responsebody
sent_count = 0 # 已经发送数据大小
while sent_count < len(reponse):
# send_size = int(sent_count + len(reponse) / 2) # 一次发送数据大小加上当前位置
# 现在发送的数据大小
now_size = handler_socket.send(
reponse[sent_count:].encode('utf-8')) # send有可能发送不完
sent_count += now_size
# ret = handler_socket.send(reponse.encode('utf-8'))
# print(ret)
finally:
# 最后关闭
handler_socket.close()
def main():
'''实现主要逻辑'''
try:
port = sys.argv[1]
frame_name = sys.argv[2] # 用来表示哪个框架的哪个接口
except:
# 获取不到参数则给定默认参数
print('参数缺少,使用默认参数端口为5000')
port = 5000
frame_name = 'application:app'
return
finally:
# 获取接口名字切分,获取的是一个列表
frame_list = frame_name.split(':')
modle_name = frame_list[0]
func_name = frame_list[1]
# 这是新的一种导包方式
# app是指接口的导用
# __import__的参数是字符串,文件名
frame_obj = __import__(modle_name)
app = getattr(frame_obj, func_name)
# 初始化我的服务器
my_server = Server(port=int(port), app=app)
gv = gevent.spawn(my_server.start()) # 这个添加函数
gv.join()
if __name__ == '__main__':
main()
from shopping import ShangHui
'''这是装饰器工厂函数来实现 类似于flask'''
# 定义一个路径字典
urlfuncdict = {}
# shsp尚汇优品对象
shsp = None
# 用户信息包括名字和密码
userinfo = None
# 验证是否登录
is_login = False
# 登陆后的账户名
user_name = ''
# 订单的货物信息
order_info = None
def route(url):
# 装饰器工厂函数
def wrapper(func):
# 添加键值对,key是路径,value是函数的引用
urlfuncdict[url] = func
def inner():
response_body = func()
return response_body
return inner
return wrapper
@route('/signin.py')
def signin():
with open('signin.html') as f:
return f.read()
@route('/handler.py')
def handler():
# 用来处理登录,返回的是布尔值
if shsp.login(user_info=userinfo):
html = '''
Title
登录成功,按确认按钮跳转主页
'''
global is_login, user_name
user_name = shsp.login_name
is_login = True
print('登陆后保存的:',user_name,is_login)
else:
html = '''
Title
账号密码错误,请重新登录
'''
return html
@route('/change_psd.py')
def change_psd():
# 用来修改密码
with open('change_psd.html') as f:
return f.read()
@route('/login.py')
def login():
with open('login.html') as f:
html_str = f.read()
# 判断是否注册后进入这个界面
if 'reg' in userinfo:
# 这是注册方法,返回布尔值判断注册成功与否
print('注册账号', userinfo)
if shsp.signin(user_info=userinfo):
html = html_str % ('注册成功请输入账号密码登录', '')
else:
html = html_str % ('注册失败,名字已存在请重试', '''''')
elif 'old' in userinfo:
# 这是修改密码方法,返回布尔值
print('修改密码', userinfo)
if shsp.change_password(user_info=userinfo):
html = html_str % ('修改密码成功请重新登录', '')
else:
html = html_str % ('修改密码失败,账号或密码是输入错误', '''''')
else:
html = html_str % ('这是登录页面', '''''')
return html
@route('/exit.py')
def exit():
# 用来关闭所有东西
global is_login
is_login = False
shsp.close()
html = '''
Title
你已经退出商城,欢迎再来!
'''
return html
@route('/index.py')
def index():
# 用来显示网页内容
''''''
with open('index.html') as f:
str_html = f.read()
print('验证是否登录:', is_login)
if is_login:
# 登录状态则添加两个按钮,退出和更改密码
html = str_html % (shsp.get_data_str(), '''''', '''''')
else:
html = str_html % (shsp.get_data_str(), '''''', '')
return html
@route('/order.py')
def order_comm():
# 这是实现下订单
if is_login:
if shsp.order_comm(order_info=userinfo):
global order_info
order_info = userinfo
with open('order_comm.html') as f:
str_html = f.read()
html = str_html
else:
html = '''
Title
下单失败,检查输入的商品名字或数量是否出错
'''
else :
html = '''
Title
账号没登录,请登录
'''
return html
@route('/confirm_order.py')
def confirm_order():
shsp.confirm_order(add_info=userinfo, order_info=order_info, sys_name=user_name)
html = '''
Title
下单成功,确认返回主页面
'''
return html
@route('/check_order.py')
def check_order():
# 展示订单
# if is_login:
#
# else:
# print('没登录,请登录')
''''''
pass
@route('/error.py')
def error():
return '404 not found'
def app(environ, start_response, user_info):
'''
:param environ: 字典,传入的是请求头
:param start_response: 这是方法
:return: 返回网页内容
'''
request_path = environ['path']
print('app方法:', user_info)
# 根据上面字典判断
staus_code = ''
global shsp, userinfo
shsp = ShangHui()
userinfo = user_info
# 判断方法
try:
func = urlfuncdict[request_path]
staus_code = '200 ok'
except BaseException:
staus_code = '404 NOT FOUND'
func = error
finally:
start_response(staus_code, [('name', 'paul'), ('age', '18')])
return func()
import pymysql
import time
'''这个程序实现数据库交互'''
class ShangHui(object):
# _instance = None
#
# def __new__(cls, *args, **kwargs):
# if not cls._instance:
# cls._instance = super(ShangHui, cls).__new__(cls, *args, **kwargs)
# return cls._instance
def __init__(self):
'''
初始化数据库'''
self.connect_obj = pymysql.connect(
host='127.0.0.1',
port=3306,
database='commodity',
user='root',
password='mysql',
charset='utf8')
self.cur = self.connect_obj.cursor() # 操作
self.login_name = '' # 登录之后的用户名
self.login_flag = 0 # 登陆验证
def close(self):
self.connect_obj.close()
self.cur.close()
print('再见')
def login(self, user_info):
'''这个是登录操作,返回一个布尔值'''
# username=zzz&password=123 取出名字和密码
try:
split_info = user_info.split('&')
inname = split_info[0][9:]
inpassword = split_info[1][9:]
except :
print('数据没有传输过来')
return False
# 不要用拼接sql字符串
sql = 'SELECT * FROM user WHERE name=%s and password=%s'
try:
# 参数列表化
ret = self.cur.execute(sql, [inname, inpassword])
except Exception as e:
ret = '数据库出错'
print(e)
if ret == 0:
print('账号或密码出错')
return False
print('#########登录成功###########')
self.login_name = inname
self.login_flag = 1
return True
def get_data_str(self):
print('商品展示')
try:
sql = '''SELECT commodity.name AS'商品名',commodity.price AS'价格',cate.name AS'商品类别',brand.name AS'商品品牌' FROM commodity INNER JOIN cate ON commodity.cate_id=cate.id INNER JOIN brand ON commodity.brand_id=brand.id'''
ret = self.cur.execute(sql)
if ret:
data_str = '商品名 价格 商品类别 品牌
'
for i in self.cur.fetchall():
for j in i:
str1 = str(j)
data_str += str1 + ' '
data_str += '
'
print(data_str)
return data_str
except Exception as e:
print(e)
def get_order_id(self):
# 这是读写文件,目的是为了保存并读取订单id,每次自增1
file_data = ''
with open('order.txt', 'r') as f:
for line in f:
old_str = line
new_str = str(int(old_str) + 1)
file_data = new_str
with open('order.txt', 'w') as f:
f.write(file_data)
# 最后异步读取
with open('order.txt', 'r') as f:
for line in f:
file_data = line
return file_data
def order_comm(self, order_info):
'''下订单,判断可不可以'''
# name=zzz&num=1 取出名字和密码
try:
split_info = order_info.split('&')
order_name = split_info[0][5:] # 这里编码问题
except:
print('数据没有传输')
return False
print('商品下单', order_name)
try:
sql = 'SELECT price FROM commodity WHERE name=%s'
ret = self.cur.execute(sql, [order_name, ])
if ret:
print('下单成功,下面输入配送信息')
return True
else:
print('输入错误,返回主界面')
return False
except Exception as e:
print(e)
def confirm_order(self, add_info, order_info, sys_name):
# 订单确认,然后写入订单表,订单详情表,配送表,
# name=zzz&phone=11132112&address=sdasda 取出名字和密码
try:
# 切割用户信息
split_info = add_info.split('&')
user_name = split_info[0][5:]
user_phone = split_info[1][6:]
user_addr = split_info[2][8:]
except:
print('数据没有传输')
return
# 切割订单
split_order = order_info.split('&')
print('############')
print(split_order)
order_name = split_order[0][5:] # 商品名字
order_num = int(split_order[1][4:])
# 挑选并确认价格
sql = 'SELECT price FROM commodity WHERE name=%s'
self.cur.execute(sql, [order_name, ])
pay_price = self.cur.fetchall()[0][0]
payment = order_num * pay_price # 总价
# 获得用户id
try:
user_sql = 'SELECT id FROM user WHERE name=%s'
self.cur.execute(user_sql, [sys_name, ])
user_id = self.cur.fetchone()[0]
# 获得商品id
comm_sql = 'SELECT id FROM commodity WHERE name=%s'
self.cur.execute(comm_sql, [order_name])
comm_id = self.cur.fetchone()[0]
print('写入订单表')
# 写入订单表 id,payment,pay_time,user_id,send_time
order_sql = 'INSERT INTO myorder VALUES(0, %s, %s, %s, %s)'
self.cur.execute(order_sql, [payment, time.ctime(), user_id, '一天后'])
# 获得订单id 用文件存储方法
order_id = int(self.get_order_id())
print('写入订单详情表')
# 写入订单详情表 id,comm_id,order_id,num,price,total_fee
items_sql = 'INSERT INTO order_items VALUES(0, %s, %s, %s, %s, %s)'
self.cur.execute(items_sql, [comm_id, order_id, order_num, pay_price, payment])
# 写入配送表 id,order_id,user_id,name,phone,address
print('写入配送表')
ship_sql = 'INSERT INTO shipping VALUES(0, %s, %s, %s, %s, %s)'
self.cur.execute(ship_sql, [order_id, user_id, user_name, user_phone, user_addr])
self.connect_obj.commit() # 写入
except Exception as e:
print(e)
def signin(self, user_info):
'''注册注意数据库保存'''
# reg_username=zzz&password=123 取出名字和密码
split_info = user_info.split('&')
inname = split_info[0][13:]
inpassword = split_info[1][9:]
print('注册账号')
try:
sql = 'SELECT * FROM user WHERE name=%s'
ret = self.cur.execute(sql, [inname, ])
if ret:
print('用户名已经存在')
return False
sql_insert = 'INSERT INTO user VALUES(0, %s, %s)'
ret1 = self.cur.execute(sql_insert, [inname, inpassword])
if ret1:
print('注册成功')
self.connect_obj.commit() # 提交
return True
except Exception as e:
print(e)
def change_password(self, user_info):
'''注册注意数据库保存'''
# username=zzz&old_psd=123&new_psd=333 取出名字和密码
split_info = user_info.split('&')
user_name = split_info[0][9:]
old_password = split_info[1][8:]
print('修改密码')
new_password = split_info[2][8:]
try:
sql = 'SELECT * FROM user WHERE name=%s and password=%s'
ret = self.cur.execute(sql, [user_name, old_password])
if ret == 0:
print('旧密码输入错误')
return False
else:
sql_update = 'UPDATE user SET password=%s WHERE name=%s'
ret1 = self.cur.execute(sql_update, [new_password, user_name])
if ret1:
print('修改成功')
self.connect_obj.commit() # 提交
return True
except Exception as e:
print(e)
def get_check_order(self):
print('订单展示')
try:
sql = '''SELECT myorder.id,myorder.,cate.name AS'商品类别',brand.name AS'商品品牌' FROM commodity INNER JOIN cate ON commodity.cate_id=cate.id INNER JOIN brand ON commodity.brand_id=brand.id'''
ret = self.cur.execute(sql)
if ret:
data_str = '商品名 价格 商品类别 品牌
'
for i in self.cur.fetchall():
for j in i:
str1 = str(j)
data_str += str1 + ' '
data_str += '
'
print(data_str)
return data_str
except Exception as e:
print(e)
这里使用了sys模块,使用终端启动py文件并且可以传输数据,先进入当前文件夹,然后启动,后面是端口和调用的框架及方法
相关代码截取出来:
启动服务器后,打开localhost或者本地回环地址127.0.0.1即可,这里自定义的逻辑是后缀为py