项目初次提交
This commit is contained in:
commit
9e14a3256f
31
.gitignore
vendored
Normal file
31
.gitignore
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
# Editor directories and files
|
||||
.idea/
|
||||
.idea
|
||||
.vscode
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
logs/*
|
||||
!logs/.gitkeep
|
||||
temp/*
|
||||
!temp/.gitkeep
|
||||
!alembic/versions/.gitkeep
|
||||
|
||||
# dotenv
|
||||
.env
|
||||
|
||||
# virtualenv
|
||||
venv/
|
||||
ENV/
|
||||
|
||||
# Spyder business settings
|
||||
.spyderproject
|
||||
|
||||
# Rope business settings
|
||||
.ropeproject
|
||||
*.db
|
||||
.DS_Store
|
||||
__pycache__
|
||||
!migrations/__init__.py
|
||||
*.pyc
|
373
README.md
Normal file
373
README.md
Normal file
@ -0,0 +1,373 @@
|
||||
# FastAPI 项目
|
||||
|
||||
fastapi Github:https://github.com/tiangolo/fastapi
|
||||
|
||||
fastapi 官方文档:https://fastapi.tiangolo.com/zh/
|
||||
|
||||
fastapi 更新说明:https://fastapi.tiangolo.com/zh/release-notes/
|
||||
|
||||
pydantic 官方文档:https://pydantic-docs.helpmanual.io/
|
||||
|
||||
pydantic 数据模型代码生成器官方文档 (Json -> Pydantic):https://koxudaxi.github.io/datamodel-code-generator/
|
||||
|
||||
SQLAlchemy-Utils:https://sqlalchemy-utils.readthedocs.io/en/latest/
|
||||
|
||||
alembic 中文文档:https://hellowac.github.io/alembic_doc/zh/_front_matter.html
|
||||
|
||||
Typer 官方文档:https://typer.tiangolo.com/
|
||||
|
||||
SQLAlchemy 2.0 (官方): https://docs.sqlalchemy.org/en/20/intro.html#installation
|
||||
|
||||
SQLAlchemy 1.4 迁移到 2.0 (官方):https://docs.sqlalchemy.org/en/20/changelog/whatsnew_20.html#whatsnew-20-orm-declarative-typing
|
||||
|
||||
PEP 484 语法(官方):https://peps.python.org/pep-0484/
|
||||
|
||||
|
||||
## 项目结构
|
||||
|
||||
使用的是仿照 Django 项目结构:
|
||||
|
||||
- alembic:数据库迁移配置目录
|
||||
- versions_dev:开发环境数据库迁移文件目录
|
||||
- versions_pro:生产环境数据库迁移文件目录
|
||||
- env.py:映射类配置文件
|
||||
- application:主项目配置目录,也存放了主路由文件
|
||||
- config:基础环境配置文件
|
||||
- development.py:开发环境
|
||||
- production.py:生产环境
|
||||
- settings.py:主项目配置文件
|
||||
- urls.py:主路由文件
|
||||
- apps:项目的app存放目录
|
||||
- vadmin:基础服务
|
||||
- auth:用户 - 角色 - 菜单接口服务
|
||||
- models:ORM 模型目录
|
||||
- params:查询参数依赖项目录
|
||||
- schemas:pydantic 模型,用于数据库序列化操作目录
|
||||
- utils:登录认证功能接口服务
|
||||
- curd.py:数据库操作
|
||||
- views.py:视图函数
|
||||
- core:核心文件目录
|
||||
- crud.py:关系型数据库操作核心封装
|
||||
- database.py:关系型数据库核心配置
|
||||
- data_types.py:自定义数据类型
|
||||
- exception.py:异常处理
|
||||
- logger:日志处理核心配置
|
||||
- middleware.py:中间件核心配置
|
||||
- dependencies.py:常用依赖项
|
||||
- event.py:全局事件
|
||||
- mongo_manage.py:mongodb 数据库操作核心封装
|
||||
- validator.py:pydantic 模型重用验证器
|
||||
- db:ORM模型基类
|
||||
- logs:日志目录
|
||||
- static:静态资源存放目录
|
||||
- utils:封装的一些工具类目录
|
||||
- main.py:主程序入口文件
|
||||
- alembic.ini:数据库迁移配置文件
|
||||
|
||||
## 开发环境
|
||||
|
||||
开发语言:Python 3.10
|
||||
|
||||
开发框架:Fastapi 0.101.1
|
||||
|
||||
ORM 框架:SQLAlchemy 2.0.20
|
||||
|
||||
## 开发工具
|
||||
|
||||
Pycharm 2022.3.2
|
||||
|
||||
推荐插件:Chinese (Simplified) Language Pack / 中文语言包
|
||||
|
||||
代码样式配置:
|
||||
|
||||

|
||||
|
||||
## 使用
|
||||
|
||||
```
|
||||
source /opt/env/kinit-pro/bin/activate
|
||||
|
||||
# 安装依赖库
|
||||
pip3 install -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple/
|
||||
|
||||
# 第三方源:
|
||||
|
||||
1. 阿里源: https://mirrors.aliyun.com/pypi/simple/
|
||||
|
||||
# 线上安装更新依赖库
|
||||
/opt/env/kinit-pro-310/bin/pip install -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple/
|
||||
```
|
||||
|
||||
### 数据初始化
|
||||
|
||||
```shell
|
||||
# 项目根目录下执行,需提前创建好数据库,并且数据库应该为空
|
||||
# 会自动将模型迁移到数据库,并生成初始化数据
|
||||
|
||||
# 在执行前一定要确认要操作的环境与application/settings.DEBUG 设置的环境是一致的,
|
||||
# 不然会导致创建表和生成数据不在一个数据库中!!!!!!!!!!!!!!!!!!!!!!
|
||||
|
||||
# 比如要初始化开发环境,那么env参数应该为 dev,并且 application/settings.DEBUG 应该 = True
|
||||
# 比如要初始化生产环境,那么env参数应该为 pro,并且 application/settings.DEBUG 应该 = False
|
||||
|
||||
# 生产环境
|
||||
python main.py init
|
||||
|
||||
# 开发环境
|
||||
python main.py init --env dev
|
||||
```
|
||||
|
||||
### 运行启动
|
||||
|
||||
```shell
|
||||
# 直接运行main文件
|
||||
python main.py run
|
||||
```
|
||||
|
||||
## 其他操作
|
||||
|
||||
在线文档地址(在配置文件里面设置路径或者关闭)
|
||||
|
||||
```
|
||||
http://127.0.0.1:9000/docs
|
||||
```
|
||||
|
||||
Git更新ignore文件直接修改gitignore是不会生效的,需要先去掉已经托管的文件,修改完成之后再重新添加并提交。
|
||||
```
|
||||
第一步:
|
||||
git rm -r --cached .
|
||||
去掉已经托管的文件
|
||||
|
||||
第二步:
|
||||
修改自己的igonre文件内容
|
||||
|
||||
第三步:
|
||||
git add .
|
||||
git commit -m "clear cached"
|
||||
```
|
||||
|
||||
## 新的数据迁移
|
||||
|
||||
- 新建模型:
|
||||
- 在你的app目录下新建一个models目录,`__init__.py`导入你需要迁移的models
|
||||
```python
|
||||
# app/.../your_app/models/__init__.py
|
||||
from .your_model import YourModel,YourModel2
|
||||
|
||||
```
|
||||
```python
|
||||
# app/.../your_app/models/your_model.py
|
||||
from db.db_base import BaseModel
|
||||
|
||||
class YourModel(BaseModel):
|
||||
# 定义你的model
|
||||
...
|
||||
|
||||
class YourModel2(BaseModel):
|
||||
# 定义你的model
|
||||
...
|
||||
```
|
||||
- 根据模型配置你的alembic:
|
||||
```
|
||||
# alembic.ini
|
||||
[dev]
|
||||
...
|
||||
sqlalchemy.url = mysql+pymysql://your_username:password@ip:port/kinit
|
||||
...
|
||||
```
|
||||
```python
|
||||
# alembic/env.py
|
||||
# 导入项目中的基本映射类,与 需要迁移的 ORM 模型
|
||||
from apps.vadmin.auth.models import *
|
||||
...
|
||||
from apps.xxx.your_app.models import *
|
||||
```
|
||||
- 执行数据库迁移命令(终端执行执行脚本):
|
||||
```shell
|
||||
# 执行命令(生产环境):
|
||||
python main.py migrate
|
||||
|
||||
# 执行命令(开发环境):
|
||||
python main.py migrate --env dev
|
||||
|
||||
# 开发环境的原命令
|
||||
alembic --name dev revision --autogenerate -m 2.0
|
||||
alembic --name dev upgrade head
|
||||
```
|
||||
|
||||
生成迁移文件后,会在alembic迁移目录中的version目录中多个迁移文件
|
||||
|
||||
## 新的CRUD
|
||||
|
||||
- 新的模型文件已经建好(上一步迁移时必须)
|
||||
- 在 scripts/crud_generate/main.py 添加执行命令
|
||||
|
||||
```python
|
||||
# scripts/crud_generate/main.py
|
||||
if __name__ == '__main__':
|
||||
from apps.xxx.your_app.models import YourModel
|
||||
|
||||
crud = CrudGenerate(YourModel, "中文名", "en_name")
|
||||
# 只打印代码,不执行创建写入
|
||||
crud.generate_codes()
|
||||
# 创建并写入代码
|
||||
crud.main()
|
||||
```
|
||||
|
||||
- 生成后会自动创建crud, params,schema, views
|
||||
|
||||
## 新的路由配置
|
||||
|
||||
```python
|
||||
# application/urls.py
|
||||
|
||||
from apps.xxx.your_app.views import app as your_app
|
||||
|
||||
urlpatterns = [
|
||||
...,
|
||||
{"ApiRouter": your_app, "prefix": "/your_router", "tags": ["your_tag"]},
|
||||
]
|
||||
```
|
||||
|
||||
完成后在 http://127.0.0.1:9000/docs 验证生成的接口
|
||||
|
||||
## 查询数据
|
||||
|
||||
### 自定义的一些查询过滤
|
||||
|
||||
```python
|
||||
# 日期查询
|
||||
# 值的类型:str
|
||||
# 值得格式:%Y-%m-%d:2023-05-14
|
||||
字段名称=("date", 值)
|
||||
|
||||
# 模糊查询
|
||||
# 值的类型: str
|
||||
字段名称=("like", 值)
|
||||
|
||||
# in 查询
|
||||
# 值的类型:list
|
||||
字段名称=("in", 值)
|
||||
|
||||
# 时间区间查询
|
||||
# 值的类型:tuple 或者 list
|
||||
字段名称=("between", 值)
|
||||
|
||||
# 月份查询
|
||||
# 值的类型:str
|
||||
# 值的格式:%Y-%m:2023-05
|
||||
字段名称=("month", 值)
|
||||
|
||||
# 不等于查询
|
||||
字段名称=("!=", 值)
|
||||
|
||||
# 大于查询
|
||||
字段名称=(">", 值)
|
||||
|
||||
# 等于 None
|
||||
字段名称=("None")
|
||||
|
||||
# 不等于 None
|
||||
字段名称=("not None")
|
||||
```
|
||||
|
||||
代码部分:
|
||||
|
||||
```python
|
||||
def __dict_filter(self, **kwargs) -> list[BinaryExpression]:
|
||||
"""
|
||||
字典过滤
|
||||
:param model:
|
||||
:param kwargs:
|
||||
"""
|
||||
conditions = []
|
||||
for field, value in kwargs.items():
|
||||
if value is not None and value != "":
|
||||
attr = getattr(self.model, field)
|
||||
if isinstance(value, tuple):
|
||||
if len(value) == 1:
|
||||
if value[0] == "None":
|
||||
conditions.append(attr.is_(None))
|
||||
elif value[0] == "not None":
|
||||
conditions.append(attr.isnot(None))
|
||||
else:
|
||||
raise CustomException("SQL查询语法错误")
|
||||
elif len(value) == 2 and value[1] not in [None, [], ""]:
|
||||
if value[0] == "date":
|
||||
# 根据日期查询, 关键函数是:func.time_format和func.date_format
|
||||
conditions.append(func.date_format(attr, "%Y-%m-%d") == value[1])
|
||||
elif value[0] == "like":
|
||||
conditions.append(attr.like(f"%{value[1]}%"))
|
||||
elif value[0] == "in":
|
||||
conditions.append(attr.in_(value[1]))
|
||||
elif value[0] == "between" and len(value[1]) == 2:
|
||||
conditions.append(attr.between(value[1][0], value[1][1]))
|
||||
elif value[0] == "month":
|
||||
conditions.append(func.date_format(attr, "%Y-%m") == value[1])
|
||||
elif value[0] == "!=":
|
||||
conditions.append(attr != value[1])
|
||||
elif value[0] == ">":
|
||||
conditions.append(attr > value[1])
|
||||
elif value[0] == "<=":
|
||||
conditions.append(attr <= value[1])
|
||||
else:
|
||||
raise CustomException("SQL查询语法错误")
|
||||
else:
|
||||
conditions.append(attr == value)
|
||||
return conditions
|
||||
```
|
||||
|
||||
示例:
|
||||
|
||||
查询所有用户id为1或2或 4或6,并且email不为空,并且名称包括李:
|
||||
|
||||
```python
|
||||
users = UserDal(db).get_datas(limit=0, id=("in", [1,2,4,6]), email=("not None", ), name=("like", "李"))
|
||||
|
||||
# limit=0:表示返回所有结果数据
|
||||
# 这里的 get_datas 默认返回的是 pydantic 模型数据
|
||||
# 如果需要返回用户对象列表,使用如下语句:
|
||||
users = UserDal(db).get_datas(
|
||||
limit=0,
|
||||
id=("in", [1,2,4,6]),
|
||||
email=("not None", ),
|
||||
name=("like", "李"),
|
||||
v_return_objs=True
|
||||
)
|
||||
```
|
||||
|
||||
查询所有用户id为1或2或 4或6,并且email不为空,并且名称包括李:
|
||||
|
||||
查询第一页,每页两条数据,并返回总数,同样可以通过 `get_datas` 实现原始查询方式:
|
||||
|
||||
```python
|
||||
v_where = [VadminUser.id.in_([1,2,4,6]), VadminUser.email.isnot(None), VadminUser.name.like(f"%李%")]
|
||||
users, count = UserDal(db).get_datas(limit=2, v_where=v_where, v_return_count=True)
|
||||
|
||||
# 这里的 get_datas 默认返回的是 pydantic 模型数据
|
||||
# 如果需要返回用户对象列表,使用如下语句:
|
||||
users, count = UserDal(db).get_datas(
|
||||
limit=2,
|
||||
v_where=v_where,
|
||||
v_return_count=True
|
||||
v_return_objs=True
|
||||
)
|
||||
```
|
||||
|
||||
### 外键查询示例
|
||||
|
||||
以常见问题表为主表,查询出创建用户名称为kinit的用户,创建了哪些常见问题,并加载出用户信息:
|
||||
|
||||
```python
|
||||
v_options = [joinedload(VadminIssue.create_user)]
|
||||
v_join = [["create_user"]]
|
||||
v_where = [VadminUser.name == "kinit"]
|
||||
datas = await crud.IssueCategoryDal(auth.db).get_datas(
|
||||
limit=0,
|
||||
v_options=options,
|
||||
v_join=v_join,
|
||||
v_where=v_where,
|
||||
v_return_objs=True
|
||||
)
|
||||
```
|
||||
|
108
alembic.ini
Normal file
108
alembic.ini
Normal file
@ -0,0 +1,108 @@
|
||||
# A generic, single database configuration.
|
||||
|
||||
[DEFAULT]
|
||||
# path to migration scripts
|
||||
script_location = alembic
|
||||
|
||||
# template used to generate migration files
|
||||
# file_template = %%(rev)s_%%(slug)s
|
||||
|
||||
# sys.path path, will be prepended to sys.path if present.
|
||||
# defaults to the current working directory.
|
||||
prepend_sys_path = .
|
||||
|
||||
# timezone to use when rendering the date within the migration file
|
||||
# as well as the filename.
|
||||
# If specified, requires the python-dateutil library that can be
|
||||
# installed by adding `alembic[tz]` to the pip requirements
|
||||
# string value is passed to dateutil.tz.gettz()
|
||||
# leave blank for localtime
|
||||
# timezone =
|
||||
|
||||
# max length of characters to apply to the
|
||||
# "slug" field
|
||||
# truncate_slug_length = 40
|
||||
|
||||
# set to 'true' to run the environment during
|
||||
# the 'revision' command, regardless of autogenerate
|
||||
# revision_environment = false
|
||||
|
||||
# set to 'true' to allow .pyc and .pyo files without
|
||||
# a source .py file to be detected as revisions in the
|
||||
# versions/ directory
|
||||
# sourceless = false
|
||||
|
||||
# version location specification; This defaults
|
||||
# to alembic/versions. When using multiple version
|
||||
# directories, initial revisions must be specified with --version-path.
|
||||
# The path separator used here should be the separator specified by "version_path_separator"
|
||||
# version_locations = %(here)s/bar:%(here)s/bat:alembic/versions
|
||||
|
||||
# version path separator; As mentioned above, this is the character used to split
|
||||
# version_locations. Valid values are:
|
||||
#
|
||||
# version_path_separator = :
|
||||
# version_path_separator = ;
|
||||
# version_path_separator = space
|
||||
version_path_separator = os
|
||||
|
||||
# the output encoding used when revision files
|
||||
# are written from script.py.mako
|
||||
# output_encoding = utf-8
|
||||
|
||||
# mysql+pymysql://username:password@host:post/name
|
||||
[dev]
|
||||
version_locations = %(here)s/alembic/versions_dev
|
||||
sqlalchemy.url = mysql+pymysql://root:root@127.0.0.1:3306/kinit
|
||||
|
||||
|
||||
[pro]
|
||||
version_locations = %(here)s/alembic/versions_pro
|
||||
sqlalchemy.url = mysql+pymysql://root:123456@177.8.0.7:3306/kinit
|
||||
|
||||
|
||||
[post_write_hooks]
|
||||
# post_write_hooks defines scripts or Python functions that are run
|
||||
# on newly generated revision scripts. See the documentation for further
|
||||
# detail and examples
|
||||
|
||||
# format using "black" - use the console_scripts runner, against the "black" entrypoint
|
||||
# hooks = black
|
||||
# black.type = console_scripts
|
||||
# black.entrypoint = black
|
||||
# black.options = -l 79 REVISION_SCRIPT_FILENAME
|
||||
|
||||
# Logging configuration
|
||||
[loggers]
|
||||
keys = root,sqlalchemy,alembic
|
||||
|
||||
[handlers]
|
||||
keys = console
|
||||
|
||||
[formatters]
|
||||
keys = generic
|
||||
|
||||
[logger_root]
|
||||
level = WARN
|
||||
handlers = console
|
||||
qualname =
|
||||
|
||||
[logger_sqlalchemy]
|
||||
level = WARN
|
||||
handlers =
|
||||
qualname = sqlalchemy.engine
|
||||
|
||||
[logger_alembic]
|
||||
level = INFO
|
||||
handlers =
|
||||
qualname = alembic
|
||||
|
||||
[handler_console]
|
||||
class = StreamHandler
|
||||
args = (sys.stderr,)
|
||||
level = NOTSET
|
||||
formatter = generic
|
||||
|
||||
[formatter_generic]
|
||||
format = %(levelname)-5.5s [%(name)s] %(message)s
|
||||
datefmt = %H:%M:%S
|
1
alembic/README
Normal file
1
alembic/README
Normal file
@ -0,0 +1 @@
|
||||
Generic single-database configuration.
|
95
alembic/env.py
Normal file
95
alembic/env.py
Normal file
@ -0,0 +1,95 @@
|
||||
from logging.config import fileConfig
|
||||
|
||||
from sqlalchemy import engine_from_config
|
||||
from sqlalchemy import pool
|
||||
|
||||
from alembic import context
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
from core.database import Base
|
||||
|
||||
# this is the Alembic Config object, which provides
|
||||
# access to the values within the .ini file in use.
|
||||
config = context.config
|
||||
|
||||
# Interpret the config file for Python logging.
|
||||
# This line sets up loggers basically.
|
||||
fileConfig(config.config_file_name)
|
||||
|
||||
# add your model's MetaData object here
|
||||
# for 'autogenerate' support
|
||||
# from myapp import mymodel
|
||||
# target_metadata = mymodel.Base.metadata
|
||||
# target_metadata = None
|
||||
|
||||
# other values from the config, defined by the needs of env.py,
|
||||
# can be acquired:
|
||||
# my_important_option = config.get_main_option("my_important_option")
|
||||
# ... etc.
|
||||
|
||||
# 添加当前项目路径到环境变量
|
||||
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
sys.path.append(BASE_DIR)
|
||||
|
||||
# 导入项目中的基本映射类,与 需要迁移的 ORM 模型
|
||||
from apps.vadmin.auth.models import *
|
||||
from apps.vadmin.system.models import *
|
||||
from apps.vadmin.record.models import *
|
||||
from apps.vadmin.help.models import *
|
||||
from apps.vadmin.resource.models import *
|
||||
from apps.business.project.models.project import *
|
||||
from apps.business.train.models.train import *
|
||||
from apps.business.detect.models.detect import *
|
||||
|
||||
# 修改配置中的参数
|
||||
target_metadata = Base.metadata
|
||||
|
||||
|
||||
def run_migrations_offline():
|
||||
"""
|
||||
以“脱机”模式运行迁移。
|
||||
"""
|
||||
url = config.get_main_option("sqlalchemy.url")
|
||||
context.configure(
|
||||
url=url,
|
||||
target_metadata=target_metadata,
|
||||
literal_binds=True,
|
||||
dialect_opts={"paramstyle": "named"},
|
||||
compare_type=True, # 是否检查字段类型,字段长度
|
||||
compare_server_default=True # 是否比较在数据库中的默认值
|
||||
)
|
||||
|
||||
with context.begin_transaction():
|
||||
context.run_migrations()
|
||||
|
||||
|
||||
def run_migrations_online():
|
||||
"""
|
||||
以“在线”模式运行迁移。
|
||||
"""
|
||||
connectable = engine_from_config(
|
||||
config.get_section(config.config_ini_section),
|
||||
prefix="sqlalchemy.",
|
||||
poolclass=pool.NullPool,
|
||||
)
|
||||
|
||||
with connectable.connect() as connection:
|
||||
context.configure(
|
||||
connection=connection,
|
||||
target_metadata=target_metadata,
|
||||
compare_type=True, # 是否检查字段类型,字段长度
|
||||
compare_server_default=True # 是否比较在数据库中的默认值
|
||||
)
|
||||
|
||||
with context.begin_transaction():
|
||||
context.run_migrations()
|
||||
|
||||
|
||||
if context.is_offline_mode():
|
||||
print("offline")
|
||||
run_migrations_offline()
|
||||
else:
|
||||
print("online")
|
||||
run_migrations_online()
|
24
alembic/script.py.mako
Normal file
24
alembic/script.py.mako
Normal file
@ -0,0 +1,24 @@
|
||||
"""${message}
|
||||
|
||||
Revision ID: ${up_revision}
|
||||
Revises: ${down_revision | comma,n}
|
||||
Create Date: ${create_date}
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
${imports if imports else ""}
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = ${repr(up_revision)}
|
||||
down_revision = ${repr(down_revision)}
|
||||
branch_labels = ${repr(branch_labels)}
|
||||
depends_on = ${repr(depends_on)}
|
||||
|
||||
|
||||
def upgrade():
|
||||
${upgrades if upgrades else "pass"}
|
||||
|
||||
|
||||
def downgrade():
|
||||
${downgrades if downgrades else "pass"}
|
0
alembic/versions_dev/.gitkeep
Normal file
0
alembic/versions_dev/.gitkeep
Normal file
468
alembic/versions_dev/655d3fa2c68d_3_10_1.py
Normal file
468
alembic/versions_dev/655d3fa2c68d_3_10_1.py
Normal file
@ -0,0 +1,468 @@
|
||||
"""3.10.1
|
||||
|
||||
Revision ID: 655d3fa2c68d
|
||||
Revises: e2dec87a3d12
|
||||
Create Date: 2025-04-03 10:19:06.323166
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import mysql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '655d3fa2c68d'
|
||||
down_revision = 'e2dec87a3d12'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('project_detect',
|
||||
sa.Column('project_id', sa.Integer(), nullable=False),
|
||||
sa.Column('detect_name', sa.String(length=64), nullable=False),
|
||||
sa.Column('detect_version', sa.Integer(), nullable=False),
|
||||
sa.Column('detect_no', sa.String(length=32), nullable=False),
|
||||
sa.Column('detect_status', sa.Integer(), nullable=False),
|
||||
sa.Column('file_type', sa.String(length=10), nullable=False),
|
||||
sa.Column('folder_url', sa.String(length=255), nullable=False),
|
||||
sa.Column('rtsp_url', sa.String(length=255), nullable=False),
|
||||
sa.Column('user_id', sa.Integer(), nullable=False),
|
||||
sa.Column('id', sa.Integer(), nullable=False, comment='主键ID'),
|
||||
sa.Column('create_datetime', sa.DateTime(), server_default=sa.text('now()'), nullable=False, comment='创建时间'),
|
||||
sa.Column('update_datetime', sa.DateTime(), server_default=sa.text('now()'), nullable=False, comment='更新时间'),
|
||||
sa.Column('delete_datetime', sa.DateTime(), nullable=True, comment='删除时间'),
|
||||
sa.Column('is_delete', sa.Boolean(), nullable=False, comment='是否软删除'),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
comment='项目推理集合'
|
||||
)
|
||||
op.create_table('project_detect_img',
|
||||
sa.Column('detect_id', sa.Integer(), nullable=False),
|
||||
sa.Column('file_name', sa.String(length=64), nullable=False),
|
||||
sa.Column('image_url', sa.String(length=255), nullable=False),
|
||||
sa.Column('thumb_image_url', sa.String(length=255), nullable=False),
|
||||
sa.Column('user_id', sa.Integer(), nullable=False),
|
||||
sa.Column('id', sa.Integer(), nullable=False, comment='主键ID'),
|
||||
sa.Column('create_datetime', sa.DateTime(), server_default=sa.text('now()'), nullable=False, comment='创建时间'),
|
||||
sa.Column('update_datetime', sa.DateTime(), server_default=sa.text('now()'), nullable=False, comment='更新时间'),
|
||||
sa.Column('delete_datetime', sa.DateTime(), nullable=True, comment='删除时间'),
|
||||
sa.Column('is_delete', sa.Boolean(), nullable=False, comment='是否软删除'),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
comment='待推理图片'
|
||||
)
|
||||
op.create_table('project_detect_log',
|
||||
sa.Column('detect_id', sa.Integer(), nullable=False),
|
||||
sa.Column('detect_version', sa.String(length=10), nullable=False),
|
||||
sa.Column('detect_name', sa.String(length=64), nullable=False),
|
||||
sa.Column('train_id', sa.Integer(), nullable=False),
|
||||
sa.Column('train_version', sa.String(length=10), nullable=False),
|
||||
sa.Column('pt_type', sa.String(length=10), nullable=False),
|
||||
sa.Column('pt_url', sa.String(length=255), nullable=False),
|
||||
sa.Column('folder_url', sa.String(length=255), nullable=False),
|
||||
sa.Column('detect_folder_url', sa.String(length=255), nullable=False),
|
||||
sa.Column('user_id', sa.Integer(), nullable=False),
|
||||
sa.Column('id', sa.Integer(), nullable=False, comment='主键ID'),
|
||||
sa.Column('create_datetime', sa.DateTime(), server_default=sa.text('now()'), nullable=False, comment='创建时间'),
|
||||
sa.Column('update_datetime', sa.DateTime(), server_default=sa.text('now()'), nullable=False, comment='更新时间'),
|
||||
sa.Column('delete_datetime', sa.DateTime(), nullable=True, comment='删除时间'),
|
||||
sa.Column('is_delete', sa.Boolean(), nullable=False, comment='是否软删除'),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
comment='项目推理记录'
|
||||
)
|
||||
op.create_table('project_detect_log_img',
|
||||
sa.Column('log_id', sa.Integer(), nullable=False),
|
||||
sa.Column('file_name', sa.String(length=64), nullable=False),
|
||||
sa.Column('image_url', sa.String(length=255), nullable=False),
|
||||
sa.Column('id', sa.Integer(), nullable=False, comment='主键ID'),
|
||||
sa.Column('create_datetime', sa.DateTime(), server_default=sa.text('now()'), nullable=False, comment='创建时间'),
|
||||
sa.Column('update_datetime', sa.DateTime(), server_default=sa.text('now()'), nullable=False, comment='更新时间'),
|
||||
sa.Column('delete_datetime', sa.DateTime(), nullable=True, comment='删除时间'),
|
||||
sa.Column('is_delete', sa.Boolean(), nullable=False, comment='是否软删除'),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
comment='项目训练版本信息表'
|
||||
)
|
||||
op.create_table('project_image',
|
||||
sa.Column('img_type', sa.String(length=10), nullable=False),
|
||||
sa.Column('file_name', sa.String(length=64), nullable=False),
|
||||
sa.Column('image_url', sa.String(length=255), nullable=False),
|
||||
sa.Column('thumb_image_url', sa.String(length=255), nullable=False),
|
||||
sa.Column('project_id', sa.Integer(), nullable=False),
|
||||
sa.Column('id', sa.Integer(), nullable=False, comment='主键ID'),
|
||||
sa.Column('create_datetime', sa.DateTime(), server_default=sa.text('now()'), nullable=False, comment='创建时间'),
|
||||
sa.Column('update_datetime', sa.DateTime(), server_default=sa.text('now()'), nullable=False, comment='更新时间'),
|
||||
sa.Column('delete_datetime', sa.DateTime(), nullable=True, comment='删除时间'),
|
||||
sa.Column('is_delete', sa.Boolean(), nullable=False, comment='是否软删除'),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
comment='项目图片表'
|
||||
)
|
||||
op.create_table('project_img_label',
|
||||
sa.Column('image_id', sa.Integer(), nullable=False),
|
||||
sa.Column('label_id', sa.Integer(), nullable=False),
|
||||
sa.Column('mark_center_x', sa.String(length=64), nullable=False),
|
||||
sa.Column('mark_center_y', sa.String(length=64), nullable=False),
|
||||
sa.Column('mark_width', sa.String(length=64), nullable=False),
|
||||
sa.Column('mark_height', sa.String(length=64), nullable=False),
|
||||
sa.Column('id', sa.Integer(), nullable=False, comment='主键ID'),
|
||||
sa.Column('create_datetime', sa.DateTime(), server_default=sa.text('now()'), nullable=False, comment='创建时间'),
|
||||
sa.Column('update_datetime', sa.DateTime(), server_default=sa.text('now()'), nullable=False, comment='更新时间'),
|
||||
sa.Column('delete_datetime', sa.DateTime(), nullable=True, comment='删除时间'),
|
||||
sa.Column('is_delete', sa.Boolean(), nullable=False, comment='是否软删除'),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
comment='项目图片标签对应表,一张图片对应多个label'
|
||||
)
|
||||
op.create_table('project_img_leafer',
|
||||
sa.Column('image_id', sa.Integer(), nullable=False),
|
||||
sa.Column('leafer', sa.JSON(), nullable=False),
|
||||
sa.Column('id', sa.Integer(), nullable=False, comment='主键ID'),
|
||||
sa.Column('create_datetime', sa.DateTime(), server_default=sa.text('now()'), nullable=False, comment='创建时间'),
|
||||
sa.Column('update_datetime', sa.DateTime(), server_default=sa.text('now()'), nullable=False, comment='更新时间'),
|
||||
sa.Column('delete_datetime', sa.DateTime(), nullable=True, comment='删除时间'),
|
||||
sa.Column('is_delete', sa.Boolean(), nullable=False, comment='是否软删除'),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
comment='项目图片leafer表'
|
||||
)
|
||||
op.create_table('project_info',
|
||||
sa.Column('project_no', sa.String(length=32), nullable=False),
|
||||
sa.Column('project_name', sa.String(length=32), nullable=False),
|
||||
sa.Column('type_code', sa.String(length=10), nullable=False),
|
||||
sa.Column('description', sa.String(length=255), nullable=False),
|
||||
sa.Column('project_status', sa.String(length=10), nullable=False),
|
||||
sa.Column('user_id', sa.Integer(), nullable=False),
|
||||
sa.Column('dept_id', sa.Integer(), nullable=False),
|
||||
sa.Column('train_version', sa.Integer(), nullable=False),
|
||||
sa.Column('del_flag', sa.Integer(), nullable=False),
|
||||
sa.Column('id', sa.Integer(), nullable=False, comment='主键ID'),
|
||||
sa.Column('create_datetime', sa.DateTime(), server_default=sa.text('now()'), nullable=False, comment='创建时间'),
|
||||
sa.Column('update_datetime', sa.DateTime(), server_default=sa.text('now()'), nullable=False, comment='更新时间'),
|
||||
sa.Column('delete_datetime', sa.DateTime(), nullable=True, comment='删除时间'),
|
||||
sa.Column('is_delete', sa.Boolean(), nullable=False, comment='是否软删除'),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('project_name'),
|
||||
sa.UniqueConstraint('project_no'),
|
||||
comment='项目类别表 - 标识项目的类型目前存在的(目标识别,OCR识别,瑕疵检测,图像分类)'
|
||||
)
|
||||
op.create_table('project_label',
|
||||
sa.Column('label_name', sa.String(length=32), nullable=False),
|
||||
sa.Column('project_id', sa.Integer(), nullable=False),
|
||||
sa.Column('meta', sa.JSON(), nullable=False),
|
||||
sa.Column('id', sa.Integer(), nullable=False, comment='主键ID'),
|
||||
sa.Column('create_datetime', sa.DateTime(), server_default=sa.text('now()'), nullable=False, comment='创建时间'),
|
||||
sa.Column('update_datetime', sa.DateTime(), server_default=sa.text('now()'), nullable=False, comment='更新时间'),
|
||||
sa.Column('delete_datetime', sa.DateTime(), nullable=True, comment='删除时间'),
|
||||
sa.Column('is_delete', sa.Boolean(), nullable=False, comment='是否软删除'),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('label_name'),
|
||||
comment='项目标签表'
|
||||
)
|
||||
op.create_table('project_train',
|
||||
sa.Column('project_id', sa.Integer(), nullable=False),
|
||||
sa.Column('train_version', sa.String(length=32), nullable=False),
|
||||
sa.Column('train_url', sa.String(length=255), nullable=False),
|
||||
sa.Column('train_data', sa.String(length=255), nullable=False),
|
||||
sa.Column('weights_id', sa.Integer(), nullable=False),
|
||||
sa.Column('weights_name', sa.String(length=32), nullable=False),
|
||||
sa.Column('epochs', sa.Integer(), nullable=False),
|
||||
sa.Column('patience', sa.Integer(), nullable=False),
|
||||
sa.Column('best_pt', sa.String(length=255), nullable=False),
|
||||
sa.Column('last_pt', sa.String(length=255), nullable=False),
|
||||
sa.Column('user_id', sa.Integer(), nullable=False),
|
||||
sa.Column('id', sa.Integer(), nullable=False, comment='主键ID'),
|
||||
sa.Column('create_datetime', sa.DateTime(), server_default=sa.text('now()'), nullable=False, comment='创建时间'),
|
||||
sa.Column('update_datetime', sa.DateTime(), server_default=sa.text('now()'), nullable=False, comment='更新时间'),
|
||||
sa.Column('delete_datetime', sa.DateTime(), nullable=True, comment='删除时间'),
|
||||
sa.Column('is_delete', sa.Boolean(), nullable=False, comment='是否软删除'),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
comment='项目训练版本信息表'
|
||||
)
|
||||
op.create_table('project_type',
|
||||
sa.Column('type_code', sa.String(length=20), nullable=False),
|
||||
sa.Column('type_name', sa.String(length=20), nullable=False),
|
||||
sa.Column('icon_path', sa.String(length=255), nullable=False),
|
||||
sa.Column('description', sa.String(length=255), nullable=False),
|
||||
sa.Column('type_status', sa.String(length=10), nullable=False),
|
||||
sa.Column('id', sa.Integer(), nullable=False, comment='主键ID'),
|
||||
sa.Column('create_datetime', sa.DateTime(), server_default=sa.text('now()'), nullable=False, comment='创建时间'),
|
||||
sa.Column('update_datetime', sa.DateTime(), server_default=sa.text('now()'), nullable=False, comment='更新时间'),
|
||||
sa.Column('delete_datetime', sa.DateTime(), nullable=True, comment='删除时间'),
|
||||
sa.Column('is_delete', sa.Boolean(), nullable=False, comment='是否软删除'),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('type_code'),
|
||||
comment='项目类别表 - 标识项目的类型目前存在的(目标识别,OCR识别,瑕疵检测,图像分类)'
|
||||
)
|
||||
op.alter_column('vadmin_auth_dept', 'create_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('now()'),
|
||||
existing_comment='创建时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_auth_dept', 'update_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('now()'),
|
||||
existing_comment='更新时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_auth_menu', 'create_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('now()'),
|
||||
existing_comment='创建时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_auth_menu', 'update_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('now()'),
|
||||
existing_comment='更新时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_auth_role', 'create_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('now()'),
|
||||
existing_comment='创建时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_auth_role', 'update_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('now()'),
|
||||
existing_comment='更新时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_auth_user', 'create_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('now()'),
|
||||
existing_comment='创建时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_auth_user', 'update_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('now()'),
|
||||
existing_comment='更新时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_help_issue', 'create_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('now()'),
|
||||
existing_comment='创建时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_help_issue', 'update_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('now()'),
|
||||
existing_comment='更新时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_help_issue_category', 'create_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('now()'),
|
||||
existing_comment='创建时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_help_issue_category', 'update_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('now()'),
|
||||
existing_comment='更新时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_record_login', 'create_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('now()'),
|
||||
existing_comment='创建时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_record_login', 'update_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('now()'),
|
||||
existing_comment='更新时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_record_sms_send', 'create_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('now()'),
|
||||
existing_comment='创建时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_record_sms_send', 'update_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('now()'),
|
||||
existing_comment='更新时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_resource_images', 'create_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('now()'),
|
||||
existing_comment='创建时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_resource_images', 'update_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('now()'),
|
||||
existing_comment='更新时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_system_dict_details', 'create_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('now()'),
|
||||
existing_comment='创建时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_system_dict_details', 'update_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('now()'),
|
||||
existing_comment='更新时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_system_dict_type', 'create_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('now()'),
|
||||
existing_comment='创建时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_system_dict_type', 'update_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('now()'),
|
||||
existing_comment='更新时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_system_settings', 'create_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('now()'),
|
||||
existing_comment='创建时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_system_settings', 'update_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('now()'),
|
||||
existing_comment='更新时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_system_settings_tab', 'create_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('now()'),
|
||||
existing_comment='创建时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_system_settings_tab', 'update_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('now()'),
|
||||
existing_comment='更新时间',
|
||||
existing_nullable=False)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.alter_column('vadmin_system_settings_tab', 'update_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('CURRENT_TIMESTAMP'),
|
||||
existing_comment='更新时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_system_settings_tab', 'create_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('CURRENT_TIMESTAMP'),
|
||||
existing_comment='创建时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_system_settings', 'update_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('CURRENT_TIMESTAMP'),
|
||||
existing_comment='更新时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_system_settings', 'create_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('CURRENT_TIMESTAMP'),
|
||||
existing_comment='创建时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_system_dict_type', 'update_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('CURRENT_TIMESTAMP'),
|
||||
existing_comment='更新时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_system_dict_type', 'create_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('CURRENT_TIMESTAMP'),
|
||||
existing_comment='创建时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_system_dict_details', 'update_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('CURRENT_TIMESTAMP'),
|
||||
existing_comment='更新时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_system_dict_details', 'create_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('CURRENT_TIMESTAMP'),
|
||||
existing_comment='创建时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_resource_images', 'update_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('CURRENT_TIMESTAMP'),
|
||||
existing_comment='更新时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_resource_images', 'create_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('CURRENT_TIMESTAMP'),
|
||||
existing_comment='创建时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_record_sms_send', 'update_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('CURRENT_TIMESTAMP'),
|
||||
existing_comment='更新时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_record_sms_send', 'create_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('CURRENT_TIMESTAMP'),
|
||||
existing_comment='创建时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_record_login', 'update_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('CURRENT_TIMESTAMP'),
|
||||
existing_comment='更新时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_record_login', 'create_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('CURRENT_TIMESTAMP'),
|
||||
existing_comment='创建时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_help_issue_category', 'update_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('CURRENT_TIMESTAMP'),
|
||||
existing_comment='更新时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_help_issue_category', 'create_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('CURRENT_TIMESTAMP'),
|
||||
existing_comment='创建时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_help_issue', 'update_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('CURRENT_TIMESTAMP'),
|
||||
existing_comment='更新时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_help_issue', 'create_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('CURRENT_TIMESTAMP'),
|
||||
existing_comment='创建时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_auth_user', 'update_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('CURRENT_TIMESTAMP'),
|
||||
existing_comment='更新时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_auth_user', 'create_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('CURRENT_TIMESTAMP'),
|
||||
existing_comment='创建时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_auth_role', 'update_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('CURRENT_TIMESTAMP'),
|
||||
existing_comment='更新时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_auth_role', 'create_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('CURRENT_TIMESTAMP'),
|
||||
existing_comment='创建时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_auth_menu', 'update_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('CURRENT_TIMESTAMP'),
|
||||
existing_comment='更新时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_auth_menu', 'create_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('CURRENT_TIMESTAMP'),
|
||||
existing_comment='创建时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_auth_dept', 'update_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('CURRENT_TIMESTAMP'),
|
||||
existing_comment='更新时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_auth_dept', 'create_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('CURRENT_TIMESTAMP'),
|
||||
existing_comment='创建时间',
|
||||
existing_nullable=False)
|
||||
op.drop_table('project_type')
|
||||
op.drop_table('project_train')
|
||||
op.drop_table('project_label')
|
||||
op.drop_table('project_info')
|
||||
op.drop_table('project_img_leafer')
|
||||
op.drop_table('project_img_label')
|
||||
op.drop_table('project_image')
|
||||
op.drop_table('project_detect_log_img')
|
||||
op.drop_table('project_detect_log')
|
||||
op.drop_table('project_detect_img')
|
||||
op.drop_table('project_detect')
|
||||
# ### end Alembic commands ###
|
335
alembic/versions_dev/df17d7155cb6_3_10_1.py
Normal file
335
alembic/versions_dev/df17d7155cb6_3_10_1.py
Normal file
@ -0,0 +1,335 @@
|
||||
"""3.10.1
|
||||
|
||||
Revision ID: df17d7155cb6
|
||||
Revises:
|
||||
Create Date: 2025-04-03 09:36:50.773434
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'df17d7155cb6'
|
||||
down_revision = None
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('vadmin_auth_dept',
|
||||
sa.Column('name', sa.String(length=50), nullable=False, comment='部门名称'),
|
||||
sa.Column('dept_key', sa.String(length=50), nullable=False, comment='部门标识'),
|
||||
sa.Column('disabled', sa.Boolean(), nullable=False, comment='是否禁用'),
|
||||
sa.Column('order', sa.Integer(), nullable=True, comment='显示排序'),
|
||||
sa.Column('desc', sa.String(length=255), nullable=True, comment='描述'),
|
||||
sa.Column('owner', sa.String(length=255), nullable=True, comment='负责人'),
|
||||
sa.Column('phone', sa.String(length=255), nullable=True, comment='联系电话'),
|
||||
sa.Column('email', sa.String(length=255), nullable=True, comment='邮箱'),
|
||||
sa.Column('parent_id', sa.Integer(), nullable=True, comment='上级部门'),
|
||||
sa.Column('id', sa.Integer(), nullable=False, comment='主键ID'),
|
||||
sa.Column('create_datetime', sa.DateTime(), server_default=sa.text('now()'), nullable=False, comment='创建时间'),
|
||||
sa.Column('update_datetime', sa.DateTime(), server_default=sa.text('now()'), nullable=False, comment='更新时间'),
|
||||
sa.Column('delete_datetime', sa.DateTime(), nullable=True, comment='删除时间'),
|
||||
sa.Column('is_delete', sa.Boolean(), nullable=False, comment='是否软删除'),
|
||||
sa.ForeignKeyConstraint(['parent_id'], ['vadmin_auth_dept.id'], ondelete='CASCADE'),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
comment='部门表'
|
||||
)
|
||||
op.create_index(op.f('ix_vadmin_auth_dept_dept_key'), 'vadmin_auth_dept', ['dept_key'], unique=False)
|
||||
op.create_index(op.f('ix_vadmin_auth_dept_name'), 'vadmin_auth_dept', ['name'], unique=False)
|
||||
op.create_table('vadmin_auth_menu',
|
||||
sa.Column('title', sa.String(length=50), nullable=False, comment='名称'),
|
||||
sa.Column('icon', sa.String(length=50), nullable=True, comment='菜单图标'),
|
||||
sa.Column('redirect', sa.String(length=100), nullable=True, comment='重定向地址'),
|
||||
sa.Column('component', sa.String(length=255), nullable=True, comment='前端组件地址'),
|
||||
sa.Column('path', sa.String(length=50), nullable=True, comment='前端路由地址'),
|
||||
sa.Column('disabled', sa.Boolean(), nullable=False, comment='是否禁用'),
|
||||
sa.Column('hidden', sa.Boolean(), nullable=False, comment='是否隐藏'),
|
||||
sa.Column('order', sa.Integer(), nullable=False, comment='排序'),
|
||||
sa.Column('menu_type', sa.String(length=8), nullable=False, comment='菜单类型'),
|
||||
sa.Column('parent_id', sa.Integer(), nullable=True, comment='父菜单'),
|
||||
sa.Column('perms', sa.String(length=50), nullable=True, comment='权限标识'),
|
||||
sa.Column('noCache', sa.Boolean(), nullable=False, comment='如果设置为true,则不会被 <keep-alive> 缓存(默认 false)'),
|
||||
sa.Column('breadcrumb', sa.Boolean(), nullable=False, comment='如果设置为false,则不会在breadcrumb面包屑中显示(默认 true)'),
|
||||
sa.Column('affix', sa.Boolean(), nullable=False, comment='如果设置为true,则会一直固定在tag项中(默认 false)'),
|
||||
sa.Column('noTagsView', sa.Boolean(), nullable=False, comment='如果设置为true,则不会出现在tag中(默认 false)'),
|
||||
sa.Column('canTo', sa.Boolean(), nullable=False, comment='设置为true即使hidden为true,也依然可以进行路由跳转(默认 false)'),
|
||||
sa.Column('alwaysShow', sa.Boolean(), nullable=False, comment='当你一个路由下面的 children 声明的路由大于1个时,自动会变成嵌套的模式,\n 只有一个时,会将那个子路由当做根路由显示在侧边栏,若你想不管路由下面的 children 声明的个数都显示你的根路由,\n 你可以设置 alwaysShow: true,这样它就会忽略之前定义的规则,一直显示根路由(默认 true)'),
|
||||
sa.Column('id', sa.Integer(), nullable=False, comment='主键ID'),
|
||||
sa.Column('create_datetime', sa.DateTime(), server_default=sa.text('now()'), nullable=False, comment='创建时间'),
|
||||
sa.Column('update_datetime', sa.DateTime(), server_default=sa.text('now()'), nullable=False, comment='更新时间'),
|
||||
sa.Column('delete_datetime', sa.DateTime(), nullable=True, comment='删除时间'),
|
||||
sa.Column('is_delete', sa.Boolean(), nullable=False, comment='是否软删除'),
|
||||
sa.ForeignKeyConstraint(['parent_id'], ['vadmin_auth_menu.id'], ondelete='CASCADE'),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
comment='菜单表'
|
||||
)
|
||||
op.create_index(op.f('ix_vadmin_auth_menu_perms'), 'vadmin_auth_menu', ['perms'], unique=False)
|
||||
op.create_table('vadmin_auth_role',
|
||||
sa.Column('name', sa.String(length=50), nullable=False, comment='名称'),
|
||||
sa.Column('role_key', sa.String(length=50), nullable=False, comment='权限字符'),
|
||||
sa.Column('data_range', sa.Integer(), nullable=False, comment='数据权限范围'),
|
||||
sa.Column('disabled', sa.Boolean(), nullable=False, comment='是否禁用'),
|
||||
sa.Column('order', sa.Integer(), nullable=True, comment='排序'),
|
||||
sa.Column('desc', sa.String(length=255), nullable=True, comment='描述'),
|
||||
sa.Column('is_admin', sa.Boolean(), nullable=False, comment='是否为超级角色'),
|
||||
sa.Column('id', sa.Integer(), nullable=False, comment='主键ID'),
|
||||
sa.Column('create_datetime', sa.DateTime(), server_default=sa.text('now()'), nullable=False, comment='创建时间'),
|
||||
sa.Column('update_datetime', sa.DateTime(), server_default=sa.text('now()'), nullable=False, comment='更新时间'),
|
||||
sa.Column('delete_datetime', sa.DateTime(), nullable=True, comment='删除时间'),
|
||||
sa.Column('is_delete', sa.Boolean(), nullable=False, comment='是否软删除'),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
comment='角色表'
|
||||
)
|
||||
op.create_index(op.f('ix_vadmin_auth_role_name'), 'vadmin_auth_role', ['name'], unique=False)
|
||||
op.create_index(op.f('ix_vadmin_auth_role_role_key'), 'vadmin_auth_role', ['role_key'], unique=False)
|
||||
op.create_table('vadmin_auth_user',
|
||||
sa.Column('avatar', sa.String(length=500), nullable=True, comment='头像'),
|
||||
sa.Column('telephone', sa.String(length=11), nullable=False, comment='手机号'),
|
||||
sa.Column('email', sa.String(length=50), nullable=True, comment='邮箱地址'),
|
||||
sa.Column('name', sa.String(length=50), nullable=False, comment='姓名'),
|
||||
sa.Column('nickname', sa.String(length=50), nullable=True, comment='昵称'),
|
||||
sa.Column('password', sa.String(length=255), nullable=True, comment='密码'),
|
||||
sa.Column('gender', sa.String(length=8), nullable=True, comment='性别'),
|
||||
sa.Column('is_active', sa.Boolean(), nullable=False, comment='是否可用'),
|
||||
sa.Column('is_reset_password', sa.Boolean(), nullable=False, comment='是否已经重置密码,没有重置的,登陆系统后必须重置密码'),
|
||||
sa.Column('last_ip', sa.String(length=50), nullable=True, comment='最后一次登录IP'),
|
||||
sa.Column('last_login', sa.DateTime(), nullable=True, comment='最近一次登录时间'),
|
||||
sa.Column('is_staff', sa.Boolean(), nullable=False, comment='是否为工作人员'),
|
||||
sa.Column('wx_server_openid', sa.String(length=255), nullable=True, comment='服务端微信平台openid'),
|
||||
sa.Column('is_wx_server_openid', sa.Boolean(), nullable=False, comment='是否已有服务端微信平台openid'),
|
||||
sa.Column('id', sa.Integer(), nullable=False, comment='主键ID'),
|
||||
sa.Column('create_datetime', sa.DateTime(), server_default=sa.text('now()'), nullable=False, comment='创建时间'),
|
||||
sa.Column('update_datetime', sa.DateTime(), server_default=sa.text('now()'), nullable=False, comment='更新时间'),
|
||||
sa.Column('delete_datetime', sa.DateTime(), nullable=True, comment='删除时间'),
|
||||
sa.Column('is_delete', sa.Boolean(), nullable=False, comment='是否软删除'),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
comment='用户表'
|
||||
)
|
||||
op.create_index(op.f('ix_vadmin_auth_user_name'), 'vadmin_auth_user', ['name'], unique=False)
|
||||
op.create_index(op.f('ix_vadmin_auth_user_telephone'), 'vadmin_auth_user', ['telephone'], unique=False)
|
||||
op.create_table('vadmin_record_login',
|
||||
sa.Column('telephone', sa.String(length=255), nullable=False, comment='手机号'),
|
||||
sa.Column('status', sa.Boolean(), nullable=False, comment='是否登录成功'),
|
||||
sa.Column('platform', sa.String(length=8), nullable=False, comment='登陆平台'),
|
||||
sa.Column('login_method', sa.String(length=8), nullable=False, comment='认证方式'),
|
||||
sa.Column('ip', sa.String(length=50), nullable=True, comment='登陆地址'),
|
||||
sa.Column('address', sa.String(length=255), nullable=True, comment='登陆地点'),
|
||||
sa.Column('country', sa.String(length=255), nullable=True, comment='国家'),
|
||||
sa.Column('province', sa.String(length=255), nullable=True, comment='县'),
|
||||
sa.Column('city', sa.String(length=255), nullable=True, comment='城市'),
|
||||
sa.Column('county', sa.String(length=255), nullable=True, comment='区/县'),
|
||||
sa.Column('operator', sa.String(length=255), nullable=True, comment='运营商'),
|
||||
sa.Column('postal_code', sa.String(length=255), nullable=True, comment='邮政编码'),
|
||||
sa.Column('area_code', sa.String(length=255), nullable=True, comment='地区区号'),
|
||||
sa.Column('browser', sa.String(length=50), nullable=True, comment='浏览器'),
|
||||
sa.Column('system', sa.String(length=50), nullable=True, comment='操作系统'),
|
||||
sa.Column('response', sa.Text(), nullable=True, comment='响应信息'),
|
||||
sa.Column('request', sa.Text(), nullable=True, comment='请求信息'),
|
||||
sa.Column('id', sa.Integer(), nullable=False, comment='主键ID'),
|
||||
sa.Column('create_datetime', sa.DateTime(), server_default=sa.text('now()'), nullable=False, comment='创建时间'),
|
||||
sa.Column('update_datetime', sa.DateTime(), server_default=sa.text('now()'), nullable=False, comment='更新时间'),
|
||||
sa.Column('delete_datetime', sa.DateTime(), nullable=True, comment='删除时间'),
|
||||
sa.Column('is_delete', sa.Boolean(), nullable=False, comment='是否软删除'),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
comment='登录记录表'
|
||||
)
|
||||
op.create_index(op.f('ix_vadmin_record_login_telephone'), 'vadmin_record_login', ['telephone'], unique=False)
|
||||
op.create_table('vadmin_system_dict_type',
|
||||
sa.Column('dict_name', sa.String(length=50), nullable=False, comment='字典名称'),
|
||||
sa.Column('dict_type', sa.String(length=50), nullable=False, comment='字典类型'),
|
||||
sa.Column('disabled', sa.Boolean(), nullable=False, comment='字典状态,是否禁用'),
|
||||
sa.Column('remark', sa.String(length=255), nullable=True, comment='备注'),
|
||||
sa.Column('id', sa.Integer(), nullable=False, comment='主键ID'),
|
||||
sa.Column('create_datetime', sa.DateTime(), server_default=sa.text('now()'), nullable=False, comment='创建时间'),
|
||||
sa.Column('update_datetime', sa.DateTime(), server_default=sa.text('now()'), nullable=False, comment='更新时间'),
|
||||
sa.Column('delete_datetime', sa.DateTime(), nullable=True, comment='删除时间'),
|
||||
sa.Column('is_delete', sa.Boolean(), nullable=False, comment='是否软删除'),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
comment='字典类型表'
|
||||
)
|
||||
op.create_index(op.f('ix_vadmin_system_dict_type_dict_name'), 'vadmin_system_dict_type', ['dict_name'], unique=False)
|
||||
op.create_index(op.f('ix_vadmin_system_dict_type_dict_type'), 'vadmin_system_dict_type', ['dict_type'], unique=False)
|
||||
op.create_table('vadmin_system_settings_tab',
|
||||
sa.Column('title', sa.String(length=255), nullable=False, comment='标题'),
|
||||
sa.Column('classify', sa.String(length=255), nullable=False, comment='分类键'),
|
||||
sa.Column('tab_label', sa.String(length=255), nullable=False, comment='tab标题'),
|
||||
sa.Column('tab_name', sa.String(length=255), nullable=False, comment='tab标识符'),
|
||||
sa.Column('hidden', sa.Boolean(), nullable=False, comment='是否隐藏'),
|
||||
sa.Column('disabled', sa.Boolean(), nullable=False, comment='是否禁用'),
|
||||
sa.Column('id', sa.Integer(), nullable=False, comment='主键ID'),
|
||||
sa.Column('create_datetime', sa.DateTime(), server_default=sa.text('now()'), nullable=False, comment='创建时间'),
|
||||
sa.Column('update_datetime', sa.DateTime(), server_default=sa.text('now()'), nullable=False, comment='更新时间'),
|
||||
sa.Column('delete_datetime', sa.DateTime(), nullable=True, comment='删除时间'),
|
||||
sa.Column('is_delete', sa.Boolean(), nullable=False, comment='是否软删除'),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
comment='系统配置分类表'
|
||||
)
|
||||
op.create_index(op.f('ix_vadmin_system_settings_tab_classify'), 'vadmin_system_settings_tab', ['classify'], unique=False)
|
||||
op.create_index(op.f('ix_vadmin_system_settings_tab_tab_name'), 'vadmin_system_settings_tab', ['tab_name'], unique=True)
|
||||
op.create_table('vadmin_auth_role_depts',
|
||||
sa.Column('role_id', sa.Integer(), nullable=True),
|
||||
sa.Column('dept_id', sa.Integer(), nullable=True),
|
||||
sa.ForeignKeyConstraint(['dept_id'], ['vadmin_auth_dept.id'], ondelete='CASCADE'),
|
||||
sa.ForeignKeyConstraint(['role_id'], ['vadmin_auth_role.id'], ondelete='CASCADE')
|
||||
)
|
||||
op.create_table('vadmin_auth_role_menus',
|
||||
sa.Column('role_id', sa.Integer(), nullable=True),
|
||||
sa.Column('menu_id', sa.Integer(), nullable=True),
|
||||
sa.ForeignKeyConstraint(['menu_id'], ['vadmin_auth_menu.id'], ondelete='CASCADE'),
|
||||
sa.ForeignKeyConstraint(['role_id'], ['vadmin_auth_role.id'], ondelete='CASCADE')
|
||||
)
|
||||
op.create_table('vadmin_auth_user_depts',
|
||||
sa.Column('user_id', sa.Integer(), nullable=True),
|
||||
sa.Column('dept_id', sa.Integer(), nullable=True),
|
||||
sa.ForeignKeyConstraint(['dept_id'], ['vadmin_auth_dept.id'], ondelete='CASCADE'),
|
||||
sa.ForeignKeyConstraint(['user_id'], ['vadmin_auth_user.id'], ondelete='CASCADE')
|
||||
)
|
||||
op.create_table('vadmin_auth_user_roles',
|
||||
sa.Column('user_id', sa.Integer(), nullable=True),
|
||||
sa.Column('role_id', sa.Integer(), nullable=True),
|
||||
sa.ForeignKeyConstraint(['role_id'], ['vadmin_auth_role.id'], ondelete='CASCADE'),
|
||||
sa.ForeignKeyConstraint(['user_id'], ['vadmin_auth_user.id'], ondelete='CASCADE')
|
||||
)
|
||||
op.create_table('vadmin_help_issue_category',
|
||||
sa.Column('name', sa.String(length=50), nullable=False, comment='类别名称'),
|
||||
sa.Column('platform', sa.String(length=8), nullable=False, comment='展示平台'),
|
||||
sa.Column('is_active', sa.Boolean(), nullable=False, comment='是否可见'),
|
||||
sa.Column('create_user_id', sa.Integer(), nullable=False, comment='创建人'),
|
||||
sa.Column('id', sa.Integer(), nullable=False, comment='主键ID'),
|
||||
sa.Column('create_datetime', sa.DateTime(), server_default=sa.text('now()'), nullable=False, comment='创建时间'),
|
||||
sa.Column('update_datetime', sa.DateTime(), server_default=sa.text('now()'), nullable=False, comment='更新时间'),
|
||||
sa.Column('delete_datetime', sa.DateTime(), nullable=True, comment='删除时间'),
|
||||
sa.Column('is_delete', sa.Boolean(), nullable=False, comment='是否软删除'),
|
||||
sa.ForeignKeyConstraint(['create_user_id'], ['vadmin_auth_user.id'], ondelete='RESTRICT'),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
comment='常见问题类别表'
|
||||
)
|
||||
op.create_index(op.f('ix_vadmin_help_issue_category_name'), 'vadmin_help_issue_category', ['name'], unique=False)
|
||||
op.create_index(op.f('ix_vadmin_help_issue_category_platform'), 'vadmin_help_issue_category', ['platform'], unique=False)
|
||||
op.create_table('vadmin_record_sms_send',
|
||||
sa.Column('user_id', sa.Integer(), nullable=False, comment='操作人'),
|
||||
sa.Column('status', sa.Boolean(), nullable=False, comment='发送状态'),
|
||||
sa.Column('content', sa.String(length=255), nullable=False, comment='发送内容'),
|
||||
sa.Column('telephone', sa.String(length=11), nullable=False, comment='目标手机号'),
|
||||
sa.Column('desc', sa.String(length=255), nullable=True, comment='失败描述'),
|
||||
sa.Column('scene', sa.String(length=50), nullable=True, comment='发送场景'),
|
||||
sa.Column('id', sa.Integer(), nullable=False, comment='主键ID'),
|
||||
sa.Column('create_datetime', sa.DateTime(), server_default=sa.text('now()'), nullable=False, comment='创建时间'),
|
||||
sa.Column('update_datetime', sa.DateTime(), server_default=sa.text('now()'), nullable=False, comment='更新时间'),
|
||||
sa.Column('delete_datetime', sa.DateTime(), nullable=True, comment='删除时间'),
|
||||
sa.Column('is_delete', sa.Boolean(), nullable=False, comment='是否软删除'),
|
||||
sa.ForeignKeyConstraint(['user_id'], ['vadmin_auth_user.id'], ondelete='CASCADE'),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
comment='短信发送记录表'
|
||||
)
|
||||
op.create_table('vadmin_resource_images',
|
||||
sa.Column('filename', sa.String(length=255), nullable=False, comment='原图片名称'),
|
||||
sa.Column('image_url', sa.String(length=500), nullable=False, comment='图片链接'),
|
||||
sa.Column('create_user_id', sa.Integer(), nullable=False, comment='创建人'),
|
||||
sa.Column('id', sa.Integer(), nullable=False, comment='主键ID'),
|
||||
sa.Column('create_datetime', sa.DateTime(), server_default=sa.text('now()'), nullable=False, comment='创建时间'),
|
||||
sa.Column('update_datetime', sa.DateTime(), server_default=sa.text('now()'), nullable=False, comment='更新时间'),
|
||||
sa.Column('delete_datetime', sa.DateTime(), nullable=True, comment='删除时间'),
|
||||
sa.Column('is_delete', sa.Boolean(), nullable=False, comment='是否软删除'),
|
||||
sa.ForeignKeyConstraint(['create_user_id'], ['vadmin_auth_user.id'], ondelete='RESTRICT'),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
comment='图片素材表'
|
||||
)
|
||||
op.create_table('vadmin_system_dict_details',
|
||||
sa.Column('label', sa.String(length=50), nullable=False, comment='字典标签'),
|
||||
sa.Column('value', sa.String(length=50), nullable=False, comment='字典键值'),
|
||||
sa.Column('disabled', sa.Boolean(), nullable=False, comment='字典状态,是否禁用'),
|
||||
sa.Column('is_default', sa.Boolean(), nullable=False, comment='是否默认'),
|
||||
sa.Column('order', sa.Integer(), nullable=False, comment='字典排序'),
|
||||
sa.Column('dict_type_id', sa.Integer(), nullable=False, comment='关联字典类型'),
|
||||
sa.Column('remark', sa.String(length=255), nullable=True, comment='备注'),
|
||||
sa.Column('id', sa.Integer(), nullable=False, comment='主键ID'),
|
||||
sa.Column('create_datetime', sa.DateTime(), server_default=sa.text('now()'), nullable=False, comment='创建时间'),
|
||||
sa.Column('update_datetime', sa.DateTime(), server_default=sa.text('now()'), nullable=False, comment='更新时间'),
|
||||
sa.Column('delete_datetime', sa.DateTime(), nullable=True, comment='删除时间'),
|
||||
sa.Column('is_delete', sa.Boolean(), nullable=False, comment='是否软删除'),
|
||||
sa.ForeignKeyConstraint(['dict_type_id'], ['vadmin_system_dict_type.id'], ondelete='CASCADE'),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
comment='字典详情表'
|
||||
)
|
||||
op.create_index(op.f('ix_vadmin_system_dict_details_label'), 'vadmin_system_dict_details', ['label'], unique=False)
|
||||
op.create_index(op.f('ix_vadmin_system_dict_details_value'), 'vadmin_system_dict_details', ['value'], unique=False)
|
||||
op.create_table('vadmin_system_settings',
|
||||
sa.Column('config_label', sa.String(length=255), nullable=False, comment='配置表标签'),
|
||||
sa.Column('config_key', sa.String(length=255), nullable=False, comment='配置表键'),
|
||||
sa.Column('config_value', sa.Text(), nullable=True, comment='配置表内容'),
|
||||
sa.Column('remark', sa.String(length=255), nullable=True, comment='备注信息'),
|
||||
sa.Column('disabled', sa.Boolean(), nullable=False, comment='是否禁用'),
|
||||
sa.Column('tab_id', sa.Integer(), nullable=False, comment='关联tab标签'),
|
||||
sa.Column('id', sa.Integer(), nullable=False, comment='主键ID'),
|
||||
sa.Column('create_datetime', sa.DateTime(), server_default=sa.text('now()'), nullable=False, comment='创建时间'),
|
||||
sa.Column('update_datetime', sa.DateTime(), server_default=sa.text('now()'), nullable=False, comment='更新时间'),
|
||||
sa.Column('delete_datetime', sa.DateTime(), nullable=True, comment='删除时间'),
|
||||
sa.Column('is_delete', sa.Boolean(), nullable=False, comment='是否软删除'),
|
||||
sa.ForeignKeyConstraint(['tab_id'], ['vadmin_system_settings_tab.id'], ondelete='CASCADE'),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
comment='系统配置表'
|
||||
)
|
||||
op.create_index(op.f('ix_vadmin_system_settings_config_key'), 'vadmin_system_settings', ['config_key'], unique=True)
|
||||
op.create_table('vadmin_help_issue',
|
||||
sa.Column('category_id', sa.Integer(), nullable=False, comment='类别'),
|
||||
sa.Column('title', sa.String(length=255), nullable=False, comment='标题'),
|
||||
sa.Column('content', sa.Text(), nullable=False, comment='内容'),
|
||||
sa.Column('view_number', sa.Integer(), nullable=False, comment='查看次数'),
|
||||
sa.Column('is_active', sa.Boolean(), nullable=False, comment='是否可见'),
|
||||
sa.Column('create_user_id', sa.Integer(), nullable=False, comment='创建人'),
|
||||
sa.Column('id', sa.Integer(), nullable=False, comment='主键ID'),
|
||||
sa.Column('create_datetime', sa.DateTime(), server_default=sa.text('now()'), nullable=False, comment='创建时间'),
|
||||
sa.Column('update_datetime', sa.DateTime(), server_default=sa.text('now()'), nullable=False, comment='更新时间'),
|
||||
sa.Column('delete_datetime', sa.DateTime(), nullable=True, comment='删除时间'),
|
||||
sa.Column('is_delete', sa.Boolean(), nullable=False, comment='是否软删除'),
|
||||
sa.ForeignKeyConstraint(['category_id'], ['vadmin_help_issue_category.id'], ondelete='CASCADE'),
|
||||
sa.ForeignKeyConstraint(['create_user_id'], ['vadmin_auth_user.id'], ondelete='RESTRICT'),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
comment='常见问题记录表'
|
||||
)
|
||||
op.create_index(op.f('ix_vadmin_help_issue_title'), 'vadmin_help_issue', ['title'], unique=False)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_index(op.f('ix_vadmin_help_issue_title'), table_name='vadmin_help_issue')
|
||||
op.drop_table('vadmin_help_issue')
|
||||
op.drop_index(op.f('ix_vadmin_system_settings_config_key'), table_name='vadmin_system_settings')
|
||||
op.drop_table('vadmin_system_settings')
|
||||
op.drop_index(op.f('ix_vadmin_system_dict_details_value'), table_name='vadmin_system_dict_details')
|
||||
op.drop_index(op.f('ix_vadmin_system_dict_details_label'), table_name='vadmin_system_dict_details')
|
||||
op.drop_table('vadmin_system_dict_details')
|
||||
op.drop_table('vadmin_resource_images')
|
||||
op.drop_table('vadmin_record_sms_send')
|
||||
op.drop_index(op.f('ix_vadmin_help_issue_category_platform'), table_name='vadmin_help_issue_category')
|
||||
op.drop_index(op.f('ix_vadmin_help_issue_category_name'), table_name='vadmin_help_issue_category')
|
||||
op.drop_table('vadmin_help_issue_category')
|
||||
op.drop_table('vadmin_auth_user_roles')
|
||||
op.drop_table('vadmin_auth_user_depts')
|
||||
op.drop_table('vadmin_auth_role_menus')
|
||||
op.drop_table('vadmin_auth_role_depts')
|
||||
op.drop_index(op.f('ix_vadmin_system_settings_tab_tab_name'), table_name='vadmin_system_settings_tab')
|
||||
op.drop_index(op.f('ix_vadmin_system_settings_tab_classify'), table_name='vadmin_system_settings_tab')
|
||||
op.drop_table('vadmin_system_settings_tab')
|
||||
op.drop_index(op.f('ix_vadmin_system_dict_type_dict_type'), table_name='vadmin_system_dict_type')
|
||||
op.drop_index(op.f('ix_vadmin_system_dict_type_dict_name'), table_name='vadmin_system_dict_type')
|
||||
op.drop_table('vadmin_system_dict_type')
|
||||
op.drop_index(op.f('ix_vadmin_record_login_telephone'), table_name='vadmin_record_login')
|
||||
op.drop_table('vadmin_record_login')
|
||||
op.drop_index(op.f('ix_vadmin_auth_user_telephone'), table_name='vadmin_auth_user')
|
||||
op.drop_index(op.f('ix_vadmin_auth_user_name'), table_name='vadmin_auth_user')
|
||||
op.drop_table('vadmin_auth_user')
|
||||
op.drop_index(op.f('ix_vadmin_auth_role_role_key'), table_name='vadmin_auth_role')
|
||||
op.drop_index(op.f('ix_vadmin_auth_role_name'), table_name='vadmin_auth_role')
|
||||
op.drop_table('vadmin_auth_role')
|
||||
op.drop_index(op.f('ix_vadmin_auth_menu_perms'), table_name='vadmin_auth_menu')
|
||||
op.drop_table('vadmin_auth_menu')
|
||||
op.drop_index(op.f('ix_vadmin_auth_dept_name'), table_name='vadmin_auth_dept')
|
||||
op.drop_index(op.f('ix_vadmin_auth_dept_dept_key'), table_name='vadmin_auth_dept')
|
||||
op.drop_table('vadmin_auth_dept')
|
||||
# ### end Alembic commands ###
|
286
alembic/versions_dev/e2dec87a3d12_3_10_1.py
Normal file
286
alembic/versions_dev/e2dec87a3d12_3_10_1.py
Normal file
@ -0,0 +1,286 @@
|
||||
"""3.10.1
|
||||
|
||||
Revision ID: e2dec87a3d12
|
||||
Revises: df17d7155cb6
|
||||
Create Date: 2025-04-03 10:15:27.587731
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import mysql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'e2dec87a3d12'
|
||||
down_revision = 'df17d7155cb6'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.alter_column('vadmin_auth_dept', 'create_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('now()'),
|
||||
existing_comment='创建时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_auth_dept', 'update_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('now()'),
|
||||
existing_comment='更新时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_auth_menu', 'create_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('now()'),
|
||||
existing_comment='创建时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_auth_menu', 'update_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('now()'),
|
||||
existing_comment='更新时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_auth_role', 'create_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('now()'),
|
||||
existing_comment='创建时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_auth_role', 'update_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('now()'),
|
||||
existing_comment='更新时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_auth_user', 'create_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('now()'),
|
||||
existing_comment='创建时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_auth_user', 'update_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('now()'),
|
||||
existing_comment='更新时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_help_issue', 'create_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('now()'),
|
||||
existing_comment='创建时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_help_issue', 'update_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('now()'),
|
||||
existing_comment='更新时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_help_issue_category', 'create_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('now()'),
|
||||
existing_comment='创建时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_help_issue_category', 'update_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('now()'),
|
||||
existing_comment='更新时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_record_login', 'create_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('now()'),
|
||||
existing_comment='创建时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_record_login', 'update_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('now()'),
|
||||
existing_comment='更新时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_record_sms_send', 'create_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('now()'),
|
||||
existing_comment='创建时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_record_sms_send', 'update_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('now()'),
|
||||
existing_comment='更新时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_resource_images', 'create_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('now()'),
|
||||
existing_comment='创建时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_resource_images', 'update_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('now()'),
|
||||
existing_comment='更新时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_system_dict_details', 'create_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('now()'),
|
||||
existing_comment='创建时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_system_dict_details', 'update_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('now()'),
|
||||
existing_comment='更新时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_system_dict_type', 'create_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('now()'),
|
||||
existing_comment='创建时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_system_dict_type', 'update_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('now()'),
|
||||
existing_comment='更新时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_system_settings', 'create_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('now()'),
|
||||
existing_comment='创建时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_system_settings', 'update_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('now()'),
|
||||
existing_comment='更新时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_system_settings_tab', 'create_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('now()'),
|
||||
existing_comment='创建时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_system_settings_tab', 'update_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('now()'),
|
||||
existing_comment='更新时间',
|
||||
existing_nullable=False)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.alter_column('vadmin_system_settings_tab', 'update_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('CURRENT_TIMESTAMP'),
|
||||
existing_comment='更新时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_system_settings_tab', 'create_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('CURRENT_TIMESTAMP'),
|
||||
existing_comment='创建时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_system_settings', 'update_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('CURRENT_TIMESTAMP'),
|
||||
existing_comment='更新时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_system_settings', 'create_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('CURRENT_TIMESTAMP'),
|
||||
existing_comment='创建时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_system_dict_type', 'update_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('CURRENT_TIMESTAMP'),
|
||||
existing_comment='更新时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_system_dict_type', 'create_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('CURRENT_TIMESTAMP'),
|
||||
existing_comment='创建时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_system_dict_details', 'update_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('CURRENT_TIMESTAMP'),
|
||||
existing_comment='更新时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_system_dict_details', 'create_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('CURRENT_TIMESTAMP'),
|
||||
existing_comment='创建时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_resource_images', 'update_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('CURRENT_TIMESTAMP'),
|
||||
existing_comment='更新时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_resource_images', 'create_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('CURRENT_TIMESTAMP'),
|
||||
existing_comment='创建时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_record_sms_send', 'update_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('CURRENT_TIMESTAMP'),
|
||||
existing_comment='更新时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_record_sms_send', 'create_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('CURRENT_TIMESTAMP'),
|
||||
existing_comment='创建时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_record_login', 'update_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('CURRENT_TIMESTAMP'),
|
||||
existing_comment='更新时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_record_login', 'create_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('CURRENT_TIMESTAMP'),
|
||||
existing_comment='创建时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_help_issue_category', 'update_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('CURRENT_TIMESTAMP'),
|
||||
existing_comment='更新时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_help_issue_category', 'create_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('CURRENT_TIMESTAMP'),
|
||||
existing_comment='创建时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_help_issue', 'update_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('CURRENT_TIMESTAMP'),
|
||||
existing_comment='更新时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_help_issue', 'create_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('CURRENT_TIMESTAMP'),
|
||||
existing_comment='创建时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_auth_user', 'update_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('CURRENT_TIMESTAMP'),
|
||||
existing_comment='更新时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_auth_user', 'create_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('CURRENT_TIMESTAMP'),
|
||||
existing_comment='创建时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_auth_role', 'update_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('CURRENT_TIMESTAMP'),
|
||||
existing_comment='更新时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_auth_role', 'create_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('CURRENT_TIMESTAMP'),
|
||||
existing_comment='创建时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_auth_menu', 'update_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('CURRENT_TIMESTAMP'),
|
||||
existing_comment='更新时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_auth_menu', 'create_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('CURRENT_TIMESTAMP'),
|
||||
existing_comment='创建时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_auth_dept', 'update_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('CURRENT_TIMESTAMP'),
|
||||
existing_comment='更新时间',
|
||||
existing_nullable=False)
|
||||
op.alter_column('vadmin_auth_dept', 'create_datetime',
|
||||
existing_type=mysql.DATETIME(),
|
||||
server_default=sa.text('CURRENT_TIMESTAMP'),
|
||||
existing_comment='创建时间',
|
||||
existing_nullable=False)
|
||||
# ### end Alembic commands ###
|
0
alembic/versions_pro/.gitkeep
Normal file
0
alembic/versions_pro/.gitkeep
Normal file
0
application/__init__.py
Normal file
0
application/__init__.py
Normal file
0
application/config/__init__.py
Normal file
0
application/config/__init__.py
Normal file
63
application/config/development.py
Normal file
63
application/config/development.py
Normal file
@ -0,0 +1,63 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2021/10/19 15:47
|
||||
# @File : development.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : 数据库生产配置文件
|
||||
|
||||
|
||||
"""
|
||||
Mysql 数据库配置项
|
||||
连接引擎官方文档:https://www.osgeo.cn/sqlalchemy/core/engines.html
|
||||
数据库链接配置说明:mysql+asyncmy://数据库用户名:数据库密码@数据库地址:数据库端口/数据库名称
|
||||
"""
|
||||
SQLALCHEMY_DATABASE_URL = "mysql+asyncmy://root:root@127.0.0.1:3306/kinit"
|
||||
|
||||
|
||||
"""
|
||||
Redis 数据库配置
|
||||
格式:"redis://:密码@地址:端口/数据库名称"
|
||||
"""
|
||||
REDIS_DB_ENABLE = True
|
||||
REDIS_DB_URL = "redis://:sdust2020@127.0.0.1:6379/0"
|
||||
|
||||
"""
|
||||
MongoDB 数据库配置
|
||||
格式:mongodb://用户名:密码@地址:端口/?authSource=数据库名称
|
||||
"""
|
||||
MONGO_DB_ENABLE = False
|
||||
MONGO_DB_NAME = "kinit"
|
||||
MONGO_DB_URL = f"mongodb://kinit:123456@177.8.0.6:27017/?authSource={MONGO_DB_NAME}"
|
||||
|
||||
|
||||
"""
|
||||
阿里云对象存储OSS配置
|
||||
阿里云账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM用户进行API访问或日常运维,请登录RAM控制台创建RAM用户。
|
||||
yourEndpoint填写Bucket所在地域对应的Endpoint。以华东1(杭州)为例,Endpoint填写为https://oss-cn-hangzhou.aliyuncs.com。
|
||||
* [accessKeyId] {String}:通过阿里云控制台创建的AccessKey。
|
||||
* [accessKeySecret] {String}:通过阿里云控制台创建的AccessSecret。
|
||||
* [bucket] {String}:通过控制台或PutBucket创建的bucket。
|
||||
* [endpoint] {String}:bucket所在的区域, 默认oss-cn-hangzhou。
|
||||
"""
|
||||
ALIYUN_OSS = {
|
||||
"accessKeyId": "accessKeyId",
|
||||
"accessKeySecret": "accessKeySecret",
|
||||
"endpoint": "endpoint",
|
||||
"bucket": "bucket",
|
||||
"baseUrl": "baseUrl"
|
||||
}
|
||||
|
||||
"""
|
||||
获取IP地址归属地
|
||||
文档:https://user.ip138.com/ip/doc
|
||||
"""
|
||||
IP_PARSE_ENABLE = False
|
||||
IP_PARSE_TOKEN = "IP_PARSE_TOKEN"
|
||||
|
||||
|
||||
datasets_url = f'D:\syg\yolov5\datasets'
|
||||
runs_url = f'D:\syg\yolov5\runs'
|
||||
detect_url = f'D:\syg\yolov5\detect'
|
||||
yolo_url = f'D:\syg\workspace\aicheckv2\yolov5'
|
||||
|
||||
images_url = f'D:\syg\images'
|
61
application/config/production.py
Normal file
61
application/config/production.py
Normal file
@ -0,0 +1,61 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2021/10/19 15:47
|
||||
# @File : production.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : 数据库开发配置文件
|
||||
|
||||
|
||||
"""
|
||||
Mysql 数据库配置项
|
||||
连接引擎官方文档:https://www.osgeo.cn/sqlalchemy/core/engines.html
|
||||
数据库链接配置说明:mysql+asyncmy://数据库用户名:数据库密码@数据库地址:数据库端口/数据库名称
|
||||
"""
|
||||
SQLALCHEMY_DATABASE_URL = "mysql+asyncmy://root:123456@177.8.0.7:3306/kinit"
|
||||
|
||||
"""
|
||||
Redis 数据库配置
|
||||
格式:"redis://:密码@地址:端口/数据库名称"
|
||||
"""
|
||||
REDIS_DB_ENABLE = True
|
||||
REDIS_DB_URL = "redis://:123456@177.8.0.5:6379/1"
|
||||
|
||||
"""
|
||||
MongoDB 数据库配置
|
||||
格式:mongodb://用户名:密码@地址:端口/?authSource=数据库名称
|
||||
"""
|
||||
MONGO_DB_ENABLE = True
|
||||
MONGO_DB_NAME = "kinit"
|
||||
MONGO_DB_URL = f"mongodb://kinit:123456@177.8.0.6:27017/?authSource={MONGO_DB_NAME}"
|
||||
|
||||
|
||||
"""
|
||||
阿里云对象存储OSS配置
|
||||
阿里云账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM用户进行API访问或日常运维,请登录RAM控制台创建RAM用户。
|
||||
yourEndpoint填写Bucket所在地域对应的Endpoint。以华东1(杭州)为例,Endpoint填写为https://oss-cn-hangzhou.aliyuncs.com。
|
||||
* [accessKeyId] {String}:通过阿里云控制台创建的AccessKey。
|
||||
* [accessKeySecret] {String}:通过阿里云控制台创建的AccessSecret。
|
||||
* [bucket] {String}:通过控制台或PutBucket创建的bucket。
|
||||
* [endpoint] {String}:bucket所在的区域, 默认oss-cn-hangzhou。
|
||||
"""
|
||||
ALIYUN_OSS = {
|
||||
"accessKeyId": "accessKeyId",
|
||||
"accessKeySecret": "accessKeySecret",
|
||||
"endpoint": "endpoint",
|
||||
"bucket": "bucket",
|
||||
"baseUrl": "baseUrl"
|
||||
}
|
||||
|
||||
"""
|
||||
获取IP地址归属地
|
||||
文档:https://user.ip138.com/ip/doc
|
||||
"""
|
||||
IP_PARSE_ENABLE = False
|
||||
IP_PARSE_TOKEN = "IP_PARSE_TOKEN"
|
||||
|
||||
datasets_url = f'/home/aicheckv2/yolov5/datasets'
|
||||
runs_url = f'/home/aicheckv2/yolov5/runs'
|
||||
detect_url = f'/home/aicheckv2/yolov5/detect'
|
||||
yolo_url = f'/home/aicheckv2/backend/yolov5'
|
||||
|
||||
images_url = f'/home/aicheckv2/images'
|
150
application/settings.py
Normal file
150
application/settings.py
Normal file
@ -0,0 +1,150 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2021/10/19 15:47
|
||||
# @File : settings.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : 主配置文件
|
||||
|
||||
import os
|
||||
from fastapi.security import OAuth2PasswordBearer
|
||||
|
||||
"""
|
||||
系统版本
|
||||
"""
|
||||
VERSION = "3.10.1"
|
||||
|
||||
"""安全警告: 不要在生产中打开调试运行!"""
|
||||
DEBUG = True
|
||||
|
||||
"""是否开启演示功能:取消所有POST,DELETE,PUT操作权限"""
|
||||
DEMO = False
|
||||
"""演示功能白名单"""
|
||||
DEMO_WHITE_LIST_PATH = [
|
||||
"/auth/login",
|
||||
"/auth/token/refresh",
|
||||
"/auth/wx/login",
|
||||
"/vadmin/system/dict/types/details",
|
||||
"/vadmin/system/settings/tabs",
|
||||
"/vadmin/resource/images",
|
||||
"/vadmin/auth/user/export/query/list/to/excel"
|
||||
]
|
||||
"""演示功能黑名单(触发异常 status_code=403),黑名单优先级更高"""
|
||||
DEMO_BLACK_LIST_PATH = [
|
||||
"/auth/api/login"
|
||||
]
|
||||
|
||||
"""
|
||||
引入数据库配置
|
||||
"""
|
||||
if DEBUG:
|
||||
from application.config.development import *
|
||||
else:
|
||||
from application.config.production import *
|
||||
|
||||
"""项目根目录"""
|
||||
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
"""
|
||||
是否开启登录认证
|
||||
只适用于简单的接口
|
||||
如果是与认证关联性比较强的接口,则无法使用
|
||||
"""
|
||||
OAUTH_ENABLE = True
|
||||
"""
|
||||
配置 OAuth2 密码流认证方式
|
||||
官方文档:https://fastapi.tiangolo.com/zh/tutorial/security/first-steps/#fastapi-oauth2passwordbearer
|
||||
auto_error:(bool) 可选参数,默认为 True。当验证失败时,如果设置为 True,FastAPI 将自动返回一个 401 未授权的响应,如果设置为 False,你需要自己处理身份验证失败的情况。
|
||||
这里的 auto_error 设置为 False 是因为存在 OpenAuth:开放认证,无认证也可以访问,
|
||||
如果设置为 True,那么 FastAPI 会自动报错,即无认证时 OpenAuth 会失效,所以不能使用 True。
|
||||
"""
|
||||
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/auth/api/login", auto_error=False) if OAUTH_ENABLE else lambda: ""
|
||||
"""安全的随机密钥,该密钥将用于对 JWT 令牌进行签名"""
|
||||
SECRET_KEY = 'vgb0tnl9d58+6n-6h-ea&u^1#s0ccp!794=kbvqacjq75vzps$'
|
||||
"""用于设定 JWT 令牌签名算法"""
|
||||
ALGORITHM = "HS256"
|
||||
"""access_token 过期时间,一天"""
|
||||
ACCESS_TOKEN_EXPIRE_MINUTES = 1440
|
||||
"""refresh_token 过期时间,用于刷新token使用,两天"""
|
||||
REFRESH_TOKEN_EXPIRE_MINUTES = 1440 * 2
|
||||
"""access_token 缓存时间,用于刷新token使用,30分钟"""
|
||||
ACCESS_TOKEN_CACHE_MINUTES = 30
|
||||
|
||||
"""
|
||||
挂载临时文件目录,并添加路由访问,此路由不会在接口文档中显示
|
||||
TEMP_DIR:临时文件目录绝对路径
|
||||
官方文档:https://fastapi.tiangolo.com/tutorial/static-files/
|
||||
"""
|
||||
TEMP_DIR = os.path.join(BASE_DIR, "temp")
|
||||
|
||||
"""
|
||||
挂载静态目录,并添加路由访问,此路由不会在接口文档中显示
|
||||
STATIC_ENABLE:是否启用静态目录访问
|
||||
STATIC_URL:路由访问
|
||||
STATIC_ROOT:静态文件目录绝对路径
|
||||
官方文档:https://fastapi.tiangolo.com/tutorial/static-files/
|
||||
"""
|
||||
STATIC_ENABLE = True
|
||||
STATIC_URL = "/media"
|
||||
STATIC_DIR = "static"
|
||||
STATIC_ROOT = os.path.join(BASE_DIR, STATIC_DIR)
|
||||
|
||||
|
||||
"""
|
||||
跨域解决
|
||||
详细解释:https://cloud.tencent.com/developer/article/1886114
|
||||
官方文档:https://fastapi.tiangolo.com/tutorial/cors/
|
||||
"""
|
||||
# 是否启用跨域
|
||||
CORS_ORIGIN_ENABLE = True
|
||||
# 只允许访问的域名列表,* 代表所有
|
||||
ALLOW_ORIGINS = ["*"]
|
||||
# 是否支持携带 cookie
|
||||
ALLOW_CREDENTIALS = True
|
||||
# 设置允许跨域的http方法,比如 get、post、put等。
|
||||
ALLOW_METHODS = ["*"]
|
||||
# 允许携带的headers,可以用来鉴别来源等作用。
|
||||
ALLOW_HEADERS = ["*"]
|
||||
|
||||
"""
|
||||
全局事件配置
|
||||
"""
|
||||
EVENTS = [
|
||||
"core.event.connect_mongo" if MONGO_DB_ENABLE else None,
|
||||
"core.event.connect_redis" if REDIS_DB_ENABLE else None,
|
||||
]
|
||||
|
||||
"""
|
||||
其他项目配置
|
||||
"""
|
||||
# 默认密码,"0" 默认为手机号后六位
|
||||
DEFAULT_PASSWORD = "0"
|
||||
# 默认头像
|
||||
DEFAULT_AVATAR = "https://vv-reserve.oss-cn-hangzhou.aliyuncs.com/avatar/2023-01-27/1674820804e81e7631.png"
|
||||
# 默认登陆时最大输入密码或验证码错误次数
|
||||
DEFAULT_AUTH_ERROR_MAX_NUMBER = 5
|
||||
# 是否开启保存登录日志
|
||||
LOGIN_LOG_RECORD = True
|
||||
# 是否开启保存每次请求日志到本地
|
||||
REQUEST_LOG_RECORD = True
|
||||
# 是否开启每次操作日志记录到MongoDB数据库
|
||||
OPERATION_LOG_RECORD = True
|
||||
# 只记录包括的请求方式操作到MongoDB数据库
|
||||
OPERATION_RECORD_METHOD = ["POST", "PUT", "DELETE"]
|
||||
# 忽略的操作接口函数名称,列表中的函数名称不会被记录到操作日志中
|
||||
IGNORE_OPERATION_FUNCTION = ["post_dicts_details"]
|
||||
|
||||
"""
|
||||
中间件配置
|
||||
"""
|
||||
MIDDLEWARES = [
|
||||
"core.middleware.register_request_log_middleware" if REQUEST_LOG_RECORD else None,
|
||||
"core.middleware.register_operation_record_middleware" if OPERATION_LOG_RECORD and MONGO_DB_ENABLE else None,
|
||||
"core.middleware.register_demo_env_middleware" if DEMO else None,
|
||||
"core.middleware.register_jwt_refresh_middleware"
|
||||
]
|
||||
|
||||
"""
|
||||
定时任务配置
|
||||
"""
|
||||
# 发布/订阅通道,与定时任务程序相互关联,请勿随意更改
|
||||
SUBSCRIBE = 'kinit_queue'
|
30
application/urls.py
Normal file
30
application/urls.py
Normal file
@ -0,0 +1,30 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2021/10/19 15:47
|
||||
# @File : urls.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : 路由文件
|
||||
|
||||
from apps.vadmin.auth.utils.login import app as auth_app
|
||||
from apps.vadmin.auth.views import app as vadmin_auth_app
|
||||
from apps.vadmin.system.views import app as vadmin_system_app
|
||||
from apps.vadmin.record.views import app as vadmin_record_app
|
||||
from apps.vadmin.workplace.views import app as vadmin_workplace_app
|
||||
from apps.vadmin.analysis.views import app as vadmin_analysis_app
|
||||
from apps.vadmin.help.views import app as vadmin_help_app
|
||||
from apps.vadmin.resource.views import app as vadmin_resource_app
|
||||
from apps.business.project.views import app as project_app
|
||||
|
||||
|
||||
# 引入应用中的路由
|
||||
urlpatterns = [
|
||||
{"ApiRouter": auth_app, "prefix": "/auth", "tags": ["系统认证"]},
|
||||
{"ApiRouter": vadmin_auth_app, "prefix": "/vadmin/auth", "tags": ["权限管理"]},
|
||||
{"ApiRouter": vadmin_system_app, "prefix": "/vadmin/system", "tags": ["系统管理"]},
|
||||
{"ApiRouter": vadmin_record_app, "prefix": "/vadmin/record", "tags": ["记录管理"]},
|
||||
{"ApiRouter": vadmin_workplace_app, "prefix": "/vadmin/workplace", "tags": ["工作区管理"]},
|
||||
{"ApiRouter": vadmin_analysis_app, "prefix": "/vadmin/analysis", "tags": ["数据分析管理"]},
|
||||
{"ApiRouter": vadmin_help_app, "prefix": "/vadmin/help", "tags": ["帮助中心管理"]},
|
||||
{"ApiRouter": vadmin_resource_app, "prefix": "/vadmin/resource", "tags": ["资源管理"]},
|
||||
{"ApiRouter": project_app, "prefix": "/business/project", "tags": ["项目管理"]},
|
||||
]
|
7
apps/__init__.py
Normal file
7
apps/__init__.py
Normal file
@ -0,0 +1,7 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2022/2/24 10:19
|
||||
# @File : __init__.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : 简要说明
|
0
apps/business/__init__.py
Normal file
0
apps/business/__init__.py
Normal file
0
apps/business/detect/__init__.py
Normal file
0
apps/business/detect/__init__.py
Normal file
47
apps/business/detect/crud.py
Normal file
47
apps/business/detect/crud.py
Normal file
@ -0,0 +1,47 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2025/04/03 10:30
|
||||
# @File : crud.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : 数据访问层
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from core.crud import DalBase
|
||||
from . import schemas, models
|
||||
|
||||
|
||||
|
||||
class ProjectDetectDal(DalBase):
|
||||
|
||||
def __init__(self, db: AsyncSession):
|
||||
super(ProjectDetectDal, self).__init__()
|
||||
self.db = db
|
||||
self.model = models.ProjectDetect
|
||||
self.schema = schemas.ProjectDetectSimpleOut
|
||||
|
||||
|
||||
class ProjectDetectImgDal(DalBase):
|
||||
|
||||
def __init__(self, db: AsyncSession):
|
||||
super(ProjectDetectImgDal, self).__init__()
|
||||
self.db = db
|
||||
self.model = models.ProjectDetectImg
|
||||
self.schema = schemas.ProjectDetectImgSimpleOut
|
||||
|
||||
|
||||
class ProjectDetectLogDal(DalBase):
|
||||
|
||||
def __init__(self, db: AsyncSession):
|
||||
super(ProjectDetectLogDal, self).__init__()
|
||||
self.db = db
|
||||
self.model = models.ProjectDetectLog
|
||||
self.schema = schemas.ProjectDetectLogSimpleOut
|
||||
|
||||
|
||||
class ProjectDetectLogImgDal(DalBase):
|
||||
|
||||
def __init__(self, db: AsyncSession):
|
||||
super(ProjectDetectLogImgDal, self).__init__()
|
||||
self.db = db
|
||||
self.model = models.ProjectDetectLogImg
|
||||
self.schema = schemas.ProjectDetectLogImgSimpleOut
|
0
apps/business/detect/models/__init__.py
Normal file
0
apps/business/detect/models/__init__.py
Normal file
67
apps/business/detect/models/detect.py
Normal file
67
apps/business/detect/models/detect.py
Normal file
@ -0,0 +1,67 @@
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
from sqlalchemy import String, Integer
|
||||
|
||||
from db.db_base import BaseModel
|
||||
|
||||
|
||||
class ProjectDetect(BaseModel):
|
||||
"""
|
||||
项目推理集合
|
||||
"""
|
||||
__tablename__ = "project_detect"
|
||||
__table_args__ = ({'comment': '项目推理集合'})
|
||||
|
||||
project_id: Mapped[int] = mapped_column(Integer, nullable=False)
|
||||
detect_name: Mapped[str] = mapped_column(String(64), nullable=False)
|
||||
detect_version: Mapped[int] = mapped_column(Integer)
|
||||
detect_no: Mapped[str] = mapped_column(String(32))
|
||||
detect_status: Mapped[int] = mapped_column(Integer)
|
||||
file_type: Mapped[str] = mapped_column(String(10))
|
||||
folder_url: Mapped[str] = mapped_column(String(255))
|
||||
rtsp_url: Mapped[str] = mapped_column(String(255))
|
||||
user_id: Mapped[int] = mapped_column(Integer, nullable=False)
|
||||
|
||||
|
||||
class ProjectDetectImg(BaseModel):
|
||||
"""
|
||||
待推理图片
|
||||
"""
|
||||
__tablename__ = "project_detect_img"
|
||||
__table_args__ = ({'comment': '待推理图片'})
|
||||
|
||||
detect_id: Mapped[int] = mapped_column(Integer, nullable=False)
|
||||
file_name: Mapped[str] = mapped_column(String(64), nullable=False)
|
||||
image_url: Mapped[str] = mapped_column(String(255), nullable=False)
|
||||
thumb_image_url: Mapped[str] = mapped_column(String(255), nullable=False)
|
||||
user_id: Mapped[int] = mapped_column(Integer, nullable=False)
|
||||
|
||||
|
||||
class ProjectDetectLog(BaseModel):
|
||||
"""
|
||||
项目推理记录
|
||||
"""
|
||||
__tablename__ = "project_detect_log"
|
||||
__table_args__ = ({'comment': '项目推理记录'})
|
||||
|
||||
detect_id: Mapped[int] = mapped_column(Integer, nullable=False)
|
||||
detect_version: Mapped[str] = mapped_column(String(10))
|
||||
detect_name: Mapped[str] = mapped_column(String(64), nullable=False)
|
||||
train_id: Mapped[int] = mapped_column(Integer, nullable=False)
|
||||
train_version: Mapped[str] = mapped_column(String(10))
|
||||
pt_type: Mapped[str] = mapped_column(String(10))
|
||||
pt_url: Mapped[str] = mapped_column(String(255))
|
||||
folder_url: Mapped[str] = mapped_column(String(255))
|
||||
detect_folder_url: Mapped[str] = mapped_column(String(255))
|
||||
user_id: Mapped[int] = mapped_column(Integer, nullable=False)
|
||||
|
||||
|
||||
class ProjectDetectLogImg(BaseModel):
|
||||
"""
|
||||
推理完成的图片
|
||||
"""
|
||||
__tablename__ = "project_detect_log_img"
|
||||
__table_args__ = ({'comment': '项目训练版本信息表'})
|
||||
|
||||
log_id: Mapped[int] = mapped_column(Integer, nullable=False)
|
||||
file_name: Mapped[str] = mapped_column(String(64), nullable=False)
|
||||
image_url: Mapped[str] = mapped_column(String(255), nullable=False)
|
4
apps/business/detect/params/__init__.py
Normal file
4
apps/business/detect/params/__init__.py
Normal file
@ -0,0 +1,4 @@
|
||||
from .project_detect import ProjectDetectParams
|
||||
from .project_detect_img import ProjectDetectImgParams
|
||||
from .project_detect_log import ProjectDetectLogParams
|
||||
from .project_detect_log_img import ProjectDetectLogImgParams
|
15
apps/business/detect/params/project_detect.py
Normal file
15
apps/business/detect/params/project_detect.py
Normal file
@ -0,0 +1,15 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2025/04/03 10:30
|
||||
# @File : project_detect.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : 项目推理集合信息
|
||||
|
||||
from fastapi import Depends
|
||||
from core.dependencies import Paging, QueryParams
|
||||
|
||||
|
||||
class ProjectDetectParams(QueryParams):
|
||||
def __init__(self, params: Paging = Depends()):
|
||||
super().__init__(params)
|
15
apps/business/detect/params/project_detect_img.py
Normal file
15
apps/business/detect/params/project_detect_img.py
Normal file
@ -0,0 +1,15 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2025/04/03 10:30
|
||||
# @File : project_detect_img.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : 项目推理集合图片信息
|
||||
|
||||
from fastapi import Depends
|
||||
from core.dependencies import Paging, QueryParams
|
||||
|
||||
|
||||
class ProjectDetectImgParams(QueryParams):
|
||||
def __init__(self, params: Paging = Depends()):
|
||||
super().__init__(params)
|
15
apps/business/detect/params/project_detect_log.py
Normal file
15
apps/business/detect/params/project_detect_log.py
Normal file
@ -0,0 +1,15 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2025/04/03 10:31
|
||||
# @File : project_detect_log.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : 项目推理记录信息
|
||||
|
||||
from fastapi import Depends
|
||||
from core.dependencies import Paging, QueryParams
|
||||
|
||||
|
||||
class ProjectDetectLogParams(QueryParams):
|
||||
def __init__(self, params: Paging = Depends()):
|
||||
super().__init__(params)
|
15
apps/business/detect/params/project_detect_log_img.py
Normal file
15
apps/business/detect/params/project_detect_log_img.py
Normal file
@ -0,0 +1,15 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2025/04/03 10:31
|
||||
# @File : project_detect_log_img.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : 项目推理记录图片信息
|
||||
|
||||
from fastapi import Depends
|
||||
from core.dependencies import Paging, QueryParams
|
||||
|
||||
|
||||
class ProjectDetectLogImgParams(QueryParams):
|
||||
def __init__(self, params: Paging = Depends()):
|
||||
super().__init__(params)
|
4
apps/business/detect/schemas/__init__.py
Normal file
4
apps/business/detect/schemas/__init__.py
Normal file
@ -0,0 +1,4 @@
|
||||
from .project_detect import ProjectDetect, ProjectDetectSimpleOut
|
||||
from .project_detect_img import ProjectDetectImg, ProjectDetectImgSimpleOut
|
||||
from .project_detect_log import ProjectDetectLog, ProjectDetectLogSimpleOut
|
||||
from .project_detect_log_img import ProjectDetectLogImg, ProjectDetectLogImgSimpleOut
|
30
apps/business/detect/schemas/project_detect.py
Normal file
30
apps/business/detect/schemas/project_detect.py
Normal file
@ -0,0 +1,30 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2025/04/03 10:30
|
||||
# @File : project_detect.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : pydantic 模型,用于数据库序列化操作
|
||||
|
||||
from pydantic import BaseModel, Field, ConfigDict
|
||||
from core.data_types import DatetimeStr
|
||||
|
||||
|
||||
class ProjectDetect(BaseModel):
|
||||
project_id: int = Field(..., title="None")
|
||||
detect_name: str = Field(..., title="None")
|
||||
detect_version: int = Field(..., title="None")
|
||||
detect_no: str = Field(..., title="None")
|
||||
detect_status: int = Field(..., title="None")
|
||||
file_type: str = Field(..., title="None")
|
||||
folder_url: str = Field(..., title="None")
|
||||
rtsp_url: str = Field(..., title="None")
|
||||
user_id: int = Field(..., title="None")
|
||||
|
||||
|
||||
class ProjectDetectSimpleOut(ProjectDetect):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: int = Field(..., title="编号")
|
||||
create_datetime: DatetimeStr = Field(..., title="创建时间")
|
||||
update_datetime: DatetimeStr = Field(..., title="更新时间")
|
26
apps/business/detect/schemas/project_detect_img.py
Normal file
26
apps/business/detect/schemas/project_detect_img.py
Normal file
@ -0,0 +1,26 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2025/04/03 10:30
|
||||
# @File : project_detect_img.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : pydantic 模型,用于数据库序列化操作
|
||||
|
||||
from pydantic import BaseModel, Field, ConfigDict
|
||||
from core.data_types import DatetimeStr
|
||||
|
||||
|
||||
class ProjectDetectImg(BaseModel):
|
||||
detect_id: int = Field(..., title="None")
|
||||
file_name: str = Field(..., title="None")
|
||||
image_url: str = Field(..., title="None")
|
||||
thumb_image_url: str = Field(..., title="None")
|
||||
user_id: int = Field(..., title="None")
|
||||
|
||||
|
||||
class ProjectDetectImgSimpleOut(ProjectDetectImg):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: int = Field(..., title="编号")
|
||||
create_datetime: DatetimeStr = Field(..., title="创建时间")
|
||||
update_datetime: DatetimeStr = Field(..., title="更新时间")
|
31
apps/business/detect/schemas/project_detect_log.py
Normal file
31
apps/business/detect/schemas/project_detect_log.py
Normal file
@ -0,0 +1,31 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2025/04/03 10:31
|
||||
# @File : project_detect_log.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : pydantic 模型,用于数据库序列化操作
|
||||
|
||||
from pydantic import BaseModel, Field, ConfigDict
|
||||
from core.data_types import DatetimeStr
|
||||
|
||||
|
||||
class ProjectDetectLog(BaseModel):
|
||||
detect_id: int = Field(..., title="None")
|
||||
detect_version: str = Field(..., title="None")
|
||||
detect_name: str = Field(..., title="None")
|
||||
train_id: int = Field(..., title="None")
|
||||
train_version: str = Field(..., title="None")
|
||||
pt_type: str = Field(..., title="None")
|
||||
pt_url: str = Field(..., title="None")
|
||||
folder_url: str = Field(..., title="None")
|
||||
detect_folder_url: str = Field(..., title="None")
|
||||
user_id: int = Field(..., title="None")
|
||||
|
||||
|
||||
class ProjectDetectLogSimpleOut(ProjectDetectLog):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: int = Field(..., title="编号")
|
||||
create_datetime: DatetimeStr = Field(..., title="创建时间")
|
||||
update_datetime: DatetimeStr = Field(..., title="更新时间")
|
24
apps/business/detect/schemas/project_detect_log_img.py
Normal file
24
apps/business/detect/schemas/project_detect_log_img.py
Normal file
@ -0,0 +1,24 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2025/04/03 10:31
|
||||
# @File : project_detect_log_img.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : pydantic 模型,用于数据库序列化操作
|
||||
|
||||
from pydantic import BaseModel, Field, ConfigDict
|
||||
from core.data_types import DatetimeStr
|
||||
|
||||
|
||||
class ProjectDetectLogImg(BaseModel):
|
||||
log_id: int = Field(..., title="None")
|
||||
file_name: str = Field(..., title="None")
|
||||
image_url: str = Field(..., title="None")
|
||||
|
||||
|
||||
class ProjectDetectLogImgSimpleOut(ProjectDetectLogImg):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: int = Field(..., title="编号")
|
||||
create_datetime: DatetimeStr = Field(..., title="创建时间")
|
||||
update_datetime: DatetimeStr = Field(..., title="更新时间")
|
150
apps/business/detect/views.py
Normal file
150
apps/business/detect/views.py
Normal file
@ -0,0 +1,150 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2025/04/03 10:30
|
||||
# @File : views.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : 路由,视图文件
|
||||
from core.dependencies import IdList
|
||||
from apps.vadmin.auth.utils.validation.auth import Auth
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from apps.vadmin.auth.utils.current import AllUserAuth
|
||||
from core.database import db_getter
|
||||
from . import schemas, crud, models, params
|
||||
from fastapi import Depends, APIRouter
|
||||
from utils.response import SuccessResponse
|
||||
|
||||
|
||||
|
||||
app = APIRouter()
|
||||
|
||||
|
||||
###########################################################
|
||||
# 项目推理集合信息
|
||||
###########################################################
|
||||
@app.get("/project/detect", summary="获取项目推理集合信息列表", tags=["项目推理集合信息"])
|
||||
async def get_project_detect_list(p: params.ProjectDetectParams = Depends(), auth: Auth = Depends(AllUserAuth())):
|
||||
datas, count = await crud.ProjectDetectDal(auth.db).get_datas(**p.dict(), v_return_count=True)
|
||||
return SuccessResponse(datas, count=count)
|
||||
|
||||
|
||||
@app.post("/project/detect", summary="创建项目推理集合信息", tags=["项目推理集合信息"])
|
||||
async def create_project_detect(data: schemas.ProjectDetect, auth: Auth = Depends(AllUserAuth())):
|
||||
return SuccessResponse(await crud.ProjectDetectDal(auth.db).create_data(data=data))
|
||||
|
||||
|
||||
@app.delete("/project/detect", summary="删除项目推理集合信息", description="硬删除", tags=["项目推理集合信息"])
|
||||
async def delete_project_detect_list(ids: IdList = Depends(), auth: Auth = Depends(AllUserAuth())):
|
||||
await crud.ProjectDetectDal(auth.db).delete_datas(ids=ids.ids, v_soft=False)
|
||||
return SuccessResponse("删除成功")
|
||||
|
||||
|
||||
@app.put("/project/detect/{data_id}", summary="更新项目推理集合信息", tags=["项目推理集合信息"])
|
||||
async def put_project_detect(data_id: int, data: schemas.ProjectDetect, auth: Auth = Depends(AllUserAuth())):
|
||||
return SuccessResponse(await crud.ProjectDetectDal(auth.db).put_data(data_id, data))
|
||||
|
||||
|
||||
@app.get("/project/detect/{data_id}", summary="获取项目推理集合信息信息", tags=["项目推理集合信息"])
|
||||
async def get_project_detect(data_id: int, db: AsyncSession = Depends(db_getter)):
|
||||
schema = schemas.ProjectDetectSimpleOut
|
||||
return SuccessResponse(await crud.ProjectDetectDal(db).get_data(data_id, v_schema=schema))
|
||||
|
||||
|
||||
|
||||
|
||||
###########################################################
|
||||
# 项目推理集合图片信息
|
||||
###########################################################
|
||||
@app.get("/project/detect/img", summary="获取项目推理集合图片信息列表", tags=["项目推理集合图片信息"])
|
||||
async def get_project_detect_img_list(p: params.ProjectDetectImgParams = Depends(), auth: Auth = Depends(AllUserAuth())):
|
||||
datas, count = await crud.ProjectDetectImgDal(auth.db).get_datas(**p.dict(), v_return_count=True)
|
||||
return SuccessResponse(datas, count=count)
|
||||
|
||||
|
||||
@app.post("/project/detect/img", summary="创建项目推理集合图片信息", tags=["项目推理集合图片信息"])
|
||||
async def create_project_detect_img(data: schemas.ProjectDetectImg, auth: Auth = Depends(AllUserAuth())):
|
||||
return SuccessResponse(await crud.ProjectDetectImgDal(auth.db).create_data(data=data))
|
||||
|
||||
|
||||
@app.delete("/project/detect/img", summary="删除项目推理集合图片信息", description="硬删除", tags=["项目推理集合图片信息"])
|
||||
async def delete_project_detect_img_list(ids: IdList = Depends(), auth: Auth = Depends(AllUserAuth())):
|
||||
await crud.ProjectDetectImgDal(auth.db).delete_datas(ids=ids.ids, v_soft=False)
|
||||
return SuccessResponse("删除成功")
|
||||
|
||||
|
||||
@app.put("/project/detect/img/{data_id}", summary="更新项目推理集合图片信息", tags=["项目推理集合图片信息"])
|
||||
async def put_project_detect_img(data_id: int, data: schemas.ProjectDetectImg, auth: Auth = Depends(AllUserAuth())):
|
||||
return SuccessResponse(await crud.ProjectDetectImgDal(auth.db).put_data(data_id, data))
|
||||
|
||||
|
||||
@app.get("/project/detect/img/{data_id}", summary="获取项目推理集合图片信息信息", tags=["项目推理集合图片信息"])
|
||||
async def get_project_detect_img(data_id: int, db: AsyncSession = Depends(db_getter)):
|
||||
schema = schemas.ProjectDetectImgSimpleOut
|
||||
return SuccessResponse(await crud.ProjectDetectImgDal(db).get_data(data_id, v_schema=schema))
|
||||
|
||||
|
||||
|
||||
|
||||
###########################################################
|
||||
# 项目推理记录信息
|
||||
###########################################################
|
||||
@app.get("/project/detect/log", summary="获取项目推理记录信息列表", tags=["项目推理记录信息"])
|
||||
async def get_project_detect_log_list(p: params.ProjectDetectLogParams = Depends(), auth: Auth = Depends(AllUserAuth())):
|
||||
datas, count = await crud.ProjectDetectLogDal(auth.db).get_datas(**p.dict(), v_return_count=True)
|
||||
return SuccessResponse(datas, count=count)
|
||||
|
||||
|
||||
@app.post("/project/detect/log", summary="创建项目推理记录信息", tags=["项目推理记录信息"])
|
||||
async def create_project_detect_log(data: schemas.ProjectDetectLog, auth: Auth = Depends(AllUserAuth())):
|
||||
return SuccessResponse(await crud.ProjectDetectLogDal(auth.db).create_data(data=data))
|
||||
|
||||
|
||||
@app.delete("/project/detect/log", summary="删除项目推理记录信息", description="硬删除", tags=["项目推理记录信息"])
|
||||
async def delete_project_detect_log_list(ids: IdList = Depends(), auth: Auth = Depends(AllUserAuth())):
|
||||
await crud.ProjectDetectLogDal(auth.db).delete_datas(ids=ids.ids, v_soft=False)
|
||||
return SuccessResponse("删除成功")
|
||||
|
||||
|
||||
@app.put("/project/detect/log/{data_id}", summary="更新项目推理记录信息", tags=["项目推理记录信息"])
|
||||
async def put_project_detect_log(data_id: int, data: schemas.ProjectDetectLog, auth: Auth = Depends(AllUserAuth())):
|
||||
return SuccessResponse(await crud.ProjectDetectLogDal(auth.db).put_data(data_id, data))
|
||||
|
||||
|
||||
@app.get("/project/detect/log/{data_id}", summary="获取项目推理记录信息信息", tags=["项目推理记录信息"])
|
||||
async def get_project_detect_log(data_id: int, db: AsyncSession = Depends(db_getter)):
|
||||
schema = schemas.ProjectDetectLogSimpleOut
|
||||
return SuccessResponse(await crud.ProjectDetectLogDal(db).get_data(data_id, v_schema=schema))
|
||||
|
||||
|
||||
|
||||
|
||||
###########################################################
|
||||
# 项目推理记录图片信息
|
||||
###########################################################
|
||||
@app.get("/project/detect/log/img", summary="获取项目推理记录图片信息列表", tags=["项目推理记录图片信息"])
|
||||
async def get_project_detect_log_img_list(p: params.ProjectDetectLogImgParams = Depends(), auth: Auth = Depends(AllUserAuth())):
|
||||
datas, count = await crud.ProjectDetectLogImgDal(auth.db).get_datas(**p.dict(), v_return_count=True)
|
||||
return SuccessResponse(datas, count=count)
|
||||
|
||||
|
||||
@app.post("/project/detect/log/img", summary="创建项目推理记录图片信息", tags=["项目推理记录图片信息"])
|
||||
async def create_project_detect_log_img(data: schemas.ProjectDetectLogImg, auth: Auth = Depends(AllUserAuth())):
|
||||
return SuccessResponse(await crud.ProjectDetectLogImgDal(auth.db).create_data(data=data))
|
||||
|
||||
|
||||
@app.delete("/project/detect/log/img", summary="删除项目推理记录图片信息", description="硬删除", tags=["项目推理记录图片信息"])
|
||||
async def delete_project_detect_log_img_list(ids: IdList = Depends(), auth: Auth = Depends(AllUserAuth())):
|
||||
await crud.ProjectDetectLogImgDal(auth.db).delete_datas(ids=ids.ids, v_soft=False)
|
||||
return SuccessResponse("删除成功")
|
||||
|
||||
|
||||
@app.put("/project/detect/log/img/{data_id}", summary="更新项目推理记录图片信息", tags=["项目推理记录图片信息"])
|
||||
async def put_project_detect_log_img(data_id: int, data: schemas.ProjectDetectLogImg, auth: Auth = Depends(AllUserAuth())):
|
||||
return SuccessResponse(await crud.ProjectDetectLogImgDal(auth.db).put_data(data_id, data))
|
||||
|
||||
|
||||
@app.get("/project/detect/log/img/{data_id}", summary="获取项目推理记录图片信息信息", tags=["项目推理记录图片信息"])
|
||||
async def get_project_detect_log_img(data_id: int, db: AsyncSession = Depends(db_getter)):
|
||||
schema = schemas.ProjectDetectLogImgSimpleOut
|
||||
return SuccessResponse(await crud.ProjectDetectLogImgDal(db).get_data(data_id, v_schema=schema))
|
||||
|
0
apps/business/project/__init__.py
Normal file
0
apps/business/project/__init__.py
Normal file
156
apps/business/project/crud.py
Normal file
156
apps/business/project/crud.py
Normal file
@ -0,0 +1,156 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2025/04/03 10:25
|
||||
# @File : crud.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : 数据访问层
|
||||
import application.settings
|
||||
from . import schemas, models, params
|
||||
from apps.vadmin.auth.utils.validation.auth import Auth
|
||||
from utils import os_utils as os, random_utils as ru
|
||||
if application.settings.DEBUG:
|
||||
from application.config.development import datasets_url, runs_url, detect_url, yolo_url, images_url
|
||||
else:
|
||||
from application.config.production import datasets_url, runs_url, detect_url, yolo_url, images_url
|
||||
|
||||
from typing import Any, List
|
||||
from core.crud import DalBase
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy import select, func, case, and_
|
||||
|
||||
|
||||
class ProjectInfoDal(DalBase):
|
||||
|
||||
def __init__(self, db: AsyncSession):
|
||||
super(ProjectInfoDal, self).__init__()
|
||||
self.db = db
|
||||
self.model = models.ProjectInfo
|
||||
self.schema = schemas.ProjectInfoOut
|
||||
|
||||
async def get_project_pager(self, project: params.ProjectInfoParams, auth: Auth):
|
||||
"""
|
||||
分页查询项目列表
|
||||
"""
|
||||
# 定义子查询
|
||||
subquery = (
|
||||
select(
|
||||
models.ProjectImage.project_id,
|
||||
func.sum(case((models.ProjectImgLeafer.id.is_(None), 1), else_=0)).label('no_mark_count'),
|
||||
func.sum(case((models.ProjectImgLeafer.id.isnot(None), 1), else_=0)).label('mark_count')
|
||||
)
|
||||
.outerjoin(models.ProjectImgLeafer, models.ProjectImage.id == models.ProjectImgLeafer.image_id)
|
||||
.group_by(models.ProjectImage.project_id)
|
||||
.subquery()
|
||||
)
|
||||
|
||||
full_query = select(
|
||||
models.ProjectInfo,
|
||||
func.ifnull(subquery.c.mark_count, 0).label("mark_count"),
|
||||
func.ifnull(subquery.c.no_mark_count, 0).label("no_mark_count")
|
||||
).select_from(models.ProjectInfo).join(
|
||||
subquery, models.ProjectInfo.id == subquery.c.project_id, isouter=True
|
||||
)
|
||||
v_where = [models.ProjectInfo.is_delete.is_(False)]
|
||||
if '*' in auth.dept_ids:
|
||||
v_where.append(models.ProjectInfo.dept_id.isnot(None))
|
||||
else:
|
||||
v_where.append(models.ProjectInfo.dept_id.in_(auth.dept_ids))
|
||||
sql = await self.filter_core(
|
||||
v_start_sql=full_query,
|
||||
v_where=v_where,
|
||||
v_return_sql=True,
|
||||
v_order=project.v_order,
|
||||
v_order_field=project.v_order_field
|
||||
)
|
||||
count = await self.get_count_sql(sql)
|
||||
if project.limit != 0:
|
||||
sql = sql.offset((project.page - 1) * project.limit).limit(project.limit)
|
||||
queryset = await self.db.execute(sql)
|
||||
result = queryset.all()
|
||||
datas = []
|
||||
for result in result:
|
||||
data = schemas.ProjectInfoPagerOut.model_validate(result[0])
|
||||
data.mark_count = int(result[1])
|
||||
data.no_mark_count = int(result[2])
|
||||
datas.append(data.model_dump())
|
||||
return datas, count
|
||||
|
||||
async def check_name(self, project_name: str):
|
||||
"""
|
||||
校验项目名称是否重名
|
||||
"""
|
||||
count = await self.get_count(v_where=[models.ProjectInfo.project_name == project_name,
|
||||
models.ProjectInfo.is_delete is False])
|
||||
return count > 0
|
||||
|
||||
async def add_project(
|
||||
self,
|
||||
project: schemas.ProjectInfoIn,
|
||||
auth: Auth
|
||||
) -> Any:
|
||||
obj = self.model(**project.model_dump())
|
||||
obj.user_id = auth.user.id
|
||||
obj.project_no = ru.random_str(6)
|
||||
obj.project_status = "0"
|
||||
obj.train_version = 0
|
||||
obj.user_id = auth.user.id
|
||||
if '*' in auth.dept_ids:
|
||||
obj.dept_id = 0
|
||||
else:
|
||||
obj.dept_id = auth.dept_ids[0]
|
||||
os.create_folder(datasets_url, obj.project_no)
|
||||
os.create_folder(runs_url, obj.project_no)
|
||||
await self.flush(obj)
|
||||
return await self.out_dict(obj, None, False, schemas.ProjectInfoOut)
|
||||
|
||||
|
||||
class ProjectImageDal(DalBase):
|
||||
|
||||
def __init__(self, db: AsyncSession):
|
||||
super(ProjectImageDal, self).__init__()
|
||||
self.db = db
|
||||
self.model = models.ProjectImage
|
||||
self.schema = schemas.ProjectImageSimpleOut
|
||||
|
||||
|
||||
class ProjectLabelDal(DalBase):
|
||||
|
||||
def __init__(self, db: AsyncSession):
|
||||
super(ProjectLabelDal, self).__init__()
|
||||
self.db = db
|
||||
self.model = models.ProjectLabel
|
||||
self.schema = schemas.ProjectLabel
|
||||
|
||||
async def check_label_name(
|
||||
self,
|
||||
name: str,
|
||||
pro_id: int,
|
||||
label_id: int = None
|
||||
):
|
||||
wheres = [
|
||||
models.ProjectLabel.project_id == pro_id,
|
||||
models.ProjectLabel.label_name == name
|
||||
]
|
||||
if label_id:
|
||||
wheres.append(models.ProjectLabel.id != label_id)
|
||||
count = await self.get_count(v_where=wheres)
|
||||
return count > 0
|
||||
|
||||
|
||||
class ProjectImgLabelDal(DalBase):
|
||||
|
||||
def __init__(self, db: AsyncSession):
|
||||
super(ProjectImgLabelDal, self).__init__()
|
||||
self.db = db
|
||||
self.model = models.ProjectImgLabel
|
||||
self.schema = schemas.ProjectImgLabelSimpleOut
|
||||
|
||||
|
||||
class ProjectImgLeaferDal(DalBase):
|
||||
|
||||
def __init__(self, db: AsyncSession):
|
||||
super(ProjectImgLeaferDal, self).__init__()
|
||||
self.db = db
|
||||
self.model = models.ProjectImgLeafer
|
||||
self.schema = schemas.ProjectImgLeaferSimpleOut
|
1
apps/business/project/models/__init__.py
Normal file
1
apps/business/project/models/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
from .project import ProjectLabel, ProjectInfo, ProjectImgLabel, ProjectImgLeafer, ProjectImage
|
73
apps/business/project/models/project.py
Normal file
73
apps/business/project/models/project.py
Normal file
@ -0,0 +1,73 @@
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
from sqlalchemy import String, Integer, JSON
|
||||
|
||||
from db.db_base import BaseModel
|
||||
|
||||
|
||||
class ProjectInfo(BaseModel):
|
||||
"""
|
||||
项目信息表
|
||||
"""
|
||||
__tablename__ = "project_info"
|
||||
__table_args__ = ({'comment': '项目类别表 - 标识项目的类型目前存在的(目标识别,OCR识别,瑕疵检测,图像分类)'})
|
||||
|
||||
project_no: Mapped[str] = mapped_column(String(32), unique=True, nullable=False)
|
||||
project_name: Mapped[str] = mapped_column(String(32), unique=True, nullable=False)
|
||||
type_code: Mapped[str] = mapped_column(String(10))
|
||||
description: Mapped[str] = mapped_column(String(255))
|
||||
project_status: Mapped[str] = mapped_column(String(10))
|
||||
user_id: Mapped[int] = mapped_column(Integer)
|
||||
dept_id: Mapped[int] = mapped_column(Integer)
|
||||
train_version: Mapped[int] = mapped_column(Integer)
|
||||
|
||||
|
||||
class ProjectLabel(BaseModel):
|
||||
"""
|
||||
项目标签表
|
||||
"""
|
||||
__tablename__ = "project_label"
|
||||
__table_args__ = ({'comment': '项目标签表'})
|
||||
|
||||
label_name: Mapped[str] = mapped_column(String(32), unique=True, nullable=False)
|
||||
project_id: Mapped[int] = mapped_column(Integer, nullable=False)
|
||||
meta: Mapped[dict] = mapped_column(JSON)
|
||||
|
||||
|
||||
class ProjectImage(BaseModel):
|
||||
"""
|
||||
项目图片表
|
||||
"""
|
||||
__tablename__ = "project_image"
|
||||
__table_args__ = ({'comment': '项目图片表'})
|
||||
|
||||
img_type: Mapped[str] = mapped_column(String(10))
|
||||
file_name: Mapped[str] = mapped_column(String(64), nullable=False)
|
||||
image_url: Mapped[str] = mapped_column(String(255), nullable=False)
|
||||
thumb_image_url: Mapped[str] = mapped_column(String(255), nullable=False)
|
||||
project_id: Mapped[int] = mapped_column(Integer)
|
||||
|
||||
|
||||
class ProjectImgLeafer(BaseModel):
|
||||
"""
|
||||
项目图片leafer表
|
||||
"""
|
||||
__tablename__ = "project_img_leafer"
|
||||
__table_args__ = ({'comment': '项目图片leafer表'})
|
||||
|
||||
image_id: Mapped[int] = mapped_column(Integer, nullable=False)
|
||||
leafer: Mapped[dict] = mapped_column(JSON)
|
||||
|
||||
|
||||
class ProjectImgLabel(BaseModel):
|
||||
"""
|
||||
项目图片标签对应表,一张图片对应多个label
|
||||
"""
|
||||
__tablename__ = "project_img_label"
|
||||
__table_args__ = ({'comment': '项目图片标签对应表,一张图片对应多个label'})
|
||||
|
||||
image_id: Mapped[int] = mapped_column(Integer, nullable=False)
|
||||
label_id: Mapped[int] = mapped_column(Integer, nullable=False)
|
||||
mark_center_x: Mapped[str] = mapped_column(String(64), nullable=False)
|
||||
mark_center_y: Mapped[str] = mapped_column(String(64), nullable=False)
|
||||
mark_width: Mapped[str] = mapped_column(String(64), nullable=False)
|
||||
mark_height: Mapped[str] = mapped_column(String(64), nullable=False)
|
5
apps/business/project/params/__init__.py
Normal file
5
apps/business/project/params/__init__.py
Normal file
@ -0,0 +1,5 @@
|
||||
from .project_info import ProjectInfoParams
|
||||
from .project_image import ProjectImageParams
|
||||
from .project_label import ProjectLabelParams
|
||||
from .project_img_label import ProjectImgLabelParams
|
||||
from .project_img_leafer import ProjectImgLeaferParams
|
25
apps/business/project/params/project_image.py
Normal file
25
apps/business/project/params/project_image.py
Normal file
@ -0,0 +1,25 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2025/04/03 10:27
|
||||
# @File : project_image.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : 项目图片信息
|
||||
|
||||
from fastapi import Depends, Query
|
||||
from core.dependencies import Paging, QueryParams
|
||||
|
||||
|
||||
class ProjectImageParams(QueryParams):
|
||||
def __init__(
|
||||
self,
|
||||
img_type: str | None = Query(None, title="图片类别"),
|
||||
project_id: int | None = Query(None, title="项目id"),
|
||||
file_name: str | None = Query(None, title="文件名称"),
|
||||
params: Paging = Depends()
|
||||
):
|
||||
super().__init__(params)
|
||||
self.img_type = img_type
|
||||
self.project_id = project_id
|
||||
self.file_name = file_name
|
||||
|
20
apps/business/project/params/project_img_label.py
Normal file
20
apps/business/project/params/project_img_label.py
Normal file
@ -0,0 +1,20 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2025/04/03 10:29
|
||||
# @File : project_img_label.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : 项目图片标签信息
|
||||
|
||||
from fastapi import Depends, Query
|
||||
from core.dependencies import Paging, QueryParams
|
||||
|
||||
|
||||
class ProjectImgLabelParams(QueryParams):
|
||||
def __init__(
|
||||
self,
|
||||
image_id: int | None = Query(None, title="图片id"),
|
||||
params: Paging = Depends()
|
||||
):
|
||||
super().__init__(params)
|
||||
self.image_id = image_id
|
21
apps/business/project/params/project_img_leafer.py
Normal file
21
apps/business/project/params/project_img_leafer.py
Normal file
@ -0,0 +1,21 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2025/04/03 10:29
|
||||
# @File : project_img_leafer.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : 项目图片leafer信息
|
||||
|
||||
from fastapi import Depends, Query
|
||||
from core.dependencies import Paging, QueryParams
|
||||
|
||||
|
||||
class ProjectImgLeaferParams(QueryParams):
|
||||
def __init__(
|
||||
self,
|
||||
image_id: int | None = Query(None, title="图片id"),
|
||||
params: Paging = Depends()
|
||||
):
|
||||
super().__init__(params)
|
||||
self.image_id = image_id
|
||||
|
28
apps/business/project/params/project_info.py
Normal file
28
apps/business/project/params/project_info.py
Normal file
@ -0,0 +1,28 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2025/04/03 10:25
|
||||
# @File : project_info.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : 项目信息
|
||||
|
||||
from fastapi import Depends, Query
|
||||
from core.dependencies import Paging, QueryParams
|
||||
|
||||
|
||||
class ProjectInfoParams(QueryParams):
|
||||
def __init__(
|
||||
self,
|
||||
project_name: str | None = Query(None, title="项目名称"),
|
||||
type_code: str | None = Query(None, title="项目类别"),
|
||||
dept_id: str | None = Query(None, title="部门id"),
|
||||
user_id: str | None = Query(None, title="用户id"),
|
||||
params: Paging = Depends()
|
||||
):
|
||||
super().__init__(params)
|
||||
self.project_name = ("like", project_name)
|
||||
self.type_code = type_code
|
||||
self.dept_id = dept_id
|
||||
self.user_id = user_id
|
||||
|
||||
|
20
apps/business/project/params/project_label.py
Normal file
20
apps/business/project/params/project_label.py
Normal file
@ -0,0 +1,20 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2025/04/03 10:28
|
||||
# @File : project_label.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : 项目标签信息
|
||||
|
||||
from fastapi import Depends, Query
|
||||
from core.dependencies import Paging, QueryParams
|
||||
|
||||
|
||||
class ProjectLabelParams(QueryParams):
|
||||
def __init__(
|
||||
self,
|
||||
project_id: int | None = Query(None, title="项目id"),
|
||||
params: Paging = Depends()
|
||||
):
|
||||
super().__init__(params)
|
||||
self.project_id = project_id
|
5
apps/business/project/schemas/__init__.py
Normal file
5
apps/business/project/schemas/__init__.py
Normal file
@ -0,0 +1,5 @@
|
||||
from .project_info import ProjectInfoIn, ProjectInfoOut, ProjectInfoPagerOut
|
||||
from .project_image import ProjectImage, ProjectImagePager, ProjectImageOut
|
||||
from .project_label import ProjectLabel
|
||||
from .project_img_label import ProjectImgLabelIn
|
||||
from .project_img_leafer import ProjectImgLeaferLabel, ProjectImgLeaferOut
|
38
apps/business/project/schemas/project_image.py
Normal file
38
apps/business/project/schemas/project_image.py
Normal file
@ -0,0 +1,38 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2025/04/03 10:27
|
||||
# @File : project_image.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : pydantic 模型,用于数据库序列化操作
|
||||
|
||||
from pydantic import BaseModel, Field, ConfigDict
|
||||
from core.data_types import DatetimeStr
|
||||
from typing import Optional
|
||||
|
||||
|
||||
class ProjectImage(BaseModel):
|
||||
id: Optional[int] = Field(None, description="id")
|
||||
project_id: Optional[int] = Field(..., description="项目id")
|
||||
img_type: Optional[str] = Field(None, description="图片类别")
|
||||
file_name: Optional[str] = Field(None, description="文件名称")
|
||||
create_time: DatetimeStr
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
|
||||
class ProjectImageOut(BaseModel):
|
||||
id: Optional[int] = Field(None, description="id")
|
||||
project_id: Optional[int] = Field(..., description="项目id")
|
||||
file_name: Optional[str] = Field(None, description="文件名称")
|
||||
create_time: DatetimeStr
|
||||
label_count: Optional[int]
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
|
||||
class ProjectImagePager(BaseModel):
|
||||
project_id: Optional[int] = Field(..., description="项目id")
|
||||
img_type: Optional[str] = Field(None, description="图片类别")
|
||||
pagerNum: Optional[int] = Field(None, description="当前页码")
|
||||
pagerSize: Optional[int] = Field(None, description="每页数量")
|
18
apps/business/project/schemas/project_img_label.py
Normal file
18
apps/business/project/schemas/project_img_label.py
Normal file
@ -0,0 +1,18 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2025/04/03 10:29
|
||||
# @File : project_img_label.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : pydantic 模型,用于数据库序列化操作
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class ProjectImgLabelIn(BaseModel):
|
||||
label_id: int
|
||||
mark_center_x: str
|
||||
mark_center_y: str
|
||||
mark_width: str
|
||||
mark_height: str
|
||||
|
25
apps/business/project/schemas/project_img_leafer.py
Normal file
25
apps/business/project/schemas/project_img_leafer.py
Normal file
@ -0,0 +1,25 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2025/04/03 10:29
|
||||
# @File : project_img_leafer.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : pydantic 模型,用于数据库序列化操作
|
||||
|
||||
from pydantic import BaseModel, Field, ConfigDict
|
||||
from typing import Optional, List
|
||||
|
||||
from . import ProjectImgLabelIn
|
||||
|
||||
|
||||
class ProjectImgLeaferLabel(BaseModel):
|
||||
image_id: Optional[int] = Field(..., description="图片id")
|
||||
leafer: Optional[dict] = Field(..., description="保存的leafer")
|
||||
label_infos: List[ProjectImgLabelIn] = Field(..., description="标签框选信息")
|
||||
|
||||
|
||||
class ProjectImgLeaferOut(BaseModel):
|
||||
image_id: Optional[int] = Field(..., description="图片id")
|
||||
leafer: Optional[dict] = Field(..., description="保存的leafer")
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
47
apps/business/project/schemas/project_info.py
Normal file
47
apps/business/project/schemas/project_info.py
Normal file
@ -0,0 +1,47 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2025/04/03 10:25
|
||||
# @File : project_info.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : pydantic 模型,用于数据库序列化操作
|
||||
|
||||
from pydantic import BaseModel, Field, ConfigDict
|
||||
from typing import Optional
|
||||
|
||||
|
||||
class ProjectInfoIn(BaseModel):
|
||||
"""项目信息输入"""
|
||||
project_name: Optional[str] = Field(..., description="项目名称")
|
||||
type_code: Optional[str] = Field(..., description="项目类型编码")
|
||||
description: Optional[str] = Field(None, description="项目描述")
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
|
||||
class ProjectInfoOut(BaseModel):
|
||||
"""项目信息输出"""
|
||||
id: Optional[int] = Field(None, description="项目id")
|
||||
project_no: Optional[str] = Field(..., description="项目编号")
|
||||
project_name: Optional[str] = Field(..., description="项目名称")
|
||||
type_code: Optional[str] = Field(..., description="项目类型编码")
|
||||
description: Optional[str] = Field(None, description="项目描述")
|
||||
train_version: Optional[int] = Field(None, description="训练版本号")
|
||||
project_status: Optional[str] = Field(None, description="项目状态")
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
|
||||
class ProjectInfoPagerOut(BaseModel):
|
||||
"""项目信息输出"""
|
||||
id: Optional[int] = Field(None, description="项目id")
|
||||
project_no: Optional[str] = Field(None, description="项目编号")
|
||||
project_name: Optional[str] = Field(None, description="项目名称")
|
||||
type_code: Optional[str] = Field(None, description="项目类型编码")
|
||||
description: Optional[str] = Field(None, description="项目描述")
|
||||
train_version: Optional[int] = Field(None, description="训练版本号")
|
||||
project_status: Optional[str] = Field(None, description="项目状态")
|
||||
mark_count: Optional[int] = Field(0, description="已标记数量")
|
||||
no_mark_count: Optional[int] = Field(0, description="未标记数量")
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
20
apps/business/project/schemas/project_label.py
Normal file
20
apps/business/project/schemas/project_label.py
Normal file
@ -0,0 +1,20 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2025/04/03 10:28
|
||||
# @File : project_label.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : pydantic 模型,用于数据库序列化操作
|
||||
|
||||
from pydantic import BaseModel, Field, ConfigDict
|
||||
from typing import Optional
|
||||
|
||||
|
||||
class ProjectLabel(BaseModel):
|
||||
"""项目标签输入输出"""
|
||||
id: Optional[int] = Field(None, description="id")
|
||||
project_id: Optional[int] = Field(None, description="项目id")
|
||||
label_name: Optional[str] = Field(..., description="标签名称")
|
||||
meta: Optional[dict] = Field(None, description="label属性")
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
107
apps/business/project/views.py
Normal file
107
apps/business/project/views.py
Normal file
@ -0,0 +1,107 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2025/04/03 10:25
|
||||
# @File : views.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : 路由,视图文件
|
||||
from utils.response import SuccessResponse, ErrorResponse
|
||||
from . import params, schemas, crud, models
|
||||
from core.dependencies import IdList
|
||||
|
||||
from fastapi import APIRouter, Depends
|
||||
from apps.vadmin.auth.utils.current import FullAdminAuth
|
||||
from apps.vadmin.auth.utils.validation.auth import Auth
|
||||
|
||||
|
||||
app = APIRouter()
|
||||
|
||||
|
||||
###########################################################
|
||||
# 项目信息
|
||||
###########################################################
|
||||
@app.get("/list", summary="获取项目信息列表")
|
||||
async def project_pager(
|
||||
param: params.ProjectInfoParams = Depends(),
|
||||
auth: Auth = Depends(FullAdminAuth())
|
||||
):
|
||||
datas, count = await crud.ProjectInfoDal(auth.db).get_project_pager(project=param, auth=auth)
|
||||
return SuccessResponse(datas, count=count)
|
||||
|
||||
|
||||
@app.post("/info", summary="新建项目")
|
||||
async def add_project(
|
||||
pro_in: schemas.ProjectInfoIn,
|
||||
auth: Auth = Depends(FullAdminAuth())
|
||||
):
|
||||
check = await crud.ProjectInfoDal(auth.db).check_name(project_name=pro_in.project_name)
|
||||
if check:
|
||||
return ErrorResponse(msg="存在相同名称的项目,不能创建")
|
||||
result = await crud.ProjectInfoDal(auth.db).add_project(pro_in, auth)
|
||||
return SuccessResponse(data=result)
|
||||
|
||||
|
||||
@app.get("/info/{pro_id}", summary="查询项目信息")
|
||||
async def project(
|
||||
pro_id: int,
|
||||
auth: Auth = Depends(FullAdminAuth())
|
||||
):
|
||||
result = await crud.ProjectInfoDal(auth.db).get_data(data_id=pro_id, v_schema=schemas.ProjectInfoOut)
|
||||
return SuccessResponse(data=result)
|
||||
|
||||
|
||||
@app.delete("/info", summary="删除项目")
|
||||
async def del_project(
|
||||
pro_ids: IdList = Depends(),
|
||||
auth: Auth = Depends(FullAdminAuth())
|
||||
):
|
||||
await crud.ProjectInfoDal(auth.db).delete_datas(ids=pro_ids.ids, v_soft=True)
|
||||
return SuccessResponse(msg="删除成功")
|
||||
|
||||
|
||||
@app.get("/label/{pro_id}", summary="查询标签列表")
|
||||
async def label_list(
|
||||
pro_id: int,
|
||||
auth: Auth = Depends(FullAdminAuth())
|
||||
):
|
||||
result = await crud.ProjectLabelDal(auth.db).get_datas(v_where=[models.ProjectLabel.project_id == pro_id],
|
||||
v_schema=schemas.ProjectLabel)
|
||||
return SuccessResponse(data=result)
|
||||
|
||||
|
||||
@app.post("/label", summary="新建标签")
|
||||
async def add_label(
|
||||
label_in: schemas.ProjectLabel,
|
||||
auth: Auth = Depends(FullAdminAuth())
|
||||
):
|
||||
check = await crud.ProjectLabelDal(auth.db).check_label_name(label_in.label_name, label_in.project_id)
|
||||
if check:
|
||||
return ErrorResponse(msg="存在相同名称的标签,不能新建")
|
||||
await crud.ProjectLabelDal(auth.db).create_data(data=label_in)
|
||||
return SuccessResponse(msg="新建成功")
|
||||
|
||||
|
||||
@app.put("/label", summary="修改标签")
|
||||
async def update_label(
|
||||
label_in: schemas.ProjectLabel,
|
||||
auth: Auth = Depends(FullAdminAuth())
|
||||
):
|
||||
check = await crud.ProjectLabelDal(auth.db).check_label_name(label_in.label_name, label_in.project_id, label_in.id)
|
||||
if check:
|
||||
return ErrorResponse(msg="存在相同名称的标签,不能修改")
|
||||
await crud.ProjectLabelDal(auth.db).put_data(data_id=label_in.id, data=label_in)
|
||||
return SuccessResponse(msg="修改成功")
|
||||
|
||||
|
||||
@app.delete("/label", summary="删除标签")
|
||||
async def delete_label(
|
||||
label_ids: IdList = Depends(),
|
||||
auth: Auth = Depends(FullAdminAuth())
|
||||
):
|
||||
await crud.ProjectLabelDal(auth.db).delete_datas(label_ids.ids)
|
||||
return SuccessResponse(msg="删除成功")
|
||||
|
||||
|
||||
|
||||
|
||||
|
0
apps/business/train/__init__.py
Normal file
0
apps/business/train/__init__.py
Normal file
20
apps/business/train/crud.py
Normal file
20
apps/business/train/crud.py
Normal file
@ -0,0 +1,20 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2025/04/03 10:32
|
||||
# @File : crud.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : 数据访问层
|
||||
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from core.crud import DalBase
|
||||
from . import models, schemas
|
||||
|
||||
|
||||
class ProjectTrainDal(DalBase):
|
||||
|
||||
def __init__(self, db: AsyncSession):
|
||||
super(ProjectTrainDal, self).__init__()
|
||||
self.db = db
|
||||
self.model = models.ProjectTrain
|
||||
self.schema = schemas.ProjectTrainSimpleOut
|
0
apps/business/train/models/__init__.py
Normal file
0
apps/business/train/models/__init__.py
Normal file
24
apps/business/train/models/train.py
Normal file
24
apps/business/train/models/train.py
Normal file
@ -0,0 +1,24 @@
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
from sqlalchemy import String, Integer
|
||||
|
||||
from db.db_base import BaseModel
|
||||
|
||||
|
||||
class ProjectTrain(BaseModel):
|
||||
"""
|
||||
项目训练版本信息表
|
||||
"""
|
||||
__tablename__ = "project_train"
|
||||
__table_args__ = ({'comment': '项目训练版本信息表'})
|
||||
|
||||
project_id: Mapped[int] = mapped_column(Integer, nullable=False)
|
||||
train_version: Mapped[str] = mapped_column(String(32), nullable=False)
|
||||
train_url: Mapped[str] = mapped_column(String(255), nullable=False)
|
||||
train_data: Mapped[str] = mapped_column(String(255), nullable=False)
|
||||
weights_id: Mapped[int] = mapped_column(Integer)
|
||||
weights_name: Mapped[str] = mapped_column(String(32))
|
||||
epochs: Mapped[int] = mapped_column(Integer)
|
||||
patience: Mapped[int] = mapped_column(Integer)
|
||||
best_pt: Mapped[str] = mapped_column(String(255), nullable=False)
|
||||
last_pt: Mapped[str] = mapped_column(String(255), nullable=False)
|
||||
user_id: Mapped[int] = mapped_column(Integer, nullable=False)
|
1
apps/business/train/params/__init__.py
Normal file
1
apps/business/train/params/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
from .project_train import ProjectTrainParams
|
15
apps/business/train/params/project_train.py
Normal file
15
apps/business/train/params/project_train.py
Normal file
@ -0,0 +1,15 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2025/04/03 10:32
|
||||
# @File : project_train.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : 项目巡逻片信息
|
||||
|
||||
from fastapi import Depends
|
||||
from core.dependencies import Paging, QueryParams
|
||||
|
||||
|
||||
class ProjectTrainParams(QueryParams):
|
||||
def __init__(self, params: Paging = Depends()):
|
||||
super().__init__(params)
|
1
apps/business/train/schemas/__init__.py
Normal file
1
apps/business/train/schemas/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
from .project_train import ProjectTrain, ProjectTrainSimpleOut
|
32
apps/business/train/schemas/project_train.py
Normal file
32
apps/business/train/schemas/project_train.py
Normal file
@ -0,0 +1,32 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2025/04/03 10:32
|
||||
# @File : project_train.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : pydantic 模型,用于数据库序列化操作
|
||||
|
||||
from pydantic import BaseModel, Field, ConfigDict
|
||||
from core.data_types import DatetimeStr
|
||||
|
||||
|
||||
class ProjectTrain(BaseModel):
|
||||
project_id: int = Field(..., title="None")
|
||||
train_version: str = Field(..., title="None")
|
||||
train_url: str = Field(..., title="None")
|
||||
train_data: str = Field(..., title="None")
|
||||
weights_id: int = Field(..., title="None")
|
||||
weights_name: str = Field(..., title="None")
|
||||
epochs: int = Field(..., title="None")
|
||||
patience: int = Field(..., title="None")
|
||||
best_pt: str = Field(..., title="None")
|
||||
last_pt: str = Field(..., title="None")
|
||||
user_id: int = Field(..., title="None")
|
||||
|
||||
|
||||
class ProjectTrainSimpleOut(ProjectTrain):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: int = Field(..., title="编号")
|
||||
create_datetime: DatetimeStr = Field(..., title="创建时间")
|
||||
update_datetime: DatetimeStr = Field(..., title="更新时间")
|
51
apps/business/train/views.py
Normal file
51
apps/business/train/views.py
Normal file
@ -0,0 +1,51 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2025/04/03 10:32
|
||||
# @File : views.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : 路由,视图文件
|
||||
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from fastapi import APIRouter, Depends
|
||||
from . import models, schemas, crud, params
|
||||
from core.dependencies import IdList
|
||||
from apps.vadmin.auth.utils.current import AllUserAuth
|
||||
from utils.response import SuccessResponse
|
||||
from apps.vadmin.auth.utils.validation.auth import Auth
|
||||
from core.database import db_getter
|
||||
|
||||
|
||||
app = APIRouter()
|
||||
|
||||
|
||||
###########################################################
|
||||
# 项目巡逻片信息
|
||||
###########################################################
|
||||
@app.get("/project/train", summary="获取项目巡逻片信息列表", tags=["项目巡逻片信息"])
|
||||
async def get_project_train_list(p: params.ProjectTrainParams = Depends(), auth: Auth = Depends(AllUserAuth())):
|
||||
datas, count = await crud.ProjectTrainDal(auth.db).get_datas(**p.dict(), v_return_count=True)
|
||||
return SuccessResponse(datas, count=count)
|
||||
|
||||
|
||||
@app.post("/project/train", summary="创建项目巡逻片信息", tags=["项目巡逻片信息"])
|
||||
async def create_project_train(data: schemas.ProjectTrain, auth: Auth = Depends(AllUserAuth())):
|
||||
return SuccessResponse(await crud.ProjectTrainDal(auth.db).create_data(data=data))
|
||||
|
||||
|
||||
@app.delete("/project/train", summary="删除项目巡逻片信息", description="硬删除", tags=["项目巡逻片信息"])
|
||||
async def delete_project_train_list(ids: IdList = Depends(), auth: Auth = Depends(AllUserAuth())):
|
||||
await crud.ProjectTrainDal(auth.db).delete_datas(ids=ids.ids, v_soft=False)
|
||||
return SuccessResponse("删除成功")
|
||||
|
||||
|
||||
@app.put("/project/train/{data_id}", summary="更新项目巡逻片信息", tags=["项目巡逻片信息"])
|
||||
async def put_project_train(data_id: int, data: schemas.ProjectTrain, auth: Auth = Depends(AllUserAuth())):
|
||||
return SuccessResponse(await crud.ProjectTrainDal(auth.db).put_data(data_id, data))
|
||||
|
||||
|
||||
@app.get("/project/train/{data_id}", summary="获取项目巡逻片信息信息", tags=["项目巡逻片信息"])
|
||||
async def get_project_train(data_id: int, db: AsyncSession = Depends(db_getter)):
|
||||
schema = schemas.ProjectTrainSimpleOut
|
||||
return SuccessResponse(await crud.ProjectTrainDal(db).get_data(data_id, v_schema=schema))
|
||||
|
0
apps/vadmin/__init__.py
Normal file
0
apps/vadmin/__init__.py
Normal file
0
apps/vadmin/analysis/__init__.py
Normal file
0
apps/vadmin/analysis/__init__.py
Normal file
84
apps/vadmin/analysis/views.py
Normal file
84
apps/vadmin/analysis/views.py
Normal file
@ -0,0 +1,84 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2022/10/19 15:41
|
||||
# @File : views.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : 简要说明
|
||||
|
||||
from fastapi import APIRouter, Depends
|
||||
from apps.vadmin.auth.utils.current import AllUserAuth
|
||||
from utils.response import SuccessResponse
|
||||
from apps.vadmin.auth.utils.validation.auth import Auth
|
||||
import random
|
||||
|
||||
app = APIRouter()
|
||||
|
||||
|
||||
###########################################################
|
||||
# 图表数据
|
||||
###########################################################
|
||||
@app.get("/random/number", summary="获取随机整数")
|
||||
async def get_random_number(auth: Auth = Depends(AllUserAuth())):
|
||||
return SuccessResponse(random.randint(500, 20000))
|
||||
|
||||
|
||||
@app.get("/banners", summary="轮播图")
|
||||
async def get_banners(auth: Auth = Depends(AllUserAuth())):
|
||||
data = [
|
||||
{
|
||||
"id": 1, "image": "https://ktianc.oss-cn-beijing.aliyuncs.com/kinit/system/banner/2022-11-14/1.jpg"
|
||||
},
|
||||
{
|
||||
"id": 2, "image": "https://ktianc.oss-cn-beijing.aliyuncs.com/kinit/system/banner/2022-11-09/banner1.png"
|
||||
},
|
||||
{
|
||||
"id": 3, "image": "https://ktianc.oss-cn-beijing.aliyuncs.com/kinit/system/banner/2022-11-09/banner3.png"
|
||||
},
|
||||
]
|
||||
return SuccessResponse(data)
|
||||
|
||||
|
||||
@app.get("/user/access/source", summary="用户来源")
|
||||
async def get_user_access_source(auth: Auth = Depends(AllUserAuth())):
|
||||
data = [
|
||||
{"value": 1000, "name": 'analysis.directAccess'},
|
||||
{"value": 310, "name": 'analysis.mailMarketing'},
|
||||
{"value": 234, "name": 'analysis.allianceAdvertising'},
|
||||
{"value": 135, "name": 'analysis.videoAdvertising'},
|
||||
{"value": 1548, "name": 'analysis.searchEngines'}
|
||||
]
|
||||
return SuccessResponse(data)
|
||||
|
||||
|
||||
@app.get("/weekly/user/activity", summary="每周用户活跃量")
|
||||
async def get_weekly_user_activity(auth: Auth = Depends(AllUserAuth())):
|
||||
data = [
|
||||
{"value": 13253, "name": 'analysis.monday'},
|
||||
{"value": 34235, "name": 'analysis.tuesday'},
|
||||
{"value": 26321, "name": 'analysis.wednesday'},
|
||||
{"value": 12340, "name": 'analysis.thursday'},
|
||||
{"value": 24643, "name": 'analysis.friday'},
|
||||
{"value": 1322, "name": 'analysis.saturday'},
|
||||
{"value": 1324, "name": 'analysis.sunday'}
|
||||
]
|
||||
return SuccessResponse(data)
|
||||
|
||||
|
||||
@app.get("/monthly/sales", summary="每月销售额")
|
||||
async def get_monthly_sales(auth: Auth = Depends(AllUserAuth())):
|
||||
data = [
|
||||
{"estimate": 100, "actual": 120, "name": 'analysis.january'},
|
||||
{"estimate": 120, "actual": 82, "name": 'analysis.february'},
|
||||
{"estimate": 161, "actual": 91, "name": 'analysis.march'},
|
||||
{"estimate": 134, "actual": 154, "name": 'analysis.april'},
|
||||
{"estimate": 105, "actual": 162, "name": 'analysis.may'},
|
||||
{"estimate": 160, "actual": 140, "name": 'analysis.june'},
|
||||
{"estimate": 165, "actual": 145, "name": 'analysis.july'},
|
||||
{"estimate": 114, "actual": 250, "name": 'analysis.august'},
|
||||
{"estimate": 163, "actual": 134, "name": 'analysis.september'},
|
||||
{"estimate": 185, "actual": 56, "name": 'analysis.october'},
|
||||
{"estimate": 118, "actual": 99, "name": 'analysis.november'},
|
||||
{"estimate": 123, "actual": 123, "name": 'analysis.december'}
|
||||
]
|
||||
return SuccessResponse(data)
|
0
apps/vadmin/auth/__init__.py
Normal file
0
apps/vadmin/auth/__init__.py
Normal file
1075
apps/vadmin/auth/crud.py
Normal file
1075
apps/vadmin/auth/crud.py
Normal file
File diff suppressed because it is too large
Load Diff
14
apps/vadmin/auth/models/__init__.py
Normal file
14
apps/vadmin/auth/models/__init__.py
Normal file
@ -0,0 +1,14 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2022/7/7 13:41
|
||||
# @File : __init__.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : 简要说明
|
||||
|
||||
|
||||
from .m2m import vadmin_auth_user_roles, vadmin_auth_role_menus, vadmin_auth_user_depts, vadmin_auth_role_depts
|
||||
from .menu import VadminMenu
|
||||
from .role import VadminRole
|
||||
from .user import VadminUser
|
||||
from .dept import VadminDept
|
31
apps/vadmin/auth/models/dept.py
Normal file
31
apps/vadmin/auth/models/dept.py
Normal file
@ -0,0 +1,31 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2023/10/23 13:41
|
||||
# @File : dept.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : 部门模型
|
||||
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
from db.db_base import BaseModel
|
||||
from sqlalchemy import String, Boolean, Integer, ForeignKey
|
||||
|
||||
|
||||
class VadminDept(BaseModel):
|
||||
__tablename__ = "vadmin_auth_dept"
|
||||
__table_args__ = ({'comment': '部门表'})
|
||||
|
||||
name: Mapped[str] = mapped_column(String(50), index=True, nullable=False, comment="部门名称")
|
||||
dept_key: Mapped[str] = mapped_column(String(50), index=True, nullable=False, comment="部门标识")
|
||||
disabled: Mapped[bool] = mapped_column(Boolean, default=False, comment="是否禁用")
|
||||
order: Mapped[int | None] = mapped_column(Integer, comment="显示排序")
|
||||
desc: Mapped[str | None] = mapped_column(String(255), comment="描述")
|
||||
owner: Mapped[str | None] = mapped_column(String(255), comment="负责人")
|
||||
phone: Mapped[str | None] = mapped_column(String(255), comment="联系电话")
|
||||
email: Mapped[str | None] = mapped_column(String(255), comment="邮箱")
|
||||
|
||||
parent_id: Mapped[int | None] = mapped_column(
|
||||
Integer,
|
||||
ForeignKey("vadmin_auth_dept.id", ondelete='CASCADE'),
|
||||
comment="上级部门"
|
||||
)
|
41
apps/vadmin/auth/models/m2m.py
Normal file
41
apps/vadmin/auth/models/m2m.py
Normal file
@ -0,0 +1,41 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2022/7/7 13:41
|
||||
# @File : m2m.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : 关联中间表
|
||||
|
||||
from db.db_base import Base
|
||||
from sqlalchemy import ForeignKey, Column, Table, Integer
|
||||
|
||||
|
||||
vadmin_auth_user_roles = Table(
|
||||
"vadmin_auth_user_roles",
|
||||
Base.metadata,
|
||||
Column("user_id", Integer, ForeignKey("vadmin_auth_user.id", ondelete="CASCADE")),
|
||||
Column("role_id", Integer, ForeignKey("vadmin_auth_role.id", ondelete="CASCADE")),
|
||||
)
|
||||
|
||||
|
||||
vadmin_auth_role_menus = Table(
|
||||
"vadmin_auth_role_menus",
|
||||
Base.metadata,
|
||||
Column("role_id", Integer, ForeignKey("vadmin_auth_role.id", ondelete="CASCADE")),
|
||||
Column("menu_id", Integer, ForeignKey("vadmin_auth_menu.id", ondelete="CASCADE")),
|
||||
)
|
||||
|
||||
vadmin_auth_user_depts = Table(
|
||||
"vadmin_auth_user_depts",
|
||||
Base.metadata,
|
||||
Column("user_id", Integer, ForeignKey("vadmin_auth_user.id", ondelete="CASCADE")),
|
||||
Column("dept_id", Integer, ForeignKey("vadmin_auth_dept.id", ondelete="CASCADE")),
|
||||
)
|
||||
|
||||
vadmin_auth_role_depts = Table(
|
||||
"vadmin_auth_role_depts",
|
||||
Base.metadata,
|
||||
Column("role_id", Integer, ForeignKey("vadmin_auth_role.id", ondelete="CASCADE")),
|
||||
Column("dept_id", Integer, ForeignKey("vadmin_auth_dept.id", ondelete="CASCADE")),
|
||||
)
|
||||
|
67
apps/vadmin/auth/models/menu.py
Normal file
67
apps/vadmin/auth/models/menu.py
Normal file
@ -0,0 +1,67 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2022/7/7 13:41
|
||||
# @File : menu.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : 菜单模型
|
||||
|
||||
|
||||
from db.db_base import BaseModel
|
||||
from sqlalchemy import String, Boolean, Integer, ForeignKey
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
|
||||
class VadminMenu(BaseModel):
|
||||
__tablename__ = "vadmin_auth_menu"
|
||||
__table_args__ = ({'comment': '菜单表'})
|
||||
|
||||
title: Mapped[str] = mapped_column(String(50), comment="名称")
|
||||
icon: Mapped[str | None] = mapped_column(String(50), comment="菜单图标")
|
||||
redirect: Mapped[str | None] = mapped_column(String(100), comment="重定向地址")
|
||||
component: Mapped[str | None] = mapped_column(String(255), comment="前端组件地址")
|
||||
path: Mapped[str | None] = mapped_column(String(50), comment="前端路由地址")
|
||||
disabled: Mapped[bool] = mapped_column(Boolean, default=False, comment="是否禁用")
|
||||
hidden: Mapped[bool] = mapped_column(Boolean, default=False, comment="是否隐藏")
|
||||
order: Mapped[int] = mapped_column(Integer, comment="排序")
|
||||
menu_type: Mapped[str] = mapped_column(String(8), comment="菜单类型")
|
||||
parent_id: Mapped[int | None] = mapped_column(
|
||||
Integer,
|
||||
ForeignKey("vadmin_auth_menu.id", ondelete='CASCADE'),
|
||||
comment="父菜单"
|
||||
)
|
||||
perms: Mapped[str | None] = mapped_column(String(50), comment="权限标识", unique=False, index=True)
|
||||
|
||||
"""以下属性主要用于补全前端路由属性,"""
|
||||
noCache: Mapped[bool] = mapped_column(
|
||||
Boolean,
|
||||
comment="如果设置为true,则不会被 <keep-alive> 缓存(默认 false)",
|
||||
default=False
|
||||
)
|
||||
breadcrumb: Mapped[bool] = mapped_column(
|
||||
Boolean,
|
||||
comment="如果设置为false,则不会在breadcrumb面包屑中显示(默认 true)",
|
||||
default=True
|
||||
)
|
||||
affix: Mapped[bool] = mapped_column(
|
||||
Boolean,
|
||||
comment="如果设置为true,则会一直固定在tag项中(默认 false)",
|
||||
default=False
|
||||
)
|
||||
noTagsView: Mapped[bool] = mapped_column(
|
||||
Boolean,
|
||||
comment="如果设置为true,则不会出现在tag中(默认 false)",
|
||||
default=False
|
||||
)
|
||||
canTo: Mapped[bool] = mapped_column(
|
||||
Boolean,
|
||||
comment="设置为true即使hidden为true,也依然可以进行路由跳转(默认 false)",
|
||||
default=False
|
||||
)
|
||||
alwaysShow: Mapped[bool] = mapped_column(
|
||||
Boolean,
|
||||
comment="""当你一个路由下面的 children 声明的路由大于1个时,自动会变成嵌套的模式,
|
||||
只有一个时,会将那个子路由当做根路由显示在侧边栏,若你想不管路由下面的 children 声明的个数都显示你的根路由,
|
||||
你可以设置 alwaysShow: true,这样它就会忽略之前定义的规则,一直显示根路由(默认 true)""",
|
||||
default=True
|
||||
)
|
30
apps/vadmin/auth/models/role.py
Normal file
30
apps/vadmin/auth/models/role.py
Normal file
@ -0,0 +1,30 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2022/7/7 13:41
|
||||
# @File : role.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : 角色模型
|
||||
|
||||
from sqlalchemy.orm import relationship, Mapped, mapped_column
|
||||
from db.db_base import BaseModel
|
||||
from sqlalchemy import String, Boolean, Integer
|
||||
from .menu import VadminMenu
|
||||
from .dept import VadminDept
|
||||
from .m2m import vadmin_auth_role_menus, vadmin_auth_role_depts
|
||||
|
||||
|
||||
class VadminRole(BaseModel):
|
||||
__tablename__ = "vadmin_auth_role"
|
||||
__table_args__ = ({'comment': '角色表'})
|
||||
|
||||
name: Mapped[str] = mapped_column(String(50), index=True, comment="名称")
|
||||
role_key: Mapped[str] = mapped_column(String(50), index=True, comment="权限字符")
|
||||
data_range: Mapped[int] = mapped_column(Integer, default=4, comment="数据权限范围")
|
||||
disabled: Mapped[bool] = mapped_column(Boolean, default=False, comment="是否禁用")
|
||||
order: Mapped[int | None] = mapped_column(Integer, comment="排序")
|
||||
desc: Mapped[str | None] = mapped_column(String(255), comment="描述")
|
||||
is_admin: Mapped[bool] = mapped_column(Boolean, comment="是否为超级角色", default=False)
|
||||
|
||||
menus: Mapped[set[VadminMenu]] = relationship(secondary=vadmin_auth_role_menus)
|
||||
depts: Mapped[set[VadminDept]] = relationship(secondary=vadmin_auth_role_depts)
|
72
apps/vadmin/auth/models/user.py
Normal file
72
apps/vadmin/auth/models/user.py
Normal file
@ -0,0 +1,72 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2022/7/7 13:41
|
||||
# @File : user.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : 用户模型
|
||||
|
||||
from datetime import datetime
|
||||
from sqlalchemy.orm import relationship, Mapped, mapped_column
|
||||
from db.db_base import BaseModel
|
||||
from sqlalchemy import String, Boolean, DateTime
|
||||
from passlib.context import CryptContext
|
||||
from .role import VadminRole
|
||||
from .dept import VadminDept
|
||||
from .m2m import vadmin_auth_user_roles, vadmin_auth_user_depts
|
||||
|
||||
pwd_context = CryptContext(schemes=['bcrypt'], deprecated='auto')
|
||||
|
||||
|
||||
class VadminUser(BaseModel):
|
||||
__tablename__ = "vadmin_auth_user"
|
||||
__table_args__ = ({'comment': '用户表'})
|
||||
|
||||
avatar: Mapped[str | None] = mapped_column(String(500), comment='头像')
|
||||
telephone: Mapped[str] = mapped_column(String(11), nullable=False, index=True, comment="手机号", unique=False)
|
||||
email: Mapped[str | None] = mapped_column(String(50), comment="邮箱地址")
|
||||
name: Mapped[str] = mapped_column(String(50), index=True, nullable=False, comment="姓名")
|
||||
nickname: Mapped[str | None] = mapped_column(String(50), nullable=True, comment="昵称")
|
||||
password: Mapped[str] = mapped_column(String(255), nullable=True, comment="密码")
|
||||
gender: Mapped[str | None] = mapped_column(String(8), nullable=True, comment="性别")
|
||||
is_active: Mapped[bool] = mapped_column(Boolean, default=True, comment="是否可用")
|
||||
is_reset_password: Mapped[bool] = mapped_column(
|
||||
Boolean,
|
||||
default=False,
|
||||
comment="是否已经重置密码,没有重置的,登陆系统后必须重置密码"
|
||||
)
|
||||
last_ip: Mapped[str | None] = mapped_column(String(50), comment="最后一次登录IP")
|
||||
last_login: Mapped[datetime | None] = mapped_column(DateTime, comment="最近一次登录时间")
|
||||
is_staff: Mapped[bool] = mapped_column(Boolean, default=False, comment="是否为工作人员")
|
||||
wx_server_openid: Mapped[str | None] = mapped_column(String(255), comment="服务端微信平台openid")
|
||||
is_wx_server_openid: Mapped[bool] = mapped_column(Boolean, default=False, comment="是否已有服务端微信平台openid")
|
||||
|
||||
roles: Mapped[set[VadminRole]] = relationship(secondary=vadmin_auth_user_roles)
|
||||
depts: Mapped[set[VadminDept]] = relationship(secondary=vadmin_auth_user_depts)
|
||||
|
||||
@staticmethod
|
||||
def get_password_hash(password: str) -> str:
|
||||
"""
|
||||
生成哈希密码
|
||||
:param password: 原始密码
|
||||
:return: 哈希密码
|
||||
"""
|
||||
return pwd_context.hash(password)
|
||||
|
||||
@staticmethod
|
||||
def verify_password(password: str, hashed_password: str) -> bool:
|
||||
"""
|
||||
验证原始密码是否与哈希密码一致
|
||||
:param password: 原始密码
|
||||
:param hashed_password: 哈希密码
|
||||
:return:
|
||||
"""
|
||||
return pwd_context.verify(password, hashed_password)
|
||||
|
||||
def is_admin(self) -> bool:
|
||||
"""
|
||||
获取该用户是否拥有最高权限
|
||||
以最高权限为准
|
||||
:return:
|
||||
"""
|
||||
return any([i.is_admin for i in self.roles])
|
3
apps/vadmin/auth/params/__init__.py
Normal file
3
apps/vadmin/auth/params/__init__.py
Normal file
@ -0,0 +1,3 @@
|
||||
from .user import UserParams
|
||||
from .role import RoleParams
|
||||
from .dept import DeptParams
|
31
apps/vadmin/auth/params/dept.py
Normal file
31
apps/vadmin/auth/params/dept.py
Normal file
@ -0,0 +1,31 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2023/12/18 10:19
|
||||
# @File : dept.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : 查询参数-类依赖项
|
||||
|
||||
"""
|
||||
类依赖项-官方文档:https://fastapi.tiangolo.com/zh/tutorial/dependencies/classes-as-dependencies/
|
||||
"""
|
||||
from fastapi import Depends, Query
|
||||
from core.dependencies import Paging, QueryParams
|
||||
|
||||
|
||||
class DeptParams(QueryParams):
|
||||
"""
|
||||
列表分页
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
name: str | None = Query(None, title="部门名称"),
|
||||
dept_key: str | None = Query(None, title="部门标识"),
|
||||
disabled: bool | None = Query(None, title="是否禁用"),
|
||||
params: Paging = Depends()
|
||||
):
|
||||
super().__init__(params)
|
||||
self.name = ("like", name)
|
||||
self.dept_key = ("like", dept_key)
|
||||
self.disabled = disabled
|
31
apps/vadmin/auth/params/role.py
Normal file
31
apps/vadmin/auth/params/role.py
Normal file
@ -0,0 +1,31 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2021/10/18 22:19
|
||||
# @File : role.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : 查询参数-类依赖项
|
||||
|
||||
"""
|
||||
类依赖项-官方文档:https://fastapi.tiangolo.com/zh/tutorial/dependencies/classes-as-dependencies/
|
||||
"""
|
||||
from fastapi import Depends, Query
|
||||
from core.dependencies import Paging, QueryParams
|
||||
|
||||
|
||||
class RoleParams(QueryParams):
|
||||
"""
|
||||
列表分页
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
name: str | None = Query(None, title="角色名称"),
|
||||
role_key: str | None = Query(None, title="权限字符"),
|
||||
disabled: bool | None = Query(None, title="是否禁用"),
|
||||
params: Paging = Depends()
|
||||
):
|
||||
super().__init__(params)
|
||||
self.name = ("like", name)
|
||||
self.role_key = ("like", role_key)
|
||||
self.disabled = disabled
|
37
apps/vadmin/auth/params/user.py
Normal file
37
apps/vadmin/auth/params/user.py
Normal file
@ -0,0 +1,37 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2021/10/18 22:19
|
||||
# @File : user.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : 查询参数-类依赖项
|
||||
|
||||
"""
|
||||
类依赖项-官方文档:https://fastapi.tiangolo.com/zh/tutorial/dependencies/classes-as-dependencies/
|
||||
"""
|
||||
from fastapi import Depends, Query
|
||||
from core.dependencies import Paging, QueryParams
|
||||
|
||||
|
||||
class UserParams(QueryParams):
|
||||
"""
|
||||
列表分页
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
name: str | None = Query(None, title="用户名称"),
|
||||
telephone: str | None = Query(None, title="手机号"),
|
||||
email: str | None = Query(None, title="邮箱"),
|
||||
is_active: bool | None = Query(None, title="是否可用"),
|
||||
is_staff: bool | None = Query(None, title="是否为工作人员"),
|
||||
params: Paging = Depends()
|
||||
):
|
||||
super().__init__(params)
|
||||
self.name = ("like", name)
|
||||
self.telephone = ("like", telephone)
|
||||
self.email = ("like", email)
|
||||
self.is_active = is_active
|
||||
self.is_staff = is_staff
|
||||
|
||||
|
4
apps/vadmin/auth/schemas/__init__.py
Normal file
4
apps/vadmin/auth/schemas/__init__.py
Normal file
@ -0,0 +1,4 @@
|
||||
from .user import UserOut, UserUpdate, User, UserIn, UserSimpleOut, ResetPwd, UserUpdateBaseInfo, UserPasswordOut
|
||||
from .role import Role, RoleOut, RoleIn, RoleOptionsOut, RoleSimpleOut
|
||||
from .menu import Menu, MenuSimpleOut, RouterOut, Meta, MenuTreeListOut
|
||||
from .dept import Dept, DeptSimpleOut, DeptTreeListOut
|
39
apps/vadmin/auth/schemas/dept.py
Normal file
39
apps/vadmin/auth/schemas/dept.py
Normal file
@ -0,0 +1,39 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2023/10/25 12:19
|
||||
# @File : dept.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : pydantic 模型,用于数据库序列化操作
|
||||
|
||||
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
from core.data_types import DatetimeStr
|
||||
|
||||
|
||||
class Dept(BaseModel):
|
||||
name: str
|
||||
dept_key: str
|
||||
disabled: bool = False
|
||||
order: int | None = None
|
||||
desc: str | None = None
|
||||
owner: str | None = None
|
||||
phone: str | None = None
|
||||
email: str | None = None
|
||||
|
||||
parent_id: int | None = None
|
||||
|
||||
|
||||
class DeptSimpleOut(Dept):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: int
|
||||
create_datetime: DatetimeStr
|
||||
update_datetime: DatetimeStr
|
||||
|
||||
|
||||
class DeptTreeListOut(DeptSimpleOut):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
children: list[dict] = []
|
||||
|
66
apps/vadmin/auth/schemas/menu.py
Normal file
66
apps/vadmin/auth/schemas/menu.py
Normal file
@ -0,0 +1,66 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2021/10/18 22:19
|
||||
# @File : role.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : pydantic 模型,用于数据库序列化操作
|
||||
|
||||
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
from core.data_types import DatetimeStr
|
||||
|
||||
|
||||
class Menu(BaseModel):
|
||||
title: str
|
||||
icon: str | None = None
|
||||
component: str | None = None
|
||||
redirect: str | None = None
|
||||
path: str | None = None
|
||||
disabled: bool = False
|
||||
hidden: bool = False
|
||||
order: int | None = None
|
||||
perms: str | None = None
|
||||
parent_id: int | None = None
|
||||
menu_type: str
|
||||
alwaysShow: bool | None = True
|
||||
noCache: bool | None = False
|
||||
|
||||
|
||||
class MenuSimpleOut(Menu):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: int
|
||||
create_datetime: DatetimeStr
|
||||
update_datetime: DatetimeStr
|
||||
|
||||
|
||||
class Meta(BaseModel):
|
||||
title: str
|
||||
icon: str | None = None
|
||||
hidden: bool = False
|
||||
noCache: bool | None = False
|
||||
breadcrumb: bool | None = True
|
||||
affix: bool | None = False
|
||||
noTagsView: bool | None = False
|
||||
canTo: bool | None = False
|
||||
alwaysShow: bool | None = True
|
||||
|
||||
|
||||
# 路由展示
|
||||
class RouterOut(BaseModel):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
name: str | None = None
|
||||
component: str | None = None
|
||||
path: str
|
||||
redirect: str | None = None
|
||||
meta: Meta | None = None
|
||||
order: int | None = None
|
||||
children: list[dict] = []
|
||||
|
||||
|
||||
class MenuTreeListOut(MenuSimpleOut):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
children: list[dict] = []
|
52
apps/vadmin/auth/schemas/role.py
Normal file
52
apps/vadmin/auth/schemas/role.py
Normal file
@ -0,0 +1,52 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2021/10/18 22:19
|
||||
# @File : role.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : pydantic 模型,用于数据库序列化操作
|
||||
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
from core.data_types import DatetimeStr
|
||||
from .menu import MenuSimpleOut
|
||||
from .dept import DeptSimpleOut
|
||||
|
||||
|
||||
class Role(BaseModel):
|
||||
name: str
|
||||
disabled: bool = False
|
||||
order: int | None = None
|
||||
desc: str | None = None
|
||||
data_range: int = 4
|
||||
role_key: str
|
||||
is_admin: bool = False
|
||||
|
||||
|
||||
class RoleSimpleOut(Role):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: int
|
||||
create_datetime: DatetimeStr
|
||||
update_datetime: DatetimeStr
|
||||
|
||||
|
||||
class RoleOut(RoleSimpleOut):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
menus: list[MenuSimpleOut] = []
|
||||
depts: list[DeptSimpleOut] = []
|
||||
|
||||
|
||||
class RoleIn(Role):
|
||||
menu_ids: list[int] = []
|
||||
dept_ids: list[int] = []
|
||||
|
||||
|
||||
class RoleOptionsOut(BaseModel):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
label: str = Field(alias='name')
|
||||
value: int = Field(alias='id')
|
||||
disabled: bool
|
||||
|
98
apps/vadmin/auth/schemas/user.py
Normal file
98
apps/vadmin/auth/schemas/user.py
Normal file
@ -0,0 +1,98 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2021/10/18 22:19
|
||||
# @File : user.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : pydantic 模型,用于数据库序列化操作
|
||||
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, field_validator
|
||||
from pydantic_core.core_schema import FieldValidationInfo
|
||||
from core.data_types import Telephone, DatetimeStr, Email
|
||||
from .role import RoleSimpleOut
|
||||
from .dept import DeptSimpleOut
|
||||
|
||||
|
||||
class User(BaseModel):
|
||||
name: str
|
||||
telephone: Telephone
|
||||
email: Email | None = None
|
||||
nickname: str | None = None
|
||||
avatar: str | None = None
|
||||
is_active: bool | None = True
|
||||
is_staff: bool | None = True
|
||||
gender: str | None = "0"
|
||||
is_wx_server_openid: bool | None = False
|
||||
|
||||
|
||||
class UserIn(User):
|
||||
"""
|
||||
创建用户
|
||||
"""
|
||||
role_ids: list[int] = []
|
||||
dept_ids: list[int] = []
|
||||
password: str | None = ""
|
||||
|
||||
|
||||
class UserUpdateBaseInfo(BaseModel):
|
||||
"""
|
||||
更新用户基本信息
|
||||
"""
|
||||
name: str
|
||||
telephone: Telephone
|
||||
email: Email | None = None
|
||||
nickname: str | None = None
|
||||
gender: str | None = "0"
|
||||
|
||||
|
||||
class UserUpdate(User):
|
||||
"""
|
||||
更新用户详细信息
|
||||
"""
|
||||
name: str | None = None
|
||||
telephone: Telephone
|
||||
email: Email | None = None
|
||||
nickname: str | None = None
|
||||
avatar: str | None = None
|
||||
is_active: bool | None = True
|
||||
is_staff: bool | None = False
|
||||
gender: str | None = "0"
|
||||
role_ids: list[int] = []
|
||||
dept_ids: list[int] = []
|
||||
|
||||
|
||||
class UserSimpleOut(User):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: int
|
||||
update_datetime: DatetimeStr
|
||||
create_datetime: DatetimeStr
|
||||
|
||||
is_reset_password: bool | None = None
|
||||
last_login: DatetimeStr | None = None
|
||||
last_ip: str | None = None
|
||||
|
||||
|
||||
class UserPasswordOut(UserSimpleOut):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
password: str
|
||||
|
||||
|
||||
class UserOut(UserSimpleOut):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
roles: list[RoleSimpleOut] = []
|
||||
depts: list[DeptSimpleOut] = []
|
||||
|
||||
|
||||
class ResetPwd(BaseModel):
|
||||
password: str
|
||||
password_two: str
|
||||
|
||||
@field_validator('password_two')
|
||||
def check_passwords_match(cls, v, info: FieldValidationInfo):
|
||||
if 'password' in info.data and v != info.data['password']:
|
||||
raise ValueError('两次密码不一致!')
|
||||
return v
|
7
apps/vadmin/auth/utils/__init__.py
Normal file
7
apps/vadmin/auth/utils/__init__.py
Normal file
@ -0,0 +1,7 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2022/8/8 11:02
|
||||
# @File : __init__.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : 简要说明
|
114
apps/vadmin/auth/utils/current.py
Normal file
114
apps/vadmin/auth/utils/current.py
Normal file
@ -0,0 +1,114 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2021/10/24 16:44
|
||||
# @File : current.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : 获取认证后的信息工具
|
||||
|
||||
from typing import Annotated
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy.orm import joinedload
|
||||
from apps.vadmin.auth.crud import UserDal
|
||||
from apps.vadmin.auth.models import VadminUser, VadminRole
|
||||
from core.exception import CustomException
|
||||
from utils import status
|
||||
from .validation import AuthValidation
|
||||
from fastapi import Request, Depends
|
||||
from application import settings
|
||||
from core.database import db_getter
|
||||
from .validation.auth import Auth
|
||||
|
||||
|
||||
class OpenAuth(AuthValidation):
|
||||
|
||||
"""
|
||||
开放认证,无认证也可以访问
|
||||
认证了以后可以获取到用户信息,无认证则获取不到
|
||||
"""
|
||||
|
||||
async def __call__(
|
||||
self,
|
||||
request: Request,
|
||||
token: Annotated[str, Depends(settings.oauth2_scheme)],
|
||||
db: AsyncSession = Depends(db_getter)
|
||||
):
|
||||
"""
|
||||
每次调用依赖此类的接口会执行该方法
|
||||
"""
|
||||
if not settings.OAUTH_ENABLE:
|
||||
return Auth(db=db)
|
||||
try:
|
||||
telephone, password = self.validate_token(request, token)
|
||||
user = await UserDal(db).get_data(telephone=telephone, password=password, v_return_none=True)
|
||||
return await self.validate_user(request, user, db, is_all=True)
|
||||
except CustomException:
|
||||
return Auth(db=db)
|
||||
|
||||
|
||||
class AllUserAuth(AuthValidation):
|
||||
|
||||
"""
|
||||
支持所有用户认证
|
||||
获取用户基本信息
|
||||
"""
|
||||
|
||||
async def __call__(
|
||||
self,
|
||||
request: Request,
|
||||
token: str = Depends(settings.oauth2_scheme),
|
||||
db: AsyncSession = Depends(db_getter)
|
||||
):
|
||||
"""
|
||||
每次调用依赖此类的接口会执行该方法
|
||||
"""
|
||||
if not settings.OAUTH_ENABLE:
|
||||
return Auth(db=db)
|
||||
telephone, password = self.validate_token(request, token)
|
||||
user = await UserDal(db).get_data(telephone=telephone, password=password, v_return_none=True)
|
||||
return await self.validate_user(request, user, db, is_all=True)
|
||||
|
||||
|
||||
class FullAdminAuth(AuthValidation):
|
||||
|
||||
"""
|
||||
只支持员工用户认证
|
||||
获取员工用户完整信息
|
||||
如果有权限,那么会验证该用户是否包括权限列表中的其中一个权限
|
||||
"""
|
||||
|
||||
def __init__(self, permissions: list[str] | None = None):
|
||||
if permissions:
|
||||
self.permissions = set(permissions)
|
||||
else:
|
||||
self.permissions = None
|
||||
|
||||
async def __call__(
|
||||
self,
|
||||
request: Request,
|
||||
token: str = Depends(settings.oauth2_scheme),
|
||||
db: AsyncSession = Depends(db_getter)
|
||||
) -> Auth:
|
||||
"""
|
||||
每次调用依赖此类的接口会执行该方法
|
||||
"""
|
||||
if not settings.OAUTH_ENABLE:
|
||||
return Auth(db=db)
|
||||
telephone, password = self.validate_token(request, token)
|
||||
options = [
|
||||
joinedload(VadminUser.roles).subqueryload(VadminRole.menus),
|
||||
joinedload(VadminUser.roles).subqueryload(VadminRole.depts),
|
||||
joinedload(VadminUser.depts)
|
||||
]
|
||||
user = await UserDal(db).get_data(
|
||||
telephone=telephone,
|
||||
password=password,
|
||||
v_return_none=True,
|
||||
v_options=options,
|
||||
is_staff=True
|
||||
)
|
||||
result = await self.validate_user(request, user, db, is_all=False)
|
||||
permissions = self.get_user_permissions(user)
|
||||
if permissions != {'*.*.*'} and self.permissions:
|
||||
if not (self.permissions & permissions):
|
||||
raise CustomException(msg="无权限操作", code=status.HTTP_403_FORBIDDEN)
|
||||
return result
|
180
apps/vadmin/auth/utils/login.py
Normal file
180
apps/vadmin/auth/utils/login.py
Normal file
@ -0,0 +1,180 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2021/10/24 16:44
|
||||
# @File : views.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : 安全认证视图
|
||||
|
||||
"""
|
||||
JWT 表示 「JSON Web Tokens」。https://jwt.io/
|
||||
|
||||
它是一个将 JSON 对象编码为密集且没有空格的长字符串的标准。
|
||||
|
||||
通过这种方式,你可以创建一个有效期为 1 周的令牌。然后当用户第二天使用令牌重新访问时,你知道该用户仍然处于登入状态。
|
||||
一周后令牌将会过期,用户将不会通过认证,必须再次登录才能获得一个新令牌。
|
||||
|
||||
我们需要安装 python-jose 以在 Python 中生成和校验 JWT 令牌:pip install python-jose[cryptography]
|
||||
|
||||
PassLib 是一个用于处理哈希密码的很棒的 Python 包。它支持许多安全哈希算法以及配合算法使用的实用程序。
|
||||
推荐的算法是 「Bcrypt」:pip install passlib[bcrypt]
|
||||
"""
|
||||
|
||||
from datetime import timedelta
|
||||
from redis.asyncio import Redis
|
||||
from fastapi import APIRouter, Depends, Request, Body
|
||||
from fastapi.security import OAuth2PasswordRequestForm
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from core.database import db_getter, redis_getter
|
||||
from core.exception import CustomException
|
||||
from utils import status
|
||||
from utils.response import SuccessResponse, ErrorResponse
|
||||
from application import settings
|
||||
from .login_manage import LoginManage
|
||||
from .validation import LoginForm, WXLoginForm
|
||||
from apps.vadmin.record.models import VadminLoginRecord
|
||||
from apps.vadmin.auth.crud import MenuDal, UserDal
|
||||
from apps.vadmin.auth.models import VadminUser
|
||||
from .current import FullAdminAuth
|
||||
from .validation.auth import Auth
|
||||
from utils.wx.oauth import WXOAuth
|
||||
import jwt
|
||||
|
||||
app = APIRouter()
|
||||
|
||||
|
||||
@app.post("/api/login", summary="API 手机号密码登录", description="Swagger API 文档登录认证")
|
||||
async def api_login_for_access_token(
|
||||
request: Request,
|
||||
data: OAuth2PasswordRequestForm = Depends(),
|
||||
db: AsyncSession = Depends(db_getter)
|
||||
):
|
||||
user = await UserDal(db).get_data(telephone=data.username, v_return_none=True)
|
||||
error_code = status.HTTP_401_UNAUTHORIZED
|
||||
if not user:
|
||||
raise CustomException(status_code=error_code, code=error_code, msg="该手机号不存在")
|
||||
result = VadminUser.verify_password(data.password, user.password)
|
||||
if not result:
|
||||
raise CustomException(status_code=error_code, code=error_code, msg="手机号或密码错误")
|
||||
if not user.is_active:
|
||||
raise CustomException(status_code=error_code, code=error_code, msg="此手机号已被冻结")
|
||||
elif not user.is_staff:
|
||||
raise CustomException(status_code=error_code, code=error_code, msg="此手机号无权限")
|
||||
access_token = LoginManage.create_token({"sub": user.telephone, "password": user.password})
|
||||
record = LoginForm(platform='2', method='0', telephone=data.username, password=data.password)
|
||||
resp = {"access_token": access_token, "token_type": "bearer"}
|
||||
await VadminLoginRecord.create_login_record(db, record, True, request, resp)
|
||||
return resp
|
||||
|
||||
|
||||
@app.post("/login", summary="手机号密码登录", description="员工登录通道,限制最多输错次数,达到最大值后将is_active=False")
|
||||
async def login_for_access_token(
|
||||
request: Request,
|
||||
data: LoginForm,
|
||||
manage: LoginManage = Depends(),
|
||||
db: AsyncSession = Depends(db_getter)
|
||||
):
|
||||
try:
|
||||
result = await manage.password_login(data, db, request)
|
||||
|
||||
if not result.status:
|
||||
raise ValueError(result.msg)
|
||||
|
||||
access_token = LoginManage.create_token(
|
||||
{"sub": result.user.telephone, "is_refresh": False, "password": result.user.password}
|
||||
)
|
||||
expires = timedelta(minutes=settings.REFRESH_TOKEN_EXPIRE_MINUTES)
|
||||
refresh_token = LoginManage.create_token(
|
||||
payload={"sub": result.user.telephone, "is_refresh": True, "password": result.user.password},
|
||||
expires=expires
|
||||
)
|
||||
resp = {
|
||||
"access_token": access_token,
|
||||
"refresh_token": refresh_token,
|
||||
"token_type": "bearer",
|
||||
"is_reset_password": result.user.is_reset_password,
|
||||
"is_wx_server_openid": result.user.is_wx_server_openid
|
||||
}
|
||||
await VadminLoginRecord.create_login_record(db, data, True, request, resp)
|
||||
return SuccessResponse(resp)
|
||||
except ValueError as e:
|
||||
await VadminLoginRecord.create_login_record(db, data, False, request, {"message": str(e)})
|
||||
return ErrorResponse(msg=str(e))
|
||||
|
||||
|
||||
@app.post("/wx/login", summary="微信服务端一键登录", description="员工登录通道")
|
||||
async def wx_login_for_access_token(
|
||||
request: Request,
|
||||
data: WXLoginForm,
|
||||
db: AsyncSession = Depends(db_getter),
|
||||
rd: Redis = Depends(redis_getter)
|
||||
):
|
||||
try:
|
||||
if data.platform != "1" or data.method != "2":
|
||||
raise ValueError("无效参数")
|
||||
wx = WXOAuth(rd, 0)
|
||||
telephone = await wx.parsing_phone_number(data.code)
|
||||
if not telephone:
|
||||
raise ValueError("无效Code")
|
||||
data.telephone = telephone
|
||||
user = await UserDal(db).get_data(telephone=telephone, v_return_none=True)
|
||||
if not user:
|
||||
raise ValueError("手机号不存在")
|
||||
elif not user.is_active:
|
||||
raise ValueError("手机号已被冻结")
|
||||
except ValueError as e:
|
||||
await VadminLoginRecord.create_login_record(db, data, False, request, {"message": str(e)})
|
||||
return ErrorResponse(msg=str(e))
|
||||
|
||||
# 更新登录时间
|
||||
await UserDal(db).update_login_info(user, request.client.host)
|
||||
|
||||
# 登录成功创建 token
|
||||
access_token = LoginManage.create_token({"sub": user.telephone, "is_refresh": False, "password": user.password})
|
||||
expires = timedelta(minutes=settings.REFRESH_TOKEN_EXPIRE_MINUTES)
|
||||
refresh_token = LoginManage.create_token(
|
||||
payload={"sub": user.telephone, "is_refresh": True, "password": user.password},
|
||||
expires=expires
|
||||
)
|
||||
resp = {
|
||||
"access_token": access_token,
|
||||
"refresh_token": refresh_token,
|
||||
"token_type": "bearer",
|
||||
"is_reset_password": user.is_reset_password,
|
||||
"is_wx_server_openid": user.is_wx_server_openid
|
||||
}
|
||||
await VadminLoginRecord.create_login_record(db, data, True, request, resp)
|
||||
return SuccessResponse(resp)
|
||||
|
||||
|
||||
@app.get("/getMenuList", summary="获取当前用户菜单树")
|
||||
async def get_menu_list(auth: Auth = Depends(FullAdminAuth())):
|
||||
return SuccessResponse(await MenuDal(auth.db).get_routers(auth.user))
|
||||
|
||||
|
||||
@app.post("/token/refresh", summary="刷新Token")
|
||||
async def token_refresh(refresh: str = Body(..., title="刷新Token")):
|
||||
error_code = status.HTTP_401_UNAUTHORIZED
|
||||
try:
|
||||
payload = jwt.decode(refresh, settings.SECRET_KEY, algorithms=[settings.ALGORITHM])
|
||||
telephone: str = payload.get("sub")
|
||||
is_refresh: bool = payload.get("is_refresh")
|
||||
password: str = payload.get("password")
|
||||
if not telephone or not is_refresh or not password:
|
||||
return ErrorResponse("未认证,请您重新登录", code=error_code, status=error_code)
|
||||
except jwt.exceptions.InvalidSignatureError:
|
||||
return ErrorResponse("无效认证,请您重新登录", code=error_code, status=error_code)
|
||||
except jwt.exceptions.ExpiredSignatureError:
|
||||
return ErrorResponse("登录已超时,请您重新登录", code=error_code, status=error_code)
|
||||
|
||||
access_token = LoginManage.create_token({"sub": telephone, "is_refresh": False, "password": password})
|
||||
expires = timedelta(minutes=settings.REFRESH_TOKEN_EXPIRE_MINUTES)
|
||||
refresh_token = LoginManage.create_token(
|
||||
payload={"sub": telephone, "is_refresh": True, "password": password},
|
||||
expires=expires
|
||||
)
|
||||
resp = {
|
||||
"access_token": access_token,
|
||||
"refresh_token": refresh_token,
|
||||
"token_type": "bearer"
|
||||
}
|
||||
return SuccessResponse(resp)
|
62
apps/vadmin/auth/utils/login_manage.py
Normal file
62
apps/vadmin/auth/utils/login_manage.py
Normal file
@ -0,0 +1,62 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2022/8/8 11:02
|
||||
# @File : auth_util.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : 简要说明
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
from fastapi import Request
|
||||
from application import settings
|
||||
import jwt
|
||||
from apps.vadmin.auth import models
|
||||
from core.database import redis_getter
|
||||
from utils.sms.code import CodeSMS
|
||||
from .validation import LoginValidation, LoginForm, LoginResult
|
||||
|
||||
|
||||
class LoginManage:
|
||||
"""
|
||||
登录认证工具
|
||||
"""
|
||||
|
||||
@LoginValidation
|
||||
async def password_login(self, data: LoginForm, user: models.VadminUser, **kwargs) -> LoginResult:
|
||||
"""
|
||||
验证用户密码
|
||||
"""
|
||||
result = models.VadminUser.verify_password(data.password, user.password)
|
||||
if result:
|
||||
return LoginResult(status=True, msg="验证成功")
|
||||
return LoginResult(status=False, msg="手机号或密码错误")
|
||||
|
||||
@LoginValidation
|
||||
async def sms_login(self, data: LoginForm, request: Request, **kwargs) -> LoginResult:
|
||||
"""
|
||||
验证用户短信验证码
|
||||
"""
|
||||
rd = redis_getter(request)
|
||||
sms = CodeSMS(data.telephone, rd)
|
||||
result = await sms.check_sms_code(data.password)
|
||||
if result:
|
||||
return LoginResult(status=True, msg="验证成功")
|
||||
return LoginResult(status=False, msg="验证码错误")
|
||||
|
||||
@staticmethod
|
||||
def create_token(payload: dict, expires: timedelta = None):
|
||||
"""
|
||||
创建一个生成新的访问令牌的工具函数。
|
||||
|
||||
pyjwt:https://github.com/jpadilla/pyjwt/blob/master/docs/usage.rst
|
||||
jwt 博客:https://geek-docs.com/python/python-tutorial/j_python-jwt.html
|
||||
|
||||
#TODO 传入的时间为UTC时间datetime.datetime类型,但是在解码时获取到的是本机时间的时间戳
|
||||
"""
|
||||
if expires:
|
||||
expire = datetime.utcnow() + expires
|
||||
else:
|
||||
expire = datetime.utcnow() + timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
|
||||
payload.update({"exp": expire})
|
||||
encoded_jwt = jwt.encode(payload, settings.SECRET_KEY, algorithm=settings.ALGORITHM)
|
||||
return encoded_jwt
|
10
apps/vadmin/auth/utils/validation/__init__.py
Normal file
10
apps/vadmin/auth/utils/validation/__init__.py
Normal file
@ -0,0 +1,10 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2022/11/9 10:14
|
||||
# @File : __init__.py.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : 简要说明
|
||||
|
||||
from .auth import Auth, AuthValidation
|
||||
from .login import LoginValidation, LoginForm, LoginResult, WXLoginForm
|
159
apps/vadmin/auth/utils/validation/auth.py
Normal file
159
apps/vadmin/auth/utils/validation/auth.py
Normal file
@ -0,0 +1,159 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2021/10/24 16:44
|
||||
# @File : auth.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : 用户凭证验证装饰器
|
||||
|
||||
from fastapi import Request
|
||||
import jwt
|
||||
from pydantic import BaseModel
|
||||
from application import settings
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from apps.vadmin.auth.models import VadminUser
|
||||
from core.exception import CustomException
|
||||
from utils import status
|
||||
from datetime import timedelta, datetime
|
||||
from apps.vadmin.auth.crud import UserDal
|
||||
|
||||
|
||||
class Auth(BaseModel):
|
||||
user: VadminUser = None
|
||||
db: AsyncSession
|
||||
data_range: int | None = None
|
||||
dept_ids: list | None = []
|
||||
|
||||
class Config:
|
||||
# 接收任意类型
|
||||
arbitrary_types_allowed = True
|
||||
|
||||
|
||||
class AuthValidation:
|
||||
"""
|
||||
用于用户每次调用接口时,验证用户提交的token是否正确,并从token中获取用户信息
|
||||
"""
|
||||
|
||||
# status_code = 401 时,表示强制要求重新登录,因账号已冻结,账号已过期,手机号码错误,刷新token无效等问题导致
|
||||
# 只有 code = 401 时,表示 token 过期,要求刷新 token
|
||||
# 只有 code = 错误值时,只是报错,不重新登陆
|
||||
error_code = status.HTTP_401_UNAUTHORIZED
|
||||
warning_code = status.HTTP_ERROR
|
||||
|
||||
# status_code = 403 时,表示强制要求重新登录,因无系统权限,而进入到系统访问等问题导致
|
||||
|
||||
@classmethod
|
||||
def validate_token(cls, request: Request, token: str | None) -> tuple[str, bool]:
|
||||
"""
|
||||
验证用户 token
|
||||
"""
|
||||
if not token:
|
||||
raise CustomException(
|
||||
msg="请您先登录!",
|
||||
code=status.HTTP_403_FORBIDDEN,
|
||||
status_code=status.HTTP_403_FORBIDDEN
|
||||
)
|
||||
try:
|
||||
payload = jwt.decode(token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM])
|
||||
telephone: str = payload.get("sub")
|
||||
exp: int = payload.get("exp")
|
||||
is_refresh: bool = payload.get("is_refresh")
|
||||
password: bool = payload.get("password")
|
||||
if not telephone or is_refresh or not password:
|
||||
raise CustomException(
|
||||
msg="未认证,请您重新登录",
|
||||
code=status.HTTP_403_FORBIDDEN,
|
||||
status_code=status.HTTP_403_FORBIDDEN
|
||||
)
|
||||
# 计算当前时间 + 缓冲时间是否大于等于 JWT 过期时间
|
||||
buffer_time = (datetime.now() + timedelta(minutes=settings.ACCESS_TOKEN_CACHE_MINUTES)).timestamp()
|
||||
# print("过期时间", exp, datetime.fromtimestamp(exp))
|
||||
# print("当前时间", buffer_time, datetime.fromtimestamp(buffer_time))
|
||||
# print("剩余时间", exp - buffer_time)
|
||||
if buffer_time >= exp:
|
||||
request.scope["if-refresh"] = 1
|
||||
else:
|
||||
request.scope["if-refresh"] = 0
|
||||
except (jwt.exceptions.InvalidSignatureError, jwt.exceptions.DecodeError):
|
||||
raise CustomException(
|
||||
msg="无效认证,请您重新登录",
|
||||
code=status.HTTP_403_FORBIDDEN,
|
||||
status_code=status.HTTP_403_FORBIDDEN
|
||||
)
|
||||
except jwt.exceptions.ExpiredSignatureError:
|
||||
raise CustomException(msg="认证已失效,请您重新登录", code=cls.error_code, status_code=cls.error_code)
|
||||
return telephone, password
|
||||
|
||||
@classmethod
|
||||
async def validate_user(cls, request: Request, user: VadminUser, db: AsyncSession, is_all: bool = True) -> Auth:
|
||||
"""
|
||||
验证用户信息
|
||||
:param request:
|
||||
:param user:
|
||||
:param db:
|
||||
:param is_all: 是否所有人访问,不加权限
|
||||
:return:
|
||||
"""
|
||||
if user is None:
|
||||
raise CustomException(msg="未认证,请您重新登陆", code=cls.error_code, status_code=cls.error_code)
|
||||
elif not user.is_active:
|
||||
raise CustomException(msg="用户已被冻结!", code=cls.error_code, status_code=cls.error_code)
|
||||
request.scope["telephone"] = user.telephone
|
||||
request.scope["user_id"] = user.id
|
||||
request.scope["user_name"] = user.name
|
||||
try:
|
||||
request.scope["body"] = await request.body()
|
||||
except RuntimeError:
|
||||
request.scope["body"] = "获取失败"
|
||||
if is_all:
|
||||
return Auth(user=user, db=db)
|
||||
data_range, dept_ids = await cls.get_user_data_range(user, db)
|
||||
return Auth(user=user, db=db, data_range=data_range, dept_ids=dept_ids)
|
||||
|
||||
@classmethod
|
||||
def get_user_permissions(cls, user: VadminUser) -> set:
|
||||
"""
|
||||
获取员工用户所有权限列表
|
||||
:param user: 用户实例
|
||||
:return:
|
||||
"""
|
||||
if user.is_admin():
|
||||
return {'*.*.*'}
|
||||
permissions = set()
|
||||
for role_obj in user.roles:
|
||||
for menu in role_obj.menus:
|
||||
if menu.perms and not menu.disabled:
|
||||
permissions.add(menu.perms)
|
||||
return permissions
|
||||
|
||||
@classmethod
|
||||
async def get_user_data_range(cls, user: VadminUser, db: AsyncSession) -> tuple:
|
||||
"""
|
||||
获取用户数据范围
|
||||
0 仅本人数据权限 create_user_id 查询
|
||||
1 本部门数据权限 部门 id 左连接查询
|
||||
2 本部门及以下数据权限 部门 id 左连接查询
|
||||
3 自定义数据权限 部门 id 左连接查询
|
||||
4 全部数据权限 无
|
||||
:param user:
|
||||
:param db:
|
||||
:return:
|
||||
"""
|
||||
if user.is_admin():
|
||||
return 4, ["*"]
|
||||
data_range = max([i.data_range for i in user.roles])
|
||||
dept_ids = set()
|
||||
if data_range == 0:
|
||||
pass
|
||||
elif data_range == 1:
|
||||
for dept in user.depts:
|
||||
dept_ids.add(dept.id)
|
||||
elif data_range == 2:
|
||||
# 递归获取部门列表
|
||||
dept_ids = await UserDal(db).recursion_get_dept_ids(user)
|
||||
elif data_range == 3:
|
||||
for role_obj in user.roles:
|
||||
for dept in role_obj.depts:
|
||||
dept_ids.add(dept.id)
|
||||
elif data_range == 4:
|
||||
dept_ids.add("*")
|
||||
return data_range, list(dept_ids)
|
92
apps/vadmin/auth/utils/validation/login.py
Normal file
92
apps/vadmin/auth/utils/validation/login.py
Normal file
@ -0,0 +1,92 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2022/11/9 10:15
|
||||
# @File : login.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : 登录验证装饰器
|
||||
|
||||
from fastapi import Request
|
||||
from pydantic import BaseModel, field_validator
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from application.settings import DEFAULT_AUTH_ERROR_MAX_NUMBER, DEMO, REDIS_DB_ENABLE
|
||||
from apps.vadmin.auth import crud, schemas
|
||||
from core.database import redis_getter
|
||||
from core.validator import vali_telephone
|
||||
from utils.count import Count
|
||||
|
||||
|
||||
class LoginForm(BaseModel):
|
||||
telephone: str
|
||||
password: str
|
||||
method: str = '0' # 认证方式,0:密码登录,1:短信登录,2:微信一键登录
|
||||
platform: str = '0' # 登录平台,0:PC端管理系统,1:移动端管理系统
|
||||
|
||||
# 重用验证器:https://docs.pydantic.dev/dev-v2/usage/validators/#reuse-validators
|
||||
normalize_telephone = field_validator('telephone')(vali_telephone)
|
||||
|
||||
|
||||
class WXLoginForm(BaseModel):
|
||||
telephone: str | None = None
|
||||
code: str
|
||||
method: str = '2' # 认证方式,0:密码登录,1:短信登录,2:微信一键登录
|
||||
platform: str = '1' # 登录平台,0:PC端管理系统,1:移动端管理系统
|
||||
|
||||
|
||||
class LoginResult(BaseModel):
|
||||
status: bool | None = False
|
||||
user: schemas.UserPasswordOut | None = None
|
||||
msg: str | None = None
|
||||
|
||||
class Config:
|
||||
arbitrary_types_allowed = True
|
||||
|
||||
|
||||
class LoginValidation:
|
||||
|
||||
"""
|
||||
验证用户登录时提交的数据是否有效
|
||||
"""
|
||||
|
||||
def __init__(self, func):
|
||||
self.func = func
|
||||
|
||||
async def __call__(self, data: LoginForm, db: AsyncSession, request: Request) -> LoginResult:
|
||||
self.result = LoginResult()
|
||||
if data.platform not in ["0", "1"] or data.method not in ["0", "1"]:
|
||||
self.result.msg = "无效参数"
|
||||
return self.result
|
||||
user = await crud.UserDal(db).get_data(telephone=data.telephone, v_return_none=True)
|
||||
if not user:
|
||||
self.result.msg = "该手机号不存在!"
|
||||
return self.result
|
||||
|
||||
result = await self.func(self, data=data, user=user, request=request)
|
||||
|
||||
if REDIS_DB_ENABLE:
|
||||
count_key = f"{data.telephone}_password_auth" if data.method == '0' else f"{data.telephone}_sms_auth"
|
||||
count = Count(redis_getter(request), count_key)
|
||||
else:
|
||||
count = None
|
||||
|
||||
if not result.status:
|
||||
self.result.msg = result.msg
|
||||
if not DEMO and count:
|
||||
number = await count.add(ex=86400)
|
||||
if number >= DEFAULT_AUTH_ERROR_MAX_NUMBER:
|
||||
await count.reset()
|
||||
# 如果等于最大次数,那么就将用户 is_active=False
|
||||
user.is_active = False
|
||||
await db.flush()
|
||||
elif not user.is_active:
|
||||
self.result.msg = "此手机号已被冻结!"
|
||||
elif data.platform in ["0", "1"] and not user.is_staff:
|
||||
self.result.msg = "此手机号无权限!"
|
||||
else:
|
||||
if not DEMO and count:
|
||||
await count.delete()
|
||||
self.result.msg = "OK"
|
||||
self.result.status = True
|
||||
self.result.user = schemas.UserPasswordOut.model_validate(user)
|
||||
await crud.UserDal(db).update_login_info(user, request.client.host)
|
||||
return self.result
|
307
apps/vadmin/auth/views.py
Normal file
307
apps/vadmin/auth/views.py
Normal file
@ -0,0 +1,307 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2022/2/24 17:02
|
||||
# @File : views.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : 简要说明
|
||||
|
||||
from redis.asyncio import Redis
|
||||
from fastapi import APIRouter, Depends, Body, UploadFile, Request
|
||||
from sqlalchemy.orm import joinedload
|
||||
from core.database import redis_getter
|
||||
from utils.response import SuccessResponse, ErrorResponse
|
||||
from . import schemas, crud, models
|
||||
from core.dependencies import IdList
|
||||
from apps.vadmin.auth.utils.current import AllUserAuth, FullAdminAuth, OpenAuth
|
||||
from apps.vadmin.auth.utils.validation.auth import Auth
|
||||
from .params import UserParams, RoleParams, DeptParams
|
||||
|
||||
app = APIRouter()
|
||||
|
||||
|
||||
###########################################################
|
||||
# 接口测试
|
||||
###########################################################
|
||||
@app.get("/test", summary="接口测试")
|
||||
async def test(auth: Auth = Depends(OpenAuth())):
|
||||
return SuccessResponse(await crud.TestDal(auth.db).relationship_where_operations_has())
|
||||
|
||||
|
||||
###########################################################
|
||||
# 用户管理
|
||||
###########################################################
|
||||
@app.get("/users", summary="获取用户列表")
|
||||
async def get_users(
|
||||
params: UserParams = Depends(),
|
||||
auth: Auth = Depends(FullAdminAuth(permissions=["auth.user.list"]))
|
||||
):
|
||||
model = models.VadminUser
|
||||
options = [joinedload(model.roles), joinedload(model.depts)]
|
||||
schema = schemas.UserOut
|
||||
datas, count = await crud.UserDal(auth.db).get_datas(
|
||||
**params.dict(),
|
||||
v_options=options,
|
||||
v_schema=schema,
|
||||
v_return_count=True
|
||||
)
|
||||
return SuccessResponse(datas, count=count)
|
||||
|
||||
|
||||
@app.post("/users", summary="创建用户")
|
||||
async def create_user(data: schemas.UserIn, auth: Auth = Depends(FullAdminAuth(permissions=["auth.user.create"]))):
|
||||
return SuccessResponse(await crud.UserDal(auth.db).create_data(data=data))
|
||||
|
||||
|
||||
@app.delete("/users", summary="批量删除用户", description="软删除,删除后清空所关联的角色")
|
||||
async def delete_users(ids: IdList = Depends(), auth: Auth = Depends(FullAdminAuth(permissions=["auth.user.delete"]))):
|
||||
if auth.user.id in ids.ids:
|
||||
return ErrorResponse("不能删除当前登录用户")
|
||||
elif 1 in ids.ids:
|
||||
return ErrorResponse("不能删除超级管理员用户")
|
||||
await crud.UserDal(auth.db).delete_datas(ids=ids.ids, v_soft=True, is_active=False)
|
||||
return SuccessResponse("删除成功")
|
||||
|
||||
|
||||
@app.put("/users/{data_id}", summary="更新用户信息")
|
||||
async def put_user(
|
||||
data_id: int,
|
||||
data: schemas.UserUpdate,
|
||||
auth: Auth = Depends(FullAdminAuth(permissions=["auth.user.update"]))
|
||||
):
|
||||
return SuccessResponse(await crud.UserDal(auth.db).put_data(data_id, data))
|
||||
|
||||
|
||||
@app.get("/users/{data_id}", summary="获取用户信息")
|
||||
async def get_user(
|
||||
data_id: int,
|
||||
auth: Auth = Depends(FullAdminAuth(permissions=["auth.user.view", "auth.user.update"]))
|
||||
):
|
||||
model = models.VadminUser
|
||||
options = [joinedload(model.roles), joinedload(model.depts)]
|
||||
schema = schemas.UserOut
|
||||
return SuccessResponse(await crud.UserDal(auth.db).get_data(data_id, v_options=options, v_schema=schema))
|
||||
|
||||
|
||||
@app.post("/user/current/reset/password", summary="重置当前用户密码")
|
||||
async def user_current_reset_password(data: schemas.ResetPwd, auth: Auth = Depends(AllUserAuth())):
|
||||
return SuccessResponse(await crud.UserDal(auth.db).reset_current_password(auth.user, data))
|
||||
|
||||
|
||||
@app.post("/user/current/update/info", summary="更新当前用户基本信息")
|
||||
async def post_user_current_update_info(data: schemas.UserUpdateBaseInfo, auth: Auth = Depends(AllUserAuth())):
|
||||
return SuccessResponse(await crud.UserDal(auth.db).update_current_info(auth.user, data))
|
||||
|
||||
|
||||
@app.post("/user/current/update/avatar", summary="更新当前用户头像")
|
||||
async def post_user_current_update_avatar(file: UploadFile, auth: Auth = Depends(AllUserAuth())):
|
||||
return SuccessResponse(await crud.UserDal(auth.db).update_current_avatar(auth.user, file))
|
||||
|
||||
|
||||
@app.get("/user/admin/current/info", summary="获取当前管理员信息")
|
||||
async def get_user_admin_current_info(auth: Auth = Depends(FullAdminAuth())):
|
||||
result = schemas.UserOut.model_validate(auth.user).model_dump()
|
||||
result["permissions"] = list(FullAdminAuth.get_user_permissions(auth.user))
|
||||
return SuccessResponse(result)
|
||||
|
||||
|
||||
@app.post("/user/export/query/list/to/excel", summary="导出用户查询列表为excel")
|
||||
async def post_user_export_query_list(
|
||||
header: list = Body(..., title="表头与对应字段"),
|
||||
params: UserParams = Depends(),
|
||||
auth: Auth = Depends(FullAdminAuth(permissions=["auth.user.export"]))
|
||||
):
|
||||
return SuccessResponse(await crud.UserDal(auth.db).export_query_list(header, params))
|
||||
|
||||
|
||||
@app.get("/user/download/import/template", summary="下载最新批量导入用户模板")
|
||||
async def get_user_download_new_import_template(auth: Auth = Depends(AllUserAuth())):
|
||||
return SuccessResponse(await crud.UserDal(auth.db).download_import_template())
|
||||
|
||||
|
||||
@app.post("/import/users", summary="批量导入用户")
|
||||
async def post_import_users(file: UploadFile, auth: Auth = Depends(FullAdminAuth(permissions=["auth.user.import"]))):
|
||||
return SuccessResponse(await crud.UserDal(auth.db).import_users(file))
|
||||
|
||||
|
||||
@app.post("/users/init/password/send/sms", summary="初始化所选用户密码并发送通知短信")
|
||||
async def post_users_init_password(
|
||||
request: Request,
|
||||
ids: IdList = Depends(),
|
||||
auth: Auth = Depends(FullAdminAuth(permissions=["auth.user.reset"])),
|
||||
rd: Redis = Depends(redis_getter)
|
||||
):
|
||||
return SuccessResponse(await crud.UserDal(auth.db).init_password_send_sms(ids.ids, rd))
|
||||
|
||||
|
||||
@app.post("/users/init/password/send/email", summary="初始化所选用户密码并发送通知邮件")
|
||||
async def post_users_init_password_send_email(
|
||||
request: Request,
|
||||
ids: IdList = Depends(),
|
||||
auth: Auth = Depends(FullAdminAuth(permissions=["auth.user.reset"])),
|
||||
rd: Redis = Depends(redis_getter)
|
||||
):
|
||||
return SuccessResponse(await crud.UserDal(auth.db).init_password_send_email(ids.ids, rd))
|
||||
|
||||
|
||||
@app.put("/users/wx/server/openid", summary="更新当前用户服务端微信平台openid")
|
||||
async def put_user_wx_server_openid(code: str, auth: Auth = Depends(AllUserAuth()), rd: Redis = Depends(redis_getter)):
|
||||
result = await crud.UserDal(auth.db).update_wx_server_openid(code, auth.user, rd)
|
||||
return SuccessResponse(result)
|
||||
|
||||
|
||||
###########################################################
|
||||
# 角色管理
|
||||
###########################################################
|
||||
@app.get("/roles", summary="获取角色列表")
|
||||
async def get_roles(
|
||||
params: RoleParams = Depends(),
|
||||
auth: Auth = Depends(FullAdminAuth(permissions=["auth.role.list"]))
|
||||
):
|
||||
datas, count = await crud.RoleDal(auth.db).get_datas(**params.dict(), v_return_count=True)
|
||||
return SuccessResponse(datas, count=count)
|
||||
|
||||
|
||||
@app.post("/roles", summary="创建角色信息")
|
||||
async def create_role(role: schemas.RoleIn, auth: Auth = Depends(FullAdminAuth(permissions=["auth.role.create"]))):
|
||||
return SuccessResponse(await crud.RoleDal(auth.db).create_data(data=role))
|
||||
|
||||
|
||||
@app.delete("/roles", summary="批量删除角色", description="硬删除, 如果存在用户关联则无法删除")
|
||||
async def delete_roles(ids: IdList = Depends(), auth: Auth = Depends(FullAdminAuth(permissions=["auth.role.delete"]))):
|
||||
if 1 in ids.ids:
|
||||
return ErrorResponse("不能删除管理员角色")
|
||||
await crud.RoleDal(auth.db).delete_datas(ids.ids, v_soft=False)
|
||||
return SuccessResponse("删除成功")
|
||||
|
||||
|
||||
@app.put("/roles/{data_id}", summary="更新角色信息")
|
||||
async def put_role(
|
||||
data_id: int,
|
||||
data: schemas.RoleIn,
|
||||
auth: Auth = Depends(FullAdminAuth(permissions=["auth.role.update"]))
|
||||
):
|
||||
if 1 == data_id:
|
||||
return ErrorResponse("不能修改管理员角色")
|
||||
return SuccessResponse(await crud.RoleDal(auth.db).put_data(data_id, data))
|
||||
|
||||
|
||||
@app.get("/roles/options", summary="获取角色选择项")
|
||||
async def get_role_options(auth: Auth = Depends(FullAdminAuth(permissions=["auth.user.create", "auth.user.update"]))):
|
||||
return SuccessResponse(await crud.RoleDal(auth.db).get_select_datas())
|
||||
|
||||
|
||||
@app.get("/roles/{data_id}", summary="获取角色信息")
|
||||
async def get_role(
|
||||
data_id: int,
|
||||
auth: Auth = Depends(FullAdminAuth(permissions=["auth.role.view", "auth.role.update"]))
|
||||
):
|
||||
model = models.VadminRole
|
||||
options = [joinedload(model.menus), joinedload(model.depts)]
|
||||
schema = schemas.RoleOut
|
||||
return SuccessResponse(await crud.RoleDal(auth.db).get_data(data_id, v_options=options, v_schema=schema))
|
||||
|
||||
|
||||
###########################################################
|
||||
# 菜单管理
|
||||
###########################################################
|
||||
@app.get("/menus", summary="获取菜单列表")
|
||||
async def get_menus(auth: Auth = Depends(FullAdminAuth(permissions=["auth.menu.list"]))):
|
||||
datas = await crud.MenuDal(auth.db).get_tree_list(mode=1)
|
||||
return SuccessResponse(datas)
|
||||
|
||||
|
||||
@app.get("/menus/tree/options", summary="获取菜单树选择项,添加/修改菜单时使用")
|
||||
async def get_menus_options(auth: Auth = Depends(FullAdminAuth(permissions=["auth.menu.create", "auth.menu.update"]))):
|
||||
datas = await crud.MenuDal(auth.db).get_tree_list(mode=2)
|
||||
return SuccessResponse(datas)
|
||||
|
||||
|
||||
@app.get("/menus/role/tree/options", summary="获取菜单列表树信息,角色权限使用")
|
||||
async def get_menus_treeselect(
|
||||
auth: Auth = Depends(FullAdminAuth(permissions=["auth.role.create", "auth.role.update"]))
|
||||
):
|
||||
return SuccessResponse(await crud.MenuDal(auth.db).get_tree_list(mode=3))
|
||||
|
||||
|
||||
@app.post("/menus", summary="创建菜单信息")
|
||||
async def create_menu(menu: schemas.Menu, auth: Auth = Depends(FullAdminAuth(permissions=["auth.menu.create"]))):
|
||||
if menu.parent_id:
|
||||
menu.alwaysShow = False
|
||||
return SuccessResponse(await crud.MenuDal(auth.db).create_data(data=menu))
|
||||
|
||||
|
||||
@app.delete("/menus", summary="批量删除菜单", description="硬删除, 如果存在角色关联则无法删除")
|
||||
async def delete_menus(ids: IdList = Depends(), auth: Auth = Depends(FullAdminAuth(permissions=["auth.menu.delete"]))):
|
||||
await crud.MenuDal(auth.db).delete_datas(ids.ids, v_soft=False)
|
||||
return SuccessResponse("删除成功")
|
||||
|
||||
|
||||
@app.put("/menus/{data_id}", summary="更新菜单信息")
|
||||
async def put_menus(
|
||||
data_id: int,
|
||||
data: schemas.Menu, auth: Auth = Depends(FullAdminAuth(permissions=["auth.menu.update"]))
|
||||
):
|
||||
return SuccessResponse(await crud.MenuDal(auth.db).put_data(data_id, data))
|
||||
|
||||
|
||||
@app.get("/menus/{data_id}", summary="获取菜单信息")
|
||||
async def get_menus(
|
||||
data_id: int,
|
||||
auth: Auth = Depends(FullAdminAuth(permissions=["auth.menu.view", "auth.menu.update"]))
|
||||
):
|
||||
schema = schemas.MenuSimpleOut
|
||||
return SuccessResponse(await crud.MenuDal(auth.db).get_data(data_id, v_schema=schema))
|
||||
|
||||
|
||||
@app.get("/role/menus/tree/{role_id}", summary="获取菜单列表树信息以及角色菜单权限ID,角色权限使用")
|
||||
async def get_role_menu_tree(
|
||||
role_id: int,
|
||||
auth: Auth = Depends(FullAdminAuth(permissions=["auth.role.create", "auth.role.update"]))
|
||||
):
|
||||
tree_data = await crud.MenuDal(auth.db).get_tree_list(mode=3)
|
||||
role_menu_tree = await crud.RoleDal(auth.db).get_role_menu_tree(role_id)
|
||||
return SuccessResponse({"role_menu_tree": role_menu_tree, "menus": tree_data})
|
||||
|
||||
|
||||
###########################################################
|
||||
# 部门管理
|
||||
###########################################################
|
||||
@app.get("/depts", summary="获取部门列表")
|
||||
async def get_depts(
|
||||
params: DeptParams = Depends(),
|
||||
auth: Auth = Depends(FullAdminAuth())
|
||||
):
|
||||
datas = await crud.DeptDal(auth.db).get_tree_list(1)
|
||||
return SuccessResponse(datas)
|
||||
|
||||
|
||||
@app.get("/dept/tree/options", summary="获取部门树选择项,添加/修改部门时使用")
|
||||
async def get_dept_options(auth: Auth = Depends(FullAdminAuth())):
|
||||
datas = await crud.DeptDal(auth.db).get_tree_list(mode=2)
|
||||
return SuccessResponse(datas)
|
||||
|
||||
|
||||
@app.get("/dept/user/tree/options", summary="获取部门树选择项,添加/修改用户时使用")
|
||||
async def get_dept_treeselect(auth: Auth = Depends(FullAdminAuth())):
|
||||
return SuccessResponse(await crud.DeptDal(auth.db).get_tree_list(mode=3))
|
||||
|
||||
|
||||
@app.post("/depts", summary="创建部门信息")
|
||||
async def create_dept(data: schemas.Dept, auth: Auth = Depends(FullAdminAuth())):
|
||||
return SuccessResponse(await crud.DeptDal(auth.db).create_data(data=data))
|
||||
|
||||
|
||||
@app.delete("/depts", summary="批量删除部门", description="硬删除, 如果存在用户关联则无法删除")
|
||||
async def delete_depts(ids: IdList = Depends(), auth: Auth = Depends(FullAdminAuth())):
|
||||
await crud.DeptDal(auth.db).delete_datas(ids.ids, v_soft=False)
|
||||
return SuccessResponse("删除成功")
|
||||
|
||||
|
||||
@app.put("/depts/{data_id}", summary="更新部门信息")
|
||||
async def put_dept(
|
||||
data_id: int,
|
||||
data: schemas.Dept,
|
||||
auth: Auth = Depends(FullAdminAuth())
|
||||
):
|
||||
return SuccessResponse(await crud.DeptDal(auth.db).put_data(data_id, data))
|
7
apps/vadmin/help/__init__.py
Normal file
7
apps/vadmin/help/__init__.py
Normal file
@ -0,0 +1,7 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2023-02-15 20:03:49
|
||||
# @File : __init__.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : 帮助中心
|
37
apps/vadmin/help/crud.py
Normal file
37
apps/vadmin/help/crud.py
Normal file
@ -0,0 +1,37 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2023-02-15 20:03:49
|
||||
# @File : crud.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : 帮助中心 - 增删改查
|
||||
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from core.crud import DalBase
|
||||
from . import models, schemas
|
||||
|
||||
|
||||
class IssueDal(DalBase):
|
||||
|
||||
def __init__(self, db: AsyncSession):
|
||||
super(IssueDal, self).__init__()
|
||||
self.db = db
|
||||
self.model = models.VadminIssue
|
||||
self.schema = schemas.IssueSimpleOut
|
||||
|
||||
async def add_view_number(self, data_id: int) -> None:
|
||||
"""
|
||||
更新常见问题查看次数+1
|
||||
"""
|
||||
obj: models.VadminIssue = await self.get_data(data_id)
|
||||
obj.view_number = obj.view_number + 1 if obj.view_number else 1
|
||||
await self.flush(obj)
|
||||
|
||||
|
||||
class IssueCategoryDal(DalBase):
|
||||
|
||||
def __init__(self, db: AsyncSession):
|
||||
super(IssueCategoryDal, self).__init__()
|
||||
self.db = db
|
||||
self.model = models.VadminIssueCategory
|
||||
self.schema = schemas.IssueCategorySimpleOut
|
10
apps/vadmin/help/models/__init__.py
Normal file
10
apps/vadmin/help/models/__init__.py
Normal file
@ -0,0 +1,10 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2023-02-15 20:03:49
|
||||
# @File : __init__.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : 初始化文件
|
||||
|
||||
|
||||
from .issue import VadminIssue, VadminIssueCategory
|
54
apps/vadmin/help/models/issue.py
Normal file
54
apps/vadmin/help/models/issue.py
Normal file
@ -0,0 +1,54 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2022/7/7 13:41
|
||||
# @File : issue.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : 常见问题
|
||||
|
||||
from sqlalchemy.orm import relationship, Mapped, mapped_column
|
||||
from apps.vadmin.auth.models import VadminUser
|
||||
from db.db_base import BaseModel
|
||||
from sqlalchemy import String, Boolean, Integer, ForeignKey, Text
|
||||
|
||||
|
||||
class VadminIssueCategory(BaseModel):
|
||||
__tablename__ = "vadmin_help_issue_category"
|
||||
__table_args__ = ({'comment': '常见问题类别表'})
|
||||
|
||||
name: Mapped[str] = mapped_column(String(50), index=True, nullable=False, comment="类别名称")
|
||||
platform: Mapped[str] = mapped_column(String(8), index=True, nullable=False, comment="展示平台")
|
||||
is_active: Mapped[bool] = mapped_column(Boolean, default=True, comment="是否可见")
|
||||
|
||||
issues: Mapped[list["VadminIssue"]] = relationship(back_populates='category')
|
||||
|
||||
create_user_id: Mapped[int] = mapped_column(
|
||||
Integer,
|
||||
ForeignKey("vadmin_auth_user.id", ondelete='RESTRICT'),
|
||||
comment="创建人"
|
||||
)
|
||||
create_user: Mapped[VadminUser] = relationship(foreign_keys=create_user_id)
|
||||
|
||||
|
||||
class VadminIssue(BaseModel):
|
||||
__tablename__ = "vadmin_help_issue"
|
||||
__table_args__ = ({'comment': '常见问题记录表'})
|
||||
|
||||
category_id: Mapped[int] = mapped_column(
|
||||
Integer,
|
||||
ForeignKey("vadmin_help_issue_category.id", ondelete='CASCADE'),
|
||||
comment="类别"
|
||||
)
|
||||
category: Mapped[list["VadminIssueCategory"]] = relationship(foreign_keys=category_id, back_populates='issues')
|
||||
|
||||
title: Mapped[str] = mapped_column(String(255), index=True, nullable=False, comment="标题")
|
||||
content: Mapped[str] = mapped_column(Text, comment="内容")
|
||||
view_number: Mapped[int] = mapped_column(Integer, default=0, comment="查看次数")
|
||||
is_active: Mapped[bool] = mapped_column(Boolean, default=True, comment="是否可见")
|
||||
|
||||
create_user_id: Mapped[int] = mapped_column(
|
||||
Integer,
|
||||
ForeignKey("vadmin_auth_user.id", ondelete='RESTRICT'),
|
||||
comment="创建人"
|
||||
)
|
||||
create_user: Mapped[VadminUser] = relationship(foreign_keys=create_user_id)
|
10
apps/vadmin/help/params/__init__.py
Normal file
10
apps/vadmin/help/params/__init__.py
Normal file
@ -0,0 +1,10 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2023-02-15 20:03:49
|
||||
# @File : __init__.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : 初始化文件
|
||||
|
||||
|
||||
from .issue import IssueParams, IssueCategoryParams
|
51
apps/vadmin/help/params/issue.py
Normal file
51
apps/vadmin/help/params/issue.py
Normal file
@ -0,0 +1,51 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2022/7/7 13:41
|
||||
# @File : issue.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : 常见问题
|
||||
|
||||
|
||||
from fastapi import Depends
|
||||
from core.dependencies import Paging, QueryParams
|
||||
|
||||
|
||||
class IssueParams(QueryParams):
|
||||
"""
|
||||
列表分页
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
params: Paging = Depends(),
|
||||
is_active: bool = None,
|
||||
title: str = None,
|
||||
category_id: int = None
|
||||
):
|
||||
super().__init__(params)
|
||||
self.v_order = "desc"
|
||||
self.v_order_field = "create_datetime"
|
||||
self.is_active = is_active
|
||||
self.category_id = category_id
|
||||
self.title = ("like", title)
|
||||
|
||||
|
||||
class IssueCategoryParams(QueryParams):
|
||||
"""
|
||||
列表分页
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
params: Paging = Depends(),
|
||||
is_active: bool = None,
|
||||
platform: str = None,
|
||||
name: str = None
|
||||
):
|
||||
super().__init__(params)
|
||||
self.v_order = "desc"
|
||||
self.v_order_field = "create_datetime"
|
||||
self.is_active = is_active
|
||||
self.platform = platform
|
||||
self.name = ("like", name)
|
12
apps/vadmin/help/schemas/__init__.py
Normal file
12
apps/vadmin/help/schemas/__init__.py
Normal file
@ -0,0 +1,12 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2023-02-15 20:03:49
|
||||
# @File : __init__.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : 初始化文件
|
||||
|
||||
|
||||
from .issue import Issue, IssueSimpleOut, IssueListOut
|
||||
from .issue_category import IssueCategory, IssueCategorySimpleOut, IssueCategoryListOut, IssueCategoryOptionsOut
|
||||
from .issue_m2m import IssueCategoryPlatformOut
|
38
apps/vadmin/help/schemas/issue.py
Normal file
38
apps/vadmin/help/schemas/issue.py
Normal file
@ -0,0 +1,38 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2022/7/7 13:41
|
||||
# @File : issue.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : 常见问题
|
||||
|
||||
from typing import Optional
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
from core.data_types import DatetimeStr
|
||||
from apps.vadmin.auth.schemas import UserSimpleOut
|
||||
from .issue_category import IssueCategorySimpleOut
|
||||
|
||||
|
||||
class Issue(BaseModel):
|
||||
category_id: int | None = None
|
||||
create_user_id: int | None = None
|
||||
|
||||
title: str | None = None
|
||||
content: str | None = None
|
||||
view_number: int | None = None
|
||||
is_active: bool | None = None
|
||||
|
||||
|
||||
class IssueSimpleOut(Issue):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: int
|
||||
update_datetime: DatetimeStr
|
||||
create_datetime: DatetimeStr
|
||||
|
||||
|
||||
class IssueListOut(IssueSimpleOut):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
create_user: UserSimpleOut
|
||||
category: IssueCategorySimpleOut
|
43
apps/vadmin/help/schemas/issue_category.py
Normal file
43
apps/vadmin/help/schemas/issue_category.py
Normal file
@ -0,0 +1,43 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2022/7/7 13:41
|
||||
# @File : issue_category.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : 常见问题类别
|
||||
|
||||
|
||||
from typing import Optional
|
||||
from pydantic import BaseModel, Field, ConfigDict
|
||||
from core.data_types import DatetimeStr
|
||||
from apps.vadmin.auth.schemas import UserSimpleOut
|
||||
|
||||
|
||||
class IssueCategory(BaseModel):
|
||||
name: str | None = None
|
||||
platform: str | None = None
|
||||
is_active: bool | None = None
|
||||
|
||||
create_user_id: int | None = None
|
||||
|
||||
|
||||
class IssueCategorySimpleOut(IssueCategory):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: int
|
||||
update_datetime: DatetimeStr
|
||||
create_datetime: DatetimeStr
|
||||
|
||||
|
||||
class IssueCategoryListOut(IssueCategorySimpleOut):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
create_user: UserSimpleOut
|
||||
|
||||
|
||||
class IssueCategoryOptionsOut(BaseModel):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
label: str = Field(alias='name')
|
||||
value: int = Field(alias='id')
|
||||
|
27
apps/vadmin/help/schemas/issue_m2m.py
Normal file
27
apps/vadmin/help/schemas/issue_m2m.py
Normal file
@ -0,0 +1,27 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2023/2/17 15:18
|
||||
# @File : issue_m2m.py.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : 简要说明
|
||||
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
from core.data_types import DatetimeStr
|
||||
from .issue import IssueSimpleOut
|
||||
|
||||
|
||||
class IssueCategoryPlatformOut(BaseModel):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
name: str | None = None
|
||||
platform: str | None = None
|
||||
is_active: bool | None = None
|
||||
create_user_id: int | None = None
|
||||
|
||||
id: int
|
||||
update_datetime: DatetimeStr
|
||||
create_datetime: DatetimeStr
|
||||
|
||||
issues: list[IssueSimpleOut] = None
|
||||
|
125
apps/vadmin/help/views.py
Normal file
125
apps/vadmin/help/views.py
Normal file
@ -0,0 +1,125 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2023-02-15 20:03:49
|
||||
# @File : views.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : 帮助中心视图
|
||||
|
||||
from fastapi import APIRouter, Depends
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy.orm import joinedload
|
||||
from core.database import db_getter
|
||||
from utils.response import SuccessResponse
|
||||
from . import schemas, crud, params, models
|
||||
from core.dependencies import IdList
|
||||
from apps.vadmin.auth.utils.current import AllUserAuth
|
||||
from apps.vadmin.auth.utils.validation.auth import Auth
|
||||
|
||||
app = APIRouter()
|
||||
|
||||
|
||||
###########################################################
|
||||
# 类别管理
|
||||
###########################################################
|
||||
@app.get("/issue/categorys", summary="获取类别列表")
|
||||
async def get_issue_categorys(p: params.IssueCategoryParams = Depends(), auth: Auth = Depends(AllUserAuth())):
|
||||
model = models.VadminIssueCategory
|
||||
options = [joinedload(model.create_user)]
|
||||
schema = schemas.IssueCategoryListOut
|
||||
datas, count = await crud.IssueCategoryDal(auth.db).get_datas(
|
||||
**p.dict(),
|
||||
v_options=options,
|
||||
v_schema=schema,
|
||||
v_return_count=True
|
||||
)
|
||||
return SuccessResponse(datas, count=count)
|
||||
|
||||
|
||||
@app.get("/issue/categorys/options", summary="获取类别选择项")
|
||||
async def get_issue_categorys_options(auth: Auth = Depends(AllUserAuth())):
|
||||
schema = schemas.IssueCategoryOptionsOut
|
||||
return SuccessResponse(await crud.IssueCategoryDal(auth.db).get_datas(limit=0, is_active=True, v_schema=schema))
|
||||
|
||||
|
||||
@app.post("/issue/categorys", summary="创建类别")
|
||||
async def create_issue_category(data: schemas.IssueCategory, auth: Auth = Depends(AllUserAuth())):
|
||||
data.create_user_id = auth.user.id
|
||||
return SuccessResponse(await crud.IssueCategoryDal(auth.db).create_data(data=data))
|
||||
|
||||
|
||||
@app.delete("/issue/categorys", summary="批量删除类别", description="硬删除")
|
||||
async def delete_issue_categorys(ids: IdList = Depends(), auth: Auth = Depends(AllUserAuth())):
|
||||
await crud.IssueCategoryDal(auth.db).delete_datas(ids=ids.ids, v_soft=False)
|
||||
return SuccessResponse("删除成功")
|
||||
|
||||
|
||||
@app.put("/issue/categorys/{data_id}", summary="更新类别信息")
|
||||
async def put_issue_category(data_id: int, data: schemas.IssueCategory, auth: Auth = Depends(AllUserAuth())):
|
||||
return SuccessResponse(await crud.IssueCategoryDal(auth.db).put_data(data_id, data))
|
||||
|
||||
|
||||
@app.get("/issue/categorys/{data_id}", summary="获取类别信息")
|
||||
async def get_issue_category(data_id: int, auth: Auth = Depends(AllUserAuth())):
|
||||
schema = schemas.IssueCategorySimpleOut
|
||||
return SuccessResponse(await crud.IssueCategoryDal(auth.db).get_data(data_id, v_schema=schema))
|
||||
|
||||
|
||||
@app.get("/issue/categorys/platform/{platform}", summary="获取平台中的常见问题类别列表")
|
||||
async def get_issue_category_platform(platform: str, db: AsyncSession = Depends(db_getter)):
|
||||
model = models.VadminIssueCategory
|
||||
options = [joinedload(model.issues)]
|
||||
schema = schemas.IssueCategoryPlatformOut
|
||||
result = await crud.IssueCategoryDal(db).get_datas(
|
||||
limit=0,
|
||||
platform=platform,
|
||||
is_active=True,
|
||||
v_schema=schema,
|
||||
v_options=options
|
||||
)
|
||||
return SuccessResponse(result)
|
||||
|
||||
|
||||
###########################################################
|
||||
# 问题管理
|
||||
###########################################################
|
||||
@app.get("/issues", summary="获取问题列表")
|
||||
async def get_issues(p: params.IssueParams = Depends(), auth: Auth = Depends(AllUserAuth())):
|
||||
model = models.VadminIssue
|
||||
options = [joinedload(model.create_user), joinedload(model.category)]
|
||||
schema = schemas.IssueListOut
|
||||
datas, count = await crud.IssueDal(auth.db).get_datas(
|
||||
**p.dict(),
|
||||
v_options=options,
|
||||
v_schema=schema,
|
||||
v_return_count=True
|
||||
)
|
||||
return SuccessResponse(datas, count=count)
|
||||
|
||||
|
||||
@app.post("/issues", summary="创建问题")
|
||||
async def create_issue(data: schemas.Issue, auth: Auth = Depends(AllUserAuth())):
|
||||
data.create_user_id = auth.user.id
|
||||
return SuccessResponse(await crud.IssueDal(auth.db).create_data(data=data))
|
||||
|
||||
|
||||
@app.delete("/issues", summary="批量删除问题", description="硬删除")
|
||||
async def delete_issues(ids: IdList = Depends(), auth: Auth = Depends(AllUserAuth())):
|
||||
await crud.IssueDal(auth.db).delete_datas(ids=ids.ids, v_soft=False)
|
||||
return SuccessResponse("删除成功")
|
||||
|
||||
|
||||
@app.put("/issues/{data_id}", summary="更新问题信息")
|
||||
async def put_issue(data_id: int, data: schemas.Issue, auth: Auth = Depends(AllUserAuth())):
|
||||
return SuccessResponse(await crud.IssueDal(auth.db).put_data(data_id, data))
|
||||
|
||||
|
||||
@app.get("/issues/{data_id}", summary="获取问题信息")
|
||||
async def get_issue(data_id: int, db: AsyncSession = Depends(db_getter)):
|
||||
schema = schemas.IssueSimpleOut
|
||||
return SuccessResponse(await crud.IssueDal(db).get_data(data_id, v_schema=schema))
|
||||
|
||||
|
||||
@app.get("/issues/add/view/number/{data_id}", summary="更新常见问题查看次数+1")
|
||||
async def issue_add_view_number(data_id: int, db: AsyncSession = Depends(db_getter)):
|
||||
return SuccessResponse(await crud.IssueDal(db).add_view_number(data_id))
|
0
apps/vadmin/record/__init__.py
Normal file
0
apps/vadmin/record/__init__.py
Normal file
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user