完成项目信息管理的迁移
This commit is contained in:
parent
9e14a3256f
commit
4439687870
374
README.md
374
README.md
@ -1,373 +1 @@
|
||||
# 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
|
||||
)
|
||||
```
|
||||
|
||||
基于aicheckv2改写的项目,增加后台管理(用户,部门,权限)
|
@ -38,7 +38,6 @@ 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 *
|
||||
|
@ -47,6 +47,13 @@ ALIYUN_OSS = {
|
||||
"baseUrl": "baseUrl"
|
||||
}
|
||||
|
||||
HUAWEI_OBS = {
|
||||
"AccessKeyID": "HPUAICKSMPQP4XSATCLX",
|
||||
"SecretAccessKey": "DoAL1RWydNwSQRsodS4c34nC0XyXy6TAsetVMasy",
|
||||
"server": "https://obs.cn-north-4.myhuaweicloud.com",
|
||||
"bucketName": "aicheckv2"
|
||||
}
|
||||
|
||||
"""
|
||||
获取IP地址归属地
|
||||
文档:https://user.ip138.com/ip/doc
|
||||
|
@ -46,6 +46,13 @@ ALIYUN_OSS = {
|
||||
"baseUrl": "baseUrl"
|
||||
}
|
||||
|
||||
HUAWEI_OBS = {
|
||||
"AccessKeyID": "HPUAICKSMPQP4XSATCLX",
|
||||
"SecretAccessKey": "DoAL1RWydNwSQRsodS4c34nC0XyXy6TAsetVMasy",
|
||||
"server": "https://obs.cn-north-4.myhuaweicloud.com",
|
||||
"bucketName": "aicheckv2"
|
||||
}
|
||||
|
||||
"""
|
||||
获取IP地址归属地
|
||||
文档:https://user.ip138.com/ip/doc
|
||||
|
@ -9,10 +9,7 @@ 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
|
||||
|
||||
|
||||
@ -22,9 +19,6 @@ urlpatterns = [
|
||||
{"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": ["项目管理"]},
|
||||
]
|
||||
|
@ -9,18 +9,25 @@ 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
|
||||
from utils.huawei_obs import ObsClient
|
||||
from utils import status
|
||||
from core.exception import CustomException
|
||||
if application.settings.DEBUG:
|
||||
from application.config.development import datasets_url, runs_url, detect_url, yolo_url, images_url
|
||||
from application.config.development import datasets_url, runs_url, images_url
|
||||
else:
|
||||
from application.config.production import datasets_url, runs_url, detect_url, yolo_url, images_url
|
||||
from application.config.production import datasets_url, runs_url, images_url
|
||||
|
||||
from typing import Any, List
|
||||
from core.crud import DalBase
|
||||
from fastapi import UploadFile
|
||||
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__()
|
||||
@ -106,15 +113,112 @@ class ProjectInfoDal(DalBase):
|
||||
|
||||
|
||||
class ProjectImageDal(DalBase):
|
||||
"""
|
||||
项目图片
|
||||
"""
|
||||
|
||||
def __init__(self, db: AsyncSession):
|
||||
super(ProjectImageDal, self).__init__()
|
||||
self.db = db
|
||||
self.model = models.ProjectImage
|
||||
self.schema = schemas.ProjectImageSimpleOut
|
||||
self.schema = schemas.ProjectImageOut
|
||||
|
||||
async def img_page(self, param: params.ProjectImageParams):
|
||||
"""
|
||||
分页查询图片信息,然后关联一个图片的标签数量
|
||||
"""
|
||||
subquery = (
|
||||
select(
|
||||
models.ProjectImgLabel.image_id,
|
||||
func.ifnull(func.count(models.ProjectImgLabel.id), 0).label('label_count')
|
||||
)
|
||||
.group_by(models.ProjectImgLabel.image_id)
|
||||
.subquery()
|
||||
)
|
||||
# 2 主查询
|
||||
query = (
|
||||
select(
|
||||
models.ProjectImage,
|
||||
func.ifnull(subquery.c.label_count, 0).label('label_count')
|
||||
)
|
||||
.outerjoin(subquery, models.ProjectImage.id == subquery.c.image_id)
|
||||
)
|
||||
v_where = [models.ProjectImage.project_id == param.project_id, models.ProjectImage.img_type == param.img_type]
|
||||
sql = await self.filter_core(
|
||||
v_start_sql=query,
|
||||
v_where=v_where,
|
||||
v_return_sql=True,
|
||||
v_order=param.v_order,
|
||||
v_order_field=param.v_order_field
|
||||
)
|
||||
count = await self.get_count_sql(sql)
|
||||
if param.limit != 0:
|
||||
sql = sql.offset((param.page - 1) * param.limit).limit(param.limit)
|
||||
queryset = await self.db.execute(sql)
|
||||
result = queryset.all()
|
||||
datas = []
|
||||
for result in result:
|
||||
data = schemas.ProjectImageOut.model_validate(result[0])
|
||||
data.label_count = int(result[1])
|
||||
datas.append(data.model_dump())
|
||||
return datas, count
|
||||
|
||||
async def upload_imgs(self, files: List[UploadFile], pro: schemas.ProjectInfoOut, img_type: str) -> int:
|
||||
"""
|
||||
上传项目图片
|
||||
"""
|
||||
image_models = []
|
||||
for file in files:
|
||||
image = models.ProjectImage()
|
||||
image.project_id = pro.id
|
||||
image.file_name = file.filename
|
||||
image.img_type = img_type
|
||||
# 保存原图
|
||||
path = os.save_images(images_url, pro.project_no, file=file)
|
||||
image.image_url = path
|
||||
# 上传图片到obs
|
||||
object_key = pro.project_no + '/' + img_type + '/' + file.filename
|
||||
success, key, url = ObsClient.put_file(object_key=object_key, file_path=path)
|
||||
if success:
|
||||
image.object_key = object_key
|
||||
image.thumb_image_url = url
|
||||
else:
|
||||
raise CustomException("obs上传失败", code=status.HTTP_ERROR)
|
||||
image_models.append(image)
|
||||
await self.create_datas(datas=image_models)
|
||||
return len(image_models)
|
||||
|
||||
async def check_img_name(self, file_name: str, project_id: int, img_type: str):
|
||||
"""
|
||||
校验相同的项目,相同的文件类型是否有同名的文件
|
||||
"""
|
||||
count = await self.get_count(v_where=[
|
||||
models.ProjectImage.file_name == file_name,
|
||||
models.ProjectImage.project_id == project_id,
|
||||
models.ProjectImage.img_type == img_type
|
||||
])
|
||||
return count > 0
|
||||
|
||||
async def del_img(self, ids: List[int]):
|
||||
"""
|
||||
删除图片,删除数据库数据,删除本地的文件,删除obs中的文件
|
||||
"""
|
||||
file_urls = []
|
||||
object_keys = []
|
||||
for img_id in ids:
|
||||
image = self.get_data(data_id=img_id)
|
||||
if image:
|
||||
file_urls.append(image.image_url)
|
||||
object_keys.append(image.object_key)
|
||||
os.delete_file_if_exists(**file_urls)
|
||||
ObsClient.del_objects(object_keys)
|
||||
await self.delete_datas(ids)
|
||||
|
||||
|
||||
class ProjectLabelDal(DalBase):
|
||||
"""
|
||||
项目标签
|
||||
"""
|
||||
|
||||
def __init__(self, db: AsyncSession):
|
||||
super(ProjectLabelDal, self).__init__()
|
||||
@ -139,18 +243,37 @@ class ProjectLabelDal(DalBase):
|
||||
|
||||
|
||||
class ProjectImgLabelDal(DalBase):
|
||||
|
||||
"""
|
||||
图片标签信息
|
||||
"""
|
||||
def __init__(self, db: AsyncSession):
|
||||
super(ProjectImgLabelDal, self).__init__()
|
||||
self.db = db
|
||||
self.model = models.ProjectImgLabel
|
||||
self.schema = schemas.ProjectImgLabelSimpleOut
|
||||
|
||||
async def add_img_label(self, img_label_in: schemas.ProjectImgLeaferLabel):
|
||||
# 先把历史数据都删掉,然后再保存
|
||||
image_id = img_label_in.image_id
|
||||
await self.delete_datas(image_id=image_id)
|
||||
img_labels = [self.model(**i.model_dump()) for i in img_label_in.label_infos]
|
||||
for img in img_labels:
|
||||
img.image_id = image_id
|
||||
await self.create_datas(img_labels)
|
||||
|
||||
|
||||
class ProjectImgLeaferDal(DalBase):
|
||||
"""
|
||||
图片标注信息-leafer.js
|
||||
"""
|
||||
|
||||
def __init__(self, db: AsyncSession):
|
||||
super(ProjectImgLeaferDal, self).__init__()
|
||||
self.db = db
|
||||
self.model = models.ProjectImgLeafer
|
||||
self.schema = schemas.ProjectImgLeaferSimpleOut
|
||||
self.schema = schemas.ProjectImgLeaferOut
|
||||
|
||||
async def add_leafer(self, img_label_in: schemas.ProjectImgLeaferLabel):
|
||||
# 先把历史数据都删掉,然后再保存
|
||||
image_id = img_label_in.image_id
|
||||
await self.delete_datas(image_id=image_id)
|
||||
await self.create_data(data=self.model(**img_label_in.model_dump()))
|
||||
|
@ -43,6 +43,7 @@ class ProjectImage(BaseModel):
|
||||
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)
|
||||
object_key: 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)
|
||||
|
||||
|
@ -15,11 +15,9 @@ class ProjectImageParams(QueryParams):
|
||||
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
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
from .project_info import ProjectInfoIn, ProjectInfoOut, ProjectInfoPagerOut
|
||||
from .project_image import ProjectImage, ProjectImagePager, ProjectImageOut
|
||||
from .project_image import ProjectImageOut, ProjectImage
|
||||
from .project_label import ProjectLabel
|
||||
from .project_img_label import ProjectImgLabelIn
|
||||
from .project_img_leafer import ProjectImgLeaferLabel, ProjectImgLeaferOut
|
||||
|
@ -14,8 +14,8 @@ 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="文件名称")
|
||||
thumb_image_url: Optional[str] = Field(None, description="图片在obs上的链接")
|
||||
create_time: DatetimeStr
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
@ -25,14 +25,8 @@ class ProjectImageOut(BaseModel):
|
||||
id: Optional[int] = Field(None, description="id")
|
||||
project_id: Optional[int] = Field(..., description="项目id")
|
||||
file_name: Optional[str] = Field(None, description="文件名称")
|
||||
thumb_image_url: Optional[str] = Field(None, description="图片在obs上的链接")
|
||||
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="每页数量")
|
||||
|
@ -5,12 +5,11 @@
|
||||
# @File : project_img_leafer.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : pydantic 模型,用于数据库序列化操作
|
||||
from . import ProjectImgLabelIn
|
||||
|
||||
from pydantic import BaseModel, Field, ConfigDict
|
||||
from typing import Optional, List
|
||||
|
||||
from . import ProjectImgLabelIn
|
||||
|
||||
|
||||
class ProjectImgLeaferLabel(BaseModel):
|
||||
image_id: Optional[int] = Field(..., description="图片id")
|
||||
|
@ -4,12 +4,13 @@
|
||||
# @Create Time : 2025/04/03 10:25
|
||||
# @File : views.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : 路由,视图文件
|
||||
# @desc : 路由,项目信息管理,包括项目主体,项目图片,项目标签和图片的标注信息
|
||||
from utils.response import SuccessResponse, ErrorResponse
|
||||
from . import params, schemas, crud, models
|
||||
from core.dependencies import IdList
|
||||
|
||||
from fastapi import APIRouter, Depends
|
||||
from typing import List
|
||||
from fastapi import APIRouter, Depends, UploadFile, File, Form
|
||||
from apps.vadmin.auth.utils.current import FullAdminAuth
|
||||
from apps.vadmin.auth.utils.validation.auth import Auth
|
||||
|
||||
@ -42,7 +43,7 @@ async def add_project(
|
||||
|
||||
|
||||
@app.get("/info/{pro_id}", summary="查询项目信息")
|
||||
async def project(
|
||||
async def project_info(
|
||||
pro_id: int,
|
||||
auth: Auth = Depends(FullAdminAuth())
|
||||
):
|
||||
@ -98,10 +99,64 @@ async def delete_label(
|
||||
label_ids: IdList = Depends(),
|
||||
auth: Auth = Depends(FullAdminAuth())
|
||||
):
|
||||
# 删除标签信息,然后删除图片标签关联表
|
||||
await crud.ProjectLabelDal(auth.db).delete_datas(label_ids.ids)
|
||||
for label_id in label_ids.ids:
|
||||
await crud.ProjectImgLabelDal(auth.db).delete_datas(label_id=label_id)
|
||||
return SuccessResponse(msg="删除成功")
|
||||
|
||||
|
||||
@app.get("/img", summary="获取图片列表,分两种情况,一个带分页的,一个不带分页的")
|
||||
async def project_pager(
|
||||
param: params.ProjectImageParams = Depends(),
|
||||
auth: Auth = Depends(FullAdminAuth())
|
||||
):
|
||||
if param.limit:
|
||||
# 分页查询,关联一个标签数量
|
||||
datas, count = await crud.ProjectImageDal(auth.db).img_page(param)
|
||||
return SuccessResponse(data=datas, count=count)
|
||||
else:
|
||||
# 直接查询
|
||||
datas = await crud.ProjectImageDal(auth.db).get_datas(v_schema=schemas.ProjectImage, **param.dict())
|
||||
return SuccessResponse(data=datas)
|
||||
|
||||
|
||||
@app.post("/img", summary="上传图片")
|
||||
async def up_img(
|
||||
project_id: int = Form(...),
|
||||
files: List[UploadFile] = File(...),
|
||||
img_type: str = Form(...),
|
||||
auth: Auth = Depends(FullAdminAuth())
|
||||
):
|
||||
pro = await crud.ProjectInfoDal(auth.db).get_data(data_id=project_id)
|
||||
if pro is None:
|
||||
return ErrorResponse(msg="项目查询失败,请稍后再试")
|
||||
count = await crud.ProjectImageDal(auth.db).upload_imgs(files=files, pro=pro, img_type=img_type)
|
||||
if count > 0:
|
||||
return SuccessResponse(msg="上传成功")
|
||||
|
||||
|
||||
@app.delete("/img", summary="删除照片")
|
||||
async def del_img(
|
||||
img_id: IdList = Depends(),
|
||||
auth: Auth = Depends(FullAdminAuth())
|
||||
):
|
||||
await crud.ProjectImageDal(auth.db).del_img(img_id.ids)
|
||||
return SuccessResponse(msg="删除成功")
|
||||
|
||||
|
||||
@app.post("/img_label", summary="保存图片的标注信息")
|
||||
async def add_img_label(
|
||||
img_label_in: schemas.ProjectImgLeaferLabel,
|
||||
auth: Auth = Depends(FullAdminAuth())
|
||||
):
|
||||
await crud.ProjectImgLabelDal(auth.db).add_img_label(img_label_in)
|
||||
await crud.ProjectImgLeaferDal(auth.db).add_leafer(img_label_in)
|
||||
return SuccessResponse(msg="保存成功")
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -1,84 +0,0 @@
|
||||
#!/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)
|
@ -18,7 +18,7 @@ from sqlalchemy import select, false, and_
|
||||
from core.crud import DalBase
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from core.validator import vali_telephone
|
||||
from utils.file.aliyun_oss import AliyunOSS, BucketConf
|
||||
from utils.huawei_obs import ObsClient
|
||||
from utils.excel.import_manage import ImportManage, FieldType
|
||||
from utils.excel.write_xlsx import WriteXlsx
|
||||
from utils.send_email import EmailSender
|
||||
@ -397,10 +397,10 @@ class UserDal(DalBase):
|
||||
:param file:
|
||||
:return:
|
||||
"""
|
||||
result = await AliyunOSS(BucketConf(**settings.ALIYUN_OSS)).upload_image("avatar", file)
|
||||
user.avatar = result
|
||||
success, key, url = await ObsClient().put_object("avatar"+"/"+user.name+file.filename, file)
|
||||
user.avatar = url
|
||||
await self.flush(user)
|
||||
return result
|
||||
return url
|
||||
|
||||
async def update_wx_server_openid(self, code: str, user: models.VadminUser, redis: Redis) -> bool:
|
||||
"""
|
||||
|
@ -1,20 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2023/8/25 13:15
|
||||
# @File : crud.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : 简要说明
|
||||
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from core.crud import DalBase
|
||||
from . import models, schemas
|
||||
|
||||
|
||||
class ImagesDal(DalBase):
|
||||
|
||||
def __init__(self, db: AsyncSession):
|
||||
super(ImagesDal, self).__init__()
|
||||
self.db = db
|
||||
self.model = models.VadminImages
|
||||
self.schema = schemas.ImagesSimpleOut
|
@ -1 +0,0 @@
|
||||
from .images import VadminImages
|
@ -1,27 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2023/8/25 13:41
|
||||
# @File : images.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, ForeignKey, Integer
|
||||
|
||||
|
||||
class VadminImages(BaseModel):
|
||||
__tablename__ = "vadmin_resource_images"
|
||||
__table_args__ = ({'comment': '图片素材表'})
|
||||
|
||||
filename: Mapped[str] = mapped_column(String(255), nullable=False, comment="原图片名称")
|
||||
image_url: Mapped[str] = mapped_column(String(500), nullable=False, 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)
|
@ -1 +0,0 @@
|
||||
from .images import ImagesParams
|
@ -1,27 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2023/8/25 14:59
|
||||
# @File : images.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : 简要说明
|
||||
|
||||
|
||||
from fastapi import Depends
|
||||
from core.dependencies import Paging, QueryParams
|
||||
|
||||
|
||||
class ImagesParams(QueryParams):
|
||||
"""
|
||||
列表分页
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
filename: str = None,
|
||||
params: Paging = Depends()
|
||||
):
|
||||
super().__init__(params)
|
||||
self.filename = ('like', filename)
|
||||
self.v_order = "desc"
|
||||
self.v_order_field = "create_datetime"
|
@ -1 +0,0 @@
|
||||
from .images import Images, ImagesOut, ImagesSimpleOut
|
@ -1,33 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2023/8/25 14:49
|
||||
# @File : images.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : 简要说明
|
||||
|
||||
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
from core.data_types import DatetimeStr
|
||||
from apps.vadmin.auth.schemas import UserSimpleOut
|
||||
|
||||
|
||||
class Images(BaseModel):
|
||||
filename: str
|
||||
image_url: str
|
||||
|
||||
create_user_id: int
|
||||
|
||||
|
||||
class ImagesSimpleOut(Images):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: int
|
||||
create_datetime: DatetimeStr
|
||||
update_datetime: DatetimeStr
|
||||
|
||||
|
||||
class ImagesOut(ImagesSimpleOut):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
create_user: UserSimpleOut
|
@ -1,60 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2023/8/25 9:29
|
||||
# @File : views.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : 简要说明
|
||||
|
||||
from fastapi import APIRouter, Depends, UploadFile
|
||||
from sqlalchemy.orm import joinedload
|
||||
from core.dependencies import IdList
|
||||
from utils.file.aliyun_oss import AliyunOSS, BucketConf
|
||||
from utils.response import SuccessResponse
|
||||
from . import schemas, crud, params, models
|
||||
from apps.vadmin.auth.utils.current import FullAdminAuth
|
||||
from apps.vadmin.auth.utils.validation.auth import Auth
|
||||
from application.settings import ALIYUN_OSS
|
||||
|
||||
app = APIRouter()
|
||||
|
||||
|
||||
###########################################################
|
||||
# 图片资源管理
|
||||
###########################################################
|
||||
@app.get("/images", summary="获取图片列表")
|
||||
async def get_images_list(p: params.ImagesParams = Depends(), auth: Auth = Depends(FullAdminAuth())):
|
||||
model = models.VadminImages
|
||||
v_options = [joinedload(model.create_user)]
|
||||
v_schema = schemas.ImagesOut
|
||||
datas, count = await crud.ImagesDal(auth.db).get_datas(
|
||||
**p.dict(),
|
||||
v_options=v_options,
|
||||
v_schema=v_schema,
|
||||
v_return_count=True
|
||||
)
|
||||
return SuccessResponse(datas, count=count)
|
||||
|
||||
|
||||
@app.post("/images", summary="创建图片")
|
||||
async def create_images(file: UploadFile, auth: Auth = Depends(FullAdminAuth())):
|
||||
filepath = f"/resource/images/"
|
||||
result = await AliyunOSS(BucketConf(**ALIYUN_OSS)).upload_image(filepath, file)
|
||||
data = schemas.Images(
|
||||
filename=file.filename,
|
||||
image_url=result,
|
||||
create_user_id=auth.user.id
|
||||
)
|
||||
|
||||
return SuccessResponse(await crud.ImagesDal(auth.db).create_data(data=data))
|
||||
|
||||
|
||||
@app.delete("/images", summary="删除图片", description="硬删除")
|
||||
async def delete_images(ids: IdList = Depends(), auth: Auth = Depends(FullAdminAuth())):
|
||||
await crud.ImagesDal(auth.db).delete_datas(ids.ids, v_soft=False)
|
||||
return SuccessResponse("删除成功")
|
||||
|
||||
|
||||
@app.get("/images/{data_id}", summary="获取图片信息")
|
||||
async def get_images(data_id: int, auth: Auth = Depends(FullAdminAuth())):
|
||||
return SuccessResponse(await crud.ImagesDal(auth.db).get_data(data_id, v_schema=schemas.ImagesSimpleOut))
|
@ -7,24 +7,20 @@
|
||||
# @desc : 数据库 增删改查操作
|
||||
|
||||
import json
|
||||
import os
|
||||
from enum import Enum
|
||||
from typing import Any
|
||||
from redis.asyncio import Redis
|
||||
from fastapi.encoders import jsonable_encoder
|
||||
from motor.motor_asyncio import AsyncIOMotorDatabase
|
||||
from sqlalchemy import select, update
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy.orm import joinedload
|
||||
from application.settings import STATIC_ROOT, SUBSCRIBE, REDIS_DB_ENABLE
|
||||
from core.database import redis_getter
|
||||
from application.settings import SUBSCRIBE
|
||||
from core.mongo_manage import MongoManage
|
||||
from utils.file.file_manage import FileManage
|
||||
from . import models, schemas
|
||||
from core.crud import DalBase
|
||||
from core.exception import CustomException
|
||||
from utils import status
|
||||
from fastapi import Request
|
||||
|
||||
|
||||
class DictTypeDal(DalBase):
|
||||
@ -71,121 +67,6 @@ class DictDetailsDal(DalBase):
|
||||
self.schema = schemas.DictDetailsSimpleOut
|
||||
|
||||
|
||||
class SettingsDal(DalBase):
|
||||
|
||||
def __init__(self, db: AsyncSession):
|
||||
super(SettingsDal, self).__init__()
|
||||
self.db = db
|
||||
self.model = models.VadminSystemSettings
|
||||
self.schema = schemas.SettingsSimpleOut
|
||||
|
||||
async def get_tab_values(self, tab_id: int) -> dict:
|
||||
"""
|
||||
获取系统配置标签下的信息
|
||||
"""
|
||||
datas = await self.get_datas(limit=0, tab_id=tab_id, v_return_objs=True)
|
||||
result = {}
|
||||
for data in datas:
|
||||
if not data.disabled:
|
||||
result[data.config_key] = data.config_value
|
||||
return result
|
||||
|
||||
async def update_datas(self, datas: dict, request: Request) -> None:
|
||||
"""
|
||||
更新系统配置信息
|
||||
|
||||
更新ico图标步骤:先将文件上传到本地,然后点击提交后,获取到文件地址,将上传的新文件覆盖原有文件
|
||||
原因:ico图标的路径是在前端的index.html中固定的,所以目前只能改变图片,不改变路径
|
||||
"""
|
||||
for key, value in datas.items():
|
||||
if key == "web_ico":
|
||||
continue
|
||||
elif key == "web_ico_local_path":
|
||||
if not value:
|
||||
continue
|
||||
ico = await self.get_data(config_key="web_ico", tab_id=1)
|
||||
web_ico = datas.get("web_ico")
|
||||
if ico.config_value == web_ico:
|
||||
continue
|
||||
# 将上传的ico路径替换到static/system/favicon.ico文件
|
||||
await FileManage.async_copy_file(value, os.path.join(STATIC_ROOT, "system/favicon.ico"))
|
||||
sql = update(self.model).where(self.model.config_key == "web_ico").values(config_value=web_ico)
|
||||
await self.db.execute(sql)
|
||||
else:
|
||||
sql = update(self.model).where(self.model.config_key == str(key)).values(config_value=value)
|
||||
await self.db.execute(sql)
|
||||
if "wx_server_app_id" in datas and REDIS_DB_ENABLE:
|
||||
rd = redis_getter(request)
|
||||
await rd.client().set("wx_server", json.dumps(datas))
|
||||
elif "sms_access_key" in datas and REDIS_DB_ENABLE:
|
||||
rd = redis_getter(request)
|
||||
await rd.client().set('aliyun_sms', json.dumps(datas))
|
||||
|
||||
async def get_base_config(self) -> dict:
|
||||
"""
|
||||
获取系统基本信息
|
||||
"""
|
||||
ignore_configs = ["wx_server_app_id", "wx_server_app_secret"]
|
||||
datas = await self.get_datas(limit=0, tab_id=("in", ["1", "9"]), disabled=False, v_return_objs=True)
|
||||
result = {}
|
||||
for config in datas:
|
||||
if config.config_key not in ignore_configs:
|
||||
result[config.config_key] = config.config_value
|
||||
return result
|
||||
|
||||
|
||||
class SettingsTabDal(DalBase):
|
||||
|
||||
def __init__(self, db: AsyncSession):
|
||||
super(SettingsTabDal, self).__init__(db, models.VadminSystemSettingsTab, schemas.SettingsTabSimpleOut)
|
||||
|
||||
async def get_classify_tab_values(self, classify: list[str], hidden: bool | None = False) -> dict:
|
||||
"""
|
||||
获取系统配置分类下的标签信息
|
||||
"""
|
||||
model = models.VadminSystemSettingsTab
|
||||
options = [joinedload(model.settings)]
|
||||
datas = await self.get_datas(
|
||||
limit=0,
|
||||
v_options=options,
|
||||
classify=("in", classify),
|
||||
disabled=False,
|
||||
v_return_objs=True,
|
||||
hidden=hidden
|
||||
)
|
||||
return self.__generate_values(datas)
|
||||
|
||||
async def get_tab_name_values(self, tab_names: list[str], hidden: bool | None = False) -> dict:
|
||||
"""
|
||||
获取系统配置标签下的标签信息
|
||||
"""
|
||||
model = models.VadminSystemSettingsTab
|
||||
options = [joinedload(model.settings)]
|
||||
datas = await self.get_datas(
|
||||
limit=0,
|
||||
v_options=options,
|
||||
tab_name=("in", tab_names),
|
||||
disabled=False,
|
||||
v_return_objs=True,
|
||||
hidden=hidden
|
||||
)
|
||||
return self.__generate_values(datas)
|
||||
|
||||
@classmethod
|
||||
def __generate_values(cls, datas: list[models.VadminSystemSettingsTab]) -> dict:
|
||||
"""
|
||||
生成字典值
|
||||
"""
|
||||
return {
|
||||
tab.tab_name: {
|
||||
item.config_key: item.config_value
|
||||
for item in tab.settings
|
||||
if not item.disabled
|
||||
}
|
||||
for tab in datas
|
||||
}
|
||||
|
||||
|
||||
class TaskDal(MongoManage):
|
||||
|
||||
class JobOperation(Enum):
|
||||
|
@ -1,2 +1 @@
|
||||
from .dict import VadminDictType, VadminDictDetails
|
||||
from .settings import VadminSystemSettings, VadminSystemSettingsTab
|
||||
|
@ -1,42 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2022/7/7 13:41
|
||||
# @File : settings.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : 系统字典模型
|
||||
from sqlalchemy.orm import relationship, Mapped, mapped_column
|
||||
from db.db_base import BaseModel
|
||||
from sqlalchemy import String, Integer, ForeignKey, Boolean, Text
|
||||
|
||||
|
||||
class VadminSystemSettingsTab(BaseModel):
|
||||
__tablename__ = "vadmin_system_settings_tab"
|
||||
__table_args__ = ({'comment': '系统配置分类表'})
|
||||
|
||||
title: Mapped[str] = mapped_column(String(255), comment="标题")
|
||||
classify: Mapped[str] = mapped_column(String(255), index=True, nullable=False, comment="分类键")
|
||||
tab_label: Mapped[str] = mapped_column(String(255), comment="tab标题")
|
||||
tab_name: Mapped[str] = mapped_column(String(255), index=True, nullable=False, unique=True, comment="tab标识符")
|
||||
hidden: Mapped[bool] = mapped_column(Boolean, default=False, comment="是否隐藏")
|
||||
disabled: Mapped[bool] = mapped_column(Boolean, default=False, comment="是否禁用")
|
||||
|
||||
settings: Mapped[list["VadminSystemSettings"]] = relationship(back_populates="tab")
|
||||
|
||||
|
||||
class VadminSystemSettings(BaseModel):
|
||||
__tablename__ = "vadmin_system_settings"
|
||||
__table_args__ = ({'comment': '系统配置表'})
|
||||
|
||||
config_label: Mapped[str] = mapped_column(String(255), comment="配置表标签")
|
||||
config_key: Mapped[str] = mapped_column(String(255), index=True, nullable=False, unique=True, comment="配置表键")
|
||||
config_value: Mapped[str | None] = mapped_column(Text, comment="配置表内容")
|
||||
remark: Mapped[str | None] = mapped_column(String(255), comment="备注信息")
|
||||
disabled: Mapped[bool] = mapped_column(Boolean, default=False, comment="是否禁用")
|
||||
|
||||
tab_id: Mapped[int] = mapped_column(
|
||||
Integer,
|
||||
ForeignKey("vadmin_system_settings_tab.id", ondelete='CASCADE'),
|
||||
comment="关联tab标签"
|
||||
)
|
||||
tab: Mapped[VadminSystemSettingsTab] = relationship(foreign_keys=tab_id, back_populates="settings")
|
@ -1,4 +1,2 @@
|
||||
from .dict import DictType, DictDetails, DictTypeSimpleOut, DictDetailsSimpleOut, DictTypeOptionsOut
|
||||
from .settings_tab import SettingsTab, SettingsTabSimpleOut
|
||||
from .settings import Settings, SettingsSimpleOut
|
||||
from .task import Task, TaskSimpleOut
|
||||
|
@ -1,29 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2021/10/18 22:19
|
||||
# @File : settings.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : pydantic 模型,用于数据库序列化操作
|
||||
|
||||
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
from core.data_types import DatetimeStr
|
||||
|
||||
|
||||
class Settings(BaseModel):
|
||||
config_label: str | None = None
|
||||
config_key: str
|
||||
config_value: str | None = None
|
||||
remark: str | None = None
|
||||
disabled: bool | None = None
|
||||
tab_id: int
|
||||
|
||||
|
||||
class SettingsSimpleOut(Settings):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: int
|
||||
create_datetime: DatetimeStr
|
||||
update_datetime: DatetimeStr
|
||||
|
@ -1,28 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2021/10/18 22:19
|
||||
# @File : settings_tab.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : pydantic 模型,用于数据库序列化操作
|
||||
|
||||
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
from core.data_types import DatetimeStr
|
||||
|
||||
|
||||
class SettingsTab(BaseModel):
|
||||
title: str
|
||||
classify: str
|
||||
tab_label: str
|
||||
tab_name: str
|
||||
hidden: bool
|
||||
|
||||
|
||||
class SettingsTabSimpleOut(SettingsTab):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: int
|
||||
create_datetime: DatetimeStr
|
||||
update_datetime: DatetimeStr
|
||||
|
@ -12,6 +12,7 @@ from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from application.settings import ALIYUN_OSS
|
||||
from core.database import db_getter, redis_getter, mongo_getter
|
||||
from utils.file.aliyun_oss import AliyunOSS, BucketConf
|
||||
from utils.huawei_obs import ObsClient
|
||||
from utils.file.file_manage import FileManage
|
||||
from utils.response import SuccessResponse, ErrorResponse
|
||||
from utils.sms.code import CodeSMS
|
||||
@ -102,34 +103,6 @@ async def get_dict_detail(data_id: int, auth: Auth = Depends(AllUserAuth())):
|
||||
return SuccessResponse(await crud.DictDetailsDal(auth.db).get_data(data_id, v_schema=schema))
|
||||
|
||||
|
||||
###########################################################
|
||||
# 文件上传管理
|
||||
###########################################################
|
||||
@app.post("/upload/image/to/oss", summary="上传图片到阿里云OSS")
|
||||
async def upload_image_to_oss(file: UploadFile, path: str = Form(...)):
|
||||
result = await AliyunOSS(BucketConf(**ALIYUN_OSS)).upload_image(path, file)
|
||||
return SuccessResponse(result)
|
||||
|
||||
|
||||
@app.post("/upload/video/to/oss", summary="上传视频到阿里云OSS")
|
||||
async def upload_video_to_oss(file: UploadFile, path: str = Form(...)):
|
||||
result = await AliyunOSS(BucketConf(**ALIYUN_OSS)).upload_video(path, file)
|
||||
return SuccessResponse(result)
|
||||
|
||||
|
||||
@app.post("/upload/file/to/oss", summary="上传文件到阿里云OSS")
|
||||
async def upload_file_to_oss(file: UploadFile, path: str = Form(...)):
|
||||
result = await AliyunOSS(BucketConf(**ALIYUN_OSS)).upload_file(path, file)
|
||||
return SuccessResponse(result)
|
||||
|
||||
|
||||
@app.post("/upload/image/to/local", summary="上传图片到本地")
|
||||
async def upload_image_to_local(file: UploadFile, path: str = Form(...)):
|
||||
manage = FileManage(file, path)
|
||||
path = await manage.save_image_local()
|
||||
return SuccessResponse(path)
|
||||
|
||||
|
||||
###########################################################
|
||||
# 短信服务管理
|
||||
###########################################################
|
||||
@ -142,43 +115,6 @@ async def sms_send(telephone: str, rd: Redis = Depends(redis_getter), auth: Auth
|
||||
return SuccessResponse(await sms.main_async())
|
||||
|
||||
|
||||
###########################################################
|
||||
# 系统配置管理
|
||||
###########################################################
|
||||
@app.post("/settings/tabs", summary="获取系统配置标签列表")
|
||||
async def get_settings_tabs(classifys: list[str] = Body(...), auth: Auth = Depends(FullAdminAuth())):
|
||||
return SuccessResponse(await crud.SettingsTabDal(auth.db).get_datas(limit=0, classify=("in", classifys)))
|
||||
|
||||
|
||||
@app.get("/settings/tabs/values", summary="获取系统配置标签下的信息")
|
||||
async def get_settings_tabs_values(tab_id: int, auth: Auth = Depends(FullAdminAuth())):
|
||||
return SuccessResponse(await crud.SettingsDal(auth.db).get_tab_values(tab_id=tab_id))
|
||||
|
||||
|
||||
@app.put("/settings/tabs/values", summary="更新系统配置信息")
|
||||
async def put_settings_tabs_values(
|
||||
request: Request,
|
||||
datas: dict = Body(...),
|
||||
auth: Auth = Depends(FullAdminAuth())
|
||||
):
|
||||
return SuccessResponse(await crud.SettingsDal(auth.db).update_datas(datas, request))
|
||||
|
||||
|
||||
@app.get("/settings/base/config", summary="获取系统基础配置", description="每次进入系统中时使用")
|
||||
async def get_setting_base_config(db: AsyncSession = Depends(db_getter)):
|
||||
return SuccessResponse(await crud.SettingsDal(db).get_base_config())
|
||||
|
||||
|
||||
@app.get("/settings/privacy", summary="获取隐私协议")
|
||||
async def get_settings_privacy(auth: Auth = Depends(OpenAuth())):
|
||||
return SuccessResponse((await crud.SettingsDal(auth.db).get_data(config_key="web_privacy")).config_value)
|
||||
|
||||
|
||||
@app.get("/settings/agreement", summary="获取用户协议")
|
||||
async def get_settings_agreement(auth: Auth = Depends(OpenAuth())):
|
||||
return SuccessResponse((await crud.SettingsDal(auth.db).get_data(config_key="web_agreement")).config_value)
|
||||
|
||||
|
||||
###########################################################
|
||||
# 定时任务管理
|
||||
###########################################################
|
||||
|
@ -1,7 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2022/10/19 15:41
|
||||
# @File : __init__.py.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : 简要说明
|
@ -1,159 +0,0 @@
|
||||
#!/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 apps.vadmin.auth.utils.validation.auth import Auth
|
||||
from utils.response import SuccessResponse
|
||||
import datetime
|
||||
from apps.vadmin.record.crud import LoginRecordDal
|
||||
|
||||
app = APIRouter()
|
||||
|
||||
|
||||
###########################################################
|
||||
# 工作区管理
|
||||
###########################################################
|
||||
@app.get("/project", summary="获取项目")
|
||||
async def get_project():
|
||||
data = [
|
||||
{
|
||||
"name": 'Mysql',
|
||||
"icon": 'vscode-icons:file-type-mysql',
|
||||
"message": '最流行的关系型数据库管理系统',
|
||||
"personal": 'kinit',
|
||||
"link": "https://www.mysql.com/",
|
||||
"time": datetime.datetime.now().strftime("%Y-%m-%d")
|
||||
},
|
||||
{
|
||||
"name": 'FastAPI',
|
||||
"icon": 'simple-icons:fastapi',
|
||||
"message": '一个现代、快速(高性能)的 web 框架',
|
||||
"personal": 'kinit',
|
||||
"link": "https://fastapi.tiangolo.com/zh/",
|
||||
"time": datetime.datetime.now().strftime("%Y-%m-%d")
|
||||
},
|
||||
{
|
||||
"name": 'Vue',
|
||||
"icon": 'logos:vue',
|
||||
"message": '渐进式 JavaScript 框架',
|
||||
"personal": 'kinit',
|
||||
"link": "https://cn.vuejs.org/",
|
||||
"time": datetime.datetime.now().strftime("%Y-%m-%d")
|
||||
},
|
||||
{
|
||||
"name": 'Element-plus',
|
||||
"icon": 'logos:element',
|
||||
"message": '面向设计师和开发者的组件库',
|
||||
"personal": 'kinit',
|
||||
"link": "https://element-plus.org/zh-CN/",
|
||||
"time": datetime.datetime.now().strftime("%Y-%m-%d")
|
||||
},
|
||||
{
|
||||
"name": 'Typescript',
|
||||
"icon": 'vscode-icons:file-type-typescript-official',
|
||||
"message": 'TypeScript是JavaScript类型的超集',
|
||||
"personal": 'kinit',
|
||||
"link": "https://www.typescriptlang.org/",
|
||||
"time": datetime.datetime.now().strftime("%Y-%m-%d")
|
||||
},
|
||||
{
|
||||
"name": 'Vite',
|
||||
"icon": 'vscode-icons:file-type-vite',
|
||||
"message": 'Vite 下一代的前端工具链',
|
||||
"personal": 'kinit',
|
||||
"link": "https://cn.vitejs.dev/",
|
||||
"time": datetime.datetime.now().strftime("%Y-%m-%d")
|
||||
}
|
||||
]
|
||||
return SuccessResponse(data)
|
||||
|
||||
|
||||
@app.get("/dynamic", summary="获取动态")
|
||||
async def get_dynamic():
|
||||
data = [
|
||||
{
|
||||
"keys": ['workplace.push', 'Github'],
|
||||
"time": datetime.datetime.now().strftime("%Y-%m-%d")
|
||||
},
|
||||
{
|
||||
"keys": ['workplace.push', 'Github'],
|
||||
"time": datetime.datetime.now().strftime("%Y-%m-%d")
|
||||
}
|
||||
]
|
||||
return SuccessResponse(data)
|
||||
|
||||
|
||||
@app.get("/team", summary="获取团队信息")
|
||||
async def get_team():
|
||||
data = [
|
||||
{
|
||||
"name": 'Mysql',
|
||||
"icon": 'vscode-icons:file-type-mysql'
|
||||
},
|
||||
{
|
||||
"name": 'Vue',
|
||||
"icon": 'logos:vue'
|
||||
},
|
||||
{
|
||||
"name": 'Element-plus',
|
||||
"icon": 'logos:element'
|
||||
},
|
||||
{
|
||||
"name": 'Fastapi',
|
||||
"icon": 'simple-icons:fastapi'
|
||||
},
|
||||
{
|
||||
"name": 'Typescript',
|
||||
"icon": 'vscode-icons:file-type-typescript-official'
|
||||
},
|
||||
{
|
||||
"name": 'Vite',
|
||||
"icon": 'vscode-icons:file-type-vite'
|
||||
}
|
||||
]
|
||||
return SuccessResponse(data)
|
||||
|
||||
|
||||
@app.get("/shortcuts", summary="获取快捷操作")
|
||||
async def get_shortcuts():
|
||||
data = [
|
||||
{
|
||||
"name": "Gitee 项目仓库",
|
||||
"link": "https://gitee.com/ktianc/kinit"
|
||||
},
|
||||
{
|
||||
"name": "GitHub 项目仓库",
|
||||
"link": "https://github.com/vvandk/kinit"
|
||||
},
|
||||
{
|
||||
"name": "前端文档",
|
||||
"link": "https://element-plus-admin-doc.cn/"
|
||||
},
|
||||
{
|
||||
"name": "Swagger UI 接口文档",
|
||||
"link": "http://kinit.ktianc.top/api/docs"
|
||||
},
|
||||
{
|
||||
"name": "Redoc 接口文档",
|
||||
"link": "http://kinit.ktianc.top/api/redoc"
|
||||
},
|
||||
{
|
||||
"name": "UnoCSS 中文文档",
|
||||
"link": "https://unocss.nodejs.cn/guide/"
|
||||
},
|
||||
{
|
||||
"name": "Iconify 文档",
|
||||
"link": "https://icon-sets.iconify.design/"
|
||||
},
|
||||
{
|
||||
"name": "echarts 文档",
|
||||
"link": "https://echarts.apache.org/zh/index.html"
|
||||
},
|
||||
]
|
||||
return SuccessResponse(data)
|
@ -10,13 +10,10 @@
|
||||
from fastapi import FastAPI
|
||||
from motor.motor_asyncio import AsyncIOMotorClient
|
||||
from application.settings import REDIS_DB_URL, MONGO_DB_URL, MONGO_DB_NAME, EVENTS
|
||||
from utils.cache import Cache
|
||||
from redis import asyncio as aioredis
|
||||
from redis.exceptions import AuthenticationError, TimeoutError, RedisError
|
||||
from contextlib import asynccontextmanager
|
||||
from utils.tools import import_modules_async
|
||||
from sqlalchemy.exc import ProgrammingError
|
||||
from core.logger import logger
|
||||
|
||||
|
||||
@asynccontextmanager
|
||||
@ -79,11 +76,6 @@ async def connect_redis(app: FastAPI, status: bool):
|
||||
raise TimeoutError(f"Redis 连接超时,地址或者端口错误: {e}")
|
||||
except RedisError as e:
|
||||
raise RedisError(f"Redis 连接失败: {e}")
|
||||
try:
|
||||
await Cache(app.state.redis).cache_tab_names()
|
||||
except ProgrammingError as e:
|
||||
logger.error(f"sqlalchemy.exc.ProgrammingError: {e}")
|
||||
print(f"sqlalchemy.exc.ProgrammingError: {e}")
|
||||
else:
|
||||
print("Redis 连接关闭")
|
||||
await app.state.redis.close()
|
||||
|
BIN
requirements.txt
BIN
requirements.txt
Binary file not shown.
Binary file not shown.
@ -1,89 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2022/3/21 11:03
|
||||
# @File : cache.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : 缓存
|
||||
|
||||
from typing import List
|
||||
|
||||
from sqlalchemy import false
|
||||
from sqlalchemy.future import select
|
||||
from sqlalchemy.orm import joinedload
|
||||
from core.logger import logger # 注意:报错就在这里,如果只写 core.logger 会写入日志报错,很难排查
|
||||
from core.database import db_getter
|
||||
from apps.vadmin.system.models import VadminSystemSettingsTab
|
||||
import json
|
||||
from redis.asyncio.client import Redis
|
||||
from core.exception import CustomException
|
||||
from utils import status
|
||||
|
||||
|
||||
class Cache:
|
||||
|
||||
DEFAULT_TAB_NAMES = ["wx_server", "aliyun_sms", "aliyun_oss", "web_email"]
|
||||
|
||||
def __init__(self, rd: Redis):
|
||||
self.rd = rd
|
||||
|
||||
async def __get_tab_name_values(self, tab_names: List[str]):
|
||||
"""
|
||||
获取系统配置标签下的标签信息
|
||||
"""
|
||||
async_session = db_getter()
|
||||
session = await async_session.__anext__()
|
||||
model = VadminSystemSettingsTab
|
||||
v_options = [joinedload(model.settings)]
|
||||
sql = select(model).where(
|
||||
model.is_delete == false(),
|
||||
model.tab_name.in_(tab_names),
|
||||
model.disabled == false()
|
||||
).options(*[load for load in v_options])
|
||||
queryset = await session.execute(sql)
|
||||
datas = queryset.scalars().unique().all()
|
||||
return self.__generate_values(datas)
|
||||
|
||||
@classmethod
|
||||
def __generate_values(cls, datas: List[VadminSystemSettingsTab]):
|
||||
"""
|
||||
生成字典值
|
||||
"""
|
||||
return {
|
||||
tab.tab_name: {
|
||||
item.config_key: item.config_value
|
||||
for item in tab.settings
|
||||
if not item.disabled
|
||||
}
|
||||
for tab in datas
|
||||
}
|
||||
|
||||
async def cache_tab_names(self, tab_names: List[str] = None):
|
||||
"""
|
||||
缓存系统配置
|
||||
如果手动修改了mysql数据库中的配置
|
||||
那么需要在redis中将对应的tab_name删除
|
||||
"""
|
||||
|
||||
if not tab_names:
|
||||
tab_names = self.DEFAULT_TAB_NAMES
|
||||
datas = await self.__get_tab_name_values(tab_names)
|
||||
|
||||
for k, v in datas.items():
|
||||
await self.rd.client().set(k, json.dumps(v))
|
||||
|
||||
async def get_tab_name(self, tab_name: str, retry: int = 3):
|
||||
"""
|
||||
获取系统配置
|
||||
:param tab_name: 配置表标签名称
|
||||
:param retry: 重试次数
|
||||
"""
|
||||
result = await self.rd.get(tab_name)
|
||||
if not result and retry > 0:
|
||||
logger.error(f"未从Redis中获取到{tab_name}配置信息,正在重新更新配置信息,重试次数:{retry}。")
|
||||
await self.cache_tab_names([tab_name])
|
||||
return await self.get_tab_name(tab_name, retry - 1)
|
||||
elif not result and retry == 0:
|
||||
raise CustomException(f"获取{tab_name}配置信息失败,请联系管理员!", code=status.HTTP_ERROR)
|
||||
else:
|
||||
return json.loads(result)
|
59
utils/huawei_obs.py
Normal file
59
utils/huawei_obs.py
Normal file
@ -0,0 +1,59 @@
|
||||
from application.settings import HUAWEI_OBS
|
||||
|
||||
from obs import ObsClient
|
||||
from obs import DeleteObjectsRequest
|
||||
from obs import Object
|
||||
|
||||
|
||||
class ObsClient:
|
||||
|
||||
def __int__(self):
|
||||
self.obsClient = ObsClient(
|
||||
access_key_id=HUAWEI_OBS['AccessKeyID'],
|
||||
secret_access_key=HUAWEI_OBS['SecretAccessKey'],
|
||||
server=HUAWEI_OBS["server"])
|
||||
|
||||
async def put_file(self, objectKey: str, file_path: str):
|
||||
resp = await self.obsClient.putFile(
|
||||
bucketName=HUAWEI_OBS["bucketName"],
|
||||
objectKey=objectKey,
|
||||
file_path=file_path)
|
||||
if resp.status < 300:
|
||||
print("objectKey", objectKey)
|
||||
print("url", resp.body.objectUrl)
|
||||
return True, objectKey, resp.body.objectUrl
|
||||
else:
|
||||
return False, None, None
|
||||
|
||||
async def put_object(self, objectKey: str, file_content):
|
||||
resp = await self.obsClient.put_object(
|
||||
bucketName=HUAWEI_OBS["bucketName"],
|
||||
objectKey=objectKey,
|
||||
file_cotent=file_content)
|
||||
if resp.status < 300:
|
||||
print("objectKey", objectKey)
|
||||
print("url", resp.body.objectUrl)
|
||||
return True, objectKey, resp.body.objectUrl
|
||||
else:
|
||||
return False, None, None
|
||||
|
||||
async def del_objects(self, object_keys: []):
|
||||
objects = []
|
||||
for object_key in object_keys:
|
||||
object_one = Object(key=object_key, versionId=None)
|
||||
objects.append(object_one)
|
||||
encoding_type = 'url'
|
||||
|
||||
resp = await self.obsClient.deleteObjects(
|
||||
bucketName=HUAWEI_OBS["bucketName"],
|
||||
deleteObjectsRequest=DeleteObjectsRequest(quiet=False, objects=objects, encoding_type=encoding_type))
|
||||
if resp.status < 300:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
async def close(self):
|
||||
await self.obsClient.close()
|
||||
|
||||
|
@ -15,7 +15,6 @@ from typing import List
|
||||
from redis.asyncio import Redis
|
||||
|
||||
from core.exception import CustomException
|
||||
from utils.cache import Cache
|
||||
|
||||
|
||||
class EmailSender:
|
||||
@ -32,7 +31,7 @@ class EmailSender:
|
||||
"""
|
||||
获取配置信息
|
||||
"""
|
||||
web_email = await Cache(self.rd).get_tab_name("web_email", retry)
|
||||
web_email = []
|
||||
self.email = web_email.get("email_access")
|
||||
self.password = web_email.get("email_password")
|
||||
self.smtp_server = web_email.get("email_server")
|
||||
@ -84,10 +83,3 @@ class EmailSender:
|
||||
print('邮件发送失败!错误信息:', e)
|
||||
return False
|
||||
|
||||
|
||||
# if __name__ == '__main__':
|
||||
# sender = EmailSender()
|
||||
# to_emails = ['ktianc2001@163.com', '2445667550@qq.com']
|
||||
# subject = 'Test email'
|
||||
# body = 'This is a test email'
|
||||
# sender.send_email(to_emails, subject, body)
|
||||
|
@ -28,7 +28,6 @@ from alibabacloud_tea_util import models as util_models
|
||||
from core.logger import logger
|
||||
import datetime
|
||||
from redis.asyncio.client import Redis
|
||||
from utils.cache import Cache
|
||||
from utils.db_getter import DBGetter
|
||||
|
||||
|
||||
@ -86,7 +85,7 @@ class AliyunSMS(DBGetter):
|
||||
raise ValueError("缺少 redis 对象参数!")
|
||||
elif not self.sign_conf or not self.template_code_conf:
|
||||
raise ValueError("缺少短信签名信息和短信模板ID!")
|
||||
aliyun_sms = await Cache(self.rd).get_tab_name("aliyun_sms", retry)
|
||||
aliyun_sms = []
|
||||
self.access_key = aliyun_sms.get("sms_access_key")
|
||||
self.access_key_secret = aliyun_sms.get("sms_access_key_secret")
|
||||
self.send_interval = int(aliyun_sms.get("sms_send_interval"))
|
||||
|
@ -8,7 +8,6 @@
|
||||
|
||||
import requests
|
||||
from core.logger import logger
|
||||
from utils.cache import Cache
|
||||
from utils.wx.wx_access_token import WxAccessToken
|
||||
from redis.asyncio import Redis
|
||||
|
||||
@ -35,7 +34,7 @@ class WXOAuth:
|
||||
"""
|
||||
if not self.tab_name:
|
||||
logger.error(f"请选择认证的微信平台")
|
||||
wx_config = await Cache(self.rd).get_tab_name(self.tab_name, retry)
|
||||
wx_config = []
|
||||
self.appid = wx_config.get("wx_server_app_id")
|
||||
self.secret = wx_config.get("wx_server_app_secret")
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user