param( [Parameter(Mandatory = $true)][string]$ImagePath, [Parameter(Mandatory = $true)][string]$StorageRoot, [Parameter(Mandatory = $true)][UInt64]$TenantId, [Parameter(Mandatory = $true)][UInt64]$QualificationId, [Parameter(Mandatory = $true)][string]$OldDocumentUrl, [string]$OutputDirectory = ".\reports\qualification-migration" ) $ErrorActionPreference = "Stop" $source = (Resolve-Path -LiteralPath $ImagePath).Path $file = Get-Item -LiteralPath $source if ($file.Length -le 0 -or $file.Length -gt 5MB) { throw "资质图片必须大于 0 字节且不超过 5 MB。" } $bytes = [System.IO.File]::ReadAllBytes($source) $isJpeg = $bytes.Length -ge 3 -and $bytes[0] -eq 0xFF -and $bytes[1] -eq 0xD8 -and $bytes[2] -eq 0xFF $pngSignature = [byte[]](0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A) $isPng = $bytes.Length -ge 8 for ($index = 0; $isPng -and $index -lt $pngSignature.Length; $index++) { $isPng = $bytes[$index] -eq $pngSignature[$index] } if (-not $isJpeg -and -not $isPng) { throw "资质文件签名不是有效的 JPG 或 PNG。" } $extension = if ($isPng) { ".png" } else { ".jpg" } $fileName = "{0}{1}" -f ([guid]::NewGuid().ToString()), $extension $qualificationDirectory = Join-Path (Join-Path $StorageRoot "qualifications") $TenantId $target = Join-Path $qualificationDirectory $fileName New-Item -ItemType Directory -Force -Path $qualificationDirectory | Out-Null Copy-Item -LiteralPath $source -Destination $target -ErrorAction Stop $sourceHash = (Get-FileHash -Algorithm SHA256 -LiteralPath $source).Hash $targetHash = (Get-FileHash -Algorithm SHA256 -LiteralPath $target).Hash if ($sourceHash -ne $targetHash) { Remove-Item -LiteralPath $target -Force throw "复制后的文件校验和不一致,迁移已终止并清理目标文件。" } $newUrl = "/api/merchant/v1/files/qualifications/$fileName" $escapedOldUrl = $OldDocumentUrl.Replace("'", "''") $escapedNewUrl = $newUrl.Replace("'", "''") $output = [System.IO.Path]::GetFullPath($OutputDirectory) New-Item -ItemType Directory -Force -Path $output | Out-Null $forwardSql = @" START TRANSACTION; UPDATE baiyue_iam.iam_tenant_qualification SET document_url = '$escapedNewUrl' WHERE id = $QualificationId AND tenant_id = $TenantId AND document_url = '$escapedOldUrl' AND deleted = 0; -- 仅当 ROW_COUNT() = 1 时提交,否则回滚并删除:$target COMMIT; "@ $rollbackSql = @" START TRANSACTION; UPDATE baiyue_iam.iam_tenant_qualification SET document_url = '$escapedOldUrl' WHERE id = $QualificationId AND tenant_id = $TenantId AND document_url = '$escapedNewUrl' AND deleted = 0; -- 仅当 ROW_COUNT() = 1 时提交;确认旧文件可用后再删除:$target COMMIT; "@ $forwardPath = Join-Path $output "qualification-$QualificationId-forward.sql" $rollbackPath = Join-Path $output "qualification-$QualificationId-rollback.sql" [System.IO.File]::WriteAllText($forwardPath, $forwardSql, [System.Text.UTF8Encoding]::new($false)) [System.IO.File]::WriteAllText($rollbackPath, $rollbackSql, [System.Text.UTF8Encoding]::new($false)) [pscustomobject]@{ TenantId = $TenantId QualificationId = $QualificationId DocumentUrl = $newUrl PhysicalFile = $target Sha256 = $targetHash ForwardSql = $forwardPath RollbackSql = $rollbackPath MerchantPreview = $newUrl PlatformPreview = "/api/platform/v1/files/qualifications/$TenantId/$fileName" }