251 lines
8.9 KiB
C#
251 lines
8.9 KiB
C#
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
|
|
{
|
|
// 忽略日誌記錄的錯誤
|
|
}
|
|
}
|
|
}
|
|
}
|