上传文件至 APP
This commit is contained in:
parent
706f6f5776
commit
b52da71197
93
APP/__init__.py
Normal file
93
APP/__init__.py
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
from flask import Flask, current_app
|
||||||
|
from flask_sqlalchemy import SQLAlchemy
|
||||||
|
from flask_login import LoginManager
|
||||||
|
from flask_migrate import Migrate
|
||||||
|
from flask_jwt_extended import JWTManager
|
||||||
|
from config import Config
|
||||||
|
import logging
|
||||||
|
from logging.handlers import RotatingFileHandler
|
||||||
|
import os
|
||||||
|
|
||||||
|
db = SQLAlchemy()
|
||||||
|
migrate = Migrate()
|
||||||
|
login_manager = LoginManager()
|
||||||
|
jwt = JWTManager()
|
||||||
|
|
||||||
|
def create_app(config_class=Config):
|
||||||
|
app = Flask(__name__)
|
||||||
|
app.config.from_object(config_class)
|
||||||
|
|
||||||
|
db.init_app(app)
|
||||||
|
migrate.init_app(app, db)
|
||||||
|
login_manager.init_app(app)
|
||||||
|
jwt.init_app(app)
|
||||||
|
|
||||||
|
# 设置日志记录器
|
||||||
|
if not app.debug:
|
||||||
|
if not os.path.exists('logs'):
|
||||||
|
os.mkdir('logs')
|
||||||
|
file_handler = RotatingFileHandler('logs/app.log', maxBytes=10240, backupCount=10, delay=True)
|
||||||
|
file_handler.setFormatter(logging.Formatter(
|
||||||
|
'%(asctime)s %(levelname)s: %(message)s [in %(pathname)s:%(lineno)d]'))
|
||||||
|
file_handler.setLevel(logging.INFO)
|
||||||
|
app.logger.addHandler(file_handler)
|
||||||
|
|
||||||
|
app.logger.setLevel(logging.INFO)
|
||||||
|
app.logger.info('Application startup')
|
||||||
|
|
||||||
|
from app import models
|
||||||
|
from app.models import RoleTemplate, Permission
|
||||||
|
|
||||||
|
def init_database():
|
||||||
|
db.create_all()
|
||||||
|
models.UserField.init_default_fields()
|
||||||
|
models.Department.init_default_departments()
|
||||||
|
models.Department.init_default_fields()
|
||||||
|
models.RoleField.init_default_fields()
|
||||||
|
models.Role.init_default_roles()
|
||||||
|
models.PermissionField.init_default_fields()
|
||||||
|
models.Permission.init_default_permissions()
|
||||||
|
|
||||||
|
def init_role_templates():
|
||||||
|
templates = [
|
||||||
|
{
|
||||||
|
'name': '管理员',
|
||||||
|
'description': '系统管理员,拥有所有权限',
|
||||||
|
'permissions': ['用户管理', '角色管理', '部门管理', '权限管理']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'name': '普通用户',
|
||||||
|
'description': '普通用户,拥有基本权限',
|
||||||
|
'permissions': ['查看个人信息', '修改个人信息']
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
for template in templates:
|
||||||
|
if not RoleTemplate.query.filter_by(name=template['name']).first():
|
||||||
|
permissions = [Permission.query.filter_by(name=p).first() for p in template['permissions']]
|
||||||
|
RoleTemplate.create_template(
|
||||||
|
name=template['name'],
|
||||||
|
description=template['description'],
|
||||||
|
permissions=[p for p in permissions if p]
|
||||||
|
)
|
||||||
|
|
||||||
|
with app.app_context():
|
||||||
|
init_database()
|
||||||
|
init_role_templates()
|
||||||
|
|
||||||
|
from app.auth import bp as auth_bp
|
||||||
|
app.register_blueprint(auth_bp)
|
||||||
|
|
||||||
|
from app.departments import bp as departments_bp
|
||||||
|
app.register_blueprint(departments_bp, url_prefix='/departments')
|
||||||
|
|
||||||
|
from app.roles import bp as roles_bp
|
||||||
|
app.register_blueprint(roles_bp, url_prefix='/roles')
|
||||||
|
|
||||||
|
from app.permissions import bp as permissions_bp
|
||||||
|
app.register_blueprint(permissions_bp, url_prefix='/permissions')
|
||||||
|
|
||||||
|
from app.users import bp as users_bp
|
||||||
|
app.register_blueprint(users_bp, url_prefix='/users')
|
||||||
|
|
||||||
|
return app
|
356
APP/auth.py
Normal file
356
APP/auth.py
Normal file
@ -0,0 +1,356 @@
|
|||||||
|
from flask import Blueprint, render_template, redirect, url_for, flash, request, jsonify, current_app
|
||||||
|
from flask_login import login_user, logout_user, login_required, current_user
|
||||||
|
from app import db
|
||||||
|
from app.models import User, UserField, UserLoginInfo, UserPassword, UserDetail, UserPasswordHistory
|
||||||
|
from urllib.parse import urlparse # 替换 werkzeug.urls import
|
||||||
|
import traceback
|
||||||
|
import random
|
||||||
|
import string
|
||||||
|
from flask_jwt_extended import create_access_token, create_refresh_token, jwt_required, get_jwt_identity, \
|
||||||
|
verify_jwt_in_request
|
||||||
|
from functools import wraps
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
|
bp = Blueprint('auth', __name__)
|
||||||
|
|
||||||
|
@bp.route('/logout')
|
||||||
|
def logout():
|
||||||
|
logout_user()
|
||||||
|
return redirect(url_for('index'))
|
||||||
|
|
||||||
|
@bp.route('/register', methods=['POST'])
|
||||||
|
def register():
|
||||||
|
current_app.logger.info('Received registration request')
|
||||||
|
data = request.get_json()
|
||||||
|
current_app.logger.info(f'Registration data: {data}')
|
||||||
|
|
||||||
|
email = data.get('email')
|
||||||
|
if email:
|
||||||
|
existing_user = User.find_by_username_or_email(email)
|
||||||
|
if existing_user:
|
||||||
|
return jsonify({'message': '该邮箱已被注册'}), 400
|
||||||
|
|
||||||
|
required_fields = ['first_name', 'last_name', 'id_number', 'phone', 'gender']
|
||||||
|
missing_fields = [field for field in required_fields if field not in data or not data[field]]
|
||||||
|
|
||||||
|
if missing_fields:
|
||||||
|
current_app.logger.warning(f'Registration failed: Missing required fields: {", ".join(missing_fields)}')
|
||||||
|
return jsonify({'message': f'以下字段是必填的: {", ".join(missing_fields)}'}), 400
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 生成用户名和密码
|
||||||
|
username = generate_username(data['first_name'], data['last_name'])
|
||||||
|
password = generate_password()
|
||||||
|
current_app.logger.info(f'Generated username: {username}')
|
||||||
|
|
||||||
|
user = User(username=username)
|
||||||
|
user.set_password(password)
|
||||||
|
db.session.add(user)
|
||||||
|
|
||||||
|
# 设置用户详细信息
|
||||||
|
for field in UserField.query.all():
|
||||||
|
if field.name in data:
|
||||||
|
user.set_detail(field.name, data[field.name])
|
||||||
|
current_app.logger.info(f'Set user detail: {field.name} = {data[field.name]}')
|
||||||
|
|
||||||
|
# 设置新用户标记
|
||||||
|
user.login_info.is_new_user = True
|
||||||
|
user.login_info.has_changed_initial_password = False
|
||||||
|
|
||||||
|
db.session.commit()
|
||||||
|
current_app.logger.info(f'User {username} registered successfully')
|
||||||
|
return jsonify({
|
||||||
|
'message': '注册成功',
|
||||||
|
'username': username,
|
||||||
|
'password': password
|
||||||
|
}), 201
|
||||||
|
except Exception as e:
|
||||||
|
current_app.logger.error(f'Registration error: {str(e)}')
|
||||||
|
current_app.logger.error(traceback.format_exc())
|
||||||
|
db.session.rollback()
|
||||||
|
return jsonify({'message': f'注册失败,错误: {str(e)}'}), 500
|
||||||
|
|
||||||
|
def generate_username(first_name, last_name):
|
||||||
|
base_username = f"{first_name.lower()} {last_name.lower()}"
|
||||||
|
username = base_username
|
||||||
|
counter = 1
|
||||||
|
while User.query.filter_by(username=username).first():
|
||||||
|
username = f"{base_username} {counter}"
|
||||||
|
counter += 1
|
||||||
|
return username
|
||||||
|
|
||||||
|
def generate_password():
|
||||||
|
characters = string.ascii_letters + string.digits + string.punctuation
|
||||||
|
return ''.join(random.choice(characters) for i in range(16))
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route('/login', methods=['POST'])
|
||||||
|
def login_api():
|
||||||
|
current_app.logger.info('Received login request')
|
||||||
|
data = request.get_json()
|
||||||
|
|
||||||
|
if not data:
|
||||||
|
current_app.logger.warning('Invalid request data')
|
||||||
|
return jsonify({'message': '无效的请求数据'}), 400
|
||||||
|
|
||||||
|
username_or_email = data.get('username')
|
||||||
|
password = data.get('password')
|
||||||
|
|
||||||
|
current_app.logger.info(f'Login attempt for user: {username_or_email}')
|
||||||
|
|
||||||
|
if not username_or_email or not password:
|
||||||
|
current_app.logger.warning('Missing username/email or password')
|
||||||
|
return jsonify({'message': '缺少用户名/邮箱或密码'}), 400
|
||||||
|
|
||||||
|
try:
|
||||||
|
user = User.find_by_username_or_email(username_or_email)
|
||||||
|
if user is None:
|
||||||
|
current_app.logger.warning(f'User not found: {username_or_email}')
|
||||||
|
return jsonify({'message': '无效的用户名/邮箱或密码'}), 401
|
||||||
|
|
||||||
|
if not user.check_password(password):
|
||||||
|
current_app.logger.warning(f'Invalid password for user: {username_or_email}')
|
||||||
|
return jsonify({'message': '无效的用户名/邮箱或密码'}), 401
|
||||||
|
|
||||||
|
current_app.logger.info(f'User authenticated: {username_or_email}')
|
||||||
|
current_app.logger.info(f'Is new user: {user.login_info.is_new_user}')
|
||||||
|
current_app.logger.info(f'Has changed initial password: {user.login_info.has_changed_initial_password}')
|
||||||
|
|
||||||
|
# 检查是否是新用户或未更改初始密码
|
||||||
|
if user.login_info.is_new_user or not user.login_info.has_changed_initial_password:
|
||||||
|
current_app.logger.info(f'New user or initial password not changed: {username_or_email}')
|
||||||
|
return jsonify({
|
||||||
|
'message': '首次登录,请修改密码',
|
||||||
|
'require_password_change': True
|
||||||
|
}), 403
|
||||||
|
|
||||||
|
# 检查密码是否过期
|
||||||
|
if user.needs_password_change():
|
||||||
|
current_app.logger.info(f'Password expired for user: {username_or_email}')
|
||||||
|
return jsonify({
|
||||||
|
'message': '密码已过期,请修改密码',
|
||||||
|
'require_password_change': True
|
||||||
|
}), 403
|
||||||
|
|
||||||
|
login_user(user)
|
||||||
|
user.login_info.update_login_info()
|
||||||
|
|
||||||
|
access_token = create_access_token(identity=user.id)
|
||||||
|
refresh_token = create_refresh_token(identity=user.id)
|
||||||
|
|
||||||
|
current_app.logger.info(f'User {user.username} logged in successfully')
|
||||||
|
|
||||||
|
# 获取用户详细信息
|
||||||
|
user_fields = UserField.query.all()
|
||||||
|
user_details = {}
|
||||||
|
for field in user_fields:
|
||||||
|
detail = UserDetail.query.filter_by(user_id=user.id, field_id=field.id).first()
|
||||||
|
user_details[field.name] = detail.value if detail else None
|
||||||
|
|
||||||
|
user_info = {
|
||||||
|
'id': user.id,
|
||||||
|
'username': user.username,
|
||||||
|
'primary_department': user.primary_department.name if user.primary_department else None,
|
||||||
|
'secondary_departments': [dept.name for dept in user.secondary_departments],
|
||||||
|
'roles': [role.name for role in user.roles],
|
||||||
|
'permissions': [perm.name for perm in user.get_all_permissions()], # 添加这行
|
||||||
|
'details': user_details,
|
||||||
|
'login_info': {
|
||||||
|
'register_time': user.login_info.register_time.isoformat(),
|
||||||
|
'login_count': user.login_info.login_count,
|
||||||
|
'last_login_time': user.login_info.last_login_time.isoformat() if user.login_info.last_login_time else None,
|
||||||
|
'is_new_user': user.login_info.is_new_user,
|
||||||
|
'has_changed_initial_password': user.login_info.has_changed_initial_password
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return jsonify({
|
||||||
|
'message': '登录成功',
|
||||||
|
'access_token': access_token,
|
||||||
|
'refresh_token': refresh_token,
|
||||||
|
'user_info': user_info
|
||||||
|
}), 200
|
||||||
|
except Exception as e:
|
||||||
|
current_app.logger.error(f'Login error: {str(e)}')
|
||||||
|
current_app.logger.error(traceback.format_exc())
|
||||||
|
return jsonify({'message': '登录失败,请稍后再试'}), 500
|
||||||
|
|
||||||
|
@bp.route('/refresh', methods=['POST'])
|
||||||
|
@jwt_required(refresh=True)
|
||||||
|
def refresh():
|
||||||
|
current_user_id = get_jwt_identity()
|
||||||
|
access_token = create_access_token(identity=current_user_id)
|
||||||
|
return jsonify({'access_token': access_token}), 200
|
||||||
|
|
||||||
|
@bp.route('/logout', methods=['POST'])
|
||||||
|
@login_required
|
||||||
|
def logout_api():
|
||||||
|
current_app.logger.info(f'User {current_user.username} logged out')
|
||||||
|
logout_user()
|
||||||
|
return jsonify({'message': '登出成功'}), 200
|
||||||
|
|
||||||
|
@bp.route('/profile', methods=['GET'])
|
||||||
|
@login_required
|
||||||
|
def profile():
|
||||||
|
current_app.logger.info(f'Profile requested for user {current_user.username}')
|
||||||
|
try:
|
||||||
|
profile_data = {
|
||||||
|
'username': current_user.username,
|
||||||
|
'login_info': {
|
||||||
|
'register_time': current_user.login_info.register_time.isoformat(),
|
||||||
|
'login_count': current_user.login_info.login_count,
|
||||||
|
'last_login_time': current_user.login_info.last_login_time.isoformat() if current_user.login_info.last_login_time else None,
|
||||||
|
'is_new_user': current_user.login_info.is_new_user
|
||||||
|
}
|
||||||
|
}
|
||||||
|
current_app.logger.info(f'Profile data retrieved for user {current_user.username}')
|
||||||
|
return jsonify(profile_data)
|
||||||
|
except Exception as e:
|
||||||
|
current_app.logger.error(f'Profile retrieval error for user {current_user.username}: {str(e)}')
|
||||||
|
current_app.logger.error(traceback.format_exc())
|
||||||
|
return jsonify({'message': '获取用户资料失败,请稍后再试'}), 500
|
||||||
|
|
||||||
|
@bp.route('/change_password', methods=['POST'])
|
||||||
|
@jwt_required()
|
||||||
|
def change_password():
|
||||||
|
current_app.logger.info('Received change password request')
|
||||||
|
current_user_id = get_jwt_identity()
|
||||||
|
data = request.get_json()
|
||||||
|
old_password = data.get('old_password')
|
||||||
|
new_password = data.get('new_password')
|
||||||
|
|
||||||
|
if not all([old_password, new_password]):
|
||||||
|
return jsonify({'message': '缺少必要的字段'}), 400
|
||||||
|
|
||||||
|
try:
|
||||||
|
user = User.query.get(current_user_id)
|
||||||
|
if not user:
|
||||||
|
return jsonify({'message': '用户不存在'}), 404
|
||||||
|
|
||||||
|
if not user.check_password(old_password):
|
||||||
|
return jsonify({'message': '旧密码不正确'}), 401
|
||||||
|
|
||||||
|
user.set_password(new_password)
|
||||||
|
user.login_info.has_changed_initial_password = True
|
||||||
|
|
||||||
|
# 更新密码修改历史
|
||||||
|
if user.password_history:
|
||||||
|
user.password_history.update_password_change()
|
||||||
|
else:
|
||||||
|
user.password_history = UserPasswordHistory(user=user)
|
||||||
|
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
current_app.logger.info(f'Password changed successfully for user {user.username}')
|
||||||
|
return jsonify({'message': '密码修改成功'}), 200
|
||||||
|
except Exception as e:
|
||||||
|
current_app.logger.error(f'Password change error: {str(e)}')
|
||||||
|
current_app.logger.error(traceback.format_exc())
|
||||||
|
db.session.rollback()
|
||||||
|
return jsonify({'message': '密码修改失败,请稍后再试'}), 500
|
||||||
|
|
||||||
|
def token_required(f):
|
||||||
|
@wraps(f)
|
||||||
|
def decorated(*args, **kwargs):
|
||||||
|
try:
|
||||||
|
# 验证 JWT token
|
||||||
|
verify_jwt_in_request()
|
||||||
|
# 获取当前用户的身份(通常是用户ID)
|
||||||
|
current_user_id = get_jwt_identity()
|
||||||
|
# 从数据库中获取用户对象
|
||||||
|
current_user = User.query.get(current_user_id)
|
||||||
|
if not current_user:
|
||||||
|
return jsonify({'message': '找不到用户'}), 404
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({'message': '无效的令牌'}), 401
|
||||||
|
# 将当前用户对象传递给被装饰的函数
|
||||||
|
return f(current_user, *args, **kwargs)
|
||||||
|
return decorated
|
||||||
|
|
||||||
|
@bp.route('/change_initial_password', methods=['POST'])
|
||||||
|
def change_initial_password():
|
||||||
|
data = request.get_json()
|
||||||
|
|
||||||
|
if not data:
|
||||||
|
return jsonify({'message': '缺少请求数据'}), 400
|
||||||
|
|
||||||
|
username_or_email = data.get('username_or_email') or data.get('username')
|
||||||
|
old_password = data.get('old_password')
|
||||||
|
new_password = data.get('new_password')
|
||||||
|
|
||||||
|
if not all([username_or_email, old_password, new_password]):
|
||||||
|
return jsonify({'message': '缺少必要的字段'}), 400
|
||||||
|
|
||||||
|
user = User.find_by_username_or_email(username_or_email)
|
||||||
|
if not user:
|
||||||
|
return jsonify({'message': '用户不存在'}), 404
|
||||||
|
|
||||||
|
if not user.check_password(old_password):
|
||||||
|
return jsonify({'message': '旧密码不正确'}), 401
|
||||||
|
|
||||||
|
if user.login_info.has_changed_initial_password:
|
||||||
|
return jsonify({'message': '初始密码已经被修改过'}), 400
|
||||||
|
|
||||||
|
user.set_password(new_password)
|
||||||
|
user.login_info.has_changed_initial_password = True
|
||||||
|
user.login_info.is_new_user = False
|
||||||
|
|
||||||
|
# 更新密码修改历史
|
||||||
|
if user.password_history:
|
||||||
|
user.password_history.update_password_change()
|
||||||
|
else:
|
||||||
|
user.password_history = UserPasswordHistory(user=user)
|
||||||
|
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
access_token = create_access_token(identity=user.id)
|
||||||
|
refresh_token = create_refresh_token(identity=user.id)
|
||||||
|
|
||||||
|
current_app.logger.info(f'Initial password changed for user: {user.username}')
|
||||||
|
current_app.logger.info(f'New user status: {user.login_info.is_new_user}')
|
||||||
|
current_app.logger.info(f'Has changed initial password status: {user.login_info.has_changed_initial_password}')
|
||||||
|
|
||||||
|
return jsonify({
|
||||||
|
'message': '密码修改成功,现在可以登录',
|
||||||
|
'access_token': access_token,
|
||||||
|
'refresh_token': refresh_token
|
||||||
|
}), 200
|
||||||
|
|
||||||
|
@bp.route('/current_user', methods=['GET'])
|
||||||
|
@jwt_required()
|
||||||
|
def get_current_user():
|
||||||
|
current_user_id = get_jwt_identity()
|
||||||
|
user = User.query.get(current_user_id)
|
||||||
|
|
||||||
|
if not user:
|
||||||
|
return jsonify({'message': '用户不存在'}), 404
|
||||||
|
|
||||||
|
try:
|
||||||
|
user_fields = UserField.query.all()
|
||||||
|
user_details = {}
|
||||||
|
for field in user_fields:
|
||||||
|
detail = UserDetail.query.filter_by(user_id=user.id, field_id=field.id).first()
|
||||||
|
user_details[field.name] = detail.value if detail else None
|
||||||
|
|
||||||
|
user_info = {
|
||||||
|
'id': user.id,
|
||||||
|
'username': user.username,
|
||||||
|
'details': user_details,
|
||||||
|
'primary_department': user.primary_department.name if user.primary_department else None,
|
||||||
|
'secondary_departments': [dept.name for dept in user.secondary_departments],
|
||||||
|
'roles': [role.name for role in user.roles],
|
||||||
|
'permissions': [perm.name for perm in user.get_all_permissions()],
|
||||||
|
'login_info': {
|
||||||
|
'register_time': user.login_info.register_time.isoformat(),
|
||||||
|
'login_count': user.login_info.login_count,
|
||||||
|
'last_login_time': user.login_info.last_login_time.isoformat() if user.login_info.last_login_time else None,
|
||||||
|
'is_new_user': user.login_info.is_new_user,
|
||||||
|
'has_changed_initial_password': user.login_info.has_changed_initial_password
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return jsonify(user_info), 200
|
||||||
|
except Exception as e:
|
||||||
|
current_app.logger.error(f'Error retrieving current user info: {str(e)}')
|
||||||
|
current_app.logger.error(traceback.format_exc())
|
||||||
|
return jsonify({'message': '获取用户信息失败,请稍后再试'}), 500
|
95
APP/authorization.py
Normal file
95
APP/authorization.py
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
from functools import wraps
|
||||||
|
from flask import jsonify, request, current_app
|
||||||
|
from flask_jwt_extended import get_jwt_identity, verify_jwt_in_request
|
||||||
|
from app.models import User, Permission
|
||||||
|
from .extensions import db
|
||||||
|
|
||||||
|
class Authorization:
|
||||||
|
@staticmethod
|
||||||
|
def check_permission(required_permission):
|
||||||
|
def decorator(f):
|
||||||
|
@wraps(f)
|
||||||
|
def decorated_function(*args, **kwargs):
|
||||||
|
from .models import User # 在函数内部导入以避免循环导入
|
||||||
|
current_user_id = get_jwt_identity()
|
||||||
|
user = User.query.get(current_user_id)
|
||||||
|
if not user:
|
||||||
|
return jsonify({"message": "User not found"}), 404
|
||||||
|
|
||||||
|
user_permissions = user.get_all_permissions()
|
||||||
|
if required_permission not in user_permissions:
|
||||||
|
return jsonify({"message": "Permission denied"}), 403
|
||||||
|
return f(*args, **kwargs)
|
||||||
|
return decorated_function
|
||||||
|
return decorator
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def check_role(required_role):
|
||||||
|
def decorator(f):
|
||||||
|
@wraps(f)
|
||||||
|
def decorated_function(*args, **kwargs):
|
||||||
|
current_user_id = get_jwt_identity()
|
||||||
|
user = User.query.get(current_user_id)
|
||||||
|
if not user:
|
||||||
|
return jsonify({"message": "User not found"}), 404
|
||||||
|
|
||||||
|
user_roles = [role.name for role in user.roles]
|
||||||
|
if required_role not in user_roles:
|
||||||
|
return jsonify({"message": "Role not authorized"}), 403
|
||||||
|
return f(*args, **kwargs)
|
||||||
|
return decorated_function
|
||||||
|
return decorator
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def check_department(required_department):
|
||||||
|
def decorator(f):
|
||||||
|
@wraps(f)
|
||||||
|
def decorated_function(*args, **kwargs):
|
||||||
|
current_user_id = get_jwt_identity()
|
||||||
|
user = User.query.get(current_user_id)
|
||||||
|
if not user:
|
||||||
|
return jsonify({"message": "User not found"}), 404
|
||||||
|
|
||||||
|
user_departments = [user.primary_department.name] + [dept.name for dept in user.secondary_departments]
|
||||||
|
if required_department not in user_departments:
|
||||||
|
return jsonify({"message": "Department not authorized"}), 403
|
||||||
|
return f(*args, **kwargs)
|
||||||
|
return decorated_function
|
||||||
|
return decorator
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def api_permission_required(permission):
|
||||||
|
def decorator(f):
|
||||||
|
@wraps(f)
|
||||||
|
def decorated_function(*args, **kwargs):
|
||||||
|
verify_jwt_in_request()
|
||||||
|
current_user_id = get_jwt_identity()
|
||||||
|
user = User.query.get(current_user_id)
|
||||||
|
if not user:
|
||||||
|
return jsonify({"message": "User not found"}), 404
|
||||||
|
|
||||||
|
user_permissions = user.get_all_permissions()
|
||||||
|
if permission not in user_permissions:
|
||||||
|
return jsonify({"message": "Permission denied"}), 403
|
||||||
|
return f(*args, **kwargs)
|
||||||
|
return decorated_function
|
||||||
|
return decorator
|
||||||
|
|
||||||
|
def init_permissions():
|
||||||
|
from .models import Permission # 在函数内部导入以避免循环导入
|
||||||
|
# 这个函数可以在应用启动时调用,用于初始化或更新权限
|
||||||
|
# 可以从配置文件、数据库或其他来源读取权限定义
|
||||||
|
permissions = [
|
||||||
|
"create_user", "edit_user", "delete_user",
|
||||||
|
"create_role", "edit_role", "delete_role",
|
||||||
|
"create_department", "edit_department", "delete_department",
|
||||||
|
# ... 其他权限 ...
|
||||||
|
]
|
||||||
|
for perm_name in permissions:
|
||||||
|
perm = Permission.query.filter_by(name=perm_name).first()
|
||||||
|
if not perm:
|
||||||
|
new_perm = Permission(name=perm_name)
|
||||||
|
db.session.add(new_perm)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
# 可以添加更多的辅助函数,比如检查复杂的权限组合等
|
16
APP/decorators.py
Normal file
16
APP/decorators.py
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
from functools import wraps
|
||||||
|
from flask import abort
|
||||||
|
from flask_login import current_user
|
||||||
|
|
||||||
|
def permission_required(permission):
|
||||||
|
def decorator(f):
|
||||||
|
@wraps(f)
|
||||||
|
def decorated_function(*args, **kwargs):
|
||||||
|
if not current_user.is_authenticated:
|
||||||
|
abort(403)
|
||||||
|
user_permissions = current_user.get_all_permissions()
|
||||||
|
if permission not in [p.name for p in user_permissions]:
|
||||||
|
abort(403)
|
||||||
|
return f(*args, **kwargs)
|
||||||
|
return decorated_function
|
||||||
|
return decorator
|
150
APP/departments.py
Normal file
150
APP/departments.py
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
from flask import Blueprint, request, jsonify
|
||||||
|
from app.models import Department, User, db
|
||||||
|
from sqlalchemy.exc import IntegrityError
|
||||||
|
|
||||||
|
bp = Blueprint('departments', __name__)
|
||||||
|
|
||||||
|
@bp.route('', methods=['POST'])
|
||||||
|
def create_department():
|
||||||
|
data = request.get_json()
|
||||||
|
|
||||||
|
if not data or 'name' not in data:
|
||||||
|
return jsonify({'message': '部门名称是必需的'}), 400
|
||||||
|
|
||||||
|
name = data['name']
|
||||||
|
|
||||||
|
# 检查部门名称是否已存在
|
||||||
|
if Department.query.filter_by(name=name).first():
|
||||||
|
return jsonify({'message': '部门名称已存在'}), 400
|
||||||
|
|
||||||
|
new_department = Department(name=name)
|
||||||
|
db.session.add(new_department)
|
||||||
|
db.session.flush() # 这会给 new_department 分配一个 id
|
||||||
|
|
||||||
|
# 如果提供了其他字段,设置它们
|
||||||
|
for field in ['department_code', 'manager', 'phone', 'email', 'location']:
|
||||||
|
if field in data:
|
||||||
|
new_department.set_detail(field, data[field])
|
||||||
|
|
||||||
|
try:
|
||||||
|
db.session.commit()
|
||||||
|
except IntegrityError:
|
||||||
|
db.session.rollback()
|
||||||
|
return jsonify({'message': '创建部门失败,可能是由于数据完整性问题'}), 500
|
||||||
|
|
||||||
|
return jsonify({'message': '部门创建成功', 'id': new_department.id}), 201
|
||||||
|
|
||||||
|
@bp.route('/<int:parent_id>/children', methods=['POST'])
|
||||||
|
def create_child_department(parent_id):
|
||||||
|
parent_department = Department.query.get(parent_id)
|
||||||
|
if not parent_department:
|
||||||
|
return jsonify({'message': '父部门不存在'}), 404
|
||||||
|
|
||||||
|
data = request.get_json()
|
||||||
|
|
||||||
|
if not data or 'name' not in data:
|
||||||
|
return jsonify({'message': '子部门名称是必需的'}), 400
|
||||||
|
|
||||||
|
name = data['name']
|
||||||
|
|
||||||
|
# 检查部门名称是否已存在
|
||||||
|
if Department.query.filter_by(name=name).first():
|
||||||
|
return jsonify({'message': '部门名称已存在'}), 400
|
||||||
|
|
||||||
|
new_department = Department(name=name, parent_id=parent_id)
|
||||||
|
db.session.add(new_department)
|
||||||
|
db.session.flush() # 这会给 new_department 分配一个 id
|
||||||
|
|
||||||
|
# 如果提供了其他字段,设置它们
|
||||||
|
for field in ['department_code', 'manager', 'phone', 'email', 'location']:
|
||||||
|
if field in data:
|
||||||
|
new_department.set_detail(field, data[field])
|
||||||
|
|
||||||
|
try:
|
||||||
|
db.session.commit()
|
||||||
|
except IntegrityError:
|
||||||
|
db.session.rollback()
|
||||||
|
return jsonify({'message': '创建子部门失败,可能是由于数据完整性问题'}), 500
|
||||||
|
|
||||||
|
return jsonify({'message': '子部门创建成功', 'id': new_department.id}), 201
|
||||||
|
|
||||||
|
@bp.route('/<int:department_id>/add_user', methods=['POST'])
|
||||||
|
def add_user_to_department(department_id):
|
||||||
|
data = request.get_json()
|
||||||
|
|
||||||
|
if not data or 'user_id' not in data:
|
||||||
|
return jsonify({'message': '用户ID是必需的'}), 400
|
||||||
|
|
||||||
|
user_id = data['user_id']
|
||||||
|
|
||||||
|
department = Department.query.get(department_id)
|
||||||
|
if not department:
|
||||||
|
return jsonify({'message': '部门不存在'}), 404
|
||||||
|
|
||||||
|
user = User.query.get(user_id)
|
||||||
|
if not user:
|
||||||
|
return jsonify({'message': '用户不存在'}), 404
|
||||||
|
|
||||||
|
if 'is_primary' in data and data['is_primary']:
|
||||||
|
# 如果是主要部门,直接设置
|
||||||
|
user.primary_department = department
|
||||||
|
else:
|
||||||
|
# 如果不是主要部门,添加到次要部门
|
||||||
|
if department not in user.secondary_departments:
|
||||||
|
user.secondary_departments.append(department)
|
||||||
|
|
||||||
|
try:
|
||||||
|
db.session.commit()
|
||||||
|
return jsonify({'message': '用户成功添加到部门'}), 200
|
||||||
|
except IntegrityError:
|
||||||
|
db.session.rollback()
|
||||||
|
return jsonify({'message': '添加用户到部门失败,可能是由于数据完整性问题'}), 500
|
||||||
|
|
||||||
|
@bp.route('', methods=['GET'])
|
||||||
|
def get_departments():
|
||||||
|
departments = Department.query.all()
|
||||||
|
departments_list = [{
|
||||||
|
'id': dept.id,
|
||||||
|
'name': dept.name,
|
||||||
|
'parent_id': dept.parent_id,
|
||||||
|
'created_at': dept.created_at.isoformat() if dept.created_at else None,
|
||||||
|
'updated_at': dept.updated_at.isoformat() if dept.updated_at else None
|
||||||
|
} for dept in departments]
|
||||||
|
return jsonify(departments_list), 200
|
||||||
|
|
||||||
|
@bp.route('/<int:department_id>', methods=['GET'])
|
||||||
|
def get_department_details(department_id):
|
||||||
|
department = Department.query.get(department_id)
|
||||||
|
if not department:
|
||||||
|
return jsonify({'message': '部门不存在'}), 404
|
||||||
|
|
||||||
|
# 获取主要部门的用户
|
||||||
|
primary_users = User.query.filter_by(primary_department_id=department.id).all()
|
||||||
|
|
||||||
|
# 获取次要部门的用户
|
||||||
|
secondary_users = department.users.all()
|
||||||
|
|
||||||
|
# 合并用户列表并去重
|
||||||
|
all_users = list(set(primary_users + secondary_users))
|
||||||
|
|
||||||
|
users = [{'id': u.id, 'username': u.username} for u in all_users]
|
||||||
|
children = [{'id': c.id, 'name': c.name} for c in department.children]
|
||||||
|
|
||||||
|
department_details = {
|
||||||
|
'id': department.id,
|
||||||
|
'name': department.name,
|
||||||
|
'parent_id': department.parent_id,
|
||||||
|
'created_at': department.created_at.isoformat() if department.created_at else None,
|
||||||
|
'updated_at': department.updated_at.isoformat() if department.updated_at else None,
|
||||||
|
'department_code': department.get_detail('department_code'),
|
||||||
|
'manager': department.get_detail('manager'),
|
||||||
|
'phone': department.get_detail('phone'),
|
||||||
|
'email': department.get_detail('email'),
|
||||||
|
'location': department.get_detail('location'),
|
||||||
|
'users': users,
|
||||||
|
'children': children
|
||||||
|
}
|
||||||
|
|
||||||
|
return jsonify(department_details), 200
|
||||||
|
|
||||||
|
# 你可以在这里添加更多的部门相关路由...
|
Loading…
Reference in New Issue
Block a user