/** * 订阅开通 / 升级 / 续费 / 取消 / 到期扫描 / 支付确认 */ const baseModel = require("../../middleware/baseModel"); const { op } = baseModel; async function assertUserActive(userId) { const u = await baseModel.biz_user.findByPk(userId); if (!u) throw new Error("用户不存在"); if (u.status !== "active") throw new Error("用户已禁用"); return u; } async function assertPlanActive(planId) { const p = await baseModel.biz_plan.findByPk(planId); if (!p) throw new Error("套餐不存在"); if (p.status !== "active") throw new Error("套餐未上线"); return p; } async function openSubscription(body) { const { user_id, plan_id, start_time, end_time, status = "pending", renew_mode = "manual", payment_channel, payment_ref, } = body; await assertUserActive(user_id); await assertPlanActive(plan_id); const row = await baseModel.biz_subscription.create({ user_id, plan_id, status, start_time, end_time, renew_mode, payment_channel: payment_channel || null, payment_ref: payment_ref || null, }); return row; } async function upgradeSubscription(body) { const { subscription_id, new_plan_id, start_time, end_time } = body; const sub = await baseModel.biz_subscription.findByPk(subscription_id); if (!sub) throw new Error("订阅不存在"); await assertPlanActive(new_plan_id); await sub.update({ plan_id: new_plan_id, start_time: start_time || sub.start_time, end_time: end_time || sub.end_time, }); return sub; } async function renewSubscription(body) { const { subscription_id, end_time } = body; const sub = await baseModel.biz_subscription.findByPk(subscription_id); if (!sub) throw new Error("订阅不存在"); await sub.update({ end_time, status: "active", }); return sub; } async function cancelSubscription(body) { const { subscription_id } = body; const sub = await baseModel.biz_subscription.findByPk(subscription_id); if (!sub) throw new Error("订阅不存在"); await sub.update({ status: "cancelled" }); return sub; } /** 每天扫描:将已过期且仍为 active 的订阅置为 expired */ async function expireDueSubscriptions() { const now = new Date(); const [n] = await baseModel.biz_subscription.update( { status: "expired" }, { where: { status: "active", end_time: { [op.lt]: now }, }, } ); return n; } /** 线下确认:pending -> active,写入 payment_ref */ async function confirmOfflinePayment(body) { const { subscription_id, payment_ref } = body; if (!subscription_id) throw new Error("缺少 subscription_id"); const sub = await baseModel.biz_subscription.findByPk(subscription_id); if (!sub) throw new Error("订阅不存在"); await sub.update({ status: "active", payment_channel: "offline", payment_ref: payment_ref || sub.payment_ref, }); return sub; } /** 链接支付确认(MVP:与线下类似,仅标记渠道) */ async function confirmLinkPayment(body) { const { subscription_id, payment_ref } = body; if (!subscription_id) throw new Error("缺少 subscription_id"); const sub = await baseModel.biz_subscription.findByPk(subscription_id); if (!sub) throw new Error("订阅不存在"); await sub.update({ status: "active", payment_channel: "pay_link", payment_ref: payment_ref || sub.payment_ref, }); return sub; } module.exports = { openSubscription, upgradeSubscription, renewSubscription, cancelSubscription, expireDueSubscriptions, confirmOfflinePayment, confirmLinkPayment, };