/** * 计费服务 - 处理租户到期检查、超额计算等 */ const { Tenant, SubscriptionPlan, Order, PricingConfig } = require('../models'); const { Op } = require('sequelize'); const { logOperation } = require('../utils/logger'); /** * 检查并更新租户到期状态 * 每天执行一次 */ const checkTenantExpiration = async () => { try { const now = new Date(); console.log(`[${now.toISOString()}] 开始检查租户到期状态...`); // 1. 检查试用期到期的租户 const trialExpiredTenants = await Tenant.findAll({ where: { billingStatus: 'trial_active', trialEndDate: { [Op.lt]: now } } }); for (const tenant of trialExpiredTenants) { await tenant.update({ billingStatus: 'trial_expired' }); console.log(`租户 ${tenant.name} (ID: ${tenant.id}) 试用期已过期`); // 记录日志 await logOperation({ tenantId: tenant.id, module: '计费管理', action: '试用期到期', description: `租户 ${tenant.name} 试用期已过期`, status: 'success' }); } // 2. 检查付费期到期的租户 const paidExpiredTenants = await Tenant.findAll({ where: { billingStatus: 'paid_active', paidEndDate: { [Op.lt]: now } } }); for (const tenant of paidExpiredTenants) { await tenant.update({ billingStatus: 'paid_expired' }); console.log(`租户 ${tenant.name} (ID: ${tenant.id}) 付费期已过期`); // 记录日志 await logOperation({ tenantId: tenant.id, module: '计费管理', action: '付费期到期', description: `租户 ${tenant.name} 付费期已过期`, status: 'success' }); } // 3. 检查数据保留期超过90天的租户(可选:发送提醒或清理) const retentionLimit = new Date(now.getTime() - 90 * 24 * 60 * 60 * 1000); const longExpiredTenants = await Tenant.findAll({ where: { billingStatus: { [Op.in]: ['trial_expired', 'paid_expired'] }, updateTime: { [Op.lt]: retentionLimit } } }); if (longExpiredTenants.length > 0) { console.log(`发现 ${longExpiredTenants.length} 个租户超过90天数据保留期`); // 这里可以添加数据清理逻辑或发送提醒 } console.log(`[${new Date().toISOString()}] 租户到期检查完成`); console.log(`- 试用期过期: ${trialExpiredTenants.length} 个`); console.log(`- 付费期过期: ${paidExpiredTenants.length} 个`); return { trialExpired: trialExpiredTenants.length, paidExpired: paidExpiredTenants.length, longExpired: longExpiredTenants.length }; } catch (error) { console.error('检查租户到期状态失败:', error); throw error; } }; /** * 获取即将到期的租户(用于提醒) * @param {number} days - 提前多少天提醒 */ const getUpcomingExpiredTenants = async (days = 7) => { try { const reminderDate = new Date(); reminderDate.setDate(reminderDate.getDate() + days); const upcomingTenants = await Tenant.findAll({ where: { [Op.or]: [ { billingStatus: 'trial_active', trialEndDate: { [Op.lte]: reminderDate, [Op.gte]: new Date() } }, { billingStatus: 'paid_active', paidEndDate: { [Op.lte]: reminderDate, [Op.gte]: new Date() } } ] }, include: [{ model: SubscriptionPlan, as: 'subscriptionPlan' }] }); return upcomingTenants; } catch (error) { console.error('获取即将到期租户失败:', error); throw error; } }; /** * 计算租户的超额使用量 * @param {number} tenantId - 租户ID */ const calculateOverage = async (tenantId) => { try { const tenant = await Tenant.findByPk(tenantId, { include: [{ model: SubscriptionPlan, as: 'subscriptionPlan' }] }); if (!tenant || !tenant.subscriptionPlan) { throw new Error('租户或套餐不存在'); } const plan = tenant.subscriptionPlan; // 获取当前使用量 const { Apartment, Room, User } = require('../models'); const [apartmentCount, roomCount, userCount] = await Promise.all([ Apartment.count({ where: { tenantId, isDeleted: 0 } }), Room.count({ where: { tenantId, isDeleted: 0 } }), User.count({ where: { tenantId, isDeleted: 0 } }) ]); // 计算超额 const overageApartments = Math.max(0, apartmentCount - plan.maxApartments); const overageRooms = Math.max(0, roomCount - plan.maxRooms); const overageUsers = Math.max(0, userCount - plan.maxUsers); return { usage: { apartments: apartmentCount, rooms: roomCount, users: userCount }, limits: { apartments: plan.maxApartments, rooms: plan.maxRooms, users: plan.maxUsers }, overage: { apartments: overageApartments, rooms: overageRooms, users: overageUsers } }; } catch (error) { console.error('计算超额使用量失败:', error); throw error; } }; /** * 计算续费订单金额 * @param {number} tenantId - 租户ID * @param {number} planId - 套餐ID * @param {number} months - 购买月数 */ const calculateRenewalAmount = async (tenantId, planId, months) => { try { // 获取套餐信息 const plan = await SubscriptionPlan.findByPk(planId); if (!plan) { throw new Error('套餐不存在'); } // 获取当前价格配置 const pricingConfig = await PricingConfig.findOne({ where: { isActive: true }, order: [['effectiveDate', 'DESC']] }); if (!pricingConfig) { throw new Error('价格配置不存在'); } // 计算超额使用费 const overage = await calculateOverage(tenantId); // 基础费用 = 套餐月费 × 月数 const baseAmount = plan.monthlyPrice * months; // 超额费用 = (超额公寓×单价 + 超额房间×单价 + 超额用户×单价) × 月数 const overageAmount = ( overage.overage.apartments * pricingConfig.overageApartmentPrice + overage.overage.rooms * pricingConfig.overageRoomPrice + overage.overage.users * pricingConfig.overageUserPrice ) * months; const totalAmount = baseAmount + overageAmount; return { baseAmount: parseFloat(baseAmount.toFixed(2)), overageAmount: parseFloat(overageAmount.toFixed(2)), totalAmount: parseFloat(totalAmount.toFixed(2)), details: { planName: plan.name, monthlyPrice: plan.monthlyPrice, months, overage } }; } catch (error) { console.error('计算续费金额失败:', error); throw error; } }; /** * 处理支付成功后的租户状态更新 * @param {number} tenantId - 租户ID * @param {number} months - 续费月数 * @param {number} planId - 套餐ID(可选,用于更新资源配置) */ const processPaymentSuccess = async (tenantId, months, planId = null) => { try { const tenant = await Tenant.findByPk(tenantId); if (!tenant) { throw new Error('租户不存在'); } const now = new Date(); let paidStartDate, paidEndDate, billingStatus; // 如果当前是付费期,则延长 if (tenant.billingStatus === 'paid_active' && tenant.paidEndDate && tenant.paidEndDate > now) { paidStartDate = tenant.paidStartDate; paidEndDate = new Date(tenant.paidEndDate.getTime() + months * 30 * 24 * 60 * 60 * 1000); billingStatus = 'paid_active'; } else { // 新付费期或从过期状态恢复 paidStartDate = now; paidEndDate = new Date(now.getTime() + months * 30 * 24 * 60 * 60 * 1000); billingStatus = 'paid_active'; } // 构建更新数据 const updateData = { billingStatus, paidStartDate, paidEndDate, currentPeriodStart: paidStartDate, currentPeriodEnd: paidEndDate }; // 如果有套餐ID,更新租户套餐和资源配置 if (planId) { const plan = await SubscriptionPlan.findByPk(planId); if (plan) { updateData.planId = planId; updateData.maxApartments = plan.maxApartments; updateData.maxRooms = plan.maxRooms; updateData.maxUsers = plan.maxUsers; } } await tenant.update(updateData); console.log(`租户 ${tenant.name} (ID: ${tenantId}) 续费成功,有效期至 ${paidEndDate.toISOString()}`); // 记录日志 await logOperation({ tenantId: tenant.id, module: '计费管理', action: '续费成功', description: `租户续费 ${months} 个月,有效期至 ${paidEndDate.toLocaleDateString()}`, status: 'success' }); return { billingStatus, paidStartDate, paidEndDate }; } catch (error) { console.error('处理支付成功失败:', error); throw error; } }; module.exports = { checkTenantExpiration, getUpcomingExpiredTenants, calculateOverage, calculateRenewalAmount, processPaymentSuccess };