refactor: P5 DI 注入 + 中间件迁入 src/core

【依赖注入(解决 core→modules 倒置)】
- src/core/permission.py:PermissionProvider 协议 + check_permission
  (不再 import menu 服务,改从 current_app.extensions['permission_provider'] 获取)
- src/core/middleware.py:整合 check_login/check_demo/transaction/op_log/error_handler
  (op_log 写入器 DI:current_app.extensions['operation_log_writer'])
- application/factory.py:组合根注入 MenuPermissionProvider + record_operation_log
- src/core/scheduler.py 懒加载指向 src.modules.job.scheduler_service

【验收】src/core 内零 application 引用;DI 注入验证通过;
未登录 401、白名单放行;12 passed;core+中间件 flake8 零告警

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
DjangoAdmin Dev
2026-08-07 19:39:19 +08:00
co-authored by Claude
parent 35f89395a2
commit 0b0fa0c047
13 changed files with 1184 additions and 1053 deletions
+5 -238
View File
@@ -1,241 +1,8 @@
"""
AOP 事务拦截器(企业级实现)
兼容 shim(绞杀者迁移期间保留)
=============================================================
设计理念:
1. 零代码侵入:业务代码无需任何事务管理代码
2. 自动读写分离:GET 不提交,POST/PUT/DELETE 自动提交
3. 统一异常处理:所有异常自动回滚
4. 性能监控:记录事务执行时间
使用方式:
在 Flask 应用初始化时调用 register_transaction(app) 即可。
业务代码无需任何改动。
Example:
# app/__init__.py
from application.middleware.transaction import register_transaction
def create_app():
app = Flask(__name__)
register_transaction(app) # 一行代码搞定所有事务
return app
AOP 事务拦截器已迁移至 src/core/middleware.py。
本文件仅作兼容转发,最终在 P7(切尾)阶段删除。
"""
from flask import request, g
from extensions import db
import logging
import time
from functools import wraps
logger = logging.getLogger(__name__)
# ============================================================
# 核心:AOP 事务拦截器
# ============================================================
def register_transaction(app):
"""
注册 AOP 事务拦截器到 Flask 应用
在请求生命周期中自动管理事务:
- before_request: 开启事务标记
- after_request: 自动提交(POST/PUT/DELETE)
- teardown_request: 异常回滚 + 资源清理
Args:
app: Flask 应用实例
Example:
register_transaction(app)
"""
@app.before_request
def begin_transaction():
"""请求开始:记录开始时间,开启事务标记"""
g.request_start_time = time.time()
g.transaction_active = True
logger.debug(
f"请求开始, method: {request.method}, path: {request.path}, "
f"ip: {request.remote_addr}"
)
@app.after_request
def commit_transaction(response):
"""
请求结束:自动提交事务
提交规则:
1. 只对写操作(POST/PUT/DELETE/PATCH)提交
2. 只在响应成功(2xx)且业务成功(code=0)时提交
3. GET/OPTIONS/HEAD 不提交(只读)
4. 业务失败(R.failed)不提交,自动回滚
"""
# 只对写操作处理事务
if request.method in ('POST', 'PUT', 'DELETE', 'PATCH'):
should_commit = 200 <= response.status_code < 300
# 检查业务状态码:R.failed() 返回 HTTP 200 但 code=1/ok=False
if should_commit:
try:
data = response.get_json(silent=True)
if data and data.get('code', 0) != 0:
should_commit = False
except Exception:
pass
if should_commit:
try:
if db.session.is_active:
db.session.commit()
logger.debug(
f"事务提交成功, method: {request.method}, "
f"path: {request.path}, status: {response.status_code}"
)
except Exception as e:
db.session.rollback()
logger.error(
f"事务提交失败, method: {request.method}, "
f"path: {request.path}, error: {str(e)}",
exc_info=True
)
from utils import R
return R.failed(msg="数据处理失败"), 500
else:
# 业务失败或非2xx,回滚未提交的变更
try:
if db.session.is_active:
db.session.rollback()
except Exception:
pass
# 记录响应日志
elapsed = time.time() - g.get('request_start_time', time.time())
logger.debug(
f"请求结束, method: {request.method}, path: {request.path}, "
f"status: {response.status_code}, elapsed: {elapsed:.3f}s"
)
return response
@app.teardown_request
def cleanup_transaction(exception=None):
"""
请求结束:清理资源
发生异常时自动回滚事务,确保数据一致性。
"""
if exception is not None:
try:
db.session.rollback()
logger.error(
f"请求异常,事务已回滚, method: {request.method}, "
f"path: {request.path}, error: {str(exception)}",
exc_info=True
)
except Exception as e:
logger.error(f"事务回滚失败: {str(e)}", exc_info=True)
# 清理请求标记
g.pop('transaction_active', None)
g.pop('request_start_time', None)
logger.info("AOP 事务拦截器注册成功")
# ============================================================
# 备用:精细控制装饰器(特殊场景使用)
# ============================================================
def transactional(func):
"""
事务装饰器(备用)
适用于需要精细控制事务边界的特殊场景。
大多数情况下,使用 AOP 拦截器即可。
Example:
@transactional
def complex_business():
# 精细控制的事务逻辑
pass
"""
@wraps(func)
def wrapper(*args, **kwargs):
if g.get('disable_auto_commit', False):
return func(*args, **kwargs)
try:
result = func(*args, **kwargs)
if not g.get('transaction_active', False):
db.session.commit()
return result
except Exception as e:
db.session.rollback()
logger.error(f"装饰器事务执行失败: {str(e)}", exc_info=True)
raise
return wrapper
def transactional_response(func):
"""
事务装饰器(带返回 R 对象)
用于需要直接返回 R 对象的接口。
Example:
@transactional_response
def add_position():
position.save()
return R.ok(msg="添加成功")
"""
@wraps(func)
def wrapper(*args, **kwargs):
from utils import R
try:
result = func(*args, **kwargs)
if not g.get('transaction_active', False):
db.session.commit()
return result
except Exception as e:
db.session.rollback()
logger.error(f"装饰器事务执行失败: {str(e)}", exc_info=True)
return R.failed(msg=f"操作失败: {str(e)}")
return wrapper
# ============================================================
# 备用:事务上下文管理器(特殊场景使用)
# ============================================================
class TransactionContext:
"""
事务上下文管理器
用于需要精细控制事务的特殊场景。
Example:
with TransactionContext():
position.save()
user.save()
# 自动提交或回滚
"""
def __enter__(self):
g.disable_auto_commit = True
return self
def __exit__(self, exc_type, exc_val, exc_tb):
g.disable_auto_commit = False
if exc_type is None:
if not g.get('transaction_active', False):
db.session.commit()
else:
db.session.rollback()
logger.error(f"事务上下文回滚: {str(exc_val)}", exc_info=True)
return False
from src.core.middleware import * # noqa: F401,F403