Merge remote-tracking branch 'origin/dev' into dev
This commit is contained in:
5
pom.xml
5
pom.xml
@@ -32,7 +32,7 @@
|
|||||||
<url>https://github.com/YunaiV/ruoyi-vue-pro</url>
|
<url>https://github.com/YunaiV/ruoyi-vue-pro</url>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<revision>3.0.45</revision>
|
<revision>3.0.46</revision>
|
||||||
<!-- Maven 相关 -->
|
<!-- Maven 相关 -->
|
||||||
<java.version>17</java.version>
|
<java.version>17</java.version>
|
||||||
<maven.compiler.source>${java.version}</maven.compiler.source>
|
<maven.compiler.source>${java.version}</maven.compiler.source>
|
||||||
@@ -271,7 +271,8 @@
|
|||||||
<profile>
|
<profile>
|
||||||
<id>chenbowen</id>
|
<id>chenbowen</id>
|
||||||
<properties>
|
<properties>
|
||||||
<env.name>local</env.name>
|
<!-- <env.name>local</env.name>-->
|
||||||
|
<env.name>dev</env.name>
|
||||||
<!-- <config.server-addr>localhost:8848</config.server-addr>-->
|
<!-- <config.server-addr>localhost:8848</config.server-addr>-->
|
||||||
<config.server-addr>172.16.46.63:30848</config.server-addr>
|
<config.server-addr>172.16.46.63:30848</config.server-addr>
|
||||||
<config.namespace>chenbowen</config.namespace>
|
<config.namespace>chenbowen</config.namespace>
|
||||||
|
|||||||
@@ -19,7 +19,8 @@ CREATE TABLE databus_api_definition_credential (
|
|||||||
deleted BIT DEFAULT '0' NOT NULL
|
deleted BIT DEFAULT '0' NOT NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE UNIQUE INDEX uk_databus_api_definition_credential ON databus_api_definition_credential (api_id, credential_id, deleted);
|
-- 去掉错误的唯一索引逻辑
|
||||||
|
-- CREATE UNIQUE INDEX uk_databus_api_definition_credential ON databus_api_definition_credential (api_id, credential_id, deleted);
|
||||||
CREATE INDEX idx_databus_api_definition_credential_api ON databus_api_definition_credential (api_id);
|
CREATE INDEX idx_databus_api_definition_credential_api ON databus_api_definition_credential (api_id);
|
||||||
CREATE INDEX idx_databus_api_definition_credential_cred ON databus_api_definition_credential (credential_id);
|
CREATE INDEX idx_databus_api_definition_credential_cred ON databus_api_definition_credential (credential_id);
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
<url>https://github.com/YunaiV/ruoyi-vue-pro</url>
|
<url>https://github.com/YunaiV/ruoyi-vue-pro</url>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<revision>3.0.45</revision>
|
<revision>3.0.46</revision>
|
||||||
<flatten-maven-plugin.version>1.6.0</flatten-maven-plugin.version>
|
<flatten-maven-plugin.version>1.6.0</flatten-maven-plugin.version>
|
||||||
<!-- 统一依赖管理 -->
|
<!-- 统一依赖管理 -->
|
||||||
<spring.boot.version>3.4.5</spring.boot.version>
|
<spring.boot.version>3.4.5</spring.boot.version>
|
||||||
|
|||||||
@@ -28,6 +28,14 @@ import java.util.*;
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
public class BusinessDataPermissionEntityScanner {
|
public class BusinessDataPermissionEntityScanner {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 临时排除的包前缀(物流模块 DO,不参与数据权限扫描)
|
||||||
|
*/
|
||||||
|
private static final Set<String> EXCLUDED_PACKAGE_PREFIXES = Set.of(
|
||||||
|
"com.zt.plat.module.backendlogistics",
|
||||||
|
"com.zt.plat.module.erp",
|
||||||
|
"com.zt.plat.framework.mybatis.core.dataobject.BusinessBaseDO");
|
||||||
|
|
||||||
private final Set<String> basePackages;
|
private final Set<String> basePackages;
|
||||||
private final ClassLoader classLoader;
|
private final ClassLoader classLoader;
|
||||||
|
|
||||||
@@ -70,6 +78,9 @@ public class BusinessDataPermissionEntityScanner {
|
|||||||
if (!StringUtils.hasText(className)) {
|
if (!StringUtils.hasText(className)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (isExcludedPackage(className)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
Class<?> clazz = ClassUtils.forName(className, classLoader);
|
Class<?> clazz = ClassUtils.forName(className, classLoader);
|
||||||
if (clazz == BusinessBaseDO.class || !BusinessBaseDO.class.isAssignableFrom(clazz)) {
|
if (clazz == BusinessBaseDO.class || !BusinessBaseDO.class.isAssignableFrom(clazz)) {
|
||||||
@@ -92,6 +103,15 @@ public class BusinessDataPermissionEntityScanner {
|
|||||||
return new ArrayList<>(metadataMap.values());
|
return new ArrayList<>(metadataMap.values());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isExcludedPackage(String className) {
|
||||||
|
for (String prefix : EXCLUDED_PACKAGE_PREFIXES) {
|
||||||
|
if (className.startsWith(prefix)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private EntityMetadata buildMetadata(Class<? extends BusinessBaseDO> entityClass) {
|
private EntityMetadata buildMetadata(Class<? extends BusinessBaseDO> entityClass) {
|
||||||
String tableName = resolveTableName(entityClass);
|
String tableName = resolveTableName(entityClass);
|
||||||
if (!StringUtils.hasText(tableName)) {
|
if (!StringUtils.hasText(tableName)) {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package com.zt.plat.framework.datapermission.core.rule.dept;
|
|||||||
import cn.hutool.core.collection.CollUtil;
|
import cn.hutool.core.collection.CollUtil;
|
||||||
import cn.hutool.core.util.ObjectUtil;
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
|
||||||
import com.zt.plat.framework.common.biz.system.permission.PermissionCommonApi;
|
import com.zt.plat.framework.common.biz.system.permission.PermissionCommonApi;
|
||||||
import com.zt.plat.framework.common.biz.system.permission.dto.DeptDataPermissionRespDTO;
|
import com.zt.plat.framework.common.biz.system.permission.dto.DeptDataPermissionRespDTO;
|
||||||
import com.zt.plat.framework.common.enums.UserTypeEnum;
|
import com.zt.plat.framework.common.enums.UserTypeEnum;
|
||||||
@@ -14,7 +15,7 @@ import com.zt.plat.framework.mybatis.core.util.MyBatisUtils;
|
|||||||
import com.zt.plat.framework.security.core.LoginUser;
|
import com.zt.plat.framework.security.core.LoginUser;
|
||||||
import com.zt.plat.framework.security.core.util.SecurityFrameworkUtils;
|
import com.zt.plat.framework.security.core.util.SecurityFrameworkUtils;
|
||||||
import com.zt.plat.framework.tenant.core.context.CompanyContextHolder;
|
import com.zt.plat.framework.tenant.core.context.CompanyContextHolder;
|
||||||
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
|
import com.zt.plat.framework.tenant.core.context.DeptContextHolder;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import net.sf.jsqlparser.expression.Alias;
|
import net.sf.jsqlparser.expression.Alias;
|
||||||
@@ -108,6 +109,11 @@ public class DeptDataPermissionRule implements DataPermissionRule {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 显式忽略部门数据权限时直接放行
|
||||||
|
if (DeptContextHolder.shouldIgnore()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
// 获得数据权限
|
// 获得数据权限
|
||||||
DeptDataPermissionRespDTO deptDataPermission = loginUser.getContext(CONTEXT_KEY, DeptDataPermissionRespDTO.class);
|
DeptDataPermissionRespDTO deptDataPermission = loginUser.getContext(CONTEXT_KEY, DeptDataPermissionRespDTO.class);
|
||||||
// 从上下文中拿不到,则调用逻辑进行获取
|
// 从上下文中拿不到,则调用逻辑进行获取
|
||||||
@@ -136,6 +142,20 @@ public class DeptDataPermissionRule implements DataPermissionRule {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 若存在部门上下文,优先使用上下文中的单一部门,必要时校验公司一致性
|
||||||
|
Long ctxDeptId = DeptContextHolder.getDeptId();
|
||||||
|
if (ctxDeptId != null && ctxDeptId > 0L) {
|
||||||
|
Long currentCompanyId = CompanyContextHolder.getCompanyId();
|
||||||
|
Long ctxCompanyId = DeptContextHolder.getCompanyId();
|
||||||
|
Long compareCompanyId = ctxCompanyId != null ? ctxCompanyId : currentCompanyId;
|
||||||
|
if (currentCompanyId != null && currentCompanyId > 0L
|
||||||
|
&& compareCompanyId != null && !currentCompanyId.equals(compareCompanyId)) {
|
||||||
|
log.warn("[getExpression][LoginUser({}) Table({}/{}) DeptContextHolder company mismatch: currentCompanyId={}, ctxCompanyId={}, ctxDeptId={}, source=DeptContextHolder]",
|
||||||
|
JsonUtils.toJsonString(loginUser), tableName, tableAlias == null ? null : tableAlias.getName(),
|
||||||
|
currentCompanyId, compareCompanyId, ctxDeptId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 情况一,如果是 ALL 可查看全部,则无需拼接条件
|
// 情况一,如果是 ALL 可查看全部,则无需拼接条件
|
||||||
if (deptDataPermission.getAll()) {
|
if (deptDataPermission.getAll()) {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -7,10 +7,13 @@ import com.zt.plat.framework.common.enums.UserTypeEnum;
|
|||||||
import com.zt.plat.framework.common.util.collection.SetUtils;
|
import com.zt.plat.framework.common.util.collection.SetUtils;
|
||||||
import com.zt.plat.framework.security.core.LoginUser;
|
import com.zt.plat.framework.security.core.LoginUser;
|
||||||
import com.zt.plat.framework.security.core.util.SecurityFrameworkUtils;
|
import com.zt.plat.framework.security.core.util.SecurityFrameworkUtils;
|
||||||
|
import com.zt.plat.framework.tenant.core.context.CompanyContextHolder;
|
||||||
|
import com.zt.plat.framework.tenant.core.context.DeptContextHolder;
|
||||||
import com.zt.plat.framework.test.core.ut.BaseMockitoUnitTest;
|
import com.zt.plat.framework.test.core.ut.BaseMockitoUnitTest;
|
||||||
import com.zt.plat.framework.common.biz.system.permission.dto.DeptDataPermissionRespDTO;
|
import com.zt.plat.framework.common.biz.system.permission.dto.DeptDataPermissionRespDTO;
|
||||||
import net.sf.jsqlparser.expression.Alias;
|
import net.sf.jsqlparser.expression.Alias;
|
||||||
import net.sf.jsqlparser.expression.Expression;
|
import net.sf.jsqlparser.expression.Expression;
|
||||||
|
import org.junit.jupiter.api.AfterEach;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.mockito.InjectMocks;
|
import org.mockito.InjectMocks;
|
||||||
@@ -27,6 +30,7 @@ import static org.junit.jupiter.api.Assertions.*;
|
|||||||
import static org.mockito.ArgumentMatchers.eq;
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
import static org.mockito.ArgumentMatchers.same;
|
import static org.mockito.ArgumentMatchers.same;
|
||||||
import static org.mockito.Mockito.mockStatic;
|
import static org.mockito.Mockito.mockStatic;
|
||||||
|
import static org.mockito.Mockito.verifyNoInteractions;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -48,7 +52,13 @@ class DeptDataPermissionRuleTest extends BaseMockitoUnitTest {
|
|||||||
// 清空 rule
|
// 清空 rule
|
||||||
rule.getTableNames().clear();
|
rule.getTableNames().clear();
|
||||||
((Map<String, String>) ReflectUtil.getFieldValue(rule, "deptColumns")).clear();
|
((Map<String, String>) ReflectUtil.getFieldValue(rule, "deptColumns")).clear();
|
||||||
((Map<String, String>) ReflectUtil.getFieldValue(rule, "deptColumns")).clear();
|
((Map<String, String>) ReflectUtil.getFieldValue(rule, "userColumns")).clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterEach
|
||||||
|
void tearDown() {
|
||||||
|
DeptContextHolder.clear();
|
||||||
|
CompanyContextHolder.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test // 无 LoginUser
|
@Test // 无 LoginUser
|
||||||
@@ -236,4 +246,88 @@ class DeptDataPermissionRuleTest extends BaseMockitoUnitTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test // 忽略部门数据权限,直接放行
|
||||||
|
void testGetExpression_ignoreDeptContext() {
|
||||||
|
try (MockedStatic<SecurityFrameworkUtils> secMock = mockStatic(SecurityFrameworkUtils.class);
|
||||||
|
MockedStatic<DeptContextHolder> deptCtxMock = mockStatic(DeptContextHolder.class)) {
|
||||||
|
String tableName = "t_order";
|
||||||
|
Alias alias = new Alias("o");
|
||||||
|
LoginUser loginUser = randomPojo(LoginUser.class, o -> o.setId(1L)
|
||||||
|
.setUserType(UserTypeEnum.ADMIN.getValue()));
|
||||||
|
secMock.when(SecurityFrameworkUtils::getLoginUser).thenReturn(loginUser);
|
||||||
|
deptCtxMock.when(DeptContextHolder::shouldIgnore).thenReturn(true);
|
||||||
|
|
||||||
|
Expression expression = rule.getExpression(tableName, alias);
|
||||||
|
|
||||||
|
assertNull(expression);
|
||||||
|
verifyNoInteractions(permissionApi);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test // 上下文部门存在且公司一致时,清空原集合并覆盖为单一 deptId
|
||||||
|
void testGetExpression_deptContextOverride_companyMatch() {
|
||||||
|
try (MockedStatic<SecurityFrameworkUtils> secMock = mockStatic(SecurityFrameworkUtils.class);
|
||||||
|
MockedStatic<DeptContextHolder> deptCtxMock = mockStatic(DeptContextHolder.class);
|
||||||
|
MockedStatic<CompanyContextHolder> companyCtxMock = mockStatic(CompanyContextHolder.class)) {
|
||||||
|
|
||||||
|
String tableName = "t_user";
|
||||||
|
Alias tableAlias = new Alias("u");
|
||||||
|
LoginUser loginUser = randomPojo(LoginUser.class, o -> o.setId(1L)
|
||||||
|
.setUserType(UserTypeEnum.ADMIN.getValue()));
|
||||||
|
secMock.when(SecurityFrameworkUtils::getLoginUser).thenReturn(loginUser);
|
||||||
|
|
||||||
|
DeptDataPermissionRespDTO deptDataPermission = new DeptDataPermissionRespDTO()
|
||||||
|
.setDeptIds(CollUtil.newLinkedHashSet(10L, 20L))
|
||||||
|
.setCompanyId(1L);
|
||||||
|
when(permissionApi.getDeptDataPermission(same(1L))).thenReturn(success(deptDataPermission));
|
||||||
|
|
||||||
|
deptCtxMock.when(DeptContextHolder::shouldIgnore).thenReturn(false);
|
||||||
|
deptCtxMock.when(DeptContextHolder::getDeptId).thenReturn(99L);
|
||||||
|
deptCtxMock.when(DeptContextHolder::getCompanyId).thenReturn(1L);
|
||||||
|
companyCtxMock.when(CompanyContextHolder::getCompanyId).thenReturn(1L);
|
||||||
|
companyCtxMock.when(CompanyContextHolder::isIgnore).thenReturn(false);
|
||||||
|
|
||||||
|
rule.addDeptColumn(tableName, "dept_id");
|
||||||
|
|
||||||
|
Expression expression = rule.getExpression(tableName, tableAlias);
|
||||||
|
|
||||||
|
assertEquals("u.dept_id IN (99)", expression.toString());
|
||||||
|
assertEquals(CollUtil.newLinkedHashSet(99L), deptDataPermission.getDeptIds());
|
||||||
|
assertEquals(1L, deptDataPermission.getCompanyId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test // 上下文部门存在但公司不一致时,记录告警并保持原逻辑(不覆盖)
|
||||||
|
void testGetExpression_deptContextOverride_companyMismatch() {
|
||||||
|
try (MockedStatic<SecurityFrameworkUtils> secMock = mockStatic(SecurityFrameworkUtils.class);
|
||||||
|
MockedStatic<DeptContextHolder> deptCtxMock = mockStatic(DeptContextHolder.class);
|
||||||
|
MockedStatic<CompanyContextHolder> companyCtxMock = mockStatic(CompanyContextHolder.class)) {
|
||||||
|
|
||||||
|
String tableName = "t_user";
|
||||||
|
Alias tableAlias = new Alias("u");
|
||||||
|
LoginUser loginUser = randomPojo(LoginUser.class, o -> o.setId(1L)
|
||||||
|
.setUserType(UserTypeEnum.ADMIN.getValue()));
|
||||||
|
secMock.when(SecurityFrameworkUtils::getLoginUser).thenReturn(loginUser);
|
||||||
|
|
||||||
|
DeptDataPermissionRespDTO deptDataPermission = new DeptDataPermissionRespDTO()
|
||||||
|
.setDeptIds(CollUtil.newLinkedHashSet(10L))
|
||||||
|
.setCompanyId(1L);
|
||||||
|
when(permissionApi.getDeptDataPermission(same(1L))).thenReturn(success(deptDataPermission));
|
||||||
|
|
||||||
|
deptCtxMock.when(DeptContextHolder::shouldIgnore).thenReturn(false);
|
||||||
|
deptCtxMock.when(DeptContextHolder::getDeptId).thenReturn(99L);
|
||||||
|
deptCtxMock.when(DeptContextHolder::getCompanyId).thenReturn(2L);
|
||||||
|
companyCtxMock.when(CompanyContextHolder::getCompanyId).thenReturn(1L);
|
||||||
|
companyCtxMock.when(CompanyContextHolder::isIgnore).thenReturn(false);
|
||||||
|
|
||||||
|
rule.addDeptColumn(tableName, "dept_id");
|
||||||
|
|
||||||
|
Expression expression = rule.getExpression(tableName, tableAlias);
|
||||||
|
|
||||||
|
assertEquals("u.dept_id IN (10)", expression.toString());
|
||||||
|
assertEquals(CollUtil.newLinkedHashSet(10L), deptDataPermission.getDeptIds());
|
||||||
|
assertEquals(1L, deptDataPermission.getCompanyId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,61 @@
|
|||||||
|
package com.zt.plat.framework.tenant.core.context;
|
||||||
|
|
||||||
|
import com.alibaba.ttl.TransmittableThreadLocal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 部门上下文 Holder,使用 {@link TransmittableThreadLocal} 支持在线程池/异步场景下的上下文传递。
|
||||||
|
*
|
||||||
|
* 包含当前部门编号、所属公司编号以及是否忽略部门数据权限的标识。
|
||||||
|
*/
|
||||||
|
public class DeptContextHolder {
|
||||||
|
|
||||||
|
/** 当前部门编号 */
|
||||||
|
private static final ThreadLocal<Long> DEPT_ID = new TransmittableThreadLocal<>();
|
||||||
|
/** 当前部门所属公司编号(用于一致性校验) */
|
||||||
|
private static final ThreadLocal<Long> COMPANY_ID = new TransmittableThreadLocal<>();
|
||||||
|
/** 是否忽略部门数据权限 */
|
||||||
|
private static final ThreadLocal<Boolean> IGNORE = new TransmittableThreadLocal<>();
|
||||||
|
|
||||||
|
public static Long getDeptId() {
|
||||||
|
return DEPT_ID.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Long getCompanyId() {
|
||||||
|
return COMPANY_ID.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置部门与所属公司编号。
|
||||||
|
*/
|
||||||
|
public static void setContext(Long deptId, Long companyId) {
|
||||||
|
DEPT_ID.set(deptId);
|
||||||
|
COMPANY_ID.set(companyId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setDeptId(Long deptId) {
|
||||||
|
DEPT_ID.set(deptId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setCompanyId(Long companyId) {
|
||||||
|
COMPANY_ID.set(companyId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean hasDeptId() {
|
||||||
|
Long deptId = DEPT_ID.get();
|
||||||
|
return deptId != null && deptId > 0L;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setIgnore(Boolean ignore) {
|
||||||
|
IGNORE.set(ignore);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean shouldIgnore() {
|
||||||
|
return Boolean.TRUE.equals(IGNORE.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void clear() {
|
||||||
|
DEPT_ID.remove();
|
||||||
|
COMPANY_ID.remove();
|
||||||
|
IGNORE.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ package com.zt.plat.framework.tenant.core.web;
|
|||||||
import com.zt.plat.framework.security.core.LoginUser;
|
import com.zt.plat.framework.security.core.LoginUser;
|
||||||
import com.zt.plat.framework.security.core.util.SecurityFrameworkUtils;
|
import com.zt.plat.framework.security.core.util.SecurityFrameworkUtils;
|
||||||
import com.zt.plat.framework.tenant.core.context.CompanyContextHolder;
|
import com.zt.plat.framework.tenant.core.context.CompanyContextHolder;
|
||||||
|
import com.zt.plat.framework.tenant.core.context.DeptContextHolder;
|
||||||
import com.zt.plat.framework.web.core.util.WebFrameworkUtils;
|
import com.zt.plat.framework.web.core.util.WebFrameworkUtils;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
@@ -66,11 +67,19 @@ public class CompanyVisitContextInterceptor implements HandlerInterceptor {
|
|||||||
|
|
||||||
if (companyId == null || companyId <= 0L) {
|
if (companyId == null || companyId <= 0L) {
|
||||||
CompanyContextHolder.setIgnore(true);
|
CompanyContextHolder.setIgnore(true);
|
||||||
|
DeptContextHolder.clear();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
CompanyContextHolder.setIgnore(false);
|
CompanyContextHolder.setIgnore(false);
|
||||||
CompanyContextHolder.setCompanyId(companyId);
|
CompanyContextHolder.setCompanyId(companyId);
|
||||||
|
// 默认不忽略部门数据权限;如果有有效部门则写入上下文
|
||||||
|
DeptContextHolder.setIgnore(false);
|
||||||
|
if (deptId != null && deptId > 0L) {
|
||||||
|
DeptContextHolder.setContext(deptId, companyId);
|
||||||
|
} else {
|
||||||
|
DeptContextHolder.clear();
|
||||||
|
}
|
||||||
if (loginUser == null) {
|
if (loginUser == null) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -91,7 +100,9 @@ public class CompanyVisitContextInterceptor implements HandlerInterceptor {
|
|||||||
LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
|
LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
|
||||||
if (loginUser != null) {
|
if (loginUser != null) {
|
||||||
loginUser.setVisitCompanyId(0L);
|
loginUser.setVisitCompanyId(0L);
|
||||||
|
loginUser.setVisitDeptId(0L);
|
||||||
}
|
}
|
||||||
|
DeptContextHolder.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Long resolveLong(Object value) {
|
private Long resolveLong(Object value) {
|
||||||
|
|||||||
@@ -0,0 +1,88 @@
|
|||||||
|
package com.zt.plat.framework.tenant.core.web;
|
||||||
|
|
||||||
|
import com.zt.plat.framework.security.core.LoginUser;
|
||||||
|
import com.zt.plat.framework.tenant.core.context.CompanyContextHolder;
|
||||||
|
import com.zt.plat.framework.tenant.core.context.DeptContextHolder;
|
||||||
|
import org.junit.jupiter.api.AfterEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.web.servlet.HandlerInterceptor;
|
||||||
|
import org.springframework.mock.web.MockHttpServletRequest;
|
||||||
|
import org.springframework.mock.web.MockHttpServletResponse;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CompanyVisitContextInterceptor 单测,覆盖公司/部门上下文写入及清理。
|
||||||
|
*/
|
||||||
|
class CompanyVisitContextInterceptorTest {
|
||||||
|
|
||||||
|
private final HandlerInterceptor interceptor = new CompanyVisitContextInterceptor();
|
||||||
|
|
||||||
|
@AfterEach
|
||||||
|
void tearDown() {
|
||||||
|
CompanyContextHolder.clear();
|
||||||
|
DeptContextHolder.clear();
|
||||||
|
SecurityContextHolder.clearContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test // 无公司 id:应 ignore,公司/部门上下文清空
|
||||||
|
void testPreHandle_noCompanyId_ignore() throws Exception {
|
||||||
|
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||||
|
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||||
|
|
||||||
|
boolean result = interceptor.preHandle(request, response, new Object());
|
||||||
|
|
||||||
|
assertTrue(result);
|
||||||
|
assertTrue(CompanyContextHolder.isIgnore());
|
||||||
|
assertNull(CompanyContextHolder.getCompanyId());
|
||||||
|
assertNull(DeptContextHolder.getDeptId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test // 有公司无部门:写入公司,部门清空
|
||||||
|
void testPreHandle_companyOnly() throws Exception {
|
||||||
|
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||||
|
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||||
|
LoginUser loginUser = new LoginUser();
|
||||||
|
SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken(loginUser, null));
|
||||||
|
request.addHeader("visit-company-id", "11");
|
||||||
|
|
||||||
|
boolean result = interceptor.preHandle(request, response, new Object());
|
||||||
|
|
||||||
|
assertTrue(result);
|
||||||
|
assertFalse(CompanyContextHolder.isIgnore());
|
||||||
|
assertEquals(11L, CompanyContextHolder.getCompanyId());
|
||||||
|
assertFalse(DeptContextHolder.shouldIgnore());
|
||||||
|
assertNull(DeptContextHolder.getDeptId());
|
||||||
|
assertEquals(11L, loginUser.getVisitCompanyId());
|
||||||
|
assertNull(loginUser.getVisitDeptId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test // 有公司+部门:写入公司、部门上下文,afterCompletion 清理 visitDeptId & holder
|
||||||
|
void testPreHandle_withCompanyAndDept_andAfterCompletionClear() throws Exception {
|
||||||
|
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||||
|
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||||
|
LoginUser loginUser = new LoginUser();
|
||||||
|
SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken(loginUser, null));
|
||||||
|
request.addHeader("visit-company-id", "22");
|
||||||
|
request.addHeader("visit-dept-id", "33");
|
||||||
|
|
||||||
|
boolean result = interceptor.preHandle(request, response, new Object());
|
||||||
|
|
||||||
|
assertTrue(result);
|
||||||
|
assertFalse(CompanyContextHolder.isIgnore());
|
||||||
|
assertEquals(22L, CompanyContextHolder.getCompanyId());
|
||||||
|
assertEquals(33L, DeptContextHolder.getDeptId());
|
||||||
|
assertEquals(22L, DeptContextHolder.getCompanyId());
|
||||||
|
assertEquals(22L, loginUser.getVisitCompanyId());
|
||||||
|
assertEquals(33L, loginUser.getVisitDeptId());
|
||||||
|
|
||||||
|
// afterCompletion: 清理 visitCompanyId/visitDeptId 与 holder
|
||||||
|
interceptor.afterCompletion(request, response, new Object(), null);
|
||||||
|
assertEquals(0L, loginUser.getVisitCompanyId());
|
||||||
|
assertEquals(0L, loginUser.getVisitDeptId());
|
||||||
|
assertNull(DeptContextHolder.getDeptId());
|
||||||
|
assertNull(DeptContextHolder.getCompanyId());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -33,6 +33,7 @@ import org.springframework.web.util.ContentCachingResponseWrapper;
|
|||||||
import org.springframework.web.util.UriComponentsBuilder;
|
import org.springframework.web.util.UriComponentsBuilder;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.net.URLDecoder;
|
||||||
import java.net.URLEncoder;
|
import java.net.URLEncoder;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
@@ -304,15 +305,28 @@ public class GatewaySecurityFilter extends OncePerRequestFilter {
|
|||||||
.build()
|
.build()
|
||||||
.getQueryParams();
|
.getQueryParams();
|
||||||
params.forEach((key, values) -> {
|
params.forEach((key, values) -> {
|
||||||
if (!StringUtils.hasText(key) || "signature".equalsIgnoreCase(key)) {
|
String decodedKey = URLDecoder.decode(key, StandardCharsets.UTF_8);
|
||||||
|
if (!StringUtils.hasText(decodedKey) || "signature".equalsIgnoreCase(decodedKey)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (CollectionUtils.isEmpty(values)) {
|
if (CollectionUtils.isEmpty(values)) {
|
||||||
target.put(key, "");
|
target.put(decodedKey, "");
|
||||||
} else if (values.size() == 1) {
|
return;
|
||||||
target.put(key, values.get(0));
|
}
|
||||||
|
// 对每一个 value 做 URL 解码,确保与客户端原文签名一致
|
||||||
|
List<String> decodedValues = values.stream()
|
||||||
|
.map(val -> URLDecoder.decode(val, StandardCharsets.UTF_8))
|
||||||
|
.toList();
|
||||||
|
boolean allNullLiteral = decodedValues.stream()
|
||||||
|
.allMatch(v -> "null".equals(v));
|
||||||
|
if (allNullLiteral) {
|
||||||
|
// 过滤掉仅包含字符串 "null" 的参数
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (decodedValues.size() == 1) {
|
||||||
|
target.put(decodedKey, decodedValues.get(0));
|
||||||
} else {
|
} else {
|
||||||
target.put(key, String.join(",", values));
|
target.put(decodedKey, String.join(",", decodedValues));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (IllegalArgumentException ex) {
|
} catch (IllegalArgumentException ex) {
|
||||||
|
|||||||
@@ -5,6 +5,10 @@ import com.fasterxml.jackson.core.type.TypeReference;
|
|||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.zt.plat.framework.common.util.security.CryptoSignatureUtils;
|
import com.zt.plat.framework.common.util.security.CryptoSignatureUtils;
|
||||||
|
|
||||||
|
import javax.net.ssl.SSLContext;
|
||||||
|
import javax.net.ssl.SSLParameters;
|
||||||
|
import javax.net.ssl.TrustManager;
|
||||||
|
import javax.net.ssl.X509TrustManager;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.PrintStream;
|
import java.io.PrintStream;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
@@ -23,10 +27,6 @@ import java.util.LinkedHashMap;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import javax.net.ssl.SSLContext;
|
|
||||||
import javax.net.ssl.SSLParameters;
|
|
||||||
import javax.net.ssl.TrustManager;
|
|
||||||
import javax.net.ssl.X509TrustManager;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 可直接运行的示例,演示如何使用 appId=test 与对应密钥调用本地 Databus API。
|
* 可直接运行的示例,演示如何使用 appId=test 与对应密钥调用本地 Databus API。
|
||||||
@@ -37,14 +37,14 @@ public final class DatabusApiInvocationExample {
|
|||||||
|
|
||||||
// private static final String APP_ID = "iwork";
|
// private static final String APP_ID = "iwork";
|
||||||
// private static final String APP_SECRET = "lpGXiNe/GMLk0vsbYGLa8eYxXq8tGhTbuu3/D4MJzIk=";
|
// private static final String APP_SECRET = "lpGXiNe/GMLk0vsbYGLa8eYxXq8tGhTbuu3/D4MJzIk=";
|
||||||
private static final String APP_ID = "ztmy";
|
private static final String APP_ID = "jwyw";
|
||||||
private static final String APP_SECRET = "zFre/nTRGi7LpoFjN7oQkKeOT09x1fWTyIswrc702QQ=";
|
private static final String APP_SECRET = "MhfCcqB59rDTnB5yGOVXWtp/5a0JXir7pSjPl5cVMJ8=";
|
||||||
private static final String ENCRYPTION_TYPE = CryptoSignatureUtils.ENCRYPT_TYPE_AES;
|
private static final String ENCRYPTION_TYPE = CryptoSignatureUtils.ENCRYPT_TYPE_AES;
|
||||||
// private static final String TARGET_API = "http://172.16.46.63:30081/admin-api/databus/api/portal/callback/v1";
|
// private static final String TARGET_API = "http://172.16.46.63:30081/admin-api/databus/api/portal/callback/v1";
|
||||||
// private static final String TARGET_API = "http://172.16.46.195:48080/admin-api/databus/api/portal/lgstOpenApi/v1";
|
// private static final String TARGET_API = "http://172.16.46.195:48080/admin-api/databus/api/portal/lgstOpenApi/v1";
|
||||||
// private static final String TARGET_API = "http://172.16.46.195:48080/admin-api/databus/api/portal/lgstOpenApi/v1";
|
// private static final String TARGET_API = "http://172.16.46.195:48080/admin-api/databus/api/portal/lgstOpenApi/v1";
|
||||||
private static final String TARGET_API = "https://jygk.chncopper.com:30078/admin-api/databus/api/portal/lgstOpenApi/v1";
|
// private static final String TARGET_API = "https://jygk.chncopper.com:30078/admin-api/databus/api/portal/lgstOpenApi/v1";
|
||||||
// private static final String TARGET_API = "http://localhost:48080/admin-api/databus/api/portal/callback/v1";
|
private static final String TARGET_API = "http://localhost:48080/admin-api/databus/api/portal/testcbw/456";
|
||||||
// private static final String TARGET_API = "http://localhost:48080/admin-api/databus/api/portal/lgstOpenApi/v1";
|
// private static final String TARGET_API = "http://localhost:48080/admin-api/databus/api/portal/lgstOpenApi/v1";
|
||||||
// private static final String TARGET_API = "http://localhost:48080/admin-api/databus/api/portal/testcbw/456";
|
// private static final String TARGET_API = "http://localhost:48080/admin-api/databus/api/portal/testcbw/456";
|
||||||
// ⚠️ 仅用于联调:信任所有证书 + 关闭主机名校验,生产环境请改为受信 CA 或自定义 truststore。
|
// ⚠️ 仅用于联调:信任所有证书 + 关闭主机名校验,生产环境请改为受信 CA 或自定义 truststore。
|
||||||
@@ -102,10 +102,16 @@ public final class DatabusApiInvocationExample {
|
|||||||
|
|
||||||
public static void main(String[] args) throws Exception {
|
public static void main(String[] args) throws Exception {
|
||||||
OUT.println("=== GET 请求示例 ===");
|
OUT.println("=== GET 请求示例 ===");
|
||||||
// executeGetExample();
|
executeGetExample();
|
||||||
// OUT.println();
|
// OUT.println();
|
||||||
// OUT.println("=== POST 请求示例 ===");
|
OUT.println("=== POST 请求示例 ===");
|
||||||
executePostExample();
|
executePostExample("""
|
||||||
|
{"operateFlag":"I","__interfaceType__":"R_MY_JY_03","data":{"endAddressName":"1","customerCompanyName":"中铜国贸","endAddressDetail":"测试地址","remark":" ","custSuppType":"1","shipperCompanyName":"中铜国贸","consigneeCorpCode":" ","consignerContactPhone":" 11","importFlag":"10","businessSupplierCode":" ","entrustMainCode":"WT3162251027027","endAddressCode":" ","specifyCarrierCorpCode":"10086689","materDetail":[{"detailStatus":"10","batchNo":"ZLTD2510ZTGM0017001","measureCodeMdm":"CU032110001","packType":" ","quantityPlanDetail":1,"deliveryOrderNo":"ZLTD2510ZTGM0017001","measureCode":"CU032110001","goodsSpecification":" ","measureUnitCode":"PAC","entrustDetailCode":"WT3162251027027001","brand":" ","soNumber":"68ecf0055502d565d22b378a"}],"operateFlag":1,"custSuppName":"上海锦生金属有限公司","startAddressCode":" ","planStartTime":1761556166000,"customerCompanyCode":0,"importMethod":"EXW","startAddressType":"10","shipperCompanyCode":"3162","deliverCondition":"20","businessSupplierName":" ","startAddressDetail":" 111","transType":"30","endAddressType":"20","planEndTime":1761556166000,"specifyCarrierCorpName":null,"custSuppFlag":"0101","businessType":"20","consigneeCorpName":" ","custSuppCode":"10086689","startAddressName":" 111","consignerContactName":" 11"},"datetime":"20251027170929","busiBillCode":"WT3162251027027","system":"BRMS","__requestId__":"f918841c-14fb-49eb-9640-c5d1b3d46bd1"}
|
||||||
|
""");
|
||||||
|
|
||||||
|
executePostExample("""
|
||||||
|
{"msgCode":"YWJYGK0003","data":"{\\"memberId\\":65352,\\"routes\\":[{\\"carrierCorpCode\\":\\"10193776\\",\\"carrierCorpName\\":\\"成都达海金属加工配送有限公司\\",\\"endAddressCode\\":\\"440000-440300\\",\\"endAddressDetail\\":\\"深圳港\\",\\"endAddressDetailDesc\\":\\"广东省深圳市盐田区深盐路\\",\\"endAddressLatitude\\":22.567426,\\"endAddressLongitude\\":114.283271,\\"endAddressName\\":\\"广东省-深圳市\\",\\"endAddressType\\":\\"port\\",\\"startAddressCode\\":\\"520000-0\\",\\"startAddressDetail\\":\\"安龙\\",\\"startAddressDetailDesc\\":\\"贵州省安龙县德卧镇坡告村\\",\\"startAddressLatitude\\":25.066532,\\"startAddressLongitude\\":105.244186,\\"startAddressName\\":\\"贵州省-null\\",\\"startAddressType\\":\\"railway-station\\",\\"taskEndTime\\":1766592000000,\\"taskStartTime\\":1766332800000,\\"transType\\":\\"10\\"},{\\"carrierCorpCode\\":\\"10193776\\",\\"carrierCorpName\\":\\"成都达海金属加工配送有限公司\\",\\"endAddressCode\\":\\"230000-230600\\",\\"endAddressDetail\\":\\"大庆东\\",\\"endAddressDetailDesc\\":\\"黑龙江省大庆市龙凤区凤一路28号\\",\\"endAddressLatitude\\":46.544097,\\"endAddressLongitude\\":125.118902,\\"endAddressName\\":\\"黑龙江省-大庆市\\",\\"endAddressType\\":\\"railway-station\\",\\"startAddressCode\\":\\"440000-440300\\",\\"startAddressDetail\\":\\"深圳港\\",\\"startAddressDetailDesc\\":\\"广东省深圳市盐田区深盐路\\",\\"startAddressLatitude\\":22.567426,\\"startAddressLongitude\\":114.283271,\\"startAddressName\\":\\"广东省-深圳市\\",\\"startAddressType\\":\\"port\\",\\"taskEndTime\\":1767110400000,\\"taskStartTime\\":1766592000000,\\"transType\\":\\"30\\"},{\\"carrierCorpCode\\":\\"10193776\\",\\"carrierCorpName\\":\\"成都达海金属加工配送有限公司\\",\\"endAddressCode\\":\\"520000-0\\",\\"endAddressDetail\\":\\"郑屯\\",\\"endAddressDetailDesc\\":\\"贵州省郑屯镇\\",\\"endAddressName\\":\\"贵州省-null\\",\\"endAddressType\\":\\"railway-station\\",\\"startAddressCode\\":\\"230000-230600\\",\\"startAddressDetail\\":\\"大庆东\\",\\"startAddressDetailDesc\\":\\"黑龙江省大庆市龙凤区凤一路28号\\",\\"startAddressLatitude\\":46.544097,\\"startAddressLongitude\\":125.118902,\\"startAddressName\\":\\"黑龙江省-大庆市\\",\\"startAddressType\\":\\"railway-station\\",\\"taskEndTime\\":1768320000000,\\"taskStartTime\\":1767110400000,\\"transType\\":\\"20\\"}],\\"taskLineNumber\\":\\"CT202512230001_001\\",\\"taskNumber\\":\\"CT202512230001\\"}"}
|
||||||
|
""");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void executeGetExample() throws Exception {
|
private static void executeGetExample() throws Exception {
|
||||||
@@ -113,9 +119,11 @@ public final class DatabusApiInvocationExample {
|
|||||||
queryParams.put("businessCode", "11");
|
queryParams.put("businessCode", "11");
|
||||||
queryParams.put("fileId", "11");
|
queryParams.put("fileId", "11");
|
||||||
queryParams.put("null", null);
|
queryParams.put("null", null);
|
||||||
|
queryParams.put("empty", "");
|
||||||
|
queryParams.put("taskTimeEnd", "2025-12-28 23:00:00");
|
||||||
String signature = generateSignature(queryParams, Map.of());
|
String signature = generateSignature(queryParams, Map.of());
|
||||||
URI requestUri = buildUri(TARGET_API, queryParams);
|
URI requestUri = buildUri(TARGET_API, queryParams);
|
||||||
String nonce = "171615676c7d4d96b9f55f3d90ad27e0";
|
String nonce = randomNonce();
|
||||||
|
|
||||||
HttpRequest request = HttpRequest.newBuilder(requestUri)
|
HttpRequest request = HttpRequest.newBuilder(requestUri)
|
||||||
.timeout(Duration.ofSeconds(10))
|
.timeout(Duration.ofSeconds(10))
|
||||||
@@ -131,16 +139,14 @@ public final class DatabusApiInvocationExample {
|
|||||||
printResponse(response);
|
printResponse(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void executePostExample() throws Exception {
|
private static void executePostExample(String json) throws Exception {
|
||||||
Map<String, Object> queryParams = new LinkedHashMap<>();
|
Map<String, Object> queryParams = new LinkedHashMap<>();
|
||||||
|
|
||||||
long extraTimestamp = 1761556157185L;
|
long extraTimestamp = 1761556157185L;
|
||||||
// String bodyJson = String.format("""
|
// String bodyJson = String.json("""
|
||||||
// {"operateFlag":"I","__interfaceType__":"R_MY_JY_03","data":{"endAddressName":"1","customerCompanyName":"中铜国贸","endAddressDetail":"测试地址","remark":" ","custSuppType":"1","shipperCompanyName":"中铜国贸","consigneeCorpCode":" ","consignerContactPhone":" 11","importFlag":"10","businessSupplierCode":" ","entrustMainCode":"WT3162251027027","endAddressCode":" ","specifyCarrierCorpCode":"10086689","materDetail":[{"detailStatus":"10","batchNo":"ZLTD2510ZTGM0017001","measureCodeMdm":"CU032110001","packType":" ","quantityPlanDetail":1,"deliveryOrderNo":"ZLTD2510ZTGM0017001","measureCode":"CU032110001","goodsSpecification":" ","measureUnitCode":"PAC","entrustDetailCode":"WT3162251027027001","brand":" ","soNumber":"68ecf0055502d565d22b378a"}],"operateFlag":1,"custSuppName":"上海锦生金属有限公司","startAddressCode":" ","planStartTime":1761556166000,"customerCompanyCode":0,"importMethod":"EXW","startAddressType":"10","shipperCompanyCode":"3162","deliverCondition":"20","businessSupplierName":" ","startAddressDetail":" 111","transType":"30","endAddressType":"20","planEndTime":1761556166000,"specifyCarrierCorpName":null,"custSuppFlag":"0101","businessType":"20","consigneeCorpName":" ","custSuppCode":"10086689","startAddressName":" 111","consignerContactName":" 11"},"datetime":"20251027170929","busiBillCode":"WT3162251027027","system":"BRMS","__requestId__":"f918841c-14fb-49eb-9640-c5d1b3d46bd1"}
|
// {"operateFlag":"I","__interfaceType__":"R_MY_JY_03","data":{"endAddressName":"1","customerCompanyName":"中铜国贸","endAddressDetail":"测试地址","remark":" ","custSuppType":"1","shipperCompanyName":"中铜国贸","consigneeCorpCode":" ","consignerContactPhone":" 11","importFlag":"10","businessSupplierCode":" ","entrustMainCode":"WT3162251027027","endAddressCode":" ","specifyCarrierCorpCode":"10086689","materDetail":[{"detailStatus":"10","batchNo":"ZLTD2510ZTGM0017001","measureCodeMdm":"CU032110001","packType":" ","quantityPlanDetail":1,"deliveryOrderNo":"ZLTD2510ZTGM0017001","measureCode":"CU032110001","goodsSpecification":" ","measureUnitCode":"PAC","entrustDetailCode":"WT3162251027027001","brand":" ","soNumber":"68ecf0055502d565d22b378a"}],"operateFlag":1,"custSuppName":"上海锦生金属有限公司","startAddressCode":" ","planStartTime":1761556166000,"customerCompanyCode":0,"importMethod":"EXW","startAddressType":"10","shipperCompanyCode":"3162","deliverCondition":"20","businessSupplierName":" ","startAddressDetail":" 111","transType":"30","endAddressType":"20","planEndTime":1761556166000,"specifyCarrierCorpName":null,"custSuppFlag":"0101","businessType":"20","consigneeCorpName":" ","custSuppCode":"10086689","startAddressName":" 111","consignerContactName":" 11"},"datetime":"20251027170929","busiBillCode":"WT3162251027027","system":"BRMS","__requestId__":"f918841c-14fb-49eb-9640-c5d1b3d46bd1"}
|
||||||
// """, extraTimestamp);
|
// """, extraTimestamp);
|
||||||
String bodyJson = String.format("""
|
String bodyJson = String.format(json, extraTimestamp);
|
||||||
{}
|
|
||||||
""", extraTimestamp);
|
|
||||||
|
|
||||||
Map<String, Object> bodyParams = parseBodyJson(bodyJson);
|
Map<String, Object> bodyParams = parseBodyJson(bodyJson);
|
||||||
String signature = generateSignature(queryParams, bodyParams);
|
String signature = generateSignature(queryParams, bodyParams);
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ package com.zt.plat.module.infra.service.file;
|
|||||||
|
|
||||||
import cn.hutool.core.io.resource.ResourceUtil;
|
import cn.hutool.core.io.resource.ResourceUtil;
|
||||||
import cn.hutool.core.util.IdUtil;
|
import cn.hutool.core.util.IdUtil;
|
||||||
|
import com.google.common.cache.CacheLoader;
|
||||||
|
import com.google.common.cache.LoadingCache;
|
||||||
import com.zt.plat.framework.common.pojo.PageResult;
|
import com.zt.plat.framework.common.pojo.PageResult;
|
||||||
import com.zt.plat.framework.common.util.json.JsonUtils;
|
import com.zt.plat.framework.common.util.json.JsonUtils;
|
||||||
import com.zt.plat.framework.common.util.validation.ValidationUtils;
|
import com.zt.plat.framework.common.util.validation.ValidationUtils;
|
||||||
@@ -14,8 +16,6 @@ import com.zt.plat.module.infra.framework.file.core.client.FileClient;
|
|||||||
import com.zt.plat.module.infra.framework.file.core.client.FileClientConfig;
|
import com.zt.plat.module.infra.framework.file.core.client.FileClientConfig;
|
||||||
import com.zt.plat.module.infra.framework.file.core.client.FileClientFactory;
|
import com.zt.plat.module.infra.framework.file.core.client.FileClientFactory;
|
||||||
import com.zt.plat.module.infra.framework.file.core.enums.FileStorageEnum;
|
import com.zt.plat.module.infra.framework.file.core.enums.FileStorageEnum;
|
||||||
import com.google.common.cache.CacheLoader;
|
|
||||||
import com.google.common.cache.LoadingCache;
|
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import jakarta.validation.Validator;
|
import jakarta.validation.Validator;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
@@ -172,7 +172,7 @@ public class FileConfigServiceImpl implements FileConfigService {
|
|||||||
// 校验存在
|
// 校验存在
|
||||||
validateFileConfigExists(id);
|
validateFileConfigExists(id);
|
||||||
// 上传文件
|
// 上传文件
|
||||||
byte[] content = ResourceUtil.readBytes("file/erweima.jpg");
|
byte[] content = ResourceUtil.readBytes("file/bg1.png");
|
||||||
return getFileClient(id).upload(content, IdUtil.fastSimpleUUID() + ".jpg", "image/jpeg");
|
return getFileClient(id).upload(content, IdUtil.fastSimpleUUID() + ".jpg", "image/jpeg");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 6.4 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 18 KiB |
@@ -41,7 +41,7 @@ public class FtpFileClientTest {
|
|||||||
client.init();
|
client.init();
|
||||||
// 上传文件
|
// 上传文件
|
||||||
String path = IdUtil.fastSimpleUUID() + ".jpg";
|
String path = IdUtil.fastSimpleUUID() + ".jpg";
|
||||||
byte[] content = ResourceUtil.readBytes("file/erweima.jpg");
|
byte[] content = ResourceUtil.readBytes("file/bg1.png");
|
||||||
String fullPath = client.upload(content, path, "image/jpeg");
|
String fullPath = client.upload(content, path, "image/jpeg");
|
||||||
System.out.println("访问地址:" + fullPath);
|
System.out.println("访问地址:" + fullPath);
|
||||||
if (false) {
|
if (false) {
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ public class LocalFileClientTest {
|
|||||||
client.init();
|
client.init();
|
||||||
// 上传文件
|
// 上传文件
|
||||||
String path = IdUtil.fastSimpleUUID() + ".jpg";
|
String path = IdUtil.fastSimpleUUID() + ".jpg";
|
||||||
byte[] content = ResourceUtil.readBytes("file/erweima.jpg");
|
byte[] content = ResourceUtil.readBytes("file/bg1.png");
|
||||||
String fullPath = client.upload(content, path, "image/jpeg");
|
String fullPath = client.upload(content, path, "image/jpeg");
|
||||||
System.out.println("访问地址:" + fullPath);
|
System.out.println("访问地址:" + fullPath);
|
||||||
client.delete(path);
|
client.delete(path);
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ public class S3FileClientTest {
|
|||||||
client.init();
|
client.init();
|
||||||
// 上传文件
|
// 上传文件
|
||||||
String path = IdUtil.fastSimpleUUID() + ".jpg";
|
String path = IdUtil.fastSimpleUUID() + ".jpg";
|
||||||
byte[] content = ResourceUtil.readBytes("file/erweima.jpg");
|
byte[] content = ResourceUtil.readBytes("file/bg1.png");
|
||||||
String fullPath = client.upload(content, path, "image/jpeg");
|
String fullPath = client.upload(content, path, "image/jpeg");
|
||||||
System.out.println("访问地址:" + fullPath);
|
System.out.println("访问地址:" + fullPath);
|
||||||
// 读取文件
|
// 读取文件
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ public class SftpFileClientTest {
|
|||||||
client.init();
|
client.init();
|
||||||
// 上传文件
|
// 上传文件
|
||||||
String path = IdUtil.fastSimpleUUID() + ".jpg";
|
String path = IdUtil.fastSimpleUUID() + ".jpg";
|
||||||
byte[] content = ResourceUtil.readBytes("file/erweima.jpg");
|
byte[] content = ResourceUtil.readBytes("file/bg1.png");
|
||||||
String fullPath = client.upload(content, path, "image/jpeg");
|
String fullPath = client.upload(content, path, "image/jpeg");
|
||||||
System.out.println("访问地址:" + fullPath);
|
System.out.println("访问地址:" + fullPath);
|
||||||
if (false) {
|
if (false) {
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ public class FileServiceImplTest extends BaseDbUnitTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testCreateFile_success_01() throws Exception {
|
public void testCreateFile_success_01() throws Exception {
|
||||||
// 准备参数
|
// 准备参数
|
||||||
byte[] content = ResourceUtil.readBytes("file/erweima.jpg");
|
byte[] content = ResourceUtil.readBytes("file/bg1.png");
|
||||||
String name = "单测文件名";
|
String name = "单测文件名";
|
||||||
String directory = randomString();
|
String directory = randomString();
|
||||||
String type = "image/jpeg";
|
String type = "image/jpeg";
|
||||||
@@ -122,7 +122,7 @@ public class FileServiceImplTest extends BaseDbUnitTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testCreateFile_success_02() throws Exception {
|
public void testCreateFile_success_02() throws Exception {
|
||||||
// 准备参数
|
// 准备参数
|
||||||
byte[] content = ResourceUtil.readBytes("file/erweima.jpg");
|
byte[] content = ResourceUtil.readBytes("file/bg1.png");
|
||||||
// mock Master 文件客户端
|
// mock Master 文件客户端
|
||||||
String type = "image/jpeg";
|
String type = "image/jpeg";
|
||||||
FileClient client = mock(FileClient.class);
|
FileClient client = mock(FileClient.class);
|
||||||
@@ -318,7 +318,7 @@ public class FileServiceImplTest extends BaseDbUnitTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testCreateFile_withSameHash() throws Exception {
|
public void testCreateFile_withSameHash() throws Exception {
|
||||||
// 准备参数
|
// 准备参数
|
||||||
byte[] content = ResourceUtil.readBytes("file/erweima.jpg");
|
byte[] content = ResourceUtil.readBytes("file/bg1.png");
|
||||||
String name = "单测文件名";
|
String name = "单测文件名";
|
||||||
String directory = randomString();
|
String directory = randomString();
|
||||||
String type = "image/jpeg";
|
String type = "image/jpeg";
|
||||||
|
|||||||
Reference in New Issue
Block a user