fix: yuzhou支付重新对接

This commit is contained in:
old burden 2026-01-22 15:10:47 +08:00
parent 95db7f9e30
commit 6701b8f765
9 changed files with 251 additions and 86 deletions

View File

@ -31,8 +31,8 @@ public class PayController {
*/
@GetMapping("/jinsha-pay")
@ApiOperation("jinsha请求支付")
public AjaxResult jinShaPay(Long gearId, String cardno, String cardname) throws Exception {
PayResVO payResVO = jinShaService.jinShaPay(gearId, cardno, cardname);
public AjaxResult jinShaPay(Long gearId) throws Exception {
PayResVO payResVO = jinShaService.jinShaPay(gearId);
return AjaxResult.success(payResVO);
}

View File

@ -71,6 +71,12 @@
<artifactId>commons-io</artifactId>
</dependency>
<!-- Apache Commons Codec for Base64 encoding/decoding -->
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
</dependency>
<!-- excel工具 -->
<dependency>
<groupId>org.apache.poi</groupId>

View File

@ -1,14 +1,19 @@
package com.ruoyi.common.utils.sign;
import cn.hutool.json.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.ArrayUtils;
import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.io.UnsupportedEncodingException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.ArrayList;
@ -16,25 +21,29 @@ import java.util.Collections;
import java.util.List;
import java.util.Map;
@Slf4j
public class RSAUtils {
// RSA最加密明
// RSA最大加密明文大小
private static final int MAX_ENCRYPT_BLOCK = 117;
// 不仅可以使DSA算法同样也可以使RSA算法做数字签名
// 不仅可以使用DSA算法同样也可以使用RSA算法做数字签名
private static final String KEY_ALGORITHM = "RSA";
public static String encryptByPrivateKey(String str, String privateKey) throws Exception {
public static String encryptByPrivateKey(String str, String privateKey)
throws InvalidKeySpecException, NoSuchAlgorithmException, javax.crypto.NoSuchPaddingException, java.security.InvalidKeyException,
javax.crypto.IllegalBlockSizeException, javax.crypto.BadPaddingException, UnsupportedEncodingException {
// base64编码的公钥
byte[] keyBytes = decryptBASE64(privateKey);
RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance(KEY_ALGORITHM).generatePrivate(new PKCS8EncodedKeySpec(keyBytes));
RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance(KEY_ALGORITHM)
.generatePrivate(new PKCS8EncodedKeySpec(keyBytes));
// RSA加密
Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, priKey);
byte[] data = str.getBytes("UTF-8");
// 加密时超过117字节就报错为此采分段加密的办法来加密
// 加密时超过117字节就报错为此采分段加密的办法来加密
byte[] enBytes = null;
for (int i = 0; i < data.length; i += MAX_ENCRYPT_BLOCK) {
// 注意要使2的倍数否则会出现加密后的内容再解密时为乱码
// 注意要使2的倍数否则会出现加密后的内容再解密时为乱码
byte[] doFinal = cipher.doFinal(ArrayUtils.subarray(data, i, i + MAX_ENCRYPT_BLOCK));
enBytes = ArrayUtils.addAll(enBytes, doFinal);
}
@ -43,15 +52,22 @@ public class RSAUtils {
}
private static String encryptBASE64(byte[] data) {
return new String(Base64.encode(data));
return new String(Base64.encodeBase64(data));
}
private static byte[] decryptBASE64(String data) {
return Base64.decode(data);
return Base64.decodeBase64(data);
}
/**
* Verify signature
*
* @param params
* @return
*/
public static boolean verifySign(JSONObject params, String publickey) {
String platSign = params.getStr("signature"); // sign
log.info("signature:" + platSign);
List<String> paramNameList = new ArrayList<>();
for (String key : params.keySet()) {
if (!"signature".equals(key)) {
@ -64,13 +80,14 @@ public class RSAUtils {
String name = paramNameList.get(i);
stringBuilder.append(params.getStr(name));
}
log.info("keys:" + stringBuilder);
String decryptSign = "";
try {
decryptSign = publicDecrypt(platSign, getPublicKey(publickey)
);
decryptSign = publicDecrypt(platSign, getPublicKey(publickey));
} catch (Exception e) {
System.out.println(e.toString());
log.error("Error decrypting signature", e);
}
log.info("decryptSign:" + decryptSign);
if (!stringBuilder.toString().equalsIgnoreCase(decryptSign)) {
return false;
}
@ -89,11 +106,12 @@ public class RSAUtils {
stringBuilder.append(createMap.get(key)); // 拼接参数
}
String keyStr = stringBuilder.toString(); // 得到待加密的字符串
log.info("keyStr:" + keyStr);
String signedStr = "";
try {
signedStr = privateEncrypt(keyStr, getPrivateKey(MCH_PRIVATE_KEY)); // 私钥加密
} catch (Exception e) {
System.out.println(e.toString());
log.error("Error encrypting signature", e);
}
return signedStr;
}
@ -110,47 +128,70 @@ public class RSAUtils {
stringBuilder.append(createMap.get(key)); // 拼接参数
}
String keyStr = stringBuilder.toString(); // 得到待加密的字符串
log.info("keyStr:" + keyStr);
String signedStr = "";
try {
signedStr = privateEncrypt(keyStr, getPrivateKey(MCH_PRIVATE_KEY)); // 私钥加密
} catch (Exception e) {
System.out.println(e.toString());
log.error("Error encrypting signature", e);
}
return signedStr;
}
/**
* private key encryption
*
* @param data
* @param privateKey
* @return
*/
public static String privateEncrypt(String data, RSAPrivateKey privateKey) {
try {
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
return Base64.encode(rsaSplitCodec(cipher, Cipher.ENCRYPT_MODE, data.getBytes("UTF-8"), privateKey.getModulus().bitLength()));
return Base64.encodeBase64String(rsaSplitCodec(cipher, Cipher.ENCRYPT_MODE, data.getBytes("UTF-8"), privateKey.getModulus().bitLength()));
} catch (Exception e) {
throw new RuntimeException("Exception encountered while encry pting string [" + data + "]", e);
throw new RuntimeException("Exception encountered while encrypting string [" + data + "]", e);
}
}
/**
* public key decryption
*/
public static String publicDecrypt(String data, RSAPublicKey publicKey) {
try {
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, publicKey);
return new String(rsaSplitCodec(cipher, Cipher.DECRYPT_MODE, Base64.decode(data), publicKey.getModulus().bitLength()), "UTF-8");
return new String(rsaSplitCodec(cipher, Cipher.DECRYPT_MODE, Base64.decodeBase64(data), publicKey.getModulus().bitLength()), "UTF-8");
} catch (Exception e) {
throw new RuntimeException("An exception was encountered whil e decrypting the string[" + data + "]", e);
throw new RuntimeException("An exception was encountered while decrypting the string[" + data + "]", e);
}
}
public static RSAPrivateKey getPrivateKey(String privateKey) throws Exception {
/**
* get private key
*
* @param privateKey key string (base64 encoded)
* @throws Exception
*/
public static RSAPrivateKey getPrivateKey(String privateKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
//Get the private key object through the PKCS#8 encoded Key instruction
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(Base64.decode(privateKey));
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKey));
RSAPrivateKey key = (RSAPrivateKey) keyFactory.generatePrivate(pkcs8KeySpec);
return key;
}
public static RSAPublicKey getPublicKey(String publicKey) throws Exception {
/**
* get the public key
*
* @param publicKey key string (base64 encoded)
* @throws Exception
*/
public static RSAPublicKey getPublicKey(String publicKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
//Get the public key object through the X509 encoded Key instruction
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(Base64.decode(publicKey));
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(Base64.decodeBase64(publicKey));
RSAPublicKey key = (RSAPublicKey) keyFactory.generatePublic(x509KeySpec);
return key;
}
@ -179,7 +220,7 @@ public class RSAUtils {
offSet = i * maxBlock;
}
} catch (Exception e) {
throw new RuntimeException("An exception occurred when encryp ting and decrypting data whose threshold is [" + maxBlock + "]", e);
throw new RuntimeException("An exception occurred when encrypting and decrypting data whose threshold is [" + maxBlock + "]", e);
}
byte[] resultDatas = out.toByteArray();
IOUtils.closeQuietly(out);

View File

@ -43,6 +43,10 @@ public class AiTemplate extends BaseEntity {
@Excel(name = "模版图片URL")
private String imageUrl;
/** AI类型 */
@Excel(name = "AI类型")
private Long aiId;
/** 状态0-禁用1-启用 */
@Excel(name = "状态0-禁用1-启用")
private Integer status;

View File

@ -9,10 +9,8 @@ public interface IJinShaService {
/**
* 支付
* @param gearId 档位ID
* @param cardno 银行卡号
* @param cardname 银行卡姓名
*/
PayResVO jinShaPay(Long gearId, String cardno, String cardname) throws Exception;
PayResVO jinShaPay(Long gearId) throws Exception;
/**

View File

@ -53,15 +53,7 @@ public class JinShaService implements IJinShaService {
private IExchangeRateService exchangeRateService;
@Override
public PayResVO jinShaPay(Long gearId, String cardno, String cardname) throws Exception {
// 验证参数
if (cardno == null || cardno.trim().isEmpty()) {
throw new ServiceException("银行卡号不能为空", -1);
}
if (cardname == null || cardname.trim().isEmpty()) {
throw new ServiceException("银行卡姓名不能为空", -1);
}
public PayResVO jinShaPay(Long gearId) throws Exception {
//充值挡位查询
AiRechargeGiftGear aiRechargeGiftGear = aiRechargeGiftGearService.selectAiRechargeGiftGearById(gearId);
@ -77,16 +69,20 @@ public class JinShaService implements IJinShaService {
String dateTime = new SimpleDateFormat("yyyyMMdd").format(new Date());
String orderNo = dateTime + "js" + uuid;
// 金额转换为int类型去除小数部分
int amountInt = amount.intValue();
// 金额转换为number类型保留两位小数
String amountStr = String.format("%.2f", amount.doubleValue());
// 1. 配置签名参数按照key排序不包含notify_url和sign
// 构建回调地址和返回地址
String notifyUrlFull = notifyUrl + "/api/pay/jinsha-callBack";
String returnUrlFull = returnUrl + "/recharge";
// 1. 配置签名参数按照key排序appid, amount, orderno, notify_url, return_url不包含sign
TreeMap<String, Object> signParams = new TreeMap<>();
signParams.put("appid", appId);
signParams.put("amount", amountInt);
signParams.put("amount", amountStr);
signParams.put("orderno", orderNo);
signParams.put("cardno", cardno);
signParams.put("cardname", cardname);
signParams.put("notify_url", notifyUrlFull);
signParams.put("return_url", returnUrlFull);
// 2. 生成签名
String sign = this.generateSign(signParams);
@ -94,20 +90,21 @@ public class JinShaService implements IJinShaService {
// 3. 构建请求参数使用FormBody.Builder自动处理URL编码
FormBody.Builder formBuilder = new FormBody.Builder();
formBuilder.add("appid", appId);
formBuilder.add("amount", String.valueOf(amountInt));
formBuilder.add("amount", amountStr);
formBuilder.add("orderno", orderNo);
formBuilder.add("cardno", cardno);
formBuilder.add("cardname", cardname);
formBuilder.add("notify_url", notifyUrl + "/api/pay/jinsha-callBack");
formBuilder.add("notify_url", notifyUrlFull);
formBuilder.add("return_url", returnUrlFull);
formBuilder.add("sign", sign);
RequestBody requestBody = formBuilder.build();
// 5. 构建 POST 请求
// 4. 构建 POST 请求
Request request = new Request.Builder()
.url(url + "/api/pay")
.header("Content-Type", "application/x-www-form-urlencoded;charset=utf-8")
.post(requestBody)
.build();
// 6. 发送请求并获取响应
// 5. 发送请求并获取响应
Response response = OkHttpUtils.newCall(request).execute();
ResponseBody body = response.body();
if (null == body) {
@ -126,6 +123,16 @@ public class JinShaService implements IJinShaService {
throw new ServiceException(msg != null ? msg : "支付请求失败", code != null ? code : -1);
}
// 检查返回的payurl
String payUrl = null;
if (jinShaBodyRes.getData() != null) {
payUrl = jinShaBodyRes.getData().getPayurl();
}
if (payUrl == null || payUrl.trim().isEmpty()) {
log.error("jinsha支付返回的payurl为空订单号: {}", orderNo);
throw new ServiceException("支付返回的支付链接为空", -1);
}
// 创建充值管理
AiUser userInfo = aiUserService.getUserInfo(SecurityUtils.getAiUserId());
AiRecharge aiRecharge = new AiRecharge();
@ -137,12 +144,12 @@ public class JinShaService implements IJinShaService {
aiRecharge.setSource(userInfo.getSource());
AiRechargeGift aiRechargeGift = aiRechargeGiftService.selectAiRechargeGiftById(aiRechargeGiftGear.getRechargeId());
aiRecharge.setPayType(aiRechargeGift.getPayType());
// jinsha支付成功返回后不需要支付URL订单状态由回调更新
aiRecharge.setPayUrl(payUrl);
aiRechargeService.insertAiRecharge(aiRecharge);
PayResVO payResVO = new PayResVO();
payResVO.setOrderNo(orderNo);
// jinsha支付成功后不需要跳转支付页面直接返回订单号
payResVO.setPayUrl(payUrl);
return payResVO;
}

View File

@ -60,37 +60,111 @@ public class YuZhouService implements IYuZhouService {
String dateTime = new SimpleDateFormat("yyyyMMdd").format(new Date());
String orderNo = dateTime + "yz" + uuid;
// 获取用户信息
AiUser userInfo = aiUserService.getUserInfo(SecurityUtils.getAiUserId());
// 处理用户姓名如果没有则使用默认值
String firstName = "John";
String lastName = "Doe";
if (userInfo.getNickname() != null && !userInfo.getNickname().trim().isEmpty()) {
String[] nameParts = userInfo.getNickname().trim().split("\\s+");
if (nameParts.length >= 2) {
firstName = nameParts[0];
lastName = nameParts[nameParts.length - 1];
} else if (nameParts.length == 1) {
firstName = nameParts[0];
}
}
// 从用户表获取邮箱
String email = userInfo.getEmail();
if (email == null || email.trim().isEmpty()) {
log.warn("用户邮箱为空 userId: {}, nickname: {}", userInfo.getId(), userInfo.getNickname());
// 如果用户邮箱为空使用默认邮箱格式
email = "user" + userInfo.getId() + "@example.com";
} else {
email = email.trim();
}
// 处理电话如果没有则使用默认值
String phone = userInfo.getPhone();
if (phone == null || phone.trim().isEmpty()) {
phone = "1234567890";
} else {
// 确保电话是10位数字
phone = phone.replaceAll("[^0-9]", "");
if (phone.length() < 10) {
// 如果不足10位用0补齐
phone = String.format("%010d", phone.length() > 0 ? Long.parseLong(phone) : 0);
} else if (phone.length() > 10) {
// 如果超过10位取后10位
phone = phone.substring(phone.length() - 10);
}
}
// 构建请求参数
Map<String, Object> createMap = new HashMap<>();
createMap.put("merchantNo", appId);
createMap.put("orderNo", orderNo);
createMap.put("money", amount.toString());
createMap.put("description", "test");
createMap.put("name", "test");
createMap.put("email", "test@gmail.com");
createMap.put("callbackUrl", callbackUrl + "/api/pay/yuzhou-callBack");
createMap.put("phone", "7383442114");
createMap.put("description", "Recharge");
createMap.put("email", email);
createMap.put("phone", phone);
createMap.put("notifyUrl", callbackUrl + "/api/pay/yuzhou-callBack");
createMap.put("expiredPeriod", "1440");
createMap.put("redirectUrl", redirectUrl + "/recharge");
createMap.put("currency", "USD");
createMap.put("firstName", firstName);
createMap.put("lastName", lastName);
createMap.put("street", "123 Main Street");
createMap.put("city", "New York");
createMap.put("state", "NY");
createMap.put("country", userInfo.getCountry() != null && !userInfo.getCountry().trim().isEmpty() ? userInfo.getCountry() : "US");
createMap.put("postcode", "10001");
// 生成签名注意签名时不包括signature字段
String signedStr = RSAUtils.getSignStr(createMap, secretKey);
createMap.put("signature", signedStr);
String postStr = JSONUtil.toJsonStr(createMap);
String response = HttpRequest.post(url + "/gateway/order/US/payIn").header("Content-Type", "application/json")
.body(postStr).execute().body();
log.info("yuzhou支付请求参数: {}", postStr);
// 调用新的接口地址
String response = HttpRequest.post(url + "/gateway/US/payIn")
.header("Content-Type", "application/json; charset=utf-8")
.body(postStr)
.execute()
.body();
log.info("yuzhou支付响应: {}", response);
JSONObject returnObj = JSONUtil.parseObj(response);
Integer code = returnObj.getInt("code");
if (200 != code) {
log.error("yuzhou支付请求的响应异常 {}", returnObj);
throw new Exception("yuzhoupay responsebody is null");
String msg = returnObj.getStr("msg");
log.error("yuzhou支付请求的响应异常 code: {}, msg: {}, response: {}", code, msg, returnObj);
throw new ServiceException("yuzhoupay request failed: " + (msg != null ? msg : "unknown error"), code);
}
JSONObject data = returnObj.getJSONObject("data");
if (data == null) {
log.error("yuzhou支付响应数据为空: {}", returnObj);
throw new ServiceException("yuzhoupay response data is null", -1);
}
String money = data.getStr("money");
String payUrl = data.getStr("url");
String platformOrderNo = data.getStr("platformOrderNo");
if (payUrl == null || payUrl.trim().isEmpty()) {
log.error("yuzhou支付URL为空: {}", data);
throw new ServiceException("yuzhoupay url is null", -1);
}
// 创建充值管理
AiUser userInfo = aiUserService.getUserInfo(SecurityUtils.getAiUserId());
AiRecharge aiRecharge = new AiRecharge();
aiRecharge.setOrderNum(orderNo);
aiRecharge.setUserId(SecurityUtils.getAiUserId());
aiRecharge.setAmount(new BigDecimal(money));
aiRecharge.setAmount(new BigDecimal(money != null ? money : amount.toString()));
aiRecharge.setGearId(gearId);
aiRecharge.setSource(userInfo.getSource());
aiRecharge.setGearAmount(aiRechargeGiftGear.getRechargeAmount());
@ -98,6 +172,7 @@ public class YuZhouService implements IYuZhouService {
aiRecharge.setPayType(aiRechargeGift.getPayType());
aiRecharge.setPayUrl(payUrl);
aiRechargeService.insertAiRecharge(aiRecharge);
PayResVO payResVO = new PayResVO();
payResVO.setOrderNo(orderNo);
payResVO.setPayUrl(payUrl);
@ -106,31 +181,60 @@ public class YuZhouService implements IYuZhouService {
@Override
public String callBack(Map<String, Object> map) throws Exception {
int code = Integer.parseInt(map.get("status").toString());
String orderNo = map.get("orderNo").toString();
if (10 != code) {
log.error("yuzhou支付失败 {}", orderNo);
return null;
log.info("yuzhou支付回调接收参数: {}", map);
// 验证必要参数
if (map == null || map.isEmpty()) {
log.error("yuzhou支付回调参数为空");
return "FAIL";
}
// Map<String, Object> createMap = new HashMap<>();
// createMap.put("orderNo", orderNo);
// createMap.put("platOrderNo", map.get("platOrderNo"));
// createMap.put("money", map.get("money"));
// createMap.put("fee", map.get("fee"));
// createMap.put("status", map.get("status"));
// createMap.put("message", map.get("message"));
String signature = map.get("signature").toString();
Object statusObj = map.get("status");
Object orderNoObj = map.get("orderNo");
if (statusObj == null || orderNoObj == null) {
log.error("yuzhou支付回调缺少必要参数: status={}, orderNo={}", statusObj, orderNoObj);
return "FAIL";
}
int status = Integer.parseInt(statusObj.toString());
String orderNo = orderNoObj.toString();
log.info("yuzhou支付回调 orderNo: {}, status: {}", orderNo, status);
// 验证签名
Object signatureObj = map.get("signature");
if (signatureObj == null) {
log.error("yuzhou支付回调缺少签名参数 orderNo: {}", orderNo);
return "FAIL";
}
JSONObject entries = JSONUtil.parseObj(map);
boolean b = RSAUtils.verifySign(entries, publicKey);
if (!b) {
log.error("yuzhou支付回调签名错误 {}", orderNo);
log.error("yuzhou支付回调签名 {}", signature);
log.error("yuzhou支付回调报文 {}", map);
return null;
boolean verifyResult = RSAUtils.verifySign(entries, publicKey);
if (!verifyResult) {
log.error("yuzhou支付回调签名验证失败 orderNo: {}", orderNo);
log.error("yuzhou支付回调签名: {}", signatureObj);
log.error("yuzhou支付回调完整报文: {}", map);
return "FAIL";
}
log.info("yuzhou支付回调签名验证成功 orderNo: {}", orderNo);
// 判断订单状态10表示成功
if (10 != status) {
log.warn("yuzhou支付订单状态非成功 orderNo: {}, status: {}", orderNo, status);
return "SUCCESS"; // 即使失败也返回SUCCESS避免重复回调
}
// 充值成功处理
try {
aiRechargeService.addRecharge(orderNo);
log.info("yuzhou支付回调处理成功 orderNo: {}", orderNo);
return "SUCCESS";
} catch (Exception e) {
log.error("yuzhou支付回调处理失败 orderNo: {}", orderNo, e);
return "FAIL";
}
//充值成功处理
aiRechargeService.addRecharge(orderNo);
return "SUCCESS";
}

View File

@ -10,6 +10,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<result property="chineseContent" column="chinese_content" />
<result property="englishContent" column="english_content" />
<result property="imageUrl" column="image_url" />
<result property="aiId" column="ai_id" />
<result property="status" column="status" />
<result property="remark" column="remark" />
<result property="createTime" column="create_time" />
@ -20,7 +21,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</resultMap>
<sql id="selectAiTemplateVo">
select id, name, chinese_content, english_content, image_url, status, remark, create_time, create_by, update_by, update_time, del_flag from ai_template
select id, name, chinese_content, english_content, image_url, ai_id, status, remark, create_time, create_by, update_by, update_time, del_flag from ai_template
</sql>
<select id="selectAiTemplateList" parameterType="AiTemplate" resultMap="AiTemplateResult">
@ -30,6 +31,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="chineseContent != null and chineseContent != ''"> and chinese_content = #{chineseContent}</if>
<if test="englishContent != null and englishContent != ''"> and english_content = #{englishContent}</if>
<if test="imageUrl != null and imageUrl != ''"> and image_url = #{imageUrl}</if>
<if test="aiId != null and aiId != ''"> and ai_id = #{aiId}</if>
<if test="status != null "> and status = #{status}</if>
</where>
</select>
@ -46,6 +48,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="chineseContent != null">chinese_content,</if>
<if test="englishContent != null">english_content,</if>
<if test="imageUrl != null">image_url,</if>
<if test="aiId != null">ai_id,</if>
<if test="status != null">status,</if>
<if test="remark != null">remark,</if>
<if test="createTime != null">create_time,</if>
@ -59,6 +62,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="chineseContent != null">#{chineseContent},</if>
<if test="englishContent != null">#{englishContent},</if>
<if test="imageUrl != null">#{imageUrl},</if>
<if test="aiId != null">#{aiId},</if>
<if test="status != null">#{status},</if>
<if test="remark != null">#{remark},</if>
<if test="createTime != null">#{createTime},</if>
@ -76,6 +80,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="chineseContent != null">chinese_content = #{chineseContent},</if>
<if test="englishContent != null">english_content = #{englishContent},</if>
<if test="imageUrl != null">image_url = #{imageUrl},</if>
<if test="aiId != null">ai_id = #{aiId},</if>
<if test="status != null">status = #{status},</if>
<if test="remark != null">remark = #{remark},</if>
<if test="createTime != null">create_time = #{createTime},</if>

View File

@ -2866,7 +2866,7 @@ CREATE TABLE `ai_recharge` (
`gift_amount` decimal(10, 2) NULL DEFAULT NULL COMMENT '到账金额',
`give_amount` decimal(10, 2) NULL DEFAULT NULL COMMENT '赠送金额',
`pay_type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '支付方式',
`pay_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '支付链接',
`pay_url` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '支付链接',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 118 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '充值记录' ROW_FORMAT = DYNAMIC;