新窗口接续用。核心:签名
X_SIG_1已破解;必须用curl_cffi发请求(requests会被 TLS 风控判为非真机、返回code:200空壳,curl_cffi才出code:1真数据);sessionId 靠脚本自登录拿。以上均已实测验证。域名https://api.jiwuplanet.com,全 POST+JSON。
每个请求带 6 个头:X_VERSION / X_TIME / X_SID / X_RAND / X_PLATFORM / X_SIG_1。
X_SIG_1 = base64_std( HMAC_SHA256( key, msg ) )
key = "EahycFpDpewggO2rksvQwTlnhCbTHFx6" # 生产环境密钥(逆向所得)
msg = path + "2.12.2" + "150" + sessionId + X_TIME + X_RAND + canonicalBody
path:URL 路径(不含域名/query),如 /search/app/index/topX_VERSION="2.12.2",X_PLATFORM="150"(常量)X_TIME:秒级时间戳字符串 str(int(time.time()))X_RAND:6 位随机 str(random.randint(100000,999999))canonicalBody:json.dumps(body, sort_keys=True, separators=(",",":"), ensure_ascii=False)——递归按 key 排序、紧凑、非 ASCII 不转义;实际发送的 body 就是这个串(与被签名内容完全一致)X_SID 头 = body 里 sessionId 字段 = 签名串里的 sessionId验证基准(用抓包固定参数复算,结果一致):
path=/search/app/index/top,X_TIME=1787045062,X_SID=ccab0d5917cd4e289d07b820e4497989,X_RAND=295778,body={"currentPage":"1","limit":"10","productType":"3","sessionId":"ccab0d5917cd4e289d07b820e4497989","systemBusinessType":5} → X_SIG_1 = tZuw8vRCz2DVb6RSLv0KBlKgcctmBCxi07SiDH3em+k=(与真实抓包逐字符相同)。
curl_cffi(impersonate="chrome")。pip install curl_cffi。proxies={"http":None,"https":None}(本机有 HTTP(S)_PROXY=127.0.0.1:7890 的 Clash,间歇开着,requests 默认走它会卡死)。code:200 + data.result:[] = 被风控/会话无效的空壳;code:1 + data.records[...] = 真成功。jiwu_core.do_request(..., need_auth=False) 默认免登录——用随机未绑定 sid 签名、不调 regLogin。实测免登录:在售 index/top、已售 corp/history、详情 merchantGoodsId(随机 sid 即出 code:1);需登录(need_auth=True):商家 hotRecommend、购买记录 publicity/user/group/pager、拆卡报告 gift/report(随机 sid 返 code:200 空壳)。regLogin 上报后服务端沿用该值。POST /acct/user/regLogin,成功 code==1,返回 data.sessionId。mobile=19521500850, loginPassword=pass2022, deviceNumber=40b03f4bec96675f(账号 ccc123, userId 21588067)。登录 body(自造 sid 填进去):
{"appVersions":"2.12.2","deviceNumber":"40b03f4bec96675f","identificationNumber":"19521500850","ip":"10.1.10.1","language":"ZH","loginPassword":"pass2022","loginType":"ACCOUNT","mobile":"19521500850","mobileCode":"86","platformInfo":"Google Pixel 5","platformType":"ANDROID_USER","sessionId":"<自造sid>","systemBusinessType":6,"terminalVersions":"11"}
登录 / 会话流程图(核心:无 token,客户端自造 sessionId;「签名是门票、sessionId 是身份」;三处 sessionId 头 X_SID/body/签名串须同值):
flowchart TD
A["do_request(path, body, need_auth)"] --> B{"need_auth?"}
B -- "False 免登录(默认)" --> C["get_anon_sid()<br/>随机自造 sid,不调 regLogin"]
B -- "True 需登录" --> D["ensure_session()"]
D --> E{"缓存 sid 未过期?<br/>SESSION_TTL=1800s"}
E -- "是" --> F["复用缓存 sid"]
E -- "否" --> G["login(): 自造 sid<br/>→ regLogin 绑定 → 写缓存"]
C --> H["sessionId 写入 body.sessionId + 头 X_SID + 签名串<br/>(三处同值)"]
F --> H
G --> H
H --> I["_sign(): X_SIG_1 = base64(HMAC_SHA256(key, msg))<br/>msg = path+版本+平台+sessionId+时间+随机+body"]
I --> J["curl_cffi POST(impersonate=chrome,直连)"]
J --> K{"code == 1 ?"}
K -- "是" --> L["成功,返回 JSON"]
K -- "否 · 免登录" --> M["轮换匿名 sid,下轮再来"]
K -- "否 · 需登录" --> N["清缓存 → 重登,最多再试 1 次"]
need_auth=False,默认):在售 index/top、已售 corp/history、详情 merchantGoodsId——匿名随机 sid,不暴露账号。need_auth=True):商家 hotRecommend、购买记录 publicity/user/group/pager、拆卡报告 gift/report——ensure_session 复用/续期真会话。python verify.py 应打印 code=1 + ccc123 + 真实商品)import hmac, hashlib, base64, json, time, random
from curl_cffi import requests as creq
KEY = "EahycFpDpewggO2rksvQwTlnhCbTHFx6"; BASE = "https://api.jiwuplanet.com"
NOP = {"http": None, "https": None}
def cb(o): return json.dumps(o, sort_keys=True, separators=(",", ":"), ensure_ascii=False)
def post(path, body, sid):
body = dict(body); body["sessionId"] = sid; b = cb(body)
xt = str(int(time.time())); xr = str(random.randint(100000, 999999))
msg = path + "2.12.2" + "150" + sid + xt + xr + b
sig = base64.b64encode(hmac.new(KEY.encode(), msg.encode(), hashlib.sha256).digest()).decode()
h = {"X_VERSION": "2.12.2", "X_TIME": xt, "X_SID": sid, "X_RAND": xr, "X_PLATFORM": "150",
"X_SIG_1": sig, "Content-Type": "application/json;charset=UTF-8",
"User-Agent": "okhttp/3.12.10", "Host": "api.jiwuplanet.com"}
return creq.post(BASE + path, headers=h, data=b.encode(), timeout=20, impersonate="chrome", proxies=NOP).json()
# 1) 自登录
sid = hashlib.md5(f"{time.time()}{random.random()}".encode()).hexdigest()
acct = {"appVersions":"2.12.2","deviceNumber":"40b03f4bec96675f","identificationNumber":"19521500850",
"ip":"10.1.10.1","language":"ZH","loginPassword":"pass2022","loginType":"ACCOUNT","mobile":"19521500850",
"mobileCode":"86","platformInfo":"Google Pixel 5","platformType":"ANDROID_USER","systemBusinessType":6,"terminalVersions":"11"}
j = post("/acct/user/regLogin", acct, sid); d = j.get("data") or {}; sid = d.get("sessionId") or sid
print("登录:", j.get("code"), d.get("nickName"), d.get("userId"))
# 2) 在售列表
j = post("/search/app/index/top", {"currentPage":"1","limit":"10","productType":"3","systemBusinessType":5}, sid)
dd = j.get("data") or {}; recs = dd.get("records") or []
print("在售:", j.get("code"), "total=", dd.get("totalCount"), "首条=", recs[0]["goodsName"] if recs else None)
| 板块 | path | 关键参数 |
|---|---|---|
| 在售 | /search/app/index/top |
currentPage,limit,productType(1福袋2变风3错版卡4原盒),systemBusinessType:5 |
| 商家(142家) | /search/app/index/corp/hotRecommend |
currentPage,limit,systemBusinessType:6 |
| 已售 | /search/app/corp/history |
corpInfoId,currentPage,limit,systemBusinessType:5 |
| 商品详情 | /search/app/merchantGoodsId(免登录) |
goodsId,systemBusinessType:5。监控判结束用。⚠️ 库存字段=stock(总)/surplusStock(剩余)、价格=price/highestPrice,与 index/top 的 stockAmount/residueStockAmount 不同名。(返回里 goodsBuyRspDtoList 只有 6 条预览,不是全量购买记录) |
| 购买记录 | /order/merchant/app/query/gift/publicity/user/group/pager(赠品公示·玩家维度,需登录) |
currentPage,giftBusinessName:"",goodsId,limit,systemBusinessType:5。每条=一个买家 userId/userNick/picId/count,翻页拿全量(如 1660823 共 18 人)。不 DB 去重,靠已售表 buy_fetched 状态位控制每商品抓一次(取全→批量入库→置位)。 |
| 拆卡报告 | /goods/gift/report/query/search/pager(需登录,sbt=6) |
goodsId,currentPage,limit。字段:giftReportId(唯一递增)/serialItemName(开出卡名)/resAddrList/winnerStatus/anonymousStatus/userId(有值)/corpId/corpName/goodsName/username(脱敏)/createTime/myGiftStatus。已售商品可事后翻页查全量 |
core.to_yuan 换算成元存 DECIMAL(12,2),库里/报告直接是元;监控读实时接口(原始值)仍 ÷1000000 展示。ls/python xxx.py 判断文件在不在(会误报"文件不存在")。跑脚本让主公在本机跑,或用"关闭沙箱 + 同一条命令内 heredoc 建+跑"。decompiled/(jadx,包名 com.jiwu.star),签名逻辑在 com/jiwu/star/api/LogInterceptor.java;密钥在 EnvironmentManager,HMAC 在 HmacUtils.hmacSHA256Base64。b8d398e2-f27e-42ce-af78-336867460122)。D:\work\2026-08-02(deca_spider)——整个流程/产出/逻辑与之一致,只是加解密不同。报告都是 openpyxl 生成 Excel 走 send_wechat_group_file 发文件(不是 markdown);已售报告里重点商家各占一个独立 sheet。| 板块 | 脚本 | 说明 |
|---|---|---|
| 1 在售抓取 | jw_onsale_spider.py |
免登录全量翻 index/top upsert jw_onsale_product_record+下架对账;同步写每日快照 jw_onsale_daily_record(商品+日期 upsert 保最新,供在售趋势差分);每天 09/15/20/01 四档 |
| 2+3 已售+购买记录+拆卡报告 | jw_sold_spider.py |
hotRecommend(需登录)商家→corp/history(免登录)逐商家只增 jw_sold_product_record;再对全站已售商品抓:购买记录=赠品公示玩家维度 publicity/user/group/pager(需登录, 翻页拿全量, fetch-once)落 jw_player_record;拆卡报告 gift/report(需登录, 结束 REPORT_REFETCH_DAYS 天内每轮重查、INSERT IGNORE 去重)落 jw_report_record;每天 08:00(配合已售报告业务日窗口, 06:00 后再采) |
| 4 每日报告 | jw_onsale_report.py(09/15/20/01 四档) + jw_sold_report.py(09:10) |
只读库→Excel→企微发文件;共用样式库 jw_report_excel.py(openpyxl)。在售每档各出一份带小时文件、含「在售趋势」sheet(快照差分);已售按业务日窗口 [昨17:00,今06:00]、COALESCE(finish_time,soldout_time) 过滤,Jake/九叔 各独立 sheet(汇总+明细+购买人数),金额已是元 |
| 5 在售监控 | jw_onsale_alert.py |
手写轮询(默认全天、每轮随机 60~90s),全量翻 index/top(免登录) 按 corpInfoId 过滤 Jake(100716)/九叔(100715);三类告警 新品上架/进度过半(≥50%)/一车结束,去重靠 jw_onsale_alert_record 三标记位 + 内存 _seen_onsale;企微 markdown 推送 |
openpyxl(报告用)已装。运行=5 个常驻进程各自 python xxx.py(在售/已售/两报告/监控)。schema 共 7 张表(在售商品/已售商品/商家/购买记录/拆卡报告/监控告警/在售每日快照 jw_onsale_daily_record)。detail_fetched;已售表加 buy_fetched;jw_player_record 改玩家维度买家、无唯一键靠状态位去重;新增在售每日快照表 jw_onsale_daily_record)。CREATE IF NOT EXISTS 不会改已存在的表,故建议 DROP 掉全部 jw_ 表后 python init_db.py 重建(只加新表可直接重跑 init_db)。hotRecommend(商家列表)、publicity/user/group/pager(购买记录)、gift/report(拆卡报告) need_auth=True;在售/已售/商品详情全免登录。jw_player_record(COUNT DISTINCT user_id);监控结束战报「购买人数」=玩家维度接口 totalCount(实时准确)。merchantGoodsId 用 stock/surplusStock/price/highestPrice,别用 index/top 的 stockAmount/residueStockAmount(监控 confirm_ended/send_ended 已按详情字段名修正)。jw_detail.enrich_detail(merchantGoodsId, 免登录) 逐商品补,各表 detail_fetched 状态位控制每商品补一次(在售 upsert 不覆盖这些列)。img = FILE_DOMAIN + resList 首图 resAddr,FILE_DOMAIN=https://files.jiwustar.com/(逆向 EnvironmentManager 正式环境);collection_card_name/gift_way/random_way 在详情 cardGoods 子对象。soldTime(上架/开售时间) ≥ 本场窗口起点(最近一个 RUN_START) 判新(is_new_arrival/_window_start)。若实测 soldTime 语义不同,改这两处。/search/app/merchantGoodsId 详情,surplusStock<=0(售罄) 或 已过 offShelfTime(下架/销售结束时间) 判结束(confirm_ended)。若 offShelfTime 语义不同,改此处。企微素材上传异常: ... SSLEOFError(8, '[SSL: UNEXPECTED_EOF_WHILE_READING]...')(onsale_report_20260827.log:14、sold_report_20260827.log:12)。qyapi.weixin.qq.com:443 秒连 TLSv1.3)。是上传 Excel 素材(upload_media)时到腾讯的 TLS 连接偶发被中间网络设备掐断(OpenSSL 3.0 把「对端没发 close_notify 就断开」从静默 EOF 改成硬报错 UNEXPECTED_EOF_WHILE_READING)。放大器:auto_send_wx_msg._upload_media 原为裸 try/except,一次瞬断就 return None 整份放弃、零重试,两轮恰好都撞上。auto_send_wx_msg.py):按骨架规范加 tenacity 重试。新增 after_log 回调 + 两个带 @retry 的内部函数 _do_upload_media(文件上传)/_post_json_with_retry(文案/文件消息);策略 = 仅对 RequestException(网络/TLS)重试、间隔 2→4→8s 递增、最多 4 次,业务错误码(errcode≠0)不重试直接失败。关键:上传函数把 open 放进被重试的函数体内,每次重试重开文件(multipart 流读过就到 EOF,不重开会传空体)。发送目标/业务逻辑/对外签名不变。b8d398e2)文案+文件 errcode 均 0,通过。依赖:tenacity(已装)。auto_send_wx_msg.py:25 的 WEBHOOK_URL(现指测试群,正式群 key=2d41b34f 已注释在下一行)。run_all.py(主公要部署服务器):把 5 个常驻任务(在售抓取/已售抓取/在售日报/已售日报/在售监控)各拉成独立子进程并守护——巡检存活、退出的隔 5s 自动重启、Ctrl+C 优雅关闭。服务器只跑 python run_all.py 即可(各任务仍各自维护 schedule/日志、互不干扰;单跑调试仍可 python jw_xxx.py)。README §7 运行已改为推荐 run_all。jw_onsale_report.build_overview 加指标「今日累计已售(份)」= SUM(sold_count) WHERE is_on_sale=1;改用 指标|数值 表头(write_table)替代原标题条;「今日新上架商品」→「今日新增商品」。⚠️「今日新增商家」今天因重建商家表(所有商家 gmt_create_time=今天)暂显全量 82,以后不再 DROP 重建商家表即恢复为当天真正新增。gift_series 由表末尾 ALTER MODIFY ... AFTER goods_ip_name 移到 IP名 后(两商品表);schema 同步。纯物理列序、不动数据。goods_ip_name(goodsIPName,只是 IP 大类如"球星卡/宝可梦"),但 App「赠品详情/赠品系列」页显示的系列是 cardGoods.giftSeries(如"2025-26 Topps Chrome Updates Hobby")。giftSeries 只在商品详情(merchantGoodsId)里。修复:两商品表加详情列 gift_series(schema + ALTER 现有表);jw_detail DETAIL_COLS 增 gift_series、parse 取 cardGoods.giftSeries(兜底顶层 specifications);已售报告 产品系列榜按 gift_series 分组、明细「系列」列用 gift_series;在售报告 商品明细「IP系列」列改为「系列」用 gift_series。已 reset detail_fetched 重跑 enrich_detail 回填(已售 123/123 到位)。注:gift_series 靠详情补全,新商品由 enrich_detail 自动填、无需改 parse。jw_sold_report._sold_gmv 原用 sold=stock−residue,但已售历史 corp/history 的 residueStockAmount 对成交(组齐)团恒 = 总份数(实测全 123 行 residue==stock,且 SUM(购买记录 buy_count)==stock_amount)→ 算出 sold=0、GMV=0。改为 已售团 sold=stock_amount(成交即全售出)。GMV/人均/占比/系列榜/GMV榜/集中度/进度% 全部随之修正。fetch_corp_distinct_buyers/fetch_platform_distinct_buyers(JOIN 购买记录 COUNT DISTINCT user_id),平台/各商家汇总均用跨团去重人头;人均消费=GMV/人头。明细列「参与人数(本团)」仍用 buyers_map(每团去重)。parse_shop 原字段名写错(hotRecommend 无 corpName/fansAmount/goodsAmount 等)→全 NULL。查清 hotRecommend 记录字段实为 corpInfoId/corpInfoName/number(粉丝数,主公确认)/saleNum(在售商品数,与在售表吻合)/corpLogo;userId 是查询者本人账号id(每行恒等=21588067)、非商家id,不存;好评/新品/简介 接口体系根本无(CommonShop 模型也没有)。
jw_shop_record 最终定为 corp_info_id/corp_name/fans_amount(number)/onsale_amount(saleNum)(中途曾一度精简掉 fans_amount,后确认 number=粉丝又加回)。parse_shop/get_shops/schema 已改、DROP 重建回填。在售报告「今日新增商家/其他商家」两 sheet 列 = 商家|粉丝数|在售商品数|今日新上架(粉丝取 shop 表、在售数/新上架从在售商品表 JOIN)。get_shops 末页判定原为 len(recs) < PAGE_LIMIT(20),但 hotRecommend 服务端每页固定只回 10 条(响应 data.limit=10,无视请求的 limit=20)→ 第 1 页 10<20 就误判末页停了,只入库 10 个商家。改为用响应里的实际每页大小 data.limit 判末页(len(recs) < data.limit 才停)。实测全量翻页 82 个商家(9 页)、其中 29 个有在售。已重跑 get_shops 入库 82 家(粉丝 Top: Jake 426/卡愣子潮玩 184/棠棠潮玩 80…)。main_task 原为「corp_ids = get_shops() 返回啥就循环啥」(只本轮 hotRecommend 返回的)。改为 get_shops() 先刷新商家表,再 SELECT corp_info_id FROM jw_shop_record 取全量商家循环抓已售(受 TARGET_CORPS 可选过滤)。理由:hotRecommend 每天轮换只返一批热门商家,jw_shop_record 随天数累积覆盖更全;对全量商家跑 corp/history(免登录)才不漏。代价:每轮多几十个 corp/history 请求(多数无已售、秒回空),免登录无账号风险。liveReplayUrl 是相对路径 /live/xxx.mp4,需拼直播域名 https://play.jiwustar.com(逆向 EnvironmentManager RELEASE 的 BASE_PULL_STREAM_URL=https://play.jiwustar.com/live/,路径已含 /live/ 故只拼 host)。新增 jw_sold_spider.full_live_url(),parse_sold 用它;库内历史 123 条已 UPDATE ... CONCAT('https://play.jiwustar.com', live_replay_url) WHERE live_replay_url LIKE '/live/%' 回填、残留 0。jw_onsale_report.build_products 的 PRODUCT_TYPE_NAME.get(ptype,...) → get(str(ptype),...),与已售统一,防 product_type 为 int 时显示数字码。corp/history 实测仅 Jake(100716) 有 123 条已售,九叔(100715)/羽佳/其他 totalCount=0 → 已售报告只有 Jake、九叔明细/其他商家空是真实数据,非漏抓。② hotRecommend 是「热门推荐」商家、每页 10 条需翻页(全量约 82 家,见翻页修复条);不含好评/新品/简介(粉丝=number 有)。sold=stock−residue(在售剩余是真实值,口径对)、GMV/占比/人均/环比除零护栏、窗口边界含两端、金额=单价映射、用户榜 SUM(buy_count×amount)、col_types 与列数对齐、pct 传 0~1。全表 NULL 审计:已售/购买记录/拆卡报告各列均正常填充(仅 pic_id 匿名买家空、finish_time 今日团 25 条空但 soldout_time 兜底)。jw_gift_report_record → jw_report_record。同步改:schema.sql(CREATE TABLE,索引名 uk_gift_report_id/列名 gift_report_id 跟列走、不动)、jw_sold_spider.py(insert_many 目标表 + 重查去重 SQL + 顶部注释/docstring)、README.md、本文档。需重建库(DROP 旧表后 python init_db.py)。jw_buy_record → jw_player_record。同步改:schema.sql(CREATE TABLE,索引 idx_goods/idx_user 不含表名、不动)、jw_sold_spider.py(insert_many 目标表 + 注释/docstring)、jw_sold_report.py(购买人数 COUNT DISTINCT SQL + docstring)、README.md、本文档。需重建库(DROP 旧表后 python init_db.py)。jw_onsale_monitor.py → jw_onsale_alert.py(与表名 jw_onsale_alert_record 配对、贴合"上架提醒"交付物)。纯文件重命名,无其它 .py import 它(独立常驻脚本直接跑);模块 docstring 未自指文件名、不用改。同步改:README.md(目录树/板块表/配置项/待实测项)、本文档。不涉及库变更。jw_sold_report.py 的 WIN_SOLD 从 DATE(gmt_create_time)=CURDATE() 改为 COALESCE(finish_time, soldout_time) 落在 [昨17:00, 今06:00](含两端),对齐 deca 的 completed_at 口径;平台总览新增「统计窗口」展示。配套:已售采集 jw_sold_spider.py 定时 00:30 → 08:00(窗口 06:00 关闭后再采、报告 09:10 前齐全),并清掉 fetch_sold_for_corp 里调试用的 print(j)、把 schedule_task 里启动即跑的 main_task(log=logger) 注释回去。jw_onsale_spider.py/jw_onsale_report.py 定时都从单点改成 09/15/20/01 四档(上午/下午/晚上/凌晨场);报告每档各出一份带小时文件名(_%Y%m%d_%H时)。新增每日快照表 jw_onsale_daily_record(唯一键 goods_id+snapshot_date,四档 upsert 只刷量价、保留商品/商家/日期),采集每页 upsert_daily_snapshot 同步写入;报告加「在售趋势」sheet:fetch_onsale_trend 按快照集合差分(当日集合−前日集合=净新增商家/拼团),列头 日期|在售商家数|在售拼团数|新增商家数(差分)|新增拼团数(差分),倒序最近 4 天。jw_onsale_daily_record 已进 schema.sql,重跑 python init_db.py 即可(CREATE IF NOT EXISTS 只补新表)。同步改 README(板块表/表清单/运行命令/DROP 列表)。jw_onsale_report.py 新增两 sheet——「其他商家」(存量商家 gmt_create_time!=CURDATE(),商家表 LEFT JOIN 在售表,列 商家/粉丝数/在售商品数/今日新增商品,在售数倒序 LIMIT 100) 和「上架时段分布」(按 HOUR(sold_time) 24 桶、近 7 日,带 █ 条形)。最终顺序:概览/今日新增商家/其他商家/商品明细/在售趋势/上架时段分布。已实测生成 6 sheet 通过。jw_onsale_alert.py,得卡 onsale_alert_spider.py 2026/08 改版):
_window_start();删除 NEW_ARRIVAL_LOOKBACK_MIN。confirm_ended 由「仅 surplusStock≤0」改为「surplusStock≤0 OR 已过 offShelfTime(下架/销售结束时间)」,二者任一即结束。RUN_ALL_DAY 由 True 改 False,对齐 deca 窗口制 [20:30, 06:00];RUN_START 同时是新品判定门槛。如集物直播时段不同,改 RUN_START/RUN_END 或设回 RUN_ALL_DAY=True。argparse):_parse_start_time(校验 HH:MM/HH:MM:SS→规范化 HH:MM) + _parse_args(位置参数 start 与 --start 等价、位置优先);__main__ 里不传保持默认 RUN_START,传了则 RUN_START=_start 覆盖模块全局(_window_start/_in_run_window/_seconds_to_next_window 三处裸读该全局,一改齐变)。用法:python jw_onsale_alert.py 17:00 或 --start 17:00。已实测 --help/校验/非法格式报错通过。_seen_onsale去重、发成功才置位/过半先置位、免登录全站取数、企微 markdown 不挂链接)集物本就与 deca 一致,未动。jw_sold_report.py 全量重写。sheet:①平台总览(KPI+当日环比vs昨日同窗口 WIN_SOLD_YDAY+商家GMV集中度Top1/3/5/10) ②产品系列榜(goods_ip_name,Top15) ③商家GMV榜(Top10) ④运营节奏(重点商家快照:已售团数/参与人数/规格分布 + 成交时段分布 HOUR(COALESCE(finish,soldout)) 24桶近7日) ⑤Jake明细(汇总+逐团明细+购买记录覆盖检测 buy_fetched) ⑥Jake用户排行榜(jw_player_record join 已售表算金额) ⑦九叔明细 ⑧九叔用户排行榜 ⑨其他商家。GMV=已售数×单价元、已售数=stock-residue、参与人数=jw_player_record 去重 user_id。deca 的「进度25/50/75%里程碑」列集物无进度轨迹、略去。已实测 9 sheet 生成通过。同步改 README。stats/得卡已售每日报告_*.xlsx + stats/daily_report.py:988-1210 build 代码逐 sheet 比对,首版是「重新设计」标签对不上、返工):标题「集物星球 · 已售每日统计报告」+「成交时间窗」;平台汇总用 deca 标签(商家数/销售额/成团数/参与人数/均拼单价/人均消费);环比行(组齐GMV/成团数/活跃商家数/T均单价);运营快照(今日新开团/已组齐/规格分布,新开团=sold_time 落窗口内);明细列(序号|团名(商品标题)|系列|类型|单价|总份数|进度%|总金额|参与人数(本团)|开售时间|成交时间|售卖时长);覆盖检测用「成交X团·采到Y团·漏采Z团(用户排行见「…」sheet)」+漏采逐行「 - {goods_id} {goods_name}」。两处有意差异:集物两重点商家都有真实购买记录→参与人数全用真实去重买家(deca 非重点商家用中卡近似)、其他商家亦然;集物无进度轨迹→去掉 25/50/75%里程碑列。售卖时长=成交时间-开售时间「X小时Y分」。已实测发测试群通过。write_section_title 用合并单元格——合并区内文字无法溢出,列窄(尤其空数据 autosize 压到最小)时长标题被切在合并区右边界("占平台组齐总 ("、"当日 Top15,"都是这么被切的;公式栏文字其实是全的,纯显示截断)。修法照抄 deca _write_section_title/_style_row:改为「不合并 + 给若干列铺蓝底(空单元格) + 文字写 A 列不换行」,让文字自然溢出到右侧有色空单元格上(非合并单元格文字能无限溢出、永不截断);铺色跨度固定 = 调用方给的 ncols(同一 sheet 内所有分区标题条传同一 ncols → 色条等长、严谨;之前用随标题长度自适应的跨度导致同 sheet 内长短不一被主公吐槽,已改)。为保证窄表(空数据 autosize 压窄)时色条仍罩住长标题:_autosize 改「只增不减」(不覆盖预设更宽列宽),并给 build_series/build_merchant_rank 首列预设 24/22 宽。已用脚本全 sheet 复检:各 sheet 标题条 span 统一 + 每条色条宽 ≥ 标题显示宽(全部罩住)。do_request 决策链(免登录匿名 sid vs 需登录 ensure_session/login → 三处 sessionId 同值 → _sign 签名 → curl_cffi 发 → code==1 判定/失效重试),并列出免登录/需登录各接口与"最小登录面"动机。纯文档。另:新增 write_title()(大标题不合并不填色、自然溢出);build_overview 大标题改 write_title、"成交时间窗"改纯文本行(不再当 KV 挤窄 B 列折 4 行)。KV 蓝底标签/橙底数值保留(deca _write_summary_block 就是这配色)。注:样式库在售/已售共用,两报告同时生效。数据全 0 属正常:已售表 60 行成交时间在 08-17~08-19 白天、不落今天窗口 [昨17:00,今06:00],非 bug。÷1000000=元(实测 amount=99000000→App ¥99.00),原文档写的 ÷10000 是错的。且入库前用 core.to_yuan 换算成元存 DECIMAL(12,2)(parse_product/parse_sold/parse_detail_fields),报告 yuan() 改透传不再除;监控读实时接口(原始值)仍 ÷1000000 展示。/order/merchant/app/query/gift/publicity/user/group/pager(赠品公示·玩家维度,需登录),翻页拿全量买家(userId/userNick/picId/count)。曾误用详情内嵌 goodsBuyRspDtoList(仅6条预览)和幻影接口 publicity/pager,均已废弃。jw_player_record 无唯一键,靠已售表 buy_fetched 状态位「取全→入库→置位」控制每商品抓一次。gift/report(需登录,sbt=6) 落 jw_report_record;全站已售、结束 REPORT_REFETCH_DAYS(3)天内每轮重查(INSERT IGNORE 补迟到更新)。jw_detail.py,两商品表补 12 个详情列 + detail_fetched(img=FILE_DOMAIN+resList首图;FILE_DOMAIN=https://files.jiwustar.com/;collection_card_name/gift_way/random_way 在 cardGoods)。do_request(need_auth=False) 默认免登录;仅 hotRecommend/购买记录/拆卡报告 需登录。jw_onsale_spider)独立日更;购买记录+拆卡报告并入已售(jw_sold_spider)。曾短暂把在售+购买记录合并成 jw_onsale_buy_spider,后因购买记录可对已售事后查全量而拆回、该文件已删。jiwu_core main 自测第 3 项改玩家维度接口;相关 docstring/注释)。全量 py_compile 通过、无悬空函数引用。fetch_watch_onsale 每 60~90s 全量翻 index/top 4 个 productType 的所有页——全站在售量大时请求数偏高、有风控风险。可选优化:加大轮询间隔 / 只翻前 N 页(新品通常靠前) / 找更轻的按商家在售接口。