Compare commits
merge into: huangqizhen:master
huangqizhen:dev
huangqizhen:huangqizheng/feature-20250707173453-7.7bug修改
huangqizhen:huangqizheng/feature-20250708175155-7.8bug修复
huangqizhen:huangqizheng/feature-20250709163552-7.8导出传输对象修复
huangqizhen:huangqizheng/feature-20250710151401-数据同步完成
huangqizhen:huangqizheng/feature-20250714180618-数据同步
huangqizhen:huangqizheng/feature-20250715100115-非测试数据筛选
huangqizhen:huangqizheng/feature-20250717200321-优化导出
huangqizhen:huangqizheng/feature-20250718222252-导出修改完毕
huangqizhen:huangqizheng/feature-20250719105717-后端完毕
huangqizhen:huangqizheng/feature-20250721170818-后端周末修改
huangqizhen:huangqizheng/feature-20250722105318-BUG修改
huangqizhen:huangqizheng/feature-20250725144236-精简日志
huangqizhen:huangqizheng/feature-20250728134614-三期数据库配置
huangqizhen:huangqizheng/feature-20250731164458-地区部分修改以及导出的封装
huangqizhen:huangqizheng/feature-20250801114957-导出
huangqizhen:huangqizheng/feature-20250801180134-消费导出
huangqizhen:huangqizheng/feature-20250803175216-导出完毕
huangqizhen:huangqizheng/feature-20250804114811-打包配置
huangqizhen:huangqizheng/feature-20250805200446-配置修改及充值导出修改
huangqizhen:huangqizheng/feature-20250806120055-修复BUG
huangqizhen:huangqizheng/feature-20250808172612-bug修复
huangqizhen:huangqizheng/feature-20250809103653-修改配置导出重新上传
huangqizhen:huangqizheng/feature-20250809184038-研发部校验
huangqizhen:huangqizheng/feature-20251104113536-现金管理二期退款修改
huangqizhen:huangqizheng/feature-20251119150446-退款合并后
huangqizhen:lihuilin/hotfix-20250810160509-余额地区传参
huangqizhen:lihuilin/hotfix-20250815-审核默认更新时间
huangqizhen:lihuilin1015备份
huangqizhen:lijianlin/feature-20250623120104-工作台与审核
huangqizhen:lijianlin/feature-20250710152503-二期工作台与部分权限
huangqizhen:lijianlin/feature-20250728171217-三期金豆消费相关
huangqizhen:lijianlin/feature-202509231533026-现金管理-收款管理
huangqizhen:lijianlin/feature-20251104110749-现金管理二期
huangqizhen:master
huangqizhen:milestone-20250702-金币重构一期
huangqizhen:milestone-20250711-金币重构二期
huangqizhen:milestone-20250727-金币重构三期
huangqizhen:milestone-20251016-现金管理
huangqizhen:milestone-20251104-现金管理二期
huangqizhen:sunjiabei/feature-20250623130922-消费功能
huangqizhen:sunjiabei/feature-20250710132313-二期模块
huangqizhen:sunjiabei/feature-20250725135225-金币三期
huangqizhen:sunjiabei/feature-20250822111257-平台和商品id字段替换
huangqizhen:sunjiabei/feature-20250924164720-现金管理
huangqizhen:sunjiabei/feature-20251021102635-银行接口
huangqizhen:wangyetao/feature-20250628170337-汇率
huangqizhen:wangyetao/feature-20250715095107-地区数据权限
huangqizhen:yufenghao/feature-20251106105659-现金管理接口优化
huangqizhen:yufenghao1106
huangqizhen:工作流配置
pull from: huangqizhen:sunjiabei/feature-20251021102635-银行接口
huangqizhen:dev
huangqizhen:huangqizheng/feature-20250707173453-7.7bug修改
huangqizhen:huangqizheng/feature-20250708175155-7.8bug修复
huangqizhen:huangqizheng/feature-20250709163552-7.8导出传输对象修复
huangqizhen:huangqizheng/feature-20250710151401-数据同步完成
huangqizhen:huangqizheng/feature-20250714180618-数据同步
huangqizhen:huangqizheng/feature-20250715100115-非测试数据筛选
huangqizhen:huangqizheng/feature-20250717200321-优化导出
huangqizhen:huangqizheng/feature-20250718222252-导出修改完毕
huangqizhen:huangqizheng/feature-20250719105717-后端完毕
huangqizhen:huangqizheng/feature-20250721170818-后端周末修改
huangqizhen:huangqizheng/feature-20250722105318-BUG修改
huangqizhen:huangqizheng/feature-20250725144236-精简日志
huangqizhen:huangqizheng/feature-20250728134614-三期数据库配置
huangqizhen:huangqizheng/feature-20250731164458-地区部分修改以及导出的封装
huangqizhen:huangqizheng/feature-20250801114957-导出
huangqizhen:huangqizheng/feature-20250801180134-消费导出
huangqizhen:huangqizheng/feature-20250803175216-导出完毕
huangqizhen:huangqizheng/feature-20250804114811-打包配置
huangqizhen:huangqizheng/feature-20250805200446-配置修改及充值导出修改
huangqizhen:huangqizheng/feature-20250806120055-修复BUG
huangqizhen:huangqizheng/feature-20250808172612-bug修复
huangqizhen:huangqizheng/feature-20250809103653-修改配置导出重新上传
huangqizhen:huangqizheng/feature-20250809184038-研发部校验
huangqizhen:huangqizheng/feature-20251104113536-现金管理二期退款修改
huangqizhen:huangqizheng/feature-20251119150446-退款合并后
huangqizhen:lihuilin/hotfix-20250810160509-余额地区传参
huangqizhen:lihuilin/hotfix-20250815-审核默认更新时间
huangqizhen:lihuilin1015备份
huangqizhen:lijianlin/feature-20250623120104-工作台与审核
huangqizhen:lijianlin/feature-20250710152503-二期工作台与部分权限
huangqizhen:lijianlin/feature-20250728171217-三期金豆消费相关
huangqizhen:lijianlin/feature-202509231533026-现金管理-收款管理
huangqizhen:lijianlin/feature-20251104110749-现金管理二期
huangqizhen:master
huangqizhen:milestone-20250702-金币重构一期
huangqizhen:milestone-20250711-金币重构二期
huangqizhen:milestone-20250727-金币重构三期
huangqizhen:milestone-20251016-现金管理
huangqizhen:milestone-20251104-现金管理二期
huangqizhen:sunjiabei/feature-20250623130922-消费功能
huangqizhen:sunjiabei/feature-20250710132313-二期模块
huangqizhen:sunjiabei/feature-20250725135225-金币三期
huangqizhen:sunjiabei/feature-20250822111257-平台和商品id字段替换
huangqizhen:sunjiabei/feature-20250924164720-现金管理
huangqizhen:sunjiabei/feature-20251021102635-银行接口
huangqizhen:wangyetao/feature-20250628170337-汇率
huangqizhen:wangyetao/feature-20250715095107-地区数据权限
huangqizhen:yufenghao/feature-20251106105659-现金管理接口优化
huangqizhen:yufenghao1106
huangqizhen:工作流配置
5 Commits
master
...
sunjiabei/
| Author | SHA1 | Message | Date |
|---|---|---|---|
|
|
ab8285bd9b |
Merge branch 'refs/heads/milestone-20251104-现金管理二期' into sunjiabei/feature-20251021102635-银行接口
|
3 days ago |
|
|
2eff7c71ad |
11月23日银行接口
|
4 days ago |
|
|
16a16ab86c |
Merge branch 'milestone-20251104-现金管理二期' into sunjiabei/feature-20251021102635-银行接口
|
4 days ago |
|
|
a2154cf643 |
11月21日银行接口
|
6 days ago |
|
|
62741879f5 |
11月21日银行接口
|
6 days ago |
11 changed files with 925 additions and 6 deletions
-
186src/main/java/com/example/demo/Util/DateConvertUtil.java
-
74src/main/java/com/example/demo/controller/cash/Bank.java
-
83src/main/java/com/example/demo/controller/cash/BankController.java
-
26src/main/java/com/example/demo/domain/DTO/BankDTO.java
-
37src/main/java/com/example/demo/domain/DTO/PaymentDTO.java
-
31src/main/java/com/example/demo/domain/DTO/StripeDTO.java
-
27src/main/java/com/example/demo/domain/vo/cash/BankVO.java
-
5src/main/java/com/example/demo/mapper/cash/CashCollectionMapper.java
-
30src/main/java/com/example/demo/service/cash/BankService.java
-
410src/main/java/com/example/demo/serviceImpl/cash/BankServiceImpl.java
-
20src/main/resources/cashMapper/CashCollectionMapper.xml
@ -0,0 +1,186 @@ |
|||
package com.example.demo.Util; |
|||
|
|||
/** |
|||
* @program: gold-java |
|||
* @ClassName DateConvertUtil |
|||
* @description: |
|||
* @author: Double |
|||
* @create: 2025−11-21 17:45 |
|||
* @Version 1.0 |
|||
**/ |
|||
import java.text.ParseException; |
|||
import java.text.SimpleDateFormat; |
|||
import java.time.LocalDate; |
|||
import java.time.format.DateTimeFormatter; |
|||
import java.time.format.DateTimeParseException; |
|||
import java.util.Date; |
|||
import java.util.TimeZone; |
|||
|
|||
public class DateConvertUtil { |
|||
|
|||
// 定义支持的日期格式(可根据需求扩展) |
|||
private static final DateTimeFormatter FORMATTER_YYYYMMDD = DateTimeFormatter.ofPattern("yyyyMMdd"); |
|||
private static final DateTimeFormatter FORMATTER_YYYY_MM_DD = DateTimeFormatter.ofPattern("yyyy-MM-dd"); |
|||
private static final DateTimeFormatter FORMATTER_YYYY_SLASH_MM_SLASH_DD = DateTimeFormatter.ofPattern("yyyy/MM/dd"); |
|||
|
|||
private static final SimpleDateFormat SDF_YYYYMMDD = new SimpleDateFormat("yyyyMMdd"); |
|||
private static final SimpleDateFormat SDF_YYYY_MM_DD = new SimpleDateFormat("yyyy-MM-dd"); |
|||
private static final SimpleDateFormat SDF_YYYY_SLASH_MM_SLASH_DD = new SimpleDateFormat("yyyy/MM/dd"); |
|||
|
|||
// 默认时区(可改为 UTC:TimeZone.getTimeZone("UTC")) |
|||
private static final TimeZone DEFAULT_TIME_ZONE = TimeZone.getTimeZone("Asia/Shanghai"); |
|||
|
|||
static { |
|||
// 统一设置SimpleDateFormat时区 |
|||
SDF_YYYYMMDD.setTimeZone(DEFAULT_TIME_ZONE); |
|||
SDF_YYYY_MM_DD.setTimeZone(DEFAULT_TIME_ZONE); |
|||
SDF_YYYY_SLASH_MM_SLASH_DD.setTimeZone(DEFAULT_TIME_ZONE); |
|||
} |
|||
|
|||
// -------------------------- String → LocalDate(Java 8+ 推荐)-------------------------- |
|||
/** |
|||
* 字符串转LocalDate(自动匹配支持的格式) |
|||
*/ |
|||
public static LocalDate stringToLocalDate(String dateStr) { |
|||
if (dateStr == null || dateStr.trim().isEmpty()) { |
|||
throw new IllegalArgumentException("日期字符串不能为空"); |
|||
} |
|||
dateStr = dateStr.trim(); |
|||
// 依次尝试三种格式 |
|||
try { |
|||
return LocalDate.parse(dateStr, FORMATTER_YYYYMMDD); |
|||
} catch (DateTimeParseException e1) { |
|||
try { |
|||
return LocalDate.parse(dateStr, FORMATTER_YYYY_MM_DD); |
|||
} catch (DateTimeParseException e2) { |
|||
try { |
|||
return LocalDate.parse(dateStr, FORMATTER_YYYY_SLASH_MM_SLASH_DD); |
|||
} catch (DateTimeParseException e3) { |
|||
throw new IllegalArgumentException( |
|||
String.format("日期格式错误!支持格式:yyyyMMdd、yyyy-MM-dd、yyyy/MM/dd,当前输入:%s", dateStr), |
|||
e3 |
|||
); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 字符串转LocalDate(指定格式) |
|||
*/ |
|||
public static LocalDate stringToLocalDate(String dateStr, String pattern) { |
|||
if (dateStr == null || dateStr.trim().isEmpty()) { |
|||
throw new IllegalArgumentException("日期字符串不能为空"); |
|||
} |
|||
if (pattern == null || pattern.trim().isEmpty()) { |
|||
throw new IllegalArgumentException("格式模板不能为空"); |
|||
} |
|||
try { |
|||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern); |
|||
return LocalDate.parse(dateStr.trim(), formatter); |
|||
} catch (DateTimeParseException e) { |
|||
throw new IllegalArgumentException( |
|||
String.format("日期格式错误!指定格式:%s,当前输入:%s", pattern, dateStr), |
|||
e |
|||
); |
|||
} |
|||
} |
|||
|
|||
// -------------------------- String → Date(兼容旧代码)-------------------------- |
|||
/** |
|||
* 字符串转Date(自动匹配支持的格式) |
|||
*/ |
|||
public static Date stringToDate(String dateStr) { |
|||
if (dateStr == null || dateStr.trim().isEmpty()) { |
|||
throw new IllegalArgumentException("日期字符串不能为空"); |
|||
} |
|||
dateStr = dateStr.trim(); |
|||
try { |
|||
return SDF_YYYYMMDD.parse(dateStr); |
|||
} catch (ParseException e1) { |
|||
try { |
|||
return SDF_YYYY_MM_DD.parse(dateStr); |
|||
} catch (ParseException e2) { |
|||
try { |
|||
return SDF_YYYY_SLASH_MM_SLASH_DD.parse(dateStr); |
|||
} catch (ParseException e3) { |
|||
throw new IllegalArgumentException( |
|||
String.format("日期格式错误!支持格式:yyyyMMdd、yyyy-MM-dd、yyyy/MM/dd,当前输入:%s", dateStr), |
|||
e3 |
|||
); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 字符串转Date(指定格式) |
|||
*/ |
|||
public static Date stringToDate(String dateStr, String pattern) { |
|||
if (dateStr == null || dateStr.trim().isEmpty()) { |
|||
throw new IllegalArgumentException("日期字符串不能为空"); |
|||
} |
|||
if (pattern == null || pattern.trim().isEmpty()) { |
|||
throw new IllegalArgumentException("格式模板不能为空"); |
|||
} |
|||
try { |
|||
SimpleDateFormat sdf = new SimpleDateFormat(pattern); |
|||
sdf.setTimeZone(DEFAULT_TIME_ZONE); |
|||
return sdf.parse(dateStr.trim()); |
|||
} catch (ParseException e) { |
|||
throw new IllegalArgumentException( |
|||
String.format("日期格式错误!指定格式:%s,当前输入:%s", pattern, dateStr), |
|||
e |
|||
); |
|||
} |
|||
} |
|||
|
|||
// -------------------------- Date ↔ LocalDate 互转 -------------------------- |
|||
/** |
|||
* Date转LocalDate |
|||
*/ |
|||
public static LocalDate dateToLocalDate(Date date) { |
|||
if (date == null) { |
|||
throw new IllegalArgumentException("Date对象不能为空"); |
|||
} |
|||
return date.toInstant() |
|||
.atZone(DEFAULT_TIME_ZONE.toZoneId()) |
|||
.toLocalDate(); |
|||
} |
|||
|
|||
/** |
|||
* LocalDate转Date |
|||
*/ |
|||
public static Date localDateToDate(LocalDate localDate) { |
|||
if (localDate == null) { |
|||
throw new IllegalArgumentException("LocalDate对象不能为空"); |
|||
} |
|||
return Date.from( |
|||
localDate.atStartOfDay(DEFAULT_TIME_ZONE.toZoneId()) |
|||
.toInstant() |
|||
); |
|||
} |
|||
|
|||
// -------------------------- 测试方法 -------------------------- |
|||
public static void main(String[] args) { |
|||
// 测试1:String→LocalDate(yyyyMMdd格式) |
|||
LocalDate localDate1 = stringToLocalDate("20250911"); |
|||
System.out.println("String→LocalDate(yyyyMMdd):" + localDate1); // 输出:2025-09-11 |
|||
|
|||
// 测试2:String→Date(yyyy-MM-dd格式) |
|||
Date date1 = stringToDate("2025-09-11"); |
|||
System.out.println("String→Date(yyyy-MM-dd):" + date1); // 输出:Wed Sep 11 00:00:00 CST 2025 |
|||
|
|||
// 测试3:Date→LocalDate |
|||
LocalDate localDate2 = dateToLocalDate(date1); |
|||
System.out.println("Date→LocalDate:" + localDate2); // 输出:2025-09-11 |
|||
|
|||
// 测试4:LocalDate→Date |
|||
Date date2 = localDateToDate(localDate2); |
|||
System.out.println("LocalDate→Date:" + date2); // 输出:Wed Sep 11 00:00:00 CST 2025 |
|||
|
|||
// 测试5:指定格式转换(yyyy/MM/dd) |
|||
LocalDate localDate3 = stringToLocalDate("2025/09/11", "yyyy/MM/dd"); |
|||
System.out.println("指定格式(yyyy/MM/dd)→LocalDate:" + localDate3); // 输出:2025-09-11 |
|||
} |
|||
} |
|||
@ -0,0 +1,83 @@ |
|||
package com.example.demo.controller.cash; |
|||
|
|||
import com.example.demo.config.interfac.Log; |
|||
import com.example.demo.domain.DTO.BankDTO; |
|||
import com.example.demo.domain.vo.cash.BankVO; |
|||
import com.example.demo.domain.vo.coin.Result; |
|||
import com.example.demo.service.cash.BankService; |
|||
import lombok.RequiredArgsConstructor; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.web.bind.annotation.*; |
|||
|
|||
/** |
|||
* @program: gold-java |
|||
* @ClassName Bank |
|||
* @description: |
|||
* @author: Double |
|||
* @create: 2025−11-17 13:31 |
|||
* @Version 1.0 |
|||
**/ |
|||
|
|||
|
|||
@RestController |
|||
@RequestMapping("/bank") |
|||
@RequiredArgsConstructor |
|||
@Slf4j |
|||
@CrossOrigin |
|||
public class BankController { |
|||
|
|||
@Autowired |
|||
private BankService bankService; |
|||
|
|||
//payment银行接口(批量) |
|||
@Log("payment银行接口(批量)") |
|||
@PostMapping("/paymentAuto") |
|||
public Result paymentAuto(@RequestBody BankDTO bankDTO) { |
|||
try { |
|||
return Result.success(bankService.paymentAuto(bankDTO)); |
|||
} catch (Exception e) { |
|||
return Result.error(e.getMessage()); |
|||
} |
|||
} |
|||
|
|||
//payment银行接口(单个) |
|||
@Log("payment银行接口(单个)") |
|||
@PostMapping("/getPayment") |
|||
public Result getPayment(@RequestBody BankDTO bankDTO) { |
|||
try { |
|||
if(bankDTO.getTime().isEmpty() || bankDTO.getOrderNo().isEmpty()){ |
|||
return Result.error("时间或订单号为空"); |
|||
} |
|||
return bankService.getPayment(bankDTO); |
|||
} catch (Exception e) { |
|||
return Result.error(e.getMessage()); |
|||
} |
|||
} |
|||
|
|||
//stripe银行接口(批量) |
|||
@Log("stripe银行接口(批量)") |
|||
@PostMapping("/stripeAuto") |
|||
public Result stripeAuto(@RequestBody BankDTO bankDTO) { |
|||
try { |
|||
return Result.success(bankService.stripeAuto(bankDTO)); |
|||
} catch (Exception e) { |
|||
return Result.error(e.getMessage()); |
|||
} |
|||
} |
|||
|
|||
//stripe银行接口(单个) |
|||
@Log("stripe银行接口(单个)") |
|||
@PostMapping("/getStripe") |
|||
public Result getStripe(@RequestBody BankDTO bankDTO) { |
|||
try { |
|||
if(bankDTO.getTime().isEmpty() || bankDTO.getOrderNo().isEmpty()){ |
|||
return Result.error("时间或订单号为空"); |
|||
} |
|||
return bankService.getStripe(bankDTO); |
|||
} catch (Exception e) { |
|||
return Result.error(e.getMessage()); |
|||
} |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
package com.example.demo.domain.DTO; |
|||
|
|||
import lombok.Data; |
|||
import lombok.NoArgsConstructor; |
|||
|
|||
import java.util.List; |
|||
|
|||
/** |
|||
* @program: gold-java |
|||
* @ClassName BankDTO |
|||
* @description: |
|||
* @author: Double |
|||
* @create: 2025−11-21 10:32 |
|||
* @Version 1.0 |
|||
**/ |
|||
|
|||
@Data |
|||
@NoArgsConstructor |
|||
public class BankDTO { |
|||
//时间格式yyyyMMdd |
|||
private String time = ""; |
|||
//订单号 |
|||
private String orderNo = ""; |
|||
//交易ID |
|||
private String transactionId = ""; |
|||
} |
|||
@ -0,0 +1,37 @@ |
|||
package com.example.demo.domain.DTO; |
|||
|
|||
/** |
|||
* @program: gold-java |
|||
* @ClassName PaymentDTO |
|||
* @description: |
|||
* @author: Double |
|||
* @create: 2025−11-21 10:42 |
|||
* @Version 1.0 |
|||
**/ |
|||
|
|||
import com.fasterxml.jackson.annotation.JsonFormat; |
|||
import lombok.Data; |
|||
import lombok.NoArgsConstructor; |
|||
|
|||
import java.math.BigDecimal; |
|||
import java.util.Date; |
|||
|
|||
@Data |
|||
@NoArgsConstructor |
|||
public class PaymentDTO { |
|||
|
|||
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "Asia/Shanghai") |
|||
private Date time; |
|||
private String type; |
|||
private String provider; |
|||
private String request_reference; |
|||
private String merchant_reference; |
|||
private String provider_reference; |
|||
private String currency; |
|||
private BigDecimal order_amount; |
|||
private BigDecimal charge; |
|||
private BigDecimal net_amount; |
|||
private String status; |
|||
private String created_time; |
|||
private String completed_time; |
|||
} |
|||
@ -0,0 +1,31 @@ |
|||
package com.example.demo.domain.DTO; |
|||
|
|||
/** |
|||
* @program: gold-java |
|||
* @ClassName StripeDTO |
|||
* @description: |
|||
* @author: Double |
|||
* @create: 2025−11-21 15:26 |
|||
* @Version 1.0 |
|||
**/ |
|||
|
|||
|
|||
import com.fasterxml.jackson.annotation.JsonFormat; |
|||
import lombok.Data; |
|||
import lombok.NoArgsConstructor; |
|||
|
|||
import java.util.Date; |
|||
|
|||
@Data |
|||
@NoArgsConstructor |
|||
public class StripeDTO { |
|||
private String orderNo;//订单号 |
|||
private String balanceTransaction;//余额交易ID |
|||
private String currency;//货币 |
|||
private String amount;//金额 |
|||
private String fee;//手续费 |
|||
private String net;//净额 |
|||
private String chargeCurrency;//收款币种 |
|||
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "Asia/Shanghai") |
|||
private Date availableOn; |
|||
} |
|||
@ -0,0 +1,27 @@ |
|||
package com.example.demo.domain.vo.cash; |
|||
|
|||
import com.example.demo.domain.DTO.PaymentDTO; |
|||
import com.example.demo.domain.DTO.StripeDTO; |
|||
import lombok.Data; |
|||
import lombok.NoArgsConstructor; |
|||
|
|||
import java.util.List; |
|||
|
|||
/** |
|||
* @program: gold-java |
|||
* @ClassName BankVO |
|||
* @description: |
|||
* @author: Double |
|||
* @create: 2025−11-21 10:44 |
|||
* @Version 1.0 |
|||
**/ |
|||
|
|||
@Data |
|||
@NoArgsConstructor |
|||
public class BankVO { |
|||
private List<PaymentDTO> paymentDTOList; |
|||
private PaymentDTO paymentDTO; |
|||
private List<StripeDTO> stripeDTOList; |
|||
private StripeDTO stripeDTO; |
|||
private List<String> message; |
|||
} |
|||
@ -0,0 +1,30 @@ |
|||
package com.example.demo.service.cash; |
|||
|
|||
|
|||
import com.example.demo.domain.DTO.BankDTO; |
|||
import com.example.demo.domain.vo.cash.BankVO; |
|||
import com.example.demo.domain.vo.coin.Result; |
|||
import com.stripe.exception.StripeException; |
|||
|
|||
/** |
|||
* @program: gold-java |
|||
* @ClassName BankService |
|||
* @description: |
|||
* @author: Double |
|||
* @create: 2025−11-21 10:43 |
|||
* @Version 1.0 |
|||
**/ |
|||
|
|||
public interface BankService { |
|||
//payment银行接口(批量) |
|||
Result paymentAuto(BankDTO bankDTO); |
|||
|
|||
//payment银行接口(单个) |
|||
Result getPayment(BankDTO bankDTO); |
|||
|
|||
//stripe银行接口(批量) |
|||
Result stripeAuto(BankDTO bankDTO); |
|||
|
|||
//stripe银行接口(单个) |
|||
Result getStripe(BankDTO bankDTO) throws StripeException; |
|||
} |
|||
@ -0,0 +1,410 @@ |
|||
package com.example.demo.serviceImpl.cash; |
|||
|
|||
import com.alibaba.fastjson.JSON; |
|||
import com.alibaba.fastjson.JSONArray; |
|||
import com.alibaba.fastjson.JSONObject; |
|||
import com.example.demo.Util.DateConvertUtil; |
|||
import com.example.demo.domain.DTO.BankDTO; |
|||
import com.example.demo.domain.DTO.PaymentDTO; |
|||
import com.example.demo.domain.DTO.StripeDTO; |
|||
import com.example.demo.domain.vo.cash.BankVO; |
|||
import com.example.demo.domain.vo.cash.CashCollection; |
|||
import com.example.demo.domain.vo.coin.Result; |
|||
import com.example.demo.mapper.cash.CashCollectionMapper; |
|||
import com.example.demo.service.cash.BankService; |
|||
import com.stripe.Stripe; |
|||
import com.stripe.exception.StripeException; |
|||
import com.stripe.model.BalanceTransaction; |
|||
import com.stripe.model.Charge; |
|||
import com.stripe.model.ChargeCollection; |
|||
import com.stripe.param.ChargeListParams; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.http.*; |
|||
import org.springframework.stereotype.Service; |
|||
import org.springframework.util.LinkedMultiValueMap; |
|||
import org.springframework.util.MultiValueMap; |
|||
import org.springframework.web.client.RestTemplate; |
|||
|
|||
import java.math.BigDecimal; |
|||
import java.nio.charset.StandardCharsets; |
|||
import java.security.MessageDigest; |
|||
import java.security.NoSuchAlgorithmException; |
|||
import java.text.SimpleDateFormat; |
|||
import java.util.*; |
|||
|
|||
/** |
|||
* @program: gold-java |
|||
* @ClassName BankServiceImpl |
|||
* @description: |
|||
* @author: Double |
|||
* @create: 2025−11-21 10:46 |
|||
* @Version 1.0 |
|||
**/ |
|||
|
|||
@Service |
|||
@Slf4j |
|||
public class BankServiceImpl implements BankService { |
|||
|
|||
@Autowired |
|||
private CashCollectionMapper cashCollectionMapper; |
|||
|
|||
// 第三方API地址 |
|||
private static final String API_URL = "https://gateway.pa-sys.com/v1.1/reconciliation/519e26b2-8145-418c-b3e7-c1e88e52b946/settlement"; |
|||
// 签名密钥 |
|||
private static final String SECRET = "8987d1b8-1d82-4b15-af06-828d0b12076f"; |
|||
|
|||
// 注入RestTemplate用于HTTP请求(需在Spring配置类中定义) |
|||
private final RestTemplate restTemplate; |
|||
|
|||
public BankServiceImpl(RestTemplate restTemplate) { |
|||
this.restTemplate = restTemplate; |
|||
} |
|||
|
|||
//payment银行接口(单个) |
|||
@Override |
|||
public Result getPayment(BankDTO bankDTO) { |
|||
try { |
|||
// 1. 准备参数 |
|||
String settlementDate = bankDTO.getTime(); // 从BankDTO对象获取time作为settlement_date |
|||
String network = "FPS"; // 固定值 |
|||
// 2. 生成签名 |
|||
Map<String, String> params = new TreeMap<>(); // 按key升序排序 |
|||
params.put("settlement_date", settlementDate); |
|||
params.put("network", network); |
|||
|
|||
String signSource = buildQueryString(params) + SECRET; |
|||
String sign = sha512(signSource); |
|||
|
|||
// 3. 构建form-data请求参数 |
|||
MultiValueMap<String, String> formData = new LinkedMultiValueMap<>(); |
|||
formData.add("settlement_date", settlementDate); |
|||
formData.add("network", network); |
|||
formData.add("sign", sign); |
|||
|
|||
// 4. 发送HTTP POST请求(优化:显式设置multipart/form-data的字符集) |
|||
HttpHeaders headers = new HttpHeaders(); |
|||
// 补充charset=UTF-8,避免部分服务器对编码敏感 |
|||
headers.setContentType(new MediaType("multipart", "form-data", StandardCharsets.UTF_8)); |
|||
HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(formData, headers); |
|||
|
|||
// 调用第三方API(使用配置好SSL协议的RestTemplate) |
|||
ResponseEntity<String> response = restTemplate.exchange( |
|||
API_URL, |
|||
HttpMethod.POST, |
|||
requestEntity, |
|||
String.class |
|||
); |
|||
|
|||
if (response.getStatusCode().is2xxSuccessful()) { |
|||
String responseBody = response.getBody(); |
|||
log.info("第三方API响应: {}", responseBody); |
|||
|
|||
// 解析JSON获取payload.transactions数组 |
|||
JSONObject jsonObject = JSON.parseObject(responseBody); |
|||
JSONArray transactions = jsonObject.getJSONObject("payload").getJSONArray("transactions"); |
|||
|
|||
// 创建BankDTO并设置paymentDTOList |
|||
BankVO bankVO = new BankVO(); |
|||
List<PaymentDTO> paymentDTOList = transactions.toJavaList(PaymentDTO.class); |
|||
bankVO.setPaymentDTOList(paymentDTOList); |
|||
// 获取订单号 |
|||
String orderNo = bankDTO.getOrderNo(); |
|||
|
|||
// 如果订单号不为空,则进行匹配查找 |
|||
if (orderNo != null && !orderNo.isEmpty()) { |
|||
boolean found = false; |
|||
for (PaymentDTO paymentDTO : paymentDTOList) { |
|||
if (orderNo.equals(paymentDTO.getMerchant_reference())) { |
|||
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd"); |
|||
paymentDTO.setTime(sdf.parse(bankDTO.getTime())); |
|||
paymentDTO.setOrder_amount(paymentDTO.getOrder_amount().multiply(BigDecimal.valueOf(100))); |
|||
paymentDTO.setCharge(paymentDTO.getCharge().multiply(BigDecimal.valueOf(100))); |
|||
paymentDTO.setNet_amount(paymentDTO.getNet_amount().multiply(BigDecimal.valueOf(100))); |
|||
paymentDTO.setCurrency("2"); |
|||
bankVO.setPaymentDTO(paymentDTO); |
|||
found = true; |
|||
break; |
|||
} |
|||
} |
|||
// 如果没有找到匹配的订单号,返回提示信息 |
|||
if (!found) { |
|||
log.info("当前日期 {} 该订单号 {} 未查到", settlementDate, orderNo); |
|||
// 可以根据业务需求进行相应处理,比如抛出异常或设置特定标识 |
|||
return Result.error("payment当前日期 " + settlementDate + " 该订单号 " + orderNo + " 未查到"); |
|||
} |
|||
} |
|||
|
|||
CashCollection cashCollection = cashCollectionMapper.selectByGoldCoinOrderCode(orderNo); |
|||
if (cashCollection == null) { |
|||
return Result.error("金币系统当前日期 " + settlementDate + " 该订单号 " + orderNo + " 未查到"); |
|||
} else { |
|||
cashCollectionMapper.updateByGoldCoinOrderCodeByPayment(bankVO.getPaymentDTO()); |
|||
} |
|||
return Result.success(bankVO); |
|||
} else { |
|||
throw new RuntimeException("API请求失败,状态码: " + response.getStatusCodeValue()); |
|||
} |
|||
|
|||
} catch (Exception e) { |
|||
log.error("payment银行接口处理失败", e); |
|||
throw new RuntimeException("处理失败: " + e.getMessage()); |
|||
} |
|||
} |
|||
|
|||
//payment银行接口(批量) |
|||
@Override |
|||
public Result paymentAuto(BankDTO bankDTO) { |
|||
try { |
|||
// 1. 准备参数 |
|||
String settlementDate = bankDTO.getTime(); // 从BankDTO对象获取time作为settlement_date |
|||
String network = "FPS"; // 固定值 |
|||
// 2. 生成签名 |
|||
Map<String, String> params = new TreeMap<>(); // 按key升序排序 |
|||
params.put("settlement_date", settlementDate); |
|||
params.put("network", network); |
|||
|
|||
String signSource = buildQueryString(params) + SECRET; |
|||
String sign = sha512(signSource); |
|||
|
|||
// 3. 构建form-data请求参数 |
|||
MultiValueMap<String, String> formData = new LinkedMultiValueMap<>(); |
|||
formData.add("settlement_date", settlementDate); |
|||
formData.add("network", network); |
|||
formData.add("sign", sign); |
|||
|
|||
// 4. 发送HTTP POST请求(优化:显式设置multipart/form-data的字符集) |
|||
HttpHeaders headers = new HttpHeaders(); |
|||
// 补充charset=UTF-8,避免部分服务器对编码敏感 |
|||
headers.setContentType(new MediaType("multipart", "form-data", StandardCharsets.UTF_8)); |
|||
HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(formData, headers); |
|||
|
|||
// 调用第三方API(使用配置好SSL协议的RestTemplate) |
|||
ResponseEntity<String> response = restTemplate.exchange( |
|||
API_URL, |
|||
HttpMethod.POST, |
|||
requestEntity, |
|||
String.class |
|||
); |
|||
|
|||
if (response.getStatusCode().is2xxSuccessful()) { |
|||
String responseBody = response.getBody(); |
|||
log.info("第三方API响应: {}", responseBody); |
|||
|
|||
// 解析JSON获取payload.transactions数组 |
|||
JSONObject jsonObject = JSON.parseObject(responseBody); |
|||
JSONArray transactions = jsonObject.getJSONObject("payload").getJSONArray("transactions"); |
|||
|
|||
// 创建BankVO并设置paymentDTOList |
|||
BankVO bankVO = new BankVO(); |
|||
List<PaymentDTO> paymentDTOList = transactions.toJavaList(PaymentDTO.class); |
|||
bankVO.setPaymentDTOList(paymentDTOList); |
|||
// 收集处理信息 |
|||
List<String> messages = new ArrayList<>(); |
|||
// 对paymentDTOList中的每个元素进行处理 |
|||
List<PaymentDTO> processedPayments = new ArrayList<>(); |
|||
for (PaymentDTO paymentDTO : paymentDTOList) { |
|||
try { |
|||
|
|||
// 格式化时间 |
|||
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd"); |
|||
paymentDTO.setTime(sdf.parse(settlementDate)); |
|||
// 金额转换(假设原始数据是元为单位,需要转为分为单位) |
|||
paymentDTO.setOrder_amount(paymentDTO.getOrder_amount().multiply(BigDecimal.valueOf(100))); |
|||
paymentDTO.setCharge(paymentDTO.getCharge().multiply(BigDecimal.valueOf(100))); |
|||
paymentDTO.setNet_amount(paymentDTO.getNet_amount().multiply(BigDecimal.valueOf(100))); |
|||
// 设置货币代码 |
|||
paymentDTO.setCurrency("2"); |
|||
|
|||
// 获取订单号 |
|||
String orderNo = paymentDTO.getMerchant_reference(); |
|||
|
|||
// 查询金币系统中的订单 |
|||
CashCollection cashCollection = cashCollectionMapper.selectByGoldCoinOrderCode(orderNo); |
|||
if (cashCollection != null) { |
|||
// 更新金币系统中的订单信息 |
|||
cashCollectionMapper.updateByGoldCoinOrderCodeByPayment(paymentDTO); |
|||
processedPayments.add(paymentDTO); |
|||
log.info("成功处理订单: {}", orderNo); |
|||
messages.add("成功处理订单: " + orderNo); |
|||
} else { |
|||
log.warn("金币系统中未找到订单: {}", orderNo); |
|||
messages.add("金币系统中未找到订单: " + orderNo); |
|||
} |
|||
} catch (Exception e) { |
|||
log.error("处理单个支付记录时发生错误: {}", paymentDTO.getMerchant_reference(), e); |
|||
} |
|||
} |
|||
|
|||
// 设置处理成功的支付列表 |
|||
bankVO.setPaymentDTOList(processedPayments); |
|||
bankVO.setMessage(messages); |
|||
|
|||
return Result.success(bankVO); |
|||
} else { |
|||
throw new RuntimeException("API请求失败,状态码: " + response.getStatusCodeValue()); |
|||
} |
|||
|
|||
} catch (Exception e) { |
|||
log.error("payment银行接口处理失败", e); |
|||
throw new RuntimeException("处理失败: " + e.getMessage()); |
|||
} |
|||
} |
|||
|
|||
//stripe银行接口(批量) |
|||
@Override |
|||
public Result stripeAuto(BankDTO bankDTO) { |
|||
return Result.success(bankDTO); |
|||
} |
|||
|
|||
//stripe银行接口(单个) |
|||
@Override |
|||
public Result getStripe(BankDTO bankDTO) throws StripeException { |
|||
try { |
|||
// 设置Stripe API密钥 |
|||
Stripe.apiKey = "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"; |
|||
// 方式一:通过订单号查找最近数据 |
|||
String orderNo = bankDTO.getOrderNo(); |
|||
|
|||
if (orderNo == null || orderNo.isEmpty()) { |
|||
return Result.error("订单号为空"); |
|||
} |
|||
|
|||
// 从Stripe获取最近的收费记录(最多200条) |
|||
List<Charge> allCharges = new ArrayList<>(); |
|||
String startingAfter = null; |
|||
int totalLimit = 200; // 目标获取条数 |
|||
int pageSize = 100; // 单次最大获取条数 |
|||
|
|||
do { |
|||
// 计算当前页需查询的条数(最后一页可能不足100条) |
|||
int currentPageSize = Math.min(pageSize, totalLimit - allCharges.size()); |
|||
if (currentPageSize <= 0) { |
|||
break; // 已获取足够条数,停止 |
|||
} |
|||
|
|||
// 构建分页参数 |
|||
ChargeListParams params = ChargeListParams.builder() |
|||
.setLimit((long) currentPageSize) |
|||
.setStartingAfter(startingAfter) |
|||
.build(); |
|||
|
|||
try { |
|||
// 执行分页查询 |
|||
ChargeCollection charges = Charge.list(params); |
|||
List<Charge> currentPageData = charges.getData(); |
|||
allCharges.addAll(currentPageData); |
|||
|
|||
// 更新分页游标:若有下一页且未达200条,继续 |
|||
boolean hasMore = charges.getHasMore() && allCharges.size() < totalLimit; |
|||
startingAfter = hasMore ? currentPageData.get(currentPageData.size() - 1).getId() : null; |
|||
|
|||
} catch (StripeException e) { |
|||
log.error("Stripe 分页查询失败:" + e.getMessage()); |
|||
break; // 异常时停止查询,返回已获取的数据 |
|||
} |
|||
|
|||
} while (startingAfter != null); |
|||
|
|||
// 在获取的所有记录中查找匹配订单号的记录 |
|||
Charge matchedCharge = null; |
|||
for (Charge charge : allCharges) { |
|||
// 从metadata中获取订单号进行匹配 |
|||
if (charge.getMetadata() != null) { |
|||
String chargeOrderNo = charge.getMetadata().get("order_no"); |
|||
if (chargeOrderNo != null && orderNo.equals(chargeOrderNo)) { |
|||
matchedCharge = charge; |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
|
|||
// 如果未找到匹配的订单,返回错误信息 |
|||
if (matchedCharge == null) { |
|||
return Result.error("未找到订单号 " + orderNo + " 的支付记录"); |
|||
} |
|||
|
|||
// 获取匹配到的charge对应的余额交易ID |
|||
String balanceTransactionId = matchedCharge.getBalanceTransaction(); |
|||
|
|||
// 通过余额交易ID获取详细信息 |
|||
BalanceTransaction balanceTransaction = BalanceTransaction.retrieve(balanceTransactionId); |
|||
|
|||
// 创建StripeDTO对象并填充所需数据点 |
|||
StripeDTO stripeDTO = new StripeDTO(); |
|||
// 设置订单号 |
|||
stripeDTO.setOrderNo(matchedCharge.getMetadata().get("order_no")); |
|||
// 设置余额交易ID |
|||
stripeDTO.setBalanceTransaction(matchedCharge.getBalanceTransaction()); |
|||
|
|||
// 设置付款币种和金额(来自charge) |
|||
stripeDTO.setCurrency(matchedCharge.getCurrency().toUpperCase()); |
|||
stripeDTO.setAmount(String.valueOf(matchedCharge.getAmount())); |
|||
|
|||
// 设置收款币种(来自charge) |
|||
stripeDTO.setChargeCurrency(matchedCharge.getCurrency().toUpperCase()); |
|||
|
|||
// 设置到账金额和手续费(来自balanceTransaction) |
|||
stripeDTO.setNet(String.valueOf(balanceTransaction.getNet())); |
|||
stripeDTO.setFee(String.valueOf(balanceTransaction.getFee())); |
|||
|
|||
// 设置到账币种(来自balanceTransaction) |
|||
stripeDTO.setCurrency(balanceTransaction.getCurrency().toUpperCase()); |
|||
|
|||
// 设置available_on日期 |
|||
if (balanceTransaction.getAvailableOn() != null) { |
|||
long availableOnInSeconds = balanceTransaction.getAvailableOn(); |
|||
// 将Unix时间戳转换为Date对象 |
|||
Date availableOnDate = new Date(availableOnInSeconds * 1000L); |
|||
stripeDTO.setAvailableOn(availableOnDate); |
|||
} |
|||
|
|||
// 创建响应VO对象 |
|||
BankVO bankVO = new BankVO(); |
|||
bankVO.setStripeDTO(stripeDTO); |
|||
CashCollection cashCollection = cashCollectionMapper.selectByGoldCoinOrderCode(orderNo); |
|||
if (cashCollection == null) { |
|||
return Result.error("金币系统当前日期 " + " 该订单号 " + orderNo + " 未查到"); |
|||
} else { |
|||
cashCollectionMapper.updateByGoldCoinOrderCodeByStripe(bankVO.getStripeDTO()); |
|||
} |
|||
return Result.success(bankVO); |
|||
} catch (Exception e) { |
|||
log.error("stripe银行接口处理失败", e); |
|||
throw new RuntimeException("处理失败: " + e.getMessage()); |
|||
} |
|||
} |
|||
|
|||
|
|||
/** |
|||
* http_build_query的查询字符串(key=value&key=value) |
|||
*/ |
|||
private String buildQueryString(Map<String, String> params) { |
|||
StringBuilder sb = new StringBuilder(); |
|||
for (Map.Entry<String, String> entry : params.entrySet()) { |
|||
if (sb.length() > 0) { |
|||
sb.append("&"); |
|||
} |
|||
sb.append(entry.getKey()).append("=").append(entry.getValue()); |
|||
} |
|||
return sb.toString(); |
|||
} |
|||
|
|||
/** |
|||
* SHA512加密 |
|||
*/ |
|||
private String sha512(String content) throws NoSuchAlgorithmException { |
|||
MessageDigest md = MessageDigest.getInstance("SHA-512"); |
|||
byte[] bytes = md.digest(content.getBytes(StandardCharsets.UTF_8)); |
|||
// 转换为十六进制字符串 |
|||
StringBuilder hexStr = new StringBuilder(); |
|||
for (byte b : bytes) { |
|||
String hex = Integer.toHexString(0xff & b); |
|||
if (hex.length() == 1) { |
|||
hexStr.append('0'); |
|||
} |
|||
hexStr.append(hex); |
|||
} |
|||
return hexStr.toString(); |
|||
} |
|||
} |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue