commit dfcb8f95f76a716f915cb37ff253ea3d84b45bb7 Author: 钱涛 <15850646081@163.com> Date: Wed Mar 26 15:35:34 2025 +0800 初始化艾志OA仓库 diff --git a/resource/WEB-INF/CBS2ECConfig.xml b/resource/WEB-INF/CBS2ECConfig.xml new file mode 100644 index 0000000..e6bde94 --- /dev/null +++ b/resource/WEB-INF/CBS2ECConfig.xml @@ -0,0 +1,36 @@ + + + + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + + +
+ + +
diff --git a/resource/WEB-INF/config/MyBatis.xml b/resource/WEB-INF/config/MyBatis.xml new file mode 100644 index 0000000..31cc6f0 --- /dev/null +++ b/resource/WEB-INF/config/MyBatis.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/resource/WEB-INF/prop/hrmSalaryCBS8.properties b/resource/WEB-INF/prop/hrmSalaryCBS8.properties new file mode 100644 index 0000000..0207299 --- /dev/null +++ b/resource/WEB-INF/prop/hrmSalaryCBS8.properties @@ -0,0 +1,12 @@ +# ??ID +app_id=Yg8fWSvs +# ???? +app_secret=1f23768c02219d7864b0009c30a1b6a59fe84606 +# ???? +bodyEncryptionKey=0467E11F7CA86D884C990D4F3F5C2A1EFEEBB02B9878F4FDDCB7899DFABADCC8F38FC23F007A8AF9B2EB8A1D313959647CD8A1542F0414116AE6CFCA792346A802 +# ???????? +signEncryptionPrivateKey=135d6d61d5e820a979e58f4939abbd528c82528c8c276852445854377e1df247 +# ???????? +bodyDecryptionKey=135d6d61d5e820a979e58f4939abbd528c82528c8c276852445854377e1df247 +# ???? +host=https://cbs8-openapi-reprd.csuat.cmburl.cn \ No newline at end of file diff --git a/src/com/engine/salary/exception/CBS8RunTimeException.java b/src/com/engine/salary/exception/CBS8RunTimeException.java new file mode 100644 index 0000000..b9594e9 --- /dev/null +++ b/src/com/engine/salary/exception/CBS8RunTimeException.java @@ -0,0 +1,15 @@ +package com.engine.salary.exception; + +public class CBS8RunTimeException extends RuntimeException { + public CBS8RunTimeException(String message) { + super(message); + } + + public CBS8RunTimeException(Throwable cause) { + super(cause); + } + + public CBS8RunTimeException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/com/engine/salary/mapper/SQLMapper.java b/src/com/engine/salary/mapper/SQLMapper.java new file mode 100644 index 0000000..c3a7f81 --- /dev/null +++ b/src/com/engine/salary/mapper/SQLMapper.java @@ -0,0 +1,22 @@ +package com.engine.salary.mapper; + +import org.apache.ibatis.annotations.Param; + +import java.util.List; +import java.util.Map; + +/** + * SQLMapper + *

Copyright: Copyright (c) 2024

+ *

Company: 泛微软件

+ * + * @author qiantao + * @version 1.0 + **/ +public interface SQLMapper { + List runSQL(@Param("sql") String sql); + + List listLong(@Param("sql") String sql); + + List listString(@Param("sql") String sql); +} diff --git a/src/com/engine/salary/mapper/SQLMapper.xml b/src/com/engine/salary/mapper/SQLMapper.xml new file mode 100644 index 0000000..3f67dea --- /dev/null +++ b/src/com/engine/salary/mapper/SQLMapper.xml @@ -0,0 +1,16 @@ + + + + + + + + + + diff --git a/src/com/engine/salary/mapper/cbs/UfHkrdzbMapper.java b/src/com/engine/salary/mapper/cbs/UfHkrdzbMapper.java new file mode 100644 index 0000000..cfe5e56 --- /dev/null +++ b/src/com/engine/salary/mapper/cbs/UfHkrdzbMapper.java @@ -0,0 +1,57 @@ +package com.engine.salary.mapper.cbs; + +import com.engine.salary.remote.cbs8.po.UfHkrdzbPO; + +import java.util.List; + +public interface UfHkrdzbMapper { + + /** + * 查询所有记录 + * + * @return 返回集合,没有返回空List + */ + List listAll(); + + /** + * 条件查询 + * + * @return 返回集合,没有返回空List + */ + List listSome(UfHkrdzbPO ufHkrdzb); + + + /** + * 根据主键查询 + * + * @param id 主键 + * @return 返回记录,没有返回null + */ + UfHkrdzbPO getById(Integer id); + + /** + * 新增,忽略null字段 + * + * @param ufHkrdzb 新增的记录 + * @return 返回影响行数 + */ + int insertIgnoreNull(UfHkrdzbPO ufHkrdzb); + + /** + * 修改,修改所有字段 + * + * @param ufHkrdzb 修改的记录 + * @return 返回影响行数 + */ + int update(UfHkrdzbPO ufHkrdzb); + + /** + * 修改,忽略null字段 + * + * @param ufHkrdzb 修改的记录 + * @return 返回影响行数 + */ + int updateIgnoreNull(UfHkrdzbPO ufHkrdzb); + + +} \ No newline at end of file diff --git a/src/com/engine/salary/mapper/cbs/UfHkrdzbMapper.xml b/src/com/engine/salary/mapper/cbs/UfHkrdzbMapper.xml new file mode 100644 index 0000000..7380ec2 --- /dev/null +++ b/src/com/engine/salary/mapper/cbs/UfHkrdzbMapper.xml @@ -0,0 +1,322 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + t + . + id + , t.requestId + , t.formmodeid + , t.modedatacreater + , t.modedatacreatertype + , t.modedatacreatedate + , t.modedatacreatetime + , t.MODEUUID + , t.form_biz_id + , t.gsbm + , t.khbm + , t.khmc + , t.bmbm + , t.bmd + , t.bm + , t.modedatamodifier + , t.modedatamodifydatetime + + + + + + + + + + + + + + + INSERT INTO uf_hkrdzb + + + + requestId, + + + formmodeid, + + + modedatacreater, + + + modedatacreatertype, + + + modedatacreatedate, + + + modedatacreatetime, + + + MODEUUID, + + + form_biz_id, + + + gsbm, + + + khbm, + + + khmc, + + + bmbm, + + + bmd, + + + bm, + + + modedatamodifier, + + + modedatamodifydatetime, + + + + + #{requestId}, + + + #{formmodeid}, + + + #{modedatacreater}, + + + #{modedatacreatertype}, + + + #{modedatacreatedate}, + + + #{modedatacreatetime}, + + + #{modeuuid}, + + + #{formBizId}, + + + #{gsbm}, + + + #{khbm}, + + + #{khmc}, + + + #{bmbm}, + + + #{bmd}, + + + #{bm}, + + + #{modedatamodifier}, + + + #{modedatamodifydatetime}, + + + + + + + + + UPDATE uf_hkrdzb + + requestId=#{requestId}, + formmodeid=#{formmodeid}, + modedatacreater=#{modedatacreater}, + modedatacreatertype=#{modedatacreatertype}, + modedatacreatedate=#{modedatacreatedate}, + modedatacreatetime=#{modedatacreatetime}, + MODEUUID=#{modeuuid}, + form_biz_id=#{formBizId}, + gsbm=#{gsbm}, + khbm=#{khbm}, + khmc=#{khmc}, + bmbm=#{bmbm}, + bmd=#{bmd}, + bm=#{bm}, + modedatamodifier=#{modedatamodifier}, + modedatamodifydatetime=#{modedatamodifydatetime}, + + WHERE id = #{id} + + + + + + UPDATE uf_hkrdzb + + + requestId=#{requestId}, + + + formmodeid=#{formmodeid}, + + + modedatacreater=#{modedatacreater}, + + + modedatacreatertype=#{modedatacreatertype}, + + + modedatacreatedate=#{modedatacreatedate}, + + + modedatacreatetime=#{modedatacreatetime}, + + + MODEUUID=#{modeuuid}, + + + form_biz_id=#{formBizId}, + + + gsbm=#{gsbm}, + + + khbm=#{khbm}, + + + khmc=#{khmc}, + + + bmbm=#{bmbm}, + + + bmd=#{bmd}, + + + bm=#{bm}, + + + modedatamodifier=#{modedatamodifier}, + + + modedatamodifydatetime=#{modedatamodifydatetime}, + + + WHERE id = #{id} + + + + + + \ No newline at end of file diff --git a/src/com/engine/salary/remote/cbs8/HttpUtil.java b/src/com/engine/salary/remote/cbs8/HttpUtil.java new file mode 100644 index 0000000..0df6b19 --- /dev/null +++ b/src/com/engine/salary/remote/cbs8/HttpUtil.java @@ -0,0 +1,233 @@ +package com.engine.salary.remote.cbs8; + + +import lombok.extern.slf4j.Slf4j; +import org.apache.http.Consts; +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.client.HttpClient; +import org.apache.http.client.config.AuthSchemes; +import org.apache.http.client.config.CookieSpecs; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.config.Registry; +import org.apache.http.config.RegistryBuilder; +import org.apache.http.conn.ConnectTimeoutException; +import org.apache.http.conn.socket.ConnectionSocketFactory; +import org.apache.http.conn.socket.PlainConnectionSocketFactory; +import org.apache.http.conn.ssl.NoopHostnameVerifier; +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; +import org.apache.http.util.EntityUtils; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; +import java.io.IOException; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.X509Certificate; +import java.util.Arrays; +import java.util.Collections; +import java.util.Map; + +/** + * Http调用处理类 + * + * @author Terry + */ +@Slf4j +public class HttpUtil { + + public static final String TEXT_TYPE = "text/plain"; + public static final String JSON_TYPE = "application/json"; + public static final String XML_TYPE = "text/xml"; + public static final String HTML_TYPE = "text/html"; + + public static final String EXCEL_TYPE = "application/vnd.ms-excel"; + public static final String STREAM_TYPE = "application/octet-stream"; + public static final int SLEEP_TIME = 1000; + public static final int IAS_SUCCESS = 400; + + public static HttpClient httpsTrustClient() { + try { + // 在调用SSL之前需要重写验证方法,取消检测SSL + X509TrustManager trustManager = new X509TrustManager() { + @Override + public X509Certificate[] getAcceptedIssuers() { + return null; + } + + @Override + public void checkClientTrusted(X509Certificate[] xcs, String str) { + } + + @Override + public void checkServerTrusted(X509Certificate[] xcs, String str) { + } + }; + SSLContext ctx = SSLContext.getInstance(SSLConnectionSocketFactory.TLS); + ctx.init(null, new TrustManager[]{trustManager}, null); + SSLConnectionSocketFactory socketFactory = + new SSLConnectionSocketFactory(ctx, NoopHostnameVerifier.INSTANCE); + // 创建Registry + RequestConfig requestConfig = + RequestConfig.custom().setCookieSpec(CookieSpecs.STANDARD_STRICT).setExpectContinueEnabled(Boolean.TRUE) + .setTargetPreferredAuthSchemes(Arrays.asList(AuthSchemes.NTLM, AuthSchemes.DIGEST)) + .setProxyPreferredAuthSchemes(Collections.singletonList(AuthSchemes.BASIC)).build(); + Registry socketFactoryRegistry = RegistryBuilder.create() + .register("http", PlainConnectionSocketFactory.INSTANCE).register("https", socketFactory).build(); + // 创建ConnectionManager,添加Connection配置信息 + PoolingHttpClientConnectionManager connectionManager = + new PoolingHttpClientConnectionManager(socketFactoryRegistry); + + return HttpClients.custom().setConnectionManager(connectionManager) + .setDefaultRequestConfig(requestConfig).build(); + } catch (KeyManagementException | NoSuchAlgorithmException ex) { + throw new RuntimeException(ex); + } + } + + /** + * 获取 HttpClient + * + * @param path + * @return + */ + public static HttpClient wrapClient(String path) { + HttpClient httpClient = HttpClientBuilder.create().build(); + if (path != null && path.startsWith("https://")) { + return httpsTrustClient(); + } + return httpClient; + } + + /** + * 设置http超时时间 + * + * @param requestConfig + * @return + */ + private static RequestConfig setTimeOutConfig(RequestConfig requestConfig) { + if (requestConfig == null) { + requestConfig = RequestConfig.DEFAULT; + } + return RequestConfig.copy(requestConfig).setConnectionRequestTimeout(900000).setConnectTimeout(900000) + .setSocketTimeout(900000).build(); + } + + /** + * get 请求 + * + * @param url + * @param header + * @param params + * @return + */ + public static String getRequest(String url, Map header, Map params) { + CloseableHttpClient httpClient = null; + CloseableHttpResponse response = null; + String result = ""; + + StringBuilder urlStr = new StringBuilder(); + urlStr.append(url).append("?"); + + int i = params.size(); + for (Map.Entry map : params.entrySet()) { + urlStr.append(map.getKey()).append("=").append(map.getValue()); + if ((--i) == 0) { + continue; + } + urlStr.append("&"); + } + try { + httpClient = (CloseableHttpClient) wrapClient(url); + HttpGet httpGet = new HttpGet(urlStr.toString()); + if (null != header && !header.isEmpty()) { + for (String key : header.keySet()) { + httpGet.setHeader(key, header.get(key)); + } + } + + RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(20000) + .setConnectionRequestTimeout(20000).setSocketTimeout(40000).build(); + httpGet.setConfig(requestConfig); + response = httpClient.execute(httpGet); + HttpEntity entity = response.getEntity(); + result = EntityUtils.toString(entity); + httpGet.abort(); + EntityUtils.consume(entity); + } catch (IOException e) { + log.error("发起GET请求失败", e); + result = "发起GET请求失败"; + } finally { + if (null != response) { + try { + httpClient.close(); + response.close(); + } catch (IOException e) { + log.error("发起GET请求失败", e); + } + } + + if (null != httpClient) { + try { + httpClient.close(); + } catch (IOException e) { + log.error("发起GET请求失败", e); + } + } + } + return result; + } + + /** + * HTTP Post 获取内容 + * + * @param url 请求的url地址 ?之前的地址 + * @param params 请求的参数 + * @param header 编码格式 + * @return 页面内容 + */ + public static String doPost(String url, Map header, String params, String contentType) { + String result = null; + try { + HttpPost httpPost = new HttpPost(url); + httpPost.setHeader("Connection", "close"); + httpPost.setConfig(setTimeOutConfig(httpPost.getConfig())); + httpPost.setHeader("Content-Type", contentType); + if (null != header && !header.isEmpty()) { + for (String key : header.keySet()) { + httpPost.setHeader(key, header.get(key)); + } + } + if (null != params) { + httpPost.setEntity(new StringEntity(params, Consts.UTF_8)); + } + HttpClient httpClient = wrapClient(url); + HttpResponse response = httpClient.execute(httpPost); + int statusCode = response.getStatusLine().getStatusCode(); + if (statusCode != 200) { + httpPost.abort(); + throw new RuntimeException("HttpClient,error status code :" + statusCode); + } + HttpEntity entity = response.getEntity(); + if (entity != null) { + result = EntityUtils.toString(entity, Consts.UTF_8); + } + EntityUtils.consume(entity); + httpPost.abort(); + return result; + } catch (Exception e) { + log.error("发起POST请求失败", e); + result = "发起POST请求失败"; + } + return result; + } +} diff --git a/src/com/engine/salary/remote/cbs8/JsonUtil.java b/src/com/engine/salary/remote/cbs8/JsonUtil.java new file mode 100644 index 0000000..51a9fb9 --- /dev/null +++ b/src/com/engine/salary/remote/cbs8/JsonUtil.java @@ -0,0 +1,97 @@ +package com.engine.salary.remote.cbs8; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson.parser.Feature; +import com.alibaba.fastjson.serializer.SerializerFeature; +import com.alibaba.fastjson.util.TypeUtils; + +import java.lang.reflect.Type; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +public class JsonUtil { + private static final SerializerFeature[] DEFAULT_S_FEATURES; + private static final SerializerFeature[] PRETTY_S_FEATURES; + private static final Feature[] DEFAULT_P_FEATURES; + + public JsonUtil() { + } + + public static String toJsonString(Object obj) { + return JSON.toJSONString(obj, DEFAULT_S_FEATURES); + } + + public static String toJsonString(Object obj, SerializerFeature... features) { + return JSON.toJSONString(obj, features); + } + + public static String toPrettyJson(Object object) { + return JSON.toJSONString(object, PRETTY_S_FEATURES); + } + + public static JSONObject parseJsonObject(String jsonStr) { + return JSON.parseObject(jsonStr, DEFAULT_P_FEATURES); + } + + public static T parseValue(JSONObject jsonObject, String key, Class clazz) { + if (jsonObject != null) { + T value = jsonObject.getObject(key, (Type) clazz); + return value; + } else { + return null; + } + } + + public static T parseObject(String jsonStr, Class clazz) { + return JSON.parseObject(jsonStr, (Type) clazz, DEFAULT_P_FEATURES); + } + + public static List parseList(String jsonStr, Class clazz) { + return JSON.parseArray(jsonStr, clazz); + } + + public static List parseList(Object jsonObject, Class clazz) { + String jsonStr = toJsonString(jsonObject); + return parseList(jsonStr, clazz); + } + + public static Map parseMap(String jsonStr, Class valueCls) { + Map result = new LinkedHashMap(); + Map map = JSON.parseObject(jsonStr, DEFAULT_P_FEATURES); + if (map != null && map.size() > 0) { + Iterator var4 = map.entrySet().iterator(); + + while (var4.hasNext()) { + Entry entry = (Entry) var4.next(); + Object obj = entry.getValue(); + V value = JSON.parseObject(JSON.toJSONString(obj), valueCls); + result.put(entry.getKey(), value); + } + } + + return result; + } + + public static Map parseMap(Object jsonObject, Class valueCls) { + String jsonStr = toJsonString(jsonObject); + return parseMap(jsonStr, valueCls); + } + + public static T parseBean(String jsonString, Class beanClazz) { + return parseBean(parseJsonObject(jsonString), beanClazz); + } + + public static T parseBean(JSONObject jsonObject, Class beanClazz) { + return TypeUtils.castToJavaBean(jsonObject, beanClazz); + } + + static { + DEFAULT_S_FEATURES = new SerializerFeature[]{SerializerFeature.WriteDateUseDateFormat, SerializerFeature.SortField}; + PRETTY_S_FEATURES = new SerializerFeature[]{SerializerFeature.WriteDateUseDateFormat, SerializerFeature.SortField, SerializerFeature.PrettyFormat}; + DEFAULT_P_FEATURES = new Feature[]{Feature.OrderedField}; + } +} diff --git a/src/com/engine/salary/remote/cbs8/SalaryEntityUtil.java b/src/com/engine/salary/remote/cbs8/SalaryEntityUtil.java new file mode 100644 index 0000000..06eb7aa --- /dev/null +++ b/src/com/engine/salary/remote/cbs8/SalaryEntityUtil.java @@ -0,0 +1,417 @@ +package com.engine.salary.remote.cbs8; + +import com.alibaba.fastjson.JSON; +import com.engine.salary.exception.CBS8RunTimeException; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.collections4.MapUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.math.NumberUtils; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.text.DecimalFormat; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Collector; +import java.util.stream.Collectors; + +/** + * 实体类相关 + *

Copyright: Copyright (c) 2022

+ *

Company: 泛微软件

+ * + * @author qiantao + * @version 1.0 + **/ +public class SalaryEntityUtil { + + private static final DecimalFormat decimalFormat = new DecimalFormat("#,##0.00"); + + /** + * 数字正则表达式 + * 包含负数、正数、小数、0、0.00000等 + */ + public static final String NUMBER_REGEX = "(-?[1-9]\\d*\\.?\\d+)|(-?0\\.\\d*[0-9])|(\\d+)"; + + /** + * 千分位格式化 + * + * @param originMap 原始map + * @param targetMap 目标map + */ + public static void thousandthConvert(Map originMap, Map targetMap) { + + if (MapUtils.isNotEmpty(originMap)) { + originMap.forEach((k, v) -> { + if (StringUtils.isNotBlank(String.valueOf(v))) { + targetMap.put(k, decimalFormat.format(Double.valueOf(String.valueOf(v)))); + } + }); + } + } + + /** + * 千分位格式化 + * + * @param originString 原始字符串 + * @return 格式化后的字符串 + */ + public static String thousandthConvert(String originString) { + if (StringUtils.isNotBlank(originString)) { + return decimalFormat.format(Double.valueOf(originString)); + } + return "0.00"; + } + + /** + * 判断对象或对象数组中每一个对象是否为空: 对象为null,字符序列长度为0,集合类、Map为empty + * + * @param obj + * @return + */ + public static boolean isNullOrEmpty(Object obj) { + if (obj == null) { + return true; + } + if (obj instanceof CharSequence) { + return ((CharSequence) obj).length() == 0; + } + if (obj instanceof Collection) { + return ((Collection) obj).isEmpty(); + } + if (obj instanceof Map) { + return ((Map) obj).isEmpty(); + } + if (obj instanceof Object[]) { + Object[] object = (Object[]) obj; + if (object.length == 0) { + return true; + } + boolean empty = true; + for (int i = 0; i < object.length; i++) { + if (!isNullOrEmpty(object[i])) { + empty = false; + break; + } + } + return empty; + } + + return false; + } + + public static boolean isNotNullOrEmpty(Object obj) { + return !isNullOrEmpty(obj); + } + + public static A properties(Collection objs, Function function, Collector collectors) { + return objs.stream().map(function).collect(collectors); + } + + public static Set properties(Collection objs, Function function) { + if (CollectionUtils.isEmpty(objs)) { + return Sets.newHashSet(); + } + return properties(objs, function, Collectors.toSet()); + } + + public static Map convert2Map(Collection objs, Function function) { + if (CollectionUtils.isEmpty(objs)) { + return Maps.newHashMap(); + } + return objs.stream().collect(Collectors.toMap(function, Function.identity(), (a, b) -> a)); + } + + public static Map convert2Map(Collection objs, Function keyMapper, Function valueMapper) { + if (CollectionUtils.isEmpty(objs)) { + return Maps.newHashMap(); + } + return objs.stream() + .filter(e -> valueMapper.apply(e) != null && keyMapper.apply(e) != null) + .collect(Collectors.toMap(keyMapper, valueMapper, (a, b) -> a)); + } + + public static Map> group2Map(Collection objs, Function function) { + if (CollectionUtils.isEmpty(objs)) { + return Maps.newHashMap(); + } + return objs.stream().collect(Collectors.groupingBy(function)); + } + + public static Map> group2Map(Collection objs, Function keyMapper, Function valueMapper) { + if (CollectionUtils.isEmpty(objs)) { + return Maps.newHashMap(); + } + return objs.stream() + .filter(e -> keyMapper.apply(e) != null && valueMapper.apply(e) != null) + .collect(Collectors.groupingBy(keyMapper, + Collectors.collectingAndThen(Collectors.toList(), e -> e.stream().map(valueMapper).collect(Collectors.toSet())))); + } + + public static Map> group2ListMap(Collection objs, Function keyMapper, Function valueMapper) { + if (CollectionUtils.isEmpty(objs)) { + return Maps.newHashMap(); + } + return objs.stream() + .filter(e -> keyMapper.apply(e) != null && valueMapper.apply(e) != null) + .collect(Collectors.groupingBy(keyMapper, + Collectors.collectingAndThen(Collectors.toList(), e -> e.stream().map(valueMapper).collect(Collectors.toList())))); + } + + public static Map> list2Map(Collection objs, Function function1, Function function2, Function function3) { + if (CollectionUtils.isEmpty(objs)) { + return Maps.newHashMap(); + } + Map> collect = objs.stream().collect(Collectors.groupingBy(function1)); + Map> map = new HashMap<>(); + for (Map.Entry> entry: collect.entrySet()) { + Map values = map.getOrDefault(entry.getKey(), new HashMap<>()); + entry.getValue().forEach(e -> values.put(function2.apply(e), function3.apply(e))); + map.put(entry.getKey(), values); + } + return map; + } + + public static Map> list2Map(Collection objs, Function function1, Function function2) { + if (CollectionUtils.isEmpty(objs)) { + return Maps.newHashMap(); + } + Map> collect = objs.stream().collect(Collectors.groupingBy(function1)); + Map> map = new HashMap<>(); + for (Map.Entry> entry: collect.entrySet()) { + Map values = map.getOrDefault(entry.getKey(), new HashMap<>()); + entry.getValue().forEach(e -> values.put(function2.apply(e), e)); + map.put(entry.getKey(), values); + } + return map; + } + + + + /** + * LinkedHashMap有序去重 + * + * @param keyExtractor + * @param + * @return + */ + public static Predicate distinctByKey(Function keyExtractor) { + LinkedHashMap map = new LinkedHashMap<>(); + return t -> map.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null; + } + + /** + * ConcurrentHashMap无序去重 + * + * @param keyExtractor + * @param + * @return + */ + public static Predicate distinctByKeyMap(Function keyExtractor) { + ConcurrentHashMap map = new ConcurrentHashMap<>(); + return t -> map.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null; + } + + public static BigDecimal reduce(Collection objs, Function function) { + if (CollectionUtils.isEmpty(objs)) { + return BigDecimal.ZERO; + } + return objs.stream() + .filter(e -> function.apply(e) != null) + .map(function) + .reduce(BigDecimal.ZERO, BigDecimal::add); + } + + public static BigDecimal empty2Zero(String value) { + if (StringUtils.isEmpty(value)) { + return BigDecimal.ZERO; + } + try { + return new BigDecimal(value); + } catch (Exception e) { + return BigDecimal.ZERO; + } + } + + + + /** + * 两个集合是否有交集 + * + * @param list1 + * @param list2 + * @param + * @return + */ + public static boolean judgeIntersection(List list1, List list2) { + boolean flag = false; + List origin = new ArrayList<>(); + origin.addAll(list1); + origin.retainAll(list2); + if (origin.size() > 0) { + flag = true; + } + return flag; + } + + /** + * String转Long + * + * @param obj + * @return + */ + public static Long string2Long(String obj) { + if (NumberUtils.isCreatable(obj)) { + return Long.valueOf(obj); + } + return null; + } + + /** + * String转Integer + * + * @param obj + * @return + */ + public static Integer string2Integer(String obj) { + if (NumberUtils.isCreatable(obj)) { + return Integer.valueOf(obj); + } + return null; + } + + /** + * String转BigDecimal + * + * @param obj + * @return + */ + public static BigDecimal string2BigDecimal(String obj) { + if (NumberUtils.isCreatable(obj)) { + return new BigDecimal(obj); + } + return null; + } + + /** + * String转BigDecimal + * + * @param obj + * @return + */ + public static BigDecimal string2BigDecimalDefault0(String obj) { + if (NumberUtils.isCreatable(obj)) { + return new BigDecimal(obj); + } + return BigDecimal.ZERO; + } + + /** + * 判断字符串是否等于0 + * + * @param obj + * @return + */ + public static boolean StringEqZERO(String obj) { + if (NumberUtils.isCreatable(obj)) { + return BigDecimal.ZERO.compareTo(new BigDecimal(obj)) == 0; + } + return false; + } + + + public static Double string2DoubleDefault0(String obj) { + if (NumberUtils.isCreatable(obj)) { + return new Double(obj); + } + return new Double("0.0"); + } + + /** + * 两个集合交集,(forEasy) + * + * @param arr1 + * @param arr2 + * @return + */ + public static Collection intersectionForList(Collection arr1, Collection arr2) { + + Collection resultList = new ArrayList<>(); + + if (CollectionUtils.isEmpty(arr1) || CollectionUtils.isEmpty(arr1)) { + return resultList; + } + arr1.forEach(a1 -> { + if (arr2.contains(a1)) { + resultList.add(a1); + } + }); + return resultList; + } + + public static String toJSONString(Object obj) { + if (obj != null) { + return JSON.toJSONString(obj); + } + return ""; + } + + + public static String null2String(Object obj) { + if (Objects.isNull(obj)) { + return ""; + } + return obj.toString(); + } + + public static String null2String(Object obj, String def) { + if (Objects.isNull(obj)) { + return def; + } + return obj.toString(); + } + + public static Integer getIntValue(Object obj, Integer def) { + if (Objects.isNull(obj)) { + return def; + } + try { + return StringUtils.isEmpty(String.valueOf(obj)) ? def : Integer.valueOf(String.valueOf(obj)); + } catch (NumberFormatException e) { + return def; + } + } + + public static BigDecimal getBigDecimal(Object value, int scale) { + String valueStr = null2String(value); + if (StringUtils.isEmpty(valueStr)) { + return BigDecimal.ZERO; + } + try { + return new BigDecimal(valueStr).setScale(scale, RoundingMode.HALF_UP); + } catch (NumberFormatException e) { + return null; + } + } + + public static BigDecimal getBigDecimal(Object value, int scale, BigDecimal defValue) { + try { + return new BigDecimal(null2String(value)).setScale(scale, RoundingMode.HALF_UP); + } catch (NumberFormatException e) { + return defValue; + } + } + + + public static T findFirst(Collection objs) { + if (CollectionUtils.isEmpty(objs)) { + throw new CBS8RunTimeException("the collection can not be empty"); + } + return objs.stream().findFirst().orElse(null); + } + +} diff --git a/src/com/engine/salary/remote/cbs8/client/AccountManagementClient.java b/src/com/engine/salary/remote/cbs8/client/AccountManagementClient.java new file mode 100644 index 0000000..03f0d50 --- /dev/null +++ b/src/com/engine/salary/remote/cbs8/client/AccountManagementClient.java @@ -0,0 +1,59 @@ +package com.engine.salary.remote.cbs8.client; + +import com.engine.salary.exception.CBS8RunTimeException; +import com.engine.salary.remote.cbs8.request.GetTransactionDetailRequest; +import com.engine.salary.remote.cbs8.response.GetTransactionDetailResponse; +import com.engine.salary.remote.cbs8.JsonUtil; +import lombok.extern.slf4j.Slf4j; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Objects; + +@Slf4j +public class AccountManagementClient extends CBS8BaseClient { + + public GetTransactionDetailResponse transactionDetailQuery(GetTransactionDetailRequest requestParam) throws IOException { + String url = host + "/openapi/account/openapi/v1/transaction-detail/query"; + + + CloseableHttpClient client = HttpClients.custom() + // 禁止HttpClient自动解压缩 + .disableContentCompression() + .build(); + + String requestData = JsonUtil.toJsonString(requestParam); + log.info("获取cbs交易参数," + url + "\n" + requestData); + + HttpPost httpPost = setupRequest(url, requestData); + try (CloseableHttpResponse response = client.execute(httpPost)) { + byte[] finalResponseData = handleResponse(response); + String req = new String(finalResponseData, StandardCharsets.UTF_8); + GetTransactionDetailResponse getTransactionDetailResponse = JsonUtil.parseBean(req, GetTransactionDetailResponse.class); + log.info("获取cbs交易结果," + "\n" + JsonUtil.toJsonString(getTransactionDetailResponse)); + + if (Objects.isNull(getTransactionDetailResponse)) { + throw new CBS8RunTimeException("服务异常"); + } + if (!"0".equals(getTransactionDetailResponse.getCode())) { + throw new CBS8RunTimeException(getTransactionDetailResponse.getMsg()); + } + if (getTransactionDetailResponse.getData() == null) { + throw new CBS8RunTimeException("未获取数据"); + } + + return getTransactionDetailResponse; + + } catch (IOException ignored) { + log.error("网络连接失败或超时!",ignored); + throw new CBS8RunTimeException("网络连接失败或超时!"); + } finally { + client.close(); + } + } + +} diff --git a/src/com/engine/salary/remote/cbs8/client/BillManagementClient.java b/src/com/engine/salary/remote/cbs8/client/BillManagementClient.java new file mode 100644 index 0000000..6a57057 --- /dev/null +++ b/src/com/engine/salary/remote/cbs8/client/BillManagementClient.java @@ -0,0 +1,56 @@ +package com.engine.salary.remote.cbs8.client; + +import com.engine.salary.exception.CBS8RunTimeException; +import com.engine.salary.remote.cbs8.request.GetDtaRequest; +import com.engine.salary.remote.cbs8.response.GetDtaResponse; +import com.engine.salary.remote.cbs8.JsonUtil; +import lombok.extern.slf4j.Slf4j; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Objects; + +@Slf4j +public class BillManagementClient extends CBS8BaseClient { + + public GetDtaResponse dtaQuery(GetDtaRequest requestParam) throws IOException { + String url = host + "/openapi/draft/openapi/v1/dta/query"; + + CloseableHttpClient client = HttpClients.custom() + // 禁止HttpClient自动解压缩 + .disableContentCompression() + .build(); + + String requestData = JsonUtil.toJsonString(requestParam); + log.info("获取cbs票据参数," + url + "\n" + requestData); + + HttpPost httpPost = setupRequest(url, requestData); + try (CloseableHttpResponse response = client.execute(httpPost)) { + byte[] finalResponseData = handleResponse(response); + String req = new String(finalResponseData, StandardCharsets.UTF_8); + GetDtaResponse getDtaResponse = JsonUtil.parseBean(req, GetDtaResponse.class); + log.info("获取cbs票据结果," + "\n" + JsonUtil.toJsonString(getDtaResponse)); + + if (Objects.isNull(getDtaResponse)) { + throw new CBS8RunTimeException("服务异常"); + } + if (!"0".equals(getDtaResponse.getCode())) { + throw new CBS8RunTimeException(getDtaResponse.getMsg()); + } + if (getDtaResponse.getData() == null) { + throw new CBS8RunTimeException("未获取数据"); + } + + return getDtaResponse; + } catch (IOException ignored) { + throw new CBS8RunTimeException("网络连接失败或超时!"); + } finally { + client.close(); + } + } + +} diff --git a/src/com/engine/salary/remote/cbs8/client/CBS8BaseClient.java b/src/com/engine/salary/remote/cbs8/client/CBS8BaseClient.java new file mode 100644 index 0000000..930c5f2 --- /dev/null +++ b/src/com/engine/salary/remote/cbs8/client/CBS8BaseClient.java @@ -0,0 +1,166 @@ +package com.engine.salary.remote.cbs8.client; + +import cn.hutool.core.util.StrUtil; +import com.engine.salary.exception.CBS8RunTimeException; +import com.engine.salary.remote.cbs8.common.Constants; +import com.engine.salary.remote.cbs8.response.GetTokenResponse; +import com.engine.salary.remote.cbs8.util.SM2Util; +import com.engine.salary.remote.cbs8.HttpUtil; +import com.engine.salary.remote.cbs8.JsonUtil; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.io.IOUtils; +import org.apache.http.Header; +import org.apache.http.HttpMessage; +import org.apache.http.HttpResponse; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.ByteArrayEntity; +import org.apache.http.protocol.HTTP; +import weaver.general.BaseBean; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.zip.GZIPInputStream; + +@Slf4j +public class CBS8BaseClient { + + static final String appId = new BaseBean().getPropValue("hrmSalaryCBS8", "app_id"); + + static final String appSecret = new BaseBean().getPropValue("hrmSalaryCBS8", "app_secret"); + + /** + * 财资管理云公钥(平台公钥) + */ + static final String bodyEncryptionKey = new BaseBean().getPropValue("hrmSalaryCBS8", "bodyEncryptionKey"); + + /** + * 企业私钥(加密) + */ + static final String signEncryptionPrivateKey = new BaseBean().getPropValue("hrmSalaryCBS8", "signEncryptionPrivateKey"); + + /** + * 企业私钥(解密) + */ + static final String bodyDecryptionKey = new BaseBean().getPropValue("hrmSalaryCBS8", "bodyDecryptionKey"); + + /** + * 域名 + */ + static final String host = new BaseBean().getPropValue("hrmSalaryCBS8", "host"); + + + /** + * 获取token + * + * @return + */ + public static String getToken() { + Map params = new HashMap<>(); + params.put("app_id", appId); + params.put("app_secret", appSecret); + params.put("grant_type", "client_credentials"); + // 开始请求 + String paramBody = JsonUtil.toJsonString(params); + String res = HttpUtil.doPost(host + "/openapi/app/v1/app/token", new HashMap<>(), paramBody, HttpUtil.JSON_TYPE); + GetTokenResponse getTokenResponse = JsonUtil.parseBean(res, GetTokenResponse.class); + log.info("获取token params:{} res: {}", paramBody, res); + if (Objects.isNull(getTokenResponse)) { + throw new CBS8RunTimeException("服务异常"); + } + if (!"0".equals(getTokenResponse.getCode())) { + throw new CBS8RunTimeException(getTokenResponse.getMsg()); + } + if (getTokenResponse.getData() == null) { + throw new CBS8RunTimeException("未获取数据"); + } + if (StrUtil.isBlank(getTokenResponse.getData().getToken())) { + throw new CBS8RunTimeException("未获取token"); + } + String token = getTokenResponse.getData().getToken(); + return token; + } + + + /** + * 生成请求报文 + */ + public static HttpPost setupRequest(String url, String requestData) { + + long timestamp = System.currentTimeMillis(); + + // 请求数据拼接: 报文体+时间戳 + byte[] requestDataBytes = requestData.getBytes(StandardCharsets.UTF_8); + byte[] timestampBytes = ("×tamp=" + timestamp).getBytes(StandardCharsets.UTF_8); + byte[] newBytes = new byte[requestDataBytes.length + timestampBytes.length]; + System.arraycopy(requestDataBytes, 0, newBytes, 0, requestDataBytes.length); + System.arraycopy(timestampBytes, 0, newBytes, requestDataBytes.length, timestampBytes.length); + + // 生成签名 + byte[] signature = SM2Util.sign(signEncryptionPrivateKey, newBytes); + String sign = Base64.encodeBase64String(SM2Util.encodeDERSignature(signature)); + + // 设置请求URL + HttpPost httpPost = new HttpPost(url); + // 请求头设置签名 + httpPost.setHeader(Constants.SIGN_HEADER_NAME, sign); + // 请求头设置时间戳 + httpPost.setHeader(Constants.TIMESTAMP_HEADER, Long.toString(timestamp)); + // 请求头设置请求参数格式,请根据实际情况改写 + httpPost.setHeader(HTTP.CONTENT_TYPE, Constants.TARGET_CONTENT_TYPE); + // 请求头设置TOKEN + String token = getToken(); + httpPost.setHeader(Constants.AUTHORIZATION, Constants.BEARER + token); + + // 报文体加密 + byte[] encryptedData = SM2Util.encrypt(bodyEncryptionKey, requestDataBytes); + // 设置请求体 + httpPost.setEntity(new ByteArrayEntity(encryptedData)); + + return httpPost; + } + + /** + * 处理响应报文 + */ + public byte[] handleResponse(HttpResponse response) throws IOException { + InputStream content = response.getEntity().getContent(); + byte[] responseData = IOUtils.toByteArray(content); + + if (responseData == null || responseData.length == 0) { + return responseData == null ? new byte[0] : responseData; + } + + // 步骤1 原始响应报文解密 如果服务网关获取加解密密钥失败,则无法解密请求报文,且无法加密响应报文。 这时候,网关会直接返回错误信息,响应报文是未加密状态。 + Boolean encryptionEnable = getHeader(response, Constants.ENCRYPTION_ENABLED_HEADER_NAME); + + if (Boolean.TRUE.equals(encryptionEnable)) { + responseData = SM2Util.decrypt(bodyDecryptionKey, responseData); + } + + Boolean xMbcloudCompress = getHeader(response, Constants.X_MBCLOUD_COMPRESS); + if (Boolean.TRUE.equals(xMbcloudCompress)) { + responseData = decompress(responseData); + } + + return responseData; + + + } + + private static Boolean getHeader(HttpMessage message, String name) { + Header header = message.getFirstHeader(name); + return header != null; + } + + public static byte[] decompress(byte[] data) throws IOException { + ByteArrayInputStream input = new ByteArrayInputStream(data); + GZIPInputStream gzipInput = new GZIPInputStream(input); + return IOUtils.toByteArray(gzipInput); + } +} diff --git a/src/com/engine/salary/remote/cbs8/common/Constants.java b/src/com/engine/salary/remote/cbs8/common/Constants.java new file mode 100644 index 0000000..d0fbcd5 --- /dev/null +++ b/src/com/engine/salary/remote/cbs8/common/Constants.java @@ -0,0 +1,23 @@ +package com.engine.salary.remote.cbs8.common; + +/** + * @author: KeXue + * @time: 2022/8/25 + * @description: 常量类 + */ +public interface Constants { + + String TARGET_CONTENT_TYPE = "application/json"; + + String SIGN_HEADER_NAME = "X-MBCLOUD-API-SIGN"; + + String TIMESTAMP_HEADER = "X-MBCLOUD-TIMESTAMP"; + + String ENCRYPTION_ENABLED_HEADER_NAME = "X-MBCLOUD-ENCRYPTION-ENABLED"; + + String X_MBCLOUD_COMPRESS = "X-Mbcloud-Compress"; + + String AUTHORIZATION = "Authorization"; + + String BEARER = "Bearer "; +} diff --git a/src/com/engine/salary/remote/cbs8/config/EBS2ECConfig.java b/src/com/engine/salary/remote/cbs8/config/EBS2ECConfig.java new file mode 100644 index 0000000..dcc3a3f --- /dev/null +++ b/src/com/engine/salary/remote/cbs8/config/EBS2ECConfig.java @@ -0,0 +1,63 @@ +package com.engine.salary.remote.cbs8.config; + +import com.thoughtworks.xstream.annotations.XStreamAlias; +import com.thoughtworks.xstream.annotations.XStreamAsAttribute; +import com.thoughtworks.xstream.annotations.XStreamImplicit; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@XStreamAlias("config") +public class EBS2ECConfig { + @XStreamAlias("name") + @XStreamAsAttribute + private String name; + + @XStreamImplicit(itemFieldName = "table") + private List tables; + + @Data + public static class Table { + @XStreamAlias("name") + @XStreamAsAttribute + private String name; + + @XStreamAlias("key") + @XStreamAsAttribute + private String key; + + @XStreamAlias("modeId") + @XStreamAsAttribute + private Integer modeId; + + @XStreamImplicit(itemFieldName = "field") + private List fields; + + @Data + public static class Field { + @XStreamAlias("name") + @XStreamAsAttribute + private String name; + + @XStreamAlias("key") + @XStreamAsAttribute + private String key; + + @XStreamAlias("ebsKey") + @XStreamAsAttribute + private String ebsKey; + + @XStreamAlias("unique") + @XStreamAsAttribute + private boolean unique; + + } + } +} diff --git a/src/com/engine/salary/remote/cbs8/example/SM2Example.java b/src/com/engine/salary/remote/cbs8/example/SM2Example.java new file mode 100644 index 0000000..c3f46cd --- /dev/null +++ b/src/com/engine/salary/remote/cbs8/example/SM2Example.java @@ -0,0 +1,155 @@ +package com.engine.salary.remote.cbs8.example; + +import com.engine.salary.remote.cbs8.common.Constants; +import com.engine.salary.remote.cbs8.util.SM2Util; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.io.IOUtils; +import org.apache.http.Header; +import org.apache.http.HttpMessage; +import org.apache.http.HttpResponse; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.ByteArrayEntity; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.protocol.HTTP; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.zip.GZIPInputStream; + +/** + * @author: KeXue + * @time: 2022/8/25 + * @description: SM2请求加解密实例 + */ +@Slf4j +public class SM2Example { + + /** + * 财资管理云公钥(平台公钥) + */ + static final String bodyEncryptionKey = "0467E11F7CA86D884C990D4F3F5C2A1EFEEBB02B9878F4FDDCB7899DFABADCC8F38FC23F007A8AF9B2EB8A1D313959647CD8A1542F0414116AE6CFCA792346A802"; + + /** + * 企业私钥(加密) + */ + static final String signEncryptionPrivateKey = "135d6d61d5e820a979e58f4939abbd528c82528c8c276852445854377e1df247"; + + /** + * 企业私钥(解密) + */ + static final String bodyDecryptionKey = "135d6d61d5e820a979e58f4939abbd528c82528c8c276852445854377e1df247"; + + /** + * 根据appid和appsecert获取的token + */ + static final String token = "e4cdf00f-3fc9-4845-ac57-6baaee620895"; + + /** + * 接口路径 + */ + static final String TARGET_URL = "https://cbs8-openapi-reprd.csuat.cmburl.cn/openapi/account/accounts-current-balance/erp/query"; + + /** + * 请求体数据 + */ + static final String requestData = "\n" + "{\"accountNo\":\"\"}"; + + + public static void main(String[] args) throws Exception { + CloseableHttpClient client = HttpClients.custom() + // 禁止HttpClient自动解压缩 + .disableContentCompression() + .build(); + + HttpPost httpPost = setupRequest(); + try (CloseableHttpResponse response = client.execute(httpPost)) { + byte[] finalResponseData = handleResponse(response); + log.info("\n返回结果:{}", new String(finalResponseData)); + }catch (IOException ignored){ + throw new IOException("网络连接失败或超时!"); + }finally { + client.close(); + } + } + + /** + * 生成请求报文 + */ + private static HttpPost setupRequest() { + long timestamp = System.currentTimeMillis(); + + // 请求数据拼接: 报文体+时间戳 + byte[] requestDataBytes = requestData.getBytes(StandardCharsets.UTF_8); + byte[] timestampBytes = ("×tamp=" + timestamp).getBytes(StandardCharsets.UTF_8); + byte[] newBytes = new byte[requestDataBytes.length + timestampBytes.length]; + System.arraycopy(requestDataBytes, 0, newBytes, 0, requestDataBytes.length); + System.arraycopy(timestampBytes, 0, newBytes, requestDataBytes.length, timestampBytes.length); + + // 生成签名 + byte[] signature = SM2Util.sign(signEncryptionPrivateKey, newBytes); + String sign = Base64.encodeBase64String(SM2Util.encodeDERSignature(signature)); + log.info("签名:{}", sign); + + // 设置请求URL + HttpPost httpPost = new HttpPost(TARGET_URL); + // 请求头设置签名 + httpPost.setHeader(Constants.SIGN_HEADER_NAME, sign); + // 请求头设置时间戳 + httpPost.setHeader(Constants.TIMESTAMP_HEADER, Long.toString(timestamp)); + // 请求头设置请求参数格式,请根据实际情况改写 + httpPost.setHeader(HTTP.CONTENT_TYPE, Constants.TARGET_CONTENT_TYPE); + // 请求头设置TOKEN + httpPost.setHeader(Constants.AUTHORIZATION, Constants.BEARER + token); + + // 报文体加密 + byte[] encryptedData = SM2Util.encrypt(bodyEncryptionKey, requestDataBytes); + // 设置请求体 + httpPost.setEntity(new ByteArrayEntity(encryptedData)); + + return httpPost; + } + + /** + * 处理响应报文 + */ + private static byte[] handleResponse(HttpResponse response) throws Exception { + InputStream content = response.getEntity().getContent(); + byte[] responseData = IOUtils.toByteArray(content); + + if (responseData == null || responseData.length == 0) { + return responseData == null ? new byte[0] : responseData; + } + + // 步骤1 原始响应报文解密 如果服务网关获取加解密密钥失败,则无法解密请求报文,且无法加密响应报文。 这时候,网关会直接返回错误信息,响应报文是未加密状态。 + Boolean encryptionEnable = getHeader(response, Constants.ENCRYPTION_ENABLED_HEADER_NAME); + + if (Boolean.TRUE.equals(encryptionEnable)) { + responseData = SM2Util.decrypt(bodyDecryptionKey, responseData); + } + + Boolean xMbcloudCompress = getHeader(response, Constants.X_MBCLOUD_COMPRESS); + if (Boolean.TRUE.equals(xMbcloudCompress)) { + responseData = decompress(responseData); + } + + return responseData; + + + } + + private static Boolean getHeader(HttpMessage message, String name) { + Header header = message.getFirstHeader(name); + return header != null; + } + + public static byte[] decompress(byte[] data) throws IOException { + ByteArrayInputStream input = new ByteArrayInputStream(data); + GZIPInputStream gzipInput = new GZIPInputStream(input); + return IOUtils.toByteArray(gzipInput); + } +} diff --git a/src/com/engine/salary/remote/cbs8/po/UfHkrdzbPO.java b/src/com/engine/salary/remote/cbs8/po/UfHkrdzbPO.java new file mode 100644 index 0000000..4d3de9b --- /dev/null +++ b/src/com/engine/salary/remote/cbs8/po/UfHkrdzbPO.java @@ -0,0 +1,55 @@ +package com.engine.salary.remote.cbs8.po; + + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.Collection; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class UfHkrdzbPO { + private Integer id; + + private Integer requestId; + + private Integer formmodeid; + + private Integer modedatacreater; + + private Integer modedatacreatertype; + + private String modedatacreatedate; + + private String modedatacreatetime; + + private String modeuuid; + + private String formBizId; + + private String gsbm; + + private String khbm; + + private String khmc; + + private String bmbm; + + private Integer bmd; + + private Integer bm; + + private Integer modedatamodifier; + + private String modedatamodifydatetime; + + + private Collection ids; + + private Integer departmentId; + +} \ No newline at end of file diff --git a/src/com/engine/salary/remote/cbs8/request/CBS8BaseRequest.java b/src/com/engine/salary/remote/cbs8/request/CBS8BaseRequest.java new file mode 100644 index 0000000..ec73482 --- /dev/null +++ b/src/com/engine/salary/remote/cbs8/request/CBS8BaseRequest.java @@ -0,0 +1,10 @@ +package com.engine.salary.remote.cbs8.request; + +import lombok.Data; + + +@Data +public class CBS8BaseRequest { + private int currentPage; + private int pageSize; +} diff --git a/src/com/engine/salary/remote/cbs8/request/GetDtaRequest.java b/src/com/engine/salary/remote/cbs8/request/GetDtaRequest.java new file mode 100644 index 0000000..cf39708 --- /dev/null +++ b/src/com/engine/salary/remote/cbs8/request/GetDtaRequest.java @@ -0,0 +1,108 @@ +package com.engine.salary.remote.cbs8.request; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.math.BigDecimal; +import java.util.List; + +@Data +@EqualsAndHashCode(callSuper = true) +public class GetDtaRequest extends CBS8BaseRequest { + /** + * 出票日期起 格式为yyyy-mm-dd,出票日期起止不允许一边有值一边无值,日期间隔最大为一年 + */ + private String issueDateStart; + /** + * 出票日期止 格式为yyyy-mm-dd,出票日期起止不允许一边有值一边无值,日期间隔最大为一年 + */ + private String issueDateEnd; + /** + * 到期日期起 格式为yyyy-mm-dd,到期日期起止不允许一边有值一边无值,日期间隔最大为一年 + */ + private String dueDateStart; + /** + * 到期日期止 格式为yyyy-mm-dd,到期日期起止不允许一边有值一边无值,日期间隔最大为一年 + */ + private String dueDateEnd; + /** + * 票据种类 AC01-银承,AC02-商承 + */ + private String billType; + /** + * 票据来源 多选。1-直联交易,2-台账交易,3-台账登记,4-挑票同步,5-任务同步 + */ + private List draftSourceList; + /** + * 持票银行类型 多选。见附录4.1.1.银行类型枚举 + */ + private List holdBankTypeList; + /** + * 持票人单位编码 多选。填在cbs系统公共设置>基础信息>组织机构维护的单位编码 + */ + private List displayHoldOrganizationCodeList; + /** + * 持票人账号 多选。 + */ + private List holdAccountList; + /** + * 持票签收日期起 格式为yyyy-mm-dd,持票签收日期起止不允许一边有值一边无值,日期间隔最大为一年 + */ + private String holdSignDateStart; + /** + * 持票签收日期止 格式为yyyy-mm-dd,持票签收日期起止不允许一边有值一边无值,日期间隔最大为一年 + */ + private String holdSignDateEnd; + /** + * 系统票据编号 + */ + private String draftNbr; + /** + * 票据(包)号 + */ + private String billNbr; + + /** + * 票据金额起 大于等于0,最多为两位小数且整数位上限为13位。票据金额起止不允许一边有值一边无值。单位(元) + */ + private BigDecimal billAmountStart; + + /** + * 票据金额止 大于等于0,最多为两位小数且整数位上限为13位。票据金额起止不允许一边有值一边无值。单位(元) + */ + private BigDecimal billAmountEnd; + + /** + * 子票区间起 子票区间起止不允许一边有值一边无值,最多12位 + */ + private String subBillIntervalStart; + /** + * 子票区间止 子票区间起止不允许一边有值一边无值,最多12位 + */ + private String subBillIntervalEnd; + /** + * 系统票据类型 多选。按附录4.1.3票据类型-票据状态-流通标志级联关系表选择后按顺序填写。系统票据类型枚举见附录4.1.3。票据状态枚举见附录4.1.4。流通标志枚举见附录4.1.5。 + */ + private List billVarietyList; + /** + * 票据状态 + */ + private List billStsList; + /** + * 流通标志 + */ + private List billTrsStsList; + /** + * 库存状态 1-已入库,2-已出库。 为空时默认查询全部数据 + */ + private String stockFlag; + /** + * 出库方式 0-空,2-背书,3-质押,4-解质,5-贴现,6-分包,7-结清, 8-作废。 为空时默认查询全部数据 + */ + private String outInvType; + /** + * 记录状态 NOR-正常,USE-占用,DEL-删除 SUC-完成。为空时默认查询全部数据 + */ + private String lockFlag; + +} diff --git a/src/com/engine/salary/remote/cbs8/request/GetTransactionDetailRequest.java b/src/com/engine/salary/remote/cbs8/request/GetTransactionDetailRequest.java new file mode 100644 index 0000000..b1248d8 --- /dev/null +++ b/src/com/engine/salary/remote/cbs8/request/GetTransactionDetailRequest.java @@ -0,0 +1,75 @@ +package com.engine.salary.remote.cbs8.request; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.List; + +@Data +@EqualsAndHashCode(callSuper = true) +public class GetTransactionDetailRequest extends CBS8BaseRequest { + /** + * 开始日期 格式为yyyy-mm-dd + */ + private String startDate; + /** + * 结束日期 格式为yyyy-mm-dd + */ + private String endDate; + /** + * 日期类型 0-交易日期 + */ + private String dateType; + /** + * 账户列表 查询账户列表,不传默认返回全部 + */ + private List accountNoList; + /** + * 银行类型列表 例如招行CMB,银行类型枚举见附录4.1.5 + */ + private List bankTypeList; + /** + * 币种列表 见附录币种枚举,4.1.1 + */ + private List currencyList; + /** + * 明细来源 + * B:银行,即银行直联 + * U:用户,即手工/ERP接口导入 + * 不传默认查全部 + */ + private String detailedSources; + /** + * 明细类型 1-当日明细 2-历史明细 与明细日期无关,仅标识数据来源银行的不同接口 + */ + private String currentFlag; + /** + * 借贷类型 1-借;2-贷 + */ + private String loanType; + /** + * 账户性质列表 客户在公共设置>基础信息>账户性质查询自定义内容,例如AA-综合户,此处传值“AA” + */ + private List accountNatureList; + /** + * 银行流水号 银行流水号 + */ + private String bankSerialNumber; + /** + * 交易流水号 交易流水号,由CBS8定义生成的唯一标识 + */ + private Long transactionSerialNumber; + /** + * 单位编码列表 客户在公共设置>基础信息>组织机构维护,例如0001-XX科技有限公司,此处传0001 + */ + private List unitCodeList; + /** + * ERP业务参考号 erpSerialNumber + */ + private String erpSerialNumber; + /** + * 款项性质列表 + */ + private List paymentNatureList; + +} diff --git a/src/com/engine/salary/remote/cbs8/response/CBS8BaseResponse.java b/src/com/engine/salary/remote/cbs8/response/CBS8BaseResponse.java new file mode 100644 index 0000000..1a44572 --- /dev/null +++ b/src/com/engine/salary/remote/cbs8/response/CBS8BaseResponse.java @@ -0,0 +1,10 @@ +package com.engine.salary.remote.cbs8.response; + +import lombok.Data; + + +@Data +public class CBS8BaseResponse { + private String code; + private String msg; +} diff --git a/src/com/engine/salary/remote/cbs8/response/CBS8PageInfo.java b/src/com/engine/salary/remote/cbs8/response/CBS8PageInfo.java new file mode 100644 index 0000000..2ae5c7e --- /dev/null +++ b/src/com/engine/salary/remote/cbs8/response/CBS8PageInfo.java @@ -0,0 +1,77 @@ +package com.engine.salary.remote.cbs8.response; + +import lombok.Data; + + +@Data +public class CBS8PageInfo { + /** + * 当前页 + */ + private int pageNum; + /** + * 每页的数量 + */ + private int pageSize; + /** + * 当前页的数量 + */ + private int size; + /** + * 总页数 + */ + private int pages; + /** + * 上一页 + */ + private int prePage; + /** + * 下一页 + */ + private int nextPage; + /** + * 总记录数 + */ + private long total; + /** + * 当前页面第一个元素在数据库中的行号 + */ + private int startRow; + /** + * 当前页面最后一个元素在数据库中的行号 + */ + private int endRow; + /** + * 是否为第一页 + */ + private boolean isFirstPage; + /** + * 是否为最后一页 + */ + private boolean isLastPage; + /** + * 是否有前一页 + */ + private boolean hasPreviousPage; + /** + * 是否有下一页 + */ + private boolean hasNextPage; + /** + * 导航页码数 + */ + private int navigatePages; + /** + * 所有导航页号 + */ + private int[] navigatepageNums; + /** + * 导航条上的第一页 + */ + private int navigateFirstPage; + /** + * 导航条上的最后一页 + */ + private int navigateLastPage; + +} diff --git a/src/com/engine/salary/remote/cbs8/response/GetDtaResponse.java b/src/com/engine/salary/remote/cbs8/response/GetDtaResponse.java new file mode 100644 index 0000000..e2587a9 --- /dev/null +++ b/src/com/engine/salary/remote/cbs8/response/GetDtaResponse.java @@ -0,0 +1,440 @@ +package com.engine.salary.remote.cbs8.response; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.math.BigDecimal; +import java.util.Date; +import java.util.List; + +@Data +@EqualsAndHashCode(callSuper = true) +public class GetDtaResponse extends CBS8BaseResponse { + /** + * 返回数据 + */ + private Body data; + + @Data + public static class Body extends CBS8PageInfo{ + /** + * 明细列表 + */ + private List list; + } + + @Data + public static class Detail { + /** + * 系统票据编号 + */ + private String draftNbr; + /** + * 父系统票据编号 + */ + private String fatDraftNbr; + /** + * 系统票据类型 系统票据类型枚举见附录4.1.3。 + */ + private String billVariety; + /** + * 票据(包)号 + */ + private String billNbr; + /** + * 是否可分包 0-未分包 1-已分包 + */ + private String splitFlag; + /** + * 子票区间起 + */ + private String subBillIntervalStart; + /** + * 子票区间止 + */ + private String subBillIntervalEnd; + /** + * 子票区间 + */ + private String subBillInterval; + + /** + * 票据金额 大于等于0,最多为两位小数且整数位上限为13位。单位(元) + */ + private BigDecimal billAmount; + + /** + * 票据种类 AC01-银承,AC02-商承 + */ + private String billType; + /** + * 出票日期 格式为yyyy-mm-dd + */ + private Date issueDate; + /** + * 到期日期 格式为yyyy-mm-dd + */ + private Date dueDate; + /** + * 票面收票日期 第一手收票人签收日期,格式为yyyy-mm-dd + */ + private Date receiptDate; + /** + * 持票签收日期 票据签收日期,格式为yyyy-mm-dd + */ + private Date holdSignDate; + /** + * 票面不得转让标记 EM00-可转让,EM01-不可转让 + */ + private String trfFlag; + /** + * 背书不得转让标记 EM00-可转让,EM01-不可转让 + */ + private String endorsementTransferFlg; + /** + * 票据状态 票据状态枚举见附录4.1.4 + */ + private String billSts; + /** + * 流通标志 流通标志枚举见附录4.1.5。 + */ + private String billTrsSts; + /** + * 库存状态 0-未入库,1-已入库,2-已出库 + */ + private String stockFlag; + /** + * 出库方式 0-空,2-背书,3-质押,4-解质,5-贴现,6-分包,7-结清, 8-作废 + */ + private String outInvType; + /** + * 风险状态 RS00-非风险票据 RS01-挂失止付 RS02-公示催告 RS03-司法冻结 RS05-争议票据 RS06-除权判决 + */ + private String riskFlag; + /** + * 记录状态 NOR-正常,USE-占用,DEL-删除 SUC-完成 + */ + private String lockFlag; + /** + * 票据来源 + * 1-直联交易 + * 2-系统交易 + * 3-台账登记 + * 4-挑票同步 + * 5-任务同步 + */ + private String draftSource; + /** + * 承兑人类型 RC00-银行(02), RC01-企业(00) ,RC02- 人民银行(01),RC03 -被代理行(A1),RC04-被代理财务公司(A2), RC05-财务公司(03), RC06-证券公司(03) ,RC07-基金公司(03) + */ + private String acceptorType; + /** + * 是否出票保证 1-是,0-否 + */ + private String drawerEnsureFlag; + /** + * 是否承兑保证 1-是,0-否 + */ + private String acceptorEnsureFlag; + /** + * 是否背书保证 1-是,0-否 + */ + private String backEnsureFlag; + /** + * 出票人名称 + */ + private String drawerName; + /** + * 出票人是否内部企业 1-是,0-否 + */ + private String drawerInternalFlag; + /** + * 出票人单位名称 + */ + private String drawerOrganizationName; + /** + * 出票人单位编码 + */ + private String displayDrawerOrganizationCode; + /** + * 出票人是否客商企业 1-是,0-否 + */ + private String drawerCustomerFlg; + /** + * 出票人客商名称 + */ + private String drawerCustomerName; + /** + * 出票人客商编号 填在cbs系统公共设置>基础信息>客商管理>客商信息管理维护的客商编号 + */ + private String drawerCustomerNbr; + /** + * 出票人账户名称 + */ + private String drawerAccountName; + /** + * 出票人账号 + */ + private String drawerAccount; + /** + * 出票人开户行名称 + */ + private String drawerBrnName; + /** + * 出票人联行号 + */ + private String drawerInterbankNbr; + /** + * 收款人名称 + */ + private String payeeName; + /** + * 收款人是否内部企业 1-是,0-否 + */ + private String payeeInternalFlag; + /** + * 收款人单位名称 + */ + private String payeeOrganizationName; + /** + * 收款人单位编码 + */ + private String displayPayeeOrganizationCode; + /** + * 收款人是否客商企业 1-是,0-否 + */ + private String payeeCustomerFlag; + /** + * 收款人客商名称 + */ + private String payeeCustomerName; + /** + * 收款人客商编号 填在cbs系统公共设置>基础信息>客商管理>客商信息管理维护的客商编号 + */ + private String payeeCustomerNbr; + /** + * 收款人账户名称 + */ + private String payeeAccountName; + /** + * 收款人账号 + */ + private String payeeAccount; + /** + * 收款人联行号 + */ + private String payeeInterbankNbr; + /** + * 收款人开户行名称 + */ + private String payeeBrnName; + /** + * 承兑人名称 + */ + private String acceptorName; + /** + * 承兑人是否内部企业 1-是,0-否 + */ + private String acceptorInternalFlag; + /** + * 承兑人单位名称 + */ + private String acceptorOrganizationName; + /** + * 承兑人单位编码 + */ + private String displayAcceptorOrganizationCode; + /** + * 承兑人是否客商企业 1-是,0-否 + */ + private String acceptorCustomerFlg; + /** + * 承兑人客商名称 + */ + private String acceptorCustomerName; + /** + * 承兑人客商编号 填在cbs系统公共设置>基础信息>客商管理>客商信息管理维护的客商编号 + */ + private String acceptorCustomerNbr; + /** + * 承兑人账户名称 + */ + private String acceptorAccountName; + /** + * 承兑人账号 + */ + private String acceptorAccount; + /** + * 承兑人开户行名称 + */ + private String acceptorBrnName; + /** + * 承兑人联行号 + */ + private String acceptorInterbankNbr; + /** + * 到期无条件支付委托/承诺 + */ + private String expireUnconditionalPay; + /** + * 承兑日期 格式为yyyy-mm-dd + */ + private Date acceptorDate; + /** + * 出票人评级主体 + */ + private String drawerRateSubject; + /** + * 出票人信用等级 信用等级枚举见附录4.1.7。 + */ + private String drawerCreditRating; + /** + * 出票人评级到期日 格式为yyyy-mm-dd + */ + private Date drawerRateDueDate; + /** + * 承兑人评级主体 + */ + private String acceptorRateSubject; + /** + * 承兑人信用等级 信用等级枚举见附录4.1.7。 + */ + private String acceptorCreditRating; + /** + * 承兑人评级到期日 格式为yyyy-mm-dd + */ + private Date acceptorRateDueDate; + /** + * 持票人名称 + */ + private String holdName; + /** + * 持票人单位名称 + */ + private String holdOrganizationName; + /** + * 持票人单位编码 + */ + private String displayHoldOrganizationCode; + /** + * 持票人账户名称 + */ + private String holdAccountName; + /** + * 持票人银行类型 见附录4.1.1.银行类型枚举 + */ + private String holdBankType; + /** + * 持票人账号 + */ + private String holdAccount; + /** + * 持票人开户行名称 + */ + private String holdBrnName; + /** + * 持票人联行号 + */ + private String holdInterbankNbr; + /** + * 前手名称 + */ + private String preName; + /** + * 前手是否内部企业 1-是,0-否 + */ + private String preInternalFlag; + /** + * 前手单位名称 + */ + private String preOrganizationName; + /** + * 前手单位编码 + */ + private String displayPreOrganizationCode; + /** + * 前手是否客商企业 1-是,0-否 + */ + private String preCustomerFlag; + /** + * 前手客商名称 + */ + private String preCustomerName; + /** + * 前手客商编号 填在cbs系统公共设置>基础信息>客商管理>客商信息管理维护的客商编号 + */ + private String preCustomerNbr; + /** + * 前手账户名称 + */ + private String preAccountName; + /** + * 前手账号 + */ + private String preAccount; + /** + * 前手开户行名称 + */ + private String preBrnName; + /** + * 前手联行号 + */ + private String preInterbankNbr; + /** + * 后手名称 + */ + private String nextName; + /** + * 后手是否内部企业 1-是,0-否 + */ + private String nextInternalFlag; + /** + * 后手单位名称 + */ + private String nextOrganizationName; + /** + * 后手单位编码 + */ + private String displayNextOrganizationCode; + /** + * 后手是否客商企业 1-是,0-否 + */ + private String nextCustomerFlag; + /** + * 后手客商名称 + */ + private String nextCustomerName; + /** + * 后手客商编号 填在cbs系统公共设置>基础信息>客商管理>客商信息管理维护的客商编号 + */ + private String nextCustomerNbr; + /** + * 后手账户名称 + */ + private String nextAccountName; + /** + * 后手账号 + */ + private String nextAccount; + /** + * 后手开户行名称 + */ + private String nextBrnName; + /** + * 后手联行号 + */ + private String nextInterbankNbr; + /** + * 签收方式 1-收票签收,2-背书签收 + */ + private String signMethod; + /** + * 同步状态 + * INIT-未同步 + * ING-同步中 + * SUC-同步完成 + * FAL-同步失败 + */ + private String asyncStatus; + } +} diff --git a/src/com/engine/salary/remote/cbs8/response/GetTokenResponse.java b/src/com/engine/salary/remote/cbs8/response/GetTokenResponse.java new file mode 100644 index 0000000..8aa0c4d --- /dev/null +++ b/src/com/engine/salary/remote/cbs8/response/GetTokenResponse.java @@ -0,0 +1,20 @@ +package com.engine.salary.remote.cbs8.response; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Data +@EqualsAndHashCode(callSuper = true) +public class GetTokenResponse extends CBS8BaseResponse { + /** + * 返回数据 + */ + private Body data; + + @Data + public static class Body { + private Integer expires; + private String token; + private String token_type; + } +} diff --git a/src/com/engine/salary/remote/cbs8/response/GetTransactionDetailResponse.java b/src/com/engine/salary/remote/cbs8/response/GetTransactionDetailResponse.java new file mode 100644 index 0000000..61b2f5d --- /dev/null +++ b/src/com/engine/salary/remote/cbs8/response/GetTransactionDetailResponse.java @@ -0,0 +1,268 @@ +package com.engine.salary.remote.cbs8.response; + +import com.alibaba.fastjson.JSON; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.math.BigDecimal; +import java.util.Date; +import java.util.List; + +@Data +@EqualsAndHashCode(callSuper = true) +public class GetTransactionDetailResponse extends CBS8BaseResponse { + /** + * 返回数据 + */ + private Body data; + + @Data + @EqualsAndHashCode(callSuper = true) + public static class Body extends CBS8PageInfo{ + /** + * 明细列表 + */ + private List list; + } + + @Data + public static class Detail { + /** + * 银行账号 + */ + private String accountNo; + /** + * 账户名称 + */ + private String accountName; + /** + * 银行类型 银行类型见附录4.1.5 + */ + private String bankType; + /** + * 开户行名称 + */ + private String openBank; + /** + * 交易日期 时间戳 + */ + private Date bankTransactionDate; + /** + * 银行流水号 + */ + private String bankSerialNumber; + /** + * 交易流水号 + */ + private Long transactionSerialNumber; + /** + * 币种 币种枚举见附录4.1.1 + */ + private String currency; + /** + * 借贷类型 1-借;2-贷 + */ + private String loanType; + + /** + * 发生额 + */ + private BigDecimal incurredAmount; + + /** + * 交易后余额 + */ + private BigDecimal accountBalance; + /** + * 用途 + */ + private String purpose; + /** + * 摘要 + */ + private String digest; + /** + * 对方账号 + */ + private String oppositeAccount; + /** + * 对方户名 + */ + private String oppositeName; + /** + * 对方开户行 + */ + private String oppositeOpeningBank; + /** + * 关联客户号 + */ + private String associatedCustomerNumber; + /** + * 客商编号 客户在公共设置>基础信息>客商管理登记的客商编码,例如C002-XX供应链管理。此处返回C002。 + */ + private String merchantNumber; + /** + * 客商名称 客户在公共设置>基础信息>客商管理登记的客商名称,例如C002-XX供应链管理。此处返回XX供应链管理。 + */ + private String merchantName; + /** + * 起息日 时间戳 + */ + private Date valueDate; + /** + * 交易代码 见附录枚举4.1.8 + */ + private String transactionCode; + /** + * 款项性质匹配标记 1-未匹配;2-手工处理;3-系统处理;4-匹配失败 + */ + private String paymentNatureFlag; + /** + * 款项性质代码 客户在公共设置>基础信息>款项性质查询登记的款项性质,例如A001-往来款,此处返回A001 + */ + private String paymentNature; + /** + * 明细来源 B:银行,即银行直联 U:用户,即手工/ERP接口导入 + */ + private String detailSource; + /** + * 明细类型 1-当日明细 2-历史明细 与明细日期无关,仅标识数据来源银行的不同接口。 + */ + private String detailType; + /** + * 账户状态 0-正常,1-销户 + */ + private String accountStatus; + /** + * 账户性质 客户在公共设置>基础信息>账户性质查询自定义内容,例如AA-综合户,此处返回值“AA” + */ + private String accountNature; + /** + * 对账码 对账码,是CBS8系统通过一定规则匹配交易明细与回单或交易明细与支付成功后生成的对账码 + */ + private String checkCode; + /** + * 单位编码 客户在公共设置>基础信息>组织机构维护,例如0001-XX科技有限公司,此处返回0001 + */ + private String unitCode; + /** + * 单位名称 + */ + private String unitName; + /** + * 备注 + */ + private String remark; + /** + * ERP业务参考号 此字段为支付接口的referenceNum(业务参考号) + */ + private String erpSerialNumber; + /** + * 支付申请备注1 + */ + private String PayApplyRemark1; + /** + * 支付申请备注2 + */ + private String PayApplyRemark2; + /** + * 支付申请备注3 + */ + private String PayApplyRemark3; + /** + * 交易识别码 + */ + private String corporateIdentityCode; + /** + * 预留字段1 + */ + private String reserveField1; + /** + * 预留字段2 + */ + private String reserveField2; + /** + * 预留字段3 + */ + private String reserveField3; + /** + * 预留字段4 + */ + private String reserveField4; + /** + * 扩展字段信息 fieldKey:字段KEY title : 标题 content : 字段内容 + */ + private List extensionMsg; + /** + * 附言 + */ + private String postscript; + /** + * 凭证编号 + */ + private String voucherCode; + /** + * 记账日期 + */ + private Date bookingDate; + /** + * 银行个性化字段1 银行个性化字段见附录4.1.20 + */ + private String personalizeField1; + /** + * 银行个性化字段2 银行个性化字段见附录4.1.20 + */ + private String personalizeField2; + /** + * 银行个性化字段3 银行个性化字段见附录4.1.20 + */ + private String personalizeField3; + /** + * 银行个性化字段4 银行个性化字段见附录4.1.20 + */ + private String personalizeField4; + /** + * 银行个性化字段5 银行个性化字段见附录4.1.20 + */ + private String personalizeField5; + /** + * 虚拟户账号 + */ + private String virtualAccount; + /** + * 虚拟户账号名称 + */ + private String virtualAccountName; + /** + * 交易类型 + */ + private String protocolType; + /** + * 交易类型名称 + */ + private String protocolTypeName; + /** + * 母/子公司账号 + */ + private String parentSubCompanyAccount; + /** + * 母/子公司名称母/子公司名称 + */ + private String parentSubCompanyName; + /** + * 通过9091->公共设置->业务配置-> + * 通用参数管理->账户管理->erp_trans_detail_mark_return_flag(交易明细查询ERP接口是否返回关联业务表字段)设置可开启字段返回,参数关闭时字段为null + * id:关联业务id + * bizCode:关联业务(见附录4.1.22) + * bizFlag:关联业务标记 + * (0-手工标记 + * 1-按规则标记 + * 2-模块关联标记 + * 3-ERP接口标记) + * moduleCode:关联模块 + * menuName:关联菜单 + * bizOrderNo:业务编号 + */ + private List markingList; + } +} diff --git a/src/com/engine/salary/remote/cbs8/util/SM2Util.java b/src/com/engine/salary/remote/cbs8/util/SM2Util.java new file mode 100644 index 0000000..358a82c --- /dev/null +++ b/src/com/engine/salary/remote/cbs8/util/SM2Util.java @@ -0,0 +1,215 @@ +package com.engine.salary.remote.cbs8.util; + +import lombok.extern.slf4j.Slf4j; +import org.bouncycastle.asn1.*; +import org.bouncycastle.crypto.engines.SM2Engine; +import org.bouncycastle.crypto.params.*; +import org.bouncycastle.crypto.signers.SM2Signer; +import org.bouncycastle.jce.ECNamedCurveTable; +import org.bouncycastle.jce.spec.ECParameterSpec; +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.util.encoders.Hex; + +import java.io.ByteArrayInputStream; +import java.math.BigInteger; +import java.security.SecureRandom; +import java.util.Enumeration; + +/** + * @author: KeXue + * @time: 2022/8/25 + * @description: SM2加解密工具类 + */ +@Slf4j +public class SM2Util { + + private SM2Util() { + throw new IllegalStateException("Utility class"); + } + + private static final String STD_NAME = "sm2p256v1"; + + /** + * SM2加密算法 + * + * @param publicKey 公钥 + * @param data 明文数据 + * @return + */ + public static byte[] encrypt(String publicKey, byte[] data) { + ECPublicKeyParameters ecPublicKeyParameters = encodePublicKey(Hex.decode(publicKey)); + SM2Engine engine = new SM2Engine(); + engine.init(true, new ParametersWithRandom(ecPublicKeyParameters, new SecureRandom())); + + byte[] bytes = null; + try { + byte[] cipherText = engine.processBlock(data, 0, data.length); + bytes = C1C2C3ToC1C3C2(cipherText); + } catch (Exception e) { + log.warn("SM2加密时出现异常:" + e.getMessage()); + } + return bytes; + } + + /** + * SM2解密算法 + * + * @param privateKey 私钥 + * @param cipherData 密文数据 + * @return + */ + public static byte[] decrypt(String privateKey, byte[] cipherData) { + ECPrivateKeyParameters ecPrivateKeyParameters = encodePrivateKey(Hex.decode(privateKey)); + SM2Engine engine = new SM2Engine(); + engine.init(false, ecPrivateKeyParameters); + + byte[] bytes = null; + try { + cipherData = C1C3C2ToC1C2C3(cipherData); + bytes = engine.processBlock(cipherData, 0, cipherData.length); + } catch (Exception e) { + log.warn("SM2解密时出现异常:" + e.getMessage()); + } + return bytes; + } + + /** + * 签名算法 + * + * @param privateKey 私钥 + * @param data 明文数据 + * @return + */ + public static byte[] sign(String privateKey, byte[] data) { + ECPrivateKeyParameters ecPrivateKeyParameters = encodePrivateKey(hexToByte(privateKey)); + SM2Signer signer = new SM2Signer(); + ParametersWithID parameters = new ParametersWithID(ecPrivateKeyParameters, "1234567812345678".getBytes()); + signer.init(true, parameters); + signer.update(data, 0, data.length); + + byte[] signature = null; + try { + signature = decodeDERSignature(signer.generateSignature()); + } catch (Exception e) { + log.warn("SM2签名时出现异常:" + e.getMessage()); + } + return signature; + } + + private static byte[] hexToByte(String hex) + throws IllegalArgumentException { + if (hex.length() % 2 != 0) { + throw new IllegalArgumentException(); + } + char[] arr = hex.toCharArray(); + byte[] b = new byte[hex.length() / 2]; + for (int i = 0, j = 0, l = hex.length(); i < l; i++, j++) { + String swap = "" + arr[i++] + arr[i]; + int byteInt = Integer.parseInt(swap, 16) & 0xFF; + b[j] = BigInteger.valueOf(byteInt).byteValue(); + } + return b; + } + + private static byte[] C1C2C3ToC1C3C2(byte[] cipherText) throws Exception { + if (cipherText != null && cipherText.length >= 97) { + byte[] bytes = new byte[cipherText.length]; + System.arraycopy(cipherText, 0, bytes, 0, 65); + System.arraycopy(cipherText, cipherText.length - 32, bytes, 65, 32); + System.arraycopy(cipherText, 65, bytes, 97, cipherText.length - 97); + return bytes; + } else { + throw new Exception("SM2 cipher text error, must be more than 96 bytes and in the format C1||C3||C2."); + } + } + + private static byte[] C1C3C2ToC1C2C3(byte[] cipherText) throws Exception { + if (cipherText != null && cipherText.length >= 97) { + byte[] bytes = new byte[cipherText.length]; + System.arraycopy(cipherText, 0, bytes, 0, 65); + System.arraycopy(cipherText, 97, bytes, 65, cipherText.length - 97); + System.arraycopy(cipherText, 65, bytes, cipherText.length - 32, 32); + return bytes; + } else { + throw new Exception("SM2 cipher text error, must be more than 96 bytes and in the format C1||C3||C2."); + } + } + + private static ECPublicKeyParameters encodePublicKey(byte[] value) { + byte[] x = new byte[32]; + byte[] y = new byte[32]; + System.arraycopy(value, 1, x, 0, 32); + System.arraycopy(value, 33, y, 0, 32); + BigInteger X = new BigInteger(1, x); + BigInteger Y = new BigInteger(1, y); + ECPoint Q = getSM2Curve().createPoint(X, Y); + return new ECPublicKeyParameters(Q, getECDomainParameters()); + } + + private static ECCurve getSM2Curve() { + ECParameterSpec spec = ECNamedCurveTable.getParameterSpec(STD_NAME); + return spec.getCurve(); + } + + private static ECPrivateKeyParameters encodePrivateKey(byte[] value) { + BigInteger d = new BigInteger(1, value); + return new ECPrivateKeyParameters(d, getECDomainParameters()); + } + + private static ECDomainParameters getECDomainParameters() { + ECParameterSpec spec = ECNamedCurveTable.getParameterSpec(STD_NAME); + return new ECDomainParameters(spec.getCurve(), spec.getG(), spec.getN(), spec.getH(), spec.getSeed()); + } + + private static byte[] decodeDERSignature(byte[] signature) { + ASN1InputStream stream = new ASN1InputStream(new ByteArrayInputStream(signature)); + + byte[] bytes = new byte[64]; + try { + ASN1Sequence primitive = (ASN1Sequence) stream.readObject(); + Enumeration enumeration = primitive.getObjects(); + BigInteger R = ((ASN1Integer) enumeration.nextElement()).getValue(); + BigInteger S = ((ASN1Integer) enumeration.nextElement()).getValue(); + byte[] r = format(R.toByteArray()); + byte[] s = format(S.toByteArray()); + System.arraycopy(r, 0, bytes, 0, 32); + System.arraycopy(s, 0, bytes, 32, 32); + } catch (Exception e) { + log.warn("decodeDERSignature时出现异常:" + e.getMessage()); + } + return bytes; + } + + public static byte[] encodeDERSignature(byte[] signature) { + byte[] r = new byte[32]; + byte[] s = new byte[32]; + System.arraycopy(signature, 0, r, 0, 32); + System.arraycopy(signature, 32, s, 0, 32); + ASN1EncodableVector vector = new ASN1EncodableVector(); + vector.add(new ASN1Integer(new BigInteger(1, r))); + vector.add(new ASN1Integer(new BigInteger(1, s))); + + byte[] encoded = null; + try { + encoded = (new DERSequence(vector)).getEncoded(); + } catch (Exception e) { + log.warn("encodeDERSignature时出现异常:" + e.getMessage()); + } + return encoded; + } + + private static byte[] format(byte[] value) { + if (value.length == 32) { + return value; + } else { + byte[] bytes = new byte[32]; + if (value.length > 32) { + System.arraycopy(value, value.length - 32, bytes, 0, 32); + } else { + System.arraycopy(value, 0, bytes, 32 - value.length, value.length); + } + return bytes; + } + } +} diff --git a/src/com/engine/salary/remote/cbs8/util/db/IdGenerator.java b/src/com/engine/salary/remote/cbs8/util/db/IdGenerator.java new file mode 100644 index 0000000..0722256 --- /dev/null +++ b/src/com/engine/salary/remote/cbs8/util/db/IdGenerator.java @@ -0,0 +1,51 @@ +package com.engine.salary.remote.cbs8.util.db; + +import java.util.UUID; +import java.util.concurrent.atomic.AtomicLong; + +public class IdGenerator { + private static AtomicLong next = new AtomicLong(1L); + public static final int ID_LENGTH_36 = 36; + + public IdGenerator() { + } + + public static long generate() { + return System.currentTimeMillis() + next.getAndIncrement(); + } + + public static String generateId() { + return System.currentTimeMillis() + String.valueOf(next.getAndIncrement()); + } + + public static String generateStrId() { + return System.currentTimeMillis() + generateStrId(36); + } + + public static String generateStrId(int idLength) { + if (idLength >= 1 && idLength <= 36) { + char[] srcChars = new char[]{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'g', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'}; + char[] chars = new char[idLength]; + + for(int i = 0; i < idLength; ++i) { + if (i != 8 && i != 13 && i != 18 && i != 23) { + if (i == 0) { + chars[i] = srcChars[(int)(Math.random() * 26.0D) % 26]; + } else { + chars[i] = srcChars[(int)(Math.random() * 36.0D) % 36]; + } + } else { + chars[i] = '_'; + } + } + + return new String(chars); + } else { + return ""; + } + } + + public static String getUUID() { + return UUID.randomUUID().toString().replaceAll("-", ""); + } +} diff --git a/src/com/engine/salary/remote/cbs8/util/db/MapperProxyFactory.java b/src/com/engine/salary/remote/cbs8/util/db/MapperProxyFactory.java new file mode 100644 index 0000000..f639b7a --- /dev/null +++ b/src/com/engine/salary/remote/cbs8/util/db/MapperProxyFactory.java @@ -0,0 +1,79 @@ +/** + * + */ +package com.engine.salary.remote.cbs8.util.db; + +import org.apache.ibatis.session.SqlSession; +import weaver.conn.mybatis.MyBatisFactory; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; + +/** + * mapper代理工厂 + *

Copyright: Copyright (c) 2022

+ *

Company: 泛微软件

+ * + * @author qiantao + * @version 1.0 + **/ +public class MapperProxyFactory implements InvocationHandler { + private Class clazz; + private boolean enableTransactions = false; + private SqlSession session; + + public MapperProxyFactory(Class clazz) { + this.clazz = clazz; + } + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Exception { + this.session = MyBatisFactory.sqlSessionFactory.openSession(); + try { + Object target = session.getMapper(clazz); + return method.invoke(target, args); + } finally { + if (!enableTransactions) { + session.commit(); + session.close(); + } + } + } + + public Object getProxy() { + ClassLoader loader = Thread.currentThread().getContextClassLoader(); + Class[] interfaces = new Class[1]; + interfaces[0] = this.clazz; + return Proxy.newProxyInstance(loader, interfaces, this); + } + + public Object getProxy(boolean enableTransactions) { + this.enableTransactions = enableTransactions; + return this.getProxy(); + } + + public void commit() { + if (this.session != null) { + this.session.commit(); + this.session.close(); + } + } + + public void rollback() { + if (this.session != null) { + this.session.rollback(); + this.session.close(); + } + } + + public static T getProxy(Class clazz) { + MapperProxyFactory handle = new MapperProxyFactory(clazz); + return (T) handle.getProxy(); + } + + public static T getProxy(Class clazz, boolean enableTransactions) { + MapperProxyFactory handle = new MapperProxyFactory(clazz); + return (T) handle.getProxy(enableTransactions); + } +} diff --git a/src/com/engine/salary/remote/cbs8/xml/XStreamUtil.java b/src/com/engine/salary/remote/cbs8/xml/XStreamUtil.java new file mode 100644 index 0000000..5116f59 --- /dev/null +++ b/src/com/engine/salary/remote/cbs8/xml/XStreamUtil.java @@ -0,0 +1,68 @@ +package com.engine.salary.remote.cbs8.xml; + +import cn.hutool.core.util.StrUtil; +import com.thoughtworks.xstream.XStream; +import com.thoughtworks.xstream.io.HierarchicalStreamDriver; +import com.thoughtworks.xstream.io.naming.NoNameCoder; +import com.thoughtworks.xstream.io.xml.DomDriver; +import com.thoughtworks.xstream.security.AnyTypePermission; + +import java.util.Objects; + +public class XStreamUtil { + //new NoNameCoder() 解决 _ 被序列化成 __ 的问题 + private static final NoNameCoder noNameCoder = new NoNameCoder(); + + private static XStream xStream = new XStream(new DomDriver("UTF-8", noNameCoder)); + + private static XStream getxStream(HierarchicalStreamDriver driver){ + if(Objects.isNull(driver)){ + return xStream; + } + return new XStream(driver); + } + + public static T unmarshal(String pkgName, Class cla, String xmlStr, HierarchicalStreamDriver driver) { + xStream = getxStream(driver); + + if (StrUtil.isEmpty(pkgName)) { + //高版本为了解决安全漏洞,增加了白名单机制, 如果不设置这个权限可能会报错 + xStream.addPermission(AnyTypePermission.ANY); + } else { + //设置允许解析的包,如果不想设置可以用 addPermission(AnyTypePermission.ANY) 代替 + xStream.allowTypesByWildcard(new String[]{pkgName}); + } + + //支持注解,不然使用的XStream注解不会生效且不报错 + xStream.autodetectAnnotations(true); + xStream.processAnnotations(cla); + + //忽略未知属性, 如果不添加这个,当Xml报文中出现实体中没有的属性时会报错 No such field + xStream.ignoreUnknownElements(); + + return (T) xStream.fromXML(xmlStr); + } + + public static T unmarshal(String pkgName, Class cla, String xmlStr) { + return unmarshal(pkgName, cla, xmlStr, null); + } + + public static T unmarshal(Class cla, String xmlStr) { + return unmarshal(null, cla, xmlStr, null); + } + + public static String marshal(Object o, DomDriver driver) { + xStream = getxStream(driver); + + //支持注解,不然使用的 @XStreamAlias() 注解不会生效而且不会报错 + xStream.autodetectAnnotations(true); + + //注册自定义时间转换器,使得XStream全局支持LocalDateTime, 或者不在这里注册,使用@XStreamConverter(LocalDateTimeConverter.class)注解在单一字段上 +// xStream.registerConverter(new LocalDateTimeConverter()); + return xStream.toXML(o); + } + + public static String marshal(Object o){ + return marshal(o, null); + } +} diff --git a/src/com/engine/salary/timer/SyncCBSAccountDetailsJob.java b/src/com/engine/salary/timer/SyncCBSAccountDetailsJob.java new file mode 100644 index 0000000..b2c7fa5 --- /dev/null +++ b/src/com/engine/salary/timer/SyncCBSAccountDetailsJob.java @@ -0,0 +1,285 @@ +package com.engine.salary.timer; + +import cn.hutool.core.util.StrUtil; +import com.engine.salary.exception.CBS8RunTimeException; +import com.engine.salary.mapper.SQLMapper; +import com.engine.salary.mapper.cbs.UfHkrdzbMapper; +import com.engine.salary.remote.cbs8.client.AccountManagementClient; +import com.engine.salary.remote.cbs8.config.EBS2ECConfig; +import com.engine.salary.remote.cbs8.po.UfHkrdzbPO; +import com.engine.salary.remote.cbs8.request.GetTransactionDetailRequest; +import com.engine.salary.remote.cbs8.response.GetTransactionDetailResponse; +import com.engine.salary.remote.cbs8.JsonUtil; +import com.engine.salary.remote.cbs8.SalaryEntityUtil; +import com.engine.salary.remote.cbs8.util.db.MapperProxyFactory; +import com.thoughtworks.xstream.XStream; +import com.thoughtworks.xstream.security.AnyTypePermission; +import lombok.extern.slf4j.Slf4j; +import weaver.conn.RecordSet; +import weaver.formmode.setup.ModeRightInfo; +import weaver.general.GCONST; +import weaver.general.TimeUtil; +import weaver.hrm.User; +import weaver.interfaces.schedule.BaseCronJob; + +import java.io.File; +import java.text.SimpleDateFormat; +import java.time.LocalDate; +import java.util.*; +import java.util.stream.Collectors; + +/** + * 获取境内账户明细 + *

Copyright: Copyright (c) 2024

+ *

Company: 泛微软件

+ * + * @author qiantao + * @version 1.0 + **/ +@Slf4j +public class SyncCBSAccountDetailsJob extends BaseCronJob { + + /** + * 开始日期 格式为yyyy-mm-dd + */ + private String startDate; + /** + * 结束日期 格式为yyyy-mm-dd + */ + private String endDate; + /** + * 借贷类型 1-借;2-贷 + */ + private String loanType; + /** + * 款项性质列表 + */ + private String paymentNatureList; + + /** + * 日期类型 0-交易日期 + */ + private String dateType; + /** + * 账户列表 查询账户列表,不传默认返回全部 + */ + private String accountNoList; + /** + * 银行类型列表 例如招行CMB,银行类型枚举见附录4.1.5 + */ + private String bankTypeList; + /** + * 币种列表 见附录币种枚举,4.1.1 + */ + private String currencyList; + /** + * 明细来源 + * B:银行,即银行直联 + * U:用户,即手工/ERP接口导入 + * 不传默认查全部 + */ + private String detailedSources; + /** + * 明细类型 1-当日明细 2-历史明细 与明细日期无关,仅标识数据来源银行的不同接口 + */ + private String currentFlag; + + /** + * 账户性质列表 客户在公共设置>基础信息>账户性质查询自定义内容,例如AA-综合户,此处传值“AA” + */ + private String accountNatureList; + /** + * 银行流水号 银行流水号 + */ + private String bankSerialNumber; + /** + * 交易流水号 交易流水号,由CBS8定义生成的唯一标识 + */ + private Long transactionSerialNumber; + /** + * 单位编码列表 客户在公共设置>基础信息>组织机构维护,例如0001-XX科技有限公司,此处传0001 + */ + private String unitCodeList; + /** + * ERP业务参考号 erpSerialNumber + */ + private String erpSerialNumber; + + private SQLMapper getSQLMapper() { + return MapperProxyFactory.getProxy(SQLMapper.class); + } + + private UfHkrdzbMapper getUfHkrdzbMapper() { + return MapperProxyFactory.getProxy(UfHkrdzbMapper.class); + } + + @Override + public void execute() { + User user = new User(); + user.setUid(1); + user.setLoginid("sysadmin"); + + try { + GetTransactionDetailRequest requestParam = new GetTransactionDetailRequest(); + requestParam.setCurrentPage(1); + requestParam.setPageSize(1000); + if (StrUtil.isNotBlank(startDate) && StrUtil.isNotBlank(endDate)) { + requestParam.setStartDate(startDate); + requestParam.setEndDate(endDate); + } else { + String nowDate = LocalDate.now().toString(); + requestParam.setStartDate(nowDate); + requestParam.setEndDate(nowDate); + } + requestParam.setLoanType(loanType); + requestParam.setPaymentNatureList(paymentNatureList == null ? null :Arrays.stream(paymentNatureList.split(",")).collect(Collectors.toList())); + requestParam.setDateType(dateType); + requestParam.setAccountNoList(accountNoList== null ? null :Arrays.stream(accountNoList.split(",")).collect(Collectors.toList())); + requestParam.setBankTypeList(bankTypeList== null ? null :Arrays.stream(bankTypeList.split(",")).collect(Collectors.toList())); + requestParam.setCurrencyList(currencyList== null ? null :Arrays.stream(currencyList.split(",")).collect(Collectors.toList())); + requestParam.setDetailedSources(detailedSources); + requestParam.setCurrentFlag(currentFlag); + requestParam.setAccountNatureList(accountNatureList== null ? null :Arrays.stream(accountNatureList.split(",")).collect(Collectors.toList())); + requestParam.setBankSerialNumber(bankSerialNumber); + requestParam.setTransactionSerialNumber(transactionSerialNumber); + requestParam.setUnitCodeList(unitCodeList== null ? null :Arrays.stream(unitCodeList.split(",")).collect(Collectors.toList())); + requestParam.setErpSerialNumber(erpSerialNumber); + + //查询前1000条数据 + AccountManagementClient accountManagementClient = new AccountManagementClient(); + GetTransactionDetailResponse response = accountManagementClient.transactionDetailQuery(requestParam); + List list = response.getData().getList(); + + //判断是否还存在数据,递归查询 + boolean hasNextPage = response.getData().isHasNextPage(); + int nextPage = response.getData().getNextPage(); + while (hasNextPage) { + requestParam.setCurrentPage(nextPage); + GetTransactionDetailResponse nextPageResponse = accountManagementClient.transactionDetailQuery(requestParam); + List pageData = nextPageResponse.getData().getList(); + list.addAll(pageData); + hasNextPage = nextPageResponse.getData().isHasNextPage(); + nextPage = nextPageResponse.getData().getNextPage(); + } + + //加载cbs配置 + XStream xStream = new XStream(); + String resource = GCONST.getRootPath() + "WEB-INF" + File.separatorChar + "CBS2ECConfig.xml"; + File file = new File(resource); + xStream.addPermission(AnyTypePermission.ANY); + xStream.processAnnotations(EBS2ECConfig.class); + EBS2ECConfig dto = (EBS2ECConfig) xStream.fromXML(file); + EBS2ECConfig.Table table = dto.getTables().get(0); + Integer modeId = table.getModeId(); + String tableName = table.getKey(); + + //获取已存在的数据 + EBS2ECConfig.Table.Field uniqueField = table.getFields().stream().filter(EBS2ECConfig.Table.Field::isUnique).findFirst().orElse(null); + if (uniqueField == null) { + throw new CBS8RunTimeException("未设置唯一标识字段"); + } + String uniqueKey = uniqueField.getKey(); + String uniqueEbsKey = uniqueField.getEbsKey(); + List uniqueDataKeys = getSQLMapper().listString(String.format("select %s from %s", uniqueKey, tableName)); + + //获取汇款人与办事处的对照数据 + List ufHkrdzbPOS = getUfHkrdzbMapper().listAll(); + Map customerDepartmentMap = SalaryEntityUtil.convert2Map(ufHkrdzbPOS, UfHkrdzbPO::getKhmc, UfHkrdzbPO::getDepartmentId); + + for (GetTransactionDetailResponse.Detail detail : list) { + Map detailMap = JsonUtil.parseMap(detail, String.class); + String uniqueData = detailMap.get(uniqueEbsKey); + if (StrUtil.isBlank(uniqueData)) { + log.warn("跳过cbs交易数据,唯一标识返回空,uniqueKey:{},uniqueEbsKey:{}", uniqueKey, uniqueEbsKey); + continue; + } + if (uniqueDataKeys.contains(uniqueData)) { + log.warn("跳过cbs交易数据,数据已存在,uniqueKey:{},uniqueEbsKey:{},值{}", uniqueKey, uniqueEbsKey, uniqueData); + continue; + } + + List fields = new ArrayList() {{ + //建模默认字段 + add("formmodeid"); + add("modedatacreater"); + add("modedatacreatertype"); + add("modedatacreatedate"); + add("modedatacreatetime"); + }}; + + String currDate = TimeUtil.getCurrentDateString(); + SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss"); + String currTime = sdf.format(new Date()); + List values = new ArrayList() {{ + add(modeId); + add(1); + add(0); + add(String.format("'%s'", currDate)); + add(String.format("'%s'", currTime)); + }}; + + String hkr = ""; + for (EBS2ECConfig.Table.Field field : table.getFields()) { + //数据库字段 + String fieldName = field.getKey(); + fields.add(fieldName); + // 接口值 + String value = detailMap.getOrDefault(field.getEbsKey(), ""); + values.add(String.format("'%s'", detailMap.getOrDefault(field.getEbsKey(), ""))); + + //汇款人 + if ("hkr".equals(fieldName)) { + hkr = value; + } + } + + //业务逻辑字段,收款类型,默认是银行存款 + fields.add("sklx"); + values.add(0); + + /* + * 认领逻辑, + * 根据汇款人去表里匹配【汇款人与办事处的对照表】 + * 如果能匹配到数据,则状态改为已自动认领,同时把办事处(部门)的值同步到办事处字段中,是否系统自动认领同步为是; + * 如对照标准无数据,则状态为:未认领; + */ + Integer departmentId = customerDepartmentMap.get(hkr); + if(departmentId!=null){ + //认领 + fields.add("zt"); + values.add(1); + + //是否系统认领 + fields.add("sfxtzdrl"); + values.add(1); + + //办事处 + fields.add("szbm"); + values.add(departmentId); + }else { + //未认领 + fields.add("zt"); + values.add(0); + } + + String sql = String.format("insert into %s (%s) values (%s)", tableName, String.join(",", fields), values.stream().map(Object::toString).collect(Collectors.joining(","))); + RecordSet rs = new RecordSet(); + rs.execute(sql); + + if (modeId != null) { + rs.executeQuery("select max(id) from " + tableName); + int mainId = 0; + if (rs.next()) { + mainId = rs.getInt(1); + } + ModeRightInfo ModeRightInfo = new ModeRightInfo(); + ModeRightInfo.setNewRight(true); + ModeRightInfo.editModeDataShare(1, modeId, mainId); + } + } + } catch (Exception e) { + log.error("获取CBS交易信息失败", e); + throw new CBS8RunTimeException("获取CBS交易信息失败," + e.getMessage(), e); + } + } +} diff --git a/src/com/engine/salary/timer/SyncCBSBillPoolJob.java b/src/com/engine/salary/timer/SyncCBSBillPoolJob.java new file mode 100644 index 0000000..c78142f --- /dev/null +++ b/src/com/engine/salary/timer/SyncCBSBillPoolJob.java @@ -0,0 +1,311 @@ +package com.engine.salary.timer; + +import cn.hutool.core.util.StrUtil; +import com.engine.salary.exception.CBS8RunTimeException; +import com.engine.salary.mapper.SQLMapper; +import com.engine.salary.remote.cbs8.client.BillManagementClient; +import com.engine.salary.remote.cbs8.config.EBS2ECConfig; +import com.engine.salary.remote.cbs8.request.GetDtaRequest; +import com.engine.salary.remote.cbs8.response.GetDtaResponse; +import com.engine.salary.remote.cbs8.JsonUtil; +import com.engine.salary.remote.cbs8.util.db.MapperProxyFactory; +import com.thoughtworks.xstream.XStream; +import com.thoughtworks.xstream.security.AnyTypePermission; +import lombok.extern.slf4j.Slf4j; +import weaver.conn.RecordSet; +import weaver.formmode.setup.ModeRightInfo; +import weaver.general.GCONST; +import weaver.general.TimeUtil; +import weaver.general.Util; +import weaver.hrm.User; +import weaver.interfaces.schedule.BaseCronJob; + +import java.io.File; +import java.math.BigDecimal; +import java.text.SimpleDateFormat; +import java.time.LocalDate; +import java.util.*; +import java.util.stream.Collectors; + +/** + * 获取票据池 + *

Copyright: Copyright (c) 2024

+ *

Company: 泛微软件

+ * + * @author qiantao + * @version 1.0 + **/ +@Slf4j +public class SyncCBSBillPoolJob extends BaseCronJob { + + /** + * 持票人单位编码 多选。填在cbs系统公共设置>基础信息>组织机构维护的单位编码 + */ + private String displayHoldOrganizationCodeList; + /** + * 持票人账号 多选。 + */ + private String holdAccountList; + + /** + * 系统票据类型 多选。按附录4.1.3票据类型-票据状态-流通标志级联关系表选择后按顺序填写。系统票据类型枚举见附录4.1.3。票据状态枚举见附录4.1.4。流通标志枚举见附录4.1.5。 + */ + private String billVarietyList; + + /** + * 出票日期起 格式为yyyy-mm-dd,出票日期起止不允许一边有值一边无值,日期间隔最大为一年 + */ + private String issueDateStart; + + /** + * 出票日期止 格式为yyyy-mm-dd,出票日期起止不允许一边有值一边无值,日期间隔最大为一年 + */ + private String issueDateEnd; + + + /** + * 到期日期起 格式为yyyy-mm-dd,到期日期起止不允许一边有值一边无值,日期间隔最大为一年 + */ + private String dueDateStart; + /** + * 到期日期止 格式为yyyy-mm-dd,到期日期起止不允许一边有值一边无值,日期间隔最大为一年 + */ + private String dueDateEnd; + /** + * 票据种类 AC01-银承,AC02-商承 + */ + private String billType; + /** + * 票据来源 多选。1-直联交易,2-台账交易,3-台账登记,4-挑票同步,5-任务同步 + */ + private String draftSourceList; + /** + * 持票银行类型 多选。见附录4.1.1.银行类型枚举 + */ + private String holdBankTypeList; + + /** + * 持票签收日期起 格式为yyyy-mm-dd,持票签收日期起止不允许一边有值一边无值,日期间隔最大为一年 + */ + private String holdSignDateStart; + /** + * 持票签收日期止 格式为yyyy-mm-dd,持票签收日期起止不允许一边有值一边无值,日期间隔最大为一年 + */ + private String holdSignDateEnd; + /** + * 系统票据编号 + */ + private String draftNbr; + /** + * 票据(包)号 + */ + private String billNbr; + + /** + * 票据金额起 大于等于0,最多为两位小数且整数位上限为13位。票据金额起止不允许一边有值一边无值。单位(元) + */ + private BigDecimal billAmountStart; + + /** + * 票据金额止 大于等于0,最多为两位小数且整数位上限为13位。票据金额起止不允许一边有值一边无值。单位(元) + */ + private BigDecimal billAmountEnd; + + /** + * 子票区间起 子票区间起止不允许一边有值一边无值,最多12位 + */ + private String subBillIntervalStart; + /** + * 子票区间止 子票区间起止不允许一边有值一边无值,最多12位 + */ + private String subBillIntervalEnd; + + /** + * 票据状态 + */ + private String billStsList; + /** + * 流通标志 + */ + private String billTrsStsList; + /** + * 库存状态 1-已入库,2-已出库。 为空时默认查询全部数据 + */ + private String stockFlag; + /** + * 出库方式 0-空,2-背书,3-质押,4-解质,5-贴现,6-分包,7-结清, 8-作废。 为空时默认查询全部数据 + */ + private String outInvType; + /** + * 记录状态 NOR-正常,USE-占用,DEL-删除 SUC-完成。为空时默认查询全部数据 + */ + private String lockFlag; + + + private SQLMapper getSQLMapper() { + return MapperProxyFactory.getProxy(SQLMapper.class); + } + + @Override + public void execute() { + User user = new User(); + user.setUid(1); + user.setLoginid("sysadmin"); + + try { + GetDtaRequest requestParam = new GetDtaRequest(); + requestParam.setCurrentPage(1); + requestParam.setPageSize(1000); + + if (StrUtil.isBlank(displayHoldOrganizationCodeList)) { + throw new CBS8RunTimeException("持票人单位编码为空,请在计划任务配置此项!"); + } + requestParam.setDisplayHoldOrganizationCodeList(Arrays.stream(displayHoldOrganizationCodeList.split(",")).collect(Collectors.toList())); + + if (StrUtil.isBlank(holdAccountList)) { + throw new CBS8RunTimeException("持票人账号为空,请在计划任务配置此项!"); + } + requestParam.setHoldAccountList(Arrays.stream(holdAccountList.split(",")).collect(Collectors.toList())); + + if (StrUtil.isNotBlank(issueDateStart) && StrUtil.isNotBlank(issueDateEnd)) { + requestParam.setIssueDateStart(issueDateStart); + requestParam.setIssueDateEnd(issueDateEnd); + } else { + String nowDate = LocalDate.now().toString(); + requestParam.setIssueDateStart(nowDate); + requestParam.setIssueDateEnd(nowDate); + } + + //系统票据类型 + requestParam.setBillVarietyList(Arrays.stream(Util.null2String(billVarietyList).split(",")).collect(Collectors.toList())); + + requestParam.setDueDateStart(dueDateStart); + requestParam.setDueDateEnd(dueDateEnd); + requestParam.setBillType(billType); + requestParam.setDraftSourceList(draftSourceList==null ? null :Arrays.stream(draftSourceList.split(",")).collect(Collectors.toList())); + requestParam.setHoldBankTypeList(holdBankTypeList==null ? null :Arrays.stream(holdBankTypeList.split(",")).collect(Collectors.toList())); + requestParam.setHoldSignDateStart(holdSignDateStart); + requestParam.setHoldSignDateEnd(holdSignDateEnd); + requestParam.setDraftNbr(draftNbr); + requestParam.setBillNbr(billNbr); + requestParam.setBillAmountStart(billAmountStart); + requestParam.setBillAmountEnd(billAmountEnd); + requestParam.setSubBillIntervalStart(subBillIntervalStart); + requestParam.setSubBillIntervalEnd(subBillIntervalEnd); + requestParam.setBillStsList(billStsList==null ? null :Arrays.stream(billStsList.split(",")).collect(Collectors.toList())); + requestParam.setBillTrsStsList(billTrsStsList==null ? null :Arrays.stream(billTrsStsList.split(",")).collect(Collectors.toList())); + requestParam.setStockFlag(stockFlag); + requestParam.setOutInvType(outInvType); + requestParam.setLockFlag(lockFlag); + + //查询前1000条数据 + BillManagementClient bailManagementClient = new BillManagementClient(); + GetDtaResponse response = bailManagementClient.dtaQuery(requestParam); + List list = response.getData().getList(); + + //判断是否还存在数据,递归查询 + boolean hasNextPage = response.getData().isHasNextPage(); + int nextPage = response.getData().getNextPage(); + while (hasNextPage) { + requestParam.setCurrentPage(nextPage); + GetDtaResponse nextPageResponse = bailManagementClient.dtaQuery(requestParam); + List pageData = nextPageResponse.getData().getList(); + list.addAll(pageData); + hasNextPage = nextPageResponse.getData().isHasNextPage(); + nextPage = nextPageResponse.getData().getNextPage(); + } + + //加载cbs配置 + XStream xStream = new XStream(); + String resource = GCONST.getRootPath() + "WEB-INF" + File.separatorChar + "CBS2ECConfig.xml"; + File file = new File(resource); + xStream.addPermission(AnyTypePermission.ANY); + xStream.processAnnotations(EBS2ECConfig.class); + EBS2ECConfig dto = (EBS2ECConfig) xStream.fromXML(file); + EBS2ECConfig.Table table = dto.getTables().get(2); + Integer modeId = table.getModeId(); + String tableName = table.getKey(); + + //获取已存在的数据 + EBS2ECConfig.Table.Field uniqueField = table.getFields().stream().filter(EBS2ECConfig.Table.Field::isUnique).findFirst().orElse(null); + if (uniqueField == null) { + throw new CBS8RunTimeException("未设置唯一标识字段"); + } + String uniqueKey = uniqueField.getKey(); + String uniqueEbsKey = uniqueField.getEbsKey(); + List uniqueDataKeys = getSQLMapper().listString(String.format("select %s from %s", uniqueKey, tableName)); + + + for (GetDtaResponse.Detail detail : list) { + Map detailMap = JsonUtil.parseMap(detail, String.class); + String uniqueData = detailMap.get(uniqueEbsKey); + if (StrUtil.isBlank(uniqueData)) { + log.warn("跳过cbs票据池数据,唯一标识返回空,uniqueKey:{},uniqueEbsKey:{}", uniqueKey, uniqueEbsKey); + continue; + } + if (uniqueDataKeys.contains(uniqueData)) { + log.warn("跳过cbs票据池数据,数据已存在,uniqueKey:{},uniqueEbsKey:{},值{}", uniqueKey, uniqueEbsKey, uniqueData); + continue; + } + + List fields = new ArrayList() {{ + add("formmodeid"); + add("modedatacreater"); + add("modedatacreatertype"); + add("modedatacreatedate"); + add("modedatacreatetime"); + }}; + String currDate = TimeUtil.getCurrentDateString(); + SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss"); + String currTime = sdf.format(new Date()); + List values = new ArrayList() {{ + add(modeId); + add(1); + add(0); + add(String.format("'%s'", currDate)); + add(String.format("'%s'", currTime)); + }}; + + for (EBS2ECConfig.Table.Field field : table.getFields()) { + //数据库字段 + String fieldName = field.getKey(); + fields.add(fieldName); + // 接口值 + String value = detailMap.getOrDefault(field.getEbsKey(), ""); + values.add(String.format("'%s'", value)); + } + + //业务逻辑字段,收款类型 + fields.add("pjlx"); + //应付票据 + Integer pjlx; + if ("ISS".equals(detail.getBillVariety())) { + pjlx = 2; + } else { + //应收票据 0:银承 1:商承 + pjlx = "AC01".equals(detail.getBillType()) ? 0 : 1; + } + values.add(pjlx); + + + String sql = String.format("insert into %s (%s) values (%s)", tableName, String.join(",", fields), values.stream().map(Object::toString).collect(Collectors.joining(","))); + RecordSet rs = new RecordSet(); + rs.execute(sql); + + if (modeId != null) { + rs.executeQuery("select max(id) from " + tableName); + int mainId = 0; + if (rs.next()) { + mainId = rs.getInt(1); + } + ModeRightInfo ModeRightInfo = new ModeRightInfo(); + ModeRightInfo.setNewRight(true); + ModeRightInfo.editModeDataShare(1, modeId, mainId); + } + } + } catch (Exception e) { + log.error("获取CBS票据池数据失败", e); + throw new CBS8RunTimeException("获取CBS票据池数据失败," + e.getMessage(), e); + } + } +} diff --git a/src/com/engine/salary/timer/SyncCBSPayDetailsJob.java b/src/com/engine/salary/timer/SyncCBSPayDetailsJob.java new file mode 100644 index 0000000..8756423 --- /dev/null +++ b/src/com/engine/salary/timer/SyncCBSPayDetailsJob.java @@ -0,0 +1,344 @@ +package com.engine.salary.timer; + +import cn.hutool.core.util.StrUtil; +import com.engine.salary.exception.CBS8RunTimeException; +import com.engine.salary.mapper.SQLMapper; +import com.engine.salary.mapper.cbs.UfHkrdzbMapper; +import com.engine.salary.remote.cbs8.client.BillManagementClient; +import com.engine.salary.remote.cbs8.config.EBS2ECConfig; +import com.engine.salary.remote.cbs8.po.UfHkrdzbPO; +import com.engine.salary.remote.cbs8.request.GetDtaRequest; +import com.engine.salary.remote.cbs8.response.GetDtaResponse; +import com.engine.salary.remote.cbs8.JsonUtil; +import com.engine.salary.remote.cbs8.SalaryEntityUtil; +import com.engine.salary.remote.cbs8.util.db.MapperProxyFactory; +import com.thoughtworks.xstream.XStream; +import com.thoughtworks.xstream.security.AnyTypePermission; +import lombok.extern.slf4j.Slf4j; +import weaver.conn.RecordSet; +import weaver.formmode.setup.ModeRightInfo; +import weaver.general.GCONST; +import weaver.general.TimeUtil; +import weaver.hrm.User; +import weaver.interfaces.schedule.BaseCronJob; + +import java.io.File; +import java.math.BigDecimal; +import java.text.SimpleDateFormat; +import java.time.LocalDate; +import java.util.*; +import java.util.stream.Collectors; + +/** + * 获取票据收款明细 + *

Copyright: Copyright (c) 2024

+ *

Company: 泛微软件

+ * + * @author qiantao + * @version 1.0 + **/ +@Slf4j +public class SyncCBSPayDetailsJob extends BaseCronJob { + + + /** + * 持票人单位编码 多选。填在cbs系统公共设置>基础信息>组织机构维护的单位编码 + */ + private String displayHoldOrganizationCodeList; + /** + * 持票人账号 多选。 + */ + private String holdAccountList; + + /** + * 系统票据类型 多选。按附录4.1.3票据类型-票据状态-流通标志级联关系表选择后按顺序填写。系统票据类型枚举见附录4.1.3。票据状态枚举见附录4.1.4。流通标志枚举见附录4.1.5。 + */ + private String billVarietyList; + + /** + * 出票日期起 格式为yyyy-mm-dd,出票日期起止不允许一边有值一边无值,日期间隔最大为一年 + */ + private String issueDateStart; + + /** + * 出票日期止 格式为yyyy-mm-dd,出票日期起止不允许一边有值一边无值,日期间隔最大为一年 + */ + private String issueDateEnd; + + + /** + * 到期日期起 格式为yyyy-mm-dd,到期日期起止不允许一边有值一边无值,日期间隔最大为一年 + */ + private String dueDateStart; + /** + * 到期日期止 格式为yyyy-mm-dd,到期日期起止不允许一边有值一边无值,日期间隔最大为一年 + */ + private String dueDateEnd; + /** + * 票据种类 AC01-银承,AC02-商承 + */ + private String billType; + /** + * 票据来源 多选。1-直联交易,2-台账交易,3-台账登记,4-挑票同步,5-任务同步 + */ + private String draftSourceList; + /** + * 持票银行类型 多选。见附录4.1.1.银行类型枚举 + */ + private String holdBankTypeList; + + /** + * 持票签收日期起 格式为yyyy-mm-dd,持票签收日期起止不允许一边有值一边无值,日期间隔最大为一年 + */ + private String holdSignDateStart; + /** + * 持票签收日期止 格式为yyyy-mm-dd,持票签收日期起止不允许一边有值一边无值,日期间隔最大为一年 + */ + private String holdSignDateEnd; + /** + * 系统票据编号 + */ + private String draftNbr; + /** + * 票据(包)号 + */ + private String billNbr; + + /** + * 票据金额起 大于等于0,最多为两位小数且整数位上限为13位。票据金额起止不允许一边有值一边无值。单位(元) + */ + private BigDecimal billAmountStart; + + /** + * 票据金额止 大于等于0,最多为两位小数且整数位上限为13位。票据金额起止不允许一边有值一边无值。单位(元) + */ + private BigDecimal billAmountEnd; + + /** + * 子票区间起 子票区间起止不允许一边有值一边无值,最多12位 + */ + private String subBillIntervalStart; + /** + * 子票区间止 子票区间起止不允许一边有值一边无值,最多12位 + */ + private String subBillIntervalEnd; + + /** + * 票据状态 + */ + private String billStsList; + /** + * 流通标志 + */ + private String billTrsStsList; + /** + * 库存状态 1-已入库,2-已出库。 为空时默认查询全部数据 + */ + private String stockFlag; + /** + * 出库方式 0-空,2-背书,3-质押,4-解质,5-贴现,6-分包,7-结清, 8-作废。 为空时默认查询全部数据 + */ + private String outInvType; + /** + * 记录状态 NOR-正常,USE-占用,DEL-删除 SUC-完成。为空时默认查询全部数据 + */ + private String lockFlag; + + private SQLMapper getSQLMapper() { + return MapperProxyFactory.getProxy(SQLMapper.class); + } + + private UfHkrdzbMapper getUfHkrdzbMapper() { + return MapperProxyFactory.getProxy(UfHkrdzbMapper.class); + } + + @Override + public void execute() { + User user = new User(); + user.setUid(1); + user.setLoginid("sysadmin"); + + try { + GetDtaRequest requestParam = new GetDtaRequest(); + requestParam.setCurrentPage(1); + requestParam.setPageSize(1000); + + if (StrUtil.isBlank(displayHoldOrganizationCodeList)) { + throw new CBS8RunTimeException("持票人单位编码为空,请在计划任务配置此项!"); + } + requestParam.setDisplayHoldOrganizationCodeList(Arrays.stream(displayHoldOrganizationCodeList.split(",")).collect(Collectors.toList())); + + if (StrUtil.isBlank(holdAccountList)) { + throw new CBS8RunTimeException("持票人账号为空,请在计划任务配置此项!"); + } + requestParam.setHoldAccountList(Arrays.stream(holdAccountList.split(",")).collect(Collectors.toList())); + + if (StrUtil.isNotBlank(issueDateStart) && StrUtil.isNotBlank(issueDateEnd)) { + requestParam.setIssueDateStart(issueDateStart); + requestParam.setIssueDateEnd(issueDateEnd); + } else { + String nowDate = LocalDate.now().toString(); + requestParam.setIssueDateStart(nowDate); + requestParam.setIssueDateEnd(nowDate); + } + + //系统票据类型 + requestParam.setBillVarietyList(Arrays.stream(billVarietyList.split(",")).collect(Collectors.toList())); + + requestParam.setDueDateStart(dueDateStart); + requestParam.setDueDateEnd(dueDateEnd); + requestParam.setBillType(billType); + requestParam.setDraftSourceList(draftSourceList==null ? null :Arrays.stream(draftSourceList.split(",")).collect(Collectors.toList())); + requestParam.setHoldBankTypeList(holdBankTypeList==null ? null :Arrays.stream(holdBankTypeList.split(",")).collect(Collectors.toList())); + requestParam.setHoldSignDateStart(holdSignDateStart); + requestParam.setHoldSignDateEnd(holdSignDateEnd); + requestParam.setDraftNbr(draftNbr); + requestParam.setBillNbr(billNbr); + requestParam.setBillAmountStart(billAmountStart); + requestParam.setBillAmountEnd(billAmountEnd); + requestParam.setSubBillIntervalStart(subBillIntervalStart); + requestParam.setSubBillIntervalEnd(subBillIntervalEnd); + requestParam.setBillStsList(billStsList==null ? null :Arrays.stream(billStsList.split(",")).collect(Collectors.toList())); + requestParam.setBillTrsStsList(billTrsStsList==null ? null :Arrays.stream(billTrsStsList.split(",")).collect(Collectors.toList())); + requestParam.setStockFlag(stockFlag); + requestParam.setOutInvType(outInvType); + requestParam.setLockFlag(lockFlag); + + //查询前1000条数据 + BillManagementClient bailManagementClient = new BillManagementClient(); + GetDtaResponse response = bailManagementClient.dtaQuery(requestParam); + List list = response.getData().getList(); + + //判断是否还存在数据,递归查询 + boolean hasNextPage = response.getData().isHasNextPage(); + int nextPage = response.getData().getNextPage(); + while (hasNextPage) { + requestParam.setCurrentPage(nextPage); + GetDtaResponse nextPageResponse = bailManagementClient.dtaQuery(requestParam); + List pageData = nextPageResponse.getData().getList(); + list.addAll(pageData); + hasNextPage = nextPageResponse.getData().isHasNextPage(); + nextPage = nextPageResponse.getData().getNextPage(); + } + + //加载cbs配置 + XStream xStream = new XStream(); + String resource = GCONST.getRootPath() + "WEB-INF" + File.separatorChar + "CBS2ECConfig.xml"; + File file = new File(resource); + xStream.addPermission(AnyTypePermission.ANY); + xStream.processAnnotations(EBS2ECConfig.class); + EBS2ECConfig dto = (EBS2ECConfig) xStream.fromXML(file); + EBS2ECConfig.Table table = dto.getTables().get(1); + Integer modeId = table.getModeId(); + String tableName = table.getKey(); + + //获取已存在的数据 + EBS2ECConfig.Table.Field uniqueField = table.getFields().stream().filter(EBS2ECConfig.Table.Field::isUnique).findFirst().orElse(null); + if (uniqueField == null) { + throw new CBS8RunTimeException("未设置唯一标识字段"); + } + String uniqueKey = uniqueField.getKey(); + String uniqueEbsKey = uniqueField.getEbsKey(); + List uniqueDataKeys = getSQLMapper().listString(String.format("select %s from %s", uniqueKey, tableName)); + + //获取汇款人与办事处的对照数据 + List ufHkrdzbPOS = getUfHkrdzbMapper().listAll(); + Map customerDepartmentMap = SalaryEntityUtil.convert2Map(ufHkrdzbPOS, UfHkrdzbPO::getKhmc, UfHkrdzbPO::getDepartmentId); + + + for (GetDtaResponse.Detail detail : list) { + Map detailMap = JsonUtil.parseMap(detail, String.class); + String uniqueData = detailMap.get(uniqueEbsKey); + if (StrUtil.isBlank(uniqueData)) { + log.warn("跳过cbs票据收款数据,唯一标识返回空,uniqueKey:{},uniqueEbsKey:{}", uniqueKey, uniqueEbsKey); + continue; + } + if (uniqueDataKeys.contains(uniqueData)) { + log.warn("跳过cbs票据收款数据,数据已存在,uniqueKey:{},uniqueEbsKey:{},值{}", uniqueKey, uniqueEbsKey, uniqueData); + continue; + } + + List fields = new ArrayList() {{ + add("formmodeid"); + add("modedatacreater"); + add("modedatacreatertype"); + add("modedatacreatedate"); + add("modedatacreatetime"); + }}; + String currDate = TimeUtil.getCurrentDateString(); + SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss"); + String currTime = sdf.format(new Date()); + List values = new ArrayList() {{ + add(modeId); + add(1); + add(0); + add(String.format("'%s'", currDate)); + add(String.format("'%s'", currTime)); + }}; + + String hkr = ""; + for (EBS2ECConfig.Table.Field field : table.getFields()) { + //数据库字段 + String fieldName = field.getKey(); + fields.add(fieldName); + // 接口值 + String value = detailMap.getOrDefault(field.getEbsKey(), ""); + values.add(String.format("'%s'", value)); + + //汇款人 + if ("hkr".equals(fieldName)) { + hkr = value; + } + } + + //业务逻辑字段,收款类型 + fields.add("sklx"); + //应收票据 0:银承 1:商承 + values. add("AC01".equals(detail.getBillType()) ? 0 : 1); + + /* + * 认领逻辑, + * 根据汇款人去表里匹配【汇款人与办事处的对照表】 + * 如果能匹配到数据,则状态改为已自动认领,同时把办事处(部门)的值同步到办事处字段中,是否系统自动认领同步为是; + * 如对照标准无数据,则状态为:未认领; + */ + Integer departmentId = customerDepartmentMap.get(hkr); + if(departmentId!=null){ + //认领 + fields.add("zt"); + values.add(1); + + //是否系统认领 + fields.add("sfxtzdrl"); + values.add(1); + + //办事处 + fields.add("szbm"); + values.add(departmentId); + }else { + //未认领 + fields.add("zt"); + values.add(0); + } + + String sql = String.format("insert into %s (%s) values (%s)", tableName, String.join(",", fields), values.stream().map(Object::toString).collect(Collectors.joining(","))); + RecordSet rs = new RecordSet(); + rs.execute(sql); + + if (modeId != null) { + rs.executeQuery("select max(id) from " + tableName); + int mainId = 0; + if (rs.next()) { + mainId = rs.getInt(1); + } + ModeRightInfo ModeRightInfo = new ModeRightInfo(); + ModeRightInfo.setNewRight(true); + ModeRightInfo.editModeDataShare(1, modeId, mainId); + } + } + } catch (Exception e) { + log.error("获取CBS票据收款数据失败", e); + throw new CBS8RunTimeException("获取CBS票据收款数据信息失败," + e.getMessage(), e); + } + } +}