| @@ -0,0 +1,111 @@ | |||||
| package com.iformall.controller.callback.car.ketuo; | |||||
| import com.alibaba.fastjson.JSON; | |||||
| import com.alibaba.fastjson.JSONObject; | |||||
| import com.github.pagehelper.PageInfo; | |||||
| import com.iformall.common.Result; | |||||
| import com.iformall.controller.base.BaseController; | |||||
| import com.iformall.controller.callback.car.WxParkCallBackBaseController; | |||||
| import com.iformall.domain.po.*; | |||||
| import com.iformall.domain.vo.WxCarCYFVo; | |||||
| import com.iformall.enums.EnumCarCmd; | |||||
| import com.iformall.enums.EnumCarVendor; | |||||
| import com.iformall.enums.EnumCouponSendSendType; | |||||
| import com.iformall.enums.EnumPayWay; | |||||
| import com.iformall.service.*; | |||||
| import com.iformall.service.park.impl.cyf.CYFUtil; | |||||
| import com.iformall.service.park.impl.tjd.TJDUtil; | |||||
| import lombok.extern.slf4j.Slf4j; | |||||
| import org.apache.commons.collections.CollectionUtils; | |||||
| import org.apache.commons.lang3.StringUtils; | |||||
| import org.springframework.beans.factory.annotation.Autowired; | |||||
| import org.springframework.web.bind.annotation.PostMapping; | |||||
| import org.springframework.web.bind.annotation.RequestBody; | |||||
| import org.springframework.web.bind.annotation.RequestMapping; | |||||
| import org.springframework.web.bind.annotation.RestController; | |||||
| import java.util.Date; | |||||
| import java.util.HashMap; | |||||
| import java.util.List; | |||||
| import java.util.Map; | |||||
| /** | |||||
| * @author: furunxin | |||||
| * @Date: 2020/7/6 15:29 | |||||
| * @Description: 车易付回调接口 | |||||
| */ | |||||
| @Slf4j | |||||
| @RestController | |||||
| @RequestMapping("/keTuoCarCallback") | |||||
| public class WxCarKeTuoCallBackController extends WxParkCallBackBaseController { | |||||
| @Autowired | |||||
| WxParkService wxParkService; | |||||
| @Autowired | |||||
| WxCarCmdLogService wxCarCmdLogService; | |||||
| @Autowired | |||||
| WxCUserService wxCUserService; | |||||
| @Autowired | |||||
| WxCUserCarService wxCUserCarService; | |||||
| @Autowired | |||||
| WxCouponSendService wxCouponSendService; | |||||
| @Autowired | |||||
| WxCarPayRecordService wxCarPayRecordService; | |||||
| @Autowired | |||||
| WxCUserBasicInfoService wxCUserBasicInfoService; | |||||
| /** | |||||
| * @description 科拓 车辆入场通知 | |||||
| * {dataItems=[{"inTime":"2020-12-09 11:21:15","carNumber":"浙-AF81797","inCarPhoto":"p201127315/NISSP_IMG_PARK_IN/20201209/a9f1130651b9493f8eab64f14afbdd47","isReal":0,"itemId":"a9f1130651b9493f8eab64f14afbdd47","inOperator":"超级管理员","equipName":"东门入口","parkName":"杭州东站西子国际","vehicleInfo":"{\"plateNo\":\"浙-AF81797\",\"plateColor\":\"GREEN\",\"plateBackColor\":0,\"plateWordColor\":0,\"vehicleColor\":null,\"vehicleLogo\":null,\"vehicleModel\":null,\"mainModel\":0,\"subModel\":0,\"vehicleModelTrust\":0,\"mainModelTrust\":0,\"subModelTrust\":0,\"plateNoTrust\":1,\"vehicleLogoTrust\":0,\"vehicleColorTrust\":0}","equipCode":"208202496","parkCode":"p201127315"}], pno=dzxzgj, sn=6C958D99D2769AA16F3A3F06E962263F, tn=-2, ts=20201209112118886, ve=1.0} | |||||
| * | |||||
| * | |||||
| * | |||||
| * @Params [paramMap] | |||||
| * @return com.iformall.common.Result | |||||
| * @Author furunxin | |||||
| * @Date 2020/7/8 上午8:27 | |||||
| **/ | |||||
| @PostMapping(value = "/PostCarInInfo") | |||||
| public Result parkInCallback(@RequestBody Map<String, Object> paramMap) { | |||||
| log.info("["+getIpAddr()+"] keTuoCarCallback parkIn: " + paramMap.toString()); | |||||
| //解析车易付参数 | |||||
| return super.parkInCallBack(EnumCarVendor.CAR_KETUO, EnumCarCmd.CAR_KETUO_CALLBACK_PARK_IN, paramMap); | |||||
| } | |||||
| /** | |||||
| * @description 车易付 车辆离场通知 | |||||
| * @Params [param] | |||||
| * @return java.util.Map | |||||
| * @Author furunxin | |||||
| * @Date 2020/7/12 上午9:00 | |||||
| **/ | |||||
| @PostMapping(value = "/PostCarOutInfo") | |||||
| public Result parkOutCallback(@RequestBody Map<String, Object> paramMap) { | |||||
| log.info("["+getIpAddr()+"] keTuoCarCallback parkOut: " + paramMap.toString()); | |||||
| return super.parkOutCallBack(EnumCarVendor.CAR_KETUO, EnumCarCmd.CAR_KETUO_CALLBACK_PARK_OUT, paramMap); | |||||
| } | |||||
| @PostMapping(value = "/unbind") | |||||
| public Result unbindCallback(@RequestBody Map<String, Object> paramMap) { | |||||
| log.info("["+getIpAddr()+"] keTuoCarCallback unbind: " + paramMap.toString()); | |||||
| //解析车易付参数 | |||||
| return super.unbindCarCallBack(EnumCarVendor.CAR_KETUO, EnumCarCmd.CAR_KETUO_CALLBACK_UNBIND, paramMap); | |||||
| } | |||||
| @PostMapping(value = "/PostPayFeeInfo") | |||||
| public Result paidCallback(@RequestBody Map<String, Object> paramMap) { | |||||
| log.info("["+getIpAddr()+"] keTuoCarCallback paid: " + paramMap.toString()); | |||||
| //解析车易付参数 | |||||
| return super.paidCallback(EnumCarVendor.CAR_KETUO, EnumCarCmd.CAR_KETUO_CALLBACK_PAID, paramMap); | |||||
| } | |||||
| } | |||||
| @@ -39,7 +39,12 @@ | |||||
| <groupId>com.alipay.sdk</groupId> | <groupId>com.alipay.sdk</groupId> | ||||
| <artifactId>alipay-easysdk</artifactId> | <artifactId>alipay-easysdk</artifactId> | ||||
| <version>2.2.0</version> | <version>2.2.0</version> | ||||
| </dependency> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>com.iformall</groupId> | |||||
| <artifactId>mallinkAdmin</artifactId> | |||||
| <version>1.0</version> | |||||
| </dependency> | |||||
| </dependencies> | </dependencies> | ||||
| </project> | </project> | ||||
| @@ -44,7 +44,12 @@ public enum EnumCarCmd { | |||||
| CAR_LIFANG_CALLBACK_PARK_IN(636,"立方进场通知"), | CAR_LIFANG_CALLBACK_PARK_IN(636,"立方进场通知"), | ||||
| CAR_LIFANG_CALLBACK_PARK_OUT(637,"立方出场通知"), | CAR_LIFANG_CALLBACK_PARK_OUT(637,"立方出场通知"), | ||||
| CAR_LIFANG_CALLBACK_UNBIND(638,"立方解绑车牌通知"), | CAR_LIFANG_CALLBACK_UNBIND(638,"立方解绑车牌通知"), | ||||
| CAR_LIFANG_CALLBACK_PAID(639,"立方支付通知") | |||||
| CAR_LIFANG_CALLBACK_PAID(639,"立方支付通知"), | |||||
| CAR_KETUO_CALLBACK_PARK_IN(640,"科拓进场通知"), | |||||
| CAR_KETUO_CALLBACK_PARK_OUT(641,"科拓出场通知"), | |||||
| CAR_KETUO_CALLBACK_UNBIND(642,"科拓解绑车牌通知"), | |||||
| CAR_KETUO_CALLBACK_PAID(643,"科拓支付通知") | |||||
| ; | ; | ||||
| public static EnumCarCmd getEnum(Integer code) { | public static EnumCarCmd getEnum(Integer code) { | ||||
| @@ -8,16 +8,17 @@ public enum EnumCarVendor { | |||||
| // 1-ETCP, 2-TJD停简单 3-Dahua大华, 4-ShangAn尚安, 5-BoLink泊链,6-车易付 | // 1-ETCP, 2-TJD停简单 3-Dahua大华, 4-ShangAn尚安, 5-BoLink泊链,6-车易付 | ||||
| CAR_NOTFOUND(0, "无",false), | CAR_NOTFOUND(0, "无",false), | ||||
| CAR_ETCP(1, "ETCP",false), | CAR_ETCP(1, "ETCP",false), | ||||
| CAR_TJD(2, "TJD",false), | |||||
| CAR_DAHUA(3, "DAHUA",false), | |||||
| CAR_SHANGAN(4, "ShangAn",false), | |||||
| CAR_BOLINK(5, "BoLink",false), | |||||
| CAE_CYF(6,"CYF",false), | |||||
| CAR_JIESHUN(7,"JIESHUN",true), | |||||
| CAR_TJD(2, "TJD",false),// 【接口不支持未接通,不能使用】 | |||||
| CAR_DAHUA(3, "DAHUA",false),// 【接口不支持未接通,不能使用】 | |||||
| CAR_SHANGAN(4, "ShangAn",false),// 【接口不支持未接通,不能使用】 | |||||
| CAR_BOLINK(5, "BoLink",false),// 【接口不支持未接通,不能使用】 | |||||
| CAE_CYF(6,"CYF",false),// 【接口不支持未接通,不能使用】 | |||||
| CAR_JIESHUN(7,"JIESHUN",true),//捷顺老版本,西子用 | |||||
| CAR_JIESHUN_V2(10,"JIESHUNV2",true),//捷顺通用版本 | CAR_JIESHUN_V2(10,"JIESHUNV2",true),//捷顺通用版本 | ||||
| CAR_HAIKANGWEISHI(8,"HAIKANGWEISHI",true), | CAR_HAIKANGWEISHI(8,"HAIKANGWEISHI",true), | ||||
| CAR_FUTE(9,"FUTE",false),//深圳市福特智能科技有限公司 | CAR_FUTE(9,"FUTE",false),//深圳市福特智能科技有限公司 | ||||
| CAR_LIFANG(10,"LIFANG",false)//立方停车 | |||||
| CAR_LIFANG(10,"LIFANG",false),//立方停车【接口不支持未接通,不能使用】 | |||||
| CAR_KETUO(11,"KETUO",false)//科拓停车 | |||||
| ; | ; | ||||
| public static EnumCarVendor getEnum(Integer code) { | public static EnumCarVendor getEnum(Integer code) { | ||||
| @@ -21,6 +21,8 @@ import com.iformall.service.park.impl.jieshun.JieShunParkCallbackService; | |||||
| import com.iformall.service.park.impl.jieshun.JieShunParkService; | import com.iformall.service.park.impl.jieshun.JieShunParkService; | ||||
| import com.iformall.service.park.impl.jieshunV2.JieShunV2ParkCallbackService; | import com.iformall.service.park.impl.jieshunV2.JieShunV2ParkCallbackService; | ||||
| import com.iformall.service.park.impl.jieshunV2.JieShunV2ParkService; | import com.iformall.service.park.impl.jieshunV2.JieShunV2ParkService; | ||||
| import com.iformall.service.park.impl.ketuo.KeTuoParkCallbackService; | |||||
| import com.iformall.service.park.impl.ketuo.KeTuoParkService; | |||||
| import com.iformall.service.park.impl.lifang.LiFangParkCallbackService; | import com.iformall.service.park.impl.lifang.LiFangParkCallbackService; | ||||
| import com.iformall.service.park.impl.lifang.LiFangParkService; | import com.iformall.service.park.impl.lifang.LiFangParkService; | ||||
| import com.iformall.service.park.impl.shangan.ShangAnParkService; | import com.iformall.service.park.impl.shangan.ShangAnParkService; | ||||
| @@ -49,6 +51,8 @@ public class ParkFactory { | |||||
| FuteParkService futeParkService; | FuteParkService futeParkService; | ||||
| @Autowired | @Autowired | ||||
| LiFangParkService lifangParkService; | LiFangParkService lifangParkService; | ||||
| @Autowired | |||||
| KeTuoParkService keTuoParkService; | |||||
| @Autowired | @Autowired | ||||
| CYFParkCallbackService cyfCallbackService; | CYFParkCallbackService cyfCallbackService; | ||||
| @@ -62,6 +66,8 @@ public class ParkFactory { | |||||
| FuteParkCallbackService futeParkCallbackService; | FuteParkCallbackService futeParkCallbackService; | ||||
| @Autowired | @Autowired | ||||
| LiFangParkCallbackService lifangParkCallbackService; | LiFangParkCallbackService lifangParkCallbackService; | ||||
| @Autowired | |||||
| KeTuoParkCallbackService keTuoParkCallbackService; | |||||
| private Map<Integer,ParkAdapterService> parkServiceMap ; | private Map<Integer,ParkAdapterService> parkServiceMap ; | ||||
| @@ -85,6 +91,7 @@ public class ParkFactory { | |||||
| parkServiceMap.put(EnumCarVendor.CAR_HAIKANGWEISHI.getCode(), haikangWeishiService); | parkServiceMap.put(EnumCarVendor.CAR_HAIKANGWEISHI.getCode(), haikangWeishiService); | ||||
| parkServiceMap.put(EnumCarVendor.CAR_FUTE.getCode(), futeParkService); | parkServiceMap.put(EnumCarVendor.CAR_FUTE.getCode(), futeParkService); | ||||
| parkServiceMap.put(EnumCarVendor.CAR_LIFANG.getCode(), lifangParkService); | parkServiceMap.put(EnumCarVendor.CAR_LIFANG.getCode(), lifangParkService); | ||||
| parkServiceMap.put(EnumCarVendor.CAR_KETUO.getCode(), keTuoParkService); | |||||
| return parkServiceMap; | return parkServiceMap; | ||||
| } | } | ||||
| @@ -108,6 +115,7 @@ public class ParkFactory { | |||||
| callBackServiceMap.put(EnumCarVendor.CAE_CYF.getCode(), cyfCallbackService); | callBackServiceMap.put(EnumCarVendor.CAE_CYF.getCode(), cyfCallbackService); | ||||
| callBackServiceMap.put(EnumCarVendor.CAR_FUTE.getCode(), futeParkCallbackService); | callBackServiceMap.put(EnumCarVendor.CAR_FUTE.getCode(), futeParkCallbackService); | ||||
| callBackServiceMap.put(EnumCarVendor.CAR_LIFANG.getCode(), lifangParkCallbackService); | callBackServiceMap.put(EnumCarVendor.CAR_LIFANG.getCode(), lifangParkCallbackService); | ||||
| callBackServiceMap.put(EnumCarVendor.CAR_KETUO.getCode(), keTuoParkCallbackService); | |||||
| return callBackServiceMap; | return callBackServiceMap; | ||||
| } | } | ||||
| @@ -6,9 +6,12 @@ import java.util.Map; | |||||
| import org.springframework.beans.factory.annotation.Autowired; | import org.springframework.beans.factory.annotation.Autowired; | ||||
| import org.springframework.stereotype.Service; | import org.springframework.stereotype.Service; | ||||
| import com.iformall.common.ErrorCode; | |||||
| import com.iformall.common.ResultData; | import com.iformall.common.ResultData; | ||||
| import com.iformall.domain.po.WxCUserBasicInfo; | import com.iformall.domain.po.WxCUserBasicInfo; | ||||
| import com.iformall.domain.po.WxCoupon; | |||||
| import com.iformall.domain.po.WxPark; | import com.iformall.domain.po.WxPark; | ||||
| import com.iformall.exception.MallinkException; | |||||
| import com.iformall.service.park.impl.util.ParkHelper; | import com.iformall.service.park.impl.util.ParkHelper; | ||||
| @Service | @Service | ||||
| @@ -17,4 +20,27 @@ public class BaseParkService { | |||||
| @Autowired | @Autowired | ||||
| ParkHelper parkHelper; | ParkHelper parkHelper; | ||||
| protected boolean isReduceMoney(WxCoupon coupon) throws Exception{ | |||||
| //抵扣时长 | |||||
| if (coupon.getUnit().intValue() == 1 ) { | |||||
| return false; | |||||
| }else if (coupon.getUnit().intValue() == 0 ) { | |||||
| return true; | |||||
| }else { | |||||
| throw new MallinkException(ErrorCode.SYS_SERVER_ERROR.getCode(),"coupon has no config [unit]"); | |||||
| } | |||||
| } | |||||
| protected Integer getReduceValue(WxCoupon coupon) throws Exception{ | |||||
| //时长,小时 | |||||
| if (coupon.getUnit().intValue() == 1 ) { | |||||
| return coupon.getPrice()/100; | |||||
| }else if (coupon.getUnit().intValue() == 0 ) { | |||||
| //分 | |||||
| return coupon.getPrice(); | |||||
| }else { | |||||
| throw new MallinkException(ErrorCode.SYS_SERVER_ERROR.getCode(),"coupon has no config [unit]"); | |||||
| } | |||||
| } | |||||
| } | } | ||||
| @@ -0,0 +1,134 @@ | |||||
| package com.iformall.service.park.impl.ketuo; | |||||
| import java.text.ParseException; | |||||
| import java.util.ArrayList; | |||||
| import java.util.Date; | |||||
| import java.util.List; | |||||
| import java.util.Map; | |||||
| import org.apache.commons.lang3.StringUtils; | |||||
| import org.springframework.beans.factory.annotation.Autowired; | |||||
| import org.springframework.stereotype.Service; | |||||
| import com.alibaba.fastjson.JSON; | |||||
| import com.alibaba.fastjson.JSONArray; | |||||
| import com.alibaba.fastjson.JSONObject; | |||||
| import com.iformall.common.ErrorCode; | |||||
| import com.iformall.common.IdWorker; | |||||
| import com.iformall.domain.po.WxCarJSOrder; | |||||
| import com.iformall.domain.po.WxPark; | |||||
| import com.iformall.domain.vo.WxCarCYFVo; | |||||
| import com.iformall.enums.EnumCarVendor; | |||||
| import com.iformall.exception.MallinkException; | |||||
| import com.iformall.mapper.WxCarJSOrderMapper; | |||||
| import com.iformall.service.WxParkService; | |||||
| import com.iformall.service.park.ParkBatchCallBackAdapterService; | |||||
| import com.iformall.service.park.ParkCallBackAdapterService; | |||||
| import com.iformall.service.park.entity.ParkNotifyParam; | |||||
| import com.iformall.service.park.impl.BaseParkService; | |||||
| import lombok.extern.slf4j.Slf4j; | |||||
| @Slf4j | |||||
| @Service | |||||
| public class KeTuoParkCallbackService extends BaseParkService implements ParkCallBackAdapterService { | |||||
| @Autowired | |||||
| KeTuoParkService keTuoParkService; | |||||
| @Autowired | |||||
| WxParkService wxParkService; | |||||
| @Autowired | |||||
| WxCarJSOrderMapper wxCarJSOrderMapper; | |||||
| KeTuoUtil ketuo = new KeTuoUtil(); | |||||
| /** | |||||
| * {dataItems=[{"inTime":"2020-12-09 11:21:15","carNumber":"浙-AF81797","inCarPhoto":"p201127315/NISSP_IMG_PARK_IN/20201209/a9f1130651b9493f8eab64f14afbdd47","isReal":0,"itemId":"a9f1130651b9493f8eab64f14afbdd47","inOperator":"超级管理员","equipName":"东门入口","parkName":"杭州东站西子国际","vehicleInfo":"{\"plateNo\":\"浙-AF81797\",\"plateColor\":\"GREEN\",\"plateBackColor\":0,\"plateWordColor\":0,\"vehicleColor\":null,\"vehicleLogo\":null,\"vehicleModel\":null,\"mainModel\":0,\"subModel\":0,\"vehicleModelTrust\":0,\"mainModelTrust\":0,\"subModelTrust\":0,\"plateNoTrust\":1,\"vehicleLogoTrust\":0,\"vehicleColorTrust\":0}","equipCode":"208202496","parkCode":"p201127315"}], pno=dzxzgj, sn=6C958D99D2769AA16F3A3F06E962263F, tn=-2, ts=20201209112118886, ve=1.0} | |||||
| */ | |||||
| @Override | |||||
| public ParkNotifyParam parseInNoticyParam(Object param) { | |||||
| Map<String,Object> paramm = (Map<String, Object>) param; | |||||
| String carNumber = paramm.get("plateNo").toString(); | |||||
| String parkCode = paramm.get("parkId").toString(); | |||||
| String synId = paramm.get("trafficId").toString(); | |||||
| String entranceTime = paramm.get("entryTime").toString(); | |||||
| ParkNotifyParam p = new ParkNotifyParam(); | |||||
| p.setCarNumber(carNumber); | |||||
| p.setParkId(parkCode); | |||||
| p.setParkName("ketuo"); | |||||
| p.setSynId(synId); | |||||
| try { | |||||
| p.setEntranceTime(ketuo.utcToLocal(entranceTime)); | |||||
| } catch (ParseException e) { | |||||
| log.error("jieshun entranceTime format error",e); | |||||
| return null; | |||||
| } | |||||
| return p; | |||||
| } | |||||
| @Override | |||||
| public ParkNotifyParam parseOutNoticyParam(Object param) { | |||||
| Map<String,Object> paramm = (Map<String, Object>) param; | |||||
| String carNumber = paramm.get("plateNo").toString(); | |||||
| String parkCode = paramm.get("parkId").toString(); | |||||
| String synId = paramm.get("trafficId").toString(); | |||||
| String entranceTime = paramm.get("entryTime").toString(); | |||||
| String outTime = paramm.get("leaveTime").toString(); | |||||
| ParkNotifyParam p = new ParkNotifyParam(); | |||||
| p.setCarNumber(carNumber); | |||||
| p.setParkId(parkCode); | |||||
| p.setParkName("ketuo"); | |||||
| p.setSynId(synId); | |||||
| try { | |||||
| p.setEntranceTime(ketuo.utcToLocal(entranceTime)); | |||||
| } catch (ParseException e) { | |||||
| log.error("cyf entranceTime format error",e); | |||||
| return null; | |||||
| } | |||||
| try { | |||||
| p.setOutTime(ketuo.utcToLocal(outTime)); | |||||
| } catch (ParseException e) { | |||||
| log.error("cyf outTime format error",e); | |||||
| return null; | |||||
| } | |||||
| Object payMentInfo = paramm.get("payMentInfo"); | |||||
| String fee = "0"; | |||||
| if (null != payMentInfo) { | |||||
| JSONObject pay = JSON.parseObject(JSON.toJSONString(payMentInfo)); | |||||
| if (null != pay) { | |||||
| fee = String.valueOf(pay.getInteger("paidMoney")); | |||||
| } | |||||
| } | |||||
| p.setFee(fee); | |||||
| return p; | |||||
| } | |||||
| @Override | |||||
| public ParkNotifyParam parseUnbindNoticyParam(Object param) { | |||||
| return null; | |||||
| } | |||||
| @Override | |||||
| public ParkNotifyParam parsePaidNoticyParam(Object param) { | |||||
| Map<String,Object> paramm = (Map<String, Object>) param; | |||||
| ParkNotifyParam p = new ParkNotifyParam(); | |||||
| p.setCarNumber((String) paramm.get("plateNo")); | |||||
| p.setParkId(String.valueOf(paramm.get("parkId"))); | |||||
| p.setParkOrderId((String) paramm.get("billId")); | |||||
| p.setSynId((String) paramm.get("reqId")); | |||||
| p.setFee(paramm.get("paidMoney").toString()); | |||||
| try { | |||||
| p.setPayTime(ketuo.utcToLocal((String)paramm.get("payTime"))); | |||||
| } catch (ParseException e) { | |||||
| e.printStackTrace(); | |||||
| } | |||||
| return p; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,200 @@ | |||||
| package com.iformall.service.park.impl.ketuo; | |||||
| import java.util.Date; | |||||
| import java.util.HashMap; | |||||
| import java.util.Map; | |||||
| import org.apache.commons.lang3.StringUtils; | |||||
| import org.slf4j.Logger; | |||||
| import org.slf4j.LoggerFactory; | |||||
| import org.springframework.beans.factory.annotation.Autowired; | |||||
| import org.springframework.beans.factory.annotation.Qualifier; | |||||
| import org.springframework.data.redis.core.RedisTemplate; | |||||
| import org.springframework.stereotype.Service; | |||||
| import com.alibaba.fastjson.JSON; | |||||
| import com.alibaba.fastjson.JSONArray; | |||||
| import com.alibaba.fastjson.JSONObject; | |||||
| import com.iformall.common.ErrorCode; | |||||
| import com.iformall.common.IdWorker; | |||||
| import com.iformall.common.ResultData; | |||||
| import com.iformall.domain.po.WxCUserBasicInfo; | |||||
| import com.iformall.domain.po.WxCarJSOrder; | |||||
| import com.iformall.domain.po.WxCoupon; | |||||
| import com.iformall.domain.po.WxPark; | |||||
| import com.iformall.domain.vo.WxCouponOrderCarCVo; | |||||
| import com.iformall.domain.vo.WxParkCouponConfig; | |||||
| import com.iformall.enums.EnumCarVendor; | |||||
| import com.iformall.enums.EnumCouponUnit; | |||||
| import com.iformall.exception.MallinkException; | |||||
| import com.iformall.mapper.WxCarJSOrderMapper; | |||||
| import com.iformall.service.WxParkService; | |||||
| import com.iformall.service.park.ParkAdapterService; | |||||
| import com.iformall.service.park.entity.ParkCreatePayOrder; | |||||
| import com.iformall.service.park.entity.ParkNotifyPaid; | |||||
| import com.iformall.service.park.entity.ParkPaidNotifyResult; | |||||
| import com.iformall.service.park.entity.ParkStopFee; | |||||
| import com.iformall.service.park.impl.BaseParkService; | |||||
| import com.iformall.service.park.impl.util.ParkHelper; | |||||
| import com.iformall.service.park.utils.ParkCacheUtils; | |||||
| import com.iformall.utils.RedisCacheUtils; | |||||
| import com.iformall.utils.RedisLock; | |||||
| /** | |||||
| * | |||||
| */ | |||||
| @Service | |||||
| public class KeTuoParkService extends BaseParkService implements ParkAdapterService { | |||||
| private final Logger logger = LoggerFactory.getLogger(this.getClass()); | |||||
| @Autowired | |||||
| ParkHelper parkHelper; | |||||
| @Autowired | |||||
| @Qualifier("objectCommonRedisTemplate") | |||||
| RedisTemplate<String, Object> redisTemplate; | |||||
| @Autowired | |||||
| RedisLock redisLock; | |||||
| @Autowired | |||||
| WxCarJSOrderMapper wxCarJSOrderMapper; | |||||
| KeTuoUtil ketuo = new KeTuoUtil(); | |||||
| /** | |||||
| * 先去捷顺注册,调用登陆获取令牌,然后根据令牌调用别的接口 | |||||
| * http://ap.jieshun.cn:9088/apply/ 注册信息地址 fumao/iformall2020 | |||||
| * | |||||
| * 捷顺商户平台:http://merchant.jslife.com.cn/merchant/index.html#/auth/login | |||||
| * 捷顺门店平台:http://merchant.jslife.com.cn/store/index.html#/auth/login | |||||
| * 门店商户号 000000008032172000003 密码 ycHqp61Acudxz | |||||
| * | |||||
| * | |||||
| * API对接注意事项: | |||||
| 1、token有效期两个小时,每两小时内登录一次获取token,调业务接口不需要频繁登录用,建议获取一次token存本地缓存,之后从本地获取,两小时内刷新一次, | |||||
| 为避免调业务接口时刚好token被刷新,导致token失效(无效的令牌或令牌已过期),可以做下容错,重新登录获取token或用最新token重新调业务接口。 | |||||
| 2、token是和ip是一对一绑定,新的ip服务器调捷顺的接口会出现ip不合法,需把ip提供捷顺方,添加白名单。多个服务调捷顺接口共用一个ip情况下,只能共用一套登录获取token机制,各自登录会冲突。 | |||||
| 3、所有接口要带车牌的一律带横杠。格式如:粤-B12345。 | |||||
| 4、无效的数据签名是指sn加密方式不对。sn加密方式是对p+signkey进行Md5加密,加密后的字符串大写。 | |||||
| */ | |||||
| @Override | |||||
| public ResultData parkInitConfig(WxPark park) throws Exception { | |||||
| return null; | |||||
| } | |||||
| @Override | |||||
| public String initLogin(Map<String, String> paramMap, WxPark park, WxCUserBasicInfo member) throws Exception{ | |||||
| return null; | |||||
| } | |||||
| @Override | |||||
| public String bindCar(Map<String, String> paramMap, WxPark park, WxCUserBasicInfo member) throws Exception{ | |||||
| return "ketuo"; | |||||
| } | |||||
| @Override | |||||
| public void unbindCar(Map<String, String> paramMap, WxPark park, WxCUserBasicInfo member) throws Exception{ | |||||
| } | |||||
| /** | |||||
| * 每一次查询都会产生订单,然后根据订单号查询 | |||||
| */ | |||||
| @Override | |||||
| public ParkStopFee carStopFee(Map<String, String> paramMap, WxPark park,String carNumber) throws Exception{ | |||||
| return ketuoCarStopFee(paramMap, park,carNumber); | |||||
| } | |||||
| private ParkStopFee ketuoCarStopFee(Map<String, String> paramMap, WxPark park,String carNumber) throws Exception{ | |||||
| String ret = ketuo.getCarStopFee(park, carNumber); | |||||
| if (StringUtils.isBlank(ret)) { | |||||
| throw new MallinkException(ErrorCode.SYS_SERVER_ERROR.getCode(), "ketuo getCarStopFee error. has no result"); | |||||
| } | |||||
| JSONObject retObj = JSON.parseObject(ret); | |||||
| if (retObj.getIntValue("resCode") == 0){ | |||||
| JSONObject retObject = retObj.getJSONObject("data"); | |||||
| if (null == retObject ) { | |||||
| throw new MallinkException(retObj.getInteger("resCode"), "ketuo getCarStopFee error. no order result"+carNumber); | |||||
| } | |||||
| Double totalFee = retObject.getDouble("payable"); | |||||
| String appId = "wx54e676273869baa6"; | |||||
| String parkOrderId = retObject.getString("orderNo"); | |||||
| Integer freeMinute = (Integer)park.getVendorParamsByKey("freeMinute"); | |||||
| String createTime = retObject.getString("entryTime");//计费时间,格式为“yyyy-MM-dd HH:mi:ss” | |||||
| String endTime = retObject.getString("payTime");//离场时间,格式为“yyyy-MM-dd HH:mi:ss” | |||||
| String payPath = "pages/index/index?lotId="+Integer.parseInt(park.getParkingId())+"&lpn="+carNumber; | |||||
| return new ParkStopFee(parkOrderId,ketuo.utcToLocal(createTime),ketuo.utcToLocal(endTime), | |||||
| String.valueOf(totalFee),appId,payPath,null,null,"请支付后"+freeMinute+"分钟内离场"); | |||||
| }else { | |||||
| logger.error("ketuoCarStopFee error. paramMap: {} . ketuoResult: {}",JSON.toJSONString(paramMap),ret); | |||||
| String msg = retObj.getString("resMsg"); | |||||
| throw new MallinkException(ErrorCode.CYF_STOP_FEE_FAIL.getCode(), "停车费获取失败:"+msg); | |||||
| } | |||||
| } | |||||
| /** | |||||
| * 使用3.8打折方案,3.9有限制如果领券了之后未使用,不能再次领券。导致出现异常之后,用户永远无法使用优惠券。 | |||||
| * @return | |||||
| */ | |||||
| @Override | |||||
| public ResultData useCoupon(Map<String, String> paramMap, WxPark park, WxCouponOrderCarCVo userCar,WxCoupon coupon,String carNumber) throws Exception{ | |||||
| if (null == userCar) { | |||||
| return new ResultData(ErrorCode.CYF_STOP_FEE_FAIL.getCode(),"当前用户未查询到优惠券!"); | |||||
| } | |||||
| if (StringUtils.isBlank(userCar.getCUserPhone())) { | |||||
| return new ResultData(ErrorCode.CYF_STOP_FEE_FAIL.getCode(),"当前用户未查询到手机号!"+userCar.getcUserId()); | |||||
| } | |||||
| Map<String,String> valueMap = WxParkCouponConfig.parseValue(userCar.getVendorParams()); | |||||
| if (null == valueMap) { | |||||
| return new ResultData(ErrorCode.COUPON_IS_EMPTY.getCode(),"当前停车券没有关联停车场优惠券配置。"+coupon.getTitle()); | |||||
| } | |||||
| //String orderNo = paramMap.get("parkOrderId"); | |||||
| String planNo = valueMap.get("couponNo"); | |||||
| if (StringUtils.isBlank(planNo)) { | |||||
| return new ResultData(ErrorCode.COUPON_IS_EMPTY.getCode(),"当前停车券没有关联停车场优惠券配置[couponNo]。"+coupon.getTitle()); | |||||
| } | |||||
| //查询停车费 | |||||
| ParkStopFee fee = ketuoCarStopFee(paramMap, park, carNumber); | |||||
| //保存抵扣 | |||||
| boolean isReduceMoney = this.isReduceMoney(coupon); | |||||
| Integer reduceValue = this.getReduceValue(coupon); | |||||
| String executeRet = ketuo.executediscount(park, fee.getOrderId(), planNo, Integer.parseInt(fee.getRemainingFee()),isReduceMoney, reduceValue); | |||||
| logger.info("ketuo executediscount result:"+executeRet); | |||||
| JSONObject executeRetObj = JSON.parseObject(executeRet); | |||||
| if (executeRetObj.getIntValue("resCode") == 0){ | |||||
| return new ResultData(); | |||||
| }else { | |||||
| return new ResultData(executeRetObj.getIntValue("resCode"),"保存科拓支付信息失败。"+executeRetObj.getString("resMsg")); | |||||
| } | |||||
| } | |||||
| @Override | |||||
| public ParkCreatePayOrder createPayOrder(Map<String, String> paramMap, WxPark park, String carNumber) throws Exception { | |||||
| return null; | |||||
| } | |||||
| @Override | |||||
| public ParkPaidNotifyResult notifyPaid(WxPark park,ParkNotifyPaid notifyPaid) throws Exception { | |||||
| return null; | |||||
| } | |||||
| @Override | |||||
| public ResultData getParkStatus(WxPark park) throws Exception{ | |||||
| return new ResultData(); | |||||
| } | |||||
| @Override | |||||
| public boolean ignoreUseCouponCache() { | |||||
| return false; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,212 @@ | |||||
| package com.iformall.service.park.impl.ketuo; | |||||
| import com.alibaba.fastjson.JSON; | |||||
| import com.alibaba.fastjson.JSONObject; | |||||
| import com.google.common.base.Joiner; | |||||
| import com.iformall.domain.po.WxPark; | |||||
| import com.iformall.utils.Utility; | |||||
| import lombok.extern.slf4j.Slf4j; | |||||
| import org.apache.http.HttpEntity; | |||||
| import org.apache.http.HttpResponse; | |||||
| import org.apache.http.client.methods.HttpPost; | |||||
| import org.apache.http.entity.ContentType; | |||||
| import org.apache.http.entity.StringEntity; | |||||
| import org.apache.http.impl.client.CloseableHttpClient; | |||||
| import org.apache.http.impl.client.HttpClients; | |||||
| import org.apache.http.protocol.HTTP; | |||||
| import org.apache.http.util.EntityUtils; | |||||
| import java.io.IOException; | |||||
| import java.nio.charset.StandardCharsets; | |||||
| import java.security.MessageDigest; | |||||
| import java.security.NoSuchAlgorithmException; | |||||
| import java.text.ParseException; | |||||
| import java.text.SimpleDateFormat; | |||||
| import java.util.ArrayList; | |||||
| import java.util.Calendar; | |||||
| import java.util.Date; | |||||
| import java.util.HashMap; | |||||
| import java.util.List; | |||||
| import java.util.Map; | |||||
| import java.util.TreeMap; | |||||
| /** | |||||
| * @author: furunxin | |||||
| * @Date: 2020/7/1 17:16 | |||||
| * @Description: 车易付接口对接参数类 | |||||
| */ | |||||
| @Slf4j | |||||
| public class KeTuoUtil { | |||||
| public static final String QUERY_FEE = "/api/wec/GetParkingPaymentInfo"; | |||||
| public static final String PAY_FEE = "/api/wec/PayParkingFee"; | |||||
| public static Date utcToLocal(String seconds) throws ParseException { | |||||
| String format = "yyyy-MM-dd HH:mm:ss"; | |||||
| SimpleDateFormat sdf = new SimpleDateFormat(format); | |||||
| Date date=sdf.parse(seconds); | |||||
| return date; | |||||
| } | |||||
| /** | |||||
| * 查询停车费 | |||||
| **/ | |||||
| public String getCarStopFee(WxPark wxPark,String carNumber){ | |||||
| String vendroParams = wxPark.getVendorParams(); | |||||
| JSONObject vp = JSON.parseObject(vendroParams); | |||||
| JSONObject param = new JSONObject(); | |||||
| param.put("plateNo", carNumber);//车牌号 | |||||
| log.info("ketuo getCarStopFee :" + JSON.toJSONString(param)); | |||||
| return Proc(QUERY_FEE,"getParkingPaymentInfo", vp.getString("appId"), vp.getString("appSercet"), Integer.parseInt(wxPark.getParkingId()),vp.getString("version"),param); | |||||
| } | |||||
| /** | |||||
| * 抵扣 | |||||
| */ | |||||
| public String executediscount(WxPark wxPark,String orderNo,String couponNo,int fee, boolean isDiscountMoney,int discountValue) { | |||||
| String vendroParams = wxPark.getVendorParams(); | |||||
| JSONObject vp = JSON.parseObject(vendroParams); | |||||
| JSONObject param = new JSONObject(); | |||||
| param.put("orderNo", orderNo);//车场订单号 | |||||
| param.put("payableAmount", fee);//应付金额 | |||||
| param.put("amount", 0);//支付金额(不包含减免部分),单位为分 | |||||
| param.put("payType", 4);//收费终端 | |||||
| param.put("payMethod", 1010);//付款方式 | |||||
| if (isDiscountMoney) { | |||||
| param.put("freeMoney", discountValue);//减免总金额(单位 分) | |||||
| }else { | |||||
| param.put("freeTime", discountValue*3600);//减免总时长(单位 秒) | |||||
| } | |||||
| param.put("isCarLeave", 0);//车辆是否离场 | |||||
| Map freeDeail = new HashMap(); | |||||
| if (isDiscountMoney) { | |||||
| freeDeail.put("money", discountValue); | |||||
| freeDeail.put("time", 0); | |||||
| }else { | |||||
| freeDeail.put("money", 0); | |||||
| freeDeail.put("time", discountValue*3600); | |||||
| } | |||||
| freeDeail.put("code", couponNo);//车场券编码 | |||||
| freeDeail.put("type", 1); | |||||
| freeDeail.put("freeName", "fmMiniApp"); | |||||
| List<Map> freeDetails = new ArrayList<Map>(); | |||||
| freeDetails.add(freeDeail); | |||||
| param.put("freeDetail", JSON.toJSONString(freeDetails));//json数组字符串 | |||||
| log.info("ketuo executediscount :" + JSON.toJSONString(param)); | |||||
| return Proc(PAY_FEE,"payParkingFee", vp.getString("appId"), vp.getString("appSercet"), Integer.parseInt(wxPark.getParkingId()),vp.getString("version"),param); | |||||
| } | |||||
| private static String Proc(String url, String serviceCode,String appId,String appSercet,Integer parkId,String version, JSONObject params) { | |||||
| CloseableHttpClient httpClient = HttpClients.createDefault(); | |||||
| HttpPost httpPost = new HttpPost(url); | |||||
| httpPost.addHeader(HTTP.CONTENT_TYPE,"application/json"); | |||||
| httpPost.addHeader("Accept", "application/json"); | |||||
| httpPost.addHeader("Accept-Encoding", "UTF-8"); | |||||
| httpPost.addHeader("version",version);//1.0.0 | |||||
| if (null != params) { | |||||
| try { | |||||
| params.put("appId", appId); | |||||
| params.put("parkId", parkId); | |||||
| params.put("serviceCode", serviceCode); | |||||
| params.put("ts", String.valueOf(Calendar.getInstance().getTimeInMillis())); | |||||
| params.put("key", paramsSign(params, appSercet)); | |||||
| params.put("reqId", Utility.generate32UUID()+String.valueOf(Calendar.getInstance().getTimeInMillis())); | |||||
| httpPost.setEntity(new StringEntity(JSONObject.toJSONString(params),ContentType.create("application/json","utf-8"))); | |||||
| } catch (Exception e) { | |||||
| log.error(e.getLocalizedMessage(),e); | |||||
| return null; | |||||
| } | |||||
| } | |||||
| if (null != params) { | |||||
| log.info("ketuo httpRequest:[url]"+url+"[params]"+JSON.toJSONString(params)); | |||||
| }else { | |||||
| log.info("ketuo httpRequest:[url]"+url); | |||||
| } | |||||
| HttpResponse response = null; | |||||
| try { | |||||
| response = httpClient.execute(httpPost); | |||||
| } catch (Exception e) { | |||||
| log.error(e.getLocalizedMessage(),e); | |||||
| } | |||||
| String result = null; | |||||
| //打印StatusLine | |||||
| log.debug("StatusLine: " + response.getStatusLine()); | |||||
| try{ | |||||
| //获取实体 | |||||
| HttpEntity httpEntity= response.getEntity(); | |||||
| result = EntityUtils.toString(httpEntity, "UTF-8"); | |||||
| log.debug(result); | |||||
| } catch (Exception e) { | |||||
| log.error(e.getLocalizedMessage(),e); | |||||
| } | |||||
| try { //关闭流并释放资源 | |||||
| httpClient.close(); | |||||
| } catch (IOException e) { | |||||
| log.error(e.getLocalizedMessage()); | |||||
| } | |||||
| return result; | |||||
| } | |||||
| private static String paramsSign(JSONObject requestBody, String appSecret) { | |||||
| TreeMap<String, String> params = new TreeMap<>(); | |||||
| //过滤掉key,appId字段,空属性及Map或List等复杂对象 | |||||
| requestBody.entrySet().stream().filter( | |||||
| p -> !"key".equals(p.getKey()) | |||||
| && !"appId".equals(p.getKey()) | |||||
| && p.getValue() != null | |||||
| && !(p.getValue() instanceof Map) | |||||
| && !(p.getValue() instanceof Iterable)) | |||||
| .forEach(p -> { | |||||
| if (!p.getValue().equals("")) { | |||||
| params.put(p.getKey(), p.getValue().toString()); | |||||
| } | |||||
| }); | |||||
| //拼接appSecret | |||||
| String temp = Joiner.on("&").withKeyValueSeparator("=").join(params).concat("&").concat(appSecret); | |||||
| return md5(temp).toUpperCase(); | |||||
| } | |||||
| /** | |||||
| * 对文本执行 md5 摘要加密, 此算法与 mysql,JavaScript生成的md5摘要进行过一致性对比. | |||||
| * | |||||
| * @param plainText | |||||
| * @return 返回值中的字母为小写 | |||||
| */ | |||||
| private static String md5(String plainText) { | |||||
| if (null == plainText) { | |||||
| plainText = ""; | |||||
| } | |||||
| String mD5Str = null; | |||||
| try { | |||||
| // JDK 支持以下6种消息摘要算法,不区分大小写 | |||||
| // md5,sha(sha-1),md2,sha-256,sha-384,sha-512 | |||||
| MessageDigest md = MessageDigest.getInstance("MD5"); | |||||
| md.update(plainText.getBytes(StandardCharsets.UTF_8)); | |||||
| byte[] b = md.digest(); | |||||
| int i; | |||||
| StringBuilder builder = new StringBuilder(32); | |||||
| for (byte value : b) { | |||||
| i = value; | |||||
| if (i < 0) { | |||||
| i += 256; | |||||
| } | |||||
| if (i < 16) { | |||||
| builder.append("0"); | |||||
| } | |||||
| builder.append(Integer.toHexString(i)); | |||||
| } | |||||
| mD5Str = builder.toString(); | |||||
| } catch (NoSuchAlgorithmException e) { | |||||
| e.printStackTrace(); | |||||
| } | |||||
| return mD5Str; | |||||
| } | |||||
| } | |||||