2026-03-08 16:28:33 +00:00
|
|
|
|
const { Rental, Room, Apartment } = require('../models');
|
2026-03-02 12:36:41 +00:00
|
|
|
|
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 formatRentalData = (rental) => {
|
|
|
|
|
|
const formattedRental = {
|
|
|
|
|
|
...rental.toJSON(),
|
|
|
|
|
|
startDate: formatDate(rental.startDate),
|
|
|
|
|
|
endDate: formatDate(rental.endDate),
|
2026-03-03 15:36:48 +00:00
|
|
|
|
createTime: formatDate(rental.createTime),
|
|
|
|
|
|
updateTime: formatDate(rental.updateTime)
|
2026-03-02 12:36:41 +00:00
|
|
|
|
};
|
2026-03-04 07:09:52 +00:00
|
|
|
|
|
2026-03-02 12:36:41 +00:00
|
|
|
|
return formattedRental;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 检查并更新租房状态
|
|
|
|
|
|
const checkAndUpdateRentalStatus = async () => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
// 获取当前日期
|
|
|
|
|
|
const currentDate = new Date();
|
2026-03-09 13:27:09 +00:00
|
|
|
|
// 计算5天后的日期
|
|
|
|
|
|
const fiveDaysLater = new Date();
|
|
|
|
|
|
fiveDaysLater.setDate(currentDate.getDate() + 5);
|
2026-03-04 07:09:52 +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 },
|
|
|
|
|
|
include: [
|
|
|
|
|
|
{
|
|
|
|
|
|
model: Room,
|
|
|
|
|
|
where: { isDeleted: 0 }
|
|
|
|
|
|
}
|
|
|
|
|
|
]
|
2026-03-02 12:36:41 +00:00
|
|
|
|
});
|
2026-03-04 07:09:52 +00:00
|
|
|
|
|
2026-03-02 12:36:41 +00:00
|
|
|
|
// 检查每个租房记录的状态
|
|
|
|
|
|
for (const rental of rentals) {
|
|
|
|
|
|
const endDate = new Date(rental.endDate);
|
2026-03-04 07:09:52 +00:00
|
|
|
|
|
2026-03-02 12:36:41 +00:00
|
|
|
|
// 检查是否已到期
|
|
|
|
|
|
if (endDate < currentDate) {
|
2026-03-04 05:03:31 +00:00
|
|
|
|
// 更新房间附属状态为已到期
|
|
|
|
|
|
const room = await Room.findByPk(rental.roomId);
|
|
|
|
|
|
if (room && room.status === 'rented') {
|
|
|
|
|
|
await room.update({ subStatus: 'expired' });
|
2026-03-02 12:36:41 +00:00
|
|
|
|
}
|
2026-03-09 13:27:09 +00:00
|
|
|
|
} else if (endDate <= fiveDaysLater) {
|
2026-03-04 05:03:31 +00:00
|
|
|
|
// 更新房间附属状态为即将到期
|
|
|
|
|
|
const room = await Room.findByPk(rental.roomId);
|
|
|
|
|
|
if (room && room.status === 'rented') {
|
|
|
|
|
|
await room.update({ subStatus: 'soon_expire' });
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 更新房间附属状态为正常
|
|
|
|
|
|
const room = await Room.findByPk(rental.roomId);
|
|
|
|
|
|
if (room && room.status === 'rented') {
|
|
|
|
|
|
await room.update({ subStatus: 'normal' });
|
2026-03-02 12:36:41 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-03-04 07:09:52 +00:00
|
|
|
|
|
2026-03-02 12:36:41 +00:00
|
|
|
|
console.log('租房状态检查和更新完成');
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('检查和更新租房状态时出错:', error);
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 获取所有租房(支持搜索和分页)
|
|
|
|
|
|
const getAllRentals = async (req, res) => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
// 先检查并更新租房状态
|
|
|
|
|
|
await checkAndUpdateRentalStatus();
|
2026-03-04 07:09:52 +00:00
|
|
|
|
|
2026-03-07 07:23:18 +00:00
|
|
|
|
const {
|
|
|
|
|
|
apartmentId,
|
|
|
|
|
|
roomId,
|
|
|
|
|
|
tenantName,
|
|
|
|
|
|
status,
|
|
|
|
|
|
startDateFrom,
|
|
|
|
|
|
startDateTo,
|
|
|
|
|
|
endDateFrom,
|
|
|
|
|
|
endDateTo,
|
|
|
|
|
|
page = 1,
|
|
|
|
|
|
pageSize = 10
|
2026-03-05 16:05:51 +00:00
|
|
|
|
} = req.query;
|
2026-03-04 07:09:52 +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 (status) {
|
|
|
|
|
|
where.status = status;
|
|
|
|
|
|
}
|
2026-03-04 07:09:52 +00:00
|
|
|
|
if (roomId) {
|
|
|
|
|
|
where.roomId = roomId;
|
|
|
|
|
|
}
|
2026-03-05 15:26:13 +00:00
|
|
|
|
if (tenantName) {
|
|
|
|
|
|
where.tenantName = { [Op.like]: `%${tenantName}%` };
|
|
|
|
|
|
}
|
2026-03-05 16:05:51 +00:00
|
|
|
|
if (startDateFrom && startDateTo) {
|
|
|
|
|
|
where.startDate = { [Op.between]: [new Date(startDateFrom), new Date(startDateTo)] };
|
|
|
|
|
|
}
|
|
|
|
|
|
if (endDateFrom && endDateTo) {
|
|
|
|
|
|
where.endDate = { [Op.between]: [new Date(endDateFrom), new Date(endDateTo)] };
|
|
|
|
|
|
}
|
2026-03-04 07:09:52 +00:00
|
|
|
|
|
2026-03-05 15:26:13 +00:00
|
|
|
|
// 构建包含关系
|
2026-03-02 12:36:41 +00:00
|
|
|
|
const include = [
|
|
|
|
|
|
{
|
2026-03-05 15:26:13 +00:00
|
|
|
|
model: Room,
|
2026-03-07 07:23:18 +00:00
|
|
|
|
where: {
|
2026-03-05 16:05:51 +00:00
|
|
|
|
isDeleted: 0,
|
|
|
|
|
|
...(apartmentId ? { apartmentId } : {})
|
|
|
|
|
|
},
|
2026-03-05 15:26:13 +00:00
|
|
|
|
include: [
|
|
|
|
|
|
{
|
|
|
|
|
|
model: Apartment,
|
2026-03-08 16:28:33 +00:00
|
|
|
|
where: { isDeleted: 0 }
|
2026-03-05 15:26:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
]
|
2026-03-03 15:36:48 +00:00
|
|
|
|
}
|
2026-03-02 12:36:41 +00:00
|
|
|
|
];
|
2026-03-04 07:09:52 +00:00
|
|
|
|
|
2026-03-02 12:36:41 +00:00
|
|
|
|
// 计算偏移量
|
|
|
|
|
|
const offset = (page - 1) * pageSize;
|
2026-03-04 07:09:52 +00:00
|
|
|
|
|
2026-03-02 12:36:41 +00:00
|
|
|
|
// 查询租房数据
|
|
|
|
|
|
const { count, rows } = await Rental.findAndCountAll({
|
|
|
|
|
|
where,
|
|
|
|
|
|
include,
|
|
|
|
|
|
limit: parseInt(pageSize),
|
2026-03-04 07:09:52 +00:00
|
|
|
|
offset: parseInt(offset),
|
|
|
|
|
|
order: [['createTime', 'DESC']] // 按创建时间倒序排序
|
2026-03-02 12:36:41 +00:00
|
|
|
|
});
|
2026-03-04 07:09:52 +00:00
|
|
|
|
|
2026-03-02 12:36:41 +00:00
|
|
|
|
// 格式化数据
|
|
|
|
|
|
const formattedRentals = rows.map(formatRentalData);
|
2026-03-04 07:09:52 +00:00
|
|
|
|
|
2026-03-02 12:36:41 +00:00
|
|
|
|
// 返回结果
|
|
|
|
|
|
res.status(200).json({
|
|
|
|
|
|
data: formattedRentals,
|
|
|
|
|
|
total: count,
|
|
|
|
|
|
page: parseInt(page),
|
|
|
|
|
|
pageSize: parseInt(pageSize)
|
|
|
|
|
|
});
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
res.status(500).json({ error: error.message });
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 获取单个租房
|
|
|
|
|
|
const getRentalById = async (req, res) => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const { id } = req.params;
|
2026-03-03 15:36:48 +00:00
|
|
|
|
const rental = await Rental.findOne({
|
|
|
|
|
|
where: { id, isDeleted: 0 },
|
2026-03-02 12:36:41 +00:00
|
|
|
|
include: [
|
|
|
|
|
|
{
|
|
|
|
|
|
model: Room,
|
2026-03-03 15:36:48 +00:00
|
|
|
|
where: { isDeleted: 0 },
|
2026-03-02 12:36:41 +00:00
|
|
|
|
include: [
|
|
|
|
|
|
{
|
|
|
|
|
|
model: Apartment,
|
2026-03-09 13:27:09 +00:00
|
|
|
|
where: { isDeleted: 0 }
|
2026-03-02 12:36:41 +00:00
|
|
|
|
}
|
|
|
|
|
|
]
|
2026-03-03 15:36:48 +00:00
|
|
|
|
}
|
2026-03-02 12:36:41 +00:00
|
|
|
|
]
|
|
|
|
|
|
});
|
|
|
|
|
|
if (!rental) {
|
|
|
|
|
|
return res.status(404).json({ error: '租房记录不存在' });
|
|
|
|
|
|
}
|
|
|
|
|
|
const formattedRental = formatRentalData(rental);
|
|
|
|
|
|
res.status(200).json(formattedRental);
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
res.status(500).json({ error: error.message });
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 创建租房
|
|
|
|
|
|
const createRental = async (req, res) => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
console.log('接收到的请求数据:', req.body);
|
2026-03-04 07:09:52 +00:00
|
|
|
|
|
2026-03-02 12:36:41 +00:00
|
|
|
|
// 直接使用req.body中的数据
|
|
|
|
|
|
const body = req.body;
|
2026-03-04 07:09:52 +00:00
|
|
|
|
|
2026-03-02 12:36:41 +00:00
|
|
|
|
// 检查请求体是否存在
|
|
|
|
|
|
if (!body) {
|
|
|
|
|
|
return res.status(400).json({ error: '请求体不能为空' });
|
|
|
|
|
|
}
|
2026-03-04 07:09:52 +00:00
|
|
|
|
|
2026-03-02 12:36:41 +00:00
|
|
|
|
// 检查所有必要参数
|
|
|
|
|
|
if (!body.roomId) {
|
|
|
|
|
|
return res.status(400).json({ error: '缺少房间ID' });
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!body.tenantName) {
|
|
|
|
|
|
return res.status(400).json({ error: '缺少租客姓名' });
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!body.startDate) {
|
|
|
|
|
|
return res.status(400).json({ error: '缺少开始日期' });
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!body.endDate) {
|
|
|
|
|
|
return res.status(400).json({ error: '缺少结束日期' });
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!body.rent) {
|
|
|
|
|
|
return res.status(400).json({ error: '缺少租金' });
|
|
|
|
|
|
}
|
2026-03-04 07:09:52 +00:00
|
|
|
|
|
2026-03-02 12:36:41 +00:00
|
|
|
|
// 转换roomId为整数类型
|
|
|
|
|
|
const parsedRoomId = parseInt(body.roomId);
|
|
|
|
|
|
if (isNaN(parsedRoomId)) {
|
|
|
|
|
|
return res.status(400).json({ error: '无效的房间ID' });
|
|
|
|
|
|
}
|
2026-03-04 07:09:52 +00:00
|
|
|
|
|
|
|
|
|
|
// 处理押金,为空时设置为0
|
|
|
|
|
|
const deposit = body.deposit || 0;
|
2026-03-09 13:27:09 +00:00
|
|
|
|
// 处理已退押金,为空时设置为0
|
|
|
|
|
|
const refundedDeposit = body.refundedDeposit || 0;
|
2026-03-04 07:09:52 +00:00
|
|
|
|
|
2026-03-02 12:36:41 +00:00
|
|
|
|
// 创建租房记录
|
2026-03-04 07:09:52 +00:00
|
|
|
|
console.log('创建租房记录:', {
|
|
|
|
|
|
roomId: parsedRoomId,
|
2026-03-05 15:26:13 +00:00
|
|
|
|
tenantName: body.tenantName,
|
2026-03-04 07:09:52 +00:00
|
|
|
|
startDate: body.startDate,
|
|
|
|
|
|
endDate: body.endDate,
|
|
|
|
|
|
rent: body.rent,
|
|
|
|
|
|
deposit: deposit,
|
2026-03-09 13:27:09 +00:00
|
|
|
|
refundedDeposit: refundedDeposit,
|
2026-03-10 11:55:34 +00:00
|
|
|
|
status: body.status || 'active',
|
|
|
|
|
|
remark: body.remark
|
2026-03-02 12:36:41 +00:00
|
|
|
|
});
|
2026-03-04 07:09:52 +00:00
|
|
|
|
|
2026-03-02 12:36:41 +00:00
|
|
|
|
const rental = await Rental.create({
|
|
|
|
|
|
roomId: parsedRoomId,
|
2026-03-05 15:26:13 +00:00
|
|
|
|
tenantName: body.tenantName,
|
2026-03-02 12:36:41 +00:00
|
|
|
|
startDate: body.startDate,
|
|
|
|
|
|
endDate: body.endDate,
|
|
|
|
|
|
rent: body.rent,
|
2026-03-04 07:09:52 +00:00
|
|
|
|
deposit: deposit,
|
2026-03-09 13:27:09 +00:00
|
|
|
|
refundedDeposit: refundedDeposit,
|
2026-03-08 16:28:33 +00:00
|
|
|
|
status: body.status || 'active',
|
2026-03-10 11:55:34 +00:00
|
|
|
|
remark: body.remark,
|
2026-03-08 16:28:33 +00:00
|
|
|
|
createBy: req.user.id,
|
|
|
|
|
|
updateBy: req.user.id
|
2026-03-02 12:36:41 +00:00
|
|
|
|
});
|
2026-03-04 07:09:52 +00:00
|
|
|
|
|
2026-03-02 12:36:41 +00:00
|
|
|
|
console.log('租房记录:', rental);
|
2026-03-04 07:09:52 +00:00
|
|
|
|
|
2026-03-02 12:36:41 +00:00
|
|
|
|
// 更新房间状态为已租
|
|
|
|
|
|
await Room.update({ status: 'rented' }, { where: { id: parsedRoomId } });
|
2026-03-04 07:09:52 +00:00
|
|
|
|
|
2026-03-02 12:36:41 +00:00
|
|
|
|
res.status(201).json(rental);
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('创建租房记录时出错:', error);
|
|
|
|
|
|
res.status(500).json({ error: error.message });
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 更新租房
|
|
|
|
|
|
const updateRental = async (req, res) => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const { id } = req.params;
|
2026-03-09 13:27:09 +00:00
|
|
|
|
const { roomId, tenantName, startDate, endDate, rent, deposit, refundedDeposit, status, remark } = req.body;
|
2026-03-03 15:36:48 +00:00
|
|
|
|
const rental = await Rental.findOne({
|
|
|
|
|
|
where: { id, isDeleted: 0 }
|
|
|
|
|
|
});
|
2026-03-02 12:36:41 +00:00
|
|
|
|
if (!rental) {
|
|
|
|
|
|
return res.status(404).json({ error: '租房记录不存在' });
|
|
|
|
|
|
}
|
2026-03-04 07:09:52 +00:00
|
|
|
|
// 处理押金,为空时设置为0
|
|
|
|
|
|
const updateDeposit = deposit || 0;
|
2026-03-09 13:27:09 +00:00
|
|
|
|
// 处理已退押金,为空时设置为0
|
|
|
|
|
|
const updateRefundedDeposit = refundedDeposit || 0;
|
2026-03-08 16:28:33 +00:00
|
|
|
|
await rental.update({
|
|
|
|
|
|
roomId,
|
|
|
|
|
|
tenantName,
|
|
|
|
|
|
startDate,
|
|
|
|
|
|
endDate,
|
|
|
|
|
|
rent,
|
|
|
|
|
|
deposit: updateDeposit,
|
2026-03-09 13:27:09 +00:00
|
|
|
|
refundedDeposit: updateRefundedDeposit,
|
2026-03-08 16:28:33 +00:00
|
|
|
|
status,
|
2026-03-09 13:27:09 +00:00
|
|
|
|
remark,
|
2026-03-08 16:28:33 +00:00
|
|
|
|
updateBy: req.user.id
|
|
|
|
|
|
});
|
2026-03-02 12:36:41 +00:00
|
|
|
|
res.status(200).json(rental);
|
|
|
|
|
|
} 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 deleteRental = async (req, res) => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const { id } = req.params;
|
2026-03-03 15:36:48 +00:00
|
|
|
|
const rental = await Rental.findOne({
|
|
|
|
|
|
where: { id, isDeleted: 0 }
|
|
|
|
|
|
});
|
2026-03-02 12:36:41 +00:00
|
|
|
|
if (!rental) {
|
|
|
|
|
|
return res.status(404).json({ error: '租房记录不存在' });
|
|
|
|
|
|
}
|
2026-03-08 16:28:33 +00:00
|
|
|
|
await rental.update({
|
|
|
|
|
|
isDeleted: 1,
|
|
|
|
|
|
updateBy: req.user.id
|
|
|
|
|
|
});
|
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 listRentals = async (req, res) => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
// 先检查并更新租房状态
|
|
|
|
|
|
await checkAndUpdateRentalStatus();
|
|
|
|
|
|
|
2026-03-07 07:23:18 +00:00
|
|
|
|
const {
|
|
|
|
|
|
apartmentId,
|
|
|
|
|
|
roomId,
|
|
|
|
|
|
tenantName,
|
|
|
|
|
|
status,
|
|
|
|
|
|
startDateFrom,
|
|
|
|
|
|
startDateTo,
|
|
|
|
|
|
endDateFrom,
|
|
|
|
|
|
endDateTo
|
2026-03-05 16:05:51 +00:00
|
|
|
|
} = req.query;
|
2026-03-04 07:39:06 +00:00
|
|
|
|
|
|
|
|
|
|
// 构建查询条件
|
|
|
|
|
|
const where = { isDeleted: 0 };
|
|
|
|
|
|
if (status) {
|
|
|
|
|
|
where.status = status;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (roomId) {
|
|
|
|
|
|
where.roomId = roomId;
|
|
|
|
|
|
}
|
2026-03-05 15:26:13 +00:00
|
|
|
|
if (tenantName) {
|
|
|
|
|
|
where.tenantName = { [Op.like]: `%${tenantName}%` };
|
|
|
|
|
|
}
|
2026-03-05 16:05:51 +00:00
|
|
|
|
if (startDateFrom && startDateTo) {
|
|
|
|
|
|
where.startDate = { [Op.between]: [new Date(startDateFrom), new Date(startDateTo)] };
|
|
|
|
|
|
}
|
|
|
|
|
|
if (endDateFrom && endDateTo) {
|
|
|
|
|
|
where.endDate = { [Op.between]: [new Date(endDateFrom), new Date(endDateTo)] };
|
|
|
|
|
|
}
|
2026-03-04 07:39:06 +00:00
|
|
|
|
|
2026-03-05 15:26:13 +00:00
|
|
|
|
// 构建包含关系
|
2026-03-04 07:39:06 +00:00
|
|
|
|
const include = [
|
|
|
|
|
|
{
|
2026-03-05 15:26:13 +00:00
|
|
|
|
model: Room,
|
2026-03-07 07:23:18 +00:00
|
|
|
|
where: {
|
2026-03-05 16:05:51 +00:00
|
|
|
|
isDeleted: 0,
|
|
|
|
|
|
...(apartmentId ? { apartmentId } : {})
|
|
|
|
|
|
},
|
2026-03-05 15:26:13 +00:00
|
|
|
|
include: [
|
|
|
|
|
|
{
|
|
|
|
|
|
model: Apartment,
|
2026-03-08 16:28:33 +00:00
|
|
|
|
where: { isDeleted: 0 }
|
2026-03-05 15:26:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
]
|
2026-03-04 07:39:06 +00:00
|
|
|
|
}
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
// 查询租房数据
|
|
|
|
|
|
const rentals = await Rental.findAll({
|
|
|
|
|
|
where,
|
|
|
|
|
|
include,
|
|
|
|
|
|
order: [['createTime', 'DESC']] // 按创建时间倒序排序
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 格式化数据
|
|
|
|
|
|
const formattedRentals = rentals.map(formatRentalData);
|
|
|
|
|
|
|
|
|
|
|
|
// 返回结果
|
|
|
|
|
|
res.status(200).json(formattedRentals);
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
res.status(500).json({ error: error.message });
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-05-23 07:55:32 +00:00
|
|
|
|
// 获取日历数据(按月查询到期租约)
|
|
|
|
|
|
const getCalendarRentals = async (req, res) => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const { year, month, apartmentId, tenantName } = req.query;
|
|
|
|
|
|
const currentYear = parseInt(year) || new Date().getFullYear();
|
|
|
|
|
|
const currentMonth = parseInt(month) || (new Date().getMonth() + 1);
|
|
|
|
|
|
|
|
|
|
|
|
const startDate = new Date(currentYear, currentMonth - 1, 1);
|
|
|
|
|
|
const endDate = new Date(currentYear, currentMonth, 0, 23, 59, 59);
|
|
|
|
|
|
|
|
|
|
|
|
// 1. 以房间为主表查询(在租房间)
|
|
|
|
|
|
const roomWhere = { isDeleted: 0, status: 'rented' };
|
|
|
|
|
|
if (apartmentId) roomWhere.apartmentId = apartmentId;
|
|
|
|
|
|
|
|
|
|
|
|
const rooms = await Room.findAll({
|
|
|
|
|
|
where: roomWhere,
|
|
|
|
|
|
include: [{ model: Apartment, where: { isDeleted: 0 } }]
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
if (rooms.length === 0) {
|
|
|
|
|
|
return res.status(200).json({ data: [], year: currentYear, month: currentMonth });
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 2. 查这些房间的所有租约(不限 endDate),按 id 降序
|
|
|
|
|
|
const roomIds = rooms.map(r => r.id);
|
|
|
|
|
|
const rentals = await Rental.findAll({
|
|
|
|
|
|
where: { isDeleted: 0, roomId: { [Op.in]: roomIds } },
|
|
|
|
|
|
order: [['id', 'DESC']]
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 3. 每个房间只保留最新一条租约
|
|
|
|
|
|
const latestByRoom = new Map();
|
|
|
|
|
|
for (const rental of rentals) {
|
|
|
|
|
|
if (!latestByRoom.has(rental.roomId)) {
|
|
|
|
|
|
latestByRoom.set(rental.roomId, rental);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 4. 租客过滤(在去重之后,只按最新租约的 tenantName 匹配)
|
|
|
|
|
|
const latestRentals = Array.from(latestByRoom.values());
|
|
|
|
|
|
const filteredLatest = tenantName
|
|
|
|
|
|
? latestRentals.filter(r => r.tenantName === tenantName)
|
|
|
|
|
|
: latestRentals;
|
|
|
|
|
|
|
|
|
|
|
|
// 5. 从最新租约中过滤出当月到期的
|
|
|
|
|
|
const monthRentals = filteredLatest.filter(rental => {
|
|
|
|
|
|
return rental.endDate >= startDate && rental.endDate <= endDate;
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 6. 组装数据
|
|
|
|
|
|
const roomMap = new Map(rooms.map(r => [r.id, r]));
|
|
|
|
|
|
const data = monthRentals.map(rental => {
|
|
|
|
|
|
const room = roomMap.get(rental.roomId) || {};
|
|
|
|
|
|
return {
|
|
|
|
|
|
id: rental.id,
|
|
|
|
|
|
tenantName: rental.tenantName,
|
2026-05-23 15:34:15 +00:00
|
|
|
|
startDate: formatDate(rental.startDate),
|
2026-05-23 07:55:32 +00:00
|
|
|
|
endDate: formatDate(rental.endDate),
|
|
|
|
|
|
status: rental.status,
|
|
|
|
|
|
rent: rental.rent,
|
|
|
|
|
|
deposit: rental.deposit,
|
|
|
|
|
|
roomId: room.id || null,
|
|
|
|
|
|
roomNumber: room.roomNumber || '',
|
|
|
|
|
|
apartmentName: room.Apartment?.name || '',
|
|
|
|
|
|
subStatus: room.subStatus || 'normal'
|
|
|
|
|
|
};
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 7. 按到期日排序
|
|
|
|
|
|
data.sort((a, b) => a.endDate.localeCompare(b.endDate));
|
|
|
|
|
|
|
|
|
|
|
|
res.status(200).json({
|
|
|
|
|
|
data,
|
|
|
|
|
|
year: currentYear,
|
|
|
|
|
|
month: currentMonth
|
|
|
|
|
|
});
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
res.status(500).json({ error: error.message });
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-03-02 12:36:41 +00:00
|
|
|
|
module.exports = {
|
|
|
|
|
|
getAllRentals,
|
2026-03-04 07:39:06 +00:00
|
|
|
|
listRentals,
|
2026-03-02 12:36:41 +00:00
|
|
|
|
getRentalById,
|
|
|
|
|
|
createRental,
|
|
|
|
|
|
updateRental,
|
2026-05-23 07:55:32 +00:00
|
|
|
|
deleteRental,
|
|
|
|
|
|
getCalendarRentals
|
2026-03-04 07:09:52 +00:00
|
|
|
|
};
|