using System;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Threading;
using System.Data.SqlClient;
using ChatworkBulkSender.Forms;
using ChatworkBulkSender.Dtos;
using ChatworkBulkSender.Daos;
using ChatworkBulkSender.Utils;
using ChatworkBulkSender.Services.Chatwork;
namespace ChatworkBulkSender.UserControls
{
public partial class MessageConfirmControl : AbstractUserControl
{
// 親フォーム通知用
public event EventHandler SwitchToMessageInput;
public event EventHandler SwitchToMessageResult;
// 送信処理に関するDto
private BulkSendJobDto _bulkSendJobDto;
// 送信中フラグ
private bool _isSending = false;
// 親フォーム参照
private Form _parentForm;
public MessageConfirmControl()
{
InitializeComponent();
// 親フォームができあがったタイミングで FormClosing をフック
this.Load += (s, e) =>
{
_parentForm = this.FindForm();
if (_parentForm != null)
_parentForm.FormClosing += ParentForm_FormClosing;
};
}
public void SetBulkSendJobDto(BulkSendJobDto dto)
{
_bulkSendJobDto = dto;
destinationAndFilePathAndActualContentsControl1.SetDestinationAndFilePathGrid(dto);
}
public BulkSendJobDto GetBulkSendJobDto()
{
return _bulkSendJobDto;
}
/// <summary>
/// 親フォームの閉じる操作をフック。
/// 送信中ならキャンセルする。
/// </summary>
private void ParentForm_FormClosing(object sender, FormClosingEventArgs e)
{
if (_isSending)
{
// 送信中は閉じさせない
MessageBoxUtil.Show("送信処理実行中のため、アプリを閉じることはできません。", "送信中");
e.Cancel = true;
}
}
/// <summary>
/// 戻るボタン
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnBack_Click(object sender, EventArgs e)
{
SwitchToMessageInput?.Invoke(this, EventArgs.Empty);
}
/// <summary>
/// 送信開始ボタン
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private async void btnSend_Click(object sender, EventArgs e)
{
// 二重実行防止
if (_isSending) return;
// 最終確認
var result = MessageBoxUtil.ShowYesNo(MessageBoxUtil.ASK_001);
if (result != DialogResult.Yes) return;
// 最新のChatworkAPIを取得
var senderMaster = new SenderMasterDao().GetAll().FirstOrDefault();
_bulkSendJobDto.ApiToken = senderMaster.SenderApiToken;
// CancellationTokenSource を作成
var cts = new CancellationTokenSource();
// 送信処理(子画面で進捗表示)
using (var sendProgressForm = new T_SendProgress())
{
// 全送信件数をセット
int total = _bulkSendJobDto.DestinationList.Count;
sendProgressForm.SetMaximum(total);
// CTSをセット
sendProgressForm.CancellationTokenSource = cts;
// IProgress<int> の実体。UIスレッド上で更新される。
var progress = new Progress<int>(count => sendProgressForm.ReportProgress(count));
// ダイアログをモードレスで表示
sendProgressForm.Show(_parentForm);
// 送信処理/進捗レポート
await ExecuteBulkSendAsync(progress, cts.Token);
// ダイアログを閉じる
sendProgressForm.Close();
}
// 画面切り替え
SwitchToMessageResult?.Invoke(this, EventArgs.Empty);
}
/// <summary>
/// bulkSendJobDto.DestinationList の各宛先に対して、PDFアップロード+メッセージ送信を逐次実行する。
/// 送信中はUI操作(再送信/画面切り替え/終了)を抑制する。
/// トランザクション処理は意図的にしていない。途中でエラーが起こった場合でも、送信済み情報についてはログに残したいため。
/// </summary>
/// <param name="progress"></param>
/// <param name="token"></param>
/// <returns></returns>
public async Task ExecuteBulkSendAsync(IProgress<int> progress, CancellationToken token)
{
// 1) UI 制御を無効化
_isSending = true;
btnSend.Enabled = false;
btnBack.Enabled = false;
int successCount = 0, failureCount = 0;
var destList = _bulkSendJobDto.DestinationList;
var detailDao = new SendHistoryDetailDao();
try
{
// 2) DB登録(ヘッダー)
using (var conn = new SqlConnection(new DBAccess().connection_str))
{
await conn.OpenAsync(token);
_bulkSendJobDto.SendHistoryId = await new SendHistoryDao().InsertSendHistoryHeaderAsync(_bulkSendJobDto, conn);
}
// 3) 宛先ごとに送信&DB登録(明細)
using (var conn = new SqlConnection(new DBAccess().connection_str))
{
await conn.OpenAsync(token);
for (int detailIdx = 0; detailIdx < destList.Count; detailIdx++)
{
// キャンセルチェック
token.ThrowIfCancellationRequested();
// 送信
var dest = destList[detailIdx];
if (string.IsNullOrEmpty(dest.FilePath))
{
// メッセージ送信
dest.Result = await new ChatworkService().SendMessageAsync(dest.RoomId, dest.SendActualContents);
}
else
{
// ファイルアップロード+メッセージ送信
dest.Result = await new ChatworkService().UploadFileAsync(dest.RoomId, dest.FilePath, dest.SendActualContents);
}
// カウント集計
if (dest.Result.Success == Constants.SEND_RESULT.SUCCESS)
successCount++;
else
failureCount++;
// 明細登録
await detailDao.InsertSendHistoryDetailAsync((int)_bulkSendJobDto.SendHistoryId, dest, conn);
// 進捗更新
progress?.Report(detailIdx + 1);
}
}
MessageBoxUtil.Show("すべての送信が完了しました。", "送信完了");
}
catch (OperationCanceledException)
{
// 4) キャンセル発生時でも、未送信分を「試行せず」レコードだけ残す
using (var conn = new SqlConnection(new DBAccess().connection_str))
{
await conn.OpenAsync();
for (int j = successCount + failureCount; j < destList.Count; j++)
{
var dest = destList[j];
// キャンセル扱いの未送信は「失敗」としてカウント
failureCount++;
// 明細登録
await detailDao.InsertSendHistoryDetailAsync((int)_bulkSendJobDto.SendHistoryId, dest, conn);
}
}
MessageBoxUtil.ShowWarning("送信をキャンセルしました。", "中止");
}
catch (Exception ex)
{
MessageBoxUtil.ShowErr("送信処理でエラーが発生しました。\n" + ex.Message, "エラー");
}
finally
{
// 5) ヘッダーの成功/失敗件数と完了日時を更新
using (var conn = new SqlConnection(new DBAccess().connection_str))
{
if(_bulkSendJobDto.SendHistoryId != null)
{
await conn.OpenAsync();
await new SendHistoryDao()
.UpdateSendHistoryHeaderCountsAsync((int)_bulkSendJobDto.SendHistoryId, successCount, failureCount, conn);
}
}
// 6) UI 再有効化
_isSending = false;
btnSend.Enabled = true;
btnBack.Enabled = true;
}
}
private void MessageConfirmControl_Resize(object sender, EventArgs e)
{
CenterButton();
}
/// <summary>
/// 送信開始ボタンを左右中央に表示する。
/// </summary>
private void CenterButton()
{
int x = (this.ClientSize.Width - btnSend.Width) / 2;
btnSend.Left = x;
}
}
}