chore: 首次簽入 Thinkyu ASP.NET 專案

- 加入 Visual Studio / ASP.NET .gitignore
- 排除建置輸出、IDE 設定、NuGet packages、大型 MSI 安裝檔
This commit is contained in:
2026-09-10 09:42:37 +08:00
commit 577060bc78
2496 changed files with 501389 additions and 0 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

+7
View File
@@ -0,0 +1,7 @@
三竹官網:https://sms.mitake.com.tw/
帳號:27968860
密碼:TY27968860
MMS APIhttps://message.mitake.com.tw
clientID235vx4hnn2ga248cq12w
clientSecrethINE8vzjsBLKSNNthzdRiS5G
@@ -0,0 +1,309 @@
# 多簡訊平台架構設計
> 建立日期:2026-06-02
> 目的:支援 EVERY8D 與三竹(MITAKE)兩個簡訊發送平台
---
## 需求說明
- 加入三竹(MITAKE)簡訊發送平台
- 整個系統統一切換發送平台(由系統設定決定)
- 存放帳號參數的 `ProjectMMSAccount` 資料表加入平台代碼欄位
- 新增 `MMSPlatform` 資料表,存放平台代碼、主帳號密碼等資料(API 端點硬寫於各 Provider 靜態變數)
-`BWEX` 資料表加入設定 `CurrentMMSPlatform`,記錄目前使用哪一個簡訊平台
- 簡訊帳號設定畫面需支援:
- 選擇平台
- 顯示目前的預設平台
- 設定各簡訊平台的主帳號密碼
- 管理各計畫的分帳號密碼
---
## 架構概覽
```
SMSSendingService.SendMessage()
└─ switch (message.PlatformCode)
├─ "EVERY8D" → EVERY8DSMSProvider.SendMMS(...)
└─ "MITAKE" → MITAKESMSProvider.SendMMS(...)
```
**設計原則:兩個 Provider 均為純靜態類別,不使用介面或工廠,直接 switch 呼叫。**
平台代碼優先順序:
1. `MessageQueueItem.PlatformCode`(由計畫的 `ProjectMMSAccount.PlatformCode` 填入)
2. 若計畫無設定,使用 `PublicVariable.CurrentMMSPlatform`(由 `BWEX.CurrentMMSPlatform` 載入)
---
## 資料庫異動
### 新增資料表 `MMSPlatform`(簡訊平台設定)
| No | Field Name | Data Type | Length | Not Null | PK | Identity | Description |
| --- | ------------ | --------- | ------ | -------- | --- | -------- | ---------------------- |
| 1 | ID | INT | | V | V | V | 流水號 |
| 2 | PlatformCode | NVARCHAR | 20 | V | | | 平台代碼(EVERY8D / MITAKE |
| 3 | PlatformName | NVARCHAR | 50 | V | | | 平台名稱(EVERY8D / 三竹) |
| 4 | Account | NVARCHAR | 100 | | | | 主帳號 |
| 5 | Password | NVARCHAR | 100 | | | | 主密碼 |
| 6 | Remark | NVARCHAR | 200 | | | | 備註 |
| 7 | DB_APPNO | INT | | V | | | 樂觀鎖定版本號 |
| 8 | DB_CRDAT | DATETIME | | V | | | 建立日期 |
| 9 | DB_CRUSR | NVARCHAR | 20 | V | | | 建立人員 |
| 10 | DB_TRDAT | DATETIME | | V | | | 修改日期 |
| 11 | DB_TRUSR | NVARCHAR | 20 | V | | | 修改人員 |
> API 端點網址硬寫於各 Provider 靜態變數,不存入資料庫。
預設插入兩筆:
| PlatformCode | PlatformName |
| ------------ | ------------ |
| EVERY8D | EVERY8D |
| MITAKE | 三竹 |
### 修改資料表 `ProjectMMSAccount`
新增欄位:
| No | Field Name | Data Type | Length | Not Null | Description |
| --- | ------------ | --------- | ------ | -------- | ----------- |
| 5 | PlatformCode | NVARCHAR | 20 | V | 平台代碼 |
### 修改資料表 `BWEX`(系統設定)
新增一筆設定資料:
| KeyName | KeyValue | Description |
| ------------------ | -------- | ----------- |
| CurrentMMSPlatform | EVERY8D | 目前使用的簡訊平台代碼 |
---
## 新增檔案
### `Services/MITAKESMSProvider.cs`(三竹靜態類別)
- 純靜態類別(`public static class MITAKESMSProvider`
- API 端點以靜態變數定義:
```csharp
private const string ApiBaseUrl = "https://message.mitake.com.tw";
```
- 依 `message.MMSAccount` / `message.MMSPassword` 呼叫三竹 API
- 三竹 API 端點格式:
`{ApiBaseUrl}/SmSendGet.asp?username={帳號}&password={密碼}&dstaddr={手機}&smbody={內容}`
- 回傳格式為純文字:`[1]` 開頭表示成功,`statuscode=` 欄位為狀態碼
- 三竹狀態碼說明:
| statuscode | 說明 |
| ---------- | ------ |
| 0 | 已送達手機 |
| 1 | 已送達三竹 |
| 2 | 已送達業者 |
| 1000 | 預約傳送中 |
| 1001 | 預約已取消 |
| 2000 | 內容有錯誤 |
| 2001 | 門號有錯誤 |
| 2002 | 訊息已停用 |
| 2003 | 逾時無法送達 |
| 2005 | 內容有關鍵字 |
| 2999 | 業者系統錯誤 |
| 9998 | 傳送錯誤 |
| 9999 | 三竹系統錯誤 |
- `CheckBalance`:呼叫 `{ApiBaseUrl}/SmQueryGet.asp?username={帳號}&password={密碼}&CharsetURL=UTF-8`
### `Models/MMSPlatformItem.cs`(資料紀錄類別)
對應 `MMSPlatform` 資料表的單筆資料,屬性包含:
`ID`、`PlatformCode`、`PlatformName`、`Account`、`Password`、`Remark`,以及標準 DB 欄位。
### `Models/MMSPlatformList.cs`(資料集合類別)
`List<MMSPlatformItem>` 的集合類別。
### `DAC/DAC_MMSPlatform.cs`(資料存取類別)
- `MMSPlatformList SelectAll()` — 查詢所有平台
- `MMSPlatformItem SelectByCode(string platformCode)` — 依代碼查一筆
- `MMSPlatformItem InsertOne(MMSPlatformItem item)` — 新增
- `MMSPlatformItem UpdateOne(MMSPlatformItem item)` — 修改(含 `DB_APPNO` 樂觀鎖定)
---
## 修改現有檔案
### `Services/EVERY8DSMSProvider.cs`
- 維持純靜態類別,不需異動介面或繼承關係
- `SendMMS`、`CheckBalance` 保持 static 方法不變
- API 端點以靜態常數定義(已存在或補充確認):
```csharp
private const string ApiBaseUrl = "https://new.e8d.tw/API21/HTTP";
```
### `Models/ProjectMMSAccountItem.cs`(或所在類別檔)
新增屬性:
```csharp
public string PlatformCode { get; set; }
```
### `Models/MessageQueueItem.cs`(或所在類別檔)
新增屬性:
```csharp
public string PlatformCode { get; set; }
```
### `DAC/DAC_ProjectMMSAccount.cs`
- `SelectPage`、`Select`、`InsertOne`、`UpdateOne` 均加入 `PlatformCode` 欄位處理
### `Common/PublicVariable.cs`
新增靜態變數:
```csharp
/// <summary>
/// 目前使用的簡訊平台代碼(由 BWEX.CurrentMMSPlatform 載入)
/// </summary>
public static string CurrentMMSPlatform = "EVERY8D";
```
### `Global.asax.cs`
`Application_Start` 新增從 DB 讀取 `BWEX` 的 `CurrentMMSPlatform`,設定至 `PublicVariable.CurrentMMSPlatform`。
EVERY8D 相關的 AppSettings 讀取保留作為向後相容 fallback。
### `Services/SMSSendingService.cs`
- `Start()` 的帳號填入迴圈,同時填入 `message.PlatformCode`
- 計畫有設定 `PlatformCode` → 使用計畫的 `PlatformCode`
- 否則使用 `PublicVariable.CurrentMMSPlatform`
- `SendMessage()` 改用 `switch (message.PlatformCode)` 直接呼叫對應靜態 Provider,移除 `SendSMSToEVERY8D()` 方法:
```csharp
switch (message.PlatformCode)
{
case "MITAKE":
result = MITAKESMSProvider.SendMMS(message, out failMessage);
break;
default: // EVERY8D
result = EVERY8DSMSProvider.SendMMS(message, out failMessage);
break;
}
```
### `Services/MessageAccountManage.asmx.cs`
新增 WebMethod
| Method | 說明 |
| ----------------------------------------- | ------------------------------------------------- |
| `GetPlatforms()` | 回傳所有平台清單(BaseResponse, Data: MMSPlatformList |
| `GetCurrentPlatform()` | 回傳目前系統預設平台代碼(BaseResponse, Data: MMSPlatformItem |
| `SavePlatform(MMSPlatformItem item)` | 儲存平台設定(帳號 / 密碼) |
| `SetCurrentPlatform(string platformCode)` | 更新 BWEX 的 CurrentMMSPlatform,同時更新 PublicVariable |
修改 `Save()`:加入 `PlatformCode` 欄位的驗證與儲存。
### `Forms/MessageAccountManage.aspx`
畫面新增區塊:
1. **目前預設平台區塊**(搜尋面板上方):
- 顯示目前使用的平台名稱與代碼
- 提供「切換平台」按鈕開啟平台切換彈窗
2. **平台設定區塊**(可展開/收合):
- 列出所有平台(EVERY8D、三竹)
- 每平台顯示:主帳號、主密碼、操作按鈕(儲存)(API 端點固定於程式碼,不顯示於畫面)
3. **列表欄位**
- 新增「平台」欄(顯示 `PlatformCode`NULL 時顯示「系統預設」)
4. **新增/編輯彈窗**
- 新增「使用平台」下拉選單(選項:`使用系統預設`、`EVERY8D`、`三竹`
### `Scripts/MessageAccountManage.js`
新增 `data`
- `platforms: []` — 平台清單
- `currentPlatform: null` — 目前預設平台物件
- `showPlatformSettings: false` — 控制平台設定展開
- `showSwitchModal: false` — 切換平台彈窗
新增 `methods`
- `loadPlatforms()` — 呼叫 `GetPlatforms()`
- `loadCurrentPlatform()` — 呼叫 `GetCurrentPlatform()`
- `savePlatformSetting(platform)` — 呼叫 `SavePlatform()`
- `setCurrentPlatform(platformCode)` — 呼叫 `SetCurrentPlatform()`
修改 `editItem`
- 新增 `PlatformCode: ''`(空字串 = 使用系統預設)
---
## 三竹 API 說明
### 發送 API
```
ApiBaseUrl = https://message.mitake.com.tw (硬寫於 MITAKESMSProvider)
GET/POST {ApiBaseUrl}/SmSendGet.asp
參數:
username = 帳號
password = 密碼
dstaddr = 手機號碼(多筆以 $ 分隔)
smbody = 簡訊內容(URL Encode, UTF-8
CharsetURL = UTF-8
```
回傳格式(純文字,每筆一行):
```
[序號]
statuscode=1
destaddr=0912345678
msgid=訊息ID
```
### 查詢餘額 API
```
GET {ApiBaseUrl}/SmQueryGet.asp
參數:
username = 帳號
password = 密碼
CharsetURL = UTF-8
```
回傳格式:
```
AccountPoint=100
```
---
## 檔案清單
| 異動類型 | 路徑 |
| ---- | --------------------------------------- |
| 新增 | `DOC/多簡訊平台架構設計.md` |
| 新增 | `Services/MITAKESMSProvider.cs` |
| 新增 | `Models/MMSPlatformItem.cs` |
| 新增 | `Models/MMSPlatformList.cs` |
| 新增 | `DAC/DAC_MMSPlatform.cs` |
| 修改 | `Services/EVERY8DSMSProvider.cs` |
| 修改 | `Models/ProjectMMSAccountItem.cs` |
| 修改 | `Models/MessageQueueItem.cs` |
| 修改 | `DAC/DAC_ProjectMMSAccount.cs` |
| 修改 | `Common/PublicVariable.cs` |
| 修改 | `Global.asax.cs` |
| 修改 | `Services/SMSSendingService.cs` |
| 修改 | `Services/MessageAccountManage.asmx.cs` |
| 修改 | `Forms/MessageAccountManage.aspx` |
| 修改 | `Scripts/MessageAccountManage.js` |
+65
View File
@@ -0,0 +1,65 @@
# 活動人員管理
## 資料關聯
Classes.ID (1) <-> (*) SignUp.CourseID
## 資料類別
* 各資料存取類別、資料集合類別、資料類別已有程式碼
* 如需擴充加在 DAC_XXXX2.cs 裡面
* DAC_Classes:活動資料處理類別
* ClassesList:活動資料集類別
* ClassesItem:活動資料項目類別
* DAC_SignUp:活動參加人員資料處理類別
* SignUpList:活動參加人員資料集類別
* SignUpItem:活動參加人員資料項目類別
- 主要欄位:ID, CourseID, SeqNo, Name, Mobile, IDNumber, QRCode, SignUpType, CheckInTime, CheckOutTime, IDType, MealType, Email, Gender, LossJobTimes, AgreementStatus
## 畫面檔
### 活動參加人員
* Forms/SignUpList.aspx
* 畫面上方查詢活動,用下拉選擇活動
* 畫面下方列出活動參加人員
* 分頁顯示,每頁預設 20 筆,可選 10, 20, 50, 100 筆
* 分頁列 [第一頁][上一頁] 第[頁數下拉]頁 [下一頁][最後頁] | 每頁[筆數下拉]筆共[筆數]筆
### 匯入參加人員
* Forms/SignUpImport.aspx
* 畫面上方查詢活動,用下拉選擇活動
* 選擇 .xlsx 檔案,上傳
* 列出預覽及驗證錯誤處
* 執行匯入,匯入選擇到的活動
* 匯入格式與必要欄位是 Name 學員姓名、Mobile 手機號
* 要能偵測並跳過表頭
### 匯出參加人員
* Forms/SignUpExport.aspx
* 畫面上方查詢活動,用下拉選擇活動
* 執行匯出 .xlsx 檔
* 匯出格式是
* 表頭:活動名稱、日期、地點(空行)
* 表身:序號、學員姓名、手機號、簽到時間、簽退時間、身分別、身分證字號、餐點、電子郵件、失業給付認定次數、性別、報名方式
## 後端服務
* Services/ClassesService.asmx
* 活動的後端服務
* SearchClasses:查詢活動清單
* SearchProjects:搜尋計畫(用於自動完成)
* Services/SignUpService.asmx
* 活動參加人員的後端服務
* SearchSignUps:以活動編號(CourseID)查詢參加人員(基本查詢)
* SearchSignUpsAdvanced:進階查詢(支援序號、身分別、身分證字號、餐點、Email 等條件)
* SaveSignUp:儲存活動參加人員(新增或更新)
* DeleteSignUp(int id, int dbAppNo):刪除活動參加人員
* ConfirmImport(int courseID, List<SignUpItem> items, bool clearBefore):執行活動參加人員匯入
* ExportSignUps(int courseID, string timeFormat):匯出活動參加人員
* SendSmsBatch(int courseID, List<int> signupIds):批量發送簡訊
* GetSignUpById(int signupId):查詢單筆參加人員
* CheckBalance:查詢簡訊點數餘額
* Services/FileUploadHandler.ashx
* 檔案上傳處理器
* action=ProcessImportFile:上傳並解析匯入 Excel 檔案(含驗證),參數 courseID 由 QueryString 傳入
## 技術規格
* 前端使用 Vue.js 實作,不使用 Server Control
* 後端使用 WebService,要 EnableSession
@@ -0,0 +1,46 @@
# 活動人員簽到簽退
## 資料關聯
Classes.ID (1) <-> (*) SignUp.CourseID
## 資料類別
* 各資料存取類別、資料集合類別、資料類別已有程式碼
* 如需擴充加在 DAC_XXXX2.cs 裡面
* DAC_Classes:活動資料處理類別
* ClassesList:活動資料集類別
* ClassesItem:活動資料項目類別
* DAC_SignUp:活動參加人員資料處理類別
* SignUpList:活動參加人員資料集類別
* SignUpItem:活動參加人員資料項目類別
* CheckInItem:簽到資料項目類別(用於 UploadCheckIns
## 畫面檔
* 沒有,這是寫給 WinForm 客戶端使用的後端服務
## 後端服務
* Services/ClassesService.asmx
* 活動的後端服務
* SearchClasses:查詢活動清單
* Services/SignUpService.asmx
* 活動參加人員的後端服務
* SearchSignUps:以活動編號(CourseID)查詢參加人員
* UploadCheckIns(int courseID, List<CheckInItem> items):批量上傳簽到簽退資料
* 上傳資料欄位(CheckInItem):CourseID, Name, Mobile, IDNumber, IDType, MealType, Email, SignUpType, CheckInType, CheckInTime
* 驗證規則
* CourseID: 必填、必須存在於課程表 Classes.ID
* Name, Mobile: 必填
* SignUpType: 必填,允許值 (預約 / 現場)
* CheckInType: 必填,允許值 (CHECKIN / CHECKOUT)
* CheckInTime: 必填,格式 yyyy-MM-dd HH:mm:ss
* 處理邏輯
* 按 (CourseID, Name, Mobile) 查詢 SignUp 表
* 若找到則依照 CheckInType 更新 CheckInTime/CheckOutTime,計入成功筆數
* 若沒找到則新增 (SignUpType="現場"),並依 CheckInType 設定時間欄位,計入成功筆數
* 不使用 transaction
* 回應格式
* 成功: { Success: true, Message: "...", Data: { CreatedCount, UpdatedCount, FailedCount, SkippedCount, SuccessIDs, Errors } }
* 失敗: { Success: false, Message: "...", Data: {...} }
## 技術規格
* 前端使用 Vue.js 實作,不使用 Server Control
* 後端使用 WebService,要 EnableSession
+95
View File
@@ -0,0 +1,95 @@
# 活動管理
## 畫面檔:
~/Forms/ClassesList.aspx
## 資料存取類別
~/Models/DAC_Classes1.cs(自動產生,勿修改)
~/Models/DAC_Classes2.cs(自訂擴充)
- DAC_Classes:資料處理類別
- ClassesList:資料集類別
- ClassesItem:資料項目類別
## 畫面排列:
上方:搜尋列
- 使用 CourseCode、CourseName、CourseDate_1CourseDate 起)、CourseDate_2CourseDate 迄)、CourseLocation 為查詢條件
下方:搜尋結果列表
- 顯示欄位為 CourseCode、CourseName、CourseDate、CourseLocation、操作
- 新增按鈕放置在表頭的操作欄
- 更正、刪除按鈕放置在操作欄
- CourseCode、CourseDate 向中對齊,其餘向左對齊
## UI 規範:
- 小窗尺寸:700x500px
- 搜尋結果每頁顯示 20 筆
- 操作按鈕使用 Bootstrap 樣式
## 錯誤處理:
- 使用系統共用的 $message 來顯示
## 新增更正:
- 新增更正時彈出小窗
- 新增有兩個存檔按鈕
存檔:存檔後關閉小窗
存檔後繼續新增:存檔後清除輸入內容重新進入新增模式
- 更正存檔後關閉小窗
## 刪除:
- 彈 confirm 確認後刪除
## 資料輸入:
- CourseCode 不可輸入,不可修改,自動編號(BeforeInsertOne 處理)
- CourseName 必填,可輸入,可修改,單行文字框,最長 100 Unicode 字元
- CourseDate 必填,可輸入,可修改,日期輸入框,YYYY-MM-DD 格式,不必驗證是否晚於今天
- CourseLocation 非必填,可輸入,可修改,單行文字框
- NotificationTitle 非必填,可輸入,可修改,單行文字框,最長 100 Unicode 字元
- NotificationTemplate 非必填,可輸入,可修改,多行文字框,最長 500 Unicode 字元
NotificationTemplate 插入規則:
- 插入格式:{活動日期}、{活動名稱}、{活動地點}
- 插入位置:游標當前位置
- 範例:「提醒您,{活動名稱}將於{活動日期}在{活動地點}舉行」
- AgreedTerms 非必填
## 前端程式碼:
- 使用 Vue.js 實現動態資料綁定與操作。
- 不使用 Server control
## 後端程式碼:
- 使用 WebService 實現非同步資料交換。
## WebService 端點:
Services/ClassesService.asmx
- EnableSession(True)
- SearchClasses(int pageIndex, int pageSize, string courseCode, string courseName, DateTime? courseDate1, DateTime? courseDate2, string courseLocation, bool? closed, string projectID):搜尋活動
- pageIndex 從 1 開始
- 查詢 function 用 DAC_Classes.SelectPage(bool noInputReturnAll, int startRowIndex, int maximumRows, string CourseCode, string CourseName, DateTime? CourseDate_1, DateTime? CourseDate_2, string CourseLocation, bool? Closed, string ProjectID, string orderBy = "")
- 筆數 function 用 DAC_Classes.SelectCount(bool noInputReturnAll, string CourseCode, string CourseName, DateTime? CourseDate_1, DateTime? CourseDate_2, string CourseLocation, bool? Closed, string ProjectID, string orderBy = "")
- 查詢條件為 AND 關係
- 空白條件視為不限制(noInputReturnAll = true
- CourseDate 為範圍查詢
- 每筆結果補填 ProjectName(由 DAC_BPAA.SelectProjectName 查詢)
- 回傳:BaseResponseSuccess, Message, Data: ClassesList.ToDict(), TotalCount: 總筆數)
- SaveClasses(bool isNew, ClassesItem item):存檔
- isNew:是否為新增
- item:新增或更正的活動資料(型別 ClassesItem
- 回傳:BaseResponseSuccess, Message, Data: 儲存後的 ClassesItem
- DeleteClasses(int id, int dbAppNo):刪除活動
- 呼叫 DAC_Classes.DeleteOne(int ID, int DB_APPNO)
- 回傳:BaseResponseSuccess, Message
- SearchProjects(string keyword):搜尋計畫(用於自動完成)
- 呼叫 DAC_BPAA.Select(keyword)
- 回傳:BaseResponseSuccess, Message, Data: NameValueList
- ExportClassesReport(int classesID, string timeFormat = "datetime"):匯出學員簽到表
- 輸入參數:classesID(課程 ID)、timeFormat(時間格式,"datetime" 或 "time"
- 查詢課程及參加人員資料,使用 NPOI 產生 Excel
- 回傳:BaseResponseSuccess, Message, Data: { FileName, FileBase64, FileSize, DataCount }
- ExportClassesList(DateTime? courseDate1, DateTime? courseDate2, string projectID):匯出課程清單
- 輸入參數:courseDate1(課程日期起)、courseDate2(課程日期迄)、projectID(計畫代號)
- 查詢課程清單及各課程的參加人員統計,使用 NPOI 產生 Excel
- 回傳:BaseResponseSuccess, Message, Data: { FileName, FileBase64, FileSize }
+44
View File
@@ -0,0 +1,44 @@
# 簡訊帳號管理
用來管理計畫的簡訊發送帳號密碼
## 資料表
- 計畫資料表 BPAA:只用來做輸入的自動完成
- 資料存取類別 DAC_BPAA
- 計畫簡訊帳號密碼 ProjectMMSAccount
- 資料存取類別 DAC_ProjectMMSAccount
- 資料集合類別 ProjectMMSAccountList
- 資料紀錄類別 ProjectMMSAccountItem
- 類別已存在,不需撰寫
- 資料表結構
|No |Field Name |Data Type|Length|Not Null|PK |Identity|Description |
|---|-----------|---------|------|--------|---|--------|---------------|
|1 |ID |INT | |V |V |V |流水號 |
|2 |ProjectID |NVARCHAR |20 |V | | |計畫代號 |
|3 |MMSAccount |NVARCHAR |50 |V | | |簡訊帳號 |
|4 |MMSPassword|NVARCHAR |50 |V | | |簡訊密碼 |
## 前端畫面檔
- ~/Forms/MessageAccountManage.aspx
- ~/Scripts/MessageAccountManage.js
- 使用 Vue.js,不使用 Server control
- 參照專案其他畫面的做法,分為查詢區域、列表區域、新增修改彈窗
- 刪除前需要確認
## 後端服務
### 計畫輸入自動完成
- ~/Services/MessageAccountManage.asmx/SearchProjects(string keyword)
- 呼叫 DAC_BPAA.Select(keyword)
- 回傳:BaseResponseSuccess, Message, Data: NameValueList
### 計畫簡訊帳號密碼 CRUD
- ~/Services/MessageAccountManage.asmx
- 查詢方法 Search(int pageNo, int pageSize, string projectID)
- 每筆結果補填 ProjectName(由 DAC_BPAA.SelectProjectName 查詢)
- 回傳:BaseResponseSuccess, Message, Data: ProjectMMSAccountList, TotalCount
- 存檔方法 Save(bool isNew, ProjectMMSAccountItem item)
- 新增、修改時都需檢查 ProjectID 必填且唯一
- 回傳:BaseResponseSuccess, Message, Data: 儲存後的 ProjectMMSAccountItem
- 刪除方法 Delete(int id, int dbAppNo)
- 回傳:BaseResponseSuccess, Message
+72
View File
@@ -0,0 +1,72 @@
# 發送簡訊
## 架構
* 從 Global.asax 的 Application_Start 事件啟動一個 Timer(預設每 1 分鐘觸發),定時啟動 SMSSendingService
* 發送服務(Services/SMSSendingService.cs
* 是單例類(Singleton),有 IsBusy 狀態可以顯示是否正在忙碌
* 每次啟動就把待發送簡訊全部發完
* 啟動時(Start 方法)
* 若 _busy == true 則直接返回,避免重複執行
* 設定 _busy = true
* 查詢 MessageQueue 表 Status 是 PENDING 或 SENDING 的
* 依課程 ProjectID 查詢對應的簡訊帳號密碼(DAC_ProjectMMSAccount),若無設定則使用系統預設帳號密碼
* 逐一發送(SendMessage 方法)
* 每一個發送開始時改狀態為 SENDING
* 呼叫 EVERY8DSMSProvider.SendMMS 發送
* 成功或失敗結果寫入 MessageLog 表
* 刪除 MessageQueue 紀錄
* 每一個發送做一個 DB transaction
* 最後重置 _busy = false(在 finally 確保執行)
* 錯誤寫入 ~/Log/SMSError_yyyyMMdd.txt
## 預約發送(加入佇列)
* Services/SignUpService.asmx
* METHOD SendSmsBatch(int courseID, List<int> signupIds)
* 驗證課程的 NotificationTitle 和 NotificationTemplate 已設定
* 對每位參加人員
* 若無 QRCode 則自動產生(DAC_SignUp.GenQRCodeText)並更新回資料表
* 以 ZXing 產生 QRCode 圖片,轉成 Base64
* 將訊息寫入 MessageQueue 表,Status = "PENDING"
* NotificationTemplate 支援替換字串:{課程日期}/{活動日期}、{課程名稱}/{活動名稱}、{課程地點}/{活動地點}、{學員姓名}、{開始時間}、{結束時間}、{講師}
* 回傳:BaseResponseSuccess, Message, Data: { SuccessCount, FailedCount, SuccessRecords, Errors, TotalRequested }
## 資料表
### 待發送簡訊 MessageQueue
* 資料存取類別 DAC_MessageQueue
* 資料表結構
| No | Field Name | Data Type | Length | Not Null | PK | Identity | Description |
|----|-------------|-----------|--------|----------|----|----------|-----------------------------|
| 1 | ID | INT | | V | V | V | 流水號 |
| 2 | CourseID | INT | | V | | | 課程ID |
| 3 | Status | NVARCHAR | 10 | | | | 發送狀態 PENDING = 等候發送 |
| | | | | | | | SENDING = 發送中 |
| | | | | | | | SENT = 已發送 |
| | | | | | | | FAILED = 發送失敗 |
| 4 | Mobile | NVARCHAR | 20 | | | | 手機號 |
| 5 | Subject | NVARCHAR | 100 | | | | 簡訊標題 |
| 6 | Content | NVARCHAR | 500 | | | | 發送內容 |
| 7 | QRCode | TEXT | | | | | QRCode 圖片的 BASE64 |
| 8 | Name | NVARCHAR | 100 | | | | 學員姓名 |
| 9 | FailMessage | NVARCHAR | 200 | | | | 發送失敗訊息 |
### 發送紀錄 MessageLog
* 資料存取類別 DAC_MessageLog
* 資料表結構
| No | Field Name | Data Type | Length | Not Null | PK | Identity | Description |
|----|-------------|-----------|--------|----------|----|----------|------------------------|
| 1 | ID | INT | | V | V | V | 流水號 |
| 2 | CourseID | INT | | V | | | 課程ID |
| 3 | Status | NVARCHAR | 10 | | | | 發送狀態 SENT = 已發送 |
| | | | | | | | FAILED = 發送失敗 |
| 4 | Mobile | NVARCHAR | 20 | | | | 手機號 |
| 5 | Subject | NVARCHAR | 100 | | | | 簡訊標題 |
| 6 | Content | NVARCHAR | 500 | | | | 發送內容 |
| 7 | Name | NVARCHAR | 100 | | | | 學員姓名 |
| 8 | FailMessage | NVARCHAR | 200 | | | | 發送失敗訊息 |
## 簡訊服務商
服務商:EVERY8D
具體的簡訊發送實作在 Services/EVERY8DSMSProvider.cs
帳號密碼設定在 PublicVariable(系統預設)或各計畫的 ProjectMMSAccount 資料表(計畫特定)
+50
View File
@@ -0,0 +1,50 @@
# 群組人員管理
## 畫面檔:
~/Forms/RoleManage.aspx
## 畫面排列:
左側 1/3:群組清單區域
- 顯示所有系統群組以及說明的列表。
右側 2/3:人員清單區域
- 左半部:該群組下所有成員列表(群組成員)
- 右半部:人員查詢區域
* 查詢條件:員工編號或姓名
* 查詢結果:符合條件的員工列表(可加入群組)
功能說明:
- 從右側人員查詢結果中選擇成員,點擊「加入群組」按鈕,將該成員加入左側所選群組中。
- 從左側群組成員列表中選擇成員,點擊「移除群組」按鈕,將該成員從群組中移除。
- 支援多選成員進行批次加入或移除操作。
## 資料來源:
- 群組清單及說明:程式內定義的 GroupList 常數陣列(位置:RoleManage.aspx 裡 Vue data 的 GroupList,內容我會補充上去)
GroupList 結構:[{ id: '群組代碼', name: '群組名稱', description: '群組說明' }, ...]
- 群組成員列表:來自 DAC_BWEH.SelectByGroupIdDAC_BWEH 為群組與員工對應表,儲存哪些員工屬於哪些群組,查詢結果的 Name 是員工姓名,Value 是員工編號。
- 人員查詢,來自 Models.DAC_BPSN.Select,查詢結果的 Name 是員工姓名,Value 是員工編號。
## 前端程式碼:
- 使用 Vue.js 實現動態資料綁定與操作。
- 不使用 Server control
## 後端程式碼:
- 使用 WebService 實現非同步資料交換。
- 提供 API 端點處理成員搜尋、成員加入與移除群組的請求。
## WebService 端點:Services/RoleManageService.asmx
- SearchEmployees(string keyword):搜尋員工,關鍵字可為員工編號或姓名
回傳:BaseResponseSuccess, Message, Data: NameValueList, TotalCount
- GetGroupMembers(string groupID):取得群組成員列表
回傳:BaseResponseSuccess, Message, Data: NameValueList, TotalCount
- AddMemberToGroup(string groupID, string[] employeeIDs):將成員加入群組
回傳:BaseResponseSuccess, Message, TotalCount: 成功加入筆數)
- RemoveMemberFromGroup(string groupID, string[] employeeIDs):將成員從群組移除
回傳:BaseResponseSuccess, Message, TotalCount: 成功移除筆數)
## 操作流程:
1. 從左側群組清單中選擇一個群組
2. 右側自動載入該群組的現有成員
3. 使用右側查詢區域搜尋要加入的人員
4. 選擇人員後點擊「加入群組」或從成員列表中「移除群組」
+37
View File
@@ -0,0 +1,37 @@
# 課程報表
## 畫面檔:
~/Forms/ClassesReport.aspx
## 資料存取類別
~/Models/DAC_Classes1.cs / DAC_Classes2.cs
- DAC_Classes:資料處理類別
- ClassesList:資料集類別
- ClassesItem:資料項目類別
~/Models/DAC_SignUp1.cs / DAC_SignUp2.cs
- DAC_SignUp:資料處理類別
- SignUpList:資料集類別
- SignUpItem:資料項目類別
## 畫面排列:
上方:搜尋列
- 比照 ~/Forms/ClassesList.aspx
下方:搜尋結果列表
- 除了操作欄位之外的資料顯示欄位比照 ~/Forms/ClassesList.aspx
- 報表匯出按鈕放置在操作欄,按下後呼叫後端的 ExportClassesReport
## 後端處理
- ~/Services/ClassesService.asmx
- ExportClassesReport(int classesID, string timeFormat = "datetime"):匯出學員簽到表
- 輸入參數:classesID(課程 ID)、timeFormat(時間格式,"datetime" 顯示完整日期時間,"time" 只顯示時間)
- 查詢課程資訊(含 ProjectName)及該課程所有參加人員
- 統計:實到人數、應到人數、缺席人數、現場報名、異常人數
- 使用 NPOI 產生 Excel,欄位:序號、學員姓名、手機號碼、身分證字號、性別、認定次數、用餐別、報名方式、簽到時間、簽退時間、出席狀況
- 回傳:BaseResponseSuccess, Message, Data: { FileName, FileBase64, FileSize, DataCount }
- ExportClassesList(DateTime? courseDate1, DateTime? courseDate2, string projectID):匯出課程清單
- 輸入參數:courseDate1(課程日期起)、courseDate2(課程日期迄)、projectID(計畫代號)
- 查詢符合條件的課程,並逐一統計各課程的實到/應到/缺席/現場報名人數
- 使用 NPOI 產生 Excel,欄位:課程名稱、課程地點、授課講師、實到人數、應到人數、缺席人數、現場報名
- 回傳:BaseResponseSuccess, Message, Data: { FileName, FileBase64, FileSize }
+45
View File
@@ -0,0 +1,45 @@
# 資料同步
## 員工資料下載
* Services/BPSNService.asmx
* METHOD Download:下載員工資料
* 資料存取類別:DAC_BPSN
* 資料讀取方法:SelectForClient()
## 報到資料上傳
* Services/SignUpService.asmx
* METHOD UploadCheckIns(int courseID, List<CheckInItem> items):上傳簽到簽退資料清單
* 資料表
* SignUp 參加人員
* Classes 課程
* 資料存取類別
* DAC_SignUp 參加人員
* DAC_Classes 課程
* 上傳資料欄位(CheckInItem
* CourseID (INTEGER NOT NULL) - 課程ID
* Name (TEXT NOT NULL) - 學員姓名
* Mobile (TEXT NOT NULL) - 手機號
* IDNumber (TEXT) - 身分證字號
* IDType (TEXT) - 身分別
* MealType (TEXT) - 餐點
* Email (TEXT) - 電子郵件
* SignUpType (TEXT) - 報名方式(預約 / 現場)
* CheckInType (TEXT) - 簽到類別(CHECKIN / CHECKOUT
* CheckInTime (DATETIME) - 簽到/簽退時間
* 驗證規則
* CourseID: 必填、必須存在於課程表 Classes.ID
* Name: 必填
* Mobile: 必填
* SignUpType: 必填,允許值 (預約 / 現場)
* CheckInType: 必填,允許值(CHECKIN / CHECKOUT
* CheckInTime: 必填,格式 yyyy-MM-dd HH:mm:ss
* 處理邏輯
* 用 CourseID 查詢 Classes.ID,若不存在則拒絕整批資料
* 按 (CourseID, Name, Mobile) 作為複合主鍵查詢 SignUp 表
* 若找到則依照 CheckInType 使用傳入的 CheckInTime 更新 CheckInTime/CheckOutTime,計入成功筆數
* 若沒找到則新增 (SignUpType="現場"),並依 CheckInType 設定時間欄位,計入成功筆數
* 不使用 transaction
* 回應格式
* 成功: { Success: true, Message: "...", Data: { CreatedCount, UpdatedCount, FailedCount, SkippedCount, SuccessIDs, Errors } }
* 失敗: { Success: false, Message: "..." }