1165 lines
28 KiB
JavaScript
1165 lines
28 KiB
JavaScript
|
|
const { SubscriptionPlan, PricingConfig, Order, Payment, Tenant, User, Apartment, Room, PaymentSetting } = require('../models');
|
|||
|
|
const { Op } = require('sequelize');
|
|||
|
|
const billingService = require('../services/billingService');
|
|||
|
|
|
|||
|
|
// 获取所有套餐列表
|
|||
|
|
const getAllPlans = async (req, res) => {
|
|||
|
|
try {
|
|||
|
|
const plans = await SubscriptionPlan.findAll({
|
|||
|
|
where: { isDeleted: 0 },
|
|||
|
|
order: [['sort', 'ASC']]
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
res.status(200).json({
|
|||
|
|
code: 200,
|
|||
|
|
message: '获取套餐列表成功',
|
|||
|
|
data: plans
|
|||
|
|
});
|
|||
|
|
} catch (error) {
|
|||
|
|
res.status(500).json({
|
|||
|
|
code: 500,
|
|||
|
|
message: '获取套餐列表失败',
|
|||
|
|
error: error.message
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 创建套餐
|
|||
|
|
const createPlan = async (req, res) => {
|
|||
|
|
try {
|
|||
|
|
const { name, description, maxApartments, maxRooms, maxUsers, monthlyPrice } = req.body;
|
|||
|
|
|
|||
|
|
// 验证必填字段
|
|||
|
|
if (!name) {
|
|||
|
|
return res.status(400).json({
|
|||
|
|
code: 400,
|
|||
|
|
message: '套餐名称不能为空'
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 检查是否是第一个套餐
|
|||
|
|
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
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
res.status(201).json({
|
|||
|
|
code: 200,
|
|||
|
|
message: '套餐创建成功',
|
|||
|
|
data: plan
|
|||
|
|
});
|
|||
|
|
} catch (error) {
|
|||
|
|
res.status(500).json({
|
|||
|
|
code: 500,
|
|||
|
|
message: '创建套餐失败',
|
|||
|
|
error: error.message
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 更新套餐
|
|||
|
|
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 res.status(400).json({
|
|||
|
|
code: 400,
|
|||
|
|
message: '套餐不存在'
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 更新套餐信息
|
|||
|
|
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
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
res.status(200).json({
|
|||
|
|
code: 200,
|
|||
|
|
message: '套餐更新成功',
|
|||
|
|
data: plan
|
|||
|
|
});
|
|||
|
|
} catch (error) {
|
|||
|
|
res.status(500).json({
|
|||
|
|
code: 500,
|
|||
|
|
message: '更新套餐失败',
|
|||
|
|
error: error.message
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 删除套餐(软删除)
|
|||
|
|
const deletePlan = async (req, res) => {
|
|||
|
|
try {
|
|||
|
|
const { id } = req.params;
|
|||
|
|
|
|||
|
|
// 查找套餐
|
|||
|
|
const plan = await SubscriptionPlan.findOne({
|
|||
|
|
where: { id, isDeleted: 0 }
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
if (!plan) {
|
|||
|
|
return res.status(400).json({
|
|||
|
|
code: 400,
|
|||
|
|
message: '套餐不存在'
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 不能删除默认套餐
|
|||
|
|
if (plan.isDefault) {
|
|||
|
|
return res.status(400).json({
|
|||
|
|
code: 400,
|
|||
|
|
message: '不能删除默认套餐'
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 软删除
|
|||
|
|
await plan.update({ isDeleted: 1 });
|
|||
|
|
|
|||
|
|
res.status(200).json({
|
|||
|
|
code: 200,
|
|||
|
|
message: '套餐删除成功'
|
|||
|
|
});
|
|||
|
|
} catch (error) {
|
|||
|
|
res.status(500).json({
|
|||
|
|
code: 500,
|
|||
|
|
message: '删除套餐失败',
|
|||
|
|
error: error.message
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 设置默认套餐
|
|||
|
|
const setDefaultPlan = async (req, res) => {
|
|||
|
|
try {
|
|||
|
|
const { id } = req.params;
|
|||
|
|
|
|||
|
|
// 查找套餐
|
|||
|
|
const plan = await SubscriptionPlan.findOne({
|
|||
|
|
where: { id, isDeleted: 0 }
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
if (!plan) {
|
|||
|
|
return res.status(400).json({
|
|||
|
|
code: 400,
|
|||
|
|
message: '套餐不存在'
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 将其他套餐 isDefault 设为 false
|
|||
|
|
await SubscriptionPlan.update(
|
|||
|
|
{ isDefault: false },
|
|||
|
|
{ where: { isDeleted: 0 } }
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
// 将指定套餐 isDefault 设为 true
|
|||
|
|
await plan.update({ isDefault: true });
|
|||
|
|
|
|||
|
|
res.status(200).json({
|
|||
|
|
code: 200,
|
|||
|
|
message: '设置默认套餐成功',
|
|||
|
|
data: plan
|
|||
|
|
});
|
|||
|
|
} catch (error) {
|
|||
|
|
res.status(500).json({
|
|||
|
|
code: 500,
|
|||
|
|
message: '设置默认套餐失败',
|
|||
|
|
error: error.message
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 获取当前价格配置
|
|||
|
|
const getPricingConfig = async (req, res) => {
|
|||
|
|
try {
|
|||
|
|
// 查询 isActive=true 且最新的记录
|
|||
|
|
const config = await PricingConfig.findOne({
|
|||
|
|
where: { isActive: true, isDeleted: 0 },
|
|||
|
|
order: [['createTime', 'DESC']]
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 如果没有则返回默认配置
|
|||
|
|
if (!config) {
|
|||
|
|
return res.status(200).json({
|
|||
|
|
code: 200,
|
|||
|
|
message: '获取价格配置成功',
|
|||
|
|
data: {
|
|||
|
|
overageApartmentPrice: 10.00,
|
|||
|
|
overageRoomPrice: 2.00,
|
|||
|
|
overageUserPrice: 5.00,
|
|||
|
|
currency: 'CNY'
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
res.status(200).json({
|
|||
|
|
code: 200,
|
|||
|
|
message: '获取价格配置成功',
|
|||
|
|
data: config
|
|||
|
|
});
|
|||
|
|
} catch (error) {
|
|||
|
|
res.status(500).json({
|
|||
|
|
code: 500,
|
|||
|
|
message: '获取价格配置失败',
|
|||
|
|
error: error.message
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 更新价格配置
|
|||
|
|
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
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
res.status(200).json({
|
|||
|
|
code: 200,
|
|||
|
|
message: '价格配置更新成功',
|
|||
|
|
data: config
|
|||
|
|
});
|
|||
|
|
} catch (error) {
|
|||
|
|
res.status(500).json({
|
|||
|
|
code: 500,
|
|||
|
|
message: '更新价格配置失败',
|
|||
|
|
error: error.message
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 初始化默认套餐数据
|
|||
|
|
const initDefaultPlans = async (req, res) => {
|
|||
|
|
try {
|
|||
|
|
// 检查是否已有套餐
|
|||
|
|
const existingCount = await SubscriptionPlan.count({ where: { isDeleted: 0 } });
|
|||
|
|
if (existingCount > 0) {
|
|||
|
|
return res.status(400).json({
|
|||
|
|
code: 400,
|
|||
|
|
message: '套餐数据已存在,无需初始化'
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 默认套餐数据
|
|||
|
|
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);
|
|||
|
|
|
|||
|
|
res.status(200).json({
|
|||
|
|
code: 200,
|
|||
|
|
message: '默认套餐初始化成功',
|
|||
|
|
data: plans
|
|||
|
|
});
|
|||
|
|
} catch (error) {
|
|||
|
|
res.status(500).json({
|
|||
|
|
code: 500,
|
|||
|
|
message: '初始化默认套餐失败',
|
|||
|
|
error: error.message
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 初始化默认价格配置
|
|||
|
|
const initDefaultPricing = async (req, res) => {
|
|||
|
|
try {
|
|||
|
|
// 检查是否已有活跃配置
|
|||
|
|
const existingConfig = await PricingConfig.findOne({
|
|||
|
|
where: { isActive: true, isDeleted: 0 }
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
if (existingConfig) {
|
|||
|
|
return res.status(400).json({
|
|||
|
|
code: 400,
|
|||
|
|
message: '价格配置已存在,无需初始化'
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 创建默认价格配置
|
|||
|
|
const config = await PricingConfig.create({
|
|||
|
|
overageApartmentPrice: 10.00,
|
|||
|
|
overageRoomPrice: 2.00,
|
|||
|
|
overageUserPrice: 5.00,
|
|||
|
|
isActive: true
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
res.status(200).json({
|
|||
|
|
code: 200,
|
|||
|
|
message: '默认价格配置初始化成功',
|
|||
|
|
data: config
|
|||
|
|
});
|
|||
|
|
} catch (error) {
|
|||
|
|
res.status(500).json({
|
|||
|
|
code: 500,
|
|||
|
|
message: '初始化默认价格配置失败',
|
|||
|
|
error: error.message
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 获取订单列表
|
|||
|
|
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']
|
|||
|
|
}
|
|||
|
|
]
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
res.json({
|
|||
|
|
code: 200,
|
|||
|
|
data: {
|
|||
|
|
list: rows,
|
|||
|
|
total: count,
|
|||
|
|
page: parseInt(page),
|
|||
|
|
pageSize: parseInt(pageSize)
|
|||
|
|
},
|
|||
|
|
message: '获取订单列表成功'
|
|||
|
|
});
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('获取订单列表失败:', error);
|
|||
|
|
res.status(500).json({
|
|||
|
|
code: 500,
|
|||
|
|
message: '获取订单列表失败',
|
|||
|
|
error: error.message
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 计算续费价格(含超额费用)
|
|||
|
|
const calculatePrice = async (req, res) => {
|
|||
|
|
try {
|
|||
|
|
const { planId, months } = req.body;
|
|||
|
|
const tenantId = req.tenantId;
|
|||
|
|
|
|||
|
|
// 验证必填字段
|
|||
|
|
if (!planId || !months) {
|
|||
|
|
return res.status(400).json({
|
|||
|
|
code: 400,
|
|||
|
|
message: '套餐ID和购买月数不能为空'
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (months < 1 || months > 36) {
|
|||
|
|
return res.status(400).json({
|
|||
|
|
code: 400,
|
|||
|
|
message: '购买月数必须在1-36之间'
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 获取套餐信息
|
|||
|
|
const plan = await SubscriptionPlan.findOne({
|
|||
|
|
where: { id: planId, isDeleted: 0 }
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
if (!plan) {
|
|||
|
|
return res.status(400).json({
|
|||
|
|
code: 400,
|
|||
|
|
message: '套餐不存在'
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 计算订单金额(含超额费用)
|
|||
|
|
const amountInfo = await billingService.calculateRenewalAmount(tenantId, planId, months);
|
|||
|
|
|
|||
|
|
res.json({
|
|||
|
|
code: 200,
|
|||
|
|
message: '计算成功',
|
|||
|
|
data: amountInfo
|
|||
|
|
});
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('计算价格失败:', error);
|
|||
|
|
res.status(500).json({
|
|||
|
|
code: 500,
|
|||
|
|
message: '计算价格失败',
|
|||
|
|
error: error.message
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 创建续费订单
|
|||
|
|
const createOrder = async (req, res) => {
|
|||
|
|
try {
|
|||
|
|
const { planId, months } = req.body;
|
|||
|
|
const tenantId = req.tenantId;
|
|||
|
|
|
|||
|
|
// 验证必填字段
|
|||
|
|
if (!planId || !months) {
|
|||
|
|
return res.status(400).json({
|
|||
|
|
code: 400,
|
|||
|
|
message: '套餐ID和购买月数不能为空'
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (months < 1 || months > 36) {
|
|||
|
|
return res.status(400).json({
|
|||
|
|
code: 400,
|
|||
|
|
message: '购买月数必须在1-36之间'
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 获取套餐信息
|
|||
|
|
const plan = await SubscriptionPlan.findOne({
|
|||
|
|
where: { id: planId, isDeleted: 0 }
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
if (!plan) {
|
|||
|
|
return res.status(400).json({
|
|||
|
|
code: 400,
|
|||
|
|
message: '套餐不存在'
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 计算订单金额
|
|||
|
|
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']
|
|||
|
|
}
|
|||
|
|
]
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
res.status(201).json({
|
|||
|
|
code: 200,
|
|||
|
|
message: '订单创建成功',
|
|||
|
|
data: {
|
|||
|
|
order: createdOrder.toJSON(),
|
|||
|
|
details: amountInfo.details
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('创建订单失败:', error);
|
|||
|
|
res.status(500).json({
|
|||
|
|
code: 500,
|
|||
|
|
message: '创建订单失败',
|
|||
|
|
error: error.message
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 获取订单详情
|
|||
|
|
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 res.status(404).json({
|
|||
|
|
code: 404,
|
|||
|
|
message: '订单不存在'
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 权限检查:普通租户只能查看自己的订单
|
|||
|
|
if (!isSystemAdmin && order.tenantId !== currentTenantId) {
|
|||
|
|
return res.status(403).json({
|
|||
|
|
code: 403,
|
|||
|
|
message: '无权查看此订单'
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
res.json({
|
|||
|
|
code: 200,
|
|||
|
|
data: order,
|
|||
|
|
message: '获取订单详情成功'
|
|||
|
|
});
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('获取订单详情失败:', error);
|
|||
|
|
res.status(500).json({
|
|||
|
|
code: 500,
|
|||
|
|
message: '获取订单详情失败',
|
|||
|
|
error: error.message
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 取消订单
|
|||
|
|
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 res.status(404).json({
|
|||
|
|
code: 404,
|
|||
|
|
message: '订单不存在'
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 权限检查:普通租户只能取消自己的订单
|
|||
|
|
if (!isSystemAdmin && order.tenantId !== currentTenantId) {
|
|||
|
|
return res.status(403).json({
|
|||
|
|
code: 403,
|
|||
|
|
message: '无权取消此订单'
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 只能取消待支付的订单
|
|||
|
|
if (order.status !== 'pending') {
|
|||
|
|
return res.status(400).json({
|
|||
|
|
code: 400,
|
|||
|
|
message: '只能取消待支付的订单'
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
await order.update({ status: 'cancelled' });
|
|||
|
|
|
|||
|
|
res.json({
|
|||
|
|
code: 200,
|
|||
|
|
message: '订单取消成功'
|
|||
|
|
});
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('取消订单失败:', error);
|
|||
|
|
res.status(500).json({
|
|||
|
|
code: 500,
|
|||
|
|
message: '取消订单失败',
|
|||
|
|
error: error.message
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 支付订单(管理员确认收款)
|
|||
|
|
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';
|
|||
|
|
const currentTenantId = req.tenantId;
|
|||
|
|
|
|||
|
|
if (!orderId) {
|
|||
|
|
return res.status(400).json({
|
|||
|
|
code: 400,
|
|||
|
|
message: '订单ID不能为空'
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 只有管理员可以确认支付
|
|||
|
|
if (!isSystemAdmin) {
|
|||
|
|
return res.status(403).json({
|
|||
|
|
code: 403,
|
|||
|
|
message: '无权操作,请联系管理员'
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 查询订单
|
|||
|
|
const order = await Order.findOne({
|
|||
|
|
where: { id: orderId, isDeleted: 0 }
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
if (!order) {
|
|||
|
|
return res.status(404).json({
|
|||
|
|
code: 404,
|
|||
|
|
message: '订单不存在'
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 验证订单状态
|
|||
|
|
if (order.status !== 'pending') {
|
|||
|
|
return res.status(400).json({
|
|||
|
|
code: 400,
|
|||
|
|
message: '订单状态异常,无法支付'
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 验证金额
|
|||
|
|
const paymentAmount = parseFloat(amount) || order.actualAmount;
|
|||
|
|
if (paymentAmount <= 0) {
|
|||
|
|
return res.status(400).json({
|
|||
|
|
code: 400,
|
|||
|
|
message: '支付金额必须大于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);
|
|||
|
|
|
|||
|
|
res.json({
|
|||
|
|
code: 200,
|
|||
|
|
message: '支付成功',
|
|||
|
|
data: {
|
|||
|
|
payment,
|
|||
|
|
order: {
|
|||
|
|
...order.toJSON(),
|
|||
|
|
status: 'paid',
|
|||
|
|
paidTime: paidTime
|
|||
|
|
},
|
|||
|
|
tenant: paymentResult
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('支付订单失败:', error);
|
|||
|
|
res.status(500).json({
|
|||
|
|
code: 500,
|
|||
|
|
message: '支付订单失败',
|
|||
|
|
error: error.message
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 获取支付记录
|
|||
|
|
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']
|
|||
|
|
}
|
|||
|
|
]
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
res.json({
|
|||
|
|
code: 200,
|
|||
|
|
data: {
|
|||
|
|
list: rows,
|
|||
|
|
total: count,
|
|||
|
|
page: parseInt(page),
|
|||
|
|
pageSize: parseInt(pageSize)
|
|||
|
|
},
|
|||
|
|
message: '获取支付记录成功'
|
|||
|
|
});
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('获取支付记录失败:', error);
|
|||
|
|
res.status(500).json({
|
|||
|
|
code: 500,
|
|||
|
|
message: '获取支付记录失败',
|
|||
|
|
error: error.message
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 获取当前租户计费信息
|
|||
|
|
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 res.status(404).json({
|
|||
|
|
code: 404,
|
|||
|
|
message: '租户不存在'
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 获取当前使用量
|
|||
|
|
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 } })
|
|||
|
|
]);
|
|||
|
|
|
|||
|
|
res.json({
|
|||
|
|
code: 200,
|
|||
|
|
data: {
|
|||
|
|
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
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
message: '获取计费信息成功'
|
|||
|
|
});
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('获取计费信息失败:', error);
|
|||
|
|
res.status(500).json({
|
|||
|
|
code: 500,
|
|||
|
|
message: '获取计费信息失败',
|
|||
|
|
error: error.message
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 获取资源使用情况
|
|||
|
|
const getUsageStats = async (req, res) => {
|
|||
|
|
try {
|
|||
|
|
const tenantId = req.tenantId;
|
|||
|
|
|
|||
|
|
const usageStats = await billingService.calculateOverage(tenantId);
|
|||
|
|
|
|||
|
|
res.json({
|
|||
|
|
code: 200,
|
|||
|
|
data: usageStats,
|
|||
|
|
message: '获取资源使用情况成功'
|
|||
|
|
});
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('获取资源使用情况失败:', error);
|
|||
|
|
res.status(500).json({
|
|||
|
|
code: 500,
|
|||
|
|
message: '获取资源使用情况失败',
|
|||
|
|
error: error.message
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 获取计费统计(仅系统管理员)
|
|||
|
|
const getBillingStats = async (req, res) => {
|
|||
|
|
try {
|
|||
|
|
const isSystemAdmin = req.user.userType === 'super_admin';
|
|||
|
|
|
|||
|
|
if (!isSystemAdmin) {
|
|||
|
|
return res.status(403).json({
|
|||
|
|
code: 403,
|
|||
|
|
message: '无权访问此接口'
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 各计费状态租户数量
|
|||
|
|
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
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
res.json({
|
|||
|
|
code: 200,
|
|||
|
|
data: {
|
|||
|
|
billingStatusStats: billingStatusStats.reduce((acc, item) => {
|
|||
|
|
acc[item.billingStatus] = parseInt(item.get('count'));
|
|||
|
|
return acc;
|
|||
|
|
}, {}),
|
|||
|
|
totalRevenue: totalRevenue || 0,
|
|||
|
|
monthRevenue: monthRevenue || 0,
|
|||
|
|
upcomingExpiredCount,
|
|||
|
|
expiredCount,
|
|||
|
|
pendingOrdersCount
|
|||
|
|
},
|
|||
|
|
message: '获取计费统计成功'
|
|||
|
|
});
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('获取计费统计失败:', error);
|
|||
|
|
res.status(500).json({
|
|||
|
|
code: 500,
|
|||
|
|
message: '获取计费统计失败',
|
|||
|
|
error: error.message
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 获取支付设置
|
|||
|
|
const getPaymentSettings = async (req, res) => {
|
|||
|
|
try {
|
|||
|
|
// 查询第一条记录(系统中只有一条支付设置记录)
|
|||
|
|
let settings = await PaymentSetting.findOne({
|
|||
|
|
where: { isDeleted: 0 }
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 如果没有记录,返回空对象
|
|||
|
|
if (!settings) {
|
|||
|
|
return res.json({
|
|||
|
|
code: 200,
|
|||
|
|
message: '获取支付设置成功',
|
|||
|
|
data: {}
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
res.json({
|
|||
|
|
code: 200,
|
|||
|
|
message: '获取支付设置成功',
|
|||
|
|
data: settings
|
|||
|
|
});
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('获取支付设置失败:', error);
|
|||
|
|
res.status(500).json({
|
|||
|
|
code: 500,
|
|||
|
|
message: '获取支付设置失败',
|
|||
|
|
error: error.message
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 更新支付设置
|
|||
|
|
const updatePaymentSettings = async (req, res) => {
|
|||
|
|
try {
|
|||
|
|
const isSystemAdmin = req.user.userType === 'super_admin';
|
|||
|
|
|
|||
|
|
// 只有超级管理员可以修改支付设置
|
|||
|
|
if (!isSystemAdmin) {
|
|||
|
|
return res.status(403).json({
|
|||
|
|
code: 403,
|
|||
|
|
message: '无权操作,请联系管理员'
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
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
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
res.json({
|
|||
|
|
code: 200,
|
|||
|
|
message: '支付设置更新成功',
|
|||
|
|
data: settings
|
|||
|
|
});
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('更新支付设置失败:', error);
|
|||
|
|
res.status(500).json({
|
|||
|
|
code: 500,
|
|||
|
|
message: '更新支付设置失败',
|
|||
|
|
error: error.message
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
module.exports = {
|
|||
|
|
getAllPlans,
|
|||
|
|
createPlan,
|
|||
|
|
updatePlan,
|
|||
|
|
deletePlan,
|
|||
|
|
setDefaultPlan,
|
|||
|
|
getPricingConfig,
|
|||
|
|
updatePricingConfig,
|
|||
|
|
initDefaultPlans,
|
|||
|
|
initDefaultPricing,
|
|||
|
|
getOrders,
|
|||
|
|
createOrder,
|
|||
|
|
calculatePrice,
|
|||
|
|
getOrderDetail,
|
|||
|
|
cancelOrder,
|
|||
|
|
payOrder,
|
|||
|
|
getPayments,
|
|||
|
|
getBillingInfo,
|
|||
|
|
getUsageStats,
|
|||
|
|
getBillingStats,
|
|||
|
|
getPaymentSettings,
|
|||
|
|
updatePaymentSettings
|
|||
|
|
};
|