using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using ChatworkBulkSender.Daos;
using ChatworkBulkSender.Dtos;
using ChatworkBulkSender.UserControls;
using ChatworkBulkSender.Utils;
namespace ChatworkBulkSender.Forms
{
public partial class M_CustomerMasterIndividualEdit : Form,ICreatable<CustomerMasterDto>,IUpdateble<CustomerMasterDto>
{
public enum ButtonMode
{
Register, // 新規登録
Update // 更新
,
}
private CustomerIndividualEditControl _customerIndividualEdit = null;
private BtnRegisterControl _btnRegister = null;
private BtnUpdateControl _btnUpdate = null;
public event EventHandler<CustomerMasterDto> DataCreated;
public event EventHandler<CustomerMasterDto> DataUpdated;
// 初期値は、新規登録
private ButtonMode _btnMode = ButtonMode.Register;
// データ変更検知
private DataChangeDetector _changeDetector = new DataChangeDetector();
// 楽観的ロック用の更新日時
private DateTime? _currentUpdatedAt = null;
public M_CustomerMasterIndividualEdit(ButtonMode buttonMode = ButtonMode.Register, CustomerMasterDto customer = null)
{
InitializeComponent();
_btnMode = buttonMode;
this.ClientSize = new Size(1600,900);
this.MinimumSize = new Size(1200,700);
// ウィンドウサイズを固定にする
this.FormBorderStyle = FormBorderStyle.FixedSingle;
this.StartPosition = FormStartPosition.CenterScreen;
_customerIndividualEdit = new CustomerIndividualEditControl();
_customerIndividualEdit.Dock = DockStyle.Fill;
if (_btnMode == ButtonMode.Update)
{
_btnUpdate = new BtnUpdateControl();
_btnUpdate.Dock = DockStyle.Fill;
panel2.Controls.Add(_btnUpdate);
if (customer != null)
{
// 最新データをDBから取得
var dao = new CustomerMasterDao();
var latestData = dao.GetByManagementNumber(customer.ManagementNumber);
if (latestData != null)
{
_customerIndividualEdit = new CustomerIndividualEditControl(latestData);
// 楽観的ロック用に現在の更新日時を保持
_currentUpdatedAt = latestData.UpdatedAt;
}
else
{
_customerIndividualEdit = new CustomerIndividualEditControl(customer);
_currentUpdatedAt = customer.UpdatedAt;
}
}
}
else
{
_btnRegister = new BtnRegisterControl();
_btnRegister.Dock = DockStyle.Fill;
panel2.Controls.Add(_btnRegister);
_customerIndividualEdit = new CustomerIndividualEditControl();
CheckBox chkIsUnused = _customerIndividualEdit.Controls.Find("chkUnusedData", true).FirstOrDefault() as CheckBox;
chkIsUnused.Visible = false;
}
panel1.Controls.Add(_customerIndividualEdit);
}
private void M_CustomerMasterIndividualEdit_Load(object sender, EventArgs e)
{
this.ActiveControl = panel1;
if (_btnMode == ButtonMode.Update)
{
_btnUpdate.BtnUpdateClicked += BtnUpdate_Click;
_btnUpdate.BtnReturnClicked += BtnReturn_Click;
}
else
{
_btnRegister.BtnRegisterClicked += BtnRegister_Click;
_btnRegister.BtnReturnClicked += BtnReturn_Click;
}
// フォントの統一設定を適用
FontSettingHelper.ApplyFontsToForm(this);
// ユーザーコントロールにも適用
FontSettingHelper.ApplyFontsToControl(_customerIndividualEdit);
if (_btnMode == ButtonMode.Update)
{
FontSettingHelper.ApplyFontsToControl(_btnUpdate);
}
else
{
FontSettingHelper.ApplyFontsToControl(_btnRegister);
}
// TabIndexの設定(フォーカス順制御)
TabIndexHelper.SetIndividualEditScreenTabOrder(this);
// データ変更検知を開始
_changeDetector.StartMonitoring(_customerIndividualEdit);
}
private void DisposeViewControls()
{
panel1.Controls.Clear();
panel2.Controls.Clear();
_customerIndividualEdit.Dispose();
if (_btnMode == ButtonMode.Update)
{
_btnUpdate.Dispose();
}
else
{
_btnRegister.Dispose();
}
}
private void BtnRegister_Click(object sender, EventArgs e)
{
try
{
var newCustomer = _customerIndividualEdit.GetCustomerData();
var dao = new CustomerMasterDao();
var createdId = dao.Insert(newCustomer);
if (createdId == 1)
{
DataCreated?.Invoke(this, newCustomer);
// 登録成功後は変更状態をリセット
_changeDetector.ResetChanges();
MessageBox.Show("登録が完了しました。", "情報", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
}
catch(Exception ex)
{
MessageBoxUtil.ShowErr($"{ex.Message}\n\n");
}
}
private void BtnUpdate_Click(object sender, EventArgs e)
{
try
{
var updatedCustomer = _customerIndividualEdit.GetCustomerData();
// 更新日時がない場合はエラー
if (_currentUpdatedAt == null)
{
MessageBox.Show("データの更新日時が取得できませんでした。", "エラー",
MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
var dao = new CustomerMasterDao();
// 更新前に最新のデータを取得して、他のユーザーが更新していないか確認
var currentDbData = dao.GetByManagementNumber(updatedCustomer.ManagementNumber);
// DateTime精度問題対策:1秒以内の差は同じとみなす
bool isUpdatedByOthers = false;
if (currentDbData != null)
{
if (currentDbData.UpdatedAt.HasValue && _currentUpdatedAt.HasValue)
{
var timeDiff = Math.Abs((currentDbData.UpdatedAt.Value - _currentUpdatedAt.Value).TotalSeconds);
isUpdatedByOthers = timeDiff > 1.0; // 1秒以上の差がある場合のみ更新とみなす
}
else if (currentDbData.UpdatedAt != _currentUpdatedAt)
{
isUpdatedByOthers = true;
}
}
if (isUpdatedByOthers)
{
// 他のユーザーが既に更新している
MessageBox.Show(MessageBoxUtil.DB_011, "エラー",
MessageBoxButtons.OK, MessageBoxIcon.Warning);
// 最新のデータで画面を更新
_customerIndividualEdit.SetCustomerData(currentDbData);
_currentUpdatedAt = currentDbData.UpdatedAt;
_changeDetector.ResetChanges();
return;
}
var updateCount = dao.Update(updatedCustomer, _currentUpdatedAt.Value);
if (updateCount == 0)
{
// 楽観的ロックエラー
MessageBox.Show(MessageBoxUtil.DB_011, "エラー",
MessageBoxButtons.OK, MessageBoxIcon.Warning);
// 最新のデータを取得して画面を更新
var refreshedData = dao.GetByManagementNumber(updatedCustomer.ManagementNumber);
if (refreshedData != null)
{
_customerIndividualEdit.SetCustomerData(refreshedData);
_currentUpdatedAt = refreshedData.UpdatedAt;
}
_changeDetector.ResetChanges();
return;
}
if (updateCount == 1)
{
// 更新成功後、最新のデータを取得して更新日時を更新
var latestData = dao.GetByManagementNumber(updatedCustomer.ManagementNumber);
if (latestData != null)
{
_currentUpdatedAt = latestData.UpdatedAt;
updatedCustomer.UpdatedAt = latestData.UpdatedAt; // イベントで渡すデータも更新
}
DataUpdated?.Invoke(this, updatedCustomer);
// 更新成功後は変更状態をリセット
_changeDetector.ResetChanges();
MessageBox.Show(MessageBoxUtil.DB_002, "情報",
MessageBoxButtons.OK, MessageBoxIcon.Information);
}
}
catch (Exception ex)
{
MessageBoxUtil.ShowErr($"{ex.Message}\n\n");
}
}
private void BtnReturn_Click(object sender, EventArgs e)
{
// 変更がある場合のみ確認
if (_changeDetector.HasChanges)
{
DialogResult result = MessageBox.Show(
"編集内容が保存されていません。破棄してよろしいですか?",
"確認",
MessageBoxButtons.YesNo,
MessageBoxIcon.Warning);
if (result != DialogResult.Yes)
{
return;
}
}
this.Close();
}
private void M_CustomerIndividualEdit_FormClosing(object sender, FormClosingEventArgs e)
{
// 戻るボタンから呼ばれた場合は、既に確認済みなのでスキップ
if (e.CloseReason == CloseReason.UserClosing && this.DialogResult != DialogResult.Cancel)
{
// 変更がある場合のみ確認
if (_changeDetector.HasChanges)
{
DialogResult result = MessageBox.Show(
"編集内容が保存されていません。破棄してよろしいですか?",
"確認",
MessageBoxButtons.YesNo,
MessageBoxIcon.Warning);
if (result != DialogResult.Yes)
{
e.Cancel = true;
return;
}
}
// 変更がない場合は確認メッセージを出さない
}
// イベント登録の解除
if (_btnMode == ButtonMode.Update)
{
_btnUpdate.BtnReturnClicked -= BtnReturn_Click;
}
else
{
_btnRegister.BtnReturnClicked -= BtnReturn_Click;
}
// 変更検知を停止
_changeDetector.StopMonitoring();
_changeDetector.Dispose();
// 明示的に解放する
this.DisposeViewControls();
// 設定メニューを表示する
this.Owner.Show();
// 再描写を行う
this.Owner.Refresh();
}
}
}