项目接入阿里云内容审核增强版本,文本审核与图片审核。第三方审核

这篇具有很好参考价值的文章主要介绍了项目接入阿里云内容审核增强版本,文本审核与图片审核。第三方审核。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

为什么要引入

        自己的项目新增了一个社区的服务,社区可以发布文章,浏览文章,类似一些平台的文章管理的功能。那么发布的文章肯定是需要审核的,但是又不能全部为人工审核,这样的话效率太低也忙不过来,所以就想着先引入一个自动审核,只有当自动审核不通过之后,才进行人工审核。

引入阿里云内容审核

首先介绍一下内容安全:

        内容安全是识别服务,支持对图片、视频、文本、语音等对象进行多样化场景检测,有效降低内容违规风险。

        目前很多平台都支持内容检测,如阿里云、腾讯云、百度AI、网易云等国内大型互联网公司都对外提供了API。

        我因为已经在阿里云购买了云服务器,相对来说比较方便一些,当然阿里云提供的这个服务是收费的,但是不贵,开发测试是可以接收的。

        阿里云收费标准:收费标准链接

准备

在使用内容检测API之前,需要先注册阿里云账号,然后需要创建,记住自己的accesskey和secret。

项目接入阿里云内容审核增强版本,文本审核与图片审核。第三方审核,阿里云,云计算

这个创建一般一个账户只能绑定两个,也是一种可以访问你的用户的一个账号密码,可以供外接平台使用,这个密码创建之后是隐藏的,难以找到,所以建议第一次创建好了之后就记录下来。

具体实现

那下面我们就来说明一下这个具体实现。

首先这个内容安全的接入分为内容安全增强版和内容安全1.0。这个区别就是内容安全1.0是需要企业认证的,也就是说个人用不了。所以你没有认证是无法调用内容安全的sdk的。

这里面注意切换版本

当然我也把文档地址给大家。大家也可以自己去看。如何调用文本检测接口进行文本内容审核_内容安全(Content Moderation)-阿里云帮助中心

项目接入阿里云内容审核增强版本,文本审核与图片审核。第三方审核,阿里云,云计算

下面我就来结合代码说明一下。

1.添加依赖

<!--        增强版-->
        <dependency>
            <groupId>com.aliyun</groupId>
            <artifactId>green20220302</artifactId>
            <version>1.1.0</version>
        </dependency>
<!--        本地-->
        <!--安装OSS SDK-->
        <dependency>
            <groupId>com.aliyun.oss</groupId>
            <artifactId>aliyun-sdk-oss</artifactId>
            <version>3.16.3</version>
        </dependency>
2.文本审核代码工具类

注意,这里的accesskey和secret可以直接在这里写自己的,也可以在nacos或者本地进行配置,反正能读取就行了。

方法返回值为map是因为后续需要调用该方法,通过它的返回结果来判断是否需要进一步的人工审核。

Config是一些配置,用于连接到阿里云这个接口的配置。

这里为了省劲,测试的时候只用了hashmap,追求线程安全的也可以用一下concurrent包下的map类。

同时不要忘了设置set_service,这个如果自己不设置的话是不成功的。

项目接入阿里云内容审核增强版本,文本审核与图片审核。第三方审核,阿里云,云计算

package com.neu.base.aliyun;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.aliyun.green20220302.Client;
import com.aliyun.green20220302.models.TextModerationRequest;
import com.aliyun.green20220302.models.TextModerationResponse;
import com.aliyun.green20220302.models.TextModerationResponseBody;
import com.aliyun.teaopenapi.models.Config;
import com.aliyun.teautil.models.RuntimeOptions;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.exceptions.ServerException;
import com.aliyuncs.green.model.v20180509.TextScanRequest;
import com.aliyuncs.http.FormatType;
import com.aliyuncs.http.HttpResponse;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.profile.IClientProfile;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;

import java.util.*;

@Getter
@Setter
@Component
@ConfigurationProperties(prefix = "aliyun")
public class GreenTextScan {
	
    private String accessKeyId;
    private String secret;
    //传入文本,返回一个map,包含了文本的审核结果
    public Map greenTextScanStrongVersion(String content) throws Exception {
        System.out.println(accessKeyId);
        Config config = new Config();
        System.out.println("");
        config.setAccessKeyId(accessKeyId);
        config.setAccessKeySecret(secret);
        //接入区域和地址请根据实际情况修改
        config.setRegionId("cn-shanghai");
        config.setEndpoint("green-cip.cn-shanghai.aliyuncs.com");
        //连接时超时时间,单位毫秒(ms)。
        config.setReadTimeout(6000);
        //读取时超时时间,单位毫秒(ms)。
        config.setConnectTimeout(3000);
        // 注意,此处实例化的client请尽可能重复使用,避免重复建立连接,提升检测性能
        Client client = new Client(config);

        // 创建RuntimeObject实例并设置运行参数。
        RuntimeOptions runtime = new RuntimeOptions();
        runtime.readTimeout = 10000;
        runtime.connectTimeout = 10000;

        //检测参数构造
        Map<String,Object> resultMap= new HashMap<>();
        JSONObject serviceParameters = new JSONObject();
        //设置要审核的内容
        serviceParameters.put("content", content);

        if (serviceParameters.get("content") == null || serviceParameters.getString("content").trim().length() == 0) {
            System.out.println("text moderation content is empty");
            resultMap.put("suggestion", "检测内容为空");
            return resultMap;
        }

        TextModerationRequest textModerationRequest = new TextModerationRequest();
        /*
        文本检测service:内容安全控制台文本增强版规则配置的serviceCode,示例:chat_detection
        */
        textModerationRequest.setService("comment_detection");
        textModerationRequest.setServiceParameters(serviceParameters.toJSONString());
        try {
            // 调用方法获取检测结果。
            TextModerationResponse response = client.textModerationWithOptions(textModerationRequest, runtime);

            // 自动路由。
            if (response != null) {
                // 服务端错误,区域切换到cn-beijing。
                if (500 == response.getStatusCode() || (response.getBody() != null && 500 == (response.getBody().getCode()))) {
                    // 接入区域和地址请根据实际情况修改。
                    config.setRegionId("cn-beijing");
                    config.setEndpoint("green-cip.cn-beijing.aliyuncs.com");
                    client = new Client(config);
                    response = client.textModerationWithOptions(textModerationRequest, runtime);
                }

            }
            // 打印检测结果。
            if (response != null) {
                if (response.getStatusCode() == 200) {
                    TextModerationResponseBody result = response.getBody();
                    System.out.println(JSON.toJSONString(result));
                    Integer code = result.getCode();
                    if (code != null && code == 200) {
                        TextModerationResponseBody.TextModerationResponseBodyData data = result.getData();
                        if(data.getLabels().isEmpty()&& data.getReason().isEmpty()){
                            resultMap.put("suggestion", "pass");
                            return resultMap;
                        }
                        System.out.println("labels = [" + data.getLabels() + "]");
                        System.out.println("reason = [" + data.getReason() + "]");
                        String labels=data.getLabels();
                        String reason=data.getReason();
                        resultMap.put("labels", labels);
                        resultMap.put("reason", reason);
                        resultMap.put("suggestion", "block");
                        return resultMap;

                    } else {
                        System.out.println("text moderation not success. code:" + code);
                        String information="text moderation not success :"+code;
                        resultMap.put("information", "review");
                        return resultMap;
                    }
                } else {
                    System.out.println("response not success. status:" + response.getStatusCode());
                    String information="response not success"+response.getStatusCode();
                    resultMap.put("information", "review");
                    return resultMap;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;

    }


}
工具类测试

我们只需要把写的工具类交给springboot容器管理,然后进行注入测试就可以了。一些springboottest注解我就不写了,这里只展示测试代码。

  @Autowired
    private GreenTextScan greenTextScan;  

/**
     * 测试文本内容审核
     */
    @Test
    public void testScanText() throws Exception {
        Map map = greenTextScan.greenTextScanStrongVersion("neu帅不帅");
        System.out.println(map.get("suggestion"));
        System.out.println(map);
    }

测试结果:

项目接入阿里云内容审核增强版本,文本审核与图片审核。第三方审核,阿里云,云计算

返回结果是pass的话只需要从自己的调用方法里面取出,然后进行判断就可以了。

当然你如果这么说

  @Test
    public void testScanText() throws Exception {
        Map map = greenTextScan.greenTextScanStrongVersion("neu是不是傻子xuexiao");
        System.out.println(map.get("suggestion"));
        System.out.println(map);
    }

项目接入阿里云内容审核增强版本,文本审核与图片审核。第三方审核,阿里云,云计算

当然奥,我肯定热爱neu奥。

为了证明,我痛花两条测试,日子艰难,痛失几分钱,希望大家给个关注和点赞吧哈哈哈,当然这样大家也可以看看自己是否测试成功。

管理控制台 - 用量统计 (aliyun.com)

项目接入阿里云内容审核增强版本,文本审核与图片审核。第三方审核,阿里云,云计算

3.图片审核代码工具类

        图片审核我测试过公网的图片,好像符合https协议的才可以,而我的域名是http协议的,测试一下好像即使公网可以访问但也出了问题,所以这里我采用的是本地图片测试。也就是说把我放在公网minio里的文件进行下载到本地,设置成临时文件,由于文件的存活周期所以也不用担心。

        而我们实际应用当中可能一次审核多张照片,但是这个功能在内容安全1.0版本是可以实现的,但是增强版不可以,无奈我们只能多次调用该接口,下面给出工具类。

同样不要忘了设置服务类型规则的设置。

public class GreenImageScan {

    private String accessKeyId;
    private String secret;

    //服务是否部署在vpc上
    public static boolean isVPC = false;

    //文件上传token endpoint->token
    public static Map<String, DescribeUploadTokenResponseBody.DescribeUploadTokenResponseBodyData> tokenMap = new HashMap<>();

    //上传文件请求客户端
    public static OSS ossClient = null;

    //内容增强扫描本地

    public Map imageScanStrongLocalVersion(String url) throws Exception{
        /**
         * 阿里云账号AccessKey拥有所有API的访问权限,建议您使用RAM用户进行API访问或日常运维。
         * 常见获取环境变量方式:
         * 方式一:
         *     获取RAM用户AccessKey ID:System.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID");
         *     获取RAM用户AccessKey Secret:System.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET");
         * 方式二:
         *     获取RAM用户AccessKey ID:System.getProperty("ALIBABA_CLOUD_ACCESS_KEY_ID");
         *     获取RAM用户AccessKey Secret:System.getProperty("ALIBABA_CLOUD_ACCESS_KEY_SECRET");
         */
        // 接入区域和地址请根据实际情况修改。
        ImageModerationResponse response = invokeLocalFunction(url,accessKeyId, secret, "green-cip.cn-shanghai.aliyuncs.com");
        Map<String,Object> resultMap=new HashMap<>();
        try {
            // 自动路由。
            if (response != null) {
                //区域切换到cn-beijing。
                if (500 == response.getStatusCode() || (response.getBody() != null && 500 == (response.getBody().getCode()))) {
                    // 接入区域和地址请根据实际情况修改。
                    response = invokeLocalFunction(url,accessKeyId, secret, "green-cip.cn-beijing.aliyuncs.com");
                }
            }
            // 打印检测结果。
            if (response != null) {
                if (response.getStatusCode() == 200) {
                    ImageModerationResponseBody body = response.getBody();
                    System.out.println("requestId=" + body.getRequestId());
                    System.out.println("code=" + body.getCode());
                    System.out.println("msg=" + body.getMsg());
                    resultMap.put("code",body.getCode());
                    resultMap.put("msg",body.getMsg());
                    resultMap.put("requestId",body.getRequestId());

                    if (body.getCode() == 200) {
                        ImageModerationResponseBody.ImageModerationResponseBodyData data = body.getData();
                        System.out.println("dataId=" + data.getDataId());
                        List<ImageModerationResponseBody.ImageModerationResponseBodyDataResult> results = data.getResult();
                        for (ImageModerationResponseBody.ImageModerationResponseBodyDataResult result : results) {
                            System.out.println("label=" + result.getLabel());
                            System.out.println("confidence=" + result.getConfidence());
                        }
                        resultMap.put("suggestion","pass");
                        return resultMap;
                    } else {
                        System.out.println("image moderation not success. code:" + body.getCode());
                        resultMap.put("information","image moderation not success. code:" + body.getCode());
                        resultMap.put("suggestion","review");
                        return resultMap;
                    }
                } else {
                    System.out.println("response not success. status:" + response.getStatusCode());
                    resultMap.put("information","response not success. status:" + response.getStatusCode());
                    resultMap.put("suggestion","block");
                    return resultMap;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * 创建请求客户端
     *
     * @param accessKeyId
     * @param accessKeySecret
     * @param endpoint
     * @return
     * @throws Exception
     */
    public static Client createClient(String accessKeyId, String accessKeySecret, String endpoint) throws Exception {
        Config config = new Config();
        config.setAccessKeyId(accessKeyId);
        config.setAccessKeySecret(accessKeySecret);
        // 设置http代理。
        //config.setHttpProxy("http://10.10.xx.xx:xxxx");
        // 设置https代理。
        //config.setHttpsProxy("https://10.10.xx.xx:xxxx");
        // 接入区域和地址请根据实际情况修改
        config.setEndpoint(endpoint);
        return new Client(config);
    }

    /**
     * 创建上传文件请求客户端
     *
     * @param tokenData
     * @param isVPC
     */
    public static void getOssClient(DescribeUploadTokenResponseBody.DescribeUploadTokenResponseBodyData tokenData, boolean isVPC) {
        //注意,此处实例化的client请尽可能重复使用,避免重复建立连接,提升检测性能。
        if (isVPC) {
            ossClient = new OSSClientBuilder().build(tokenData.ossInternalEndPoint, tokenData.getAccessKeyId(), tokenData.getAccessKeySecret(), tokenData.getSecurityToken());
        } else {
            ossClient = new OSSClientBuilder().build(tokenData.ossInternetEndPoint, tokenData.getAccessKeyId(), tokenData.getAccessKeySecret(), tokenData.getSecurityToken());
        }
    }


    /**
     * 上传文件
     *
     * @param filePath
     * @param tokenData
     * @return
     * @throws Exception
     */
    public static String uploadFile(String filePath, DescribeUploadTokenResponseBody.DescribeUploadTokenResponseBodyData tokenData) throws Exception {
        //将文件路径 filePath 根据点号 . 进行分割
        String[] split = filePath.split("\\.");
        String objectName;
        if (split.length > 1) {
            objectName = tokenData.getFileNamePrefix() + UUID.randomUUID() + "." + split[split.length - 1];
        } else {
            objectName = tokenData.getFileNamePrefix() + UUID.randomUUID();
        }
        PutObjectRequest putObjectRequest = new PutObjectRequest(tokenData.getBucketName(), objectName, new File(filePath));
        ossClient.putObject(putObjectRequest);
        return objectName;
    }


    public static ImageModerationResponse invokeLocalFunction(String url,String accessKeyId, String accessKeySecret, String endpoint) throws Exception {
        //注意,此处实例化的client请尽可能重复使用,避免重复建立连接,提升检测性能。
        Client client = createClient(accessKeyId, accessKeySecret, endpoint);
        RuntimeOptions runtime = new RuntimeOptions();

        //本地文件的完整路径,例如D:\localPath\exampleFile.png。
        String filePath = url;
        String bucketName = null;
        DescribeUploadTokenResponseBody.DescribeUploadTokenResponseBodyData uploadToken = tokenMap.get(endpoint);
        //获取文件上传token
        if (uploadToken == null || uploadToken.expiration <= System.currentTimeMillis() / 1000) {
            DescribeUploadTokenResponse tokenResponse = client.describeUploadToken();
            uploadToken = tokenResponse.getBody().getData();
            bucketName = uploadToken.getBucketName();
        }
        //上传文件请求客户端
        getOssClient(uploadToken, isVPC);

        //上传文件
        String objectName = uploadFile(filePath, uploadToken);
        // 检测参数构造。
        Map<String, String> serviceParameters = new HashMap<>();
        //文件上传信息
        serviceParameters.put("ossBucketName", bucketName);
        serviceParameters.put("ossObjectName", objectName);
        serviceParameters.put("dataId", UUID.randomUUID().toString());

        ImageModerationRequest request = new ImageModerationRequest();
        // 图片检测service:内容安全控制台图片增强版规则配置的serviceCode,示例:baselineCheck
        request.setService("baselineCheck");
        request.setServiceParameters(JSON.toJSONString(serviceParameters));

        ImageModerationResponse response = null;
        try {
            response = client.imageModerationWithOptions(request, runtime);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return response;
    }
}

这里我的思路是传入一个url,invokelocal那里的参数和imageScanlocal那里的参数传入的都是本地图片的绝对路径。

而我们这个图片拉取的时候是从minio里面拉取的,所以我们还需要从minio里下载图片,这个下载图片的方法我目前就先不给出了,额,因为我觉得不算是重点,如果大家需要的话可以评论留言,我会给出。

测试代码

这里的url我没有给出,因为图片是需要本地的,我把url下载的图片保存到本地了,这里的url是minio的访问图片。

    @Test
    public void testScanImage() throws Exception {
        String url="";
        byte[] bytes = fileStorageService.downLoadFile(url);
        //在本地创建一个临时文件,将bytes写入到临时文件中
        // 获取文件扩展名
        //文件扩展名
        String fileExtension = url.substring(url.lastIndexOf("."));
        System.out.println(fileExtension);
        Path tempFilePath ;
        try {
            tempFilePath = Files.createTempFile("temp", fileExtension);
        } catch (IOException e) {
            e.printStackTrace();
            return;
        }
        // 将字节流写入本地临时文件
        try {
            Files.write(tempFilePath, bytes, StandardOpenOption.CREATE);
            System.out.println("文件下载成功,保存在:" + tempFilePath.toString());
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("文件下载失败");
        }
        String tempFileUrl = tempFilePath.toString();
        Map map = greenImageScan.imageScanStrongLocalVersion(tempFileUrl);

        System.out.println(map.get("suggestion"));
        System.out.println(map);
    }

这个就是测试结果了。又痛失我大洋了家人,给赞啊!

项目接入阿里云内容审核增强版本,文本审核与图片审核。第三方审核,阿里云,云计算

当然违规图片我就不给了,哈哈哈,好青年一枚奥。文章来源地址https://www.toymoban.com/news/detail-787411.html

到了这里,关于项目接入阿里云内容审核增强版本,文本审核与图片审核。第三方审核的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Spring Cloud】新闻头条微服务项目:文章内容安全审核(新增DFA+OCR过滤敏感词需求)

    个人简介:  📦个人主页:赵四司机 🏆学习方向:JAVA后端开发  ⏰往期文章:SpringBoot项目整合微信支付 🔔博主推荐网站:牛客网 刷题|面试|找工作神器 📣种一棵树最好的时间是十年前,其次是现在! 💖喜欢的话麻烦点点关注喔,你们的支持是我的最大动力。 前言:

    2023年04月08日
    浏览(94)
  • 小程序内容安全检测校验文本/图片违规

    最近微信小程序遇到内容安全检测接口校验文本/图片是否含有敏感内容。 其实一开始真的很懵逼,为什么会遇到这种问题,原来现在我们所上传的图片、文本需要经过规定合法合规才能上传。比如说是色情、低俗,违法政治言论等。也许我们平常在开发的时候和运营的时候

    2024年02月13日
    浏览(47)
  • Java poi之word文本图片内容提取

    应公司需求,需实现以下功能 word文本内容的替换; word文本内容的提取; word文档中图片的提取存放 此文章将使用Apache POI实现Word文档中文本内容及图片的提取; Apache POI 是基于 Office Open XML 标准(OOXML)和 Microsoft 的 OLE 2 复合文档格式(OLE2)处理各种文件格式的开源项目。

    2024年02月10日
    浏览(53)
  • Java poi之Excel文本图片内容提取

    应公司需求,需实现以下功能 Excel文本内容的替换; Excel文本内容的提取; Excel中图片的提取存放 此文章将使用Apache POI实现Excel文件中文本内容及图片的提取; Apache POI 是基于 Office Open XML 标准(OOXML)和 Microsoft 的 OLE 2 复合文档格式(OLE2)处理各种文件格式的开源项目。

    2024年02月05日
    浏览(82)
  • React实现文本框输入文字内容动态给图片添加文字信息(多个)并生成新的图片

    收到这个需求的时候,我的内心是崩溃的,脑子里已经跑过一万匹草泥马,内心想这种事为啥不交给ps去做,哪怕是手机里图片编辑也可以做到吧,专业的事交给专业的工具去干不就好了,何必出这种XX需求。后来想想就释然了,反正拿钱干活,干啥不是干,只要给钱,再XX的

    2024年02月06日
    浏览(54)
  • 搜文本搜位置搜图片,1小时玩转阿里云 Elasticsearch

    作者:朱杰、奚悦、黄宇 AI 和搜索的整合已成为下一代搜索引擎的发展趋势,技术革新的浪潮下,你是否想抓住搜索领域的新机会,增强 AI 产品力与技术竞争力? 想学习搜索引擎技术的你,是否面临这样的困惑: 初学实操搭建,缺少指导解惑,刚起步就困在原地 对基础搜

    2024年02月10日
    浏览(32)
  • JAVA读取(DOC、DOCX、PDF、PPT、PPTX)文件文本内容及图片

    温馨提示:有很多方法均可以解析这些常见的文件,以下内容使用的是apache-poi + apache-pdfbox实现的。         关于文档解析,在网上搜索了很久,无奈内容太过繁杂,找不到合适的代码,一大半都是只支持文本。没办法,只能自己在网上一点一点CV了,最终提取了这些代码

    2024年02月03日
    浏览(51)
  • uni-sec-check内容安全unicloud公共模块,校验微信小程序文本内容安全识别和图片智能鉴黄,uniapp进阶

    uni-sec-check内容安全是unicloud封装了微信小程序的免费接口,文本内容安全识别(msgSecCheck)和音视频内容安全识别(mediaCheckAsync),如果我没选择使用uniapp+unicloud开发的话,可以轻松从插件市场引入uni-sec-check公共模块,完成内容安全检测,包含图片和文字检测,下面就针对文

    2024年02月04日
    浏览(51)
  • openCV实践项目:图片文本检测

    上一期我们通过对实验:银行卡卡号识别 加深了对前面所学openCV图像处理的一些理解 openCV实践项目:银行卡卡号识别_老师我作业忘带了的博客-CSDN博客 本次图片文本检测相对于要容易一些,内容如下:   把一个这样的图片,通过仿射变换转换成那样的图片。 然后再通过

    2024年02月07日
    浏览(41)
  • 【itext7】itext7操作PDF文档之添加段落文本内容、添加List列表、添加Image图片、添加Table表格

    这篇文章,主要介绍itext7操作PDF文档之添加段落文本内容、添加List列表、添加Image图片、添加Table表格。 目录 一、itext7操作PDF内容 1.1、添加段落文本内容 1.2、添加列表内容 1.3、添加图片 1.4、添加表格 (1)列宽采用点单位(pt点单位) (2)采用百分比单位(%百分比) it

    2024年02月16日
    浏览(50)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包