247 lines
7.8 KiB
C#
247 lines
7.8 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.Data;
|
||
using System.Data.SqlClient;
|
||
using System.Linq;
|
||
using System.Web.Http;
|
||
using System.Configuration;
|
||
|
||
/// <summary>
|
||
/// pivot01Controller - 法會報名統計分析 API
|
||
/// 設計理念:直接查詢 SQL VIEW,保持彈性與簡潔
|
||
/// </summary>
|
||
[ezAuthorize]
|
||
public class pivot01Controller : ApiController
|
||
{
|
||
// 連線字串
|
||
private readonly string _connectionString;
|
||
|
||
public pivot01Controller()
|
||
{
|
||
// 優先使用 shopConn 連線字串(純 SQL Server 連線字串)
|
||
var shopConnectionString = ConfigurationManager.ConnectionStrings["shopConn"]?.ConnectionString;
|
||
if (!string.IsNullOrEmpty(shopConnectionString))
|
||
{
|
||
// 移除不相容的 Provider 參數
|
||
_connectionString = shopConnectionString.Replace("Provider=SQLOLEDB;", "");
|
||
}
|
||
else
|
||
{
|
||
// 備用方案:從 Entity Framework 連線字串中提取 SQL Server 連線字串
|
||
var efConnectionString = ConfigurationManager.ConnectionStrings["ezEntities"]?.ConnectionString;
|
||
if (!string.IsNullOrEmpty(efConnectionString))
|
||
{
|
||
// 解析 EF 連線字串,提取 provider connection string 部分
|
||
var startIndex = efConnectionString.IndexOf("provider connection string="") + "provider connection string="".Length;
|
||
var endIndex = efConnectionString.LastIndexOf(""");
|
||
if (startIndex > 0 && endIndex > startIndex)
|
||
{
|
||
_connectionString = efConnectionString.Substring(startIndex, endIndex - startIndex);
|
||
}
|
||
else
|
||
{
|
||
throw new InvalidOperationException("無法解析 Entity Framework 連線字串");
|
||
}
|
||
}
|
||
else
|
||
{
|
||
throw new InvalidOperationException("找不到可用的資料庫連線字串");
|
||
}
|
||
}
|
||
}
|
||
|
||
#region 通用 VIEW 查詢方法
|
||
|
||
/// <summary>
|
||
/// 執行 SQL 查詢並回傳 DataTable
|
||
/// </summary>
|
||
/// <param name="sql">SQL 查詢語句</param>
|
||
/// <param name="parameters">參數陣列</param>
|
||
/// <returns>查詢結果 DataTable</returns>
|
||
private DataTable ExecuteSqlQuery(string sql, SqlParameter[] parameters = null)
|
||
{
|
||
var dataTable = new DataTable();
|
||
|
||
try
|
||
{
|
||
using (var connection = new SqlConnection(_connectionString))
|
||
using (var command = new SqlCommand(sql, connection))
|
||
{
|
||
// 設定逾時時間為 60 秒
|
||
command.CommandTimeout = 60;
|
||
|
||
// 加入參數
|
||
if (parameters != null && parameters.Length > 0)
|
||
{
|
||
command.Parameters.AddRange(parameters);
|
||
}
|
||
|
||
// 開啟連線並執行查詢
|
||
connection.Open();
|
||
using (var adapter = new SqlDataAdapter(command))
|
||
{
|
||
adapter.Fill(dataTable);
|
||
}
|
||
}
|
||
}
|
||
catch (SqlException ex)
|
||
{
|
||
throw new Exception($"SQL 查詢錯誤: {ex.Message}", ex);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
throw new Exception($"執行查詢時發生錯誤: {ex.Message}", ex);
|
||
}
|
||
|
||
return dataTable;
|
||
}
|
||
|
||
/// <summary>
|
||
/// DataTable 轉換為動態物件列表(保留中文欄位名)
|
||
/// </summary>
|
||
/// <param name="dt">DataTable</param>
|
||
/// <returns>Dictionary 列表</returns>
|
||
private List<Dictionary<string, object>> DataTableToDictionary(DataTable dt)
|
||
{
|
||
var list = new List<Dictionary<string, object>>();
|
||
|
||
foreach (DataRow row in dt.Rows)
|
||
{
|
||
var dict = new Dictionary<string, object>();
|
||
foreach (DataColumn col in dt.Columns)
|
||
{
|
||
// 保留原始欄位名稱(包含中文)
|
||
dict[col.ColumnName] = row[col] == DBNull.Value ? null : row[col];
|
||
}
|
||
list.Add(dict);
|
||
}
|
||
|
||
return list;
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region API 端點
|
||
|
||
/// <summary>
|
||
/// GET /api/pivot01/activity_stats
|
||
/// 查詢法會統計(對應「法會統計」VIEW)
|
||
/// </summary>
|
||
/// <param name="year">查詢年份</param>
|
||
/// <returns>法會統計資料</returns>
|
||
[HttpGet]
|
||
[Route("api/pivot01/activity_stats")]
|
||
public IHttpActionResult GetActivityStats(int year)
|
||
{
|
||
try
|
||
{
|
||
// 驗證年份參數
|
||
if (year < 1900 || year > 2100)
|
||
{
|
||
return BadRequest("年份參數不正確,請輸入 1900 ~ 2100 之間的年份");
|
||
}
|
||
|
||
// 建立 SQL 查詢(包含當年度及無日期的法會)
|
||
string sql = @"
|
||
SELECT * FROM [法會統計]
|
||
WHERE (YEAR(開始日期) = @year OR 開始日期 IS NULL)
|
||
ORDER BY
|
||
CASE WHEN 開始日期 IS NULL THEN 1 ELSE 0 END,
|
||
開始日期 DESC,
|
||
結束日期 DESC
|
||
";
|
||
|
||
// 建立參數
|
||
var parameters = new[]
|
||
{
|
||
new SqlParameter("@year", SqlDbType.Int) { Value = year }
|
||
};
|
||
|
||
// 執行查詢
|
||
var dataTable = ExecuteSqlQuery(sql, parameters);
|
||
var data = DataTableToDictionary(dataTable);
|
||
|
||
// 回應結果
|
||
var result = new
|
||
{
|
||
success = true,
|
||
data = data,
|
||
message = "查詢成功",
|
||
rowCount = data.Count
|
||
};
|
||
|
||
return Ok(result);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
var errorResponse = new
|
||
{
|
||
success = false,
|
||
message = $"查詢失敗:{ex.Message}",
|
||
error = ex.ToString()
|
||
};
|
||
return Content(System.Net.HttpStatusCode.BadRequest, errorResponse);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// GET /api/pivot01/registration_details
|
||
/// 查詢報名明細(對應「報名明細查詢」VIEW)
|
||
/// </summary>
|
||
/// <param name="activityNum">法會編號(必填)</param>
|
||
/// <returns>報名明細資料</returns>
|
||
[HttpGet]
|
||
[Route("api/pivot01/registration_details")]
|
||
public IHttpActionResult GetRegistrationDetails(int? activityNum = null)
|
||
{
|
||
try
|
||
{
|
||
// 驗證參數
|
||
if (!activityNum.HasValue || activityNum.Value <= 0)
|
||
{
|
||
return BadRequest("請提供有效的法會編號(activityNum)");
|
||
}
|
||
|
||
// 建立查詢 SQL
|
||
string sql = @"
|
||
SELECT * FROM [報名明細查詢]
|
||
WHERE 法會ID = @activityNum
|
||
ORDER BY 報名日期 DESC, 報名編號 DESC
|
||
";
|
||
|
||
// 建立參數
|
||
var parameters = new[]
|
||
{
|
||
new SqlParameter("@activityNum", SqlDbType.Int) { Value = activityNum.Value }
|
||
};
|
||
|
||
// 執行查詢
|
||
var dataTable = ExecuteSqlQuery(sql, parameters);
|
||
var data = DataTableToDictionary(dataTable);
|
||
|
||
// 回應結果
|
||
var result = new
|
||
{
|
||
success = true,
|
||
data = data,
|
||
message = "查詢成功",
|
||
rowCount = data.Count
|
||
};
|
||
|
||
return Ok(result);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
var errorResponse = new
|
||
{
|
||
success = false,
|
||
message = $"查詢失敗:{ex.Message}",
|
||
error = ex.ToString()
|
||
};
|
||
return Content(System.Net.HttpStatusCode.BadRequest, errorResponse);
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
}
|