药品流向数据导出(华东医药 + 重药)
一个入口覆盖两个平台的流向数据导出。用 --platform 或 config.json 里的 platform 选择平台。
| platform | 站点 | 目标页面 | 产物 |
|---|---|---|---|
hd |
华东医药 https://su.eastpharm.com |
供应商专区 / 数据流向(新) | {组名}_{华东医药}_{月份周次}_明细.xlsx + ..._合计.xlsx(各一份) |
cq-p |
重药 https://new-data-flow.cq-p.com.cn:8080 |
批发 / 销售流向查询 | {组名}_{重药}_{商品名}_{月份周次}.xlsx(单文件) |
两个平台都支持:指定月份、按月份第几周收窄、保留站点原始文件名、无头运行。
一份配置里可以放多组账号(不同平台、不同账号混合),一次命令跑完。
快速开始
1)安装依赖(只需一次):
cd ~/.workbuddy/skills/pharm-flow-export/scripts
npm install
2)在工作目录放 config.json(模板见 assets/)。
- 单个账号:顶层直接写
platform、account、password(重药还需loginUrl) - 多个账号:写到
profiles数组里,每组一个平台 + 账号 + 密码(见下方「多组配置」)
3)导出(在存放 config.json 的工作目录执行):
node ~/.workbuddy/skills/pharm-flow-export/scripts/download-flow.js
# 显式指定平台(单组配置时)
... --platform=hd
... --platform=cq-p
# 指定月份
... --platform=hd --month=2026-09
# 按月份第几周导出
... --platform=hd --week=2 # 当月第 2 周
... --platform=cq-p --month=2026-09 --week=1-3
# 多组配置:跑全部 / 跑指定组
... --all
... --profile=华东医药
... --profile=华东医药,重药 --month=2026-10 --week=2
# 查看配置里有哪些组 / 支持哪些平台
... --profiles
... --platforms
# 先看周切分再决定导哪周
... --platform=hd --weeks
产物落在工作目录 downloads/(多组时按组分目录),过程日志、登录态、失败截图在 logs/。
参数
| 参数 | 说明 |
|---|---|
--platform=hd\|cq-p |
指定平台;省略时取 config.json(或配置组)的 platform,再按 loginUrl/flowUrl 域名推断。别名:hd/华东/eastpharm、cqp/重药/chongqing |
--platforms |
列出支持的两个平台 |
--profiles |
列出 config.json 里配置的账号组 |
--profile=名称 |
只跑某(几)组:可多次传、逗号分隔,也接受序号(如 --profile=2)。多组配置下不传则报错提示 |
--all |
跑配置里的所有组 |
--month=YYYY-MM |
目标月份(默认当月) |
--week=N |
月内第几周(1-6);--week=1-3 取连续几周;不传则整月 |
--weeks |
只打印目标月份的自然周切分,不导出 |
--query |
仅重药:导出前先点「查询」,打印数量/金额统计 |
--headless |
无头运行(默认有头,便于观察) |
--keep-name |
保留站点原始文件名,不按规则重命名 |
--out=目录 |
指定下载目录 |
--config=路径 |
指定其它配置文件 |
多组配置(一份配置管多个账号)
顶层放公共字段,profiles 数组里每组 = 平台 + 账号 + 密码(组内还能覆盖任意公共字段):
{
"dateMode": "currentMonth",
"downloadDir": "downloads",
"profiles": [
{ "name": "华东医药", "platform": "hd", "account": "a", "password": "1" },
{
"name": "重药",
"platform": "cq-p",
"account": "b",
"password": "2",
"loginUrl": "https://cqp-sxy-sso.cq-p.com.cn/ssoLogin?redirect_url=...&idmsyscode=10009",
"productName": "缬沙坦氨氯地平片"
}
]
}
规则:
name用于--profile=指定、产物子目录,以及文件名前缀;缺省取账号;也接受序号(--profile=2)。单组形态也可在顶层写name- 组内不写
platform时,按组内/顶层的loginUrl、flowUrl域名推断 - 优先级:CLI 参数 > 组字段 > 顶层公共字段 > 平台默认值
- 多组自动隔离:登录态存
logs/state-{组}.json(避免两个账号串号),产物落downloads/{组}/(避免同名覆盖);组内自己写了downloadDir则以它为准 - 只有一个账号时,仍可用单组形态(顶层
account/password),行为与以前完全一致
配置项
通用字段(两平台一致):
platform:hd或cq-paccount/password:账号密码dateMode:currentMonth(默认)/lastMonth/today/customendAtToday:true时当月截止到今天,默认取月末customStartDate/customEndDate:dateMode=custom时使用weekOfMonth:月内第几周,0=整月(默认);支持1~6,1-3表示连续几周;CLI--week优先weekMode:clipped(默认)按自然周切分并裁剪到月内(首周从 1 号起、末周到月末止);iso保留跨月的完整自然周。自然周一律为 周一~周日downloadDir:下载目录,默认downloadskeepName:保留原始文件名(旧字段keepOriginalName仍兼容)headless/slowMo/actionTimeout/downloadTimeout/browserPath
平台专属:
- 华东
hd:loginUrl、baseUrl、flowUrl、downloadCenterUrl、exportTimeout(等待队列完成)、loginTimeout - 重药
cq-p:loginUrl(必填,带redirect_url的 SSO 地址)、flowUrl、productName(命名用)、runQueryFirst、selectAllColumns、fileType(.xlsx/.xls/.csv/.xml)、patchExportParams、productColumn(默认品名)
命名规则(--keep-name 时一律用站点原始文件名,不加前缀):
- 前缀 =
{配置组名}_{平台名},组名与平台名重复时只保留一个(如组名就叫「华东医药」则只有一段) - 华东:
{前缀}_{YYYYMM}[W周次]_明细.xlsx、..._合计.xlsx
如杭州远大_华东医药_202610W2_明细.xlsx、华东医药_202610W2_合计.xlsx - 重药:
{前缀}_{商品名}_{YYYYMM}[W周次].xlsx
如杭州远大_重药_缬沙坦氨氯地平片_202610W2.xlsx;多商品时为..._A等2种_202609.xlsx - 单组形态未设
name时前缀只剩平台名,如重药_缬沙坦氨氯地平片_202610W2.xlsx
关键坑(踩过的)
华东
- 登录有 4 位图形验证码:用
scripts/ocr.py(ddddocr)自动识别,识别异常自动「换一组」重试,需pip install ddddocr pillow。 - 下载中心的「下载」按钮被站点 CSS 设为
visibility:hidden,模拟点击不可靠。脚本改为直接调站点自身接口:pageExportQueue查队列 →fileLib/batchGetWebUrl换 OSS 签名地址 → 直链下载。 - 导出是异步任务:提交后需轮询队列等
SUCCESS,按「导出类型 + 创建时间不早于本次运行」匹配,避免误取历史任务。下载中心里 序号1=合计、序号2=明细。
重药
- 导出接口会额外带上页面默认的
startTime/endDate,与目标月份不一致时结果被过滤为空。脚本默认patchExportParams: true改写请求体,不要关掉。 - 路由守卫会弹回首页:脚本校验是否真正落在
#/wholesale/flowdirection,否则重新登录/重试。 - 同名按钮有隐藏副本(「导出」「查询」),定位时必须过滤可见元素。
- 别点页面空白处关闭日期面板(左侧是菜单,会误触跳页),用 Enter + Escape。
- 站点导出的 xlsx 的 dimension 声明错误(写成 A1):openpyxl 只读模式只能读到 1 行,必须用 exceljs 或非只读模式读取。
页面结构、选择器与接口细节见 references/site-notes-hd.md、references/site-notes-cqp.md。
文件
scripts/download-flow.js CLI 统一入口
scripts/ocr.py 验证码识别(华东登录用)
scripts/src/index.js 平台分发 + 多组编排
scripts/src/platform.js 平台注册表与解析(--platform / config / 域名推断)
scripts/src/profiles.js 多组配置(profiles)解析与选择(--profile / --all)
scripts/src/shared/ 共用:dates / config / browser / logger
scripts/src/hd/ 华东:config / auth / flow / api / naming / index
scripts/src/cqp/ 重药:config / auth / flow / exporter / inspect / naming / index
scripts/tests/unit.test.js 单元测试 27 条
scripts/tests/e2e.test.js 端到端冒烟(需 RUN_E2E=1)
assets/config.example*.json 配置模板(多组 / 单组 / 各平台)
references/site-notes-*.md 两平台的站点备忘
测试:
node --test scripts/tests/unit.test.js
RUN_E2E=1 PLATFORM=hd WORKDIR=D:/业务/郭轩/华东医药流向 node --test scripts/tests/e2e.test.js
排错
| 现象 | 原因与处理 |
|---|---|
配置里有 N 组账号,请用 --profile=名称 指定 |
多组配置下必须明确跑哪些组:传 --profile=名称(或序号),或 --all 跑全部 |
未找到配置组「x」 |
组名不匹配 → --profiles 查看实际组名(也可用序号) |
config.json 缺少 password |
某个配置组字段不全 → 检查该组的 account/password(重药还要 loginUrl) |
无法确定平台 |
配置里缺 platform 且 URL 无法推断 → 在组内或顶层加 "platform": "hd",或传 --platform= |
| 验证码多次识别失败 | 未装 OCR 依赖 → pip install ddddocr pillow;或看 logs/captcha-*.png 确认截图正常 |
| 等待导出任务完成超时(华东) | 数据量过大 → 调大 exportTimeout;或改用 --week 分批 |
| 重药导出文件为空/0 行 | patchExportParams 被关掉,或该日期区间内本无数据 |
| 读取导出文件只有 1 行 | xlsx dimension 声明错误,用 exceljs 读(脚本已如此处理) |
| 找不到 Chrome/Edge | 设置环境变量 CHROME_PATH,或在 config.json 填 browserPath |