Java实现微信支付、退款(小程序支付JSAPI-V3-整合微信sdk开发)

news2024/12/23 22:19:11

一、废话不多说,直接上教程:

写代码之前首先要明白微信支付的支付流程。

二、支付流程:

小程序调用后端预支付接口 => 预支付接口调用成功返回给小程序支付凭证id => 小程序拿到支付凭证调用微信后台支付接口 => 小程序支付成功后,微信后台执行支付回调将支付订单信息返回(预支付调用微信后台是需要传入支付成功后自己本地的回调接口地址,可以在回调接口里设置自己的业务逻辑)

三、退款流程:

服务端自定义设置商户退款单号进行退款申请=>服务端通过微信退款sdk对微信服务器发起验签请求=>退款成功后,微信后台执行退款回调将退款信息返回(退款地址回调微信后台是需要传入退款申请成功后自己本地的回调接口地址,可以在回调接口里设置自己的业务逻辑)

四、官方文档:

微信小程序支付官方文档

如果我们按照微信支付官方文档去一步步写会很慢很繁琐,我教大家使用微信支付官网推荐java-sdk工具包去实现。

微信支付java-sdk工具包

五、开始手撸代码:

1.导入依赖
Gradle:
implementation 'com.github.wechatpay-apiv3:wechatpay-java:0.2.11'
Maven:
<dependency>
  <groupId>com.github.wechatpay-apiv3</groupId>
  <artifactId>wechatpay-java</artifactId>
  <version>0.2.11</version>
</dependency>
2.配置微信支付前必要的密钥和商户信息
资源文件配置:

在这里插入图片描述

# 微信小程序支付配置信息
wx:
  # 微信小程序appid
  app-id: wx************
  # 商户号
  mch-id: ************
  # 证书序列号
  mch-serial-no: ************
  # 小程序密钥
  app-secret: ************
  # apiV3密钥
  api-key: ************
  # 回调接口地址
  notify-url: https://************/wxpay/payNotify
  # 退款回调接口地址
  refund-notify-url: https://************/wxpay/refund/payNotify
  # 证书地址
  key-path: ************/src/main/resources/cert/apiclient_key.pem
  cert-path: ************/src/main/resources/cert/apiclient_key.pem
  cert-p12-path: ************/src/main/resources/cert/apiclient_key.pem

获取配置信息:

@Component
@ConfigurationProperties(prefix = "wx")
@Data
@ToString
public class WxPayV3Bean {

	private String appId;

	private String mchId;

	private String mchSerialNo;

	private String appSecret;

	private String apiKey;

	private String notifyUrl;

	private String refundNotifyUrl;

	private String keyPath;

}

3.创建接口请求类和返回类

查看官方文档,查看调用微信支付时需要传哪些必传的参数。
在这里插入图片描述

预支付请求类:

@Data
@Accessors(chain = true)
public class WXPayOrderReqVO {

	@Schema(description = "订单支付类型(1.申请保证金 2. 客户保证金)",required = true)
	@NotBlank(message = "订单支付类型不能为空!")
	private String orderType;//附加数据,回调时可根据这个数据辨别订单类型或其他

	@Schema(description = "总金额(单位:分)",required = true)
	@NotNull(message = "总金额不能为空!")
	private Integer totalPrice;

	@Schema(description = "商品名称",required = true)
	@NotBlank(message = "商品名称不能为空!")
	private String goodsName;

	@Schema(description = "openid",required = true)
	@NotBlank(message = "openId不能为空!")
	private String openId;

	@Schema(description = "商品订单号",required = true)
	@NotNull(message = "商品订单号不能为空!")
	private Long orderSn;
}

预退款请求类:
@Data
@Accessors(chain = true)
public class WXRefundOrderReqVO {

	/**
	 * 微信支付订单号,微信支付订单号和商家订单号二选一
	 */
	@Schema(description = "微信支付订单号")
	private String transactionId;

	/**
	 * 商家订单号,对应 out_trade_no,
	 */
	@Schema(description = "商家订单号")
	private String orderId;

	/**
	 * 商户退款单号,对应out_refund_no
	 */
	@Schema(description = "商户退款单号")
	private String outRefundNo;

	/**
	 * 退款原因,选填
	 */
	@Schema(description = "退款原因")
	private String reason;

	/**
	 * 回调地址
	 */
	@Schema(description = "回调地址")
	private String notify;

	/**
	 * 退款金额
	 */
	@Schema(description = "退款金额")
	private Integer refundMoney;

	/**
	 * 原订单金额,必填
	 */
	@Schema(description = "原订单金额")
	private Integer totalMoney;
}

由于使用的这个sdk工具类里面有返回类,可以不用自己创建返回类。

这里我自己创了个返回类。

预支付返回类:

@Data
@Accessors(chain = true)
public class WxPayRespVO implements Serializable {

	private static final long serialVersionUID = 1L;
	/**
	 * 预支付交易会话标识小程序下单接口返回的prepay_id参数值
	 */
	@Schema(description = "预支付交易会话标识小程序下单接口返回的prepay_id参数值")
	private String prepayId;
	/**
	 * 随机字符串
	 */
	@Schema(description = "随机字符串")
	private String nonceStr;
	/**
	 * 时间戳
	 */
	@Schema(description = "时间戳")
	private Long timeStamp;
	/**
	 * 签名
	 */
	@Schema(description = "签名")
	private String paySign;
}

预退款返回类(可不用):

@Data
@Accessors(chain = true)
public class WXRefundOrderRespVO {
	/**
	 * 微信支付退款号
	 */
	@Schema(description = "微信支付退款号")
	private String refundId;

	/**
	 * 商户退款单号
	 */
	@Schema(description = "商户退款单号")
	private String outRefundNo;

	/**
	 * 微信支付订单号
	 */
	@Schema(description = "微信支付订单号")
	private String transactionId;

	/**
	 * 商户订单号
	 */
	@Schema(description = "商户订单号")
	private String outTradeNo;

	/**
	 * 退款渠道
	 */
	@Schema(description = "退款渠道")
	private String channel;

	/**
	 * 退款入账账户
	 */
	@Schema(description = "退款入账账户")
	private String userReceivedAccount;

	/**
	 * 退款成功时间
	 */
	@Schema(description = "退款成功时间")
	private String successTime;

	/**
	 * 退款创建时间
	 */
	@Schema(description = "退款创建时间")
	private String createTime;

	/**
	 * 退款状态
	 */
	@Schema(description = "退款状态")
	private String status;

	/**
	 * 资金账户
	 */
	@Schema(description = "资金账户")
	private String fundsAccount;
}

工具类:

/**
 * @project 小程序支付
 * @Classname WXPayUtil
 * @Description TODO
 * @Author: whl
 * @CreateTime: 2023-10-16  16:00
 */
public class WXPayUtil {

	//随机字符串设置
	private static final String SYMBOLS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";

	private static final Random RANDOM = new SecureRandom();


	public static String getSign(String signatureStr,String privateKey) throws InvalidKeyException, NoSuchAlgorithmException, SignatureException, IOException, URISyntaxException {
		//replace 根据实际情况,不一定都需要
		String replace = privateKey.replace("\\n", "\n");
		PrivateKey merchantPrivateKey = PemUtil.loadPrivateKeyFromPath(replace);
		Signature sign = Signature.getInstance("SHA256withRSA");
		sign.initSign(merchantPrivateKey);
		sign.update(signatureStr.getBytes(StandardCharsets.UTF_8));
		return Base64Utils.encodeToString(sign.sign());
	}

	/**
	 * 获取随机字符串 Nonce Str
	 *
	 * @return String 随机字符串
	 */
	public static String generateNonceStr() {
		char[] nonceChars = new char[32];
		for (int index = 0; index < nonceChars.length; ++index) {
			nonceChars[index] = SYMBOLS.charAt(RANDOM.nextInt(SYMBOLS.length()));
		}
		return new String(nonceChars);
	}

}

4.开始写支付接口
Controller层:
预支付:
	/**
	 * 微信预支付测试接口
	 * @return R
	 */
	@Operation(summary = "微信预支付测试接口" , description = "微信预支付测试接口" )
	@SysLog("微信预支付测试接口" )
	@PostMapping("/createOrder")
	public R<WxPayRespVO> createOrder(@RequestBody @Validated WXPayOrderReqVO req) throws Exception {
		return R.ok(wxPayService.createOrder(req));
	}
支付回调:
	/**
	 * 微信支付回调
	 * @return R
	 */
	@Operation(summary = "微信支付回调" , description = "微信支付回调" )
	@SysLog("微信支付回调" )
	@PostMapping("/payNotify")
	public R payNotify(HttpServletRequest request) throws Exception {
		wxPayService.payNotify(request);//注意:回调接口需要暴露到公网上,且要放开token验证
		return R.ok();
	}
Service、ServiceImpl层:
欲支付:

Service

	/**
	 * 功能描述:
	 * 〈微信预支付〉
	 * @Param: [req]
	 * @Return: [WxPayRespVO]
	 * @Author: whl
	 * @Date: 2023/10/10 13:46
	 */
	WxPayRespVO createOrder(WXPayOrderReqVO req) throws Exception;

ServiceImpl


	@Override
	public WxPayRespVO createOrder(WXPayOrderReqVO req) throws Exception {
		try {
			// 使用自动更新平台证书的RSA配置
			// 一个商户号只能初始化一个配置,否则会因为重复的下载任务报错
			Config config =
					new RSAAutoCertificateConfig.Builder()
							.merchantId(wxPayV3Bean.getMchId())
							// 使用 com.wechat.pay.java.core.util 中的函数从本地文件中加载商户私钥,商户私钥会用来生成请求的签名
							.privateKeyFromPath(wxPayV3Bean.getKeyPath())
							.merchantSerialNumber(wxPayV3Bean.getMchSerialNo())
							.apiV3Key(wxPayV3Bean.getApiKey())
							.build();
			// 构建service
			JsapiService service = new JsapiService.Builder().config(config).build();
			// request.setXxx(val)设置所需参数,具体参数可见Request定义
			PrepayRequest request = new PrepayRequest();
			Amount amount = new Amount();
			amount.setTotal(req.getTotalPrice());
			request.setAmount(amount);
			request.setAppid(wxPayV3Bean.getAppId());
			request.setMchid(wxPayV3Bean.getMchId());
			request.setDescription(req.getGoodsName());
			request.setNotifyUrl(wxPayV3Bean.getNotifyUrl());
			request.setOutTradeNo(req.getOrderSn().toString());
			request.setAttach(req.getOrderType());
			Payer payer = new Payer();
			payer.setOpenid(req.getOpenId());
			request.setPayer(payer);
			// 调用下单方法,得到应答
			// 调用微信sdk接口,生成预支付交易单
			PrepayResponse response = service.prepay(request);
			WxPayRespVO vo = new WxPayRespVO();
			Long timeStamp = System.currentTimeMillis() / 1000;
			vo.setTimeStamp(timeStamp);
			String substring = UUID.randomUUID().toString().replaceAll("-", "").substring(0, 32);
			vo.setNonceStr(substring);
			String signatureStr = Stream.of(wxPayV3Bean.getAppId(), String.valueOf(timeStamp), substring, "prepay_id=" + response.getPrepayId())
					.collect(Collectors.joining("\n", "", "\n"));
			String sign = WXPayUtil.getSign(signatureStr, wxPayV3Bean.getKeyPath());
			vo.setPaySign(sign);
			vo.setPrepayId("prepay_id=" + response.getPrepayId());
			return vo;
		}catch (ServiceException e){
			JSONObject parse = JSONObject.parseObject(e.getResponseBody());
			throw new ResultException(ResultEnum.ERROR,parse.getString("message"));
		}catch (Exception e){
			throw new ResultException(ResultEnum.ERROR,e.toString());
		}
	}

这个签名是工具类生成的,之前找v3版本的生成签名找半天才找到,这个特别难找。

注意:这里有个特别大的一个坑,大家一定要注意。JSONObject千万要引fastjson2的包,之前没注意引用的jsonfast的包测试环境一直报错,特别坑。找了半天才找到,直接当场破防。

支付回调:

Service

	/**
	 * 功能描述:
	 * 〈微信预支付〉
	 * @Param: [req]
	 * @Return: [WxPayRespVO]
	 * @Author: whl
	 * @Date: 2023/10/10 13:46
	 */
	WxPayRespVO createOrder(WXPayOrderReqVO req) throws Exception;

ServiceImpl


	@Override
	public void payNotify(HttpServletRequest request) throws Exception {
		try {
			//读取请求体的信息
			ServletInputStream inputStream = request.getInputStream();
			StringBuffer stringBuffer = new StringBuffer();
			BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
			String s;
			//读取回调请求体
			while ((s = bufferedReader.readLine()) != null) {
				stringBuffer.append(s);
			}
			String s1 = stringBuffer.toString();
			String timestamp = request.getHeader(WECHAT_PAY_TIMESTAMP);
			String nonce = request.getHeader(WECHAT_PAY_NONCE);
			String signType = request.getHeader("Wechatpay-Signature-Type");
			String serialNo = request.getHeader(WECHAT_PAY_SERIAL);
			String signature = request.getHeader(WECHAT_PAY_SIGNATURE);
			// 如果已经初始化了 RSAAutoCertificateConfig,可直接使用
			// 没有的话,则构造一个
			log.error(JSON.toJSONString(wxPayV3Bean));
			NotificationConfig config = new RSAAutoCertificateConfig.Builder()
					.merchantId(wxPayV3Bean.getMchId())
					.privateKeyFromPath(wxPayV3Bean.getKeyPath())
					.merchantSerialNumber(wxPayV3Bean.getMchSerialNo())
					.apiV3Key(wxPayV3Bean.getApiKey())
					.build();
			// 初始化 NotificationParser
			NotificationParser parser = new NotificationParser(config);
			RequestParam requestParam=new RequestParam.Builder()
					.serialNumber(serialNo)
					.nonce(nonce)
					.signature(signature)
					.timestamp(timestamp)
					// 若未设置signType,默认值为 WECHATPAY2-SHA256-RSA2048
					.signType(signType)
					.body(s1)
					.build();
			Transaction parse = parser.parse(requestParam, Transaction.class);
			System.out.println("parse = " + parse);
			//parse.getTradeState().equals("SUCCESS");说明支付成功
			if (parse.getTradeState().equals(Transaction.TradeStateEnum.SUCCESS)){
			//你的业务代码
			}
		} catch (Exception e) {
			throw new ResultException(ResultEnum.ERROR,e.toString());
		}
	}
5.开始写退款接口
Controller层:
退款接口:
	/**
	 * 退款
	 * @param id id(这里可以是你业务的订单id或者就商户下的退款id,具体看你的业务)
	 * @return R
	 */
	@Operation(summary = "交易订单退款申请" , description = "交易订单退款申请" )
	@SysLog("交易订单退款申请" )
	@GetMapping("/reviewRefund/{id}" )
	public R reviewRefundById(@PathVariable("id" ) String id) throws Exception {
		return R.ok(wxPayService.refundOrder(id));
	}
退款回调接口:
	/**
	 * 微信退款回调
	 * @return R
	 */
	@Operation(summary = "微信退款回调" , description = "微信退款回调" )
	@SysLog("微信退款回调" )
	@PostMapping("/refund/payNotify")
	public R refundPayNotify(HttpServletRequest request) throws Exception {
		wxPayService.refundNotify(request);//注意:回调接口需要暴露到公网上,且要放开token验证
		return R.ok();
	}

退款涉及我的部分业务代码,这里就不一一展示了,具体传参可以看我写的退款请求类

Service、ServiceImpl层:
退款申请:

Service

	/**
	 * 功能描述:
	 * 〈微信退款〉
	 * @Param: [req]
	 * @Return: [void]
	 * @Author: whl
	 * @Date: 2023/10/12 15:46
	 */
	WXRefundOrderRespVO refund(WXRefundOrderReqVO refundOrder) throws Exception;

ServiceImpl


	/**
	 * 功能描述:
	 * 〈微信退款〉
	 * @Param: [req]
	 * @Return: [void]
	 * @Author: whl
	 * @Date: 2023/10/12 15:46
	 */
	@Override
	public WXRefundOrderRespVO refund(WXRefundOrderReqVO refundOrder) throws Exception {
		try {
			// 使用自动更新平台证书的RSA配置
			// 一个商户号只能初始化一个配置,否则会因为重复的下载任务报错
			Config config =
					new RSAAutoCertificateConfig.Builder()
							.merchantId(wxPayV3Bean.getMchId())
							//使用 SDK 不需要计算请求签名和验证应答签名
							// 使用 com.wechat.pay.java.core.util 中的函数从本地文件中加载商户私钥,商户私钥会用来生成请求的签名
							.privateKeyFromPath(wxPayV3Bean.getKeyPath())
							.merchantSerialNumber(wxPayV3Bean.getMchSerialNo())
							.apiV3Key(wxPayV3Bean.getApiKey())
							.build();
			// 构建退款service
			RefundService service = new RefundService.Builder().config(config).build();
			// request.setXxx(val)设置所需参数,具体参数可见Request定义
			//构建退款请求
			CreateRequest request = new CreateRequest();
			//构建订单金额信息
			AmountReq amountReq = new AmountReq();
			//退款金额
			amountReq.setRefund(Long.valueOf(refundOrder.getRefundMoney()));
			//原订单金额
			amountReq.setTotal(Long.valueOf(refundOrder.getTotalMoney()));
			//货币类型(默认人民币)
			amountReq.setCurrency("CNY");
			request.setAmount(amountReq);
			//商户退款单号
			request.setOutRefundNo(String.valueOf(refundOrder.getOutRefundNo()));
			//退款通知回调地址
			request.setNotifyUrl(wxPayV3Bean.getRefundNotifyUrl());
			// 调用退款方法,得到应答
			// 调用微信sdk接口
			Refund refund = service.create(request);
			//接收退款返回参数
			Refund refundResponse = new Refund();
			refundResponse.setStatus(refund.getStatus());
			if (refundResponse.getStatus().equals(Status.SUCCESS)){
				//说明退款成功,开始接下来的业务操作
				WXRefundOrderRespVO refundOrderRespVO = new WXRefundOrderRespVO();
				//你的业务代码
				return refundOrderRespVO;
			}
		}catch (ServiceException e){
			JSONObject parse = JSONObject.parseObject(e.getResponseBody());
			throw new ResultException(ResultEnum.ERROR,parse.getString("message"));
		}catch (Exception e){
			throw new ResultException(ResultEnum.ERROR,e.toString());
		}
		return null;
	}
退款回调:

Service

	/**
	 * 功能描述:
	 * 〈微信退款回调〉
	 * @Param: [request, response]
	 * @Return: [void]
	 * @Author: whl
	 * @Date: 2023/10/13 10:46
	 */
	void refundNotify(HttpServletRequest request) throws Exception ;

ServiceImpl


	/**
	 * 功能描述:
	 * 〈微信退款回调〉
	 * @Param: [request, response]
	 * @Return: [void]
	 * @Author: whl
	 * @Date: 2023/10/13 10:46
	 */
	@Override
	public void refundNotify(HttpServletRequest request) throws Exception {
		try {
			//读取请求体的信息
			ServletInputStream inputStream = request.getInputStream();
			StringBuffer stringBuffer = new StringBuffer();
			BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
			String s;
			//读取回调请求体
			while ((s = bufferedReader.readLine()) != null) {
				stringBuffer.append(s);
			}
			String s1 = stringBuffer.toString();
			String timestamp = request.getHeader(WECHAT_PAY_TIMESTAMP);
			String nonce = request.getHeader(WECHAT_PAY_NONCE);
			String signType = request.getHeader("Wechatpay-Signature-Type");
			String serialNo = request.getHeader(WECHAT_PAY_SERIAL);
			String signature = request.getHeader(WECHAT_PAY_SIGNATURE);
			// 如果已经初始化了 RSAAutoCertificateConfig,可直接使用
			// 没有的话,则构造一个
			log.error(JSON.toJSONString(wxPayV3Bean));
			NotificationConfig config = new RSAAutoCertificateConfig.Builder()
					.merchantId(wxPayV3Bean.getMchId())
					.privateKeyFromPath(wxPayV3Bean.getKeyPath())
					.merchantSerialNumber(wxPayV3Bean.getMchSerialNo())
					.apiV3Key(wxPayV3Bean.getApiKey())
					.build();
			// 初始化 NotificationParser
			NotificationParser parser = new NotificationParser(config);
			RequestParam requestParam=new RequestParam.Builder()
					.serialNumber(serialNo)
					.nonce(nonce)
					.signature(signature)
					.timestamp(timestamp)
					// 若未设置signType,默认值为 WECHATPAY2-SHA256-RSA2048
					.signType(signType)
					.body(s1)
					.build();
			RefundNotification parse = parser.parse(requestParam, RefundNotification.class);
			System.out.println("parse = " + parse);
			//parse.getRefundStatus().equals("SUCCESS");说明退款成功
			if (parse.getRefundStatus().equals(Transaction.TradeStateEnum.SUCCESS)){
				//你的业务代码
			}
		} catch (Exception e) {
			throw new ResultException(ResultEnum.ERROR,e.toString());
		}
	}

自此整合微信支付、退款流程就全部完毕,又由本次是基于微信sdk的开发,所有省略了大量重复接口请求的编写和签名的验证等,代码相对精简,便于开发学习

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1098796.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JOSEF约瑟 多档切换式漏电(剩余)继电器JHOK-ZBL1 30/100/300/500mA

系列型号&#xff1a; JHOK-ZBL多档切换式漏电&#xff08;剩余&#xff09;继电器&#xff08;导轨&#xff09; JHOK-ZBL1多档切换式漏电&#xff08;剩余&#xff09;继电器 JHOK-ZBL2多档切换式漏电&#xff08;剩余&#xff09;继电器 JHOK-ZBM多档切换式漏电&#xf…

自己写spring boot starter问题总结

1. Unable to find main class 创建spring boot项目写自己的starterxi写完之后使用install出现Unable to find main class&#xff0c;这是因为spring boot打包需要一个启动类&#xff0c;按照以下写法就没事 <plugins><plugin><groupId>org.springframewo…

嵌入式系统学习路径:

嵌入式系统学习路径&#xff1a; 00001. 确保扎实的C语言基础&#xff0c;包括高级编程知识和数据结构算法。 00002. 00003. 学习Linux应用层开发&#xff0c;包括并发程序设计、网络编程和数据库开发。 00004. 00005. 探索无线通信领域&#xff0c;如Zigbee、低功…

OpenHarmony创新赛|赋能直播第四期

开放原子开源大赛OpenHarmony创新赛进入了中期评审环节&#xff0c;为了解决开发者痛点&#xff0c;本期以三方库移植、MQTT移植案例、开发工具介绍的3节系列技术课程&#xff0c;帮助开发者提升开发效率&#xff0c;为作品的创新能力奠定坚实基础。 扫描下方长图二维码&#x…

手把手教学mfc140u.dll丢失的解决方法,快速解决mfc140u.dll丢失

不知道大家有没有有没有遇到过mfc140u.dll丢失的情况。今天手把手教学mfc140u.dll丢失的解决方法&#xff0c;快速解决mfc140u.dll丢失&#xff0c;这么详细的步骤&#xff0c;我不信这你还不能解决mfc140u.dll丢失的问题。 一.解决mfc140u.dll丢失的方法 解决办法一&#xff…

centos7.3无法使用ping命令提示connect: Resource temporarily unavailable

centos7.3无法使用ping命令 问题描述linux版本解决方法 问题描述 有一台linux服务器&#xff0c;因网络策略开通所需&#xff0c;要登录服务器进行ping测试。结果登录后使用ping命令提示&#xff1a;connect: Resource temporarily unavailable linux版本 查看版本文件得知…

半导体产业链解析:晶圆厂、无晶圆厂与代工厂的比较与作用

半导体产业一直是全球科技发展的关键驱动力&#xff0c;在半导体产业中&#xff0c;晶圆厂、无晶圆厂公司和代工厂是三个重要的参与者。它们在产业环节、生产方式、经营模式和市场竞争等方面存在一些显著差异。本文将探讨半导体晶圆厂、无晶圆厂公司和代工厂之间的区别&#xf…

从零到一完成Midway.js登录、注册、鉴权功能

您好&#xff0c;如果喜欢我的文章&#xff0c;可以关注我的公众号「量子前端」&#xff0c;将不定期关注推送前端好文~ 前言 本文将从项目搭建到实现从零到一开发一个登录、注册、鉴权的简易版注册登录系统&#xff0c;主要功能和技术选型如下&#xff1a; 服务端框架———…

mac 版本 Lightroom Classic 2024 正式版来了 七个有趣的新功能值得更新

mac 版本 Lightroom Classic 2024 正式版终于来了&#xff01;此次更新有七大功能&#xff1a;新增高动态范围编辑和输出、智能 AI 交互式镜头模糊、全局和局部焦点颜色、预设搜索、支持新型相机和镜头等功能。本文将做详细解读&#xff0c;看看这七个有趣的新功能值得去更新吗…

RK3568驱动指南|第六篇-平台总线-第54章 点亮LED灯实验

瑞芯微RK3568芯片是一款定位中高端的通用型SOC&#xff0c;采用22nm制程工艺&#xff0c;搭载一颗四核Cortex-A55处理器和Mali G52 2EE 图形处理器。RK3568 支持4K 解码和 1080P 编码&#xff0c;支持SATA/PCIE/USB3.0 外围接口。RK3568内置独立NPU&#xff0c;可用于轻量级人工…

流量渠道分析

文章目录 流量渠道分析一、为什么需要流量渠道分析1.了解流量来源&#xff0c;制定营销策略2.发现流量缺失点&#xff0c;提高转化率3.提高用户体验&#xff0c;增加用户留存 二、流量渠道分析方法1.确定分析目标2.选择分析工具3.设置分析参数4.收集数据5.数据分析6.制定优化方…

2024上海国际智慧城市展览会(世亚智博会)智慧城市,数字中国

在数字化、智能化的时代背景下&#xff0c;智慧城市成为了全球瞩目的焦点。而作为智慧城市领域的重要盛会&#xff0c;2024上海国际智慧城市展览会&#xff08;简称&#xff1a;世亚智博会&#xff09;则将再次汇聚全球目光。此次展览将于2024年3月26日至28日在上海跨国采购会展…

解决java.lang.IllegalArgumentException: servlet映射中的<url pattern>[demo1]无效

当我使用tomcat启动使用servlet项目时&#xff0c;出现了报错&#xff1a; java.lang.IllegalArgumentException: servlet映射中的<url pattern>[demo1]无效 显示路径错误&#xff0c;于是去检查Web.xml中的配置&#xff0c;发现是配置文件的路径写错了&#xff0c;少写了…

【Python第三方包】快速获取硬件信息和使用情况(psutil、platform)

文章目录 前言一、psutil包1.1 安装psutil包1.2 psutil 使用方式获取CPU使用率获取内存使用情况将内存的获取的使用情况变成GB和MB获取磁盘使用情况磁盘内存进行转换获取网络信息网络info 二、platform2.1 platform的介绍2.2 platform 使用方式获取操作系统的名称获取架构的名称…

unapp项目发布h5 详细操作流程以及注意事项 (hash模式)

首先需要在manifest.json文件中的web配置中 这里因为公司项目都是hash模式 所以写的./ 接下来看源码视图会发现多了一个配置项&#xff1a; 接下来点击hbx上面的 发行》网站-PC Web或手机H5(仅适用于uni-app)(H) 此时查看控制台会显示打包后的存放路径 接下来就直接给后端同…

Windows下Redis3.0主从模式架构搭建

redis版本&#xff1a;Redis-x64-3.0.504 复制相同文件 修改文件夹下redis.windows.conf 文件配置(注意&#xff1a;主有密码&#xff0c;从必须有密码且跟主相同) 修改端口&#xff1a; 主库&#xff1a;端口号6379 从库1&#xff1a;修改端口号为6380 从库2&#xff1a;修…

金融用户实践|分布式存储支持数据仓库业务系统性能验证

作者&#xff1a;深耕行业的 SmartX 金融团队 闫海涛 估值是指对资产或负债的价值进行评估的过程&#xff0c;这对于投资决策具有重要意义。每个金融公司资管业务人员都期望能够实现实时的业务估值&#xff0c;快速获取最新的数据和指标&#xff0c;从而做出更明智的投资决策。…

【学习笔记】RabbitMQ01:基础概念认识以及快速部署

参考资料 RabbitMQ官方网站RabbitMQ官方文档噼咔噼咔-动力节点教程 文章目录 一、认识RabbitMQ1.1 消息中间件&#xff08;MQ Message Queue 消息队列1.2 主流的消息中间件1.3 MQ的应用场景1.3.1 异步处理1.3.2 系统解耦1.3.3 流量削峰1.3.4 日志处理 二、RabbitMQ运行环境搭建…

Windows10不常用操作(录屏、开启超级管理员、关闭自动IP配置、Edge崩溃等)

Win10家庭版开启超级管理员 Win10家庭版开启或禁用超级管理员账户步骤如下&#xff1a; 在搜索框中输入CMD&#xff0c;右键以管理员方式运行。 开启 net user administrator /active:yes禁用 net user administrator /active:no Win10关闭自动IP配置 win10设置完静态ip&am…

微服务架构 | 超时管理

INDEX LSA 级别与全年停机时间速查表LSA 级别实战TP 性能超时时间设计原则 LSA 级别与全年停机时间速查表 计算公式&#xff1a;60 * 60 * 24 * 365 * (1-LSA) 31,536,000‬ * (1-LSA) 系统级别LSA级别全年停机时间099.999%5分钟099.99%52分钟199.9%8.8小时299%3.65 天 LSA…