Files
smartDriveEEUniApp/docs/backend-fix-tenant-id-issue.md
2026-01-09 19:41:18 +08:00

247 lines
8.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 后端修复转文本更新时去掉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<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` 注解:
```java
@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中调用
```java
// 在转文本方法中
int updateCount = audioManagementSegmentsMapper.updateTranscriptionWithoutTenant(
segmentId,
transcriptionText,
LocalDateTime.now()
);
boolean updateSuccess = updateCount > 0;
```
### 方案3在Service层添加专门的方法并禁用多租户拦截
`AudioManagementSegmentsService` 接口中添加新方法:
```java
public interface AudioManagementSegmentsService extends IService<AudioManagementSegments> {
/**
* 更新转文本结果,忽略多租户拦截
*/
boolean updateTranscription(String id, String recordingText, LocalDateTime transcribeEndTime);
}
```
在实现类中:
```java
@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仍然被拦截请使用方案2Mapper层的自定义方法
### 方案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<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` 注解:
**优点**
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. 修改后需要测试其他功能是否受影响