Merge remote-tracking branch 'origin/main'

This commit is contained in:
attiya 2025-04-28 11:33:22 +08:00
commit 5fa9515ed9
34 changed files with 1780 additions and 0 deletions

View File

@ -85,4 +85,5 @@ public class EbikeSysRoperatesetController {
return JsonResult.success(dto);
}
}

View File

@ -24,4 +24,6 @@ public interface EbikeSysRoperatesetService extends IService<EbikeSysRoperateset
* @return
*/
Boolean deleteByRegionId(String regionId);
// Boolean save()
}

172
ebike-payment/pom.xml Normal file
View File

@ -0,0 +1,172 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.cdzy</groupId>
<artifactId>ebike-share</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>ebike-payment</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>ebike-payment</name>
<description>ebike-payment-process</description>
<packaging>jar</packaging>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>com.cdzy</groupId>
<artifactId>ebike-common</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.cdzy</groupId>
<artifactId>ebike-feign</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<!-- 微信支付SDK -->
<dependency>
<groupId>com.github.wechatpay-apiv3</groupId>
<artifactId>wechatpay-java</artifactId>
<version>0.2.17</version>
</dependency>
<!-- mybatis-flex -->
<dependency>
<groupId>com.mybatis-flex</groupId>
<artifactId>mybatis-flex-spring-boot3-starter</artifactId>
<version>${mybatis-flex.version}</version>
</dependency>
<dependency>
<groupId>com.mybatis-flex</groupId>
<artifactId>mybatis-flex-processor</artifactId>
<version>${mybatis-flex.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${org.projectlombok.version}</version>
</dependency>
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>${HikariCP.version}</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>${mybatis.version}</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-core</artifactId>
<version>${hutool.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
<version>${boot.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>${okhttp.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>${fastjson.version}</version>
</dependency>
<!-- nacos客户端依赖包 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>${nacos.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>${mysql.version}</version>
<scope>runtime</scope>
</dependency>
<!-- for test only -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.mybatis-flex</groupId>
<artifactId>mybatis-flex-codegen</artifactId>
<version>1.10.8</version>
<scope>test</scope>
</dependency>
</dependencies>
<profiles>
<!--开发环境-->
<profile>
<id>dev</id>
<properties>
<profile.active>dev</profile.active>
<!--谁设置为true 就激活那个环境-->
<activeByDefault>true</activeByDefault>
</properties>
</profile>
<!--生产环境-->
<profile>
<id>prod</id>
<properties>
<profile.active>prod</profile.active>
</properties>
</profile>
<profile>
<id>test</id>
<properties>
<profile.active>test</profile.active>
</properties>
</profile>
</profiles>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${boot.version}</version> <!-- 与项目版本一致 -->
<executions>
<execution>
<goals>
<goal>repackage</goal> <!-- 必须包含此目标 -->
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,23 @@
package com.cdzy.payment;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
/**
* 支付模块
*
* @author dingchao
* @date 2025/4/24
* @modified by:
*/
@SpringBootApplication
@MapperScan("com.cdzy.payment.mapper")
//引入Spring Task
@EnableScheduling
public class EbikePaymentApplication {
public static void main(String[] args) {
SpringApplication.run(EbikePaymentApplication.class, args);
}
}

View File

@ -0,0 +1,41 @@
package com.cdzy.payment.config;
import com.mybatisflex.annotation.KeyType;
import com.mybatisflex.core.FlexGlobalConfig;
import com.mybatisflex.core.audit.AuditManager;
import com.mybatisflex.core.dialect.DbType;
import com.mybatisflex.core.dialect.DialectFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;
/**
* @author attiya
*/
@Configuration
public class MyBatisFlexConfiguration {
private static final Logger logger = LoggerFactory
.getLogger("mybatis-flex-sql:");
public MyBatisFlexConfiguration() {
//开启审计功能
AuditManager.setAuditEnable(true);
//设置 SQL 审计收集器
AuditManager.setMessageCollector(auditMessage ->
logger.info("{},{}ms", auditMessage.getFullSql()
, auditMessage.getElapsedTime())
);
//全局ID生成策略配置
FlexGlobalConfig.KeyConfig keyConfig = new FlexGlobalConfig.KeyConfig();
keyConfig.setKeyType(KeyType.Generator);
keyConfig.setValue("snowFlakeId");
keyConfig.setBefore(true);
FlexGlobalConfig.getDefaultConfig().setKeyConfig(keyConfig);
DialectFactory.registerDialect(DbType.MYSQL,new PermissionDialect());
}
}

View File

@ -0,0 +1,24 @@
package com.cdzy.payment.config;
import com.mybatisflex.core.dialect.impl.CommonsDialectImpl;
import com.mybatisflex.core.query.CPI;
import com.mybatisflex.core.query.QueryTable;
import com.mybatisflex.core.query.QueryWrapper;
import java.util.List;
/**
* @author attiya
* @since 2025-03-14
*/
public class PermissionDialect extends CommonsDialectImpl {
@Override
public String forSelectByQuery(QueryWrapper queryWrapper) {
//用于严重table是否需要添加数据权限查询条件
List<QueryTable> tables = CPI.getQueryTables(queryWrapper);
//获取当前用户信息 queryWrapper 添加额外的条件
return super.buildSelectSql(queryWrapper);
}
}

View File

@ -0,0 +1,90 @@
package com.cdzy.payment.config;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.core.cipher.RSASigner;
import com.wechat.pay.java.core.util.PemUtil;
import com.wechat.pay.java.service.payments.jsapi.JsapiService;
import com.wechat.pay.java.service.refund.RefundService;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.security.PrivateKey;
/**
* 微信支付配置参数
*
* @author dingchao
* @date 2025/4/24
* @modified by:
*/
@Slf4j
@Data
@Configuration
@ConfigurationProperties(prefix = "payment.wx-pay")
public class WxPayConfig {
/**
* 微信小程序 appId
*/
private String appId;
/**
* 微信支付商户号
*/
private String merchantId;
/**
* 微信支付商户密钥文件路径
*/
private String privateKeyPath;
/**
* 微信支付商户证书序列号
*/
private String merchantSerialNumber;
/**
* 微信支付商户 APIv3 密钥
*/
private String apiV3Key;
/**
* 微信支付回调地址
*/
private String payNotifyUrl;
/**
* 微信支付退款回调地址
*/
private String refundNotifyUrl;
/**
* 支付退款过期时间分钟
* 默认30分钟超过30分钟订单会自动关闭
*/
private Integer expireMinute = 30;
@Bean("certConfig")
private Config certificateConfig() {
return new RSAAutoCertificateConfig.Builder()
.merchantId(merchantId)
// 使用 com.wechat.pay.java.core.util 中的函数从本地文件中加载商户私钥商户私钥会用来生成请求的签名
.privateKeyFromPath(privateKeyPath)
.merchantSerialNumber(merchantSerialNumber)
.apiV3Key(apiV3Key)
.build();
}
@Bean
public JsapiService wxJsapiService(@Qualifier("certConfig") Config certificateConfig) {
return new JsapiService.Builder().config(certificateConfig).build();
}
@Bean
public RefundService wxRefundService(@Qualifier("certConfig") Config certificateConfig) {
return new RefundService.Builder().config(certificateConfig).build();
}
@Bean
public RSASigner wxRsaSigner(){
PrivateKey privateKey = PemUtil.loadPrivateKeyFromPath(privateKeyPath);
return new RSASigner(merchantSerialNumber, privateKey);
}
}

View File

@ -0,0 +1,90 @@
package com.cdzy.payment.controller;
import com.alibaba.fastjson2.JSONObject;
import com.cdzy.common.model.JsonResult;
import com.cdzy.payment.model.dto.EbikePaymentDto;
import com.cdzy.payment.model.dto.EbikeRefundDto;
import com.cdzy.payment.service.WxPayService;
import com.wechat.pay.java.service.payments.model.Transaction;
import com.wechat.pay.java.service.refund.model.Refund;
import jakarta.annotation.Resource;
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.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 用户订单微信支付 控制层
*
* @author dingchao
* @since 2025-04-24
*/
@RestController
@RequestMapping("/wxPayment")
public class EbikeWxPaymentController {
@Resource
private WxPayService wxPayService;
/**
* 微信支付下单
*
* @param paymentDto 支付信息
* @return 下单成功返回true否则返回false
*/
@PostMapping("/prepay")
public JsonResult<?> prepay(@RequestBody EbikePaymentDto paymentDto) {
JSONObject r = wxPayService.prepay(paymentDto.getOrderId(), paymentDto.getDescription(), paymentDto.getGoodsTag(), paymentDto.getOpenId(), paymentDto.getAmount(), paymentDto.getDetail(), paymentDto.getClientIp());
return r == null?JsonResult.failed("下单失败"):JsonResult.success(r);
}
/**
* 通过交易订单号查询支付订单
*
* @param transactionId 微信支付订单号
* @return 支付订单信息
*/
@GetMapping("/queryOrderById/{transactionId}")
public JsonResult<?> queryOrderById(@PathVariable String transactionId) {
Transaction r = wxPayService.queryOrderById(transactionId);
return r == null?JsonResult.failed(String.format("交易订单号{%s}查询支付订单失败", transactionId)):JsonResult.success(r);
}
/**
* 通过商户(骑行)订单号查询支付订单
*
* @param outTradeNo 商户(骑行)订单号
* @return 支付订单信息
*/
@GetMapping("/queryOrderByOutTradeNo/{outTradeNo}")
public JsonResult<?> queryOrderByOutTradeNo(@PathVariable String outTradeNo) {
Transaction r = wxPayService.queryOrderByOutTradeNo(outTradeNo);
return r == null?JsonResult.failed(String.format("骑行订单号{%s}查询支付订单失败", outTradeNo)):JsonResult.success(r);
}
/**
* 退款申请
*
* @param refundDto 退款信息
* @return 退款成功返回true否则返回false
*/
@PostMapping("/refund")
public JsonResult<?> refund(@RequestBody EbikeRefundDto refundDto) {
String r = wxPayService.refund(refundDto.getPaymentId(), refundDto.getOrderId(), refundDto.getReason(), refundDto.getAmount());
return r == null?JsonResult.failed("退款失败"):JsonResult.success(r);
}
/**
* 通过退款单号查询退款信息
*
* @param outRefundNo 商户(骑行)退款单号
* @return 退款信息
*/
@GetMapping("/refundQuery/{outRefundNo}")
public JsonResult<?> refundQuery(@PathVariable String outRefundNo) {
Refund r = wxPayService.queryRefundByOutNo(outRefundNo);
return r == null?JsonResult.failed(String.format("退款单号{%s}查询退款失败", outRefundNo)):JsonResult.success(r);
}
}

View File

@ -0,0 +1,14 @@
package com.cdzy.payment.mapper;
import com.mybatisflex.core.BaseMapper;
import com.cdzy.payment.model.entity.EbikePayment;
/**
* 用户订单支付记录 映射层
*
* @author dingchao
* @since 2025-04-24
*/
public interface EbikePaymentMapper extends BaseMapper<EbikePayment> {
}

View File

@ -0,0 +1,14 @@
package com.cdzy.payment.mapper;
import com.mybatisflex.core.BaseMapper;
import com.cdzy.payment.model.entity.EbikeRefund;
/**
* 用户订单退款记录 映射层
*
* @author dingchao
* @since 2025-04-25
*/
public interface EbikeRefundMapper extends BaseMapper<EbikeRefund> {
}

View File

@ -0,0 +1,28 @@
package com.cdzy.payment.model.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
* 支付金额信息
*
* @author dingchao
* @date 2025/4/26
* @modified by:
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class AmountDto implements Serializable {
/**
* 订单金额单位
*/
private Double total;
/**
* 货币类型默认CNY
*/
private String currency="CNY";
}

View File

@ -0,0 +1,32 @@
package com.cdzy.payment.model.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
* 退款信息 (忽略来源明细)
*
* @author dingchao
* @date 2025/4/26
* @modified by:
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class AmountRefundDto implements Serializable{
/**
* 退款金额单位为元
*/
private Double refund;
/**
* 原订单金额单位为元
*/
private Double total;
/**
* 退款币种 (默认CNY)
*/
private String currency = "CNY";
}

View File

@ -0,0 +1,33 @@
package com.cdzy.payment.model.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.List;
/**
* 支付明细信息
*
* @author dingchao
* @date 2025/4/26
* @modified by:
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class DetailDto implements Serializable {
/**
* 订单原价单位
*/
private Double costPrice;
/**
* 商品小票ID可选
*/
private String invoiceId;
/**
* 收费明显列表
*/
private List<PayDetailDto> goodsDetail;
}

View File

@ -0,0 +1,46 @@
package com.cdzy.payment.model.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 用户订单微信支付 请求类
*
* @author dingchao
* @date 2025/4/25
* @modified by:
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class EbikePaymentDto {
/**
* 商户(骑行)订单号
*/
private String orderId;
/**
* 商品描述
*/
private String description;
/**
* 商品标记
*/
private String goodsTag;
/**
* 用户标识
*/
private String openId;
/**
* 金额
*/
private AmountDto amount;
/**
* 费用详情
*/
private DetailDto detail;
/**
* 客户端IP
*/
private String clientIp;
}

View File

@ -0,0 +1,38 @@
package com.cdzy.payment.model.dto;
import com.wechat.pay.java.service.payments.jsapi.model.Amount;
import com.wechat.pay.java.service.payments.jsapi.model.Detail;
import com.wechat.pay.java.service.refund.model.AmountReq;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 用户订单退款 请求类
*
* @author dingchao
* @date 2025/4/25
* @modified by:
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class EbikeRefundDto {
/**
* 商户(骑行)订单号
*/
private String orderId;
/**
* 支付交易会话标识
*/
private String paymentId;
/**
* 退款原因
*/;
private String reason;
/**
* 金额
*/
private AmountRefundDto amount;
}

View File

@ -0,0 +1,36 @@
package com.cdzy.payment.model.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
* 支付费用明细信息
*
* @author dingchao
* @date 2025/4/26
* @modified by:
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class PayDetailDto implements Serializable {
/**
* 费用项目ID
*/
private String itemId;
/**
* 费用名称
*/
private String itemName;
/**
* 数量
*/
private Integer quantity;
/**
* 单价(单位)
*/
private Double unitPrice;
}

View File

@ -0,0 +1,69 @@
package com.cdzy.payment.model.dto;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.annotation.JSONField;
import com.wechat.pay.java.core.cipher.RSASigner;
import com.wechat.pay.java.core.cipher.SignatureResult;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import java.util.StringJoiner;
/**
* JSAPI 调起微信支付参数
*
* @author dingchao
* @date 2025/4/25
* @modified by:
*/
@Slf4j
@Data
@NoArgsConstructor
public class WxJsapiPromptDto {
/**
* 下单时传入的appid
*/
private String appId;
/**
* 传秒级时间戳
*/
private String timeStamp;
/**
* 随机字符串
*/
private String nonceStr;
/**
* 订单详情扩展提交格式如prepay_id=***
*/
// json中转为 "package"
@JSONField(name = "package")
private String extension;
/**
* 签名类型固定填RSA
*/
private String signType = "RSA";
/**
* 签名使用字段appIdtimeStampnonceStrpackage计算得出的签名值
*/
private String paySign;
public JSONObject toJson(RSASigner rsaSigner) {
try{
// 计算签名
StringJoiner signMsg = new StringJoiner("\n");
signMsg.add(appId);
signMsg.add(timeStamp);
signMsg.add(nonceStr);
signMsg.add(extension);
SignatureResult sign = rsaSigner.sign(signMsg.toString());
this.paySign = sign.getSign();
//result.put("package", result.remove("extension"));
return JSONObject.parseObject(JSONObject.toJSONString(this));
}catch (Exception e){
log.error("计算签名失败", e);
throw new RuntimeException("计算签名失败", e);
}
}
}

View File

@ -0,0 +1,83 @@
package com.cdzy.payment.model.entity;
import com.mybatisflex.annotation.Id;
import com.mybatisflex.annotation.Table;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.io.Serial;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 用户订单支付记录 实体类
*
* @author dingchao
* @since 2025-04-24
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Table("ebike_payment")
public class EbikePayment implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
@Id
private String recordId;
/**
* 骑行订单号
*/
@Id
private String orderId;
/**
* 支付交易会话标识;有效期为2小时
*/
private String paymentId;
/**
* 提交时间
*/
private LocalDateTime createTime;
/**
* 支付成功时间
*/
private LocalDateTime paymentTime;
/**
* 支付方式;wechat/alipay/balance
*/
private String paymentMethod;
/**
* 支付状态;0支付成功 1退款 2未支付 3关闭 4取消 5支付中 6支付错误 7接受
*/
private String tradeState;
/**
* 订单原价
*/
private Double costPrice;
/**
* 货币
*/
private String currency;
/**
* 总金额
*/
private Double total;
}

View File

@ -0,0 +1,89 @@
package com.cdzy.payment.model.entity;
import com.mybatisflex.annotation.Id;
import com.mybatisflex.annotation.Table;
import java.io.Serializable;
import java.math.BigDecimal;
import java.io.Serial;
import java.time.LocalDateTime;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 用户订单退款记录 实体类
*
* @author dingchao
* @since 2025-04-25
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Table("ebike_refund")
public class EbikeRefund implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
@Id
private String refundId;
/**
* 骑行订单号
*/
private String orderId;
/**
* 支付记录id
*/
private String recordId;
/**
* 支付交易会话标识;有效期为2小时
*/
private String paymentId;
/**
* 提交时间
*/
private LocalDateTime createTime;
/**
* 退款成功时间
*/
private LocalDateTime refundTime;
/**
* 支付状态;0退款成功 1关闭 2退款中 3异常
*/
private String status;
/**
* 货币
*/
private String currency;
/**
* 退款金额
*/
private Double refund;
/**
* 退款总金额
*/
private Double total;
/**
* 退款原因
*/
private String reason;
}

View File

@ -0,0 +1,15 @@
package com.cdzy.payment.model.enums;
import com.google.gson.annotations.SerializedName;
public enum PayMethod {
@SerializedName("wechat")
wechat,
@SerializedName("alipay")
alipay,
@SerializedName("balance")
balance;
private PayMethod() {
}
}

View File

@ -0,0 +1,32 @@
package com.cdzy.payment.service;
import com.mybatisflex.core.service.IService;
import com.cdzy.payment.model.entity.EbikePayment;
import com.wechat.pay.java.service.payments.model.Transaction;
import java.util.List;
/**
* 用户订单支付记录 服务层
*
* @author dingchao
* @since 2025-04-24
*/
public interface EbikePaymentService extends IService<EbikePayment> {
/**
* 查询未支付订单
*
* @param duration 订单创建时间超过duration分钟单位分钟
* @return 未支付订单列表
*/
List<EbikePayment> getNoPayOrderByDuration(int duration);
/**
* 更新支付状态
*
* @param recordId 记录ID
* @param transaction 支付结果
* @return 更新成功返回true否则返回false
*/
Boolean updatePaymentStatus(String recordId, Transaction transaction);
}

View File

@ -0,0 +1,41 @@
package com.cdzy.payment.service;
import com.mybatisflex.core.service.IService;
import com.cdzy.payment.model.entity.EbikeRefund;
import com.wechat.pay.java.service.payments.model.Transaction;
import com.wechat.pay.java.service.refund.model.Refund;
import java.util.List;
/**
* 用户订单退款记录 服务层
*
* @author dingchao
* @since 2025-04-25
*/
public interface EbikeRefundService extends IService<EbikeRefund> {
/**
* 保存退款记录
*
* @param ebikeRefund 退款结果
* @return 保存成功返回主键id否则返回null
*/
Boolean saveRefundResult(EbikeRefund ebikeRefund);
/**
* 查询未成功退款订单
*
* @param duration 订单创建时间超过duration分钟单位分钟
* @return 未成功退款订单列表
*/
List<EbikeRefund> getNoSuccessRefundOrderByDuration(int duration);
/**
* 更新退款状态
*
* @param refundId 退款ID
* @param refund 退款结果
* @return 更新成功返回true否则返回false
*/
Boolean updateRefundStatus(String refundId, Refund refund);
}

View File

@ -0,0 +1,73 @@
package com.cdzy.payment.service;
import com.alibaba.fastjson2.JSONObject;
import com.cdzy.payment.model.dto.AmountDto;
import com.cdzy.payment.model.dto.AmountRefundDto;
import com.cdzy.payment.model.dto.DetailDto;
import com.wechat.pay.java.service.payments.model.Transaction;
import com.wechat.pay.java.service.refund.model.Refund;
/**
* 微信支付服务类JSAPI支付小程序
*
* @author dingchao
* @date 2025/4/25
* @modified by:
*/
public interface WxPayService {
/**
* 关闭订单
*
* @param outTradeNo 商户(骑行)订单号
* @return
*/
boolean closeOrder(String outTradeNo);
/**
* JSAPI支付下单
*
* @param outTradeNo 商户(骑行)订单号
* @param description 商品描述
* @param goodsTag 商品标记
* @param openId 用户标识
* @param amount 金额
* @param detail 商品详情
* @param clientIp 客户端IP
* @return 下单成功返回true否则返回false
*/
JSONObject prepay(String outTradeNo, String description, String goodsTag, String openId, AmountDto amount, DetailDto detail, String clientIp);
/**
* 通过交易订单号查询支付订单
*
* @param transactionId 微信支付订单号
* @return 支付订单信息
*/
Transaction queryOrderById(String transactionId);
/**
* 通过商户(骑行)订单号查询支付订单
*
* @param outTradeNo 商户(骑行)订单号
* @return 支付订单信息
*/
Transaction queryOrderByOutTradeNo(String outTradeNo);
/**
* 退款申请
*
* @param transactionId 微信支付订单号
* @param outTradeNo 商户(骑行)订单号
* @param reason 退款原因
* @param amount 退款金额
* @return 退款信息id
*/
String refund(String transactionId, String outTradeNo, String reason, AmountRefundDto amount);
/**
* 通过商户退款单号查询退款信息
*
* @param outRefundNo 商户退款订单号
* @return 退款信息
*/
Refund queryRefundByOutNo(String outRefundNo);
}

View File

@ -0,0 +1,63 @@
package com.cdzy.payment.service.impl;
import com.cdzy.payment.model.enums.PayMethod;
import com.ebike.feign.clients.OrdersFeignClient;
import com.ebike.feign.model.res.ResFeignOrderPaymentDto;
import com.mybatisflex.core.query.QueryColumn;
import com.mybatisflex.core.query.QueryMethods;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import com.cdzy.payment.model.entity.EbikePayment;
import com.cdzy.payment.mapper.EbikePaymentMapper;
import com.cdzy.payment.service.EbikePaymentService;
import com.wechat.pay.java.service.payments.model.Transaction;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.List;
import static com.cdzy.payment.model.entity.table.EbikePaymentTableDef.EBIKE_PAYMENT;
/**
* 用户订单支付记录 服务层实现
*
* @author dingchao
* @since 2025-04-24
*/
@Service
public class EbikePaymentServiceImpl extends ServiceImpl<EbikePaymentMapper, EbikePayment> implements EbikePaymentService{
@Resource
private OrdersFeignClient ordersFeignClient;
@Override
public List<EbikePayment> getNoPayOrderByDuration(int duration) {
QueryColumn time = QueryMethods.secToTime(String.valueOf(duration * 60));
// trade_state 等于 NOTPAY未支付的 并且创建时间超过duration分钟的订单
QueryWrapper query = QueryWrapper.create()
.where(EBIKE_PAYMENT.TRADE_STATE.eq(Transaction.TradeStateEnum.NOTPAY.ordinal()))
.and(QueryMethods.addTime(EBIKE_PAYMENT.CREATE_TIME, time).ge(QueryMethods.now()));
return list(query);
}
@Override
public Boolean updatePaymentStatus(String recordId, Transaction transaction) {
EbikePayment ebikePayment = new EbikePayment();
ebikePayment.setRecordId(recordId);
ebikePayment.setTradeState(String.valueOf(transaction.getTradeState().ordinal()));
if (Transaction.TradeStateEnum.SUCCESS.equals(transaction.getTradeState())) {
// 支付成功 更新订单状态
double v = transaction.getAmount().getTotal().doubleValue() / 100.0;
ebikePayment.setTotal(v);
ebikePayment.setPaymentTime(LocalDateTime.now());
// 同步支付状态
ResFeignOrderPaymentDto paymentParam = new ResFeignOrderPaymentDto();
paymentParam.setOrderId(Long.valueOf(transaction.getOutTradeNo()));
paymentParam.setPaymentTime(ebikePayment.getPaymentTime());
paymentParam.setPaymentMethod(PayMethod.wechat.name());
ordersFeignClient.payment(paymentParam);
}
return updateById(ebikePayment);
}
}

View File

@ -0,0 +1,60 @@
package com.cdzy.payment.service.impl;
import com.ebike.feign.clients.OrdersFeignClient;
import com.mybatisflex.core.query.QueryMethods;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import com.cdzy.payment.model.entity.EbikeRefund;
import com.cdzy.payment.mapper.EbikeRefundMapper;
import com.cdzy.payment.service.EbikeRefundService;
import com.wechat.pay.java.service.payments.model.Transaction;
import com.wechat.pay.java.service.refund.model.Refund;
import com.wechat.pay.java.service.refund.model.Status;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.List;
import static com.cdzy.payment.model.entity.table.EbikeRefundTableDef.EBIKE_REFUND;
/**
* 用户订单退款记录 服务层实现
*
* @author dingchao
* @since 2025-04-25
*/
@Service
public class EbikeRefundServiceImpl extends ServiceImpl<EbikeRefundMapper, EbikeRefund> implements EbikeRefundService{
@Resource
private OrdersFeignClient ordersFeignClient;
@Override
public Boolean saveRefundResult(EbikeRefund ebikeRefund) {
// 同步发起退款
ordersFeignClient.refund(Long.valueOf(ebikeRefund.getOrderId()));
return save(ebikeRefund);
}
@Override
public List<EbikeRefund> getNoSuccessRefundOrderByDuration(int duration) {
// trade_state 不等于 SUCCESS退款成功的 并且创建时间超过duration分钟的订单
QueryWrapper query = QueryWrapper.create()
.where(EBIKE_REFUND.STATUS.ne(Status.SUCCESS.ordinal()))
.and(QueryMethods.addTime(EBIKE_REFUND.CREATE_TIME, QueryMethods.secToTime(String.valueOf(duration * 60))).ge(QueryMethods.now()));
return list(query);
}
@Override
public Boolean updateRefundStatus(String refundId, Refund refund) {
EbikeRefund ebikeRefund = new EbikeRefund();
ebikeRefund.setRefundId(refundId);
ebikeRefund.setStatus(String.valueOf(refund.getStatus().ordinal()));
if (Status.SUCCESS.equals(refund.getStatus())) {
ebikeRefund.setRefundTime(LocalDateTime.now());
ebikeRefund.setRefund(refund.getAmount().getRefund().doubleValue() / 100.0);
}
return updateById(ebikeRefund);
}
}

View File

@ -0,0 +1,245 @@
package com.cdzy.payment.service.impl;
import cn.hutool.core.util.RandomUtil;
import com.alibaba.fastjson2.JSONObject;
import com.cdzy.payment.config.WxPayConfig;
import com.cdzy.payment.model.dto.AmountDto;
import com.cdzy.payment.model.dto.AmountRefundDto;
import com.cdzy.payment.model.dto.DetailDto;
import com.cdzy.payment.model.dto.WxJsapiPromptDto;
import com.cdzy.payment.model.entity.EbikePayment;
import com.cdzy.payment.model.entity.EbikeRefund;
import com.cdzy.payment.model.enums.PayMethod;
import com.cdzy.payment.service.EbikePaymentService;
import com.cdzy.payment.service.EbikeRefundService;
import com.cdzy.payment.service.WxPayService;
import com.cdzy.payment.utils.StringUtil;
import com.wechat.pay.java.core.cipher.RSASigner;
import com.wechat.pay.java.core.exception.HttpException;
import com.wechat.pay.java.core.exception.MalformedMessageException;
import com.wechat.pay.java.core.exception.ServiceException;
import com.wechat.pay.java.service.payments.jsapi.model.*;
import com.wechat.pay.java.service.payments.jsapi.model.Amount;
import com.wechat.pay.java.service.payments.jsapi.model.GoodsDetail;
import com.wechat.pay.java.service.payments.model.Transaction;
import com.wechat.pay.java.service.refund.RefundService;
import com.wechat.pay.java.service.refund.model.*;
import com.wechat.pay.java.service.payments.jsapi.JsapiService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
/**
* 微信支付服务类JSAPI支付小程序
*
* @author dingchao
* @date 2025/4/25
* @modified by:
*/
@Slf4j
@Service
public class WxPayServiceImpl implements WxPayService {
@Resource
private JsapiService wxJsapiService;
@Resource
private RefundService wxRefundService;
@Resource
private WxPayConfig wxPayConfig;
@Resource
private RSASigner wxRsaSigner;
@Resource
private EbikePaymentService ebikePaymentService;
@Resource
private EbikeRefundService ebikeRefundService;
@Override
public boolean closeOrder(String outTradeNo) {
try {
CloseOrderRequest request = new CloseOrderRequest();
request.setOutTradeNo(outTradeNo);
wxJsapiService.closeOrder(request);
log.info("关闭订单成功,订单号:{}", outTradeNo);
//ebikePaymentService.updatePaymentStatus(outTradeNo, 2);
return true;
} catch (Exception e) {
logError("关闭订单closeOrder", e);
return false;
}
}
@Override
public JSONObject prepay(String outTradeNo, String description, String goodsTag, String openId, AmountDto amount, DetailDto detail, String clientIp) {
try {
PrepayRequest request = new PrepayRequest();
request.setAppid(wxPayConfig.getAppId());
request.setMchid(wxPayConfig.getMerchantId());
request.setDescription(description);
request.setOutTradeNo(outTradeNo);
request.setNotifyUrl(wxPayConfig.getPayNotifyUrl());
LocalDateTime expireTime = LocalDateTime.now().plusMinutes(wxPayConfig.getExpireMinute());
request.setTimeExpire(StringUtil.formatLocalDatetime(expireTime));
request.setGoodsTag(goodsTag);
Amount amountReq = new Amount();
amountReq.setTotal(BigDecimal.valueOf(amount.getTotal() * 100.0).intValue());
amountReq.setCurrency(amount.getCurrency());
request.setAmount(amountReq);
Payer payer = new Payer();
payer.setOpenid(openId);
request.setPayer(payer);
Detail detailReq = new Detail();
detailReq.setGoodsDetail(detail.getGoodsDetail().stream().map(goodsDetail -> {
GoodsDetail goodsDetailReq = new GoodsDetail();
goodsDetailReq.setGoodsName(goodsDetail.getItemName());
goodsDetailReq.setMerchantGoodsId(goodsDetail.getItemId());
goodsDetailReq.setQuantity(goodsDetail.getQuantity());
goodsDetailReq.setUnitPrice(BigDecimal.valueOf(goodsDetail.getUnitPrice()*100.0).intValue());
return goodsDetailReq;
}).toList());
detailReq.setCostPrice(BigDecimal.valueOf(detail.getCostPrice()*100.0).intValue());
detailReq.setInvoiceId(detail.getInvoiceId());
request.setDetail(detailReq);
SceneInfo sceneInfo = new SceneInfo();
sceneInfo.setPayerClientIp(clientIp);
request.setSceneInfo(sceneInfo);
PrepayResponse response = wxJsapiService.prepay(request);
String payId = response.getPrepayId();
//入库
if (payId != null) {
EbikePayment ebikePayment = new EbikePayment();
ebikePayment.setOrderId(outTradeNo);
ebikePayment.setPaymentId(payId);
ebikePayment.setCreateTime(LocalDateTime.now());
ebikePayment.setPaymentMethod(PayMethod.wechat.name());
ebikePayment.setCostPrice(amount.getTotal());
int state = Transaction.TradeStateEnum.NOTPAY.ordinal();
ebikePayment.setTradeState(String.valueOf(state));
ebikePaymentService.save(ebikePayment);
// 返回给小程序的参数调起微信支付
WxJsapiPromptDto wxJsapiPromptDto = new WxJsapiPromptDto();
wxJsapiPromptDto.setAppId(wxPayConfig.getAppId());
wxJsapiPromptDto.setTimeStamp(String.valueOf(Instant.now().getEpochSecond()));
wxJsapiPromptDto.setNonceStr(RandomUtil.randomStringUpper(32));
wxJsapiPromptDto.setExtension("prepay_id=" + payId);
return wxJsapiPromptDto.toJson(wxRsaSigner);
}
log.error("微信支付下单prepay失败订单号{}", outTradeNo);
return null;
}catch (Exception e) {
logError("微信支付下单prepay", e);
return null;
}
}
@Override
public Transaction queryOrderById(String transactionId) {
try {
QueryOrderByIdRequest request = new QueryOrderByIdRequest();
request.setTransactionId(transactionId);
return wxJsapiService.queryOrderById(request);
}catch (Exception e) {
logError("通过交易订单号查询支付订单queryOrderById", e);
}
return null;
}
@Override
public Transaction queryOrderByOutTradeNo(String outTradeNo) {
try {
QueryOrderByOutTradeNoRequest request = new QueryOrderByOutTradeNoRequest();
request.setOutTradeNo(outTradeNo);
return wxJsapiService.queryOrderByOutTradeNo(request);
}catch (Exception e) {
logError("通过商户(骑行)订单号查询支付订单queryOrderByOutTradeNo", e);
return null;
}
}
@Override
public String refund(String transactionId, String outTradeNo, String reason, AmountRefundDto amount) {
try {
// 雪花算法生成退款单号
String outRefundNo = StringUtil.generateSnowflakeId("refundId");
EbikeRefund ebikeRefund = new EbikeRefund();
ebikeRefund.setCreateTime(LocalDateTime.now());
// 发起退款
CreateRequest request = new CreateRequest();
request.setTransactionId(transactionId);
request.setOutTradeNo(outTradeNo);
request.setOutRefundNo(outRefundNo);
request.setReason(reason);
request.setNotifyUrl(wxPayConfig.getRefundNotifyUrl());
AmountReq amountReq = new AmountReq();
amountReq.setRefund(BigDecimal.valueOf(amount.getRefund() * 100.0).longValue());
amountReq.setTotal(BigDecimal.valueOf(amount.getTotal() * 100.0).longValue());
amountReq.setCurrency(amount.getCurrency());
request.setAmount(amountReq);
Refund result = wxRefundService.create(request);
// 入库
if (result != null) {
ebikeRefund.setRefundId(outRefundNo);
ebikeRefund.setOrderId(outTradeNo);
ebikeRefund.setPaymentId(transactionId);
ebikeRefund.setTotal(amount.getTotal());
ebikeRefund.setReason(reason);
ebikeRefund.setStatus(String.valueOf(result.getStatus().ordinal()));
ebikeRefund.setCurrency(amount.getCurrency());
ebikeRefundService.saveRefundResult(ebikeRefund);
if(Status.SUCCESS.equals(result.getStatus())){
ebikeRefund.setRefund(result.getAmount().getRefund().doubleValue() / 100.0);
ebikeRefund.setRefundTime(LocalDateTime.now());
}
ebikeRefundService.saveRefundResult(ebikeRefund);
return outRefundNo;
}
log.error("退款申请refund失败订单号{}", outTradeNo);
return null;
}catch (Exception e) {
logError("退款申请refund", e);
return null;
}
}
@Override
public Refund queryRefundByOutNo(String outRefundNo) {
try {
QueryByOutRefundNoRequest request = new QueryByOutRefundNoRequest();
request.setOutRefundNo(outRefundNo);
return wxRefundService.queryByOutRefundNo(request);
}catch (Exception e) {
logError("通过商户退款单号查询退款queryByOutRefundNo", e);
return null;
}
}
/**
* 打印日志
*
* @param desc 描述
* @param e 异常
*/
private void logError(String desc, Exception e) {
if (e instanceof HttpException httpException) {
log.error("{} 发送HTTP请求失败, {}", desc, httpException.getHttpRequest());
}
else if (e instanceof ServiceException serviceException) {
log.error("{} 服务返回状态不正常, {}", desc, serviceException.getResponseBody());
}
else if (e instanceof MalformedMessageException malformedMessageException) {
log.error("{} 返回体类型不合法或者解析返回体失败, {}", desc, malformedMessageException.getMessage());
}
else {
log.error("{} 执行异常, {}", desc, e.getMessage());
}
}
}

View File

@ -0,0 +1,79 @@
package com.cdzy.payment.task;
import com.cdzy.payment.config.WxPayConfig;
import com.cdzy.payment.model.entity.EbikePayment;
import com.cdzy.payment.model.entity.EbikeRefund;
import com.cdzy.payment.service.EbikePaymentService;
import com.cdzy.payment.service.EbikeRefundService;
import com.cdzy.payment.service.WxPayService;
import com.wechat.pay.java.service.payments.model.Transaction;
import com.wechat.pay.java.service.refund.model.Refund;
import com.wechat.pay.java.service.refund.model.Status;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* 用户订单支付任务检测支付状态更新订单状态
*
* @author dingchao
* @date 2025/4/25
* @modified by:
*/
@Slf4j
@Component
public class WsPayTask {
@Resource
private WxPayConfig wxPayConfig;
@Resource
private WxPayService wxPayService;
@Resource
private EbikePaymentService ebikePaymentService;
@Resource
private EbikeRefundService ebikeRefundService;
/**
* 从第0秒开始每隔30秒执行1次查询创建超过5分钟并且未支付的订单
*/
@Scheduled(cron = "0/30 * * * * ?")
public void orderConfirm() throws Exception {
log.info("orderConfirm 执行......");
// 1. 查询未支付的订单
List<EbikePayment> ebikePaymentList = ebikePaymentService.getNoPayOrderByDuration(wxPayConfig.getExpireMinute());
// 2. 遍历订单查询支付状态
for (EbikePayment ebikePayment : ebikePaymentList) {
log.warn("超时未支付的订单号 ===> {}", ebikePayment.getOrderId());
// 调用微信支付查询接口查询支付状态
Transaction transaction = wxPayService.queryOrderByOutTradeNo(ebikePayment.getOrderId());
// 3. 更新订单状态
if (transaction != null)
ebikePaymentService.updatePaymentStatus(ebikePayment.getRecordId(), transaction);
}
}
/**
* 从第0秒开始每隔30秒执行1次查询创建超过5分钟并且未成功的退款单
*/
@Scheduled(cron = "0/30 * * * * ?")
public void refundConfirm() throws Exception {
log.info("refundConfirm 执行......");
// 1. 查询未成功的退款单
List<EbikeRefund> ebikeRefundList = ebikeRefundService.getNoSuccessRefundOrderByDuration(wxPayConfig.getExpireMinute());
// 2. 遍历退款单查询退款状态
for (EbikeRefund ebikeRefund : ebikeRefundList) {
log.warn("超时未退款的退款单号 ===> {}", ebikeRefund.getRefundId());
// 调用微信退款查询接口查询退款状态
Refund refund = wxPayService.queryRefundByOutNo(ebikeRefund.getRefundId());
if (refund!= null&& Status.SUCCESS.equals(refund.getStatus())){
// 3. 更新退款单状态
//ebikeRefundService.updateRefundStatus(ebikeRefund.getRefundId(), 2);
}
}
}
}

View File

@ -0,0 +1,46 @@
package com.cdzy.payment.utils;
import com.mybatisflex.core.keygen.IKeyGenerator;
import com.mybatisflex.core.keygen.KeyGeneratorFactory;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
/**
* 字符串处理工具类
*
* @author dingchao
* @date 2025/4/27
* @modified by:
*/
public class StringUtil {
/**
* 格式化时间
* 格式yyyy-MM-dd'T'HH:mm:ss.SSSX
* 示例2025-04-25T10:10:10.100+08:00
* 注意时区为+8
*
* @param localDateTime 本地时间
* @return
*/
public static String formatLocalDatetime(LocalDateTime localDateTime) {
ZonedDateTime localZoned = localDateTime.atZone(ZoneId.of("+8"));
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssXXX");
return localZoned.format(formatter);
}
public static String generateSnowflakeId(String idFieldName) {
// 获取名为 "snowFlakeId" 的生成器
IKeyGenerator generator = KeyGeneratorFactory.getKeyGenerator("snowFlakeId");
Long key = (Long)generator.generate(null, idFieldName);
return String.valueOf(key);
}
//public static void main(String[] args) {
// System.out.println(formatLocalDatetime(LocalDateTime.now()));
// System.out.println(generateSnowflakeId("refundId"));
//}
}

View File

@ -0,0 +1,43 @@
server:
port: 10017
spring:
application:
name: ebike-pay
cloud:
nacos:
server-addr: 127.0.0.1:8848 # nacos
username: nacos
password: nacos
jackson:
serialization:
write-dates-as-timestamps: false
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
datasource:
url: jdbc:mysql://192.168.2.226:3306/ebike_orders?serverTimezone=Asia/Shanghai&useSSL=false&characterEncoding=utf8
username: root
password: 970529
hikari:
## 最小空闲连接数量
minimum-idle: 5
## 空闲连接存活最大时间默认60000010分钟
idle-timeout: 180000
## 连接池最大连接数默认是10
maximum-pool-size: 10
## 数据库连接超时时间,默认30秒即30000
connection-timeout: 30000
connection-test-query: SELECT 1
##此属性控制池中连接的最长生命周期值0表示无限生命周期默认1800000即30分钟
max-lifetime: 1800000
mybatis-flex:
mapper-locations: classpath:mapper/*.xml
payment:
wx-pay:
app-id: wx097b305458b0757c
merchant-id: 1668000369
private-key-path: D:\Projects\eBIKE\docs\wechatpay\apiclient_key.pem
merchant-serial-number: 5157F09EFDC096DE15EBE81A47057A72********
api-v3-key: 5157F09EFDC096DE15EBE81A47057A72********
pay-notify_url: http://192.168.2.156:10017/wxPayment/pay-notify
refund-notify_url: http://192.168.2.156:10017/wxPayment/refund-notify
expire-minutes: 5

View File

@ -0,0 +1,10 @@
server:
port: 10017
spring:
application:
name: ebike-pay
cloud:
nacos:
server-addr: 192.168.2.226:8848 # nacos
username: nacos
password: nacos

View File

@ -0,0 +1,39 @@
server:
port: 10017
spring:
application:
name: ebike-pay
cloud:
nacos:
server-addr: 192.168.2.226:8848 # nacos
username: nacos
password: nacos
jackson:
serialization:
write-dates-as-timestamps: false
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
datasource:
url: jdbc:mysql://192.168.2.226:3306/ebike_user?useSSL=false
username: root
password: 970529
hikari:
## 最小空闲连接数量
minimum-idle: 5
## 空闲连接存活最大时间默认60000010分钟
idle-timeout: 180000
## 连接池最大连接数默认是10
maximum-pool-size: 10
## 数据库连接超时时间,默认30秒即30000
connection-timeout: 30000
connection-test-query: SELECT 1
##此属性控制池中连接的最长生命周期值0表示无限生命周期默认1800000即30分钟
max-lifetime: 1800000
sql:
init:
platform: mysql
mode: always
schema-locations: classpath:db/init.sql
data-locations: classpath:db/data.sql
mybatis-flex:
mapper-locations: classpath:mapper/*.xml

View File

@ -0,0 +1,3 @@
spring:
profiles:
active: @profile.active@

View File

@ -0,0 +1,98 @@
package com.cdzy.payment;
import com.mybatisflex.codegen.Generator;
import com.mybatisflex.codegen.config.GlobalConfig;
import com.zaxxer.hikari.HikariDataSource;
import org.junit.jupiter.api.Test;
/**
* 功能描述
*
* @author dingchao
* @date 2025/4/24
* @modified by:
*/
public class EbikePaymentApplicationTest {
private static final String model_path ="D:/Projects/eBIKE/mybatis-flex/ebike-pay";
private static final String mapperPath="D:/Projects/eBIKE/mybatis-flex/ebike-pay/resources/mapper";
private static final String packageName ="com.cdzy.payment";
private static final String[] tables= new String[]{
"ebike_payment",
"ebike_refund",
"ebike_order_details"
};
@Test
public void pay_mybatis_code() {
//配置数据源
HikariDataSource dataSource = new HikariDataSource();
dataSource.setJdbcUrl("jdbc:mysql://192.168.2.226:3306/ebike_orders?characterEncoding=utf-8");
dataSource.setUsername("root");
dataSource.setPassword("970529");
//生成全库的
GlobalConfig globalConfig = createGlobalConfigUseStyle2();
//单表的
//GlobalConfig globalConfig = createGlobalConfigUseStyle2();
Generator generator = new Generator(dataSource, globalConfig);
//生成代码
generator.generate();
}
private GlobalConfig createGlobalConfigUseStyle1() {
// 创建配置内容
GlobalConfig globalConfig = new GlobalConfig();
// 设置项目源目录和基础包
globalConfig.getPackageConfig()
.setSourceDir(model_path)
.setBasePackage(packageName);
// 启用生成 entity并启用 Lombok
globalConfig.setEntityGenerateEnable(true);
globalConfig.setEntityWithLombok(true);
// 设置项目的JDK版本
globalConfig.setEntityJdkVersion(17);
// 启用生成 mapperservicecontroller
globalConfig.enableEntity();
globalConfig.enableMapper();
globalConfig.enableService();
globalConfig.enableServiceImpl();
globalConfig.enableController();
globalConfig.enableMapperXml();
globalConfig.setMapperXmlPath(mapperPath);
// 配置 Mapper XML 生成路径和文件名
globalConfig.getMapperXmlConfig()
.setFilePrefix("") // 设置合适的前缀
.setFileSuffix("Mapper"); // 确保设置正确的后缀名
return globalConfig;
}
private GlobalConfig createGlobalConfigUseStyle2() {
// 创建配置内容
GlobalConfig globalConfig = new GlobalConfig();
// 设置项目源目录和基础包
globalConfig.getPackageConfig()
.setSourceDir(model_path)
.setBasePackage(packageName);
// 启用生成 entity并启用 Lombok
globalConfig.setEntityGenerateEnable(true);
globalConfig.setEntityWithLombok(true);
// 设置项目的JDK版本
globalConfig.setEntityJdkVersion(17);
// 启用生成 mapperservicecontroller
globalConfig.enableEntity();
globalConfig.enableMapper();
globalConfig.enableService();
globalConfig.enableServiceImpl();
globalConfig.enableController();
globalConfig.enableMapperXml();
globalConfig.setMapperXmlPath(mapperPath);
// 配置 Mapper XML 生成路径和文件名
globalConfig.getMapperXmlConfig()
.setFilePrefix("") // 设置合适的前缀
.setFileSuffix("Mapper"); // 确保设置正确的后缀名
//设置表前缀和只生成哪些表
// globalConfig.setTablePrefix("tb_");
globalConfig.setGenerateTable(tables);
// 返回配置
return globalConfig;
}
}

View File

@ -21,6 +21,7 @@
<module>ebike-operate</module>
<module>ebike-orders</module>
<module>ebike-report</module>
<module>ebike-payment</module>
</modules>
<properties>
@ -209,6 +210,13 @@
<scope>import</scope>
<type>pom</type>
</dependency>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-launcher</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</dependencyManagement>