diff --git a/common/model.py b/common/model.py index 5cec05e..59908d0 100644 --- a/common/model.py +++ b/common/model.py @@ -5,6 +5,7 @@ from sqlalchemy.orm import relationship from sqlalchemy import Table from sqlalchemy import ForeignKey from sqlalchemy.orm import backref +from marshmallow_sqlalchemy import SQLAlchemyAutoSchema class BaseModel(db.Model): @@ -85,3 +86,6 @@ Interval = db.Interval Enum = db.Enum ARRAY = db.ARRAY JSON = db.JSON + + +SQLAlchemyAutoSchema=SQLAlchemyAutoSchema diff --git a/common/utils/captcha.py b/common/utils/captcha.py new file mode 100644 index 0000000..349f687 --- /dev/null +++ b/common/utils/captcha.py @@ -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) diff --git a/common/utils/fonts/2.ttf b/common/utils/fonts/2.ttf new file mode 100644 index 0000000..3a452b6 Binary files /dev/null and b/common/utils/fonts/2.ttf differ diff --git a/common/utils/mail.py b/common/utils/mail.py new file mode 100644 index 0000000..eddc666 --- /dev/null +++ b/common/utils/mail.py @@ -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 diff --git a/entrance/extend/mail.py b/entrance/extend/mail.py new file mode 100644 index 0000000..97987ea --- /dev/null +++ b/entrance/extend/mail.py @@ -0,0 +1,3 @@ +from flask_mail import Mail + +mail = Mail() diff --git a/entrance/extend/migrate.py b/entrance/extend/migrate.py index 3042ff7..6752984 100644 --- a/entrance/extend/migrate.py +++ b/entrance/extend/migrate.py @@ -1,6 +1,7 @@ from flask_migrate import Migrate from entrance.extend.orm import db +# 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') diff --git a/entrance/extend/orm.py b/entrance/extend/orm.py index 05f52bf..1a561dd 100644 --- a/entrance/extend/orm.py +++ b/entrance/extend/orm.py @@ -8,7 +8,6 @@ from flask_sqlalchemy import SQLAlchemy, BaseQuery class Query(BaseQuery): def all_json(self) -> list: - print(self) return [dict(i) for i in self.all()] def soft_delete(self): @@ -18,21 +17,11 @@ class Query(BaseQuery): return self.filter_by(delete_at=None).all() def layui_paginate(self): - """ - layui表格分页 - page - limit - """ return self.paginate(page=request.args.get('page', type=int), per_page=request.args.get('limit', type=int), error_out=False) def layui_paginate_json(self) -> Tuple[list, int]: - """ - layui表格分页 - page - limit - """ p = self.paginate(page=request.args.get('page', type=int), per_page=request.args.get('limit', type=int), error_out=False) diff --git a/entrance/extensions.py b/entrance/extensions.py index ec3b8d5..dda9946 100644 --- a/entrance/extensions.py +++ b/entrance/extensions.py @@ -1,9 +1,12 @@ from flask import Flask +from entrance.extend.mail import mail from entrance.extend.orm import db from entrance.extend.migrate import migrate def init_extensions(app: Flask): db.init_app(app) - migrate.init_app(app) \ No newline at end of file + migrate.init_app(app) + mail.init_app(app) + diff --git a/modules/sys/models/admin_dept.py b/modules/sys/models/admin_dept.py index 413f82e..bf737b4 100644 --- a/modules/sys/models/admin_dept.py +++ b/modules/sys/models/admin_dept.py @@ -1,6 +1,6 @@ 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): @@ -16,4 +16,11 @@ class Dept(BaseModel): remark = Column(Text, comment="备注") address = Column(String(255), comment="详细地址") create_at = Column(DateTime, default=datetime.datetime.now, comment='创建时间') - update_at = Column(DateTime, default=datetime.datetime.now, onupdate=datetime.datetime.now, comment='创建时间') \ No newline at end of file + 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 diff --git a/modules/sys/models/admin_dict.py b/modules/sys/models/admin_dict.py index 7ed90f6..df148f5 100644 --- a/modules/sys/models/admin_dict.py +++ b/modules/sys/models/admin_dict.py @@ -1,6 +1,6 @@ import datetime -from common.model import BaseModel, Column, Integer, String, DateTime +from common.model import BaseModel, Column, Integer, String, DateTime, SQLAlchemyAutoSchema class DictType(BaseModel): @@ -14,6 +14,13 @@ class DictType(BaseModel): 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): __tablename__ = 'sys_dict_data' id = Column(Integer, primary_key=True) @@ -25,3 +32,10 @@ class DictData(BaseModel): remark = Column(String(255), comment='备注') create_time = Column(DateTime, default=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 diff --git a/modules/sys/models/admin_log.py b/modules/sys/models/admin_log.py index cd58767..2712b73 100644 --- a/modules/sys/models/admin_log.py +++ b/modules/sys/models/admin_log.py @@ -1,6 +1,6 @@ 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): @@ -13,4 +13,11 @@ class AdminLog(BaseModel): ip = Column(String(255)) success = Column(Integer) user_agent = Column(Text) - create_time = Column(DateTime, default=datetime.datetime.now) \ No newline at end of file + create_time = Column(DateTime, default=datetime.datetime.now) + + +class AdminLogSchema(SQLAlchemyAutoSchema): + class Meta: + model = AdminLog + include_fk = True + load_instance = True diff --git a/modules/sys/models/admin_photo.py b/modules/sys/models/admin_photo.py index 47ae27a..b2eec42 100644 --- a/modules/sys/models/admin_photo.py +++ b/modules/sys/models/admin_photo.py @@ -1,6 +1,6 @@ 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): @@ -10,4 +10,11 @@ class Photo(BaseModel): href = Column(String(255)) mime = Column(CHAR(50), nullable=False) size = Column(CHAR(30), nullable=False) - create_time = Column(DateTime, default=datetime.datetime.now) \ No newline at end of file + create_time = Column(DateTime, default=datetime.datetime.now) + + +class AdminLogSchema(SQLAlchemyAutoSchema): + class Meta: + model = Photo + include_fk = True + load_instance = True \ No newline at end of file diff --git a/modules/sys/models/admin_power.py b/modules/sys/models/admin_power.py index cc55800..bc611df 100644 --- a/modules/sys/models/admin_power.py +++ b/modules/sys/models/admin_power.py @@ -1,6 +1,6 @@ import datetime -from common.model import BaseModel, Column, Integer, String, DateTime +from common.model import BaseModel, Column, Integer, String, DateTime, SQLAlchemyAutoSchema class Power(BaseModel): @@ -17,3 +17,10 @@ class Power(BaseModel): create_time = Column(DateTime, default=datetime.datetime.now, comment='创建时间') update_time = Column(DateTime, default=datetime.datetime.now, onupdate=datetime.datetime.now, comment='更新时间') enable = Column(Integer, comment='是否开启') + + +class PowerSchema(SQLAlchemyAutoSchema): + class Meta: + model = Power + include_fk = True + load_instance = True diff --git a/modules/sys/models/admin_role.py b/modules/sys/models/admin_role.py index 3241ddf..ab95c0b 100644 --- a/modules/sys/models/admin_role.py +++ b/modules/sys/models/admin_role.py @@ -1,5 +1,5 @@ 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): @@ -14,3 +14,9 @@ class Role(BaseModel): create_time = Column(DateTime, default=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')) + +class RoleSchema(SQLAlchemyAutoSchema): + class Meta: + model = Role + include_fk = True + load_instance = True \ No newline at end of file diff --git a/modules/sys/models/admin_user.py b/modules/sys/models/admin_user.py index 6324db6..ae28566 100644 --- a/modules/sys/models/admin_user.py +++ b/modules/sys/models/admin_user.py @@ -2,7 +2,7 @@ import datetime from flask_login import UserMixin 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): @@ -24,3 +24,12 @@ class User(BaseModel, UserMixin): def validate_password(self, 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'] diff --git a/modules/sys/view/user.py b/modules/sys/view/user.py index 8075e33..602f771 100644 --- a/modules/sys/view/user.py +++ b/modules/sys/view/user.py @@ -1,11 +1,5 @@ -from typing import List - 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') diff --git a/require/common.txt b/require/common.txt new file mode 100644 index 0000000..daf7ca9 --- /dev/null +++ b/require/common.txt @@ -0,0 +1,2 @@ +-r min.txt +-r extra.txt \ No newline at end of file diff --git a/require/extra.txt b/require/extra.txt new file mode 100644 index 0000000..ea6677a --- /dev/null +++ b/require/extra.txt @@ -0,0 +1 @@ +Flask-Mail==0.9.1 \ No newline at end of file diff --git a/require/min.txt b/require/min.txt index 2958f6f..95d8211 100644 --- a/require/min.txt +++ b/require/min.txt @@ -5,4 +5,5 @@ pymysql==1.0.2 Flask-Migrate==3.1.0 flask-marshmallow==0.14.0 marshmallow-sqlalchemy==0.27.0 -Flask-Login==0.5.0 \ No newline at end of file +Flask-Login==0.5.0 +Pillow==9.0.0 \ No newline at end of file