111 lines
2.9 KiB
JavaScript
111 lines
2.9 KiB
JavaScript
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 表示无限制;已用量超过 limit 则超限(允许用到等于 limit) */
|
||
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,
|
||
};
|