const baseModel = require("../../middleware/baseModel"); const { op } = baseModel; function currentStatMonth(d = new Date()) { const y = d.getFullYear(); const m = String(d.getMonth() + 1).padStart(2, "0"); return `${y}-${m}`; } function num(v) { if (v === null || v === undefined || v === "") return 0; const n = Number(v); return Number.isNaN(n) ? 0 : n; } /** 0 表示无限制(不校验) */ function quotaExceeded(used, limit) { const li = num(limit); if (li <= 0) return false; return num(used) >= li; } /** * 取或创建当月用量行 */ async function getOrCreateUsage(userId, planId, statMonth) { const [row] = await baseModel.biz_usage_monthly.findOrCreate({ where: { user_id: userId, stat_month: statMonth }, defaults: { user_id: userId, plan_id: planId, stat_month: statMonth, msg_count: 0, mass_count: 0, friend_count: 0, sns_count: 0, active_user_count: 0, }, }); if (num(row.plan_id) !== num(planId)) { await row.update({ plan_id: planId }); } return row; } async function applyDelta(userId, planId, statMonth, delta) { const row = await getOrCreateUsage(userId, planId, statMonth); const next = { msg_count: num(row.msg_count) + num(delta.msg), mass_count: num(row.mass_count) + num(delta.mass), friend_count: num(row.friend_count) + num(delta.friend), sns_count: num(row.sns_count) + num(delta.sns), active_user_count: num(row.active_user_count) + num(delta.active_user), }; await row.update(next); return row.reload(); } /** * 校验「增量」后是否超限(与套餐额度对比) * feature: msg | mass | friend | sns | active_user */ function checkQuotaAfterDelta(plan, usageRow, delta) { const checks = [ ["msg", "msg_count", "msg_quota"], ["mass", "mass_count", "mass_quota"], ["friend", "friend_count", "friend_quota"], ["sns", "sns_count", "sns_quota"], ["active_user", "active_user_count", "active_user_limit"], ]; for (const [key, uCol, pCol] of checks) { const add = num(delta[key]); if (add <= 0) continue; const used = num(usageRow[uCol]) + add; if (quotaExceeded(used, plan[pCol])) { return { ok: false, error_code: "QUOTA_EXCEEDED", message: `额度不足: ${key}` }; } } return { ok: true }; } /** * 为当前月所有有效订阅补用量行(月结/初始化) */ async function ensureUsageRowsForCurrentMonth() { const statMonth = currentStatMonth(); const now = new Date(); const subs = await baseModel.biz_subscription.findAll({ where: { status: "active", start_time: { [op.lte]: now }, end_time: { [op.gte]: now }, }, }); let n = 0; for (const s of subs) { await getOrCreateUsage(s.user_id, s.plan_id, statMonth); n += 1; } return n; } module.exports = { currentStatMonth, getOrCreateUsage, applyDelta, checkQuotaAfterDelta, ensureUsageRowsForCurrentMonth, num, };