using DocumentFormat.OpenXml.EMMA; using DocumentFormat.OpenXml.Office.CustomUI; using DocumentFormat.OpenXml.Wordprocessing; using Microsoft.AspNet.SignalR; using Model; using Org.BouncyCastle.Asn1.Ocsp; using System; using System.Collections.Generic; using System.Data.Entity; using System.Linq; using System.Net; using System.Threading.Tasks; using System.Web; using System.Web.Http; /// /// regionRoomBedController 的摘要描述 /// [ezAuthorize] public class regionRoomBedController : ApiController { private Model.ezEntities _db = new Model.ezEntities(); public regionRoomBedController() { // // TODO: 在這裡新增建構函式邏輯 // } [HttpGet] [Route("api/region/room/bed/list")] public IHttpActionResult GetRegionRoomBedListByRoomId(Guid? roomUuid, DateTime StartTime, DateTime? EndTime) { if (StartTime == null || EndTime == null) return BadRequest("时间不能为空"); if (roomUuid == null) return BadRequest("roomId不能为空"); // 先取出床位 var beds = _db.RegionRoomBed .Where(a => a.RoomUuid == roomUuid) .ToList(); StartTime = StartTime.Date; EndTime = EndTime?.Date; var data = beds.Select(a => { var bedSchedules = _db.RegionAndRoomAndBedSchedule .Where(s => s.GuaDanOrderGuest.StatusCode != "403") .Where(s => s.GuaDanOrderGuest.StatusCode != "404") .Where(b => b.TargetUuid == a.Uuid && (b.ScheduleDate == null || (b.ScheduleDate >= StartTime && b.ScheduleDate < EndTime))) .ToList() .Select(c => new { c.Uuid, c.Description, c.IsDeleted, c.GuaDanOrderNo, c.UseType, c.Title, c.TargetUuid, scheduledate = c.ScheduleDate, }) .ToList(); bool canUsed = !bedSchedules.Any(); bool bedIsStop = IsBedStopped(a); return new { a.Uuid, a.Name, a.Gender, a.IsActive, a.StatusCode, a.RoomUuid, canUsed, bedIsStop, schedule = bedSchedules }; }); return Ok(data); } public bool IsBedStopped(RegionRoomBed bed) { // 1️⃣ 床位本身不可用 if (!bed.IsActive || bed.IsDeleted) return true; // 2️⃣ 所属房间不可用 var room = bed.Room; if (room == null || !room.IsActive.Value || room.IsDeleted) return true; // 3️⃣ 所属区域不可用 var region = room.Region; while (region != null) { if (!region.IsActive || region.IsDeleted) return true; // 有任意一级区域不可用就返回 true if (!region.ParentUuid.HasValue) break; // 到顶层了 region = _db.Region.FirstOrDefault(r => r.Uuid == region.ParentUuid.Value); } // 4️⃣ 全部检查通过 → 床位可用 return false; } [HttpPost] [Route("api/region/bed/create")] public IHttpActionResult CreateBed([FromBody] RegionRoomBed bed) { var room = _db.Room.Find(bed.RoomUuid); if (room == null) { return BadRequest("當前客房不存在"); } if (room.Gender != bed.Gender) { return BadRequest("床為性別和房間性別必須一致"); } if (room.BedCount != null && room.BedCount < room.RegionRoomBed.Count() + 1) { return BadRequest("該客房床位已滿"); } var regionBed = new RegionRoomBed { Name = bed.Name, RoomUuid = bed.RoomUuid, StatusCode = bed.StatusCode, IsActive = bed.IsActive, Gender = bed.Gender, Uuid = Guid.NewGuid(), }; _db.RegionRoomBed.Add(regionBed); _db.SaveChanges(); //創建床位 return Ok(new { uuid = regionBed.Uuid, roomUuid = regionBed.RoomUuid, statuscode = regionBed.StatusCode, isactive = regionBed.IsActive, gender = regionBed.Gender, name = regionBed.Name, }); } [HttpPost] [Route("api/region/bed/update")] public IHttpActionResult UpdateBed([FromBody] RegionRoomBed bed) { if (string.IsNullOrWhiteSpace(bed.Name)) { return BadRequest("床位名稱不可為空"); } var oldBed = _db.RegionRoomBed.Find(bed.Uuid); if (oldBed == null) { return BadRequest("更新失敗,床位不存在"); } var room = _db.Room.Find(bed.RoomUuid); if (room == null) { return BadRequest("當前客房不存在"); } if (room.Gender != bed.Gender) { return BadRequest("床為性別和房間性別必須一致"); } if (bed.IsActive == false) { var hasPendingBeds = oldBed.GuaDanOrderGuest.Any(g => !g.IsDeleted && (g.RegionRoomBedStatus.Code == GuaDanOrderGuest.STATUS_BOOKED || // 预约中 g.RegionRoomBedStatus.Code == GuaDanOrderGuest.STATUS_CHECKED_IN)); if (hasPendingBeds) { return Content(HttpStatusCode.BadRequest, new { code = "BED_IS_USED", message = "該床位正在掛單中,請先處理" }); } } oldBed.StatusCode = bed.StatusCode; oldBed.IsActive = bed.IsActive; oldBed.Name = bed.Name; oldBed.Gender = bed.Gender; _db.SaveChanges(); //創建床位 return Ok(new { message = "床位更新成功" }); } [HttpPost] [Route("api/region/bed/delete")] public IHttpActionResult Delete([FromUri] Guid uuid) { var bed = _db.RegionRoomBed.Find(uuid); if (bed == null) { return BadRequest("未找到床位"); } try { _db.RegionRoomBed.Remove(bed); _db.SaveChanges(); return Ok(new { message = "刪除成功" }); } catch (System.Data.Entity.Infrastructure.DbUpdateException ex) { // 判斷是否為外鍵關聯錯誤 if (ex.InnerException?.InnerException is System.Data.SqlClient.SqlException sqlEx && (sqlEx.Number == 547)) // 547 = SQL Server 外鍵違反錯誤碼 { return BadRequest("刪除失敗:該床位已被使用或存在關聯資料,無法刪除。"); } return InternalServerError(ex); // 其他資料庫錯誤 } catch (Exception ex) { return InternalServerError(ex); // 其他未預期錯誤 } } [HttpGet] [Route("api/region/bed/getavailablebedcountbytime")] public async Task GetCanUseBedCountByTime(DateTime startTime, DateTime endTime) { //获取某个时间段内可用床位数量 var start = startTime.Date; var end = endTime.Date; // 找出所有在日期範圍內被占用的床位 var busyBedUuids = await _db.RegionAndRoomAndBedSchedule .Where(s => s.GuaDanOrderGuest.StatusCode != "403") .Where(s => s.GuaDanOrderGuest.StatusCode != "404") .Where(a => a.IsCancel == false) .Where(s => s.IsDeleted == false && (s.ScheduleDate == null // 長期占用 || (s.ScheduleDate >= start && s.ScheduleDate < end))) .Select(s => s.TargetUuid) .Distinct() .ToListAsync(); // 可用床位 = 所有床位 - 忙碌床位 var availableBeds = _db.RegionRoomBed .Where(b => b.IsActive) .Where(b => !busyBedUuids.Contains(b.Uuid)); var result = await availableBeds .GroupBy(b => b.Gender) .Select(g => new { Gender = g.Key, Count = g.Count() }) .ToListAsync(); var male = result.Where(r => r.Gender == true).Select(r => r.Count).FirstOrDefault(); var female = result.Where(r => r.Gender == false).Select(r => r.Count).FirstOrDefault(); return Ok(new {male, female}); } [HttpPost] [Route("api/region/bed/preallocation")] public async Task PreAutoAllocationBed([FromBody] ConfirmAllocationRequest request) { DateTime allocationStart = request.CheckInAt; DateTime? allocationEnd = request.CheckOutAt; bool isAllallocation = true; //是否全部分配完毕 // 获取可用床位列表 var availableBeds = await RegionAndRoomAndBedSchedule.GetAvailableBedsAsync(_db, allocationStart, allocationEnd); // 将床位按房间分组 var roomGroups = availableBeds .GroupBy(b => b.RoomUuid) .Select(g => new { RoomUuid = g.Key, Beds = g.ToList(), Gender = g.First().Room.Gender // bool,不可空 }) .ToList(); var data = new List(); // 按性别分组请求 var maleFollowers = request.PreBeds.Where(f => f.sex == "M").ToList(); var femaleFollowers = request.PreBeds.Where(f => f.sex == "F").ToList(); void AllocateByGender(List followers, bool isMale) { var rooms = roomGroups.Where(r => r.Gender == isMale).ToList(); int remaining = followers.Count; int index = 0; while (remaining > 0 && rooms.Any(r => r.Beds.Count > 0)) { // 找能容纳所有人的房间 var fullRoom = rooms .Where(r => r.Beds.Count >= remaining) .OrderBy(r => r.Beds.Count - remaining) // 分配后剩余床位最少优先 .FirstOrDefault(); if (fullRoom != null) { // 分配所有剩余人员 for (int i = index; i < followers.Count; i++) { var follower = followers[i]; var bed = fullRoom.Beds.First(); data.Add(new FollowerBedResponse { num = follower.num, sex = follower.sex, bedUuid = bed.Uuid }); fullRoom.Beds.Remove(bed); remaining--; index++; } break; // 全部分配完 } else { // 找剩余床位最多的房间 var room = rooms.OrderByDescending(r => r.Beds.Count).First(); int toAssign = Math.Min(remaining, room.Beds.Count); for (int i = 0; i < toAssign; i++) { var follower = followers[index++]; var bed = room.Beds.First(); data.Add(new FollowerBedResponse { num = follower.num, sex = follower.sex, bedUuid = bed.Uuid }); room.Beds.Remove(bed); remaining--; } } } if (index < followers.Count) { isAllallocation = false; return; } // 剩余无法分配的人员 for (int i = index; i < followers.Count; i++) { data.Add(new FollowerBedResponse { num = followers[i].num, sex = followers[i].sex, bedUuid = null }); } } // 先分配男生,再分配女生 AllocateByGender(maleFollowers, true); if (!isAllallocation) { return BadRequest("分配失敗,床位不足"); } AllocateByGender(femaleFollowers, false); if (!isAllallocation) { return BadRequest("分配失敗,床位不足"); } return Ok(new { message = "預分配成功", data }); } [HttpPost] [Route("api/region/bed/confirmallocation")] public async Task ConfirmAutoAllocationBed([FromBody] ConfirmAllocationRequest request) { if (request?.PreBeds == null || !request.PreBeds.Any()) return BadRequest("请求床位不能为空"); using (var transaction = _db.Database.BeginTransaction()) { try { DateTime allocationStart = request.CheckInAt.Date; // 保證時間清零 DateTime? allocationEnd = request.CheckOutAt?.Date; foreach (var req in request.PreBeds) { if (req.bedUuid != null) { // 先拉出床位相關排程到內存,避免 EF 不支援 .Date var schedules = _db.RegionAndRoomAndBedSchedule .Where(s => s.IsDeleted == false && !s.IsCancel && s.TargetUuid == req.bedUuid) .ToList(); bool conflictExists = schedules.Any(s => s.ScheduleDate == null // 長期占用 || (allocationEnd.HasValue ? (s.ScheduleDate >= allocationStart && s.ScheduleDate <= allocationEnd.Value) : s.ScheduleDate >= allocationStart) ); if (conflictExists) { transaction.Rollback(); return BadRequest($"床位 {req.bedUuid} 在时间段内已被占用,分配失败"); } } // 找到房間 UUID Guid? roomUuid = null; if (req.bedUuid != null) { var bed = _db.RegionRoomBed.Find(req.bedUuid); if (bed != null) roomUuid = bed.RoomUuid; } // 新增挂单入住客人记录 var guest = new GuaDanOrderGuest { GuaDanOrderNo = request.OrderNo, FollowerNum = req.num, BedUuid = req.bedUuid, Uuid = Guid.NewGuid(), RoomUuid = roomUuid, CheckInAt = allocationStart, CheckOutAt = allocationEnd, StatusCode = "401", }; _db.GuaDanOrderGuest.Add(guest); // 新增每日排程 if (allocationEnd.HasValue) { for (var date = allocationStart; date < allocationEnd.Value; date = date.AddDays(1)) { var newSchedule = new RegionAndRoomAndBedSchedule { Uuid = Guid.NewGuid(), TargetUuid = req.bedUuid, ScheduleDate = date, UseType = (int)RegionAndRoomAndBedSchedule.SchedulePurpose.Bed_Reservation, IsDeleted = false, CreatedBy = "系统自动分配", CreatedAt = DateTime.Now, GuaDanOrderNo = guest.GuaDanOrderNo, Title = "掛單", GuaDanOrderGuestUuid = guest.Uuid, }; _db.RegionAndRoomAndBedSchedule.Add(newSchedule); } } else { // 長期占用 var newSchedule = new RegionAndRoomAndBedSchedule { Uuid = Guid.NewGuid(), TargetUuid = req.bedUuid, ScheduleDate = null, UseType = (int)RegionAndRoomAndBedSchedule.SchedulePurpose.Bed_Reservation, IsDeleted = false, CreatedBy = "系统自动分配", CreatedAt = DateTime.Now, GuaDanOrderNo = guest.GuaDanOrderNo, Title = "掛單", GuaDanOrderGuestUuid = guest.Uuid, }; _db.RegionAndRoomAndBedSchedule.Add(newSchedule); } } await _db.SaveChangesAsync(); transaction.Commit(); return Ok(new { message = "分配成功" }); } catch (Exception ex) { transaction.Rollback(); return InternalServerError(ex); } } } private bool BedIsCanUsed(RegionRoomBed bed, DateTime? StartTime, DateTime? EndTime) { if (!bed.IsActive) { return false; } return true; } public class FollowerBedRequest { public int num { get; set; } public string sex { get; set; } } public class FollowerBedResponse { public int num { get; set; } public string sex { get; set; } public Guid? bedUuid { get; set; } = null; public string bedName { get; set; } } public class ConfirmAllocationRequest { public List PreBeds { get; set; } public string OrderNo { get; set; } public DateTime CheckInAt { get; set; } public DateTime? CheckOutAt { get; set; } } }