8.6 KiB
8.6 KiB
后端修复:转文本更新时去掉tenant_id限制
⚡ 快速修复(推荐)
最快解决方案:在Mapper接口中添加自定义更新方法
- 在
AudioManagementSegmentsMapper.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
);
- 在Controller的转文本方法中,将:
boolean updateSuccess = audioManagementSegmentsService.update(textUpdateEntity, textUpdateWrapper);
替换为:
int updateCount = audioManagementSegmentsMapper.updateTranscriptionWithoutTenant(
segmentId, transcriptionText, LocalDateTime.now()
);
boolean updateSuccess = updateCount > 0;
问题描述
在转文本功能中,更新 audio_management_segments 表时,MyBatis-Plus 的多租户插件自动添加了 AND tenant_id = null 条件,导致更新失败。
从日志可以看到:
UPDATE audio_management_segments SET recording_text=?, transcribe_end_time=?
WHERE (id = ?) AND tenant_id = null
解决方案
方案1:在Controller层使用baseMapper直接更新(最简单有效)
在转文本的Controller方法中,直接使用 baseMapper 更新,绕过Service层的多租户拦截:
@PostMapping("/transcribe/{id}")
@Operation(summary = "转文本", description = "分段音频文件转文本")
public ResponseEntity<Map<String, Object>> transcribeSegmentById(
@Parameter(description = "分段ID", required = true)
@PathVariable String id) {
try {
// ... 转文本逻辑 ...
// 更新录音文本 - 直接使用baseMapper,绕过多租户拦截
AudioManagementSegments textUpdateEntity = new AudioManagementSegments();
textUpdateEntity.setRecordingText(transcriptionText);
textUpdateEntity.setTranscribeEndTime(LocalDateTime.now());
LambdaUpdateWrapper<AudioManagementSegments> 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 注解:
@Mapper
public interface AudioManagementSegmentsMapper extends BaseMapper<AudioManagementSegments> {
/**
* 更新转文本结果,忽略多租户拦截
*/
@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中调用:
// 在转文本方法中
int updateCount = audioManagementSegmentsMapper.updateTranscriptionWithoutTenant(
segmentId,
transcriptionText,
LocalDateTime.now()
);
boolean updateSuccess = updateCount > 0;
方案3:在Service层添加专门的方法并禁用多租户拦截
在 AudioManagementSegmentsService 接口中添加新方法:
public interface AudioManagementSegmentsService extends IService<AudioManagementSegments> {
/**
* 更新转文本结果,忽略多租户拦截
*/
boolean updateTranscription(String id, String recordingText, LocalDateTime transcribeEndTime);
}
在实现类中:
@Service
public class AudioManagementSegmentsServiceImpl extends ServiceImpl<AudioManagementSegmentsMapper, AudioManagementSegments>
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<AudioManagementSegments> wrapper = new LambdaUpdateWrapper<>();
wrapper.eq(AudioManagementSegments::getId, id);
// 注意:这里仍然可能被拦截,所以最好使用Mapper的自定义方法
return this.update(entity, wrapper);
}
}
注意:如果方案3仍然被拦截,请使用方案2(Mapper层的自定义方法)。
方案4:配置多租户插件排除该表(全局方案)
在 MyBatis-Plus 的多租户配置中,排除 audio_management_segments 表:
@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,直接执行更新语句:
@Mapper
public interface AudioManagementSegmentsMapper extends BaseMapper<AudioManagementSegments> {
@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 注解:
优点:
- ✅ 最直接有效,不会被拦截器拦截
- ✅ 只影响转文本的更新操作
- ✅ 不影响其他查询和更新操作
- ✅ 代码清晰,易于维护
- ✅ 性能好,直接执行SQL
实现步骤:
- 在
AudioManagementSegmentsMapper接口中添加方法(见方案2代码) - 在Controller中调用
audioManagementSegmentsMapper.updateTranscriptionWithoutTenant() - 替换原来的
service.update()调用
备选方案:方案4 - 配置排除表
如果方案2不适用,可以使用方案4,在配置中排除整个表:
- ⚠️ 会影响所有对该表的操作
- ⚠️ 需要确保安全性
- ✅ 一次配置,全局生效
注意事项
- 如果使用方案3(全局排除表),会影响所有对该表的操作,需要确保安全性
- 如果使用方案4(自定义SQL),需要手动处理SQL注入防护
- 修改后需要测试其他功能是否受影响