签入版本
This commit is contained in:
@@ -0,0 +1,584 @@
|
||||
# 扩展性设计
|
||||
|
||||
本章详细描述系统的扩展性设计,包括模块可插拔、数据库驱动可切换、中间件可插拔三大扩展维度。
|
||||
|
||||
:::tip 扩展性设计原则
|
||||
遵循开闭原则(OCP):对扩展开放,对修改关闭。新功能通过添加新模块实现,而非修改现有代码。核心框架通过基类、装饰器、中间件等机制提供扩展点,业务模块只需声明差异即可接入。
|
||||
:::
|
||||
|
||||
## 一、模块可插拔设计
|
||||
|
||||
### 模块标准结构
|
||||
|
||||
每个业务模块遵循固定的五文件结构,新增模块只需创建这五个文件即可接入系统:
|
||||
|
||||
```
|
||||
src/modules/{group}/{name}/
|
||||
├── models.py # ORM 模型
|
||||
├── repository.py # 数据访问层
|
||||
├── schema.py # 请求校验
|
||||
├── service.py # 业务逻辑层
|
||||
└── router.py # 路由定义
|
||||
```
|
||||
|
||||
### 新增模块步骤
|
||||
|
||||
以新增「培训管理(Training)」模块为例。
|
||||
|
||||
**步骤一:创建模块目录和文件**
|
||||
|
||||
创建 `src/modules/system/training/` 目录,包含以下五个文件:
|
||||
|
||||
`models.py` — ORM 模型:
|
||||
|
||||
```python
|
||||
# src/modules/system/training/models.py
|
||||
from sqlalchemy import Column, String, Integer, text
|
||||
from src.common.base_db import BaseDB
|
||||
from src.common.base_model import BaseModel
|
||||
from config.database import DB_PREFIX
|
||||
|
||||
|
||||
class Training(BaseModel, BaseDB):
|
||||
__tablename__ = DB_PREFIX + "training"
|
||||
__table_comment__ = "培训记录表"
|
||||
|
||||
title = Column(String(200), nullable=False, comment="培训标题")
|
||||
content = Column(String(2000), nullable=True, comment="培训内容")
|
||||
trainer = Column(String(50), nullable=True, comment="培训讲师")
|
||||
status = Column(Integer, default=1, server_default=text('1'), comment="状态")
|
||||
sort = Column(Integer, default=0, server_default=text('0'), comment="排序")
|
||||
```
|
||||
|
||||
`repository.py` — 数据访问层:
|
||||
|
||||
```python
|
||||
# src/modules/system/training/repository.py
|
||||
from src.modules.system.training.models import Training
|
||||
from src.common.base_repository import BaseRepository
|
||||
|
||||
|
||||
class TrainingRepository(BaseRepository):
|
||||
model = Training
|
||||
|
||||
|
||||
_training_repository = TrainingRepository()
|
||||
```
|
||||
|
||||
`schema.py` — 请求校验:
|
||||
|
||||
```python
|
||||
# src/modules/system/training/schema.py
|
||||
from wtforms import StringField, IntegerField
|
||||
from wtforms.validators import DataRequired, Length, NumberRange
|
||||
|
||||
from src.common.forms.base_form import BaseForm
|
||||
|
||||
|
||||
class TrainingForm(BaseForm):
|
||||
title = StringField('培训标题', validators=[
|
||||
DataRequired(message='培训标题不能为空'),
|
||||
Length(min=1, max=200, message='培训标题长度必须在1-200个字符之间')
|
||||
])
|
||||
content = StringField('培训内容', validators=[
|
||||
Length(max=2000, message='培训内容长度不能超过2000个字符')
|
||||
])
|
||||
trainer = StringField('培训讲师', validators=[
|
||||
Length(max=50, message='培训讲师长度不能超过50个字符')
|
||||
])
|
||||
status = IntegerField('状态', validators=[
|
||||
DataRequired(message='状态不能为空'),
|
||||
NumberRange(min=1, max=2, message='状态必须是1或2')
|
||||
])
|
||||
sort = IntegerField('排序', validators=[
|
||||
DataRequired(message='排序不能为空'),
|
||||
NumberRange(min=0, max=99999, message='排序必须在0-99999之间')
|
||||
])
|
||||
|
||||
|
||||
class TrainingStatusForm(BaseForm):
|
||||
id = IntegerField('培训ID', validators=[
|
||||
DataRequired(message='培训ID不能为空'),
|
||||
NumberRange(min=1, message='培训ID必须大于0')
|
||||
])
|
||||
status = IntegerField('状态', validators=[
|
||||
DataRequired(message='状态不能为空'),
|
||||
NumberRange(min=1, max=2, message='状态必须是1或2')
|
||||
])
|
||||
```
|
||||
|
||||
`service.py` — 业务逻辑层:
|
||||
|
||||
```python
|
||||
# src/modules/system/training/service.py
|
||||
from src.common.base_service import BaseService
|
||||
from src.modules.system.training.models import Training
|
||||
from src.modules.system.training.repository import TrainingRepository
|
||||
|
||||
|
||||
class TrainingService(BaseService):
|
||||
repository = TrainingRepository
|
||||
form = TrainingForm
|
||||
status_form = TrainingStatusForm
|
||||
model_name = "培训"
|
||||
|
||||
|
||||
# 模块级代理函数
|
||||
get_training_page = TrainingService.get_page
|
||||
get_training_detail = TrainingService.get_detail
|
||||
add_training = TrainingService.add
|
||||
update_training = TrainingService.update
|
||||
delete_trainings = TrainingService.remove
|
||||
update_training_status = TrainingService.update_status
|
||||
```
|
||||
|
||||
`router.py` — 路由定义:
|
||||
|
||||
```python
|
||||
# src/modules/system/training/router.py
|
||||
from flask import Blueprint, request
|
||||
|
||||
from src.core.permission import check_permission
|
||||
from src.core.response import R
|
||||
from src.common.utils.request import parse_request_body, parse_id_list
|
||||
|
||||
training_blue = Blueprint('training', __name__, url_prefix='/training')
|
||||
|
||||
|
||||
@training_blue.route('/page', methods=['GET'])
|
||||
@check_permission('sys:training:page')
|
||||
def page():
|
||||
from src.modules.system.training.service import get_training_page
|
||||
return get_training_page()
|
||||
|
||||
|
||||
@training_blue.route('/detail/<int:id>', methods=['GET'])
|
||||
@check_permission('sys:training:detail')
|
||||
def detail(id):
|
||||
from src.modules.system.training.service import get_training_detail
|
||||
return R.ok(data=get_training_detail(id))
|
||||
|
||||
|
||||
@training_blue.route('/add', methods=['POST'])
|
||||
@check_permission('sys:training:add')
|
||||
def add():
|
||||
from src.modules.system.training.service import add_training
|
||||
from src.modules.system.training.schema import TrainingForm
|
||||
data = parse_request_body(request, TrainingForm)
|
||||
return add_training(data)
|
||||
|
||||
|
||||
@training_blue.route('/update', methods=['PUT'])
|
||||
@check_permission('sys:training:update')
|
||||
def update():
|
||||
from src.modules.system.training.service import update_training
|
||||
from src.modules.system.training.schema import TrainingForm
|
||||
data = parse_request_body(request, TrainingForm)
|
||||
return update_training(data)
|
||||
|
||||
|
||||
@training_blue.route('/delete/<int:id>', methods=['DELETE'])
|
||||
@check_permission('sys:training:delete')
|
||||
def delete(id):
|
||||
from src.modules.system.training.service import delete_training
|
||||
return delete_training(id)
|
||||
|
||||
|
||||
@training_blue.route('/status', methods=['PUT'])
|
||||
@check_permission('sys:training:status')
|
||||
def status():
|
||||
from src.modules.system.training.service import update_training_status
|
||||
from src.modules.system.training.schema import TrainingStatusForm
|
||||
data = parse_request_body(request, TrainingStatusForm)
|
||||
return update_training_status(data)
|
||||
|
||||
|
||||
@training_blue.route('/batchDelete', methods=['DELETE'])
|
||||
@check_permission('sys:training:batchDelete')
|
||||
def batch_delete():
|
||||
from src.modules.system.training.service import batch_delete_trainings
|
||||
id_list = parse_id_list(request)
|
||||
return batch_delete_trainings(id_list)
|
||||
```
|
||||
|
||||
**步骤二:注册路由**
|
||||
|
||||
在 `src/routes/v1.py` 中添加:
|
||||
|
||||
```python
|
||||
# src/routes/v1.py
|
||||
from src.modules.system.training.router import training_blue
|
||||
|
||||
BLUEPRINTS = [
|
||||
# ... 其他模块
|
||||
training_blue,
|
||||
]
|
||||
```
|
||||
|
||||
路由注册后,该模块的所有端点自动挂在 `/api/v1/training` 前缀下,并通过 `src/routes/v1.py` 的 `register_v1()` 统一挂载到 Flask 应用实例。
|
||||
|
||||
**步骤三:配置菜单权限**
|
||||
|
||||
在数据库 `flask_menu` 表中插入对应的菜单和权限节点记录。权限标识格式为 `sys:{module}:{action}`,如 `sys:training:add`、`sys:training:page`。
|
||||
|
||||
### 模块解耦
|
||||
|
||||
模块之间通过以下方式解耦:
|
||||
|
||||
| 解耦方式 | 说明 | 示例 |
|
||||
|:---|:---|:---|
|
||||
| **Repository 单例** | 模块级单例,通过 import 引用 | `from src.modules.system.user.repository import UserRepository` |
|
||||
| **Service 代理函数** | 模块级代理函数,通过 import 引用 | `from src.modules.system.user.service import get_user_page` |
|
||||
| **跨模块引用** | 通过 Repository 引用其他模块的模型 | PositionService 引用 User 模型检查引用 |
|
||||
| **事件机制** | 暂未实现(未来可通过事件总线解耦) | — |
|
||||
|
||||
### 复杂模块扩展
|
||||
|
||||
对于业务逻辑复杂的模块(如 User、Role、Menu),在继承 `BaseService` 的基础上添加自定义方法:
|
||||
|
||||
```python
|
||||
# src/modules/system/user/service.py
|
||||
class UserService(BaseService):
|
||||
repository = UserRepository
|
||||
form = UserForm
|
||||
status_form = UserStatusForm
|
||||
model_name = "用户"
|
||||
|
||||
@classmethod
|
||||
def add(cls, data: dict) -> None:
|
||||
"""新增用户(覆写基类方法)"""
|
||||
form = cls._validate_form(data)
|
||||
# 密码加密
|
||||
pwd = form.data.get("password") or DEFAULT_PASSWORD
|
||||
salt = generate_salt()
|
||||
create_data = cls._clean_form_data(form)
|
||||
create_data["password"] = encrypt_password(pwd, salt)
|
||||
create_data["salt"] = salt
|
||||
create_data["create_user"] = get_realname()
|
||||
user = cls.repository.create(**create_data)
|
||||
user.code = f"{user.id:06d}"
|
||||
|
||||
@classmethod
|
||||
def _detail_dict(cls, obj):
|
||||
"""详情序列化:剔除密码字段,补充关联数据"""
|
||||
data = model_to_dict(obj)
|
||||
if data:
|
||||
data.pop("password", None)
|
||||
return _enrich_user_detail(data)
|
||||
|
||||
@classmethod
|
||||
def before_delete(cls, ids):
|
||||
"""删除前校验:超管禁止删除"""
|
||||
if 1 in ids:
|
||||
raise BizError("超级管理员不能删除")
|
||||
```
|
||||
|
||||
## 二、数据库驱动可切换
|
||||
|
||||
### 驱动注册机制
|
||||
|
||||
项目通过 `_SUPPORTED_DRIVERS` 声明支持的驱动,`_normalize_driver()` 处理别名映射和校验,`_build_database_url()` 按驱动构建连接串:
|
||||
|
||||
```python
|
||||
# config/database.py(简化)
|
||||
import os
|
||||
from urllib.parse import quote_plus
|
||||
|
||||
# 支持的数据库驱动
|
||||
_SUPPORTED_DRIVERS = ("mysql", "postgresql", "mssql", "sqlite", "oracle")
|
||||
|
||||
|
||||
def _normalize_driver(driver: str) -> str:
|
||||
"""归一化 DB_DRIVER:别名映射 + 校验"""
|
||||
name = str(driver).strip().lower()
|
||||
alias_map = {
|
||||
'postgres': 'postgresql',
|
||||
'pg': 'postgresql',
|
||||
'sqlserver': 'mssql',
|
||||
'sql_server': 'mssql',
|
||||
'sqlite3': 'sqlite',
|
||||
}
|
||||
name = alias_map.get(name, name)
|
||||
if name not in _SUPPORTED_DRIVERS:
|
||||
raise ValueError(f"不支持的 DB_DRIVER: {driver!r}")
|
||||
return name
|
||||
|
||||
|
||||
DB_DRIVER = _normalize_driver(os.getenv('DB_DRIVER', 'mysql'))
|
||||
DB_HOST = os.getenv('DB_HOST', '127.0.0.1')
|
||||
DB_PORT = int(os.getenv('DB_PORT', '3306'))
|
||||
DB_DATABASE = os.getenv('DB_DATABASE', 'djangoadmin.flask.elevue')
|
||||
DB_USERNAME = os.getenv('DB_USERNAME', 'root')
|
||||
DB_PASSWORD = os.getenv('DB_PASSWORD', '')
|
||||
|
||||
|
||||
def _build_database_url(driver: str = None) -> str:
|
||||
"""按驱动构建 SQLAlchemy 连接串"""
|
||||
drv = _normalize_driver(driver if driver is not None else DB_DRIVER)
|
||||
|
||||
if drv == 'sqlite':
|
||||
return 'sqlite:///./' + DB_DATABASE
|
||||
|
||||
base = quote_plus(DB_USERNAME) + ':' + quote_plus(DB_PASSWORD) + '@' + DB_HOST + ':' + str(DB_PORT)
|
||||
|
||||
if drv == 'mysql':
|
||||
return 'mysql+pymysql://' + base + '/' + DB_DATABASE + '?charset=utf8mb4'
|
||||
if drv == 'postgresql':
|
||||
return 'postgresql+psycopg://' + base + '/' + DB_DATABASE
|
||||
if drv == 'oracle':
|
||||
return 'oracle+oracledb://' + base + '/?service_name=' + quote_plus(DB_DATABASE)
|
||||
# mssql
|
||||
return 'mssql+pymssql://' + base + '/' + DB_DATABASE + '?charset=utf8'
|
||||
```
|
||||
|
||||
各驱动的连接串格式:
|
||||
|
||||
| 驱动 | 连接串格式 | Python 包 |
|
||||
|:----|:----|:----|
|
||||
| mysql | `mysql+pymysql://user:pass@host:port/db?charset=utf8mb4` | PyMySQL |
|
||||
| postgresql | `postgresql+psycopg://user:pass@host:port/db` | psycopg[binary] |
|
||||
| mssql | `mssql+pymssql://user:pass@host:port/db?charset=utf8` | pymssql |
|
||||
| oracle | `oracle+oracledb://user:pass@host:port/?service_name=db` | oracledb |
|
||||
| sqlite | `sqlite:///./path/to/db` | 内置 |
|
||||
|
||||
### 切换方式
|
||||
|
||||
只需修改 `.env` 文件中的 `DB_DRIVER` 和对应的连接参数:
|
||||
|
||||
```bash
|
||||
# MySQL(默认)
|
||||
DB_DRIVER=mysql
|
||||
DB_HOST=127.0.0.1
|
||||
DB_PORT=3306
|
||||
DB_DATABASE=djangoadmin
|
||||
DB_USERNAME=root
|
||||
DB_PASSWORD=your_password
|
||||
|
||||
# PostgreSQL
|
||||
DB_DRIVER=postgresql
|
||||
DB_HOST=127.0.0.1
|
||||
DB_PORT=5432
|
||||
DB_DATABASE=djangoadmin
|
||||
DB_USERNAME=postgres
|
||||
DB_PASSWORD=your_password
|
||||
|
||||
# SQLite
|
||||
DB_DRIVER=sqlite
|
||||
DB_DATABASE=./data.db
|
||||
|
||||
# SQL Server
|
||||
DB_DRIVER=mssql
|
||||
DB_HOST=127.0.0.1
|
||||
DB_PORT=1433
|
||||
DB_DATABASE=djangoadmin
|
||||
DB_USERNAME=sa
|
||||
DB_PASSWORD=your_password
|
||||
```
|
||||
|
||||
:::warning 驱动切换注意事项
|
||||
1. 切换驱动后需安装对应的 Python 包(如 `psycopg[binary]` for PostgreSQL)
|
||||
2. 数据迁移使用 `scripts/migrate_db.py` 工具
|
||||
3. SQLite 不支持并发写入,仅适用于开发/测试环境
|
||||
4. Oracle 需要额外的客户端库配置
|
||||
:::
|
||||
|
||||
### 跨库数据迁移
|
||||
|
||||
```bash
|
||||
# 干跑:核对迁移计划
|
||||
python scripts/migrate_db.py --src-driver mysql --src-host ... --dst-driver postgresql --dst-host ... --dry-run
|
||||
|
||||
# 执行迁移
|
||||
python scripts/migrate_db.py --src-driver mysql --src-host ... --dst-driver postgresql --dst-host ...
|
||||
|
||||
# 幂等重跑(先删除目标库数据)
|
||||
python scripts/migrate_db.py --src-driver mysql --src-host ... --dst-driver postgresql --dst-host ... --drop-target-first
|
||||
```
|
||||
|
||||
## 三、中间件可插拔
|
||||
|
||||
### 中间件注册机制
|
||||
|
||||
所有中间件在 `src/app.py` 的 `create_app()` 中按顺序注册。新增中间件只需:
|
||||
|
||||
1. 在 `src/middleware/` 目录创建中间件文件
|
||||
2. 在 `create_app()` 中注册
|
||||
|
||||
```python
|
||||
# src/app.py - create_app() 中间件注册顺序
|
||||
def create_app():
|
||||
app = Flask(__name__)
|
||||
|
||||
# 先注册 → 内层(最后处理请求)
|
||||
register_redis(app) # Redis 上下文注入
|
||||
register_db_session(app) # DB 会话管理(commit/rollback)
|
||||
register_operation_log(app) # 操作日志记录
|
||||
register_transaction(app) # AOP 事务管理
|
||||
register_v1(app) # 路由注册(login_required 依赖)
|
||||
register_exception(app) # 全局异常处理器
|
||||
|
||||
# 请求计时(先注册→内层,统计业务耗时)
|
||||
@app.before_request
|
||||
def start_timer():
|
||||
g.start_time = time.time()
|
||||
|
||||
@app.after_request
|
||||
def add_process_time_header(response):
|
||||
if hasattr(g, 'start_time'):
|
||||
process_time = time.time() - g.start_time
|
||||
response.headers["X-Process-Time"] = str(round(process_time, 5))
|
||||
return response
|
||||
|
||||
register_rate_limit(app) # 滑动窗口限流
|
||||
register_cors(app) # CORS(最后注册→最外层)
|
||||
|
||||
return app
|
||||
```
|
||||
|
||||
### 新增中间件示例
|
||||
|
||||
```python
|
||||
# src/middleware/custom_middleware.py
|
||||
from flask import request, g
|
||||
|
||||
|
||||
def register_custom(app):
|
||||
"""注册自定义中间件"""
|
||||
|
||||
@app.before_request
|
||||
def custom_before():
|
||||
"""请求前逻辑"""
|
||||
g.custom_data = "xxx"
|
||||
|
||||
@app.after_request
|
||||
def custom_after(response):
|
||||
"""响应后逻辑"""
|
||||
response.headers["X-Custom-Header"] = "value"
|
||||
return response
|
||||
```
|
||||
|
||||
```python
|
||||
# src/app.py
|
||||
from src.middleware.custom_middleware import register_custom
|
||||
|
||||
def create_app():
|
||||
# ... 其他中间件 ...
|
||||
register_custom(app)
|
||||
# ...
|
||||
```
|
||||
|
||||
### 中间件执行顺序
|
||||
|
||||
```
|
||||
请求进入 → CORS → 限流 → 上传限制 → 计时 → 操作日志 → DB会话 → Redis注入 → 路由处理
|
||||
│
|
||||
响应返回 ← CORS ← 限流 ← 上传限制 ← 计时 ← 操作日志 ← DB会话 ← Redis注入 ← 路由处理 ←┘
|
||||
```
|
||||
|
||||
:::warning 中间件注册顺序
|
||||
Flask 中间件按注册顺序执行。`register_cors` 最后注册 → 最外层(最先处理请求)。`register_redis` 最先注册 → 最内层(最后处理请求)。
|
||||
:::
|
||||
|
||||
## 四、装饰器扩展
|
||||
|
||||
### 自定义装饰器
|
||||
|
||||
通过 `check_permission` 和 `check_demo` 的模式,可以创建自定义装饰器:
|
||||
|
||||
```python
|
||||
# src/common/decorators.py
|
||||
import time
|
||||
from functools import wraps
|
||||
|
||||
|
||||
def log_operation(module: str, action: str):
|
||||
"""
|
||||
操作日志记录装饰器
|
||||
自动记录接口调用的模块、操作类型、执行耗时
|
||||
|
||||
Args:
|
||||
module: 所属模块名称,如 "培训管理"
|
||||
action: 操作类型,如 "新增"、"编辑"、"删除"
|
||||
"""
|
||||
def decorator(func):
|
||||
@wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
# 记录开始时间,用于计算接口执行耗时
|
||||
start = time.time()
|
||||
# 执行原业务函数
|
||||
result = func(*args, **kwargs)
|
||||
# 计算耗时(秒)
|
||||
duration = time.time() - start
|
||||
# 记录操作日志到数据库(包含模块、操作、耗时等信息)
|
||||
save_log(module, action, duration)
|
||||
return result
|
||||
return wrapper
|
||||
return decorator
|
||||
|
||||
|
||||
# src/modules/system/training/router.py
|
||||
|
||||
# 使用示例:多个装饰器叠加使用
|
||||
# 执行顺序:从下往上执行(先 log_operation,再 check_permission)
|
||||
@training_blue.route('/add', methods=['POST'])
|
||||
@check_permission('sys:training:add') # 权限校验:需要培训新增权限
|
||||
@log_operation("培训管理", "新增") # 操作日志记录:记录本次操作
|
||||
def add():
|
||||
"""新增培训记录接口"""
|
||||
from src.modules.system.training.service import add_training
|
||||
from src.modules.system.training.schema import TrainingForm
|
||||
data = parse_request_body(request, TrainingForm)
|
||||
return add_training(data)
|
||||
```
|
||||
|
||||
## 五、配置扩展
|
||||
|
||||
### 环境变量驱动
|
||||
|
||||
所有可配置项通过 `.env` 文件管理,新增配置项只需:
|
||||
|
||||
1. 在 `.env` 文件中添加变量
|
||||
2. 在 `config/` 对应模块中读取
|
||||
3. 在业务代码中引用
|
||||
|
||||
```bash
|
||||
# .env
|
||||
CUSTOM_FEATURE_ENABLED=true
|
||||
CUSTOM_TIMEOUT=30
|
||||
```
|
||||
|
||||
```python
|
||||
# config/custom.py
|
||||
import os
|
||||
|
||||
CUSTOM_FEATURE_ENABLED = os.getenv("CUSTOM_FEATURE_ENABLED", "false").lower() == "true"
|
||||
CUSTOM_TIMEOUT = int(os.getenv("CUSTOM_TIMEOUT", "30"))
|
||||
```
|
||||
|
||||
## 六、前端扩展
|
||||
|
||||
### 新增页面
|
||||
|
||||
1. 在 `ui/src/api/` 创建 API 文件
|
||||
2. 在 `ui/src/views/` 创建页面组件
|
||||
3. 在数据库 `flask_menu` 表中配置菜单路由
|
||||
|
||||
### 新增组件
|
||||
|
||||
公共组件放在 `ui/src/components/` 目录,遵循 Vue3 组件规范。
|
||||
|
||||
## 扩展性矩阵
|
||||
|
||||
| 扩展维度 | 扩展方式 | 修改范围 | 复杂度 |
|
||||
|:---|:---|:---|:---|
|
||||
| 新增 CRUD 模块 | 创建 5 文件 + 注册路由 | 新增文件,不改现有代码 | 低 |
|
||||
| 新增复杂模块 | 继承基类 + 自定义方法 | 新增文件,不改现有代码 | 中 |
|
||||
| 切换数据库驱动 | 修改 .env 配置 | 仅改配置 | 低 |
|
||||
| 新增中间件 | 创建中间件 + 注册 | 新增文件 + 改 app.py | 低 |
|
||||
| 新增装饰器 | 创建装饰器 + 应用 | 新增文件 | 低 |
|
||||
| 新增配置项 | .env + config 模块 | 新增配置 | 低 |
|
||||
| 新增前端页面 | API + Views + Menu | 新增文件 + 改数据库 | 中 |
|
||||
|
||||
## 总结
|
||||
|
||||
通过分层架构、基类模板方法、装饰器、中间件、环境变量配置等机制,实现了高度的可扩展性。新增 CRUD 模块只需 5 个标准文件 + 1 行路由注册,不修改任何现有代码。数据库驱动通过环境变量切换,中间件通过注册机制插拔。这种设计使得系统在功能持续增长的同时,保持代码结构的清晰和稳定。
|
||||
Reference in New Issue
Block a user