Files
thinkyu/報到系統/SignUpImport_DuplicateCheck_Design.md
sryang 577060bc78 chore: 首次簽入 Thinkyu ASP.NET 專案
- 加入 Visual Studio / ASP.NET .gitignore
- 排除建置輸出、IDE 設定、NuGet packages、大型 MSI 安裝檔
2026-09-10 09:42:37 +08:00

6.2 KiB
Raw Permalink Blame History

SignUpImport - 新增「重複檢查」功能

功能概述

匯入參加人員時,若「姓名+手機號」的組合在同一活動中已經存在,則自動跳過該記錄,避免重複匯入。

實現細節

1. 後端修改 (SignUpService.asmx.cs)

ConfirmImport 方法增強

public BaseResponse ConfirmImport(int courseID, List<SignUpItem> items)
{
    // 1. 查詢現有參加人員(同一活動)
    SignUpList existingSignups = dac.Select(
        noInputReturnAll: true,
        CourseID: courseID,
        ...
    );

    // 2. 建立重複檢查集合
    HashSet<string> existingCombinations = new HashSet<string>();
    foreach (var signup in existingSignups)
    {
        string key = CreateCompositeKey(signup.Name, signup.Mobile);
        existingCombinations.Add(key);
    }

    // 3. 逐筆檢查並匯入
    foreach (var item in items)
    {
        string compositeKey = CreateCompositeKey(item.Name, item.Mobile);
        
        if (existingCombinations.Contains(compositeKey))
        {
            // 跳過重複記錄
            skippedCount++;
            skippedRecords.Add($"{item.Name} ({item.Mobile})");
            continue;
        }
        
        // 插入新記錄
        dac.InsertOne(item);
        existingCombinations.Add(compositeKey); // 加入集合,避免批次內重複
    }
}

複合鍵生成

private string CreateCompositeKey(string name, string mobile)
{
    return (name ?? "").Trim() + "|" + (mobile ?? "").Trim();
}

2. 前端修改 (SignUpImport.aspx)

匯入結果顯示

<!-- 顯示成功、跳過、失敗的統計 -->
<span>成功:{{ importResult.details.SuccessCount }} 筆</span>
<span v-if="importResult.details.SkippedCount > 0">跳過:{{ importResult.details.SkippedCount }} 筆</span>
<span v-if="importResult.details.ErrorCount > 0">失敗:{{ importResult.details.ErrorCount }} 筆</span>

<!-- 跳過記錄詳情 -->
<div class="skipped-records" v-if="importResult.details.SkippedRecords && importResult.details.SkippedRecords.length > 0">
    <div class="skipped-title">❌ 跳過的重複記錄:</div>
    <div class="skipped-list">
        <div class="skipped-item" v-for="record in importResult.details.SkippedRecords.slice(0, 10)">
            {{ record }}
        </div>
        <div v-if="importResult.details.SkippedRecords.length > 10">
            ... 還有 {{ importResult.details.SkippedRecords.length - 10 }} 筆
        </div>
    </div>
</div>

3. 樣式增強 (SignUpImport.css)

跳過記錄樣式

.skipped-records {
    margin-top: var(--spacing-md);
    padding-top: var(--spacing-md);
    border-top: 1px solid #ddd;
}

.skipped-title {
    color: #f57c00;
    font-weight: 600;
    margin-bottom: 8px;
    font-size: 14px;
}

.skipped-item {
    color: #f57c00;
    font-size: 13px;
    margin-bottom: 4px;
    padding-left: 20px;
    position: relative;
}

.skipped-item::before {
    content: '⊘ ';
    position: absolute;
    left: 0;
}

工作流程

上傳 Excel 檔案
    ↓
後端驗證(FileUploadHandler.ashx
    ├─ 檢查欄位格式
    ├─ 驗證必填欄位
    └─ 返回預覽 (✓/✗ 狀態)
    ↓
前端顯示預覽(可編輯)
    ├─ 顯示有效列(✓)
    ├─ 顯示錯誤列(✗)和錯誤訊息
    └─ 統計有效筆數
    ↓
使用者確認匯入
    ↓
後端執行匯入(ConfirmImport)
    ├─ 查詢現有參加人員 (同一活動)
    ├─ 建立重複檢查集合 (Name|Mobile)
    ├─ 逐筆檢查:
    │  ├─ 若重複 → 跳過並記錄
    │  └─ 若不重複 → 插入並加入集合
    └─ 返回結果統計
    ↓
前端顯示結果
    ├─ 成功:X 筆
    ├─ 跳過:Y 筆(列出跳過的記錄)
    └─ 失敗:Z 筆(列出失敗原因)

回傳資料結構

ConfirmImport Response

{
    Success: true,
    Message: "成功匯入 10 筆資料,跳過 3 筆重複資料,失敗 0 筆",
    Data: {
        SuccessCount: 10,        // 成功新增筆數
        SkippedCount: 3,         // 跳過(重複)筆數
        ErrorCount: 0,           // 失敗筆數
        Errors: [],              // 失敗詳情
        SkippedRecords: [        // 跳過記錄詳情
            "王小明 (0912345678)",
            "李美麗 (0923456789)",
            "張大衛 (0934567890)"
        ]
    }
}

重複檢查邏輯

檢查範圍

  • 範圍:同一活動內
  • 檢查欄位:姓名 (Name) + 手機號 (Mobile)
  • 大小寫敏感:否(轉換為小寫比較)
  • 空白敏感:否(.Trim() 處理)

複合鍵格式

key = "王小明|0912345678"

重複判定規則

if (Name.Trim() == "王小明" AND Mobile.Trim() == "0912345678") 
{
    // 認為重複,跳過
}

使用者體驗

成功案例

成功匯入 20 筆資料,跳過 5 筆重複資料

跳過的重複記錄:
⊘ 王小明 (0912345678)
⊘ 李美麗 (0923456789)
⊘ 張大衛 (0934567890)
... 還有 2 筆

視覺反饋

  • 成功 → 綠色背景 (✓)
  • 跳過 → 橙色文字 (⊘)
  • 失敗 → 紅色文字 (✗)

相關檔案修改

檔案 修改內容
Services/SignUpService.asmx.cs ConfirmImport 方法新增重複檢查邏輯 + CreateCompositeKey 方法
Forms/SignUpImport.aspx 匯入結果顯示新增跳過統計和跳過記錄列表
Content/SignUpImport.css 新增 .skipped-records 和相關樣式
Scripts/SignUpImport.js 無修改(後端自動處理)

性能考量

算法複雜度

  • 時間複雜度:O(n) 其中 n = 現有記錄數 + 匯入記錄數
  • 空間複雜度O(n) 用於 HashSet

優化點

  1. 使用 HashSet<T> 而非 ListO(1) 查詢而非 O(n)
  2. 同時檢查「現有記錄」和「同批次內記錄」,避免同批次重複

測試清單

  • 匯入無重複資料 → 全部成功
  • 匯入有部分重複資料 → 成功 + 跳過
  • 匯入全部重複資料 → 全部跳過
  • 同批次內有重複 → 跳過後續重複
  • 驗證錯誤 + 重複混合 → 正確統計每種情況
  • 驗證跳過記錄列表正確顯示
  • 驗證成功/跳過/失敗的計數準確