Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add page to display dynamic configuration #365

Merged
merged 7 commits into from
Jul 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions backend/app/admin/api/v1/sys/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from backend.app.admin.api.v1.sys.api import router as api_router
from backend.app.admin.api.v1.sys.casbin import router as casbin_router
from backend.app.admin.api.v1.sys.config import router as config_router
from backend.app.admin.api.v1.sys.dept import router as dept_router
from backend.app.admin.api.v1.sys.dict_data import router as dict_data_router
from backend.app.admin.api.v1.sys.dict_type import router as dict_type_router
Expand All @@ -15,6 +16,7 @@

router.include_router(api_router, prefix='/apis', tags=['系统API'])
router.include_router(casbin_router, prefix='/casbin', tags=['系统Casbin权限'])
router.include_router(config_router, prefix='/configs', tags=['系统配置'])
router.include_router(dept_router, prefix='/depts', tags=['系统部门'])
router.include_router(dict_data_router, prefix='/dict_datas', tags=['系统字典数据'])
router.include_router(dict_type_router, prefix='/dict_types', tags=['系统字典类型'])
Expand Down
63 changes: 63 additions & 0 deletions backend/app/admin/api/v1/sys/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from typing import Annotated

from fastapi import APIRouter, Depends, Path, Query

from backend.app.admin.schema.config import CreateConfigParam, UpdateConfigParam
from backend.app.admin.service.config_service import config_service
from backend.common.response.response_schema import ResponseModel, response_base
from backend.common.security.jwt import DependsJwtAuth
from backend.common.security.permission import RequestPermission
from backend.common.security.rbac import DependsRBAC

router = APIRouter()


@router.get('', summary='获取系统配置详情', dependencies=[DependsJwtAuth])
async def get_config() -> ResponseModel:
config = await config_service.get()
return await response_base.success(data=config)


@router.post(
'',
summary='创建系统配置',
dependencies=[
Depends(RequestPermission('sys:config:add')),
DependsRBAC,
],
)
async def create_config(obj: CreateConfigParam) -> ResponseModel:
await config_service.create(obj=obj)
return await response_base.success()


@router.put(
'/{pk}',
summary='更新系统配置',
dependencies=[
Depends(RequestPermission('sys:config:edit')),
DependsRBAC,
],
)
async def update_config(pk: Annotated[int, Path(...)], obj: UpdateConfigParam) -> ResponseModel:
count = await config_service.update(pk=pk, obj=obj)
if count > 0:
return await response_base.success()
return await response_base.fail()


@router.delete(
'',
summary='(批量)删除系统配置',
dependencies=[
Depends(RequestPermission('sys:config:del')),
DependsRBAC,
],
)
async def delete_config(pk: Annotated[list[int], Query(...)]) -> ResponseModel:
count = await config_service.delete(pk=pk)
if count > 0:
return await response_base.success()
return await response_base.fail()
3 changes: 3 additions & 0 deletions backend/app/admin/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ class AdminSettings(BaseSettings):
CAPTCHA_LOGIN_REDIS_PREFIX: str = 'fba_login_captcha'
CAPTCHA_LOGIN_EXPIRE_SECONDS: int = 60 * 5 # 过期时间,单位:秒

# Config
CONFIG_REDIS_KEY: str = 'fba_config'


@lru_cache
def get_admin_settings() -> AdminSettings:
Expand Down
66 changes: 66 additions & 0 deletions backend/app/admin/crud/crud_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from typing import Sequence

from sqlalchemy import delete, select
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy_crud_plus import CRUDPlus

from backend.app.admin.model import Config
from backend.app.admin.schema.config import CreateConfigParam, UpdateConfigParam


class CRUDConfig(CRUDPlus[Config]):
async def get_one(self, db: AsyncSession) -> Config | None:
"""
获取 Config

:param db:
:return:
"""
query = await db.execute(select(self.model).limit(1))
return query.scalars().first()

async def get_all(self, db: AsyncSession) -> Sequence[Config]:
"""
获取所有 Config

:param db:
:return:
"""
return await self.select_models(db)

async def create(self, db: AsyncSession, obj_in: CreateConfigParam) -> None:
"""
创建 Config

:param db:
:param obj_in:
:return:
"""
await self.create_model(db, obj_in)

async def update(self, db: AsyncSession, pk: int, obj_in: UpdateConfigParam) -> int:
"""
更新 Config

:param db:
:param pk:
:param obj_in:
:return:
"""
return await self.update_model(db, pk, obj_in)

async def delete(self, db: AsyncSession, pk: list[int]) -> int:
"""
删除 Config

:param db:
:param pk:
:return:
"""
configs = await db.execute(delete(self.model).where(self.model.id.in_(pk)))
return configs.rowcount


config_dao: CRUDConfig = CRUDConfig(Config)
1 change: 1 addition & 0 deletions backend/app/admin/model/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
# -*- coding: utf-8 -*-
from backend.app.admin.model.sys_api import Api
from backend.app.admin.model.sys_casbin_rule import CasbinRule
from backend.app.admin.model.sys_config import Config
from backend.app.admin.model.sys_dept import Dept
from backend.app.admin.model.sys_dict_data import DictData
from backend.app.admin.model.sys_dict_type import DictType
Expand Down
28 changes: 28 additions & 0 deletions backend/app/admin/model/sys_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from sqlalchemy import String
from sqlalchemy.dialects.mysql import LONGTEXT
from sqlalchemy.orm import Mapped, mapped_column

from backend.common.model import Base, id_key


class Config(Base):
"""系统配置表"""

__tablename__ = 'sys_config'

id: Mapped[id_key] = mapped_column(init=False)
login_title: Mapped[str] = mapped_column(String(20), default='登陆 FBA', comment='登陆页面标题')
login_sub_title: Mapped[str] = mapped_column(
String(50), default='fastapi_best_architecture', comment='登陆页面子标题'
)
footer: Mapped[str] = mapped_column(String(50), default='FBA', comment='页脚标题')
logo: Mapped[str] = mapped_column(LONGTEXT, default='Arco', comment='Logo')
system_title: Mapped[str] = mapped_column(String(20), default='Arco', comment='系统标题')
system_comment: Mapped[str] = mapped_column(
LONGTEXT,
default='基于 FastAPI 构建的前后端分离 RBAC 权限控制系统,采用独特的伪三层架构模型设计,'
'内置 fastapi-admin 基本实现,并作为模板库免费开源',
comment='系统描述',
)
35 changes: 35 additions & 0 deletions backend/app/admin/schema/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from datetime import datetime

from pydantic import ConfigDict, Field

from backend.common.schema import SchemaBase


class ConfigSchemaBase(SchemaBase):
login_title: str = Field(default='登陆 FBA')
login_sub_title: str = Field(default='fastapi_best_architecture')
footer: str = Field(default='FBA')
logo: str = Field(default='Arco')
system_title: str = Field(default='Arco')
system_comment: str = Field(
default='基于 FastAPI 构建的前后端分离 RBAC 权限控制系统,采用独特的伪三层架构模型设计,'
'内置 fastapi-admin 基本实现,并作为模板库免费开源'
)


class CreateConfigParam(ConfigSchemaBase):
pass


class UpdateConfigParam(ConfigSchemaBase):
pass


class GetConfigListDetails(ConfigSchemaBase):
model_config = ConfigDict(from_attributes=True)

id: int
created_time: datetime
updated_time: datetime | None = None
57 changes: 57 additions & 0 deletions backend/app/admin/service/config_service.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from backend.app.admin.conf import admin_settings
from backend.app.admin.crud.crud_config import config_dao
from backend.app.admin.model import Config
from backend.app.admin.schema.config import CreateConfigParam, UpdateConfigParam
from backend.common.exception import errors
from backend.database.db_mysql import async_db_session
from backend.database.db_redis import redis_client
from backend.utils.serializers import select_as_dict


class ConfigService:
@staticmethod
async def get() -> Config | dict:
async with async_db_session() as db:
cache_config = await redis_client.hgetall(admin_settings.CONFIG_REDIS_KEY)
if not cache_config:
config = await config_dao.get_one(db)
if not config:
raise errors.NotFoundError(msg='系统配置不存在')
data_map = await select_as_dict(config)
del data_map['created_time']
del data_map['updated_time']
await redis_client.hset(admin_settings.CONFIG_REDIS_KEY, mapping=data_map)
return config
else:
return cache_config

@staticmethod
async def create(*, obj: CreateConfigParam) -> None:
async with async_db_session.begin() as db:
config = await config_dao.get_one(db)
if config:
raise errors.ForbiddenError(msg='系统配置已存在')
await config_dao.create(db, obj)
await redis_client.hset(admin_settings.CONFIG_REDIS_KEY, mapping=obj.model_dump())

@staticmethod
async def update(*, pk: int, obj: UpdateConfigParam) -> int:
async with async_db_session.begin() as db:
count = await config_dao.update(db, pk, obj)
await redis_client.hset(admin_settings.CONFIG_REDIS_KEY, mapping=obj.model_dump())
return count

@staticmethod
async def delete(*, pk: list[int]) -> int:
async with async_db_session.begin() as db:
configs = await config_dao.get_all(db)
if len(configs) == 1:
raise errors.ForbiddenError(msg='系统配置无法彻底删除')
count = await config_dao.delete(db, pk)
await redis_client.delete(admin_settings.CONFIG_REDIS_KEY)
return count


config_service = ConfigService()
4 changes: 2 additions & 2 deletions backend/templates/py/crud.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class CRUD{{ table_name_class }}(CRUDPlus[{{ schema_name }}]):
"""
return await self.select_model_by_id(db, pk)

async def get_all(self, db: AsyncSession) -> Sequence[{{ schema_name }}]:
async def get_all(self, db: AsyncSession) -> Sequence[{{ table_name_class }}]:
"""
获取所有 {{ schema_name }}

Expand Down Expand Up @@ -63,4 +63,4 @@ class CRUD{{ table_name_class }}(CRUDPlus[{{ schema_name }}]):
return {{ table_name_en }}s.rowcount


{{ table_name_en }}_dao: CRUD{{ schema_name }} = CRUD{{ schema_name }}({{ schema_name }})
{{ table_name_en }}_dao: CRUD{{ table_name_class }} = CRUD{{ table_name_class }}({{ table_name_class }})
6 changes: 3 additions & 3 deletions backend/templates/py/service.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ from backend.common.exception import errors
from backend.database.db_mysql import async_db_session


class {{ schema_name }}Service:
class {{ table_name_class }}Service:
@staticmethod
async def get(*, pk: int) -> {{ table_name_class }}:
async with async_db_session() as db:
{{ table_name_en }} = await {{ table_name_en }}_dao.get(db, pk)
if not {{ table_name_en }}:
raise errors.NotFoundError(msg='接口不存在')
raise errors.NotFoundError(msg='{{ table_simple_name_zh }}不存在')
return {{ table_name_en }}

@staticmethod
Expand Down Expand Up @@ -45,4 +45,4 @@ class {{ schema_name }}Service:
return count


{{ table_name_en }}_service = {{ schema_name }}Service()
{{ table_name_en }}_service = {{ table_name_class }}Service()
2 changes: 1 addition & 1 deletion backend/utils/gen_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ def get_vars(business: GenBusiness, models: list[GenModel]) -> dict:
'table_comment': business.table_comment,
'schema_name': to_pascal(business.schema_name),
'have_datetime_column': business.have_datetime_column,
'permission_sign': str(business.__tablename__.replace('_', ':')),
'permission_sign': str(business.table_name_en.replace('_', ':')),
'models': models,
}

Expand Down
Loading