注意:(appId账号)微信商户平台必须开通APP支付功能
文件 application.yml新增以下配置
# app微信支付配置 wx: appPay: # appId账号 appId: ****** # 商户号 mchId: ******** # apiV3秘钥 apiV3Key: ********* # 商户证书序列号 mchSerialNo: ********* # 商户私钥路径 privateKeyUrl: ************ # 平台证书路径 wechatPayCertificateUrl: ************ # 支付回调地址 notifyUrl: *************
<!-- 微信 --> <dependency> <groupId>com.github.binarywang</groupId> <artifactId>weixin-java-open</artifactId> <version>3.1.0</version> </dependency> <dependency> <groupId>com.github.binarywang</groupId> <artifactId>weixin-java-mp</artifactId> <version>3.1.0</version> </dependency> <dependency> <groupId>com.github.binarywang</groupId> <artifactId>weixin-java-pay</artifactId> <version>3.1.0</version> </dependency> <dependency> <groupId>com.github.wechatpay-apiv3</groupId> <artifactId>wechatpay-java</artifactId> <version>0.2.7</version> </dependency>
package com.controller.busi;
import cn.hutool.core.util.RandomUtil;
import com.alibaba.fastjson.JSON;
import com.busi.config.wxpayv3.IJPayHttpResponse;
import com.busi.config.wxpayv3.PayKit;
import com.busi.config.wxpayv3.RequestMethodEnum;
import com.busi.config.wxpayv3.WxPayApi;
import com.config.WxAppPayConfig;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
import com.wechat.pay.contrib.apache.httpclient.auth.Verifier;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;
import com.wechat.pay.contrib.apache.httpclient.cert.CertificatesManager;
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 io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.PrivateKey;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
@Api(value = "微信app支付Controller", tags = "app-支付")
@RestController
@RequestMapping("/app/pay")
@Slf4j
public class AppWxPayController {
@Value("${wx.appPay.appId}")
private String appId;
@Value("${wx.appPay.mchId}")
private String mchId;
@Value("${wx.appPay.apiV3Key}")
private String apiV3Key;
@Value("${wx.appPay.mchSerialNo}")
private String mchSerialNo;
@Value("${wx.appPay.privateKeyUrl}")
private String privateKeyUrl;
@Value("${wx.appPay.wechatPayCertificateUrl}")
private String wechatPayCertificateUrl;
@Value("${wx.appPay.notifyUrl}")
private String notifyUrl;
@ApiOperation("创建预支付订单")
@PostMapping("/doUnifiedOrder")
public Map<String, Object> WxPayApp() {
Map<String, Object> payMap = new HashMap<>();
// 调用app微信支付api入参
com.alibaba.fastjson.JSONObject json = new com.alibaba.fastjson.JSONObject();
json.put("appid", appId);
json.put("mchid", mchId);
// 商品描述
json.put("description", "测试商品");
// 订单号
String outTradeNo = generateNonceStr();
json.put("out_trade_no", outTradeNo);
// 回调url
json.put("notify_url", notifyUrl);
com.alibaba.fastjson.JSONObject amountJson = new com.alibaba.fastjson.JSONObject();
amountJson.put("total", 1);
amountJson.put("currency", "CNY");
json.put("amount", amountJson);
// 价格不能小于一分钱
com.alibaba.fastjson.JSONObject jsonObject = WxAppPayConfig.doPostWexinV3("https://api.mch.weixin.qq.com/v3/pay/transactions/app", json.toJSONString(),
privateKeyUrl, wechatPayCertificateUrl, mchId, mchSerialNo);
log.info("调用app微信支付api结果:" + jsonObject);
assert jsonObject != null;
String prepay_id = jsonObject.getString("prepay_id");
// 时间戳
long timestamp = System.currentTimeMillis() / 1000;
// 随机串
String nonceStr = UUID.randomUUID().toString().replace("-", "");
String sign = WxAppPayConfig.getSign(appId, timestamp, nonceStr, prepay_id, privateKeyUrl, wechatPayCertificateUrl);
log.info("签名:" + sign);
payMap.put("prepayid", prepay_id);
payMap.put("timestamp", String.valueOf(timestamp));
payMap.put("noncestr", nonceStr);
payMap.put("sign", sign);
payMap.put("appid", appId);
payMap.put("package", "Sign=WXPay");
payMap.put("extData", "sign");
payMap.put("partnerid", mchId);
// TODO 订单信息入库
// payMap是拉起app微信支付需要的参数
return payMap;
}
@ApiOperation("app微信支付回调")
@GetMapping("/notify")
public void notify(HttpServletRequest request) throws Exception {
log.info("收到app微信支付回调....");
// 获取报文
String body = getRequestBody(request);
// 随机串
String nonce = request.getHeader("Wechatpay-Nonce");
// 微信传递过来的签名
String signature = request.getHeader("Wechatpay-Signature");
// 证书序列号(微信平台)
String wechatPaySerial = request.getHeader("Wechatpay-Serial");
// 时间戳
String timestamp = request.getHeader("Wechatpay-Timestamp");
PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(Files.newInputStream(Paths.get(privateKeyUrl)));
// 获取证书管理器实例
CertificatesManager certificatesManager = CertificatesManager.getInstance();
// 向证书管理器增加需要自动更新平台证书的商户信息
certificatesManager.putMerchant(mchId, new WechatPay2Credentials(mchId,
new PrivateKeySigner(mchSerialNo, merchantPrivateKey)),
apiV3Key.getBytes(StandardCharsets.UTF_8));
// 从证书管理器中获取verifier
Verifier verifier = certificatesManager.getVerifier(mchId);
// 构建request,传入必要参数
NotificationRequest notificationRequest = new NotificationRequest.Builder().withSerialNumber(wechatPaySerial)
.withNonce(nonce)
.withTimestamp(timestamp)
.withSignature(signature)
.withBody(body)
.build();
NotificationHandler handler = new NotificationHandler(verifier, apiV3Key.getBytes(StandardCharsets.UTF_8));
// 验签和解析请求体
Notification notification = handler.parse(notificationRequest);
String result = notification.getDecryptData();
log.info("解密报文:" + result);
com.alibaba.fastjson.JSONObject resultJson = JSON.parseObject(result);
String trade_state = resultJson.getString("trade_state").trim();
String out_trade_no = resultJson.getString("out_trade_no").trim();
String trade_type = resultJson.getString("trade_type").trim();
log.info("微信支付交易状态码:" + trade_state);
log.info("微信支付交易订单号:" + out_trade_no);
log.info("微信支付交易类型:" + trade_type);
// TODO 支付成功,更新订单信息
if ("SUCCESS".equals(trade_state)) {
}
}
/**
* 获取随机字符串 Nonce Str
*/
public String generateNonceStr() {
StringBuilder stringBuffer = new StringBuilder();
int prefix = RandomUtil.randomInt(10000, 99999);
int suffix = RandomUtil.randomInt(10000, 99999);
Long time = System.currentTimeMillis();
return stringBuffer.append(prefix).append(time).append(suffix).toString();
}
private String getRequestBody(HttpServletRequest request) {
StringBuilder sb = new StringBuilder();
try (ServletInputStream inputStream = request.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
) {
String line;
while ((line = reader.readLine()) != null) {
sb.append(line);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
return sb.toString();
}
// 退款
public static void main(String[] args) throws Exception {
// 查询订单信息
String orderId = "94024170497607658181870";
int amount = 100;
String productName = "测试课程";
com.alibaba.fastjson2.JSONObject jsonObject = new com.alibaba.fastjson2.JSONObject();
jsonObject.put("out_trade_no", orderId);
// 退款单号
String outRefundNo = PayKit.generateStr();
jsonObject.put("out_refund_no", outRefundNo);
jsonObject.put("reason", "取消课程【" + productName + "】");
com.alibaba.fastjson2.JSONObject amountInfo = new com.alibaba.fastjson2.JSONObject();
amountInfo.put("refund", amount);
amountInfo.put("total", amount);
amountInfo.put("currency", "CNY");
jsonObject.put("amount", amountInfo);
// 退款参数
String requestBody = com.alibaba.fastjson2.JSON.toJSON(jsonObject).toString();
System.out.println("退款参数 ===" + requestBody);
IJPayHttpResponse response = WxPayApi.v3(
RequestMethodEnum.POST,
"https://api.mch.weixin.qq.com",
"/v3/refund/domestic/refunds",
"商户号",
"序列号",
null,
"D://file//project//apiclient_key.pem",
requestBody
);
String body = response.getBody();
System.out.println("退款结果:" + body);
}
}
package com.busi.config.wxpayv3;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.ContentType;
import java.io.File;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
/**
* @author dori
* @date 2024/4/17 15:58
* @description
*/
public class WxPayApi {
public static IJPayHttpResponse v3(RequestMethodEnum method, String urlPrefix, String urlSuffix, String mchId,
String serialNo, String platSerialNo, String keyPath, String body) throws Exception {
long timestamp = System.currentTimeMillis() / 1000;
String authType = AuthTypeEnum.RSA.getUrl();
String nonceStr = WxPayKit.generateStr();
return v3(method, urlPrefix, urlSuffix, mchId, serialNo, platSerialNo, keyPath, body, nonceStr, timestamp, authType, null);
}
public static IJPayHttpResponse v3(RequestMethodEnum method, String urlPrefix, String urlSuffix,
String mchId, String serialNo, String platSerialNo, String keyPath,
String body, String nonceStr, long timestamp, String authType,
File file) throws Exception {
// 构建 Authorization
String authorization = WxPayKit.buildAuthorization(method, urlSuffix, mchId, serialNo,
keyPath, body, nonceStr, timestamp, authType);
if (StrUtil.isEmpty(platSerialNo)) {
platSerialNo = serialNo;
}
if (method == RequestMethodEnum.GET) {
return get(urlPrefix.concat(urlSuffix), authorization, platSerialNo, null);
} else if (method == RequestMethodEnum.POST) {
return post(urlPrefix.concat(urlSuffix), authorization, platSerialNo, body);
} else if (method == RequestMethodEnum.DELETE) {
return delete(urlPrefix.concat(urlSuffix), authorization, platSerialNo, body);
} else if (method == RequestMethodEnum.UPLOAD) {
return upload(urlPrefix.concat(urlSuffix), authorization, platSerialNo, body, file);
} else if (method == RequestMethodEnum.PUT) {
return put(urlPrefix.concat(urlSuffix), authorization, platSerialNo, body);
}
return null;
}
/**
* @param url 请求url
* @param params 请求参数
* @return {@link String} 请求返回的结果
*/
public static String doGet(String url, Map<String, Object> params) {
return HttpKit.getDelegate().get(url, params);
}
/**
* get 请求
*
* @param url 请求url
* @param params 请求参数
* @param headers 请求头
* @return {@link IJPayHttpResponse} 请求返回的结果
*/
public static IJPayHttpResponse get(String url, Map<String, Object> params, Map<String, String> headers) {
return HttpKit.getDelegate().get(url, params, headers);
}
/**
* get 请求
*
* @param url 请求url
* @param authorization 授权信息
* @param serialNumber 公钥证书序列号
* @param params 请求参数
* @return {@link IJPayHttpResponse} 请求返回的结果
*/
public static IJPayHttpResponse get(String url, String authorization, String serialNumber, Map<String, Object> params) {
return get(url, params, getHeaders(authorization, serialNumber));
}
/**
* post 请求
*
* @param url 请求url
* @param data 请求参数
* @param headers 请求头
* @return {@link IJPayHttpResponse} 请求返回的结果
*/
public static IJPayHttpResponse post(String url, String data, Map<String, String> headers) {
return HttpKit.getDelegate().post(url, data, headers);
}
/**
* post 请求
*
* @param url 请求url
* @param authorization 授权信息
* @param serialNumber 公钥证书序列号
* @param data 请求参数
* @return {@link IJPayHttpResponse} 请求返回的结果
*/
public static IJPayHttpResponse post(String url, String authorization, String serialNumber, String data) {
return post(url, data, getHeaders(authorization, serialNumber));
}
/**
* delete 请求
*
* @param url 请求url
* @param data 请求参数
* @param headers 请求头
* @return {@link IJPayHttpResponse} 请求返回的结果
*/
public static IJPayHttpResponse delete(String url, String data, Map<String, String> headers) {
return HttpKit.getDelegate().delete(url, data, headers);
}
/**
* delete 请求
*
* @param url 请求url
* @param authorization 授权信息
* @param serialNumber 公钥证书序列号
* @param data 请求参数
* @return {@link IJPayHttpResponse} 请求返回的结果
*/
public static IJPayHttpResponse delete(String url, String authorization, String serialNumber, String data) {
return delete(url, data, getHeaders(authorization, serialNumber));
}
/**
* upload 请求
*
* @param url 请求url
* @param params 请求参数
* @param headers 请求头
* @return {@link IJPayHttpResponse} 请求返回的结果
*/
public static IJPayHttpResponse upload(String url, Map<String, Object> params, Map<String, String> headers) {
return HttpKit.getDelegate().post(url, params, headers);
}
/**
* upload 请求
*
* @param url 请求url
* @param authorization 授权信息
* @param serialNumber 公钥证书序列号
* @param data 请求参数
* @param file 上传文件
* @return {@link IJPayHttpResponse} 请求返回的结果
*/
public static IJPayHttpResponse upload(String url, String authorization, String serialNumber, String data, File file) {
Map<String, Object> paramMap = new HashMap<>(2);
paramMap.put("file", file);
paramMap.put("meta", data);
return upload(url, paramMap, getUploadHeaders(authorization, serialNumber));
}
/**
* put 请求
*
* @param url 请求url
* @param data 请求参数
* @param headers 请求头
* @return {@link IJPayHttpResponse} 请求返回的结果
*/
public static IJPayHttpResponse put(String url, String data, Map<String, String> headers) {
return HttpKit.getDelegate().put(url, data, headers);
}
/**
* put 请求
*
* @param url 请求url
* @param authorization 授权信息
* @param serialNumber 公钥证书序列号
* @param data 请求参数
* @return {@link IJPayHttpResponse} 请求返回的结果
*/
public static IJPayHttpResponse put(String url, String authorization, String serialNumber, String data) {
return put(url, data, getHeaders(authorization, serialNumber));
}
public static String doPost(String url, Map<String, String> params) {
return HttpKit.getDelegate().post(url, WxPayKit.toXml(params));
}
public static String doPostSsl(String url, Map<String, String> params, String certPath, String certPass) {
return HttpKit.getDelegate().post(url, WxPayKit.toXml(params), certPath, certPass);
}
public static String doPostSslByProtocol(String url, Map<String, String> params, String certPath, String certPass, String protocol) {
return HttpKit.getDelegate().post(url, WxPayKit.toXml(params), certPath, certPass, protocol);
}
public static String doPostSsl(String url, Map<String, String> params, InputStream certFile, String certPass) {
return HttpKit.getDelegate().post(url, WxPayKit.toXml(params), certFile, certPass);
}
public static String doPostSslByProtocol(String url, Map<String, String> params, InputStream certFile, String certPass, String protocol) {
return HttpKit.getDelegate().post(url, WxPayKit.toXml(params), certFile, certPass, protocol);
}
public static String doPostSsl(String url, Map<String, String> params, String certPath) {
if (params.isEmpty() || !params.containsKey("mch_id")) {
throw new RuntimeException("请求参数中必须包含 mch_id,如接口参考中不包 mch_id, 请使用其他同名构造方法。");
}
String certPass = params.get("mch_id");
return doPostSsl(url, params, certPath, certPass);
}
public static String doPostSslByProtocol(String url, Map<String, String> params, String certPath, String protocol) {
if (params.isEmpty() || !params.containsKey("mch_id")) {
throw new RuntimeException("请求参数中必须包含 mch_id,如接口参考中不包 mch_id, 请使用其他同名构造方法。");
}
String certPass = params.get("mch_id");
return doPostSslByProtocol(url, params, certPath, certPass, protocol);
}
public static String doPostSsl(String url, Map<String, String> params, InputStream certFile) {
if (params.isEmpty() || !params.containsKey("mch_id")) {
throw new RuntimeException("请求参数中必须包含 mch_id,如接口参考中不包 mch_id, 请使用其他同名构造方法。");
}
String certPass = params.get("mch_id");
return doPostSsl(url, params, certFile, certPass);
}
public static String doPostSslByProtocol(String url, Map<String, String> params, InputStream certFile, String protocol) {
if (params.isEmpty() || !params.containsKey("mch_id")) {
throw new RuntimeException("请求参数中必须包含 mch_id,如接口参考中不包 mch_id, 请使用其他同名构造方法。");
}
String certPass = params.get("mch_id");
return doPostSslByProtocol(url, params, certFile, certPass, protocol);
}
public static String doUploadSsl(String url, Map<String, String> params, String certPath, String certPass, String filePath) {
return HttpKit.getDelegate().upload(url, WxPayKit.toXml(params), certPath, certPass, filePath);
}
public static String doUploadSslByProtocol(String url, Map<String, String> params, String certPath, String certPass, String filePath, String protocol) {
return HttpKit.getDelegate().upload(url, WxPayKit.toXml(params), certPath, certPass, filePath, protocol);
}
public static String doUploadSsl(String url, Map<String, String> params, String certPath, String filePath) {
if (params.isEmpty() || !params.containsKey("mch_id")) {
throw new RuntimeException("请求参数中必须包含 mch_id,如接口参考中不包 mch_id, 请使用其他同名构造方法。");
}
String certPass = params.get("mch_id");
return doUploadSsl(url, params, certPath, certPass, filePath);
}
public static String doUploadSslByProtocol(String url, Map<String, String> params, String certPath, String filePath, String protocol) {
if (params.isEmpty() || !params.containsKey("mch_id")) {
throw new RuntimeException("请求参数中必须包含 mch_id,如接口参考中不包 mch_id, 请使用其他同名构造方法。");
}
String certPass = params.get("mch_id");
return doUploadSslByProtocol(url, params, certPath, certPass, filePath, protocol);
}
private static final String OS = System.getProperty("os.name") + "/" + System.getProperty("os.version");
private static final String VERSION = System.getProperty("java.version");
public static Map<String, String> getBaseHeaders(String authorization) {
String userAgent = String.format(
"WeChatPay-IJPay-HttpClient/%s (%s) Java/%s",
WxPayApi.class.getPackage().getImplementationVersion(),
OS,
VERSION == null ? "Unknown" : VERSION);
Map<String, String> headers = new HashMap<>(5);
headers.put("Accept", ContentType.JSON.toString());
headers.put("Authorization", authorization);
headers.put("User-Agent", userAgent);
return headers;
}
public static Map<String, String> getHeaders(String authorization, String serialNumber) {
Map<String, String> headers = getBaseHeaders(authorization);
headers.put("Content-Type", ContentType.JSON.toString());
if (StrUtil.isNotEmpty(serialNumber)) {
headers.put("Wechatpay-Serial", serialNumber);
}
return headers;
}
public static Map<String, String> getUploadHeaders(String authorization, String serialNumber) {
Map<String, String> headers = getBaseHeaders(authorization);
headers.put("Content-Type", "multipart/form-data;boundary=\"boundary\"");
if (StrUtil.isNotEmpty(serialNumber)) {
headers.put("Wechatpay-Serial", serialNumber);
}
return headers;
}
/**
* 构建返回参数
*
* @param response {@link IJPayHttpResponse}
* @return {@link Map}
*/
public static Map<String, Object> buildResMap(IJPayHttpResponse response) {
if (response == null) {
return null;
}
Map<String, Object> map = new HashMap<>(6);
String timestamp = response.getHeader("Wechatpay-Timestamp");
String nonceStr = response.getHeader("Wechatpay-Nonce");
String serialNo = response.getHeader("Wechatpay-Serial");
String signature = response.getHeader("Wechatpay-Signature");
String body = response.getBody();
int status = response.getStatus();
map.put("timestamp", timestamp);
map.put("nonceStr", nonceStr);
map.put("serialNumber", serialNo);
map.put("signature", signature);
map.put("body", body);
map.put("status", status);
return map;
}
}
package com.busi.config.wxpayv3;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.map.CaseInsensitiveMap;
import cn.hutool.core.util.StrUtil;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
public class IJPayHttpResponse implements Serializable {
private static final long serialVersionUID = 6089103955998013402L;
private String body;
private byte[] bodyByte;
private int status;
private Map<String, List<String>> headers;
public String getBody() {
return body;
}
public void setBody(String body) {
this.body = body;
}
public byte[] getBodyByte() {
return bodyByte;
}
public void setBodyByte(byte[] bodyByte) {
this.bodyByte = bodyByte;
}
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
public Map<String, List<String>> getHeaders() {
return headers;
}
public void setHeaders(Map<String, List<String>> headers) {
this.headers = headers;
}
public String getHeader(String name) {
List<String> values = this.headerList(name);
return CollectionUtil.isEmpty(values) ? null : values.get(0);
}
private List<String> headerList(String name) {
if (StrUtil.isBlank(name)) {
return null;
} else {
CaseInsensitiveMap<String, List<String>> headersIgnoreCase = new CaseInsensitiveMap<>(getHeaders());
return headersIgnoreCase.get(name.trim());
}
}
@Override
public String toString() {
return "IJPayHttpResponse{" +
"body='" + body + '\'' +
", status=" + status +
", headers=" + headers +
'}';
}
}
package com.busi.config.wxpayv3;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.io.resource.ClassPathResource;
import cn.hutool.core.io.resource.Resource;
import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.digest.HmacAlgorithm;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.Principal;
import java.security.PrivateKey;
import java.security.cert.*;
import java.util.*;
/**
* IJPay 工具类
*
* @author dori
*/
public class PayKit {
/**
* 对象路径前缀
*/
public static final String CLASS_PATH_PREFIX = "classpath:";
/**
* 生成16进制的 sha256 字符串
*
* @param data 数据
* @param key 密钥
* @return sha256 字符串
*/
public static String hmacSha256(String data, String key) {
return SecureUtil.hmac(HmacAlgorithm.HmacSHA256, key).digestHex(data);
}
/**
* SHA1加密文件,生成16进制SHA1字符串<br>
*
* @param dataFile 被加密文件
* @return SHA1 字符串
*/
public static String sha1(File dataFile) {
return SecureUtil.sha1(dataFile);
}
/**
* SHA1加密,生成16进制SHA1字符串<br>
*
* @param data 数据
* @return SHA1 字符串
*/
public static String sha1(InputStream data) {
return SecureUtil.sha1(data);
}
/**
* SHA1加密,生成16进制SHA1字符串<br>
*
* @param data 数据
* @return SHA1 字符串
*/
public static String sha1(String data) {
return SecureUtil.sha1(data);
}
/**
* 生成16进制 MD5 字符串
*
* @param data 数据
* @return MD5 字符串
*/
public static String md5(String data) {
return SecureUtil.md5(data);
}
/**
* AES 解密
*
* @param base64Data 需要解密的数据
* @param key 密钥
* @return 解密后的数据
*/
public static String decryptData(String base64Data, String key) {
return SecureUtil.aes(md5(key).toLowerCase().getBytes()).decryptStr(base64Data);
}
/**
* AES 加密
*
* @param data 需要加密的数据
* @param key 密钥
* @return 加密后的数据
*/
public static String encryptData(String data, String key) {
return SecureUtil.aes(md5(key).toLowerCase().getBytes()).encryptBase64(data.getBytes());
}
/**
* 简化的UUID,去掉了横线,使用性能更好的 ThreadLocalRandom 生成UUID
*
* @return 简化的 UUID,去掉了横线
*/
public static String generateStr() {
return IdUtil.fastSimpleUUID();
}
/**
* 雪花算法
*
* @param workerId 终端ID
* @param dataCenterId 数据中心ID
* @return {@link Snowflake}
*/
public static Snowflake getSnowflake(long workerId, long dataCenterId) {
return IdUtil.getSnowflake(workerId, dataCenterId);
}
/**
* 把所有元素排序
*
* @param params 需要排序并参与字符拼接的参数组
* @return 拼接后字符串
*/
public static String createLinkString(Map<String, String> params) {
return createLinkString(params, false);
}
/**
* @param params 需要排序并参与字符拼接的参数组
* @param encode 是否进行URLEncoder
* @return 拼接后字符串
*/
public static String createLinkString(Map<String, String> params, boolean encode) {
return createLinkString(params, "&", encode);
}
/**
* @param params 需要排序并参与字符拼接的参数组
* @param connStr 连接符号
* @param encode 是否进行URLEncoder
* @return 拼接后字符串
*/
public static String createLinkString(Map<String, String> params, String connStr, boolean encode) {
return createLinkString(params, connStr, encode, false);
}
public static String createLinkString(Map<String, String> params, String connStr, boolean encode, boolean quotes) {
List<String> keys = new ArrayList<>(params.keySet());
Collections.sort(keys);
StringBuilder content = new StringBuilder();
for (int i = 0; i < keys.size(); i++) {
String key = keys.get(i);
String value = params.get(key);
// 拼接时,不包括最后一个&字符
if (i == keys.size() - 1) {
if (quotes) {
content.append(key).append("=").append('"').append(encode ? urlEncode(value) : value).append('"');
} else {
content.append(key).append("=").append(encode ? urlEncode(value) : value);
}
} else {
if (quotes) {
content.append(key).append("=").append('"').append(encode ? urlEncode(value) : value).append('"').append(connStr);
} else {
content.append(key).append("=").append(encode ? urlEncode(value) : value).append(connStr);
}
}
}
return content.toString();
}
/**
* URL 编码
*
* @param src 需要编码的字符串
* @return 编码后的字符串
*/
public static String urlEncode(String src) {
try {
return URLEncoder.encode(src, CharsetUtil.UTF_8).replace("+", "%20");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
return null;
}
}
/**
* 遍历 Map 并构建 xml 数据
*
* @param params 需要遍历的 Map
* @param prefix xml 前缀
* @param suffix xml 后缀
* @return xml 字符串
*/
public static StringBuffer forEachMap(Map<String, String> params, String prefix, String suffix) {
StringBuffer xml = new StringBuffer();
if (StrUtil.isNotEmpty(prefix)) {
xml.append(prefix);
}
for (Map.Entry<String, String> entry : params.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
// 略过空值
if (StrUtil.isEmpty(value)) {
continue;
}
xml.append("<").append(key).append(">");
xml.append(entry.getValue());
xml.append("</").append(key).append(">");
}
if (StrUtil.isNotEmpty(suffix)) {
xml.append(suffix);
}
return xml;
}
/**
* 微信下单 map to xml
*
* @param params Map 参数
* @return xml 字符串
*/
public static String toXml(Map<String, String> params) {
StringBuffer xml = forEachMap(params, "<xml>", "</xml>");
return xml.toString();
}
/**
* 针对支付的 xml,没有嵌套节点的简单处理
*
* @param xmlStr xml 字符串
* @return 转化后的 Map
*/
public static Map<String, String> xmlToMap(String xmlStr) {
XmlHelper xmlHelper = XmlHelper.of(xmlStr);
return xmlHelper.toMap();
}
/**
* 构造签名串
*
* @param method {@link RequestMethodEnum} GET,POST,PUT等
* @param url 请求接口 /v3/certificates
* @param timestamp 获取发起请求时的系统当前时间戳
* @param nonceStr 随机字符串
* @param body 请求报文主体
* @return 待签名字符串
*/
public static String buildSignMessage(RequestMethodEnum method, String url, long timestamp, String nonceStr, String body) {
ArrayList<String> arrayList = new ArrayList<>();
arrayList.add(method.toString());
arrayList.add(url);
arrayList.add(String.valueOf(timestamp));
arrayList.add(nonceStr);
arrayList.add(body);
return buildSignMessage(arrayList);
}
/**
* 构造签名串
*
* @param timestamp 应答时间戳
* @param nonceStr 应答随机串
* @param body 应答报文主体
* @return 应答待签名字符串
*/
public static String buildSignMessage(String timestamp, String nonceStr, String body) {
ArrayList<String> arrayList = new ArrayList<>();
arrayList.add(timestamp);
arrayList.add(nonceStr);
arrayList.add(body);
return buildSignMessage(arrayList);
}
/**
* 构造签名串
*
* @param signMessage 待签名的参数
* @return 构造后带待签名串
*/
public static String buildSignMessage(ArrayList<String> signMessage) {
if (signMessage == null || signMessage.size() <= 0) {
return null;
}
StringBuilder sbf = new StringBuilder();
for (String str : signMessage) {
sbf.append(str).append("\n");
}
return sbf.toString();
}
/**
* v3 接口创建签名
*
* @param signMessage 待签名的参数
* @param keyPath key.pem 证书路径
* @return 生成 v3 签名
* @throws Exception 异常信息
*/
public static String createSign(ArrayList<String> signMessage, String keyPath) throws Exception {
return createSign(buildSignMessage(signMessage), keyPath);
}
/**
* v3 接口创建签名
*
* @param signMessage 待签名的参数
* @param privateKey 商户私钥
* @return 生成 v3 签名
* @throws Exception 异常信息
*/
public static String createSign(ArrayList<String> signMessage, PrivateKey privateKey) throws Exception {
return createSign(buildSignMessage(signMessage), privateKey);
}
/**
* v3 接口创建签名
*
* @param signMessage 待签名的参数
* @param keyPath key.pem 证书路径
* @return 生成 v3 签名
* @throws Exception 异常信息
*/
public static String createSign(String signMessage, String keyPath) throws Exception {
if (StrUtil.isEmpty(signMessage)) {
return null;
}
// 获取商户私钥
PrivateKey privateKey = PayKit.getPrivateKey(keyPath);
// 生成签名
return RsaKit.encryptByPrivateKey(signMessage, privateKey);
}
/**
* v3 接口创建签名
*
* @param signMessage 待签名的参数
* @param privateKey 商户私钥
* @return 生成 v3 签名
* @throws Exception 异常信息
*/
public static String createSign(String signMessage, PrivateKey privateKey) throws Exception {
if (StrUtil.isEmpty(signMessage)) {
return null;
}
// 生成签名
return RsaKit.encryptByPrivateKey(signMessage, privateKey);
}
/**
* 获取授权认证信息
*
* @param mchId 商户号
* @param serialNo 商户API证书序列号
* @param nonceStr 请求随机串
* @param timestamp 时间戳
* @param signature 签名值
* @param authType 认证类型
* @return 请求头 Authorization
*/
public static String getAuthorization(String mchId, String serialNo, String nonceStr, String timestamp, String signature, String authType) {
Map<String, String> params = new HashMap<>(5);
params.put("mchid", mchId);
params.put("serial_no", serialNo);
params.put("nonce_str", nonceStr);
params.put("timestamp", timestamp);
params.put("signature", signature);
return authType.concat(" ").concat(createLinkString(params, ",", false, true));
}
/**
* 获取商户私钥
*
* @param keyPath 商户私钥证书路径
* @return {@link String} 商户私钥
* @throws Exception 异常信息
*/
public static String getPrivateKeyStr(String keyPath) throws Exception {
return RsaKit.getPrivateKeyStr(getPrivateKey(keyPath));
}
/**
* 获取商户私钥
*
* @param keyPath 商户私钥证书路径
* @return {@link PrivateKey} 商户私钥
* @throws Exception 异常信息
*/
public static PrivateKey getPrivateKey(String keyPath) throws Exception {
String originalKey = getCertFileContent(keyPath);
if (StrUtil.isEmpty(originalKey)) {
throw new RuntimeException("商户私钥证书获取失败");
}
return getPrivateKeyByKeyContent(originalKey);
}
/**
* 获取商户私钥
*
* @param originalKey 私钥文本内容
* @return {@link PrivateKey} 商户私钥
* @throws Exception 异常信息
*/
public static PrivateKey getPrivateKeyByKeyContent(String originalKey) throws Exception {
String privateKey = originalKey
.replace("-----BEGIN PRIVATE KEY-----", "")
.replace("-----END PRIVATE KEY-----", "")
.replaceAll("\\s+", "");
return RsaKit.loadPrivateKey(privateKey);
}
/**
* 获取证书
*
* @param inputStream 证书文件
* @return {@link X509Certificate} 获取证书
*/
public static X509Certificate getCertificate(InputStream inputStream) {
try {
CertificateFactory cf = CertificateFactory.getInstance("X509");
X509Certificate cert = (X509Certificate) cf.generateCertificate(inputStream);
cert.checkValidity();
return cert;
} catch (CertificateExpiredException e) {
throw new RuntimeException("证书已过期", e);
} catch (CertificateNotYetValidException e) {
throw new RuntimeException("证书尚未生效", e);
} catch (CertificateException e) {
throw new RuntimeException("无效的证书", e);
}
}
/**
* 获取证书
*
* @param path 证书路径,支持相对路径以及绝得路径
* @return {@link X509Certificate} 获取证书
*/
public static X509Certificate getCertificate(String path) {
if (StrUtil.isEmpty(path)) {
return null;
}
InputStream inputStream;
try {
inputStream = getCertFileInputStream(path);
} catch (IOException e) {
throw new RuntimeException("请检查证书路径是否正确", e);
}
return getCertificate(inputStream);
}
/**
* 获取证书详细信息
*
* @param certificate {@link X509Certificate} 证书
* @return {@link CertificateModel} 获取证书详细信息
*/
public static CertificateModel getCertificateInfo(X509Certificate certificate) {
if (null == certificate) {
return null;
}
CertificateModel model = new CertificateModel();
model.setItself(certificate);
model.setIssuerDn(certificate.getIssuerDN());
model.setSubjectDn(certificate.getSubjectDN());
model.setVersion(certificate.getVersion());
model.setNotBefore(certificate.getNotBefore());
model.setNotAfter(certificate.getNotAfter());
model.setSerialNumber(certificate.getSerialNumber().toString(16));
return model;
}
/**
* 获取证书详细信息
*
* @param path 证书路径,支持相对路径以及绝得路径
* @return {@link CertificateModel} 获取证书详细信息
*/
public static CertificateModel getCertificateInfo(String path) {
X509Certificate certificate = getCertificate(path);
return getCertificateInfo(certificate);
}
/**
* 检查证书是否可用
*
* @param model {@link CertificateModel} 证书详细 model
* @param mchId 商户号
* @param offsetDay 偏移天数,正数向未来偏移,负数向历史偏移
* @return true 有效 false 无效
*/
public static boolean checkCertificateIsValid(CertificateModel model, String mchId, int offsetDay) {
if (null == model) {
return false;
}
Date notAfter = model.getNotAfter();
if (null == notAfter) {
return false;
}
// 证书颁发者
Principal issuerDn = model.getIssuerDn();
if (null == issuerDn || !issuerDn.getName().contains(IJPayConstants.ISSUER)) {
return false;
}
// 证书CN字段
if (StrUtil.isNotEmpty(mchId)) {
Principal subjectDn = model.getSubjectDn();
if (null == subjectDn || !subjectDn.getName().contains(IJPayConstants.CN.concat(mchId.trim()))) {
return false;
}
}
// 证书序列号固定40字节的字符串
String serialNumber = model.getSerialNumber();
if (StrUtil.isEmpty(serialNumber) || serialNumber.length() != IJPayConstants.SERIAL_NUMBER_LENGTH) {
return false;
}
// 偏移后的时间
DateTime dateTime = DateUtil.offsetDay(notAfter, offsetDay);
DateTime now = DateUtil.date();
int compare = DateUtil.compare(dateTime, now);
return compare >= 0;
}
/**
* 检查证书是否可用
*
* @param certificate {@link X509Certificate} 证书
* @param mchId 商户号
* @param offsetDay 偏移天数,正数向未来偏移,负数向历史偏移
* @return true 有效 false 无效
*/
public static boolean checkCertificateIsValid(X509Certificate certificate, String mchId, int offsetDay) {
if (null == certificate) {
return false;
}
CertificateModel model = getCertificateInfo(certificate);
return checkCertificateIsValid(model, mchId, offsetDay);
}
/**
* 检查证书是否可用
*
* @param path 证书路径,支持相对路径以及绝得路径
* @param mchId 商户号
* @param offsetDay 偏移天数,正数向未来偏移,负数向历史偏移
* @return true 有效 false 无效
*/
public static boolean checkCertificateIsValid(String path, String mchId, int offsetDay) {
return checkCertificateIsValid(getCertificateInfo(path), mchId, offsetDay);
}
/**
* 公钥加密
*
* @param data 待加密数据
* @param certificate 平台公钥证书
* @return 加密后的数据
* @throws Exception 异常信息
*/
public static String rsaEncryptOAEP(String data, X509Certificate certificate) throws Exception {
try {
Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding");
cipher.init(Cipher.ENCRYPT_MODE, certificate.getPublicKey());
byte[] dataByte = data.getBytes(StandardCharsets.UTF_8);
byte[] cipherData = cipher.doFinal(dataByte);
return Base64.encode(cipherData);
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
throw new RuntimeException("当前Java环境不支持RSA v1.5/OAEP", e);
} catch (InvalidKeyException e) {
throw new IllegalArgumentException("无效的证书", e);
} catch (IllegalBlockSizeException | BadPaddingException e) {
throw new IllegalBlockSizeException("加密原串的长度不能超过214字节");
}
}
/**
* 私钥解密
*
* @param cipherText 加密字符
* @param privateKey 私钥
* @return 解密后的数据
* @throws Exception 异常信息
*/
public static String rsaDecryptOAEP(String cipherText, PrivateKey privateKey) throws Exception {
try {
Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] data = Base64.decode(cipherText);
return new String(cipher.doFinal(data), StandardCharsets.UTF_8);
} catch (NoSuchPaddingException | NoSuchAlgorithmException e) {
throw new RuntimeException("当前Java环境不支持RSA v1.5/OAEP", e);
} catch (InvalidKeyException e) {
throw new IllegalArgumentException("无效的私钥", e);
} catch (BadPaddingException | IllegalBlockSizeException e) {
throw new BadPaddingException("解密失败");
}
}
/**
* 传入 classPath 静态资源路径返回文件输入流
*
* @param classPath 静态资源路径
* @return InputStream
*/
public static InputStream getFileToStream(String classPath) {
Resource resource = new ClassPathResource(classPath);
return resource.getStream();
}
/**
* 传入 classPath 静态资源路径返回绝对路径
*
* @param classPath 静态资源路径
* @return 绝对路径
*/
public static String getAbsolutePath(String classPath) {
return new ClassPathResource(classPath).getAbsolutePath();
}
/**
* 通过路径获取证书文件的输入流
*
* @param path 文件路径
* @return 文件流
* @throws IOException 异常信息
*/
public static InputStream getCertFileInputStream(String path) throws IOException {
if (StrUtil.isBlank(path)) {
return null;
}
// 绝对地址
File file = new File(path);
if (file.exists()) {
return Files.newInputStream(file.toPath());
}
// 相对地址
return getFileToStream(path);
}
/**
* 通过路径获取证书文件的内容
*
* @param path 文件路径
* @return 文件内容
*/
public static String getCertFileContent(String path) throws IOException {
InputStream certFileInputStream = getCertFileInputStream(path);
return IoUtil.read(certFileInputStream, StandardCharsets.UTF_8);
}
/**
* 获取文件真实路径
*
* @param path 文件地址
* @return 返回文件真实路径
*/
public static String getFilePath(String path) {
if (StrUtil.startWith(path, CLASS_PATH_PREFIX)) {
return getAbsolutePath(path);
} else {
return path;
}
}
}
package com.busi.config.wxpayv3;
/**
* HTTP 请求的方法
*
* @author dori
public enum RequestMethodEnum {
/**
* 上传实质是 post 请求
*/
UPLOAD("POST"),
/**
* post 请求
*/
POST("POST"),
/**
* get 请求
*/
GET("GET"),
/**
* put 请求
*/
PUT("PUT"),
/**
* delete 请求
*/
DELETE("DELETE"),
/**
* options 请求
*/
OPTIONS("OPTIONS"),
/**
* head 请求
*/
HEAD("HEAD"),
/**
* trace 请求
*/
TRACE("TRACE"),
/**
* connect 请求
*/
CONNECT("CONNECT"),
/**
* PATCH 请求
*/
PATCH("PATCH"),
;
private final String method;
RequestMethodEnum(String method) {
this.method = method;
}
@Override
public String toString() {
return this.method;
}
}
package com.config;
import com.alibaba.fastjson.JSONObject;
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import lombok.Data;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.springframework.stereotype.Component;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.nio.charset.StandardCharsets;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Base64;
@Component
@Data
public class WxAppPayConfig {
private static CloseableHttpClient httpClient;
public static void setup(String privateKeyUrl, String wechatPayCertificateUrl, String mchId, String mchSerialNo) {
// PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(privateKey);
PrivateKey merchantPrivateKey = null;
X509Certificate wechatPayCertificate = null;
try {
merchantPrivateKey = PemUtil.loadPrivateKey(
new FileInputStream(privateKeyUrl));
wechatPayCertificate = PemUtil.loadCertificate(
new FileInputStream(wechatPayCertificateUrl));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
ArrayList<X509Certificate> listCertificates = new ArrayList<>();
listCertificates.add(wechatPayCertificate);
WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
.withMerchant(mchId, mchSerialNo, merchantPrivateKey)
.withWechatPay(listCertificates);
httpClient = builder.build();
}
public static JSONObject doPostWexinV3(String url, String body, String privateKeyUrl, String wechatPayCertificateUrl, String mchId, String mchSerialNo) {
if (httpClient == null) {
setup(privateKeyUrl, wechatPayCertificateUrl, mchId, mchSerialNo);
}
HttpPost httpPost = new HttpPost(url);
httpPost.addHeader("Content-Type", "application/json;chartset=utf-8");
httpPost.addHeader("Accept", "application/json");
try {
if (body == null) {
throw new IllegalArgumentException("data参数不能为空");
}
StringEntity stringEntity = new StringEntity(body, "utf-8");
httpPost.setEntity(stringEntity);
// 直接执行execute方法,官方会自动处理签名和验签,并进行证书自动更新
HttpResponse httpResponse = httpClient.execute(httpPost);
HttpEntity httpEntity = httpResponse.getEntity();
if (httpResponse.getStatusLine().getStatusCode() == 200) {
String jsonResult = EntityUtils.toString(httpEntity);
return JSONObject.parseObject(jsonResult);
} else {
System.err.println("微信支付错误信息" + EntityUtils.toString(httpEntity));
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
// 获取签名
public static String getSign(String appId, long timestamp, String nonceStr, String pack, String privateKeyUrl, String wechatPayCertificateUrl) {
String message = buildMessage(appId, timestamp, nonceStr, pack);
String paySign = null;
try {
paySign = sign(message.getBytes(StandardCharsets.UTF_8), privateKeyUrl, wechatPayCertificateUrl);
} catch (Exception e) {
e.printStackTrace();
}
return paySign;
}
private static String buildMessage(String appId, long timestamp, String nonceStr, String pack) {
return appId + "\n"
+ timestamp + "\n"
+ nonceStr + "\n"
+ pack + "\n";
}
private static String sign(byte[] message, String privateKeyUrl, String wechatPayCertificateUrl) throws Exception {
PrivateKey merchantPrivateKey = null;
X509Certificate wechatPayCertificate = null;
try {
merchantPrivateKey = PemUtil.loadPrivateKey(
new FileInputStream(privateKeyUrl));
wechatPayCertificate = PemUtil.loadCertificate(
new FileInputStream(wechatPayCertificateUrl));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
Signature sign = Signature.getInstance("SHA256withRSA");
// 这里需要一个PrivateKey类型的参数,就是商户的私钥。
sign.initSign(merchantPrivateKey);
sign.update(message);
return Base64.getEncoder().encodeToString(sign.sign());
}
}
文章浏览阅读1.6k次。安装配置gi、安装数据库软件、dbca建库见下:http://blog.csdn.net/kadwf123/article/details/784299611、检查集群节点及状态:[root@rac2 ~]# olsnodes -srac1 Activerac2 Activerac3 Activerac4 Active[root@rac2 ~]_12c查看crs状态
文章浏览阅读1.3w次,点赞45次,收藏99次。我个人用的是anaconda3的一个python集成环境,自带jupyter notebook,但在我打开jupyter notebook界面后,却找不到对应的虚拟环境,原来是jupyter notebook只是通用于下载anaconda时自带的环境,其他环境要想使用必须手动下载一些库:1.首先进入到自己创建的虚拟环境(pytorch是虚拟环境的名字)activate pytorch2.在该环境下下载这个库conda install ipykernelconda install nb__jupyter没有pytorch环境
文章浏览阅读5.2k次,点赞19次,收藏28次。选择scoop纯属意外,也是无奈,因为电脑用户被锁了管理员权限,所有exe安装程序都无法安装,只可以用绿色软件,最后被我发现scoop,省去了到处下载XXX绿色版的烦恼,当然scoop里需要管理员权限的软件也跟我无缘了(譬如everything)。推荐添加dorado这个bucket镜像,里面很多中文软件,但是部分国外的软件下载地址在github,可能无法下载。以上两个是官方bucket的国内镜像,所有软件建议优先从这里下载。上面可以看到很多bucket以及软件数。如果官网登陆不了可以试一下以下方式。_scoop-cn
文章浏览阅读4.5k次,点赞2次,收藏3次。首先要有一个color-picker组件 <el-color-picker v-model="headcolor"></el-color-picker>在data里面data() { return {headcolor: ’ #278add ’ //这里可以选择一个默认的颜色} }然后在你想要改变颜色的地方用v-bind绑定就好了,例如:这里的:sty..._vue el-color-picker
文章浏览阅读640次。基于芯片日益增长的问题,所以内核开发者们引入了新的方法,就是在内核中只保留函数,而数据则不包含,由用户(应用程序员)自己把数据按照规定的格式编写,并放在约定的地方,为了不占用过多的内存,还要求数据以根精简的方式编写。boot启动时,传参给内核,告诉内核设备树文件和kernel的位置,内核启动时根据地址去找到设备树文件,再利用专用的编译器去反编译dtb文件,将dtb还原成数据结构,以供驱动的函数去调用。firmware是三星的一个固件的设备信息,因为找不到固件,所以内核启动不成功。_exynos 4412 刷机
文章浏览阅读2w次,点赞24次,收藏42次。Linux系统配置jdkLinux学习教程,Linux入门教程(超详细)_linux配置jdk
文章浏览阅读3.3k次,点赞5次,收藏19次。xlabel('\delta');ylabel('AUC');具体符号的对照表参照下图:_matlab微米怎么输入
文章浏览阅读119次。顺序读写指的是按照文件中数据的顺序进行读取或写入。对于文本文件,可以使用fgets、fputs、fscanf、fprintf等函数进行顺序读写。在C语言中,对文件的操作通常涉及文件的打开、读写以及关闭。文件的打开使用fopen函数,而关闭则使用fclose函数。在C语言中,可以使用fread和fwrite函数进行二进制读写。 Biaoge 于2024-03-09 23:51发布 阅读量:7 ️文章类型:【 C语言程序设计 】在C语言中,用于打开文件的函数是____,用于关闭文件的函数是____。
文章浏览阅读3.4k次,点赞2次,收藏13次。跟随鼠标移动的粒子以grid(SOP)为partical(SOP)的资源模板,调整后连接【Geo组合+point spirit(MAT)】,在连接【feedback组合】适当调整。影响粒子动态的节点【metaball(SOP)+force(SOP)】添加mouse in(CHOP)鼠标位置到metaball的坐标,实现鼠标影响。..._touchdesigner怎么让一个模型跟着鼠标移动
文章浏览阅读178次。项目运行环境配置:Jdk1.8 + Tomcat7.0 + Mysql + HBuilderX(Webstorm也行)+ Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。项目技术:Springboot + mybatis + Maven +mysql5.7或8.0+html+css+js等等组成,B/S模式 + Maven管理等等。环境需要1.运行环境:最好是java jdk 1.8,我们在这个平台上运行的。其他版本理论上也可以。_基于java技术的停车场管理系统实现与设计
文章浏览阅读3.5k次。前言对于MediaPlayer播放器的源码分析内容相对来说比较多,会从Java-&amp;gt;Jni-&amp;gt;C/C++慢慢分析,后面会慢慢更新。另外,博客只作为自己学习记录的一种方式,对于其他的不过多的评论。MediaPlayerDemopublic class MainActivity extends AppCompatActivity implements SurfaceHolder.Cal..._android多媒体播放源码分析 时序图
文章浏览阅读2.4k次,点赞41次,收藏13次。java 数据结构与算法 ——快速排序法_快速排序法