2026-03-02 12:36:41 +00:00
|
|
|
|
const { Room, Apartment, Rental } = require('../models');
|
|
|
|
|
|
const { Op } = require('sequelize');
|
|
|
|
|
|
|
|
|
|
|
|
// 格式化时间(考虑时区,转换为北京时间)
|
|
|
|
|
|
const formatDate = (date) => {
|
|
|
|
|
|
if (!date) return null;
|
|
|
|
|
|
// 创建一个新的Date对象,加上8小时的时区偏移
|
|
|
|
|
|
const beijingDate = new Date(date.getTime() + 8 * 60 * 60 * 1000);
|
|
|
|
|
|
return beijingDate.toISOString().split('T')[0];
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 格式化房间数据
|
|
|
|
|
|
const formatRoomData = (room) => {
|
|
|
|
|
|
const formattedRoom = {
|
|
|
|
|
|
...room.toJSON(),
|
2026-03-03 15:36:48 +00:00
|
|
|
|
createTime: formatDate(room.createTime),
|
|
|
|
|
|
updateTime: formatDate(room.updateTime)
|
2026-03-02 12:36:41 +00:00
|
|
|
|
};
|
2026-03-07 07:23:18 +00:00
|
|
|
|
|
2026-03-02 12:36:41 +00:00
|
|
|
|
// 格式化关联数据
|
|
|
|
|
|
if (formattedRoom.Apartment) {
|
|
|
|
|
|
formattedRoom.Apartment = {
|
|
|
|
|
|
...formattedRoom.Apartment,
|
2026-03-03 15:36:48 +00:00
|
|
|
|
createTime: formatDate(formattedRoom.Apartment.createTime),
|
|
|
|
|
|
updateTime: formatDate(formattedRoom.Apartment.updateTime)
|
2026-03-02 12:36:41 +00:00
|
|
|
|
};
|
|
|
|
|
|
}
|
2026-03-07 07:23:18 +00:00
|
|
|
|
|
2026-03-02 12:36:41 +00:00
|
|
|
|
// 格式化租房信息
|
2026-03-05 15:26:13 +00:00
|
|
|
|
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)
|
2026-03-02 12:36:41 +00:00
|
|
|
|
};
|
2026-03-07 07:23:18 +00:00
|
|
|
|
|
2026-03-05 15:26:13 +00:00
|
|
|
|
return formattedRental;
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
2026-03-07 07:23:18 +00:00
|
|
|
|
|
2026-03-02 12:36:41 +00:00
|
|
|
|
return formattedRoom;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 检查并更新租房状态
|
|
|
|
|
|
const checkAndUpdateRentalStatus = async () => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
// 获取当前日期
|
|
|
|
|
|
const currentDate = new Date();
|
|
|
|
|
|
// 计算10天后的日期
|
|
|
|
|
|
const tenDaysLater = new Date();
|
2026-03-07 07:23:18 +00:00
|
|
|
|
tenDaysLater.setDate(currentDate.getDate() + 7);
|
|
|
|
|
|
|
2026-03-05 15:26:13 +00:00
|
|
|
|
// 查找所有在租的租房记录
|
2026-03-02 12:36:41 +00:00
|
|
|
|
const rentals = await Rental.findAll({
|
2026-03-03 15:36:48 +00:00
|
|
|
|
where: { status: 'active', isDeleted: 0 }
|
2026-03-02 12:36:41 +00:00
|
|
|
|
});
|
2026-03-07 07:23:18 +00:00
|
|
|
|
|
2026-03-02 12:36:41 +00:00
|
|
|
|
// 检查每个租房记录的状态
|
|
|
|
|
|
for (const rental of rentals) {
|
|
|
|
|
|
const endDate = new Date(rental.endDate);
|
2026-03-07 07:23:18 +00:00
|
|
|
|
|
2026-03-02 12:36:41 +00:00
|
|
|
|
// 检查是否已到期
|
|
|
|
|
|
if (endDate < currentDate) {
|
2026-03-03 16:33:04 +00:00
|
|
|
|
// 更新房间附属状态为已到期
|
2026-03-02 12:36:41 +00:00
|
|
|
|
const room = await Room.findByPk(rental.roomId);
|
2026-03-03 15:36:48 +00:00
|
|
|
|
if (room && room.status === 'rented') {
|
2026-03-03 16:33:04 +00:00
|
|
|
|
await room.update({ subStatus: 'expired' });
|
2026-03-02 12:36:41 +00:00
|
|
|
|
}
|
|
|
|
|
|
} else if (endDate <= tenDaysLater) {
|
2026-03-03 15:36:48 +00:00
|
|
|
|
// 更新房间附属状态为即将到期
|
|
|
|
|
|
const room = await Room.findByPk(rental.roomId);
|
|
|
|
|
|
if (room && room.status === 'rented') {
|
|
|
|
|
|
await room.update({ subStatus: 'soon_expire' });
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 更新房间附属状态为正常
|
2026-03-02 12:36:41 +00:00
|
|
|
|
const room = await Room.findByPk(rental.roomId);
|
|
|
|
|
|
if (room && room.status === 'rented') {
|
2026-03-03 15:36:48 +00:00
|
|
|
|
await room.update({ subStatus: 'normal' });
|
2026-03-02 12:36:41 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-03-07 07:23:18 +00:00
|
|
|
|
|
2026-03-02 12:36:41 +00:00
|
|
|
|
console.log('租房状态检查和更新完成');
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('检查和更新租房状态时出错:', error);
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 获取所有房间(支持搜索和分页)
|
|
|
|
|
|
const getAllRooms = async (req, res) => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
// 先检查并更新租房状态
|
|
|
|
|
|
await checkAndUpdateRentalStatus();
|
2026-03-07 07:23:18 +00:00
|
|
|
|
|
2026-03-03 15:36:48 +00:00
|
|
|
|
const { apartmentId, roomNumber, status, otherStatus, subStatus, page = 1, pageSize = 10 } = req.query;
|
2026-03-07 07:23:18 +00:00
|
|
|
|
|
2026-03-02 12:36:41 +00:00
|
|
|
|
// 构建查询条件
|
2026-03-03 15:36:48 +00:00
|
|
|
|
const where = { isDeleted: 0 };
|
2026-03-02 12:36:41 +00:00
|
|
|
|
if (apartmentId) {
|
|
|
|
|
|
where.apartmentId = apartmentId;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (roomNumber) {
|
|
|
|
|
|
where.roomNumber = {
|
|
|
|
|
|
[Op.like]: `%${roomNumber}%`
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
if (status) {
|
|
|
|
|
|
where.status = status;
|
|
|
|
|
|
}
|
2026-03-03 15:36:48 +00:00
|
|
|
|
if (otherStatus) {
|
|
|
|
|
|
where.otherStatus = otherStatus;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (subStatus) {
|
|
|
|
|
|
where.subStatus = subStatus;
|
|
|
|
|
|
}
|
2026-03-07 07:23:18 +00:00
|
|
|
|
|
2026-03-02 12:36:41 +00:00
|
|
|
|
// 计算偏移量
|
|
|
|
|
|
const offset = (page - 1) * pageSize;
|
2026-03-07 07:23:18 +00:00
|
|
|
|
|
2026-03-02 12:36:41 +00:00
|
|
|
|
// 查询房间数据
|
|
|
|
|
|
const { count, rows } = await Room.findAndCountAll({
|
|
|
|
|
|
where,
|
|
|
|
|
|
include: [
|
|
|
|
|
|
Apartment
|
|
|
|
|
|
],
|
|
|
|
|
|
limit: parseInt(pageSize),
|
|
|
|
|
|
offset: parseInt(offset)
|
|
|
|
|
|
});
|
2026-03-07 07:23:18 +00:00
|
|
|
|
|
2026-03-02 12:36:41 +00:00
|
|
|
|
// 为每个房间获取非过期的租房信息
|
|
|
|
|
|
const formattedRooms = await Promise.all(rows.map(async (room) => {
|
|
|
|
|
|
const formattedRoom = formatRoomData(room);
|
2026-03-07 07:23:18 +00:00
|
|
|
|
|
2026-03-02 12:36:41 +00:00
|
|
|
|
// 查询非过期的租房信息
|
|
|
|
|
|
const rentals = await Rental.findAll({
|
|
|
|
|
|
where: {
|
|
|
|
|
|
roomId: room.id,
|
|
|
|
|
|
status: { [Op.ne]: 'expired' }
|
2026-03-05 15:26:13 +00:00
|
|
|
|
}
|
2026-03-02 12:36:41 +00:00
|
|
|
|
});
|
2026-03-07 07:23:18 +00:00
|
|
|
|
|
2026-03-02 12:36:41 +00:00
|
|
|
|
// 格式化租房信息
|
|
|
|
|
|
if (rentals.length > 0) {
|
|
|
|
|
|
formattedRoom.Rentals = rentals.map(rental => {
|
|
|
|
|
|
const formattedRental = {
|
|
|
|
|
|
...rental.toJSON(),
|
|
|
|
|
|
startDate: formatDate(rental.startDate),
|
|
|
|
|
|
endDate: formatDate(rental.endDate),
|
|
|
|
|
|
createTime: formatDate(rental.createTime)
|
|
|
|
|
|
};
|
2026-03-07 07:23:18 +00:00
|
|
|
|
|
2026-03-02 12:36:41 +00:00
|
|
|
|
return formattedRental;
|
|
|
|
|
|
});
|
|
|
|
|
|
} else {
|
|
|
|
|
|
formattedRoom.Rentals = [];
|
|
|
|
|
|
}
|
2026-03-07 07:23:18 +00:00
|
|
|
|
|
2026-03-02 12:36:41 +00:00
|
|
|
|
return formattedRoom;
|
|
|
|
|
|
}));
|
|
|
|
|
|
|
2026-03-07 07:23:18 +00:00
|
|
|
|
|
|
|
|
|
|
|
2026-03-02 12:36:41 +00:00
|
|
|
|
// 返回结果
|
|
|
|
|
|
res.status(200).json({
|
|
|
|
|
|
data: formattedRooms,
|
|
|
|
|
|
total: count,
|
|
|
|
|
|
page: parseInt(page),
|
|
|
|
|
|
pageSize: parseInt(pageSize)
|
|
|
|
|
|
});
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
res.status(500).json({ error: error.message });
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 获取单个房间
|
|
|
|
|
|
const getRoomById = async (req, res) => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
// 先检查并更新租房状态
|
|
|
|
|
|
await checkAndUpdateRentalStatus();
|
2026-03-07 07:23:18 +00:00
|
|
|
|
|
2026-03-02 12:36:41 +00:00
|
|
|
|
const { id } = req.params;
|
2026-03-03 15:36:48 +00:00
|
|
|
|
const room = await Room.findOne({
|
|
|
|
|
|
where: { id, isDeleted: 0 },
|
2026-03-02 12:36:41 +00:00
|
|
|
|
include: [Apartment]
|
|
|
|
|
|
});
|
|
|
|
|
|
if (!room) {
|
|
|
|
|
|
return res.status(404).json({ error: '房间不存在' });
|
|
|
|
|
|
}
|
2026-03-07 07:23:18 +00:00
|
|
|
|
|
2026-03-02 12:36:41 +00:00
|
|
|
|
// 格式化房间数据
|
|
|
|
|
|
const formattedRoom = formatRoomData(room);
|
2026-03-07 07:23:18 +00:00
|
|
|
|
|
2026-03-02 12:36:41 +00:00
|
|
|
|
// 查询非过期的租房信息
|
|
|
|
|
|
const rentals = await Rental.findAll({
|
|
|
|
|
|
where: {
|
|
|
|
|
|
roomId: room.id,
|
|
|
|
|
|
status: { [Op.ne]: 'expired' }
|
2026-03-05 15:26:13 +00:00
|
|
|
|
}
|
2026-03-02 12:36:41 +00:00
|
|
|
|
});
|
2026-03-07 07:23:18 +00:00
|
|
|
|
|
2026-03-02 12:36:41 +00:00
|
|
|
|
// 格式化租房信息
|
|
|
|
|
|
if (rentals.length > 0) {
|
|
|
|
|
|
formattedRoom.Rentals = rentals.map(rental => {
|
|
|
|
|
|
const formattedRental = {
|
|
|
|
|
|
...rental.toJSON(),
|
|
|
|
|
|
startDate: formatDate(rental.startDate),
|
|
|
|
|
|
endDate: formatDate(rental.endDate),
|
|
|
|
|
|
createTime: formatDate(rental.createTime)
|
|
|
|
|
|
};
|
2026-03-07 07:23:18 +00:00
|
|
|
|
|
2026-03-02 12:36:41 +00:00
|
|
|
|
return formattedRental;
|
|
|
|
|
|
});
|
|
|
|
|
|
} else {
|
|
|
|
|
|
formattedRoom.Rentals = [];
|
|
|
|
|
|
}
|
|
|
|
|
|
res.status(200).json(formattedRoom);
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
res.status(500).json({ error: error.message });
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 创建房间
|
|
|
|
|
|
const createRoom = async (req, res) => {
|
|
|
|
|
|
try {
|
2026-03-03 15:36:48 +00:00
|
|
|
|
const { apartmentId, roomNumber, area, monthlyPrice, yearlyPrice, status, subStatus, otherStatus } = req.body;
|
2026-03-05 15:26:13 +00:00
|
|
|
|
// 处理空值
|
|
|
|
|
|
const processedData = {
|
|
|
|
|
|
apartmentId,
|
|
|
|
|
|
roomNumber,
|
|
|
|
|
|
area: area === '' ? null : area,
|
|
|
|
|
|
monthlyPrice: monthlyPrice === '' ? null : monthlyPrice,
|
|
|
|
|
|
yearlyPrice: yearlyPrice === '' ? null : yearlyPrice,
|
|
|
|
|
|
status,
|
|
|
|
|
|
subStatus,
|
|
|
|
|
|
otherStatus
|
|
|
|
|
|
};
|
|
|
|
|
|
const room = await Room.create(processedData);
|
2026-03-02 12:36:41 +00:00
|
|
|
|
res.status(201).json(room);
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
res.status(500).json({ error: error.message });
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 更新房间
|
|
|
|
|
|
const updateRoom = async (req, res) => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const { id } = req.params;
|
2026-03-03 15:36:48 +00:00
|
|
|
|
const { apartmentId, roomNumber, area, monthlyPrice, yearlyPrice, status, subStatus, otherStatus } = req.body;
|
|
|
|
|
|
const room = await Room.findOne({
|
|
|
|
|
|
where: { id, isDeleted: 0 }
|
|
|
|
|
|
});
|
2026-03-02 12:36:41 +00:00
|
|
|
|
if (!room) {
|
|
|
|
|
|
return res.status(404).json({ error: '房间不存在' });
|
|
|
|
|
|
}
|
2026-03-05 15:26:13 +00:00
|
|
|
|
// 处理空值
|
|
|
|
|
|
const processedData = {
|
|
|
|
|
|
apartmentId,
|
|
|
|
|
|
roomNumber,
|
|
|
|
|
|
area: area === '' ? null : area,
|
|
|
|
|
|
monthlyPrice: monthlyPrice === '' ? null : monthlyPrice,
|
|
|
|
|
|
yearlyPrice: yearlyPrice === '' ? null : yearlyPrice,
|
|
|
|
|
|
status,
|
|
|
|
|
|
subStatus,
|
|
|
|
|
|
otherStatus
|
|
|
|
|
|
};
|
|
|
|
|
|
await room.update(processedData);
|
2026-03-02 12:36:41 +00:00
|
|
|
|
res.status(200).json(room);
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
res.status(500).json({ error: error.message });
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-03-03 15:36:48 +00:00
|
|
|
|
// 删除房间(软删除)
|
2026-03-02 12:36:41 +00:00
|
|
|
|
const deleteRoom = async (req, res) => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const { id } = req.params;
|
2026-03-03 15:36:48 +00:00
|
|
|
|
const room = await Room.findOne({
|
|
|
|
|
|
where: { id, isDeleted: 0 }
|
|
|
|
|
|
});
|
2026-03-02 12:36:41 +00:00
|
|
|
|
if (!room) {
|
|
|
|
|
|
return res.status(404).json({ error: '房间不存在' });
|
|
|
|
|
|
}
|
2026-03-03 15:36:48 +00:00
|
|
|
|
await room.update({ isDeleted: 1 });
|
2026-03-02 12:36:41 +00:00
|
|
|
|
res.status(200).json({ message: '房间删除成功' });
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
res.status(500).json({ error: error.message });
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-03-04 07:39:06 +00:00
|
|
|
|
// 获取所有房间(不分页)
|
|
|
|
|
|
const listRooms = async (req, res) => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
// 先检查并更新租房状态
|
|
|
|
|
|
await checkAndUpdateRentalStatus();
|
2026-03-07 07:23:18 +00:00
|
|
|
|
|
2026-03-04 07:39:06 +00:00
|
|
|
|
const { apartmentId, roomNumber, status, otherStatus, subStatus } = req.query;
|
2026-03-07 07:23:18 +00:00
|
|
|
|
|
2026-03-04 07:39:06 +00:00
|
|
|
|
// 构建查询条件
|
|
|
|
|
|
const where = { isDeleted: 0 };
|
|
|
|
|
|
if (apartmentId) {
|
|
|
|
|
|
where.apartmentId = apartmentId;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (roomNumber) {
|
|
|
|
|
|
where.roomNumber = {
|
|
|
|
|
|
[Op.like]: `%${roomNumber}%`
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
if (status) {
|
|
|
|
|
|
where.status = status;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (otherStatus) {
|
|
|
|
|
|
where.otherStatus = otherStatus;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (subStatus) {
|
|
|
|
|
|
where.subStatus = subStatus;
|
|
|
|
|
|
}
|
2026-03-07 07:23:18 +00:00
|
|
|
|
|
2026-03-04 07:39:06 +00:00
|
|
|
|
// 查询房间数据
|
|
|
|
|
|
const rooms = await Room.findAll({
|
|
|
|
|
|
where,
|
|
|
|
|
|
include: [
|
|
|
|
|
|
Apartment
|
|
|
|
|
|
]
|
|
|
|
|
|
});
|
2026-03-07 07:23:18 +00:00
|
|
|
|
|
2026-03-04 07:39:06 +00:00
|
|
|
|
// 格式化房间数据
|
|
|
|
|
|
const formattedRooms = rooms.map(formatRoomData);
|
2026-03-07 07:23:18 +00:00
|
|
|
|
|
2026-03-04 07:39:06 +00:00
|
|
|
|
// 返回结果
|
|
|
|
|
|
res.status(200).json(formattedRooms);
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
res.status(500).json({ error: error.message });
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-03-02 12:36:41 +00:00
|
|
|
|
module.exports = {
|
|
|
|
|
|
getAllRooms,
|
2026-03-04 07:39:06 +00:00
|
|
|
|
listRooms,
|
2026-03-02 12:36:41 +00:00
|
|
|
|
getRoomById,
|
|
|
|
|
|
createRoom,
|
|
|
|
|
|
updateRoom,
|
|
|
|
|
|
deleteRoom
|
2026-03-07 07:23:18 +00:00
|
|
|
|
};
|