Files
njts-accounting-core/MIGRATION_SUMMARY.md
2026-02-20 15:47:27 +09:00

9.1 KiB
Raw Blame History

【新版本追踪系統 - 完整變更摘要】

實施日期: 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

新增索引

-- 用於快速查詢最新交易(試算表經常使用)
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:

# 創建新修正版本(自動標記舊版本過時)
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 路由變更:

# 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 查詢優化

試算表查詢

舊方式(複雜,低效):

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 子查詢
  • 性能:需要掃描所有記錄查找修正
  • 複雜:邏輯散布在多個條件中

新方式(簡潔,高效):

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. 前端調用

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. 後端處理

# 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. 前端收到回應

{
  "status": "ok",
  "journal_entry_id": 456,
  "revision_created": true
}

4. 試算表查詢

# 舊行為:只看交易 123找不到因為已被標記為過時
# 新行為:只看交易 456因為 is_latest = true

SELECT * FROM journal_entries
WHERE is_deleted = false AND is_latest = true
# 結果:包括交易 456不包括交易 123

5. 修正歷史查詢

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 的交易
  • 修正歷史端點返回完整版本列表

性能驗收

  • 試算表查詢響應時間 < 500ms50000+ 記錄)
  • 索引命中率 > 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. 通知相關團隊

📞 支持與聯系

如有疑問或遇到問題,請參考:

所有文件已準備就緒,可隨時部署。