|
|
@@ -0,0 +1,287 @@
|
|
|
+package nckd.base.common.utils;
|
|
|
+
|
|
|
+import com.alibaba.fastjson.JSONObject;
|
|
|
+import kd.bos.exception.KDBizException;
|
|
|
+import kd.bos.logging.Log;
|
|
|
+import kd.bos.logging.LogFactory;
|
|
|
+
|
|
|
+import java.io.UnsupportedEncodingException;
|
|
|
+import java.security.MessageDigest;
|
|
|
+import java.security.NoSuchAlgorithmException;
|
|
|
+import java.util.*;
|
|
|
+
|
|
|
+/**
|
|
|
+ * @description:商旅同步工具类
|
|
|
+ * @author: dingsixi
|
|
|
+ * @create: 2025/12/15 12:00
|
|
|
+ */
|
|
|
+public class TripSyncUtils {
|
|
|
+
|
|
|
+ private final static Log logger = LogFactory.getLog(TripSyncUtils.class);
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 胜意接口请求路径
|
|
|
+ */
|
|
|
+ private final static String PATH = "/fcopen/fcoutapi";
|
|
|
+
|
|
|
+ /**
|
|
|
+ * SIT.胜意接口.KEY
|
|
|
+ */
|
|
|
+ public final static String SIT_FWS_KEY = "011f42937f4049209b5c9fac611020a3";
|
|
|
+ /**
|
|
|
+ * SIT.胜意接口.账号
|
|
|
+ */
|
|
|
+ public final static String ACCOUNT = "JXWL";
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @param service 接口名称
|
|
|
+ * @param urlIsContainsService url是否包含接口名称
|
|
|
+ * @param data 具体业务数据
|
|
|
+ */
|
|
|
+ public static void pushApi(String service, Boolean urlIsContainsService, Map<String, Object> data) {
|
|
|
+ logger.info(String.format("推送API接口名称:%s,业务数据参数:\n%s", service, JSONObject.toJSONString(data)));
|
|
|
+
|
|
|
+ //获取费用核算应用参数.胜意环境地址
|
|
|
+ String address = (String) ParamUtils.getSysCtrlParameter(ParamUtils.EM, "nckd_address");
|
|
|
+ //公共接口参数
|
|
|
+ Map<String, Object> syCommonParam = TripSyncUtils.createSYCommonParam();
|
|
|
+ syCommonParam.put("service", service);
|
|
|
+ //接口请求路径
|
|
|
+ String url = urlIsContainsService ? address + PATH + "/" + service : address + PATH;
|
|
|
+ //具体业务数据
|
|
|
+ syCommonParam.put("data", data);
|
|
|
+ logger.info(String.format("推送API接口名称:%s,开始推送,接口入参:\n%s", service, JSONObject.toJSONString(syCommonParam)));
|
|
|
+ //接口推送
|
|
|
+ try {
|
|
|
+ String s = HttpUtils.postJson(url, syCommonParam);
|
|
|
+ logger.info(String.format("推送API接口名称:%s,完成推送,接口出参:\n%s", service, s));
|
|
|
+
|
|
|
+ JSONObject result = JSONObject.parseObject(s);
|
|
|
+ if (result.getBoolean("fail")) {
|
|
|
+ throw new KDBizException("推送接口失败:" + result.getString("message"));
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ throw new KDBizException(e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ *
|
|
|
+ * @return 生成胜意接口公共请求参数
|
|
|
+ */
|
|
|
+ public static Map<String, Object> createSYCommonParam() {
|
|
|
+ //获取费用核算应用参数
|
|
|
+ Map<String, Object> sysCtrlParameter = ParamUtils.getSysCtrlParameter(ParamUtils.EM);
|
|
|
+ //账号
|
|
|
+ String account = (String) sysCtrlParameter.get("nckd_account");
|
|
|
+ String key = (String) sysCtrlParameter.get("nckd_key");
|
|
|
+ //总公司
|
|
|
+ String company = (String) sysCtrlParameter.get("nckd_company");
|
|
|
+ String timestamp = DateUtil.date2str(new Date(), DateUtil.DATE_TIME_FORMAT_YYYY_MM_DD_HH_MI_SS);
|
|
|
+
|
|
|
+ Map<String, Object> commonParam = new HashMap<>();
|
|
|
+ //格式为yyyy-MM-dd HH:mm:ss,API服务端允许客户端请求时间误差为10分钟。
|
|
|
+ commonParam.put("timestamp", timestamp);
|
|
|
+ //总公司编号由平台提供
|
|
|
+ commonParam.put("compid", company);
|
|
|
+ //账号由平台提供
|
|
|
+ commonParam.put("account", account);
|
|
|
+ commonParam.put("key", key);
|
|
|
+ //返回数据类型默认为1 1表示xml 2 表示json
|
|
|
+ commonParam.put("responseType", "2");
|
|
|
+ //默认为MD5支持MD5签名和国密SM3
|
|
|
+ commonParam.put("signType", "MD5");
|
|
|
+ //签名 md5(account+timestamp+key) Sm3(account+timestamp+key) key由平台提供
|
|
|
+ commonParam.put("sign", createSYSign(timestamp, account, key));
|
|
|
+ //会员ID(非必传,由胜意提供)
|
|
|
+ commonParam.put("hyid", "");
|
|
|
+ //本批次数据的唯一标识
|
|
|
+ commonParam.put("uuid", UUID.randomUUID());
|
|
|
+ return commonParam;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @return 生成胜意接口签名
|
|
|
+ */
|
|
|
+ private static String createSYSign(String dateStr, String account, String key) {
|
|
|
+ //账号+当前时间+KEY
|
|
|
+ return getMD5Sign(account + dateStr + key).toLowerCase(Locale.ROOT);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ public static void main(String[] args) {
|
|
|
+ String sign = createTripSyncSign();
|
|
|
+// String sign = createSYSign();
|
|
|
+ System.out.println(sign);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @return 生成胜意接口签名,通过main方法生成签名postman调试
|
|
|
+ */
|
|
|
+ private static String createSYSign() {
|
|
|
+ String dateStr = DateUtil.date2str(new Date(), DateUtil.DATE_TIME_FORMAT_YYYY_MM_DD_HH_MI_SS);
|
|
|
+ //账号+当前时间+KEY
|
|
|
+ return String.format("timestamp:%s\nsign:%s", dateStr, getMD5Sign(ACCOUNT + dateStr + SIT_FWS_KEY).toLowerCase(Locale.ROOT));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @return 生成商旅标准订单接口签名,通过main方法生成签名postman调试
|
|
|
+ */
|
|
|
+ private static String createTripSyncSign() {
|
|
|
+ //商旅订单标准接口报文,此处报文和实际接口入参报文存在差异,key值tripSync需要剔除,只需要传value值,且sign参数需要剔除
|
|
|
+ String str = "{\n" +
|
|
|
+ " \"method\": \"er_planebill\",\n" +
|
|
|
+ " \"service\": \"SY_SL\",\n" +
|
|
|
+ " \"data\": [\n" +
|
|
|
+ " {\n" +
|
|
|
+ " \"billstatus\": \"A\",\n" +
|
|
|
+ " \"ordernum\": \"DF210121121212212\",\n" +
|
|
|
+ " \"pnr\": \"Zr3LF\",\n" +
|
|
|
+ " \"ticketnum\": \"999-9000999000\",\n" +
|
|
|
+ " \"parentordernum\": \"\",\n" +
|
|
|
+ " \"oriordernum\": \"\",\n" +
|
|
|
+ " \"overdesc\": \"\",\n" +
|
|
|
+ " \"refundreason\": \"\",\n" +
|
|
|
+ " \"changereason\": \"\",\n" +
|
|
|
+ " \"changetype\": \"\",\n" +
|
|
|
+ " \"fromcityname\": \"上海\",\n" +
|
|
|
+ " \"takeoffportname\": \"SHA\",\n" +
|
|
|
+ " \"tocityname\": \"广州\",\n" +
|
|
|
+ " \"landingportname\": \"CAN\",\n" +
|
|
|
+ " \"airlinename\": \"川航\",\n" +
|
|
|
+ " \"flightno\": \"3U9911\",\n" +
|
|
|
+ " \"cabin\": \"Q\",\n" +
|
|
|
+ " \"tripid\": \"SHm4t\",\n" +
|
|
|
+ " \"overflag\": \"\",\n" +
|
|
|
+ " \"orderdate\": \"2024-01-31\",\n" +
|
|
|
+ " \"totalamount\": \"858.62\",\n" +
|
|
|
+ " \"ordertype\": \"O\",\n" +
|
|
|
+ " \"isbusiness\": \"1\",\n" +
|
|
|
+ " \"producttype\": \"1\",\n" +
|
|
|
+ " \"orderstatus\": \"40000\",\n" +
|
|
|
+ " \"ticketstatus\": \"UNUSED\",\n" +
|
|
|
+ " \"ticketprice\": \"178.71\",\n" +
|
|
|
+ " \"discount\": \"558.68\",\n" +
|
|
|
+ " \"airportprice\": \"94.78\",\n" +
|
|
|
+ " \"fuelprice\": \"318.53\",\n" +
|
|
|
+ " \"tax\": \"233.28\",\n" +
|
|
|
+ " \"refundamount\": \"\",\n" +
|
|
|
+ " \"endorsementamount\": \"\",\n" +
|
|
|
+ " \"assuranceamount\": \"\",\n" +
|
|
|
+ " \"servicefee\": \"\",\n" +
|
|
|
+ " \"standprice\": \"\",\n" +
|
|
|
+ " \"ordersort\": \"1\",\n" +
|
|
|
+ " \"orderstatusname\": \"\",\n" +
|
|
|
+ " \"bookedname\": \"IatNP\",\n" +
|
|
|
+ " \"travelername\": \"Nfwd9\",\n" +
|
|
|
+ " \"operationtype\": \"1\",\n" +
|
|
|
+ " \"choosenotminreason\": \"sDH6T\",\n" +
|
|
|
+ " \"isapprove\": false,\n" +
|
|
|
+ " \"cabinclass\": \"BDPLk\",\n" +
|
|
|
+ " \"takeofftime\": \"2024-01-3109:45:05\",\n" +
|
|
|
+ " \"landingtime\": \"2024-01-3109:45:05\",\n" +
|
|
|
+ " \"isaddintegral\": false,\n" +
|
|
|
+ " \"isdeductible\": true,\n" +
|
|
|
+ " \"itinerarynum\": \"XoPV8\",\n" +
|
|
|
+ " \"itinerarydate\": \"2024-01-31\",\n" +
|
|
|
+ " \"downloadlink\": \"6aouW\",\n" +
|
|
|
+ " \"happenddate\": \"2024-01-3109:45:05\",\n" +
|
|
|
+ " \"lowestpirce\": \"524.76\",\n" +
|
|
|
+ " \"islowestpirce\": false,\n" +
|
|
|
+ " \"otheramount\": \"828.17\",\n" +
|
|
|
+ " \"bookeddate\": \"2024-01-31\",\n" +
|
|
|
+ " \"servicefeepaytype\": \"1\",\n" +
|
|
|
+ " \"personalfee\": \"231.29\",\n" +
|
|
|
+ " \"oabillformid\": \"er_tripreqbill\",\n" +
|
|
|
+ " \"sourcebookedid\": \"qwe\",\n" +
|
|
|
+ " \"expcommitcomnum\": 123,\n" +
|
|
|
+ " \"expcommitdepnum\": 123,\n" +
|
|
|
+ " \"sourcetravelerid\": \"qwe\",\n" +
|
|
|
+ " \"travelerdept\": 123,\n" +
|
|
|
+ " \"bookeddept\": 123,\n" +
|
|
|
+ " \"company\": 123,\n" +
|
|
|
+ " \"std_costcenter\": 123,\n" +
|
|
|
+ " \"currency\": 123\n" +
|
|
|
+ " }\n" +
|
|
|
+ " ]\n" +
|
|
|
+ " }";
|
|
|
+ Map map = JSONObject.parseObject(str, Map.class);
|
|
|
+ String result = genSign(map);
|
|
|
+ return String.format("sign:%s", getMD5Sign(result + SIT_FWS_KEY).toLowerCase(Locale.ROOT));
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ public static String genSign(Map<String, Object> params) {
|
|
|
+ params.remove("sign");
|
|
|
+ StringBuilder buf = new StringBuilder();
|
|
|
+
|
|
|
+ try {
|
|
|
+ List<Map.Entry<String, Object>> infoIds = new ArrayList(params.entrySet());
|
|
|
+ infoIds.sort(new Comparator<Map.Entry<String, Object>>() {
|
|
|
+ public int compare(Map.Entry<String, Object> o1, Map.Entry<String, Object> o2) {
|
|
|
+ return ((String) o1.getKey()).compareTo((String) o2.getKey());
|
|
|
+ }
|
|
|
+ });
|
|
|
+ Iterator var3 = infoIds.iterator();
|
|
|
+
|
|
|
+ while (var3.hasNext()) {
|
|
|
+ Map.Entry<String, Object> item = (Map.Entry) var3.next();
|
|
|
+ if (item.getKey() != null && !((String) item.getKey()).isEmpty()) {
|
|
|
+ String key = (String) item.getKey();
|
|
|
+ String val = item.getValue() == null ? "" : item.getValue().toString();
|
|
|
+ if (buf.toString().isEmpty()) {
|
|
|
+ buf.append(key).append("=").append(val);
|
|
|
+ } else {
|
|
|
+ buf.append("&").append(key).append("=").append(val);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+// logger.info("商旅推送:生成签名前字符串:{}", buf.toString());
|
|
|
+ } catch (Exception e) {
|
|
|
+// throw new KDBizException(String.format(ResManager.loadKDString("商旅集成,获取签名失败:%s", "TripCommonUtil_03", "fi-er-business", new Object[0]), var7));
|
|
|
+ }
|
|
|
+ return buf.toString();
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ public static String getMD5Sign(String input) {
|
|
|
+ StringBuilder sb = new StringBuilder(32);
|
|
|
+
|
|
|
+ try {
|
|
|
+ MessageDigest md5 = MessageDigest.getInstance("MD5");
|
|
|
+ if (input == null) {
|
|
|
+ throw new RuntimeException("md5 input string couldn't null");
|
|
|
+ }
|
|
|
+
|
|
|
+ md5.update(input.getBytes("utf-8"));
|
|
|
+ byte[] digest = md5.digest();
|
|
|
+ byte[] var4 = digest;
|
|
|
+ int var5 = digest.length;
|
|
|
+
|
|
|
+ for (int var6 = 0; var6 < var5; ++var6) {
|
|
|
+ byte d = var4[var6];
|
|
|
+ sb.append(hex(d));
|
|
|
+ }
|
|
|
+ } catch (NoSuchAlgorithmException var8) {
|
|
|
+// throw new KDException(var8, new ErrorCode("md5 error", "not exist md5 instance"), new Object[0]);
|
|
|
+ } catch (UnsupportedEncodingException var9) {
|
|
|
+// throw new RuntimeException(var9);
|
|
|
+ }
|
|
|
+
|
|
|
+ return sb.toString();
|
|
|
+ }
|
|
|
+
|
|
|
+ public static char[] hex(byte b) {
|
|
|
+ int low = b & 15;
|
|
|
+ int high = b >> 4 & 15;
|
|
|
+ char[] cs = new char[]{base[high], base[low]};
|
|
|
+ return cs;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static final char[] base = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
|
|
|
+
|
|
|
+
|
|
|
+}
|