chore: 首次簽入 Thinkyu ASP.NET 專案
- 加入 Visual Studio / ASP.NET .gitignore - 排除建置輸出、IDE 設定、NuGet packages、大型 MSI 安裝檔
This commit is contained in:
@@ -0,0 +1,782 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
using System.Web.Script.Services;
|
||||
using System.Web.Services;
|
||||
using System.Data;
|
||||
using System.Data.Common;
|
||||
using System.IO;
|
||||
using WebLib;
|
||||
using Newtonsoft.Json;
|
||||
using NPOI.SS.UserModel;
|
||||
using NPOI.XSSF.UserModel;
|
||||
|
||||
namespace Education.Services
|
||||
{
|
||||
[WebService(Namespace = "http://tempuri.org/")]
|
||||
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
|
||||
[System.ComponentModel.ToolboxItem(false)]
|
||||
[ScriptService]
|
||||
public class 火車票價維護 : ServiceBase
|
||||
{
|
||||
[WebMethod(EnableSession = true)]
|
||||
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
|
||||
public ServiceResponse GetVersions()
|
||||
{
|
||||
try
|
||||
{
|
||||
DAC_火車票價 dac = new DAC_火車票價();
|
||||
var versions = dac.查詢版本號List();
|
||||
return ServiceResponse.CreateSuccess("取得版本清單成功", versions);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return ServiceResponse.CreateError($"取得版本清單失敗:{ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
[WebMethod(EnableSession = true)]
|
||||
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
|
||||
public ServiceResponse GetFareTable(int versionId)
|
||||
{
|
||||
try
|
||||
{
|
||||
DAC_火車票價 dac = new DAC_火車票價();
|
||||
var data = dac.查詢票價表(versionId);
|
||||
|
||||
if (data.Count == 0)
|
||||
{
|
||||
return ServiceResponse.CreateSuccess("無資料", new
|
||||
{
|
||||
originStations = new List<string>(),
|
||||
destStations = new List<string>(),
|
||||
fares = new Dictionary<string, int>()
|
||||
});
|
||||
}
|
||||
|
||||
// 取得所有起站和訖站
|
||||
var originStations = data.OrderBy(x => x.起站站碼).Select(x => x.起站).Distinct().ToList();
|
||||
var destStations = data.OrderBy(x => x.迄站站碼).Select(x => x.迄站).Distinct().ToList();
|
||||
|
||||
// 起訖站由北至南排序
|
||||
|
||||
|
||||
|
||||
|
||||
// 建立票價字典
|
||||
var fares = new Dictionary<string, int>();
|
||||
foreach (var item in data)
|
||||
{
|
||||
var key = $"{item.起站}_{item.迄站}";
|
||||
fares[key] = item.票價;
|
||||
}
|
||||
|
||||
return ServiceResponse.CreateSuccess("取得票價表成功", new
|
||||
{
|
||||
originStations = originStations,
|
||||
destStations = destStations,
|
||||
fares = fares
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return ServiceResponse.CreateError($"取得票價表失敗:{ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
[WebMethod(EnableSession = true)]
|
||||
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
|
||||
public ServiceResponse SaveFareTable(int versionId, string fares)
|
||||
{
|
||||
try
|
||||
{
|
||||
Dictionary<string, int> faresDict = JsonConvert.DeserializeObject<Dictionary<string, int>>(fares);
|
||||
|
||||
using (var ts = DAC.NewTransactionScope())
|
||||
{
|
||||
using (var conn = DAC.NewConnection())
|
||||
{
|
||||
DAC_火車票價 dac = new DAC_火車票價(conn);
|
||||
|
||||
// 刪除原有的版本資料
|
||||
var existingData = dac.Select(noInputReturnAll: true, 版本號: versionId, 起站: null, 迄站: null);
|
||||
|
||||
// 保存原有的生效日期(應該所有同版本的記錄生效日期都一樣)
|
||||
DateTime effectiveDate = existingData.Count > 0 ? existingData[0].生效日期 : DateTime.Today;
|
||||
|
||||
foreach (var item in existingData)
|
||||
{
|
||||
dac.DeleteOne(item.編號, item.DB_APPNO);
|
||||
}
|
||||
|
||||
// 新增更新後的資料,使用保存的生效日期
|
||||
foreach (var kvp in faresDict)
|
||||
{
|
||||
var parts = kvp.Key.Split('_');
|
||||
if (parts.Length != 2) continue;
|
||||
|
||||
var origin = parts[0];
|
||||
var destination = parts[1];
|
||||
|
||||
if (origin == destination) continue; // 跳過同站
|
||||
|
||||
var newItem = new 火車票價Item
|
||||
{
|
||||
版本號 = versionId,
|
||||
生效日期 = effectiveDate,
|
||||
起站 = origin,
|
||||
迄站 = destination,
|
||||
票價 = kvp.Value
|
||||
};
|
||||
|
||||
dac.InsertOne(newItem);
|
||||
}
|
||||
|
||||
ts.Complete();
|
||||
}
|
||||
}
|
||||
|
||||
return ServiceResponse.CreateSuccess("儲存成功");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return ServiceResponse.CreateError($"儲存失敗:{ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
[WebMethod(EnableSession = true)]
|
||||
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
|
||||
public ServiceResponse CreateNewVersion(int versionId)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 只保留年月日,不要時間
|
||||
DateTime effectiveDate = PublicVariable.TaiwanNow.Date;
|
||||
|
||||
using (var ts = DAC.NewTransactionScope())
|
||||
{
|
||||
using (var conn = DAC.NewConnection())
|
||||
{
|
||||
DAC_火車票價 dac = new DAC_火車票價(conn);
|
||||
|
||||
// 檢查版本是否已存在
|
||||
var existingData = dac.Select(noInputReturnAll: true, 版本號: versionId, 起站: null, 迄站: null);
|
||||
if (existingData.Count > 0)
|
||||
{
|
||||
return ServiceResponse.CreateError($"版本 {versionId} 已存在");
|
||||
}
|
||||
|
||||
// 建立預設車站清單(使用現有最新版本的車站)
|
||||
var latestVersion = dac.SelectAll().GroupBy(x => x.版本號).OrderByDescending(x => x.Key).FirstOrDefault();
|
||||
if (latestVersion != null)
|
||||
{
|
||||
// 複製最新版本的結構(但票價為零)
|
||||
var stations = latestVersion.Select(x => x.起站).Distinct().ToList();
|
||||
foreach (var origin in stations)
|
||||
{
|
||||
foreach (var dest in stations)
|
||||
{
|
||||
if (origin != dest)
|
||||
{
|
||||
var newItem = new 火車票價Item
|
||||
{
|
||||
版本號 = versionId,
|
||||
生效日期 = effectiveDate,
|
||||
起站 = origin,
|
||||
迄站 = dest,
|
||||
票價 = 0
|
||||
};
|
||||
dac.InsertOne(newItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ts.Complete();
|
||||
}
|
||||
}
|
||||
|
||||
return ServiceResponse.CreateSuccess("新版本建立成功");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return ServiceResponse.CreateError($"建立新版本失敗:{ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
[WebMethod(EnableSession = true)]
|
||||
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
|
||||
public ServiceResponse DeleteVersion(int versionId)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var ts = DAC.NewTransactionScope())
|
||||
{
|
||||
using (var conn = DAC.NewConnection())
|
||||
{
|
||||
DAC_火車票價 dac = new DAC_火車票價(conn);
|
||||
|
||||
// 刪除該版本的所有資料
|
||||
var existingData = dac.Select(noInputReturnAll: true, 版本號: versionId, 起站: null, 迄站: null);
|
||||
foreach (var item in existingData)
|
||||
{
|
||||
dac.DeleteOne(item.編號, item.DB_APPNO);
|
||||
}
|
||||
|
||||
ts.Complete();
|
||||
}
|
||||
}
|
||||
|
||||
return ServiceResponse.CreateSuccess("版本刪除成功");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return ServiceResponse.CreateError($"版本刪除失敗:{ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
[WebMethod(EnableSession = true)]
|
||||
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
|
||||
public ServiceResponse ExportFareData(int versionId)
|
||||
{
|
||||
try
|
||||
{
|
||||
DAC_火車票價 dac = new DAC_火車票價();
|
||||
var data = dac.Select(noInputReturnAll: true, 版本號: versionId, 起站: null, 迄站: null);
|
||||
|
||||
List<string> originStations;
|
||||
List<string> destStations;
|
||||
var fares = new Dictionary<string, int>();
|
||||
|
||||
if (data.Count == 0)
|
||||
{
|
||||
// 指定版本沒有資料,嘗試從其他版本取得車站清單
|
||||
var allData = dac.SelectAll();
|
||||
if (allData.Count == 0)
|
||||
{
|
||||
// 完全沒有任何資料,使用預設車站清單
|
||||
originStations = GetDefaultOriginStations();
|
||||
destStations = GetDefaultDestStations();
|
||||
}
|
||||
else
|
||||
{
|
||||
// 使用現有版本的車站清單(會包含所有現有版本出現過的車站)
|
||||
originStations = allData.Select(x => x.起站).Distinct().ToList();
|
||||
destStations = allData.Select(x => x.迄站).Distinct().ToList();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 取得該版本的起站和訖站
|
||||
originStations = data.Select(x => x.起站).Distinct().ToList();
|
||||
destStations = data.Select(x => x.迄站).Distinct().ToList();
|
||||
|
||||
// 建立票價字典
|
||||
foreach (var item in data)
|
||||
{
|
||||
var key = $"{item.起站}_{item.迄站}";
|
||||
fares[key] = item.票價;
|
||||
}
|
||||
}
|
||||
|
||||
// 建立 Excel 檔案
|
||||
var excelData = CreateFareExcel(originStations, destStations, fares, versionId);
|
||||
|
||||
var response = new
|
||||
{
|
||||
Data = new
|
||||
{
|
||||
originStations = originStations,
|
||||
destStations = destStations,
|
||||
fares = fares
|
||||
},
|
||||
ExcelData = excelData,
|
||||
Success = true,
|
||||
Message = "匯出成功"
|
||||
};
|
||||
|
||||
return ServiceResponse.CreateSuccess("匯出成功", response);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return ServiceResponse.CreateError($"匯出失敗:{ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
[WebMethod(EnableSession = true)]
|
||||
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
|
||||
public ServiceResponse GetExcelTemplate()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 建立預設車站清單
|
||||
var originStations = GetDefaultOriginStations();
|
||||
var destStations = GetDefaultDestStations();
|
||||
|
||||
// 建立空的 Excel 範本
|
||||
var excelData = CreateFareExcel(originStations, destStations, new Dictionary<string, int>(), 0, isTemplate: true);
|
||||
|
||||
return ServiceResponse.CreateSuccess("取得範本成功", excelData);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return ServiceResponse.CreateError($"取得範本失敗:{ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private List<string> GetDefaultOriginStations()
|
||||
{
|
||||
return new List<string>
|
||||
{
|
||||
"台北", "桃園", "中壢", "埔心", "楊梅", "竹北", "新竹", "竹南", "苗栗",
|
||||
"花蓮", "豐原", "清水", "台中"
|
||||
};
|
||||
}
|
||||
|
||||
private List<string> GetDefaultDestStations()
|
||||
{
|
||||
return new List<string>
|
||||
{
|
||||
"基隆", "八堵", "七堵", "汐止", "南港", "松山", "台北", "板橋", "樹林",
|
||||
"鶯歌", "桃園", "中壢", "楊梅", "竹北", "新竹", "竹東", "竹南", "苗栗",
|
||||
"豐原", "大甲", "清水", "台中", "沙鹿", "彰化", "員林", "田中", "二水",
|
||||
"斗六", "斗南", "嘉義", "新營", "善化", "台南", "岡山", "高雄", "屏東"
|
||||
};
|
||||
}
|
||||
|
||||
private string CreateFareExcel(List<string> originStations, List<string> destStations,
|
||||
Dictionary<string, int> fares, int versionId, bool isTemplate = false)
|
||||
{
|
||||
XSSFWorkbook workbook = new XSSFWorkbook();
|
||||
try
|
||||
{
|
||||
var sheet = workbook.CreateSheet("火車票價");
|
||||
|
||||
// 設定列寬
|
||||
sheet.SetColumnWidth(0, 3500);
|
||||
for (int i = 1; i <= originStations.Count; i++)
|
||||
{
|
||||
sheet.SetColumnWidth(i, 2500);
|
||||
}
|
||||
|
||||
// 設定標題列(橫軸為起站)
|
||||
var headerRow = sheet.CreateRow(0);
|
||||
headerRow.CreateCell(0).SetCellValue("訖站\\起站");
|
||||
for (int i = 0; i < originStations.Count; i++)
|
||||
{
|
||||
headerRow.CreateCell(i + 1).SetCellValue(originStations[i]);
|
||||
}
|
||||
|
||||
// 設定資料列(縱軸為訖站)
|
||||
for (int i = 0; i < destStations.Count; i++)
|
||||
{
|
||||
var row = sheet.CreateRow(i + 1);
|
||||
row.CreateCell(0).SetCellValue(destStations[i]);
|
||||
|
||||
for (int j = 0; j < originStations.Count; j++)
|
||||
{
|
||||
var cell = row.CreateCell(j + 1);
|
||||
|
||||
if (originStations[j] == destStations[i])
|
||||
{
|
||||
// 同站,不填票價
|
||||
cell.SetCellValue("");
|
||||
}
|
||||
else
|
||||
{
|
||||
var key = $"{originStations[j]}_{destStations[i]}";
|
||||
if (fares.ContainsKey(key))
|
||||
{
|
||||
cell.SetCellValue(fares[key]);
|
||||
}
|
||||
else if (!isTemplate)
|
||||
{
|
||||
cell.SetCellValue("");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 轉換為 Base64
|
||||
using (var ms = new MemoryStream())
|
||||
{
|
||||
workbook.Write(ms);
|
||||
return Convert.ToBase64String(ms.ToArray());
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
workbook.Close();
|
||||
}
|
||||
}
|
||||
|
||||
[WebMethod(EnableSession = true)]
|
||||
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
|
||||
public ServiceResponse ImportFareData(string filePath)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 驗證檔案路徑
|
||||
if (string.IsNullOrEmpty(filePath) || !File.Exists(filePath))
|
||||
{
|
||||
return ServiceResponse.CreateError("檔案不存在或已過期");
|
||||
}
|
||||
|
||||
// 在迴圈外取得當前日期,只保留年月日
|
||||
DateTime effectiveDate = PublicVariable.TaiwanNow.Date;
|
||||
|
||||
// 從檔案讀取並驗證資料
|
||||
var fares = new Dictionary<string, int>();
|
||||
var originStations = new List<string>();
|
||||
var destStations = new List<string>();
|
||||
|
||||
using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read))
|
||||
{
|
||||
IWorkbook workbook = null;
|
||||
|
||||
try
|
||||
{
|
||||
fileStream.Position = 0;
|
||||
workbook = new XSSFWorkbook(fileStream);
|
||||
}
|
||||
catch
|
||||
{
|
||||
DeleteUploadedFile(filePath);
|
||||
return ServiceResponse.CreateError("檔案格式錯誤");
|
||||
}
|
||||
|
||||
if (workbook == null || workbook.NumberOfSheets == 0)
|
||||
{
|
||||
DeleteUploadedFile(filePath);
|
||||
return ServiceResponse.CreateError("Excel 檔案格式錯誤");
|
||||
}
|
||||
|
||||
var sheet = workbook.GetSheetAt(0);
|
||||
if (sheet == null || sheet.PhysicalNumberOfRows == 0)
|
||||
{
|
||||
DeleteUploadedFile(filePath);
|
||||
return ServiceResponse.CreateError("Excel 工作表為空");
|
||||
}
|
||||
|
||||
// 讀取標題列
|
||||
var headerRow = sheet.GetRow(0);
|
||||
if (headerRow == null)
|
||||
{
|
||||
DeleteUploadedFile(filePath);
|
||||
return ServiceResponse.CreateError("Excel 缺少標題列");
|
||||
}
|
||||
|
||||
// 取得起站列表(橫軸為起站,從第二欄開始)
|
||||
for (int i = 1; i < headerRow.PhysicalNumberOfCells; i++)
|
||||
{
|
||||
var cell = headerRow.GetCell(i);
|
||||
if (cell != null && !string.IsNullOrEmpty(cell.StringCellValue))
|
||||
{
|
||||
originStations.Add(cell.StringCellValue);
|
||||
}
|
||||
}
|
||||
|
||||
// 讀取資料列(縱軸為訖站,第一欄為訖站)
|
||||
for (int rowIdx = 1; rowIdx <= sheet.LastRowNum; rowIdx++)
|
||||
{
|
||||
var row = sheet.GetRow(rowIdx);
|
||||
if (row == null) continue;
|
||||
|
||||
// 第一欄是訖站
|
||||
var destCell = row.GetCell(0);
|
||||
if (destCell == null || string.IsNullOrEmpty(destCell.StringCellValue))
|
||||
continue;
|
||||
|
||||
string dest = destCell.StringCellValue;
|
||||
destStations.Add(dest);
|
||||
|
||||
// 從第二欄開始是票價,對應起站(橫軸)
|
||||
for (int colIdx = 1; colIdx <= originStations.Count; colIdx++)
|
||||
{
|
||||
var fareCell = row.GetCell(colIdx);
|
||||
string origin = originStations[colIdx - 1];
|
||||
|
||||
if (origin == dest)
|
||||
continue; // 跳過同站
|
||||
|
||||
if (fareCell != null && fareCell.CellType != CellType.Blank)
|
||||
{
|
||||
try
|
||||
{
|
||||
int fare = (int)fareCell.NumericCellValue;
|
||||
if (fare >= 0)
|
||||
{
|
||||
var key = $"{origin}_{dest}";
|
||||
fares[key] = fare;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// 忽略格式錯誤的單元格
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 執行匯入 - 只有新增模式,自動產生新版本號
|
||||
using (var ts = DAC.NewTransactionScope())
|
||||
{
|
||||
using (var conn = DAC.NewConnection())
|
||||
{
|
||||
DAC_火車票價 dac = new DAC_火車票價(conn);
|
||||
|
||||
// 取得下一個版本號
|
||||
int targetVersionId = 1;
|
||||
var allVersions = dac.SelectAll();
|
||||
if (allVersions.Count > 0)
|
||||
{
|
||||
targetVersionId = allVersions.Max(x => x.版本號) + 1;
|
||||
}
|
||||
|
||||
// 新增資料(使用迴圈外取得的 effectiveDate,只保留年月日)
|
||||
foreach (var kvp in fares)
|
||||
{
|
||||
var parts = kvp.Key.Split('_');
|
||||
if (parts.Length != 2) continue;
|
||||
|
||||
var origin = parts[0];
|
||||
var destination = parts[1];
|
||||
|
||||
if (origin == destination) continue; // 跳過同站
|
||||
|
||||
var newItem = new 火車票價Item
|
||||
{
|
||||
版本號 = targetVersionId,
|
||||
生效日期 = effectiveDate, // 使用迴圈外的年月日
|
||||
起站 = origin,
|
||||
迄站 = destination,
|
||||
票價 = kvp.Value
|
||||
};
|
||||
|
||||
dac.InsertOne(newItem);
|
||||
}
|
||||
|
||||
ts.Complete();
|
||||
}
|
||||
}
|
||||
|
||||
// 匯入成功後刪除上傳的臨時檔案
|
||||
DeleteUploadedFile(filePath);
|
||||
|
||||
return ServiceResponse.CreateSuccess("匯入成功");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// 匯入失敗也刪除上傳的檔案
|
||||
if (!string.IsNullOrEmpty(filePath))
|
||||
{
|
||||
DeleteUploadedFile(filePath);
|
||||
}
|
||||
return ServiceResponse.CreateError($"匯入失敗:{ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
[WebMethod(EnableSession = true)]
|
||||
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
|
||||
public ServiceResponse ValidateImportFile(string filePath)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrEmpty(filePath) || !File.Exists(filePath))
|
||||
{
|
||||
return ServiceResponse.CreateError("檔案不存在");
|
||||
}
|
||||
|
||||
var fares = new Dictionary<string, int>();
|
||||
var originStations = new List<string>();
|
||||
var destStations = new List<string>();
|
||||
var errors = new List<string>();
|
||||
int rowCount = 0;
|
||||
|
||||
using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read))
|
||||
{
|
||||
IWorkbook workbook = null;
|
||||
|
||||
// 嘗試讀取為 XLSX
|
||||
try
|
||||
{
|
||||
fileStream.Position = 0;
|
||||
workbook = new XSSFWorkbook(fileStream);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// 檔案格式錯誤,刪除上傳的檔案
|
||||
DeleteUploadedFile(filePath);
|
||||
return ServiceResponse.CreateError("只支援 .xlsx 格式的 Excel 檔案");
|
||||
}
|
||||
|
||||
if (workbook == null || workbook.NumberOfSheets == 0)
|
||||
{
|
||||
// 檔案格式錯誤,刪除上傳的檔案
|
||||
DeleteUploadedFile(filePath);
|
||||
return ServiceResponse.CreateError("Excel 檔案格式錯誤");
|
||||
}
|
||||
|
||||
var sheet = workbook.GetSheetAt(0);
|
||||
if (sheet == null || sheet.PhysicalNumberOfRows == 0)
|
||||
{
|
||||
// 工作表為空,刪除上傳的檔案
|
||||
DeleteUploadedFile(filePath);
|
||||
return ServiceResponse.CreateError("Excel 工作表為空");
|
||||
}
|
||||
|
||||
// 讀取標題列
|
||||
var headerRow = sheet.GetRow(0);
|
||||
if (headerRow == null)
|
||||
{
|
||||
// 標題列缺失,刪除上傳的檔案
|
||||
DeleteUploadedFile(filePath);
|
||||
return ServiceResponse.CreateError("Excel 缺少標題列");
|
||||
}
|
||||
|
||||
// 取得起站列表(橫軸為起站,從第二欄開始)
|
||||
for (int i = 1; i < headerRow.PhysicalNumberOfCells; i++)
|
||||
{
|
||||
var cell = headerRow.GetCell(i);
|
||||
if (cell != null && !string.IsNullOrEmpty(cell.StringCellValue))
|
||||
{
|
||||
originStations.Add(cell.StringCellValue);
|
||||
}
|
||||
}
|
||||
|
||||
// 讀取資料列(縱軸為訖站,第一欄為訖站)
|
||||
for (int rowIdx = 1; rowIdx <= sheet.LastRowNum; rowIdx++)
|
||||
{
|
||||
var row = sheet.GetRow(rowIdx);
|
||||
if (row == null) continue;
|
||||
|
||||
// 第一欄是訖站
|
||||
var destCell = row.GetCell(0);
|
||||
if (destCell == null || string.IsNullOrEmpty(destCell.StringCellValue))
|
||||
continue;
|
||||
|
||||
string dest = destCell.StringCellValue;
|
||||
destStations.Add(dest);
|
||||
|
||||
// 從第二欄開始是票價,對應起站(橫軸)
|
||||
for (int colIdx = 1; colIdx <= originStations.Count; colIdx++)
|
||||
{
|
||||
var fareCell = row.GetCell(colIdx);
|
||||
string origin = originStations[colIdx - 1];
|
||||
|
||||
if (origin == dest)
|
||||
continue; // 跳過同站
|
||||
|
||||
if (fareCell != null && fareCell.CellType != CellType.Blank)
|
||||
{
|
||||
try
|
||||
{
|
||||
int fare = (int)fareCell.NumericCellValue;
|
||||
if (fare < 0)
|
||||
{
|
||||
errors.Add($"第 {rowIdx + 1} 列,{origin} 到 {dest} 的票價不能為負數");
|
||||
}
|
||||
else
|
||||
{
|
||||
var key = $"{origin}_{dest}";
|
||||
fares[key] = fare;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
errors.Add($"第 {rowIdx + 1} 列,{origin} 到 {dest} 的票價格式不正確");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rowCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if (errors.Count > 0)
|
||||
{
|
||||
// 驗證失敗,刪除上傳的檔案
|
||||
DeleteUploadedFile(filePath);
|
||||
return ServiceResponse.CreateSuccess("檔案驗證失敗", new
|
||||
{
|
||||
Errors = errors,
|
||||
RowCount = 0
|
||||
});
|
||||
}
|
||||
|
||||
return ServiceResponse.CreateSuccess("檔案驗證成功", new
|
||||
{
|
||||
Fares = fares,
|
||||
OriginStations = originStations.Distinct().ToList(),
|
||||
DestStations = destStations.Distinct().ToList(),
|
||||
Errors = new List<string>()
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// 驗證異常,刪除上傳的檔案
|
||||
if (!string.IsNullOrEmpty(filePath))
|
||||
{
|
||||
DeleteUploadedFile(filePath);
|
||||
}
|
||||
return ServiceResponse.CreateError($"驗證檔案失敗:{ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void DeleteUploadedFile(string filePath)
|
||||
{
|
||||
if (string.IsNullOrEmpty(filePath))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// 刪除指定的檔案
|
||||
if (File.Exists(filePath))
|
||||
{
|
||||
File.Delete(filePath);
|
||||
}
|
||||
|
||||
// 清理所有 TrainFareImport_*.xlsx 格式的檔案
|
||||
string uploadDir = Path.Combine(HttpContext.Current.Server.MapPath("~/"), "DocUpload");
|
||||
if (Directory.Exists(uploadDir))
|
||||
{
|
||||
try
|
||||
{
|
||||
var searchPattern = "TrainFareImport_*.xlsx";
|
||||
var oldFiles = Directory.GetFiles(uploadDir, searchPattern);
|
||||
|
||||
foreach (var oldFile in oldFiles)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 刪除30分鐘以前的檔案
|
||||
var fileInfo = new FileInfo(oldFile);
|
||||
if (DateTime.Now - fileInfo.LastWriteTime > TimeSpan.FromMinutes(30))
|
||||
{
|
||||
File.Delete(oldFile);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"清理舊檔案失敗:{oldFile} - {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"掃描清理檔案失敗:{ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// 記錄刪除失敗但不拋出例外
|
||||
System.Diagnostics.Debug.WriteLine($"刪除上傳檔案失敗:{filePath} - {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user