| | |
| | | @TableField(exist = false) |
| | | private String tagName; |
| | | |
| | | @ApiModelProperty(value = "是否白名单:1=是,2=否") |
| | | @TableField(exist = false) |
| | | private Integer whitelistFlag; |
| | | |
| | | @ApiModelProperty(value = "单位名称") |
| | | @TableField(exist = false) |
| | | private String companyName; |
| | |
| | | return R.ok(page); |
| | | } |
| | | List<Long> pageUserIds = page.getRecords().stream().map(TAppUser::getId).collect(Collectors.toList()); |
| | | List<String> pageUserPhones = page.getRecords().stream() |
| | | .map(TAppUser::getPhone) |
| | | .filter(StringUtils::isNotBlank) |
| | | .map(String::trim) |
| | | .distinct() |
| | | .collect(Collectors.toList()); |
| | | LambdaQueryChainWrapper<TAppUserWhitelist> pageWhitelistQuery = appUserWhitelistService.lambdaQuery() |
| | | .eq(TAppUserWhitelist::getStatus, 1) |
| | | .eq(TAppUserWhitelist::getDelFlag, false) |
| | | .and(wrapper -> { |
| | | wrapper.in(TAppUserWhitelist::getAppUserId, pageUserIds); |
| | | if (!pageUserPhones.isEmpty()) { |
| | | wrapper.or().in(TAppUserWhitelist::getPhone, pageUserPhones); |
| | | } |
| | | }); |
| | | List<TAppUserWhitelist> pageWhitelistList = pageWhitelistQuery.list(); |
| | | Set<Long> activeWhitelistUserIds = pageWhitelistList.stream() |
| | | .map(TAppUserWhitelist::getAppUserId) |
| | | .filter(Objects::nonNull) |
| | | .collect(Collectors.toSet()); |
| | | Set<String> activeWhitelistPhones = pageWhitelistList.stream() |
| | | .map(TAppUserWhitelist::getPhone) |
| | | .filter(StringUtils::isNotBlank) |
| | | .map(String::trim) |
| | | .collect(Collectors.toSet()); |
| | | Map<Long, String> userTagNameMap = getUserTagNameMap(pageUserIds); |
| | | Map<Long, BigDecimal> exchangeCodeBalanceMap = otherClient.getExchangeCodeBalanceMap(pageUserIds).getData(); |
| | | if (exchangeCodeBalanceMap == null) { |
| | |
| | | for (TAppUser appUser : page.getRecords()) { |
| | | appUser.setExchangeCodeBalance(exchangeCodeBalanceMap.getOrDefault(appUser.getId(), BigDecimal.ZERO)); |
| | | appUser.setTagName(userTagNameMap.get(appUser.getId())); |
| | | boolean activeWhitelist = activeWhitelistUserIds.contains(appUser.getId()) |
| | | || (StringUtils.isNotBlank(appUser.getPhone()) && activeWhitelistPhones.contains(appUser.getPhone().trim())); |
| | | appUser.setWhitelistFlag(activeWhitelist ? 1 : 2); |
| | | if (appUser.getVipEndTime()!=null&&appUser.getVipEndTime().isAfter(LocalDateTime.now())) { |
| | | //匹配vipMap的值 |
| | | appUser.setVipName(vipMap.getData().get(appUser.getVipId())); |
| | |
| | | List<TAppUserCar> carList = appUserCarService.lambdaQuery().eq(TAppUserCar::getAppUserId, id).orderByDesc(BasePojo::getCreateTime).list(); |
| | | userDetailDto.setCarList(carList); |
| | | |
| | | List<Integer> tagIds = appUserTagService.lambdaQuery().eq(TAppUserTag::getAppUserId, id).orderByDesc(TAppUserTag::getCreateTime).list().stream().map(TAppUserTag::getUserTagId).collect(Collectors.toList()); |
| | | R<Map<Integer, String>> tagMap = otherClient.getTagMap(tagIds); |
| | | userDetailDto.setTagName(tagMap.getData().values() |
| | | .stream() |
| | | .collect(Collectors.joining(","))); |
| | | userDetailDto.setTagName(getUserTagNameMap(Collections.singletonList(id)).get(id)); |
| | | |
| | | |
| | | |
| | |
| | | |
| | | <!-- name的值是变量的名称,value的值时变量定义的值。通过定义的值会被插入到logger上下文中。定义后,可以使“${}”来使用变量。 --> |
| | | <!-- <property name="log.path" value="/data/log/app/ruoyi-chargingPile"/>--> |
| | | <property name="log.path" value="/Users/mitao/Documents/log/mx_charging_pile/log/app/ruoyi-chargingPile"/> |
| | | <property name="log.path" value="/Users/PC/Documents/log/mx_charging_pile/log/app/ruoyi-chargingPile"/> |
| | | |
| | | <!--0. 日志格式和颜色渲染 --> |
| | | <!-- 彩色日志依赖的渲染类 --> |
| | |
| | | |
| | | @ResponseBody |
| | | @PostMapping(value = "/pay/order/special/refund/query") |
| | | @ApiOperation(value = "特殊退款查询", tags = {"管理后台-支付订单-订单信息"}) |
| | | @RequiresPermissions(value = {"/paymentOrder/specialRefund", "/paymentOrder/specialRefund/refund"}, logical = Logical.OR) |
| | | @ApiOperation(value = "特殊退款查询", tags = {"管理后台-支付订单-特殊退款处理"}) |
| | | public R<PayOrderInfoDto> specialRefundQuery(@RequestBody PayOrderRefundDto payOrderQueryDto) { |
| | | return chargingOrderService.getSpecialRefundOrder(payOrderQueryDto); |
| | | } |
| | | |
| | | @ResponseBody |
| | | @PostMapping(value = "/pay/order/special/refund") |
| | | @ApiOperation(value = "特殊退款", tags = {"管理后台-支付订单-订单信息"}) |
| | | @RequiresPermissions(value = {"/paymentOrder/specialRefund/refund"}, logical = Logical.OR) |
| | | @ApiOperation(value = "特殊退款", tags = {"管理后台-支付订单-特殊退款处理"}) |
| | | public R specialRefund(@RequestBody PayOrderRefundDto payOrderQueryDto) { |
| | | return chargingOrderService.specialRefund(payOrderQueryDto); |
| | | } |
| | |
| | | |
| | | |
| | | |
| | | @RequiresPermissions(value = {"/paymentOrder/refund", "/paymentOrder/order"}, logical = Logical.OR) |
| | | @RequiresPermissions(value = {"/paymentOrder/refund", "/paymentOrder/order", "/paymentOrder/specialRefund"}, logical = Logical.OR) |
| | | @ResponseBody |
| | | @PostMapping(value = "/pay/order/refund/list") |
| | | @ApiOperation(value = "列表", tags = {"管理后台-支付订单-退款订单"}) |
| | |
| | | private String code; |
| | | @ApiModelProperty("支付流水号") |
| | | private String serialNumber; |
| | | @ApiModelProperty("支付宝特殊退款方式:ALI=支付宝退款,ICBC=工商退款") |
| | | @ApiModelProperty("退款通道(兼容旧参数,特殊退款按原支付路径退款)") |
| | | private String refundChannel; |
| | | private BigDecimal refundAmount; |
| | | private String reason; |
| | |
| | | import java.math.RoundingMode; |
| | | import java.time.LocalDate; |
| | | import java.time.LocalDateTime; |
| | | import java.time.format.DateTimeFormatter; |
| | | import java.time.temporal.ChronoUnit; |
| | | import java.time.temporal.TemporalAdjusters; |
| | | import java.util.ArrayList; |
| | |
| | | import java.util.HashMap; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.Objects; |
| | | import java.util.Set; |
| | | import java.util.stream.Collectors; |
| | | |
| | | /** |
| | |
| | | List<Map<String, Object>> list = chargingSettlementBillService.queryList(pageInfo, dto.getType(), dto.getState(), dto.getDateRange(), statisticalDateType, dto.getSiteIds()); |
| | | ChargingBillWapper chargingBillVO = new ChargingBillWapper(); |
| | | List<Map<String, Object>> pageList = new ArrayList<>(); |
| | | List<TChargingOrder> total = chargingOrderList.selectList(new LambdaQueryWrapper<>(TChargingOrder.class) |
| | | .isNotNull(TChargingOrder::getPayTime)); |
| | | List<TChargingOrderAccountingStrategy> strategyList = chargingOrderAccountingStrategyService.list(); |
| | | Map<Integer, BigDecimal> siteServiceShareRatioMap = loadSiteServiceShareRatioMap(); |
| | | PageInfo<Map<String, Object>> sumPageInfo = new PageInfo<>(1, 100000); |
| | | List<Map<String, Object>> sumList = chargingSettlementBillService.queryList(sumPageInfo, dto.getType(), dto.getState(), dto.getDateRange(), statisticalDateType, dto.getSiteIds()); |
| | | Map<String, Map<String, BigDecimal>> incomeIndex = buildChargingBillIncomeIndex(sumList, siteServiceShareRatioMap); |
| | | if(null != list){ |
| | | for (Map<String, Object> map : list) { |
| | | Integer site_id = Integer.valueOf(map.get("site_id").toString()); |
| | |
| | | String chargingCapacity = map.get("total_electricity").toString(); |
| | | String electrovalence = map.get("total_electricity_charge").toString(); |
| | | String serviceCharge = map.get("total_service_fee").toString(); |
| | | Map<String, BigDecimal> incomeMap = calculateChargingBillIncome(dto.getType(), site_id, billingCycle, total, strategyList, siteServiceShareRatioMap); |
| | | Map<String, BigDecimal> incomeMap = getChargingBillIncome(incomeIndex, site_id, billingCycle); |
| | | Map<String, Object> data1 = new HashMap<>(); |
| | | data1.put("code", code); |
| | | data1.put("orderState", 2); |
| | |
| | | BigDecimal discount = (null == map || null == map.get("couponDeduction")) ? BigDecimal.ZERO : new BigDecimal(map.get("couponDeduction").toString()); |
| | | BigDecimal platformIncome = BigDecimal.ZERO; |
| | | BigDecimal partnerIncome = BigDecimal.ZERO; |
| | | PageInfo<Map<String, Object>> sumPageInfo = new PageInfo<>(1, 100000); |
| | | List<Map<String, Object>> sumList = chargingSettlementBillService.queryList(sumPageInfo, dto.getType(), dto.getState(), dto.getDateRange(), statisticalDateType, dto.getSiteIds()); |
| | | if (sumList != null) { |
| | | for (Map<String, Object> row : sumList) { |
| | | Integer siteId = Integer.valueOf(row.get("site_id").toString()); |
| | | String billingCycle = row.get("billingCycle").toString(); |
| | | Map<String, BigDecimal> incomeMap = calculateChargingBillIncome(dto.getType(), siteId, billingCycle, total, strategyList, siteServiceShareRatioMap); |
| | | Map<String, BigDecimal> incomeMap = getChargingBillIncome(incomeIndex, siteId, billingCycle); |
| | | platformIncome = platformIncome.add(incomeMap.get("platformIncome")); |
| | | partnerIncome = partnerIncome.add(incomeMap.get("partnerIncome")); |
| | | } |
| | |
| | | return chargingBillVO; |
| | | } |
| | | |
| | | private Map<String, Map<String, BigDecimal>> buildChargingBillIncomeIndex(List<Map<String, Object>> billRows, |
| | | Map<Integer, BigDecimal> siteServiceShareRatioMap) { |
| | | Map<String, Map<String, BigDecimal>> incomeIndex = new HashMap<>(); |
| | | if (billRows == null || billRows.isEmpty()) { |
| | | return incomeIndex; |
| | | } |
| | | Set<String> billingCycles = billRows.stream() |
| | | .map(row -> row.get("billingCycle")) |
| | | .filter(Objects::nonNull) |
| | | .map(Object::toString) |
| | | .collect(Collectors.toSet()); |
| | | if (billingCycles.isEmpty()) { |
| | | return incomeIndex; |
| | | } |
| | | LocalDate firstMonth = billingCycles.stream() |
| | | .map(cycle -> LocalDate.parse(cycle + "-01")) |
| | | .min(LocalDate::compareTo) |
| | | .orElse(null); |
| | | LocalDate lastMonth = billingCycles.stream() |
| | | .map(cycle -> LocalDate.parse(cycle + "-01")) |
| | | .max(LocalDate::compareTo) |
| | | .orElse(null); |
| | | if (firstMonth == null || lastMonth == null) { |
| | | return incomeIndex; |
| | | } |
| | | |
| | | List<TChargingOrder> orders = chargingOrderList.selectList(new LambdaQueryWrapper<>(TChargingOrder.class) |
| | | .select(TChargingOrder::getId, TChargingOrder::getSiteId, TChargingOrder::getPayTime, TChargingOrder::getOrderSource) |
| | | .ge(TChargingOrder::getPayTime, firstMonth.atStartOfDay()) |
| | | .lt(TChargingOrder::getPayTime, lastMonth.plusMonths(1).atStartOfDay())); |
| | | if (orders == null || orders.isEmpty()) { |
| | | return incomeIndex; |
| | | } |
| | | |
| | | Map<Long, BigDecimal[]> strategyAmountMap = new HashMap<>(); |
| | | List<Long> orderIds = orders.stream().map(TChargingOrder::getId).filter(Objects::nonNull).collect(Collectors.toList()); |
| | | final int batchSize = 1000; |
| | | for (int from = 0; from < orderIds.size(); from += batchSize) { |
| | | List<Long> batchIds = orderIds.subList(from, Math.min(from + batchSize, orderIds.size())); |
| | | List<TChargingOrderAccountingStrategy> strategies = chargingOrderAccountingStrategyService.list( |
| | | new LambdaQueryWrapper<TChargingOrderAccountingStrategy>() |
| | | .select(TChargingOrderAccountingStrategy::getChargingOrderId, |
| | | TChargingOrderAccountingStrategy::getPeriodElectricPrice, |
| | | TChargingOrderAccountingStrategy::getPeriodServicePrice) |
| | | .in(TChargingOrderAccountingStrategy::getChargingOrderId, batchIds)); |
| | | for (TChargingOrderAccountingStrategy strategy : strategies) { |
| | | BigDecimal[] amounts = strategyAmountMap.computeIfAbsent(strategy.getChargingOrderId(), key -> new BigDecimal[]{BigDecimal.ZERO, BigDecimal.ZERO}); |
| | | amounts[0] = amounts[0].add(strategy.getPeriodElectricPrice() == null ? BigDecimal.ZERO : strategy.getPeriodElectricPrice()); |
| | | amounts[1] = amounts[1].add(strategy.getPeriodServicePrice() == null ? BigDecimal.ZERO : strategy.getPeriodServicePrice()); |
| | | } |
| | | } |
| | | |
| | | for (TChargingOrder order : orders) { |
| | | if (order.getPayTime() == null || order.getSiteId() == null) { |
| | | continue; |
| | | } |
| | | String billingCycle = order.getPayTime().format(DateTimeFormatter.ofPattern("yyyy-MM")); |
| | | if (!billingCycles.contains(billingCycle)) { |
| | | continue; |
| | | } |
| | | BigDecimal[] amounts = strategyAmountMap.getOrDefault(order.getId(), new BigDecimal[]{BigDecimal.ZERO, BigDecimal.ZERO}); |
| | | BigDecimal paymentAmount; |
| | | BigDecimal partnerIncome = BigDecimal.ZERO; |
| | | if (Integer.valueOf(2).equals(order.getOrderSource())) { |
| | | paymentAmount = amounts[0].add(amounts[1].multiply(new BigDecimal("0.8")).setScale(2, RoundingMode.DOWN)); |
| | | } else { |
| | | paymentAmount = amounts[0].add(amounts[1]); |
| | | if (Integer.valueOf(1).equals(order.getOrderSource())) { |
| | | BigDecimal serviceShareRatio = siteServiceShareRatioMap.get(order.getSiteId()); |
| | | if (serviceShareRatio != null && serviceShareRatio.compareTo(BigDecimal.ZERO) > 0) { |
| | | partnerIncome = amounts[1].multiply(serviceShareRatio).divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP); |
| | | } |
| | | } |
| | | } |
| | | addChargingBillIncome(incomeIndex, chargingBillIncomeKey(0, billingCycle), paymentAmount, partnerIncome); |
| | | addChargingBillIncome(incomeIndex, chargingBillIncomeKey(order.getSiteId(), billingCycle), paymentAmount, partnerIncome); |
| | | } |
| | | return incomeIndex; |
| | | } |
| | | |
| | | private void addChargingBillIncome(Map<String, Map<String, BigDecimal>> incomeIndex, String key, |
| | | BigDecimal paymentAmount, BigDecimal partnerIncome) { |
| | | Map<String, BigDecimal> income = incomeIndex.computeIfAbsent(key, ignored -> { |
| | | Map<String, BigDecimal> value = new HashMap<>(); |
| | | value.put("platformIncome", BigDecimal.ZERO); |
| | | value.put("partnerIncome", BigDecimal.ZERO); |
| | | return value; |
| | | }); |
| | | income.put("platformIncome", income.get("platformIncome").add(paymentAmount).subtract(partnerIncome)); |
| | | income.put("partnerIncome", income.get("partnerIncome").add(partnerIncome)); |
| | | } |
| | | |
| | | private Map<String, BigDecimal> getChargingBillIncome(Map<String, Map<String, BigDecimal>> incomeIndex, |
| | | Integer siteId, String billingCycle) { |
| | | Map<String, BigDecimal> income = incomeIndex.get(chargingBillIncomeKey(siteId, billingCycle)); |
| | | Map<String, BigDecimal> result = new HashMap<>(); |
| | | result.put("platformIncome", income == null ? BigDecimal.ZERO : income.get("platformIncome").max(BigDecimal.ZERO).setScale(2, RoundingMode.HALF_UP)); |
| | | result.put("partnerIncome", income == null ? BigDecimal.ZERO : income.get("partnerIncome").setScale(2, RoundingMode.HALF_UP)); |
| | | return result; |
| | | } |
| | | |
| | | private String chargingBillIncomeKey(Integer siteId, String billingCycle) { |
| | | return (siteId == null ? 0 : siteId) + "|" + billingCycle; |
| | | } |
| | | |
| | | @Override |
| | | public ChargingBillVO chargingBillListExport(ChargingListQuery dto) { |
| | | if (dto.getState()!=null){ |
| | |
| | | req.setReturnUrl(addChargingOrder.getReturnUrl()); |
| | | req.setOpenId(appUser.getWxOpenid()); |
| | | req.setProfitSharing(buildIcbcProfitSharing(site)); |
| | | IcbcPaymentResp data = icbcPaymentClient.payment(req).getData(); |
| | | R<IcbcPaymentResp> result = icbcPaymentClient.payment(req); |
| | | if (result == null || R.isError(result)) { |
| | | String reason = result == null ? "工行支付服务无响应" : firstNotBlank(result.getMsg(), "工行支付服务调用失败"); |
| | | log.error("调起工行支付失败,订单号:{},原因:{}", chargingOrder.getCode(), reason); |
| | | throw new RuntimeException("调起工行支付失败:" + reason); |
| | | } |
| | | IcbcPaymentResp data = result.getData(); |
| | | if (isIcbcPaymentCreated(data)) { |
| | | if (StringUtils.hasLength(data.getTradeNo())) { |
| | | chargingOrder.setRechargeSerialNumber(data.getTradeNo()); |
| | |
| | | } |
| | | return AjaxResult.success(data); |
| | | } |
| | | throw new RuntimeException("调起工行支付失败"); |
| | | String reason = buildIcbcPaymentFailReason(data); |
| | | log.error("调起工行支付失败,订单号:{},工行返回:{}", chargingOrder.getCode(), reason); |
| | | throw new RuntimeException("调起工行支付失败:" + reason); |
| | | } |
| | | if (Integer.valueOf(2).equals(addChargingOrder.getPaymentType())) { |
| | | AliPaymentReq req = new AliPaymentReq(); |
| | |
| | | * @param chargingOrder |
| | | */ |
| | | public void editUserTag(TChargingOrder chargingOrder) { |
| | | if (chargingOrder == null || chargingOrder.getAppUserId() == null) { |
| | | return; |
| | | } |
| | | //处理用户标签数据 |
| | | List<TUserTag> data = userTagClient.getAllUserTag().getData(); |
| | | R<List<TUserTag>> tagResult = userTagClient.getAllUserTag(); |
| | | List<TUserTag> data = tagResult == null ? null : tagResult.getData(); |
| | | if (data == null || data.isEmpty()) { |
| | | return; |
| | | } |
| | | //累计充电次数 |
| | | List<TUserTag> userTagList1 = data.stream().filter(s -> s.getStandardCondition() == 1).collect(Collectors.toList()); |
| | | List<TUserTag> userTagList1 = data.stream().filter(s -> Integer.valueOf(1).equals(s.getStandardCondition())).collect(Collectors.toList()); |
| | | for (TUserTag tUserTag : userTagList1) { |
| | | Integer times = JSON.parseObject(tUserTag.getConditions()).getInteger("times"); |
| | | JSONObject conditions = JSON.parseObject(tUserTag.getConditions()); |
| | | Integer times = conditions == null ? null : conditions.getInteger("times"); |
| | | if (times == null || times <= 0 || tUserTag.getCreateTime() == null) { |
| | | continue; |
| | | } |
| | | TAppUserTag appUserTag = new TAppUserTag(); |
| | | appUserTag.setAppUserId(chargingOrder.getAppUserId()); |
| | | appUserTag.setUserTagId(tUserTag.getId()); |
| | |
| | | } |
| | | } |
| | | //充电频率 |
| | | List<TUserTag> userTagList2 = data.stream().filter(s -> s.getStandardCondition() == 2).collect(Collectors.toList()); |
| | | List<TUserTag> userTagList2 = data.stream().filter(s -> Integer.valueOf(2).equals(s.getStandardCondition())).collect(Collectors.toList()); |
| | | for (TUserTag tUserTag : userTagList2) { |
| | | TAppUserTag appUserTag = new TAppUserTag(); |
| | | appUserTag.setAppUserId(chargingOrder.getAppUserId()); |
| | | appUserTag.setUserTagId(tUserTag.getId()); |
| | | TAppUserTag data1 = appUserTagClient.getUserTag(appUserTag).getData(); |
| | | |
| | | Integer day = JSON.parseObject(tUserTag.getConditions()).getInteger("day"); |
| | | Integer times = JSON.parseObject(tUserTag.getConditions()).getInteger("times"); |
| | | JSONObject conditions = JSON.parseObject(tUserTag.getConditions()); |
| | | Integer day = conditions == null ? null : conditions.getInteger("day"); |
| | | Integer times = conditions == null ? null : conditions.getInteger("times"); |
| | | if (day == null || day <= 0 || times == null || times <= 0) { |
| | | continue; |
| | | } |
| | | LocalDateTime endTime = LocalDateTime.now(); |
| | | endTime.plusDays(day); |
| | | String start = tUserTag.getCreateTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); |
| | | String start = endTime.minusDays(day).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); |
| | | String end = endTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); |
| | | long count1 = this.count(new LambdaQueryWrapper<TChargingOrder>().eq(TChargingOrder::getAppUserId, chargingOrder.getAppUserId()) |
| | | .eq(TChargingOrder::getRechargePaymentStatus, 2).between(TChargingOrder::getStartTime, start, end).eq(TChargingOrder::getDelFlag, 0)); |
| | |
| | | |
| | | @Override |
| | | public R<PageInfo<PayOrderDto>> payOrderQuery(PayOrderQueryDto payOrderQueryDto) { |
| | | if (payOrderQueryDto == null) { |
| | | payOrderQueryDto = new PayOrderQueryDto(); |
| | | } |
| | | PageInfo<PayOrderDto> pageInfo = new PageInfo<>(payOrderQueryDto.getPageCurr(), payOrderQueryDto.getPageSize()); |
| | | List<PayOrderDto> list = this.baseMapper.payOrderQuery(pageInfo, payOrderQueryDto); |
| | | if (list == null || list.isEmpty()) { |
| | | pageInfo.setRecords(Collections.emptyList()); |
| | | return R.ok(pageInfo); |
| | | } |
| | | for (PayOrderDto payOrderDto : list) { |
| | | if (payOrderDto.getType() == 2 || payOrderDto.getType() == 3) { |
| | | if (payOrderDto == null) { |
| | | continue; |
| | | } |
| | | if (Integer.valueOf(2).equals(payOrderDto.getType()) || Integer.valueOf(3).equals(payOrderDto.getType())) { |
| | | payOrderDto.setFinalAmount(payOrderDto.getOrderAmount()); |
| | | } |
| | | if (payOrderDto.getType() == 1 && payOrderDto.getStatus() == 3) { |
| | | if (Integer.valueOf(1).equals(payOrderDto.getType()) && Integer.valueOf(3).equals(payOrderDto.getStatus())) { |
| | | payOrderDto.setFinalAmount(payOrderDto.getOrderAmount()); |
| | | payOrderDto.setOrderAmount(payOrderDto.getOrderAmount()); |
| | | payOrderDto.setPaymentAmount(payOrderDto.getOrderAmount()); |
| | |
| | | if (order == null) { |
| | | return R.fail("未查询到支付成功的充电订单"); |
| | | } |
| | | RLock lock = redissonClient.getLock("SPECIAL_REFUND_ORDER_LOCK:" + order.getId()); |
| | | if (!lock.tryLock()) { |
| | | return R.fail("该订单正在退款处理中,请勿重复提交"); |
| | | } |
| | | try { |
| | | order = this.getById(order.getId()); |
| | | if (order == null) { |
| | | return R.fail("充电订单不存在"); |
| | | } |
| | | BigDecimal refundAmount = money(payOrderQueryDto.getRefundAmount()); |
| | | BigDecimal refundableAmount = money(order.getPaymentAmount()).subtract(money(order.getRefundAmount())).setScale(2, RoundingMode.HALF_UP); |
| | | if (refundAmount.compareTo(refundableAmount) > 0) { |
| | |
| | | } |
| | | Integer payType = order.getRechargePaymentType(); |
| | | boolean success = false; |
| | | if (Integer.valueOf(1).equals(payType) || "ICBC".equalsIgnoreCase(payOrderQueryDto.getRefundChannel())) { |
| | | if (Integer.valueOf(1).equals(payType)) { |
| | | R shareRefund = refundIcbcShareBeforeMainRefund(order, refund, thirdRefundAmount); |
| | | if (shareRefund.getCode() != 200) { |
| | | return shareRefund; |
| | | } |
| | | success = refundByIcbcAndFinish(order, refund, thirdRefundAmount, SPECIAL_REFUND_TITLE, true, refundAmount, balanceRefundAmount); |
| | | } else if (Integer.valueOf(2).equals(payType)) { |
| | | RefundReq dto = new RefundReq(); |
| | |
| | | } |
| | | } |
| | | return success ? R.ok() : R.fail("退款失败,请检查支付渠道或稍后重试"); |
| | | } finally { |
| | | if (lock.isHeldByCurrentThread()) { |
| | | lock.unlock(); |
| | | } |
| | | } |
| | | } |
| | | |
| | | private R refundIcbcShareBeforeMainRefund(TChargingOrder order, TChargingOrderRefund refund, BigDecimal thirdRefundAmount) { |
| | | if (order == null || refund == null || thirdRefundAmount == null || thirdRefundAmount.compareTo(BigDecimal.ZERO) <= 0) { |
| | | return R.ok(); |
| | | } |
| | | if (!"ICBC".equals(order.getSourceName())) { |
| | | return R.ok(); |
| | | } |
| | | List<TChargingOrderShareRecord> shareRecords = chargingOrderShareRecordMapper.selectList( |
| | | new LambdaQueryWrapper<TChargingOrderShareRecord>() |
| | | .eq(TChargingOrderShareRecord::getChargingOrderId, order.getId()) |
| | | .eq(TChargingOrderShareRecord::getPayChannel, "ICBC") |
| | | .eq(TChargingOrderShareRecord::getNeedOnlineShare, true) |
| | | .eq(TChargingOrderShareRecord::getOnlineShareStatus, "SUCCESS") |
| | | .eq(TChargingOrderShareRecord::getDelFlag, false) |
| | | .orderByAsc(TChargingOrderShareRecord::getId)); |
| | | if (shareRecords == null || shareRecords.isEmpty()) { |
| | | return R.ok(); |
| | | } |
| | | BigDecimal thirdPayAmount = getRefundableThirdPayAmount(order); |
| | | if (thirdPayAmount.compareTo(BigDecimal.ZERO) <= 0) { |
| | | return R.fail("订单已分账但第三方支付金额异常,禁止退款"); |
| | | } |
| | | BigDecimal cumulativeThirdRefund = money(order.getThirdRefundAmount()).add(thirdRefundAmount).min(thirdPayAmount).setScale(2, RoundingMode.HALF_UP); |
| | | for (TChargingOrderShareRecord shareRecord : shareRecords) { |
| | | BigDecimal shareAmount = money(shareRecord.getShareAmount()); |
| | | if (shareAmount.compareTo(BigDecimal.ZERO) <= 0) { |
| | | continue; |
| | | } |
| | | BigDecimal targetRefundAmount = shareAmount.multiply(cumulativeThirdRefund).divide(thirdPayAmount, 2, RoundingMode.HALF_UP).min(shareAmount).setScale(2, RoundingMode.HALF_UP); |
| | | BigDecimal refundedShareAmount; |
| | | try { |
| | | refundedShareAmount = queryIcbcShareRefundAmount(order, shareRecord); |
| | | } catch (ServiceException e) { |
| | | return R.fail(e.getMessage()); |
| | | } |
| | | BigDecimal shareRefundAmount = targetRefundAmount.subtract(refundedShareAmount).setScale(2, RoundingMode.HALF_UP); |
| | | if (shareRefundAmount.compareTo(BigDecimal.ZERO) <= 0) { |
| | | continue; |
| | | } |
| | | R result = refundIcbcShareRecord(order, shareRecord, refund, shareRefundAmount); |
| | | if (result.getCode() != 200) { |
| | | return result; |
| | | } |
| | | } |
| | | return R.ok(); |
| | | } |
| | | |
| | | private BigDecimal queryIcbcShareRefundAmount(TChargingOrder order, TChargingOrderShareRecord shareRecord) { |
| | | try { |
| | | IcbcSubOrderQueryReq queryReq = IcbcAdminFundOperationSupport.buildSubOrderQueryRequest(order, shareRecord); |
| | | R<IcbcSubOrderQueryResp> queryResult = icbcPaymentClient.querySubOrder(queryReq); |
| | | IcbcSubOrderQueryResp queryResp = queryResult == null ? null : queryResult.getData(); |
| | | if (queryResult == null || queryResult.getCode() != 200 || queryResp == null || !"SUCCESS".equals(queryResp.getStatus())) { |
| | | throw new ServiceException(queryResult == null ? "工行子订单查询无响应" : firstNotBlank(queryResult.getMsg(), "工行子订单查询失败")); |
| | | } |
| | | return parseIcbcAmount(queryResp.getOrderRefundAmnt()); |
| | | } catch (IllegalArgumentException e) { |
| | | throw new ServiceException(e.getMessage()); |
| | | } catch (ServiceException e) { |
| | | throw e; |
| | | } catch (Exception e) { |
| | | throw new ServiceException("工行子订单查询异常:" + firstNotBlank(e.getMessage(), "未知异常")); |
| | | } |
| | | } |
| | | |
| | | private R refundIcbcShareRecord(TChargingOrder order, TChargingOrderShareRecord shareRecord, TChargingOrderRefund refund, BigDecimal shareRefundAmount) { |
| | | String suffix = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS")); |
| | | String retSubOrderId = ("SPSUB" + refund.getRefundCode() + shareRecord.getId()).substring(0, Math.min(50, ("SPSUB" + refund.getRefundCode() + shareRecord.getId()).length())); |
| | | String outtrxSerialNo = ("SPSUBREF" + suffix + shareRecord.getId()).substring(0, Math.min(50, ("SPSUBREF" + suffix + shareRecord.getId()).length())); |
| | | try { |
| | | IcbcSubOrderRefundReq refundReq = IcbcAdminFundOperationSupport.buildSubOrderRefundRequest( |
| | | order, shareRecord, shareRefundAmount, retSubOrderId, outtrxSerialNo); |
| | | refundReq.setMerSpInfor("特殊退款分账退回:" + order.getCode()); |
| | | R<IcbcSubOrderRefundResp> refundResult = icbcPaymentClient.refundSubOrder(refundReq); |
| | | IcbcSubOrderRefundResp refundResp = refundResult == null ? null : refundResult.getData(); |
| | | if (refundResult == null || refundResult.getCode() != 200 || refundResp == null || !"SUCCESS".equals(refundResp.getStatus())) { |
| | | String message = refundResult == null ? "工行分账退款无响应" : firstNotBlank(refundResult.getMsg(), refundResp == null ? null : refundResp.getRawResponse(), "工行分账退款失败"); |
| | | log.error("特殊退款分账退回失败,订单号:{},退款单号:{},分账记录:{},金额:{},原因:{}", |
| | | order.getCode(), refund.getRefundCode(), shareRecord.getId(), shareRefundAmount, message); |
| | | return R.fail("分账退回失败:" + message); |
| | | } |
| | | log.info("特殊退款分账退回成功,订单号:{},退款单号:{},分账记录:{},金额:{}", |
| | | order.getCode(), refund.getRefundCode(), shareRecord.getId(), shareRefundAmount); |
| | | return R.ok(); |
| | | } catch (IllegalArgumentException | ServiceException e) { |
| | | log.error("特殊退款分账退回失败,订单号:{},退款单号:{},分账记录:{},金额:{}", |
| | | order.getCode(), refund.getRefundCode(), shareRecord.getId(), shareRefundAmount, e); |
| | | return R.fail("分账退回失败:" + e.getMessage()); |
| | | } catch (Exception e) { |
| | | log.error("特殊退款分账退回异常,订单号:{},退款单号:{},分账记录:{},金额:{}", |
| | | order.getCode(), refund.getRefundCode(), shareRecord.getId(), shareRefundAmount, e); |
| | | return R.fail("分账退回异常:" + firstNotBlank(e.getMessage(), "未知异常")); |
| | | } |
| | | } |
| | | |
| | | private BigDecimal parseIcbcAmount(String amount) { |
| | | if (!StringUtils.hasLength(amount)) { |
| | | return BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP); |
| | | } |
| | | String value = amount.trim(); |
| | | try { |
| | | if (value.contains(".")) { |
| | | return money(new BigDecimal(value)); |
| | | } |
| | | return new BigDecimal(value).divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP); |
| | | } catch (NumberFormatException e) { |
| | | throw new ServiceException("工行金额格式异常:" + amount); |
| | | } |
| | | } |
| | | |
| | | private R validateOperationPassword(PayOrderRefundDto payOrderQueryDto) { |
| | |
| | | } |
| | | } |
| | | |
| | | private String buildIcbcPaymentFailReason(IcbcPaymentResp resp) { |
| | | if (resp == null) { |
| | | return "工行支付服务未返回支付数据"; |
| | | } |
| | | String reason = firstNotBlank(resp.getRawResponse(), resp.getStatus()); |
| | | if (!StringUtils.hasLength(reason)) { |
| | | return "工行支付服务返回数据不完整"; |
| | | } |
| | | if (reason.length() > 500) { |
| | | return reason.substring(0, 500); |
| | | } |
| | | return reason; |
| | | } |
| | | |
| | | private void triggerChargingOrderShare(TChargingOrder chargingOrder, Site site) { |
| | | if (!isNeedChargingOrderShare(chargingOrder)) { |
| | | return; |
| | |
| | | |
| | | import javax.annotation.Resource; |
| | | import java.time.LocalDateTime; |
| | | import java.util.HashMap; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.stream.Collectors; |
| | |
| | | //获取标签map |
| | | @PostMapping(value = "/getTagMap") |
| | | public R<Map<Integer, String>> getTagMap(@RequestBody List<Integer> ids) { |
| | | if (ids == null || ids.isEmpty()) { |
| | | return R.ok(new HashMap<>()); |
| | | } |
| | | |
| | | List<TUserTag> list = tUserTagService.lambdaQuery().in(!ids.isEmpty(), TUserTag::getId, ids).list(); |
| | | List<TUserTag> list = tUserTagService.lambdaQuery().in(TUserTag::getId, ids).list(); |
| | | Map<Integer, String> idNameMap = list.stream() |
| | | .collect(Collectors.toMap( |
| | | TUserTag::getId, |
| | |
| | | @Configuration |
| | | @ConfigurationProperties(prefix = "icbc.pay") |
| | | public class IcbcPayProperties { |
| | | private static final String ICBC_GATEWAY = "https://gw.open.icbc.com.cn/api/cardbusiness"; |
| | | |
| | | private String appId; |
| | | private String privateKey; |
| | | private String icbcPublicKey; |
| | | private String signType = "RSA2"; |
| | | private String charset = "UTF-8"; |
| | | private String format = "json"; |
| | | private String gatewayUrl; |
| | | private String orderQueryUrl; |
| | | private String refundUrl; |
| | | private String subOrderSplitUrl; |
| | | private String subOrderRefundUrl; |
| | | private String subOrderQueryUrl; |
| | | private String gatewayUrl = ICBC_GATEWAY + "/aggregatepay/b2c/online/consumepurchase/V2"; |
| | | private String orderQueryUrl = ICBC_GATEWAY + "/aggregatepay/b2c/online/orderqry/V2"; |
| | | private String refundUrl = ICBC_GATEWAY + "/aggregatepay/b2c/online/merrefund/V1"; |
| | | private String subOrderSplitUrl = ICBC_GATEWAY + "/aggregatepay/b2c/online/sersuborder/V1"; |
| | | private String subOrderRefundUrl = ICBC_GATEWAY + "/aggregatepay/b2c/online/sersuborderrefund/V1"; |
| | | private String subOrderQueryUrl = ICBC_GATEWAY + "/order/suborderquery/V1"; |
| | | private String notifyUrl; |
| | | private String merId; |
| | | private String merPrtclNo; |
| New file |
| | |
| | | -- 支付订单:特殊退款处理菜单及执行按钮权限 |
| | | |
| | | SET @payment_order_id := ( |
| | | SELECT menu_id |
| | | FROM sys_menu |
| | | WHERE menu_name = '支付订单' |
| | | AND (perms = '/paymentOrder' OR path = '/order/payMentOrder' OR path = 'payMentOrder') |
| | | ORDER BY menu_id ASC |
| | | LIMIT 1 |
| | | ); |
| | | |
| | | INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, remark) |
| | | SELECT '特殊退款处理', @payment_order_id, 3, '/order/payMentOrder/specialRefund', 'order/payMentOrder/specialRefund', 1, 0, 'C', '0', 0, '/paymentOrder/specialRefund', '#', 'admin', NOW(), '支付订单特殊退款处理' |
| | | WHERE @payment_order_id IS NOT NULL |
| | | AND NOT EXISTS ( |
| | | SELECT 1 |
| | | FROM sys_menu |
| | | WHERE parent_id = @payment_order_id |
| | | AND (perms = '/paymentOrder/specialRefund' OR path = '/order/payMentOrder/specialRefund') |
| | | ); |
| | | |
| | | SET @special_refund_id := ( |
| | | SELECT menu_id |
| | | FROM sys_menu |
| | | WHERE parent_id = @payment_order_id |
| | | AND (perms = '/paymentOrder/specialRefund' OR path = '/order/payMentOrder/specialRefund') |
| | | ORDER BY menu_id ASC |
| | | LIMIT 1 |
| | | ); |
| | | |
| | | UPDATE sys_menu |
| | | SET menu_name = '特殊退款处理', |
| | | parent_id = @payment_order_id, |
| | | order_num = 3, |
| | | path = '/order/payMentOrder/specialRefund', |
| | | component = 'order/payMentOrder/specialRefund', |
| | | menu_type = 'C', |
| | | visible = '0', |
| | | status = 0, |
| | | perms = '/paymentOrder/specialRefund', |
| | | update_time = NOW() |
| | | WHERE @special_refund_id IS NOT NULL |
| | | AND menu_id = @special_refund_id; |
| | | |
| | | INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, remark) |
| | | SELECT '发起特殊退款', @special_refund_id, 1, '', NULL, 1, 0, 'F', '0', 0, '/paymentOrder/specialRefund/refund', '#', 'admin', NOW(), '发起特殊退款按钮权限' |
| | | WHERE @special_refund_id IS NOT NULL |
| | | AND NOT EXISTS ( |
| | | SELECT 1 |
| | | FROM sys_menu |
| | | WHERE parent_id = @special_refund_id |
| | | AND perms = '/paymentOrder/specialRefund/refund' |
| | | ); |
| | | |
| | | -- 管理员角色补上新权限;其他角色请在角色管理中按需勾选。 |
| | | INSERT IGNORE INTO sys_role_menu (role_id, menu_id) |
| | | SELECT 1, menu_id |
| | | FROM sys_menu |
| | | WHERE menu_id IN (@special_refund_id) |
| | | OR parent_id = @special_refund_id; |