Files
thinkyu/報到系統/Services/ClassesService.asmx.cs
sryang 577060bc78 chore: 首次簽入 Thinkyu ASP.NET 專案
- 加入 Visual Studio / ASP.NET .gitignore
- 排除建置輸出、IDE 設定、NuGet packages、大型 MSI 安裝檔
2026-09-10 09:42:37 +08:00

677 lines
30 KiB
C#

using NPOI.SS.UserModel;
using NPOI.Util;
using NPOI.XSSF.UserModel;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web;
using System.Web.Script.Services;
using System.Web.Services;
using WebLib;
namespace 報到系統
{
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
[ScriptService]
public class ClassesService : System.Web.Services.WebService
{
/// <summary>
/// 搜尋活動
/// </summary>
[WebMethod(EnableSession = true)]
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
public BaseResponse SearchClasses(int pageIndex, int pageSize, string courseCode, string courseName, DateTime? courseDate1, DateTime? courseDate2, string courseLocation, bool? closed, string projectID)
{
try
{
// 計算起始索引 (pageIndex 從 1 開始)
int startRowIndex = (pageIndex - 1) * pageSize;
// 判斷是否所有條件都為空
bool noInput = string.IsNullOrEmpty(courseCode) &&
string.IsNullOrEmpty(courseName) &&
!courseDate1.HasValue &&
!courseDate2.HasValue &&
string.IsNullOrEmpty(courseLocation);
using (var conn = DAC.NewConnection())
{
DAC_Classes daoClasses = new DAC_Classes(conn);
//DAC_SignUp daoSignUp = new DAC_SignUp(conn);
DAC_BPAA daoBpaa = new DAC_BPAA(conn);
// 查詢資料
ClassesList result = daoClasses.SelectPage(
noInputReturnAll: true,
startRowIndex: startRowIndex,
maximumRows: pageSize,
CourseCode: courseCode,
CourseName: courseName,
CourseDate_1: courseDate1,
CourseDate_2: courseDate2,
CourseLocation: courseLocation,
Closed: closed,
ProjectID: projectID,
orderBy: "CourseDate DESC, CourseCode ASC"
);
// 取得總筆數
int totalCount = daoClasses.SelectCount(
noInputReturnAll: true,
CourseCode: courseCode,
CourseName: courseName,
CourseDate_1: courseDate1,
CourseDate_2: courseDate2,
CourseLocation: courseLocation,
Closed: closed,
ProjectID: projectID,
orderBy: ""
);
foreach (var item in result)
{
// 填入關聯欄位與統計欄位
item.ProjectName = daoBpaa.SelectProjectName(item.ProjectID);
// TODO: 這些等用戶提出再加上
//var thisCourdeSignUps = daoSignUp.Select(false, item.ID, null, null, null, null);
//item.Actual = thisCourdeSignUps.Count(x => x.IsPresent);
//item.Expected = thisCourdeSignUps.Count(x => !x.IsOnSite);
//item.Absent = thisCourdeSignUps.Count(x => !x.IsOnSite && x.IsPresent);
//item.Exception = thisCourdeSignUps.Count(x => x.IsException);
//item.OnSite = thisCourdeSignUps.Count(x => x.IsOnSite);
}
return new BaseResponse
{
Success = true,
Message = "查詢成功",
Data = result?.ToDict() ?? new ClassesList().ToDict(),
TotalCount = totalCount
};
}
}
catch (Exception ex)
{
return new BaseResponse
{
Success = false,
Message = "查詢活動發生錯誤:" + ex.Message,
Data = new ClassesList(),
TotalCount = 0
};
}
}
/// <summary>
/// 儲存活動 (新增或更正)
/// </summary>
[WebMethod(EnableSession = true)]
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
public BaseResponse SaveClasses(bool isNew, ClassesItem item)
{
try
{
if (item == null)
{
return new BaseResponse
{
Success = false,
Message = "參數不完整"
};
}
// 驗證必填欄位
if (string.IsNullOrEmpty(item.CourseName))
{
return new BaseResponse
{
Success = false,
Message = "課程名稱為必填欄位"
};
}
if (item.CourseName.Length > 100)
{
return new BaseResponse
{
Success = false,
Message = "課程名稱最長 100 個字元"
};
}
if (item.CourseDate == DateTime.MinValue)
{
return new BaseResponse
{
Success = false,
Message = "課程日期為必填欄位"
};
}
if (!string.IsNullOrEmpty(item.NotificationTitle) && item.NotificationTitle.Length > 100)
{
return new BaseResponse
{
Success = false,
Message = "通知標題最長 100 個字元"
};
}
if (!string.IsNullOrEmpty(item.NotificationTemplate) && item.NotificationTemplate.Length > 500)
{
return new BaseResponse
{
Success = false,
Message = "通知模板最長 500 個字元"
};
}
DAC_Classes dac = new DAC_Classes();
if (isNew)
{
// 新增
ClassesItem newItem = dac.InsertOne(item);
return new BaseResponse
{
Success = true,
Message = "新增成功",
Data = newItem
};
}
else
{
// 更正
ClassesItem updatedItem = dac.UpdateOne(item);
return new BaseResponse
{
Success = true,
Message = "更新成功",
Data = updatedItem
};
}
}
catch (Exception ex)
{
return new BaseResponse
{
Success = false,
Message = "儲存活動發生錯誤:" + ex.Message
};
}
}
/// <summary>
/// 刪除活動
/// </summary>
[WebMethod(EnableSession = true)]
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
public BaseResponse DeleteClasses(int id, int dbAppNo)
{
try
{
if (id <= 0)
{
return new BaseResponse
{
Success = false,
Message = "參數不完整"
};
}
DAC_Classes dac = new DAC_Classes();
int rowsAffected = dac.DeleteOne(id, dbAppNo);
if (rowsAffected > 0)
{
return new BaseResponse
{
Success = true,
Message = "刪除成功"
};
}
else
{
return new BaseResponse
{
Success = false,
Message = "找不到要刪除的資料或資料已被修改"
};
}
}
catch (Exception ex)
{
return new BaseResponse
{
Success = false,
Message = "刪除活動發生錯誤:" + ex.Message
};
}
}
/// <summary>
/// 搜尋專案 - 用於自動完成
/// </summary>
[WebMethod(EnableSession = true)]
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
public BaseResponse SearchProjects(string keyword)
{
try
{
DAC_BPAA dac = new DAC_BPAA();
var result = dac.Select(keyword);
return new BaseResponse
{
Success = true,
Message = "搜尋成功",
Data = result ?? new NameValueList()
};
}
catch (Exception ex)
{
return new BaseResponse
{
Success = false,
Message = "搜尋專案發生錯誤:" + ex.Message,
Data = new NameValueList()
};
}
}
/// <summary>
/// 匯出學員簽到表 - 使用 NPOI 生成 Excel 檔案並回傳 Base64
/// </summary>
[WebMethod(EnableSession = true)]
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
public BaseResponse ExportClassesReport(int classesID, string timeFormat = "datetime")
{
try
{
if (classesID <= 0)
{
return new BaseResponse
{
Success = false,
Message = "課程編號為必填欄位"
};
}
using (var conn = DAC.NewConnection())
{
DAC_Classes dacClasses = new DAC_Classes(conn);
DAC_SignUp dacSignUp = new DAC_SignUp(conn);
DAC_BPAA dacBpaa = new DAC_BPAA(conn);
// 查詢課程資訊
ClassesList classes = dacClasses.SelectOne(classesID);
if (classes.Count == 0)
{
return new BaseResponse
{
Success = false,
Message = "找不到課程資訊"
};
}
ClassesItem classItem = classes[0];
classItem.ProjectName = dacBpaa.SelectProjectName(classItem.ProjectID);
// 查詢該課程的參加人員
SignUpList signups = dacSignUp.Select(
noInputReturnAll: true,
CourseID: classesID,
SeqNo: null,
Name: null,
Mobile: null,
QRCode: null,
orderBy: "ID ASC"
);
var 實到人數 = signups.Count(x => x.IsPresent);
var 應到人數 = signups.Count(x => !x.IsOnSite );
var 缺席人數 = signups.Count(x => !x.IsOnSite && !x.IsPresent);
var 異常人數 = signups.Count(x => x.IsException);
var 現場報名 = signups.Count(x => x.IsOnSite);
// 使用 NPOI 建立 Excel 檔案
Excel excel = new Excel();
IWorkbook workbook = new XSSFWorkbook();
ISheet sheet = workbook.CreateSheet("學員簽到表");
excel.WorkBook = workbook;
excel.Sheet = sheet;
// 建立字體和樣式
IFont titleFont = excel.NewFont(null, 12, true);
IFont headerFont = excel.NewFont(null, 10, true);
IFont bodyFont = excel.NewFont(null, 10, false);
ICellStyle titleStyle = excel.NewCellStyle(titleFont, HorizontalAlignment.Center, VerticalAlignment.Center, true);
ICellStyle headerStyleCenter = excel.NewCellStyle(headerFont, HorizontalAlignment.Center, VerticalAlignment.Center, true);
ICellStyle headerStyleLeft = excel.NewCellStyle(headerFont, HorizontalAlignment.Left, VerticalAlignment.Center, true);
ICellStyle dataStyle = excel.NewCellStyle(bodyFont, HorizontalAlignment.Left, VerticalAlignment.Center, true);
ICellStyle dataStyleCenter = excel.NewCellStyle(bodyFont, HorizontalAlignment.Center, VerticalAlignment.Center, true);
ICellStyle dataStyleRight = excel.NewCellStyle(bodyFont, HorizontalAlignment.Right, VerticalAlignment.Center, true);
int rowIndex = 0;
// 表頭
excel.MergeCell(rowIndex, 0, rowIndex, 10);
excel.SetCellValue(rowIndex, 0, classItem.ProjectName, titleStyle);
excel.SetRowHeightInPoints(rowIndex, 15);
rowIndex++;
excel.MergeCell(rowIndex, 0, rowIndex, 10);
excel.SetCellValue(rowIndex, 0, "學員簽到表", headerStyleCenter);
excel.SetRowHeightInPoints(rowIndex, 15);
rowIndex++;
// 課程時間
excel.MergeCell(rowIndex, 0, rowIndex, 1);
excel.SetCellValue(rowIndex, 0, "課程時間", headerStyleCenter);
excel.MergeCell(rowIndex, 2, rowIndex, 10);
excel.SetCellValue(rowIndex, 2, string.Format(PublicVariable.TaiwanCulture, "{0:yyyy年MM月dd日} {1}-{2}", classItem.CourseDate, classItem.StartTime, classItem.EndTime), headerStyleLeft);
rowIndex++;
// 授課講師
excel.MergeCell(rowIndex, 0, rowIndex, 1);
excel.SetCellValue(rowIndex, 0, "授課講師", headerStyleCenter);
excel.MergeCell(rowIndex, 2, rowIndex, 10);
excel.SetCellValue(rowIndex, 2, classItem.Teacher, headerStyleLeft);
rowIndex++;
// 課程地點
excel.MergeCell(rowIndex, 0, rowIndex, 1);
excel.SetCellValue(rowIndex, 0, "課程地點", headerStyleCenter);
excel.MergeCell(rowIndex, 2, rowIndex, 10);
excel.SetCellValue(rowIndex, 2, classItem.CourseLocation, headerStyleLeft);
rowIndex++;
// 課程名稱
excel.MergeCell(rowIndex, 0, rowIndex, 1);
excel.SetCellValue(rowIndex, 0, "課程名稱", headerStyleCenter);
excel.MergeCell(rowIndex, 2, rowIndex, 10);
excel.SetCellValue(rowIndex, 2, classItem.CourseName, headerStyleLeft);
rowIndex++;
// 到課人數
excel.MergeCell(rowIndex, 0, rowIndex, 1);
excel.SetCellValue(rowIndex, 0, "到課人數", headerStyleCenter);
excel.MergeCell(rowIndex, 2, rowIndex, 10);
excel.SetCellValue(rowIndex, 2,
string.Format("實到人數:{0} 人 = 應到人數:{1} 人 - 缺席人數:{2} 人 + 現場報名:{3} 人 / (異常人數:{4} 人)",
實到人數, 應到人數, 缺席人數, 現場報名, 異常人數),
headerStyleLeft);
rowIndex++;
// 參加人員表頭
excel.SetCellValue(rowIndex, 0, "序號", headerStyleCenter);
excel.SetCellValue(rowIndex, 1, "學員姓名", headerStyleCenter);
excel.SetCellValue(rowIndex, 2, "手機號碼", headerStyleCenter);
excel.SetCellValue(rowIndex, 3, "身分證字號", headerStyleCenter);
excel.SetCellValue(rowIndex, 4, "性別", headerStyleCenter);
excel.SetCellValue(rowIndex, 5, "認定次數", headerStyleCenter);
excel.SetCellValue(rowIndex, 6, "用餐別", headerStyleCenter);
excel.SetCellValue(rowIndex, 7, "報名方式", headerStyleCenter);
excel.SetCellValue(rowIndex, 8, "簽到時間", headerStyleCenter);
excel.SetCellValue(rowIndex, 9, "簽退時間", headerStyleCenter);
excel.SetCellValue(rowIndex, 10, "出席狀況", headerStyleCenter);
rowIndex++;
// 參加人員資料
foreach (var signup in signups)
{
excel.SetCellValue(rowIndex, 0, signup.SeqNo, dataStyleCenter);
excel.SetCellValue(rowIndex, 1, signup.Name, dataStyleCenter);
excel.SetCellValue(rowIndex, 2, signup.Mobile, dataStyleCenter);
excel.SetCellValue(rowIndex, 3, string.IsNullOrEmpty(signup.IDNumber) ? "-未填寫-" : signup.IDNumber, dataStyleCenter);
excel.SetCellValue(rowIndex, 4, string.IsNullOrEmpty(signup.Gender) ? "-未填寫-" : signup.Gender, dataStyleCenter);
excel.SetCellValue(rowIndex, 5, signup.LossJobTimes.HasValue ? signup.LossJobTimes.ToString() : "-未填寫-", dataStyleCenter);
excel.SetCellValue(rowIndex, 6, string.IsNullOrEmpty(signup.MealType) ? "-未填寫-" : signup.MealType, dataStyleCenter);
excel.SetCellValue(rowIndex, 7, string.IsNullOrEmpty(signup.SignUpType) ? "預約" : signup.SignUpType, dataStyleCenter);
excel.SetCellValue(rowIndex, 8, Utils.FormatTimeValue(signup.CheckInTime, timeFormat), dataStyleCenter);
excel.SetCellValue(rowIndex, 9, Utils.FormatTimeValue(signup.CheckOutTime, timeFormat), dataStyleCenter);
excel.SetCellValue(rowIndex, 10, signup.IsException ? "異常" : (signup.IsPresent ? "" : "缺席"), dataStyleCenter);
rowIndex++;
}
// 設定欄寬
excel.SetColumnWidthInChars(0, 6);
excel.SetColumnWidthInChars(1, 15);
excel.SetColumnWidthInChars(2, 15);
excel.SetColumnWidthInChars(3, 15);
excel.SetColumnWidthInChars(4, 8);
excel.SetColumnWidthInChars(5, 8);
excel.SetColumnWidthInChars(6, 8);
excel.SetColumnWidthInChars(7, 10);
excel.SetColumnWidthInChars(8, timeFormat == "datetime" ? 20 : 10);
excel.SetColumnWidthInChars(9, timeFormat == "datetime" ? 20 : 10);
excel.SetColumnWidthInChars(10, 8);
// 儲存到記憶體流並轉換為 Base64
MemoryStream ms = new MemoryStream();
workbook.Write(ms);
byte[] fileBytes = ms.ToArray();
string fileBase64 = Convert.ToBase64String(fileBytes);
string fileName = $"{classItem.CourseName}_{DateTime.Now:yyyyMMdd}.xlsx";
return new BaseResponse
{
Success = true,
Message = "匯出成功",
Data = new
{
FileName = fileName,
FileBase64 = fileBase64,
FileSize = fileBytes.Length,
DataCount = signups.Count
}
};
}
}
catch (Exception ex)
{
return new BaseResponse
{
Success = false,
Message = "匯出課程報表發生錯誤:" + ex.Message
};
}
}
/// <summary>
/// 匯出課程清單 - 使用 NPOI 生成 Excel 檔案並回傳 Base64
/// </summary>
[WebMethod(EnableSession = true)]
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
public BaseResponse ExportClassesList(DateTime? courseDate1, DateTime? courseDate2, string projectID)
{
try
{
// 判斷是否所有條件都為空
bool noInput = !courseDate1.HasValue &&
!courseDate2.HasValue &&
string.IsNullOrEmpty(projectID);
using (var conn = DAC.NewConnection())
{
DAC_Classes dac = new DAC_Classes(conn);
DAC_BPAA dacBpaa = new DAC_BPAA(conn);
DAC_SignUp dacSignUp = new DAC_SignUp(conn);
var projectName = !string.IsNullOrEmpty(projectID) ? dacBpaa.SelectProjectName(projectID) : "全部";
// 查詢課程資料
ClassesList result = dac.SelectPage(
noInputReturnAll: true,
startRowIndex: 0,
maximumRows: Int32.MaxValue,
CourseCode: null,
CourseName: null,
CourseDate_1: courseDate1,
CourseDate_2: courseDate2,
CourseLocation: null,
Closed: null,
ProjectID: projectID,
orderBy: "ProjectID ASC, CourseDate DESC"
);
if (result.Count == 0)
{
return new BaseResponse
{
Success = false,
Message = "沒有符合條件的資料",
Data = null
};
}
var reportData = new List<ClassesReportItem>();
foreach (var item in result)
{
// 查詢課程的參加人員
SignUpList signups = dacSignUp.Select(
noInputReturnAll: true,
CourseID: item.ID,
SeqNo: null,
Name: null,
Mobile: null,
QRCode: null
);
reportData.Add(new ClassesReportItem
{
CourseName = item.CourseName,
CourseLocation = item.CourseLocation,
Teacher = item.Teacher,
實到人數 = signups.Count(x => x.IsPresent),
應到人數 = signups.Count(x => x.SignUpType != "現場"),
缺席人數 = signups.Count(x => x.SignUpType != "現場" && !x.IsPresent),
現場報名 = signups.Count(x => x.SignUpType == "現場")
});
}
// 使用 NPOI 建立 Excel 檔案
Excel excel = new Excel();
IWorkbook workbook = new XSSFWorkbook();
ISheet sheet = workbook.CreateSheet("課程匯出");
excel.WorkBook = workbook;
excel.Sheet = sheet;
// 建立字體和樣式
IFont titleFont = excel.NewFont(null, 12, true);
IFont headerFont = excel.NewFont(null, 10, true);
IFont bodyFont = excel.NewFont(null, 10, false);
ICellStyle titleStyle = excel.NewCellStyle(titleFont, HorizontalAlignment.Center, VerticalAlignment.Center, true);
ICellStyle headerStyleCenter = excel.NewCellStyle(headerFont, HorizontalAlignment.Center, VerticalAlignment.Center, true);
ICellStyle headerStyleLeft = excel.NewCellStyle(headerFont, HorizontalAlignment.Left, VerticalAlignment.Center, true);
ICellStyle dataStyle = excel.NewCellStyle(bodyFont, HorizontalAlignment.Left, VerticalAlignment.Center, true);
ICellStyle dataStyleCenter = excel.NewCellStyle(bodyFont, HorizontalAlignment.Center, VerticalAlignment.Center, true);
ICellStyle dataStyleRight = excel.NewCellStyle(bodyFont, HorizontalAlignment.Right, VerticalAlignment.Center, true);
int rowIndex = 0;
// 表頭
excel.MergeCell(rowIndex, 0, rowIndex, 6);
excel.SetCellValue(rowIndex, 0, "課程匯出", headerStyleCenter);
excel.SetRowHeightInPoints(rowIndex, 15);
rowIndex++;
// 計畫
excel.SetCellValue(rowIndex, 0, "計畫", headerStyleCenter);
excel.MergeCell(rowIndex, 1, rowIndex, 6);
excel.SetCellValue(rowIndex, 1, projectName, headerStyleLeft);
rowIndex++;
// 課程日期
excel.SetCellValue(rowIndex, 0, "課程日期", headerStyleCenter);
excel.MergeCell(rowIndex, 1, rowIndex, 6);
excel.SetCellValue(rowIndex, 1, string.Format("{0:yyyy-MM-dd} - {1:yyyy-MM-dd}", courseDate1, courseDate2), headerStyleLeft);
rowIndex++;
// 參加人員表頭
excel.SetCellValue(rowIndex, 0, "課程名稱", headerStyleCenter);
excel.SetCellValue(rowIndex, 1, "課程地點", headerStyleCenter);
excel.SetCellValue(rowIndex, 2, "授課講師", headerStyleCenter);
excel.SetCellValue(rowIndex, 3, "實到人數", headerStyleCenter);
excel.SetCellValue(rowIndex, 4, "應到人數", headerStyleCenter);
excel.SetCellValue(rowIndex, 5, "缺席人數", headerStyleCenter);
excel.SetCellValue(rowIndex, 6, "現場報名", headerStyleCenter);
rowIndex++;
// 參加人員資料
foreach (var reportItem in reportData)
{
excel.SetCellValue(rowIndex, 0, reportItem.CourseName, dataStyleCenter);
excel.SetCellValue(rowIndex, 1, reportItem.CourseLocation, dataStyleCenter);
excel.SetCellValue(rowIndex, 2, reportItem.Teacher, dataStyleCenter);
excel.SetCellValue(rowIndex, 3, reportItem.實到人數, dataStyleCenter);
excel.SetCellValue(rowIndex, 4, reportItem.應到人數, dataStyleCenter);
excel.SetCellValue(rowIndex, 5, reportItem.缺席人數, dataStyleCenter);
excel.SetCellValue(rowIndex, 6, reportItem.現場報名, dataStyleCenter);
rowIndex++;
}
// 設定欄寬
excel.SetColumnWidthInChars(0, 40);
excel.SetColumnWidthInChars(1, 40);
excel.SetColumnWidthInChars(2, 20);
excel.SetColumnWidthInChars(3, 12);
excel.SetColumnWidthInChars(4, 12);
excel.SetColumnWidthInChars(5, 12);
excel.SetColumnWidthInChars(6, 12);
// 儲存到記憶體流並轉換為 Base64
MemoryStream ms = new MemoryStream();
workbook.Write(ms);
byte[] fileBytes = ms.ToArray();
string fileBase64 = Convert.ToBase64String(fileBytes);
string fileName = $"課程匯出_{DateTime.Now:yyyyMMdd}.xlsx";
return new BaseResponse
{
Success = true,
Message = "匯出成功",
Data = new
{
FileName = fileName,
FileBase64 = fileBase64,
FileSize = fileBytes.Length
}
};
}
}
catch (Exception ex)
{
return new BaseResponse
{
Success = false,
Message = "匯出課程報表發生錯誤:" + ex.Message
};
}
}
/// <summary>
/// 課程報表項目
/// </summary>
private class ClassesReportItem
{
public string CourseName { get; set; }
public string CourseLocation { get; set; }
public string Teacher { get; set; }
public int 實到人數 { get; set; }
public int 應到人數 { get; set; }
public int 缺席人數 { get; set; }
public int 現場報名 { get; set; }
}
}
}