ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/OrderController.java
@@ -3,6 +3,8 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.ruoyi.common.core.domain.R; import com.ruoyi.framework.web.service.TokenService; import com.ruoyi.system.dto.CreateOrderDto; import com.ruoyi.system.dto.CreateOrderRequest; import com.ruoyi.system.query.AppletOrderListQuery; import com.ruoyi.system.service.TAppUserService; import com.ruoyi.system.service.TTicketOrderService; @@ -52,6 +54,22 @@ } /** * 下单 * */ @ApiOperation(value = "下单") @PostMapping("/addOrder") public R<Void> addOrder(@RequestBody CreateOrderDto request) throws Exception { Integer userId = tokenService.getLoginUserApplet().getUserId(); boolean save = tTicketOrderService.addOrder(request, userId); if (!save) { return R.fail("下单失败"); } return R.ok(); } } ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/TicketController.java
@@ -2,16 +2,12 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.ruoyi.common.core.domain.R; import com.ruoyi.framework.web.service.TokenService; import com.ruoyi.system.model.TTourist; import com.ruoyi.system.query.AppletOrderListQuery; import com.ruoyi.system.service.TAppUserService; import com.ruoyi.system.service.TTicketOrderService; import com.ruoyi.system.service.TTouristService; import com.ruoyi.system.vo.AppletOrderListVO; import com.ruoyi.system.vo.TTicketOrderVo; import com.ruoyi.system.vo.TicketListVo; import com.ruoyi.system.utils.ticketUtil.TicketUtil; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.RequiredArgsConstructor; @@ -37,8 +33,8 @@ public class TicketController { private final TokenService tokenService; private final TAppUserService appUserService; private final TTouristService tTouristService; private final TicketUtil ticketUtil; @ApiOperation(value = "获取游客信息", response = TTourist.class) @@ -48,8 +44,6 @@ List<TTourist> list = tTouristService.list(new LambdaQueryWrapper<TTourist>().eq(TTourist::getAppUserId, userId)); return R.ok(list); } @ApiOperation(value = "获取游客详情信息", response = TTourist.class) @@ -109,4 +103,12 @@ return R.ok(); } @ApiOperation(value = "获取门票信息", response = TicketListVo.class) @GetMapping("/ticketList") public R<List<TicketListVo>> ticketList() throws Exception { List<TicketListVo> list = ticketUtil.getTicketList(); return R.ok(list); } } ruoyi-system/pom.xml
@@ -127,6 +127,12 @@ <scope>system</scope> <systemPath>${project.basedir}/lib/icbc-ca.jar</systemPath> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>2.0.24</version> <scope>compile</scope> </dependency> </dependencies> ruoyi-system/src/main/java/com/ruoyi/system/dto/CreateOrderDto.java
New file @@ -0,0 +1,33 @@ package com.ruoyi.system.dto;// 请求DTO import io.swagger.annotations.ApiModelProperty; import lombok.Data; import javax.validation.constraints.*; import java.util.List; @Data public class CreateOrderDto { @NotBlank(message = "游玩日期不能为空") @Pattern(regexp = "^\\d{4}-\\d{2}-\\d{2}$", message = "游玩日期格式错误,应为yyyy-MM-dd") @ApiModelProperty(value = "游玩日期") private String travelDate; @NotNull(message = "预订数量不能为空") @Min(value = 1, message = "预订数量必须大于0") @ApiModelProperty(value = "预订数量") private Integer quantity; @NotBlank(message = "产品编号不能为空") @ApiModelProperty(value = "产品编号") private String productId; @NotBlank(message = "产品名称") @ApiModelProperty(value = "产品名称") private String productName; @NotEmpty(message = "游玩人Ids不能为空") @ApiModelProperty(value = "游玩人Ids") private List<Integer> visitPersons; } ruoyi-system/src/main/java/com/ruoyi/system/dto/CreateOrderRequest.java
New file @@ -0,0 +1,38 @@ package com.ruoyi.system.dto;// 请求DTO import lombok.Data; import javax.validation.constraints.*; import java.util.List; @Data public class CreateOrderRequest { @NotBlank(message = "不传") private String orderId; @NotBlank(message = "游玩日期不能为空") @Pattern(regexp = "^\\d{4}-\\d{2}-\\d{2}$", message = "游玩日期格式错误,应为yyyy-MM-dd") private String travelDate; @Pattern(regexp = "^\\d{2}:\\d{2}~\\d{2}:\\d{2}$", message = "分时预约时段格式错误,应为HH:mm~HH:mm") private String travelTime; @NotNull(message = "预订数量不能为空") @Min(value = 1, message = "预订数量必须大于0") private Integer quantity; @NotBlank(message = "产品编号不能为空") private String productId; @NotBlank(message = "预订人姓名") private String bookName; @NotBlank(message = "预订人手机号") private String bookMobile; @NotBlank(message = "预订人身份证号") private String idCard; @NotEmpty(message = "游玩人信息不能为空") private List<VisitPerson> visitPersons; } ruoyi-system/src/main/java/com/ruoyi/system/dto/VisitPerson.java
New file @@ -0,0 +1,23 @@ package com.ruoyi.system.dto; import lombok.Data; import javax.validation.constraints.NotBlank; import javax.validation.constraints.Pattern; @Data public class VisitPerson { @NotBlank(message = "游玩人姓名") private String name; @NotBlank(message = "游玩人手机号") private String mobile; @NotBlank(message = "身份证号不能为空") @Pattern(regexp = "^[1-9]\\d{5}(18|19|20)\\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\\d|3[01])\\d{3}[\\dXx]$", message = "身份证号格式不正确") private String idCard; @NotBlank(message = "人像图片") private String face; // 人像图片base64 } ruoyi-system/src/main/java/com/ruoyi/system/model/TTicketOrder.java
@@ -76,6 +76,10 @@ @TableField("refund_reason") private String refundReason; @ApiModelProperty(value = "开放平台订单号") @TableField("partner_order_id") private String partnerOrderId; @ApiModelProperty(value = "审核意见") @TableField("audit_content") private String auditContent; ruoyi-system/src/main/java/com/ruoyi/system/service/TTicketOrderService.java
@@ -1,6 +1,8 @@ package com.ruoyi.system.service; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.ruoyi.system.dto.CreateOrderDto; import com.ruoyi.system.dto.CreateOrderRequest; import com.ruoyi.system.model.TTicketOrder; import com.baomidou.mybatisplus.extension.service.IService; import com.ruoyi.system.query.AppletOrderListQuery; @@ -24,4 +26,6 @@ Page<TTicketOrderVo> refundPageList(Page<TTicketOrderVo> page, TTicketOrderQuery query); boolean addOrder(CreateOrderDto request, Integer userId) throws Exception; } ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TTicketOrderServiceImpl.java
@@ -1,16 +1,28 @@ package com.ruoyi.system.service.impl; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.ruoyi.common.utils.OrderNos; import com.ruoyi.system.dto.CreateOrderDto; import com.ruoyi.system.dto.CreateOrderRequest; import com.ruoyi.system.dto.VisitPerson; import com.ruoyi.system.model.TTicketOrder; import com.ruoyi.system.mapper.TTicketOrderMapper; import com.ruoyi.system.model.TTourist; import com.ruoyi.system.query.AppletOrderListQuery; import com.ruoyi.system.query.TTicketOrderQuery; import com.ruoyi.system.service.TAppUserService; import com.ruoyi.system.service.TTicketOrderService; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.ruoyi.system.service.TTouristService; import com.ruoyi.system.utils.ticketUtil.TicketUtil; import com.ruoyi.system.vo.CreateOrderVo; import com.ruoyi.system.vo.TTicketOrderVo; import org.springframework.stereotype.Service; import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; /** * <p> @@ -22,6 +34,19 @@ */ @Service public class TTicketOrderServiceImpl extends ServiceImpl<TTicketOrderMapper, TTicketOrder> implements TTicketOrderService { private final TicketUtil ticketUtil; private final TTouristService tTouristService; public TTicketOrderServiceImpl(TicketUtil ticketUtil, TTouristService tTouristService) { this.ticketUtil = ticketUtil; this.tTouristService = tTouristService; } @Override public Page<TTicketOrderVo> pageList(Page<TTicketOrderVo> page, TTicketOrderQuery query) { @@ -44,4 +69,49 @@ page.setRecords( list); return page; } @Override public boolean addOrder(CreateOrderDto dto, Integer userId) throws Exception { CreateOrderRequest request = new CreateOrderRequest(); String did = OrderNos.getDid(); request.setOrderId(did); request.setTravelDate(dto.getTravelDate()); request.setQuantity(dto.getQuantity()); request.setProductId(dto.getProductId()); if (dto.getQuantity()!=dto.getVisitPersons().size()) { throw new RuntimeException("人数不匹配"); } List<TTourist> tTourists = tTouristService.listByIds(dto.getVisitPersons()); ArrayList<VisitPerson> visitPeople = new ArrayList<>(); for (TTourist tTourist : tTourists) { VisitPerson visitPerson = new VisitPerson(); visitPerson.setName(tTourist.getName()); visitPerson.setMobile(tTourist.getPhone()); visitPerson.setIdCard(tTourist.getIdCard()); visitPeople.add(visitPerson); } request.setVisitPersons(visitPeople); CreateOrderVo createOrderVo = ticketUtil.saveOrder(request); TTicketOrder order = new TTicketOrder(); order.setAppUserId(userId); order.setOrderNo(did); order.setPartnerOrderId(createOrderVo.getPartnerOrderId()); order.setOrderPrice(new BigDecimal(createOrderVo.getOrderAmount()).setScale(2, BigDecimal.ROUND_HALF_UP)); String result = dto.getVisitPersons().stream() .map(String::valueOf) // Integer 转 String .collect(Collectors.joining(",")); order.setTourist(result); order.setTicketNum(dto.getQuantity()); order.setStatus(0); order.setTicketInfo(dto.getProductName()); boolean save = this.save(order); return save; } } ruoyi-system/src/main/java/com/ruoyi/system/utils/ticketUtil/DesUtil.java
New file @@ -0,0 +1,109 @@ package com.ruoyi.system.utils.ticketUtil; import java.io.IOException; import java.security.SecureRandom; import java.util.Base64; import javax.crypto.Cipher; import javax.crypto.SecretKey; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.DESKeySpec; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import sun.misc.BASE64Decoder; import sun.misc.BASE64Encoder; public class DesUtil { private static final String DES = "DES"; /** * Description 根据键值进行加密 * @param data * @param key 加密键byte数组 * @return * @throws Exception */ public static String encrypt(String data, String key) throws Exception { byte[] bt = encrypt(data.getBytes("UTF-8"), key.getBytes("UTF-8")); String strs = new BASE64Encoder().encode(bt); return strs; } /** * Description 根据键值进行解密 * @param data * @param key 加密键byte数组 * @return * @throws IOException * @throws Exception */ public static String decrypt(String data, String key) throws Exception, Exception { if (data == null) return null; BASE64Decoder decoder = new BASE64Decoder(); byte[] buf = decoder.decodeBuffer(data); byte[] bt = decrypt(buf,key.getBytes("UTF-8")); return new String(bt, "UTF-8"); } /** * Description 根据键值进行加密 * @param data * @param key 加密键byte数组 * @return * @throws Exception */ private static byte[] encrypt(byte[] data, byte[] key) throws Exception { // 生成一个可信任的随机数源 SecureRandom sr = new SecureRandom(); // 从原始密钥数据创建DESKeySpec对象 DESKeySpec dks = new DESKeySpec(key); // 创建一个密钥工厂,然后用它把DESKeySpec转换成SecretKey对象 SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(DES); SecretKey securekey = keyFactory.generateSecret(dks); // Cipher对象实际完成加密操作 Cipher cipher = Cipher.getInstance(DES); // 用密钥初始化Cipher对象 cipher.init(Cipher.ENCRYPT_MODE, securekey, sr); return cipher.doFinal(data); } /** * Description 根据键值进行解密 * @param data * @param key 加密键byte数组 * @return * @throws Exception */ private static byte[] decrypt(byte[] data, byte[] key) throws Exception { // 生成一个可信任的随机数源 SecureRandom sr = new SecureRandom(); // 从原始密钥数据创建DESKeySpec对象 DESKeySpec dks = new DESKeySpec(key); // 创建一个密钥工厂,然后用它把DESKeySpec转换成SecretKey对象 SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(DES); SecretKey securekey = keyFactory.generateSecret(dks); // Cipher对象实际完成解密操作 Cipher cipher = Cipher.getInstance(DES); // 用密钥初始化Cipher对象 cipher.init(Cipher.DECRYPT_MODE, securekey, sr); return cipher.doFinal(data); } public static void main(String[] args) throws Exception { } /** * 辅助方法:将字符串进行Base64编码 */ public static String toBase64(String data) throws Exception { return Base64.getEncoder().encodeToString(data.getBytes("UTF-8")); } /** * 辅助方法:将Base64字符串解码 */ public static String fromBase64(String base64Data) throws Exception { byte[] decodedBytes = Base64.getDecoder().decode(base64Data); return new String(decodedBytes, "UTF-8"); } } ruoyi-system/src/main/java/com/ruoyi/system/utils/ticketUtil/TicketUtil.java
New file @@ -0,0 +1,228 @@ package com.ruoyi.system.utils.ticketUtil; import cn.hutool.crypto.SecureUtil; import cn.hutool.http.HttpRequest; import cn.hutool.http.HttpResponse; import cn.hutool.http.HttpUtil; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.ruoyi.system.dto.CreateOrderRequest; import com.ruoyi.system.vo.CreateOrderVo; import com.ruoyi.system.vo.TicketListVo; import java.util.ArrayList; import java.util.Base64; import java.util.HashMap; import java.util.List; /** * 门票接口 */ public class TicketUtil { public List<TicketListVo> getTicketList() throws Exception { ArrayList<TicketListVo> ticketListVos = new ArrayList<>(); String userId = "1d9a42e0-9de3-451b-9964-4e9f8e62b101"; String method = "getProductInfo"; String timestamp = System.currentTimeMillis() + ""; String desKey = "TFD9BEOK"; // DES密钥必须是8字节 String userKey = "TFD9BEOK"; // 用于签名的用户密钥 // 1. 构建请求体数据 HashMap<String, Object> bodyData = new HashMap<>(); bodyData.put("pageIndex", 1); bodyData.put("pageSize", 100); bodyData.put("sceneryNo", ""); String requestBody = JSON.toJSONString(bodyData); System.out.println("原始请求体: " + requestBody); // 2. 使用DES加密请求体 String encryptedBody = DesUtil.encrypt(requestBody, desKey); System.out.println("DES加密后的请求体: " + encryptedBody); // 3. 生成签名 String params = userId + method + timestamp + encryptedBody + userKey; String md5 = SecureUtil.md5(params); System.out.println("MD5签名: " + md5); // 4. 构建请求头 HashMap<String, Object> headMap = new HashMap<>(); headMap.put("user_id", userId); headMap.put("method", method); headMap.put("timestamp", timestamp); headMap.put("sign", md5); // 5. 构建完整的请求消息 HashMap<String, Object> fullRequestMap = new HashMap<>(); fullRequestMap.put("requestHead", headMap); fullRequestMap.put("requestBody", encryptedBody); // 6. 将完整消息转换为JSON字符串 String fullRequestJson = JSON.toJSONString(fullRequestMap); System.out.println("完整请求JSON: " + fullRequestJson); // 7. 对整个JSON字符串进行Base64编码 String base64EncodedRequest = Base64.getEncoder().encodeToString(fullRequestJson.getBytes("UTF-8")); System.out.println("Base64编码后的请求: " + base64EncodedRequest); // 8. 发送HTTP请求 HttpRequest post = HttpUtil.createPost("http://ota.huilvyun.com.cn:8086/scenic-ota/ota/v2/zshl/rent034/getProductInfo"); // 设置请求头 post.header("Content-Type", "text/plain;charset=UTF-8"); // 根据要求,可能是纯文本 // 或者如果是application/json,则使用: // post.header("Content-Type", "application/json;charset=UTF-8"); // 直接将Base64编码后的字符串作为body发送 post.body(base64EncodedRequest); System.out.println("发送请求..."); HttpResponse execute = post.execute(); // 9. 处理响应 System.out.println("响应状态码: " + execute.getStatus()); String responseBody = execute.body(); System.out.println("原始响应Body: " + responseBody); // 10. 解析响应(假设响应也是Base64编码的) if (responseBody != null && !responseBody.isEmpty()) { try { // 先Base64解码 byte[] decodedBytes = Base64.getDecoder().decode(responseBody); String decodedResponse = new String(decodedBytes, "UTF-8"); System.out.println("Base64解码后的响应: " + decodedResponse); String responseBody1 = JSONObject.parseObject(decodedResponse).get("responseBody").toString(); String decryptedResponseBody = DesUtil.decrypt(responseBody1, desKey); System.out.println("解密后的响应: " + decryptedResponseBody); JSONArray productList = JSONObject.parseObject(decryptedResponseBody).getJSONArray("productList"); for (int i = 0; i < productList.size(); i++) { JSONObject product = productList.getJSONObject(i); TicketListVo ticketListVo = new TicketListVo(); ticketListVo.setProductId(product.getString("productId")); ticketListVo.setProductName(product.getString("productName")); ticketListVo.setPrice(product.getLong("price")); ticketListVo.setSceneryNo(product.getString("sceneryNo")); ticketListVo.setSceneryName(product.getString("sceneryName")); ticketListVo.setBeginSaleDate(product.getString("beginSaleDate")); ticketListVo.setEndSaleDate(product.getString("endSaleDate")); ticketListVo.setIsCanRefund(product.getInteger("isCanRefund")); ticketListVos.add(ticketListVo); } return ticketListVos; } catch (IllegalArgumentException e) { System.out.println("响应不是有效的Base64格式: " + e.getMessage()); } } return ticketListVos; } public CreateOrderVo saveOrder(CreateOrderRequest request) throws Exception { String userId = "1d9a42e0-9de3-451b-9964-4e9f8e62b101"; String method = "createOrder"; String timestamp = System.currentTimeMillis() + ""; String desKey = "TFD9BEOK"; // DES密钥必须是8字节 String userKey = "TFD9BEOK"; // 用于签名的用户密钥 // 1. 构建请求体数据 // HashMap<String, Object> bodyData = new HashMap<>(); // bodyData.put("pageIndex", 1); // bodyData.put("pageSize", 100); // bodyData.put("sceneryNo", ""); String requestBody = JSON.toJSONString(request); System.out.println("原始请求体: " + requestBody); // 2. 使用DES加密请求体 String encryptedBody = DesUtil.encrypt(requestBody, desKey); System.out.println("DES加密后的请求体: " + encryptedBody); // 3. 生成签名 String params = userId + method + timestamp + encryptedBody + userKey; String md5 = SecureUtil.md5(params); System.out.println("MD5签名: " + md5); // 4. 构建请求头 HashMap<String, Object> headMap = new HashMap<>(); headMap.put("user_id", userId); headMap.put("method", method); headMap.put("timestamp", timestamp); headMap.put("sign", md5); // 5. 构建完整的请求消息 HashMap<String, Object> fullRequestMap = new HashMap<>(); fullRequestMap.put("requestHead", headMap); fullRequestMap.put("requestBody", encryptedBody); // 6. 将完整消息转换为JSON字符串 String fullRequestJson = JSON.toJSONString(fullRequestMap); System.out.println("完整请求JSON: " + fullRequestJson); // 7. 对整个JSON字符串进行Base64编码 String base64EncodedRequest = Base64.getEncoder().encodeToString(fullRequestJson.getBytes("UTF-8")); System.out.println("Base64编码后的请求: " + base64EncodedRequest); // 8. 发送HTTP请求 HttpRequest post = HttpUtil.createPost("http://ota.huilvyun.com.cn:8086/scenic-ota/ota/v2/zshl/rent034/createOrder"); // 设置请求头 post.header("Content-Type", "text/plain;charset=UTF-8"); // 根据要求,可能是纯文本 // 或者如果是application/json,则使用: // post.header("Content-Type", "application/json;charset=UTF-8"); // 直接将Base64编码后的字符串作为body发送 post.body(base64EncodedRequest); System.out.println("发送请求..."); HttpResponse execute = post.execute(); // 9. 处理响应 System.out.println("响应状态码: " + execute.getStatus()); String responseBody = execute.body(); System.out.println("原始响应Body: " + responseBody); // 10. 解析响应(假设响应也是Base64编码的) if (responseBody != null && !responseBody.isEmpty()) { try { // 先Base64解码 byte[] decodedBytes = Base64.getDecoder().decode(responseBody); String decodedResponse = new String(decodedBytes, "UTF-8"); System.out.println("Base64解码后的响应: " + decodedResponse); String res_code = JSONObject.parseObject(decodedResponse).get("res_code").toString(); if("1000".equals(res_code)){ String responseBody1 = JSONObject.parseObject(decodedResponse).get("responseBody").toString(); String decryptedResponseBody = DesUtil.decrypt(responseBody1, desKey); System.out.println("解密后的响应: " + decryptedResponseBody); JSONObject jsonObject = JSONObject.parseObject(decryptedResponseBody); CreateOrderVo createOrderVo = new CreateOrderVo(); createOrderVo.setOrderId(jsonObject.getString("orderId")); createOrderVo.setOrderAmount(jsonObject.getString("orderAmount")); createOrderVo.setPartnerOrderId(jsonObject.getString("partnerOrderId")); return createOrderVo; }else { String res_msg = JSONObject.parseObject(decodedResponse).get("res_msg").toString(); System.out.println("错误信息: " + res_msg); throw new RuntimeException(res_msg); } } catch (IllegalArgumentException e) { System.out.println("响应不是有效的Base64格式: " + e.getMessage()); } } return null; } public static void main(String[] args) { try { } catch (Exception e) { throw new RuntimeException(e); } } } ruoyi-system/src/main/java/com/ruoyi/system/vo/CreateOrderVo.java
New file @@ -0,0 +1,10 @@ package com.ruoyi.system.vo; import lombok.Data; @Data public class CreateOrderVo { private String orderId; // 分销商订单号 private String partnerOrderId; // 开放平台订单号 private String orderAmount; // 订单金额 } ruoyi-system/src/main/java/com/ruoyi/system/vo/TicketListVo.java
New file @@ -0,0 +1,60 @@ package com.ruoyi.system.vo; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; /** * 门票信息Vo */ @ApiModel("门票信息Vo") @Data public class TicketListVo { /** * 门票ID */ @ApiModelProperty("门票ID") private String productId; /** * 门票名称 */ @ApiModelProperty("门票名称") private String productName; /** * 门票价格 */ @ApiModelProperty("门票价格") private Long price; /** * 景点编号 */ @ApiModelProperty("景点编号") private String sceneryNo; /** * 景点名称 */ @ApiModelProperty("景点名称") private String sceneryName; /** * 开售时间 */ @ApiModelProperty("开售时间") private String beginSaleDate; /** * 截止时间 */ @ApiModelProperty("截止时间") private String endSaleDate; /** * 是否可退(0不支持 1支持) */ @ApiModelProperty("是否可退(0不支持 1支持)") private Integer isCanRefund; }