From fd9ef5c8852518cbf1ac7bf5e4912f21b9e57450 Mon Sep 17 00:00:00 2001 From: chenhao Date: Fri, 12 Jun 2026 09:09:44 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E7=A7=9F=E6=88=B7?= =?UTF-8?q?=E4=B8=9A=E5=8A=A1=E6=95=B0=E6=8D=AE=E9=80=BB=E8=BE=91=E5=88=A0?= =?UTF-8?q?=E9=99=A4=E5=92=8C=E4=BC=98=E5=8C=96=E4=BC=9A=E8=AE=AE=E7=8A=B6?= =?UTF-8?q?=E6=80=81=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在多个 Mapper 中添加 `logicalDeleteByTenantId` 方法,支持按租户 ID 逻辑删除数据 - 优化 `MeetingUnifiedStatusServiceImpl` 中的会议状态处理逻辑,调整 `isAndroidOfflineMeetingWaitingUpload` 的调用位置 - 更新 `Meetings.tsx` 中的会议状态判断逻辑,新增 `isUnifiedTerminalProgress` 方法 - 优化 `AndroidChunkUploadServiceImpl` 中的文件上传逻辑,移除不必要的 `try-finally` 块 - 在 `TenantManagementServicePrimaryImpl` 中添加逻辑删除租户业务数据的方法,并更新相关依赖注入 --- .../com/imeeting/mapper/LicenseMapper.java | 10 ++ .../biz/MeetingPointsAccountMapper.java | 10 ++ .../mapper/biz/MeetingPointsLedgerMapper.java | 10 ++ .../biz/MeetingSummaryChargeRecordMapper.java | 10 ++ .../biz/TenantMeetingPointsSettingMapper.java | 12 ++ .../impl/AndroidChunkUploadServiceImpl.java | 38 ++++--- .../impl/MeetingUnifiedStatusServiceImpl.java | 9 +- .../TenantManagementServicePrimaryImpl.java | 107 +++++++----------- frontend/src/pages/business/Meetings.tsx | 15 ++- 9 files changed, 133 insertions(+), 88 deletions(-) diff --git a/backend/src/main/java/com/imeeting/mapper/LicenseMapper.java b/backend/src/main/java/com/imeeting/mapper/LicenseMapper.java index 54e78eb..9788825 100644 --- a/backend/src/main/java/com/imeeting/mapper/LicenseMapper.java +++ b/backend/src/main/java/com/imeeting/mapper/LicenseMapper.java @@ -220,4 +220,14 @@ public interface LicenseMapper extends BaseMapper { AND is_deleted = 0 """) int invalidateById(@Param("id") Long id); + + @InterceptorIgnore(tenantLine = "true") + @Update(""" + UPDATE biz_license + SET is_deleted = 1, + updated_at = CURRENT_TIMESTAMP + WHERE tenant_id = #{tenantId} + AND is_deleted = 0 + """) + int logicalDeleteByTenantId(@Param("tenantId") Long tenantId); } diff --git a/backend/src/main/java/com/imeeting/mapper/biz/MeetingPointsAccountMapper.java b/backend/src/main/java/com/imeeting/mapper/biz/MeetingPointsAccountMapper.java index 99d6050..c981056 100644 --- a/backend/src/main/java/com/imeeting/mapper/biz/MeetingPointsAccountMapper.java +++ b/backend/src/main/java/com/imeeting/mapper/biz/MeetingPointsAccountMapper.java @@ -5,6 +5,7 @@ import com.imeeting.entity.biz.MeetingPointsAccount; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Select; +import org.apache.ibatis.annotations.Update; @Mapper public interface MeetingPointsAccountMapper extends BaseMapper { @@ -18,4 +19,13 @@ public interface MeetingPointsAccountMapper extends BaseMapper { + @Update(""" + UPDATE biz_meeting_points_ledgers + SET is_deleted = 1, + updated_at = CURRENT_TIMESTAMP + WHERE tenant_id = #{tenantId} + AND is_deleted = 0 + """) + int logicalDeleteByTenantId(@Param("tenantId") Long tenantId); } diff --git a/backend/src/main/java/com/imeeting/mapper/biz/MeetingSummaryChargeRecordMapper.java b/backend/src/main/java/com/imeeting/mapper/biz/MeetingSummaryChargeRecordMapper.java index 11209dd..ce26f87 100644 --- a/backend/src/main/java/com/imeeting/mapper/biz/MeetingSummaryChargeRecordMapper.java +++ b/backend/src/main/java/com/imeeting/mapper/biz/MeetingSummaryChargeRecordMapper.java @@ -5,6 +5,7 @@ import com.imeeting.entity.biz.MeetingSummaryChargeRecord; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Select; +import org.apache.ibatis.annotations.Update; @Mapper public interface MeetingSummaryChargeRecordMapper extends BaseMapper { @@ -17,4 +18,13 @@ public interface MeetingSummaryChargeRecordMapper extends BaseMapper { @@ -17,4 +19,14 @@ public interface TenantMeetingPointsSettingMapper extends BaseMapper> listTenants(Integer current, Integer size, String name, String code) { - if (isSingleTenantMode()) { - SysTenantDTO defaultTenant = sysTenantService.findById(getDefaultTenantId()); - PageResult> result = new PageResult<>(); - result.setRecords(defaultTenant == null ? List.of() : List.of(defaultTenant)); - result.setTotal(defaultTenant == null ? 0 : 1); - return result; - } - return sysTenantService.page(current, size, name, code); + return delegate.listTenants(current, size, name, code); } @Override public SysTenantDTO getTenant(Long tenantId) { - return sysTenantService.findById(tenantId); + return delegate.getTenant(tenantId); } @Override @Transactional(rollbackFor = Exception.class) public Long createTenant(CreateTenantDTO tenant) { - assertTenantLifecycleAllowed(); - Long tenantId = sysTenantService.createTenantWithAdmin(tenant); + Long tenantId = delegate.createTenant(tenant); licenseService.initializeTemporaryLicenses(tenantId); meetingPointsService.initializeTenantPointsAccount(tenantId); tenantMeetingPointsSettingService.initializeTenantSetting(tenantId); @@ -68,55 +75,25 @@ public class TenantManagementServicePrimaryImpl implements TenantManagementServi @Override public boolean updateTenant(Long tenantId, SysTenantDTO tenant) { - assertDefaultTenantCanBeUpdated(tenantId, tenant == null ? null : tenant.getStatus()); - if (tenant == null) { - throw new IllegalArgumentException("租户信息不能为空"); - } - tenant.setId(tenantId); - return sysTenantService.update(tenant); + return delegate.updateTenant(tenantId, tenant); } @Override + @Transactional(rollbackFor = Exception.class) public boolean deleteTenant(Long tenantId) { - assertTenantLifecycleAllowed(); - return sysTenantService.deleteById(tenantId); + boolean deleted = delegate.deleteTenant(tenantId); + if (!deleted || tenantId == null) { + return deleted; + } + logicalDeleteTenantBizData(tenantId); + return true; } - private boolean isSingleTenantMode() { - if (unisBaseProperties == null || unisBaseProperties.getTenant() == null) { - return false; - } - String mode = unisBaseProperties.getTenant().getMode(); - if (mode != null && !mode.isBlank()) { - return "single".equalsIgnoreCase(mode.trim()); - } - return !unisBaseProperties.getTenant().isEnabled(); - } - - private Long getDefaultTenantId() { - if (unisBaseProperties == null || unisBaseProperties.getTenant() == null) { - return DEFAULT_TENANT_ID; - } - Long configured = unisBaseProperties.getTenant().getDefaultTenantId(); - return configured == null || configured <= 0 ? DEFAULT_TENANT_ID : configured; - } - - private void assertTenantLifecycleAllowed() { - if (isSingleTenantMode()) { - throw new IllegalArgumentException("当前为单租户模式,不支持租户生命周期操作"); - } - } - - private void assertDefaultTenantCanBeUpdated(Long tenantId, Integer status) { - if (!isSingleTenantMode()) { - return; - } - Long defaultTenantId = getDefaultTenantId(); - if (!defaultTenantId.equals(tenantId)) { - throw new IllegalArgumentException("当前为单租户模式,只允许维护默认租户"); - } - if (status != null && status != 1) { - throw new IllegalArgumentException("当前为单租户模式,不允许禁用默认租户"); - } + private void logicalDeleteTenantBizData(Long tenantId) { + tenantMeetingPointsSettingMapper.logicalDeleteByTenantId(tenantId); + meetingPointsLedgerMapper.logicalDeleteByTenantId(tenantId); + meetingSummaryChargeRecordMapper.logicalDeleteByTenantId(tenantId); + meetingPointsAccountMapper.logicalDeleteByTenantId(tenantId); + licenseMapper.logicalDeleteByTenantId(tenantId); } } diff --git a/frontend/src/pages/business/Meetings.tsx b/frontend/src/pages/business/Meetings.tsx index 53f1eb5..9eac195 100644 --- a/frontend/src/pages/business/Meetings.tsx +++ b/frontend/src/pages/business/Meetings.tsx @@ -115,7 +115,18 @@ const shouldTrackGenerationProgress = (item: MeetingVO) => !hasLatestGenerationFailure(item) && (item.status === 0 || item.status === 1 || item.status === 2); const isTerminalMeetingProgress = (progress?: MeetingProgress | null) => - !!progress && (progress.percent === 100 || progress.percent < 0); + !!progress && ( + progress.percent === 100 + || progress.percent < 0 + || progress.unifiedStatus?.statusCode === "COMPLETED" + || progress.unifiedStatus?.statusCode?.startsWith("FAILED_") + ); + +const isUnifiedTerminalProgress = (progress?: MeetingProgress | null) => + !!progress && ( + progress.unifiedStatus?.statusCode === "COMPLETED" + || progress.unifiedStatus?.statusCode?.startsWith("FAILED_") + ); const shouldPollMeetingCard = (item: MeetingVO) => shouldTrackGenerationProgress(item) @@ -212,7 +223,7 @@ const IntegratedStatusTag: React.FC<{ meeting: MeetingVO; progress: MeetingProgr const displayConfig = progress?.unifiedStatus?.statusText ? { ...config, text: progress.unifiedStatus.statusText } : config; - const isProcessing = shouldTrackGenerationProgress(meeting); + const isProcessing = shouldTrackGenerationProgress(meeting) && !isUnifiedTerminalProgress(progress); const percent = isProcessing ? progress?.percent || 0 : 0; return (