This commit is contained in:
parent
79160aae9b
commit
8edf0f1afe
|
|
@ -60,7 +60,7 @@ const checkAndUpdateRentalStatus = async () => {
|
||||||
const currentDate = new Date();
|
const currentDate = new Date();
|
||||||
// 计算10天后的日期
|
// 计算10天后的日期
|
||||||
const tenDaysLater = new Date();
|
const tenDaysLater = new Date();
|
||||||
tenDaysLater.setDate(currentDate.getDate() + 10);
|
tenDaysLater.setDate(currentDate.getDate() + 30);
|
||||||
|
|
||||||
// 查找所有活跃的租房记录
|
// 查找所有活跃的租房记录
|
||||||
const rentals = await Rental.findAll({
|
const rentals = await Rental.findAll({
|
||||||
|
|
@ -73,12 +73,10 @@ const checkAndUpdateRentalStatus = async () => {
|
||||||
|
|
||||||
// 检查是否已到期
|
// 检查是否已到期
|
||||||
if (endDate < currentDate) {
|
if (endDate < currentDate) {
|
||||||
// 更新租房状态为已到期
|
// 更新房间附属状态为已到期
|
||||||
await rental.update({ status: 'expired' });
|
|
||||||
// 更新房间状态为空房
|
|
||||||
const room = await Room.findByPk(rental.roomId);
|
const room = await Room.findByPk(rental.roomId);
|
||||||
if (room && room.status === 'rented') {
|
if (room && room.status === 'rented') {
|
||||||
await room.update({ status: 'empty', subStatus: 'normal' });
|
await room.update({ subStatus: 'expired' });
|
||||||
}
|
}
|
||||||
} else if (endDate <= tenDaysLater) {
|
} else if (endDate <= tenDaysLater) {
|
||||||
// 更新房间附属状态为即将到期
|
// 更新房间附属状态为即将到期
|
||||||
|
|
|
||||||
|
|
@ -4,67 +4,53 @@ const { Op } = require('sequelize');
|
||||||
// 租金统计
|
// 租金统计
|
||||||
const getRentStatistics = async (req, res) => {
|
const getRentStatistics = async (req, res) => {
|
||||||
try {
|
try {
|
||||||
// 获取当前年份
|
// 导入Contract模型
|
||||||
const currentYear = new Date().getFullYear();
|
const { Contract } = require('../models');
|
||||||
|
|
||||||
// 从数据库查询所有租房记录(包括活跃和已到期的)
|
// 计算过去12个月的开始日期
|
||||||
const rentals = await Rental.findAll({
|
const now = new Date();
|
||||||
include: [Room]
|
const startDate = new Date(now.getFullYear(), now.getMonth() - 11, 1); // 12个月前的第一天
|
||||||
});
|
|
||||||
|
|
||||||
// 按月份统计租金
|
// 初始化过去12个月的数据
|
||||||
const monthlyRent = {};
|
const monthlyRent = {};
|
||||||
|
for (let i = 0; i < 12; i++) {
|
||||||
// 初始化12个月的数据
|
const date = new Date(now.getFullYear(), now.getMonth() - i, 1);
|
||||||
for (let i = 1; i <= 12; i++) {
|
const monthKey = `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, '0')}`;
|
||||||
const monthKey = `${currentYear}-${i.toString().padStart(2, '0')}`;
|
|
||||||
monthlyRent[monthKey] = 0;
|
monthlyRent[monthKey] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 计算每个月的租金收入
|
// 从数据库查询过去12个月的合同记录
|
||||||
rentals.forEach(rental => {
|
const contracts = await Contract.findAll({
|
||||||
if (rental.Room) {
|
where: {
|
||||||
// 检查租金是否有效
|
createTime: {
|
||||||
const rentAmount = parseFloat(rental.rent) || 0;
|
[Op.gte]: startDate
|
||||||
if (rentAmount > 0 && rental.startDate && rental.endDate) {
|
|
||||||
// 解析开始和结束日期
|
|
||||||
const startDate = new Date(rental.startDate);
|
|
||||||
const endDate = new Date(rental.endDate);
|
|
||||||
|
|
||||||
// 计算当前年份的开始和结束日期
|
|
||||||
const yearStart = new Date(currentYear, 0, 1); // 1月1日
|
|
||||||
const yearEnd = new Date(currentYear, 11, 31); // 12月31日
|
|
||||||
|
|
||||||
// 确定实际的开始和结束日期(考虑年份范围)
|
|
||||||
const actualStart = startDate > yearStart ? startDate : yearStart;
|
|
||||||
const actualEnd = endDate < yearEnd ? endDate : yearEnd;
|
|
||||||
|
|
||||||
// 如果实际开始日期晚于实际结束日期,跳过
|
|
||||||
if (actualStart > actualEnd) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 遍历当前年份的每个月
|
|
||||||
for (let i = 1; i <= 12; i++) {
|
|
||||||
const monthKey = `${currentYear}-${i.toString().padStart(2, '0')}`;
|
|
||||||
const monthStart = new Date(currentYear, i - 1, 1); // 当月1日
|
|
||||||
const monthEnd = new Date(currentYear, i, 0); // 当月最后一天
|
|
||||||
|
|
||||||
// 检查租赁期是否与当前月份重叠
|
|
||||||
if (actualStart <= monthEnd && actualEnd >= monthStart) {
|
|
||||||
// 如果重叠,添加当月租金
|
|
||||||
monthlyRent[monthKey] += rentAmount;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 转换为数组格式
|
// 按月份统计已收租金
|
||||||
const rentStatistics = Object.entries(monthlyRent).map(([month, amount]) => ({
|
contracts.forEach(contract => {
|
||||||
month,
|
// 检查租金是否有效
|
||||||
amount: Math.round(amount * 100) / 100 // 保留两位小数
|
const rentAmount = parseFloat(contract.rent) || 0;
|
||||||
}));
|
if (rentAmount > 0 && contract.createTime) {
|
||||||
|
// 解析创建时间
|
||||||
|
const createDate = new Date(contract.createTime);
|
||||||
|
const monthKey = `${createDate.getFullYear()}-${(createDate.getMonth() + 1).toString().padStart(2, '0')}`;
|
||||||
|
|
||||||
|
// 如果该月份在我们的统计范围内,添加租金
|
||||||
|
if (monthlyRent.hasOwnProperty(monthKey)) {
|
||||||
|
monthlyRent[monthKey] += rentAmount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 转换为数组格式并按月份倒序排序
|
||||||
|
const rentStatistics = Object.entries(monthlyRent)
|
||||||
|
.map(([month, amount]) => ({
|
||||||
|
month,
|
||||||
|
amount: Math.round(amount * 100) / 100 // 保留两位小数
|
||||||
|
}))
|
||||||
|
.sort((a, b) => b.month.localeCompare(a.month));
|
||||||
|
|
||||||
res.status(200).json(rentStatistics);
|
res.status(200).json(rentStatistics);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
@ -76,23 +62,46 @@ const getRentStatistics = async (req, res) => {
|
||||||
// 房间状态统计
|
// 房间状态统计
|
||||||
const getRoomStatusStatistics = async (req, res) => {
|
const getRoomStatusStatistics = async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const statusCounts = await Room.count({
|
// 获取所有房间
|
||||||
group: ['status']
|
const rooms = await Room.findAll();
|
||||||
|
|
||||||
|
// 初始化统计数据
|
||||||
|
let empty = 0;
|
||||||
|
let rented = 0;
|
||||||
|
let soon_expire = 0;
|
||||||
|
let expired = 0;
|
||||||
|
let cleaning = 0;
|
||||||
|
let maintenance = 0;
|
||||||
|
|
||||||
|
// 统计各状态房间数量
|
||||||
|
rooms.forEach(room => {
|
||||||
|
if (room.status === 'empty') {
|
||||||
|
empty++;
|
||||||
|
} else if (room.status === 'rented') {
|
||||||
|
rented++;
|
||||||
|
// 统计附属状态
|
||||||
|
if (room.subStatus === 'soon_expire') {
|
||||||
|
soon_expire++;
|
||||||
|
} else if (room.subStatus === 'expired') {
|
||||||
|
expired++;
|
||||||
|
}
|
||||||
|
// 统计其他状态
|
||||||
|
if (room.otherStatus === 'cleaning') {
|
||||||
|
cleaning++;
|
||||||
|
} else if (room.otherStatus === 'maintenance') {
|
||||||
|
maintenance++;
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const statusMap = {
|
const roomStatusStatistics = [
|
||||||
empty: '空房',
|
{ status: '空房', count: empty },
|
||||||
rented: '在租',
|
{ status: '在租', count: rented },
|
||||||
soon_expire: '即将到期',
|
{ status: '即将到期', count: soon_expire },
|
||||||
expired: '到期',
|
{ status: '到期', count: expired },
|
||||||
cleaning: '打扫中',
|
{ status: '打扫中', count: cleaning },
|
||||||
maintenance: '维修中'
|
{ status: '维修中', count: maintenance }
|
||||||
};
|
];
|
||||||
|
|
||||||
const roomStatusStatistics = Object.entries(statusMap).map(([status, label]) => {
|
|
||||||
const count = statusCounts.find(item => item.status === status)?.count || 0;
|
|
||||||
return { status: label, count };
|
|
||||||
});
|
|
||||||
|
|
||||||
res.status(200).json(roomStatusStatistics);
|
res.status(200).json(roomStatusStatistics);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
@ -122,19 +131,22 @@ const getRegionHouseStatistics = async (req, res) => {
|
||||||
|
|
||||||
region.Apartments.forEach(apartment => {
|
region.Apartments.forEach(apartment => {
|
||||||
apartment.Rooms.forEach(room => {
|
apartment.Rooms.forEach(room => {
|
||||||
switch (room.status) {
|
if (room.status === 'empty') {
|
||||||
case 'empty': empty++;
|
empty++;
|
||||||
break;
|
} else if (room.status === 'rented') {
|
||||||
case 'rented': rented++;
|
rented++;
|
||||||
break;
|
// 统计附属状态
|
||||||
case 'soon_expire': soon_expire++;
|
if (room.subStatus === 'soon_expire') {
|
||||||
break;
|
soon_expire++;
|
||||||
case 'expired': expired++;
|
} else if (room.subStatus === 'expired') {
|
||||||
break;
|
expired++;
|
||||||
case 'cleaning': cleaning++;
|
}
|
||||||
break;
|
// 统计其他状态
|
||||||
case 'maintenance': maintenance++;
|
if (room.otherStatus === 'cleaning') {
|
||||||
break;
|
cleaning++;
|
||||||
|
} else if (room.otherStatus === 'maintenance') {
|
||||||
|
maintenance++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
@ -147,7 +159,7 @@ const getRegionHouseStatistics = async (req, res) => {
|
||||||
expired,
|
expired,
|
||||||
cleaning,
|
cleaning,
|
||||||
maintenance,
|
maintenance,
|
||||||
total: empty + rented + soon_expire + expired + cleaning + maintenance
|
total: empty + rented
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -181,19 +193,22 @@ const getRegionApartmentHouseStatistics = async (req, res) => {
|
||||||
let maintenance = 0;
|
let maintenance = 0;
|
||||||
|
|
||||||
apartment.Rooms.forEach(room => {
|
apartment.Rooms.forEach(room => {
|
||||||
switch (room.status) {
|
if (room.status === 'empty') {
|
||||||
case 'empty': empty++;
|
empty++;
|
||||||
break;
|
} else if (room.status === 'rented') {
|
||||||
case 'rented': rented++;
|
rented++;
|
||||||
break;
|
// 统计附属状态
|
||||||
case 'soon_expire': soon_expire++;
|
if (room.subStatus === 'soon_expire') {
|
||||||
break;
|
soon_expire++;
|
||||||
case 'expired': expired++;
|
} else if (room.subStatus === 'expired') {
|
||||||
break;
|
expired++;
|
||||||
case 'cleaning': cleaning++;
|
}
|
||||||
break;
|
// 统计其他状态
|
||||||
case 'maintenance': maintenance++;
|
if (room.otherStatus === 'cleaning') {
|
||||||
break;
|
cleaning++;
|
||||||
|
} else if (room.otherStatus === 'maintenance') {
|
||||||
|
maintenance++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -206,7 +221,7 @@ const getRegionApartmentHouseStatistics = async (req, res) => {
|
||||||
expired,
|
expired,
|
||||||
cleaning,
|
cleaning,
|
||||||
maintenance,
|
maintenance,
|
||||||
total: empty + rented + soon_expire + expired + cleaning + maintenance
|
total: empty + rented
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
@ -217,9 +232,51 @@ const getRegionApartmentHouseStatistics = async (req, res) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Dashboard统计数据
|
||||||
|
const getDashboardStatistics = async (req, res) => {
|
||||||
|
try {
|
||||||
|
// 导入所有需要的模型
|
||||||
|
const { Region, Apartment, Room, Tenant, Contract, WaterBill, Rental } = require('../models');
|
||||||
|
|
||||||
|
// 并行查询所有统计数据
|
||||||
|
const [regionCount, apartmentCount, roomCount, tenantCount, contractCount, emptyRoomCount, rentedRoomCount, collectedRentAmount, collectedWaterAmount] = await Promise.all([
|
||||||
|
Region.count(),
|
||||||
|
Apartment.count(),
|
||||||
|
Room.count(),
|
||||||
|
Tenant.count(),
|
||||||
|
Contract.count(),
|
||||||
|
Room.count({ where: { status: 'empty' } }),
|
||||||
|
Room.count({ where: { status: 'rented' } }),
|
||||||
|
// 统计已收租金
|
||||||
|
Rental.sum('rent', { where: { status: 'active' } }),
|
||||||
|
// 统计已收水费
|
||||||
|
WaterBill.sum('amount', { where: { status: 'paid' } })
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 构造响应数据
|
||||||
|
const dashboardStatistics = {
|
||||||
|
regionCount,
|
||||||
|
apartmentCount,
|
||||||
|
roomCount,
|
||||||
|
tenantCount,
|
||||||
|
contractCount,
|
||||||
|
emptyRoomCount,
|
||||||
|
rentedRoomCount,
|
||||||
|
collectedRentAmount: parseFloat(collectedRentAmount) || 0,
|
||||||
|
collectedWaterAmount: parseFloat(collectedWaterAmount) || 0
|
||||||
|
};
|
||||||
|
|
||||||
|
res.status(200).json(dashboardStatistics);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取Dashboard统计数据时出错:', error);
|
||||||
|
res.status(500).json({ error: error.message });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
getRentStatistics,
|
getRentStatistics,
|
||||||
getRoomStatusStatistics,
|
getRoomStatusStatistics,
|
||||||
getRegionHouseStatistics,
|
getRegionHouseStatistics,
|
||||||
getRegionApartmentHouseStatistics
|
getRegionApartmentHouseStatistics,
|
||||||
|
getDashboardStatistics
|
||||||
};
|
};
|
||||||
|
|
@ -7,5 +7,6 @@ router.get('/rent', statisticsController.getRentStatistics);
|
||||||
router.get('/room-status', statisticsController.getRoomStatusStatistics);
|
router.get('/room-status', statisticsController.getRoomStatusStatistics);
|
||||||
router.get('/region-house', statisticsController.getRegionHouseStatistics);
|
router.get('/region-house', statisticsController.getRegionHouseStatistics);
|
||||||
router.get('/region-apartment-house', statisticsController.getRegionApartmentHouseStatistics);
|
router.get('/region-apartment-house', statisticsController.getRegionApartmentHouseStatistics);
|
||||||
|
router.get('/dashboard', statisticsController.getDashboardStatistics);
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
Loading…
Reference in New Issue