You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

7.4 KiB

企业新闻舆情部署与维护

后台页面 /companyNews/index 支持新闻采集、公司和发布日期筛选、人工正文编辑、留存审核及批次记录。当前没有对外展示接口。所有新闻默认 is_visible=false,保存与审核也不会开启展示。

业务规则

  • 点击“获取更新”创建一个全量候选和已占额企业快照,不受新闻列表筛选和分页影响;仅 company_qcc_accounts.status 为 selected 或 occupied 的企业进入批次,按公司 ID 顺序执行。
  • 执行前再次检查企业是否仍为候选或已占额;不符合的任务记录失败/跳过,不调用元禾。
  • 单次请求使用元禾 enterprise/packInfo,连接超时 10 秒、总超时 60 秒;只有外层 code=200 且 data.news 是数组或 null 才进行处理。null/空数组记成功、0 新增;缺字段、非数组记失败。
  • 本功能直接调用 Repository,不调用 QccCallService;成功、失败、超时均不更新企业户状态。原来的 qcc:verify-pack-info 诊断命令仍有状态流转,不能用它代替本功能。
  • 按企业、数据提供方及去重键建立唯一约束。去重键按 NewsId、Id、原文 URL 的优先级生成 SHA-256;来源标识必须跨次稳定。同一新闻关联不同公司分别保存。
  • 重复采集只更新来源字段、原始 JSON 与最近采集信息,不覆盖标题、人工正文、审核结论或展示状态。不留存是逻辑状态,不删除记录。
  • 原文 HTML 使用现有 TinyMCE 组件编辑,并通过服务端 HTMLPurifier 清理。来源 Content 独立保存,接口正文为空时不会伪造原文。
  • 原文抓取仅预留字段,本期不自动访问来源链接抓取正文。
  • 新闻列表日期筛选依据原文 PublishTime,按应用时区保存与筛选,结束日期包含当天全部时间。
  • 单条新闻字段错误会记录序号与错误,其余正常条目继续保存;该企业任务记失败,以便核查和重试。成功/失败按企业计数,新增加总实际插入行数。

企业下拉查询

公司筛选仅返回企业户中 selected、occupied 的企业,不返回 unknown 或未纳入的企业。接口按企业名称、ID 排序,每页 30 家;前端滚动触底加载下一页,输入关键词时从第一页重新搜索。“获取更新”采集全部候选和已占额企业,不受当前公司筛选或下拉分页影响。

元禾接口配置

后端 .env 必须设置 YUANHE_BASE_URL、YUANHE_CUSTOMERID、YUANHE_AUTHKEY,值使用当前授权的元禾配置。密钥不得提交到版本控制。项目通过 config/yuanhe.php 读取,因此使用配置缓存的环境更新后需重新生成配置缓存,并重启采集进程。

创建批次和启动消费者前会校验配置,缺少配置时直接说明缺少的键名,不会创建一批注定失败的任务;页面顶部也会显示配置错误。

上线步骤

部署本次 PHP 源码后,在项目根目录执行这两条迁移(不包含工作区其他功能的迁移):

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

新增五张表: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:

npm run build:prod -- --dest dist

后端要使用与现有项目一致的 PHP CLI 和 composer.lock 依赖(HTMLPurifier 已包含在当前锁定的生产依赖中)。更新了路由缓存的环境需重新生成或清理路由缓存。

调度执行

本地使用 php artisan serve 启动 HTTP 服务并不会启动后台调度。company-news/latest 只读查询进度,轮询它不会执行企业请求。需在另一个终端保持以下专用消费者运行:

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 路径:

cd /www/wwwroot/wx.sstbc.com && php artisan schedule:run

也可以手工消费已经创建的批次:

php artisan qcc:collect-news

限制本次消费者处理量:

php artisan qcc:collect-news --limit=20

命令不创建批次、不改企业户状态;没有任务时立即退出。1000 家为顺序执行,实际耗时取决于元禾响应时间,页面持续展示进度,不承诺 10 分钟完成。

重复执行和故障恢复

  • 创建批次和领取任务都锁定 company_news_control 的唯一控制行。同一时间仅允许一个活动批次,一个企业请求在执行;多实例 scheduler 可重复启动消费者,但不会重复领取活跃任务。
  • 领取时生成 worker_token 和 5 分钟租约。外部请求在数据库事务外执行,结果提交时核验 token;过期 worker 不允许写入后续 worker 的任务结果。
  • 进程崩溃后,下次消费者在租约到期时把中断任务记为失败,继续后续企业,不会自动重复外部请求。
  • 网络超时不能证明元禾未收到请求。对方没有提供幂等协议时,无法保证外部调用严格只发生一次;“仅重试失败企业”由管理员明确启动新批次,并重新检查候选或已占额状态。
  • 某个任务保存失败,其事务回滚后独立记失败;数据库整体不可用时命令退出,调度恢复后按租约继续。
  • 页面显示“等待后台调度”长时间无进展:检查宝塔 scheduler、PHP CLI 版本、任务日志和 active_batch_id。不要通过删除控制行或随意清锁恢复,这可能破坏防重入机制。
  • 不留存记录必须保留,否则后续采集又会变成新的待审核记录。

验证命令

php vendor/bin/phpunit tests/Feature/CompanyNewsWorkflowTest.php tests/Unit/Services/QccCallServiceTest.php

前端项目:

npx vue-cli-service test:unit tests/unit/components/CompanyNews.spec.js --runInBand

后端测试使用 SQLite 内存数据库、模拟元禾响应,不访问业务数据库、不调用收费接口。上线人工验收:创建包含候选和已占额企业的小规模批次,查看进度、历史与失败明细;重复采集验证新闻不重复,编辑/留存后再采集验证人工数据保持。