mirror of
https://codeup.aliyun.com/67c68d4e484ca2f0a13ac3c1/ydc/jsowell-charger-web.git
synced 2026-05-11 05:20:14 +08:00
Compare commits
15 Commits
master
...
feature-ex
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
98bc86ef43 | ||
|
|
d9d60dd677 | ||
|
|
62bcad3007 | ||
|
|
fc3a941617 | ||
|
|
4a6d569f43 | ||
|
|
392b68b305 | ||
|
|
1c30406323 | ||
|
|
18308f82cd | ||
|
|
216613b67c | ||
|
|
7df9283481 | ||
|
|
0eb86c999d | ||
|
|
ab8930ea4c | ||
|
|
80a0f2ec22 | ||
|
|
f7d541d44a | ||
|
|
19c89db265 |
@@ -8,7 +8,12 @@ import com.jsowell.common.exception.BusinessException;
|
||||
import com.jsowell.common.response.RestApiResponse;
|
||||
import com.jsowell.pile.dto.MerchantOrderReportDTO;
|
||||
import com.jsowell.pile.dto.ParkingCouponRecordQueryDTO;
|
||||
import com.jsowell.pile.dto.business.BusinessOperationAnalysisQueryDTO;
|
||||
import com.jsowell.pile.dto.business.BusinessScaleQueryDTO;
|
||||
import com.jsowell.pile.service.BusinessFinancialService;
|
||||
import com.jsowell.pile.vo.uniapp.business.BusinessGunEfficiencyAnalysisVO;
|
||||
import com.jsowell.pile.vo.uniapp.business.BusinessOperationAnalysisVO;
|
||||
import com.jsowell.pile.vo.uniapp.business.BusinessScaleVO;
|
||||
import com.jsowell.pile.vo.web.MerchantOrderReportVO;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -87,4 +92,60 @@ public class BusinessFinancialController extends BaseController {
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询经营分析
|
||||
*
|
||||
* @param dto 查询参数
|
||||
* @return 经营分析
|
||||
*/
|
||||
@PostMapping("/operationAnalysis")
|
||||
public RestApiResponse<?> getBusinessOperationAnalysis(@RequestBody BusinessOperationAnalysisQueryDTO dto) {
|
||||
logger.info("查询经营分析 params:{}", JSONObject.toJSONString(dto));
|
||||
RestApiResponse<?> response;
|
||||
try {
|
||||
if (dto == null) {
|
||||
throw new BusinessException(ReturnCodeEnum.CODE_PARAM_NOT_NULL_ERROR);
|
||||
}
|
||||
BusinessOperationAnalysisVO result = businessFinancialService.getBusinessOperationAnalysis(dto);
|
||||
response = new RestApiResponse<>(result);
|
||||
logger.info("查询经营分析成功 startTime:{}, endTime:{}, selectedMetricCode:{}",
|
||||
dto.getStartTime(), dto.getEndTime(), dto.getSelectedMetricCode());
|
||||
} catch (BusinessException e) {
|
||||
logger.warn("查询经营分析业务异常 code:{}, message:{}", e.getCode(), e.getMessage(), e);
|
||||
response = new RestApiResponse<>(e.getCode(), e.getMessage());
|
||||
} catch (Exception e) {
|
||||
logger.error("查询经营分析系统异常 params:{}", JSONObject.toJSONString(dto), e);
|
||||
response = new RestApiResponse<>(e);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询经营规模
|
||||
*
|
||||
* @param dto 查询参数
|
||||
* @return 经营规模数据(含指标卡片和曲线图)
|
||||
*/
|
||||
@PostMapping("/businessScale")
|
||||
public RestApiResponse<?> getBusinessScale(@RequestBody BusinessScaleQueryDTO dto) {
|
||||
logger.info("查询经营规模 params:{}", JSONObject.toJSONString(dto));
|
||||
RestApiResponse<?> response;
|
||||
try {
|
||||
if (dto == null) {
|
||||
throw new BusinessException(ReturnCodeEnum.CODE_PARAM_NOT_NULL_ERROR);
|
||||
}
|
||||
BusinessScaleVO result = businessFinancialService.getBusinessScale(dto);
|
||||
response = new RestApiResponse<>(result);
|
||||
logger.info("查询经营规模成功 startTime:{}, endTime:{}, selectedMetricCode:{}",
|
||||
dto.getStartTime(), dto.getEndTime(), dto.getSelectedMetricCode());
|
||||
} catch (BusinessException e) {
|
||||
logger.warn("查询经营规模业务异常 code:{}, message:{}", e.getCode(), e.getMessage(), e);
|
||||
response = new RestApiResponse<>(e.getCode(), e.getMessage());
|
||||
} catch (Exception e) {
|
||||
logger.error("查询经营规模系统异常 params:{}", JSONObject.toJSONString(dto), e);
|
||||
response = new RestApiResponse<>(e);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,8 @@ import com.jsowell.common.util.poi.ExcelUtil;
|
||||
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.OrderInvoiceRecordExportVO;
|
||||
import com.jsowell.pile.vo.web.OrderInvoiceRecordImportVO;
|
||||
import com.jsowell.pile.vo.web.OrderInvoiceRecordVO;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
@@ -21,6 +22,7 @@ import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.List;
|
||||
@@ -37,9 +39,6 @@ public class OrderInvoiceRecordController extends BaseController {
|
||||
@Autowired
|
||||
private OrderInvoiceRecordService orderInvoiceRecordService;
|
||||
|
||||
@Autowired
|
||||
private PileMerchantInfoService pileMerchantInfoService;
|
||||
|
||||
/**
|
||||
* 查询申请开票列表
|
||||
*/
|
||||
@@ -58,12 +57,28 @@ public class OrderInvoiceRecordController extends BaseController {
|
||||
@PreAuthorize("@ss.hasPermi('order:invoice:export')")
|
||||
@Log(title = "申请开票", businessType = BusinessType.EXPORT)
|
||||
@PostMapping("/export")
|
||||
public void export(HttpServletResponse response, OrderInvoiceRecord orderInvoiceRecord) {
|
||||
List<OrderInvoiceRecord> list = orderInvoiceRecordService.selectOrderInvoiceRecordList(orderInvoiceRecord);
|
||||
ExcelUtil<OrderInvoiceRecord> util = new ExcelUtil<OrderInvoiceRecord>(OrderInvoiceRecord.class);
|
||||
public void export(HttpServletResponse response, GetInvoiceInfoDTO dto) {
|
||||
List<OrderInvoiceRecordExportVO> list = orderInvoiceRecordService.getInvoiceExportListWithAuth(dto);
|
||||
ExcelUtil<OrderInvoiceRecordExportVO> util = new ExcelUtil<OrderInvoiceRecordExportVO>(OrderInvoiceRecordExportVO.class);
|
||||
util.exportExcel(response, list, "申请开票数据");
|
||||
}
|
||||
|
||||
/**
|
||||
* 导入开票结果
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('order:invoice:edit')")
|
||||
@Log(title = "申请开票", businessType = BusinessType.IMPORT)
|
||||
@PostMapping("/importData")
|
||||
public AjaxResult importData(MultipartFile file) throws Exception {
|
||||
if (file == null || file.isEmpty()) {
|
||||
return AjaxResult.error("上传文件不能为空");
|
||||
}
|
||||
ExcelUtil<OrderInvoiceRecordImportVO> util = new ExcelUtil<OrderInvoiceRecordImportVO>(OrderInvoiceRecordImportVO.class);
|
||||
List<OrderInvoiceRecordImportVO> importList = util.importExcel(file.getInputStream());
|
||||
String message = orderInvoiceRecordService.importInvoiceImages(importList, getUsername());
|
||||
return AjaxResult.success(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取申请开票详细信息
|
||||
*/
|
||||
|
||||
@@ -38,7 +38,7 @@ spring:
|
||||
master:
|
||||
# url: jdbc:mysql://106.14.94.149:3306/jsowell_pre?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
|
||||
# username: jsowell_pre
|
||||
url: jdbc:mysql://192.168.0.4:3306/jsowell_dev?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
|
||||
url: jdbc:mysql://192.168.0.32:3306/jsowell_dev?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
|
||||
username: jsowell_dev
|
||||
password: 123456
|
||||
# 从库数据源
|
||||
@@ -273,9 +273,9 @@ dubbo:
|
||||
registry:
|
||||
address: nacos://106.14.94.149:8848
|
||||
parameters:
|
||||
namespace: 200784c9-7e8f-4b2b-a44f-1eb52e675491
|
||||
namespace: e328faaf-8516-42d0-817a-7406232b3581
|
||||
username: nacos
|
||||
password: 3rtJPEb0KaUs5NAm
|
||||
password: 79HMu!6nlOiLm^Q[
|
||||
protocol:
|
||||
name: dubbo
|
||||
port: -1
|
||||
|
||||
@@ -48,7 +48,7 @@ spring:
|
||||
# 数据库索引
|
||||
database: 0
|
||||
# 密码
|
||||
password: 123456
|
||||
password: js160829
|
||||
# 连接超时时间
|
||||
timeout: 10s
|
||||
lettuce:
|
||||
|
||||
@@ -8,12 +8,18 @@ import com.huifu.adapay.core.exception.BaseAdaPayException;
|
||||
import com.huifu.adapay.model.Payment;
|
||||
import com.huifu.adapay.model.PaymentReverse;
|
||||
import com.huifu.adapay.model.Refund;
|
||||
import com.jsowell.adapay.common.AdaPayment;
|
||||
import com.jsowell.JsowellApplication;
|
||||
import com.jsowell.adapay.common.DivMember;
|
||||
import com.jsowell.adapay.common.PaymentConfirmInfo;
|
||||
import com.jsowell.adapay.common.RefundInfo;
|
||||
import com.jsowell.adapay.config.AbstractAdapayConfig;
|
||||
import com.jsowell.adapay.dto.PaymentConfirmParam;
|
||||
import com.jsowell.adapay.dto.QueryConfirmReverseDTO;
|
||||
import com.jsowell.adapay.dto.QueryPaymentConfirmDTO;
|
||||
import com.jsowell.adapay.factory.AdapayConfigFactory;
|
||||
import com.jsowell.adapay.operation.PaymentReverseOperation;
|
||||
import com.jsowell.adapay.response.ConfirmReverseResponse;
|
||||
import com.jsowell.adapay.response.PaymentConfirmResponse;
|
||||
import com.jsowell.adapay.response.PaymentReverseResponse;
|
||||
import com.jsowell.adapay.response.QueryPaymentConfirmDetailResponse;
|
||||
@@ -24,10 +30,12 @@ import com.jsowell.api.uniapp.customer.TempController;
|
||||
import com.jsowell.common.constant.CacheConstants;
|
||||
import com.jsowell.common.constant.Constants;
|
||||
import com.jsowell.common.core.redis.RedisCache;
|
||||
import com.jsowell.common.enums.adapay.AdapayStatusEnum;
|
||||
import com.jsowell.common.enums.ykc.ScenarioEnum;
|
||||
import com.jsowell.common.util.AdapayUtil;
|
||||
import com.jsowell.common.util.DateUtils;
|
||||
import com.jsowell.common.util.PageUtils;
|
||||
import com.jsowell.common.util.StringUtils;
|
||||
import com.jsowell.pile.domain.AdapayUnsplitRecord;
|
||||
import com.jsowell.pile.domain.OrderBasicInfo;
|
||||
import com.jsowell.pile.domain.OrderDetail;
|
||||
@@ -60,7 +68,7 @@ import java.util.concurrent.TimeUnit;
|
||||
/**
|
||||
* 专用处理汇付支付相关
|
||||
*/
|
||||
@ActiveProfiles("dev")
|
||||
@ActiveProfiles("sit")
|
||||
@SpringBootTest(classes = JsowellApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||
@RunWith(SpringRunner.class)
|
||||
public class PaymentTestController {
|
||||
@@ -940,4 +948,197 @@ public class PaymentTestController {
|
||||
logger.info("{}", JSON.toJSONString(paymentConfirmInfo));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void queryPaymentAllInfoTest() throws BaseAdaPayException {
|
||||
String paymentId = System.getProperty("paymentId");
|
||||
if (StringUtils.isBlank(paymentId)) {
|
||||
throw new IllegalArgumentException("请通过 -DpaymentId=支付ID 传入 paymentId");
|
||||
}
|
||||
JSONObject result = queryPaymentAllInfo(paymentId);
|
||||
logger.info("paymentId:{}, result:{}", paymentId, JSON.toJSONString(result));
|
||||
}
|
||||
|
||||
public JSONObject queryPaymentAllInfo(String paymentId) throws BaseAdaPayException {
|
||||
if (StringUtils.isBlank(paymentId)) {
|
||||
throw new IllegalArgumentException("paymentId不能为空");
|
||||
}
|
||||
|
||||
PaymentQueryContext context = queryPaymentContext(paymentId);
|
||||
if (context == null) {
|
||||
throw new IllegalArgumentException("未匹配到支付单,paymentId:" + paymentId);
|
||||
}
|
||||
|
||||
QueryPaymentConfirmDTO queryPaymentConfirmDTO = new QueryPaymentConfirmDTO();
|
||||
queryPaymentConfirmDTO.setWechatAppId(context.getWechatAppId());
|
||||
queryPaymentConfirmDTO.setPaymentId(paymentId);
|
||||
QueryPaymentConfirmDetailResponse confirmResponse = adapayService.queryPaymentConfirmList(queryPaymentConfirmDTO);
|
||||
List<PaymentConfirmInfo> paymentConfirms = confirmResponse == null || CollectionUtils.isEmpty(confirmResponse.getPaymentConfirms())
|
||||
? Lists.newArrayList()
|
||||
: paymentResponseToList(confirmResponse.getPaymentConfirms());
|
||||
|
||||
List<PaymentReverseResponse> paymentReverses = adapayService.queryPaymentReverse(paymentId, context.getWechatAppId());
|
||||
if (paymentReverses == null) {
|
||||
paymentReverses = Lists.newArrayList();
|
||||
}
|
||||
|
||||
List<RefundInfo> refunds = adapayService.queryPaymentRefund(paymentId, context.getWechatAppId());
|
||||
if (refunds == null) {
|
||||
refunds = Lists.newArrayList();
|
||||
}
|
||||
|
||||
AdaPayment payment = context.getPayment();
|
||||
BigDecimal payAmount = safeAmount(payment.getPay_amt());
|
||||
BigDecimal apiConfirmedAmount = safeAmount(payment.getConfirmed_amt());
|
||||
BigDecimal apiRefundedAmount = safeAmount(payment.getRefunded_amt());
|
||||
BigDecimal apiReservedAmount = safeAmount(payment.getReserved_amt());
|
||||
|
||||
BigDecimal successConfirmedAmount = sumConfirmedAmount(paymentConfirms, context.getWechatAppId());
|
||||
BigDecimal successRefundAmount = sumRefundAmount(refunds);
|
||||
BigDecimal successReverseAmount = sumReverseAmount(paymentReverses);
|
||||
|
||||
BigDecimal alreadySplitAmount = apiConfirmedAmount.compareTo(BigDecimal.ZERO) > 0
|
||||
? apiConfirmedAmount
|
||||
: successConfirmedAmount;
|
||||
BigDecimal alreadyRefundAmount;
|
||||
if (StringUtils.equals("delay", payment.getPay_mode())) {
|
||||
alreadyRefundAmount = successReverseAmount.compareTo(BigDecimal.ZERO) > 0
|
||||
? successReverseAmount
|
||||
: apiReservedAmount;
|
||||
} else {
|
||||
alreadyRefundAmount = successRefundAmount.compareTo(BigDecimal.ZERO) > 0
|
||||
? successRefundAmount
|
||||
: apiRefundedAmount;
|
||||
}
|
||||
BigDecimal unsplitAmount = payAmount.subtract(alreadySplitAmount).subtract(alreadyRefundAmount);
|
||||
if (unsplitAmount.compareTo(BigDecimal.ZERO) < 0) {
|
||||
unsplitAmount = BigDecimal.ZERO;
|
||||
}
|
||||
|
||||
JSONObject summary = new JSONObject();
|
||||
summary.put("payMode", payment.getPay_mode());
|
||||
summary.put("payAmount", AdapayUtil.formatAmount(payAmount));
|
||||
summary.put("apiConfirmedAmount", AdapayUtil.formatAmount(apiConfirmedAmount));
|
||||
summary.put("apiRefundedAmount", AdapayUtil.formatAmount(apiRefundedAmount));
|
||||
summary.put("apiReservedAmount", AdapayUtil.formatAmount(apiReservedAmount));
|
||||
summary.put("successConfirmedAmount", AdapayUtil.formatAmount(successConfirmedAmount));
|
||||
summary.put("successRefundAmount", AdapayUtil.formatAmount(successRefundAmount));
|
||||
summary.put("successReverseAmount", AdapayUtil.formatAmount(successReverseAmount));
|
||||
summary.put("alreadySplitAmount", AdapayUtil.formatAmount(alreadySplitAmount));
|
||||
summary.put("alreadyRefundAmount", AdapayUtil.formatAmount(alreadyRefundAmount));
|
||||
summary.put("unsplitAmount", AdapayUtil.formatAmount(unsplitAmount));
|
||||
|
||||
JSONObject result = new JSONObject();
|
||||
result.put("paymentId", paymentId);
|
||||
result.put("wechatAppId", context.getWechatAppId());
|
||||
result.put("adapayAppId", context.getAdapayAppId());
|
||||
result.put("payment", payment);
|
||||
result.put("paymentRaw", JSON.parseObject(JSON.toJSONString(payment)));
|
||||
result.put("paymentConfirms", paymentConfirms);
|
||||
result.put("paymentReverses", paymentReverses);
|
||||
result.put("refunds", refunds);
|
||||
result.put("summary", summary);
|
||||
return result;
|
||||
}
|
||||
|
||||
private PaymentQueryContext queryPaymentContext(String paymentId) throws BaseAdaPayException {
|
||||
List<AbstractAdapayConfig> configList = AdapayConfigFactory.getAllConfig();
|
||||
if (CollectionUtils.isEmpty(configList)) {
|
||||
throw new IllegalStateException("未加载到汇付商户配置");
|
||||
}
|
||||
|
||||
for (AbstractAdapayConfig config : configList) {
|
||||
AdaPayment payment = adapayService.queryPaymentDetailWithFallback(paymentId, config.getWechatAppId());
|
||||
if (payment != null) {
|
||||
return new PaymentQueryContext(config.getWechatAppId(), config.getAdapayAppId(), payment);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private BigDecimal sumConfirmedAmount(List<PaymentConfirmInfo> paymentConfirms, String wechatAppId) throws BaseAdaPayException {
|
||||
BigDecimal totalAmount = BigDecimal.ZERO;
|
||||
if (CollectionUtils.isEmpty(paymentConfirms)) {
|
||||
return totalAmount;
|
||||
}
|
||||
for (PaymentConfirmInfo paymentConfirm : paymentConfirms) {
|
||||
if (isConfirmReversed(paymentConfirm, wechatAppId)) {
|
||||
continue;
|
||||
}
|
||||
totalAmount = totalAmount.add(safeAmount(paymentConfirm.getConfirmedAmt()));
|
||||
}
|
||||
return totalAmount;
|
||||
}
|
||||
|
||||
private boolean isConfirmReversed(PaymentConfirmInfo paymentConfirm, String wechatAppId) throws BaseAdaPayException {
|
||||
QueryConfirmReverseDTO queryConfirmReverseDTO = QueryConfirmReverseDTO.builder()
|
||||
.wechatAppId(wechatAppId)
|
||||
.paymentConfirmId(paymentConfirm.getId())
|
||||
.build();
|
||||
ConfirmReverseResponse confirmReverseResponse = adapayService.queryConfirmReverse(queryConfirmReverseDTO);
|
||||
return confirmReverseResponse != null && confirmReverseResponse.isSuccess();
|
||||
}
|
||||
|
||||
private BigDecimal sumRefundAmount(List<RefundInfo> refunds) {
|
||||
BigDecimal totalAmount = BigDecimal.ZERO;
|
||||
if (CollectionUtils.isEmpty(refunds)) {
|
||||
return totalAmount;
|
||||
}
|
||||
for (RefundInfo refund : refunds) {
|
||||
if (!StringUtils.equals("S", refund.getTrans_status())) {
|
||||
continue;
|
||||
}
|
||||
totalAmount = totalAmount.add(safeAmount(refund.getRefund_amt()));
|
||||
}
|
||||
return totalAmount;
|
||||
}
|
||||
|
||||
private BigDecimal sumReverseAmount(List<PaymentReverseResponse> paymentReverses) {
|
||||
BigDecimal totalAmount = BigDecimal.ZERO;
|
||||
if (CollectionUtils.isEmpty(paymentReverses)) {
|
||||
return totalAmount;
|
||||
}
|
||||
for (PaymentReverseResponse paymentReverseResponse : paymentReverses) {
|
||||
if (!StringUtils.equals(AdapayStatusEnum.SUCCEEDED.getValue(), paymentReverseResponse.getStatus())) {
|
||||
continue;
|
||||
}
|
||||
totalAmount = totalAmount.add(safeAmount(paymentReverseResponse.getReverse_amt()));
|
||||
}
|
||||
return totalAmount;
|
||||
}
|
||||
|
||||
private BigDecimal safeAmount(String amount) {
|
||||
if (StringUtils.isBlank(amount)) {
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
return new BigDecimal(amount);
|
||||
}
|
||||
|
||||
private List<PaymentConfirmInfo> paymentResponseToList(List<PaymentConfirmInfo> paymentConfirms) {
|
||||
return Lists.newArrayList(paymentConfirms);
|
||||
}
|
||||
|
||||
private static class PaymentQueryContext {
|
||||
private final String wechatAppId;
|
||||
private final String adapayAppId;
|
||||
private final AdaPayment payment;
|
||||
|
||||
private PaymentQueryContext(String wechatAppId, String adapayAppId, AdaPayment payment) {
|
||||
this.wechatAppId = wechatAppId;
|
||||
this.adapayAppId = adapayAppId;
|
||||
this.payment = payment;
|
||||
}
|
||||
|
||||
private String getWechatAppId() {
|
||||
return wechatAppId;
|
||||
}
|
||||
|
||||
private String getAdapayAppId() {
|
||||
return adapayAppId;
|
||||
}
|
||||
|
||||
private AdaPayment getPayment() {
|
||||
return payment;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
package com.jsowell.common.util;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
|
||||
/**
|
||||
* BigDecimal工具类
|
||||
*
|
||||
* @author Auto
|
||||
* @Date 2026/4/1
|
||||
*/
|
||||
public class BigDecimalUtils {
|
||||
|
||||
/**
|
||||
* 工具类不允许实例化
|
||||
*/
|
||||
private BigDecimalUtils() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 空值转零
|
||||
*
|
||||
* @param value 原始值
|
||||
* @return 非空数值
|
||||
*/
|
||||
public static BigDecimal nullToZero(BigDecimal value) {
|
||||
return value == null ? BigDecimal.ZERO : value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 安全除法
|
||||
*
|
||||
* @param dividend 被除数
|
||||
* @param divisor 除数
|
||||
* @param scale 保留小数位
|
||||
* @return 计算结果
|
||||
*/
|
||||
public static BigDecimal safeDivide(BigDecimal dividend, BigDecimal divisor, int scale) {
|
||||
BigDecimal safeDividend = nullToZero(dividend);
|
||||
BigDecimal safeDivisor = nullToZero(divisor);
|
||||
if (safeDivisor.compareTo(BigDecimal.ZERO) == 0) {
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
return safeDividend.divide(safeDivisor, scale, RoundingMode.HALF_UP);
|
||||
}
|
||||
|
||||
/**
|
||||
* 标准化数值显示
|
||||
*
|
||||
* @param value 原始值
|
||||
* @return 标准化后的数值
|
||||
*/
|
||||
public static BigDecimal normalize(BigDecimal value) {
|
||||
BigDecimal safeValue = nullToZero(value);
|
||||
if (safeValue.compareTo(BigDecimal.ZERO) == 0) {
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
return safeValue.stripTrailingZeros();
|
||||
}
|
||||
|
||||
/**
|
||||
* 将字符串安全转换为BigDecimal
|
||||
*
|
||||
* @param value 原始字符串
|
||||
* @return BigDecimal值
|
||||
*/
|
||||
public static BigDecimal parseOrZero(String value) {
|
||||
if (StringUtils.isBlank(value)) {
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
try {
|
||||
return new BigDecimal(value.trim());
|
||||
} catch (Exception e) {
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算环比百分比
|
||||
*
|
||||
* @param currentValue 当前周期值
|
||||
* @param previousValue 上周期值
|
||||
* @return 环比字符串
|
||||
*/
|
||||
public static String calculateRate(BigDecimal currentValue, BigDecimal previousValue) {
|
||||
BigDecimal safeCurrentValue = nullToZero(currentValue);
|
||||
BigDecimal safePreviousValue = nullToZero(previousValue);
|
||||
if (safePreviousValue.compareTo(BigDecimal.ZERO) == 0) {
|
||||
return "0%";
|
||||
}
|
||||
BigDecimal rate = safeCurrentValue.subtract(safePreviousValue)
|
||||
.divide(safePreviousValue, 4, RoundingMode.HALF_UP)
|
||||
.multiply(new BigDecimal("100"));
|
||||
BigDecimal normalizedRate = normalize(rate);
|
||||
if (normalizedRate.compareTo(BigDecimal.ZERO) > 0) {
|
||||
return "+" + normalizedRate.toPlainString() + "%";
|
||||
}
|
||||
return normalizedRate.toPlainString() + "%";
|
||||
}
|
||||
}
|
||||
@@ -1345,7 +1345,74 @@ public class AdapayService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询支付退款对象
|
||||
* 按汇付文档查询支付对象详情
|
||||
* Payment.query(paymentId, merchantKey)
|
||||
*/
|
||||
public AdaPayment queryPaymentDetail(String paymentId, String wechatAppId) throws BaseAdaPayException {
|
||||
AbstractAdapayConfig config = AdapayConfigFactory.getConfig(wechatAppId);
|
||||
if (config == null) {
|
||||
throw new BusinessException(ReturnCodeEnum.CODE_ADAPAY_CONFIG_IS_NULL_ERROR);
|
||||
}
|
||||
Map<String, Object> response = Payment.query(paymentId, config.getWechatAppId());
|
||||
if (response == null || response.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
String resultPaymentId = (String) response.get("id");
|
||||
String errorCode = (String) response.get("error_code");
|
||||
if (!StringUtils.equals(paymentId, resultPaymentId) || StringUtils.isNotBlank(errorCode)) {
|
||||
return null;
|
||||
}
|
||||
return JSON.parseObject(JSON.toJSONString(response), AdaPayment.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 按汇付文档通过 payment_id 查询支付对象列表
|
||||
* 作为 Payment.query 的补充,用于查询 3 天前的交易
|
||||
*/
|
||||
public List<AdaPayment> queryPaymentListByPaymentId(String paymentId, String wechatAppId) throws BaseAdaPayException {
|
||||
List<AdaPayment> resultList = Lists.newArrayList();
|
||||
AbstractAdapayConfig config = AdapayConfigFactory.getConfig(wechatAppId);
|
||||
if (config == null) {
|
||||
throw new BusinessException(ReturnCodeEnum.CODE_ADAPAY_CONFIG_IS_NULL_ERROR);
|
||||
}
|
||||
Map<String, Object> queryListParam = Maps.newHashMap();
|
||||
queryListParam.put("app_id", config.getAdapayAppId());
|
||||
queryListParam.put("payment_id", paymentId);
|
||||
queryListParam.put("page_index", 1);
|
||||
queryListParam.put("page_size", 1);
|
||||
Map<String, Object> paymentListResult = Payment.queryList(queryListParam, config.getWechatAppId());
|
||||
if (paymentListResult == null || paymentListResult.isEmpty()) {
|
||||
return resultList;
|
||||
}
|
||||
|
||||
JSONObject jsonObject = JSON.parseObject(JSON.toJSONString(paymentListResult));
|
||||
List<AdaPayment> payments = jsonObject.getList("payments", AdaPayment.class, JSONReader.Feature.FieldBased);
|
||||
if (CollectionUtils.isNotEmpty(payments)) {
|
||||
resultList.addAll(payments);
|
||||
}
|
||||
return resultList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询支付对象详情
|
||||
* 先查 Payment.query,未命中时再按 payment_id 查询列表
|
||||
*/
|
||||
public AdaPayment queryPaymentDetailWithFallback(String paymentId, String wechatAppId) throws BaseAdaPayException {
|
||||
AdaPayment payment = queryPaymentDetail(paymentId, wechatAppId);
|
||||
if (payment != null) {
|
||||
return payment;
|
||||
}
|
||||
|
||||
List<AdaPayment> payments = queryPaymentListByPaymentId(paymentId, wechatAppId);
|
||||
if (CollectionUtils.isEmpty(payments)) {
|
||||
return null;
|
||||
}
|
||||
return payments.get(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 按汇付文档通过 payment_id 查询退款对象
|
||||
* Refund.query({payment_id}, merchantKey)
|
||||
*/
|
||||
public List<RefundInfo> queryPaymentRefund(String paymentId, String wechatAppId) throws BaseAdaPayException {
|
||||
AbstractAdapayConfig config = AdapayConfigFactory.getConfig(wechatAppId);
|
||||
@@ -1354,7 +1421,6 @@ public class AdapayService {
|
||||
}
|
||||
Map<String, Object> refundParams = Maps.newHashMap();
|
||||
refundParams.put("payment_id", paymentId);
|
||||
refundParams.put("app_id", config.getAdapayAppId());
|
||||
Map<String, Object> response = Refund.query(refundParams, config.getWechatAppId());
|
||||
PaymentRefundResponse paymentRefundResponse = JSON.parseObject(JSON.toJSONString(response), PaymentRefundResponse.class);
|
||||
List<RefundInfo> refunds = paymentRefundResponse.getRefunds();
|
||||
|
||||
@@ -45,6 +45,11 @@ public class OrderInvoiceRecord extends BaseEntity {
|
||||
|
||||
private String email;
|
||||
|
||||
/**
|
||||
* 发票图片url
|
||||
*/
|
||||
private String invoiceUrl;
|
||||
|
||||
/**
|
||||
* 申请订单编号(逗号分割)
|
||||
*/
|
||||
@@ -154,6 +159,7 @@ public class OrderInvoiceRecord extends BaseEntity {
|
||||
.append("totalAmount", getTotalAmount())
|
||||
.append("totalServiceAmount", getTotalServiceAmount())
|
||||
.append("totalElecAmount", getTotalElecAmount())
|
||||
.append("invoiceUrl", getInvoiceUrl())
|
||||
.append("createBy", getCreateBy())
|
||||
.append("createTime", getCreateTime())
|
||||
.append("updateBy", getUpdateBy())
|
||||
|
||||
@@ -15,6 +15,11 @@ public class GetInvoiceInfoDTO {
|
||||
private Integer pageNum;
|
||||
private Integer pageSize;
|
||||
|
||||
/**
|
||||
* 勾选导出的申请开票记录id
|
||||
*/
|
||||
private Integer[] ids;
|
||||
|
||||
/**
|
||||
* 会员id
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.jsowell.pile.dto.business;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 经营状态/经营诊断查询DTO
|
||||
*
|
||||
* @author Auto
|
||||
* @Date 2026/4/1
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Builder
|
||||
public class BusinessOperationAnalysisQueryDTO {
|
||||
|
||||
/**
|
||||
* 开始时间,格式:yyyy-MM-dd
|
||||
*/
|
||||
private String startTime;
|
||||
|
||||
/**
|
||||
* 结束时间,格式:yyyy-MM-dd
|
||||
*/
|
||||
private String endTime;
|
||||
|
||||
/**
|
||||
* 站点id列表
|
||||
*/
|
||||
private List<String> stationIdList;
|
||||
|
||||
/**
|
||||
* 当前选中的指标编码
|
||||
*/
|
||||
private String selectedMetricCode;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.jsowell.pile.dto.business;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 经营分析日期区间DTO
|
||||
*
|
||||
* @author Auto
|
||||
* @Date 2026/4/1
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Builder
|
||||
public class BusinessOperationDateRangeDTO {
|
||||
|
||||
/**
|
||||
* 当前周期开始时间
|
||||
*/
|
||||
private String currentStart;
|
||||
|
||||
/**
|
||||
* 当前周期结束时间
|
||||
*/
|
||||
private String currentEnd;
|
||||
|
||||
/**
|
||||
* 上周期开始时间
|
||||
*/
|
||||
private String previousStart;
|
||||
|
||||
/**
|
||||
* 上周期结束时间
|
||||
*/
|
||||
private String previousEnd;
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package com.jsowell.pile.dto.business;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 经营分析汇总DTO
|
||||
*
|
||||
* @author Auto
|
||||
* @Date 2026/4/1
|
||||
*/
|
||||
@Data
|
||||
public class BusinessOperationSummaryDTO {
|
||||
|
||||
/**
|
||||
* 订单总额
|
||||
*/
|
||||
private BigDecimal orderAmount = BigDecimal.ZERO;
|
||||
|
||||
/**
|
||||
* 服务费总额
|
||||
*/
|
||||
private BigDecimal serviceAmount = BigDecimal.ZERO;
|
||||
|
||||
/**
|
||||
* 总用电度数
|
||||
*/
|
||||
private BigDecimal useElectricity = BigDecimal.ZERO;
|
||||
|
||||
/**
|
||||
* 订单量
|
||||
*/
|
||||
private BigDecimal orderCount = BigDecimal.ZERO;
|
||||
|
||||
/**
|
||||
* 总充电时长
|
||||
*/
|
||||
private BigDecimal chargeTime = BigDecimal.ZERO;
|
||||
|
||||
/**
|
||||
* 单均服务费
|
||||
*/
|
||||
private BigDecimal avgServiceFee = BigDecimal.ZERO;
|
||||
|
||||
/**
|
||||
* 单均度数
|
||||
*/
|
||||
private BigDecimal avgElectricity = BigDecimal.ZERO;
|
||||
|
||||
/**
|
||||
* 单均充电时长
|
||||
*/
|
||||
private BigDecimal avgChargeTime = BigDecimal.ZERO;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.jsowell.pile.dto.business;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 经营规模查询DTO
|
||||
*
|
||||
* @author zhangziao
|
||||
* @date 2026/4/10
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Builder
|
||||
public class BusinessScaleQueryDTO {
|
||||
|
||||
/**
|
||||
* 开始时间,格式:yyyy-MM-dd
|
||||
*/
|
||||
private String startTime;
|
||||
|
||||
/**
|
||||
* 结束时间,格式:yyyy-MM-dd
|
||||
*/
|
||||
private String endTime;
|
||||
|
||||
/**
|
||||
* 站点id列表
|
||||
*/
|
||||
private List<String> stationIdList;
|
||||
|
||||
/**
|
||||
* 当前选中的指标编码,用于查询对应的曲线图数据
|
||||
* ORDER_AMOUNT-订单总额, ORDER_COUNT-充电订单量,
|
||||
* SERVICE_AMOUNT-充电服务费, USE_ELECTRICITY-充电电量
|
||||
*/
|
||||
private String selectedMetricCode;
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
package com.jsowell.pile.mapper;
|
||||
|
||||
import com.jsowell.pile.domain.OrderInvoiceRecord;
|
||||
import com.jsowell.pile.dto.QueryInvoiceRecordDTO;
|
||||
import com.jsowell.pile.dto.GetInvoiceInfoDTO;
|
||||
import com.jsowell.pile.dto.QueryInvoiceRecordDTO;
|
||||
import com.jsowell.pile.vo.web.OrderInvoiceRecordExportVO;
|
||||
import com.jsowell.pile.vo.web.OrderInvoiceRecordVO;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.springframework.stereotype.Repository;
|
||||
@@ -40,6 +41,14 @@ public interface OrderInvoiceRecordMapper {
|
||||
*/
|
||||
List<OrderInvoiceRecordVO> getInvoiceListWithAuth(@Param("dto") GetInvoiceInfoDTO dto);
|
||||
|
||||
/**
|
||||
* 查询申请开票导出列表(带权限校验)
|
||||
*
|
||||
* @param dto 查询条件
|
||||
* @return 导出集合
|
||||
*/
|
||||
List<OrderInvoiceRecordExportVO> getInvoiceExportListWithAuth(@Param("dto") GetInvoiceInfoDTO dto);
|
||||
|
||||
/**
|
||||
* 新增申请开票
|
||||
*
|
||||
|
||||
@@ -3,6 +3,11 @@ package com.jsowell.pile.service;
|
||||
import com.jsowell.common.core.page.PageResponse;
|
||||
import com.jsowell.pile.dto.MerchantOrderReportDTO;
|
||||
import com.jsowell.pile.dto.ParkingCouponRecordQueryDTO;
|
||||
import com.jsowell.pile.dto.business.BusinessOperationAnalysisQueryDTO;
|
||||
import com.jsowell.pile.dto.business.BusinessScaleQueryDTO;
|
||||
import com.jsowell.pile.vo.uniapp.business.BusinessGunEfficiencyAnalysisVO;
|
||||
import com.jsowell.pile.vo.uniapp.business.BusinessOperationAnalysisVO;
|
||||
import com.jsowell.pile.vo.uniapp.business.BusinessScaleVO;
|
||||
import com.jsowell.pile.vo.web.MerchantOrderReportVO;
|
||||
import com.jsowell.pile.vo.web.ParkingCouponRecordVO;
|
||||
|
||||
@@ -16,4 +21,28 @@ public interface BusinessFinancialService {
|
||||
* @return 分页结果
|
||||
*/
|
||||
PageResponse queryParkingCouponRecords(ParkingCouponRecordQueryDTO dto);
|
||||
|
||||
/**
|
||||
* 查询经营分析
|
||||
*
|
||||
* @param dto 查询条件
|
||||
* @return 经营分析
|
||||
*/
|
||||
BusinessOperationAnalysisVO getBusinessOperationAnalysis(BusinessOperationAnalysisQueryDTO dto);
|
||||
|
||||
/**
|
||||
* 查询枪均效率分析
|
||||
*
|
||||
* @param dto 查询条件
|
||||
* @return 枪均效率分析
|
||||
*/
|
||||
BusinessGunEfficiencyAnalysisVO getBusinessGunEfficiencyAnalysis(BusinessOperationAnalysisQueryDTO dto);
|
||||
|
||||
/**
|
||||
* 查询经营规模
|
||||
*
|
||||
* @param dto 查询条件
|
||||
* @return 经营规模数据(含指标卡片和曲线图)
|
||||
*/
|
||||
BusinessScaleVO getBusinessScale(BusinessScaleQueryDTO dto);
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ import com.jsowell.pile.domain.OrderInvoiceRecord;
|
||||
import com.jsowell.pile.dto.GetInvoiceInfoDTO;
|
||||
import com.jsowell.pile.dto.QueryInvoiceRecordDTO;
|
||||
import com.jsowell.pile.vo.web.InvoiceRecordVO;
|
||||
import com.jsowell.pile.vo.web.OrderInvoiceRecordExportVO;
|
||||
import com.jsowell.pile.vo.web.OrderInvoiceRecordImportVO;
|
||||
import com.jsowell.pile.vo.web.OrderInvoiceRecordVO;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
@@ -41,6 +43,23 @@ public interface OrderInvoiceRecordService {
|
||||
*/
|
||||
List<OrderInvoiceRecordVO> getInvoiceListWithAuth(GetInvoiceInfoDTO dto);
|
||||
|
||||
/**
|
||||
* 查询申请开票导出列表(带权限校验)
|
||||
*
|
||||
* @param dto 查询条件
|
||||
* @return 导出列表
|
||||
*/
|
||||
List<OrderInvoiceRecordExportVO> getInvoiceExportListWithAuth(GetInvoiceInfoDTO dto);
|
||||
|
||||
/**
|
||||
* 导入发票图片并更新申请开票记录
|
||||
*
|
||||
* @param importList 导入数据
|
||||
* @param operName 操作人
|
||||
* @return 导入结果
|
||||
*/
|
||||
String importInvoiceImages(List<OrderInvoiceRecordImportVO> importList, String operName);
|
||||
|
||||
List<OrderInvoiceRecord> selectInvoiceRecordList(QueryInvoiceRecordDTO memberId);
|
||||
|
||||
List<OrderInvoiceRecordVO> selectInvoiceVOList(QueryInvoiceRecordDTO memberId);
|
||||
|
||||
@@ -5,24 +5,54 @@ import com.github.pagehelper.PageInfo;
|
||||
import com.huifu.adapay.core.exception.BaseAdaPayException;
|
||||
import com.jsowell.adapay.service.AdapayService;
|
||||
import com.jsowell.adapay.vo.AdapayAccountBalanceVO;
|
||||
import com.jsowell.common.util.BigDecimalUtils;
|
||||
import com.jsowell.common.core.domain.vo.AuthorizedDeptVO;
|
||||
import com.jsowell.common.core.page.PageResponse;
|
||||
import com.jsowell.common.enums.ykc.ReturnCodeEnum;
|
||||
import com.jsowell.common.exception.BusinessException;
|
||||
import com.jsowell.pile.dto.MerchantOrderReportDTO;
|
||||
import com.jsowell.pile.dto.ParkingCouponRecordQueryDTO;
|
||||
import com.jsowell.pile.dto.business.BusinessOperationAnalysisQueryDTO;
|
||||
import com.jsowell.pile.dto.business.BusinessOperationDateRangeDTO;
|
||||
import com.jsowell.pile.dto.business.BusinessOperationSummaryDTO;
|
||||
import com.jsowell.pile.dto.business.BusinessScaleQueryDTO;
|
||||
import com.jsowell.pile.domain.PileConnectorInfo;
|
||||
import com.jsowell.pile.domain.SettleOrderReport;
|
||||
import com.jsowell.pile.service.BusinessFinancialService;
|
||||
import com.jsowell.pile.service.CarCouponRecordService;
|
||||
import com.jsowell.pile.service.ClearingWithdrawInfoService;
|
||||
import com.jsowell.pile.service.PileConnectorInfoService;
|
||||
import com.jsowell.pile.service.PileStationInfoService;
|
||||
import com.jsowell.pile.service.SettleOrderReportService;
|
||||
import com.jsowell.pile.util.UserUtils;
|
||||
import com.jsowell.pile.vo.uniapp.business.BusinessGunEfficiencyAnalysisVO;
|
||||
import com.jsowell.pile.vo.uniapp.business.BusinessOperationAnalysisVO;
|
||||
import com.jsowell.pile.vo.uniapp.business.BusinessOperationDiagnosisItemVO;
|
||||
import com.jsowell.pile.vo.uniapp.business.BusinessOperationMetricVO;
|
||||
import com.jsowell.pile.vo.uniapp.business.BusinessScaleChartVO;
|
||||
import com.jsowell.pile.vo.uniapp.business.BusinessScaleMetricVO;
|
||||
import com.jsowell.pile.vo.uniapp.business.BusinessScaleVO;
|
||||
import com.jsowell.pile.vo.web.MerchantOrderReportVO;
|
||||
import com.jsowell.pile.vo.web.ParkingCouponRecordVO;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 运营端小程序财务相关Service
|
||||
@@ -31,6 +61,30 @@ import java.util.List;
|
||||
@Service
|
||||
public class BusinessFinancialServiceImpl implements BusinessFinancialService {
|
||||
|
||||
private static final String METRIC_ORDER_AMOUNT = "ORDER_AMOUNT";
|
||||
private static final String METRIC_AVG_SERVICE_FEE = "AVG_SERVICE_FEE";
|
||||
private static final String METRIC_AVG_ELECTRICITY = "AVG_ELECTRICITY";
|
||||
|
||||
private static final String FACTOR_ORDER_COUNT = "ORDER_COUNT";
|
||||
private static final String FACTOR_AVG_ELECTRICITY = "AVG_ELECTRICITY";
|
||||
private static final String FACTOR_AVG_CHARGE_TIME = "AVG_CHARGE_TIME";
|
||||
|
||||
private static final String SCALE_METRIC_ORDER_AMOUNT = "ORDER_AMOUNT";
|
||||
private static final String SCALE_METRIC_ORDER_COUNT = "ORDER_COUNT";
|
||||
private static final String SCALE_METRIC_SERVICE_AMOUNT = "SERVICE_AMOUNT";
|
||||
private static final String SCALE_METRIC_USE_ELECTRICITY = "USE_ELECTRICITY";
|
||||
|
||||
/**
|
||||
* 财务查询通用线程池(支持多方法并行查询)
|
||||
* 核心线程数4:覆盖经营规模(2路)、我的钱包(3路)、经营分析(2路)、枪均效率(2路)等场景
|
||||
*/
|
||||
private static final ExecutorService FINANCIAL_THREAD_POOL = Executors.newFixedThreadPool(
|
||||
4, r -> {
|
||||
Thread t = new Thread(r, "business-financial-query");
|
||||
t.setDaemon(true);
|
||||
return t;
|
||||
});
|
||||
|
||||
@Autowired
|
||||
private SettleOrderReportService settleOrderReportService;
|
||||
|
||||
@@ -46,49 +100,63 @@ public class BusinessFinancialServiceImpl implements BusinessFinancialService {
|
||||
@Autowired
|
||||
private PileStationInfoService pileStationInfoService;
|
||||
|
||||
@Autowired
|
||||
private PileConnectorInfoService pileConnectorInfoService;
|
||||
|
||||
/**
|
||||
* 我的钱包查询
|
||||
* 优化:订单报表DB、汇付余额HTTP、提现金额DB 三个独立数据源并行查询
|
||||
* @param dto
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public MerchantOrderReportVO getMyWallet(MerchantOrderReportDTO dto) {
|
||||
// 查询运营商订单报表
|
||||
MerchantOrderReportVO result = settleOrderReportService.getMerchantOrderReportV2(dto);
|
||||
String merchantId = dto.getMerchantId();
|
||||
|
||||
// 查询账户余额
|
||||
AdapayAccountBalanceVO accountBalanceVO = new AdapayAccountBalanceVO();
|
||||
// 并行查询三个独立数据源
|
||||
CompletableFuture<MerchantOrderReportVO> reportFuture = CompletableFuture.supplyAsync(
|
||||
() -> settleOrderReportService.getMerchantOrderReportV2(dto), FINANCIAL_THREAD_POOL);
|
||||
CompletableFuture<AdapayAccountBalanceVO> balanceFuture = CompletableFuture.supplyAsync(() -> {
|
||||
try {
|
||||
return adapayService.queryAdapayAccountBalance(merchantId);
|
||||
} catch (BaseAdaPayException e) {
|
||||
log.error("查询汇付账户余额异常 merchantId:{}", merchantId, e);
|
||||
return null;
|
||||
}
|
||||
}, FINANCIAL_THREAD_POOL);
|
||||
CompletableFuture<BigDecimal> withdrawFuture = CompletableFuture.supplyAsync(() -> {
|
||||
try {
|
||||
BigDecimal withdraw = clearingWithdrawInfoService.queryTotalWithdraw(merchantId);
|
||||
return withdraw != null ? withdraw : BigDecimal.ZERO;
|
||||
} catch (Exception e) {
|
||||
log.error("查询累计提现金额异常 merchantId:{}", merchantId, e);
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
}, FINANCIAL_THREAD_POOL);
|
||||
|
||||
// 等待所有查询完成
|
||||
MerchantOrderReportVO result = reportFuture.join();
|
||||
AdapayAccountBalanceVO accountBalanceVO = balanceFuture.join();
|
||||
BigDecimal totalWithdraw = withdrawFuture.join();
|
||||
|
||||
// 设置账户余额
|
||||
BigDecimal acctBalance = BigDecimal.ZERO;
|
||||
BigDecimal pendingAmount = BigDecimal.ZERO;
|
||||
try {
|
||||
accountBalanceVO = adapayService.queryAdapayAccountBalance(dto.getMerchantId());
|
||||
if (accountBalanceVO == null) {
|
||||
log.error("我的钱包查询异常 查询出accountBalanceVO 为null");
|
||||
return result;
|
||||
}
|
||||
BigDecimal canWithdrawAmount = null;
|
||||
if (accountBalanceVO != null) {
|
||||
if (accountBalanceVO.getAcctBalance() != null) {
|
||||
acctBalance = accountBalanceVO.getAcctBalance();
|
||||
}
|
||||
if (accountBalanceVO.getPendingAmount() != null) {
|
||||
pendingAmount = accountBalanceVO.getPendingAmount();
|
||||
}
|
||||
} catch (BaseAdaPayException e) {
|
||||
log.error("查询汇付账户余额异常 merchantId:{}", dto.getMerchantId(), e);
|
||||
canWithdrawAmount = accountBalanceVO.getLastAvlBalance();
|
||||
} else {
|
||||
log.error("我的钱包查询异常 查询出accountBalanceVO 为null");
|
||||
}
|
||||
result.getMerchantOrderReport().setAcctBalance(acctBalance);
|
||||
result.getMerchantOrderReport().setPendingAmount(pendingAmount);
|
||||
result.getMerchantOrderReport().setCanWithdrawAmount(accountBalanceVO.getLastAvlBalance());
|
||||
|
||||
// 查询累计提现金额
|
||||
BigDecimal totalWithdraw = BigDecimal.ZERO;
|
||||
try {
|
||||
BigDecimal withdraw = clearingWithdrawInfoService.queryTotalWithdraw(dto.getMerchantId());
|
||||
if (withdraw != null) {
|
||||
totalWithdraw = withdraw;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("查询累计提现金额异常 merchantId:{}", dto.getMerchantId(), e);
|
||||
}
|
||||
result.getMerchantOrderReport().setCanWithdrawAmount(canWithdrawAmount);
|
||||
result.getMerchantOrderReport().setTotalWithdraw(totalWithdraw);
|
||||
|
||||
return result;
|
||||
@@ -148,4 +216,573 @@ public class BusinessFinancialServiceImpl implements BusinessFinancialService {
|
||||
.total(pageInfo.getTotal())
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询经营分析
|
||||
* 优化:当前周期和上周期汇总并行查询
|
||||
*
|
||||
* @param dto 查询条件
|
||||
* @return 经营分析
|
||||
*/
|
||||
@Override
|
||||
public BusinessOperationAnalysisVO getBusinessOperationAnalysis(BusinessOperationAnalysisQueryDTO dto) {
|
||||
if (dto == null) {
|
||||
return null;
|
||||
}
|
||||
if (StringUtils.isBlank(dto.getStartTime()) && StringUtils.isBlank(dto.getEndTime())) {
|
||||
LocalDate endDate = LocalDate.now();
|
||||
LocalDate startDate = endDate.minusDays(6);
|
||||
dto.setStartTime(startDate.toString());
|
||||
dto.setEndTime(endDate.toString());
|
||||
}
|
||||
|
||||
BusinessOperationDateRangeDTO range = buildDateRange(dto);
|
||||
List<String> stationIdList = resolveStationIds(dto.getStationIdList());
|
||||
|
||||
// 并行查询当前周期和上周期原始数据
|
||||
CompletableFuture<List<SettleOrderReport>> currentReportFuture = CompletableFuture.supplyAsync(
|
||||
() -> queryRawReports(stationIdList, range.getCurrentStart(), range.getCurrentEnd()),
|
||||
FINANCIAL_THREAD_POOL);
|
||||
CompletableFuture<List<SettleOrderReport>> previousReportFuture = CompletableFuture.supplyAsync(
|
||||
() -> queryRawReports(stationIdList, range.getPreviousStart(), range.getPreviousEnd()),
|
||||
FINANCIAL_THREAD_POOL);
|
||||
|
||||
BusinessOperationSummaryDTO currentSummary = buildSummaryFromReports(currentReportFuture.join());
|
||||
BusinessOperationSummaryDTO previousSummary = buildSummaryFromReports(previousReportFuture.join());
|
||||
|
||||
List<BusinessOperationMetricVO> metricList = new ArrayList<>();
|
||||
metricList.add(buildMetric(METRIC_ORDER_AMOUNT, "订单总额",
|
||||
currentSummary.getOrderAmount(), previousSummary.getOrderAmount()));
|
||||
metricList.add(buildMetric(METRIC_AVG_SERVICE_FEE, "单均服务费",
|
||||
currentSummary.getAvgServiceFee(), previousSummary.getAvgServiceFee()));
|
||||
metricList.add(buildMetric(METRIC_AVG_ELECTRICITY, "单均度数",
|
||||
currentSummary.getAvgElectricity(), previousSummary.getAvgElectricity()));
|
||||
|
||||
BusinessOperationDiagnosisItemVO diagnosisItem = buildSelectedDiagnosisItem(dto, currentSummary, previousSummary);
|
||||
|
||||
return BusinessOperationAnalysisVO.builder()
|
||||
.currentStartTime(range.getCurrentStart())
|
||||
.currentEndTime(range.getCurrentEnd())
|
||||
.previousStartTime(range.getPreviousStart())
|
||||
.previousEndTime(range.getPreviousEnd())
|
||||
.metricList(metricList)
|
||||
.diagnosisItem(diagnosisItem)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询枪均效率分析
|
||||
* 优化:订单汇总DB查询与枪口数量DB查询并行执行
|
||||
*
|
||||
* @param dto 查询条件
|
||||
* @return 枪均效率分析
|
||||
*/
|
||||
@Override
|
||||
public BusinessGunEfficiencyAnalysisVO getBusinessGunEfficiencyAnalysis(BusinessOperationAnalysisQueryDTO dto) {
|
||||
if (dto == null) {
|
||||
return null;
|
||||
}
|
||||
if (StringUtils.isBlank(dto.getStartTime()) && StringUtils.isBlank(dto.getEndTime())) {
|
||||
LocalDate endDate = LocalDate.now();
|
||||
LocalDate startDate = endDate.minusDays(6);
|
||||
dto.setStartTime(startDate.toString());
|
||||
dto.setEndTime(endDate.toString());
|
||||
}
|
||||
|
||||
BusinessOperationDateRangeDTO range = buildDateRange(dto);
|
||||
List<String> stationIdList = resolveStationIds(dto.getStationIdList());
|
||||
|
||||
// 并行查询订单汇总和枪口数量
|
||||
CompletableFuture<BusinessOperationSummaryDTO> summaryFuture = CompletableFuture.supplyAsync(
|
||||
() -> buildSummaryFromReports(
|
||||
queryRawReports(stationIdList, range.getCurrentStart(), range.getCurrentEnd())),
|
||||
FINANCIAL_THREAD_POOL);
|
||||
CompletableFuture<Integer> connectorCountFuture = CompletableFuture.supplyAsync(
|
||||
() -> countConnectors(stationIdList), FINANCIAL_THREAD_POOL);
|
||||
|
||||
BusinessOperationSummaryDTO summary = summaryFuture.join();
|
||||
int connectorCount = connectorCountFuture.join();
|
||||
|
||||
BigDecimal connectorCountBd = BigDecimal.valueOf(connectorCount);
|
||||
BigDecimal dayCount = BigDecimal.valueOf(calculateInclusiveDays(range.getCurrentStart(), range.getCurrentEnd()));
|
||||
BigDecimal connectorDayCount = connectorCountBd.multiply(dayCount);
|
||||
|
||||
BigDecimal gunAvgDailyServiceFee = BigDecimalUtils.safeDivide(summary.getServiceAmount(), connectorDayCount, 2);
|
||||
BigDecimal gunAvgDailyElectricity = BigDecimalUtils.safeDivide(summary.getUseElectricity(), connectorDayCount, 4);
|
||||
BigDecimal avgServiceFeePerDegree = BigDecimalUtils.safeDivide(summary.getServiceAmount(), summary.getUseElectricity(), 4);
|
||||
BigDecimal gunAvgDailyChargeTimeMinutes = BigDecimalUtils.safeDivide(summary.getChargeTime(), connectorDayCount, 2);
|
||||
BigDecimal avgPower = BigDecimalUtils.safeDivide(
|
||||
summary.getUseElectricity().multiply(new BigDecimal("60")),
|
||||
summary.getChargeTime(),
|
||||
4);
|
||||
|
||||
return BusinessGunEfficiencyAnalysisVO.builder()
|
||||
.startTime(range.getCurrentStart())
|
||||
.endTime(range.getCurrentEnd())
|
||||
.gunAvgDailyServiceFee(BigDecimalUtils.normalize(gunAvgDailyServiceFee))
|
||||
.gunAvgDailyElectricity(BigDecimalUtils.normalize(gunAvgDailyElectricity))
|
||||
.avgServiceFeePerDegree(BigDecimalUtils.normalize(avgServiceFeePerDegree))
|
||||
.gunAvgDailyChargeTime(formatMinutesToHourMinute(gunAvgDailyChargeTimeMinutes))
|
||||
.avgPower(BigDecimalUtils.normalize(avgPower))
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建当前选中指标对应的最大影响因素
|
||||
*
|
||||
* @param dto 查询条件
|
||||
* @param currentSummary 当前周期汇总
|
||||
* @param previousSummary 上周期汇总
|
||||
* @return 经营诊断指标
|
||||
*/
|
||||
private BusinessOperationDiagnosisItemVO buildSelectedDiagnosisItem(BusinessOperationAnalysisQueryDTO dto,
|
||||
BusinessOperationSummaryDTO currentSummary,
|
||||
BusinessOperationSummaryDTO previousSummary) {
|
||||
String selectedMetricCode = dto.getSelectedMetricCode();
|
||||
if (METRIC_AVG_SERVICE_FEE.equals(selectedMetricCode)) {
|
||||
return buildDiagnosis(METRIC_AVG_SERVICE_FEE, "单均服务费",
|
||||
FACTOR_AVG_ELECTRICITY, "单均度数",
|
||||
currentSummary.getAvgElectricity(), previousSummary.getAvgElectricity());
|
||||
}
|
||||
if (METRIC_AVG_ELECTRICITY.equals(selectedMetricCode)) {
|
||||
return buildDiagnosis(METRIC_AVG_ELECTRICITY, "单均度数",
|
||||
FACTOR_AVG_CHARGE_TIME, "单均充电时长",
|
||||
currentSummary.getAvgChargeTime(), previousSummary.getAvgChargeTime());
|
||||
}
|
||||
return buildDiagnosis(METRIC_ORDER_AMOUNT, "订单总数",
|
||||
FACTOR_ORDER_COUNT, "订单量",
|
||||
currentSummary.getOrderCount(), previousSummary.getOrderCount());
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建经营状态指标项
|
||||
*
|
||||
* @param metricCode 指标编码
|
||||
* @param metricName 指标名称
|
||||
* @param currentValue 当前周期值
|
||||
* @param previousValue 上周期值
|
||||
* @return 经营状态指标
|
||||
*/
|
||||
private BusinessOperationMetricVO buildMetric(String metricCode, String metricName,
|
||||
BigDecimal currentValue, BigDecimal previousValue) {
|
||||
BigDecimal safeCurrentValue = BigDecimalUtils.nullToZero(currentValue);
|
||||
BigDecimal safePreviousValue = BigDecimalUtils.nullToZero(previousValue);
|
||||
BigDecimal changeValue = BigDecimalUtils.normalize(safeCurrentValue.subtract(safePreviousValue));
|
||||
return BusinessOperationMetricVO.builder()
|
||||
.metricCode(metricCode)
|
||||
.metricName(metricName)
|
||||
.currentValue(BigDecimalUtils.normalize(safeCurrentValue))
|
||||
.previousValue(BigDecimalUtils.normalize(safePreviousValue))
|
||||
.changeValue(changeValue)
|
||||
.changeRate(BigDecimalUtils.calculateRate(safeCurrentValue, safePreviousValue))
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建经营诊断指标项
|
||||
*
|
||||
* @param metricCode 指标编码
|
||||
* @param metricName 指标名称
|
||||
* @param factorCode 影响因素编码
|
||||
* @param factorName 影响因素名称
|
||||
* @param currentValue 当前周期值
|
||||
* @param previousValue 上周期值
|
||||
* @return 经营诊断指标
|
||||
*/
|
||||
private BusinessOperationDiagnosisItemVO buildDiagnosis(String metricCode, String metricName,
|
||||
String factorCode, String factorName,
|
||||
BigDecimal currentValue, BigDecimal previousValue) {
|
||||
BigDecimal safeCurrentValue = BigDecimalUtils.nullToZero(currentValue);
|
||||
BigDecimal safePreviousValue = BigDecimalUtils.nullToZero(previousValue);
|
||||
BigDecimal changeValue = BigDecimalUtils.normalize(safeCurrentValue.subtract(safePreviousValue));
|
||||
return BusinessOperationDiagnosisItemVO.builder()
|
||||
.metricCode(metricCode)
|
||||
.metricName(metricName)
|
||||
.factorCode(factorCode)
|
||||
.factorName(factorName)
|
||||
.currentValue(BigDecimalUtils.normalize(safeCurrentValue))
|
||||
.previousValue(BigDecimalUtils.normalize(safePreviousValue))
|
||||
.changeValue(changeValue)
|
||||
.changeRate(BigDecimalUtils.calculateRate(safeCurrentValue, safePreviousValue))
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据查询条件构建当前周期和上周期日期区间
|
||||
*
|
||||
* @param dto 查询条件
|
||||
* @return 日期区间
|
||||
*/
|
||||
private BusinessOperationDateRangeDTO buildDateRange(BusinessOperationAnalysisQueryDTO dto) {
|
||||
try {
|
||||
LocalDate currentStart = LocalDate.parse(dto.getStartTime());
|
||||
LocalDate currentEnd = LocalDate.parse(dto.getEndTime());
|
||||
if (currentEnd.isBefore(currentStart)) {
|
||||
throw new BusinessException(ReturnCodeEnum.CODE_PARAM_NOT_NULL_ERROR);
|
||||
}
|
||||
long days = ChronoUnit.DAYS.between(currentStart, currentEnd) + 1;
|
||||
LocalDate previousEnd = currentStart.minusDays(1);
|
||||
LocalDate previousStart = previousEnd.minusDays(days - 1);
|
||||
return BusinessOperationDateRangeDTO.builder()
|
||||
.currentStart(currentStart.toString())
|
||||
.currentEnd(currentEnd.toString())
|
||||
.previousStart(previousStart.toString())
|
||||
.previousEnd(previousEnd.toString())
|
||||
.build();
|
||||
} catch (BusinessException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
throw new BusinessException(ReturnCodeEnum.CODE_PARAM_NOT_NULL_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析本次查询可用的站点ID列表
|
||||
*
|
||||
* @param stationIdList 前端传入站点ID列表
|
||||
* @return 实际查询站点ID列表
|
||||
*/
|
||||
private List<String> resolveStationIds(List<String> stationIdList) {
|
||||
List<String> authorizedStationIds = getAuthorizedStationIds();
|
||||
if (authorizedStationIds == null) {
|
||||
return CollectionUtils.isEmpty(stationIdList) ? null : stationIdList;
|
||||
}
|
||||
if (!CollectionUtils.isEmpty(stationIdList)) {
|
||||
return stationIdList.stream()
|
||||
.filter(authorizedStationIds::contains)
|
||||
.distinct()
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
return authorizedStationIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前账号可访问的站点ID列表,平台账号返回null表示不限制
|
||||
*
|
||||
* @return 站点ID列表
|
||||
*/
|
||||
private List<String> getAuthorizedStationIds() {
|
||||
try {
|
||||
AuthorizedDeptVO authorizedMap = UserUtils.getAuthorizedMap();
|
||||
if (authorizedMap == null) {
|
||||
return null;
|
||||
}
|
||||
if (!CollectionUtils.isEmpty(authorizedMap.getStationDeptIds())) {
|
||||
return pileStationInfoService.queryByStationDeptIds(authorizedMap.getStationDeptIds());
|
||||
}
|
||||
if (!CollectionUtils.isEmpty(authorizedMap.getMerchantDeptIds())) {
|
||||
return pileStationInfoService.getStationIdsByMerchantIds(authorizedMap.getMerchantDeptIds());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("解析经营分析站点权限失败", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计站点范围内的枪口数量
|
||||
*
|
||||
* @param stationIdList 站点ID列表
|
||||
* @return 枪口数量
|
||||
*/
|
||||
private int countConnectors(List<String> stationIdList) {
|
||||
if (stationIdList == null) {
|
||||
return pileConnectorInfoService.selectPileConnectorInfoList(new PileConnectorInfo()).size();
|
||||
}
|
||||
if (stationIdList.isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
return pileConnectorInfoService.batchSelectConnectorList(stationIdList).size();
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算包含起止日期在内的天数
|
||||
*
|
||||
* @param startTime 开始时间
|
||||
* @param endTime 结束时间
|
||||
* @return 天数
|
||||
*/
|
||||
private long calculateInclusiveDays(String startTime, String endTime) {
|
||||
LocalDate startDate = LocalDate.parse(startTime);
|
||||
LocalDate endDate = LocalDate.parse(endTime);
|
||||
return ChronoUnit.DAYS.between(startDate, endDate) + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将分钟数转换为xxhxxm格式
|
||||
*
|
||||
* @param minutes 分钟数
|
||||
* @return 格式化结果
|
||||
*/
|
||||
private String formatMinutesToHourMinute(BigDecimal minutes) {
|
||||
long totalMinutes = BigDecimalUtils.nullToZero(minutes)
|
||||
.setScale(0, RoundingMode.HALF_UP)
|
||||
.longValue();
|
||||
long hours = totalMinutes / 60;
|
||||
long remainMinutes = totalMinutes % 60;
|
||||
return String.format("%02dh%02dm", hours, remainMinutes);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询经营规模
|
||||
*
|
||||
* @param dto 查询条件
|
||||
* @return 经营规模数据
|
||||
*/
|
||||
@Override
|
||||
public BusinessScaleVO getBusinessScale(BusinessScaleQueryDTO dto) {
|
||||
if (dto == null) {
|
||||
return null;
|
||||
}
|
||||
if (StringUtils.isBlank(dto.getStartTime()) && StringUtils.isBlank(dto.getEndTime())) {
|
||||
LocalDate endDate = LocalDate.now();
|
||||
LocalDate startDate = endDate.minusDays(6);
|
||||
dto.setStartTime(startDate.toString());
|
||||
dto.setEndTime(endDate.toString());
|
||||
}
|
||||
|
||||
BusinessOperationDateRangeDTO range = buildDateRangeFromScale(dto);
|
||||
List<String> stationIdList = resolveStationIds(dto.getStationIdList());
|
||||
|
||||
// 并行查询当前周期和上周期的原始报表数据,各只查一次
|
||||
CompletableFuture<List<SettleOrderReport>> currentReportFuture = CompletableFuture.supplyAsync(
|
||||
() -> queryRawReports(stationIdList, range.getCurrentStart(), range.getCurrentEnd()),
|
||||
FINANCIAL_THREAD_POOL);
|
||||
CompletableFuture<List<SettleOrderReport>> previousReportFuture = CompletableFuture.supplyAsync(
|
||||
() -> queryRawReports(stationIdList, range.getPreviousStart(), range.getPreviousEnd()),
|
||||
FINANCIAL_THREAD_POOL);
|
||||
|
||||
// 等待两个查询完成
|
||||
List<SettleOrderReport> currentReports = currentReportFuture.join();
|
||||
List<SettleOrderReport> previousReports = previousReportFuture.join();
|
||||
|
||||
// 从同一份原始数据分别构建汇总指标和每日明细
|
||||
BusinessOperationSummaryDTO currentSummary = buildSummaryFromReports(currentReports);
|
||||
BusinessOperationSummaryDTO previousSummary = buildSummaryFromReports(previousReports);
|
||||
|
||||
Map<String, SettleOrderReport> currentDailyMap = buildDailyMapFromReports(currentReports);
|
||||
Map<String, SettleOrderReport> previousDailyMap = buildDailyMapFromReports(previousReports);
|
||||
|
||||
// 构建指标卡片
|
||||
List<BusinessScaleMetricVO> metricList = new ArrayList<>();
|
||||
metricList.add(buildScaleMetric(SCALE_METRIC_ORDER_AMOUNT, "订单总额", "元",
|
||||
currentSummary.getOrderAmount(), previousSummary.getOrderAmount()));
|
||||
metricList.add(buildScaleMetric(SCALE_METRIC_ORDER_COUNT, "充电订单量", "单",
|
||||
currentSummary.getOrderCount(), previousSummary.getOrderCount()));
|
||||
metricList.add(buildScaleMetric(SCALE_METRIC_SERVICE_AMOUNT, "充电服务费", "元",
|
||||
currentSummary.getServiceAmount(), previousSummary.getServiceAmount()));
|
||||
metricList.add(buildScaleMetric(SCALE_METRIC_USE_ELECTRICITY, "充电电量", "度",
|
||||
currentSummary.getUseElectricity(), previousSummary.getUseElectricity()));
|
||||
|
||||
// 构建选中指标的曲线图数据(复用已查询的dailyMap)
|
||||
String selectedMetricCode = StringUtils.isBlank(dto.getSelectedMetricCode())
|
||||
? SCALE_METRIC_USE_ELECTRICITY : dto.getSelectedMetricCode();
|
||||
BusinessScaleChartVO chartData = buildScaleChartFromMaps(
|
||||
selectedMetricCode, range, currentDailyMap, previousDailyMap);
|
||||
|
||||
return BusinessScaleVO.builder()
|
||||
.currentStartTime(range.getCurrentStart())
|
||||
.currentEndTime(range.getCurrentEnd())
|
||||
.previousStartTime(range.getPreviousStart())
|
||||
.previousEndTime(range.getPreviousEnd())
|
||||
.metricList(metricList)
|
||||
.chartData(chartData)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建经营规模日期区间
|
||||
*/
|
||||
private BusinessOperationDateRangeDTO buildDateRangeFromScale(BusinessScaleQueryDTO dto) {
|
||||
try {
|
||||
LocalDate currentStart = LocalDate.parse(dto.getStartTime());
|
||||
LocalDate currentEnd = LocalDate.parse(dto.getEndTime());
|
||||
if (currentEnd.isBefore(currentStart)) {
|
||||
throw new BusinessException(ReturnCodeEnum.CODE_PARAM_NOT_NULL_ERROR);
|
||||
}
|
||||
long days = ChronoUnit.DAYS.between(currentStart, currentEnd) + 1;
|
||||
LocalDate previousEnd = currentStart.minusDays(1);
|
||||
LocalDate previousStart = previousEnd.minusDays(days - 1);
|
||||
return BusinessOperationDateRangeDTO.builder()
|
||||
.currentStart(currentStart.toString())
|
||||
.currentEnd(currentEnd.toString())
|
||||
.previousStart(previousStart.toString())
|
||||
.previousEnd(previousEnd.toString())
|
||||
.build();
|
||||
} catch (BusinessException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
throw new BusinessException(ReturnCodeEnum.CODE_PARAM_NOT_NULL_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建经营规模指标卡片
|
||||
*/
|
||||
private BusinessScaleMetricVO buildScaleMetric(String metricCode, String metricName, String unit,
|
||||
BigDecimal currentValue, BigDecimal previousValue) {
|
||||
BigDecimal safeCurrentValue = BigDecimalUtils.nullToZero(currentValue);
|
||||
BigDecimal safePreviousValue = BigDecimalUtils.nullToZero(previousValue);
|
||||
BigDecimal changeValue = BigDecimalUtils.normalize(safeCurrentValue.subtract(safePreviousValue));
|
||||
return BusinessScaleMetricVO.builder()
|
||||
.metricCode(metricCode)
|
||||
.metricName(metricName)
|
||||
.unit(unit)
|
||||
.currentValue(BigDecimalUtils.normalize(safeCurrentValue))
|
||||
.previousValue(BigDecimalUtils.normalize(safePreviousValue))
|
||||
.changeValue(changeValue)
|
||||
.changeRate(BigDecimalUtils.calculateRate(safeCurrentValue, safePreviousValue))
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询原始报表数据(供并行调用)
|
||||
*/
|
||||
private List<SettleOrderReport> queryRawReports(List<String> stationIdList,
|
||||
String startTime, String endTime) {
|
||||
if (stationIdList != null && stationIdList.isEmpty()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
List<SettleOrderReport> reportList = settleOrderReportService.queryOrderReport(
|
||||
stationIdList, startTime, endTime);
|
||||
return CollectionUtils.isEmpty(reportList) ? new ArrayList<>() : reportList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从原始报表数据构建汇总指标
|
||||
*/
|
||||
private BusinessOperationSummaryDTO buildSummaryFromReports(List<SettleOrderReport> reportList) {
|
||||
BusinessOperationSummaryDTO summary = new BusinessOperationSummaryDTO();
|
||||
if (CollectionUtils.isEmpty(reportList)) {
|
||||
return summary;
|
||||
}
|
||||
for (SettleOrderReport report : reportList) {
|
||||
summary.setOrderAmount(summary.getOrderAmount().add(BigDecimalUtils.nullToZero(report.getTotalAmount())));
|
||||
summary.setServiceAmount(summary.getServiceAmount().add(BigDecimalUtils.nullToZero(report.getServiceAmount())));
|
||||
summary.setUseElectricity(summary.getUseElectricity().add(BigDecimalUtils.nullToZero(report.getUseElectricity())));
|
||||
summary.setOrderCount(summary.getOrderCount().add(BigDecimalUtils.parseOrZero(report.getChargeNum())));
|
||||
summary.setChargeTime(summary.getChargeTime().add(BigDecimalUtils.parseOrZero(report.getChargeTime())));
|
||||
}
|
||||
summary.setAvgServiceFee(BigDecimalUtils.safeDivide(summary.getServiceAmount(), summary.getOrderCount(), 2));
|
||||
summary.setAvgElectricity(BigDecimalUtils.safeDivide(summary.getUseElectricity(), summary.getOrderCount(), 2));
|
||||
summary.setAvgChargeTime(BigDecimalUtils.safeDivide(summary.getChargeTime(), summary.getOrderCount(), 2));
|
||||
return summary;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从原始报表数据构建按日期分组的Map
|
||||
*/
|
||||
private Map<String, SettleOrderReport> buildDailyMapFromReports(List<SettleOrderReport> reportList) {
|
||||
Map<String, SettleOrderReport> dailyMap = new LinkedHashMap<>();
|
||||
if (CollectionUtils.isEmpty(reportList)) {
|
||||
return dailyMap;
|
||||
}
|
||||
for (SettleOrderReport report : reportList) {
|
||||
String tradeDate = report.getTradeDate();
|
||||
if (StringUtils.isBlank(tradeDate)) {
|
||||
continue;
|
||||
}
|
||||
SettleOrderReport existing = dailyMap.get(tradeDate);
|
||||
if (existing == null) {
|
||||
dailyMap.put(tradeDate, report);
|
||||
} else {
|
||||
existing.setTotalAmount(existing.getTotalAmount().add(
|
||||
BigDecimalUtils.nullToZero(report.getTotalAmount())));
|
||||
existing.setServiceAmount(existing.getServiceAmount().add(
|
||||
BigDecimalUtils.nullToZero(report.getServiceAmount())));
|
||||
existing.setUseElectricity(existing.getUseElectricity().add(
|
||||
BigDecimalUtils.nullToZero(report.getUseElectricity())));
|
||||
existing.setChargeNum(String.valueOf(
|
||||
BigDecimalUtils.parseOrZero(existing.getChargeNum())
|
||||
.add(BigDecimalUtils.parseOrZero(report.getChargeNum()))));
|
||||
}
|
||||
}
|
||||
return dailyMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从已有的dailyMap构建曲线图数据(不再重复查DB)
|
||||
*/
|
||||
private BusinessScaleChartVO buildScaleChartFromMaps(String metricCode,
|
||||
BusinessOperationDateRangeDTO range,
|
||||
Map<String, SettleOrderReport> currentDailyMap,
|
||||
Map<String, SettleOrderReport> previousDailyMap) {
|
||||
String metricName;
|
||||
String unit;
|
||||
switch (metricCode) {
|
||||
case SCALE_METRIC_ORDER_AMOUNT:
|
||||
metricName = "订单总额";
|
||||
unit = "元";
|
||||
break;
|
||||
case SCALE_METRIC_ORDER_COUNT:
|
||||
metricName = "充电订单量";
|
||||
unit = "单";
|
||||
break;
|
||||
case SCALE_METRIC_SERVICE_AMOUNT:
|
||||
metricName = "充电服务费";
|
||||
unit = "元";
|
||||
break;
|
||||
default:
|
||||
metricName = "充电电量";
|
||||
unit = "度";
|
||||
break;
|
||||
}
|
||||
|
||||
LocalDate currentStart = LocalDate.parse(range.getCurrentStart());
|
||||
LocalDate currentEnd = LocalDate.parse(range.getCurrentEnd());
|
||||
LocalDate previousStart = LocalDate.parse(range.getPreviousStart());
|
||||
|
||||
long dayCount = ChronoUnit.DAYS.between(currentStart, currentEnd) + 1;
|
||||
DateTimeFormatter displayFormatter = DateTimeFormatter.ofPattern("MM/dd");
|
||||
|
||||
List<BusinessScaleChartVO.ChartPoint> chartPoints = new ArrayList<>();
|
||||
for (long i = 0; i < dayCount; i++) {
|
||||
LocalDate currentDate = currentStart.plusDays(i);
|
||||
LocalDate previousDate = previousStart.plusDays(i);
|
||||
|
||||
String dateKey = currentDate.toString();
|
||||
SettleOrderReport currentReport = currentDailyMap.get(dateKey);
|
||||
SettleOrderReport previousReport = previousDailyMap.get(previousDate.toString());
|
||||
|
||||
BigDecimal currentValue = extractMetricValue(metricCode, currentReport);
|
||||
BigDecimal previousValue = extractMetricValue(metricCode, previousReport);
|
||||
|
||||
chartPoints.add(BusinessScaleChartVO.ChartPoint.builder()
|
||||
.date(currentDate.format(displayFormatter))
|
||||
.fullDate(dateKey)
|
||||
.value(BigDecimalUtils.normalize(currentValue))
|
||||
.previousValue(BigDecimalUtils.normalize(previousValue))
|
||||
.changeRate(BigDecimalUtils.calculateRate(
|
||||
BigDecimalUtils.nullToZero(currentValue),
|
||||
BigDecimalUtils.nullToZero(previousValue)))
|
||||
.build());
|
||||
}
|
||||
|
||||
return BusinessScaleChartVO.builder()
|
||||
.metricCode(metricCode)
|
||||
.metricName(metricName)
|
||||
.unit(unit)
|
||||
.chartData(chartPoints)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 从报表记录中提取指定指标的值
|
||||
*/
|
||||
private BigDecimal extractMetricValue(String metricCode, SettleOrderReport report) {
|
||||
if (report == null) {
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
switch (metricCode) {
|
||||
case SCALE_METRIC_ORDER_AMOUNT:
|
||||
return BigDecimalUtils.nullToZero(report.getTotalAmount());
|
||||
case SCALE_METRIC_ORDER_COUNT:
|
||||
return BigDecimalUtils.parseOrZero(report.getChargeNum());
|
||||
case SCALE_METRIC_SERVICE_AMOUNT:
|
||||
return BigDecimalUtils.nullToZero(report.getServiceAmount());
|
||||
case SCALE_METRIC_USE_ELECTRICITY:
|
||||
default:
|
||||
return BigDecimalUtils.nullToZero(report.getUseElectricity());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,10 @@ package com.jsowell.pile.service.impl;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.jsowell.common.core.domain.vo.AuthorizedDeptVO;
|
||||
import com.jsowell.common.util.StringUtils;
|
||||
import com.jsowell.common.util.file.AliyunOssUploadUtils;
|
||||
import com.jsowell.common.util.file.FileUtils;
|
||||
import com.jsowell.common.config.JsowellConfig;
|
||||
import com.jsowell.pile.domain.OrderInvoiceRecord;
|
||||
import com.jsowell.pile.dto.GetInvoiceInfoDTO;
|
||||
import com.jsowell.pile.dto.QueryInvoiceRecordDTO;
|
||||
@@ -13,14 +17,22 @@ import com.jsowell.pile.util.UserUtils;
|
||||
import com.jsowell.pile.vo.base.OrderAmountDetailVO;
|
||||
import com.jsowell.pile.vo.uniapp.customer.InvoiceTitleVO;
|
||||
import com.jsowell.pile.vo.web.InvoiceRecordVO;
|
||||
import com.jsowell.pile.vo.web.OrderInvoiceRecordExportVO;
|
||||
import com.jsowell.pile.vo.web.OrderInvoiceRecordImportVO;
|
||||
import com.jsowell.pile.vo.web.OrderInvoiceRecordVO;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Paths;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 申请开票Service业务层处理
|
||||
@@ -69,6 +81,7 @@ public class OrderInvoiceRecordServiceImpl implements OrderInvoiceRecordService
|
||||
.memberId(vo.getMemberId())
|
||||
.phoneNumber(vo.getPhoneNumber())
|
||||
.status(vo.getStatus())
|
||||
.invoiceUrl(vo.getInvoiceUrl())
|
||||
.invoiceTitle(invoiceTitleVO)
|
||||
.orderList(orderAmountDetailVOS)
|
||||
.createTime(vo.getCreateTime())
|
||||
@@ -94,16 +107,114 @@ public class OrderInvoiceRecordServiceImpl implements OrderInvoiceRecordService
|
||||
*/
|
||||
@Override
|
||||
public List<OrderInvoiceRecordVO> getInvoiceListWithAuth(GetInvoiceInfoDTO dto) {
|
||||
if (!fillAuthorizedMerchantDeptIds(dto)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
return orderInvoiceRecordMapper.getInvoiceListWithAuth(dto);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<OrderInvoiceRecordExportVO> getInvoiceExportListWithAuth(GetInvoiceInfoDTO dto) {
|
||||
if (!fillAuthorizedMerchantDeptIds(dto)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
return orderInvoiceRecordMapper.getInvoiceExportListWithAuth(dto);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String importInvoiceImages(List<OrderInvoiceRecordImportVO> importList, String operName) {
|
||||
if (CollectionUtils.isEmpty(importList)) {
|
||||
return "导入数据为空";
|
||||
}
|
||||
Set<Integer> requestedIds = importList.stream()
|
||||
.map(OrderInvoiceRecordImportVO::getId)
|
||||
.filter(id -> id != null)
|
||||
.collect(Collectors.toCollection(HashSet::new));
|
||||
if (CollectionUtils.isEmpty(requestedIds)) {
|
||||
return "导入失败,文件中缺少申请记录ID";
|
||||
}
|
||||
GetInvoiceInfoDTO dto = new GetInvoiceInfoDTO();
|
||||
dto.setIds(requestedIds.toArray(new Integer[0]));
|
||||
Set<Integer> authorizedIds = getInvoiceExportListWithAuth(dto).stream()
|
||||
.map(OrderInvoiceRecordExportVO::getId)
|
||||
.filter(id -> id != null)
|
||||
.collect(Collectors.toCollection(HashSet::new));
|
||||
|
||||
int successNum = 0;
|
||||
int skipNum = 0;
|
||||
List<String> messages = new ArrayList<>();
|
||||
for (int i = 0; i < importList.size(); i++) {
|
||||
OrderInvoiceRecordImportVO item = importList.get(i);
|
||||
int rowNum = i + 2;
|
||||
if (item.getId() == null) {
|
||||
messages.add("第" + rowNum + "行缺少申请记录ID");
|
||||
continue;
|
||||
}
|
||||
if (!authorizedIds.contains(item.getId())) {
|
||||
messages.add("第" + rowNum + "行申请记录无权限或不存在");
|
||||
continue;
|
||||
}
|
||||
if (StringUtils.isEmpty(item.getInvoiceImage())) {
|
||||
skipNum++;
|
||||
continue;
|
||||
}
|
||||
|
||||
String absolutePath = buildAbsoluteImportPath(item.getInvoiceImage());
|
||||
try {
|
||||
byte[] imageBytes = java.nio.file.Files.readAllBytes(Paths.get(absolutePath));
|
||||
String url = AliyunOssUploadUtils.upload2OSS(imageBytes, new File(absolutePath).getName());
|
||||
OrderInvoiceRecord updateRecord = new OrderInvoiceRecord();
|
||||
updateRecord.setId(item.getId());
|
||||
updateRecord.setInvoiceUrl(url);
|
||||
updateRecord.setStatus("1");
|
||||
updateRecord.setUpdateBy(operName);
|
||||
updateRecord.setUpdateTime(new Date());
|
||||
if (orderInvoiceRecordMapper.updateOrderInvoiceRecord(updateRecord) > 0) {
|
||||
successNum++;
|
||||
} else {
|
||||
messages.add("第" + rowNum + "行更新失败");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
messages.add("第" + rowNum + "行导入失败:" + e.getMessage());
|
||||
} finally {
|
||||
FileUtils.deleteFile(absolutePath);
|
||||
}
|
||||
}
|
||||
|
||||
StringBuilder result = new StringBuilder();
|
||||
result.append("成功导入").append(successNum).append("条发票数据");
|
||||
if (skipNum > 0) {
|
||||
result.append(",跳过未贴图记录").append(skipNum).append("条");
|
||||
}
|
||||
if (CollectionUtils.isNotEmpty(messages)) {
|
||||
result.append(",失败").append(messages.size()).append("条:<br/>")
|
||||
.append(String.join("<br/>", messages));
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
private boolean fillAuthorizedMerchantDeptIds(GetInvoiceInfoDTO dto) {
|
||||
// 获取登录账号信息
|
||||
AuthorizedDeptVO authorizedMap = UserUtils.getAuthorizedMap();
|
||||
if (authorizedMap == null) {
|
||||
return new ArrayList<>();
|
||||
return false;
|
||||
}
|
||||
List<String> merchantDeptIds = authorizedMap.getMerchantDeptIds();
|
||||
if (CollectionUtils.isNotEmpty(merchantDeptIds)) {
|
||||
dto.setMerchantDeptIds(merchantDeptIds);
|
||||
}
|
||||
return orderInvoiceRecordMapper.getInvoiceListWithAuth(dto);
|
||||
return true;
|
||||
}
|
||||
|
||||
private String buildAbsoluteImportPath(String relativePath) {
|
||||
if (StringUtils.isEmpty(relativePath)) {
|
||||
return relativePath;
|
||||
}
|
||||
File file = new File(relativePath);
|
||||
if (file.isAbsolute()) {
|
||||
return relativePath;
|
||||
}
|
||||
return Paths.get(JsowellConfig.getProfile(), relativePath).toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.jsowell.pile.vo.uniapp.business;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 枪均效率分析VO
|
||||
*
|
||||
* @author Auto
|
||||
* @Date 2026/4/3
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Builder
|
||||
public class BusinessGunEfficiencyAnalysisVO {
|
||||
|
||||
/**
|
||||
* 开始时间
|
||||
*/
|
||||
private String startTime;
|
||||
|
||||
/**
|
||||
* 结束时间
|
||||
*/
|
||||
private String endTime;
|
||||
|
||||
/**
|
||||
* 枪日均服务费(元)
|
||||
*/
|
||||
private BigDecimal gunAvgDailyServiceFee;
|
||||
|
||||
/**
|
||||
* 枪日均充电量(度)
|
||||
*/
|
||||
private BigDecimal gunAvgDailyElectricity;
|
||||
|
||||
/**
|
||||
* 度均服务费(元)
|
||||
*/
|
||||
private BigDecimal avgServiceFeePerDegree;
|
||||
|
||||
/**
|
||||
* 枪日均充电时长,格式:xxhxxm
|
||||
*/
|
||||
private String gunAvgDailyChargeTime;
|
||||
|
||||
/**
|
||||
* 平均功率(KW)
|
||||
*/
|
||||
private BigDecimal avgPower;
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.jsowell.pile.vo.uniapp.business;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 经营分析VO
|
||||
*
|
||||
* @author Auto
|
||||
* @Date 2026/4/1
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Builder
|
||||
public class BusinessOperationAnalysisVO {
|
||||
|
||||
/**
|
||||
* 当前周期开始时间
|
||||
*/
|
||||
private String currentStartTime;
|
||||
|
||||
/**
|
||||
* 当前周期结束时间
|
||||
*/
|
||||
private String currentEndTime;
|
||||
|
||||
/**
|
||||
* 上周期开始时间
|
||||
*/
|
||||
private String previousStartTime;
|
||||
|
||||
/**
|
||||
* 上周期结束时间
|
||||
*/
|
||||
private String previousEndTime;
|
||||
|
||||
/**
|
||||
* 顶部指标列表
|
||||
*/
|
||||
private List<BusinessOperationMetricVO> metricList;
|
||||
|
||||
/**
|
||||
* 当前选中指标对应的最大影响因素
|
||||
*/
|
||||
private BusinessOperationDiagnosisItemVO diagnosisItem;
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package com.jsowell.pile.vo.uniapp.business;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 经营诊断项VO
|
||||
*
|
||||
* @author Auto
|
||||
* @Date 2026/4/1
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Builder
|
||||
public class BusinessOperationDiagnosisItemVO {
|
||||
|
||||
/**
|
||||
* 指标编码
|
||||
*/
|
||||
private String metricCode;
|
||||
|
||||
/**
|
||||
* 指标名称
|
||||
*/
|
||||
private String metricName;
|
||||
|
||||
/**
|
||||
* 最大影响因素编码
|
||||
*/
|
||||
private String factorCode;
|
||||
|
||||
/**
|
||||
* 最大影响因素名称
|
||||
*/
|
||||
private String factorName;
|
||||
|
||||
/**
|
||||
* 当前周期值
|
||||
*/
|
||||
private BigDecimal currentValue;
|
||||
|
||||
/**
|
||||
* 上周期值
|
||||
*/
|
||||
private BigDecimal previousValue;
|
||||
|
||||
/**
|
||||
* 变化值
|
||||
*/
|
||||
private BigDecimal changeValue;
|
||||
|
||||
/**
|
||||
* 环比
|
||||
*/
|
||||
private String changeRate;
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.jsowell.pile.vo.uniapp.business;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 经营指标VO
|
||||
*
|
||||
* @author Auto
|
||||
* @Date 2026/4/1
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Builder
|
||||
public class BusinessOperationMetricVO {
|
||||
|
||||
/**
|
||||
* 指标编码
|
||||
*/
|
||||
private String metricCode;
|
||||
|
||||
/**
|
||||
* 指标名称
|
||||
*/
|
||||
private String metricName;
|
||||
|
||||
/**
|
||||
* 当前周期值
|
||||
*/
|
||||
private BigDecimal currentValue;
|
||||
|
||||
/**
|
||||
* 上周期值
|
||||
*/
|
||||
private BigDecimal previousValue;
|
||||
|
||||
/**
|
||||
* 变化值
|
||||
*/
|
||||
private BigDecimal changeValue;
|
||||
|
||||
/**
|
||||
* 环比
|
||||
*/
|
||||
private String changeRate;
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package com.jsowell.pile.vo.uniapp.business;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 经营规模曲线图数据VO
|
||||
*
|
||||
* @author zhangziao
|
||||
* @date 2026/4/10
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Builder
|
||||
public class BusinessScaleChartVO {
|
||||
|
||||
/**
|
||||
* 指标编码
|
||||
*/
|
||||
private String metricCode;
|
||||
|
||||
/**
|
||||
* 指标名称
|
||||
*/
|
||||
private String metricName;
|
||||
|
||||
/**
|
||||
* 指标单位
|
||||
*/
|
||||
private String unit;
|
||||
|
||||
/**
|
||||
* 曲线图数据点列表
|
||||
*/
|
||||
private List<ChartPoint> chartData;
|
||||
|
||||
/**
|
||||
* 曲线图单个数据点
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Builder
|
||||
public static class ChartPoint {
|
||||
|
||||
/**
|
||||
* 日期,格式:MM/dd
|
||||
*/
|
||||
private String date;
|
||||
|
||||
/**
|
||||
* 完整日期,格式:yyyy-MM-dd
|
||||
*/
|
||||
private String fullDate;
|
||||
|
||||
/**
|
||||
* 当前周期值
|
||||
*/
|
||||
private BigDecimal value;
|
||||
|
||||
/**
|
||||
* 上周期同日值
|
||||
*/
|
||||
private BigDecimal previousValue;
|
||||
|
||||
/**
|
||||
* 环比
|
||||
*/
|
||||
private String changeRate;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.jsowell.pile.vo.uniapp.business;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 经营规模指标卡片VO
|
||||
*
|
||||
* @author zhangziao
|
||||
* @date 2026/4/10
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Builder
|
||||
public class BusinessScaleMetricVO {
|
||||
|
||||
/**
|
||||
* 指标编码
|
||||
*/
|
||||
private String metricCode;
|
||||
|
||||
/**
|
||||
* 指标名称
|
||||
*/
|
||||
private String metricName;
|
||||
|
||||
/**
|
||||
* 指标单位
|
||||
*/
|
||||
private String unit;
|
||||
|
||||
/**
|
||||
* 当前周期值
|
||||
*/
|
||||
private BigDecimal currentValue;
|
||||
|
||||
/**
|
||||
* 上周期值
|
||||
*/
|
||||
private BigDecimal previousValue;
|
||||
|
||||
/**
|
||||
* 变化值
|
||||
*/
|
||||
private BigDecimal changeValue;
|
||||
|
||||
/**
|
||||
* 环比,格式如 +3.91% 或 -3.91%
|
||||
*/
|
||||
private String changeRate;
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.jsowell.pile.vo.uniapp.business;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 经营规模VO
|
||||
*
|
||||
* @author zhangziao
|
||||
* @date 2026/4/10
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Builder
|
||||
public class BusinessScaleVO {
|
||||
|
||||
/**
|
||||
* 当前周期开始时间
|
||||
*/
|
||||
private String currentStartTime;
|
||||
|
||||
/**
|
||||
* 当前周期结束时间
|
||||
*/
|
||||
private String currentEndTime;
|
||||
|
||||
/**
|
||||
* 上周期开始时间
|
||||
*/
|
||||
private String previousStartTime;
|
||||
|
||||
/**
|
||||
* 上周期结束时间
|
||||
*/
|
||||
private String previousEndTime;
|
||||
|
||||
/**
|
||||
* 指标卡片列表(订单总额、充电订单量、充电服务费、充电电量)
|
||||
*/
|
||||
private List<BusinessScaleMetricVO> metricList;
|
||||
|
||||
/**
|
||||
* 当前选中指标对应的曲线图数据
|
||||
*/
|
||||
private BusinessScaleChartVO chartData;
|
||||
}
|
||||
@@ -44,6 +44,11 @@ public class InvoiceRecordVO {
|
||||
*/
|
||||
private String updateTime;
|
||||
|
||||
/**
|
||||
* 发票图片url
|
||||
*/
|
||||
private String invoiceUrl;
|
||||
|
||||
/**
|
||||
* 发票抬头信息
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
package com.jsowell.pile.vo.web;
|
||||
|
||||
import com.jsowell.common.annotation.Excel;
|
||||
import com.jsowell.common.annotation.Excel.ColumnType;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 申请开票导出对象
|
||||
*/
|
||||
@Data
|
||||
public class OrderInvoiceRecordExportVO {
|
||||
@Excel(name = "申请记录ID")
|
||||
private Integer id;
|
||||
|
||||
@Excel(name = "会员ID")
|
||||
private String memberId;
|
||||
|
||||
@Excel(name = "会员手机号")
|
||||
private String memberPhoneNumber;
|
||||
|
||||
@Excel(name = "运营商")
|
||||
private String merchantName;
|
||||
|
||||
@Excel(name = "抬头类型", readConverterExp = "1=单位,2=个人")
|
||||
private String titleType;
|
||||
|
||||
@Excel(name = "抬头名称")
|
||||
private String titleName;
|
||||
|
||||
@Excel(name = "税号")
|
||||
private String taxId;
|
||||
|
||||
@Excel(name = "单位地址")
|
||||
private String unitAddress;
|
||||
|
||||
@Excel(name = "抬头电话")
|
||||
private String titlePhoneNumber;
|
||||
|
||||
@Excel(name = "开票邮箱")
|
||||
private String email;
|
||||
|
||||
@Excel(name = "开户银行")
|
||||
private String bankName;
|
||||
|
||||
@Excel(name = "银行账户")
|
||||
private String bankAccountNumber;
|
||||
|
||||
@Excel(name = "申请订单编号")
|
||||
private String orderCodes;
|
||||
|
||||
@Excel(name = "状态", readConverterExp = "0=未开票,1=已开票")
|
||||
private String status;
|
||||
|
||||
@Excel(name = "开票总金额")
|
||||
private BigDecimal totalAmount;
|
||||
|
||||
@Excel(name = "总服务费金额")
|
||||
private BigDecimal totalServiceAmount;
|
||||
|
||||
@Excel(name = "总电费金额")
|
||||
private BigDecimal totalElecAmount;
|
||||
|
||||
@Excel(name = "申请时间")
|
||||
private String createTime;
|
||||
|
||||
@Excel(name = "开票时间")
|
||||
private String updateTime;
|
||||
|
||||
@Excel(name = "发票图片", cellType = ColumnType.IMAGE, width = 30, height = 80)
|
||||
private String invoiceImage;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.jsowell.pile.vo.web;
|
||||
|
||||
import com.jsowell.common.annotation.Excel;
|
||||
import com.jsowell.common.annotation.Excel.ColumnType;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 申请开票导入对象
|
||||
*/
|
||||
@Data
|
||||
public class OrderInvoiceRecordImportVO {
|
||||
@Excel(name = "申请记录ID")
|
||||
private Integer id;
|
||||
|
||||
@Excel(name = "发票图片", cellType = ColumnType.IMAGE)
|
||||
private String invoiceImage;
|
||||
}
|
||||
@@ -18,6 +18,7 @@ public class OrderInvoiceRecordVO {
|
||||
private String merchantName;
|
||||
private String titleId;
|
||||
private String email;
|
||||
private String invoiceUrl;
|
||||
private String orderCodes;
|
||||
private String status;
|
||||
private String createTime;
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
<result property="titleId" column="title_id" />
|
||||
<result property="phoneNumber" column="phone_number" />
|
||||
<result property="email" column="email" />
|
||||
<result property="invoiceUrl" column="invoice_url" />
|
||||
<result property="orderCodes" column="order_codes" />
|
||||
<result property="status" column="status" />
|
||||
<result property="totalAmount" column="total_amount" />
|
||||
@@ -25,7 +26,7 @@
|
||||
|
||||
<sql id="Base_Column_List">
|
||||
<!--@mbg.generated-->
|
||||
id, member_id, merchant_id, title_id, phone_number, email, order_codes, status, total_amount, total_service_amount, total_elec_amount,
|
||||
id, member_id, merchant_id, title_id, phone_number, email, invoice_url, order_codes, status, total_amount, total_service_amount, total_elec_amount,
|
||||
create_by, create_time, update_by, update_time, del_flag
|
||||
</sql>
|
||||
|
||||
@@ -59,6 +60,7 @@
|
||||
<if test="titleId != null">title_id,</if>
|
||||
<if test="phoneNumber != null">phone_number,</if>
|
||||
<if test="email != null">email,</if>
|
||||
<if test="invoiceUrl != null">invoice_url,</if>
|
||||
<if test="orderCodes != null">order_codes,</if>
|
||||
<if test="status != null">status,</if>
|
||||
<if test="totalAmount != null">total_amount,</if>
|
||||
@@ -76,6 +78,7 @@
|
||||
<if test="titleId != null">#{titleId},</if>
|
||||
<if test="phoneNumber != null">#{phoneNumber},</if>
|
||||
<if test="email != null">#{email},</if>
|
||||
<if test="invoiceUrl != null">#{invoiceUrl},</if>
|
||||
<if test="orderCodes != null">#{orderCodes},</if>
|
||||
<if test="status != null">#{status},</if>
|
||||
<if test="totalAmount != null">#{totalAmount},</if>
|
||||
@@ -94,6 +97,10 @@
|
||||
<trim prefix="SET" suffixOverrides=",">
|
||||
<if test="memberId != null">member_id = #{memberId},</if>
|
||||
<if test="merchantId != null">merchant_id = #{merchantId},</if>
|
||||
<if test="titleId != null">title_id = #{titleId},</if>
|
||||
<if test="phoneNumber != null">phone_number = #{phoneNumber},</if>
|
||||
<if test="email != null">email = #{email},</if>
|
||||
<if test="invoiceUrl != null">invoice_url = #{invoiceUrl},</if>
|
||||
<if test="orderCodes != null">order_codes = #{orderCodes},</if>
|
||||
<if test="status != null">status = #{status},</if>
|
||||
<if test="totalAmount != null">total_amount = #{totalAmount},</if>
|
||||
@@ -145,6 +152,7 @@
|
||||
t2.merchant_name as merchantName,
|
||||
t1.title_id as titleId,
|
||||
t1.email as email,
|
||||
t1.invoice_url as invoiceUrl,
|
||||
t1.order_codes as orderCodes,
|
||||
t1.STATUS as status,
|
||||
t1.create_time as createTime,
|
||||
@@ -169,6 +177,52 @@
|
||||
order by t1.create_time desc
|
||||
</select>
|
||||
|
||||
<select id="getInvoiceExportListWithAuth" resultType="com.jsowell.pile.vo.web.OrderInvoiceRecordExportVO">
|
||||
SELECT
|
||||
t1.id AS id,
|
||||
t1.member_id AS memberId,
|
||||
t4.mobile_number AS memberPhoneNumber,
|
||||
t2.merchant_name AS merchantName,
|
||||
t3.title_type AS titleType,
|
||||
t3.name AS titleName,
|
||||
t3.tax_id AS taxId,
|
||||
t3.unit_address AS unitAddress,
|
||||
t3.phone_number AS titlePhoneNumber,
|
||||
t3.email AS email,
|
||||
t3.bank_name AS bankName,
|
||||
t3.bank_account_number AS bankAccountNumber,
|
||||
t1.order_codes AS orderCodes,
|
||||
t1.status AS status,
|
||||
t1.total_amount AS totalAmount,
|
||||
t1.total_service_amount AS totalServiceAmount,
|
||||
t1.total_elec_amount AS totalElecAmount,
|
||||
t1.create_time AS createTime,
|
||||
t1.update_time AS updateTime,
|
||||
t1.invoice_url AS invoiceImage
|
||||
FROM order_invoice_record t1
|
||||
LEFT JOIN pile_merchant_info t2 ON t1.merchant_id = t2.id
|
||||
LEFT JOIN member_invoice_title t3 ON t1.title_id = t3.id AND t3.del_flag = '0'
|
||||
LEFT JOIN member_basic_info t4 ON t1.member_id = t4.member_id
|
||||
WHERE t1.del_flag = '0'
|
||||
<if test="dto.merchantDeptIds != null and dto.merchantDeptIds.size() != 0">
|
||||
and t2.dept_id in
|
||||
<foreach collection="dto.merchantDeptIds" item="deptId" open="(" separator="," close=")">
|
||||
#{deptId,jdbcType=VARCHAR}
|
||||
</foreach>
|
||||
</if>
|
||||
<if test="dto.memberId != null and dto.memberId != ''"> and t1.member_id = #{dto.memberId}</if>
|
||||
<if test="dto.merchantId != null and dto.merchantId != ''"> and t1.merchant_id = #{dto.merchantId}</if>
|
||||
<if test="dto.orderCodes != null and dto.orderCodes != ''"> and t1.order_codes like concat('%', #{dto.orderCodes}, '%')</if>
|
||||
<if test="dto.status != null and dto.status != ''"> and t1.status = #{dto.status}</if>
|
||||
<if test="dto.ids != null and dto.ids.length != 0">
|
||||
and t1.id in
|
||||
<foreach collection="dto.ids" item="id" open="(" separator="," close=")">
|
||||
#{id,jdbcType=INTEGER}
|
||||
</foreach>
|
||||
</if>
|
||||
order by t1.create_time desc
|
||||
</select>
|
||||
|
||||
<select id="selectInvoiceVOList" resultType="com.jsowell.pile.vo.web.OrderInvoiceRecordVO">
|
||||
SELECT
|
||||
t1.member_id,
|
||||
@@ -204,6 +258,7 @@
|
||||
t2.mobile_number AS phoneNumber,
|
||||
t1.merchant_id AS merchantId,
|
||||
t1.title_id AS titleId,
|
||||
t1.invoice_url AS invoiceUrl,
|
||||
t1.order_codes AS orderCodes,
|
||||
t1.STATUS,
|
||||
t1.total_amount AS totalAmount,
|
||||
@@ -216,4 +271,4 @@
|
||||
AND t1.del_flag = '0'
|
||||
where t1.id = #{id,jdbcType=INTEGER}
|
||||
</select>
|
||||
</mapper>
|
||||
</mapper>
|
||||
|
||||
2
sql/20260415_add_invoice_url_to_order_invoice_record.sql
Normal file
2
sql/20260415_add_invoice_url_to_order_invoice_record.sql
Normal file
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE order_invoice_record
|
||||
ADD COLUMN invoice_url varchar(500) DEFAULT NULL COMMENT '发票图片url' AFTER email;
|
||||
Reference in New Issue
Block a user