feat(aihr): 重构运营后台与租户知识治理
- 重构运营总览、侧栏和内容运营工作台\n- 增加大喇叭、成果审核与运营统计链路\n- 补齐租户、知识空间、分类和知识维护闭环\n- 加固标签页租户上下文、停用租户写保护与迁移预检
This commit is contained in:
+14
@@ -8,6 +8,7 @@ import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.dromara.common.core.constant.SystemConstants;
|
||||
import org.dromara.common.core.constant.TenantConstants;
|
||||
import org.dromara.common.core.domain.R;
|
||||
import org.dromara.common.core.validate.AddGroup;
|
||||
@@ -147,10 +148,23 @@ public class SysTenantController extends BaseController {
|
||||
@SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY)
|
||||
@GetMapping("/dynamic/{tenantId}")
|
||||
public R<Void> dynamicTenant(@NotBlank(message = "租户ID不能为空") @PathVariable String tenantId) {
|
||||
SysTenantVo tenant = TenantHelper.ignore(() -> tenantService.queryByTenantId(tenantId));
|
||||
if (tenant == null || !SystemConstants.NORMAL.equals(tenant.getStatus())) {
|
||||
return R.fail("目标租户不存在或已停用");
|
||||
}
|
||||
TenantHelper.setDynamic(tenantId, true);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询当前超级管理员的动态租户。
|
||||
*/
|
||||
@SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY)
|
||||
@GetMapping("/dynamic")
|
||||
public R<String> currentDynamicTenant() {
|
||||
return R.ok(TenantHelper.getDynamic());
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除动态租户
|
||||
*/
|
||||
|
||||
+6
@@ -25,6 +25,12 @@ public interface ISysTenantService {
|
||||
*/
|
||||
SysTenantVo queryByTenantId(String tenantId);
|
||||
|
||||
/**
|
||||
* 在当前事务中锁定并查询租户,供需要与租户停用操作串行化的写入流程使用。
|
||||
* 此查询绕过缓存;调用方必须已开启事务。
|
||||
*/
|
||||
SysTenantVo lockByTenantId(String tenantId);
|
||||
|
||||
/**
|
||||
* 查询租户列表
|
||||
*/
|
||||
|
||||
+13
@@ -24,6 +24,7 @@ import org.dromara.common.oss.core.OssClient;
|
||||
import org.dromara.common.oss.entity.UploadResult;
|
||||
import org.dromara.common.oss.enums.AccessPolicyType;
|
||||
import org.dromara.common.oss.factory.OssFactory;
|
||||
import org.dromara.common.tenant.helper.TenantHelper;
|
||||
import org.dromara.system.domain.SysOss;
|
||||
import org.dromara.system.domain.SysOssExt;
|
||||
import org.dromara.system.domain.bo.SysOssBo;
|
||||
@@ -195,6 +196,13 @@ public class SysOssServiceImpl implements ISysOssService, OssService {
|
||||
if (ObjectUtil.isNull(file) || file.isEmpty()) {
|
||||
throw new ServiceException("上传文件不能为空");
|
||||
}
|
||||
// Resolve and pin the request tenant before an irreversible object-store write.
|
||||
// This rejects a stale page context before upload and keeps an in-flight upload
|
||||
// associated with the tenant it was accepted under.
|
||||
return TenantHelper.dynamic(TenantHelper.getTenantId(), () -> uploadMultipartFile(file));
|
||||
}
|
||||
|
||||
private SysOssVo uploadMultipartFile(MultipartFile file) {
|
||||
String originalfileName = file.getOriginalFilename();
|
||||
String suffix = StringUtils.substring(originalfileName, originalfileName.lastIndexOf("."), originalfileName.length());
|
||||
OssClient storage = OssFactory.instance();
|
||||
@@ -222,6 +230,11 @@ public class SysOssServiceImpl implements ISysOssService, OssService {
|
||||
if (ObjectUtil.isNull(file) || !file.isFile() || file.length() <= 0) {
|
||||
throw new ServiceException("上传文件不能为空");
|
||||
}
|
||||
// Keep non-HTTP/background imports on their resolved temporary tenant as well.
|
||||
return TenantHelper.dynamic(TenantHelper.getTenantId(), () -> uploadLocalFile(file));
|
||||
}
|
||||
|
||||
private SysOssVo uploadLocalFile(File file) {
|
||||
String originalfileName = file.getName();
|
||||
String suffix = StringUtils.substring(originalfileName, originalfileName.lastIndexOf("."), originalfileName.length());
|
||||
OssClient storage = OssFactory.instance();
|
||||
|
||||
+10
@@ -75,6 +75,16 @@ public class SysTenantServiceImpl implements ISysTenantService {
|
||||
return baseMapper.selectVoOne(new LambdaQueryWrapper<SysTenant>().eq(SysTenant::getTenantId, tenantId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询时持有租户行锁,避免租户停用与受保护写入在同一时刻交错。
|
||||
*/
|
||||
@Override
|
||||
public SysTenantVo lockByTenantId(String tenantId) {
|
||||
return baseMapper.selectVoOne(new LambdaQueryWrapper<SysTenant>()
|
||||
.eq(SysTenant::getTenantId, tenantId)
|
||||
.last("for update"));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询租户列表
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user