const { SubscriptionPlan, PricingConfig, Order, Payment, Tenant, User, Apartment, Room, PaymentSetting } = require('../models'); const { Op } = require('sequelize'); const billingService = require('../services/billingService'); const response = require('../utils/response'); // 格式化日期时间(年月日时分秒) const formatDateTime = (date) => { if (!date) return null; const dateObj = date instanceof Date ? date : new Date(date); if (isNaN(dateObj.getTime())) return null; const beijingDate = new Date(dateObj.getTime() + 8 * 60 * 60 * 1000); return beijingDate.toISOString().replace('T', ' ').slice(0, 19); }; // 格式化订单数据 const formatOrderData = (order) => { const data = order.toJSON ? order.toJSON() : order; return { ...data, createTime: formatDateTime(data.createTime), updateTime: formatDateTime(data.updateTime), paidTime: formatDateTime(data.paidTime) }; }; // 获取所有套餐列表 const getAllPlans = async (req, res) => { try { const plans = await SubscriptionPlan.findAll({ where: { isDeleted: 0 }, order: [['sort', 'ASC']] }); response.success(res, '获取套餐列表成功', plans); } catch (error) { response.serverError(res, '获取套餐列表失败', error); } }; // 创建套餐 const createPlan = async (req, res) => { try { const { name, description, maxApartments, maxRooms, maxUsers, monthlyPrice } = req.body; // 验证必填字段 if (!name) { return response.badRequest(res, '套餐名称不能为空'); } // 检查是否是第一个套餐 const existingPlans = await SubscriptionPlan.count({ where: { isDeleted: 0 } }); const isDefault = existingPlans === 0; // 创建套餐 const plan = await SubscriptionPlan.create({ name, description, maxApartments: maxApartments || 10, maxRooms: maxRooms || 50, maxUsers: maxUsers || 5, monthlyPrice: monthlyPrice || 0, isDefault }); response.created(res, '套餐创建成功', plan); } catch (error) { response.serverError(res, '创建套餐失败', error); } }; // 更新套餐 const updatePlan = async (req, res) => { try { const { id } = req.params; const { name, description, maxApartments, maxRooms, maxUsers, monthlyPrice, status, sort } = req.body; // 查找套餐 const plan = await SubscriptionPlan.findOne({ where: { id, isDeleted: 0 } }); if (!plan) { return response.badRequest(res, '套餐不存在'); } // 更新套餐信息 await plan.update({ name: name !== undefined ? name : plan.name, description: description !== undefined ? description : plan.description, maxApartments: maxApartments !== undefined ? maxApartments : plan.maxApartments, maxRooms: maxRooms !== undefined ? maxRooms : plan.maxRooms, maxUsers: maxUsers !== undefined ? maxUsers : plan.maxUsers, monthlyPrice: monthlyPrice !== undefined ? monthlyPrice : plan.monthlyPrice, status: status !== undefined ? status : plan.status, sort: sort !== undefined ? sort : plan.sort }); response.success(res, '套餐更新成功', plan); } catch (error) { response.serverError(res, '更新套餐失败', error); } }; // 删除套餐(软删除) const deletePlan = async (req, res) => { try { const { id } = req.params; // 查找套餐 const plan = await SubscriptionPlan.findOne({ where: { id, isDeleted: 0 } }); if (!plan) { return response.badRequest(res, '套餐不存在'); } // 不能删除默认套餐 if (plan.isDefault) { return response.badRequest(res, '不能删除默认套餐'); } // 软删除 await plan.update({ isDeleted: 1 }); response.success(res, '套餐删除成功'); } catch (error) { response.serverError(res, '删除套餐失败', error); } }; // 设置默认套餐 const setDefaultPlan = async (req, res) => { try { const { id } = req.params; // 查找套餐 const plan = await SubscriptionPlan.findOne({ where: { id, isDeleted: 0 } }); if (!plan) { return response.badRequest(res, '套餐不存在'); } // 将其他套餐 isDefault 设为 false await SubscriptionPlan.update( { isDefault: false }, { where: { isDeleted: 0 } } ); // 将指定套餐 isDefault 设为 true await plan.update({ isDefault: true }); response.success(res, '设置默认套餐成功', plan); } catch (error) { response.serverError(res, '设置默认套餐失败', error); } }; // 获取当前价格配置 const getPricingConfig = async (req, res) => { try { // 查询 isActive=true 且最新的记录 const config = await PricingConfig.findOne({ where: { isActive: true, isDeleted: 0 }, order: [['createTime', 'DESC']] }); // 如果没有则返回默认配置 if (!config) { return response.success(res, '获取价格配置成功', { overageApartmentPrice: 10.00, overageRoomPrice: 2.00, overageUserPrice: 5.00, currency: 'CNY' }); } response.success(res, '获取价格配置成功', config); } catch (error) { response.serverError(res, '获取价格配置失败', error); } }; // 更新价格配置 const updatePricingConfig = async (req, res) => { try { const { overageApartmentPrice, overageRoomPrice, overageUserPrice } = req.body; // 将旧配置 isActive 设为 false await PricingConfig.update( { isActive: false }, { where: { isActive: true } } ); // 创建新配置记录 const config = await PricingConfig.create({ overageApartmentPrice: overageApartmentPrice || 10.00, overageRoomPrice: overageRoomPrice || 2.00, overageUserPrice: overageUserPrice || 5.00, isActive: true }); response.success(res, '价格配置更新成功', config); } catch (error) { response.serverError(res, '更新价格配置失败', error); } }; // 初始化默认套餐数据 const initDefaultPlans = async (req, res) => { try { // 检查是否已有套餐 const existingCount = await SubscriptionPlan.count({ where: { isDeleted: 0 } }); if (existingCount > 0) { return response.badRequest(res, '套餐数据已存在,无需初始化'); } // 默认套餐数据 const defaultPlans = [ { name: '免费版', description: '适合个人用户试用', maxApartments: 2, maxRooms: 10, maxUsers: 2, monthlyPrice: 0, isDefault: true, sort: 1 }, { name: '基础版', description: '适合小型公寓管理', maxApartments: 5, maxRooms: 50, maxUsers: 5, monthlyPrice: 99, isDefault: false, sort: 2 }, { name: '专业版', description: '适合中型公寓管理', maxApartments: 20, maxRooms: 200, maxUsers: 20, monthlyPrice: 299, isDefault: false, sort: 3 }, { name: '旗舰版', description: '适合大型公寓管理', maxApartments: 100, maxRooms: 1000, maxUsers: 100, monthlyPrice: 999, isDefault: false, sort: 4 } ]; // 批量创建套餐 const plans = await SubscriptionPlan.bulkCreate(defaultPlans); response.success(res, '默认套餐初始化成功', plans); } catch (error) { response.serverError(res, '初始化默认套餐失败', error); } }; // 初始化默认价格配置 const initDefaultPricing = async (req, res) => { try { // 检查是否已有活跃配置 const existingConfig = await PricingConfig.findOne({ where: { isActive: true, isDeleted: 0 } }); if (existingConfig) { return response.badRequest(res, '价格配置已存在,无需初始化'); } // 创建默认价格配置 const config = await PricingConfig.create({ overageApartmentPrice: 10.00, overageRoomPrice: 2.00, overageUserPrice: 5.00, isActive: true }); response.success(res, '默认价格配置初始化成功', config); } catch (error) { response.serverError(res, '初始化默认价格配置失败', error); } }; // 获取订单列表 const getOrders = async (req, res) => { try { const { page = 1, pageSize = 10, tenantId, status } = req.query; const offset = (page - 1) * pageSize; const isSystemAdmin = req.user.userType === 'super_admin'; const currentTenantId = req.tenantId; const where = { isDeleted: 0 }; // 权限检查:普通租户只能查看自己的订单 if (!isSystemAdmin) { where.tenantId = currentTenantId; } else if (tenantId) { // 系统管理员可以查看指定租户的订单 where.tenantId = tenantId; } if (status) { where.status = status; } const { count, rows } = await Order.findAndCountAll({ where, limit: parseInt(pageSize), offset: parseInt(offset), order: [['createTime', 'DESC']], include: [ { model: Tenant, as: 'tenant', attributes: ['id', 'code', 'contactName'] }, { model: SubscriptionPlan, as: 'subscriptionPlan', attributes: ['id', 'name', 'monthlyPrice'] } ] }); response.success(res, '获取订单列表成功', { list: rows.map(formatOrderData), total: count, page: parseInt(page), pageSize: parseInt(pageSize) }); } catch (error) { console.error('获取订单列表失败:', error); response.serverError(res, '获取订单列表失败', error); } }; // 计算续费价格(含超额费用) const calculatePrice = async (req, res) => { try { const { planId, months } = req.body; const tenantId = req.tenantId; // 验证必填字段 if (!planId || !months) { return response.badRequest(res, '套餐ID和购买月数不能为空'); } if (months < 1 || months > 36) { return response.badRequest(res, '购买月数必须在1-36之间'); } // 获取套餐信息 const plan = await SubscriptionPlan.findOne({ where: { id: planId, isDeleted: 0 } }); if (!plan) { return response.badRequest(res, '套餐不存在'); } // 计算订单金额(含超额费用) const amountInfo = await billingService.calculateRenewalAmount(tenantId, planId, months); response.success(res, '计算成功', amountInfo); } catch (error) { console.error('计算价格失败:', error); response.serverError(res, '计算价格失败', error); } }; // 创建续费订单 const createOrder = async (req, res) => { try { const { planId, months } = req.body; const tenantId = req.tenantId; // 验证必填字段 if (!planId || !months) { return response.badRequest(res, '套餐ID和购买月数不能为空'); } if (months < 1 || months > 36) { return response.badRequest(res, '购买月数必须在1-36之间'); } // 获取套餐信息 const plan = await SubscriptionPlan.findOne({ where: { id: planId, isDeleted: 0 } }); if (!plan) { return response.badRequest(res, '套餐不存在'); } // 计算订单金额 const amountInfo = await billingService.calculateRenewalAmount(tenantId, planId, months); // 生成订单编号 const orderNo = `ORD${Date.now()}${Math.floor(Math.random() * 1000)}`; // 计算过期时间(30分钟后过期) const expireTime = new Date(); expireTime.setMinutes(expireTime.getMinutes() + 30); // 创建订单 const order = await Order.create({ orderNo, tenantId, planId, planName: plan.name, months, amount: amountInfo.totalAmount, discountAmount: 0, actualAmount: amountInfo.totalAmount, status: 'pending', expireTime, createBy: req.user.id, updateBy: req.user.id }); // 重新查询订单以确保数据完整,包含关联数据 const createdOrder = await Order.findByPk(order.id, { include: [ { model: Tenant, as: 'tenant', attributes: ['id', 'code', 'contactName'] }, { model: SubscriptionPlan, as: 'subscriptionPlan', attributes: ['id', 'name', 'monthlyPrice'] } ] }); response.created(res, '订单创建成功', { order: formatOrderData(createdOrder), details: amountInfo.details }); } catch (error) { console.error('创建订单失败:', error); response.serverError(res, '创建订单失败', error); } }; // 获取订单详情 const getOrderDetail = async (req, res) => { try { const { id } = req.params; const isSystemAdmin = req.user.userType === 'super_admin'; const currentTenantId = req.tenantId; const order = await Order.findOne({ where: { id, isDeleted: 0 }, include: [ { model: Tenant, as: 'tenant', attributes: ['id', 'code', 'contactName'] }, { model: SubscriptionPlan, as: 'subscriptionPlan', attributes: ['id', 'name', 'description', 'monthlyPrice', 'maxApartments', 'maxRooms', 'maxUsers'] }, { model: Payment, as: 'payments', attributes: ['id', 'amount', 'paymentMethod', 'status', 'transactionId', 'paidAt', 'createTime'] } ] }); if (!order) { return response.notFound(res, '订单不存在'); } // 权限检查:普通租户只能查看自己的订单 if (!isSystemAdmin && order.tenantId !== currentTenantId) { return response.forbidden(res, '无权查看此订单'); } response.success(res, '获取订单详情成功', formatOrderData(order)); } catch (error) { console.error('获取订单详情失败:', error); response.serverError(res, '获取订单详情失败', error); } }; // 取消订单 const cancelOrder = async (req, res) => { try { const { id } = req.params; const isSystemAdmin = req.user.userType === 'super_admin'; const currentTenantId = req.tenantId; const order = await Order.findOne({ where: { id, isDeleted: 0 } }); if (!order) { return response.notFound(res, '订单不存在'); } // 权限检查:普通租户只能取消自己的订单 if (!isSystemAdmin && order.tenantId !== currentTenantId) { return response.forbidden(res, '无权取消此订单'); } // 只能取消待支付的订单 if (order.status !== 'pending') { return response.badRequest(res, '只能取消待支付的订单'); } await order.update({ status: 'cancelled' }); response.success(res, '订单取消成功'); } catch (error) { console.error('取消订单失败:', error); response.serverError(res, '取消订单失败', error); } }; // 支付订单(管理员确认收款) const payOrder = async (req, res) => { try { const { id: orderId } = req.params; const { paymentMethod = 'other', amount, transactionId: customTransactionId, remark } = req.body || {}; const isSystemAdmin = req.user.userType === 'super_admin'; if (!orderId) { return response.badRequest(res, '订单ID不能为空'); } // 只有管理员可以确认支付 if (!isSystemAdmin) { return response.forbidden(res, '无权操作,请联系管理员'); } // 查询订单 const order = await Order.findOne({ where: { id: orderId, isDeleted: 0 } }); if (!order) { return response.notFound(res, '订单不存在'); } // 验证订单状态 if (order.status !== 'pending') { return response.badRequest(res, '订单状态异常,无法支付'); } // 验证金额 const paymentAmount = parseFloat(amount) || order.actualAmount; if (paymentAmount <= 0) { return response.badRequest(res, '支付金额必须大于0'); } // 生成或使用自定义交易流水号 const transactionId = customTransactionId || `PAY${Date.now()}${Math.floor(Math.random() * 1000)}`; // 创建支付记录 const payment = await Payment.create({ orderId, tenantId: order.tenantId, amount: paymentAmount, paymentMethod: ['alipay', 'wechat', 'bank', 'other'].includes(paymentMethod) ? paymentMethod : 'other', status: 'success', transactionId, paidAt: new Date(), remark: remark || '管理员确认收款' }); // 更新订单状态 const paidTime = new Date(); await order.update({ status: 'paid', paidTime: paidTime }); // 处理支付成功后的租户状态更新 // 使用订单的months字段和planId更新资源配置 const paymentResult = await billingService.processPaymentSuccess(order.tenantId, order.months, order.planId); response.success(res, '支付成功', { payment, order: { ...formatOrderData(order), status: 'paid', paidTime: formatDateTime(paidTime) }, tenant: paymentResult }); } catch (error) { console.error('支付订单失败:', error); response.serverError(res, '支付订单失败', error); } }; // 获取支付记录 const getPayments = async (req, res) => { try { const { page = 1, pageSize = 10, tenantId } = req.query; const offset = (page - 1) * pageSize; const isSystemAdmin = req.user.userType === 'super_admin'; const currentTenantId = req.tenantId; const where = { isDeleted: 0 }; // 权限检查:普通租户只能查看自己的支付记录 if (!isSystemAdmin) { where.tenantId = currentTenantId; } else if (tenantId) { // 系统管理员可以查看指定租户的支付记录 where.tenantId = tenantId; } const { count, rows } = await Payment.findAndCountAll({ where, limit: parseInt(pageSize), offset: parseInt(offset), order: [['createTime', 'DESC']], include: [ { model: Tenant, as: 'tenant', attributes: ['id', 'code', 'contactName'] }, { model: Order, as: 'order', attributes: ['id', 'orderNo', 'planId', 'planName', 'months', 'amount', 'discountAmount', 'actualAmount'] } ] }); response.success(res, '获取支付记录成功', { list: rows.map(r => ({ ...(r.toJSON ? r.toJSON() : r), createTime: formatDateTime(r.createTime), paidAt: formatDateTime(r.paidAt) })), total: count, page: parseInt(page), pageSize: parseInt(pageSize) }); } catch (error) { console.error('获取支付记录失败:', error); response.serverError(res, '获取支付记录失败', error); } }; // 获取当前租户计费信息 const getBillingInfo = async (req, res) => { try { const tenantId = req.tenantId; const tenant = await Tenant.findOne({ where: { id: tenantId, isDeleted: 0 }, include: [ { model: SubscriptionPlan, as: 'subscriptionPlan', attributes: ['id', 'name', 'description', 'monthlyPrice', 'maxApartments', 'maxRooms', 'maxUsers'] } ] }); if (!tenant) { return response.notFound(res, '租户不存在'); } // 获取当前使用量 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 } }) ]); response.success(res, '获取计费信息成功', { tenant: { id: tenant.id, name: tenant.name, code: tenant.code, billingStatus: tenant.billingStatus, trialStartDate: tenant.trialStartDate, trialEndDate: tenant.trialEndDate, paidStartDate: tenant.paidStartDate, paidEndDate: tenant.paidEndDate, currentPeriodStart: tenant.currentPeriodStart, currentPeriodEnd: tenant.currentPeriodEnd }, plan: tenant.subscriptionPlan, usage: { apartments: apartmentCount, rooms: roomCount, users: userCount } }); } catch (error) { console.error('获取计费信息失败:', error); response.serverError(res, '获取计费信息失败', error); } }; // 获取资源使用情况 const getUsageStats = async (req, res) => { try { const tenantId = req.tenantId; const usageStats = await billingService.calculateOverage(tenantId); response.success(res, '获取资源使用情况成功', usageStats); } catch (error) { console.error('获取资源使用情况失败:', error); response.serverError(res, '获取资源使用情况失败', error); } }; // 获取计费统计(仅系统管理员) const getBillingStats = async (req, res) => { try { const isSystemAdmin = req.user.userType === 'super_admin'; if (!isSystemAdmin) { return response.forbidden(res, '无权访问此接口'); } // 各计费状态租户数量 const billingStatusStats = await Tenant.findAll({ where: { isDeleted: 0 }, attributes: ['billingStatus', [require('sequelize').fn('COUNT', require('sequelize').col('id')), 'count']], group: ['billingStatus'] }); // 总收入 const totalRevenue = await Payment.sum('amount', { where: { status: 'success', isDeleted: 0 } }); // 本月收入 const now = new Date(); const monthStart = new Date(now.getFullYear(), now.getMonth(), 1); const monthRevenue = await Payment.sum('amount', { where: { status: 'success', isDeleted: 0, paidAt: { [Op.gte]: monthStart } } }); // 即将到期租户数(7天内) const reminderDate = new Date(); reminderDate.setDate(reminderDate.getDate() + 7); const upcomingExpiredCount = await Tenant.count({ where: { isDeleted: 0, [Op.or]: [ { billingStatus: 'trial_active', trialEndDate: { [Op.lte]: reminderDate, [Op.gte]: now } }, { billingStatus: 'paid_active', paidEndDate: { [Op.lte]: reminderDate, [Op.gte]: now } } ] } }); // 已过期租户数 const expiredCount = await Tenant.count({ where: { isDeleted: 0, billingStatus: { [Op.in]: ['trial_expired', 'paid_expired'] } } }); // 待处理订单数 const pendingOrdersCount = await Order.count({ where: { status: 'pending', isDeleted: 0 } }); response.success(res, '获取计费统计成功', { billingStatusStats: billingStatusStats.reduce((acc, item) => { acc[item.billingStatus] = parseInt(item.get('count')); return acc; }, {}), totalRevenue: totalRevenue || 0, monthRevenue: monthRevenue || 0, upcomingExpiredCount, expiredCount, pendingOrdersCount }); } catch (error) { console.error('获取计费统计失败:', error); response.serverError(res, '获取计费统计失败', error); } }; // 获取支付设置 const getPaymentSettings = async (req, res) => { try { // 查询第一条记录(系统中只有一条支付设置记录) let settings = await PaymentSetting.findOne({ where: { isDeleted: 0 } }); // 如果没有记录,返回空对象 if (!settings) { return response.success(res, '获取支付设置成功', {}); } response.success(res, '获取支付设置成功', settings); } catch (error) { console.error('获取支付设置失败:', error); response.serverError(res, '获取支付设置失败', error); } }; // 更新支付设置 const updatePaymentSettings = async (req, res) => { try { const isSystemAdmin = req.user.userType === 'super_admin'; // 只有超级管理员可以修改支付设置 if (!isSystemAdmin) { return response.forbidden(res, '无权操作,请联系管理员'); } const { alipayAccount, alipayName, wechatId, wechatText, bankName, bankAccount, bankHolder, servicePhone, serviceTime } = req.body; // 查询是否存在记录 let settings = await PaymentSetting.findOne({ where: { isDeleted: 0 } }); if (settings) { // 更新现有记录 await settings.update({ alipayAccount: alipayAccount !== undefined ? alipayAccount : settings.alipayAccount, alipayName: alipayName !== undefined ? alipayName : settings.alipayName, wechatId: wechatId !== undefined ? wechatId : settings.wechatId, wechatText: wechatText !== undefined ? wechatText : settings.wechatText, bankName: bankName !== undefined ? bankName : settings.bankName, bankAccount: bankAccount !== undefined ? bankAccount : settings.bankAccount, bankHolder: bankHolder !== undefined ? bankHolder : settings.bankHolder, servicePhone: servicePhone !== undefined ? servicePhone : settings.servicePhone, serviceTime: serviceTime !== undefined ? serviceTime : settings.serviceTime }); } else { // 创建新记录 settings = await PaymentSetting.create({ alipayAccount, alipayName, wechatId, wechatText, bankName, bankAccount, bankHolder, servicePhone, serviceTime }); } response.success(res, '支付设置更新成功', settings); } catch (error) { console.error('更新支付设置失败:', error); response.serverError(res, '更新支付设置失败', error); } }; module.exports = { getAllPlans, createPlan, updatePlan, deletePlan, setDefaultPlan, getPricingConfig, updatePricingConfig, initDefaultPlans, initDefaultPricing, getOrders, createOrder, calculatePrice, getOrderDetail, cancelOrder, payOrder, getPayments, getBillingInfo, getUsageStats, getBillingStats, getPaymentSettings, updatePaymentSettings };