2026-04-20 06:43:09 +00:00
|
|
|
const { Renter, Rental, Room, Apartment } = require('../models');
|
|
|
|
|
const { Op } = require('sequelize');
|
2026-04-22 06:48:32 +00:00
|
|
|
const response = require('../utils/response');
|
2026-04-20 06:43:09 +00:00
|
|
|
|
|
|
|
|
// 获取租客列表(分页)
|
|
|
|
|
const getRenters = async (req, res) => {
|
|
|
|
|
try {
|
|
|
|
|
const { page = 1, pageSize = 10, keyword, status } = req.query;
|
|
|
|
|
const tenantId = req.tenantId;
|
|
|
|
|
|
|
|
|
|
const where = { tenantId, isDeleted: 0 };
|
|
|
|
|
|
|
|
|
|
if (keyword) {
|
|
|
|
|
where[Op.or] = [
|
|
|
|
|
{ name: { [Op.like]: `%${keyword}%` } },
|
|
|
|
|
{ phone: { [Op.like]: `%${keyword}%` } },
|
|
|
|
|
{ idCard: { [Op.like]: `%${keyword}%` } }
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (status) {
|
|
|
|
|
where.status = status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const { count, rows } = await Renter.findAndCountAll({
|
|
|
|
|
where,
|
|
|
|
|
order: [['createTime', 'DESC']],
|
|
|
|
|
offset: (page - 1) * pageSize,
|
|
|
|
|
limit: parseInt(pageSize)
|
|
|
|
|
});
|
|
|
|
|
|
2026-04-22 06:48:32 +00:00
|
|
|
response.success(res, '获取成功', {
|
|
|
|
|
list: rows,
|
|
|
|
|
total: count,
|
|
|
|
|
page: parseInt(page),
|
|
|
|
|
pageSize: parseInt(pageSize)
|
2026-04-20 06:43:09 +00:00
|
|
|
});
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('获取租客列表失败:', error);
|
2026-04-22 06:48:32 +00:00
|
|
|
response.serverError(res, '获取租客列表失败', error);
|
2026-04-20 06:43:09 +00:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 获取租客列表(不分页)
|
|
|
|
|
const getRenterList = async (req, res) => {
|
|
|
|
|
try {
|
|
|
|
|
const { keyword, status } = req.query;
|
|
|
|
|
const tenantId = req.tenantId;
|
|
|
|
|
|
|
|
|
|
const where = { tenantId, isDeleted: 0 };
|
|
|
|
|
|
|
|
|
|
if (keyword) {
|
|
|
|
|
where[Op.or] = [
|
|
|
|
|
{ name: { [Op.like]: `%${keyword}%` } },
|
|
|
|
|
{ phone: { [Op.like]: `%${keyword}%` } },
|
|
|
|
|
{ idCard: { [Op.like]: `%${keyword}%` } }
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (status) {
|
|
|
|
|
where.status = status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const rows = await Renter.findAll({
|
|
|
|
|
where,
|
|
|
|
|
order: [['createTime', 'DESC']]
|
|
|
|
|
});
|
|
|
|
|
|
2026-04-22 06:48:32 +00:00
|
|
|
response.success(res, '获取成功', rows);
|
2026-04-20 06:43:09 +00:00
|
|
|
} catch (error) {
|
|
|
|
|
console.error('获取租客列表失败:', error);
|
2026-04-22 06:48:32 +00:00
|
|
|
response.serverError(res, '获取租客列表失败', error);
|
2026-04-20 06:43:09 +00:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 获取租客详情
|
|
|
|
|
const getRenterById = async (req, res) => {
|
|
|
|
|
try {
|
|
|
|
|
const { id } = req.params;
|
|
|
|
|
const tenantId = req.tenantId;
|
|
|
|
|
|
|
|
|
|
const renter = await Renter.findOne({
|
|
|
|
|
where: { id, tenantId, isDeleted: 0 }
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (!renter) {
|
2026-04-22 06:48:32 +00:00
|
|
|
return response.notFound(res, '租客不存在');
|
2026-04-20 06:43:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 获取租客的租赁记录
|
|
|
|
|
const rentals = await Rental.findAll({
|
|
|
|
|
where: { renterId: id, tenantId },
|
|
|
|
|
include: [
|
|
|
|
|
{
|
|
|
|
|
model: Room,
|
|
|
|
|
attributes: ['id', 'roomNumber'],
|
|
|
|
|
include: [
|
|
|
|
|
{
|
|
|
|
|
model: Apartment,
|
|
|
|
|
attributes: ['id', 'name']
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
order: [['createTime', 'DESC']]
|
|
|
|
|
});
|
|
|
|
|
|
2026-04-22 06:48:32 +00:00
|
|
|
response.success(res, '获取成功', {
|
|
|
|
|
...renter.toJSON(),
|
|
|
|
|
rentals
|
2026-04-20 06:43:09 +00:00
|
|
|
});
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('获取租客详情失败:', error);
|
2026-04-22 06:48:32 +00:00
|
|
|
response.serverError(res, '获取租客详情失败', error);
|
2026-04-20 06:43:09 +00:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 创建租客
|
|
|
|
|
const createRenter = async (req, res) => {
|
|
|
|
|
try {
|
|
|
|
|
const tenantId = req.tenantId;
|
|
|
|
|
const createBy = req.user.id;
|
|
|
|
|
|
|
|
|
|
const {
|
|
|
|
|
name,
|
|
|
|
|
phone,
|
|
|
|
|
email,
|
|
|
|
|
idCard,
|
|
|
|
|
gender,
|
|
|
|
|
birthday,
|
|
|
|
|
emergencyContact,
|
|
|
|
|
emergencyPhone,
|
|
|
|
|
address,
|
|
|
|
|
workUnit,
|
|
|
|
|
remark
|
|
|
|
|
} = req.body;
|
|
|
|
|
|
|
|
|
|
// 验证必填字段
|
|
|
|
|
if (!name) {
|
2026-04-22 06:48:32 +00:00
|
|
|
return response.badRequest(res, '租客姓名不能为空');
|
2026-04-20 06:43:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 检查手机号是否已存在
|
|
|
|
|
if (phone) {
|
|
|
|
|
const existingRenter = await Renter.findOne({
|
|
|
|
|
where: { phone, tenantId, isDeleted: 0 }
|
|
|
|
|
});
|
|
|
|
|
if (existingRenter) {
|
2026-04-22 06:48:32 +00:00
|
|
|
return response.badRequest(res, '该手机号已存在');
|
2026-04-20 06:43:09 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const renter = await Renter.create({
|
|
|
|
|
name,
|
|
|
|
|
phone,
|
|
|
|
|
email,
|
|
|
|
|
idCard,
|
|
|
|
|
gender,
|
|
|
|
|
birthday,
|
|
|
|
|
emergencyContact,
|
|
|
|
|
emergencyPhone,
|
|
|
|
|
address,
|
|
|
|
|
workUnit,
|
|
|
|
|
remark,
|
|
|
|
|
tenantId,
|
|
|
|
|
createBy,
|
|
|
|
|
status: 'active'
|
|
|
|
|
});
|
|
|
|
|
|
2026-04-22 06:48:32 +00:00
|
|
|
response.created(res, '创建成功', renter);
|
2026-04-20 06:43:09 +00:00
|
|
|
} catch (error) {
|
|
|
|
|
console.error('创建租客失败:', error);
|
2026-04-22 06:48:32 +00:00
|
|
|
response.serverError(res, '创建租客失败', error);
|
2026-04-20 06:43:09 +00:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 更新租客
|
|
|
|
|
const updateRenter = async (req, res) => {
|
|
|
|
|
try {
|
|
|
|
|
const { id } = req.params;
|
|
|
|
|
const tenantId = req.tenantId;
|
|
|
|
|
const updateBy = req.user.id;
|
|
|
|
|
|
|
|
|
|
const renter = await Renter.findOne({
|
|
|
|
|
where: { id, tenantId, isDeleted: 0 }
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (!renter) {
|
2026-04-22 06:48:32 +00:00
|
|
|
return response.notFound(res, '租客不存在');
|
2026-04-20 06:43:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const {
|
|
|
|
|
name,
|
|
|
|
|
phone,
|
|
|
|
|
email,
|
|
|
|
|
idCard,
|
|
|
|
|
gender,
|
|
|
|
|
birthday,
|
|
|
|
|
emergencyContact,
|
|
|
|
|
emergencyPhone,
|
|
|
|
|
address,
|
|
|
|
|
workUnit,
|
|
|
|
|
remark,
|
|
|
|
|
status
|
|
|
|
|
} = req.body;
|
|
|
|
|
|
|
|
|
|
// 检查手机号是否被其他租客使用
|
|
|
|
|
if (phone && phone !== renter.phone) {
|
|
|
|
|
const existingRenter = await Renter.findOne({
|
|
|
|
|
where: { phone, tenantId, isDeleted: 0, id: { [Op.ne]: id } }
|
|
|
|
|
});
|
|
|
|
|
if (existingRenter) {
|
2026-04-22 06:48:32 +00:00
|
|
|
return response.badRequest(res, '该手机号已被其他租客使用');
|
2026-04-20 06:43:09 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
await renter.update({
|
|
|
|
|
name,
|
|
|
|
|
phone,
|
|
|
|
|
email,
|
|
|
|
|
idCard,
|
|
|
|
|
gender,
|
|
|
|
|
birthday,
|
|
|
|
|
emergencyContact,
|
|
|
|
|
emergencyPhone,
|
|
|
|
|
address,
|
|
|
|
|
workUnit,
|
|
|
|
|
remark,
|
|
|
|
|
status,
|
|
|
|
|
updateBy
|
|
|
|
|
});
|
|
|
|
|
|
2026-04-22 06:48:32 +00:00
|
|
|
response.success(res, '更新成功', renter);
|
2026-04-20 06:43:09 +00:00
|
|
|
} catch (error) {
|
|
|
|
|
console.error('更新租客失败:', error);
|
2026-04-22 06:48:32 +00:00
|
|
|
response.serverError(res, '更新租客失败', error);
|
2026-04-20 06:43:09 +00:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 删除租客
|
|
|
|
|
const deleteRenter = async (req, res) => {
|
|
|
|
|
try {
|
|
|
|
|
const { id } = req.params;
|
|
|
|
|
const tenantId = req.tenantId;
|
|
|
|
|
|
|
|
|
|
const renter = await Renter.findOne({
|
|
|
|
|
where: { id, tenantId, isDeleted: 0 }
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (!renter) {
|
2026-04-22 06:48:32 +00:00
|
|
|
return response.notFound(res, '租客不存在');
|
2026-04-20 06:43:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 检查是否有进行中的租赁
|
|
|
|
|
const activeRentals = await Rental.count({
|
|
|
|
|
where: {
|
|
|
|
|
tenantName: renter.name,
|
|
|
|
|
tenantId,
|
|
|
|
|
status: 'active'
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (activeRentals > 0) {
|
2026-04-22 06:48:32 +00:00
|
|
|
return response.badRequest(res, '该租客有进行中的租赁,无法删除');
|
2026-04-20 06:43:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
await renter.update({ isDeleted: 1 });
|
|
|
|
|
|
2026-04-22 06:48:32 +00:00
|
|
|
response.success(res, '删除成功');
|
2026-04-20 06:43:09 +00:00
|
|
|
} catch (error) {
|
|
|
|
|
console.error('删除租客失败:', error);
|
2026-04-22 06:48:32 +00:00
|
|
|
response.serverError(res, '删除租客失败', error);
|
2026-04-20 06:43:09 +00:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 获取租客下拉列表(用于租赁表单)
|
|
|
|
|
const getRenterOptions = async (req, res) => {
|
|
|
|
|
try {
|
|
|
|
|
const tenantId = req.tenantId;
|
|
|
|
|
const { keyword } = req.query;
|
|
|
|
|
|
|
|
|
|
const where = { tenantId, isDeleted: 0, status: 'active' };
|
|
|
|
|
|
|
|
|
|
if (keyword) {
|
|
|
|
|
where[Op.or] = [
|
|
|
|
|
{ name: { [Op.like]: `%${keyword}%` } },
|
|
|
|
|
{ phone: { [Op.like]: `%${keyword}%` } }
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const renters = await Renter.findAll({
|
|
|
|
|
where,
|
|
|
|
|
attributes: ['id', 'name', 'phone'],
|
|
|
|
|
order: [['name', 'ASC']],
|
|
|
|
|
limit: 50
|
|
|
|
|
});
|
|
|
|
|
|
2026-04-22 06:48:32 +00:00
|
|
|
response.success(res, '获取成功', renters.map(r => ({
|
|
|
|
|
value: r.id,
|
|
|
|
|
label: `${r.name} ${r.phone ? '(' + r.phone + ')' : ''}`
|
|
|
|
|
})));
|
2026-04-20 06:43:09 +00:00
|
|
|
} catch (error) {
|
|
|
|
|
console.error('获取租客选项失败:', error);
|
2026-04-22 06:48:32 +00:00
|
|
|
response.serverError(res, '获取租客选项失败', error);
|
2026-04-20 06:43:09 +00:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
module.exports = {
|
|
|
|
|
getRenters,
|
|
|
|
|
getRenterList,
|
|
|
|
|
getRenterById,
|
|
|
|
|
createRenter,
|
|
|
|
|
updateRenter,
|
|
|
|
|
deleteRenter,
|
|
|
|
|
getRenterOptions
|
|
|
|
|
};
|