SpringBoot科大讯飞语音识别

1.前言

我使用的科大讯飞的录音文件转写。需要购买科大讯飞的产品,可以自行购买

识别的速度有点慢,大概在5s左右。不知道为什么,有喜欢的可以一起研究一下。不喜勿喷,谢谢

2.使用依赖

<!--语音识别使用的依赖-->
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.7.21</version>
</dependency>
<dependency>
    <groupId>org.json</groupId>
    <artifactId>json</artifactId>
    <version>20231013</version> <!-- 使用最新版本 -->
</dependency>
<dependency>
    <groupId>commons-lang</groupId>
    <artifactId>commons-lang</artifactId>
    <version>2.4</version>
</dependency>
<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.5.11</version>
    <scope>compile</scope>
</dependency>

<dependency>
    <groupId>com.google.cloud</groupId>
    <artifactId>google-cloud-speech</artifactId>
    <version>0.84.0-beta</version>
</dependency>

3.工具类

import cn.hutool.json.JSONUtil;
import com.hxc.sign.LfasrSignature;
import org.apache.commons.lang.StringEscapeUtils;
import com.google.gson.Gson;
import java.io.*;
import java.security.SignatureException;
import java.util.HashMap;

public class Ifasrdemo {
    private static final String HOST = "https://raasr.xfyun.cn";
    public static String AUDIO_FILE_PATH = "";
    private static final String appid = "自己购买的科大讯飞的appid";
    private static final String keySecret = "购买的科大讯飞的keySecret";

    private static final Gson gson = new Gson();




    public static String a1() throws Exception {
        String result = upload();
        String jsonStr = StringEscapeUtils.unescapeJavaScript(result);
        String orderId = String.valueOf(JSONUtil.getByPath(JSONUtil.parse(jsonStr), "content.orderId"));
        return getResult(orderId);
    }

    private static String upload() throws SignatureException, FileNotFoundException {
        HashMap<String, Object> map = new HashMap<>(16);
        File audio = new File(AUDIO_FILE_PATH);
        String fileName = audio.getName();
        long fileSize = audio.length();
        map.put("appId", appid);
        map.put("fileSize", fileSize);
        map.put("fileName", fileName);
        map.put("duration", "200");
        LfasrSignature lfasrSignature = new LfasrSignature(appid, keySecret);
        map.put("signa", lfasrSignature.getSigna());
        map.put("ts", lfasrSignature.getTs());

        String paramString = HttpUtil.parseMapToPathParam(map);
        System.out.println("upload paramString:" + paramString);

        String url = HOST + "/v2/api/upload" + "?" + paramString;
        System.out.println("upload_url:" + url);
        String response = HttpUtil.iflyrecUpload(url, new FileInputStream(audio));

        System.out.println("upload response:" + response);
        return response;
    }

    private static String getResult(String orderId) throws SignatureException, InterruptedException, IOException {
        HashMap<String, Object> map = new HashMap<>(16);
        map.put("orderId", orderId);
        LfasrSignature lfasrSignature = new LfasrSignature(appid, keySecret);
        map.put("signa", lfasrSignature.getSigna());
        map.put("ts", lfasrSignature.getTs());
        map.put("appId", appid);
        map.put("resultType", "transfer,predict");
        String paramString = HttpUtil.parseMapToPathParam(map);
        String url = HOST + "/v2/api/getResult" + "?" + paramString;
        System.out.println("\nget_result_url:" + url);
        while (true) {
            String response = HttpUtil.iflyrecGet(url);
            JsonParse jsonParse = gson.fromJson(response, JsonParse.class);
            if (jsonParse.content.orderInfo.status == 4 || jsonParse.content.orderInfo.status == -1) {
                System.out.println("订单完成:" + response);
                write(response);
                return response;
            } else {
                System.out.println("进行中...,状态为:" + jsonParse.content.orderInfo.status);
                //建议使用回调的方式查询结果,查询接口有请求频率限制
                Thread.sleep(7000);
            }
        }
    }

    public static void write(String resp) throws IOException {//
        //将写入转化为流的形式
        BufferedWriter bw = new BufferedWriter(new FileWriter("D:\\MyJavaProject\\springBoot3\\boot3-01\\src\\main\\resources\\output\\test.txt"));
        String ss = resp;
        bw.write(ss);
        //关闭流
        bw.close();
        System.out.println("写入txt成功");
    }

    class JsonParse {
        Content content;
    }

    class Content {
        OrderInfo orderInfo;
    }

    class OrderInfo {
        Integer status;
    }
}


import org.apache.http.Consts;
import org.apache.http.HttpEntity;
import org.apache.http.HttpStatus;
import org.apache.http.client.config.RequestConfig;
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.methods.HttpRequestBase;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.InputStreamEntity;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

/**
 * HTTP请求工具类
 */
public class HttpUtil {
    private HttpUtil() {
    }

    private static final Logger LOGGER = LoggerFactory.getLogger(HttpUtil.class);

    private static final String UTF8 = "UTF-8";
    /**
     * 组件的httpClient
     */
    private static CloseableHttpClient httpClient;

    static {
        // 听见服务、流控组件连接池
        PoolingHttpClientConnectionManager pool = new PoolingHttpClientConnectionManager();
        pool.setMaxTotal(600);//客户端总并行链接最大数
        pool.setDefaultMaxPerRoute(200);//每个主机的最大并行链接数
        httpClient = HttpClients.createMinimal(pool);
    }

    /**
     * 请求的upload接口, 发送音频创建转写订单
     *
     * @param url       请求地址
     * @param in        需要转写的音频流
     * @return 返回结果
     */
    public static String iflyrecUpload(String url,  InputStream in) {
        // 1、准备参数
        HttpPost httpPost = new HttpPost(url);
        // 设置超时时间, 防止504的时候会耗时30分钟
        RequestConfig requestConfig = RequestConfig.custom()
                //从连接池中获取连接的超时时间
                .setConnectionRequestTimeout(5000)
                //与服务器连接超时时间, 指的是连接一个url的连接等待时间
                .setConnectTimeout(600000)
                // 读取超时, 指的是连接上一个url,获取response的返回等待时间
                .setSocketTimeout(600000).build();
        httpPost.setConfig(requestConfig);
        HttpEntity requestEntity = new InputStreamEntity(in, ContentType.APPLICATION_JSON);
        //System.out.println("---"+requestEntity);
        httpPost.setEntity(requestEntity);

        // 2、执行请求
        return doExecute(httpPost, null);
    }

    /**
     * 请求听见的获取结果接口
     *
     * @param url       请求路径
     * @return 返回结果
     */
    public static String iflyrecGet(String url) {
        // 1、准备参数
        HttpGet httpget = new HttpGet(url);
        // 2、执行请求
        return doExecute(httpget, UTF8);
    }

    /**
     * 流控组件调用
     *
     * @param url 请求路径
     * @return 返回结果
     */
    public static String flowCtrlGet(String url) {
        // 1、准备参数
        HttpGet httpget = new HttpGet(url);
        RequestConfig requestConfig = RequestConfig.custom()
                //从连接池中获取连接的超时时间
                .setConnectionRequestTimeout(500)
                //与服务器连接超时时间
                .setConnectTimeout(500)
                // 读取超时
                .setSocketTimeout(500).build();
        httpget.setConfig(requestConfig);
        // 2、执行请求
        return doExecute(httpget, null);
    }

    /**
     * 流传输的post
     *
     * @param url  请求路径
     * @param body 字节流数据
     * @return 返回结果
     */
    public static String post(String url, byte[] body) {
        // 1、准备参数
        HttpPost httpPost = new HttpPost(url);
        httpPost.setEntity(new ByteArrayEntity(body, ContentType.create("application/octet-stream", Consts.UTF_8)));
        // 2、执行请求
        return doExecute(httpPost, UTF8);
    }

    /**
     * 带字符串参数的post请求
     *
     * @param url   请求路径
     * @param param 字符串参数
     * @return 返回结果
     */
    public static String post(String url, String param) {
        // 1、准备参数
        HttpPost httpPost = new HttpPost(url);
        // 设置超时时间
        RequestConfig requestConfig = RequestConfig.custom()
                //从连接池中获取连接的超时时间
                .setConnectionRequestTimeout(1000)
                //与服务器连接超时时间, 指的是连接一个url的连接等待时间
                .setConnectTimeout(10000)
                // 读取超时, 指的是连接上一个url,获取response的返回等待时间
                .setSocketTimeout(10000).build();
        httpPost.setConfig(requestConfig);
        httpPost.setEntity(new StringEntity(param, ContentType.APPLICATION_JSON));
        // 2、执行请求
        return doExecute(httpPost, null);
    }

    /**
     * 发送HttpGet请求
     *
     * @param url 请求路径
     * @return 返回结果
     */
    public static String sendGet(String url) {
        // 1、准备参数
        HttpGet httpget = new HttpGet(url);
        // 2、执行请求
        return doExecute(httpget, null);
    }

    /**
     * 执行网络请求
     *
     * @param requestBase http请求对象
     * @param charset     字符集
     * @return 返回结果
     */
    private static String doExecute(HttpRequestBase requestBase, String charset) {
        String result = null;
        try (CloseableHttpResponse response = httpClient.execute(requestBase)) {
            // 3、检查结果状态
            int statusCode = response.getStatusLine().getStatusCode();
            if (statusCode != HttpStatus.SC_OK) {
                LOGGER.error("网络异常");
                return null;
            }
            // 4、获取结果
            result = charset == null
                    ? EntityUtils.toString(response.getEntity())
                    : EntityUtils.toString(response.getEntity(), charset);
        } catch (Exception e) {
            LOGGER.error("网络异常", e);
        }
        return result;
    }

    /**
     * 发送post请求
     *
     * @param url    请求路径
     * @param header 请求头
     * @param body   请求数据
     * @return 返回结果
     */
    public static String postWithHeader(String url, Map<String, String> header, String body) {
        StringBuilder result = new StringBuilder();
        BufferedReader in = null;
        PrintWriter out = null;
        InputStreamReader is = null;
        try {
            // 设置 url
            URL realUrl = new URL(url);
            URLConnection connection = realUrl.openConnection();
            // 设置 header
            for (Entry<String, String> entry : header.entrySet()) {
                connection.setRequestProperty(entry.getKey(), entry.getValue());
            }
            // 设置请求 body
            connection.setDoOutput(true);
            connection.setDoInput(true);
            out = new PrintWriter(connection.getOutputStream());
            // 保存body
            out.print(body);
            // 发送body
            out.flush();
            // 获取响应body
            is = new InputStreamReader(connection.getInputStream());
            in = new BufferedReader(is);
            String line;
            while ((line = in.readLine()) != null) {
                result.append(line);
            }
        } catch (Exception e) {
            LOGGER.error("HttpUtil postWithHeader Exception!", e);
            return null;
        } finally {
            if (is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    LOGGER.error("close IO resource Exception!", e);
                }
            }
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                    LOGGER.error("close IO resource Exception!", e);
                }
            }
            if (out != null) {
                out.close();
            }
        }
        return result.toString();
    }

    /**
     * 将集合转换为路径参数
     *
     * @param param 集合参数
     * @return 路径参数
     * @author white
     */
    public static String parseMapToPathParam(Map<String, Object> param) {
        StringBuilder sb = new StringBuilder();
        try {
            Set<Entry<String, Object>> entryset = param.entrySet();
            boolean isFirst = true;
            for (Entry<String, Object> entry : entryset) {
                if (!isFirst) {
                    sb.append("&");
                } else {
                    isFirst = false;
                }
                sb.append(URLEncoder.encode(entry.getKey(), UTF8));
                sb.append("=");
                sb.append(URLEncoder.encode(entry.getValue().toString(), UTF8));
            }
        } catch (UnsupportedEncodingException e) {
            LOGGER.error("HttpUtil parseMapToPathParam Exception!", e);
        }

        return sb.toString();
    }
}
import cn.hutool.json.JSONUtil;
import com.hxc.sign.LfasrSignature;
import org.apache.commons.lang.StringEscapeUtils;
import com.google.gson.Gson;
import java.io.*;
import java.security.SignatureException;
import java.util.HashMap;

public class Ifasrdemo {
    private static final String HOST = "https://raasr.xfyun.cn";
    public static String AUDIO_FILE_PATH = "";
    private static final String appid = "购买的科大讯飞的appid";
    private static final String keySecret = "购买的科大讯飞的key";

    private static final Gson gson = new Gson();




    public static String a1() throws Exception {
        String result = upload();
        String jsonStr = StringEscapeUtils.unescapeJavaScript(result);
        String orderId = String.valueOf(JSONUtil.getByPath(JSONUtil.parse(jsonStr), "content.orderId"));
        return getResult(orderId);
    }

    private static String upload() throws SignatureException, FileNotFoundException {
        HashMap<String, Object> map = new HashMap<>(16);
        File audio = new File(AUDIO_FILE_PATH);
        String fileName = audio.getName();
        long fileSize = audio.length();
        map.put("appId", appid);
        map.put("fileSize", fileSize);
        map.put("fileName", fileName);
        map.put("duration", "200");
        LfasrSignature lfasrSignature = new LfasrSignature(appid, keySecret);
        map.put("signa", lfasrSignature.getSigna());
        map.put("ts", lfasrSignature.getTs());

        String paramString = HttpUtil.parseMapToPathParam(map);
        System.out.println("upload paramString:" + paramString);

        String url = HOST + "/v2/api/upload" + "?" + paramString;
        System.out.println("upload_url:" + url);
        String response = HttpUtil.iflyrecUpload(url, new FileInputStream(audio));

        System.out.println("upload response:" + response);
        return response;
    }

    private static String getResult(String orderId) throws SignatureException, InterruptedException, IOException {
        HashMap<String, Object> map = new HashMap<>(16);
        map.put("orderId", orderId);
        LfasrSignature lfasrSignature = new LfasrSignature(appid, keySecret);
        map.put("signa", lfasrSignature.getSigna());
        map.put("ts", lfasrSignature.getTs());
        map.put("appId", appid);
        map.put("resultType", "transfer,predict");
        String paramString = HttpUtil.parseMapToPathParam(map);
        String url = HOST + "/v2/api/getResult" + "?" + paramString;
        System.out.println("\nget_result_url:" + url);
        while (true) {
            String response = HttpUtil.iflyrecGet(url);
            JsonParse jsonParse = gson.fromJson(response, JsonParse.class);
            if (jsonParse.content.orderInfo.status == 4 || jsonParse.content.orderInfo.status == -1) {
                System.out.println("订单完成:" + response);
                write(response);
                return response;
            } else {
                System.out.println("进行中...,状态为:" + jsonParse.content.orderInfo.status);
                //建议使用回调的方式查询结果,查询接口有请求频率限制
                Thread.sleep(7000);
            }
        }
    }

    public static void write(String resp) throws IOException {//
        //将写入转化为流的形式
        BufferedWriter bw = new BufferedWriter(new FileWriter("D:\\MyJavaProject\\springBoot3\\boot3-01\\src\\main\\resources\\output\\test.txt"));
        String ss = resp;
        bw.write(ss);
        //关闭流
        bw.close();
        System.out.println("写入txt成功");
    }

    class JsonParse {
        Content content;
    }

    class Content {
        OrderInfo orderInfo;
    }

    class OrderInfo {
        Integer status;
    }
}
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;

public class JsonProcessor {

    // 读取 JSON 文件
    public static JSONObject readJsonFile(String path) throws IOException, JSONException {
        StringBuilder content = new StringBuilder();
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(path)))) {
            String line;
            while ((line = reader.readLine()) != null) {
                content.append(line);
            }
        }
        return new JSONObject(content.toString());
    }

    // 处理单个 VAD 的结果
    public static String mergeResultForOneVad(JSONObject resultVad) throws JSONException {
        StringBuilder spkStr = new StringBuilder();
        JSONArray rtArray = resultVad.getJSONObject("st").getJSONArray("rt");

        for (int i = 0; i < rtArray.length(); i++) {
            JSONObject rtDic = rtArray.getJSONObject(i);
            //spkStr.append(3 - resultVad.getJSONObject("st").getInt("rl"));

            JSONArray wsArray = rtDic.getJSONArray("ws");
            for (int j = 0; j < wsArray.length(); j++) {
                JSONObject wsDic = wsArray.getJSONObject(j);
                JSONArray cwArray = wsDic.getJSONArray("cw");
                for (int k = 0; k < cwArray.length(); k++) {
                    JSONObject cwDic = cwArray.getJSONObject(k);
                    String wArray = cwDic.getString("w");
                    //for (int l = 0; l < wArray.length(); l++) {
                        spkStr.append(wArray);
                    //}
                }
            }
            //spkStr.append("\n");
        }

        return spkStr.toString();
    }

    // 将内容写入文件
    public static String contentToFile(List<String> content, String outputFilePath) throws IOException {
            StringBuilder contentStr = new StringBuilder();
            for (String line : content) {
                contentStr.append(line);
            }
            return contentStr.toString();
    }

    public static String a2(String s) {
        try {
            //String pathXunfei = "D:\\java-project-dome\\springboot3-dome\\springboot-dome1\\target\\classes\\output\\test.txt";
            String outputPathXunfei = "xunfei_output.txt";

            // 读取 JSON 文件
            //JSONObject jsXunfei = readJsonFile(pathXunfei);
            JSONObject jsXunfei = new JSONObject(s);
            JSONObject orderResult = new JSONObject(jsXunfei.getJSONObject("content").getString("orderResult"));

            // 处理 lattice 数据
            JSONArray latticeArray = orderResult.getJSONArray("lattice");
            List<String> content = new ArrayList<>();
            for (int i = 0; i < latticeArray.length(); i++) {
                JSONObject latticeItem = latticeArray.getJSONObject(i);
                JSONObject json1Best = new JSONObject(latticeItem.getString("json_1best"));
                content.add(mergeResultForOneVad(json1Best));
            }

            // 将结果写入文件
            String s1 = contentToFile(content, outputPathXunfei);
            System.out.println("处理完成,结果已写入文件:" + outputPathXunfei);
            return s1;
        } catch (IOException | JSONException e) {
            e.printStackTrace();
        }
        return "失败";
    }
import java.net.MalformedURLException;
import java.net.URL;
import java.security.SignatureException;

/**
 * 加签加密抽象类
 *
 * @author : jun
 * @date : 2021年04月08日
 */
public abstract class AbstractSignature {
    /**
     * 签名ID
     */
    private String id;

    /**
     * 加密key
     */
    private String key;

    /**
     * 服务url
     */
    private String url;

    /**
     * 加密算法
     */
    private String encryptType;

    /**
     * 待加密原始字符
     */
    private String originSign;

    /**
     * 最终生成的签名
     */
    protected String signa;

    /**
     * 时间戳timestamp
     */
    private String ts;

    /**
     * 请求类型,默认get
     */
    protected String requestMethod = "GET";

    /**
     * @param id
     * @param key
     * @param url
     */
    public AbstractSignature(String id, String key, String url) {
        this.id = id;
        this.key = key;
        this.url = url;
        this.ts = generateTs();
    }

    /**
     * 可设置请求类型
     * @param id
     * @param key
     * @param url
     * @param isPost 是否为POST
     */
    public AbstractSignature(String id, String key, String url, boolean isPost) {
        this.id = id;
        this.key = key;
        this.url = url;
        if (isPost) {
            this.requestMethod = "POST";
        }else{
            this.requestMethod = "GET";
        }

        this.ts = generateTs();
    }

    /**
     * 生成ts时间
     */
    public String generateTs() {
        return String.valueOf(System.currentTimeMillis() / 1000L);
    }


    /**
     * 完成签名,返回完整签名
     *
     * @return
     * @throws SignatureException
     */
    public abstract String getSigna() throws SignatureException;

    public String generateOriginSign() throws SignatureException {
        try {
            URL url = new URL(this.getUrl());

            return "host: " + url.getHost() + "\n" +
                    "date: " + this.getTs() + "\n" +
                    "GET " + url.getPath() + " HTTP/1.1";
        } catch (MalformedURLException e) {
            throw new SignatureException("MalformedURLException:" + e.getMessage());
        }
    }


    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getKey() {
        return key;
    }

    public void setKey(String key) {
        this.key = key;
    }

    public String getOriginSign() {
        return originSign;
    }

    public void setOriginSign(String originSign) {
        this.originSign = originSign;
    }

    public String getTs() {
        return ts;
    }

    public void setTs(String ts) {
        this.ts = ts;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getEncryptType() {
        return encryptType;
    }

    public void setEncryptType(String encryptType) {
        this.encryptType = encryptType;
    }
}
import cn.hutool.core.util.ObjectUtil;
import com.hxc.sign.utils.CryptTools;

import java.security.NoSuchAlgorithmException;
import java.security.SignatureException;

/**
 * Lfasr能力签名实体
 *
 * @author : jun
 * @date : 2021年03月29日
 */
public class LfasrSignature extends AbstractSignature {

    /**
     *
     * @param appId
     * @param keySecret
     */
    public LfasrSignature(String appId, String keySecret) {
        super(appId, keySecret, null);
    }

    @Override
    public String getSigna() throws SignatureException {
        if (ObjectUtil.isEmpty(this.signa)) {
            this.setOriginSign(generateOriginSign());
            this.signa = generateSignature();
        }
        return this.signa;
    }

    /**
     * 生成最终的签名,需要先生成原始sign
     *
     * @throws SignatureException
     */
    public String generateSignature() throws SignatureException {
        return CryptTools.hmacEncrypt(CryptTools.HMAC_SHA1, this.getOriginSign(), this.getKey());
    }

    /**
     * 生成待加密原始字符
     *
     * @throws NoSuchAlgorithmException
     */
    @Override
    public String generateOriginSign() throws SignatureException {
        return CryptTools.md5Encrypt(this.getId() + this.getTs());
    }
}

4.Controller代码

import com.hxc.sign.utils.Ifasrdemo;
import com.hxc.sign.utils.JsonProcessor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.io.*;


@RestController
@RequestMapping("/xf")
@Slf4j
public class XfyunController {

    @PostMapping("/uploadSdk")
    public String uploadSdk(@RequestParam("files") MultipartFile files) throws Exception {
        // 指定保存文件的本地路径
        String uploadDir = "D:/uploads/";  // 你可以根据实际情况更改路径
        // 获取文件原始文件名
        String fileName = files.getOriginalFilename();
        // 创建目标文件对象
        File dest = new File(uploadDir + fileName);
        // 创建上传目录(如果没有的话)
        if (!dest.getParentFile().exists()) {
            dest.getParentFile().mkdirs();
        }
        // 将文件保存到指定路径
        files.transferTo(dest);
        Ifasrdemo.AUDIO_FILE_PATH = dest.getAbsolutePath();
        String s = Ifasrdemo.a1();
        return JsonProcessor.a2(s);
    }

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值