本文从本人博客搬运,原文格式更加美观,可以移步原文阅读:WxJava微信公众号开发实战
之前我们介绍了Java如何进行微信公众号开发,阅读本文前小伙伴们可以先去了解下Java微信公众号开发
之前我们开发微信公众号时,都是要手动自己实现微信接收、响应消息的封装,消息类型的判断,access_token的过期时间管理等等,还是比较麻烦的。有没有已经封装好的开源项目来简化开发呢?这里推荐WxJava,它的地址如下:
- github:https://github.com/Wechat-Group/WxJava
- gitee:https://gitee.com/binary/weixin-java-tools
WxJava介绍
WxJava是一个java的微信开发工具包,支持包括微信支付、开放平台、公众号、企业微信/企业号、小程序等微信功能的后端开发,对微信开发相关内容进行了高度封装,极大简化了我们的编码
要使用WxJava只需要引入相关模块的maven依赖即可
<dependency> <groupId>com.github.binarywang</groupId> <artifactId>(不同模块参考下文)</artifactId> <version>4.0.0</version> </dependency>
讯享网
不同模块的名称如下:
- 微信小程序:
weixin-java-miniapp - 微信支付:
weixin-java-pay - 微信开放平台:
weixin-java-open - 公众号(包括订阅号和服务号):
weixin-java-mp - 企业号/企业微信:
weixin-java-cp
WxJava微信公众号开发
1.环境准备
创建Springboot工程,引入WxJava微信公众号整合springboot的依赖
讯享网<dependency> <groupId>com.github.binarywang</groupId> <artifactId>wx-java-mp-spring-boot-starter</artifactId> <version>4.0.0</version> </dependency>
yml中配置微信公众号必要信息
wx: mp: app-id: appid secret: secret token: token # 配置消息回调地址接入公众号时需要的token server: port: 8088
编写测试Controller
讯享网@RestController @RequestMapping("/wxjava/mp") @Slf4j public class WxjavaTestController {
@Autowired private WxMpService wxMpService; @Autowired private WxMpConfigStorage wxMpConfigStorage; @GetMapping("test") public void testAutowire(){
System.out.println(wxMpService); System.out.println(wxMpConfigStorage); } }
wx-java-mp-spring-boot-starter主要帮我们自动配置了如下两个对象:
WxMpService:可以完成微信公众号提供的各种功能WxMpConfigStorage:保存了微信公众号配置信息
运行访问测试接口,打印如下
me.chanjar.weixin.mp.api.impl.WxMpServiceHttpClientImpl@17c0a627 {
"appId":"...","secret":"...","token":"...","expiresTime":0,"httpProxyPort":0,"jsapiTicketExpiresTime":0,"sdkTicketExpiresTime":0,"cardApiTicketExpiresTime":0,"accessTokenLock":{
"sync":{
"state":0}},"jsapiTicketLock":{
"sync":{
"state":0}},"sdkTicketLock":{
"sync":{
"state":0}},"cardApiTicketLock":{
"sync":{
"state":0}}}
2.接入微信公众号回调
在Controller中添加接入方法
@GetMapping("message") public String configAccess(String signature,String timestamp,String nonce,String echostr) {
// 校验签名 if (wxMpService.checkSignature(timestamp, nonce, signature)){
// 校验成功原样返回echostr return echostr; } // 校验失败 return null; }
可以看出,与之前我们自己实现复杂的校验流程相比,代码简洁了很多
3.接收与响应消息
WxJava为了对不同类型的微信消息进行分类处理,避免出现很多if/else判断,设计了如下的消息处理流程:
- 针对不同类型的消息处理,我们需要自己实现消息处理器,消息处理器必须实现
WxMpMessageHandler接口
public interface WxMpMessageHandler {
/ * 处理微信推送消息. * * @param wxMessage 微信推送消息 * @param context 上下文,如果handler或interceptor之间有信息要传递,可以用这个 * @param wxMpService 服务类 * @param sessionManager session管理器 * @return xml格式的消息,如果在异步规则里处理的话,可以返回null * @throws WxErrorException 异常 */ WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context, WxMpService wxMpService, WxSessionManager sessionManager) throws WxErrorException; }
- 自定义路由,将不同类型的消息交给不同的消息处理器来处理。路由对象为
WxMpMessageRouter
根据上述流程,我们首先定义2个消息处理器,分别实现对文本消息和图片消息的处理,并存入Spring容器
// 文本消息处理器 @Component public class TextHandler implements WxMpMessageHandler {
@Override public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context, WxMpService wxMpService, WxSessionManager sessionManager) throws WxErrorException {
// 接收的消息内容 String inContent = wxMessage.getContent(); // 响应的消息内容 String outContent; // 根据不同的关键字回复消息 if (inContent.contains("游戏")){
outContent = "仙剑奇侠传"; }else if (inContent.contains("动漫")){
outContent = "进击的巨人"; }else {
outContent = inContent; } // 构造响应消息对象 return WxMpXmlOutMessage.TEXT().content(outContent).fromUser(wxMessage.getToUser()) .toUser(wxMessage.getFromUser()).build(); } } // 图片消息处理器 @Component public class ImageHandler implements WxMpMessageHandler {
@Override public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context, WxMpService wxMpService, WxSessionManager sessionManager) throws WxErrorException {
// 原样返回收到的图片 return WxMpXmlOutMessage.IMAGE().fromUser(wxMessage.getToUser()).toUser(wxMessage.getFromUser()) .mediaId(wxMessage.getMediaId()).build(); } }
然后创建配置类,创建一个WxMpMessageRouter,指定消息路由规则,并将其存入Spring容器
@Configuration public class WxJavaConfig {
@Autowired private WxMpService wxMpService; @Autowired private TextHandler textHandler; @Autowired private ImageHandler imageHandler; @Bean public WxMpMessageRouter messageRouter() {
// 创建消息路由 final WxMpMessageRouter router = new WxMpMessageRouter(wxMpService); // 添加文本消息路由 router.rule().async(false).msgType(WxConsts.XmlMsgType.TEXT).handler(textHandler).end(); // 添加图片消息路由 router.rule().async(false).msgType(WxConsts.XmlMsgType.IMAGE).handler(imageHandler).end(); return router; } }
最后在Controller中注入WxMpMessageRouter,定义消息处理方法,将消息路由到对应的处理器
@PostMapping(value = "message", produces = "application/xml; charset=UTF-8") public String handleMessage(@RequestBody String requestBody, @RequestParam("signature") String signature, @RequestParam("timestamp") String timestamp, @RequestParam("nonce") String nonce) {
log.info("handleMessage调用"); // 校验消息是否来自微信 if (!wxMpService.checkSignature(timestamp, nonce, signature)) {
throw new IllegalArgumentException("非法请求,可能属于伪造的请求!"); } // 解析消息体,封装为对象 WxMpXmlMessage inMessage = WxMpXmlMessage.fromXml(requestBody); WxMpXmlOutMessage outMessage; try {
// 将消息路由给对应的处理器,获取响应 outMessage = wxMpMessageRouter.route(inMessage); } catch (Exception e) {
log.error("微信消息路由异常", e); outMessage = null; } // 将响应消息转换为xml格式返回 return outMessage == null ? "" : outMessage.toXml(); }
4.事件推送
我们来实现一个接收关注、取消关注事件推送的处理。首先定义关注、取消关注消息处理器,存入容器
// 关注处理器 @Component @Slf4j public class SubscribeHandler implements WxMpMessageHandler {
@Override public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context, WxMpService wxMpService, WxSessionManager sessionManager) throws WxErrorException {
log.info("SubscribeHandler调用"); return WxMpXmlOutMessage.TEXT().fromUser(wxMessage.getToUser()).toUser(wxMessage.getFromUser()) .content("欢迎关注").build(); } } // 取消关注处理器 @Component @Slf4j public class UnSubscribeHandler implements WxMpMessageHandler {
@Override public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context, WxMpService wxMpService, WxSessionManager sessionManager) throws WxErrorException {
log.info("UnSubscribeHandler调用"); // 因为已经取消关注,所以即使回复消息也收不到 return WxMpXmlOutMessage.TEXT().fromUser(wxMessage.getToUser()).toUser(wxMessage.getFromUser()) .content("请别离开我").build(); } }
然后修改路由,添加对应的推送消息与处理器的关联

5.自定义菜单
我们来尝试创建2个按钮,第2个按钮包含3个子按钮,只要用WxJava封装好的菜单和按钮对象,非常方便:
WxMenu:菜单对象WxMenuButton:按钮对象
@GetMapping("createMenu") public String createMenu() throws WxErrorException {
// 创建菜单对象 WxMenu menu = new WxMenu(); // 创建按钮1 WxMenuButton button1 = new WxMenuButton(); button1.setType(WxConsts.MenuButtonType.CLICK); button1.setName("今日歌曲"); button1.setKey("V1001_TODAY_MUSIC"); // 创建按钮2 WxMenuButton button2 = new WxMenuButton(); button2.setName("菜单"); // 创建按钮2的子按钮1 WxMenuButton button21 = new WxMenuButton(); button21.setType(WxConsts.MenuButtonType.VIEW); button21.setName("搜索"); button21.setUrl("https://www.baidu.com/"); // 创建按钮2的子按钮2 WxMenuButton button22 = new WxMenuButton(); button22.setType(WxConsts.MenuButtonType.VIEW); button22.setName("视频"); button22.setUrl("https://v..com/"); // 创建按钮2的子按钮3 WxMenuButton button23 = new WxMenuButton(); button23.setType(WxConsts.MenuButtonType.CLICK); button23.setName("赞一下我们"); button23.setKey("V1001_GOOD"); // 将子按钮添加到按钮2 button2.getSubButtons().add(button21); button2.getSubButtons().add(button22); button2.getSubButtons().add(button23); // 将按钮1和你按钮2添加到菜单 menu.getButtons().add(button1); menu.getButtons().add(button2); // 创建按钮 return wxMpService.getMenuService().menuCreate(menu); }
成功创建菜单后效果如下

6.发送模板消息
测试模板如下

在controller中添加发送模板消息的方法
@GetMapping("sendTemplateMessage") public String sendTemplateMessage() throws WxErrorException {
// 创建模板消息,设置模板id、指定模板消息要发送的目标用户 WxMpTemplateMessage wxMpTemplateMessage = WxMpTemplateMessage.builder() .templateId("uCl1-JREW8k1vW084PTcFmrvMvFJX9H2Xs51gQeGG2I") .toUser("ommzW5192wiIazYpp2WRzcsL_6Vk") .build(); // 填充模板消息中的变量 wxMpTemplateMessage.addData(new WxMpTemplateData("goodsName", "华为mate40pro")); wxMpTemplateMessage.addData(new WxMpTemplateData("time", "2020-10-25")); wxMpTemplateMessage.addData(new WxMpTemplateData("price", "6999")); wxMpTemplateMessage.addData(new WxMpTemplateData("remark", "麒麟9000牛逼")); // 发送模板消息,返回消息id return wxMpService.getTemplateMsgService().sendTemplateMsg(wxMpTemplateMessage); }
发送成功后效果如下

7.将accesstoken持久化到redis
默认情况下,微信相关配置都会保存到内存中的WxMpConfigStorage对象。比如我们创建自定义菜单、发送模板消息都需要首先获取access_token,而WxJava会将获取access_token的过程封装到对应的api中,比如我们发送模板消息前后输出WxMpConfigStorage,会发现多了access_token的数据

但是这样做的话,每次重启项目,原先的access_token就失效了,即便它可能还没有过期。此时需要重新获取access_token,这样做的坏处是:
- 需要重新发起网络请求,效率低
- 获取
access_token的微信接口有调用次数限制,没过期就调用可能会因为达到次数上限而获取失败 - 如果是分布式的环境下,每个服务都要各自去获取
access_token,没有必要
所以我们可以将access_token持久化到redis。在yaml中增加redis配置
wx: mp: app-id: ... secret: ... token: ... config-storage: type: redistemplate spring: redis: host: 192.168.157.130 port: 6379 database: 0 server: port: 8088
注意,
wx.mp.config-storage.type只有选择jedis,wxjava才会读取自己配置的redis连接信息,利用jedis连接。如果配置为redisTemplate,那么wxjava将忽略自己的redis连接信息,使用Spring容器中的redisTemplate操作redis,所以此时要引入Springboot整合redis的依赖
添加redis依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
此时再调用发送模板消息等需要access_token的接口之前,会先获取到token并存入redis

我们分别在调用前后打印access_token



可以发现存入redis的token有效期大约为2小时,与微信官方一致。此时重新启动项目后,如果token没有过期,会从redis中取出token,直接使用,而不是再次调用接口获取

我们可以发现,
access_token存入redis的key为wx:access_token:公众号的appid,这也是为什么重启后可以再次从redis中获取到
8.网页授权
首先要构建授权页面url
@GetMapping("buildAuthPage") public String auth() {
WxOAuth2Service oAuth2Service = wxMpService.getOAuth2Service(); // 构建授权url return oAuth2Service.buildAuthorizationUrl("https://baobao.cn.utools.club/wxjava/mp/callback", WxConsts.OAuth2Scope.SNSAPI_USERINFO, null); }
其中buildAuthorizationUrl的说明如下:
/ * <pre> * 构造oauth2授权的url连接. * 详情请见: http://mp.weixin..com/wiki/index.php?title=网页授权获取用户基本信息 * </pre> * * @param redirectUri 用户授权完成后的重定向链接,无需urlencode, 方法内会进行encode * @param scope scope * @param state state * @return url */ String buildAuthorizationUrl(String redirectUri, String scope, String state);
然后编写用户确认授权后的回调处理,利用code获取accessToken,再利用accessToken获取用户信息
@GetMapping("callback") public WxOAuth2UserInfo callback(String code) throws WxErrorException {
WxOAuth2Service oAuth2Service = wxMpService.getOAuth2Service(); // 利用code获取accessToken WxOAuth2AccessToken accessToken = oAuth2Service.getAccessToken(code); // 利用accessToken获取用户信息 WxOAuth2UserInfo userInfo = oAuth2Service.getUserInfo(accessToken, null); return userInfo; }
测试:首先访问/buildAuthPage,获取授权url

将授权url复制到微信浏览器中打开,同意授权后进入回调
9.生成带参数的二维码
一般情况下用户如果需要关注公众号,只需扫描微信提供的固定二维码即可。但为了满足用户渠道推广分析和用户帐号绑定等场景的需要,公众平台提供了生成带参数二维码的接口。使用该接口可以获得多个带不同场景值的二维码,用户扫描后,公众号可以接收到事件推送
目前有2种类型的二维码:
- 临时二维码,是有过期时间的,最长可以设置为在二维码生成后的30天(即秒)后过期,但能够生成较多数量。临时二维码主要用于帐号绑定等不要求二维码永久保存的业务场景
- 永久二维码,是无过期时间的,但数量较少(目前为最多10万个)。永久二维码主要用于适用于帐号绑定、用户来源统计等场景。
用户扫描带场景值二维码时,可能推送以下两种事件:
- 如果用户还未关注公众号,则用户可以关注公众号,关注后微信会将带场景值关注事件推送给开发者。
- 如果用户已经关注公众号,在用户扫描后会自动进入会话,微信也会将带场景值扫描事件推送给开发者。
获取带参数的二维码的过程包括两步,首先创建二维码ticket,然后凭借ticket到指定URL换取二维码,步骤说明如下:
- 创建二维码ticket:每次创建二维码ticket需要提供一个开发者自行设定的参数(scene_id),分别介绍临时二维码和永久二维码的创建二维码ticket过程:
- 临时二维码请求说明:
- http请求方式: POST URL: https://api.weixin..com/cgi-bin/qrcode/create?access_token=TOKEN
- POST数据格式:json
- POST数据例子:
{"expire_seconds": , "action_name": "QR_SCENE", "action_info": {"scene": {"scene_id": 123}}}或者也可以使用以下POST数据创建字符串形式的二维码参数:{"expire_seconds": , "action_name": "QR_STR_SCENE", "action_info": {"scene": {"scene_str": "test"}}}
- 永久二维码请求说明:
- http请求方式: POST URL: https://api.weixin..com/cgi-bin/qrcode/create?access_token=TOKEN
- POST数据格式:json
- POST数据例子:
{"action_name": "QR_LIMIT_SCENE", "action_info": {"scene": {"scene_id": 123}}}或者也可以使用以下POST数据创建字符串形式的二维码参数:{"action_name": "QR_LIMIT_STR_SCENE", "action_info": {"scene": {"scene_str": "test"}}}
- 临时二维码请求说明:
| 参数 | 说明 |
|---|---|
| expire_seconds | 该二维码有效时间,以秒为单位。 最大不超过(即30天),此字段如果不填,则默认有效期为30秒。 |
| action_name | 二维码类型,QR_SCENE为临时的整型参数值,QR_STR_SCENE为临时的字符串参数值,QR_LIMIT_SCENE为永久的整型参数值,QR_LIMIT_STR_SCENE为永久的字符串参数值 |
| action_info | 二维码详细信息 |
| scene_id | 场景值ID,临时二维码时为32位非0整型,永久二维码时最大值为(目前参数只支持1–) |
| scene_str | 场景值ID(字符串形式的ID),字符串类型,长度限制为1到64 |
正确的Json返回结果:
{
"ticket":"gQH47joAAAAAAAAAASxodHRwOi8vd2VpeGluLnFxLmNvbS9xL2taZ2Z3TVRtNzJXV1Brb3ZhYmJJAAIEZ23sUwMEmm 3sUw==","expire_seconds":60,"url":"http://weixin..com/q/kZgfwMTm72WWPkovabbI"}
| 参数 | 说明 |
|---|---|
| ticket | 获取的二维码ticket,凭借此ticket可以在有效时间内换取二维码。 |
| expire_seconds | 该二维码有效时间,以秒为单位。 最大不超过(即30天)。 |
| url | 二维码图片解析后的地址,开发者可根据该地址自行生成需要的二维码图片 |
- 通过
ticket换取二维码:获取二维码ticket后,开发者可用ticket换取二维码图片。请注意,本接口无须登录态即可调用
HTTP GET请求(请使用https协议)https://mp.weixin..com/cgi-bin/showqrcode?ticket=TICKET # 提醒:TICKET记得进行UrlEncode
ticket正确情况下,http 返回码是200,是一张图片,可以直接展示或者下载
HTTP头(示例)如下: Accept-Ranges:bytes Cache-control:max-age= Connection:keep-alive Content-Length:28026 Content-Type:image/jpg Date:Wed, 16 Oct 2013 06:37:10 GMT Expires:Wed, 23 Oct 2013 14:37:10 +0800 Server:nginx/1.4.1
错误情况下(如ticket非法)返回HTTP错误码404
用户扫描带参数的二维码后,我们会收到微信的事件推送:
- 如果用户还未关注公众号,则用户可以关注公众号,关注后微信会将带场景值关注事件推送给开发者。推送的xml数据包示例
<xml> <ToUserName><![CDATA[toUser]]></ToUserName> <FromUserName><![CDATA[FromUser]]></FromUserName> <CreateTime></CreateTime> <MsgType><![CDATA[event]]></MsgType> <Event><![CDATA[subscribe]]></Event> <EventKey><![CDATA[qrscene_]]></EventKey> <Ticket><![CDATA[TICKET]]></Ticket> </xml>
| 参数 | 描述 |
|---|---|
| ToUserName | 开发者微信号 |
| FromUserName | 发送方帐号(一个OpenID) |
| CreateTime | 消息创建时间 (整型) |
| MsgType | 消息类型,event |
| Event | 事件类型,subscribe |
| EventKey | 事件KEY值,qrscene_为前缀,后面为二维码的参数值(即创建二维码时设定的scene_id或scene_str) |
| Ticket | 二维码的ticket,可用来换取二维码图片 |
- 如果用户已经关注公众号,则微信会将带场景值扫描事件推送给开发者。推送XML数据包示例:
<xml> <ToUserName><![CDATA[toUser]]></ToUserName> <FromUserName><![CDATA[FromUser]]></FromUserName> <CreateTime></CreateTime> <MsgType><![CDATA[event]]></MsgType> <Event><![CDATA[SCAN]]></Event> <EventKey><![CDATA[SCENE_VALUE]]></EventKey> <Ticket><![CDATA[TICKET]]></Ticket> </xml>
| 参数 | 描述 |
|---|---|
| ToUserName | 开发者微信号 |
| FromUserName | 发送方帐号(一个OpenID) |
| CreateTime | 消息创建时间 (整型) |
| MsgType | 消息类型,event |
| Event | 事件类型,SCAN |
| EventKey | 事件KEY值,是一个32位无符号整数,即创建二维码时的二维码scene_id |
| Ticket | 二维码的ticket,可用来换取二维码图片 |
利用wxjava的实现如下:
// 获取二维码的ticket // 获取临时二维码的ticket,其中scene为场景值,expire_seconds为二维码超时时间 WxMpQrCodeTicket ticket = wxMpService.getQrcodeService().qrCodeCreateTmpTicket(scene, expire_seconds); // 获取永久二维码的ticket,其中scene为场景值 WxMpQrCodeTicket ticket = wxMpService.getQrcodeService().qrCodeCreateLastTicket(scene); // 用ticket换取二维码图片 WxMpQrCodeTicket ticket = ...; // 获得在系统临时目录下的文件,需要自己保存使用,注意:临时文件夹下存放的文件不可靠,不要直接使用 File file = wxMpService.getQrcodeService().qrCodePicture(ticket);
一般实际开发中利用带参数二维码绑定自己应用的账号的流程如下:
- 前端请求后端生成二维码,后端生成uuid,将uuid作为二维码的参数(即场景码scene)。然后将
ticket和场景码返回给前端- 前端拿到
ticket和场景码,用ticket向微信服务器请求获取二维码- 用户扫码成功后,会发生事件推送到后端接口,后端判断如果是带参数的扫码事件,将场景码uuid作为key,扫码用户的openid作为value存入redis
- 前端展示完二维码后,可以携带场景码作为参数轮询后端接口,后端通过场景码去redis中获取对应的value,并返回相应的结果给前端,前端根据不同结果做不同处理
- 如果redis中没有场景码对应的key,说明用户还没有扫码,前端不做任何处理
- 如果redis中获取到了场景码对应的用户openid,那么后端判断该openid是否绑定过账号:
- 如果没有绑定过,前端跳转到绑定页面,用户输入用户名密码后请求后端绑定接口完成绑定
- 如果已经绑定过,前端提示登录成功并跳转到主页面
10.消息排重
微信官方推荐的公众号消息排重方式是:
- 关于重试的消息排重,推荐使用
msgid排重 - 事件消息:关于重试的消息排重,推荐使用
FromUserName + CreateTime排重
WxJava采用的是更简单的方式:如果有MsgId就用MsgId排重,如果没有就用FromUserName+CreateTime排重
WxJava消息排重的默认实现是基于内存的排重器WxMessageInMemoryDuplicateChecker,在WxMessageRouter构造的时候会创建

但是默认的基于内存的消息排重器,在应用重启后就无法识别重复的消息了,所以我们也可以实现自定义的WxMessageDuplicateChecker。 只要在WxMessageRouter.route前setMessageDuplicateChecker为自己的实现就行了
下面我们尝试实现一个基于redis的消息排重器:
- 定义一个消息排重器,实现
WxMessageDuplicateChecker接口,并存入容器
@Component @Slf4j public class WxMessageRedisDuplicateChecker implements WxMessageDuplicateChecker {
@Autowired private StringRedisTemplate stringRedisTemplate; @Override public boolean isDuplicate(String messageId) {
log.info("isDuplicate调用,msgId:" + messageId); // 从redis中获取key为messageId的值 String value = stringRedisTemplate.opsForValue().get(messageId); if (value == null){
// 获取不到,说明不是重复消息,存入redis,返回false stringRedisTemplate.opsForValue().set(messageId, "", 10, TimeUnit.SECONDS); return false; } log.info("消息重复"); // 能获取到说明是重复消息,返回true return true; } }
- 在配置类中,创建
WxMessageRouter时为其设置自定义的WxMessageDuplicateChecker

然后为了测试效果,我们在文本消息处理器中故意睡眠,不返回消息给微信

然后随便发送一条文本消息给公众号,即可看到排重效果


版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/16089.html