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:
Invoice Master
2026-02-04 20:14:34 +01:00
commit 05ea67144f
250 changed files with 50402 additions and 0 deletions

877
API_DESIGN.md Normal file
View File

@@ -0,0 +1,877 @@
# Invoice Master - API 设计文档
**版本**: v3.0
**Base URL**: `https://api.invoice-master.app/api/v1`
**日期**: 2026-02-03
**技术栈**: .NET 8 + ASP.NET Core + MediatR (CQRS)
---
## 1. 概述
### 1.1 多会计系统支持
本 API 支持连接多个会计系统Fortnox, Visma, Hogia 等),通过统一的抽象层提供一致的接口。
**Provider 标识:**
- `fortnox` - Fortnox (瑞典)
- `visma` - Visma eAccounting (北欧)
- `hogia` - Hogia Smart (瑞典)
### 1.2 认证方式
API 使用 **JWT Bearer Token** 进行认证:
```http
Authorization: Bearer <jwt_token>
```
### 1.3 响应格式
所有响应使用 JSON 格式,统一结构:
```json
{
"success": true,
"data": { ... },
"meta": {
"request_id": "req_abc123",
"timestamp": "2026-02-03T10:30:00Z"
}
}
```
错误响应:
```json
{
"success": false,
"error": {
"code": "ERROR_CODE",
"message": "Human readable message",
"details": { ... }
},
"meta": {
"request_id": "req_abc123",
"timestamp": "2026-02-03T10:30:00Z"
}
}
```
### 1.4 HTTP 状态码
| 状态码 | 含义 |
|--------|------|
| 200 | 成功 |
| 201 | 创建成功 |
| 400 | 请求参数错误 |
| 401 | 未认证 |
| 403 | 无权限 |
| 404 | 资源不存在 |
| 409 | 资源冲突 |
| 422 | 业务逻辑错误 |
| 429 | 请求过于频繁 |
| 500 | 服务器错误 |
---
## 2. 认证相关
### 2.1 用户注册
```http
POST /auth/register
Content-Type: application/json
{
"email": "user@example.com",
"password": "SecurePass123!",
"full_name": "John Doe"
}
```
**响应:**
```json
{
"success": true,
"data": {
"user": {
"id": "uuid",
"email": "user@example.com",
"full_name": "John Doe",
"created_at": "2026-02-03T10:30:00Z"
},
"tokens": {
"access_token": "eyJhbGciOiJIUzI1NiIs...",
"refresh_token": "eyJhbGciOiJIUzI1NiIs...",
"expires_in": 900
}
}
}
```
### 2.2 用户登录
```http
POST /auth/login
Content-Type: application/json
{
"email": "user@example.com",
"password": "SecurePass123!"
}
```
**响应:**
```json
{
"success": true,
"data": {
"user": {
"id": "uuid",
"email": "user@example.com",
"full_name": "John Doe",
"connections": [
{
"provider": "fortnox",
"connected": true,
"company_name": "My Company AB"
}
]
},
"tokens": {
"access_token": "eyJhbGciOiJIUzI1NiIs...",
"refresh_token": "eyJhbGciOiJIUzI1NiIs...",
"expires_in": 900
}
}
}
```
### 2.3 刷新 Token
```http
POST /auth/refresh
Content-Type: application/json
{
"refresh_token": "eyJhbGciOiJIUzI1NiIs..."
}
```
### 2.4 登出
```http
POST /auth/logout
Authorization: Bearer <token>
```
---
## 3. 会计系统集成 (通用接口)
### 3.1 获取支持的会计系统列表
```http
GET /accounting/providers
Authorization: Bearer <token>
```
**响应:**
```json
{
"success": true,
"data": {
"providers": [
{
"id": "fortnox",
"name": "Fortnox",
"description": "Swedish accounting software",
"available": true,
"connected": true
},
{
"id": "visma",
"name": "Visma eAccounting",
"description": "Nordic accounting software",
"available": true,
"connected": false
},
{
"id": "hogia",
"name": "Hogia Smart",
"description": "Swedish accounting software",
"available": false,
"connected": false
}
]
}
}
```
### 3.2 获取授权 URL
```http
GET /accounting/{provider}/auth/url
Authorization: Bearer <token>
```
**参数:**
| 参数 | 类型 | 说明 |
|------|------|------|
| provider | string | 会计系统标识 (fortnox, visma, hogia) |
**响应:**
```json
{
"success": true,
"data": {
"provider": "fortnox",
"authorization_url": "https://apps.fortnox.se/oauth-v1/auth?client_id=xxx&redirect_uri=...&scope=...&state=...",
"state": "random_state_string"
}
}
```
### 3.3 OAuth 回调处理
```http
GET /accounting/{provider}/auth/callback?code=xxx&state=xxx
```
**响应:**
```json
{
"success": true,
"data": {
"provider": "fortnox",
"connected": true,
"company_name": "My Company AB",
"company_org_number": "556677-8899",
"connected_at": "2026-02-03T10:30:00Z"
}
}
```
### 3.4 获取用户的所有连接
```http
GET /accounting/connections
Authorization: Bearer <token>
```
**响应:**
```json
{
"success": true,
"data": {
"connections": [
{
"provider": "fortnox",
"connected": true,
"company_name": "My Company AB",
"company_org_number": "556677-8899",
"scopes": ["supplier", "voucher", "account"],
"expires_at": "2026-02-03T11:30:00Z",
"settings": {
"default_voucher_series": "A",
"default_account_code": 5460,
"auto_attach_pdf": true,
"auto_create_supplier": false
}
}
]
}
}
```
### 3.5 获取特定连接状态
```http
GET /accounting/connections/{provider}
Authorization: Bearer <token>
```
**响应:**
```json
{
"success": true,
"data": {
"provider": "fortnox",
"connected": true,
"company_name": "My Company AB",
"company_org_number": "556677-8899",
"scopes": ["supplier", "voucher", "account"],
"expires_at": "2026-02-03T11:30:00Z"
}
}
```
### 3.6 更新连接设置
```http
PATCH /accounting/connections/{provider}/settings
Authorization: Bearer <token>
Content-Type: application/json
{
"default_voucher_series": "A",
"default_account_code": 5460,
"auto_attach_pdf": true,
"auto_create_supplier": false
}
```
### 3.7 断开连接
```http
DELETE /accounting/connections/{provider}
Authorization: Bearer <token>
```
---
## 4. 发票处理
### 4.1 上传发票
```http
POST /invoices
Authorization: Bearer <token>
Content-Type: multipart/form-data
file: <binary>
provider: "fortnox" # 目标会计系统
auto_process: false # 是否自动处理
```
**响应 (预览模式):**
```json
{
"success": true,
"data": {
"id": "inv_uuid",
"status": "preview",
"provider": "fortnox",
"file": {
"name": "Invoice_2024_001.pdf",
"size": 1024567,
"url": "https://blob.azure/..."
},
"extraction": {
"supplier_name": "ABC Company",
"supplier_org_number": "556677-8899",
"invoice_number": "F2024-001",
"invoice_date": "2024-01-15",
"due_date": "2024-02-15",
"amount_total": 1250.00,
"amount_vat": 250.00,
"vat_rate": 25,
"ocr_number": "7350012345678",
"bankgiro": "123-4567",
"currency": "SEK",
"confidence": 0.95
},
"supplier_match": {
"action": "USE_EXISTING",
"supplier_number": "123",
"supplier_name": "ABC Company",
"confidence": 1.0
},
"voucher_preview": {
"series": "A",
"rows": [
{
"account": 5460,
"account_name": "Kontorsmaterial",
"debit": 1000.00,
"credit": 0,
"description": "ABC Company - F2024-001"
},
{
"account": 2610,
"account_name": "Ingående moms",
"debit": 250.00,
"credit": 0,
"description": "Moms 25%"
},
{
"account": 2440,
"account_name": "Leverantörsskulder",
"debit": 0,
"credit": 1250.00,
"description": "Faktura F2024-001",
"supplier_number": "123"
}
]
},
"created_at": "2026-02-03T10:30:00Z"
}
}
```
### 4.2 获取发票列表
```http
GET /invoices?page=1&limit=20&status=imported&provider=fortnox&sort=-created_at
Authorization: Bearer <token>
```
**查询参数:**
| 参数 | 类型 | 说明 |
|------|------|------|
| page | int | 页码,默认 1 |
| limit | int | 每页数量,默认 20最大 100 |
| status | string | 过滤状态 |
| provider | string | 过滤会计系统 |
| sort | string | 排序字段,`-` 前缀表示降序 |
**响应:**
```json
{
"success": true,
"data": {
"items": [
{
"id": "inv_uuid",
"status": "imported",
"provider": "fortnox",
"file_name": "Invoice_2024_001.pdf",
"supplier_name": "ABC Company",
"amount_total": 1250.00,
"invoice_date": "2024-01-15",
"voucher": {
"series": "A",
"number": "1234",
"url": "https://api.fortnox.se/3/vouchers/A/1234"
},
"created_at": "2026-02-03T10:30:00Z"
}
],
"pagination": {
"page": 1,
"limit": 20,
"total": 156,
"total_pages": 8
}
}
}
```
### 4.3 获取发票详情
```http
GET /invoices/{id}
Authorization: Bearer <token>
```
### 4.4 更新发票数据 (审核时)
```http
PATCH /invoices/{id}
Authorization: Bearer <token>
Content-Type: application/json
{
"extraction": {
"supplier_name": "Corrected Name",
"supplier_org_number": "556677-8899",
"amount_total": 1300.00,
"vat_rate": 25
},
"voucher_rows": [
{
"account": 6210,
"debit": 1040.00,
"credit": 0
},
{
"account": 2610,
"debit": 260.00,
"credit": 0
},
{
"account": 2440,
"debit": 0,
"credit": 1300.00
}
]
}
```
### 4.5 导入到会计系统
```http
POST /invoices/{id}/import
Authorization: Bearer <token>
Content-Type: application/json
{
"provider": "fortnox",
"create_supplier": false,
"supplier_data": {
"name": "New Supplier",
"organisation_number": "112233-4455"
}
}
```
**响应:**
```json
{
"success": true,
"data": {
"id": "inv_uuid",
"status": "imported",
"provider": "fortnox",
"voucher": {
"series": "A",
"number": "1234",
"url": "https://api.fortnox.se/3/vouchers/A/1234"
},
"supplier": {
"number": "123",
"name": "ABC Company"
},
"attachment": {
"id": "att_xxx",
"uploaded": true
},
"accounting_url": "https://apps.fortnox.se/...",
"imported_at": "2026-02-03T10:35:00Z"
}
}
```
### 4.6 删除发票
```http
DELETE /invoices/{id}
Authorization: Bearer <token>
```
仅允许删除未导入 (`pending`, `preview`, `failed`) 状态的发票。
---
## 5. 供应商管理 (通用接口)
### 5.1 获取供应商列表
```http
GET /accounting/{provider}/suppliers?search=ABC&page=1&limit=50
Authorization: Bearer <token>
```
**响应:**
```json
{
"success": true,
"data": {
"items": [
{
"supplier_number": "123",
"name": "ABC Company",
"organisation_number": "556677-8899",
"address": "Storgatan 1, 123 45 Stockholm",
"phone": "08-123 45 67",
"email": "info@abc.com",
"bankgiro": "123-4567",
"cached_at": "2026-02-03T09:00:00Z"
}
],
"pagination": {
"page": 1,
"limit": 50,
"total": 45
},
"from_cache": true
}
}
```
### 5.2 创建供应商
```http
POST /accounting/{provider}/suppliers
Authorization: Bearer <token>
Content-Type: application/json
{
"name": "New Supplier AB",
"organisation_number": "112233-4455",
"address1": "Testgatan 1",
"postcode": "123 45",
"city": "Stockholm",
"phone": "08-123 45 67",
"email": "info@supplier.com",
"bankgiro": "765-4321"
}
```
### 5.3 刷新供应商缓存
```http
POST /accounting/{provider}/suppliers/refresh-cache
Authorization: Bearer <token>
```
---
## 6. 会计科目 (通用接口)
### 6.1 获取科目列表
```http
GET /accounting/{provider}/accounts
Authorization: Bearer <token>
```
**响应:**
```json
{
"success": true,
"data": {
"accounts": [
{
"code": 2440,
"name": "Leverantörsskulder",
"type": "liability"
},
{
"code": 2610,
"name": "Ingående moms",
"type": "liability"
},
{
"code": 5460,
"name": "Kontorsmaterial",
"type": "expense"
}
]
}
}
```
### 6.2 获取科目映射规则
```http
GET /account-mappings?provider=fortnox
Authorization: Bearer <token>
```
### 6.3 创建科目映射规则
```http
POST /account-mappings
Authorization: Bearer <token>
Content-Type: application/json
{
"provider": "fortnox",
"supplier_org_number": "556677-8899",
"keyword": "kontor",
"account_code": 5460,
"vat_rate": 25,
"description_template": "{supplier_name} - Kontorsmaterial",
"priority": 5
}
```
---
## 7. Webhooks
### 7.1 通用 Webhook 接收
```http
POST /webhooks/{provider}
Headers:
X-Provider-Event: voucher.created
X-Provider-Signature: sha256=...
{
"event": "voucher.created",
"provider": "fortnox",
"timestamp": "2026-02-03T10:30:00Z",
"data": {
"voucher_number": "1234",
"series": "A",
"company_org_number": "556677-8899"
}
}
```
### 7.2 注册 Webhook (内部)
```http
POST /webhooks/register
Authorization: Bearer <admin_token>
Content-Type: application/json
{
"provider": "fortnox",
"url": "https://api.invoice-master.app/webhooks/fortnox",
"events": ["voucher.created", "voucher.updated"]
}
```
---
## 8. 健康检查
### 8.1 基础健康检查
```http
GET /health
```
**响应:**
```json
{
"status": "healthy",
"timestamp": "2026-02-03T10:30:00Z",
"version": "2.0.0"
}
```
### 8.2 详细健康检查
```http
GET /health/detailed
Authorization: Bearer <admin_token>
```
**响应:**
```json
{
"status": "healthy",
"timestamp": "2026-02-03T10:30:00Z",
"checks": {
"database": { "status": "healthy", "latency_ms": 5 },
"redis": { "status": "healthy", "latency_ms": 2 },
"providers": {
"fortnox": { "status": "healthy", "latency_ms": 150 },
"visma": { "status": "not_configured" }
},
"ocr_api": { "status": "healthy", "latency_ms": 50 },
"blob_storage": { "status": "healthy", "latency_ms": 30 }
}
}
```
---
## 9. 错误代码表
| 错误代码 | HTTP 状态 | 说明 |
|----------|-----------|------|
| `UNAUTHORIZED` | 401 | Token 无效或过期 |
| `FORBIDDEN` | 403 | 无权限访问 |
| `NOT_FOUND` | 404 | 资源不存在 |
| `VALIDATION_ERROR` | 400 | 请求参数验证失败 |
| `PROVIDER_NOT_SUPPORTED` | 400 | 不支持的会计系统 |
| `PROVIDER_NOT_CONNECTED` | 422 | 用户未连接该会计系统 |
| `PROVIDER_TOKEN_EXPIRED` | 401 | 会计系统 Token 过期 |
| `PROVIDER_RATE_LIMITED` | 429 | 会计系统 API 限流 |
| `OCR_FAILED` | 422 | OCR 提取失败 |
| `INVOICE_ALREADY_IMPORTED` | 409 | 发票已导入 |
| `INVALID_FILE_TYPE` | 400 | 不支持的文件类型 |
| `FILE_TOO_LARGE` | 400 | 文件超过大小限制 |
| `SUPPLIER_NOT_FOUND` | 404 | 供应商不存在 |
| `VOUCHER_CREATE_FAILED` | 422 | 凭证创建失败 |
---
## 10. 速率限制
| 端点 | 限制 |
|------|------|
| `/auth/*` | 10 req/min |
| `/invoices` (POST) | 10 req/min |
| `/invoices/*` | 100 req/min |
| `/accounting/{provider}/*` | 30 req/min |
| 其他 | 100 req/min |
限速响应头:
```http
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1643875200
```
---
## 11. OpenAPI 规范
完整的 OpenAPI 3.0 规范可在 `/docs` 端点查看 (Swagger UI)。
```http
GET /docs # Swagger UI
GET /openapi.json # OpenAPI JSON
```
---
## 12. API 变更日志
### v3.0 (2026-02-03)
**技术栈变更:**
- Python/FastAPI → .NET 8 + ASP.NET Core
- 新增 CQRS 模式 (MediatR)
- 新增领域事件 (审计支持)
- 新增审计 API 端点
**新增:**
- `/invoices/{id}/audit-log` - 获取发票审计日志
- `/users/me/audit-log` - 获取用户操作历史
- `/admin/audit-export` - 导出审计报告
### v2.0 (2026-02-03)
**新增:**
- 多会计系统支持
- `/accounting/providers` - 获取支持的会计系统列表
- `/accounting/{provider}/auth/url` - 通用授权 URL 接口
- `/accounting/{provider}/auth/callback` - 通用 OAuth 回调
- `/accounting/connections` - 获取所有连接
- `/accounting/connections/{provider}` - 特定连接管理
**变更:**
- `/fortnox/*` 接口迁移到 `/accounting/{provider}/*`
- `/suppliers` 迁移到 `/accounting/{provider}/suppliers`
- `/accounts` 迁移到 `/accounting/{provider}/accounts`
- 发票上传增加 `provider` 参数
- 发票导入增加 `provider` 参数
**废弃:**
- `/fortnox/*` (旧接口,将在 v3.0 移除)
---
**文档历史:**
| 版本 | 日期 | 作者 | 变更 |
|------|------|------|------|
| 3.0 | 2026-02-03 | Claude Code | 重构为 .NET + CQRS + 审计支持 |
| 2.0 | 2026-02-03 | Claude Code | 添加多会计系统支持 |
| 1.0 | 2026-02-03 | Claude Code | 初始版本 |