Skip to content
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
17 changes: 17 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
name: Plugin Release

on:
push:
tags:
- "v*"

jobs:
update-submodule:
runs-on: ubuntu-latest
steps:
- name: Update plugin submodule
uses: fastapi-practices/plugin-release@v1
with:
push-to: fastapi-practices/plugins
env:
GH_TOKEN: ${{ secrets.GH_TOKEN }}
Empty file added __init__.py
Empty file.
Empty file added api/__init__.py
Empty file.
10 changes: 10 additions & 0 deletions api/router.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from fastapi import APIRouter

from backend.core.conf import settings
from backend.plugin.tenant.api.v1.package import router as package_router
from backend.plugin.tenant.api.v1.tenant import router as tenant_router

v1 = APIRouter(prefix=settings.FASTAPI_API_V1_PATH)

v1.include_router(tenant_router, prefix='/tenants', tags=['租户管理'])
v1.include_router(package_router, prefix='/tenant/packages', tags=['租户套餐管理'])
Empty file added api/v1/__init__.py
Empty file.
99 changes: 99 additions & 0 deletions api/v1/package.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
from typing import Annotated

from fastapi import APIRouter, Depends, Path, Query

from backend.app.admin.schema.menu import GetMenuTree
from backend.common.pagination import DependsPagination, PageData
from backend.common.response.response_schema import ResponseModel, ResponseSchemaModel, response_base
from backend.common.security.jwt import DependsJwtAuth
from backend.common.security.permission import RequestPermission
from backend.common.security.rbac import DependsRBAC
from backend.database.db import CurrentSession, CurrentSessionTransaction
from backend.plugin.tenant.schema.package import (
CreateTenantPackageParam,
GetTenantPackageDetail,
UpdateTenantPackageParam,
)
from backend.plugin.tenant.service.package_service import tenant_package_service

router = APIRouter()


@router.get('/{pk}', summary='获取套餐详情', dependencies=[DependsJwtAuth])
async def get_tenant_package(
db: CurrentSession, pk: Annotated[int, Path(description='套餐 ID')]
) -> ResponseSchemaModel[GetTenantPackageDetail]:
package = await tenant_package_service.get(db=db, pk=pk)
return response_base.success(data=package)


@router.get('/{pk}/menus', summary='获取套餐菜单树', dependencies=[DependsJwtAuth])
async def get_tenant_package_menus(
db: CurrentSession, pk: Annotated[int, Path(description='套餐 ID')]
) -> ResponseSchemaModel[list[GetMenuTree] | None]:
menus = await tenant_package_service.get_menu_tree(db=db, pk=pk)
return response_base.success(data=menus)


@router.get(
'',
summary='分页获取所有套餐',
dependencies=[
DependsJwtAuth,
DependsPagination,
],
)
async def get_tenant_packages_paginated(
db: CurrentSession,
name: Annotated[str | None, Query(description='套餐名称')] = None,
status: Annotated[int | None, Query(description='状态')] = None,
) -> ResponseSchemaModel[PageData[GetTenantPackageDetail]]:
page_data = await tenant_package_service.get_list(db=db, name=name, status=status)
return response_base.success(data=page_data)


@router.post(
'',
summary='创建套餐',
dependencies=[
Depends(RequestPermission('tenant:package:add')),
DependsRBAC,
],
)
async def create_tenant_package(db: CurrentSessionTransaction, obj: CreateTenantPackageParam) -> ResponseModel:
await tenant_package_service.create(db=db, obj=obj)
return response_base.success()


@router.put(
'/{pk}',
summary='更新套餐',
dependencies=[
Depends(RequestPermission('tenant:package:edit')),
DependsRBAC,
],
)
async def update_tenant_package(
db: CurrentSessionTransaction, pk: Annotated[int, Path(description='套餐 ID')], obj: UpdateTenantPackageParam
) -> ResponseModel:
count = await tenant_package_service.update(db=db, pk=pk, obj=obj)
if count > 0:
return response_base.success()
return response_base.fail()


@router.delete(
'/{pk}',
summary='删除套餐',
dependencies=[
Depends(RequestPermission('tenant:package:del')),
DependsRBAC,
],
)
async def delete_tenant_package(
db: CurrentSessionTransaction, pk: Annotated[int, Path(description='套餐 ID')]
) -> ResponseModel:
count = await tenant_package_service.delete(db=db, pk=pk)
if count > 0:
return response_base.success()
return response_base.fail()
132 changes: 132 additions & 0 deletions api/v1/tenant.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
from typing import Annotated

from fastapi import APIRouter, Depends, Path, Query

from backend.common.pagination import DependsPagination, PageData
from backend.common.response.response_schema import ResponseModel, ResponseSchemaModel, response_base
from backend.common.security.jwt import DependsJwtAuth
from backend.common.security.permission import RequestPermission
from backend.common.security.rbac import DependsRBAC
from backend.core.conf import settings
from backend.database.db import CurrentSession, CurrentSessionTransaction
from backend.plugin.tenant.schema.tenant import (
CreateTenantParam,
DeleteTenantParam,
GetTenantDetail,
UpdateTenantAdminPwdParam,
UpdateTenantParam,
)
from backend.plugin.tenant.service.tenant_service import tenant_service

router = APIRouter()


@router.get('/enabled', summary='获取租户开启状态')
async def get_tenant_enabled_status() -> ResponseSchemaModel[bool]:
return response_base.success(data=settings.TENANT_ENABLED)


@router.get('/id', summary='获取租户 ID')
async def get_tenant_id(
db: CurrentSession,
domain: Annotated[str, Query(description='租户域名')],
) -> ResponseSchemaModel[int | None]:
tenant_id = await tenant_service.get_id_by_domain(db=db, domain=domain)
return response_base.success(data=tenant_id)


@router.get('/{pk}', summary='获取租户详情', dependencies=[DependsJwtAuth])
async def get_tenant(
db: CurrentSession, pk: Annotated[int, Path(description='租户 ID')]
) -> ResponseSchemaModel[GetTenantDetail]:
tenant = await tenant_service.get(db=db, pk=pk)
return response_base.success(data=tenant)


@router.get(
'',
summary='分页获取所有租户',
dependencies=[
DependsJwtAuth,
DependsPagination,
],
)
async def get_tenants_paginated(
db: CurrentSession,
name: Annotated[str | None, Query(description='租户名称')] = None,
code: Annotated[str | None, Query(description='租户编码')] = None,
domain: Annotated[str | None, Query(description='租户域名')] = None,
package_id: Annotated[int | None, Query(description='套餐 ID')] = None,
status: Annotated[int | None, Query(description='状态')] = None,
) -> ResponseSchemaModel[PageData[GetTenantDetail]]:
page_data = await tenant_service.get_list(
db=db,
name=name,
code=code,
domain=domain,
package_id=package_id,
status=status,
)
return response_base.success(data=page_data)


@router.post(
'',
summary='创建租户',
dependencies=[
Depends(RequestPermission('tenant:management:add')),
DependsRBAC,
],
)
async def create_tenant(db: CurrentSessionTransaction, obj: CreateTenantParam) -> ResponseModel:
await tenant_service.create(db=db, obj=obj)
return response_base.success()


@router.put(
'/{pk}',
summary='更新租户',
dependencies=[
Depends(RequestPermission('tenant:management:edit')),
DependsRBAC,
],
)
async def update_tenant(
db: CurrentSessionTransaction, pk: Annotated[int, Path(description='租户 ID')], obj: UpdateTenantParam
) -> ResponseModel:
count = await tenant_service.update(db=db, pk=pk, obj=obj)
if count > 0:
return response_base.success()
return response_base.fail()


@router.put(
'/{pk}/admin/password',
summary='修改租户管理员密码',
dependencies=[
Depends(RequestPermission('tenant:management:pwd')),
DependsRBAC,
],
)
async def update_tenant_admin_password(
db: CurrentSessionTransaction,
pk: Annotated[int, Path(description='租户 ID')],
obj: UpdateTenantAdminPwdParam,
) -> ResponseModel:
await tenant_service.update_admin_password(db=db, pk=pk, password=obj.password)
return response_base.success()


@router.delete(
'',
summary='批量删除租户',
dependencies=[
Depends(RequestPermission('tenant:management:del')),
DependsRBAC,
],
)
async def delete_tenants(db: CurrentSessionTransaction, obj: DeleteTenantParam) -> ResponseModel:
count = await tenant_service.delete(db=db, obj=obj)
if count > 0:
return response_base.success()
return response_base.fail()
Empty file added crud/__init__.py
Empty file.
Loading