$ErrorActionPreference = "Stop" function Read-DotEnv { param([string]$Path) $map = @{} Get-Content -Encoding UTF8 $Path | ForEach-Object { $line = $_.Trim() if ($line.Length -eq 0 -or $line.StartsWith("#")) { return } if ($line -match "^([^=]+)=(.*)$") { $map[$Matches[1].Trim()] = $Matches[2].Trim() } } return $map } function Field { param( [string]$Type, [string]$TargetClass = "" ) if ($Type -eq "Pointer") { return @{ type = $Type; targetClass = $TargetClass } } return @{ type = $Type } } $envPath = Join-Path $PSScriptRoot "..\.env" $envMap = Read-DotEnv -Path $envPath $required = @("PARSE_APP_ID", "PARSE_REST_API_KEY", "PARSE_MASTER_KEY", "PARSE_SERVER_URL") $missing = $required | Where-Object { -not $envMap.ContainsKey($_) -or [string]::IsNullOrWhiteSpace($envMap[$_]) } if ($missing.Count -gt 0) { throw "Missing Parse env: $($missing -join ', ')" } $baseUrl = $envMap["PARSE_SERVER_URL"].TrimEnd("/") $headers = @{ "X-Parse-Application-Id" = $envMap["PARSE_APP_ID"] "X-Parse-REST-API-Key" = $envMap["PARSE_REST_API_KEY"] "X-Parse-Master-Key" = $envMap["PARSE_MASTER_KEY"] } $authenticatedClp = @{ find = @{ requiresAuthentication = $true } get = @{ requiresAuthentication = $true } count = @{ requiresAuthentication = $true } create = @{ requiresAuthentication = $true } update = @{ requiresAuthentication = $true } delete = @{} addField = @{} } $schemas = @( @{ className = "_User" description = "用户表" fields = @{ name = (Field "String") role = (Field "String") phone = (Field "String") avatarUrl = (Field "String") status = (Field "String") company = (Field "Pointer" "Company") } }, @{ className = "Company" description = "客户企业表" fields = @{ name = (Field "String") creditCode = (Field "String") contactName = (Field "String") contactPhone = (Field "String") email = (Field "String") serviceLevel = (Field "String") address = (Field "String") remark = (Field "String") status = (Field "String") mockBatch = (Field "String") } }, @{ className = "Ticket" description = "工单表" fields = @{ ticketNo = (Field "String") title = (Field "String") description = (Field "String") category = (Field "String") priority = (Field "String") impactScope = (Field "String") status = (Field "String") company = (Field "Pointer" "Company") creator = (Field "Pointer" "_User") assignee = (Field "Pointer" "_User") firstRespondedAt = (Field "Date") resolvedAt = (Field "Date") closedAt = (Field "Date") isOverdue = (Field "Boolean") slaDeadline = (Field "Date") companyName = (Field "String") ownerName = (Field "String") mockBatch = (Field "String") } }, @{ className = "TicketComment" description = "工单沟通记录" fields = @{ ticket = (Field "Pointer" "Ticket") author = (Field "Pointer" "_User") content = (Field "String") visibility = (Field "String") mockBatch = (Field "String") } }, @{ className = "TicketAttachment" description = "工单附件" fields = @{ ticket = (Field "Pointer" "Ticket") uploader = (Field "Pointer" "_User") fileName = (Field "String") fileUrl = (Field "String") fileType = (Field "String") fileSize = (Field "Number") businessType = (Field "String") mockBatch = (Field "String") } }, @{ className = "TicketStatusLog" description = "工单状态流转日志" fields = @{ ticket = (Field "Pointer" "Ticket") operator = (Field "Pointer" "_User") fromStatus = (Field "String") toStatus = (Field "String") remark = (Field "String") mockBatch = (Field "String") } }, @{ className = "SlaRule" description = "SLA 规则" fields = @{ priority = (Field "String") responseMinutes = (Field "Number") resolveHours = (Field "Number") alertBeforeMinutes = (Field "Number") enabled = (Field "Boolean") mockBatch = (Field "String") } }, @{ className = "KnowledgeCategory" description = "知识库分类" fields = @{ name = (Field "String") sortOrder = (Field "Number") enabled = (Field "Boolean") mockBatch = (Field "String") } }, @{ className = "KnowledgeArticle" description = "知识库文章" fields = @{ title = (Field "String") category = (Field "Pointer" "KnowledgeCategory") summary = (Field "String") content = (Field "String") tags = (Field "Array") visibility = (Field "String") status = (Field "String") author = (Field "Pointer" "_User") viewCount = (Field "Number") referenceCount = (Field "Number") mockBatch = (Field "String") } }, @{ className = "SatisfactionReview" description = "满意度评价" fields = @{ ticket = (Field "Pointer" "Ticket") customer = (Field "Pointer" "_User") serviceScore = (Field "Number") speedScore = (Field "Number") solutionScore = (Field "Number") averageScore = (Field "Number") content = (Field "String") followedUp = (Field "Boolean") mockBatch = (Field "String") } }, @{ className = "Notification" description = "站内通知" fields = @{ receiver = (Field "Pointer" "_User") title = (Field "String") content = (Field "String") type = (Field "String") read = (Field "Boolean") relatedTicket = (Field "Pointer" "Ticket") mockBatch = (Field "String") } }, @{ className = "AuditLog" description = "操作日志" fields = @{ operator = (Field "Pointer" "_User") module = (Field "String") action = (Field "String") targetClass = (Field "String") targetId = (Field "String") ip = (Field "String") success = (Field "Boolean") detail = (Field "Object") mockBatch = (Field "String") } } ) function Test-SchemaExists { param([string]$ClassName) try { Invoke-RestMethod -Method Get -Uri "$baseUrl/schemas/$ClassName" -Headers $headers | Out-Null return $true } catch { if ($_.Exception.Response -and [int]$_.Exception.Response.StatusCode -eq 404) { return $false } if ($_.ErrorDetails -and $_.ErrorDetails.Message -match '"code"\s*:\s*103') { return $false } if ($_.Exception.Message -match 'does not exist') { return $false } throw } } foreach ($schema in $schemas) { $exists = Test-SchemaExists -ClassName $schema.className $body = @{ fields = $schema.fields } if ($schema.className -ne "_User") { $body.classLevelPermissions = $authenticatedClp } $json = $body | ConvertTo-Json -Depth 12 $method = if ($exists) { "Put" } else { "Post" } Invoke-RestMethod -Method $method -Uri "$baseUrl/schemas/$($schema.className)" -Headers $headers -ContentType "application/json" -Body $json | Out-Null $verb = if ($exists) { "Updated" } else { "Created" } Write-Host "$verb $($schema.className) - $($schema.description)" } Write-Host "Parse schema initialization finished. Class count: $($schemas.Count)"