485 lines
20 KiB
C#
485 lines
20 KiB
C#
/***********************************************************************
|
||
* Project: CoreCms
|
||
* ProjectName: 核心内容管理系统
|
||
* Web: https://www.corecms.net
|
||
* Author: 大灰灰
|
||
* Email: jianweie@163.com
|
||
* CreateTime: 2025/9/2 12:47:30
|
||
* Description: 暂无
|
||
***********************************************************************/
|
||
|
||
using System;
|
||
using System.Collections.Generic;
|
||
using System.Linq.Expressions;
|
||
using System.Threading.Tasks;
|
||
|
||
using CoreCms.Net.Caching.AutoMate.RedisCache;
|
||
using CoreCms.Net.Configuration;
|
||
using CoreCms.Net.IRepository;
|
||
using CoreCms.Net.IRepository.UnitOfWork;
|
||
using CoreCms.Net.IServices;
|
||
using CoreCms.Net.Model.Entities;
|
||
using CoreCms.Net.Model.ViewModels.Basics;
|
||
using CoreCms.Net.Model.ViewModels.UI;
|
||
using CoreCms.Net.Model.ViewModels.SQ;
|
||
using CoreCms.Net.Utility.Helper;
|
||
|
||
using SqlSugar;
|
||
using System.Linq;
|
||
|
||
|
||
namespace CoreCms.Net.Services
|
||
{
|
||
/// <summary>
|
||
/// 房间表 接口实现
|
||
/// </summary>
|
||
public class SQRoomsServices : BaseServices<SQRooms>, ISQRoomsServices
|
||
{
|
||
private readonly ISQRoomsRepository _dal;
|
||
private readonly IUnitOfWork _unitOfWork;
|
||
private readonly IRedisOperationRepository _redisOperationRepository;
|
||
private readonly ISQRoomPricingServices _pricingServices;
|
||
private readonly ISQReservationsServices _reservationsServices;
|
||
private readonly ISQRoomUnavailableTimesServices _unavailableTimesServices;
|
||
|
||
public SQRoomsServices(IUnitOfWork unitOfWork, ISQRoomsRepository dal,
|
||
IRedisOperationRepository redisOperationRepository,
|
||
ISQRoomPricingServices pricingServices,
|
||
ISQReservationsServices reservationsServices,
|
||
ISQRoomUnavailableTimesServices unavailableTimesServices)
|
||
{
|
||
this._dal = dal;
|
||
base.BaseDal = dal;
|
||
_unitOfWork = unitOfWork;
|
||
_redisOperationRepository = redisOperationRepository;
|
||
_pricingServices = pricingServices;
|
||
_reservationsServices = reservationsServices;
|
||
_unavailableTimesServices = unavailableTimesServices;
|
||
}
|
||
|
||
#region 实现重写增删改查操作==========================================================
|
||
|
||
/// <summary>
|
||
/// 重写异步插入方法
|
||
/// </summary>
|
||
/// <param name="entity">实体数据</param>
|
||
/// <returns></returns>
|
||
public new async Task<AdminUiCallBack> InsertAsync(SQRooms entity)
|
||
{
|
||
return await _dal.InsertAsync(entity);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 重写异步更新方法方法
|
||
/// </summary>
|
||
/// <param name="entity"></param>
|
||
/// <returns></returns>
|
||
public new async Task<AdminUiCallBack> UpdateAsync(SQRooms entity)
|
||
{
|
||
return await _dal.UpdateAsync(entity);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 重写异步更新方法方法
|
||
/// </summary>
|
||
/// <param name="entity"></param>
|
||
/// <returns></returns>
|
||
public new async Task<AdminUiCallBack> UpdateAsync(List<SQRooms> entity)
|
||
{
|
||
return await _dal.UpdateAsync(entity);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 重写删除指定ID的数据
|
||
/// </summary>
|
||
/// <param name="id"></param>
|
||
/// <returns></returns>
|
||
public new async Task<AdminUiCallBack> DeleteByIdAsync(object id)
|
||
{
|
||
return await _dal.DeleteByIdAsync(id);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 重写删除指定ID集合的数据(批量删除)
|
||
/// </summary>
|
||
/// <param name="ids"></param>
|
||
/// <returns></returns>
|
||
public new async Task<AdminUiCallBack> DeleteByIdsAsync(int[] ids)
|
||
{
|
||
return await _dal.DeleteByIdsAsync(ids);
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 获取缓存的所有数据==========================================================
|
||
|
||
/// <summary>
|
||
/// 获取缓存的所有数据
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
public async Task<List<SQRooms>> GetCaChe()
|
||
{
|
||
return await _dal.GetCaChe();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 更新cache
|
||
/// </summary>
|
||
public async Task<List<SQRooms>> UpdateCaChe()
|
||
{
|
||
return await _dal.UpdateCaChe();
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 重写根据条件查询分页数据
|
||
/// <summary>
|
||
/// 重写根据条件查询分页数据
|
||
/// </summary>
|
||
/// <param name="predicate">判断集合</param>
|
||
/// <param name="orderByType">排序方式</param>
|
||
/// <param name="pageIndex">当前页面索引</param>
|
||
/// <param name="pageSize">分布大小</param>
|
||
/// <param name="orderByExpression"></param>
|
||
/// <param name="blUseNoLock">是否使用WITH(NOLOCK)</param>
|
||
/// <returns></returns>
|
||
public new async Task<IPageList<SQRooms>> QueryPageAsync(Expression<Func<SQRooms, bool>> predicate,
|
||
Expression<Func<SQRooms, object>> orderByExpression, OrderByType orderByType, int pageIndex = 1,
|
||
int pageSize = 20, bool blUseNoLock = false)
|
||
{
|
||
return await _dal.QueryPageAsync(predicate, orderByExpression, orderByType, pageIndex, pageSize, blUseNoLock);
|
||
}
|
||
|
||
/// <summary>
|
||
///
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
public async Task<List<SQRooms>> GetRoomList()
|
||
{
|
||
var key = $"room:GetRoomList";
|
||
var list = await _redisOperationRepository.Get<List<SQRooms>>(key);
|
||
if (list == null)
|
||
{
|
||
list = await _dal.QueryAsync(true);
|
||
await _redisOperationRepository.Set(key, list, TimeSpan.FromSeconds(60));
|
||
}
|
||
return list;
|
||
}
|
||
#endregion
|
||
|
||
#region 按时段预约相关方法
|
||
|
||
/// <summary>
|
||
/// 获取房间列表及时段状态
|
||
/// </summary>
|
||
public async Task<List<SQRoomListDto>> GetRoomListWithSlotsAsync(DateTime date, bool showOnlyAvailable = false, int? currentTimeSlot = null)
|
||
{
|
||
var dayStart = date.Date;
|
||
var dayEnd = dayStart.AddDays(1).AddSeconds(-1);
|
||
var now = DateTime.Now;
|
||
|
||
// 查询所有可用房间
|
||
var allRooms = await _dal.QueryListByClauseAsync(r => r.status == true, r => r.sort_order ?? r.id, OrderByType.Asc);
|
||
if (allRooms == null || allRooms.Count == 0)
|
||
{
|
||
return new List<SQRoomListDto>();
|
||
}
|
||
|
||
var result = new List<SQRoomListDto>();
|
||
|
||
foreach (var room in allRooms)
|
||
{
|
||
var roomDto = new SQRoomListDto
|
||
{
|
||
id = room.id,
|
||
name = room.name,
|
||
room_type_name = room.room_type_name ?? room.room_type,
|
||
image_url = room.image_url,
|
||
capacity = room.capacity,
|
||
description = room.description,
|
||
status = GlobalConstVars.RoomStatusAvailable,
|
||
is_available = true,
|
||
can_reserve = false,
|
||
time_slots = new List<SQTimeSlotDto>()
|
||
};
|
||
|
||
// 获取房间所有时段的价格配置
|
||
var pricingList = await _pricingServices.GetRoomAllPricingAsync(room.id, date);
|
||
|
||
// 查询当天的预约记录
|
||
var reservations = await _reservationsServices.QueryListByClauseAsync(
|
||
r => r.room_id == room.id && r.status < 3 && r.start_time < dayEnd && r.end_time > dayStart,
|
||
r => r.start_time, OrderByType.Asc);
|
||
|
||
// 查询不可用时段
|
||
var unavailableTimes = await _unavailableTimesServices.QueryListByClauseAsync(
|
||
t => t.room_id == room.id && t.start_time < dayEnd && t.end_time > dayStart,
|
||
t => t.start_time, OrderByType.Asc);
|
||
|
||
// 构建4个时段的状态
|
||
for (int slotType = 0; slotType <= 3; slotType++)
|
||
{
|
||
var (slotStart, slotEnd) = TimeSlotHelper.GetTimeRange(dayStart, slotType);
|
||
var pricing = pricingList.FirstOrDefault(p => p.time_slot_type == slotType);
|
||
|
||
var slotDto = new SQTimeSlotDto
|
||
{
|
||
slot_type = slotType,
|
||
slot_name = TimeSlotHelper.GetTimeSlotName(slotType),
|
||
status = GlobalConstVars.RoomStatusAvailable,
|
||
standard_price = pricing?.standard_price ?? 0,
|
||
member_price = pricing?.member_price ?? 0,
|
||
price_desc_standard = pricing?.price_desc_standard,
|
||
price_desc_member = pricing?.price_desc_member
|
||
};
|
||
|
||
// 检查时段是否已过期(仅当查询日期是今天时)
|
||
bool isPassed = dayStart.Date == DateTime.Today && TimeSlotHelper.IsTimeSlotPassed(dayStart, slotType);
|
||
|
||
if (isPassed)
|
||
{
|
||
// 时段已过期,标记为不可用
|
||
slotDto.status = GlobalConstVars.RoomStatusUnavailable;
|
||
}
|
||
else
|
||
{
|
||
// 检查不可用时段,并获取禁用类型
|
||
var unavailableRecord = unavailableTimes?.FirstOrDefault(u =>
|
||
(u.time_slot_type.HasValue && u.time_slot_type.Value == slotType) ||
|
||
(u.start_time < slotEnd && u.end_time > slotStart));
|
||
|
||
if (unavailableRecord != null)
|
||
{
|
||
slotDto.status = GlobalConstVars.RoomStatusUnavailable;
|
||
slotDto.unavailable_type = unavailableRecord.type; // 设置禁用类型
|
||
}
|
||
else
|
||
{
|
||
// 检查是否已预约
|
||
bool isReserved = reservations?.Any(r =>
|
||
r.start_time < slotEnd && r.end_time > slotStart) ?? false;
|
||
|
||
if (isReserved)
|
||
{
|
||
slotDto.status = GlobalConstVars.RoomStatusReserved;
|
||
// 检查是否正在使用中
|
||
bool isUsing = reservations.Any(r => r.start_time <= now && r.end_time > now);
|
||
if (isUsing && dayStart.Date == DateTime.Today)
|
||
{
|
||
slotDto.status = GlobalConstVars.RoomStatusUsing;
|
||
roomDto.status = GlobalConstVars.RoomStatusUsing;
|
||
roomDto.is_available = false;
|
||
}
|
||
}
|
||
else if (slotDto.status == GlobalConstVars.RoomStatusAvailable)
|
||
{
|
||
roomDto.can_reserve = true;
|
||
}
|
||
}
|
||
}
|
||
|
||
roomDto.time_slots.Add(slotDto);
|
||
}
|
||
|
||
// 设置价格说明(使用第一个可用时段的价格作为展示)
|
||
var firstPricing = pricingList.FirstOrDefault();
|
||
if (firstPricing != null)
|
||
{
|
||
roomDto.standard_price_desc = firstPricing.price_desc_standard;
|
||
roomDto.member_price_desc = firstPricing.price_desc_member;
|
||
}
|
||
|
||
// 如果启用筛选,只显示指定时段可用的房间
|
||
if (showOnlyAvailable && currentTimeSlot.HasValue)
|
||
{
|
||
var currentSlot = roomDto.time_slots.FirstOrDefault(s => s.slot_type == currentTimeSlot.Value);
|
||
if (currentSlot == null || currentSlot.status != GlobalConstVars.RoomStatusAvailable)
|
||
{
|
||
continue;
|
||
}
|
||
}
|
||
|
||
result.Add(roomDto);
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取房间详情
|
||
/// </summary>
|
||
public async Task<SQRoomDetailDto> GetRoomDetailAsync(int roomId, DateTime date)
|
||
{
|
||
// 1. 查询房间信息
|
||
var room = await _dal.QueryByIdAsync(roomId);
|
||
if (room == null)
|
||
{
|
||
return null;
|
||
}
|
||
|
||
// 2. 确定查询日期范围
|
||
var dayStart = date.Date;
|
||
var dayEnd = dayStart.AddDays(1).AddSeconds(-1);
|
||
var now = DateTime.Now;
|
||
|
||
// 3. 构建房间详情DTO
|
||
var roomDetail = new SQRoomDetailDto
|
||
{
|
||
id = room.id,
|
||
name = room.name,
|
||
room_type = room.room_type,
|
||
room_type_name = room.room_type_name ?? room.room_type,
|
||
image_url = room.image_url,
|
||
price_per_hour = room.price_per_hour,
|
||
capacity = room.capacity,
|
||
description = room.description,
|
||
status = GlobalConstVars.RoomStatusAvailable,
|
||
is_available = true,
|
||
can_reserve = false
|
||
};
|
||
|
||
// 4. 解析多图(image_detailed_url,按逗号分割)
|
||
if (!string.IsNullOrEmpty(room.image_detailed_url))
|
||
{
|
||
roomDetail.images = room.image_detailed_url
|
||
.Split(',', StringSplitOptions.RemoveEmptyEntries)
|
||
.Select(img => img.Trim())
|
||
.ToList();
|
||
}
|
||
|
||
// 5. 解析设施列表(amenities,按逗号分割)
|
||
if (!string.IsNullOrEmpty(room.amenities))
|
||
{
|
||
roomDetail.amenities = room.amenities
|
||
.Split(',', StringSplitOptions.RemoveEmptyEntries)
|
||
.Select(a => a.Trim())
|
||
.ToList();
|
||
}
|
||
|
||
// 6. 获取房间所有时段的价格配置
|
||
var pricingList = await _pricingServices.GetRoomAllPricingAsync(room.id, date);
|
||
|
||
// 7. 查询当天的预约记录
|
||
var reservations = await _reservationsServices.QueryListByClauseAsync(
|
||
r => r.room_id == roomId && r.status < 3 && r.start_time < dayEnd && r.end_time > dayStart,
|
||
r => r.start_time, OrderByType.Asc);
|
||
|
||
// 8. 查询不可用时段
|
||
var unavailableTimes = await _unavailableTimesServices.QueryListByClauseAsync(
|
||
t => t.room_id == roomId && t.start_time < dayEnd && t.end_time > dayStart,
|
||
t => t.start_time, OrderByType.Asc);
|
||
|
||
// 9. 构建4个时段的状态(与列表接口逻辑一致,并整合预约信息)
|
||
for (int slotType = 0; slotType <= 3; slotType++)
|
||
{
|
||
var (slotStart, slotEnd) = TimeSlotHelper.GetTimeRange(dayStart, slotType);
|
||
var pricing = pricingList.FirstOrDefault(p => p.time_slot_type == slotType);
|
||
|
||
var slotDto = new SQTimeSlotDto
|
||
{
|
||
slot_type = slotType,
|
||
slot_name = TimeSlotHelper.GetTimeSlotName(slotType),
|
||
status = GlobalConstVars.RoomStatusAvailable,
|
||
standard_price = pricing?.standard_price ?? 0,
|
||
member_price = pricing?.member_price ?? 0,
|
||
price_desc_standard = pricing?.price_desc_standard,
|
||
price_desc_member = pricing?.price_desc_member
|
||
};
|
||
|
||
// 检查时段是否已过期(仅当查询日期是今天时)
|
||
bool isPassed = dayStart.Date == DateTime.Today && TimeSlotHelper.IsTimeSlotPassed(dayStart, slotType);
|
||
|
||
if (isPassed)
|
||
{
|
||
// 时段已过期,标记为不可用
|
||
slotDto.status = GlobalConstVars.RoomStatusUnavailable;
|
||
}
|
||
else
|
||
{
|
||
// 检查不可用时段
|
||
bool isUnavailable = unavailableTimes?.Any(u =>
|
||
(u.time_slot_type.HasValue && u.time_slot_type.Value == slotType) ||
|
||
(u.start_time < slotEnd && u.end_time > slotStart)) ?? false;
|
||
|
||
if (isUnavailable)
|
||
{
|
||
slotDto.status = GlobalConstVars.RoomStatusUnavailable;
|
||
}
|
||
else
|
||
{
|
||
// 查找属于该时段的预约(预约时间与时段有重叠)
|
||
var slotReservations = reservations?.Where(r =>
|
||
r.start_time < slotEnd && r.end_time > slotStart).ToList() ?? new List<SQReservations>();
|
||
|
||
if (slotReservations.Count > 0)
|
||
{
|
||
slotDto.status = GlobalConstVars.RoomStatusReserved;
|
||
// 检查是否正在使用中
|
||
bool isUsing = slotReservations.Any(r => r.start_time <= now && r.end_time > now);
|
||
if (isUsing && dayStart.Date == DateTime.Today)
|
||
{
|
||
slotDto.status = GlobalConstVars.RoomStatusUsing;
|
||
roomDetail.status = GlobalConstVars.RoomStatusUsing;
|
||
roomDetail.is_available = false;
|
||
}
|
||
|
||
// 填充该时段内的预约信息
|
||
slotDto.reservations = slotReservations.Select(r => new SQTimeSlotReservationDto
|
||
{
|
||
reservation_id = r.id,
|
||
start_time = r.start_time.ToString("yyyy-MM-dd HH:mm:ss"),
|
||
end_time = r.end_time.ToString("yyyy-MM-dd HH:mm:ss"),
|
||
status = r.status,
|
||
title = r.title,
|
||
game_type = r.game_type
|
||
}).ToList();
|
||
}
|
||
else if (slotDto.status == GlobalConstVars.RoomStatusAvailable)
|
||
{
|
||
roomDetail.can_reserve = true;
|
||
}
|
||
}
|
||
}
|
||
|
||
roomDetail.time_slots.Add(slotDto);
|
||
}
|
||
|
||
// 10. 设置价格说明(使用第一个可用时段的价格作为展示)
|
||
var firstPricing = pricingList.FirstOrDefault();
|
||
if (firstPricing != null)
|
||
{
|
||
roomDetail.standard_price_desc = firstPricing.price_desc_standard;
|
||
roomDetail.member_price_desc = firstPricing.price_desc_member;
|
||
}
|
||
|
||
// 11. 填充今日预约情况(保留兼容性,但建议使用 time_slots 中的 reservations)
|
||
if (reservations != null && reservations.Count > 0)
|
||
{
|
||
roomDetail.today_reservations = reservations.Select(r => new SQRoomDetailReservationDto
|
||
{
|
||
start_time = r.start_time.ToString("yyyy-MM-dd HH:mm:ss"),
|
||
end_time = r.end_time.ToString("yyyy-MM-dd HH:mm:ss"),
|
||
status = r.status
|
||
}).ToList();
|
||
}
|
||
|
||
// 12. 最终状态检查:如果有任何不可用时段,设置整体状态
|
||
if (unavailableTimes != null && unavailableTimes.Count > 0)
|
||
{
|
||
// 只有当所有时段都不可用时,才设置为 unavailable
|
||
bool allUnavailable = roomDetail.time_slots.All(s => s.status == GlobalConstVars.RoomStatusUnavailable);
|
||
if (allUnavailable)
|
||
{
|
||
roomDetail.status = GlobalConstVars.RoomStatusUnavailable;
|
||
roomDetail.is_available = false;
|
||
}
|
||
}
|
||
|
||
return roomDetail;
|
||
}
|
||
|
||
#endregion
|
||
|
||
}
|
||
}
|