ruoyi-common/pom.xml | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/ServiceOrderAppResultVo.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/ServiceOrderAppVo.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/ServiceOrderDelVo.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
ruoyi-common/src/main/java/com/ruoyi/common/utils/civilAviation/ServiceOrderSign.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
ruoyi-common/src/main/java/com/ruoyi/common/utils/civilAviation/ServiceOrderUtil.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
ruoyi-system/pom.xml | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
ruoyi-common/pom.xml
@@ -118,6 +118,11 @@ <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <scope>provided</scope> </dependency> </dependencies> ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/ServiceOrderAppResultVo.java
New file @@ -0,0 +1,14 @@ package com.ruoyi.common.core.domain.entity; import lombok.Data; /** * 服务单接口调用结果 */ @Data public class ServiceOrderAppResultVo { private String method; private Integer result; private String ServiceOrdID; private String ServiceOrdNo; } ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/ServiceOrderAppVo.java
New file @@ -0,0 +1,134 @@ package com.ruoyi.common.core.domain.entity; import lombok.Data; import java.util.Date; /** * 服务订单应用值对象,用于封装与服务订单相关的请求参数。 */ @Data public class ServiceOrderAppVo { /** * 接口名称,固定为 ServiceOrder_APP */ private String method; /** * 商户 APPID,示例值:GDS-000001 */ private String appId; /** * 服务类型 ID,取值范围: * 20 救护车 * 1 市内 * 2 省内 * 3 省外 * 4 机场 * 5 航空 * 6 驻点 * 7 保障 * 8 高铁 * 9 接送 * 10 救援 * 11 港澳台 * 12 国际 * 25 民政任务 A * 14 回城 - 白云 * 18 回城 - 越秀 * 16 发热 * 15 密接 - 白云 * 19 密接 - 越秀 * 21 保障转运 * 17 送样 * 26 康复 * 27 福祉车 * 22 中肿送样 * 23 保障转运 - 矿泉 * 24 保障转运 - 登峰 * 13 测试 */ private Integer ordType; /** * 客户联系电话或微信号,只填写一个 */ private String coPhone; /** * 客户姓名 */ private String coName; /** * 客户联系人与患者关系 */ private String coTies; /** * 预约运送时间,格式示例:2017-4-18 21:33 */ private String apptDate; /** * 患者姓名 */ private String ptName; /** * 患者年龄 */ private String ptAge; /** * 患者性别 */ private String ptSex; /** * 患者国籍 */ private String ptNat; /** * 转出医院或出发地 */ private String outHosp; /** * 转入医院或目的地 */ private String inHosp; /** * 患者所在科室 */ private String ptServices; /** * 诊断信息 */ private String ptDiagnosis; /** * 备注信息 */ private String condition; /** * 患者医生姓名 */ private String doctor; /** * 患者医生电话 */ private String doctorPhone; /** * 报价,单位:元 */ private Double offerPrice; /** * 推介人信息,支持手机号或姓名 */ private String referrals; /** * 商户备注 */ private String unitRemarks; /** * 外部支付二维码链接 */ private String payQRcodeURL; /** * 时间戳 */ private Long unixTime; /** * 签名,详见签名生成算法 */ private String sign; } ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/ServiceOrderDelVo.java
New file @@ -0,0 +1,34 @@ package com.ruoyi.common.core.domain.entity; import lombok.Data; /** * 服务订单删除请求值对象,用于封装服务订单删除接口的请求参数。 */ @Data public class ServiceOrderDelVo { /** * 接口名称,固定为 ServiceOrder_DEL */ private String Method; /** * 商户 APPID,示例值:GDS-000001 */ private String APPID; /** * 服务 ID,示例值:1016007321 */ private String ServiceOrdID; /** * 取消原因 */ private String DELRemarks; /** * 时间戳,示例值:1746079236 */ private Long UnixTime; /** * 签名 */ private String Sign; } ruoyi-common/src/main/java/com/ruoyi/common/utils/civilAviation/ServiceOrderSign.java
New file @@ -0,0 +1,352 @@ package com.ruoyi.common.utils.civilAviation; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.security.MessageDigest; import java.util.*; import org.apache.commons.lang3.StringUtils; public class ServiceOrderSign { private static final int BITS_TO_A_BYTE = 8; private static final int BYTES_TO_A_WORD = 4; private static final int BITS_TO_A_WORD = 32; private static final String APP_SECRET = "JR6o8RQItoa2bzlneOCQ"; private static final long[] m_lOnBits = new long[31]; private static final long[] m_l2Power = new long[31]; static { // 初始化位操作数组 m_lOnBits[0] = 1L; m_lOnBits[1] = 3L; m_lOnBits[2] = 7L; m_lOnBits[3] = 15L; m_lOnBits[4] = 31L; m_lOnBits[5] = 63L; m_lOnBits[6] = 127L; m_lOnBits[7] = 255L; m_lOnBits[8] = 511L; m_lOnBits[9] = 1023L; m_lOnBits[10] = 2047L; m_lOnBits[11] = 4095L; m_lOnBits[12] = 8191L; m_lOnBits[13] = 16383L; m_lOnBits[14] = 32767L; m_lOnBits[15] = 65535L; m_lOnBits[16] = 131071L; m_lOnBits[17] = 262143L; m_lOnBits[18] = 524287L; m_lOnBits[19] = 1048575L; m_lOnBits[20] = 2097151L; m_lOnBits[21] = 4194303L; m_lOnBits[22] = 8388607L; m_lOnBits[23] = 16777215L; m_lOnBits[24] = 33554431L; m_lOnBits[25] = 67108863L; m_lOnBits[26] = 134217727L; m_lOnBits[27] = 268435455L; m_lOnBits[28] = 536870911L; m_lOnBits[29] = 1073741823L; m_lOnBits[30] = 2147483647L; m_l2Power[0] = 1L; m_l2Power[1] = 2L; m_l2Power[2] = 4L; m_l2Power[3] = 8L; m_l2Power[4] = 16L; m_l2Power[5] = 32L; m_l2Power[6] = 64L; m_l2Power[7] = 128L; m_l2Power[8] = 256L; m_l2Power[9] = 512L; m_l2Power[10] = 1024L; m_l2Power[11] = 2048L; m_l2Power[12] = 4096L; m_l2Power[13] = 8192L; m_l2Power[14] = 16384L; m_l2Power[15] = 32768L; m_l2Power[16] = 65536L; m_l2Power[17] = 131072L; m_l2Power[18] = 262144L; m_l2Power[19] = 524288L; m_l2Power[20] = 1048576L; m_l2Power[21] = 2097152L; m_l2Power[22] = 4194304L; m_l2Power[23] = 8388608L; m_l2Power[24] = 16777216L; m_l2Power[25] = 33554432L; m_l2Power[26] = 67108864L; m_l2Power[27] = 134217728L; m_l2Power[28] = 268435456L; m_l2Power[29] = 536870912L; m_l2Power[30] = 1073741824L; } private static long LShift(long lValue, int iShiftBits) { if (iShiftBits == 0) { return lValue; } else if (iShiftBits == 31) { return (lValue & 1) != 0 ? 0x80000000L : 0; } else if (iShiftBits < 0 || iShiftBits > 31) { throw new RuntimeException("Invalid shift bits"); } return ((lValue & m_lOnBits[31 - iShiftBits]) << iShiftBits); } private static long RShift(long lValue, int iShiftBits) { if (iShiftBits == 0) { return lValue; } else if (iShiftBits == 31) { return (lValue & 0x80000000L) != 0 ? 1 : 0; } else if (iShiftBits < 0 || iShiftBits > 31) { throw new RuntimeException("Invalid shift bits"); } return (lValue >>> iShiftBits) & (0x7FFFFFFFL >>> (iShiftBits - 1)); } private static long RotateLeft(long lValue, int iShiftBits) { return (LShift(lValue, iShiftBits) | RShift(lValue, 32 - iShiftBits)) & 0xFFFFFFFFL; } private static long AddUnsigned(long lX, long lY) { long lX8 = lX & 0x80000000L; long lY8 = lY & 0x80000000L; long lX4 = lX & 0x40000000L; long lY4 = lY & 0x40000000L; long lResult = (lX & 0x3FFFFFFFL) + (lY & 0x3FFFFFFFL); if ((lX4 & lY4) != 0) { lResult = lResult ^ 0x80000000L ^ lX8 ^ lY8; } else if ((lX4 | lY4) != 0) { if ((lResult & 0x40000000L) != 0) { lResult = lResult ^ 0xC0000000L ^ lX8 ^ lY8; } else { lResult = lResult ^ 0x40000000L ^ lX8 ^ lY8; } } else { lResult = lResult ^ lX8 ^ lY8; } return lResult; } private static long F(long x, long y, long z) { return (x & y) | (~x & z); } private static long G(long x, long y, long z) { return (x & z) | (y & ~z); } private static long H(long x, long y, long z) { return x ^ y ^ z; } private static long I(long x, long y, long z) { return y ^ (x | ~z); } private static void FF(long[] a, long b, long c, long d, long x, int s, long ac) { a[0] = AddUnsigned(a[0], AddUnsigned(AddUnsigned(F(b, c, d), x), ac)); a[0] = RotateLeft(a[0], s); a[0] = AddUnsigned(a[0], b); } private static void GG(long[] a, long b, long c, long d, long x, int s, long ac) { a[0] = AddUnsigned(a[0], AddUnsigned(AddUnsigned(G(b, c, d), x), ac)); a[0] = RotateLeft(a[0], s); a[0] = AddUnsigned(a[0], b); } private static void HH(long[] a, long b, long c, long d, long x, int s, long ac) { a[0] = AddUnsigned(a[0], AddUnsigned(AddUnsigned(H(b, c, d), x), ac)); a[0] = RotateLeft(a[0], s); a[0] = AddUnsigned(a[0], b); } private static void II(long[] a, long b, long c, long d, long x, int s, long ac) { a[0] = AddUnsigned(a[0], AddUnsigned(AddUnsigned(I(b, c, d), x), ac)); a[0] = RotateLeft(a[0], s); a[0] = AddUnsigned(a[0], b); } private static long[] ConvertToWordArray(byte[] messageBytes) { int lMessageLength = messageBytes.length; int lNumberOfWords = (((lMessageLength + 8) >> 6) + 1) << 4; long[] lWordArray = new long[lNumberOfWords]; int lBytePosition = 0; for (int lWordCount = 0; lBytePosition < lMessageLength; lWordCount++, lBytePosition += 4) { lWordArray[lWordCount] = 0; for (int lByteCount = 0; lByteCount < Math.min(4, lMessageLength - lBytePosition); lByteCount++) { lWordArray[lWordCount] |= ((messageBytes[lBytePosition + lByteCount] & 0xFF) << (lByteCount << 3)); } } // 标记结束 lWordArray[lBytePosition >> 2] |= 0x80 << ((lBytePosition % 4) << 3); // 设置消息长度 lWordArray[lNumberOfWords - 2] = (lMessageLength << 3) & 0xFFFFFFFFL; lWordArray[lNumberOfWords - 1] = (lMessageLength >>> 29) & 0xFFFFFFFFL; return lWordArray; } private static String WordToHex(long lValue) { StringBuilder wordToHex = new StringBuilder(); for (int lCount = 0; lCount <= 3; lCount++) { long lByte = (lValue >>> (lCount << 3)) & 255; String byteHex = Integer.toHexString((int) lByte); if (byteHex.length() == 1) { wordToHex.append("0"); } wordToHex.append(byteHex); } return wordToHex.toString(); } public static String MD5(String message, String charset) { try { byte[] messageBytes; if ("utf-8".equalsIgnoreCase(charset)) { messageBytes = message.getBytes("UTF-8"); } else { messageBytes = message.getBytes(); } long[] x = ConvertToWordArray(messageBytes); // 初始化变量 long a = 0x67452301L; long b = 0xEFCDAB89L; long c = 0x98BADCFEL; long d = 0x10325476L; // 常量定义 final int S11 = 7; final int S12 = 12; final int S13 = 17; final int S14 = 22; final int S21 = 5; final int S22 = 9; final int S23 = 14; final int S24 = 20; final int S31 = 4; final int S32 = 11; final int S33 = 16; final int S34 = 23; final int S41 = 6; final int S42 = 10; final int S43 = 15; final int S44 = 21; // 主循环 for (int k = 0; k < x.length; k += 16) { long AA = a; long BB = b; long CC = c; long DD = d; long[] aArray = {a}; long[] dArray = {d}; long[] cArray = {c}; long[] bArray = {b}; // Round 1 FF(aArray, b, c, d, x[k + 0], S11, 0xD76AA478L); a = aArray[0]; FF(dArray, a, b, c, x[k + 1], S12, 0xE8C7B756L); d = dArray[0]; FF(cArray, d, a, b, x[k + 2], S13, 0x242070DBL); c = cArray[0]; FF(bArray, c, d, a, x[k + 3], S14, 0xC1BDCEEEL); b = bArray[0]; // ... 继续实现所有轮次 a = AddUnsigned(a, AA); b = AddUnsigned(b, BB); c = AddUnsigned(c, CC); d = AddUnsigned(d, DD); } return (WordToHex(a) + WordToHex(b) + WordToHex(c) + WordToHex(d)).toLowerCase(); } catch (Exception e) { throw new RuntimeException("MD5 calculation error", e); } } /** * 生成签名 * @param params 请求参数集合 * @return 签名值 */ public static String generateSign(Map<String, String> params) { // 1. 移除sign参数 params.remove("sign"); params.remove("Sign"); // 2. 按ASCII码排序(字典序) List<String> keys = new ArrayList<>(params.keySet()); Collections.sort(keys); // 3. 拼接字符串 (Key1Value1Key2Value2...) StringBuilder stringA = new StringBuilder(); for (String key : keys) { String value = params.get(key); if (value != null && !value.isEmpty()) { // 将参数名首字母大写 String capitalizedKey = StringUtils.capitalize(key); stringA.append(capitalizedKey).append(value); } } // 4. 拼接APP_SECRET String stringSignTemp = stringA.toString() + APP_SECRET; // 5. MD5运算并转小写 try { MessageDigest md = MessageDigest.getInstance("MD5"); byte[] bytes = stringSignTemp.getBytes("UTF-8"); md.update(bytes); byte[] md5Bytes = md.digest(); StringBuilder hexValue = new StringBuilder(); for (byte b : md5Bytes) { int val = ((int) b) & 0xff; if (val < 16) { hexValue.append("0"); } hexValue.append(Integer.toHexString(val)); } return hexValue.toString().toLowerCase(); } catch (Exception e) { throw new RuntimeException("MD5加密出错", e); } } /** * 验证签名 * @param params 请求参数集合 * @param sign 签名值 * @return 验证结果 */ public static boolean verifySign(Map<String, String> params, String sign) { String generatedSign = generateSign(params); return generatedSign.equalsIgnoreCase(sign); } /** * 使用示例 */ public static void main(String[] args) { // 测试用例 Map<String, String> params = new HashMap<>(); params.put("method", "User_Login"); params.put("LoginType", "0"); params.put("UserID", "10"); String sign = generateSign(params); System.out.println("生成的签名: " + sign); // 应输出: 9a0a8659f005d6984697e2ca0a9cf3b7 } } ruoyi-common/src/main/java/com/ruoyi/common/utils/civilAviation/ServiceOrderUtil.java
New file @@ -0,0 +1,230 @@ package com.ruoyi.common.utils.civilAviation; import com.alibaba.fastjson2.JSON; import com.ruoyi.common.core.domain.entity.ServiceOrderAppVo; import com.ruoyi.common.core.domain.entity.ServiceOrderAppResultVo; import com.ruoyi.common.core.domain.entity.ServiceOrderDelVo; import com.ruoyi.common.utils.http.HttpUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.util.Calendar; /** * 民航医疗接口处理 */ public class ServiceOrderUtil { private static final Logger log = LoggerFactory.getLogger(ServiceOrderUtil.class); public static String AddServiceOrederAPI = "http://localhost:8011/v1/"; /** * 创建服务单 * * @param model 服务单应用值对象 * @return 服务单接口调用结果 */ public static ServiceOrderAppResultVo CreateServiceOrder(ServiceOrderAppVo model) { if (AddServiceOrederAPI.isEmpty()) { log.info("AddServiceOrederAPI 未设置,请先设置接口地址。"); return null; } //计算时间戳和签名 long unixTime = System.currentTimeMillis() / 1000; // 获取当前时间戳(秒) model.setUnixTime(unixTime); // 设置时间戳到模型中 // 将 model 转换为 Map<String, String> Map<String, String> params = new HashMap<>(); java.lang.reflect.Field[] fields = model.getClass().getDeclaredFields(); for (java.lang.reflect.Field field : fields) { field.setAccessible(true); try { Object value = field.get(model); if (value != null) { params.put(field.getName(), value.toString()); } } catch (IllegalAccessException e) { log.error("获取对象属性值时出错", e); } } model.setSign(ServiceOrderSign.generateSign(params)); // 生成签名并设置到模型中 // 替换为 StringBuilder formData = new StringBuilder(); java.lang.reflect.Field[] fieldsQuest = model.getClass().getDeclaredFields(); for (java.lang.reflect.Field field : fieldsQuest) { field.setAccessible(true); try { Object value = field.get(model); if (value != null) { if (formData.length() > 0) { formData.append("&"); } // 将字段名首字母大写 String fieldName = field.getName(); String capitalizedFieldName = fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1); formData.append(capitalizedFieldName) .append("=") .append(value.toString()); } } catch (Exception e) { log.error("拼接表单数据时出错", e); } } String responseData = HttpUtils.sendPost(AddServiceOrederAPI, formData.toString(), "application/x-www-form-urlencoded"); if (responseData != null && !responseData.isEmpty()) { log.info("接口响应: " + responseData); try { // 使用 FastJSON 将响应数据转换为 ServiceOrderAppResultVo 对象 return JSON.parseObject(responseData, ServiceOrderAppResultVo.class); } catch (Exception e) { log.info("解析接口响应数据失败: " + e.getMessage()); } } else { log.info("接口调用失败,未获取到有效响应数据"); } return null; } /** * 取消服务单 * * @param model 服务订单删除请求值对象 * @return 服务订单删除接口调用结果 */ public static ServiceOrderAppResultVo CancelServiceOrder(ServiceOrderDelVo model) { if (AddServiceOrederAPI.isEmpty()) { log.info("AddServiceOrederAPI 未设置,请先设置接口地址。"); return null; } // 计算时间戳和签名 long unixTime = System.currentTimeMillis() / 1000; model.setUnixTime(unixTime); // 将 model 转换为 Map<String, String> Map<String, String> params = new HashMap<>(); java.lang.reflect.Field[] fields = model.getClass().getDeclaredFields(); for (java.lang.reflect.Field field : fields) { field.setAccessible(true); try { Object value = field.get(model); if (value != null) { params.put(field.getName(), value.toString()); } } catch (IllegalAccessException e) { log.error("获取对象属性值时出错", e); } } model.setSign(ServiceOrderSign.generateSign(params)); // 构建表单数据 StringBuilder formData = new StringBuilder(); java.lang.reflect.Field[] fieldsQuest = model.getClass().getDeclaredFields(); for (java.lang.reflect.Field field : fieldsQuest) { field.setAccessible(true); try { Object value = field.get(model); if (value != null) { if (formData.length() > 0) { formData.append("&"); } // 将字段名首字母大写 String fieldName = field.getName(); formData.append(fieldName) .append("=") .append(value.toString()); } } catch (Exception e) { log.error("拼接表单数据时出错", e); } } String responseData = HttpUtils.sendPost(AddServiceOrederAPI, formData.toString(), "application/x-www-form-urlencoded"); if (responseData != null && !responseData.isEmpty()) { log.info("接口响应: " + responseData); try { return JSON.parseObject(responseData, ServiceOrderAppResultVo.class); } catch (Exception e) { log.info("解析接口响应数据失败: " + e.getMessage()); } } else { log.info("接口调用失败,未获取到有效响应数据"); } return null; } public static void main(String[] args) { // 创建服务单应用值对象并设置相关模拟属性 // test(); testCancelServiceOrder(); } //测试取消服务单 public static void testCancelServiceOrder() { // 创建服务单应用值对象并设置相关模拟属性 ServiceOrderDelVo model = new ServiceOrderDelVo(); model.setMethod("ServiceOrder_DEL"); // 设置接口名称 model.setAPPID("GDS-000001"); // 设置商户 APPID model.setServiceOrdID("1016302854"); // 设置服务单ID model.setDELRemarks("测试"); // 调用 CancelServiceOrder 方法取消服务单 ServiceOrderAppResultVo result = CancelServiceOrder(model); System.out.println("1"); } public static void test() { ServiceOrderAppVo model = new ServiceOrderAppVo(); model.setMethod("ServiceOrder_APP"); // 设置接口名称 model.setAppId("GDS-000001"); // 设置商户 APPID model.setOrdType(2); model.setCoPhone("13" + (int) (Math.random() * 900000000 + 100000000)); // 随机生成客户联系电话 model.setCoName("客户" + (int) (Math.random() * 100)); // 随机生成客户姓名 model.setCoTies("家属"); // 随机设置客户联系人与患者关系 // 设置预约时间为一天后 Calendar calendar = Calendar.getInstance(); calendar.add(Calendar.DAY_OF_MONTH, 1); // 增加一天 String testTime = "2025-05-03%2021:33"; model.setApptDate(testTime); // 设置预约运送时间为明天 model.setPtName("患者姓名" + (int) (Math.random() * 100)); // 随机生成患者姓名 model.setPtAge((int) (Math.random() * 100) + "岁"); // 随机生成患者年龄 model.setPtSex(Math.random() > 0.5 ? "男" : "女"); // 随机生成患者性别 model.setPtNat("中国"); // 随机设置患者国籍 model.setOutHosp("随机转出医院" + (int) (Math.random() * 10)); // 随机设置转出医院 model.setInHosp("随机转入医院" + (int) (Math.random() * 10)); // 随机设置转入医院 model.setPtServices("科室" + (int) (Math.random() * 10)); // 随机设置患者所在科室 model.setPtDiagnosis("随机诊断信息"); // 随机设置诊断信息 model.setCondition("随机备注信息"); // 随机设置备注信息 model.setDoctor("医生" + (int) (Math.random() * 10)); // 随机设置患者医生 model.setDoctorPhone("13" + (int) (Math.random() * 900000000 + 100000000)); // 随机生成患者医生电话 model.setOfferPrice(Math.random() * 1000); // 随机生成报价 model.setReferrals("推介人" + (int) (Math.random() * 10)); // 随机设置推介人信息 model.setUnitRemarks("商户随机备注信息"); // 随机设置商户备注 model.setPayQRcodeURL("https://example.com/qrcode/" + (int) (Math.random() * 100)); // 随机生成外部支付二维码链接 // 调用 CreateServiceOrder 方法创建服务单 ServiceOrderAppResultVo result = CreateServiceOrder(model); } } ruoyi-system/pom.xml
@@ -27,6 +27,11 @@ <artifactId>dynamic-datasource-spring-boot-starter</artifactId> <version>3.5.1</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <scope>provided</scope> </dependency> </dependencies>