const { Room, Rental, Apartment, Bill, Renter } = require('../models'); const { Op } = require('sequelize'); const response = require('../utils/response'); // 租金统计 const getRentStatistics = async (req, res) => { try { // 计算过去12个月的开始日期 const now = new Date(); const startDate = new Date(now.getFullYear(), now.getMonth() - 11, 1); // 12个月前的第一天 // 初始化过去12个月的数据 const monthlyRent = {}; for (let i = 0; i < 12; i++) { const date = new Date(now.getFullYear(), now.getMonth() - i, 1); const monthKey = `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, '0')}`; monthlyRent[monthKey] = { amount: 0, depositReceived: 0, depositRefunded: 0, expense: 0 }; } // 安全地查询租赁记录 let rentals = []; try { rentals = await Rental.findAll({ where: { tenantId: req.user.tenantId, createTime: { [Op.gte]: startDate }, isDeleted: 0 } }); } catch (e) { console.warn('查询租赁记录失败:', e.message); rentals = []; } // 按月份统计已收租金和押金 rentals.forEach(rental => { if (rental.createTime) { // 解析创建时间 const createDate = new Date(rental.createTime); const monthKey = `${createDate.getFullYear()}-${(createDate.getMonth() + 1).toString().padStart(2, '0')}`; // 如果该月份在我们的统计范围内 if (monthlyRent.hasOwnProperty(monthKey)) { // 统计租金 const rentAmount = parseFloat(rental.rent) || 0; if (rentAmount > 0) { monthlyRent[monthKey].amount += rentAmount; } // 统计已收押金(创建租赁记录时的押金) const depositReceived = parseFloat(rental.deposit) || 0; if (depositReceived > 0) { monthlyRent[monthKey].depositReceived += depositReceived; } } } }); // 安全地查询支出记录(从Bill表中的expense类型) try { const expenses = await Bill.findAll({ where: { tenantId: req.user.tenantId, type: 'expense', billDate: { [Op.gte]: startDate }, isDeleted: 0 } }); // 按月份统计费用支出 expenses.forEach(expense => { if (expense.billDate) { // 解析费用日期 const expenseDate = new Date(expense.billDate); const monthKey = `${expenseDate.getFullYear()}-${(expenseDate.getMonth() + 1).toString().padStart(2, '0')}`; // 如果该月份在我们的统计范围内 if (monthlyRent.hasOwnProperty(monthKey)) { // 统计费用支出 const expenseAmount = parseFloat(expense.receivedAmount) || 0; if (expenseAmount > 0) { monthlyRent[monthKey].expense += expenseAmount; } } } }); } catch (e) { console.warn('查询支出记录失败:', e.message); } // 转换为数组格式并按月份倒序排序 const rentStatistics = Object.entries(monthlyRent) .map(([month, data]) => ({ month, amount: Math.round(data.amount * 100) / 100, // 保留两位小数 depositReceived: Math.round(data.depositReceived * 100) / 100, // 保留两位小数 depositRefunded: Math.round(data.depositRefunded * 100) / 100, // 保留两位小数 expense: Math.round(data.expense * 100) / 100 // 保留两位小数 })) .sort((a, b) => b.month.localeCompare(a.month)); response.success(res, '获取成功', rentStatistics); } catch (error) { console.error('获取租金统计数据时出错:', error); response.serverError(res, '获取租金统计数据失败', error); } }; // 房间状态统计 const getRoomStatusStatistics = async (req, res) => { try { // 获取所有房间(排除已删除的) const rooms = await Room.findAll({ where: { tenantId: req.user.tenantId, isDeleted: 0 } }); // 初始化统计数据 let empty = 0; let reserved = 0; let rented = 0; let soon_expire = 0; let expired = 0; // 统计各状态房间数量 rooms.forEach(room => { if (room.status === 'empty') { empty++; } else if (room.status === 'reserved') { reserved++; } else if (room.status === 'rented') { rented++; // 统计附属状态 if (room.rentalStatus === 'soon_expire') { soon_expire++; } else if (room.rentalStatus === 'expired') { expired++; } } }); // 返回前端期望的数据格式 const roomStatusStatistics = { total: rooms.length, empty: empty, reserved: reserved, rented: rented, soonExpire: soon_expire, expired: expired }; response.success(res, '获取成功', roomStatusStatistics); } catch (error) { response.serverError(res, '获取房间状态统计失败', error); } }; // Dashboard统计数据 const getDashboardStatistics = async (req, res) => { try { const tenantId = req.user.tenantId; const baseWhere = { tenantId, isDeleted: 0 }; // 并行查询所有统计数据 const [apartmentCount, roomCount, emptyRoomCount, reservedRoomCount, rentedRoomCount, soonExpireRoomCount, expiredRoomCount] = await Promise.all([ Apartment.count({ where: baseWhere }), Room.count({ where: baseWhere }), Room.count({ where: { ...baseWhere, status: 'empty' } }), Room.count({ where: { ...baseWhere, status: 'reserved' } }), Room.count({ where: { ...baseWhere, status: 'rented' } }), Room.count({ where: { ...baseWhere, status: 'rented', rentalStatus: 'soon_expire' } }), Room.count({ where: { ...baseWhere, status: 'rented', rentalStatus: 'expired' } }) ]); // 安全地获取sum统计 let collectedRentAmount = 0; let collectedWaterAmount = 0; try { const rentResult = await Rental.sum('rent', { where: { tenantId, isDeleted: 0 } }); collectedRentAmount = rentResult || 0; } catch (e) { console.warn('统计租金失败:', e.message); collectedRentAmount = 0; } try { const waterResult = await Bill.sum('receivedAmount', { where: { tenantId, category: 'water', status: 'paid', isDeleted: 0 } }); collectedWaterAmount = waterResult || 0; } catch (e) { console.warn('统计水费失败:', e.message); collectedWaterAmount = 0; } // 构造响应数据 const dashboardStatistics = { apartmentCount: apartmentCount || 0, roomCount: roomCount || 0, emptyRoomCount: emptyRoomCount || 0, reservedRoomCount: reservedRoomCount || 0, rentedRoomCount: rentedRoomCount || 0, soonExpireRoomCount: soonExpireRoomCount || 0, expiredRoomCount: expiredRoomCount || 0, collectedRentAmount: parseFloat(collectedRentAmount) || 0, collectedWaterAmount: parseFloat(collectedWaterAmount) || 0 }; response.success(res, '获取成功', dashboardStatistics); } catch (error) { console.error('获取Dashboard统计数据时出错:', error); response.serverError(res, '获取Dashboard统计数据失败', error); } }; // 公寓房间状态分布统计 const getApartmentRoomStatusStatistics = async (req, res) => { try { // 获取所有公寓(排除已删除的) const apartments = await Apartment.findAll({ where: { tenantId: req.user.tenantId, isDeleted: 0 } }); // 获取所有房间(排除已删除的) const rooms = await Room.findAll({ where: { tenantId: req.user.tenantId, isDeleted: 0 } }); // 构建公寓房间状态分布数据 const apartmentRoomStatusStatistics = apartments.map(apartment => { const apartmentRooms = rooms.filter(room => room.apartmentId === apartment.id); let empty = 0; let reserved = 0; let rented = 0; let soon_expire = 0; let expired = 0; apartmentRooms.forEach(room => { if (room.status === 'empty') { empty++; } else if (room.status === 'reserved') { reserved++; } else if (room.status === 'rented') { rented++; if (room.rentalStatus === 'soon_expire') { soon_expire++; } else if (room.rentalStatus === 'expired') { expired++; } } }); return { apartmentId: apartment.id, apartment: apartment.name, empty, reserved, rented, soon_expire, expired, total: empty + reserved + rented }; }); response.success(res, '获取成功', apartmentRoomStatusStatistics); } catch (error) { console.error('获取公寓房间状态分布数据时出错:', error); response.serverError(res, '获取公寓房间状态分布数据失败', error); } }; // 空房分布统计(按公寓分组) const getEmptyRoomsByApartment = async (req, res) => { try { // 获取所有公寓(排除已删除的) const apartments = await Apartment.findAll({ where: { tenantId: req.user.tenantId, isDeleted: 0 } }); // 获取所有空房(排除已删除的) const emptyRooms = await Room.findAll({ where: { tenantId: req.user.tenantId, status: 'empty', isDeleted: 0 } }); // 构建按公寓分组的空房数据 const emptyRoomsByApartment = apartments.map(apartment => { const apartmentEmptyRooms = emptyRooms .filter(room => room.apartmentId === apartment.id) .map(room => ({ id: room.id, roomNumber: room.roomNumber, type: room.type, area: room.area })); return { apartmentId: apartment.id, apartmentName: apartment.name, emptyRooms: apartmentEmptyRooms }; }); response.success(res, '获取成功', emptyRoomsByApartment); } catch (error) { console.error('获取空房分布数据时出错:', error); response.serverError(res, '获取空房分布数据失败', error); } }; // 在租分布统计(按公寓分组) const getRentedRoomsByApartment = async (req, res) => { try { // 获取所有公寓(排除已删除的) const apartments = await Apartment.findAll({ where: { tenantId: req.user.tenantId, isDeleted: 0 } }); // 获取所有在租房(排除已删除的) const rentedRooms = await Room.findAll({ where: { tenantId: req.user.tenantId, status: 'rented', isDeleted: 0 } }); // 构建按公寓分组的在租数据 const rentedRoomsByApartment = apartments.map(apartment => { const apartmentRentedRooms = rentedRooms .filter(room => room.apartmentId === apartment.id) .map(room => ({ id: room.id, roomNumber: room.roomNumber, type: room.type, area: room.area })); return { apartmentId: apartment.id, apartmentName: apartment.name, rentedRooms: apartmentRentedRooms }; }); response.success(res, '获取成功', rentedRoomsByApartment); } catch (error) { console.error('获取在租分布数据时出错:', error); response.serverError(res, '获取在租分布数据失败', error); } }; // 租客在租统计 const getTenantRentalStats = async (req, res) => { try { const tenantId = req.user.tenantId; // 获取所有在租的租赁记录(排除已删除的) const rentals = await Rental.findAll({ where: { tenantId, status: 'active', isDeleted: 0 }, include: [{ model: Room, where: { isDeleted: 0 } }, { model: Renter, attributes: ['id', 'name'] }] }); // 获取所有公寓(排除已删除的) const apartments = await Apartment.findAll({ where: { tenantId, isDeleted: 0 } }); // 按租客分组 const tenantMap = new Map(); rentals.forEach(rental => { const tenantName = rental.Renter ? rental.Renter.name : '未知租客'; const room = rental.Room; if (!tenantMap.has(tenantName)) { tenantMap.set(tenantName, { tenantName, count: 0, apartments: [] }); } const tenantData = tenantMap.get(tenantName); tenantData.count++; // 查找房间所属的公寓 const apartment = apartments.find(apt => apt.id === room.apartmentId); if (apartment) { let apartmentData = tenantData.apartments.find(apt => apt.apartmentId === apartment.id); if (!apartmentData) { apartmentData = { apartmentId: apartment.id, apartmentName: apartment.name, rooms: [] }; tenantData.apartments.push(apartmentData); } apartmentData.rooms.push({ id: room.id, roomNumber: room.roomNumber, type: room.type, area: room.area }); } }); // 转换为数组 const tenantRentalStats = Array.from(tenantMap.values()); response.success(res, '获取成功', tenantRentalStats); } catch (error) { console.error('获取租客在租统计数据时出错:', error); response.serverError(res, '获取租客在租统计数据失败', error); } }; // 即将到期房间分布统计(按公寓分组) const getSoonExpireRoomsByApartment = async (req, res) => { try { // 获取所有公寓(排除已删除的) const apartments = await Apartment.findAll({ where: { tenantId: req.user.tenantId, isDeleted: 0 } }); // 获取所有即将到期的房间(排除已删除的) const soonExpireRooms = await Room.findAll({ where: { tenantId: req.user.tenantId, status: 'rented', rentalStatus: 'soon_expire', isDeleted: 0 } }); // 构建按公寓分组的即将到期房间数据 const soonExpireRoomsByApartment = apartments.map(apartment => { const apartmentSoonExpireRooms = soonExpireRooms .filter(room => room.apartmentId === apartment.id) .map(room => ({ id: room.id, roomNumber: room.roomNumber, type: room.type, area: room.area })); return { apartmentId: apartment.id, apartmentName: apartment.name, soonExpireRooms: apartmentSoonExpireRooms }; }); response.success(res, '获取成功', soonExpireRoomsByApartment); } catch (error) { console.error('获取即将到期分布数据时出错:', error); response.serverError(res, '获取即将到期分布数据失败', error); } }; // 已到期房间分布统计(按公寓分组) const getExpiredRoomsByApartment = async (req, res) => { try { // 获取所有公寓(排除已删除的) const apartments = await Apartment.findAll({ where: { tenantId: req.user.tenantId, isDeleted: 0 } }); // 获取所有已到期的房间(排除已删除的) const expiredRooms = await Room.findAll({ where: { tenantId: req.user.tenantId, status: 'rented', rentalStatus: 'expired', isDeleted: 0 } }); // 构建按公寓分组的已到期房间数据 const expiredRoomsByApartment = apartments.map(apartment => { const apartmentExpiredRooms = expiredRooms .filter(room => room.apartmentId === apartment.id) .map(room => ({ id: room.id, roomNumber: room.roomNumber, type: room.type, area: room.area })); return { apartmentId: apartment.id, apartmentName: apartment.name, expiredRooms: apartmentExpiredRooms }; }); response.success(res, '获取成功', expiredRoomsByApartment); } catch (error) { console.error('获取已到期分布数据时出错:', error); response.serverError(res, '获取已到期分布数据失败', error); } }; module.exports = { getRentStatistics, getRoomStatusStatistics, getDashboardStatistics, getApartmentRoomStatusStatistics, getEmptyRoomsByApartment, getRentedRoomsByApartment, getSoonExpireRoomsByApartment, getExpiredRoomsByApartment, getTenantRentalStats };