JNA USB-hid 设备连接的案例(半成品)回顾录

目录

1.准备设备,二维码扫描模块及其信息、它的dll文件(在Windows)。

2.参考    HidPosApi 使 用 说 明 文 档    ,编写接口

 3.准备一个图形化界面(swing)

3.1这是主界面程序(不完整版)

3.2 项目的pom.xml文件(不一定全用上)

 4.声明当前的各种版本

5.项目结构

 7.重点部分:

7 .1接口类文件

7.1.1回调函数实现类

7.2重点:对二维码模块的识别和把数据传输到swing界面上的JTextArea文本框中

8.回忆项目中我面临的难点

 8.1 接口类的相对路径的设置(lib前不要/),


1.准备设备,二维码扫描模块及其信息、它的dll文件(在Windows)。

文件在自己的阿里云盘:

 1.1获取设备信息(vid与pid)

在“我的电脑“的管理-》

 得到

vid:1FC9

pid: 5AA7

2.参考    HidPosApi 使 用 说 明 文 档    ,编写接口,准备一点本地jna的jar

本地的jna    jar包

 3.准备一个图形化界面(swing)

3.1这是主界面程序(不完整版)
package org.frame;


import com.sun.jna.ptr.IntByReference;
import org.api.HidPosAPI;
import org.myClass.MyCallback;
import org.util.PathUtils;
import org.util.ScreenUtils;
import org.util.SerialReader;



import javax.swing.*;
import javax.swing.border.BevelBorder;
import javax.swing.border.Border;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import java.io.*;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.text.NumberFormat;


public class UserFrame {
//TODO 对象new区
    final int WIDTH = 500;

    final int HEIGHT = 600;
    private static final short VENDOR_ID = 0x1FC9; // USB设备的供应商ID
    private static final short PRODUCT_ID = 0x5AA7; // USB设备的产品ID
    //usb变量设置全局变量
    /*UsbDevice device = null;*/
   /*Usbdevice device = null;*/


    JFrame frame = new JFrame("打标信息系统");

    JMenuBar menuBar = new JMenuBar();
    JPanel jPanel = new JPanel();
    JPanel jPanel2 = new JPanel();
    JPanel jPanel3 = new JPanel();
    //
    JLabel jLabel = new JLabel("打标数量:");
    //
    Box vBox = Box.createVerticalBox();
    //
    JTextArea jTextArea = new JTextArea("",15,30);
    //整数输入的文本框
    NumberFormat format = NumberFormat.getIntegerInstance();
    JFormattedTextField numberTextField = new JFormattedTextField(format);
    //USB文本传输对象
    private InputStream input;
    SerialReader serialReader = new SerialReader(input);
    //



//TODO 组件区
    JMenu fileMenue = new JMenu("文件");
    JMenu editMenue = new JMenu("配置");


    //TODO 监听器区
    JMenuItem open = new JMenuItem(new AbstractAction("打开") {
        @Override
        public void actionPerformed(ActionEvent e) {
            JFileChooser fileChooser = new JFileChooser();
            //
            int result = fileChooser.showOpenDialog(null);
            if (result == JFileChooser.APPROVE_OPTION){
                //
                File selectedFile = fileChooser.getSelectedFile();
                String filePath = selectedFile.getAbsolutePath();
                //
                try {
                    BufferedReader reader = null;
                    //防止中文乱码
                    reader = new BufferedReader(new InputStreamReader(new FileInputStream(filePath),"UTF-8"));
                    String line;
                    StringBuilder sb = new StringBuilder();
                    while ((line = reader.readLine()) != null){sb.append(line).append("\n");
                    }
                    reader.close();
                    //
                    jTextArea.setText(sb.toString());
                } catch (IOException ex) {
                    throw new RuntimeException(ex);
                }
            }
    }});

    //
    JMenuItem save = new JMenuItem(new AbstractAction("另存为") {
        @Override
        public void actionPerformed(ActionEvent e) {
            //
            JFileChooser fileChooser = new JFileChooser(".");
            int result = fileChooser.showOpenDialog(null);
            if (result == JFileChooser.APPROVE_OPTION) {
                File selectedFile = fileChooser.getSelectedFile();
                String contentType = null;
                try {
                    contentType = Files.probeContentType(selectedFile.toPath());
                } catch (IOException ex) {
                    throw new RuntimeException(ex);
                }
                if (contentType != null) {
                    String extension = null;
                    switch (contentType) {
                        case "image/jpeg":
                            extension = "jpg";
                            break;
                        case "image/png":
                            extension = "png";
                            break;
                        case "application/pdf":
                            extension = "pdf";
                            break;
                        // add more cases for other file types
                        default:
                            extension = "dat";
                    }
                    String originalFileName = selectedFile.getName();
                    String newFileName = originalFileName.substring(0, originalFileName.lastIndexOf(".")) + "." + extension;
                    String newFilePath = selectedFile.getParent() + File.separator + newFileName;
                    try (InputStream in = new BufferedInputStream(new FileInputStream(selectedFile));
                         OutputStream out = new BufferedOutputStream(new FileOutputStream(newFilePath))) {
                        byte[] buffer = new byte[1024];
                        int lengthRead;
                        while ((lengthRead = in.read(buffer)) > 0) {
                            out.write(buffer, 0, lengthRead);
                            out.flush();
                        }
                    } catch (IOException ex) {
                        ex.printStackTrace();
                    }
                } else {
                    System.out.println("Unknown file type");
                }
            }
        }

        //



    });
    //


    JMenuItem auto = new JMenuItem("自动换行");
    /*添加图标在菜单条上*/
    JMenuItem copy = new JMenuItem("复制",new ImageIcon("D:\\IDEA\\xmFile\\txhdb\\src\\main\\resources\\images\\copy.png"));//从绝对路径改过来的,不知道是不是相对路径

    JMenu paste = new JMenu("主题风格");
    JMenu msMenu = new JMenu("模式");

    //创建了一个单选分组,以及单选内容
    ButtonGroup moshiButton = new ButtonGroup();
    JRadioButtonMenuItem moshi = new JRadioButtonMenuItem("手动模式");
    JRadioButtonMenuItem moshi2 = new JRadioButtonMenuItem("连续模式");

    //设置了默认为感应模式
    JRadioButtonMenuItem moshi3 = new JRadioButtonMenuItem("感应模式",true);

    //风格组
    ButtonGroup fengeButtonGroup = new ButtonGroup();
    JRadioButtonMenuItem metalItem = new JRadioButtonMenuItem("mtetal 风格");
    JRadioButtonMenuItem NimbusItem = new JRadioButtonMenuItem("Nimbus 风格");
    JRadioButtonMenuItem windowsItem = new JRadioButtonMenuItem("windows 风格");
    JRadioButtonMenuItem motifItem = new JRadioButtonMenuItem("motif 风格");



    /*JButton end = new JButton("结束");*/

    //
    JToolBar jToolBar = new JToolBar("功能条",SwingConstants.HORIZONTAL);

    //TODO 工具按钮点击监听
    Action start2 = new AbstractAction("开始",new ImageIcon("D:\\IDEA\\xmFile\\txhdb\\src\\main\\resources\\images\\开始.png")) {
        @Override
        public void actionPerformed(ActionEvent e) {

        }
    };
    /*java.net.URI imgURL = UserFrame.class.getResource("/images/end.png");
    ImageIcon img1 = new ImageIcon(imgURL);*/


    Action end2 = new AbstractAction("结束",new ImageIcon("D:\\IDEA\\xmFile\\txhdb\\src\\main\\resources\\images\\end.png")) {
        @Override
        public void actionPerformed(ActionEvent e) {

        }
    };
    //监听装入JButton可以显示文字
    JButton start = new JButton(start2);
    JButton end = new JButton(end2);

    public UserFrame() throws URISyntaxException {
    }

    public void init(){
        //TODO 组装中的new对象
        //usb工具类
       /* UsbDataUtil usbDataUtil = new UsbDataUtil();
        usbDataUtil.setjTextArea(jTextArea);*/
        // TODO 组装视图
        JPanel bottomPaner = new JPanel();
        /*bottomPaner.add(start);
        bottomPaner.add(end);*/
        /*frame.add(bottomPaner,BorderLayout.SOUTH);*/
        //把JButton放入工具条
        jToolBar.add(start);
        jToolBar.add(end);

        //
        fileMenue.add(open);
        fileMenue.add(save);
        //TODO 组装中间文本框

        frame.add(jToolBar,BorderLayout.SOUTH);
        //拖动条文本框
       /* jTextArea.setFont(new Font("微软雅黑", Font.PLAIN, 12)); // 设置字体
        jTextArea.setLocale(Locale.CHINA); // 设置字符集*/
        JScrollPane jScrollPane = new JScrollPane(jTextArea);
        //组装数量文本框预留空间大小
        numberTextField.setPreferredSize(new Dimension(40,numberTextField.getPreferredSize().height));
        //
        jPanel2.add(jLabel);
        jPanel2.add(Box.createHorizontalStrut(20));
        jPanel2.add(numberTextField);
        //向左边挤数量框
        jPanel2.add(Box.createHorizontalStrut(200));
        vBox.add(jScrollPane);
        vBox.add(jPanel2);
        jPanel.add(vBox);

        frame.add(jPanel);
        /*System.out.println(new File("."));*/

        moshiButton.add(moshi);
        moshiButton.add(moshi2);
        moshiButton.add(moshi3);
        //风格组装
        fengeButtonGroup.add(metalItem);
        fengeButtonGroup.add(NimbusItem);
        fengeButtonGroup.add(windowsItem);
        fengeButtonGroup.add(motifItem);
        paste.add(metalItem);
        paste.add(NimbusItem);
        paste.add(windowsItem);
        paste.add(motifItem);
        //TODO USB文本输出到程序文本框,并设置程序主窗口监听
        //给工具类一个文本框
        /*serialReader.setjTextArea(jTextArea);*/
        //监听主窗口关闭则,释放串口
       /* frame.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                try {
                    input.close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
                frame.dispose();
            }
        });*/
        //TODO 初始化usb口
        //采用JNA调用第三方dll文件
        //

        IntByReference ref = new IntByReference();
        HidPosAPI myLibrary = HidPosAPI.hidPos;

        //dll类加载路径
        System.out.println("当前dll加载路径:"+System.getProperty("java.library.path"));
        boolean idxx = HidPosAPI.hidPos.OpenDevice(VENDOR_ID, PRODUCT_ID);
        if (idxx) {
            //
            boolean connectStaus = HidPosAPI.hidPos.ConnectStaus();
            if (connectStaus){
                String str = "hh";
                boolean getDeviceSn = HidPosAPI.hidPos.GetDeviceSn(str, ref);
                if (getDeviceSn){
                    System.out.println("当前设备的SN号: "+ref.getValue());
                    //
                    MyCallback myCallback = new MyCallback();
                    myLibrary.SetCallback(myCallback);
                }else {
                    System.out.println("设备状态信息异常:--》"+getDeviceSn);
                    HidPosAPI.hidPos.CloseDevice();
                }

            }else {
                System.out.println("设备状态信息异常:--》"+connectStaus);
                HidPosAPI.hidPos.CloseDevice();

            }
        }
        if (!idxx)
            HidPosAPI.hidPos.CloseDevice();

        //1.查找usb设备
      /*  try {
            device = usbDataUtil.findDevice(UsbHostManager.getUsbServices().getRootUsbHub(), VENDOR_ID, PRODUCT_ID);
            //2.
            usbDataUtil.initUseDevice(device);
            boolean connectSuccess = usbDataUtil.isConnectSuccess();
            System.out.println("连接信息:");
            System.out.println(connectSuccess);
            System.out.println("------------->");
            //3.把获取的数据添加到jTexeArea中
            String s = usbDataUtil.getjTextArea().getText();
            jTextArea.append(s);

        } catch (UsbException e) {
            throw new RuntimeException(e);
        }
*/


        //TODO 监听区2
        //风格监听器
        ActionListener listener = new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                String actionCommand = e.getActionCommand();
                try {
                    changeFlavor(actionCommand);
                } catch (Exception ex) {
                    throw new RuntimeException(ex);
                }
            }
        };
        //整数文本监听--->需要有一个定时器
        numberTextField.getDocument().addDocumentListener(new DocumentListener() {
            @Override
            public void insertUpdate(DocumentEvent e) {
                handleEdit(e);

            }

            @Override
            public void removeUpdate(DocumentEvent e) {
                handleEdit(e);
            }

            @Override
            public void changedUpdate(DocumentEvent e) {
                handleEdit(e);
            }
            //
            private void handleEdit(DocumentEvent e){
                String text = numberTextField.getText().replace(",", "");
                try {
                    int value = Integer.parseInt(text);

                    System.out.println("new value:"+ value);
                    //TODO 整数值 此处添加相应代码,将值发送到另一个组件或者通过其他协议发送

                } catch (NumberFormatException ex) {
                    System.err.println("Invalid number format");
                }
            }
        });
        //注册风格监听
        metalItem.addActionListener(listener);
        NimbusItem.addActionListener(listener);
        windowsItem.addActionListener(listener);
        motifItem.addActionListener(listener);
        //组装组件
        msMenu.add(moshi);
        msMenu.add(moshi2);
        msMenu.add(moshi3);
        editMenue.add(auto);
        editMenue.add(copy);
        editMenue.add(paste);
        //中间的横线
        editMenue.addSeparator();
        editMenue.add(msMenu);

        //菜单条
        menuBar.add(fileMenue);
        menuBar.add(editMenue);
        // TODO frame中间做了一个炫彩边框
        Border beveborder = BorderFactory.createBevelBorder(BevelBorder.RAISED,Color.RED,Color.GREEN, Color.BLUE, Color.GRAY);
        //当前炫彩框与文本框重叠了,故注释掉
       /* frame.add(getJPanelWithBorder(beveborder,"ceshi"));*/
        getJPanelWithBorder(beveborder,"程序信息");
        //让工具条可以拖动
        jToolBar.setFloatable(true);



//TODO frame组装区
        //把菜单条加入frame
        frame.setJMenuBar(menuBar);
        //frame点击X关闭
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.pack();
        frame.setVisible(true);
        //窗口居中
        frame.setBounds((ScreenUtils.getScreenWidth()-WIDTH)/2,(ScreenUtils.getScreenHeight()-HEIGHT)/2,WIDTH,HEIGHT);
        frame.setResizable(false);

        /*

        try {
            frame.setIconImage(ImageIO.read(new File(PathUtils.getRealPath("开始.png"))));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }*/
    }
//TODO 主线程main
    public static void main(String[] args) throws URISyntaxException {
        new UserFrame().init();


    }
    //定义一个方法,用于改变界面风格
    private void changeFlavor(String command) throws Exception {
        switch (command) {
            case "Metal 风格":
                UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel");
                break;
            case "Nimbus 风格":
                UIManager.setLookAndFeel("javax.swing.plaf.nimbus.NimbusLookAndFeel");
                break;
            case "Windows 风格":
                UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
                break;
            case "Windows 经典风格":
                UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsClassicLookAndFeel");
                break;
            case "Motif 风格":
                UIManager.setLookAndFeel("com.sun.java.swing.plaf.motif.MotifLookAndFeel");
                break;
        }
        //刷新组件的外观
        SwingUtilities.updateComponentTreeUI(frame.getContentPane());
        SwingUtilities.updateComponentTreeUI(menuBar);
        //SwingUtilities.updateComponentTreeUI(jPopupMenu);

    }
    //TODO 边框炫彩模块
    public void getJPanelWithBorder(Border border,String content){
     //

        jPanel.add(new JLabel(content));
        //
        jPanel.setBorder(border);


    }

}
3.2 项目的pom.xml文件(不一定全用上)
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.ithyy</groupId>
    <artifactId>txhdb</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <java.version>17</java.version>
    </properties>
    <dependencies>
        <!--json数据转换-->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-annotations</artifactId>
            <version>2.15.0</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>2.15.0</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.15.0</version>
        </dependency>

        <!--USB串口传输库-->
       <!-- <dependency>
            <groupId>comm</groupId>
            <artifactId>rxtxcomm</artifactId>
            <version>20081208</version>
        </dependency>-->
        <!--usb口传输库-->
        <dependency>
            <groupId>org.usb4java</groupId>
            <artifactId>usb4java</artifactId>
            <version>1.3.0</version>
        </dependency>

        <!--usb包啊api-->
       <!-- <dependency>
            <groupId>javax.usb</groupId>
            <artifactId>javax.usb-api</artifactId>
            <version>1.0.2</version>
        </dependency>-->
        <!--<dependency>
            <groupId>com.test</groupId>
            <artifactId>rxtxcomm</artifactId>
            <version>2.2</version>
            &lt;!&ndash;<scope>system</scope>
            <systemPath>${basedir}/lib/RXTXcomm.jar</systemPath>&ndash;&gt;
        </dependency>-->
        <dependency>
            <groupId>net.java.dev.jna</groupId>
            <artifactId>jna</artifactId>
            <version>5.13.0</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>9</source>
                    <target>9</target>
                </configuration>
            </plugin>
            <!--日志插件-->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <compilerArgs>
                        <arg>-Xlint:all</arg>
                    </compilerArgs>
                </configuration>
                <executions>
                    <execution>
                        <id>default-compile</id>
                        <phase>compile</phase>
                        <goals>
                            <goal>compile</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>lib/**</include>
                </includes>
                <filtering>false</filtering>
            </resource>
        </resources>
    </build>


</project>

 4.声明当前的各种版本

jdk 17

jna  5.13.0

5.项目结构

 7.重点部分:

7 .1接口类文件
package org.api;

import com.sun.jna.Callback;
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.ptr.IntByReference;
import org.util.DllIntoUtil;

import javax.swing.*;

public interface HidPosAPI extends Library {
    public static HidPosAPI hidPos = Native.load("lib/HidPosApi", HidPosAPI.class);
    //打开设备
    boolean OpenDevice(int vid,int pid);
    //关闭设备
    boolean CloseDevice();
    //获取设备连接状态
    boolean ConnectStaus();
    //获取设备Sn号
    boolean GetDeviceSn(String pSn,IntByReference pSnLength);
    //设置超时接收时间
    boolean SetReceiveTimeOut(int nTimeOutMillisecond);
    //发送数据
    boolean WriteUsb(Pointer lpBuffer,int dwSize);
    //
    interface ReceivePosData extends Callback{
        int callback(Pointer pData,int pDataLength,byte cResult);
    }
    //设置回调函数
    int SetCallback(ReceivePosData pf);


}
7.1.1回调函数实现类
package org.myClass;

import com.sun.jna.Pointer;
import org.api.HidPosAPI;

import javax.swing.*;
//回调函数的实现类,具体实现数据的发送到jTeextArea中去
public class MyCallback implements HidPosAPI.ReceivePosData {
    public JTextArea jTextArea;

    public MyCallback(JTextArea jTextArea) {
        this.jTextArea = jTextArea;
    }

    public MyCallback() {
    }

    @Override
    public int callback(Pointer pData, int pDataLength, byte cResult) {
        String data = pData.getString(0);
        //
        SwingUtilities.invokeLater(()->{
            jTextArea.append(data+"\n");
        });
        return 0;
    }
}
7.2重点:对二维码模块的识别和把数据传输到swing界面上的JTextArea文本框中
  //采用JNA调用第三方dll文件
        //

        IntByReference ref = new IntByReference();
        HidPosAPI myLibrary = HidPosAPI.hidPos;

        //dll类加载路径
        System.out.println("当前dll加载路径:"+System.getProperty("java.library.path"));
        boolean idxx = HidPosAPI.hidPos.OpenDevice(VENDOR_ID, PRODUCT_ID);
        if (idxx) {
            //
            boolean connectStaus = HidPosAPI.hidPos.ConnectStaus();
            if (connectStaus){
                String str = "hh";
                boolean getDeviceSn = HidPosAPI.hidPos.GetDeviceSn(str, ref);
                if (getDeviceSn){
                    System.out.println("当前设备的SN号: "+ref.getValue());
                    //
                    MyCallback myCallback = new MyCallback();
                    myLibrary.SetCallback(myCallback);
                }else {
                    System.out.println("设备状态信息异常:--》"+getDeviceSn);
                    HidPosAPI.hidPos.CloseDevice();
                }

            }else {
                System.out.println("设备状态信息异常:--》"+connectStaus);
                HidPosAPI.hidPos.CloseDevice();

            }
        }
        if (!idxx)
            HidPosAPI.hidPos.CloseDevice();

8.回忆项目中我面临的难点

 8.1 接口类的相对路径的设置(lib前不要/),
   public static HidPosAPI hidPos = Native.load("lib/HidPosApi", HidPosAPI.class);

在寻找文件路径的时候,感觉这个加载jvm中的参数比较重要

 

 

在寻找这个文件的时候要有

jna依赖

   <groupId>net.java.dev.jna</groupId>
            <artifactId>jna</artifactId>
            <version>5.13.0</version>
        </dependency>

在<build>中要有这个打包插件,

<resources>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>lib/**</include>
                </includes>
                <filtering>false</filtering>
            </resource>
        </resources>

顺便提一下Maven插件

<plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>9</source>
                    <target>9</target>
                </configuration>
            </plugin>
8.2回忆遇到的问题

8.2.1 在debug调试的时候遇到了提示变量未初始化,然后找了很久,找到一个文件为找到(eutc.tte字体文件),最终查询了win10已经丢弃这个文件了,在jdk9中需要这个文件,于是我将jdk换成17的就没要求了(当然是在Windows系统中)

换jdk的步骤:1.更换系统变量  JAVA_HOME17(前提配好了jdk多环境配置)(后面发现,跟jdk没关系)

                

                        2.项目中的更换jdk

 8.2.2  要是Maven中刷不出jna的依赖,就用本地jar包导入

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值