招采订阅与推送说明
招采订阅与推送 — 流程、规则与操作说明
适用版本:本站当前实现(PushService、shop_dingyue招采订阅、by_tender_info招标表)
最后更新:2026-06-01
1. 功能概览
招采推送用于:当平台出现新的招标/采购信息时,按用户订阅条件(城市、标题关键词、通知渠道),向匹配用户发送短信、站内信、邮箱等通知。
| 模块 | 作用 |
|---|---|
| 用户招采订阅 | 用户配置「收哪些城市、哪些关键词、用什么渠道收」 |
| 招标数据 | 采集入库、商家发布、后台发布,写入 by_tender_info |
| 推送服务 | Banyan/Lib/Service/PushService.class.php 统一匹配订阅并发送 |
| 后台管理 | 招标列表、手动按渠道补推、订阅审核列表 |
2. 核心数据表
2.1 招采订阅 by_shop_dingyue
招采订阅通过固定字段区分(与生活信息、商家资讯订阅共用一张表):
| 字段 | 招采含义 |
|---|---|
channel_id | 固定 100(招采订阅) |
catlist | 固定 tender |
uid | 订阅用户 ID |
city_id | 订阅城市;0 = 不限城市(全国) |
province_id | 省份(选具体城市时有效) |
sitelist | 城市串,招采为 城市ID,0,0 或 0,0,0 |
tender_keywords | 标题关键词,逗号/中文逗号分隔;空 = 不限关键词 |
channel_sms / channel_weixin / channel_msg / channel_email | 各渠道开关(1 开启) |
audit | 1 已审核才参与推送;0 待审核不参与 |
status | ≥0 有效;-1 等视为无效 |
2.2 招标信息 by_tender_info
| 字段 | 说明 |
|---|---|
tender_id | 主键 |
type | 1=采集 2=商家发布 3=平台/后台发布 |
status | 1=已发布 才可推送;0 草稿;-1 删除 |
city_id / city_name | 地区(采集时尽力解析) |
title | 标题 |
content / content_summary | 正文与摘要(关键词匹配会查标题+摘要+正文) |
is_tuisong_sms 等 | 各渠道是否已推送及推送时间 |
3. 推送方式(两种)
flowchart TB
subgraph 数据来源
A1[采集入库 TenderCollectService]
A2[商家发布 Merchant/Distributors Tender]
A3[后台发布 Backstage Tenderinfo publish]
end
subgraph 自动推送
B[PushService::triggerAutoPush]
C[pushTender 四渠道: sms/weixin/msg/email]
end
subgraph 手动推送
D[后台 tenderinfo/index 点击推送链接]
E[pushTender 单渠道]
end
A1 --> B
A2 --> B
A3 --> B
B --> C
D --> E
3.1 自动推送(默认)
触发时机(招标 status=1 且入库/发布成功后):
| 场景 | 代码位置 |
|---|---|
| 采集新增一条 | TenderCollectService 入库成功后 triggerTenderAutoPush |
| 商家后台发布 | Merchant/TenderAction::create(status=1) |
| 公司手机后台发布 | Distributors/TenderAction::create(status=1) |
| 后台新建且已发布 | Backstage/TenderinfoAction::create |
| 后台点击「发布」 | Backstage/TenderinfoAction::publish(单条/批量) |
行为:
- 调用
PushService::autoPushTender($tender_id) - 一次尝试四个渠道:
sms、weixin、msg、email - 每个用户只走其订阅里勾选的渠道(见下文匹配规则)
- 无匹配订阅人:静默结束,写日志
[TenderAutoPush],不弹错给操作者 - 有匹配人但全部发送失败:记日志,不阻断采集/发布主流程
3.2 手动推送(运营补推)
入口:后台 → 招标信息管理 → 列表操作列
| 链接 | 方法 | 渠道 |
|---|---|---|
| 短信推送 | tenderinfo/sms | 仅短信 |
| 微信推送 | tenderinfo/weixin | 仅微信 |
| 站内信推送 | tenderinfo/msg | 仅站内信 |
| 邮箱推送 | tenderinfo/email | 仅邮箱 |
行为:
- 同样走
PushService::pushTender - 若无匹配订阅用户,页面报错:「没有匹配该城市/关键词的订阅用户」
- 推送成功会更新
is_tuisong_*标记与时间
4. 订阅匹配规则(推送前筛人)
实现类:PushService::buildTenderRecipients()
4.1 订阅记录前置条件
同时满足才进入候选列表:
channel_id = 100catlist = 'tender'status >= 0audit = 1(待审核订阅不会收到推送)- 对应
uid在users表存在
4.2 城市匹配
订阅 city_id | 招标 city_id | 是否匹配 |
|---|---|---|
| 0(不限城市) | 任意 | ✅ |
| 具体城市 A | 0(未识别) | ✅ |
| 具体城市 A | 同 ID A | ✅ |
| 滁州市 | 来安县等下属区县 | ✅(区县通过 area 表反查所属 city_id,归入地级市) |
| 滁州市 | 合肥等其他市 | ❌ |
说明:
- 订阅「不限城市」:接收全国招标(仍受关键词约束)。
- 招标未标城市:对所有已选具体城市的订阅都视为可匹配(与「不限城市」订阅一起参与关键词筛选)。
4.3 关键词匹配(标题 + 摘要 + 正文)
- 读取订阅字段
tender_keywords - 留空:不限制关键词,该订阅对所有通过城市筛选的招标均可收通知
- 有内容:将
,、、、;、;、空格等规范为英文逗号后拆分,任意一个词在招标title、content_summary、content任一字段中出现即匹配(不区分大小写)
示例:
- 关键词:
保安,物业,安保 - 标题:
滁州市某某保安服务项目招标公告→ ✅(含「保安」) - 标题:
某某项目采购公告、正文含「物业」 → ✅
4.4 渠道分配
通过城市+关键词后,total 计数 +1,再按本次推送请求的渠道列表与用户订阅开关分配:
| 渠道 | 订阅字段 | 额外条件 |
|---|---|---|
| 短信 | channel_sms=1 | 用户有手机号;短信额度等见 PushService 配额逻辑 |
| 微信 | channel_weixin=1 | 招采微信推送当前为占位,返回「未实现招采微信推送」 |
| 站内信 | channel_msg=1 | 写入 msg 表 |
| 邮箱 | channel_email=1 | 用户有邮箱 |
自动推送一次请求包含四渠道;用户只会收到自己勾选且满足条件的渠道。
5. 用户订阅 — 操作流程
5.1 手机端(Wap)
路径:我的 → 订阅设置 → 招采订阅(或 shopdingyue/settings,subscribe_type=tender)
- 所在城市:可选具体城市,或选 「不限城市(全国)」
- 标题关键词:如
保安, 物业, 安保;留空表示不限 - 通知方式:至少勾选一种(短信需先购买短信余额)
- 提交 → 写入/更新
shop_dingyue
控制器:Banyan/Lib/Action/User/ShopdingyueAction.class.php → handleTenderSubscribe()
5.2 电脑端商家会员(Members)
路径:商家中心 → 招采订阅(shopdingyue/base,subscribe_type=tender)
字段与规则与 Wap 相同。控制器:Members/ShopdingyueAction.class.php
5.3 订阅去重与更新
同一用户、同一 city_id、同一 tender_keywords 视为一条订阅;再次提交则更新渠道开关,不重复插多条。
5.4 订阅数量限制
若后台配置了 shop_dingyue_set.count_number,超过上限的新订阅可能 audit=0 待审核,审核通过前不会参与推送。
6. 后台运营 — 操作流程
6.1 招标信息管理
路径:后台 → 招标信息管理(Backstage/Tenderinfo)
- 列表查看采集/商家/平台招标
- 编辑、发布、下架、删除
- 手动推送:短信 / 微信 / 站内 / 邮箱(见 3.2)
- 已推送渠道会显示推送时间,避免重复点(仍可通过未推送渠道补推)
6.2 招采订阅审核列表
路径:后台 → 订阅相关 → 招标采购订阅列表(Shopdingyue/tenderlist)
- 筛选
channel_id=100且catlist=tender - 查看用户、城市(
city_id=0显示「不限城市」)、关键词、渠道、审核状态
6.3 采集任务
路径:后台 → 招标采集(Tendercollect)
- 配置采集源、手动/定时采集
- 每新增一条已发布招标 → 自动触发招采推送(无订阅匹配则仅写日志)
7. 招标数据来源与 city_id
| 来源 | type | 发布状态 | 自动推送 |
|---|---|---|---|
| 采集 | 1 | 入库时 status=1 | ✅ |
| 商家 | 2 | 创建时 status=1 | ✅ |
| 后台 | 3 | 创建/发布 status=1 | ✅ |
采集时 city_id 来源(TenderCollectService::deriveTenderFields):
- 接口字段
areaname/areacode映射 - 标题中提取「XX市/县/区」
- 与
city表名称精确匹配得到city_id
若解析为县/区 ID,推送匹配时会尝试归入对应地级市,以便「滁州市」订阅能收到下属区县招标。
8. 日志与排查
8.1 自动推送日志
- 关键字:
[TenderAutoPush] - 位置:ThinkPHP 日志(如
Runtime/Logs/) - 内容:
tender_id、成功与否、说明信息
8.2 手动推送失败常见原因
| 提示 | 原因 |
|---|---|
| 没有匹配该城市/关键词的订阅用户 | 无 audit=1 订阅通过城市+标题关键词筛选 |
| 仅已发布的招标信息可推送 | status≠1 |
| 推送失败:所有渠道均未成功发送 | 有匹配人但短信/邮箱等发送失败 |
| 微信相关 | 招采微信渠道未实现,可改用站内/邮箱/短信 |
8.3 排查清单(以用户「滁州 + 保安,物业,安保」为例)
- 后台
tenderlist确认该用户订阅audit=1,渠道已勾选 - 打开待推招标,看
city_id是否为滁州或下属县(或 0) - 看招标 标题/摘要/正文 是否包含保安/物业/安保之一
- 站内信/邮箱:用户资料是否完整
- 短信:是否购买短信、余额是否足够
9. 相关代码文件索引
| 文件 | 说明 |
|---|---|
Banyan/Lib/Service/PushService.class.php | 招采推送核心:匹配、发送、自动推送入口 |
Banyan/Lib/Service/TenderCollectService.class.php | 采集入库 + 自动推送触发 |
Banyan/Lib/Action/User/ShopdingyueAction.class.php | Wap 招采订阅 |
Banyan/Lib/Action/Members/ShopdingyueAction.class.php | PC 商家招采订阅 |
Banyan/Lib/Action/Backstage/TenderinfoAction.class.php | 后台招标 + 手动推送 + 发布自动推送 |
Banyan/Lib/Action/Merchant/TenderAction.class.php | 商家招采发布 + 自动推送 |
Banyan/Lib/Action/Distributors/TenderAction.class.php | 公司手机后台招采发布 + 自动推送 |
Banyan/Lib/Action/Backstage/ShopdingyueAction.class.php | 招采订阅审核列表 |
themes/default/User/shopdingyue/settings.html | Wap 招采订阅表单 |
themes/default/Members/shopdingyue/base.html | PC 招采订阅表单 |
themes/default/Backstage/tenderinfo/index.html | 手动推送入口 |
10. 规则速查表
| 项目 | 规则 |
|---|---|
| 谁可收推送 | shop_dingyue 招采订阅且 audit=1 |
| 城市 | city_id=0 全国;否则市/县匹配及区县归市 |
| 关键词 | 标题+摘要+正文;空=不限 |
| 何时自动推 | 采集新增、商家发布、后台发布成功 |
| 何时手动推 | 后台列表点渠道链接 |
| 微信招采 | 暂未实现 |
| 重复推送 | 自动推送不查历史;手动推送会更新 is_tuisong_* 标记 |
11. 修订记录
| 日期 | 说明 |
|---|---|
| 2026-06-01 | 增加自动推送;城市可选全国、区县归市;关键词匹配标题+摘要+正文;后台 fw 页增加说明 |
