mirror of
https://codeup.aliyun.com/67c68d4e484ca2f0a13ac3c1/ydc/jsowell-charger-web.git
synced 2026-08-12 17:23:50 +08:00
Merge branch 'dev' into feature-business-minigram
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
package com.jsowell.api.uniapp.business;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.jsowell.common.core.controller.BaseController;
|
||||
import com.jsowell.common.core.page.PageResponse;
|
||||
@@ -7,6 +8,7 @@ import com.jsowell.common.enums.ykc.ReturnCodeEnum;
|
||||
import com.jsowell.common.exception.BusinessException;
|
||||
import com.jsowell.common.response.RestApiResponse;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.jsowell.pile.dto.IndexQueryDTO;
|
||||
import com.jsowell.pile.dto.MerchantOrderReportDTO;
|
||||
import com.jsowell.pile.dto.ParkingCouponRecordQueryDTO;
|
||||
import com.jsowell.pile.dto.business.BusinessDailySettlementQueryDTO;
|
||||
@@ -16,6 +18,7 @@ import com.jsowell.pile.dto.business.BusinessScaleQueryDTO;
|
||||
import com.jsowell.pile.dto.business.BusinessWorkbenchSummaryQueryDTO;
|
||||
import com.jsowell.pile.vo.uniapp.business.BusinessDailySettlementDetailVO;
|
||||
import com.jsowell.pile.service.BusinessFinancialService;
|
||||
import com.jsowell.pile.service.OrderBasicInfoService;
|
||||
import com.jsowell.pile.vo.uniapp.business.BusinessEfficiencyAnalysisVO;
|
||||
import com.jsowell.pile.vo.uniapp.business.BusinessEfficiencyVO;
|
||||
import com.jsowell.pile.vo.uniapp.business.BusinessGunEfficiencyAnalysisVO;
|
||||
@@ -23,6 +26,7 @@ import com.jsowell.pile.vo.uniapp.business.BusinessOperationAnalysisVO;
|
||||
import com.jsowell.pile.vo.uniapp.business.BusinessScaleVO;
|
||||
import com.jsowell.pile.vo.uniapp.business.BusinessWorkbenchSummaryVO;
|
||||
import com.jsowell.pile.vo.web.MerchantOrderReportVO;
|
||||
import com.jsowell.pile.vo.web.TimeDistributionVO;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
@@ -30,6 +34,8 @@ import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 运营端财务信息相关Controller
|
||||
*
|
||||
@@ -42,6 +48,9 @@ public class BusinessFinancialController extends BaseController {
|
||||
@Autowired
|
||||
private BusinessFinancialService businessFinancialService;
|
||||
|
||||
@Autowired
|
||||
private OrderBasicInfoService orderBasicInfoService;
|
||||
|
||||
/**
|
||||
* 查询工作台首页汇总
|
||||
*
|
||||
@@ -293,4 +302,30 @@ public class BusinessFinancialController extends BaseController {
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* 大数据平台-充电时段分布
|
||||
*/
|
||||
@PostMapping("/getTimeDistribution")
|
||||
public RestApiResponse<?> getTimeDistribution(@RequestBody(required = false) IndexQueryDTO dto) {
|
||||
if (dto == null) dto = new IndexQueryDTO();
|
||||
// 默认查询最近30天,避免全表扫描超时
|
||||
if (com.jsowell.common.util.StringUtils.isEmpty(dto.getStartTime()) || com.jsowell.common.util.StringUtils.isEmpty(dto.getEndTime())) {
|
||||
java.time.LocalDate endDate = java.time.LocalDate.now();
|
||||
java.time.LocalDate startDate = endDate.minusDays(29);
|
||||
java.time.format.DateTimeFormatter fmt = java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
||||
dto.setStartTime(startDate.format(fmt) + " 00:00:00");
|
||||
dto.setEndTime(endDate.format(fmt) + " 23:59:59");
|
||||
}
|
||||
logger.info("运营端小程序查询充电时段分布查询 param:{}", JSON.toJSONString(dto));
|
||||
RestApiResponse<?> response;
|
||||
try {
|
||||
List<TimeDistributionVO> data = orderBasicInfoService.getTimeDistribution(dto);
|
||||
response = new RestApiResponse<>(data);
|
||||
} catch (Exception e) {
|
||||
logger.error("运营端小程序查询充电时段分布查询错误", e);
|
||||
response = new RestApiResponse<>("00200009", "充电时段分布查询错误");
|
||||
}
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import com.jsowell.common.constant.Constants;
|
||||
import com.jsowell.common.core.controller.BaseController;
|
||||
import com.jsowell.common.core.redis.RedisCache;
|
||||
import com.jsowell.common.enums.adapay.AdapayPayChannelEnum;
|
||||
import com.jsowell.common.enums.ykc.OrderPayModeEnum;
|
||||
import com.jsowell.common.enums.ykc.ReturnCodeEnum;
|
||||
import com.jsowell.common.exception.BusinessException;
|
||||
import com.jsowell.common.response.RestApiResponse;
|
||||
@@ -125,7 +126,8 @@ public class PayController extends BaseController {
|
||||
if (StringUtils.isBlank(memberId)) {
|
||||
throw new BusinessException(ReturnCodeEnum.CODE_TOKEN_ERROR);
|
||||
}
|
||||
if (dto.getPayAmount() == null) {
|
||||
if (dto.getPayAmount() == null
|
||||
&& !StringUtils.equals(OrderPayModeEnum.PAYMENT_OF_PRINCIPAL_BALANCE.getValue(), dto.getPayMode())) {
|
||||
throw new BusinessException(ReturnCodeEnum.CODE_PARAM_NOT_NULL_ERROR);
|
||||
}
|
||||
dto.setMemberId(memberId);
|
||||
|
||||
@@ -17,6 +17,7 @@ import com.jsowell.pile.service.PileBasicInfoService;
|
||||
import com.jsowell.pile.service.PileFirmwareInfoService;
|
||||
import com.jsowell.pile.service.PileMerchantInfoService;
|
||||
import com.jsowell.pile.service.PileReservationInfoService;
|
||||
import com.jsowell.pile.service.YuxinReservationChargingService;
|
||||
import com.jsowell.pile.vo.PileReservationInfoVO;
|
||||
import com.jsowell.pile.vo.uniapp.customer.PersonPileConnectorSumInfoVO;
|
||||
import com.jsowell.pile.vo.uniapp.customer.PersonPileRealTimeVO;
|
||||
@@ -55,6 +56,9 @@ public class PersonPileController extends BaseController {
|
||||
@Autowired
|
||||
private PileReservationInfoService pileReservationInfoService;
|
||||
|
||||
@Autowired
|
||||
private YuxinReservationChargingService yuxinReservationChargingService;
|
||||
|
||||
@Autowired
|
||||
private PileFirmwareInfoService pileFirmwareInfoService;
|
||||
|
||||
@@ -508,6 +512,52 @@ public class PersonPileController extends BaseController {
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* 羽信主板添加预约充电
|
||||
* http://localhost:8080/uniapp/personalPile/yuxin/createReserved
|
||||
*/
|
||||
@PostMapping("/yuxin/createReserved")
|
||||
public RestApiResponse<?> createYuxinReserved(HttpServletRequest request, @RequestBody YuxinReservationChargingDTO dto) {
|
||||
RestApiResponse<?> response = null;
|
||||
try {
|
||||
String memberId = getMemberIdByAuthorization(request);
|
||||
dto.setMemberId(memberId);
|
||||
yuxinReservationChargingService.createReservation(dto);
|
||||
response = new RestApiResponse<>();
|
||||
} catch (BusinessException e) {
|
||||
logger.error("羽信添加预约充电error, params:{}", dto, e);
|
||||
response = new RestApiResponse<>(e.getCode(), e.getMessage());
|
||||
} catch (Exception e) {
|
||||
logger.error("羽信添加预约充电error, params:{}", dto, e);
|
||||
response = new RestApiResponse<>(ReturnCodeEnum.CODE_CREATE_RESERVED_ERROR);
|
||||
}
|
||||
logger.info("羽信添加预约充电params:{}, result:{}", dto, response);
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* 羽信主板取消预约充电
|
||||
* http://localhost:8080/uniapp/personalPile/yuxin/cancelReserved
|
||||
*/
|
||||
@PostMapping("/yuxin/cancelReserved")
|
||||
public RestApiResponse<?> cancelYuxinReserved(HttpServletRequest request, @RequestBody YuxinReservationChargingDTO dto) {
|
||||
RestApiResponse<?> response = null;
|
||||
try {
|
||||
String memberId = getMemberIdByAuthorization(request);
|
||||
dto.setMemberId(memberId);
|
||||
yuxinReservationChargingService.cancelReservation(dto);
|
||||
response = new RestApiResponse<>();
|
||||
} catch (BusinessException e) {
|
||||
logger.error("羽信取消预约充电error, params:{}", dto, e);
|
||||
response = new RestApiResponse<>(e.getCode(), e.getMessage());
|
||||
} catch (Exception e) {
|
||||
logger.error("羽信取消预约充电error, params:{}", dto, e);
|
||||
response = new RestApiResponse<>(ReturnCodeEnum.CODE_UPDATE_RESERVED_STATUS_ERROR);
|
||||
}
|
||||
logger.info("羽信取消预约充电params:{}, result:{}", dto, response);
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存蓝牙充电记录
|
||||
* http://localhost:8080/uniapp/personalPile/saveBluetoothChargingRecord
|
||||
|
||||
@@ -606,6 +606,10 @@ public class OrderService {
|
||||
if (StringUtils.isNotBlank(String.valueOf(stationInfo.getOccupyFee()))) {
|
||||
vo.setOccupyFee(stationInfo.getOccupyFee());
|
||||
}
|
||||
// 保险金额
|
||||
if (StringUtils.isNotBlank(String.valueOf(orderBasicInfo.getInsuranceAmount()))) {
|
||||
vo.setInsuranceAmount(String.valueOf(orderBasicInfo.getInsuranceAmount()));
|
||||
}
|
||||
|
||||
// 获取充电桩枪口信息
|
||||
PileConnectorDetailVO pileConnectorDetailVO = pileService.queryPileConnectorDetail(vo.getPileConnectorCode());
|
||||
@@ -1295,19 +1299,20 @@ public class OrderService {
|
||||
}
|
||||
}, executor);
|
||||
|
||||
// 初始化充电金额
|
||||
BigDecimal chargeAmount = amount;
|
||||
// 支付订单成功
|
||||
OrderBasicInfo orderBasicInfo = orderBasicInfoService.getOrderInfoByOrderCode(orderCode);
|
||||
BigDecimal insuranceAmount = orderBasicInfo.getInsuranceAmount();
|
||||
if (insuranceAmount.compareTo(BigDecimal.ZERO) > 0) {
|
||||
// 如果用户支付了保险金额,则充电金额需将保险金额减去
|
||||
// 主表 payAmount 只存充电金额(不含保险):充电金额 = 网关实付(amount) - 保险金额;
|
||||
// order_pay_record 仍记录完整实付(amount),故校验时 明细 == payAmount + 保险。
|
||||
BigDecimal chargeAmount = amount;
|
||||
if (insuranceAmount != null && insuranceAmount.compareTo(BigDecimal.ZERO) > 0) {
|
||||
chargeAmount = amount.subtract(insuranceAmount).setScale(2, RoundingMode.HALF_UP);
|
||||
}
|
||||
// 支付订单成功
|
||||
log.info("adapayCallback-订单:{}, 网关支付金额(amount):{}, 订单保险金额(insuranceAmount):{}, 充电金额(payAmount将记录):{}, order_pay_record记录:{}",
|
||||
orderCode, amount, insuranceAmount, chargeAmount, amount);
|
||||
PayOrderSuccessCallbackDTO callbackDTO = PayOrderSuccessCallbackDTO.builder()
|
||||
.orderCode(orderCode)
|
||||
.payAmount(chargeAmount) // amount
|
||||
// .payMode(OrderPayModeEnum.PAYMENT_OF_WECHATPAY.getValue())
|
||||
.payAmount(chargeAmount) // 充电金额(不含保险)
|
||||
.payMode(payModel)
|
||||
.acquirer(AcquirerEnum.ADAPAY.getValue())
|
||||
.build();
|
||||
|
||||
@@ -237,9 +237,12 @@ public class AdapayMemberController extends BaseController {
|
||||
try {
|
||||
adapayService.updateSettleAccountConfig(dto);
|
||||
result = AjaxResult.success();
|
||||
} catch (BusinessException e) {
|
||||
logger.warn("修改汇付结算配置warn", e);
|
||||
result = AjaxResult.error(e.getMessage());
|
||||
} catch (BaseAdaPayException e) {
|
||||
logger.error("查询汇付账户余额error", e);
|
||||
result = AjaxResult.error();
|
||||
logger.error("修改汇付结算配置error", e);
|
||||
result = AjaxResult.error("修改汇付结算配置异常");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -254,9 +257,12 @@ public class AdapayMemberController extends BaseController {
|
||||
try {
|
||||
adapayService.changeBankCard(dto);
|
||||
result = AjaxResult.success();
|
||||
} catch (BusinessException e) {
|
||||
logger.warn("换绑银行卡warn", e);
|
||||
result = AjaxResult.error(e.getMessage());
|
||||
} catch (BaseAdaPayException e) {
|
||||
logger.error("换绑银行卡error", e);
|
||||
result = AjaxResult.error();
|
||||
result = AjaxResult.error("换绑银行卡异常");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -292,12 +298,14 @@ public class AdapayMemberController extends BaseController {
|
||||
public AjaxResult deleteSettleAccount(@RequestBody AdapayMemberInfoDTO dto) {
|
||||
AjaxResult result = null;
|
||||
try {
|
||||
// 新写删除方法
|
||||
adapayService.deleteSettleAccount(dto);
|
||||
result = AjaxResult.success();
|
||||
} catch (BusinessException e) {
|
||||
logger.warn("删除结算账户warn", e);
|
||||
result = AjaxResult.error(e.getMessage());
|
||||
} catch (Exception e) {
|
||||
logger.error("删除结算账户 error,", e);
|
||||
result = AjaxResult.error();
|
||||
result = AjaxResult.error("删除结算账户异常");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -314,9 +322,13 @@ public class AdapayMemberController extends BaseController {
|
||||
AjaxResult result = null;
|
||||
try {
|
||||
adapayService.createBankAccount(dto);
|
||||
result = AjaxResult.success();
|
||||
} catch (BusinessException e) {
|
||||
logger.warn("重新创建结算账户warn", e);
|
||||
result = AjaxResult.error(e.getMessage());
|
||||
} catch (Exception e) {
|
||||
logger.error("重新创建结算账户 error, ", e);
|
||||
result = AjaxResult.error();
|
||||
result = AjaxResult.error("重新创建结算账户异常");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -132,13 +132,13 @@ public class MemberBasicInfoController extends BaseController {
|
||||
/**
|
||||
* 获取会员基础信息详细信息
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('member:memberGroup:list')")
|
||||
@PreAuthorize("@ss.hasPermi('member:info:query')")
|
||||
@GetMapping(value = "/{memberId}")
|
||||
public AjaxResult getInfo(@PathVariable("memberId") String memberId) {
|
||||
return AjaxResult.success(memberBasicInfoService.queryMemberInfoByMemberId(memberId));
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('member:memberGroup:list')")
|
||||
@PreAuthorize("@ss.hasPermi('member:info:query')")
|
||||
@GetMapping(value = "/getMemberPersonPileInfo/{memberId}")
|
||||
public AjaxResult getMemberPersonPileInfo(@PathVariable("memberId") String memberId) {
|
||||
return AjaxResult.success(memberBasicInfoService.getMemberPersonPileInfo(memberId));
|
||||
|
||||
@@ -10,6 +10,7 @@ import com.jsowell.pile.domain.OrderInvoiceRecord;
|
||||
import com.jsowell.pile.dto.GetInvoiceInfoDTO;
|
||||
import com.jsowell.pile.service.OrderInvoiceRecordService;
|
||||
import com.jsowell.pile.service.PileMerchantInfoService;
|
||||
import com.jsowell.pile.vo.web.OrderInvoiceExportVO;
|
||||
import com.jsowell.pile.vo.web.OrderInvoiceRecordVO;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
@@ -18,6 +19,7 @@ import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
@@ -64,6 +66,18 @@ public class OrderInvoiceRecordController extends BaseController {
|
||||
util.exportExcel(response, list, "申请开票数据");
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出所选申请开票详情
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('order:invoice:export')")
|
||||
@Log(title = "申请开票", businessType = BusinessType.EXPORT)
|
||||
@PostMapping("/export/selected")
|
||||
public void exportSelected(HttpServletResponse response, @RequestParam("ids") Integer[] ids) {
|
||||
List<OrderInvoiceExportVO> list = orderInvoiceRecordService.selectInvoiceExportList(ids);
|
||||
ExcelUtil<OrderInvoiceExportVO> util = new ExcelUtil<OrderInvoiceExportVO>(OrderInvoiceExportVO.class);
|
||||
util.exportExcel(response, list, "所选申请开票详情数据");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取申请开票详细信息
|
||||
*/
|
||||
|
||||
@@ -32,6 +32,7 @@ import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -422,7 +423,81 @@ public class PileStationInfoController extends BaseController {
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存停车平台配置
|
||||
*
|
||||
* @param config 停车平台配置
|
||||
* @return
|
||||
*/
|
||||
@PostMapping("/saveParkingConfig")
|
||||
public RestApiResponse<?> saveParkingConfig(@RequestBody ThirdpartyParkingConfig config) {
|
||||
logger.info("保存停车平台配置 params:{}", JSON.toJSONString(config));
|
||||
RestApiResponse<?> response = null;
|
||||
try {
|
||||
if (config == null) {
|
||||
throw new BusinessException(ReturnCodeEnum.CODE_PARAM_NOT_NULL_ERROR);
|
||||
}
|
||||
if (StringUtils.isBlank(config.getParkingName())
|
||||
|| StringUtils.isBlank(config.getPlatformType())
|
||||
|| StringUtils.isBlank(config.getSecretKey())
|
||||
|| StringUtils.isBlank(config.getParkId())
|
||||
|| StringUtils.isBlank(config.getApiUrl())) {
|
||||
throw new BusinessException(ReturnCodeEnum.CODE_PARAM_NOT_NULL_ERROR);
|
||||
}
|
||||
if ("4".equals(config.getPlatformType()) && StringUtils.isBlank(config.getAppId())) {
|
||||
throw new BusinessException(ReturnCodeEnum.CODE_PARAM_NOT_NULL_ERROR);
|
||||
}
|
||||
|
||||
Date now = new Date();
|
||||
if (config.getId() == null) {
|
||||
config.setCreateTime(now);
|
||||
config.setCreateBy(getUsername());
|
||||
if (StringUtils.isBlank(config.getDelFlag())) {
|
||||
config.setDelFlag("0");
|
||||
}
|
||||
parkingConfigService.insertSelective(config);
|
||||
} else {
|
||||
config.setUpdateTime(now);
|
||||
config.setUpdateBy(getUsername());
|
||||
parkingConfigService.updateByPrimaryKeySelective(config);
|
||||
}
|
||||
response = new RestApiResponse<>(config);
|
||||
} catch (BusinessException e) {
|
||||
logger.error("保存停车平台配置 error,", e);
|
||||
response = new RestApiResponse<>(e.getCode(), e.getMessage());
|
||||
} catch (Exception e) {
|
||||
logger.error("保存停车平台配置 error", e);
|
||||
response = new RestApiResponse<>(e);
|
||||
}
|
||||
logger.info("保存停车平台配置 result:{}", response);
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据站点获取已绑定停车平台配置
|
||||
*
|
||||
* @param stationId 站点ID
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@GetMapping("/getParkingConfigByStationId/{stationId}")
|
||||
public RestApiResponse<?> getParkingConfigByStationId(@PathVariable("stationId") String stationId) {
|
||||
RestApiResponse<?> response = null;
|
||||
try {
|
||||
if (StringUtils.isBlank(stationId)) {
|
||||
throw new BusinessException(ReturnCodeEnum.CODE_PARAM_NOT_NULL_ERROR);
|
||||
}
|
||||
ThirdpartyParkingConfig config = parkingConfigService.selectByStationId(stationId);
|
||||
response = new RestApiResponse<>(config);
|
||||
} catch (BusinessException e) {
|
||||
logger.error("根据站点获取停车平台配置 error,", e);
|
||||
response = new RestApiResponse<>(e.getCode(), e.getMessage());
|
||||
} catch (Exception e) {
|
||||
logger.error("根据站点获取停车平台配置 error", e);
|
||||
response = new RestApiResponse<>(e);
|
||||
}
|
||||
logger.info("根据站点获取停车平台配置 result:{}", response);
|
||||
return response;
|
||||
}
|
||||
|
||||
// public AjaxResult checkStationAmap
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ spring:
|
||||
# redis 配置
|
||||
redis:
|
||||
# 地址
|
||||
host: 192.168.0.8
|
||||
host: 192.168.0.6
|
||||
# 端口,默认为6379
|
||||
port: 6379
|
||||
# 数据库索引
|
||||
@@ -36,9 +36,9 @@ spring:
|
||||
druid:
|
||||
# 主库数据源
|
||||
master:
|
||||
# url: jdbc:mysql://192.168.0.8:3306/jsowell_pre?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
|
||||
# url: jdbc:mysql://192.168.0.6:3306/jsowell_pre?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
|
||||
# username: jsowell_pre
|
||||
url: jdbc:mysql://192.168.0.8:3306/jsowell_prd?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
|
||||
url: jdbc:mysql://192.168.0.6:3306/jsowell_prd?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
|
||||
username: jsowell_prd
|
||||
password: dev@js160829
|
||||
# 从库数据源
|
||||
@@ -90,7 +90,7 @@ spring:
|
||||
|
||||
# rabbitmq配置 dev
|
||||
rabbitmq:
|
||||
host: 192.168.0.8
|
||||
host: 192.168.0.6
|
||||
port: 5672
|
||||
username: admin
|
||||
password: admin
|
||||
@@ -121,7 +121,7 @@ logging:
|
||||
|
||||
# 基础URL前缀
|
||||
baseurl:
|
||||
prefix: http://192.168.0.8:8080
|
||||
prefix: http://192.168.0.6:8080
|
||||
|
||||
# Minio配置
|
||||
minio:
|
||||
@@ -271,7 +271,7 @@ dubbo:
|
||||
name: wcc-server
|
||||
qosEnable: false
|
||||
registry:
|
||||
address: nacos://192.168.0.8:8848
|
||||
address: nacos://192.168.0.6:8848
|
||||
parameters:
|
||||
namespace: e328faaf-8516-42d0-817a-7406232b3581
|
||||
username: nacos
|
||||
|
||||
@@ -0,0 +1,157 @@
|
||||
package com.jsowell.pile.service.impl;
|
||||
|
||||
import com.jsowell.common.enums.DelFlagEnum;
|
||||
import com.jsowell.common.enums.MemberWalletEnum;
|
||||
import com.jsowell.common.enums.ykc.OrderPayModeEnum;
|
||||
import com.jsowell.common.enums.ykc.OrderPayRecordEnum;
|
||||
import com.jsowell.common.enums.ykc.OrderPayStatusEnum;
|
||||
import com.jsowell.pile.domain.MemberWalletLog;
|
||||
import com.jsowell.pile.domain.OrderBasicInfo;
|
||||
import com.jsowell.pile.domain.OrderPayRecord;
|
||||
import com.jsowell.pile.service.MemberWalletLogService;
|
||||
import com.jsowell.pile.service.OrderPayRecordService;
|
||||
import com.jsowell.pile.vo.web.OrderPaymentCheckResult;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mockito;
|
||||
import org.objenesis.ObjenesisStd;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* 支付凭证校验({@code inspectOrderPayment})保险口径的单元测试(mock 依赖,不连库)。
|
||||
*
|
||||
* <p>规则:主表 payAmount = 充电金额;order_pay_record 汇总 = 完整实付(充电 + 保险);
|
||||
* {@code payAmount==明细 或 payAmount+保险==明细} 任一成立即视为一致。</p>
|
||||
*/
|
||||
class OrderPaymentVoucherInsuranceTest {
|
||||
|
||||
private static final String ORDER_CODE = "O-TEST";
|
||||
|
||||
private static void setField(Object target, String name, Object value) {
|
||||
try {
|
||||
Field f = target.getClass().getDeclaredField(name);
|
||||
f.setAccessible(true);
|
||||
f.set(target, value);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static OrderPaymentCheckResult inspect(OrderBasicInfoServiceImpl service, OrderBasicInfo order) {
|
||||
try {
|
||||
Method m = OrderBasicInfoServiceImpl.class.getDeclaredMethod("inspectOrderPayment", OrderBasicInfo.class);
|
||||
m.setAccessible(true);
|
||||
return (OrderPaymentCheckResult) m.invoke(service, order);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static OrderPayRecord payRecord(String payMode, String amount) {
|
||||
return OrderPayRecord.builder()
|
||||
.payMode(payMode)
|
||||
.payAmount(new BigDecimal(amount))
|
||||
.delFlag(DelFlagEnum.NORMAL.getValue())
|
||||
.createTime(new Date())
|
||||
.build();
|
||||
}
|
||||
|
||||
private static MemberWalletLog walletOut(String amount) {
|
||||
return MemberWalletLog.builder()
|
||||
.type(MemberWalletEnum.TYPE_OUT.getValue())
|
||||
.subType(MemberWalletEnum.SUBTYPE_PAYMENT_FOR_ORDER.getValue())
|
||||
.amount(new BigDecimal(amount))
|
||||
.createTime(new Date())
|
||||
.build();
|
||||
}
|
||||
|
||||
private static OrderBasicInfo order(String payMode, String payAmount, String insurance) {
|
||||
OrderBasicInfo o = new OrderBasicInfo();
|
||||
o.setOrderCode(ORDER_CODE);
|
||||
o.setPayMode(payMode);
|
||||
o.setPayStatus(OrderPayStatusEnum.paid.getValue());
|
||||
o.setPayAmount(new BigDecimal(payAmount));
|
||||
o.setPayTime(new Date());
|
||||
o.setInsuranceAmount(new BigDecimal(insurance));
|
||||
return o;
|
||||
}
|
||||
|
||||
private OrderBasicInfoServiceImpl serviceWith(List<OrderPayRecord> records, List<MemberWalletLog> walletLogs) {
|
||||
OrderPayRecordService payRecordService = Mockito.mock(OrderPayRecordService.class);
|
||||
MemberWalletLogService walletLogService = Mockito.mock(MemberWalletLogService.class);
|
||||
when(payRecordService.getOrderPayRecordList(ORDER_CODE)).thenReturn(records);
|
||||
when(walletLogService.getOrderRecord(ORDER_CODE, MemberWalletEnum.TYPE_OUT.getValue())).thenReturn(walletLogs);
|
||||
// OrderBasicInfoServiceImpl 构造器/字段初始化里有 SpringUtils.getBean,依赖 Spring 容器;
|
||||
// 用 Objenesis 绕过构造器实例化,只注入本用例需要的依赖。
|
||||
OrderBasicInfoServiceImpl service = new ObjenesisStd().newInstance(OrderBasicInfoServiceImpl.class);
|
||||
setField(service, "logger", LoggerFactory.getLogger(OrderPaymentVoucherInsuranceTest.class));
|
||||
setField(service, "orderPayRecordService", payRecordService);
|
||||
setField(service, "memberWalletLogService", walletLogService);
|
||||
return service;
|
||||
}
|
||||
|
||||
@Test
|
||||
void balanceInsuranceOrder_isConsistent() {
|
||||
// 余额:payAmount=50(充电),明细=50.5(充电+保险),保险0.5 → 50+0.5==50.5 ✓
|
||||
OrderBasicInfoServiceImpl service = serviceWith(
|
||||
Collections.singletonList(payRecord(OrderPayRecordEnum.PRINCIPAL_BALANCE_PAYMENT.getValue(), "50.5")),
|
||||
Collections.singletonList(walletOut("-50.5")));
|
||||
OrderPaymentCheckResult r = inspect(service,
|
||||
order(OrderPayModeEnum.PAYMENT_OF_PRINCIPAL_BALANCE.getValue(), "50.00", "0.5"));
|
||||
assertTrue(r.isOrderPaymentReady(), r.getReason());
|
||||
}
|
||||
|
||||
@Test
|
||||
void balanceNoInsurance_isConsistent() {
|
||||
// 余额无保险:payAmount=50,明细=50 → 50==50 ✓(回归)
|
||||
OrderBasicInfoServiceImpl service = serviceWith(
|
||||
Collections.singletonList(payRecord(OrderPayRecordEnum.PRINCIPAL_BALANCE_PAYMENT.getValue(), "50")),
|
||||
Collections.singletonList(walletOut("-50")));
|
||||
OrderPaymentCheckResult r = inspect(service,
|
||||
order(OrderPayModeEnum.PAYMENT_OF_PRINCIPAL_BALANCE.getValue(), "50", "0"));
|
||||
assertTrue(r.isOrderPaymentReady(), r.getReason());
|
||||
}
|
||||
|
||||
@Test
|
||||
void balanceGenuineMismatch_isFlagged() {
|
||||
// 余额真异常:payAmount=40,明细=50.5,保险0.5 → 40≠50.5 且 40.5≠50.5 → 不通过
|
||||
OrderBasicInfoServiceImpl service = serviceWith(
|
||||
Collections.singletonList(payRecord(OrderPayRecordEnum.PRINCIPAL_BALANCE_PAYMENT.getValue(), "50.5")),
|
||||
Collections.singletonList(walletOut("-50.5")));
|
||||
OrderPaymentCheckResult r = inspect(service,
|
||||
order(OrderPayModeEnum.PAYMENT_OF_PRINCIPAL_BALANCE.getValue(), "40", "0.5"));
|
||||
assertFalse(r.isOrderPaymentReady());
|
||||
}
|
||||
|
||||
@Test
|
||||
void onlineInsuranceOrder_isConsistent() {
|
||||
// 在线:payAmount=50(充电),明细=50.5,保险0.5 → 50+0.5==50.5 ✓
|
||||
OrderBasicInfoServiceImpl service = serviceWith(
|
||||
Collections.singletonList(payRecord(OrderPayRecordEnum.WECHATPAY_PAYMENT.getValue(), "50.5")),
|
||||
Collections.emptyList());
|
||||
OrderPaymentCheckResult r = inspect(service,
|
||||
order(OrderPayModeEnum.PAYMENT_OF_WECHATPAY.getValue(), "50.00", "0.5"));
|
||||
assertTrue(r.isOrderPaymentReady(), r.getReason());
|
||||
}
|
||||
|
||||
@Test
|
||||
void onlineLegacyFullPayAmount_isConsistent() {
|
||||
// 兼容 f11b781c2 期间历史单:payAmount=50.5(含保险),明细=50.5 → 50.5==50.5 ✓
|
||||
OrderBasicInfoServiceImpl service = serviceWith(
|
||||
Collections.singletonList(payRecord(OrderPayRecordEnum.WECHATPAY_PAYMENT.getValue(), "50.5")),
|
||||
Collections.emptyList());
|
||||
OrderPaymentCheckResult r = inspect(service,
|
||||
order(OrderPayModeEnum.PAYMENT_OF_WECHATPAY.getValue(), "50.5", "0.5"));
|
||||
assertTrue(r.isOrderPaymentReady(), r.getReason());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
package com.jsowell.pile.service.programlogic;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
/**
|
||||
* 余额支付「电费 + 保险费」退款计算的单元测试(纯计算,不连库)。
|
||||
*
|
||||
* <p>前提:order_pay_record 汇总(principalPay + giftPay) = 完整实付(充电 + 保险),
|
||||
* 故 {@code 电费预付款 = 总支付 - 保险}。这是「余额与在线一样从钱包扣保险」之后才成立的口径。</p>
|
||||
*
|
||||
* <p>覆盖场景:</p>
|
||||
* <ol>
|
||||
* <li>无保险 + 正常消费(回归,行为不变)</li>
|
||||
* <li>有保险 + 正常消费(消费≥1) → 保险不退</li>
|
||||
* <li>有保险 + 消费<1 → 保险退</li>
|
||||
* <li>保险跨本金/赠金拆分 + 消费<1</li>
|
||||
* <li>折扣把实际电费消费压到<1 → 保险退</li>
|
||||
* <li>消费恰好=1(边界)→ 保险不退</li>
|
||||
* </ol>
|
||||
*/
|
||||
class BalanceInsuranceRefundTest {
|
||||
|
||||
private static Map<String, BigDecimal> refund(String principalPay, String giftPay,
|
||||
String orderAmount, String discount, String insurance) {
|
||||
try {
|
||||
Method method = AbstractProgramLogic.class.getDeclaredMethod("calculateBalanceRefund",
|
||||
BigDecimal.class, BigDecimal.class, BigDecimal.class, BigDecimal.class, BigDecimal.class);
|
||||
method.setAccessible(true);
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, BigDecimal> result = (Map<String, BigDecimal>) method.invoke(null,
|
||||
new BigDecimal(principalPay), new BigDecimal(giftPay), new BigDecimal(orderAmount),
|
||||
new BigDecimal(discount), new BigDecimal(insurance));
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void assertAmount(String key, String expected, BigDecimal actual) {
|
||||
assertTrue(actual != null && new BigDecimal(expected).compareTo(actual) == 0,
|
||||
key + " 期望 " + expected + " 实际 " + actual);
|
||||
}
|
||||
|
||||
/** electricityPrincipal/Gift = 电费退款;insurancePrincipal/Gift = 保险退款。 */
|
||||
private static void assertRefund(Map<String, BigDecimal> m,
|
||||
String electricityPrincipal, String electricityGift,
|
||||
String insurancePrincipal, String insuranceGift) {
|
||||
assertAmount("returnPrincipal", electricityPrincipal, m.get("returnPrincipal"));
|
||||
assertAmount("returnGift", electricityGift, m.get("returnGift"));
|
||||
assertAmount("returnPrincipalForInsurance", insurancePrincipal, m.get("returnPrincipalForInsurance"));
|
||||
assertAmount("returnGiftForInsurance", insuranceGift, m.get("returnGiftForInsurance"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void noInsurance_normalConsumption_refundUnusedElectricityOnly() {
|
||||
// 本金50全充电,消费30 → 退20;无保险
|
||||
assertRefund(refund("50", "0", "30", "0", "0"), "20", "0", "0", "0");
|
||||
}
|
||||
|
||||
@Test
|
||||
void withInsurance_normalConsumption_insuranceNotRefunded() {
|
||||
// 实付50.5(充电50+保险0.5),消费30(≥1) → 电费退20,保险不退
|
||||
assertRefund(refund("50.5", "0", "30", "0", "0.5"), "20", "0", "0", "0");
|
||||
}
|
||||
|
||||
@Test
|
||||
void withInsurance_lowConsumption_insuranceRefunded() {
|
||||
// 实付50.5,消费0.5(<1) → 电费退49.5,保险退0.5
|
||||
assertRefund(refund("50.5", "0", "0.5", "0", "0.5"), "49.5", "0", "0.5", "0");
|
||||
}
|
||||
|
||||
@Test
|
||||
void insuranceSplitAcrossPrincipalAndGift_lowConsumption() {
|
||||
// 本金0.3+赠金50.2=50.5;保险0.5优先扣本金0.3、赠金补0.2;消费0.2(<1)
|
||||
// 电费:本金退0,赠金退49.8;保险退:本金0.3、赠金0.2
|
||||
assertRefund(refund("0.3", "50.2", "0.2", "0", "0.5"), "0", "49.8", "0.3", "0.2");
|
||||
}
|
||||
|
||||
@Test
|
||||
void discountReducesConsumptionBelowOne_insuranceRefunded() {
|
||||
// 消费10、折扣9.5 → 实际电费消费0.5(<1) → 电费退49.5,保险退0.5
|
||||
assertRefund(refund("50.5", "0", "10", "9.5", "0.5"), "49.5", "0", "0.5", "0");
|
||||
}
|
||||
|
||||
@Test
|
||||
void consumptionExactlyOne_insuranceNotRefunded() {
|
||||
// 实际电费消费=1.0,非严格<1 → 保险不退;电费退49
|
||||
assertRefund(refund("50.5", "0", "1", "0", "0.5"), "49", "0", "0", "0");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.jsowell.pile.util;
|
||||
|
||||
import com.jsowell.common.constant.Constants;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
class ChargeAmountUtilsTest {
|
||||
|
||||
@Test
|
||||
void resolveStartupAmount_shouldPreferFrontendAmount() {
|
||||
BigDecimal amount = ChargeAmountUtils.resolveStartupAmount(
|
||||
new BigDecimal("100"), new BigDecimal("300"));
|
||||
|
||||
assertEquals(new BigDecimal("100"), amount);
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveStartupAmount_shouldUseMemberDefaultWhenFrontendAmountIsNull() {
|
||||
BigDecimal amount = ChargeAmountUtils.resolveStartupAmount(null, new BigDecimal("300"));
|
||||
|
||||
assertEquals(new BigDecimal("300"), amount);
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveStartupAmount_shouldUseCodeDefaultWhenOtherAmountsAreUnavailable() {
|
||||
BigDecimal amount = ChargeAmountUtils.resolveStartupAmount(null, BigDecimal.ZERO);
|
||||
|
||||
assertEquals(Constants.BALANCE_PAY_MAX_AMOUNT_NEW, amount);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user