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