ONBOARDING · HANGao POS & MAPPING

汉高 POS 与商品 Mapping 推送
维护与实施指南

覆盖两条数据链路的完整原理、前后端配置、页面操作、日常巡检、故障排查与变更流程。目标:新人按本指南可以独立接手维护与实施。

适用系统:pipeline / fms-fnd / data-flow / esb-broker / ffc-web / aiops-web 版本:v1.0

开始之前 · 零基础先读

这一节不讲代码。看完你会明白:这个系统为谁解决什么问题、两份数据分别是什么、你每天会在哪几个页面操作。

先讲一个每天都在发生的场景

假设周一有位顾客在京东「汉高家清京东自营旗舰店」买了 2 瓶施华蔻洗发水。接下来会发生这些事:

1
平台产生销售数据京东记录这笔订单;鲲驰的爬虫从各平台抓取订单/销售原始数据,存入数仓(按店铺、SKU、日期整理好)。
↓
2
pipeline 每天定时取数它每天通过 spider_data 从数仓取数并清洗;记住「上次推到 10-14」,今天就取 10-15,取完把水位推进到 10-16。
↓
3
逐条投递给 esb-broker每条 SKU 销售记录一条消息;broker 先收下,再异步转发,失败会重试 3 次。
↓
4
data-flow 把内部格式翻译成汉高格式用 FreeMarker 模板把 sku/time/deal_amt 转成 skuId/salesDate/actualSalesAmount,再加上汉高 token。
↓
5
汉高 DME 收到并返回 code=0汉高的销售看板第二天就能看到「某店某商品卖了多少」。
↓
6
同时维护「商品身份证」pipeline 把「店铺 + 平台 SKU」记进商品对照表;第二天全量 mapping 任务把它推给汉高,汉高才知道这个平台 SKU 对应自家哪个产品。

如果没有这个系统

人工做法每天从各平台导报表、拼 Excel、发邮件;新商品上架后靠人记得补对照表;出错后不知道是哪天、哪个 SKU、哪一步错的。
现在定时自动取数、自动重试、每条都有流水可查;mapping 有页面可维护;缺数可以按天补。

两份数据,用大白话解释

POS(日销)Mapping(商品对照)
打个比方每天销售小票的汇总商品身份证对照表
回答什么问题某天、某店、某个商品,卖了几件、卖了多少钱平台上的这个商品编码,是汉高的哪个产品、哪个条码
数据形态增量,按天全量快照,每天推一遍
汉高拿去做什么销售分析、市场看板识别商品、对账、结算
你不维护会怎样汉高看不到当天销量汉高不知道卖的是哪个货,数据挂错商品
数据从哪来(重要) 各电商平台(天猫 / 京东 / 唯品会)→ 爬虫抓取原始订单、退款、商品数据 → 存入数仓 → pipeline 通过 spider_data 从数仓取数、清洗 → 推送汉高。我们不是数据的生产者,只是数仓的使用方。
一张图看懂:这个系统每天在做什么 上面这条线是“卖了多少”(POS),下面这条线是“卖的是哪个货”(Mapping),两条线最终都进入汉高 DME 平台。 POS 日销 商品 Mapping 消费者下单 京东 / 天猫 / 唯品会 真实成交 电商平台 订单/退款/商品数据 爬虫定时抓取 数仓(原始数据) 按店铺/SKU/日期 src_tmcs_* 等 pipeline 每天定时取数 逐条推送 汉高 /sales-daily 某天某店某 SKU 销量 / 销售额 汉高 销售看板 市场分析 爬虫 spider_data esb+模板 商品上架 每个平台一个编码 京东SKU/猫超ID… 平台商品编码 平台只认自己的码 汉高不认 商品对照表 平台码 → 鲲驰码 fms-fnd 维护 pipeline 每天全量推送 约 100 页 汉高 /product/mapping 平台码 → 条码 汉高识别商品 汉高 商品主数据 对账/结算 通俗理解:POS 是“销售小票的每日汇总”,Mapping 是“商品身份证对照表”。没有 Mapping,汉高看到一串平台编码,不知道对应自家哪个产品。 这个系统就是每天自动把这两份数据整理好、推给汉高;出问题时,也是按上面两条线逐段排查。
图 0-1 一张图看懂两条数据线

先记住这 13 个词

词大白话在本系统里指什么
爬虫从各电商平台抓取订单/退款/商品原始数据的程序抓取结果存入数仓,供 spider_data 查询
数仓 / 宽表存放爬虫原始数据、按维度整理后供下游取数src_tmcs_*、src_ppzh_*、src_vph_*
SKU / 平台编码平台给每个商品规格编的号京东 sku、猫超 sku_id、唯品会 goodsCode
鲲驰码 / 69 码鲲驰码或国家商品条码product_code、子表的 subBarCode
POS每日销售汇总推送到汉高 /sales-daily
Mapping商品对照关系推送到汉高 /product/mapping
水位 nextStartTime系统记住「上次推到哪一天」report_job_task.nextStartTime
模板一段把内部字段翻译成汉高字段的脚本mongo data_flow_template
订阅者broker 里的「收件人」配置broker_subscriber
幂等同一条数据重复推,结果不变汉高按 id 或 shopId+skuId+salesDate 覆盖
补数把水位拨回某一天重跑aiops 改时间 + xxl-job 手动触发
回流 / 补抓回流:京东次月重推的上月完整数据;补抓:事后补的某天数据day_p1(回流)/ day_p2(补抓)
全量 / 增量每次都推全部 / 只推变化的部分Mapping 全量;POS 按天增量

你每天会碰到的 3 个界面 + 1 个后台

新人最常用的两条页面操作路径 左边是加映射(ffc-web),右边是加/改任务(aiops-web)。按编号点即可。 A. 新增/修改商品映射(ffc-web) 登录ffc-web 基础数据商品对照 查询/新增编辑/导入 保存成功 次日03:00 保存后不立即推送;等次日全量任务。急用去 xxl-job 手动触发 MappingReportToHangao。 B. 新增/修改 POS 任务(aiops-web) 登录aiops-web 汉高Report任务配置 新增/改时间改参数 xxl-job触发 查流水/esb 保存后还要去 xxl-job 配调度;改时间只影响“取哪一天”,不是“几点跑”。 C. 补数(POS) 改时间目标日期 xxl-job手动执行 查推送流水task_status 查 broker_messageresultStatus 一次补一天;多天重复。补数前确认汉高幂等,避免重复计数。 D. 新增一个汉高店铺(实施) shop_config加店铺 导入 mappingExcel 模板加shopId 分支 新增 POS 任务+ xxl-job 四步缺一不可;漏了 shop_config,POS 自动累积会被跳过。
图 6-1 页面操作与实施路径
界面你会在上面做什么对应章节
ffc-web · 商品对照新增/编辑/导入映射,启用/禁用,导出第 6 章
aiops-web · Report任务配置查看任务、改下次执行时间(补数)、改参数第 7 章
xxl-job看调度日志、手动触发、确认任务是否执行7.4 / 9.4
mongo / mysql查证据:任务配置、推送流水、消息与下游响应9.3

本指南的图索引

图位置用它来理解
图 0-1本节两条数据线的全貌(先看这张)
图 2-12.2 前系统架构:谁负责哪一段
图 3-1第 3 章末配置对象之间怎么关联
图 4-14.2 前POS 每一步的报文长什么样
图 4-24.7 前水位怎么推进、补数怎么做
图 4-34.5 内日常 day 与次月回流 day_p1 的关系
图 5-15.4 前Mapping 每一步的报文长什么样
图 5-25.5 前编辑子表后数据库的变化
图 6-1本节日常页面操作路径
图 9-19.2 前一条消息的状态与重试
图 9-29.3 前从业务反馈到根因怎么查

0读前必看 & 30 分钟上手

如果你今天刚接手,按下面 5 步走一遍,就能把整条链路跑通并定位问题。

0.1 你需要拿到的权限/账号

系统用途找谁/在哪
aiops-web查看/修改 Report 任务配置、改下次执行时间菜单「汉高 → Report任务配置」
ffc-web维护商品码对照(mapping)菜单「基础数据 → 商品对照」
xxl-job 控制台查看调度日志、手动触发、补数运维/负责人
MongoDB(thirdhub)查任务配置、消息、模板、路由负责人提供只读/读写账号
MySQL(fms_fnd)查映射台账、推送流水负责人提供只读账号
Nacos查看/修改 pipeline、data-flow 配置(汉高 appKey 等)运维
安全提醒 本文不记录任何生产密码。数据库、Nacos、xxl-job 的账号密码请从团队密钥管理或负责人处获取,禁止写入代码仓库、文档或聊天记录。

0.2 30 分钟上手路径

1
看懂两条链路读第 2 章的两张链路图。记住:POS 是「按天按 SKU 明细」,Mapping 是「每天全量快照」。
↓
2
打开两个页面aiops-web「汉高 → Report任务配置」查任务;ffc-web「基础数据 → 商品对照」查映射。
↓
3
查一条真实消息在 mongo broker_message 找一条 subscriberId=hangao_report-jd_report_order 的记录,看 reqBody、respBody、resultStatus。
↓
4
手动跑一次补数按 9.4 的步骤,把某个任务的 nextStartTime 改成目标日期,在 xxl-job 手动触发一次,观察 brand_data_push_task 与 broker_message。
↓
5
跑一遍巡检按 9.1 的 SQL/Mongo 脚本检查最近 7 天成功率、水位、mapping 缺口。

0.3 一分钟版本(背下来)

一句话链路 POS:report_job_task → spider_data → 逐条 → esb-broker → data-flow 模板 → 汉高 /sales-daily。
Mapping:brand_platform_sku_mapping → 每页 50 条 → esb-broker → data-flow 模板 → 汉高 /product/mapping。
判断成功:pipeline 的 task_status=2 只代表 broker 收单;汉高真实结果看 broker_message.resultStatus。

1业务背景

先搞清楚“为什么要推两份数据”,后面的字段和过滤规则才有判断依据。

1.1 客户与场景

汉高(Henkel)是鲲驰代运营的品牌方之一,涉及施华蔻(Schwarzkopf)、丝蕴(Syoss)、资生堂专业美发、汉高家清等品牌,在猫超、京东自营、京东 POP、唯品会等多个渠道销售。汉高侧使用 DME 平台(域名 dmeapi-prod.schwarzkopfclub.com.cn)接收鲲驰推送的数据,用于:

数据来源:爬虫从各平台抓取原始订单/退款/商品数据并存入数仓;本系统只从数仓取数、清洗、推送,不负责采集。

1.2 两份数据的区别

维度POS 日销商品 Mapping
业务含义某天某店某 SKU 卖了多少平台 SKU 对应哪个鲲驰商品/条码
数据形态增量、按日期全量快照、无日期
推送粒度一行一条消息一页(50 条)一条消息
汉高接口POST /sales-dailyPOST /product/mapping
失败影响当天销售数据缺失汉高无法识别新商品,销售数据可能挂错商品
维护入口aiops-web Report任务配置ffc-web 商品对照
京东还有一份“补正”数据 京东的历史销量会回溯变化,所以除了每天推常规数据,次月 7 号还会把上月 1 号到月底完整重推一遍(数仓标记 period=day_p1,汉高侧标记“回流”)。汉高两份都看,回流用于修正上月。详见 4.5。

1.3 汉高接口与鉴权

项说明
域名https://dmeapi-prod.schwarzkopfclub.com.cn(生产);测试环境域名由 data-flow 配置决定
鉴权请求头 X-Auth-Token,token 由 GET /auth/access-token 获取,有效期 1800 秒,Redis 缓存 expiresIn-5 秒
签名signature = md5(appKey + appSecret + timestamp)
成功判定HTTP 200 且响应体 {"code":"0","msg":"success"}
配置位置data-flow 的 Nacos 配置:hangao.appKey、hangao.appSecret、hangao.ip

2系统全景

六个系统各司其职,任何一段出问题都会表现为“汉高没收到”,所以必须能画出这张图。

2.1 系统职责

系统/服务仓库职责关键类
mall-pipeline-providerD:\java-project\pipeline定时取数、逐条/分页推送、写推送流水、回写映射CommReportPublishJobHandler
CommSkuMappingPublishJobHandler
BrandDataPushTaskService
spider_data独立取数服务(不在本地仓库)按 table/db_type/日期 查询数仓宽表(爬虫采集后入库),返回分页 JSONHTTP 接口 /openapi/spider_data
esb-broker-providerD:\java-project\esb-broker消息总线:同步收单、落库、异步投递、失败重试MessageController
DeliveryMessageListener
data-flow-providerD:\java-project\data-flow用 FreeMarker 模板把内部报文转成汉高报文,并加鉴权头推送CommController
RequestTemplateSupport
HangaoTemplateFlow
fms-fnd-providerD:\java-project\fms-fnd商品映射台账、推送流水、店铺配置BrandPlatformSkuMappingController
BrandDataPushTaskController
ffc-webD:\java-project\ffc-web商品码对照维护页面src/views/baseData/goods-comparison.vue
aiops-webD:\java-project\aiops-webReport 任务配置页面src/views/reportJobTask/
系统架构:谁负责哪一段 左边是主链路(从上到下),右边是支撑组件。新人先记住 6 个主节点,再记 5 个支撑组件。 数仓 取数 调度 消息 转换 外部 数仓(原始数据) 爬虫抓取各平台原始订单/退款/商品数据 → 入库 MySQL(9030) / PG:src_tmcs_* / src_ppzh_* / src_vph_* spider_data 取数服务 GET /openapi/spider_data?table=…&日期字段=…&limit/offset mall-pipeline-provider(xxl-job) CommReportPublishJobHandler / CommSkuMappingPublishJobHandler esb-broker-provider 同步收单 → 落库 → RabbitMQ 异步投递 → 最多 3 次重试 data-flow-provider FreeMarker 模板转换 + 汉高 token(X-Auth-Token) 汉高 DME POST /sales-daily(POS) · POST /product/mapping(Mapping) SQL 查询 HTTP JSON POST /message/send 异步 HTTP POST HTTPS + Token 支撑组件(谁在用) MongoDB · thirdhub report_job_task:任务与水位 broker_*:订阅者/绑定/消息/日志 data_flow_*:路由与 FreeMarker 模板 MySQL · fms_fnd brand_platform_sku_mapping:映射台账 brand_data_push_task:推送流水 shop_config:店铺名 ↔ shopId RabbitMQ broker_topic → broker.basic 失败:broker.dead → broker.retry Redis 汉高 accessToken 缓存(1800s) broker 订阅者/绑定缓存(300s) 管理页面 ffc-web:基础数据 → 商品对照 aiops-web:汉高 → Report任务配置 xxl-job:调度、日志、手动触发 常见误解 pipeline 显示成功 ≠ 汉高收到 真实结果看 broker_message.resultStatus
图 2-1 系统架构与支撑组件

2.2 POS 日销链路

1
mongo report_job_task每个店铺/口径一条配置,_id = jobName,nextStartTime 决定取哪一天
↓
2
spider_data从数仓按 table/db_type/日期 分页取数:GET /openapi/spider_data?table=...&db_type=...&日期字段=...,返回 {status:200, count, results}
↓
3
pipeline 逐条处理每条先建 brand_data_push_task,再 POST esb-broker,再回写成功/失败
↓
4
esb-broker按 targetService-targetMethod 找订阅者,落 broker_message,异步 POST
↓
5
data-flow-provider按 URL 找模板 → FreeMarker 转换 → 加 token → POST 汉高
↓
6
汉高 DME/sales-daily,返回 code=0 视为成功

2.3 商品 Mapping 链路

1
mongo report_job_task固定一条 MappingReportToHangao,nextStartTime 只是运行计数,不参与取数
↓
2
fms-fnd searchMapping全量分页,不过滤 status,默认按 updateTime desc, status desc, id desc
↓
3
pipeline 逐页组装productCode 为空的记录直接丢弃;每页组装成 {size, data:[...]}
↓
4
esb-brokerhangao_product-product_mapping
↓
5
data-flow-provider模板 ffc-hangao_product_mapping → POST 汉高
↓
6
汉高 DME/product/mapping,按 id/skuId 做 upsert
注意 两条链路共用同一个 pipeline 任务表和同一个 ESB 通道,但走不同的 Handler。看到 jobName=MappingReportToHangao 就知道是 Mapping,其余基本都是 POS。

3核心数据模型

这一章是维护的地基。改配置、查问题、写 SQL,都围绕这些表和集合。

3.1 mongo report_job_task(任务配置,最重要)

集合:thirdhub.report_job_task,_id 就是 jobName。共约 30 条配置。

字段必填作用示例/注意
_id是任务唯一标识,也是 xxl-job 参数里的 jobNameJd108ReportToHangao
jobInfo是任务描述,会写入推送流水的 job_info汉高家清京东自营旗舰店==>hangao
nextStartTime是水位:取数用的业务日期,执行一次后 += intervalMinutes2026-10-16 16:00:00;只有日期部分有用
reportPathPOS 必填上游取数地址http://10.0.1.216:9008/openapi/spider_data
targetPath是esb-broker 投递地址(含 targetService/targetMethod)见 3.2
intervalMinutes是每次执行水位前进多少分钟POS 固定 1440(1 天)
compensationMinutes否取数日期相对水位的补偿分钟数现有配置均为 0
retryCount / retrySecond否上游 HTTP 重试次数/间隔秒3 / 10
pageSize是上游每页条数统一 50
pageSleep否页间休眠毫秒300
param是上游静态查询参数(table/db_type/fields/店铺/品牌/period 等)见 4.4
timeFieldListPOS 必填上游日期字段名列表,每个字段都塞同一个日期值京东 ["time","start_date"];猫超 ["date"]
timePatternPOS 必填日期格式yyyy-MM-dd 或 yyyyMMdd
successField / successValue是上游成功标识status / 200
countFieldPOS 必填上游总条数字段count
dataListFieldPOS 必填上游数据列表字段results
version是Spring Data 乐观锁版本新增写 0
不要漏 POS 任务如果 timeFieldList 为 null,basicQuery() 会直接 NPE;Mapping 任务如果 nextStartTime 为 null,收尾 plusMinutes() 也会 NPE。手工插入配置时这两项必须给值。

3.2 esb-broker 路由配置

集合作用汉高相关文档
broker_subscriber订阅者:目标地址、绑定、成功码、重试开关hangao_report-tm_report_order
hangao_report-jd_report_order
hangao_report-wph_report_order
hangao_report-jd_pop_report_order
hangao_product-product_mapping
broker_bindingRabbitMQ 绑定broker_topic@broker.basic@basic(POS)
broker_topic@broker.basic@basic(Mapping 同)
broker_queue队列定义broker.basic
broker_listener监听器定义broker.basic,type=DELIVERY

一条 POS 配置的 targetPath 样例:

http://isb-gateway.ikunchi.com/esb-broker-provider/message/send
  ?sourceService=report
  &sourceMethod=jd-order
  &targetService=hangao_report
  &targetMethod=jd_report_order

Mapping 的 targetPath:

http://isb-gateway.ikunchi.com/esb-broker-provider/message/send
  ?sourceService=pipeline
  &sourceMethod=mapping_publish
  &targetService=hangao_product
  &targetMethod=product_mapping

3.3 esb-broker 消息与日志

集合关键字段用途
broker_message_id(消息ID)、subscriberId、targetUrl、reqBody、postBody、respBody、messageStatus、resultStatus、retryCount每条推送的完整记录,判断汉高是否成功看这里
broker_loggingmessageId、reqTime、respTime、respCode、respBody、spend、exception每次 HTTP 调用的明细(含重试)

状态字典:

3.4 data-flow 模板配置

集合作用关键字段
data_flow_apiURL 路径 → 模板 ID_id=路径、templateId
data_flow_template模板内容与目标地址targetHost、targetPath、serviceId=hangao、template、expectField、expectValue、valid
路径模板 ID汉高接口
/message/template/receive/report/tm/hangao/ordertm_report-hangao_order/sales-daily
/message/template/receive/report/jd/hangao/orderjd_report-hangao_order/sales-daily
/message/template/receive/wph/hangao/reportwph_report-hangao_report/sales-daily
/message/template/receive/report/jdpop/hangao/orderjd_pop_report-hangao_order/sales-daily
/message/template/receive/ffc/hangao/productMappingffc-hangao_product_mapping/product/mapping

3.5 fms_fnd 业务表

表作用规模(参考)
brand_platform_sku_mapping商品映射台账(核心)约 5,000 行
brand_data_push_task推送流水(POS 每条一行,Mapping 每页一行)约 229 万行
shop_config店铺名称 ↔ shopId(mapping 自动同步依赖)少量

brand_platform_sku_mapping 关键字段

字段含义注意
id主键推送给汉高作为 id
shop_id店铺 ID101/102/.../10231339
shop_name店铺名称展示用,需与 shop_config 对应
platform_code平台商品编码与 shop_id 组成业务唯一键
product_code鲲驰码/69 码为空则不会推送汉高
product_name平台商品名称截断 128 字符
sub_sku_mapping子品项 JSON[{subSkuName,subBarCode,quantity,skuTag,shouldAlert}]
type1 单品 / 2 套装推送时转成“单品/套装”
status0 禁用 / 1 启用禁用也会推送,enableStates=0
source_type1 手工 / 2 系统自动同步自动同步只覆盖京东系
version乐观锁编辑必传
effective_time / expire_time生效/失效时间默认 1970 / 9999

3.6 上游数仓表

这些表都在数仓里,数据来源是爬虫从各平台抓取的原始订单/退款/商品数据。我们作为使用方,只负责按日期取数、清洗、推送,不负责采集。

渠道表库类型日期字段
猫超(2023/2025)src_tmcs_merchandise_message_viewmysqldate(yyyyMMdd)
猫超(老,2021)src_tmcs1_market_daily_newmysqlbusiness_date_time
京东自营src_ppzh_report_manage_propgtime、start_date
京东 POPsrc_sz_product_listpgstart_date
汉高家清 POPsrc_sz_store_skupgstart_date
唯品会src_vph_mflp_detailmysqllogDate、start_date
配置对象关系:改一个任务,实际牵动哪些配置 箭头上的字是“关联键”。新增/排障时,沿着箭头逐个确认,避免漏配。 xxl-job JobHandler + 参数 report_job_task _id = jobName broker_subscriber service-method data_flow_api _id = URL 路径 data_flow_template FreeMarker + 目标地址 broker_binding exchange@queue@routingKey broker_queue / listener queue=broker.basic brand_platform_sku_mapping 映射台账 Mapping 任务读取 · POS 任务写入 shop_config shop_name → shop_id brand_data_push_task 推送流水(每行/每页) broker_message 消息与下游结果 jobName target targetPath templateId binding destination shop_name 排障时的顺序:先看 report_job_task 的 targetPath → 是否对应 broker_subscriber → 它的 targetPath 是否对应 data_flow_api → 模板是否存在。 Mapping 另有一条依赖:shop_config 必须先有店铺,否则 POS 自动累积会被跳过(日志里是 “无法通过shopName获取对应的shopId”)。
图 3-1 配置对象关系与关联键

4POS 推送实现

核心类:mall-pipeline-provider/src/main/java/com/kunchi/mall/pipeline/job/report/CommReportPublishJobHandler.java

4.1 执行流程(逐行对应代码)

  1. 读配置:task(param) 用 {"jobName":"..."} 查 report_job_task;查不到返回 false。
  2. 生成批次号:yyyyMMddHHmmssSSS-8位随机,贯穿本次所有推送流水。
  3. 拼上游 URL:startTime = nextStartTime - compensationMinutes,按 timePattern 格式化,然后给 timeFieldList 每个字段都塞这个值,再拼 param 静态参数和 limit/offset。
  4. 取总数:GET 一次,用 successField/successValue(status=200)判断成功,读 countField=count。
  5. 无数据分支:totalCount=0 → 记告警、建 no_data=true 的流水、抛异常。异常导致水位不推进,下次重试同一天。
  6. 逐页取数:读 dataListField=results,先累计 mapping 去重数据,再逐条 sendMessage。
  7. 汇总 mapping:把本批次所有页的 (shopName, sku) 去重后批量调 fms-fnd addMapping(每 1000 条一批)。只对含 sku 字段的行生效(京东系)。
  8. 推进水位:nextStartTime += intervalMinutes,写回 mongo。
POS 数据流:每一步的报文长什么样 从左到右是 5 个阶段。排查时先确定“卡在哪一段”,再对照该段的报文形态。 1 任务配置mongo report_job_task 2 取数spider_data 3 投递esb-broker /message/send 4 模板转换data-flow FreeMarker 5 汉高/sales-daily 配置示例 _id: Jd108Report… nextStartTime: 2026-10-16 timeFieldList: [time, start_date] timePattern: yyyy-MM-dd pageSize: 50 param.shop_name: 汉高家清京东 自营旗舰店 GET 请求 / 响应 table=src_ppzh_… time=2026-10-16 start_date=2026-10-16 limit=50&offset=0 { "status":"200", "count":91, "results":[ {sku,time,…}, … ] } POST /message/send sourceService=report sourceMethod=jd-order targetService= hangao_report targetMethod= jd_report_order body:单条 results[i] {shop_name,sku, time,deal_amt,…} 返回 data = 消息ID (esbId) 模板输出(汉高报文) [{ "shopId":"108", "shopName":"汉高 家清…", "skuId":"1000…", "salesDate": "2026-10-16", "salesQuantity":8, "actualSalesAmount": "166.00", "dataType":"常规" }] 标量字段带引号,数字不带 汉高响应 { "code":"0", "msg":"success", "data":null } data-flow 判定: code == "0" → 外层返回 201 broker 判定: code == 201 → resultStatus=OK 关键点:pipeline 的 task_status=2 只代表第 3 步“broker 收单”成功;第 5 步汉高是否成功,要看 broker_message.resultStatus 与 respBody。 如果某天数据不对,先看第 2 步返回的原始 row,再看第 4 步模板输出,最后看第 5 步汉高响应——三段逐一对,基本 5 分钟能定位。
图 4-1 POS 数据流与各阶段报文形态

4.2 日期窗口与分页

关键理解 这个任务没有“起止时间区间”入参。它只认 nextStartTime 的日期部分,每次只取一天。日志里的“本次执行: xxx - yyy”只是打印,不参与查询。

分页参数固定写死:limit=pageSize(50)、offset=(pageIndex-1)*pageSize。第 1 页先取总数,再循环 1..totalPage。

4.3 单条推送与状态回写

createTask   → fms-fnd /brandDataPushTask/v2/create   (task_status=0)
markProcessing → /brandDataPushTask/v2/update        (task_status=1)
HttpUtil.doPost(esb-broker /message/send)
   └─ 响应 data 非空 → markSuccess (task_status=2)
   └─ 否则          → markFail    (task_status=3)
最容易误解的一点 这里的 task_status=2 只代表 esb-broker 收下了消息,不代表汉高收到了。汉高真实结果在 broker_message.resultStatus。排查“汉高说没收到”时,不要只看 brand_data_push_task。

4.4 现有 POS 任务清单

以 mongo 实际配置为准(aiops 页面可查)。下表按渠道分组,帮助新人快速定位。

渠道jobName上游表店铺条件period
猫超TmSchwarzkopfReportToHangao2023src_tmcs_merchandise_message_viewbrand_name=Schwarzkopf/施华蔻—
TmSYOSSReportToHangao2023同上brand_name=SYOSS/丝蕴—
TmSchwarzkopfProfessionalReportToHangao2025同上brand_name=SCHWARZKOPF%20PROFESSIONAL/施华蔻专业—
京东自营Jd102ReportToHangaosrc_ppzh_report_manage_pro施华蔻专业线京东自营旗舰店day
Jd103ReportToHangao施华蔻京东自营旗舰店day
Jd104ReportToHangao丝蕴京东自营旗舰店day
Jd107ReportToHangao资生堂专业美发京东自营旗舰店day
Jd108ReportToHangao汉高家清京东自营旗舰店day
京东专区Jd103ReportToHangao_zhuanqu同上美发造型京东自营专区day
京东慧采Jd103ReportToHangao_huicai同上username=慧采-施华蔻day
京东回流Jd102ReportToHangao_huiliu同上施华蔻专业线京东自营旗舰店day_p1
Jd103ReportToHangao_huiliu施华蔻京东自营旗舰店day_p1
Jd104ReportToHangao_huiliu丝蕴京东自营旗舰店day_p1
Jd107ReportToHangao_huiliu资生堂专业美发京东自营官方旗舰店day_p1
Jd108ReportToHangao_huiliu汉高家清京东自营旗舰店day_p1
京东补抓Jd102ReportToHangao_retry同上施华蔻专业线京东自营旗舰店day_p2
Jd103ReportToHangao_retry施华蔻京东自营旗舰店day_p2
Jd104ReportToHangao_retry丝蕴京东自营旗舰店day_p2
Jd107ReportToHangao_retry资生堂专业美发京东自营官方旗舰店day_p2
Jd108ReportToHangao_retry汉高家清京东自营旗舰店day_p2
京东 POPJd10231339ReportToHangaosrc_sz_product_listusername=JD-POP-施华蔻_DEVday
汉高家清 POPJd109ReportToHangaosrc_sz_store_skuusername=汉高家清旗舰店day
唯品会Wph105ReportToHangaosrc_vph_mflp_detailbrandName=丝蕴Syossday
Wph106ReportToHangaobrandName=施华蔻Schwarzkopfday
Wph110ReportToHangaobrandName=资生堂专业美发SHISEIDO PROFESSIONAL(URL 编码)day
老猫超(已停/停滞)TmSchwarzkopfReportToHangao
TmSYOSSReportToHangao
src_tmcs1_market_daily_new——
京东回流是“月度动作” 上表里的 *_huiliu 不是每天跑,而是次月集中把上月 1 号到月底逐天重推一遍;日常每天跑的是不带后缀的常规任务(period=day)。

4.5 period 语义与京东回流(day_p1)

period 值业务含义汉高报文 dataType
day常规日销(每天跑)常规
day_p1京东次月重推的上月完整数据(每月固定动作)回流
day_p2临时补抓的某天数据常规(模板只认 day_p1 为回流)
注意 Jd10x_retry 用的是 day_p2,模板里不匹配“回流”,所以推送出去是“常规”。如果业务要求补抓也标回流,需要同时改模板或改用 day_p1。

为什么京东要单独做“回流”

京东的 POS 数据有一个特点:历史数据会回溯变化。同一张订单/同一个 SKU,今天抓到的“10 月 1 日销量”过几天可能被平台修正。比如 10 月 1 日某个品当天看到总共卖了 10,000,到 10 月 5 日再抓同一天,可能已经变成 15,000。所以每天按 by-day 抓到的数据只是“当时的快照”,不是最终值。

和汉高约定:次月 7 号,把上个月 1 号到月底的 by-day 数据完整重抓一遍,数仓里标记为 period=day_p1,再完整推一次给汉高。汉高会同时保留“每天常规”和“次月回流”两份数据,两份都会看;回流用于修正/复核上个月的最终数。

京东回流:每天推“快照”,次月再推一次“全月最终版” 常规任务 period=day(dataType=常规);回流任务 period=day_p1(dataType=回流)。 京东历史数据会回溯修正:10/1 某 SKU 当天看到 10,000,到 10/5 可能变成 15,000,所以按天抓的快照不是最终值。 约定:次月 7 号把上月 1 号到月底的 by-day 数据完整重抓一遍,标记 period=day_p1,再推一次给汉高。 常规(day) 9/1 9/5 9/10 9/15 9/20 9/30 每天推当天;汉高先看到的是“当时快照” 回流(day_p1) 9/1 ~ 9/30 全月 by-day 重新抓一遍 period=day_p1 → 一次跑一天,约 30 次 次月 7 号前后集中触发;漏哪天单独拨回那天补 汉高 DME 常规 + 回流 两份都保留 都会看 回流用于 修正上月
图 4-3 京东回流:日常 day 快照 + 次月 day_p1 全月重推

技术上怎么实现

执行/核对步骤

  1. 确认目标月份:例如 10 月要推 9 月,水位应设为 2026-09-01 00:00:00。
  2. 在 xxl-job 找到对应店铺的 *_huiliu 任务,连续触发,直到水位走过 2026-09-30(约 30 次)。
  3. 查 brand_data_push_task:job_info 带“(回流)”的记录数应约等于“上月天数 × 每天常规条数”。
  4. 抽查 broker_message:subscriberId=hangao_report-jd_report_order,reqBody 里应包含 "period":"day_p1",time/start_date 覆盖 09-01~09-30;respBody 外层 201、内层 code=0。
  5. 如果某天漏了:把水位拨回那一天,单独触发一次;不要重跑整月(会重复推送)。
不要混:回流 ≠ 补抓 _huiliu 用 day_p1,模板输出 回流,是每月固定动作;_retry 用 day_p2,模板输出 常规,是临时补抓。两者的业务含义和汉高侧口径不同,不要互相替代。

4.6 新增一个 POS 任务(两种方式)

方式 A:aiops-web 页面(推荐)

  1. 打开「汉高 → Report任务配置」→ 点「新增任务」。
  2. 按 7.4 的字段说明填写。注意 任务名称(ID) 必须唯一,建议沿用 {渠道}{店铺}ReportToHangao 命名。
  3. 保存后到 xxl-job 新增一个调度:Handler=CommReportPublishJobHandler,参数={"jobName":"你填的ID"},路由策略选「第一个」或「一致性哈希」,阻塞策略选「单机串行」。
  4. 手动触发一次(建议把 nextStartTime 设为目标日期 00:00:00),观察日志与 brand_data_push_task。

方式 B:直接插 mongo(批量/脚本)

db.report_job_task.insertOne({
  "_id": "Jd110ReportToHangao",
  "jobInfo": "xxx店==>hangao",
  "nextStartTime": "2026-10-20 00:00:00",
  "reportPath": "http://10.0.1.216:9008/openapi/spider_data",
  "targetPath": "http://isb-gateway.ikunchi.com/esb-broker-provider/message/send?sourceService=report&sourceMethod=jd-order&targetService=hangao_report&targetMethod=jd_report_order",
  "intervalMinutes": 1440,
  "compensationMinutes": 0,
  "retryCount": 3,
  "retrySecond": 10,
  "pageSize": 50,
  "pageSleep": 300,
  "param": {
    "table": "src_ppzh_report_manage_pro",
    "db_type": "pg",
    "fields": "record_load_time,username,start_date,end_date,period,brand,time,pro_name,sku,first_category_name,second_category_name,third_category_name,shop_name,shop_type,pv,uv,avg_visit_num,avg_stay_time,deal_user,deal_rate,deal_num,deal_pro_num,deal_amt,deal_price_avg,to_cart_num,to_cart_rate",
    "shop_name": "xxx店",
    "period": "day"
  },
  "timeFieldList": ["time", "start_date"],
  "timePattern": "yyyy-MM-dd",
  "successField": "status",
  "successValue": "200",
  "countField": "count",
  "dataListField": "results",
  "createTime": new Date(),
  "updateTime": new Date(),
  "version": NumberLong(0)
})
水位与补数:任务是怎么“一天一天往前走”的 nextStartTime 是“取哪一天的数据”,不是“下次几点跑”。每次执行只前进一天。 D-1 D D+1 D+2 D+3 D+4 第 1 次:取 D-1 水位 D-1 → D 第 2 次:取 D 水位 D → D+1 人工改到 D+2(跳过) 或保持 D+1 重试 第 3 次:取 D+2 水位 D+2 → D+3 第 4 次:取 D+3 取 D+1:0 条 totalCount=0 → 抛异常 水位停在 D+1,不会自动跳过 补数会重复推送同一天 先确认汉高侧幂等 再决定跳过还是重试 日常巡检:nextStartTime 落后业务日期 2 天以上,或 updateTime 超过 24 小时没变,就要查上游是否有数、任务是否在抛 “未查询到数据”。 跳过还是重试:确认上游永远不会有这一天数据才跳过;否则保持原地重试,避免漏推。
图 4-2 水位推进与补数

4.7 补数(重跑某一天)

  1. 在 aiops 页面找到任务,点「改时间」,把 下次执行时间 改为要补的日期(页面会强制成 YYYY-MM-DD 00:00:00)。
  2. 到 xxl-job 手动触发一次该任务。
  3. 观察:brand_data_push_task 新增记录 → broker_message 的 resultStatus。
  4. 要补多天就重复 2~3 步;任务每次只前进一天。
补数前确认 补数会重复推送同一天数据。先确认汉高侧是否幂等(按 shopId+skuId+salesDate 覆盖)。如果不确定,先与业务/汉高确认,避免重复计数。

5Mapping 推送实现

核心类:mall-pipeline-provider/src/main/java/com/kunchi/mall/pipeline/job/report/CommSkuMappingPublishJobHandler.java

5.1 数据从哪来

来源source_type入口覆盖店铺
手工维护1ffc-web 新增/编辑/Excel 导入全部
POS 自动累积2日报跑完后 addMapping仅京东系(102/103/104/107/108/专区)
为什么猫超/唯品会不自动新增 CommReportPublishJobHandler.accumulateUniqueMappingData() 只认字段名 sku。猫超是 sku_code/sku_id、唯品会是 goodsCode、京东 POP 是 pro_id,都会被 continue 跳过。所以这些渠道的新 SKU 必须手工导入。

5.2 全量推送流程

  1. 读 MappingReportToHangao 配置(pageSize=50)。
  2. 调 fms-fnd POST /brandPlatformSkuMapping/v2/searchMapping,请求体只有 pageNo/pageSize(param 为空),不传 status,即不过滤状态。
  3. 默认排序 updateTime desc, status desc, id desc。
  4. 逐页把 DB 字段转成汉高字段(见 5.3),productCode 为空的记录直接丢弃。
  5. 每页组装 {"size":N,"data":[...]},调 pushOnce 投 esb-broker。
  6. 页间休眠 pageSleep;全部完成后 nextStartTime += intervalMinutes(这个值不参与取数)。

5.3 字段转换表

汉高字段来源(fms-fnd)规则
idid主键,汉高侧做幂等
shopIdshop_id原样
shopNameshop_name原样
skuIdplatform_code原样
skuNameproduct_name原样
typetype2→“套装”,其余→“单品”
barCodeproduct_code单品单码;套装多码 + 连接;为空不推
enableStatesstatus0/1,禁用也推
updatedTime / createdTimeupdate_time / create_time字符串
effectiveTime / expirationTimeeffective_time / expire_time默认 1970 / 9999
subSkuMapping[]sub_sku_mapping JSONsubSkuName/subBarCode/quantity/skuTag
Mapping 数据流:全量快照,每页 50 条 和 POS 不同:没有日期,每天把所有映射全量推一遍;productCode 为空的记录会被丢弃。 1 映射台账brand_platform_sku_mapping 2 分页查询fms-fnd searchMapping 3 组装 + 过滤pipeline toTargetItem 4 模板转换data-flow FreeMarker 5 汉高/product/mapping 一行记录(约 5,085 行) id: 6551 shop_id: 106 shop_name: 唯品会施华蔻 platform_code: 6920267948308 product_code: 6920177948308 status: 1 type: 1 source_type: 1 POST /v2/searchMapping { "pageNo":1, "pageSize":50 } 返回(不过滤 status) content[50] totalElements: 5085 排序: updateTime desc, status desc, id desc 过滤 + 组装 productCode 为空 → 丢弃,不推送 { "size":47, "data":[{ id, shopId, shopName, skuId, skuName, type, barCode, enableStates, subSkuMapping }]} 模板输出 { "size":"47", "data":[{ "id":6551, "shopId":"106", "skuId":"6920…", "type":"单品", "barCode":"6920…", "enableStates":1, "subSkuMapping": [{…}] }]} 汉高响应 { "code":"0", "msg":"success" } 空页特殊情况: {size:0,data:[]} → data-flow 返回 201 + "无效数据" → broker 记 OK (监控上看起来成功) 关键点:Mapping 是“全量覆盖”,不是增量。新增一条映射后,不需要单独触发;等次日 03:00 的全量任务即可(急用可手动触发)。 如果汉高说“某个商品没对码”,先查 product_code 是否为空,再查 status 是否启用,最后查 broker_message 是否推送成功。
图 5-1 Mapping 数据流与各阶段报文形态

5.4 过滤、版本与幂等

编辑映射子表后,数据库里发生了什么 不是“改一行”,而是“旧记录失效 + 新增一条”。这是新人最容易困惑的地方。 编辑前 id=1001 version=0 status=1(启用) effective_time=1970-01-01 00:00:00 expire_time=9999-12-31 23:59:59 sub_sku_mapping=[A](子表 A) product_code=6920177948308 改子表 A → B 编辑后:两条记录 id=1001 status=0(禁用) expire_time=昨天 23:59:59 sub_sku_mapping=[A](保留历史) → 推给汉高时 enableStates=0 id=1002 version=0 status=1(启用) effective_time=今天 00:00:00 expire_time=9999-12-31 23:59:59 sub_sku_mapping=[B] → enableStates=1 因为全量推送不过滤 status,所以两条都会发给汉高;汉高按 id 区分,把 1001 禁用、1002 启用。不要手工删旧记录。
图 5-2 Mapping 编辑的版本机制

5.5 新增一个店铺的 Mapping

  1. 确认 shop_config 已有该店铺(shop_id、shop_name、current_shop_name、is_deleted=0)。没有就找 fms-fnd 负责人加。
  2. 在 ffc-web「商品对照」用 Excel 导入初始映射(见 6.9)。
  3. 确认汉高模板里已有该 shopId 分支(JD 模板是按 shop_name/username 映射的,新店铺可能要加分支)。
  4. 跑一次 Mapping 任务,检查 broker_message 里 shopId 是否正确。

6前端操作:ffc-web 商品对照

入口:基础数据 → 商品对照,路由 /goods-comparison,面包屑「基础数据 / 商品码对照维护列表」。对应代码 ffc-web/src/views/baseData/goods-comparison.vue。

6.1 页面结构

区域内容
顶部查询区平台商品编码、店铺名称、条形码、操作人、状态;按钮「查询」「清空」
操作按钮区新增、下载模板、导入、导出、批量禁用、批量启用、批量删除
数据表主表 + 可展开的子品项表;每行有「编辑」「禁用/启用」
分页每页 20 条(默认),可切页大小

6.2 查询

输入条件后点「查询」,实际请求:

POST /fms-fnd-provider/brandPlatformSkuMapping/v2/findByQoWithPage
{
  "pageNo": 1,
  "pageSize": 20,
  "platformCode": "100142031852",
  "shopName": "汉高家清京东自营旗舰店",
  "productCode": "6920177948308",
  "operator": "geyongqiang",
  "status": 1
}

所有条件都是模糊匹配(Containing)。「清空」会重置表单并重新查询。

技巧 查某个店铺的全量映射,只填「店铺名称」即可。店铺名称必须与 shop_config.shop_name 完全一致,否则查不到(模糊匹配也要有连续子串)。

6.3 列表字段含义

列含义关注点
TP名称代运营方,默认“鲲驰”一般不用改
店铺名称平台店铺需与 shop_config 对应
平台侧商品编码京东 SKU / 猫超 sku_id / 唯品会 goodsCode与店铺组成唯一键
条形码鲲驰码/69 码,套装为 + 拼接为空则不会推送汉高
修改版本0=初始版本,1/2...=编辑子表后生成的新版本有多个版本时以 status=1 的为准
来源系统手动上传 / 系统自动同步自动同步只来自京东日报
平台商品名称平台标题截断 128 字符
类型单品 / 套装由子表推断
状态启用 / 禁用禁用也会推给汉高(enableStates=0)
更新时间 / 操作人最后修改信息操作人可能是拼音账号
生效时间 / 失效时间默认 1970 / 9999编辑子表后旧记录失效时间为昨天

展开行可以看到子品项:商品名称、商品条形码、数量、是否告警、标签。

6.4 新增映射

  1. 点「新增」,弹窗标题「新增商品码对照」。
  2. 填写:
    • 店铺名称(必填):必须与 shop_config 完全一致;
    • 平台侧商品编码(必填):平台 SKU;
    • 平台商品名称(必填);
    • 条形码:新增时该字段不参与提交,由子表自动推导;
    • TP名称默认“鲲驰”,状态默认启用,操作人自动取登录账号(只读)。
  3. 在子表点「增行」,填写子品项:
    • 商品名称、商品条形码、数量;
    • 是否告警:默认“是”,用于完整性告警;
    • 标签:正装 / 赠品 / 促销品。
  4. 点「确定」,请求 POST /v2/save。
条码是怎么来的 后端 deriveProductCode():单品取第一个非空 subBarCode;套装按列表顺序用 + 拼接,每个条码重复 quantity 次。例:[111×1, 22×2] → 111+22+22。所以子表的条形码必须填对。
常见报错 “未找到店铺名称[xxx]对应的店铺配置”:店铺名称与 shop_config.shop_name 不一致。
“已存在平台商品编码[xxx]的映射记录”:同店铺同平台编码已存在(含禁用),请改用「编辑」。

6.5 编辑映射(版本机制,重点)

  1. 点行内「编辑」,弹窗回填当前记录与子表。
  2. 可改:店铺名称、平台侧商品编码、平台商品名称、子表(名称/条码/数量/告警/标签)。主表「条形码」即使改了,也会被子表推导值覆盖。
  3. 点「确定」,请求 POST /v2/update(提交完整行对象 + 最新子表)。
改了什么后端行为结果
子表任意字段或条数变化原记录 status=0、expire_time=昨天 23:59:59;新增一条新版本列表多一条,旧记录禁用/失效,新记录启用
只改名称/操作人/状态等原地更新(乐观锁 version)仍是一条
改身份字段(店铺/平台编码)先校验唯一性,再原地更新冲突则返回 0(不报错但未更新)
重要 编辑子表后,旧记录不会删除。汉高会同时收到旧记录(enableStates=0)和新记录(enableStates=1),按 id 区分。不要手工删除旧记录,否则影响历史追溯。

6.6 启用 / 禁用

禁用只影响 enableStates,不影响是否推送;要停止推送只能删除或让 product_code 为空。

6.7 批量删除

勾选行 → 「批量删除」→ 请求 POST /v2/deleteBatch,物理删除,不可恢复。删除后汉高侧不会自动删除,只会停止后续推送。

6.8 导出

点「导出」,按当前查询条件请求 GET /v2/exportMapping,下载 Excel。导出不受分页限制(后端按 Qo 查询全量)。

注意 页面表单里的 shopCode 字段后端 Qo 里没有(Qo 是 shopId),所以它不生效;按店铺筛选用「店铺名称」。

6.9 导入(最常用,必须掌握)

步骤

  1. 点「下载模板」,得到 商品码对照.xlsx。
  2. 按模板填写,一次最多传 3 个文件(可多选)。
  3. 点「导入」,上传后返回形如 导入成功:X 条;重复/跳过:Y 条。
  4. 刷新列表核对。

模板列(必须与表头一致)

列必填规则
TP名称否不填默认“鲲驰”
店铺ID是101/102/103/104/105/106/107/108/109/110/10231339/765276150
店铺名称是需能在 shop_config 中找到
商家产品编码是平台 SKU,与店铺 ID 组成唯一键
69条形码否单品可填;套装以子条码拼接结果为准
商家产品名称是截断 128 字符
启用状态否默认 1
类型否1=套装,其它=单品(与页面相反,注意)
子品项名称单品可空/套装必填
子品项条形码关键是它用于推导 product_code
数量否默认 1;套装拼接时按数量重复
子品项标签否正装/赠品/促销品
子品项是否告警否0 否 / 1 是,默认 1

导入规则

常见问题 “导入成功但列表查不到”:多半是店铺 ID/名称不匹配被跳过,或同 (店铺ID, 平台编码) 已存在。
“套装条码不对”:检查子品项数量;拼接会把条码按数量重复。

6.10 页面操作的副作用(务必知道)

操作立即影响对汉高的影响
新增/导入台账新增,source_type=1下次全量 Mapping 任务(次日)推送
编辑子表旧记录禁用+失效,新增新版本次日推送旧(禁用)+新(启用)两条
禁用status=0次日推送 enableStates=0
删除物理删除汉高侧不会删除,只是不再推送
清空条形码product_code 可能变空次日不再推送该条

7前端操作:aiops-web Report任务配置

入口:汉高 → Report任务配置,路由 /reportJobTask。对应代码 aiops-web/src/views/reportJobTask/。

7.1 页面结构

区域内容
查询条件任务名称(精确匹配)、任务描述(模糊搜索)
列表任务名称(ID)、任务描述、下次执行时间、目标推送地址、查询参数、操作
操作改时间、改参数、删除;顶部「新增任务」

接口前缀:/mall-pipeline-provider/mongo/job/report/task,后端是 JobTaskController.java。

7.2 查询

POST /page,请求 {"jobName":"","jobInfo":"","pageNo":1,"pageSize":10}。

所以 页面上看不到 reportPath、pageSize、timeFieldList 等字段,这是后端只 include 了 5 个字段,不是数据缺失。

7.3 列表字段

列说明
任务名称(ID)Mongo _id,xxl-job 参数要用它
任务描述推送流水里的 job_info
下次执行时间水位,只显示日期部分
目标推送地址targetPath,鼠标悬停看完整
查询参数param 的键值对

7.4 新增任务(逐字段)

点「新增任务」,弹窗分四块:

基础信息

字段必填说明
任务名称(ID)是唯一,不可重复;建议 {渠道}{店铺}ReportToHangao
任务描述否会写入流水,建议写清「店铺==>hangao」
下次执行时间否提交时自动补 00:00:00

推送配置

字段说明
数据查询路径reportPath,spider_data 地址
目标推送地址targetPath,esb-broker 地址,必须带 targetService/targetMethod

执行参数

字段建议值说明
时间间隔(分钟)1440每次水位前进 1 天
补偿时间(分钟)0取数日期相对水位偏移
重试次数3上游 HTTP 重试
重试间隔(秒)10
每页大小50spider_data 分页
分页休眠(ms)300页间休眠

查询与解析

字段说明京东示例
param(键值对)上游静态参数table=src_ppzh_report_manage_pro、db_type=pg、fields=...、shop_name=...、period=day
查询时间参数字段列表timeFieldList,回车添加多个time、start_date
时间格式timePatternyyyy-MM-dd
成功标识字段 / 值successField/successValuestatus / 200
数据总条数字段countFieldcount
返回数据列表字段dataListFieldresults

7.5 改时间(补数最常用)

行内「改时间」→ 选日期 → 确认。请求 POST /updateStartTime,参数 jobName、nextStartTime=YYYY-MM-DD 00:00:00。

注意 页面只能选日期,时间固定 00:00:00;改完立即生效,下一次 xxl-job 触发就会用新日期。不要把它当成“下次运行时刻”,它是“取哪一天的数据”。

7.6 改参数(只能改 4 个字段)

行内「改参数」→ 可修改:任务描述、下次执行时间、目标推送地址、查询参数(param)。其余字段保持原值。

请求 POST /updateV2,后端白名单:

jobInfo、nextStartTime、targetPath、param
两个坑 1)不能通过页面改 reportPath、pageSize、timeFieldList、timePattern、countField 等。要改只能直接操作 mongo,或调旧的 /update 接口(会全量覆盖,慎用)。
2)param 清空不会生效:updateV2 会跳过空 Map。要清空 param 必须直接改 mongo。

7.7 删除

行内「删除」→ 二次确认 → POST /delete?jobName=xxx,物理删除,不可恢复。删除后 xxl-job 再触发会报「未配置任务」。

7.8 新增后还要做什么

1
xxl-job 加调度Handler=CommReportPublishJobHandler,参数 {"jobName":"..."},阻塞策略建议「单机串行」
↓
2
手动触发一次先改 nextStartTime 为目标日期
↓
3
核对流水brand_data_push_task.job_info 是否出现该任务
↓
4
核对下游broker_message 里 resultStatus=OK、respBody.data 内层 code=0

8后端配置(不走页面)

页面覆盖不到的地方:直接改 mongo、加模板、加路由。改之前先在测试环境验证。

8.1 新增一个汉高店铺(完整清单)

步骤位置操作
1fms_fnd shop_config插入 shop_id、shop_name、current_shop_name、is_deleted=0
2ffc-web 商品对照Excel 导入该店铺初始 mapping
3mongo data_flow_template如果模板里没有该店铺的 shopId 分支,补一个
4aiops-web新增 POS 任务(或复用已有任务,若同一店铺多口径则加 _huiliu/_retry)
5xxl-job新增调度并手动触发验证
6核对broker_message 的 shopId、skuId、salesDate 是否正确

8.2 新增一个汉高接口模板

假设新增「退货」推送。需要两件事:

① 新增 API 路径映射

db.data_flow_api.insertOne({
  "_id": "/message/template/receive/ffc/hangao/productReturn",
  "templateId": "ffc-hangao_product_return",
  "info": "汉高退货",
  "createTime": new Date(),
  "updateTime": new Date(),
  "version": 1
})

② 新增模板

db.data_flow_template.insertOne({
  "_id": "ffc-hangao_product_return",
  "info": "汉高退货",
  "targetHost": "https://dmeapi-prod.schwarzkopfclub.com.cn",
  "targetPath": "/commodity/return",
  "serviceId": "hangao",
  "expectField": "code",
  "expectValue": "0",
  "template": "[{\"refundNum\":\"${requestBody.refundId}\",\"skuId\":\"${requestBody.skuId}\"}]",
  "createTime": new Date(),
  "updateTime": new Date(),
  "version": 0
})
模板三条铁律 1)输出必须是合法 JSON(不要在 JSON 里写 // 注释,唯品会模板就有这个历史问题)。
2)serviceId=hangao 才会走鉴权(HangaoTemplateFlow 加 X-Auth-Token)。
3)expectField/expectValue 用于判断汉高业务码,通常是 code=0。

8.3 新增一个 esb-broker 路由

db.broker_subscriber.insertOne({
  "_id": "hangao_product-product_return",
  "service": "hangao_product",
  "method": "product_return",
  "binding": "broker_topic@broker.basic@basic",
  "targetHost": "http://data-flow-provider",
  "targetPath": "/message/template/receive/ffc/hangao/productReturn",
  "subscriberType": "HTTP",
  "expectCode": "code",
  "expectValue": "201",
  "retryWhenError": true,
  "retryWhenFailure": true,
  "retryTtl": 300000,
  "maxRetry": 3,
  "description": "汉高退货"
})

绑定关系 broker_topic@broker.basic@basic 已存在(destination=broker.basic,routingKey=basic),不用新建 binding/queue/listener。

缓存 broker_subscriber 有 5 分钟 JetCache 缓存;新增后如果立即调用 /message/send 报「没有目标地址」,等 5 分钟或重启/清缓存。

8.4 xxl-job 配置约定

任务类型JobHandler参数建议
POS 日销CommReportPublishJobHandler{"jobName":"Jd108ReportToHangao"}单机串行;失败重试 0(任务自身会因无数据失败并重试)
商品 MappingCommSkuMappingPublishJobHandler{"jobName":"MappingReportToHangao"}单机串行;每天 1 次
不要并发 同一个 jobName 不要并发触发。两个实例同时跑会同时读写 nextStartTime,导致水位错乱或重复推送。

8.5 配置修改检查清单

9日常巡检与排障

这一章是可以照着执行的操作手册。建议每天上午跑一遍 9.1。

9.1 每日巡检(5 分钟)

① 推送流水成功率

SELECT job_info,
       COUNT(*) AS total,
       SUM(task_status = 2) AS ok,
       SUM(task_status = 3) AS fail,
       MIN(create_time) AS first_time,
       MAX(create_time) AS last_time
FROM fms_fnd.brand_data_push_task
WHERE create_time >= CURDATE() - INTERVAL 1 DAY
GROUP BY job_info
ORDER BY fail DESC, job_info;

关注:fail > 0;某任务 24 小时无记录(可能没调度或一直失败)。

② 水位是否停滞/滞后

db.report_job_task.find({},
  {jobInfo:1, nextStartTime:1, updateTime:1})
  .sort({updateTime:-1})

关注:nextStartTime 是否落后业务日期 > 2 天;updateTime 是否超过 24 小时没变。

③ 下游失败与汉高业务码

db.broker_message.aggregate([
  {$match:{
    createTime:{$gte:new Date(Date.now()-24*3600*1000)},
    subscriberId:{$in:[
      "hangao_report-tm_report_order",
      "hangao_report-jd_report_order",
      "hangao_report-wph_report_order",
      "hangao_report-jd_pop_report_order",
      "hangao_product-product_mapping"
    ]}
  }},
  {$group:{_id:{sub:"$subscriberId", st:"$resultStatus"}, cnt:{$sum:1}}}
])

关注:ERROR(汉高业务码不对/模板异常)、FAILURE(网络/超时)。

④ Mapping 缺口

SELECT shop_name,
       COUNT(*) AS total,
       SUM(product_code IS NULL OR product_code = '') AS blank_code
FROM fms_fnd.brand_platform_sku_mapping
GROUP BY shop_name
ORDER BY blank_code DESC;

关注:blank_code 增长(新增 SKU 未补条码,不会推给汉高)。

⑤ Mapping 是否每天跑了

SELECT DATE(create_time) AS d, COUNT(*) AS pages
FROM fms_fnd.brand_data_push_task
WHERE job_info = '全量Mapping==>hangao'
  AND create_time >= CURDATE() - INTERVAL 7 DAY
GROUP BY DATE(create_time)
ORDER BY d DESC;

正常每天约 100 页(5,000 条 ÷ 50)。为 0 说明任务没跑或没数据。

⑥ 每月 7 号后:京东回流是否覆盖上月

SELECT job_info, COUNT(*) AS rows_cnt,
       MIN(create_time) AS first_time, MAX(create_time) AS last_time
FROM fms_fnd.brand_data_push_task
WHERE job_info LIKE '%回流%'
  AND create_time >= DATE_FORMAT(CURDATE(), '%Y-%m-01')
GROUP BY job_info;

记录数应约等于“上月天数 × 每天常规条数”。再抽查 mongo broker_message:reqBody 含 "period":"day_p1",日期覆盖上月 1 号到月底。

一条消息的状态流转(esb-broker) messageStatus 表示“投递阶段”,resultStatus 表示“投递结果”。两者不要混。 PENDING 待投递 DELIVERY HTTP POST 下游 OK resultStatus=0 ERROR 业务码/模板异常 FAILURE 网络/超时/连接失败 ENDING 投递结束(不看结果) RETRY broker.dead → 300s → retry 终态 不再重试 投递 成功 业务失败 系统失败 置 ENDING 可重试 3 次后 重试(≤3 次,间隔 300s) 为什么 ENDING 不代表成功:投递完成后无论 HTTP 结果如何,messageStatus 都会置为 ENDING;真正的结果在 resultStatus。
图 9-1 消息状态机与重试路径

9.2 症状 → 排查路径

症状先查判断/处理
aiops 列表看不到任务mongo report_job_task配置不存在或被删;按 4.6 新增
任务执行但 0 条xxl-job 日志 + spider_data 返回上游当天无数据;任务会抛“未查询到数据”
nextStartTime 不动xxl-job 执行日志大概率 totalCount=0 抛异常,水位未推进
汉高说没收到broker_message.resultStatusOK=已送达;ERROR/FAILURE 看 respBody
汉高返回业务错误respBody.data(内层 JSON)内层 code≠0,看 msg
mapping 缺某商品ffc-web 查 platform_code不存在→新增;存在但条码空→补条码
mapping 有两条列表按 updateTime 排序编辑子表产生的新版本,正常;旧记录 status=0
POS 数量/金额不对broker_message.postBody对比模板字段映射与上游原值
重复推送nextStartTime 是否回退/重跑补数会重复;确认汉高幂等
模板转换异常respBody + data-flow 日志模板语法/字段为空导致 JSON 非法
排障决策树:汉高说“没收到 / 数据不对” 从业务反馈出发,按编号走,不要一上来就翻代码。 汉高反馈:没收到 / 数据不对 先确认是 POS 还是 Mapping ① brand_data_push_task 有记录吗? 按 job_info + 日期查 没有 查调度与上游 xxl-job 日志 / 任务配置 / 上游有数 有 ② broker_message.resultStatus? 用 esb_id 关联 OK 解析 respBody.data 内层 code code=0 → 汉高侧确认 code≠0 → 看 msg(业务校验) ERROR 业务码不对 / 模板异常 看 respBody 与 data-flow 日志 常见:字段空、类型错、JSON 非法 FAILURE 网络 / 超时 / 连接失败 看 broker_logging 的 exception/spend 重试 3 次仍失败 → 联系下游 Mapping 专项(与上面并列) 汉高说“某个商品没对码” 查 ffc-web:product_code 是否为空 查 status 是否启用 / 是否已推送 条码为空 → 补条码/重新导入 子表 subBarCode 必须填 次日全量生效(急用可手动触发) 条码不为空但仍未推送 查 broker_message 是否 OK 查 Mapping 任务当天有没有跑
图 9-2 从业务反馈到根因的排查路径

9.3 全链路追踪(一次推送的完整证据链)

1
pipeline 日志搜 生成批次号 batchNo:{...},拿到批次号
↓
2
MySQL brand_data_push_taskWHERE batch_no='...',看 esb_id、task_status、esb_resp
↓
3
Mongo broker_message_id = esb_id,看 messageStatus/resultStatus/respBody
↓
4
Mongo broker_loggingmessageId = esb_id,看每次 HTTP 调用的耗时、响应、异常
↓
5
data-flow 日志搜 respBody.subMessage 里的 traceId(形如 [prod][时间][traceId])
↓
6
汉高响应respBody.data 是字符串,解析后看 code/msg
实用查询:按 esbId 反查
db.broker_message.findOne({_id:"你的-esb-id"})
db.broker_logging.find({messageId:"你的-esb-id"}).sort({reqTime:1})

9.4 补数 / 重推

POS 补数

  1. aiops 页面「改时间」→ 目标日期。
  2. xxl-job 手动触发该任务。
  3. 查 brand_data_push_task 与 broker_message。
  4. 多天则重复;每次只补一天。

Mapping 重推

  1. mapping 是每天全量,通常不需要单独补;改完数据等次日 03:00 即可。
  2. 急用:xxl-job 手动触发 MappingReportToHangao(不改任何参数)。

京东回流补推(次月)

  1. 把对应店铺的 *_huiliu 任务水位改到目标月 1 号(如 2026-09-01 00:00:00)。
  2. 连续触发,直到水位走过月底;漏哪天就单独把水位拨到那天再触发一次。
  3. 核对 broker_message 的 period=day_p1 与日期覆盖。

单条消息重推(esb-broker)

POST /esb-broker-provider/message/sendById?id={messageId}

9.5 已知问题与规避(接手必读)

#问题影响规避/建议
1POS task_status=2 不代表汉高收到误判“已成功”以 broker_message.resultStatus 为准
2HttpUtil.doGet(url) 未设置超时上游卡住时 job 线程挂死巡检看任务耗时;后续修复加 timeout
3totalCount=0 抛异常且不推进水位任务永久卡在某天查上游是否有数;必要时人工改 nextStartTime
4mapping 自动累积只认 sku 字段猫超/唯品会/POP 新 SKU 不进台账这些渠道手工导入
5约 36% mapping 的 product_code 为空不会推给汉高用 incompleteMapping / 告警补条码
6唯品会模板含 // 注释行输出非严格 JSON,靠汉高宽容解析改模板时一并清理
7猫超模板同时输出两套字段重复 key,依赖“后者覆盖”改模板时只保留一套
8broker 重试判断内层只看 retryWhenFailureretryWhenError 可能不生效汉高订阅者两开关都 true,暂不受影响
9broker_message 无 TTL,约 2,500 万条持续增长建议加 TTL 索引(如 90 天)
10RetryJob 默认不覆盖 report/pipelinePOS/Mapping 失败无定时兜底需要时在 xxl-job 参数里显式传 sourceServiceList
11aiops updateV2 清空 param 不生效无法通过页面清空参数直接改 mongo
12Mapping 的 nextStartTime 不参与取数可能跑到未来日期忽略即可,保持非空

10变更与发布

改哪里、要不要发版、发布顺序,一张表说清。

10.1 变更类型与发布方式

变更内容改哪里是否发版注意
新增/修改 POS 任务aiops 页面 / mongo否同步配 xxl-job
新增/修改 mapping 数据ffc-web否次日全量生效
修改汉高模板mongo data_flow_template否先 /template/test 验证 JSON
新增汉高接口路由data_flow_api + broker_subscriber否注意 subscriber 缓存 5 分钟
修改 pipeline 推送逻辑pipeline 代码是走正常分支/合并/发布流程
修改 fms-fnd 映射规则fms-fnd 代码是影响面大,需回归 ffc-web 全部操作
修改前端页面ffc-web / aiops-web是只影响操作,不影响数据

10.2 代码入口索引

功能文件
POS 调度mall-pipeline-provider/.../job/report/CommReportPublishJobHandler.java
Mapping 调度mall-pipeline-provider/.../job/report/CommSkuMappingPublishJobHandler.java
推送流水mall-pipeline-provider/.../service/BrandDataPushTaskService.java
映射累积mall-pipeline-provider/.../service/BrandPlatformSkuMappingService.java
任务配置后台mall-pipeline-provider/.../provider/controller/task/JobTaskController.java
映射 Controllerfms-fnd-provider/.../controller/BrandPlatformSkuMappingController.java
映射 Servicefms-fnd-logic/.../service/impl/BrandPlatformSkuMappingServiceImpl.java
模板分发data-flow-provider/.../support/RequestTemplateSupport.java
汉高鉴权data-flow-provider/.../service/hangao/impl/HangaoServiceImpl.java
汉高推送data-flow-provider/.../flow/template/impl/hangao/HangaoTemplateFlow.java
ESB 发送入口esb-broker-provider/.../provider/controller/MessageController.java
ESB 投递esb-broker-provider/.../provider/support/DeliveryMessageListener.java
商品对照页ffc-web/src/views/baseData/goods-comparison.vue
Report 任务页aiops-web/src/views/reportJobTask/index.tsx

10.3 发布顺序建议

  1. 先发布 fms-fnd(如果改了映射规则/接口)。
  2. 再发布 pipeline(依赖 fms-fnd 接口)。
  3. 再发布 data-flow(如果改了模板加载/鉴权逻辑;只改 mongo 模板不用发版)。
  4. 最后发布前端页面。
发布前 在测试环境完整跑一遍:新增映射 → 编辑子表 → 导入 → 导出 → POS 补数 → 查 broker_message。不要只在生产“点一下看看”。

11附录

11.1 接口清单

系统方法路径用途
fms-fndPOST/brandPlatformSkuMapping/v2/findByQoWithPage商品对照页查询
fms-fndPOST/brandPlatformSkuMapping/v2/save新增映射
fms-fndPOST/brandPlatformSkuMapping/v2/update编辑/批量状态
fms-fndPOST/brandPlatformSkuMapping/v2/deleteBatch批量删除
fms-fndPOST/brandPlatformSkuMapping/v2/uploadMappingExcel 导入
fms-fndGET/brandPlatformSkuMapping/v2/exportMapping导出
fms-fndPOST/brandPlatformSkuMapping/v2/searchMapping对外分页查询(Mapping 任务用)
fms-fndPOST/brandPlatformSkuMapping/v2/addMapping系统自动新增(POS 累积)
fms-fndGET/brandPlatformSkuMapping/v2/incompleteMapping查缺条码映射
fms-fndPOST/brandPlatformSkuMapping/v2/sendAlert完整性告警
fms-fndPOST/brandDataPushTask/v2/create建推送流水
fms-fndPOST/brandDataPushTask/v2/update更新推送流水
pipelinePOST/mongo/job/report/task/page任务分页
pipelinePOST/mongo/job/report/task/updateStartTime改下次执行时间
pipelinePOST/mongo/job/report/task/updateV2改参数(白名单)
pipelinePOST/mongo/job/report/task/update全量覆盖(慎用)
pipelinePOST/mongo/job/report/task/delete删除任务
esb-brokerPOST/message/send发消息(pipeline 用)
esb-brokerPOST/message/sendById按消息 ID 重发
esb-brokerGET/message/findById查消息
esb-brokerGET/logging/findByMessageId查调用日志
esb-brokerPOST/retry/handle批量重试
data-flowPOST/message/template/receive/**模板转发入口
data-flowPOST/template/test模板测试

11.2 表/集合清单

存储名称用途
Mongo thirdhubreport_job_task任务配置与水位
Mongo thirdhubbroker_subscriberESB 订阅者
Mongo thirdhubbroker_bindingESB 绑定
Mongo thirdhubbroker_queue / broker_listenerESB 队列/监听
Mongo thirdhubbroker_message消息与下游结果
Mongo thirdhubbroker_loggingHTTP 调用明细
Mongo thirdhubdata_flow_api / data_flow_template模板路由与内容
MySQL fms_fndbrand_platform_sku_mapping映射台账
MySQL fms_fndbrand_data_push_task推送流水
MySQL fms_fndshop_config店铺配置

11.3 枚举字典

字段值含义
brand_data_push_task.task_status0未开始
1处理中
2推送成功(broker 受理)
3推送失败
broker_message.messageStatusPENDING待投递
SENDING投递中
ENDING已结束(不代表成功)
RETRYING重试中
broker_message.resultStatusOK成功
ERROR业务码不符/模板异常
FAILURE网络/超时/连接失败
brand_platform_sku_mapping.type1单品
2套装
brand_platform_sku_mapping.source_type1手动上传
2系统自动同步
status0禁用
1启用
periodday常规
day_p1回流
day_p2补抓(模板按常规)

11.4 店铺 ID 对照

shopId店铺名称渠道
101汉高施华蔻丝蕴猫超
102施华蔻专业线京东自营旗舰店京东自营
103施华蔻京东自营官方旗舰店 / 施华蔻京东自营旗舰店 / 美发造型京东自营专区京东自营
104丝蕴京东自营旗舰店京东自营
105唯品会丝蕴唯品会
106唯品会施华蔻唯品会
107资生堂专业美发京东自营官方旗舰店京东自营
108汉高家清京东自营旗舰店京东自营
109汉高家清旗舰店(POP)京东 POP
110唯品会资生堂专业美发唯品会
10231339JD-POP-施华蔻官方旗舰店京东 POP
765276150丝蕴官方旗舰店天猫
小坑 Excel 导入的 SHOP_ID_NAME_MAP 里,"765276150 " 的 key 末尾多了一个空格。如果导入时提示店铺不存在,先按这个原因排查。

12FAQ 20 问

1. 为什么 aiops 列表只有 5 个字段?

后端分页接口只 include 了 jobInfo/nextStartTime/targetPath/param(加上 _id),不是数据缺失。要看完整配置只能查 mongo。

2. 为什么“改参数”改不了 pageSize、timeFieldList?

updateV2 只允许改 jobInfo/nextStartTime/targetPath/param。这是为了防误操作。其他字段请直接改 mongo。

3. 为什么“新增任务”没有 reportPath?

新增模式下有「数据查询路径」,编辑模式下隐藏(因为不允许改)。

4. 为什么编辑 mapping 后多了一条记录?

只要子表(subSkuMapping)有变化,后端就会把旧记录禁用+失效,并新增一条新版本记录。这是设计行为,用于保留历史版本。

5. 为什么导入成功但列表查不到?

常见原因:店铺 ID/名称不在允许列表(整行跳过)、同 (店铺ID, 平台编码) 已存在(整组跳过)。看导入返回的“重复/跳过”数量。

6. 为什么禁用了,汉高还在用?

禁用只是把 status=0,下次全量 Mapping 推送会带 enableStates=0,由汉高侧决定是否停用。如果汉高没处理,需要与对方确认。

7. 为什么改了条形码没生效?

检查是否改了子表的「子品项条形码」。product_code 是从子表推导的,直接改主表条形码会被推导值覆盖。

8. 为什么 POS 任务一直卡在同一天?

当天 totalCount=0,任务抛“未查询到数据”异常,水位不推进。确认数仓当天是否有数据;如果是永久空缺,需要人工把 nextStartTime 往后调。

9. 为什么流水显示成功,汉高说没收到?

task_status=2 只代表 esb-broker 受理。查 broker_message.resultStatus 和 respBody;如果 broker 显示 OK 但汉高说没有,拿 respBody.data 里的内层响应与汉高对账。

10. 为什么 mapping 每天全量推?

当前设计就是全量快照,每天约 100 页(5,000 条)。汉高按 id/skuId 做 upsert。好处是不会漏;代价是消息量大。

11. 补数要改哪里?

aiops「改时间」改 nextStartTime,然后 xxl-job 手动触发。一次只补一天,多天重复。

12. 如何新增一个店铺?

按 8.1 的六步:shop_config → 导入 mapping → 模板 shopId 分支 → 新增 POS 任务 → xxl-job → 核对。

13. 如何新增一个平台接口?

按 8.2/8.3:data_flow_api + data_flow_template + broker_subscriber 三件套,然后 pipeline 里配置 targetPath。

14. 如何查看汉高原样返回?

broker_message.respBody 是 data-flow 的返回;其中 data 字段是字符串,里面才是汉高原样响应。

15. 模板改完要发版吗?

不用。模板存在 mongo,改完立即生效(下次请求)。但建议先用 POST /template/test 验证。

16. 操作人为什么是拼音?

新增时取登录账号(可能是拼音);后端保存时会尝试通过 aic-billing 转中文姓名,查不到就保留拼音。

17. 为什么有的 mapping 没有条形码?

子品项条码没填,推导不出 product_code。这类记录不会推给汉高,会进入完整性告警。

18. 为什么同一店铺有两条同名映射?

编辑子表产生的新版本。旧记录 status=0、失效时间为昨天;新记录启用。以启用那条为准。

19. 为什么唯品会/猫超的新 SKU 不自动进 mapping?

自动累积只认字段名 sku,这两个渠道字段是 goodsCode/sku_id。需要手工导入。

20. 出问题找谁?

按链路分段:任务配置/调度找 pipeline 负责人;映射数据找 fms-fnd 负责人;模板/汉高鉴权找 data-flow 负责人;消息投递找 esb-broker 负责人;页面问题找对应前端负责人。

文档结束。建议把本文件放在团队知识库,并在下列场景更新:新增店铺、新增汉高接口、模板变更、xxl-job 调度调整、表结构变更。