StringId:
  type: string
  pattern: '^[0-9]+$'
  description: 数据库BIGINT在JSON中的安全十进制字符串表达。

UtcDateTime:
  type: string
  format: date-time
  pattern: 'Z$'
  example: '2026-07-23T03:00:00.000Z'

RoleCode:
  type: string
  enum: [USER, ADMIN, AUDITOR]

AllowedModule:
  type: string
  enum: [DOCUMENT_BROWSER, BACKEND_MANAGEMENT, AUDIT_LOG]

DocumentType:
  type: string
  enum: [MAIN, SUB_PLAN, ATTACHMENT]

DocumentStatus:
  type: string
  enum: [DRAFT, PUBLISHED, ARCHIVED]

SecurityLevel:
  type: string
  enum: [PUBLIC, INTERNAL, SECRET, CONFIDENTIAL, TOP_SECRET]
  x-comparison-values:
    PUBLIC: 10
    INTERNAL: 20
    SECRET: 30
    CONFIDENTIAL: 40
    TOP_SECRET: 50

VisibilityType:
  type: string
  enum: [ALL_AUTHENTICATED, ORGANIZATION, CUSTOM]

AttachmentType:
  type: string
  enum: [POLICY, WORK_STANDARD, TABLE, DIAGRAM, OTHER]

SubjectType:
  type: string
  enum: [ORG, USER]

AllowedAction:
  type: string
  enum:
    - VIEW
    - DOWNLOAD
    - EDIT
    - CONFIG_PERMISSION
    - DELETE
    - BIND_ATTACHMENT
    - UNBIND_ATTACHMENT

CategoryType:
  type: string
  enum: [SCENE, STYLE, SITUATION, VERSION, OTHER]

EnabledStatus:
  type: string
  enum: [ENABLED, DISABLED]

AuditResult:
  type: string
  enum: [SUCCESS, FAILURE]

AuditAction:
  type: string
  enum:
    - LOGIN
    - LOGOUT
    - VIEW_DOCUMENT
    - DOWNLOAD_DOCUMENT
    - UPLOAD_DOCUMENT
    - BATCH_IMPORT
    - EDIT_DOCUMENT
    - DELETE_DOCUMENT
    - CREATE_CATEGORY
    - EDIT_CATEGORY
    - DELETE_CATEGORY
    - CHANGE_PERMISSION
    - BIND_ATTACHMENT
    - UNBIND_ATTACHMENT
    - RESTORE_DOCUMENT

AuditTarget:
  type: string
  enum:
    - AUTH
    - CATEGORY
    - DOCUMENT
    - ATTACHMENT
    - PERMISSION
    - ATTACHMENT_BINDING

PageResult:
  type: object
  required: [items, page, pageSize, total, totalPages]
  properties:
    items:
      type: array
      items: {}
    page:
      type: integer
      minimum: 1
      default: 1
    pageSize:
      type: integer
      minimum: 1
      maximum: 100
      default: 20
    total:
      type: integer
      minimum: 0
    totalPages:
      type: integer
      minimum: 0

HealthData:
  type: object
  additionalProperties: false
  required: [service, status]
  properties:
    service:
      type: string
      enum: [dms]
    status:
      type: string
      enum: [UP]

HealthResponse:
  type: object
  additionalProperties: false
  required: [code, message, data, requestId]
  properties:
    code:
      type: string
      enum: [OK]
    message:
      type: string
      enum: [success]
    data:
      $ref: '#/HealthData'
    requestId:
      type: string
      format: uuid

ErrorResponse:
  type: object
  additionalProperties: false
  required: [code, message, details, requestId]
  properties:
    code:
      type: string
      enum:
        - INVALID_ARGUMENT
        - INVALID_CREDENTIALS
        - TOKEN_INVALID
        - TOKEN_EXPIRED
        - USER_DISABLED
        - AUTH_VERSION_MISMATCH
        - FORBIDDEN
        - SECURITY_LEVEL_FORBIDDEN
        - DOCUMENT_VIEW_FORBIDDEN
        - DOCUMENT_DOWNLOAD_FORBIDDEN
        - RESOURCE_NOT_FOUND
        - FILE_NOT_FOUND
        - UNSUPPORTED_FILE_TYPE
        - PREVIEW_UNAVAILABLE
        - PAYLOAD_TOO_LARGE
        - BATCH_MANIFEST_MISMATCH
        - DATA_VERSION_CONFLICT
        - CATEGORY_IN_USE
        - MAIN_PLAN_HAS_CHILDREN
        - ATTACHMENT_IN_USE
        - ATTACHMENT_HAS_NO_ACL
        - SUB_PLAN_PERMISSION_INHERITED
        - DUPLICATE_PERMISSION_SUBJECT
        - MAIN_PLAN_NOT_FOUND
        - INTERNAL_ERROR
        - DOCUMENT_NOT_DELETED
        - FILE_INTEGRITY_MISMATCH
        - FILE_PATH_INVALID
        - RESTORE_FILE_CHANGED
        - RESTORE_CATEGORY_INVALID
        - RESTORE_PARENT_INVALID
        - RESTORE_PERMISSION_INVALID
        - RESTORE_RELATION_CONFLICT
    message:
      type: string
    details:
      type: object
      nullable: true
      additionalProperties: true
    requestId:
      type: string
      format: uuid

UserSummary:
  type: object
  additionalProperties: false
  required:
    - id
    - username
    - realName
    - organizationId
    - organizationName
    - roleCode
    - securityLevel
    - status
    - allowedModules
  properties:
    id:
      $ref: '#/StringId'
    username:
      type: string
    realName:
      type: string
    organizationId:
      allOf:
        - $ref: '#/StringId'
      nullable: true
    organizationName:
      type: string
      nullable: true
    roleCode:
      $ref: '#/RoleCode'
    securityLevel:
      $ref: '#/SecurityLevel'
    status:
      $ref: '#/EnabledStatus'
    allowedModules:
      type: array
      uniqueItems: true
      items:
        $ref: '#/AllowedModule'

LoginRequest:
  type: object
  additionalProperties: false
  required: [username, password, keepSignedIn]
  properties:
    username:
      type: string
      minLength: 1
    password:
      type: string
      format: password
      minLength: 1
      writeOnly: true
    keepSignedIn:
      type: boolean

LoginData:
  type: object
  additionalProperties: false
  required: [accessToken, tokenType, expiresIn, user]
  properties:
    accessToken:
      type: string
    tokenType:
      type: string
      enum: [Bearer]
    expiresIn:
      type: integer
      enum: [7200, 604800]
    user:
      $ref: '#/UserSummary'

LoginResponse:
  type: object
  additionalProperties: false
  required: [code, message, data, requestId]
  properties:
    code:
      type: string
      enum: [OK]
    message:
      type: string
      enum: [success]
    data:
      $ref: '#/LoginData'
    requestId:
      type: string
      format: uuid

UserResponse:
  type: object
  additionalProperties: false
  required: [code, message, data, requestId]
  properties:
    code:
      type: string
      enum: [OK]
    message:
      type: string
      enum: [success]
    data:
      $ref: '#/UserSummary'
    requestId:
      type: string
      format: uuid

OrganizationNode:
  type: object
  additionalProperties: false
  required: [id, parentId, orgCode, orgName, orgPath, sortNo, status, children]
  properties:
    id:
      $ref: '#/StringId'
    parentId:
      allOf:
        - $ref: '#/StringId'
      nullable: true
    orgCode:
      type: string
    orgName:
      type: string
    orgPath:
      type: string
    sortNo:
      type: integer
    status:
      $ref: '#/EnabledStatus'
    children:
      type: array
      items:
        $ref: '#/OrganizationNode'

OrganizationTreeResponse:
  type: object
  additionalProperties: false
  required: [code, message, data, requestId]
  properties:
    code:
      type: string
      enum: [OK]
    message:
      type: string
      enum: [success]
    data:
      type: array
      items:
        $ref: '#/OrganizationNode'
    requestId:
      type: string
      format: uuid

UserPage:
  type: object
  additionalProperties: false
  required: [items, page, pageSize, total, totalPages]
  properties:
    items:
      type: array
      items:
        $ref: '#/UserSummary'
    page:
      type: integer
      minimum: 1
    pageSize:
      type: integer
      minimum: 1
      maximum: 100
    total:
      type: integer
      minimum: 0
    totalPages:
      type: integer
      minimum: 0

UserPageResponse:
  type: object
  additionalProperties: false
  required: [code, message, data, requestId]
  properties:
    code:
      type: string
      enum: [OK]
    message:
      type: string
      enum: [success]
    data:
      $ref: '#/UserPage'
    requestId:
      type: string
      format: uuid

EmptySuccessResponse:
  type: object
  additionalProperties: false
  required: [code, message, data, requestId]
  properties:
    code:
      type: string
      enum: [OK]
    message:
      type: string
      enum: [success]
    data:
      type: object
      additionalProperties: false
    requestId:
      type: string
      format: uuid

CategoryNode:
  type: object
  additionalProperties: false
  required:
    - id
    - categoryCode
    - categoryName
    - categoryType
    - parentId
    - categoryPath
    - sortNo
    - documentCount
    - rowVersion
    - children
  properties:
    id:
      $ref: '#/StringId'
    categoryCode:
      type: string
      maxLength: 64
    categoryName:
      type: string
      maxLength: 128
    categoryType:
      $ref: '#/CategoryType'
    parentId:
      allOf:
        - $ref: '#/StringId'
      nullable: true
    categoryPath:
      type: string
      maxLength: 1000
    sortNo:
      type: integer
    documentCount:
      type: integer
      minimum: 0
    rowVersion:
      type: integer
      minimum: 0
    children:
      type: array
      items:
        $ref: '#/CategoryNode'

CreateCategoryRequest:
  type: object
  additionalProperties: false
  required: [categoryCode, categoryName, categoryType, parentId, sortNo]
  properties:
    categoryCode:
      type: string
      minLength: 1
      maxLength: 64
    categoryName:
      type: string
      minLength: 1
      maxLength: 128
    categoryType:
      $ref: '#/CategoryType'
    parentId:
      allOf:
        - $ref: '#/StringId'
      nullable: true
    sortNo:
      type: integer

UpdateCategoryRequest:
  type: object
  additionalProperties: false
  required: [categoryName, categoryType, parentId, sortNo, rowVersion]
  properties:
    categoryName:
      type: string
      minLength: 1
      maxLength: 128
    categoryType:
      $ref: '#/CategoryType'
    parentId:
      allOf:
        - $ref: '#/StringId'
      nullable: true
    sortNo:
      type: integer
    rowVersion:
      type: integer
      minimum: 0

CategoryTreeResponse:
  type: object
  additionalProperties: false
  required: [code, message, data, requestId]
  properties:
    code:
      type: string
      enum: [OK]
    message:
      type: string
      enum: [success]
    data:
      type: array
      items:
        $ref: '#/CategoryNode'
    requestId:
      type: string
      format: uuid

CategoryResponse:
  type: object
  additionalProperties: false
  required: [code, message, data, requestId]
  properties:
    code:
      type: string
      enum: [OK]
    message:
      type: string
      enum: [success]
    data:
      $ref: '#/CategoryNode'
    requestId:
      type: string
      format: uuid

DeleteCategoryData:
  type: object
  additionalProperties: false
  required: [id, deleted]
  properties:
    id:
      $ref: '#/StringId'
    deleted:
      type: boolean
      enum: [true]

DeleteCategoryResponse:
  type: object
  additionalProperties: false
  required: [code, message, data, requestId]
  properties:
    code:
      type: string
      enum: [OK]
    message:
      type: string
      enum: [success]
    data:
      $ref: '#/DeleteCategoryData'
    requestId:
      type: string
      format: uuid

PermissionSummary:
  type: object
  additionalProperties: false
  required: [organizationCount, userCount, inheritedFromMainPlan]
  properties:
    organizationCount:
      type: integer
      minimum: 0
    userCount:
      type: integer
      minimum: 0
    inheritedFromMainPlan:
      type: boolean

DocumentSummary:
  type: object
  required:
    - id
    - documentName
    - summary
    - documentType
    - status
    - securityLevel
    - visibilityType
    - visibilitySummary
    - categoryId
    - categoryName
    - categoryPath
    - parentDocumentId
    - rootDocumentId
    - attachmentType
    - fileExtension
    - tags
    - childCount
    - attachmentCount
    - viewCount
    - downloadCount
    - createdByName
    - createdAt
    - updatedAt
    - rowVersion
    - allowedActions
  properties:
    id:
      $ref: '#/StringId'
    documentName:
      type: string
    summary:
      type: string
      nullable: true
    documentType:
      $ref: '#/DocumentType'
    status:
      $ref: '#/DocumentStatus'
    securityLevel:
      $ref: '#/SecurityLevel'
    visibilityType:
      $ref: '#/VisibilityType'
    visibilitySummary:
      type: string
    categoryId:
      allOf:
        - $ref: '#/StringId'
      nullable: true
    categoryName:
      type: string
      nullable: true
    categoryPath:
      type: string
      nullable: true
    parentDocumentId:
      allOf:
        - $ref: '#/StringId'
      nullable: true
    rootDocumentId:
      allOf:
        - $ref: '#/StringId'
      nullable: true
    attachmentType:
      allOf:
        - $ref: '#/AttachmentType'
      nullable: true
    fileExtension:
      type: string
    tags:
      type: array
      items:
        type: string
    childCount:
      type: integer
      minimum: 0
    attachmentCount:
      type: integer
      minimum: 0
    viewCount:
      type: integer
      minimum: 0
    downloadCount:
      type: integer
      minimum: 0
    createdByName:
      type: string
      nullable: true
    createdAt:
      $ref: '#/UtcDateTime'
    updatedAt:
      $ref: '#/UtcDateTime'
    rowVersion:
      type: integer
      minimum: 0
    allowedActions:
      type: array
      uniqueItems: true
      items:
        $ref: '#/AllowedAction'
    mountedPlanCount:
      type: integer
      minimum: 0
      description: 仅共享附件响应返回。

DocumentDetail:
  allOf:
    - $ref: '#/DocumentSummary'
    - type: object
      required:
        - originalFileName
        - mimeType
        - fileSize
        - fileHash
        - permissionSummary
      properties:
        originalFileName:
          type: string
        mimeType:
          type: string
          nullable: true
        fileSize:
          type: integer
          minimum: 0
        fileHash:
          type: string
          pattern: '^[0-9a-fA-F]{64}$'
        permissionSummary:
          $ref: '#/PermissionSummary'

AttachmentBindingSummary:
  allOf:
    - $ref: '#/DocumentSummary'
    - type: object
      required: [bindingId, bindingSortNo, mountedPlanCount]
      properties:
        bindingId:
          $ref: '#/StringId'
        bindingSortNo:
          type: integer
        mountedPlanCount:
          type: integer
          minimum: 0

DocumentPage:
  type: object
  additionalProperties: false
  required: [items, page, pageSize, total, totalPages]
  properties:
    items:
      type: array
      items:
        $ref: '#/DocumentSummary'
    page:
      type: integer
      minimum: 1
    pageSize:
      type: integer
      minimum: 1
      maximum: 100
    total:
      type: integer
      minimum: 0
    totalPages:
      type: integer
      minimum: 0

AttachmentBindingPage:
  type: object
  additionalProperties: false
  required: [items, page, pageSize, total, totalPages]
  properties:
    items:
      type: array
      items:
        $ref: '#/AttachmentBindingSummary'
    page:
      type: integer
      minimum: 1
    pageSize:
      type: integer
      minimum: 1
      maximum: 100
    total:
      type: integer
      minimum: 0
    totalPages:
      type: integer
      minimum: 0

MainPlanBrief:
  type: object
  additionalProperties: false
  required: [id, documentName, categoryName, securityLevel]
  properties:
    id:
      $ref: '#/StringId'
    documentName:
      type: string
    categoryName:
      type: string
      nullable: true
    securityLevel:
      $ref: '#/SecurityLevel'

MainPlanBriefCollection:
  type: object
  additionalProperties: false
  required: [items, total]
  properties:
    items:
      type: array
      items:
        $ref: '#/MainPlanBrief'
    total:
      type: integer
      minimum: 0

DocumentPageResponse:
  type: object
  additionalProperties: false
  required: [code, message, data, requestId]
  properties:
    code:
      type: string
      enum: [OK]
    message:
      type: string
      enum: [success]
    data:
      $ref: '#/DocumentPage'
    requestId:
      type: string
      format: uuid

DocumentDetailResponse:
  type: object
  additionalProperties: false
  required: [code, message, data, requestId]
  properties:
    code:
      type: string
      enum: [OK]
    message:
      type: string
      enum: [success]
    data:
      $ref: '#/DocumentDetail'
    requestId:
      type: string
      format: uuid

AttachmentBindingPageResponse:
  type: object
  additionalProperties: false
  required: [code, message, data, requestId]
  properties:
    code:
      type: string
      enum: [OK]
    message:
      type: string
      enum: [success]
    data:
      $ref: '#/AttachmentBindingPage'
    requestId:
      type: string
      format: uuid

MainPlanBriefCollectionResponse:
  type: object
  additionalProperties: false
  required: [code, message, data, requestId]
  properties:
    code:
      type: string
      enum: [OK]
    message:
      type: string
      enum: [success]
    data:
      $ref: '#/MainPlanBriefCollection'
    requestId:
      type: string
      format: uuid

DocumentCreateMetadata:
  type: object
  additionalProperties: false
  required: [documentName, documentType, summary, categoryId, securityLevel, visibilityType, status, tags]
  properties:
    documentName: {type: string, minLength: 1, maxLength: 255}
    documentType: {type: string, enum: [MAIN, SUB_PLAN]}
    summary: {type: string, nullable: true}
    categoryId: {$ref: '#/StringId'}
    parentDocumentId:
      allOf: [{$ref: '#/StringId'}]
      description: SUB_PLAN必填，MAIN禁止提交。
    securityLevel: {$ref: '#/SecurityLevel'}
    visibilityType: {$ref: '#/VisibilityType'}
    status: {$ref: '#/DocumentStatus'}
    tags:
      type: array
      maxItems: 50
      items: {type: string, minLength: 1, maxLength: 64}

AttachmentCreateMetadata:
  type: object
  additionalProperties: false
  required: [documentName, attachmentType, summary, tags]
  properties:
    documentName: {type: string, minLength: 1, maxLength: 255}
    attachmentType: {$ref: '#/AttachmentType'}
    summary: {type: string, nullable: true}
    tags:
      type: array
      maxItems: 50
      items: {type: string, minLength: 1, maxLength: 64}

UpdateDocumentRequest:
  type: object
  additionalProperties: false
  required: [documentName, summary, categoryId, securityLevel, visibilityType, status, tags, rowVersion]
  properties:
    documentName: {type: string, minLength: 1, maxLength: 255}
    summary: {type: string, nullable: true}
    categoryId: {$ref: '#/StringId'}
    securityLevel: {$ref: '#/SecurityLevel'}
    visibilityType: {$ref: '#/VisibilityType'}
    status: {$ref: '#/DocumentStatus'}
    tags:
      type: array
      maxItems: 50
      items: {type: string, minLength: 1, maxLength: 64}
    rowVersion: {type: integer, minimum: 0}

UpdateAttachmentRequest:
  type: object
  additionalProperties: false
  required: [documentName, attachmentType, summary, tags, rowVersion]
  properties:
    documentName: {type: string, minLength: 1, maxLength: 255}
    attachmentType: {$ref: '#/AttachmentType'}
    summary: {type: string, nullable: true}
    tags:
      type: array
      maxItems: 50
      items: {type: string, minLength: 1, maxLength: 64}
    rowVersion: {type: integer, minimum: 0}

DeleteDocumentData:
  type: object
  additionalProperties: false
  required: [id, deleted]
  properties:
    id: {$ref: '#/StringId'}
    deleted: {type: boolean, enum: [true]}

DeleteDocumentResponse:
  type: object
  additionalProperties: false
  required: [code, message, data, requestId]
  properties:
    code: {type: string, enum: [OK]}
    message: {type: string, enum: [success]}
    data: {$ref: '#/DeleteDocumentData'}
    requestId: {type: string, format: uuid}

BatchImportMultipart:
  type: object
  additionalProperties: false
  required: [files, items]
  properties:
    files:
      type: array
      minItems: 1
      items: {type: string, format: binary}
      description: 重复files字段，顺序与items数组严格对应。
    items:
      type: string
      description: 文档或附件metadata对象数组的JSON字符串。

BatchImportItem:
  type: object
  additionalProperties: false
  required: [index, originalFileName, success]
  properties:
    index: {type: integer, minimum: 0}
    originalFileName: {type: string}
    success: {type: boolean}
    document: {$ref: '#/DocumentDetail'}
    errorCode: {type: string}
    errorMessage: {type: string}

BatchImportData:
  type: object
  additionalProperties: false
  required: [total, successCount, failureCount, items]
  properties:
    total: {type: integer, minimum: 0}
    successCount: {type: integer, minimum: 0}
    failureCount: {type: integer, minimum: 0}
    items:
      type: array
      items: {$ref: '#/BatchImportItem'}

BatchImportResponse:
  type: object
  additionalProperties: false
  required: [code, message, data, requestId]
  properties:
    code: {type: string, enum: [OK]}
    message: {type: string, enum: [success]}
    data: {$ref: '#/BatchImportData'}
    requestId: {type: string, format: uuid}

BindAttachmentsRequest:
  type: object
  additionalProperties: false
  required: [attachmentIds, mainPlanRowVersion]
  properties:
    attachmentIds:
      type: array
      minItems: 1
      items: {$ref: '#/StringId'}
    mainPlanRowVersion:
      type: integer
      minimum: 0

BindAttachmentsData:
  type: object
  additionalProperties: false
  required: [createdCount, existingCount, attachmentCount, mainPlanRowVersion]
  properties:
    createdCount: {type: integer, minimum: 0}
    existingCount: {type: integer, minimum: 0}
    attachmentCount: {type: integer, minimum: 0}
    mainPlanRowVersion: {type: integer, minimum: 0}

BindAttachmentsResponse:
  type: object
  additionalProperties: false
  required: [code, message, data, requestId]
  properties:
    code: {type: string, enum: [OK]}
    message: {type: string, enum: [success]}
    data: {$ref: '#/BindAttachmentsData'}
    requestId: {type: string, format: uuid}

UnbindAttachmentData:
  type: object
  additionalProperties: false
  required: [attachmentCount, mainPlanRowVersion]
  properties:
    attachmentCount: {type: integer, minimum: 0}
    mainPlanRowVersion: {type: integer, minimum: 0}

UnbindAttachmentResponse:
  type: object
  additionalProperties: false
  required: [code, message, data, requestId]
  properties:
    code: {type: string, enum: [OK]}
    message: {type: string, enum: [success]}
    data: {$ref: '#/UnbindAttachmentData'}
    requestId: {type: string, format: uuid}

PermissionEntry:
  type: object
  additionalProperties: false
  required:
    - id
    - subjectType
    - subjectId
    - subjectName
    - canView
    - canDownload
    - canEdit
    - canManagePermission
    - canDelete
  properties:
    id: {$ref: '#/StringId'}
    subjectType: {$ref: '#/SubjectType'}
    subjectId: {$ref: '#/StringId'}
    subjectName: {type: string}
    canView: {type: boolean}
    canDownload: {type: boolean}
    canEdit: {type: boolean}
    canManagePermission: {type: boolean}
    canDelete: {type: boolean}

SavePermissionEntry:
  type: object
  additionalProperties: false
  required:
    - subjectType
    - subjectId
    - canView
    - canDownload
    - canEdit
    - canManagePermission
    - canDelete
  properties:
    subjectType: {$ref: '#/SubjectType'}
    subjectId: {$ref: '#/StringId'}
    canView: {type: boolean}
    canDownload: {type: boolean}
    canEdit: {type: boolean}
    canManagePermission: {type: boolean}
    canDelete: {type: boolean}

SavePermissionsRequest:
  type: object
  additionalProperties: false
  required: [visibilityType, documentRowVersion, entries]
  properties:
    visibilityType: {$ref: '#/VisibilityType'}
    documentRowVersion: {type: integer, minimum: 0}
    entries:
      type: array
      items: {$ref: '#/SavePermissionEntry'}
      description: ALL_AUTHENTICATED必须为空；ORGANIZATION至少一个ORG；CUSTOM可为空。

PermissionDetail:
  type: object
  additionalProperties: false
  required:
    - documentId
    - sourceDocumentId
    - inherited
    - visibilityType
    - documentRowVersion
    - entries
  properties:
    documentId: {$ref: '#/StringId'}
    sourceDocumentId: {$ref: '#/StringId'}
    inherited: {type: boolean}
    visibilityType: {$ref: '#/VisibilityType'}
    documentRowVersion: {type: integer, minimum: 0}
    entries:
      type: array
      items: {$ref: '#/PermissionEntry'}

PermissionDetailResponse:
  type: object
  additionalProperties: false
  required: [code, message, data, requestId]
  properties:
    code: {type: string, enum: [OK]}
    message: {type: string, enum: [success]}
    data: {$ref: '#/PermissionDetail'}
    requestId: {type: string, format: uuid}

AuditGranularity:
  type: string
  enum: [DAY, WEEK, MONTH]

AuditLogSummary:
  type: object
  additionalProperties: false
  required:
    - id
    - username
    - realName
    - organizationName
    - actionType
    - targetType
    - targetId
    - targetName
    - operationResult
    - failureReason
    - clientIp
    - requestId
    - createdAt
  properties:
    id: {$ref: '#/StringId'}
    username: {type: string, nullable: true}
    realName: {type: string, nullable: true}
    organizationName: {type: string, nullable: true}
    actionType: {$ref: '#/AuditAction'}
    targetType:
      allOf: [{$ref: '#/AuditTarget'}]
      nullable: true
    targetId:
      allOf: [{$ref: '#/StringId'}]
      nullable: true
    targetName: {type: string, nullable: true}
    operationResult: {$ref: '#/AuditResult'}
    failureReason: {type: string, nullable: true}
    clientIp: {type: string, nullable: true}
    requestId: {type: string, nullable: true}
    createdAt: {$ref: '#/UtcDateTime'}

AuditLogPage:
  type: object
  additionalProperties: false
  required: [items, page, pageSize, total, totalPages]
  properties:
    items: {type: array, items: {$ref: '#/AuditLogSummary'}}
    page: {type: integer, minimum: 1}
    pageSize: {type: integer, minimum: 1, maximum: 100}
    total: {type: integer, minimum: 0}
    totalPages: {type: integer, minimum: 0}

AuditLogPageResponse:
  type: object
  additionalProperties: false
  required: [code, message, data, requestId]
  properties:
    code: {type: string, enum: [OK]}
    message: {type: string, enum: [success]}
    data: {$ref: '#/AuditLogPage'}
    requestId: {type: string, format: uuid}

DocumentStatistics:
  type: object
  additionalProperties: false
  required: [planDocumentCount, mainPlanCount, subPlanCount, attachmentCount, todayViewerCount]
  properties:
    planDocumentCount: {type: integer, minimum: 0}
    mainPlanCount: {type: integer, minimum: 0}
    subPlanCount: {type: integer, minimum: 0}
    attachmentCount: {type: integer, minimum: 0}
    todayViewerCount: {type: integer, minimum: 0}

DocumentStatisticsResponse:
  type: object
  additionalProperties: false
  required: [code, message, data, requestId]
  properties:
    code: {type: string, enum: [OK]}
    message: {type: string, enum: [success]}
    data: {$ref: '#/DocumentStatistics'}
    requestId: {type: string, format: uuid}

AuditTrendItem:
  type: object
  additionalProperties: false
  required: [period, operationCount]
  properties:
    period: {type: string}
    operationCount: {type: integer, minimum: 0}

AuditTrendData:
  type: object
  additionalProperties: false
  required: [items]
  properties:
    items: {type: array, items: {$ref: '#/AuditTrendItem'}}

AuditTrendResponse:
  type: object
  additionalProperties: false
  required: [code, message, data, requestId]
  properties:
    code: {type: string, enum: [OK]}
    message: {type: string, enum: [success]}
    data: {$ref: '#/AuditTrendData'}
    requestId: {type: string, format: uuid}

AuditActionStatisticsItem:
  type: object
  additionalProperties: false
  required: [actionType, count]
  properties:
    actionType: {$ref: '#/AuditAction'}
    count: {type: integer, minimum: 0}

AuditActionStatisticsData:
  type: object
  additionalProperties: false
  required: [items]
  properties:
    items: {type: array, items: {$ref: '#/AuditActionStatisticsItem'}}

AuditActionStatisticsResponse:
  type: object
  additionalProperties: false
  required: [code, message, data, requestId]
  properties:
    code: {type: string, enum: [OK]}
    message: {type: string, enum: [success]}
    data: {$ref: '#/AuditActionStatisticsData'}
    requestId: {type: string, format: uuid}

AuditUserStatisticsItem:
  type: object
  additionalProperties: false
  required: [userId, realName, organizationName, operationCount]
  properties:
    userId: {$ref: '#/StringId'}
    realName: {type: string, nullable: true}
    organizationName: {type: string, nullable: true}
    operationCount: {type: integer, minimum: 0}

AuditUserStatisticsData:
  type: object
  additionalProperties: false
  required: [items]
  properties:
    items: {type: array, items: {$ref: '#/AuditUserStatisticsItem'}}

AuditUserStatisticsResponse:
  type: object
  additionalProperties: false
  required: [code, message, data, requestId]
  properties:
    code: {type: string, enum: [OK]}
    message: {type: string, enum: [success]}
    data: {$ref: '#/AuditUserStatisticsData'}
    requestId: {type: string, format: uuid}

DeletedBySnapshot:
  type: object
  additionalProperties: false
  required: [userId, username, realName, organizationName]
  properties:
    userId:
      allOf:
        - $ref: '#/StringId'
      nullable: true
    username: {type: string, nullable: true}
    realName: {type: string, nullable: true}
    organizationName: {type: string, nullable: true}

RecycleBinDocumentSummary:
  type: object
  additionalProperties: false
  required:
    - id
    - documentName
    - documentType
    - originalFileName
    - categoryId
    - categoryName
    - parentDocumentId
    - securityLevel
    - documentStatus
    - deletedAt
    - updatedBy
    - updatedByName
    - deletedBy
    - rowVersion
  properties:
    id: {$ref: '#/StringId'}
    documentName: {type: string}
    documentType: {$ref: '#/DocumentType'}
    originalFileName: {type: string}
    categoryId:
      allOf:
        - $ref: '#/StringId'
      nullable: true
    categoryName: {type: string, nullable: true}
    parentDocumentId:
      allOf:
        - $ref: '#/StringId'
      nullable: true
    securityLevel: {$ref: '#/SecurityLevel'}
    documentStatus: {$ref: '#/DocumentStatus'}
    deletedAt: {$ref: '#/UtcDateTime'}
    updatedBy:
      allOf:
        - $ref: '#/StringId'
      nullable: true
    updatedByName: {type: string, nullable: true}
    deletedBy:
      allOf:
        - $ref: '#/DeletedBySnapshot'
      nullable: true
    rowVersion: {type: integer, minimum: 0}

RecycleBinDocumentPage:
  type: object
  additionalProperties: false
  required: [items, page, pageSize, total, totalPages]
  properties:
    items:
      type: array
      items: {$ref: '#/RecycleBinDocumentSummary'}
    page: {type: integer, minimum: 1}
    pageSize: {type: integer, minimum: 1, maximum: 100}
    total: {type: integer, minimum: 0}
    totalPages: {type: integer, minimum: 0}

RecycleBinDocumentPageResponse:
  type: object
  additionalProperties: false
  required: [code, message, data, requestId]
  properties:
    code: {type: string, enum: [OK]}
    message: {type: string, enum: [success]}
    data: {$ref: '#/RecycleBinDocumentPage'}
    requestId: {type: string, format: uuid}

RestoreDocumentRequest:
  type: object
  additionalProperties: false
  required: [rowVersion]
  properties:
    rowVersion: {type: integer, minimum: 0}

RestoredDocument:
  allOf:
    - $ref: '#/DocumentSummary'
    - type: object
      required: [originalFileName, mimeType, fileSize, permissionSummary]
      properties:
        originalFileName: {type: string}
        mimeType: {type: string, nullable: true}
        fileSize: {type: integer, minimum: 0}
        permissionSummary: {$ref: '#/PermissionSummary'}
        mountedPlanCount: {type: integer, minimum: 0}

RestoreDocumentResult:
  type: object
  additionalProperties: false
  required:
    - document
    - restoredPermissionCount
    - skippedPermissionCount
    - restoredBindingCount
    - skippedBindingCount
  properties:
    document: {$ref: '#/RestoredDocument'}
    restoredPermissionCount: {type: integer, minimum: 0}
    skippedPermissionCount: {type: integer, minimum: 0}
    restoredBindingCount: {type: integer, minimum: 0}
    skippedBindingCount: {type: integer, minimum: 0}

RestoreDocumentResponse:
  type: object
  additionalProperties: false
  required: [code, message, data, requestId]
  properties:
    code: {type: string, enum: [OK]}
    message: {type: string, enum: [success]}
    data: {$ref: '#/RestoreDocumentResult'}
    requestId: {type: string, format: uuid}

DictionaryOption:
  type: object
  additionalProperties: false
  required: [code, label, sortNo]
  properties:
    code: {type: string, minLength: 1}
    label: {type: string, minLength: 1}
    sortNo: {type: integer, minimum: 0}

DictionaryOptionList:
  type: array
  items: {$ref: '#/DictionaryOption'}

UiDictionaryGroups:
  type: object
  additionalProperties: false
  required:
    - roleCodes
    - allowedModules
    - documentTypes
    - documentStatuses
    - securityLevels
    - visibilityTypes
    - attachmentTypes
    - subjectTypes
    - allowedActions
    - categoryTypes
    - enabledStatuses
    - auditResults
    - auditActions
    - auditTargets
  properties:
    roleCodes: {$ref: '#/DictionaryOptionList'}
    allowedModules: {$ref: '#/DictionaryOptionList'}
    documentTypes: {$ref: '#/DictionaryOptionList'}
    documentStatuses: {$ref: '#/DictionaryOptionList'}
    securityLevels: {$ref: '#/DictionaryOptionList'}
    visibilityTypes: {$ref: '#/DictionaryOptionList'}
    attachmentTypes: {$ref: '#/DictionaryOptionList'}
    subjectTypes: {$ref: '#/DictionaryOptionList'}
    allowedActions: {$ref: '#/DictionaryOptionList'}
    categoryTypes: {$ref: '#/DictionaryOptionList'}
    enabledStatuses: {$ref: '#/DictionaryOptionList'}
    auditResults: {$ref: '#/DictionaryOptionList'}
    auditActions: {$ref: '#/DictionaryOptionList'}
    auditTargets: {$ref: '#/DictionaryOptionList'}

UiDictionaries:
  type: object
  additionalProperties: false
  required: [version, locale, dictionaries]
  properties:
    version:
      type: string
      pattern: '^[0-9]{4}\.[0-9]+$'
      example: '2026.1'
    locale:
      type: string
      example: zh-CN
    dictionaries: {$ref: '#/UiDictionaryGroups'}

UiDictionariesResponse:
  type: object
  additionalProperties: false
  required: [code, message, data, requestId]
  properties:
    code: {type: string, enum: [OK]}
    message: {type: string, enum: [success]}
    data: {$ref: '#/UiDictionaries'}
    requestId: {type: string, format: uuid}
