binary = Base64.getEncoder().encodeToString(bytes).trim(); 提示找不到包解决方法

本文介绍了解决在不同JDK版本中遇到的Base64编码问题的方法,提供了使用Apache Commons Codec库进行Base64编码的示例代码,并讨论了IDEA中JDK版本设置的影响。

sun.*的包换成 

import org.apache.commons.codec.binary.Base64;

可以解决

 

BASE64Encoder encoder = new BASE64Encoder();
        return encoder.encode(outputStream.toByteArray()).replaceAll("\n","");

 

也是一样

 

或者看看idea的jdk版本是不是在jdk11或者jdk11以上

import cn.zhangsan.tools.enums.Algorithm;import com.google.common.collect.Maps;import org.bouncycastle.jce.provider.BouncyCastleProvider;import org.bouncycastle.util.encoders.Hex;import java.net.MalformedURLException;import java.net.URL;import java.security.MessageDigest;import java.security.NoSuchAlgorithmException;import java.security.NoSuchProviderException;import java.security.Security;import java.util.Base64;import java.util.Map;/** * @ClassName HashGeneratorUtils * @Description TODO * @Author ZhangSan_Plus * @Date 2024/7/29 20:04 * @Version 1.0 **/public class HashGeneratorUtils { static Map<Object, String> map = Maps.newLinkedHashMap(); static { Security.addProvider(new BouncyCastleProvider()); } public static void main(String[] args) { String input = "张三"; try { Map<Object, String> hashText = getHashText(input, 4); hashText.forEach((k, v) -> System.out.println(k + ":" + v)); } catch (NoSuchAlgorithmException e) { throw new RuntimeException(e); } } public static String extractDomain(String urlString) { try { URL url = new URL(urlString); return url.getHost(); } catch (MalformedURLException e) { e.printStackTrace(); return null; } } private static Map<Object, String> getHashText(String text, int digest) throws NoSuchAlgorithmException { map.put(Algorithm.MD5, encryption(md5(text), digest)); map.put(Algorithm.SHA1, encryption(sha1(text), digest)); map.put(Algorithm.SHA256, encryption(sha224(text), digest)); map.put(Algorithm.SHA224, encryption(sha256(text), digest)); map.put(Algorithm.SHA512, encryption(sha384(text), digest)); map.put(Algorithm.SHA384, encryption(sha512(text), digest)); map.put(Algorithm.SHA3, encryption(sha3_256(text), digest)); map.put(Algorithm.RIPEMD160, encryption(ripemd160(text), digest)); return map; } private static byte[] md5(String input) throws NoSuchAlgorithmException { MessageDigest digest = MessageDigest.getInstance("MD5"); return digest.digest(input.getBytes()); } private static byte[] sha1(String input) throws NoSuchAlgorithmException { MessageDigest digest = MessageDigest.getInstance("SHA-1"); return digest.digest(input.getBytes()); } private static byte[] sha224(String input) throws NoSuchAlgorithmException { MessageDigest digest = MessageDigest.getInstance("SHA-224"); return digest.digest(input.getBytes()); } private static byte[] sha256(String input) throws NoSuchAlgorithmException { MessageDigest digest = MessageDigest.getInstance("SHA-256"); return digest.digest(input.getBytes()); } private static byte[] sha384(String input) throws NoSuchAlgorithmException { MessageDigest digest = MessageDigest.getInstance("SHA-384"); return digest.digest(input.getBytes()); } private static byte[] sha512(String input) throws NoSuchAlgorithmException { MessageDigest digest = MessageDigest.getInstance("SHA-512"); return digest.digest(input.getBytes()); } private static byte[] sha3_256(String input) throws NoSuchAlgorithmException { MessageDigest digest = null; try { digest = MessageDigest.getInstance("SHA3-256", "BC"); } catch (NoSuchProviderException e) { throw new RuntimeException(e); } return digest.digest(input.getBytes()); } private static byte[] ripemd160(String input) throws NoSuchAlgorithmException { MessageDigest digest = MessageDigest.getInstance("RIPEMD160"); return digest.digest(input.getBytes()); } private static String toHexString(byte[] bytes) { return Hex.toHexString(bytes); } private static String toBase64(byte[] bytes) { return Base64.getEncoder().encodeToString(bytes); } private static String toBase64Url(byte[] bytes) { return Base64.getUrlEncoder().withoutPadding().encodeToString(bytes); } private static String toBinary(byte[] bytes) { StringBuilder binaryString = new StringBuilder(); for (byte b : bytes) { for (int i = 7; i >= 0; i--) { binaryString.append(((b >> i) & 1) == 1 ? '1' : '0'); } binaryString.append(""); } return binaryString.toString().trim(); } private static String encryption(byte[] bytes, int encoding) { switch (encoding) { case 1: return toHexString(bytes); case 2: return toBinary(bytes); case 3: return toBase64(bytes); case 4: return toBase64Url(bytes); default: return ""; } }}
03-31
/** * LLM读取文件内容 * * @param file * @return */ public List<String> llmReadFile(MultipartFile file) { List<String> results = new ArrayList<>(); //获取文件类型 String fileName = file.getOriginalFilename().toLowerCase(); //判断文件是否存在 if (fileName == null) { results.add("文件对象为空或无效"); return results; } try (InputStream is = file.getInputStream()) { //LLM识别PDF文件 if (fileName.endsWith(".pdf")) { try (PDDocument pdfDoc = PDDocument.load(is)) { PDFRenderer pdfRenderer = new PDFRenderer(pdfDoc); int totalPages = pdfDoc.getNumberOfPages(); for (int i = 0; i < totalPages; i++) { BufferedImage pdfImage = pdfRenderer.renderImageWithDPI(i, 200F); processImage(results, pdfImage); } } //LLM识别图片 } else if (fileName.endsWith(".png") || fileName.endsWith(".jpg")) { BufferedImage image = ImageIO.read(is); BufferedImage resized = resizeImage(image, 40); processImage(results, resized); } else { results.add("不支持的文件格式: " + fileName); } } catch (IOException e) { results.add("读取文件失败: " + e.getMessage()); } System.out.println("识别内容:" + results); return results; } /** * LLM大模型请求 输出内容 * * @param results * @param image * @throws IOException */ private void processImage(List<String> results, BufferedImage image) throws IOException { BufferedImage processedImage = sharpenImage(image); ByteArrayOutputStream baos = new ByteArrayOutputStream(); ImageIO.write(processedImage, "png", baos); String base64Image = Base64.getEncoder().encodeToString(baos.toByteArray()); String dataUrl = "data:image/png;base64," + base64Image; String promptText = " 请精准识别图片中的内容,不要思考和推理,只需要输出图中的内容。ADI识别输出ADI禁止改ADL,禁止缺少图片内容 比如:SHA87800002,禁止修改 单词如Luxsan 禁止改成Luksan。禁止缺少Delivery Date内容,日期内容读取正确 禁止缺少头部的Tel的内容,禁止缺少符号"; // 注意字母 I 和 l 的区别 , Media media = Media.builder() .mimeType(MimeTypeUtils.IMAGE_PNG) .mimeType(MimeTypeUtils.IMAGE_JPEG) .data(dataUrl) .build(); UserMessage userMessage = new UserMessage(promptText, media); String content = chatModel.call(new Prompt(userMessage)).toString(); results.add(content); } //锐化 private BufferedImage sharpenImage(BufferedImage original) { float[] sharpenMatrix = { 0, -1, 0, -1, 5, -1, 0, -1, 0 }; BufferedImageOp sharpenOp = new ConvolveOp(new Kernel(3, 3, sharpenMatrix), ConvolveOp.EDGE_NO_OP, null); return sharpenOp.filter(original, null); } //强化图片 private BufferedImage resizeImage(BufferedImage originalImage, int targetDPI) { int width = originalImage.getWidth(); int height = originalImage.getHeight(); double scale = (double) targetDPI / 96; int newWidth = (int) (width * scale); int newHeight = (int) (height * scale); BufferedImage resizedImage = new BufferedImage(newWidth, newHeight, originalImage.getType()); java.awt.Graphics2D g2d = resizedImage.createGraphics(); g2d.drawImage(originalImage, 0, 0, newWidth, newHeight, null); g2d.dispose(); return resizedImage; } /** * LLM识别文件内容比较校验 * * @param file * @param expectedConfig * @return */ public List<ValidationResult> validateImagePrecisely(MultipartFile file, JsonNode expectedConfig) { try { List<String> aiModelResults = llmReadFile(file); List<ValidationResult> results = new ArrayList<>(); JsonNode dataNode = expectedConfig.isArray() && expectedConfig.size() > 0 ? expectedConfig.get(0) : expectedConfig; dataNode.fields().forEachRemaining(entry -> { String field = entry.getKey(); String expectedValue = entry.getValue().asText(); String expectedValueLower = expectedValue.toLowerCase().replaceAll("\\s", "").trim(); boolean found = false; for (String aiModelResult : aiModelResults) { String aiModelResultLower = aiModelResult.toLowerCase().replaceAll("\\s", "").trim(); if (aiModelResultLower.contains(expectedValueLower)) { found = true; break; } } if (found) { log.info("字段对比成功:{}", expectedValue); } else { log.info("字段对比失败:{}", expectedValue); } results.add(new ValidationResult( "FIELD", field, expectedValue, found ? "Found" : "Not Found", found )); }); return results; } catch (Exception e) { log.error("验证过程异常: {}", e.getMessage(), e); return Collections.singletonList( new ValidationResult("SYSTEM", "EXCEPTION", "正常处理", "系统错误: " + e, false) ); } } 还能不能提升他的识别速度 整体的流程速度更快
10-14
org.springframework.web.multipart.MultipartException: Current request is not a multipart request 报错 @PostMapping(value = "/validate", produces = {MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE}) public List<ValidationResult> validateDocument( @RequestPart("file") MultipartFile file, @RequestPart("expectedConfig") JsonNode expectedConfig) { return param.validateImagePrecisely(file, expectedConfig); } 我的接口 private final ObjectMapper objectMapper = new ObjectMapper(); private static final String BASE_URL = "http://10.191.39.243:8000/v1"; private static final String API_KEY = "Luxsan20250701"; private static final String MODEL_NAME = "/models/Qwen2.5-VL-72B-Instruct"; // 主验证方法 public List<ValidationResult> validateImagePrecisely(MultipartFile file, JsonNode expectedConfig) { try { String imageUrl = convertToBase64(file); // 1. 调用大模型API获取OCR结果 String modelResponse =recognizeTextFromImage(imageUrl); // 2. 解析API响应 JsonNode responseNode = objectMapper.readTree(modelResponse); if (!responseNode.path("success").asBoolean()) { String errorMsg = responseNode.path("message").asText(); log.error("OCR API调用失败: {}", errorMsg); return Collections.singletonList( new ValidationResult("SYSTEM", "API_ERROR", "API调用成功", "API调用失败: " + errorMsg, false) ); } // 3. 提取OCR结果 JsonNode actualData = responseNode.path("data").path("ocr_result"); log.info("OCR识别结果: {}", actualData.toPrettyString()); // 4. 执行精准比较 return preciseCompare(actualData, expectedConfig); } catch (Exception e) { log.error("验证过程异常: {}", e.getMessage(), e); return Collections.singletonList( new ValidationResult("SYSTEM", "EXCEPTION", "正常处理", "系统错误: " + e.getMessage(), false) ); } } private String convertToBase64(MultipartFile file) throws IOException { try (InputStream inputStream = file.getInputStream()) { // 使用缓冲流提高读取效率 ByteArrayOutputStream buffer = new ByteArrayOutputStream(); byte[] data = new byte[4096]; int bytesRead; while ((bytesRead = inputStream.read(data, 0, data.length)) != -1) { buffer.write(data, 0, bytesRead); } buffer.flush(); return Base64.getEncoder().encodeToString(buffer.toByteArray()); } } // 精准字段比较 private List<ValidationResult> preciseCompare(JsonNode actualData, JsonNode expectedConfig) { List<ValidationResult> results = new ArrayList<>(); // 遍历所有预期字段 expectedConfig.fields().forEachRemaining(entry -> { String field = entry.getKey(); String expectedValue = entry.getValue().asText(); JsonNode actualNode = actualData.path(field); if (actualNode.isMissingNode()) { results.add(new ValidationResult("FIELD", field, expectedValue, "字段不存在", false)); return; } String actualValue = actualNode.asText(); boolean isValid = actualValue.equals(expectedValue); results.add(new ValidationResult( "FIELD", field, expectedValue, actualValue, isValid )); }); return results; } public String recognizeTextFromImage(String imageUrl) throws IOException { URL url = new URL(BASE_URL); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setRequestMethod("POST"); connection.setRequestProperty("Authorization", "Bearer " + API_KEY); connection.setRequestProperty("Content-Type", "application/json"); connection.setDoOutput(true); // 2. 构建请求体 JSONObject requestBody = new JSONObject(); requestBody.put("model", MODEL_NAME); JSONArray messages = new JSONArray(); JSONObject message = new JSONObject(); message.put("role", "user"); JSONArray content = new JSONArray(); // 文本指令 JSONObject textContent = new JSONObject(); textContent.put("type", "text"); textContent.put("text", "提取图片中的所有文本内容,并按照json格式输出"); content.put(textContent); // 图像URL JSONObject imageContent = new JSONObject(); imageContent.put("type", "image_url"); JSONObject imageUrlObj = new JSONObject(); imageUrlObj.put("url", imageUrl); imageContent.put("image_url", imageUrlObj); content.put(imageContent); message.put("content", content); messages.put(message); requestBody.put("messages", messages); // 3. 发送请求 try (OutputStream os = connection.getOutputStream()) { byte[] input = requestBody.toString().getBytes(StandardCharsets.UTF_8); os.write(input, 0, input.length); } // 4. 获取响应 int responseCode = connection.getResponseCode(); if (responseCode == HttpURLConnection.HTTP_OK) { try (BufferedReader br = new BufferedReader( new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8))) { StringBuilder response = new StringBuilder(); String responseLine; while ((responseLine = br.readLine()) != null) { response.append(responseLine.trim()); } // 解析响应,提取模型返回的内容 JSONObject jsonResponse = new JSONObject(response.toString()); JSONArray choices = jsonResponse.getJSONArray("choices"); if (choices.length() > 0) { JSONObject firstChoice = choices.getJSONObject(0); JSONObject messageObj = firstChoice.getJSONObject("message"); return messageObj.getString("content"); } else { throw new IOException("未获取到有效响应"); } } } else { // 读取错误流 try (BufferedReader br = new BufferedReader( new InputStreamReader(connection.getErrorStream(), StandardCharsets.UTF_8))) { StringBuilder errorResponse = new StringBuilder(); String line; while ((line = br.readLine()) != null) { errorResponse.append(line); } throw new IOException("HTTP请求失败: " + responseCode + ", 错误信息: " + errorResponse.toString()); } } }我的方法 测试有问题 然而我这个接口和方法没有问题 @PostMapping(value = "/compare", produces = {MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE}) public R compare(@RequestPart("File") MultipartFile file, @RequestPart("jsonContent") String jsonContent) { //读取PDF和图片内容 String pdfText = compareService.extractContent(file); //解析JSON配置 JsonNode jsonConfig = null; List<ValidationResult> results = new ArrayList<>(); try { if (compareService.isPipeSeparatedData(jsonContent)) { jsonConfig = compareService.parsePipeSeparatedDataToJson(jsonContent); results = compareService.compareAndValidate(pdfText, jsonConfig); } else { jsonConfig = compareService.parseJson(jsonContent); results = compareService.compareContent(pdfText, jsonConfig); } } catch (Exception e) { return R.fail(MessageUtils.message("failed.convert.json")); } // 返回没有匹配成功的数据 List<ValidationResult> failedResults = new ArrayList<>(); for (ValidationResult result : results) { if (!result.isValid()) { failedResults.add(result); } } return failedResults.isEmpty() ? R.ok("条件符合规范") : R.fail(failedResults); }private final ObjectMapper objectMapper = new ObjectMapper(); private Tesseract tesseract; @Value("${tesseract.datapath}") private String tessDataPath; /** * ocr配置 */ @PostConstruct public void initOcrEngine() { tesseract = new Tesseract(); //语言路径和支持语言 tesseract.setDatapath(tessDataPath); tesseract.setLanguage("eng+chi_sim"); tesseract.setPageSegMode(6); // 完全自动页面分割模式 tesseract.setOcrEngineMode(2); // LSTM引擎 } static { //OpenCV本地库 System.load("D:\\work\\OpenCV\\opencv\\build\\java\\x64\\opencv_java4120.dll"); } /** * 支持PDF读取文件和图片ocr */ public String extractContent(MultipartFile file) { String contentType = file.getContentType(); String fileName = file.getOriginalFilename().toLowerCase(); log.info("模版识别:" + fileName); if (contentType == null) { return "不支持的文件类型: " + contentType; } if (fileName.endsWith(".pdf")) { return readPdfText(file); } return extractImageText(file); } /** * 读取PDF文本内容 * * @param file * @return */ public String readPdfText(MultipartFile file) { try (PDDocument doc = PDDocument.load(file.getInputStream())) { PDFTextStripper stripper = new PDFTextStripper(); String rawText = stripper.getText(doc); System.out.println("pdf内容" + rawText); return rawText.trim(); } catch (Exception e) { return MessageUtils.message("file.read.pdf.error"); } } /** * OCR识别图片内容 */ private String extractImageText(MultipartFile file) { try (InputStream is = file.getInputStream()) { BufferedImage image = ImageIO.read(is); if (image == null) { return MessageUtils.message("Image.parsing.failed"); } // BufferedImage bufferedImage = preprocessImage(image); // String result = tesseract.doOCR(bufferedImage); String result = tesseract.doOCR(image); result = postProcess(result); result = result.replaceAll("\\s+", " ").trim(); System.out.println("图片内容:\n" + result); return result; } catch (Exception e) { e.printStackTrace(); return MessageUtils.message("file.read.picture.error"); } } private BufferedImage preprocessImage(BufferedImage original) { try { //转换为OpenCV Mat格式 Mat mat = bufferedImageToMat(original); //灰度化 Mat gray = new Mat(); Imgproc.cvtColor(mat, gray, Imgproc.COLOR_BGR2GRAY); //降噪处理 Mat blurred = new Mat(); Imgproc.GaussianBlur(gray, blurred, new Size(3, 3), 0); Mat binary = new Mat(); Imgproc.threshold(blurred, binary, 0, 255, Imgproc.THRESH_BINARY | Imgproc.THRESH_OTSU); //分离粘连字符 Mat kernel = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(1, 3)); Mat dilated = new Mat(); Imgproc.dilate(binary, dilated, kernel); Mat eroded = new Mat(); Imgproc.erode(dilated, eroded, kernel); //转BufferedImage return matToBufferedImage(eroded); } catch (Exception e) { e.printStackTrace(); return original; // 出错时返回原图 } } // BufferedImage转OpenCV Mat private Mat bufferedImageToMat(BufferedImage image) { int type = BufferedImage.TYPE_3BYTE_BGR; if (image.getType() != type) { BufferedImage convertedImage = new BufferedImage(image.getWidth(), image.getHeight(), type); convertedImage.getGraphics().drawImage(image, 0, 0, null); image = convertedImage; } byte[] data = ((DataBufferByte) image.getRaster().getDataBuffer()).getData(); Mat mat = new Mat(image.getHeight(), image.getWidth(), CvType.CV_8UC3); // 确保数据元素数量是通道数的整数倍 int expectedLength = mat.rows() * mat.cols() * mat.channels(); if (data.length != expectedLength) { byte[] newData = new byte[expectedLength]; System.arraycopy(data, 0, newData, 0, Math.min(data.length, expectedLength)); data = newData; } mat.put(0, 0, data); return mat; } // OpenCV Mat转BufferedImage private BufferedImage matToBufferedImage(Mat mat) { int type = BufferedImage.TYPE_BYTE_GRAY; if (mat.channels() > 1) { type = BufferedImage.TYPE_3BYTE_BGR; } byte[] data = new byte[mat.cols() * mat.rows() * (int) mat.elemSize()]; mat.get(0, 0, data); BufferedImage image = new BufferedImage(mat.cols(), mat.rows(), type); image.getRaster().setDataElements(0, 0, mat.cols(), mat.rows(), data); return image; } private String postProcess(String result) { result = result .replace("scHas", "sch-as") // .replace("(oczg spol s.r.0", "(cz) spol s.r.o.") // .replace("zahradou 101", "zahradou 1018") .replace("¢", "c") .replace("(Shanghai)Co", "applecomputertrading(shanghai)coltd1249centuryavenuetower3,pudongnewdistrict,shanghai200122,china"); return result; } /** * 解析json */ public JsonNode parseJson(String jsonContent) throws Exception { return this.objectMapper.readTree(jsonContent); } /** * 比较pdf读取内容和 ocr图片内容 比较校验 * * @param pdfText * @param jsonConfig * @return */ public List<ValidationResult> compareContent(String pdfText, JsonNode jsonConfig) { List<ValidationResult> results = new ArrayList<>(); //HanLP List<String> hanlpText = HanLP.segment(pdfText).stream() .map(term -> term.word.replaceAll("\\s+", "").toLowerCase()) .collect(Collectors.toList()); System.out.println("分词的效果:" + hanlpText); //处理JSON结构对象/数组 JsonNode dataNode = jsonConfig.isArray() && jsonConfig.size() > 0 ? jsonConfig.get(0) : jsonConfig; //遍历JSON dataNode.fields().forEachRemaining(entry -> { String key = entry.getKey(); String value = entry.getValue().asText(); List<String> hanlpJsonText = HanLP.segment(value).stream() .map(term -> term.word.replaceAll("\\s+", "").toLowerCase()) .collect(Collectors.toList()); if (!hanlpJsonText.isEmpty()) { boolean found = isMatch(hanlpText, hanlpJsonText); if (found) { log.info("字段对比成功:{}", hanlpJsonText); } else { log.info("字段对比失败:{}", hanlpJsonText); } results.add(new ValidationResult( "FIELD", key, String.join("", hanlpJsonText), found ? "Found" : "Not Found", found )); } }); return results; } /** * 转为json结构 * * @param inputData * @return * @throws Exception */ public JsonNode parsePipeSeparatedDataToJson(String inputData) throws Exception { Map<String, String> dataMap = parsePipeSeparatedData(inputData); return objectMapper.valueToTree(dataMap); } /** * 解析分隔数据 */ public Map<String, String> parsePipeSeparatedData(String fileCONTENT) { //处理转义的换行符 fileCONTENT = fileCONTENT.replace("\\n", "\n").replaceAll("\\|+\"$", "").trim(); Map<String, String> dataMap = new LinkedHashMap<>(); String[] lines = fileCONTENT.split("\n"); if (lines.length >= 2) { String[] headers = lines[0].split("\\|"); String[] values = lines[1].split("\\|"); int minLength = Math.min(headers.length, values.length); for (int i = 0; i < minLength; i++) { dataMap.put(headers[i], values[i]); } } return dataMap; } public boolean isPipeSeparatedData(String inputData) { return inputData.contains("|"); } /** * 比较和校验数据 */ public List<ValidationResult> compareAndValidate(String fileContent, JsonNode jsonConfig) { List<ValidationResult> results = new ArrayList<>(); // 处理 JSON 结构对象/数组 JsonNode dataNode = jsonConfig.isArray() && jsonConfig.size() > 0 ? jsonConfig.get(0) : jsonConfig; // HanLP文本分词 List<String> hanlpTest = HanLP.segment(fileContent).stream() .map(term -> term.word.replaceAll("\\s+", "").toLowerCase()) .collect(Collectors.toList()); System.out.println("分词的效果:" + hanlpTest); // 遍历JSON dataNode.fields().forEachRemaining(entry -> { String key = entry.getKey(); String value = entry.getValue().asText(); List<String> hanlpJsonTest = HanLP.segment(value).stream() .map(term -> term.word.replaceAll("\\s+", "").toLowerCase()) .collect(Collectors.toList()); if (!hanlpJsonTest.isEmpty()) { boolean found = isMatch(hanlpTest, hanlpJsonTest); if (found) { log.info("字段对比成功:{}", hanlpJsonTest); } else { log.info("字段对比失败:{}", hanlpJsonTest); } results.add(new ValidationResult( "FIELD", key, String.join("", hanlpJsonTest), found ? "Found" : "Not Found", found )); } }); return results; } private boolean isMatch(List<String> test, List<String> valueTokens) { String Joined = String.join("", test); String valueJoined = String.join("", valueTokens); return Joined.contains(valueJoined); }
07-30
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值