feat:正元商户接口

This commit is contained in:
dzc committed 2025-02-28 20:09:49 +08:00
1 parent 49056d4563
commit cd99c104c3
12 files changed
+461 -35

No files matched your search

@@ -133,4 +133,10 @@ public interface CacheConstants {
String WALLET_SETTING_DETAIL = "wallet:detail:setting";
/* ************************************* end 钱包相关 end ************************************* */
/* ************************************* start 第三方对接相关 start ************************************* */
String THIRDPARTY_ZY_MER_TOKEN = "thirdparty:zy:mer_token";
/* ************************************* end 第三方对接相关 end ************************************* */
}
+1 -1
View File
@@ -42,5 +42,5 @@
<groupId>com.bsd</groupId>
<artifactId>pjcloud-common-excel</artifactId>
</dependency>
</dependencies>
</dependencies>
</project>
@@ -1,11 +1,17 @@
package com.bsd.pjcloud.thirdparty.dto.zy.mer;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 商户信息同步
*/
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Data
public class ZyMerSyncDealerInfoDto {
/**
@@ -21,7 +27,7 @@ public class ZyMerSyncDealerInfoDto {
/**
* 商户是否有效0:禁用 1:有效
*/
private String IsValid;
private Integer IsValid;
/**
* 所属记账单位编号
@@ -1,10 +1,16 @@
package com.bsd.pjcloud.thirdparty.dto.zy.mer;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 记账单位同步
*/
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Data
public class ZyMerSyncEnterpriseInfoDto {
@@ -1,12 +1,19 @@
package com.bsd.pjcloud.thirdparty.vo.zy;
import cn.hutool.core.util.ObjectUtil;
import lombok.Data;
@Data
public class ZyBaseVo {
public static String successCode = "0";
private Integer code;
private String msg;
public boolean isSuccess() {
return ObjectUtil.equal(code, ZyBaseVo.successCode);
}
}
+8 -7
View File
@@ -76,13 +76,13 @@
<!--选配: mybatis 依赖 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
</dependency>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
</dependency>
<!--选配: druid 连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-3-starter</artifactId>
</dependency>
<artifactId>druid-spring-boot-3-starter</artifactId>
</dependency>
<!--选配: mysql 数据库驱动 -->
<dependency>
<groupId>com.mysql</groupId>
@@ -97,8 +97,9 @@
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- 引入分布式事务-->
<!--引入分布式事务-->
<dependency>
<groupId>com.bsd</groupId>
<artifactId>pjcloud-common-seata</artifactId>
@@ -115,7 +116,7 @@
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<executions>
<execution>
<goals>
<goal>repackage</goal>
@@ -125,7 +126,7 @@
</configuration>
</execution>
</executions>
</plugin>
</plugin>
<plugin>
<groupId>io.fabric8</groupId>
<artifactId>docker-maven-plugin</artifactId>
@@ -0,0 +1,19 @@
package com.bsd.pjcloud.thirdparty.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties(prefix = "zy")
@Data
public class ZyConfig {
private String baseUrl;
private String appId;
private String appSecret;
}
@@ -1,4 +1,36 @@
package com.bsd.pjcloud.thirdparty.service.impl;
import com.bsd.pjcloud.thirdparty.dto.zy.mer.ZyMerSyncAccClassInfoDto;
import com.bsd.pjcloud.thirdparty.dto.zy.mer.ZyMerSyncAccountInfoDto;
import com.bsd.pjcloud.thirdparty.dto.zy.mer.ZyMerSyncDealerInfoDto;
import com.bsd.pjcloud.thirdparty.dto.zy.mer.ZyMerSyncEnterpriseInfoDto;
import com.bsd.pjcloud.thirdparty.vo.zy.mer.*;
public interface ZyMerService {
/**
* 获取token
*/
ZyMerGetTokenVo getTokenApi();
/**
* 商户信息同步
*/
ZyMerSyncDealerInfoVo syncDealerInfoApi(ZyMerSyncDealerInfoDto dto);
/**
* 记账单位同步
*/
ZyMerSyncEnterpriseInfoVo syncEnterpriseInfoApi(ZyMerSyncEnterpriseInfoDto dto);
/**
* 账户信息同步
*/
ZyMerSyncAccountInfoVo syncAccountInfoApi(ZyMerSyncAccountInfoDto dto);
/**
* 身份信息同步
*/
ZyMerSyncAccClassInfoVo syncAccClassInfo(ZyMerSyncAccClassInfoDto dto);
}
@@ -1,14 +1,214 @@
package com.bsd.pjcloud.thirdparty.service.impl.zy;
import cn.hutool.core.lang.Pair;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.json.JSONUtil;
import com.bsd.pjcloud.common.core.constant.CacheConstants;
import com.bsd.pjcloud.thirdparty.config.ZyConfig;
import com.bsd.pjcloud.thirdparty.dto.zy.mer.ZyMerSyncAccClassInfoDto;
import com.bsd.pjcloud.thirdparty.dto.zy.mer.ZyMerSyncAccountInfoDto;
import com.bsd.pjcloud.thirdparty.dto.zy.mer.ZyMerSyncDealerInfoDto;
import com.bsd.pjcloud.thirdparty.dto.zy.mer.ZyMerSyncEnterpriseInfoDto;
import com.bsd.pjcloud.thirdparty.service.impl.ZyMerService;
import com.bsd.pjcloud.thirdparty.utils.zy.api.ZyApi;
import com.bsd.pjcloud.thirdparty.vo.zy.mer.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import static org.apache.commons.codec.digest.DigestUtils.md5Hex;
@Service
@Validated
@Slf4j
@RequiredArgsConstructor
public class ZyMerServiceImpl implements ZyMerService {
private final String TAG = "ZyMerServiceImpl";
private final ZyConfig zyConfig;
private final StringRedisTemplate redisTemplate;
/**
* 获取token
*
* @return
*/
@Override
public ZyMerGetTokenVo getTokenApi() {
String url = zyConfig.getBaseUrl() + ZyApi.Mer.GET_TOKEN;
url = String.format(url, zyConfig.getAppId(), zyConfig.getAppSecret());
try {
String res = HttpRequest.get(url)
.contentType("application/x-www-form-urlencoded")
.execute()
.body();
if (ObjectUtil.isEmpty(res)) {
throw new RuntimeException("response is empty");
}
ZyMerGetTokenVo vo = JSONUtil.toBean(res, ZyMerGetTokenVo.class);
if (!vo.isSuccess()) {
throw new RuntimeException(String.format("请求错误res|%s|url|%s|", res, url));
}
return vo;
} catch (Exception e) {
log.error("|{}||getTokenApi|意外的错误error", e.getMessage());
}
return null;
}
/**
* 商户信息同步
*/
@Override
public ZyMerSyncDealerInfoVo syncDealerInfoApi(ZyMerSyncDealerInfoDto dto) {
String url = zyConfig.getBaseUrl() + ZyApi.Mer.SYNC_DEALER_INFO;
try {
url = String.format(url, getToken());
Map<String, Object> encryptionBody = new HashMap<>();
encryptionBody.put("dealers", dto);
String body = getEncryptionDto(encryptionBody);
String res = HttpRequest.post(url)
.body(body)
.execute()
.body();
if (ObjectUtil.isEmpty(res)) {
throw new RuntimeException("response is empty");
}
ZyMerSyncDealerInfoVo vo = JSONUtil.toBean(res, ZyMerSyncDealerInfoVo.class);
if (!vo.isSuccess()) {
throw new RuntimeException(String.format("请求错误res|%s|url|%s|body|%s|", res, url, body));
}
return vo;
} catch (Exception e) {
log.error("|{}||syncDealerInfo|意外的错误error", e.getMessage());
}
return null;
}
/**
* 记账单位同步
*/
@Override
public ZyMerSyncEnterpriseInfoVo syncEnterpriseInfoApi(ZyMerSyncEnterpriseInfoDto dto) {
String url = zyConfig.getBaseUrl() + ZyApi.Mer.SYNC_ENTERPRISE_INFO;
try {
url = String.format(url, getToken());
Map<String, Object> encryptionBody = new HashMap<>();
encryptionBody.put("enterprises", dto);
String body = getEncryptionDto(encryptionBody);
String res = HttpRequest.post(url)
.body(body)
.execute()
.body();
if (ObjectUtil.isEmpty(res)) {
throw new RuntimeException("response is empty");
}
ZyMerSyncEnterpriseInfoVo vo = JSONUtil.toBean(res, ZyMerSyncEnterpriseInfoVo.class);
if (!vo.isSuccess()) {
throw new RuntimeException(String.format("请求错误res|%s|url|%s|body|%s|", res, url, body));
}
return vo;
} catch (Exception e) {
log.error("|{}||syncEnterpriseInfo|意外的错误error", e.getMessage());
}
return null;
}
/**
* 账户信息同步
*/
@Override
public ZyMerSyncAccountInfoVo syncAccountInfoApi(ZyMerSyncAccountInfoDto dto) {
String url = zyConfig.getBaseUrl() + ZyApi.Mer.SYNC_ACCOUNT_INFO;
try {
url = String.format(url, getToken());
Map<String, Object> encryptionBody = new HashMap<>();
encryptionBody.put("accounts", dto);
String body = getEncryptionDto(encryptionBody);
String res = HttpRequest.post(url)
.body(body)
.execute()
.body();
if (ObjectUtil.isEmpty(res)) {
throw new RuntimeException("response is empty");
}
ZyMerSyncAccountInfoVo vo = JSONUtil.toBean(res, ZyMerSyncAccountInfoVo.class);
if (!vo.isSuccess()) {
throw new RuntimeException(String.format("请求错误res|%s|url|%s|body|%s|", res, url, body));
}
return vo;
} catch (Exception e) {
log.error("|{}||syncAccountInfo|意外的错误error", e.getMessage());
}
return null;
}
/**
* 身份信息同步
*/
@Override
public ZyMerSyncAccClassInfoVo syncAccClassInfo(ZyMerSyncAccClassInfoDto dto) {
String url = zyConfig.getBaseUrl() + ZyApi.Mer.SYNC_ACC_CLASS_INFO;
try {
url = String.format(url, getToken());
Map<String, Object> encryptionBody = new HashMap<>();
encryptionBody.put("accClasses", dto);
String body = getEncryptionDto(encryptionBody);
String res = HttpRequest.post(url)
.body(body)
.execute()
.body();
if (ObjectUtil.isEmpty(res)) {
throw new RuntimeException("response is empty");
}
ZyMerSyncAccClassInfoVo vo = JSONUtil.toBean(res, ZyMerSyncAccClassInfoVo.class);
if (!vo.isSuccess()) {
throw new RuntimeException(String.format("请求错误res|%s|url|%s|body|%s|", res, url, body));
}
return vo;
}catch (Exception e){
log.error("|{}||syncAccClassInfo|意外的错误error", e.getMessage());
}
return null;
}
private String getToken() {
try {
String redisKey = CacheConstants.THIRDPARTY_ZY_MER_TOKEN;
String token = redisTemplate.opsForValue().get(redisKey);
if (ObjectUtil.isEmpty(token)) {
ZyMerGetTokenVo vo = getTokenApi();
if (ObjectUtil.isNotEmpty(vo)) {
String accessToken = vo.getAccess_token();
redisTemplate.opsForValue().set(redisKey, accessToken);
return accessToken;
}
}
return token;
} catch (Exception e) {
log.error("|{}|token获取错误无法进行,意外的错误error", e.getMessage());
}
return null;
}
private <T> String getEncryptionDto(T params) {
String json = JSONUtil.toJsonStr(params);
String encodedData = Base64.getEncoder().encodeToString(json.getBytes(StandardCharsets.UTF_8));
String stringToSign = String.format("data=%s&key=%s", json, zyConfig.getAppId());
String sign = md5Hex(stringToSign);
Map<String, Object> body = new HashMap<>();
body.put("data", encodedData);
body.put("sign", sign);
return JSONUtil.toJsonStr(body);
}
}
@@ -5,48 +5,50 @@ import lombok.experimental.UtilityClass;
@UtilityClass
public class ZyApi {
// 商户信息同步
class Mer {
public static class Mer {
// 获取token
static final String GET_TOKEN = "/getToken?appId={appid}&appSecret={appsecret}";
public static final String GET_TOKEN = "/api/getToken?appId=%s&appSecret=%s";
// 商户信息同步
static final String SYNC_DEALER_INFO = "/syncDealerInfo?access_token={access_token}";
public static final String SYNC_DEALER_INFO = "/api/syncDealerInfo?access_token=%s";
// 记账单位同步
static final String SYNC_ENTERPRISE_INFO = "/syncEnterpriseInfo?access_token={access_token}";
public static final String SYNC_ENTERPRISE_INFO = "/api/syncEnterpriseInfo?access_token=%s";
// 账户信息同步
static final String SYNC_ACCOUNT_INFO = "/syncAccountInfo?access_token={access_token}";
public static final String SYNC_ACCOUNT_INFO = "/api/syncAccountInfo?access_token=%s";
// 身份信息同步
static final String SYNC_ACC_CLASS_INFO = "/syncAccClassInfo?access_token={access_token}";
public static final String SYNC_ACC_CLASS_INFO = "/api/syncAccClassInfo?access_token=%s";
}
// 统一身份验证功能
class Stride {
public class Stride {
// 统一身份验证功能
static final String AUTHENTICATION = "/authentication?access_token={access_token}";
public static final String AUTHENTICATION = "/authentication?access_token={access_token}";
// 智慧园区通刷平台提供消费接口
static final String CONSUMPTION = "/consumption?access_token={access_token}";
public static final String CONSUMPTION = "/consumption?access_token={access_token}";
// 智慧园区通刷平台提供冲正接口
static final String CORRECT = "/correct?access_token={access_token}";
public static final String CORRECT = "/correct?access_token={access_token}";
}
// 本地一卡通交易数据同步
class Foreign {
public static class Foreign {
// 本地一卡通交易数据同步
static final String SYNC_NATIVE_CARD_TRADE_INFO = "/getToken/syncNativeCardTradeInfo?access_token={access_token}";
public static final String SYNC_NATIVE_CARD_TRADE_INFO = "/getToken/syncNativeCardTradeInfo?access_token={access_token}";
// 获取区域编码
static final String GET_REGION_ID_BY_TOKEN = "/getRegionIdByToken?access_token={access_token}";
public static final String GET_REGION_ID_BY_TOKEN = "/getRegionIdByToken?access_token={access_token}";
}
// 第三方需提供的接口
class Self {
public static class Self {
// 第三方url健康探测
static final String CHECK_SYSTEM_STATUS = "/checkSystemStatus";
public static final String CHECK_SYSTEM_STATUS = "/checkSystemStatus";
// 第三方一卡通系统提供冲正接口
static final String CORRECT = "/correct?access_token={access_token}";
public static final String CORRECT = "/correct?access_token={access_token}";
// 第三方一卡通系统提供扣费接口
static final String PAY = "/pay?access_token={access_token}";
public static final String PAY = "/pay?access_token={access_token}";
// 第三方提供获取token
static final String GET_TOKEN = "/getToken?appId={appid}&appSecret={appsecret}_1730169197889";
public static final String GET_TOKEN = "/getToken?appId={appid}&appSecret={appsecret}_1730169197889";
}
}
@@ -40,11 +40,10 @@ oauth2:
shjn_token_url: /prod-api/login/shpj
# 浙证钉和诗画公用的获取用户信息接口
zzd_sh_user_info_url: /prod-api/oauth2/apis/sso/v1/fetch_user_by_uabase_token
# datasource:
# type: com.alibaba.druid.pool.DruidDataSource
# druid:
# driver-class-name: com.mysql.cj.jdbc.Driver
# username: root
# password: root
# # url: jdbc:mysql://pjcloud-mysql:3306/pjcloudx_thirdparty?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=GMT%2B8&allowMultiQueries=true
# url: jdbc:mysql://${MYSQL_HOST:pjcloud-mysql}:${MYSQL_PORT:3306}/${MYSQL_DB:pjcloudx_thirdparty}?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=GMT%2B8&allowMultiQueries=true
# 正元智慧
# https://gwyc.zj.gov.cn/API/%E7%9C%81%E5%B9%B3%E5%8F%B0API%E5%AF%B9%E6%8E%A5%E6%8E%A5%E5%8F%A3.html
zy:
base-url: http://60.191.37.212:18079
app-id: 03D9608D1735D424F984733BA71321C6
app-secret: 65497A86DB1401B252FEA6DE0A080766
@@ -0,0 +1,148 @@
package com.bsd.pjcloud.admin;
import cn.hutool.json.JSONUtil;
import com.bsd.pjcloud.thirdparty.ThirdPartyApp;
import com.bsd.pjcloud.thirdparty.dto.zy.mer.ZyMerSyncAccClassInfoDto;
import com.bsd.pjcloud.thirdparty.dto.zy.mer.ZyMerSyncAccountInfoDto;
import com.bsd.pjcloud.thirdparty.dto.zy.mer.ZyMerSyncDealerInfoDto;
import com.bsd.pjcloud.thirdparty.dto.zy.mer.ZyMerSyncEnterpriseInfoDto;
import com.bsd.pjcloud.thirdparty.service.impl.ZyMerService;
import com.bsd.pjcloud.thirdparty.vo.zy.mer.*;
import jakarta.annotation.Resource;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import java.security.MessageDigest;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
@SpringBootTest(classes = ThirdPartyApp.class)
public class ZyTest {
@Resource
private ZyMerService zyMerService;
/************************** start mer start **************************/
/**
* 获取token
*/
@Test
public void merGetTokenApi() {
ZyMerGetTokenVo vo = zyMerService.getTokenApi();
System.out.println(1);
}
/**
* 商户信息同步
*/
@Test
public void merSyncDealerInfo() {
ZyMerSyncDealerInfoDto dto = ZyMerSyncDealerInfoDto
.builder()
.DealerKeyObj("123")
.DealerName("食堂")
.IsValid(1)
.EPID("1")
.FixId("20190920100101")
.build();
ZyMerSyncDealerInfoVo vo = zyMerService.syncDealerInfoApi(dto);
System.out.println(vo);
}
/**
* 记账单位同步
*/
@Test
public void merSyncEnterpriseInfo(){
ZyMerSyncEnterpriseInfoDto dto = new ZyMerSyncEnterpriseInfoDto();
ZyMerSyncEnterpriseInfoVo vo = zyMerService.syncEnterpriseInfoApi(dto);
System.out.println(vo);
}
/**
* 账户信息同步
*/
@Test
public void merSyncAccountInfoApi(){
ZyMerSyncAccountInfoDto dto = new ZyMerSyncAccountInfoDto();
ZyMerSyncAccountInfoVo vo = zyMerService.syncAccountInfoApi(dto);
System.out.println(vo);
}
/**
* 身份信息同步
*/
@Test
public void merSyncAccClassInfo(){
ZyMerSyncAccClassInfoDto dto = new ZyMerSyncAccClassInfoDto();
ZyMerSyncAccClassInfoVo vo = zyMerService.syncAccClassInfo(dto);
System.out.println(vo);
}
/************************** end mer end **************************/
/************************** start mer start **************************/
/************************** end mer end **************************/
@Test
public void encryption() {
try {
// 1. 构建业务数据
Map<String, Object> dealer = new HashMap<>();
dealer.put("EPID", "1");
dealer.put("DealerKeyObj", "123");
dealer.put("IsValid", 1);
dealer.put("DealerName", "食堂");
dealer.put("FixId", "20190920100101");
Map<String, Object> data = new HashMap<>();
data.put("dealers", new Object[]{dealer});
// 2. 生成 data 参数(Base64 编码)
String jsonData = JSONUtil.toJsonStr(data);
String base64Data = Base64.getEncoder().encodeToString(jsonData.getBytes("UTF-8"));
// 3. 生成签名
String signStr = String.format("data=%s&key=%s", jsonData, "03D9608D1735D424F984733BA71321C6");
String sign = md5(signStr).toUpperCase();
// 4. 构建请求参数
Map<String, String> params = new HashMap<>();
params.put("data", base64Data);
params.put("sign", sign);
System.out.println(params);
} catch (Exception e) {
e.printStackTrace();
}
}
// MD5 加密方法
private static String md5(String input) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] messageDigest = md.digest(input.getBytes());
StringBuilder hexString = new StringBuilder();
for (byte b : messageDigest) {
String hex = Integer.toHexString(0xFF & b);
if (hex.length() == 1) {
hexString.append('0');
}
hexString.append(hex);
}
return hexString.toString();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}