【Java】阿里环球Antom支付对接
阿里环球Antom支付对接
线上文档地址:
GitHub:https://github.com/alipay/global-open-sdk-java
文档:https://global.alipay.com/docs/ac/ams_zh-cn/session_cashier
maven:
<!--阿里国际支付--><dependency><groupId>com.alipay.global.sdk</groupId><artifactId>global-open-sdk-java</artifactId><version>2.0.49</version></dependency>
新增作用在实现类上的注解
import com.ycwl.blindBox.enums.PayTypeEnum;import java.lang.annotation.*;/*** <p>支付方式策略类型</p>** @Description*/
//作用在类上
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
//子类可以继承此注解
@Inherited
public @interface PayType {/*** 支付方式策略类型** @return 策略类型*/PayTypeEnum type();}
编写配置实体:AntomConfig
import lombok.Data;/*** <p>DESC: </p>* <p>VERSION:1.0.0</p>*/
@Data
public class AntomConfig {/*** 支付网关地址*/private String gatewayUrl;/*** 商户私钥*/private String merchantPrivateKey;/*** 支付宝公钥*/private String alipayPublicKey;/*** 客户端 ID*/private String clientId;/*** 系统商户 ID*/private String referenceMerchantId;/*** 支付回调地址*/private String payNotifyUrl;/*** 退款回调地址*/private String refundNotifyUrl;/*** 货币代码*/private String currency;/*** 最小币种单位*/private String currencyValue;}
编写服务类:AlipayService
import com.alipay.global.api.exception.AlipayApiException;
import com.alipay.global.api.request.ams.notify.AlipayPayResultNotify;
import com.alipay.global.api.request.ams.notify.AlipayRefundNotify;
import com.ycwl.blindBox.constant.baseResp.ApiResponse;
import com.ycwl.blindBox.model.common.AntomResultResp;
import com.ycwl.blindBox.model.common.StatusReqDTO;
import com.ycwl.blindBox.model.dto.BagBoxDeliverDTO;
import com.ycwl.blindBox.model.dto.BagBoxExchangeDTO;
import com.ycwl.blindBox.model.dto.BagBoxTrailDTO;
import com.ycwl.blindBox.model.dto.BagBoxTrailGruopGoodsDTO;
import com.ycwl.blindBox.model.dto.giveRecord.GiveAppReqDTO;
import com.ycwl.blindBox.model.vo.*;
import org.springframework.web.bind.annotation.RequestBody;import java.math.BigDecimal;
import java.util.List;
import java.util.Set;/*** @author songmingsong* @description antom支付*/
public interface AlipayService {/*** 创建支付订单** @param paymentRequestId 商户为标识支付请求所分配的专属 ID。Antom 使用此字段进行幂等性控制。* @param orderId 在商户端标识订单的专属 ID,由直接向客户提供服务或商品的商户分配* @param orderDescription 订单的摘要描述。此字段用于显示用户消费记录以及其他后续操作* @param redirectUrl 用户完成支付后跳转到的商户页面链接。* @return* @throws AlipayApiException*/AntomPayResultVO createPayment(String paymentRequestId,String orderId,String orderDescription,String redirectUrl,BigDecimal amount) throws AlipayApiException;/*** 支付结果查询** @param payOrderNo 支付订单号* @return* @throws AlipayApiException*/AntomPayResultVO paymentResultInquiry(String payOrderNo) throws AlipayApiException;/*** 取消支付** @param payOrderNo 支付订单号* @return* @throws AlipayApiException*/AntomPayResultVO paymentCancel(String payOrderNo) throws AlipayApiException;/*** 支付回调** @param alipayPayResultNotify*/AntomResultResp payNotify(AlipayPayResultNotify alipayPayResultNotify) throws AlipayApiException;/*** 退款** @param refundVO*/AntomRefundResultVO refund(AntomRefundVO refundVO) throws AlipayApiException;/*** 退款结果查询** @param payOrderNo 支付订单号* @return* @throws AlipayApiException*/AntomRefundResultVO refundInquiry(String payOrderNo) throws AlipayApiException;/*** 退款回调** @param alipayRefundNotify*/AntomResultResp refundNotify(AlipayRefundNotify alipayRefundNotify);
}
服务实现类:AlipayServiceImpl
这里面包含了一些业务逻辑,不需要的话请删除
package com.ycwl.blindBox.service.impl;import com.alibaba.fastjson2.JSONObject;
import com.alipay.global.api.AlipayClient;
import com.alipay.global.api.DefaultAlipayClient;
import com.alipay.global.api.exception.AlipayApiException;
import com.alipay.global.api.model.Result;
import com.alipay.global.api.model.ResultStatusType;
import com.alipay.global.api.model.ams.*;
import com.alipay.global.api.model.ams.Order;
import com.alipay.global.api.request.ams.notify.AlipayPayResultNotify;
import com.alipay.global.api.request.ams.notify.AlipayRefundNotify;
import com.alipay.global.api.request.ams.pay.*;
import com.alipay.global.api.response.ams.pay.*;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.ycwl.blindBox.annotation.PayType;
import com.ycwl.blindBox.config.ApplicationContextHelper;
import com.ycwl.blindBox.constant.OrderNoTypeConstant;
import com.ycwl.blindBox.constant.SystemSettingTypeConstant;
import com.ycwl.blindBox.enums.*;
import com.ycwl.blindBox.exception.BaseException;
import com.ycwl.blindBox.mapper.*;
import com.ycwl.blindBox.model.common.AntomConfig;
import com.ycwl.blindBox.model.common.AntomResultResp;
import com.ycwl.blindBox.model.dto.OrderParamDTO;
import com.ycwl.blindBox.model.dto.vipCard.VipCardAwardSetDTO;
import com.ycwl.blindBox.model.entity.*;
import com.ycwl.blindBox.model.vo.AntomPayResultVO;
import com.ycwl.blindBox.model.vo.AntomRefundResultVO;
import com.ycwl.blindBox.model.vo.AntomRefundVO;
import com.ycwl.blindBox.model.vo.PayParamVO;
import com.ycwl.blindBox.service.*;
import com.ycwl.blindBox.utils.capital.CapitalChangeUtil;
import com.ycwl.blindBox.utils.dateUtils.DateUtils;
import com.ycwl.blindBox.utils.pubfunc.PubfuncUtil;
import com.ycwl.blindBox.utils.snowFlakeUtils.SnowFlakeUtil;
import com.ycwl.blindBox.utils.userLevelUtils.UserLevelUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;import static com.ycwl.blindBox.constant.SystemSettingTypeConstant.ANTOM;
import static com.ycwl.blindBox.enums.BizCodeEnum.*;
import static com.ycwl.blindBox.enums.SysSettingEnum.CZ_INTEGRAL;
import static com.ycwl.blindBox.enums.SysSettingEnum.GZ_INTEGRAL;/*** @Author: songmingsong* @CreateTime: 2024-12-25* @Description: 阿里Antom全球支付实现* @Version: 1.0*/
@PayType(type = PayTypeEnum.ONLINE)
@Component("AlipayServiceImpl")
@Slf4j
public class AlipayServiceImpl implements PayProcessingService, AlipayService {@Autowiredprivate RefundRecordLogMapper refundRecordLogMapper;@Autowiredprivate OrderMapper orderMapper;@Autowiredprivate OrderExpressMapper orderExpressMapper;@Autowiredprivate UserVipCardOrderMapper userVipCardOrderMapper;@Autowiredprivate BlindBoxOrderServiceImpl blindBoxOrderService;@Autowiredprivate UserVipCardMapper userVipCardMapper;@Autowiredprivate VipCardAwardSetMapper awardSetMapper;@Autowiredprivate VipCardAwardMapper awardMapper;@Autowiredprivate UserCardAwardReceiveRecordMapper userCardAwardReceiveRecordMapper;@Autowiredprivate VipCardMapper vipCardMapper;@Autowiredprivate UserBalanceRechargeLogMapper userBalanceRechargeLogMapper;@Autowiredprivate OrderService orderService;@Autowiredprivate CapitalChangeUtil capitalChangeUtil;@Autowiredprivate UserBalanceChangeLogMapper userBalanceChangeLogMapper;@Autowiredprivate UserLevelUtil userLevelUtil;@Autowiredprivate RechargeSettingMapper rechargeSettingMapper;@Autowiredprivate BagServiceImpl bagService;@Autowiredprivate ShopOrderServiceImpl shopOrderService;@Autowiredprivate InviteNewRecordService inviteNewRecordService;private final Map<String, Lock> lockMap = new ConcurrentHashMap<>();private AntomConfig antomConfig = new AntomConfig();private String GATEWAY_URL;private String MERCHANT_PRIVATE_KEY;private String ALIPAY_PUBLIC_KEY;private String CLIENT_ID;private String REFERENCE_MERCHANT_ID; // 用于标识直接向客户提供服务或商品的商户 ID。private String PAY_NOTIFY_URL; // 支付回调地址private String REFUND_NOTIFY_URL; // 退款回调地址// 查询地址:https://www.iban.hk/currency-codesprivate String CURRENCY_VND = "VND"; // 默认越南盾代码// 查询地址:https://docs.antom.com/ac/ref_zh-cn/cc#ONkIeprivate BigDecimal VALUE_VND = BigDecimal.ONE; // 默认越南盾最小币种单位private final AlipayClient client;public AlipayServiceImpl() {getAntomConfig();this.GATEWAY_URL = antomConfig.getGatewayUrl();this.MERCHANT_PRIVATE_KEY = antomConfig.getMerchantPrivateKey();this.ALIPAY_PUBLIC_KEY = antomConfig.getAlipayPublicKey();this.CLIENT_ID = antomConfig.getClientId();this.REFERENCE_MERCHANT_ID = antomConfig.getReferenceMerchantId();this.PAY_NOTIFY_URL = antomConfig.getPayNotifyUrl();this.REFUND_NOTIFY_URL = antomConfig.getRefundNotifyUrl();this.CURRENCY_VND = antomConfig.getCurrency();this.VALUE_VND = BigDecimal.valueOf(Integer.parseInt(antomConfig.getCurrencyValue()));this.client = new DefaultAlipayClient(GATEWAY_URL, MERCHANT_PRIVATE_KEY, ALIPAY_PUBLIC_KEY, CLIENT_ID);}public AntomConfig getAntomConfig() {List<SysSetting> data = PubfuncUtil.getSdParam(ANTOM);data.forEach(setting -> {String key = setting.getKey();String value = setting.getValue();// 根据枚举匹配对应的方法调用AntomConfigKeyEnum.fromKey(key).ifPresent(action -> action.apply(antomConfig, value));});return antomConfig;}@Overridepublic JSONObject pay(PayParamVO payParamVO) throws AlipayApiException {orderService.checkStatus(payParamVO.getOrderNo());String payOrderNo = payParamVO.getPayOrderNo();if (payOrderNo.isEmpty()) {throw new BaseException(PAY_NO_EMPTY);}JSONObject rtnJson = new JSONObject();AntomPayResultVO payment = createPayment(payOrderNo, payParamVO.getOrderNo(), payParamVO.getNotes(), payParamVO.getRedirectUrl(), payParamVO.getPayPrice());log.info("支付结果:{}", payment);// 更新第三方单号String paymentId = payment.getPaymentId();String orderNo = payParamVO.getOrderNo();Boolean success = payment.getSuccess();if (payment.getSuccess()) {// 支付收银台创建成功,返回链接rtnJson.put("url", payment.getNormalUrl());} else {throw new BaseException(PAYMENT_FAILED);}return rtnJson;}@Overridepublic JSONObject refund(RefundRecordLog refundRecordLog) throws AlipayApiException {AntomRefundVO antomRefundVO = new AntomRefundVO();antomRefundVO.setRefundRequestId(refundRecordLog.getOrderNo()); // 订单号antomRefundVO.setReferenceRefundId(refundRecordLog.getOrderId().toString());antomRefundVO.setRefundAmount(refundRecordLog.getRefundAmount());refund(antomRefundVO);return null;}@Overridepublic AntomPayResultVO paymentResultInquiry(String payOrderNo) throws AlipayApiException {AlipayPayQueryRequest alipayPayQueryRequest = new AlipayPayQueryRequest();alipayPayQueryRequest.setMerchantAccountId(REFERENCE_MERCHANT_ID);alipayPayQueryRequest.setClientId(CLIENT_ID);alipayPayQueryRequest.setPaymentRequestId(payOrderNo);// TODO 沙箱地址
// alipayPayQueryRequest.setPath("/ams/sandbox/api/v1/payments/inquiryPayment");AntomPayResultVO antomPayResultVO = new AntomPayResultVO();AlipayPayQueryResponse execute = client.execute(alipayPayQueryRequest);String orderNo = execute.getPaymentRequestId();String paymentId = execute.getPaymentId();String paymentTime = execute.getPaymentTime(); // 支付时间Result result = execute.getResult();String resultMessage = result.getResultMessage();ResultStatusType resultStatus = result.getResultStatus();if (resultStatus == ResultStatusType.F) {antomPayResultVO.setSuccess(false);antomPayResultVO.setMsg(result.getResultMessage());payUpdateStatus(orderNo, paymentId, resultMessage, parseTime(paymentTime), false);} else if (resultStatus == ResultStatusType.U) {// 需要重试return null;} else {// 成功TransactionStatusType paymentStatus = execute.getPaymentStatus();if (paymentStatus.equals(TransactionStatusType.SUCCESS)) { // 成功antomPayResultVO.setSuccess(true);antomPayResultVO.setTime(parseTime(paymentTime));payUpdateStatus(orderNo, paymentId, resultMessage, parseTime(paymentTime), true);} else if (paymentStatus.equals(TransactionStatusType.FAIL)) { // 失败antomPayResultVO.setSuccess(false);payUpdateStatus(orderNo, paymentId, resultMessage, parseTime(paymentTime), false);}}return antomPayResultVO;}@Overridepublic AntomPayResultVO paymentCancel(String payOrderNo) throws AlipayApiException {AlipayPayCancelRequest alipayPayCancelRequest = new AlipayPayCancelRequest();alipayPayCancelRequest.setMerchantAccountId(REFERENCE_MERCHANT_ID);alipayPayCancelRequest.setClientId(CLIENT_ID);alipayPayCancelRequest.setPaymentRequestId(payOrderNo);AntomPayResultVO antomPayResultVO = new AntomPayResultVO();AlipayPayCancelResponse execute = client.execute(alipayPayCancelRequest);Result result = execute.getResult();ResultStatusType resultStatus = result.getResultStatus();if (resultStatus == ResultStatusType.F) {antomPayResultVO.setSuccess(false);antomPayResultVO.setMsg(result.getResultMessage());} else if (resultStatus == ResultStatusType.U) {// 需要重试return this.paymentCancel(payOrderNo);} else {// 成功String paymentTime = execute.getCancelTime();antomPayResultVO.setSuccess(true);antomPayResultVO.setTime(parseTime(paymentTime));}return antomPayResultVO;}@Overridepublic AntomResultResp payNotify(AlipayPayResultNotify alipayPayResultNotify) throws AlipayApiException {/** PAYMENT_RESULT: 表示通知是关于支付结果的。* PAYMENT_PENDING: 表示用户已完成支付。商家需要等待最终的支付结果* */String notifyType = alipayPayResultNotify.getNotifyType();Result result = alipayPayResultNotify.getResult();String orderNo = alipayPayResultNotify.getPaymentRequestId(); // 订单号String paymentId = alipayPayResultNotify.getPaymentId();String paymentTime = alipayPayResultNotify.getPaymentTime(); // 支付时间/** S: 当 notifyType 为PAYMENT_RESULT时,表示支付成功;当 notifyType 为PAYMENT_PENDING时,表示支付处理中。* F: 表示支付失败。* */ResultStatusType resultStatus = result.getResultStatus();String resultMessage = result.getResultMessage();if (resultStatus == ResultStatusType.S && "PAYMENT_RESULT".equals(notifyType)) {payUpdateStatus(orderNo, paymentId, resultMessage, parseTime(paymentTime), true);} else if (resultStatus == ResultStatusType.S && "PAYMENT_PENDING".equals(notifyType)) {// 支付处理中,单开线程查询结果new Thread(() -> {ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);AtomicInteger attemptCounter = new AtomicInteger(0);int maxAttempts = 20; // 最多执行 20 次long delayInSeconds = 10; // 每隔 10 秒执行一次scheduler.scheduleAtFixedRate(() -> {try {int currentAttempt = attemptCounter.incrementAndGet();if (currentAttempt > maxAttempts) {scheduler.shutdown(); // 超过最大次数,停止任务return;}AntomPayResultVO antomPayResultVO = paymentResultInquiry(orderNo);if (!Objects.isNull(antomPayResultVO)) {Boolean success = antomPayResultVO.getSuccess();payUpdateStatus(orderNo, paymentId, resultMessage, parseTime(paymentTime), success);scheduler.shutdown(); // 停止任务}} catch (AlipayApiException e) {scheduler.shutdown(); // 出现异常时停止任务throw new RuntimeException(e);}}, 0, delayInSeconds, TimeUnit.SECONDS);}).start();} else if (resultStatus == ResultStatusType.F) {payUpdateStatus(orderNo, paymentId, resultMessage, parseTime(paymentTime), false);}return new AntomResultResp();}@Overridepublic AntomRefundResultVO refund(AntomRefundVO refundVO) throws AlipayApiException {AlipayRefundRequest alipayRefundRequest = new AlipayRefundRequest();alipayRefundRequest.setClientId(CLIENT_ID);Amount refundAmount = new Amount();refundAmount.setCurrency(CURRENCY_VND);refundAmount.setValue(refundVO.getRefundAmount().multiply(VALUE_VND).toString());alipayRefundRequest.setRefundAmount(refundAmount);alipayRefundRequest.setRefundRequestId(refundVO.getRefundRequestId());alipayRefundRequest.setReferenceRefundId(refundVO.getReferenceRefundId());alipayRefundRequest.setRefundNotifyUrl(REFUND_NOTIFY_URL); // 用于接收支付结果通知的链接。// TODO 沙箱地址
// alipayRefundRequest.setPath("/ams/sandbox/api/v1/payments/refund");AlipayRefundResponse execute = client.execute(alipayRefundRequest);Result result = execute.getResult();ResultStatusType resultStatus = result.getResultStatus();AntomRefundResultVO resultVO = new AntomRefundResultVO();if (resultStatus == ResultStatusType.F) {resultVO.setSuccess(false);resultVO.setMsg(result.getResultMessage());} else if (resultStatus == ResultStatusType.U) {/**结果未知。可能由于系统或网络问题导致处理失败。使用相同的 refundRequestId 重试,或使用 退款查询 接口查询退款结果* */this.refund(refundVO);} else {// 成功String refundTime = execute.getRefundTime();resultVO.setSuccess(true);resultVO.setTime(parseTime((refundTime)));resultVO.setReferenceRefundId(execute.getAcquirerReferenceNo());refundVO.setRefundRequestId(execute.getRefundRequestId());}return resultVO;}@Overridepublic AntomRefundResultVO refundInquiry(String payOrderNo) throws AlipayApiException {AlipayInquiryRefundRequest alipayInquiryRefundRequest = new AlipayInquiryRefundRequest();alipayInquiryRefundRequest.setMerchantAccountId(REFERENCE_MERCHANT_ID);alipayInquiryRefundRequest.setClientId(CLIENT_ID);alipayInquiryRefundRequest.setRefundRequestId(payOrderNo);AntomRefundResultVO resultVO = new AntomRefundResultVO();AlipayInquiryRefundResponse execute = client.execute(alipayInquiryRefundRequest);Result result = execute.getResult();ResultStatusType resultStatus = result.getResultStatus();if (resultStatus == ResultStatusType.F) {resultVO.setSuccess(false);resultVO.setMsg(result.getResultMessage());} else if (resultStatus == ResultStatusType.U) {// 需要重试return this.refundInquiry(payOrderNo);} else if (resultStatus == ResultStatusType.S) {// 成功TransactionStatusType refundStatus = execute.getRefundStatus();if (refundStatus.equals(TransactionStatusType.SUCCESS)) { // 成功String time = execute.getRefundTime();resultVO.setSuccess(true);resultVO.setTime(parseTime(time));} else if (refundStatus.equals(TransactionStatusType.FAIL)) { // 失败resultVO.setSuccess(false);} else if (refundStatus.equals(TransactionStatusType.PROCESSING)) { // 退款处理中resultVO.setSuccess(false);resultVO.setMsg("退款处理中");}}return resultVO;}@Override@Transactionalpublic AntomResultResp refundNotify(AlipayRefundNotify alipayRefundNotify) {/** REFUND_RESULT:表示是关于退款结果的通知* */String notifyType = alipayRefundNotify.getNotifyType();Result result = alipayRefundNotify.getResult();String resultCode = result.getResultCode();Amount refundAmount = alipayRefundNotify.getRefundAmount();String value = refundAmount.getValue(); // 退款金额String paymentRequestId = alipayRefundNotify.getRefundRequestId(); // 订单号String time = alipayRefundNotify.getRefundTime(); // 退款时间/** S: 当 notifyType 为PAYMENT_RESULT时,表示支付成功;当 notifyType 为PAYMENT_PENDING时,表示支付处理中。* F: 表示支付失败。* */ResultStatusType resultStatus = result.getResultStatus();RefundRecordLog refundRecord = new RefundRecordLog();refundRecord.setRefundTime(parseTime(time));if (resultStatus == ResultStatusType.S && "REFUND_RESULT".equals(notifyType)) {// 成功refundRecord.setStatus(1); // 退款状态 1成功 2失败 3退款中refundUpdateStatus(paymentRequestId, value);} else if (resultStatus == ResultStatusType.F) {// 失败refundRecord.setStatus(2); // 退款状态 1成功 2失败 3退款中orderMapper.updatePayStatus(paymentRequestId, 8);}// 更新退款记录refundRecordLogMapper.update(refundRecord, new LambdaQueryWrapper<RefundRecordLog>().eq(RefundRecordLog::getOrderNo, paymentRequestId));// 修改订单状态return new AntomResultResp();}@Overridepublic AntomPayResultVO createPayment(String paymentRequestId,String orderId,String orderDescription,String redirectUrl,BigDecimal amount) throws AlipayApiException {AlipayPayRequest alipayPayRequest = new AlipayPayRequest();alipayPayRequest.setClientId(CLIENT_ID);alipayPayRequest.setProductCode(ProductCodeType.CASHIER_PAYMENT);alipayPayRequest.setPaymentRequestId(paymentRequestId); // 商户为标识支付请求所分配的专属 ID。Antom 使用此字段进行幂等性控制。alipayPayRequest.setPath("ams/api/v1/payments/pay");// TODO 沙箱地址
// alipayPayRequest.setPath("/ams/sandbox/api/v1/payments/pay");// 商家在订单币种中请求接收的支付金额Amount paymentAmount = new Amount();paymentAmount.setCurrency(CURRENCY_VND); // 币种代码paymentAmount.setValue(amount.multiply(VALUE_VND).setScale(0, RoundingMode.UP).toString()); // 以最小币种单位的正整数形式表示的金额值alipayPayRequest.setPaymentAmount(paymentAmount);// 包括买家、商户、商品、金额、配送信息和购买环境的订单信息Order order = new Order();order.setReferenceOrderId(orderId); // 在商户端标识订单的专属 ID,由直接向客户提供服务或商品的商户分配order.setOrderDescription(orderDescription); // 订单的摘要描述。此字段用于显示用户消费记录以及其他后续操作。// Set merchant infoMerchant merchant = new Merchant();merchant.setReferenceMerchantId(REFERENCE_MERCHANT_ID);order.setMerchant(merchant);// order中请求接收的支付金额Amount orderAmount = new Amount();orderAmount.setCurrency(CURRENCY_VND);orderAmount.setValue(amount.multiply(VALUE_VND).setScale(0, RoundingMode.UP).toString());order.setOrderAmount(orderAmount);alipayPayRequest.setOrder(order);// 有关下单环境的信息,例如设备信息Env env = new Env();/** terminalType String REQUIRED*商户服务适用的终端类型。有效值为:*WEB: 客户端终端类型为网站,通过 PC 浏览器打开。*WAP: 客户端终端类型为 H5 页面,通过移动浏览器打开。*APP: 客户端终端类型为移动应用。*MINI_APP: 商户端的终端类型为手机小程序。** */env.setTerminalType(TerminalType.APP);/**操作系统类型。有效值包括:*IOS: 表示操作系统是苹果的 iOS。*ANDROID: 表示操作系统是谷歌的 Android。**/env.setOsType(OsType.IOS);alipayPayRequest.setEnv(env);// 商户或收单机构用于收款的支付方式。PaymentMethod paymentMethod = new PaymentMethod();// 支付方式选项中包含的支付方式类型: https://global.alipay.com/docs/ac/pm_zh-cn/enumeration_valuespaymentMethod.setPaymentMethodType(WalletPaymentMethodType.ZALOPAY.name());alipayPayRequest.setPaymentMethod(paymentMethod);// 回调地址alipayPayRequest.setPaymentNotifyUrl(PAY_NOTIFY_URL); // 用于接收支付结果通知的链接。alipayPayRequest.setPaymentRedirectUrl(redirectUrl); // 用户完成支付后跳转到的商户页面链接。// 支付请求的结算策略SettlementStrategy settlementStrategy = new SettlementStrategy();settlementStrategy.setSettlementCurrency(CURRENCY_VND); // 商户期望结算币种的 ISO 币种代码alipayPayRequest.setSettlementStrategy(settlementStrategy);AlipayPayResponse alipayPayResponse = client.execute(alipayPayRequest);String paymentId = alipayPayResponse.getPaymentId();Result result = alipayPayResponse.getResult();ResultStatusType resultStatus = result.getResultStatus();AntomPayResultVO antomPayResultVO = new AntomPayResultVO();antomPayResultVO.setPaymentId(paymentId);if (resultStatus == ResultStatusType.F) {antomPayResultVO.setSuccess(false);antomPayResultVO.setMsg(result.getResultMessage());} else if (resultStatus == ResultStatusType.U) {/**当返回此值时,检查结果代码:*结果代码不是 PAYMENT_IN_PROCESS: 接口调用失败。使用新的 paymentRequestId 值再次调用此接口。*结果代码是 PAYMENT_IN_PROCESS: 检查是否返回了一个或多个(appLinkUrl, normalUrl, schemeUrl)链接:*返回了一个或多个链接: 交易创建成功。将用户重定向到链接指定的地址以完成支付。*没有返回链接: 交易创建失败。使用新的 paymentRequestId 值再次调用 支付(收银台) 接口。如果问题持续存在,请联系 Antom 技术支持。* */if (!result.getResultCode().equals("PAYMENT_IN_PROCESS")) {antomPayResultVO.setSuccess(false);antomPayResultVO.setMsg(result.getResultMessage());} else {String normalUrl = alipayPayResponse.getNormalUrl();if (StringUtils.isNotBlank(normalUrl)) {antomPayResultVO.setSuccess(true);antomPayResultVO.setNormalUrl(normalUrl);} else {antomPayResultVO.setSuccess(false);antomPayResultVO.setMsg(result.getResultMessage());}}} else {// 成功String normalUrl = alipayPayResponse.getNormalUrl(); // 将用户跳转到默认浏览器或嵌入式 WebView 中的 WAP 或 WEB 页面的链接。antomPayResultVO.setSuccess(true);antomPayResultVO.setNormalUrl(normalUrl);}return antomPayResultVO;}/*** 退款-修改对应订单状态** @param orderNo* @param value 退款金额*/private void refundUpdateStatus(String orderNo, String value) {BigDecimal refundAmount = BigDecimal.valueOf(Integer.parseInt(value));if (orderNo.contains(OrderNoTypeConstant.ORDER)) {com.ycwl.blindBox.model.entity.Order orderDB = orderMapper.selectOne(new LambdaQueryWrapper<com.ycwl.blindBox.model.entity.Order>().eq(com.ycwl.blindBox.model.entity.Order::getOrderNo, orderNo));com.ycwl.blindBox.model.entity.Order order = new com.ycwl.blindBox.model.entity.Order();BigDecimal payPrice = orderDB.getPayPrice();if (refundAmount.compareTo(payPrice) <= 0) {order.setPayStatus(4);order.setStatus(2);} else {order.setPayStatus(3);order.setStatus(7);}orderMapper.update(order, new LambdaUpdateWrapper<com.ycwl.blindBox.model.entity.Order>().eq(com.ycwl.blindBox.model.entity.Order::getOrderNo, orderNo));} else if (orderNo.contains(OrderNoTypeConstant.EXPRESS)) {OrderExpress orderExpressDB = orderExpressMapper.selectOne(new LambdaQueryWrapper<OrderExpress>().eq(OrderExpress::getOrderNo, orderNo));BigDecimal amount = orderExpressDB.getAmount();OrderExpress orderExpress = new OrderExpress();if (refundAmount.compareTo(amount) <= 0) {orderExpress.setPayStatus(4);} else {orderExpress.setPayStatus(3);}orderExpressMapper.update(orderExpress, new LambdaUpdateWrapper<OrderExpress>().eq(OrderExpress::getOrderNo, orderNo));} else if (orderNo.contains(OrderNoTypeConstant.VIP)) {UserVipCardOrder userVipCardOrderDB = userVipCardOrderMapper.selectOne(new LambdaQueryWrapper<UserVipCardOrder>().eq(UserVipCardOrder::getOrderNo, orderNo));BigDecimal price = userVipCardOrderDB.getPrice();UserVipCardOrder userVipCardOrder = new UserVipCardOrder();if (refundAmount.compareTo(price) <= 0) {userVipCardOrder.setPayStatus(4);} else {userVipCardOrder.setPayStatus(3);}userVipCardOrderMapper.update(userVipCardOrder, new LambdaUpdateWrapper<UserVipCardOrder>().eq(UserVipCardOrder::getOrderNo, orderNo));}}/*** 支付-修改对应订单状态** @param orderNo* @param paymentId* @param rtnMsg* @param time* @param success*/private void payUpdateStatus(String orderNo, String paymentId, String rtnMsg, Date time, Boolean success) throws AlipayApiException {if (orderNo.contains(OrderNoTypeConstant.ORDER)) {// order表com.ycwl.blindBox.model.entity.Order order = new com.ycwl.blindBox.model.entity.Order();order.setThirdCode(paymentId);order.setReturnMsg(rtnMsg);order.setPayTime(time);order.setPayStatus(2);order.setStatus(2);if (!success) {order.setPayStatus(6);orderMapper.update(order, new LambdaUpdateWrapper<com.ycwl.blindBox.model.entity.Order>().eq(com.ycwl.blindBox.model.entity.Order::getOrderNo, orderNo));} else {// 验证订单信息com.ycwl.blindBox.model.entity.Order orderDB = orderMapper.selectOne(new LambdaQueryWrapper<com.ycwl.blindBox.model.entity.Order>().eq(com.ycwl.blindBox.model.entity.Order::getOrderNo, orderNo));Integer type = orderDB.getType();if (type == 2) {// 盲盒订单OrderParamDTO orderParamDTO = new OrderParamDTO();orderParamDTO.setId(order.getId());orderParamDTO.setTotalNum(order.getTotalNum());orderParamDTO.setUnitPrice(order.getUnitPrice());orderParamDTO.setOrderNo(order.getOrderNo());orderParamDTO.setTraceId(0);orderParamDTO.setBoxId(order.getBoxId());orderParamDTO.setBlindboxId(order.getBlindboxId());orderParamDTO.setUserId(order.getUserId());orderParamDTO.setUserName(order.getUserName());orderParamDTO.setPayPrice(order.getPayPrice());orderParamDTO.setPayIntegral(order.getPayIntegral());orderParamDTO.setPlayId(order.getPlayId());orderParamDTO.setPayTime(time);orderParamDTO.setPayOrderNo(order.getPayOrderNo());orderParamDTO.setNotes("盲盒购买" + order.getTotalNum() + "个");Integer payWay = order.getPayWay();PayTypeEnum payTypeEnum = PayTypeEnum.getByType(payWay);orderParamDTO.setPayTypeEnum(payTypeEnum);if (payTypeEnum == PayTypeEnum.ONLINE) {blindBoxOrderService.completeOrder(orderParamDTO);}} else {// 普通订单
// orderMapper.update(order, new LambdaUpdateWrapper<com.ycwl.blindBox.model.entity.Order>()
// .eq(com.ycwl.blindBox.model.entity.Order::getOrderNo, orderNo)
// .eq(com.ycwl.blindBox.model.entity.Order::getPayStatus, 1));shopOrderService.completeOrder(order);}}} else if (orderNo.contains(OrderNoTypeConstant.EXPRESS)) {// 运费表OrderExpress orderExpress = orderExpressMapper.selectOne(new LambdaQueryWrapper<OrderExpress>().eq(OrderExpress::getOrderNo, orderNo));if (orderExpress.getPayStatus() == 1) {orderExpress.setThirdNo(paymentId);orderExpress.setThirdReturn(paymentId);orderExpress.setPayTime(time);orderExpress.setPayStatus(2);if (!success) {orderExpress.setPayStatus(6);orderExpress.setPayError(rtnMsg);orderExpressMapper.updateById(orderExpress);} else {bagService.completeExpressOrder(orderExpress);}}} else if (orderNo.contains(OrderNoTypeConstant.VIP)) {// 会员卡表// 加锁,防止主动查询和回调同时触发以下逻辑Lock lock = lockMap.computeIfAbsent(orderNo, k -> new ReentrantLock());lock.lock();try {UserVipCardOrder order = userVipCardOrderMapper.getByOrderNo(orderNo);order.setReturnMsg(rtnMsg);order.setExternalOrderId(paymentId);// 支付状态 0待支付 1已支付 2已取消Integer payStatus = order.getPayStatus();if (payStatus == 1 || payStatus == 2) {vipCardPaymentCallback(order, success);log.info("会员卡支付结果同步成功");} else {log.error("会员卡支付结果已同步,不再重复执行");}} catch (Exception e) {log.error("会员卡支付回调异常", e);} finally {lock.unlock();lockMap.remove(orderNo);}} else if (orderNo.contains(OrderNoTypeConstant.RECHARGE)) {// 加锁,防止主动查询和回调同时触发以下逻辑Lock lock = lockMap.computeIfAbsent(orderNo, k -> new ReentrantLock());lock.lock();try {// 充值订单UserBalanceRechargeLog userBalanceRechargeLog = userBalanceRechargeLogMapper.selectOne(new LambdaQueryWrapper<UserBalanceRechargeLog>().eq(UserBalanceRechargeLog::getRechargeNo, orderNo));userBalanceRechargeLog.setThirdReturn(rtnMsg);userBalanceRechargeLog.setThirdNo(paymentId);// 充值状态 1:待支付 2:支付成功 3:支付失败 4:过期 5:取消Integer status = userBalanceRechargeLog.getStatus();if (status != 1) {rechargeSuccess(userBalanceRechargeLog, success);log.info("充值订单支付结果同步成功");} else {log.error("充值订单支付结果已同步,不再重复执行");}} catch (Exception e) {log.error("充值订单回调异常", e);} finally {lock.unlock();lockMap.remove(orderNo);}}}/*** 充值成功处理逻辑* 添加积分、添加经验、添加余额** @param userBalanceRechargeLog 充值订单* @param success 是否成功*/@Transactional(rollbackFor = Exception.class)public void rechargeSuccess(UserBalanceRechargeLog userBalanceRechargeLog, Boolean success) {if (success) {Integer userId = userBalanceRechargeLog.getUserId();Integer orderId = userBalanceRechargeLog.getId();// 充值金额BigDecimal amount = userBalanceRechargeLog.getAmount();// 增加积分capitalChangeUtil.addIntegral(amount, userId, orderId, IntegralChangeTypeEnum.RECHARGE_ADDITION);// 更新余额capitalChangeUtil.addBalance(amount, userId, orderId, BalanceChangeTypeEnum.RECHARGE_ADDITION);// 增加赠送金额RechargeSetting rechargeSetting = rechargeSettingMapper.getMaxByAmount(amount);if (rechargeSetting != null) {Integer giveRatio = rechargeSetting.getGiveRatio();BigDecimal giveAmount = amount.multiply(BigDecimal.valueOf(giveRatio)).divide(BigDecimal.valueOf(100), 2, RoundingMode.DOWN);capitalChangeUtil.addBalance(giveAmount, userId, orderId, BalanceChangeTypeEnum.RECHARGE_ADDITION);}// 增加用户经验、更新用户等级userLevelUtil.updateUserLevelUtil(amount, userId, PayTypeEnum.ONLINE, true);userBalanceRechargeLog.setStatus(2);userBalanceRechargeLogMapper.updateById(userBalanceRechargeLog);} else {userBalanceRechargeLog.setStatus(3);userBalanceRechargeLogMapper.updateById(userBalanceRechargeLog);}}/*** 转换时间 2019-11-27T12:01:01+08:00 转成date** @param dateTimeString* @return*/private Date parseTime(String dateTimeString) {OffsetDateTime offsetDateTime = OffsetDateTime.parse(dateTimeString, DateTimeFormatter.ISO_OFFSET_DATE_TIME);return Date.from(offsetDateTime.toInstant());}/*** 会员卡支付回调** @param order* @param success*/private void vipCardPaymentCallback(UserVipCardOrder order, Boolean success) {Integer orderId = order.getId();Integer userId = order.getUserId();if (success) {// 假设支付成功// 订单状态改为已支付order.setPayStatus(VipCardOrderStatusEnum.PAID_ALREADY.getCode());order.setPayTime(new Date());userVipCardOrderMapper.updateById(order);Long cardId = order.getCardId();// 支付成功,给用户添加会员卡,如果用户已有会员卡就修改会员卡的状态和过期日期UserVipCard userVipCard = userVipCardMapper.getUserVipCardByCardId(order.getCardId(), userId);// 本次购买的月卡,领取奖励的起始时间Date startDate = null;if (userVipCard == null) {// 用户没有该会员卡,新增用户会员卡userVipCard = new UserVipCard();userVipCard.setId(SnowFlakeUtil.getLongId());userVipCard.setCardId(order.getCardId());userVipCard.setCardType(order.getCardType());userVipCard.setCardName(order.getCardName());userVipCard.setUserId(userId);userVipCard.setStatus(UserVipCardStatusEnum.UNUSED.getCode());// 计算过期日期Date expiredDate = getExpiredDate(userVipCard.getCardType(), new Date());startDate = new Date();userVipCard.setExpiredDate(expiredDate);userVipCardMapper.insert(userVipCard);} else {// 用户已有该会员卡,修改会员卡的状态和延长过期日期Integer status = userVipCard.getStatus();Date expiredDate = null;if (status.equals(UserVipCardStatusEnum.USED.getCode())) {userVipCard.setStatus(UserVipCardStatusEnum.UNUSED.getCode());// 计算过期日期,原來的已经过期了,就在当前时间上累加expiredDate = getExpiredDate(userVipCard.getCardType(), new Date());startDate = new Date();} else {startDate = DateUtils.addDateDays(userVipCard.getExpiredDate(), 1);// 计算过期日期,原來的还没过期就再原来的时间上累加expiredDate = getExpiredDate(userVipCard.getCardType(), userVipCard.getExpiredDate());}userVipCard.setExpiredDate(expiredDate);userVipCardMapper.updateById(userVipCard);}// 添加用户会员卡奖励信息// 用户会员卡idLong userVipCardId = userVipCard.getId();// 用户可领取奖励列表List<UserCardAwardReceiveRecord> userCardAwardList = new ArrayList<>();List<VipCardAwardSetDTO> awardSetList = awardSetMapper.getByCardId(cardId);for (VipCardAwardSetDTO awardSet : awardSetList) {List<VipCardAward> cardAwards = awardMapper.getBySetId(awardSet.getId());for (VipCardAward cardAward : cardAwards) {UserCardAwardReceiveRecord receiveRecord = new UserCardAwardReceiveRecord();receiveRecord.setId(SnowFlakeUtil.getLongId());receiveRecord.setUserCardId(userVipCardId);receiveRecord.setOrderId(orderId);receiveRecord.setAwardType(cardAward.getType());receiveRecord.setCouponId(cardAward.getCouponId());receiveRecord.setCouponQuantity(cardAward.getCouponQuantity());receiveRecord.setAmount(cardAward.getAmount());receiveRecord.setUserId(userId);receiveRecord.setDay(awardSet.getDay());receiveRecord.setReceiveStatus(UserCardAwardReceiveStatusEnum.NOT_CLAIMED.getCode());if (awardSet.getDay() == 1 && startDate.compareTo(new Date()) == 0) {receiveRecord.setStatus(UserCardAwardStatusEnum.RECEIVING_IN_PROGRESS.getCode());} else {receiveRecord.setStatus(UserCardAwardStatusEnum.NOT_STARTED_YET.getCode());}// 奖励过期日期(创建日期+day)receiveRecord.setExpiredDate(DateUtils.addDateDays(startDate, awardSet.getDay()));userCardAwardList.add(receiveRecord);}}// 添加用户奖励信息userCardAwardReceiveRecordMapper.insertBatch(userCardAwardList);// 增加用户积分capitalChangeUtil.addIntegral(order.getPrice(), userId, orderId, IntegralChangeTypeEnum.SPENDING_REWARDS);// 增加用户经验、更新用户等级userLevelUtil.updateUserLevelUtil(order.getPrice(), userId, PayTypeEnum.BALANCE, false);//消费返佣inviteNewRecordService.grantInviteNewAward(order.getUserId(), InviteNewGiftTypeEnum.COMMISSION.getCode(), order.getPrice());} else {// 支付失败// 订单状态改为取消或者支付异常order.setPayStatus(VipCardOrderStatusEnum.CANCELLED.getCode());order.setCancelTime(new Date());userVipCardOrderMapper.updateById(order);// 回退会员卡库存vipCardMapper.addInventory(order.getCardId(), 1);}}/*** 计算会员卡的过期日期** @param type 会员卡类型 0周卡 1月卡* @param expiredDate 会员卡现在的过期日期,没有就取当前时间* @return 计算出来的过期日期*/private Date getExpiredDate(Integer type, Date expiredDate) {if (expiredDate == null) {log.error("会员卡现在的过期日期不能为空");
// throw new BaseException("会员卡现在的过期日期不能为空");return new Date();}if (type == 0) {// 周卡// 周卡有效期7天int days = 7;return DateUtils.addDateDays(expiredDate, days);} else if (type == 1) {// 月卡// 月卡有效期30天int days = 30;return DateUtils.addDateDays(expiredDate, days);} else {log.error("会员卡类型错误");
// throw new BaseException("会员卡类型错误");return new Date();}}
}
获取配置方法(这里的配置可以直接写在yml配置文件中):
/*** 根据Type获取系统参数** @param type 类型* @return 参数值*/public static List<SysSetting> getSdParam(String type) {SysSettingMapper sysSettingDao = ApplicationContextHelper.popBean(SysSettingMapper.class);List<SysSetting> sysBase = sysSettingDao.selectList(new LambdaQueryWrapper<SysSetting>().eq(SysSetting::getType, type));return sysBase;}
相关文章:
【Java】阿里环球Antom支付对接
阿里环球Antom支付对接 线上文档地址: GitHub:https://github.com/alipay/global-open-sdk-java 文档:https://global.alipay.com/docs/ac/ams_zh-cn/session_cashier maven: <!--阿里国际支付--><dependency><g…...
【vim】vim编辑器如何设置行号
vim编辑器如何设置行号 一、**临时设置行号**二、永久设置行号2.1. **用户配置文件方式(针对当前用户)**2.2. **全局配置文件方式(谨慎使用,会影响所有用户)** 在Vim中设置行号有以下两种常见的方法: 一、…...
爬虫基础之爬取某站视频
目标网址:为了1/4螺口买小米SU7,开了一个月,它值吗?_哔哩哔哩_bilibili 本案例所使用到的模块 requests (发送HTTP请求)subprocess(执行系统命令)re (正则表达式操作)json (处理JSON数据) 需求分析: 视频的名称 F12 打开开发者工具 or 右击…...
2024嵌入式系统的未来发展与技术洞察分享
时间如白驹过隙,不知不觉又是一年,这一年收获满满。接下来,将本年度对技术的感悟和洞察分析如下,希望对大家有所帮助。 在过去几十年里,嵌入式系统技术迅速发展,成为现代电子设备和智能硬件的核心组成部分。…...
[微服务]注册中心优化
环境隔离 企业实际开发中,往往会搭建多个运行环境,例如: 开发环境测试环境预发布环境生产环境 这些不同环境之间的服务和数据之间需要隔离。 还有的企业中,会开发多个项目,共享nacos集群。此时,这些项目…...
Leetcode 3426. Manhattan Distances of All Arrangements of Pieces
Leetcode 3426. Manhattan Distances of All Arrangements of Pieces 1. 解题思路2. 代码实现 题目链接:3426. Manhattan Distances of All Arrangements of Pieces 1. 解题思路 这道题很惭愧,一开始没有搞定,后来看了答案想了想ÿ…...
【重庆市乡镇界】面图层shp格式arcgis数据乡镇名称和编码wgs84坐标无偏移内容测评
标题中的“最新重庆市乡镇界面图层shp格式arcgis数据乡镇名称和编码wgs84坐标无偏移最新”指的是一个地理信息系统(GIS)的数据集,特别设计用于ArcGIS软件。这个数据集包含了重庆市所有乡镇的边界信息,以Shapefile(.shp…...
基于ChatGPT的论文写作辅助工具研究
**基于ChatGPT的论文写作辅助工具研究** **摘要**: 随着人工智能技术的飞速发展,自然语言处理(NLP)领域取得了显著进步。ChatGPT作为OpenAI最新推出的生成式预训练Transformer模型,在文本生成、对话系统等方面展现出…...
定时器setTimeout和setInterval
setTimeOut()异步 setInterval()异步...
PCL 部分点云视点问题【2025最新版】
目录 一、问题概述二、解决方案1、软件实现2、代码实现三、调整之后博客长期更新,本文最近更新时间为:2025年1月18日。 一、问题概述 针对CloudCompare软件处理过的pcd格式点云,在使用PCL进行特征点提取、配准等实验中最终显示结果出现点云位置偏差较大的问题,本博客给出解…...
Cursor 与常见集成开发环境(IDE)的优势对比
Cursor与常见集成开发环境(IDE)的优势对比 一、AI 辅助编程能力 强大的代码生成功能: Cursor: 以其内置的强大 AI 辅助编程功能为核心优势。用户可以通过输入自然语言描述,快速生成各种编程语言的代码。例如…...
TDengine 做为 FLINK 数据源技术参考手册
Apache Flink 是一款由 Apache 软件基金会支持的开源分布式流批一体化处理框架,可用于流处理、批处理、复杂事件处理、实时数据仓库构建及为机器学习提供实时数据支持等诸多大数据处理场景。与此同时,Flink 拥有丰富的连接器与各类工具,可对接…...
不重启JVM,替换掉已经加载的类
不重启JVM,替换掉已经加载的类 直接操作字节码 使用ASM框架直接操作class文件,在类中修改代码,然后retransform就可以了 下边是BTrace官方提供的一个简单例子: package com.sun.btrace.samples;import com.sun.btrace.annotati…...
axios的使用总结
一、Axios 简介 Axios 是一个基于 Promise 的 HTTP 客户端,用于浏览器和 Node.js。在 Vue 项目中,它主要用于发送 HTTP 请求来获取数据(如从 API 获取数据)或者提交数据(如用户登录、注册等表单数据)。 二…...
使用 F12 查看 Network 及数据格式
在浏览器中,F12 开发者工具的 “Network” 面板是用于查看网页在加载过程中发起的所有网络请求,包括 API 请求,以及查看这些请求的详细信息和响应数据的。以下以常见的 Chrome 浏览器为例,介绍如何使用 F12 控制台查看 Network 里…...
HTML<img>标签
例子 如何插入图片: <img src"img_girl.jpg" alt"Girl in a jacket" width"500" height"600"> 下面有更多“自己尝试”的示例。 定义和用法 该<img>标签用于在 HTML 页面中嵌入图像。 从技术上讲&#x…...
Android系统开发(六):从Linux到Android:模块化开发,GKI内核的硬核科普
引言: 今天我们聊聊Android生态中最“硬核”的话题:通用内核镜像(GKI)与内核模块接口(KMI)。这是内核碎片化终结者的秘密武器,解决了内核和供应商模块之间无尽的兼容性问题。为什么重要&#x…...
每日一刷——1.20——准备蓝桥杯
链接:登录—专业IT笔试面试备考平台_牛客网 来源:牛客网 题目一 请统计某个给定范围[L, R]的所有整数中,数字2出现的次数。 比如给定范围[2, 22],数字2在数2中出现了1次,在数12中出现1次,在数20中出现1次&a…...
知行合一:解决有心无力的问题,解决知易行难的问题,知行合一并不意味着事事都要合一,而是....
问题是什么? 想学习的时候,有手机阻碍我们。想戒掉手机短视频,卸载后,几天的时间,又下载了回来。制定了减肥计划,但就是不执行。明知道这样做是不对的,但依然行动不起来。 沉溺于各种各样的享…...
C++ Qt练习项目 日期时间数据 未完待续
个人学习笔记 新建项目 设计UI 实现组件功能 参考资料 4.7日期时间数据_哔哩哔哩_bilibili...
Golang学习笔记_28——工厂方法模式(实例)
Golang学习笔记_26——通道 Golang学习笔记_27——单例模式 Golang学习笔记_28——工厂方法模式 工厂方法模式(实例) package factory_method_demoimport "fmt"// Order 接口,定义订单的基本操作 type Order interface {Calculate…...
linux下springboot项目nohup日志或tomcat日志切割处理方案
目录 1. 配置流程 2. 配置说明 其他配置选项: 3. 测试执行 4. 手动执行 https://juejin.cn/post/7081890486453010469 通常情况下,我们的springboot项目部署到linux服务器中,通过nohup java -jar xxx.jar &指令来进行后台运行我们…...
SentencePiece和 WordPiece tokenization 的含义和区别
SentencePiece和 WordPiece tokenization 的含义和区别 SentencePiece 和 WordPiece 都是常用的分词(tokenization)技术,主要用于自然语言处理(NLP)中的文本预处理,尤其是在处理大规模文本数据时。它们都基于子词(subword)单元,能够将未登录词(out-of-vocabulary, O…...
视频修复最强算法 部署笔记2025
目录 模型下载: 模型: 原版保存的视频,vs code不播放: 模型下载: Release ProPainter V0.1.0 Release sczhou/ProPainter GitHub huggingface-cli download --resume-download lixiaowen/diffuEraser --local-dir /mnt/pfs/models/huggingface/models--lixiaowen--d…...
Java数据结构——优先队列
目录 引言1. 优先队列2. 优先队列的实现2.1 堆的概念2.2 堆的创建2.2.1 堆的向下调整2.3 堆的插入2.4 堆的删除 3. 总结 引言 前面一篇文章讲了二叉树,本篇将讲述数据结构中的优先队列,优先队列则需要用到完全二叉树来实现。 1. 优先队列 队列&#x…...
红外热成像之无人机载荷
电力巡检 相较于传统的人工电力巡线方式,无人机巡检能够在高空对人工难以达到或无法检测的设备进行检测,实现了电子化、信息化、智能化巡检,可以提高巡检的工作效率和应急抢险水平。 森林防火 无人机搭载红外光电系统能在森林高空进行全天候监…...
深入Spring Boot:自定义Starter开发与实践
引言 Spring Boot通过其强大的自动配置机制和丰富的Starter模块,极大地简化了Spring应用的开发过程。Starter模块封装了一组相关的依赖和配置,使得开发者可以通过简单的依赖引入,快速启用特定的功能。然而,除了使用Spring Boot提…...
MasterSAM downloadService任意文件读取(CVE-2024-55457)(附脚本)
免责申明: 本文所描述的漏洞及其复现步骤仅供网络安全研究与教育目的使用。任何人不得将本文提供的信息用于非法目的或未经授权的系统测试。作者不对任何由于使用本文信息而导致的直接或间接损害承担责任。如涉及侵权,请及时与我们联系,我们将尽快处理并删除相关内容。 0x0…...
【Pytest】基础到高级功能的理解使用
文章目录 第一部分:Pytest 简介1.1 什么是 Pytest?1.2 Pytest 的历史1.3 Pytest 的核心概念1.4 Pytest 的特点1.5 为什么选择 Pytest? 第二部分:Pytest 的基本使用2.1 安装 Pytest2.2 编写第一个测试用例2.2.1 创建一个简单的测试…...
【Linux系统编程】—— 从零开始实现一个简单的自定义Shell
文章目录 什么是自主shell命令行解释器?实现shell的基础认识全局变量的配置初始化环境变量实现内置命令(如 cd 和 echo)cd命令:echo命令: 构建命令行提示符获取并解析用户输入的命令执行内置命令与外部命令Shell的主循…...
探索云原生可观测性:技术与团队协作的深度结合
TheNewStack 出品的电子书《Cloud Native Observability for DevOps Teams》读后感,老书新读,还是另有一番领悟。 阅读原文请转到:https://jimmysong.io/blog/cloud-native-observability-devops/ 最近读了 TheNewStack 发布的电子书《Cloud …...
Vue基础(2)
19、组件之间传递数据 组件与组件之间不是完全独立的,而是有交集的,那就是组件与组 件之间是可以传递数据的 传递数据的解决方案就是 props ComponentA.vue <template><!-- 使用ComponentB组件,并传递title属性 --><h3>…...
Yearning开源MySQL SQL审核平台
一款MYSQL SQL语句/查询审计工具,为DBA与开发人员使用. 本地部署,注重隐私,简单高效的MYSQL审计平台。 它可以通过流程审批,实现真实线上环境sql的审核和执行,还可以回滚执行,能够确保线上SQL更新的可靠性…...
《鸿蒙Next应用商店:人工智能开启智能推荐与运营新时代》
在科技飞速发展的当下,鸿蒙Next系统的出现为操作系统领域带来了新的变革与机遇,而人工智能技术的融入更是让其应用商店的智能化推荐和运营迈向了一个全新的高度。 用户画像精准构建 在鸿蒙Next系统中,应用商店可以借助系统强大的权限管理和…...
SSTI注入
ssti漏洞成因 ssti服务端模板注入,ssti主要为python的一些框架 jinja2 mako tornado django,PHP框架smarty twig,java框架jade velocity等等使用了渲染函数时,由于代码不规范或信任了用户输入而导致了服务端模板注入,…...
根据经纬度查询地理位置信息API
API 概述 接口名称:查询地理位置信息V2接口类型:HTTP GET接口地址:https://api.kertennet.com/geography/locationInfo_v2请求编码格式:utf-8 请求说明 请求头部 标签类型必填说明参数示例Content-Typestring是请求的内容类型…...
【知识分享】PCIe5.0 TxRx 电气设计参数汇总
目录 0 引言 1 参考时钟--Refclk 2 发射端通道设计 3 发送均衡技术 4 接收端通道设计 5 接收均衡技术 6 结语 7 参考文献 8 扩展阅读 0 引言 PCI Express Base Specification 5.0的电气规范中,关键技术要点如下: 1. 支持2.5、5.0、8.0、16.0和3…...
Airsim 项目结构分析
Airsim 项目结构分析 本文只分析最核心的 AirLib 项目结构,以及其与 Unreal 项目的关系 假如已经根据 Airsim 主页,克隆了完整项目。 Build on Linux - AirSim 克隆源码 # go to the folder where you clone GitHub projects git clone https://git…...
STM32+W5500+以太网应用开发+003_TCP服务器添加OLED(u8g2)显示状态
STM32W5500以太网应用开发003_TCP服务器添加OLED(u8g2)显示状态 实验效果3-TCP服务器OLED1 拷贝显示驱动代码1.1 拷贝源代码1.2 将源代码添加到工程1.3 修改代码优化等级1.4 添加头文件路径1.5 修改STM32CubeMX工程 2 修改源代码2.1 添加头文件2.2 main函…...
SQLmap 注入-03 获得用户信息
1: Sqlmap 先进入库,然后进入table, 然后列出column: sqlmap -u "http://192.168.56.133/mutillidae/index.php?pageuser-info.php&usernamexiaosheng&passwordabc&user-info-php-submit-buttonViewAccountDetails" --batch -p username -D …...
Kafka 和 MQ 的区别
1.概述 1.1.MQ简介 消息中间件,其实准确的叫法应该叫消息队列(message queue),简称MQ。其本质上是个队列,有FIFO的性质,即first in first out,先入先出。 目前市场上主流的MQ有三款ÿ…...
若依报错:无法访问com.ruoyi.common.annotation
无法访问com.ruoyi.common.annotation 若依的父工程的pom文件中设置了jdk为1.8,将idea的jdk也改为1.8即可。...
MCU、MPU、SOC、ECU、CPU、GPU的区别到底是什么
MCU、MPU、SOC、ECU、CPU、GPU的区别 参数MCUMPUSoCECUCPUGPU处理能力低至中中至高综合,视具体设计而定专用于汽车控制中至高高(并行能力强)集成度高低高高低(需配合主板使用)低(通常作为外部设备ÿ…...
档案事业与数据要素之间有什么关系?
在数字时代背景下,档案事业正经历着前所未有的变革。随着大数据、云计算、人工智能等技术的快速发展,档案数据已成为重要的基础性战略资源和关键生产要素。那么档案事业与数据要素之间究竟有什么关系? 一、档案数据要素的内涵与价值 数据要…...
HarmonyOS NEXT:华为分享-碰一碰开发分享
随着科技的不断进步,智能手机和智能设备之间的互联互通变得越来越重要。华为作为科技行业的领军企业,一直致力于为用户提供更加便捷、高效的使用体验。HarmonyOS NEXT系统的推出,特别是其中的“碰一碰”功能,为用户带来了前所未有…...
nuxt3项目打包部署到服务器后配置端口号和开启https
nuxt3打包后的项目部署相对于一般vite打包的静态文件部署要稍微麻烦一些,还有一个主要的问题是开发环境配置的.env环境变量在打包后部署时获取不到,具体的解决方案可以参考我之前文章 nuxt3项目打包后获取.env设置的环境变量无效的解决办法。 这里使用的…...
面试:Hadoop,块,HDFS的优缺点,HDFS的读写流程
Hadoop CDH会简化Hadoop的安装 Hue主要用于数据分析和处理,而CM(Cloudera Manager)则主要用于集群的管理和运维。 HDFS HDFS的块 块是 HDFS 系统当中的最小存储单位, 在hadoop2.0和3.0中默认128MB 在HDFS上的文件会被拆分成多个块,每个块作为独立的单…...
Codeforces Round 903 (Div. 3) E. Block Sequence
题解: 想到从后向前DP f[i] 表示从 i ~ n 转化为“美观”所需要的最少的步骤 第一种转移方式:直接删除掉第i个元素,那么就是上一步 f[i 1] 加上 1;第二种转移方式:从第 i a[i] 1 个元素直接转移,不需要增加步数&a…...
web-view环境下,H5页面打开其他小程序
在Web-view环境下,H5页面无法直接打开其他小程序。正确的实现方式是先从H5页面跳转回当前小程序,再由当前小程序跳转到目标小程序。具体实现方法如下: H5页面跳转回小程序时,调用wx.miniProgram.navigateTo()方法。 小程序跳转到…...
C语言之饭店外卖信息管理系统
🌟 嗨,我是LucianaiB! 🌍 总有人间一两风,填我十万八千梦。 🚀 路漫漫其修远兮,吾将上下而求索。 C语言之饭店外卖信息管理系统 目录 设计题目设计目的设计任务描述设计要求输入和输出要求验…...