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
+250
View File
@@ -0,0 +1,250 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Transactions;
using WebLib;
namespace
{
/// <summary>
/// 簡訊發送服務
/// 單例類,用於定時發送待發送簡訊
/// </summary>
public sealed class SMSSendingService
{
private static SMSSendingService _instance;
private static readonly object _lockObject = new object();
private bool _busy = false;
/// <summary>
/// 取得單一實例
/// </summary>
public static SMSSendingService Instance
{
get
{
if (_instance == null)
{
lock (_lockObject)
{
if (_instance == null)
{
_instance = new SMSSendingService();
}
}
}
return _instance;
}
}
private SMSSendingService()
{
}
/// <summary>
/// 是否正在忙碌中
/// </summary>
public bool IsBusy
{
get { return _busy; }
}
/// <summary>
/// 啟動發送服務,發送所有待發送的簡訊
/// </summary>
public void Start()
{
// 若已在忙碌中,則不重複啟動
if (_busy)
{
return;
}
try
{
_busy = true;
using (var conn = DAC.NewConnection())
{
// 查詢待發送簡訊(Status 為 PENDING 或 SENDING 的)
var dacQueue = new DAC_MessageQueue(conn);
var messagesToSend = dacQueue.SelectAllPendingOrSending();
// 如果沒有待發送簡訊,直接返回
if (messagesToSend.Count == 0)
{
_busy = false;
return;
}
// 查詢簡訊發送帳號密碼併填入 messagesToSend 的項目裡
var dacClasses = new DAC_Classes(conn);
var classes = dacClasses.SelectAll();
// 查詢簡訊平台的主帳號密碼
var dacMessagePlatforms = new DAC_MMSPlatform(conn);
var currentMMSPlatformAccount = dacMessagePlatforms.SelectByCode(PublicVariable.CurrentMMSPlatform);
// 查詢專案的簡訊帳號密碼
var dacMessageAccount = new DAC_ProjectMMSAccount(conn);
var messageAccounts = dacMessageAccount.SelectAll();
foreach (var message in messagesToSend)
{
var cls = classes.FirstOrDefault(p => p.ID == message.CourseID);
// 沒有設定計畫的,使用預設平台的主要帳號密碼
if (cls == null || string.IsNullOrEmpty(cls.ProjectID))
{
message.PlatformCode = PublicVariable.CurrentMMSPlatform;
message.MMSAccount = currentMMSPlatformAccount?.Account;
message.MMSPassword = currentMMSPlatformAccount?.Password;
continue;
}
var acc = messageAccounts.FirstOrDefault(p => p.ProjectID == cls.ProjectID && p.PlatformCode == PublicVariable.CurrentMMSPlatform);
// 沒有設定計畫簡訊帳號密碼的,使用預設平台的主要帳號密碼
if (acc == null || string.IsNullOrEmpty(acc.MMSAccount) || string.IsNullOrEmpty(acc.MMSPassword))
{
message.PlatformCode = PublicVariable.CurrentMMSPlatform;
message.MMSAccount = currentMMSPlatformAccount?.Account;
message.MMSPassword = currentMMSPlatformAccount?.Password;
continue;
}
// 使用計畫指定的帳號密碼
message.MMSAccount = acc.MMSAccount;
message.MMSPassword = acc.MMSPassword;
message.PlatformCode = PublicVariable.CurrentMMSPlatform;
}
// 發送
var dacLog = new DAC_MessageLog(conn);
foreach (var message in messagesToSend)
{
SendMessage(message, dacQueue, dacLog);
Thread.Sleep(500);
}
}
}
catch (Exception ex)
{
// 記錄錯誤日誌
LogError("SMSSendingService.Start", ex);
}
finally
{
_busy = false;
}
}
/// <summary>
/// 發送單條簡訊
/// </summary>
private void SendMessage(MessageQueueItem message, DAC_MessageQueue dacQueue, DAC_MessageLog dacLog)
{
using (TransactionScope ts = DAC.NewTransactionScope())
{
try
{
// 1. 更新簡訊狀態為 SENDING
message.Status = "SENDING";
dacQueue.UpdateOne(message);
message.DB_APPNO++;
// 2. 呼叫簡訊服務商 API 發送
bool sendSuccess = false;
string failMessage = "";
try
{
switch (message.PlatformCode)
{
case "EVERY8D":
sendSuccess = EVERY8DSMSProvider.SendMMS(message, out failMessage);
break;
case "MITAKE":
default:
sendSuccess = MITAKESMSProvider.SendMMS(message, out failMessage);
break;
}
}
catch (Exception ex)
{
sendSuccess = false;
failMessage = ex.Message;
}
// 3. 記錄發送結果到 MessageLog
MessageLogItem logItem = new MessageLogItem
{
CourseID = message.CourseID,
Status = sendSuccess ? "SENT" : "FAILED",
Name = message.Name,
Mobile = message.Mobile,
Subject = message.Subject,
Content = message.Content,
DB_APPNO = 1,
DB_CRDAT = DateTime.Now,
DB_CRUSR = "SYSTEM",
DB_TRDAT = DateTime.MinValue,
DB_TRUSR = ""
};
// 如果失敗,記錄失敗訊息
if (!sendSuccess && !string.IsNullOrEmpty(failMessage))
{
logItem.FailMessage = failMessage;
}
dacLog.InsertOne(logItem);
// 4. 刪除 MessageQueue 紀錄
dacQueue.DeleteOne(message);
ts.Complete();
}
catch (Exception ex)
{
LogError("SendMessage", ex, message.ID.ToString());
ts.Dispose();
}
}
}
/// <summary>
/// 記錄錯誤日誌
/// </summary>
private void LogError(string source, Exception ex, string details = "")
{
try
{
string logPath = System.Web.HttpContext.Current?.Server.MapPath("~/Log") ?? "~/Log";
if (!System.IO.Directory.Exists(logPath))
{
System.IO.Directory.CreateDirectory(logPath);
}
string fileName = DateTime.Now.ToString("yyyyMMdd_HHmmss_fff");
string message = string.Format(
"[{0}] {1}\r\nSource: {2}\r\nException: {3}\r\nDetails: {4}\r\nStackTrace: {5}\r\n",
DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"),
"SMS Sending Error",
source,
ex.Message,
details,
ex.StackTrace
);
System.IO.File.AppendAllText(
System.IO.Path.Combine(logPath, string.Format("SMSError_{0}.txt", DateTime.Now.ToString("yyyyMMdd"))),
message
);
}
catch
{
// 忽略日誌記錄的錯誤
}
}
}
}