1、发票抬头管理、可开票列表部分接口完善;2、微信支付发起和回调测试;

This commit is contained in:
gaoqr
2025-08-22 15:52:41 +08:00
parent 8cc23e8e43
commit 20f7fde7cd
31 changed files with 748 additions and 59 deletions
+2 -1
View File
@@ -78,6 +78,7 @@
<jimureport.version>1.9.4</jimureport.version>
<xercesImpl.version>2.12.2</xercesImpl.version>
<weixin-java.version>4.7.5.B</weixin-java.version>
<weixin-pay-java.version>4.7.6.B</weixin-pay-java.version>
<!-- 拼音 -->
<pingyinfj.version>2.5.1</pingyinfj.version>
@@ -608,7 +609,7 @@
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-pay</artifactId>
<version>${weixin-java.version}</version>
<version>${weixin-pay-java.version}</version>
</dependency>
<dependency>
<groupId>com.github.binarywang</groupId>
@@ -19,6 +19,11 @@ public class ChenfengWechatPayConfig extends WxPayConfig {
@Value("${chenfeng.encrypt.publicKey:}")
private String publicKey;
/**
* 给微信支付的回调地址
*/
private String notifyUrl;
@Override
public void setAppId(String appId) {
if (StringUtils.isNotEmpty(publicKey)) {
@@ -47,13 +52,56 @@ public class ChenfengWechatPayConfig extends WxPayConfig {
}
@Override
public void setKeyContent(byte[] keyContent) {
public void setApiV3Key(String apiV3Key) {
if (StringUtils.isNotEmpty(publicKey)) {
String encrypted = new String(keyContent, StandardCharsets.UTF_8);
String decrypted = AesUtils.decrypt(encrypted, publicKey);
super.setKeyContent(decrypted.getBytes(StandardCharsets.UTF_8));
super.setApiV3Key(AesUtils.decrypt(apiV3Key, publicKey));
} else {
super.setKeyContent(keyContent);
super.setApiV3Key(apiV3Key);
}
}
@Override
public void setCertSerialNo(String certSerialNo) {
if (StringUtils.isNotEmpty(publicKey)) {
super.setCertSerialNo(AesUtils.decrypt(certSerialNo, publicKey));
} else {
super.setCertSerialNo(certSerialNo);
}
}
@Override
public void setPublicKeyId(String publicKeyId) {
if (StringUtils.isNotEmpty(publicKey)) {
super.setPublicKeyId(AesUtils.decrypt(publicKeyId, publicKey));
} else {
super.setPublicKeyId(publicKeyId);
}
}
@Override
public void setNotifyUrl(String notifyUrl) {
if (StringUtils.isNotEmpty(publicKey)) {
super.setNotifyUrl(AesUtils.decrypt(notifyUrl, publicKey));
} else {
super.setNotifyUrl(notifyUrl);
}
}
@Override
public void setPublicKeyString(String publicKeyString) {
if (StringUtils.isNotEmpty(publicKey)) {
super.setPublicKeyContent(AesUtils.decrypt(publicKeyString, publicKey).getBytes(StandardCharsets.UTF_8));
} else {
super.setPublicKeyContent(publicKeyString.getBytes(StandardCharsets.UTF_8));
}
}
@Override
public void setPrivateKeyString(String privateKeyString) {
if (StringUtils.isNotEmpty(publicKey)) {
super.setPrivateKeyContent(AesUtils.decrypt(privateKeyString, publicKey).getBytes(StandardCharsets.UTF_8));
} else {
super.setPrivateKeyContent(privateKeyString.getBytes(StandardCharsets.UTF_8));
}
}
}
@@ -437,6 +437,7 @@ public class ErrorCodeConstants {
public static final ErrorCode ORGAN_PRODUCT_PAY_AMOUNT_CHANGE_ERROR = new ErrorCode(1_002_043_025, "发起产品支付失败,组织资金已发生改变,请刷新或稍后再发起支付");
public static final ErrorCode PAY_RECHARGE_ACTIVITY_CHANNEL_MISMATCH_ERROR = new ErrorCode(1_002_043_026, "充值活动类型和支付渠道不匹配,请选择其他充值活动!");
public static final ErrorCode PAY_RECHARGE_ACTIVITY_EXPIRE_ERROR = new ErrorCode(1_002_043_027, "充值活动已结束,请选择其他充值活动!");
public static final ErrorCode INVOICE_TITLE_NOT_EXISTS_ERROR = new ErrorCode(1_002_043_028, "发票抬头不存在,请先创建发票抬头");
//=========== 支付相关 1-002-048-000 ============
public static final ErrorCode PAY_CHANNEL_NOT_SUPPORT = new ErrorCode(1_002_048_000, "暂不支持的支付渠道,请使用其他支付渠道");
@@ -2,11 +2,14 @@ package com.cf.imes.module.system.controller.admin.funds.invoice;
import com.cf.imes.framework.common.pojo.CommonResult;
import com.cf.imes.module.system.controller.admin.funds.invoice.vo.InvoiceAmountAndAvailablePageReqVO;
import com.cf.imes.module.system.controller.admin.funds.invoice.vo.InvoiceAmountAndAvailablePageRespVO;
import com.cf.imes.module.system.controller.admin.funds.invoice.vo.InvoiceTitleReqVO;
import com.cf.imes.module.system.service.funds.invoice.InvoiceService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
@@ -32,15 +35,16 @@ public class InvoiceController {
private InvoiceService invoiceService;
@PostMapping("")
@PostMapping("/title")
@Operation(summary = "新增/修改发票抬头信息")
public CommonResult<Boolean> insetInvoiceTitle(@Valid @RequestBody InvoiceTitleReqVO reqVO) {
invoiceService.insetInvoiceTitle(reqVO);
return success(Boolean.TRUE);
}
@GetMapping("/available/info")
@Operation(summary = "获取组织的开票金额信息")
public CommonResult<InvoiceAmountAndAvailablePageRespVO> getInvoiceAmountAndAvailablePage(@Valid InvoiceAmountAndAvailablePageReqVO pageReqVO) {
return success(invoiceService.getInvoiceAmount(pageReqVO));
}
}
@@ -0,0 +1,29 @@
package com.cf.imes.module.system.controller.admin.funds.invoice.vo;
import com.cf.imes.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static com.cf.imes.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
/**
* @author Gqr
* @since 2025/8/21 16:11
*/
@Data
@Schema(description = "管理后台 - 组织开票金额和可开票列表 Req VO")
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class InvoiceAmountAndAvailablePageReqVO extends PageParam {
@Schema(description = "费用账期", example = "[2022-07-01 01:01:01 ,2022-07-01 01:01:01]")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
@Schema(description = "开票金额", example = "[0,100]")
private Integer[] invoiceAmount;
}
@@ -0,0 +1,20 @@
package com.cf.imes.module.system.controller.admin.funds.invoice.vo;
import com.cf.imes.framework.common.pojo.PageResult;
import com.cf.imes.module.system.dal.dataobject.funds.purchase.PurchaseRecordDO;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* @author Gqr
* @since 2025/8/21 16:11
*/
@Data
@Schema(description = "管理后台 - 组织开票金额和可开票列表 Response VO")
public class InvoiceAmountAndAvailablePageRespVO {
@Schema(description = "发票金额信息")
private InvoiceAmountRespVO invoiceAmountRespVO;
@Schema(description = "可开票记录")
private PageResult<PurchaseRecordDO> availableInvoicePurchaseRecords;
}
@@ -0,0 +1,23 @@
package com.cf.imes.module.system.controller.admin.funds.invoice.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
@Data
public class InvoiceAmountRespVO {
@Schema(description = "可开票")
private BigDecimal invocable;
@Schema(description = "开票中")
private BigDecimal inTheInvoice;
@Schema(description = "已开票")
private BigDecimal invoiced;
}
@@ -0,0 +1,61 @@
package com.cf.imes.module.system.controller.admin.funds.invoice.vo;
import com.cf.imes.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.springframework.format.annotation.DateTimeFormat;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import static com.cf.imes.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 组织可开票的数据明细 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@Builder
public class InvoiceDetailsPageReqVO extends PageParam {
@Schema(description = "组织ID")
private Long organId;
@Schema(description = "发票号")
private String invoiceNo;
@Schema(description = "发票状态,1待开票 2已开票 3开票失败")
private Integer status;
@Schema(description = "申请人")
private String creator;
@Schema(description = "开票人")
private String invoicePerson;
@Schema(description = "发票金额")
private BigDecimal[] invoiceAmount;
@Schema(description = "开票时间", example = "[2022-07-01 01:01:01 ,2022-07-01 01:01:01]")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] invoiceTime;
@Schema(description = "交易时间", example = "[2022-07-01 01:01:01 ,2022-07-01 01:01:01]")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
@Schema(description = "交易金额")
private BigDecimal[] amount;
}
@@ -0,0 +1,28 @@
package com.cf.imes.module.system.controller.admin.funds.invoice.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class InvoiceDetailsRespVO {
@Schema(description = "发票对应的购买记录")
private List<InvoicePurchaseRecord> invoicePurchaseRecords;
@Schema(description = "发票抬头信息")
private InvoiceTitleInfo invoiceTitleInfo;
}
@@ -0,0 +1,38 @@
package com.cf.imes.module.system.controller.admin.funds.invoice.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Data
public class InvoicePurchaseRecord {
@Schema(description = "流水号")
private Long businessNo;
@Schema(description = "购买记录ID")
private Long purchaseRecordId;
@Schema(description = "产品名称")
private String productName;
@Schema(description = "交易类型")
private Integer tradeType;
@Schema(description = "可开票金额")
private BigDecimal rechargeAmount;
@Schema(description = "交易时间")
private LocalDateTime createTime;
}
@@ -0,0 +1,55 @@
package com.cf.imes.module.system.controller.admin.funds.invoice.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
public class InvoiceTitleInfo {
@Schema(description = "发票抬头ID")
private Long id;
@Schema(description = "组织ID")
private Long organId;
@Schema(description = "发票抬头")
private String invoiceTitle;
@Schema(description = "开具类型,0企业 1个人")
private Integer issueType;
@Schema(description = "发票类型,0增值税普通发票 1增值税专用发票")
private Integer invoiceType;
@Schema(description = "纳税人识别号")
private String taxpayerId;
@Schema(description = "开户银行名称")
private String bankName;
@Schema(description = "开户银行账号")
private String bankAccount;
@Schema(description = "注册场所地址")
private String registeredAddress;
@Schema(description = "注册固定电话")
private String fixedPhone;
@Schema(description = "邮箱")
private String email;
}
@@ -1,7 +1,11 @@
package com.cf.imes.module.system.controller.admin.funds.invoice.vo;
import com.cf.imes.module.system.validation.invoice.InvoiceIssueTypeValid;
import com.cf.imes.module.system.validation.invoice.InvoiceTypeValid;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.Size;
import lombok.Data;
import jakarta.validation.constraints.NotBlank;
@@ -16,47 +20,58 @@ public class InvoiceTitleReqVO {
@Schema(description = "组织ID")
@NotNull(message = "组织ID不能为空")
private Long organId;
@Schema(description = "发票抬头")
@NotBlank(message = "发票抬头不能为空")
@Size(max = 64, message = "发票抬头长度不能超过64个字符")
private String invoiceTitle;
@Schema(description = "开具类型,0企业 1个人")
@NotNull(message = "开具类型不能为空")
@InvoiceIssueTypeValid
private Integer issueType;
@Schema(description = "发票类型,0增值税普通发票 1增值税专用发票")
@NotNull(message = "发票类型不能为空")
@InvoiceTypeValid
private Integer invoiceType;
@Schema(description = "纳税人识别号")
@NotNull(message = "纳税人识别号不能为空")
@Size(max = 20, message = "纳税人识别号长度不能超过20个字符")
private String taxpayerId;
@Schema(description = "开户银行名称")
@Size(max = 64, message = "开户银行名称长度不能超过64个字符")
private String bankName;
@Schema(description = "开户银行账号")
@Size(max = 40, message = "开户银行账号长度不能超过40个字符")
private String bankAccount;
@Schema(description = "注册场所地址")
@Size(max = 64, message = "注册场所地址长度不能超过64个字符")
private String registeredAddress;
@Schema(description = "注册固定电话")
@Size(max = 20, message = "注册固定电话长度不能超过20个字符")
private String fixedPhone;
@Schema(description = "邮箱")
@NotNull(message = "邮箱不能为空")
@Size(max = 64, message = "邮箱长度不能超过64个字符")
@Email(message = "邮箱格式不正确")
private String email;
@@ -14,6 +14,7 @@ import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@@ -67,9 +68,10 @@ public class PayController {
@PermitAll
public String notifyOrder(@PathVariable("channelCode") Integer channelCode,
@RequestParam(required = false) Map<String, String> params,
@RequestBody(required = false) String body) {
log.info("[notifyOrder][channelCode({}) 回调数据({}/{})]", channelCode, params, body);
payOrderService.doOrderNotify(channelCode, params, body);
@RequestBody(required = false) String body,
@RequestHeader Map<String, String> headers) {
log.info("[notifyOrder][channelCode({}) 回调数据({}/{}/{})]", channelCode, params, body, headers);
payOrderService.doOrderNotify(channelCode, params, body, headers);
// 支付接口回调相应必须是"success"
return "success";
}
@@ -1,10 +1,15 @@
package com.cf.imes.module.system.dal.mysql.funds.purchase;
import cn.hutool.core.util.ObjectUtil;
import com.cf.imes.framework.common.pojo.PageResult;
import com.cf.imes.framework.mybatis.core.mapper.BaseMapperX;
import com.cf.imes.framework.mybatis.core.query.LambdaQueryWrapperX;
import com.cf.imes.framework.security.core.util.SecurityFrameworkUtils;
import com.cf.imes.module.system.controller.admin.funds.invoice.vo.InvoiceAmountAndAvailablePageReqVO;
import com.cf.imes.module.system.controller.admin.funds.invoice.vo.InvoiceAmountRespVO;
import com.cf.imes.module.system.controller.admin.funds.purchase.vo.PurchaseRecordPageReqVO;
import com.cf.imes.module.system.dal.dataobject.funds.purchase.PurchaseRecordDO;
import com.cf.imes.module.system.enums.pay.InvoiceStatusEnum;
import org.apache.ibatis.annotations.Mapper;
import java.math.BigDecimal;
@@ -38,4 +43,29 @@ public interface PurchaseRecordMapper extends BaseMapperX<PurchaseRecordDO> {
.likeIfPresent(PurchaseRecordDO::getCreator, pageReqVO.getCreator()));
}
/**
* 分页查询可开票记录
*
* @param pageReqVO
* @return
*/
default PageResult<PurchaseRecordDO> selectPage(InvoiceAmountAndAvailablePageReqVO pageReqVO) {
return selectPage(pageReqVO, new LambdaQueryWrapperX<PurchaseRecordDO>()
.betweenIfPresent(PurchaseRecordDO::getCreateTime, pageReqVO.getCreateTime())
.geIfPresent(PurchaseRecordDO::getTotalAmount, ObjectUtil.isNotNull(pageReqVO.getInvoiceAmount()) ? pageReqVO.getInvoiceAmount()[0] : null)
.leIfPresent(PurchaseRecordDO::getTotalAmount, ObjectUtil.isNotNull(pageReqVO.getInvoiceAmount()) ? pageReqVO.getInvoiceAmount()[1] : null)
.eq(PurchaseRecordDO::getOrganId, SecurityFrameworkUtils.getUserOrganId())
.eq(PurchaseRecordDO::getDeleted, false)
.in(PurchaseRecordDO::getIsInvocing, InvoiceStatusEnum.INVOICABLE.getStatus(), InvoiceStatusEnum.INVOICINGFAILED.getStatus())
);
}
/**
* 查询机构下开票金额统计:可开票、开票中、已开票
* @param organId
* @return
*/
InvoiceAmountRespVO getOrgInvoiceAmountStatistics(Long organId);
}
@@ -0,0 +1,25 @@
package com.cf.imes.module.system.enums.pay;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 发票开具类型枚举
* @author Gqr
* @since 2025/8/21 12:00
*/
@Getter
@AllArgsConstructor
public enum InvoiceIssueTypeEnum {
/**
* 企业
*/
ENTERPRISE(0),
/**
* 个人
*/
PERSONAL(1);
private final Integer value;
}
@@ -0,0 +1,28 @@
package com.cf.imes.module.system.enums.pay;
/**
* 发票类型枚举
* @author Gqr
* @since 2025/8/21 12:03
*/
public enum InvoiceTypeEnum {
/**
* 增值税普通发票
*/
VAT_GENERAL(0),
/**
* 增值税专用发票
*/
VAT_SPECIAL(1);
private final Integer value;
InvoiceTypeEnum(Integer value) {
this.value = value;
}
public Integer getValue() {
return value;
}
}
@@ -11,7 +11,7 @@ public class PayConstants {
public static final String PAY_PASSBACK_PARAMS_KEY = "passback_params";// 回调参数passback key
public static final String PAY_ORDER_PREFIX_FORMAT = "P%dO%s%s";
public static final String PAY_ORDER_PREFIX_FORMAT = "P%dO%s";
public static final String PAY_PASSBACK_PARAMS_PURCHASE_RECORD_ID_KEY = "purchaseRecordId"; //支付passback 购买记录key
}
@@ -1,6 +1,11 @@
package com.cf.imes.module.system.service.funds.invoice;
import com.cf.imes.framework.common.pojo.PageResult;
import com.cf.imes.module.system.controller.admin.funds.invoice.vo.InvoiceAmountAndAvailablePageReqVO;
import com.cf.imes.module.system.controller.admin.funds.invoice.vo.InvoiceAmountAndAvailablePageRespVO;
import com.cf.imes.module.system.controller.admin.funds.invoice.vo.InvoiceDetailsPageReqVO;
import com.cf.imes.module.system.controller.admin.funds.invoice.vo.InvoiceDetailsRespVO;
import com.cf.imes.module.system.controller.admin.funds.invoice.vo.InvoiceTitleReqVO;
/**
@@ -8,5 +13,26 @@ import com.cf.imes.module.system.controller.admin.funds.invoice.vo.InvoiceTitleR
*/
public interface InvoiceService {
/**
* 新增/修改抬头信息
*
* @param reqVO
*/
void insetInvoiceTitle(InvoiceTitleReqVO reqVO);
/**
* 查询组织开票金额和可开票列表
*
* @param invoiceAmountAndAvailablePageReqVO
* @return
*/
InvoiceAmountAndAvailablePageRespVO getInvoiceAmount(InvoiceAmountAndAvailablePageReqVO invoiceAmountAndAvailablePageReqVO);
/**
* 查询开票分页
*
* @param pageReqVO
* @return
*/
PageResult<InvoiceDetailsRespVO> getInvoiceDetails(InvoiceDetailsPageReqVO pageReqVO);
}
@@ -1,40 +1,55 @@
package com.cf.imes.module.system.service.funds.invoice;
import com.cf.imes.framework.common.pojo.PageResult;
import com.cf.imes.framework.common.util.Assert.AssertUtils;
import com.cf.imes.framework.common.util.object.BeanUtils;
import com.cf.imes.framework.security.core.util.SecurityFrameworkUtils;
import com.cf.imes.module.system.controller.admin.funds.invoice.vo.InvoiceAmountAndAvailablePageReqVO;
import com.cf.imes.module.system.controller.admin.funds.invoice.vo.InvoiceAmountAndAvailablePageRespVO;
import com.cf.imes.module.system.controller.admin.funds.invoice.vo.InvoiceDetailsPageReqVO;
import com.cf.imes.module.system.controller.admin.funds.invoice.vo.InvoiceDetailsRespVO;
import com.cf.imes.module.system.controller.admin.funds.invoice.vo.InvoiceTitleReqVO;
import com.cf.imes.module.system.dal.dataobject.funds.invoice.InvoiceTitleInfoDO;
import com.cf.imes.module.system.dal.mysql.funds.invoice.InvoiceTitleInfoMapper;
import com.cf.imes.module.system.dal.mysql.funds.purchase.PurchaseRecordMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import jakarta.annotation.Resource;
import static com.cf.imes.module.system.enums.ErrorCodeConstants.INVOICE_TITLE_NOT_EXISTS_ERROR;
/**
* @author 发票管理接口 实现类
*/
@Slf4j
@Service
public class InvoiceServiceImpl implements InvoiceService{
public class InvoiceServiceImpl implements InvoiceService {
@Resource
private InvoiceTitleInfoMapper invoiceTitleInfoMapper;
@Resource
private PurchaseRecordMapper purchaseRecordMapper;
@Override
public void insetInvoiceTitle(InvoiceTitleReqVO reqVO) {
if(reqVO.getId() != null){
if (reqVO.getId() != null) {
validTitleOrg(reqVO.getId(), reqVO.getOrganId());
// 更新抬头
InvoiceTitleInfoDO titleInfoDO = BeanUtils.toBean(reqVO, InvoiceTitleInfoDO.class);
invoiceTitleInfoMapper.updateById(titleInfoDO);
return;
}
// 新增抬头
invoiceTitleInfoMapper.insert(InvoiceTitleInfoDO.builder()
.organId(reqVO.getOrganId())
.invoiceTitle(reqVO.getInvoiceTitle())
.invoiceType(reqVO.getInvoiceType())
.issueType(reqVO.getIssueType())
@@ -45,6 +60,63 @@ public class InvoiceServiceImpl implements InvoiceService{
.fixedPhone(reqVO.getFixedPhone())
.email(reqVO.getEmail())
.build());
}
/**
* 校验抬头组织
*
* @param id
* @param organId
*/
private void validTitleOrg(Long id, Long organId) {
InvoiceTitleInfoDO titleInfoDO = invoiceTitleInfoMapper.selectById(id);
// 抬头不存在
AssertUtils.notEmpty(titleInfoDO, INVOICE_TITLE_NOT_EXISTS_ERROR);
// 抬头组织不匹配
AssertUtils.notEquals(titleInfoDO.getOrganId(), organId, INVOICE_TITLE_NOT_EXISTS_ERROR);
}
@Override
public InvoiceAmountAndAvailablePageRespVO getInvoiceAmount(InvoiceAmountAndAvailablePageReqVO invoiceAmountAndAvailablePageReqVO) {
InvoiceAmountAndAvailablePageRespVO invoiceAmountAndAvailablePageRespVO = new InvoiceAmountAndAvailablePageRespVO();
// 开票金额
invoiceAmountAndAvailablePageRespVO.setInvoiceAmountRespVO(purchaseRecordMapper.getOrgInvoiceAmountStatistics(SecurityFrameworkUtils.getUserOrganId()));
// 可开票记录查询
invoiceAmountAndAvailablePageRespVO.setAvailableInvoicePurchaseRecords(purchaseRecordMapper.selectPage(invoiceAmountAndAvailablePageReqVO));
return invoiceAmountAndAvailablePageRespVO;
}
@Override
public PageResult<InvoiceDetailsRespVO> getInvoiceDetails(InvoiceDetailsPageReqVO pageReqVO) {
//
// PageDTO<InvoiceDetailsRespVO> page = new PageDTO<>(pageReqVO.getPageNo(), pageReqVO.getPageSize());
//
// List<Integer> invoiceStatus = new ArrayList<>();
// invoiceStatus.add(InvoiceStatusEnum.INVOICABLE.getStatus());
// invoiceStatus.add(InvoiceStatusEnum.INVOICINGFAILED.getStatus());
//
// QueryWrapperX<IncomeExpenseDetailsDO> queryWrapperX = new QueryWrapperX<>();
//
// pageReqVO.setCreateTime(LocalDateTimeUtils.generateTimeSection(pageReqVO.getCreateTime()));
// queryWrapperX
// .eq("ied.organId",getUserOrganId())
// .eq("ied.deleted",false)
// .eq("ied.trade_type", TradeTypeEnum.PRODUCT.getCode())
// .in("pr.is_invocing", invoiceStatus)
// .betweenIfPresent("ied.create_time",pageReqVO.getCreateTime())
// .betweenIfPresent("ied.cash_amount_change",pageReqVO.getAmount());
//
// IPage<InvoiceDetailsRespVO> respVOIPage = incomeExpenseDetailsMapper.selectOrganInvoicable(page, queryWrapperX);
//
// List<InvoiceDetailsRespVO> records = respVOIPage.getRecords();
// if (CollUtil.isEmpty(records)) {
// return new PageResult<>();
// }
//
// return new PageResult<>(records, respVOIPage.getTotal());
return null;
}
@@ -43,7 +43,7 @@ public interface PayOrderService {
* @param body
* @return
*/
void doOrderNotify(Integer channelCode, Map<String, String> params, String body);
void doOrderNotify(Integer channelCode, Map<String, String> params, String body, Map<String, String> headers);
/**
* 处理过期订单
@@ -1,7 +1,5 @@
package com.cf.imes.module.system.service.pay;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
@@ -320,7 +318,7 @@ public class PayOrderServiceImpl implements PayOrderService {
Long organId = SecurityFrameworkUtils.getUserOrganId();
payOrderDO.setId(payOrderId);
payOrderDO.setPayNo(String.format(PayConstants.PAY_ORDER_PREFIX_FORMAT, organId, payOrderId, DateUtil.format(LocalDateTime.now(), DatePattern.PURE_DATETIME_PATTERN)));
payOrderDO.setPayNo(String.format(PayConstants.PAY_ORDER_PREFIX_FORMAT, organId, payOrderId));
payOrderDO.setStatus(PayOrderStatusEnum.WAITING);
payOrderDO.setExpireTime(LocalDateTime.now().plus(payConfig.getPayExpireTime()));
payOrderDO.setPrice(price);
@@ -385,18 +383,18 @@ public class PayOrderServiceImpl implements PayOrderService {
@Override
@Transactional(rollbackFor = Exception.class)
public void doOrderNotify(Integer channelCode, Map<String, String> params, String body) {
public void doOrderNotify(Integer channelCode, Map<String, String> params, String body, Map<String, String> headers) {
// 获取渠道方处理器
AbstractPayHandler payHandler = payHandlerFactory.getPayHandler(channelCode);
try {
// 解析回调数据
PayOrderRespDTO payOrderRespDTO = payHandler.doParseOrderNotify(params, body);
PayOrderRespDTO payOrderRespDTO = payHandler.doParseOrderNotify(params, body, headers);
// 处理支付回调数据
this.notifyOrder(payOrderRespDTO);
} catch (Throwable e) {
log.error("[parseOrderNotify][客户端({}) params({}) body({}) headers({}) 解析失败]", channelCode, params, body, e);
log.error("[parseOrderNotify][客户端({}) params({}) body({}) headers({}) 解析失败]", channelCode, params, body, headers, e);
throw new PayException(PAY_ORDER_NOTIFY_DATA_PARSE_ERROR);
}
}
@@ -36,5 +36,5 @@ public abstract class AbstractPayHandler {
* @param body
* @return
*/
public abstract PayOrderRespDTO doParseOrderNotify(Map<String, String> params, String body) throws Throwable;
public abstract PayOrderRespDTO doParseOrderNotify(Map<String, String> params, String body, Map<String, String> headers) throws Throwable;
}
@@ -108,7 +108,7 @@ public class AlipayPcPayHandler extends AbstractPayHandler {
}
@Override
public PayOrderRespDTO doParseOrderNotify(Map<String, String> params, String body) throws Throwable {
public PayOrderRespDTO doParseOrderNotify(Map<String, String> params, String body, Map<String, String> headers) throws Throwable {
// 1. 校验回调数据
verifyNotifyData(params);
@@ -2,6 +2,7 @@ package com.cf.imes.module.system.service.pay.factory.impl.wechat;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.date.TemporalAccessorUtil;
import cn.hutool.core.text.CharSequenceUtil;
import com.alibaba.fastjson.JSON;
import com.cf.imes.framework.common.util.json.JsonUtils;
import com.cf.imes.framework.pay.config.ChenfengWechatPayConfig;
@@ -10,9 +11,10 @@ import com.cf.imes.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
import com.cf.imes.framework.pay.core.enums.order.PayOrderDisplayModeEnum;
import com.cf.imes.module.system.enums.pay.PayOrderStatusEnum;
import com.cf.imes.module.system.service.pay.factory.impl.AbstractPayHandler;
import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
import com.github.binarywang.wxpay.bean.order.WxPayAppOrderResult;
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
import com.github.binarywang.wxpay.bean.notify.SignatureHeader;
import com.github.binarywang.wxpay.bean.notify.WxPayNotifyV3Result;
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderV3Request;
import com.github.binarywang.wxpay.bean.result.enums.TradeTypeEnum;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl;
@@ -23,9 +25,9 @@ import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Map;
import java.util.Objects;
import static cn.hutool.core.date.DatePattern.PURE_DATETIME_PATTERN;
import static cn.hutool.core.date.DatePattern.UTC_WITH_XXX_OFFSET_PATTERN;
import static com.cf.imes.framework.common.util.servlet.ServletUtils.getClientIP;
/**
* 微信native支付处理器
@@ -43,22 +45,21 @@ public class WechatNativePayHandler extends AbstractPayHandler {
@Override
public PayOrderRespDTO unifiedOrder(PayOrderUnifiedReqDTO reqDTO) throws WxPayException {
// 构建 WxPayUnifiedOrderRequest 对象
WxPayUnifiedOrderRequest request = WxPayUnifiedOrderRequest.newBuilder()
.outTradeNo(reqDTO.getOutTradeNo())
.body(reqDTO.getSubject())
.detail(reqDTO.getBody())
.totalFee(reqDTO.getPrice()) // 单位分
.timeExpire(TemporalAccessorUtil.format(reqDTO.getExpireTime().atZone(ZoneId.systemDefault()), PURE_DATETIME_PATTERN))
.spbillCreateIp(reqDTO.getUserIp())
.notifyUrl(reqDTO.getNotifyUrl())
.attach(JSON.toJSONString(reqDTO.getPassbackParams()))
.build();
WxPayUnifiedOrderV3Request request = new WxPayUnifiedOrderV3Request();
request.setOutTradeNo(reqDTO.getOutTradeNo());
request.setDescription(reqDTO.getSubject());
request.setAmount(new WxPayUnifiedOrderV3Request.Amount().setTotal(reqDTO.getPrice())); // 单位分
request.setTimeExpire(formatDateV3(reqDTO.getExpireTime()));
request.setSceneInfo(new WxPayUnifiedOrderV3Request.SceneInfo().setPayerClientIp(getClientIP()));
request.setNotifyUrl(reqDTO.getNotifyUrl());
request.setAttach(JSON.toJSONString(reqDTO.getPassbackParams()));
// 加载配置,创建client
WxPayService client = new WxPayServiceImpl();
client.setConfig(chenfengWechatPayConfig);
// 执行请求
WxPayAppOrderResult response = client.createOrder(request);
String response = client.createOrderV3(TradeTypeEnum.NATIVE, request);
// 转换结果
return PayOrderRespDTO.waitingOf(PayOrderDisplayModeEnum.APP.getMode(), JsonUtils.toJsonString(response),
@@ -71,20 +72,60 @@ public class WechatNativePayHandler extends AbstractPayHandler {
}
@Override
public PayOrderRespDTO doParseOrderNotify(Map<String, String> params, String body) throws WxPayException {
public PayOrderRespDTO doParseOrderNotify(Map<String, String> params, String body, Map<String, String> headers) throws WxPayException {
// 1. 解析回调
SignatureHeader signatureHeader = getRequestHeader(headers);
// 加载配置,创建client
WxPayService client = new WxPayServiceImpl();
client.setConfig(chenfengWechatPayConfig);
WxPayOrderNotifyResult response = client.parseOrderNotifyResult(body);
WxPayNotifyV3Result response = client.parseOrderNotifyV3Result(body, signatureHeader);
WxPayNotifyV3Result.DecryptNotifyResult result = response.getResult();
// 2. 构建结果
// V2 微信支付的回调,只有 SUCCESS 支付成功、CLOSED 支付失败两种情况,无需像支付宝一样解析的比较复杂
Integer status = Objects.equals(response.getResultCode(), "SUCCESS") ?
PayOrderStatusEnum.SUCCESS.getStatus() : PayOrderStatusEnum.FAIL.getStatus();
return PayOrderRespDTO.of(status, response.getTransactionId(), response.getOpenid(), parseDateV2(response.getTimeEnd()),
response.getOutTradeNo(), body);
Integer status = parseStatus(result.getTradeState());
String openid = result.getPayer() != null ? result.getPayer().getOpenid() : null;
return PayOrderRespDTO.of(status, result.getTransactionId(), openid, parseDateV3(result.getSuccessTime()),
result.getOutTradeNo(), body);
}
static LocalDateTime parseDateV2(String time) {
return LocalDateTimeUtil.parse(time, PURE_DATETIME_PATTERN);
static String formatDateV3(LocalDateTime time) {
return TemporalAccessorUtil.format(time.atZone(ZoneId.systemDefault()), UTC_WITH_XXX_OFFSET_PATTERN);
}
/**
* 组装请求头重的签名信息
*
* @see <a href="https://github.com/binarywang/weixin-java-pay-demo/blob/master/src/main/java/com/github/binarywang/demo/wx/pay/controller/WxPayV3Controller.java#L202-L221">官方示例</a>
*/
private SignatureHeader getRequestHeader(Map<String, String> headers) {
return SignatureHeader.builder()
.signature(headers.get("wechatpay-signature"))
.nonce(headers.get("wechatpay-nonce"))
.serial(headers.get("wechatpay-serial"))
.timeStamp(headers.get("wechatpay-timestamp"))
.build();
}
private static Integer parseStatus(String tradeState) {
switch (tradeState) {
case "NOTPAY":
case "USERPAYING": // 支付中,等待用户输入密码(条码支付独有)
return PayOrderStatusEnum.WAITING.getStatus();
case "SUCCESS":
return PayOrderStatusEnum.SUCCESS.getStatus();
// case "REFUND":
// return PayOrderStatusEnum.REFUND.getStatus();
case "CLOSED":
case "REVOKED": // 已撤销(刷卡支付独有)
case "PAYERROR": // 支付失败(其它原因,如银行返回失败)
return PayOrderStatusEnum.FAIL.getStatus();
default:
throw new IllegalArgumentException(CharSequenceUtil.format("未知的支付状态({})", tradeState));
}
}
static LocalDateTime parseDateV3(String time) {
return LocalDateTimeUtil.parse(time, UTC_WITH_XXX_OFFSET_PATTERN);
}
}
@@ -0,0 +1,32 @@
package com.cf.imes.module.system.validation.invoice;
import jakarta.validation.Constraint;
import jakarta.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 发票开具类型校验注解
*
* @author Gqr
* @since 2025/8/21 14:36
*/
@Target({
ElementType.FIELD,
})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(
validatedBy = {InvoiceIssueTypeValidtor.class}
)
public @interface InvoiceIssueTypeValid {
String message() default "不支持的发票开具类型";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
@@ -0,0 +1,27 @@
package com.cf.imes.module.system.validation.invoice;
import cn.hutool.core.util.ObjectUtil;
import com.cf.imes.module.system.enums.pay.InvoiceIssueTypeEnum;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
/**
* 发票开具类型校验器
*
* @author Gqr
* @since 2025/8/21 14:37
*/
public class InvoiceIssueTypeValidtor implements ConstraintValidator<InvoiceIssueTypeValid, Integer> {
@Override
public boolean isValid(Integer integer, ConstraintValidatorContext constraintValidatorContext) {
if (ObjectUtil.isNull(integer)) {
return true;
}
for (InvoiceIssueTypeEnum issueTypeEnum : InvoiceIssueTypeEnum.values()) {
if (issueTypeEnum.getValue().equals(integer)) {
return true;
}
}
return false;
}
}
@@ -0,0 +1,32 @@
package com.cf.imes.module.system.validation.invoice;
import jakarta.validation.Constraint;
import jakarta.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 发票类型校验注解
*
* @author Gqr
* @since 2025/8/21 14:36
*/
@Target({
ElementType.FIELD,
})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(
validatedBy = {InvoiceTypeValidtor.class}
)
public @interface InvoiceTypeValid {
String message() default "不支持的发票类型";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
@@ -0,0 +1,27 @@
package com.cf.imes.module.system.validation.invoice;
import cn.hutool.core.util.ObjectUtil;
import com.cf.imes.module.system.enums.pay.InvoiceTypeEnum;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
/**
* 发票类型校验器
*
* @author Gqr
* @since 2025/8/21 14:37
*/
public class InvoiceTypeValidtor implements ConstraintValidator<InvoiceTypeValid, Integer> {
@Override
public boolean isValid(Integer integer, ConstraintValidatorContext constraintValidatorContext) {
if (ObjectUtil.isNull(integer)) {
return true;
}
for (InvoiceTypeEnum invoiceTypeEnum : InvoiceTypeEnum.values()) {
if (invoiceTypeEnum.getValue().equals(integer)) {
return true;
}
}
return false;
}
}
@@ -266,6 +266,17 @@ chenfeng:
privateKey: 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
alipayPublicKey: KbUGE7zsPBKWPx0/zMcAdLRldKrcgy2LRiOrqOBm7y4iPJLXTYtXFJT15p2JMQiyOM8mqiYQR0OEDwfrKxDgHXLtIut/e0EAk9bZwZoLrqVe0pN1huseRC38zEglbWil3yz+tZfHZuAaEq2EzvWJltX/tRKOzNBqAT9l+UvnqNedgCFB9MY2EErqJjbn/at3gYCkV2idt31ghF0z8SEQ5h9ztNYtmTvk8c8ipi0/hIjX8XryN6mlOvGQfyZ4rWrBWBuZ6iD/SyuThwxKmFYU4dhHbHVpBrva5nJWU43CP7RVaJm649nNJnce+Ppj2Tv8+ugSLe5poZWo2zmGe3Xn5WmN6OTZyRI1pnmn2/hGxtOMmTZJX7Gh0pI4v3wmU0gOd/gbRlqlvN+u12T92nyH8MQrHdA7UXuttAtolYUa/Rli/20qHX69QtOtvfZk8mNvDSj9dDvODseGcrFHm07jyfxpaIkQDeN75cQ3NwNgDQEW8urQszaU1IJ7hc+20tt0Z3td4+7MfiVPpGfVxsGE66gwLgpB8wsxEhyBTaBr7osMLqOv
notifyUrl: VvJ5DOVuGOBw39j7tyrvsEEtLSseVM8T3u9kqsWKago+iI5ao8JzoggmeSe+N9LrsYxutK4fkGpmr4jOGJ3JTQFZ7YBXLxf2oLIXOZH9O27EnDtm
wechat:
appId: vTVnNGeOPD8AeiTIS5cnUzq8q3EiOW0HZ18QtxFE/UxTYwV1DSwxrbYDbBGjoA==
mchId: p/zqgyb9tAG9n9DKROHdHuIv0FNQGfbQ0AH7uTsglakv9J4ZHGA=
mchKey: tdnmIOPmmqq0ST47xYWg61chgI2ycJGhjeApSjnraW103upgR/EMx/qfbFMVKA59BISqzbSukjQPV5pR
apiV3Key: tdnmIOPmmqq0ST47xYWg61chgI2ycJGhjeApSjnraW103upgR/EMx/qfbFMVKA59BISqzbSukjQPV5pR
publicKeyId: 9NU9NFR9WWUaN2xfCujfPhcOToJop6M0G4JGbpZDlkrfCQVK3KYDOEeinJwEuxvJGcoO2hKUVkapHmeUngH3VxPu+qBcf+gN8Q==
certSerialNo: zzAFIJnTSEhme+K37z/ciNI04S//eHxBR/VCKLdGRGTrWAlvHYU2LSw9YPl07OEBGOpIBHG3oep77cr5g8wpGXGRvJQ=
tradeType: eaQet/V56gw8fTCVKypLG8cAjgheMpxKo6Y6wQab1P5b9w==
publicKeyString: F+bPjhy/AwNvl16dMdqHCgKYIfA9c8Tho+g+fAeNOb7GjQf0NCX36B2ZH7yngpN6v82Q10/PfblVrPGrvMbDZcU4gxomhXN22L2hfaijPESgodEuoAd89Z31WYFuRRCKxAWWzjhXa+E5bBSUpMkuToLlgZgqiLmAmjfTJO9NOIM0i0+D/2foka4ALixhyklznefk8Dz2RwFb60luZSUE03NFoqp1/uifn6LB4r/Lery1wH8b1EN5rgjvJXDIRGsyaHWbtdQr0sB0NW6LoR5PzZPeq4X4DE6iBDzG7u55kFCFwVQXmxCgHN4bdniVHcu28m9piCTuvyGQHXZ2J6prsytv/N24ef/xBVTHMMx+bcj+AIB0z44/OfgK4DF2U4JURNsCmD9h6dY60Hi6kFHOe8Y9GcR8T9V2LrfOJdm2mX19U+ziCE7jo0zllxo6vzLCMwjfm61gWhxU30fOaZgXbhGHps3z/TS5LlYiLgZcv3LE+Ttvl3fl0Cm3SitvNqxwQTnLxxKjcr2+ZE523Azr6/6JF1t8wSBKz4MAH9FrNvHVbziu7QrrWOFpCUL22sbBXtglQvQ1fjHqQ84qcGh6fDJC9k3f+lWVX1jAQiJT6h2MjfwaSIbhUGFS1BG6mQ==
privateKeyString: P+7gYGAyY0lKxrzAowku4RCubro0QjTPX6IHP8oOmsgLnhzkF9EiWvyrcWrzHOsu6/JPrMM0NW7bC/2DSTmvGw9m2p8AI8syLT/1neQXJNq6N9TgbGVNfsYYsc2EKTmJhl1Y1ZuOEgze24n3HUmUwHCFUCc9LQQ5wHFhhVPwbyMEd7hHXnolEZ2qIBonCwX0bBX2NUHdHUDRHFBHbxDeypeA9G/Z9Wz1hkyRXAlPUdhnVhEVXb9/3ynvfLdCk4kTpZYR3D31y8d8jW+3PSGeWWZRkToEhCI2xbJto//8Lt13B9nPo3r5GZTj0M44nRsjEmDsbBXOC2Y1Nu0Bnx/C7JePcnEtOiLI1P4mf7e8vhXjzY4q6NZQ7D1NXF8U4x86izvy7dD/aaN0SCDnUaT91Xi3+h+eGMitWSrAHt634xkj81ijg56H+Q0hiUby0ejgwlgyGrO2Y1yh5umYvljPyKnUNagZJFmsG85yJ2CX4a5McGHitAAJhJzqwq2+bTIESd61TZCqIVDLV42AZMrEq25DXjgFwoBD5tnbh8EMIGhAWIXMTbEKQy5NV5bWbY/w4XUSl16v6K6W3RtgK8UANAoipIOcaReQ/25lxRN2C6tbPoRnG4ddHUj2J5/fG9X3FfJ1BVBqopCtzp/vnntq+73DgQy3dthB7EA1cCgHzlPGdTTe6O6N7gMwjA2SdtFTQtxEX6Pi8JhjuEF5zSsMvs1CWxh3l/mRFCwQw3qrEU/q1CFeIWQF2o3swhXPjcNWz38zPdFTvADsXz+GowN2IsQdq/uvRHFqBSyEMBcEGyn5VqRcmcB5Rk0L6dD3lKGOn/9H+3z+s2Ljf9dXdXm9jv/Nld20klrQs0ySwmIcbWra7vD4iUwNEx+F/QoeDsAI3OhUGaaoir+dJ6pjwFZO/A3P20dseZLRVlvGCE6cFVfJenv4pBTpOmKTFiLKsqyk3MykaDwRwSvCLI0NHKDI+ckgiTHTVoL+0gjjVtKlyFJk0whY6lu7lObNKj+DbDmetWMa1EWhzcscVX1f6dzTRRo9GjL+SFQT4s1dGjQ9lOpmP1ICP6X5C9MuJ/y2ScZ7ywSkrqmnxAPi122T8hVDRL/HwqxneKP3oWl1DlDHcQoAQqo8iwRDs9e2gta48g4J/NyX2FJ76I2F86L//qhuKq0uSXQTr6ara4eKeFi8fjdbipEOsvP6wEQEFegH//7o/Fta7wxj3Lt81fEqJ9XOuCW+1x6xtXowlhjNM+qF1cGNa5Jd5ATfTd3KH0ClgNk8pki8q26jRpyOChCCoZg5a1CqyzbFFaXYYNmtuuwUSgbOUIDLFtmJgpoj95oDyBzYbzPzTV26jvJIBpiM6BWFKGBcDXUPPUTGCCZo/QDIDeBBF1Ar52i5QDWUWYEwKBQ70llN4AByy+tPtqhoLNfDiNvEDMxXEOh+beB3U5wUKMm4f3TP2VpGjgYBMCNwoV4v/iNhDrdvzmhmRYUDCTF1ttn2LlFKAO+Yba7IPwAjHMGHZ7pm6/bJ/bAAi57Kr7C5sO38Ovi7ilkn2yXC4ZOCZe0En7n3AUyGwvcDB7RkW08U6mVMKHr8mKT8wpt0ia1IJDmuHMO7r6dJWMimIBeY+eo8R3/NZY7hLyLd0mz+onq+nYYSJBzXG1J0I9VYcU7T7/RMk+pt6W1Jb3jkd+oytK0LANumN4Tb4ufnJevYyq3wRiQV5V7aZNyvPWLQigdc2Q3MkmZ8PCTcRDnhVw8h7WxgvwiArKe2ThF7UdFEa8cnyp7ATu77fw1DlIyzLFscOiOleBp67+e+kEzXNoQkGKsBxkBZrfK03fw5+8SMB/59c7UB6Ym92BO9U8CyrwyoDq9Im75wPDXR3KedjBLvbpEOBQsf2v0a3CUfIdpCP4r9R2SGKK3PAsl1/I+/5/ICF+7wHpmZMg4DO5CqpKGnHop4qkg/JXbTpeKeCrZpMZXVJxsr/4a4wHGWl+LqbUFhwfUS+9dRGXnx047g+lDLBp2evHE2w11wlLJeDW/5tmxDAzqb3fI9ZMAFfhkj8jkAL6HtNMg3iDevp9F42pgHkrdkXcChTsQWfKNMqKjRkfscgmVvtcnPHTbke8F8UlKqRAWaTKpir9xpbhvD53eNDktjC5cwKG/k7vj3etojBzhGO3txtHlTzSMBO/Mjf7guTNVyb5f+DTSjYhjLvYVKBtH1dlXlFW/V1xefyVxrKynPaNzid4kPWLd7j8d3JbMv+/JlZRxTT2CUskLtrBH1xg7RNRx6sKtmBd+wE7jMwhCvzIa00OBqKwJZk1jcqWtcwKPm7y14eg==
notifyUrl: RIEeHCoPHaAryuVN6mEYskBSl+EPvhsxRpjIbuIE7f7jABVxm46bLc01WQUe4Fn9NPYyNTv0xCxM68iRrmFDjAUjQ/hkIVNG+yD/kNsjcYCWa2gtOQPrvWZzDA==
justauth:
enabled: true
@@ -291,12 +291,17 @@ chenfeng:
privateKey: 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
alipayPublicKey: KbUGE7zsPBKWPx0/zMcAdLRldKrcgy2LRiOrqOBm7y4iPJLXTYtXFJT15p2JMQiyOM8mqiYQR0OEDwfrKxDgHXLtIut/e0EAk9bZwZoLrqVe0pN1huseRC38zEglbWil3yz+tZfHZuAaEq2EzvWJltX/tRKOzNBqAT9l+UvnqNedgCFB9MY2EErqJjbn/at3gYCkV2idt31ghF0z8SEQ5h9ztNYtmTvk8c8ipi0/hIjX8XryN6mlOvGQfyZ4rWrBWBuZ6iD/SyuThwxKmFYU4dhHbHVpBrva5nJWU43CP7RVaJm649nNJnce+Ppj2Tv8+ugSLe5poZWo2zmGe3Xn5WmN6OTZyRI1pnmn2/hGxtOMmTZJX7Gh0pI4v3wmU0gOd/gbRlqlvN+u12T92nyH8MQrHdA7UXuttAtolYUa/Rli/20qHX69QtOtvfZk8mNvDSj9dDvODseGcrFHm07jyfxpaIkQDeN75cQ3NwNgDQEW8urQszaU1IJ7hc+20tt0Z3td4+7MfiVPpGfVxsGE66gwLgpB8wsxEhyBTaBr7osMLqOv
notifyUrl: RIEeHCoPHaAryuVN6mEYskBSl+EPvhsxRpjIbuIE7f7jABVxm46bLc01WQUe4Fn9NPYyNTv0xCxM68iRrmFDjAUjQ/hkIVNG+yD/kNsjcYCWa2gtOQPrvWZzDA==
# wechat:
# payBaseUrl: https://api.mch.weixin.qq.com/xdc/apiv2sandbox
# appId: wxd0b9c0b5d0b9c0b5
# mchId: 1725093395
# mchKey: Cf100261028910026102891002610289
wechat:
appId: vTVnNGeOPD8AeiTIS5cnUzq8q3EiOW0HZ18QtxFE/UxTYwV1DSwxrbYDbBGjoA==
mchId: p/zqgyb9tAG9n9DKROHdHuIv0FNQGfbQ0AH7uTsglakv9J4ZHGA=
mchKey: tdnmIOPmmqq0ST47xYWg61chgI2ycJGhjeApSjnraW103upgR/EMx/qfbFMVKA59BISqzbSukjQPV5pR
apiV3Key: tdnmIOPmmqq0ST47xYWg61chgI2ycJGhjeApSjnraW103upgR/EMx/qfbFMVKA59BISqzbSukjQPV5pR
publicKeyId: 9NU9NFR9WWUaN2xfCujfPhcOToJop6M0G4JGbpZDlkrfCQVK3KYDOEeinJwEuxvJGcoO2hKUVkapHmeUngH3VxPu+qBcf+gN8Q==
certSerialNo: zzAFIJnTSEhme+K37z/ciNI04S//eHxBR/VCKLdGRGTrWAlvHYU2LSw9YPl07OEBGOpIBHG3oep77cr5g8wpGXGRvJQ=
tradeType: eaQet/V56gw8fTCVKypLG8cAjgheMpxKo6Y6wQab1P5b9w==
publicKeyString: F+bPjhy/AwNvl16dMdqHCgKYIfA9c8Tho+g+fAeNOb7GjQf0NCX36B2ZH7yngpN6v82Q10/PfblVrPGrvMbDZcU4gxomhXN22L2hfaijPESgodEuoAd89Z31WYFuRRCKxAWWzjhXa+E5bBSUpMkuToLlgZgqiLmAmjfTJO9NOIM0i0+D/2foka4ALixhyklznefk8Dz2RwFb60luZSUE03NFoqp1/uifn6LB4r/Lery1wH8b1EN5rgjvJXDIRGsyaHWbtdQr0sB0NW6LoR5PzZPeq4X4DE6iBDzG7u55kFCFwVQXmxCgHN4bdniVHcu28m9piCTuvyGQHXZ2J6prsytv/N24ef/xBVTHMMx+bcj+AIB0z44/OfgK4DF2U4JURNsCmD9h6dY60Hi6kFHOe8Y9GcR8T9V2LrfOJdm2mX19U+ziCE7jo0zllxo6vzLCMwjfm61gWhxU30fOaZgXbhGHps3z/TS5LlYiLgZcv3LE+Ttvl3fl0Cm3SitvNqxwQTnLxxKjcr2+ZE523Azr6/6JF1t8wSBKz4MAH9FrNvHVbziu7QrrWOFpCUL22sbBXtglQvQ1fjHqQ84qcGh6fDJC9k3f+lWVX1jAQiJT6h2MjfwaSIbhUGFS1BG6mQ==
privateKeyString: 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
notifyUrl: RIEeHCoPHaAryuVN6mEYskBSl+EPvhsxRpjIbuIE7f7jABVxm46bLc01WQUe4Fn9NPYyNTv0xCxM68iRrmFDjAUjQ/hkIVNG+yD/kNsjcYCWa2gtOQPrvWZzDA==
justauth:
enabled: true
@@ -5,4 +5,14 @@
select sum(account_balance_spent) from purchase_record
where organ_id = #{organId} and is_invocing = '${@com.cf.imes.module.system.enums.pay.InvoiceStatusEnum@INVOICABLE.status}';
</select>
<!-- 查询机构下开票金额统计:可开票、开票中、已开票-->
<select id="getOrgInvoiceAmountStatistics" resultType="com.cf.imes.module.system.controller.admin.funds.invoice.vo.InvoiceAmountRespVO">
select organ_id,
sum(case when is_invocing = '${@com.cf.imes.module.system.enums.pay.InvoiceStatusEnum@INVOICABLE.status}' then account_balance_spent else 0 end) as invocable,
sum(case when is_invocing = '${@com.cf.imes.module.system.enums.pay.InvoiceStatusEnum@PENDINGINVOICING.status}' then account_balance_spent else 0 end) as inTheInvoice,
sum(case when is_invocing = '${@com.cf.imes.module.system.enums.pay.InvoiceStatusEnum@INVOICINGSUCCESSFUL.status}' then account_balance_spent else 0 end) as invoiced
from purchase_record
where organ_id = #{organId}
</select>
</mapper>