【Flask + Vue3 前后端分离管理系统】

Flask + Vue3 前后端分离管理系统

项目概述

本项目是一个基于 Flask 后端和 Vue3 前端的前后端分离管理系统。项目实现了用户管理、角色管理、菜单管理、权限控制等完整的后台管理功能。

技术栈

后端技术栈:

  • Flask 3.0.0 - Python Web框架
  • Flask-SQLAlchemy 3.1.1 - ORM数据库操作
  • Flask-JWT-Extended 4.6.0 - JWT身份认证
  • Flask-CORS 4.0.0 - 跨域资源共享
  • PyMySQL 1.1.0 - MySQL数据库驱动
  • passlib[bcrypt] 1.7.4 - 密码加密(兼容Django)

前端技术栈:

  • Vue 3 - 渐进式JavaScript框架
  • TypeScript - 类型安全的JavaScript
  • Element Plus - Vue 3 UI组件库
  • Pinia - Vue 3状态管理
  • Vite - 现代化构建工具
  • TailwindCSS - 原子化CSS框架

项目结构

Flask_Pure/
├── Flask-Backend/ # Flask后端项目
│ ├── app/ # 应用核心代码
│ │ ├── init.py # 应用工厂
│ │ ├── models.py # 数据模型
│ │ ├── auth.py # 认证模块
│ │ ├── user.py # 用户管理
│ │ ├── role.py # 角色管理
│ │ ├── menu.py # 菜单管理
│ │ ├── permission.py # 权限管理
│ │ └── utils.py # 工具函数
│ ├── config.py # 配置文件
│ ├── requirements.txt # Python依赖
│ └── run.py # 启动脚本
├── Frontend/ # Vue3前端项目
│ ├── src/ # 源代码
│ │ ├── api/ # API接口
│ │ ├── components/ # 组件
│ │ ├── views/ # 页面视图
│ │ ├── store/ # 状态管理
│ │ └── utils/ # 工具函数
│ └── package.json # 前端依赖

核心功能实现

1. 用户认证系统

登录功能

登录

Flask后端实现了完整的JWT认证系统:

@auth_bp.route('/auth/login', methods=['POST'])
def login():"""用户登录"""try:data = request.get_json()username = data.get('username')password = data.get('password')# 查找用户user = User.query.filter_by(username=username).first()if not user or not user.check_password(password):return jsonify({'code': 401, 'message': '用户名或密码错误'}), 401# 创建JWT令牌access_token = create_access_token(identity=str(user.id))refresh_token = create_refresh_token(identity=str(user.id))return jsonify({'code': 0,'data': {'accessToken': access_token}})except Exception as e:return jsonify({'code': 500, 'message': str(e)}), 500
密码兼容性处理

为了兼容Django的密码哈希,我们实现了特殊的密码处理逻辑:

def django_password_hash(password, salt=None):"""使用Django的pbkdf2_sha256方法生成密码哈希"""if salt is None:salt = secrets.token_bytes(12)hash_obj = pbkdf2_sha256.hash(password, salt=salt)return hash_objdef check_password_compatible(password, hashed_password):"""兼容性密码验证,支持Django和Flask格式"""if is_django_password(hashed_password):return django_password_verify(password, hashed_password)else:return check_password_hash(hashed_password, password)

2. 用户管理系统

用户管理

用户列表接口
@user_bp.route('/user/list', methods=['GET'])
@jwt_required()
def get_user_list():"""获取用户列表 - 完全匹配Django格式"""try:# 分页参数page = request.args.get('page', 1, type=int)page_size = request.args.get('page_size', 10, type=int)# 构建查询query = User.query.order_by(User.date_joined.desc())# 分页pagination = query.paginate(page=page, per_page=page_size, error_out=False)# 构建用户数据users = []for user in pagination.items:user_data = {'id': user.id,'username': user.username,'realName': user.realName or '','email': user.email or '','is_active': user.is_active,'groups': [{'id': group.id, 'name': group.name} for group in user.groups],'date_joined': user.date_joined.isoformat() if user.date_joined else None,'last_login': user.last_login.isoformat() if user.last_login else None}users.append(user_data)return jsonify({'code': 0,'data': {'items': users,'total': pagination.total,'page': page,'pageSize': page_size}}), 200except Exception as e:return jsonify({'code': 500, 'message': str(e)}), 500

3. 角色管理系统

角色管理

角色权限管理
@role_bp.route('/role/<int:role_id>/permissions', methods=['GET'])
@jwt_required()
def get_role_permissions(role_id):"""获取角色权限"""try:group = Group.query.get(role_id)if not group:return jsonify({'code': 404, 'message': '角色不存在'}), 404# 获取所有权限all_permissions = Permission.query.all()role_permissions = group.permissionsreturn jsonify({'code': 0,'data': {'all_permissions': [{'id': perm.id,'name': perm.name,'codename': perm.codename,'content_type': f"{perm.content_type.app_label}.{perm.content_type.model}" if perm.content_type else f"content_type_{perm.content_type_id}"} for perm in all_permissions],'role_permissions': [{'id': perm.id,'name': perm.name,'codename': perm.codename,'content_type': f"{perm.content_type.app_label}.{perm.content_type.model}" if perm.content_type else f"content_type_{perm.content_type_id}"} for perm in role_permissions]}}), 200except Exception as e:return jsonify({'code': 500, 'message': str(e)}), 500

4. 菜单管理系统

菜单管理

菜单树形结构
@menu_bp.route('/menu/list', methods=['GET'])
@jwt_required()
def get_menu_list():"""获取菜单列表(扁平化,用于管理界面)"""try:# 获取所有菜单,按order排序all_menus = Menu.query.order_by(Menu.order).all()def build_flat_menu_list(menus):"""构建扁平化的菜单列表,包含父子关系"""flat_list = []for menu in menus:menu_data = {'id': menu.id,'name': menu.name,'path': menu.path,'component': menu.component,'redirect': menu.redirect,'meta': menu.meta or {},'parent': menu.parent_id,'parent_name': menu.parent.name if menu.parent else None,'order': menu.order,'groups': [{'id': group.id, 'name': group.name} for group in menu.groups],'children': []}# 递归处理子菜单children = Menu.query.filter_by(parent_id=menu.id).order_by(Menu.order).all()for child in children:child_data = build_flat_menu_list([child])[0]menu_data['children'].append(child_data)flat_list.append(menu_data)return flat_list# 构建扁平化菜单列表menu_list = build_flat_menu_list(all_menus)return jsonify({'code': 0,'data': menu_list}), 200except Exception as e:return jsonify({'code': 500, 'message': str(e)}), 500

5. 个人信息管理

个人信息

用户信息更新
@user_bp.route('/user/update-info', methods=['POST'])
@jwt_required()
def update_user_info():"""更新用户信息"""try:user_id = get_jwt_identity()user = User.query.get(int(user_id))if not user:return jsonify({'code': 404, 'message': '用户不存在'}), 404data = request.get_json()# 更新允许的字段if 'realName' in data:user.realName = data['realName']if 'phone' in data:user.phone = data['phone']if 'email' in data:user.email = data['email']if 'desc' in data:user.desc = data['desc']if 'gender' in data:user.gender = data['gender']db.session.commit()return jsonify({'code': 0,'message': '个人信息更新成功'}), 200except Exception as e:db.session.rollback()return jsonify({'code': 500, 'message': str(e)}), 500

6. 账户管理

账户管理

密码修改功能
@user_bp.route('/user/change-password', methods=['POST'])
@jwt_required()
def change_password():"""修改密码"""try:user_id = get_jwt_identity()user = User.query.get(int(user_id))if not user:return jsonify({'code': 404, 'message': '用户不存在'}), 404data = request.get_json()old_password = data.get('oldPassword')new_password = data.get('newPassword')if not old_password or not new_password:return jsonify({'code': 400, 'message': '旧密码和新密码不能为空'}), 400if not user.check_password(old_password):return jsonify({'code': 400, 'message': '旧密码错误'}), 400user.set_password(new_password)db.session.commit()return jsonify({'code': 0,'message': '密码修改成功'}), 200except Exception as e:db.session.rollback()return jsonify({'code': 500, 'message': str(e)}), 500

数据库设计

核心数据模型

class User(db.Model, UserMixin):"""用户模型"""__tablename__ = 'app_user'id = db.Column(db.BigInteger, primary_key=True, autoincrement=True)username = db.Column(db.String(150), unique=True, nullable=False)password = db.Column(db.String(128), nullable=False)email = db.Column(db.String(254), nullable=True)realName = db.Column(db.String(150), nullable=True)phone = db.Column(db.String(150), nullable=True)avatar = db.Column(db.String(255), nullable=True)gender = db.Column(db.String(1), nullable=True)desc = db.Column(db.Text, nullable=True)is_active = db.Column(db.Boolean, default=True)is_staff = db.Column(db.Boolean, default=False)is_superuser = db.Column(db.Boolean, default=False)date_joined = db.Column(db.DateTime, default=datetime.utcnow)last_login = db.Column(db.DateTime, nullable=True)# 关系groups = db.relationship('Group', secondary='app_user_groups', backref='users')user_permissions = db.relationship('Permission', secondary='app_user_user_permissions', backref='users')class Group(db.Model):"""用户组模型"""__tablename__ = 'auth_group'id = db.Column(db.Integer, primary_key=True, autoincrement=True)name = db.Column(db.String(150), unique=True, nullable=False)# 关系permissions = db.relationship('Permission', secondary='auth_group_permissions', backref='groups')class Permission(db.Model):"""权限模型"""__tablename__ = 'auth_permission'id = db.Column(db.Integer, primary_key=True, autoincrement=True)name = db.Column(db.String(255), nullable=False)content_type_id = db.Column(db.Integer, db.ForeignKey('django_content_type.id'), nullable=False)codename = db.Column(db.String(100), nullable=False)class Menu(db.Model):"""菜单模型"""__tablename__ = 'app_menu'id = db.Column(db.BigInteger, primary_key=True, autoincrement=True)name = db.Column(db.String(100), nullable=False)path = db.Column(db.String(255), nullable=False)component = db.Column(db.String(255), nullable=True)redirect = db.Column(db.String(255), nullable=True)meta = db.Column(db.JSON, default=dict)parent_id = db.Column(db.BigInteger, db.ForeignKey('app_menu.id'), nullable=True)order = db.Column(db.Integer, default=0)# 关系parent = db.relationship('Menu', remote_side=[id], backref='children')groups = db.relationship('Group', secondary='app_menu_groups', backref='menus')

前端实现

1. 状态管理

使用Pinia进行状态管理:

// store/modules/user.ts
export const useUserStore = defineStore("user", {state: (): UserState => ({username: storageLocal().getItem<DataInfo<number>>(userKey)?.username ?? "",nickname: storageLocal().getItem<DataInfo<number>>(userKey)?.nickname ?? "",avatar: storageLocal().getItem<DataInfo<number>>(userKey)?.avatar ?? "",roles: storageLocal().getItem<DataInfo<number>>(userKey)?.roles ?? [],permissions: storageLocal().getItem<DataInfo<number>>(userKey)?.permissions ?? []}),actions: {/** 登录 */async loginByUsername(data: LoginForm) {return new Promise<LoginResult>((resolve, reject) => {getLogin(data).then(data => {if (data?.code === 0) {setToken(data.data);resolve(data);}}).catch(error => {reject(error);});});}}
});

2. HTTP请求拦截

// utils/http/index.ts
private httpInterceptorsRequest(): void {PureHttp.axiosInstance.interceptors.request.use(async (config: PureHttpRequestConfig): Promise<any> => {const whiteList = ["/api/auth/refresh", "/api/auth/login", "/api/auth/register"];return whiteList.some(url => config.url.endsWith(url))? config: new Promise(resolve => {const data = getToken();if (data) {const now = new Date().getTime();const expired = parseInt(data.expires) - now <= 0;if (expired) {// token过期刷新逻辑useUserStoreHook().handRefreshToken().then(res => {const token = res.data;config.headers["Authorization"] = formatToken(token);resolve(config);});} else {config.headers["Authorization"] = formatToken(data.accessToken);resolve(config);}} else {resolve(config);}});});
}

3. 路由权限控制

// router/index.ts
router.beforeEach(async (to, _from, next) => {const userStore = useUserStoreHook();const token = getToken();if (token) {if (to.path === "/login") {next({ path: "/" });} else {if (userStore.roles.length === 0) {try {await userStore.getUserInfo();next({ ...to, replace: true });} catch {userStore.resetState();next("/login");}} else {next();}}} else {if (whiteList.indexOf(to.path) !== -1) {next();} else {next("/login");}}
});

部署配置

1. 后端部署

环境要求
  • Python 3.8+
  • MySQL 5.7+
  • Redis (可选)
安装依赖
cd Flask-Backend
pip install -r requirements.txt
配置数据库
# config.py
class Config:SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://root:password@localhost:3306/database_name?charset=utf8mb4'JWT_SECRET_KEY = 'your-secret-key'SECRET_KEY = 'your-flask-secret-key'
启动服务
python run.py

2. 前端部署

环境要求
  • Node.js 16+
  • pnpm (推荐) 或 npm
安装依赖
cd Frontend
pnpm install
开发环境启动
pnpm dev
生产环境构建
pnpm build

系统截图展示

首页

首页

系统首页展示了整体的数据概览和快速操作入口,包括用户统计、系统状态等信息。

系统设置

系统设置

系统设置页面提供了系统级别的配置选项,包括基本设置、安全配置等。

技术亮点

1. 完全兼容Django后端

  • 数据库表结构完全一致
  • API接口格式完全匹配
  • 密码哈希算法兼容
  • 权限系统完全对应

2. 现代化技术栈

  • Flask 3.0最新版本
  • Vue 3 Composition API
  • TypeScript类型安全
  • Element Plus组件库

3. 安全性保障

  • JWT身份认证
  • 密码加密存储
  • 跨域安全配置
  • 权限细粒度控制

4. 开发体验优化

  • 热重载开发
  • TypeScript类型提示
  • 统一的错误处理
  • 完整的日志记录

高级技术实现

1. 权限管理系统

细粒度权限控制

系统实现了基于Django权限模型的细粒度权限控制:

# Flask-Backend/app/permission.py
@permission_bp.route('/permission/list', methods=['GET'])
@jwt_required()
def get_permission_list():"""获取权限列表"""try:user_id = get_jwt_identity()current_user = User.query.get(int(user_id))if not current_user:return jsonify({'code': 404, 'message': '用户不存在'}), 404# 获取所有权限permissions = Permission.query.all()permissions_data = []for perm in permissions:perm_data = {'id': perm.id,'name': perm.name,'codename': perm.codename,'content_type': perm.content_type_id}permissions_data.append(perm_data)return jsonify({'code': 0,'data': permissions_data}), 200except Exception as e:return jsonify({'code': 500, 'message': str(e)}), 500
权限验证装饰器
def permission_required(permission_codename):"""权限验证装饰器"""def decorator(f):@wraps(f)@jwt_required()def decorated_function(*args, **kwargs):user_id = get_jwt_identity()user = User.query.get(int(user_id))if not user or not user.has_perm(permission_codename):return jsonify({'code': 403, 'message': '权限不足'}), 403return f(*args, **kwargs)return decorated_functionreturn decorator

2. 通知系统

实时通知功能

系统实现了完整的通知管理功能,支持多种通知类型:

# Flask-Backend/app/notification.py
@notification_bp.route('/notification/list', methods=['GET'])
@jwt_required()
def get_notification_list():"""获取通知列表"""try:user_id = get_jwt_identity()user = User.query.get(user_id)if not user:return jsonify({'error': '用户不存在'}), 404page = request.args.get('page', 1, type=int)per_page = request.args.get('per_page', 20, type=int)notification_type = request.args.get('type', '')# 获取用户收到的通知query = Notification.query.join(Notification.recipients).filter(Notification.recipients.any(id=user_id))if notification_type:query = query.filter(Notification.notification_type == notification_type)pagination = query.order_by(Notification.created_at.desc()).paginate(page=page, per_page=per_page, error_out=False)notifications = []for notification in pagination.items:# 获取通知状态status = NotificationStatus.query.filter_by(notification_id=notification.id, user_id=user_id).first()notifications.append({'id': notification.id,'title': notification.title,'message': notification.message,'notification_type': notification.notification_type,'link': notification.link,'created_at': notification.created_at.isoformat(),'time_ago': notification.time_ago,'is_read': status.is_read if status else False,'sender': {'id': notification.sender.id,'username': notification.sender.username,'realName': notification.sender.realName} if notification.sender else None})return jsonify({'count': pagination.total,'next': pagination.next_num if pagination.has_next else None,'previous': pagination.prev_num if pagination.has_prev else None,'results': notifications}), 200except Exception as e:return jsonify({'error': str(e)}), 500

3. 文件上传系统

头像上传功能
@user_bp.route('/user/upload-avatar', methods=['POST'])
@jwt_required()
def upload_avatar():"""上传用户头像"""try:user_id = get_jwt_identity()user = User.query.get(int(user_id))if not user:return jsonify({'code': 404, 'message': '用户不存在'}), 404if 'file' not in request.files:return jsonify({'code': 400, 'message': '没有选择文件'}), 400file = request.files['file']if file.filename == '':return jsonify({'code': 400, 'message': '没有选择文件'}), 400if file and allowed_file(file.filename):filename = secure_filename(file.filename)# 生成唯一文件名unique_filename = f"{user.id}_{datetime.now().strftime('%Y%m%d_%H%M%S')}_{filename}"file_path = os.path.join(current_app.config['UPLOAD_FOLDER'], 'avatars', unique_filename)# 确保目录存在os.makedirs(os.path.dirname(file_path), exist_ok=True)# 保存文件file.save(file_path)# 更新用户头像路径user.avatar = f"avatars/{unique_filename}"db.session.commit()return jsonify({'code': 0,'data': {'avatar': user.avatar,'url': f"/media/{user.avatar}"},'message': '头像上传成功'}), 200else:return jsonify({'code': 400, 'message': '不支持的文件类型'}), 400except Exception as e:db.session.rollback()return jsonify({'code': 500, 'message': str(e)}), 500

4. 数据库连接池优化

连接池配置
# Flask-Backend/config.py
class Config:"""基础配置类"""# 数据库配置SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://root:123456@127.0.0.1:3306/Django_Pure?charset=utf8mb4'SQLALCHEMY_TRACK_MODIFICATIONS = FalseSQLALCHEMY_ENGINE_OPTIONS = {'pool_size': 10,          # 连接池大小'pool_recycle': 3600,     # 连接回收时间(秒)'pool_pre_ping': True     # 连接前预检查}# JWT配置JWT_SECRET_KEY = 'jwt-secret-key-1723&ea+jkt1c(rf2ez6ur+m)%olhfu@v(i-@95&xu#$o^rx'JWT_ACCESS_TOKEN_EXPIRES = timedelta(minutes=30)JWT_REFRESH_TOKEN_EXPIRES = timedelta(days=7)JWT_TOKEN_LOCATION = ['headers']JWT_HEADER_NAME = 'Authorization'JWT_HEADER_TYPE = 'Bearer'# 文件上传配置UPLOAD_FOLDER = 'media'MAX_CONTENT_LENGTH = 16 * 1024 * 1024  # 16MBALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif'}# CORS配置CORS_ORIGINS = ["http://127.0.0.1:5777","http://localhost:5777"]CORS_SUPPORTS_CREDENTIALS = True# 分页配置ITEMS_PER_PAGE = 10

5. 错误处理机制

统一错误处理
# Flask-Backend/app/errors.py
from flask import jsonify
from flask_jwt_extended import JWTManagerdef register_error_handlers(app):"""注册错误处理器"""@app.errorhandler(400)def bad_request(error):return jsonify({'code': 400,'message': '请求参数错误'}), 400@app.errorhandler(401)def unauthorized(error):return jsonify({'code': 401,'message': '未授权访问'}), 401@app.errorhandler(403)def forbidden(error):return jsonify({'code': 403,'message': '权限不足'}), 403@app.errorhandler(404)def not_found(error):return jsonify({'code': 404,'message': '资源不存在'}), 404@app.errorhandler(500)def internal_error(error):return jsonify({'code': 500,'message': '服务器内部错误'}), 500# JWT错误处理@JWTManager.expired_token_loaderdef expired_token_callback(jwt_header, jwt_payload):return jsonify({'code': 401,'message': 'Token已过期'}), 401@JWTManager.invalid_token_loaderdef invalid_token_callback(error):return jsonify({'code': 401,'message': '无效的Token'}), 401@JWTManager.unauthorized_loaderdef missing_token_callback(error):return jsonify({'code': 401,'message': '缺少Authorization头'}), 401

6. 性能优化策略

数据库查询优化
# 使用预加载减少N+1查询问题
def get_user_with_relations(user_id):"""获取用户及其关联数据"""return User.query.options(db.joinedload(User.groups),db.joinedload(User.user_permissions)).get(user_id)# 使用索引优化查询
class User(db.Model):__tablename__ = 'app_user'# 为常用查询字段添加索引username = db.Column(db.String(150), unique=True, nullable=False, index=True)email = db.Column(db.String(254), nullable=True, index=True)is_active = db.Column(db.Boolean, default=True, index=True)
缓存策略
from flask_caching import Cachecache = Cache()# 缓存用户信息
@cache.memoize(timeout=300)  # 5分钟缓存
def get_user_info(user_id):"""获取用户信息(带缓存)"""return User.query.get(user_id)# 缓存菜单数据
@cache.memoize(timeout=600)  # 10分钟缓存
def get_menu_tree():"""获取菜单树(带缓存)"""return build_menu_tree()

7. 安全防护措施

密码安全
# Flask-Backend/app/utils.py
def django_password_hash(password, salt=None):"""使用Django的pbkdf2_sha256方法生成密码哈希"""if salt is None:# 生成随机的bytes类型的saltsalt = secrets.token_bytes(12)# 使用pbkdf2_sha256算法hash_obj = pbkdf2_sha256.hash(password, salt=salt)return hash_objdef check_password_compatible(password, hashed_password):"""兼容性密码验证,支持Django和Flask格式"""print(f"检查密码哈希格式: {hashed_password[:50]}...")print(f"是否为Django格式: {is_django_password(hashed_password)}")if is_django_password(hashed_password):print("使用Django密码验证")return django_password_verify(password, hashed_password)else:print("使用Flask密码验证")return check_password_hash(hashed_password, password)
输入验证
from marshmallow import Schema, fields, validateclass UserCreateSchema(Schema):"""用户创建验证模式"""username = fields.Str(required=True, validate=[validate.Length(min=3, max=150),validate.Regexp(r'^[a-zA-Z0-9_]+$', error='用户名只能包含字母、数字和下划线')])email = fields.Email(required=True)password = fields.Str(required=True, validate=validate.Length(min=6))realName = fields.Str(validate=validate.Length(max=150))phone = fields.Str(validate=validate.Regexp(r'^1[3-9]\d{9}$', error='手机号格式不正确'))def validate_user_data(data):"""验证用户数据"""schema = UserCreateSchema()try:return schema.load(data)except ValidationError as err:return None, err.messages

8. 日志系统

结构化日志
import logging
from logging.handlers import RotatingFileHandler
import jsonclass JSONFormatter(logging.Formatter):"""JSON格式化器"""def format(self, record):log_entry = {'timestamp': self.formatTime(record),'level': record.levelname,'logger': record.name,'message': record.getMessage(),'module': record.module,'function': record.funcName,'line': record.lineno}if record.exc_info:log_entry['exception'] = self.formatException(record.exc_info)return json.dumps(log_entry, ensure_ascii=False)def setup_logging(app):"""设置日志"""if not app.debug:# 文件日志file_handler = RotatingFileHandler('logs/app.log', maxBytes=10240000,  # 10MBbackupCount=10)file_handler.setFormatter(JSONFormatter())file_handler.setLevel(logging.INFO)app.logger.addHandler(file_handler)# 控制台日志console_handler = logging.StreamHandler()console_handler.setFormatter(JSONFormatter())console_handler.setLevel(logging.INFO)app.logger.addHandler(console_handler)app.logger.setLevel(logging.INFO)app.logger.info('应用启动')

9. API文档生成

Swagger集成
from flask_restx import Api, Resource, fieldsapi = Api(app, doc='/docs/', title='Flask Admin API', version='1.0')# 定义数据模型
user_model = api.model('User', {'id': fields.Integer(description='用户ID'),'username': fields.String(description='用户名'),'email': fields.String(description='邮箱'),'realName': fields.String(description='真实姓名'),'is_active': fields.Boolean(description='是否激活')
})# 定义API端点
@api.route('/users')
class UserList(Resource):@api.doc('get_users')@api.marshal_list_with(user_model)def get(self):"""获取用户列表"""users = User.query.all()return users

10. 测试策略

单元测试
import unittest
from app import create_app, db
from app.models import Userclass UserModelTestCase(unittest.TestCase):def setUp(self):self.app = create_app('testing')self.app_context = self.app.app_context()self.app_context.push()db.create_all()def tearDown(self):db.session.remove()db.drop_all()self.app_context.pop()def test_user_creation(self):"""测试用户创建"""user = User(username='testuser',email='test@example.com',realName='测试用户')user.set_password('password123')db.session.add(user)db.session.commit()self.assertTrue(user.check_password('password123'))self.assertFalse(user.check_password('wrongpassword'))def test_user_authentication(self):"""测试用户认证"""user = User.query.filter_by(username='testuser').first()self.assertIsNotNone(user)self.assertTrue(user.check_password('password123'))
集成测试
class AuthAPITestCase(unittest.TestCase):def setUp(self):self.app = create_app('testing')self.client = self.app.test_client()self.app_context = self.app.app_context()self.app_context.push()db.create_all()# 创建测试用户user = User(username='testuser',email='test@example.com',realName='测试用户')user.set_password('password123')db.session.add(user)db.session.commit()def test_login_success(self):"""测试登录成功"""response = self.client.post('/api/auth/login', json={'username': 'testuser','password': 'password123'})self.assertEqual(response.status_code, 200)data = response.get_json()self.assertEqual(data['code'], 0)self.assertIn('accessToken', data['data'])def test_login_failure(self):"""测试登录失败"""response = self.client.post('/api/auth/login', json={'username': 'testuser','password': 'wrongpassword'})self.assertEqual(response.status_code, 401)data = response.get_json()self.assertEqual(data['code'], 401)

11. 监控和运维

健康检查
@api.route('/health')
class HealthCheck(Resource):def get(self):"""健康检查"""try:# 检查数据库连接db.session.execute('SELECT 1')db_status = 'healthy'except Exception as e:db_status = f'unhealthy: {str(e)}'return {'status': 'healthy' if db_status == 'healthy' else 'unhealthy','database': db_status,'timestamp': datetime.utcnow().isoformat(),'version': '1.0.0'}
性能监控
from flask import g
import time@app.before_request
def before_request():g.start_time = time.time()@app.after_request
def after_request(response):if hasattr(g, 'start_time'):duration = time.time() - g.start_timeresponse.headers['X-Response-Time'] = f'{duration:.3f}s'# 记录慢请求if duration > 1.0:  # 超过1秒的请求app.logger.warning(f'Slow request: {request.path} took {duration:.3f}s')return response

12. 部署最佳实践

Docker化部署
# Dockerfile
FROM python:3.9-slimWORKDIR /app# 安装系统依赖
RUN apt-get update && apt-get install -y \gcc \default-libmysqlclient-dev \pkg-config \&& rm -rf /var/lib/apt/lists/*# 复制依赖文件
COPY requirements.txt .# 安装Python依赖
RUN pip install --no-cache-dir -r requirements.txt# 复制应用代码
COPY . .# 创建非root用户
RUN useradd --create-home --shell /bin/bash app \&& chown -R app:app /app
USER app# 暴露端口
EXPOSE 5000# 启动命令
CMD ["gunicorn", "--bind", "0.0.0.0:5000", "--workers", "4", "run:app"]
Docker Compose配置
# docker-compose.yml
version: '3.8'services:web:build: .ports:- "5000:5000"environment:- FLASK_ENV=production- DATABASE_URL=mysql+pymysql://root:password@db:3306/flask_admindepends_on:- db- redisvolumes:- ./media:/app/media- ./logs:/app/logsdb:image: mysql:8.0environment:MYSQL_ROOT_PASSWORD: passwordMYSQL_DATABASE: flask_adminvolumes:- mysql_data:/var/lib/mysqlports:- "3306:3306"redis:image: redis:7-alpineports:- "6379:6379"nginx:image: nginx:alpineports:- "80:80"- "443:443"volumes:- ./nginx.conf:/etc/nginx/nginx.conf- ./ssl:/etc/nginx/ssldepends_on:- webvolumes:mysql_data:

13. 性能调优建议

数据库优化
-- 创建索引
CREATE INDEX idx_user_username ON app_user(username);
CREATE INDEX idx_user_email ON app_user(email);
CREATE INDEX idx_user_is_active ON app_user(is_active);
CREATE INDEX idx_notification_created_at ON app_notification(created_at);
CREATE INDEX idx_notification_recipients ON app_notification_recipients(user_id);-- 查询优化
EXPLAIN SELECT * FROM app_user WHERE username = 'admin';
EXPLAIN SELECT * FROM app_notification n 
JOIN app_notification_recipients nr ON n.id = nr.notification_id 
WHERE nr.user_id = 1 ORDER BY n.created_at DESC;
应用层优化
# 使用连接池
from sqlalchemy.pool import QueuePoolengine = create_engine(DATABASE_URL,poolclass=QueuePool,pool_size=20,max_overflow=30,pool_pre_ping=True,pool_recycle=3600
)# 使用缓存
from flask_caching import Cachecache = Cache(app, config={'CACHE_TYPE': 'redis','CACHE_REDIS_URL': 'redis://localhost:6379/0','CACHE_DEFAULT_TIMEOUT': 300
})# 异步任务处理
from celery import Celerycelery = Celery('flask_admin', broker='redis://localhost:6379/1')@celery.task
def send_notification_async(notification_id):"""异步发送通知"""# 发送通知逻辑pass

技术亮点

1. 现代化技术栈

  • Flask 3.0最新版本
  • Vue 3 Composition API
  • TypeScript类型安全
  • Element Plus组件库

2. 安全性保障

  • JWT身份认证
  • 密码加密存储
  • 跨域安全配置
  • 权限细粒度控制

3. 开发体验优化

  • 热重载开发
  • TypeScript类型提示
  • 统一的错误处理
  • 完整的日志记录

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/95438.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/95438.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

51c视觉~3D~合集5

自己的原文哦~ https://blog.51cto.com/whaosoft/14165531 #AnimateAnyMesh 文本驱动通用网格动画新范式&#xff0c;实现高效高质量4D内容生成 4D 内容生成&#xff0c;即包含时间维度信息的 3D 内容创建&#xff0c;在 VR/AR、游戏等领域具有广阔的应用前景。…

开悟篇Docker从零到实战一篇文章搞定

目录 一:概述 1:why docker 2:Docker是什么? 3:Docker核心概念 二:初步体验 1:Docker核心架构图 2:准备工作 1:服务器 2:Docker安装 3:阿里云docker安装 4:镜像加速 三:Docker命令和帮助文档的使用 1:帮助文档 2:镜像的基本操作 1:查看本地…

LINUX驱动篇(二)驱动开发

系列文章目录 文章目录系列文章目录总结介绍字符设备驱动工作原理驱动框架加载卸载注册注销设备号详解打开关闭等操作实例分析led驱动编写地址映射LED驱动改进驱动方式总结自动注册注销设备号自动创建设备节点设备树设备树LED驱动实验pinctrl和gpio并发和竞争原子操作自旋锁块设…

【工具】开源大屏设计器 自用整理

【工具】开源大屏设计器 自用整理 GoView低代码数据可视化 GoView 说明文档 | 低代码数据可视化开发平台 JimuReport积木报表(免费报表工具) https://github.com/jeecgboot/JimuReport 「数据可视化&#xff1a;报表、大屏、数据看板」积木报表是一款类Excel操作风格&#xf…

.NetCore MVC

这个是我自己记得笔记&#xff0c;最好有点基础看我的。 html 辅助标签 Html.DropList 分布视图 使用 RenderPartialAsync 呈现分部视图。 此方法不返回 IHtmlContent。 它将呈现的输出直接流式传输到响应。 因为该方法不返回结果&#xff0c;所以必须在 Razor 代码块内调用它…

@GitLab 介绍部署使用详细指南

文章目录**GitLab 介绍&部署&使用详细指南****1. GitLab 介绍与核心概念****1.1 什么是 GitLab&#xff1f;****1.2 核心特性****1.3 版本区别****2. 部署指南 (以 Ubuntu 22.04 LTS 为例)****2.1 环境准备****2.2 安装步骤****2.3 重要配置文件****3. 基本使用入门***…

如何通过 AI IDE 集成开发工具快速生成简易留言板系统

在当今快速迭代的软件开发环境中&#xff0c;AI 辅助编程工具已经成为开发者提高效率的重要手段。本文将详细介绍如何利用 AI IDE 集成开发工具快速构建一个功能完整的简易留言板系统&#xff0c;涵盖从需求分析到部署上线的全过程&#xff0c;并提供完整代码、流程图、Prompt …

机器学习:从技术原理到实践应用的深度解析

目录引言一.什么是机器学习&#xff08;ML&#xff09;&#xff1f;——从技术本质到核心目标1.与传统编程的本质区别&#xff1a;规则的“来源不同”2.核心目标&#xff1a;在“偏差-方差权衡”下优化性能指标二.机器学习的核心分类——基于“数据标签”与“学习范式”的技术划…

[muduo网络库]-muduo库TcpServer类解析

本贴用于记录muduo库的学习过程&#xff0c;以下是关于TcpServer的个人理解。 TcpServer内含Acceptor、threadpool等类&#xff0c;算是把主线程所有要做的事封装了起来。 重要成员变量 EventLoop *loop_; // baseloop 用户自定义的loopconst std::string ipPort_;const std…

工作两年,最后从css转向tailwind了!

菜鸟上班已经两年了&#xff0c;从一个对技术充满热情的小伙子&#xff0c;变成了一个职场老鸟了。自以为自己在不停的学习&#xff0c;但是其实就是学一些零碎的知识点&#xff0c;比如&#xff1a;vue中什么东西没见过、js什么特性没用过、css新出了个啥 …… 菜鸟感觉自己也…

macOS 更新后找不到钥匙串访问工具的解决方案

macOS 更新后找不到钥匙串访问工具的解决方案 随着macOS的不断更新&#xff0c;一些系统工具的位置可能会发生变化&#xff0c;给用户带来不便。钥匙串访问&#xff08;Keychain Access&#xff09;是macOS中一个非常重要的工具&#xff0c;用于管理密码、证书等敏感信息。最近…

深入理解Go 与 PHP 在参数传递上的核心区别

$run_return_data []; $ret $this->handleData($event_req_info, $run_return_data); public function handleData($event_req_info, &$run_return_data): array {$run_return_data [ //使用引用变量返回数据shop_id > $shop_id,request_id > $request_…

【Dify智能体】2025 最新版Linux部署Dify教程(Ubuntu)

一、前言 Dify 是一款开源的智能体工作流平台,可以用来快速构建 AI 应用。相比手动搭建复杂的依赖环境,Docker Compose 部署方式更简单、更快速、更稳定。本文将一步步带你在 Ubuntu 22.04 + Docker Compose v2 上安装 Dify,并给出常见问题与优化方案。 ps:如果还没有安装…

基础思想:动态规划与贪心算法

一、动态规划核心思想&#xff1a;将复杂问题分解为相互重叠的子问题&#xff0c;通过保存子问题的解来避免重复计算&#xff08;记忆化&#xff09;。动态规划需要通过子问题的最优解&#xff0c;推导出最终问题的最优解&#xff0c;因此这种方法特别注重子问题之间的转移关系…

使用生成对抗网络增强网络入侵检测性能

文章目录前言一、GAN 模型介绍二、研究方法1.数据集选择与处理2.IDS 基线模型构建3. GAN 模型设计与样本生成4.生成样本质量评估三、实验评估四、总结前言 网络入侵检测系统&#xff08;Network Intrusion Detection System, NIDS&#xff09;在保护关键数字基础设施免受网络威…

VR森林经营模拟体验带动旅游经济发展

将VR森林经营模拟体验作为一种独特的旅游项目&#xff0c;正逐渐成为旅游市场的新热点。游客们无需长途跋涉前往深山老林&#xff0c;只需在旅游景区的VR体验中心&#xff0c;戴上VR设备&#xff0c;就能开启一场奇妙的森林之旅。在虚拟森林中&#xff0c;他们可以尽情探索&…

Vue2存量项目国际化改造踩坑

Vue2存量项目国际化改造踩坑 一、背景 在各类业务场景中&#xff0c;国际化作为非常重要的一部分已经有非常多成熟的方案&#xff0c;但对于一些存量项目则存在非常的改造成本&#xff0c;本文将分享一个的Vue2项目国际化改造方案&#xff0c;通过自定义Webpack插件自动提取中文…

硬件开发(1)—单片机(1)

1.单片机相关概念1.CPU&#xff1a;中央处理器&#xff0c;数据运算、指令处理&#xff0c;CPU性能越高&#xff0c;完成指令处理和数据运算的速度越快核心&#xff1a;指令解码执行数据运算处理2.MCU&#xff1a;微控制器&#xff0c;集成度比较高&#xff0c;将所有功能集成到…

Elasticsearch面试精讲 Day 4:集群发现与节点角色

【Elasticsearch面试精讲 Day 4】集群发现与节点角色 在“Elasticsearch面试精讲”系列的第四天&#xff0c;我们将深入探讨Elasticsearch分布式架构中的核心机制——集群发现&#xff08;Cluster Discovery&#xff09;与节点角色&#xff08;Node Roles&#xff09;。这是构…

微信小程序长按识别图片二维码

提示&#xff1a;二维码图片才能显示识别菜单1.第一种方法添加属性&#xff1a;show-menu-by-longpress添加属性&#xff1a;show-menu-by-longpress <image src"{{shop.wx_qrcode}}" mode"widthFix" show-menu-by-longpress></image>2.第二种…