10 Commits

Author SHA1 Message Date
Guoqs
98bc86ef43 增加发票导入功能 2026-04-15 12:28:02 +08:00
Guoqs
d9d60dd677 Merge branch 'feature-export_application_record' of codeup.aliyun.com:67c68d4e484ca2f0a13ac3c1/ydc/jsowell-charger-web into feature-export_application_record 2026-04-15 10:31:11 +08:00
Guoqs
62bcad3007 update 2026-04-15 10:30:58 +08:00
Guoqs
fc3a941617 Merge branch 'dev' into feature-export_application_record 2026-04-15 10:07:39 +08:00
Guoqs
4a6d569f43 update dev nacos信息 2026-04-15 10:07:29 +08:00
Lemon
392b68b305 update sit环境更改redis密码 2026-04-14 15:48:31 +08:00
Guoqs
1c30406323 Merge branch 'dev' into feature-export_application_record 2026-04-14 13:30:44 +08:00
Lemon
18308f82cd Merge branch 'feature-business-minigram' into dev 2026-04-10 14:40:37 +08:00
Guoqs
7df9283481 update 导出申请记录 2026-04-10 14:34:49 +08:00
Guoqs
ab8930ea4c update 2026-04-08 14:30:11 +08:00
16 changed files with 603 additions and 18 deletions

View File

@@ -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);
}
/**
* 获取申请开票详细信息
*/

View File

@@ -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

View File

@@ -48,7 +48,7 @@ spring:
# 数据库索引
database: 0
# 密码
password: 123456
password: js160829
# 连接超时时间
timeout: 10s
lettuce:

View File

@@ -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;
}
}
}

View File

@@ -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();

View File

@@ -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())

View File

@@ -15,6 +15,11 @@ public class GetInvoiceInfoDTO {
private Integer pageNum;
private Integer pageSize;
/**
* 勾选导出的申请开票记录id
*/
private Integer[] ids;
/**
* 会员id
*/

View File

@@ -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);
/**
* 新增申请开票
*

View File

@@ -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);

View File

@@ -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

View File

@@ -44,6 +44,11 @@ public class InvoiceRecordVO {
*/
private String updateTime;
/**
* 发票图片url
*/
private String invoiceUrl;
/**
* 发票抬头信息
*/

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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>

View File

@@ -0,0 +1,2 @@
ALTER TABLE order_invoice_record
ADD COLUMN invoice_url varchar(500) DEFAULT NULL COMMENT '发票图片url' AFTER email;