Compare commits

...
54 Commits
Author SHA1 Message Date
insistence 6cb2afca39 feat: 新增github actions配置文件 2025-12-19 16:30:24 +08:00
insistence d47613970b docs: 新增更新日志文档 2025-12-19 16:13:27 +08:00
insistence 8833980008 docs: 更新README文档 2025-12-19 11:20:19 +08:00
insistence 0067e66fc8 chore: 升级版本至1.8.0 2025-12-19 11:20:09 +08:00
insistence 39c4590c3f chore: 更新后端依赖 2025-12-19 11:05:48 +08:00
insistence 3196962197 fix: 修复代码生成v3模板时间控件between选择后清空报错问题 2025-12-19 10:31:52 +08:00
insistence 1ffaaf5225 perf: 优化topbar顶部菜单样式 2025-12-19 09:35:44 +08:00
insistence 4ad671023d perf: 默认固定头部 2025-12-19 09:30:05 +08:00
insistence 45bd2ddb21 perf: 优化字典组件值宽松匹配 2025-12-19 09:28:13 +08:00
insistence 267f76d6a2 perf: 优化字典组件数字类型值处理逻辑 2025-12-19 09:26:48 +08:00
insistence 67c09f3113 perf: 优化index页面标题读取配置 2025-12-19 09:25:32 +08:00
insistence c8da8d5406 perf: 优化操作日志详细请求参数显示 2025-12-19 09:24:40 +08:00
insistence 6ad0a63c68 feat: 菜单导航设置支持纯顶部 2025-12-19 09:23:19 +08:00
insistence ab4eb00bc1 行内表单默认设置固定宽度 2025-12-19 09:05:14 +08:00
insistence 954085666a perf: 优化表单构建关闭页签销毁复制插件 2025-12-19 09:02:58 +08:00
insistence 35aa26f8a9 perf: 优化生成代码下载的zip文件名 2025-12-19 09:01:01 +08:00
insistence 1e281508b2 perf: 登录/注册页面底部版权信息修改为读取配置 2025-12-19 08:45:10 +08:00
insistence a4e4c20c5e fix: 修复表单构建移除所有控件后切换路由回来空白问题 2025-12-19 08:42:46 +08:00
insistence d4b85f38d8 fix: 修复固定头部时出现的导航栏偏移问题 2025-12-19 08:38:41 +08:00
insistence ed88512bf8 feat: 代码生成controller模版使用APIRouterPro 2025-12-19 08:23:00 +08:00
insistence b9b2f713b9 feat: 新增docker compose部署方式 2025-12-18 16:03:44 +08:00
insistence b1563b0d39 fix: 移除代码生成表业务表外键,修复无法删除的问题 2025-12-17 17:02:42 +08:00
insistence 1857663c9f feat: 新增自动注册路由功能 2025-12-17 15:52:22 +08:00
insistence a28922acf0 style: 格式化代码 2025-12-17 15:51:36 +08:00
insistence cdacb1643c feat: 新增配置文档静态资源方法 2025-12-16 16:15:52 +08:00
insistence ad0327bf51 refactor: 重构app与server设计 2025-12-16 14:38:31 +08:00
insistence 50af1bf573 fix: 修复在线用户列表与PageResponseModel不兼容的问题 2025-12-15 17:08:43 +08:00
insistence d364394575 fix: 修复磁盘存在异常时服务监控无法正常运行的问题 2025-12-15 16:49:30 +08:00
insistence 98651f4d8f perf: 接口API文档的接口描述信息 2025-12-15 16:18:20 +08:00
insistence 40ccb84922 perf: 优化所有接口的API文档响应模型显示 2025-12-15 15:02:49 +08:00
insistence d9a19ce3ee perf: 操作响应模型改为使用公共vo模块的CrudResponseModel 2025-12-13 11:28:43 +08:00
insistence e216b97163 perf: 代码生成模板使用公共vo模块 2025-12-13 11:20:33 +08:00
insistence 1f51b628af perf: 优化API文档的响应模型显示 2025-12-12 17:44:54 +08:00
insistence 1656670203 perf: 分页模型改为使用公共vo模块的PageModel 2025-12-12 17:44:27 +08:00
insistence b8dcbdc448 feat: 新增公共vo模块 2025-12-12 17:42:26 +08:00
insistence dc2e255cd2 fix: 确保ApschedulerJobs字段类型与apscheduler默认创建的表字段类型一致 #53 2025-12-12 09:32:18 +08:00
insistence d9245ed2c9 fix: 修复单账号登录模式下强退功能失效的问题 #52 2025-12-12 08:49:55 +08:00
insistence 8f4058977d chore: fastapi -> 0.124.2 2025-12-11 17:01:53 +08:00
insistence 044fa0f30b feat: 新增上下文清理中间件 2025-12-11 17:00:33 +08:00
insistence 6565c50e08 perf: 当前用户信息使用上下文变量 2025-12-11 17:00:12 +08:00
insistence 184982eb08 perf: controller层全部使用新依赖项 2025-12-11 16:59:18 +08:00
insistence 9a1c53d9f4 feat: 新增UserInterfaceAuthDependency和RoleInterfaceAuthDependency依赖 2025-12-11 16:57:58 +08:00
insistence 44678985e9 feat: 新增DBSessionDependency依赖 2025-12-11 16:57:35 +08:00
insistence 077c8870b4 feat: 新增DataScopeDependency依赖 2025-12-11 16:57:10 +08:00
insistence d1fcb6dc11 feat: 新增依赖项工具类 2025-12-11 16:56:48 +08:00
insistence 9114d65308 feat: 新增PreAuthDependency和CurrentUserDependency依赖 2025-12-11 16:55:46 +08:00
insistence f5ec612850 feat: 新增请求上下文管理类 2025-12-11 16:54:09 +08:00
insistence a3355e2545 refactor&feat: 优化项目结构,新增common模块,原annotation、aspect、constant、enums模块移动至common模块下 2025-12-05 15:02:04 +08:00
insistence 1516c1a941 refactor&perf: 增强ruff规则,完善类型提示 2025-12-05 10:00:25 +08:00
insistence a389ef66bc docs: 更新README文档 2025-10-14 11:05:47 +08:00
insistence dcdeaa0394 chore: 升级版本至1.7.1 2025-10-14 11:05:39 +08:00
insistence f4d7d7a416 chore&fix: 移除passlib直接使用bcrypt修复密码校验异常的问题 报错 #48 #49 2025-10-14 11:05:20 +08:00
insistence f5a91c0ece perf: 代码生成do模板补充表描述 2025-09-05 09:21:40 +08:00
insistence 5ba6587b6c fix: 修复代码生成controller模板编辑接口异常生成字段的问题 2025-09-05 09:21:29 +08:00
175 changed files with 6666 additions and 2807 deletions
+1
View File
@@ -0,0 +1 @@
github: [insistence]
+31
View File
@@ -0,0 +1,31 @@
name: Tests
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
lint-format:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.9"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install ruff
- name: Run linting
run: |
ruff check ruoyi-fastapi-backend
- name: Run format check
run: |
ruff format ruoyi-fastapi-backend --check
+494
View File
@@ -0,0 +1,494 @@
# 更新日志
## RuoYi-Vue3-FastAPI v1.8.0
### 项目依赖
## 后端
1.后端依赖升级到最新版本,请升级依赖或重新创建环境。
### 新增功能
1.新增请求上下文管理类。
2.新增`PreAuthDependency``CurrentUserDependency``DataScopeDependency``DBSessionDependency``UserInterfaceAuthDependency``RoleInterfaceAuthDependency`依赖函数。
3.新增上下文清理中间件。
4.新增公共vo模块。
5.新增配置文档静态资源方法。
6.新增自动注册路由功能。
7.新增docker compose部署方式。
8.菜单导航设置支持纯顶部。
### BUG修复
1.修复单账号登录模式下强退功能失效的问题 #52
2.确保ApschedulerJobs字段类型与apscheduler默认创建的表字段类型一致 #53
3.修复磁盘存在异常时服务监控无法正常运行的问题。
4.移除代码生成表业务表外键,修复无法删除的问题。
5.修复固定头部时出现的导航栏偏移问题。
6.修复表单构建移除所有控件后切换路由回来空白问题。
7.修复代码生成v3模板时间控件between选择后清空报错问题。
### 代码重构
1.增强ruff规则,完善类型提示。
2.优化项目结构,新增common模块,原annotation、aspect、constant、enums模块移动至common模块下。
3.重构app与server设计。
### 代码优化
1.controller层全部使用新依赖项。
2.当前用户信息使用上下文变量。
3.分页模型改为使用公共vo模块的PageModel。
4.优化API文档的响应模型显示。
5.操作响应模型改为使用公共vo模块的CrudResponseModel。
6.优化API文档的接口描述信息。
7.登录/注册页面底部版权信息修改为读取配置。
8.优化生成代码下载的zip文件名。
9.优化表单构建关闭页签销毁复制插件。
10.行内表单默认设置固定宽度。
11.优化操作日志详细请求参数显示。
12.优化index页面标题读取配置。
13.优化字典组件数字类型值处理逻辑。
14.优化字典组件值宽松匹配。
15.默认固定头部。
## RuoYi-Vue3-FastAPI v1.7.1
### 项目依赖
1.后端依赖移除passlib,直接使用bcrypt。
### BUG修复
1.修复代码生成controller模板编辑接口异常生成字段的问题。
2.移除passlib直接使用bcrypt修复密码校验异常的问题 #48 #49
### 代码优化
1.代码生成do模板补充表描述。
## RuoYi-Vue3-FastAPI v1.7.0
### 项目依赖
1.前后端依赖升级,请升级依赖或重新创建环境。
### 新增功能
1.新增alembic支持。
2.文件&图片上传组件支持自定义地址&参数。
3.新增默认打包配置项。
4.显隐列组件支持全选/全不选。
5.添加页签openPage支持传递参数。
6.外链加载时遮罩信息提示。
7.上传组件新增拖动排序属性。
8.图片上传组件新增disabled属性。
9.代码生成列支持拖动排序。
10.新增用户默认初始化密码。
11.新增页签图标显示开关功能。
12.新增底部版权信息及开关。
13.用户归属部门新增清除。
14.用户导入新增验证提示。
15.菜单搜索支持键盘选择&悬浮主题背景。
16.新增apscheduler_jobs表对应sqlalchemy模型类。
17.初始密码支持自定义修改策略。
18.账号密码支持自定义更新周期。
19.注册账号设置默认密码最后更新时间。
20.显示列信息支持对象格式。
### BUG修复
1.修复logout接口未按照app_same_time_login配置项动态判断的问题 #IBZZ1S
2.修复上传组件被多次引用拖动仅对第一个有效的问题。
### 代码优化
1.优化接口耗时计算。
2.优化启动信息显示。
3.优化前端处理路由函数代码。
4.登录页和注册页表头使用VITE_APP_BASE_API配置值。
5.优化角色禁用不允许分配。
6.优化富文本控制台警告异常。
7.优化checkbox废弃API。
8.优化导航栏显示昵称&设置。
### 代码重构
1.重构IP归属区域查询为异步调用。
2.调整do与sql使其相互适配以支持alembic。
3.富文本复制粘贴图片上传至url。
## RuoYi-Vue3-FastAPI v1.6.2
### 新增功能
1.文件上传组件新增disabled属性。
2.文件上传组件新增类型。
### BUG修复
1.修复日志管理时间查询报错 #27
2.修复定时任务状态暂停时执行单次任务会触发cron表达式的问题 #31
3.修复修改字典类型时获取dict_code异常的问题。
4.修复修改字典类型时字典数据更新时间异常的问题。
5.修复代码生成模板时间查询问题 #28
6.修复用户导出缺失部门名称的问题。
### 代码优化
1.优化代码生成新增和编辑字段显示和渲染。
2.pagination更换成flex布局。
3.优化代码生成vue模板 #23
## RuoYi-Vue3-FastAPI v1.6.1
### 项目依赖
#### 后端
1.新增sqlglot依赖
```bash
pip install sqlglot[rs]==26.6.0 -i https://pypi.tuna.tsinghua.edu.cn/simple
```
### BUG修复
1.引入sqlglot修复sql语句解析异常的问题。
2.修复代码生成字段唯一性校验dao层模板判断异常的问题。
3.引入泛型修复as_query和as_form装饰模型文档丢失的问题。
4.修复代码生成主子表vo模板可能缺失NotBlank的问题。
## RuoYi-Vue3-FastAPI v1.6.0
### 项目依赖
1.后端依赖升级到最新版本,请升级依赖或重新创建环境。
### 新增功能
1.新增代码生成功能,支持配置数据库表信息一键生成和下载前后端代码,需要重新执行sql文件,请先备份数据。
2.新增表单构建功能。
3.用户头像新增支持http(s)链接。
4.新增trace中间件强化日志链路追踪和响应头 [@y1ren](https://gitee.com/y1ren)。
5.用户管理支持分栏拖动。
6.菜单面包屑导航支持多层级显示。
7.白名单支持对通配符路径匹配。
8.支持开启暗黑模式。
### BUG修复
1.修复默认关闭Tags-Views时,内链页面打不开。
2.修复删除当前登录用户拦截失效的问题。
3.修复定时任务目标字符串规则校验不全的问题。
4.修复执行单次任务时会覆盖已启用任务的问题 #IBEKD2
5.修复个人中心特殊字符密码修改失败问题。
### 代码优化
1.优化导出方法。
2.参数键值更换为多行文本。
3.优化日志中操作方法显示。
4.优化日志装饰器获取核心参数的方式。
5.用户管理过滤掉已禁用部门。
6.优化TopNav内链菜单点击没有高亮。
7.ResponseUtil补充完整参数。
## RuoYi-Vue3-FastAPI v1.5.1
### 新增功能
1.定时任务新增支持调用异步函数。
### 代码优化
1.优化字典数组条件判断。
2.校检文件名是否包含特殊字符。
3.移除已弃用的log_decorator装饰器。
## RuoYi-Vue3-FastAPI v1.5.0
### 新增功能
1.新增对PostgreSQL数据库的支持。
### BUG修复
1.修复DictTag组件控制台抛异常的问题 #IAYSVZ
2.修复登录日志导出文件名称错误的问题。
### 代码回滚
1.因fastapi查询参数模型底层存在bug,回滚查询参数模型声明方式为as_query。
### 代码优化
1.优化CamelCaseUtil和SnakeCaseUtil以兼容更多转换场景。
2.优化列表查询排序。
3.优化参数设置页面。
4.优化上传图片带域名时不增加前缀。
## RuoYi-Vue3-FastAPI v1.4.0
### 项目依赖
#### 后端
1.更新fastapi版本为0.115.0
```bash
pip install fastapi[all]==0.115.0 -i https://pypi.tuna.tsinghua.edu.cn/simple
```
### 代码重构
1.基于fastapi 0.115.0版本新特性,直接使用pydantic模型接收查询参数和表单数据,移除原有as_query和as_form使用方式。
### BUG修复
1.修复角色管理service书写错误。
### 代码优化
1.优化前端登录请求方法。
## RuoYi-Vue3-FastAPI v1.3.3
### 项目依赖
#### 后端
1.更新pydantic-validation-decorator版本为0.1.4,修复了一些底层bug。
### BUG修复
1.修复在线用户模块条件查询无效的问题。
### 代码优化
1.优化在线用户模块前后端字段描述一致。
2.日志装饰器异常处理增加logger打印日志。
## RuoYi-Vue3-FastAPI v1.3.2
### 新增功能
1.新增gzip压缩中间件。
### BUG修复
1.修复分页函数计算has_next错误的问题 #10
2.修复定时任务监听函数中事件没有job_id报错的问题。
### 代码优化
1.优化添加中间件函数注释。
## RuoYi-Vue3-FastAPI v1.3.1
### BUG修复
1.修复1.3.0版本采用新的异常处理机制后日志装饰器无法记录异常日志的问题。
### 代码优化
1.补充定时任务违规字符串。
## RuoYi-Vue3-FastAPI v1.3.0
### 项目依赖
1.前后端依赖均升级到最新版本,请升级依赖或重新创建环境。
2.使用`PyJWT`替换`python-jose`以解决一些安全性问题。
### 新增功能
1.新增字段校验装饰器,支持手动触发校验,已封装为`pydantic-validation-decorator`库。
2.各模块`service`层新增字段唯一性校验。
3.全局新增`ServiceException`自定义服务异常和`ServiceWarning`自定义服务警告,无需在接口中写大量的异常捕获。
4.菜单管理新增路由名称,请执行以下sql为数据库新增字段:
```sql
ALTER TABLE sys_menu ADD COLUMN route_name varchar(50) DEFAULT '';
```
5.新增`constant`常量配置及`enums`枚举类型配置。
6.新增`StringUtil``CronUtil`工具类。
### BUG修复
1.修复用户管理、角色管理、部门管理越权漏洞。
2.修复各模块`dao``status``del_flag`类型与数据库不一致的问题。
3.修复移动端左侧菜单无法显示的问题。
4.修复其他已知BUG。
### 代码重构
1.重构日志装饰器为`Log`,未来版本将删除`log_decorator`装饰器,请尽快迁移。
2.重构`RedisInitKeyConfig`为枚举类型,现在可通过以下方式获取对应的`key``remark`
`RedisInitKeyConfig.ACCESS_TOKEN.key``RedisInitKeyConfig.ACCESS_TOKEN.remark`
3.重构数据权限逻辑,底层进行优化,使用方法与之前相同。
### 代码优化
1.引入`ruff`对后端代码进行格式化及检测修复,优化导入。
2.各模块基于`ServiceException`自定义服务异常和`ServiceWarning`自定义服务警告优化了异常处理逻辑。
3.各模块`vo`层使用`Field`声明字段。
4.优化API文档字段描述显示。
## RuoYi-Vue3-FastAPI v1.2.2
### BUG修复
1.修复删除定时任务时未移除调度中任务的问题。
2.修复菜单生成路由时组件条件判断错误的问题。
## RuoYi-Vue3-FastAPI v1.2.1
### BUG修复
1.修复各模块新增数据时创建时间记录异常的问题。
2.修复菜单挂载到根目录时路由加载异常等一系列相关问题。
### 代码及性能优化
1.修改代理localhost为127.0.0.1以适配部分设备解析localhost异常的问题。
## RuoYi-Vue3-FastAPI v1.2.0
### 重要说明
本次更新为 **_破坏性更新_** ,重构数据库orm为异步,代码改动很大,请谨慎升级。
1.原有的Session类型声明统一变更为AsyncSession。
2.service层和dao层的函数修改为异步函数,请使用await调用。
3.orm查询不再支持query,请使用select、update、delete等语句,具体使用方法请参考[https://docs.sqlalchemy.org/en/20/orm/queryguide/index.html](https://docs.sqlalchemy.org/en/20/orm/queryguide/index.html)。
### 项目依赖
#### 后端
1.增加asyncmy依赖用于支持orm异步操作mysql,请重新安装依赖
```bash
pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple。
```
### 新增功能
1.新增SnakeCaseUtil工具类,将原CamelCaseUtil工具类的camel_to_snake函数迁移至SnakeCaseUtil工具类。
### BUG修复
1.修复用户管理模块重置用户密码时会异常重置用户岗位和角色的问题。
2.修复清空定时任务日志异常的问题。
## RuoYi-Vue3-FastAPI v1.1.3
### 新增功能
1.用户密码新增非法字符验证。
### BUG修复
1.修复通知公告列表查询前后端字段不一致的问题。
2.修复个人中心修改基本资料后端异常的问题。
## RuoYi-Vue3-FastAPI v1.1.2
### 新增功能
1.配置文件新增数据库连接池相关配置。
### BUG修复
1.修复个人中心修改密码后端异常的问题。
### 代码及性能优化
1.使用@lru_cache缓存ip归属区域查询结果,避免重复调用ip归属区域查询接口以优化性能。
## RuoYi-Vue3-FastAPI v1.1.1
### BUG修复
1.修复编辑定时任务时更新的信息未同步至scheduler的问题 #I9EK56
2.修复编辑角色数据权限时后端异常的问题 #I9ENQN
3.修复菜单配置路由参数不生效的问题。
4.修复获取路由信息时菜单排序不生效的问题。
5.修复添加菜单时是否外链和是否缓存回显异常的问题。
## RuoYi-Vue3-FastAPI v1.1.0
### 新增功能
1.后端配置文件新增sqlalchemy日志开关配置。
2.后端配置文件新增IP归属区域查询开关配置。
3.后端配置文件新增账号同时登录开关配置。
### BUG修复
1.修复token本身过期时退出登录接口异常的问题 #I9CBWT
2.修复系统版本号或浏览器版本号无法获取时登录异常的问题 #I9CYNM
## RuoYi-Vue3-FastAPI v1.0.3
### 新增功能
1.账号密码登录新增IP黑名单校验。
### BUG修复
1.修复外链菜单无法打开的问题 #I95KBK
2.修复添加和编辑菜单页面中是否缓存和是否外链字段回显异常的问题 #I95KBK
## RuoYi-Vue3-FastAPI v1.0.2
### 新增功能
1.用户接口权限校验增加列表接收参数,实现同一接口支持多个权限标识校验。
2.新增按角色校验接口权限依赖
### BUG修复
1.修复用户管理和部门管理模块数据权限异常的问题。
### 代码及性能优化
1.调整参数设置、部门管理、字典管理、定时任务、日志管理、角色管理、菜单管理模块部分接口权限标识。
## RuoYi-Vue3-FastAPI v1.0.1
### 项目依赖
#### 后端
1.更新fastapi版本为0.109.1,修复一些安全性问题,命令:
```bash
pip install fastapi[all]==0.109.1 -i https://mirrors.aliyun.com/pypi/simple/
```
### 新增功能
1.日志管理模块新增字段排序查询。
## RuoYi-Vue3-FastAPI v1.0.0
RuoYi-Vue3-FastAPI第一个版本发布啦!
此版本功能如下:
1.用户管理:用户是系统操作者,该功能主要完成系统用户配置。
2.角色管理:角色菜单权限分配。
3.菜单管理:配置系统菜单,操作权限,按钮权限标识等。
4.部门管理:配置系统组织机构(公司、部门、小组)。
5.岗位管理:配置系统用户所属担任职务。
6.字典管理:对系统中经常使用的一些较为固定的数据进行维护。
7.参数管理:对系统动态配置常用参数。
8.通知公告:系统通知公告信息发布维护。
9.操作日志:系统正常操作日志记录和查询;系统异常信息日志记录和查询。
10.登录日志:系统登录日志记录查询包含登录异常。
11.在线用户:当前系统中活跃用户状态监控。
12.定时任务:在线(添加、修改、删除)任务调度包含执行结果日志。
13.服务监控:监视当前系统CPU、内存、磁盘、堆栈等相关信息。
14.缓存监控:对系统的缓存信息查询,命令统计等。
15.系统接口:根据业务代码自动生成相关的api接口文档。
+2 -2
View File
@@ -1,12 +1,12 @@
<p align="center"> <p align="center">
<img alt="logo" src="https://oscimg.oschina.net/oscnet/up-d3d0a9303e11d522a06cd263f3079027715.png"> <img alt="logo" src="https://oscimg.oschina.net/oscnet/up-d3d0a9303e11d522a06cd263f3079027715.png">
</p> </p>
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">RuoYi-Vue3-FastAPI v1.7.0</h1> <h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">RuoYi-Vue3-FastAPI v1.8.0</h1>
<h4 align="center">基于RuoYi-Vue3+FastAPI前后端分离的快速开发框架</h4> <h4 align="center">基于RuoYi-Vue3+FastAPI前后端分离的快速开发框架</h4>
<p align="center"> <p align="center">
<a href="https://gitee.com/insistence2022/RuoYi-Vue3-FastAPI/stargazers"><img src="https://gitee.com/insistence2022/RuoYi-Vue3-FastAPI/badge/star.svg?theme=dark"></a> <a href="https://gitee.com/insistence2022/RuoYi-Vue3-FastAPI/stargazers"><img src="https://gitee.com/insistence2022/RuoYi-Vue3-FastAPI/badge/star.svg?theme=dark"></a>
<a href="https://github.com/insistence/RuoYi-Vue3-FastAPI"><img src="https://img.shields.io/github/stars/insistence/RuoYi-Vue3-FastAPI?style=social"></a> <a href="https://github.com/insistence/RuoYi-Vue3-FastAPI"><img src="https://img.shields.io/github/stars/insistence/RuoYi-Vue3-FastAPI?style=social"></a>
<a href="https://gitee.com/insistence2022/RuoYi-Vue3-FastAPI"><img src="https://img.shields.io/badge/RuoYiVue3FastAPI-v1.7.0-brightgreen.svg"></a> <a href="https://gitee.com/insistence2022/RuoYi-Vue3-FastAPI"><img src="https://img.shields.io/badge/RuoYiVue3FastAPI-v1.8.0-brightgreen.svg"></a>
<a href="https://gitee.com/insistence2022/RuoYi-Vue3-FastAPI/blob/master/LICENSE"><img src="https://img.shields.io/github/license/mashape/apistatus.svg"></a> <a href="https://gitee.com/insistence2022/RuoYi-Vue3-FastAPI/blob/master/LICENSE"><img src="https://img.shields.io/github/license/mashape/apistatus.svg"></a>
<img src="https://img.shields.io/badge/node-≥18-blue"> <img src="https://img.shields.io/badge/node-≥18-blue">
<img src="https://img.shields.io/badge/python-≥3.9-blue"> <img src="https://img.shields.io/badge/python-≥3.9-blue">
+73
View File
@@ -0,0 +1,73 @@
services:
# 前端服务
ruoyi-frontend:
build:
context: ./ruoyi-fastapi-frontend
dockerfile: Dockerfile
image: ruoyi-frontend:latest
container_name: ruoyi-frontend
ports:
- "12580:80"
volumes:
- ./ruoyi-fastapi-frontend/bin/nginx.dockermy.conf:/etc/nginx/conf.d/default.conf
depends_on:
- ruoyi-backend-my
networks:
- ruoyi-network
# 后端服务(MySQL版本)
ruoyi-backend-my:
build:
context: ./ruoyi-fastapi-backend
dockerfile: Dockerfile.my
image: ruoyi-backend-my:latest
container_name: ruoyi-backend-my
ports:
- "19099:9099"
depends_on:
ruoyi-mysql:
condition: service_healthy
ruoyi-redis:
condition: service_healthy
networks:
- ruoyi-network
# MySQL服务
ruoyi-mysql:
image: mysql:8.0
container_name: ruoyi-mysql
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: ruoyi-fastapi
ports:
- "13306:3306"
volumes:
- ./ruoyi-fastapi-backend/sql/ruoyi-fastapi.sql:/docker-entrypoint-initdb.d/ruoyi-fastapi.sql
command: --character-set-server=utf8mb4 --collation-server=utf8mb4_general_ci --skip-character-set-client-handshake=1
networks:
- ruoyi-network
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-proot"]
interval: 5s
timeout: 10s
retries: 30
# Redis服务
ruoyi-redis:
image: redis:latest
container_name: ruoyi-redis
ports:
- "16379:6379"
networks:
- ruoyi-network
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 10s
retries: 30
# 网络配置
networks:
ruoyi-network:
name: ruoyi-network
driver: bridge
+66
View File
@@ -0,0 +1,66 @@
services:
# 前端服务
ruoyi-frontend:
build:
context: ./ruoyi-fastapi-frontend
dockerfile: Dockerfile
image: ruoyi-frontend:latest
container_name: ruoyi-frontend
ports:
- "12580:80"
volumes:
- ./ruoyi-fastapi-frontend/bin/nginx.dockerpg.conf:/etc/nginx/conf.d/default.conf
depends_on:
- ruoyi-backend-pg
networks:
- ruoyi-network
# 后端服务(PostgreSQL版本)
ruoyi-backend-pg:
build:
context: ./ruoyi-fastapi-backend
dockerfile: Dockerfile.pg
image: ruoyi-backend-pg:latest
container_name: ruoyi-backend-pg
ports:
- "19099:9099"
depends_on:
- ruoyi-pg
- ruoyi-redis
networks:
- ruoyi-network
# PostgreSQL服务
ruoyi-pg:
image: postgres:14
container_name: ruoyi-pg
environment:
POSTGRES_PASSWORD: root
POSTGRES_DB: ruoyi-fastapi
POSTGRES_INITDB_ARGS: --encoding=UTF8 --lc-collate=C --lc-ctype=C
ports:
- "15432:5432"
volumes:
- ./ruoyi-fastapi-backend/sql/ruoyi-fastapi-pg.sql:/docker-entrypoint-initdb.d/ruoyi-fastapi-pg.sql
networks:
- ruoyi-network
healthcheck:
test: ["CMD", "pg_isready", "-U", "postgres"]
interval: 5s
timeout: 10s
retries: 30
# Redis服务
ruoyi-redis:
image: redis:latest
container_name: ruoyi-redis
ports:
- "16379:6379"
networks:
- ruoyi-network
# 网络配置
networks:
ruoyi-network:
name: ruoyi-network
driver: bridge
+1 -1
View File
@@ -10,7 +10,7 @@ APP_HOST = '0.0.0.0'
# 应用端口 # 应用端口
APP_PORT = 9099 APP_PORT = 9099
# 应用版本 # 应用版本
APP_VERSION= '1.7.0' APP_VERSION= '1.8.0'
# 应用是否开启热重载 # 应用是否开启热重载
APP_RELOAD = true APP_RELOAD = true
# 应用是否开启IP归属区域查询 # 应用是否开启IP归属区域查询
+66
View File
@@ -0,0 +1,66 @@
# -------- 应用配置 --------
# 应用运行环境
APP_ENV = 'prod'
# 应用名称
APP_NAME = 'RuoYi-FastAPI'
# 应用代理路径
APP_ROOT_PATH = '/docker-api'
# 应用主机
APP_HOST = '0.0.0.0'
# 应用端口
APP_PORT = 9099
# 应用版本
APP_VERSION= '1.8.0'
# 应用是否开启热重载
APP_RELOAD = false
# 应用是否开启IP归属区域查询
APP_IP_LOCATION_QUERY = true
# 应用是否允许账号同时登录
APP_SAME_TIME_LOGIN = true
# -------- Jwt配置 --------
# Jwt秘钥
JWT_SECRET_KEY = 'b01c66dc2c58dc6a0aabfe2144256be36226de378bf87f72c0c795dda67f4d55'
# Jwt算法
JWT_ALGORITHM = 'HS256'
# 令牌过期时间
JWT_EXPIRE_MINUTES = 1440
# redis中令牌过期时间
JWT_REDIS_EXPIRE_MINUTES = 30
# -------- 数据库配置 --------
# 数据库类型,可选的有'mysql'、'postgresql',默认为'mysql'
DB_TYPE = 'mysql'
# 数据库主机
DB_HOST = 'ruoyi-mysql'
# 数据库端口
DB_PORT = 3306
# 数据库用户名
DB_USERNAME = 'root'
# 数据库密码
DB_PASSWORD = 'root'
# 数据库名称
DB_DATABASE = 'ruoyi-fastapi'
# 是否开启sqlalchemy日志
DB_ECHO = true
# 允许溢出连接池大小的最大连接数
DB_MAX_OVERFLOW = 10
# 连接池大小,0表示连接数无限制
DB_POOL_SIZE = 50
# 连接回收时间(单位:秒)
DB_POOL_RECYCLE = 3600
# 连接池中没有线程可用时,最多等待的时间(单位:秒)
DB_POOL_TIMEOUT = 30
# -------- Redis配置 --------
# Redis主机
REDIS_HOST = 'ruoyi-redis'
# Redis端口
REDIS_PORT = 6379
# Redis用户名
REDIS_USERNAME = ''
# Redis密码
REDIS_PASSWORD = ''
# Redis数据库
REDIS_DATABASE = 2
+66
View File
@@ -0,0 +1,66 @@
# -------- 应用配置 --------
# 应用运行环境
APP_ENV = 'prod'
# 应用名称
APP_NAME = 'RuoYi-FastAPI'
# 应用代理路径
APP_ROOT_PATH = '/docker-api'
# 应用主机
APP_HOST = '0.0.0.0'
# 应用端口
APP_PORT = 9099
# 应用版本
APP_VERSION= '1.8.0'
# 应用是否开启热重载
APP_RELOAD = false
# 应用是否开启IP归属区域查询
APP_IP_LOCATION_QUERY = true
# 应用是否允许账号同时登录
APP_SAME_TIME_LOGIN = true
# -------- Jwt配置 --------
# Jwt秘钥
JWT_SECRET_KEY = 'b01c66dc2c58dc6a0aabfe2144256be36226de378bf87f72c0c795dda67f4d55'
# Jwt算法
JWT_ALGORITHM = 'HS256'
# 令牌过期时间
JWT_EXPIRE_MINUTES = 1440
# redis中令牌过期时间
JWT_REDIS_EXPIRE_MINUTES = 30
# -------- 数据库配置 --------
# 数据库类型,可选的有'mysql'、'postgresql',默认为'mysql'
DB_TYPE = 'postgresql'
# 数据库主机
DB_HOST = 'ruoyi-pg'
# 数据库端口
DB_PORT = 5432
# 数据库用户名
DB_USERNAME = 'postgres'
# 数据库密码
DB_PASSWORD = 'root'
# 数据库名称
DB_DATABASE = 'ruoyi-fastapi'
# 是否开启sqlalchemy日志
DB_ECHO = true
# 允许溢出连接池大小的最大连接数
DB_MAX_OVERFLOW = 10
# 连接池大小,0表示连接数无限制
DB_POOL_SIZE = 50
# 连接回收时间(单位:秒)
DB_POOL_RECYCLE = 3600
# 连接池中没有线程可用时,最多等待的时间(单位:秒)
DB_POOL_TIMEOUT = 30
# -------- Redis配置 --------
# Redis主机
REDIS_HOST = 'ruoyi-redis'
# Redis端口
REDIS_PORT = 6379
# Redis用户名
REDIS_USERNAME = ''
# Redis密码
REDIS_PASSWORD = ''
# Redis数据库
REDIS_DATABASE = 2
+1 -1
View File
@@ -10,7 +10,7 @@ APP_HOST = '0.0.0.0'
# 应用端口 # 应用端口
APP_PORT = 9099 APP_PORT = 9099
# 应用版本 # 应用版本
APP_VERSION= '1.7.0' APP_VERSION= '1.8.0'
# 应用是否开启热重载 # 应用是否开启热重载
APP_RELOAD = false APP_RELOAD = false
# 应用是否开启IP归属区域查询 # 应用是否开启IP归属区域查询
+14
View File
@@ -0,0 +1,14 @@
FROM python:3.10
WORKDIR /app
# 复制源代码
COPY . .
# 安装依赖
RUN pip install --no-cache-dir -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
# 暴露端口
EXPOSE 9099
# 启动命令
CMD ["python", "app.py", "--env=dockermy"]
+14
View File
@@ -0,0 +1,14 @@
FROM python:3.10
WORKDIR /app
# 复制源代码
COPY . .
# 安装依赖
RUN pip install --no-cache-dir -r requirements-pg.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
# 暴露端口
EXPOSE 9099
# 启动命令
CMD ["python", "app.py", "--env=dockerpg"]
+13 -4
View File
@@ -1,13 +1,18 @@
import asyncio import asyncio
import os import os
from alembic import context from collections.abc import Iterable
from logging.config import fileConfig from logging.config import fileConfig
from typing import Optional, Union
from alembic import context
from alembic.migration import MigrationContext
from alembic.operations.ops import MigrationScript
from sqlalchemy import pool from sqlalchemy import pool
from sqlalchemy.engine import Connection from sqlalchemy.engine import Connection
from sqlalchemy.ext.asyncio import async_engine_from_config from sqlalchemy.ext.asyncio import async_engine_from_config
from config.database import Base, ASYNC_SQLALCHEMY_DATABASE_URL
from utils.import_util import ImportUtil
from config.database import ASYNC_SQLALCHEMY_DATABASE_URL, Base
from utils.import_util import ImportUtil
# 判断vesrions目录是否存在,如果不存在则创建 # 判断vesrions目录是否存在,如果不存在则创建
alembic_veresions_path = 'alembic/versions' alembic_veresions_path = 'alembic/versions'
@@ -60,7 +65,11 @@ def run_migrations_offline() -> None:
def do_run_migrations(connection: Connection) -> None: def do_run_migrations(connection: Connection) -> None:
def process_revision_directives(context, revision, directives): def process_revision_directives(
context: MigrationContext,
revision: Union[str, Iterable[Optional[str]], Iterable[str]],
directives: list[MigrationScript],
) -> None:
script = directives[0] script = directives[0]
# 检查所有操作集是否为空 # 检查所有操作集是否为空
+4 -1
View File
@@ -1,6 +1,9 @@
import uvicorn import uvicorn
from server import app, AppConfig # noqa: F401
from config.env import AppConfig
from server import create_app
app = create_app()
if __name__ == '__main__': if __name__ == '__main__':
uvicorn.run( uvicorn.run(
@@ -0,0 +1,415 @@
import inspect
import json
import time
from collections.abc import Awaitable
from datetime import datetime
from functools import wraps
from typing import Any, Callable, Literal, Optional, TypeVar
import httpx
from async_lru import alru_cache
from fastapi import Request
from fastapi.responses import JSONResponse, ORJSONResponse, UJSONResponse
from sqlalchemy.ext.asyncio import AsyncSession
from starlette.status import HTTP_200_OK
from typing_extensions import ParamSpec
from user_agents import parse
from common.context import RequestContext
from common.enums import BusinessType
from config.env import AppConfig
from exceptions.exception import LoginException, ServiceException, ServiceWarning
from module_admin.entity.vo.log_vo import LogininforModel, OperLogModel
from module_admin.service.log_service import LoginLogService, OperationLogService
from utils.dependency_util import DependencyUtil
from utils.log_util import logger
from utils.response_util import ResponseUtil
P = ParamSpec('P')
R = TypeVar('R')
class Log:
"""
日志装饰器
"""
def __init__(
self,
title: str,
business_type: BusinessType,
log_type: Optional[Literal['login', 'operation']] = 'operation',
) -> None:
"""
日志装饰器
:param title: 当前日志装饰器装饰的模块标题
:param business_type: 业务类型(OTHER其它 INSERT新增 UPDATE修改 DELETE删除 GRANT授权 EXPORT导出 IMPORT导入 FORCE强退 GENCODE生成代码 CLEAN清空数据)
:param log_type: 日志类型(login表示登录日志,operation表示为操作日志)
:return:
"""
self.title = title
self.business_type = business_type.value
self.log_type = log_type
self._oper_param_len = 2000
def __call__(self, func: Callable[P, Awaitable[R]]) -> Callable[P, Awaitable[R]]:
@wraps(func)
async def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
start_time = time.perf_counter()
# 获取当前被装饰函数所在路径
func_path = self._get_decorator_func_path(func)
# 获取上下文信息
request_name_list = get_function_parameters_name_by_type(func, Request)
request = get_function_parameters_value_by_name(func, request_name_list[0], *args, **kwargs)
DependencyUtil.check_exclude_routes(request, err_msg='当前路由不在认证规则内,不可使用Log装饰器')
session_name_list = get_function_parameters_name_by_type(func, AsyncSession)
query_db = get_function_parameters_value_by_name(func, session_name_list[0], *args, **kwargs)
request_method = request.method
user_agent = request.headers.get('User-Agent')
# 获取操作类型
operator_type = self._get_oper_type(user_agent)
# 获取请求的url
oper_url = request.url.path
# 获取请求ip
oper_ip = request.headers.get('X-Forwarded-For')
# 获取请求ip归属区域
oper_location = await self._get_oper_location(oper_ip)
# 获取请求参数
oper_param = await self._get_request_params(request)
# 日志表请求参数字段长度最大为2000,因此在此处判断长度
if len(oper_param) > self._oper_param_len:
oper_param = '请求参数过长'
# 获取操作时间
oper_time = datetime.now()
# 此处在登录之前向原始函数传递一些登录信息,用于监测在线用户的相关信息
login_log = self._get_login_log(user_agent, oper_ip, oper_location, oper_time, kwargs)
try:
# 调用原始函数
result = await func(*args, **kwargs)
except (LoginException, ServiceWarning) as e:
logger.warning(e.message)
result = ResponseUtil.failure(data=e.data, msg=e.message)
except ServiceException as e:
logger.error(e.message)
result = ResponseUtil.error(data=e.data, msg=e.message)
except Exception as e:
logger.exception(e)
result = ResponseUtil.error(msg=str(e))
# 获取请求耗时
cost_time = float(time.perf_counter() - start_time) * 1000
# 判断请求是否来自api文档
request_from_swagger, request_from_redoc = self._is_request_from_swagger_or_redoc(request)
# 根据响应结果的类型使用不同的方法获取响应结果参数
result_dict = self._get_result_dict(result, request_from_swagger, request_from_redoc)
json_result = json.dumps(result_dict, ensure_ascii=False)
# 根据响应结果获取响应状态及异常信息
status, error_msg = self._get_status_and_error_msg(result_dict)
# 根据日志类型向对应的日志表插入数据
if self.log_type == 'login':
# 登录请求来自于api文档时不记录登录日志,其余情况则记录
if request_from_swagger or request_from_redoc:
pass
else:
user = kwargs.get('form_data')
login_log.update(
{
'loginTime': oper_time,
'userName': user.username,
'status': str(status),
'msg': result_dict.get('msg'),
}
)
await LoginLogService.add_login_log_services(query_db, LogininforModel(**login_log))
else:
current_user = RequestContext.get_current_user()
oper_name = current_user.user.user_name
dept_name = current_user.user.dept.dept_name if current_user.user.dept else None
operation_log = OperLogModel(
title=self.title,
businessType=self.business_type,
method=func_path,
requestMethod=request_method,
operatorType=operator_type,
operName=oper_name,
deptName=dept_name,
operUrl=oper_url,
operIp=oper_ip,
operLocation=oper_location,
operParam=oper_param,
jsonResult=json_result,
status=status,
errorMsg=error_msg,
operTime=oper_time,
costTime=int(cost_time),
)
await OperationLogService.add_operation_log_services(query_db, operation_log)
return result
return wrapper
def _get_decorator_func_path(self, func: Callable) -> str:
"""
获取被装饰函数所在路径
:param func: 被装饰函数
:return: 被装饰函数所在路径
"""
# 获取被装饰函数所在的模块
module = inspect.getmodule(func)
# 获取完整模块路径
module_path = module.__name__ if module else ''
# 获取当前被装饰函数所在路径
func_path = f'{module_path}.{func.__name__}()'
return func_path
def _get_oper_type(self, user_agent: Any) -> int:
"""
获取操作类型
:param user_agent: 用户代理字符串
:return: 操作类型
"""
operator_type = 0
if 'Windows' in user_agent or 'Macintosh' in user_agent or 'Linux' in user_agent:
operator_type = 1
if 'Mobile' in user_agent or 'Android' in user_agent or 'iPhone' in user_agent:
operator_type = 2
return operator_type
async def _get_oper_location(self, oper_ip: str) -> str:
"""
获取请求IP归属区域
:param oper_ip: 请求IP
:return: 请求IP归属区域
"""
oper_location = '内网IP'
if AppConfig.app_ip_location_query:
oper_location = await get_ip_location(oper_ip)
return oper_location
async def _get_request_params(self, request: Request) -> str:
"""
获取请求参数
:param request: Request对象
:return: 格式化后的请求参数字符串
"""
params = {}
# 路径和查询参数
path_params = dict(request.path_params)
query_params = dict(request.query_params)
params.update({k: v for k, v in {'path_params': path_params, 'query_params': query_params}.items() if v})
# 请求体处理
content_type = request.headers.get('Content-Type', '')
# JSON请求
if 'application/json' in content_type:
json_body = await request.json()
if json_body:
params['json_body'] = json_body
# 表单数据
elif 'multipart/form-data' in content_type or 'application/x-www-form-urlencoded' in content_type:
form_data = await request.form()
if form_data:
# 过滤掉文件对象,只保留普通表单字段
form_dict = {key: value for key, value in form_data.items() if not hasattr(value, 'filename')}
if form_dict:
params['form_data'] = form_dict
# 仅在multipart时尝试处理文件
if 'multipart/form-data' in content_type:
file_info = {}
for key, value in form_data.items():
if hasattr(value, 'filename'):
file_info[key] = {
'filename': value.filename,
'content_type': value.content_type,
'size': value.size,
'headers': dict(value.headers),
}
if file_info:
params['files'] = file_info
# 其他文本请求
elif 'application/octet-stream' not in content_type:
body = await request.body()
if body:
params['raw_body'] = body.decode('utf-8')
return json.dumps(params, ensure_ascii=False, indent=2) if params else ''
def _get_login_log(
self, user_agent: Any, oper_ip: str, oper_location: str, oper_time: datetime, origin_kwargs: dict
) -> dict:
"""
获取登录日志信息
:param user_agent: 用户代理字符串
:param oper_ip: 操作ip
:param oper_location: 操作区域
:param oper_time: 操作时间
:param origin_kwargs: 原始函数参数
:return: 登录日志信息
"""
login_log = {}
if self.log_type == 'login':
user_agent_info = parse(user_agent)
browser = f'{user_agent_info.browser.family}'
system_os = f'{user_agent_info.os.family}'
if user_agent_info.browser.version != ():
browser += f' {user_agent_info.browser.version[0]}'
if user_agent_info.os.version != ():
system_os += f' {user_agent_info.os.version[0]}'
login_log = {
'ipaddr': oper_ip,
'loginLocation': oper_location,
'browser': browser,
'os': system_os,
'loginTime': oper_time.strftime('%Y-%m-%d %H:%M:%S'),
}
self._set_login_data(login_log, origin_kwargs)
return login_log
def _set_login_data(self, login_log: dict, origin_kwargs: dict) -> None:
"""
设置登录日志数据
:param login_log: 登录日志信息
:param origin_kwargs: 原始函数参数
:return: None
"""
if 'form_data' in origin_kwargs:
origin_kwargs['form_data'].login_info = login_log
def _get_status_and_error_msg(self, result_dict: dict) -> tuple[int, str]:
"""
获取操作状态和错误信息
:param result_dict: 操作结果字典
:return: 操作状态和错误信息元组
"""
status = 1
error_msg = ''
if result_dict.get('code') == HTTP_200_OK:
status = 0
else:
error_msg = result_dict.get('msg')
return status, error_msg
def _is_request_from_swagger_or_redoc(self, request: Request) -> tuple[bool, bool]:
"""
判断请求是否来自swagger或redoc
:param request: Request对象
:return: 是否来自swagger请求和是否来自redoc请求元组
"""
request_from_swagger = (
request.headers.get('referer').endswith('docs') if request.headers.get('referer') else False
)
request_from_redoc = (
request.headers.get('referer').endswith('redoc') if request.headers.get('referer') else False
)
return request_from_swagger, request_from_redoc
def _get_result_dict(self, result: Any, request_from_swagger: bool, request_from_redoc: bool) -> dict:
"""
获取操作结果字典
:param result: 操作结果
:param request_from_swagger: 是否来自swagger请求
:param request_from_redoc: 是否来自redoc请求
:return: 操作结果字典
"""
if isinstance(result, (JSONResponse, ORJSONResponse, UJSONResponse)):
result_dict = json.loads(str(result.body, 'utf-8'))
elif request_from_swagger or request_from_redoc:
result_dict = {}
elif result.status_code == HTTP_200_OK:
result_dict = {'code': result.status_code, 'message': '获取成功'}
else:
result_dict = {'code': result.status_code, 'message': '获取失败'}
return result_dict
@alru_cache()
async def get_ip_location(oper_ip: str) -> str:
"""
查询ip归属区域
:param oper_ip: 需要查询的ip
:return: ip归属区域
"""
oper_location = '内网IP'
try:
if oper_ip not in ['127.0.0.1', 'localhost']:
oper_location = '未知'
async with httpx.AsyncClient() as client:
ip_result = await client.get(f'https://qifu-api.baidubce.com/ip/geo/v1/district?ip={oper_ip}')
if ip_result.status_code == HTTP_200_OK:
prov = ip_result.json().get('data', {}).get('prov')
city = ip_result.json().get('data', {}).get('city')
if prov or city:
oper_location = f'{prov}-{city}'
except Exception as e:
oper_location = '未知'
print(e)
return oper_location
def get_function_parameters_name_by_type(func: Callable, param_type: Any) -> list:
"""
获取函数指定类型的参数名称
:param func: 函数
:param arg_type: 参数类型
:return: 函数指定类型的参数名称
"""
# 获取函数的参数信息
parameters = inspect.signature(func).parameters
# 找到指定类型的参数名称
parameters_name_list = []
# 遍历所有参数
for name, param in parameters.items():
# 处理参数注解
annotation = param.annotation
# 检查参数类型是否匹配
# 1. 直接匹配
# 2. 检查是否为Annotated类型(通过类型名称判断)
if annotation == param_type or (
hasattr(annotation, '__class__')
and annotation.__class__.__name__ == '_AnnotatedAlias'
and annotation.__origin__ == param_type
):
parameters_name_list.append(name)
return parameters_name_list
def get_function_parameters_value_by_name(func: Callable, name: str, *args, **kwargs) -> Any:
"""
获取函数指定参数的值
:param func: 函数
:param name: 参数名
:return: 参数值
"""
# 获取参数值
bound_parameters = inspect.signature(func).bind(*args, **kwargs)
bound_parameters.apply_defaults()
parameters_value = bound_parameters.arguments.get(name)
return parameters_value
@@ -1,21 +1,23 @@
import inspect import inspect
from typing import TYPE_CHECKING, TypeVar
from fastapi import Form, Query from fastapi import Form, Query
from pydantic import BaseModel from pydantic import BaseModel
from pydantic.fields import FieldInfo
from typing import Type, TypeVar
if TYPE_CHECKING:
from pydantic.fields import FieldInfo
BaseModelVar = TypeVar('BaseModelVar', bound=BaseModel) BaseModelVar = TypeVar('BaseModelVar', bound=BaseModel)
def as_query(cls: Type[BaseModelVar]) -> Type[BaseModelVar]: def as_query(cls: type[BaseModelVar]) -> type[BaseModelVar]:
""" """
pydantic模型查询参数装饰器将pydantic模型用于接收查询参数 pydantic模型查询参数装饰器将pydantic模型用于接收查询参数
""" """
new_parameters = [] new_parameters = []
for field_name, model_field in cls.model_fields.items(): for model_field in cls.model_fields.values():
model_field: FieldInfo # type: ignore model_field: FieldInfo
if not model_field.is_required(): if not model_field.is_required():
new_parameters.append( new_parameters.append(
@@ -36,24 +38,24 @@ def as_query(cls: Type[BaseModelVar]) -> Type[BaseModelVar]:
) )
) )
async def as_query_func(**data): async def as_query_func(**data) -> type[BaseModelVar]:
return cls(**data) return cls(**data)
sig = inspect.signature(as_query_func) sig = inspect.signature(as_query_func)
sig = sig.replace(parameters=new_parameters) sig = sig.replace(parameters=new_parameters)
as_query_func.__signature__ = sig # type: ignore as_query_func.__signature__ = sig
setattr(cls, 'as_query', as_query_func) cls.as_query = as_query_func
return cls return cls
def as_form(cls: Type[BaseModelVar]) -> Type[BaseModelVar]: def as_form(cls: type[BaseModelVar]) -> type[BaseModelVar]:
""" """
pydantic模型表单参数装饰器将pydantic模型用于接收表单参数 pydantic模型表单参数装饰器将pydantic模型用于接收表单参数
""" """
new_parameters = [] new_parameters = []
for field_name, model_field in cls.model_fields.items(): for model_field in cls.model_fields.values():
model_field: FieldInfo # type: ignore model_field: FieldInfo
if not model_field.is_required(): if not model_field.is_required():
new_parameters.append( new_parameters.append(
@@ -74,11 +76,11 @@ def as_form(cls: Type[BaseModelVar]) -> Type[BaseModelVar]:
) )
) )
async def as_form_func(**data): async def as_form_func(**data) -> type[BaseModelVar]:
return cls(**data) return cls(**data)
sig = inspect.signature(as_form_func) sig = inspect.signature(as_form_func)
sig = sig.replace(parameters=new_parameters) sig = sig.replace(parameters=new_parameters)
as_form_func.__signature__ = sig # type: ignore as_form_func.__signature__ = sig
setattr(cls, 'as_form', as_form_func) cls.as_form = as_form_func
return cls return cls
@@ -1,7 +1,9 @@
from fastapi import Depends
from typing import Optional from typing import Optional
from module_admin.entity.vo.user_vo import CurrentUserModel
from module_admin.service.login_service import LoginService from fastapi import Depends, Request, params
from common.context import RequestContext
from utils.dependency_util import DependencyUtil
class GetDataScope: class GetDataScope:
@@ -21,7 +23,7 @@ class GetDataScope:
db_alias: Optional[str] = 'db', db_alias: Optional[str] = 'db',
user_alias: Optional[str] = 'user_id', user_alias: Optional[str] = 'user_id',
dept_alias: Optional[str] = 'dept_id', dept_alias: Optional[str] = 'dept_id',
): ) -> None:
""" """
获取当前用户数据权限对应的查询sql语句 获取当前用户数据权限对应的查询sql语句
@@ -35,7 +37,9 @@ class GetDataScope:
self.user_alias = user_alias self.user_alias = user_alias
self.dept_alias = dept_alias self.dept_alias = dept_alias
def __call__(self, current_user: CurrentUserModel = Depends(LoginService.get_current_user)): def __call__(self, request: Request) -> str:
DependencyUtil.check_exclude_routes(request, err_msg='当前路由不在认证规则内,不可使用GetDataScope依赖项')
current_user = RequestContext.get_current_user()
user_id = current_user.user.user_id user_id = current_user.user.user_id
dept_id = current_user.user.dept_id dept_id = current_user.user.dept_id
custom_data_scope_role_id_list = [ custom_data_scope_role_id_list = [
@@ -46,7 +50,7 @@ class GetDataScope:
if current_user.user.admin or role.data_scope == self.DATA_SCOPE_ALL: if current_user.user.admin or role.data_scope == self.DATA_SCOPE_ALL:
param_sql_list = ['1 == 1'] param_sql_list = ['1 == 1']
break break
elif role.data_scope == self.DATA_SCOPE_CUSTOM: if role.data_scope == self.DATA_SCOPE_CUSTOM:
if len(custom_data_scope_role_id_list) > 1: if len(custom_data_scope_role_id_list) > 1:
param_sql_list.append( param_sql_list.append(
f"{self.query_alias}.{self.dept_alias}.in_(select(SysRoleDept.dept_id).where(SysRoleDept.role_id.in_({custom_data_scope_role_id_list}))) if hasattr({self.query_alias}, '{self.dept_alias}') else 1 == 0" f"{self.query_alias}.{self.dept_alias}.in_(select(SysRoleDept.dept_id).where(SysRoleDept.role_id.in_({custom_data_scope_role_id_list}))) if hasattr({self.query_alias}, '{self.dept_alias}') else 1 == 0"
@@ -70,6 +74,24 @@ class GetDataScope:
else: else:
param_sql_list.append('1 == 0') param_sql_list.append('1 == 0')
param_sql_list = list(dict.fromkeys(param_sql_list)) param_sql_list = list(dict.fromkeys(param_sql_list))
param_sql = f"or_({', '.join(param_sql_list)})" param_sql = f'or_({", ".join(param_sql_list)})'
return param_sql return param_sql
def DataScopeDependency( # noqa: N802
query_alias: Optional[str] = '',
db_alias: Optional[str] = 'db',
user_alias: Optional[str] = 'user_id',
dept_alias: Optional[str] = 'dept_id',
) -> params.Depends:
"""
当前用户数据权限依赖
:param query_alias: 所要查询表对应的sqlalchemy模型名称默认为''
:param db_alias: orm对象别名默认为'db'
:param user_alias: 用户id字段别名默认为'user_id'
:param dept_alias: 部门id字段别名默认为'dept_id'
:return: 当前用户数据权限依赖
"""
return Depends(GetDataScope(query_alias, db_alias, user_alias, dept_alias))
@@ -0,0 +1,12 @@
from fastapi import Depends, params
from config.get_db import get_db
def DBSessionDependency() -> params.Depends: # noqa: N802
"""
数据库会话依赖
:return: 数据库会话依赖
"""
return Depends(get_db)
@@ -0,0 +1,96 @@
from typing import Union
from fastapi import Depends, Request, params
from common.context import RequestContext
from exceptions.exception import PermissionException
from utils.dependency_util import DependencyUtil
class CheckUserInterfaceAuth:
"""
校验当前用户是否具有相应的接口权限
"""
def __init__(self, perm: Union[str, list], is_strict: bool = False) -> None:
"""
校验当前用户是否具有相应的接口权限
:param perm: 权限标识
:param is_strict: 当传入的权限标识是list类型时,是否开启严格模式,开启表示会校验列表中的每一个权限标识,所有的校验结果都需要为True才会通过
"""
self.perm = perm
self.is_strict = is_strict
def __call__(self, request: Request) -> bool:
DependencyUtil.check_exclude_routes(
request, err_msg='当前路由不在认证规则内,不可使用CheckUserInterfaceAuth依赖项'
)
current_user = RequestContext.get_current_user()
user_auth_list = current_user.permissions
if '*:*:*' in user_auth_list:
return True
if isinstance(self.perm, str) and self.perm in user_auth_list:
return True
if isinstance(self.perm, list):
if self.is_strict:
if all(perm_str in user_auth_list for perm_str in self.perm):
return True
elif any(perm_str in user_auth_list for perm_str in self.perm):
return True
raise PermissionException(data='', message='该用户无此接口权限')
class CheckRoleInterfaceAuth:
"""
根据角色校验当前用户是否具有相应的接口权限
"""
def __init__(self, role_key: Union[str, list], is_strict: bool = False) -> None:
"""
根据角色校验当前用户是否具有相应的接口权限
:param role_key: 角色标识
:param is_strict: 当传入的角色标识是list类型时,是否开启严格模式,开启表示会校验列表中的每一个角色标识,所有的校验结果都需要为True才会通过
"""
self.role_key = role_key
self.is_strict = is_strict
def __call__(self, request: Request) -> bool:
DependencyUtil.check_exclude_routes(
request, err_msg='当前路由不在认证规则内,不可使用CheckRoleInterfaceAuth依赖项'
)
current_user = RequestContext.get_current_user()
user_role_list = current_user.user.role
user_role_key_list = [role.role_key for role in user_role_list]
if isinstance(self.role_key, str) and self.role_key in user_role_key_list:
return True
if isinstance(self.role_key, list):
if self.is_strict:
if all(role_key_str in user_role_key_list for role_key_str in self.role_key):
return True
elif any(role_key_str in user_role_key_list for role_key_str in self.role_key):
return True
raise PermissionException(data='', message='该用户无此接口权限')
def UserInterfaceAuthDependency(perm: Union[str, list], is_strict: bool = False) -> params.Depends: # noqa: N802
"""
根据权限标识校验当前用户接口权限依赖
:param perm: 权限标识
:param is_strict: 当传入的权限标识是list类型时,是否开启严格模式,开启表示会校验列表中的每一个权限标识,所有的校验结果都需要为True才会通过
:return: 根据权限标识校验当前用户接口权限依赖
"""
return Depends(CheckUserInterfaceAuth(perm, is_strict))
def RoleInterfaceAuthDependency(role_key: Union[str, list], is_strict: bool = False) -> params.Depends: # noqa: N802
"""
根据角色校验当前用户接口权限依赖
:param role_key: 角色标识
:param is_strict: 当传入的角色标识是list类型时,是否开启严格模式,开启表示会校验列表中的每一个角色标识,所有的校验结果都需要为True才会通过
:return: 根据角色校验当前用户接口权限依赖
"""
return Depends(CheckRoleInterfaceAuth(role_key, is_strict))
@@ -0,0 +1,146 @@
import re
from typing import Literal, Optional, TypedDict, Union
from fastapi import Depends, Request, params
from fastapi.security import OAuth2PasswordBearer
from sqlalchemy.ext.asyncio import AsyncSession
from common.context import RequestContext
from config.env import AppConfig
from config.get_db import get_db
from exceptions.exception import AuthException
from module_admin.entity.vo.user_vo import CurrentUserModel
from module_admin.service.login_service import LoginService
# 定义排除路由的字典结构
class ExcludeRoute(TypedDict, total=False):
"""
排除路由的字典结构
:param path: 路由路径(必填)
:param methods: HTTP方法列表,空列表表示所有方法(可选,默认为[])
:param ignore_paths: 需要忽略的特定路径列表,即使匹配通配符也不排除(可选,默认为[])
"""
path: str
methods: list[Literal['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS']]
ignore_paths: list[str]
# 创建OAuth2PasswordBearer对象
oauth2_scheme = OAuth2PasswordBearer(tokenUrl='/login')
class PreAuth:
"""
登录认证前置校验依赖类
"""
def __init__(self, exclude_routes: Optional[list[ExcludeRoute]] = None) -> None:
"""
初始化登录认证前置校验依赖
:param exclude_routes: 需要排除的路由列表,格式为:
[{'path': '/path1', 'methods': ['GET', 'POST']}, {'path': '/path2/{param}', 'methods': ['GET']}]
methods 可以是字符串或列表,空列表表示所有方法
"""
self.exclude_routes = exclude_routes or []
# 编译排除路径为正则表达式模式,并存储方法信息
self.exclude_patterns = []
for route in self.exclude_routes:
# 使用TypedDict,确保路由字典包含path字段
path = route.get('path', '')
methods = route.get('methods', [])
ignore_paths = route.get('ignore_paths', [])
# 编译路径为正则表达式
pattern = self._compile_path_pattern(path)
# 存储编译后的模式和方法信息
self.exclude_patterns.append(
{
'pattern': pattern,
'methods': [method.upper() for method in methods],
'original_path': path,
'ignore_paths': ignore_paths,
}
)
def _compile_path_pattern(self, path: str) -> re.Pattern:
"""
将FastAPI路径转换为正则表达式模式
:param path: FastAPI路径(如 /configKey/{config_key}
:return: 编译后的正则表达式模式
"""
# 将FastAPI路径参数转换为正则表达式
# 例如:/configKey/{config_key} -> /configKey/[^/]+
pattern_str = re.sub(r'\{[^}]+\}', r'[^/]+', path)
# 添加开始和结束锚点,确保精确匹配
return re.compile(f'^{pattern_str}$')
async def __call__(self, request: Request, db: AsyncSession = Depends(get_db)) -> Union[CurrentUserModel, None]:
"""
执行登录认证校验
:param request: 当前请求对象
:param db: 数据库会话
:return: 当前用户信息
"""
# 获取当前请求路径和方法
path = request.url.path
method = request.method.upper()
# 从配置中获取APP_ROOT_PATH
app_root_path = AppConfig.app_root_path
# 去掉APP_ROOT_PATH前缀
if app_root_path and path.startswith(app_root_path):
path = path[len(app_root_path) :]
# 设置上下文变量
RequestContext.set_current_exclude_patterns(self.exclude_patterns)
# 检查路径和方法是否匹配排除模式
for item in self.exclude_patterns:
pattern = item['pattern']
exclude_methods = item['methods']
ignore_paths = item['ignore_paths']
# 检查当前路径是否在忽略列表中
if path in ignore_paths:
continue
# 检查路径是否匹配,并且methods为空列表(匹配所有方法)或者当前方法在允许列表中
if pattern.match(path) and (not exclude_methods or method in exclude_methods):
# 跳过认证
return None
# 否则执行正常认证
token = request.headers.get('Authorization')
if not token:
raise AuthException(data='', message='用户未登录,请先完成登录')
current_user = await LoginService.get_current_user(request, token, db)
return current_user
def PreAuthDependency(exclude_routes: Optional[list[ExcludeRoute]] = None) -> params.Depends: # noqa: N802
"""
登录认证前置校验依赖
:param exclude_routes: 需要排除的路由列表,格式为:
[{'path': '/path1', 'methods': ['GET', 'POST']}, {'path': '/path2/{param}', 'methods': ['GET']}]
methods 可以是字符串或列表,空列表表示所有方法
:return: 登录认证前置校验依赖
"""
return Depends(PreAuth(exclude_routes))
def CurrentUserDependency() -> params.Depends: # noqa: N802
"""
当前登录用户信息依赖
:return: 当前登录用户信息依赖
"""
return Depends(LoginService.get_current_user)
@@ -5,6 +5,7 @@ class CommonConstant:
""" """
常用常量 常用常量
PASSWORD_ERROR_COUNT: 密码错误次数
WWW: www主域 WWW: www主域
HTTP: http请求 HTTP: http请求
HTTPS: https请求 HTTPS: https请求
@@ -19,6 +20,7 @@ class CommonConstant:
NOT_UNIQUE: 校验是否唯一的返回标识 NOT_UNIQUE: 校验是否唯一的返回标识
""" """
PASSWORD_ERROR_COUNT = 5
WWW = 'www.' WWW = 'www.'
HTTP = 'http://' HTTP = 'http://'
HTTPS = 'https://' HTTPS = 'https://'
+100
View File
@@ -0,0 +1,100 @@
import re
from contextvars import ContextVar, Token
from typing import Literal, Optional, Union
from exceptions.exception import LoginException
from module_admin.entity.vo.user_vo import CurrentUserModel
# 定义上下文变量
# 存储当前请求的编译后的排除路由模式列表
current_exclude_patterns: ContextVar[
Optional[
list[
dict[str, Union[str, list[Literal['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS']], re.Pattern]]
]
]
] = ContextVar('current_exclude_patterns', default=None)
# 存储当前用户信息
current_user: ContextVar[Optional[CurrentUserModel]] = ContextVar('current_user', default=None)
class RequestContext:
"""
请求上下文管理类,用于设置和清理上下文变量
"""
@staticmethod
def set_current_exclude_patterns(
exclude_patterns: list[
dict[str, Union[str, list[Literal['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS']], re.Pattern]]
],
) -> Token:
"""
设置当前请求的编译后的排除路由模式列表
:param exclude_patterns: 编译后的排除路由模式列表
:return: 上下文变量令牌,用于重置
"""
return current_exclude_patterns.set(exclude_patterns)
@staticmethod
def get_current_exclude_patterns() -> list[
dict[str, Union[str, list[Literal['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS']], re.Pattern]]
]:
"""
获取当前请求的编译后的排除路由模式列表
:return: 编译后的排除路由模式列表
"""
_exclude_patterns = current_exclude_patterns.get()
if _exclude_patterns is None:
_exclude_patterns = []
return _exclude_patterns
@staticmethod
def set_current_user(user: CurrentUserModel) -> Token:
"""
设置当前用户信息
:param user: 用户信息
:return: 上下文变量令牌,用于重置
"""
return current_user.set(user)
@staticmethod
def get_current_user() -> CurrentUserModel:
"""
获取当前用户信息
:return: 用户信息
"""
_current_user = current_user.get()
if _current_user is None:
raise LoginException(data='', message='当前用户信息为空,请检查是否已登录')
return _current_user
@staticmethod
def reset_current_exclude_patterns(token: Token) -> None:
"""
重置当前请求的编译后的排除路由模式列表
:param token: 设置编译后的排除路由模式列表时返回的令牌
"""
current_exclude_patterns.reset(token)
@staticmethod
def reset_current_user(token: Token) -> None:
"""
重置当前用户信息
:param token: 设置用户信息时返回的令牌
"""
current_user.reset(token)
@staticmethod
def clear_all() -> None:
"""
清除所有上下文变量
"""
current_exclude_patterns.set(None)
current_user.set(None)
@@ -1,4 +1,5 @@
from enum import Enum from enum import Enum
from typing import Union
class BusinessType(Enum): class BusinessType(Enum):
@@ -35,11 +36,11 @@ class RedisInitKeyConfig(Enum):
""" """
@property @property
def key(self): def key(self) -> Union[str, None]:
return self.value.get('key') return self.value.get('key')
@property @property
def remark(self): def remark(self) -> Union[str, None]:
return self.value.get('remark') return self.value.get('remark')
ACCESS_TOKEN = {'key': 'access_token', 'remark': '登录令牌信息'} ACCESS_TOKEN = {'key': 'access_token', 'remark': '登录令牌信息'}
+405
View File
@@ -0,0 +1,405 @@
import importlib
import os
import sys
from collections.abc import Sequence
from enum import Enum
from typing import Annotated, Any, Callable, Literal, Optional, Union
from annotated_doc import Doc
from fastapi import FastAPI, params
from fastapi.datastructures import Default
from fastapi.routing import APIRoute, APIRouter
from fastapi.utils import generate_unique_id
from starlette.responses import JSONResponse, Response
from starlette.routing import BaseRoute
from starlette.types import ASGIApp, Lifespan
from typing_extensions import deprecated
class APIRouterPro(APIRouter):
"""
`APIRouterPro` class, inherited from the `APIRouter` class, it has all the functions of `APIRouter` and provides some additional parameter settings.
`APIRouter` class, used to group *path operations*, for example to structure
an app in multiple files. It would then be included in the `FastAPI` app, or
in another `APIRouter` (ultimately included in the app).
Read more about it in the
[FastAPI docs for Bigger Applications - Multiple Files](https://fastapi.tiangolo.com/tutorial/bigger-applications/).
## Example
```python
from common.router import APIRouterPro, FastAPI
app = FastAPI()
router = APIRouterPro()
@router.get('/users/', tags=['users'])
async def read_users():
return [{'username': 'Rick'}, {'username': 'Morty'}]
app.include_router(router)
```
"""
def __init__( # noqa: PLR0913
self,
*,
prefix: Annotated[str, Doc('An optional path prefix for the router.')] = '',
order_num: Annotated[int, Doc('An optional order number for the router.')] = 100,
auto_register: Annotated[bool, Doc('An optional auto register flag for the router.')] = True,
tags: Annotated[
Optional[list[Union[str, Enum]]],
Doc(
"""
A list of tags to be applied to all the *path operations* in this
router.
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
Read more about it in the
[FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/).
"""
),
] = None,
dependencies: Annotated[
Optional[Sequence[params.Depends]],
Doc(
"""
A list of dependencies (using `Depends()`) to be applied to all the
*path operations* in this router.
Read more about it in the
[FastAPI docs for Bigger Applications - Multiple Files](https://fastapi.tiangolo.com/tutorial/bigger-applications/#include-an-apirouter-with-a-custom-prefix-tags-responses-and-dependencies).
"""
),
] = None,
default_response_class: Annotated[
type[Response],
Doc(
"""
The default response class to be used.
Read more in the
[FastAPI docs for Custom Response - HTML, Stream, File, others](https://fastapi.tiangolo.com/advanced/custom-response/#default-response-class).
"""
),
] = Default(JSONResponse),
responses: Annotated[
Optional[dict[Union[int, str], dict[str, Any]]],
Doc(
"""
Additional responses to be shown in OpenAPI.
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
Read more about it in the
[FastAPI docs for Additional Responses in OpenAPI](https://fastapi.tiangolo.com/advanced/additional-responses/).
And in the
[FastAPI docs for Bigger Applications](https://fastapi.tiangolo.com/tutorial/bigger-applications/#include-an-apirouter-with-a-custom-prefix-tags-responses-and-dependencies).
"""
),
] = None,
callbacks: Annotated[
Optional[list[BaseRoute]],
Doc(
"""
OpenAPI callbacks that should apply to all *path operations* in this
router.
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
Read more about it in the
[FastAPI docs for OpenAPI Callbacks](https://fastapi.tiangolo.com/advanced/openapi-callbacks/).
"""
),
] = None,
routes: Annotated[
Optional[list[BaseRoute]],
Doc(
"""
**Note**: you probably shouldn't use this parameter, it is inherited
from Starlette and supported for compatibility.
---
A list of routes to serve incoming HTTP and WebSocket requests.
"""
),
deprecated(
"""
You normally wouldn't use this parameter with FastAPI, it is inherited
from Starlette and supported for compatibility.
In FastAPI, you normally would use the *path operation methods*,
like `router.get()`, `router.post()`, etc.
"""
),
] = None,
redirect_slashes: Annotated[
bool,
Doc(
"""
Whether to detect and redirect slashes in URLs when the client doesn't
use the same format.
"""
),
] = True,
default: Annotated[
Optional[ASGIApp],
Doc(
"""
Default function handler for this router. Used to handle
404 Not Found errors.
"""
),
] = None,
dependency_overrides_provider: Annotated[
Optional[Any],
Doc(
"""
Only used internally by FastAPI to handle dependency overrides.
You shouldn't need to use it. It normally points to the `FastAPI` app
object.
"""
),
] = None,
route_class: Annotated[
type[APIRoute],
Doc(
"""
Custom route (*path operation*) class to be used by this router.
Read more about it in the
[FastAPI docs for Custom Request and APIRoute class](https://fastapi.tiangolo.com/how-to/custom-request-and-route/#custom-apiroute-class-in-a-router).
"""
),
] = APIRoute,
on_startup: Annotated[
Optional[Sequence[Callable[[], Any]]],
Doc(
"""
A list of startup event handler functions.
You should instead use the `lifespan` handlers.
Read more in the [FastAPI docs for `lifespan`](https://fastapi.tiangolo.com/advanced/events/).
"""
),
] = None,
on_shutdown: Annotated[
Optional[Sequence[Callable[[], Any]]],
Doc(
"""
A list of shutdown event handler functions.
You should instead use the `lifespan` handlers.
Read more in the
[FastAPI docs for `lifespan`](https://fastapi.tiangolo.com/advanced/events/).
"""
),
] = None,
# the generic to Lifespan[AppType] is the type of the top level application
# which the router cannot know statically, so we use typing.Any
lifespan: Annotated[
Optional[Lifespan[Any]],
Doc(
"""
A `Lifespan` context manager handler. This replaces `startup` and
`shutdown` functions with a single context manager.
Read more in the
[FastAPI docs for `lifespan`](https://fastapi.tiangolo.com/advanced/events/).
"""
),
] = None,
deprecated: Annotated[
Optional[bool],
Doc(
"""
Mark all *path operations* in this router as deprecated.
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
Read more about it in the
[FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/).
"""
),
] = None,
include_in_schema: Annotated[
bool,
Doc(
"""
To include (or not) all the *path operations* in this router in the
generated OpenAPI.
This affects the generated OpenAPI (e.g. visible at `/docs`).
Read more about it in the
[FastAPI docs for Query Parameters and String Validations](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#exclude-parameters-from-openapi).
"""
),
] = True,
generate_unique_id_function: Annotated[
Callable[[APIRoute], str],
Doc(
"""
Customize the function used to generate unique IDs for the *path
operations* shown in the generated OpenAPI.
This is particularly useful when automatically generating clients or
SDKs for your API.
Read more about it in the
[FastAPI docs about how to Generate Clients](https://fastapi.tiangolo.com/advanced/generate-clients/#custom-generate-unique-id-function).
"""
),
] = Default(generate_unique_id),
) -> None:
self.order_num = order_num
self.auto_register = auto_register
super().__init__(
prefix=prefix,
tags=tags,
dependencies=dependencies,
default_response_class=default_response_class,
responses=responses,
callbacks=callbacks,
routes=routes,
redirect_slashes=redirect_slashes,
default=default,
dependency_overrides_provider=dependency_overrides_provider,
route_class=route_class,
on_startup=on_startup,
on_shutdown=on_shutdown,
lifespan=lifespan,
deprecated=deprecated,
include_in_schema=include_in_schema,
generate_unique_id_function=generate_unique_id_function,
)
class RouterRegister:
"""
路由注册器,用于自动注册所有controller目录下的路由
"""
def __init__(self, app: FastAPI) -> None:
"""
初始化路由注册器
:param app: FastAPI对象
"""
self.app = app
# 获取项目根目录
self.project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
sys.path.insert(0, self.project_root)
def _find_controller_files(self) -> list[str]:
"""
查找所有controller目录下的py文件
:return: py文件路径列表
"""
controller_files = []
# 遍历所有目录,查找controller目录
for root, _dirs, files in os.walk(self.project_root):
# 检查当前目录是否为controller目录
if os.path.basename(root) == 'controller':
# 遍历controller目录下的所有py文件
for file in files:
if file.endswith('.py') and not file.startswith('__'):
file_path = os.path.join(root, file)
controller_files.append(file_path)
return controller_files
def _import_module_and_get_routers(self, controller_files: list[str]) -> list[tuple[str, APIRouter]]:
"""
导入模块并获取路由实例
:param controller_files: controller目录下的py文件路径列表
:return: 路由实例列表
"""
routers = []
for file_path in controller_files:
# 计算模块路径
relative_path = os.path.relpath(file_path, self.project_root)
module_name = relative_path.replace(os.sep, '.')[:-3]
try:
# 动态导入模块
module = importlib.import_module(module_name)
# 遍历模块属性,寻找APIRouter和APIRouterPro实例
for attr_name in dir(module):
attr = getattr(module, attr_name)
# 对于APIRouterPro实例,只有当auto_register=True时才添加
if isinstance(attr, APIRouterPro):
if attr.auto_register:
routers.append((attr_name, attr))
# 对于APIRouter实例,直接添加
elif isinstance(attr, APIRouter):
routers.append((attr_name, attr))
except Exception as e:
print(f'Error importing module {module_name}: {e}')
return routers
def _sort_routers(self, routers: list[tuple[str, APIRouter]]) -> list[tuple[str, APIRouter]]:
"""
按规则排序路由
:param routers: 路由实例列表
:return: 排序后的路由实例列表
"""
# 按规则排序路由
def sort_key(item: tuple[str, APIRouter]) -> Union[tuple[Literal[0], int, str], tuple[Literal[1], str]]:
attr_name, router = item
# APIRouterPro实例按order_num排序,序号越小越靠前
if isinstance(router, APIRouterPro):
return (0, router.order_num, attr_name)
# APIRouter实例按变量名首字母排序
return (1, attr_name)
return sorted(routers, key=sort_key)
def _register_routers_to_app(self, routers: list[tuple[str, APIRouter]]) -> None:
"""
将路由注册到FastAPI应用
:param routers: 排序后的路由实例列表
:return: None
"""
for _attr_name, router in routers:
self.app.include_router(router=router)
def register_routers(self) -> None:
"""
自动注册所有controller目录下的路由
:return: None
"""
# 查找所有controller目录下的py文件
controller_files = self._find_controller_files()
# 导入模块并获取路由实例
routers = self._import_module_and_get_routers(controller_files)
# 按规则排序路由
sorted_routers = self._sort_routers(routers)
# 注册路由到FastAPI应用
self._register_routers_to_app(sorted_routers)
def auto_register_routers(app: FastAPI) -> None:
"""
自动注册所有controller目录下的路由
:param app: FastAPI对象
:return: None
"""
# 使用路由注册器进行注册
router_register = RouterRegister(app)
router_register.register_routers()
+105
View File
@@ -0,0 +1,105 @@
from datetime import datetime
from typing import Any, Generic, Optional, TypeVar, Union
from pydantic import BaseModel, ConfigDict, Field, create_model
from pydantic.alias_generators import to_camel
from typing_extensions import Self
from common.constant import HttpStatusConstant
T = TypeVar('T')
class CrudResponseModel(BaseModel):
"""
操作响应模型
"""
is_success: bool = Field(description='操作是否成功')
message: str = Field(description='响应信息')
result: Optional[Any] = Field(default=None, description='响应结果')
class ResponseBaseModel(BaseModel):
"""
响应模型
"""
code: int = Field(default=HttpStatusConstant.SUCCESS, description='响应码')
msg: str = Field(default='操作成功', description='响应信息')
success: bool = Field(default=True, description='响应是否成功')
time: datetime = Field(default_factory=datetime.now, description='响应时间')
class DynamicResponseModel(ResponseBaseModel, Generic[T]):
"""
动态响应模型
"""
model_config = ConfigDict(alias_generator=to_camel)
def __class_getitem__(cls, item: Any) -> Union[Any, Self]:
"""
当使用 DynamicResponseModel[Item] 语法时,动态创建一个包含所有字段的新模型
"""
# 检查是否已经为该类型创建了模型
if not hasattr(cls, '_cached_models'):
cls._cached_models = {}
if item in cls._cached_models:
return cls._cached_models[item]
# 检查item是否为Pydantic模型
if not hasattr(item, 'model_fields'):
raise TypeError(f'{item} 不是一个Pydantic模型,请使用Pydantic模型作为泛型参数')
# 获取ResponseBaseModel的字段
base_fields = {}
for field_name, field in cls.model_fields.items():
base_fields[field_name] = (field.annotation, field)
# 获取泛型类型的字段
item_fields = {}
for field_name, field in item.model_fields.items():
item_fields[field_name] = (field.annotation, field)
# 合并所有字段
all_fields = {**base_fields, **item_fields}
# 动态创建新模型
new_model = create_model(
f'DynamicResponseModel[{item.__name__}]', __base__=cls, __config__=cls.model_config, **all_fields
)
# 缓存模型
cls._cached_models[item] = new_model
return new_model
class PageModel(BaseModel, Generic[T]):
"""
分页模型
"""
model_config = ConfigDict(alias_generator=to_camel)
rows: list[T] = Field(description='记录列表')
page_num: int = Field(description='当前页码')
page_size: int = Field(description='每页记录数')
total: int = Field(description='总记录数')
has_next: bool = Field(description='是否有下一页')
class PageResponseModel(PageModel, ResponseBaseModel, Generic[T]):
"""
分页响应模型
"""
class DataResponseModel(ResponseBaseModel, Generic[T]):
"""
数据响应模型
"""
data: T = Field(description='响应数据')
+4 -4
View File
@@ -1,8 +1,8 @@
from sqlalchemy.ext.asyncio import create_async_engine
from sqlalchemy.ext.asyncio import async_sessionmaker
from sqlalchemy.ext.asyncio import AsyncAttrs
from sqlalchemy.orm import DeclarativeBase
from urllib.parse import quote_plus from urllib.parse import quote_plus
from sqlalchemy.ext.asyncio import AsyncAttrs, async_sessionmaker, create_async_engine
from sqlalchemy.orm import DeclarativeBase
from config.env import DataBaseConfig from config.env import DataBaseConfig
ASYNC_SQLALCHEMY_DATABASE_URL = ( ASYNC_SQLALCHEMY_DATABASE_URL = (
+13 -19
View File
@@ -2,11 +2,11 @@ import argparse
import configparser import configparser
import os import os
import sys import sys
from typing import Literal
from dotenv import load_dotenv from dotenv import load_dotenv
from functools import lru_cache
from pydantic import computed_field from pydantic import computed_field
from pydantic_settings import BaseSettings from pydantic_settings import BaseSettings
from typing import Literal
class AppSettings(BaseSettings): class AppSettings(BaseSettings):
@@ -86,7 +86,7 @@ class GenSettings:
GEN_PATH = 'vf_admin/gen_path' GEN_PATH = 'vf_admin/gen_path'
def __init__(self): def __init__(self) -> None:
if not os.path.exists(self.GEN_PATH): if not os.path.exists(self.GEN_PATH):
os.makedirs(self.GEN_PATH) os.makedirs(self.GEN_PATH)
@@ -130,7 +130,7 @@ class UploadSettings:
] ]
DOWNLOAD_PATH = 'vf_admin/download_path' DOWNLOAD_PATH = 'vf_admin/download_path'
def __init__(self): def __init__(self) -> None:
if not os.path.exists(self.UPLOAD_PATH): if not os.path.exists(self.UPLOAD_PATH):
os.makedirs(self.UPLOAD_PATH) os.makedirs(self.UPLOAD_PATH)
if not os.path.exists(self.DOWNLOAD_PATH): if not os.path.exists(self.DOWNLOAD_PATH):
@@ -151,59 +151,53 @@ class GetConfig:
获取配置 获取配置
""" """
def __init__(self): def __init__(self) -> None:
self.parse_cli_args() self.parse_cli_args()
@lru_cache() def get_app_config(self) -> AppSettings:
def get_app_config(self):
""" """
获取应用配置 获取应用配置
""" """
# 实例化应用配置模型 # 实例化应用配置模型
return AppSettings() return AppSettings()
@lru_cache() def get_jwt_config(self) -> JwtSettings:
def get_jwt_config(self):
""" """
获取Jwt配置 获取Jwt配置
""" """
# 实例化Jwt配置模型 # 实例化Jwt配置模型
return JwtSettings() return JwtSettings()
@lru_cache() def get_database_config(self) -> DataBaseSettings:
def get_database_config(self):
""" """
获取数据库配置 获取数据库配置
""" """
# 实例化数据库配置模型 # 实例化数据库配置模型
return DataBaseSettings() return DataBaseSettings()
@lru_cache() def get_redis_config(self) -> RedisSettings:
def get_redis_config(self):
""" """
获取Redis配置 获取Redis配置
""" """
# 实例化Redis配置模型 # 实例化Redis配置模型
return RedisSettings() return RedisSettings()
@lru_cache() def get_gen_config(self) -> GenSettings:
def get_gen_config(self):
""" """
获取代码生成配置 获取代码生成配置
""" """
# 实例化代码生成配置 # 实例化代码生成配置
return GenSettings() return GenSettings()
@lru_cache() def get_upload_config(self) -> UploadSettings:
def get_upload_config(self):
""" """
获取数据库配置 获取上传配置
""" """
# 实例上传配置 # 实例上传配置
return UploadSettings() return UploadSettings()
@staticmethod @staticmethod
def parse_cli_args(): def parse_cli_args() -> None:
""" """
解析命令行参数 解析命令行参数
""" """
+7 -3
View File
@@ -1,8 +1,12 @@
from config.database import async_engine, AsyncSessionLocal, Base from collections.abc import AsyncGenerator
from sqlalchemy.ext.asyncio import AsyncSession
from config.database import AsyncSessionLocal, Base, async_engine
from utils.log_util import logger from utils.log_util import logger
async def get_db(): async def get_db() -> AsyncGenerator[AsyncSession, None]:
""" """
每一个请求处理完毕后会关闭当前连接,不同的请求使用不同的连接 每一个请求处理完毕后会关闭当前连接,不同的请求使用不同的连接
@@ -12,7 +16,7 @@ async def get_db():
yield current_db yield current_db
async def init_create_table(): async def init_create_table() -> None:
""" """
应用启动时初始化数据库连接 应用启动时初始化数据库连接
+8 -5
View File
@@ -1,5 +1,8 @@
from fastapi import FastAPI
from redis import asyncio as aioredis from redis import asyncio as aioredis
from redis.exceptions import AuthenticationError, TimeoutError, RedisError from redis.exceptions import AuthenticationError, RedisError
from redis.exceptions import TimeoutError as RedisTimeoutError
from config.database import AsyncSessionLocal from config.database import AsyncSessionLocal
from config.env import RedisConfig from config.env import RedisConfig
from module_admin.service.config_service import ConfigService from module_admin.service.config_service import ConfigService
@@ -37,14 +40,14 @@ class RedisUtil:
logger.error('❌️ redis连接失败') logger.error('❌️ redis连接失败')
except AuthenticationError as e: except AuthenticationError as e:
logger.error(f'❌️ redis用户名或密码错误,详细错误信息:{e}') logger.error(f'❌️ redis用户名或密码错误,详细错误信息:{e}')
except TimeoutError as e: except RedisTimeoutError as e:
logger.error(f'❌️ redis连接超时,详细错误信息:{e}') logger.error(f'❌️ redis连接超时,详细错误信息:{e}')
except RedisError as e: except RedisError as e:
logger.error(f'❌️ redis连接错误,详细错误信息:{e}') logger.error(f'❌️ redis连接错误,详细错误信息:{e}')
return redis return redis
@classmethod @classmethod
async def close_redis_pool(cls, app): async def close_redis_pool(cls, app: FastAPI) -> None:
""" """
应用关闭时关闭redis连接 应用关闭时关闭redis连接
@@ -55,7 +58,7 @@ class RedisUtil:
logger.info('✅️ 关闭redis连接成功') logger.info('✅️ 关闭redis连接成功')
@classmethod @classmethod
async def init_sys_dict(cls, redis): async def init_sys_dict(cls, redis: FastAPI) -> None:
""" """
应用启动时缓存字典表 应用启动时缓存字典表
@@ -66,7 +69,7 @@ class RedisUtil:
await DictDataService.init_cache_sys_dict_services(session, redis) await DictDataService.init_cache_sys_dict_services(session, redis)
@classmethod @classmethod
async def init_sys_config(cls, redis): async def init_sys_config(cls, redis: aioredis.Redis) -> None:
""" """
应用启动时缓存参数配置表 应用启动时缓存参数配置表
+39 -38
View File
@@ -1,7 +1,12 @@
import json import json
from apscheduler.events import EVENT_ALL from asyncio import iscoroutinefunction
from datetime import datetime, timedelta
from typing import Optional, Union
from apscheduler.events import EVENT_ALL, SchedulerEvent
from apscheduler.executors.asyncio import AsyncIOExecutor from apscheduler.executors.asyncio import AsyncIOExecutor
from apscheduler.executors.pool import ProcessPoolExecutor from apscheduler.executors.pool import ProcessPoolExecutor
from apscheduler.job import Job
from apscheduler.jobstores.memory import MemoryJobStore from apscheduler.jobstores.memory import MemoryJobStore
from apscheduler.jobstores.redis import RedisJobStore from apscheduler.jobstores.redis import RedisJobStore
from apscheduler.jobstores.sqlalchemy import SQLAlchemyJobStore from apscheduler.jobstores.sqlalchemy import SQLAlchemyJobStore
@@ -9,27 +14,29 @@ from apscheduler.schedulers.asyncio import AsyncIOScheduler
from apscheduler.triggers.combining import OrTrigger from apscheduler.triggers.combining import OrTrigger
from apscheduler.triggers.cron import CronTrigger from apscheduler.triggers.cron import CronTrigger
from apscheduler.triggers.date import DateTrigger from apscheduler.triggers.date import DateTrigger
from asyncio import iscoroutinefunction
from datetime import datetime, timedelta
from sqlalchemy.engine import create_engine from sqlalchemy.engine import create_engine
from sqlalchemy.orm import sessionmaker from sqlalchemy.orm import sessionmaker
from typing import Union
import module_task # noqa: F401
from config.database import AsyncSessionLocal, quote_plus from config.database import AsyncSessionLocal, quote_plus
from config.env import DataBaseConfig, RedisConfig from config.env import DataBaseConfig, RedisConfig
from module_admin.dao.job_dao import JobDao from module_admin.dao.job_dao import JobDao
from module_admin.entity.vo.job_vo import JobLogModel, JobModel from module_admin.entity.vo.job_vo import JobLogModel, JobModel
from module_admin.service.job_log_service import JobLogService from module_admin.service.job_log_service import JobLogService
from utils.log_util import logger from utils.log_util import logger
import module_task # noqa: F401
# 重写Cron定时 # 重写Cron定时
class MyCronTrigger(CronTrigger): class MyCronTrigger(CronTrigger):
CRON_EXPRESSION_LENGTH_MIN = 6
CRON_EXPRESSION_LENGTH_MAX = 7
WEEKDAY_COUNT = 5
@classmethod @classmethod
def from_crontab(cls, expr: str, timezone=None): def from_crontab(cls, expr: str, timezone: Optional[str] = None) -> 'MyCronTrigger':
values = expr.split() values = expr.split()
if len(values) != 6 and len(values) != 7: if len(values) != cls.CRON_EXPRESSION_LENGTH_MIN and len(values) != cls.CRON_EXPRESSION_LENGTH_MAX:
raise ValueError('Wrong number of fields; got {}, expected 6 or 7'.format(len(values))) raise ValueError(f'Wrong number of fields; got {len(values)}, expected 6 or 7')
second = values[0] second = values[0]
minute = values[1] minute = values[1]
@@ -37,7 +44,7 @@ class MyCronTrigger(CronTrigger):
if '?' in values[3]: if '?' in values[3]:
day = None day = None
elif 'L' in values[5]: elif 'L' in values[5]:
day = f"last {values[5].replace('L', '')}" day = f'last {values[5].replace("L", "")}'
elif 'W' in values[3]: elif 'W' in values[3]:
day = cls.__find_recent_workday(int(values[3].split('W')[0])) day = cls.__find_recent_workday(int(values[3].split('W')[0]))
else: else:
@@ -49,11 +56,8 @@ class MyCronTrigger(CronTrigger):
week = int(values[5].split('#')[1]) week = int(values[5].split('#')[1])
else: else:
week = values[5] week = values[5]
if '#' in values[5]: day_of_week = int(values[5].split('#')[0]) - 1 if '#' in values[5] else None
day_of_week = int(values[5].split('#')[0]) - 1 year = values[6] if len(values) == cls.CRON_EXPRESSION_LENGTH_MAX else None
else:
day_of_week = None
year = values[6] if len(values) == 7 else None
return cls( return cls(
second=second, second=second,
minute=minute, minute=minute,
@@ -67,18 +71,16 @@ class MyCronTrigger(CronTrigger):
) )
@classmethod @classmethod
def __find_recent_workday(cls, day: int): def __find_recent_workday(cls, day: int) -> int:
now = datetime.now() now = datetime.now()
date = datetime(now.year, now.month, day) date = datetime(now.year, now.month, day)
if date.weekday() < 5: if date.weekday() < cls.WEEKDAY_COUNT:
return date.day return date.day
else:
diff = 1 diff = 1
while True: while True:
previous_day = date - timedelta(days=diff) previous_day = date - timedelta(days=diff)
if previous_day.weekday() < 5: if previous_day.weekday() < cls.WEEKDAY_COUNT:
return previous_day.day return previous_day.day
else:
diff += 1 diff += 1
@@ -100,18 +102,17 @@ engine = create_engine(
pool_timeout=DataBaseConfig.db_pool_timeout, pool_timeout=DataBaseConfig.db_pool_timeout,
) )
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
redis_config = {
'host': RedisConfig.redis_host,
'port': RedisConfig.redis_port,
'username': RedisConfig.redis_username,
'password': RedisConfig.redis_password,
'db': RedisConfig.redis_database,
}
job_stores = { job_stores = {
'default': MemoryJobStore(), 'default': MemoryJobStore(),
'sqlalchemy': SQLAlchemyJobStore(url=SQLALCHEMY_DATABASE_URL, engine=engine), 'sqlalchemy': SQLAlchemyJobStore(url=SQLALCHEMY_DATABASE_URL, engine=engine),
'redis': RedisJobStore( 'redis': RedisJobStore(**redis_config),
**dict(
host=RedisConfig.redis_host,
port=RedisConfig.redis_port,
username=RedisConfig.redis_username,
password=RedisConfig.redis_password,
db=RedisConfig.redis_database,
)
),
} }
executors = {'default': AsyncIOExecutor(), 'processpool': ProcessPoolExecutor(5)} executors = {'default': AsyncIOExecutor(), 'processpool': ProcessPoolExecutor(5)}
job_defaults = {'coalesce': False, 'max_instance': 1} job_defaults = {'coalesce': False, 'max_instance': 1}
@@ -125,7 +126,7 @@ class SchedulerUtil:
""" """
@classmethod @classmethod
async def init_system_scheduler(cls): async def init_system_scheduler(cls) -> None:
""" """
应用启动时初始化定时任务 应用启动时初始化定时任务
@@ -142,7 +143,7 @@ class SchedulerUtil:
logger.info('✅️ 系统初始定时任务加载成功') logger.info('✅️ 系统初始定时任务加载成功')
@classmethod @classmethod
async def close_system_scheduler(cls): async def close_system_scheduler(cls) -> None:
""" """
应用关闭时关闭定时任务 应用关闭时关闭定时任务
@@ -152,7 +153,7 @@ class SchedulerUtil:
logger.info('✅️ 关闭定时任务成功') logger.info('✅️ 关闭定时任务成功')
@classmethod @classmethod
def get_scheduler_job(cls, job_id: Union[str, int]): def get_scheduler_job(cls, job_id: Union[str, int]) -> Job:
""" """
根据任务id获取任务对象 根据任务id获取任务对象
@@ -164,7 +165,7 @@ class SchedulerUtil:
return query_job return query_job
@classmethod @classmethod
def add_scheduler_job(cls, job_info: JobModel): def add_scheduler_job(cls, job_info: JobModel) -> None:
""" """
根据输入的任务对象信息添加任务 根据输入的任务对象信息添加任务
@@ -183,14 +184,14 @@ class SchedulerUtil:
id=str(job_info.job_id), id=str(job_info.job_id),
name=job_info.job_name, name=job_info.job_name,
misfire_grace_time=1000000000000 if job_info.misfire_policy == '3' else None, misfire_grace_time=1000000000000 if job_info.misfire_policy == '3' else None,
coalesce=True if job_info.misfire_policy == '2' else False, coalesce=job_info.misfire_policy == '2',
max_instances=3 if job_info.concurrent == '0' else 1, max_instances=3 if job_info.concurrent == '0' else 1,
jobstore=job_info.job_group, jobstore=job_info.job_group,
executor=job_executor, executor=job_executor,
) )
@classmethod @classmethod
def execute_scheduler_job_once(cls, job_info: JobModel): def execute_scheduler_job_once(cls, job_info: JobModel) -> None:
""" """
根据输入的任务对象执行一次任务 根据输入的任务对象执行一次任务
@@ -212,14 +213,14 @@ class SchedulerUtil:
id=str(job_info.job_id), id=str(job_info.job_id),
name=job_info.job_name, name=job_info.job_name,
misfire_grace_time=1000000000000 if job_info.misfire_policy == '3' else None, misfire_grace_time=1000000000000 if job_info.misfire_policy == '3' else None,
coalesce=True if job_info.misfire_policy == '2' else False, coalesce=job_info.misfire_policy == '2',
max_instances=3 if job_info.concurrent == '0' else 1, max_instances=3 if job_info.concurrent == '0' else 1,
jobstore=job_info.job_group, jobstore=job_info.job_group,
executor=job_executor, executor=job_executor,
) )
@classmethod @classmethod
def remove_scheduler_job(cls, job_id: Union[str, int]): def remove_scheduler_job(cls, job_id: Union[str, int]) -> None:
""" """
根据任务id移除任务 根据任务id移除任务
@@ -231,7 +232,7 @@ class SchedulerUtil:
scheduler.remove_job(job_id=str(job_id)) scheduler.remove_job(job_id=str(job_id))
@classmethod @classmethod
def scheduler_event_listener(cls, event): def scheduler_event_listener(cls, event: SchedulerEvent) -> None:
# 获取事件类型和任务ID # 获取事件类型和任务ID
event_type = event.__class__.__name__ event_type = event.__class__.__name__
# 获取任务执行异常信息 # 获取任务执行异常信息
@@ -260,7 +261,7 @@ class SchedulerUtil:
# 获取任务触发器 # 获取任务触发器
job_trigger = str(query_job_info.get('trigger')) job_trigger = str(query_job_info.get('trigger'))
# 构造日志消息 # 构造日志消息
job_message = f"事件类型: {event_type}, 任务ID: {job_id}, 任务名称: {job_name}, 执行于{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}" job_message = f'事件类型: {event_type}, 任务ID: {job_id}, 任务名称: {job_name}, 执行于{datetime.now().strftime("%Y-%m-%d %H:%M:%S")}'
job_log = JobLogModel( job_log = JobLogModel(
jobName=job_name, jobName=job_name,
jobGroup=job_group, jobGroup=job_group,
@@ -1,9 +1,12 @@
from typing import Optional
class LoginException(Exception): class LoginException(Exception):
""" """
自定义登录异常LoginException 自定义登录异常LoginException
""" """
def __init__(self, data: str = None, message: str = None): def __init__(self, data: Optional[str] = None, message: Optional[str] = None) -> None:
self.data = data self.data = data
self.message = message self.message = message
@@ -13,7 +16,7 @@ class AuthException(Exception):
自定义令牌异常AuthException 自定义令牌异常AuthException
""" """
def __init__(self, data: str = None, message: str = None): def __init__(self, data: Optional[str] = None, message: Optional[str] = None) -> None:
self.data = data self.data = data
self.message = message self.message = message
@@ -23,7 +26,7 @@ class PermissionException(Exception):
自定义权限异常PermissionException 自定义权限异常PermissionException
""" """
def __init__(self, data: str = None, message: str = None): def __init__(self, data: Optional[str] = None, message: Optional[str] = None) -> None:
self.data = data self.data = data
self.message = message self.message = message
@@ -33,7 +36,7 @@ class ServiceException(Exception):
自定义服务异常ServiceException 自定义服务异常ServiceException
""" """
def __init__(self, data: str = None, message: str = None): def __init__(self, data: Optional[str] = None, message: Optional[str] = None) -> None:
self.data = data self.data = data
self.message = message self.message = message
@@ -43,7 +46,7 @@ class ServiceWarning(Exception):
自定义服务警告ServiceWarning 自定义服务警告ServiceWarning
""" """
def __init__(self, data: str = None, message: str = None): def __init__(self, data: Optional[str] = None, message: Optional[str] = None) -> None:
self.data = data self.data = data
self.message = message self.message = message
@@ -53,6 +56,6 @@ class ModelValidatorException(Exception):
自定义模型校验异常ModelValidatorException 自定义模型校验异常ModelValidatorException
""" """
def __init__(self, data: str = None, message: str = None): def __init__(self, data: Optional[str] = None, message: Optional[str] = None) -> None:
self.data = data self.data = data
self.message = message self.message = message
+13 -12
View File
@@ -1,6 +1,7 @@
from fastapi import FastAPI, Request from fastapi import FastAPI, Request, Response
from fastapi.exceptions import HTTPException from fastapi.exceptions import HTTPException
from pydantic_validation_decorator import FieldValidationError from pydantic_validation_decorator import FieldValidationError
from exceptions.exception import ( from exceptions.exception import (
AuthException, AuthException,
LoginException, LoginException,
@@ -10,62 +11,62 @@ from exceptions.exception import (
ServiceWarning, ServiceWarning,
) )
from utils.log_util import logger from utils.log_util import logger
from utils.response_util import jsonable_encoder, JSONResponse, ResponseUtil from utils.response_util import JSONResponse, ResponseUtil, jsonable_encoder
def handle_exception(app: FastAPI): def handle_exception(app: FastAPI) -> None:
""" """
全局异常处理 全局异常处理
""" """
# 自定义token检验异常 # 自定义token检验异常
@app.exception_handler(AuthException) @app.exception_handler(AuthException)
async def auth_exception_handler(request: Request, exc: AuthException): async def auth_exception_handler(request: Request, exc: AuthException) -> Response:
return ResponseUtil.unauthorized(data=exc.data, msg=exc.message) return ResponseUtil.unauthorized(data=exc.data, msg=exc.message)
# 自定义登录检验异常 # 自定义登录检验异常
@app.exception_handler(LoginException) @app.exception_handler(LoginException)
async def login_exception_handler(request: Request, exc: LoginException): async def login_exception_handler(request: Request, exc: LoginException) -> Response:
return ResponseUtil.failure(data=exc.data, msg=exc.message) return ResponseUtil.failure(data=exc.data, msg=exc.message)
# 自定义模型检验异常 # 自定义模型检验异常
@app.exception_handler(ModelValidatorException) @app.exception_handler(ModelValidatorException)
async def model_validator_exception_handler(request: Request, exc: ModelValidatorException): async def model_validator_exception_handler(request: Request, exc: ModelValidatorException) -> Response:
logger.warning(exc.message) logger.warning(exc.message)
return ResponseUtil.failure(data=exc.data, msg=exc.message) return ResponseUtil.failure(data=exc.data, msg=exc.message)
# 自定义字段检验异常 # 自定义字段检验异常
@app.exception_handler(FieldValidationError) @app.exception_handler(FieldValidationError)
async def field_validation_error_handler(request: Request, exc: FieldValidationError): async def field_validation_error_handler(request: Request, exc: FieldValidationError) -> Response:
logger.warning(exc.message) logger.warning(exc.message)
return ResponseUtil.failure(msg=exc.message) return ResponseUtil.failure(msg=exc.message)
# 自定义权限检验异常 # 自定义权限检验异常
@app.exception_handler(PermissionException) @app.exception_handler(PermissionException)
async def permission_exception_handler(request: Request, exc: PermissionException): async def permission_exception_handler(request: Request, exc: PermissionException) -> Response:
return ResponseUtil.forbidden(data=exc.data, msg=exc.message) return ResponseUtil.forbidden(data=exc.data, msg=exc.message)
# 自定义服务异常 # 自定义服务异常
@app.exception_handler(ServiceException) @app.exception_handler(ServiceException)
async def service_exception_handler(request: Request, exc: ServiceException): async def service_exception_handler(request: Request, exc: ServiceException) -> Response:
logger.error(exc.message) logger.error(exc.message)
return ResponseUtil.error(data=exc.data, msg=exc.message) return ResponseUtil.error(data=exc.data, msg=exc.message)
# 自定义服务警告 # 自定义服务警告
@app.exception_handler(ServiceWarning) @app.exception_handler(ServiceWarning)
async def service_warning_handler(request: Request, exc: ServiceWarning): async def service_warning_handler(request: Request, exc: ServiceWarning) -> Response:
logger.warning(exc.message) logger.warning(exc.message)
return ResponseUtil.failure(data=exc.data, msg=exc.message) return ResponseUtil.failure(data=exc.data, msg=exc.message)
# 处理其他http请求异常 # 处理其他http请求异常
@app.exception_handler(HTTPException) @app.exception_handler(HTTPException)
async def http_exception_handler(request: Request, exc: HTTPException): async def http_exception_handler(request: Request, exc: HTTPException) -> Response:
return JSONResponse( return JSONResponse(
content=jsonable_encoder({'code': exc.status_code, 'msg': exc.detail}), status_code=exc.status_code content=jsonable_encoder({'code': exc.status_code, 'msg': exc.detail}), status_code=exc.status_code
) )
# 处理其他异常 # 处理其他异常
@app.exception_handler(Exception) @app.exception_handler(Exception)
async def exception_handler(request: Request, exc: Exception): async def exception_handler(request: Request, exc: Exception) -> Response:
logger.exception(exc) logger.exception(exc)
return ResponseUtil.error(msg=str(exc)) return ResponseUtil.error(msg=str(exc))
@@ -0,0 +1,29 @@
from fastapi import FastAPI, Request
from starlette.middleware.base import BaseHTTPMiddleware, RequestResponseEndpoint
from starlette.responses import Response
from common.context import RequestContext
class ContextCleanupMiddleware(BaseHTTPMiddleware):
"""
上下文清理中间件
"""
async def dispatch(self, request: Request, call_next: RequestResponseEndpoint) -> Response:
"""
在每个请求处理完成后清理上下文信息
"""
response = await call_next(request)
# 请求处理完成后清理所有上下文变量
RequestContext.clear_all()
return response
def add_context_cleanup_middleware(app: FastAPI) -> None:
"""
添加上下文清理中间件
:param app: FastAPI对象
"""
app.add_middleware(ContextCleanupMiddleware)
@@ -2,7 +2,7 @@ from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware from fastapi.middleware.cors import CORSMiddleware
def add_cors_middleware(app: FastAPI): def add_cors_middleware(app: FastAPI) -> None:
""" """
添加跨域中间件 添加跨域中间件
@@ -2,7 +2,7 @@ from fastapi import FastAPI
from starlette.middleware.gzip import GZipMiddleware from starlette.middleware.gzip import GZipMiddleware
def add_gzip_middleware(app: FastAPI): def add_gzip_middleware(app: FastAPI) -> None:
""" """
添加gzip压缩中间件 添加gzip压缩中间件
+5 -1
View File
@@ -1,13 +1,17 @@
from fastapi import FastAPI from fastapi import FastAPI
from middlewares.context_middleware import add_context_cleanup_middleware
from middlewares.cors_middleware import add_cors_middleware from middlewares.cors_middleware import add_cors_middleware
from middlewares.gzip_middleware import add_gzip_middleware from middlewares.gzip_middleware import add_gzip_middleware
from middlewares.trace_middleware import add_trace_middleware from middlewares.trace_middleware import add_trace_middleware
def handle_middleware(app: FastAPI): def handle_middleware(app: FastAPI) -> None:
""" """
全局中间件处理 全局中间件处理
""" """
# 加载上下文清理中间件
add_context_cleanup_middleware(app)
# 加载跨域中间件 # 加载跨域中间件
add_cors_middleware(app) add_cors_middleware(app)
# 加载gzip压缩中间件 # 加载gzip压缩中间件
@@ -1,4 +1,5 @@
from fastapi import FastAPI from fastapi import FastAPI
from .ctx import TraceCtx from .ctx import TraceCtx
from .middle import TraceASGIMiddleware from .middle import TraceASGIMiddleware
@@ -7,7 +8,7 @@ __all__ = ('TraceASGIMiddleware', 'TraceCtx')
__version__ = '0.1.0' __version__ = '0.1.0'
def add_trace_middleware(app: FastAPI): def add_trace_middleware(app: FastAPI) -> None:
""" """
添加trace中间件 添加trace中间件
@@ -1,10 +1,3 @@
# -*- coding: utf-8 -*-
"""
@author: peng
@file: ctx.py
@time: 2025/1/17 16:57
"""
import contextvars import contextvars
from uuid import uuid4 from uuid import uuid4
@@ -13,11 +6,11 @@ CTX_REQUEST_ID: contextvars.ContextVar[str] = contextvars.ContextVar('request-id
class TraceCtx: class TraceCtx:
@staticmethod @staticmethod
def set_id(): def set_id() -> str:
_id = uuid4().hex _id = uuid4().hex
CTX_REQUEST_ID.set(_id) CTX_REQUEST_ID.set(_id)
return _id return _id
@staticmethod @staticmethod
def get_id(): def get_id() -> str:
return CTX_REQUEST_ID.get() return CTX_REQUEST_ID.get()
@@ -1,13 +1,8 @@
# -*- coding: utf-8 -*-
"""
@author: peng
@file: middle.py
@time: 2025/1/17 16:57
"""
from functools import wraps from functools import wraps
from starlette.types import ASGIApp, Message, Receive, Scope, Send from starlette.types import ASGIApp, Message, Receive, Scope, Send
from .span import get_current_span, Span
from .span import Span, get_current_span
class TraceASGIMiddleware: class TraceASGIMiddleware:
@@ -21,11 +16,11 @@ class TraceASGIMiddleware:
self.app = app self.app = app
@staticmethod @staticmethod
async def my_receive(receive: Receive, span: Span): async def my_receive(receive: Receive, span: Span) -> Receive:
await span.request_before() await span.request_before()
@wraps(receive) @wraps(receive)
async def my_receive(): async def my_receive() -> Message:
message = await receive() message = await receive()
await span.request_after(message) await span.request_after(message)
return message return message
@@ -1,12 +1,8 @@
# -*- coding: utf-8 -*- from collections.abc import AsyncGenerator
"""
@author: peng
@file: span.py
@time: 2025/1/17 16:57
"""
from contextlib import asynccontextmanager from contextlib import asynccontextmanager
from starlette.types import Scope, Message
from starlette.types import Message, Scope
from .ctx import TraceCtx from .ctx import TraceCtx
@@ -16,16 +12,16 @@ class Span:
request(before) --> request(after) --> response(before) --> response(after) request(before) --> request(after) --> response(before) --> response(after)
""" """
def __init__(self, scope: Scope): def __init__(self, scope: Scope) -> None:
self.scope = scope self.scope = scope
async def request_before(self): async def request_before(self) -> None:
""" """
request_before: 处理header信息等, 如记录请求体信息 request_before: 处理header信息等, 如记录请求体信息
""" """
TraceCtx.set_id() TraceCtx.set_id()
async def request_after(self, message: Message): async def request_after(self, message: Message) -> Message:
""" """
request_after: 处理请求bytes 如记录请求参数 request_after: 处理请求bytes 如记录请求参数
@@ -34,7 +30,7 @@ class Span:
""" """
return message return message
async def response(self, message: Message): async def response(self, message: Message) -> Message:
""" """
if message['type'] == "http.response.start": -----> request-before if message['type'] == "http.response.start": -----> request-before
pass pass
@@ -48,5 +44,5 @@ class Span:
@asynccontextmanager @asynccontextmanager
async def get_current_span(scope: Scope): async def get_current_span(scope: Scope) -> AsyncGenerator[Span, None]:
yield Span(scope) yield Span(scope)
@@ -1,263 +0,0 @@
import httpx
import inspect
import json
import os
import time
from async_lru import alru_cache
from datetime import datetime
from fastapi import Request
from fastapi.responses import JSONResponse, ORJSONResponse, UJSONResponse
from functools import wraps
from sqlalchemy.ext.asyncio import AsyncSession
from typing import Any, Callable, Literal, Optional
from user_agents import parse
from config.enums import BusinessType
from config.env import AppConfig
from exceptions.exception import LoginException, ServiceException, ServiceWarning
from module_admin.entity.vo.log_vo import LogininforModel, OperLogModel
from module_admin.service.log_service import LoginLogService, OperationLogService
from module_admin.service.login_service import LoginService
from utils.log_util import logger
from utils.response_util import ResponseUtil
class Log:
"""
日志装饰器
"""
def __init__(
self,
title: str,
business_type: BusinessType,
log_type: Optional[Literal['login', 'operation']] = 'operation',
):
"""
日志装饰器
:param title: 当前日志装饰器装饰的模块标题
:param business_type: 业务类型(OTHER其它 INSERT新增 UPDATE修改 DELETE删除 GRANT授权 EXPORT导出 IMPORT导入 FORCE强退 GENCODE生成代码 CLEAN清空数据)
:param log_type: 日志类型(login表示登录日志,operation表示为操作日志)
:return:
"""
self.title = title
self.business_type = business_type.value
self.log_type = log_type
def __call__(self, func):
@wraps(func)
async def wrapper(*args, **kwargs):
start_time = time.perf_counter()
# 获取被装饰函数的文件路径
file_path = inspect.getfile(func)
# 获取项目根路径
project_root = os.getcwd()
# 处理文件路径,去除项目根路径部分
relative_path = os.path.relpath(file_path, start=project_root)[0:-2].replace('\\', '.').replace('/', '.')
# 获取当前被装饰函数所在路径
func_path = f'{relative_path}{func.__name__}()'
# 获取上下文信息
request_name_list = get_function_parameters_name_by_type(func, Request)
request = get_function_parameters_value_by_name(func, request_name_list[0], *args, **kwargs)
token = request.headers.get('Authorization')
session_name_list = get_function_parameters_name_by_type(func, AsyncSession)
query_db = get_function_parameters_value_by_name(func, session_name_list[0], *args, **kwargs)
request_method = request.method
operator_type = 0
user_agent = request.headers.get('User-Agent')
if 'Windows' in user_agent or 'Macintosh' in user_agent or 'Linux' in user_agent:
operator_type = 1
if 'Mobile' in user_agent or 'Android' in user_agent or 'iPhone' in user_agent:
operator_type = 2
# 获取请求的url
oper_url = request.url.path
# 获取请求的ip及ip归属区域
oper_ip = request.headers.get('X-Forwarded-For')
oper_location = '内网IP'
if AppConfig.app_ip_location_query:
oper_location = await get_ip_location(oper_ip)
# 根据不同的请求类型使用不同的方法获取请求参数
content_type = request.headers.get('Content-Type')
if content_type and (
'multipart/form-data' in content_type or 'application/x-www-form-urlencoded' in content_type
):
payload = await request.form()
oper_param = '\n'.join([f'{key}: {value}' for key, value in payload.items()])
else:
payload = await request.body()
# 通过 request.path_params 直接访问路径参数
path_params = request.path_params
oper_param = {}
if payload:
oper_param.update(json.loads(str(payload, 'utf-8')))
if path_params:
oper_param.update(path_params)
oper_param = json.dumps(oper_param, ensure_ascii=False)
# 日志表请求参数字段长度最大为2000,因此在此处判断长度
if len(oper_param) > 2000:
oper_param = '请求参数过长'
# 获取操作时间
oper_time = datetime.now()
# 此处在登录之前向原始函数传递一些登录信息,用于监测在线用户的相关信息
login_log = {}
if self.log_type == 'login':
user_agent_info = parse(user_agent)
browser = f'{user_agent_info.browser.family}'
system_os = f'{user_agent_info.os.family}'
if user_agent_info.browser.version != ():
browser += f' {user_agent_info.browser.version[0]}'
if user_agent_info.os.version != ():
system_os += f' {user_agent_info.os.version[0]}'
login_log = dict(
ipaddr=oper_ip,
loginLocation=oper_location,
browser=browser,
os=system_os,
loginTime=oper_time.strftime('%Y-%m-%d %H:%M:%S'),
)
kwargs['form_data'].login_info = login_log
try:
# 调用原始函数
result = await func(*args, **kwargs)
except (LoginException, ServiceWarning) as e:
logger.warning(e.message)
result = ResponseUtil.failure(data=e.data, msg=e.message)
except ServiceException as e:
logger.error(e.message)
result = ResponseUtil.error(data=e.data, msg=e.message)
except Exception as e:
logger.exception(e)
result = ResponseUtil.error(msg=str(e))
# 获取请求耗时
cost_time = float(time.perf_counter() - start_time) * 1000
# 判断请求是否来自api文档
request_from_swagger = (
request.headers.get('referer').endswith('docs') if request.headers.get('referer') else False
)
request_from_redoc = (
request.headers.get('referer').endswith('redoc') if request.headers.get('referer') else False
)
# 根据响应结果的类型使用不同的方法获取响应结果参数
if (
isinstance(result, JSONResponse)
or isinstance(result, ORJSONResponse)
or isinstance(result, UJSONResponse)
):
result_dict = json.loads(str(result.body, 'utf-8'))
else:
if request_from_swagger or request_from_redoc:
result_dict = {}
else:
if result.status_code == 200:
result_dict = {'code': result.status_code, 'message': '获取成功'}
else:
result_dict = {'code': result.status_code, 'message': '获取失败'}
json_result = json.dumps(result_dict, ensure_ascii=False)
# 根据响应结果获取响应状态及异常信息
status = 1
error_msg = ''
if result_dict.get('code') == 200:
status = 0
else:
error_msg = result_dict.get('msg')
# 根据日志类型向对应的日志表插入数据
if self.log_type == 'login':
# 登录请求来自于api文档时不记录登录日志,其余情况则记录
if request_from_swagger or request_from_redoc:
pass
else:
user = kwargs.get('form_data')
user_name = user.username
login_log['loginTime'] = oper_time
login_log['userName'] = user_name
login_log['status'] = str(status)
login_log['msg'] = result_dict.get('msg')
await LoginLogService.add_login_log_services(query_db, LogininforModel(**login_log))
else:
current_user = await LoginService.get_current_user(request, token, query_db)
oper_name = current_user.user.user_name
dept_name = current_user.user.dept.dept_name if current_user.user.dept else None
operation_log = OperLogModel(
title=self.title,
businessType=self.business_type,
method=func_path,
requestMethod=request_method,
operatorType=operator_type,
operName=oper_name,
deptName=dept_name,
operUrl=oper_url,
operIp=oper_ip,
operLocation=oper_location,
operParam=oper_param,
jsonResult=json_result,
status=status,
errorMsg=error_msg,
operTime=oper_time,
costTime=int(cost_time),
)
await OperationLogService.add_operation_log_services(query_db, operation_log)
return result
return wrapper
@alru_cache()
async def get_ip_location(oper_ip: str):
"""
查询ip归属区域
:param oper_ip: 需要查询的ip
:return: ip归属区域
"""
oper_location = '内网IP'
try:
if oper_ip != '127.0.0.1' and oper_ip != 'localhost':
oper_location = '未知'
async with httpx.AsyncClient() as client:
ip_result = await client.get(f'https://qifu-api.baidubce.com/ip/geo/v1/district?ip={oper_ip}')
if ip_result.status_code == 200:
prov = ip_result.json().get('data', {}).get('prov')
city = ip_result.json().get('data', {}).get('city')
if prov or city:
oper_location = f'{prov}-{city}'
except Exception as e:
oper_location = '未知'
print(e)
return oper_location
def get_function_parameters_name_by_type(func: Callable, param_type: Any):
"""
获取函数指定类型的参数名称
:param func: 函数
:param arg_type: 参数类型
:return: 函数指定类型的参数名称
"""
# 获取函数的参数信息
parameters = inspect.signature(func).parameters
# 找到指定类型的参数名称
parameters_name_list = []
for name, param in parameters.items():
if param.annotation == param_type:
parameters_name_list.append(name)
return parameters_name_list
def get_function_parameters_value_by_name(func: Callable, name: str, *args, **kwargs):
"""
获取函数指定参数的值
:param func: 函数
:param name: 参数名
:return: 参数值
"""
# 获取参数值
bound_parameters = inspect.signature(func).bind(*args, **kwargs)
bound_parameters.apply_defaults()
parameters_value = bound_parameters.arguments.get(name)
return parameters_value
@@ -1,68 +0,0 @@
from fastapi import Depends
from typing import List, Union
from exceptions.exception import PermissionException
from module_admin.entity.vo.user_vo import CurrentUserModel
from module_admin.service.login_service import LoginService
class CheckUserInterfaceAuth:
"""
校验当前用户是否具有相应的接口权限
"""
def __init__(self, perm: Union[str, List], is_strict: bool = False):
"""
校验当前用户是否具有相应的接口权限
:param perm: 权限标识
:param is_strict: 当传入的权限标识是list类型时,是否开启严格模式,开启表示会校验列表中的每一个权限标识,所有的校验结果都需要为True才会通过
"""
self.perm = perm
self.is_strict = is_strict
def __call__(self, current_user: CurrentUserModel = Depends(LoginService.get_current_user)):
user_auth_list = current_user.permissions
if '*:*:*' in user_auth_list:
return True
if isinstance(self.perm, str):
if self.perm in user_auth_list:
return True
if isinstance(self.perm, list):
if self.is_strict:
if all([perm_str in user_auth_list for perm_str in self.perm]):
return True
else:
if any([perm_str in user_auth_list for perm_str in self.perm]):
return True
raise PermissionException(data='', message='该用户无此接口权限')
class CheckRoleInterfaceAuth:
"""
根据角色校验当前用户是否具有相应的接口权限
"""
def __init__(self, role_key: Union[str, List], is_strict: bool = False):
"""
根据角色校验当前用户是否具有相应的接口权限
:param role_key: 角色标识
:param is_strict: 当传入的角色标识是list类型时,是否开启严格模式,开启表示会校验列表中的每一个角色标识,所有的校验结果都需要为True才会通过
"""
self.role_key = role_key
self.is_strict = is_strict
def __call__(self, current_user: CurrentUserModel = Depends(LoginService.get_current_user)):
user_role_list = current_user.user.role
user_role_key_list = [role.role_key for role in user_role_list]
if isinstance(self.role_key, str):
if self.role_key in user_role_key_list:
return True
if isinstance(self.role_key, list):
if self.is_strict:
if all([role_key_str in user_role_key_list for role_key_str in self.role_key]):
return True
else:
if any([role_key_str in user_role_key_list for role_key_str in self.role_key]):
return True
raise PermissionException(data='', message='该用户无此接口权限')
@@ -1,20 +1,29 @@
from fastapi import APIRouter, Depends, Request from typing import Annotated
from typing import List
from module_admin.aspect.interface_auth import CheckUserInterfaceAuth from fastapi import Path, Request, Response
from common.aspect.interface_auth import UserInterfaceAuthDependency
from common.aspect.pre_auth import PreAuthDependency
from common.router import APIRouterPro
from common.vo import DataResponseModel, ResponseBaseModel
from module_admin.entity.vo.cache_vo import CacheInfoModel, CacheMonitorModel from module_admin.entity.vo.cache_vo import CacheInfoModel, CacheMonitorModel
from module_admin.service.cache_service import CacheService from module_admin.service.cache_service import CacheService
from module_admin.service.login_service import LoginService
from utils.log_util import logger from utils.log_util import logger
from utils.response_util import ResponseUtil from utils.response_util import ResponseUtil
cache_controller = APIRouterPro(
cacheController = APIRouter(prefix='/monitor/cache', dependencies=[Depends(LoginService.get_current_user)]) prefix='/monitor/cache', order_num=15, tags=['系统监控-缓存监控'], dependencies=[PreAuthDependency()]
@cacheController.get(
'', response_model=CacheMonitorModel, dependencies=[Depends(CheckUserInterfaceAuth('monitor:cache:list'))]
) )
async def get_monitor_cache_info(request: Request):
@cache_controller.get(
'',
summary='获取缓存监控信息接口',
description='用于获取缓存监控信息',
response_model=DataResponseModel[CacheMonitorModel],
dependencies=[UserInterfaceAuthDependency('monitor:cache:list')],
)
async def get_monitor_cache_info(request: Request) -> Response:
# 获取全量数据 # 获取全量数据
cache_info_query_result = await CacheService.get_cache_monitor_statistical_info_services(request) cache_info_query_result = await CacheService.get_cache_monitor_statistical_info_services(request)
logger.info('获取成功') logger.info('获取成功')
@@ -22,12 +31,14 @@ async def get_monitor_cache_info(request: Request):
return ResponseUtil.success(data=cache_info_query_result) return ResponseUtil.success(data=cache_info_query_result)
@cacheController.get( @cache_controller.get(
'/getNames', '/getNames',
response_model=List[CacheInfoModel], summary='获取缓存名称列表接口',
dependencies=[Depends(CheckUserInterfaceAuth('monitor:cache:list'))], description='用于获取缓存名称列表',
response_model=DataResponseModel[list[CacheInfoModel]],
dependencies=[UserInterfaceAuthDependency('monitor:cache:list')],
) )
async def get_monitor_cache_name(request: Request): async def get_monitor_cache_name(request: Request) -> Response:
# 获取全量数据 # 获取全量数据
cache_name_list_result = await CacheService.get_cache_monitor_cache_name_services() cache_name_list_result = await CacheService.get_cache_monitor_cache_name_services()
logger.info('获取成功') logger.info('获取成功')
@@ -35,12 +46,14 @@ async def get_monitor_cache_name(request: Request):
return ResponseUtil.success(data=cache_name_list_result) return ResponseUtil.success(data=cache_name_list_result)
@cacheController.get( @cache_controller.get(
'/getKeys/{cache_name}', '/getKeys/{cache_name}',
response_model=List[str], summary='获取缓存键列表接口',
dependencies=[Depends(CheckUserInterfaceAuth('monitor:cache:list'))], description='用于获取指定缓存名称下的所有缓存键列表',
response_model=DataResponseModel[list[str]],
dependencies=[UserInterfaceAuthDependency('monitor:cache:list')],
) )
async def get_monitor_cache_key(request: Request, cache_name: str): async def get_monitor_cache_key(request: Request, cache_name: Annotated[str, Path(description='缓存名称')]) -> Response:
# 获取全量数据 # 获取全量数据
cache_key_list_result = await CacheService.get_cache_monitor_cache_key_services(request, cache_name) cache_key_list_result = await CacheService.get_cache_monitor_cache_key_services(request, cache_name)
logger.info('获取成功') logger.info('获取成功')
@@ -48,12 +61,18 @@ async def get_monitor_cache_key(request: Request, cache_name: str):
return ResponseUtil.success(data=cache_key_list_result) return ResponseUtil.success(data=cache_key_list_result)
@cacheController.get( @cache_controller.get(
'/getValue/{cache_name}/{cache_key}', '/getValue/{cache_name}/{cache_key}',
response_model=CacheInfoModel, summary='获取缓存值接口',
dependencies=[Depends(CheckUserInterfaceAuth('monitor:cache:list'))], description='用于获取指定缓存名称下的指定缓存键对应的值',
response_model=DataResponseModel[CacheInfoModel],
dependencies=[UserInterfaceAuthDependency('monitor:cache:list')],
) )
async def get_monitor_cache_value(request: Request, cache_name: str, cache_key: str): async def get_monitor_cache_value(
request: Request,
cache_name: Annotated[str, Path(description='缓存名称')],
cache_key: Annotated[str, Path(description='缓存键')],
) -> Response:
# 获取全量数据 # 获取全量数据
cache_value_list_result = await CacheService.get_cache_monitor_cache_value_services(request, cache_name, cache_key) cache_value_list_result = await CacheService.get_cache_monitor_cache_value_services(request, cache_name, cache_key)
logger.info('获取成功') logger.info('获取成功')
@@ -61,28 +80,44 @@ async def get_monitor_cache_value(request: Request, cache_name: str, cache_key:
return ResponseUtil.success(data=cache_value_list_result) return ResponseUtil.success(data=cache_value_list_result)
@cacheController.delete( @cache_controller.delete(
'/clearCacheName/{cache_name}', dependencies=[Depends(CheckUserInterfaceAuth('monitor:cache:list'))] '/clearCacheName/{cache_name}',
summary='清除缓存名称接口',
description='用于清除指定缓存名称下的所有缓存键值对',
response_model=ResponseBaseModel,
dependencies=[UserInterfaceAuthDependency('monitor:cache:list')],
) )
async def clear_monitor_cache_name(request: Request, cache_name: str): async def clear_monitor_cache_name(
request: Request, cache_name: Annotated[str, Path(description='缓存名称')]
) -> Response:
clear_cache_name_result = await CacheService.clear_cache_monitor_cache_name_services(request, cache_name) clear_cache_name_result = await CacheService.clear_cache_monitor_cache_name_services(request, cache_name)
logger.info(clear_cache_name_result.message) logger.info(clear_cache_name_result.message)
return ResponseUtil.success(msg=clear_cache_name_result.message) return ResponseUtil.success(msg=clear_cache_name_result.message)
@cacheController.delete( @cache_controller.delete(
'/clearCacheKey/{cache_key}', dependencies=[Depends(CheckUserInterfaceAuth('monitor:cache:list'))] '/clearCacheKey/{cache_key}',
summary='清除缓存键接口',
description='用于清除指定缓存键对应的值',
response_model=ResponseBaseModel,
dependencies=[UserInterfaceAuthDependency('monitor:cache:list')],
) )
async def clear_monitor_cache_key(request: Request, cache_key: str): async def clear_monitor_cache_key(request: Request, cache_key: Annotated[str, Path(description='缓存键')]) -> Response:
clear_cache_key_result = await CacheService.clear_cache_monitor_cache_key_services(request, cache_key) clear_cache_key_result = await CacheService.clear_cache_monitor_cache_key_services(request, cache_key)
logger.info(clear_cache_key_result.message) logger.info(clear_cache_key_result.message)
return ResponseUtil.success(msg=clear_cache_key_result.message) return ResponseUtil.success(msg=clear_cache_key_result.message)
@cacheController.delete('/clearCacheAll', dependencies=[Depends(CheckUserInterfaceAuth('monitor:cache:list'))]) @cache_controller.delete(
async def clear_monitor_cache_all(request: Request): '/clearCacheAll',
summary='清除所有缓存接口',
description='用于清除所有缓存键值对',
response_model=ResponseBaseModel,
dependencies=[UserInterfaceAuthDependency('monitor:cache:list')],
)
async def clear_monitor_cache_all(request: Request) -> Response:
clear_cache_all_result = await CacheService.clear_cache_monitor_all_services(request) clear_cache_all_result = await CacheService.clear_cache_monitor_all_services(request)
logger.info(clear_cache_all_result.message) logger.info(clear_cache_all_result.message)
@@ -1,28 +1,31 @@
import uuid import uuid
from datetime import timedelta from datetime import timedelta
from fastapi import APIRouter, Request
from config.enums import RedisInitKeyConfig from fastapi import Request, Response
from common.enums import RedisInitKeyConfig
from common.router import APIRouterPro
from common.vo import DynamicResponseModel
from module_admin.entity.vo.login_vo import CaptchaCode from module_admin.entity.vo.login_vo import CaptchaCode
from module_admin.service.captcha_service import CaptchaService from module_admin.service.captcha_service import CaptchaService
from utils.response_util import ResponseUtil
from utils.log_util import logger from utils.log_util import logger
from utils.response_util import ResponseUtil
captcha_controller = APIRouterPro(order_num=2, tags=['验证码模块'])
captchaController = APIRouter() @captcha_controller.get(
'/captchaImage',
summary='获取图片验证码接口',
@captchaController.get('/captchaImage') description='用于获取图片验证码',
async def get_captcha_image(request: Request): response_model=DynamicResponseModel[CaptchaCode],
)
async def get_captcha_image(request: Request) -> Response:
captcha_enabled = ( captcha_enabled = (
True await request.app.state.redis.get(f'{RedisInitKeyConfig.SYS_CONFIG.key}:sys.account.captchaEnabled') == 'true'
if await request.app.state.redis.get(f'{RedisInitKeyConfig.SYS_CONFIG.key}:sys.account.captchaEnabled')
== 'true'
else False
) )
register_enabled = ( register_enabled = (
True await request.app.state.redis.get(f'{RedisInitKeyConfig.SYS_CONFIG.key}:sys.account.registerUser') == 'true'
if await request.app.state.redis.get(f'{RedisInitKeyConfig.SYS_CONFIG.key}:sys.account.registerUser') == 'true'
else False
) )
session_id = str(uuid.uuid4()) session_id = str(uuid.uuid4())
captcha_result = await CaptchaService.create_captcha_image_service() captcha_result = await CaptchaService.create_captcha_image_service()
@@ -1,35 +1,73 @@
from fastapi import APIRouter, BackgroundTasks, Depends, File, Query, Request, UploadFile from typing import Annotated
from fastapi import BackgroundTasks, File, Query, Request, Response, UploadFile
from fastapi.responses import StreamingResponse
from common.aspect.pre_auth import PreAuthDependency
from common.router import APIRouterPro
from common.vo import DynamicResponseModel
from module_admin.entity.vo.common_vo import UploadResponseModel
from module_admin.service.common_service import CommonService from module_admin.service.common_service import CommonService
from module_admin.service.login_service import LoginService
from utils.log_util import logger from utils.log_util import logger
from utils.response_util import ResponseUtil from utils.response_util import ResponseUtil
commonController = APIRouter(prefix='/common', dependencies=[Depends(LoginService.get_current_user)]) common_controller = APIRouterPro(prefix='/common', order_num=16, tags=['通用模块'], dependencies=[PreAuthDependency()])
@commonController.post('/upload') @common_controller.post(
async def common_upload(request: Request, file: UploadFile = File(...)): '/upload',
summary='通用文件上传接口',
description='用于上传文件',
response_model=DynamicResponseModel[UploadResponseModel],
)
async def common_upload(request: Request, file: Annotated[UploadFile, File(...)]) -> Response:
upload_result = await CommonService.upload_service(request, file) upload_result = await CommonService.upload_service(request, file)
logger.info('上传成功') logger.info('上传成功')
return ResponseUtil.success(model_content=upload_result.result) return ResponseUtil.success(model_content=upload_result.result)
@commonController.get('/download') @common_controller.get(
'/download',
summary='通用文件下载接口',
description='用于下载下载目录中的文件',
response_class=StreamingResponse,
responses={
200: {
'description': '流式返回文件',
'content': {
'application/octet-stream': {},
},
}
},
)
async def common_download( async def common_download(
request: Request, request: Request,
background_tasks: BackgroundTasks, background_tasks: BackgroundTasks,
file_name: str = Query(alias='fileName'), file_name: Annotated[str, Query(alias='fileName')],
delete: bool = Query(), delete: Annotated[bool, Query()],
): ) -> Response:
download_result = await CommonService.download_services(background_tasks, file_name, delete) download_result = await CommonService.download_services(background_tasks, file_name, delete)
logger.info(download_result.message) logger.info(download_result.message)
return ResponseUtil.streaming(data=download_result.result) return ResponseUtil.streaming(data=download_result.result)
@commonController.get('/download/resource') @common_controller.get(
async def common_download_resource(request: Request, resource: str = Query()): '/download/resource',
summary='通用资源文件下载接口',
description='用于下载上传目录中的资源文件',
response_class=StreamingResponse,
responses={
200: {
'description': '流式返回文件',
'content': {
'application/octet-stream': {},
},
}
},
)
async def common_download_resource(request: Request, resource: Annotated[str, Query()]) -> Response:
download_resource_result = await CommonService.download_resource_services(resource) download_resource_result = await CommonService.download_resource_services(resource)
logger.info(download_resource_result.message) logger.info(download_resource_result.message)
@@ -1,32 +1,42 @@
from datetime import datetime from datetime import datetime
from fastapi import APIRouter, Depends, Form, Request from typing import Annotated
from fastapi import Form, Path, Query, Request, Response
from fastapi.responses import StreamingResponse
from pydantic_validation_decorator import ValidateFields from pydantic_validation_decorator import ValidateFields
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from config.enums import BusinessType
from config.get_db import get_db from common.annotation.log_annotation import Log
from module_admin.annotation.log_annotation import Log from common.aspect.db_seesion import DBSessionDependency
from module_admin.aspect.interface_auth import CheckUserInterfaceAuth from common.aspect.interface_auth import UserInterfaceAuthDependency
from common.aspect.pre_auth import CurrentUserDependency, PreAuthDependency
from common.enums import BusinessType
from common.router import APIRouterPro
from common.vo import DataResponseModel, PageResponseModel, ResponseBaseModel
from module_admin.entity.vo.config_vo import ConfigModel, ConfigPageQueryModel, DeleteConfigModel from module_admin.entity.vo.config_vo import ConfigModel, ConfigPageQueryModel, DeleteConfigModel
from module_admin.entity.vo.user_vo import CurrentUserModel from module_admin.entity.vo.user_vo import CurrentUserModel
from module_admin.service.config_service import ConfigService from module_admin.service.config_service import ConfigService
from module_admin.service.login_service import LoginService
from utils.common_util import bytes2file_response from utils.common_util import bytes2file_response
from utils.log_util import logger from utils.log_util import logger
from utils.page_util import PageResponseModel
from utils.response_util import ResponseUtil from utils.response_util import ResponseUtil
config_controller = APIRouterPro(
configController = APIRouter(prefix='/system/config', dependencies=[Depends(LoginService.get_current_user)]) prefix='/system/config', order_num=9, tags=['系统管理-参数管理'], dependencies=[PreAuthDependency()]
)
@configController.get( @config_controller.get(
'/list', response_model=PageResponseModel, dependencies=[Depends(CheckUserInterfaceAuth('system:config:list'))] '/list',
summary='获取参数分页列表接口',
description='用于获取参数分页列表',
response_model=PageResponseModel[ConfigModel],
dependencies=[UserInterfaceAuthDependency('system:config:list')],
) )
async def get_system_config_list( async def get_system_config_list(
request: Request, request: Request,
config_page_query: ConfigPageQueryModel = Depends(ConfigPageQueryModel.as_query), config_page_query: Annotated[ConfigPageQueryModel, Query()],
query_db: AsyncSession = Depends(get_db), query_db: Annotated[AsyncSession, DBSessionDependency()],
): ) -> Response:
# 获取分页数据 # 获取分页数据
config_page_query_result = await ConfigService.get_config_list_services(query_db, config_page_query, is_page=True) config_page_query_result = await ConfigService.get_config_list_services(query_db, config_page_query, is_page=True)
logger.info('获取成功') logger.info('获取成功')
@@ -34,15 +44,21 @@ async def get_system_config_list(
return ResponseUtil.success(model_content=config_page_query_result) return ResponseUtil.success(model_content=config_page_query_result)
@configController.post('', dependencies=[Depends(CheckUserInterfaceAuth('system:config:add'))]) @config_controller.post(
'',
summary='新增参数接口',
description='用于新增参数',
response_model=ResponseBaseModel,
dependencies=[UserInterfaceAuthDependency('system:config:add')],
)
@ValidateFields(validate_model='add_config') @ValidateFields(validate_model='add_config')
@Log(title='参数管理', business_type=BusinessType.INSERT) @Log(title='参数管理', business_type=BusinessType.INSERT)
async def add_system_config( async def add_system_config(
request: Request, request: Request,
add_config: ConfigModel, add_config: ConfigModel,
query_db: AsyncSession = Depends(get_db), query_db: Annotated[AsyncSession, DBSessionDependency()],
current_user: CurrentUserModel = Depends(LoginService.get_current_user), current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
): ) -> Response:
add_config.create_by = current_user.user.user_name add_config.create_by = current_user.user.user_name
add_config.create_time = datetime.now() add_config.create_time = datetime.now()
add_config.update_by = current_user.user.user_name add_config.update_by = current_user.user.user_name
@@ -53,15 +69,21 @@ async def add_system_config(
return ResponseUtil.success(msg=add_config_result.message) return ResponseUtil.success(msg=add_config_result.message)
@configController.put('', dependencies=[Depends(CheckUserInterfaceAuth('system:config:edit'))]) @config_controller.put(
'',
summary='编辑参数接口',
description='用于编辑参数',
response_model=ResponseBaseModel,
dependencies=[UserInterfaceAuthDependency('system:config:edit')],
)
@ValidateFields(validate_model='edit_config') @ValidateFields(validate_model='edit_config')
@Log(title='参数管理', business_type=BusinessType.UPDATE) @Log(title='参数管理', business_type=BusinessType.UPDATE)
async def edit_system_config( async def edit_system_config(
request: Request, request: Request,
edit_config: ConfigModel, edit_config: ConfigModel,
query_db: AsyncSession = Depends(get_db), query_db: Annotated[AsyncSession, DBSessionDependency()],
current_user: CurrentUserModel = Depends(LoginService.get_current_user), current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
): ) -> Response:
edit_config.update_by = current_user.user.user_name edit_config.update_by = current_user.user.user_name
edit_config.update_time = datetime.now() edit_config.update_time = datetime.now()
edit_config_result = await ConfigService.edit_config_services(request, query_db, edit_config) edit_config_result = await ConfigService.edit_config_services(request, query_db, edit_config)
@@ -70,18 +92,37 @@ async def edit_system_config(
return ResponseUtil.success(msg=edit_config_result.message) return ResponseUtil.success(msg=edit_config_result.message)
@configController.delete('/refreshCache', dependencies=[Depends(CheckUserInterfaceAuth('system:config:remove'))]) @config_controller.delete(
'/refreshCache',
summary='刷新参数缓存接口',
description='用于刷新参数缓存',
response_model=ResponseBaseModel,
dependencies=[UserInterfaceAuthDependency('system:config:remove')],
)
@Log(title='参数管理', business_type=BusinessType.UPDATE) @Log(title='参数管理', business_type=BusinessType.UPDATE)
async def refresh_system_config(request: Request, query_db: AsyncSession = Depends(get_db)): async def refresh_system_config(
request: Request,
query_db: Annotated[AsyncSession, DBSessionDependency()],
) -> Response:
refresh_config_result = await ConfigService.refresh_sys_config_services(request, query_db) refresh_config_result = await ConfigService.refresh_sys_config_services(request, query_db)
logger.info(refresh_config_result.message) logger.info(refresh_config_result.message)
return ResponseUtil.success(msg=refresh_config_result.message) return ResponseUtil.success(msg=refresh_config_result.message)
@configController.delete('/{config_ids}', dependencies=[Depends(CheckUserInterfaceAuth('system:config:remove'))]) @config_controller.delete(
'/{config_ids}',
summary='删除参数接口',
description='用于删除参数',
response_model=ResponseBaseModel,
dependencies=[UserInterfaceAuthDependency('system:config:remove')],
)
@Log(title='参数管理', business_type=BusinessType.DELETE) @Log(title='参数管理', business_type=BusinessType.DELETE)
async def delete_system_config(request: Request, config_ids: str, query_db: AsyncSession = Depends(get_db)): async def delete_system_config(
request: Request,
config_ids: Annotated[str, Path(description='需要删除的参数主键')],
query_db: Annotated[AsyncSession, DBSessionDependency()],
) -> Response:
delete_config = DeleteConfigModel(configIds=config_ids) delete_config = DeleteConfigModel(configIds=config_ids)
delete_config_result = await ConfigService.delete_config_services(request, query_db, delete_config) delete_config_result = await ConfigService.delete_config_services(request, query_db, delete_config)
logger.info(delete_config_result.message) logger.info(delete_config_result.message)
@@ -89,18 +130,31 @@ async def delete_system_config(request: Request, config_ids: str, query_db: Asyn
return ResponseUtil.success(msg=delete_config_result.message) return ResponseUtil.success(msg=delete_config_result.message)
@configController.get( @config_controller.get(
'/{config_id}', response_model=ConfigModel, dependencies=[Depends(CheckUserInterfaceAuth('system:config:query'))] '/{config_id}',
summary='获取参数详情接口',
description='用于获取指定参数的详细信息',
response_model=DataResponseModel[ConfigModel],
dependencies=[UserInterfaceAuthDependency('system:config:query')],
) )
async def query_detail_system_config(request: Request, config_id: int, query_db: AsyncSession = Depends(get_db)): async def query_detail_system_config(
request: Request,
config_id: Annotated[int, Path(description='参数主键')],
query_db: Annotated[AsyncSession, DBSessionDependency()],
) -> Response:
config_detail_result = await ConfigService.config_detail_services(query_db, config_id) config_detail_result = await ConfigService.config_detail_services(query_db, config_id)
logger.info(f'获取config_id为{config_id}的信息成功') logger.info(f'获取config_id为{config_id}的信息成功')
return ResponseUtil.success(data=config_detail_result) return ResponseUtil.success(data=config_detail_result)
@configController.get('/configKey/{config_key}') @config_controller.get(
async def query_system_config(request: Request, config_key: str): '/configKey/{config_key}',
summary='根据参数键查询参数值接口',
description='用于根据参数键从缓存中查询参数值',
response_model=ResponseBaseModel,
)
async def query_system_config(request: Request, config_key: str) -> Response:
# 获取全量数据 # 获取全量数据
config_query_result = await ConfigService.query_config_list_from_cache_services(request.app.state.redis, config_key) config_query_result = await ConfigService.query_config_list_from_cache_services(request.app.state.redis, config_key)
logger.info('获取成功') logger.info('获取成功')
@@ -108,13 +162,27 @@ async def query_system_config(request: Request, config_key: str):
return ResponseUtil.success(msg=config_query_result) return ResponseUtil.success(msg=config_query_result)
@configController.post('/export', dependencies=[Depends(CheckUserInterfaceAuth('system:config:export'))]) @config_controller.post(
'/export',
summary='导出参数列表接口',
description='用于导出当前符合查询条件的参数列表数据',
response_class=StreamingResponse,
responses={
200: {
'description': '流式返回参数列表excel文件',
'content': {
'application/octet-stream': {},
},
}
},
dependencies=[UserInterfaceAuthDependency('system:config:export')],
)
@Log(title='参数管理', business_type=BusinessType.EXPORT) @Log(title='参数管理', business_type=BusinessType.EXPORT)
async def export_system_config_list( async def export_system_config_list(
request: Request, request: Request,
config_page_query: ConfigPageQueryModel = Form(), config_page_query: Annotated[ConfigPageQueryModel, Form()],
query_db: AsyncSession = Depends(get_db), query_db: Annotated[AsyncSession, DBSessionDependency()],
): ) -> Response:
# 获取全量数据 # 获取全量数据
config_query_result = await ConfigService.get_config_list_services(query_db, config_page_query, is_page=False) config_query_result = await ConfigService.get_config_list_services(query_db, config_page_query, is_page=False)
config_export_result = await ConfigService.export_config_list_services(config_query_result) config_export_result = await ConfigService.export_config_list_services(config_query_result)
@@ -1,35 +1,42 @@
from datetime import datetime from datetime import datetime
from fastapi import APIRouter, Depends, Request from typing import Annotated
from fastapi import Path, Query, Request, Response
from pydantic_validation_decorator import ValidateFields from pydantic_validation_decorator import ValidateFields
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from typing import List
from config.enums import BusinessType from common.annotation.log_annotation import Log
from config.get_db import get_db from common.aspect.data_scope import DataScopeDependency
from module_admin.annotation.log_annotation import Log from common.aspect.db_seesion import DBSessionDependency
from module_admin.aspect.data_scope import GetDataScope from common.aspect.interface_auth import UserInterfaceAuthDependency
from module_admin.aspect.interface_auth import CheckUserInterfaceAuth from common.aspect.pre_auth import CurrentUserDependency, PreAuthDependency
from common.enums import BusinessType
from common.router import APIRouterPro
from common.vo import DataResponseModel, ResponseBaseModel
from module_admin.entity.vo.dept_vo import DeleteDeptModel, DeptModel, DeptQueryModel from module_admin.entity.vo.dept_vo import DeleteDeptModel, DeptModel, DeptQueryModel
from module_admin.entity.vo.user_vo import CurrentUserModel from module_admin.entity.vo.user_vo import CurrentUserModel
from module_admin.service.dept_service import DeptService from module_admin.service.dept_service import DeptService
from module_admin.service.login_service import LoginService
from utils.log_util import logger from utils.log_util import logger
from utils.response_util import ResponseUtil from utils.response_util import ResponseUtil
dept_controller = APIRouterPro(
deptController = APIRouter(prefix='/system/dept', dependencies=[Depends(LoginService.get_current_user)]) prefix='/system/dept', order_num=6, tags=['系统管理-部门管理'], dependencies=[PreAuthDependency()]
)
@deptController.get( @dept_controller.get(
'/list/exclude/{dept_id}', '/list/exclude/{dept_id}',
response_model=List[DeptModel], summary='获取编辑部门的下拉树接口',
dependencies=[Depends(CheckUserInterfaceAuth('system:dept:list'))], description='用于获取部门下拉树,不包含指定部门及其子部门',
response_model=DataResponseModel[list[DeptModel]],
dependencies=[UserInterfaceAuthDependency('system:dept:list')],
) )
async def get_system_dept_tree_for_edit_option( async def get_system_dept_tree_for_edit_option(
request: Request, request: Request,
dept_id: int, dept_id: Annotated[int, Path(description='部门id')],
query_db: AsyncSession = Depends(get_db), query_db: Annotated[AsyncSession, DBSessionDependency()],
data_scope_sql: str = Depends(GetDataScope('SysDept')), data_scope_sql: Annotated[str, DataScopeDependency('SysDept')],
): ) -> Response:
dept_query = DeptModel(deptId=dept_id) dept_query = DeptModel(deptId=dept_id)
dept_query_result = await DeptService.get_dept_for_edit_option_services(query_db, dept_query, data_scope_sql) dept_query_result = await DeptService.get_dept_for_edit_option_services(query_db, dept_query, data_scope_sql)
logger.info('获取成功') logger.info('获取成功')
@@ -37,30 +44,40 @@ async def get_system_dept_tree_for_edit_option(
return ResponseUtil.success(data=dept_query_result) return ResponseUtil.success(data=dept_query_result)
@deptController.get( @dept_controller.get(
'/list', response_model=List[DeptModel], dependencies=[Depends(CheckUserInterfaceAuth('system:dept:list'))] '/list',
summary='获取部门列表接口',
description='用于获取部门列表',
response_model=DataResponseModel[list[DeptModel]],
dependencies=[UserInterfaceAuthDependency('system:dept:list')],
) )
async def get_system_dept_list( async def get_system_dept_list(
request: Request, request: Request,
dept_query: DeptQueryModel = Depends(DeptQueryModel.as_query), dept_query: Annotated[DeptQueryModel, Query()],
query_db: AsyncSession = Depends(get_db), query_db: Annotated[AsyncSession, DBSessionDependency()],
data_scope_sql: str = Depends(GetDataScope('SysDept')), data_scope_sql: Annotated[str, DataScopeDependency('SysDept')],
): ) -> Response:
dept_query_result = await DeptService.get_dept_list_services(query_db, dept_query, data_scope_sql) dept_query_result = await DeptService.get_dept_list_services(query_db, dept_query, data_scope_sql)
logger.info('获取成功') logger.info('获取成功')
return ResponseUtil.success(data=dept_query_result) return ResponseUtil.success(data=dept_query_result)
@deptController.post('', dependencies=[Depends(CheckUserInterfaceAuth('system:dept:add'))]) @dept_controller.post(
'',
summary='新增部门接口',
description='用于新增部门',
response_model=ResponseBaseModel,
dependencies=[UserInterfaceAuthDependency('system:dept:add')],
)
@ValidateFields(validate_model='add_dept') @ValidateFields(validate_model='add_dept')
@Log(title='部门管理', business_type=BusinessType.INSERT) @Log(title='部门管理', business_type=BusinessType.INSERT)
async def add_system_dept( async def add_system_dept(
request: Request, request: Request,
add_dept: DeptModel, add_dept: DeptModel,
query_db: AsyncSession = Depends(get_db), query_db: Annotated[AsyncSession, DBSessionDependency()],
current_user: CurrentUserModel = Depends(LoginService.get_current_user), current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
): ) -> Response:
add_dept.create_by = current_user.user.user_name add_dept.create_by = current_user.user.user_name
add_dept.create_time = datetime.now() add_dept.create_time = datetime.now()
add_dept.update_by = current_user.user.user_name add_dept.update_by = current_user.user.user_name
@@ -68,19 +85,25 @@ async def add_system_dept(
add_dept_result = await DeptService.add_dept_services(query_db, add_dept) add_dept_result = await DeptService.add_dept_services(query_db, add_dept)
logger.info(add_dept_result.message) logger.info(add_dept_result.message)
return ResponseUtil.success(data=add_dept_result) return ResponseUtil.success(msg=add_dept_result.message)
@deptController.put('', dependencies=[Depends(CheckUserInterfaceAuth('system:dept:edit'))]) @dept_controller.put(
'',
summary='编辑部门接口',
description='用于编辑部门',
response_model=ResponseBaseModel,
dependencies=[UserInterfaceAuthDependency('system:dept:edit')],
)
@ValidateFields(validate_model='edit_dept') @ValidateFields(validate_model='edit_dept')
@Log(title='部门管理', business_type=BusinessType.UPDATE) @Log(title='部门管理', business_type=BusinessType.UPDATE)
async def edit_system_dept( async def edit_system_dept(
request: Request, request: Request,
edit_dept: DeptModel, edit_dept: DeptModel,
query_db: AsyncSession = Depends(get_db), query_db: Annotated[AsyncSession, DBSessionDependency()],
current_user: CurrentUserModel = Depends(LoginService.get_current_user), current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
data_scope_sql: str = Depends(GetDataScope('SysDept')), data_scope_sql: Annotated[str, DataScopeDependency('SysDept')],
): ) -> Response:
if not current_user.user.admin: if not current_user.user.admin:
await DeptService.check_dept_data_scope_services(query_db, edit_dept.dept_id, data_scope_sql) await DeptService.check_dept_data_scope_services(query_db, edit_dept.dept_id, data_scope_sql)
edit_dept.update_by = current_user.user.user_name edit_dept.update_by = current_user.user.user_name
@@ -91,15 +114,21 @@ async def edit_system_dept(
return ResponseUtil.success(msg=edit_dept_result.message) return ResponseUtil.success(msg=edit_dept_result.message)
@deptController.delete('/{dept_ids}', dependencies=[Depends(CheckUserInterfaceAuth('system:dept:remove'))]) @dept_controller.delete(
'/{dept_ids}',
summary='删除部门接口',
description='用于删除部门',
response_model=ResponseBaseModel,
dependencies=[UserInterfaceAuthDependency('system:dept:remove')],
)
@Log(title='部门管理', business_type=BusinessType.DELETE) @Log(title='部门管理', business_type=BusinessType.DELETE)
async def delete_system_dept( async def delete_system_dept(
request: Request, request: Request,
dept_ids: str, dept_ids: Annotated[str, Path(description='需要删除的部门id')],
query_db: AsyncSession = Depends(get_db), query_db: Annotated[AsyncSession, DBSessionDependency()],
current_user: CurrentUserModel = Depends(LoginService.get_current_user), current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
data_scope_sql: str = Depends(GetDataScope('SysDept')), data_scope_sql: Annotated[str, DataScopeDependency('SysDept')],
): ) -> Response:
dept_id_list = dept_ids.split(',') if dept_ids else [] dept_id_list = dept_ids.split(',') if dept_ids else []
if dept_id_list: if dept_id_list:
for dept_id in dept_id_list: for dept_id in dept_id_list:
@@ -114,16 +143,20 @@ async def delete_system_dept(
return ResponseUtil.success(msg=delete_dept_result.message) return ResponseUtil.success(msg=delete_dept_result.message)
@deptController.get( @dept_controller.get(
'/{dept_id}', response_model=DeptModel, dependencies=[Depends(CheckUserInterfaceAuth('system:dept:query'))] '/{dept_id}',
summary='获取部门详情接口',
description='用于获取指定部门的详情信息',
response_model=DataResponseModel[DeptModel],
dependencies=[UserInterfaceAuthDependency('system:dept:query')],
) )
async def query_detail_system_dept( async def query_detail_system_dept(
request: Request, request: Request,
dept_id: int, dept_id: Annotated[int, Path(description='部门id')],
query_db: AsyncSession = Depends(get_db), query_db: Annotated[AsyncSession, DBSessionDependency()],
current_user: CurrentUserModel = Depends(LoginService.get_current_user), current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
data_scope_sql: str = Depends(GetDataScope('SysDept')), data_scope_sql: Annotated[str, DataScopeDependency('SysDept')],
): ) -> Response:
if not current_user.user.admin: if not current_user.user.admin:
await DeptService.check_dept_data_scope_services(query_db, dept_id, data_scope_sql) await DeptService.check_dept_data_scope_services(query_db, dept_id, data_scope_sql)
detail_dept_result = await DeptService.dept_detail_services(query_db, dept_id) detail_dept_result = await DeptService.dept_detail_services(query_db, dept_id)
@@ -1,12 +1,18 @@
from datetime import datetime from datetime import datetime
from fastapi import APIRouter, Depends, Form, Request from typing import Annotated
from fastapi import Form, Path, Query, Request, Response
from fastapi.responses import StreamingResponse
from pydantic_validation_decorator import ValidateFields from pydantic_validation_decorator import ValidateFields
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from typing import List
from config.enums import BusinessType from common.annotation.log_annotation import Log
from config.get_db import get_db from common.aspect.db_seesion import DBSessionDependency
from module_admin.annotation.log_annotation import Log from common.aspect.interface_auth import UserInterfaceAuthDependency
from module_admin.aspect.interface_auth import CheckUserInterfaceAuth from common.aspect.pre_auth import CurrentUserDependency, PreAuthDependency
from common.enums import BusinessType
from common.router import APIRouterPro
from common.vo import DataResponseModel, PageResponseModel, ResponseBaseModel
from module_admin.entity.vo.dict_vo import ( from module_admin.entity.vo.dict_vo import (
DeleteDictDataModel, DeleteDictDataModel,
DeleteDictTypeModel, DeleteDictTypeModel,
@@ -17,24 +23,27 @@ from module_admin.entity.vo.dict_vo import (
) )
from module_admin.entity.vo.user_vo import CurrentUserModel from module_admin.entity.vo.user_vo import CurrentUserModel
from module_admin.service.dict_service import DictDataService, DictTypeService from module_admin.service.dict_service import DictDataService, DictTypeService
from module_admin.service.login_service import LoginService
from utils.common_util import bytes2file_response from utils.common_util import bytes2file_response
from utils.log_util import logger from utils.log_util import logger
from utils.page_util import PageResponseModel
from utils.response_util import ResponseUtil from utils.response_util import ResponseUtil
dict_controller = APIRouterPro(
dictController = APIRouter(prefix='/system/dict', dependencies=[Depends(LoginService.get_current_user)]) prefix='/system/dict', order_num=8, tags=['系统管理-字典管理'], dependencies=[PreAuthDependency()]
)
@dictController.get( @dict_controller.get(
'/type/list', response_model=PageResponseModel, dependencies=[Depends(CheckUserInterfaceAuth('system:dict:list'))] '/type/list',
summary='获取字典类型分页列表接口',
description='用于获取字典类型分页列表',
response_model=PageResponseModel[DictTypeModel],
dependencies=[UserInterfaceAuthDependency('system:dict:list')],
) )
async def get_system_dict_type_list( async def get_system_dict_type_list(
request: Request, request: Request,
dict_type_page_query: DictTypePageQueryModel = Depends(DictTypePageQueryModel.as_query), dict_type_page_query: Annotated[DictTypePageQueryModel, Query()],
query_db: AsyncSession = Depends(get_db), query_db: Annotated[AsyncSession, DBSessionDependency()],
): ) -> Response:
# 获取分页数据 # 获取分页数据
dict_type_page_query_result = await DictTypeService.get_dict_type_list_services( dict_type_page_query_result = await DictTypeService.get_dict_type_list_services(
query_db, dict_type_page_query, is_page=True query_db, dict_type_page_query, is_page=True
@@ -44,15 +53,21 @@ async def get_system_dict_type_list(
return ResponseUtil.success(model_content=dict_type_page_query_result) return ResponseUtil.success(model_content=dict_type_page_query_result)
@dictController.post('/type', dependencies=[Depends(CheckUserInterfaceAuth('system:dict:add'))]) @dict_controller.post(
'/type',
summary='新增字典类型接口',
description='用于新增字典类型',
response_model=ResponseBaseModel,
dependencies=[UserInterfaceAuthDependency('system:dict:add')],
)
@ValidateFields(validate_model='add_dict_type') @ValidateFields(validate_model='add_dict_type')
@Log(title='字典类型', business_type=BusinessType.INSERT) @Log(title='字典类型', business_type=BusinessType.INSERT)
async def add_system_dict_type( async def add_system_dict_type(
request: Request, request: Request,
add_dict_type: DictTypeModel, add_dict_type: DictTypeModel,
query_db: AsyncSession = Depends(get_db), query_db: Annotated[AsyncSession, DBSessionDependency()],
current_user: CurrentUserModel = Depends(LoginService.get_current_user), current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
): ) -> Response:
add_dict_type.create_by = current_user.user.user_name add_dict_type.create_by = current_user.user.user_name
add_dict_type.create_time = datetime.now() add_dict_type.create_time = datetime.now()
add_dict_type.update_by = current_user.user.user_name add_dict_type.update_by = current_user.user.user_name
@@ -63,15 +78,21 @@ async def add_system_dict_type(
return ResponseUtil.success(msg=add_dict_type_result.message) return ResponseUtil.success(msg=add_dict_type_result.message)
@dictController.put('/type', dependencies=[Depends(CheckUserInterfaceAuth('system:dict:edit'))]) @dict_controller.put(
'/type',
summary='编辑字典类型接口',
description='用于编辑字典类型',
response_model=ResponseBaseModel,
dependencies=[UserInterfaceAuthDependency('system:dict:edit')],
)
@ValidateFields(validate_model='edit_dict_type') @ValidateFields(validate_model='edit_dict_type')
@Log(title='字典类型', business_type=BusinessType.UPDATE) @Log(title='字典类型', business_type=BusinessType.UPDATE)
async def edit_system_dict_type( async def edit_system_dict_type(
request: Request, request: Request,
edit_dict_type: DictTypeModel, edit_dict_type: DictTypeModel,
query_db: AsyncSession = Depends(get_db), query_db: Annotated[AsyncSession, DBSessionDependency()],
current_user: CurrentUserModel = Depends(LoginService.get_current_user), current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
): ) -> Response:
edit_dict_type.update_by = current_user.user.user_name edit_dict_type.update_by = current_user.user.user_name
edit_dict_type.update_time = datetime.now() edit_dict_type.update_time = datetime.now()
edit_dict_type_result = await DictTypeService.edit_dict_type_services(request, query_db, edit_dict_type) edit_dict_type_result = await DictTypeService.edit_dict_type_services(request, query_db, edit_dict_type)
@@ -80,18 +101,34 @@ async def edit_system_dict_type(
return ResponseUtil.success(msg=edit_dict_type_result.message) return ResponseUtil.success(msg=edit_dict_type_result.message)
@dictController.delete('/type/refreshCache', dependencies=[Depends(CheckUserInterfaceAuth('system:dict:remove'))]) @dict_controller.delete(
'/type/refreshCache',
summary='刷新字典缓存接口',
description='用于刷新字典缓存',
response_model=ResponseBaseModel,
dependencies=[UserInterfaceAuthDependency('system:dict:remove')],
)
@Log(title='字典类型', business_type=BusinessType.UPDATE) @Log(title='字典类型', business_type=BusinessType.UPDATE)
async def refresh_system_dict(request: Request, query_db: AsyncSession = Depends(get_db)): async def refresh_system_dict(request: Request, query_db: Annotated[AsyncSession, DBSessionDependency()]) -> Response:
refresh_dict_result = await DictTypeService.refresh_sys_dict_services(request, query_db) refresh_dict_result = await DictTypeService.refresh_sys_dict_services(request, query_db)
logger.info(refresh_dict_result.message) logger.info(refresh_dict_result.message)
return ResponseUtil.success(msg=refresh_dict_result.message) return ResponseUtil.success(msg=refresh_dict_result.message)
@dictController.delete('/type/{dict_ids}', dependencies=[Depends(CheckUserInterfaceAuth('system:dict:remove'))]) @dict_controller.delete(
'/type/{dict_ids}',
summary='删除字典类型接口',
description='用于删除字典类型',
response_model=ResponseBaseModel,
dependencies=[UserInterfaceAuthDependency('system:dict:remove')],
)
@Log(title='字典类型', business_type=BusinessType.DELETE) @Log(title='字典类型', business_type=BusinessType.DELETE)
async def delete_system_dict_type(request: Request, dict_ids: str, query_db: AsyncSession = Depends(get_db)): async def delete_system_dict_type(
request: Request,
dict_ids: Annotated[str, Path(description='需要删除的字典主键')],
query_db: Annotated[AsyncSession, DBSessionDependency()],
) -> Response:
delete_dict_type = DeleteDictTypeModel(dictIds=dict_ids) delete_dict_type = DeleteDictTypeModel(dictIds=dict_ids)
delete_dict_type_result = await DictTypeService.delete_dict_type_services(request, query_db, delete_dict_type) delete_dict_type_result = await DictTypeService.delete_dict_type_services(request, query_db, delete_dict_type)
logger.info(delete_dict_type_result.message) logger.info(delete_dict_type_result.message)
@@ -99,33 +136,62 @@ async def delete_system_dict_type(request: Request, dict_ids: str, query_db: Asy
return ResponseUtil.success(msg=delete_dict_type_result.message) return ResponseUtil.success(msg=delete_dict_type_result.message)
@dictController.get('/type/optionselect', response_model=List[DictTypeModel]) @dict_controller.get(
async def query_system_dict_type_options(request: Request, query_db: AsyncSession = Depends(get_db)): '/type/optionselect',
summary='获取字典类型下拉列表接口',
description='用于获取字典类型下拉列表',
response_model=DataResponseModel[list[DictTypeModel]],
)
async def query_system_dict_type_options(
request: Request, query_db: Annotated[AsyncSession, DBSessionDependency()]
) -> Response:
dict_type_query_result = await DictTypeService.get_dict_type_list_services( dict_type_query_result = await DictTypeService.get_dict_type_list_services(
query_db, DictTypePageQueryModel(**dict()), is_page=False query_db, DictTypePageQueryModel(), is_page=False
) )
logger.info('获取成功') logger.info('获取成功')
return ResponseUtil.success(data=dict_type_query_result) return ResponseUtil.success(data=dict_type_query_result)
@dictController.get( @dict_controller.get(
'/type/{dict_id}', response_model=DictTypeModel, dependencies=[Depends(CheckUserInterfaceAuth('system:dict:query'))] '/type/{dict_id}',
summary='获取字典类型详情接口',
description='用于获取指定字典类型的详细信息',
response_model=DataResponseModel[DictTypeModel],
dependencies=[UserInterfaceAuthDependency('system:dict:query')],
) )
async def query_detail_system_dict_type(request: Request, dict_id: int, query_db: AsyncSession = Depends(get_db)): async def query_detail_system_dict_type(
request: Request,
dict_id: Annotated[int, Path(description='字典主键')],
query_db: Annotated[AsyncSession, DBSessionDependency()],
) -> Response:
dict_type_detail_result = await DictTypeService.dict_type_detail_services(query_db, dict_id) dict_type_detail_result = await DictTypeService.dict_type_detail_services(query_db, dict_id)
logger.info(f'获取dict_id为{dict_id}的信息成功') logger.info(f'获取dict_id为{dict_id}的信息成功')
return ResponseUtil.success(data=dict_type_detail_result) return ResponseUtil.success(data=dict_type_detail_result)
@dictController.post('/type/export', dependencies=[Depends(CheckUserInterfaceAuth('system:dict:export'))]) @dict_controller.post(
'/type/export',
summary='导出字典类型列表接口',
description='用于导出当前符合查询条件的字典类型列表数据',
response_class=StreamingResponse,
responses={
200: {
'description': '流式返回字典类型列表excel文件',
'content': {
'application/octet-stream': {},
},
}
},
dependencies=[UserInterfaceAuthDependency('system:dict:export')],
)
@Log(title='字典类型', business_type=BusinessType.EXPORT) @Log(title='字典类型', business_type=BusinessType.EXPORT)
async def export_system_dict_type_list( async def export_system_dict_type_list(
request: Request, request: Request,
dict_type_page_query: DictTypePageQueryModel = Form(), dict_type_page_query: Annotated[DictTypePageQueryModel, Form()],
query_db: AsyncSession = Depends(get_db), query_db: Annotated[AsyncSession, DBSessionDependency()],
): ) -> Response:
# 获取全量数据 # 获取全量数据
dict_type_query_result = await DictTypeService.get_dict_type_list_services( dict_type_query_result = await DictTypeService.get_dict_type_list_services(
query_db, dict_type_page_query, is_page=False query_db, dict_type_page_query, is_page=False
@@ -136,8 +202,17 @@ async def export_system_dict_type_list(
return ResponseUtil.streaming(data=bytes2file_response(dict_type_export_result)) return ResponseUtil.streaming(data=bytes2file_response(dict_type_export_result))
@dictController.get('/data/type/{dict_type}') @dict_controller.get(
async def query_system_dict_type_data(request: Request, dict_type: str, query_db: AsyncSession = Depends(get_db)): '/data/type/{dict_type}',
summary='获取指定字典类型的数据列表接口',
description='用于从缓存中获取指定字典类型的所有数据项',
response_model=DataResponseModel[list[DictDataModel]],
)
async def query_system_dict_type_data(
request: Request,
dict_type: Annotated[str, Path(description='字典类型')],
query_db: Annotated[AsyncSession, DBSessionDependency()],
) -> Response:
# 获取全量数据 # 获取全量数据
dict_data_query_result = await DictDataService.query_dict_data_list_from_cache_services( dict_data_query_result = await DictDataService.query_dict_data_list_from_cache_services(
request.app.state.redis, dict_type request.app.state.redis, dict_type
@@ -147,14 +222,18 @@ async def query_system_dict_type_data(request: Request, dict_type: str, query_db
return ResponseUtil.success(data=dict_data_query_result) return ResponseUtil.success(data=dict_data_query_result)
@dictController.get( @dict_controller.get(
'/data/list', response_model=PageResponseModel, dependencies=[Depends(CheckUserInterfaceAuth('system:dict:list'))] '/data/list',
summary='获取字典数据分页列表接口',
description='用于获取字典数据分页列表',
response_model=PageResponseModel[DictDataModel],
dependencies=[UserInterfaceAuthDependency('system:dict:list')],
) )
async def get_system_dict_data_list( async def get_system_dict_data_list(
request: Request, request: Request,
dict_data_page_query: DictDataPageQueryModel = Depends(DictDataPageQueryModel.as_query), dict_data_page_query: Annotated[DictDataPageQueryModel, Query()],
query_db: AsyncSession = Depends(get_db), query_db: Annotated[AsyncSession, DBSessionDependency()],
): ) -> Response:
# 获取分页数据 # 获取分页数据
dict_data_page_query_result = await DictDataService.get_dict_data_list_services( dict_data_page_query_result = await DictDataService.get_dict_data_list_services(
query_db, dict_data_page_query, is_page=True query_db, dict_data_page_query, is_page=True
@@ -164,15 +243,21 @@ async def get_system_dict_data_list(
return ResponseUtil.success(model_content=dict_data_page_query_result) return ResponseUtil.success(model_content=dict_data_page_query_result)
@dictController.post('/data', dependencies=[Depends(CheckUserInterfaceAuth('system:dict:add'))]) @dict_controller.post(
'/data',
summary='新增字典数据接口',
description='用于新增字典数据',
response_model=ResponseBaseModel,
dependencies=[UserInterfaceAuthDependency('system:dict:add')],
)
@ValidateFields(validate_model='add_dict_data') @ValidateFields(validate_model='add_dict_data')
@Log(title='字典数据', business_type=BusinessType.INSERT) @Log(title='字典数据', business_type=BusinessType.INSERT)
async def add_system_dict_data( async def add_system_dict_data(
request: Request, request: Request,
add_dict_data: DictDataModel, add_dict_data: DictDataModel,
query_db: AsyncSession = Depends(get_db), query_db: Annotated[AsyncSession, DBSessionDependency()],
current_user: CurrentUserModel = Depends(LoginService.get_current_user), current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
): ) -> Response:
add_dict_data.create_by = current_user.user.user_name add_dict_data.create_by = current_user.user.user_name
add_dict_data.create_time = datetime.now() add_dict_data.create_time = datetime.now()
add_dict_data.update_by = current_user.user.user_name add_dict_data.update_by = current_user.user.user_name
@@ -183,15 +268,21 @@ async def add_system_dict_data(
return ResponseUtil.success(msg=add_dict_data_result.message) return ResponseUtil.success(msg=add_dict_data_result.message)
@dictController.put('/data', dependencies=[Depends(CheckUserInterfaceAuth('system:dict:edit'))]) @dict_controller.put(
'/data',
summary='编辑字典数据接口',
description='用于编辑字典数据',
response_model=ResponseBaseModel,
dependencies=[UserInterfaceAuthDependency('system:dict:edit')],
)
@ValidateFields(validate_model='edit_dict_data') @ValidateFields(validate_model='edit_dict_data')
@Log(title='字典数据', business_type=BusinessType.UPDATE) @Log(title='字典数据', business_type=BusinessType.UPDATE)
async def edit_system_dict_data( async def edit_system_dict_data(
request: Request, request: Request,
edit_dict_data: DictDataModel, edit_dict_data: DictDataModel,
query_db: AsyncSession = Depends(get_db), query_db: Annotated[AsyncSession, DBSessionDependency()],
current_user: CurrentUserModel = Depends(LoginService.get_current_user), current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
): ) -> Response:
edit_dict_data.update_by = current_user.user.user_name edit_dict_data.update_by = current_user.user.user_name
edit_dict_data.update_time = datetime.now() edit_dict_data.update_time = datetime.now()
edit_dict_data_result = await DictDataService.edit_dict_data_services(request, query_db, edit_dict_data) edit_dict_data_result = await DictDataService.edit_dict_data_services(request, query_db, edit_dict_data)
@@ -200,9 +291,19 @@ async def edit_system_dict_data(
return ResponseUtil.success(msg=edit_dict_data_result.message) return ResponseUtil.success(msg=edit_dict_data_result.message)
@dictController.delete('/data/{dict_codes}', dependencies=[Depends(CheckUserInterfaceAuth('system:dict:remove'))]) @dict_controller.delete(
'/data/{dict_codes}',
summary='删除字典数据接口',
description='用于删除字典数据',
response_model=ResponseBaseModel,
dependencies=[UserInterfaceAuthDependency('system:dict:remove')],
)
@Log(title='字典数据', business_type=BusinessType.DELETE) @Log(title='字典数据', business_type=BusinessType.DELETE)
async def delete_system_dict_data(request: Request, dict_codes: str, query_db: AsyncSession = Depends(get_db)): async def delete_system_dict_data(
request: Request,
dict_codes: Annotated[str, Path(description='需要删除的字典编码')],
query_db: Annotated[AsyncSession, DBSessionDependency()],
) -> Response:
delete_dict_data = DeleteDictDataModel(dictCodes=dict_codes) delete_dict_data = DeleteDictDataModel(dictCodes=dict_codes)
delete_dict_data_result = await DictDataService.delete_dict_data_services(request, query_db, delete_dict_data) delete_dict_data_result = await DictDataService.delete_dict_data_services(request, query_db, delete_dict_data)
logger.info(delete_dict_data_result.message) logger.info(delete_dict_data_result.message)
@@ -210,25 +311,45 @@ async def delete_system_dict_data(request: Request, dict_codes: str, query_db: A
return ResponseUtil.success(msg=delete_dict_data_result.message) return ResponseUtil.success(msg=delete_dict_data_result.message)
@dictController.get( @dict_controller.get(
'/data/{dict_code}', '/data/{dict_code}',
response_model=DictDataModel, summary='获取字典数据详情接口',
dependencies=[Depends(CheckUserInterfaceAuth('system:dict:query'))], description='用于获取指定字典数据的详细信息',
response_model=DataResponseModel[DictDataModel],
dependencies=[UserInterfaceAuthDependency('system:dict:query')],
) )
async def query_detail_system_dict_data(request: Request, dict_code: int, query_db: AsyncSession = Depends(get_db)): async def query_detail_system_dict_data(
request: Request,
dict_code: Annotated[int, Path(description='字典编码')],
query_db: Annotated[AsyncSession, DBSessionDependency()],
) -> Response:
detail_dict_data_result = await DictDataService.dict_data_detail_services(query_db, dict_code) detail_dict_data_result = await DictDataService.dict_data_detail_services(query_db, dict_code)
logger.info(f'获取dict_code为{dict_code}的信息成功') logger.info(f'获取dict_code为{dict_code}的信息成功')
return ResponseUtil.success(data=detail_dict_data_result) return ResponseUtil.success(data=detail_dict_data_result)
@dictController.post('/data/export', dependencies=[Depends(CheckUserInterfaceAuth('system:dict:export'))]) @dict_controller.post(
'/data/export',
summary='导出字典数据列表接口',
description='用于导出当前符合查询条件的字典数据列表数据',
response_class=StreamingResponse,
responses={
200: {
'description': '流式返回字典数据列表excel文件',
'content': {
'application/octet-stream': {},
},
}
},
dependencies=[UserInterfaceAuthDependency('system:dict:export')],
)
@Log(title='字典数据', business_type=BusinessType.EXPORT) @Log(title='字典数据', business_type=BusinessType.EXPORT)
async def export_system_dict_data_list( async def export_system_dict_data_list(
request: Request, request: Request,
dict_data_page_query: DictDataPageQueryModel = Form(), dict_data_page_query: Annotated[DictDataPageQueryModel, Form()],
query_db: AsyncSession = Depends(get_db), query_db: Annotated[AsyncSession, DBSessionDependency()],
): ) -> Response:
# 获取全量数据 # 获取全量数据
dict_data_query_result = await DictDataService.get_dict_data_list_services( dict_data_query_result = await DictDataService.get_dict_data_list_services(
query_db, dict_data_page_query, is_page=False query_db, dict_data_page_query, is_page=False
@@ -1,15 +1,23 @@
from datetime import datetime from datetime import datetime
from fastapi import APIRouter, Depends, Form, Request from typing import Annotated
from fastapi import Form, Path, Query, Request, Response
from fastapi.responses import StreamingResponse
from pydantic_validation_decorator import ValidateFields from pydantic_validation_decorator import ValidateFields
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from config.enums import BusinessType
from config.get_db import get_db from common.annotation.log_annotation import Log
from module_admin.annotation.log_annotation import Log from common.aspect.db_seesion import DBSessionDependency
from module_admin.aspect.interface_auth import CheckUserInterfaceAuth from common.aspect.interface_auth import UserInterfaceAuthDependency
from common.aspect.pre_auth import CurrentUserDependency, PreAuthDependency
from common.enums import BusinessType
from common.router import APIRouterPro
from common.vo import DataResponseModel, PageResponseModel, ResponseBaseModel
from module_admin.entity.vo.job_vo import ( from module_admin.entity.vo.job_vo import (
DeleteJobLogModel, DeleteJobLogModel,
DeleteJobModel, DeleteJobModel,
EditJobModel, EditJobModel,
JobLogModel,
JobLogPageQueryModel, JobLogPageQueryModel,
JobModel, JobModel,
JobPageQueryModel, JobPageQueryModel,
@@ -17,40 +25,49 @@ from module_admin.entity.vo.job_vo import (
from module_admin.entity.vo.user_vo import CurrentUserModel from module_admin.entity.vo.user_vo import CurrentUserModel
from module_admin.service.job_log_service import JobLogService from module_admin.service.job_log_service import JobLogService
from module_admin.service.job_service import JobService from module_admin.service.job_service import JobService
from module_admin.service.login_service import LoginService
from utils.common_util import bytes2file_response from utils.common_util import bytes2file_response
from utils.log_util import logger from utils.log_util import logger
from utils.page_util import PageResponseModel
from utils.response_util import ResponseUtil from utils.response_util import ResponseUtil
job_controller = APIRouterPro(
jobController = APIRouter(prefix='/monitor', dependencies=[Depends(LoginService.get_current_user)]) prefix='/monitor', order_num=13, tags=['系统监控-定时任务'], dependencies=[PreAuthDependency()]
)
@jobController.get( @job_controller.get(
'/job/list', response_model=PageResponseModel, dependencies=[Depends(CheckUserInterfaceAuth('monitor:job:list'))] '/job/list',
summary='获取定时任务分页列表接口',
description='用于获取定时任务分页列表',
response_model=PageResponseModel[JobModel],
dependencies=[UserInterfaceAuthDependency('monitor:job:list')],
) )
async def get_system_job_list( async def get_system_job_list(
request: Request, request: Request,
job_page_query: JobPageQueryModel = Depends(JobPageQueryModel.as_query), job_page_query: Annotated[JobPageQueryModel, Query()],
query_db: AsyncSession = Depends(get_db), query_db: Annotated[AsyncSession, DBSessionDependency()],
): ) -> Response:
# 获取分页数据 # 获取分页数据
notice_page_query_result = await JobService.get_job_list_services(query_db, job_page_query, is_page=True) job_page_query_result = await JobService.get_job_list_services(query_db, job_page_query, is_page=True)
logger.info('获取成功') logger.info('获取成功')
return ResponseUtil.success(model_content=notice_page_query_result) return ResponseUtil.success(model_content=job_page_query_result)
@jobController.post('/job', dependencies=[Depends(CheckUserInterfaceAuth('monitor:job:add'))]) @job_controller.post(
'/job',
summary='新增定时任务接口',
description='用于新增定时任务',
response_model=ResponseBaseModel,
dependencies=[UserInterfaceAuthDependency('monitor:job:add')],
)
@ValidateFields(validate_model='add_job') @ValidateFields(validate_model='add_job')
@Log(title='定时任务', business_type=BusinessType.INSERT) @Log(title='定时任务', business_type=BusinessType.INSERT)
async def add_system_job( async def add_system_job(
request: Request, request: Request,
add_job: JobModel, add_job: JobModel,
query_db: AsyncSession = Depends(get_db), query_db: Annotated[AsyncSession, DBSessionDependency()],
current_user: CurrentUserModel = Depends(LoginService.get_current_user), current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
): ) -> Response:
add_job.create_by = current_user.user.user_name add_job.create_by = current_user.user.user_name
add_job.create_time = datetime.now() add_job.create_time = datetime.now()
add_job.update_by = current_user.user.user_name add_job.update_by = current_user.user.user_name
@@ -61,15 +78,21 @@ async def add_system_job(
return ResponseUtil.success(msg=add_job_result.message) return ResponseUtil.success(msg=add_job_result.message)
@jobController.put('/job', dependencies=[Depends(CheckUserInterfaceAuth('monitor:job:edit'))]) @job_controller.put(
'/job',
summary='编辑定时任务接口',
description='用于编辑定时任务',
response_model=ResponseBaseModel,
dependencies=[UserInterfaceAuthDependency('monitor:job:edit')],
)
@ValidateFields(validate_model='edit_job') @ValidateFields(validate_model='edit_job')
@Log(title='定时任务', business_type=BusinessType.UPDATE) @Log(title='定时任务', business_type=BusinessType.UPDATE)
async def edit_system_job( async def edit_system_job(
request: Request, request: Request,
edit_job: EditJobModel, edit_job: EditJobModel,
query_db: AsyncSession = Depends(get_db), query_db: Annotated[AsyncSession, DBSessionDependency()],
current_user: CurrentUserModel = Depends(LoginService.get_current_user), current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
): ) -> Response:
edit_job.update_by = current_user.user.user_name edit_job.update_by = current_user.user.user_name
edit_job.update_time = datetime.now() edit_job.update_time = datetime.now()
edit_job_result = await JobService.edit_job_services(query_db, edit_job) edit_job_result = await JobService.edit_job_services(query_db, edit_job)
@@ -78,14 +101,20 @@ async def edit_system_job(
return ResponseUtil.success(msg=edit_job_result.message) return ResponseUtil.success(msg=edit_job_result.message)
@jobController.put('/job/changeStatus', dependencies=[Depends(CheckUserInterfaceAuth('monitor:job:changeStatus'))]) @job_controller.put(
'/job/changeStatus',
summary='修改定时任务状态接口',
description='用于修改定时任务状态',
response_model=ResponseBaseModel,
dependencies=[UserInterfaceAuthDependency('monitor:job:changeStatus')],
)
@Log(title='定时任务', business_type=BusinessType.UPDATE) @Log(title='定时任务', business_type=BusinessType.UPDATE)
async def change_system_job_status( async def change_system_job_status(
request: Request, request: Request,
change_job: EditJobModel, change_job: EditJobModel,
query_db: AsyncSession = Depends(get_db), query_db: Annotated[AsyncSession, DBSessionDependency()],
current_user: CurrentUserModel = Depends(LoginService.get_current_user), current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
): ) -> Response:
edit_job = EditJobModel( edit_job = EditJobModel(
jobId=change_job.job_id, jobId=change_job.job_id,
status=change_job.status, status=change_job.status,
@@ -99,18 +128,38 @@ async def change_system_job_status(
return ResponseUtil.success(msg=edit_job_result.message) return ResponseUtil.success(msg=edit_job_result.message)
@jobController.put('/job/run', dependencies=[Depends(CheckUserInterfaceAuth('monitor:job:changeStatus'))]) @job_controller.put(
'/job/run',
summary='执行定时任务接口',
description='用于执行指定的定时任务',
response_model=ResponseBaseModel,
dependencies=[UserInterfaceAuthDependency('monitor:job:changeStatus')],
)
@Log(title='定时任务', business_type=BusinessType.UPDATE) @Log(title='定时任务', business_type=BusinessType.UPDATE)
async def execute_system_job(request: Request, execute_job: JobModel, query_db: AsyncSession = Depends(get_db)): async def execute_system_job(
request: Request,
execute_job: JobModel,
query_db: Annotated[AsyncSession, DBSessionDependency()],
) -> Response:
execute_job_result = await JobService.execute_job_once_services(query_db, execute_job) execute_job_result = await JobService.execute_job_once_services(query_db, execute_job)
logger.info(execute_job_result.message) logger.info(execute_job_result.message)
return ResponseUtil.success(msg=execute_job_result.message) return ResponseUtil.success(msg=execute_job_result.message)
@jobController.delete('/job/{job_ids}', dependencies=[Depends(CheckUserInterfaceAuth('monitor:job:remove'))]) @job_controller.delete(
'/job/{job_ids}',
summary='删除定时任务接口',
description='用于删除定时任务',
response_model=ResponseBaseModel,
dependencies=[UserInterfaceAuthDependency('monitor:job:remove')],
)
@Log(title='定时任务', business_type=BusinessType.DELETE) @Log(title='定时任务', business_type=BusinessType.DELETE)
async def delete_system_job(request: Request, job_ids: str, query_db: AsyncSession = Depends(get_db)): async def delete_system_job(
request: Request,
job_ids: Annotated[str, Path(description='需要删除的定时任务ID')],
query_db: Annotated[AsyncSession, DBSessionDependency()],
) -> Response:
delete_job = DeleteJobModel(jobIds=job_ids) delete_job = DeleteJobModel(jobIds=job_ids)
delete_job_result = await JobService.delete_job_services(query_db, delete_job) delete_job_result = await JobService.delete_job_services(query_db, delete_job)
logger.info(delete_job_result.message) logger.info(delete_job_result.message)
@@ -118,23 +167,45 @@ async def delete_system_job(request: Request, job_ids: str, query_db: AsyncSessi
return ResponseUtil.success(msg=delete_job_result.message) return ResponseUtil.success(msg=delete_job_result.message)
@jobController.get( @job_controller.get(
'/job/{job_id}', response_model=JobModel, dependencies=[Depends(CheckUserInterfaceAuth('monitor:job:query'))] '/job/{job_id}',
summary='获取定时任务详情接口',
description='用于获取指定定时任务的详情信息',
response_model=DataResponseModel[JobModel],
dependencies=[UserInterfaceAuthDependency('monitor:job:query')],
) )
async def query_detail_system_job(request: Request, job_id: int, query_db: AsyncSession = Depends(get_db)): async def query_detail_system_job(
request: Request,
job_id: Annotated[int, Path(description='任务ID')],
query_db: Annotated[AsyncSession, DBSessionDependency()],
) -> Response:
job_detail_result = await JobService.job_detail_services(query_db, job_id) job_detail_result = await JobService.job_detail_services(query_db, job_id)
logger.info(f'获取job_id为{job_id}的信息成功') logger.info(f'获取job_id为{job_id}的信息成功')
return ResponseUtil.success(data=job_detail_result) return ResponseUtil.success(data=job_detail_result)
@jobController.post('/job/export', dependencies=[Depends(CheckUserInterfaceAuth('monitor:job:export'))]) @job_controller.post(
'/job/export',
summary='导出定时任务列表接口',
description='用于导出当前符合查询条件的定时任务列表数据',
response_class=StreamingResponse,
responses={
200: {
'description': '流式返回定时任务列表excel文件',
'content': {
'application/octet-stream': {},
},
}
},
dependencies=[UserInterfaceAuthDependency('monitor:job:export')],
)
@Log(title='定时任务', business_type=BusinessType.EXPORT) @Log(title='定时任务', business_type=BusinessType.EXPORT)
async def export_system_job_list( async def export_system_job_list(
request: Request, request: Request,
job_page_query: JobPageQueryModel = Form(), job_page_query: Annotated[JobPageQueryModel, Form()],
query_db: AsyncSession = Depends(get_db), query_db: Annotated[AsyncSession, DBSessionDependency()],
): ) -> Response:
# 获取全量数据 # 获取全量数据
job_query_result = await JobService.get_job_list_services(query_db, job_page_query, is_page=False) job_query_result = await JobService.get_job_list_services(query_db, job_page_query, is_page=False)
job_export_result = await JobService.export_job_list_services(request, job_query_result) job_export_result = await JobService.export_job_list_services(request, job_query_result)
@@ -143,14 +214,18 @@ async def export_system_job_list(
return ResponseUtil.streaming(data=bytes2file_response(job_export_result)) return ResponseUtil.streaming(data=bytes2file_response(job_export_result))
@jobController.get( @job_controller.get(
'/jobLog/list', response_model=PageResponseModel, dependencies=[Depends(CheckUserInterfaceAuth('monitor:job:list'))] '/jobLog/list',
summary='获取定时任务调度日志分页列表接口',
description='用于获取定时任务调度日志分页列表',
response_model=PageResponseModel[JobLogModel],
dependencies=[UserInterfaceAuthDependency('monitor:job:list')],
) )
async def get_system_job_log_list( async def get_system_job_log_list(
request: Request, request: Request,
job_log_page_query: JobLogPageQueryModel = Depends(JobLogPageQueryModel.as_query), job_log_page_query: Annotated[JobLogPageQueryModel, Query()],
query_db: AsyncSession = Depends(get_db), query_db: Annotated[AsyncSession, DBSessionDependency()],
): ) -> Response:
# 获取分页数据 # 获取分页数据
job_log_page_query_result = await JobLogService.get_job_log_list_services( job_log_page_query_result = await JobLogService.get_job_log_list_services(
query_db, job_log_page_query, is_page=True query_db, job_log_page_query, is_page=True
@@ -160,18 +235,37 @@ async def get_system_job_log_list(
return ResponseUtil.success(model_content=job_log_page_query_result) return ResponseUtil.success(model_content=job_log_page_query_result)
@jobController.delete('/jobLog/clean', dependencies=[Depends(CheckUserInterfaceAuth('monitor:job:remove'))]) @job_controller.delete(
'/jobLog/clean',
summary='清空定时任务调度日志接口',
description='用于清空所有定时任务调度日志',
response_model=ResponseBaseModel,
dependencies=[UserInterfaceAuthDependency('monitor:job:remove')],
)
@Log(title='定时任务调度日志', business_type=BusinessType.CLEAN) @Log(title='定时任务调度日志', business_type=BusinessType.CLEAN)
async def clear_system_job_log(request: Request, query_db: AsyncSession = Depends(get_db)): async def clear_system_job_log(
request: Request,
query_db: Annotated[AsyncSession, DBSessionDependency()],
) -> Response:
clear_job_log_result = await JobLogService.clear_job_log_services(query_db) clear_job_log_result = await JobLogService.clear_job_log_services(query_db)
logger.info(clear_job_log_result.message) logger.info(clear_job_log_result.message)
return ResponseUtil.success(msg=clear_job_log_result.message) return ResponseUtil.success(msg=clear_job_log_result.message)
@jobController.delete('/jobLog/{job_log_ids}', dependencies=[Depends(CheckUserInterfaceAuth('monitor:job:remove'))]) @job_controller.delete(
'/jobLog/{job_log_ids}',
summary='删除定时任务调度日志接口',
description='用于删除定时任务调度日志',
response_model=ResponseBaseModel,
dependencies=[UserInterfaceAuthDependency('monitor:job:remove')],
)
@Log(title='定时任务调度日志', business_type=BusinessType.DELETE) @Log(title='定时任务调度日志', business_type=BusinessType.DELETE)
async def delete_system_job_log(request: Request, job_log_ids: str, query_db: AsyncSession = Depends(get_db)): async def delete_system_job_log(
request: Request,
job_log_ids: Annotated[str, Path(description='需要删除的定时任务日志ID')],
query_db: Annotated[AsyncSession, DBSessionDependency()],
) -> Response:
delete_job_log = DeleteJobLogModel(jobLogIds=job_log_ids) delete_job_log = DeleteJobLogModel(jobLogIds=job_log_ids)
delete_job_log_result = await JobLogService.delete_job_log_services(query_db, delete_job_log) delete_job_log_result = await JobLogService.delete_job_log_services(query_db, delete_job_log)
logger.info(delete_job_log_result.message) logger.info(delete_job_log_result.message)
@@ -179,13 +273,27 @@ async def delete_system_job_log(request: Request, job_log_ids: str, query_db: As
return ResponseUtil.success(msg=delete_job_log_result.message) return ResponseUtil.success(msg=delete_job_log_result.message)
@jobController.post('/jobLog/export', dependencies=[Depends(CheckUserInterfaceAuth('monitor:job:export'))]) @job_controller.post(
'/jobLog/export',
summary='导出定时任务调度日志列表接口',
description='用于导出当前符合查询条件的定时任务调度日志列表数据',
response_class=StreamingResponse,
responses={
200: {
'description': '流式返回定时任务日志列表excel文件',
'content': {
'application/octet-stream': {},
},
}
},
dependencies=[UserInterfaceAuthDependency('monitor:job:export')],
)
@Log(title='定时任务调度日志', business_type=BusinessType.EXPORT) @Log(title='定时任务调度日志', business_type=BusinessType.EXPORT)
async def export_system_job_log_list( async def export_system_job_log_list(
request: Request, request: Request,
job_log_page_query: JobLogPageQueryModel = Form(), job_log_page_query: Annotated[JobLogPageQueryModel, Form()],
query_db: AsyncSession = Depends(get_db), query_db: Annotated[AsyncSession, DBSessionDependency()],
): ) -> Response:
# 获取全量数据 # 获取全量数据
job_log_query_result = await JobLogService.get_job_log_list_services(query_db, job_log_page_query, is_page=False) job_log_query_result = await JobLogService.get_job_log_list_services(query_db, job_log_page_query, is_page=False)
job_log_export_result = await JobLogService.export_job_log_list_services(request, job_log_query_result) job_log_export_result = await JobLogService.export_job_log_list_services(request, job_log_query_result)
@@ -1,37 +1,47 @@
from fastapi import APIRouter, Depends, Form, Request from typing import Annotated
from fastapi import Form, Path, Query, Request, Response
from fastapi.responses import StreamingResponse
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from config.enums import BusinessType
from config.get_db import get_db from common.annotation.log_annotation import Log
from module_admin.annotation.log_annotation import Log from common.aspect.db_seesion import DBSessionDependency
from module_admin.aspect.interface_auth import CheckUserInterfaceAuth from common.aspect.interface_auth import UserInterfaceAuthDependency
from common.aspect.pre_auth import PreAuthDependency
from common.enums import BusinessType
from common.router import APIRouterPro
from common.vo import PageResponseModel, ResponseBaseModel
from module_admin.entity.vo.log_vo import ( from module_admin.entity.vo.log_vo import (
DeleteLoginLogModel, DeleteLoginLogModel,
DeleteOperLogModel, DeleteOperLogModel,
LogininforModel,
LoginLogPageQueryModel, LoginLogPageQueryModel,
OperLogModel,
OperLogPageQueryModel, OperLogPageQueryModel,
UnlockUser, UnlockUser,
) )
from module_admin.service.log_service import LoginLogService, OperationLogService from module_admin.service.log_service import LoginLogService, OperationLogService
from module_admin.service.login_service import LoginService
from utils.common_util import bytes2file_response from utils.common_util import bytes2file_response
from utils.log_util import logger from utils.log_util import logger
from utils.page_util import PageResponseModel
from utils.response_util import ResponseUtil from utils.response_util import ResponseUtil
log_controller = APIRouterPro(
logController = APIRouter(prefix='/monitor', dependencies=[Depends(LoginService.get_current_user)]) prefix='/monitor', order_num=11, tags=['系统管理-日志管理'], dependencies=[PreAuthDependency()]
)
@logController.get( @log_controller.get(
'/operlog/list', '/operlog/list',
response_model=PageResponseModel, summary='获取操作日志分页列表接口',
dependencies=[Depends(CheckUserInterfaceAuth('monitor:operlog:list'))], description='用于获取操作日志分页列表',
response_model=PageResponseModel[OperLogModel],
dependencies=[UserInterfaceAuthDependency('monitor:operlog:list')],
) )
async def get_system_operation_log_list( async def get_system_operation_log_list(
request: Request, request: Request,
operation_log_page_query: OperLogPageQueryModel = Depends(OperLogPageQueryModel.as_query), operation_log_page_query: Annotated[OperLogPageQueryModel, Query()],
query_db: AsyncSession = Depends(get_db), query_db: Annotated[AsyncSession, DBSessionDependency()],
): ) -> Response:
# 获取分页数据 # 获取分页数据
operation_log_page_query_result = await OperationLogService.get_operation_log_list_services( operation_log_page_query_result = await OperationLogService.get_operation_log_list_services(
query_db, operation_log_page_query, is_page=True query_db, operation_log_page_query, is_page=True
@@ -41,18 +51,36 @@ async def get_system_operation_log_list(
return ResponseUtil.success(model_content=operation_log_page_query_result) return ResponseUtil.success(model_content=operation_log_page_query_result)
@logController.delete('/operlog/clean', dependencies=[Depends(CheckUserInterfaceAuth('monitor:operlog:remove'))]) @log_controller.delete(
'/operlog/clean',
summary='清空操作日志接口',
description='用于清空所有操作日志',
response_model=ResponseBaseModel,
dependencies=[UserInterfaceAuthDependency('monitor:operlog:remove')],
)
@Log(title='操作日志', business_type=BusinessType.CLEAN) @Log(title='操作日志', business_type=BusinessType.CLEAN)
async def clear_system_operation_log(request: Request, query_db: AsyncSession = Depends(get_db)): async def clear_system_operation_log(
request: Request, query_db: Annotated[AsyncSession, DBSessionDependency()]
) -> Response:
clear_operation_log_result = await OperationLogService.clear_operation_log_services(query_db) clear_operation_log_result = await OperationLogService.clear_operation_log_services(query_db)
logger.info(clear_operation_log_result.message) logger.info(clear_operation_log_result.message)
return ResponseUtil.success(msg=clear_operation_log_result.message) return ResponseUtil.success(msg=clear_operation_log_result.message)
@logController.delete('/operlog/{oper_ids}', dependencies=[Depends(CheckUserInterfaceAuth('monitor:operlog:remove'))]) @log_controller.delete(
'/operlog/{oper_ids}',
summary='删除操作日志接口',
description='用于删除操作日志',
response_model=ResponseBaseModel,
dependencies=[UserInterfaceAuthDependency('monitor:operlog:remove')],
)
@Log(title='操作日志', business_type=BusinessType.DELETE) @Log(title='操作日志', business_type=BusinessType.DELETE)
async def delete_system_operation_log(request: Request, oper_ids: str, query_db: AsyncSession = Depends(get_db)): async def delete_system_operation_log(
request: Request,
oper_ids: Annotated[str, Path(description='需要删除的日志主键')],
query_db: Annotated[AsyncSession, DBSessionDependency()],
) -> Response:
delete_operation_log = DeleteOperLogModel(operIds=oper_ids) delete_operation_log = DeleteOperLogModel(operIds=oper_ids)
delete_operation_log_result = await OperationLogService.delete_operation_log_services( delete_operation_log_result = await OperationLogService.delete_operation_log_services(
query_db, delete_operation_log query_db, delete_operation_log
@@ -62,13 +90,27 @@ async def delete_system_operation_log(request: Request, oper_ids: str, query_db:
return ResponseUtil.success(msg=delete_operation_log_result.message) return ResponseUtil.success(msg=delete_operation_log_result.message)
@logController.post('/operlog/export', dependencies=[Depends(CheckUserInterfaceAuth('monitor:operlog:export'))]) @log_controller.post(
'/operlog/export',
summary='导出操作日志接口',
description='用于导出当前符合查询条件的操作日志数据',
response_class=StreamingResponse,
responses={
200: {
'description': '流式返回操作日志列表excel文件',
'content': {
'application/octet-stream': {},
},
}
},
dependencies=[UserInterfaceAuthDependency('monitor:operlog:export')],
)
@Log(title='操作日志', business_type=BusinessType.EXPORT) @Log(title='操作日志', business_type=BusinessType.EXPORT)
async def export_system_operation_log_list( async def export_system_operation_log_list(
request: Request, request: Request,
operation_log_page_query: OperLogPageQueryModel = Form(), operation_log_page_query: Annotated[OperLogPageQueryModel, Form()],
query_db: AsyncSession = Depends(get_db), query_db: Annotated[AsyncSession, DBSessionDependency()],
): ) -> Response:
# 获取全量数据 # 获取全量数据
operation_log_query_result = await OperationLogService.get_operation_log_list_services( operation_log_query_result = await OperationLogService.get_operation_log_list_services(
query_db, operation_log_page_query, is_page=False query_db, operation_log_page_query, is_page=False
@@ -81,16 +123,18 @@ async def export_system_operation_log_list(
return ResponseUtil.streaming(data=bytes2file_response(operation_log_export_result)) return ResponseUtil.streaming(data=bytes2file_response(operation_log_export_result))
@logController.get( @log_controller.get(
'/logininfor/list', '/logininfor/list',
response_model=PageResponseModel, summary='获取登录日志分页列表接口',
dependencies=[Depends(CheckUserInterfaceAuth('monitor:logininfor:list'))], description='用于获取登录日志分页列表',
response_model=PageResponseModel[LogininforModel],
dependencies=[UserInterfaceAuthDependency('monitor:logininfor:list')],
) )
async def get_system_login_log_list( async def get_system_login_log_list(
request: Request, request: Request,
login_log_page_query: LoginLogPageQueryModel = Depends(LoginLogPageQueryModel.as_query), login_log_page_query: Annotated[LoginLogPageQueryModel, Query()],
query_db: AsyncSession = Depends(get_db), query_db: Annotated[AsyncSession, DBSessionDependency()],
): ) -> Response:
# 获取分页数据 # 获取分页数据
login_log_page_query_result = await LoginLogService.get_login_log_list_services( login_log_page_query_result = await LoginLogService.get_login_log_list_services(
query_db, login_log_page_query, is_page=True query_db, login_log_page_query, is_page=True
@@ -100,20 +144,36 @@ async def get_system_login_log_list(
return ResponseUtil.success(model_content=login_log_page_query_result) return ResponseUtil.success(model_content=login_log_page_query_result)
@logController.delete('/logininfor/clean', dependencies=[Depends(CheckUserInterfaceAuth('monitor:logininfor:remove'))]) @log_controller.delete(
'/logininfor/clean',
summary='清空登录日志接口',
description='用于清空所有登录日志',
response_model=ResponseBaseModel,
dependencies=[UserInterfaceAuthDependency('monitor:logininfor:remove')],
)
@Log(title='登录日志', business_type=BusinessType.CLEAN) @Log(title='登录日志', business_type=BusinessType.CLEAN)
async def clear_system_login_log(request: Request, query_db: AsyncSession = Depends(get_db)): async def clear_system_login_log(
request: Request, query_db: Annotated[AsyncSession, DBSessionDependency()]
) -> Response:
clear_login_log_result = await LoginLogService.clear_login_log_services(query_db) clear_login_log_result = await LoginLogService.clear_login_log_services(query_db)
logger.info(clear_login_log_result.message) logger.info(clear_login_log_result.message)
return ResponseUtil.success(msg=clear_login_log_result.message) return ResponseUtil.success(msg=clear_login_log_result.message)
@logController.delete( @log_controller.delete(
'/logininfor/{info_ids}', dependencies=[Depends(CheckUserInterfaceAuth('monitor:logininfor:remove'))] '/logininfor/{info_ids}',
summary='删除登录日志接口',
description='用于删除登录日志',
response_model=ResponseBaseModel,
dependencies=[UserInterfaceAuthDependency('monitor:logininfor:remove')],
) )
@Log(title='登录日志', business_type=BusinessType.DELETE) @Log(title='登录日志', business_type=BusinessType.DELETE)
async def delete_system_login_log(request: Request, info_ids: str, query_db: AsyncSession = Depends(get_db)): async def delete_system_login_log(
request: Request,
info_ids: Annotated[str, Path(description='需要删除的访问ID')],
query_db: Annotated[AsyncSession, DBSessionDependency()],
) -> Response:
delete_login_log = DeleteLoginLogModel(infoIds=info_ids) delete_login_log = DeleteLoginLogModel(infoIds=info_ids)
delete_login_log_result = await LoginLogService.delete_login_log_services(query_db, delete_login_log) delete_login_log_result = await LoginLogService.delete_login_log_services(query_db, delete_login_log)
logger.info(delete_login_log_result.message) logger.info(delete_login_log_result.message)
@@ -121,11 +181,19 @@ async def delete_system_login_log(request: Request, info_ids: str, query_db: Asy
return ResponseUtil.success(msg=delete_login_log_result.message) return ResponseUtil.success(msg=delete_login_log_result.message)
@logController.get( @log_controller.get(
'/logininfor/unlock/{user_name}', dependencies=[Depends(CheckUserInterfaceAuth('monitor:logininfor:unlock'))] '/logininfor/unlock/{user_name}',
summary='解锁账户接口',
description='用于解锁指定用户账户',
response_model=ResponseBaseModel,
dependencies=[UserInterfaceAuthDependency('monitor:logininfor:unlock')],
) )
@Log(title='账户解锁', business_type=BusinessType.OTHER) @Log(title='账户解锁', business_type=BusinessType.OTHER)
async def unlock_system_user(request: Request, user_name: str, query_db: AsyncSession = Depends(get_db)): async def unlock_system_user(
request: Request,
user_name: Annotated[str, Path(description='用户名称')],
query_db: Annotated[AsyncSession, DBSessionDependency()],
) -> Response:
unlock_user = UnlockUser(userName=user_name) unlock_user = UnlockUser(userName=user_name)
unlock_user_result = await LoginLogService.unlock_user_services(request, unlock_user) unlock_user_result = await LoginLogService.unlock_user_services(request, unlock_user)
logger.info(unlock_user_result.message) logger.info(unlock_user_result.message)
@@ -133,13 +201,27 @@ async def unlock_system_user(request: Request, user_name: str, query_db: AsyncSe
return ResponseUtil.success(msg=unlock_user_result.message) return ResponseUtil.success(msg=unlock_user_result.message)
@logController.post('/logininfor/export', dependencies=[Depends(CheckUserInterfaceAuth('monitor:logininfor:export'))]) @log_controller.post(
'/logininfor/export',
summary='导出登录日志接口',
description='用于导出当前符合查询条件的登录日志数据',
response_class=StreamingResponse,
responses={
200: {
'description': '流式返回登录日志列表excel文件',
'content': {
'application/octet-stream': {},
},
}
},
dependencies=[UserInterfaceAuthDependency('monitor:logininfor:export')],
)
@Log(title='登录日志', business_type=BusinessType.EXPORT) @Log(title='登录日志', business_type=BusinessType.EXPORT)
async def export_system_login_log_list( async def export_system_login_log_list(
request: Request, request: Request,
login_log_page_query: LoginLogPageQueryModel = Form(), login_log_page_query: Annotated[LoginLogPageQueryModel, Form()],
query_db: AsyncSession = Depends(get_db), query_db: Annotated[AsyncSession, DBSessionDependency()],
): ) -> Response:
# 获取全量数据 # 获取全量数据
login_log_query_result = await LoginLogService.get_login_log_list_services( login_log_query_result = await LoginLogService.get_login_log_list_services(
query_db, login_log_page_query, is_page=False query_db, login_log_page_query, is_page=False
@@ -1,35 +1,42 @@
import jwt
import uuid import uuid
from datetime import datetime, timedelta from datetime import datetime, timedelta
from fastapi import APIRouter, Depends, Request from typing import Annotated, Optional
import jwt
from fastapi import Depends, Request, Response
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from typing import Optional
from config.enums import BusinessType, RedisInitKeyConfig from common.annotation.log_annotation import Log
from common.aspect.db_seesion import DBSessionDependency
from common.aspect.pre_auth import CurrentUserDependency
from common.enums import BusinessType, RedisInitKeyConfig
from common.router import APIRouterPro
from common.vo import CrudResponseModel, DataResponseModel, DynamicResponseModel, ResponseBaseModel
from config.env import AppConfig, JwtConfig from config.env import AppConfig, JwtConfig
from config.get_db import get_db from module_admin.entity.vo.login_vo import RouterModel, Token, UserLogin, UserRegister
from module_admin.annotation.log_annotation import Log
from module_admin.entity.vo.common_vo import CrudResponseModel
from module_admin.entity.vo.login_vo import UserLogin, UserRegister, Token
from module_admin.entity.vo.user_vo import CurrentUserModel, EditUserModel from module_admin.entity.vo.user_vo import CurrentUserModel, EditUserModel
from module_admin.service.login_service import CustomOAuth2PasswordRequestForm, LoginService, oauth2_scheme from module_admin.service.login_service import CustomOAuth2PasswordRequestForm, LoginService, oauth2_scheme
from module_admin.service.user_service import UserService from module_admin.service.user_service import UserService
from utils.log_util import logger from utils.log_util import logger
from utils.response_util import ResponseUtil from utils.response_util import ResponseUtil
login_controller = APIRouterPro(order_num=1, tags=['登录模块'])
loginController = APIRouter()
@loginController.post('/login', response_model=Token) @login_controller.post(
'/login',
summary='登录接口',
description='用于用户登录',
response_model=DynamicResponseModel[Token],
)
@Log(title='用户登录', business_type=BusinessType.OTHER, log_type='login') @Log(title='用户登录', business_type=BusinessType.OTHER, log_type='login')
async def login( async def login(
request: Request, form_data: CustomOAuth2PasswordRequestForm = Depends(), query_db: AsyncSession = Depends(get_db) request: Request,
): form_data: Annotated[CustomOAuth2PasswordRequestForm, Depends()],
query_db: Annotated[AsyncSession, DBSessionDependency()],
) -> Response:
captcha_enabled = ( captcha_enabled = (
True await request.app.state.redis.get(f'{RedisInitKeyConfig.SYS_CONFIG.key}:sys.account.captchaEnabled') == 'true'
if await request.app.state.redis.get(f'{RedisInitKeyConfig.SYS_CONFIG.key}:sys.account.captchaEnabled')
== 'true'
else False
) )
user = UserLogin( user = UserLogin(
userName=form_data.username, userName=form_data.username,
@@ -77,37 +84,56 @@ async def login(
return ResponseUtil.success(msg='登录成功', dict_content={'token': access_token}) return ResponseUtil.success(msg='登录成功', dict_content={'token': access_token})
@loginController.get('/getInfo', response_model=CurrentUserModel) @login_controller.get(
'/getInfo',
summary='获取用户信息接口',
description='用于获取当前登录用户的信息',
response_model=DynamicResponseModel[CurrentUserModel],
)
async def get_login_user_info( async def get_login_user_info(
request: Request, current_user: CurrentUserModel = Depends(LoginService.get_current_user) request: Request, current_user: Annotated[CurrentUserModel, CurrentUserDependency()]
): ) -> Response:
logger.info('获取成功') logger.info('获取成功')
return ResponseUtil.success(model_content=current_user) return ResponseUtil.success(model_content=current_user)
@loginController.get('/getRouters') @login_controller.get(
'/getRouters',
summary='获取用户路由接口',
description='用于获取当前登录用户的路由信息',
response_model=DataResponseModel[list[RouterModel]],
)
async def get_login_user_routers( async def get_login_user_routers(
request: Request, request: Request,
current_user: CurrentUserModel = Depends(LoginService.get_current_user), current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
query_db: AsyncSession = Depends(get_db), query_db: Annotated[AsyncSession, DBSessionDependency()],
): ) -> Response:
logger.info('获取成功') logger.info('获取成功')
user_routers = await LoginService.get_current_user_routers(current_user.user.user_id, query_db) user_routers = await LoginService.get_current_user_routers(current_user.user.user_id, query_db)
return ResponseUtil.success(data=user_routers) return ResponseUtil.success(data=user_routers)
@loginController.post('/register', response_model=CrudResponseModel) @login_controller.post(
async def register_user(request: Request, user_register: UserRegister, query_db: AsyncSession = Depends(get_db)): '/register',
summary='注册接口',
description='用于用户注册',
response_model=DataResponseModel[CrudResponseModel],
)
async def register_user(
request: Request,
user_register: UserRegister,
query_db: Annotated[AsyncSession, DBSessionDependency()],
) -> Response:
user_register_result = await LoginService.register_user_services(request, query_db, user_register) user_register_result = await LoginService.register_user_services(request, query_db, user_register)
logger.info(user_register_result.message) logger.info(user_register_result.message)
return ResponseUtil.success(data=user_register_result, msg=user_register_result.message) return ResponseUtil.success(data=user_register_result, msg=user_register_result.message)
# @loginController.post("/getSmsCode", response_model=SmsCode) # @login_controller.post("/getSmsCode", response_model=SmsCode)
# async def get_sms_code(request: Request, user: ResetUserModel, query_db: AsyncSession = Depends(get_db)): # async def get_sms_code(request: Request, user: ResetUserModel, query_db: AsyncSession = DBSessionDependency()):
# try: # try:
# sms_result = await LoginService.get_sms_code_services(request, query_db, user) # sms_result = await LoginService.get_sms_code_services(request, query_db, user)
# if sms_result.is_success: # if sms_result.is_success:
@@ -121,8 +147,8 @@ async def register_user(request: Request, user_register: UserRegister, query_db:
# return ResponseUtil.error(msg=str(e)) # return ResponseUtil.error(msg=str(e))
# #
# #
# @loginController.post("/forgetPwd", response_model=CrudResponseModel) # @login_controller.post("/forgetPwd", response_model=CrudResponseModel)
# async def forget_user_pwd(request: Request, forget_user: ResetUserModel, query_db: AsyncSession = Depends(get_db)): # async def forget_user_pwd(request: Request, forget_user: ResetUserModel, query_db: AsyncSession = DBSessionDependency()):
# try: # try:
# forget_user_result = await LoginService.forget_user_services(request, query_db, forget_user) # forget_user_result = await LoginService.forget_user_services(request, query_db, forget_user)
# if forget_user_result.is_success: # if forget_user_result.is_success:
@@ -136,8 +162,13 @@ async def register_user(request: Request, user_register: UserRegister, query_db:
# return ResponseUtil.error(msg=str(e)) # return ResponseUtil.error(msg=str(e))
@loginController.post('/logout') @login_controller.post(
async def logout(request: Request, token: Optional[str] = Depends(oauth2_scheme)): '/logout',
summary='退出登录接口',
description='用于用户退出登录',
response_model=ResponseBaseModel,
)
async def logout(request: Request, token: Annotated[Optional[str], Depends(oauth2_scheme)]) -> Response:
payload = jwt.decode( payload = jwt.decode(
token, JwtConfig.jwt_secret_key, algorithms=[JwtConfig.jwt_algorithm], options={'verify_exp': False} token, JwtConfig.jwt_secret_key, algorithms=[JwtConfig.jwt_algorithm], options={'verify_exp': False}
) )
@@ -1,72 +1,98 @@
from datetime import datetime from datetime import datetime
from fastapi import APIRouter, Depends, Request from typing import Annotated
from fastapi import Path, Query, Request, Response
from pydantic_validation_decorator import ValidateFields from pydantic_validation_decorator import ValidateFields
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from typing import List
from config.enums import BusinessType from common.annotation.log_annotation import Log
from config.get_db import get_db from common.aspect.db_seesion import DBSessionDependency
from module_admin.annotation.log_annotation import Log from common.aspect.interface_auth import UserInterfaceAuthDependency
from module_admin.aspect.interface_auth import CheckUserInterfaceAuth from common.aspect.pre_auth import CurrentUserDependency, PreAuthDependency
from module_admin.entity.vo.menu_vo import DeleteMenuModel, MenuModel, MenuQueryModel from common.enums import BusinessType
from common.router import APIRouterPro
from common.vo import DataResponseModel, DynamicResponseModel, ResponseBaseModel
from module_admin.entity.vo.menu_vo import DeleteMenuModel, MenuModel, MenuQueryModel, MenuTreeModel
from module_admin.entity.vo.role_vo import RoleMenuQueryModel
from module_admin.entity.vo.user_vo import CurrentUserModel from module_admin.entity.vo.user_vo import CurrentUserModel
from module_admin.service.login_service import LoginService
from module_admin.service.menu_service import MenuService from module_admin.service.menu_service import MenuService
from utils.log_util import logger from utils.log_util import logger
from utils.response_util import ResponseUtil from utils.response_util import ResponseUtil
menu_controller = APIRouterPro(
menuController = APIRouter(prefix='/system/menu', dependencies=[Depends(LoginService.get_current_user)]) prefix='/system/menu', order_num=5, tags=['系统管理-菜单管理'], dependencies=[PreAuthDependency()]
)
@menuController.get('/treeselect') @menu_controller.get(
'/treeselect',
summary='获取菜单树接口',
description='用于获取当前用户可见的菜单树',
response_model=DataResponseModel[list[MenuTreeModel]],
)
async def get_system_menu_tree( async def get_system_menu_tree(
request: Request, request: Request,
query_db: AsyncSession = Depends(get_db), query_db: Annotated[AsyncSession, DBSessionDependency()],
current_user: CurrentUserModel = Depends(LoginService.get_current_user), current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
): ) -> Response:
menu_query_result = await MenuService.get_menu_tree_services(query_db, current_user) menu_query_result = await MenuService.get_menu_tree_services(query_db, current_user)
logger.info('获取成功') logger.info('获取成功')
return ResponseUtil.success(data=menu_query_result) return ResponseUtil.success(data=menu_query_result)
@menuController.get('/roleMenuTreeselect/{role_id}') @menu_controller.get(
'/roleMenuTreeselect/{role_id}',
summary='获取角色菜单树接口',
description='用于获取指定角色可见的菜单树',
response_model=DynamicResponseModel[RoleMenuQueryModel],
)
async def get_system_role_menu_tree( async def get_system_role_menu_tree(
request: Request, request: Request,
role_id: int, role_id: Annotated[int, Path(description='角色ID')],
query_db: AsyncSession = Depends(get_db), query_db: Annotated[AsyncSession, DBSessionDependency()],
current_user: CurrentUserModel = Depends(LoginService.get_current_user), current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
): ) -> Response:
role_menu_query_result = await MenuService.get_role_menu_tree_services(query_db, role_id, current_user) role_menu_query_result = await MenuService.get_role_menu_tree_services(query_db, role_id, current_user)
logger.info('获取成功') logger.info('获取成功')
return ResponseUtil.success(model_content=role_menu_query_result) return ResponseUtil.success(model_content=role_menu_query_result)
@menuController.get( @menu_controller.get(
'/list', response_model=List[MenuModel], dependencies=[Depends(CheckUserInterfaceAuth('system:menu:list'))] '/list',
summary='获取菜单列表接口',
description='用于获取当前用户可见的菜单列表',
response_model=DataResponseModel[list[MenuModel]],
dependencies=[UserInterfaceAuthDependency('system:menu:list')],
) )
async def get_system_menu_list( async def get_system_menu_list(
request: Request, request: Request,
menu_query: MenuQueryModel = Depends(MenuQueryModel.as_query), menu_query: Annotated[MenuQueryModel, Query()],
query_db: AsyncSession = Depends(get_db), query_db: Annotated[AsyncSession, DBSessionDependency()],
current_user: CurrentUserModel = Depends(LoginService.get_current_user), current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
): ) -> Response:
menu_query_result = await MenuService.get_menu_list_services(query_db, menu_query, current_user) menu_query_result = await MenuService.get_menu_list_services(query_db, menu_query, current_user)
logger.info('获取成功') logger.info('获取成功')
return ResponseUtil.success(data=menu_query_result) return ResponseUtil.success(data=menu_query_result)
@menuController.post('', dependencies=[Depends(CheckUserInterfaceAuth('system:menu:add'))]) @menu_controller.post(
'',
summary='新增菜单接口',
description='用于新增菜单',
response_model=ResponseBaseModel,
dependencies=[UserInterfaceAuthDependency('system:menu:add')],
)
@ValidateFields(validate_model='add_menu') @ValidateFields(validate_model='add_menu')
@Log(title='菜单管理', business_type=BusinessType.INSERT) @Log(title='菜单管理', business_type=BusinessType.INSERT)
async def add_system_menu( async def add_system_menu(
request: Request, request: Request,
add_menu: MenuModel, add_menu: MenuModel,
query_db: AsyncSession = Depends(get_db), query_db: Annotated[AsyncSession, DBSessionDependency()],
current_user: CurrentUserModel = Depends(LoginService.get_current_user), current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
): ) -> Response:
add_menu.create_by = current_user.user.user_name add_menu.create_by = current_user.user.user_name
add_menu.create_time = datetime.now() add_menu.create_time = datetime.now()
add_menu.update_by = current_user.user.user_name add_menu.update_by = current_user.user.user_name
@@ -77,15 +103,21 @@ async def add_system_menu(
return ResponseUtil.success(msg=add_menu_result.message) return ResponseUtil.success(msg=add_menu_result.message)
@menuController.put('', dependencies=[Depends(CheckUserInterfaceAuth('system:menu:edit'))]) @menu_controller.put(
'',
summary='编辑菜单接口',
description='用于编辑菜单',
response_model=ResponseBaseModel,
dependencies=[UserInterfaceAuthDependency('system:menu:edit')],
)
@ValidateFields(validate_model='edit_menu') @ValidateFields(validate_model='edit_menu')
@Log(title='菜单管理', business_type=BusinessType.UPDATE) @Log(title='菜单管理', business_type=BusinessType.UPDATE)
async def edit_system_menu( async def edit_system_menu(
request: Request, request: Request,
edit_menu: MenuModel, edit_menu: MenuModel,
query_db: AsyncSession = Depends(get_db), query_db: Annotated[AsyncSession, DBSessionDependency()],
current_user: CurrentUserModel = Depends(LoginService.get_current_user), current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
): ) -> Response:
edit_menu.update_by = current_user.user.user_name edit_menu.update_by = current_user.user.user_name
edit_menu.update_time = datetime.now() edit_menu.update_time = datetime.now()
edit_menu_result = await MenuService.edit_menu_services(query_db, edit_menu) edit_menu_result = await MenuService.edit_menu_services(query_db, edit_menu)
@@ -94,9 +126,19 @@ async def edit_system_menu(
return ResponseUtil.success(msg=edit_menu_result.message) return ResponseUtil.success(msg=edit_menu_result.message)
@menuController.delete('/{menu_ids}', dependencies=[Depends(CheckUserInterfaceAuth('system:menu:remove'))]) @menu_controller.delete(
'/{menu_ids}',
summary='删除菜单接口',
description='用于删除菜单',
response_model=ResponseBaseModel,
dependencies=[UserInterfaceAuthDependency('system:menu:remove')],
)
@Log(title='菜单管理', business_type=BusinessType.DELETE) @Log(title='菜单管理', business_type=BusinessType.DELETE)
async def delete_system_menu(request: Request, menu_ids: str, query_db: AsyncSession = Depends(get_db)): async def delete_system_menu(
request: Request,
menu_ids: Annotated[str, Path(description='需要删除的菜单ID')],
query_db: Annotated[AsyncSession, DBSessionDependency()],
) -> Response:
delete_menu = DeleteMenuModel(menuIds=menu_ids) delete_menu = DeleteMenuModel(menuIds=menu_ids)
delete_menu_result = await MenuService.delete_menu_services(query_db, delete_menu) delete_menu_result = await MenuService.delete_menu_services(query_db, delete_menu)
logger.info(delete_menu_result.message) logger.info(delete_menu_result.message)
@@ -104,10 +146,18 @@ async def delete_system_menu(request: Request, menu_ids: str, query_db: AsyncSes
return ResponseUtil.success(msg=delete_menu_result.message) return ResponseUtil.success(msg=delete_menu_result.message)
@menuController.get( @menu_controller.get(
'/{menu_id}', response_model=MenuModel, dependencies=[Depends(CheckUserInterfaceAuth('system:menu:query'))] '/{menu_id}',
summary='获取菜单详情接口',
description='用于获取指定菜单的详情信息',
response_model=DataResponseModel[MenuModel],
dependencies=[UserInterfaceAuthDependency('system:menu:query')],
) )
async def query_detail_system_menu(request: Request, menu_id: int, query_db: AsyncSession = Depends(get_db)): async def query_detail_system_menu(
request: Request,
menu_id: Annotated[int, Path(description='菜单ID')],
query_db: Annotated[AsyncSession, DBSessionDependency()],
) -> Response:
menu_detail_result = await MenuService.menu_detail_services(query_db, menu_id) menu_detail_result = await MenuService.menu_detail_services(query_db, menu_id)
logger.info(f'获取menu_id为{menu_id}的信息成功') logger.info(f'获取menu_id为{menu_id}的信息成功')
@@ -1,31 +1,40 @@
from datetime import datetime from datetime import datetime
from fastapi import APIRouter, Depends, Request from typing import Annotated
from fastapi import Path, Query, Request, Response
from pydantic_validation_decorator import ValidateFields from pydantic_validation_decorator import ValidateFields
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from config.enums import BusinessType
from config.get_db import get_db from common.annotation.log_annotation import Log
from module_admin.annotation.log_annotation import Log from common.aspect.db_seesion import DBSessionDependency
from module_admin.aspect.interface_auth import CheckUserInterfaceAuth from common.aspect.interface_auth import UserInterfaceAuthDependency
from common.aspect.pre_auth import CurrentUserDependency, PreAuthDependency
from common.enums import BusinessType
from common.router import APIRouterPro
from common.vo import DataResponseModel, PageResponseModel, ResponseBaseModel
from module_admin.entity.vo.notice_vo import DeleteNoticeModel, NoticeModel, NoticePageQueryModel from module_admin.entity.vo.notice_vo import DeleteNoticeModel, NoticeModel, NoticePageQueryModel
from module_admin.entity.vo.user_vo import CurrentUserModel from module_admin.entity.vo.user_vo import CurrentUserModel
from module_admin.service.login_service import LoginService
from module_admin.service.notice_service import NoticeService from module_admin.service.notice_service import NoticeService
from utils.log_util import logger from utils.log_util import logger
from utils.page_util import PageResponseModel
from utils.response_util import ResponseUtil from utils.response_util import ResponseUtil
notice_controller = APIRouterPro(
noticeController = APIRouter(prefix='/system/notice', dependencies=[Depends(LoginService.get_current_user)]) prefix='/system/notice', order_num=10, tags=['系统管理-通知公告管理'], dependencies=[PreAuthDependency()]
)
@noticeController.get( @notice_controller.get(
'/list', response_model=PageResponseModel, dependencies=[Depends(CheckUserInterfaceAuth('system:notice:list'))] '/list',
summary='获取通知公告分页列表接口',
description='用于获取通知公告分页列表',
response_model=PageResponseModel[NoticeModel],
dependencies=[UserInterfaceAuthDependency('system:notice:list')],
) )
async def get_system_notice_list( async def get_system_notice_list(
request: Request, request: Request,
notice_page_query: NoticePageQueryModel = Depends(NoticePageQueryModel.as_query), notice_page_query: Annotated[NoticePageQueryModel, Query()],
query_db: AsyncSession = Depends(get_db), query_db: Annotated[AsyncSession, DBSessionDependency()],
): ) -> Response:
# 获取分页数据 # 获取分页数据
notice_page_query_result = await NoticeService.get_notice_list_services(query_db, notice_page_query, is_page=True) notice_page_query_result = await NoticeService.get_notice_list_services(query_db, notice_page_query, is_page=True)
logger.info('获取成功') logger.info('获取成功')
@@ -33,15 +42,21 @@ async def get_system_notice_list(
return ResponseUtil.success(model_content=notice_page_query_result) return ResponseUtil.success(model_content=notice_page_query_result)
@noticeController.post('', dependencies=[Depends(CheckUserInterfaceAuth('system:notice:add'))]) @notice_controller.post(
'',
summary='新增通知公告接口',
description='用于新增通知公告',
response_model=ResponseBaseModel,
dependencies=[UserInterfaceAuthDependency('system:notice:add')],
)
@ValidateFields(validate_model='add_notice') @ValidateFields(validate_model='add_notice')
@Log(title='通知公告', business_type=BusinessType.INSERT) @Log(title='通知公告', business_type=BusinessType.INSERT)
async def add_system_notice( async def add_system_notice(
request: Request, request: Request,
add_notice: NoticeModel, add_notice: NoticeModel,
query_db: AsyncSession = Depends(get_db), query_db: Annotated[AsyncSession, DBSessionDependency()],
current_user: CurrentUserModel = Depends(LoginService.get_current_user), current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
): ) -> Response:
add_notice.create_by = current_user.user.user_name add_notice.create_by = current_user.user.user_name
add_notice.create_time = datetime.now() add_notice.create_time = datetime.now()
add_notice.update_by = current_user.user.user_name add_notice.update_by = current_user.user.user_name
@@ -52,15 +67,21 @@ async def add_system_notice(
return ResponseUtil.success(msg=add_notice_result.message) return ResponseUtil.success(msg=add_notice_result.message)
@noticeController.put('', dependencies=[Depends(CheckUserInterfaceAuth('system:notice:edit'))]) @notice_controller.put(
'',
summary='编辑通知公告接口',
description='用于编辑通知公告',
response_model=ResponseBaseModel,
dependencies=[UserInterfaceAuthDependency('system:notice:edit')],
)
@ValidateFields(validate_model='edit_notice') @ValidateFields(validate_model='edit_notice')
@Log(title='通知公告', business_type=BusinessType.UPDATE) @Log(title='通知公告', business_type=BusinessType.UPDATE)
async def edit_system_notice( async def edit_system_notice(
request: Request, request: Request,
edit_notice: NoticeModel, edit_notice: NoticeModel,
query_db: AsyncSession = Depends(get_db), query_db: Annotated[AsyncSession, DBSessionDependency()],
current_user: CurrentUserModel = Depends(LoginService.get_current_user), current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
): ) -> Response:
edit_notice.update_by = current_user.user.user_name edit_notice.update_by = current_user.user.user_name
edit_notice.update_time = datetime.now() edit_notice.update_time = datetime.now()
edit_notice_result = await NoticeService.edit_notice_services(query_db, edit_notice) edit_notice_result = await NoticeService.edit_notice_services(query_db, edit_notice)
@@ -69,9 +90,19 @@ async def edit_system_notice(
return ResponseUtil.success(msg=edit_notice_result.message) return ResponseUtil.success(msg=edit_notice_result.message)
@noticeController.delete('/{notice_ids}', dependencies=[Depends(CheckUserInterfaceAuth('system:notice:remove'))]) @notice_controller.delete(
'/{notice_ids}',
summary='删除通知公告接口',
description='用于删除通知公告',
response_model=ResponseBaseModel,
dependencies=[UserInterfaceAuthDependency('system:notice:remove')],
)
@Log(title='通知公告', business_type=BusinessType.DELETE) @Log(title='通知公告', business_type=BusinessType.DELETE)
async def delete_system_notice(request: Request, notice_ids: str, query_db: AsyncSession = Depends(get_db)): async def delete_system_notice(
request: Request,
notice_ids: Annotated[str, Path(description='需要删除的公告ID')],
query_db: Annotated[AsyncSession, DBSessionDependency()],
) -> Response:
delete_notice = DeleteNoticeModel(noticeIds=notice_ids) delete_notice = DeleteNoticeModel(noticeIds=notice_ids)
delete_notice_result = await NoticeService.delete_notice_services(query_db, delete_notice) delete_notice_result = await NoticeService.delete_notice_services(query_db, delete_notice)
logger.info(delete_notice_result.message) logger.info(delete_notice_result.message)
@@ -79,10 +110,18 @@ async def delete_system_notice(request: Request, notice_ids: str, query_db: Asyn
return ResponseUtil.success(msg=delete_notice_result.message) return ResponseUtil.success(msg=delete_notice_result.message)
@noticeController.get( @notice_controller.get(
'/{notice_id}', response_model=NoticeModel, dependencies=[Depends(CheckUserInterfaceAuth('system:notice:query'))] '/{notice_id}',
summary='获取通知公告详情接口',
description='用于获取指定通知公告的详细信息',
response_model=DataResponseModel[NoticeModel],
dependencies=[UserInterfaceAuthDependency('system:notice:query')],
) )
async def query_detail_system_post(request: Request, notice_id: int, query_db: AsyncSession = Depends(get_db)): async def query_detail_system_post(
request: Request,
notice_id: Annotated[int, Path(description='公告ID')],
query_db: Annotated[AsyncSession, DBSessionDependency()],
) -> Response:
notice_detail_result = await NoticeService.notice_detail_services(query_db, notice_id) notice_detail_result = await NoticeService.notice_detail_services(query_db, notice_id)
logger.info(f'获取notice_id为{notice_id}的信息成功') logger.info(f'获取notice_id为{notice_id}的信息成功')
@@ -1,38 +1,58 @@
from fastapi import APIRouter, Depends, Request from typing import Annotated
from fastapi import Path, Query, Request, Response
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from config.enums import BusinessType
from config.get_db import get_db from common.annotation.log_annotation import Log
from module_admin.annotation.log_annotation import Log from common.aspect.db_seesion import DBSessionDependency
from module_admin.aspect.interface_auth import CheckUserInterfaceAuth from common.aspect.interface_auth import UserInterfaceAuthDependency
from module_admin.entity.vo.online_vo import DeleteOnlineModel, OnlineQueryModel from common.aspect.pre_auth import PreAuthDependency
from module_admin.service.login_service import LoginService from common.enums import BusinessType
from common.router import APIRouterPro
from common.vo import ResponseBaseModel
from module_admin.entity.vo.online_vo import DeleteOnlineModel, OnlinePageResponseModel, OnlineQueryModel
from module_admin.service.online_service import OnlineService from module_admin.service.online_service import OnlineService
from utils.log_util import logger from utils.log_util import logger
from utils.page_util import PageResponseModel
from utils.response_util import ResponseUtil from utils.response_util import ResponseUtil
online_controller = APIRouterPro(
onlineController = APIRouter(prefix='/monitor/online', dependencies=[Depends(LoginService.get_current_user)]) prefix='/monitor/online', order_num=12, tags=['系统监控-在线用户'], dependencies=[PreAuthDependency()]
)
@onlineController.get( @online_controller.get(
'/list', response_model=PageResponseModel, dependencies=[Depends(CheckUserInterfaceAuth('monitor:online:list'))] '/list',
summary='获取在线用户分页列表接口',
description='用于获取在线用户分页列表',
response_model=OnlinePageResponseModel,
dependencies=[UserInterfaceAuthDependency('monitor:online:list')],
) )
async def get_monitor_online_list( async def get_monitor_online_list(
request: Request, online_page_query: OnlineQueryModel = Depends(OnlineQueryModel.as_query) request: Request,
): online_page_query: Annotated[OnlineQueryModel, Query()],
) -> Response:
# 获取全量数据 # 获取全量数据
online_query_result = await OnlineService.get_online_list_services(request, online_page_query) online_query_result = await OnlineService.get_online_list_services(request, online_page_query)
logger.info('获取成功') logger.info('获取成功')
return ResponseUtil.success( return ResponseUtil.success(
model_content=PageResponseModel(rows=online_query_result, total=len(online_query_result)) model_content=OnlinePageResponseModel(rows=online_query_result, total=len(online_query_result))
) )
@onlineController.delete('/{token_ids}', dependencies=[Depends(CheckUserInterfaceAuth('monitor:online:forceLogout'))]) @online_controller.delete(
'/{token_ids}',
summary='强退在线用户接口',
description='用于强退指定会话编号的在线用户',
response_model=ResponseBaseModel,
dependencies=[UserInterfaceAuthDependency('monitor:online:forceLogout')],
)
@Log(title='在线用户', business_type=BusinessType.FORCE) @Log(title='在线用户', business_type=BusinessType.FORCE)
async def delete_monitor_online(request: Request, token_ids: str, query_db: AsyncSession = Depends(get_db)): async def delete_monitor_online(
request: Request,
token_ids: Annotated[str, Path(description='需要强退的会话编号')],
query_db: Annotated[AsyncSession, DBSessionDependency()],
) -> Response:
delete_online = DeleteOnlineModel(tokenIds=token_ids) delete_online = DeleteOnlineModel(tokenIds=token_ids)
delete_online_result = await OnlineService.delete_online_services(request, delete_online) delete_online_result = await OnlineService.delete_online_services(request, delete_online)
logger.info(delete_online_result.message) logger.info(delete_online_result.message)
@@ -1,105 +0,0 @@
from datetime import datetime
from fastapi import APIRouter, Depends, Form, Request
from pydantic_validation_decorator import ValidateFields
from sqlalchemy.ext.asyncio import AsyncSession
from config.enums import BusinessType
from config.get_db import get_db
from module_admin.annotation.log_annotation import Log
from module_admin.aspect.interface_auth import CheckUserInterfaceAuth
from module_admin.service.login_service import LoginService
from module_admin.service.post_service import PostService
from module_admin.entity.vo.post_vo import DeletePostModel, PostModel, PostPageQueryModel
from module_admin.entity.vo.user_vo import CurrentUserModel
from utils.common_util import bytes2file_response
from utils.log_util import logger
from utils.page_util import PageResponseModel
from utils.response_util import ResponseUtil
postController = APIRouter(prefix='/system/post', dependencies=[Depends(LoginService.get_current_user)])
@postController.get(
'/list', response_model=PageResponseModel, dependencies=[Depends(CheckUserInterfaceAuth('system:post:list'))]
)
async def get_system_post_list(
request: Request,
post_page_query: PostPageQueryModel = Depends(PostPageQueryModel.as_query),
query_db: AsyncSession = Depends(get_db),
):
# 获取分页数据
post_page_query_result = await PostService.get_post_list_services(query_db, post_page_query, is_page=True)
logger.info('获取成功')
return ResponseUtil.success(model_content=post_page_query_result)
@postController.post('', dependencies=[Depends(CheckUserInterfaceAuth('system:post:add'))])
@ValidateFields(validate_model='add_post')
@Log(title='岗位管理', business_type=BusinessType.INSERT)
async def add_system_post(
request: Request,
add_post: PostModel,
query_db: AsyncSession = Depends(get_db),
current_user: CurrentUserModel = Depends(LoginService.get_current_user),
):
add_post.create_by = current_user.user.user_name
add_post.create_time = datetime.now()
add_post.update_by = current_user.user.user_name
add_post.update_time = datetime.now()
add_post_result = await PostService.add_post_services(query_db, add_post)
logger.info(add_post_result.message)
return ResponseUtil.success(msg=add_post_result.message)
@postController.put('', dependencies=[Depends(CheckUserInterfaceAuth('system:post:edit'))])
@ValidateFields(validate_model='edit_post')
@Log(title='岗位管理', business_type=BusinessType.UPDATE)
async def edit_system_post(
request: Request,
edit_post: PostModel,
query_db: AsyncSession = Depends(get_db),
current_user: CurrentUserModel = Depends(LoginService.get_current_user),
):
edit_post.update_by = current_user.user.user_name
edit_post.update_time = datetime.now()
edit_post_result = await PostService.edit_post_services(query_db, edit_post)
logger.info(edit_post_result.message)
return ResponseUtil.success(msg=edit_post_result.message)
@postController.delete('/{post_ids}', dependencies=[Depends(CheckUserInterfaceAuth('system:post:remove'))])
@Log(title='岗位管理', business_type=BusinessType.DELETE)
async def delete_system_post(request: Request, post_ids: str, query_db: AsyncSession = Depends(get_db)):
delete_post = DeletePostModel(postIds=post_ids)
delete_post_result = await PostService.delete_post_services(query_db, delete_post)
logger.info(delete_post_result.message)
return ResponseUtil.success(msg=delete_post_result.message)
@postController.get(
'/{post_id}', response_model=PostModel, dependencies=[Depends(CheckUserInterfaceAuth('system:post:query'))]
)
async def query_detail_system_post(request: Request, post_id: int, query_db: AsyncSession = Depends(get_db)):
post_detail_result = await PostService.post_detail_services(query_db, post_id)
logger.info(f'获取post_id为{post_id}的信息成功')
return ResponseUtil.success(data=post_detail_result)
@postController.post('/export', dependencies=[Depends(CheckUserInterfaceAuth('system:post:export'))])
@Log(title='岗位管理', business_type=BusinessType.EXPORT)
async def export_system_post_list(
request: Request,
post_page_query: PostPageQueryModel = Form(),
query_db: AsyncSession = Depends(get_db),
):
# 获取全量数据
post_query_result = await PostService.get_post_list_services(query_db, post_page_query, is_page=False)
post_export_result = await PostService.export_post_list_services(post_query_result)
logger.info('导出成功')
return ResponseUtil.streaming(data=bytes2file_response(post_export_result))
@@ -0,0 +1,159 @@
from datetime import datetime
from typing import Annotated
from fastapi import Form, Path, Query, Request, Response
from fastapi.responses import StreamingResponse
from pydantic_validation_decorator import ValidateFields
from sqlalchemy.ext.asyncio import AsyncSession
from common.annotation.log_annotation import Log
from common.aspect.db_seesion import DBSessionDependency
from common.aspect.interface_auth import UserInterfaceAuthDependency
from common.aspect.pre_auth import CurrentUserDependency, PreAuthDependency
from common.enums import BusinessType
from common.router import APIRouterPro
from common.vo import DataResponseModel, PageResponseModel, ResponseBaseModel
from module_admin.entity.vo.post_vo import DeletePostModel, PostModel, PostPageQueryModel
from module_admin.entity.vo.user_vo import CurrentUserModel
from module_admin.service.post_service import PostService
from utils.common_util import bytes2file_response
from utils.log_util import logger
from utils.response_util import ResponseUtil
post_controller = APIRouterPro(
prefix='/system/post', order_num=7, tags=['系统管理-岗位管理'], dependencies=[PreAuthDependency()]
)
@post_controller.get(
'/list',
summary='获取岗位分页列表接口',
description='用于获取岗位分页列表',
response_model=PageResponseModel[PostModel],
dependencies=[UserInterfaceAuthDependency('system:post:list')],
)
async def get_system_post_list(
request: Request,
post_page_query: Annotated[PostPageQueryModel, Query()],
query_db: Annotated[AsyncSession, DBSessionDependency()],
) -> Response:
# 获取分页数据
post_page_query_result = await PostService.get_post_list_services(query_db, post_page_query, is_page=True)
logger.info('获取成功')
return ResponseUtil.success(model_content=post_page_query_result)
@post_controller.post(
'',
summary='新增岗位接口',
description='用于新增岗位',
response_model=ResponseBaseModel,
dependencies=[UserInterfaceAuthDependency('system:post:add')],
)
@ValidateFields(validate_model='add_post')
@Log(title='岗位管理', business_type=BusinessType.INSERT)
async def add_system_post(
request: Request,
add_post: PostModel,
query_db: Annotated[AsyncSession, DBSessionDependency()],
current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
) -> Response:
add_post.create_by = current_user.user.user_name
add_post.create_time = datetime.now()
add_post.update_by = current_user.user.user_name
add_post.update_time = datetime.now()
add_post_result = await PostService.add_post_services(query_db, add_post)
logger.info(add_post_result.message)
return ResponseUtil.success(msg=add_post_result.message)
@post_controller.put(
'',
summary='编辑岗位接口',
description='用于编辑岗位',
response_model=ResponseBaseModel,
dependencies=[UserInterfaceAuthDependency('system:post:edit')],
)
@ValidateFields(validate_model='edit_post')
@Log(title='岗位管理', business_type=BusinessType.UPDATE)
async def edit_system_post(
request: Request,
edit_post: PostModel,
query_db: Annotated[AsyncSession, DBSessionDependency()],
current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
) -> Response:
edit_post.update_by = current_user.user.user_name
edit_post.update_time = datetime.now()
edit_post_result = await PostService.edit_post_services(query_db, edit_post)
logger.info(edit_post_result.message)
return ResponseUtil.success(msg=edit_post_result.message)
@post_controller.delete(
'/{post_ids}',
summary='删除岗位接口',
description='用于删除岗位',
response_model=ResponseBaseModel,
dependencies=[UserInterfaceAuthDependency('system:post:remove')],
)
@Log(title='岗位管理', business_type=BusinessType.DELETE)
async def delete_system_post(
request: Request,
post_ids: Annotated[str, Path(description='需要删除的岗位ID')],
query_db: Annotated[AsyncSession, DBSessionDependency()],
) -> Response:
delete_post = DeletePostModel(postIds=post_ids)
delete_post_result = await PostService.delete_post_services(query_db, delete_post)
logger.info(delete_post_result.message)
return ResponseUtil.success(msg=delete_post_result.message)
@post_controller.get(
'/{post_id}',
summary='获取岗位详情接口',
description='用于获取指定岗位的详细信息',
response_model=DataResponseModel[PostModel],
dependencies=[UserInterfaceAuthDependency('system:post:query')],
)
async def query_detail_system_post(
request: Request,
post_id: Annotated[int, Path(description='岗位ID')],
query_db: Annotated[AsyncSession, DBSessionDependency()],
) -> Response:
post_detail_result = await PostService.post_detail_services(query_db, post_id)
logger.info(f'获取post_id为{post_id}的信息成功')
return ResponseUtil.success(data=post_detail_result)
@post_controller.post(
'/export',
summary='导出岗位列表接口',
description='用于导出当前符合查询条件的岗位列表数据',
response_class=StreamingResponse,
responses={
200: {
'description': '流式返回岗位列表excel文件',
'content': {
'application/octet-stream': {},
},
}
},
dependencies=[UserInterfaceAuthDependency('system:post:export')],
)
@Log(title='岗位管理', business_type=BusinessType.EXPORT)
async def export_system_post_list(
request: Request,
post_page_query: Annotated[PostPageQueryModel, Form()],
query_db: Annotated[AsyncSession, DBSessionDependency()],
) -> Response:
# 获取全量数据
post_query_result = await PostService.get_post_list_services(query_db, post_page_query, is_page=False)
post_export_result = await PostService.export_post_list_services(post_query_result)
logger.info('导出成功')
return ResponseUtil.streaming(data=bytes2file_response(post_export_result))
@@ -1,36 +1,54 @@
from datetime import datetime from datetime import datetime
from fastapi import APIRouter, Depends, Form, Request from typing import Annotated
from fastapi import Form, Path, Query, Request, Response
from fastapi.responses import StreamingResponse
from pydantic_validation_decorator import ValidateFields from pydantic_validation_decorator import ValidateFields
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from config.enums import BusinessType
from config.get_db import get_db from common.annotation.log_annotation import Log
from module_admin.annotation.log_annotation import Log from common.aspect.data_scope import DataScopeDependency
from module_admin.aspect.data_scope import GetDataScope from common.aspect.db_seesion import DBSessionDependency
from module_admin.aspect.interface_auth import CheckUserInterfaceAuth from common.aspect.interface_auth import UserInterfaceAuthDependency
from common.aspect.pre_auth import CurrentUserDependency, PreAuthDependency
from common.enums import BusinessType
from common.router import APIRouterPro
from common.vo import DataResponseModel, DynamicResponseModel, PageResponseModel, ResponseBaseModel
from module_admin.entity.vo.dept_vo import DeptModel from module_admin.entity.vo.dept_vo import DeptModel
from module_admin.entity.vo.role_vo import AddRoleModel, DeleteRoleModel, RoleModel, RolePageQueryModel from module_admin.entity.vo.role_vo import (
from module_admin.entity.vo.user_vo import CrudUserRoleModel, CurrentUserModel, UserRolePageQueryModel AddRoleModel,
DeleteRoleModel,
RoleDeptQueryModel,
RoleModel,
RolePageQueryModel,
)
from module_admin.entity.vo.user_vo import CrudUserRoleModel, CurrentUserModel, UserInfoModel, UserRolePageQueryModel
from module_admin.service.dept_service import DeptService from module_admin.service.dept_service import DeptService
from module_admin.service.login_service import LoginService
from module_admin.service.role_service import RoleService from module_admin.service.role_service import RoleService
from module_admin.service.user_service import UserService from module_admin.service.user_service import UserService
from utils.common_util import bytes2file_response from utils.common_util import bytes2file_response
from utils.log_util import logger from utils.log_util import logger
from utils.page_util import PageResponseModel
from utils.response_util import ResponseUtil from utils.response_util import ResponseUtil
role_controller = APIRouterPro(
roleController = APIRouter(prefix='/system/role', dependencies=[Depends(LoginService.get_current_user)]) prefix='/system/role', order_num=4, tags=['系统管理-角色管理'], dependencies=[PreAuthDependency()]
)
@roleController.get('/deptTree/{role_id}', dependencies=[Depends(CheckUserInterfaceAuth('system:role:query'))]) @role_controller.get(
'/deptTree/{role_id}',
summary='获取自定义数据权限时可见的部门树接口',
description='用于自定义数据权限时获取当前用户可见的部门树',
response_model=DynamicResponseModel[RoleDeptQueryModel],
dependencies=[UserInterfaceAuthDependency('system:role:query')],
)
async def get_system_role_dept_tree( async def get_system_role_dept_tree(
request: Request, request: Request,
role_id: int, role_id: Annotated[int, Path(description='角色ID')],
query_db: AsyncSession = Depends(get_db), query_db: Annotated[AsyncSession, DBSessionDependency()],
data_scope_sql: str = Depends(GetDataScope('SysDept')), data_scope_sql: Annotated[str, DataScopeDependency('SysDept')],
): ) -> Response:
dept_query_result = await DeptService.get_dept_tree_services(query_db, DeptModel(**{}), data_scope_sql) dept_query_result = await DeptService.get_dept_tree_services(query_db, DeptModel(), data_scope_sql)
role_dept_query_result = await RoleService.get_role_dept_tree_services(query_db, role_id) role_dept_query_result = await RoleService.get_role_dept_tree_services(query_db, role_id)
role_dept_query_result.depts = dept_query_result role_dept_query_result.depts = dept_query_result
logger.info('获取成功') logger.info('获取成功')
@@ -38,15 +56,19 @@ async def get_system_role_dept_tree(
return ResponseUtil.success(model_content=role_dept_query_result) return ResponseUtil.success(model_content=role_dept_query_result)
@roleController.get( @role_controller.get(
'/list', response_model=PageResponseModel, dependencies=[Depends(CheckUserInterfaceAuth('system:role:list'))] '/list',
summary='获取角色分页列表接口',
description='用于获取角色分页列表',
response_model=PageResponseModel[RoleModel],
dependencies=[UserInterfaceAuthDependency('system:role:list')],
) )
async def get_system_role_list( async def get_system_role_list(
request: Request, request: Request,
role_page_query: RolePageQueryModel = Depends(RolePageQueryModel.as_query), role_page_query: Annotated[RolePageQueryModel, Query()],
query_db: AsyncSession = Depends(get_db), query_db: Annotated[AsyncSession, DBSessionDependency()],
data_scope_sql: str = Depends(GetDataScope('SysDept')), data_scope_sql: Annotated[str, DataScopeDependency('SysDept')],
): ) -> Response:
role_page_query_result = await RoleService.get_role_list_services( role_page_query_result = await RoleService.get_role_list_services(
query_db, role_page_query, data_scope_sql, is_page=True query_db, role_page_query, data_scope_sql, is_page=True
) )
@@ -55,15 +77,21 @@ async def get_system_role_list(
return ResponseUtil.success(model_content=role_page_query_result) return ResponseUtil.success(model_content=role_page_query_result)
@roleController.post('', dependencies=[Depends(CheckUserInterfaceAuth('system:role:add'))]) @role_controller.post(
'',
summary='新增角色接口',
description='用于新增角色',
response_model=ResponseBaseModel,
dependencies=[UserInterfaceAuthDependency('system:role:add')],
)
@ValidateFields(validate_model='add_role') @ValidateFields(validate_model='add_role')
@Log(title='角色管理', business_type=BusinessType.INSERT) @Log(title='角色管理', business_type=BusinessType.INSERT)
async def add_system_role( async def add_system_role(
request: Request, request: Request,
add_role: AddRoleModel, add_role: AddRoleModel,
query_db: AsyncSession = Depends(get_db), query_db: Annotated[AsyncSession, DBSessionDependency()],
current_user: CurrentUserModel = Depends(LoginService.get_current_user), current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
): ) -> Response:
add_role.create_by = current_user.user.user_name add_role.create_by = current_user.user.user_name
add_role.create_time = datetime.now() add_role.create_time = datetime.now()
add_role.update_by = current_user.user.user_name add_role.update_by = current_user.user.user_name
@@ -74,16 +102,22 @@ async def add_system_role(
return ResponseUtil.success(msg=add_role_result.message) return ResponseUtil.success(msg=add_role_result.message)
@roleController.put('', dependencies=[Depends(CheckUserInterfaceAuth('system:role:edit'))]) @role_controller.put(
'',
summary='编辑角色接口',
description='用于编辑角色',
response_model=ResponseBaseModel,
dependencies=[UserInterfaceAuthDependency('system:role:edit')],
)
@ValidateFields(validate_model='edit_role') @ValidateFields(validate_model='edit_role')
@Log(title='角色管理', business_type=BusinessType.UPDATE) @Log(title='角色管理', business_type=BusinessType.UPDATE)
async def edit_system_role( async def edit_system_role(
request: Request, request: Request,
edit_role: AddRoleModel, edit_role: AddRoleModel,
query_db: AsyncSession = Depends(get_db), query_db: Annotated[AsyncSession, DBSessionDependency()],
current_user: CurrentUserModel = Depends(LoginService.get_current_user), current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
data_scope_sql: str = Depends(GetDataScope('SysDept')), data_scope_sql: Annotated[str, DataScopeDependency('SysDept')],
): ) -> Response:
await RoleService.check_role_allowed_services(edit_role) await RoleService.check_role_allowed_services(edit_role)
if not current_user.user.admin: if not current_user.user.admin:
await RoleService.check_role_data_scope_services(query_db, str(edit_role.role_id), data_scope_sql) await RoleService.check_role_data_scope_services(query_db, str(edit_role.role_id), data_scope_sql)
@@ -95,15 +129,21 @@ async def edit_system_role(
return ResponseUtil.success(msg=edit_role_result.message) return ResponseUtil.success(msg=edit_role_result.message)
@roleController.put('/dataScope', dependencies=[Depends(CheckUserInterfaceAuth('system:role:edit'))]) @role_controller.put(
'/dataScope',
summary='编辑角色数据权限接口',
description='用于编辑角色数据权限',
response_model=ResponseBaseModel,
dependencies=[UserInterfaceAuthDependency('system:role:edit')],
)
@Log(title='角色管理', business_type=BusinessType.GRANT) @Log(title='角色管理', business_type=BusinessType.GRANT)
async def edit_system_role_datascope( async def edit_system_role_datascope(
request: Request, request: Request,
role_data_scope: AddRoleModel, role_data_scope: AddRoleModel,
query_db: AsyncSession = Depends(get_db), query_db: Annotated[AsyncSession, DBSessionDependency()],
current_user: CurrentUserModel = Depends(LoginService.get_current_user), current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
data_scope_sql: str = Depends(GetDataScope('SysDept')), data_scope_sql: Annotated[str, DataScopeDependency('SysDept')],
): ) -> Response:
await RoleService.check_role_allowed_services(role_data_scope) await RoleService.check_role_allowed_services(role_data_scope)
if not current_user.user.admin: if not current_user.user.admin:
await RoleService.check_role_data_scope_services(query_db, str(role_data_scope.role_id), data_scope_sql) await RoleService.check_role_data_scope_services(query_db, str(role_data_scope.role_id), data_scope_sql)
@@ -121,15 +161,21 @@ async def edit_system_role_datascope(
return ResponseUtil.success(msg=role_data_scope_result.message) return ResponseUtil.success(msg=role_data_scope_result.message)
@roleController.delete('/{role_ids}', dependencies=[Depends(CheckUserInterfaceAuth('system:role:remove'))]) @role_controller.delete(
'/{role_ids}',
summary='删除角色接口',
description='用于删除角色',
response_model=ResponseBaseModel,
dependencies=[UserInterfaceAuthDependency('system:role:remove')],
)
@Log(title='角色管理', business_type=BusinessType.DELETE) @Log(title='角色管理', business_type=BusinessType.DELETE)
async def delete_system_role( async def delete_system_role(
request: Request, request: Request,
role_ids: str, role_ids: Annotated[str, Path(description='需要删除的角色ID')],
query_db: AsyncSession = Depends(get_db), query_db: Annotated[AsyncSession, DBSessionDependency()],
current_user: CurrentUserModel = Depends(LoginService.get_current_user), current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
data_scope_sql: str = Depends(GetDataScope('SysDept')), data_scope_sql: Annotated[str, DataScopeDependency('SysDept')],
): ) -> Response:
role_id_list = role_ids.split(',') if role_ids else [] role_id_list = role_ids.split(',') if role_ids else []
if role_id_list: if role_id_list:
for role_id in role_id_list: for role_id in role_id_list:
@@ -143,16 +189,20 @@ async def delete_system_role(
return ResponseUtil.success(msg=delete_role_result.message) return ResponseUtil.success(msg=delete_role_result.message)
@roleController.get( @role_controller.get(
'/{role_id}', response_model=RoleModel, dependencies=[Depends(CheckUserInterfaceAuth('system:role:query'))] '/{role_id}',
summary='获取角色详情接口',
description='用于获取指定角色的详细信息',
response_model=DataResponseModel[RoleModel],
dependencies=[UserInterfaceAuthDependency('system:role:query')],
) )
async def query_detail_system_role( async def query_detail_system_role(
request: Request, request: Request,
role_id: int, role_id: Annotated[int, Path(description='角色ID')],
query_db: AsyncSession = Depends(get_db), query_db: Annotated[AsyncSession, DBSessionDependency()],
current_user: CurrentUserModel = Depends(LoginService.get_current_user), current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
data_scope_sql: str = Depends(GetDataScope('SysDept')), data_scope_sql: Annotated[str, DataScopeDependency('SysDept')],
): ) -> Response:
if not current_user.user.admin: if not current_user.user.admin:
await RoleService.check_role_data_scope_services(query_db, str(role_id), data_scope_sql) await RoleService.check_role_data_scope_services(query_db, str(role_id), data_scope_sql)
role_detail_result = await RoleService.role_detail_services(query_db, role_id) role_detail_result = await RoleService.role_detail_services(query_db, role_id)
@@ -161,14 +211,28 @@ async def query_detail_system_role(
return ResponseUtil.success(data=role_detail_result.model_dump(by_alias=True)) return ResponseUtil.success(data=role_detail_result.model_dump(by_alias=True))
@roleController.post('/export', dependencies=[Depends(CheckUserInterfaceAuth('system:role:export'))]) @role_controller.post(
'/export',
summary='导出角色列表接口',
description='用于导出当前符合查询条件的角色列表数据',
response_class=StreamingResponse,
responses={
200: {
'description': '流式返回角色列表excel文件',
'content': {
'application/octet-stream': {},
},
}
},
dependencies=[UserInterfaceAuthDependency('system:role:export')],
)
@Log(title='角色管理', business_type=BusinessType.EXPORT) @Log(title='角色管理', business_type=BusinessType.EXPORT)
async def export_system_role_list( async def export_system_role_list(
request: Request, request: Request,
role_page_query: RolePageQueryModel = Form(), role_page_query: Annotated[RolePageQueryModel, Form()],
query_db: AsyncSession = Depends(get_db), query_db: Annotated[AsyncSession, DBSessionDependency()],
data_scope_sql: str = Depends(GetDataScope('SysDept')), data_scope_sql: Annotated[str, DataScopeDependency('SysDept')],
): ) -> Response:
# 获取全量数据 # 获取全量数据
role_query_result = await RoleService.get_role_list_services( role_query_result = await RoleService.get_role_list_services(
query_db, role_page_query, data_scope_sql, is_page=False query_db, role_page_query, data_scope_sql, is_page=False
@@ -179,15 +243,21 @@ async def export_system_role_list(
return ResponseUtil.streaming(data=bytes2file_response(role_export_result)) return ResponseUtil.streaming(data=bytes2file_response(role_export_result))
@roleController.put('/changeStatus', dependencies=[Depends(CheckUserInterfaceAuth('system:role:edit'))]) @role_controller.put(
'/changeStatus',
summary='修改角色状态接口',
description='用于修改角色状态',
response_model=ResponseBaseModel,
dependencies=[UserInterfaceAuthDependency('system:role:edit')],
)
@Log(title='角色管理', business_type=BusinessType.UPDATE) @Log(title='角色管理', business_type=BusinessType.UPDATE)
async def reset_system_role_status( async def reset_system_role_status(
request: Request, request: Request,
change_role: AddRoleModel, change_role: AddRoleModel,
query_db: AsyncSession = Depends(get_db), query_db: Annotated[AsyncSession, DBSessionDependency()],
current_user: CurrentUserModel = Depends(LoginService.get_current_user), current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
data_scope_sql: str = Depends(GetDataScope('SysDept')), data_scope_sql: Annotated[str, DataScopeDependency('SysDept')],
): ) -> Response:
await RoleService.check_role_allowed_services(change_role) await RoleService.check_role_allowed_services(change_role)
if not current_user.user.admin: if not current_user.user.admin:
await RoleService.check_role_data_scope_services(query_db, str(change_role.role_id), data_scope_sql) await RoleService.check_role_data_scope_services(query_db, str(change_role.role_id), data_scope_sql)
@@ -204,17 +274,19 @@ async def reset_system_role_status(
return ResponseUtil.success(msg=edit_role_result.message) return ResponseUtil.success(msg=edit_role_result.message)
@roleController.get( @role_controller.get(
'/authUser/allocatedList', '/authUser/allocatedList',
response_model=PageResponseModel, summary='获取已分配用户分页列表接口',
dependencies=[Depends(CheckUserInterfaceAuth('system:role:list'))], description='用于获取指定角色已分配的用户分页列表',
response_model=PageResponseModel[UserInfoModel],
dependencies=[UserInterfaceAuthDependency('system:role:list')],
) )
async def get_system_allocated_user_list( async def get_system_allocated_user_list(
request: Request, request: Request,
user_role: UserRolePageQueryModel = Depends(UserRolePageQueryModel.as_query), user_role: Annotated[UserRolePageQueryModel, Query()],
query_db: AsyncSession = Depends(get_db), query_db: Annotated[AsyncSession, DBSessionDependency()],
data_scope_sql: str = Depends(GetDataScope('SysUser')), data_scope_sql: Annotated[str, DataScopeDependency('SysUser')],
): ) -> Response:
role_user_allocated_page_query_result = await RoleService.get_role_user_allocated_list_services( role_user_allocated_page_query_result = await RoleService.get_role_user_allocated_list_services(
query_db, user_role, data_scope_sql, is_page=True query_db, user_role, data_scope_sql, is_page=True
) )
@@ -223,17 +295,19 @@ async def get_system_allocated_user_list(
return ResponseUtil.success(model_content=role_user_allocated_page_query_result) return ResponseUtil.success(model_content=role_user_allocated_page_query_result)
@roleController.get( @role_controller.get(
'/authUser/unallocatedList', '/authUser/unallocatedList',
response_model=PageResponseModel, summary='获取未分配用户分页列表接口',
dependencies=[Depends(CheckUserInterfaceAuth('system:role:list'))], description='用于获取指定角色未分配的用户分页列表',
response_model=PageResponseModel[UserInfoModel],
dependencies=[UserInterfaceAuthDependency('system:role:list')],
) )
async def get_system_unallocated_user_list( async def get_system_unallocated_user_list(
request: Request, request: Request,
user_role: UserRolePageQueryModel = Depends(UserRolePageQueryModel.as_query), user_role: Annotated[UserRolePageQueryModel, Query()],
query_db: AsyncSession = Depends(get_db), query_db: Annotated[AsyncSession, DBSessionDependency()],
data_scope_sql: str = Depends(GetDataScope('SysUser')), data_scope_sql: Annotated[str, DataScopeDependency('SysUser')],
): ) -> Response:
role_user_unallocated_page_query_result = await RoleService.get_role_user_unallocated_list_services( role_user_unallocated_page_query_result = await RoleService.get_role_user_unallocated_list_services(
query_db, user_role, data_scope_sql, is_page=True query_db, user_role, data_scope_sql, is_page=True
) )
@@ -242,15 +316,21 @@ async def get_system_unallocated_user_list(
return ResponseUtil.success(model_content=role_user_unallocated_page_query_result) return ResponseUtil.success(model_content=role_user_unallocated_page_query_result)
@roleController.put('/authUser/selectAll', dependencies=[Depends(CheckUserInterfaceAuth('system:role:edit'))]) @role_controller.put(
'/authUser/selectAll',
summary='分配用户给角色接口',
description='用于给指定角色分配用户',
response_model=ResponseBaseModel,
dependencies=[UserInterfaceAuthDependency('system:role:edit')],
)
@Log(title='角色管理', business_type=BusinessType.GRANT) @Log(title='角色管理', business_type=BusinessType.GRANT)
async def add_system_role_user( async def add_system_role_user(
request: Request, request: Request,
add_role_user: CrudUserRoleModel = Depends(CrudUserRoleModel.as_query), add_role_user: Annotated[CrudUserRoleModel, Query()],
query_db: AsyncSession = Depends(get_db), query_db: Annotated[AsyncSession, DBSessionDependency()],
current_user: CurrentUserModel = Depends(LoginService.get_current_user), current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
data_scope_sql: str = Depends(GetDataScope('SysDept')), data_scope_sql: Annotated[str, DataScopeDependency('SysDept')],
): ) -> Response:
if not current_user.user.admin: if not current_user.user.admin:
await RoleService.check_role_data_scope_services(query_db, str(add_role_user.role_id), data_scope_sql) await RoleService.check_role_data_scope_services(query_db, str(add_role_user.role_id), data_scope_sql)
add_role_user_result = await UserService.add_user_role_services(query_db, add_role_user) add_role_user_result = await UserService.add_user_role_services(query_db, add_role_user)
@@ -259,24 +339,38 @@ async def add_system_role_user(
return ResponseUtil.success(msg=add_role_user_result.message) return ResponseUtil.success(msg=add_role_user_result.message)
@roleController.put('/authUser/cancel', dependencies=[Depends(CheckUserInterfaceAuth('system:role:edit'))]) @role_controller.put(
'/authUser/cancel',
summary='取消分配用户给角色接口',
description='用于取消指定用户分配给角色',
response_model=ResponseBaseModel,
dependencies=[UserInterfaceAuthDependency('system:role:edit')],
)
@Log(title='角色管理', business_type=BusinessType.GRANT) @Log(title='角色管理', business_type=BusinessType.GRANT)
async def cancel_system_role_user( async def cancel_system_role_user(
request: Request, cancel_user_role: CrudUserRoleModel, query_db: AsyncSession = Depends(get_db) request: Request,
): cancel_user_role: CrudUserRoleModel,
query_db: Annotated[AsyncSession, DBSessionDependency()],
) -> Response:
cancel_user_role_result = await UserService.delete_user_role_services(query_db, cancel_user_role) cancel_user_role_result = await UserService.delete_user_role_services(query_db, cancel_user_role)
logger.info(cancel_user_role_result.message) logger.info(cancel_user_role_result.message)
return ResponseUtil.success(msg=cancel_user_role_result.message) return ResponseUtil.success(msg=cancel_user_role_result.message)
@roleController.put('/authUser/cancelAll', dependencies=[Depends(CheckUserInterfaceAuth('system:role:edit'))]) @role_controller.put(
'/authUser/cancelAll',
summary='批量取消分配用户给角色接口',
description='用于批量取消用户分配给角色',
response_model=ResponseBaseModel,
dependencies=[UserInterfaceAuthDependency('system:role:edit')],
)
@Log(title='角色管理', business_type=BusinessType.GRANT) @Log(title='角色管理', business_type=BusinessType.GRANT)
async def batch_cancel_system_role_user( async def batch_cancel_system_role_user(
request: Request, request: Request,
batch_cancel_user_role: CrudUserRoleModel = Depends(CrudUserRoleModel.as_query), batch_cancel_user_role: Annotated[CrudUserRoleModel, Query()],
query_db: AsyncSession = Depends(get_db), query_db: Annotated[AsyncSession, DBSessionDependency()],
): ) -> Response:
batch_cancel_user_role_result = await UserService.delete_user_role_services(query_db, batch_cancel_user_role) batch_cancel_user_role_result = await UserService.delete_user_role_services(query_db, batch_cancel_user_role)
logger.info(batch_cancel_user_role_result.message) logger.info(batch_cancel_user_role_result.message)
@@ -1,19 +1,27 @@
from fastapi import APIRouter, Depends, Request from fastapi import Request, Response
from module_admin.aspect.interface_auth import CheckUserInterfaceAuth
from common.aspect.interface_auth import UserInterfaceAuthDependency
from common.aspect.pre_auth import PreAuthDependency
from common.router import APIRouterPro
from common.vo import DataResponseModel
from module_admin.entity.vo.server_vo import ServerMonitorModel from module_admin.entity.vo.server_vo import ServerMonitorModel
from module_admin.service.login_service import LoginService
from module_admin.service.server_service import ServerService from module_admin.service.server_service import ServerService
from utils.response_util import ResponseUtil
from utils.log_util import logger from utils.log_util import logger
from utils.response_util import ResponseUtil
server_controller = APIRouterPro(
serverController = APIRouter(prefix='/monitor/server', dependencies=[Depends(LoginService.get_current_user)]) prefix='/monitor/server', order_num=14, tags=['系统监控-服务监控'], dependencies=[PreAuthDependency()]
@serverController.get(
'', response_model=ServerMonitorModel, dependencies=[Depends(CheckUserInterfaceAuth('monitor:server:list'))]
) )
async def get_monitor_server_info(request: Request):
@server_controller.get(
'',
summary='获取服务器监控信息接口',
description='用于获取当前服务器的监控信息',
response_model=DataResponseModel[ServerMonitorModel],
dependencies=[UserInterfaceAuthDependency('monitor:server:list')],
)
async def get_monitor_server_info(request: Request) -> Response:
# 获取全量数据 # 获取全量数据
server_info_query_result = await ServerService.get_server_monitor_info() server_info_query_result = await ServerService.get_server_monitor_info()
logger.info('获取成功') logger.info('获取成功')
@@ -1,18 +1,26 @@
import os import os
from datetime import datetime from datetime import datetime
from fastapi import APIRouter, Depends, File, Form, Query, Request, UploadFile from typing import Annotated, Literal, Optional, Union
from sqlalchemy.ext.asyncio import AsyncSession
from typing import Literal, Optional, Union import aiofiles
from fastapi import File, Form, Path, Query, Request, Response, UploadFile
from fastapi.responses import StreamingResponse
from pydantic_validation_decorator import ValidateFields from pydantic_validation_decorator import ValidateFields
from config.get_db import get_db from sqlalchemy.ext.asyncio import AsyncSession
from config.enums import BusinessType
from common.annotation.log_annotation import Log
from common.aspect.data_scope import DataScopeDependency
from common.aspect.db_seesion import DBSessionDependency
from common.aspect.interface_auth import UserInterfaceAuthDependency
from common.aspect.pre_auth import CurrentUserDependency, PreAuthDependency
from common.enums import BusinessType
from common.router import APIRouterPro
from common.vo import DataResponseModel, DynamicResponseModel, PageResponseModel, ResponseBaseModel
from config.env import UploadConfig from config.env import UploadConfig
from module_admin.annotation.log_annotation import Log from module_admin.entity.vo.dept_vo import DeptModel, DeptTreeModel
from module_admin.aspect.data_scope import GetDataScope
from module_admin.aspect.interface_auth import CheckUserInterfaceAuth
from module_admin.entity.vo.dept_vo import DeptModel
from module_admin.entity.vo.user_vo import ( from module_admin.entity.vo.user_vo import (
AddUserModel, AddUserModel,
AvatarModel,
CrudUserRoleModel, CrudUserRoleModel,
CurrentUserModel, CurrentUserModel,
DeleteUserModel, DeleteUserModel,
@@ -26,41 +34,53 @@ from module_admin.entity.vo.user_vo import (
UserProfileModel, UserProfileModel,
UserRoleQueryModel, UserRoleQueryModel,
UserRoleResponseModel, UserRoleResponseModel,
UserRowModel,
) )
from module_admin.service.login_service import LoginService
from module_admin.service.user_service import UserService
from module_admin.service.role_service import RoleService
from module_admin.service.dept_service import DeptService from module_admin.service.dept_service import DeptService
from module_admin.service.role_service import RoleService
from module_admin.service.user_service import UserService
from utils.common_util import bytes2file_response from utils.common_util import bytes2file_response
from utils.log_util import logger from utils.log_util import logger
from utils.page_util import PageResponseModel
from utils.pwd_util import PwdUtil from utils.pwd_util import PwdUtil
from utils.response_util import ResponseUtil from utils.response_util import ResponseUtil
from utils.upload_util import UploadUtil from utils.upload_util import UploadUtil
user_controller = APIRouterPro(
userController = APIRouter(prefix='/system/user', dependencies=[Depends(LoginService.get_current_user)]) prefix='/system/user', order_num=3, tags=['系统管理-用户管理'], dependencies=[PreAuthDependency()]
)
@userController.get('/deptTree', dependencies=[Depends(CheckUserInterfaceAuth('system:user:list'))]) @user_controller.get(
'/deptTree',
summary='获取部门树接口',
description='用于获取当前登录用户可见的部门树',
response_model=DataResponseModel[list[DeptTreeModel]],
dependencies=[UserInterfaceAuthDependency('system:user:list')],
)
async def get_system_dept_tree( async def get_system_dept_tree(
request: Request, query_db: AsyncSession = Depends(get_db), data_scope_sql: str = Depends(GetDataScope('SysDept')) request: Request,
): query_db: Annotated[AsyncSession, DBSessionDependency()],
dept_query_result = await DeptService.get_dept_tree_services(query_db, DeptModel(**{}), data_scope_sql) data_scope_sql: Annotated[str, DataScopeDependency('SysDept')],
) -> Response:
dept_query_result = await DeptService.get_dept_tree_services(query_db, DeptModel(), data_scope_sql)
logger.info('获取成功') logger.info('获取成功')
return ResponseUtil.success(data=dept_query_result) return ResponseUtil.success(data=dept_query_result)
@userController.get( @user_controller.get(
'/list', response_model=PageResponseModel, dependencies=[Depends(CheckUserInterfaceAuth('system:user:list'))] '/list',
summary='获取用户分页列表接口',
description='用于获取用户分页列表',
response_model=PageResponseModel[UserRowModel],
dependencies=[UserInterfaceAuthDependency('system:user:list')],
) )
async def get_system_user_list( async def get_system_user_list(
request: Request, request: Request,
user_page_query: UserPageQueryModel = Depends(UserPageQueryModel.as_query), user_page_query: Annotated[UserPageQueryModel, Query()],
query_db: AsyncSession = Depends(get_db), query_db: Annotated[AsyncSession, DBSessionDependency()],
data_scope_sql: str = Depends(GetDataScope('SysUser')), data_scope_sql: Annotated[str, DataScopeDependency('SysUser')],
): ) -> Response:
# 获取分页数据 # 获取分页数据
user_page_query_result = await UserService.get_user_list_services( user_page_query_result = await UserService.get_user_list_services(
query_db, user_page_query, data_scope_sql, is_page=True query_db, user_page_query, data_scope_sql, is_page=True
@@ -70,17 +90,23 @@ async def get_system_user_list(
return ResponseUtil.success(model_content=user_page_query_result) return ResponseUtil.success(model_content=user_page_query_result)
@userController.post('', dependencies=[Depends(CheckUserInterfaceAuth('system:user:add'))]) @user_controller.post(
'',
summary='新增用户接口',
description='用于新增用户',
response_model=ResponseBaseModel,
dependencies=[UserInterfaceAuthDependency('system:user:add')],
)
@ValidateFields(validate_model='add_user') @ValidateFields(validate_model='add_user')
@Log(title='用户管理', business_type=BusinessType.INSERT) @Log(title='用户管理', business_type=BusinessType.INSERT)
async def add_system_user( async def add_system_user(
request: Request, request: Request,
add_user: AddUserModel, add_user: AddUserModel,
query_db: AsyncSession = Depends(get_db), query_db: Annotated[AsyncSession, DBSessionDependency()],
current_user: CurrentUserModel = Depends(LoginService.get_current_user), current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
dept_data_scope_sql: str = Depends(GetDataScope('SysDept')), dept_data_scope_sql: Annotated[str, DataScopeDependency('SysDept')],
role_data_scope_sql: str = Depends(GetDataScope('SysDept')), role_data_scope_sql: Annotated[str, DataScopeDependency('SysDept')],
): ) -> Response:
if not current_user.user.admin: if not current_user.user.admin:
await DeptService.check_dept_data_scope_services(query_db, add_user.dept_id, dept_data_scope_sql) await DeptService.check_dept_data_scope_services(query_db, add_user.dept_id, dept_data_scope_sql)
await RoleService.check_role_data_scope_services( await RoleService.check_role_data_scope_services(
@@ -97,18 +123,24 @@ async def add_system_user(
return ResponseUtil.success(msg=add_user_result.message) return ResponseUtil.success(msg=add_user_result.message)
@userController.put('', dependencies=[Depends(CheckUserInterfaceAuth('system:user:edit'))]) @user_controller.put(
'',
summary='编辑用户接口',
description='用于编辑用户',
response_model=ResponseBaseModel,
dependencies=[UserInterfaceAuthDependency('system:user:edit')],
)
@ValidateFields(validate_model='edit_user') @ValidateFields(validate_model='edit_user')
@Log(title='用户管理', business_type=BusinessType.UPDATE) @Log(title='用户管理', business_type=BusinessType.UPDATE)
async def edit_system_user( async def edit_system_user(
request: Request, request: Request,
edit_user: EditUserModel, edit_user: EditUserModel,
query_db: AsyncSession = Depends(get_db), query_db: Annotated[AsyncSession, DBSessionDependency()],
current_user: CurrentUserModel = Depends(LoginService.get_current_user), current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
user_data_scope_sql: str = Depends(GetDataScope('SysUser')), user_data_scope_sql: Annotated[str, DataScopeDependency('SysUser')],
dept_data_scope_sql: str = Depends(GetDataScope('SysDept')), dept_data_scope_sql: Annotated[str, DataScopeDependency('SysDept')],
role_data_scope_sql: str = Depends(GetDataScope('SysDept')), role_data_scope_sql: Annotated[str, DataScopeDependency('SysDept')],
): ) -> Response:
await UserService.check_user_allowed_services(edit_user) await UserService.check_user_allowed_services(edit_user)
if not current_user.user.admin: if not current_user.user.admin:
await UserService.check_user_data_scope_services(query_db, edit_user.user_id, user_data_scope_sql) await UserService.check_user_data_scope_services(query_db, edit_user.user_id, user_data_scope_sql)
@@ -124,15 +156,21 @@ async def edit_system_user(
return ResponseUtil.success(msg=edit_user_result.message) return ResponseUtil.success(msg=edit_user_result.message)
@userController.delete('/{user_ids}', dependencies=[Depends(CheckUserInterfaceAuth('system:user:remove'))]) @user_controller.delete(
'/{user_ids}',
summary='删除用户接口',
description='用于删除用户',
response_model=ResponseBaseModel,
dependencies=[UserInterfaceAuthDependency('system:user:remove')],
)
@Log(title='用户管理', business_type=BusinessType.DELETE) @Log(title='用户管理', business_type=BusinessType.DELETE)
async def delete_system_user( async def delete_system_user(
request: Request, request: Request,
user_ids: str, user_ids: Annotated[str, Path(description='需要删除的用户ID')],
query_db: AsyncSession = Depends(get_db), query_db: Annotated[AsyncSession, DBSessionDependency()],
current_user: CurrentUserModel = Depends(LoginService.get_current_user), current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
data_scope_sql: str = Depends(GetDataScope('SysUser')), data_scope_sql: Annotated[str, DataScopeDependency('SysUser')],
): ) -> Response:
user_id_list = user_ids.split(',') if user_ids else [] user_id_list = user_ids.split(',') if user_ids else []
if user_id_list: if user_id_list:
if current_user.user.user_id in list(map(int, user_id_list)): if current_user.user.user_id in list(map(int, user_id_list)):
@@ -150,15 +188,21 @@ async def delete_system_user(
return ResponseUtil.success(msg=delete_user_result.message) return ResponseUtil.success(msg=delete_user_result.message)
@userController.put('/resetPwd', dependencies=[Depends(CheckUserInterfaceAuth('system:user:resetPwd'))]) @user_controller.put(
'/resetPwd',
summary='重置用户密码接口',
description='用于重置用户密码',
response_model=ResponseBaseModel,
dependencies=[UserInterfaceAuthDependency('system:user:resetPwd')],
)
@Log(title='用户管理', business_type=BusinessType.UPDATE) @Log(title='用户管理', business_type=BusinessType.UPDATE)
async def reset_system_user_pwd( async def reset_system_user_pwd(
request: Request, request: Request,
reset_user: EditUserModel, reset_user: EditUserModel,
query_db: AsyncSession = Depends(get_db), query_db: Annotated[AsyncSession, DBSessionDependency()],
current_user: CurrentUserModel = Depends(LoginService.get_current_user), current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
data_scope_sql: str = Depends(GetDataScope('SysUser')), data_scope_sql: Annotated[str, DataScopeDependency('SysUser')],
): ) -> Response:
await UserService.check_user_allowed_services(reset_user) await UserService.check_user_allowed_services(reset_user)
if not current_user.user.admin: if not current_user.user.admin:
await UserService.check_user_data_scope_services(query_db, reset_user.user_id, data_scope_sql) await UserService.check_user_data_scope_services(query_db, reset_user.user_id, data_scope_sql)
@@ -176,15 +220,21 @@ async def reset_system_user_pwd(
return ResponseUtil.success(msg=edit_user_result.message) return ResponseUtil.success(msg=edit_user_result.message)
@userController.put('/changeStatus', dependencies=[Depends(CheckUserInterfaceAuth('system:user:edit'))]) @user_controller.put(
'/changeStatus',
summary='修改用户状态接口',
description='用于修改用户状态',
response_model=ResponseBaseModel,
dependencies=[UserInterfaceAuthDependency('system:user:edit')],
)
@Log(title='用户管理', business_type=BusinessType.UPDATE) @Log(title='用户管理', business_type=BusinessType.UPDATE)
async def change_system_user_status( async def change_system_user_status(
request: Request, request: Request,
change_user: EditUserModel, change_user: EditUserModel,
query_db: AsyncSession = Depends(get_db), query_db: Annotated[AsyncSession, DBSessionDependency()],
current_user: CurrentUserModel = Depends(LoginService.get_current_user), current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
data_scope_sql: str = Depends(GetDataScope('SysUser')), data_scope_sql: Annotated[str, DataScopeDependency('SysUser')],
): ) -> Response:
await UserService.check_user_allowed_services(change_user) await UserService.check_user_allowed_services(change_user)
if not current_user.user.admin: if not current_user.user.admin:
await UserService.check_user_data_scope_services(query_db, change_user.user_id, data_scope_sql) await UserService.check_user_data_scope_services(query_db, change_user.user_id, data_scope_sql)
@@ -201,31 +251,44 @@ async def change_system_user_status(
return ResponseUtil.success(msg=edit_user_result.message) return ResponseUtil.success(msg=edit_user_result.message)
@userController.get('/profile', response_model=UserProfileModel) @user_controller.get(
'/profile',
summary='获取用户个人信息接口',
description='用于获取当前登录用户的个人信息',
response_model=DynamicResponseModel[UserProfileModel],
)
async def query_detail_system_user_profile( async def query_detail_system_user_profile(
request: Request, request: Request,
query_db: AsyncSession = Depends(get_db), query_db: Annotated[AsyncSession, DBSessionDependency()],
current_user: CurrentUserModel = Depends(LoginService.get_current_user), current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
): ) -> Response:
profile_user_result = await UserService.user_profile_services(query_db, current_user.user.user_id) profile_user_result = await UserService.user_profile_services(query_db, current_user.user.user_id)
logger.info(f'获取user_id为{current_user.user.user_id}的信息成功') logger.info(f'获取user_id为{current_user.user.user_id}的信息成功')
return ResponseUtil.success(model_content=profile_user_result) return ResponseUtil.success(model_content=profile_user_result)
@userController.get( @user_controller.get(
'/{user_id}', response_model=UserDetailModel, dependencies=[Depends(CheckUserInterfaceAuth('system:user:query'))] '/{user_id}',
summary='获取用户详情接口',
description='用于获取指定用户的详情信息',
response_model=DynamicResponseModel[UserDetailModel],
dependencies=[UserInterfaceAuthDependency('system:user:query')],
) )
@userController.get( @user_controller.get(
'/', response_model=UserDetailModel, dependencies=[Depends(CheckUserInterfaceAuth('system:user:query'))] '/',
summary='获取用户岗位和角色列表接口',
description='用于获取当前登录用户可见的岗位和角色列表',
response_model=DynamicResponseModel[UserDetailModel],
dependencies=[UserInterfaceAuthDependency('system:user:query')],
) )
async def query_detail_system_user( async def query_detail_system_user(
request: Request, request: Request,
query_db: Annotated[AsyncSession, DBSessionDependency()],
current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
data_scope_sql: Annotated[str, DataScopeDependency('SysUser')],
user_id: Optional[Union[int, Literal['']]] = '', user_id: Optional[Union[int, Literal['']]] = '',
query_db: AsyncSession = Depends(get_db), ) -> Response:
current_user: CurrentUserModel = Depends(LoginService.get_current_user),
data_scope_sql: str = Depends(GetDataScope('SysUser')),
):
if user_id and not current_user.user.admin: if user_id and not current_user.user.admin:
await UserService.check_user_data_scope_services(query_db, user_id, data_scope_sql) await UserService.check_user_data_scope_services(query_db, user_id, data_scope_sql)
detail_user_result = await UserService.user_detail_services(query_db, user_id) detail_user_result = await UserService.user_detail_services(query_db, user_id)
@@ -234,14 +297,19 @@ async def query_detail_system_user(
return ResponseUtil.success(model_content=detail_user_result) return ResponseUtil.success(model_content=detail_user_result)
@userController.post('/profile/avatar') @user_controller.post(
'/profile/avatar',
summary='修改用户头像接口',
description='用于修改当前登录用户的头像',
response_model=DynamicResponseModel[AvatarModel],
)
@Log(title='个人信息', business_type=BusinessType.UPDATE) @Log(title='个人信息', business_type=BusinessType.UPDATE)
async def change_system_user_profile_avatar( async def change_system_user_profile_avatar(
request: Request, request: Request,
avatarfile: bytes = File(), avatarfile: Annotated[bytes, File()],
query_db: AsyncSession = Depends(get_db), query_db: Annotated[AsyncSession, DBSessionDependency()],
current_user: CurrentUserModel = Depends(LoginService.get_current_user), current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
): ) -> Response:
if avatarfile: if avatarfile:
relative_path = ( relative_path = (
f'avatar/{datetime.now().strftime("%Y")}/{datetime.now().strftime("%m")}/{datetime.now().strftime("%d")}' f'avatar/{datetime.now().strftime("%Y")}/{datetime.now().strftime("%m")}/{datetime.now().strftime("%d")}'
@@ -253,8 +321,8 @@ async def change_system_user_profile_avatar(
pass pass
avatar_name = f'avatar_{datetime.now().strftime("%Y%m%d%H%M%S")}{UploadConfig.UPLOAD_MACHINE}{UploadUtil.generate_random_number()}.png' avatar_name = f'avatar_{datetime.now().strftime("%Y%m%d%H%M%S")}{UploadConfig.UPLOAD_MACHINE}{UploadUtil.generate_random_number()}.png'
avatar_path = os.path.join(dir_path, avatar_name) avatar_path = os.path.join(dir_path, avatar_name)
with open(avatar_path, 'wb') as f: async with aiofiles.open(avatar_path, 'wb') as f:
f.write(avatarfile) await f.write(avatarfile)
edit_user = EditUserModel( edit_user = EditUserModel(
userId=current_user.user.user_id, userId=current_user.user.user_id,
avatar=f'{UploadConfig.UPLOAD_PREFIX}/{relative_path}/{avatar_name}', avatar=f'{UploadConfig.UPLOAD_PREFIX}/{relative_path}/{avatar_name}',
@@ -265,18 +333,23 @@ async def change_system_user_profile_avatar(
edit_user_result = await UserService.edit_user_services(query_db, edit_user) edit_user_result = await UserService.edit_user_services(query_db, edit_user)
logger.info(edit_user_result.message) logger.info(edit_user_result.message)
return ResponseUtil.success(dict_content={'imgUrl': edit_user.avatar}, msg=edit_user_result.message) return ResponseUtil.success(model_content=AvatarModel(imgUrl=edit_user.avatar), msg=edit_user_result.message)
return ResponseUtil.failure(msg='上传图片异常,请联系管理员') return ResponseUtil.failure(msg='上传图片异常,请联系管理员')
@userController.put('/profile') @user_controller.put(
'/profile',
summary='修改用户个人信息接口',
description='用于修改当前登录用户的个人信息',
response_model=ResponseBaseModel,
)
@Log(title='个人信息', business_type=BusinessType.UPDATE) @Log(title='个人信息', business_type=BusinessType.UPDATE)
async def change_system_user_profile_info( async def change_system_user_profile_info(
request: Request, request: Request,
user_info: UserInfoModel, user_info: UserInfoModel,
query_db: AsyncSession = Depends(get_db), query_db: Annotated[AsyncSession, DBSessionDependency()],
current_user: CurrentUserModel = Depends(LoginService.get_current_user), current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
): ) -> Response:
edit_user = EditUserModel( edit_user = EditUserModel(
**user_info.model_dump(exclude_unset=True, by_alias=True, exclude={'role_ids', 'post_ids'}), **user_info.model_dump(exclude_unset=True, by_alias=True, exclude={'role_ids', 'post_ids'}),
userId=current_user.user.user_id, userId=current_user.user.user_id,
@@ -293,14 +366,19 @@ async def change_system_user_profile_info(
return ResponseUtil.success(msg=edit_user_result.message) return ResponseUtil.success(msg=edit_user_result.message)
@userController.put('/profile/updatePwd') @user_controller.put(
'/profile/updatePwd',
summary='修改用户密码接口',
description='用于修改当前登录用户的密码',
response_model=ResponseBaseModel,
)
@Log(title='个人信息', business_type=BusinessType.UPDATE) @Log(title='个人信息', business_type=BusinessType.UPDATE)
async def reset_system_user_password( async def reset_system_user_password(
request: Request, request: Request,
reset_password: ResetPasswordModel, reset_password: ResetPasswordModel,
query_db: AsyncSession = Depends(get_db), query_db: Annotated[AsyncSession, DBSessionDependency()],
current_user: CurrentUserModel = Depends(LoginService.get_current_user), current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
): ) -> Response:
reset_user = ResetUserModel( reset_user = ResetUserModel(
userId=current_user.user.user_id, userId=current_user.user.user_id,
oldPassword=reset_password.old_password, oldPassword=reset_password.old_password,
@@ -315,17 +393,23 @@ async def reset_system_user_password(
return ResponseUtil.success(msg=reset_user_result.message) return ResponseUtil.success(msg=reset_user_result.message)
@userController.post('/importData', dependencies=[Depends(CheckUserInterfaceAuth('system:user:import'))]) @user_controller.post(
'/importData',
summary='批量导入用户接口',
description='用于批量导入用户数据',
response_model=ResponseBaseModel,
dependencies=[UserInterfaceAuthDependency('system:user:import')],
)
@Log(title='用户管理', business_type=BusinessType.IMPORT) @Log(title='用户管理', business_type=BusinessType.IMPORT)
async def batch_import_system_user( async def batch_import_system_user(
request: Request, request: Request,
file: UploadFile = File(...), file: Annotated[UploadFile, File(...)],
update_support: bool = Query(alias='updateSupport'), update_support: Annotated[bool, Query(alias='updateSupport')],
query_db: AsyncSession = Depends(get_db), query_db: Annotated[AsyncSession, DBSessionDependency()],
current_user: CurrentUserModel = Depends(LoginService.get_current_user), current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
user_data_scope_sql: str = Depends(GetDataScope('SysUser')), user_data_scope_sql: Annotated[str, DataScopeDependency('SysUser')],
dept_data_scope_sql: str = Depends(GetDataScope('SysDept')), dept_data_scope_sql: Annotated[str, DataScopeDependency('SysDept')],
): ) -> Response:
batch_import_result = await UserService.batch_import_user_services( batch_import_result = await UserService.batch_import_user_services(
request, query_db, file, update_support, current_user, user_data_scope_sql, dept_data_scope_sql request, query_db, file, update_support, current_user, user_data_scope_sql, dept_data_scope_sql
) )
@@ -334,22 +418,52 @@ async def batch_import_system_user(
return ResponseUtil.success(msg=batch_import_result.message) return ResponseUtil.success(msg=batch_import_result.message)
@userController.post('/importTemplate', dependencies=[Depends(CheckUserInterfaceAuth('system:user:import'))]) @user_controller.post(
async def export_system_user_template(request: Request, query_db: AsyncSession = Depends(get_db)): '/importTemplate',
summary='获取用户导入模板接口',
description='用于获取用户导入模板excel文件',
response_class=StreamingResponse,
responses={
200: {
'description': '流式返回导入用户模板excel文件',
'content': {
'application/octet-stream': {},
},
}
},
dependencies=[UserInterfaceAuthDependency('system:user:import')],
)
async def export_system_user_template(
request: Request, query_db: Annotated[AsyncSession, DBSessionDependency()]
) -> Response:
user_import_template_result = await UserService.get_user_import_template_services() user_import_template_result = await UserService.get_user_import_template_services()
logger.info('获取成功') logger.info('获取成功')
return ResponseUtil.streaming(data=bytes2file_response(user_import_template_result)) return ResponseUtil.streaming(data=bytes2file_response(user_import_template_result))
@userController.post('/export', dependencies=[Depends(CheckUserInterfaceAuth('system:user:export'))]) @user_controller.post(
'/export',
summary='导出用户列表接口',
description='用于导出当前符合查询条件的用户列表数据',
response_class=StreamingResponse,
responses={
200: {
'description': '流式返回用户列表excel文件',
'content': {
'application/octet-stream': {},
},
}
},
dependencies=[UserInterfaceAuthDependency('system:user:export')],
)
@Log(title='用户管理', business_type=BusinessType.EXPORT) @Log(title='用户管理', business_type=BusinessType.EXPORT)
async def export_system_user_list( async def export_system_user_list(
request: Request, request: Request,
user_page_query: UserPageQueryModel = Form(), user_page_query: Annotated[UserPageQueryModel, Form()],
query_db: AsyncSession = Depends(get_db), query_db: Annotated[AsyncSession, DBSessionDependency()],
data_scope_sql: str = Depends(GetDataScope('SysUser')), data_scope_sql: Annotated[str, DataScopeDependency('SysUser')],
): ) -> Response:
# 获取全量数据 # 获取全量数据
user_query_result = await UserService.get_user_list_services( user_query_result = await UserService.get_user_list_services(
query_db, user_page_query, data_scope_sql, is_page=False query_db, user_page_query, data_scope_sql, is_page=False
@@ -360,12 +474,18 @@ async def export_system_user_list(
return ResponseUtil.streaming(data=bytes2file_response(user_export_result)) return ResponseUtil.streaming(data=bytes2file_response(user_export_result))
@userController.get( @user_controller.get(
'/authRole/{user_id}', '/authRole/{user_id}',
response_model=UserRoleResponseModel, summary='获取用户已分配角色列表接口',
dependencies=[Depends(CheckUserInterfaceAuth('system:user:query'))], description='用于获取指定用户已分配的角色列表',
response_model=DynamicResponseModel[UserRoleResponseModel],
dependencies=[UserInterfaceAuthDependency('system:user:query')],
) )
async def get_system_allocated_role_list(request: Request, user_id: int, query_db: AsyncSession = Depends(get_db)): async def get_system_allocated_role_list(
request: Request,
user_id: Annotated[int, Path(description='用户ID')],
query_db: Annotated[AsyncSession, DBSessionDependency()],
) -> Response:
user_role_query = UserRoleQueryModel(userId=user_id) user_role_query = UserRoleQueryModel(userId=user_id)
user_role_allocated_query_result = await UserService.get_user_role_allocated_list_services( user_role_allocated_query_result = await UserService.get_user_role_allocated_list_services(
query_db, user_role_query query_db, user_role_query
@@ -375,21 +495,23 @@ async def get_system_allocated_role_list(request: Request, user_id: int, query_d
return ResponseUtil.success(model_content=user_role_allocated_query_result) return ResponseUtil.success(model_content=user_role_allocated_query_result)
@userController.put( @user_controller.put(
'/authRole', '/authRole',
response_model=UserRoleResponseModel, summary='给用户分配角色接口',
dependencies=[Depends(CheckUserInterfaceAuth('system:user:edit'))], description='用于给指定用户分配角色',
response_model=ResponseBaseModel,
dependencies=[UserInterfaceAuthDependency('system:user:edit')],
) )
@Log(title='用户管理', business_type=BusinessType.GRANT) @Log(title='用户管理', business_type=BusinessType.GRANT)
async def update_system_role_user( async def update_system_role_user(
request: Request, request: Request,
user_id: int = Query(alias='userId'), user_id: Annotated[int, Query(alias='userId')],
role_ids: str = Query(alias='roleIds'), role_ids: Annotated[str, Query(alias='roleIds')],
query_db: AsyncSession = Depends(get_db), query_db: Annotated[AsyncSession, DBSessionDependency()],
current_user: CurrentUserModel = Depends(LoginService.get_current_user), current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
user_data_scope_sql: str = Depends(GetDataScope('SysUser')), user_data_scope_sql: Annotated[str, DataScopeDependency('SysUser')],
role_data_scope_sql: str = Depends(GetDataScope('SysDept')), role_data_scope_sql: Annotated[str, DataScopeDependency('SysDept')],
): ) -> Response:
if not current_user.user.admin: if not current_user.user.admin:
await UserService.check_user_data_scope_services(query_db, user_id, user_data_scope_sql) await UserService.check_user_data_scope_services(query_db, user_id, user_data_scope_sql)
await RoleService.check_role_data_scope_services(query_db, role_ids, role_data_scope_sql) await RoleService.check_role_data_scope_services(query_db, role_ids, role_data_scope_sql)
@@ -1,6 +1,10 @@
from datetime import datetime, time from datetime import datetime, time
from typing import Any, Union
from sqlalchemy import delete, select, update from sqlalchemy import delete, select, update
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from common.vo import PageModel
from module_admin.entity.do.config_do import SysConfig from module_admin.entity.do.config_do import SysConfig
from module_admin.entity.vo.config_vo import ConfigModel, ConfigPageQueryModel from module_admin.entity.vo.config_vo import ConfigModel, ConfigPageQueryModel
from utils.page_util import PageUtil from utils.page_util import PageUtil
@@ -12,7 +16,7 @@ class ConfigDao:
""" """
@classmethod @classmethod
async def get_config_detail_by_id(cls, db: AsyncSession, config_id: int): async def get_config_detail_by_id(cls, db: AsyncSession, config_id: int) -> Union[SysConfig, None]:
""" """
根据参数配置id获取参数配置详细信息 根据参数配置id获取参数配置详细信息
@@ -25,7 +29,7 @@ class ConfigDao:
return config_info return config_info
@classmethod @classmethod
async def get_config_detail_by_info(cls, db: AsyncSession, config: ConfigModel): async def get_config_detail_by_info(cls, db: AsyncSession, config: ConfigModel) -> Union[SysConfig, None]:
""" """
根据参数配置参数获取参数配置信息 根据参数配置参数获取参数配置信息
@@ -49,7 +53,9 @@ class ConfigDao:
return config_info return config_info
@classmethod @classmethod
async def get_config_list(cls, db: AsyncSession, query_object: ConfigPageQueryModel, is_page: bool = False): async def get_config_list(
cls, db: AsyncSession, query_object: ConfigPageQueryModel, is_page: bool = False
) -> Union[PageModel, list[dict[str, Any]]]:
""" """
根据查询参数获取参数配置列表信息 根据查询参数获取参数配置列表信息
@@ -74,12 +80,14 @@ class ConfigDao:
.order_by(SysConfig.config_id) .order_by(SysConfig.config_id)
.distinct() .distinct()
) )
config_list = await PageUtil.paginate(db, query, query_object.page_num, query_object.page_size, is_page) config_list: Union[PageModel, list[dict[str, Any]]] = await PageUtil.paginate(
db, query, query_object.page_num, query_object.page_size, is_page
)
return config_list return config_list
@classmethod @classmethod
async def add_config_dao(cls, db: AsyncSession, config: ConfigModel): async def add_config_dao(cls, db: AsyncSession, config: ConfigModel) -> SysConfig:
""" """
新增参数配置数据库操作 新增参数配置数据库操作
@@ -94,7 +102,7 @@ class ConfigDao:
return db_config return db_config
@classmethod @classmethod
async def edit_config_dao(cls, db: AsyncSession, config: dict): async def edit_config_dao(cls, db: AsyncSession, config: dict) -> None:
""" """
编辑参数配置数据库操作 编辑参数配置数据库操作
@@ -105,7 +113,7 @@ class ConfigDao:
await db.execute(update(SysConfig), [config]) await db.execute(update(SysConfig), [config])
@classmethod @classmethod
async def delete_config_dao(cls, db: AsyncSession, config: ConfigModel): async def delete_config_dao(cls, db: AsyncSession, config: ConfigModel) -> None:
""" """
删除参数配置数据库操作 删除参数配置数据库操作
@@ -1,7 +1,10 @@
from collections.abc import Sequence
from typing import Union
from sqlalchemy import bindparam, func, or_, select, update # noqa: F401 from sqlalchemy import bindparam, func, or_, select, update # noqa: F401
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.util import immutabledict from sqlalchemy.util import immutabledict
from typing import List
from module_admin.entity.do.dept_do import SysDept from module_admin.entity.do.dept_do import SysDept
from module_admin.entity.do.role_do import SysRoleDept # noqa: F401 from module_admin.entity.do.role_do import SysRoleDept # noqa: F401
from module_admin.entity.do.user_do import SysUser from module_admin.entity.do.user_do import SysUser
@@ -14,7 +17,7 @@ class DeptDao:
""" """
@classmethod @classmethod
async def get_dept_by_id(cls, db: AsyncSession, dept_id: int): async def get_dept_by_id(cls, db: AsyncSession, dept_id: int) -> Union[SysDept, None]:
""" """
根据部门id获取在用部门信息 根据部门id获取在用部门信息
@@ -27,7 +30,7 @@ class DeptDao:
return dept_info return dept_info
@classmethod @classmethod
async def get_dept_detail_by_id(cls, db: AsyncSession, dept_id: int): async def get_dept_detail_by_id(cls, db: AsyncSession, dept_id: int) -> Union[SysDept, None]:
""" """
根据部门id获取部门详细信息 根据部门id获取部门详细信息
@@ -44,7 +47,7 @@ class DeptDao:
return dept_info return dept_info
@classmethod @classmethod
async def get_dept_detail_by_info(cls, db: AsyncSession, dept: DeptModel): async def get_dept_detail_by_info(cls, db: AsyncSession, dept: DeptModel) -> Union[SysDept, None]:
""" """
根据部门参数获取部门信息 根据部门参数获取部门信息
@@ -68,7 +71,9 @@ class DeptDao:
return dept_info return dept_info
@classmethod @classmethod
async def get_dept_info_for_edit_option(cls, db: AsyncSession, dept_info: DeptModel, data_scope_sql: str): async def get_dept_info_for_edit_option(
cls, db: AsyncSession, dept_info: DeptModel, data_scope_sql: str
) -> Sequence[SysDept]:
""" """
获取部门编辑对应的在用部门列表信息 获取部门编辑对应的在用部门列表信息
@@ -101,7 +106,7 @@ class DeptDao:
return dept_result return dept_result
@classmethod @classmethod
async def get_children_dept_dao(cls, db: AsyncSession, dept_id: int): async def get_children_dept_dao(cls, db: AsyncSession, dept_id: int) -> Sequence[SysDept]:
""" """
根据部门id查询当前部门的子部门列表信息 根据部门id查询当前部门的子部门列表信息
@@ -116,7 +121,9 @@ class DeptDao:
return dept_result return dept_result
@classmethod @classmethod
async def get_dept_list_for_tree(cls, db: AsyncSession, dept_info: DeptModel, data_scope_sql: str): async def get_dept_list_for_tree(
cls, db: AsyncSession, dept_info: DeptModel, data_scope_sql: str
) -> Sequence[SysDept]:
""" """
获取所有在用部门列表信息 获取所有在用部门列表信息
@@ -146,7 +153,7 @@ class DeptDao:
return dept_result return dept_result
@classmethod @classmethod
async def get_dept_list(cls, db: AsyncSession, page_object: DeptModel, data_scope_sql: str): async def get_dept_list(cls, db: AsyncSession, page_object: DeptModel, data_scope_sql: str) -> Sequence[SysDept]:
""" """
根据查询参数获取部门列表信息 根据查询参数获取部门列表信息
@@ -177,7 +184,7 @@ class DeptDao:
return dept_result return dept_result
@classmethod @classmethod
async def add_dept_dao(cls, db: AsyncSession, dept: DeptModel): async def add_dept_dao(cls, db: AsyncSession, dept: DeptModel) -> SysDept:
""" """
新增部门数据库操作 新增部门数据库操作
@@ -192,7 +199,7 @@ class DeptDao:
return db_dept return db_dept
@classmethod @classmethod
async def edit_dept_dao(cls, db: AsyncSession, dept: dict): async def edit_dept_dao(cls, db: AsyncSession, dept: dict) -> None:
""" """
编辑部门数据库操作 编辑部门数据库操作
@@ -203,7 +210,7 @@ class DeptDao:
await db.execute(update(SysDept), [dept]) await db.execute(update(SysDept), [dept])
@classmethod @classmethod
async def update_dept_children_dao(cls, db: AsyncSession, update_dept: List): async def update_dept_children_dao(cls, db: AsyncSession, update_dept: list) -> None:
""" """
更新子部门信息 更新子部门信息
@@ -225,7 +232,7 @@ class DeptDao:
) )
@classmethod @classmethod
async def update_dept_status_normal_dao(cls, db: AsyncSession, dept_id_list: List): async def update_dept_status_normal_dao(cls, db: AsyncSession, dept_id_list: list) -> None:
""" """
批量更新部门状态为正常 批量更新部门状态为正常
@@ -236,7 +243,7 @@ class DeptDao:
await db.execute(update(SysDept).where(SysDept.dept_id.in_(dept_id_list)).values(status='0')) await db.execute(update(SysDept).where(SysDept.dept_id.in_(dept_id_list)).values(status='0'))
@classmethod @classmethod
async def delete_dept_dao(cls, db: AsyncSession, dept: DeptModel): async def delete_dept_dao(cls, db: AsyncSession, dept: DeptModel) -> None:
""" """
删除部门数据库操作 删除部门数据库操作
@@ -251,7 +258,7 @@ class DeptDao:
) )
@classmethod @classmethod
async def count_normal_children_dept_dao(cls, db: AsyncSession, dept_id: int): async def count_normal_children_dept_dao(cls, db: AsyncSession, dept_id: int) -> Union[int, None]:
""" """
根据部门id查询查询所有子部门正常状态的数量 根据部门id查询查询所有子部门正常状态的数量
@@ -270,7 +277,7 @@ class DeptDao:
return normal_children_dept_count return normal_children_dept_count
@classmethod @classmethod
async def count_children_dept_dao(cls, db: AsyncSession, dept_id: int): async def count_children_dept_dao(cls, db: AsyncSession, dept_id: int) -> Union[int, None]:
""" """
根据部门id查询查询所有子部门所有状态的数量 根据部门id查询查询所有子部门所有状态的数量
@@ -290,7 +297,7 @@ class DeptDao:
return children_dept_count return children_dept_count
@classmethod @classmethod
async def count_dept_user_dao(cls, db: AsyncSession, dept_id: int): async def count_dept_user_dao(cls, db: AsyncSession, dept_id: int) -> Union[int, None]:
""" """
根据部门id查询查询部门下的用户数量 根据部门id查询查询部门下的用户数量
@@ -1,7 +1,12 @@
from collections.abc import Sequence
from datetime import datetime, time from datetime import datetime, time
from typing import Any, Union
from sqlalchemy import and_, delete, func, select, update from sqlalchemy import and_, delete, func, select, update
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from module_admin.entity.do.dict_do import SysDictType, SysDictData
from common.vo import PageModel
from module_admin.entity.do.dict_do import SysDictData, SysDictType
from module_admin.entity.vo.dict_vo import DictDataModel, DictDataPageQueryModel, DictTypeModel, DictTypePageQueryModel from module_admin.entity.vo.dict_vo import DictDataModel, DictDataPageQueryModel, DictTypeModel, DictTypePageQueryModel
from utils.page_util import PageUtil from utils.page_util import PageUtil
from utils.time_format_util import list_format_datetime from utils.time_format_util import list_format_datetime
@@ -13,7 +18,7 @@ class DictTypeDao:
""" """
@classmethod @classmethod
async def get_dict_type_detail_by_id(cls, db: AsyncSession, dict_id: int): async def get_dict_type_detail_by_id(cls, db: AsyncSession, dict_id: int) -> Union[SysDictType, None]:
""" """
根据字典类型id获取字典类型详细信息 根据字典类型id获取字典类型详细信息
@@ -26,7 +31,7 @@ class DictTypeDao:
return dict_type_info return dict_type_info
@classmethod @classmethod
async def get_dict_type_detail_by_info(cls, db: AsyncSession, dict_type: DictTypeModel): async def get_dict_type_detail_by_info(cls, db: AsyncSession, dict_type: DictTypeModel) -> Union[SysDictType, None]:
""" """
根据字典类型参数获取字典类型信息 根据字典类型参数获取字典类型信息
@@ -50,7 +55,7 @@ class DictTypeDao:
return dict_type_info return dict_type_info
@classmethod @classmethod
async def get_all_dict_type(cls, db: AsyncSession): async def get_all_dict_type(cls, db: AsyncSession) -> list[Any]:
""" """
获取所有的字典类型信息 获取所有的字典类型信息
@@ -62,7 +67,9 @@ class DictTypeDao:
return list_format_datetime(dict_type_info) return list_format_datetime(dict_type_info)
@classmethod @classmethod
async def get_dict_type_list(cls, db: AsyncSession, query_object: DictTypePageQueryModel, is_page: bool = False): async def get_dict_type_list(
cls, db: AsyncSession, query_object: DictTypePageQueryModel, is_page: bool = False
) -> Union[PageModel, list[dict[str, Any]]]:
""" """
根据查询参数获取字典类型列表信息 根据查询参数获取字典类型列表信息
@@ -87,12 +94,14 @@ class DictTypeDao:
.order_by(SysDictType.dict_id) .order_by(SysDictType.dict_id)
.distinct() .distinct()
) )
dict_type_list = await PageUtil.paginate(db, query, query_object.page_num, query_object.page_size, is_page) dict_type_list: Union[PageModel, list[dict[str, Any]]] = await PageUtil.paginate(
db, query, query_object.page_num, query_object.page_size, is_page
)
return dict_type_list return dict_type_list
@classmethod @classmethod
async def add_dict_type_dao(cls, db: AsyncSession, dict_type: DictTypeModel): async def add_dict_type_dao(cls, db: AsyncSession, dict_type: DictTypeModel) -> SysDictType:
""" """
新增字典类型数据库操作 新增字典类型数据库操作
@@ -107,7 +116,7 @@ class DictTypeDao:
return db_dict_type return db_dict_type
@classmethod @classmethod
async def edit_dict_type_dao(cls, db: AsyncSession, dict_type: dict): async def edit_dict_type_dao(cls, db: AsyncSession, dict_type: dict) -> None:
""" """
编辑字典类型数据库操作 编辑字典类型数据库操作
@@ -118,7 +127,7 @@ class DictTypeDao:
await db.execute(update(SysDictType), [dict_type]) await db.execute(update(SysDictType), [dict_type])
@classmethod @classmethod
async def delete_dict_type_dao(cls, db: AsyncSession, dict_type: DictTypeModel): async def delete_dict_type_dao(cls, db: AsyncSession, dict_type: DictTypeModel) -> None:
""" """
删除字典类型数据库操作 删除字典类型数据库操作
@@ -135,7 +144,7 @@ class DictDataDao:
""" """
@classmethod @classmethod
async def get_dict_data_detail_by_id(cls, db: AsyncSession, dict_code: int): async def get_dict_data_detail_by_id(cls, db: AsyncSession, dict_code: int) -> Union[SysDictData, None]:
""" """
根据字典数据id获取字典数据详细信息 根据字典数据id获取字典数据详细信息
@@ -150,7 +159,7 @@ class DictDataDao:
return dict_data_info return dict_data_info
@classmethod @classmethod
async def get_dict_data_detail_by_info(cls, db: AsyncSession, dict_data: DictDataModel): async def get_dict_data_detail_by_info(cls, db: AsyncSession, dict_data: DictDataModel) -> Union[SysDictData, None]:
""" """
根据字典数据参数获取字典数据信息 根据字典数据参数获取字典数据信息
@@ -175,7 +184,9 @@ class DictDataDao:
return dict_data_info return dict_data_info
@classmethod @classmethod
async def get_dict_data_list(cls, db: AsyncSession, query_object: DictDataPageQueryModel, is_page: bool = False): async def get_dict_data_list(
cls, db: AsyncSession, query_object: DictDataPageQueryModel, is_page: bool = False
) -> Union[PageModel, list[dict[str, Any]]]:
""" """
根据查询参数获取字典数据列表信息 根据查询参数获取字典数据列表信息
@@ -194,12 +205,14 @@ class DictDataDao:
.order_by(SysDictData.dict_sort) .order_by(SysDictData.dict_sort)
.distinct() .distinct()
) )
dict_data_list = await PageUtil.paginate(db, query, query_object.page_num, query_object.page_size, is_page) dict_data_list: Union[PageModel, list[dict[str, Any]]] = await PageUtil.paginate(
db, query, query_object.page_num, query_object.page_size, is_page
)
return dict_data_list return dict_data_list
@classmethod @classmethod
async def query_dict_data_list(cls, db: AsyncSession, dict_type: str): async def query_dict_data_list(cls, db: AsyncSession, dict_type: str) -> Sequence[SysDictData]:
""" """
根据查询参数获取字典数据列表信息 根据查询参数获取字典数据列表信息
@@ -229,7 +242,7 @@ class DictDataDao:
return dict_data_list return dict_data_list
@classmethod @classmethod
async def add_dict_data_dao(cls, db: AsyncSession, dict_data: DictDataModel): async def add_dict_data_dao(cls, db: AsyncSession, dict_data: DictDataModel) -> SysDictData:
""" """
新增字典数据数据库操作 新增字典数据数据库操作
@@ -244,7 +257,7 @@ class DictDataDao:
return db_data_type return db_data_type
@classmethod @classmethod
async def edit_dict_data_dao(cls, db: AsyncSession, dict_data: dict): async def edit_dict_data_dao(cls, db: AsyncSession, dict_data: dict) -> None:
""" """
编辑字典数据数据库操作 编辑字典数据数据库操作
@@ -255,7 +268,7 @@ class DictDataDao:
await db.execute(update(SysDictData), [dict_data]) await db.execute(update(SysDictData), [dict_data])
@classmethod @classmethod
async def delete_dict_data_dao(cls, db: AsyncSession, dict_data: DictDataModel): async def delete_dict_data_dao(cls, db: AsyncSession, dict_data: DictDataModel) -> None:
""" """
删除字典数据数据库操作 删除字典数据数据库操作
@@ -266,7 +279,7 @@ class DictDataDao:
await db.execute(delete(SysDictData).where(SysDictData.dict_code.in_([dict_data.dict_code]))) await db.execute(delete(SysDictData).where(SysDictData.dict_code.in_([dict_data.dict_code])))
@classmethod @classmethod
async def count_dict_data_dao(cls, db: AsyncSession, dict_type: str): async def count_dict_data_dao(cls, db: AsyncSession, dict_type: str) -> Union[int, None]:
""" """
根据字典类型查询字典类型关联的字典数据数量 根据字典类型查询字典类型关联的字典数据数量
@@ -1,5 +1,10 @@
from collections.abc import Sequence
from typing import Any, Union
from sqlalchemy import delete, select, update from sqlalchemy import delete, select, update
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from common.vo import PageModel
from module_admin.entity.do.job_do import SysJob from module_admin.entity.do.job_do import SysJob
from module_admin.entity.vo.job_vo import JobModel, JobPageQueryModel from module_admin.entity.vo.job_vo import JobModel, JobPageQueryModel
from utils.page_util import PageUtil from utils.page_util import PageUtil
@@ -11,7 +16,7 @@ class JobDao:
""" """
@classmethod @classmethod
async def get_job_detail_by_id(cls, db: AsyncSession, job_id: int): async def get_job_detail_by_id(cls, db: AsyncSession, job_id: int) -> Union[SysJob, None]:
""" """
根据定时任务id获取定时任务详细信息 根据定时任务id获取定时任务详细信息
@@ -24,7 +29,7 @@ class JobDao:
return job_info return job_info
@classmethod @classmethod
async def get_job_detail_by_info(cls, db: AsyncSession, job: JobModel): async def get_job_detail_by_info(cls, db: AsyncSession, job: JobModel) -> Union[SysJob, None]:
""" """
根据定时任务参数获取定时任务信息 根据定时任务参数获取定时任务信息
@@ -53,7 +58,9 @@ class JobDao:
return job_info return job_info
@classmethod @classmethod
async def get_job_list(cls, db: AsyncSession, query_object: JobPageQueryModel, is_page: bool = False): async def get_job_list(
cls, db: AsyncSession, query_object: JobPageQueryModel, is_page: bool = False
) -> Union[PageModel, list[dict[str, Any]]]:
""" """
根据查询参数获取定时任务列表信息 根据查询参数获取定时任务列表信息
@@ -72,12 +79,14 @@ class JobDao:
.order_by(SysJob.job_id) .order_by(SysJob.job_id)
.distinct() .distinct()
) )
job_list = await PageUtil.paginate(db, query, query_object.page_num, query_object.page_size, is_page) job_list: Union[PageModel, list[dict[str, Any]]] = await PageUtil.paginate(
db, query, query_object.page_num, query_object.page_size, is_page
)
return job_list return job_list
@classmethod @classmethod
async def get_job_list_for_scheduler(cls, db: AsyncSession): async def get_job_list_for_scheduler(cls, db: AsyncSession) -> Sequence[SysJob]:
""" """
获取定时任务列表信息 获取定时任务列表信息
@@ -89,7 +98,7 @@ class JobDao:
return job_list return job_list
@classmethod @classmethod
async def add_job_dao(cls, db: AsyncSession, job: JobModel): async def add_job_dao(cls, db: AsyncSession, job: JobModel) -> SysJob:
""" """
新增定时任务数据库操作 新增定时任务数据库操作
@@ -104,7 +113,7 @@ class JobDao:
return db_job return db_job
@classmethod @classmethod
async def edit_job_dao(cls, db: AsyncSession, job: dict, old_job: JobModel): async def edit_job_dao(cls, db: AsyncSession, job: dict, old_job: JobModel) -> None:
""" """
编辑定时任务数据库操作 编辑定时任务数据库操作
@@ -124,7 +133,7 @@ class JobDao:
) )
@classmethod @classmethod
async def delete_job_dao(cls, db: AsyncSession, job: JobModel): async def delete_job_dao(cls, db: AsyncSession, job: JobModel) -> None:
""" """
删除定时任务数据库操作 删除定时任务数据库操作
@@ -1,7 +1,11 @@
from datetime import datetime, time from datetime import datetime, time
from typing import Any, Union
from sqlalchemy import delete, desc, select from sqlalchemy import delete, desc, select
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
from common.vo import PageModel
from module_admin.entity.do.job_do import SysJobLog from module_admin.entity.do.job_do import SysJobLog
from module_admin.entity.vo.job_vo import JobLogModel, JobLogPageQueryModel from module_admin.entity.vo.job_vo import JobLogModel, JobLogPageQueryModel
from utils.page_util import PageUtil from utils.page_util import PageUtil
@@ -13,7 +17,9 @@ class JobLogDao:
""" """
@classmethod @classmethod
async def get_job_log_list(cls, db: AsyncSession, query_object: JobLogPageQueryModel, is_page: bool = False): async def get_job_log_list(
cls, db: AsyncSession, query_object: JobLogPageQueryModel, is_page: bool = False
) -> Union[PageModel, list[dict[str, Any]]]:
""" """
根据查询参数获取定时任务日志列表信息 根据查询参数获取定时任务日志列表信息
@@ -38,12 +44,14 @@ class JobLogDao:
.order_by(desc(SysJobLog.create_time)) .order_by(desc(SysJobLog.create_time))
.distinct() .distinct()
) )
job_log_list = await PageUtil.paginate(db, query, query_object.page_num, query_object.page_size, is_page) job_log_list: Union[PageModel, list[dict[str, Any]]] = await PageUtil.paginate(
db, query, query_object.page_num, query_object.page_size, is_page
)
return job_log_list return job_log_list
@classmethod @classmethod
def add_job_log_dao(cls, db: Session, job_log: JobLogModel): def add_job_log_dao(cls, db: Session, job_log: JobLogModel) -> SysJobLog:
""" """
新增定时任务日志数据库操作 新增定时任务日志数据库操作
@@ -58,7 +66,7 @@ class JobLogDao:
return db_job_log return db_job_log
@classmethod @classmethod
async def delete_job_log_dao(cls, db: AsyncSession, job_log: JobLogModel): async def delete_job_log_dao(cls, db: AsyncSession, job_log: JobLogModel) -> None:
""" """
删除定时任务日志数据库操作 删除定时任务日志数据库操作
@@ -69,7 +77,7 @@ class JobLogDao:
await db.execute(delete(SysJobLog).where(SysJobLog.job_log_id.in_([job_log.job_log_id]))) await db.execute(delete(SysJobLog).where(SysJobLog.job_log_id.in_([job_log.job_log_id])))
@classmethod @classmethod
async def clear_job_log_dao(cls, db: AsyncSession): async def clear_job_log_dao(cls, db: AsyncSession) -> None:
""" """
清除定时任务日志数据库操作 清除定时任务日志数据库操作
@@ -1,6 +1,10 @@
from datetime import datetime, time from datetime import datetime, time
from typing import Any, Union
from sqlalchemy import asc, delete, desc, select from sqlalchemy import asc, delete, desc, select
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from common.vo import PageModel
from module_admin.entity.do.log_do import SysLogininfor, SysOperLog from module_admin.entity.do.log_do import SysLogininfor, SysOperLog
from module_admin.entity.vo.log_vo import LogininforModel, LoginLogPageQueryModel, OperLogModel, OperLogPageQueryModel from module_admin.entity.vo.log_vo import LogininforModel, LoginLogPageQueryModel, OperLogModel, OperLogPageQueryModel
from utils.common_util import SnakeCaseUtil from utils.common_util import SnakeCaseUtil
@@ -14,7 +18,9 @@ class OperationLogDao:
""" """
@classmethod @classmethod
async def get_operation_log_list(cls, db: AsyncSession, query_object: OperLogPageQueryModel, is_page: bool = False): async def get_operation_log_list(
cls, db: AsyncSession, query_object: OperLogPageQueryModel, is_page: bool = False
) -> Union[PageModel, list[dict[str, Any]]]:
""" """
根据查询参数获取操作日志列表信息 根据查询参数获取操作日志列表信息
@@ -48,12 +54,14 @@ class OperationLogDao:
.distinct() .distinct()
.order_by(order_by_column) .order_by(order_by_column)
) )
operation_log_list = await PageUtil.paginate(db, query, query_object.page_num, query_object.page_size, is_page) operation_log_list: Union[PageModel, list[dict[str, Any]]] = await PageUtil.paginate(
db, query, query_object.page_num, query_object.page_size, is_page
)
return operation_log_list return operation_log_list
@classmethod @classmethod
async def add_operation_log_dao(cls, db: AsyncSession, operation_log: OperLogModel): async def add_operation_log_dao(cls, db: AsyncSession, operation_log: OperLogModel) -> SysOperLog:
""" """
新增操作日志数据库操作 新增操作日志数据库操作
@@ -68,7 +76,7 @@ class OperationLogDao:
return db_operation_log return db_operation_log
@classmethod @classmethod
async def delete_operation_log_dao(cls, db: AsyncSession, operation_log: OperLogModel): async def delete_operation_log_dao(cls, db: AsyncSession, operation_log: OperLogModel) -> None:
""" """
删除操作日志数据库操作 删除操作日志数据库操作
@@ -79,7 +87,7 @@ class OperationLogDao:
await db.execute(delete(SysOperLog).where(SysOperLog.oper_id.in_([operation_log.oper_id]))) await db.execute(delete(SysOperLog).where(SysOperLog.oper_id.in_([operation_log.oper_id])))
@classmethod @classmethod
async def clear_operation_log_dao(cls, db: AsyncSession): async def clear_operation_log_dao(cls, db: AsyncSession) -> None:
""" """
清除操作日志数据库操作 清除操作日志数据库操作
@@ -95,7 +103,9 @@ class LoginLogDao:
""" """
@classmethod @classmethod
async def get_login_log_list(cls, db: AsyncSession, query_object: LoginLogPageQueryModel, is_page: bool = False): async def get_login_log_list(
cls, db: AsyncSession, query_object: LoginLogPageQueryModel, is_page: bool = False
) -> Union[PageModel, list[dict[str, Any]]]:
""" """
根据查询参数获取登录日志列表信息 根据查询参数获取登录日志列表信息
@@ -130,12 +140,14 @@ class LoginLogDao:
.distinct() .distinct()
.order_by(order_by_column) .order_by(order_by_column)
) )
login_log_list = await PageUtil.paginate(db, query, query_object.page_num, query_object.page_size, is_page) login_log_list: Union[PageModel, list[dict[str, Any]]] = await PageUtil.paginate(
db, query, query_object.page_num, query_object.page_size, is_page
)
return login_log_list return login_log_list
@classmethod @classmethod
async def add_login_log_dao(cls, db: AsyncSession, login_log: LogininforModel): async def add_login_log_dao(cls, db: AsyncSession, login_log: LogininforModel) -> SysLogininfor:
""" """
新增登录日志数据库操作 新增登录日志数据库操作
@@ -150,7 +162,7 @@ class LoginLogDao:
return db_login_log return db_login_log
@classmethod @classmethod
async def delete_login_log_dao(cls, db: AsyncSession, login_log: LogininforModel): async def delete_login_log_dao(cls, db: AsyncSession, login_log: LogininforModel) -> None:
""" """
删除登录日志数据库操作 删除登录日志数据库操作
@@ -161,7 +173,7 @@ class LoginLogDao:
await db.execute(delete(SysLogininfor).where(SysLogininfor.info_id.in_([login_log.info_id]))) await db.execute(delete(SysLogininfor).where(SysLogininfor.info_id.in_([login_log.info_id])))
@classmethod @classmethod
async def clear_login_log_dao(cls, db: AsyncSession): async def clear_login_log_dao(cls, db: AsyncSession) -> None:
""" """
清除登录日志数据库操作 清除登录日志数据库操作
@@ -1,10 +1,13 @@
from sqlalchemy import and_, select from typing import Union
from sqlalchemy import Row, and_, select
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from module_admin.entity.do.dept_do import SysDept from module_admin.entity.do.dept_do import SysDept
from module_admin.entity.do.user_do import SysUser from module_admin.entity.do.user_do import SysUser
async def login_by_account(db: AsyncSession, user_name: str): async def login_by_account(db: AsyncSession, user_name: str) -> Union[Row[tuple[SysUser, SysDept]], None]:
""" """
根据用户名查询用户信息 根据用户名查询用户信息
@@ -1,5 +1,9 @@
from collections.abc import Sequence
from typing import Union
from sqlalchemy import and_, delete, func, select, update from sqlalchemy import and_, delete, func, select, update
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from module_admin.entity.do.menu_do import SysMenu from module_admin.entity.do.menu_do import SysMenu
from module_admin.entity.do.role_do import SysRole, SysRoleMenu from module_admin.entity.do.role_do import SysRole, SysRoleMenu
from module_admin.entity.do.user_do import SysUser, SysUserRole from module_admin.entity.do.user_do import SysUser, SysUserRole
@@ -12,7 +16,7 @@ class MenuDao:
""" """
@classmethod @classmethod
async def get_menu_detail_by_id(cls, db: AsyncSession, menu_id: int): async def get_menu_detail_by_id(cls, db: AsyncSession, menu_id: int) -> Union[SysMenu, None]:
""" """
根据菜单id获取菜单详细信息 根据菜单id获取菜单详细信息
@@ -25,7 +29,7 @@ class MenuDao:
return menu_info return menu_info
@classmethod @classmethod
async def get_menu_detail_by_info(cls, db: AsyncSession, menu: MenuModel): async def get_menu_detail_by_info(cls, db: AsyncSession, menu: MenuModel) -> Union[SysMenu, None]:
""" """
根据菜单参数获取菜单信息 根据菜单参数获取菜单信息
@@ -50,7 +54,7 @@ class MenuDao:
return menu_info return menu_info
@classmethod @classmethod
async def get_menu_list_for_tree(cls, db: AsyncSession, user_id: int, role: list): async def get_menu_list_for_tree(cls, db: AsyncSession, user_id: int, role: list) -> Sequence[SysMenu]:
""" """
根据角色信息获取所有在用菜单列表信息 根据角色信息获取所有在用菜单列表信息
@@ -94,7 +98,9 @@ class MenuDao:
return menu_query_all return menu_query_all
@classmethod @classmethod
async def get_menu_list(cls, db: AsyncSession, page_object: MenuQueryModel, user_id: int, role: list): async def get_menu_list(
cls, db: AsyncSession, page_object: MenuQueryModel, user_id: int, role: list
) -> Sequence[SysMenu]:
""" """
根据查询参数获取菜单列表信息 根据查询参数获取菜单列表信息
@@ -156,7 +162,7 @@ class MenuDao:
return menu_query_all return menu_query_all
@classmethod @classmethod
async def add_menu_dao(cls, db: AsyncSession, menu: MenuModel): async def add_menu_dao(cls, db: AsyncSession, menu: MenuModel) -> SysMenu:
""" """
新增菜单数据库操作 新增菜单数据库操作
@@ -171,7 +177,7 @@ class MenuDao:
return db_menu return db_menu
@classmethod @classmethod
async def edit_menu_dao(cls, db: AsyncSession, menu: dict): async def edit_menu_dao(cls, db: AsyncSession, menu: dict) -> None:
""" """
编辑菜单数据库操作 编辑菜单数据库操作
@@ -182,7 +188,7 @@ class MenuDao:
await db.execute(update(SysMenu), [menu]) await db.execute(update(SysMenu), [menu])
@classmethod @classmethod
async def delete_menu_dao(cls, db: AsyncSession, menu: MenuModel): async def delete_menu_dao(cls, db: AsyncSession, menu: MenuModel) -> None:
""" """
删除菜单数据库操作 删除菜单数据库操作
@@ -193,7 +199,7 @@ class MenuDao:
await db.execute(delete(SysMenu).where(SysMenu.menu_id.in_([menu.menu_id]))) await db.execute(delete(SysMenu).where(SysMenu.menu_id.in_([menu.menu_id])))
@classmethod @classmethod
async def has_child_by_menu_id_dao(cls, db: AsyncSession, menu_id: int): async def has_child_by_menu_id_dao(cls, db: AsyncSession, menu_id: int) -> Union[int, None]:
""" """
根据菜单id查询菜单关联子菜单的数量 根据菜单id查询菜单关联子菜单的数量
@@ -208,7 +214,7 @@ class MenuDao:
return menu_count return menu_count
@classmethod @classmethod
async def check_menu_exist_role_dao(cls, db: AsyncSession, menu_id: int): async def check_menu_exist_role_dao(cls, db: AsyncSession, menu_id: int) -> Union[int, None]:
""" """
根据菜单id查询菜单关联角色数量 根据菜单id查询菜单关联角色数量
@@ -1,6 +1,10 @@
from datetime import datetime, time from datetime import datetime, time
from typing import Any, Union
from sqlalchemy import delete, select, update from sqlalchemy import delete, select, update
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from common.vo import PageModel
from module_admin.entity.do.notice_do import SysNotice from module_admin.entity.do.notice_do import SysNotice
from module_admin.entity.vo.notice_vo import NoticeModel, NoticePageQueryModel from module_admin.entity.vo.notice_vo import NoticeModel, NoticePageQueryModel
from utils.page_util import PageUtil from utils.page_util import PageUtil
@@ -12,7 +16,7 @@ class NoticeDao:
""" """
@classmethod @classmethod
async def get_notice_detail_by_id(cls, db: AsyncSession, notice_id: int): async def get_notice_detail_by_id(cls, db: AsyncSession, notice_id: int) -> Union[SysNotice, None]:
""" """
根据通知公告id获取通知公告详细信息 根据通知公告id获取通知公告详细信息
@@ -25,7 +29,7 @@ class NoticeDao:
return notice_info return notice_info
@classmethod @classmethod
async def get_notice_detail_by_info(cls, db: AsyncSession, notice: NoticeModel): async def get_notice_detail_by_info(cls, db: AsyncSession, notice: NoticeModel) -> Union[SysNotice, None]:
""" """
根据通知公告参数获取通知公告信息 根据通知公告参数获取通知公告信息
@@ -50,7 +54,9 @@ class NoticeDao:
return notice_info return notice_info
@classmethod @classmethod
async def get_notice_list(cls, db: AsyncSession, query_object: NoticePageQueryModel, is_page: bool = False): async def get_notice_list(
cls, db: AsyncSession, query_object: NoticePageQueryModel, is_page: bool = False
) -> Union[PageModel, list[dict[str, Any]]]:
""" """
根据查询参数获取通知公告列表信息 根据查询参数获取通知公告列表信息
@@ -75,12 +81,14 @@ class NoticeDao:
.order_by(SysNotice.notice_id) .order_by(SysNotice.notice_id)
.distinct() .distinct()
) )
notice_list = await PageUtil.paginate(db, query, query_object.page_num, query_object.page_size, is_page) notice_list: Union[PageModel, list[dict[str, Any]]] = await PageUtil.paginate(
db, query, query_object.page_num, query_object.page_size, is_page
)
return notice_list return notice_list
@classmethod @classmethod
async def add_notice_dao(cls, db: AsyncSession, notice: NoticeModel): async def add_notice_dao(cls, db: AsyncSession, notice: NoticeModel) -> SysNotice:
""" """
新增通知公告数据库操作 新增通知公告数据库操作
@@ -95,7 +103,7 @@ class NoticeDao:
return db_notice return db_notice
@classmethod @classmethod
async def edit_notice_dao(cls, db: AsyncSession, notice: dict): async def edit_notice_dao(cls, db: AsyncSession, notice: dict) -> None:
""" """
编辑通知公告数据库操作 编辑通知公告数据库操作
@@ -106,7 +114,7 @@ class NoticeDao:
await db.execute(update(SysNotice), [notice]) await db.execute(update(SysNotice), [notice])
@classmethod @classmethod
async def delete_notice_dao(cls, db: AsyncSession, notice: NoticeModel): async def delete_notice_dao(cls, db: AsyncSession, notice: NoticeModel) -> None:
""" """
删除通知公告数据库操作 删除通知公告数据库操作
@@ -1,5 +1,9 @@
from typing import Any, Union
from sqlalchemy import delete, func, select, update from sqlalchemy import delete, func, select, update
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from common.vo import PageModel
from module_admin.entity.do.post_do import SysPost from module_admin.entity.do.post_do import SysPost
from module_admin.entity.do.user_do import SysUserPost from module_admin.entity.do.user_do import SysUserPost
from module_admin.entity.vo.post_vo import PostModel, PostPageQueryModel from module_admin.entity.vo.post_vo import PostModel, PostPageQueryModel
@@ -12,7 +16,7 @@ class PostDao:
""" """
@classmethod @classmethod
async def get_post_by_id(cls, db: AsyncSession, post_id: int): async def get_post_by_id(cls, db: AsyncSession, post_id: int) -> Union[SysPost, None]:
""" """
根据岗位id获取在用岗位详细信息 根据岗位id获取在用岗位详细信息
@@ -29,7 +33,7 @@ class PostDao:
return post_info return post_info
@classmethod @classmethod
async def get_post_detail_by_id(cls, db: AsyncSession, post_id: int): async def get_post_detail_by_id(cls, db: AsyncSession, post_id: int) -> Union[SysPost, None]:
""" """
根据岗位id获取岗位详细信息 根据岗位id获取岗位详细信息
@@ -42,7 +46,7 @@ class PostDao:
return post_info return post_info
@classmethod @classmethod
async def get_post_detail_by_info(cls, db: AsyncSession, post: PostModel): async def get_post_detail_by_info(cls, db: AsyncSession, post: PostModel) -> Union[SysPost, None]:
""" """
根据岗位参数获取岗位信息 根据岗位参数获取岗位信息
@@ -67,7 +71,9 @@ class PostDao:
return post_info return post_info
@classmethod @classmethod
async def get_post_list(cls, db: AsyncSession, query_object: PostPageQueryModel, is_page: bool = False): async def get_post_list(
cls, db: AsyncSession, query_object: PostPageQueryModel, is_page: bool = False
) -> Union[PageModel, list[dict[str, Any]]]:
""" """
根据查询参数获取岗位列表信息 根据查询参数获取岗位列表信息
@@ -86,12 +92,14 @@ class PostDao:
.order_by(SysPost.post_sort) .order_by(SysPost.post_sort)
.distinct() .distinct()
) )
post_list = await PageUtil.paginate(db, query, query_object.page_num, query_object.page_size, is_page) post_list: Union[PageModel, list[dict[str, Any]]] = await PageUtil.paginate(
db, query, query_object.page_num, query_object.page_size, is_page
)
return post_list return post_list
@classmethod @classmethod
async def add_post_dao(cls, db: AsyncSession, post: PostModel): async def add_post_dao(cls, db: AsyncSession, post: PostModel) -> SysPost:
""" """
新增岗位数据库操作 新增岗位数据库操作
@@ -106,7 +114,7 @@ class PostDao:
return db_post return db_post
@classmethod @classmethod
async def edit_post_dao(cls, db: AsyncSession, post: dict): async def edit_post_dao(cls, db: AsyncSession, post: dict) -> None:
""" """
编辑岗位数据库操作 编辑岗位数据库操作
@@ -117,7 +125,7 @@ class PostDao:
await db.execute(update(SysPost), [post]) await db.execute(update(SysPost), [post])
@classmethod @classmethod
async def delete_post_dao(cls, db: AsyncSession, post: PostModel): async def delete_post_dao(cls, db: AsyncSession, post: PostModel) -> None:
""" """
删除岗位数据库操作 删除岗位数据库操作
@@ -128,7 +136,7 @@ class PostDao:
await db.execute(delete(SysPost).where(SysPost.post_id.in_([post.post_id]))) await db.execute(delete(SysPost).where(SysPost.post_id.in_([post.post_id])))
@classmethod @classmethod
async def count_user_post_dao(cls, db: AsyncSession, post_id: int): async def count_user_post_dao(cls, db: AsyncSession, post_id: int) -> Union[int, None]:
""" """
根据岗位id查询岗位关联的用户数量 根据岗位id查询岗位关联的用户数量
@@ -1,9 +1,14 @@
from collections.abc import Sequence
from datetime import datetime, time from datetime import datetime, time
from typing import Any, Union
from sqlalchemy import and_, delete, desc, func, or_, select, update # noqa: F401 from sqlalchemy import and_, delete, desc, func, or_, select, update # noqa: F401
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from common.vo import PageModel
from module_admin.entity.do.dept_do import SysDept from module_admin.entity.do.dept_do import SysDept
from module_admin.entity.do.menu_do import SysMenu from module_admin.entity.do.menu_do import SysMenu
from module_admin.entity.do.role_do import SysRole, SysRoleMenu, SysRoleDept from module_admin.entity.do.role_do import SysRole, SysRoleDept, SysRoleMenu
from module_admin.entity.do.user_do import SysUser, SysUserRole from module_admin.entity.do.user_do import SysUser, SysUserRole
from module_admin.entity.vo.role_vo import RoleDeptModel, RoleMenuModel, RoleModel, RolePageQueryModel from module_admin.entity.vo.role_vo import RoleDeptModel, RoleMenuModel, RoleModel, RolePageQueryModel
from utils.page_util import PageUtil from utils.page_util import PageUtil
@@ -15,7 +20,7 @@ class RoleDao:
""" """
@classmethod @classmethod
async def get_role_by_name(cls, db: AsyncSession, role_name: str): async def get_role_by_name(cls, db: AsyncSession, role_name: str) -> Union[SysRole, None]:
""" """
根据角色名获取在用角色信息 根据角色名获取在用角色信息
@@ -39,7 +44,7 @@ class RoleDao:
return query_role_info return query_role_info
@classmethod @classmethod
async def get_role_by_info(cls, db: AsyncSession, role: RoleModel): async def get_role_by_info(cls, db: AsyncSession, role: RoleModel) -> Union[SysRole, None]:
""" """
根据角色参数获取角色信息 根据角色参数获取角色信息
@@ -67,7 +72,7 @@ class RoleDao:
return query_role_info return query_role_info
@classmethod @classmethod
async def get_role_by_id(cls, db: AsyncSession, role_id: int): async def get_role_by_id(cls, db: AsyncSession, role_id: int) -> Union[SysRole, None]:
""" """
根据角色id获取在用角色信息 根据角色id获取在用角色信息
@@ -88,7 +93,7 @@ class RoleDao:
return role_info return role_info
@classmethod @classmethod
async def get_role_detail_by_id(cls, db: AsyncSession, role_id: int): async def get_role_detail_by_id(cls, db: AsyncSession, role_id: int) -> Union[SysRole, None]:
""" """
根据role_id获取角色详细信息 根据role_id获取角色详细信息
@@ -105,7 +110,7 @@ class RoleDao:
return query_role_info return query_role_info
@classmethod @classmethod
async def get_role_select_option_dao(cls, db: AsyncSession): async def get_role_select_option_dao(cls, db: AsyncSession) -> Sequence[SysRole]:
""" """
获取编辑页面对应的在用角色列表信息 获取编辑页面对应的在用角色列表信息
@@ -127,7 +132,7 @@ class RoleDao:
@classmethod @classmethod
async def get_role_list( async def get_role_list(
cls, db: AsyncSession, query_object: RolePageQueryModel, data_scope_sql: str, is_page: bool = False cls, db: AsyncSession, query_object: RolePageQueryModel, data_scope_sql: str, is_page: bool = False
): ) -> Union[PageModel, list[dict[str, Any]]]:
""" """
根据查询参数获取角色列表信息 根据查询参数获取角色列表信息
@@ -159,12 +164,14 @@ class RoleDao:
.order_by(SysRole.role_sort) .order_by(SysRole.role_sort)
.distinct() .distinct()
) )
role_list = await PageUtil.paginate(db, query, query_object.page_num, query_object.page_size, is_page) role_list: Union[PageModel, list[dict[str, Any]]] = await PageUtil.paginate(
db, query, query_object.page_num, query_object.page_size, is_page
)
return role_list return role_list
@classmethod @classmethod
async def add_role_dao(cls, db: AsyncSession, role: RoleModel): async def add_role_dao(cls, db: AsyncSession, role: RoleModel) -> SysRole:
""" """
新增角色数据库操作 新增角色数据库操作
@@ -179,7 +186,7 @@ class RoleDao:
return db_role return db_role
@classmethod @classmethod
async def edit_role_dao(cls, db: AsyncSession, role: dict): async def edit_role_dao(cls, db: AsyncSession, role: dict) -> None:
""" """
编辑角色数据库操作 编辑角色数据库操作
@@ -190,7 +197,7 @@ class RoleDao:
await db.execute(update(SysRole), [role]) await db.execute(update(SysRole), [role])
@classmethod @classmethod
async def delete_role_dao(cls, db: AsyncSession, role: RoleModel): async def delete_role_dao(cls, db: AsyncSession, role: RoleModel) -> None:
""" """
删除角色数据库操作 删除角色数据库操作
@@ -205,7 +212,7 @@ class RoleDao:
) )
@classmethod @classmethod
async def get_role_menu_dao(cls, db: AsyncSession, role: RoleModel): async def get_role_menu_dao(cls, db: AsyncSession, role: RoleModel) -> Sequence[SysMenu]:
""" """
根据角色id获取角色菜单关联列表信息 根据角色id获取角色菜单关联列表信息
@@ -241,7 +248,7 @@ class RoleDao:
return role_menu_query_all return role_menu_query_all
@classmethod @classmethod
async def add_role_menu_dao(cls, db: AsyncSession, role_menu: RoleMenuModel): async def add_role_menu_dao(cls, db: AsyncSession, role_menu: RoleMenuModel) -> None:
""" """
新增角色菜单关联信息数据库操作 新增角色菜单关联信息数据库操作
@@ -253,7 +260,7 @@ class RoleDao:
db.add(db_role_menu) db.add(db_role_menu)
@classmethod @classmethod
async def delete_role_menu_dao(cls, db: AsyncSession, role_menu: RoleMenuModel): async def delete_role_menu_dao(cls, db: AsyncSession, role_menu: RoleMenuModel) -> None:
""" """
删除角色菜单关联信息数据库操作 删除角色菜单关联信息数据库操作
@@ -264,7 +271,7 @@ class RoleDao:
await db.execute(delete(SysRoleMenu).where(SysRoleMenu.role_id.in_([role_menu.role_id]))) await db.execute(delete(SysRoleMenu).where(SysRoleMenu.role_id.in_([role_menu.role_id])))
@classmethod @classmethod
async def get_role_dept_dao(cls, db: AsyncSession, role: RoleModel): async def get_role_dept_dao(cls, db: AsyncSession, role: RoleModel) -> Sequence[SysDept]:
""" """
根据角色id获取角色部门关联列表信息 根据角色id获取角色部门关联列表信息
@@ -300,7 +307,7 @@ class RoleDao:
return role_dept_query_all return role_dept_query_all
@classmethod @classmethod
async def add_role_dept_dao(cls, db: AsyncSession, role_dept: RoleDeptModel): async def add_role_dept_dao(cls, db: AsyncSession, role_dept: RoleDeptModel) -> None:
""" """
新增角色部门关联信息数据库操作 新增角色部门关联信息数据库操作
@@ -312,7 +319,7 @@ class RoleDao:
db.add(db_role_dept) db.add(db_role_dept)
@classmethod @classmethod
async def delete_role_dept_dao(cls, db: AsyncSession, role_dept: RoleDeptModel): async def delete_role_dept_dao(cls, db: AsyncSession, role_dept: RoleDeptModel) -> None:
""" """
删除角色部门关联信息数据库操作 删除角色部门关联信息数据库操作
@@ -323,7 +330,7 @@ class RoleDao:
await db.execute(delete(SysRoleDept).where(SysRoleDept.role_id.in_([role_dept.role_id]))) await db.execute(delete(SysRoleDept).where(SysRoleDept.role_id.in_([role_dept.role_id])))
@classmethod @classmethod
async def count_user_role_dao(cls, db: AsyncSession, role_id: int): async def count_user_role_dao(cls, db: AsyncSession, role_id: int) -> Union[int, None]:
""" """
根据角色id查询角色关联用户数量 根据角色id查询角色关联用户数量
@@ -1,6 +1,11 @@
from collections.abc import Sequence
from datetime import datetime, time from datetime import datetime, time
from typing import Any, Union
from sqlalchemy import and_, delete, desc, func, or_, select, update from sqlalchemy import and_, delete, desc, func, or_, select, update
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from common.vo import PageModel
from module_admin.entity.do.dept_do import SysDept from module_admin.entity.do.dept_do import SysDept
from module_admin.entity.do.menu_do import SysMenu from module_admin.entity.do.menu_do import SysMenu
from module_admin.entity.do.post_do import SysPost from module_admin.entity.do.post_do import SysPost
@@ -23,7 +28,7 @@ class UserDao:
""" """
@classmethod @classmethod
async def get_user_by_name(cls, db: AsyncSession, user_name: str): async def get_user_by_name(cls, db: AsyncSession, user_name: str) -> Union[SysUser, None]:
""" """
根据用户名获取用户信息 根据用户名获取用户信息
@@ -47,7 +52,7 @@ class UserDao:
return query_user_info return query_user_info
@classmethod @classmethod
async def get_user_by_info(cls, db: AsyncSession, user: UserModel): async def get_user_by_info(cls, db: AsyncSession, user: UserModel) -> Union[SysUser, None]:
""" """
根据用户参数获取用户信息 根据用户参数获取用户信息
@@ -76,7 +81,7 @@ class UserDao:
return query_user_info return query_user_info
@classmethod @classmethod
async def get_user_by_id(cls, db: AsyncSession, user_id: int): async def get_user_by_id(cls, db: AsyncSession, user_id: int) -> dict[str, Any]:
""" """
根据user_id获取用户信息 根据user_id获取用户信息
@@ -172,18 +177,18 @@ class UserDao:
.all() .all()
) )
results = dict( results = {
user_basic_info=query_user_basic_info, 'user_basic_info': query_user_basic_info,
user_dept_info=query_user_dept_info, 'user_dept_info': query_user_dept_info,
user_role_info=query_user_role_info, 'user_role_info': query_user_role_info,
user_post_info=query_user_post_info, 'user_post_info': query_user_post_info,
user_menu_info=query_user_menu_info, 'user_menu_info': query_user_menu_info,
) }
return results return results
@classmethod @classmethod
async def get_user_detail_by_id(cls, db: AsyncSession, user_id: int): async def get_user_detail_by_id(cls, db: AsyncSession, user_id: int) -> dict[str, Any]:
""" """
根据user_id获取用户详细信息 根据user_id获取用户详细信息
@@ -263,20 +268,20 @@ class UserDao:
.scalars() .scalars()
.all() .all()
) )
results = dict( results = {
user_basic_info=query_user_basic_info, 'user_basic_info': query_user_basic_info,
user_dept_info=query_user_dept_info, 'user_dept_info': query_user_dept_info,
user_role_info=query_user_role_info, 'user_role_info': query_user_role_info,
user_post_info=query_user_post_info, 'user_post_info': query_user_post_info,
user_menu_info=query_user_menu_info, 'user_menu_info': query_user_menu_info,
) }
return results return results
@classmethod @classmethod
async def get_user_list( async def get_user_list(
cls, db: AsyncSession, query_object: UserPageQueryModel, data_scope_sql: str, is_page: bool = False cls, db: AsyncSession, query_object: UserPageQueryModel, data_scope_sql: str, is_page: bool = False
): ) -> Union[PageModel, list[list[dict[str, Any]]]]:
""" """
根据查询参数获取用户列表信息 根据查询参数获取用户列表信息
@@ -321,12 +326,14 @@ class UserDao:
.order_by(SysUser.user_id) .order_by(SysUser.user_id)
.distinct() .distinct()
) )
user_list = await PageUtil.paginate(db, query, query_object.page_num, query_object.page_size, is_page) user_list: Union[PageModel, list[list[dict[str, Any]]]] = await PageUtil.paginate(
db, query, query_object.page_num, query_object.page_size, is_page
)
return user_list return user_list
@classmethod @classmethod
async def add_user_dao(cls, db: AsyncSession, user: UserModel): async def add_user_dao(cls, db: AsyncSession, user: UserModel) -> SysUser:
""" """
新增用户数据库操作 新增用户数据库操作
@@ -341,7 +348,7 @@ class UserDao:
return db_user return db_user
@classmethod @classmethod
async def edit_user_dao(cls, db: AsyncSession, user: dict): async def edit_user_dao(cls, db: AsyncSession, user: dict) -> None:
""" """
编辑用户数据库操作 编辑用户数据库操作
@@ -352,7 +359,7 @@ class UserDao:
await db.execute(update(SysUser), [user]) await db.execute(update(SysUser), [user])
@classmethod @classmethod
async def delete_user_dao(cls, db: AsyncSession, user: UserModel): async def delete_user_dao(cls, db: AsyncSession, user: UserModel) -> None:
""" """
删除用户数据库操作 删除用户数据库操作
@@ -367,7 +374,9 @@ class UserDao:
) )
@classmethod @classmethod
async def get_user_role_allocated_list_by_user_id(cls, db: AsyncSession, query_object: UserRoleQueryModel): async def get_user_role_allocated_list_by_user_id(
cls, db: AsyncSession, query_object: UserRoleQueryModel
) -> Sequence[SysRole]:
""" """
根据用户id获取用户已分配的角色列表信息数据库操作 根据用户id获取用户已分配的角色列表信息数据库操作
@@ -400,7 +409,7 @@ class UserDao:
@classmethod @classmethod
async def get_user_role_allocated_list_by_role_id( async def get_user_role_allocated_list_by_role_id(
cls, db: AsyncSession, query_object: UserRolePageQueryModel, data_scope_sql: str, is_page: bool = False cls, db: AsyncSession, query_object: UserRolePageQueryModel, data_scope_sql: str, is_page: bool = False
): ) -> Union[PageModel, list[dict[str, Any]]]:
""" """
根据角色id获取已分配的用户列表信息 根据角色id获取已分配的用户列表信息
@@ -424,14 +433,16 @@ class UserDao:
) )
.distinct() .distinct()
) )
allocated_user_list = await PageUtil.paginate(db, query, query_object.page_num, query_object.page_size, is_page) allocated_user_list: Union[PageModel, list[dict[str, Any]]] = await PageUtil.paginate(
db, query, query_object.page_num, query_object.page_size, is_page
)
return allocated_user_list return allocated_user_list
@classmethod @classmethod
async def get_user_role_unallocated_list_by_role_id( async def get_user_role_unallocated_list_by_role_id(
cls, db: AsyncSession, query_object: UserRolePageQueryModel, data_scope_sql: str, is_page: bool = False cls, db: AsyncSession, query_object: UserRolePageQueryModel, data_scope_sql: str, is_page: bool = False
): ) -> Union[PageModel, list[dict[str, Any]]]:
""" """
根据角色id获取未分配的用户列表信息 根据角色id获取未分配的用户列表信息
@@ -463,14 +474,14 @@ class UserDao:
) )
.distinct() .distinct()
) )
unallocated_user_list = await PageUtil.paginate( unallocated_user_list: Union[PageModel, list[dict[str, Any]]] = await PageUtil.paginate(
db, query, query_object.page_num, query_object.page_size, is_page db, query, query_object.page_num, query_object.page_size, is_page
) )
return unallocated_user_list return unallocated_user_list
@classmethod @classmethod
async def add_user_role_dao(cls, db: AsyncSession, user_role: UserRoleModel): async def add_user_role_dao(cls, db: AsyncSession, user_role: UserRoleModel) -> None:
""" """
新增用户角色关联信息数据库操作 新增用户角色关联信息数据库操作
@@ -482,7 +493,7 @@ class UserDao:
db.add(db_user_role) db.add(db_user_role)
@classmethod @classmethod
async def delete_user_role_dao(cls, db: AsyncSession, user_role: UserRoleModel): async def delete_user_role_dao(cls, db: AsyncSession, user_role: UserRoleModel) -> None:
""" """
删除用户角色关联信息数据库操作 删除用户角色关联信息数据库操作
@@ -493,7 +504,7 @@ class UserDao:
await db.execute(delete(SysUserRole).where(SysUserRole.user_id.in_([user_role.user_id]))) await db.execute(delete(SysUserRole).where(SysUserRole.user_id.in_([user_role.user_id])))
@classmethod @classmethod
async def delete_user_role_by_user_and_role_dao(cls, db: AsyncSession, user_role: UserRoleModel): async def delete_user_role_by_user_and_role_dao(cls, db: AsyncSession, user_role: UserRoleModel) -> None:
""" """
根据用户id及角色id删除用户角色关联信息数据库操作 根据用户id及角色id删除用户角色关联信息数据库操作
@@ -509,7 +520,7 @@ class UserDao:
) )
@classmethod @classmethod
async def get_user_role_detail(cls, db: AsyncSession, user_role: UserRoleModel): async def get_user_role_detail(cls, db: AsyncSession, user_role: UserRoleModel) -> Union[SysUserRole, None]:
""" """
根据用户角色关联获取用户角色关联详细信息 根据用户角色关联获取用户角色关联详细信息
@@ -532,7 +543,7 @@ class UserDao:
return user_role_info return user_role_info
@classmethod @classmethod
async def add_user_post_dao(cls, db: AsyncSession, user_post: UserPostModel): async def add_user_post_dao(cls, db: AsyncSession, user_post: UserPostModel) -> None:
""" """
新增用户岗位关联信息数据库操作 新增用户岗位关联信息数据库操作
@@ -544,7 +555,7 @@ class UserDao:
db.add(db_user_post) db.add(db_user_post)
@classmethod @classmethod
async def delete_user_post_dao(cls, db: AsyncSession, user_post: UserPostModel): async def delete_user_post_dao(cls, db: AsyncSession, user_post: UserPostModel) -> None:
""" """
删除用户岗位关联信息数据库操作 删除用户岗位关联信息数据库操作
@@ -555,7 +566,7 @@ class UserDao:
await db.execute(delete(SysUserPost).where(SysUserPost.user_id.in_([user_post.user_id]))) await db.execute(delete(SysUserPost).where(SysUserPost.user_id.in_([user_post.user_id])))
@classmethod @classmethod
async def get_user_dept_info(cls, db: AsyncSession, dept_id: int): async def get_user_dept_info(cls, db: AsyncSession, dept_id: int) -> Union[SysDept, None]:
dept_basic_info = ( dept_basic_info = (
( (
await db.execute( await db.execute(
@@ -1,5 +1,7 @@
from datetime import datetime from datetime import datetime
from sqlalchemy import CHAR, Column, DateTime, Integer, String from sqlalchemy import CHAR, Column, DateTime, Integer, String
from config.database import Base from config.database import Base
from config.env import DataBaseConfig from config.env import DataBaseConfig
from utils.common_util import SqlalchemyUtil from utils.common_util import SqlalchemyUtil
@@ -1,5 +1,7 @@
from datetime import datetime from datetime import datetime
from sqlalchemy import BigInteger, CHAR, Column, DateTime, Integer, String
from sqlalchemy import CHAR, BigInteger, Column, DateTime, Integer, String
from config.database import Base from config.database import Base
from config.env import DataBaseConfig from config.env import DataBaseConfig
from utils.common_util import SqlalchemyUtil from utils.common_util import SqlalchemyUtil
@@ -1,5 +1,7 @@
from datetime import datetime from datetime import datetime
from sqlalchemy import BigInteger, CHAR, Column, DateTime, Integer, String
from sqlalchemy import CHAR, BigInteger, Column, DateTime, Integer, String
from config.database import Base from config.database import Base
from config.env import DataBaseConfig from config.env import DataBaseConfig
from utils.common_util import SqlalchemyUtil from utils.common_util import SqlalchemyUtil
@@ -1,5 +1,7 @@
from datetime import datetime from datetime import datetime
from sqlalchemy import BigInteger, CHAR, Column, DateTime, DOUBLE, Index, LargeBinary, String
from sqlalchemy import CHAR, DOUBLE, BigInteger, Column, DateTime, Float, Index, LargeBinary, String, Unicode
from config.database import Base from config.database import Base
from config.env import DataBaseConfig from config.env import DataBaseConfig
from utils.common_util import SqlalchemyUtil from utils.common_util import SqlalchemyUtil
@@ -65,9 +67,11 @@ class ApschedulerJobs(Base):
__tablename__ = 'apscheduler_jobs' __tablename__ = 'apscheduler_jobs'
id = Column(String(191), primary_key=True, nullable=False) id = Column(Unicode(191), primary_key=True, nullable=False)
next_run_time = Column( next_run_time = Column(
DOUBLE, nullable=True, server_default=SqlalchemyUtil.get_server_default_null(DataBaseConfig.db_type, False) DOUBLE if DataBaseConfig.db_type == 'mysql' else Float(25),
nullable=True,
server_default=SqlalchemyUtil.get_server_default_null(DataBaseConfig.db_type, False),
) )
job_state = Column(LargeBinary, nullable=False) job_state = Column(LargeBinary, nullable=False)
@@ -1,5 +1,7 @@
from datetime import datetime from datetime import datetime
from sqlalchemy import BigInteger, CHAR, Column, DateTime, Index, Integer, String
from sqlalchemy import CHAR, BigInteger, Column, DateTime, Index, Integer, String
from config.database import Base from config.database import Base
@@ -38,7 +40,9 @@ class SysOperLog(Base):
business_type = Column(Integer, nullable=True, server_default='0', comment='业务类型(0其它 1新增 2修改 3删除)') business_type = Column(Integer, nullable=True, server_default='0', comment='业务类型(0其它 1新增 2修改 3删除)')
method = Column(String(100), nullable=True, server_default="''", comment='方法名称') method = Column(String(100), nullable=True, server_default="''", comment='方法名称')
request_method = Column(String(10), nullable=True, server_default="''", comment='请求方式') request_method = Column(String(10), nullable=True, server_default="''", comment='请求方式')
operator_type = Column(Integer, nullable=True, server_default='0', comment='操作类别(0其它 1后台用户 2手机端用户)') operator_type = Column(
Integer, nullable=True, server_default='0', comment='操作类别(0其它 1后台用户 2手机端用户)'
)
oper_name = Column(String(50), nullable=True, server_default="''", comment='操作人员') oper_name = Column(String(50), nullable=True, server_default="''", comment='操作人员')
dept_name = Column(String(50), nullable=True, server_default="''", comment='部门名称') dept_name = Column(String(50), nullable=True, server_default="''", comment='部门名称')
oper_url = Column(String(255), nullable=True, server_default="''", comment='请求URL') oper_url = Column(String(255), nullable=True, server_default="''", comment='请求URL')
@@ -1,5 +1,7 @@
from datetime import datetime from datetime import datetime
from sqlalchemy import BigInteger, CHAR, Column, DateTime, Integer, String
from sqlalchemy import CHAR, BigInteger, Column, DateTime, Integer, String
from config.database import Base from config.database import Base
from config.env import DataBaseConfig from config.env import DataBaseConfig
from utils.common_util import SqlalchemyUtil from utils.common_util import SqlalchemyUtil
@@ -1,6 +1,8 @@
from datetime import datetime from datetime import datetime
from sqlalchemy import CHAR, Column, DateTime, Integer, LargeBinary, String from sqlalchemy import CHAR, Column, DateTime, Integer, LargeBinary, String
from sqlalchemy.dialects import mysql from sqlalchemy.dialects import mysql
from config.database import Base from config.database import Base
from config.env import DataBaseConfig from config.env import DataBaseConfig
from utils.common_util import SqlalchemyUtil from utils.common_util import SqlalchemyUtil
@@ -1,5 +1,7 @@
from datetime import datetime from datetime import datetime
from sqlalchemy import BigInteger, CHAR, Column, DateTime, Integer, String
from sqlalchemy import CHAR, BigInteger, Column, DateTime, Integer, String
from config.database import Base from config.database import Base
from config.env import DataBaseConfig from config.env import DataBaseConfig
from utils.common_util import SqlalchemyUtil from utils.common_util import SqlalchemyUtil
@@ -1,6 +1,8 @@
from datetime import datetime from datetime import datetime
from sqlalchemy import BigInteger, CHAR, Column, DateTime, Integer, SmallInteger, String
from sqlalchemy import CHAR, BigInteger, Column, DateTime, Integer, SmallInteger, String
from sqlalchemy.dialects import mysql from sqlalchemy.dialects import mysql
from config.database import Base from config.database import Base
from config.env import DataBaseConfig from config.env import DataBaseConfig
from utils.common_util import SqlalchemyUtil from utils.common_util import SqlalchemyUtil
@@ -1,5 +1,7 @@
from datetime import datetime from datetime import datetime
from sqlalchemy import BigInteger, CHAR, Column, DateTime, String
from sqlalchemy import CHAR, BigInteger, Column, DateTime, String
from config.database import Base from config.database import Base
from config.env import DataBaseConfig from config.env import DataBaseConfig
from utils.common_util import SqlalchemyUtil from utils.common_util import SqlalchemyUtil
@@ -1,6 +1,7 @@
from typing import Any, Optional
from pydantic import BaseModel, ConfigDict, Field from pydantic import BaseModel, ConfigDict, Field
from pydantic.alias_generators import to_camel from pydantic.alias_generators import to_camel
from typing import Any, List, Optional
class CacheMonitorModel(BaseModel): class CacheMonitorModel(BaseModel):
@@ -10,7 +11,7 @@ class CacheMonitorModel(BaseModel):
model_config = ConfigDict(alias_generator=to_camel) model_config = ConfigDict(alias_generator=to_camel)
command_stats: Optional[List] = Field(default=[], description='命令统计') command_stats: Optional[list] = Field(default=[], description='命令统计')
db_size: Optional[int] = Field(default=None, description='Key数量') db_size: Optional[int] = Field(default=None, description='Key数量')
info: Optional[dict] = Field(default={}, description='Redis信息') info: Optional[dict] = Field(default={}, description='Redis信息')
@@ -1,16 +1,7 @@
from typing import Optional
from pydantic import BaseModel, ConfigDict, Field from pydantic import BaseModel, ConfigDict, Field
from pydantic.alias_generators import to_camel from pydantic.alias_generators import to_camel
from typing import Any, Optional
class CrudResponseModel(BaseModel):
"""
操作响应模型
"""
is_success: bool = Field(description='操作是否成功')
message: str = Field(description='响应信息')
result: Optional[Any] = Field(default=None, description='响应结果')
class UploadResponseModel(BaseModel): class UploadResponseModel(BaseModel):
@@ -1,9 +1,9 @@
from datetime import datetime from datetime import datetime
from typing import Literal, Optional, Union
from pydantic import BaseModel, ConfigDict, Field from pydantic import BaseModel, ConfigDict, Field
from pydantic.alias_generators import to_camel from pydantic.alias_generators import to_camel
from pydantic_validation_decorator import NotBlank, Size from pydantic_validation_decorator import NotBlank, Size
from typing import Literal, Optional
from module_admin.annotation.pydantic_annotation import as_query
class ConfigModel(BaseModel): class ConfigModel(BaseModel):
@@ -26,20 +26,20 @@ class ConfigModel(BaseModel):
@NotBlank(field_name='config_key', message='参数名称不能为空') @NotBlank(field_name='config_key', message='参数名称不能为空')
@Size(field_name='config_key', min_length=0, max_length=100, message='参数名称长度不能超过100个字符') @Size(field_name='config_key', min_length=0, max_length=100, message='参数名称长度不能超过100个字符')
def get_config_key(self): def get_config_key(self) -> Union[str, None]:
return self.config_key return self.config_key
@NotBlank(field_name='config_name', message='参数键名不能为空') @NotBlank(field_name='config_name', message='参数键名不能为空')
@Size(field_name='config_name', min_length=0, max_length=100, message='参数键名长度不能超过100个字符') @Size(field_name='config_name', min_length=0, max_length=100, message='参数键名长度不能超过100个字符')
def get_config_name(self): def get_config_name(self) -> Union[str, None]:
return self.config_name return self.config_name
@NotBlank(field_name='config_value', message='参数键值不能为空') @NotBlank(field_name='config_value', message='参数键值不能为空')
@Size(field_name='config_value', min_length=0, max_length=500, message='参数键值长度不能超过500个字符') @Size(field_name='config_value', min_length=0, max_length=500, message='参数键值长度不能超过500个字符')
def get_config_value(self): def get_config_value(self) -> Union[str, None]:
return self.config_value return self.config_value
def validate_fields(self): def validate_fields(self) -> None:
self.get_config_key() self.get_config_key()
self.get_config_name() self.get_config_name()
self.get_config_value() self.get_config_value()
@@ -54,7 +54,6 @@ class ConfigQueryModel(ConfigModel):
end_time: Optional[str] = Field(default=None, description='结束时间') end_time: Optional[str] = Field(default=None, description='结束时间')
@as_query
class ConfigPageQueryModel(ConfigQueryModel): class ConfigPageQueryModel(ConfigQueryModel):
""" """
参数配置管理分页查询模型 参数配置管理分页查询模型
@@ -1,9 +1,9 @@
from datetime import datetime from datetime import datetime
from typing import Literal, Optional, Union
from pydantic import BaseModel, ConfigDict, Field from pydantic import BaseModel, ConfigDict, Field
from pydantic.alias_generators import to_camel from pydantic.alias_generators import to_camel
from pydantic_validation_decorator import Network, NotBlank, Size from pydantic_validation_decorator import Network, NotBlank, Size
from typing import Literal, Optional
from module_admin.annotation.pydantic_annotation import as_query
class DeptModel(BaseModel): class DeptModel(BaseModel):
@@ -30,30 +30,29 @@ class DeptModel(BaseModel):
@NotBlank(field_name='dept_name', message='部门名称不能为空') @NotBlank(field_name='dept_name', message='部门名称不能为空')
@Size(field_name='dept_name', min_length=0, max_length=30, message='部门名称长度不能超过30个字符') @Size(field_name='dept_name', min_length=0, max_length=30, message='部门名称长度不能超过30个字符')
def get_dept_name(self): def get_dept_name(self) -> Union[str, None]:
return self.dept_name return self.dept_name
@NotBlank(field_name='order_num', message='显示顺序不能为空') @NotBlank(field_name='order_num', message='显示顺序不能为空')
def get_order_num(self): def get_order_num(self) -> Union[int, None]:
return self.order_num return self.order_num
@Size(field_name='phone', min_length=0, max_length=11, message='联系电话长度不能超过11个字符') @Size(field_name='phone', min_length=0, max_length=11, message='联系电话长度不能超过11个字符')
def get_phone(self): def get_phone(self) -> Union[str, None]:
return self.phone return self.phone
@Network(field_name='email', field_type='EmailStr', message='邮箱格式不正确') @Network(field_name='email', field_type='EmailStr', message='邮箱格式不正确')
@Size(field_name='email', min_length=0, max_length=50, message='邮箱长度不能超过50个字符') @Size(field_name='email', min_length=0, max_length=50, message='邮箱长度不能超过50个字符')
def get_email(self): def get_email(self) -> Union[str, None]:
return self.email return self.email
def validate_fields(self): def validate_fields(self) -> None:
self.get_dept_name() self.get_dept_name()
self.get_order_num() self.get_order_num()
self.get_phone() self.get_phone()
self.get_email() self.get_email()
@as_query
class DeptQueryModel(DeptModel): class DeptQueryModel(DeptModel):
""" """
部门管理不分页查询模型 部门管理不分页查询模型
@@ -63,6 +62,19 @@ class DeptQueryModel(DeptModel):
end_time: Optional[str] = Field(default=None, description='结束时间') end_time: Optional[str] = Field(default=None, description='结束时间')
class DeptTreeModel(BaseModel):
"""
部门树模型
"""
model_config = ConfigDict(alias_generator=to_camel)
id: int = Field(description='部门id')
label: str = Field(description='部门名称')
parent_id: int = Field(description='父部门id')
children: Optional[list['DeptTreeModel']] = Field(default=None, description='子部门树')
class DeleteDeptModel(BaseModel): class DeleteDeptModel(BaseModel):
""" """
删除部门模型 删除部门模型
@@ -1,9 +1,9 @@
from datetime import datetime from datetime import datetime
from typing import Literal, Optional, Union
from pydantic import BaseModel, ConfigDict, Field from pydantic import BaseModel, ConfigDict, Field
from pydantic.alias_generators import to_camel from pydantic.alias_generators import to_camel
from pydantic_validation_decorator import NotBlank, Pattern, Size from pydantic_validation_decorator import NotBlank, Pattern, Size
from typing import Literal, Optional
from module_admin.annotation.pydantic_annotation import as_query
class DictTypeModel(BaseModel): class DictTypeModel(BaseModel):
@@ -25,7 +25,7 @@ class DictTypeModel(BaseModel):
@NotBlank(field_name='dict_name', message='字典名称不能为空') @NotBlank(field_name='dict_name', message='字典名称不能为空')
@Size(field_name='dict_name', min_length=0, max_length=100, message='字典类型名称长度不能超过100个字符') @Size(field_name='dict_name', min_length=0, max_length=100, message='字典类型名称长度不能超过100个字符')
def get_dict_name(self): def get_dict_name(self) -> Union[str, None]:
return self.dict_name return self.dict_name
@NotBlank(field_name='dict_type', message='字典类型不能为空') @NotBlank(field_name='dict_type', message='字典类型不能为空')
@@ -35,10 +35,10 @@ class DictTypeModel(BaseModel):
regexp='^[a-z][a-z0-9_]*$', regexp='^[a-z][a-z0-9_]*$',
message='字典类型必须以字母开头,且只能为(小写字母,数字,下滑线)', message='字典类型必须以字母开头,且只能为(小写字母,数字,下滑线)',
) )
def get_dict_type(self): def get_dict_type(self) -> Union[str, None]:
return self.dict_type return self.dict_type
def validate_fields(self): def validate_fields(self) -> None:
self.get_dict_name() self.get_dict_name()
self.get_dict_type() self.get_dict_type()
@@ -67,24 +67,24 @@ class DictDataModel(BaseModel):
@NotBlank(field_name='dict_label', message='字典标签不能为空') @NotBlank(field_name='dict_label', message='字典标签不能为空')
@Size(field_name='dict_label', min_length=0, max_length=100, message='字典标签长度不能超过100个字符') @Size(field_name='dict_label', min_length=0, max_length=100, message='字典标签长度不能超过100个字符')
def get_dict_label(self): def get_dict_label(self) -> Union[str, None]:
return self.dict_label return self.dict_label
@NotBlank(field_name='dict_value', message='字典键值不能为空') @NotBlank(field_name='dict_value', message='字典键值不能为空')
@Size(field_name='dict_value', min_length=0, max_length=100, message='字典键值长度不能超过100个字符') @Size(field_name='dict_value', min_length=0, max_length=100, message='字典键值长度不能超过100个字符')
def get_dict_value(self): def get_dict_value(self) -> Union[str, None]:
return self.dict_value return self.dict_value
@NotBlank(field_name='dict_type', message='字典类型不能为空') @NotBlank(field_name='dict_type', message='字典类型不能为空')
@Size(field_name='dict_type', min_length=0, max_length=100, message='字典类型长度不能超过100个字符') @Size(field_name='dict_type', min_length=0, max_length=100, message='字典类型长度不能超过100个字符')
def get_dict_type(self): def get_dict_type(self) -> Union[str, None]:
return self.dict_type return self.dict_type
@Size(field_name='css_class', min_length=0, max_length=100, message='样式属性长度不能超过100个字符') @Size(field_name='css_class', min_length=0, max_length=100, message='样式属性长度不能超过100个字符')
def get_css_class(self): def get_css_class(self) -> Union[str, None]:
return self.css_class return self.css_class
def validate_fields(self): def validate_fields(self) -> None:
self.get_dict_label() self.get_dict_label()
self.get_dict_value() self.get_dict_value()
self.get_dict_type() self.get_dict_type()
@@ -100,7 +100,6 @@ class DictTypeQueryModel(DictTypeModel):
end_time: Optional[str] = Field(default=None, description='结束时间') end_time: Optional[str] = Field(default=None, description='结束时间')
@as_query
class DictTypePageQueryModel(DictTypeQueryModel): class DictTypePageQueryModel(DictTypeQueryModel):
""" """
字典类型管理分页查询模型 字典类型管理分页查询模型
@@ -129,7 +128,6 @@ class DictDataQueryModel(DictDataModel):
end_time: Optional[str] = Field(default=None, description='结束时间') end_time: Optional[str] = Field(default=None, description='结束时间')
@as_query
class DictDataPageQueryModel(DictDataQueryModel): class DictDataPageQueryModel(DictDataQueryModel):
""" """
字典数据管理分页查询模型 字典数据管理分页查询模型
@@ -1,9 +1,9 @@
from datetime import datetime from datetime import datetime
from typing import Literal, Optional, Union
from pydantic import BaseModel, ConfigDict, Field from pydantic import BaseModel, ConfigDict, Field
from pydantic.alias_generators import to_camel from pydantic.alias_generators import to_camel
from pydantic_validation_decorator import NotBlank, Size from pydantic_validation_decorator import NotBlank, Size
from typing import Literal, Optional
from module_admin.annotation.pydantic_annotation import as_query
class JobModel(BaseModel): class JobModel(BaseModel):
@@ -34,15 +34,15 @@ class JobModel(BaseModel):
@NotBlank(field_name='invoke_target', message='调用目标字符串不能为空') @NotBlank(field_name='invoke_target', message='调用目标字符串不能为空')
@Size(field_name='invoke_target', min_length=0, max_length=500, message='调用目标字符串长度不能超过500个字符') @Size(field_name='invoke_target', min_length=0, max_length=500, message='调用目标字符串长度不能超过500个字符')
def get_invoke_target(self): def get_invoke_target(self) -> Union[str, None]:
return self.invoke_target return self.invoke_target
@NotBlank(field_name='cron_expression', message='Cron执行表达式不能为空') @NotBlank(field_name='cron_expression', message='Cron执行表达式不能为空')
@Size(field_name='cron_expression', min_length=0, max_length=255, message='Cron执行表达式不能超过255个字符') @Size(field_name='cron_expression', min_length=0, max_length=255, message='Cron执行表达式不能超过255个字符')
def get_cron_expression(self): def get_cron_expression(self) -> Union[str, None]:
return self.cron_expression return self.cron_expression
def validate_fields(self): def validate_fields(self) -> None:
self.get_invoke_target() self.get_invoke_target()
self.get_cron_expression() self.get_cron_expression()
@@ -77,7 +77,6 @@ class JobQueryModel(JobModel):
end_time: Optional[str] = Field(default=None, description='结束时间') end_time: Optional[str] = Field(default=None, description='结束时间')
@as_query
class JobPageQueryModel(JobQueryModel): class JobPageQueryModel(JobQueryModel):
""" """
定时任务管理分页查询模型 定时任务管理分页查询模型
@@ -114,7 +113,6 @@ class JobLogQueryModel(JobLogModel):
end_time: Optional[str] = Field(default=None, description='结束时间') end_time: Optional[str] = Field(default=None, description='结束时间')
@as_query
class JobLogPageQueryModel(JobLogQueryModel): class JobLogPageQueryModel(JobLogQueryModel):
""" """
定时任务日志管理分页查询模型 定时任务日志管理分页查询模型
@@ -1,8 +1,8 @@
from datetime import datetime from datetime import datetime
from typing import Literal, Optional
from pydantic import BaseModel, ConfigDict, Field from pydantic import BaseModel, ConfigDict, Field
from pydantic.alias_generators import to_camel from pydantic.alias_generators import to_camel
from typing import Literal, Optional
from module_admin.annotation.pydantic_annotation import as_query
class OperLogModel(BaseModel): class OperLogModel(BaseModel):
@@ -68,7 +68,6 @@ class OperLogQueryModel(OperLogModel):
end_time: Optional[str] = Field(default=None, description='结束时间') end_time: Optional[str] = Field(default=None, description='结束时间')
@as_query
class OperLogPageQueryModel(OperLogQueryModel): class OperLogPageQueryModel(OperLogQueryModel):
""" """
操作日志管理分页查询模型 操作日志管理分页查询模型
@@ -101,7 +100,6 @@ class LoginLogQueryModel(LogininforModel):
end_time: Optional[str] = Field(default=None, description='结束时间') end_time: Optional[str] = Field(default=None, description='结束时间')
@as_query
class LoginLogPageQueryModel(LoginLogQueryModel): class LoginLogPageQueryModel(LoginLogQueryModel):
""" """
登录日志管理分页查询模型 登录日志管理分页查询模型
@@ -1,7 +1,9 @@
import re import re
from typing import Optional, Union
from pydantic import BaseModel, ConfigDict, Field, model_validator from pydantic import BaseModel, ConfigDict, Field, model_validator
from pydantic.alias_generators import to_camel from pydantic.alias_generators import to_camel
from typing import List, Optional, Union
from exceptions.exception import ModelValidatorException from exceptions.exception import ModelValidatorException
from module_admin.entity.vo.menu_vo import MenuModel from module_admin.entity.vo.menu_vo import MenuModel
@@ -31,7 +33,6 @@ class UserRegister(BaseModel):
pattern = r"""^[^<>"'|\\]+$""" pattern = r"""^[^<>"'|\\]+$"""
if self.password is None or re.match(pattern, self.password): if self.password is None or re.match(pattern, self.password):
return self return self
else:
raise ModelValidatorException(message='密码不能包含非法字符:< > " \' \\ |') raise ModelValidatorException(message='密码不能包含非法字符:< > " \' \\ |')
@@ -57,7 +58,7 @@ class SmsCode(BaseModel):
class MenuTreeModel(MenuModel): class MenuTreeModel(MenuModel):
children: Optional[Union[List['MenuTreeModel'], None]] = Field(default=None, description='子菜单') children: Optional[Union[list['MenuTreeModel'], None]] = Field(default=None, description='子菜单')
class MetaModel(BaseModel): class MetaModel(BaseModel):
@@ -84,4 +85,4 @@ class RouterModel(BaseModel):
default=None, description='当你一个路由下面的children声明的路由大于1个时,自动会变成嵌套的模式--如组件页面' default=None, description='当你一个路由下面的children声明的路由大于1个时,自动会变成嵌套的模式--如组件页面'
) )
meta: Optional[MetaModel] = Field(default=None, description='其他元素') meta: Optional[MetaModel] = Field(default=None, description='其他元素')
children: Optional[Union[List['RouterModel'], None]] = Field(default=None, description='子路由') children: Optional[Union[list['RouterModel'], None]] = Field(default=None, description='子路由')
@@ -1,9 +1,9 @@
from datetime import datetime from datetime import datetime
from typing import Literal, Optional, Union
from pydantic import BaseModel, ConfigDict, Field from pydantic import BaseModel, ConfigDict, Field
from pydantic.alias_generators import to_camel from pydantic.alias_generators import to_camel
from pydantic_validation_decorator import NotBlank, Size from pydantic_validation_decorator import NotBlank, Size
from typing import Literal, Optional
from module_admin.annotation.pydantic_annotation import as_query
class MenuModel(BaseModel): class MenuModel(BaseModel):
@@ -36,30 +36,30 @@ class MenuModel(BaseModel):
@NotBlank(field_name='menu_name', message='菜单名称不能为空') @NotBlank(field_name='menu_name', message='菜单名称不能为空')
@Size(field_name='menu_name', min_length=0, max_length=50, message='菜单名称长度不能超过50个字符') @Size(field_name='menu_name', min_length=0, max_length=50, message='菜单名称长度不能超过50个字符')
def get_menu_name(self): def get_menu_name(self) -> Union[str, None]:
return self.menu_name return self.menu_name
@NotBlank(field_name='order_num', message='显示顺序不能为空') @NotBlank(field_name='order_num', message='显示顺序不能为空')
def get_order_num(self): def get_order_num(self) -> Union[int, None]:
return self.order_num return self.order_num
@Size(field_name='path', min_length=0, max_length=200, message='路由地址长度不能超过200个字符') @Size(field_name='path', min_length=0, max_length=200, message='路由地址长度不能超过200个字符')
def get_path(self): def get_path(self) -> Union[str, None]:
return self.path return self.path
@Size(field_name='component', min_length=0, max_length=255, message='组件路径长度不能超过255个字符') @Size(field_name='component', min_length=0, max_length=255, message='组件路径长度不能超过255个字符')
def get_component(self): def get_component(self) -> Union[str, None]:
return self.component return self.component
@NotBlank(field_name='menu_type', message='菜单类型不能为空') @NotBlank(field_name='menu_type', message='菜单类型不能为空')
def get_menu_type(self): def get_menu_type(self) -> Union[Literal['M', 'C', 'F'], None]:
return self.menu_type return self.menu_type
@Size(field_name='perms', min_length=0, max_length=100, message='权限标识长度不能超过100个字符') @Size(field_name='perms', min_length=0, max_length=100, message='权限标识长度不能超过100个字符')
def get_perms(self): def get_perms(self) -> Union[str, None]:
return self.perms return self.perms
def validate_fields(self): def validate_fields(self) -> None:
self.get_menu_name() self.get_menu_name()
self.get_order_num() self.get_order_num()
self.get_path() self.get_path()
@@ -68,7 +68,6 @@ class MenuModel(BaseModel):
self.get_perms() self.get_perms()
@as_query
class MenuQueryModel(MenuModel): class MenuQueryModel(MenuModel):
""" """
菜单管理不分页查询模型 菜单管理不分页查询模型
@@ -78,6 +77,19 @@ class MenuQueryModel(MenuModel):
end_time: Optional[str] = Field(default=None, description='结束时间') end_time: Optional[str] = Field(default=None, description='结束时间')
class MenuTreeModel(BaseModel):
"""
菜单树模型
"""
model_config = ConfigDict(alias_generator=to_camel)
id: int = Field(description='菜单id')
label: str = Field(description='菜单名称')
parent_id: int = Field(description='父菜单id')
children: Optional[list['MenuTreeModel']] = Field(default=None, description='子菜单树')
class DeleteMenuModel(BaseModel): class DeleteMenuModel(BaseModel):
""" """
删除菜单模型 删除菜单模型
@@ -1,9 +1,9 @@
from datetime import datetime from datetime import datetime
from typing import Literal, Optional, Union
from pydantic import BaseModel, ConfigDict, Field from pydantic import BaseModel, ConfigDict, Field
from pydantic.alias_generators import to_camel from pydantic.alias_generators import to_camel
from pydantic_validation_decorator import NotBlank, Size, Xss from pydantic_validation_decorator import NotBlank, Size, Xss
from typing import Literal, Optional
from module_admin.annotation.pydantic_annotation import as_query
class NoticeModel(BaseModel): class NoticeModel(BaseModel):
@@ -27,10 +27,10 @@ class NoticeModel(BaseModel):
@Xss(field_name='notice_title', message='公告标题不能包含脚本字符') @Xss(field_name='notice_title', message='公告标题不能包含脚本字符')
@NotBlank(field_name='notice_title', message='公告标题不能为空') @NotBlank(field_name='notice_title', message='公告标题不能为空')
@Size(field_name='notice_title', min_length=0, max_length=50, message='公告标题不能超过50个字符') @Size(field_name='notice_title', min_length=0, max_length=50, message='公告标题不能超过50个字符')
def get_notice_title(self): def get_notice_title(self) -> Union[str, None]:
return self.notice_title return self.notice_title
def validate_fields(self): def validate_fields(self) -> None:
self.get_notice_title() self.get_notice_title()
@@ -43,7 +43,6 @@ class NoticeQueryModel(NoticeModel):
end_time: Optional[str] = Field(default=None, description='结束时间') end_time: Optional[str] = Field(default=None, description='结束时间')
@as_query
class NoticePageQueryModel(NoticeQueryModel): class NoticePageQueryModel(NoticeQueryModel):
""" """
通知公告管理分页查询模型 通知公告管理分页查询模型
@@ -1,8 +1,8 @@
from datetime import datetime from datetime import datetime
from typing import Optional
from pydantic import BaseModel, ConfigDict, Field from pydantic import BaseModel, ConfigDict, Field
from pydantic.alias_generators import to_camel from pydantic.alias_generators import to_camel
from typing import Optional
from module_admin.annotation.pydantic_annotation import as_query
class OnlineModel(BaseModel): class OnlineModel(BaseModel):
@@ -22,7 +22,6 @@ class OnlineModel(BaseModel):
login_time: Optional[datetime] = Field(default=None, description='登录时间') login_time: Optional[datetime] = Field(default=None, description='登录时间')
@as_query
class OnlineQueryModel(OnlineModel): class OnlineQueryModel(OnlineModel):
""" """
岗位管理不分页查询模型 岗位管理不分页查询模型
@@ -32,6 +31,17 @@ class OnlineQueryModel(OnlineModel):
end_time: Optional[str] = Field(default=None, description='结束时间') end_time: Optional[str] = Field(default=None, description='结束时间')
class OnlinePageResponseModel(BaseModel):
"""
在线用户分页响应模型
"""
model_config = ConfigDict(alias_generator=to_camel)
rows: list[OnlineModel] = Field(description='在线用户记录列表')
total: int = Field(description='总记录数')
class DeleteOnlineModel(BaseModel): class DeleteOnlineModel(BaseModel):
""" """
强退在线用户模型 强退在线用户模型
@@ -1,9 +1,9 @@
from datetime import datetime from datetime import datetime
from typing import Literal, Optional, Union
from pydantic import BaseModel, ConfigDict, Field from pydantic import BaseModel, ConfigDict, Field
from pydantic.alias_generators import to_camel from pydantic.alias_generators import to_camel
from pydantic_validation_decorator import NotBlank, Size from pydantic_validation_decorator import NotBlank, Size
from typing import Literal, Optional
from module_admin.annotation.pydantic_annotation import as_query
class PostModel(BaseModel): class PostModel(BaseModel):
@@ -26,19 +26,19 @@ class PostModel(BaseModel):
@NotBlank(field_name='post_code', message='岗位编码不能为空') @NotBlank(field_name='post_code', message='岗位编码不能为空')
@Size(field_name='post_code', min_length=0, max_length=64, message='岗位编码长度不能超过64个字符') @Size(field_name='post_code', min_length=0, max_length=64, message='岗位编码长度不能超过64个字符')
def get_post_code(self): def get_post_code(self) -> Union[str, None]:
return self.post_code return self.post_code
@NotBlank(field_name='post_name', message='岗位名称不能为空') @NotBlank(field_name='post_name', message='岗位名称不能为空')
@Size(field_name='post_name', min_length=0, max_length=50, message='岗位名称长度不能超过50个字符') @Size(field_name='post_name', min_length=0, max_length=50, message='岗位名称长度不能超过50个字符')
def get_post_name(self): def get_post_name(self) -> Union[str, None]:
return self.post_name return self.post_name
@NotBlank(field_name='post_sort', message='显示顺序不能为空') @NotBlank(field_name='post_sort', message='显示顺序不能为空')
def get_post_sort(self): def get_post_sort(self) -> Union[int, None]:
return self.post_sort return self.post_sort
def validate_fields(self): def validate_fields(self) -> None:
self.get_post_code() self.get_post_code()
self.get_post_name() self.get_post_name()
self.get_post_sort() self.get_post_sort()
@@ -53,7 +53,6 @@ class PostQueryModel(PostModel):
end_time: Optional[str] = Field(default=None, description='结束时间') end_time: Optional[str] = Field(default=None, description='结束时间')
@as_query
class PostPageQueryModel(PostQueryModel): class PostPageQueryModel(PostQueryModel):
""" """
岗位管理分页查询模型 岗位管理分页查询模型
@@ -1,9 +1,9 @@
from datetime import datetime from datetime import datetime
from typing import Literal, Optional, Union
from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator
from pydantic.alias_generators import to_camel from pydantic.alias_generators import to_camel
from pydantic_validation_decorator import NotBlank, Size from pydantic_validation_decorator import NotBlank, Size
from typing import List, Literal, Optional, Union
from module_admin.annotation.pydantic_annotation import as_query
class RoleModel(BaseModel): class RoleModel(BaseModel):
@@ -55,19 +55,19 @@ class RoleModel(BaseModel):
@NotBlank(field_name='role_name', message='角色名称不能为空') @NotBlank(field_name='role_name', message='角色名称不能为空')
@Size(field_name='role_name', min_length=0, max_length=30, message='角色名称长度不能超过30个字符') @Size(field_name='role_name', min_length=0, max_length=30, message='角色名称长度不能超过30个字符')
def get_role_name(self): def get_role_name(self) -> Union[str, None]:
return self.role_name return self.role_name
@NotBlank(field_name='role_key', message='权限字符不能为空') @NotBlank(field_name='role_key', message='权限字符不能为空')
@Size(field_name='role_key', min_length=0, max_length=100, message='权限字符长度不能超过100个字符') @Size(field_name='role_key', min_length=0, max_length=100, message='权限字符长度不能超过100个字符')
def get_role_key(self): def get_role_key(self) -> Union[str, None]:
return self.role_key return self.role_key
@NotBlank(field_name='role_sort', message='显示顺序不能为空') @NotBlank(field_name='role_sort', message='显示顺序不能为空')
def get_role_sort(self): def get_role_sort(self) -> Union[int, None]:
return self.role_sort return self.role_sort
def validate_fields(self): def validate_fields(self) -> None:
self.get_role_name() self.get_role_name()
self.get_role_key() self.get_role_key()
self.get_role_sort() self.get_role_sort()
@@ -104,7 +104,6 @@ class RoleQueryModel(RoleModel):
end_time: Optional[str] = Field(default=None, description='结束时间') end_time: Optional[str] = Field(default=None, description='结束时间')
@as_query
class RolePageQueryModel(RoleQueryModel): class RolePageQueryModel(RoleQueryModel):
""" """
角色管理分页查询模型 角色管理分页查询模型
@@ -121,8 +120,8 @@ class RoleMenuQueryModel(BaseModel):
model_config = ConfigDict(alias_generator=to_camel) model_config = ConfigDict(alias_generator=to_camel)
menus: List = Field(default=[], description='菜单信息') menus: list = Field(default=[], description='菜单信息')
checked_keys: List[int] = Field(default=[], description='已选择的菜单ID信息') checked_keys: list[int] = Field(default=[], description='已选择的菜单ID信息')
class RoleDeptQueryModel(BaseModel): class RoleDeptQueryModel(BaseModel):
@@ -132,8 +131,8 @@ class RoleDeptQueryModel(BaseModel):
model_config = ConfigDict(alias_generator=to_camel) model_config = ConfigDict(alias_generator=to_camel)
depts: List = Field(default=[], description='部门信息') depts: list = Field(default=[], description='部门信息')
checked_keys: List[int] = Field(default=[], description='已选择的部门ID信息') checked_keys: list[int] = Field(default=[], description='已选择的部门ID信息')
class AddRoleModel(RoleModel): class AddRoleModel(RoleModel):
@@ -141,8 +140,8 @@ class AddRoleModel(RoleModel):
新增角色模型 新增角色模型
""" """
dept_ids: List = Field(default=[], description='部门ID信息') dept_ids: list = Field(default=[], description='部门ID信息')
menu_ids: List = Field(default=[], description='菜单ID信息') menu_ids: list = Field(default=[], description='菜单ID信息')
type: Optional[str] = Field(default=None, description='操作类型') type: Optional[str] = Field(default=None, description='操作类型')
@@ -153,6 +152,6 @@ class DeleteRoleModel(BaseModel):
model_config = ConfigDict(alias_generator=to_camel) model_config = ConfigDict(alias_generator=to_camel)
role_ids: str = Field(description='需要删除的菜单ID') role_ids: str = Field(description='需要删除的角色ID')
update_by: Optional[str] = Field(default=None, description='更新者') update_by: Optional[str] = Field(default=None, description='更新者')
update_time: Optional[datetime] = Field(default=None, description='更新时间') update_time: Optional[datetime] = Field(default=None, description='更新时间')
@@ -1,6 +1,7 @@
from typing import Optional
from pydantic import BaseModel, ConfigDict, Field from pydantic import BaseModel, ConfigDict, Field
from pydantic.alias_generators import to_camel from pydantic.alias_generators import to_camel
from typing import List, Optional
class CpuInfo(BaseModel): class CpuInfo(BaseModel):
@@ -64,4 +65,4 @@ class ServerMonitorModel(BaseModel):
py: Optional[PyInfo] = Field(description='Python相关信息') py: Optional[PyInfo] = Field(description='Python相关信息')
mem: Optional[MemoryInfo] = Field(description='內存相关信息') mem: Optional[MemoryInfo] = Field(description='內存相关信息')
sys: Optional[SysInfo] = Field(description='服务器相关信息') sys: Optional[SysInfo] = Field(description='服务器相关信息')
sys_files: Optional[List[SysFiles]] = Field(description='磁盘相关信息') sys_files: Optional[list[SysFiles]] = Field(description='磁盘相关信息')
@@ -1,11 +1,12 @@
import re import re
from datetime import datetime from datetime import datetime
from typing import Literal, Optional, Union
from pydantic import BaseModel, ConfigDict, Field, model_validator from pydantic import BaseModel, ConfigDict, Field, model_validator
from pydantic.alias_generators import to_camel from pydantic.alias_generators import to_camel
from pydantic_validation_decorator import Network, NotBlank, Size, Xss from pydantic_validation_decorator import Network, NotBlank, Size, Xss
from typing import List, Literal, Optional, Union
from exceptions.exception import ModelValidatorException from exceptions.exception import ModelValidatorException
from module_admin.annotation.pydantic_annotation import as_query
from module_admin.entity.vo.dept_vo import DeptModel from module_admin.entity.vo.dept_vo import DeptModel
from module_admin.entity.vo.post_vo import PostModel from module_admin.entity.vo.post_vo import PostModel
from module_admin.entity.vo.role_vo import RoleModel from module_admin.entity.vo.role_vo import RoleModel
@@ -53,7 +54,6 @@ class UserModel(BaseModel):
pattern = r"""^[^<>"'|\\]+$""" pattern = r"""^[^<>"'|\\]+$"""
if self.password is None or re.match(pattern, self.password): if self.password is None or re.match(pattern, self.password):
return self return self
else:
raise ModelValidatorException(message='密码不能包含非法字符:< > " \' \\ |') raise ModelValidatorException(message='密码不能包含非法字符:< > " \' \\ |')
@model_validator(mode='after') @model_validator(mode='after')
@@ -67,30 +67,38 @@ class UserModel(BaseModel):
@Xss(field_name='user_name', message='用户账号不能包含脚本字符') @Xss(field_name='user_name', message='用户账号不能包含脚本字符')
@NotBlank(field_name='user_name', message='用户账号不能为空') @NotBlank(field_name='user_name', message='用户账号不能为空')
@Size(field_name='user_name', min_length=0, max_length=30, message='用户账号长度不能超过30个字符') @Size(field_name='user_name', min_length=0, max_length=30, message='用户账号长度不能超过30个字符')
def get_user_name(self): def get_user_name(self) -> Union[str, None]:
return self.user_name return self.user_name
@Xss(field_name='nick_name', message='用户昵称不能包含脚本字符') @Xss(field_name='nick_name', message='用户昵称不能包含脚本字符')
@Size(field_name='nick_name', min_length=0, max_length=30, message='用户昵称长度不能超过30个字符') @Size(field_name='nick_name', min_length=0, max_length=30, message='用户昵称长度不能超过30个字符')
def get_nick_name(self): def get_nick_name(self) -> Union[str, None]:
return self.nick_name return self.nick_name
@Network(field_name='email', field_type='EmailStr', message='邮箱格式不正确') @Network(field_name='email', field_type='EmailStr', message='邮箱格式不正确')
@Size(field_name='email', min_length=0, max_length=50, message='邮箱长度不能超过50个字符') @Size(field_name='email', min_length=0, max_length=50, message='邮箱长度不能超过50个字符')
def get_email(self): def get_email(self) -> Union[str, None]:
return self.email return self.email
@Size(field_name='phonenumber', min_length=0, max_length=11, message='手机号码长度不能超过11个字符') @Size(field_name='phonenumber', min_length=0, max_length=11, message='手机号码长度不能超过11个字符')
def get_phonenumber(self): def get_phonenumber(self) -> Union[str, None]:
return self.phonenumber return self.phonenumber
def validate_fields(self): def validate_fields(self) -> None:
self.get_user_name() self.get_user_name()
self.get_nick_name() self.get_nick_name()
self.get_email() self.get_email()
self.get_phonenumber() self.get_phonenumber()
class UserRowModel(UserModel):
"""
用户列表行数据模型
"""
dept: Optional[DeptModel] = Field(default=None, description='部门信息')
class UserRoleModel(BaseModel): class UserRoleModel(BaseModel):
""" """
用户和角色关联表对应pydantic模型 用户和角色关联表对应pydantic模型
@@ -117,14 +125,14 @@ class UserInfoModel(UserModel):
post_ids: Optional[Union[str, None]] = Field(default=None, description='岗位ID信息') post_ids: Optional[Union[str, None]] = Field(default=None, description='岗位ID信息')
role_ids: Optional[Union[str, None]] = Field(default=None, description='角色ID信息') role_ids: Optional[Union[str, None]] = Field(default=None, description='角色ID信息')
dept: Optional[Union[DeptModel, None]] = Field(default=None, description='部门信息') dept: Optional[Union[DeptModel, None]] = Field(default=None, description='部门信息')
role: Optional[List[Union[RoleModel, None]]] = Field(default=[], description='角色信息') role: Optional[list[Union[RoleModel, None]]] = Field(default=[], description='角色信息')
class CurrentUserModel(BaseModel): class CurrentUserModel(BaseModel):
model_config = ConfigDict(alias_generator=to_camel) model_config = ConfigDict(alias_generator=to_camel)
permissions: List = Field(description='权限信息') permissions: list = Field(description='权限信息')
roles: List = Field(description='角色信息') roles: list = Field(description='角色信息')
user: Union[UserInfoModel, None] = Field(description='用户信息') user: Union[UserInfoModel, None] = Field(description='用户信息')
is_default_modify_pwd: bool = Field(default=False, description='是否初始密码修改提醒') is_default_modify_pwd: bool = Field(default=False, description='是否初始密码修改提醒')
is_password_expired: bool = Field(default=False, description='密码是否过期提醒') is_password_expired: bool = Field(default=False, description='密码是否过期提醒')
@@ -138,10 +146,10 @@ class UserDetailModel(BaseModel):
model_config = ConfigDict(alias_generator=to_camel) model_config = ConfigDict(alias_generator=to_camel)
data: Optional[Union[UserInfoModel, None]] = Field(default=None, description='用户信息') data: Optional[Union[UserInfoModel, None]] = Field(default=None, description='用户信息')
post_ids: Optional[List] = Field(default=None, description='岗位ID信息') post_ids: Optional[list] = Field(default=None, description='岗位ID信息')
posts: List[Union[PostModel, None]] = Field(description='岗位信息') posts: list[Union[PostModel, None]] = Field(description='岗位信息')
role_ids: Optional[List] = Field(default=None, description='角色ID信息') role_ids: Optional[list] = Field(default=None, description='角色ID信息')
roles: List[Union[RoleModel, None]] = Field(description='角色信息') roles: list[Union[RoleModel, None]] = Field(description='角色信息')
class UserProfileModel(BaseModel): class UserProfileModel(BaseModel):
@@ -156,6 +164,16 @@ class UserProfileModel(BaseModel):
role_group: Union[str, None] = Field(description='角色信息') role_group: Union[str, None] = Field(description='角色信息')
class AvatarModel(BaseModel):
"""
上传头像响应模型
"""
model_config = ConfigDict(alias_generator=to_camel)
img_url: str = Field(description='头像地址')
class UserQueryModel(UserModel): class UserQueryModel(UserModel):
""" """
用户管理不分页查询模型 用户管理不分页查询模型
@@ -165,7 +183,6 @@ class UserQueryModel(UserModel):
end_time: Optional[str] = Field(default=None, description='结束时间') end_time: Optional[str] = Field(default=None, description='结束时间')
@as_query
class UserPageQueryModel(UserQueryModel): class UserPageQueryModel(UserQueryModel):
""" """
用户管理分页查询模型 用户管理分页查询模型
@@ -180,8 +197,8 @@ class AddUserModel(UserModel):
新增用户模型 新增用户模型
""" """
role_ids: Optional[List] = Field(default=[], description='角色ID信息') role_ids: Optional[list] = Field(default=[], description='角色ID信息')
post_ids: Optional[List] = Field(default=[], description='岗位ID信息') post_ids: Optional[list] = Field(default=[], description='岗位ID信息')
type: Optional[str] = Field(default=None, description='操作类型') type: Optional[str] = Field(default=None, description='操作类型')
@@ -190,7 +207,7 @@ class EditUserModel(AddUserModel):
编辑用户模型 编辑用户模型
""" """
role: Optional[List] = Field(default=[], description='角色信息') role: Optional[list] = Field(default=[], description='角色信息')
class ResetPasswordModel(BaseModel): class ResetPasswordModel(BaseModel):
@@ -208,7 +225,6 @@ class ResetPasswordModel(BaseModel):
pattern = r"""^[^<>"'|\\]+$""" pattern = r"""^[^<>"'|\\]+$"""
if self.new_password is None or re.match(pattern, self.new_password): if self.new_password is None or re.match(pattern, self.new_password):
return self return self
else:
raise ModelValidatorException(message='密码不能包含非法字符:< > " \' \\ |') raise ModelValidatorException(message='密码不能包含非法字符:< > " \' \\ |')
@@ -242,7 +258,6 @@ class UserRoleQueryModel(UserModel):
role_id: Optional[int] = Field(default=None, description='角色ID') role_id: Optional[int] = Field(default=None, description='角色ID')
@as_query
class UserRolePageQueryModel(UserRoleQueryModel): class UserRolePageQueryModel(UserRoleQueryModel):
""" """
用户角色关联管理分页查询模型 用户角色关联管理分页查询模型
@@ -267,11 +282,10 @@ class UserRoleResponseModel(BaseModel):
model_config = ConfigDict(alias_generator=to_camel) model_config = ConfigDict(alias_generator=to_camel)
roles: List[Union[SelectedRoleModel, None]] = Field(default=[], description='角色信息') roles: list[Union[SelectedRoleModel, None]] = Field(default=[], description='角色信息')
user: UserInfoModel = Field(description='用户信息') user: UserInfoModel = Field(description='用户信息')
@as_query
class CrudUserRoleModel(BaseModel): class CrudUserRoleModel(BaseModel):
""" """
新增删除用户关联角色及角色关联用户模型 新增删除用户关联角色及角色关联用户模型
@@ -1,8 +1,9 @@
from fastapi import Request from fastapi import Request
from config.enums import RedisInitKeyConfig
from common.enums import RedisInitKeyConfig
from common.vo import CrudResponseModel
from config.get_redis import RedisUtil from config.get_redis import RedisUtil
from module_admin.entity.vo.cache_vo import CacheInfoModel, CacheMonitorModel from module_admin.entity.vo.cache_vo import CacheInfoModel, CacheMonitorModel
from module_admin.entity.vo.common_vo import CrudResponseModel
class CacheService: class CacheService:
@@ -11,7 +12,7 @@ class CacheService:
""" """
@classmethod @classmethod
async def get_cache_monitor_statistical_info_services(cls, request: Request): async def get_cache_monitor_statistical_info_services(cls, request: Request) -> CacheMonitorModel:
""" """
获取缓存监控信息service 获取缓存监控信息service
@@ -22,34 +23,33 @@ class CacheService:
db_size = await request.app.state.redis.dbsize() db_size = await request.app.state.redis.dbsize()
command_stats_dict = await request.app.state.redis.info('commandstats') command_stats_dict = await request.app.state.redis.info('commandstats')
command_stats = [ command_stats = [
dict(name=key.split('_')[1], value=str(value.get('calls'))) for key, value in command_stats_dict.items() {'name': key.split('_')[1], 'value': str(value.get('calls'))} for key, value in command_stats_dict.items()
] ]
result = CacheMonitorModel(commandStats=command_stats, dbSize=db_size, info=info) result = CacheMonitorModel(commandStats=command_stats, dbSize=db_size, info=info)
return result return result
@classmethod @classmethod
async def get_cache_monitor_cache_name_services(cls): async def get_cache_monitor_cache_name_services(cls) -> list[CacheInfoModel]:
""" """
获取缓存名称列表信息service 获取缓存名称列表信息service
:return: 缓存名称列表信息 :return: 缓存名称列表信息
""" """
name_list = [] name_list = [
for key_config in RedisInitKeyConfig:
name_list.append(
CacheInfoModel( CacheInfoModel(
cacheKey='', cacheKey='',
cacheName=key_config.key, cacheName=key_config.key,
cacheValue='', cacheValue='',
remark=key_config.remark, remark=key_config.remark,
) )
) for key_config in RedisInitKeyConfig
]
return name_list return name_list
@classmethod @classmethod
async def get_cache_monitor_cache_key_services(cls, request: Request, cache_name: str): async def get_cache_monitor_cache_key_services(cls, request: Request, cache_name: str) -> list[str]:
""" """
获取缓存键名列表信息service 获取缓存键名列表信息service
@@ -57,13 +57,15 @@ class CacheService:
:param cache_name: 缓存名称 :param cache_name: 缓存名称
:return: 缓存键名列表信息 :return: 缓存键名列表信息
""" """
cache_keys = await request.app.state.redis.keys(f'{cache_name}*') cache_keys: list[str] = await request.app.state.redis.keys(f'{cache_name}*')
cache_key_list = [key.split(':', 1)[1] for key in cache_keys if key.startswith(f'{cache_name}:')] cache_key_list = [key.split(':', 1)[1] for key in cache_keys if key.startswith(f'{cache_name}:')]
return cache_key_list return cache_key_list
@classmethod @classmethod
async def get_cache_monitor_cache_value_services(cls, request: Request, cache_name: str, cache_key: str): async def get_cache_monitor_cache_value_services(
cls, request: Request, cache_name: str, cache_key: str
) -> CacheInfoModel:
""" """
获取缓存内容信息service 获取缓存内容信息service
@@ -77,7 +79,7 @@ class CacheService:
return CacheInfoModel(cacheKey=cache_key, cacheName=cache_name, cacheValue=cache_value, remark='') return CacheInfoModel(cacheKey=cache_key, cacheName=cache_name, cacheValue=cache_value, remark='')
@classmethod @classmethod
async def clear_cache_monitor_cache_name_services(cls, request: Request, cache_name: str): async def clear_cache_monitor_cache_name_services(cls, request: Request, cache_name: str) -> CrudResponseModel:
""" """
清除缓存名称对应所有键值service 清除缓存名称对应所有键值service
@@ -92,7 +94,7 @@ class CacheService:
return CrudResponseModel(is_success=True, message=f'{cache_name}对应键值清除成功') return CrudResponseModel(is_success=True, message=f'{cache_name}对应键值清除成功')
@classmethod @classmethod
async def clear_cache_monitor_cache_key_services(cls, request: Request, cache_key: str): async def clear_cache_monitor_cache_key_services(cls, request: Request, cache_key: str) -> CrudResponseModel:
""" """
清除缓存名称对应所有键值service 清除缓存名称对应所有键值service
@@ -107,7 +109,7 @@ class CacheService:
return CrudResponseModel(is_success=True, message=f'{cache_key}清除成功') return CrudResponseModel(is_success=True, message=f'{cache_key}清除成功')
@classmethod @classmethod
async def clear_cache_monitor_all_services(cls, request: Request): async def clear_cache_monitor_all_services(cls, request: Request) -> CrudResponseModel:
""" """
清除所有缓存service 清除所有缓存service
@@ -2,6 +2,7 @@ import base64
import io import io
import os import os
import random import random
from PIL import Image, ImageDraw, ImageFont from PIL import Image, ImageDraw, ImageFont
@@ -11,7 +12,7 @@ class CaptchaService:
""" """
@classmethod @classmethod
async def create_captcha_image_service(cls): async def create_captcha_image_service(cls) -> list[str, int]:
# 创建空白图像 # 创建空白图像
image = Image.new('RGB', (160, 60), color='#EAEAEA') image = Image.new('RGB', (160, 60), color='#EAEAEA')
@@ -1,9 +1,13 @@
import os import os
from datetime import datetime from datetime import datetime
import aiofiles
from fastapi import BackgroundTasks, Request, UploadFile from fastapi import BackgroundTasks, Request, UploadFile
from common.vo import CrudResponseModel
from config.env import UploadConfig from config.env import UploadConfig
from exceptions.exception import ServiceException from exceptions.exception import ServiceException
from module_admin.entity.vo.common_vo import CrudResponseModel, UploadResponseModel from module_admin.entity.vo.common_vo import UploadResponseModel
from utils.upload_util import UploadUtil from utils.upload_util import UploadUtil
@@ -13,7 +17,7 @@ class CommonService:
""" """
@classmethod @classmethod
async def upload_service(cls, request: Request, file: UploadFile): async def upload_service(cls, request: Request, file: UploadFile) -> CrudResponseModel:
""" """
通用上传service 通用上传service
@@ -23,8 +27,9 @@ class CommonService:
""" """
if not UploadUtil.check_file_extension(file): if not UploadUtil.check_file_extension(file):
raise ServiceException(message='文件类型不合法') raise ServiceException(message='文件类型不合法')
else: relative_path = (
relative_path = f'upload/{datetime.now().strftime("%Y")}/{datetime.now().strftime("%m")}/{datetime.now().strftime("%d")}' f'upload/{datetime.now().strftime("%Y")}/{datetime.now().strftime("%m")}/{datetime.now().strftime("%d")}'
)
dir_path = os.path.join(UploadConfig.UPLOAD_PATH, relative_path) dir_path = os.path.join(UploadConfig.UPLOAD_PATH, relative_path)
try: try:
os.makedirs(dir_path) os.makedirs(dir_path)
@@ -32,10 +37,13 @@ class CommonService:
pass pass
filename = f'{file.filename.rsplit(".", 1)[0]}_{datetime.now().strftime("%Y%m%d%H%M%S")}{UploadConfig.UPLOAD_MACHINE}{UploadUtil.generate_random_number()}.{file.filename.rsplit(".")[-1]}' filename = f'{file.filename.rsplit(".", 1)[0]}_{datetime.now().strftime("%Y%m%d%H%M%S")}{UploadConfig.UPLOAD_MACHINE}{UploadUtil.generate_random_number()}.{file.filename.rsplit(".")[-1]}'
filepath = os.path.join(dir_path, filename) filepath = os.path.join(dir_path, filename)
with open(filepath, 'wb') as f: async with aiofiles.open(filepath, 'wb') as f:
# 流式写出大型文件,这里的10代表10MB # 流式写出大型文件,这里的10代表10MB
for chunk in iter(lambda: file.file.read(1024 * 1024 * 10), b''): while True:
f.write(chunk) chunk = await file.read(1024 * 1024 * 10)
if not chunk:
break
await f.write(chunk)
return CrudResponseModel( return CrudResponseModel(
is_success=True, is_success=True,
@@ -49,7 +57,9 @@ class CommonService:
) )
@classmethod @classmethod
async def download_services(cls, background_tasks: BackgroundTasks, file_name, delete: bool): async def download_services(
cls, background_tasks: BackgroundTasks, file_name: str, delete: bool
) -> CrudResponseModel:
""" """
下载下载目录文件service 下载下载目录文件service
@@ -61,15 +71,14 @@ class CommonService:
filepath = os.path.join(UploadConfig.DOWNLOAD_PATH, file_name) filepath = os.path.join(UploadConfig.DOWNLOAD_PATH, file_name)
if '..' in file_name: if '..' in file_name:
raise ServiceException(message='文件名称不合法') raise ServiceException(message='文件名称不合法')
elif not UploadUtil.check_file_exists(filepath): if not UploadUtil.check_file_exists(filepath):
raise ServiceException(message='文件不存在') raise ServiceException(message='文件不存在')
else:
if delete: if delete:
background_tasks.add_task(UploadUtil.delete_file, filepath) background_tasks.add_task(UploadUtil.delete_file, filepath)
return CrudResponseModel(is_success=True, result=UploadUtil.generate_file(filepath), message='下载成功') return CrudResponseModel(is_success=True, result=UploadUtil.generate_file(filepath), message='下载成功')
@classmethod @classmethod
async def download_resource_services(cls, resource: str): async def download_resource_services(cls, resource: str) -> CrudResponseModel:
""" """
下载上传目录文件service 下载上传目录文件service
@@ -85,7 +94,6 @@ class CommonService:
or not UploadUtil.check_file_random_code(filename) or not UploadUtil.check_file_random_code(filename)
): ):
raise ServiceException(message='文件名称不合法') raise ServiceException(message='文件名称不合法')
elif not UploadUtil.check_file_exists(filepath): if not UploadUtil.check_file_exists(filepath):
raise ServiceException(message='文件不存在') raise ServiceException(message='文件不存在')
else:
return CrudResponseModel(is_success=True, result=UploadUtil.generate_file(filepath), message='下载成功') return CrudResponseModel(is_success=True, result=UploadUtil.generate_file(filepath), message='下载成功')
@@ -1,11 +1,14 @@
from typing import Any, Union
from fastapi import Request from fastapi import Request
from redis import asyncio as aioredis
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from typing import List
from config.constant import CommonConstant from common.constant import CommonConstant
from config.enums import RedisInitKeyConfig from common.enums import RedisInitKeyConfig
from common.vo import CrudResponseModel, PageModel
from exceptions.exception import ServiceException from exceptions.exception import ServiceException
from module_admin.dao.config_dao import ConfigDao from module_admin.dao.config_dao import ConfigDao
from module_admin.entity.vo.common_vo import CrudResponseModel
from module_admin.entity.vo.config_vo import ConfigModel, ConfigPageQueryModel, DeleteConfigModel from module_admin.entity.vo.config_vo import ConfigModel, ConfigPageQueryModel, DeleteConfigModel
from utils.common_util import CamelCaseUtil from utils.common_util import CamelCaseUtil
from utils.excel_util import ExcelUtil from utils.excel_util import ExcelUtil
@@ -19,7 +22,7 @@ class ConfigService:
@classmethod @classmethod
async def get_config_list_services( async def get_config_list_services(
cls, query_db: AsyncSession, query_object: ConfigPageQueryModel, is_page: bool = False cls, query_db: AsyncSession, query_object: ConfigPageQueryModel, is_page: bool = False
): ) -> Union[PageModel, list[dict[str, Any]]]:
""" """
获取参数配置列表信息service 获取参数配置列表信息service
@@ -33,7 +36,7 @@ class ConfigService:
return config_list_result return config_list_result
@classmethod @classmethod
async def init_cache_sys_config_services(cls, query_db: AsyncSession, redis): async def init_cache_sys_config_services(cls, query_db: AsyncSession, redis: aioredis.Redis) -> None:
""" """
应用初始化获取所有参数配置对应的键值对信息并缓存service 应用初始化获取所有参数配置对应的键值对信息并缓存service
@@ -46,15 +49,15 @@ class ConfigService:
# 删除匹配的键 # 删除匹配的键
if keys: if keys:
await redis.delete(*keys) await redis.delete(*keys)
config_all = await ConfigDao.get_config_list(query_db, ConfigPageQueryModel(**dict()), is_page=False) config_all = await ConfigDao.get_config_list(query_db, ConfigPageQueryModel(), is_page=False)
for config_obj in config_all: for config_obj in config_all:
await redis.set( await redis.set(
f"{RedisInitKeyConfig.SYS_CONFIG.key}:{config_obj.get('configKey')}", f'{RedisInitKeyConfig.SYS_CONFIG.key}:{config_obj.get("configKey")}',
config_obj.get('configValue'), config_obj.get('configValue'),
) )
@classmethod @classmethod
async def query_config_list_from_cache_services(cls, redis, config_key: str): async def query_config_list_from_cache_services(cls, redis: aioredis.Redis, config_key: str) -> Any:
""" """
从缓存获取参数键名对应值service 从缓存获取参数键名对应值service
@@ -67,7 +70,7 @@ class ConfigService:
return result return result
@classmethod @classmethod
async def check_config_key_unique_services(cls, query_db: AsyncSession, page_object: ConfigModel): async def check_config_key_unique_services(cls, query_db: AsyncSession, page_object: ConfigModel) -> bool:
""" """
校验参数键名是否唯一service 校验参数键名是否唯一service
@@ -82,7 +85,9 @@ class ConfigService:
return CommonConstant.UNIQUE return CommonConstant.UNIQUE
@classmethod @classmethod
async def add_config_services(cls, request: Request, query_db: AsyncSession, page_object: ConfigModel): async def add_config_services(
cls, request: Request, query_db: AsyncSession, page_object: ConfigModel
) -> CrudResponseModel:
""" """
新增参数配置信息service 新增参数配置信息service
@@ -93,7 +98,6 @@ class ConfigService:
""" """
if not await cls.check_config_key_unique_services(query_db, page_object): if not await cls.check_config_key_unique_services(query_db, page_object):
raise ServiceException(message=f'新增参数{page_object.config_name}失败,参数键名已存在') raise ServiceException(message=f'新增参数{page_object.config_name}失败,参数键名已存在')
else:
try: try:
await ConfigDao.add_config_dao(query_db, page_object) await ConfigDao.add_config_dao(query_db, page_object)
await query_db.commit() await query_db.commit()
@@ -106,7 +110,9 @@ class ConfigService:
raise e raise e
@classmethod @classmethod
async def edit_config_services(cls, request: Request, query_db: AsyncSession, page_object: ConfigModel): async def edit_config_services(
cls, request: Request, query_db: AsyncSession, page_object: ConfigModel
) -> CrudResponseModel:
""" """
编辑参数配置信息service 编辑参数配置信息service
@@ -120,7 +126,6 @@ class ConfigService:
if config_info.config_id: if config_info.config_id:
if not await cls.check_config_key_unique_services(query_db, page_object): if not await cls.check_config_key_unique_services(query_db, page_object):
raise ServiceException(message=f'修改参数{page_object.config_name}失败,参数键名已存在') raise ServiceException(message=f'修改参数{page_object.config_name}失败,参数键名已存在')
else:
try: try:
await ConfigDao.edit_config_dao(query_db, edit_config) await ConfigDao.edit_config_dao(query_db, edit_config)
await query_db.commit() await query_db.commit()
@@ -139,7 +144,9 @@ class ConfigService:
raise ServiceException(message='参数配置不存在') raise ServiceException(message='参数配置不存在')
@classmethod @classmethod
async def delete_config_services(cls, request: Request, query_db: AsyncSession, page_object: DeleteConfigModel): async def delete_config_services(
cls, request: Request, query_db: AsyncSession, page_object: DeleteConfigModel
) -> CrudResponseModel:
""" """
删除参数配置信息service 删除参数配置信息service
@@ -156,7 +163,6 @@ class ConfigService:
config_info = await cls.config_detail_services(query_db, int(config_id)) config_info = await cls.config_detail_services(query_db, int(config_id))
if config_info.config_type == CommonConstant.YES: if config_info.config_type == CommonConstant.YES:
raise ServiceException(message=f'内置参数{config_info.config_key}不能删除') raise ServiceException(message=f'内置参数{config_info.config_key}不能删除')
else:
await ConfigDao.delete_config_dao(query_db, ConfigModel(configId=int(config_id))) await ConfigDao.delete_config_dao(query_db, ConfigModel(configId=int(config_id)))
delete_config_key_list.append(f'{RedisInitKeyConfig.SYS_CONFIG.key}:{config_info.config_key}') delete_config_key_list.append(f'{RedisInitKeyConfig.SYS_CONFIG.key}:{config_info.config_key}')
await query_db.commit() await query_db.commit()
@@ -170,7 +176,7 @@ class ConfigService:
raise ServiceException(message='传入参数配置id为空') raise ServiceException(message='传入参数配置id为空')
@classmethod @classmethod
async def config_detail_services(cls, query_db: AsyncSession, config_id: int): async def config_detail_services(cls, query_db: AsyncSession, config_id: int) -> ConfigModel:
""" """
获取参数配置详细信息service 获取参数配置详细信息service
@@ -179,15 +185,12 @@ class ConfigService:
:return: 参数配置id对应的信息 :return: 参数配置id对应的信息
""" """
config = await ConfigDao.get_config_detail_by_id(query_db, config_id=config_id) config = await ConfigDao.get_config_detail_by_id(query_db, config_id=config_id)
if config: result = ConfigModel(**CamelCaseUtil.transform_result(config)) if config else ConfigModel()
result = ConfigModel(**CamelCaseUtil.transform_result(config))
else:
result = ConfigModel(**dict())
return result return result
@staticmethod @staticmethod
async def export_config_list_services(config_list: List): async def export_config_list_services(config_list: list) -> bytes:
""" """
导出参数配置信息service 导出参数配置信息service
@@ -218,7 +221,7 @@ class ConfigService:
return binary_data return binary_data
@classmethod @classmethod
async def refresh_sys_config_services(cls, request: Request, query_db: AsyncSession): async def refresh_sys_config_services(cls, request: Request, query_db: AsyncSession) -> CrudResponseModel:
""" """
刷新字典缓存信息service 刷新字典缓存信息service

Some files were not shown because too many files have changed in this diff Show More