feat: 扩展关键词完成 68 条空品类线索分类 (100% 覆盖率)
- scripts/expand_keywords.js: 扩展 15 品类关键词至 27 个,匹配 58 条 - scripts/assign_remaining.js: 10 条剩余线索合理分配 (消毒剂→涂料, 重水→仪表, 光伏支架→钢材等) - scripts/CATEGORY_CLEANUP.md: AdminPage 品类清理建议 (保留核心 15 + 7 个扩展, 合并/删除 24 个 AI 新建细分类) 最终状态: 761 条线索 100% 已分配品类, 42 个品类 (15 主类目 + 27 扩展/AI 新建)
This commit is contained in:
parent
a9ef6e952f
commit
4896453dfb
98
scripts/CATEGORY_CLEANUP.md
Normal file
98
scripts/CATEGORY_CLEANUP.md
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
# 品类清理建议
|
||||||
|
|
||||||
|
**生成时间**: 2026-07-01
|
||||||
|
**当前状态**: 761 条线索 100% 已分配品类,42 个品类(15 个初始 + 27 个扩展/AI 新建)
|
||||||
|
|
||||||
|
## ✅ 保留的初始 15 品类(主类目)
|
||||||
|
|
||||||
|
| 品类 | 线索数 | 说明 |
|
||||||
|
|------|--------|------|
|
||||||
|
| 钢材 | 206 | 最常见品类 |
|
||||||
|
| 管材 | 79 | |
|
||||||
|
| 混凝土 | 75 | |
|
||||||
|
| 电线电缆 | 65 | |
|
||||||
|
| 仪表 | 57 | |
|
||||||
|
| 电机 | 35 | |
|
||||||
|
| 风机 | 33 | |
|
||||||
|
| 阀门 | 25 | |
|
||||||
|
| 配电箱 | 19 | |
|
||||||
|
| 变压器 | 17 | |
|
||||||
|
| 开关 | 16 | |
|
||||||
|
| 涂料 | 15 | 含新增的消毒剂/引气剂/化学药剂 |
|
||||||
|
| 水泵 | 11 | |
|
||||||
|
| 灯具 | 9 | |
|
||||||
|
| 防火门 | 7 | |
|
||||||
|
|
||||||
|
## 🆕 扩展品类(建议保留,单条 ≥ 5)
|
||||||
|
|
||||||
|
| 品类 | 线索数 | 关键词 | 建议 |
|
||||||
|
|------|--------|--------|------|
|
||||||
|
| 设备配件 | 11 | 配件/配套设备/螺栓/拉伸机/设备 | ✅ 保留 |
|
||||||
|
| 工程服务 | 9 | 服务/劳务/可行性研究/运维 | ✅ 保留 |
|
||||||
|
| 杂品 | 8 | 零星/小五金/棉被/三防布/化学药剂 | ⚠️ 保留但加备注"杂项" |
|
||||||
|
| 防火门 | 7 | 防火门/防火卷帘/防火窗 | ✅ 主类目已存在 |
|
||||||
|
| 标识标牌 | 6 | 标识/标牌/广告/安全标准化 | ✅ 保留(采购高频) |
|
||||||
|
| 施工设备 | 6 | 盾构/钻爆/登高车/平台/备品备件 | ⚠️ 可合并到"设备配件" |
|
||||||
|
| 油料 | 5 | 柴油/汽油/润滑油 | ✅ 保留 |
|
||||||
|
| 网络设备 | 4 | 网络/服务器/交换机 | ⚠️ 可合并到"设备配件" |
|
||||||
|
| 电梯 | 3 | 电梯/扶梯/升降机 | ⚠️ 可合并到"钢材"或"杂品" |
|
||||||
|
|
||||||
|
## 🗑️ AI 新建但建议删除/合并的细分类(≤ 2 条)
|
||||||
|
|
||||||
|
| 品类 | 线索数 | 关键词 | 建议动作 |
|
||||||
|
|------|--------|--------|----------|
|
||||||
|
| 环保设备 | 3 | 空气净化/净化设备 | ⚠️ 可删除,归"杂品" |
|
||||||
|
| 消防器材 | 3 | 消防/灭火 | ⚠️ 可合并到"防火门"或独立保留 |
|
||||||
|
| 石材 | 2 | 石材/大理石/花岗岩/块石 | ⚠️ 可合并到"管材"或"杂品" |
|
||||||
|
| 生活物资 | 2 | 生活物资 | ⚠️ 删除,归"杂品" |
|
||||||
|
| 服务器 | 2 | 服务器 | ⚠️ 合并到"网络设备" |
|
||||||
|
| 土工材料 | 2 | 土工布/防水板 | ⚠️ 可保留(独立品类) |
|
||||||
|
| 劳保用品 | 2 | 劳保 | ⚠️ 合并到"杂品" |
|
||||||
|
| 集成房屋 | 1 | 集成房屋 | 🗑️ 删除,归"杂品" |
|
||||||
|
| 钻探设备 | 1 | 钻探 | 🗑️ 删除,归"施工设备"→"设备配件" |
|
||||||
|
| 绳索 | 1 | 绳索 | 🗑️ 删除,归"杂品" |
|
||||||
|
| 石英石 | 1 | 石英石 | 🗑️ 删除,归"石材"→"管材" |
|
||||||
|
| 盾构刀具 | 1 | 盾构刀具 | 🗑️ 删除,归"施工设备"→"设备配件" |
|
||||||
|
| 洒水车 | 1 | 洒水车 | 🗑️ 删除,归"杂品" |
|
||||||
|
| 法兰螺栓 | 1 | 法兰/螺栓 | 🗑️ 删除,归"钢材"或"设备配件" |
|
||||||
|
| 油缸 | 1 | 油缸 | 🗑️ 删除,归"设备配件" |
|
||||||
|
| 柴油 | 1 | 柴油 | ⚠️ 合并到"油料" |
|
||||||
|
| 机柜 | 1 | 机柜 | 🗑️ 删除,归"配电箱" |
|
||||||
|
| 无功补偿装置 | 1 | 无功补偿 | 🗑️ 删除,归"配电箱" |
|
||||||
|
| 数据服务 | 1 | 数据服务 | 🗑️ 删除,归"工程服务" |
|
||||||
|
| 搬运设备 | 1 | 搬运 | 🗑️ 删除,归"施工设备"→"设备配件" |
|
||||||
|
| 挖掘机 | 1 | 挖掘机 | 🗑️ 删除,归"施工设备"→"设备配件" |
|
||||||
|
| 建筑材料 | 1 | 建筑材料 | 🗑️ 删除,归"钢材"或"杂品" |
|
||||||
|
| 广告材料 | 1 | 广告 | 🗑️ 删除,归"标识标牌" |
|
||||||
|
| 工具 | 1 | 工具 | 🗑️ 删除,归"杂品"或"设备配件" |
|
||||||
|
| 审计服务 | 1 | 审计 | 🗑️ 删除,归"工程服务" |
|
||||||
|
|
||||||
|
## 📊 建议的最终品类清单(约 20-22 个)
|
||||||
|
|
||||||
|
### 核心主类目(15 个,初始 + 微调)
|
||||||
|
钢材 / 管材 / 混凝土 / 电线电缆 / 仪表 / 电机 / 风机 / 阀门 / 配电箱 / 变压器 / 开关 / 涂料 / 水泵 / 灯具 / 防火门
|
||||||
|
|
||||||
|
### 推荐新增(5-7 个,按需 review)
|
||||||
|
- **设备配件** ✅(含螺栓/法兰/搬运/挖掘机/钻探设备/机柜/无功补偿装置 等)
|
||||||
|
- **工程服务** ✅(含服务/劳务/数据/审计 等)
|
||||||
|
- **杂品** ✅(含小五金/棉被/化学药剂/洒水车/集成房屋/工具 等)
|
||||||
|
- **标识标牌** ✅(含广告/标牌/安全标准化)
|
||||||
|
- **油料** ✅(含柴油/汽油/润滑油)
|
||||||
|
- **电梯** ⚠️ 可合并到"钢材"或独立保留
|
||||||
|
- **施工设备** ⚠️ 可合并到"设备配件"或独立保留
|
||||||
|
- **网络设备** ⚠️ 可合并到"设备配件"或独立保留
|
||||||
|
- **消防器材** ⚠️ 可合并到"防火门"或独立保留
|
||||||
|
- **土工材料** ⚠️ 可合并到"管材"或独立保留
|
||||||
|
|
||||||
|
## 操作建议
|
||||||
|
|
||||||
|
1. 进入 AdminPage → 品类管理
|
||||||
|
2. 删除「AI 自动创建」的 1-2 条细分类(钻探设备、绳索、石英石、盾构刀具、洒水车、法兰螺栓、油缸、机柜、无功补偿装置 等单条品类)
|
||||||
|
3. 合并「相似品类」(柴油→油料,挖掘机→设备配件,工具→杂品,集成房屋→杂品 等)
|
||||||
|
4. 保留「高频品类」(钢材、管材、混凝土 等 15 主类目 + 设备配件/工程服务/标识标牌/油料 等扩展)
|
||||||
|
|
||||||
|
## 后续维护
|
||||||
|
|
||||||
|
- **新增采集场景**:编辑 `ProductCategory.keywords`(AdminPage)
|
||||||
|
- **新数据采集**:采集器入库时自动走关键词匹配(CrawlerService.batchInfer 已接入)
|
||||||
|
- **AI 新建品类**:自动标记 `isAIGenerated=true`,人工 review 后可手动改为 false
|
||||||
47
scripts/assign_remaining.js
Normal file
47
scripts/assign_remaining.js
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
const { PrismaClient } = require('@prisma/client')
|
||||||
|
const prisma = new PrismaClient()
|
||||||
|
|
||||||
|
const ASSIGNMENTS = [
|
||||||
|
{ keyword: '消毒剂', category: '涂料' },
|
||||||
|
{ keyword: '重水供应', category: '仪表' },
|
||||||
|
{ keyword: '拦污网', category: '杂品' },
|
||||||
|
{ keyword: '110kV变电站施工材料', category: '电线电缆' },
|
||||||
|
{ keyword: '线路金具', category: '电线电缆' },
|
||||||
|
{ keyword: '弱电材料', category: '电线电缆' },
|
||||||
|
{ keyword: '弱电智能化', category: '电线电缆' },
|
||||||
|
{ keyword: '引气剂', category: '涂料' },
|
||||||
|
{ keyword: '光伏支架', category: '钢材' },
|
||||||
|
{ keyword: '淋浴隔断', category: '杂品' },
|
||||||
|
]
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
const emptyLeads = await prisma.lead.findMany({
|
||||||
|
where: { OR: [{ category: null }, { category: '' }] },
|
||||||
|
select: { id: true, title: true },
|
||||||
|
})
|
||||||
|
console.log(`剩余空品类线索: ${emptyLeads.length} 条`)
|
||||||
|
|
||||||
|
let assigned = 0
|
||||||
|
for (const lead of emptyLeads) {
|
||||||
|
for (const a of ASSIGNMENTS) {
|
||||||
|
if (lead.title.includes(a.keyword)) {
|
||||||
|
await prisma.lead.update({ where: { id: lead.id }, data: { category: a.category } })
|
||||||
|
assigned++
|
||||||
|
console.log(`✓ ${lead.title.slice(0, 60)} → ${a.category}`)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const stillEmpty = await prisma.lead.count({
|
||||||
|
where: { OR: [{ category: null }, { category: '' }] },
|
||||||
|
})
|
||||||
|
console.log(`\n已分配: ${assigned}, 剩余空品类: ${stillEmpty}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
main()
|
||||||
|
.catch((e) => {
|
||||||
|
console.error(e)
|
||||||
|
process.exit(1)
|
||||||
|
})
|
||||||
|
.finally(() => prisma.$disconnect())
|
||||||
86
scripts/expand_keywords.js
Normal file
86
scripts/expand_keywords.js
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
const { PrismaClient } = require('@prisma/client')
|
||||||
|
const prisma = new PrismaClient()
|
||||||
|
|
||||||
|
// 基础 15 品类 + 扩展关键词(覆盖剩余 68 条线索的常见标题模式)
|
||||||
|
const EXTENDED_CATEGORIES = {
|
||||||
|
'钢材': ['钢', '钢管', '钢板', '钢筋', '型材', '铜覆', '热镀锌', '塔筒', '金属', '圆线', '钢绞线', '角钢'],
|
||||||
|
'混凝土': ['混凝土', '商混', '商品砼', '水泥', '砂浆', '砼', '灌浆料'],
|
||||||
|
'阀门': ['阀门', '闸阀', '球阀', '蝶阀', '截止阀', '止回阀', '调节阀', '控制阀', '节流装置', '旁路'],
|
||||||
|
'管材': ['管材', '管道', 'PVC', 'PE管', '镀锌管', '无缝管', '螺旋管', '给水管', '铸铁管', '膨胀节', '地砖', '面砖'],
|
||||||
|
'仪表': ['仪表', '仪器', '传感器', '变送器', '流量计', '压力表', '温度计', '标准孔板', '文丘里', '监测', '监测站', '监测设备'],
|
||||||
|
'电机': ['电机', '电动机', '减速机', '减速器', '变频', '电气系统'],
|
||||||
|
'水泵': ['水泵', '离心泵', '潜污泵', '消防泵', '排污泵', '循环泵'],
|
||||||
|
'变压器': ['变压器', '箱变', '干式变', '油浸变', '电力变压器', '换流站'],
|
||||||
|
'电线电缆': ['电缆', '电线', '导线', '电力线', '控制电缆', '光缆', '架空线'],
|
||||||
|
'配电箱': ['配电箱', '配电柜', '配电盘'],
|
||||||
|
'开关': ['开关', '断路器', '接触器', '继电器', '熔断器', '隔离开关'],
|
||||||
|
'风机': ['风机', '通风机', '排风机', '轴流风机', '离心风机'],
|
||||||
|
'灯具': ['灯具', '照明灯', '路灯', '防爆灯', 'LED灯', '太阳能灯'],
|
||||||
|
'涂料': ['涂料', '油漆', '防腐漆', '防火涂料', '乳胶漆', '防水涂料', '化学药剂', '聚氯化铝'],
|
||||||
|
'防火门': ['防火门', '防火卷帘', '防火窗', '钢质防火', '木质防火'],
|
||||||
|
'标识标牌': ['标识', '标牌', '广告', '安全标准化'],
|
||||||
|
'电梯': ['电梯', '扶梯', '升降机'],
|
||||||
|
'油料': ['柴油', '汽油', '润滑油', '油料'],
|
||||||
|
'环保设备': ['空气净化', '净化设备', '除尘', '废气'],
|
||||||
|
'网络设备': ['网络', '服务器', '计算机', '交换机', 'SVDU', '工作站'],
|
||||||
|
'消防器材': ['消防', '灭火', '阻燃', '防火材料'],
|
||||||
|
'施工设备': ['盾构', '钻爆', '登高车', '平台', '备品备件', '台车'],
|
||||||
|
'石材': ['石材', '大理石', '花岗岩', '块石', '戈壁料'],
|
||||||
|
'土工材料': ['土工布', '两布一膜', '土工膜', '防水板', '隔墙板'],
|
||||||
|
'杂品': ['零星', '小五金', '棉被', '三防布', '安全培训', '化学药剂', '介质'],
|
||||||
|
'工程服务': ['服务', '劳务', '可行性研究', '地质', '技术测评', '运维', '管理', '设计', '评估'],
|
||||||
|
'设备配件': ['配件', '配套设备', '螺栓', '拉伸机', '设备', '汽机', '氢气储存', '压力容器', '生产线'],
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
const emptyLeads = await prisma.lead.findMany({
|
||||||
|
where: { OR: [{ category: null }, { category: '' }] },
|
||||||
|
select: { id: true, title: true },
|
||||||
|
})
|
||||||
|
console.log(`空品类线索: ${emptyLeads.length} 条`)
|
||||||
|
|
||||||
|
let filled = 0
|
||||||
|
const remaining = []
|
||||||
|
for (const lead of emptyLeads) {
|
||||||
|
let matched = null
|
||||||
|
for (const [cat, keywords] of Object.entries(EXTENDED_CATEGORIES)) {
|
||||||
|
if (keywords.some((kw) => lead.title.includes(kw))) {
|
||||||
|
matched = cat
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (matched) {
|
||||||
|
await prisma.lead.update({ where: { id: lead.id }, data: { category: matched } })
|
||||||
|
filled++
|
||||||
|
} else {
|
||||||
|
remaining.push(lead)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`扩展关键词匹配: ${filled} 条`)
|
||||||
|
console.log(`仍需人工: ${remaining.length} 条`)
|
||||||
|
if (remaining.length > 0) {
|
||||||
|
console.log('\n--- 需要人工判断的标题 ---')
|
||||||
|
remaining.forEach((l, i) => console.log(`${i + 1}. ${l.title}`))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增扩展品类到 ProductCategory 表
|
||||||
|
const newCats = Object.keys(EXTENDED_CATEGORIES).filter(
|
||||||
|
(c) => !['防火门', '配电箱', '电线电缆', '阀门', '钢材', '涂料', '混凝土', '变压器', '水泵', '风机', '管材', '开关', '灯具', '仪表', '电机'].includes(c),
|
||||||
|
)
|
||||||
|
for (const catName of newCats) {
|
||||||
|
await prisma.productCategory.upsert({
|
||||||
|
where: { name: catName },
|
||||||
|
update: { keywords: JSON.stringify(EXTENDED_CATEGORIES[catName]) },
|
||||||
|
create: { name: catName, keywords: JSON.stringify(EXTENDED_CATEGORIES[catName]) },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
console.log(`\n已新增/更新 ${newCats.length} 个扩展品类到 ProductCategory`)
|
||||||
|
}
|
||||||
|
|
||||||
|
main()
|
||||||
|
.catch((e) => {
|
||||||
|
console.error(e)
|
||||||
|
process.exit(1)
|
||||||
|
})
|
||||||
|
.finally(() => prisma.$disconnect())
|
||||||
Loading…
Reference in New Issue
Block a user