# 药店采购比价系统 - 前端集成指南(爬虫平台功能) > **⚠️ MVP 阶段说明** > 本项目为**全新项目,尚未上线**,当前处于 **MVP(最小可行产品)阶段**。 > **核心业务**:药店采购比价系统,爬取各医药B2B平台药品价格,生成采购链接。 > **目标用户**:药店采购人员、药师,目标日活20万+。 > > 📅 版本:v1.0-MVP | 📆 更新日期:2026-06-16 | ✅ 状态:开发中 --- ## 📋 目录 - [功能概述](#功能概述) - [快速开始](#快速开始) - [接口详解](#接口详解) - [前端实现示例](#前端实现示例) - [数据流说明](#数据流说明) - [注意事项](#注意事项) - [常见问题](#常见问题) --- ## 🎯 功能概述 ### 核心价值 本次更新为爬虫系统添加了**全渠道医药平台区分**功能,支持记录每次爬取的采购平台来源: | 价值点 | 说明 | 收益 | |--------|------|------| | 🎯 最优价格采购 | 覆盖34个主流医药渠道 | 降低采购成本10%-30% | | 🔗 一键跳转购买 | 生成各平台采购链接 | 提升转化率50%+ | | 📊 跨平台对比 | 实时价格对比分析 | 提高决策效率 | | 👥 邀请裂变 | 通过分享获得额外配额 | 提升用户注册率和日活 | ### 平台分类 ``` 🏭 B2B批发平台(14个)- 药店主要采购渠道 ├─ 核心平台:药师帮、药京采、健之佳 └─ 扩展平台:1药网、康爱多、阿里健康等 🏪 B2C零售平台(8个)- 小批量采购/急单 ├─ O2O即时配送:叮当快药、美团买药 └─ 连锁药房:老百姓、大参林、益丰等 🛒 综合电商医药频道(7个)- 价格参考 └─ 京东健康、天猫医药、拼多多医药等 👨‍⚕️ 垂直医药平台(4个)- 特色渠道 └─ 微医、平安好医生、春雨医生等 ``` --- ## 🚀 快速开始 ### 1. MVP阶段核心流程 ```javascript // Step 1: 用户搜索药品 const drugName = '阿莫西林胶囊'; // Step 2: 选择比价平台(MVP推荐3个核心平台) const platforms = ['yaoshibang', 'yaobangmang', 'yiyaocheng']; // Step 3: 逐个平台爬取价格 for (const platform of platforms) { // 消耗配额 await consumeQuota(platform); // 爬取价格并生成采购链接 const priceData = await crawlPrice(drugName, platform); } // Step 4: 展示比价结果,点击跳转到对应平台购买 ``` ### 2. 关键代码片段 ```javascript // 消耗配额(必须传递platform参数) const consumeQuota = async (platform) => { const response = await fetch('/api/crawler/consume', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${accessToken}` }, body: JSON.stringify({ count: 1, useCouponFirst: false, platform: platform // ⚠️ 必填:平台代码 }) }); const result = await response.json(); if (result.code !== 200) { throw new Error(result.message || '配额不足'); } return result.data; }; ``` --- ## 📡 接口详解 ### 1. 消耗爬虫次数 ⭐⭐⭐ **接口信息:** - **URL**: `POST /api/crawler/consume` - **认证**: 需要 JWT Token - **用途**: 用户点击"比价"按钮时调用,扣减配额并记录平台 **请求参数:** | 字段 | 类型 | 必填 | 默认值 | 说明 | |------|------|------|--------|------| | count | Number | 是 | - | 消耗次数(通常传1) | | useCouponFirst | Boolean | 否 | false | 是否优先使用优惠券 | | platform | String | **是** | - | 平台代码(见下方枚举) | **MVP核心平台枚举:** ```javascript const MVP_PLATFORMS = { YAOSHBANG: 'yaoshibang', // 药师帮 - 国内最大医药B2B ⭐ YAOBANGMANG: 'yaobangmang', // 药帮忙 - 西南地区领先 ⭐ YIYAOCHENG: 'yiyaocheng' // 1药城 - 华中地区知名 ⭐ }; ``` **完整平台列表(Phase 2-6扩展):**
点击查看34个平台完整列表 ```javascript const ALL_PLATFORMS = { // B2B批发平台(14个) yaoshibang: '药师帮', yaojingcai: '药京采', jianzhijia: '健之佳', '1yao': '1药网', kangaiduoduo: '康爱多', alihealth: '阿里健康', sinopharm: '国药控股', jointown: '九州通', zhencheng: '珍诚医药', yaoyitong: '药易通', hezong: '合纵药易购', yicaotang: '宜草堂', yaodu: '药都在线', yaocaiying: '药材盈', // B2C零售平台(8个) dingdang: '叮当快药', quanyuantang: '泉源堂', dashenlin: '大参林', laobaixing: '老百姓大药房', yixintang: '一心堂', yifeng: '益丰大药房', haiwang: '海王星辰', guoda: '国大药房', // 综合电商医药频道(7个) jd_health: '京东健康', tmall_pharma: '天猫医药', pdd_pharma: '拼多多医药', douyin_pharma: '抖音医药', kuaishou_pharma: '快手医药', meituan_pharma: '美团买药', eleme_pharma: '饿了么医药', // 垂直医药平台(4个) weyi: '微医', pingan_good: '平安好医生', chunyu: '春雨医生', haodf: '好大夫在线' }; ```
**响应示例:** ```json { "code": 200, "message": "success", "data": true } ``` **错误响应:** ```json { "code": 400, "message": "每日配额已用完,请升级会员或等待明天", "data": null } ``` **前端调用示例:** ```javascript // ✅ 正确用法 async function startPriceComparison(drugName, platform) { try { // 1. 消耗配额 await fetch('/api/crawler/consume', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` }, body: JSON.stringify({ count: 1, platform: platform // 必须传递 }) }); // 2. 爬取价格(调用后端爬虫服务) const priceData = await crawlDrugPrice(drugName, platform); return priceData; } catch (error) { console.error('比价失败:', error); throw error; } } // ❌ 错误用法:不传platform参数 await fetch('/api/crawler/consume', { body: JSON.stringify({ count: 1 }) // 缺少platform字段! }); ``` --- ### 2. 获取爬虫使用记录 **接口信息:** - **URL**: `GET /api/crawler/logs?days=7` - **认证**: 需要 JWT Token - **用途**: 展示用户的历史爬取记录 **查询参数:** | 参数 | 类型 | 必填 | 默认值 | 说明 | |------|------|------|--------|------| | days | Number | 否 | 7 | 查询最近N天的记录 | **响应示例:** ```json { "code": 200, "message": "success", "data": [ { "id": 1, "userId": 1, "usageDate": "2026-06-16", "usageCount": 3, "platform": "yaoshibang", // ✅ 平台代码 "source": "QUOTA", // QUOTA:配额 COUPON:优惠券 "remark": null, "createTime": "2026-06-16T10:30:00" } ] } ``` **前端调用示例:** ```javascript async function fetchUsageLogs(days = 7) { const response = await fetch(`/api/crawler/logs?days=${days}`, { headers: { 'Authorization': `Bearer ${token}` } }); const result = await response.json(); return result.data || []; } ``` --- ### 3. 查看用户爬虫详情(运营后台专用) **接口信息:** - **URL**: `GET /api/admin/crawler/user-detail?userId=1` - **认证**: 需要管理员 JWT Token - **用途**: 运营后台查看用户的平台使用分布统计 **查询参数:** | 参数 | 类型 | 必填 | 说明 | |------|------|------|------| | userId | Long | 是 | 用户ID | **响应示例:** ```json { "code": 200, "message": "success", "data": { "userId": 1, "nickname": "测试用户", "phone": "138****8000", "todayTotalUsage": 8, "monthlyTotalUsage": 45, "platformStats": [ { "platformCode": "yaoshibang", "platformName": "药师帮", "todayUsage": 3, "monthlyUsage": 15, "totalUsage": 50 }, { "platformCode": "yaobangmang", "platformName": "药帮忙", "todayUsage": 2, "monthlyUsage": 10, "totalUsage": 30 } ] } } ``` --- ## 💻 前端实现示例 ### React组件:药品比价功能(MVP简化版) ```jsx import React, { useState } from 'react'; // MVP核心平台配置(3个) const PLATFORM_CONFIG = { yaoshibang: { name: '药师帮', icon: '💊', color: '#1890ff', description: '国内最大医药B2B平台' }, yaobangmang: { name: '药帮忙', icon: '🏥', color: '#52c41a', description: '西南地区领先平台' }, yiyaocheng: { name: '1药城', icon: '💉', color: '#faad14', description: '华中地区知名平台' } }; function DrugPriceComparison() { const [drugName, setDrugName] = useState(''); const [selectedPlatforms, setSelectedPlatforms] = useState(['yaoshibang']); const [isComparing, setIsComparing] = useState(false); const [priceResults, setPriceResults] = useState([]); const [quotaInfo, setQuotaInfo] = useState(null); // 启动比价 const startComparison = async () => { if (!drugName.trim()) { alert('请输入药品名称'); return; } if (selectedPlatforms.length === 0) { alert('请至少选择一个平台'); return; } try { setIsComparing(true); const results = []; // 逐个平台爬取价格 for (const platform of selectedPlatforms) { try { // 1. 消耗配额 await consumeQuota(platform); // 2. 爬取该平台药品价格 const priceData = await crawlDrugPrice(drugName, platform); results.push(priceData); } catch (error) { console.error(`${PLATFORM_CONFIG[platform].name}爬取失败:`, error); // 继续处理其他平台 } } // 按价格从低到高排序 results.sort((a, b) => a.price - b.price); setPriceResults(results); // 刷新配额状态 await refreshQuotaStatus(); } catch (error) { console.error('比价失败:', error); alert('操作失败,请重试'); } finally { setIsComparing(false); } }; // 消耗配额 const consumeQuota = async (platform) => { const response = await fetch('/api/crawler/consume', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${localStorage.getItem('accessToken')}` }, body: JSON.stringify({ count: 1, useCouponFirst: false, platform: platform // ✅ 必填 }) }); const result = await response.json(); if (result.code !== 200) { throw new Error(result.message || `${PLATFORM_CONFIG[platform].name}配额不足`); } return result.data; }; // 爬取药品价格(实际应调用后端爬虫服务) const crawlDrugPrice = async (drugName, platform) => { // TODO: 调用后端爬虫接口 // const response = await fetch(`/api/crawler/search?drug=${drugName}&platform=${platform}`); // 模拟数据 return { platform, drugName, price: Math.random() * 100 + 10, purchaseLink: `https://${platform}.com/buy?drug=${encodeURIComponent(drugName)}`, supplier: '某某医药公司', stock: '有货', specification: '0.25g*24粒' }; }; // 刷新配额状态 const refreshQuotaStatus = async () => { const response = await fetch('/api/crawler/status', { headers: { 'Authorization': `Bearer ${localStorage.getItem('accessToken')}` } }); const result = await response.json(); setQuotaInfo(result.data); }; return (

💊 药品采购比价

{/* 配额信息 */} {quotaInfo && (
今日剩余: {quotaInfo.dailyRemaining}/{quotaInfo.dailyLimit}次 本月剩余: {quotaInfo.monthlyRemaining}/{quotaInfo.monthlyLimit}次
)} {/* 药品搜索 */}
setDrugName(e.target.value)} onKeyPress={(e) => e.key === 'Enter' && startComparison()} />
{/* 平台选择 */}

选择比价平台:

{Object.entries(PLATFORM_CONFIG).map(([code, info]) => ( ))}
{/* 开始比价按钮 */} {/* 比价结果 */} {priceResults.length > 0 && (

比价结果(按价格从低到高)

{priceResults.map((result, index) => ( ))}
排名 平台 供应商 规格 价格 库存 操作
{index === 0 && 🏆 最低价} {index + 1} {PLATFORM_CONFIG[result.platform].icon} {PLATFORM_CONFIG[result.platform].name} {result.supplier} {result.specification} ¥{result.price.toFixed(2)} {result.stock} 🔗 去采购

💡 点击"去采购"将跳转到对应平台的购买页面

)}
); } export default DrugPriceComparison; ``` ### CSS样式(配套) ```css .drug-price-comparison { max-width: 1200px; margin: 0 auto; padding: 20px; } .quota-info { background: #f0f5ff; padding: 12px; border-radius: 8px; margin-bottom: 20px; display: flex; gap: 20px; font-size: 14px; } .search-box input { width: 100%; padding: 12px; font-size: 16px; border: 2px solid #d9d9d9; border-radius: 8px; margin-bottom: 20px; } .platform-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 12px; margin-top: 12px; } .platform-card { display: flex; align-items: center; padding: 12px; border: 2px solid #d9d9d9; border-radius: 8px; cursor: pointer; transition: all 0.3s; } .platform-card.selected { border-color: #1890ff; background: #f0f5ff; } .platform-card:hover { border-color: #1890ff; } .platform-info { display: flex; flex-direction: column; margin-left: 8px; } .platform-name { font-weight: bold; } .platform-desc { font-size: 12px; color: #999; } .compare-btn { width: 100%; padding: 14px; font-size: 16px; background: #1890ff; color: white; border: none; border-radius: 8px; cursor: pointer; margin: 20px 0; } .compare-btn:disabled { background: #d9d9d9; cursor: not-allowed; } .price-table { width: 100%; border-collapse: collapse; margin-top: 16px; } .price-table th, .price-table td { padding: 12px; text-align: left; border-bottom: 1px solid #f0f0f0; } .best-price { background: #fff7e6; } .badge { background: #ff4d4f; color: white; padding: 2px 8px; border-radius: 4px; font-size: 12px; } .platform-badge { display: inline-block; padding: 4px 12px; border-radius: 16px; color: white; font-size: 14px; } .price { color: #ff4d4f; font-weight: bold; font-size: 16px; } .buy-link { color: #1890ff; text-decoration: none; } .buy-link:hover { text-decoration: underline; } .tip { margin-top: 12px; color: #999; font-size: 14px; } ``` --- ## 📊 数据流说明 ### 完整业务流程 ``` ┌─────────────┐ │ 用户搜索药品 │ └──────┬──────┘ │ ▼ ┌──────────────────┐ │ 选择比价平台 │ ← MVP推荐3个核心平台 └──────┬───────────┘ │ ▼ ┌──────────────────────────┐ │ 循环每个平台: │ │ 1. 调用 /consume 接口 │ ← 扣减配额,记录platform │ 2. 调用爬虫服务 │ ← 爬取价格数据 │ 3. 生成采购链接 │ ← 返回购买URL └──────┬───────────────────┘ │ ▼ ┌──────────────────┐ │ 按价格排序展示 │ └──────┬───────────┘ │ ▼ ┌──────────────────┐ │ 用户点击购买 │ → 跳转到对应平台 └──────────────────┘ ``` ### 数据流转图 ``` 前端 后端 数据库 │ │ │ │ POST /consume │ │ │ {platform: 'yaoshibang'} │ │ ├─────────────────────────────>│ │ │ │ 验证配额 │ │ ├───────────────────────────>│ │ │ │ │ │ 扣减配额 │ │ │ INSERT usage_log │ │ │ (含platform字段) │ │ │ │ │ │<───────────────────────────┤ │ │ │ │ 返回成功 │ │ │<─────────────────────────────┤ │ │ │ │ │ GET /logs │ │ ├─────────────────────────────>│ │ │ │ 查询使用记录 │ │ ├───────────────────────────>│ │ │ SELECT * FROM usage_log │ │ │ WHERE platform IS NOT NULL │ │ │ │ │ 返回记录(含platform) │<───────────────────────────┤ │<─────────────────────────────┤ │ ``` --- ## ⚠️ 注意事项 ### 1. 必填参数 **❌ 错误做法:** ```javascript // 不传platform参数 await fetch('/api/crawler/consume', { body: JSON.stringify({ count: 1 }) }); ``` **✅ 正确做法:** ```javascript // 必须传递platform参数 await fetch('/api/crawler/consume', { body: JSON.stringify({ count: 1, platform: 'yaoshibang' // 必填 }) }); ``` ### 2. 平台代码规范 - 使用小写字母和下划线:`yaoshibang` ✅ - 不要使用中文:`药师帮` ❌ - 不要使用大写:`Yaoshibang` ❌ ### 3. 错误处理 ```javascript try { await consumeQuota(platform); } catch (error) { // 常见错误: // - 配额不足:code=400, message="每日配额已用完" // - 未登录:code=401, message="未认证" // - 参数错误:code=400, message="平台代码无效" if (error.message.includes('配额')) { // 引导用户升级会员或等待明天 showUpgradeModal(); } else { // 显示错误提示 showError(error.message); } } ``` ### 4. 性能优化 - **并发控制**:建议串行爬取,避免同时发起多个请求导致配额快速耗尽 - **缓存策略**:相同药品的比价结果可缓存5分钟,减少重复爬取 - **懒加载**:先展示部分平台结果,其余平台异步加载 ### 5. 向后兼容 - 历史数据的 `platform` 字段可能为 `null` - 前端需要做空值判断:`log.platform || '未知平台'` --- ## ❓ 常见问题 ### Q1: 为什么要传递platform参数? **A:** 用于记录用户主要在哪些平台进行比价,便于: - 运营分析用户偏好 - 优化爬虫资源分配 - 提供个性化推荐 ### Q2: 如果用户选择了多个平台,如何调用接口? **A:** 逐个平台调用,每次调用传递对应的platform参数: ```javascript for (const platform of selectedPlatforms) { await consumeQuota(platform); // 每次传不同的platform const priceData = await crawlPrice(drugName, platform); } ``` ### Q3: 平台代码从哪里获取? **A:** 建议使用常量定义: ```javascript // constants/platforms.js export const PLATFORM_CODES = { YAOSHBANG: 'yaoshibang', YAOBANGMANG: 'yaobangmang', YIYAOCHENG: 'yiyaocheng' }; // 使用时 import { PLATFORM_CODES } from '@/constants/platforms'; await consumeQuota(PLATFORM_CODES.YAOSHBANG); ``` ### Q4: 如何处理配额不足的情况? **A:** 捕获错误并引导用户: ```javascript try { await consumeQuota(platform); } catch (error) { if (error.message.includes('配额')) { // 显示升级弹窗 Modal.confirm({ title: '配额不足', content: '每日配额已用完,升级VIP可获得更多配额', okText: '立即升级', onOk: () => navigate('/upgrade') }); } } ``` ### Q5: 能否一次性消耗多次配额? **A:** 可以,但不建议。建议每次爬取一个平台就调用一次接口: ```javascript // ✅ 推荐:逐个平台调用 for (const platform of platforms) { await consumeQuota(platform); // count=1 } // ❌ 不推荐:一次性消耗多次 await consumeQuota('yaoshibang', { count: 3 }); ``` --- ## 📞 技术支持 如有问题,请联系: - 📧 邮箱:dev@zhijiayun.com - 💬 企业微信:药店采购比价系统技术群 - 📱 电话:400-XXX-XXXX --- **最后更新**:2026-06-16 **文档版本**:v1.0-MVP **维护团队**:后端开发组