目录

目录

一、简单介绍一下项目

二、单点登录

(一)什么是单点登录

(二)单一服务器的登录

(三)单点登录实现方式

cookie机制

(四)单点登录系统,如果cookie禁用,你们怎么解决?

(五)同一账号以客户端登录怎么挤掉另一端

(六)单点登录怎么进行测试

三、购物车功能

(一)说一下购物车都有哪些功能

(二)说一下购物车功能是怎么做的

(三)加入购物车

(四)购物车测试用例

四、redis部分

(一)你说你用了redis缓存,你redis存的是什么格式的数据,是怎么存的

(二)redis为什么可以做缓存?项目中使用redis的目的是什么?redis什么时候使用?

(三)redis常见面试题

五、rabbitmq

(一)rabbitMQ的作用、原理?

(二)activeMQ在项目中如何应用的?

(三)MQ的优点。

(四)mq常见面试题


一、简单介绍一下项目

商城项目是一个b2c项目,基于高并发、海量数据环境进行实现。 是一个分布式项目,分为前后台两套系统实现,前台系统使用Redis实现缓存数据查询,包含首页服务,单点登录服务、购物车服务等。 后台系统包括商品管理,订单管理、权限管理等,主要使用的技术包括spring boot、spring mvc、mybatis plus、rabbitmq、redis等技术实现。

二、单点登录

(一)什么是单点登录

单点登录(Single Sign On),简称SSO. 用户只需要登陆一次就可以访问所有相互信任的应用系统

(二)单一服务器的登录

当⽤户第⼀次访问淘宝的时候,因为还没有登录,会被引导到认证中⼼进⾏登录。
    根据⽤户提供的登录信息,认证系统进⾏身份验证,如果通过,则登录成功,并返回给⽤户⼀个认证的凭据(JWT token)。
    当⽤户访问天猫时,就会将这个 JWT token 带上,作为⾃⼰认证的凭据。
    应⽤系统接收到请求后会把 JWT token 送到认证中⼼进⾏校验。
    如果通过校验,⽤户就可以在不⽤再次登录的情况下访问天猫了。

补充:token 是在服务端产生的。如果前端使用用户名/密码向服务端请求认证,服务端认证成功,那么在服务端会返回 token 给前端。前端可以在每次请求的时候带上 token 证明自己的合法地位。如果这个 token 在服务端持久化(比如存入数据库),那它就是一个永久的身份令牌。

(三)单点登录实现方式

1、token

token是什么: token是按照一定规则生成字符串,字符串可以包含用户信息等

比如: ip#用户名#用户信息 把他们经过加密,得到的字符串。这是一般存储的字符串

用法:

在项目某个模块登陆,登陆后,按照规则生成字符串,把登录之后的信息包含到生成的字符串中,把字符串经过一系列处理(编码+加密),得到它。
把字符串通过cookie返回
再去访问其他模块,在地址栏都带着字符串,在访问模块里面获取到地址栏字符串。
根据字符串获取里面的用户信息,如果可以在cookie中获取到就是登陆成功

2、cookie+ redis实现
在项目的任何一个模块进行登录,登录之后,把数据放到两个地方
redis:在key中:唯一的随机值,在value中:用户数据
cookie:把redis里面生成key的值放到cookie中
访问项目中的其他模块是,发送请求带着cookie进行发送,获取cookie值,拿着cookie做事情
把cookie获取值,到redis进行查询,根据key进行查询,如果查询出来数据就是登陆

1:首先用户输入账号密码登录后在数据库进行对比
2:账号密码错误重新登录,账号密码正确则进行下一步
3:cookie在同一个浏览器中是共享的,在用户登录成功之后,我们使用加密算法进行加密混淆形成一个token存入cookoie中,键为自定义标识字段(比如userSSH),值为token(不要在cookie中存储重要信息,容易被破解,所以在这里我们存入token,这个token就是验证用户信息的媒介)
4:cookie已经形成了,这时候用到了redis,就好比一个验证用户中心数据库一样,我们将形成的cookie的值,就是这个token存入redis中,键为token,值为用户的信息,并设置过期销毁的时间
5:在这里我们已经形成了cookie和redis用户信息,用户使用其他域名访问的时候,首先验证是否有userSSH这个cookie,如果没有,则引导至登录界面
6:如果有这个cookie,则把这个cookie的值token取出,用这个凭证去和redis中的信息对比,凭证生效,用户可以正常操作,并刷新redis中该数据的过期时间,凭证无效,则引导至登录界面
7:至此,redis+cookie实现的单点登录算是完成了,但在实际应用中一定远远比这个复杂,尤其是安全考虑方面

cookie机制

1)cookie机制采用的是在客户端保持 HTTP 状态信息的方案。当浏览器访问WEB服务器的某个资源时,WEB服务器会在HTTP响应头中添加一个键值对传送给浏览器,再由浏览器将该cookie放到客户端磁盘的一个文件中,该文件可理解为cookie域(键值对的集合),往后每次访问某个网站时,都会在请求头中带着这个网站的所有cookie值。(至于怎么区分不同网站的cookie的,很简单,每个网站都给他一个唯一标识比如网址等,每次打开某网址时,就查询该网站下的所有cookie值即可。)

2)每一个cookie都有一个name和一个value,且name是唯一的。相同名字时,后者会覆盖掉前者(类似哈希表的key的效果)。

3)一个WEB浏览器也可以存储多个WEB站点提供的Cookie。浏览器一般只允许存放300个Cookie,每个站点最多存放20个Cookie,每个Cookie的大小限制为4KB。

(四)单点登录系统,如果cookie禁用,你们怎么解决?

如果禁用cookie可以使用url中带参数,把token传递给服务端。当然此方法涉及安全性问题,其实在cookie中保存token同样存在安全性问题。推荐使用sso框架CAS实现单点登录。

(五)同一账号以客户端登录怎么挤掉另一端

用户登录后需要在Cookie中保存用户的id。当用户登录时,从当前所有的Cookie中判断是否有此用户id的存在,如果存在的话就把保存此用户id的Cookie销毁。

(六)单点登录怎么进行测试

登录的测试用例设计点汇总

服务端

验证码是否正确 (对应时间戳是否过期)

账户是否存在 (未注册、已注销)

密码是否正确 (记录连续输入错误次数,超过5次,账号锁定4小时。或提升验证等级,采取账号+密码+验证码+短信验证)

返回session、token

功能性用例设计点:

  1. 输入已注册的用户名和正确的密码,验证是否成功登录
  2. 输入已注册的用户名和不正确的密码,验证是否成功失败,且提示信息正确,提示:“账号或密码错误”
  3. 输入未注册的用户名和任意密码,验证是否登录失败,且提示信息正确,提示:“账号或密码错误”
  4. 使用未激活账户登录,验证是否登录失败,提示:“账号未激活”
  5. 使用被停用用户登录,验证是否登录失败,提示:“账号已停用”
  6. 用户名和密码两者都为空,验证是否登录失败,且提示信息正确,提示“账号或密码错误”
  7. 用户名和密码两者之一为空,验证是否登录失败,并且提示信息正确,如提示“请输入账号/请输入密码”
  8. 如果登录功能启用了验证码功能,在用户名和密码正确的情况下,输入正确的验证码,验证是否登录成功
  9. 如果登录功能启用了验证码功能,在用户名和密码正确的情况下,输入错误的验证码,验证是否登录失败,且提示信息正确,提示“验证码错误”
  10. 用户名和密码是否大小写敏感
  11. 页面上的密码框是否加密显示、或者是否需要有明暗码切换按钮
  12. 后台系统创建的用户第一次登录成功时,是否提示修改密码
  13. 忘记用户名和忘记密码的功能是否可用
  14. 前端页面是否根据设计需求限制用户名和密码长度
  15. 如果登录功能需要验证码,点击验证码图片或者点击换一张是否可以更换验证码,更换后的验证码是否可用
  16. 刷新页面是否会刷新验证码
  17. 如果验证码有时效性,需要分别时效性内和时效性外验证码的有效性
  18. 用户登录成功但是会话超时后,继续操作是否会重定向到用户登录界面
  19. 不同级别的用户,比如管理员和普通用户,登录系统后权限是否正确
  20. 页面默认焦点是否定位在用户输入框中
  21. 快捷键Tab和Enter等,是否可以正常使用
  22. 为空和输入空格字符串的校验是否一致
  23. 使用中文键盘输入字母和使用英文键盘输入字母传入后端的字符长度是否一致
  24. 成功登录后的session的时效设置
  25. 输入栏是否设置快速删除按钮
  26. 用户名和密码是否支持特殊字符和中文
  27. 浏览器的前进后退按钮,是否有效
  28. 成功登出后,点击浏览器回退按钮,是否可以继续操作系统
  29. 需求中是否有登录时间限制,如果有验证时间限制是否有效
  30. 验证不同登录方式的正确性:扫码、账号密码、第三方……
  31. 若支持手机号+验证码登录,验证码是否有时间限制,移动设备是否可以直接获取验证码
  32. 操作错误提示信息是否简单明了

页面测试:

  1. 登录页面显示是否正常?文字和图片能否正常显示,相应的提示信息是否正确,按钮的设置和排列是否正常,页面是否简洁壮观等。

  2. 页面默认焦点是否定位在用户名的输入框中

  3. 首次登录时相应的输入框是否为空?或者如果有默认文案,当点击输入框时默认方案是否消失?

  4. 相应的按钮如登录、重置等,是否可用;页面的前进、后退、刷新按钮是否可用?

  5. 快捷键Tab,Esc,Enter 等,能否控制使用

  6. 兼容性测试:不同浏览器,不同操作系统,不同分辨率下界面是否正常

兼容性测试用例设计点:

  1. 不同浏览器下,验证登录页面的显示以及功能正确性
  2. 相同浏览器的不同版本下验证登录页面的显示以及功能正确性
  3. 不同移动设备终端的不同浏览器下,验证登录页面显示以及功能的正确性
  4. 不同分辨率的界面下,验证登录页面的显示以及功能正确性

安全性测试用例设计点:

  1. 用户密码后台存储是否加密
  2. 用户密码在网络传输过程中是否加密
  3. 密码是否具有有效期,密码有效期到期后,是否提示需要修改密码
  4. 不登录的情况下,在浏览器中直接输入登录后的URL地址,验证是否会重新定向到用户登录界面
  5. 密码输入框是否不支持复制粘贴
  6. 密码输入框内输入的密码是否都可以在页面源码模式下被查看
  7. 用户名和密码输入框分别输入典型的“SQL注入攻击”字符串,验证系统的返回页面
  8. 用户名和密码输入框分别输入典型的“XSS跨站脚本攻击”字符串,验证系统行为是否被篡改,用户名和密码的的输入框,应该禁止输入脚本 (防止XSS攻击)
  9. 连续多次登录失败的情况下,系统是否会阻止后续的尝试以应对暴力破解
  10. 同一用户在同一终端的多种浏览器上登录,验证登录功能的互斥性是否符合设计预期
  11. 同一用户先后在多台终端的浏览器上登录,验证登录是否具有互斥性
  12. 是否可以记住密码,记住的密码保存是否加密,记住的密码是否有有效期,过了有效期后是否清空密码
  13. 是否支持第三方登录
  14. 密码的强弱性,复杂度校验
  15. 异地登录校验、更换设备登录校验、登陆信息异常是否考虑账户冻结停用、是否允许第三方平台存储密码
  16. 是否可以使用登录的api发送登录请求,并绕开验证码校验
  17. 是否可以用抓包工具抓到的请求包直接登录
  18. 截取到的token等信息,是否可以在其他终端上直接使用,绕开登录,token过期时间校验
  19. 登录错误后的提示是否存在安全隐患
  20. 登录成功后生成的Cookie,是否是httponly (否则容易被脚本盗取)
  21. 错误登陆的次数限制(防止暴力破解)

性能压力测试的用例设计点:

单用户登录系统的响应时间是否符合"3-5-8"原则
用户数在临界点时并发登录是否还能符合"3-5-8"原则
压力:大量并发用户登录,系统的响应时间是多少?系统会出现宕机、内存泄露、cpu饱和、无法登录吗?
稳定性:系统能否处理并发用户数在临界点以内连续登录N个时的场景?
  1. 单用户登录的响应时间是否小于3秒
  2. 单用户登录时,后台请求数量是否过多
  3. 高并发场景下用户登录的响应时间是否小于5秒
  4. 高并发场景下服务端的监控指标是否符合预期
  5. 高集合点并发场景下,是否存在资源死锁和不合理资源等待
  6. 长时间大量用户连续登录和登出,服务器是否存在内存泄露
  7. 输入内容校验是否加入了函数防抖

其它测试:

  1. 连续输入3次或以上错误密码,是否被锁一定时间(如:15分钟)或者冻结账户?时间内不允许登录,超出时间点是否可以继续登录。

  2. 用户session过期后,重新登录是否还能重新返回这前session过期的页面?

  3. 用户名和密码输入框是否支持键盘快捷键?如:撤销、复制、粘贴等等

  4. 是否允许同名用户同时登录进行操作?考虑web和app同时登录

  5. 手机登录时,是否先判断网络可用?

  6. 手机登录时,是否先判断app存在新版本?

  7. 是否支持单点登录?

  8. 是否有埋点接口

三、购物车功能

(一)说一下购物车都有哪些功能

把商品添加到购物车(订购)
删除购物车中已订购的商品、
修改购物车中某一商品的订购数量
清空购物车
显示购物车中商品的清单及数量、价格

(二)说一下购物车功能是怎么做的

一个用户对应一个购物车,一个用户不管买多少商品,都会存在属于自己的购物车中,单点登录一定要在购物车之前。用户添加购物车,登录状态会存放在Redis缓存中

当用户点击购物车跳转的时候判断用户是否没有登录的话就跳转到登录页面

当用户登录之后他得用户信息就会被保存下来,我们就可以将用户的username(单点登录的时候将用户对象封装到字符串中放到redis中)取出来(将封装的用户的字符串转换 成对象)作为redis的key,商品的信息作为value存放在redis中。

1、购物车第一步先判断cookie传递过来的值car+取出的redis里面的存储的字符串转化的对象的username(username唯一)

2、然后判断redis是否存在

3、如果不存在的话就创建一个购物车存放在redis中 key为car+username

4、不能用car+uuid因为每次登陆uuid是会变化的所以用不变的就是username

5、获取用户的名字

//判断是否有购物车if(dao.exists(key)){//若果存在的话 判断集合中是否存在该商品String redisResult = dao.get(key);List list = JsonUtils.jsonToList(redisResult, TbItemImages.class);boolean isExist = false;
//如果购物车存在但是id不存在就重新添加一个并重新set进去
//没有key表示没有购物车 将购物车装到redis中
//查询购物车中的商品 利用tttoken获得redis里面的对象在获得其username

在用户加入购物车的时候,我们需要先看一下   当前商品和规格   在redis里面是否已经存在  如果存在  就直接在数量上面加减  不存在的就直接加入就可以了

(三)加入购物车

<%@page import="com.ys.cart.entity.Cart"%>
<%@page import="com.ys.cart.entity.Goods"%>
<%@page import="com.ys.cart.biz.impl.GoodsBizImpl"%>
<%@page import="java.util.ArrayList"%>
<%@page import="java.util.List"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"pageEncoding="UTF-8"%>
<!-- 加入购物车的do处理页面 --><!-- 购物车流程:1.Goods---显示商品信息2.Cart ---购物车中的信息加入购物车 不是将Goods进行存储  而是存储的是Cart实体-->
<%//将点击的商品存储到购物车容器中(session)//设置编码request.setCharacterEncoding("utf-8");//获取商品编号String id = request.getParameter("gid");int gid = 0;if (null != id) {gid = Integer.valueOf(id);}//拿到cid  根据cid获取商品的其它信息Goods goods = new GoodsBizImpl().getGoodsByCid(gid);//数量默认一件//总价格    商品的单价 * 数量(1件)//将上面获取的信息封装到Cart实体中Cart cart = new Cart();cart.setGoods(goods);cart.setCcount(1);cart.setCtotal();//1.获取购物车List<Cart> listCarts = (List<Cart>) session.getAttribute("listCarts");//2.判断非空boolean flag = true;if (null == listCarts) {//说明用户时第一次将商品加入购物车  //创建购物车listCarts = new ArrayList<Cart>();} else {//否则  购物车中存在商品//遍历所有的购物车中的商品  看该购物车中是否存在刚才点击的cart实体封装for (Cart c : listCarts) {//判断传递的cid与当前listCarts中的每一个cid进行匹配if (gid == c.getGoods().getGid()) {//说明购物车中存在该商品flag = false;//修改数量c.setCcount(c.getCcount() + 1);//修改总价格c.setCtotal();}}}//判断flag标记if (flag == true) {//说明购物车中没有该商品listCarts.add(cart);}//重新保存购物车session.setAttribute("listCarts", listCarts);response.sendRedirect("cart.jsp");
%>

(四)购物车测试用例

购物车设计测试用例----必背_Aimin2020.的博客-CSDN博客_购物车测试用例

四、redis部分

(一)你说你用了redis缓存,你redis存的是什么格式的数据,是怎么存的

redis中存储的都是key-value格式的。拿商品数据来说,key就是商品id,value是商品相关信息的json数据。

(二)redis为什么可以做缓存?项目中使用redis的目的是什么?redis什么时候使用?

     1.Redis是key-value形式的nosql数据库。可以快速的定位到所查找的key,并把其中的value取出来。并且redis的所有的数据都是放到内存中,存取的速度非常快,一般都是用来做缓存使用。

2.项目中使用redis一般都是作为缓存来使用的,缓存的目的就是为了减轻数据库的压力提高存取的效率。

3.在互联网项目中只要是涉及高并发或者是存在大量读数据的情况下都可以使用redis作为缓存。当然redis提供丰富的数据类型,除了缓存还可以根据实际的业务场景来决定redis的作用。例如使用redis保存用户的购物车信息、生成订单号。

(三)redis常见面试题

https://blog.csdn.net/m0_48795607/article/details/115642129

五、rabbitmq

(一)rabbitMQ的作用、原理?

采用AMQP高级消息队列协议的一种消息队列技术,最大的特点就是消费并不需要确保提供方存在,实现了服务之间的高度解耦。

rabbitmq的作用就是系统之间进行通信。当然可以使用其他方式进行系统间通信,如果使用rabbitmq的话可以对系统之间的调用进行解耦,实现系统间的异步通信。原理就是生产者生产消息,把消息发送给rabbitmq。rabbitmq接收到消息,然后查看有多少个消费者,然后把消息转发给消费者。

(二)activeMQ在项目中如何应用的?

Activemq在项目中主要是完成系统之间通信,并且将系统之间的调用进行解耦。例如在添加、修改商品信息后,需要将商品信息同步到索引库、同步缓存中的数据以及生成静态页面一系列操作。在此场景下就可以使用activemq。一旦后台对商品信息进行修改后,就向activemq发送一条消息,然后通过activemq将消息发送给消息的消费端,消费端接收到消息可以进行相应的业务处理。

(三)MQ的优点。

异步处理 - 相比于传统的串行、并行方式,提高了系统吞吐量。

应用解耦 - 系统间通过消息通信,不用关心其他系统的处理。

流量削锋 - 可以通过消息队列长度控制请求量;可以缓解短时间内的高并发请求。

日志处理 - 解决大量日志传输。

消息通讯 - 消息队列一般都内置了高效的通信机制,因此也可以用在纯的消息通讯。比如实现点对点消息队列,或者聊天室等。

(四)mq常见面试题

https://blog.csdn.net/m0_48795607/article/details/116064045

java面试专题(商城面试问题)相关推荐

  1. 23Java面试专题 八股文面试全套真题(含大厂高频面试真题)多线程

    文档说明 在文档中对所有的面试题都进行了 难易程度和 出现频率的等级说明 星数越多代表权重越大,最多五颗星(☆☆☆☆☆) 最少一颗星(☆) Java多线程相关面试题 1.线程的基础知识 1.1 线程和 ...

  2. 【Java架构师面试题】设计模式面试专题(共35题含答案)

    设计模式(DesignPattern)是前辈们对代码开发经验的总结,是解决特定问题的一系列套路.它不是语法规定,而是一套用来提高代码可复用性.可维护性.可读性.稳健性以及安全性的解决方案. 本篇为设计 ...

  3. boss直聘java几轮面试,京东商城java高级面试:通过在Boss直聘上收到来自京东金 - 职朋职业圈...

    为了帮助职业圈网友能够及时了解京东商城的面试流程以及面试过程所涉及的面试问题,职业圈小编把刚获得的京东商城面试经验马上编辑好,快速提供给大家,以便能够尽快帮助到有需要的人.这次面试总共花了1天.面试形 ...

  4. hashmap中用红黑树不用其他树_HashMap面试专题:常问六题深入解析

    引言 其实我很早以前就想写一篇关于HashMap的面试专题.对于JAVA求职者来说,HashMap可谓是集合类的重中之重,甚至你在复习的时候,其他集合类都不用看,专攻HashMap即可. 然而,鉴于网 ...

  5. BAT Android面试专题深入探究:四大组件+ViewPager+组件化架构+Bitmap

    本篇是结合我之前面试别人的经验,以及跟一些在BAT上班的朋友,讨论总结出的一份很深的大公司需要用到的一些高端Android技术.这里也专门整理了一个文档,重点和难点都有详细解析.这些题目有点技术含量, ...

  6. java ibatis 获取执行的sql_阿里Java技术面开源框架面试真题曝光,这些真题你能答对多少?...

    前言: 又到了一年的金九银十季,相信有很多朋友都已经有了跳槽的准备,或者有已经跳槽成功的朋友,我也有一个利用这次机会跳槽阿里的朋友,下面我分享的就是他面试记得的阿里面试关于spring全家桶的一些问题 ...

  7. 啃完这些Java面试题,面试阿里P7稳了

    现在2021年的互联网行业竞争越来越严峻,面试也是越来越难,一直以来我都想整理一套完美的面试宝典,奈何难抽出时间,这套1000+道的Java面试手册我整理了整整3个月,上传到Git上目前star数达到 ...

  8. 大学应届毕业生应聘java工程师怎样应对面试官的提问?

    有人说,面试有技巧.最初,我不太信,但是接触的多了就不由得我不信了. 前两年老同学在北京面试,已经有了三四年的工作经验,他之所以要换工作是因老板只谈理想不谈钱,属于典型的"耍流氓" ...

  9. Spring面试专题

    Spring面试专题 1.什么是 Spring 框架?Spring 框架有哪些主要模块? 2.使用 Spring 框架能带来哪些好处? 3.什么是控制反转(IOC)?什么是依赖注入? 4.请解释下 S ...

  10. 老师讲的真棒!Java高级程序员面试集合,你还看不懂吗?

    前言: 首先介绍一下我的同学,专科毕业应用电子技术专业,已经毕业快两年了.因为专业的原因工作一年觉得没什么发展前途就想转行,身为他的"好基友",他觉得我这个工作挺好的,就咨询了我一 ...

最新文章

  1. 在Ubuntu 14.04.3 64bit上安装照片编辑软件Fotoxx 15.10
  2. 13 Java程序员面试宝典视频课程之容器
  3. 聊聊jump consistent hash
  4. java中的list时间排序
  5. awk 系列Part5:如何使用 awk 复合表达式
  6. Elasticsearch教程 elasticsearch Mapping的创建
  7. c语言 整型转bool,C语言的布尔类型(_Bool)【转】
  8. DotNetTextBox V3.0 所见即所得编辑器控件 For Asp.Net2.0(ver 3.0.1Beta)
  9. assembly 输出ab中所有数_罗克韦尔(AB)PLC控制器选型(2)-CompactLogix 5370
  10. zend studio【快捷键】
  11. 算法设计与分析基础——假币问题(三分法)
  12. Oracle数据库常用的管理工具介绍
  13. java中怎么编写一个类_Java入门(8) 创建新类
  14. iptables实现网卡包的转发
  15. 操作系统核心与驱动程序
  16. C# 中的多线程和异步编程
  17. onedrive已停止工作_4、工作簿的基本操作
  18. ArcMAP实现矢量数据平移
  19. 动手实现一个跑步小程序
  20. 小程序购物城项目实战(下篇)

热门文章

  1. JavaScript历史
  2. SCAPE: shape completion and animation of people
  3. SAR-Scape处理SBAS-InSAR报错原因之一
  4. 税务UKey开票软件 V1.0.22_ZS_20221231 版本数据库分析
  5. C++编程 打印三角形的各种玩法
  6. [附源码]java毕业设计小区物业管理系统论文
  7. div设置下拉滚动条以及翻滚到底部
  8. 从scratch到python轻松学下载_STEAM教育-[少儿创客] 从Scratch到Python——python turtle-电路城论坛 - 电子工程师学习交流园地...
  9. 阿里天池Python训练营-异常处理
  10. 解决Python调试OSError: [WinError 193] %1 不是有效的 Win32 应用程序