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

230 lines
6.2 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# SignUpImport - 新增「重複檢查」功能
## 功能概述
匯入參加人員時,若「姓名+手機號」的組合在同一活動中已經存在,則自動跳過該記錄,避免重複匯入。
## 實現細節
### 1. 後端修改 (SignUpService.asmx.cs)
#### ConfirmImport 方法增強
```csharp
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); // 加入集合,避免批次內重複
}
}
```
#### 複合鍵生成
```csharp
private string CreateCompositeKey(string name, string mobile)
{
return (name ?? "").Trim() + "|" + (mobile ?? "").Trim();
}
```
### 2. 前端修改 (SignUpImport.aspx)
#### 匯入結果顯示
```html
<!-- 顯示成功、跳過、失敗的統計 -->
<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)
#### 跳過記錄樣式
```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
```javascript
{
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. 同時檢查「現有記錄」和「同批次內記錄」,避免同批次重複
## 測試清單
- [ ] 匯入無重複資料 → 全部成功
- [ ] 匯入有部分重複資料 → 成功 + 跳過
- [ ] 匯入全部重複資料 → 全部跳過
- [ ] 同批次內有重複 → 跳過後續重複
- [ ] 驗證錯誤 + 重複混合 → 正確統計每種情況
- [ ] 驗證跳過記錄列表正確顯示
- [ ] 驗證成功/跳過/失敗的計數準確