2026-04-20 06:43:09 +00:00
|
|
|
|
const { Apartment, Tenant } = require('../models');
|
|
|
|
|
|
const { Op } = require('sequelize');
|
|
|
|
|
|
const { logOperation } = require('../utils/logger');
|
2026-04-22 06:48:32 +00:00
|
|
|
|
const response = require('../utils/response');
|
2026-04-20 06:43:09 +00:00
|
|
|
|
|
|
|
|
|
|
// 格式化时间(考虑时区,转换为北京时间)
|
|
|
|
|
|
const formatDate = (date) => {
|
|
|
|
|
|
if (!date) return null;
|
|
|
|
|
|
// 确保date是Date对象
|
|
|
|
|
|
const dateObj = date instanceof Date ? date : new Date(date);
|
|
|
|
|
|
// 创建一个新的Date对象,加上8小时的时区偏移
|
|
|
|
|
|
const beijingDate = new Date(dateObj.getTime() + 8 * 60 * 60 * 1000);
|
|
|
|
|
|
return beijingDate.toISOString().split('T')[0];
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 格式化公寓数据
|
|
|
|
|
|
const formatApartmentData = (apartment) => {
|
|
|
|
|
|
return {
|
|
|
|
|
|
...apartment.toJSON(),
|
|
|
|
|
|
createTime: formatDate(apartment.createTime),
|
|
|
|
|
|
updateTime: formatDate(apartment.updateTime)
|
|
|
|
|
|
};
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 获取所有公寓(支持搜索和分页)
|
|
|
|
|
|
const getAllApartments = async (req, res) => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const { regionId, name, page = 1, pageSize = 10 } = req.query;
|
|
|
|
|
|
|
|
|
|
|
|
// 构建查询条件
|
|
|
|
|
|
const where = { isDeleted: 0 };
|
|
|
|
|
|
if (regionId) {
|
|
|
|
|
|
where.regionId = regionId;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (name) {
|
|
|
|
|
|
where.name = {
|
|
|
|
|
|
[Op.like]: `%${name}%`
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 计算偏移量
|
|
|
|
|
|
const offset = (page - 1) * pageSize;
|
|
|
|
|
|
|
|
|
|
|
|
// 查询公寓数据
|
|
|
|
|
|
const { count, rows } = await Apartment.findAndCountAll({
|
|
|
|
|
|
where,
|
|
|
|
|
|
limit: parseInt(pageSize),
|
|
|
|
|
|
offset: parseInt(offset)
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 格式化数据
|
|
|
|
|
|
const formattedApartments = rows.map(formatApartmentData);
|
|
|
|
|
|
|
|
|
|
|
|
// 返回结果
|
2026-04-22 06:48:32 +00:00
|
|
|
|
response.success(res, '获取成功', {
|
|
|
|
|
|
list: formattedApartments,
|
2026-04-20 06:43:09 +00:00
|
|
|
|
total: count,
|
|
|
|
|
|
page: parseInt(page),
|
|
|
|
|
|
pageSize: parseInt(pageSize)
|
|
|
|
|
|
});
|
|
|
|
|
|
} catch (error) {
|
2026-04-22 06:48:32 +00:00
|
|
|
|
response.serverError(res, '获取公寓列表失败', error);
|
2026-04-20 06:43:09 +00:00
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 获取单个公寓
|
|
|
|
|
|
const getApartmentById = async (req, res) => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const { id } = req.params;
|
|
|
|
|
|
const apartment = await Apartment.findOne({
|
|
|
|
|
|
where: { id, isDeleted: 0 }
|
|
|
|
|
|
});
|
|
|
|
|
|
if (!apartment) {
|
2026-04-22 06:48:32 +00:00
|
|
|
|
return response.notFound(res, '公寓不存在');
|
2026-04-20 06:43:09 +00:00
|
|
|
|
}
|
|
|
|
|
|
const formattedApartment = formatApartmentData(apartment);
|
2026-04-22 06:48:32 +00:00
|
|
|
|
response.success(res, '获取成功', formattedApartment);
|
2026-04-20 06:43:09 +00:00
|
|
|
|
} catch (error) {
|
2026-04-22 06:48:32 +00:00
|
|
|
|
response.serverError(res, '获取公寓详情失败', error);
|
2026-04-20 06:43:09 +00:00
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 创建公寓
|
|
|
|
|
|
const createApartment = async (req, res) => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const { name, address, description } = req.body;
|
|
|
|
|
|
|
|
|
|
|
|
// 检查租户资源使用情况(仅记录日志,不阻止创建)
|
|
|
|
|
|
const tenant = await Tenant.findByPk(req.user.tenantId);
|
2026-04-22 06:48:32 +00:00
|
|
|
|
let currentApartmentCount = 0;
|
2026-04-20 06:43:09 +00:00
|
|
|
|
if (tenant) {
|
|
|
|
|
|
// 获取当前公寓数量
|
2026-04-22 06:48:32 +00:00
|
|
|
|
currentApartmentCount = await Apartment.count({
|
2026-04-20 06:43:09 +00:00
|
|
|
|
where: { tenantId: req.user.tenantId, isDeleted: 0 }
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 检查是否超出限制
|
|
|
|
|
|
if (currentApartmentCount >= tenant.maxApartments) {
|
|
|
|
|
|
// 记录超额使用日志
|
|
|
|
|
|
await logOperation({
|
|
|
|
|
|
userId: req.user.id,
|
|
|
|
|
|
username: req.user.username,
|
|
|
|
|
|
tenantId: req.user.tenantId,
|
|
|
|
|
|
module: '公寓管理',
|
|
|
|
|
|
action: '超额创建',
|
|
|
|
|
|
description: `创建公寓"${name}",当前已使用 ${currentApartmentCount + 1}/${tenant.maxApartments} 栋(超出限制)`,
|
|
|
|
|
|
method: req.method,
|
|
|
|
|
|
path: req.path,
|
|
|
|
|
|
ip: req.ip,
|
|
|
|
|
|
status: 'success'
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const apartment = await Apartment.create({
|
|
|
|
|
|
name,
|
|
|
|
|
|
address,
|
|
|
|
|
|
description,
|
|
|
|
|
|
tenantId: req.user.tenantId,
|
|
|
|
|
|
createBy: req.user.id,
|
|
|
|
|
|
updateBy: req.user.id
|
|
|
|
|
|
});
|
2026-04-22 06:48:32 +00:00
|
|
|
|
response.created(res, '创建成功', {
|
|
|
|
|
|
apartment,
|
2026-04-20 06:43:09 +00:00
|
|
|
|
warning: tenant && currentApartmentCount >= tenant.maxApartments ? `当前已超出套餐限制(${tenant.maxApartments}栋),续费时将收取超额费用` : null
|
|
|
|
|
|
});
|
|
|
|
|
|
} catch (error) {
|
2026-04-22 06:48:32 +00:00
|
|
|
|
response.serverError(res, '创建公寓失败', error);
|
2026-04-20 06:43:09 +00:00
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 更新公寓
|
|
|
|
|
|
const updateApartment = async (req, res) => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const { id } = req.params;
|
|
|
|
|
|
const { name, address, description } = req.body;
|
|
|
|
|
|
const apartment = await Apartment.findOne({
|
|
|
|
|
|
where: { id, isDeleted: 0 }
|
|
|
|
|
|
});
|
|
|
|
|
|
if (!apartment) {
|
2026-04-22 06:48:32 +00:00
|
|
|
|
return response.notFound(res, '公寓不存在');
|
2026-04-20 06:43:09 +00:00
|
|
|
|
}
|
|
|
|
|
|
await apartment.update({
|
|
|
|
|
|
name,
|
|
|
|
|
|
address,
|
|
|
|
|
|
description,
|
|
|
|
|
|
updateBy: req.user.id
|
|
|
|
|
|
});
|
2026-04-22 06:48:32 +00:00
|
|
|
|
response.success(res, '更新成功', apartment);
|
2026-04-20 06:43:09 +00:00
|
|
|
|
} catch (error) {
|
2026-04-22 06:48:32 +00:00
|
|
|
|
response.serverError(res, '更新公寓失败', error);
|
2026-04-20 06:43:09 +00:00
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 删除公寓(软删除)
|
|
|
|
|
|
const deleteApartment = async (req, res) => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const { id } = req.params;
|
|
|
|
|
|
const apartment = await Apartment.findOne({
|
|
|
|
|
|
where: { id, isDeleted: 0 }
|
|
|
|
|
|
});
|
|
|
|
|
|
if (!apartment) {
|
2026-04-22 06:48:32 +00:00
|
|
|
|
return response.notFound(res, '公寓不存在');
|
2026-04-20 06:43:09 +00:00
|
|
|
|
}
|
|
|
|
|
|
await apartment.update({
|
|
|
|
|
|
isDeleted: 1,
|
|
|
|
|
|
updateBy: req.user.id
|
|
|
|
|
|
});
|
2026-04-22 06:48:32 +00:00
|
|
|
|
response.success(res, '公寓删除成功');
|
2026-04-20 06:43:09 +00:00
|
|
|
|
} catch (error) {
|
2026-04-22 06:48:32 +00:00
|
|
|
|
response.serverError(res, '删除公寓失败', error);
|
2026-04-20 06:43:09 +00:00
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 获取所有公寓(不分页)
|
|
|
|
|
|
const listApartments = async (req, res) => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const { regionId, name } = req.query;
|
|
|
|
|
|
|
|
|
|
|
|
// 构建查询条件
|
|
|
|
|
|
const where = { isDeleted: 0 };
|
|
|
|
|
|
if (regionId) {
|
|
|
|
|
|
where.regionId = regionId;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (name) {
|
|
|
|
|
|
where.name = {
|
|
|
|
|
|
[Op.like]: `%${name}%`
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 查询公寓数据
|
|
|
|
|
|
const apartments = await Apartment.findAll({
|
|
|
|
|
|
where
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 格式化数据
|
|
|
|
|
|
const formattedApartments = apartments.map(formatApartmentData);
|
|
|
|
|
|
|
|
|
|
|
|
// 返回结果
|
2026-04-22 06:48:32 +00:00
|
|
|
|
response.success(res, '获取成功', formattedApartments);
|
2026-04-20 06:43:09 +00:00
|
|
|
|
} catch (error) {
|
2026-04-22 06:48:32 +00:00
|
|
|
|
response.serverError(res, '获取公寓列表失败', error);
|
2026-04-20 06:43:09 +00:00
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
module.exports = {
|
|
|
|
|
|
getAllApartments,
|
|
|
|
|
|
listApartments,
|
|
|
|
|
|
getApartmentById,
|
|
|
|
|
|
createApartment,
|
|
|
|
|
|
updateApartment,
|
|
|
|
|
|
deleteApartment
|
2026-04-22 06:48:32 +00:00
|
|
|
|
};
|