const { Room, Apartment, Rental, Renter, Tenant } = require('../models'); const { Op } = require('sequelize'); const { logOperation } = require('../utils/logger'); const response = require('../utils/response'); // 格式化时间(考虑时区,转换为北京时间) const formatDate = (date) => { if (!date) return null; // 确保 date 是 Date 对象 const dateObj = date instanceof Date ? date : new Date(date); if (isNaN(dateObj.getTime())) return null; // 创建一个新的Date对象,加上8小时的时区偏移 const beijingDate = new Date(dateObj.getTime() + 8 * 60 * 60 * 1000); return beijingDate.toISOString().split('T')[0]; }; // 格式化房间数据 const formatRoomData = (room) => { const formattedRoom = { ...room.toJSON(), createTime: formatDate(room.createTime), updateTime: formatDate(room.updateTime) }; // 格式化关联数据 if (formattedRoom.Apartment) { formattedRoom.Apartment = { ...formattedRoom.Apartment, createTime: formatDate(formattedRoom.Apartment.createTime), updateTime: formatDate(formattedRoom.Apartment.updateTime) }; } // 格式化租房信息 if (formattedRoom.Rentals && formattedRoom.Rentals.length > 0) { formattedRoom.Rentals = formattedRoom.Rentals.map(rental => { const formattedRental = { ...rental, startDate: formatDate(rental.startDate), endDate: formatDate(rental.endDate), createTime: formatDate(rental.createTime), updateTime: formatDate(rental.updateTime) }; return formattedRental; }); } return formattedRoom; }; // 检查并更新租房状态 const checkAndUpdateRentalStatus = async () => { try { // 获取当前日期 const currentDate = new Date(); // 计算5天后的日期 const fiveDaysLater = new Date(); fiveDaysLater.setDate(currentDate.getDate() + 5); // 查找所有在租的租房记录 const rentals = await Rental.findAll({ where: { status: 'active', isDeleted: 0 } }); // 检查每个租房记录的状态 for (const rental of rentals) { const endDate = new Date(rental.endDate); // 检查是否已到期 if (endDate < currentDate) { // 更新房间租房标识为已到期 const room = await Room.findByPk(rental.roomId); if (room && room.status === 'rented') { await room.update({ rentalStatus: 'expired' }); } } else if (endDate <= fiveDaysLater) { // 更新房间租房标识为即将到期 const room = await Room.findByPk(rental.roomId); if (room && room.status === 'rented') { await room.update({ rentalStatus: 'soon_expire' }); } } else { // 更新房间租房标识为正常 const room = await Room.findByPk(rental.roomId); if (room && room.status === 'rented') { await room.update({ rentalStatus: 'normal' }); } } } console.log('租房状态检查和更新完成'); } catch (error) { console.error('检查和更新租房状态时出错:', error); } }; // 获取所有房间(支持搜索和分页) const getAllRooms = async (req, res) => { try { // 先检查并更新租房状态 await checkAndUpdateRentalStatus(); const { apartmentId, roomNumber, status, rentalStatus, page = 1, pageSize = 10 } = req.query; // 构建查询条件 const where = { isDeleted: 0 }; if (apartmentId) { where.apartmentId = apartmentId; } if (roomNumber) { where.roomNumber = { [Op.like]: `%${roomNumber}%` }; } if (status) { where.status = status; } if (rentalStatus) { where.rentalStatus = rentalStatus; } // 计算偏移量 const offset = (page - 1) * pageSize; // 查询房间数据 const { count, rows } = await Room.findAndCountAll({ where, include: [ Apartment ], limit: parseInt(pageSize), offset: parseInt(offset), order: [['sortOrder', 'ASC'], ['id', 'DESC']] }); // 为每个房间获取非过期的租房信息 const formattedRooms = await Promise.all(rows.map(async (room) => { const formattedRoom = formatRoomData(room); // 查询非过期且未删除的租房信息,按创建时间倒序排列 const rentals = await Rental.findAll({ where: { roomId: room.id, status: { [Op.ne]: 'expired' }, isDeleted: 0 }, include: [ { model: Renter, attributes: ['id', 'name', 'phone'] } ], order: [['createTime', 'DESC']] }); // 格式化租房信息 if (rentals.length > 0) { formattedRoom.Rentals = rentals.map(rental => { const rentalData = rental.toJSON(); const formattedRental = { ...rentalData, startDate: formatDate(rental.startDate), endDate: formatDate(rental.endDate), createTime: formatDate(rental.createTime), // 兼容前端显示的字段 tenantName: rentalData.Renter ? rentalData.Renter.name : '', tenantPhone: rentalData.Renter ? rentalData.Renter.phone : '' }; return formattedRental; }); } else { formattedRoom.Rentals = []; } return formattedRoom; })); // 返回结果 response.success(res, '获取成功', { list: formattedRooms, total: count, page: parseInt(page), pageSize: parseInt(pageSize) }); } catch (error) { response.serverError(res, '获取房间列表失败', error); } }; // 获取单个房间 const getRoomById = async (req, res) => { try { // 先检查并更新租房状态 await checkAndUpdateRentalStatus(); const { id } = req.params; const room = await Room.findOne({ where: { id, isDeleted: 0 }, include: [Apartment] }); if (!room) { return response.notFound(res, '房间不存在'); } // 格式化房间数据 const formattedRoom = formatRoomData(room); // 查询非过期且未删除的租房信息 const rentals = await Rental.findAll({ where: { roomId: room.id, status: { [Op.ne]: 'expired' }, isDeleted: 0 }, include: [ { model: Renter, attributes: ['id', 'name', 'phone'] } ] }); // 格式化租房信息 if (rentals.length > 0) { formattedRoom.Rentals = rentals.map(rental => { const rentalData = rental.toJSON(); const formattedRental = { ...rentalData, startDate: formatDate(rental.startDate), endDate: formatDate(rental.endDate), createTime: formatDate(rental.createTime), // 兼容前端显示的字段 tenantName: rentalData.Renter ? rentalData.Renter.name : '', tenantPhone: rentalData.Renter ? rentalData.Renter.phone : '' }; return formattedRental; }); } else { formattedRoom.Rentals = []; } response.success(res, '获取成功', formattedRoom); } catch (error) { response.serverError(res, '获取房间详情失败', error); } }; // 创建房间 const createRoom = async (req, res) => { try { const { apartmentId, roomNumber, floor, roomType, area, monthlyPrice, yearlyPrice, deposit, sortOrder, status, rentalStatus } = req.body; // 检查租户资源使用情况(仅记录日志,不阻止创建) const tenant = await Tenant.findByPk(req.user.tenantId); let currentRoomCount = 0; if (tenant) { // 获取当前房间数量 currentRoomCount = await Room.count({ where: { tenantId: req.user.tenantId, isDeleted: 0 } }); // 检查是否超出限制 if (currentRoomCount >= tenant.maxRooms) { // 记录超额使用日志 await logOperation({ userId: req.user.id, username: req.user.username, tenantId: req.user.tenantId, module: '房间管理', action: '超额创建', description: `创建房间"${roomNumber}",当前已使用 ${currentRoomCount + 1}/${tenant.maxRooms} 间(超出限制)`, method: req.method, path: req.path, ip: req.ip, status: 'success' }); } } // 处理空值 const processedData = { apartmentId, roomNumber, floor: floor === '' ? null : floor, roomType: roomType === '' ? null : roomType, area: area === '' ? null : area, monthlyPrice: monthlyPrice === '' ? null : monthlyPrice, yearlyPrice: yearlyPrice === '' ? null : yearlyPrice, deposit: deposit === '' ? null : deposit, sortOrder: sortOrder === '' ? 0 : sortOrder, status, rentalStatus: rentalStatus || 'normal', tenantId: req.user.tenantId, createBy: req.user.id, updateBy: req.user.id }; const room = await Room.create(processedData); response.created(res, '创建成功', { room, warning: tenant && currentRoomCount >= tenant.maxRooms ? `当前已超出套餐限制(${tenant.maxRooms}间),续费时将收取超额费用` : null }); } catch (error) { response.serverError(res, '创建房间失败', error); } }; // 更新房间 const updateRoom = async (req, res) => { try { const { id } = req.params; const { apartmentId, roomNumber, floor, roomType, area, monthlyPrice, yearlyPrice, deposit, sortOrder, status, rentalStatus } = req.body; const room = await Room.findOne({ where: { id, isDeleted: 0 } }); if (!room) { return response.notFound(res, '房间不存在'); } // 处理空值 const processedData = { apartmentId, roomNumber, floor: floor === '' ? null : floor, roomType: roomType === '' ? null : roomType, area: area === '' ? null : area, monthlyPrice: monthlyPrice === '' ? null : monthlyPrice, yearlyPrice: yearlyPrice === '' ? null : yearlyPrice, deposit: deposit === '' ? null : deposit, sortOrder: sortOrder === '' ? 0 : sortOrder, status, rentalStatus: rentalStatus || 'normal', updateBy: req.user.id }; await room.update(processedData); response.success(res, '更新成功', room); } catch (error) { response.serverError(res, '更新房间失败', error); } }; // 删除房间(软删除) const deleteRoom = async (req, res) => { try { const { id } = req.params; const room = await Room.findOne({ where: { id, isDeleted: 0 } }); if (!room) { return response.notFound(res, '房间不存在'); } await room.update({ isDeleted: 1, updateBy: req.user.id }); response.success(res, '房间删除成功'); } catch (error) { response.serverError(res, '删除房间失败', error); } }; // 获取所有房间(不分页) const listRooms = async (req, res) => { try { // 先检查并更新租房状态 await checkAndUpdateRentalStatus(); const { apartmentId, roomNumber, status, rentalStatus } = req.query; // 构建查询条件 const where = { isDeleted: 0 }; if (apartmentId) { where.apartmentId = apartmentId; } if (roomNumber) { where.roomNumber = { [Op.like]: `%${roomNumber}%` }; } if (status) { where.status = status; } if (rentalStatus) { where.rentalStatus = rentalStatus; } // 查询房间数据 const rooms = await Room.findAll({ where, include: [ Apartment ], order: [['sortOrder', 'ASC'], ['id', 'DESC']] }); // 为每个房间获取非过期的租房信息 const formattedRooms = await Promise.all(rooms.map(async (room) => { const formattedRoom = formatRoomData(room); // 查询非过期且未删除的租房信息,按创建时间倒序排列 const rentals = await Rental.findAll({ where: { roomId: room.id, status: { [Op.ne]: 'expired' }, isDeleted: 0 }, include: [ { model: Renter, attributes: ['id', 'name', 'phone'] } ], order: [['createTime', 'DESC']] }); // 格式化租房信息,取第一条作为当前租赁信息 if (rentals.length > 0) { const rental = rentals[0]; const rentalData = rental.toJSON(); formattedRoom.rental = { ...rentalData, startDate: formatDate(rental.startDate), endDate: formatDate(rental.endDate), createTime: formatDate(rental.createTime), // 兼容前端显示的字段 tenantName: rentalData.Renter ? rentalData.Renter.name : '', tenantPhone: rentalData.Renter ? rentalData.Renter.phone : '' }; } return formattedRoom; })); // 返回结果 response.success(res, '获取成功', formattedRooms); } catch (error) { response.serverError(res, '获取房间列表失败', error); } }; module.exports = { getAllRooms, listRooms, getRoomById, createRoom, updateRoom, deleteRoom };