From faaf70b3fc0cff90d25fa30b37f225f679a0d5a5 Mon Sep 17 00:00:00 2001
From: longz <longzhaoz@qq.com>
Date: 星期一, 20 七月 2026 19:30:08 +0800
Subject: [PATCH] 特殊退款查询
---
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/TChargingOrderServiceImpl.java | 294 +++++++++++++++++++++++------
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/TChargingBillServiceImpl.java | 120 +++++++++++
ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/icbc/config/IcbcPayProperties.java | 14
ruoyi-api/ruoyi-api-account/src/main/java/com/ruoyi/account/api/model/TAppUser.java | 4
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/controller/TAppUserController.java | 34 ++
sql/20260720_special_refund_menu.sql | 60 ++++++
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/controller/TUserTagController.java | 6
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/dto/PayOrderRefundDto.java | 2
ruoyi-service/ruoyi-chargingPile/src/main/resources/logback.xml | 2
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/controller/TChargingOrderController.java | 8
10 files changed, 460 insertions(+), 84 deletions(-)
diff --git a/ruoyi-api/ruoyi-api-account/src/main/java/com/ruoyi/account/api/model/TAppUser.java b/ruoyi-api/ruoyi-api-account/src/main/java/com/ruoyi/account/api/model/TAppUser.java
index 2ad2a5d..3d22d5b 100644
--- a/ruoyi-api/ruoyi-api-account/src/main/java/com/ruoyi/account/api/model/TAppUser.java
+++ b/ruoyi-api/ruoyi-api-account/src/main/java/com/ruoyi/account/api/model/TAppUser.java
@@ -132,6 +132,10 @@
@TableField(exist = false)
private String tagName;
+ @ApiModelProperty(value = "是否白名单:1=是,2=否")
+ @TableField(exist = false)
+ private Integer whitelistFlag;
+
@ApiModelProperty(value = "单位名称")
@TableField(exist = false)
private String companyName;
diff --git a/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/controller/TAppUserController.java b/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/controller/TAppUserController.java
index c06423e..fd2bb96 100644
--- a/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/controller/TAppUserController.java
+++ b/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/controller/TAppUserController.java
@@ -428,6 +428,31 @@
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) {
@@ -441,6 +466,9 @@
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()));
@@ -731,11 +759,7 @@
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));
diff --git a/ruoyi-service/ruoyi-chargingPile/src/main/resources/logback.xml b/ruoyi-service/ruoyi-chargingPile/src/main/resources/logback.xml
index 3865fab..2e40bbb 100644
--- a/ruoyi-service/ruoyi-chargingPile/src/main/resources/logback.xml
+++ b/ruoyi-service/ruoyi-chargingPile/src/main/resources/logback.xml
@@ -8,7 +8,7 @@
<!-- 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. 日志格式和颜色渲染 -->
<!-- 彩色日志依赖的渲染类 -->
diff --git a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/controller/TChargingOrderController.java b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/controller/TChargingOrderController.java
index df707b0..31539c6 100644
--- a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/controller/TChargingOrderController.java
+++ b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/controller/TChargingOrderController.java
@@ -440,14 +440,16 @@
@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);
}
@@ -616,7 +618,7 @@
- @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 = {"管理后台-支付订单-退款订单"})
diff --git a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/dto/PayOrderRefundDto.java b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/dto/PayOrderRefundDto.java
index b6b9006..107ad23 100644
--- a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/dto/PayOrderRefundDto.java
+++ b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/dto/PayOrderRefundDto.java
@@ -16,7 +16,7 @@
private String code;
@ApiModelProperty("支付流水号")
private String serialNumber;
- @ApiModelProperty("支付宝特殊退款方式:ALI=支付宝退款,ICBC=工商退款")
+ @ApiModelProperty("退款通道(兼容旧参数,特殊退款按原支付路径退款)")
private String refundChannel;
private BigDecimal refundAmount;
private String reason;
diff --git a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/TChargingBillServiceImpl.java b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/TChargingBillServiceImpl.java
index 29e25b1..b3d838c 100644
--- a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/TChargingBillServiceImpl.java
+++ b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/TChargingBillServiceImpl.java
@@ -36,6 +36,7 @@
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;
@@ -43,6 +44,8 @@
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;
/**
@@ -1057,10 +1060,10 @@
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());
@@ -1077,7 +1080,7 @@
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);
@@ -1118,13 +1121,11 @@
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"));
}
@@ -1145,6 +1146,111 @@
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){
diff --git a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/TChargingOrderServiceImpl.java b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/TChargingOrderServiceImpl.java
index baff07a..c506ddc 100644
--- a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/TChargingOrderServiceImpl.java
+++ b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/TChargingOrderServiceImpl.java
@@ -966,7 +966,13 @@
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());
@@ -974,7 +980,9 @@
}
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();
@@ -2385,12 +2393,23 @@
* @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());
@@ -2409,18 +2428,21 @@
}
}
//充电频率
- 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));
@@ -2934,13 +2956,23 @@
@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());
@@ -4700,61 +4732,189 @@
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) {
- return R.fail("退款金额不能超过剩余可退金额");
+ RLock lock = redissonClient.getLock("SPECIAL_REFUND_ORDER_LOCK:" + order.getId());
+ if (!lock.tryLock()) {
+ return R.fail("该订单正在退款处理中,请勿重复提交");
}
- BigDecimal refundedThirdAmount = money(order.getThirdRefundAmount());
- BigDecimal refundableThirdAmount = money(order.getThirdPayAmount()).subtract(refundedThirdAmount);
- if (refundableThirdAmount.compareTo(BigDecimal.ZERO) < 0) {
- refundableThirdAmount = BigDecimal.ZERO;
+ 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) {
+ return R.fail("退款金额不能超过剩余可退金额");
+ }
+ BigDecimal refundedThirdAmount = money(order.getThirdRefundAmount());
+ BigDecimal refundableThirdAmount = money(order.getThirdPayAmount()).subtract(refundedThirdAmount);
+ if (refundableThirdAmount.compareTo(BigDecimal.ZERO) < 0) {
+ refundableThirdAmount = BigDecimal.ZERO;
+ }
+ BigDecimal thirdRefundAmount = isBalancePayMode(order.getPayMethod())
+ ? refundAmount.min(refundableThirdAmount).setScale(2, RoundingMode.HALF_UP)
+ : refundAmount;
+ BigDecimal balanceRefundAmount = refundAmount.subtract(thirdRefundAmount).setScale(2, RoundingMode.HALF_UP);
+ if (balanceRefundAmount.compareTo(BigDecimal.ZERO) < 0) {
+ balanceRefundAmount = BigDecimal.ZERO;
+ }
+ TChargingOrderRefund refund = buildSpecialChargingRefund(order, payOrderQueryDto, refundAmount, balanceRefundAmount, thirdRefundAmount);
+ if (thirdRefundAmount.compareTo(BigDecimal.ZERO) == 0) {
+ refund.setRefundSerialNumber("BALANCE");
+ finishManualChargingRefund(order, refund, refundAmount, balanceRefundAmount, thirdRefundAmount);
+ return R.ok();
+ }
+ Integer payType = order.getRechargePaymentType();
+ boolean success = false;
+ 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();
+ dto.setOutTradeNo(order.getCode());
+ dto.setOutRequestNo(refund.getRefundCode());
+ dto.setRefundAmount(thirdRefundAmount.toString());
+ dto.setRefundReason(SPECIAL_REFUND_TITLE);
+ RefundResp resp = aliPaymentClient.refund(dto).getData();
+ if (resp != null) {
+ refund.setRefundSerialNumber(resp.getTradeNo());
+ refund.setRefundTime(LocalDateTime.now());
+ finishManualChargingRefund(order, refund, refundAmount, balanceRefundAmount, thirdRefundAmount);
+ success = true;
+ }
+ } else if (Integer.valueOf(3).equals(payType)) {
+ RefundReq dto = new RefundReq();
+ dto.setOutTradeNo(order.getRechargeSerialNumber());
+ dto.setOutRequestNo(refund.getRefundCode());
+ dto.setRefundAmount(thirdRefundAmount.toString());
+ dto.setRefundReason(SPECIAL_REFUND_TITLE);
+ RefundResp resp = h5AliPaymentClient.refund(dto).getData();
+ if (resp != null && "10000".equals(resp.getCode())) {
+ refund.setRefundSerialNumber(resp.getTradeNo());
+ refund.setRefundTime(LocalDateTime.now());
+ finishManualChargingRefund(order, refund, refundAmount, balanceRefundAmount, thirdRefundAmount);
+ success = true;
+ }
+ }
+ return success ? R.ok() : R.fail("退款失败,请检查支付渠道或稍后重试");
+ } finally {
+ if (lock.isHeldByCurrentThread()) {
+ lock.unlock();
+ }
}
- BigDecimal thirdRefundAmount = isBalancePayMode(order.getPayMethod())
- ? refundAmount.min(refundableThirdAmount).setScale(2, RoundingMode.HALF_UP)
- : refundAmount;
- BigDecimal balanceRefundAmount = refundAmount.subtract(thirdRefundAmount).setScale(2, RoundingMode.HALF_UP);
- if (balanceRefundAmount.compareTo(BigDecimal.ZERO) < 0) {
- balanceRefundAmount = BigDecimal.ZERO;
- }
- TChargingOrderRefund refund = buildSpecialChargingRefund(order, payOrderQueryDto, refundAmount, balanceRefundAmount, thirdRefundAmount);
- if (thirdRefundAmount.compareTo(BigDecimal.ZERO) == 0) {
- refund.setRefundSerialNumber("BALANCE");
- finishManualChargingRefund(order, refund, refundAmount, balanceRefundAmount, thirdRefundAmount);
+ }
+
+ private R refundIcbcShareBeforeMainRefund(TChargingOrder order, TChargingOrderRefund refund, BigDecimal thirdRefundAmount) {
+ if (order == null || refund == null || thirdRefundAmount == null || thirdRefundAmount.compareTo(BigDecimal.ZERO) <= 0) {
return R.ok();
}
- Integer payType = order.getRechargePaymentType();
- boolean success = false;
- if (Integer.valueOf(1).equals(payType) || "ICBC".equalsIgnoreCase(payOrderQueryDto.getRefundChannel())) {
- success = refundByIcbcAndFinish(order, refund, thirdRefundAmount, SPECIAL_REFUND_TITLE, true, refundAmount, balanceRefundAmount);
- } else if (Integer.valueOf(2).equals(payType)) {
- RefundReq dto = new RefundReq();
- dto.setOutTradeNo(order.getCode());
- dto.setOutRequestNo(refund.getRefundCode());
- dto.setRefundAmount(thirdRefundAmount.toString());
- dto.setRefundReason(SPECIAL_REFUND_TITLE);
- RefundResp resp = aliPaymentClient.refund(dto).getData();
- if (resp != null) {
- refund.setRefundSerialNumber(resp.getTradeNo());
- refund.setRefundTime(LocalDateTime.now());
- finishManualChargingRefund(order, refund, refundAmount, balanceRefundAmount, thirdRefundAmount);
- success = true;
+ 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;
}
- } else if (Integer.valueOf(3).equals(payType)) {
- RefundReq dto = new RefundReq();
- dto.setOutTradeNo(order.getRechargeSerialNumber());
- dto.setOutRequestNo(refund.getRefundCode());
- dto.setRefundAmount(thirdRefundAmount.toString());
- dto.setRefundReason(SPECIAL_REFUND_TITLE);
- RefundResp resp = h5AliPaymentClient.refund(dto).getData();
- if (resp != null && "10000".equals(resp.getCode())) {
- refund.setRefundSerialNumber(resp.getTradeNo());
- refund.setRefundTime(LocalDateTime.now());
- finishManualChargingRefund(order, refund, refundAmount, balanceRefundAmount, thirdRefundAmount);
- success = true;
+ 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 success ? R.ok() : R.fail("退款失败,请检查支付渠道或稍后重试");
+ 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) {
@@ -7038,6 +7198,20 @@
}
}
+ 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;
diff --git a/ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/controller/TUserTagController.java b/ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/controller/TUserTagController.java
index a4f5fa9..c82aeb9 100644
--- a/ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/controller/TUserTagController.java
+++ b/ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/controller/TUserTagController.java
@@ -14,6 +14,7 @@
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;
@@ -59,8 +60,11 @@
//获取标签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,
diff --git a/ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/icbc/config/IcbcPayProperties.java b/ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/icbc/config/IcbcPayProperties.java
index 5ea37d0..1866103 100644
--- a/ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/icbc/config/IcbcPayProperties.java
+++ b/ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/icbc/config/IcbcPayProperties.java
@@ -13,18 +13,20 @@
@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;
diff --git a/sql/20260720_special_refund_menu.sql b/sql/20260720_special_refund_menu.sql
new file mode 100644
index 0000000..554875b
--- /dev/null
+++ b/sql/20260720_special_refund_menu.sql
@@ -0,0 +1,60 @@
+-- 支付订单:特殊退款处理菜单及执行按钮权限
+
+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;
--
Gitblit v1.7.1