Merge branch 'refs/heads/zt-test' into test

# Conflicts:
#	zt-module-infra/zt-module-infra-server/src/main/resources/application.yaml
This commit is contained in:
FCL
2026-01-04 15:19:35 +08:00
119 changed files with 4265 additions and 184 deletions

View File

@@ -0,0 +1,71 @@
package com.zt.plat.module.databus.api.provider;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.module.databus.api.data.DatabusUserDeptData;
import com.zt.plat.module.databus.api.dto.CursorPageReqDTO;
import com.zt.plat.module.databus.api.dto.CursorPageResult;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* Databus 用户-部门关系数据提供者 API
* <p>
* 供 Databus 调用,获取用户-部门关联数据用于全量/增量同步
*
* @author ZT
*/
@FeignClient(name = "${databus.provider.user-dept.service:system-server}")
@Tag(name = "RPC 服务 - Databus 用户-部门关系数据提供者")
public interface DatabusUserDeptProviderApi {
String PREFIX = "/rpc/databus/user-dept";
/**
* 游标分页查询用户-部门关系数据(用于全量同步)
*
* @param reqDTO 游标分页请求
* @return 用户-部门关系数据分页结果
*/
@PostMapping(PREFIX + "/page-by-cursor")
@Operation(summary = "游标分页查询用户-部门关系数据")
CommonResult<CursorPageResult<DatabusUserDeptData>> getPageByCursor(@RequestBody CursorPageReqDTO reqDTO);
/**
* 根据ID查询用户-部门关系详情(用于增量同步)
*
* @param id 关系ID
* @return 用户-部门关系数据
*/
@GetMapping(PREFIX + "/get")
@Operation(summary = "查询用户-部门关系详情")
@Parameter(name = "id", description = "关系ID", required = true, example = "1001")
CommonResult<DatabusUserDeptData> getById(@RequestParam("id") Long id);
/**
* 批量查询用户-部门关系详情(用于增量同步批量获取)
*
* @param ids 关系ID列表
* @return 用户-部门关系数据列表
*/
@GetMapping(PREFIX + "/list")
@Operation(summary = "批量查询用户-部门关系详情")
@Parameter(name = "ids", description = "关系ID列表", required = true, example = "1001,1002,1003")
CommonResult<List<DatabusUserDeptData>> getListByIds(@RequestParam("ids") List<Long> ids);
/**
* 统计用户-部门关系总数(用于全量同步进度计算)
*
* @param tenantId 租户ID可选
* @return 用户-部门关系总数
*/
@GetMapping(PREFIX + "/count")
@Operation(summary = "统计用户-部门关系总数")
@Parameter(name = "tenantId", description = "租户ID", example = "1")
CommonResult<Long> count(@RequestParam(value = "tenantId", required = false) Long tenantId);
}

View File

@@ -0,0 +1,71 @@
package com.zt.plat.module.databus.api.provider;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.module.databus.api.data.DatabusUserPostData;
import com.zt.plat.module.databus.api.dto.CursorPageReqDTO;
import com.zt.plat.module.databus.api.dto.CursorPageResult;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* Databus 用户-岗位关系数据提供者 API
* <p>
* 供 Databus 调用,获取用户-岗位关联数据用于全量/增量同步
*
* @author ZT
*/
@FeignClient(name = "${databus.provider.user-post.service:system-server}")
@Tag(name = "RPC 服务 - Databus 用户-岗位关系数据提供者")
public interface DatabusUserPostProviderApi {
String PREFIX = "/rpc/databus/user-post";
/**
* 游标分页查询用户-岗位关系数据(用于全量同步)
*
* @param reqDTO 游标分页请求
* @return 用户-岗位关系数据分页结果
*/
@PostMapping(PREFIX + "/page-by-cursor")
@Operation(summary = "游标分页查询用户-岗位关系数据")
CommonResult<CursorPageResult<DatabusUserPostData>> getPageByCursor(@RequestBody CursorPageReqDTO reqDTO);
/**
* 根据ID查询用户-岗位关系详情(用于增量同步)
*
* @param id 关系ID
* @return 用户-岗位关系数据
*/
@GetMapping(PREFIX + "/get")
@Operation(summary = "查询用户-岗位关系详情")
@Parameter(name = "id", description = "关系ID", required = true, example = "1001")
CommonResult<DatabusUserPostData> getById(@RequestParam("id") Long id);
/**
* 批量查询用户-岗位关系详情(用于增量同步批量获取)
*
* @param ids 关系ID列表
* @return 用户-岗位关系数据列表
*/
@GetMapping(PREFIX + "/list")
@Operation(summary = "批量查询用户-岗位关系详情")
@Parameter(name = "ids", description = "关系ID列表", required = true, example = "1001,1002,1003")
CommonResult<List<DatabusUserPostData>> getListByIds(@RequestParam("ids") List<Long> ids);
/**
* 统计用户-岗位关系总数(用于全量同步进度计算)
*
* @param tenantId 租户ID可选
* @return 用户-岗位关系总数
*/
@GetMapping(PREFIX + "/count")
@Operation(summary = "统计用户-岗位关系总数")
@Parameter(name = "tenantId", description = "租户ID", example = "1")
CommonResult<Long> count(@RequestParam(value = "tenantId", required = false) Long tenantId);
}

View File

@@ -185,6 +185,11 @@
<version>4.12.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.zt.plat</groupId>
<artifactId>zt-spring-boot-starter-databus-client</artifactId>
<version>${revision}</version>
</dependency>
</dependencies>
<build>

View File

@@ -33,6 +33,7 @@ import org.springframework.web.util.ContentCachingResponseWrapper;
import org.springframework.web.util.UriComponentsBuilder;
import java.io.IOException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
@@ -304,15 +305,28 @@ public class GatewaySecurityFilter extends OncePerRequestFilter {
.build()
.getQueryParams();
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;
}
if (CollectionUtils.isEmpty(values)) {
target.put(key, "");
} else if (values.size() == 1) {
target.put(key, values.get(0));
target.put(decodedKey, "");
return;
}
// 对每一个 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 {
target.put(key, String.join(",", values));
target.put(decodedKey, String.join(",", decodedValues));
}
});
} catch (IllegalArgumentException ex) {

View File

@@ -4,9 +4,13 @@ import com.zt.plat.framework.common.biz.system.oauth2.OAuth2TokenCommonApi;
import com.zt.plat.module.databus.api.provider.DatabusDeptProviderApi;
import com.zt.plat.module.databus.api.provider.DatabusPostProviderApi;
import com.zt.plat.module.databus.api.provider.DatabusUserProviderApi;
import com.zt.plat.module.databus.api.provider.DatabusUserDeptProviderApi;
import com.zt.plat.module.databus.api.provider.DatabusUserPostProviderApi;
import com.zt.plat.module.system.api.dept.DeptApi;
import com.zt.plat.module.system.api.dept.PostApi;
import com.zt.plat.module.system.api.user.AdminUserApi;
import com.zt.plat.module.system.api.userdept.UserDeptApi;
import com.zt.plat.module.system.api.userpost.UserPostApi;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Configuration;
@@ -21,9 +25,12 @@ import org.springframework.context.annotation.Configuration;
DatabusDeptProviderApi.class,
DatabusUserProviderApi.class,
DatabusPostProviderApi.class,
PostApi.class,
DeptApi.class,
AdminUserApi.class,
DatabusUserDeptProviderApi.class,
DatabusUserPostProviderApi.class,
PostApi.class,
DeptApi.class,
UserDeptApi.class,
UserPostApi.class,
})
public class RpcConfiguration {
}

View File

@@ -5,6 +5,10 @@ import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
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.PrintStream;
import java.net.URI;
@@ -23,10 +27,6 @@ import java.util.LinkedHashMap;
import java.util.Map;
import java.util.TreeMap;
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。
@@ -37,14 +37,14 @@ public final class DatabusApiInvocationExample {
// private static final String APP_ID = "iwork";
// private static final String APP_SECRET = "lpGXiNe/GMLk0vsbYGLa8eYxXq8tGhTbuu3/D4MJzIk=";
private static final String APP_ID = "ztmy";
private static final String APP_SECRET = "zFre/nTRGi7LpoFjN7oQkKeOT09x1fWTyIswrc702QQ=";
private static final String APP_ID = "jwyw";
private static final String APP_SECRET = "MhfCcqB59rDTnB5yGOVXWtp/5a0JXir7pSjPl5cVMJ8=";
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.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 = "http://localhost:48080/admin-api/databus/api/portal/callback/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/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/testcbw/456";
// ⚠️ 仅用于联调:信任所有证书 + 关闭主机名校验,生产环境请改为受信 CA 或自定义 truststore。
@@ -102,10 +102,16 @@ public final class DatabusApiInvocationExample {
public static void main(String[] args) throws Exception {
OUT.println("=== GET 请求示例 ===");
// executeGetExample();
executeGetExample();
// OUT.println();
// OUT.println("=== POST 请求示例 ===");
executePostExample();
OUT.println("=== POST 请求示例 ===");
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 {
@@ -113,9 +119,11 @@ public final class DatabusApiInvocationExample {
queryParams.put("businessCode", "11");
queryParams.put("fileId", "11");
queryParams.put("null", null);
queryParams.put("empty", "");
queryParams.put("taskTimeEnd", "2025-12-28 23:00:00");
String signature = generateSignature(queryParams, Map.of());
URI requestUri = buildUri(TARGET_API, queryParams);
String nonce = "171615676c7d4d96b9f55f3d90ad27e0";
String nonce = randomNonce();
HttpRequest request = HttpRequest.newBuilder(requestUri)
.timeout(Duration.ofSeconds(10))
@@ -131,16 +139,14 @@ public final class DatabusApiInvocationExample {
printResponse(response);
}
private static void executePostExample() throws Exception {
private static void executePostExample(String json) throws Exception {
Map<String, Object> queryParams = new LinkedHashMap<>();
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"}
// """, extraTimestamp);
String bodyJson = String.format("""
{}
""", extraTimestamp);
String bodyJson = String.format(json, extraTimestamp);
Map<String, Object> bodyParams = parseBodyJson(bodyJson);
String signature = generateSignature(queryParams, bodyParams);