Compare commits
10
Commits
2bf2179611
...
2857e0f2da
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2857e0f2da | ||
|
|
d45b61d808 | ||
|
|
8937799973 | ||
|
|
2b001dde95 | ||
|
|
5504f3f2c6 | ||
|
|
4d74b31431 | ||
|
|
4b0b28105c | ||
|
|
410096547f | ||
|
|
0b0fa0c047 | ||
|
|
35f89395a2 |
@@ -0,0 +1,98 @@
|
||||
# ============================================================
|
||||
# .dockerignore —— 排除不需要进入构建上下文的文件
|
||||
# 位置:项目根目录,与 manage.py 同级
|
||||
# ============================================================
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# 版本控制(避免 .git 进镜像,省体积、防泄露)
|
||||
# ------------------------------------------------------------
|
||||
.git
|
||||
.gitignore
|
||||
.gitattributes
|
||||
.gitmodules
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# Python 缓存与编译产物
|
||||
# ------------------------------------------------------------
|
||||
__pycache__
|
||||
*.pyc
|
||||
*.pyo
|
||||
*.pyd
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
.pytest_cache
|
||||
.mypy_cache
|
||||
.ruff_cache
|
||||
.coverage
|
||||
.coverage.*
|
||||
htmlcov
|
||||
.tox
|
||||
.nox
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# 虚拟环境
|
||||
# ------------------------------------------------------------
|
||||
venv
|
||||
.venv
|
||||
env
|
||||
ENV
|
||||
virtualenv
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# Python 打包产物
|
||||
# ------------------------------------------------------------
|
||||
build
|
||||
dist
|
||||
*.egg-info
|
||||
*.egg
|
||||
.eggs
|
||||
pip-wheel-metadata
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# 前端依赖与构建产物
|
||||
# ------------------------------------------------------------
|
||||
node_modules
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
pnpm-debug.log
|
||||
.ui-build
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# 日志与本地数据库
|
||||
# ------------------------------------------------------------
|
||||
*.log
|
||||
logs
|
||||
*.sqlite3
|
||||
*.db
|
||||
*.sql.gz
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# 编辑器与 IDE
|
||||
# ------------------------------------------------------------
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
.idea
|
||||
.vscode
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# CI / 部署配置(保留 .jenkins/Dockerfile)
|
||||
# ------------------------------------------------------------
|
||||
.jenkins/*
|
||||
!.jenkins/Dockerfile
|
||||
jenkins
|
||||
*.jenkins
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# 项目自定义排除
|
||||
# ------------------------------------------------------------
|
||||
# 前端 UI 目录(按需排除,如不需要可删)
|
||||
/ui
|
||||
/public
|
||||
|
||||
# 文档与说明(按需保留)
|
||||
# README.md
|
||||
# docs
|
||||
# *.md
|
||||
+18
-7
@@ -1,5 +1,5 @@
|
||||
# ============================================================
|
||||
# DjangoAdmin Flask+EleVue 环境变量模板
|
||||
# DjangoAdmin Flask 环境变量模板
|
||||
# ============================================================
|
||||
# 使用方法:
|
||||
# 1. 复制为 .env:cp .env.example .env
|
||||
@@ -11,7 +11,7 @@
|
||||
# 应用基础配置(config/app.py)
|
||||
# ============================================================
|
||||
FLASK_NAME=Flask+EleVue旗舰版
|
||||
FLASK_VERSION=v2.8.0
|
||||
FLASK_VERSION=v3.0.0
|
||||
FLASK_APP=app.py
|
||||
FLASK_ENV=development # development / production
|
||||
FLASK_DEBUG=True # 生产环境必须为 False
|
||||
@@ -28,7 +28,7 @@ CORS_ALLOWED_ORIGINS=http://localhost:8001
|
||||
|
||||
# 上传配置
|
||||
FLASK_UPLOAD_DIR=./uploads
|
||||
FLASK_FILE_URL=http://images.flask.elevue
|
||||
FLASK_FILE_URL=http://file.flask.elevue
|
||||
UPLOAD_ALLOWED_EXTS=.jpg,.jpeg,.png,.gif,.bmp,.webp,.svg,.ico,.txt,.pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx,.mp3,.mp4,.zip,.rar,.7z
|
||||
UPLOAD_MAX_SIZE_MB=10
|
||||
|
||||
@@ -38,6 +38,16 @@ PROXY_NUM=0
|
||||
# ============================================================
|
||||
# 数据库配置(config/database.py)
|
||||
# ============================================================
|
||||
# 数据库驱动 - 可选 mysql/postgresql/mssql/sqlite/oracle
|
||||
# (别名:postgres=postgresql、sqlserver=mssql、sqlite3=sqlite)
|
||||
# 示例:
|
||||
# MySQL: DB_DRIVER=mysql DB_HOST=127.0.0.1 DB_PORT=3306 DB_USERNAME=root DB_PASSWORD=***
|
||||
# PostgreSQL: DB_DRIVER=postgresql DB_HOST=127.0.0.1 DB_PORT=5432 DB_USERNAME=postgres DB_PASSWORD=***
|
||||
# SQL Server: DB_DRIVER=mssql DB_HOST=127.0.0.1 DB_PORT=1433 DB_USERNAME=sa DB_PASSWORD=***
|
||||
# SQLite: DB_DRIVER=sqlite DB_DATABASE=dev.db(此时 DB_DATABASE 即数据库文件完整路径,
|
||||
# 可填相对/绝对路径如 C:/apps/dev.db,忽略 host/port/user/password)
|
||||
# Oracle: DB_DRIVER=oracle DB_HOST=127.0.0.1 DB_PORT=1521 DB_USERNAME=system DB_PASSWORD=***
|
||||
# (DB_DATABASE 填 SERVICE_NAME,如 XEPDB1)
|
||||
DB_DRIVER=mysql
|
||||
DB_HOST=127.0.0.1
|
||||
DB_PORT=3306
|
||||
@@ -66,7 +76,8 @@ REDIS_DECODE_RESPONSES=True
|
||||
# JWT / 权限 / 白名单(config/auth.py)
|
||||
# ============================================================
|
||||
JWT_SALT=<必填,JWT 签名密钥,建议随机长串>
|
||||
JWT_EXPIRE_MINUTES=20
|
||||
JWT_EXPIRE_MINUTES=20 # 滑动会话空闲超时(分钟),无请求则过期,活跃自动续期
|
||||
JWT_ABSOLUTE_TIMEOUT_MINUTES=1440 # 令牌绝对有效期(分钟),防止无限续期,须≥JWT_EXPIRE_MINUTES
|
||||
LOGIN_FAIL_MAX=5
|
||||
LOGIN_LOCK_SECONDS=300
|
||||
PERMISSION_ENABLED=True # 权限校验总开关(开发可关)
|
||||
@@ -76,9 +87,9 @@ INTERNAL_API_KEY=<必填>
|
||||
INTERNAL_ALLOWED_IPS=127.0.0.1
|
||||
|
||||
# 登录白名单(以下路径跳过登录校验)
|
||||
LOGIN_WHITELIST_EXACT=/login,/captcha,/favicon.ico
|
||||
LOGIN_WHITELIST_PREFIX=/job/sendSms,/public,/static,/api/webhook
|
||||
LOGIN_WHITELIST_WILDCARD=/job/*/send*,/api/v1/*/public*
|
||||
LOGIN_WHITELIST_EXACT=/api/v1/login,/api/v1/captcha,/favicon.ico,/health
|
||||
LOGIN_WHITELIST_PREFIX=/api/v1/job/sendSms,/public,/static,/api/webhook
|
||||
LOGIN_WHITELIST_WILDCARD=/api/v1/job/*/send*,/api/v1/*/public*
|
||||
LOGIN_WHITELIST_DEBUG=True
|
||||
LOGIN_WHITELIST_LOG_ACCESS=True
|
||||
|
||||
|
||||
+1
-2
@@ -33,7 +33,6 @@ __pycache__/
|
||||
*$py.class
|
||||
*.egg-info/
|
||||
.eggs/
|
||||
build/
|
||||
dist/
|
||||
.venv/
|
||||
venv/
|
||||
@@ -75,4 +74,4 @@ coverage.xml
|
||||
# ============================================================
|
||||
*.pid
|
||||
*.sqlite3
|
||||
captcha.png
|
||||
captcha.png
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
masterProfiles: ['','uat','prod']
|
||||
Profiles: ['','dev']
|
||||
jobConfigFile: "djangoadminConfig.yml"
|
||||
npmCompileCMD: |
|
||||
export PATH=/usr/local/node/bin:$PATH
|
||||
set -e \
|
||||
&& yarn config set registry https://registry.npmmirror.com \
|
||||
&& yarn config get registry \
|
||||
&& yarn config set strict-ssl false \
|
||||
&& yarn config set sass_binary_site https://repo.huaweicloud.com/node-sass/node-sass \
|
||||
&& rm -rf package-lock.json \
|
||||
&& rm -rf node_modules/.cache 2>/dev/null || true \
|
||||
&& yarn cache clean --force \
|
||||
&& yarn global add node-gyp@10.3.1 \
|
||||
&& yarn install --unsafe-perm --legacy-peer-deps --network-timeout 300000 \
|
||||
&& yarn run build:${PROFILE}
|
||||
@@ -0,0 +1,142 @@
|
||||
# ============================================================
|
||||
# Dockerfile:项目镜像(多阶段构建)
|
||||
# 基础镜像:python-base:3.12.8(RockyLinux 8 + Python 3.12.8 + mariadb-connector-c)
|
||||
#
|
||||
# 设计要点
|
||||
# - builder 只编译依赖,装到 /opt/deps,不碰基础镜像的 Python 目录
|
||||
# - runtime 继承基础镜像的 Python,只把依赖的 site-packages COPY 过来
|
||||
# - MySQL 运行库(mariadb-connector-c)由基础镜像预置,runtime 无需重复安装
|
||||
# - 用 COPY --chown 指定属主,避免额外的 chown 层
|
||||
# - 源码 COPY 放在依赖之后,保护依赖层缓存
|
||||
#
|
||||
# 构建参数
|
||||
# VAR_BASE_IMAGE 基础镜像
|
||||
# VAR_PYTHON_PREFIX Python 安装前缀(与基础镜像一致)
|
||||
# VAR_PYTHON_SHORT_VERSION Python 主次版本号,用于 site-packages 路径
|
||||
# VAR_PYFILE_PATH 项目源码目录
|
||||
# VAR_PROFILES 应用 profile(dev / prod)
|
||||
# VAR_PORT 应用监听端口
|
||||
# VAR_PYFILE 入口文件名(不含 .py)
|
||||
# ============================================================
|
||||
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# 全局构建参数
|
||||
# 在 FROM 之前声明,可供后续各阶段引用
|
||||
# ------------------------------------------------------------
|
||||
ARG VAR_BASE_IMAGE
|
||||
ARG VAR_PYTHON_PREFIX=/usr/local/python3.12.8
|
||||
ARG VAR_PYTHON_SHORT_VERSION=3.12
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 阶段一:builder —— 编译并安装项目依赖
|
||||
# ============================================================
|
||||
FROM ${VAR_BASE_IMAGE} AS builder
|
||||
|
||||
# 阶段内重新声明,否则读不到全局 ARG
|
||||
# Docker 规则:全局 ARG 只在 FROM 行可见,阶段内使用必须重新声明
|
||||
ARG VAR_PYTHON_PREFIX
|
||||
ARG VAR_PYTHON_SHORT_VERSION
|
||||
ARG VAR_PYFILE_PATH
|
||||
|
||||
# 基础镜像末尾是 esxi 用户;dnf/pip 需要写系统目录和 /opt/deps,
|
||||
# 所以这里仍需 root
|
||||
USER root
|
||||
|
||||
LABEL MAINTAINER="djangoadmin@126.com"
|
||||
|
||||
# 编译阶段环境变量
|
||||
# PYTHONUNBUFFERED=1 日志实时输出
|
||||
# PYTHONDONTWRITEBYTECODE=1 不生成 .pyc
|
||||
# PIP_NO_CACHE_DIR=1 不缓存 wheel
|
||||
# PIP_DISABLE_PIP_VERSION_CHECK=1 关闭 pip 版本检查
|
||||
ENV PYTHONUNBUFFERED=1 \
|
||||
PYTHONDONTWRITEBYTECODE=1 \
|
||||
PIP_NO_CACHE_DIR=1 \
|
||||
PIP_DISABLE_PIP_VERSION_CHECK=1
|
||||
|
||||
# 安装编译工具链
|
||||
# gcc / gcc-c++ / make 编译 C 扩展(mysqlclient、uwsgi 等)
|
||||
# openssl-devel cryptography、_ssl 等
|
||||
# bzip2-devel / libffi-devel
|
||||
# mariadb-devel 编译 mysqlclient(提供 .pc 文件)
|
||||
# pkgconf-pkg-config 提供 pkg-config 命令(mysqlclient 2.2.x 必需)
|
||||
RUN set -eux; \
|
||||
# 替换 dnf 源为阿里云镜像
|
||||
sed -e 's|^mirrorlist=|#mirrorlist=|g' \
|
||||
-e 's|^#baseurl=http://dl.rockylinux.org/$contentdir|baseurl=https://mirrors.aliyun.com/rockylinux|g' \
|
||||
-i.bak /etc/yum.repos.d/*.repo; \
|
||||
dnf install -y --setopt=install_weak_deps=False \
|
||||
gcc gcc-c++ make \
|
||||
openssl-devel bzip2-devel libffi-devel \
|
||||
mariadb-devel pkgconf-pkg-config; \
|
||||
dnf clean all; rm -rf /var/cache/dnf
|
||||
|
||||
# 配置 pip 国内镜像源(阿里云 PyPI)
|
||||
RUN pip3 config set global.index-url http://mirrors.aliyun.com/pypi/simple && \
|
||||
pip3 config set install.trusted-host mirrors.aliyun.com
|
||||
|
||||
# 只拷 requirements.txt
|
||||
# 源码变化但依赖未变时,可复用 pip install 层缓存
|
||||
WORKDIR /opt/apps
|
||||
COPY ${VAR_PYFILE_PATH}/requirements.txt ./
|
||||
|
||||
# 依赖装到 /opt/deps,与基础镜像 Python 目录解耦
|
||||
# --prefix=/opt/deps 安装到独立目录,runtime 只 COPY 这个目录的产物
|
||||
# uwsgi 生产环境 WSGI 服务器,单独安装
|
||||
RUN python3 -m pip install --prefix=/opt/deps -r requirements.txt && \
|
||||
python3 -m pip install --prefix=/opt/deps uwsgi
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 阶段二:runtime —— 最终业务镜像
|
||||
# ============================================================
|
||||
FROM ${VAR_BASE_IMAGE} AS runtime
|
||||
|
||||
# 阶段内重新声明
|
||||
ARG VAR_PYTHON_PREFIX
|
||||
ARG VAR_PYTHON_SHORT_VERSION
|
||||
ARG VAR_PROFILES
|
||||
ARG VAR_PORT
|
||||
ARG VAR_PYFILE
|
||||
ARG VAR_PYFILE_PATH
|
||||
|
||||
LABEL MAINTAINER="djangoadmin@126.com"
|
||||
|
||||
# 运行期环境变量
|
||||
ENV PYTHONUNBUFFERED=1 \
|
||||
PYTHONDONTWRITEBYTECODE=1
|
||||
|
||||
# ARG 转 ENV,运行期可读取
|
||||
# PORT 供 EXPOSE 与 CMD 使用
|
||||
# PYFILE 入口文件名
|
||||
# PROFILES 应用读取的 profile
|
||||
ENV PORT=${VAR_PORT} \
|
||||
PYFILE=${VAR_PYFILE} \
|
||||
PROFILES=${VAR_PROFILES}
|
||||
|
||||
# 把 builder 编译好的 site-packages 和可执行文件拷到基础镜像 Python 对应目录
|
||||
# 目标路径必须与基础镜像 Python 的实际 site-packages 一致
|
||||
# 用 COPY --chown 指定属主,避免额外的 chown 层
|
||||
COPY --chown=esxi:esxi --from=builder \
|
||||
/opt/deps/lib/python${VAR_PYTHON_SHORT_VERSION}/site-packages/ \
|
||||
${VAR_PYTHON_PREFIX}/lib/python${VAR_PYTHON_SHORT_VERSION}/site-packages/
|
||||
COPY --chown=esxi:esxi --from=builder /opt/deps/bin/ ${VAR_PYTHON_PREFIX}/bin/
|
||||
|
||||
WORKDIR /opt/apps
|
||||
|
||||
# 拷项目源码(放在依赖之后,保护 pip 层缓存)
|
||||
# 建议配合 .dockerignore 排除 .git、__pycache__、本地虚拟环境等
|
||||
COPY --chown=esxi:esxi ${VAR_PYFILE_PATH}/ ./
|
||||
|
||||
USER esxi
|
||||
EXPOSE ${PORT}
|
||||
|
||||
# 启动命令
|
||||
# ${PYFILE} 为入口文件名(不含 .py),如 manage
|
||||
# runserver 仅适合开发/调试;生产环境建议改用 uwsgi / gunicorn,例如:
|
||||
# CMD ["sh","-c","uwsgi --http 0.0.0.0:${PORT} \
|
||||
# --module 你的项目名.wsgi:application \
|
||||
# --processes 4 --threads 2"]
|
||||
CMD ["sh", "-c", "python3 app.py runserver 0.0.0.0:${PORT} --noreload"]
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
@Library('share-library@master')_
|
||||
orgName = 'djangoadmin'
|
||||
pipelinePythonService(orgName)
|
||||
@@ -0,0 +1,3 @@
|
||||
@Library('share-library@master')_
|
||||
orgName = 'djangoadmin'
|
||||
pipelineNodeService(orgName)
|
||||
@@ -1,177 +0,0 @@
|
||||
现在我对项目有了全面的了解。以下是分析结果:
|
||||
|
||||
---
|
||||
后端功能完整性分析
|
||||
已实现的模块(共 20 个业务模块)
|
||||
|
||||
┌──────────┬───────────────┬─────────────────────────┬───────────────────────────────────────────┐
|
||||
│ 分类 │ 模块 │ CRUD │ 特殊功能 │
|
||||
├──────────┼───────────────┼─────────────────────────┼───────────────────────────────────────────┤
|
||||
│ 系统核心 │ login │ - │ 登录、验证码、退出、Token黑名单 │
|
||||
├──────────┼───────────────┼─────────────────────────┼───────────────────────────────────────────┤
|
||||
│ │ index │ - │ 菜单获取、用户信息、个人中心、改密 │
|
||||
├──────────┼───────────────┼─────────────────────────┼───────────────────────────────────────────┤
|
||||
│ 系统管理 │ user │ 完整CRUD+批量删除 │ 状态切换、密码重置 │
|
||||
├──────────┼───────────────┼─────────────────────────┼───────────────────────────────────────────┤
|
||||
│ │ role │ 完整CRUD+批量删除 │ 角色列表(下拉) │
|
||||
├──────────┼───────────────┼─────────────────────────┼───────────────────────────────────────────┤
|
||||
│ │ role_menu │ - │ 角色菜单权限查询/保存 │
|
||||
├──────────┼───────────────┼─────────────────────────┼───────────────────────────────────────────┤
|
||||
│ │ menu │ 完整CRUD │ 树形列表 │
|
||||
├──────────┼───────────────┼─────────────────────────┼───────────────────────────────────────────┤
|
||||
│ │ dept │ 完整CRUD │ 树形列表、部门数据下拉 │
|
||||
├──────────┼───────────────┼─────────────────────────┼───────────────────────────────────────────┤
|
||||
│ │ position │ 完整CRUD+批量删除 │ 状态切换、岗位列表(下拉) │
|
||||
├──────────┼───────────────┼─────────────────────────┼───────────────────────────────────────────┤
|
||||
│ │ level │ 完整CRUD+批量删除 │ 状态切换、Excel导入/导出、职级列表(下拉) │
|
||||
├──────────┼───────────────┼─────────────────────────┼───────────────────────────────────────────┤
|
||||
│ 系统配置 │ param │ 完整CRUD+批量删除 │ 状态切换 │
|
||||
├──────────┼───────────────┼─────────────────────────┼───────────────────────────────────────────┤
|
||||
│ │ dict │ 完整CRUD+批量删除 │ - │
|
||||
├──────────┼───────────────┼─────────────────────────┼───────────────────────────────────────────┤
|
||||
│ │ dict_item │ 完整CRUD+批量删除 │ - │
|
||||
├──────────┼───────────────┼─────────────────────────┼───────────────────────────────────────────┤
|
||||
│ │ config │ 完整CRUD+批量删除 │ 网站配置查询/保存 │
|
||||
├──────────┼───────────────┼─────────────────────────┼───────────────────────────────────────────┤
|
||||
│ │ config_item │ 完整CRUD+批量删除 │ 状态切换 │
|
||||
├──────────┼───────────────┼─────────────────────────┼───────────────────────────────────────────┤
|
||||
│ 内容管理 │ category │ 完整CRUD │ 分类数据下拉 │
|
||||
├──────────┼───────────────┼─────────────────────────┼───────────────────────────────────────────┤
|
||||
│ │ article │ 完整CRUD+批量删除 │ 状态切换、点击量 │
|
||||
├──────────┼───────────────┼─────────────────────────┼───────────────────────────────────────────┤
|
||||
│ │ notice │ 完整CRUD+批量删除 │ - │
|
||||
├──────────┼───────────────┼─────────────────────────┼───────────────────────────────────────────┤
|
||||
│ │ link │ 完整CRUD+批量删除 │ 状态切换 │
|
||||
├──────────┼───────────────┼─────────────────────────┼───────────────────────────────────────────┤
|
||||
│ 辅助功能 │ upload │ - │ 文件上传 │
|
||||
├──────────┼───────────────┼─────────────────────────┼───────────────────────────────────────────┤
|
||||
│ │ file_template │ 完整CRUD+批量删除 │ - │
|
||||
├──────────┼───────────────┼─────────────────────────┼───────────────────────────────────────────┤
|
||||
│ 监控日志 │ login_log │ 分页+详情+删除+批量删除 │ - │
|
||||
├──────────┼───────────────┼─────────────────────────┼───────────────────────────────────────────┤
|
||||
│ │ operation_log │ 分页+详情+删除+批量删除 │ - │
|
||||
├──────────┼───────────────┼─────────────────────────┼───────────────────────────────────────────┤
|
||||
│ 定时任务 │ job │ 完整CRUD+批量删除 │ 状态切换、暂停/恢复、立即执行、重载调度器 │
|
||||
├──────────┼───────────────┼─────────────────────────┼───────────────────────────────────────────┤
|
||||
│ 其他业务 │ city │ 完整CRUD │ 省市联动查询 │
|
||||
└──────────┴───────────────┴─────────────────────────┴───────────────────────────────────────────┘
|
||||
|
||||
基础设施(完整度较高)
|
||||
|
||||
┌──────────────┬──────┬────────────────────────────────────────────────────────────────┐
|
||||
│ 组件 │ 状态 │ 说明 │
|
||||
├──────────────┼──────┼────────────────────────────────────────────────────────────────┤
|
||||
│ AOP事务管理 │ 完整 │ before/after/teardown自动提交/回滚 │
|
||||
├──────────────┼──────┼────────────────────────────────────────────────────────────────┤
|
||||
│ JWT认证 │ 完整 │ 登录验证、Token黑名单、白名单(精确/前缀/通配符) │
|
||||
├──────────────┼──────┼────────────────────────────────────────────────────────────────┤
|
||||
│ RBAC权限 │ 完整 │ 装饰器权限校验、超管放行、内部调用放行 │
|
||||
├──────────────┼──────┼────────────────────────────────────────────────────────────────┤
|
||||
│ 操作日志 │ 完整 │ after_request自动记录写操作 │
|
||||
├──────────────┼──────┼────────────────────────────────────────────────────────────────┤
|
||||
│ 登录日志 │ 完整 │ 登录/退出自动记录 │
|
||||
├──────────────┼──────┼────────────────────────────────────────────────────────────────┤
|
||||
│ 全局异常处理 │ 完整 │ 400/401/403/404/405/500/Exception统一JSON响应 │
|
||||
├──────────────┼──────┼────────────────────────────────────────────────────────────────┤
|
||||
│ Demo模式 │ 完整 │ 写操作拦截 │
|
||||
├──────────────┼──────┼────────────────────────────────────────────────────────────────┤
|
||||
│ CORS │ 完整 │ 支持指定源+凭证或通配符 │
|
||||
├──────────────┼──────┼────────────────────────────────────────────────────────────────┤
|
||||
│ ProxyFix │ 完整 │ 可信代理配置 │
|
||||
├──────────────┼──────┼────────────────────────────────────────────────────────────────┤
|
||||
│ 数据库迁移 │ 完整 │ Flask-Migrate/Alembic,3个版本文件 │
|
||||
├──────────────┼──────┼────────────────────────────────────────────────────────────────┤
|
||||
│ APScheduler │ 完整 │ 调度器初始化/重载/暂停/恢复/立即执行 │
|
||||
├──────────────┼──────┼────────────────────────────────────────────────────────────────┤
|
||||
│ Flask-Mail │ 完整 │ 扩展初始化,配置齐全 │
|
||||
├──────────────┼──────┼────────────────────────────────────────────────────────────────┤
|
||||
│ Redis │ 完整 │ 缓存+会话 │
|
||||
├──────────────┼──────┼────────────────────────────────────────────────────────────────┤
|
||||
│ Docker │ 完整 │ Dockerfile + docker-compose.yml │
|
||||
├──────────────┼──────┼────────────────────────────────────────────────────────────────┤
|
||||
│ 工具库 │ 完整 │ 17个工具模块(R/JWT/密码/验证码/文件/IP/UA/格式化/大小写转换等) │
|
||||
├──────────────┼──────┼────────────────────────────────────────────────────────────────┤
|
||||
│ 表单验证 │ 完整 │ 22个WTForms表单+BaseForm+ArrayField │
|
||||
└──────────────┴──────┴────────────────────────────────────────────────────────────────┘
|
||||
|
||||
存在的问题和缺失
|
||||
|
||||
1. user_role 模块缺失视图层
|
||||
- models/user_role.py 和 services/user_role.py 存在,但 views/ 中没有 user_role.py
|
||||
- _blueprint_modules 列表中也没有 user_role
|
||||
- 用户-角色关联管理没有独立API(可能内嵌在user模块中,但未确认)
|
||||
|
||||
2. 部分模块缺少常见功能
|
||||
|
||||
┌─────────────────────────┬─────────────────────────────────────────────────────────────┐
|
||||
│ 模块 │ 缺失功能 │
|
||||
├─────────────────────────┼─────────────────────────────────────────────────────────────┤
|
||||
│ user │ 缺少导入/导出(level有,user没有) │
|
||||
├─────────────────────────┼─────────────────────────────────────────────────────────────┤
|
||||
│ role │ 缺少状态切换 │
|
||||
├─────────────────────────┼─────────────────────────────────────────────────────────────┤
|
||||
│ menu │ 缺少批量删除、排序接口 │
|
||||
├─────────────────────────┼─────────────────────────────────────────────────────────────┤
|
||||
│ dept │ 缺少批量删除 │
|
||||
├─────────────────────────┼─────────────────────────────────────────────────────────────┤
|
||||
│ dict/dict_item │ 缺少状态切换、根据字典编码获取字典项的接口 │
|
||||
├─────────────────────────┼─────────────────────────────────────────────────────────────┤
|
||||
│ notice │ 缺少状态切换、通知推送/已读标记 │
|
||||
├─────────────────────────┼─────────────────────────────────────────────────────────────┤
|
||||
│ article │ 缺少导入/导出 │
|
||||
├─────────────────────────┼─────────────────────────────────────────────────────────────┤
|
||||
│ category │ 缺少批量删除、排序接口 │
|
||||
├─────────────────────────┼─────────────────────────────────────────────────────────────┤
|
||||
│ config │ 缺少状态切换 │
|
||||
├─────────────────────────┼─────────────────────────────────────────────────────────────┤
|
||||
│ file_template │ 缺少状态切换、模板文件上传/下载接口(注释提到但未实现路由) │
|
||||
├─────────────────────────┼─────────────────────────────────────────────────────────────┤
|
||||
│ login_log/operation_log │ 缺少清空日志、统计/趋势分析接口(注释提到但未实现路由) │
|
||||
└─────────────────────────┴─────────────────────────────────────────────────────────────┘
|
||||
|
||||
3. 系统级缺失功能
|
||||
|
||||
┌──────────────┬─────────────────────────────────────────────┐
|
||||
│ 功能 │ 说明 │
|
||||
├──────────────┼─────────────────────────────────────────────┤
|
||||
│ 数据权限 │ 无行级数据权限控制(如只能看本部门数据) │
|
||||
├──────────────┼─────────────────────────────────────────────┤
|
||||
│ 缓存管理 │ 无Redis缓存查看/清理接口 │
|
||||
├──────────────┼─────────────────────────────────────────────┤
|
||||
│ 在线用户 │ 无在线用户查看/强退接口 │
|
||||
├──────────────┼─────────────────────────────────────────────┤
|
||||
│ 系统监控 │ 无服务器状态监控(CPU/内存/磁盘等) │
|
||||
├──────────────┼─────────────────────────────────────────────┤
|
||||
│ API限流 │ 无请求频率限制 │
|
||||
├──────────────┼─────────────────────────────────────────────┤
|
||||
│ 数据备份 │ 无数据库备份/恢复接口 │
|
||||
├──────────────┼─────────────────────────────────────────────┤
|
||||
│ WebSocket │ 无实时推送能力 │
|
||||
├──────────────┼─────────────────────────────────────────────┤
|
||||
│ 国际化(i18n) │ 无多语言支持 │
|
||||
├──────────────┼─────────────────────────────────────────────┤
|
||||
│ 审计日志 │ 操作日志有,但无数据变更前后对比记录 │
|
||||
├──────────────┼─────────────────────────────────────────────┤
|
||||
│ 文件管理 │ 只有上传,无文件列表/删除/预览管理 │
|
||||
├──────────────┼─────────────────────────────────────────────┤
|
||||
│ 邮件发送 │ Flask-Mail已初始化,但无发送邮件的API接口 │
|
||||
├──────────────┼─────────────────────────────────────────────┤
|
||||
│ 短信发送 │ job中有sendSms路由但只是空壳(return R.ok()) │
|
||||
└──────────────┴─────────────────────────────────────────────┘
|
||||
|
||||
4. 代码层面问题
|
||||
|
||||
- batchDelete 逻辑在每个模块重复实现,未抽取公共方法
|
||||
- user_role 有 model 和 service 但无 view,属于半成品
|
||||
- job/sendSms 是空实现
|
||||
- file_template 注释提到上传/下载但未实现路由
|
||||
- login_log/operation_log 注释提到清空/统计但未实现路由
|
||||
|
||||
总结
|
||||
|
||||
后端功能完整度约 75-80%。核心的RBAC权限体系、用户/角色/菜单/部门管理、AOP事务、日志记录等基础设施非常完整。主要短板在于:
|
||||
|
||||
1. 已有模块的功能不够均匀 — 部分模块缺少状态切换、批量删除、导入导出等常见操作
|
||||
2. 运维监控类功能缺失 — 在线用户、缓存管理、服务器监控、数据备份等
|
||||
3. 部分半成品代码 — user_role视图缺失、sendSms空实现、文件模板上传下载未实现
|
||||
4. 代码复用不足 — batchDelete等重复代码应抽取公共方法
|
||||
@@ -2,6 +2,10 @@
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
> **重构状态**:项目已按《重构方案-v2.md》完成 P1-P7 全部阶段。
|
||||
> 后端源码位于 `src/`(core/common/modules/routes),API 统一 `/api/v1` 前缀,
|
||||
> 服务层为「入参数据/出参数据 + BizError」范式,全项目 flake8 零告警。
|
||||
|
||||
## Project Overview
|
||||
|
||||
DjangoAdmin Flask+EleVue is a separated frontend-backend admin framework. Backend: Flask + SQLAlchemy + MySQL + Redis. Frontend: Vue 3 + Element Plus + TypeScript + Vite + Pinia. The project is primarily documented and commented in Chinese.
|
||||
@@ -60,13 +64,14 @@ docker-compose up -d
|
||||
|
||||
Each business module follows a strict **View -> Service -> Model** separation:
|
||||
|
||||
- **`application/views/`** — Route controllers (Flask Blueprint). Each file defines routes using `@{module}_blue.route()`, decorated with `@check_permission("sys:module:action")`. Views are thin — they delegate to service functions and return `R.ok()`/`R.failed()` responses. Write operations check `FLASK_DEMO` first to block demo-mode writes.
|
||||
- **`application/services/`** — Business logic layer. Contains all query building, data transformation, and orchestration. Functions receive data from `request` object directly (not passed as parameters from views). Private helpers prefixed with `_` (e.g., `_apply_filters`, `_build_page_data`).
|
||||
- **`application/models/`** — SQLAlchemy models. All models inherit from `BaseModel` (provides id, create_user, create_time, update_user, update_time, is_delete fields) and `BaseDB` (provides save(), save_all(), delete() methods). Table names use `DB_PREFIX` (default `flask_`) + snake_case class name.
|
||||
- **`src/modules/<domain>/<name>/router.py`** — Route controllers (Flask Blueprint). Registered under `/api/v1` prefix (see `src/routes/v1.py`). Each file defines routes decorated with `@check_permission("sys:module:action")`. Routers parse request params, delegate to service functions, and wrap with `R.ok()`/`R.failed()`. Write operations check `FLASK_DEMO` first (via `@check_demo`).
|
||||
- **`src/modules/<domain>/<name>/service.py`** — Business logic layer. **服务函数「入参数据/出参数据」**(不直接读 `request`,不返回 R);业务失败 `raise BizError`;私有辅助函数以 `_` 开头。数据访问委托 repository。标准 CRUD 模块继承 `BaseService`(`src/common/base_service.py`,含 page/detail/add/update/remove/update_status/list 模板方法 + before_*/after_*/_detail_dict/_enrich_page_records 覆写钩子),模块级代理函数暴露给 router 调用(router 零改动)。
|
||||
- **`src/modules/<domain>/<name>/models.py`** — SQLAlchemy models. All models inherit from `BaseModel` + `BaseDB` (in `src/common/`). Table names use `DB_PREFIX` (default `flask_`) + snake_case class name.
|
||||
- **`src/modules/<domain>/<name>/repository.py`** — 数据访问层,继承 `BaseRepository`(`src/common/base_repository.py`)。
|
||||
|
||||
### AOP Transaction Management
|
||||
|
||||
The framework uses an AOP-style transaction interceptor (`application/middleware/transaction.py`) registered via `register_transaction(app)`. **Business code never calls commit/rollback directly.**
|
||||
The framework uses an AOP-style transaction interceptor (`src/middleware/transaction.py`) registered via `register_transaction(app)`. **Business code never calls commit/rollback directly.**
|
||||
|
||||
- `before_request`: marks transaction active
|
||||
- `after_request`: auto-commits on POST/PUT/DELETE/PATCH only if HTTP 2xx AND `code==0` in response JSON; auto-rollbacks on business failures (`R.failed` returns HTTP 200 but `code=1`)
|
||||
@@ -76,14 +81,17 @@ The framework uses an AOP-style transaction interceptor (`application/middleware
|
||||
|
||||
### Key Backend Modules
|
||||
|
||||
- **`application/__init__.py`** — App factory (`create_app()`). Initialization order: Flask core -> Redis config -> DB config -> JSON encoder -> CORS -> ProxyFix -> AOP transaction -> register blueprints -> init extensions -> register middleware.
|
||||
- **`application/views/__init__.py`** — Auto-discovery blueprint registration. Add module name to `_blueprint_modules` list; the system dynamically imports the module and registers any `Blueprint` instance found in it. No manual `app.register_blueprint()` needed.
|
||||
- **`application/middleware/`** — `check_login` (before_request hook, JWT validation with token blacklist, whitelist matching via exact/prefix/wildcard patterns) and `check_permission` (RBAC decorator; superadmin user_id=1 bypasses all checks; internal calls via API key or IP whitelist also bypass).
|
||||
- **`application/forms/`** — WTForms validation forms. All inherit `BaseForm` (in `forms/forms.py`) which handles `ArrayField` JSON serialization. Forms are instantiated with `data=json_data` from parsed request body.
|
||||
- **`application/constants/`** — Shared constants: `PAGE_SIZE=10`, `DEFAULT_PASSWORD="123456"`, `MESSAGE_OK/FAIL`, `SERIALIZE_EXCLUDE_FIELDS` (is_delete, password, salt excluded from `to_dict()`).
|
||||
- **`src/app.py`** — App factory (`create_app()`). Initialization order: Flask core -> Redis config -> DB config -> JSON encoder -> CORS -> ProxyFix -> op_log/transaction -> register `/api/v1` routes -> init extensions -> middleware -> error handlers -> DI injection.
|
||||
- **`src/routes/v1.py`** — API v1 路由注册中心。所有业务蓝图挂载到 `/api/v1` 前缀。
|
||||
- **`src/middleware/`** — 中间件包(按职责拆分:`auth.py` 登录校验 + 白名单、`demo.py` 演示模式、`transaction.py` AOP 事务、`operation_log.py` 操作日志 + `@operation_log` 标题装饰器、`errors.py` 异常处理)。对外统一 `from src.middleware import ...`。
|
||||
- **`src/core/permission.py`** — `check_permission` (RBAC decorator; superadmin user_id=1 bypasses; 权限数据通过 `current_app.extensions['permission_provider']` 注入,见 `src/app.py` `_inject_dependencies`).
|
||||
- **`src/core/exceptions.py`** — `BizError` 业务异常(业务层 `raise BizError` 统一转 `R.failed`)。
|
||||
- **`src/common/forms/`** — WTForms 验证表单。继承 `BaseForm`(含 `ArrayField` JSON 序列化)。
|
||||
- **`src/common/constants/`** — 共享常量:`PAGE_SIZE=10`, `DEFAULT_PASSWORD="123456"`, `MESSAGE_OK/FAIL`, `SERIALIZE_EXCLUDE_FIELDS`.
|
||||
- **`config/`** — Split into `base.py` (Flask host/port/debug/paths), `database.py` (SQLAlchemy URI, SSL options), `redis.py` (Redis URL), `auth.py` (JWT salt, permission toggle, login whitelist, internal API key). All read from `.env` via python-dotenv. `config/__init__.py` loads `.env` before any sub-module imports.
|
||||
- **`extensions/`** — Flask extension initialization: SQLAlchemy (`db`), Redis, APScheduler. All initialized via `register_extends(app)`.
|
||||
- **`utils/`** — `R` (standardized JSON response: `{code, data, msg, ok}` where code=0 is success, code=1 is failure), `jwt` (JWT token handling with blacklist), `model_utils` (models_to_list, model_to_dict, format_datetime), `request` (parse_request_body, parse_id_list), `security` (get_user_id, get_username from JWT), `case_converter` (camelCase <-> snake_case for dict keys, recursive), `regular` (WTForms error extraction), `password` (bcrypt-based encrypt/verify — imported as `from utils import password` then used as `bcrypt.verify_password()` etc.), `captcha`, `file`, `content` (rich text + file URL handling), `redis_client`, `ip`, `user_agent`, `formatter`, `json`.
|
||||
- **`src/extensions.py`** — Flask extension registration orchestration (`register_extends(app)`). 实例定义在 `src/core/`(database/redis_client/mail/scheduler)。
|
||||
- **`src/core/`** — `response`(R 统一响应)、`jwt`、`password`(bcrypt)、`security`(get_user_id/get_username)、`redis_client`。
|
||||
- **`src/common/utils/`** — `model_utils`(models_to_list/model_to_dict)、`request`(parse_request_body/parse_id_list)、`case_converter`(camelCase <-> snake_case)、`regular`(WTForms 错误提取)、`captcha`、`file`、`rich_text`、`ip`、`user_agent`、`formatter`、`json`、`ip2region`.
|
||||
|
||||
### Frontend Structure (ui/src/)
|
||||
|
||||
@@ -101,6 +109,7 @@ The framework uses an AOP-style transaction interceptor (`application/middleware
|
||||
- Backend routes follow RESTful patterns: `GET /{module}/page`, `GET /{module}/detail/<id>`, `POST /{module}/add`, `PUT /{module}/update`, `DELETE /{module}/delete/<id>`, `PUT /{module}/status`, `GET /{module}/list`
|
||||
- All responses use `R.ok(data=...)` / `R.failed(msg=...)` format with `{code, data, msg, ok}` structure
|
||||
- Frontend response handler checks `code === 0` for success; `code=401` triggers re-login
|
||||
- **统一约定:所有响应(含认证/权限失败 401/403)返回 HTTP 200 + 业务 code**,不返回对应 HTTP 状态码——前端按 `code` 判断(401 重登、403 无权限)。若需按 HTTP 状态码防护(WAF/代理),应解析业务 code 而非 HTTP status
|
||||
- Permission strings follow pattern: `sys:module:action` (e.g., `sys:user:page`, `sys:role:add`)
|
||||
- Frontend proxies `/api` prefix to backend via Vite dev server proxy (configured in `ui/.env.development` as `VITE_PROXY=[["/api","http://127.0.0.1:8021/"]]`)
|
||||
|
||||
@@ -115,19 +124,23 @@ The framework uses an AOP-style transaction interceptor (`application/middleware
|
||||
|
||||
### Adding a New Module
|
||||
|
||||
1. Create model in `application/models/{module}.py` inheriting `BaseModel` + `BaseDB`
|
||||
2. Create service in `application/services/{module}.py` with business logic
|
||||
3. Create view in `application/views/{module}.py` with routes and permission decorators
|
||||
4. Add module name string to `_blueprint_modules` list in `application/views/__init__.py`
|
||||
5. Create form in `application/forms/{module}.py` inheriting `BaseForm` if input validation needed
|
||||
6. Add frontend API in `ui/src/api/{module}/`, views in `ui/src/views/{module}/`
|
||||
1. Create model in `src/modules/<domain>/<name>/models.py` inheriting `BaseModel` + `BaseDB`
|
||||
2. Create service in `src/modules/<domain>/<name>/service.py`(入参数据/出参数据 + raise BizError):
|
||||
- 标准 CRUD 模块:`class XxxService(BaseService)` 指定 `repository/form/status_form/model_name`,差异逻辑覆写钩子,末尾暴露模块级代理函数(如 `get_xxx_page = XxxService.get_page`)供 router 调用
|
||||
- 深度定制模块(如 user/auth):保持模块级函数 + raise BizError
|
||||
3. Create repository in `src/modules/<domain>/<name>/repository.py` inheriting `BaseRepository`(标准模块需实现 `page(params)` 与 `list_enabled()`)
|
||||
4. Create router in `src/modules/<domain>/<name>/router.py` with routes and permission decorators
|
||||
5. Create schema in `src/modules/<domain>/<name>/schema.py` inheriting `BaseForm` if input validation needed(唯一性校验用 `XxxRepository.exists_by(filters, exclude_id)`,禁止直接 `Model.query`——查询只允许在 Repository 层)
|
||||
6. Add the blueprint to `BLUEPRINTS` list in `src/routes/v1.py`
|
||||
7. Add frontend API in `ui/src/api/{module}/`, views in `ui/src/views/{module}/`(API 前缀 /api/v1)
|
||||
|
||||
## Environment Configuration
|
||||
|
||||
All config is in `.env` at project root. Key settings:
|
||||
- `FLASK_HOST` / `FLASK_PORT` (default 127.0.0.1:8021)
|
||||
- `FLASK_SECRET_KEY` — must be >= 16 chars or app refuses to start
|
||||
- `DB_*` — MySQL connection (database: `djangoadmin.flask.elevue`, prefix: `flask_`)
|
||||
- `DB_*` — 数据库连接,**多库支持**(`config/database.py` 的 `_build_database_url()` 按 `DB_DRIVER` 构建 URL):`mysql`(默认)/`postgresql`/`mssql`/`sqlite`/`oracle`(oracledb 驱动,thin 模式无需 Oracle Client;`DB_DATABASE` 填 SERVICE_NAME)。切换只需改 `DB_DRIVER` + 账号,然后运行 `python scripts/setup_db.py`(`create_all` 建表 + 种子数据,幂等)。注意:`flask db migrate/upgrade`(方案 B)仍针对 MySQL,新建其它库请用 `scripts/setup_db.py`
|
||||
- **跨库数据迁移**:`python scripts/migrate_db.py --src-driver=mysql --src-host=127.0.0.1 --src-port=3306 --src-db=<源库名> --src-user=root --src-pass=<密码> --dst-driver=postgresql --dst-host=127.0.0.1 --dst-port=5432 --dst-db=<目标库名> --dst-user=postgres --dst-pass=<密码> --drop-target-first`。CLI 显式参数、不依赖 `.env`;源/目标均支持 `mysql/postgresql/mssql/sqlite/oracle`(别名 `postgres=postgresql`、`sqlserver=mssql`、`sqlite3=sqlite`),自动建目标库 + `create_all` 建表 + 逐表泵数据 + 按方言重置自增(PG setval / MSSQL CHECKIDENT / Oracle 补 sequence+触发器)+ 逐表行数校验;先加 `--dry-run` 干跑核对计划
|
||||
- `REDIS_*` — Redis connection
|
||||
- `JWT_SALT` — JWT signing key; `JWT_EXPIRE_MINUTES` (default 20)
|
||||
- `FLASK_DEMO` — Demo mode restricts write operations
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# ============================================================
|
||||
# DjangoAdmin Flask+EleVue 常用命令集
|
||||
# DjangoAdmin Flask 常用命令集
|
||||
# 用法:make help / make lint / make test / ...
|
||||
# ============================================================
|
||||
|
||||
@@ -7,7 +7,7 @@ PYTHON ?= python
|
||||
PIP ?= $(PYTHON) -m pip
|
||||
FLASK ?= $(PYTHON) -m flask
|
||||
|
||||
# 说明:.env 中 FLASK_VERSION=v2.8.0 会被 Flask CLI 误解析为 --version 布尔标志,
|
||||
# 说明:.env 中 FLASK_VERSION=v3.0.0 会被 Flask CLI 误解析为 --version 布尔标志,
|
||||
# 导致 flask db 系列命令报错。故 db-* 目标显式跳过 CLI 自动加载 .env
|
||||
# (config/__init__.py 会自行加载 .env,应用配置不受影响)。
|
||||
FLASK_ENV_PREFIX := FLASK_SKIP_DOTENV=1 FLASK_APP=app.py
|
||||
@@ -32,23 +32,23 @@ dev: ## 启动开发服务器(127.0.0.1:8021)
|
||||
|
||||
# ---- 代码质量 ----
|
||||
lint: ## 静态检查(flake8 + black + isort)
|
||||
flake8 application utils extensions config src tests scripts
|
||||
black --check --line-length 120 application utils extensions config src tests scripts
|
||||
isort --check-only application utils extensions config src tests scripts
|
||||
flake8 src config tests scripts
|
||||
black --check --line-length 120 src config tests scripts
|
||||
isort --check-only src config tests scripts
|
||||
|
||||
format: ## 自动格式化(black + isort)
|
||||
black --line-length 120 application utils extensions config src tests scripts
|
||||
isort application utils extensions config src tests scripts
|
||||
black --line-length 120 src config tests scripts
|
||||
isort src config tests scripts
|
||||
|
||||
type-check: ## 类型检查(mypy)
|
||||
mypy application src
|
||||
mypy src
|
||||
|
||||
# ---- 测试 ----
|
||||
test: ## 运行全部测试
|
||||
pytest
|
||||
|
||||
test-cov: ## 带覆盖率运行测试
|
||||
pytest --cov=application --cov-report=term-missing
|
||||
pytest --cov=src --cov-report=term-missing
|
||||
|
||||
# ---- 数据库迁移 ----
|
||||
db-migrate: ## 生成迁移脚本(模型变更后)
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
# DjangoAdmin_Flask_EleVue
|
||||
|
||||
#### Description
|
||||
一款 Python 语言基于Flask、Vue2.x、ElementUI、MySQL等框架精心打造的一款模块化、高性能、企业级的敏捷开发框架,本着简化开发、提升开发效率的初衷触发,框架自研了一套个性化的组件,实现了可插拔的组件式开发方式:单图上传、多图上传、下拉选择、开关按钮、单选按钮、多选按钮、图片裁剪等等一系列个性化、轻量级的组件,是一款真正意义上实现组件化开发的敏捷开发框架。
|
||||
|
||||
#### Software Architecture
|
||||
Software architecture description
|
||||
|
||||
#### Installation
|
||||
|
||||
1. xxxx
|
||||
2. xxxx
|
||||
3. xxxx
|
||||
|
||||
#### Instructions
|
||||
|
||||
1. xxxx
|
||||
2. xxxx
|
||||
3. xxxx
|
||||
|
||||
#### Contribution
|
||||
|
||||
1. Fork the repository
|
||||
2. Create Feat_xxx branch
|
||||
3. Commit your code
|
||||
4. Create Pull Request
|
||||
|
||||
|
||||
#### Gitee Feature
|
||||
|
||||
1. You can use Readme\_XXX.md to support different languages, such as Readme\_en.md, Readme\_zh.md
|
||||
2. Gitee blog [blog.gitee.com](https://blog.gitee.com)
|
||||
3. Explore open source project [https://gitee.com/explore](https://gitee.com/explore)
|
||||
4. The most valuable open source project [GVP](https://gitee.com/gvp)
|
||||
5. The manual of Gitee [https://gitee.com/help](https://gitee.com/help)
|
||||
6. The most popular members [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)
|
||||
|
||||
@@ -1,123 +1,219 @@
|
||||
<h1 align="center">DjangoAdmin 数据中台解决方案(Flask + ElementPlus 版本)</h1>
|
||||
|
||||
<p align="center"><img src="./static/image/logo.png" alt="DjangoAdmin Logo" width="200" height="200" /></p>
|
||||
|
||||
## 📚 项目介绍
|
||||
|
||||
一款 Python 语言基于Flask、Vue2.x、ElementUI、MySQL等框架精心打造的一款模块化、高性能、企业级的敏捷开发框架,本着简化开发、提升开发效率的初衷触发,框架自研了一套个性化的组件,实现了可插拔的组件式开发方式:单图上传、多图上传、下拉选择、开关按钮、单选按钮、多选按钮、图片裁剪等等一系列个性化、轻量级的组件,是一款真正意义上实现组件化开发的敏捷开发框架。
|
||||
DjangoAdmin 数据中台解决方案是一套面向企业级应用场景的一站式敏捷开发平台。后端以 Python + Flask 为核心,前端采用 Vue3 + TypeScript + ElementPlus + Vite 构建,并通过 Vue Router 与 Pinia 实现路由和状态管理。全栈技术体系成熟稳定,可快速支撑中后台管理系统、数据中台、业务运营平台等多类项目落地。
|
||||
|
||||
框架以"简化开发、提升效率"为设计初衷,自研了一套个性化、可插拔的组件式开发体系,涵盖单图上传、多图上传、下拉选择、开关按钮、单选按钮、多选按钮等常用交互组件。开发者只需按需组合,即可完成业务页面搭建,真正实现低代码开发。
|
||||
|
||||
在工程化层面,框架内置代码生成器,可根据数据库表结构自动生成完整的 CRUD 模块,包括后端代码、前端页面以及菜单权限记录,将重复性的增删改查工作一键自动化,让开发者能够将精力聚焦于核心业务逻辑与差异化功能。
|
||||
|
||||
在架构层面,框架采用前后端分离设计:后端提供标准 API,前端独立部署并支持多端适配。权限体系基于 RBAC 模型构建,覆盖菜单权限、按钮权限、数据权限三级控制;认证采用 JWT 无状态方案,使前后端协作更加高效顺畅。后端采用 View → Service → Model 三层架构,配合 AOP 事务管理、统一响应规范与完善的开发工具链,为团队协作与长期维护提供坚实保障。
|
||||
|
||||
## 🍻 项目特点
|
||||
|
||||
+ 模块化、松耦合
|
||||
+ 模块丰富、开箱即用
|
||||
+ 简洁易用、快速接入
|
||||
+ 文档详尽、易于维护
|
||||
+ 自顶向下、体系化设计
|
||||
+ 统一框架、统一组件、降低选择成本
|
||||
+ 开发规范、设计模式、代码分层模型
|
||||
+ 强大便捷的开发工具链
|
||||
+ 完善的本地中文化支持
|
||||
+ 设计为团队及企业使用
|
||||
- **低代码生成** — 通过代码生成器一键生成完整模块,从数据库表定义到前后端代码、菜单权限全自动完成
|
||||
- **模块化、松耦合** — 每个业务模块独立封装,互不干扰,支持按需加载
|
||||
- **模块丰富、开箱即用** — 内置用户、角色、菜单、部门、字典、日志等十余个基础模块
|
||||
- **前后端分离** — 前后端独立开发与部署,接口规范统一,支持多端适配
|
||||
- **权限体系完善** — 基于 RBAC 模型,支持菜单权限、按钮权限、数据权限三级控制
|
||||
- **认证安全可靠** — 采用无状态认证方案,易于扩展,适配多端与分布式场景
|
||||
- **AOP 事务管理** — 业务代码无需手动提交或回滚,中间件自动根据响应状态处理事务
|
||||
- **统一响应规范** — 接口返回格式统一,前后端协作更高效
|
||||
- **开发规范、设计模式** — 代码分层清晰,职责明确,静态检查零告警,便于扩展与维护
|
||||
- **完善的本地中文化支持** — 错误提示、表单验证、操作日志全面中文化
|
||||
- **强大便捷的开发工具链** — 内置代码检查与格式化工具,代码质量有保障
|
||||
- **设计为团队及企业使用** — 规范统一,便于多人协作与长期维护
|
||||
|
||||
## 🍪 内置模块
|
||||
|
||||
+ 用户管理:用于维护管理系统的用户,常规信息的维护与账号设置。
|
||||
+ 角色管理:角色菜单管理与权限分配、设置角色所拥有的菜单权限。
|
||||
+ 菜单管理:配置系统菜单,操作权限,按钮权限标识等。
|
||||
+ 职级管理:主要管理用户的职级。
|
||||
+ 岗位管理:主要管理用户担任职务。
|
||||
+ 部门管理:配置系统组织机构,树结构展现支持数据权限。
|
||||
+ 字典管理:对系统中常用的较为固定的数据进行统一维护。
|
||||
+ 配置管理:对系统的常规配置信息进行维护,网站配置管理功能进行统一维护。
|
||||
+ 通知公告:系统通知公告信息发布维护。
|
||||
- 用户管理:维护管理系统的用户,常规信息的维护与账号设置,支持头像上传裁剪
|
||||
- 角色管理:角色菜单管理与权限分配,设置角色所拥有的菜单权限
|
||||
- 菜单管理:配置系统菜单、操作权限、按钮权限标识,支持动态路由生成
|
||||
- 职级管理:主要管理用户的职级信息
|
||||
- 岗位管理:主要管理用户担任的职务
|
||||
- 部门管理:配置系统组织机构,树结构展现支持数据权限
|
||||
- 字典管理:对系统中常用的较为固定的数据进行统一维护
|
||||
- 字典项管理:字典数据的明细项管理
|
||||
- 配置管理:对系统的常规配置信息进行维护,网站配置管理功能统一维护
|
||||
- 通知公告:系统通知公告信息发布维护
|
||||
- 操作日志:记录用户关键操作,支持日志查询与审计
|
||||
- 登录日志:记录用户登录信息,支持 IP 归属地解析
|
||||
- 定时任务:基于 APScheduler 的定时任务管理,支持任务调度与执行日志
|
||||
- 文件模板:文件模板管理与导出
|
||||
- 代码生成器:根据数据库表结构自动生成前后端模块代码
|
||||
|
||||
## 🚀 快速开始
|
||||
|
||||
### 环境要求
|
||||
|
||||
- **Python** >= 3.12
|
||||
- **Node.js** >= 22
|
||||
- **pnpm** >= 7
|
||||
- **MySQL** >= 8.0
|
||||
- **Redis** >= 6.0
|
||||
|
||||
## 🗄️ 数据库支持
|
||||
|
||||
框架默认支持 **MySQL** 数据库,同时也支持多种数据库,可根据实际业务场景灵活切换。
|
||||
|
||||
| 数据库 | 驱动 | 支持情况 |
|
||||
| --- | --- | --- |
|
||||
| MySQL | PyMySQL 1.1.2 | ✅ 默认支持 |
|
||||
| PostgreSQL | psycopg2-binary 2.9.10 | ✅ 支持 |
|
||||
| SQL Server | pymssql 2.3.3 | ✅ 支持 |
|
||||
| Oracle | oracledb 2.5.1(thin 模式,无需 Oracle Client) | ✅ 支持 |
|
||||
| SQLite | Python 内置 | ✅ 支持 |
|
||||
|
||||
## 📁 项目结构
|
||||
|
||||
```
|
||||
├── src/ # Flask 后端源码目录
|
||||
│ ├── app.py # 应用工厂(create_app())
|
||||
│ ├── extensions.py # Flask 扩展注册
|
||||
│ ├── models_registry.py # 模型注册中心
|
||||
│ ├── routes/ # 路由注册
|
||||
│ │ └── v1.py # API v1 路由注册中心(/api/v1 前缀)
|
||||
│ ├── middleware/ # 中间件
|
||||
│ │ ├── auth.py # JWT 登录校验 + 白名单
|
||||
│ │ ├── demo.py # 演示模式限制
|
||||
│ │ ├── transaction.py # AOP 事务管理
|
||||
│ │ ├── operation_log.py # 操作日志记录
|
||||
│ │ └── errors.py # 全局异常处理
|
||||
│ ├── core/ # 核心组件
|
||||
│ │ ├── database.py # SQLAlchemy 数据库初始化
|
||||
│ │ ├── redis_client.py # Redis 客户端
|
||||
│ │ ├── jwt.py # JWT 工具
|
||||
│ │ ├── password.py # bcrypt 密码工具
|
||||
│ │ ├── permission.py # RBAC 权限校验装饰器
|
||||
│ │ ├── response.py # R 统一响应封装
|
||||
│ │ ├── exceptions.py # BizError 业务异常
|
||||
│ │ ├── security.py # 安全工具(get_user_id 等)
|
||||
│ │ ├── scheduler.py # APScheduler 定时任务
|
||||
│ │ └── mail.py # 邮件发送
|
||||
│ ├── common/ # 公共基础层
|
||||
│ │ ├── base_model.py # BaseModel 基类
|
||||
│ │ ├── base_db.py # BaseDB 数据库操作基类
|
||||
│ │ ├── base_service.py # BaseService 服务基类(CRUD 模板方法)
|
||||
│ │ ├── base_repository.py # BaseRepository 数据访问基类
|
||||
│ │ ├── forms/ # WTForms 表单验证
|
||||
│ │ ├── constants/ # 公共常量
|
||||
│ │ └── utils/ # 工具函数(JWT、响应、文件、IP 等)
|
||||
│ └── modules/ # 业务模块(每个模块含 router/service/model/repository)
|
||||
│ ├── system/ # 系统管理
|
||||
│ │ ├── user/ # 用户管理
|
||||
│ │ ├── role/ # 角色管理
|
||||
│ │ ├── menu/ # 菜单管理
|
||||
│ │ ├── dept/ # 部门管理
|
||||
│ │ ├── level/ # 职级管理
|
||||
│ │ └── position/ # 岗位管理
|
||||
│ ├── association/ # 关联关系
|
||||
│ │ ├── user_role/ # 用户-角色关联
|
||||
│ │ └── role_menu/ # 角色-菜单关联
|
||||
│ ├── auth/ # 登录认证
|
||||
│ ├── dictionary/ # 字典管理
|
||||
│ ├── configuration/ # 配置管理
|
||||
│ ├── notice/ # 通知公告
|
||||
│ ├── job/ # 定时任务
|
||||
│ ├── job_log/ # 任务执行日志
|
||||
│ ├── operation_log/ # 操作日志
|
||||
│ ├── login_log/ # 登录日志
|
||||
│ ├── upload/ # 文件上传
|
||||
│ ├── file_template/ # 文件模板
|
||||
│ ├── generator/ # 代码生成器 Web 端
|
||||
│ ├── city/ # 城市数据
|
||||
│ ├── cms/ # 内容管理(文章等)
|
||||
│ └── example/ # 示例模块(代码生成器产出的参考)
|
||||
├── config/ # 配置文件目录
|
||||
│ ├── __init__.py # 配置入口(加载 .env + 导出所有配置项)
|
||||
│ ├── app.py # 应用基础配置
|
||||
│ ├── database.py # 数据库配置(多数据库支持)
|
||||
│ ├── redis.py # Redis 配置
|
||||
│ ├── auth.py # JWT / 权限 / 白名单配置
|
||||
│ ├── mail.py # 邮件配置
|
||||
│ ├── sms.py # 短信配置
|
||||
│ └── captcha.py # 验证码配置
|
||||
├── migrations/ # Flask-Migrate / Alembic 迁移文件
|
||||
├── scripts/ # 运维脚本
|
||||
│ ├── setup_db.py # 数据库初始化(建表 + 种子数据)
|
||||
│ └── migrate_db.py # 跨库数据迁移工具
|
||||
├── tests/ # 单元测试
|
||||
├── docker/ # Docker 部署配置
|
||||
├── ui/ # Vue3 前端项目
|
||||
│ ├── src/
|
||||
│ │ ├── api/ # API 接口模块
|
||||
│ │ ├── views/ # 页面视图
|
||||
│ │ ├── components/ # 公共组件(BasicTable / BasicForm 等)
|
||||
│ │ ├── router/ # 路由配置(动态路由生成)
|
||||
│ │ ├── store/ # Pinia 状态管理
|
||||
│ │ └── utils/ # 工具函数(Axios 封装等)
|
||||
│ ├── .env.development # 开发环境配置
|
||||
│ ├── .env.production # 生产环境配置
|
||||
│ └── package.json # 前端依赖配置
|
||||
├── generator.py # 代码生成器 CLI 入口
|
||||
├── config.json # 代码生成器配置示例
|
||||
├── gunicorn.conf.py # Gunicorn 生产部署配置
|
||||
├── app.py # WSGI 入口(开发启动)
|
||||
├── pyproject.toml # Python 项目配置(black / isort / flake8)
|
||||
├── requirements.txt # Python 依赖清单
|
||||
├── Makefile # 开发命令快捷入口
|
||||
├── CLAUDE.md # Claude Code 开发指南
|
||||
└── .env # 环境变量配置
|
||||
```
|
||||
|
||||
## 👷 软件信息
|
||||
|
||||
* 软件名称:DjangoAdmin敏捷开发框架Flask+EleVue版本
|
||||
* 软件作者:@一米阳光 团队荣誉出品
|
||||
* 软件出处:一米阳光团队
|
||||
* 软件协议:Apache-2.0
|
||||
* 官网网址:[https://www.djangoadmin.cn](https://www.djangoadmin.cn)
|
||||
* 文档网址:[http://docs.flask.elevue.djangoadmin.cn](http://docs.flask.elevue.djangoadmin.cn)
|
||||
|
||||
## 🎨 系统演示
|
||||
|
||||
+ 演示地址:http://manage.flask.elevue.djangoadmin.cn
|
||||
|
||||
账号 | 密码| 操作权限
|
||||
---|---|---
|
||||
admin | 123456| 演示环境无法进行修改删除操作
|
||||
- 软件名称:DjangoAdmin 敏捷开发框架 Flask + ElementPlus 版本
|
||||
- 软件作者:@小蚂蚁云 团队荣誉出品
|
||||
- 软件出处:小蚂蚁云团队
|
||||
- 软件协议:Apache-2.0
|
||||
- 官网网址:<https://www.djangoadmin.cn>
|
||||
- 文档网址:<http://docs.flask.elevue.djangoadmin.cn>
|
||||
- 演示地址:<http://manage.flask.elevue.djangoadmin.cn>
|
||||
|
||||
## 📌 版本说明
|
||||
|
||||
版本名称 | 版本说明 | 版本地址
|
||||
---|---|---
|
||||
Django+Layui混编版 | 采用Django、Layui等框架研发 | https://gitee.com/djangoadmin/DjangoAdmin_Django_Layui
|
||||
Flask+Layui混编版 | 采用Flask、Layui等框架研发 | https://gitee.com/djangoadmin/DjangoAdmin_Flask_Layui
|
||||
FastAPI+Layui混编版 | 采用FastAPI、Layui等框架研发 | https://gitee.com/djangoadmin/DjangoAdmin_Fastapi_Layui
|
||||
Tornado+Layui混编版 | 采用Tornado、Layui等框架研发 | https://gitee.com/djangoadmin/DjangoAdmin_Tornado_Layui
|
||||
Django+EleVue前后端分离版 | 采用Django、Vue2.x、ElementUI等框架研发前后端分离版本 | https://gitee.com/djangoadmin/DjangoAdmin_Django_EleVue
|
||||
Flask+EleVue前后端分离版 | 采用Flask、Vue2.x、ElementUI等框架研发前后端分离版本 | https://gitee.com/djangoadmin/DjangoAdmin_Flask_EleVue
|
||||
FastAPI+EleVue前后端分离版 | 采用FastAPI、Vue2.x、ElementUI等框架研发前后端分离版本 | https://gitee.com/djangoadmin/DjangoAdmin_Fastapi_EleVue
|
||||
Tornado+EleVue前后端分离版 | 采用Tornado、Vue2.x、ElementUI等框架研发前后端分离版本 | https://gitee.com/djangoadmin/DjangoAdmin_Tornado_EleVue
|
||||
Django+AntdVue前后端分离版 | 采用Django、Vue3.x、AntDesign等框架研发前后端分离版本 | https://gitee.com/djangoadmin/DjangoAdmin_Django_AntdVue
|
||||
Flask+AntdVue前后端分离版 | 采用Flask、Vue3.x、AntDesign等框架研发前后端分离版本 | https://gitee.com/djangoadmin/DjangoAdmin_Flask_AntdVue
|
||||
FastAPI+AntdVue前后端分离版 | 采用FastAPI、Vue3.x、AntDesign等框架研发前后端分离版本 | https://gitee.com/djangoadmin/DjangoAdmin_Fastapi_AntdVue
|
||||
Tornado+AntdVue前后端分离版 | 采用Tornado、Vue、AntDesign等框架研发前后端分离版本 | https://gitee.com/djangoadmin/DjangoAdmin_Tornado_AntdVue
|
||||
| 版本名称 | 版本说明 | 版本地址 |
|
||||
| --- | --- | --- |
|
||||
| Django + Layui 混编版 | 采用 Django、Layui 等框架研发 | <https://gitee.com/djangoadmin/DjangoAdmin_Django_Layui> |
|
||||
| Flask + Layui 混编版 | 采用 Flask、Layui 等框架研发 | <https://gitee.com/djangoadmin/DjangoAdmin_Flask_Layui> |
|
||||
| FastAPI + Layui 混编版 | 采用 FastAPI、Layui 等框架研发 | <https://gitee.com/djangoadmin/DjangoAdmin_Fastapi_Layui> |
|
||||
| Tornado + Layui 混编版 | 采用 Tornado、Layui 等框架研发 | <https://gitee.com/djangoadmin/DjangoAdmin_Tornado_Layui> |
|
||||
| Django + EleVue 前后端分离版 | 采用 Django、Vue3、TypeScript、ElementPlus 等框架研发前后端分离版本 | <https://gitee.com/djangoadmin/DjangoAdmin_Django_EleVue> |
|
||||
| Flask + EleVue 前后端分离版 | 采用 Flask、Vue3、TypeScript、ElementPlus 等框架研发前后端分离版本 | <https://gitee.com/djangoadmin/DjangoAdmin_Flask_EleVue> |
|
||||
| FastAPI + EleVue 前后端分离版 | 采用 FastAPI、Vue3、TypeScript、ElementPlus 等框架研发前后端分离版本 | <https://gitee.com/djangoadmin/DjangoAdmin_Fastapi_EleVue> |
|
||||
| Tornado + EleVue 前后端分离版 | 采用 Tornado、Vue3、TypeScript、ElementPlus 等框架研发前后端分离版本 | <https://gitee.com/djangoadmin/DjangoAdmin_Tornado_EleVue> |
|
||||
| Django + AntdVue 前后端分离版 | 采用 Django、Vue3、AntDesign 等框架研发前后端分离版本 | <https://gitee.com/djangoadmin/DjangoAdmin_Django_AntdVue> |
|
||||
| Flask + AntdVue 前后端分离版 | 采用 Flask、Vue3、AntDesign 等框架研发前后端分离版本 | <https://gitee.com/djangoadmin/DjangoAdmin_Flask_AntdVue> |
|
||||
| FastAPI + AntdVue 前后端分离版 | 采用 FastAPI、Vue3、AntDesign 等框架研发前后端分离版本 | <https://gitee.com/djangoadmin/DjangoAdmin_Fastapi_AntdVue> |
|
||||
| Tornado + AntdVue 前后端分离版 | 采用 Tornado、Vue3、AntDesign 等框架研发前后端分离版本 | <https://gitee.com/djangoadmin/DjangoAdmin_Tornado_AntdVue> |
|
||||
| Django + NaiveUI 前后端分离版 | 采用 Django、Vue3、TypeScript、NaiveUI 等框架研发前后端分离版本 | <https://gitee.com/djangoadmin/DjangoAdmin_Django_NaiveUI> |
|
||||
| Flask + NaiveUI 前后端分离版 | 采用 Flask、Vue3、TypeScript、NaiveUI 等框架研发前后端分离版本 | <https://gitee.com/djangoadmin/DjangoAdmin_Flask_NaiveUI> |
|
||||
| FastAPI + NaiveUI 前后端分离版 | 采用 FastAPI、Vue3、TypeScript、NaiveUI 等框架研发前后端分离版本 | <https://gitee.com/djangoadmin/DjangoAdmin_Fastapi_NaiveUI> |
|
||||
| Tornado + NaiveUI 前后端分离版 | 采用 Tornado、Vue3、TypeScript、NaiveUI 等框架研发前后端分离版本 | <https://gitee.com/djangoadmin/DjangoAdmin_Tornado_NaiveUI> |
|
||||
|
||||
|
||||
## 🔧 模块展示
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
| | |
|
||||
| --- | --- |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|
||||
## ✨ 特别鸣谢
|
||||
|
||||
感谢[Flask](https://flask.palletsprojects.com/en/2.2.x/)、[Vue](https://cn.vuejs.org/)、[ElementUI](https://element.eleme.cn/#/)等优秀开源项目。
|
||||
|
||||
## 📚 版权信息
|
||||
|
||||
商业版使用需授权,未授权禁止恶意传播和用于商业用途,任何个人、单位、组织授权所获取的框架源码禁止分享、转售、恶意传播、开源(包括框架源码及二次开发后的产品)、禁止简单更改包装声称是自己的产品,否则将追究相关人的法律责任。
|
||||
|
||||
本软件框架禁止任何单位和个人用于任何违法、侵害他人合法利益等恶意的行为,禁止用于任何违反我国法律法规的一切平台研发,任何单位和个人使用本软件框架用于产品研发而产生的任何意外、疏忽、合约毁坏、诽谤、版权或知识产权侵犯及其造成的损失 (
|
||||
包括但不限于直接、间接、附带或衍生的损失等),本团队不承担任何法律责任。本软件框架只能用于公司和个人内部的法律所允许的合法合规的软件产品研发,详细声明内容请阅读《框架免责声明》附件;
|
||||
|
||||
本项目包含的第三方源码和二进制文件之版权信息另行标注。
|
||||
|
||||
版权所有Copyright © 2021~2026 [djangoadmin.cn](https://www.djangoadmin.cn) All rights reserved。
|
||||
|
||||
更多细节参阅 [LICENSE](LICENSE)
|
||||
感谢 [Flask](https://flask.palletsprojects.com/)、[Vue](https://cn.vuejs.org/)、[TypeScript](https://www.typescriptlang.org/)、[ElementPlus](https://element-plus.org/)、[Vite](https://cn.vitejs.dev/)、[Pinia](https://pinia.vuejs.org/)、[TailwindCSS](https://tailwindcss.com/)、[SQLAlchemy](https://www.sqlalchemy.org/)、[APScheduler](https://apscheduler.readthedocs.io/) 等优秀开源项目。
|
||||
|
||||
@@ -1,383 +0,0 @@
|
||||
# Flask+EleVue 使用文档
|
||||
|
||||
## 目录
|
||||
|
||||
- [环境配置](#环境配置)
|
||||
- [基础配置](#基础配置)
|
||||
- [数据库配置](#数据库配置)
|
||||
- [Redis缓存配置](#redis缓存配置)
|
||||
- [邮件配置](#邮件配置)
|
||||
- [短信配置(阿里云)](#短信配置阿里云)
|
||||
- [JWT令牌配置](#jwt令牌配置)
|
||||
- [登录白名单配置](#登录白名单配置)
|
||||
- [权限验证配置](#权限验证配置)
|
||||
- [启动方式](#启动方式)
|
||||
- [开发环境](#开发环境)
|
||||
- [生产环境](#生产环境)
|
||||
- [Docker部署](#docker部署)
|
||||
- [Gunicorn参数调优](#gunicorn参数调优)
|
||||
- [应用流程](#应用流程)
|
||||
- [启动流程](#启动流程)
|
||||
- [请求处理流程](#请求处理流程)
|
||||
- [AOP事务管理](#aop事务管理)
|
||||
- [邮件服务](#邮件服务)
|
||||
- [短信服务](#短信服务)
|
||||
|
||||
---
|
||||
|
||||
## 环境配置
|
||||
|
||||
所有配置项在项目根目录 `.env` 文件中管理,应用启动时由 `python-dotenv` 自动加载。
|
||||
|
||||
### 基础配置
|
||||
|
||||
| 配置项 | 默认值 | 说明 |
|
||||
|--------|--------|------|
|
||||
| `FLASK_NAME` | Flask+EleVue旗舰版 | 应用名称 |
|
||||
| `FLASK_VERSION` | v2.8.0 | 应用版本 |
|
||||
| `FLASK_SECRET_KEY` | - | 应用秘钥,用于Session加密和CSRF保护,必须≥16字符 |
|
||||
| `FLASK_HOST` | 127.0.0.1 | 运行地址。`0.0.0.0` 允许外部访问 |
|
||||
| `FLASK_PORT` | 8021 | 运行端口 |
|
||||
| `FLASK_ENV` | development | 环境标识:`development` / `production` |
|
||||
| `FLASK_DEBUG` | True | 调试模式。**生产环境必须设为 False** |
|
||||
| `FLASK_USE_RELOADER` | False | 自动重启(仅 DEBUG=True 时生效) |
|
||||
| `PROXY_NUM` | 0 | 可信代理层数。Nginx后设为1 |
|
||||
| `CORS_ALLOWED_ORIGINS` | - | CORS允许的源,逗号分隔。配置后支持携带凭证 |
|
||||
| `FLASK_DEMO` | False | 演示模式,True时限制写操作 |
|
||||
| `CAPTCHA_BYPASS_CODE` | 618618 | 万能验证码(仅DEMO模式生效,生产环境必须为空) |
|
||||
| `FLASK_UPLOAD_DIR` | - | 文件上传存储目录 |
|
||||
| `FLASK_FILE_URL` | - | 图片域名,用于生成完整URL |
|
||||
| `UPLOAD_ALLOWED_EXTS` | .jpg,.jpeg,.png,... | 允许上传的文件扩展名 |
|
||||
| `UPLOAD_MAX_SIZE_MB` | 10 | 上传文件大小上限(MB) |
|
||||
|
||||
### 数据库配置
|
||||
|
||||
| 配置项 | 默认值 | 说明 |
|
||||
|--------|--------|------|
|
||||
| `DB_DRIVER` | mysql | 数据库驱动:`mysql` / `pgsql` / `sqlite` |
|
||||
| `DB_HOST` | 127.0.0.1 | 数据库地址 |
|
||||
| `DB_PORT` | 3306 | 数据库端口 |
|
||||
| `DB_DATABASE` | - | 数据库名称 |
|
||||
| `DB_USERNAME` | root | 数据库账号 |
|
||||
| `DB_PASSWORD` | - | 数据库密码 |
|
||||
| `DB_PREFIX` | flask_ | 表前缀 |
|
||||
| `DB_DEBUG` | True | 是否打印SQL日志 |
|
||||
| `DB_SSL` | False | 是否启用SSL加密连接 |
|
||||
|
||||
连接池高级参数(取消注释即可生效):
|
||||
|
||||
| 配置项 | 默认值 | 说明 |
|
||||
|--------|--------|------|
|
||||
| `DB_POOL_SIZE` | 10 | 常驻连接数 |
|
||||
| `DB_MAX_OVERFLOW` | 10 | 最大额外连接数 |
|
||||
| `DB_POOL_RECYCLE` | 1800 | 连接回收时间(秒),应小于MySQL的wait_timeout |
|
||||
| `DB_POOL_TIMEOUT` | 30 | 获取连接超时(秒) |
|
||||
| `DB_POOL_PRE_PING` | True | 使用前检测连接存活 |
|
||||
|
||||
### Redis缓存配置
|
||||
|
||||
| 配置项 | 默认值 | 说明 |
|
||||
|--------|--------|------|
|
||||
| `REDIS_HOST` | 127.0.0.1 | Redis地址 |
|
||||
| `REDIS_PORT` | 6379 | Redis端口 |
|
||||
| `REDIS_PASSWORD` | - | Redis密码 |
|
||||
| `REDIS_INDEX` | 0 | 库索引(0-15) |
|
||||
| `REDIS_AUTH` | True | 是否需要认证 |
|
||||
| `REDIS_DECODE_RESPONSES` | True | 是否自动解码 |
|
||||
|
||||
### 邮件配置
|
||||
|
||||
| 配置项 | 默认值 | 说明 |
|
||||
|--------|--------|------|
|
||||
| `MAIL_MAILER` | smtp | 邮寄方式:`smtp` / `sendmail` |
|
||||
| `MAIL_SERVER` | smtp.163.com | SMTP服务器地址 |
|
||||
| `MAIL_PORT` | 465 | SMTP端口(465=SSL,587=TLS) |
|
||||
| `MAIL_USE_SSL` | True | 是否启用SSL |
|
||||
| `MAIL_USERNAME` | - | 授权邮箱用户名 |
|
||||
| `MAIL_PASSWORD` | - | 授权码(非登录密码) |
|
||||
| `MAIL_ENCRYPTION` | - | 加密方式:`ssl` / `tls` |
|
||||
| `MAIL_FROM_NAME` | - | 发件人名称 |
|
||||
| `MAIL_FROM_ADDRESS` | - | 发件人地址 |
|
||||
|
||||
### 短信配置(阿里云)
|
||||
|
||||
| 配置项 | 默认值 | 说明 |
|
||||
|--------|--------|------|
|
||||
| `SMS_PROVIDER` | aliyun | 短信服务商(当前仅支持aliyun) |
|
||||
| `SMS_ALIYUN_ACCESS_KEY_ID` | - | 阿里云 AccessKey ID |
|
||||
| `SMS_ALIYUN_ACCESS_KEY_SECRET` | - | 阿里云 AccessKey Secret |
|
||||
| `SMS_ALIYUN_SIGN_NAME` | - | 短信签名(需在阿里云审核通过) |
|
||||
| `SMS_ALIYUN_ENDPOINT` | dysmsapi.aliyuncs.com | 接入点 |
|
||||
| `SMS_ALIYUN_CONNECT_TIMEOUT` | 10000 | 连接超时(毫秒) |
|
||||
| `SMS_ALIYUN_READ_TIMEOUT` | 10000 | 读取超时(毫秒) |
|
||||
|
||||
### JWT令牌配置
|
||||
|
||||
| 配置项 | 默认值 | 说明 |
|
||||
|--------|--------|------|
|
||||
| `JWT_SALT` | - | JWT签名密钥,至少32字节,务必使用强随机密钥 |
|
||||
| `JWT_EXPIRE_MINUTES` | 20 | 令牌过期时间(分钟) |
|
||||
|
||||
生成密钥:
|
||||
|
||||
```bash
|
||||
python -c "import secrets; print(secrets.token_urlsafe(48))"
|
||||
```
|
||||
|
||||
### 登录白名单配置
|
||||
|
||||
白名单路径免登录验证,支持三种匹配模式:
|
||||
|
||||
| 配置项 | 说明 |
|
||||
|--------|------|
|
||||
| `LOGIN_WHITELIST_EXACT` | 精确匹配,逗号分隔。如 `/login,/captcha` |
|
||||
| `LOGIN_WHITELIST_PREFIX` | 前缀匹配,逗号分隔。如 `/public,/static` |
|
||||
| `LOGIN_WHITELIST_WILDCARD` | 通配符匹配,`*` 匹配任意字符,`?` 匹配单字符 |
|
||||
| `LOGIN_WHITELIST_DEBUG` | 是否打印匹配调试日志 |
|
||||
| `LOGIN_WHITELIST_LOG_ACCESS` | 白名单请求是否记录访问日志 |
|
||||
|
||||
### 权限验证配置
|
||||
|
||||
| 配置项 | 默认值 | 说明 |
|
||||
|--------|--------|------|
|
||||
| `PERMISSION_ENABLED` | True | 是否启用权限验证。False时所有请求放行 |
|
||||
| `INTERNAL_API_KEY` | - | 内部调用API密钥,请求头携带 `X-Internal-API-Key` |
|
||||
| `INTERNAL_ALLOWED_IPS` | 127.0.0.1 | 内部调用白名单IP,逗号分隔 |
|
||||
|
||||
---
|
||||
|
||||
## 启动方式
|
||||
|
||||
### 开发环境
|
||||
|
||||
```bash
|
||||
# 安装依赖
|
||||
pip install -r requirements.txt
|
||||
|
||||
# 启动后端(默认 127.0.0.1:8021)
|
||||
python app.py
|
||||
```
|
||||
|
||||
`python app.py` 执行 `__main__` 块,使用 `app.run()` 启动 Werkzeug 开发服务器。
|
||||
|
||||
开发环境支持 debug 模式和自动重载,由 `.env` 中 `FLASK_DEBUG` 和 `FLASK_USE_RELOADER` 控制。
|
||||
|
||||
前端开发服务器:
|
||||
|
||||
```bash
|
||||
cd ui
|
||||
pnpm install
|
||||
pnpm run dev # 默认 localhost:8001,自动代理 /api 到后端 :8021
|
||||
```
|
||||
|
||||
### 生产环境
|
||||
|
||||
```bash
|
||||
gunicorn -c gunicorn.conf.py app:app
|
||||
```
|
||||
|
||||
Gunicorn 通过 `app:app` 导入 Flask 应用对象,**不执行 `__main__` 块**。`gunicorn.conf.py` 中配置了 gevent worker,支持高并发。
|
||||
|
||||
关键区别:
|
||||
|
||||
| | 开发环境 | 生产环境 |
|
||||
|---|---------|---------|
|
||||
| 启动命令 | `python app.py` | `gunicorn -c gunicorn.conf.py app:app` |
|
||||
| WSGI服务器 | Werkzeug | Gunicorn + gevent |
|
||||
| Debug | 可开启 | 必须关闭 |
|
||||
| 自动重载 | 可开启 | 不支持 |
|
||||
| 并发模型 | 单进程 | 多worker + 协程 |
|
||||
|
||||
### Docker部署
|
||||
|
||||
```bash
|
||||
cd docker
|
||||
|
||||
# 构建并启动
|
||||
docker-compose up -d --build
|
||||
|
||||
# 查看日志
|
||||
docker-compose logs -f flask_elevue
|
||||
|
||||
# 重新构建(代码更新后)
|
||||
docker-compose up -d --build
|
||||
|
||||
# 停止
|
||||
docker-compose down
|
||||
```
|
||||
|
||||
Docker 容器内使用 Gunicorn 启动,Dockerfile 和 docker-compose.yml 已配置好 gevent worker。
|
||||
|
||||
### Gunicorn参数调优
|
||||
|
||||
所有参数通过环境变量覆盖,无需修改配置文件:
|
||||
|
||||
| 环境变量 | 默认值 | 说明 |
|
||||
|----------|--------|------|
|
||||
| `GUNICORN_BIND` | 0.0.0.0:8021 | 监听地址 |
|
||||
| `GUNICORN_WORKERS` | 2*CPU+1 | worker数量 |
|
||||
| `GUNICORN_WORKER_CONNECTIONS` | 1000 | 每worker协程数 |
|
||||
| `GUNICORN_MAX_REQUESTS` | 1000 | 处理请求数后重启(防内存泄漏) |
|
||||
| `GUNICORN_MAX_REQUESTS_JITTER` | 50 | 重启抖动,避免同时重启 |
|
||||
| `GUNICORN_TIMEOUT` | 120 | worker超时(秒) |
|
||||
| `GUNICORN_GRACEFUL_TIMEOUT` | 30 | 优雅关闭超时(秒) |
|
||||
| `GUNICORN_LOG_LEVEL` | info | 日志级别 |
|
||||
| `GUNICORN_ACCESS_LOG` | - | 访问日志路径(- 为stdout) |
|
||||
| `GUNICORN_ERROR_LOG` | - | 错误日志路径(- 为stderr) |
|
||||
|
||||
Docker 中通过 docker-compose.yml 的 `environment` 注入:
|
||||
|
||||
```yaml
|
||||
environment:
|
||||
GUNICORN_WORKERS: 4
|
||||
GUNICORN_WORKER_CONNECTIONS: 2000
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 应用流程
|
||||
|
||||
### 启动流程
|
||||
|
||||
```
|
||||
app.py
|
||||
└─ create_app() # 应用工厂
|
||||
├─ Flask 核心对象创建
|
||||
├─ Redis 配置
|
||||
├─ 数据库配置加载
|
||||
├─ JSON 序列化器设置
|
||||
├─ CORS 跨域配置
|
||||
├─ ProxyFix 可信代理配置
|
||||
├─ register_operation_log() # 操作日志中间件(先于事务注册)
|
||||
├─ register_transaction() # AOP 事务拦截器
|
||||
├─ register_blueprint() # 蓝图自动发现与注册
|
||||
├─ register_extends() # 扩展初始化(DB/Redis/Mail/...)
|
||||
├─ register_middleware() # 登录验证中间件
|
||||
└─ register_error_handlers() # 全局异常处理
|
||||
```
|
||||
|
||||
### 请求处理流程
|
||||
|
||||
```
|
||||
HTTP 请求
|
||||
└─ before_request
|
||||
├─ 操作日志记录(记录请求信息)
|
||||
├─ AOP事务标记(标记事务开始)
|
||||
├─ 登录验证(check_login,白名单路径跳过)
|
||||
└─ 权限验证(check_permission 装饰器,超管/内部调用跳过)
|
||||
└─ View 路由处理
|
||||
└─ Service 业务逻辑
|
||||
└─ Model 数据操作(flush,不commit)
|
||||
└─ after_request
|
||||
├─ AOP事务提交/回滚(POST/PUT/DELETE/PATCH,2xx且code=0时提交)
|
||||
└─ 操作日志写入
|
||||
└─ 响应返回
|
||||
```
|
||||
|
||||
### AOP事务管理
|
||||
|
||||
框架通过 `application/middleware/transaction.py` 实现声明式事务管理,**业务代码无需手动 commit/rollback**。
|
||||
|
||||
- `before_request`:标记事务活跃
|
||||
- `after_request`:
|
||||
- 写操作(POST/PUT/DELETE/PATCH)且 HTTP 2xx 且响应 `code==0` → 自动提交
|
||||
- 业务失败(`R.failed` 返回 HTTP 200 但 `code=1`)→ 自动回滚
|
||||
- `teardown_request`:未处理异常时回滚
|
||||
|
||||
`BaseDB.save()` 和 `BaseDB.save_all()` 仅调用 `db.session.flush()`,由 AOP 拦截器统一提交。
|
||||
|
||||
特殊场景可使用:
|
||||
- `@transactional` 装饰器
|
||||
- `@transactional_response` 装饰器
|
||||
- `TransactionContext` 上下文管理器
|
||||
|
||||
---
|
||||
|
||||
## 邮件服务
|
||||
|
||||
邮件服务封装在 `application/services/mail_svc.py`,基于 Flask-Mail,供业务模块和定时任务直接调用。
|
||||
|
||||
**调用方式:**
|
||||
|
||||
```python
|
||||
from application.services import mail_svc
|
||||
|
||||
# 简单文本邮件
|
||||
mail_svc.send_mail(to='user@example.com', subject='标题', body='内容')
|
||||
|
||||
# HTML 邮件
|
||||
mail_svc.send_mail(to='user@example.com', subject='标题', html='<h1>Hello</h1>')
|
||||
|
||||
# 多收件人 + 抄送
|
||||
mail_svc.send_mail(
|
||||
to=['a@example.com', 'b@example.com'],
|
||||
subject='标题',
|
||||
body='内容',
|
||||
cc=['cc@example.com'],
|
||||
)
|
||||
```
|
||||
|
||||
**参数说明:**
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|------|
|
||||
| `to` | str / List[str] | 是 | 收件人 |
|
||||
| `subject` | str | 是 | 邮件主题 |
|
||||
| `body` | str | 否* | 纯文本正文 |
|
||||
| `html` | str | 否* | HTML正文(与body至少提供一个) |
|
||||
| `cc` | str / List[str] | 否 | 抄送 |
|
||||
| `bcc` | str / List[str] | 否 | 密送 |
|
||||
| `reply_to` | str | 否 | 回复地址 |
|
||||
|
||||
返回 `True` 表示发送成功,`False` 表示失败(异常已捕获并记录日志)。
|
||||
|
||||
**前置条件:** `.env` 中配置 `MAIL_SERVER`、`MAIL_USERNAME`、`MAIL_PASSWORD`。
|
||||
|
||||
---
|
||||
|
||||
## 短信服务
|
||||
|
||||
短信服务封装在 `application/services/sms_svc.py`,基于阿里云短信 SDK,供业务模块和定时任务直接调用。
|
||||
|
||||
**调用方式:**
|
||||
|
||||
```python
|
||||
from application.services import sms_svc
|
||||
|
||||
# 模板短信
|
||||
sms_svc.send_sms(
|
||||
phone='13800138000',
|
||||
template_code='SMS_123456',
|
||||
template_params={'code': '123456'}
|
||||
)
|
||||
|
||||
# 指定签名(覆盖默认签名)
|
||||
sms_svc.send_sms(
|
||||
phone='13800138000',
|
||||
template_code='SMS_123456',
|
||||
template_params={'code': '123456'},
|
||||
sign_name='我的应用'
|
||||
)
|
||||
|
||||
# 群发(多号码)
|
||||
sms_svc.send_sms(
|
||||
phone=['13800138000', '13900139000'],
|
||||
template_code='SMS_123456',
|
||||
template_params={'name': '张三'}
|
||||
)
|
||||
```
|
||||
|
||||
**参数说明:**
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|------|
|
||||
| `phone` | str / List[str] | 是 | 手机号,支持单个或多个 |
|
||||
| `template_code` | str | 是 | 短信模板CODE(阿里云控制台获取) |
|
||||
| `template_params` | dict | 否 | 模板变量,如 `{'code': '123456'}` 对应 `${code}` |
|
||||
| `sign_name` | str | 否 | 短信签名,默认使用配置中的 `SMS_ALIYUN_SIGN_NAME` |
|
||||
|
||||
返回 `True` 表示全部发送成功,`False` 表示至少一条失败。
|
||||
|
||||
**前置条件:**
|
||||
1. `.env` 中配置 `SMS_ALIYUN_ACCESS_KEY_ID`、`SMS_ALIYUN_ACCESS_KEY_SECRET`、`SMS_ALIYUN_SIGN_NAME`
|
||||
2. 安装依赖:`pip install alibabacloud-dysmsapi20170525 alibabacloud-tea-openapi`(已包含在 requirements.txt)
|
||||
@@ -11,7 +11,7 @@
|
||||
→ Gunicorn 通过 `app:app` 导入 app 对象,不执行 __main__ 块
|
||||
"""
|
||||
|
||||
from application.factory import create_app
|
||||
from src.app import create_app
|
||||
from config import FLASK_HOST, FLASK_PORT, FLASK_DEBUG, FLASK_USE_RELOADER
|
||||
|
||||
# 创建应用实例
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
# +======================================================================
|
||||
# | DjangoAdmin 企业数字化开发平台 [ 赋能开发者,助力企业发展 ]
|
||||
# +----------------------------------------------------------------------
|
||||
# | Copyright (c) 2021-2026 一米阳光团队 版权所有
|
||||
# +----------------------------------------------------------------------
|
||||
# | 开源协议: Apache-2.0
|
||||
# | 重要声明: 未经书面许可,不得移除或修改本框架相关版权标识。
|
||||
# +----------------------------------------------------------------------
|
||||
# | 官方网站: https://www.djangoadmin.cn
|
||||
# +----------------------------------------------------------------------
|
||||
# | 研发团队: 一米阳光团队 荣誉出品
|
||||
# +======================================================================
|
||||
# | 版权和免责声明:
|
||||
# | 一、知识产权声明
|
||||
# | 本团队对该软件框架产品(以下简称“本产品”)拥有完整知识产权,包括但不限于商标权、专利权、
|
||||
# | 著作权、商业秘密等。前述权利均受中华人民共和国及相关国际法律法规保护。未经本团队书面授权,
|
||||
# | 任何个人或单位不得基于本产品本身申请任何形式的知识产权。
|
||||
# | 二、使用限制与禁止条款
|
||||
# | 您理解并同意,禁止将本产品用于以下任何行为:
|
||||
# | 1. 任何违反中华人民共和国法律法规的用途;
|
||||
# | 2. 任何侵害他人合法权益(包括但不限于人身权、财产权、知识产权等)的行为;
|
||||
# | 3. 任何危害国家安全、公共利益及社会道德风尚的行为。
|
||||
# | 三、责任豁免
|
||||
# | 因使用本产品而产生的任何损失或法律纠纷(包括但不限于直接损失、间接损失、附带损失、衍生损失
|
||||
# | 以及因违约、侵权等引发的责任),本团队均不承担任何法律责任。若因违规使用本产品导致任何第三
|
||||
# | 方提起索赔或行政机关进行调查,您应独立承担全部责任。
|
||||
# | 四、违约与法律责任
|
||||
# | 如发现任何违反本声明的行为,本团队有权:
|
||||
# | 1. 无条件配合公安机关及其他有权机关的调查取证工作;
|
||||
# | 2. 依法追究相关方的民事责任,并要求赔偿全部损失;
|
||||
# | 3. 保留进一步采取刑事控告等法律措施的权利。
|
||||
# | 五、许可范围说明
|
||||
# | 1、被授权方应视本产品源代码为核心机密信息,负有最高标准的保密与保管义务。禁止以任何形式(包
|
||||
# | 括但不限于直接或间接的分享、复制、传播、分发、转售、出租、开源、反向工程、反编译、反汇编或通
|
||||
# | 过网络服务提供给任何第三方)泄露、披露或处置源代码的全部或部分内容。任何未经授权的复制、使用、
|
||||
# | 传播行为均构成对本团队知识产权的严重侵权,本团队有权依法追究其全部法律责任,并要求赔偿因此造
|
||||
# | 成的一切经济损失及商誉损害。
|
||||
# | 2、本产品仅限于公司或个人在法律允许范围内,用于内部且合规的软件产品研发。具体权利义务以《软件
|
||||
# | 许可协议》附件为准。
|
||||
# +======================================================================
|
||||
|
||||
"""
|
||||
application 包
|
||||
=============================================================
|
||||
|
||||
Flask 应用核心包。
|
||||
|
||||
包结构:
|
||||
- factory.py 应用工厂(create_app)
|
||||
- constants/ 常量定义
|
||||
- forms/ 表单验证
|
||||
- middleware/ 请求/响应中间件
|
||||
- models/ 数据模型
|
||||
- services/ 业务逻辑
|
||||
- views/ 路由控制器
|
||||
|
||||
说明:
|
||||
应用工厂独立于 factory.py,保持本包导入轻量,避免包初始化时
|
||||
触发全量导入(修复循环导入,支持单元测试与模块级导入)。
|
||||
重构方向:本包后续整体迁移至 src/(见重构方案-v2.md)。
|
||||
"""
|
||||
@@ -1,8 +0,0 @@
|
||||
"""
|
||||
兼容 shim(绞杀者迁移期间保留)
|
||||
=============================================================
|
||||
|
||||
已迁移至 src/common/constants/common.py。
|
||||
本文件仅作兼容转发,最终在 P7(切尾)阶段删除。
|
||||
"""
|
||||
from src.common.constants.common import * # noqa: F401,F403
|
||||
@@ -1,8 +0,0 @@
|
||||
"""
|
||||
兼容 shim(绞杀者迁移期间保留)
|
||||
=============================================================
|
||||
|
||||
已迁移至 src/common/constants/dictionary_constants.py。
|
||||
本文件仅作兼容转发,最终在 P7(切尾)阶段删除。
|
||||
"""
|
||||
from src.common.constants.dictionary_constants import * # noqa: F401,F403
|
||||
@@ -1,179 +0,0 @@
|
||||
# +======================================================================
|
||||
# | DjangoAdmin 企业数字化开发平台 [ 赋能开发者,助力企业发展 ]
|
||||
# +----------------------------------------------------------------------
|
||||
# | Copyright (c) 2021-2026 一米阳光团队 版权所有
|
||||
# +----------------------------------------------------------------------
|
||||
# | 开源协议: Apache-2.0
|
||||
# | 重要声明: 未经书面许可,不得移除或修改本框架相关版权标识。
|
||||
# +----------------------------------------------------------------------
|
||||
# | 官方网站: https://www.djangoadmin.cn
|
||||
# +----------------------------------------------------------------------
|
||||
# | 研发团队: 一米阳光团队 荣誉出品
|
||||
# +======================================================================
|
||||
# | 版权和免责声明:
|
||||
# | 一、知识产权声明
|
||||
# | 本团队对该软件框架产品(以下简称“本产品”)拥有完整知识产权,包括但不限于商标权、专利权、
|
||||
# | 著作权、商业秘密等。前述权利均受中华人民共和国及相关国际法律法规保护。未经本团队书面授权,
|
||||
# | 任何个人或单位不得基于本产品本身申请任何形式的知识产权。
|
||||
# | 二、使用限制与禁止条款
|
||||
# | 您理解并同意,禁止将本产品用于以下任何行为:
|
||||
# | 1. 任何违反中华人民共和国法律法规的用途;
|
||||
# | 2. 任何侵害他人合法权益(包括但不限于人身权、财产权、知识产权等)的行为;
|
||||
# | 3. 任何危害国家安全、公共利益及社会道德风尚的行为。
|
||||
# | 三、责任豁免
|
||||
# | 因使用本产品而产生的任何损失或法律纠纷(包括但不限于直接损失、间接损失、附带损失、衍生损失
|
||||
# | 以及因违约、侵权等引发的责任),本团队均不承担任何法律责任。若因违规使用本产品导致任何第三
|
||||
# | 方提起索赔或行政机关进行调查,您应独立承担全部责任。
|
||||
# | 四、违约与法律责任
|
||||
# | 如发现任何违反本声明的行为,本团队有权:
|
||||
# | 1. 无条件配合公安机关及其他有权机关的调查取证工作;
|
||||
# | 2. 依法追究相关方的民事责任,并要求赔偿全部损失;
|
||||
# | 3. 保留进一步采取刑事控告等法律措施的权利。
|
||||
# | 五、许可范围说明
|
||||
# | 1、被授权方应视本产品源代码为核心机密信息,负有最高标准的保密与保管义务。禁止以任何形式(包
|
||||
# | 括但不限于直接或间接的分享、复制、传播、分发、转售、出租、开源、反向工程、反编译、反汇编或通
|
||||
# | 过网络服务提供给任何第三方)泄露、披露或处置源代码的全部或部分内容。任何未经授权的复制、使用、
|
||||
# | 传播行为均构成对本团队知识产权的严重侵权,本团队有权依法追究其全部法律责任,并要求赔偿因此造
|
||||
# | 成的一切经济损失及商誉损害。
|
||||
# | 2、本产品仅限于公司或个人在法律允许范围内,用于内部且合规的软件产品研发。具体权利义务以《软件
|
||||
# | 许可协议》附件为准。
|
||||
# +======================================================================
|
||||
|
||||
"""
|
||||
应用工厂模块
|
||||
=============================================================
|
||||
|
||||
负责创建和配置 Flask 应用实例。
|
||||
|
||||
说明:
|
||||
应用工厂从 application/__init__.py 独立至此模块,使 application
|
||||
包保持轻量,避免包初始化时触发全量导入(修复循环导入,支持单元测试)。
|
||||
重构方向:本模块后续迁移至 src/app.py(见重构方案-v2.md P2)。
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from flask import Flask
|
||||
from flask_cors import CORS
|
||||
from werkzeug.middleware.proxy_fix import ProxyFix
|
||||
|
||||
from application.middleware.transaction import register_transaction
|
||||
from application.middleware import register_middleware
|
||||
from application.middleware.error_handler import register_error_handlers
|
||||
from application.middleware.op_log import register_operation_log
|
||||
from application.views import register_blueprint
|
||||
from config import DatabaseConfig, REDIS_URL
|
||||
from extensions import register_extends
|
||||
from utils.json import JSONEncoder
|
||||
|
||||
# 模型注册中心:确保 Flask-Migrate 全量识别表结构(见 src/models_registry.py)
|
||||
import src.models_registry # noqa: F401
|
||||
|
||||
|
||||
def create_app():
|
||||
"""
|
||||
创建并配置 Flask 应用实例
|
||||
|
||||
配置流程:
|
||||
1. 创建 Flask 核心对象
|
||||
2. 配置 Redis 连接
|
||||
3. 加载数据库配置
|
||||
4. 设置 JSON 序列化器
|
||||
5. 配置跨域资源共享
|
||||
6. 初始化数据库
|
||||
7. 初始化 Redis 缓存
|
||||
8. 注册路由蓝图
|
||||
9. 初始化第三方扩展
|
||||
10. 注册中间件
|
||||
|
||||
Returns:
|
||||
Flask: 配置完成的应用实例
|
||||
"""
|
||||
# ============================================================
|
||||
# 创建 Flask 核心对象
|
||||
# ============================================================
|
||||
app = Flask(
|
||||
__name__,
|
||||
template_folder='../templates',
|
||||
static_folder='../static'
|
||||
)
|
||||
|
||||
# ============================================================
|
||||
# Redis 配置(用于缓存和会话存储)
|
||||
# ============================================================
|
||||
app.config['REDIS_URL'] = REDIS_URL
|
||||
|
||||
# ============================================================
|
||||
# 加载数据库配置
|
||||
# ============================================================
|
||||
app.config.from_object(DatabaseConfig)
|
||||
|
||||
# ============================================================
|
||||
# 自定义 JSON 序列化器(支持 datetime 等特殊类型)
|
||||
# ============================================================
|
||||
app.json = JSONEncoder(app)
|
||||
|
||||
# ============================================================
|
||||
# 跨域资源共享配置
|
||||
# ============================================================
|
||||
# 注意:origins="*" 与 supports_credentials=True 不兼容,
|
||||
# 浏览器会拒绝携带凭证的通配符CORS响应。
|
||||
# 配置了 CORS_ALLOWED_ORIGINS 时使用指定源(支持凭证),
|
||||
# 未配置时使用通配符(不支持凭证)。
|
||||
from config import CORS_ALLOWED_ORIGINS
|
||||
if CORS_ALLOWED_ORIGINS:
|
||||
allowed_origins = [origin.strip() for origin in CORS_ALLOWED_ORIGINS.split(',') if origin.strip()]
|
||||
CORS(
|
||||
app,
|
||||
resources={r"/*": {"origins": allowed_origins}},
|
||||
supports_credentials=True
|
||||
)
|
||||
else:
|
||||
CORS(
|
||||
app,
|
||||
resources={r"/*": {"origins": "*"}},
|
||||
supports_credentials=False
|
||||
)
|
||||
|
||||
# ============================================================
|
||||
# 可信代理配置(反向代理环境下获取真实客户端IP)
|
||||
# ============================================================
|
||||
# PROXY_NUM: 可信代理层数,默认0(无代理,不信任任何转发头)
|
||||
# 设为1表示前面有1层可信代理(如Nginx),ProxyFix会从
|
||||
# X-Forwarded-For 最右侧取1个IP作为真实客户端IP
|
||||
proxy_num = int(os.getenv('PROXY_NUM', 0))
|
||||
if proxy_num > 0:
|
||||
app.wsgi_app = ProxyFix(app.wsgi_app, x_for=proxy_num, x_proto=proxy_num)
|
||||
|
||||
# ============================================================
|
||||
# 注册操作日志自动记录中间件(必须在事务拦截器之前注册,
|
||||
# 确保 after_request 中日志写入先于事务提交,日志随事务一起持久化)
|
||||
# ============================================================
|
||||
register_operation_log(app)
|
||||
|
||||
# ============================================================
|
||||
# 注册 AOP 事务拦截器(核心:一行代码搞定所有事务)
|
||||
# ============================================================
|
||||
register_transaction(app)
|
||||
|
||||
# ============================================================
|
||||
# 注册蓝图(路由分组管理)
|
||||
# ============================================================
|
||||
register_blueprint(app)
|
||||
|
||||
# ============================================================
|
||||
# 初始化第三方扩展
|
||||
# ============================================================
|
||||
register_extends(app)
|
||||
|
||||
# ============================================================
|
||||
# 注册中间件(请求/响应拦截处理)
|
||||
# ============================================================
|
||||
register_middleware(app)
|
||||
|
||||
# ============================================================
|
||||
# 注册全局异常处理器(确保最后注册,能捕获所有环节异常)
|
||||
# ============================================================
|
||||
register_error_handlers(app)
|
||||
|
||||
return app
|
||||
@@ -1,8 +0,0 @@
|
||||
"""
|
||||
兼容 shim(绞杀者迁移期间保留)
|
||||
=============================================================
|
||||
|
||||
表单已迁移至 src.modules.cms.article.schema。
|
||||
本文件仅作兼容转发,最终在 P7(切尾)阶段删除。
|
||||
"""
|
||||
from src.modules.cms.article.schema import * # noqa: F401,F403
|
||||
@@ -1,8 +0,0 @@
|
||||
"""
|
||||
兼容 shim(绞杀者迁移期间保留)
|
||||
=============================================================
|
||||
|
||||
表单基类已迁移至 src/common/forms/base_form.py。
|
||||
本文件仅作兼容转发,最终在 P7(切尾)阶段删除。
|
||||
"""
|
||||
from src.common.forms.base_form import * # noqa: F401,F403
|
||||
@@ -1,8 +0,0 @@
|
||||
"""
|
||||
兼容 shim(绞杀者迁移期间保留)
|
||||
=============================================================
|
||||
|
||||
表单已迁移至 src.modules.cms.category.schema。
|
||||
本文件仅作兼容转发,最终在 P7(切尾)阶段删除。
|
||||
"""
|
||||
from src.modules.cms.category.schema import * # noqa: F401,F403
|
||||
@@ -1,8 +0,0 @@
|
||||
"""
|
||||
兼容 shim(绞杀者迁移期间保留)
|
||||
=============================================================
|
||||
|
||||
表单已迁移至 src.modules.city.schema。
|
||||
本文件仅作兼容转发,最终在 P7(切尾)阶段删除。
|
||||
"""
|
||||
from src.modules.city.schema import * # noqa: F401,F403
|
||||
@@ -1,8 +0,0 @@
|
||||
"""
|
||||
兼容 shim(绞杀者迁移期间保留)
|
||||
=============================================================
|
||||
|
||||
表单已迁移至 src.modules.configuration.schema。
|
||||
本文件仅作兼容转发,最终在 P7(切尾)阶段删除。
|
||||
"""
|
||||
from src.modules.configuration.schema import * # noqa: F401,F403
|
||||
@@ -1,8 +0,0 @@
|
||||
"""
|
||||
兼容 shim(绞杀者迁移期间保留)
|
||||
=============================================================
|
||||
|
||||
表单已迁移至 src.modules.configuration.schema。
|
||||
本文件仅作兼容转发,最终在 P7(切尾)阶段删除。
|
||||
"""
|
||||
from src.modules.configuration.schema import * # noqa: F401,F403
|
||||
@@ -1,8 +0,0 @@
|
||||
"""
|
||||
兼容 shim(绞杀者迁移期间保留)
|
||||
=============================================================
|
||||
|
||||
表单已迁移至 src.modules.system.dept.schema。
|
||||
本文件仅作兼容转发,最终在 P7(切尾)阶段删除。
|
||||
"""
|
||||
from src.modules.system.dept.schema import * # noqa: F401,F403
|
||||
@@ -1,8 +0,0 @@
|
||||
"""
|
||||
兼容 shim(绞杀者迁移期间保留)
|
||||
=============================================================
|
||||
|
||||
表单已迁移至 src.modules.dictionary.schema。
|
||||
本文件仅作兼容转发,最终在 P7(切尾)阶段删除。
|
||||
"""
|
||||
from src.modules.dictionary.schema import * # noqa: F401,F403
|
||||
@@ -1,8 +0,0 @@
|
||||
"""
|
||||
兼容 shim(绞杀者迁移期间保留)
|
||||
=============================================================
|
||||
|
||||
表单已迁移至 src.modules.dictionary.schema。
|
||||
本文件仅作兼容转发,最终在 P7(切尾)阶段删除。
|
||||
"""
|
||||
from src.modules.dictionary.schema import * # noqa: F401,F403
|
||||
@@ -1,8 +0,0 @@
|
||||
"""
|
||||
兼容 shim(绞杀者迁移期间保留)
|
||||
=============================================================
|
||||
|
||||
自定义表单字段已迁移至 src/common/forms/fields.py。
|
||||
本文件仅作兼容转发,最终在 P7(切尾)阶段删除。
|
||||
"""
|
||||
from src.common.forms.fields import * # noqa: F401,F403
|
||||
@@ -1,8 +0,0 @@
|
||||
"""
|
||||
兼容 shim(绞杀者迁移期间保留)
|
||||
=============================================================
|
||||
|
||||
表单已迁移至 src.modules.file_template.schema。
|
||||
本文件仅作兼容转发,最终在 P7(切尾)阶段删除。
|
||||
"""
|
||||
from src.modules.file_template.schema import * # noqa: F401,F403
|
||||
@@ -1,8 +0,0 @@
|
||||
"""
|
||||
兼容 shim(绞杀者迁移期间保留)
|
||||
=============================================================
|
||||
|
||||
表单已迁移至 src.modules.job.schema。
|
||||
本文件仅作兼容转发,最终在 P7(切尾)阶段删除。
|
||||
"""
|
||||
from src.modules.job.schema import * # noqa: F401,F403
|
||||
@@ -1,8 +0,0 @@
|
||||
"""
|
||||
兼容 shim(绞杀者迁移期间保留)
|
||||
=============================================================
|
||||
|
||||
表单已迁移至 src.modules.system.level.schema。
|
||||
本文件仅作兼容转发,最终在 P7(切尾)阶段删除。
|
||||
"""
|
||||
from src.modules.system.level.schema import * # noqa: F401,F403
|
||||
@@ -1,8 +0,0 @@
|
||||
"""
|
||||
兼容 shim(绞杀者迁移期间保留)
|
||||
=============================================================
|
||||
|
||||
表单已迁移至 src.modules.cms.link.schema。
|
||||
本文件仅作兼容转发,最终在 P7(切尾)阶段删除。
|
||||
"""
|
||||
from src.modules.cms.link.schema import * # noqa: F401,F403
|
||||
@@ -1,8 +0,0 @@
|
||||
"""
|
||||
兼容 shim(绞杀者迁移期间保留)
|
||||
=============================================================
|
||||
|
||||
表单已迁移至 src.modules.auth.schema。
|
||||
本文件仅作兼容转发,最终在 P7(切尾)阶段删除。
|
||||
"""
|
||||
from src.modules.auth.schema import * # noqa: F401,F403
|
||||
@@ -1,8 +0,0 @@
|
||||
"""
|
||||
兼容 shim(绞杀者迁移期间保留)
|
||||
=============================================================
|
||||
|
||||
表单已迁移至 src.modules.system.menu.schema。
|
||||
本文件仅作兼容转发,最终在 P7(切尾)阶段删除。
|
||||
"""
|
||||
from src.modules.system.menu.schema import * # noqa: F401,F403
|
||||
@@ -1,8 +0,0 @@
|
||||
"""
|
||||
兼容 shim(绞杀者迁移期间保留)
|
||||
=============================================================
|
||||
|
||||
表单已迁移至 src.modules.notice.schema。
|
||||
本文件仅作兼容转发,最终在 P7(切尾)阶段删除。
|
||||
"""
|
||||
from src.modules.notice.schema import * # noqa: F401,F403
|
||||
@@ -1,8 +0,0 @@
|
||||
"""
|
||||
兼容 shim(绞杀者迁移期间保留)
|
||||
=============================================================
|
||||
|
||||
表单已迁移至 src.modules.param.schema。
|
||||
本文件仅作兼容转发,最终在 P7(切尾)阶段删除。
|
||||
"""
|
||||
from src.modules.param.schema import * # noqa: F401,F403
|
||||
@@ -1,8 +0,0 @@
|
||||
"""
|
||||
兼容 shim(绞杀者迁移期间保留)
|
||||
=============================================================
|
||||
|
||||
表单已迁移至 src.modules.system.position.schema。
|
||||
本文件仅作兼容转发,最终在 P7(切尾)阶段删除。
|
||||
"""
|
||||
from src.modules.system.position.schema import * # noqa: F401,F403
|
||||
@@ -1,8 +0,0 @@
|
||||
"""
|
||||
兼容 shim(绞杀者迁移期间保留)
|
||||
=============================================================
|
||||
|
||||
表单已迁移至 src.modules.system.role.schema。
|
||||
本文件仅作兼容转发,最终在 P7(切尾)阶段删除。
|
||||
"""
|
||||
from src.modules.system.role.schema import * # noqa: F401,F403
|
||||
@@ -1,8 +0,0 @@
|
||||
"""
|
||||
兼容 shim(绞杀者迁移期间保留)
|
||||
=============================================================
|
||||
|
||||
表单已迁移至 src.modules.association.role_menu.schema。
|
||||
本文件仅作兼容转发,最终在 P7(切尾)阶段删除。
|
||||
"""
|
||||
from src.modules.association.role_menu.schema import * # noqa: F401,F403
|
||||
@@ -1,8 +0,0 @@
|
||||
"""
|
||||
兼容 shim(绞杀者迁移期间保留)
|
||||
=============================================================
|
||||
|
||||
表单已迁移至 src.modules.system.user.schema。
|
||||
本文件仅作兼容转发,最终在 P7(切尾)阶段删除。
|
||||
"""
|
||||
from src.modules.system.user.schema import * # noqa: F401,F403
|
||||
@@ -1,8 +0,0 @@
|
||||
"""
|
||||
兼容 shim(绞杀者迁移期间保留)
|
||||
=============================================================
|
||||
|
||||
表单已迁移至 src.modules.association.user_role.schema。
|
||||
本文件仅作兼容转发,最终在 P7(切尾)阶段删除。
|
||||
"""
|
||||
from src.modules.association.user_role.schema import * # noqa: F401,F403
|
||||
@@ -1,8 +0,0 @@
|
||||
# ============================================================
|
||||
# application/middleware/ 目录专用忽略
|
||||
# ============================================================
|
||||
# 说明:
|
||||
# 1. 原文件内容为 `*`,误将整个中间件源码层排除在版本库外,
|
||||
# 已修正。中间件源码必须入库。
|
||||
# 2. 缓存目录由根目录 .gitignore 统一处理。
|
||||
# ============================================================
|
||||
@@ -1,181 +0,0 @@
|
||||
# +======================================================================
|
||||
# | DjangoAdmin 企业数字化开发平台 [ 赋能开发者,助力企业发展 ]
|
||||
# +----------------------------------------------------------------------
|
||||
# | Copyright (c) 2021-2026 一米阳光团队 版权所有
|
||||
# +----------------------------------------------------------------------
|
||||
# | 开源协议: Apache-2.0
|
||||
# | 重要声明: 未经书面许可,不得移除或修改本框架相关版权标识。
|
||||
# +----------------------------------------------------------------------
|
||||
# | 官方网站: https://www.djangoadmin.cn
|
||||
# +----------------------------------------------------------------------
|
||||
# | 研发团队: 一米阳光团队 荣誉出品
|
||||
# +======================================================================
|
||||
# | 版权和免责声明:
|
||||
# | 一、知识产权声明
|
||||
# | 本团队对该软件框架产品(以下简称“本产品”)拥有完整知识产权,包括但不限于商标权、专利权、
|
||||
# | 著作权、商业秘密等。前述权利均受中华人民共和国及相关国际法律法规保护。未经本团队书面授权,
|
||||
# | 任何个人或单位不得基于本产品本身申请任何形式的知识产权。
|
||||
# | 二、使用限制与禁止条款
|
||||
# | 您理解并同意,禁止将本产品用于以下任何行为:
|
||||
# | 1. 任何违反中华人民共和国法律法规的用途;
|
||||
# | 2. 任何侵害他人合法权益(包括但不限于人身权、财产权、知识产权等)的行为;
|
||||
# | 3. 任何危害国家安全、公共利益及社会道德风尚的行为。
|
||||
# | 三、责任豁免
|
||||
# | 因使用本产品而产生的任何损失或法律纠纷(包括但不限于直接损失、间接损失、附带损失、衍生损失
|
||||
# | 以及因违约、侵权等引发的责任),本团队均不承担任何法律责任。若因违规使用本产品导致任何第三
|
||||
# | 方提起索赔或行政机关进行调查,您应独立承担全部责任。
|
||||
# | 四、违约与法律责任
|
||||
# | 如发现任何违反本声明的行为,本团队有权:
|
||||
# | 1. 无条件配合公安机关及其他有权机关的调查取证工作;
|
||||
# | 2. 依法追究相关方的民事责任,并要求赔偿全部损失;
|
||||
# | 3. 保留进一步采取刑事控告等法律措施的权利。
|
||||
# | 五、许可范围说明
|
||||
# | 1、被授权方应视本产品源代码为核心机密信息,负有最高标准的保密与保管义务。禁止以任何形式(包
|
||||
# | 括但不限于直接或间接的分享、复制、传播、分发、转售、出租、开源、反向工程、反编译、反汇编或通
|
||||
# | 过网络服务提供给任何第三方)泄露、披露或处置源代码的全部或部分内容。任何未经授权的复制、使用、
|
||||
# | 传播行为均构成对本团队知识产权的严重侵权,本团队有权依法追究其全部法律责任,并要求赔偿因此造
|
||||
# | 成的一切经济损失及商誉损害。
|
||||
# | 2、本产品仅限于公司或个人在法律允许范围内,用于内部且合规的软件产品研发。具体权利义务以《软件
|
||||
# | 许可协议》附件为准。
|
||||
# +======================================================================
|
||||
|
||||
import fnmatch
|
||||
import hmac
|
||||
import logging
|
||||
from functools import wraps
|
||||
|
||||
from flask import request, g
|
||||
|
||||
from application.services import menu
|
||||
from config import PERMISSION_ENABLED, INTERNAL_API_KEY, INTERNAL_ALLOWED_IPS
|
||||
from utils import R
|
||||
from utils.security import get_user_id
|
||||
|
||||
# 获取日志记录器
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def is_internal_call() -> bool:
|
||||
"""
|
||||
判断是否为内部系统调用(仅定时任务等无用户场景)
|
||||
|
||||
验证方式(满足任一即可):
|
||||
1. 权限验证总开关关闭(开发环境)
|
||||
2. API密钥验证通过(请求头携带正确密钥)
|
||||
3. IP白名单验证通过(来源IP在白名单中)
|
||||
|
||||
Returns:
|
||||
bool: True-内部调用(跳过权限验证),False-普通调用(需要权限验证)
|
||||
"""
|
||||
# ============================================================
|
||||
# 方式1:权限验证总开关关闭(开发/测试环境)
|
||||
# ============================================================
|
||||
if not PERMISSION_ENABLED:
|
||||
logger.debug("权限验证已关闭,所有请求视为内部调用")
|
||||
return True
|
||||
|
||||
# ============================================================
|
||||
# 方式2:API密钥验证(定时任务使用)
|
||||
# ============================================================
|
||||
# 定时任务在请求头中携带 X-Scheduler-Key 或 X-Internal-API-Key
|
||||
if INTERNAL_API_KEY:
|
||||
# 优先检查调度器专用密钥
|
||||
req_key = request.headers.get('X-Scheduler-Key', '')
|
||||
if not req_key:
|
||||
req_key = request.headers.get('X-Internal-API-Key', '')
|
||||
|
||||
if hmac.compare_digest(req_key, INTERNAL_API_KEY):
|
||||
logger.debug(f"内部API密钥验证通过: IP={request.remote_addr}")
|
||||
return True
|
||||
|
||||
# ============================================================
|
||||
# 方式3:IP白名单验证(可选)
|
||||
# ============================================================
|
||||
if INTERNAL_ALLOWED_IPS:
|
||||
client_ip = request.remote_addr
|
||||
allowed_ips = [ip.strip() for ip in INTERNAL_ALLOWED_IPS.split(',') if ip.strip()]
|
||||
|
||||
for allowed_ip in allowed_ips:
|
||||
if fnmatch.fnmatch(client_ip, allowed_ip):
|
||||
logger.debug(f"内部IP白名单验证通过: IP={client_ip}")
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def check_permission(permission: str, log: bool = False):
|
||||
"""
|
||||
权限验证装饰器
|
||||
|
||||
权限验证规则:
|
||||
1. 定时任务等内部调用 → 跳过权限验证,直接放行
|
||||
2. 超级管理员(user_id=1) → 拥有所有权限
|
||||
3. 普通用户 → 验证是否拥有指定权限节点
|
||||
|
||||
Args:
|
||||
permission: 权限节点标识(如:sys:job:page)
|
||||
log: 是否记录访问日志
|
||||
|
||||
Returns:
|
||||
decorator: 装饰器函数
|
||||
"""
|
||||
|
||||
def decorator(func):
|
||||
@wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
# ============================================================
|
||||
# 定时任务等内部调用:跳过权限验证,直接放行
|
||||
# ============================================================
|
||||
if is_internal_call():
|
||||
logger.debug(f"定时任务内部调用,跳过权限验证: {func.__name__}")
|
||||
return func(*args, **kwargs)
|
||||
|
||||
# ============================================================
|
||||
# 普通请求:正常进行权限验证
|
||||
# ============================================================
|
||||
|
||||
# 获取用户ID(优先从g对象获取)
|
||||
user_id = getattr(g, 'user_id', None)
|
||||
if user_id is None:
|
||||
try:
|
||||
user_id = get_user_id()
|
||||
except Exception as e:
|
||||
logger.error(f"获取用户ID失败: {str(e)}")
|
||||
user_id = None
|
||||
|
||||
# 超级管理员放行
|
||||
if user_id == 1:
|
||||
logger.debug(f"超级管理员权限验证通过: user_id={user_id}")
|
||||
return func(*args, **kwargs)
|
||||
|
||||
# 普通用户权限验证
|
||||
if user_id:
|
||||
try:
|
||||
permission_list = menu.get_user_permissions(user_id)
|
||||
|
||||
if permission not in permission_list:
|
||||
if log:
|
||||
logger.warning(
|
||||
f"权限不足: user_id={user_id}, "
|
||||
f"permission={permission}, "
|
||||
f"path={request.path}"
|
||||
)
|
||||
return R.failed(msg="权限不足!", code=403)
|
||||
elif log:
|
||||
logger.info(
|
||||
f"权限验证通过: user_id={user_id}, "
|
||||
f"permission={permission}"
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"权限验证异常: {str(e)}")
|
||||
return R.failed(msg="权限验证失败", code=500)
|
||||
else:
|
||||
# 未登录用户
|
||||
logger.warning(f"未登录用户访问: path={request.path}")
|
||||
return R.failed(msg="请先登录", code=401)
|
||||
|
||||
return func(*args, **kwargs)
|
||||
|
||||
return wrapper
|
||||
|
||||
return decorator
|
||||
@@ -1,220 +0,0 @@
|
||||
"""
|
||||
操作日志自动记录中间件
|
||||
=============================================================
|
||||
|
||||
通过 after_request 钩子自动记录写操作(POST/PUT/DELETE/PATCH)日志。
|
||||
无需在每个视图函数中手动调用 record_operation_log。
|
||||
|
||||
记录规则:
|
||||
- 只记录写操作(POST/PUT/DELETE/PATCH),GET/OPTIONS/HEAD 不记录
|
||||
- 只记录成功操作(HTTP 2xx 且业务 code=0)
|
||||
- 内部调用(定时任务等)不记录
|
||||
- 登录/退出/验证码等路径不记录(由 login_log 单独记录)
|
||||
- 日志记录失败不影响主流程
|
||||
"""
|
||||
|
||||
import logging
|
||||
import time
|
||||
|
||||
from flask import g, request
|
||||
|
||||
from application.models.operation_log import OperationLog
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# 不记录操作日志的路径前缀(登录/退出/验证码等由 login_log 单独记录)
|
||||
_EXCLUDED_PREFIXES = ('/login', '/captcha', '/logout')
|
||||
|
||||
# HTTP 方法 → 操作日志类型 映射
|
||||
# 类型值参考 application/constants/dict.py 的 OPERATION_LOG_TYPE_LIST
|
||||
_METHOD_LOG_TYPE = {
|
||||
'POST': 3, # 新增
|
||||
'PUT': 4, # 修改
|
||||
'DELETE': 5, # 删除
|
||||
'PATCH': 4, # 修改
|
||||
}
|
||||
|
||||
# 路径后缀 → 操作日志类型 覆盖映射(更精确)
|
||||
_PATH_ACTION_TYPE = {
|
||||
'/add': 3, # 新增
|
||||
'/update': 4, # 修改
|
||||
'/delete': 5, # 删除
|
||||
'/status': 4, # 状态变更→修改
|
||||
'/batchdelete': 5, # 批量删除
|
||||
'/reset': 4, # 重置→修改
|
||||
'/save': 4, # 保存→修改
|
||||
'/sort': 4, # 排序→修改
|
||||
}
|
||||
|
||||
|
||||
def register_operation_log(app):
|
||||
"""
|
||||
注册操作日志自动记录中间件
|
||||
|
||||
在 after_request 中自动记录写操作日志。
|
||||
|
||||
Args:
|
||||
app: Flask 应用实例
|
||||
"""
|
||||
|
||||
@app.after_request
|
||||
def record_op_log(response):
|
||||
# 只记录写操作
|
||||
if request.method not in ('POST', 'PUT', 'DELETE', 'PATCH'):
|
||||
return response
|
||||
|
||||
# 排除登录/退出/验证码等路径
|
||||
path = request.path
|
||||
if any(path.startswith(prefix) for prefix in _EXCLUDED_PREFIXES):
|
||||
return response
|
||||
|
||||
# 只记录成功操作(HTTP 2xx 且业务 code=0)
|
||||
if not (200 <= response.status_code < 300):
|
||||
return response
|
||||
|
||||
try:
|
||||
data = response.get_json(silent=True)
|
||||
if data and data.get('code', 0) != 0:
|
||||
return response
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# 内部调用不记录
|
||||
if getattr(g, '_internal_call', False):
|
||||
return response
|
||||
|
||||
# 异步记录,不阻塞响应
|
||||
try:
|
||||
_write_log(response)
|
||||
except Exception as e:
|
||||
logger.warning(f"记录操作日志失败: {str(e)}")
|
||||
|
||||
return response
|
||||
|
||||
logger.info("操作日志中间件注册成功")
|
||||
|
||||
|
||||
def _write_log(response):
|
||||
"""写入操作日志"""
|
||||
from application.services import operation_log
|
||||
from utils.security import get_username
|
||||
from utils.ip import get_client_ip
|
||||
from utils.ip2region import get_ip_location
|
||||
from utils.user_agent import parse_user_agent
|
||||
|
||||
username = get_username() or 'system'
|
||||
client_ip = get_client_ip()
|
||||
ua_info = parse_user_agent()
|
||||
|
||||
# 确定操作类型
|
||||
log_type = _infer_log_type(request.method, request.path)
|
||||
|
||||
# 构建标题:从路径提取模块名,如 /user/add → "用户管理-新增"
|
||||
title = _build_title(request.path)
|
||||
|
||||
# 请求参数(限制长度,避免存入过大的数据)
|
||||
param = _get_request_param()
|
||||
|
||||
# 返回结果(限制长度)
|
||||
result = _get_response_result(response)
|
||||
|
||||
# 请求耗时
|
||||
start_time = getattr(g, 'request_start_time', None)
|
||||
consume_time = int((time.time() - start_time) * 1000) if start_time else 0
|
||||
|
||||
operation_log.record_operation_log(
|
||||
title=title,
|
||||
log_type=log_type,
|
||||
source=OperationLog.Source.ADMIN,
|
||||
status=OperationLog.Status.NORMAL,
|
||||
method=request.method,
|
||||
request_method=request.endpoint or '',
|
||||
url=request.path,
|
||||
param=param,
|
||||
result=result,
|
||||
ip=client_ip,
|
||||
location=get_ip_location(client_ip),
|
||||
os=ua_info.get('os', '未知'),
|
||||
browser=ua_info.get('browser', '未知'),
|
||||
consume_time=consume_time,
|
||||
error='',
|
||||
username=username,
|
||||
)
|
||||
|
||||
|
||||
def _infer_log_type(method, path):
|
||||
"""根据请求方法和路径推断操作日志类型"""
|
||||
# 优先根据路径后缀匹配
|
||||
path_lower = path.lower()
|
||||
for suffix, log_type in _PATH_ACTION_TYPE.items():
|
||||
if path_lower.endswith(suffix):
|
||||
return log_type
|
||||
|
||||
# 回退到 HTTP 方法映射
|
||||
return _METHOD_LOG_TYPE.get(method, 18) # 18=其他
|
||||
|
||||
|
||||
def _get_request_param():
|
||||
"""获取请求参数"""
|
||||
try:
|
||||
if request.method in ('POST', 'PUT', 'PATCH', 'DELETE'):
|
||||
# 优先解析 JSON body(batchDelete 等 DELETE 请求也带 body)
|
||||
data = request.get_json(silent=True)
|
||||
if data:
|
||||
import json
|
||||
return json.dumps(data, ensure_ascii=False)
|
||||
# 回退到 URL 查询参数
|
||||
if request.args:
|
||||
import json
|
||||
from collections import OrderedDict
|
||||
return json.dumps(OrderedDict(request.args), ensure_ascii=False)
|
||||
except Exception:
|
||||
pass
|
||||
return ''
|
||||
|
||||
|
||||
def _get_response_result(response):
|
||||
"""获取响应结果中的data字段"""
|
||||
try:
|
||||
data = response.get_json(silent=True)
|
||||
if data and isinstance(data, dict):
|
||||
result_data = data.get('data')
|
||||
if result_data is not None:
|
||||
import json
|
||||
return json.dumps(result_data, ensure_ascii=False)
|
||||
# data字段为空时取msg
|
||||
return data.get('msg', '')
|
||||
except Exception:
|
||||
pass
|
||||
return ''
|
||||
|
||||
|
||||
def _build_title(path):
|
||||
"""从请求路径构建操作标题"""
|
||||
# 路径格式: /user/add, /role/update, /menu/delete 等
|
||||
parts = path.strip('/').split('/')
|
||||
if len(parts) >= 2:
|
||||
module = parts[0]
|
||||
# 模块名中文化映射
|
||||
module_names = {
|
||||
'user': '用户管理', 'role': '角色管理', 'menu': '菜单管理',
|
||||
'dept': '部门管理', 'position': '岗位管理', 'level': '职级管理',
|
||||
'param': '参数配置', 'dict': '字典管理', 'dictitem': '字典项管理',
|
||||
'config': '系统配置', 'configitem': '配置项管理',
|
||||
'category': '分类管理', 'article': '文章管理',
|
||||
'notice': '通知管理', 'link': '友情链接',
|
||||
'job': '定时任务', 'login': '登录管理',
|
||||
'loginlog': '登录日志', 'operationlog': '操作日志',
|
||||
'upload': '文件上传',
|
||||
}
|
||||
module_name = module_names.get(module.lower(), module)
|
||||
action = parts[-1] if len(parts) > 1 else ''
|
||||
action_names = {
|
||||
'add': '新增', 'update': '修改', 'delete': '删除',
|
||||
'status': '状态变更', 'batchdelete': '批量删除',
|
||||
'reset': '重置', 'save': '保存', 'sort': '排序',
|
||||
'page': '查询', 'detail': '查询', 'list': '查询',
|
||||
}
|
||||
action_name = action_names.get(action.lower(), action)
|
||||
return f"{module_name}-{action_name}" if action_name else module_name
|
||||
return path
|
||||
@@ -1,8 +0,0 @@
|
||||
# ============================================================
|
||||
# application/models/ 目录专用忽略
|
||||
# ============================================================
|
||||
# 说明:
|
||||
# 1. 原文件内容为 `*`,误将整个模型源码层排除在版本库外,
|
||||
# 已修正。模型源码必须入库。
|
||||
# 2. 缓存目录由根目录 .gitignore 统一处理。
|
||||
# ============================================================
|
||||
@@ -1,22 +0,0 @@
|
||||
from . import article
|
||||
from . import category
|
||||
from . import city
|
||||
from . import config
|
||||
from . import config_item
|
||||
from . import dept
|
||||
from . import dict
|
||||
from . import dict_item
|
||||
from . import file_template
|
||||
from . import job
|
||||
from . import level
|
||||
from . import link
|
||||
from . import login_log
|
||||
from . import menu
|
||||
from . import notice
|
||||
from . import operation_log
|
||||
from . import param
|
||||
from . import position
|
||||
from . import role
|
||||
from . import role_menu
|
||||
from . import user
|
||||
from . import user_role
|
||||
@@ -1,8 +0,0 @@
|
||||
"""
|
||||
兼容 shim(绞杀者迁移期间保留)
|
||||
=============================================================
|
||||
|
||||
模型已迁移至 src.modules.cms.article.models。
|
||||
本文件仅作兼容转发,最终在 P7(切尾)阶段删除。
|
||||
"""
|
||||
from src.modules.cms.article.models import * # noqa: F401,F403
|
||||
@@ -1,8 +0,0 @@
|
||||
"""
|
||||
兼容 shim(绞杀者迁移期间保留)
|
||||
=============================================================
|
||||
|
||||
数据库操作基类已迁移至 src.common.base_db.py。
|
||||
本文件仅作兼容转发,最终在 P7(切尾)阶段删除。
|
||||
"""
|
||||
from src.common.base_db import * # noqa: F401,F403
|
||||
@@ -1,8 +0,0 @@
|
||||
"""
|
||||
兼容 shim(绞杀者迁移期间保留)
|
||||
=============================================================
|
||||
|
||||
模型基类已迁移至 src.common.base_model.py。
|
||||
本文件仅作兼容转发,最终在 P7(切尾)阶段删除。
|
||||
"""
|
||||
from src.common.base_model import * # noqa: F401,F403
|
||||
@@ -1,8 +0,0 @@
|
||||
"""
|
||||
兼容 shim(绞杀者迁移期间保留)
|
||||
=============================================================
|
||||
|
||||
模型已迁移至 src.modules.cms.category.models。
|
||||
本文件仅作兼容转发,最终在 P7(切尾)阶段删除。
|
||||
"""
|
||||
from src.modules.cms.category.models import * # noqa: F401,F403
|
||||
@@ -1,8 +0,0 @@
|
||||
"""
|
||||
兼容 shim(绞杀者迁移期间保留)
|
||||
=============================================================
|
||||
|
||||
模型已迁移至 src.modules.city.models。
|
||||
本文件仅作兼容转发,最终在 P7(切尾)阶段删除。
|
||||
"""
|
||||
from src.modules.city.models import * # noqa: F401,F403
|
||||
@@ -1,8 +0,0 @@
|
||||
"""
|
||||
兼容 shim(绞杀者迁移期间保留)
|
||||
=============================================================
|
||||
|
||||
模型已迁移至 src.modules.configuration.models。
|
||||
本文件仅作兼容转发,最终在 P7(切尾)阶段删除。
|
||||
"""
|
||||
from src.modules.configuration.models import * # noqa: F401,F403
|
||||
@@ -1,8 +0,0 @@
|
||||
"""
|
||||
兼容 shim(绞杀者迁移期间保留)
|
||||
=============================================================
|
||||
|
||||
模型已迁移至 src.modules.configuration.models。
|
||||
本文件仅作兼容转发,最终在 P7(切尾)阶段删除。
|
||||
"""
|
||||
from src.modules.configuration.models import * # noqa: F401,F403
|
||||
@@ -1,8 +0,0 @@
|
||||
"""
|
||||
兼容 shim(绞杀者迁移期间保留)
|
||||
=============================================================
|
||||
|
||||
模型已迁移至 src.modules.system.dept.models。
|
||||
本文件仅作兼容转发,最终在 P7(切尾)阶段删除。
|
||||
"""
|
||||
from src.modules.system.dept.models import * # noqa: F401,F403
|
||||
@@ -1,8 +0,0 @@
|
||||
"""
|
||||
兼容 shim(绞杀者迁移期间保留)
|
||||
=============================================================
|
||||
|
||||
模型已迁移至 src.modules.dictionary.models。
|
||||
本文件仅作兼容转发,最终在 P7(切尾)阶段删除。
|
||||
"""
|
||||
from src.modules.dictionary.models import * # noqa: F401,F403
|
||||
@@ -1,8 +0,0 @@
|
||||
"""
|
||||
兼容 shim(绞杀者迁移期间保留)
|
||||
=============================================================
|
||||
|
||||
模型已迁移至 src.modules.dictionary.models。
|
||||
本文件仅作兼容转发,最终在 P7(切尾)阶段删除。
|
||||
"""
|
||||
from src.modules.dictionary.models import * # noqa: F401,F403
|
||||
@@ -1,8 +0,0 @@
|
||||
"""
|
||||
兼容 shim(绞杀者迁移期间保留)
|
||||
=============================================================
|
||||
|
||||
模型已迁移至 src.modules.file_template.models。
|
||||
本文件仅作兼容转发,最终在 P7(切尾)阶段删除。
|
||||
"""
|
||||
from src.modules.file_template.models import * # noqa: F401,F403
|
||||
@@ -1,8 +0,0 @@
|
||||
"""
|
||||
兼容 shim(绞杀者迁移期间保留)
|
||||
=============================================================
|
||||
|
||||
模型已迁移至 src.modules.job.models。
|
||||
本文件仅作兼容转发,最终在 P7(切尾)阶段删除。
|
||||
"""
|
||||
from src.modules.job.models import * # noqa: F401,F403
|
||||
@@ -1,8 +0,0 @@
|
||||
"""
|
||||
兼容 shim(绞杀者迁移期间保留)
|
||||
=============================================================
|
||||
|
||||
模型已迁移至 src.modules.system.level.models。
|
||||
本文件仅作兼容转发,最终在 P7(切尾)阶段删除。
|
||||
"""
|
||||
from src.modules.system.level.models import * # noqa: F401,F403
|
||||
@@ -1,8 +0,0 @@
|
||||
"""
|
||||
兼容 shim(绞杀者迁移期间保留)
|
||||
=============================================================
|
||||
|
||||
模型已迁移至 src.modules.cms.link.models。
|
||||
本文件仅作兼容转发,最终在 P7(切尾)阶段删除。
|
||||
"""
|
||||
from src.modules.cms.link.models import * # noqa: F401,F403
|
||||
@@ -1,8 +0,0 @@
|
||||
"""
|
||||
兼容 shim(绞杀者迁移期间保留)
|
||||
=============================================================
|
||||
|
||||
模型已迁移至 src.modules.log.models。
|
||||
本文件仅作兼容转发,最终在 P7(切尾)阶段删除。
|
||||
"""
|
||||
from src.modules.log.models import * # noqa: F401,F403
|
||||
@@ -1,8 +0,0 @@
|
||||
"""
|
||||
兼容 shim(绞杀者迁移期间保留)
|
||||
=============================================================
|
||||
|
||||
模型已迁移至 src.modules.system.menu.models。
|
||||
本文件仅作兼容转发,最终在 P7(切尾)阶段删除。
|
||||
"""
|
||||
from src.modules.system.menu.models import * # noqa: F401,F403
|
||||
@@ -1,8 +0,0 @@
|
||||
"""
|
||||
兼容 shim(绞杀者迁移期间保留)
|
||||
=============================================================
|
||||
|
||||
模型已迁移至 src.modules.notice.models。
|
||||
本文件仅作兼容转发,最终在 P7(切尾)阶段删除。
|
||||
"""
|
||||
from src.modules.notice.models import * # noqa: F401,F403
|
||||
@@ -1,8 +0,0 @@
|
||||
"""
|
||||
兼容 shim(绞杀者迁移期间保留)
|
||||
=============================================================
|
||||
|
||||
模型已迁移至 src.modules.log.models。
|
||||
本文件仅作兼容转发,最终在 P7(切尾)阶段删除。
|
||||
"""
|
||||
from src.modules.log.models import * # noqa: F401,F403
|
||||
@@ -1,8 +0,0 @@
|
||||
"""
|
||||
兼容 shim(绞杀者迁移期间保留)
|
||||
=============================================================
|
||||
|
||||
模型已迁移至 src.modules.param.models。
|
||||
本文件仅作兼容转发,最终在 P7(切尾)阶段删除。
|
||||
"""
|
||||
from src.modules.param.models import * # noqa: F401,F403
|
||||
@@ -1,8 +0,0 @@
|
||||
"""
|
||||
兼容 shim(绞杀者迁移期间保留)
|
||||
=============================================================
|
||||
|
||||
模型已迁移至 src.modules.system.position.models。
|
||||
本文件仅作兼容转发,最终在 P7(切尾)阶段删除。
|
||||
"""
|
||||
from src.modules.system.position.models import * # noqa: F401,F403
|
||||
@@ -1,8 +0,0 @@
|
||||
"""
|
||||
兼容 shim(绞杀者迁移期间保留)
|
||||
=============================================================
|
||||
|
||||
模型已迁移至 src.modules.system.role.models。
|
||||
本文件仅作兼容转发,最终在 P7(切尾)阶段删除。
|
||||
"""
|
||||
from src.modules.system.role.models import * # noqa: F401,F403
|
||||
@@ -1,8 +0,0 @@
|
||||
"""
|
||||
兼容 shim(绞杀者迁移期间保留)
|
||||
=============================================================
|
||||
|
||||
模型已迁移至 src.modules.association.role_menu.models。
|
||||
本文件仅作兼容转发,最终在 P7(切尾)阶段删除。
|
||||
"""
|
||||
from src.modules.association.role_menu.models import * # noqa: F401,F403
|
||||
@@ -1,8 +0,0 @@
|
||||
"""
|
||||
兼容 shim(绞杀者迁移期间保留)
|
||||
=============================================================
|
||||
|
||||
模型已迁移至 src.modules.system.user.models。
|
||||
本文件仅作兼容转发,最终在 P7(切尾)阶段删除。
|
||||
"""
|
||||
from src.modules.system.user.models import * # noqa: F401,F403
|
||||
@@ -1,8 +0,0 @@
|
||||
"""
|
||||
兼容 shim(绞杀者迁移期间保留)
|
||||
=============================================================
|
||||
|
||||
模型已迁移至 src.modules.association.user_role.models。
|
||||
本文件仅作兼容转发,最终在 P7(切尾)阶段删除。
|
||||
"""
|
||||
from src.modules.association.user_role.models import * # noqa: F401,F403
|
||||
@@ -1,26 +0,0 @@
|
||||
from . import article
|
||||
from . import category
|
||||
from . import city
|
||||
from . import config
|
||||
from . import config_item
|
||||
from . import dept
|
||||
from . import dict
|
||||
from . import dict_item
|
||||
from . import job_executor
|
||||
from . import file_template
|
||||
from . import job
|
||||
from . import level
|
||||
from . import link
|
||||
from . import login_log
|
||||
from . import login
|
||||
from . import menu
|
||||
from . import notice
|
||||
from . import operation_log
|
||||
from . import param
|
||||
from . import position
|
||||
from . import role
|
||||
from . import role_menu
|
||||
from . import scheduler
|
||||
from . import upload
|
||||
from . import user
|
||||
from . import user_role
|
||||
@@ -1,8 +0,0 @@
|
||||
"""
|
||||
兼容 shim(绞杀者迁移期间保留)
|
||||
=============================================================
|
||||
|
||||
服务已迁移至 src.modules.cms.article.service。
|
||||
本文件仅作兼容转发,最终在 P7(切尾)阶段删除。
|
||||
"""
|
||||
from src.modules.cms.article.service import * # noqa: F401,F403
|
||||
@@ -1,8 +0,0 @@
|
||||
"""
|
||||
兼容 shim(绞杀者迁移期间保留)
|
||||
=============================================================
|
||||
|
||||
服务已迁移至 src.modules.cms.category.service。
|
||||
本文件仅作兼容转发,最终在 P7(切尾)阶段删除。
|
||||
"""
|
||||
from src.modules.cms.category.service import * # noqa: F401,F403
|
||||
@@ -1,8 +0,0 @@
|
||||
"""
|
||||
兼容 shim(绞杀者迁移期间保留)
|
||||
=============================================================
|
||||
|
||||
服务已迁移至 src.modules.city.service。
|
||||
本文件仅作兼容转发,最终在 P7(切尾)阶段删除。
|
||||
"""
|
||||
from src.modules.city.service import * # noqa: F401,F403
|
||||
@@ -1,8 +0,0 @@
|
||||
"""
|
||||
兼容 shim(绞杀者迁移期间保留)
|
||||
=============================================================
|
||||
|
||||
服务已迁移至 src.modules.configuration.service。
|
||||
本文件仅作兼容转发,最终在 P7(切尾)阶段删除。
|
||||
"""
|
||||
from src.modules.configuration.service import * # noqa: F401,F403
|
||||
@@ -1,8 +0,0 @@
|
||||
"""
|
||||
兼容 shim(绞杀者迁移期间保留)
|
||||
=============================================================
|
||||
|
||||
服务已迁移至 src.modules.configuration.service。
|
||||
本文件仅作兼容转发,最终在 P7(切尾)阶段删除。
|
||||
"""
|
||||
from src.modules.configuration.service import * # noqa: F401,F403
|
||||
@@ -1,8 +0,0 @@
|
||||
"""
|
||||
兼容 shim(绞杀者迁移期间保留)
|
||||
=============================================================
|
||||
|
||||
服务已迁移至 src.modules.system.dept.service。
|
||||
本文件仅作兼容转发,最终在 P7(切尾)阶段删除。
|
||||
"""
|
||||
from src.modules.system.dept.service import * # noqa: F401,F403
|
||||
@@ -1,8 +0,0 @@
|
||||
"""
|
||||
兼容 shim(绞杀者迁移期间保留)
|
||||
=============================================================
|
||||
|
||||
服务已迁移至 src.modules.dictionary.service。
|
||||
本文件仅作兼容转发,最终在 P7(切尾)阶段删除。
|
||||
"""
|
||||
from src.modules.dictionary.service import * # noqa: F401,F403
|
||||
@@ -1,8 +0,0 @@
|
||||
"""
|
||||
兼容 shim(绞杀者迁移期间保留)
|
||||
=============================================================
|
||||
|
||||
服务已迁移至 src.modules.dictionary.service。
|
||||
本文件仅作兼容转发,最终在 P7(切尾)阶段删除。
|
||||
"""
|
||||
from src.modules.dictionary.service import * # noqa: F401,F403
|
||||
@@ -1,8 +0,0 @@
|
||||
"""
|
||||
兼容 shim(绞杀者迁移期间保留)
|
||||
=============================================================
|
||||
|
||||
服务已迁移至 src.modules.file_template.service。
|
||||
本文件仅作兼容转发,最终在 P7(切尾)阶段删除。
|
||||
"""
|
||||
from src.modules.file_template.service import * # noqa: F401,F403
|
||||
@@ -1,8 +0,0 @@
|
||||
"""
|
||||
兼容 shim(绞杀者迁移期间保留)
|
||||
=============================================================
|
||||
|
||||
服务已迁移至 src.modules.job.service。
|
||||
本文件仅作兼容转发,最终在 P7(切尾)阶段删除。
|
||||
"""
|
||||
from src.modules.job.service import * # noqa: F401,F403
|
||||
@@ -1,8 +0,0 @@
|
||||
"""
|
||||
兼容 shim(绞杀者迁移期间保留)
|
||||
=============================================================
|
||||
|
||||
服务已迁移至 src.modules.job.executor。
|
||||
本文件仅作兼容转发,最终在 P7(切尾)阶段删除。
|
||||
"""
|
||||
from src.modules.job.executor import * # noqa: F401,F403
|
||||
@@ -1,8 +0,0 @@
|
||||
"""
|
||||
兼容 shim(绞杀者迁移期间保留)
|
||||
=============================================================
|
||||
|
||||
服务已迁移至 src.modules.system.level.service。
|
||||
本文件仅作兼容转发,最终在 P7(切尾)阶段删除。
|
||||
"""
|
||||
from src.modules.system.level.service import * # noqa: F401,F403
|
||||
@@ -1,8 +0,0 @@
|
||||
"""
|
||||
兼容 shim(绞杀者迁移期间保留)
|
||||
=============================================================
|
||||
|
||||
服务已迁移至 src.modules.cms.link.service。
|
||||
本文件仅作兼容转发,最终在 P7(切尾)阶段删除。
|
||||
"""
|
||||
from src.modules.cms.link.service import * # noqa: F401,F403
|
||||
@@ -1,8 +0,0 @@
|
||||
"""
|
||||
兼容 shim(绞杀者迁移期间保留)
|
||||
=============================================================
|
||||
|
||||
服务已迁移至 src.modules.auth.service。
|
||||
本文件仅作兼容转发,最终在 P7(切尾)阶段删除。
|
||||
"""
|
||||
from src.modules.auth.service import * # noqa: F401,F403
|
||||
@@ -1,8 +0,0 @@
|
||||
"""
|
||||
兼容 shim(绞杀者迁移期间保留)
|
||||
=============================================================
|
||||
|
||||
服务已迁移至 src.modules.log.service。
|
||||
本文件仅作兼容转发,最终在 P7(切尾)阶段删除。
|
||||
"""
|
||||
from src.modules.log.service import * # noqa: F401,F403
|
||||
@@ -1,8 +0,0 @@
|
||||
"""
|
||||
兼容 shim(绞杀者迁移期间保留)
|
||||
=============================================================
|
||||
|
||||
服务已迁移至 src.modules.job.mail_service。
|
||||
本文件仅作兼容转发,最终在 P7(切尾)阶段删除。
|
||||
"""
|
||||
from src.modules.job.mail_service import * # noqa: F401,F403
|
||||
@@ -1,8 +0,0 @@
|
||||
"""
|
||||
兼容 shim(绞杀者迁移期间保留)
|
||||
=============================================================
|
||||
|
||||
服务已迁移至 src.modules.system.menu.service。
|
||||
本文件仅作兼容转发,最终在 P7(切尾)阶段删除。
|
||||
"""
|
||||
from src.modules.system.menu.service import * # noqa: F401,F403
|
||||
@@ -1,8 +0,0 @@
|
||||
"""
|
||||
兼容 shim(绞杀者迁移期间保留)
|
||||
=============================================================
|
||||
|
||||
服务已迁移至 src.modules.notice.service。
|
||||
本文件仅作兼容转发,最终在 P7(切尾)阶段删除。
|
||||
"""
|
||||
from src.modules.notice.service import * # noqa: F401,F403
|
||||
@@ -1,8 +0,0 @@
|
||||
"""
|
||||
兼容 shim(绞杀者迁移期间保留)
|
||||
=============================================================
|
||||
|
||||
服务已迁移至 src.modules.log.service。
|
||||
本文件仅作兼容转发,最终在 P7(切尾)阶段删除。
|
||||
"""
|
||||
from src.modules.log.service import * # noqa: F401,F403
|
||||
@@ -1,8 +0,0 @@
|
||||
"""
|
||||
兼容 shim(绞杀者迁移期间保留)
|
||||
=============================================================
|
||||
|
||||
服务已迁移至 src.modules.param.service。
|
||||
本文件仅作兼容转发,最终在 P7(切尾)阶段删除。
|
||||
"""
|
||||
from src.modules.param.service import * # noqa: F401,F403
|
||||
@@ -1,8 +0,0 @@
|
||||
"""
|
||||
兼容 shim(绞杀者迁移期间保留)
|
||||
=============================================================
|
||||
|
||||
服务已迁移至 src.modules.system.position.service。
|
||||
本文件仅作兼容转发,最终在 P7(切尾)阶段删除。
|
||||
"""
|
||||
from src.modules.system.position.service import * # noqa: F401,F403
|
||||
@@ -1,8 +0,0 @@
|
||||
"""
|
||||
兼容 shim(绞杀者迁移期间保留)
|
||||
=============================================================
|
||||
|
||||
服务已迁移至 src.modules.system.role.service。
|
||||
本文件仅作兼容转发,最终在 P7(切尾)阶段删除。
|
||||
"""
|
||||
from src.modules.system.role.service import * # noqa: F401,F403
|
||||
@@ -1,8 +0,0 @@
|
||||
"""
|
||||
兼容 shim(绞杀者迁移期间保留)
|
||||
=============================================================
|
||||
|
||||
服务已迁移至 src.modules.association.role_menu.service。
|
||||
本文件仅作兼容转发,最终在 P7(切尾)阶段删除。
|
||||
"""
|
||||
from src.modules.association.role_menu.service import * # noqa: F401,F403
|
||||
@@ -1,8 +0,0 @@
|
||||
"""
|
||||
兼容 shim(绞杀者迁移期间保留)
|
||||
=============================================================
|
||||
|
||||
服务已迁移至 src.modules.job.scheduler_service。
|
||||
本文件仅作兼容转发,最终在 P7(切尾)阶段删除。
|
||||
"""
|
||||
from src.modules.job.scheduler_service import * # noqa: F401,F403
|
||||
@@ -1,8 +0,0 @@
|
||||
"""
|
||||
兼容 shim(绞杀者迁移期间保留)
|
||||
=============================================================
|
||||
|
||||
服务已迁移至 src.modules.job.sms_service。
|
||||
本文件仅作兼容转发,最终在 P7(切尾)阶段删除。
|
||||
"""
|
||||
from src.modules.job.sms_service import * # noqa: F401,F403
|
||||
@@ -1,8 +0,0 @@
|
||||
"""
|
||||
兼容 shim(绞杀者迁移期间保留)
|
||||
=============================================================
|
||||
|
||||
服务已迁移至 src.modules.upload.service。
|
||||
本文件仅作兼容转发,最终在 P7(切尾)阶段删除。
|
||||
"""
|
||||
from src.modules.upload.service import * # noqa: F401,F403
|
||||
@@ -1,8 +0,0 @@
|
||||
"""
|
||||
兼容 shim(绞杀者迁移期间保留)
|
||||
=============================================================
|
||||
|
||||
服务已迁移至 src.modules.system.user.service。
|
||||
本文件仅作兼容转发,最终在 P7(切尾)阶段删除。
|
||||
"""
|
||||
from src.modules.system.user.service import * # noqa: F401,F403
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user