利用微信官方提供的SDK wechatpay-apache-httpclient 实现。以微信小程序支付为例,其他支付也是一样的,就是参数和接口地址不同。

微信支付V3文档

首先要在微信商户平台设置好APIv3密钥和微信商户证书,查看商户号和商户证书的序列号,并准备好创建商户证书时生成的apiclient_key.pem文件,这个文件就是商户证书私钥。

创建微信配置文件(也可以配置在yml中通过@Value获取):


public class WechatConstants {//微信支付商户号public static final String WECHAT_MCH_ID = "";//微信商户平台v2密钥public static final String WECHAT_MCH_SECRET_V2 = "";//微信商户平台v3密钥public static final String WECHAT_MCH_SECRET_V3 = "";//微信商户平台商户API证书序列号public static final String WECHAT_MCH_SERIAL_NUM = "";//微信商户平台证书私钥 即证书中的apiclient_key.pem文件中的内容 可以直接写在这里 也可以用流读取文件public static final String WECHAT_MCH_PRIVATE_KEY = "";//微信小程序appidpublic static final String WECHAT_MP_APPID = "";//微信小程序密钥public static final String WECHAT_MP_SECRET = "";
}

创建微信地址配置文件:


/*** 微信相关接口请求地址*/
public class WechatUrlConstants {//小程序code获取openidpublic final static String CODE_2_SESSION = "https://api.weixin.qq.com/sns/jscode2session";//微信支付v3 jsapi下单public final static String PAY_V3_JSAPI = "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi";//微信支付v3 申请退款public final static String PAY_V3_REFUND = "https://api.mch.weixin.qq.com/v3/refund/domestic/refunds";//微信支付v3 通过商户订单号查询订单public final static String PAY_V3_QUERY_OUT = "https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/%s?mchid=%s";//微信支付v3 查询单笔退款public final static String PAY_V3_QUERY_REFUND = "https://api.mch.weixin.qq.com/v3/refund/domestic/refunds/%s";//微信支付v2 支付通知接口地址public final static String PAY_V2_NOTIFY = "https://xxx.com/api/wechatPay/wechatPayNotify";//微信支付v2 退款通知接口地址public final static String PAY_V2_REFUND_NOTIFY = "https://xxx.com/api/wechatPay/wechatRefundNotify";//微信支付v3 支付通知接口地址public final static String PAY_V3_NOTIFY = "https://xxx.com/api/wechatPay/v3/wechatPayNotify";//微信支付v3 退款通知接口地址public final static String PAY_V3_REFUND_NOTIFY = "https://xxx.com/api/wechatPay/v3/wechatRefundNotify";
}

其中最后四个通知接口地址,是自己服务器的接口地址,必须为外网可访问的url,不能携带参数。 公网域名必须为https,如果是走专线接入,使用专线NAT IP或者私有回调域名可使用http。

创建微信支付v3工具类:


import com.alibaba.fastjson.JSONObject;
import com.lzn.wechatpaydemo.common.constant.WechatConstants;
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.auth.*;
import com.wechat.pay.contrib.apache.httpclient.cert.CertificatesManager;
import com.wechat.pay.contrib.apache.httpclient.constant.WechatPayHttpHeaders;
import com.wechat.pay.contrib.apache.httpclient.notification.Notification;
import com.wechat.pay.contrib.apache.httpclient.notification.NotificationHandler;
import com.wechat.pay.contrib.apache.httpclient.notification.NotificationRequest;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Component;import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.security.PrivateKey;
import java.util.Map;/*** 微信V3支付工具类*/
@Component
public class WechatPayV3Utils {protected final Logger logger = LoggerFactory.getLogger(this.getClass());//商户证书私钥private PrivateKey merchantPrivateKey;//证书private Verifier verifier;//请求客户端private CloseableHttpClient httpClient;/*** 获取商户证书私钥*/private void setMerchantPrivateKey() throws Exception {ClassPathResource classPathResource = new ClassPathResource("cer/apiclient_key.pem");InputStream certStream = classPathResource.getInputStream();//读取文件形式加载商户私钥merchantPrivateKey = PemUtil.loadPrivateKey(certStream);//直接把商户私钥以字符串形式配置//merchantPrivateKey = PemUtil.loadPrivateKey(new ByteArrayInputStream(WechatConstants.WECHAT_MCH_PRIVATE_KEY.getBytes(StandardCharsets.UTF_8)));}/*** 获取微信证书** @throws Exception*/private void setVerifier() throws Exception {if (merchantPrivateKey == null) {setMerchantPrivateKey();}// 获取证书管理器实例CertificatesManager certificatesManager = CertificatesManager.getInstance();// 向证书管理器增加需要自动更新平台证书的商户信息certificatesManager.putMerchant(WechatConstants.WECHAT_MCH_ID,new WechatPay2Credentials(WechatConstants.WECHAT_MCH_ID, new PrivateKeySigner(WechatConstants.WECHAT_MCH_SERIAL_NUM, merchantPrivateKey)),WechatConstants.WECHAT_MCH_SECRET_V3.getBytes(StandardCharsets.UTF_8));// ... 若有多个商户号,可继续调用putMerchant添加商户信息// 从证书管理器中获取verifierverifier = certificatesManager.getVerifier(WechatConstants.WECHAT_MCH_ID);}/*** 创建请求客户端** @throws Exception*/private void setHttpClient() throws Exception {if (verifier == null) {setVerifier();}WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create().withMerchant(WechatConstants.WECHAT_MCH_ID, WechatConstants.WECHAT_MCH_SERIAL_NUM, merchantPrivateKey).withValidator(new WechatPay2Validator(verifier));// ... 接下来,你仍然可以通过builder设置各种参数,来配置你的HttpClienthttpClient = builder.build();}/*** 发送POST请求** @param url    请求地址* @param params json参数* @return*/public JSONObject sendPost(String url, JSONObject params) {try {if (httpClient == null) {setHttpClient();}HttpPost httpPost = new HttpPost(url);httpPost.addHeader("Accept", "application/json");httpPost.addHeader("Content-type", "application/json; charset=utf-8");httpPost.setEntity(new StringEntity(params.toJSONString(), StandardCharsets.UTF_8));CloseableHttpResponse response = httpClient.execute(httpPost);String bodyAsString = EntityUtils.toString(response.getEntity());logger.info("微信返回的内容:" + bodyAsString);if (StringUtils.isEmpty(bodyAsString)) {return null;}return JSONObject.parseObject(bodyAsString);} catch (Exception e) {logger.error("微信支付V3请求失败");e.printStackTrace();return null;}}/*** 发送get请求** @param url 请求地址 参数直接在地址上拼接* @return*/public JSONObject sendGet(String url) {try {if (httpClient == null) {setHttpClient();}URIBuilder uriBuilder = new URIBuilder(url);HttpGet httpGet = new HttpGet(uriBuilder.build());httpGet.addHeader("Accept", "application/json");CloseableHttpResponse response = httpClient.execute(httpGet);String bodyAsString = EntityUtils.toString(response.getEntity());logger.info("微信返回的内容:" + bodyAsString);if (StringUtils.isEmpty(bodyAsString)) {return null;}return JSONObject.parseObject(bodyAsString);} catch (Exception e) {logger.error("微信支付V3请求失败");e.printStackTrace();return null;}}/*** 回调通知验签与解密** @param request* @return*/public JSONObject getCallbackData(HttpServletRequest request) {try {if (verifier == null) {setVerifier();}String wechatPaySerial = request.getHeader(WechatPayHttpHeaders.WECHAT_PAY_SERIAL);String nonce = request.getHeader(WechatPayHttpHeaders.WECHAT_PAY_NONCE);String timestamp = request.getHeader(WechatPayHttpHeaders.WECHAT_PAY_TIMESTAMP);String signature = request.getHeader(WechatPayHttpHeaders.WECHAT_PAY_SIGNATURE);String body;BufferedReader reader = request.getReader();String line ;StringBuilder inputString = new StringBuilder();while ((line = reader.readLine()) != null) {inputString.append(line);}body = inputString.toString();reader.close();// 构建request,传入必要参数NotificationRequest res = new NotificationRequest.Builder().withSerialNumber(wechatPaySerial).withNonce(nonce).withTimestamp(timestamp).withSignature(signature).withBody(body).build();NotificationHandler handler = new NotificationHandler(verifier, WechatConstants.WECHAT_MCH_SECRET_V3.getBytes(StandardCharsets.UTF_8));// 验签和解析请求体Notification notification = handler.parse(res);logger.info("回调通知数据:" + notification.toString());// 解析开数据String decryptData = notification.getDecryptData();logger.info("回调解析数据:" + decryptData);if (StringUtils.isEmpty(decryptData)) {return null;}return JSONObject.parseObject(decryptData);} catch (Exception e) {logger.error("微信支付V3回调失败");e.printStackTrace();return null;}}/*** 微信支付v3签名 RSA签名** @param message 要签名的字符串* @return*/public String signRSA(String message) {try {if (merchantPrivateKey == null) {setMerchantPrivateKey();}Signer signer = new PrivateKeySigner(WechatConstants.WECHAT_MCH_SERIAL_NUM, merchantPrivateKey);Signer.SignatureResult signature = signer.sign(message.getBytes(StandardCharsets.UTF_8));return signature.getSign();} catch (Exception e) {e.printStackTrace();return "";}}
}

这里我是通过文件流读取的商户证书私钥,因此需要把apiclient_key.pem文件放到resource目录下的cer目录中(其他目录也可以,需要自行对应修改路径)

controller接口调用方法:

import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.lzn.wechatpaydemo.common.constant.WechatConstants;
import com.lzn.wechatpaydemo.common.constant.WechatUrlConstants;
import com.lzn.wechatpaydemo.common.util.WechatPayV3Utils;
import com.lzn.wechatpaydemo.project.web.BaseController;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;/*** 微信支付相关接口demo v3版本* 文档地址 https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_5_1.shtml*/
@RestController
@RequestMapping("/api/wechatPay/v3")
public class WechatPayV3ApiController extends BaseController {protected final Logger logger = LoggerFactory.getLogger(this.getClass());@Autowiredpublic WechatPayV3Utils wechatPayV3Utils;/*** 发起微信小程序支付** @param code 用于换取openid 正式使用时openid可以直接从用户信息中获取 不需要在此接口中获取* @return 小程序支付所需参数*/@PostMapping("/wechatPay")public Map<String, Object> wechatPay() {//@TODO demo中先写死的一些参数Long userId = 1L; //先写死一个用户idString openid = "xxx"; //先写死一个openidBigDecimal amount = new BigDecimal("0.01"); //先写死一个金额 单位:元String content = "支付demo-买牛订金"; //先写死一个商品描述String attach = "我是附加数据"; //先写死一个附加数据 这是可选的 可以用来判断支付内容做支付成功后的处理//@TODO 在自己的数据库中创建订单数据 待支付状态String out_trade_no = createOrderNo("DJ", userId); //创建商户订单号Calendar calendar = Calendar.getInstance();calendar.set(Calendar.MINUTE, calendar.get(Calendar.MINUTE) + 15);SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX");JSONObject params = new JSONObject();params.put("appid", WechatConstants.WECHAT_MP_APPID); //小程序appidparams.put("mchid", WechatConstants.WECHAT_MCH_ID); //商户号params.put("description", content); //商品描述params.put("out_trade_no", out_trade_no); //商户订单号params.put("time_expire", sdf.format(calendar.getTime())); //交易结束时间 选填 时间到了之后将不能再支付 遵循rfc3339标准格式params.put("attach", attach); //附加数据 选填 在查询API和支付通知中原样返回 可作为自定义参数使用params.put("notify_url", WechatUrlConstants.PAY_V3_NOTIFY); //支付结果异步通知接口JSONObject amount_json = new JSONObject();amount_json.put("total", Integer.parseInt(amount_fee(amount))); //支付金额 单位:分params.put("amount", amount_json); //订单金额信息JSONObject payer = new JSONObject();payer.put("openid", openid); //用户在小程序侧的openidparams.put("payer", payer); //支付者信息JSONObject res = wechatPayV3Utils.sendPost(WechatUrlConstants.PAY_V3_JSAPI, params); //发起请求if (res == null || StringUtils.isEmpty(res.getString("prepay_id"))) {//@TODO 支付发起失败可以将订单数据回滚return error("支付发起失败");}StringBuilder sb = new StringBuilder();//返回给小程序拉起微信支付的参数Map<String, String> result = new HashMap<>();result.put("appId", WechatConstants.WECHAT_MP_APPID); //小程序appidsb.append(result.get("appId")).append("\n");result.put("timeStamp", (new Date().getTime() / 1000) + ""); //时间戳sb.append(result.get("timeStamp")).append("\n");result.put("nonceStr", RandomStringUtils.randomAlphanumeric(32)); //32位随机字符串sb.append(result.get("nonceStr")).append("\n");result.put("package", "prepay_id=" + res.getString("prepay_id")); //预支付id 格式为 prepay_id=xxxsb.append(result.get("package")).append("\n");result.put("paySign", wechatPayV3Utils.signRSA(sb.toString())); //签名result.put("signType", "RSA"); //加密方式 固定RSAresult.put("out_trade_no", out_trade_no); //商户订单号 此参数不是小程序拉起支付所需的参数 因此不参与签名return success(result);}/*** 支付成功后查询订单状态** @param out_trade_no 商户订单号* @return null代表查询失败 SUCCESS-成功 USERPAYING(用户支付中)和ACCEPT(已接收,等待扣款)为中间态 需要重新查询 其他为支付失败*/@PostMapping("/checkPay")public Map<String, Object> checkPay(String out_trade_no) {//@TODO 先查询自己数据库中的订单状态是否支付成功 若成功 则直接返回SUCCESS 若未成功 则需调用查询支付接口String status = orderQueryByOutTradeNo(out_trade_no);return success("请求成功", status);}/*** 申请微信退款* 交易时间超过一年的订单无法提交退款* 微信支付退款支持单笔交易分多次退款,多次退款需要提交原支付订单的商户订单号和设置不同的退款单号。* 申请退款总金额不能超过订单金额。* 一笔退款失败后重新提交,请不要更换退款单号,请使用原商户退款单号* 每个支付订单的部分退款次数不能超过50次* 同一笔订单多次退款的请求需相隔1分钟** @param transaction_id 微信支付订单号* @return*/@PostMapping("/wechatRefund")public Map<String, Object> wechatRefund(String transaction_id) {//@TODO demo中先写死的一些参数Long userId = 1L; //先写死一个用户idBigDecimal amount = new BigDecimal("0.01"); //先写死一个退款金额 单位:元String reason = "支付demo-退还订金"; //先写死一个退款原因 这是可选的//@TODO 先查询订单是否可退款 将订单修改为退款中等业务处理String out_refund_no = createOrderNo("TK", userId); //创建商户退款单号JSONObject params = new JSONObject();params.put("transaction_id", transaction_id); //微信支付订单号 也可以传out_trade_no 即发起支付时创建的商户订单号 二选一 transaction_id>out_trade_noparams.put("out_refund_no", out_refund_no); //商户退款单号params.put("reason", reason); //退款原因 选填 若填写 会在退款消息中显示给用户params.put("notify_url", WechatUrlConstants.PAY_V3_REFUND_NOTIFY); //退款结果异步通知接口JSONObject amountJson = new JSONObject();amountJson.put("refund", Integer.parseInt(amount_fee(amount))); //退款金额 单位:分amountJson.put("total", Integer.parseInt(amount_fee(amount))); //原订单金额 单位:分amountJson.put("currency", "CNY"); //退款币种params.put("amount", amountJson); //订单金额信息JSONObject res = wechatPayV3Utils.sendPost(WechatUrlConstants.PAY_V3_REFUND, params); //发起请求if (res == null) {//@TODO 退款失败时回滚订单状态return error("退款申请失败");}logger.info("微信退款单号:" + res.getString("refund_id"));//@TODO 可以在此更新订单微信退款单号等信息return success();}/*** 微信支付异步通知** @param request* @return*/@PostMapping("/wechatPayNotify")public Map<String, String> wechatPayNotify(HttpServletRequest request) {Map<String, String> result = new HashMap<>(2);JSONObject res = wechatPayV3Utils.getCallbackData(request);if (res == null) {result.put("code", "FAIL");result.put("message", "失败");return result;}logger.info("最终拿到的微信支付通知数据:" + res);//@TODO 处理支付成功后的业务 例如 将订单状态修改为已支付 具体参数键值可参考文档 注意!!! 微信可能会多次发送重复的通知 因此要判断业务是否已经处理过了 避免重复处理result.put("code", "SUCCESS");result.put("message", "OK");return result;}/*** 微信退款异步通知** @param request* @return*/@PostMapping("/wechatRefundNotify")public Map<String, String> wechatRefundNotify(HttpServletRequest request) {Map<String, String> result = new HashMap<>(2);JSONObject res = wechatPayV3Utils.getCallbackData(request);if (res == null) {result.put("code", "FAIL");result.put("message", "失败");return result;}logger.info("最终拿到的微信退款通知数据:" + res);//@TODO 处理退款成功后的业务 例如 将订单状态修改位已退款 具体参数键值可参考文档 注意!!! 微信可能会多次发送重复的通知 因此要判断业务是否已经处理过了 避免重复处理result.put("code", "SUCCESS");result.put("message", "OK");return result;}/*** 通过商户订单号查询订单在微信侧支付状态** @param out_trade_no 发起支付时创建的商户订单号* @return null代表查询失败 SUCCESS-成功 USERPAYING和ACCEPT为中间态 其他为支付失败*/public String orderQueryByOutTradeNo(String out_trade_no) {JSONObject res = wechatPayV3Utils.sendGet(String.format(WechatUrlConstants.PAY_V3_QUERY_OUT, out_trade_no, WechatConstants.WECHAT_MCH_ID));return res == null ? null : res.getString("trade_state");}/*** 查询单笔退款** @param out_refund_no 申请退款时创建的商户退款单号* @return*/public JSONObject refundQuery(String out_refund_no) {return wechatPayV3Utils.sendGet(String.format(WechatUrlConstants.PAY_V3_QUERY_REFUND, out_refund_no));}/*** 创建商户订单号* 要求 32个字符内,只能是数字、大小写字母_-|*且在同一个商户号下唯一* 组成 两位前缀 + 17位时间戳 + 9位id补零 + 4位随机数 合计32位** @param head 例如 商品-SP 订金-DJ 退款-TK 等等* @param id   用户id* @return*/public String createOrderNo(String head, Long id) {StringBuilder uid = new StringBuilder(id.toString());Date date = new Date();SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");int length = uid.length();for (int i = 0; i < 9 - length; i++) {uid.insert(0, "0");}return head + sdf.format(date) + uid + (int) ((Math.random() * 9 + 1) * 1000);}/*** 金额元转分字符串** @param cny 元* @return*/public String amount_fee(BigDecimal cny) {BigDecimal b2 = new BigDecimal(100);return cny.multiply(b2).setScale(0, RoundingMode.DOWN).toString();}}

demo中很多参数都是写死的,具体使用请根据自己的业务情况调整。

springboot利用官方SDK(wechatpay-apache-httpclient)接入微信支付V3相关推荐

  1. springboot集成微信支付V3 SDK

    微信支付开通支付方法在这里可以参考一下:申请开通微信支付教程_个人怎么申请微信支付_郑鹏川的博客-CSDN博客 因为微信支付回调需要一个外网可访问的地址,我本地调试是采用的内网穿透的方式进行调试的. ...

  2. Unity接入微信支付SDK

    最近1年转了UE开发,博客更新的比较少,技术栈宽了不少,以后有空尽量多更新,也方便总结记忆 Unity接入微信支付整个过程坑比较多,网上之前的教程要么比较老,要么比较零碎,只能东拼西凑摸索,跑通后还是 ...

  3. uni-app - 最详细 H5 网页接入微信支付功能,提供从详细的示例代码与注释(移动端网页实现微信支付能力,微信公众号前端支付 JSAPI / JS-SDK 详细示例源码)官方最新超级详细教程

    前言 关于 uni-app 项目中接入微信支付的文章鱼龙混杂,各种 JSAPI / JS-SDK 乱代码.过时.没注释.不讲流程原理,非常难用. 本文实现了 uni-app H5 移动端网页项目,实现 ...

  4. SpringBoot集成微信支付V3

    河南循中网络科技有限公司 - 精心创作,详细分解,按照步骤,均可成功! 文章目录 吐槽! 此文章包含实现了哪些接口? 学习资料 集成微信支付V3 什么是"商户证书"?什么是&quo ...

  5. 平台如何接入微信支付

    但凡系统或者平台,都离不开支付系统,除非你是免费的,hhhh.既然用到支付系统,就离不开,微信.支付宝.银行卡等支付.那今天就先说说平台怎么接入微信支付. 准备工作: 首先去微信开发平台的商户平台,注 ...

  6. Java接入微信支付超级详细教程——从入门到精通

    源码下载 源码获取邮箱:xiaoshu1024@qq.com 本文介绍了"二维码付款"的代码.其他微信支付方式的代码都在源码中. 一.准备开发所需的账号以及配置信息 解释:想要接入 ...

  7. java异步调用微信接口_微信支付V3 SDK(Java版,支持同步异步调用)

    我们在开发微信支付时,发现微信官方已经对SDK做了升级,V3版本的SDK从设计上符合RESTful规范. 我们再在开源库中寻找是否有现成de开箱即用.并且支持响应式编程的SDK版本.经过一凡寻找,令我 ...

  8. 极客日报:阿里旗下App接入微信支付;马斯克成世界首富;PostgreSQL 14 RC 1发布

    一分钟速览新闻点! 阿里回应App接入微信支付 抖音起诉知乎名誉侵权 小米上诉"小米穿戴"图形商标被驳回 拼多多.美团已支持众多主流支付渠道 清华AI学生华智冰首次露正脸唱歌 快手 ...

  9. pc网站和手机端h5网站开发接入微信支付

    有关支付类开发,现在比以前要简单很多了,微信和支付宝两大支付巨头早已经给出了非常详细的接入文档,并且迭代了好多版本,但在实际开发中其实文档的可读性还是有些磕磕绊绊的,而且也有一些坑需要注意.以微信支付 ...

最新文章

  1. UVA 10570 Meeting with Aliens
  2. ArtRage中文版
  3. 数据结构与算法笔记(三)—— 链表(单链表、循环链表、双向链表)
  4. 基于PHP的CURL快速入门
  5. javax.net.ssl.SSLException: closing inbound before receiving peer‘s close_notif---SpringCloud工作笔记111
  6. 【电脑帮助】解决Wind10系统桌面没有“我的电脑”图标的问题
  7. 设计模式精髓—封装变化
  8. C-order/Fortran-order(Row-/Column-major order)
  9. Chrome 开发工具之 Memory
  10. 机器学习基础算法28-EM算法
  11. 小米平板 计算机 连接打印机,小米米家喷墨打印机:打印方式介绍
  12. 2021-6-25 组态王与modbus rtu从机STM32精英开发板通信
  13. Mysql(2)事务
  14. ModSecurity规则
  15. L1-6 斯德哥尔摩火车上的题
  16. 苹果内存不够怎么办_手机内存清理了还是不够用?不知道这些方法,真是太可惜了...
  17. vulnhub Photographer: 1
  18. excel 分组计数
  19. mac文件反选_【PS反选键是什么?】Photoshop该如何进行反向选择?
  20. 关于大学生如何轻松找到高薪工作

热门文章

  1. 云耀服务器切换系统,云耀云服务器切换操作系统
  2. 华为编程决赛后的感想
  3. 无任何编程基础的人,该怎么入门编程?
  4. 字符串长度测量,大小比较
  5. LTE系统信息(1)-MIB
  6. Win10解决:系统管理员已阻止你运行此应用
  7. LintCode 138.子数组之和
  8. 任务调度的合理性 (25 分)
  9. 朴素贝叶斯分类器(离散型)算法实现(一)
  10. (商品评价页)商品星级评分html+css+js