模型类
from flask_sqlalchemy import SQLAlchemy
from datetime import datetime
db = SQLAlchemy()
class UserModel(db.Model):
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
name = db.Column(db.String(32), comment="用户名")
password = db.Column(db.String(16), comment="密码,加密存储")
order_id = db.relationship("OrderModel", backref="user")
class TonyModel(db.Model):
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
name = db.Column(db.String(32), comment="托尼")
price = db.Column(db.Integer, comment="价格")
service_id = db.Column(db.Integer, default=0, comment="服务对象id")
order_id = db.relationship("OrderModel", backref="tony")
class OrderModel(db.Model):
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
start_time = db.Column(db.DateTime, comment="开始服务时间", default=datetime.now)
end_time = db.Column(db.DateTime, comment="结束服务时间")
price = db.Column(db.DECIMAL(10, 2), comment="价格")
tony_id = db.Column(db.Integer, db.ForeignKey("tony_model.id"), comment="所属托尼")
user_id = db.Column(db.Integer, db.ForeignKey("user_model.id"), comment="所属用户")
问题一:用户选择tony,tony只能被当前用户选择,不能被其他用户选择
添加历史记录,计算总价格
1.在tony表中添加一个service_id字段
service_id = db.Column(db.Integer, default=0, comment="服务对象id")
2.如果用户选择了当前tony,就让service_id=用户id,来让其他用户选择不了,并添加历史记录
class TonyView(Resource):
@check_login
def put(self):
user_id = g.user_id
req = reqparse.RequestParser()
req.add_argument("tony_id")
args = req.parse_args()
tony_id = args["tony_id"]
t = TonyModel.query.get(tony_id)
if not t:
return jsonify({
"code": 400,
"msg": "tony不存在"
})
if t.service_id != 0:
return jsonify({
"code": 400,
"msg": "托尼正在忙"
})
t.service_id = user_id
db.session.commit()
# 生成一个订单 添加美发历史记录
o = OrderModel(tony_id=tony_id, user_id=user_id)
db.session.add(o)
db.session.commit()
return jsonify({
"code": 200,
"msg": "tony选择成功"
})
3.设定字段状态来让页面显示tony状态
class TonyView(Resource):
@check_login
def get(self):
user_id = g.user_id
tony_list = TonyModel.query.all()
status = ""
temp = []
for i in tony_list:
if i.service_id == user_id:
status = "结束"
elif i.service_id == 0:
status = "空闲"
else:
status = "正忙"
temp_dict = {
"id": i.id,
"name": i.name,
"price": i.price,
"server": i.service_id,
"status": status
}
temp.append(temp_dict)
return jsonify({
"code": 200,
"msg": "展示成功",
"data": temp
})
4.释放tony,并计算服务时长,总价格
@check_login
def delete(self):
user_id = g.user_id
req = reqparse.RequestParser()
req.add_argument("tony_id")
args = req.parse_args()
tony_id = args["tony_id"]
order = OrderModel.query.filter(OrderModel.tony_id == tony_id, OrderModel.user_id == user_id,
OrderModel.end_time == None).first()
if not order:
return jsonify({
"code": 400,
"msg": "没有正在进行的服务"
})
# 开始时间转换格式
start_time = int(time.mktime(order.start_time.timetuple()))
print("start_time>>>>>>>", start_time)
# 查看服务多长时间
# time.time()现在时间 时间戳
all_time = int(time.time()) - start_time
price = order.tony.price
order_price = float(price) / 3600 * all_time # 订单价格
print("order_price>>>>>>", order_price)
# 折扣
zhekou = 0
history_order = OrderModel.query.filter(OrderModel.user_id == user_id,
OrderModel.end_time != None).all()
if len(history_order) >= 3:
zhekou = 0.85
order_price = float(price) / 3600 * all_time * zhekou
print("order_price333>>>>>>>", order_price)
order.end_time = datetime.now()
order.price = order_price
# 还要修改tony服务者
order.tony.service_id = 0
db.session.commit()
return jsonify({
"code": 200,
"msg": "结束服务"
})
问题二:密码加密
def jiami(password):
"""md5加密"""
import hashlib # 内置的
# 害怕被找出规律,所以要加上密钥
password += current_app.config.get("SECRET_KEY")
md5 = hashlib.md5() # 实例化md5对象
md5.update(password.encode("utf-8")) # 传入要加密的字符串 因为参数要求必须传入bytes类型 所以需要把字符串编码为bytes类型
sign = md5.hexdigest() # 获取到加密结果
print(sign)
if __name__ == '__main__':
from app import app
with app.app_context():
jiami("11111")
问题三:退出登录 token禁用
后端
# 校验登录 解码token
def login(func):
def inner(*args, **kwargs):
req = reqparse.RequestParser()
req.add_argument("token", location="headers") # 必须指定参数获取位置
args = req.parse_args()
token = args["token"]
if not token:
return jsonify({
"code": 400,
"msg": "token不存在"
})
# 先判断一下token是否已经被禁用掉了
res = RedisTool().res
is_exists = res.exists("token_%s" % token) # 判断某一个键是否存在 bool值
# 如果失效token存在,说明token已禁用
if is_exists:
# 代表被禁用
return jsonify({
"code": 403,
"msg": "用户未登录"
})
key = current_app.config.get("SECRET_KEY")
try:
payload = jwt.decode(token, key, algorithms="HS256")
print("解码出的用户信息", payload)
except Exception as e:
print("解码失败", e)
print("捕获异常的类型", type(e))
# <class 'jwt.exceptions.ExpiredSignatureError'>
return jsonify({
"code": 403,
"msg": "用户未登录"
})
g.user_id = payload["user_id"] # user_id从token中的payload部分解析出来
return func(*args, **kwargs)
return inner
class Login(Resource):
@login
def delete(self):
req = reqparse.RequestParser()
req.add_argument("token",location="headers")
args = req.parse_args()
token = args["token"]
"""
前端点击退出登录,把退出登录的token存到redis,此时说明我们往redis里存的是 已经退出登录失效
的token
然后在强制登陆的装饰器里校验 如果redis里有退出登录后写入的失效token,那就说明token已经禁用
"""
# 存到redis的目的 让某一个token失效
# token本身是有有效期的
res = RedisTool().res
res.set("token_%s" % token, "1", ex=7200)
return jsonify({
"code": 200,
"msg": "退出登录成功"
})
前端
<template>
<div class="container">
<el-button type="primary" @click="login_out">退出登录</el-button>
</div>
</template>
<script>
export default {
methods:{
// 退出登录 清空token
login_out() {
this.$axios
.delete("/user/login",{
headers: {
token: localStorage.getItem("token"),
},
})
.then((resp) => {
console.log("退出登录的响应", resp);
//清空本地存储token
localStorage.removeItem("token");
})
.catch((err) => {
console.log(err);
});
//跳转到登录页面
this.$router.push("/login");
},
}
}
</script>
图片验证码
后端
from captcha.image import ImageCaptcha
from model.model import UserModel, db
import redis
from flask import Blueprint, jsonify, current_app, g, send_file, request
from flask_restful import reqparse, Resource, Api
import random
user_bp = Blueprint("user_bp", __name__, url_prefix="/user")
api = Api(user_bp)
# 生成图形验证码
class ImageCode(Resource):
def get(self, uuid):
if not uuid:
return jsonify({
"code": 400,
"msg": "数据不能为空"
})
code = str(random.randint(1000, 9999))
res = redis.Redis(host="127.0.0.1", port=6379, password="123456")
res.set("image_%s" % uuid, code, ex=5 * 60)
img = ImageCaptcha()
img_code = img.generate(code, format="png")
return send_file(img_code, "png")
api.add_resource(ImageCode, "/img/<uuid>")
前端
<template>
<div>
图片验证码:<input type="text" v-model="code" />
<img :src="'http://127.0.0.1:5000/user/img/' + uuid" @click="checkImage" />
</div>
</template>
<script>
import { v4 as uuid4 } from "uuid";
export default {
name: "Login",
data() {
return {
code: "",
uuid: ""
};
},
mounted() {
this.checkImage();
},
methods: {
checkImage() {
this.uuid = uuid4();
}
}
};
</script>
<style scoped></style>
问题: 自动生成大小写字母数字混合的6位用户名,且不可重复。
# 生成6位随机大小写数字+大小写字母
# string.ascii_letters 大小写字母
# random.sample(参数1,n) 随机生成n位
username = random.sample(string.digits + string.ascii_letters, 6)
# "".json(参数) 以空字符串的方式把列表里的数据连接起来
username = str("".join(username))
# 死循环
while True:
u = UserModel.query.filter(UserModel.username == username).first()
# 如果用户名存在
if u:
# 重新生成
username = random.sample(string.digits + string.ascii_letters, 6)
username = str("".join(username))
# 不存在跳出循环
else:
break
问题:用户注册时随机选择一个头像
# 随机五个头像
img = ["01.png", "02.png", "03.jpg", "04.png"]
# random.shuffle(img)把列表里的数据随机打乱
random.shuffle(img)
# 存入数据库,取随机后第一个头像
img = "static/" + img[0]
问题:统计一个小时内注册的用户
u = UserModel.query.all()
temp = []
count = 0
for i in u:
# 开始转换时间格式 注册时间
create_time = int(time.mktime(i.create_time.timetuple()))
# 现在时间
all_time = int(time.time()) - create_time
# 3600是一个小时 60分*60秒
all_time = all_time / 3600
print(all_time)
# 统计一个小时内注册用户
if all_time <= 1:
count += 1
flask上传文件
后端
class UploadView(BaseView):
def post(self):
img = request.files.get("img")
filename = datetime.strftime(datetime.now(), "%Y%m%d%H%M%S") + str(random.randint(10000, 99999))
filename += "." + img.filename.split(".")[-1]
img.save("./static/" + filename)
# 上传到七牛云
from qiniu import Auth, put_file, etag
# 需要填写你的 Access Key 和 Secret Key
access_key = ''
secret_key = ''
# 构建鉴权对象
q = Auth(access_key, secret_key)
# 要上传的空间
bucket_name = 'sssxiaoxi'
# 上传后保存的文件名
key = 'static/' + filename
# 生成上传 Token,可以指定过期时间等
token = q.upload_token(bucket_name, key, 3600)
# 要上传文件的本地路径
localfile = './static/' + filename
ret, info = put_file(token, key, localfile, version='v2')
print(info)
if ret['key'] == key:
return self.success(data={
"filepath": "static/" + filename
})
else:
return self.error(msg="上传图片失败")
class UploadMp3(BaseView):
def get(self):
from qiniu import Auth
access_key = current_app.config.get("QINIU_AK")
secret_key = current_app.config.get("QINIU_SK")
auth = Auth(access_key, secret_key)
backet_name = "sssxiaoxi"
token = auth.upload_token(backet_name, expires=3600)
return self.success({
"token": token
})
前端
<el-upload
action="http://127.0.0.1:5000/admin/upload/img"
name="img"
:on-success="upload"
:show-file-list="false"
>
<div><img class="img" :src="user_info.host + user_info.avatar" /></div>
</el-upload>
upload(res) {
console.log(res);
if (res.code == 20000) {
this.user_info.avatar = res.data.filepath;
} else {
this.$message.error(res.data.msg);
}
},