chore: 首次簽入 Thinkyu ASP.NET 專案
- 加入 Visual Studio / ASP.NET .gitignore - 排除建置輸出、IDE 設定、NuGet packages、大型 MSI 安裝檔
This commit is contained in:
@@ -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
|
||||
{
|
||||
// 忽略日誌記錄的錯誤
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user