| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315 |
- $ErrorActionPreference = "Stop"
- $base = "http://127.0.0.1:8755/api/v1"
- $origin = "http://127.0.0.1:9346"
- function Invoke-Json {
- param(
- [string]$Method,
- [string]$Path,
- [string]$Token,
- [object]$Body = $null,
- [hashtable]$ExtraHeaders = @{}
- )
- $headers = @{}
- if ($Token) {
- $headers.Authorization = "Bearer $Token"
- }
- foreach ($key in $ExtraHeaders.Keys) {
- $headers[$key] = $ExtraHeaders[$key]
- }
- $parameters = @{
- Method = $Method
- Uri = "$base$Path"
- Headers = $headers
- UseBasicParsing = $true
- }
- if ($null -ne $Body) {
- $parameters.ContentType = "application/json"
- $parameters.Body = ($Body | ConvertTo-Json -Depth 10 -Compress)
- }
- try {
- $response = Invoke-WebRequest @parameters
- return @{
- Status = [int]$response.StatusCode
- Body = ($response.Content | ConvertFrom-Json)
- Headers = $response.Headers
- }
- }
- catch {
- $response = $_.Exception.Response
- $content = $_.ErrorDetails.Message
- if (-not $content) {
- $reader = New-Object System.IO.StreamReader($response.GetResponseStream())
- $content = $reader.ReadToEnd()
- $reader.Dispose()
- }
- return @{
- Status = [int]$response.StatusCode
- Body = ($content | ConvertFrom-Json)
- Headers = $response.Headers
- }
- }
- }
- function Assert-True {
- param([bool]$Condition, [string]$Message)
- if (-not $Condition) {
- throw $Message
- }
- }
- function Upload-Document {
- param(
- [string]$Token,
- [string]$File,
- [hashtable]$Metadata,
- [bool]$Attachment = $false
- )
- $path = if ($Attachment) { "/attachments" } else { "/documents" }
- $metadataJson = $Metadata | ConvertTo-Json -Depth 10 -Compress
- $metadataFile = [System.IO.Path]::GetTempFileName()
- try {
- [System.IO.File]::WriteAllText(
- $metadataFile,
- $metadataJson,
- [System.Text.UTF8Encoding]::new($false)
- )
- $output = & curl.exe -sS -X POST "$base$path" `
- -H "Authorization: Bearer $Token" `
- -F "file=@$File" `
- -F "metadata=<$metadataFile"
- if ($LASTEXITCODE -ne 0) {
- throw "curl上传失败"
- }
- }
- finally {
- Remove-Item -LiteralPath $metadataFile -Force -ErrorAction SilentlyContinue
- }
- $body = $output | ConvertFrom-Json
- Assert-True ($body.code -eq "OK") "上传失败:$output"
- return $body.data
- }
- function Issue-Token {
- param([string]$Username)
- $response = Invoke-Json POST "/auth/login" "" @{
- username = $Username
- password = $env:DMS_TEST_USER_PASSWORD
- keepSignedIn = $false
- }
- Assert-True ($response.Status -eq 200) "$Username 登录失败"
- return $response.Body.data.accessToken
- }
- $adminToken = Issue-Token "admin"
- $userToken = Issue-Token "user"
- $auditorToken = Issue-Token "auditor"
- $health = Invoke-Json GET "/health" "" $null @{Origin = $origin}
- Assert-True ($health.Status -eq 200) "健康检查失败"
- Assert-True (
- $health.Headers["Access-Control-Expose-Headers"] -match "Content-Disposition" -and
- $health.Headers["Access-Control-Expose-Headers"] -match "X-Request-Id"
- ) "CORS暴露响应头缺失"
- $tree = Invoke-Json GET "/categories/tree" $adminToken
- Assert-True ($tree.Status -eq 200) "分类树查询失败"
- $categoryId = [string]$tree.Body.data[0].id
- $sampleFile = (
- Get-ChildItem -LiteralPath (
- Join-Path $PSScriptRoot "..\..\dms-test-storage\original"
- ) -Filter "*.docx" -File | Select-Object -First 1
- ).FullName
- Assert-True ([bool]$sampleFile) "未找到联调上传样本文件"
- $main = Upload-Document $adminToken $sampleFile @{
- documentName = "F8_RECYCLE_MAIN"
- documentType = "MAIN"
- summary = "B8 HTTP MAIN"
- categoryId = $categoryId
- securityLevel = "INTERNAL"
- visibilityType = "CUSTOM"
- status = "PUBLISHED"
- tags = @("B8", "F8")
- }
- $attachment = Upload-Document $adminToken $sampleFile @{
- documentName = "F8_RECYCLE_ATTACHMENT"
- attachmentType = "OTHER"
- summary = "B8 HTTP ATTACHMENT"
- tags = @("B8", "F8")
- } $true
- $users = Invoke-Json GET "/users?keyword=user&page=1&pageSize=20" $adminToken
- $subjectUser = $users.Body.data.items | Where-Object {$_.username -eq "user"} |
- Select-Object -First 1
- $permission = Invoke-Json PUT "/documents/$($main.id)/permissions" $adminToken @{
- visibilityType = "CUSTOM"
- documentRowVersion = $main.rowVersion
- entries = @(
- @{
- subjectType = "USER"
- subjectId = [string]$subjectUser.id
- canView = $true
- canDownload = $true
- canEdit = $false
- canManagePermission = $false
- canDelete = $false
- }
- )
- }
- Assert-True ($permission.Status -eq 200) "权限配置失败"
- $binding = Invoke-Json POST "/main-plans/$($main.id)/attachments/bind" $adminToken @{
- attachmentIds = @([string]$attachment.id)
- mainPlanRowVersion = $permission.Body.data.documentRowVersion
- }
- Assert-True ($binding.Status -eq 200) "附件挂载失败"
- $main.rowVersion = $binding.Body.data.mainPlanRowVersion
- $deleteMain = Invoke-Json DELETE "/documents/$($main.id)?rowVersion=$($main.rowVersion)" $adminToken
- Assert-True ($deleteMain.Status -eq 200) "主案删除失败"
- $bin = Invoke-Json GET "/recycle-bin/documents?keyword=F8_RECYCLE_MAIN" $adminToken $null @{Origin = $origin}
- Assert-True ($bin.Status -eq 200 -and $bin.Body.data.total -eq 1) "主案未进入回收站"
- $deletedMain = $bin.Body.data.items[0]
- Assert-True ($null -ne $deletedMain.deletedBy) "删除人快照缺失"
- $wrongVersion = Invoke-Json POST "/recycle-bin/documents/$($main.id)/restore" $adminToken @{
- rowVersion = [Math]::Max(0, [int]$deletedMain.rowVersion - 1)
- }
- Assert-True (
- $wrongVersion.Status -eq 409 -and
- $wrongVersion.Body.code -eq "DATA_VERSION_CONFLICT"
- ) "rowVersion冲突未按契约返回"
- $restoreMain = Invoke-Json POST "/recycle-bin/documents/$($main.id)/restore" $adminToken @{
- rowVersion = [int]$deletedMain.rowVersion
- }
- Assert-True ($restoreMain.Status -eq 200) "主案恢复失败"
- Assert-True (
- $restoreMain.Body.data.restoredPermissionCount -eq 1 -and
- $restoreMain.Body.data.restoredBindingCount -eq 1
- ) "主案ACL或挂载未恢复"
- $restoredPermissions = Invoke-Json GET "/documents/$($main.id)/permissions" $adminToken
- $restoredAttachments = Invoke-Json GET "/main-plans/$($main.id)/attachments" $adminToken
- Assert-True ($restoredPermissions.Body.data.entries.Count -eq 1) "恢复后ACL不同步"
- Assert-True ($restoredAttachments.Body.data.total -eq 1) "恢复后挂载不同步"
- $sub = Upload-Document $adminToken $sampleFile @{
- documentName = "F8_RECYCLE_SUB_PLAN"
- documentType = "SUB_PLAN"
- summary = "B8 HTTP SUB PLAN"
- categoryId = $categoryId
- securityLevel = "INTERNAL"
- visibilityType = "CUSTOM"
- status = "PUBLISHED"
- tags = @("B8", "F8")
- parentDocumentId = [string]$main.id
- }
- $deleteSub = Invoke-Json DELETE "/documents/$($sub.id)?rowVersion=$($sub.rowVersion)" $adminToken
- Assert-True ($deleteSub.Status -eq 200) "子方案删除失败"
- $subBin = Invoke-Json GET "/recycle-bin/documents?keyword=F8_RECYCLE_SUB_PLAN" $adminToken
- $deletedSub = $subBin.Body.data.items[0]
- $currentMainPermission = Invoke-Json GET "/documents/$($main.id)/permissions" $adminToken
- $deleteParent = Invoke-Json DELETE (
- "/documents/$($main.id)?rowVersion=" +
- $currentMainPermission.Body.data.documentRowVersion
- ) $adminToken
- Assert-True ($deleteParent.Status -eq 200) "父主案二次删除失败"
- $invalidParent = Invoke-Json POST "/recycle-bin/documents/$($sub.id)/restore" $adminToken @{
- rowVersion = [int]$deletedSub.rowVersion
- }
- Assert-True (
- $invalidParent.Status -eq 409 -and
- $invalidParent.Body.code -eq "RESTORE_PARENT_INVALID"
- ) "无效父主案冲突未返回"
- $mainBinAgain = Invoke-Json GET "/recycle-bin/documents?keyword=F8_RECYCLE_MAIN" $adminToken
- $restoreMainAgain = Invoke-Json POST "/recycle-bin/documents/$($main.id)/restore" $adminToken @{
- rowVersion = [int]$mainBinAgain.Body.data.items[0].rowVersion
- }
- Assert-True ($restoreMainAgain.Status -eq 200) "主案二次恢复失败"
- $restoreSub = Invoke-Json POST "/recycle-bin/documents/$($sub.id)/restore" $adminToken @{
- rowVersion = [int]$deletedSub.rowVersion
- }
- Assert-True ($restoreSub.Status -eq 200) "子方案恢复失败"
- $mainPermissionAfterSub = Invoke-Json GET "/documents/$($main.id)/permissions" $adminToken
- $unbind = Invoke-Json DELETE (
- "/main-plans/$($main.id)/attachments/$($attachment.id)" +
- "?mainPlanRowVersion=" +
- $mainPermissionAfterSub.Body.data.documentRowVersion
- ) $adminToken
- Assert-True ($unbind.Status -eq 200) "解除挂载失败"
- $deleteAttachment = Invoke-Json DELETE (
- "/attachments/$($attachment.id)?rowVersion=$($attachment.rowVersion)"
- ) $adminToken
- Assert-True ($deleteAttachment.Status -eq 200) "附件删除失败"
- $attachmentBin = Invoke-Json GET "/recycle-bin/documents?keyword=F8_RECYCLE_ATTACHMENT" $adminToken
- $restoreAttachment = Invoke-Json POST (
- "/recycle-bin/documents/$($attachment.id)/restore"
- ) $adminToken @{
- rowVersion = [int]$attachmentBin.Body.data.items[0].rowVersion
- }
- Assert-True ($restoreAttachment.Status -eq 200) "附件恢复失败"
- Assert-True (
- $restoreAttachment.Body.data.restoredBindingCount -eq 0 -and
- $restoreAttachment.Body.data.document.mountedPlanCount -eq 0
- ) "附件错误恢复了历史挂载"
- foreach ($token in @($userToken, $auditorToken)) {
- $forbidden = Invoke-Json GET "/recycle-bin/documents" $token
- Assert-True (
- $forbidden.Status -eq 403 -and $forbidden.Body.code -eq "FORBIDDEN"
- ) "非管理员未被拒绝"
- }
- $oldPath = Invoke-Json POST "/documents/$($main.id)/restore" $adminToken @{
- rowVersion = 0
- }
- Assert-True ($oldPath.Status -eq 404) "旧恢复路径未保持404"
- $restoreAudits = Invoke-Json GET (
- "/audit/logs?actionType=RESTORE_DOCUMENT&page=1&pageSize=100"
- ) $auditorToken
- Assert-True ($restoreAudits.Body.data.total -ge 3) "恢复审计缺失"
- $viewAudits = Invoke-Json GET (
- "/audit/logs?actionType=VIEW_DOCUMENT&targetId=$($main.id)&page=1&pageSize=100"
- ) $auditorToken
- Assert-True ($viewAudits.Body.data.total -eq 0) "恢复响应产生了VIEW_DOCUMENT"
- # 最终仅保留三条F8回收站样本,全部通过正式DELETE接口产生。
- $deleteSubFinal = Invoke-Json DELETE (
- "/documents/$($sub.id)?rowVersion=$($restoreSub.Body.data.document.rowVersion)"
- ) $adminToken
- Assert-True ($deleteSubFinal.Status -eq 200) "F8子方案最终删除失败"
- $mainPermissionFinal = Invoke-Json GET "/documents/$($main.id)/permissions" $adminToken
- $deleteMainFinal = Invoke-Json DELETE (
- "/documents/$($main.id)?rowVersion=" +
- $mainPermissionFinal.Body.data.documentRowVersion
- ) $adminToken
- Assert-True ($deleteMainFinal.Status -eq 200) "F8主案最终删除失败"
- $deleteAttachmentFinal = Invoke-Json DELETE (
- "/attachments/$($attachment.id)" +
- "?rowVersion=$($restoreAttachment.Body.data.document.rowVersion)"
- ) $adminToken
- Assert-True ($deleteAttachmentFinal.Status -eq 200) "F8附件最终删除失败"
- $finalBin = Invoke-Json GET "/recycle-bin/documents?page=1&pageSize=100" $adminToken
- $finalItems = @($finalBin.Body.data.items | Where-Object {
- $_.documentName -in @(
- "F8_RECYCLE_MAIN",
- "F8_RECYCLE_SUB_PLAN",
- "F8_RECYCLE_ATTACHMENT"
- )
- })
- Assert-True ($finalItems.Count -eq 3) "F8三条回收站样本不完整"
- [pscustomobject]@{
- mainId = [string]$main.id
- subPlanId = [string]$sub.id
- attachmentId = [string]$attachment.id
- recycleBinTotal = $finalBin.Body.data.total
- restoreAuditTotal = $restoreAudits.Body.data.total
- corsExposeHeaders = $health.Headers["Access-Control-Expose-Headers"]
- } | ConvertTo-Json -Compress
|