using DocumentFormat.OpenXml.EMMA; using Model; using Org.BouncyCastle.Asn1.Ocsp; using PagedList; using System; using System.Collections.Generic; using System.Data.Entity; using System.Linq; using System.Threading.Tasks; using System.util.collections; using System.Web; using System.Web.Http; using static Model.admin_log; /// /// guadanOderController 的摘要描述 /// [ezAuthorize] public class guadanOrderController : ApiController { private Model.ezEntities _db = new Model.ezEntities(); [HttpPost] [Route("api/guadan/list")] public async Task getGuadanList([FromBody] guadan_order_search_dto search) { var lastCheckoutTime = _db.GuadanTimeSetting.FirstOrDefault(); string lastCheckoutTimeStr = null; if (lastCheckoutTime != null) { lastCheckoutTimeStr = lastCheckoutTime.LatestCheckOut; } var query = _db.GuaDanOrder .Where(a => a.IsCancel == false) .Where(a => a.IsDeleted == false); if (!string.IsNullOrEmpty(search.guaDanOrderNo)) { query = query.Where(order => order.GuaDanOrderNo == search.guaDanOrderNo); } if (search.guadanUser != null) { query = query.Where(order => order.BookerName == search.guadanUser); } if (search.startDate != null && search.endDate != null) { query = query.Where(order => order.StartDate >= search.startDate) .Where(order => order.EndDate <= search.endDate); } else { if (search.startDate != null) { query = query.Where(order => order.StartDate == search.startDate); } else if (search.endDate != null) { query = query.Where(order => order.EndDate == search.endDate); } } var total = query.Count(); var data1 = await query.ToListAsync(); var data = data1 .OrderByDescending(b => b.CreatedAt) .Select(a => new { uuid = a.Uuid, guaDanOrderNo = a.GuaDanOrderNo, start_date = a.StartDate, end_date = a.EndDate, created_at = a.CreatedAt, updated_at = a.UpdatedAt, notes = a.Notes, is_timeout = !string.IsNullOrEmpty(lastCheckoutTimeStr) && _db.GuaDanOrderGuest .Where(g => g.GuaDanOrderNo == a.GuaDanOrderNo && !g.IsDeleted && g.StatusCode == GuaDanOrderGuest.STATUS_CHECKED_IN) .ToList() .Any(g => g.CheckOutAt.HasValue && DateTime.Parse(g.CheckOutAt.Value.ToString("yyyy-MM-dd") + " " + lastCheckoutTimeStr) < DateTime.Now ), activity = _db.activities .Where(act => act.num == a.ActivityNum) .Select(act => new { subject = act.subject }) .FirstOrDefault(), bookerName = a.BookerName, guest_count = _db.GuaDanOrderGuest .Where(c => c.GuaDanOrderNo == a.GuaDanOrderNo && c.IsDeleted == false) .Where(c => c.RegionRoomBedStatus.Code != GuaDanOrderGuest.STATUS_CANCELLED) .Count(), guadan_status = _db.GuaDanOrderGuest .Where(g => g.GuaDanOrderNo == a.GuaDanOrderNo && a.IsDeleted == false) .Where(g => g.StatusCode != GuaDanOrderGuest.STATUS_CANCELLED) .All(g => g.StatusCode == "401") ? new { code = 501, name = "預約" } : _db.GuaDanOrderGuest .Where(g => g.GuaDanOrderNo == a.GuaDanOrderNo && a.IsDeleted == false) .Where(g => g.StatusCode != GuaDanOrderGuest.STATUS_CANCELLED) .All(g => g.StatusCode == "403") ? new { code = 502, name = "全部退房" } : _db.GuaDanOrderGuest .Where(g => g.GuaDanOrderNo == a.GuaDanOrderNo && a.IsDeleted == false) .Where(g => g.StatusCode != GuaDanOrderGuest.STATUS_CANCELLED) .Any(g => g.StatusCode == "402" && a.IsCancel == false) ? new { code = 503, name = "正在入住" } : new { code = 504, name = "部分退房" } }) .Skip((search.page - 1) * search.pageSize) .Take(search.pageSize) .ToList(); return Ok(new { total, data }); } [HttpPost] [Route("api/guadan/getorderbyuuid")] public async Task getGuadanOrderByUuid([FromBody] dynamic req) { if (!Guid.TryParse((string)req.uuid, out Guid targetGuid)) { return BadRequest("無效的 GUID 格式"); } var order = await _db.GuaDanOrder .Where(a => a.Uuid == targetGuid) .Where(a => a.IsCancel == false && a.IsDeleted == false) .FirstOrDefaultAsync(); if (order == null) { return BadRequest("未找到對應掛單資料"); } // 1. 先把全台/全區的 Region 載入記憶體做成字典,方便 GetParentPath 快速查找 var regionDict = _db.Region.ToDictionary(r => r.Uuid); // 2. 修改你的 GetParentPath 邏輯(改用高效的 while 迴圈) List GetRegionPathNames(Guid? startRegionUuid) { var pathNames = new List(); Guid? nextId = startRegionUuid; while (nextId.HasValue && regionDict.TryGetValue(nextId.Value, out var reg)) { pathNames.Add(reg.Name); // 收集區域名稱 nextId = reg.ParentUuid; // 假設你的欄位叫 ParentUuid,請依實際欄位修改 } // 反轉順序讓它變成:[大區域, 中區域, 小區域] pathNames.Reverse(); return pathNames; } var result = new { order_form = new { order.admin, order.followers, startdate = order.StartDate?.ToString("yyyy-MM-dd"), enddate = order.EndDate?.ToString("yyyy-MM-dd"), order.CreateUser, order.CreatedAt, order.UpdatedAt, note = order.Notes, orderNo = order.GuaDanOrderNo, order.BookerFollowerNum, bookerName = order.BookerName, bookerPhone = order.BookerPhone, order.IsDeleted, uuid = order.Uuid, activityNum = order.ActivityNum, }, details = _db.GuaDanOrderGuest .Where(g => g.GuaDanOrderNo == order.GuaDanOrderNo && g.IsDeleted == false) .Select(g => new { g.Uuid, g.GuaDanOrderNo, g.RoomUuid, g.BedUuid, checkinat = g.CheckInAt, checkoutat = g.CheckOutAt, gender = g.Sex, specialNeeds = g.Handi, name = g.Name, statuscode = g.StatusCode, g.FollowerNum, bedName = _db.RegionRoomBed.Where(b => b.Uuid == g.BedUuid).Select(b => b.Name).FirstOrDefault(), // 先把資料庫能查的查出來 roomRaw = _db.Room.Where(r => r.Uuid == g.RoomUuid).Select(r => new { r.Name, r.RegionUuid }).FirstOrDefault() }).AsEnumerable() // 🔥 關鍵:從這裡切斷,後面的 Select 會在 C# 記憶體內執行 .Select(g => new { g.Uuid, g.GuaDanOrderNo, g.RoomUuid, g.BedUuid, g.checkinat, g.checkoutat, g.gender, g.specialNeeds, g.name, g.statuscode, g.FollowerNum, g.bedName, room = g.roomRaw == null ? null : new { g.roomRaw.Name, g.roomRaw.RegionUuid, // 在記憶體中,這時候可以直接查字典拿到當前區域名稱 regionName = regionDict.TryGetValue(g.roomRaw.RegionUuid, out var r) ? r.Name : null, // 🔥 這時候呼叫區域函式就不會報錯了! resion = GetRegionPathNames(g.roomRaw.RegionUuid) } }) .ToList() }; return Ok(result); } [HttpGet] [Route("api/guadan/getorderbyid")] public async Task getGuadanOrderById(string orderId) { var order = await _db.GuaDanOrder .Where(a => a.GuaDanOrderNo == orderId) .Where(a => a.IsCancel == false && a.IsDeleted == false) .FirstOrDefaultAsync(); if (order == null) { return BadRequest("未找到對應訂單"); } var result = new { order.admin, order.followers, StartDate = order.StartDate?.ToString("yyyy-MM-dd"), EndDate = order.EndDate?.ToString("yyyy-MM-dd"), order.CreateUser, order.CreatedAt, order.UpdatedAt, order.Notes, order.GuaDanOrderNo, order.BookerFollowerNum, order.BookerName, order.BookerPhone, order.IsDeleted, order.Uuid, order.ActivityNum, }; return Ok(result); } [HttpPost] [Route("api/guadan/create")] public async Task createGuadanOrder(GuaDanOrderView model) { if (model == null) { return BadRequest("掛單資料不可為空"); } if (model.Uuid.HasValue) { return BadRequest("已存在對應掛單資料"); } try { if (model.startdate > model.enddate) { return BadRequest("掛單結束時間不能再開始時間之前"); } } catch { } var guadanorder = new GuaDanOrder { StartDate = model.startdate, EndDate = model.enddate, CreateUser = model.createuser, CreatedAt = DateTime.Now, UpdatedAt = DateTime.Now, Notes = model.note, GuaDanOrderNo = GuaDanOrder.GenerateStatusCode(), BookerName = model.bookerName, BookerPhone = model.bookerPhone, Uuid = Guid.NewGuid(), ActivityNum = model.activityNum, }; _db.GuaDanOrder.Add(guadanorder); await _db.SaveChangesAsync(); return Ok(guadanorder); } [HttpPost] [Route("api/guadan/changeGuadanGuest")] public async Task ChangeGuadanGuest([FromBody] dynamic req) { var json = req; if (json != null && json.guest != null) { var guest = json.guest; if (((string)guest.uuid).StartsWith("temp")) { //建立掛單明細 var new_guest = new GuaDanOrderGuest { GuaDanOrderNo = guest.guaDanOrderNo, Uuid = Guid.NewGuid(), StatusCode = GuaDanOrderGuest.STATUS_BOOKED, // 預約狀態 RoomUuid = guest.roomUuid, BedUuid = guest.bedUuid, CheckInAt = guest.checkInAt, CheckOutAt = guest.checkOutAt, Sex = guest.gender, Handi = guest.handi, IsDeleted = false, Name = guest.name, OrderUuid=guest.orderUuid, }; //_db.GuaDanOrderGuest.Remove(old); _db.GuaDanOrderGuest.Add(new_guest); int totalDays = (new_guest.CheckOutAt.Value - new_guest.CheckInAt.Value).Days; var bedList = new List(); for (int i = 0; i < totalDays; i++) { var scheduleDate = new_guest.CheckOutAt.Value.AddDays(i); var schedul = new RegionAndRoomAndBedSchedule { Title = "掛單", Description = "床位掛單", ScheduleDate = scheduleDate, IsDeleted = false, IsCancel = false, TargetUuid = new_guest.BedUuid, UseType = (int)RegionAndRoomAndBedSchedule.SchedulePurpose.Bed_Reservation, CreatedAt = DateTime.Now, GuaDanOrderNo = new_guest.GuaDanOrderNo, Uuid = Guid.NewGuid(), GuaDanOrderGuestUuid = new_guest.Uuid, }; //_db.RegionAndRoomAndBedSchedule.Add(schedul); bedList.Add(schedul); } _db.RegionAndRoomAndBedSchedule.AddRange(bedList); } else { if (!Guid.TryParse((string)guest.uuid, out Guid targetGuid)) { return BadRequest("無效的 GUID 格式"); } var old = _db.GuaDanOrderGuest.Where(a => a.Uuid == targetGuid).FirstOrDefault(); if (old == null) { return BadRequest("未找到對應掛單明細資料"); } var sch = _db.RegionAndRoomAndBedSchedule.Where(a => a.GuaDanOrderGuestUuid == old.Uuid).ToList(); if (sch != null) { _db.RegionAndRoomAndBedSchedule.RemoveRange(sch); } //建立掛單明細 var new_guest = new GuaDanOrderGuest { GuaDanOrderNo = old.GuaDanOrderNo, Uuid = Guid.NewGuid(), StatusCode = GuaDanOrderGuest.STATUS_BOOKED, // 預約狀態 RoomUuid = guest.roomUuid, BedUuid = guest.bedUuid, CheckInAt = guest.checkInAt, CheckOutAt = guest.checkOutAt, Sex = guest.gender, Handi = guest.handi, IsDeleted = false, Name = guest.name, }; _db.GuaDanOrderGuest.Remove(old); _db.GuaDanOrderGuest.Add(new_guest); int totalDays = (new_guest.CheckOutAt.Value - new_guest.CheckInAt.Value).Days; var bedList = new List(); for (int i = 0; i < totalDays; i++) { var scheduleDate = new_guest.CheckOutAt.Value.AddDays(i); var schedul = new RegionAndRoomAndBedSchedule { Title = "掛單", Description = "床位掛單", ScheduleDate = scheduleDate, IsDeleted = false, IsCancel = false, TargetUuid = new_guest.BedUuid, UseType = (int)RegionAndRoomAndBedSchedule.SchedulePurpose.Bed_Reservation, CreatedAt = DateTime.Now, GuaDanOrderNo = new_guest.GuaDanOrderNo, Uuid = Guid.NewGuid(), GuaDanOrderGuestUuid = new_guest.Uuid, }; //_db.RegionAndRoomAndBedSchedule.Add(schedul); bedList.Add(schedul); } _db.RegionAndRoomAndBedSchedule.AddRange(bedList); } _db.SaveChanges(); } else { return BadRequest("請提供掛單明細資料"); } return Ok(); } [HttpPost] [Route("api/guadan/updateGandan")] public async Task UpdateGandan([FromBody] dynamic req) { var json = req; if (json != null && json.order != null) { var order = json.order; string guadanOrderNo = order.GuaDanOrderNo; var oldOrder = _db.GuaDanOrder.Where(a => a.GuaDanOrderNo == guadanOrderNo).FirstOrDefault(); if (oldOrder == null) { return BadRequest("未找到對應掛單資料"); } oldOrder.StartDate = order.startdate; oldOrder.EndDate = order.enddate; oldOrder.UpdatedAt = DateTime.Now; oldOrder.Notes = order.note; oldOrder.BookerName = order.bookerName; oldOrder.BookerPhone = order.bookerPhone; await _db.SaveChangesAsync(); } return Ok(); } [HttpPost] [Route("api/guadan/saveGandan")] public async Task SaveGandan([FromBody] dynamic req) { var json = req; if (json != null && json.order != null) { var order = json.order; if (string.IsNullOrEmpty(order.GuaDanOrderNo)) {//表示新增掛單 // 生成新的掛單編號跟uuid var guadanorder = new GuaDanOrder { StartDate = order.startdate, EndDate = order.enddate, CreateUser = order.createuser, CreatedAt = DateTime.Now, UpdatedAt = DateTime.Now, Notes = order.note, GuaDanOrderNo = GuaDanOrder.GenerateStatusCode(), BookerName = order.bookerName, BookerPhone = order.bookerPhone, Uuid = Guid.NewGuid(), ActivityNum = order.activityNum, }; //掛單明細 var list = new List(); var guest = new GuaDanOrderGuest(); var bedList = new List(); if (json.detail != null) { var details = json.detail; foreach (var detail in details) { guest = new GuaDanOrderGuest { GuaDanOrderNo = guadanorder.GuaDanOrderNo, Uuid = Guid.NewGuid(), StatusCode = GuaDanOrderGuest.STATUS_BOOKED, // 預約狀態 RoomUuid = detail.roomUuid, BedUuid = detail.bedUuid, CheckInAt = detail.checkInAt, CheckOutAt = detail.checkOutAt, Sex = detail.gender, Handi = detail.handi, IsDeleted = false, Name = detail.name, OrderUuid=guadanorder.Uuid }; list.Add(guest); if (!Guid.TryParse((string)detail.bedUuid, out Guid targetGuid)) { return BadRequest("無效的 GUID 格式"); } if (detail.bedUuid != null && !string.IsNullOrEmpty((string)detail.bedUuid)) { var bed = _db.RegionRoomBed.Where(r => r.Uuid == targetGuid); if (bed == null) return BadRequest("床位不存在"); // 檢查床位可用性 var bedIsCanUse = await RegionAndRoomAndBedSchedule.IsBedAvailableAsync( _db, targetGuid, guest.CheckInAt.Value, guest.CheckOutAt.Value ); if (!bedIsCanUse) return BadRequest("床位在該時間段內已被占用"); } else { return BadRequest("未設定床位"); } int totalDays = (guest.CheckOutAt.Value - guest.CheckInAt.Value).Days; for (int i = 0; i < totalDays; i++) { var scheduleDate = guest.CheckOutAt.Value.AddDays(i); var schedul = new RegionAndRoomAndBedSchedule { Title = "掛單", Description = "床位掛單", ScheduleDate = scheduleDate, IsDeleted = false, IsCancel = false, TargetUuid = guest.BedUuid, UseType = (int)RegionAndRoomAndBedSchedule.SchedulePurpose.Bed_Reservation, CreatedAt = DateTime.Now, GuaDanOrderNo = guest.GuaDanOrderNo, Uuid = Guid.NewGuid(), GuaDanOrderGuestUuid = guest.Uuid, }; //_db.RegionAndRoomAndBedSchedule.Add(schedul); bedList.Add(schedul); } } } _db.GuaDanOrder.Add(guadanorder); _db.GuaDanOrderGuest.AddRange(list); _db.RegionAndRoomAndBedSchedule.AddRange(bedList); _db.SaveChanges(); } } return Ok(); } [HttpPost] [Route("api/guadan/update")] public async Task updateGuadanOrder(GuaDanOrderView model) { if (model == null) { return BadRequest("掛單資料不可為空"); } if (!model.Uuid.HasValue) { return BadRequest(""); } try { if (model.startdate > model.enddate) { return BadRequest("掛單結束時間不能再開始時間之前"); } } catch { } var order = await _db.GuaDanOrder.FindAsync(model.Uuid.Value); if (order == null) { return BadRequest("未找到對應掛單資料"); } order.StartDate = model.startdate; order.EndDate = model.enddate; order.Notes = model.note; order.BookerName = model.bookerName; order.BookerPhone = model.bookerPhone; order.ActivityNum = model.activityNum; await _db.SaveChangesAsync(); return Ok(model); } [HttpPost] [Route("api/guadan/cancel")] public async Task CancelGuadanOrder([FromUri] Guid uuid) { var guadan = await _db.GuaDanOrder.FindAsync(uuid); if (guadan == null) { return NotFound(); } if (_db.GuaDanOrderGuest.Any(a => (a.GuaDanOrderNo == guadan.GuaDanOrderNo) && a.StatusCode != "404")) { return BadRequest($"該掛單已經存在掛單蓮友,不能取消!"); } using (var transaction = _db.Database.BeginTransaction()) { try { var guadanGuests = await _db.GuaDanOrderGuest .Where(a => a.GuaDanOrderNo == guadan.GuaDanOrderNo) .ToListAsync(); if (guadanGuests.Any()) { foreach (var guest in guadanGuests) { guest.StatusCode = "404"; // 取消所有相關的排程 if (guest.RegionAndRoomAndBedSchedule != null && guest.RegionAndRoomAndBedSchedule.Any()) { foreach (var schedule in guest.RegionAndRoomAndBedSchedule) { schedule.IsCancel = true; } } } await _db.SaveChangesAsync(); } guadan.IsCancel = true; await _db.SaveChangesAsync(); transaction.Commit(); return Ok(new { message = "取消成功" }); } catch (Exception ex) { transaction.Rollback(); return InternalServerError(ex); } } } public class guadan_order_dto { public int id { get; set; } public int room_id { get; set; } public int bed_id { get; set; } public int guest_id { get; set; } public DateTime start_date { get; set; } public DateTime? end_date { get; set; } public int? create_user { get; set; } public DateTime created_at { get; set; } public DateTime updated_at { get; set; } public string notes { get; set; } public int? follower_num { get; set; } public Room room { get; set; } = null; public RegionRoomBed bed { get; set; } = null; } public class guadan_order_search_dto { public DateTime? startDate { get; set; } public DateTime? endDate { get; set; } public string guadanUser { get; set; } public int page { get; set; } = 1; public int pageSize { get; set; } = 10; public string guaDanOrderNo { get; set; } = null; } }