using DocumentFormat.OpenXml.Drawing;
using Model;
using OfficeOpenXml.FormulaParsing.Excel.Functions.DateTime;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using System.Web;
using System.Web.Http;
///
/// guadanOrderGuest 的摘要描述
///
[ezAuthorize]
public class guadanOrderGuestController : ApiController
{
private Model.ezEntities _db = new Model.ezEntities();
[HttpGet]
[Route("api/guadanorderguest/get")]
public async Task Get()
{
var data = await _db.GuaDanOrderGuest.ToListAsync();
return Ok(data);
}
[HttpGet]
[Route("api/guadanorderguest/getbyorderno")]
public async Task getByOrderNo(string orderNo)
{
// 先查資料庫,不做格式化
var qry = await _db.GuaDanOrderGuest
.Where(a => a.GuaDanOrderNo == orderNo && a.IsDeleted == false && a.RegionRoomBedStatus.Code != "404")
.ToListAsync();
// 拉到記憶體後再處理日期
var data = qry.Select(a => new guadan_order_guest_display_dto
{
Uuid = a.Uuid,
name = null,
followerNum = a.FollowerNum,
roomUuid = a.RoomUuid,
bedUuid = a.BedUuid,
checkinat = a.CheckInAt.HasValue ? a.CheckInAt.Value.ToString("yyyy-MM-dd") : null,
checkoutat = a.CheckOutAt.HasValue ? a.CheckOutAt.Value.ToString("yyyy-MM-dd") : null,
phone = null,
roomName = a.Room.Name,
bedName = a.RegionRoomBed.Name,
orderNo = a.GuaDanOrderNo,
follower = a.followers == null ? null : new FollowerDto
{
num = a.followers.num,
u_name = a.followers.u_name
},
statuscode = a.StatusCode,
statusName = a.RegionRoomBedStatus?.Name,
}).ToList();
return Ok(data);
}
[HttpPost]
[Route("api/guadanorderguest/create")]
public async Task create([FromBody] guadan_order_guest_dto model)
{
if (model == null)
return BadRequest("");
/*if(model.statuscode == null)
{
return BadRequest("狀態不能為空");
}*/
// 驗證床位與蓮友
var bed = _db.RegionRoomBed.Find(model.bedUuid.Value);
if (model.followerNum.HasValue && model.bedUuid.HasValue)
{
var follower = _db.followers.Find(model.followerNum.Value);
if (bed == null || follower == null)
return BadRequest("床位或蓮友不存在");
bool isMaleFollower;
if (follower.sex == "男眾")
isMaleFollower = true;
else if (follower.sex == "女眾")
isMaleFollower = false;
else
return BadRequest("蓮友性別未知");
if (bed.Gender != isMaleFollower)
return BadRequest("床位性別與蓮友性別不同");
}
if (!model.bedUuid.HasValue)
return BadRequest("床位 UUID 不能為空");
if (!model.checkInAt.HasValue)
return BadRequest("入住時間不能為空");
// 長期占用處理:checkOutAt 可為 null
DateTime? checkOut = model.checkOutAt.Value.Date;
if (checkOut.HasValue && model.checkInAt > checkOut)
return BadRequest("掛單結束時間不能再開始時間之前");
if (model.checkInAt == model.checkOutAt)
{
return BadRequest("掛單結束時間和開始時間不能是同一天");
}
// 檢查床位可用性
var bedIsCanUse = await RegionAndRoomAndBedSchedule.IsBedAvailableAsync(
_db,
model.bedUuid.Value,
model.checkInAt.Value.Date,
checkOut
);
if (!bedIsCanUse)
return BadRequest("床位在該時間段內已被占用");
if (model.followerNum.HasValue)
{
if (_db.GuaDanOrderGuest.Any(a => a.FollowerNum == model.followerNum
&& a.GuaDanOrderNo == model.orderNo
&& a.StatusCode != "404"
))
return BadRequest("該蓮友已經在該掛單中");
}
//建立訂單的的時候,狀態只能是401或者402
var targetStatus = _db.RegionRoomBedStatus.Where(a => a.Code == GuaDanOrderGuest.STATUS_BOOKED).FirstOrDefault();
if (targetStatus == null)
{
return Content(HttpStatusCode.PreconditionFailed, "找不到目標狀態,請先建立對應狀態");
}
var guest = new GuaDanOrderGuest
{
GuaDanOrderNo = model.orderNo,
FollowerNum = model.followerNum,
RoomUuid = model.roomUuid,
BedUuid = model.bedUuid,
CheckInAt = model.checkInAt?.Date,
CheckOutAt = checkOut,
Uuid = Guid.NewGuid(),
StatusCode = GuaDanOrderGuest.STATUS_BOOKED,
};
_db.GuaDanOrderGuest.Add(guest);
await _db.SaveChangesAsync();
// 生成每日排程
if (checkOut.HasValue)
{
int totalDays = (checkOut.Value - model.checkInAt.Value.Date).Days;
for (int i = 0; i < totalDays; i++)
{
var scheduleDate = model.checkInAt.Value.Date.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);
}
}
else
{
// 長期占用,ScheduleDate = null
var schedul = new RegionAndRoomAndBedSchedule
{
Title = "掛單",
Description = "床位掛單(長期占用)",
ScheduleDate = null,
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);
}
await _db.SaveChangesAsync();
return Ok();
}
[HttpPost]
[Route("api/guadanorderguest/update")]
public async Task update([FromBody] guadan_order_guest_dto model)
{
if (model == null)
return BadRequest("");
// 驗證床位與蓮友
if (model.followerNum.HasValue && model.bedUuid.HasValue)
{
var bed = _db.RegionRoomBed.Find(model.bedUuid.Value);
var follower = _db.followers.Find(model.followerNum.Value);
if (bed == null || follower == null)
return BadRequest("床位或蓮友不存在");
bool isMaleFollower;
if (follower.sex == "男眾") isMaleFollower = true;
else if (follower.sex == "女眾") isMaleFollower = false;
else return BadRequest("蓮友性別未知");
if (bed.Gender != isMaleFollower)
return BadRequest("床位性別與蓮友性別不同");
}
if (!model.bedUuid.HasValue)
return BadRequest("床位 UUID 不能為空");
if (!model.checkInAt.HasValue)
return BadRequest("入住時間不能為空");
// 長期占用處理
DateTime? checkOut = model.checkOutAt?.Date;
if (checkOut.HasValue && model.checkInAt > checkOut)
return BadRequest("掛單結束時間不能再開始時間之前");
var guest = await _db.GuaDanOrderGuest.FindAsync(model.Uuid);
if (guest == null) return BadRequest();
// 檢查床位可用性
var bedIsCanUse = await RegionAndRoomAndBedSchedule.IsBedAvailableAsync(
_db,
model.bedUuid.Value,
model.checkInAt.Value.Date,
checkOut
);
if (!bedIsCanUse && guest.BedUuid != model.bedUuid)
return BadRequest("床位在該時間段內已被占用");
if (model.followerNum.HasValue)
{
bool exists = await _db.GuaDanOrderGuest
.Where(a => a.FollowerNum == model.followerNum
&& a.GuaDanOrderNo == model.orderNo
&& a.Uuid != model.Uuid)
.AnyAsync();
if (exists) return BadRequest("該蓮友已經在該掛單中");
}
/*var targetStatus = _db.RegionRoomBedStatus.Find(model.statuscode);
if (targetStatus == null)
{
return BadRequest("目標狀態不存在");
}
if(!StatusTransitionManager.CanTransition(guest.StatusCode,targetStatus.Code))
{
return BadRequest("狀態的變化不合法");
}*/
// 更新掛單基本資料
guest.FollowerNum = model.followerNum;
guest.RoomUuid = model.roomUuid;
guest.BedUuid = model.bedUuid;
guest.CheckInAt = model.checkInAt?.Date;
guest.CheckOutAt = checkOut;
//guest.StatusCode = model.statuscode;
//更新的時候不能更新狀態,狀態都用單獨的操作api控制
// 刪除原有每日排程
var oldSchedules = _db.RegionAndRoomAndBedSchedule
.Where(s => s.GuaDanOrderNo == guest.GuaDanOrderNo && s.TargetUuid == guest.BedUuid)
.ToList();
_db.RegionAndRoomAndBedSchedule.RemoveRange(oldSchedules);
// 重新生成每日排程
if (checkOut.HasValue)
{
int totalDays = (checkOut.Value - model.checkInAt.Value.Date).Days;
for (int i = 0; i < totalDays; i++)
{
var date = model.checkInAt.Value.Date.AddDays(i);
var schedul = new RegionAndRoomAndBedSchedule
{
Title = "掛單",
Description = "床位掛單",
ScheduleDate = date,
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);
}
}
else
{
// 長期占用
var schedul = new RegionAndRoomAndBedSchedule
{
Title = "掛單",
Description = "床位掛單(長期占用)",
ScheduleDate = null,
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);
}
await _db.SaveChangesAsync();
await _db.SaveChangesAsync();
return Ok();
}
[HttpPost]
[Route("api/guadanorderguest/xuzhu")]
public async Task ExtendStay([FromBody] XuZhuModel model)
{
//續住方法
if (model == null)
return BadRequest("請求數據為空");
if (model.GuestUuid == Guid.Empty || model.GuestBedUuid == Guid.Empty)
return BadRequest("GuestUuid 或 GuestBedUuid 無效");
var guest = await _db.GuaDanOrderGuest.FindAsync(model.GuestUuid);
if (guest == null)
{
return BadRequest("掛單不存在");
}
if (guest.BedUuid != model.GuestBedUuid)
{
return BadRequest("床位不正確");
}
var bedIsCanUse = await RegionAndRoomAndBedSchedule.IsBedAvailableAsync(_db, model.GuestBedUuid, model.CurrentCheckoutDate, model.NewCheckoutDate);
if (!bedIsCanUse)
{
return BadRequest("該床位在續住時間段內被預定,無法續住");
}
var newStartDate = model.CurrentCheckoutDate.Date;
var newEndDate = model.NewCheckoutDate.Date.AddDays(-1);
if (newEndDate < newStartDate)
return BadRequest("續住日期區間無效");
for (var date = newStartDate; date <= newEndDate; date = date.AddDays(1))
{
var newSchedule = new RegionAndRoomAndBedSchedule
{
GuaDanOrderNo = guest.GuaDanOrderNo,
Uuid = Guid.NewGuid(),
TargetUuid = model.GuestBedUuid,
GuaDanOrderGuestUuid = model.GuestUuid,
ScheduleDate = date,
Title = "續住掛單", // 一天一條,開始和結束是同一天
Description = "續住掛單",
UseType = 30,
CreatedAt = DateTime.UtcNow
};
_db.RegionAndRoomAndBedSchedule.Add(newSchedule);
}
guest.CheckOutAt = model.NewCheckoutDate.Date;
await _db.SaveChangesAsync(); // 保存資料庫操作
return Ok(new { message = "續住成功" });
}
[HttpPost]
[Route("api/guadanorderguest/cancel")]
public async Task CancelGuadanGuest([FromUri] Guid uuid)
{
var guest = await _db.GuaDanOrderGuest.FindAsync(uuid);
if (guest == null)
return NotFound();
if (guest.StatusCode == "404")
return BadRequest("該掛單已取消,無需再取消");
if (!StatusTransitionManager.CanTransition(guest.StatusCode, "404"))
{
return BadRequest("當前狀態不能取消");
}
var cancelStatus = await _db.RegionRoomBedStatus
.FirstOrDefaultAsync(a => a.Code == "404");
if (cancelStatus == null)
return Content(HttpStatusCode.PreconditionFailed, "找不到取消狀態(Code=404),請先建立對應狀態");
//把狀態設置為取消
using (var tx = _db.Database.BeginTransaction())
{
try
{
// 1) 更新 guest
guest.StatusCode = "404";
// 2) 取消相關排程(常見做法:只取消未來&未取消的)
var today = DateTime.Today;
var schedules = await _db.RegionAndRoomAndBedSchedule
.Where(s => s.GuaDanOrderGuestUuid == guest.Uuid
&& s.IsCancel == false
//&& s.ScheduleDate >= today
)
// ✅ 只取消今天與未來的,能取消就代表未入住,
// 未入住就要全部取消,如果是入住後提前退房,
// 就要取消未來的,取消未來的時候要注意今日是否包含的問題
.ToListAsync();
foreach (var s in schedules)
{
s.IsCancel = true;
}
// 3) 釋放占用資源(若有床位/房間占用紀錄)
if (guest.BedUuid != null)
{
// 先抓到目標狀態
var freeStatus = await _db.RegionRoomBedStatus
.FirstOrDefaultAsync(a => a.Code == "101");
if (freeStatus == null)
return Content(HttpStatusCode.PreconditionFailed, "找不到床位狀態 Code=101");
if (guest.RegionRoomBed != null)
{
guest.RegionRoomBed.StatusCode = freeStatus.Code;
}
}
await _db.SaveChangesAsync();
tx.Commit();
return Ok(new
{
message = "取消成功",
guestUuid = guest.Uuid,
canceledSchedules = schedules.Count
});
}
catch (DbUpdateConcurrencyException)
{
tx.Rollback();
return StatusCode(HttpStatusCode.PreconditionFailed); // 或自訂訊息
}
catch (Exception ex)
{
tx.Rollback();
return InternalServerError(ex);
}
}
}
[HttpPost]
[Route("api/guadanorderguest/checkout")]
public IHttpActionResult CheckoutGuadanOrderGuest(Guid uuid)
{
DbContextTransaction transaction = null;
try
{
transaction = _db.Database.BeginTransaction(); // 開啟事務
// 1️⃣ 取得該筆掛單
var guest = _db.GuaDanOrderGuest
.Where(a => a.Uuid == uuid)
.Where(a => !a.IsDeleted && a.StatusCode != "404")
.FirstOrDefault();
if (guest == null)
return NotFound();
// 2️⃣ 標記為已退房
var targetStatus = _db.RegionRoomBedStatus
.Where(a => a.Code == "403")
.FirstOrDefault();
if (targetStatus == null)
return Content(HttpStatusCode.PreconditionFailed, "找不到退房狀態(Code=403),請先建立對應狀態");
if (!StatusTransitionManager.CanTransition(guest.StatusCode, targetStatus.Code))
return BadRequest("掛單狀態轉換不對");
if (!StatusTransitionManager.CanTransition(guest.RegionRoomBed.StatusCode, "101"))
return BadRequest("床位掛單狀態轉換不對");
guest.StatusCode = targetStatus.Code;
guest.RegionRoomBed.StatusCode = "101";
//更新未來排程為取消
var latestCheckoutStr = _db.GuadanTimeSetting
.Select(a => a.LatestCheckOut) // 字串 "HH:mm"
.FirstOrDefault();
TimeSpan? latestCheckoutTime = null;
if (!string.IsNullOrEmpty(latestCheckoutStr))
{
// 嘗試解析字串
if (TimeSpan.TryParse(latestCheckoutStr, out var ts))
{
latestCheckoutTime = ts;
}
}
var futureSchedules = _db.RegionAndRoomAndBedSchedule
.Where(s => s.GuaDanOrderGuestUuid == guest.Uuid);
if (latestCheckoutTime == null || DateTime.Now.TimeOfDay > latestCheckoutTime)
{
// 包含今天
futureSchedules = futureSchedules.Where(s => s.ScheduleDate > DateTime.Today);
}
else
{
// 不包含今天
futureSchedules = futureSchedules.Where(s => s.ScheduleDate >= DateTime.Today);
}
foreach (var schedule in futureSchedules)
{
schedule.IsCancel = true;
}
// 4️⃣ 保存所有變更
_db.SaveChanges();
// 5️⃣ 提交事務
transaction.Commit();
return Ok(new { message = "退房完成", guestUuid = guest.Uuid });
}
catch (Exception ex)
{
if (transaction != null)
transaction.Rollback(); // 回滾事務
return InternalServerError(ex);
}
finally
{
if (transaction != null)
transaction.Dispose();
}
}
[HttpPost]
[Route("api/guadanorderguest/checkin")]
public IHttpActionResult CheckinGuadanGuest([FromUri] Guid uuid)
{
if (uuid == Guid.Empty)
return BadRequest("uuid不能為空");
// 獲取掛單客人
var guest = _db.GuaDanOrderGuest
.Include(g => g.RegionRoomBedStatus) // 包含導航屬性
.FirstOrDefault(g => g.Uuid == uuid);
if (guest == null)
return NotFound();
string currentStatus = guest.StatusCode;
// 判斷狀態流轉是否合法
if (!StatusTransitionManager.CanTransition(currentStatus, GuaDanOrderGuest.STATUS_CHECKED_IN))
{
return BadRequest("當前狀態不允許入住");
}
// ---------- 新增:檢查今天是否在排程表 ----------
var today = DateTime.Today;
bool hasScheduleToday = _db.RegionAndRoomAndBedSchedule
.Any(s => s.GuaDanOrderGuestUuid == guest.Uuid && s.ScheduleDate == today);
if (!hasScheduleToday)
{
return BadRequest("不在入住時間段內,無法入住");
}
try
{
// 更新狀態
guest.StatusCode = GuaDanOrderGuest.STATUS_CHECKED_IN;
// 如果需要,更新床位狀態,比如變為占用
if (guest.BedUuid != null)
{
var bed = _db.RegionRoomBed.FirstOrDefault(b => b.Uuid == guest.BedUuid);
if (bed == null)
{
return BadRequest("入住床位不存在");
}
if (StatusTransitionManager.CanTransition(bed.StatusCode, "102")) // 102 = 占用
{
bed.StatusCode = "102";
}
else
{
return BadRequest($"當前床位狀態:{bed.RegionRoomBedStatus.Name} 不能入住");
}
}
else if (guest.BedUuid == null)
{
return BadRequest("入住床位不存在");
}
_db.SaveChanges();
return Ok(new { message = "入住成功", statusCode = guest.StatusCode });
}
catch (Exception ex)
{
return InternalServerError(ex);
}
}
public class guadan_order_guest_dto
{
public Guid? Uuid { get; set; }
public int? followerNum { get; set; }
public string orderNo { get; set; }
public Guid? roomUuid { get; set; }
public Guid? bedUuid { get; set; }
public DateTime? checkInAt { get; set; }
public DateTime? checkOutAt { get; set; }
public string statuscode { get; set; }
}
public class guadan_order_guest_display_dto
{
public Guid? Uuid { get; set; }
public int? followerNum { get; set; }
public string orderNo { get; set; }
public string name { get; set; }
public Guid? roomUuid { get; set; }
public Guid? bedUuid { get; set; }
public string checkinat { get; set; }
public string checkoutat { get; set; }
public int? gender { get; set; }
public string statuscode { get; set; }
public string statusName { get; set; }
public string phone { get; set; }
public string note { get; set; }
public string roomName { get; set; }
public string bedName { get; set; }
public bool iscancel { get; set; }
public FollowerDto follower { get; set; }
}
public class FollowerDto
{
public int num { get; set; }
public string u_name { get; set; }
}
public class XuZhuModel
{
public Guid GuestUuid { get; set; } // 不可為空
public Guid GuestBedUuid { get; set; } // 不可為空
public DateTime CurrentCheckoutDate { get; set; } // 當前退房時間
public DateTime NewCheckoutDate { get; set; } // 新退房時間
}
}