# 后端修复:转文本更新时去掉tenant_id限制 ## ⚡ 快速修复(推荐) **最快解决方案**:在Mapper接口中添加自定义更新方法 1. 在 `AudioManagementSegmentsMapper.java` 中添加: ```java @InterceptorIgnore(tenantLine = "true") @Update("UPDATE audio_management_segments SET recording_text = #{recordingText}, transcribe_end_time = #{transcribeEndTime} WHERE id = #{id}") int updateTranscriptionWithoutTenant( @Param("id") String id, @Param("recordingText") String recordingText, @Param("transcribeEndTime") LocalDateTime transcribeEndTime ); ``` 2. 在Controller的转文本方法中,将: ```java boolean updateSuccess = audioManagementSegmentsService.update(textUpdateEntity, textUpdateWrapper); ``` 替换为: ```java int updateCount = audioManagementSegmentsMapper.updateTranscriptionWithoutTenant( segmentId, transcriptionText, LocalDateTime.now() ); boolean updateSuccess = updateCount > 0; ``` --- ## 问题描述 在转文本功能中,更新 `audio_management_segments` 表时,MyBatis-Plus 的多租户插件自动添加了 `AND tenant_id = null` 条件,导致更新失败。 从日志可以看到: ```sql UPDATE audio_management_segments SET recording_text=?, transcribe_end_time=? WHERE (id = ?) AND tenant_id = null ``` ## 解决方案 ### 方案1:在Controller层使用baseMapper直接更新(最简单有效) 在转文本的Controller方法中,直接使用 `baseMapper` 更新,绕过Service层的多租户拦截: ```java @PostMapping("/transcribe/{id}") @Operation(summary = "转文本", description = "分段音频文件转文本") public ResponseEntity> transcribeSegmentById( @Parameter(description = "分段ID", required = true) @PathVariable String id) { try { // ... 转文本逻辑 ... // 更新录音文本 - 直接使用baseMapper,绕过多租户拦截 AudioManagementSegments textUpdateEntity = new AudioManagementSegments(); textUpdateEntity.setRecordingText(transcriptionText); textUpdateEntity.setTranscribeEndTime(LocalDateTime.now()); LambdaUpdateWrapper textUpdateWrapper = new LambdaUpdateWrapper<>(); textUpdateWrapper.eq(AudioManagementSegments::getId, segmentId); // 关键:使用 baseMapper 直接更新,并设置忽略多租户拦截 // 方法1:使用 @SqlParser(filter = true) - 旧版本MyBatis-Plus // 方法2:直接调用 baseMapper.update,但需要在Mapper方法上添加注解 // 推荐:在Mapper接口中添加自定义更新方法 int updateCount = audioManagementSegmentsMapper.updateTranscriptionWithoutTenant( segmentId, transcriptionText, LocalDateTime.now() ); boolean updateSuccess = updateCount > 0; // ... 其他逻辑 ... } catch (Exception e) { // ... 错误处理 ... } } ``` ### 方案2:在Mapper接口中添加自定义更新方法(推荐) 在 `AudioManagementSegmentsMapper` 中添加自定义更新方法,使用 `@InterceptorIgnore` 注解: ```java @Mapper public interface AudioManagementSegmentsMapper extends BaseMapper { /** * 更新转文本结果,忽略多租户拦截 */ @InterceptorIgnore(tenantLine = "true") @Update("UPDATE audio_management_segments SET recording_text = #{recordingText}, transcribe_end_time = #{transcribeEndTime} WHERE id = #{id}") int updateTranscriptionWithoutTenant( @Param("id") String id, @Param("recordingText") String recordingText, @Param("transcribeEndTime") LocalDateTime transcribeEndTime ); } ``` 然后在Controller中调用: ```java // 在转文本方法中 int updateCount = audioManagementSegmentsMapper.updateTranscriptionWithoutTenant( segmentId, transcriptionText, LocalDateTime.now() ); boolean updateSuccess = updateCount > 0; ``` ### 方案3:在Service层添加专门的方法并禁用多租户拦截 在 `AudioManagementSegmentsService` 接口中添加新方法: ```java public interface AudioManagementSegmentsService extends IService { /** * 更新转文本结果,忽略多租户拦截 */ boolean updateTranscription(String id, String recordingText, LocalDateTime transcribeEndTime); } ``` 在实现类中: ```java @Service public class AudioManagementSegmentsServiceImpl extends ServiceImpl implements AudioManagementSegmentsService { @Override @InterceptorIgnore(tenantLine = "true") // 禁用多租户拦截 public boolean updateTranscription(String id, String recordingText, LocalDateTime transcribeEndTime) { AudioManagementSegments entity = new AudioManagementSegments(); entity.setId(id); entity.setRecordingText(recordingText); entity.setTranscribeEndTime(transcribeEndTime); LambdaUpdateWrapper wrapper = new LambdaUpdateWrapper<>(); wrapper.eq(AudioManagementSegments::getId, id); // 注意:这里仍然可能被拦截,所以最好使用Mapper的自定义方法 return this.update(entity, wrapper); } } ``` **注意**:如果方案3仍然被拦截,请使用方案2(Mapper层的自定义方法)。 ### 方案4:配置多租户插件排除该表(全局方案) 在 MyBatis-Plus 的多租户配置中,排除 `audio_management_segments` 表: ```java @Configuration public class MybatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); // 多租户插件 TenantLineInnerInterceptor tenantInterceptor = new TenantLineInnerInterceptor(); tenantInterceptor.setTenantLineHandler(new TenantLineHandler() { @Override public Expression getTenantId() { // 返回租户ID(从上下文获取) String tenantId = TenantContextHolder.getTenantId(); return tenantId != null ? new StringValue(tenantId) : null; } @Override public String getTenantIdColumn() { return "tenant_id"; } @Override public boolean ignoreTable(String tableName) { // 排除 audio_management_segments 表,不进行多租户拦截 return "audio_management_segments".equalsIgnoreCase(tableName); } }); interceptor.addInnerInterceptor(tenantInterceptor); return interceptor; } } ``` **注意**:这个方案会影响所有对 `audio_management_segments` 表的操作,请确保安全性。 ### 方案4:使用自定义SQL(不推荐,但可行) 如果以上方案都不适用,可以使用自定义SQL,直接执行更新语句: ```java @Mapper public interface AudioManagementSegmentsMapper extends BaseMapper { @Update("UPDATE audio_management_segments SET recording_text = #{recordingText}, transcribe_end_time = #{transcribeEndTime} WHERE id = #{id}") int updateTranscription(@Param("id") String id, @Param("recordingText") String recordingText, @Param("transcribeEndTime") LocalDateTime transcribeEndTime); } ``` ## 推荐方案(按优先级) ### 最推荐:方案2 - Mapper层自定义方法 **强烈推荐使用方案2**,在Mapper接口中添加自定义更新方法,使用 `@InterceptorIgnore` 注解: **优点**: 1. ✅ 最直接有效,不会被拦截器拦截 2. ✅ 只影响转文本的更新操作 3. ✅ 不影响其他查询和更新操作 4. ✅ 代码清晰,易于维护 5. ✅ 性能好,直接执行SQL **实现步骤**: 1. 在 `AudioManagementSegmentsMapper` 接口中添加方法(见方案2代码) 2. 在Controller中调用 `audioManagementSegmentsMapper.updateTranscriptionWithoutTenant()` 3. 替换原来的 `service.update()` 调用 ### 备选方案:方案4 - 配置排除表 如果方案2不适用,可以使用方案4,在配置中排除整个表: - ⚠️ 会影响所有对该表的操作 - ⚠️ 需要确保安全性 - ✅ 一次配置,全局生效 ## 注意事项 1. 如果使用方案3(全局排除表),会影响所有对该表的操作,需要确保安全性 2. 如果使用方案4(自定义SQL),需要手动处理SQL注入防护 3. 修改后需要测试其他功能是否受影响