fix: enforce account data boundaries and real-data integrity
This commit is contained in:
+38
-30
@@ -286,6 +286,8 @@ async function applyAuthenticatedSession(session) {
|
||||
updateAccountIdentityBadges(session.user?.membership || {});
|
||||
document.querySelector("#settingsButton").hidden = !isAdmin;
|
||||
document.querySelector("#syncButton").hidden = !isAdmin;
|
||||
document.querySelector("#reasonForm").hidden = !isAdmin;
|
||||
document.querySelector("#sectorPhaseManager").hidden = !isAdmin;
|
||||
const factorSyncButton = document.querySelector("#factorSyncButton");
|
||||
if (factorSyncButton) factorSyncButton.hidden = false;
|
||||
document.querySelector("#authGate").hidden = true;
|
||||
@@ -1309,8 +1311,9 @@ function renderDragonTraderDetail(trader) {
|
||||
|
||||
function renderUnclassifiedSeats() {
|
||||
const seats = state.dragonTiger?.unclassified_seats || [];
|
||||
document.querySelector("#dragonUnclassifiedSection").hidden = seats.length === 0;
|
||||
document.querySelector("#dragonUnclassifiedFilter").hidden = seats.length === 0;
|
||||
const canManage = state.user?.role === "admin";
|
||||
document.querySelector("#dragonUnclassifiedSection").hidden = !canManage || seats.length === 0;
|
||||
document.querySelector("#dragonUnclassifiedFilter").hidden = !canManage || seats.length === 0;
|
||||
if (!seats.length && state.dragonFilter === "unclassified") {
|
||||
state.dragonFilter = "all";
|
||||
document.querySelectorAll("[data-dragon-filter]").forEach((button) => {
|
||||
@@ -1826,7 +1829,7 @@ async function loadMentorSetup(force = false) {
|
||||
state.mentorSetup = payload;
|
||||
const selectedExists = payload.mentors.some((item) => item.id === state.selectedMentorId);
|
||||
state.selectedMentorId = selectedExists ? state.selectedMentorId : payload.mentors[0]?.id || "";
|
||||
state.mentorMessages = loadStoredMentorMessages();
|
||||
state.mentorMessages = await loadMentorMessages();
|
||||
renderMentorWorkspace();
|
||||
} catch (error) {
|
||||
showMentorNotice(error.message || "问师模块加载失败");
|
||||
@@ -1854,13 +1857,14 @@ function renderMentorWorkspace() {
|
||||
renderMentorMessages();
|
||||
}
|
||||
|
||||
function selectMentor(mentorId) {
|
||||
async function selectMentor(mentorId) {
|
||||
if (mentorId === state.selectedMentorId) return;
|
||||
saveStoredMentorMessages();
|
||||
state.selectedMentorId = mentorId;
|
||||
state.mentorMessages = loadStoredMentorMessages();
|
||||
state.mentorMessages = [];
|
||||
hideMentorNotice();
|
||||
renderMentorWorkspace();
|
||||
state.mentorMessages = await loadMentorMessages();
|
||||
renderMentorMessages();
|
||||
}
|
||||
|
||||
function renderMentorMessages() {
|
||||
@@ -1925,7 +1929,6 @@ async function sendMentorQuestion(event) {
|
||||
content: payload.answer,
|
||||
meta: `${displayCompactDate(payload.data_trade_date)} · ${modelRole} ${payload.model} · ${number(payload.latency_ms)}ms`,
|
||||
});
|
||||
saveStoredMentorMessages();
|
||||
if (payload.notice) showMentorNotice(payload.notice);
|
||||
setStatus("问师回答完成");
|
||||
} catch (error) {
|
||||
@@ -1945,33 +1948,37 @@ function useMentorQuickPrompt(prompt) {
|
||||
input.focus();
|
||||
}
|
||||
|
||||
function clearMentorConversation() {
|
||||
async function clearMentorConversation() {
|
||||
if (!state.mentorMessages.length || !window.confirm("确定清空当前老师的对话记录吗?")) return;
|
||||
state.mentorMessages = [];
|
||||
try { localStorage.removeItem(mentorStorageKey()); } catch {}
|
||||
hideMentorNotice();
|
||||
renderMentorMessages();
|
||||
}
|
||||
|
||||
function mentorStorageKey() {
|
||||
const dateKey = (state.mentorSetup?.trade_date || elements.tradeDate.value).replaceAll("-", "");
|
||||
return `xiaobai-mentor-chat:${state.selectedMentorId}:${dateKey}`;
|
||||
}
|
||||
|
||||
function loadStoredMentorMessages() {
|
||||
if (!state.selectedMentorId) return [];
|
||||
try {
|
||||
const messages = JSON.parse(localStorage.getItem(mentorStorageKey()) || "[]");
|
||||
if (!Array.isArray(messages)) return [];
|
||||
return messages.filter((item) => ["user", "assistant"].includes(item?.role) && typeof item.content === "string").slice(-20);
|
||||
} catch {
|
||||
return [];
|
||||
const query = new URLSearchParams({
|
||||
mentor_id: state.selectedMentorId,
|
||||
trade_date: state.mentorSetup?.trade_date || elements.tradeDate.value,
|
||||
});
|
||||
await apiRequest(`/api/mentors/messages?${query}`, "DELETE");
|
||||
state.mentorMessages = [];
|
||||
hideMentorNotice();
|
||||
renderMentorMessages();
|
||||
} catch (error) {
|
||||
showToast(error.message || "对话记录清空失败");
|
||||
}
|
||||
}
|
||||
|
||||
function saveStoredMentorMessages() {
|
||||
if (!state.selectedMentorId) return;
|
||||
try { localStorage.setItem(mentorStorageKey(), JSON.stringify(state.mentorMessages.slice(-20))); } catch {}
|
||||
async function loadMentorMessages() {
|
||||
if (!state.selectedMentorId) return [];
|
||||
try {
|
||||
const query = new URLSearchParams({
|
||||
mentor_id: state.selectedMentorId,
|
||||
trade_date: state.mentorSetup?.trade_date || elements.tradeDate.value,
|
||||
});
|
||||
const payload = await apiRequest(`/api/mentors/messages?${query}`);
|
||||
return (payload.items || []).filter(
|
||||
(item) => ["user", "assistant"].includes(item?.role) && typeof item.content === "string",
|
||||
).slice(-100);
|
||||
} catch (error) {
|
||||
showMentorNotice(error.message || "对话记录加载失败");
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
function showMentorNotice(message) {
|
||||
@@ -2744,11 +2751,12 @@ function drawQiUseConnections(animate = false) {
|
||||
|
||||
function renderSectorPhaseOverrides(items) {
|
||||
const container = document.querySelector("#sectorPhaseOverrides");
|
||||
const canManage = state.user?.role === "admin";
|
||||
container.innerHTML = items.length ? items.map((item) => `
|
||||
<div class="sector-phase-override-row">
|
||||
<span class="phase-symbol phase-${phaseClass(item.element)}">${escapeHtml(item.element)}</span>
|
||||
<strong>${escapeHtml(item.name)}</strong>
|
||||
<button class="icon-button" type="button" data-sector-phase-delete="${escapeHtml(item.name)}" title="删除手动归类" aria-label="删除 ${escapeHtml(item.name)} 的手动归类">×</button>
|
||||
${canManage ? `<button class="icon-button" type="button" data-sector-phase-delete="${escapeHtml(item.name)}" title="删除手动归类" aria-label="删除 ${escapeHtml(item.name)} 的手动归类">×</button>` : ""}
|
||||
</div>
|
||||
`).join("") : '<p class="sector-phase-empty">暂无手动归类</p>';
|
||||
container.querySelectorAll("[data-sector-phase-delete]").forEach((button) => {
|
||||
|
||||
+2
-2
@@ -544,7 +544,7 @@
|
||||
<div class="workspace-heading"><h3>历法细目</h3><span>中运、司天、在泉与节气定位</span></div>
|
||||
<div id="fortuneMetrics" class="fortune-metrics"></div>
|
||||
</section>
|
||||
<details class="sector-phase-manager">
|
||||
<details id="sectorPhaseManager" class="sector-phase-manager">
|
||||
<summary class="sector-phase-manager-heading"><strong>管理手动归类</strong><span>精确名称优先</span><i data-lucide="chevron-down" aria-hidden="true"></i></summary>
|
||||
<div class="sector-phase-manager-body">
|
||||
<form id="sectorPhaseForm" class="sector-phase-form">
|
||||
@@ -1036,7 +1036,7 @@
|
||||
<h3>事件逻辑</h3>
|
||||
<p id="detailReason">--</p>
|
||||
<span id="detailSector" class="sector-chip">--</span>
|
||||
<form id="reasonForm" class="inline-edit-form">
|
||||
<form id="reasonForm" class="inline-edit-form" hidden>
|
||||
<input id="reasonInput" type="text" maxlength="200" placeholder="修订事件逻辑">
|
||||
<button class="button" type="submit">保存修订</button>
|
||||
</form>
|
||||
|
||||
Reference in New Issue
Block a user