fix:优化当前的项目

This commit is contained in:
Daniel
2026-04-28 18:36:38 +08:00
parent 04f26bdaaf
commit f47453a656
22 changed files with 3671 additions and 89 deletions

292
app/static/upgrade.js Normal file
View File

@@ -0,0 +1,292 @@
const $ = (id) => document.getElementById(id);
let pendingOrderNo = "";
let pendingPollTimer = null;
let pendingPollCount = 0;
function setStatus(msg, danger = false) {
const el = $("status");
if (!el) return;
el.style.color = danger ? "#b42318" : "#0f5f3d";
el.textContent = msg;
}
function setLoading(button, loading, idleText, loadingText) {
if (!button) return;
button.disabled = loading;
button.textContent = loading ? loadingText : idleText;
}
function importantNotice(message, title = "提示") {
if (typeof window.uiAlert === "function") {
void window.uiAlert(message, title);
return;
}
setStatus(message);
}
function openPayLink(url) {
const payUrl = String(url || "").trim();
if (!payUrl) return false;
const tab = window.open(payUrl, "_blank", "noopener");
if (tab) return true;
window.location.href = payUrl;
return true;
}
async function postJSON(url, body) {
const res = await fetch(url, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body),
});
const data = await res.json();
if (!res.ok) throw new Error(data.detail || "请求失败");
return data;
}
async function fetchMe() {
const res = await fetch("/api/auth/me");
const data = await res.json();
if (!data.logged_in) {
window.location.href = "/auth?next=/upgrade";
return null;
}
return data;
}
function formatDateTime(tsSec) {
const n = Number(tsSec || 0);
if (!n) return "-";
return new Date(n * 1000).toLocaleString();
}
function formatDate(tsSec) {
const n = Number(tsSec || 0);
if (!n) return "-";
const d = new Date(n * 1000);
const y = d.getFullYear();
const m = String(d.getMonth() + 1).padStart(2, "0");
const day = String(d.getDate()).padStart(2, "0");
return `${y}-${m}-${day}`;
}
function refreshPurchaseCycleText() {
const el = $("purchaseCycleText");
if (!el) return;
const start = Math.floor(Date.now() / 1000);
const end = start + 30 * 24 * 3600;
el.textContent = `时长1个月到期${formatDate(end)}`;
}
function renderVip(vip) {
const shared = Number(vip.shared_credits || vip.token_balance || 0);
const seatRemaining = Number(vip.seat_remaining_credits || 0);
const totalAvailable = Number(vip.total_available_credits || seatRemaining + shared);
const enabled = Boolean(vip.vip_enabled);
const cycleStart = Number(vip.cycle_started_at || 0);
const cycleEnd = Number(vip.cycle_expires_at || 0);
if ($("upgradeTokenBalance")) $("upgradeTokenBalance").textContent = String(totalAvailable);
if ($("upgradeTokenBalanceHero")) $("upgradeTokenBalanceHero").textContent = String(totalAvailable);
if ($("vipTokenBalance")) $("vipTokenBalance").textContent = String(shared);
if ($("vipSeatRemaining")) $("vipSeatRemaining").textContent = String(seatRemaining);
if ($("vipTotalConsumed")) $("vipTotalConsumed").textContent = String(Number(vip.total_consumed_tokens || 0));
if ($("vipEnabledSelect")) $("vipEnabledSelect").value = enabled ? "1" : "0";
if ($("vipStateText")) {
$("vipStateText").textContent = totalAvailable <= 0 ? "额度已用完" : enabled ? "平台模型已开启" : "平台模型已关闭";
}
if ($("vipCycleHint")) {
if (cycleStart > 0 && cycleEnd > 0) {
const startText = formatDateTime(cycleStart);
const endText = formatDateTime(cycleEnd);
$("vipCycleHint").textContent = `当前周期:${startText} - ${endText}(到期自动清零)`;
} else {
$("vipCycleHint").textContent = "当前未开始月周期,首次支付成功后开始计时。";
}
}
if (totalAvailable <= 0) {
setStatus("Credits 额度已用完,请充值共享加油包或等待下个计费周期。", true);
}
}
async function refresh() {
const me = await fetchMe();
if (me && me.vip) renderVip(me.vip);
}
async function fetchBillingOverview() {
const res = await fetch("/api/billing/overview");
const data = await res.json();
if (!res.ok || !data.ok) throw new Error(data.detail || "账单读取失败");
return data;
}
function stopPendingPoll() {
if (pendingPollTimer) {
window.clearInterval(pendingPollTimer);
pendingPollTimer = null;
}
pendingPollCount = 0;
}
function startPendingPoll(orderNo, showCreatedNotice = true) {
if (!orderNo) return;
pendingOrderNo = orderNo;
stopPendingPoll();
const msg = `订单 ${orderNo} 已创建,请完成支付,系统将自动刷新余额。`;
setStatus(msg);
if (showCreatedNotice) {
importantNotice(msg, "订单已创建");
}
pendingPollTimer = window.setInterval(async () => {
pendingPollCount += 1;
try {
const data = await fetchBillingOverview();
const records = Array.isArray(data.recharge_records) ? data.recharge_records : [];
const current = records.find((r) => (r.order_no || "") === pendingOrderNo);
if (current && (current.status || "") === "paid") {
stopPendingPoll();
pendingOrderNo = "";
setStatus("支付成功,余额已自动刷新。");
await refresh();
return;
}
if (current && ["cancelled", "closed"].includes(String(current.status || "").toLowerCase())) {
stopPendingPoll();
pendingOrderNo = "";
setStatus("订单超过15分钟未支付已自动取消。", true);
await refresh();
return;
}
if (pendingPollCount >= 120) {
stopPendingPoll();
setStatus("订单仍未支付,可支付后点击刷新查看余额。", true);
}
} catch {
if (pendingPollCount >= 120) {
stopPendingPoll();
}
}
}, 3000);
}
const saveVipBtn = $("saveVipBtn");
const vipRechargeBtn = $("vipRechargeBtn");
const vipRechargeTokensInput = $("vipRechargeTokens");
const vipRechargeAmountInput = $("vipRechargeAmount");
const payChannelOptions = Array.from(document.querySelectorAll(".pay-channel-option"));
let selectedPayChannel = "wechat";
function bindPayChannelOptions() {
if (!payChannelOptions.length) return;
payChannelOptions.forEach((btn) => {
btn.addEventListener("click", () => {
selectedPayChannel = (btn.dataset.channel || "wechat").trim() || "wechat";
payChannelOptions.forEach((item) => {
const active = item === btn;
item.classList.toggle("is-active", active);
item.setAttribute("aria-pressed", active ? "true" : "false");
});
});
});
}
function packageRate() {
const credits = Number((vipRechargeTokensInput && vipRechargeTokensInput.dataset.packageCredits) || "1500");
const amount = Number((vipRechargeAmountInput && vipRechargeAmountInput.dataset.packageAmount) || "19.9");
return {
packageCredits: Number.isFinite(credits) && credits > 0 ? credits : 1500,
packageAmount: Number.isFinite(amount) && amount > 0 ? amount : 19.9,
};
}
function syncRechargeAmount() {
const { packageCredits, packageAmount } = packageRate();
const credits = packageCredits;
const amount = packageAmount;
if (vipRechargeTokensInput) vipRechargeTokensInput.value = String(credits);
vipRechargeAmountInput.value = amount.toFixed(2);
if ($("purchaseCredits")) $("purchaseCredits").textContent = String(credits);
if ($("purchaseAmount")) $("purchaseAmount").textContent = `¥${amount.toFixed(2)}`;
}
if (vipRechargeAmountInput) vipRechargeAmountInput.readOnly = true;
syncRechargeAmount();
refreshPurchaseCycleText();
bindPayChannelOptions();
if (saveVipBtn) {
saveVipBtn.addEventListener("click", async () => {
setLoading(saveVipBtn, true, "保存升级设置", "保存中...");
try {
const enabled = (($("vipEnabledSelect") && $("vipEnabledSelect").value) || "0") === "1";
const out = await postJSON("/api/auth/vip/toggle", { enabled });
if (!out.ok) {
setStatus(out.detail || "VIP 设置保存失败", true);
return;
}
setStatus("升级设置已保存。");
await refresh();
} catch (e) {
setStatus(e.message || "VIP 设置保存失败", true);
} finally {
setLoading(saveVipBtn, false, "保存升级设置", "保存中...");
}
});
}
if (vipRechargeBtn) {
vipRechargeBtn.addEventListener("click", async () => {
setLoading(vipRechargeBtn, true, "订阅", "创建订单中...");
try {
const tokens = Number((($("vipRechargeTokens") && $("vipRechargeTokens").value) || "0").trim());
const amount = Number((($("vipRechargeAmount") && $("vipRechargeAmount").value) || "0").trim());
if (!Number.isFinite(tokens) || tokens <= 0) {
setStatus("请输入正确的 Credits 数量", true);
return;
}
if (!Number.isFinite(amount) || amount <= 0) {
setStatus("请输入正确的支付金额", true);
return;
}
const out = await postJSON("/api/pay/wechat/", {
tokens: Math.round(tokens),
amount_cny: Number(amount.toFixed(2)),
channel: selectedPayChannel || "wechat",
subscriber_name: "",
subscriber_phone: "",
shipping_address: "",
});
if (!out.ok) {
setStatus(out.detail || "充值失败", true);
return;
}
const orderNo = out.order && out.order.order_no ? out.order.order_no : "";
if (orderNo) startPendingPoll(orderNo, true);
if (out.pay_url) {
openPayLink(out.pay_url);
return;
}
const tip = "订单已创建,但未获取到支付链接,请检查支付网关配置。";
setStatus(tip, true);
importantNotice(tip, "支付链接缺失");
} catch (e) {
setStatus(e.message || "充值失败", true);
} finally {
setLoading(vipRechargeBtn, false, "订阅", "创建订单中...");
}
});
}
refresh();
(async () => {
await fetchMe();
try {
const data = await fetchBillingOverview();
const records = Array.isArray(data.recharge_records) ? data.recharge_records : [];
const pending = records.find((r) => (r.status || "") === "pending");
if (pending && pending.order_no) startPendingPoll(pending.order_no, false);
} catch {
// ignore
}
})();
window.setInterval(refreshPurchaseCycleText, 60000);