Files
17168ERP/web/App_Code/api/regionRoomBedController.cs
2025-09-25 15:18:34 +08:00

532 lines
18 KiB
C#
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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;
/// <summary>
/// regionRoomBedController 的摘要描述
/// </summary>
[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<IHttpActionResult> 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<IHttpActionResult> 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<FollowerBedResponse>();
// 按性别分组请求
var maleFollowers = request.PreBeds.Where(f => f.sex == "M").ToList();
var femaleFollowers = request.PreBeds.Where(f => f.sex == "F").ToList();
void AllocateByGender(List<FollowerBedResponse> 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<IHttpActionResult> 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<FollowerBedResponse> PreBeds { get; set; }
public string OrderNo { get; set; }
public DateTime CheckInAt { get; set; }
public DateTime? CheckOutAt { get; set; }
}
}