feat: initial project setup
- Add .NET 8 backend with Clean Architecture - Add React + Vite + TypeScript frontend - Implement authentication with JWT - Implement Azure Blob Storage client - Implement OCR integration - Implement supplier matching service - Implement voucher generation - Implement Fortnox provider - Add unit and integration tests - Add Docker Compose configuration
This commit is contained in:
853
DEPLOYMENT_GUIDE.md
Normal file
853
DEPLOYMENT_GUIDE.md
Normal file
@@ -0,0 +1,853 @@
|
||||
# Invoice Master - 部署指南
|
||||
|
||||
**版本**: v3.0
|
||||
**目标平台**: Azure
|
||||
**运行时**: .NET 8
|
||||
**日期**: 2026-02-03
|
||||
|
||||
---
|
||||
|
||||
## 1. 架构概览
|
||||
|
||||
### 1.1 多会计系统架构部署图
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
│ Azure Sweden Central │
|
||||
│ │
|
||||
│ ┌─────────────────────────────────────────────────────────────────────┐ │
|
||||
│ │ Azure Container Apps Environment │ │
|
||||
│ │ │ │
|
||||
│ │ ┌─────────────────┐ ┌─────────────────┐ ┌───────────────┐ │ │
|
||||
│ │ │ Frontend App │ │ Backend API │ │ Worker │ │ │
|
||||
│ │ │ (Static Web) │ │ (FastAPI) │ │ (Background) │ │ │
|
||||
│ │ │ │ │ │ │ │ │ │
|
||||
│ │ │ Vercel/Azure │ │ CPU: 1 vCPU │ │ CPU: 0.5 │ │ │
|
||||
│ │ │ Static Web │ │ Memory: 2 GiB │ │ Memory: 1GiB │ │ │
|
||||
│ │ │ │ │ Replicas: 1-5 │ │ Replicas: 1-3│ │ │
|
||||
│ │ └─────────────────┘ └─────────────────┘ └───────────────┘ │ │
|
||||
│ │ │ │
|
||||
│ │ ┌─────────────────────────────────────────────────────────────┐ │ │
|
||||
│ │ │ Azure Application Gateway (WAF) │ │ │
|
||||
│ │ │ SSL Termination │ │ │
|
||||
│ │ └─────────────────────────────────────────────────────────────┘ │ │
|
||||
│ └─────────────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─────────────────────────┐ ┌─────────────────────────────────────────┐ │
|
||||
│ │ PostgreSQL Flexible │ │ Azure Cache for Redis │ │
|
||||
│ │ Server │ │ │ │
|
||||
│ │ - SKU: Standard_B1ms │ │ - SKU: Basic C1 │ │
|
||||
│ │ - Storage: 32 GB │ │ - Memory: 1 GB │ │
|
||||
│ │ - Backup: 7 days │ │ │ │
|
||||
│ │ - accounting_ │ │ - Multi-provider cache │ │
|
||||
│ │ connections table │ │ │ │
|
||||
│ └─────────────────────────┘ └─────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─────────────────────────────────────────────────────────────────────┐ │
|
||||
│ │ Azure Blob Storage │ │
|
||||
│ │ - Tier: Hot │ │
|
||||
│ │ - Redundancy: LRS │ │
|
||||
│ │ - Invoice PDFs (multi-tenant) │ │
|
||||
│ └─────────────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─────────────────────────────────────────────────────────────────────┐ │
|
||||
│ │ Azure Key Vault │ │
|
||||
│ │ - Encryption Keys │ │
|
||||
│ │ - Fortnox Client Credentials │ │
|
||||
│ │ - Visma Client Credentials (future) │ │
|
||||
│ │ - Hogia Client Credentials (future) │ │
|
||||
│ │ - Provider-specific configs │ │
|
||||
│ └─────────────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 1.2 多会计系统配置
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ Key Vault Secrets Structure │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ Global Secrets: │
|
||||
│ ├── jwt-secret │
|
||||
│ ├── encryption-key │
|
||||
│ ├── db-password │
|
||||
│ └── ocr-api-key │
|
||||
│ │
|
||||
│ Provider-Specific Secrets: │
|
||||
│ ├── fortnox-client-id │
|
||||
│ ├── fortnox-client-secret │
|
||||
│ ├── fortnox-redirect-uri │
|
||||
│ ├── visma-client-id (future) │
|
||||
│ ├── visma-client-secret (future) │
|
||||
│ └── visma-redirect-uri (future) │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. 前置要求
|
||||
|
||||
### 2.1 工具安装
|
||||
|
||||
```bash
|
||||
# Azure CLI
|
||||
curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash
|
||||
|
||||
# Terraform
|
||||
wget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
|
||||
echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
|
||||
sudo apt update && sudo apt install terraform
|
||||
|
||||
# kubectl (可选)
|
||||
az aks install-cli
|
||||
```
|
||||
|
||||
### 2.2 Azure 登录
|
||||
|
||||
```bash
|
||||
# 登录 Azure
|
||||
az login
|
||||
|
||||
# 设置订阅
|
||||
az account set --subscription "Your Subscription Name"
|
||||
|
||||
# 创建 Service Principal (用于 Terraform)
|
||||
az ad sp create-for-rbac --name "invoice-master-terraform" --role Contributor \
|
||||
--scopes /subscriptions/{subscription-id}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 基础设施部署
|
||||
|
||||
### 3.1 Terraform 配置
|
||||
|
||||
```bash
|
||||
cd infrastructure/terraform
|
||||
|
||||
# 初始化
|
||||
terraform init
|
||||
|
||||
# 创建变量文件
|
||||
cat > terraform.tfvars <<EOF
|
||||
# 基础配置
|
||||
environment = "production"
|
||||
location = "swedencentral"
|
||||
resource_group_name = "rg-invoice-master-prod"
|
||||
|
||||
# 数据库
|
||||
db_admin_username = "dbadmin"
|
||||
db_admin_password = "YourSecurePassword123!"
|
||||
db_sku = "Standard_B1ms"
|
||||
db_storage_mb = 32768
|
||||
|
||||
# Redis
|
||||
redis_sku = "Basic"
|
||||
redis_family = "C"
|
||||
redis_capacity = 1
|
||||
|
||||
# Container Apps
|
||||
ca_cpu = "1.0"
|
||||
ca_memory = "2.0Gi"
|
||||
ca_min_replicas = 1
|
||||
ca_max_replicas = 5
|
||||
|
||||
# 域名
|
||||
domain_name = "app.invoice-master.app"
|
||||
EOF
|
||||
|
||||
# 计划
|
||||
terraform plan
|
||||
|
||||
# 应用
|
||||
terraform apply
|
||||
```
|
||||
|
||||
### 3.2 Terraform 模块结构
|
||||
|
||||
```
|
||||
infrastructure/terraform/
|
||||
├── main.tf # 主配置
|
||||
├── variables.tf # 变量定义
|
||||
├── outputs.tf # 输出
|
||||
├── providers.tf # 提供商配置
|
||||
├── backend.tf # 远程状态
|
||||
├── terraform.tfvars # 变量值 (gitignore)
|
||||
│
|
||||
└── modules/
|
||||
├── database/ # PostgreSQL 模块
|
||||
│ ├── main.tf
|
||||
│ ├── variables.tf
|
||||
│ └── outputs.tf
|
||||
│
|
||||
├── cache/ # Redis 模块
|
||||
│ ├── main.tf
|
||||
│ ├── variables.tf
|
||||
│ └── outputs.tf
|
||||
│
|
||||
├── storage/ # Blob Storage 模块
|
||||
│ ├── main.tf
|
||||
│ ├── variables.tf
|
||||
│ └── outputs.tf
|
||||
│
|
||||
├── container_apps/ # Container Apps 模块
|
||||
│ ├── main.tf
|
||||
│ ├── variables.tf
|
||||
│ └── outputs.tf
|
||||
│
|
||||
└── keyvault/ # Key Vault 模块
|
||||
├── main.tf
|
||||
├── variables.tf
|
||||
└── outputs.tf
|
||||
```
|
||||
|
||||
### 3.3 主要资源清单
|
||||
|
||||
| 资源 | 名称 | SKU/配置 |
|
||||
|------|------|----------|
|
||||
| Resource Group | rg-invoice-master-prod | Sweden Central |
|
||||
| PostgreSQL | psql-invoice-master-prod | Standard_B1ms |
|
||||
| Redis | redis-invoice-master-prod | Basic C1 |
|
||||
| Blob Storage | stinvoicemasterprod | Standard LRS |
|
||||
| Container Apps Env | cae-invoice-master-prod | Consumption |
|
||||
| Backend App | ca-invoice-master-api | 1 CPU, 2Gi |
|
||||
| Worker App | ca-invoice-master-worker | 0.5 CPU, 1Gi |
|
||||
| Key Vault | kv-invoice-master-prod | Standard |
|
||||
| Log Analytics | log-invoice-master-prod | Per GB2018 |
|
||||
| Application Insights | ai-invoice-master-prod | Web |
|
||||
|
||||
---
|
||||
|
||||
## 4. 应用部署
|
||||
|
||||
### 4.1 构建 .NET 应用
|
||||
|
||||
#### 4.1.1 本地发布
|
||||
|
||||
```bash
|
||||
cd backend/src/InvoiceMaster.API
|
||||
|
||||
# 发布 Release 版本
|
||||
dotnet publish -c Release -o ./publish \
|
||||
--runtime linux-x64 \
|
||||
--self-contained false
|
||||
|
||||
# 或者自包含(无需运行时)
|
||||
dotnet publish -c Release -o ./publish \
|
||||
--runtime linux-x64 \
|
||||
--self-contained true \
|
||||
-p:PublishSingleFile=true
|
||||
```
|
||||
|
||||
#### 4.1.2 Docker 构建
|
||||
|
||||
```dockerfile
|
||||
# Dockerfile
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
|
||||
WORKDIR /app
|
||||
EXPOSE 8080
|
||||
|
||||
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
|
||||
WORKDIR /src
|
||||
COPY ["src/InvoiceMaster.API/InvoiceMaster.API.csproj", "src/InvoiceMaster.API/"]
|
||||
COPY ["src/InvoiceMaster.Core/InvoiceMaster.Core.csproj", "src/InvoiceMaster.Core/"]
|
||||
COPY ["src/InvoiceMaster.Application/InvoiceMaster.Application.csproj", "src/InvoiceMaster.Application/"]
|
||||
COPY ["src/InvoiceMaster.Infrastructure/InvoiceMaster.Infrastructure.csproj", "src/InvoiceMaster.Infrastructure/"]
|
||||
COPY ["src/InvoiceMaster.Integrations/InvoiceMaster.Integrations.csproj", "src/InvoiceMaster.Integrations/"]
|
||||
RUN dotnet restore "src/InvoiceMaster.API/InvoiceMaster.API.csproj"
|
||||
COPY . .
|
||||
WORKDIR "/src/src/InvoiceMaster.API"
|
||||
RUN dotnet build "InvoiceMaster.API.csproj" -c Release -o /app/build
|
||||
|
||||
FROM build AS publish
|
||||
RUN dotnet publish "InvoiceMaster.API.csproj" -c Release -o /app/publish
|
||||
|
||||
FROM base AS final
|
||||
WORKDIR /app
|
||||
COPY --from=publish /app/publish .
|
||||
ENTRYPOINT ["dotnet", "InvoiceMaster.API.dll"]
|
||||
```
|
||||
|
||||
```bash
|
||||
# 构建并推送镜像
|
||||
docker build -t invoice-master-api:latest -f Dockerfile .
|
||||
docker tag invoice-master-api:latest {acr-name}.azurecr.io/invoice-master-api:v1.0.0
|
||||
|
||||
# 推送到 ACR
|
||||
az acr login --name {acr-name}
|
||||
docker push {acr-name}.azurecr.io/invoice-master-api:v1.0.0
|
||||
```
|
||||
|
||||
### 4.2 配置环境变量
|
||||
|
||||
在 Azure Key Vault 中创建以下 secrets:
|
||||
|
||||
#### 全局 Secrets
|
||||
|
||||
| Secret Name | 说明 |
|
||||
|-------------|------|
|
||||
| `db-password` | PostgreSQL 密码 |
|
||||
| `jwt-secret` | JWT 签名密钥 |
|
||||
| `encryption-key` | AES-256 加密密钥 |
|
||||
| `ocr-api-key` | Invoice Master API Key |
|
||||
|
||||
#### Provider-Specific Secrets
|
||||
|
||||
| Secret Name | 说明 | 状态 |
|
||||
|-------------|------|------|
|
||||
| `fortnox-client-id` | Fortnox OAuth Client ID | Required |
|
||||
| `fortnox-client-secret` | Fortnox OAuth Client Secret | Required |
|
||||
| `fortnox-redirect-uri` | Fortnox OAuth Redirect URI | Required |
|
||||
| `visma-client-id` | Visma OAuth Client ID | Future |
|
||||
| `visma-client-secret` | Visma OAuth Client Secret | Future |
|
||||
| `visma-redirect-uri` | Visma OAuth Redirect URI | Future |
|
||||
|
||||
```bash
|
||||
# 示例: 添加 global secrets
|
||||
az keyvault secret set --vault-name kv-invoice-master-prod \
|
||||
--name "jwt-secret" \
|
||||
--value "your-256-bit-secret-key-here"
|
||||
|
||||
# 示例: 添加 Fortnox secrets
|
||||
az keyvault secret set --vault-name kv-invoice-master-prod \
|
||||
--name "fortnox-client-id" \
|
||||
--value "your-fortnox-client-id"
|
||||
|
||||
az keyvault secret set --vault-name kv-invoice-master-prod \
|
||||
--name "fortnox-client-secret" \
|
||||
--value "your-fortnox-client-secret"
|
||||
```
|
||||
|
||||
### 4.3 部署到 Container Apps
|
||||
|
||||
```bash
|
||||
# 更新后端应用
|
||||
az containerapp update \
|
||||
--name ca-invoice-master-api \
|
||||
--resource-group rg-invoice-master-prod \
|
||||
--image {acr-name}.azurecr.io/invoice-master-api:v1.0.0 \
|
||||
--set-env-vars "ENVIRONMENT=production"
|
||||
```
|
||||
|
||||
### 4.4 数据库迁移 (EF Core)
|
||||
|
||||
```bash
|
||||
# 方法 1: 使用 EF Core CLI (推荐开发环境)
|
||||
cd backend/src/InvoiceMaster.Infrastructure
|
||||
|
||||
# 添加迁移
|
||||
dotnet ef migrations add InitialCreate \
|
||||
--startup-project ../InvoiceMaster.API
|
||||
|
||||
# 更新数据库
|
||||
dotnet ef database update \
|
||||
--startup-project ../InvoiceMaster.API \
|
||||
--connection "Host=psql-invoice-master-prod.postgres.database.azure.com;Database=invoice_master;Username=dbadmin;Password=$DB_PASSWORD"
|
||||
|
||||
# 生成 SQL 脚本 (生产环境推荐)
|
||||
dotnet ef migrations script \
|
||||
--startup-project ../InvoiceMaster.API \
|
||||
--output migration.sql
|
||||
|
||||
# 执行 SQL 脚本
|
||||
psql "Host=psql-invoice-master-prod.postgres.database.azure.com;Database=invoice_master;Username=dbadmin" \
|
||||
-f migration.sql
|
||||
```
|
||||
|
||||
#### 4.4.1 生产环境迁移最佳实践
|
||||
|
||||
```bash
|
||||
# 使用临时 Container App Job 运行迁移
|
||||
az containerapp job create \
|
||||
--name db-migration-job \
|
||||
--resource-group rg-invoice-master-prod \
|
||||
--environment cae-invoice-master-prod \
|
||||
--image {acr-name}.azurecr.io/invoice-master-api:v1.0.0 \
|
||||
--command "dotnet ef database update" \
|
||||
--env-vars "ConnectionStrings__DefaultConnection=secretref:db-connection-string"
|
||||
```
|
||||
|
||||
### 4.5 添加新的会计系统 Provider
|
||||
|
||||
当需要添加新的会计系统时:
|
||||
|
||||
```bash
|
||||
# 1. 在 Key Vault 中添加新 Provider 的 credentials
|
||||
az keyvault secret set \
|
||||
--vault-name kv-invoice-master-prod \
|
||||
--name "visma-client-id" \
|
||||
--value "your-visma-client-id"
|
||||
|
||||
# 2. 更新 Container Apps 环境变量
|
||||
az containerapp update \
|
||||
--name ca-invoice-master-api \
|
||||
--resource-group rg-invoice-master-prod \
|
||||
--set-env-vars "VISMA_CLIENT_ID=secretref:visma-client-id"
|
||||
|
||||
# 3. 重启应用
|
||||
az containerapp revision restart \
|
||||
--name ca-invoice-master-api \
|
||||
--resource-group rg-invoice-master-prod
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 前端部署
|
||||
|
||||
### 5.1 构建
|
||||
|
||||
```bash
|
||||
cd frontend
|
||||
|
||||
# 安装依赖
|
||||
npm install
|
||||
|
||||
# 构建生产版本
|
||||
npm run build
|
||||
```
|
||||
|
||||
### 5.2 部署到 Azure Static Web Apps
|
||||
|
||||
```bash
|
||||
# 使用 SWA CLI
|
||||
npm install -g @azure/static-web-apps-cli
|
||||
|
||||
# 部署
|
||||
swa deploy ./dist \
|
||||
--env production \
|
||||
--app-name stapp-invoice-master-prod \
|
||||
--resource-group rg-invoice-master-prod
|
||||
```
|
||||
|
||||
### 5.3 或部署到 Vercel
|
||||
|
||||
```bash
|
||||
# 安装 Vercel CLI
|
||||
npm i -g vercel
|
||||
|
||||
# 部署
|
||||
vercel --prod
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 域名和 SSL
|
||||
|
||||
### 6.1 配置自定义域名
|
||||
|
||||
```bash
|
||||
# Container Apps 自定义域名
|
||||
az containerapp hostname add \
|
||||
--name ca-invoice-master-api \
|
||||
--resource-group rg-invoice-master-prod \
|
||||
--hostname api.invoice-master.app
|
||||
|
||||
# 绑定证书 (Managed Certificate)
|
||||
az containerapp hostname bind \
|
||||
--name ca-invoice-master-api \
|
||||
--resource-group rg-invoice-master-prod \
|
||||
--hostname api.invoice-master.app \
|
||||
--environment cae-invoice-master-prod \
|
||||
--validation-method CNAME
|
||||
```
|
||||
|
||||
### 6.2 DNS 配置
|
||||
|
||||
在 DNS 提供商处添加以下记录:
|
||||
|
||||
| 类型 | 主机 | 值 |
|
||||
|------|------|-----|
|
||||
| CNAME | api | ca-invoice-master-api.{region}.azurecontainerapps.io |
|
||||
| CNAME | app | {static-web-app-url} |
|
||||
|
||||
---
|
||||
|
||||
## 7. 监控配置
|
||||
|
||||
### 7.1 Application Insights
|
||||
|
||||
```bash
|
||||
# 获取连接字符串
|
||||
APP_INSIGHTS_CONN=$(az monitor app-insights component show \
|
||||
--app ai-invoice-master-prod \
|
||||
--resource-group rg-invoice-master-prod \
|
||||
--query connectionString -o tsv)
|
||||
|
||||
# 配置到 Container Apps
|
||||
az containerapp update \
|
||||
--name ca-invoice-master-api \
|
||||
--resource-group rg-invoice-master-prod \
|
||||
--set-env-vars "APPLICATIONINSIGHTS_CONNECTION_STRING=$APP_INSIGHTS_CONN"
|
||||
```
|
||||
|
||||
### 7.2 多会计系统监控
|
||||
|
||||
```kusto
|
||||
// 查看各 Provider API 调用情况
|
||||
AppRequests
|
||||
| where TimeGenerated > ago(1h)
|
||||
| where OperationName contains "accounting"
|
||||
| summarize count(), avg(DurationMs) by Provider = tostring(CustomDimensions.provider)
|
||||
| order by count_ desc
|
||||
|
||||
// 查看 Provider 错误率
|
||||
AppExceptions
|
||||
| where TimeGenerated > ago(1h)
|
||||
| where ProblemId contains "fortnox" or ProblemId contains "visma"
|
||||
| summarize count() by Provider = tostring(CustomDimensions.provider), bin(TimeGenerated, 5m)
|
||||
| render timechart
|
||||
```
|
||||
|
||||
### 7.3 告警规则
|
||||
|
||||
```bash
|
||||
# 创建 CPU 使用率告警
|
||||
az monitor metrics alert create \
|
||||
--name "High CPU Alert" \
|
||||
--resource-group rg-invoice-master-prod \
|
||||
--scopes $(az containerapp show --name ca-invoice-master-api --resource-group rg-invoice-master-prod --query id -o tsv) \
|
||||
--condition "avg cpu percentage > 80" \
|
||||
--window-size 5m \
|
||||
--evaluation-frequency 1m \
|
||||
--action $(az monitor action-group show --name ag-invoice-master --resource-group rg-invoice-master-prod --query id -o tsv)
|
||||
|
||||
# 创建 Provider API 错误告警
|
||||
az monitor metrics alert create \
|
||||
--name "Provider API Errors" \
|
||||
--resource-group rg-invoice-master-prod \
|
||||
--scopes $(az monitor app-insights component show --name ai-invoice-master-prod --resource-group rg-invoice-master-prod --query id -o tsv) \
|
||||
--condition "count exceptions > 10" \
|
||||
--window-size 5m \
|
||||
--evaluation-frequency 1m
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. 备份策略
|
||||
|
||||
### 8.1 数据库备份
|
||||
|
||||
```bash
|
||||
# 配置自动备份 (已在 Terraform 中配置)
|
||||
# 手动备份
|
||||
az postgres flexible-server backup create \
|
||||
--name manual-backup-$(date +%Y%m%d) \
|
||||
--server-name psql-invoice-master-prod \
|
||||
--resource-group rg-invoice-master-prod
|
||||
|
||||
# 备份 accounting_connections 表 (关键)
|
||||
pg_dump "host=psql-invoice-master-prod.postgres.database.azure.com user=dbadmin dbname=invoice_master sslmode=require" \
|
||||
--table=accounting_connections \
|
||||
--table=invoices \
|
||||
--table=users > critical_tables_backup.sql
|
||||
```
|
||||
|
||||
### 8.2 Blob Storage 备份
|
||||
|
||||
```bash
|
||||
# 启用版本控制
|
||||
az storage account blob-service-properties update \
|
||||
--account-name stinvoicemasterprod \
|
||||
--enable-versioning true
|
||||
|
||||
# 配置生命周期策略
|
||||
az storage account management-policy create \
|
||||
--account-name stinvoicemasterprod \
|
||||
--policy @lifecycle-policy.json
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. 安全加固
|
||||
|
||||
### 9.1 网络安全
|
||||
|
||||
```bash
|
||||
# 配置防火墙规则
|
||||
az postgres flexible-server firewall-rule create \
|
||||
--name AllowContainerApps \
|
||||
--server-name psql-invoice-master-prod \
|
||||
--resource-group rg-invoice-master-prod \
|
||||
--start-ip-address 0.0.0.0 \
|
||||
--end-ip-address 0.0.0.0
|
||||
|
||||
# 启用私有链接 (可选)
|
||||
```
|
||||
|
||||
### 9.2 Key Vault 访问策略
|
||||
|
||||
```bash
|
||||
# 授予 Container Apps 访问 Key Vault 的权限
|
||||
az keyvault set-policy \
|
||||
--name kv-invoice-master-prod \
|
||||
--object-id $(az containerapp show --name ca-invoice-master-api --resource-group rg-invoice-master-prod --query identity.principalId -o tsv) \
|
||||
--secret-permissions get list
|
||||
|
||||
# 为新的 Provider 添加 secrets 时,确保权限已配置
|
||||
```
|
||||
|
||||
### 9.3 多租户数据隔离
|
||||
|
||||
```sql
|
||||
-- 验证数据隔离
|
||||
SELECT provider, COUNT(*) as connection_count
|
||||
FROM accounting_connections
|
||||
GROUP BY provider;
|
||||
|
||||
-- 检查用户数据访问权限
|
||||
SELECT u.email, ac.provider, ac.company_name
|
||||
FROM users u
|
||||
JOIN accounting_connections ac ON u.id = ac.user_id
|
||||
WHERE u.email = 'test@example.com';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 10. 回滚策略
|
||||
|
||||
### 10.1 应用回滚
|
||||
|
||||
```bash
|
||||
# 回滚到上一个版本
|
||||
az containerapp revision list \
|
||||
--name ca-invoice-master-api \
|
||||
--resource-group rg-invoice-master-prod
|
||||
|
||||
az containerapp update \
|
||||
--name ca-invoice-master-api \
|
||||
--resource-group rg-invoice-master-prod \
|
||||
--revision-suffix {previous-revision}
|
||||
```
|
||||
|
||||
### 10.2 数据库回滚
|
||||
|
||||
```bash
|
||||
# 从备份恢复
|
||||
az postgres flexible-server restore \
|
||||
--name psql-invoice-master-prod-restored \
|
||||
--resource-group rg-invoice-master-prod \
|
||||
--source-server psql-invoice-master-prod \
|
||||
--point-in-time "2026-02-03T10:00:00Z"
|
||||
|
||||
# 回滚特定 Provider 的数据 (谨慎操作)
|
||||
DELETE FROM accounting_connections WHERE provider = 'fortnox';
|
||||
```
|
||||
|
||||
### 10.3 Provider 配置回滚
|
||||
|
||||
```bash
|
||||
# 如果新 Provider 配置出错,快速禁用
|
||||
az containerapp update \
|
||||
--name ca-invoice-master-api \
|
||||
--resource-group rg-invoice-master-prod \
|
||||
--set-env-vars "ENABLED_PROVIDERS=fortnox"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 11. CI/CD 配置
|
||||
|
||||
### 11.1 GitHub Actions 工作流
|
||||
|
||||
```yaml
|
||||
# .github/workflows/deploy.yml
|
||||
name: Deploy to Azure
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
deploy-backend:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Azure Login
|
||||
uses: azure/login@v1
|
||||
with:
|
||||
creds: ${{ secrets.AZURE_CREDENTIALS }}
|
||||
|
||||
- name: Build and Push
|
||||
run: |
|
||||
az acr build --registry ${{ secrets.ACR_NAME }} \
|
||||
--image invoice-master-api:${{ github.sha }} \
|
||||
./backend
|
||||
|
||||
- name: Deploy
|
||||
run: |
|
||||
az containerapp update \
|
||||
--name ca-invoice-master-api \
|
||||
--resource-group rg-invoice-master-prod \
|
||||
--image ${{ secrets.ACR_NAME }}.azurecr.io/invoice-master-api:${{ github.sha }}
|
||||
|
||||
- name: Run Migrations
|
||||
run: |
|
||||
# 使用临时容器运行 EF Core 迁移
|
||||
az containerapp job create \
|
||||
--name migration-job \
|
||||
--resource-group rg-invoice-master-prod \
|
||||
--image ${{ secrets.ACR_NAME }}.azurecr.io/invoice-master-api:${{ github.sha }} \
|
||||
--command "dotnet ef database update --no-build"
|
||||
```
|
||||
|
||||
### 11.2 多环境部署
|
||||
|
||||
```yaml
|
||||
# 不同环境使用不同配置
|
||||
- name: Deploy to Staging
|
||||
if: github.ref == 'refs/heads/develop'
|
||||
run: |
|
||||
az containerapp update \
|
||||
--name ca-invoice-master-api-staging \
|
||||
--resource-group rg-invoice-master-staging
|
||||
|
||||
- name: Deploy to Production
|
||||
if: github.ref == 'refs/heads/main'
|
||||
run: |
|
||||
az containerapp update \
|
||||
--name ca-invoice-master-api \
|
||||
--resource-group rg-invoice-master-prod
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 12. 成本估算
|
||||
|
||||
### 12.1 基础资源成本
|
||||
|
||||
| 资源 | 每月估算 (SEK) |
|
||||
|------|----------------|
|
||||
| PostgreSQL (B1ms) | ~150 |
|
||||
| Redis (Basic C1) | ~100 |
|
||||
| Container Apps | ~200-500 |
|
||||
| Blob Storage | ~50-200 |
|
||||
| Key Vault | ~30 |
|
||||
| Application Insights | ~100 |
|
||||
| **基础总计** | **~630-1,080 SEK** |
|
||||
|
||||
### 12.2 多会计系统额外成本
|
||||
|
||||
| 项目 | 每月估算 (SEK) | 说明 |
|
||||
|------|----------------|------|
|
||||
| 额外的 API 调用 | ~0-100 | 取决于 Provider 数量 |
|
||||
| 额外的 Redis 缓存 | ~0-50 | Provider token 缓存 |
|
||||
| 额外的日志存储 | ~0-100 | 多 Provider 日志 |
|
||||
| **额外总计** | **~0-250 SEK** |
|
||||
|
||||
---
|
||||
|
||||
## 13. 故障排除
|
||||
|
||||
### 13.1 常见问题
|
||||
|
||||
**问题**: Container App 无法启动
|
||||
```bash
|
||||
# 查看日志
|
||||
az containerapp logs show \
|
||||
--name ca-invoice-master-api \
|
||||
--resource-group rg-invoice-master-prod \
|
||||
--follow
|
||||
```
|
||||
|
||||
**问题**: Provider 认证失败
|
||||
```bash
|
||||
# 检查 Key Vault secrets
|
||||
az keyvault secret list --vault-name kv-invoice-master-prod
|
||||
|
||||
# 验证特定 Provider 配置
|
||||
curl https://api.invoice-master.app/api/v1/accounting/providers \
|
||||
-H "Authorization: Bearer $TOKEN"
|
||||
```
|
||||
|
||||
**问题**: 数据库连接失败
|
||||
```bash
|
||||
# 测试连接
|
||||
psql "host=psql-invoice-master-prod.postgres.database.azure.com port=5432 dbname=invoice_master user=dbadmin sslmode=require"
|
||||
|
||||
# 检查连接表
|
||||
psql $DB_URL -c "SELECT provider, COUNT(*) FROM accounting_connections GROUP BY provider;"
|
||||
```
|
||||
|
||||
### 13.2 Provider 特定故障排除
|
||||
|
||||
**Fortnox OAuth 问题**:
|
||||
```bash
|
||||
# 检查 Fortnox 连接状态
|
||||
curl https://api.invoice-master.app/api/v1/accounting/fortnox/connection \
|
||||
-H "Authorization: Bearer $TOKEN"
|
||||
|
||||
# 重新授权
|
||||
# 引导用户访问: https://api.invoice-master.app/api/v1/accounting/fortnox/auth/url
|
||||
```
|
||||
|
||||
### 13.3 健康检查
|
||||
|
||||
```bash
|
||||
# API 健康检查
|
||||
curl https://api.invoice-master.app/health
|
||||
|
||||
# 详细健康检查 (包含 Provider 状态)
|
||||
curl https://api.invoice-master.app/health/detailed \
|
||||
-H "Authorization: Bearer $ADMIN_TOKEN"
|
||||
|
||||
# 预期响应:
|
||||
# {
|
||||
# "status": "healthy",
|
||||
# "providers": {
|
||||
# "fortnox": { "status": "connected", "latency_ms": 150 },
|
||||
# "visma": { "status": "not_configured" }
|
||||
# }
|
||||
# }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 14. 维护窗口
|
||||
|
||||
| 任务 | 频率 | 时间 |
|
||||
|------|------|------|
|
||||
| 安全更新 | 每周 | 周日 02:00-04:00 |
|
||||
| 数据库备份验证 | 每月 | 第一个周日 |
|
||||
| Provider API 健康检查 | 每周 | 周一 |
|
||||
| 性能审查 | 每季度 | - |
|
||||
| 证书续期检查 | 每月 | - |
|
||||
| Provider SDK 更新检查 | 每月 | - |
|
||||
|
||||
---
|
||||
|
||||
## 15. 扩展检查清单
|
||||
|
||||
### 添加新 Provider 时的部署检查清单
|
||||
|
||||
- [ ] 在 Key Vault 中添加 Provider credentials
|
||||
- [ ] 更新 Container Apps 环境变量
|
||||
- [ ] 更新 DNS/防火墙规则 (如需要)
|
||||
- [ ] 测试 Provider 连接
|
||||
- [ ] 更新监控告警规则
|
||||
- [ ] 更新文档
|
||||
- [ ] 通知团队
|
||||
|
||||
---
|
||||
|
||||
## 16. 联系信息
|
||||
|
||||
| 角色 | 联系方式 |
|
||||
|------|----------|
|
||||
| 技术负责人 | tech@example.com |
|
||||
| 运维团队 | ops@example.com |
|
||||
| 紧急联系 | +46-xxx-xxx-xxxx |
|
||||
| Provider 支持 | providers@example.com |
|
||||
|
||||
---
|
||||
|
||||
**文档历史:**
|
||||
|
||||
| 版本 | 日期 | 作者 | 变更 |
|
||||
|------|------|------|------|
|
||||
| 3.0 | 2026-02-03 | Claude Code | 重构为 .NET 8 + EF Core 部署 |
|
||||
| 2.0 | 2026-02-03 | Claude Code | 更新为多会计系统架构 |
|
||||
| 1.0 | 2026-02-03 | Claude Code | 初始版本 |
|
||||
Reference in New Issue
Block a user