using AutoMapper; using CoreCms.Net.Auth.HttpContextUser; using CoreCms.Net.IRepository.UnitOfWork; using CoreCms.Net.IServices; using CoreCms.Net.Model.Entities; using CoreCms.Net.Model.Entities.Expression; using CoreCms.Net.Model.ViewModels.Basics; using CoreCms.Net.Model.ViewModels.SQ; using CoreCms.Net.Model.ViewModels.UI; using CoreCms.Net.Services; using CoreCms.Net.Services.SQ; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using NPOI.SS.Formula.Functions; using SqlSugar; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using CoreCms.Net.Utility.Extensions; using ToolGood.Words.internals; using NPOI.SS.Formula.PTG; using NPOI.OpenXmlFormats.Dml; using Humanizer; namespace CoreCms.Net.Web.WebApi.Controllers; /// /// 预约接口 /// [Route("api/[controller]/[action]")] [ApiController] public class SQController : ControllerBase { private readonly IWebHostEnvironment _webHostEnvironment; private readonly ISQReservationsServices _SQReservationsServices; private readonly ISQRoomsServices _SQRoomsServices; private readonly ISysDictionaryDataServices _sysDictionaryDataServices; private readonly ISysDictionaryServices _sysDictionaryServices; private readonly ISQReservationParticipantsServices _SQReservationParticipantsServices; private readonly IMapper _mapper; private readonly ICoreCmsUserServices _userServices; private readonly IHttpContextUser _user; private readonly SqlSugarScope _dbBase; private readonly ICoreCmsUserBlacklistServices _coreCmsUserBlacklistServices; private readonly ISQReservationReputationServices _sQReservationReputationServices; private readonly ISQReservationEvaluateServices _sQReservationEvaluateServices; private readonly ISQRoomUnavailableTimesServices _sQRoomUnavailableTimesServices; /// /// 构造函数 /// public SQController(IWebHostEnvironment webHostEnvironment , ISQReservationsServices SQReservationsServices , ISQRoomsServices SQRoomsServices , ISysDictionaryServices sysDictionaryServices , ISysDictionaryDataServices sysDictionaryDataServices , ISQReservationParticipantsServices sQReservationParticipantsServices , IMapper mapper , ICoreCmsUserServices userServices , IHttpContextUser user , IUnitOfWork unitOfWork , ICoreCmsUserBlacklistServices coreCmsUserBlacklistServices , ISQReservationEvaluateServices sQReservationEvaluateServices , ISQReservationReputationServices sQReservationReputationServices , ISQRoomUnavailableTimesServices sQRoomUnavailableTimesServices ) { _webHostEnvironment = webHostEnvironment; _SQReservationsServices = SQReservationsServices; _SQRoomsServices = SQRoomsServices; _sysDictionaryServices = sysDictionaryServices; _sysDictionaryDataServices = sysDictionaryDataServices; _SQReservationParticipantsServices = sQReservationParticipantsServices; _mapper = mapper; _userServices = userServices; _user = user; _dbBase = unitOfWork.GetDbClient(); _coreCmsUserBlacklistServices = coreCmsUserBlacklistServices; _sQReservationEvaluateServices = sQReservationEvaluateServices; _sQReservationReputationServices = sQReservationReputationServices; _sQRoomUnavailableTimesServices = sQRoomUnavailableTimesServices; } /// /// 我的预约记录 /// /// 0 参与者,1发起者 /// 起始页 /// 页大小 /// [HttpGet] [Authorize] public async Task GetMyReservation([FromQuery] int type = 0, [FromQuery] int index = 1, [FromQuery] int size = 10) { var userId = _user.ID; var list = await _dbBase.Ado.SqlQueryAsync($"select r.*,p.id pid, p.status pstatus,p.join_time, p.quit_time ,p.is_refund from SQReservationParticipants p inner join SQReservations r on p.reservation_id=r.id where p.user_id={userId} and p.role={type} order by p.id desc OFFSET {(index - 1) * size} ROWS FETCH NEXT {size} ROWS ONLY "); if (list != null && list.Count > 0) { var roomList = await _SQRoomsServices.GetRoomList(); // 转为基类列表 var baseList = list.Cast().ToList(); await baseList.LoadSQReservationParticipantsApiDto(userId, _dbBase, _coreCmsUserBlacklistServices, roomList, _mapper); } return new WebApiDto() { Data = list, Code = 0, }; } /// /// 获取正在进行的预约 /// /// [HttpGet] [Authorize] public async Task GetMyUseReservation() { var userId = _user.ID; var list = await _dbBase.Ado.SqlQueryAsync($"SELECT r.*, p.role AS Role, p.is_arrive, CASE WHEN r.status = 1 THEN 0 WHEN r.status = 2 THEN 1 WHEN r.status = 0 THEN 2 WHEN r.status = 3 THEN 3 END AS orderid FROM SQReservations r LEFT JOIN SQReservationParticipants p ON r.id = p.reservation_id WHERE r.status < 4 AND p.user_id = {userId} AND p.status = 0 AND DATEADD(day,2, end_time) > GETDATE() ORDER BY orderid asc, r.start_time Desc "); if (list != null && list.Count > 0) { var roomList = await _SQRoomsServices.GetRoomList(); // 转为基类列表 var baseList = list.Cast().ToList(); await baseList.LoadSQReservationParticipantsApiDto(userId, _dbBase, _coreCmsUserBlacklistServices, roomList, _mapper); } return new WebApiDto() { Data = list, Code = 0, }; } /// /// 首页预约列表 /// /// /// /// [HttpGet] public async Task GetReservationList([FromQuery] int pageIndex = 1, [FromQuery] int pageSize = 20) { var userId = _user.ID; var now = DateTime.Now; var roomList = await _SQRoomsServices.GetRoomList(); var where = PredicateBuilder.True(); where = where.And(it => it.end_time > now); where = where.And(it => it.status < 3); var list = await _SQReservationsServices.QueryPageAsync(where, it => it.start_time, OrderByType.Asc, pageIndex, pageSize, true); var pageList = _mapper.Map>(list); if (pageList != null && pageList.Count > 0) { // 转为基类列表 var baseList = pageList.Cast().ToList(); await baseList.LoadSQReservationParticipantsApiDto(userId, _dbBase, _coreCmsUserBlacklistServices, roomList, _mapper); } return new WebApiDto() { Data = pageList, Code = 0, Msg = "ok", }; } /// /// 获取预约评价 /// /// /// [HttpGet] [Authorize] public async Task GetEvaluateServices([FromQuery] int reId) { var userId = _user.ID; var list = await _sQReservationEvaluateServices.QueryListByClauseAsync(it => it.reservation_id == reId && it.user_id == userId); var participants = await _dbBase.Ado.SqlQueryAsync($"select p.id,p.reservation_id,p.status,p.join_time,p.user_id, p.role,u.nickName UserName,u.avatarImage AvatarImage,ISNULL(u.play_level,4) play_level,ISNULL(u.skills_level,4) skills_level from SQReservationParticipants p inner join CoreCmsUser u on p.user_id=u.id where p.status=0 and p.reservation_id ={reId} and p.is_arrive=1 "); if (participants == null || participants.Count == 0) { return new WebApiDto() { Code = 0, Data = null }; } var userBlack = await _coreCmsUserBlacklistServices.GetUserBlacklists(userId); List obj = new List(); foreach (var item in participants) { if (item.user_id == userId) { continue; } var is_evaluate = false; decimal play_level = 0; decimal skills_level = 0; decimal user_play_level = 4; decimal user_skills_level = 4; DateTime? now = null; var userBlackStatus = false; if (list.Count > 0) { var e = list.Find(it => it.to_user_id == item.user_id); if (e != null) { is_evaluate = true; play_level = e.play_level; skills_level = e.skills_level; now = e.created_at; user_play_level = e.play_level; user_skills_level = e.skills_level; } } if (userBlack.Count > 0) { if (userBlack.Any(it => it == item.user_id)) { userBlackStatus = true; } } var o = new { item.UserName, item.user_id, item.AvatarImage, userBlackStatus, user_play_level, user_skills_level, is_evaluate, play_level, skills_level, evaluate_at = now }; obj.Add(o); } return new WebApiDto() { Code = 0, Data = obj }; } /// /// 评价用户 /// /// /// [HttpPost] [Authorize] public async Task AddEvaluateServices([FromBody] SQReservationEvaluateDto evaluate) { var userId = _user.ID; var reId = evaluate.reservation_id; var list = await _sQReservationEvaluateServices.QueryListByClauseAsync(it => it.reservation_id == reId && it.user_id == userId); var participants = await _dbBase.Ado.SqlQueryAsync($"select p.id,p.reservation_id,p.status,p.join_time,p.user_id, p.role,u.nickName UserName,u.avatarImage AvatarImage,ISNULL(u.play_level,4) play_level,ISNULL(u.skills_level,4) skills_level from SQReservationParticipants p inner join CoreCmsUser u on p.user_id=u.id where p.status=0 and p.reservation_id ={reId} and p.is_arrive=1 "); if (participants == null) { return new WebApiDto() { Code = 0, Data = null }; } List obj = new List(); var p = participants.Find(it => it.user_id == evaluate.to_user_id); if (p == null) { return new WebApiDto() { Code = 500, Data = null, Msg = "未找到对局记录" }; } //_sQReservationReputationServices. SQReservationEvaluate sQReservationEvaluate = new SQReservationEvaluate() { created_at = DateTime.Now, play_level = evaluate.play_level, reservation_id = evaluate.reservation_id, role = p.role, skills_level = evaluate.skills_level, to_user_id = evaluate.to_user_id, user_id = userId, }; await _sQReservationEvaluateServices.InsertAsync(sQReservationEvaluate); var play_level = _dbBase.Ado.SqlQuerySingle($"select ISNULL(sum(play_level),0) level,count(1) rs_count from SQReservationEvaluate where to_user_id={evaluate.to_user_id} "); var skills_level = _dbBase.Ado.SqlQuerySingle($"select ISNULL(sum(skills_level),0) level,count(1) rs_count from SQReservationEvaluate where to_user_id={evaluate.to_user_id} "); //先加4 play_level.level += 4; if (play_level.rs_count > 0) { if (play_level.rs_count == 1) { play_level.rs_count++; } play_level.level = play_level.level / play_level.rs_count; } //先加4 skills_level.level += 4; if (skills_level.rs_count > 0) { if (skills_level.rs_count == 1) { skills_level.rs_count++; } skills_level.level = skills_level.level / skills_level.rs_count; } if (play_level.rs_count > 0 || skills_level.rs_count > 0) { await _userServices.UpdateAsync(it => new CoreCmsUser { play_level = play_level.level, skills_level = skills_level.level }, it => it.id == evaluate.to_user_id); } return new WebApiDto() { Code = 0, Data = null, Msg = "评价成功" }; } /// /// 获取我的声誉记录 /// /// /// /// [HttpGet] [Authorize] public async Task GetReputationByUser([FromQuery] int pageIndex = 1, [FromQuery] int pageSize = 20) { var list = await _sQReservationReputationServices.QueryPageAsync(it => it.user_id == _user.ID, it => it.created_at, OrderByType.Desc, pageIndex, pageSize, true); var o = new List(); foreach (var item in list) { var c = new { item.created_at, reputation_value = item.reputation_value.ToString("#.#"), title = item.remark }; o.Add(c); } return new WebApiDto() { Code = 0, Data = o, Msg = "ok", }; } /// /// 获取评价我的记录 /// /// 页码 /// 每页数量 /// [HttpGet] [Authorize] public async Task GetEvaluateToMe([FromQuery] int pageIndex = 1, [FromQuery] int pageSize = 20) { var userId = _user.ID; var list = await _sQReservationEvaluateServices.QueryPageAsync( it => it.to_user_id == userId, it => it.created_at, OrderByType.Desc, pageIndex, pageSize, true ); return new WebApiDto() { Code = 0, Data = list, Msg = "ok" }; } /// /// 获取可预约的房间列表 /// /// 开始时间 时间戳 /// 结束时间 时间戳 /// [HttpGet] public async Task GetReservationRoomList([FromQuery] long startTime, [FromQuery] long endTime) { if (startTime == 0 || endTime == 0) { return new WebApiDto() { Code = 500, Data = null, Msg = "参数错误" }; } // 时间戳转DateTime var start = DateTimeOffset.FromUnixTimeSeconds(startTime).DateTime; var end = DateTimeOffset.FromUnixTimeSeconds(endTime).DateTime; // 1. 查询所有可用房间 var allRooms = await _SQRoomsServices.GetRoomList(); // 2. 查询有不可用时间段冲突的房间 var unavailableRoomIds = (await _sQRoomUnavailableTimesServices.QueryListByClauseAsync( t => t.start_time < end && t.end_time > start )).Select(t => t.room_id).Distinct().ToList(); // 3. 查询已被预约的房间(未取消的预约,时间有重叠) var reservedRoomIds = (await _SQReservationsServices.QueryListByClauseAsync( r => r.status < 3 && r.start_time < end && r.end_time > start )).Select(r => r.room_id).Distinct().ToList(); // 4. 可预约房间 = 所有可用房间 - 不可用房间 - 已预约房间 var availableRooms = allRooms .Where(r => r.status == true && !unavailableRoomIds.Contains(r.id) && !reservedRoomIds.Contains(r.id)) .OrderBy(r => r.name) .Select(it => new { name = it.name + $" {it.price_per_hour.ToString("#.##")}/小时", it.id, it.capacity }) .ToList(); return new WebApiDto() { Code = 0, Data = availableRooms, Msg = "ok" }; } /// /// 用户预约接口 /// /// /// [HttpPost] [Authorize] public async Task AddSQReservation([FromBody] SQReservationsAddDto dto) { var userId = _user.ID; var start_time = DateTimeOffset.FromUnixTimeSeconds(dto.start_time).AddHours(8).DateTime; var end_time = DateTimeOffset.FromUnixTimeSeconds(dto.end_time).AddHours(8).DateTime; // 1. 参数校验 if (dto == null || dto.room_id <= 0 || start_time >= end_time) { return new WebApiDto() { Code = 500, Data = null, Msg = "参数错误" }; } // 2. 检查房间是否存在 var room = await _SQRoomsServices.QueryByClauseAsync(r => r.id == dto.room_id, r => r.id, OrderByType.Asc); if (room == null) { return new WebApiDto() { Code = 500, Data = null, Msg = "房间不存在" }; } // 3. 检查房间是否可预约(不可用时间段) var hasUnavailable = await _sQRoomUnavailableTimesServices.QueryListByClauseAsync( t => t.room_id == dto.room_id && t.start_time < end_time && t.end_time > start_time, t => t.id, OrderByType.Asc); if (hasUnavailable != null && hasUnavailable.Count > 0) { return new WebApiDto() { Code = 500, Data = null, Msg = "该时间段房间不可预约" }; } // 2.1 校验用户是否有其它预约时间冲突 // 2.1 校验用户是否有其它预约时间冲突(只查未开始或正在进行中的预约) var now = DateTime.Now; var hasConflict = await _SQReservationParticipantsServices.QueryMuchAsync( (p, r) => new object[] { JoinType.Inner, p.reservation_id == r.id }, (p, r) => r.id, (p, r) => p.user_id == userId && p.status == 0 && // 只查未退出 r.status < 3 && // 只查未取消/未结束 r.end_time > now && // 只查未结束 r.start_time < end_time && r.end_time > start_time // 时间有重叠 ); if (hasConflict != null && hasConflict.Count > 0) { return new WebApiDto { Code = 402, Data = null, Msg = "您有其它预约时间冲突,无法创建该预约!" }; } // 4. 检查房间是否已被预约(未取消的预约,时间有重叠) var hasReserved = await _SQReservationsServices.QueryListByClauseAsync( r => r.room_id == dto.room_id && r.status < 2 && r.start_time < end_time && r.end_time > start_time, r => r.id, OrderByType.Asc); if (hasReserved != null && hasReserved.Count > 0) { return new WebApiDto() { Code = 500, Data = null, Msg = "该时间段房间已被预约" }; } // 5. 创建预约记录 var reservation = new SQReservations { room_id = dto.room_id, room_name = room.name, start_time = start_time, end_time = end_time, game_type = dto.game_type, created_at = DateTime.Now, updated_at = DateTime.Now, credit_limit = dto.credit_limit, player_count = dto.player_count, deposit_fee = dto.deposit_fee, duration_minutes = (int)(end_time - start_time).TotalMinutes, extra_info = dto.extra_info, game_rule = dto.game_rule, gender_limit = dto.gender_limit, is_smoking = dto.is_smoking, max_age = dto.max_age, min_age = dto.min_age, remarks = "", status = 0, title = dto.title, // 其他字段可根据需要补充 }; var reservationId = await _dbBase.Insertable(reservation).ExecuteReturnIdentityAsync(); if (reservationId <= 0) { return new WebApiDto() { Code = 500, Data = null, Msg = "预约失败" }; } // 6. 创建预约参与人记录(发起者,role=1) var participant = new SQReservationParticipants { reservation_id = reservationId, user_id = userId, join_time = DateTime.Now, role = 1, status = 0, is_refund = dto.deposit_fee > 0 ? 1 : 0, important_data = dto.important_data }; await _SQReservationParticipantsServices.InsertAsync(participant); return new WebApiDto() { Code = 0, Data = new { reservation_id = reservationId }, Msg = "预约成功" }; } /// /// 用户是否可以创建预约(仅校验,不创建) /// /// 创建预约所需参数 /// 返回可否创建及原因 [HttpPost] [Authorize] public async Task CanCreateSQReservation([FromBody] SQReservationsAddDto dto) { var userId = _user.ID; var start_time = DateTimeOffset.FromUnixTimeSeconds(dto.start_time).AddHours(8).DateTime; var end_time = DateTimeOffset.FromUnixTimeSeconds(dto.end_time).AddHours(8).DateTime; // 1. 参数校验 if (dto == null || dto.room_id <= 0 || start_time >= end_time) { return new WebApiDto() { Code = 500, Data = new { canCreate = false }, Msg = "参数错误" }; } // 2. 检查房间是否存在 var room = await _SQRoomsServices.QueryByClauseAsync(r => r.id == dto.room_id, r => r.id, OrderByType.Asc); if (room == null) { return new WebApiDto() { Code = 500, Data = new { canCreate = false }, Msg = "房间不存在" }; } // 3. 检查房间是否可预约(不可用时间段) var hasUnavailable = await _sQRoomUnavailableTimesServices.QueryListByClauseAsync( t => t.room_id == dto.room_id && t.start_time < end_time && t.end_time > start_time, t => t.id, OrderByType.Asc); if (hasUnavailable != null && hasUnavailable.Count > 0) { return new WebApiDto() { Code = 500, Data = new { canCreate = false }, Msg = "该时间段房间不可预约" }; } // 2.1 校验用户是否有其它预约时间冲突(只查未开始或正在进行中的预约) var now = DateTime.Now; var hasConflict = await _SQReservationParticipantsServices.QueryMuchAsync( (p, r) => new object[] { JoinType.Inner, p.reservation_id == r.id }, (p, r) => r.id, (p, r) => p.user_id == userId && p.status == 0 && r.status < 3 && r.end_time > now && r.start_time < end_time && r.end_time > start_time ); if (hasConflict != null && hasConflict.Count > 0) { return new WebApiDto { Code = 402, Data = new { canCreate = false }, Msg = "您有其它预约时间冲突,无法创建该预约!" }; } // 4. 检查房间是否已被预约(未取消的预约,时间有重叠) var hasReserved = await _SQReservationsServices.QueryListByClauseAsync( r => r.room_id == dto.room_id && r.status < 2 && r.start_time < end_time && r.end_time > start_time, r => r.id, OrderByType.Asc); if (hasReserved != null && hasReserved.Count > 0) { return new WebApiDto() { Code = 500, Data = new { canCreate = false }, Msg = "该时间段房间已被预约" }; } // 全部通过 return new WebApiDto() { Code = 0, Data = new { canCreate = true }, Msg = "ok" }; } /// /// 用户加入预约接口 /// /// 预约 /// [HttpPost] [Authorize] public async Task JoinReservation([FromBody] SQReservationParticipantsAddDto dto) { var userId = _user.ID; var reservationId = dto.ReservationsId; // 1. 校验预约是否存在且未结束 var reservation = await _SQReservationsServices.QueryByClauseAsync( r => r.id == reservationId && r.end_time > DateTime.Now && r.status < 2, r => r.id, OrderByType.Asc); if (reservation == null) { return new WebApiDto { Code = 404, Data = null, Msg = "预约不存在或已结束" }; } // 2. 校验用户是否已加入该预约 var exists = await _SQReservationParticipantsServices.QueryByClauseAsync( p => p.reservation_id == reservationId && p.user_id == userId && p.status == 0, p => p.id, OrderByType.Asc); if (exists != null) { return new WebApiDto { Code = 400, Data = null, Msg = "您已加入该预约" }; } var user = _userServices.QueryById(userId); // 2.0.1 校验用户条件 是否符合要求 如 性别(user.sex 1男2女) 年龄(user.birthday这个是生日 用生日去动态计算年龄) 信用分(credit_score) //reservation.credit_limit 最低信誉分 同 用户表的的 信用分 // reservation.gender_limit 0 不限 1男 2 女 // 最大 和最小 年龄 0 不限制 reservation.max_age reservation.min_age if (user == null) { return new WebApiDto { Code = 404, Data = null, Msg = "用户不存在" }; } // 信誉分校验 if (reservation.credit_limit > 0 && user.credit_score < reservation.credit_limit) { return new WebApiDto { Code = 400, Data = null, Msg = $"您的信誉分不足,最低要求为{reservation.credit_limit}" }; } // 性别限制:0 不限,1 男,2 女 if (reservation.gender_limit == 1 || reservation.gender_limit == 2) { if (user.sex != reservation.gender_limit) { return new WebApiDto { Code = 400, Data = null, Msg = "您的性别不符合该预约要求" }; } } // 年龄限制(按生日计算实际年龄) if (reservation.min_age > 0 || reservation.max_age > 0) { if (!user.birthday.HasValue) { return new WebApiDto { Code = 400, Data = null, Msg = "请先完善生日信息以校验年龄" }; } var today = DateTime.Today; var birth = user.birthday.Value.Date; var age = today.Year - birth.Year; if (birth > today.AddYears(-age)) age--; if (reservation.min_age > 0 && age < reservation.min_age) { return new WebApiDto { Code = 400, Data = null, Msg = $"年龄小于最小限制:{reservation.min_age}岁" }; } if (reservation.max_age > 0 && age > reservation.max_age) { return new WebApiDto { Code = 400, Data = null, Msg = $"年龄超过最大限制:{reservation.max_age}岁" }; } } // 2.1 校验用户是否有其它预约时间冲突 // 2.1 校验用户是否有其它预约时间冲突(只查未开始或正在进行中的预约) var now = DateTime.Now; var hasConflict = await _SQReservationParticipantsServices.QueryMuchAsync( (p, r) => new object[] { JoinType.Inner, p.reservation_id == r.id }, (p, r) => r.id, (p, r) => p.user_id == userId && p.status == 0 && // 只查未退出 r.status < 3 && // 只查未取消/未结束 r.end_time > now && // 只查未结束 r.start_time < reservation.end_time && r.end_time > reservation.start_time // 时间有重叠 ); if (hasConflict != null && hasConflict.Count > 0) { return new WebApiDto { Code = 402, Data = null, Msg = "您有其它预约时间冲突,无法加入该预约" }; } //// 查询用户所有未结束的预约参与记录 //var userReservations = await _SQReservationParticipantsServices.QueryListByClauseAsync( // p => p.user_id == userId && p.status == 0, // 只查未退出的 // p => p.id, OrderByType.Asc); //if (userReservations != null && userReservations.Count > 0) //{ // // 获取这些预约的ID // var reservationIds = userReservations.Select(p => p.reservation_id).ToList(); // // 查询这些预约的时间段 // var reservations = await _SQReservationsServices.QueryListByClauseAsync( // r => reservationIds.Contains(r.id) && r.status < 2 && r.end_time > DateTime.Now, // r => r.id, OrderByType.Asc); // // 检查时间是否有重叠 // foreach (var r in reservations) // { // if (r.start_time < reservation.end_time && r.end_time > reservation.start_time) // { // return new WebApiDto // { // Code = 402, // Data = null, // Msg = "您有其它预约时间冲突,无法加入该预约" // }; // } // } //} //3.校验预约是否已满(如有容量限制,可补充) var participantsCount = await _SQReservationParticipantsServices.QueryListByClauseAsync( p => p.reservation_id == reservationId && p.status == 0, p => p.id, OrderByType.Asc); if (participantsCount.Count >= reservation.player_count) { return new WebApiDto() { Code = 401, Data = null, Msg = "预约已满" }; } // 4. 插入参与人记录 var participant = new SQReservationParticipants { reservation_id = reservationId, user_id = userId, join_time = DateTime.Now, role = 0, // 0为参与者 status = 0, is_refund = reservation.deposit_fee > 0 ? 1 : 0, important_data = dto.important_data }; await _SQReservationParticipantsServices.InsertAsync(participant); return new WebApiDto { Code = 0, Data = new { reservation_id = reservationId }, Msg = "加入预约成功" }; } /// /// 取消预约接口 /// /// 取消预约参数 /// [HttpPost] [Authorize] public async Task CancelReservation([FromBody] SQReservationCancelDto dto) { var userId = _user.ID; // 1. 参数校验 if (dto == null || dto.reservation_id <= 0) { return new WebApiDto() { Code = 500, Data = null, Msg = "参数错误" }; } // 2. 查询预约是否存在 var reservation = await _SQReservationsServices.QueryByClauseAsync( r => r.id == dto.reservation_id, r => r.id, OrderByType.Asc); if (reservation == null) { return new WebApiDto() { Code = 404, Data = null, Msg = "预约不存在" }; } if (reservation.start_time.AddMinutes(-30) < DateTime.Now) { return new WebApiDto() { Code = 400, Data = null, Msg = "预约开始前30分钟,无法取消" }; } if (reservation.status == 1) // { return new WebApiDto() { Code = 400, Data = null, Msg = "该预约已组局成功锁定,无法取消" }; } // 3. 检查预约状态是否允许取消 if (reservation.status >= 3) // 3=已结束,4=取消 { return new WebApiDto() { Code = 400, Data = null, Msg = "该预约已结束或已取消,无法再次取消" }; } // 4. 检查用户是否有权限取消该预约 var participant = await _SQReservationParticipantsServices.QueryByClauseAsync( p => p.reservation_id == dto.reservation_id && p.user_id == userId, p => p.id, OrderByType.Asc); if (participant == null) { return new WebApiDto() { Code = 403, Data = null, Msg = "您没有权限取消该预约" }; } // 5. 检查是否已经开始(如果已经开始,只有发起者可以取消) var now = DateTime.Now; if (reservation.start_time <= now && participant.role != 1) { return new WebApiDto() { Code = 400, Data = null, Msg = "预约已开始,只有发起者可以取消" }; } // 7. 更新参与人状态为已退出(如果是发起者取消整个预约,所有参与人都标记为退出) if (participant.role == 1) // 发起者取消 { // 7.1.如果是发起者取消,则当前对局全部取消 var updateResult = await _SQReservationsServices.UpdateAsync( it => new SQReservations { status = 4, // 用户取消 updated_at = DateTime.Now, remarks = string.IsNullOrEmpty(dto.cancel_reason) ? "未支付鸽子费" : dto.cancel_reason }, it => it.id == dto.reservation_id); if (!updateResult) { return new WebApiDto() { Code = 500, Data = null, Msg = "取消预约失败" }; } // 7.2发起者取消整个预约,所有参与人都标记为退出 await _SQReservationParticipantsServices.UpdateAsync( it => new SQReservationParticipants { status = 1, // 已退出 quit_time = DateTime.Now }, it => it.reservation_id == dto.reservation_id); //判断是否有参与者 var list = await _SQReservationParticipantsServices.QueryListByClauseAsync(it => it.reservation_id == dto.reservation_id && it.role == 0 && it.status == 0); if (list.Count > 0) { await _SQReservationsServices.NotifyReservationFailedAsync(reservation, list, "发起者解散组局!"); } } else // 参与者取消 { // 参与者只退出自己 await _SQReservationParticipantsServices.UpdateAsync( it => new SQReservationParticipants { status = 1, // 已退出 quit_time = DateTime.Now }, it => it.reservation_id == dto.reservation_id && it.user_id == userId); } return new WebApiDto() { Code = 0, Data = null, Msg = "取消预约成功" }; } /// /// 预约签到(仅发起者可操作,且只能签到一次) /// /// 签到参数:不包含发起者的参会名单 /// [HttpPost] [Authorize] public async Task CheckInReservation([FromBody] SQReservationCheckInDto dto) { var userId = _user.ID; if (dto == null || dto.reservation_id <= 0) { return new WebApiDto { Code = 500, Data = null, Msg = "参数错误" }; } // 查询预约 var reservation = await _SQReservationsServices.QueryByClauseAsync( r => r.id == dto.reservation_id, r => r.id, OrderByType.Asc); if (reservation == null) { return new WebApiDto { Code = 404, Data = null, Msg = "预约不存在" }; } // 已结束或已取消 if (reservation.status >= 3) { return new WebApiDto { Code = 400, Data = null, Msg = "预约已结束或已取消,无法签到" }; } // 已进行中(已签到),禁止重复 if (reservation.status == 2) { return new WebApiDto { Code = 400, Data = null, Msg = "已签到,无法重复签到" }; } // 权限校验:仅发起者可签到 var initiator = await _SQReservationParticipantsServices.QueryByClauseAsync( p => p.reservation_id == dto.reservation_id && p.user_id == userId && p.role == 1 && p.status == 0, p => p.id, OrderByType.Asc); if (initiator == null) { return new WebApiDto { Code = 403, Data = null, Msg = "仅发起者可进行签到" }; } dto.attendeds ??= new List(); var falseUserIds = dto.attendeds .Where(a => a != null && a.isAttended == false) .Select(a => a.user_id) .Distinct() .ToList(); try { _dbBase.Ado.BeginTran(); // 1) 预约置为进行中 var updatedReservation = await _SQReservationsServices.UpdateAsync( it => new SQReservations { status = 2, updated_at = DateTime.Now }, it => it.id == dto.reservation_id); if (!updatedReservation) { _dbBase.Ado.RollbackTran(); return new WebApiDto { Code = 500, Data = null, Msg = "签到失败:更新预约状态异常" }; } // 2) 默认将所有未退出的参与者标记为已赴约(1) await _SQReservationParticipantsServices.UpdateAsync( it => new SQReservationParticipants { is_arrive = 1 }, it => it.reservation_id == dto.reservation_id && it.status == 0); // 3) 对于传入的未出席名单,标记为未赴约(2) if (falseUserIds.Count > 0) { await _SQReservationParticipantsServices.UpdateAsync( it => new SQReservationParticipants { is_arrive = 2 }, it => it.reservation_id == dto.reservation_id && it.status == 0 && falseUserIds.Contains(it.user_id)); //添加鸽子次数 foreach (var user_id in falseUserIds) { var user = await _userServices.QueryByIdAsync(user_id); if (user != null) { user.dove_count++; } await _userServices.UpdateAsync(user); } } // 4) 确保发起者始终为已赴约(1) await _SQReservationParticipantsServices.UpdateAsync( it => new SQReservationParticipants { is_arrive = 1 }, it => it.reservation_id == dto.reservation_id && it.status == 0 && it.user_id == userId); _dbBase.Ado.CommitTran(); return new WebApiDto { Code = 0, Data = null, Msg = "签到成功" }; } catch (Exception) { _dbBase.Ado.RollbackTran(); return new WebApiDto { Code = 500, Data = null, Msg = "签到失败" }; } } }