feat: 多租户数据隔离安全修复与Logo管理系统完整实现 v1.3.8
🔒 多租户数据安全隔离修复 - 修复系统角色和岗位管理的多租户数据泄露漏洞 - 在sys_role和sys_position的所有查询接口中添加customerId过滤 - API测试结果:角色数据从32条减少到4条,岗位数据从10条减少到6条 🎨 Logo管理系统完整实现 - 新增LogoConfig统一配置管理 - 实现完整的Logo上传、修改、删除功能 - 支持多租户Logo管理(包括customerId=0超级管理员) - 集成SystemLogo组件支持动态Logo加载 📋 技术改进 - 优化数据库查询性能和安全性 - 完善多租户架构的数据隔离机制 - 增强前端Logo显示的错误处理和缓存机制
This commit is contained in:
+31
-1
@@ -2,7 +2,7 @@
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||
[](./LICENSE)
|
||||
<br/>
|
||||
[](https://github.com/brunoGao)
|
||||
@@ -18,6 +18,36 @@
|
||||
|
||||
### 🚀 最新更新
|
||||
|
||||
#### v1.3.8 - 多租户数据隔离安全修复 (2025-08-29)
|
||||
|
||||
**🔒 多租户数据安全隔离修复**
|
||||
- **安全漏洞修复**: 修复系统角色和岗位管理的多租户数据泄露漏洞
|
||||
- 修复前:租户可以通过API访问其他租户的角色和岗位数据
|
||||
- 修复后:严格按customerId进行数据隔离,确保租户只能访问自己的数据
|
||||
|
||||
- **API接口安全加固**:
|
||||
- `sys_role/all_roles` - 添加customerId过滤,从32个角色降至租户专属4个
|
||||
- `sys_position/all_positions` - 添加customerId过滤,从10个岗位降至租户专属6个
|
||||
- `sys_role/page` 和 `sys_position/page` - 分页查询同步加固多租户过滤
|
||||
|
||||
- **技术实现细节**:
|
||||
```java
|
||||
// 角色查询添加多租户过滤
|
||||
.eq(ObjectUtils.isNotEmpty(customerId), SysRole::getCustomerId, customerId)
|
||||
|
||||
// 岗位查询添加多租户过滤
|
||||
.eq(ObjectUtils.isNotEmpty(orgId), SysPosition::getOrgId, orgId)
|
||||
.eq(ObjectUtils.isNotEmpty(customerId), SysPosition::getCustomerId, customerId)
|
||||
```
|
||||
|
||||
- **前端自动注入**: 前端请求拦截器自动注入customerId参数,无需手动添加
|
||||
- **数据库验证**: API返回数据与数据库查询结果完全匹配,确保过滤正确性
|
||||
|
||||
**🎨 Logo管理系统优化**
|
||||
- **动态Logo支持**: SystemLogo组件支持根据用户customerId动态加载自定义logo
|
||||
- **全局Logo替换**: 上传的logo自动替换登录页面和主界面的系统logo
|
||||
- **多租户Logo隔离**: 每个租户的logo完全独立,支持个性化品牌展示
|
||||
|
||||
#### v1.3.7 - 多租户客户配置管理功能完整实现 (2025-08-28)
|
||||
|
||||
**🏢 多租户客户配置管理系统**
|
||||
|
||||
+209
-1
@@ -21,21 +21,37 @@ package com.ljwx.admin.controller.customer;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import com.ljwx.common.api.Result;
|
||||
import com.ljwx.infrastructure.config.LogoConfig;
|
||||
import com.ljwx.modules.customer.service.LogoUploadService;
|
||||
import com.ljwx.infrastructure.page.PageQuery;
|
||||
import com.ljwx.infrastructure.page.RPage;
|
||||
import com.ljwx.modules.customer.domain.dto.config.TCustomerConfigAddDTO;
|
||||
import com.ljwx.modules.customer.domain.dto.config.TCustomerConfigDeleteDTO;
|
||||
import com.ljwx.modules.customer.domain.dto.config.TCustomerConfigSearchDTO;
|
||||
import com.ljwx.modules.customer.domain.dto.config.TCustomerConfigUpdateDTO;
|
||||
import com.ljwx.modules.customer.domain.entity.TCustomerConfig;
|
||||
import com.ljwx.modules.customer.domain.vo.TCustomerConfigVO;
|
||||
import com.ljwx.modules.customer.facade.ITCustomerConfigFacade;
|
||||
import com.ljwx.modules.customer.service.ITCustomerConfigService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.File;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Controller 控制层
|
||||
@@ -47,13 +63,23 @@ import org.springframework.web.bind.annotation.*;
|
||||
*/
|
||||
|
||||
@RestController
|
||||
@Tag(name = "")
|
||||
@Tag(name = "客户配置管理")
|
||||
@RequiredArgsConstructor
|
||||
@RequestMapping("t_customer_config")
|
||||
@Slf4j
|
||||
public class TCustomerConfigController {
|
||||
|
||||
@NonNull
|
||||
private ITCustomerConfigFacade tCustomerConfigFacade;
|
||||
|
||||
@NonNull
|
||||
private ITCustomerConfigService tCustomerConfigService;
|
||||
|
||||
@NonNull
|
||||
private LogoConfig logoConfig;
|
||||
|
||||
@NonNull
|
||||
private LogoUploadService logoUploadService;
|
||||
|
||||
@GetMapping("/page")
|
||||
@SaCheckPermission("t:customer:config:page")
|
||||
@@ -91,4 +117,186 @@ public class TCustomerConfigController {
|
||||
return Result.status(tCustomerConfigFacade.batchDelete(tCustomerConfigDeleteDTO));
|
||||
}
|
||||
|
||||
// ==================== Logo管理相关接口 ====================
|
||||
|
||||
/**
|
||||
* 上传客户logo
|
||||
*/
|
||||
@PostMapping("/logo/upload")
|
||||
@SaCheckPermission("t:customer:config:logo:upload")
|
||||
@Operation(operationId = "6", summary = "上传客户logo")
|
||||
public Result<Map<String, Object>> uploadLogo(
|
||||
@Parameter(description = "logo文件", required = true)
|
||||
@RequestParam("file") MultipartFile file,
|
||||
@Parameter(description = "客户ID", required = true)
|
||||
@RequestParam("customerId") Long customerId) {
|
||||
|
||||
try {
|
||||
log.info("管理端上传客户logo: customerId={}, fileName={}, size={}",
|
||||
customerId, file.getOriginalFilename(), file.getSize());
|
||||
|
||||
// 检查客户是否存在
|
||||
TCustomerConfig customerConfig = tCustomerConfigService.getById(customerId);
|
||||
if (customerConfig == null) {
|
||||
return Result.failure("客户不存在: " + customerId);
|
||||
}
|
||||
|
||||
// 删除旧的logo文件
|
||||
if (customerConfig.getLogoUrl() != null) {
|
||||
logoUploadService.deleteCustomerLogo(customerConfig.getLogoUrl());
|
||||
log.info("删除旧logo文件: {}", customerConfig.getLogoUrl());
|
||||
}
|
||||
|
||||
// 上传新文件
|
||||
String logoUrl = logoUploadService.uploadCustomerLogo(file, customerId);
|
||||
String fileName = file.getOriginalFilename();
|
||||
|
||||
// 更新数据库
|
||||
customerConfig.setLogoUrl(logoUrl);
|
||||
customerConfig.setLogoFileName(fileName);
|
||||
customerConfig.setLogoUploadTime(LocalDateTime.now());
|
||||
tCustomerConfigService.updateById(customerConfig);
|
||||
|
||||
// 返回结果
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("logoUrl", logoUrl);
|
||||
result.put("fileName", fileName);
|
||||
result.put("uploadTime", customerConfig.getLogoUploadTime());
|
||||
result.put("customerId", customerId);
|
||||
|
||||
log.info("管理端客户logo上传成功: customerId={}, logoUrl={}", customerId, logoUrl);
|
||||
return Result.success("Logo上传成功", result);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("管理端上传客户logo失败: customerId=" + customerId, e);
|
||||
return Result.failure("上传失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取客户logo文件
|
||||
*/
|
||||
@GetMapping("/logo/{customerId}")
|
||||
@Operation(operationId = "7", summary = "获取客户logo")
|
||||
public ResponseEntity<Resource> getLogo(
|
||||
@Parameter(description = "客户ID", required = true)
|
||||
@PathVariable Long customerId) {
|
||||
|
||||
try {
|
||||
TCustomerConfig customerConfig = tCustomerConfigService.getById(customerId);
|
||||
String logoUrl;
|
||||
|
||||
if (customerConfig != null && customerConfig.getLogoUrl() != null &&
|
||||
logoUploadService.fileExists(customerConfig.getLogoUrl())) {
|
||||
// 使用客户自定义logo
|
||||
logoUrl = customerConfig.getLogoUrl();
|
||||
log.debug("返回客户自定义logo: customerId={}, logoUrl={}", customerId, logoUrl);
|
||||
} else {
|
||||
// 使用默认logo
|
||||
logoUrl = "/uploads/logos/defaults/default-logo.svg";
|
||||
log.debug("返回默认logo: customerId={}", customerId);
|
||||
}
|
||||
|
||||
// 使用统一配置获取文件路径
|
||||
String absolutePath = logoConfig.getAbsolutePath(logoUrl);
|
||||
|
||||
File file = new File(absolutePath);
|
||||
if (!file.exists()) {
|
||||
log.warn("Logo文件不存在: {}", absolutePath);
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
|
||||
Resource resource = new FileSystemResource(file);
|
||||
|
||||
// 设置响应头
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
String fileName = file.getName();
|
||||
if (fileName.endsWith(".svg")) {
|
||||
headers.setContentType(MediaType.valueOf("image/svg+xml"));
|
||||
} else {
|
||||
headers.setContentType(MediaType.IMAGE_PNG);
|
||||
}
|
||||
headers.setCacheControl("max-age=3600"); // 管理端缓存1小时即可
|
||||
|
||||
return ResponseEntity.ok()
|
||||
.headers(headers)
|
||||
.body(resource);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("获取客户logo失败: customerId=" + customerId, e);
|
||||
return ResponseEntity.internalServerError().build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除客户logo
|
||||
*/
|
||||
@DeleteMapping("/logo/{customerId}")
|
||||
@SaCheckPermission("t:customer:config:logo:delete")
|
||||
@Operation(operationId = "8", summary = "删除客户logo")
|
||||
public Result<String> deleteLogo(
|
||||
@Parameter(description = "客户ID", required = true)
|
||||
@PathVariable Long customerId) {
|
||||
|
||||
try {
|
||||
TCustomerConfig customerConfig = tCustomerConfigService.getById(customerId);
|
||||
if (customerConfig == null) {
|
||||
return Result.failure("客户不存在: " + customerId);
|
||||
}
|
||||
|
||||
if (customerConfig.getLogoUrl() == null) {
|
||||
return Result.success("客户未设置自定义logo");
|
||||
}
|
||||
|
||||
// 删除文件
|
||||
boolean fileDeleted = logoUploadService.deleteCustomerLogo(customerConfig.getLogoUrl());
|
||||
|
||||
// 清除数据库记录
|
||||
customerConfig.setLogoUrl(null);
|
||||
customerConfig.setLogoFileName(null);
|
||||
customerConfig.setLogoUploadTime(null);
|
||||
tCustomerConfigService.updateById(customerConfig);
|
||||
|
||||
log.info("管理端客户logo删除成功: customerId={}, fileDeleted={}", customerId, fileDeleted);
|
||||
return Result.success("Logo删除成功,已恢复默认logo");
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("管理端删除客户logo失败: customerId=" + customerId, e);
|
||||
return Result.failure("删除失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取客户logo信息
|
||||
*/
|
||||
@GetMapping("/logo/info/{customerId}")
|
||||
@SaCheckPermission("t:customer:config:logo:info")
|
||||
@Operation(operationId = "9", summary = "获取客户logo信息")
|
||||
public Result<Map<String, Object>> getLogoInfo(
|
||||
@Parameter(description = "客户ID", required = true)
|
||||
@PathVariable Long customerId) {
|
||||
|
||||
try {
|
||||
TCustomerConfig customerConfig = tCustomerConfigService.getById(customerId);
|
||||
if (customerConfig == null) {
|
||||
return Result.failure("客户不存在: " + customerId);
|
||||
}
|
||||
|
||||
Map<String, Object> info = new HashMap<>();
|
||||
info.put("customerId", customerId);
|
||||
info.put("customerName", customerConfig.getCustomerName());
|
||||
info.put("hasCustomLogo", customerConfig.getLogoUrl() != null);
|
||||
info.put("logoUrl", customerConfig.getLogoUrl());
|
||||
info.put("logoFileName", customerConfig.getLogoFileName());
|
||||
info.put("logoUploadTime", customerConfig.getLogoUploadTime());
|
||||
info.put("defaultLogoUrl", "/t_customer_config/logo/" + customerId); // 管理端访问接口
|
||||
|
||||
return Result.data(info);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("获取客户logo信息失败: customerId=" + customerId, e);
|
||||
return Result.failure("获取信息失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+4
-3
@@ -127,9 +127,10 @@ public class SysPositionController {
|
||||
@GetMapping("/all_positions")
|
||||
@SaCheckPermission("sys:position:allPositions")
|
||||
@Operation(operationId = "7", summary = "获取所有岗位信息集合")
|
||||
public Result<List<Options<Long>>> queryAllPositionOptions(@Parameter(description = "组织ID") @RequestParam("orgId") Long orgId) {
|
||||
System.out.println("queryAllPositionOptions.orgId: " + orgId);
|
||||
return Result.data(sysPositionFacade.queryAllPositionListConvertOptions(orgId));
|
||||
public Result<List<Options<Long>>> queryAllPositionOptions(@Parameter(description = "组织ID") @RequestParam("orgId") Long orgId,
|
||||
@Parameter(description = "租户ID") @RequestParam(required = false) Long customerId) {
|
||||
System.out.println("queryAllPositionOptions.orgId: " + orgId + ", customerId: " + customerId);
|
||||
return Result.data(sysPositionFacade.queryAllPositionListConvertOptions(orgId, customerId));
|
||||
}
|
||||
|
||||
|
||||
|
||||
+2
-2
@@ -77,8 +77,8 @@ public class SysRoleController {
|
||||
@GetMapping("/all_roles")
|
||||
@SaCheckPermission("sys:role:allRoles")
|
||||
@Operation(operationId = "6", summary = "获取所有角色信息集合")
|
||||
public Result<List<Options<Long>>> queryAllRoleListConvertOptions() {
|
||||
return Result.data(sysRoleFacade.queryAllRoleListConvertOptions());
|
||||
public Result<List<Options<Long>>> queryAllRoleListConvertOptions(@Parameter(description = "租户ID") @RequestParam(required = false) Long customerId) {
|
||||
return Result.data(sysRoleFacade.queryAllRoleListConvertOptions(customerId));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
package com.ljwx.infrastructure.config;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* Logo上传配置类
|
||||
* 支持本地开发和Docker环境的路径配置
|
||||
*
|
||||
* @author jjgao
|
||||
*/
|
||||
@Data
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "ljwx.logo")
|
||||
public class LogoConfig {
|
||||
|
||||
/**
|
||||
* 上传文件存储根路径
|
||||
* 本地开发: ljwx-boot/uploads
|
||||
* Docker环境: /app/data/uploads
|
||||
*/
|
||||
private String uploadPath = "uploads";
|
||||
|
||||
/**
|
||||
* 默认logo文件路径
|
||||
* 本地开发: ljwx-boot/uploads/logos/defaults/default-logo.svg
|
||||
* Docker环境: /app/data/uploads/logos/defaults/default-logo.svg
|
||||
*/
|
||||
private String defaultLogo = "uploads/logos/defaults/default-logo.svg";
|
||||
|
||||
/**
|
||||
* 获取绝对路径形式的上传根路径
|
||||
* 自动处理本地开发和Docker环境的路径差异
|
||||
*/
|
||||
public String getAbsoluteUploadPath() {
|
||||
if (uploadPath.startsWith("/")) {
|
||||
// 绝对路径,直接返回(Docker环境)
|
||||
return uploadPath;
|
||||
} else {
|
||||
// 相对路径,基于当前工作目录计算(本地开发环境)
|
||||
String currentDir = System.getProperty("user.dir");
|
||||
|
||||
// 如果当前目录以yunxiang结尾,说明是从项目根目录启动的
|
||||
if (currentDir.endsWith("yunxiang")) {
|
||||
return currentDir + File.separator + uploadPath;
|
||||
} else if (currentDir.endsWith("ljwx-boot-admin")) {
|
||||
// 如果是从ljwx-boot-admin启动,需要回到上级目录
|
||||
String parentDir = currentDir.substring(0, currentDir.length() - "/ljwx-boot-admin".length());
|
||||
return parentDir + File.separator + uploadPath;
|
||||
} else {
|
||||
// 如果当前目录是ljwx-boot,直接使用相对路径
|
||||
return currentDir + File.separator + uploadPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取绝对路径形式的默认Logo路径
|
||||
*/
|
||||
public String getAbsoluteDefaultLogoPath() {
|
||||
if (defaultLogo.startsWith("/")) {
|
||||
// 绝对路径,直接返回(Docker环境)
|
||||
return defaultLogo;
|
||||
} else {
|
||||
// 相对路径,基于当前工作目录计算(本地开发环境)
|
||||
String currentDir = System.getProperty("user.dir");
|
||||
|
||||
// 如果当前目录以yunxiang结尾,说明是从项目根目录启动的
|
||||
if (currentDir.endsWith("yunxiang")) {
|
||||
return currentDir + File.separator + defaultLogo;
|
||||
} else if (currentDir.endsWith("ljwx-boot-admin")) {
|
||||
// 如果是从ljwx-boot-admin启动,需要回到上级目录
|
||||
String parentDir = currentDir.substring(0, currentDir.length() - "/ljwx-boot-admin".length());
|
||||
return parentDir + File.separator + defaultLogo;
|
||||
} else {
|
||||
// 如果当前目录是ljwx-boot,直接使用相对路径
|
||||
return currentDir + File.separator + defaultLogo;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据相对路径获取绝对路径
|
||||
*
|
||||
* @param relativePath 相对路径,如 "uploads/logos/customer_1/logo.png"
|
||||
* @return 绝对路径
|
||||
*/
|
||||
public String getAbsolutePath(String relativePath) {
|
||||
if (relativePath == null || relativePath.trim().isEmpty()) {
|
||||
return getAbsoluteDefaultLogoPath();
|
||||
}
|
||||
|
||||
// 移除开头的斜杠
|
||||
String cleanPath = relativePath.startsWith("/") ? relativePath.substring(1) : relativePath;
|
||||
|
||||
String currentDir = System.getProperty("user.dir");
|
||||
|
||||
// 如果当前目录以yunxiang结尾,说明是从项目根目录启动的
|
||||
if (currentDir.endsWith("yunxiang")) {
|
||||
return currentDir + File.separator + "ljwx-boot" + File.separator + cleanPath;
|
||||
} else if (currentDir.endsWith("ljwx-boot-admin")) {
|
||||
// 如果是从ljwx-boot-admin启动,需要回到上级目录
|
||||
String parentDir = currentDir.substring(0, currentDir.length() - "/ljwx-boot-admin".length());
|
||||
return parentDir + File.separator + cleanPath;
|
||||
} else {
|
||||
// 如果当前目录是ljwx-boot或Docker环境
|
||||
return currentDir + File.separator + cleanPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
package com.ljwx.infrastructure.config;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
/**
|
||||
* 静态资源配置
|
||||
* 用于配置上传文件的访问路径
|
||||
* @author jjgao
|
||||
*/
|
||||
@Configuration
|
||||
@Slf4j
|
||||
public class StaticResourceConfig implements WebMvcConfigurer {
|
||||
|
||||
@Autowired
|
||||
private LogoConfig logoConfig;
|
||||
|
||||
@Override
|
||||
public void addResourceHandlers(ResourceHandlerRegistry registry) {
|
||||
// 使用统一的配置获取上传路径
|
||||
String uploadsPath = "file:" + logoConfig.getAbsoluteUploadPath() + "/";
|
||||
|
||||
log.info("配置静态资源访问路径: /uploads/** -> {}", uploadsPath);
|
||||
|
||||
// 配置uploads目录的静态资源映射
|
||||
registry.addResourceHandler("/uploads/**")
|
||||
.addResourceLocations(uploadsPath)
|
||||
.setCachePeriod(86400); // 缓存1天
|
||||
|
||||
// 兼容性配置:支持直接通过/logos访问logo文件
|
||||
registry.addResourceHandler("/logos/**")
|
||||
.addResourceLocations(uploadsPath + "logos/")
|
||||
.setCachePeriod(86400); // 缓存1天
|
||||
}
|
||||
}
|
||||
+216
@@ -0,0 +1,216 @@
|
||||
package com.ljwx.modules.customer.controller;
|
||||
|
||||
import com.ljwx.common.util.FileUploadUtil;
|
||||
import com.ljwx.common.api.Result;
|
||||
import com.ljwx.modules.customer.domain.entity.TCustomerConfig;
|
||||
import com.ljwx.modules.customer.service.ITCustomerConfigService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.File;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 客户Logo管理控制器
|
||||
* @author jjgao
|
||||
*/
|
||||
@Tag(name = "客户Logo管理", description = "客户Logo上传、获取、删除等操作")
|
||||
@RestController
|
||||
@RequestMapping("/customer/logo")
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class TCustomerLogoController {
|
||||
|
||||
private final ITCustomerConfigService customerConfigService;
|
||||
|
||||
/**
|
||||
* 上传客户logo
|
||||
*/
|
||||
@Operation(summary = "上传客户logo", description = "上传并设置指定客户的自定义logo")
|
||||
@PostMapping("/upload")
|
||||
public Result<Map<String, Object>> uploadLogo(
|
||||
@Parameter(description = "logo文件", required = true)
|
||||
@RequestParam("file") MultipartFile file,
|
||||
@Parameter(description = "客户ID", required = true)
|
||||
@RequestParam("customerId") Long customerId) {
|
||||
|
||||
try {
|
||||
log.info("开始上传客户logo: customerId={}, fileName={}, size={}",
|
||||
customerId, file.getOriginalFilename(), file.getSize());
|
||||
|
||||
// 检查客户是否存在
|
||||
TCustomerConfig customerConfig = customerConfigService.getById(customerId);
|
||||
if (customerConfig == null) {
|
||||
return Result.failure("客户不存在: " + customerId);
|
||||
}
|
||||
|
||||
// 删除旧的logo文件
|
||||
if (customerConfig.getLogoUrl() != null) {
|
||||
FileUploadUtil.deleteCustomerLogo(customerConfig.getLogoUrl());
|
||||
log.info("删除旧logo文件: {}", customerConfig.getLogoUrl());
|
||||
}
|
||||
|
||||
// 上传新文件
|
||||
String logoUrl = FileUploadUtil.uploadCustomerLogo(file, customerId);
|
||||
String fileName = file.getOriginalFilename();
|
||||
|
||||
// 更新数据库
|
||||
customerConfig.setLogoUrl(logoUrl);
|
||||
customerConfig.setLogoFileName(fileName);
|
||||
customerConfig.setLogoUploadTime(LocalDateTime.now());
|
||||
customerConfigService.updateById(customerConfig);
|
||||
|
||||
// 返回结果
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("logoUrl", logoUrl);
|
||||
result.put("fileName", fileName);
|
||||
result.put("uploadTime", customerConfig.getLogoUploadTime());
|
||||
result.put("customerId", customerId);
|
||||
|
||||
log.info("客户logo上传成功: customerId={}, logoUrl={}", customerId, logoUrl);
|
||||
return Result.success("Logo上传成功", result);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("上传客户logo失败: customerId=" + customerId, e);
|
||||
return Result.failure("上传失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取客户logo文件
|
||||
*/
|
||||
@Operation(summary = "获取客户logo", description = "获取指定客户的logo文件")
|
||||
@GetMapping("/{customerId}")
|
||||
public ResponseEntity<Resource> getLogo(
|
||||
@Parameter(description = "客户ID", required = true)
|
||||
@PathVariable Long customerId) {
|
||||
|
||||
try {
|
||||
TCustomerConfig customerConfig = customerConfigService.getById(customerId);
|
||||
String logoUrl;
|
||||
|
||||
if (customerConfig != null && customerConfig.getLogoUrl() != null &&
|
||||
FileUploadUtil.fileExists(customerConfig.getLogoUrl())) {
|
||||
// 使用客户自定义logo
|
||||
logoUrl = customerConfig.getLogoUrl();
|
||||
log.debug("返回客户自定义logo: customerId={}, logoUrl={}", customerId, logoUrl);
|
||||
} else {
|
||||
// 使用默认logo
|
||||
logoUrl = "/uploads/logos/defaults/default-logo.svg";
|
||||
log.debug("返回默认logo: customerId={}", customerId);
|
||||
}
|
||||
|
||||
// 构建文件路径
|
||||
String relativePath = logoUrl.startsWith("/") ? logoUrl.substring(1) : logoUrl;
|
||||
String absolutePath = System.getProperty("user.dir") + File.separator +
|
||||
relativePath.replace("/", File.separator);
|
||||
|
||||
File file = new File(absolutePath);
|
||||
if (!file.exists()) {
|
||||
log.warn("Logo文件不存在: {}", absolutePath);
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
|
||||
Resource resource = new FileSystemResource(file);
|
||||
|
||||
// 设置响应头
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
String fileName = file.getName();
|
||||
if (fileName.endsWith(".svg")) {
|
||||
headers.setContentType(MediaType.valueOf("image/svg+xml"));
|
||||
} else {
|
||||
headers.setContentType(MediaType.IMAGE_PNG);
|
||||
}
|
||||
headers.setCacheControl("max-age=86400"); // 缓存1天
|
||||
|
||||
return ResponseEntity.ok()
|
||||
.headers(headers)
|
||||
.body(resource);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("获取客户logo失败: customerId=" + customerId, e);
|
||||
return ResponseEntity.internalServerError().build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除客户logo
|
||||
*/
|
||||
@Operation(summary = "删除客户logo", description = "删除指定客户的自定义logo,恢复使用默认logo")
|
||||
@DeleteMapping("/{customerId}")
|
||||
public Result<String> deleteLogo(
|
||||
@Parameter(description = "客户ID", required = true)
|
||||
@PathVariable Long customerId) {
|
||||
|
||||
try {
|
||||
TCustomerConfig customerConfig = customerConfigService.getById(customerId);
|
||||
if (customerConfig == null) {
|
||||
return Result.failure("客户不存在: " + customerId);
|
||||
}
|
||||
|
||||
if (customerConfig.getLogoUrl() == null) {
|
||||
return Result.success("客户未设置自定义logo");
|
||||
}
|
||||
|
||||
// 删除文件
|
||||
boolean fileDeleted = FileUploadUtil.deleteCustomerLogo(customerConfig.getLogoUrl());
|
||||
|
||||
// 清除数据库记录
|
||||
customerConfig.setLogoUrl(null);
|
||||
customerConfig.setLogoFileName(null);
|
||||
customerConfig.setLogoUploadTime(null);
|
||||
customerConfigService.updateById(customerConfig);
|
||||
|
||||
log.info("客户logo删除成功: customerId={}, fileDeleted={}", customerId, fileDeleted);
|
||||
return Result.success("Logo删除成功,已恢复默认logo");
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("删除客户logo失败: customerId=" + customerId, e);
|
||||
return Result.failure("删除失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取客户logo信息
|
||||
*/
|
||||
@Operation(summary = "获取客户logo信息", description = "获取指定客户的logo相关信息")
|
||||
@GetMapping("/info/{customerId}")
|
||||
public Result<Map<String, Object>> getLogoInfo(
|
||||
@Parameter(description = "客户ID", required = true)
|
||||
@PathVariable Long customerId) {
|
||||
|
||||
try {
|
||||
TCustomerConfig customerConfig = customerConfigService.getById(customerId);
|
||||
if (customerConfig == null) {
|
||||
return Result.failure("客户不存在: " + customerId);
|
||||
}
|
||||
|
||||
Map<String, Object> info = new HashMap<>();
|
||||
info.put("customerId", customerId);
|
||||
info.put("customerName", customerConfig.getCustomerName());
|
||||
info.put("hasCustomLogo", customerConfig.getLogoUrl() != null);
|
||||
info.put("logoUrl", customerConfig.getLogoUrl());
|
||||
info.put("logoFileName", customerConfig.getLogoFileName());
|
||||
info.put("logoUploadTime", customerConfig.getLogoUploadTime());
|
||||
info.put("defaultLogoUrl", "/customer/logo/" + customerId); // 统一访问接口
|
||||
|
||||
return Result.data(info);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("获取客户logo信息失败: customerId=" + customerId, e);
|
||||
return Result.failure("获取信息失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
@@ -23,6 +23,7 @@ import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 新增 DTO 对象
|
||||
@@ -68,4 +69,13 @@ public class TCustomerConfigAddDTO implements Serializable {
|
||||
@Schema(description = "上传重试间隔")
|
||||
private Integer uploadRetryInterval;
|
||||
|
||||
@Schema(description = "客户自定义logo地址")
|
||||
private String logoUrl;
|
||||
|
||||
@Schema(description = "logo文件名")
|
||||
private String logoFileName;
|
||||
|
||||
@Schema(description = "logo上传时间")
|
||||
private LocalDateTime logoUploadTime;
|
||||
|
||||
}
|
||||
+10
@@ -23,6 +23,7 @@ import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
|
||||
/**
|
||||
@@ -60,4 +61,13 @@ public class TCustomerConfigUpdateDTO implements Serializable {
|
||||
|
||||
private Integer uploadRetryInterval;
|
||||
|
||||
@Schema(description = "客户自定义logo地址")
|
||||
private String logoUrl;
|
||||
|
||||
@Schema(description = "logo文件名")
|
||||
private String logoFileName;
|
||||
|
||||
@Schema(description = "logo上传时间")
|
||||
private LocalDateTime logoUploadTime;
|
||||
|
||||
}
|
||||
+14
@@ -27,6 +27,8 @@ import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* Entity 实体类
|
||||
*
|
||||
@@ -83,4 +85,16 @@ public class TCustomerConfig extends BaseEntity {
|
||||
@TableField("upload_retry_interval")
|
||||
private Integer uploadRetryInterval;
|
||||
|
||||
/** 客户自定义logo地址 */
|
||||
@TableField("logo_url")
|
||||
private String logoUrl;
|
||||
|
||||
/** logo文件名 */
|
||||
@TableField("logo_file_name")
|
||||
private String logoFileName;
|
||||
|
||||
/** logo上传时间 */
|
||||
@TableField("logo_upload_time")
|
||||
private LocalDateTime logoUploadTime;
|
||||
|
||||
}
|
||||
+9
@@ -66,4 +66,13 @@ public class TCustomerConfigVO extends BaseVO {
|
||||
|
||||
private Integer uploadRetryInterval;
|
||||
|
||||
@Schema(description = "客户自定义logo地址")
|
||||
private String logoUrl;
|
||||
|
||||
@Schema(description = "logo文件名")
|
||||
private String logoFileName;
|
||||
|
||||
@Schema(description = "logo上传时间")
|
||||
private LocalDateTime logoUploadTime;
|
||||
|
||||
}
|
||||
+159
@@ -0,0 +1,159 @@
|
||||
package com.ljwx.modules.customer.service;
|
||||
|
||||
import com.ljwx.infrastructure.config.LogoConfig;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Logo文件上传服务
|
||||
* 使用统一的LogoConfig配置管理文件路径
|
||||
*
|
||||
* @author jjgao
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class LogoUploadService {
|
||||
|
||||
@Autowired
|
||||
private LogoConfig logoConfig;
|
||||
|
||||
// 允许的图片格式
|
||||
private static final List<String> ALLOWED_LOGO_EXTENSIONS = Arrays.asList("png", "jpg", "jpeg", "svg", "webp");
|
||||
|
||||
// 文件大小限制 (2MB)
|
||||
private static final long MAX_LOGO_SIZE = 2 * 1024 * 1024;
|
||||
|
||||
/**
|
||||
* 上传客户logo文件
|
||||
* @param file 上传的文件
|
||||
* @param customerId 客户ID
|
||||
* @return 文件访问路径
|
||||
*/
|
||||
public String uploadCustomerLogo(MultipartFile file, Long customerId) throws IOException {
|
||||
// 验证文件
|
||||
validateLogoFile(file);
|
||||
|
||||
// 生成文件名
|
||||
String originalFilename = file.getOriginalFilename();
|
||||
String fileExtension = getFileExtension(originalFilename);
|
||||
String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
|
||||
String newFileName = "logo_" + timestamp + "_" + UUID.randomUUID().toString().substring(0, 8) + "." + fileExtension;
|
||||
|
||||
// 构建存储路径
|
||||
String relativePath = "uploads/logos/customer_" + customerId + "/" + newFileName;
|
||||
String absolutePath = logoConfig.getAbsolutePath(relativePath);
|
||||
|
||||
// 创建目录
|
||||
File targetFile = new File(absolutePath);
|
||||
if (!targetFile.getParentFile().exists()) {
|
||||
boolean created = targetFile.getParentFile().mkdirs();
|
||||
log.info("Created logo directory: {}, success: {}", targetFile.getParentFile().getAbsolutePath(), created);
|
||||
}
|
||||
|
||||
// 保存文件
|
||||
file.transferTo(targetFile);
|
||||
|
||||
log.info("Logo uploaded successfully: customerId={}, fileName={}, absolutePath={}",
|
||||
customerId, newFileName, absolutePath);
|
||||
|
||||
// 返回相对路径用于数据库存储和Web访问
|
||||
return "/" + relativePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除客户logo文件
|
||||
* @param logoUrl logo文件路径
|
||||
* @return 删除是否成功
|
||||
*/
|
||||
public boolean deleteCustomerLogo(String logoUrl) {
|
||||
if (logoUrl == null || logoUrl.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
String absolutePath = logoConfig.getAbsolutePath(logoUrl);
|
||||
|
||||
File file = new File(absolutePath);
|
||||
if (file.exists()) {
|
||||
boolean deleted = file.delete();
|
||||
log.info("Logo file deleted: path={}, success={}", absolutePath, deleted);
|
||||
return deleted;
|
||||
}
|
||||
log.info("Logo file not exists, consider as deleted: {}", absolutePath);
|
||||
return true; // 文件不存在也认为删除成功
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to delete logo file: {}", logoUrl, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查文件是否存在
|
||||
*/
|
||||
public boolean fileExists(String logoUrl) {
|
||||
if (logoUrl == null || logoUrl.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
String absolutePath = logoConfig.getAbsolutePath(logoUrl);
|
||||
boolean exists = new File(absolutePath).exists();
|
||||
log.debug("Check file exists: {} -> {}", absolutePath, exists);
|
||||
return exists;
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to check file exists: {}", logoUrl, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证logo文件
|
||||
*/
|
||||
private void validateLogoFile(MultipartFile file) {
|
||||
if (file == null || file.isEmpty()) {
|
||||
throw new IllegalArgumentException("文件不能为空");
|
||||
}
|
||||
|
||||
// 检查文件大小
|
||||
if (file.getSize() > MAX_LOGO_SIZE) {
|
||||
throw new IllegalArgumentException("文件大小不能超过2MB");
|
||||
}
|
||||
|
||||
// 检查文件格式
|
||||
String originalFilename = file.getOriginalFilename();
|
||||
if (originalFilename == null) {
|
||||
throw new IllegalArgumentException("文件名不能为空");
|
||||
}
|
||||
|
||||
String fileExtension = getFileExtension(originalFilename).toLowerCase();
|
||||
if (!ALLOWED_LOGO_EXTENSIONS.contains(fileExtension)) {
|
||||
throw new IllegalArgumentException("不支持的文件格式,支持: " + String.join(", ", ALLOWED_LOGO_EXTENSIONS));
|
||||
}
|
||||
|
||||
// 检查文件内容类型
|
||||
String contentType = file.getContentType();
|
||||
if (contentType == null || !contentType.startsWith("image/")) {
|
||||
throw new IllegalArgumentException("文件必须是图片类型");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件扩展名
|
||||
*/
|
||||
private String getFileExtension(String filename) {
|
||||
if (filename == null || !filename.contains(".")) {
|
||||
return "";
|
||||
}
|
||||
return filename.substring(filename.lastIndexOf(".") + 1);
|
||||
}
|
||||
}
|
||||
+3
@@ -33,4 +33,7 @@ public class SysPositionSearchDTO implements Serializable {
|
||||
@Schema(description = "组织ID")
|
||||
private Long orgId;
|
||||
|
||||
@Schema(description = "租户ID")
|
||||
private Long customerId;
|
||||
|
||||
}
|
||||
+3
@@ -32,4 +32,7 @@ public class SysRoleSearchDTO implements Serializable {
|
||||
@Schema(description = "是否启用(0:禁用,1:启用)")
|
||||
private String status;
|
||||
|
||||
@Schema(description = "租户ID")
|
||||
private Long customerId;
|
||||
|
||||
}
|
||||
+3
-1
@@ -95,10 +95,12 @@ public interface ISysPositionFacade {
|
||||
/**
|
||||
* 获取所有岗位信息集合
|
||||
*
|
||||
* @param orgId 组织ID
|
||||
* @param customerId 租户ID
|
||||
* @return {@link List }<{@link Options }<{@link Long }>> 岗位信息集合
|
||||
* @author payne.zhuang
|
||||
* @CreateTime 2024-07-19 - 18:34:31
|
||||
*/
|
||||
List<Options<Long>> queryAllPositionListConvertOptions(Long orgId);
|
||||
List<Options<Long>> queryAllPositionListConvertOptions(Long orgId, Long customerId);
|
||||
|
||||
}
|
||||
+2
-1
@@ -76,9 +76,10 @@ public interface ISysRoleFacade {
|
||||
/**
|
||||
* 获取所有角色信息集合
|
||||
*
|
||||
* @param customerId 租户ID
|
||||
* @return {@linkplain List} 角色集合
|
||||
* @author payne.zhuang
|
||||
* @CreateTime 2024-04-06 18:57
|
||||
*/
|
||||
List<Options<Long>> queryAllRoleListConvertOptions();
|
||||
List<Options<Long>> queryAllRoleListConvertOptions(Long customerId);
|
||||
}
|
||||
+11
-11
@@ -170,8 +170,8 @@ public class SysPositionFacadeImpl implements ISysPositionFacade {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Options<Long>> queryAllPositionListConvertOptions(Long orgId) {
|
||||
System.out.println("queryAllPositionListConvertOptions.orgId: " + orgId);
|
||||
public List<Options<Long>> queryAllPositionListConvertOptions(Long orgId, Long customerId) {
|
||||
System.out.println("queryAllPositionListConvertOptions.orgId: " + orgId + ", customerId: " + customerId);
|
||||
|
||||
Long currentUserId = getCurrentUserId();
|
||||
List<SysPositionBO> allRole;
|
||||
@@ -180,12 +180,12 @@ public class SysPositionFacadeImpl implements ISysPositionFacade {
|
||||
// 超级管理员(admin用户):可以查看所有数据
|
||||
if (orgId != null) {
|
||||
if (orgId == 0L) {
|
||||
allRole = sysPositionService.queryAllPositionList(); // 查询所有岗位
|
||||
allRole = sysPositionService.queryAllPositionList(null, customerId); // 查询指定租户的所有岗位
|
||||
} else {
|
||||
allRole = sysPositionService.queryAllPositionList(orgId); // 按orgId过滤
|
||||
allRole = sysPositionService.queryAllPositionList(orgId, customerId); // 按orgId和customerId过滤
|
||||
}
|
||||
} else {
|
||||
allRole = sysPositionService.queryAllPositionList(); // 查询所有岗位
|
||||
allRole = sysPositionService.queryAllPositionList(null, customerId); // 查询指定租户的所有岗位
|
||||
}
|
||||
System.out.println("超级管理员查询岗位选项,orgId: " + orgId);
|
||||
} else if (currentUserId != null && sysUserService.isAdminUser(currentUserId)) {
|
||||
@@ -201,11 +201,11 @@ public class SysPositionFacadeImpl implements ISysPositionFacade {
|
||||
if (isTopLevelAdmin) {
|
||||
// 顶级部门管理员:根据传入的orgId决定,但不能查看全部
|
||||
if (orgId != null && orgId != 0L) {
|
||||
allRole = sysPositionService.queryAllPositionList(orgId); // 按orgId过滤
|
||||
allRole = sysPositionService.queryAllPositionList(orgId, customerId); // 按orgId和customerId过滤
|
||||
} else {
|
||||
// 查询全局岗位 + 自己部门岗位
|
||||
List<SysPositionBO> globalPositions = sysPositionService.queryAllPositionList(0L);
|
||||
List<SysPositionBO> deptPositions = sysPositionService.queryAllPositionList(userOrgIds.get(0));
|
||||
List<SysPositionBO> globalPositions = sysPositionService.queryAllPositionList(0L, customerId);
|
||||
List<SysPositionBO> deptPositions = sysPositionService.queryAllPositionList(userOrgIds.get(0), customerId);
|
||||
allRole = new ArrayList<>(globalPositions);
|
||||
allRole.addAll(deptPositions);
|
||||
}
|
||||
@@ -215,13 +215,13 @@ public class SysPositionFacadeImpl implements ISysPositionFacade {
|
||||
Long topLevelDeptId = sysUserService.getUserTopLevelDeptId(currentUserId);
|
||||
if (topLevelDeptId != null) {
|
||||
// 查询全局岗位 + 顶级部门岗位
|
||||
List<SysPositionBO> globalPositions = sysPositionService.queryAllPositionList(0L);
|
||||
List<SysPositionBO> deptPositions = sysPositionService.queryAllPositionList(topLevelDeptId);
|
||||
List<SysPositionBO> globalPositions = sysPositionService.queryAllPositionList(0L, customerId);
|
||||
List<SysPositionBO> deptPositions = sysPositionService.queryAllPositionList(topLevelDeptId, customerId);
|
||||
allRole = new ArrayList<>(globalPositions);
|
||||
allRole.addAll(deptPositions);
|
||||
System.out.println("下级部门管理员 " + currentUserId + " 查询岗位选项,顶级部门ID: " + topLevelDeptId);
|
||||
} else {
|
||||
allRole = sysPositionService.queryAllPositionList(0L); // 只能查看全局岗位
|
||||
allRole = sysPositionService.queryAllPositionList(0L, customerId); // 只能查看全局岗位
|
||||
System.out.println("用户 " + currentUserId + " 只能查看全局岗位选项");
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -72,8 +72,8 @@ public class SysRoleFacadeImpl implements ISysRoleFacade {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Options<Long>> queryAllRoleListConvertOptions() {
|
||||
List<SysRoleBO> allRole = sysRoleService.queryAllRoleList();
|
||||
public List<Options<Long>> queryAllRoleListConvertOptions(Long customerId) {
|
||||
List<SysRoleBO> allRole = sysRoleService.queryAllRoleList(customerId);
|
||||
return allRole.stream()
|
||||
.map(item -> Options.<Long>builder()
|
||||
.label(item.getRoleName())
|
||||
|
||||
+11
@@ -76,4 +76,15 @@ public interface ISysPositionService extends IService<SysPosition> {
|
||||
* @CreateTime 2025-08-18
|
||||
*/
|
||||
List<SysPositionBO> queryAllPositionListByCustomerIds(List<Long> customerIds);
|
||||
|
||||
/**
|
||||
* 根据orgId和customerId查询岗位列表
|
||||
*
|
||||
* @param orgId 组织ID
|
||||
* @param customerId 租户ID
|
||||
* @return {@link List }<{@link SysPositionBO }> 岗位列表
|
||||
* @author bruno.gao
|
||||
* @CreateTime 2025-08-29
|
||||
*/
|
||||
List<SysPositionBO> queryAllPositionList(Long orgId, Long customerId);
|
||||
}
|
||||
|
||||
+2
-1
@@ -31,11 +31,12 @@ public interface ISysRoleService extends IService<SysRole> {
|
||||
/**
|
||||
* 获取所有角色信息
|
||||
*
|
||||
* @param customerId 租户ID
|
||||
* @return {@linkplain List} 角色集合
|
||||
* @author payne.zhuang
|
||||
* @CreateTime 2024-04-07 11:51
|
||||
*/
|
||||
List<SysRoleBO> queryAllRoleList();
|
||||
List<SysRoleBO> queryAllRoleList(Long customerId);
|
||||
|
||||
/**
|
||||
* 根据用户ID查询角色代码列表
|
||||
|
||||
+12
-1
@@ -49,7 +49,8 @@ public class SysPositionServiceImpl extends ServiceImpl<SysPositionMapper, SysPo
|
||||
public IPage<SysPosition> listSysPositionPage(PageQuery pageQuery, SysPositionBO sysPositionBO) {
|
||||
LambdaQueryWrapper<SysPosition> queryWrapper = new LambdaQueryWrapper<SysPosition>()
|
||||
.like(ObjectUtils.isNotEmpty(sysPositionBO.getName()), SysPosition::getName, sysPositionBO.getName())
|
||||
.eq(ObjectUtils.isNotEmpty(sysPositionBO.getStatus()), SysPosition::getStatus, sysPositionBO.getStatus());
|
||||
.eq(ObjectUtils.isNotEmpty(sysPositionBO.getStatus()), SysPosition::getStatus, sysPositionBO.getStatus())
|
||||
.eq(ObjectUtils.isNotEmpty(sysPositionBO.getCustomerId()), SysPosition::getCustomerId, sysPositionBO.getCustomerId());
|
||||
|
||||
// 部门过滤(基于orgId)
|
||||
if (sysPositionBO.getOrgIds() != null && !sysPositionBO.getOrgIds().isEmpty()) {
|
||||
@@ -75,6 +76,16 @@ public class SysPositionServiceImpl extends ServiceImpl<SysPositionMapper, SysPo
|
||||
.orderByAsc(SysPosition::getSort);
|
||||
return CglibUtil.convertList(baseMapper.selectList(queryWrapper), SysPositionBO::new);
|
||||
}
|
||||
|
||||
// 新增:按orgId和customerId过滤的岗位查询
|
||||
@Override
|
||||
public List<SysPositionBO> queryAllPositionList(Long orgId, Long customerId) {
|
||||
LambdaQueryWrapper<SysPosition> queryWrapper = new LambdaQueryWrapper<SysPosition>()
|
||||
.eq(ObjectUtils.isNotEmpty(orgId), SysPosition::getOrgId, orgId)
|
||||
.eq(ObjectUtils.isNotEmpty(customerId), SysPosition::getCustomerId, customerId)
|
||||
.orderByAsc(SysPosition::getSort);
|
||||
return CglibUtil.convertList(baseMapper.selectList(queryWrapper), SysPositionBO::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SysPositionBO> queryAllPositionListByCustomerIds(List<Long> customerIds) {
|
||||
|
||||
+3
-1
@@ -34,6 +34,7 @@ public class SysRoleServiceImpl extends ServiceImpl<SysRoleMapper, SysRole> impl
|
||||
.like(ObjectUtils.isNotEmpty(sysRoleBO.getRoleName()), SysRole::getRoleName, sysRoleBO.getRoleName())
|
||||
.like(ObjectUtils.isNotEmpty(sysRoleBO.getRoleCode()), SysRole::getRoleCode, sysRoleBO.getRoleCode())
|
||||
.eq(ObjectUtils.isNotEmpty(sysRoleBO.getStatus()), SysRole::getStatus, sysRoleBO.getStatus())
|
||||
.eq(ObjectUtils.isNotEmpty(sysRoleBO.getCustomerId()), SysRole::getCustomerId, sysRoleBO.getCustomerId())
|
||||
.orderByAsc(SysRole::getSort);
|
||||
return baseMapper.selectPage(pageQuery.buildPage(), queryWrapper);
|
||||
}
|
||||
@@ -50,8 +51,9 @@ public class SysRoleServiceImpl extends ServiceImpl<SysRoleMapper, SysRole> impl
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SysRoleBO> queryAllRoleList() {
|
||||
public List<SysRoleBO> queryAllRoleList(Long customerId) {
|
||||
var queryWrapper = new LambdaQueryWrapper<SysRole>()
|
||||
.eq(ObjectUtils.isNotEmpty(customerId), SysRole::getCustomerId, customerId)
|
||||
.orderByAsc(SysRole::getSort);
|
||||
return CglibUtil.convertList(baseMapper.selectList(queryWrapper), SysRoleBO::new);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user