From c7798e5eebecb3c1be9277374dc5ab259d5e8cc6 Mon Sep 17 00:00:00 2001 From: zhangtao <9480807882@qq.com> Date: Thu, 25 Dec 2025 01:30:44 +0800 Subject: [PATCH] =?UTF-8?q?refactor(=E6=9D=83=E9=99=90=E7=AE=A1=E7=90=86):?= =?UTF-8?q?=20=E7=BB=9F=E4=B8=80=E8=AF=A6=E6=83=85=E6=9D=83=E9=99=90?= =?UTF-8?q?=E6=A0=87=E8=AF=86=E4=BB=8Equery=E6=94=B9=E4=B8=BAdetail?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 删除租户和客户管理相关模块代码 - 更新多个视图文件的权限标识 - 修改全局响应类型添加success字段 - 优化基础模型字段索引配置 --- .../api/v1/module_system/customer/__init__.py | 2 - .../v1/module_system/customer/controller.py | 215 ----- .../app/api/v1/module_system/customer/crud.py | 125 --- .../api/v1/module_system/customer/model.py | 38 - .../api/v1/module_system/customer/schema.py | 82 -- .../api/v1/module_system/customer/service.py | 312 ------- .../api/v1/module_system/tenant/__init__.py | 2 - .../api/v1/module_system/tenant/controller.py | 215 ----- .../app/api/v1/module_system/tenant/crud.py | 125 --- .../app/api/v1/module_system/tenant/model.py | 34 - .../app/api/v1/module_system/tenant/schema.py | 78 -- .../api/v1/module_system/tenant/service.py | 479 ----------- backend/app/core/base_model.py | 73 +- backend/app/scripts/data/sys_menu.json | 386 +-------- backend/app/scripts/data/sys_tenant.json | 11 - frontend/src/api/module_system/customer.ts | 101 --- frontend/src/api/module_system/tenant.ts | 102 --- frontend/src/types/global.d.ts | 1 + .../job/components/JobLogDrawer.vue | 19 +- .../views/module_application/job/index.vue | 1 + .../src/views/module_gencode/demo/index.vue | 2 +- .../views/module_system/customer/index.vue | 749 ----------------- .../src/views/module_system/dept/index.vue | 2 +- .../dict/components/DataDrawer.vue | 2 +- .../src/views/module_system/dict/index.vue | 2 +- .../src/views/module_system/log/index.vue | 2 +- .../src/views/module_system/menu/index.vue | 2 +- .../src/views/module_system/notice/index.vue | 2 +- .../src/views/module_system/param/index.vue | 2 +- .../views/module_system/position/index.vue | 2 +- .../src/views/module_system/role/index.vue | 2 +- .../src/views/module_system/tenant/index.vue | 758 ------------------ .../src/views/module_system/token/index.vue | 2 +- 33 files changed, 36 insertions(+), 3894 deletions(-) delete mode 100644 backend/app/api/v1/module_system/customer/__init__.py delete mode 100644 backend/app/api/v1/module_system/customer/controller.py delete mode 100644 backend/app/api/v1/module_system/customer/crud.py delete mode 100644 backend/app/api/v1/module_system/customer/model.py delete mode 100644 backend/app/api/v1/module_system/customer/schema.py delete mode 100644 backend/app/api/v1/module_system/customer/service.py delete mode 100644 backend/app/api/v1/module_system/tenant/__init__.py delete mode 100644 backend/app/api/v1/module_system/tenant/controller.py delete mode 100644 backend/app/api/v1/module_system/tenant/crud.py delete mode 100644 backend/app/api/v1/module_system/tenant/model.py delete mode 100644 backend/app/api/v1/module_system/tenant/schema.py delete mode 100644 backend/app/api/v1/module_system/tenant/service.py delete mode 100644 backend/app/scripts/data/sys_tenant.json delete mode 100644 frontend/src/api/module_system/customer.ts delete mode 100644 frontend/src/api/module_system/tenant.ts delete mode 100644 frontend/src/views/module_system/customer/index.vue delete mode 100644 frontend/src/views/module_system/tenant/index.vue diff --git a/backend/app/api/v1/module_system/customer/__init__.py b/backend/app/api/v1/module_system/customer/__init__.py deleted file mode 100644 index 633f8661..00000000 --- a/backend/app/api/v1/module_system/customer/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -# -*- coding: utf-8 -*- - diff --git a/backend/app/api/v1/module_system/customer/controller.py b/backend/app/api/v1/module_system/customer/controller.py deleted file mode 100644 index e47da8cc..00000000 --- a/backend/app/api/v1/module_system/customer/controller.py +++ /dev/null @@ -1,215 +0,0 @@ -# -*- coding: utf-8 -*- - -from fastapi import APIRouter, Body, Depends, Path, UploadFile -from fastapi.responses import JSONResponse, StreamingResponse -import urllib.parse - -from app.common.response import StreamResponse, SuccessResponse -from app.utils.common_util import bytes2file_response -from app.core.base_params import PaginationQueryParam -from app.core.dependencies import AuthPermission -from app.core.router_class import OperationLogRoute -from app.core.base_schema import BatchSetAvailable -from app.core.logger import log - -from app.api.v1.module_system.auth.schema import AuthSchema -from .service import CustomerService -from .schema import ( - CustomerCreateSchema, - CustomerUpdateSchema, - CustomerQueryParam -) - - -CustomerRouter = APIRouter(route_class=OperationLogRoute, prefix="/customer", tags=["客户模块"]) - -@CustomerRouter.get("/detail/{id}", summary="获取客户详情", description="获取客户详情") -async def get_obj_detail_controller( - id: int = Path(..., description="客户ID"), - auth: AuthSchema = Depends(AuthPermission(["module_system:customer:detail"])) -) -> JSONResponse: - """ - 获取客户详情 - - 参数: - - id (int): 客户ID - - auth (AuthSchema): 认证信息模型 - - 返回: - - JSONResponse: 包含客户详情的JSON响应 - """ - result_dict = await CustomerService.detail_service(id=id, auth=auth) - log.info(f"获取客户详情成功 {id}") - return SuccessResponse(data=result_dict, msg="获取客户详情成功") - -@CustomerRouter.get("/list", summary="查询客户列表", description="查询客户列表") -async def get_obj_list_controller( - page: PaginationQueryParam = Depends(), - search: CustomerQueryParam = Depends(), - auth: AuthSchema = Depends(AuthPermission(["module_system:customer:query"])) -) -> JSONResponse: - """ - 查询客户列表 - - 参数: - - page (PaginationQueryParam): 分页查询参数 - - search (CustomerQueryParam): 查询参数 - - auth (AuthSchema): 认证信息模型 - - 返回: - - JSONResponse: 包含客户列表分页信息的JSON响应 - """ - # 使用数据库分页而不是应用层分页 - result_dict = await CustomerService.page_service( - auth=auth, - page_no=page.page_no if page.page_no is not None else 1, - page_size=page.page_size if page.page_size is not None else 10, - search=search, - order_by=page.order_by - ) - log.info("查询客户列表成功") - return SuccessResponse(data=result_dict, msg="查询客户列表成功") - -@CustomerRouter.post("/create", summary="创建客户", description="创建客户") -async def create_obj_controller( - data: CustomerCreateSchema, - auth: AuthSchema = Depends(AuthPermission(["module_system:customer:create"])) -) -> JSONResponse: - """ - 创建客户 - - 参数: - - data (CustomerCreateSchema): 客户创建模型 - - auth (AuthSchema): 认证信息模型 - - 返回: - - JSONResponse: 包含创建客户详情的JSON响应 - """ - result_dict = await CustomerService.create_service(auth=auth, data=data) - log.info(f"创建客户成功: {result_dict.get('name')}") - return SuccessResponse(data=result_dict, msg="创建客户成功") - -@CustomerRouter.put("/update/{id}", summary="修改客户", description="修改客户") -async def update_obj_controller( - data: CustomerUpdateSchema, - id: int = Path(..., description="客户ID"), - auth: AuthSchema = Depends(AuthPermission(["module_system:customer:update"])) -) -> JSONResponse: - """ - 修改客户 - - 参数: - - data (CustomerUpdateSchema): 客户更新模型 - - id (int): 客户ID - - auth (AuthSchema): 认证信息模型 - - 返回: - - JSONResponse: 包含修改客户详情的JSON响应 - """ - result_dict = await CustomerService.update_service(auth=auth, id=id, data=data) - log.info(f"修改客户成功: {result_dict.get('name')}") - return SuccessResponse(data=result_dict, msg="修改客户成功") - -@CustomerRouter.delete("/delete", summary="删除客户", description="删除客户") -async def delete_obj_controller( - ids: list[int] = Body(..., description="ID列表"), - auth: AuthSchema = Depends(AuthPermission(["module_system:customer:delete"])) -) -> JSONResponse: - """ - 删除客户 - - 参数: - - ids (list[int]): 客户ID列表 - - auth (AuthSchema): 认证信息模型 - - 返回: - - JSONResponse: 包含删除客户详情的JSON响应 - """ - await CustomerService.delete_service(auth=auth, ids=ids) - log.info(f"删除客户成功: {ids}") - return SuccessResponse(msg="删除客户成功") - -@CustomerRouter.patch("/available/setting", summary="批量修改客户状态", description="批量修改客户状态") -async def batch_set_available_obj_controller( - data: BatchSetAvailable, - auth: AuthSchema = Depends(AuthPermission(["module_system:customer:patch"])) -) -> JSONResponse: - """ - 批量修改客户状态 - - 参数: - - data (BatchSetAvailable): 批量修改客户状态模型 - - auth (AuthSchema): 认证信息模型 - - 返回: - - JSONResponse: 包含批量修改客户状态详情的JSON响应 - """ - await CustomerService.set_available_service(auth=auth, data=data) - log.info(f"批量修改客户状态成功: {data.ids}") - return SuccessResponse(msg="批量修改客户状态成功") - -@CustomerRouter.post('/export', summary="导出客户", description="导出客户") -async def export_obj_list_controller( - search: CustomerQueryParam = Depends(), - auth: AuthSchema = Depends(AuthPermission(["module_system:customer:export"])) -) -> StreamingResponse: - """ - 导出客户 - - 参数: - - search (CustomerQueryParam): 查询参数 - - auth (AuthSchema): 认证信息模型 - - 返回: - - StreamingResponse: 包含客户列表的Excel文件流响应 - """ - result_dict_list = await CustomerService.list_service(search=search, auth=auth) - export_result = await CustomerService.batch_export_service(obj_list=result_dict_list) - log.info('导出客户成功') - - return StreamResponse( - data=bytes2file_response(export_result), - media_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', - headers={ - 'Content-Disposition': 'attachment; filename=example.xlsx' - } - ) - -@CustomerRouter.post('/import', summary="导入客户", description="导入客户") -async def import_obj_list_controller( - file: UploadFile, - auth: AuthSchema = Depends(AuthPermission(["module_system:tenant:import"])) -) -> JSONResponse: - """ - 导入租户 - - 参数: - - file (UploadFile): 导入的Excel文件 - - auth (AuthSchema): 认证信息模型 - - 返回: - - JSONResponse: 包含导入客户详情的JSON响应 - """ - batch_import_result = await CustomerService.batch_import_service(file=file, auth=auth, update_support=True) - log.info(f"导入客户成功: {batch_import_result}") - return SuccessResponse(data=batch_import_result, msg="导入租户成功") - -@CustomerRouter.post('/download/template', summary="获取客户导入模板", description="获取客户导入模板", dependencies=[Depends(AuthPermission(["module_system:customer:download"]))]) -async def export_obj_template_controller() -> StreamingResponse: - """ - 获取租户导入模板 - - 返回: - - StreamingResponse: 包含租户导入模板的Excel文件流响应 - """ - example_import_template_result = await CustomerService.import_template_download_service() - log.info('获取客户导入模板成功') - - return StreamResponse( - data=bytes2file_response(example_import_template_result), - media_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', - headers={ - 'Content-Disposition': f'attachment; filename={urllib.parse.quote("客户导入模板.xlsx")}', - 'Access-Control-Expose-Headers': 'Content-Disposition' - } - ) \ No newline at end of file diff --git a/backend/app/api/v1/module_system/customer/crud.py b/backend/app/api/v1/module_system/customer/crud.py deleted file mode 100644 index c32112c7..00000000 --- a/backend/app/api/v1/module_system/customer/crud.py +++ /dev/null @@ -1,125 +0,0 @@ -# -*- coding: utf-8 -*- - -from typing import Dict, List, Optional, Sequence, Union, Any - -from app.core.base_crud import CRUDBase - -from app.api.v1.module_system.auth.schema import AuthSchema -from .model import CustomerModel -from .schema import CustomerCreateSchema, CustomerUpdateSchema, CustomerOutSchema - - -class CustomerCRUD(CRUDBase[CustomerModel, CustomerCreateSchema, CustomerUpdateSchema]): - """客户数据层""" - - def __init__(self, auth: AuthSchema) -> None: - """ - 初始化CRUD数据层 - - 参数: - - auth (AuthSchema): 认证信息模型 - """ - super().__init__(model=CustomerModel, auth=auth) - - async def get_by_id_crud(self, id: int, preload: Optional[List[Union[str, Any]]] = None) -> Optional[CustomerModel]: - """ - 详情 - - 参数: - - id (int): 客户ID - - preload (Optional[List[Union[str, Any]]]): 预加载关系,未提供时使用模型默认项 - - 返回: - - Optional[CustomerModel]: 客户模型实例或None - """ - return await self.get(id=id, preload=preload) - - async def list_crud(self, search: Optional[Dict] = None, order_by: Optional[List[Dict[str, str]]] = None, preload: Optional[List[Union[str, Any]]] = None) -> Sequence[CustomerModel]: - """ - 列表查询 - - 参数: - - search (Optional[Dict]): 查询参数 - - order_by (Optional[List[Dict[str, str]]]): 排序参数 - - preload (Optional[List[Union[str, Any]]]): 预加载关系,未提供时使用模型默认项 - - 返回: - - Sequence[TenantModel]: 租户模型实例序列 - """ - return await self.list(search=search, order_by=order_by, preload=preload) - - async def create_crud(self, data: CustomerCreateSchema) -> Optional[CustomerModel]: - """ - 创建 - - 参数: - - data (CustomerCreateSchema): 客户创建模型 - - 返回: - - Optional[CustomerModel]: 客户模型实例或None - """ - return await self.create(data=data) - - async def update_crud(self, id: int, data: CustomerUpdateSchema) -> Optional[CustomerModel]: - """ - 更新 - - 参数: - - id (int): 客户ID - - data (CustomerUpdateSchema): 客户更新模型 - - 返回: - - Optional[CustomerModel]: 客户模型实例或None - """ - return await self.update(id=id, data=data) - - async def delete_crud(self, ids: List[int]) -> None: - """ - 批量删除 - - 参数: - - ids (List[int]): 客户ID列表 - - 返回: - - None - """ - return await self.delete(ids=ids) - - async def set_available_crud(self, ids: List[int], status: str) -> None: - """ - 批量设置可用状态 - - 参数: - - ids (List[int]): 客户ID列表 - - status (bool): 可用状态 - - 返回: - - None - """ - return await self.set(ids=ids, status=status) - - async def page_crud(self, offset: int, limit: int, order_by: Optional[List[Dict[str, str]]] = None, search: Optional[Dict] = None, preload: Optional[List[Union[str, Any]]] = None) -> Dict: - """ - 分页查询 - - 参数: - - offset (int): 偏移量 - - limit (int): 每页数量 - - order_by (Optional[List[Dict[str, str]]]): 排序参数 - - search (Optional[Dict]): 查询参数 - - preload (Optional[List[Union[str, Any]]]): 预加载关系,未提供时使用模型默认项 - - 返回: - - Dict: 分页数据 - """ - order_by_list = order_by or [{'id': 'asc'}] - search_dict = search or {} - - return await self.page( - offset=offset, - limit=limit, - order_by=order_by_list, - search=search_dict, - out_schema=CustomerOutSchema, - preload=preload - ) diff --git a/backend/app/api/v1/module_system/customer/model.py b/backend/app/api/v1/module_system/customer/model.py deleted file mode 100644 index 0ebf8f13..00000000 --- a/backend/app/api/v1/module_system/customer/model.py +++ /dev/null @@ -1,38 +0,0 @@ -# -*- coding: utf-8 -*- - -from typing import TYPE_CHECKING -from sqlalchemy import String -from sqlalchemy.orm import Mapped, mapped_column, relationship, validates - -from app.core.base_model import ModelMixin, UserMixin, TenantMixin -if TYPE_CHECKING: - from app.api.v1.module_system.user.model import UserModel - - -class CustomerModel(ModelMixin, TenantMixin, UserMixin): - """ - 客户表 - """ - __tablename__: str = 'sys_customer' - __table_args__: dict[str, str] = ({'comment': '客户表'}) - __loader_options__: list[str] = ["created_by", "updated_by", "tenant"] - - name: Mapped[str] = mapped_column(String(64), nullable=False, comment='客户名称') - code: Mapped[str] = mapped_column(String(20), nullable=False, index=True, comment='客户编码') - - - @validates('name') - def validate_name(self, key: str, name: str) -> str: - """验证名称不为空""" - if not name or not name.strip(): - raise ValueError('名称不能为空') - return name - - @validates('code') - def validate_code(self, key: str, code: str) -> str: - """验证编码格式校验""" - if not code or not code.strip(): - raise ValueError('编码不能为空') - if not code.isalnum(): - raise ValueError('编码只能包含字母和数字') - return code diff --git a/backend/app/api/v1/module_system/customer/schema.py b/backend/app/api/v1/module_system/customer/schema.py deleted file mode 100644 index 915ca09b..00000000 --- a/backend/app/api/v1/module_system/customer/schema.py +++ /dev/null @@ -1,82 +0,0 @@ -# -*- coding: utf-8 -*- - -from typing import Optional -from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator -from fastapi import Query - -from app.core.base_schema import BaseSchema, UserBySchema, TenantSchema, CustomerSchema -from app.core.validator import DateTimeStr - - -class CustomerCreateSchema(BaseModel): - """新增模型""" - name: str = Field(..., description='客户名称') - code: Optional[str] = Field(default=None, description='客户编码') - status: str = Field(default="0", description="是否启用(0:启用 1:禁用)") - description: Optional[str] = Field(default=None, description="描述") - - @field_validator('name') - @classmethod - def _validate_name(cls, v: str) -> str: - v = v.strip() - if not v: - raise ValueError('名称不能为空') - return v - - @model_validator(mode='after') - def _after_validation(self): - """ - 核心业务规则校验 - """ - # 长度校验:名称最小长度 - if len(self.name) < 2 or len(self.name) > 64: - raise ValueError('名称长度必须在2-50个字符之间') - # 格式校验:名称只能包含字母、数字、下划线和中划线 - if not self.name.isalnum() and not all(c in '-_' for c in self.name): - raise ValueError('名称只能包含字母、数字、下划线和中划线') - return self - - -class CustomerUpdateSchema(CustomerCreateSchema): - """更新模型""" - ... - - -class CustomerOutSchema(CustomerCreateSchema, BaseSchema, UserBySchema): - """响应模型""" - model_config = ConfigDict(from_attributes=True) - - -class CustomerQueryParam: - """客户查询参数""" - - def __init__( - self, - name: str | None = Query(None, description="名称"), - description: str | None = Query(None, description="描述"), - status: str | None = Query(None, description="是否启用"), - created_time: list[DateTimeStr] | None = Query(None, description="创建时间范围", examples=["2025-01-01 00:00:00", "2025-12-31 23:59:59"]), - updated_time: list[DateTimeStr] | None = Query(None, description="更新时间范围", examples=["2025-01-01 00:00:00", "2025-12-31 23:59:59"]), - created_id: int | None = Query(None, description="创建人"), - updated_id: int | None = Query(None, description="更新人") - ) -> None: - # 模糊查询字段 - self.name = ("like", name) - if description: - self.description = ("like", description) - - # 精确查询字段 - if status: - self.status = ("eq", status) - - # 时间范围查询 - if created_time and len(created_time) == 2: - self.created_time = ("between", (created_time[0], created_time[1])) - if updated_time and len(updated_time) == 2: - self.updated_time = ("between", (updated_time[0], updated_time[1])) - - # 关联查询字段 - if created_id: - self.created_id = ("eq", created_id) - if updated_id: - self.updated_id = ("eq", updated_id) diff --git a/backend/app/api/v1/module_system/customer/service.py b/backend/app/api/v1/module_system/customer/service.py deleted file mode 100644 index 70f0372f..00000000 --- a/backend/app/api/v1/module_system/customer/service.py +++ /dev/null @@ -1,312 +0,0 @@ -# -*- coding: utf-8 -*- - -import io -from typing import Any, List, Dict, Optional -from fastapi import UploadFile -import pandas as pd - -from app.api.v1.module_system.tenant.crud import TenantCRUD -from app.core.base_schema import BatchSetAvailable -from app.core.exceptions import CustomException -from app.utils.excel_util import ExcelUtil -from app.core.logger import log - -from app.api.v1.module_system.auth.schema import AuthSchema -from .schema import CustomerCreateSchema, CustomerUpdateSchema, CustomerOutSchema, CustomerQueryParam -from .crud import CustomerCRUD - - -class CustomerService: - """ - 客户管理模块服务层 - """ - - @classmethod - async def detail_service(cls, auth: AuthSchema, id: int) -> Dict: - """ - 详情 - - 参数: - - auth (AuthSchema): 认证信息模型 - - id (int): 客户ID - - 返回: - - Dict: 客户模型实例字典 - """ - obj = await CustomerCRUD(auth).get_by_id_crud(id=id) - if not obj: - raise CustomException(msg="该数据不存在") - return CustomerOutSchema.model_validate(obj).model_dump() - - @classmethod - async def list_service(cls, auth: AuthSchema, search: Optional[CustomerQueryParam] = None, order_by: Optional[List[Dict[str, str]]] = None) -> List[Dict]: - """ - 列表查询 - - 参数: - - auth (AuthSchema): 认证信息模型 - - search (Optional[CustomerQueryParam]): 查询参数 - - order_by (Optional[List[Dict[str, str]]]): 排序参数 - - 返回: - - List[Dict]: 客户模型实例字典列表 - """ - search_dict = search.__dict__ if search else None - obj_list = await CustomerCRUD(auth).list_crud(search=search_dict, order_by=order_by) - return [CustomerOutSchema.model_validate(obj).model_dump() for obj in obj_list] - - @classmethod - async def page_service(cls, auth: AuthSchema, page_no: int, page_size: int, search: Optional[CustomerQueryParam] = None, order_by: Optional[List[Dict[str, str]]] = None) -> Dict: - """ - 分页查询 - - 参数: - - auth (AuthSchema): 认证信息模型 - - page_no (int): 页码 - - page_size (int): 每页数量 - - search (Optional[CustomerQueryParam]): 查询参数 - - order_by (Optional[List[Dict[str, str]]]): 排序参数 - - 返回: - - Dict: 分页数据 - """ - search_dict = search.__dict__ if search else {} - order_by_list = order_by or [{'id': 'asc'}] - offset = (page_no - 1) * page_size - - result = await CustomerCRUD(auth).page_crud( - offset=offset, - limit=page_size, - order_by=order_by_list, - search=search_dict - ) - return result - - @classmethod - async def create_service(cls, auth: AuthSchema, data: CustomerCreateSchema) -> Dict: - """ - 创建 - - 参数: - - auth (AuthSchema): 认证信息模型 - - data (CustomerCreateSchema): 客户创建模型 - - 返回: - - Dict: 客户模型实例字典 - """ - obj = await CustomerCRUD(auth).get(name=data.name) - if obj: - raise CustomException(msg='创建失败,名称已存在') - obj = await CustomerCRUD(auth).get(code=data.code) - if obj: - raise CustomException(msg='创建失败,编码已存在') - obj = await CustomerCRUD(auth).create_crud(data=data) - return CustomerOutSchema.model_validate(obj).model_dump() - - @classmethod - async def update_service(cls, auth: AuthSchema, id: int, data: CustomerUpdateSchema) -> Dict: - """ - 更新 - - 参数: - - auth (AuthSchema): 认证信息模型 - - id (int): 客户ID - - data (CustomerUpdateSchema): 客户更新模型 - - 返回: - - Dict: 客户模型实例字典 - """ - # 检查数据是否存在 - obj = await CustomerCRUD(auth).get_by_id_crud(id=id) - if not obj: - raise CustomException(msg='更新失败,该数据不存在') - - # 检查名称是否重复 - exist_obj = await CustomerCRUD(auth).get(name=data.name) - if exist_obj and exist_obj.id != id: - raise CustomException(msg='更新失败,名称重复') - - obj = await CustomerCRUD(auth).update_crud(id=id, data=data) - return CustomerOutSchema.model_validate(obj).model_dump() - - @classmethod - async def delete_service(cls, auth: AuthSchema, ids: List[int]) -> None: - """ - 删除 - - 参数: - - auth (AuthSchema): 认证信息模型 - - ids (List[int]): 客户ID列表 - - 返回: - - None - """ - if len(ids) < 1: - raise CustomException(msg='删除失败,删除对象不能为空') - - # 检查所有要删除的数据是否存在 - for id in ids: - obj = await CustomerCRUD(auth).get_by_id_crud(id=id) - if not obj: - raise CustomException(msg=f'删除失败,ID为{id}的数据不存在') - - await CustomerCRUD(auth).delete_crud(ids=ids) - - @classmethod - async def set_available_service(cls, auth: AuthSchema, data: BatchSetAvailable) -> None: - """ - 批量设置状态 - - 参数: - - auth (AuthSchema): 认证信息模型 - - data (BatchSetAvailable): 批量设置状态模型 - - 返回: - - None - """ - await CustomerCRUD(auth).set_available_crud(ids=data.ids, status=data.status) - - @classmethod - async def batch_export_service(cls, obj_list: List[Dict[str, Any]]) -> bytes: - """ - 批量导出 - - 参数: - - obj_list (List[Dict[str, Any]]): 客户模型实例字典列表 - - 返回: - - bytes: Excel文件字节流 - """ - mapping_dict = { - 'id': '编号', - 'name': '名称', - 'code': '编码', - 'status': '状态', - 'description': '备注', - 'created_time': '创建时间', - 'updated_time': '更新时间', - 'creator': '创建者', - 'code': '编码', - } - - # 复制数据并转换状态 - data = obj_list.copy() - for item in data: - # 处理状态 - item['status'] = '启用' if item.get('status') == '0' else '停用' - # 处理创建者 - creator_info = item.get('creator') - if isinstance(creator_info, dict): - item['creator'] = creator_info.get('name', '未知') - else: - item['creator'] = '未知' - - return ExcelUtil.export_list2excel(list_data=data, mapping_dict=mapping_dict) - - @classmethod - async def batch_import_service(cls, auth: AuthSchema, file: UploadFile, update_support: bool = False) -> str: - """ - 批量导入 - - 参数: - - auth (AuthSchema): 认证信息模型 - - file (UploadFile): 上传的Excel文件 - - update_support (bool): 是否支持更新存在数据 - - 返回: - - str: 导入结果信息 - """ - - header_dict = { - '名称': 'name', - '状态': 'status', - '描述': 'description' - } - - try: - # 读取Excel文件 - contents = await file.read() - df = pd.read_excel(io.BytesIO(contents)) - await file.close() - - if df.empty: - raise CustomException(msg="导入文件为空") - - # 检查表头是否完整 - missing_headers = [header for header in header_dict.keys() if header not in df.columns] - if missing_headers: - raise CustomException(msg=f"导入文件缺少必要的列: {', '.join(missing_headers)}") - - # 重命名列名 - df.rename(columns=header_dict, inplace=True) - - # 验证必填字段 - required_fields = ['name', 'status'] - for field in required_fields: - missing_rows = df[df[field].isnull()].index.tolist() - raise CustomException(msg=f"{[k for k,v in header_dict.items() if v == field][0]}不能为空,第{[i+1 for i in missing_rows]}行") - - error_msgs = [] - success_count = 0 - count = 0 - - # 处理每一行数据 - for index, row in df.iterrows(): - count += 1 - try: - # 数据转换前的类型检查 - try: - status = True if row['status'] == '正常' else False - except ValueError: - error_msgs.append(f"第{count}行: 状态必须是'正常'或'停用'") - continue - - # 构建用户数据 - data = { - "name": str(row['name']), - "status": status, - "description": str(row['description']), - } - - # 处理用户导入 - exists_obj = await TenantCRUD(auth).get(name=data["name"]) - if exists_obj: - if update_support: - await TenantCRUD(auth).update(id=exists_obj.id, data=data) - success_count += 1 - else: - error_msgs.append(f"第{count}行: 对象 {data['name']} 已存在") - else: - await TenantCRUD(auth).create(data=data) - success_count += 1 - - except Exception as e: - error_msgs.append(f"第{count}行: {str(e)}") - continue - - # 返回详细的导入结果 - result = f"成功导入 {success_count} 条数据" - if error_msgs: - result += "\n错误信息:\n" + "\n".join(error_msgs) - return result - - except Exception as e: - log.error(f"批量导入用户失败: {str(e)}") - raise CustomException(msg=f"导入失败: {str(e)}") - - @classmethod - async def import_template_download_service(cls) -> bytes: - """ - 下载导入模板 - - 返回: - - bytes: Excel文件字节流 - """ - header_list = ['名称', '状态', '描述'] - selector_header_list = ['状态'] - option_list = [{'状态': ['正常', '停用']}] - return ExcelUtil.get_excel_template( - header_list=header_list, - selector_header_list=selector_header_list, - option_list=option_list - ) \ No newline at end of file diff --git a/backend/app/api/v1/module_system/tenant/__init__.py b/backend/app/api/v1/module_system/tenant/__init__.py deleted file mode 100644 index 633f8661..00000000 --- a/backend/app/api/v1/module_system/tenant/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -# -*- coding: utf-8 -*- - diff --git a/backend/app/api/v1/module_system/tenant/controller.py b/backend/app/api/v1/module_system/tenant/controller.py deleted file mode 100644 index b54e005a..00000000 --- a/backend/app/api/v1/module_system/tenant/controller.py +++ /dev/null @@ -1,215 +0,0 @@ -# -*- coding: utf-8 -*- - -from fastapi import APIRouter, Body, Depends, Path, UploadFile -from fastapi.responses import JSONResponse, StreamingResponse -import urllib.parse - -from app.common.response import StreamResponse, SuccessResponse -from app.utils.common_util import bytes2file_response -from app.core.base_params import PaginationQueryParam -from app.core.dependencies import AuthPermission -from app.core.router_class import OperationLogRoute -from app.core.base_schema import BatchSetAvailable -from app.core.logger import log - -from app.api.v1.module_system.auth.schema import AuthSchema -from .service import TenantService -from .schema import ( - TenantCreateSchema, - TenantUpdateSchema, - TenantQueryParam -) - - -TenantRouter = APIRouter(route_class=OperationLogRoute, prefix="/tenant", tags=["租户模块"]) - -@TenantRouter.get("/detail/{id}", summary="获取租户详情", description="获取租户详情") -async def get_obj_detail_controller( - id: int = Path(..., description="租户ID"), - auth: AuthSchema = Depends(AuthPermission(["module_system:tenant:detail"])) -) -> JSONResponse: - """ - 获取租户详情 - - 参数: - - id (int): 租户ID - - auth (AuthSchema): 认证信息模型 - - 返回: - - JSONResponse: 包含租户详情的JSON响应 - """ - result_dict = await TenantService.detail_service(id=id, auth=auth) - log.info(f"获取租户详情成功 {id}") - return SuccessResponse(data=result_dict, msg="获取租户详情成功") - -@TenantRouter.get("/list", summary="查询租户列表", description="查询租户列表") -async def get_obj_list_controller( - page: PaginationQueryParam = Depends(), - search: TenantQueryParam = Depends(), - auth: AuthSchema = Depends(AuthPermission(["module_system:tenant:query"])) -) -> JSONResponse: - """ - 查询租户列表 - - 参数: - - page (PaginationQueryParam): 分页查询参数 - - search (TenantQueryParam): 查询参数 - - auth (AuthSchema): 认证信息模型 - - 返回: - - JSONResponse: 包含租户列表分页信息的JSON响应 - """ - # 使用数据库分页而不是应用层分页 - result_dict = await TenantService.page_service( - auth=auth, - page_no=page.page_no if page.page_no is not None else 1, - page_size=page.page_size if page.page_size is not None else 10, - search=search, - order_by=page.order_by - ) - log.info("查询租户列表成功") - return SuccessResponse(data=result_dict, msg="查询租户列表成功") - -@TenantRouter.post("/create", summary="创建租户", description="创建租户") -async def create_obj_controller( - data: TenantCreateSchema, - auth: AuthSchema = Depends(AuthPermission(["module_system:tenant:create"])) -) -> JSONResponse: - """ - 创建租户 - - 参数: - - data (TenantCreateSchema): 租户创建模型 - - auth (AuthSchema): 认证信息模型 - - 返回: - - JSONResponse: 包含创建租户详情的JSON响应 - """ - result_dict = await TenantService.create_service(auth=auth, data=data) - log.info(f"创建租户成功: {result_dict.get('name')}") - return SuccessResponse(data=result_dict, msg="创建租户成功") - -@TenantRouter.put("/update/{id}", summary="修改租户", description="修改租户") -async def update_obj_controller( - data: TenantUpdateSchema, - id: int = Path(..., description="租户ID"), - auth: AuthSchema = Depends(AuthPermission(["module_system:tenant:update"])) -) -> JSONResponse: - """ - 修改租户 - - 参数: - - data (TenantUpdateSchema): 租户更新模型 - - id (int): 租户ID - - auth (AuthSchema): 认证信息模型 - - 返回: - - JSONResponse: 包含修改租户详情的JSON响应 - """ - result_dict = await TenantService.update_service(auth=auth, id=id, data=data) - log.info(f"修改租户成功: {result_dict.get('name')}") - return SuccessResponse(data=result_dict, msg="修改租户成功") - -@TenantRouter.delete("/delete", summary="删除租户", description="删除租户") -async def delete_obj_controller( - ids: list[int] = Body(..., description="ID列表"), - auth: AuthSchema = Depends(AuthPermission(["module_system:tenant:delete"])) -) -> JSONResponse: - """ - 删除租户 - - 参数: - - ids (list[int]): 租户ID列表 - - auth (AuthSchema): 认证信息模型 - - 返回: - - JSONResponse: 包含删除租户详情的JSON响应 - """ - await TenantService.delete_service(auth=auth, ids=ids) - log.info(f"删除租户成功: {ids}") - return SuccessResponse(msg="删除租户成功") - -@TenantRouter.patch("/available/setting", summary="批量修改租户状态", description="批量修改租户状态") -async def batch_set_available_obj_controller( - data: BatchSetAvailable, - auth: AuthSchema = Depends(AuthPermission(["module_system:tenant:patch"])) -) -> JSONResponse: - """ - 批量修改租户状态 - - 参数: - - data (BatchSetAvailable): 批量修改租户状态模型 - - auth (AuthSchema): 认证信息模型 - - 返回: - - JSONResponse: 包含批量修改租户状态详情的JSON响应 - """ - await TenantService.set_available_service(auth=auth, data=data) - log.info(f"批量修改租户状态成功: {data.ids}") - return SuccessResponse(msg="批量修改租户状态成功") - -@TenantRouter.post('/export', summary="导出租户", description="导出租户") -async def export_obj_list_controller( - search: TenantQueryParam = Depends(), - auth: AuthSchema = Depends(AuthPermission(["module_system:tenant:export"])) -) -> StreamingResponse: - """ - 导出租户 - - 参数: - - search (TenantQueryParam): 查询参数 - - auth (AuthSchema): 认证信息模型 - - 返回: - - StreamingResponse: 包含租户列表的Excel文件流响应 - """ - result_dict_list = await TenantService.list_service(search=search, auth=auth) - export_result = await TenantService.batch_export_service(obj_list=result_dict_list) - log.info('导出租户成功') - - return StreamResponse( - data=bytes2file_response(export_result), - media_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', - headers={ - 'Content-Disposition': 'attachment; filename=example.xlsx' - } - ) - -@TenantRouter.post('/import', summary="导入租户", description="导入租户") -async def import_obj_list_controller( - file: UploadFile, - auth: AuthSchema = Depends(AuthPermission(["module_system:tenant:import"])) -) -> JSONResponse: - """ - 导入租户 - - 参数: - - file (UploadFile): 导入的Excel文件 - - auth (AuthSchema): 认证信息模型 - - 返回: - - JSONResponse: 包含导入租户详情的JSON响应 - """ - batch_import_result = await TenantService.batch_import_service(file=file, auth=auth, update_support=True) - log.info(f"导入租户成功: {batch_import_result}") - return SuccessResponse(data=batch_import_result, msg="导入租户成功") - -@TenantRouter.post('/download/template', summary="获取租户导入模板", description="获取租户导入模板", dependencies=[Depends(AuthPermission(["module_system:tenant:download"]))]) -async def export_obj_template_controller() -> StreamingResponse: - """ - 获取租户导入模板 - - 返回: - - StreamingResponse: 包含租户导入模板的Excel文件流响应 - """ - example_import_template_result = await TenantService.import_template_download_service() - log.info('获取租户导入模板成功') - - return StreamResponse( - data=bytes2file_response(example_import_template_result), - media_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', - headers={ - 'Content-Disposition': f'attachment; filename={urllib.parse.quote("租户导入模板.xlsx")}', - 'Access-Control-Expose-Headers': 'Content-Disposition' - } - ) \ No newline at end of file diff --git a/backend/app/api/v1/module_system/tenant/crud.py b/backend/app/api/v1/module_system/tenant/crud.py deleted file mode 100644 index 269104e0..00000000 --- a/backend/app/api/v1/module_system/tenant/crud.py +++ /dev/null @@ -1,125 +0,0 @@ -# -*- coding: utf-8 -*- - -from typing import Dict, List, Optional, Sequence, Union, Any - -from app.core.base_crud import CRUDBase - -from app.api.v1.module_system.auth.schema import AuthSchema -from .model import TenantModel -from .schema import TenantCreateSchema, TenantUpdateSchema, TenantOutSchema - - -class TenantCRUD(CRUDBase[TenantModel, TenantCreateSchema, TenantUpdateSchema]): - """租户数据层""" - - def __init__(self, auth: AuthSchema) -> None: - """ - 初始化CRUD数据层 - - 参数: - - auth (AuthSchema): 认证信息模型 - """ - super().__init__(model=TenantModel, auth=auth) - - async def get_by_id_crud(self, id: int, preload: Optional[List[Union[str, Any]]] = None) -> Optional[TenantModel]: - """ - 详情 - - 参数: - - id (int): 租户ID - - preload (Optional[List[Union[str, Any]]]): 预加载关系,未提供时使用模型默认项 - - 返回: - - Optional[TenantModel]: 租户模型实例或None - """ - return await self.get(id=id, preload=preload) - - async def list_crud(self, search: Optional[Dict] = None, order_by: Optional[List[Dict[str, str]]] = None, preload: Optional[List[Union[str, Any]]] = None) -> Sequence[TenantModel]: - """ - 列表查询 - - 参数: - - search (Optional[Dict]): 查询参数 - - order_by (Optional[List[Dict[str, str]]]): 排序参数 - - preload (Optional[List[Union[str, Any]]]): 预加载关系,未提供时使用模型默认项 - - 返回: - - Sequence[TenantModel]: 租户模型实例序列 - """ - return await self.list(search=search, order_by=order_by, preload=preload) - - async def create_crud(self, data: TenantCreateSchema) -> Optional[TenantModel]: - """ - 创建 - - 参数: - - data (TenantCreateSchema): 租户创建模型 - - 返回: - - Optional[TenantModel]: 租户模型实例或None - """ - return await self.create(data=data) - - async def update_crud(self, id: int, data: TenantUpdateSchema) -> Optional[TenantModel]: - """ - 更新 - - 参数: - - id (int): 租户ID - - data (TenantUpdateSchema): 租户更新模型 - - 返回: - - Optional[TenantModel]: 租户模型实例或None - """ - return await self.update(id=id, data=data) - - async def delete_crud(self, ids: List[int]) -> None: - """ - 批量删除 - - 参数: - - ids (List[int]): 租户ID列表 - - 返回: - - None - """ - return await self.delete(ids=ids) - - async def set_available_crud(self, ids: List[int], status: str) -> None: - """ - 批量设置可用状态 - - 参数: - - ids (List[int]): 租户ID列表 - - status (bool): 可用状态 - - 返回: - - None - """ - return await self.set(ids=ids, status=status) - - async def page_crud(self, offset: int, limit: int, order_by: Optional[List[Dict[str, str]]] = None, search: Optional[Dict] = None, preload: Optional[List[Union[str, Any]]] = None) -> Dict: - """ - 分页查询 - - 参数: - - offset (int): 偏移量 - - limit (int): 每页数量 - - order_by (Optional[List[Dict[str, str]]]): 排序参数 - - search (Optional[Dict]): 查询参数 - - preload (Optional[List[Union[str, Any]]]): 预加载关系,未提供时使用模型默认项 - - 返回: - - Dict: 分页数据 - """ - order_by_list = order_by or [{'id': 'asc'}] - search_dict = search or {} - - return await self.page( - offset=offset, - limit=limit, - order_by=order_by_list, - search=search_dict, - out_schema=TenantOutSchema, - preload=preload - ) diff --git a/backend/app/api/v1/module_system/tenant/model.py b/backend/app/api/v1/module_system/tenant/model.py deleted file mode 100644 index e4e61c2b..00000000 --- a/backend/app/api/v1/module_system/tenant/model.py +++ /dev/null @@ -1,34 +0,0 @@ -# -*- coding: utf-8 -*- - -from sqlalchemy import String -from sqlalchemy.orm import Mapped, mapped_column, validates - -from app.core.base_model import ModelMixin - - -class TenantModel(ModelMixin): - """ - 租户模型 - """ - __tablename__: str = 'sys_tenant' - __table_args__: dict[str, str] = {'comment': '租户表'} - - name: Mapped[str] = mapped_column(String(64), nullable=False, unique=True, comment='租户名称') - code: Mapped[str] = mapped_column(String(20), nullable=False, unique=True, comment='租户编码') - domain: Mapped[str | None] = mapped_column(String(100), unique=True, comment="租户域名") - - @validates('name') - def validate_name(self, key: str, name: str) -> str: - """验证名称不为空""" - if not name or not name.strip(): - raise ValueError('名称不能为空') - return name - - @validates('code') - def validate_code(self, key: str, code: str) -> str: - """验证编码格式校验""" - if not code or not code.strip(): - raise ValueError('编码不能为空') - if not code.isalnum(): - raise ValueError('编码只能包含字母和数字') - return code diff --git a/backend/app/api/v1/module_system/tenant/schema.py b/backend/app/api/v1/module_system/tenant/schema.py deleted file mode 100644 index 5371be9d..00000000 --- a/backend/app/api/v1/module_system/tenant/schema.py +++ /dev/null @@ -1,78 +0,0 @@ -# -*- coding: utf-8 -*- - -from fastapi import Query -from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator - -from app.core.base_schema import BaseSchema -from app.core.validator import DateTimeStr - - -class TenantCreateSchema(BaseModel): - """新增模型""" - name: str = Field(..., description='租户名称') - code: str | None = Field(default=None, description='租户编码') - domain: str | None = Field(default=None, description='租户域名') - status: str = Field(default="0", description="是否启用(0:启用 1:禁用)") - description: str | None = Field(default=None, description="描述") - - @field_validator('name') - @classmethod - def _validate_name(cls, v: str) -> str: - v = v.strip() - if not v: - raise ValueError('名称不能为空') - return v - - @model_validator(mode='after') - def _after_validation(self): - """ - 核心业务规则校验 - """ - # 长度校验:名称最小长度 - if len(self.name) < 2 or len(self.name) > 64: - raise ValueError('名称长度必须在2-50个字符之间') - # 格式校验:名称只能包含字母、数字、下划线和中划线 - if not self.name.isalnum() and not all(c in '-_' for c in self.name): - raise ValueError('名称只能包含字母、数字、下划线和中划线') - - return self - - -class TenantUpdateSchema(TenantCreateSchema): - """更新模型""" - ... - -class TenantOutSchema(TenantCreateSchema, BaseSchema): - """响应模型""" - model_config = ConfigDict(from_attributes=True) - - -class TenantQueryParam: - """租户查询参数""" - - def __init__( - self, - name: str | None = Query(None, description="名称"), - description: str | None = Query(None, description="描述"), - status: str | None = Query(None, description="是否启用"), - created_time: list[DateTimeStr] | None = Query(None, description="创建时间范围", examples=["2025-01-01 00:00:00", "2025-12-31 23:59:59"]), - updated_time: list[DateTimeStr] | None = Query(None, description="更新时间范围", examples=["2025-01-01 00:00:00", "2025-12-31 23:59:59"]), - created_id: int | None = Query(None, description="创建人"), - updated_id: int | None = Query(None, description="更新人") - ) -> None: - # 模糊查询字段 - self.name = ("like", name) - if description: - self.description = ("like", description) - - # 精确查询字段 - if status: - self.status = ("eq", status) - - # 时间范围查询 - if created_time and len(created_time) == 2: - self.created_time = ("between", (created_time[0], created_time[1])) - if updated_time and len(updated_time) == 2: - self.updated_time = ("between", (updated_time[0], updated_time[1])) - - diff --git a/backend/app/api/v1/module_system/tenant/service.py b/backend/app/api/v1/module_system/tenant/service.py deleted file mode 100644 index ffc19a8c..00000000 --- a/backend/app/api/v1/module_system/tenant/service.py +++ /dev/null @@ -1,479 +0,0 @@ -# -*- coding: utf-8 -*- - -import io -import random -import string -from typing import Any, List, Dict, Optional -from fastapi import UploadFile -import pandas as pd - -from app.core.base_schema import BatchSetAvailable -from app.core.exceptions import CustomException -from app.utils.excel_util import ExcelUtil -from app.core.logger import log -from app.utils.hash_bcrpy_util import PwdUtil - -from app.api.v1.module_system.auth.schema import AuthSchema -from app.api.v1.module_system.user.crud import UserCRUD -from .schema import TenantCreateSchema, TenantUpdateSchema, TenantOutSchema, TenantQueryParam -from .crud import TenantCRUD - - -class TenantService: - """ - 租户管理模块服务层 - """ - - @classmethod - async def detail_service(cls, auth: AuthSchema, id: int) -> Dict: - """ - 详情 - - 参数: - - auth (AuthSchema): 认证信息模型 - - id (int): 租户ID - - 返回: - - Dict: 租户模型实例字典 - """ - obj = await TenantCRUD(auth).get_by_id_crud(id=id) - if not obj: - raise CustomException(msg="该数据不存在") - - # 获取租户详情基础数据 - result = TenantOutSchema.model_validate(obj).model_dump() - - return result - - @classmethod - async def list_service(cls, auth: AuthSchema, search: Optional[TenantQueryParam] = None, order_by: Optional[List[Dict[str, str]]] = None) -> List[Dict]: - """ - 列表查询 - - 参数: - - auth (AuthSchema): 认证信息模型 - - search (Optional[TenantQueryParam]): 查询参数 - - order_by (Optional[List[Dict[str, str]]]): 排序参数 - - 返回: - - List[Dict]: 租户模型实例字典列表 - """ - search_dict = search.__dict__ if search else None - obj_list = await TenantCRUD(auth).list_crud(search=search_dict, order_by=order_by) - return [TenantOutSchema.model_validate(obj).model_dump() for obj in obj_list] - - @classmethod - async def page_service(cls, auth: AuthSchema, page_no: int, page_size: int, search: Optional[TenantQueryParam] = None, order_by: Optional[List[Dict[str, str]]] = None) -> Dict: - """ - 分页查询 - - 参数: - - auth (AuthSchema): 认证信息模型 - - page_no (int): 页码 - - page_size (int): 每页数量 - - search (Optional[TenantQueryParam]): 查询参数 - - order_by (Optional[List[Dict[str, str]]]): 排序参数 - - 返回: - - Dict: 分页数据 - """ - search_dict = search.__dict__ if search else {} - order_by_list = order_by or [{'id': 'asc'}] - offset = (page_no - 1) * page_size - - result = await TenantCRUD(auth).page_crud( - offset=offset, - limit=page_size, - order_by=order_by_list, - search=search_dict - ) - return result - - @classmethod - async def create_service(cls, auth: AuthSchema, data: TenantCreateSchema) -> Dict: - """ - 创建 - - 参数: - - auth (AuthSchema): 认证信息模型 - - data (TenantCreateSchema): 租户创建模型 - - 返回: - - Dict: 租户模型实例字典 - """ - obj = await TenantCRUD(auth).get(name=data.name) - if obj: - raise CustomException(msg='创建失败,名称已存在') - obj = await TenantCRUD(auth).get(code=data.code) - if obj: - raise CustomException(msg='创建失败,编码已存在') - - # 创建租户 - tenant_obj = await TenantCRUD(auth).create_crud(data=data) - - # 自动创建租户初始管理员用户 - await cls._create_tenant_admin_user(auth, tenant_obj) - - return TenantOutSchema.model_validate(tenant_obj).model_dump() - - @classmethod - async def _create_tenant_admin_user(cls, auth: AuthSchema, tenant_obj) -> None: - """ - 为新创建的租户自动创建初始管理员用户 - - 参数: - - auth (AuthSchema): 认证信息模型 - - tenant_obj: 租户对象 - - 返回: - - None - """ - try: - # 生成初始管理员用户名(使用租户编码) - username = f"{tenant_obj.code}_admin" - - # 生成随机密码 - password_length = 12 - characters = string.ascii_letters + string.digits + "!@#$%^&*" - password = ''.join(random.choice(characters) for _ in range(password_length)) - - # 创建管理员用户数据 - admin_user_data = { - "username": username, - "password": PwdUtil.set_password_hash(password=password), - "name": f"{tenant_obj.name}管理员", - "tenant_id": tenant_obj.id, - "user_type": "1", # 租户管理员类型 - "status": True, - "created_id": auth.user.id if auth.user else None - } - - # 创建用户 - new_user = await UserCRUD(auth).create(data=admin_user_data) - - # 记录日志,包含临时密码信息(仅开发环境记录,生产环境应避免) - log.info(f"为租户[{tenant_obj.name}]创建初始管理员用户成功,用户名: {username},临时密码: {password}") - - except Exception as e: - log.error(f"为租户[{tenant_obj.name}]创建初始管理员用户失败: {str(e)}") - # 不中断租户创建流程,仅记录错误 - pass - - @classmethod - async def update_service(cls, auth: AuthSchema, id: int, data: TenantUpdateSchema) -> Dict: - """ - 更新 - - 参数: - - auth (AuthSchema): 认证信息模型 - - id (int): 租户ID - - data (TenantUpdateSchema): 租户更新模型 - - 返回: - - Dict: 租户模型实例字典 - """ - # 系统租户特殊处理 - if id == 1: - obj = await TenantCRUD(auth).update_crud(id=id, data=data) - log.info(f"系统租户配额设置已更新") - - return TenantOutSchema.model_validate(obj).model_dump() - - # 检查数据是否存在 - obj = await TenantCRUD(auth).get_by_id_crud(id=id) - if not obj: - raise CustomException(msg='更新失败,该数据不存在') - - # 检查名称是否重复 - exist_obj = await TenantCRUD(auth).get(name=data.name) - if exist_obj and exist_obj.id != id: - raise CustomException(msg='更新失败,名称重复') - - obj = await TenantCRUD(auth).update_crud(id=id, data=data) - return TenantOutSchema.model_validate(obj).model_dump() - - @classmethod - async def delete_service(cls, auth: AuthSchema, ids: List[int]) -> None: - """ - 删除 - - 参数: - - auth (AuthSchema): 认证信息模型 - - ids (List[int]): 租户ID列表 - - 返回: - - None - """ - if len(ids) < 1: - raise CustomException(msg='删除失败,删除对象不能为空') - - # 系统租户保护:不允许删除系统租户(id=1) - if 1 in ids: - raise CustomException(msg='系统租户不允许删除') - - # 检查所有要删除的数据是否存在 - for id in ids: - obj = await TenantCRUD(auth).get_by_id_crud(id=id) - if not obj: - raise CustomException(msg=f'删除失败,ID为{id}的数据不存在') - - await TenantCRUD(auth).delete_crud(ids=ids) - - @classmethod - async def set_available_service(cls, auth: AuthSchema, data: BatchSetAvailable) -> None: - """ - 批量设置状态 - - 参数: - - auth (AuthSchema): 认证信息模型 - - data (BatchSetAvailable): 批量设置状态模型 - - 返回: - - None - """ - # 系统租户保护:不允许禁用系统租户(id=1) - if data.status is False and 1 in data.ids: - raise CustomException(msg='系统租户不允许禁用') - - await TenantCRUD(auth).set_available_crud(ids=data.ids, status=data.status) - - @classmethod - async def batch_export_service(cls, obj_list: List[Dict[str, Any]]) -> bytes: - """ - 批量导出 - - 参数: - - obj_list (List[Dict[str, Any]]): 租户模型实例字典列表 - - 返回: - - bytes: Excel文件字节流 - """ - mapping_dict = { - 'id': '编号', - 'name': '名称', - 'code': '编码', - 'status': '状态', - 'description': '备注', - 'start_time': '开始时间', - 'end_time': '结束时间', - 'created_time': '创建时间', - 'updated_time': '更新时间', - 'created_id': '创建者', - } - - # 复制数据并转换状态 - data = obj_list.copy() - for item in data: - # 系统租户特殊标记 - if item.get('id') == 1: - item['name'] = f"{item.get('name')} [系统租户]" - - # 处理状态 - item['status'] = '启用' if item.get('status') == '0' else '停用' - - # 处理创建者 - creator_info = item.get('created_id') - if isinstance(creator_info, dict): - item['created_id'] = creator_info.get('name', '未知') - else: - item['created_id'] = '未知' - - # 限制导出数量,防止大数据量导出 - max_export_count = 1000 - if len(data) > max_export_count: - data = data[:max_export_count] - log.warning(f'导出数据超过{max_export_count}条限制,仅导出前{max_export_count}条') - - return ExcelUtil.export_list2excel(list_data=data, mapping_dict=mapping_dict) - - @classmethod - async def batch_import_service(cls, auth: AuthSchema, file: UploadFile, update_support: bool = False) -> str: - """ - 批量导入 - - 参数: - - auth (AuthSchema): 认证信息模型 - - file (UploadFile): 上传的Excel文件 - - update_support (bool): 是否支持更新存在数据 - - 返回: - - str: 导入结果信息 - """ - - header_dict = { - '名称': 'name', - '编码': 'code', - '状态': 'status', - '描述': 'description', - '开始时间': 'start_time', - '结束时间': 'end_time' - } - - try: - # 读取Excel文件 - contents = await file.read() - df = pd.read_excel(io.BytesIO(contents)) - await file.close() - - # 验证导入数量限制 - max_import_count = 100 - if len(df) > max_import_count: - raise CustomException(msg=f"单次导入不能超过{max_import_count}条数据") - - if df.empty: - raise CustomException(msg="导入文件为空") - - # 检查表头是否完整 - missing_headers = [header for header in header_dict.keys() if header not in df.columns] - if missing_headers: - raise CustomException(msg=f"导入文件缺少必要的列: {', '.join(missing_headers)}") - - # 重命名列名 - df.rename(columns=header_dict, inplace=True) - - # 验证必填字段 - required_fields = ['name', 'code', 'status'] - for field in required_fields: - missing_rows = df[df[field].isnull()].index.tolist() - if missing_rows: - field_name = [k for k,v in header_dict.items() if v == field][0] - error_rows = [i+1 for i in missing_rows] - raise CustomException(msg=f"{field_name}不能为空,第{error_rows}行") - - error_msgs = [] - success_count = 0 - count = 0 - processed_names = set() # 用于检测重复名称 - processed_codes = set() # 用于检测重复编码 - - # 处理每一行数据 - for index, row in df.iterrows(): - count += 1 - try: - # 数据转换前的类型检查 - try: - status = True if str(row['status']).strip() == '正常' else False - except ValueError: - error_msgs.append(f"第{count}行: 状态必须是'正常'或'停用'") - continue - - # 字段格式验证 - name = str(row['name']).strip() - if len(name) < 2 or len(name) > 64: - error_msgs.append(f"第{count}行: 租户名称长度必须在2-64个字符之间") - continue - - # 检查名称是否只包含允许的字符 - if not all(c.isalnum() or c in '-_' for c in name.replace(' ', '')): - error_msgs.append(f"第{count}行: 租户名称只能包含字母、数字、下划线、中划线和空格") - continue - - # 检查导入文件内的重复名称 - if name in processed_names: - error_msgs.append(f"第{count}行: 租户名称 '{name}' 在文件中重复") - continue - processed_names.add(name) - - # 处理编码 - code = str(row['code']).strip() - if code in processed_codes: - error_msgs.append(f"第{count}行: 租户编码 '{code}' 在文件中重复") - continue - processed_codes.add(code) - - # 构建租户数据 - data = { - "name": name, - "code": code, - "status": status, - "description": str(row['description']).strip(), - } - - - # 检查时间有效性 - if 'start_time' in data and 'end_time' in data and data['start_time'] > data['end_time']: - error_msgs.append(f"第{count}行: 开始时间不能晚于结束时间") - continue - - # 处理租户导入 - exists_obj = await TenantCRUD(auth).get(name=data["name"]) - if exists_obj: - # 系统租户保护 - if exists_obj.id == 1: - error_msgs.append(f"第{count}行: 系统租户不允许修改") - continue - - if update_support: - await TenantCRUD(auth).update(id=exists_obj.id, data=data) - success_count += 1 - else: - error_msgs.append(f"第{count}行: 租户 {data['name']} 已存在") - else: - # 检查编码是否已存在 - exists_code = await TenantCRUD(auth).get(code=data["code"]) - if exists_code: - error_msgs.append(f"第{count}行: 租户编码 '{data['code']}' 已存在") - continue - - # 创建租户 - new_tenant = await TenantCRUD(auth).create(data=data) - success_count += 1 - - # 自动创建租户管理员(如果导入数量不是特别大) - if success_count < 10: # 限制自动创建管理员的数量 - await cls._create_tenant_admin_user(auth, new_tenant) - else: - log.info(f"批量导入超过10个租户,跳过自动创建管理员用户") - - except Exception as e: - error_msgs.append(f"第{count}行: {str(e)}") - continue - - # 返回详细的导入结果 - result = f"成功导入 {success_count} 条数据" - if error_msgs: - result += "\n错误信息:\n" + "\n".join(error_msgs) - # 记录错误详情到日志 - log.error(f"租户批量导入错误详情: {error_msgs}") - - log.info(f"租户批量导入完成: 成功{success_count}条, 失败{len(error_msgs)}条") - return result - - except CustomException: - raise - except Exception as e: - log.error(f"批量导入租户失败: {str(e)}") - raise CustomException(msg=f"导入失败: {str(e)}") - - @classmethod - async def import_template_download_service(cls) -> bytes: - """ - 下载导入模板 - - 返回: - - bytes: Excel文件字节流 - """ - header_list = ['名称', '编码', '状态', '描述', '开始时间', '结束时间'] - selector_header_list = ['状态'] - option_list = [{'状态': ['正常', '停用']}] - - # 添加示例数据和说明 - sample_data = [ - ['测试租户1', 'TEST001', '正常', '这是一个测试租户', '', ''], - ['测试租户2', 'TEST002', '正常', '这是另一个测试租户', '', ''] - ] - - # 添加说明文本 - description = """导入说明: -1. 名称和编码为必填项,名称长度2-64个字符 -2. 编码如果不填写,系统会自动生成 -3. 状态只能选择'正常'或'停用' -4. 时间格式:YYYY-MM-DD HH:MM:SS或YYYY-MM-DD -5. 单次导入最多支持100条数据 -""" - - return ExcelUtil.get_excel_template( - header_list=header_list, - selector_header_list=selector_header_list, - option_list=option_list - ) \ No newline at end of file diff --git a/backend/app/core/base_model.py b/backend/app/core/base_model.py index a8547e29..c365eb62 100644 --- a/backend/app/core/base_model.py +++ b/backend/app/core/base_model.py @@ -9,8 +9,6 @@ from typing import TYPE_CHECKING if TYPE_CHECKING: from app.api.v1.module_system.user.model import UserModel - from app.api.v1.module_system.customer.model import CustomerModel - from app.api.v1.module_system.tenant.model import TenantModel from app.utils.common_util import uuid4_str @@ -61,12 +59,12 @@ class ModelMixin(MappedBase): __abstract__: bool = True # 基础字段 - id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True, comment='主键ID') - uuid: Mapped[str] = mapped_column(String(64), default=uuid4_str, nullable=False, unique=True, comment='UUID全局唯一标识') - status: Mapped[str] = mapped_column(String(10), default='0', nullable=False, comment="是否启用(0:启用 1:禁用)") - description: Mapped[str | None] = mapped_column(Text, default=None, nullable=True, comment="备注/描述") - created_time: Mapped[datetime] = mapped_column(DateTime, default=datetime.now, nullable=False, comment='创建时间') - updated_time: Mapped[datetime] = mapped_column(DateTime, default=datetime.now, onupdate=datetime.now, nullable=False, comment='更新时间') + id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True, comment='主键ID', index=True) + uuid: Mapped[str] = mapped_column(String(64), default=uuid4_str, nullable=False, unique=True, comment='UUID全局唯一标识', index=True) + status: Mapped[str] = mapped_column(String(10), default='0', nullable=False, comment="是否启用(0:启用 1:禁用)", index=True) + description: Mapped[str | None] = mapped_column(Text, default=None, nullable=True, comment="备注/描述", index=True) + created_time: Mapped[datetime] = mapped_column(DateTime, default=datetime.now, nullable=False, comment='创建时间', index=True) + updated_time: Mapped[datetime] = mapped_column(DateTime, default=datetime.now, onupdate=datetime.now, nullable=False, comment='更新时间', index=True) class UserMixin(MappedBase): @@ -118,62 +116,3 @@ class UserMixin(MappedBase): foreign_keys=lambda: cls.updated_id, uselist=False ) - - -class TenantMixin(MappedBase): - """ - 租户字段 Mixin - """ - __abstract__: bool = True - - tenant_id: Mapped[int | None] = mapped_column( - Integer, - ForeignKey('sys_tenant.id', ondelete="CASCADE", onupdate="CASCADE"), - nullable=False, - index=True, - comment="所属租户ID" - ) - - @declared_attr - def tenant(cls) -> Mapped["TenantModel"]: - """ - 租户关联关系(延迟加载,避免循环依赖) - """ - return relationship( - "TenantModel", - primaryjoin=f"{cls.__name__}.tenant_id == TenantModel.id", - lazy="selectin", - foreign_keys=lambda: [cls.tenant_id], - viewonly=True, - uselist=False - ) - - -class CustomerMixin(MappedBase): - """ - 客户隔离字段 Mixin - """ - __abstract__: bool = True - - customer_id: Mapped[int | None] = mapped_column( - Integer, - ForeignKey('sys_customer.id', ondelete="CASCADE", onupdate="CASCADE"), - default=None, - nullable=True, - index=True, - comment="所属客户ID(NULL表示租户级数据,>0表示客户级数据)" - ) - - @declared_attr - def customer(cls) -> Mapped["CustomerModel"]: - """ - 客户关联关系(延迟加载,避免循环依赖) - """ - return relationship( - "CustomerModel", - primaryjoin=f"{cls.__name__}.customer_id == CustomerModel.id", - lazy="selectin", - foreign_keys=lambda: [cls.customer_id], - viewonly=True, - uselist=False - ) \ No newline at end of file diff --git a/backend/app/scripts/data/sys_menu.json b/backend/app/scripts/data/sys_menu.json index 228bb526..3001067a 100644 --- a/backend/app/scripts/data/sys_menu.json +++ b/backend/app/scripts/data/sys_menu.json @@ -1520,395 +1520,11 @@ } ] }, - { - "name": "租户管理", - "type": 2, - "icon": "el-icon-Avatar", - "order": 10, - "permission": "module_system:tenant:query", - "route_name": "Tenant", - "route_path": "/system/tenant", - "component_path": "module_system/tenant/index", - "status": "0", - "keep_alive": true, - "hidden": false, - "always_show": false, - "title": "租户管理", - "params": null, - "affix": false, - "redirect": null, - "description": "初始化数据", - "children": [ - { - "name": "查询租户", - "type": 3, - "icon": null, - "order": 1, - "permission": "module_system:tenant:query", - "route_name": null, - "route_path": null, - "component_path": null, - "status": "0", - "keep_alive": true, - "hidden": false, - "always_show": false, - "title": "查询租户", - "params": null, - "affix": false, - "redirect": null, - "description": "初始化数据" - }, - { - "name": "创建租户", - "type": 3, - "icon": null, - "order": 2, - "permission": "module_system:tenant:create", - "route_name": null, - "route_path": null, - "component_path": null, - "status": "0", - "keep_alive": true, - "hidden": false, - "always_show": false, - "title": "创建租户", - "params": null, - "affix": false, - "redirect": null, - "description": "初始化数据" - }, - { - "name": "更新租户", - "type": 3, - "icon": null, - "order": 3, - "permission": "module_system:tenant:update", - "route_name": null, - "route_path": null, - "component_path": null, - "status": "0", - "keep_alive": true, - "hidden": false, - "always_show": false, - "title": "更新租户", - "params": null, - "affix": false, - "redirect": null, - "description": "初始化数据" - }, - { - "name": "删除租户", - "type": 3, - "icon": null, - "order": 4, - "permission": "module_system:tenant:delete", - "route_name": null, - "route_path": null, - "component_path": null, - "status": "0", - "keep_alive": true, - "hidden": false, - "always_show": false, - "title": "删除租户", - "params": null, - "affix": false, - "redirect": null, - "description": "初始化数据" - }, - { - "name": "批量修改租户状态", - "type": 3, - "icon": null, - "order": 5, - "permission": "module_system:tenant:patch", - "route_name": null, - "route_path": null, - "component_path": null, - "status": "0", - "keep_alive": true, - "hidden": false, - "always_show": false, - "title": "批量修改租户状态", - "params": null, - "affix": false, - "redirect": null, - "description": "初始化数据" - }, - { - "name": "导出租户", - "type": 3, - "icon": null, - "order": 6, - "permission": "module_system:tenant:export", - "route_name": null, - "route_path": null, - "component_path": null, - "status": "0", - "keep_alive": true, - "hidden": false, - "always_show": false, - "title": "导出租户", - "params": null, - "affix": false, - "redirect": null, - "description": "初始化数据" - }, - { - "name": "导入租户", - "type": 3, - "icon": null, - "order": 7, - "permission": "module_system:tenant:import", - "route_name": null, - "route_path": null, - "component_path": null, - "status": "0", - "keep_alive": true, - "hidden": false, - "always_show": false, - "title": "导入租户", - "params": null, - "affix": false, - "redirect": null, - "description": "初始化数据" - }, - { - "name": "下载导入租户模版", - "type": 3, - "icon": null, - "order": 8, - "permission": "module_system:tenant:download", - "route_name": null, - "route_path": null, - "component_path": null, - "status": "0", - "keep_alive": true, - "hidden": false, - "always_show": false, - "title": "下载导入租户模版", - "params": null, - "affix": false, - "redirect": null, - "description": "初始化数据" - }, - { - "name": "租户详情", - "type": 3, - "icon": null, - "order": 9, - "permission": "module_system:tenant:detail", - "route_name": null, - "route_path": null, - "component_path": null, - "status": "0", - "keep_alive": true, - "hidden": false, - "always_show": false, - "title": "租户详情", - "params": null, - "affix": false, - "redirect": null, - "description": "初始化数据" - } - ] - }, - { - "name": "客户管理", - "type": 2, - "icon": "el-icon-Avatar", - "order": 11, - "permission": "module_system:customer:query", - "route_name": "Customer", - "route_path": "/system/customer", - "component_path": "module_system/customer/index", - "status": "0", - "keep_alive": true, - "hidden": false, - "always_show": false, - "title": "客户管理", - "params": null, - "affix": false, - "redirect": null, - "description": "初始化数据", - "children": [ - { - "name": "查询客户", - "type": 3, - "icon": null, - "order": 1, - "permission": "module_system:customer:query", - "route_name": null, - "route_path": null, - "component_path": null, - "status": "0", - "keep_alive": true, - "hidden": false, - "always_show": false, - "title": "查询客户", - "params": null, - "affix": false, - "redirect": null, - "description": "初始化数据" - }, - { - "name": "创建客户", - "type": 3, - "icon": null, - "order": 2, - "permission": "module_system:customer:create", - "route_name": null, - "route_path": null, - "component_path": null, - "status": "0", - "keep_alive": true, - "hidden": false, - "always_show": false, - "title": "创建客户", - "params": null, - "affix": false, - "redirect": null, - "description": "初始化数据" - }, - { - "name": "更新客户", - "type": 3, - "icon": null, - "order": 3, - "permission": "module_system:customer:update", - "route_name": null, - "route_path": null, - "component_path": null, - "status": "0", - "keep_alive": true, - "hidden": false, - "always_show": false, - "title": "更新客户", - "params": null, - "affix": false, - "redirect": null, - "description": "初始化数据" - }, - { - "name": "删除客户", - "type": 3, - "icon": null, - "order": 4, - "permission": "module_system:customer:delete", - "route_name": null, - "route_path": null, - "component_path": null, - "status": "0", - "keep_alive": true, - "hidden": false, - "always_show": false, - "title": "删除客户", - "params": null, - "affix": false, - "redirect": null, - "description": "初始化数据" - }, - { - "name": "批量修改客户状态", - "type": 3, - "icon": null, - "order": 5, - "permission": "module_system:customer:patch", - "route_name": null, - "route_path": null, - "component_path": null, - "status": "0", - "keep_alive": true, - "hidden": false, - "always_show": false, - "title": "批量修改客户状态", - "params": null, - "affix": false, - "redirect": null, - "description": "初始化数据" - }, - { - "name": "导出客户", - "type": 3, - "icon": null, - "order": 6, - "permission": "module_system:customer:export", - "route_name": null, - "route_path": null, - "component_path": null, - "status": "0", - "keep_alive": true, - "hidden": false, - "always_show": false, - "title": "导出客户", - "params": null, - "affix": false, - "redirect": null, - "description": "初始化数据" - }, - { - "name": "导入客户", - "type": 3, - "icon": null, - "order": 7, - "permission": "module_system:customer:import", - "route_name": null, - "route_path": null, - "component_path": null, - "status": "0", - "keep_alive": true, - "hidden": false, - "always_show": false, - "title": "导入客户", - "params": null, - "affix": false, - "redirect": null, - "description": "初始化数据" - }, - { - "name": "下载导入客户模版", - "type": 3, - "icon": null, - "order": 8, - "permission": "module_system:customer:download", - "route_name": null, - "route_path": null, - "component_path": null, - "status": "0", - "keep_alive": true, - "hidden": false, - "always_show": false, - "title": "下载导入客户模版", - "params": null, - "affix": false, - "redirect": null, - "description": "初始化数据" - }, - { - "name": "客户详情", - "type": 3, - "icon": null, - "order": 9, - "permission": "module_system:customer:detail", - "route_name": null, - "route_path": null, - "component_path": null, - "status": "0", - "keep_alive": true, - "hidden": false, - "always_show": false, - "title": "客户详情", - "params": null, - "affix": false, - "redirect": null, - "description": "初始化数据" - } - ] - }, { "name": "令牌管理", "type": 2, "icon": "el-icon-Avatar", - "order": 12, + "order": 10, "permission": "module_system:token:query", "route_name": "Token", "route_path": "/system/token", diff --git a/backend/app/scripts/data/sys_tenant.json b/backend/app/scripts/data/sys_tenant.json deleted file mode 100644 index d9425597..00000000 --- a/backend/app/scripts/data/sys_tenant.json +++ /dev/null @@ -1,11 +0,0 @@ -[ - { - "name": "运行管理平台", - "code": "SYSTEM", - "domain": null, - "status": "0", - "start_time": null, - "end_time": null, - "description": "系统内置租户,用于管理平台全局配置和所有租户" - } -] \ No newline at end of file diff --git a/frontend/src/api/module_system/customer.ts b/frontend/src/api/module_system/customer.ts deleted file mode 100644 index b19cc95a..00000000 --- a/frontend/src/api/module_system/customer.ts +++ /dev/null @@ -1,101 +0,0 @@ -import request from "@/utils/request"; - -const API_PATH = "/system/customer"; - -const CustomerAPI = { - listCustomer(query: CustomerPageQuery) { - return request>>({ - url: `${API_PATH}/list`, - method: "get", - params: query, - }); - }, - - detailCustomer(query: number) { - return request>({ - url: `${API_PATH}/detail/${query}`, - method: "get", - }); - }, - - createCustomer(body: CustomerForm) { - return request({ - url: `${API_PATH}/create`, - method: "post", - data: body, - }); - }, - - updateCustomer(id: number, body: CustomerForm) { - return request({ - url: `${API_PATH}/update/${id}`, - method: "put", - data: body, - }); - }, - - deleteCustomer(body: number[]) { - return request({ - url: `${API_PATH}/delete`, - method: "delete", - data: body, - }); - }, - - batchCustomer(body: BatchType) { - return request({ - url: `${API_PATH}/available/setting`, - method: "patch", - data: body, - }); - }, - - exportCustomer(body: CustomerPageQuery) { - return request({ - url: `${API_PATH}/export`, - method: "post", - data: body, - responseType: "blob", - }); - }, - - downloadCustomer() { - return request({ - url: `${API_PATH}/download/template`, - method: "post", - responseType: "blob", - }); - }, - - importCustomer(body: FormData) { - return request({ - url: `${API_PATH}/import`, - method: "post", - data: body, - headers: { - "Content-Type": "multipart/form-data", - }, - }); - }, -}; - -export default CustomerAPI; - -export interface CustomerPageQuery extends PageQuery { - name?: string; - status?: string; - created_time?: string[]; -} - -export interface CustomerTable extends BaseType { - name?: string; - code?: string; - created_by?: CommonType; - updated_by?: CommonType; - tenant?: CommonType; -} - -export interface CustomerForm extends BaseFormType { - name?: string; - code?: string; -} diff --git a/frontend/src/api/module_system/tenant.ts b/frontend/src/api/module_system/tenant.ts deleted file mode 100644 index bc6be9a9..00000000 --- a/frontend/src/api/module_system/tenant.ts +++ /dev/null @@ -1,102 +0,0 @@ -import request from "@/utils/request"; - -const API_PATH = "/system/tenant"; - -const TenantAPI = { - listTenant(query: TenantPageQuery) { - return request>>({ - url: `${API_PATH}/list`, - method: "get", - params: query, - }); - }, - - detailTenant(query: number) { - return request>({ - url: `${API_PATH}/detail/${query}`, - method: "get", - }); - }, - - createTenant(body: TenantForm) { - return request({ - url: `${API_PATH}/create`, - method: "post", - data: body, - }); - }, - - updateTenant(id: number, body: TenantForm) { - return request({ - url: `${API_PATH}/update/${id}`, - method: "put", - data: body, - }); - }, - - deleteTenant(body: number[]) { - return request({ - url: `${API_PATH}/delete`, - method: "delete", - data: body, - }); - }, - - batchTenant(body: BatchType) { - return request({ - url: `${API_PATH}/available/setting`, - method: "patch", - data: body, - }); - }, - - exportTenant(body: TenantPageQuery) { - return request({ - url: `${API_PATH}/export`, - method: "post", - data: body, - responseType: "blob", - }); - }, - - downloadTenant() { - return request({ - url: `${API_PATH}/download/template`, - method: "post", - responseType: "blob", - }); - }, - - importTenant(body: FormData) { - return request({ - url: `${API_PATH}/import`, - method: "post", - data: body, - headers: { - "Content-Type": "multipart/form-data", - }, - }); - }, -}; - -export default TenantAPI; - -export interface TenantPageQuery extends PageQuery { - name?: string; - status?: string; - created_time?: string[]; -} - -export interface TenantTable extends BaseType { - name?: string; - code?: string; - start_time?: string; - end_time?: string; -} - -export interface TenantForm extends BaseFormType { - name?: string; - code?: string; - start_time?: string; - end_time?: string; -} diff --git a/frontend/src/types/global.d.ts b/frontend/src/types/global.d.ts index ccc29a34..d0fd58f3 100644 --- a/frontend/src/types/global.d.ts +++ b/frontend/src/types/global.d.ts @@ -7,6 +7,7 @@ declare global { data: T; msg: string; status_code: number; + success: boolean; } /** diff --git a/frontend/src/views/module_application/job/components/JobLogDrawer.vue b/frontend/src/views/module_application/job/components/JobLogDrawer.vue index 52168274..7f170947 100644 --- a/frontend/src/views/module_application/job/components/JobLogDrawer.vue +++ b/frontend/src/views/module_application/job/components/JobLogDrawer.vue @@ -31,8 +31,21 @@ - 查询 - 重置 + + 查询 + + + 重置 +