自定义博客皮肤VIP专享

*博客头图:

格式为PNG、JPG,宽度*高度大于1920*100像素,不超过2MB,主视觉建议放在右侧,请参照线上博客头图

请上传大于1920*100像素的图片!

博客底图:

图片格式为PNG、JPG,不超过1MB,可上下左右平铺至整个背景

栏目图:

图片格式为PNG、JPG,图片宽度*高度为300*38像素,不超过0.5MB

主标题颜色:

RGB颜色,例如:#AFAFAF

Hover:

RGB颜色,例如:#AFAFAF

副标题颜色:

RGB颜色,例如:#AFAFAF

自定义博客皮肤

-+
  • 博客(147)
  • 收藏
  • 关注

原创 自动化测试--利用pytest实现整条业务链路测试

前面一章讲解了单个接口的测试,但是实际项目中,因为权限和登录状态的限制,大部分接口没办法直接访问到,这时候我们想访问到一个系统的接口,就需要模拟用户登录拿到用户的token和所拥有的权限之后再将这些信息作为参数传递给下游进行测试举个栗子:定义接口的基础 URL。所有需要这个 URL 的测试函数都可以使用这个 fixture。请求访问登录接口:return {之后请求访问登录页面这样我们就能拿到用户的token信息将用户的请求头信息按照系统存放的规则存入请求头中params = {

2024-05-17 17:15:59 163

原创 接口自动化测试工具-----pytest

以上代码的逻辑就是 根据用户传过来的name字段查询数据库,之后拿到用户信息之后和password字段作对比,如果密码正确就返回success 失败就是 error。首先确保安装了Python环境,首先,你需要确保已安装 Python 和 Pip。如果还没有安装,可以从。下载并安装最新版本的 Python。安装过程中请确保选中“Add Python to PATH”选项。之后搭建一个后端项目,以springboot项目为例。编写一个简单的模拟登录的接口,之后启动项目。之后查看是否安装成功。

2024-05-17 15:42:59 233 2

原创 百万级别mysql性能耗时自测

首次查询不走索引的条件下进行查询 耗时为 700ms左右 之后查询同样的sql,稳定在430ms (可能mysql自带的缓存机制)有索引(主键索引):首次查询140MS 之后稳定在2ms加入索引的耗时为 username字段的索引时间+主键索引的耗时 因为测试中name字段重复度较高 因此name索引查询几乎不耗时间,最终结果为4ms。

2024-04-21 17:27:36 243

原创 项目开发中用户数据脱敏如何实现?

利用 CONCAT()、LEFT()和RIGHT() 字符串函数组合使用,请看下面具体实现。-- RIGHT(str,len):从字符串str 开始,返回最右len 字符。-- CONCAT(str1,str2,…):返回结果为连接参数产生的字符串。-- LEFT(str,len):返回从字符串str 开始的len 最左字符。2.自定义工具类 实现统一管理进行数据处理。提供两种方式:正则表达式和长度判定形式。3.定义枚举类 获取脱敏信息的类型。举个栗子:对用户姓名进行脱敏。通过自定义注解的形式实现。

2024-04-06 16:55:33 314

原创 深入理解Netty以及为什么项目中要使用?(七)Netty中ByteBuf详解

在Netty中,还有另外一个比较常见的对象ByteBuf,它其实等同于Java Nio中的ByteBuffer,但是ByteBuf对Nio中的ByteBuffer的功能做了很作增强,下面我们来简单了解一下ByteBuf。下面这段代码演示了ByteBuf的创建以及内容的打印,这里显示出了和普通ByteBuffer最大的区别之一,就是ByteBuf可以自动扩容,默认长度是256,如果内容长度超过阈值时,会自动触发扩容第一种,创建基于堆内存的ByteBuf第二种,创建基于直接内存(堆外内存)的ByteBuf(

2024-03-23 21:41:24 940

原创 深入理解Netty以及为什么项目中要使用?(六)Netty核心组件实例

前面我们讲过NIO多路复用的设计模式之Reactor模型,Reactor模型的主要思想就是把网络连接、事件分发、任务处理的职责进行分离,并且通过引入多线程来提高Reactor模型中的吞吐量。其中包括三种Reactor模型单线程单Reactor模型多线程单Reactor模型多线程多Reactor模型在Netty中,可以非常轻松的实现上述三种线程模型,并且Netty推荐使用主从多线程模型,这样就可以轻松的实现成千上万的客户端连接的处理。

2024-03-23 21:23:57 1018

原创 深入理解Netty以及为什么项目中要使用?(五)Netty核心组件

服务端启动初始化Boss和Worker线程组,Boss线程组负责监听网络连接事件,当有新的连接建立时,Boss线程会把该连接Channel注册绑定到Worker线程Worker线程组会分配一个EventLoop负责处理该Channel的读写事件,每个EventLoop相当于一个线程。通过Selector进行事件循环监听。当客户端发起I/O事件时,服务端的EventLoop将就绪的Channel分发给Pipeline,进行数据的处理。

2024-03-18 17:05:55 922

原创 深入理解Netty以及为什么项目中要使用?(四)Netty详解

Netty的整体工作机制如下,整体设计就是前面我们讲过的多线程Reactor模型,分离请求监听和请求处理,通过多线程分别执行具体的handler。

2024-03-18 14:45:14 356

原创 深入理解Netty以及为什么项目中要使用?(三)Netty框架的使用

Netty其实就是一个高性能NIO框架,所以它是基于NIO基础上的封装,本质上是提供高性能网络IO通信的功能。Netty提供了上述三种Reactor模型的支持,我们可以通过Netty封装好的API来快速完成不同Reactor模型的开发,这也是为什么大家都选择Netty的原因之一,除此之外,Netty相比于NIO原生API,它有以下特点:提供了高效的I/O模型、线程模型和时间处理机制。

2024-03-18 10:50:48 821

原创 深入理解Netty以及为什么项目中要使用?(二)Reactor模型

了解了NIO多路复用后,就有必要再和大家说一下Reactor多路复用高性能I/O设计模式,Reactor本质上就是基于NIO多路复用机制提出的一个高性能IO设计模式,它的核心思想是把响应IO事件和业务处理进行分离,通过一个或者多个线程来处理IO事件,然后将就绪得到事件分发到业务处理handlers线程去异步非阻塞处理将I/O事件发派给对应的Handler处理客户端连接请求执行非阻塞读/写这是最基本的单Reactor单线程模型。

2024-03-18 08:54:22 919

原创 深入理解Netty以及为什么项目中要使用?(一) IO模型

在linux中,内核把所有的外部设备都当成是一个文件来操作,对一个文件的读写会调用内核提供的系统命令,返回一个fd(文件描述符)。而对于一个socket的读写也会有相应的文件描述符,称为socketfd。常见的IO多路复用方式有。

2024-03-14 21:26:47 840

原创 分布式架构下 网络通信的底层实现原理(三)

阻塞是指进程在等待某个事件发生之前的等待状态,它是属于操作系统层面的调度,我们通过下面操作来追踪Java程序中有多少程序,每一个线程对内核产生了哪些操作。这个时候通过tail -f out.33321这个文件,发现被阻塞的poll()方法,被POLLIN事件唤醒了,表示监听到了一次连接。5.打开out.33321这个文件(主线程后面的一个文件),shift+g到该文件的尾部,可以看到如下内容。从这个代码中可以看到,Socket的accept方法最终是调用系统的poll函数来实现线程阻塞的。

2024-03-11 16:39:46 790

原创 分布式架构下 网络通信的底层实现原理(二)

mac地址就好像个人的身份证号,人的身份证号和人户口所在的城市,出生的日期有关,但是和人所在的位置没有关系,人是会移动的,知道一个人的身份证号,并不能找到它这个人,mac地址类似,它是和设备的生产者,批次,日期之类的关联起来,知道一个设备的mac,并不能在网络中将数据发送给它,除非它和发送方的在同一个网络内。操作系统知道了目标IP地址后,就开始根据这个IP来寻找目标机器,而目标服务器一定是部署在不同的地方,这种跨网络节点的访问,需要经过网关(所谓网关就是一个网络到另外一个网络的关口)。

2024-03-08 13:34:25 908

原创 分布式架构下 网络通信的底层实现原理(一)

当我们通过浏览器访问一个网址时,一段时间后该网址会渲染出访问的内容,这个过程是怎么实现的呢?这种通信方式是基于http协议实现的,那么什么是协议?像一个中国人和一个外国人交流时,一个讲英语另一个讲解中文,肯定是无法正常交流。因此两个计算机之间要实现数据通信,必须遵循同一种协议协议的组成组成一个协议,需要具备三个要素:语法,就是这一段内容要符合一定的规则和格式。例如,括号要成对,结束要使用分号等。语义,就是这一段内容要代表某种意义。例如数字减去数字是有意义的,数字减去文本一般来说就没有意义。

2024-03-08 12:44:30 398

原创 springboot开启mybatis二级缓存

首先选一个缓存框架(EhCache )EhCache 是一个广泛使用的开源 Java 分布式缓存库,主要用于提高应用程序的性能,减少数据库访问次数,通过缓存频繁读取的数据来实现。它可以作为 Hibernate、Spring、MyBatis 等框架的缓存提供者,用于提升这些框架在数据处理方面的性能。可以缓存来自数据库的数据,当应用程序需要这些数据时,可以直接从缓存中读取,而不是每次都查询数据库。这减少了数据库的访问压力和响应时间。的 EhCache 配置文件。对于 Mapper 接口,可以通过在接口上添加。

2024-02-14 13:43:56 597

原创 ReentrantLock的公平锁机制如何实现的?(源码理解)

接下来,方法获取头节点的下一个节点(s = h.next),并检查该节点是否为null或者该节点的线程不是当前线程(s.thread!是指多个线程按照申请锁的顺序来获取锁,即线程会直接进入队列中排队,队列中的第一个线程才能获得锁。这种机制确保了线程获取锁的公平性,避免了“饥饿”现象,即等待时间过长的线程无法获取到锁的情况。默认情况下,ReentrantLock是非公平的,这是因为非公平锁的效率通常要高于公平锁。在没有特殊需求的情况下,一般推荐使用非公平锁,因为它能提供更高的性能。,则表示成功获取锁。

2024-02-04 16:36:42 549

原创 利用ReentrantLock解决死锁----以哲学家问题为例

每位哲学家需要两根筷子才能吃饭,而每根筷子只能被一位哲学家使用,这就导致了资源的互斥性。在吃饭时会拿起左右两边的筷子,并保持持有直到吃完饭。如果筷子不足,他们就会等待,在这个场景中,哲学家不会从其他哲学家手中抢夺筷子,只有当筷子可用时,他们才会拿起筷子。可能发生所有哲学家都拿着手中的一根筷子,同时等待另一根筷子的情况,这样就形成了一个环路,每个人都在等待其他人释放资源,导致所有人都无法继续进餐,即发生了死锁。运行结果: 发现代码不会继续执行 ,此时已经发生了死锁。1. 采用synchronized写法。

2024-02-04 15:52:53 419

原创 ReentrantLock相较于synchronized的区别---锁超时(源码理解)

ReentrantLock支持设置锁等待超时时间,即在一定时间内未获得锁的线程可以选择放弃等待,避免死锁arg表示获取锁时的参数,表示最大等待时间(以纳秒为单位)。这个方法具体逻辑如下:首先,线程会尝试获取锁。如果锁当前未被其他线程持有,则线程直接获取锁并返回true。如果锁被其他线程持有,线程将进入等待队列,并启动一个计时器,设置超时时间。在等待过程中,线程可能会被其他线程中断,或者等待时间达到设定的超时时间。如果发生这些情况,线程将停止等待,并返回false。

2024-02-04 15:24:03 372

原创 ReentrantLock相较于synchronized有哪些区别(一)?

可重入是指同一个线程如果首次获得了这把锁,那么因为它是这把锁的拥有者,因此有权利再次获取这把锁。如果是不可重入锁,那么第二次获得锁时,自己也会被锁挡住。调用lockInterruptibly()方法执行打断。相对于 synchronized 它具备如下特点。与 synchronized 一样,都支持。运行结果:可以发现全部加锁成功了。

2024-02-04 14:38:46 322

原创 MySQL 中有关 NULL

原因是:MySQL 中 sum 函数没统计到任何记录时,会返回 null 而不是 0,可以使用 IFNULL 函数把 null 转换为 0;MySQL 中使用诸如 =、、> 这样的算数比较操作符比较 NULL 的结果总是 NULL,这种比较就显得没有任何意 义,需要使用 IS NULL、IS NOT NULL 或 ISNULL() 函数来比较。显然,这三条 SQL 语句的执行结果和我们的期望不同:虽然记录的 score 都是 NULL,但 sum 的结果应该是 0 才对;

2024-02-02 17:36:53 528

原创 项目中的空指针处理

• 对于 rightMethod 返回的 List,由于不能确认其是否为 null,所以在调用 size 方法获得列表大小之前,同样可以使用 Optional.ofNullable 包装一下返回 值,然后通过.orElse(Collections.emptyList()) 实现在 List 为 null 的时候获得一个空的 List,最后再调用 size 方法。• 对于 String 和字面量的比较,可以把字面量放在前面,比如"OK".equals(s),这样即使 s 是 null 也不会出现空指针异常;

2024-02-02 17:30:23 377

原创 项目安全-----加密算法实现

是使用相同的密钥进行加密和解密。使用对称加密算法来加密双方的通信的话,双方需要先约定一个密钥,加密方才能加密,接收方才能 解密。常用的加密算法,有 DES、3DES 和 AES,国密算法包括SM1,SM4和SM7。目前,使用 DES 来加密数据非常不安全。因此,在业务代码中要避免使用 DES 加密。而 3DES 算法,是使用不同的密钥进行三次 DES 串联调用,虽然解决 了 DES 不够安全的问题,但是比 AES 慢,也不太推荐。

2024-02-02 17:19:03 1987

原创 项目安全问题及解决方法------使用合适的算法

我们的密码保存方式: "password": "$2a$10$wPWdQwfQO2lMxqSIb6iCROXv7lKnQq5XdMO96iCYCj7boK9pk6QPC" //格式为:$$$ 第一个$后的 2a 代表算法版本,第二个$后的 10 是代价因子(默认是 10,代表 2 的 10 次方次哈希),第三个$后的 22 个字符是盐,再后面是摘要。制作 8 位密码长度的 MD5 彩虹表需要 5 个月,那么对于 BCrypt 来说,可能就需要几十年,大部分黑客应该都没有这个耐心。

2024-02-02 17:08:13 385

原创 项目安全问题及解决方法------用户密码处理

我们一般来说是不保存原始密码,这样即使被拖库也不会造成用户数据损失,一般来说我们通常会使用 MD5 加密后保存,但是大家对于MD5是使用是否 是正确的呢?所谓加密算法,是可以使用密钥把明文加密为密文,随后还可以使用密钥解密出明文,是双向的。不管多长的数据,使用 MD5 运算后得到的都是固定长度的摘要信息或指纹信息,无法再解密为原始数据。此时,黑客可以自己注册一个账号,使用一个简单的密码,比如 1,生成的密码是 "password": "55f312f84e7785aa1efa552acbf251db"

2024-02-02 17:03:28 351

原创 项目安全问题及解决方法-----xss处理

XSS 问题的根源在于,原本是让用户传入或输入正常数据的地方,被黑客替换为了 JavaScript 脚本,页面没有经过转义直接显示了这个数据,然后脚本就被 执行了。更严重的是,脚本没有经过转义就保存到了数据库中,随后页面加载数据的时候,数据中混入的脚本又当做代码执行了。黑客可以利用这个漏洞 来盗取敏感数据,诱骗用户访问钓鱼网站等。

2024-02-02 16:50:44 1324

原创 项目中的安全问题及解决方法-----动态脚本注入

但是假如我们传入的name参数是 haha';java.lang.System.exit(0);' 就可以达到关闭整个程序的目的。原因是,我们直接把代码和数据拼接在了一起。外部如 果构造了一个特殊的用户名先闭合字符串的单引号,再执行一条 System.exit 命令的话,就可以满足脚本不出错,命令被执行

2024-02-02 16:38:12 422

原创 自定义一个线程安全的生产者-消费者模型(大厂java面试题)

生产者-消费者模型的核心思想是通过阻塞队列和线程的等待和通知机制实现生产者和消费者之间的协作,确保生产者不会向满队列中添加消息,消费者不会从空队列中获取消息,从而有效地解决了多线程间的同步问题。方法1向队列中放入消息,如果队列中的数据满了就线程等待,方法2从队列中拿数据,如果队列空了就等待。如果你的需求比较简单,只是需要创建和管理一些基本的线程,第一种方式可能更简单和直观。:这个方法用于将消息放入队列。如果队列已满,生产者需要等待,直到队列有足够的空间。方法释放锁,并处于等待状态,直到队列中有足够的空间。

2024-02-01 14:30:06 392

原创 线程在操作系统以及java中的几种状态(源码分析)

例如,调用Object.wait()方法会使线程进入等待状态,直到其他线程调用Object.notify()或Object.notifyAll()方法唤醒它。(Blocked):当一个线程在执行过程中遇到某种情况,如等待输入、等待I/O操作完成等,它可能会暂时停止执行,进入阻塞状态。例如,调用Thread.sleep(long millis)方法会使线程进入限时等待状态,直到指定的时间过去或者被其他线程中断。当一个线程尝试获取一个已经被其他线程持有的锁时,它会被阻塞,直到该锁被释放。

2024-01-26 16:05:04 401 1

原创 如何利用线程打断机制优雅的实现日志监控?

然后,在main()方法中,我们启动了这个线程,并在1秒后调用了thread.interrupt()方法来中断它。当线程被中断时,它会抛出InterruptedException异常,从而使得线程从阻塞状态中恢复执行。:Thread.interrupt()方法可以实现线程的打断。当一个线程处于阻塞状态(如等待、睡眠等)时,可以通过调用该线程的interrupt()方法来中断它。当线程被中断时,它会抛出一个InterruptedException异常,从而使得线程从阻塞状态中恢复执行。

2024-01-26 11:50:42 369

原创 项目开发中安全问题及解决方案------sql注入

所谓盲注,指的是注入后并不能从服务器得到任何执行结果(甚至是错误信息),只能寄希望服务器对于 SQL 中的真假条件表现出不同的状态。也就是说,通过在真假条件中加入 SLEEP,来实现通过判断接口的响应时间,知道条件的结果是真还是假。基于时间的盲注原理就是,虽然 不能直接查询出 password 字段的值,但可以按字符逐一来查,判断第一个字符是否是 a、是否是 b……在代码中,我们可以引入p6spy工具打印出所有执行的 SQL,观察 sqlmap 构造的一些 SQL,来分析 其中原理。

2024-01-19 15:50:02 441

原创 项目开发中安全问题及解决方法-----资金处理一定要幂等

一般而言,这些接口都会有商户订单号的概念,对于相同的商户订单号,无 法进行重复的资金处理,所以三方公司的接口可以实现唯一订单号的幂等处理。最终虽然商品订单是一个,但支付订单是多个,相同的商品订单因为 产生多个支付订单导致多次支付。这,就是全链路的意义,从一开始就需要先有业务订单产生,然后使用相同的业务订单号一直贯穿到最后的资金通路,才能真正 避免重复资金操作。任何资金操作都需要在平台侧生成业务属性的订单,可以是优惠券发放订单,可以是返现订单,也可以是借款订单,一定是先有订单再去做资金操作。

2024-01-18 19:46:13 379

原创 项目开发中安全问题及解决方法-----平台资源需要考虑防刷机制

好的防刷逻辑是,对正常使用的用户毫无影响,只有疑似异常使用的用户才会感受到。对于短信验证码,有如下 4 种可行的方式来防刷。如果不加任何限制的话,某些用户大量请求就会导致系统出现问题。下面的例子中,直接调用方法获取短信验证码。• 控制相同手机号的发送次数和发送频次;• 只有先到过注册页面才能发送验证码;• 只有固定的请求头才能发送验证码;• 增加前置图形验证码。

2024-01-18 19:40:25 353

原创 项目开发中安全问题及解决方法-----用户标识不能从客户端获取

如果接口面向内部服务,由服务调用方传入用户 ID 没什么不合理,但是这样的接口不能直接开放给客户 端或 H5 使用。在测试阶段为了方便测试调试,我们通常会实现一些无需登录即可使用的接口,直接使用客户端传过来的用户标识,却在上线之前忘记删 除类似的超级接口。关于登陆的逻辑,其实我们没有必要在每一个方法内都复制粘贴相同的获取用户身份的逻辑,可以定义一个自定义注解 @LoginRequired 到 userId 参数上, 然后通过 HandlerMethodArgumentResolver 自动实现参数的组装。

2024-01-18 19:34:39 389

原创 项目开发中安全问题及解决方法----请求头中信息仅供参考

通常我们的应用之前都部署了反向代理或负载均衡器,remoteAddr 获得的只 能是代理的 IP 地址,而不是访问用户实际的 IP。这种过于依赖 X-Forwarded-For 请求头来判断用户唯一性的实现方式,是有问题的。我们是完全可以更改它的请求头的,比如我们可以通过模拟发送下面的请求头信息来影响业务: curl http://localhost:45678/trustclientip/test -H "X-Forwarded-For:183.84.18.71, 10.253.15.1"

2024-01-18 19:04:58 314

原创 项目开发中安全问题以及解决办法——客户请求需要校验

客户端提交的参数需要校验的问题,可以引申出一个更容易忽略的点是,我们可能会把一些服务端的数据暂存在网页的隐藏域中,这样下次页面提交的时 候可以把相关数据再传给服务端。虽然用户通过网页界面的操作无法修改这些数据,但这些数据对于 HTTP 请求来说就是普通数据,完全可以随时修改为 任意值。所以,服务端在使用这些数据的时候,也同样要特别小心。比如上面的情况,我们从数据库存储的国家中选择其中三个作为白名单进行展示,选择条件是id

2024-01-18 18:46:29 332

原创 项目开发中安全问题以及解决办法——客户传进来的数据不可信

一种可行的做法是,让客户端仅传入需要的数据给服务端,像这样重新定义一个 POJO CreateOrderRequest 作为接口入参,比直接使用领域模型 Order 更合 理。在设计接口时,我们会思考哪些数据需要客户端提供,而不是把一个大而全的对象作为参数提供给服务端,以避免因为忘记在服务端重置客户端数据 而导致的安全问题。如果我们直接利用客户端传过来的数据就很可能导致出现问题,服务端也一定要重新从数据库来初始化商品的价格,重新计算最终的订单价格。

2024-01-18 18:31:37 388

原创 java中基本类型之间的转换

以double和int为例,如果将一个double类型的值强制转换为int类型,那么小数部分将被直接截断,而不是四舍五入。因此,如果你有一个带有小数部分的double值,如3.14,在强制转换为int类型后,你将得到3,而不是3.14的整数部分3。整形的数据在进行计算的时候会转换为int类型去进行计算 同时要注意的是不要超过类型的数值范围。java中的 8 种基本数据类型,以及它们的占内存的容量大小和表示的范围。这种可以使多大范围的强制转换为小范围的,但是如果范围超了会导致精度丢失。

2023-12-20 12:19:38 362

原创 SpringBoot+dubbo调用的直连方式(含代码样例)

Dubbo直连是一种在Dubbo框架中的通信方式,它可以不通过注册中心,直接建立服务消费者和提供者之间的连接。这种方式的实现主要有两种:点对点直连和单边直连。主要应用场景:我们在微服务项目中写完某个模块的代码想要测试的时候,就可以采用直连的方式,如果使用注册中心得方式就可能调用到别人提供的服务。

2023-11-01 16:27:37 1125

原创 redirect导致的循环重定向问题(史上最全)

redirect: 是在Spring MVC控制器方法中使用的特殊前缀,用于执行重定向。有相对路径和绝对路径之分,如果有/则代表绝对路径 如果 /original/target 路径恰好与另一个控制器方法匹配,那么那个方法会被调用。就不会发生死循环问题但如果 /original/target 路由与 originalMethod 方法匹配,会导致无限重定向循环,因为它会不断地调用自己。

2023-10-29 05:34:57 415

原创 秒杀系统的业务流程以及优化方案(实现异步秒杀)

确定了我们可以将判断库存和检验一人一单业务抽取出来之后,我们在想一下 还能不能优化,这个时候我们会发现,这两个操作还是在数据库进行的,那么mysql的并发本身也是不高的,现在我们就要通过另一个性能更好的数据库进行实现,就是。首先我们要思考怎么对业务进行拆分,可以想象一个我们去饭店点餐,会有前台接待,询问订单,之后将小票传给后厨去做饭,这样就会快很多,也可以接待更多的客人。如果有下单资格就可以将用户id,优惠券id,和订单id存入一个阻塞队列里面,之后异步进行写入数据库操作。

2023-08-25 18:10:31 1432

空空如也

空空如也

TA创建的收藏夹 TA关注的收藏夹

TA关注的人

提示
确定要删除当前文章?
取消 删除