口才和会议场景
This commit is contained in:
246
docs/backend-fix-tenant-id-issue.md
Normal file
246
docs/backend-fix-tenant-id-issue.md
Normal file
@@ -0,0 +1,246 @@
|
||||
# 后端修复:转文本更新时去掉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仍然被拦截,请使用方案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<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. 修改后需要测试其他功能是否受影响
|
||||
|
||||
Reference in New Issue
Block a user