"""方案计划文档管理系统(DMS)后端模块。""" from __future__ import annotations import logging from pathlib import Path from flask import Flask from dms.api.v1 import api_v1 from dms.api.v1.blueprint import ( register_dms_response_headers, register_dms_routing_error_handlers, ) from dms.config import ( load_dms_config, resolve_backup_config_path, resolve_env_path, ) from dms.extensions import db from dms.services.backup_service import BackupEngine, BackupOutcome from dms.services.document_relation_service import load_relation_presets from dms.services.ui_dictionary_service import load_ui_dictionary_config from dms.storage.paths import ensure_storage_directories from dms.services.document_converter import create_converter logger = logging.getLogger(__name__) def init_dms(app: Flask) -> None: """初始化独立DMS基础设施并注册 ``/api/v1`` Blueprint。""" app.config.from_mapping(load_dms_config()) app.extensions["dms_ui_dictionaries"] = load_ui_dictionary_config( app.config["DMS_UI_DICTIONARY_CONFIG_PATH"] ) app.extensions["dms_relation_presets"] = load_relation_presets( app.config["DMS_DOCUMENT_RELATION_PRESET_CONFIG_PATH"] ) app.extensions["dms_document_converter"] = create_converter(app.config) db.init_app(app) # 导入模型以确保SQLAlchemy metadata完整;不触发数据库连接。 import dms.models # noqa: F401 ensure_storage_directories(app.config["DMS_STORAGE_ROOT"]) app.register_blueprint(api_v1) register_dms_routing_error_handlers(app) register_dms_response_headers(app) backup_engine = BackupEngine( storage_root=Path(app.config["DMS_STORAGE_ROOT"]), business_timezone=app.config["DMS_BUSINESS_TIMEZONE"], database_url=app.config["SQLALCHEMY_DATABASE_URI"], backup_config_path=resolve_backup_config_path(), env_path=resolve_env_path(), ) app.extensions["dms_backup_engine"] = backup_engine backup_engine.start_scheduler( lambda outcome: _write_schedule_audit(app, outcome) ) def _write_schedule_audit(app: Flask, outcome: BackupOutcome) -> None: """定时备份终态审计落库(无请求上下文,独立短事务)。""" from dms.common.enums import AuditAction, AuditResult, AuditTarget from dms.models import AuditLog try: with app.app_context(): db.session.add( AuditLog( username="system", action_type=AuditAction.BACKUP_CREATE.value, target_type=AuditTarget.BACKUP.value, target_id=0, target_name=outcome.type, operation_result=( AuditResult.SUCCESS.value if outcome.success else AuditResult.FAILURE.value ), failure_reason=( None if outcome.success or outcome.error is None else outcome.error[:1000] ), operation_detail={ "type": outcome.type, "trigger": outcome.trigger, "backupId": outcome.backup_id, }, ) ) db.session.commit() except Exception: # noqa: BLE001 - 审计失败不否定已触发的备份 logger.warning("定时备份审计写入失败", exc_info=True) with app.app_context(): db.session.rollback() __all__ = ["init_dms"]