using WorkCameraExport.Models;
using WorkCameraExport.Services;
namespace WorkCameraExport.Forms
{
///
/// 迁移窗体 - 历史数据迁移功能
///
public partial class MigrationForm : Form
{
private readonly ApiService _apiService;
private readonly CosService _cosService;
private CancellationTokenSource? _migrationCts;
private bool _isMigrating;
// 记录数据
private List _records = new();
private int _totalRecords;
public MigrationForm(ApiService apiService)
{
InitializeComponent();
_apiService = apiService;
_cosService = new CosService();
}
///
/// 窗体加载事件
///
private void MigrationForm_Load(object sender, EventArgs e)
{
InitializeDefaults();
}
///
/// 初始化默认值
///
private void InitializeDefaults()
{
// 设置默认日期范围(最近90天)
dtpEndDate.Value = DateTime.Today;
dtpStartDate.Value = DateTime.Today.AddDays(-90);
// 设置默认状态为"未迁移"
cboStatus.SelectedIndex = 1; // 未迁移
UpdateMigrateButtonState();
}
#region 查询功能
///
/// 查询按钮点击事件
///
private async void btnQuery_Click(object sender, EventArgs e)
{
await DoQueryAsync();
}
///
/// 执行查询
///
private async Task DoQueryAsync()
{
SetQueryControlsEnabled(false);
lblStatusBar.Text = "正在查询...";
lblStatusBar.ForeColor = Color.Blue;
dgvRecords.Rows.Clear();
_records.Clear();
_totalRecords = 0;
try
{
var query = BuildQuery();
query.PageNum = 1;
query.PageSize = 50;
// 获取所有记录(分页获取)
var allRecords = new List();
int pageNum = 1;
while (true)
{
query.PageNum = pageNum;
var (success, message, data) = await _apiService.GetMigrationListAsync(query);
if (!success || data == null)
{
ShowError(message);
return;
}
allRecords.AddRange(data.Result);
_totalRecords = data.TotalNum;
if (data.Result.Count < 50 || pageNum >= data.TotalPage)
break;
pageNum++;
// 更新状态
lblStatusBar.Text = $"正在查询... 已获取 {allRecords.Count} 条";
}
_records = allRecords;
// 显示数据
foreach (var record in _records)
{
dgvRecords.Rows.Add(
false, // 选择框
record.Id,
record.RecordTime?.ToString("yyyy-MM-dd HH:mm") ?? "",
record.DeptName,
TruncateContent(record.Content, 40),
record.ImageCount,
GetStatusText(record.MigrationStatus)
);
}
// 更新统计信息
lblTotalRecords.Text = $"共 {_totalRecords} 条记录";
UpdateSelectedCount();
lblStatusBar.Text = $"查询完成,共 {_totalRecords} 条记录";
lblStatusBar.ForeColor = Color.Green;
}
catch (Exception ex)
{
ShowError($"查询异常: {ex.Message}");
}
finally
{
SetQueryControlsEnabled(true);
UpdateMigrateButtonState();
}
}
///
/// 构建查询参数
///
private MigrationQuery BuildQuery()
{
int? status = cboStatus.SelectedIndex switch
{
1 => 0, // 未迁移
2 => 1, // 已迁移
3 => 2, // 迁移失败
_ => null // 全部
};
return new MigrationQuery
{
StartDate = dtpStartDate.Value.Date,
EndDate = dtpEndDate.Value.Date.AddDays(1).AddSeconds(-1),
Status = status
};
}
///
/// 获取状态文本
///
private static string GetStatusText(int status)
{
return status switch
{
0 => "未迁移",
1 => "已迁移",
2 => "迁移失败",
_ => "未知"
};
}
#endregion
#region 选择功能
///
/// 全选/取消复选框变更事件
///
private void chkSelectAll_CheckedChanged(object sender, EventArgs e)
{
foreach (DataGridViewRow row in dgvRecords.Rows)
{
row.Cells[0].Value = chkSelectAll.Checked;
}
UpdateSelectedCount();
UpdateMigrateButtonState();
}
///
/// 数据网格单元格点击事件
///
private void dgvRecords_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
if (e.ColumnIndex == 0 && e.RowIndex >= 0)
{
// 延迟更新,等待复选框状态变更
BeginInvoke(new Action(() =>
{
UpdateSelectedCount();
UpdateMigrateButtonState();
}));
}
}
///
/// 更新已选数量
///
private void UpdateSelectedCount()
{
int selectedCount = GetSelectedRecordIds().Count;
lblSelectedRecords.Text = $"已选 {selectedCount} 条";
}
///
/// 获取选中的记录ID列表
///
private List GetSelectedRecordIds()
{
var selectedIds = new List();
foreach (DataGridViewRow row in dgvRecords.Rows)
{
if (row.Cells[0].Value is true)
{
if (int.TryParse(row.Cells[1].Value?.ToString(), out int id))
{
selectedIds.Add(id);
}
}
}
return selectedIds;
}
///
/// 获取选中的记录
///
private List GetSelectedRecords()
{
var selectedIds = GetSelectedRecordIds();
return _records.Where(r => selectedIds.Contains(r.Id)).ToList();
}
#endregion
#region 迁移功能
///
/// 迁移按钮点击事件
///
private async void btnMigrate_Click(object sender, EventArgs e)
{
if (_isMigrating)
{
// 取消迁移
_migrationCts?.Cancel();
return;
}
var selectedRecords = GetSelectedRecords();
if (selectedRecords.Count == 0)
{
MessageBox.Show("请先选择要迁移的记录", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
return;
}
var result = MessageBox.Show(
$"确定要迁移选中的 {selectedRecords.Count} 条记录吗?\n\n迁移过程中请勿关闭程序。",
"确认迁移",
MessageBoxButtons.YesNo,
MessageBoxIcon.Question);
if (result != DialogResult.Yes)
return;
await DoMigrationAsync(selectedRecords);
}
///
/// 执行迁移
///
private async Task DoMigrationAsync(List records)
{
_isMigrating = true;
_migrationCts = new CancellationTokenSource();
SetMigratingState(true);
int successCount = 0;
int failCount = 0;
int totalRecords = records.Count;
int processedRecords = 0;
try
{
// 获取 COS 临时密钥
lblCurrentRecord.Text = "正在获取 COS 临时密钥...";
var (credSuccess, credMessage, credentials) = await _apiService.GetTempCredentialsAsync();
if (!credSuccess || credentials == null)
{
ShowError($"获取 COS 临时密钥失败: {credMessage}");
return;
}
// 初始化 COS 服务
_cosService.Initialize(credentials);
// 逐条迁移记录
foreach (var record in records)
{
if (_migrationCts.Token.IsCancellationRequested)
{
lblStatusBar.Text = "迁移已取消";
lblStatusBar.ForeColor = Color.Orange;
break;
}
processedRecords++;
lblCurrentRecord.Text = $"正在迁移记录 {record.Id} ({processedRecords}/{totalRecords})...";
try
{
var migrationResult = await MigrateRecordAsync(record, _migrationCts.Token);
if (migrationResult)
{
successCount++;
UpdateRowStatus(record.Id, "已迁移", Color.Green);
}
else
{
failCount++;
UpdateRowStatus(record.Id, "迁移失败", Color.Red);
}
}
catch (OperationCanceledException)
{
break;
}
catch (Exception ex)
{
failCount++;
UpdateRowStatus(record.Id, "迁移失败", Color.Red);
System.Diagnostics.Debug.WriteLine($"迁移记录 {record.Id} 失败: {ex.Message}");
}
// 更新进度
int progress = (int)((processedRecords * 100.0) / totalRecords);
progressBar.Value = progress;
lblProgress.Text = $"{progress}%";
}
// 完成
if (!_migrationCts.Token.IsCancellationRequested)
{
progressBar.Value = 100;
lblProgress.Text = "100%";
lblCurrentRecord.Text = $"迁移完成!成功: {successCount}, 失败: {failCount}";
lblStatusBar.Text = $"迁移完成,成功 {successCount} 条,失败 {failCount} 条";
lblStatusBar.ForeColor = failCount > 0 ? Color.Orange : Color.Green;
MessageBox.Show(
$"迁移完成!\n\n成功: {successCount} 条\n失败: {failCount} 条",
"迁移结果",
MessageBoxButtons.OK,
failCount > 0 ? MessageBoxIcon.Warning : MessageBoxIcon.Information);
}
}
catch (Exception ex)
{
ShowError($"迁移异常: {ex.Message}");
}
finally
{
_isMigrating = false;
_migrationCts?.Dispose();
_migrationCts = null;
SetMigratingState(false);
}
}
///
/// 迁移单条记录
///
private async Task MigrateRecordAsync(MigrationRecordDto record, CancellationToken cancellationToken)
{
var urlPairs = new List();
// 获取需要迁移的图片(未迁移的)
var imagesToMigrate = record.Images.Where(img => !img.IsMigrated).ToList();
if (imagesToMigrate.Count == 0)
{
// 没有需要迁移的图片,直接返回成功
return true;
}
int imageIndex = 0;
int totalImages = imagesToMigrate.Count;
foreach (var image in imagesToMigrate)
{
if (cancellationToken.IsCancellationRequested)
throw new OperationCanceledException();
imageIndex++;
lblCurrentRecord.Text = $"正在迁移记录 {record.Id} - 图片 {imageIndex}/{totalImages}...";
try
{
// 1. 下载原图片
var (downloadSuccess, imageData, downloadMessage) =
await _apiService.DownloadImageAsync(image.Url, cancellationToken);
if (!downloadSuccess || imageData == null)
{
System.Diagnostics.Debug.WriteLine($"下载图片失败: {image.Url} - {downloadMessage}");
continue; // 跳过失败的图片,继续处理其他图片
}
// 2. 生成 COS 路径
var recordTime = record.RecordTime ?? DateTime.Now;
var fileName = CosService.GenerateFileName(".jpg");
var cosKey = CosService.GenerateCosKey(recordTime, "当日照片", "", fileName);
// 3. 上传到 COS
var (uploadSuccess, uploadMessage, cosUrl) =
await _cosService.UploadBytesAsync(imageData, cosKey, cancellationToken: cancellationToken);
if (!uploadSuccess || string.IsNullOrEmpty(cosUrl))
{
System.Diagnostics.Debug.WriteLine($"上传图片失败: {uploadMessage}");
continue; // 跳过失败的图片,继续处理其他图片
}
// 4. 记录 URL 映射
urlPairs.Add(new MigrationUrlPair
{
OldUrl = image.Url,
NewUrl = cosUrl
});
}
catch (OperationCanceledException)
{
throw;
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"处理图片异常: {image.Url} - {ex.Message}");
continue; // 跳过失败的图片,继续处理其他图片
}
}
// 5. 调用 API 更新 URL
if (urlPairs.Count > 0)
{
var updateRequest = new MigrationUpdateRequest
{
RecordId = record.Id,
ImageUrls = urlPairs
};
var (updateSuccess, updateMessage) = await _apiService.UpdateMigrationUrlsAsync(updateRequest);
if (!updateSuccess)
{
System.Diagnostics.Debug.WriteLine($"更新 URL 失败: {updateMessage}");
return false;
}
}
// 如果所有图片都成功迁移,返回成功
return urlPairs.Count == imagesToMigrate.Count;
}
///
/// 更新行状态
///
private void UpdateRowStatus(int recordId, string status, Color color)
{
foreach (DataGridViewRow row in dgvRecords.Rows)
{
if (row.Cells[1].Value?.ToString() == recordId.ToString())
{
row.Cells[6].Value = status;
row.Cells[6].Style.ForeColor = color;
break;
}
}
}
#endregion
#region UI 辅助方法
///
/// 设置查询控件启用状态
///
private void SetQueryControlsEnabled(bool enabled)
{
dtpStartDate.Enabled = enabled;
dtpEndDate.Enabled = enabled;
cboStatus.Enabled = enabled;
btnQuery.Enabled = enabled;
}
///
/// 设置迁移状态
///
private void SetMigratingState(bool migrating)
{
SetQueryControlsEnabled(!migrating);
dgvRecords.Enabled = !migrating;
chkSelectAll.Enabled = !migrating;
if (migrating)
{
btnMigrate.Text = "取消迁移";
btnMigrate.BackColor = Color.FromArgb(220, 53, 69);
}
else
{
btnMigrate.Text = "开始迁移";
btnMigrate.BackColor = Color.FromArgb(40, 167, 69);
progressBar.Value = 0;
lblProgress.Text = "";
}
}
///
/// 更新迁移按钮状态
///
private void UpdateMigrateButtonState()
{
btnMigrate.Enabled = GetSelectedRecordIds().Count > 0 || _isMigrating;
}
///
/// 显示错误信息
///
private void ShowError(string message)
{
lblStatusBar.Text = message;
lblStatusBar.ForeColor = Color.Red;
}
///
/// 截断内容
///
private static string TruncateContent(string content, int maxLength)
{
if (string.IsNullOrEmpty(content)) return "";
return content.Length <= maxLength ? content : content[..maxLength] + "...";
}
///
/// 窗体关闭事件
///
private void MigrationForm_FormClosing(object sender, FormClosingEventArgs e)
{
if (_isMigrating)
{
var result = MessageBox.Show(
"正在迁移中,确定要取消并退出吗?",
"确认",
MessageBoxButtons.YesNo,
MessageBoxIcon.Question);
if (result == DialogResult.No)
{
e.Cancel = true;
return;
}
_migrationCts?.Cancel();
}
// 释放 COS 服务
_cosService.Dispose();
}
#endregion
}
}