【同步】BOOT 和 CLOUD 的功能
This commit is contained in:
@@ -37,8 +37,10 @@ public class HttpUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 解码 URL 参数
|
* 解码 URL 参数(query parameter)
|
||||||
|
* 注意:此方法会将 + 解码为空格,适用于 query parameter,不适用于 URL path
|
||||||
*
|
*
|
||||||
|
* @see #decodeUrlPath(String)
|
||||||
* @param value 参数
|
* @param value 参数
|
||||||
* @return 解码后的参数
|
* @return 解码后的参数
|
||||||
*/
|
*/
|
||||||
@@ -46,6 +48,20 @@ public class HttpUtils {
|
|||||||
return URLDecoder.decode(value, StandardCharsets.UTF_8);
|
return URLDecoder.decode(value, StandardCharsets.UTF_8);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解码 URL 路径
|
||||||
|
* 与 {@link #decodeUtf8(String)} 不同,此方法不会将 + 解码为空格,保持 + 为字面字符
|
||||||
|
* 适用于 URL path 部分的解码
|
||||||
|
*
|
||||||
|
* @param path URL 路径
|
||||||
|
* @return 解码后的路径
|
||||||
|
*/
|
||||||
|
public static String decodeUrlPath(String path) {
|
||||||
|
// 先将 + 替换为 %2B,避免被 URLDecoder 解码为空格
|
||||||
|
String encoded = path.replace("+", "%2B");
|
||||||
|
return URLDecoder.decode(encoded, StandardCharsets.UTF_8);
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public static String replaceUrlQuery(String url, String key, String value) {
|
public static String replaceUrlQuery(String url, String key, String value) {
|
||||||
UrlBuilder builder = UrlBuilder.of(url, Charset.defaultCharset());
|
UrlBuilder builder = UrlBuilder.of(url, Charset.defaultCharset());
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
|
|||||||
import cn.iocoder.yudao.framework.ip.core.Area;
|
import cn.iocoder.yudao.framework.ip.core.Area;
|
||||||
import cn.iocoder.yudao.framework.ip.core.enums.AreaTypeEnum;
|
import cn.iocoder.yudao.framework.ip.core.enums.AreaTypeEnum;
|
||||||
import lombok.NonNull;
|
import lombok.NonNull;
|
||||||
|
import lombok.experimental.UtilityClass;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -25,44 +26,46 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.
|
|||||||
* @author 芋道源码
|
* @author 芋道源码
|
||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
|
@UtilityClass
|
||||||
public class AreaUtils {
|
public class AreaUtils {
|
||||||
|
|
||||||
/**
|
|
||||||
* 初始化 SEARCHER
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("InstantiationOfUtilityClass")
|
|
||||||
private final static AreaUtils INSTANCE = new AreaUtils();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Area 内存缓存,提升访问速度
|
* Area 内存缓存,提升访问速度
|
||||||
*/
|
*/
|
||||||
private static Map<Integer, Area> areas;
|
private static Map<Integer, Area> areas;
|
||||||
|
|
||||||
private AreaUtils() {
|
static {
|
||||||
long now = System.currentTimeMillis();
|
init();
|
||||||
areas = new HashMap<>();
|
}
|
||||||
areas.put(Area.ID_GLOBAL, new Area(Area.ID_GLOBAL, "全球", 0,
|
|
||||||
null, new ArrayList<>()));
|
|
||||||
// 从 csv 中加载数据
|
|
||||||
List<CsvRow> rows = CsvUtil.getReader().read(ResourceUtil.getUtf8Reader("area.csv")).getRows();
|
|
||||||
rows.remove(0); // 删除 header
|
|
||||||
for (CsvRow row : rows) {
|
|
||||||
// 创建 Area 对象
|
|
||||||
Area area = new Area(Integer.valueOf(row.get(0)), row.get(1), Integer.valueOf(row.get(2)),
|
|
||||||
null, new ArrayList<>());
|
|
||||||
// 添加到 areas 中
|
|
||||||
areas.put(area.getId(), area);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 构建父子关系:因为 Area 中没有 parentId 字段,所以需要重复读取
|
/**
|
||||||
for (CsvRow row : rows) {
|
* 初始化
|
||||||
Area area = areas.get(Integer.valueOf(row.get(0))); // 自己
|
*/
|
||||||
Area parent = areas.get(Integer.valueOf(row.get(3))); // 父
|
private static void init() {
|
||||||
Assert.isTrue(area != parent, "{}:父子节点相同", area.getName());
|
try {
|
||||||
area.setParent(parent);
|
long now = System.currentTimeMillis();
|
||||||
parent.getChildren().add(area);
|
areas = new HashMap<>();
|
||||||
|
areas.put(Area.ID_GLOBAL, new Area(Area.ID_GLOBAL, "全球", 0, null, new ArrayList<>()));
|
||||||
|
// 从 csv 中加载数据
|
||||||
|
List<CsvRow> rows = CsvUtil.getReader().read(ResourceUtil.getUtf8Reader("area.csv")).getRows();
|
||||||
|
rows.remove(0); // 删除 header
|
||||||
|
for (CsvRow row : rows) {
|
||||||
|
Area area = new Area(Integer.valueOf(row.get(0)), row.get(1), Integer.valueOf(row.get(2)), null, new ArrayList<>());
|
||||||
|
areas.put(area.getId(), area);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建父子关系:因为 Area 中没有 parentId 字段,所以需要重复读取
|
||||||
|
for (CsvRow row : rows) {
|
||||||
|
Area area = areas.get(Integer.valueOf(row.get(0))); // 自己
|
||||||
|
Area parent = areas.get(Integer.valueOf(row.get(3))); // 父
|
||||||
|
Assert.isTrue(area != parent, "{}:父子节点相同", area.getName());
|
||||||
|
area.setParent(parent);
|
||||||
|
parent.getChildren().add(area);
|
||||||
|
}
|
||||||
|
log.info("启动加载 AreaUtils 成功,耗时 ({}) 毫秒", System.currentTimeMillis() - now);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("AreaUtils 初始化失败", e);
|
||||||
}
|
}
|
||||||
log.info("启动加载 AreaUtils 成功,耗时 ({}) 毫秒", System.currentTimeMillis() - now);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -3,11 +3,10 @@ package cn.iocoder.yudao.framework.ip.core.utils;
|
|||||||
import cn.hutool.core.io.resource.ResourceUtil;
|
import cn.hutool.core.io.resource.ResourceUtil;
|
||||||
import cn.iocoder.yudao.framework.ip.core.Area;
|
import cn.iocoder.yudao.framework.ip.core.Area;
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
|
import lombok.experimental.UtilityClass;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.lionsoul.ip2region.xdb.Searcher;
|
import org.lionsoul.ip2region.xdb.Searcher;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* IP 工具类
|
* IP 工具类
|
||||||
*
|
*
|
||||||
@@ -16,30 +15,29 @@ import java.io.IOException;
|
|||||||
* @author wanglhup
|
* @author wanglhup
|
||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
|
@UtilityClass
|
||||||
public class IPUtils {
|
public class IPUtils {
|
||||||
|
|
||||||
/**
|
|
||||||
* 初始化 SEARCHER
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("InstantiationOfUtilityClass")
|
|
||||||
private final static IPUtils INSTANCE = new IPUtils();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* IP 查询器,启动加载到内存中
|
* IP 查询器,启动加载到内存中
|
||||||
*/
|
*/
|
||||||
private static Searcher SEARCHER;
|
private static Searcher SEARCHER;
|
||||||
|
|
||||||
|
static {
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 私有化构造
|
* 初始化
|
||||||
*/
|
*/
|
||||||
private IPUtils() {
|
private static void init() {
|
||||||
try {
|
try {
|
||||||
long now = System.currentTimeMillis();
|
long now = System.currentTimeMillis();
|
||||||
byte[] bytes = ResourceUtil.readBytes("ip2region.xdb");
|
byte[] bytes = ResourceUtil.readBytes("ip2region.xdb");
|
||||||
SEARCHER = Searcher.newWithBuffer(bytes);
|
SEARCHER = Searcher.newWithBuffer(bytes);
|
||||||
log.info("启动加载 IPUtils 成功,耗时 ({}) 毫秒", System.currentTimeMillis() - now);
|
log.info("启动加载 IPUtils 成功,耗时 ({}) 毫秒", System.currentTimeMillis() - now);
|
||||||
} catch (IOException e) {
|
} catch (Exception e) {
|
||||||
log.error("启动加载 IPUtils 失败", e);
|
throw new RuntimeException("IPUtils 初始化失败", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import org.flowable.engine.impl.bpmn.behavior.AbstractBpmnActivityBehavior;
|
|||||||
import org.flowable.engine.impl.bpmn.behavior.SequentialMultiInstanceBehavior;
|
import org.flowable.engine.impl.bpmn.behavior.SequentialMultiInstanceBehavior;
|
||||||
import org.flowable.engine.impl.persistence.entity.ExecutionEntity;
|
import org.flowable.engine.impl.persistence.entity.ExecutionEntity;
|
||||||
|
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
@@ -53,7 +54,7 @@ public class BpmSequentialMultiInstanceBehavior extends SequentialMultiInstanceB
|
|||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
Set<Long> assigneeUserIds = (Set<Long>) execution.getVariableLocal(super.collectionVariable, Set.class);
|
Set<Long> assigneeUserIds = (Set<Long>) execution.getVariableLocal(super.collectionVariable, Set.class);
|
||||||
if (assigneeUserIds == null) {
|
if (assigneeUserIds == null) {
|
||||||
assigneeUserIds = taskCandidateInvoker.calculateUsersByTask(execution);
|
assigneeUserIds = new LinkedHashSet<>(taskCandidateInvoker.calculateUsersByTask(execution));
|
||||||
if (CollUtil.isEmpty(assigneeUserIds)) {
|
if (CollUtil.isEmpty(assigneeUserIds)) {
|
||||||
// 特殊:如果没有处理人的情况下,至少有一个 null 空元素,避免自动通过!
|
// 特殊:如果没有处理人的情况下,至少有一个 null 空元素,避免自动通过!
|
||||||
// 这样,保证在 BpmUserTaskActivityBehavior 至少创建出一个 Task 任务
|
// 这样,保证在 BpmUserTaskActivityBehavior 至少创建出一个 Task 任务
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ public class S3FileClient extends AbstractFileClient<S3FileClientConfig> {
|
|||||||
public String presignGetUrl(String url, Integer expirationSeconds) {
|
public String presignGetUrl(String url, Integer expirationSeconds) {
|
||||||
// 1. 将 url 转换为 path
|
// 1. 将 url 转换为 path
|
||||||
String path = StrUtil.removePrefix(url, config.getDomain() + "/");
|
String path = StrUtil.removePrefix(url, config.getDomain() + "/");
|
||||||
path = HttpUtils.decodeUtf8(HttpUtils.removeUrlQuery(path));
|
path = HttpUtils.decodeUrlPath(HttpUtils.removeUrlQuery(path));
|
||||||
|
|
||||||
// 2.1 情况一:公开访问:无需签名
|
// 2.1 情况一:公开访问:无需签名
|
||||||
// 考虑到老版本的兼容,所以必须是 config.getEnablePublicAccess() 为 false 时,才进行签名
|
// 考虑到老版本的兼容,所以必须是 config.getEnablePublicAccess() 为 false 时,才进行签名
|
||||||
|
|||||||
@@ -20,7 +20,9 @@ import jakarta.annotation.Resource;
|
|||||||
import jakarta.annotation.security.PermitAll;
|
import jakarta.annotation.security.PermitAll;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import jakarta.validation.Valid;
|
import jakarta.validation.Valid;
|
||||||
|
import jakarta.validation.constraints.Pattern;
|
||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -33,6 +35,7 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.
|
|||||||
@Tag(name = "管理后台 - 租户")
|
@Tag(name = "管理后台 - 租户")
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/system/tenant")
|
@RequestMapping("/system/tenant")
|
||||||
|
@Validated
|
||||||
public class TenantController {
|
public class TenantController {
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
@@ -63,7 +66,8 @@ public class TenantController {
|
|||||||
@TenantIgnore
|
@TenantIgnore
|
||||||
@Operation(summary = "使用域名,获得租户信息", description = "登录界面,根据用户的域名,获得租户信息")
|
@Operation(summary = "使用域名,获得租户信息", description = "登录界面,根据用户的域名,获得租户信息")
|
||||||
@Parameter(name = "website", description = "域名", required = true, example = "www.iocoder.cn")
|
@Parameter(name = "website", description = "域名", required = true, example = "www.iocoder.cn")
|
||||||
public CommonResult<TenantRespVO> getTenantByWebsite(@RequestParam("website") String website) {
|
public CommonResult<TenantRespVO> getTenantByWebsite(
|
||||||
|
@RequestParam("website") @Pattern(regexp = "^[a-zA-Z0-9.-]+$", message = "网站域名格式不正确") String website) {
|
||||||
TenantDO tenant = tenantService.getTenantByWebsite(website);
|
TenantDO tenant = tenantService.getTenantByWebsite(website);
|
||||||
if (tenant == null || CommonStatusEnum.isDisable(tenant.getStatus())) {
|
if (tenant == null || CommonStatusEnum.isDisable(tenant.getStatus())) {
|
||||||
return success(null);
|
return success(null);
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ import io.swagger.v3.oas.annotations.Parameter;
|
|||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import jakarta.annotation.security.PermitAll;
|
import jakarta.annotation.security.PermitAll;
|
||||||
|
import jakarta.validation.constraints.Pattern;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
@@ -22,6 +24,7 @@ import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
|||||||
@Tag(name = "用户 App - 租户")
|
@Tag(name = "用户 App - 租户")
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/system/tenant")
|
@RequestMapping("/system/tenant")
|
||||||
|
@Validated
|
||||||
public class AppTenantController {
|
public class AppTenantController {
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
@@ -32,7 +35,8 @@ public class AppTenantController {
|
|||||||
@TenantIgnore
|
@TenantIgnore
|
||||||
@Operation(summary = "使用域名,获得租户信息", description = "根据用户的域名,获得租户信息")
|
@Operation(summary = "使用域名,获得租户信息", description = "根据用户的域名,获得租户信息")
|
||||||
@Parameter(name = "website", description = "域名", required = true, example = "www.iocoder.cn")
|
@Parameter(name = "website", description = "域名", required = true, example = "www.iocoder.cn")
|
||||||
public CommonResult<AppTenantRespVO> getTenantByWebsite(@RequestParam("website") String website) {
|
public CommonResult<AppTenantRespVO> getTenantByWebsite(
|
||||||
|
@RequestParam("website") @Pattern(regexp = "^[a-zA-Z0-9.-]+$", message = "网站域名格式不正确") String website) {
|
||||||
TenantDO tenant = tenantService.getTenantByWebsite(website);
|
TenantDO tenant = tenantService.getTenantByWebsite(website);
|
||||||
if (tenant == null || CommonStatusEnum.isDisable(tenant.getStatus())) {
|
if (tenant == null || CommonStatusEnum.isDisable(tenant.getStatus())) {
|
||||||
return success(null);
|
return success(null);
|
||||||
|
|||||||
Reference in New Issue
Block a user