|
|
# 企业新闻舆情部署与维护
|
|
|
|
|
|
后台页面 `/companyNews/index` 支持新闻采集、公司和发布日期筛选、人工正文编辑、留存审核及批次记录。后台驾驶舱 `/home` 的校友动态通过需管理员登录的 `/api/admin/other/alumni-news` 读取本年度已留存新闻,每页 8 条,按原文发布日期倒序;该内部展示不依赖 `is_visible`。当前没有匿名公开展示接口。所有新闻默认 `is_visible=false`,保存与审核也不会开启展示。
|
|
|
|
|
|
## 业务规则
|
|
|
|
|
|
- 点击“获取更新”创建一个全量候选和已占额企业快照,不受新闻列表筛选和分页影响;仅 `company_qcc_accounts.status` 为 `selected` 或 `occupied` 的企业进入批次,按公司 ID 顺序执行。
|
|
|
- 执行前再次检查企业是否仍为候选或已占额;不符合的任务记录失败/跳过,不调用元禾。
|
|
|
- 单次请求使用元禾 `enterprise/packInfo`,连接超时 10 秒、总超时 60 秒;只有外层 `code=200` 且 `data.news` 是数组或 null 才进行处理。null/空数组记成功、0 新增;缺字段、非数组记失败。
|
|
|
- 本功能直接调用 Repository:code=200 且 data 中存在非空业务内容(新闻、股东等)时,仅将 selected 转为 occupied,记录首次成功时间、来源和引用;已占额及首次成功记录不重复改写。空数据、失败、超时不改状态。占额记录独立于新闻入库事务,避免本地保存失败丢失上游成功事实。原来的 `qcc:verify-pack-info` 诊断命令仍有状态流转,不能用它代替本功能。
|
|
|
- 按企业、数据提供方及去重键建立唯一约束。去重键按 NewsId、Id、原文 URL 的优先级生成 SHA-256;来源标识必须跨次稳定。同一新闻关联不同公司分别保存。
|
|
|
- 重复采集只更新来源字段、原始 JSON 与最近采集信息,不覆盖标题、人工正文和人工留存结论;自动判断的记录会按本批次分类设置重新判断,始终不展示。不留存是逻辑状态,不删除记录。
|
|
|
- 原文 HTML 使用现有 TinyMCE 组件编辑,并通过服务端 HTMLPurifier 清理。来源 Content 独立保存,接口正文为空时不会伪造原文。
|
|
|
- 原文抓取仅预留字段,本期不自动访问来源链接抓取正文。
|
|
|
- 新闻列表日期筛选依据原文 PublishTime,按应用时区保存与筛选,结束日期包含当天全部时间。
|
|
|
- 单条新闻字段错误会记录序号与错误,其余正常条目继续保存;该企业任务记失败,以便核查和重试。成功/失败按企业计数,新增加总实际插入行数。
|
|
|
|
|
|
## 分类筛选与自动留存
|
|
|
|
|
|
- 分类下拉列出所有已入库的分类,按分类代码去重并统计全部新闻数量,同一新闻在同一分类只计一次;支持输入搜索、多选,命中任意选项即可。
|
|
|
- 页面默认显示已留存,状态只有全部、已留存、不留存。
|
|
|
- “分类留存设置”保存到 company_news_settings。未选择分类时全部不留存;无分类或新出现但未选中的分类不留存。
|
|
|
- 新建批次将设置保存到 company_news_batches.retained_categories,中途改设置仅影响后续批次。
|
|
|
- 新采集和重复采集的自动处理记录命中任意已选分类即留存,其他不留存;reviewed_at 非空表示已人工决定,重复采集保留人工决定。
|
|
|
- 迁移将历史待审核新闻转为不留存,后续采集再应用设置;已有人工留存结果不变。设置保存不回溯全量历史新闻。
|
|
|
|
|
|
## 企业下拉查询
|
|
|
|
|
|
公司筛选仅返回企业户中 selected、occupied 的企业,不返回 unknown 或未纳入的企业。接口按企业名称、ID 排序,每页 30 家;前端滚动触底加载下一页,输入关键词时从第一页重新搜索。“获取更新”采集全部候选和已占额企业,不受当前公司筛选或下拉分页影响。
|
|
|
|
|
|
## 元禾接口配置
|
|
|
|
|
|
后端 `.env` 必须设置 YUANHE_BASE_URL、YUANHE_CUSTOMERID、YUANHE_AUTHKEY,值使用当前授权的元禾配置。密钥不得提交到版本控制。项目通过 config/yuanhe.php 读取,因此使用配置缓存的环境更新后需重新生成配置缓存,并重启采集进程。
|
|
|
|
|
|
创建批次和启动消费者前会校验配置,缺少配置时直接说明缺少的键名,不会创建一批注定失败的任务;页面顶部也会显示配置错误。
|
|
|
|
|
|
## 上线步骤
|
|
|
|
|
|
部署本次 PHP 源码后,在项目根目录执行以下迁移(不包含工作区其他功能的迁移):
|
|
|
|
|
|
```bash
|
|
|
php artisan migrate --path=database/migrations/2026_10_09_100000_create_company_news_tables.php --force
|
|
|
php artisan migrate --path=database/migrations/2026_10_09_100001_add_company_news_menu.php --force
|
|
|
php artisan migrate --path=database/migrations/2026_10_12_100000_add_company_news_category_settings.php --force
|
|
|
php artisan migrate --path=database/migrations/2026_10_12_110000_add_company_news_auto_collection.php --force
|
|
|
```
|
|
|
|
|
|
新增五张表:company_news、company_news_logs、company_news_batches、company_news_tasks、company_news_control。
|
|
|
|
|
|
菜单迁移创建“企业新闻舆情”,页面地址 `/companyNews/index`,API 权限前缀 `api/admin/company-news/`。继承现有 `/qccEnterpriseAccount/index` 菜单的角色及直接授权;若旧菜单不存在或未给用户分配权限,请在角色管理中分配新菜单。新接口同时经过管理员登录与 RBAC 权限检查。重新登录加载新菜单。
|
|
|
|
|
|
在前端项目构建并将产物部署到站点 `public/admin`:
|
|
|
|
|
|
```bash
|
|
|
npm run build:prod -- --dest dist
|
|
|
```
|
|
|
|
|
|
后端要使用与现有项目一致的 PHP CLI 和 composer.lock 依赖(HTMLPurifier 已包含在当前锁定的生产依赖中)。更新了路由缓存的环境需重新生成或清理路由缓存。
|
|
|
|
|
|
## 调度执行
|
|
|
|
|
|
本地使用 `php artisan serve` 启动 HTTP 服务并不会启动后台调度。`company-news/latest` 只读查询进度,轮询它不会执行企业请求。需在另一个终端保持以下专用消费者运行:
|
|
|
|
|
|
```bash
|
|
|
php artisan qcc:collect-news --watch
|
|
|
```
|
|
|
|
|
|
此模式每组完成后等待 5 秒,检查每日计划并处理下一组,只运行新闻采集,不触发其他短信、邮件等调度任务。重启电脑、停止终端或更改元禾配置后需重启该进程。生产环境可使用宝塔守护进程/Supervisor 管理该命令,或使用下面已有的 Laravel scheduler 方式。
|
|
|
|
|
|
|
|
|
复用服务器已有 Laravel scheduler,每分钟执行 `php artisan schedule:run`。无需新增 Redis 或修改全站 QUEUE_CONNECTION;任务和锁存储在数据库中。
|
|
|
|
|
|
本次 Kernel 增加 `qcc:collect-news`,每分钟在后台启动消费者。它检查自动设置并处理当前批次中的一组;每天开始时间到达后,当天最多自动创建一次批次。没有常驻进程时,每组完成后由下一分钟调度继续。页面点击后通常在下一分钟开始;关闭页面不影响执行。
|
|
|
|
|
|
如果服务器尚未设置 Laravel scheduler,可在宝塔计划任务配置每分钟执行,PHP 路径使用服务器实际安装的 CLI 路径:
|
|
|
|
|
|
```bash
|
|
|
cd /www/wwwroot/wx.sstbc.com && php artisan schedule:run
|
|
|
```
|
|
|
|
|
|
也可以手工消费已经创建的批次:
|
|
|
|
|
|
```bash
|
|
|
php artisan qcc:collect-news
|
|
|
```
|
|
|
|
|
|
限制本次消费者处理量:
|
|
|
|
|
|
```bash
|
|
|
php artisan qcc:collect-news --limit=20
|
|
|
```
|
|
|
|
|
|
命令会检查自动采集设置,单次最多完成一组;--limit 可进一步限制本轮处理量,不会超过批次每组企业数。没有到期计划或待处理任务时退出。1000 家顺序执行,每组 10 家为 100 组;每家公司返回 0 条或 900 多条新闻都算 1 个企业任务,新闻不截断,去重及分类规则与手动获取一致。
|
|
|
|
|
|
## 每日自动采集设置
|
|
|
|
|
|
页面“自动获取更新设置”提供开关、每天 HH:mm 开始时间、每组 1~100 家。默认关闭,预设 22:00、10 家,时区 Asia/Shanghai。先配置守护进程或 scheduler,再启用。
|
|
|
|
|
|
- 名单、每组数量、分类留存规则在创建批次时快照保存,中途修改只影响下次批次。
|
|
|
- scheduled_date 唯一键和控制行锁防止同一天创建重复自动批次。旧批次未结束时,新任务延期等待,不截断旧任务。
|
|
|
- 当天时间已过且尚未自动采集,会在下一次检查补建当天批次;不追补漏跑的历史日期。
|
|
|
- 关闭开关只停止新建自动批次,现有批次继续。手动批次不占用当天自动批次名额。
|
|
|
- watch 模式组间等待 5 秒;scheduler 模式组间等待下一次每分钟调度。失败可从页面单独重试。
|
|
|
- 单家新闻处理完成后再请求下一家,避免把 1000 家响应同时载入内存。耗时取决于响应及入库速度,不能保证 22:00 开始一定午夜前结束。
|
|
|
- 部署时等待活动批次结束,再停止旧进程、执行迁移、更新代码并重启。手动和自动获取均使用有数据即占额规则。
|
|
|
|
|
|
## 重复执行和故障恢复
|
|
|
|
|
|
- 创建批次和领取任务都锁定 company_news_control 的唯一控制行。同一时间仅允许一个活动批次,一个企业请求在执行;多实例 scheduler 可重复启动消费者,但不会重复领取活跃任务。
|
|
|
- 领取时生成 worker_token 和 5 分钟租约。外部请求在数据库事务外执行,结果提交时核验 token;过期 worker 不允许写入后续 worker 的任务结果。
|
|
|
- 进程崩溃后,下次消费者在租约到期时把中断任务记为失败,继续后续企业,不会自动重复外部请求。
|
|
|
- 网络超时不能证明元禾未收到请求。对方没有提供幂等协议时,无法保证外部调用严格只发生一次;“仅重试失败企业”由管理员明确启动新批次,并重新检查候选或已占额状态。
|
|
|
- 某个任务保存失败,其事务回滚后独立记失败;数据库整体不可用时命令退出,调度恢复后按租约继续。
|
|
|
- 页面显示“等待后台调度”长时间无进展:检查宝塔 scheduler、PHP CLI 版本、任务日志和 active_batch_id。不要通过删除控制行或随意清锁恢复,这可能破坏防重入机制。
|
|
|
- 不留存记录必须保留,否则后续采集又会变成新的记录。
|
|
|
|
|
|
## 验证命令
|
|
|
|
|
|
```bash
|
|
|
php vendor/bin/phpunit tests/Feature/CompanyNewsWorkflowTest.php tests/Unit/Services/QccCallServiceTest.php
|
|
|
```
|
|
|
|
|
|
前端项目:
|
|
|
|
|
|
```bash
|
|
|
npx vue-cli-service test:unit tests/unit/components/CompanyNews.spec.js --runInBand
|
|
|
```
|
|
|
|
|
|
后端测试使用 SQLite 内存数据库、模拟元禾响应,不访问业务数据库、不调用收费接口。上线人工验收:创建包含候选和已占额企业的小规模批次,查看进度、历史与失败明细;重复采集验证新闻不重复,编辑/留存后再采集验证人工数据保持。
|