验证码
邮件,依赖
This commit is contained in:
@@ -5,6 +5,7 @@ from sqlalchemy.orm import relationship
|
|||||||
from sqlalchemy import Table
|
from sqlalchemy import Table
|
||||||
from sqlalchemy import ForeignKey
|
from sqlalchemy import ForeignKey
|
||||||
from sqlalchemy.orm import backref
|
from sqlalchemy.orm import backref
|
||||||
|
from marshmallow_sqlalchemy import SQLAlchemyAutoSchema
|
||||||
|
|
||||||
|
|
||||||
class BaseModel(db.Model):
|
class BaseModel(db.Model):
|
||||||
@@ -85,3 +86,6 @@ Interval = db.Interval
|
|||||||
Enum = db.Enum
|
Enum = db.Enum
|
||||||
ARRAY = db.ARRAY
|
ARRAY = db.ARRAY
|
||||||
JSON = db.JSON
|
JSON = db.JSON
|
||||||
|
|
||||||
|
|
||||||
|
SQLAlchemyAutoSchema=SQLAlchemyAutoSchema
|
||||||
|
|||||||
@@ -0,0 +1,121 @@
|
|||||||
|
import random, math
|
||||||
|
from PIL import Image, ImageDraw, ImageFont, ImageFilter
|
||||||
|
|
||||||
|
|
||||||
|
class vieCode:
|
||||||
|
__fontSize = 20 # 字体大小
|
||||||
|
__width = 120 # 画布宽度
|
||||||
|
__heigth = 45 # 画布高度
|
||||||
|
__length = 4 # 验证码长度
|
||||||
|
__draw = None # 画布
|
||||||
|
__img = None # 图片资源
|
||||||
|
__code = None # 验证码字符
|
||||||
|
__str = None # 自定义验证码字符集
|
||||||
|
__inCurve = True # 是否画干扰线
|
||||||
|
__inNoise = True # 是否画干扰点
|
||||||
|
__type = 2 # 验证码类型 1、纯字母 2、数字字母混合
|
||||||
|
__fontPatn = 'applications/common/utils/fonts/2.ttf' # 字体
|
||||||
|
|
||||||
|
def GetCodeImage(self, size=80, length=4):
|
||||||
|
'''获取验证码图片
|
||||||
|
@param int size 验证码大小
|
||||||
|
@param int length 验证码长度
|
||||||
|
'''
|
||||||
|
# 准备基础数据
|
||||||
|
self.__length = length
|
||||||
|
self.__fontSize = size
|
||||||
|
self.__width = self.__fontSize * self.__length
|
||||||
|
self.__heigth = int(self.__fontSize * 1.5)
|
||||||
|
|
||||||
|
# 生成验证码图片
|
||||||
|
self.__createCode()
|
||||||
|
self.__createImage()
|
||||||
|
self.__createNoise()
|
||||||
|
self.__printString()
|
||||||
|
self.__cerateFilter()
|
||||||
|
|
||||||
|
return self.__img, self.__code
|
||||||
|
|
||||||
|
def __cerateFilter(self):
|
||||||
|
'''模糊处理'''
|
||||||
|
self.__img = self.__img.filter(ImageFilter.BLUR)
|
||||||
|
filter = ImageFilter.ModeFilter(8)
|
||||||
|
self.__img = self.__img.filter(filter)
|
||||||
|
|
||||||
|
def __createCode(self):
|
||||||
|
'''创建验证码字符'''
|
||||||
|
# 是否自定义字符集合
|
||||||
|
if not self.__str:
|
||||||
|
# 源文本
|
||||||
|
number = "3456789"
|
||||||
|
srcLetter = "qwertyuipasdfghjkzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM"
|
||||||
|
srcUpper = srcLetter.upper()
|
||||||
|
if self.__type == 1:
|
||||||
|
self.__str = number
|
||||||
|
else:
|
||||||
|
self.__str = srcLetter + srcUpper + number
|
||||||
|
|
||||||
|
# 构造验证码
|
||||||
|
self.__code = random.sample(self.__str, self.__length)
|
||||||
|
|
||||||
|
def __createImage(self):
|
||||||
|
'''创建画布'''
|
||||||
|
bgColor = (random.randint(200, 255), random.randint(200, 255), random.randint(200, 255))
|
||||||
|
self.__img = Image.new('RGB', (self.__width, self.__heigth), bgColor)
|
||||||
|
self.__draw = ImageDraw.Draw(self.__img)
|
||||||
|
|
||||||
|
def __createNoise(self):
|
||||||
|
'''画干扰点'''
|
||||||
|
if not self.__inNoise:
|
||||||
|
return
|
||||||
|
font = ImageFont.truetype(self.__fontPatn, int(self.__fontSize / 1.5))
|
||||||
|
for i in range(5):
|
||||||
|
# 杂点颜色
|
||||||
|
noiseColor = (random.randint(150, 200), random.randint(150, 200), random.randint(150, 200))
|
||||||
|
putStr = random.sample(self.__str, 2)
|
||||||
|
for j in range(2):
|
||||||
|
# 绘杂点
|
||||||
|
size = (random.randint(-10, self.__width), random.randint(-10, self.__heigth))
|
||||||
|
self.__draw.text(size, putStr[j], font=font, fill=noiseColor)
|
||||||
|
pass
|
||||||
|
|
||||||
|
def __createCurve(self):
|
||||||
|
'''画干扰线'''
|
||||||
|
if not self.__inCurve:
|
||||||
|
return
|
||||||
|
x = y = 0
|
||||||
|
|
||||||
|
# 计算曲线系数
|
||||||
|
a = random.uniform(1, self.__heigth / 2)
|
||||||
|
b = random.uniform(-self.__width / 4, self.__heigth / 4)
|
||||||
|
f = random.uniform(-self.__heigth / 4, self.__heigth / 4)
|
||||||
|
t = random.uniform(self.__heigth, self.__width * 2)
|
||||||
|
xend = random.randint(self.__width / 2, self.__width * 2)
|
||||||
|
w = (2 * math.pi) / t
|
||||||
|
|
||||||
|
# 画曲线
|
||||||
|
color = (random.randint(30, 150), random.randint(30, 150), random.randint(30, 150))
|
||||||
|
for x in range(xend):
|
||||||
|
if w != 0:
|
||||||
|
for k in range(int(self.__heigth / 10)):
|
||||||
|
y = a * math.sin(w * x + f) + b + self.__heigth / 2
|
||||||
|
i = int(self.__fontSize / 5)
|
||||||
|
while i > 0:
|
||||||
|
px = x + i
|
||||||
|
py = y + i + k
|
||||||
|
self.__draw.point((px, py), color)
|
||||||
|
i -= i
|
||||||
|
|
||||||
|
def __printString(self):
|
||||||
|
'''打印验证码字符串'''
|
||||||
|
font = ImageFont.truetype(self.__fontPatn, self.__fontSize)
|
||||||
|
x = 0
|
||||||
|
# 打印字符到画板
|
||||||
|
for i in range(self.__length):
|
||||||
|
# 设置字体随机颜色
|
||||||
|
color = (random.randint(30, 150), random.randint(30, 150), random.randint(30, 150))
|
||||||
|
# 计算座标
|
||||||
|
x = random.uniform(self.__fontSize * i * 0.95, self.__fontSize * i * 1.1)
|
||||||
|
y = self.__fontSize * random.uniform(0.3, 0.5)
|
||||||
|
# 打印字符
|
||||||
|
self.__draw.text((x, y), self.__code[i], font=font, fill=color)
|
||||||
Binary file not shown.
@@ -0,0 +1,13 @@
|
|||||||
|
from flask_mail import Message
|
||||||
|
|
||||||
|
from applications.extensions.init_mail import mail
|
||||||
|
|
||||||
|
|
||||||
|
# send_mail(subject='title', recipients=['123@qq.com'], content='body')
|
||||||
|
def send_mail(subject, recipients, content):
|
||||||
|
try:
|
||||||
|
message = Message(subject=subject, recipients=recipients, body=content)
|
||||||
|
mail.send(message)
|
||||||
|
except Exception as e:
|
||||||
|
print('邮箱发送出错了')
|
||||||
|
raise
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
from flask_mail import Mail
|
||||||
|
|
||||||
|
mail = Mail()
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
from flask_migrate import Migrate
|
from flask_migrate import Migrate
|
||||||
|
|
||||||
from entrance.extend.orm import db
|
from entrance.extend.orm import db
|
||||||
|
# from entrance.models import model_lists <-- 这一行不要删除
|
||||||
from entrance.models import model_lists
|
from entrance.models import model_lists
|
||||||
migrate = Migrate(db=db, directory='entrance/migrations')
|
|
||||||
|
|
||||||
|
migrate = Migrate(db=db, directory='entrance/migrations')
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ from flask_sqlalchemy import SQLAlchemy, BaseQuery
|
|||||||
class Query(BaseQuery):
|
class Query(BaseQuery):
|
||||||
|
|
||||||
def all_json(self) -> list:
|
def all_json(self) -> list:
|
||||||
print(self)
|
|
||||||
return [dict(i) for i in self.all()]
|
return [dict(i) for i in self.all()]
|
||||||
|
|
||||||
def soft_delete(self):
|
def soft_delete(self):
|
||||||
@@ -18,21 +17,11 @@ class Query(BaseQuery):
|
|||||||
return self.filter_by(delete_at=None).all()
|
return self.filter_by(delete_at=None).all()
|
||||||
|
|
||||||
def layui_paginate(self):
|
def layui_paginate(self):
|
||||||
"""
|
|
||||||
layui表格分页
|
|
||||||
page
|
|
||||||
limit
|
|
||||||
"""
|
|
||||||
return self.paginate(page=request.args.get('page', type=int),
|
return self.paginate(page=request.args.get('page', type=int),
|
||||||
per_page=request.args.get('limit', type=int),
|
per_page=request.args.get('limit', type=int),
|
||||||
error_out=False)
|
error_out=False)
|
||||||
|
|
||||||
def layui_paginate_json(self) -> Tuple[list, int]:
|
def layui_paginate_json(self) -> Tuple[list, int]:
|
||||||
"""
|
|
||||||
layui表格分页
|
|
||||||
page
|
|
||||||
limit
|
|
||||||
"""
|
|
||||||
p = self.paginate(page=request.args.get('page', type=int),
|
p = self.paginate(page=request.args.get('page', type=int),
|
||||||
per_page=request.args.get('limit', type=int),
|
per_page=request.args.get('limit', type=int),
|
||||||
error_out=False)
|
error_out=False)
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
from flask import Flask
|
from flask import Flask
|
||||||
|
|
||||||
|
from entrance.extend.mail import mail
|
||||||
from entrance.extend.orm import db
|
from entrance.extend.orm import db
|
||||||
from entrance.extend.migrate import migrate
|
from entrance.extend.migrate import migrate
|
||||||
|
|
||||||
|
|
||||||
def init_extensions(app: Flask):
|
def init_extensions(app: Flask):
|
||||||
db.init_app(app)
|
db.init_app(app)
|
||||||
migrate.init_app(app)
|
migrate.init_app(app)
|
||||||
|
mail.init_app(app)
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
from common.model import BaseModel, Column, Integer, String, Text, DateTime
|
from common.model import BaseModel, Column, Integer, String, Text, DateTime, SQLAlchemyAutoSchema
|
||||||
|
|
||||||
|
|
||||||
class Dept(BaseModel):
|
class Dept(BaseModel):
|
||||||
@@ -16,4 +16,11 @@ class Dept(BaseModel):
|
|||||||
remark = Column(Text, comment="备注")
|
remark = Column(Text, comment="备注")
|
||||||
address = Column(String(255), comment="详细地址")
|
address = Column(String(255), comment="详细地址")
|
||||||
create_at = Column(DateTime, default=datetime.datetime.now, comment='创建时间')
|
create_at = Column(DateTime, default=datetime.datetime.now, comment='创建时间')
|
||||||
update_at = Column(DateTime, default=datetime.datetime.now, onupdate=datetime.datetime.now, comment='创建时间')
|
update_at = Column(DateTime, default=datetime.datetime.now, onupdate=datetime.datetime.now, comment='创建时间')
|
||||||
|
|
||||||
|
|
||||||
|
class DeptSchema(SQLAlchemyAutoSchema):
|
||||||
|
class Meta:
|
||||||
|
model = Dept
|
||||||
|
include_fk = True
|
||||||
|
load_instance = True
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
from common.model import BaseModel, Column, Integer, String, DateTime
|
from common.model import BaseModel, Column, Integer, String, DateTime, SQLAlchemyAutoSchema
|
||||||
|
|
||||||
|
|
||||||
class DictType(BaseModel):
|
class DictType(BaseModel):
|
||||||
@@ -14,6 +14,13 @@ class DictType(BaseModel):
|
|||||||
update_time = Column(DateTime, default=datetime.datetime.now, onupdate=datetime.datetime.now, comment='更新时间')
|
update_time = Column(DateTime, default=datetime.datetime.now, onupdate=datetime.datetime.now, comment='更新时间')
|
||||||
|
|
||||||
|
|
||||||
|
class DictTypeSchema(SQLAlchemyAutoSchema):
|
||||||
|
class Meta:
|
||||||
|
model = DictType
|
||||||
|
include_fk = True
|
||||||
|
load_instance = True
|
||||||
|
|
||||||
|
|
||||||
class DictData(BaseModel):
|
class DictData(BaseModel):
|
||||||
__tablename__ = 'sys_dict_data'
|
__tablename__ = 'sys_dict_data'
|
||||||
id = Column(Integer, primary_key=True)
|
id = Column(Integer, primary_key=True)
|
||||||
@@ -25,3 +32,10 @@ class DictData(BaseModel):
|
|||||||
remark = Column(String(255), comment='备注')
|
remark = Column(String(255), comment='备注')
|
||||||
create_time = Column(DateTime, default=datetime.datetime.now, comment='创建时间')
|
create_time = Column(DateTime, default=datetime.datetime.now, comment='创建时间')
|
||||||
update_time = Column(DateTime, default=datetime.datetime.now, onupdate=datetime.datetime.now, comment='更新时间')
|
update_time = Column(DateTime, default=datetime.datetime.now, onupdate=datetime.datetime.now, comment='更新时间')
|
||||||
|
|
||||||
|
|
||||||
|
class DictDataSchema(SQLAlchemyAutoSchema):
|
||||||
|
class Meta:
|
||||||
|
model = DictData
|
||||||
|
include_fk = True
|
||||||
|
load_instance = True
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
from common.model import BaseModel, Column, Integer, String, Text, DateTime
|
from common.model import BaseModel, Column, Integer, String, Text, DateTime, SQLAlchemyAutoSchema
|
||||||
|
|
||||||
|
|
||||||
class AdminLog(BaseModel):
|
class AdminLog(BaseModel):
|
||||||
@@ -13,4 +13,11 @@ class AdminLog(BaseModel):
|
|||||||
ip = Column(String(255))
|
ip = Column(String(255))
|
||||||
success = Column(Integer)
|
success = Column(Integer)
|
||||||
user_agent = Column(Text)
|
user_agent = Column(Text)
|
||||||
create_time = Column(DateTime, default=datetime.datetime.now)
|
create_time = Column(DateTime, default=datetime.datetime.now)
|
||||||
|
|
||||||
|
|
||||||
|
class AdminLogSchema(SQLAlchemyAutoSchema):
|
||||||
|
class Meta:
|
||||||
|
model = AdminLog
|
||||||
|
include_fk = True
|
||||||
|
load_instance = True
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
from common.model import BaseModel, Integer, Column, String, CHAR, DateTime
|
from common.model import BaseModel, Integer, Column, String, CHAR, DateTime, SQLAlchemyAutoSchema
|
||||||
|
|
||||||
|
|
||||||
class Photo(BaseModel):
|
class Photo(BaseModel):
|
||||||
@@ -10,4 +10,11 @@ class Photo(BaseModel):
|
|||||||
href = Column(String(255))
|
href = Column(String(255))
|
||||||
mime = Column(CHAR(50), nullable=False)
|
mime = Column(CHAR(50), nullable=False)
|
||||||
size = Column(CHAR(30), nullable=False)
|
size = Column(CHAR(30), nullable=False)
|
||||||
create_time = Column(DateTime, default=datetime.datetime.now)
|
create_time = Column(DateTime, default=datetime.datetime.now)
|
||||||
|
|
||||||
|
|
||||||
|
class AdminLogSchema(SQLAlchemyAutoSchema):
|
||||||
|
class Meta:
|
||||||
|
model = Photo
|
||||||
|
include_fk = True
|
||||||
|
load_instance = True
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
from common.model import BaseModel, Column, Integer, String, DateTime
|
from common.model import BaseModel, Column, Integer, String, DateTime, SQLAlchemyAutoSchema
|
||||||
|
|
||||||
|
|
||||||
class Power(BaseModel):
|
class Power(BaseModel):
|
||||||
@@ -17,3 +17,10 @@ class Power(BaseModel):
|
|||||||
create_time = Column(DateTime, default=datetime.datetime.now, comment='创建时间')
|
create_time = Column(DateTime, default=datetime.datetime.now, comment='创建时间')
|
||||||
update_time = Column(DateTime, default=datetime.datetime.now, onupdate=datetime.datetime.now, comment='更新时间')
|
update_time = Column(DateTime, default=datetime.datetime.now, onupdate=datetime.datetime.now, comment='更新时间')
|
||||||
enable = Column(Integer, comment='是否开启')
|
enable = Column(Integer, comment='是否开启')
|
||||||
|
|
||||||
|
|
||||||
|
class PowerSchema(SQLAlchemyAutoSchema):
|
||||||
|
class Meta:
|
||||||
|
model = Power
|
||||||
|
include_fk = True
|
||||||
|
load_instance = True
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import datetime
|
import datetime
|
||||||
from common.model import BaseModel, Column, String, Integer, DateTime,relationship
|
from common.model import BaseModel, Column, String, Integer, DateTime, relationship, SQLAlchemyAutoSchema
|
||||||
|
|
||||||
|
|
||||||
class Role(BaseModel):
|
class Role(BaseModel):
|
||||||
@@ -14,3 +14,9 @@ class Role(BaseModel):
|
|||||||
create_time = Column(DateTime, default=datetime.datetime.now, comment='创建时间')
|
create_time = Column(DateTime, default=datetime.datetime.now, comment='创建时间')
|
||||||
update_time = Column(DateTime, default=datetime.datetime.now, onupdate=datetime.datetime.now, comment='更新时间')
|
update_time = Column(DateTime, default=datetime.datetime.now, onupdate=datetime.datetime.now, comment='更新时间')
|
||||||
power = relationship('Power', secondary="admin_role_power", backref=backref('role'))
|
power = relationship('Power', secondary="admin_role_power", backref=backref('role'))
|
||||||
|
|
||||||
|
class RoleSchema(SQLAlchemyAutoSchema):
|
||||||
|
class Meta:
|
||||||
|
model = Role
|
||||||
|
include_fk = True
|
||||||
|
load_instance = True
|
||||||
@@ -2,7 +2,7 @@ import datetime
|
|||||||
from flask_login import UserMixin
|
from flask_login import UserMixin
|
||||||
from werkzeug.security import generate_password_hash, check_password_hash
|
from werkzeug.security import generate_password_hash, check_password_hash
|
||||||
|
|
||||||
from common.model import BaseModel, Column, Integer, String, DateTime, relationship
|
from common.model import BaseModel, Column, Integer, String, DateTime, relationship, SQLAlchemyAutoSchema, backref
|
||||||
|
|
||||||
|
|
||||||
class User(BaseModel, UserMixin):
|
class User(BaseModel, UserMixin):
|
||||||
@@ -24,3 +24,12 @@ class User(BaseModel, UserMixin):
|
|||||||
|
|
||||||
def validate_password(self, password):
|
def validate_password(self, password):
|
||||||
return check_password_hash(self.password_hash, password)
|
return check_password_hash(self.password_hash, password)
|
||||||
|
|
||||||
|
|
||||||
|
# 如果要全换全部字段,就不要声明fields或exclude字段即可
|
||||||
|
class UserRoleSchema(SQLAlchemyAutoSchema):
|
||||||
|
class Meta:
|
||||||
|
model = User
|
||||||
|
include_fk = True
|
||||||
|
load_instance = True
|
||||||
|
exclude = ['password_hash']
|
||||||
|
|||||||
@@ -1,11 +1,5 @@
|
|||||||
from typing import List
|
|
||||||
|
|
||||||
from common.view import Blueprint, View
|
from common.view import Blueprint, View
|
||||||
from flask import jsonify
|
|
||||||
|
|
||||||
from pydantic import BaseModel, ValidationError, constr, parse_obj_as
|
|
||||||
|
|
||||||
from entrance.extend.orm import db
|
|
||||||
|
|
||||||
user = Blueprint('sys', __name__, url_prefix='/user')
|
user = Blueprint('sys', __name__, url_prefix='/user')
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
-r min.txt
|
||||||
|
-r extra.txt
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
Flask-Mail==0.9.1
|
||||||
+2
-1
@@ -5,4 +5,5 @@ pymysql==1.0.2
|
|||||||
Flask-Migrate==3.1.0
|
Flask-Migrate==3.1.0
|
||||||
flask-marshmallow==0.14.0
|
flask-marshmallow==0.14.0
|
||||||
marshmallow-sqlalchemy==0.27.0
|
marshmallow-sqlalchemy==0.27.0
|
||||||
Flask-Login==0.5.0
|
Flask-Login==0.5.0
|
||||||
|
Pillow==9.0.0
|
||||||
Reference in New Issue
Block a user