feat: 完善知识库、策略预览与OnlyOffice集成,增强后端启动依赖检查
This commit is contained in:
@@ -1,62 +1,62 @@
|
||||
from collections.abc import Generator
|
||||
from dataclasses import dataclass
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import Depends, Header, HTTPException, status
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.db.session import get_session_factory
|
||||
|
||||
|
||||
def get_db() -> Generator[Session, None, None]:
|
||||
db = get_session_factory()()
|
||||
try:
|
||||
yield db
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class CurrentUserContext:
|
||||
username: str
|
||||
name: str
|
||||
role_codes: list[str]
|
||||
is_admin: bool
|
||||
|
||||
|
||||
def get_current_user(
|
||||
x_auth_username: Annotated[str | None, Header()] = None,
|
||||
x_auth_name: Annotated[str | None, Header()] = None,
|
||||
x_auth_role_codes: Annotated[str | None, Header()] = None,
|
||||
x_auth_is_admin: Annotated[str | None, Header()] = None,
|
||||
) -> CurrentUserContext:
|
||||
role_codes = [item.strip() for item in (x_auth_role_codes or "").split(",") if item.strip()]
|
||||
is_admin = str(x_auth_is_admin or "").strip().lower() in {"1", "true", "yes", "on"}
|
||||
|
||||
username = (x_auth_username or "").strip()
|
||||
name = (x_auth_name or username).strip()
|
||||
|
||||
if not username and not name:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="请先登录后再访问知识库。",
|
||||
)
|
||||
|
||||
return CurrentUserContext(
|
||||
username=username or name,
|
||||
name=name or username,
|
||||
role_codes=role_codes,
|
||||
is_admin=is_admin,
|
||||
)
|
||||
|
||||
|
||||
def require_admin_user(
|
||||
current_user: Annotated[CurrentUserContext, Depends(get_current_user)],
|
||||
) -> CurrentUserContext:
|
||||
if current_user.is_admin or "manager" in current_user.role_codes:
|
||||
return current_user
|
||||
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail="只有管理员可以上传、删除或修改知识库文件。",
|
||||
)
|
||||
from collections.abc import Generator
|
||||
from dataclasses import dataclass
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import Depends, Header, HTTPException, status
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.db.session import get_session_factory
|
||||
|
||||
|
||||
def get_db() -> Generator[Session, None, None]:
|
||||
db = get_session_factory()()
|
||||
try:
|
||||
yield db
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class CurrentUserContext:
|
||||
username: str
|
||||
name: str
|
||||
role_codes: list[str]
|
||||
is_admin: bool
|
||||
|
||||
|
||||
def get_current_user(
|
||||
x_auth_username: Annotated[str | None, Header()] = None,
|
||||
x_auth_name: Annotated[str | None, Header()] = None,
|
||||
x_auth_role_codes: Annotated[str | None, Header()] = None,
|
||||
x_auth_is_admin: Annotated[str | None, Header()] = None,
|
||||
) -> CurrentUserContext:
|
||||
role_codes = [item.strip() for item in (x_auth_role_codes or "").split(",") if item.strip()]
|
||||
is_admin = str(x_auth_is_admin or "").strip().lower() in {"1", "true", "yes", "on"}
|
||||
|
||||
username = (x_auth_username or "").strip()
|
||||
name = (x_auth_name or username).strip()
|
||||
|
||||
if not username and not name:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="请先登录后再访问知识库。",
|
||||
)
|
||||
|
||||
return CurrentUserContext(
|
||||
username=username or name,
|
||||
name=name or username,
|
||||
role_codes=role_codes,
|
||||
is_admin=is_admin,
|
||||
)
|
||||
|
||||
|
||||
def require_admin_user(
|
||||
current_user: Annotated[CurrentUserContext, Depends(get_current_user)],
|
||||
) -> CurrentUserContext:
|
||||
if current_user.is_admin or "manager" in current_user.role_codes:
|
||||
return current_user
|
||||
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail="只有管理员可以上传、删除或修改知识库文件。",
|
||||
)
|
||||
|
||||
@@ -1,124 +1,124 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query, Request, status
|
||||
from fastapi.responses import FileResponse
|
||||
|
||||
from app.api.deps import CurrentUserContext, get_current_user, require_admin_user
|
||||
from app.schemas.knowledge import (
|
||||
KnowledgeActionResponse,
|
||||
KnowledgeDocumentDetailRead,
|
||||
KnowledgeLibraryRead,
|
||||
KnowledgeOnlyOfficeCallbackRead,
|
||||
KnowledgeOnlyOfficeConfigRead,
|
||||
)
|
||||
from app.services.knowledge import KnowledgeService
|
||||
|
||||
router = APIRouter(prefix="/knowledge")
|
||||
|
||||
|
||||
@router.get("/library", response_model=KnowledgeLibraryRead)
|
||||
def get_knowledge_library(
|
||||
_: Annotated[CurrentUserContext, Depends(get_current_user)],
|
||||
) -> KnowledgeLibraryRead:
|
||||
return KnowledgeService().list_library()
|
||||
|
||||
|
||||
@router.get("/documents/{document_id}", response_model=KnowledgeDocumentDetailRead)
|
||||
def get_knowledge_document(
|
||||
document_id: str,
|
||||
_: Annotated[CurrentUserContext, Depends(get_current_user)],
|
||||
) -> KnowledgeDocumentDetailRead:
|
||||
try:
|
||||
return KnowledgeService().get_document_detail(document_id)
|
||||
except FileNotFoundError as exc:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="知识库文件不存在。") from exc
|
||||
|
||||
|
||||
@router.get("/documents/{document_id}/onlyoffice-config", response_model=KnowledgeOnlyOfficeConfigRead)
|
||||
def get_knowledge_document_onlyoffice_config(
|
||||
document_id: str,
|
||||
current_user: Annotated[CurrentUserContext, Depends(get_current_user)],
|
||||
) -> KnowledgeOnlyOfficeConfigRead:
|
||||
try:
|
||||
return KnowledgeService().build_onlyoffice_config(document_id, current_user)
|
||||
except FileNotFoundError as exc:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="知识库文件不存在。") from exc
|
||||
except ValueError as exc:
|
||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(exc)) from exc
|
||||
|
||||
|
||||
@router.post("/documents", response_model=KnowledgeDocumentDetailRead, status_code=status.HTTP_201_CREATED)
|
||||
async def upload_knowledge_document(
|
||||
request: Request,
|
||||
folder: Annotated[str, Query(min_length=1)],
|
||||
filename: Annotated[str, Query(min_length=1)],
|
||||
current_user: Annotated[CurrentUserContext, Depends(require_admin_user)],
|
||||
) -> KnowledgeDocumentDetailRead:
|
||||
content = await request.body()
|
||||
try:
|
||||
return KnowledgeService().upload_document(folder, filename, content, current_user)
|
||||
except ValueError as exc:
|
||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(exc)) from exc
|
||||
|
||||
|
||||
@router.delete("/documents/{document_id}", response_model=KnowledgeActionResponse)
|
||||
def delete_knowledge_document(
|
||||
document_id: str,
|
||||
_: Annotated[CurrentUserContext, Depends(require_admin_user)],
|
||||
) -> KnowledgeActionResponse:
|
||||
try:
|
||||
KnowledgeService().delete_document(document_id)
|
||||
except FileNotFoundError as exc:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="知识库文件不存在。") from exc
|
||||
|
||||
return KnowledgeActionResponse(detail="知识库文件已删除。")
|
||||
|
||||
|
||||
@router.get("/documents/{document_id}/content")
|
||||
def get_knowledge_document_content(
|
||||
document_id: str,
|
||||
disposition: Annotated[str, Query(pattern="^(inline|attachment)$")] = "inline",
|
||||
_: Annotated[CurrentUserContext, Depends(get_current_user)] = None,
|
||||
) -> FileResponse:
|
||||
try:
|
||||
file_path, media_type, filename = KnowledgeService().get_document_content(document_id)
|
||||
except FileNotFoundError as exc:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="知识库文件不存在。") from exc
|
||||
|
||||
_ = disposition
|
||||
return FileResponse(file_path, media_type=media_type, filename=filename)
|
||||
|
||||
|
||||
@router.get("/documents/{document_id}/onlyoffice/content")
|
||||
def get_knowledge_document_onlyoffice_content(
|
||||
document_id: str,
|
||||
access_token: Annotated[str, Query(min_length=1)],
|
||||
) -> FileResponse:
|
||||
try:
|
||||
service = KnowledgeService()
|
||||
service.validate_onlyoffice_access_token(document_id, access_token)
|
||||
file_path, media_type, filename = service.get_document_content(document_id)
|
||||
except FileNotFoundError as exc:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="知识库文件不存在。") from exc
|
||||
except ValueError as exc:
|
||||
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail=str(exc)) from exc
|
||||
|
||||
return FileResponse(file_path, media_type=media_type, filename=filename)
|
||||
|
||||
|
||||
@router.post("/documents/{document_id}/onlyoffice/callback", response_model=KnowledgeOnlyOfficeCallbackRead)
|
||||
async def handle_knowledge_document_onlyoffice_callback(
|
||||
document_id: str,
|
||||
request: Request,
|
||||
) -> KnowledgeOnlyOfficeCallbackRead:
|
||||
payload = await request.json()
|
||||
try:
|
||||
KnowledgeService().handle_onlyoffice_callback(document_id, payload)
|
||||
except FileNotFoundError as exc:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="知识库文件不存在。") from exc
|
||||
except ValueError as exc:
|
||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(exc)) from exc
|
||||
|
||||
return KnowledgeOnlyOfficeCallbackRead()
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query, Request, status
|
||||
from fastapi.responses import FileResponse
|
||||
|
||||
from app.api.deps import CurrentUserContext, get_current_user, require_admin_user
|
||||
from app.schemas.knowledge import (
|
||||
KnowledgeActionResponse,
|
||||
KnowledgeDocumentDetailRead,
|
||||
KnowledgeLibraryRead,
|
||||
KnowledgeOnlyOfficeCallbackRead,
|
||||
KnowledgeOnlyOfficeConfigRead,
|
||||
)
|
||||
from app.services.knowledge import KnowledgeService
|
||||
|
||||
router = APIRouter(prefix="/knowledge")
|
||||
|
||||
|
||||
@router.get("/library", response_model=KnowledgeLibraryRead)
|
||||
def get_knowledge_library(
|
||||
_: Annotated[CurrentUserContext, Depends(get_current_user)],
|
||||
) -> KnowledgeLibraryRead:
|
||||
return KnowledgeService().list_library()
|
||||
|
||||
|
||||
@router.get("/documents/{document_id}", response_model=KnowledgeDocumentDetailRead)
|
||||
def get_knowledge_document(
|
||||
document_id: str,
|
||||
_: Annotated[CurrentUserContext, Depends(get_current_user)],
|
||||
) -> KnowledgeDocumentDetailRead:
|
||||
try:
|
||||
return KnowledgeService().get_document_detail(document_id)
|
||||
except FileNotFoundError as exc:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="知识库文件不存在。") from exc
|
||||
|
||||
|
||||
@router.get("/documents/{document_id}/onlyoffice-config", response_model=KnowledgeOnlyOfficeConfigRead)
|
||||
def get_knowledge_document_onlyoffice_config(
|
||||
document_id: str,
|
||||
current_user: Annotated[CurrentUserContext, Depends(get_current_user)],
|
||||
) -> KnowledgeOnlyOfficeConfigRead:
|
||||
try:
|
||||
return KnowledgeService().build_onlyoffice_config(document_id, current_user)
|
||||
except FileNotFoundError as exc:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="知识库文件不存在。") from exc
|
||||
except ValueError as exc:
|
||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(exc)) from exc
|
||||
|
||||
|
||||
@router.post("/documents", response_model=KnowledgeDocumentDetailRead, status_code=status.HTTP_201_CREATED)
|
||||
async def upload_knowledge_document(
|
||||
request: Request,
|
||||
folder: Annotated[str, Query(min_length=1)],
|
||||
filename: Annotated[str, Query(min_length=1)],
|
||||
current_user: Annotated[CurrentUserContext, Depends(require_admin_user)],
|
||||
) -> KnowledgeDocumentDetailRead:
|
||||
content = await request.body()
|
||||
try:
|
||||
return KnowledgeService().upload_document(folder, filename, content, current_user)
|
||||
except ValueError as exc:
|
||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(exc)) from exc
|
||||
|
||||
|
||||
@router.delete("/documents/{document_id}", response_model=KnowledgeActionResponse)
|
||||
def delete_knowledge_document(
|
||||
document_id: str,
|
||||
_: Annotated[CurrentUserContext, Depends(require_admin_user)],
|
||||
) -> KnowledgeActionResponse:
|
||||
try:
|
||||
KnowledgeService().delete_document(document_id)
|
||||
except FileNotFoundError as exc:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="知识库文件不存在。") from exc
|
||||
|
||||
return KnowledgeActionResponse(detail="知识库文件已删除。")
|
||||
|
||||
|
||||
@router.get("/documents/{document_id}/content")
|
||||
def get_knowledge_document_content(
|
||||
document_id: str,
|
||||
disposition: Annotated[str, Query(pattern="^(inline|attachment)$")] = "inline",
|
||||
_: Annotated[CurrentUserContext, Depends(get_current_user)] = None,
|
||||
) -> FileResponse:
|
||||
try:
|
||||
file_path, media_type, filename = KnowledgeService().get_document_content(document_id)
|
||||
except FileNotFoundError as exc:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="知识库文件不存在。") from exc
|
||||
|
||||
_ = disposition
|
||||
return FileResponse(file_path, media_type=media_type, filename=filename)
|
||||
|
||||
|
||||
@router.get("/documents/{document_id}/onlyoffice/content")
|
||||
def get_knowledge_document_onlyoffice_content(
|
||||
document_id: str,
|
||||
access_token: Annotated[str, Query(min_length=1)],
|
||||
) -> FileResponse:
|
||||
try:
|
||||
service = KnowledgeService()
|
||||
service.validate_onlyoffice_access_token(document_id, access_token)
|
||||
file_path, media_type, filename = service.get_document_content(document_id)
|
||||
except FileNotFoundError as exc:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="知识库文件不存在。") from exc
|
||||
except ValueError as exc:
|
||||
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail=str(exc)) from exc
|
||||
|
||||
return FileResponse(file_path, media_type=media_type, filename=filename)
|
||||
|
||||
|
||||
@router.post("/documents/{document_id}/onlyoffice/callback", response_model=KnowledgeOnlyOfficeCallbackRead)
|
||||
async def handle_knowledge_document_onlyoffice_callback(
|
||||
document_id: str,
|
||||
request: Request,
|
||||
) -> KnowledgeOnlyOfficeCallbackRead:
|
||||
payload = await request.json()
|
||||
try:
|
||||
KnowledgeService().handle_onlyoffice_callback(document_id, payload)
|
||||
except FileNotFoundError as exc:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="知识库文件不存在。") from exc
|
||||
except ValueError as exc:
|
||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(exc)) from exc
|
||||
|
||||
return KnowledgeOnlyOfficeCallbackRead()
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
from fastapi import APIRouter
|
||||
|
||||
from app.api.v1.endpoints.auth import router as auth_router
|
||||
from app.api.v1.endpoints.bootstrap import router as bootstrap_router
|
||||
from app.api.v1.endpoints.employees import router as employees_router
|
||||
from app.api.v1.endpoints.health import router as health_router
|
||||
from app.api.v1.endpoints.knowledge import router as knowledge_router
|
||||
from app.api.v1.endpoints.reimbursements import router as reimbursements_router
|
||||
from app.api.v1.endpoints.settings import router as settings_router
|
||||
|
||||
router = APIRouter()
|
||||
router.include_router(health_router, tags=["health"])
|
||||
router.include_router(bootstrap_router, tags=["bootstrap"])
|
||||
router.include_router(auth_router, tags=["auth"])
|
||||
router.include_router(knowledge_router, tags=["knowledge"])
|
||||
router.include_router(employees_router, prefix="/employees", tags=["employees"])
|
||||
router.include_router(reimbursements_router, prefix="/reimbursements", tags=["reimbursements"])
|
||||
router.include_router(settings_router, tags=["settings"])
|
||||
from fastapi import APIRouter
|
||||
|
||||
from app.api.v1.endpoints.auth import router as auth_router
|
||||
from app.api.v1.endpoints.bootstrap import router as bootstrap_router
|
||||
from app.api.v1.endpoints.employees import router as employees_router
|
||||
from app.api.v1.endpoints.health import router as health_router
|
||||
from app.api.v1.endpoints.knowledge import router as knowledge_router
|
||||
from app.api.v1.endpoints.reimbursements import router as reimbursements_router
|
||||
from app.api.v1.endpoints.settings import router as settings_router
|
||||
|
||||
router = APIRouter()
|
||||
router.include_router(health_router, tags=["health"])
|
||||
router.include_router(bootstrap_router, tags=["bootstrap"])
|
||||
router.include_router(auth_router, tags=["auth"])
|
||||
router.include_router(knowledge_router, tags=["knowledge"])
|
||||
router.include_router(employees_router, prefix="/employees", tags=["employees"])
|
||||
router.include_router(reimbursements_router, prefix="/reimbursements", tags=["reimbursements"])
|
||||
router.include_router(settings_router, tags=["settings"])
|
||||
|
||||
@@ -1,65 +1,65 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from fastapi import FastAPI
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
|
||||
from app.api.router import api_router
|
||||
from app.core.config import get_settings
|
||||
from app.core.logging import get_logger, setup_logging
|
||||
from app.middleware.logging import AccessLogMiddleware
|
||||
from app.services.employee import prepare_employee_directory
|
||||
from app.services.knowledge import prepare_knowledge_library
|
||||
|
||||
|
||||
def create_app() -> FastAPI:
|
||||
settings = get_settings()
|
||||
|
||||
setup_logging(
|
||||
level=settings.log_level,
|
||||
log_dir=settings.log_dir,
|
||||
enable_file=settings.log_file_enabled,
|
||||
)
|
||||
|
||||
logger = get_logger("app.main")
|
||||
logger.info(
|
||||
"Starting %s (env=%s, debug=%s)", settings.app_name, settings.app_env, settings.app_debug
|
||||
)
|
||||
|
||||
app = FastAPI(
|
||||
title=settings.app_name,
|
||||
debug=settings.app_debug,
|
||||
version="0.1.0",
|
||||
)
|
||||
|
||||
app.add_middleware(AccessLogMiddleware)
|
||||
|
||||
if settings.cors_origins:
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=settings.cors_origins,
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
app.include_router(api_router, prefix=settings.api_v1_prefix)
|
||||
|
||||
@app.get("/", tags=["root"])
|
||||
def root() -> dict[str, str]:
|
||||
return {"message": f"{settings.app_name} is running"}
|
||||
|
||||
@app.on_event("startup")
|
||||
def _on_startup() -> None:
|
||||
prepare_employee_directory()
|
||||
prepare_knowledge_library()
|
||||
logger.info(
|
||||
"Server ready - host=%s port=%s prefix=%s",
|
||||
settings.app_host,
|
||||
settings.app_port,
|
||||
settings.api_v1_prefix,
|
||||
)
|
||||
|
||||
return app
|
||||
|
||||
|
||||
app = create_app()
|
||||
from __future__ import annotations
|
||||
|
||||
from fastapi import FastAPI
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
|
||||
from app.api.router import api_router
|
||||
from app.core.config import get_settings
|
||||
from app.core.logging import get_logger, setup_logging
|
||||
from app.middleware.logging import AccessLogMiddleware
|
||||
from app.services.employee import prepare_employee_directory
|
||||
from app.services.knowledge import prepare_knowledge_library
|
||||
|
||||
|
||||
def create_app() -> FastAPI:
|
||||
settings = get_settings()
|
||||
|
||||
setup_logging(
|
||||
level=settings.log_level,
|
||||
log_dir=settings.log_dir,
|
||||
enable_file=settings.log_file_enabled,
|
||||
)
|
||||
|
||||
logger = get_logger("app.main")
|
||||
logger.info(
|
||||
"Starting %s (env=%s, debug=%s)", settings.app_name, settings.app_env, settings.app_debug
|
||||
)
|
||||
|
||||
app = FastAPI(
|
||||
title=settings.app_name,
|
||||
debug=settings.app_debug,
|
||||
version="0.1.0",
|
||||
)
|
||||
|
||||
app.add_middleware(AccessLogMiddleware)
|
||||
|
||||
if settings.cors_origins:
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=settings.cors_origins,
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
app.include_router(api_router, prefix=settings.api_v1_prefix)
|
||||
|
||||
@app.get("/", tags=["root"])
|
||||
def root() -> dict[str, str]:
|
||||
return {"message": f"{settings.app_name} is running"}
|
||||
|
||||
@app.on_event("startup")
|
||||
def _on_startup() -> None:
|
||||
prepare_employee_directory()
|
||||
prepare_knowledge_library()
|
||||
logger.info(
|
||||
"Server ready - host=%s port=%s prefix=%s",
|
||||
settings.app_host,
|
||||
settings.app_port,
|
||||
settings.api_v1_prefix,
|
||||
)
|
||||
|
||||
return app
|
||||
|
||||
|
||||
app = create_app()
|
||||
|
||||
@@ -1 +1 @@
|
||||
__all__ = ["employee", "knowledge", "reimbursement"]
|
||||
__all__ = ["employee", "knowledge", "reimbursement"]
|
||||
|
||||
@@ -1,72 +1,72 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class KnowledgeFolderRead(BaseModel):
|
||||
name: str
|
||||
count: int
|
||||
icon: str = "mdi mdi-folder"
|
||||
|
||||
|
||||
class KnowledgePreviewStatRead(BaseModel):
|
||||
label: str
|
||||
value: str
|
||||
|
||||
|
||||
class KnowledgePreviewBlockRead(BaseModel):
|
||||
heading: str
|
||||
lines: list[str] = Field(default_factory=list)
|
||||
|
||||
|
||||
class KnowledgePreviewPageRead(BaseModel):
|
||||
title: str
|
||||
subtitle: str
|
||||
stats: list[KnowledgePreviewStatRead] = Field(default_factory=list)
|
||||
blocks: list[KnowledgePreviewBlockRead] = Field(default_factory=list)
|
||||
|
||||
|
||||
class KnowledgeDocumentRead(BaseModel):
|
||||
id: str
|
||||
name: str
|
||||
folder: str
|
||||
tag: str
|
||||
time: str
|
||||
version: str
|
||||
state: str
|
||||
stateTone: str
|
||||
owner: str
|
||||
icon: str
|
||||
fileType: str
|
||||
fileTypeLabel: str
|
||||
summary: str
|
||||
mimeType: str
|
||||
extension: str
|
||||
sizeBytes: int
|
||||
canPreview: bool = False
|
||||
|
||||
|
||||
class KnowledgeDocumentDetailRead(KnowledgeDocumentRead):
|
||||
previewKind: str
|
||||
previewPages: list[KnowledgePreviewPageRead] = Field(default_factory=list)
|
||||
|
||||
|
||||
class KnowledgeOnlyOfficeConfigRead(BaseModel):
|
||||
documentServerUrl: str
|
||||
config: dict[str, Any] = Field(default_factory=dict)
|
||||
|
||||
|
||||
class KnowledgeOnlyOfficeCallbackRead(BaseModel):
|
||||
error: int = 0
|
||||
|
||||
|
||||
class KnowledgeLibraryRead(BaseModel):
|
||||
folders: list[KnowledgeFolderRead] = Field(default_factory=list)
|
||||
documents: list[KnowledgeDocumentRead] = Field(default_factory=list)
|
||||
|
||||
|
||||
class KnowledgeActionResponse(BaseModel):
|
||||
ok: bool = True
|
||||
detail: str
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class KnowledgeFolderRead(BaseModel):
|
||||
name: str
|
||||
count: int
|
||||
icon: str = "mdi mdi-folder"
|
||||
|
||||
|
||||
class KnowledgePreviewStatRead(BaseModel):
|
||||
label: str
|
||||
value: str
|
||||
|
||||
|
||||
class KnowledgePreviewBlockRead(BaseModel):
|
||||
heading: str
|
||||
lines: list[str] = Field(default_factory=list)
|
||||
|
||||
|
||||
class KnowledgePreviewPageRead(BaseModel):
|
||||
title: str
|
||||
subtitle: str
|
||||
stats: list[KnowledgePreviewStatRead] = Field(default_factory=list)
|
||||
blocks: list[KnowledgePreviewBlockRead] = Field(default_factory=list)
|
||||
|
||||
|
||||
class KnowledgeDocumentRead(BaseModel):
|
||||
id: str
|
||||
name: str
|
||||
folder: str
|
||||
tag: str
|
||||
time: str
|
||||
version: str
|
||||
state: str
|
||||
stateTone: str
|
||||
owner: str
|
||||
icon: str
|
||||
fileType: str
|
||||
fileTypeLabel: str
|
||||
summary: str
|
||||
mimeType: str
|
||||
extension: str
|
||||
sizeBytes: int
|
||||
canPreview: bool = False
|
||||
|
||||
|
||||
class KnowledgeDocumentDetailRead(KnowledgeDocumentRead):
|
||||
previewKind: str
|
||||
previewPages: list[KnowledgePreviewPageRead] = Field(default_factory=list)
|
||||
|
||||
|
||||
class KnowledgeOnlyOfficeConfigRead(BaseModel):
|
||||
documentServerUrl: str
|
||||
config: dict[str, Any] = Field(default_factory=dict)
|
||||
|
||||
|
||||
class KnowledgeOnlyOfficeCallbackRead(BaseModel):
|
||||
error: int = 0
|
||||
|
||||
|
||||
class KnowledgeLibraryRead(BaseModel):
|
||||
folders: list[KnowledgeFolderRead] = Field(default_factory=list)
|
||||
documents: list[KnowledgeDocumentRead] = Field(default_factory=list)
|
||||
|
||||
|
||||
class KnowledgeActionResponse(BaseModel):
|
||||
ok: bool = True
|
||||
detail: str
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user