ruoyi-api/ruoyi-api-payment/src/main/java/com/ruoyi/payment/api/feignClient/WxPaymentClient.java
@@ -8,6 +8,7 @@ import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestParam; import javax.servlet.http.HttpServletRequest; import java.util.Map; @@ -50,4 +51,12 @@ */ @PostMapping("/wx/pay/ack") void ack(); /** * 关闭订单 * @param outTradeNo */ @PostMapping("/wx/pay/close") void close(String outTradeNo); } ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/controller/TChargingOrderController.java
@@ -3,6 +3,7 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.ruoyi.common.core.domain.R; import com.ruoyi.common.redis.service.RedisService; import com.ruoyi.common.security.service.TokenService; import com.ruoyi.common.core.web.domain.AjaxResult; import com.ruoyi.common.core.web.page.BasePage; @@ -16,6 +17,8 @@ import com.ruoyi.order.service.TOrderEvaluateTagService; import com.ruoyi.payment.api.feignClient.WxPaymentClient; import io.swagger.annotations.Api; import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiImplicitParams; import org.springframework.beans.factory.annotation.Autowired; import io.swagger.annotations.ApiOperation; import org.springframework.web.bind.annotation.*; @@ -51,6 +54,9 @@ @Resource private WxPaymentClient wxPaymentClient; @Resource private RedisService redisService; @@ -145,7 +151,7 @@ @ResponseBody @GetMapping(value = "/getNoInvoicedOrder") @ApiOperation(value = "获取未开票的订单数据", tags = {"小程序-充电发票"}) public AjaxResult<List<MyChargingOrderList>> getNoInvoicedOrder(@RequestBody GetNoInvoicedOrder query){ public AjaxResult<List<MyChargingOrderList>> getNoInvoicedOrder(GetNoInvoicedOrder query){ List<MyChargingOrderList> list = chargingOrderService.getNoInvoicedOrder(query); return AjaxResult.success(list); } @@ -167,9 +173,74 @@ @PostMapping(value = "/chargingOrderWXCallback") public void chargingOrderWXCallback(HttpServletRequest request){ Map<String, Object> data = wxPaymentClient.payNotify(request).getData(); if(null != data){ String out_trade_no = data.get("out_trade_no").toString(); String transaction_id = data.get("transaction_id").toString(); String attach = data.get("attach").toString(); AjaxResult ajaxResult = chargingOrderService.chargingOrderCallback(1, out_trade_no, transaction_id, attach); if(ajaxResult.isSuccess()){ wxPaymentClient.ack(); } } } /** * 支付宝支付成功后的回调 * @param request */ @ResponseBody @PostMapping(value = "/chargingOrderALICallback") public void chargingOrderALICallback(HttpServletRequest request){ Map<String, Object> data = wxPaymentClient.payNotify(request).getData(); if(null != data){ String out_trade_no = data.get("out_trade_no").toString(); String transaction_id = data.get("transaction_id").toString(); String attach = data.get("attach").toString(); AjaxResult ajaxResult = chargingOrderService.chargingOrderCallback(2, out_trade_no, transaction_id, attach); if(ajaxResult.isSuccess()){ } } } @ResponseBody @GetMapping(value = "/preChargeCheck/{id}") @ApiOperation(value = "获取安全检测数据", tags = {"小程序-扫一扫"}) @ApiImplicitParams({ @ApiImplicitParam(name = "id", value = "充电枪id", required = true) }) public AjaxResult<PreChargeCheck> preChargeCheck(@PathVariable Integer id){ String key = "AQJC_" + id; Object cacheObject = redisService.getCacheObject(key); return AjaxResult.success(cacheObject); } @ResponseBody @GetMapping(value = "/getChargingDetails/{id}") @ApiOperation(value = "获取充电中页面数据", tags = {"小程序-扫一扫"}) @ApiImplicitParams({ @ApiImplicitParam(name = "id", value = "充电枪id", required = true) }) public AjaxResult<ChargingDetails> getChargingDetails(@PathVariable Integer id){ ChargingDetails chargingDetails = chargingOrderService.getChargingDetails(id); return AjaxResult.success(chargingDetails); } @ResponseBody @PutMapping(value = "/stopCharging/{id}") @ApiOperation(value = "手动停止充电", tags = {"小程序-扫一扫"}) @ApiImplicitParams({ @ApiImplicitParam(name = "id", value = "订单id", required = true) }) public AjaxResult stopCharging(@PathVariable String id){ return chargingOrderService.stopCharging(id); } } ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/controller/TExchangeOrderController.java
@@ -1,4 +1,5 @@ package com.ruoyi.order.controller; import java.text.SimpleDateFormat; import java.time.LocalDateTime; @@ -27,6 +28,7 @@ import javax.annotation.Resource; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.stream.Collectors; @@ -158,7 +160,9 @@ @PostMapping("/create") public R<Long> exchangeCreate(@RequestBody ExchangeDto exchangeDto){ TExchangeOrder tExchangeOrder = new TExchangeOrder(); tExchangeOrder.setCode(""); SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS"); String code = "DH" + Math.random() * 1000 + sdf.format(new Date()); tExchangeOrder.setCode(code); tExchangeOrder.setAppUserId(exchangeDto.getUserId()); tExchangeOrder.setOrderType(0); tExchangeOrder.setGoodsId(0); ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/dto/ChargingDetails.java
New file @@ -0,0 +1,42 @@ package com.ruoyi.order.dto; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import java.math.BigDecimal; /** * @author zhibing.pu * @Date 2024/8/21 14:09 */ @Data @ApiModel public class ChargingDetails { @ApiModelProperty("订单id") private String id; @ApiModelProperty("充电电流") private BigDecimal chargeCurrent; @ApiModelProperty("充电电压") private BigDecimal ChargeVoltage; @ApiModelProperty("充电功率") private BigDecimal ChargePower; @ApiModelProperty("完成比例") private Integer completionRatio; @ApiModelProperty("续航") private BigDecimal endurance; @ApiModelProperty("剩余充电时间") private Long remainingChargeTime; @ApiModelProperty("充电枪名") private String name; @ApiModelProperty("订单编号") private String code; @ApiModelProperty("已充电度数") private BigDecimal chargedDegrees; @ApiModelProperty("已充电时间") private Long chargedTime; @ApiModelProperty("充电费用") private BigDecimal ChargingCost; @ApiModelProperty("充电状态(0=未知,1=等待中,2=启动中,3=充电中,4=停止中,5=已结束)") private Integer status; } ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/dto/MyChargingOrderList.java
@@ -21,6 +21,8 @@ private String title; @ApiModelProperty("充电度数") private BigDecimal chargingDegree; @ApiModelProperty("充电枪id") private Integer chargingGunId; @ApiModelProperty("充电桩枪名称") private String name; @ApiModelProperty("结束方式(0=异常终止,1=主动终止,2=满电终止,3=费用不足终止)") ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/dto/PreChargeCheck.java
New file @@ -0,0 +1,20 @@ package com.ruoyi.order.dto; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; /** * @author zhibing.pu * @Date 2024/8/21 13:47 */ @Data @ApiModel public class PreChargeCheck { @ApiModelProperty("安全连接检测") private Boolean secureConnectionDetection; @ApiModelProperty("绝缘检测") private Boolean insulationTesting; @ApiModelProperty("电子锁锁止") private Boolean electronicLockLock; } ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/TChargingOrderService.java
@@ -62,4 +62,20 @@ * @return */ AjaxResult chargingOrderCallback(Integer paymentType, String out_trade_no, String transaction_id, String attach); /** * 获取充电中的详情 * @param id * @return */ ChargingDetails getChargingDetails(Integer id); /** * 停止充电 * @param id * @return */ AjaxResult stopCharging(String id); } ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/TChargingOrderServiceImpl.java
@@ -17,6 +17,7 @@ import com.ruoyi.chargingPile.api.model.TChargingPile; import com.ruoyi.common.core.web.domain.AjaxResult; import com.ruoyi.common.core.web.page.BasePage; import com.ruoyi.common.redis.service.RedisService; import com.ruoyi.common.security.service.TokenService; import com.ruoyi.common.security.utils.SecurityUtils; import com.ruoyi.order.api.model.TChargingOrder; @@ -77,6 +78,9 @@ @Resource private AppUserVipDetailClient appUserVipDetailClient; @Resource private RedisService redisService; @@ -101,6 +105,7 @@ myChargingOrderList.setTitle(site.getName()); myChargingOrderList.setChargingDegree(tChargingOrder.getChargingCapacity()); String name = chargingGunClient.getAllName(tChargingOrder.getChargingGunId()).getData(); myChargingOrderList.setChargingGunId(tChargingOrder.getChargingGunId()); myChargingOrderList.setName(name); myChargingOrderList.setEndMode(tChargingOrder.getEndMode()); BigDecimal payMoney = tChargingOrder.getStatus() < 4 ? tChargingOrder.getRechargeAmount() : tChargingOrder.getPaymentAmount(); @@ -220,7 +225,7 @@ } } // //检查当前枪是否是正在使用中 TChargingOrder one = this.getOne(new LambdaQueryWrapper<TChargingOrder>().eq(TChargingOrder::getChargingGunId, addChargingOrder.getId()) .in(TChargingOrder::getStatus, Arrays.asList(1, 2, 3)).eq(TChargingOrder::getDelFlag, 0)); if(null != one){ @@ -230,14 +235,31 @@ one = this.getOne(new LambdaQueryWrapper<TChargingOrder>().eq(TChargingOrder::getChargingGunId, addChargingOrder.getId()) .eq(TChargingOrder::getAppUserCarId, userId).eq(TChargingOrder::getRechargePaymentStatus, 1).eq(TChargingOrder::getDelFlag, 0)); if(null != one){ //查询三方支付数据,支付中直接结束三方订单 if(1 == one.getRechargePaymentType()){ NotifyV3PayDecodeRespBody data = wxPaymentClient.queryOrderInfo(one.getCode()).getData(); if(null != data){ String trade_state = data.getTrade_state(); //支付失败,删除无效的订单 if(trade_state.equals("REFUND") || trade_state.equals("NOTPAY") || trade_state.equals("REVOKED") || trade_state.equals("PAYERROR")){ this.removeById(one.getId()); }else{ //结束第三方支付,删除订单 wxPaymentClient.close(one.getCode()); this.removeById(one.getId()); } } } if(2 == one.getRechargePaymentType()){ } } //构建新的待支付订单 SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS"); TChargingOrder chargingOrder = new TChargingOrder(); String code = Math.random() * 1000 + sdf.format(new Date()); String code = "CD" + Math.random() * 1000 + sdf.format(new Date()); chargingOrder.setCode(code); chargingOrder.setOrderType(1); chargingOrder.setOrderClassification(1); @@ -303,8 +325,65 @@ } chargingOrder.setRechargePaymentStatus(2); chargingOrder.setRechargeSerialNumber(transaction_id); this.updateById(chargingOrder); //添加安全检测数据到缓存中,每步安全检测完成后需要更新缓存数据 PreChargeCheck preChargeCheck = new PreChargeCheck(); preChargeCheck.setElectronicLockLock(false); preChargeCheck.setInsulationTesting(false); preChargeCheck.setSecureConnectionDetection(false); String key = "AQJC_" + chargingOrder.getChargingGunId(); redisService.setCacheObject(key, preChargeCheck); //开始检测充电桩状,将检查状态写入缓存。检测完成后开始开启充电 //充电结束后需要计算退款剩余金额 // todo 需要完善 return AjaxResult.success(); } /** * 获取充电中的详情 * @param id * @return */ @Override public ChargingDetails getChargingDetails(Integer id) { Long userId = tokenService.getLoginUserApplet().getUserId(); TChargingOrder one = this.getOne(new LambdaQueryWrapper<TChargingOrder>().eq(TChargingOrder::getAppUserId, userId).eq(TChargingOrder::getChargingGunId, id) .in(TChargingOrder::getStatus, Arrays.asList(1, 2, 3)).eq(TChargingOrder::getRechargePaymentStatus, 2).eq(TChargingOrder::getDelFlag, 0)); if(null == one){ return null; } // todo 需要完善充电实时数据 ChargingDetails chargingDetails = new ChargingDetails(); chargingDetails.setId(one.getId().toString()); TChargingGun chargingGun = chargingGunClient.getChargingGunById(id).getData(); TChargingPile chargingPile = chargingPileClient.getChargingPileById(chargingGun.getChargingPileId()).getData(); Site site = siteClient.getSiteByIds(Arrays.asList(chargingPile.getSiteId())).getData().get(0); chargingDetails.setName(site.getName() + "-" + chargingPile.getName()); chargingDetails.setCode(one.getCode()); chargingDetails.setStatus(one.getStatus()); return chargingDetails; } /** * 停止充电操作 * @param id 订单id * @return */ @Override public AjaxResult stopCharging(String id) { TChargingOrder chargingOrder = this.getById(id); Integer status = chargingOrder.getStatus(); if(status == 4 || status == 5){ return AjaxResult.error("不能重复操作"); } chargingOrder.setStatus(4); chargingOrder.setEndMode(1); this.updateById(chargingOrder); //调用硬件停止充电,停止成功后开始计算费用退款 // todo 待完善 return AjaxResult.success(); } } ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/wx/controller/WxPayController.java
@@ -151,10 +151,18 @@ * @return */ @PostMapping("query/queryOrderInfo") public R<NotifyV3PayDecodeRespBody> queryOrderInfo(String orderId){ // todo 需要完善商户号 NotifyV3PayDecodeRespBody query = wxV3Pay.query(orderId, ""); public R<NotifyV3PayDecodeRespBody> queryOrderInfo(@RequestParam("orderId") String orderId){ NotifyV3PayDecodeRespBody query = wxV3Pay.query(orderId); return R.ok(query); } /** * 关闭订单 * @param outTradeNo */ @PostMapping("pay/close") public void close(@RequestParam("outTradeNo") String outTradeNo){ wxV3Pay.close(outTradeNo); } } ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/wx/model/WxCloseOrderModel.java
New file @@ -0,0 +1,22 @@ package com.ruoyi.payment.wx.model; import lombok.*; import java.util.List; /** * @author xiaochen * @ClassName WxPaymentRefundModel * @Description */ @Builder @AllArgsConstructor @NoArgsConstructor @Getter @Setter @ToString public class WxCloseOrderModel { private String mchid; private String out_trade_no; } ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/wx/utils/WxAbstractPay.java
@@ -2,6 +2,7 @@ import com.fasterxml.jackson.core.type.TypeReference; import com.ruoyi.common.core.utils.WebUtils; import com.ruoyi.payment.wx.model.WxCloseOrderModel; import com.ruoyi.payment.wx.model.WxPaymentInfoModel; import com.ruoyi.payment.wx.model.WxPaymentRefundModel; import com.ruoyi.payment.wx.resp.NotifyV3PayDecodeRespBody; @@ -210,7 +211,7 @@ * @param mchid * @return */ public abstract NotifyV3PayDecodeRespBody query(String out_trade_no, String mchid); public abstract NotifyV3PayDecodeRespBody query(String out_trade_no); /** @@ -220,6 +221,10 @@ * @return */ public abstract Map<String, Object> refund(WxPaymentRefundModel refundModel); public abstract String close(String out_trade_no); /** * 订单退款 @@ -330,4 +335,29 @@ } } /** * 关闭订单 * @param httpClient * @param uri * @param httpReadTimeoutMs * @param httpConnectTimeoutMs * @param closeModel * @return */ public String close(CloseableHttpClient httpClient, String uri, int httpReadTimeoutMs, int httpConnectTimeoutMs, WxCloseOrderModel closeModel) { String reqBody = WxJsonUtils.toJsonString(closeModel); //请求URL HttpEntityEnclosingRequestBase httpPost = requestPost( uri , httpReadTimeoutMs , httpConnectTimeoutMs, reqBody); String repBody = result(httpClient, httpPost); return repBody; } } ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/wx/utils/WxV3Pay.java
@@ -5,6 +5,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.ruoyi.payment.wx.model.WeixinProperties; import com.ruoyi.payment.wx.model.WxCloseOrderModel; import com.ruoyi.payment.wx.model.WxPaymentInfoModel; import com.ruoyi.payment.wx.model.WxPaymentRefundModel; import com.ruoyi.payment.wx.resp.NotifyV3PayDecodeRespBody; @@ -170,15 +171,14 @@ * 订单查询 * * @param out_trade_no * @param mchid * @return com.abl.biz.center.payment.wx.v3.NotifyV3PayDecodeRespBody * @author xiaochen * @date 2021-12-20 16:47 */ @Override public NotifyV3PayDecodeRespBody query(String out_trade_no, String mchid) { public NotifyV3PayDecodeRespBody query(String out_trade_no) { String url = String.format("/v3/pay/transactions/out-trade-no/%s", out_trade_no) + String.format("?mchid=%s", mchid); String.format("/v3/pay/transactions/out-trade-no/%s", out_trade_no) + String.format("?mchid=%s", this.getConfig().getMchId()); return query(this.httpClient, this.config.getHttpReadTimeoutMs(), this.config.getHttpConnectTimeoutMs(), url); } @@ -195,4 +195,17 @@ return refund(this.httpClient, "/v3/refund/domestic/refunds", this.config.getHttpReadTimeoutMs(), this.config.getHttpConnectTimeoutMs(), refundModel); } /** * 关闭订单 * @param out_trade_no * @return */ @Override public String close(String out_trade_no) { String uri = String.format("/v3/pay/transactions/out-trade-no/%s/close", out_trade_no); WxCloseOrderModel wxCloseOrderModel = new WxCloseOrderModel(); wxCloseOrderModel.setMchid(this.config.getMchId()); return close(this.httpClient, uri, this.config.getHttpReadTimeoutMs(), this.config.getHttpConnectTimeoutMs(), wxCloseOrderModel); } }