rentease-backend-new/controllers/roomController.js

451 lines
13 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

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.

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
};