编写日期:2026-06-17 · 编写员:实现计划编写员(Claude Opus 4.8) 权威输入:
项目文档/需求文档/04-微服务改造方案.md、项目文档/进度/文档源码审核报告.md、项目文档/00-需求总览.md §5 D3/D4/D5/D10所有文件:行号已用 codegraph MCP(codegraph_explore / codegraph_node)核对,符号真实存在、行号吻合(误差 0–2 行)。 本文档只产出计划,不改任何源码。
加入机旁本地自动对焦(autofocustool 的 Tenengrad÷mean 四步标定,由 M2 在机旁直接算出最清晰层)后,删除微服务里"云端 OpenCV 打分→选最清晰层→回传对焦位置"这条选层打分链;同时保留与之同名但独立的抠图/clearest 链(CCD 正式拍照图的抠图、水印、视频合成、AI 识别)。删除打分后 t_picture.image_score 列对 AUTOFOCUS 行停写(变 null),必须同步修掉所有读取该字段的下游消费点(否则患者详情/对焦预览 NPE 崩溃);calPhotoPosition 按 D5 方案 A 保留接口、改输入源为本地对焦结果。
| 微服务 | 角色 | 本轮动作 |
|---|---|---|
aivfo-data-transmission |
打分生产者 + 抠图 | ★删打分链(focusPointUpdate / pictureScore / cutAutofocus / AutofocusFeign);保留 cutCCD 抠图、视频合成、AI 转发;改 2 处 orderByDesc 排序键 |
aivfo-business-manage |
打分消费者 | ★★改三处 image_score 消费(消 NPE + 换判据) |
aivof-tl-control |
对焦位置编排 | 改 calPhotoPosition 输入源(D5-A);删 AutofocusApiController/AutofocusApi/AutofocusUpdateEvent(Handle) 入云端打分回传的部分 |
aivfo-ai-middleware / aivfo-gateway / aivfo-service |
不受影响 | 仅 M3-04 回归确认 |
ivf_tl_control_2.0(C# 机旁端) |
native 本地打分 | M3-01b 只做评估 + 登记 V-007,C# 改动归 M2/合并端,本计划不改 |
Java 8 · Spring Boot 微服务集群(Feign / Kafka / MyBatis-Plus 分表)· JNA→opencv_world*.dll(打分/抠图 native)· t_picture 动态分表。
| 链路 | 处理对象 | clearest 来源 | 本轮 |
|---|---|---|---|
| ① 选层打分链 | PhotoMode.AUTOFOCUS 一 well 多层对焦图 |
微服务 OpenCV 打分 image_score |
删 |
| ② 抠图/clearest 链 | PhotoMode.CCD 正式拍照图 |
下位机直接标记 ImageDTO.clearest(DegreeOfClarity),非微服务打分 |
保留 |
误删 ② 的后果:前端展示、AI 识别、
getClearest/getClearestByDevelopTime、getPicturesByEmbryoId(eq PhotoMode.CCD)全废 → 患者看不到图、AI 不识别。删任何代码前先确认PhotoMode:碰AUTOFOCUS才删,碰CCD/DegreeOfClarity.CLEAREST一律保留。
PictureManage.java 存在两份,只改 data-transmission 的,绝不可碰 AI 侧:
aivfo-data-transmission/aivfo-data-transmission-manage/src/main/java/com/aivfo/data/transmission/manage/image/PictureManage.javaaivfo-ai-middleware/aivfo-ai-middleware-manage/src/main/java/com/aivfo/ai/middleware/manage/PictureManage.java(AI 侧 interface,与打分无关)目标:删除 ① 选层打分链的全部专属代码。严格按删除顺序:消费者/引用方先删,生产者/被引用类后删。
步骤1 ImageReceiveCompleteEventHandle: 删 case AUTOFOCUS 分支 + focusPointUpdate 方法 + autofocusFeign 字段 + import
↓(focusPointUpdate 删后,AutofocusFeign / CalAutoFocusDTO 失去唯一/全部调用方)
步骤2 AutofocusFeign 整类删(连带 CLOUD_SERVER_FOCUS_ALARM 该处引用)
↓(AutofocusFeign 删后,tl-control 的 AutofocusApi Feign 失去唯一远程调用方)
步骤3 cutAutofocus 私有方法删 + cropPicture 两个重载的 AUTOFOCUS else 分支改(见 4.1 备注)
↓(cutAutofocus 删后,PictureProcessing.pictureScore + Picture.getscore 失去唯一调用方)
步骤4 PictureProcessing(Impl).pictureScore + Picture.getscore native 绑定删
↓(D5-A 范围,与 M3-03 衔接)
步骤5 tl-control: AutofocusApiController / AutofocusApi / CalAutoFocusPositionParamDTO / AutofocusUpdateEvent(Handle) 入云端回传部分按 M3-03 处理(保留 calPhotoPosition 本体)
imageDataHandle 的 case AUTOFOCUS + focusPointUpdate 方法aivfo-data-transmission/aivfo-data-transmission-manage/src/main/java/com/aivfo/data/transmission/manage/event/ImageReceiveCompleteEventHandle.java:72-78 imageDataHandle 中的 case AUTOFOCUS: 整段(含 this.focusPointUpdate(imageBaseDataDTO);)。删后 switch 只剩 case CCD(视频合成)+ default。:120-149 private void focusPointUpdate(ImageBaseDataDTO imageBaseDataDTO) 整个方法(核心选层就在 :143 stream().max(Comparator.comparing(PictureDAO::getImageScore)).get() 和 :144 setClearPosition(max.getShootingPosition()))。:40 字段 final AutofocusFeign autofocusFeign;(删 focusPointUpdate 后无引用)。:5 import ...entity.dto.CalAutoFocusDTO;、:8 import ...rpc.feign.server.AutofocusFeign;、:24 import java.util.Comparator;(删后若无其它使用则一并清,避免 unused import 告警)。pictureService.getAutofocusPictureByEmbryoIdAndBatchNumber(PictureDAOServiceImpl:263)在本链不再被调用(仍是 public service 方法,其内部 :270 orderByDesc 由 M3-02 处理);autofocusFeign.calAutoFocusDTO 调用点消失,使步骤 2 可安全删 AutofocusFeign。autofocusFeign 后,@RequiredArgsConstructor 生成的构造器自动少一参,无残留引用——安全。AutofocusFeign 整类aivfo-data-transmission/aivfo-data-transmission-rpc/src/main/java/com/aivfo/data/transmission/rpc/feign/server/AutofocusFeign.java:1-66。codegraph 确认其 2 个调用点全部在 ImageReceiveCompleteEventHandle(步骤 1 已删),删后无其它引用。:55 AlarmTypeKeyEnums.CLOUD_SERVER_FOCUS_ALARM.getValue()("触发自动对焦失败云服务器报警")随该类删除而消失——这是打分链回传失败告警,与下位机定位失败告警 FocusAlarmHandler 无关,不要去动后者。AutofocusApi(Feign 接口)失去唯一远程调用方 → 步骤 5 / M3-03 可删 AutofocusApiController。AlarmReportFeign.reportAlarmToCloud 仍被抠图告警等多处使用,保留不动。cutAutofocus + 改 cropPicture 的 AUTOFOCUS 分支aivfo-data-transmission/aivfo-data-transmission-manage/src/main/java/com/aivfo/data/transmission/manage/image/PictureManage.java(⚠️ data-transmission 侧,非 AI 侧):331-355 private CropPictureInfo cutAutofocus(...) 整个方法(其 :336 调 pictureProcessing.pictureScore(...) 即打分入口)。cropPicture(image, client, cropPicture, savePicture) 重载 :244-247 else 分支当前为 cut = this.cutAutofocus(...)(注释"开始图片打分")。cropPicture(tlSn, houseSn, ...)(04 §4.2 标注 :174-182)的对应 AUTOFOCUS 分支同理。cut = this.cutAutofocus(...) 改为 cut = BeanUtils.clone(defaultPicture); cut.setSucceed(CropState.NOT_OPERATED.getValue());(即与 :217-223 CROPPING_SKIP 同形态,score 字段不再写入)。这样下游 buildUpdateDao 仍能落库,image_score 自然为 null(由 M3-02 兜底)。cutCCD(:273-313,走 pictureProcessing.pictureCropping 抠图加水印)原样不动。PictureProcessing.pictureScore 失去唯一调用方(cutAutofocus)→ 步骤 4 可删。cutCCD、pictureCropping、createErrorImg 不受影响(② 链)。=this.cutAutofocus(...) 会编译失败,必须同一提交内改掉两处 else 分支。pictureScore + native getscore 绑定aivfo-framework/module/aivfo-jna-spring-boot/aivfo-jna-spring-boot-core/src/main/java/com/aivfo/jna/picture/PictureProcessing.java
:52 接口方法 CropPictureInfo pictureScore(...)。aivfo-framework/module/aivfo-jna-spring-boot/aivfo-jna-spring-boot-core/src/main/java/com/aivfo/jna/picture/PictureProcessingImpl.java
:90-104 @Override pictureScore(...)(其 :96 picture.getscore(...) 是 JNA→opencv 打分 native 调用)。aivfo-framework/module/aivfo-jna-spring-boot/aivfo-jna-spring-boot-core/src/main/java/com/aivfo/jna/picture/Picture.java
:46 native 声明 CropPictureInfoC getscore(...)。findcircles(抠图 :30)、createErrorImg(:59)保留——② 链仍用。aivfo-jna-spring-boot-starter 下有测试 JnaApplication.java 引用 PictureProcessing,核查其是否调 pictureScore/getscore,若是则一并清理测试调用(登记 V-004)。pictureScore 调用悬空。本步只标范围,实际改法见 M3-03(保留 calPhotoPosition 本体、按 D5-A 改输入源)。AutofocusApiController/AutofocusApi/AutofocusUpdateEvent(Handle) 的去留取决于"本地对焦结果怎样进入 calPhotoPosition",故合并到 M3-03 一并决策,避免割裂。
来源:审核报告维度③第 12 项 / 遗漏项 2。04 文档把打分全归云端,遗漏机旁 C# 自带 native 打分。
ivf_tl_control_2.0 客户端
HouseBin.cs:2446 GetImageScoreAndSaveImage(调 native 打分并存图)AivfoHelper.cs:35(P/Invoke native 打分入口)picture.cs:150 image_score 字段GetImageScoreAndSaveImage 调用的 native 与云端 data-transmission 的 getscore(opencv_world*.dll) 是否同源/同 dll(比对 dll 名、导出符号、入参签名)。HouseBin.cs:2446 的上游触发:是否在"对焦拍图"流程中被调用、其输出(image_score)是否仍被机旁端用于选层或仅存档。image_score 删后对 AUTOFOCUS 行全 null。业务消费点必须改判据,否则一打开患者详情/对焦预览即 NPE 崩溃。 生产侧(停写):
ReceivePictureCroppingMessage.java:226/229(buildUpdateDaosetImageScore(getScore()))与ReceivePictureMessageInfo.java:179(savePicture.setImageScore(cropPictureInfo.getScore()))随 M3-01 步骤 3 后 score 恒为占位值/null——这两处生产语句可保留(写 null 不报错),无需强删;列t_picture.image_score保留停写。
getAutofocusPictures 选最高分aivfo-business-manage/.../service/impl/ResourceServiceImpl.java:462-481
:470 t.setImageName(StringUtils.format("{}_{}_{}_{}", getHouseSn(), getWellSn(), getShootingPosition(), s.getImageScore())) —— imageScore 为 null 时名字拼成 ..._null。:476 copy.stream().max(Comparator.comparing(A -> A.getImageScore())).get().setHighestScore(true) —— null 键 compareTo 抛 NPE;空流 .get() 抛 NoSuchElement。一点开对焦预览就崩。:470:去掉文件名末段的 getImageScore(),改为 "{}_{}_{}" 只拼 houseSn/wellSn/shootingPosition(或追加 image_time),不再带分数。:476:删除"选最高分→setHighestScore(true)"逻辑。换判据为 ② 体系的 clearest/居中层——例如对 CropState.SUCCEED 且 DegreeOfClarity.CLEAREST 的项设 highestScore,或取 pictureLayer==0(居中层);若无明确清晰标记则不高亮(highestScore 全 false),避免 NPE。最小实现:用 Optional 包裹并对空/ null 做兜底(filter(x->x.getImageScore()!=null) 后 findFirst),无则跳过。FocusPreviewPictureVO.imageScore/highestScore(:72/:78)失去来源——保留为兼容字段(值为 null/false)或删;前端 AutoFocusWindow.xaml.cs:149/152 读这两字段,需 D10 同步确认(登记 V-005)。getFocusBestPicture 的 compareToaivfo-data-transmission/.../impl/PictureDAOServiceImpl.java:173-183 方法内 :179 Collectors.reducing((c1, c2) -> c1.getImageScore().compareTo(c2.getImageScore()) > 0 ? c1 : c2) —— getImageScore() 为 null 时 .compareTo 直接 NPE。被 business-manage getFocusBestPicture(ResourceServiceImpl 经 Feign / PictureService:236)调用,"最佳对焦帧"查询触发即崩。getImageScore 改为 getImageTime(取最新对焦帧)或基于 clearest。即 c1.getImageTime().compareTo(c2.getImageTime()) > 0 ? c1 : c2,并对底层 SQL selectFocusBestPicture 是否仍按分数排序一并核查(若 mapper xml 内有 order by image_score 同步改 image_time)。buildDetailData(...focusBestPicture...)(ResourceService:283)消费该 Map,判据改后帧选取语义变化——登记 V-005 回归详情时间线。getPictureByDevelopTime AUTOFOCUS 分支 orderByDescaivfo-business-manage/.../service/impl/ResourceServiceImpl.java:316-357,:333 eq.orderByDesc(PictureDAO::getImageScore);(在 :331-334 的 else(layer 为 null → 查 AUTOFOCUS)分支内)。:333 排序键从 getImageScore 改为 getImageTime(与改点 2 一致,取该发育时间窗内最新对焦帧),即 eq.orderByDesc(PictureDAO::getImageTime);。aivfo-data-transmission/.../impl/PictureDAOServiceImpl.java:270 getAutofocusPictureByEmbryoIdAndBatchNumber 末 .orderByDesc(PictureDAO::getImageScore)(原是 focusPointUpdate 调的查询,M3-01 删 focusPointUpdate 后该方法在本链不再被调,但仍是 public service,避免遗留排序退化)。:325 pageQueryPictures 末位 .orderByDesc(...getEmbryoId).orderByDesc(...getImageTime).orderByDesc(PictureDAO::getImageScore)。:270 排序键改 getImageTime;:325 删除末位 .orderByDesc(getImageScore)(前面已有 getEmbryoId + getImageTime 排序,足够稳定)。D5 已可拍板选 A(审核维度④:calPhotoPosition 只依赖"最清晰位置 clearPosition"这一输入,本地标定的 FocusZ 可直接灌入,改动小、保留云端可追溯)。
[旧] AutofocusFeign.calAutoFocusDTO (data-transmission, M3-01已删)
→ Feign → AutofocusApi.calAutofocusPosition
→ AutofocusApiController.calAutofocusPosition (:35-48, 发布 AutofocusUpdateEvent)
→ AutofocusUpdateEventHandle.handler (:32-49, 取 clearPosition/mattingSuccessNumber)
→ HousePhotographSettingManageImpl.calPhotoPosition (:50-89)
- first 分支(:54-68): 初始化, 用 eepromClearPosition 算 AUTOFOCUS 起点+CCD 位置
- else 分支(:69-88): 用 clearPosition 算对焦起点(:77 calAutofocusStart) + CCD 位置(:84 calCCDPosition), 写 t_house_photograph_setting
calAutofocusStart:125 对焦起点 = clearPosition - (moveDownLayer × verticalMotorSpacePulse);calCCDPosition:145 据 clearestPosition 生成各 CCD 层位置。两者只吃 clearPosition 这个位置值,与打分解耦。
HousePhotographSettingManageImpl.calPhotoPosition(:50-89)、calAutofocusStart(:104-129)、calCCDPosition(:144-168)、HousePhotographSettingManage 接口(:30)—— 几何换算逻辑完整、与打分无关,整体保留。clearPosition 来自 data-transmission focusPointUpdate 选最高分图位置(已删)。新来源为 M2 本地对焦算出的最清晰位置(FocusZ),衔接点二选一(登记 D5-A 落地待定):
AutofocusApiController.calAutofocusPosition(:35-48)作为"本地对焦结果上报"接口,由机旁本地对焦上报 CalAutoFocusPositionParamDTO(clearPosition=本地 FocusZ、mattingSuccessNumber=本地成功层数)→ 仍发 AutofocusUpdateEvent → calPhotoPosition。改动最小:data-transmission 侧的 AutofocusFeign 删了,但 controller 入口保留,改由机旁上报。house_autofocus_calibration(数据层,M2 产出)。需确认 clearPosition 的脉冲基准与 AutofocusSettingDTO(verticalMotorSpacePulse / moveDownLayer / updateFocusNumber / updateClearestNumber)口径一致——本地 FocusZ 须换算成与原 shootingPosition 同单位的脉冲值再灌入。first 分支用 eepromClearPosition 仍走 EEPROM 初始化(与 D9 EEPROM 回写硬阻塞相关,本任务不动);else 分支 mattingSuccessNumber 阈值判断(:73/:80)依赖机旁上报的成功层数,需确认本地对焦能提供该值。删 ① 后逐项确认 ② 链与无关链路未被波及。全部为回归验证点(M7 集中测),本任务不改代码,只列清单。
| 回归点 | 文件:行号 | 验证内容 | 待验证 |
|---|---|---|---|
| CCD 抠图加水印 | PictureManage.java cutCCD :273-313;PictureProcessingImpl.pictureCropping :48-74(→ Picture.findcircles :30) | CCD 图抠图/水印像素级与改前一致 | V-R01 |
| 收图/抠图/落库/转发主体 | ReceivePictureMessageInfo.receive;ReceivePictureCroppingMessage.receive :76-199 | CCD 流程收图→抠图→落库→转发完整 | V-R02 |
| 视频合成 | ImageReceiveCompleteEventHandle.imageDataHandle case CCD → saveVideoSpliceInfo :70/:92 | CCD 末张触发视频合成正常 | V-R03 |
| AI 识别转发(按 CCD clearest) | ReceivePictureCroppingMessage.java :172-178(条件 PhotoMode.CCD + CropState.SUCCEED + 层数命中 → PictureIdentifyEvent) |
AI 仅按 CCD clearest 选图送识别,不读 image_score;文件名层号 currentFocal 正常填充 | V-R04 |
| DegreeOfClarity / getClearest 体系 | DegreeOfClarity 枚举;getClearestByDevelopTime(ResourceService:121);PictureDAOServiceImpl.getPictureList :117(eq(pictureLayer==0, getClearest, CLEAREST)) |
clearest 查询走下位机标记,与选层打分独立,不受影响 | V-R05 |
| ai-middleware / gateway / aivfo-service | — | 全量搜 image_score 无匹配,不改 | V-R06 冒烟 |
| 下位机定位失败告警 | FocusAlarmHandler(用 focusState,与打分无关) | 对焦失败告警仍正常触发(非 CLOUD_SERVER_FOCUS_ALARM) | V-R07 |
| 编号 | 内容 | 依赖 |
|---|---|---|
| V-001 | AUTOFOCUS 图到达不再触发对焦回传、仅落库;CCD 视频合成正常 | 微服务集群+Kafka |
| V-002 | tl-control 不再收 calAutofocusPosition 远程调用、无 Feign 报错、启动无 bean 缺失 | 微服务集群 |
| V-002b | data-transmission 两处 orderByDesc 改 image_time 后列表/分页次序合理 | DB |
| V-003 | AUTOFOCUS 走 NOT_OPERATED 落库 image_score=null 不报错;CCD 抠图水印像素级回归 | 微服务集群+DFS |
| V-003c | getPictureByDevelopTime 改 image_time 后"按发育时间取最佳对焦帧"临床展示正确 | DB |
| V-004 | jna 模块编译通过、不加载 getscore;JnaApplication 测试引用同步清理 | SpringBoot 工具链 |
| V-005 | business-manage 患者详情/对焦预览/最佳帧不 NPE;FocusPreviewPictureVO 字段去留与前端 AutoFocusWindow 同步(D10) | 微服务集群+前端 |
| V-006 | 本地对焦→calPhotoPosition→t_house_photograph_setting→拍照位置正确;脉冲基准/口径一致;mattingSuccessNumber 来源 | 微服务集群+DB+真机(衔接 M2) |
| V-007 | C# native 打分(HouseBin.cs:2446)与云端 getscore 是否同源/是否随本地对焦废弃(归 M2/合并端,不在本计划改 C#) | 真机+C# 工程 |
| V-R01~R07 | M3-04 保留链回归(抠图/水印/视频/AI/clearest/告警) | 微服务集群 |
DegreeOfClarity.CLEAREST 查询 / eq(PhotoMode.CCD) 任一被误删 → 患者看不到图、AI 不识别、前端展示空白。护栏:删任何代码前确认 PhotoMode——碰 AUTOFOCUS 才删,碰 CCD/CLEAREST 一律保留;改 PictureManage 只动 data-transmission 侧,不碰 ai-middleware 同名类。