 kkfluousandClaude Opus 4.7
|
812b8877d4
|
feat(ingest-core): Dispatcher emits RawArchive event per inbound frame
Dispatcher.dispatch 顶部(interceptor 之前)在 RawFrame.rawBytes() 非空时通过
EventBus 发一条 VehicleEvent.RawArchive,由 ArchiveEventSink 消费落盘。
放在 interceptor 之前的理由:原始可回放的目标是"每一条成功解码的帧都要留痕",
dedup / rate-limit 不应过滤 archive。archive sink 的写失败只影响冷存,不影响
业务事件流。
RawArchive 的 VIN 从 RawFrame.sourceMeta["vin"] 取;缺失时留空字符串,ArchiveSink
落盘时会用 "unknown-vin" 占位(见 ArchiveEventSink.buildKey)。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
2026-04-20 15:54:59 +08:00 |
|
 kkfluousandClaude Opus 4.7
|
dd838b4f73
|
feat(sink-archive): ArchiveEventSink consumes RawArchive events to cold storage
新 EventSink 实现:
- accepts 仅 VehicleEvent.RawArchive
- publish 写 ArchiveStore,key = yyyy/MM/dd/<source>/<vin>/<eventId>.bin
(基于 ingestTime 的 UTC 日期分片,便于按车按天回放)
- 相同 eventId 重复写按 put 语义覆盖
- 写失败在返回的 CompletableFuture 上 completeExceptionally,EventBus 打 WARN
AutoConfig 在 ArchiveStore bean 存在时装配 ArchiveEventSink,Disruptor EventBus 启动时
自动把它纳入扇出 handler 列表。Disruptor 使用虚拟线程消费,本地/S3/OSS 的 blocking
IO 不会 pin 平台线程。
单测覆盖 accepts 过滤、稳定 key 写入、幂等覆盖三种情形。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
2026-04-20 15:54:54 +08:00 |
|
 kkfluousandClaude Opus 4.7
|
bfc3c29c40
|
feat(api): add VehicleEvent.RawArchive sealed variant for raw cold-storage
新增 VehicleEvent.RawArchive 事件类型,承载一帧入站的原始字节,供 ArchiveEventSink
写冷存、后续迭代可回放。携带 command / infoType / rawBytes 三个字段,VIN 与
timestamps 沿用基接口约定。
同步更新两处穷尽 switch:
- TopicRouter.route:RawArchive → topics.getRawArchive()(未来 Kafka 需要时会用到)
- EnvelopeMapper.toEnvelope:只填 RawArchiveRef.size_bytes,payload oneof 留空
本期 Kafka sink 不处理 raw archive——KafkaEventSink.accepts(RawArchive) 返回 false,
原始字节只落 ArchiveStore,避免 Kafka 消息体被原始报文撑大。未来若接 URI 回填
(archive 写成功后把 URI 放进 Envelope.raw_archive 送 vehicle.raw.archive topic),
再放开过滤。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
2026-04-20 15:54:46 +08:00 |
|
 kkfluousandClaude Opus 4.7
|
4d8dd54e59
|
refactor(gb32960): extract channelRead0 command branches into private methods
把 Gb32960ChannelHandler.channelRead0 的 140 行 if/else-if 级联拆成按 CommandType
的 switch 分派到 7 个独立私有方法:
- handleVehicleLogin (0x01)
- handleVehicleLogout (0x04)
- handlePlatformLogin (0x05,返回 boolean 表示是否继续 dispatch)
- handlePlatformLogout (0x06)
- handleReportOrHeartbeat (0x02/0x03/0x07)
- handleTimeCalibration (0x08)
- logOtherFrame (其它命令的调试日志)
同时抽出 eventOrNow(msg) 静态 helper,消除 6 处
"msg.header().eventTime() != null ? msg.header().eventTime() : Instant.now()"
的重复。
纯重构,语义零变化:ACK 写入顺序、PlatformLogin 鉴权失败短路、日志 tag、VIN 白名单
预检都保持原行为。全模块 41 tests + 全仓 E2E 全绿,无回归。
channelRead0 从 140 行收敛到约 45 行,每个分支处理器 20~40 行并带 §6.3.2 规范注释,
未来新增命令或调整 ack 时序只动对应私有方法。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
2026-04-20 15:41:59 +08:00 |
|
 kkfluousandClaude Opus 4.7
|
da28a0c8d5
|
fix(gb32960): emit platform login/logout events instead of swallowing them
Gb32960RealtimeHandler.onPlatform 之前返回 List.of() 吞掉所有 0x05/0x06 事件,
而 Gb32960EventMapper.toEvents 里 PLATFORM_LOGIN / PLATFORM_LOGOUT 分支实际构造了
VehicleEvent.Login/Logout(vin="platform:<username>",metadata.kind="platform")。
两边语义冲突导致平台登入事件永远不会上送下游——mapper 里那段是死代码。
修正:
- onPlatform 改为委托 mapper.toEvents(msg),让平台会话事件真正发布到
vehicle.session topic,下游按 metadata.kind 区分车辆/平台会话
- 加 @EventEmit 声明 Login/Logout 事件类,保持元数据一致
- 去掉 @ProtocolHandler(version = "2017"):2017 不是真实的 GB/T 32960 版本(标准
仅 2016 / 2025),且 version 属性在 HandlerRegistry 路由里未使用,纯噪音
- JavaDoc 从"示例 Handler:演示..."改为生产路径描述,说明它是 GB32960 在
Dispatcher 管线里的唯一入口
新增 Gb32960RealtimeHandlerTest 2 case 覆盖平台登入/登出事件产出。
全模块 41 tests 全绿(原 39 + 新 2),无回归。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
2026-04-20 15:27:52 +08:00 |
|
 kkfluousandClaude Opus 4.7
|
ce87c8ee26
|
docs(changelog): record gb32960 body parser block isolation
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
2026-04-20 15:18:26 +08:00 |
|
 kkfluousandClaude Opus 4.7
|
f9b9c2a6a7
|
config(gb32960): document parse.lenient-block-failure in application.yml
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
2026-04-20 15:17:07 +08:00 |
|
 kkfluousandClaude Opus 4.7
|
310d1d46f1
|
config(gb32960): wire parse.lenientBlockFailure into BodyParser bean
Gb32960AutoConfiguration.gb32960BodyParser 现在注入 Gb32960Properties,
构造完 BodyParser 后把 props.getParse().isLenientBlockFailure() 通过 setter 注入,
让运行期配置 lingniu.ingest.gb32960.parse.lenient-block-failure 生效。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
2026-04-20 15:16:31 +08:00 |
|
 kkfluousandClaude Opus 4.7
|
89a7c07106
|
feat(gb32960): isolate per-block parse failures in body parser
主循环改造:单信息块 parser 抛 DecodeException / BufferUnderflowException /
IndexOutOfBoundsException 时不再放弃整帧。
- 固定长度块(fixedLen≥0):回滚 reader,按 fixedLen 截取字节兜成 InfoBlock.Raw,
position 前进 fixedLen,continue 循环继续解析后续块
- 变长块(fixedLen=-1)或剩余不足 fixedLen:剩余字节全部兜成 Raw 后 break
- parser 契约违反(consumed != declared fixedLen)仍抛异常,以暴露 parser bug,
不走 lenient 兜底,避免静默误解析长期误判
- 仅捕获 DecodeException / BufferUnderflowException / IndexOutOfBoundsException,
RuntimeException 继续向上抛,保留程序 bug 可见性
- 通过 lingniu.ingest.gb32960.parse.lenientBlockFailure=false 可回退严格模式
不改 InfoBlock.Raw record 结构;下游 Gb32960EventMapper 按 findBlock(Class) 类型
查找,新增 Raw 不影响业务事件映射。
Gb32960BodyParserIsolationTest 4/4 绿;全模块 39 tests 全绿,无回归。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
2026-04-20 15:15:41 +08:00 |
|
 kkfluousandClaude Opus 4.7
|
9cbc19578b
|
test(gb32960): red — isolation test suite for body parser block failures
4 个测试覆盖:
- fixedLengthBlockFailure_isIsolated_subsequentBlocksStillParsed
- truncatedFixedLengthBlock_isWrappedAsRaw_loopTerminates
- variableLengthBlockFailure_swallowsRemainderAsRaw_loopBreaks
- strictMode_throwsOnAnyBlockFailure(当前行为已经抛异常,即通过)
外加在 Gb32960BodyParser 上加了 lenientBlockFailure 字段 + setter 的 stub(默认 true,
尚未接入主循环逻辑;行为变化由下一个 commit 实现),确保测试文件能编译。
当前运行:Tests run: 4, Errors: 3(3 个隔离场景未实现),strictMode PASS。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
2026-04-20 15:13:53 +08:00 |
|
 kkfluousandClaude Opus 4.7
|
fa552f4b20
|
config(gb32960): add parse.lenientBlockFailure toggle
新增 lingniu.ingest.gb32960.parse.lenientBlockFailure 配置开关(默认 true),
为 Gb32960BodyParser 的单块异常隔离行为做回退开关。实现在后续 commit。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
2026-04-20 15:12:16 +08:00 |
|