huangzd1997
07b4ebe983
feat(成本): 密钥检测防抖 + 巡检降频隔日 + 客户端 4.0.16 更新日志
...
- 用户密钥「检测」90 秒新鲜期:同值重复检测复用上次通过结果,
代理提取不再因连点/手滑重复扣费(只缓存 passed,失败允许立即重试)
- 连通性巡检由每日降为隔日(cron 0 30 4 */2 * *,双实例锁不变)
- changelog 追加 4.0.16 条目
2026-09-14 19:43:22 +08:00
huangzd1997
0b2b9303d0
chore(前端): 追加客户端 4.0.15 更新日志条目
2026-09-14 18:10:48 +08:00
huangzd1997
05a2c479a5
fix(测试): CollectDataNoUploadStaleTest 的 updateById 重载歧义(补编译验证)
2026-09-14 18:00:07 +08:00
huangzd1997
52b55df7b2
feat(任务判死): 心跳正常但 180 分钟无结果上报的二次判死线(13 模块)+ 同期待发改动
...
判死线(治 28131 型「主线程卡死、心跳线程照发」):
- 判据改看 biz_task_scope_state.last_chunk_at(HTTP 心跳不刷新它);从未上报跳过不判
- 中央线覆盖 DELETE_BRAND/PRODUCT_RISK_RESOLVE/PRICE_TRACK/SHOP_MATCH/PATROL_DELETE/QUERY_ASIN/WITHDRAW
- 自带线接入 COLLECT_DATA/SIMILAR_ASIN/APPEARANCE_PATENT/SHOP_DATA_CRAWL/PUBLISH/BRAND
- 客户端心跳带处理位置 progressText,判死文案含最后位置;no-result-upload-timeout-minutes 默认 180(0 关闭)
同期带上另一工作流的待发改动:跟价换 IP 重试、品牌检测重试上限与 LLM 并发下调、
教程包后台管理页与 V126 迁移、admin-vue 教程记录页。
2026-09-14 17:56:20 +08:00
huangzd1997
b54f72d3f6
fix(品牌检测): 熔断中止落真实原因+部分结果可下载,熔断加持续时长门槛
...
- 熔断改为「连续失败持续 6 分钟未恢复」才中止:原 8 次即中止,5 线程一轮
就能凑满,重跑机制没机会生效导致任务失败率过高(任务 2309 复盘)
- 期间每 30s 冷却重试,重跑轮次 10→30;限流窗口恢复后任务自动跑完
- 失败终态上报内部接口 /api/internal/brand/tasks/{id}/abort:落真实原因 +
用已收分片部分组装结果(未检测品牌单独成 sheet),不再悬挂到心跳超时被
判「前端长时间无响应」且已跑数据无法下载
- 组装前从分片重建聚合(缓存快照可能缺后加字段如 keptBrands)
- 前端:失败任务有结果文件即显示「下载结果」
2026-09-14 17:04:19 +08:00
huangzd1997
24c5a09c7f
feat(任务派发): 客户端兜底拉取页面未推送成功的任务 + 修店铺匹配定时任务误杀
...
问题:任务派发链路的"推送"只存在于页面里(Java 解析落库 PENDING → activate →
pywebview 桥 enqueue_json 推本机队列)。只解析没点启动、推送前关页面、在纯浏览器
打开,任务都会停在 PENDING,2 小时后被 StaleTaskRepairService 标失败
(「任务长期未被领取,已自动失败」,09-11 生产清理过 263 条同画像)。
- 服务端新增 GET /api/tasks/pull-pending(TaskClientPullController,身份从 JWT 取,
不接受 user_id 参数):只挑创建超 5 分钟仍 PENDING 的本用户任务,逐条条件更新认领
(PENDING→RUNNING + 接管 owner_instance_id)——与页面 activate 同一谓词,天然互斥,
不会重复执行;认领后组装不出载荷则标 FAILED,不留 RUNNING 孤儿
- payload 由各业务模块实现 ClientTaskPullSpi 组装(task 侧不 import 业务模块,同 G5):
首批 SIMILAR_ASIN / COLLECT_DATA / APPEARANCE_PATENT——这三个 Python 消费端会自行
回拉明细,故载荷极简、客户端零模块知识;开关 aiimage.client-task-pull.enabled 默认 false
- 防双执行:三处 activate 由「非终态即可」收紧为只认 PENDING,未命中抛
「任务已在执行中(可能已由客户端自动接管),无需重复启动」(顺带堵住整行 updateById
把认领写入的 owner 覆盖回去的竞态);两个前端页 activate 失败即提示并停止入队
- 客户端:amazon/main.py 新增 pending_task_pull_worker,启动点挂在 app_client/main.py
的 start_task_monitor(独立入口的 worker 线上并不生效);开关 pending_pull_enabled 用
getattr 读取,避免 test/ 下的旧 config 缺键导致整包导入失败
- 同批修:StaleTaskRepairService 的 SCHEDULED 分支改按 scheduled_at + 120min 判死
(原按 updated_at 会必杀排期 >2h 的店铺匹配定时任务,而 activate 又被 scheduledAt-90s 挡住)
- 测试:TaskClientPullServiceTest / StaleTaskRepairServiceTest / CollectDataTaskPullSpiImplTest /
CollectDataActivateGuardTest 共 20 例;客户端 pending_task_pull_worker 7 例并更新启动顺序契约测试
2026-09-14 16:23:34 +08:00
huangzd1997
8cab9d4bad
fix(密钥配置): 保存沿用刚检测过的输入值结果,消除「三项检测通过却提示未检测」死循环
...
- 服务端:对「输入值(未保存)」的检测结果按 uid+模块+值指纹暂存 Redis(TTL 30min,
Redis 异常降级为需重新检测,不阻断保存);保存同一个值时落库该结果
(passed/failed/error 一并沿用),改过值或从未检测则维持未检测
- 前端:保存后清理本地「输入值(未保存)」绿字,展示统一走服务端快照,避免展示与门禁矛盾;
门禁提示改列「模块名(掩码):未检测 / 检测失败:原因 / 未配置」,同名掩码也能分辨模块
- 测试:UserApiSecretServiceTest 补沿用 / 值不一致 / Redis 降级用例;
新增前后端一致性守卫测试(保存后必须清检测结果、提示必须带模块名)
2026-09-14 13:49:25 +08:00
huangzd1997
5ea52e5291
perf(F5+): 行数据按需拉取(结果行版本信号)+ 修复 progress/light 恒判 missing
...
行数据按需拉取(审查 F5 后续):
- V125 给 biz_file_result 补 updated_at(DEFAULT/ON UPDATE 由数据库维护,
实体标注 insertStrategy/updateStrategy=NEVER —— 否则 selectById→updateById 的
写回会把旧值写回去、ON UPDATE 不触发,版本信号静默冻结)
- 装配器回传 rowsVersion=「最后变更时间毫秒#行数」,5 个品牌工具页版本未变即跳过
带行明细的重型 batch;前端变更信号为 rowsVersion + status/fileStatus/fileReady 复合
(任务收尾常见「行早写完、之后才置成功」,只看行版本会把界面卡在旧状态)
修复线上缺陷(同一功能验证时暴露):
- TaskProgressLightAssembler 列裁剪漏选 module_type 却用它做模块过滤 →
getModuleType() 恒为 null → light 恒把任务判成 missing;第七批把 light 接进
跟价/定时匹配/商品风险的轮询后,消费方会把运行中任务判为 FAILED
- 补选中列 + 守卫用例 taskQueryMustSelectModuleType(已反向验证:去掉修复即红)
- 前端 lightClaimsAllTasksMissing:整体性 missing 结论用重型端点复核后再采信
契约与文档:light 白名单补 rowsVersion(Java 契约测试 / spec 06 §2 / 12 个端点描述)
测试:mvn test 2901 全绿;前端 npm test 765 全绿
2026-09-14 12:08:25 +08:00
huangzd1997
9166656673
perf(C6): 去重总数据列表顺序翻页改 keyset(前后端契约一起改)
...
背景:列表页 `ORDER BY id DESC LIMIT offset,size`,带筛选且选择性低时每页都要
对命中集做一次 filesort;深翻页 offset 也白扫索引。
改法(保持跳页/回退/改每页的原有行为):
- 后端:page 接口新增可选 `last_id` 游标 —— 传了就 `id < lastId` + `LIMIT size`(无 offset),
响应新增 `nextLastId`(本页最后一行 id);不传仍是原 OFFSET 分页
- 管理前端:只有"下一页"用游标(上一页响应带回),跳页/改每页/筛选清空游标走 OFFSET
- 测试:新增 2 个后端契约测试(keyset 无 offset + nextLastId;无游标保持 LIMIT 30,15)
验证:mvn test 2897 全绿;admin-frontend-vue vue-tsc 通过 + 1619 测试全绿;
已部署 JAR 2b9ab774c60c3f5d802c9a2cee549008(双节点 health=200)与 admin-vue-20260914-112055。
2026-09-14 11:21:40 +08:00
huangzd1997
1360a44e01
fix(web): 货源查询/外观专利入队不再把 api_key 当必填
...
密钥已服务端化,本机无明文属正常状态,Java 侧 readApiKey 会按 uid 从用户密钥表兜底;
列成必填会把未在本机手输密钥的用户全部挡在启动之前。
2026-09-14 11:21:09 +08:00
huangzd1997
86c05e71a2
fix(web): 补齐命令式调用的 EP 按需样式,修拦截弹窗贴到文档左上角
...
Element Plus 走按需引入,只有模板里用到的组件才会注入样式;ElMessage /
ElMessageBox 都是 import 后函数式调用,构建产物里一条 .el-message-box 规则都没有,
弹窗因此没有定位与遮罩,渲染成普通块元素压在页头文字上(ElMessage 提示条同样受影响)。
入口显式引入两个样式模块,并加测试钉住「显式 import 的 EP 组件必须有样式引入」。
2026-09-14 11:21:08 +08:00
huangzd1997
fae26aa460
style(version): 指定版本下拉只显示版本号,去掉发布日期
2026-09-14 11:08:07 +08:00
huangzd1997
8803e22f39
feat(version): 桌面端更新面板支持指定版本安装
...
- 新增公开接口 GET /api/version/list(最近 50 条,字段口径同管理端列表),
桌面端下拉不再拼 OSS 地址(版本包被删后拼地址会 404)
- 更新面板(登录页 + 首页)加「指定版本更新」选择器:默认选中最新版,
可选全部已发布版本(含回退),回退/同版给出明确文案与二次确认
- 客户端无需发版:do_update_app(file_url) 本就接受任意版本包直链
2026-09-14 11:05:30 +08:00
huangzd1997
375b89154b
perf(C5): 外观专利服务解析改走流式解析器(最后一条活路径)
...
AppearancePatentTaskService.parseWorkbook 原为 POI 全量 DOM(用户源文件整表入堆);
改为复用已有的流式孪生实现 AppearancePatentExcelParser(EasyExcel SAX,语义一致且自带单测),
服务侧只保留原有业务处理(分组键/状态过滤/字段补齐/hydrate)。外观专利模块测试全绿。
至此:用户源文件的解析已无 DOM 路径(剩余 DOM 仅用于报表模板写入,样式/图片/公式必须 DOM)。
2026-09-14 10:58:59 +08:00
huangzd1997
6a90adc765
feat(A1/A3): 三个客户端零调用前缀立即无条件收紧
...
实测桌面端 Python 侧调用面:/api/image-video、/api/task-file-jobs、/api/ziniao 为 0 次调用
(只有带 JWT 的网页端在用),因此从 user-tool-guard-enabled 开关名单移入无条件守卫名单,
不必等客户端铺开即完成收紧;其余 14 个前缀的客户端调用面与令牌携带情况已逐一实测,
仍在开关后面(老客户端不带身份,提前打开会 401)。
新增 2 个守卫测试:开关关闭时这三组前缀对匿名同样 401、带用户令牌仍放行。
2026-09-14 10:46:05 +08:00
huangzd1997
bcf66dc1d7
fix(startup): 两个 GroupDeletionGuard 显式命名 + Bean 名唯一性守卫测试
...
事故:2026-09-14 边界收敛新增 invalidasin/dedupe 两个同名 GroupDeletionGuard,
Spring 默认用简单类名做 Bean 名 → 启动抛 ConflictingBeanDefinitionException,
主机 A 的 java-server 连续重启失败(health 不通),**单测全绿也发现不了**
(不起完整 Spring 上下文),部署后才知道。
- 两个守卫分别显式命名 @Service("invalidAsinGroupDeletionGuard") / ("dedupeGroupDeletionGuard")
- 新增 SpringComponentBeanNameUniquenessTest:扫描 main 源码,同简单名的组件必须显式命名,
否则红测试(把这次事故固化成可回归的守卫)
- 已重新打包部署:JAR 1f1e82bda227f2b0768574f8fd7c32c9,双节点 health=200
2026-09-14 10:37:44 +08:00
huangzd1997
c55c4a140b
feat(A1/A3+C8): 客户端令牌链路打通(按人鉴权就绪)+ 快照 JSON 写入节流
...
A1/A3 客户端令牌链路(服务端守卫已能按 JWT 鉴权,缺口在客户端不带身份)
- 前端:新增 user-token-bridge(纯逻辑,值变化才推送)+ user-token-sync(启动安装,
pywebviewready/storage/60s 轮询补推),main.ts 接入;桥接口补 set_user_token
- 客户端:新增 crawler_core/user_token.py(持有 + 对自家 Java 端点注入
Authorization: Bearer <jwt>,Session.request 包装,第三方域名不注入、已有头不覆盖、
登出清空、SHUFUAI_DISABLE_USER_TOKEN_HOOK 可关);main.py 暴露桥方法并在启动安装钩子
- 服务端:守卫契约测试补 5 个用户态前缀用例(开关关闭放行 / 打开后匿名 401 /
用户令牌通过 / 内部令牌仍放行 / /api/ziniao 第二层)
- 翻开关的前置条件(客户端铺开后置 aiimage.security.user-tool-guard-enabled=true)写入报告
C8 快照 JSON 写入节流
- 读端改以 biz_task_result_item 行为准、JSON 仅兜底(历史任务 JSON 仍是唯一副本时可用)
- 整档 JSON 改 30s 节流写,终态路径 force 立即写;新增 2 个契约测试钉住语义
验证:mvn test 2888 全绿;npm test 741 全绿;user_token 钩子自测(注入/归一/第三方跳过/不覆盖/登出)通过
2026-09-14 10:20:15 +08:00
huangzd1997
7643094f1d
refactor(boundary): 跨模块循环依赖清零(8 对 → 0),task→业务 依赖归零
...
共享内核下沉(跨模块共享的"身份/组织/安全"类型进 common)
- AdminUserEntity/AdminUserMapper、ShopManageGroupEntity/ShopManageGroupMapper → common
- AdminAuthSupport(33 文件 17 模块引用)、JwtService、AuthProperties、DeviceSessionPolicy
→ common/security(原先放在 admin/auth 里,任何模块用一次就多一条跨模块边)
端口化(消费方声明接口、数据方实现)
- task/spi:ResultFileJobHandler 新增 resolveResultFileUrl 钩子(BRAND 特例从 Worker 收回);
Worker 改为调用 handler.onSuccess(该钩子历史上从未被调用,withdraw 的收尾靠 Worker 里的
WITHDRAW 特判硬编码——现两者都归位,task 侧不再 import withdraw/brand)
- admin/spi/UserSecretCleanupPort(删除用户级联清理密钥)、notification/spi/ProxyBalancePort
(代理余额探测)、shopdatacrawl/spi/ManagedShopNamesPort(可管店铺名)
- shopkey/spi/GroupDeletionGuardPort:分组删除守卫改由各业务模块实现(invalidasin/dedupe 两个实现),
shopkey 不再直连它们的 Mapper
棘轮与量化(2026-09-14 实测)
- ArchitectureBoundaryTest:task→业务 119 → **0**(基线钉死为 0,新增跨模块动作必须走 task/spi)
- 跨模块 import 行数 650 → 605;双向依赖对 8 → **0**(usersecret/permission/shopkey 三向环、
task↔withdraw、task↔brand、admin↔permission、admin↔usersecret、dedup↔shopkey、
invalidasin↔shopkey、notification↔usersecret、shopdatacrawl↔shopduplicatecheck 全部拆解)
测试同步:接口契约 8→9、品牌 Handler 钩子契约、worker 的 10 个测试构造实参、通知/管理测试端口化。
mvn test 2881 全绿。
2026-09-14 10:05:04 +08:00
huangzd1997
1b480f915f
test(G4): 定时匹配/格式转换/数据拆分 补 27 个契约测试(三个模块此前均零测试文件)
...
- ShopMatchResolveService 15:候选越权与幂等、匹配去重保序、国家偏好默认顺序与坏 JSON 兜底
- ConvertTemplateService 8:内置模板禁删(软禁用)、导入命名/后缀补全、设为默认时清掉其它默认位
- SplitRunService 4:下载/删除历史必须属于本人且模块匹配(含无结果文件不可下载)
至此审查点名的 6 个零测试模块全部有测试文件。
2026-09-14 09:40:56 +08:00
huangzd1997
ac36c08460
test(G4): 取款/查询ASIN ResolveService 各补 13 个契约测试
...
取款:候选增删幂等与越权保护、按店铺名批量删的归一化去重、匹配去重保序。
查询ASIN:额外覆盖「国家 ASIN 清单」契约(国家顺序固定、空国家不出现、同国家去重),
该清单会整体推给 Python,格式错会直接导致采集错列。
2026-09-14 07:11:59 +08:00
huangzd1997
ef3a2c9bd6
docs(更新日志): 追加客户端 4.0.13 条目
2026-09-14 07:08:39 +08:00
huangzd1997
da0f10f1cc
test(G4): 巡店删除 ResolveService 补 13 个契约测试(该模块此前零测试文件)
...
覆盖:用户校验、索引未命中/同名冲突拒绝、重复添加幂等、他人记录不可删(同文案防探测)、
匹配结果去重保序、count 空值兜底。
2026-09-14 07:05:04 +08:00
huangzd1997
9ba231dc4c
feat(D2): 导入进度跨节点可见(NodeSharedStore:本地快路径 + Redis 真源)
...
- 新增 common/service/NodeSharedStore:本地 Map 快路径 + Redis 跨节点真源 + 写节流
(默认 500ms,逐行刷新进度不会打爆 Redis)+ 本节点条目快照(维护用)+ TTL 兜底过期
- 接入 DedupeTotalDataService(8 个进度/归属/分组/完成时间映射)、QueryAsinService、
SkipPriceAsinService(各 3 个):轮询落到另一节点不再报"任务不存在"
- 保留期清理改为遍历本节点快照(跨节点过期由 Redis TTL 兜底),不再依赖全量遍历
- 测试同步:去重服务测试的反射注入改用 NodeSharedStore(未注入 Redis 时等价纯本地)
mvn test 2815 全绿
2026-09-14 06:54:48 +08:00
huangzd1997
9a6b57db58
refactor+perf+fix: G5 模块边界 SPI 化、C5 流式解析、C6/C7 查询优化、D13 队列持久化、A3/A4/A7 鉴权
...
模块边界(G5 / G7)
- 新增 task/spi/TaskModuleHeartbeatSpi + 13 个模块实现:TaskHeartbeatService 不再 import 任何
业务模块(原先注入 12 个 CacheService 并用 switch 分发);启动校验重复注册
- 新增 task/spi/BrandTaskHeartbeatSpi(品牌任务心跳/中断)、BrandTaskStaleRepairSpi(陈旧修复)、
CollectDataItemCleanupSpi(历史清理):跨模块 Mapper 操作收回业务模块
- G7:10 个被跨模块借用的 productrisk VO 迁至 common/model/vo
- 架构棘轮收紧:TASK_TO_BUSINESS_BASELINE 119 → 6(实测)
- 新增 TaskModuleHeartbeatSpiCoverageTest(moduleType 覆盖与拼写)
性能与容量(C5/C6/C7/D13)
- C5 流式解析:SkipPriceAsinService(含两行表头语义)、AppearancePatentExcelParser、
BrandTaskService、DeleteBrandRunService、LocalFileStorageService.getExcelInfo 改 ExcelStreamReader;
行数上限改为迭代中生效
- C6 去重总数据列表:关键字改前缀匹配(命中 uk_data_value);V124 删除永不生效的 idx_country
- C7 撞款扫描:按店逐批取数(索引前缀),不再全表 GROUP BY + JOIN + 全量拉内存
- D13 待删对象本地日志 PendingDeleteJournal(启动回放 + 收敛重写),RustfsDeleteRetryService 与
TransientPayloadDeleteOrchestrator 接入;异步删除失败对象写回日志
- V123 删除 biz_file_result 两个被复合索引覆盖的单列索引
安全(A3/A4/A7 + 守卫名单)
- A4 数字人版本写操作要求管理员;A7 视频密钥按登录身份(超管例外)
- A3 上传接口加危险扩展名黑名单(可配置)
- AdminApiGuardFilter 用户态名单补 /api/ziniao(controller 已 requireAdmin,此处为开关打开后的第二层)
容量(明细表保留期)
- 新增 ShopDataCrawlItemRetentionService:biz_shop_data_crawl_item 按保留期(默认 30 天)分批清理
(该表此前无任何清理策略,是增长最快的表),job 锁 + 单轮批次上限
2026-09-14 06:46:27 +08:00
huangzd1997
67223f8950
perf(frontend): 工具页分页/历史截断/轻量轮询/共享纯逻辑(审查 F5/F6/F9/F12/G3)
...
- F9 新增 useTablePaging composable 并接入 7 个工具页"匹配结果"表(只切渲染窗口,
不加选择语义;每页 100 条)+ 深色分页样式
- F6 历史任务抽屉渲染截断(默认 50 条 + "显示全部/收起"),全选口径改为当前可见条目
- F5 新增 task-progress-polling 适配器:等待任务终态的紧循环走 /tasks/progress/light,
异常或空响应回退重型 batch;已接入跟价/定时匹配的 waitForTaskTerminal
- F12 背景图 bg.jpg 251KB → 166KB(1920 宽 + quality 80,image-set 仍优先 webp)
- G3 抽出共享纯逻辑 task-queue-state(开始时间表序列化校验 + 记录缺失错误判定),
5 个工具页删除逐字重复实现
- 新增 4 个单测文件(分页切片/历史截断/轻量轮询归一/任务队列纯逻辑)
2026-09-14 06:23:46 +08:00
huangzd1997
e76714c32e
fix(log): 陈旧巡检 summary 日志占位符与实参对齐
...
每段 5 个占位符(x={}/{})只传 4 个参数,导致 withdraw 之后取值整体错位、
末尾 elapsedMs/thread 被打成字面量;改为每段 4 个占位符。
2026-09-14 05:51:06 +08:00
huangzd1997
95dfb69a18
refactor(ziniao/shopkey)+fix(stale): 打破模块循环依赖 + 采集陈旧判死全局化
...
模块边界(消除 ziniao ↔ shopkey 真实循环依赖):
- 新增 ziniao/service/port/{ShopKeyCatalogPort,ManagedShopNamePort}:消费方声明契约
- shopkey 侧新增 ShopKeyCatalogAdapter(读 shop_key + 白名单状态回写)、
ManagedShopNameAdapter(店铺名校验),实现上述端口
- ZiniaoApiKeyProvider 改经端口取数,不再 import shopkey 的 Mapper/Entity;
ZiniaoShopSwitchService 改依赖 ManagedShopNamePort
- 结果:ziniao → shopkey 的 import 归零,依赖单向(shopkey → ziniao)
陈旧判死(G1 全局判死 + D9 条件更新,替代此前的 owner 过滤/旧实体覆盖):
- ShopDataCrawlTaskService.finalizeOwnedStaleTasks → finalizeStaleTasks:去掉 owner 过滤,
并入 DeleteBrandStaleTaskService 的 stale-check 巡检线(job 锁保证单实例扫描)
- 判死前必须持有任务锁(非阻塞获取,锁被占本轮跳过),FAILED 写入改 status CAS,
仅在确实由 RUNNING 翻转为 FAILED 时才删缓存与分片(原实现会用扫描期旧实体覆盖在途任务)
- tryFinalizeTask 增加 allowOwnerTakeover 重载:判死场景允许跨实例接管(P1-8 盲区)
- 测试同步:owner 契约用例改为全局判死口径;mock 的 CAS 需先渲染 SQL 片段
(MyBatis-Plus 的 where 参数延迟填充)才读参数表;新增锁被占跳过的用例
mvn test 2796 全绿
2026-09-14 05:44:37 +08:00
huangzd1997
24ada70997
refactor: 结果下载直链解析去重(11 处 → 1 处)
...
新增 common/service/ResultDownloadResolver,7 个模块的 resolveResultDownloadUrl
改为委托调用(appearancepatent/queryasin/withdraw/pricetrack/productrisk/
patroldelete/shopmatch)。
顺带修掉两处隐患:
- 原实现用 userId.equals(entity.getUserId()),userId 为 null 时 NPE
(新实现用 Objects.equals)
- shopmatch 原实现在 url 为空时抛"任务不存在",与语义不符,统一为"暂无可下载文件"
其余 4 处**刻意保留**,因为它们本就不是重复:
- shopdatacrawl:有 validateUserId + requireResultEntity + ensureResultOwner 前置校验
- deletebrand:返回 null 而非抛异常(调用方依赖该语义)
- similarasin:已分叉为返回 record ResultDownloadInfo
- brand:走的是另一套下载路径
文件名解析 resolveResultDownloadFilename 同样没收口:各模块兜底文件名策略确有差异
配套更新 3 个显式构造 Service 的测试(DelegationTest/HistoryBatchTest/
RollbackSemanticsContractTest)注入新依赖的 mock。mvn test 2795 个全绿。
2026-09-14 05:17:10 +08:00
huangzd1997
ccce03b4d4
fix: 第四批修复(结果读取并发化/分页钳制/JWT 密钥强校验/凭据按 id)
...
性能
- listResultSnapshots 对指针化载荷改并发读取(有界池 + 保序汇总):此前逐行同步对象存储读,
500 行结果文件生成要多花数十秒。仅在确有指针行时才走池——内联 JSON 直接串行读,
避免为本地读取引入调度抖动(性能基准测试容差会被影响)
安全/正确性
- 管理端 GET /{id}/credential 改为按路径 id 查询(此前忽略 id、改用 shop_name,
会出现「路径声明的店铺」与「实际读取凭据的店铺」不一致);凭据 VO 构造抽公共方法
- server profile 下 JWT 密钥缺失即拒绝启动:此前静默回退到公开默认值(等同无防护,
任何人可伪造 token),只留 warn 日志拦不住发布事故;本地/测试 profile 保持宽松
边界
- 分页/条数参数补齐上限钳制(文档早已声明"最大 N"但无校验):
PriceTrack/ShopMatch 的 page_size → 2000;TaskFileJob 的 limit → 1000;
AppearancePatent/Publish 的 limit → 100
2026-09-14 05:02:33 +08:00
huangzd1997
4daf235385
docs(更新日志): 追加客户端 4.0.12 条目
2026-09-14 04:36:35 +08:00
huangzd1997
6d46506726
fix: 全维度审查修复(安全/正确性/性能/稳定性/客户端/前端)
...
安全
- /api/ziniao/** 五个匿名接口加管理员鉴权(此前可匿名换取任意员工店铺登录令牌)
- 删除 Flask 遗留后门:默认密码建超管 + 每次启动写生产 users 表(服务端与客户端各一份)
- 进度/详情接口归属过滤:新增 TaskProgressOwnershipSupport,11 模块 progress/light 与
/tasks/batch 接入,DTO 补 userId,前端 13 个查询封装补传(未传时后端不过滤,兼容旧端)
- 代理提取链接(含账密)不再明文入日志(新增 common/util/SecretMasking)
- 全局异常兜底不再回传原始异常信息;内部令牌比较改常量时间
- 登录加失败计数与锁定(10 次锁 15 分钟);品牌源文件下载加 SSRF 防护
- AdminApiGuardFilter 覆盖前缀从 2 扩到 15(开关默认 false,行为不变,为收紧做准备)
- 生产关闭 springdoc/knife4j(/doc.html 匿名可读全部接口定义)
正确性
- 40901/40902 拆分:锁竞争不再被伪装成 success=true(此前客户端停止重试、分片静默丢失)
- 假成功收敛:集采明细批量写失败改为抛出、去重 worker 异常标失败、4 个 worker 改判
success 字段、publish 空 ASIN 行参与批次 flush、巡店删除全失败带 error 上报
- 客户端心跳 discard 移入 finally(7 模块,失败路径不再留僵尸 RUNNING 任务)
- 状态机条件更新:跟价停止循环、集采 activate/fail、imagevideo 归档回填、店铺匹配提交
性能
- 前端入口包 JS 1.05MB→204KB、CSS 355KB→10.7KB(Element Plus 改按需 + el-config-provider)
- 载荷引用计数按指针里的 taskId 收敛(原 JSON 列 IN 全表扫且逐行调用)
- 店铺明细多值批量 INSERT;快照 upsert 预载缓存;结果文件列改单条 UPDATE
- 新增迁移 V120(补 3 个缺失索引)/V121(删 4 个被覆盖的冗余索引)/V122(URL 前缀索引)
稳定性
- 新增 common/util/ThreadPools 有界线程池替换 5 处无界队列(防堆积 OOM)
- Redis 锁释放改 Lua 原子校验(原裸 delete 会误删他人已过期的锁)
- imagevideo 加死节点接管;锁续期失败重试;调度池 4→16;openStream 全部加超时
- 事务内远程对象删除移到提交后;启动恢复锁按实例命名
客户端
- 不再 taskkill /f /im chrome.exe(改为按调试端口精准回收,不杀用户自己的浏览器)
- 密码检测不再无条件杀紫鸟进程;品牌检测加全局互斥(代理池不再互相覆盖)
- base_dir 统一到 exe 目录(原被 os.getcwd() 覆盖,日志/缓存会分裂两个目录)
- 缓存加定时清理;图片下载加超时;mkstemp 句柄托管
测试
- 同步更新受影响的契约测试(构造器签名/条件更新/方法改名/新增接口方法等)
- 修复 FaultInjectionTest 等 3 处 mock 未 stub 流式 read 导致的读循环 OOM
- mvn test 2795 个测试全绿
2026-09-14 04:15:36 +08:00
huangzd1997
c448f49e30
fix(临时存储): RustFS 上传失败改为直接失败,不再静默回落 local 指针
...
多实例/容器化部署下 local 指针只有写入它的实例能读(跨节点读直接报错、容器重建即丢),
此前上传失败静默降级会把跨节点不可读的脏指针落库,故障延后到其它节点的合并/组装才爆。
现改为在写失败点抛 BusinessException(中文原因透传调用方),并新增
fallback-to-local-on-error 开关(默认 false)供单机部署回退旧行为;
跨实例读 local 指针的报错改中文并带 objectKey;超限回落策略不变。
2026-09-14 02:31:16 +08:00
huangzd1997
3f5a234c59
fix(测试): 修复 3 处既有红灯——快照行尾、缺失基准文档、失效的边界棘轮
...
这三处在本次重构开始前就是红的(已在裸 HEAD 上复现),一并修掉,使 backend-java
相关测试恢复全绿(550 测试 0 失败)。
1. SimilarAsinSnapshotTest:golden 文件在 core.autocrlf=true 的检出下是 CRLF,
而渲染结果按 LF 拼接,断言逐字符比较只差换行符即失败。改为读入时统一行尾。
(这是测试自身缺陷,不是解析行为变化——两边的可打印内容完全一致。)
2. TxDurationBenchmarkTest:依赖 docs/tx-duration-benchmark.md,而该文档从未提交过
(git 历史中不存在),导致 2 个用例必然失败。补齐文档,按 mock 环境实测记录
单次事务段基线、200 分片上界与总耗时上界,并注明该基线只用于相对劣化判定。
3. ArchitectureBoundaryTest:task→业务依赖棘轮冻结在 84(task-212 后的存量),
此后 TaskHeartbeatService 跨模块心跳(13)、StaleTaskRepairService(4)、
ModuleHistoryCleanupService(2)、TaskResultFileJobWorker(2)持续接入新模块,
实测已达 110,棘轮长期失效(恒红=无人看)。对齐到 110 恢复告警,并写明
「新增依赖请走 Handler SPI,不要直接上调」。
注意:并行会话正在改 task 模块(含 StaleTaskRepairService),其改动落地后需重新实测。
2026-09-14 02:01:16 +08:00
huangzd1997
b05bba50fa
refactor(similar-asin): 抽出 LlmPipelineSupport,Service 降至 1735 行(累计 -70.7%)
...
在上一提交(3455 行)基础上,把「Python 结果回传 → 分片落库 → LLM 检测」整条流水线
(57 个方法 / 1694 行)抽为 SimilarAsinPipelineSupport。等价搬移,未改行为。
采用依赖倒置消除循环依赖:support 包声明 SimilarAsinPipelineHost 接口
(finalizeTask / findOrCreateResultRecordForAssembly / readCategorySwitch /
finalizeExhaustedResultFileJob),由 SimilarAsinTaskService 实现;这几项保留在宿主
是因为它们属于编排与事务边界(handleResultFileJobFailure 带 @Transactional)。
同时把 5 个被两侧共用的内部 record 提为 support 包顶层类型
(SubmittedTaskMetadata / FinalizeTaskResult / SubmitContext /
PersistSubmittedChunkResult / PreparedSubmittedChunk),3 个仅流水线内部使用的
record 内联进流水线类;LlmBatchContext 一并归位。
测试适配:4 个测试类里对 mergeLlmRowsIntoChunk / bufferLlmRowsOrMerge /
flushLlmBufferedResults 的反射改指向流水线实例;RollbackSemanticsContractTest
通过 pipelineSupport() 反射取得实例(跨包,保持封装)。
验证:干净工作区叠加本改动跑 similarasin 386 + task 引用方 166 测试,
结果与基线一致(仅既有失败),零新增失败。
2026-09-14 01:51:36 +08:00
huangzd1997
9d92fb4af2
refactor(similar-asin): SimilarAsinTaskService 拆分为 9 个 support 类(5914→3455 行)
...
从 5914 行的巨型 Service 中按内聚单元抽出 9 个 support 类 + 1 个顶层 record,
净减 2459 行(-41.6%)。全部为等价搬移(Javadoc 标注搬移来源),未改任何行为:
- SimilarAsinLimits 阈值/开关解析(parse 上限、chunk merge 上限、LLM batch/缓冲/flush)
- SimilarAsinPayloadSupport 解析载荷编解码与读取门面
- SimilarAsinPoisonTracker 毒行滑窗熔断状态(Service 从此无进程内可变状态)
- SimilarAsinChunkMergeSupport 行键族 + chunk 合并纯计算
- SimilarAsinResultTextSupport 结果行判定与用户可见文本渲染
- SimilarAsinChunkPayloadSupport chunk 载荷读取、读失败诊断、orphan 兜底
- SimilarAsinResultWorkbookAssembler 结果文件装配(xlsx/zip、POI、DISPIMG、并发)
- SimilarAsinTaskOwnershipSupport 实例归属判定与 per-task 分布式锁
- SimilarAsinTaskProgressSupport 文件构建进度、任务视图映射与计数
- LlmBatchContext 从内部 record 提为顶层,供归属与 LLM 流水线共用
顺带清理 7 处死代码(imageUrlCellValue、resolveResultDownloadUrl/Filename、
applyLlmToPersistedChunks、countCompletedLlmStates、hasPromptFields、
userFacingConclusion、@PreDestroy import)。
assembleExecutor 仍由 Service 持有,shutdownAssembleExecutor 语义不变(18 个测试未动)。
验证:在干净 HEAD worktree 上叠加本改动跑 similarasin 全量测试,结果与裸 HEAD 一致
(386 测试,仅 3 个 SimilarAsinSnapshotTest 既有失败),零新增失败。
2026-09-14 01:30:11 +08:00
huangzd1997
db6869b77e
perf(LLM任务): 首查短超时快速失败 + 退避抖动 + 标题失败跳过外观识别
...
生产 24h 数据:外观专利任务 LLM 重试失败 93+82 次几乎全是超时,
终态 41 行降级为「外观识别异常」;成功调用 p99=34s、超 60s 仅 0.02%。
原策略每轮重试都挂满 90s(被全局 call-timeout 钳制),且 1.5s/3s
密集重试整批落在同一劣化窗口内。
- 第 1 次尝试改用 llm-first-attempt-read-timeout-millis(默认 60s)快速失败,
重试走完整读超时,慢而成功的正常调用不被误杀
- 重试等待改 LlmRetryBackoff:2s/10s + ±30% 抖动,覆盖更长窗口并打散同批尖峰
- 外观专利标题识别失败时跳过外观请求(该行必走回退,外观结果本就会被丢弃)
2026-09-14 00:28:40 +08:00
huangzd1997
864c22ffc7
fix(密钥检测): 直连失败重试一次,超时/网络提示中文化
...
上游 ai.t8star.org 实测约 1/8 单请求完全不应答(主机 A 上 JDK 客户端
HTTP/1.1 与 HTTP/2 均复现),检测只有一次机会时用户会看到
「网络不可达:HttpTimeoutException: request timed out」。
- 传输层失败(超时/网络不可达)对直连最后一跳重试一次(800ms 间隔);
代理模块不重试——jikip 按提取次数计费,重试会多扣一次
- 失败文案中文化(新增 CODE_TIMEOUT),英文异常串只进服务端日志
- 探测提问改「你好」(最简一次调用)
- 检测面板标明检测对象(配置密钥 sk-**** / 输入值(未保存)),
检测接口超时单独放宽(客户端 60s / 后台 180s),避免重试期间前端先超时
2026-09-14 00:28:35 +08:00
huangzd1997
fd614004b0
fix(撞款e2e): 台账分页断言限定到台账表格紧邻分页器
...
台账 tab 下页面同时有「店铺上架分布」分页器(设计要求常显)与台账分页器,
全页 .old-pagination 命中 2 个元素触发 strict mode;断言改为 .table-wrap + .old-pagination,
用例意图不变(台账分页可见)。撞款 4 用例全绿,全量 e2e 44 通过(通知面板 1 例失败系并行会话在改 NotificationBell.vue,与本改动无关)。
2026-09-14 00:25:19 +08:00
huangzd1997
ff1ffbbfa3
feat(站内通知): 铃铛面板支持时间/内容搜索、按天分组与分页
...
- 列表接口加 keyword(标题/内容模糊)与 startDate/endDate(年月日闭区间)参数,
两端控制器透传,服务端补筛选日志
- 两端铃铛面板:搜索框(防抖 300ms)+ 日期区间 + 按年月日分组 + 翻页,
面板改 Teleport 到 body(挂在顶栏时会被页面 el-select 压住,提 z-index 无效)
- 固化可见范围回归测试:超管全量/管理员只看本组/普通用户只看自己,
含读写两侧的 user_id+audience 裁剪断言与两端控制器身份来源断言
2026-09-13 23:59:06 +08:00
huangzd1997
9ffd68bae5
feat(教程管理): 后台可上传教程包,工具台按最新上传下载
...
- admin-vue 新增「记录与版本 → 教程管理」页(menuKey admin_tutorial):
zip 浏览器直传 MinIO(presign → PUT 带进度 → confirm 落库)+ 列表/下载/删除,
最新一条标「当前生效」;补操作指引与 align-tutorial 守卫测试。
- frontend-vue 工具台「立即下载教程」进页面取 /api/tutorial/latest(最新上传优先),
接口异常或无记录回退历史固定直链,按钮不失效。
- Java 侧 tutorial 模块与 V119 迁移已在 dc6e8924 提交并上线(2026-09-13)。
vue-tsc + 两端单测(工具台 695 / 后台 1617)通过;生产已实测
presign→PUT→confirm→latest 切换→删除回落全链路。
2026-09-13 23:48:24 +08:00
huangzd1997
14a723ab1c
refactor(品牌工具页): 历史批量删除抽为 runBatchDelete
...
16 个品牌页逐字相同的 batchDeleteHistory 循环(逐条删除 + 容忍个别失败计数)
收敛为 shared/utils/batch-delete.ts 的 runBatchDelete;各页仅保留自己的删除调用
与提示文案。补单测 4 例(空列表 / 全成功 / 部分失败继续 / 非 Error 异常)。
净减约 120 行;vue-tsc 构建与 699 个前端单测通过。
2026-09-13 23:32:27 +08:00
huangzd1997
dc6e8924a9
refactor(品牌工具页): 历史轮询抽为 useHistoryPolling
...
QueryAsin / Withdraw / PatrolDelete 三页逐字相同的 startHistoryPolling /
stopHistoryPolling 收敛为 shared/composables/useHistoryPolling:定时器走各页
categorized-timers(category 固定 history-poll),间隔默认主轮询的 2 倍。
categorized-timers 补 CategorizedTimers 类型导出;补注入式假定时器单测 5 例。
净减约 40 行;vue-tsc 构建与 695 个前端单测通过。
2026-09-13 23:22:49 +08:00
huangzd1997
882ccdac12
refactor(品牌工具页): 抽取 formatDateTime 与 uploadPathsToJava 公共工具
...
- 新增 shared/utils/datetime.ts:13 个品牌页逐字重复的 formatDateTime 收敛为一处;
语义不同的 3 个变体(toLocaleString / 字符串切片 / 支持时间戳)保留不动
- 新增 shared/utils/upload-to-java.ts:10 个品牌页的上传循环收敛,api 依赖注入便于单测;
保留 uploadOss(品牌Tab)与 returnEmptyWhenUnavailable(跟价)两处行为差异
- 补 datetime / upload-to-java 单测 10 例
净减约 327 行;vue-tsc 构建与 690 个前端单测全通过
2026-09-13 23:16:59 +08:00
huangzd1997
b70557a077
feat(认证/通知): 单设备登录互踢 + 站内通知铃铛系统
...
- 单设备登录:登录成功即 last-login-wins 绑定 users.machine;非超管旧 token
在下一次受保护请求抛 4011 下线,超管豁免;仅认 token 内签名 deviceId,不用请求头。
前端两端接入踢下线跳转(?kicked=1 提示),V118 清空历史 machine
- 站内通知:新增 notification 模块(任务失败扫描 / 密钥欠费 / 下游服务探测三类来源),
前后台铃铛组件 + 轮询;后台列表按主管数据范围(UserDataScopeSupport)过滤;V116 建表
均已于 2026-09-13 部署上线,此次补提交源码(此前仅存在于已部署 JAR/构建产物中)
2026-09-13 23:08:22 +08:00
huangzd1997
a0f6582914
feat(密钥管理): 代理配置后台明文展示 + 按次统计各密钥调用损耗
...
- 后台密钥管理「代理设置」列改为完整明文展示(含账号密码),便于运维核对;
新增 full 字段仅对代理模块下发,密钥两列保持脱敏
- 新增 biz_user_secret_usage_daily(V117):用户 × 模块 × 天累计真实对外请求次数
- 计次口径:LLM 每次真实 HTTP 请求(含重试)计 1 次;代理每次成功提取计 1 次
- LLM 埋点走 SecretUsageContext 上下文(批次外设置、线程池内快照恢复)
- 新增内部上报接口 /api/internal/user-secret-usage(X-Internal-Token)
- 新增后台页「密钥用量统计」:日期范围 + 用户名 + 分组筛选,含范围内汇总
2026-09-13 21:56:46 +08:00
huangzd1997
0c98c5bc15
docs(auth): 修正 JWT 默认密钥注释——桌面客户端本地 Flask 退役后服务端可独立轮换
...
原注释称"客户端内置同一密钥,服务端不能换"已过时(本地 Flask 与 /api/auth/sync
验签链路已随服务端化退役),会误导运维不敢轮换;2026-09-13 生产已轮换为
application-server.yml 独立密钥并验证。
2026-09-13 21:44:20 +08:00
huangzd1997
1066625078
feat(品牌服务): 内部接口按 uid 供用户代理提取链接;代理检测改真实提取+转发探测
...
- 新增 /api/internal/user-proxy(X-Internal-Token 自校验,18080 公网可达必须校验),
供主机 A 品牌检测服务(15126)按 uid 取用户代理提取链接,未配置返回空串
- 代理检测修复"假通过":旧实现把提取链接直接当静态代理交给 HttpClientPool,
链接无显式端口解析失败静默回退直连,检测到的是自家站点直连响应(40 个用户
3-39ms 全 301 假通过);现在先真实提取一次(余额不足/解析失败如实报),
再经提取到的代理请求自家域名,兼容旧静态代理地址
2026-09-13 21:21:48 +08:00
huangzd1997
fe052879bd
fix(用户管理): 删除用户时级联清理服务端密钥,修已删账号留下孤儿密钥行
...
biz_user_api_secret 按 uid 绑定且无外键,删除用户后密钥/代理配置成为无主行
(生产已出现 5 条孤儿)。deleteUser 内同步调用 adminClearByUser 并纳入同一事务,
删除失败(行不存在)时不触碰密钥。
2026-09-13 21:21:45 +08:00
huangzd1997
51c5fe1129
fix(任务中断): 已终态任务也幂等刷新缓存/快照,修历史遗留的处理中卡死
...
第一次修复只在 DB 刚被改成 FAILED 时同步缓存;对 DB 已是终态、但 Redis 里
仍缓存 RUNNING 的历史任务不生效(真机:28094 反复调中断仍返回 progress=RUNNING)。
现在无论是否刚更新 DB,都按 DB 现值同步模块缓存与进度快照,可自愈。
2026-09-13 15:46:18 +08:00
huangzd1997
3d208ea0e5
fix(任务中断): 标失败时同步模块缓存与进度快照,修"处理中"卡死
...
客户端重启上报中断只改了 file_task,模块缓存/进度快照仍是 RUNNING,
导致 progress/batch 继续回报 RUNNING:前端任务面板永远"处理中"并阻塞该工具后续任务
(2026-09-13 真机:店铺数据采集 28094 history=FAILED 但 progress=RUNNING,界面卡住)。
markInterrupted 现在同时刷新模块缓存(saveFileTaskCache)与进度快照(新增 markTerminal)。
2026-09-13 15:16:14 +08:00