370 lines
9.1 KiB
Markdown
370 lines
9.1 KiB
Markdown
# 【新版本追踪系統 - 完整變更摘要】
|
||
|
||
**實施日期**: 2025 年初
|
||
**系統**: 日記條目修正追踪
|
||
**狀態**: 準備就緒,等待部署
|
||
|
||
---
|
||
|
||
## 📊 高层视图
|
||
|
||
這次更新實現了一個完整的修正版本追踪系統,用來替代舊的複雜逆仕訳邏輯。
|
||
|
||
### Before(舊系統)
|
||
|
||
```
|
||
原始交易 (ID: 123) ──→ 逆仕訳 (ID: 456) ──→ 修正交易 (ID: 789)
|
||
[複雜 parent_entry_id 追踪]
|
||
```
|
||
|
||
### After(新系統)
|
||
|
||
```
|
||
原始交易 (ID: 123, revision_count: 1, is_latest: false, original_entry_id: NULL)
|
||
↓
|
||
修正版本 (ID: 234, revision_count: 2, is_latest: false, original_entry_id: 123)
|
||
↓
|
||
最終版本 (ID: 345, revision_count: 3, is_latest: true, original_entry_id: 123)
|
||
```
|
||
|
||
---
|
||
|
||
## 🔧 技術變更詳情
|
||
|
||
### 1. 数据库表结构 (`journal_entries`)
|
||
|
||
#### 新增三個欄位
|
||
|
||
| 欄位名 | 類型 | 預設值 | 用途 |
|
||
| ------------------- | ------- | ------ | --------------------------- |
|
||
| `is_latest` | BOOLEAN | true | 標記是否為最新版本 |
|
||
| `revision_count` | INTEGER | 1 | 版本編號(1=原始,2+=修正) |
|
||
| `original_entry_id` | INTEGER | NULL | 指向原始交易的ID |
|
||
|
||
#### 新增索引
|
||
|
||
```sql
|
||
-- 用於快速查詢最新交易(試算表經常使用)
|
||
CREATE INDEX idx_journal_entries_is_latest ON journal_entries(is_latest, entry_date DESC);
|
||
|
||
-- 用於快速查詢修正歷史
|
||
CREATE INDEX idx_journal_entries_original_id ON journal_entries(original_entry_id);
|
||
```
|
||
|
||
---
|
||
|
||
### 2. 後端代碼變更
|
||
|
||
#### 文件結構
|
||
|
||
```
|
||
backend/app/
|
||
├── db_auto_migration.py [新增] 自動遷移腳本
|
||
├── core/
|
||
│ └── revision_manager.py [新增] 版本管理模塊
|
||
├── routers/
|
||
│ └── journal_entries.py [修改] 簡化為使用新系統
|
||
└── main.py [修改] 添加啟動事件運行遷移
|
||
```
|
||
|
||
#### 核心函數
|
||
|
||
**revision_manager.py:**
|
||
|
||
```python
|
||
# 創建新修正版本(自動標記舊版本過時)
|
||
create_new_revision(
|
||
conn,
|
||
entry_date,
|
||
description,
|
||
fiscal_year,
|
||
lines,
|
||
created_by="system",
|
||
original_entry_id=None
|
||
) → int # 返回新交易的 ID
|
||
|
||
# 獲取修正歷史
|
||
get_revision_history(conn, original_entry_id) → list
|
||
|
||
# 獲取當前版本
|
||
get_current_version(conn, journal_entry_id) → dict
|
||
```
|
||
|
||
**journal_entries.py 路由變更:**
|
||
|
||
```python
|
||
# POST /journal-entries 簡化
|
||
@router.post("")
|
||
def create_journal_entry(req: JournalEntryRequest):
|
||
# 使用 revision_manager.create_new_revision()
|
||
# 自動處理版本遞增和標記
|
||
|
||
# GET /journal-entries 優化
|
||
@router.get("")
|
||
def get_journal_entries(...):
|
||
# 新方式: WHERE is_latest = true
|
||
# 舊方式: 複雜的 parent_entry_id 檢查和 NOT IN 子查詢
|
||
|
||
# 新增修正歷史端點
|
||
@router.get("/{journal_id}/revision-history")
|
||
def get_revision_history(journal_id: int):
|
||
# 返回指定交易的所有版本
|
||
```
|
||
|
||
---
|
||
|
||
### 3. SQL 查詢優化
|
||
|
||
#### 試算表查詢
|
||
|
||
**舊方式(複雜,低效):**
|
||
|
||
```sql
|
||
SELECT je.* FROM journal_entries je
|
||
LEFT JOIN journal_entries child ON child.parent_entry_id = je.journal_entry_id
|
||
WHERE je.is_deleted = false
|
||
AND child.journal_entry_id IS NULL -- 排除已被修正的交易
|
||
AND je.description NOT LIKE '%[逆仕訳]%' -- 排除逆仕訳
|
||
```
|
||
|
||
- ❌ 消耗資源:LEFT JOIN + NOT IN 子查詢
|
||
- ❌ 性能:需要掃描所有記錄查找修正
|
||
- ❌ 複雜:邏輯散布在多個條件中
|
||
|
||
**新方式(簡潔,高效):**
|
||
|
||
```sql
|
||
SELECT je.* FROM journal_entries je
|
||
WHERE je.is_deleted = false
|
||
AND je.is_latest = true
|
||
```
|
||
|
||
- ✅ 高效:直接使用索引 `idx_journal_entries_is_latest`
|
||
- ✅ 簡潔:單一條件
|
||
- ✅ 明確:語意清晰
|
||
|
||
**性能提升預估:**
|
||
|
||
- 查詢時間: -60% ~ -80%
|
||
- 資源消耗: -70%
|
||
- 索引使用效率: +100%(直接索引掃描)
|
||
|
||
---
|
||
|
||
## 🔄 工作流程 - 修正一筆交易
|
||
|
||
### 場景:需要修正交易 ID 123
|
||
|
||
#### 1. 前端調用
|
||
|
||
```javascript
|
||
POST /journal-entries
|
||
{
|
||
entry_date: "2025-01-15",
|
||
description: "【修正】銷售收入",
|
||
lines: [
|
||
{ account_id: 101, debit: 10000, credit: 0 },
|
||
{ account_id: 202, debit: 0, credit: 10000 }
|
||
],
|
||
original_entry_id: 123 // ← 新參數
|
||
}
|
||
```
|
||
|
||
#### 2. 後端處理
|
||
|
||
```python
|
||
# POST /journal-entries 端點
|
||
entry_id = create_new_revision(
|
||
conn=conn,
|
||
entry_date=date(2025, 1, 15),
|
||
description="【修正】銷售收入",
|
||
fiscal_year=2025,
|
||
lines=[...],
|
||
original_entry_id=123
|
||
)
|
||
|
||
# create_new_revision() 內部操作:
|
||
# 1. 查詢交易 123 的當前 revision_count(假設為 1)
|
||
# 2. 標記交易 123: is_latest = false
|
||
# 3. 創建新交易:
|
||
# - journal_entry_id = 456
|
||
# - revision_count = 2
|
||
# - is_latest = true
|
||
# - original_entry_id = 123
|
||
# 4. 返回 456
|
||
```
|
||
|
||
#### 3. 前端收到回應
|
||
|
||
```json
|
||
{
|
||
"status": "ok",
|
||
"journal_entry_id": 456,
|
||
"revision_created": true
|
||
}
|
||
```
|
||
|
||
#### 4. 試算表查詢
|
||
|
||
```python
|
||
# 舊行為:只看交易 123(找不到因為已被標記為過時)
|
||
# 新行為:只看交易 456(因為 is_latest = true)
|
||
|
||
SELECT * FROM journal_entries
|
||
WHERE is_deleted = false AND is_latest = true
|
||
# 結果:包括交易 456,不包括交易 123
|
||
```
|
||
|
||
#### 5. 修正歷史查詢
|
||
|
||
```python
|
||
GET /journal-entries/456/revision-history
|
||
# 結果:
|
||
# [
|
||
# {journal_entry_id: 123, revision_count: 1, is_latest: false, ...},
|
||
# {journal_entry_id: 456, revision_count: 2, is_latest: true, ...}
|
||
# ]
|
||
```
|
||
|
||
---
|
||
|
||
## 📁 文件清單
|
||
|
||
### 新增文件
|
||
|
||
| 文件 | 用途 | 大小 |
|
||
| ---------------------------------------------------- | ---------------- | ------ |
|
||
| `backend/app/db_auto_migration.py` | 自動遷移腳本 | ~2.5KB |
|
||
| `backend/app/core/revision_manager.py` | 版本管理核心模塊 | ~4KB |
|
||
| `backend/sql/migration_revision_tracking_manual.sql` | 手動 SQL 遷移 | ~2KB |
|
||
| `test_environment.py` | 環境驗證工具 | ~3KB |
|
||
| `IMPLEMENTATION_GUIDE.md` | 實施指南 | ~6KB |
|
||
|
||
### 修改文件
|
||
|
||
| 文件 | 變更內容 | 影響範圍 |
|
||
| ---------------------------------------- | ------------------------ | ---------------- |
|
||
| `backend/app/main.py` | 添加啟動事件運行遷移 | 應用初始化 |
|
||
| `backend/app/routers/journal_entries.py` | 簡化後端邏輯,整合新系統 | 所有日記條目操作 |
|
||
|
||
### 未修改文件
|
||
|
||
(其他所有文件保持不變)
|
||
|
||
---
|
||
|
||
## ✅ 遷移驗收清單
|
||
|
||
### 前置條件
|
||
|
||
- [ ] 已備份生產數據庫
|
||
- [ ] 已準備回滾計劃
|
||
- [ ] 已通知相關用户
|
||
|
||
### 技術驗收
|
||
|
||
- [ ] 自動遷移腳本無語法錯誤
|
||
- [ ] 新字段已添加到 `journal_entries` 表
|
||
- [ ] 索引已創建並可用
|
||
- [ ] 後端代碼編譯無誤
|
||
|
||
### 功能驗收
|
||
|
||
- [ ] 應用啟動時自動運行遷移
|
||
- [ ] 可成功創建新交易
|
||
- [ ] 可成功創建修正版本(original_entry_id 有效)
|
||
- [ ] 試算表查詢僅顯示 `is_latest = true` 的交易
|
||
- [ ] 修正歷史端點返回完整版本列表
|
||
|
||
### 性能驗收
|
||
|
||
- [ ] 試算表查詢響應時間 < 500ms(50000+ 記錄)
|
||
- [ ] 索引命中率 > 95%
|
||
- [ ] 無慢查詢告警
|
||
|
||
### 用户驗收
|
||
|
||
- [ ] 前端修正流程正常工作
|
||
- [ ] 已修正的交易不再出現在主列表中
|
||
- [ ] 用户可查看修正歷史記錄
|
||
|
||
---
|
||
|
||
## 🎯 已解決的問題
|
||
|
||
### ❌ 舊系統問題
|
||
|
||
1. **複雜的逆仕訳邏輯**
|
||
- 需要生成對沖交易
|
||
- 難以追踪修正歷史
|
||
- 用户容易混淆
|
||
|
||
2. **低效的試算表查詢**
|
||
- 使用 LEFT JOIN + NOT IN 子查詢
|
||
- 需要掃描大量記錄
|
||
- 性能隨着數據增長而惡化
|
||
|
||
3. **修正版本管理困難**
|
||
- 沒有清晰的版本編號
|
||
- 無法區分原始交易與修正版本
|
||
- 修正歷史散亂,難以查詢
|
||
|
||
### ✅ 新系統優勢
|
||
|
||
1. **清晰的版本追踪**
|
||
- 每個版本都有唯一的 revision_count
|
||
- is_latest 標誌明確標記當前版本
|
||
- original_entry_id 清楚指向原始交易
|
||
|
||
2. **高效的查詢性能**
|
||
- 直接使用索引查詢
|
||
- 簡單明需的 SQL 條件
|
||
- 性能與數據量無關
|
||
|
||
3. **更好的用户體驗**
|
||
- 修正流程更直觀
|
||
- 可查看完整的修正歷史
|
||
- 不需要理解複雜的逆仕訳概念
|
||
|
||
---
|
||
|
||
## 🚀 部署計劃
|
||
|
||
### 第 1 階段:準備(立即)
|
||
|
||
```
|
||
1. 備份生產數據庫
|
||
2. 在測試環境驗證遷移
|
||
3. 准備回滾計劃
|
||
```
|
||
|
||
### 第 2 階段:部署(確認後)
|
||
|
||
```
|
||
1. 更新應用代碼
|
||
2. 重啟應用(自動運行遷移)
|
||
3. 驗證遷移成功
|
||
4. 運行集成測試
|
||
```
|
||
|
||
### 第 3 階段:驗證(部署後)
|
||
|
||
```
|
||
1. 檢查應用日誌
|
||
2. 測試核心功能
|
||
3. 驗證性能指標
|
||
4. 通知相關團隊
|
||
```
|
||
|
||
---
|
||
|
||
## 📞 支持與聯系
|
||
|
||
如有疑問或遇到問題,請參考:
|
||
|
||
- 📖 [實施指南](IMPLEMENTATION_GUIDE.md)
|
||
- 📋 [手動 SQL 遷移](backend/sql/migration_revision_tracking_manual.sql)
|
||
- 🧪 [環境驗證工具](test_environment.py)
|
||
|
||
**所有文件已準備就緒,可隨時部署。**
|