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 表示无限制(不校验) */
|
|
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,
|
|
};
|