Compare commits
4
Commits
f8bca284a8
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
10569bb828 | ||
|
|
11f31762ad | ||
|
|
7785de7d1f | ||
|
|
4da1fc9301 |
@@ -16,7 +16,7 @@
|
||||
|
||||
### 三种数据传递模式(实测现状)
|
||||
|
||||
1. **模式 B(fetch 外部 API)**:页面 JS 用 `fetch()` 调 `https://api.doorcome.cn/api/*`(如 `stockparam`、`indexDatas`、`stockinfo`、`getdiv`、`stockep`、`stockmargin`、`stockbasic`)。代表页面:`stock_trend.php`、`stockdiv.php`、`stockep.php`、`stockmargin.php`、`hkholdbycode.php`、`index_trend.php`、`index_mv_all.php`、`index_margin.php`。
|
||||
1. **模式 B(fetch 外部 API)**:页面 JS 用 `fetch()` 调 `https://api.doorcome.cn/api/*`(如 `stockparam`、`indexDatas`、`stockinfo`、`getdiv`、`stockep`、`stockmargin`、`stockbasic`、`news/reports`、`news/events`)。代表页面:`stock_trend.php`、`stockdiv.php`、`stockep.php`、`stockmargin.php`、`hkholdbycode.php`、`index_trend.php`、`index_mv_all.php`、`index_margin.php`、`news_reports.php`(日报 + 重要事件聚合)。
|
||||
2. **模式 C(AJAX → `inc/ajax.inc.php`)**:页面 JS 用 `$.ajax` 调 `../inc/ajax.inc.php`,通过 `$_REQUEST['t']` 路由(if 链,不是 switch/case)。`t` 取值:`stockList`、`financeData`、`esfTBD`、`esfListDaily`、`newTBD`、`moneyflowData`、`estateData`。代表页面:`moneyflow.php`、`realestate.php`、`estateTradeDaily.php`、`estateListDaily.php`、`estateNewTradeDaily.php`、`stockkeydata.php`、`stockTradeRecord.php`。
|
||||
3. **模式 A(服务端注入)**:PHP 查询后 `json_encode()` 注入 `<script>` 标签中的 JS 变量。`window.chartConfig` 全局变量**仅剩 `deprecated/` 在用**;活跃页面 `charts/chartStDetail.php` 是变体(注入命名变量 `data1`…`data5`、`legend` 等)。
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
**模式 B(较新——JS fetch 调用外部 API):**
|
||||
1. 页面直接加载 JS,不注入数据
|
||||
2. JS 在 `$(document).ready` 中通过 `fetch()` 调用 `https://api.doorcome.cn/api/*`(如 `stockparam`、`indexDatas`、`stockinfo`、`getdiv`、`stockep`、`stockmargin`、`stockbasic`)获取数据
|
||||
3. 代表页面:`stock_trend.php`、`stockdiv.php`、`stockep.php`、`stockmargin.php`、`hkholdbycode.php`、`index_trend.php`、`index_mv_all.php`、`index_margin.php`
|
||||
3. 代表页面:`stock_trend.php`、`stockdiv.php`、`stockep.php`、`stockmargin.php`、`hkholdbycode.php`、`index_trend.php`、`index_mv_all.php`、`index_margin.php`、`news_reports.php`(含重要事件聚合,调 `/api/news/events/`)
|
||||
|
||||
**模式 C(AJAX → `inc/ajax.inc.php`):**
|
||||
1. 页面 JS 用 `$.ajax` 调 `../inc/ajax.inc.php`,通过 `$_REQUEST['t']` 路由(if 链):`stockList`、`financeData`、`esfTBD`、`esfListDaily`、`newTBD`、`moneyflowData`、`estateData`
|
||||
@@ -82,6 +82,7 @@
|
||||
| `charts/stockTradeRecord.php` | 个人股票交易记录 |
|
||||
| `charts/myexcel.php` | 交易记录 Excel/CSV 导入处理(配合上传流程) |
|
||||
| `charts/chartStDetail.php` | 个股详细图表(服务端注入命名变量 data1…data5) |
|
||||
| `charts/news_reports.php` | 投资资讯日报导航页(国内/国际日报列表+详情+重要事件聚合,fetch api.doorcome.cn) |
|
||||
|
||||
> 注:`stock_ih.php`、`index_ih.php`、`chartSix.php`、`chartThree.php` 及旧版 `stock_trend.php`/`index_trend.php`/`index_mv_all.php` 已移入 `deprecated/`,勿引用。
|
||||
|
||||
|
||||
+8
-1
@@ -2,7 +2,7 @@
|
||||
|
||||
数据库:`myquant`(配置在 `inc/config.php`)
|
||||
|
||||
共 16 张表,其中 10 张在使用,3 张未使用,3 张已由 API 替代。
|
||||
共 18 张表,其中 12 张在使用,3 张未使用,3 张已由 API 替代。
|
||||
|
||||
## 全表清单
|
||||
|
||||
@@ -52,6 +52,13 @@
|
||||
| `pv_log` | ✅ 在用 | 页面访问日志 | `inc/functions.inc.php` |
|
||||
| `pv_counter` | ✅ 在用 | 页面访问计数器 | `inc/functions.inc.php` |
|
||||
|
||||
### 日报 API(2 张,djapi 后端管理)
|
||||
|
||||
| 表名 | 状态 | 用途 | 引用文件 |
|
||||
|------|------|------|---------|
|
||||
| `news_report` | ✅ 在用 | 投资资讯日报主表(国内 finance / 国际 intl,每天每类型一份) | djapi `api/report/` 写入;前端 `charts/news_reports.php` 经 `api.doorcome.cn/api/news/reports/` 只读消费 |
|
||||
| `news_event` | ✅ 在用 | 日报事件明细(xwlb/news/cninfo/intl 板块,含重要度/情感/来源/链接) | 同上,`/api/news/events/` 跨日报聚合 |
|
||||
|
||||
## 说明
|
||||
|
||||
- 所有 SQL 查询均通过 `db_query()` 参数化查询(prepare/execute),`excelOperate.inc.php` 中 insert 操作用字符串拼接
|
||||
|
||||
@@ -36,24 +36,32 @@ A 股基本面分析和宁波房地产数据可视化平台,基于 PHP + EChar
|
||||
| 二手房每日成交量 | 宁波各区县二手房成交面积和套数(日/月维度) |
|
||||
| 二手房每日挂牌量 | 宁波各区县二手房每日新增挂牌套数和均价 |
|
||||
|
||||
### 投资资讯日报
|
||||
|
||||
| 页面 | 功能 |
|
||||
|------|------|
|
||||
| 日报查询导航页(`charts/news_reports.php`) | 国内 / 国际 AI 深度研究日报,按日期分模块查看(AI 摘要、新闻联播要闻、财经新闻、公告调研、数据总览) |
|
||||
| 重要事件聚合 | 跨日报检索重要事件(近 N 天 / 重要度 / 类型筛选),数据来自 `api.doorcome.cn/api/news/events/` |
|
||||
|
||||
### 其他模块
|
||||
|
||||
- **新闻联播分析**(`news/`):CCTV 新闻联播内容抓取与关键词分析
|
||||
- **研究报告**(`research/`):AI 产业链、锂矿、新能源等行业深度研究报告(含 PDF 和 HTML 版本)
|
||||
- **宏观研究**(`quant/`):宏观量化分析报告,数据存储在 `mac_report` 表
|
||||
|
||||
## 技术栈
|
||||
|
||||
- **后端**:PHP 8.4,无框架
|
||||
- **前端**:ECharts 5.4、jQuery 3.6、Tailwind CSS 3.4、DataTables 1.13、Font Awesome 6.4
|
||||
- **数据库**:MySQL,通过 `mysqli` 连接
|
||||
- **外部 API**:TuShare(`api.tushare.pro` / `api.waditu.com`)
|
||||
- **外部 API**:TuShare(`api.tushare.pro` / `api.waditu.com`)、日报接口(`api.doorcome.cn/api/news/reports/` 与 `/api/news/events/`,由 djapi 后端提供,表 `news_report`/`news_event`)
|
||||
- **数据来源**:宁波市房产交易服务信息网(`cnnbfdc.com`)
|
||||
|
||||
## 项目结构
|
||||
|
||||
```
|
||||
├── index.php / index-2.php # 入口导航页
|
||||
├── charts/ # 数据可视化页面(21个)
|
||||
├── charts/ # 数据可视化页面(含 charts/news_reports.php 日报导航页)
|
||||
├── inc/ # PHP 核心代码
|
||||
│ ├── config.php # DB连接、db_query()、jsonResponse()
|
||||
│ ├── getData.inc.php # 数据查询(指数、持仓、资金流向)
|
||||
@@ -70,11 +78,12 @@ A 股基本面分析和宁波房地产数据可视化平台,基于 PHP + EChar
|
||||
│ ├── js/ # JS 库(jQuery、ECharts、DataTables 等)
|
||||
│ ├── css/ # CSS 库(Font Awesome、DataTables)
|
||||
│ └── webfonts/ # 字体文件
|
||||
├── js/ # 页面专属图表 JS
|
||||
├── js/ # 页面专属图表/渲染 JS(newsReports.js 为日报页逻辑)
|
||||
├── css/ # 自定义样式
|
||||
├── html/ # 公共 HTML 头部/底部
|
||||
├── news/ # 新闻联播分析模块
|
||||
├── research/ # 研究报告模块
|
||||
├── podcast-docs/ # 播客相关文档
|
||||
└── deprecated/ # 已废弃的旧页面
|
||||
```
|
||||
|
||||
@@ -82,15 +91,16 @@ A 股基本面分析和宁波房地产数据可视化平台,基于 PHP + EChar
|
||||
|
||||
```
|
||||
用户请求 → .php 页面(charts/)
|
||||
→ inc/*.php 查询 MySQL 或调用 TuShare API
|
||||
→ json_encode() 注入 <script> 标签
|
||||
→ js/*.js 读取 window.chartConfig 或 fetch API
|
||||
→ ECharts 渲染交互式图表
|
||||
→ 模式A: inc/*.php 查询 MySQL → json_encode() 注入 <script> 变量 → js/*.js 渲染
|
||||
→ 模式B: js 直接 fetch api.doorcome.cn/api/* (日报、指数、个股行情等新页面)
|
||||
→ 模式C: js 通过 $.ajax 调 inc/ajax.inc.php 的 t= 路由(房地产、资金流向等)
|
||||
→ ECharts / HTML 渲染交互式图表
|
||||
```
|
||||
|
||||
## 开发环境
|
||||
|
||||
- **本地 PHP**:`d:\software\php8.4\`
|
||||
- **部署**:`sync-echart`(ssh + tar 同步到 `www.doorcome.cn:/var/www/html/echart/`)
|
||||
- **本地 PHP**:`d:\software\php8.4\`(本机 Mac 无 PHP,`php -l` 语法检查走服务器)
|
||||
- **部署**:`~/bin/sync-echart`(rsync 单向推送本地 → `simon@www.doorcome.cn:/var/www/html/echart/`,`-n` 预览;排除本地环境文件与数据目录 uploads/xls/files/research)
|
||||
- **代码仓库**:gitea `ssh://git@gitea/simon/echart.git`(`~/.ssh/config` 别名 gitea,端口 2222)
|
||||
- **调试**:XDebug 端口 9000(VS Code 配置 `.vscode/launch.json`)
|
||||
- **代码指引**:见 `CLAUDE.md` 和 `AGENTS.md`
|
||||
|
||||
+35
-2
@@ -35,7 +35,7 @@
|
||||
.badge-neg { @apply bg-red-100 text-red-700; }
|
||||
.badge-neu { @apply bg-gray-200 text-gray-600; }
|
||||
|
||||
.type-tab {
|
||||
.type-tab, .view-tab {
|
||||
@apply px-4 py-2 rounded-lg text-sm font-medium text-gray-500 hover:text-primary hover:bg-primary/5 transition-colors cursor-pointer;
|
||||
}
|
||||
.tab-active { @apply bg-primary text-white hover:text-white hover:bg-primary shadow-sm; }
|
||||
@@ -148,6 +148,10 @@
|
||||
<!-- 筛选栏 -->
|
||||
<div class="bg-white rounded-xl shadow-sm border border-gray-100 p-4 mb-6">
|
||||
<div class="flex flex-wrap items-center gap-3">
|
||||
<div class="flex bg-gray-100 rounded-lg p-1">
|
||||
<span class="view-tab tab-active" data-view="list">日报列表</span>
|
||||
<span class="view-tab" data-view="events">重要事件</span>
|
||||
</div>
|
||||
<div class="flex bg-gray-100 rounded-lg p-1">
|
||||
<span class="type-tab tab-active" data-type="">全部</span>
|
||||
<span class="type-tab" data-type="finance">国内</span>
|
||||
@@ -169,6 +173,35 @@
|
||||
<div id="reportList" class="space-y-4"></div>
|
||||
</div>
|
||||
|
||||
<!-- 重要事件聚合视图 -->
|
||||
<div id="eventsView" class="hidden">
|
||||
<div class="bg-white rounded-xl shadow-sm border border-gray-100 p-4 mb-4">
|
||||
<div class="flex flex-wrap items-center gap-3">
|
||||
<span class="text-sm text-gray-500">近</span>
|
||||
<select id="evDays" class="filter-input">
|
||||
<option value="7" selected>7</option>
|
||||
<option value="14">14</option>
|
||||
<option value="30">30</option>
|
||||
</select>
|
||||
<span class="text-sm text-gray-500">天 · 重要度 ≥</span>
|
||||
<select id="evImportance" class="filter-input">
|
||||
<option value="4" selected>4</option>
|
||||
<option value="5">5</option>
|
||||
<option value="3">3</option>
|
||||
</select>
|
||||
<span class="text-sm text-gray-500">类型</span>
|
||||
<select id="evType" class="filter-input">
|
||||
<option value="">全部</option>
|
||||
<option value="finance">国内</option>
|
||||
<option value="intl">国际</option>
|
||||
</select>
|
||||
<button id="btnEvQuery" class="btn-primary"><i class="fa-solid fa-magnifying-glass mr-1"></i>查询</button>
|
||||
</div>
|
||||
<div class="mt-3 text-sm text-gray-400" id="eventsInfo"></div>
|
||||
</div>
|
||||
<div id="eventsList"></div>
|
||||
</div>
|
||||
|
||||
<!-- 详情视图 -->
|
||||
<div id="detailView" class="hidden">
|
||||
<div class="flex items-center justify-between mb-4 flex-wrap gap-2">
|
||||
@@ -193,6 +226,6 @@
|
||||
数据来源: api.doorcome.cn 日报接口 · 仅供个人研究参考
|
||||
</footer>
|
||||
|
||||
<script src="/js/newsReports.js"></script>
|
||||
<script src="/js/newsReports.js?v=20260806"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+80
-2
@@ -14,7 +14,7 @@ var NewsReports = {
|
||||
SECTION_ICONS: { xwlb: 'fa-tower-broadcast', news: 'fa-fire', cninfo: 'fa-clipboard-list', intl: 'fa-earth-asia' },
|
||||
SENTIMENT: { positive: { icon: '🟢', cls: 'badge-pos', txt: '利好' }, negative: { icon: '🔴', cls: 'badge-neg', txt: '利空' }, neutral: { icon: '⚪', cls: 'badge-neu', txt: '中性' } },
|
||||
|
||||
state: { reportType: '', startDate: '', endDate: '', currentId: null },
|
||||
state: { reportType: '', startDate: '', endDate: '', currentId: null, eventsLoaded: false },
|
||||
|
||||
init: function () {
|
||||
var now = new Date();
|
||||
@@ -22,6 +22,10 @@ var NewsReports = {
|
||||
this.state.startDate = this.fmtDate(start);
|
||||
this.state.endDate = this.fmtDate(now);
|
||||
|
||||
// 默认值写入日期输入框 (开始=30天前, 结束=今天)
|
||||
$('#startDate').val(this.state.startDate);
|
||||
$('#endDate').val(this.state.endDate);
|
||||
|
||||
this.bindEvents();
|
||||
this.loadReports();
|
||||
},
|
||||
@@ -39,6 +43,12 @@ var NewsReports = {
|
||||
|
||||
bindEvents: function () {
|
||||
var self = this;
|
||||
// 视图切换 (日报列表 / 重要事件)
|
||||
$('.view-tab').on('click', function () {
|
||||
$('.view-tab').removeClass('tab-active');
|
||||
$(this).addClass('tab-active');
|
||||
self.switchView($(this).data('view'));
|
||||
});
|
||||
// 类型 Tabs
|
||||
$('.type-tab').on('click', function () {
|
||||
$('.type-tab').removeClass('tab-active');
|
||||
@@ -52,12 +62,73 @@ var NewsReports = {
|
||||
self.state.endDate = $('#endDate').val() || self.state.endDate;
|
||||
self.loadReports();
|
||||
});
|
||||
// 重要事件查询
|
||||
$('#btnEvQuery').on('click', function () {
|
||||
self.loadEvents();
|
||||
});
|
||||
// 返回列表
|
||||
$('#btnBack').on('click', function () {
|
||||
self.showList();
|
||||
});
|
||||
},
|
||||
|
||||
// ==================== 重要事件聚合 ====================
|
||||
switchView: function (view) {
|
||||
if (view === 'events') {
|
||||
$('#listView').addClass('hidden');
|
||||
$('#detailView').addClass('hidden');
|
||||
$('#eventsView').removeClass('hidden');
|
||||
if (!this.state.eventsLoaded) this.loadEvents();
|
||||
} else {
|
||||
$('#eventsView').addClass('hidden');
|
||||
$('#listView').removeClass('hidden');
|
||||
}
|
||||
},
|
||||
|
||||
loadEvents: function () {
|
||||
var self = this;
|
||||
this.showLoading('正在加载重要事件...');
|
||||
var days = $('#evDays').val() || 7;
|
||||
var imp = $('#evImportance').val() || 4;
|
||||
var type = $('#evType').val() || '';
|
||||
var url = this.API_EVENTS + '?days=' + days + '&importance=' + imp + '&limit=300';
|
||||
if (type) url += '&report_type=' + type;
|
||||
$.getJSON(url)
|
||||
.done(function (data) {
|
||||
self.renderEvents(data);
|
||||
self.state.eventsLoaded = true;
|
||||
self.hideLoading();
|
||||
})
|
||||
.fail(function (xhr) {
|
||||
self.renderEventsError(xhr);
|
||||
self.hideLoading();
|
||||
});
|
||||
},
|
||||
|
||||
renderEvents: function (list) {
|
||||
var self = this;
|
||||
if (!Array.isArray(list) || list.length === 0) {
|
||||
$('#eventsList').html('<div class="empty-box"><i class="fa-solid fa-inbox"></i><p>该条件下暂无重要事件</p></div>');
|
||||
$('#eventsInfo').text('');
|
||||
return;
|
||||
}
|
||||
$('#eventsInfo').text('共 ' + list.length + ' 条重要事件(按重要度、日期降序)');
|
||||
var rows = list.map(function (e, i) {
|
||||
var typeName = self.TYPE_NAMES[e.report_type] || e.report_type;
|
||||
var secName = self.SECTION_NAMES[e.section] || e.section;
|
||||
var extra = ['<span class="meta-item"><i class="fa-regular fa-calendar mr-1"></i>' + selfEscape(e.report_date) + ' · ' + selfEscape(typeName) + ' · ' + selfEscape(secName) + '</span>'];
|
||||
return self.renderEventRow(e, i + 1, extra);
|
||||
}).join('');
|
||||
$('#eventsList').html('<div class="module-card"><div class="module-title"><i class="fa-solid fa-fire text-indigo-600"></i>重要事件聚合<span class="module-sub">跨日报检索 · 重要度 ≥ ' + $('#evImportance').val() + '</span></div><div class="divide-y divide-gray-100">' + rows + '</div></div>');
|
||||
},
|
||||
|
||||
renderEventsError: function (xhr) {
|
||||
var detail = '';
|
||||
if (xhr && xhr.responseJSON && xhr.responseJSON.error) detail = xhr.responseJSON.error;
|
||||
$('#eventsList').html('<div class="empty-box"><i class="fa-solid fa-triangle-exclamation text-red-500"></i><p>重要事件加载失败</p>' + (detail ? '<p class="text-sm text-gray-400">' + selfEscape(detail) + '</p>' : '') + '</div>');
|
||||
$('#eventsInfo').text('');
|
||||
},
|
||||
|
||||
// ==================== 列表 ====================
|
||||
loadReports: function () {
|
||||
var self = this;
|
||||
@@ -230,13 +301,14 @@ var NewsReports = {
|
||||
'</div>';
|
||||
},
|
||||
|
||||
renderEventRow: function (e, idx) {
|
||||
renderEventRow: function (e, idx, extraMeta) {
|
||||
var sen = this.SENTIMENT[e.sentiment] || this.SENTIMENT.neutral;
|
||||
var impCls = e.importance >= 5 ? 'imp-5' : (e.importance >= 4 ? 'imp-4' : '');
|
||||
var title = selfEscape(e.title || '');
|
||||
if (e.url) title = '<a href="' + selfEscape(e.url) + '" target="_blank" rel="noopener" class="event-link">' + title + '<i class="fa-solid fa-up-right-from-square ml-1 text-xs"></i></a>';
|
||||
var summary = e.summary ? '<p class="event-summary">' + selfEscape(e.summary) + '</p>' : '';
|
||||
var meta = [];
|
||||
if (extraMeta) meta = meta.concat(extraMeta);
|
||||
if (e.event_type) meta.push('<span class="meta-item"><i class="fa-regular fa-tag mr-1"></i>' + selfEscape(e.event_type) + '</span>');
|
||||
if (e.source) meta.push('<span class="meta-item"><i class="fa-regular fa-newspaper mr-1"></i>' + selfEscape(e.source) + '</span>');
|
||||
return '' +
|
||||
@@ -291,6 +363,12 @@ var NewsReports = {
|
||||
}
|
||||
return;
|
||||
}
|
||||
if ('count' in item) {
|
||||
// intl 英文键格式: {"importance":2,"count":2} / {"event_type":"宏观","count":15} / {"source":"X","count":30}
|
||||
var lk2 = Object.keys(item).filter(function (k) { return k !== 'count'; })[0];
|
||||
out.push({ label: lk2 ? item[lk2] : '—', value: item['count'] });
|
||||
return;
|
||||
}
|
||||
var keys = Object.keys(item);
|
||||
if (keys.length >= 2) out.push({ label: item[keys[0]], value: item[keys[1]] });
|
||||
else if (keys.length === 1) out.push({ label: keys[0], value: item[keys[0]] });
|
||||
|
||||
Reference in New Issue
Block a user