feat: 新增免费演出雷达工具与定投评估功能,优化周报提醒逻辑
1. 新增免费演出雷达工具:含秀动/猫眼/大麦平台抓取、本地代理鉴权配置 2. 新增建行定投评估模块:四大指数PE-TTM估值与定投档位建议 3. 新增推送告警白名单过滤功能,限制推送指定指数告警 4. 重构周报首页提醒逻辑,移至服务端处理并减少前端请求 5. 新增多套调试探针脚本与单元测试用例
This commit is contained in:
+10
-112
@@ -997,95 +997,6 @@ class ToolboxHome {
|
||||
};
|
||||
}
|
||||
|
||||
htmlToPlainText(html) {
|
||||
const temp = document.createElement('div');
|
||||
temp.innerHTML = String(html || '');
|
||||
return String(temp.textContent || temp.innerText || '')
|
||||
.replace(/\u00a0/g, ' ')
|
||||
.replace(/\s+/g, ' ')
|
||||
.trim();
|
||||
}
|
||||
|
||||
getWeeklyTemplatePlainText() {
|
||||
return [
|
||||
'1、本周核心重点事项(置顶)',
|
||||
'①置顶优先级,避免杂事分散精力',
|
||||
'②本周 1-3 件最高优先级事(工作 / 家庭各列,如公司项目节点、孩子升学事宜)',
|
||||
'③必做底线项(如固定会议、家庭日常、记账 / 复盘打卡)',
|
||||
'2、上周复盘(工作)',
|
||||
'-----',
|
||||
'①公司工作完成项(标注是否达标、核心成果)',
|
||||
'②未完成项 + 原因(客观 / 主观,如时间不足、需求变更)',
|
||||
'③规则事务完成情况(重点记未完成的关键项)',
|
||||
'3、上周复盘(个人 + 家庭)',
|
||||
'-----',
|
||||
'①个人 / 家庭事务完成情况(重点记未完成的关键项)',
|
||||
'4、公司工作(计划)',
|
||||
'-----',
|
||||
'①核心任务(按重要 / 紧急排序,标注截止时间、协作人)',
|
||||
'②常规工作(日常运维、对接、汇报等固定事项)',
|
||||
'③待协调 / 需支持项(如需要同事配合、领导审批的内容)',
|
||||
'5、个人和家庭 事务(计划,含理财)',
|
||||
'-----',
|
||||
'①自我提升(学习、运动、习惯打卡,如读 1 章书、运动 3 次)',
|
||||
'②个人琐事(办事、缴费、社交等,标注时间节点)',
|
||||
'③投资理财(盯盘节点、条件单调整、复盘上周收益 / 操作)',
|
||||
'④家人相关(孩子学习 / 活动、老人照料、家庭聚餐等)',
|
||||
'⑤家庭琐事(家务、采购、物业对接等,可标注分工)',
|
||||
'⑥家庭规划(短期家庭事项,如周末出游、家电维修)',
|
||||
'6、待解决问题 / 待协调项',
|
||||
'-----',
|
||||
'①工作中可能遇到的问题(如项目延期风险、资源不足)',
|
||||
'②个人 / 家庭的待处理问题(如时间冲突、资金安排)',
|
||||
'③初步应对思路(简单标注,避免临时慌乱)',
|
||||
'7、本周小目标 / 打卡项(轻量化激励,易落地)',
|
||||
'①工作小目标(如高效完成 1 个核心任务、减少无效沟通)',
|
||||
'②个人 / 家庭小目标(如坚持记账、周末全家运动 1 次)'
|
||||
].join(' ');
|
||||
}
|
||||
|
||||
isWeeklyAutoTitle(title, weekId = '') {
|
||||
const normalizedTitle = String(title || '').trim();
|
||||
if (!normalizedTitle) return true;
|
||||
if (normalizedTitle.includes('(自动新增)')) return true;
|
||||
if (weekId && normalizedTitle === `${weekId} 周报`) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
isWeeklyMeaningfulReport(report, expectedWeekId = '') {
|
||||
if (!report || typeof report !== 'object') return false;
|
||||
const weekId = String(report.week_id || expectedWeekId || '').trim();
|
||||
const title = String(report.title || '').trim();
|
||||
const contentText = this.htmlToPlainText(report.content || '');
|
||||
if (!title || !contentText) return false;
|
||||
if (this.isWeeklyAutoTitle(title, weekId)) return false;
|
||||
if (contentText === '这是自动新增的周报。你可以修改。') return false;
|
||||
if (contentText === this.getWeeklyTemplatePlainText()) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
async getRecentWeeklyWindows(count = 3) {
|
||||
const today = this.formatDate(new Date());
|
||||
const currentYear = new Date().getFullYear();
|
||||
const years = [currentYear - 1, currentYear];
|
||||
const results = await Promise.all(
|
||||
years.map(async (year) => {
|
||||
const res = await fetch(`/api/weekly/weeks?year=${year}&t=${Date.now()}`, { cache: 'no-store' });
|
||||
if (!res.ok) throw new Error(`weekly_weeks_${res.status}`);
|
||||
const data = await res.json();
|
||||
return Array.isArray(data && data.weeks) ? data.weeks : [];
|
||||
})
|
||||
);
|
||||
const allWeeks = results
|
||||
.flat()
|
||||
.filter(item => item && item.week_id && item.start && item.end)
|
||||
.sort((a, b) => String(a.start).localeCompare(String(b.start)));
|
||||
if (allWeeks.length === 0) return [];
|
||||
const currentIndex = allWeeks.findIndex(item => String(item.start) <= today && today <= String(item.end));
|
||||
if (currentIndex < 0) return allWeeks.slice(-count);
|
||||
return allWeeks.slice(Math.max(0, currentIndex - count + 1), currentIndex + 1);
|
||||
}
|
||||
|
||||
renderFitnessDiaryReminder(summary) {
|
||||
if (!this.fitnessDiaryReminder || !this.fitnessDiaryReminderText || !this.fitnessDiaryReminderDates) return;
|
||||
const missingCount = Number(summary && summary.missingCount ? summary.missingCount : 0);
|
||||
@@ -1148,30 +1059,17 @@ class ToolboxHome {
|
||||
|
||||
async loadWeeklyReportReminder() {
|
||||
try {
|
||||
const weeks = await this.getRecentWeeklyWindows(3);
|
||||
if (!Array.isArray(weeks) || weeks.length === 0) return;
|
||||
const reportResults = await Promise.all(
|
||||
weeks.map(async (week) => {
|
||||
const res = await fetch(`/api/weekly/report?week_id=${encodeURIComponent(week.week_id)}&t=${Date.now()}`, {
|
||||
cache: 'no-store'
|
||||
});
|
||||
if (!res.ok) {
|
||||
if (res.status === 401) return { unauthorized: true, week };
|
||||
return { week, report: null };
|
||||
}
|
||||
const data = await res.json();
|
||||
return { week, report: data && data.ok === true ? data.report : null };
|
||||
})
|
||||
);
|
||||
if (reportResults.some(item => item && item.unauthorized)) return;
|
||||
const missingWeeks = reportResults
|
||||
.filter(item => !this.isWeeklyMeaningfulReport(item.report, item.week && item.week.week_id))
|
||||
.map(item => item.week)
|
||||
.filter(Boolean);
|
||||
// 【首页提醒专用】直接调后端聚合接口 /api/weekly/reminder/status。
|
||||
// 判定由服务端读库完成,不依赖周报 weekly_auth 登录态(仅需 weekly_gate 门禁);
|
||||
// 相对路径同源调用:本机访问即 localhost:8976,外网访问自动走 8975 反代,接口本身未授权一律 401。
|
||||
const res = await fetch(`/api/weekly/reminder/status?t=${Date.now()}`, { cache: 'no-store' });
|
||||
if (!res.ok) return; // 无门禁 / 接口异常:静默隐藏提醒
|
||||
const data = await res.json();
|
||||
if (!data || data.ok !== true) return;
|
||||
this.renderWeeklyReportReminder({
|
||||
weeks: 3,
|
||||
missingCount: missingWeeks.length,
|
||||
missingWeeks
|
||||
weeks: Number(data.weeks || 3),
|
||||
missingCount: Number(data.missingCount || 0),
|
||||
missingWeeks: Array.isArray(data.missingWeeks) ? data.missingWeeks : []
|
||||
});
|
||||
} catch (err) {
|
||||
console.warn('加载周报提醒失败:', err);
|
||||
|
||||
Reference in New Issue
Block a user