using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using PagedList;
using Newtonsoft.Json;
using System.Collections;
using System.Data.Entity;
// api/pivot
[ezAuthorize]
public class pivotController : ApiController
{
private Model.ezEntities _db = new Model.ezEntities();
#region 報表查詢 API
///
/// 報名明細查詢 - 對應 Excel 中的視圖結構
/// GET api/pivot/registration_details
///
/// 開始日期
/// 結束日期
/// 法會編號
/// 信眾編號
/// 頁碼
/// 每頁筆數
///
[HttpGet]
[Route("api/pivot/registration_details")]
public IHttpActionResult GetRegistrationDetails(string startDate = null, string endDate = null,
int? activityNum = null, int? followerNum = null, int page = 1, int pageSize = 1000)
{
try
{
// 對應 README.md 中的視圖查詢結構,按顏色分類欄位
var query = from pod in _db.pro_order_detail
join po in _db.pro_order on pod.order_no equals po.order_no
join ai in _db.actItems on pod.actItem_num equals ai.num
join act in _db.activities on po.activity_num equals act.num
join f in _db.followers on po.f_num equals f.num
join aik in _db.actItem_kind on ai.kind equals aik.num
select new
{
// 🟠 橘色欄位 - 法會基礎資料 (法會相關的直接來源)
法會ID = act.num,
法會名稱 = act.subject,
開始日期 = act.startDate_solar,
結束日期 = act.endDate_solar,
// 🔵 藍色欄位 - 信眾基礎資料 (信眾相關的直接來源)
信眾編號 = f.f_number,
信眾姓名 = f.u_name,
報名編號 = po.order_no,
報名日期 = po.up_time,
// 🟢綠色欄位 - 功德資訊欄位 (功德相關資訊)
功德主 = pod.parent_num != null ? "是" : "否",
功德類型 = aik.kind,
功德名稱 = ai.subject,
// 🟣 紫色欄位 - 計算欄位 (需要計算)
數量 = pod.qty,
金額 = pod.price,
已收 = 0, // 業務邏輯:固定為 0
未收 = pod.price * pod.qty - 0, // 計算未收金額
// 額外提供原始資料供進階計算
_metadata = new
{
parent_num = pod.parent_num,
unit_price = pod.price,
quantity = pod.qty,
total_amount = pod.price * pod.qty,
kind_num = aik.num,
item_num = ai.num,
activity_num = act.num,
follower_num = f.num
}
};
// 日期篩選
if (!string.IsNullOrEmpty(startDate) && DateTime.TryParse(startDate, out DateTime start))
{
query = query.Where(x => x.開始日期 >= start);
}
if (!string.IsNullOrEmpty(endDate) && DateTime.TryParse(endDate, out DateTime end))
{
query = query.Where(x => x.結束日期 <= end);
}
// 法會篩選
if (activityNum.HasValue)
{
query = query.Where(x => x.法會ID == activityNum.Value);
}
// 信眾篩選
if (followerNum.HasValue)
{
query = query.Where(x => x.信眾編號 == followerNum.ToString());
}
// 分頁處理
var totalCount = query.Count();
var pagedQuery = query.OrderByDescending(x => x.報名日期)
.ThenByDescending(x => x.法會ID)
.Skip((page - 1) * pageSize)
.Take(pageSize)
.ToList();
var result = new
{
success = true,
data = new
{
list = pagedQuery,
total = totalCount,
page = page,
pageSize = pageSize,
totalPages = (int)Math.Ceiling((double)totalCount / pageSize)
},
message = "查詢成功"
};
return Ok(result);
}
catch (Exception ex)
{
return BadRequest($"查詢失敗:{ex.Message}");
}
}
///
/// 報名明細查詢 - Excel 匯出格式
/// GET api/pivot/registration_details_export
///
/// 開始日期
/// 結束日期
/// 法會編號
/// 信眾編號
///
[HttpGet]
[Route("api/pivot/registration_details_export")]
public IHttpActionResult GetRegistrationDetailsForExport(string startDate = null, string endDate = null,
int? activityNum = null, int? followerNum = null)
{
try
{
// 完全對應 Excel 中的欄位結構
var query = from pod in _db.pro_order_detail
join po in _db.pro_order on pod.order_no equals po.order_no
join ai in _db.actItems on pod.actItem_num equals ai.num
join act in _db.activities on po.activity_num equals act.num
join f in _db.followers on po.f_num equals f.num
join aik in _db.actItem_kind on ai.kind equals aik.num
select new
{
// 完全對應 Excel 欄位名稱
法會ID = act.num,
法會名稱 = act.subject,
開始日期 = act.startDate_solar,
結束日期 = act.endDate_solar,
信眾編號 = f.f_number,
信眾姓名 = f.u_name,
報名編號 = po.order_no,
報名日期 = po.up_time,
功德主 = pod.parent_num != null ? "是" : "否",
功德類型 = aik.kind,
功德名稱 = ai.subject,
數量 = pod.qty,
金額 = pod.price,
已收 = 0,
未收 = pod.price * pod.qty
};
// 套用篩選條件
if (!string.IsNullOrEmpty(startDate) && DateTime.TryParse(startDate, out DateTime start))
{
query = query.Where(x => x.開始日期 >= start);
}
if (!string.IsNullOrEmpty(endDate) && DateTime.TryParse(endDate, out DateTime end))
{
query = query.Where(x => x.結束日期 <= end);
}
if (activityNum.HasValue)
{
query = query.Where(x => x.法會ID == activityNum.Value);
}
if (followerNum.HasValue)
{
query = query.Where(x => x.信眾編號 == followerNum.ToString());
}
var exportData = query.OrderByDescending(x => x.報名日期)
.ThenByDescending(x => x.法會ID)
.ToList()
.Select(x => new
{
x.法會ID,
x.法會名稱,
開始日期 = x.開始日期.HasValue ? x.開始日期.Value.ToString("yyyy/MM/dd") : "",
結束日期 = x.結束日期.HasValue ? x.結束日期.Value.ToString("yyyy/MM/dd") : "",
x.信眾編號,
x.信眾姓名,
x.報名編號,
報名日期 = x.報名日期.HasValue ? x.報名日期.Value.ToString("yyyy/MM/dd HH:mm") : "",
x.功德主,
x.功德類型,
x.功德名稱,
x.數量,
x.金額,
x.已收,
x.未收
})
.ToList();
// 統計資訊
var summary = new
{
總筆數 = exportData.Count,
總金額 = exportData.Sum(x => x.金額 * x.數量),
參與法會數 = exportData.Select(x => x.法會ID).Distinct().Count(),
參與信眾數 = exportData.Select(x => x.信眾編號).Distinct().Count(),
功德主數量 = exportData.Count(x => x.功德主 == "是")
};
var result = new
{
success = true,
data = new
{
details = exportData,
summary = summary,
export_time = DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss"),
filters = new
{
startDate,
endDate,
activityNum,
followerNum
}
},
message = "匯出資料準備完成"
};
return Ok(result);
}
catch (Exception ex)
{
return BadRequest($"匯出失敗:{ex.Message}");
}
}
///
/// 取得法會報名統計
/// GET api/pivot/activity_stats
///
/// 開始日期
/// 結束日期
/// 法會編號
///
[HttpGet]
[Route("api/pivot/activity_stats")]
public IHttpActionResult GetActivityStats(string startDate = null, string endDate = null, int? activityNum = null)
{
try
{
var query = from pod in _db.pro_order_detail
join po in _db.pro_order on pod.order_no equals po.order_no
join act in _db.activities on po.activity_num equals act.num
join ai in _db.actItems on pod.actItem_num equals ai.num
join aik in _db.actItem_kind on ai.kind equals aik.num
join f in _db.followers on po.f_num equals f.num
select new
{
ActivityNum = act.num,
ActivityName = act.subject,
StartDate = act.startDate_solar,
EndDate = act.endDate_solar,
FollowerNum = f.num,
FollowerName = f.u_name,
OrderNo = po.order_no,
OrderDate = po.up_time,
KindName = aik.kind,
ItemName = ai.subject,
Qty = pod.qty,
Price = pod.price,
Amount = pod.qty * pod.price
};
// 日期篩選
if (!string.IsNullOrEmpty(startDate) && DateTime.TryParse(startDate, out DateTime start))
{
query = query.Where(x => x.StartDate >= start);
}
if (!string.IsNullOrEmpty(endDate) && DateTime.TryParse(endDate, out DateTime end))
{
query = query.Where(x => x.EndDate <= end);
}
// 法會篩選
if (activityNum.HasValue)
{
query = query.Where(x => x.ActivityNum == activityNum.Value);
}
// 統計資料
var stats = query.GroupBy(x => new { x.ActivityNum, x.ActivityName, x.StartDate, x.EndDate })
.Select(g => new
{
activity_num = g.Key.ActivityNum,
activity_name = g.Key.ActivityName,
start_date = g.Key.StartDate,
end_date = g.Key.EndDate,
total_orders = g.Select(x => x.OrderNo).Distinct().Count(),
total_followers = g.Select(x => x.FollowerNum).Distinct().Count(),
total_amount = g.Sum(x => x.Amount),
total_qty = g.Sum(x => x.Qty),
avg_amount_per_follower = g.GroupBy(x => x.FollowerNum).Average(fg => fg.Sum(x => x.Amount))
})
.OrderByDescending(x => x.start_date)
.ToList();
var result = new
{
success = true,
data = stats,
message = "查詢成功"
};
return Ok(result);
}
catch (Exception ex)
{
return BadRequest($"查詢失敗:{ex.Message}");
}
}
///
/// 取得信眾參與分析
/// GET api/pivot/follower_analysis
///
/// 開始日期
/// 結束日期
/// 信眾編號
///
[HttpGet]
[Route("api/pivot/follower_analysis")]
public IHttpActionResult GetFollowerAnalysis(string startDate = null, string endDate = null, int? followerNum = null)
{
try
{
var query = from pod in _db.pro_order_detail
join po in _db.pro_order on pod.order_no equals po.order_no
join act in _db.activities on po.activity_num equals act.num
join ai in _db.actItems on pod.actItem_num equals ai.num
join aik in _db.actItem_kind on ai.kind equals aik.num
join f in _db.followers on po.f_num equals f.num
select new
{
FollowerNum = f.num,
FollowerName = f.u_name,
FollowerCode = f.f_number,
ActivityNum = act.num,
ActivityName = act.subject,
StartDate = act.startDate_solar,
EndDate = act.endDate_solar,
OrderDate = po.up_time,
KindName = aik.kind,
ItemName = ai.subject,
Amount = pod.qty * pod.price,
HasParent = pod.parent_num != null
};
// 日期篩選
if (!string.IsNullOrEmpty(startDate) && DateTime.TryParse(startDate, out DateTime start))
{
query = query.Where(x => x.StartDate >= start);
}
if (!string.IsNullOrEmpty(endDate) && DateTime.TryParse(endDate, out DateTime end))
{
query = query.Where(x => x.EndDate <= end);
}
// 信眾篩選
if (followerNum.HasValue)
{
query = query.Where(x => x.FollowerNum == followerNum.Value);
}
// 信眾統計分析
var followerStats = query.GroupBy(x => new { x.FollowerNum, x.FollowerName, x.FollowerCode })
.Select(g => new
{
follower_num = g.Key.FollowerNum,
follower_name = g.Key.FollowerName,
follower_code = g.Key.FollowerCode,
total_activities = g.Select(x => x.ActivityNum).Distinct().Count(),
total_amount = g.Sum(x => x.Amount),
total_orders = g.Select(x => x.OrderDate).Count(),
avg_amount_per_activity = g.GroupBy(x => x.ActivityNum).Average(ag => ag.Sum(x => x.Amount)),
is_patron_count = g.Count(x => x.HasParent),
participation_rate = Math.Round((double)g.Select(x => x.ActivityNum).Distinct().Count() / _db.activities.Count() * 100, 2),
favorite_kinds = g.GroupBy(x => x.KindName)
.OrderByDescending(kg => kg.Sum(x => x.Amount))
.Take(3)
.Select(kg => new { kind = kg.Key, amount = kg.Sum(x => x.Amount) })
.ToList(),
recent_activities = g.OrderByDescending(x => x.OrderDate)
.Take(5)
.Select(x => new {
activity_name = x.ActivityName,
order_date = x.OrderDate,
amount = x.Amount
})
.ToList()
})
.OrderByDescending(x => x.total_amount)
.ToList();
var result = new
{
success = true,
data = followerStats,
message = "查詢成功"
};
return Ok(result);
}
catch (Exception ex)
{
return BadRequest($"查詢失敗:{ex.Message}");
}
}
///
/// 取得收入統計
/// GET api/pivot/income_stats
///
/// 開始日期
/// 結束日期
/// 分組方式 (monthly/yearly/activity)
///
[HttpGet]
[Route("api/pivot/income_stats")]
public IHttpActionResult GetIncomeStats(string startDate = null, string endDate = null, string groupBy = "monthly")
{
try
{
var query = from pod in _db.pro_order_detail
join po in _db.pro_order on pod.order_no equals po.order_no
join act in _db.activities on po.activity_num equals act.num
join ai in _db.actItems on pod.actItem_num equals ai.num
join aik in _db.actItem_kind on ai.kind equals aik.num
select new
{
ActivityNum = act.num,
ActivityName = act.subject,
StartDate = act.startDate_solar,
OrderDate = po.up_time,
KindNum = aik.num,
KindName = aik.kind,
ItemName = ai.subject,
Amount = pod.qty * pod.price
};
// 日期篩選
if (!string.IsNullOrEmpty(startDate) && DateTime.TryParse(startDate, out DateTime start))
{
query = query.Where(x => x.StartDate >= start);
}
if (!string.IsNullOrEmpty(endDate) && DateTime.TryParse(endDate, out DateTime end))
{
query = query.Where(x => x.StartDate <= end);
}
object stats = null;
switch (groupBy?.ToLower())
{
case "monthly":
stats = query.GroupBy(x => new {
Year = x.StartDate.Value.Year,
Month = x.StartDate.Value.Month
})
.Select(g => new
{
period = $"{g.Key.Year}/{g.Key.Month:00}",
year = g.Key.Year,
month = g.Key.Month,
total_amount = g.Sum(x => x.Amount),
total_activities = g.Select(x => x.ActivityNum).Distinct().Count(),
kind_breakdown = g.GroupBy(x => x.KindName)
.Select(kg => new {
kind = kg.Key,
amount = kg.Sum(x => x.Amount)
}).ToList()
})
.OrderBy(x => x.year).ThenBy(x => x.month)
.ToList();
break;
case "yearly":
stats = query.GroupBy(x => x.StartDate.Value.Year)
.Select(g => new
{
period = g.Key.ToString(),
year = g.Key,
total_amount = g.Sum(x => x.Amount),
total_activities = g.Select(x => x.ActivityNum).Distinct().Count(),
kind_breakdown = g.GroupBy(x => x.KindName)
.Select(kg => new {
kind = kg.Key,
amount = kg.Sum(x => x.Amount)
}).ToList(),
monthly_trend = g.GroupBy(x => x.StartDate.Value.Month)
.Select(mg => new {
month = mg.Key,
amount = mg.Sum(x => x.Amount)
}).OrderBy(x => x.month).ToList()
})
.OrderBy(x => x.year)
.ToList();
break;
case "activity":
stats = query.GroupBy(x => new { x.ActivityNum, x.ActivityName, x.StartDate })
.Select(g => new
{
activity_num = g.Key.ActivityNum,
activity_name = g.Key.ActivityName,
start_date = g.Key.StartDate,
total_amount = g.Sum(x => x.Amount)
})
.OrderByDescending(x => x.start_date)
.ToList()
.Select(g => new
{
g.activity_num,
g.activity_name,
g.start_date,
g.total_amount,
kind_breakdown = query.Where(x => x.ActivityNum == g.activity_num)
.GroupBy(x => x.KindName)
.Select(kg => new
{
kind = kg.Key,
amount = kg.Sum(x => x.Amount),
percentage = g.total_amount.HasValue && g.total_amount.Value > 0
? Math.Round((double)(kg.Sum(x => x.Amount) ?? 0) / (double)g.total_amount.Value * 100, 2)
: 0
})
.OrderByDescending(x => x.amount)
.ToList()
})
.ToList();
break;
default:
return BadRequest("不支援的分組方式,請使用 monthly, yearly 或 activity");
}
var result = new
{
success = true,
data = stats,
groupBy = groupBy,
message = "查詢成功"
};
return Ok(result);
}
catch (Exception ex)
{
return BadRequest($"查詢失敗:{ex.Message}");
}
}
#endregion
#region 數據分析 API
///
/// 樞紐分析
/// POST api/pivot/pivot_analysis
///
/// 分析請求參數
///
[HttpPost]
[Route("api/pivot/pivot_analysis")]
public IHttpActionResult PostPivotAnalysis(dynamic request)
{
try
{
// TODO: 實作樞紐分析邏輯
var result = new
{
success = true,
data = new
{
rows = new List