WxJava微信公众号开发实战

article/2025/10/8 18:26:30

本文从本人博客搬运,原文格式更加美观,可以移步原文阅读: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: appidsecret: secrettoken: token # 配置消息回调地址接入公众号时需要的token
server:port: 8088

编写测试Controller

@RestController
@RequestMapping("/wxjava/mp")
@Slf4j
public class WxjavaTestController {@Autowiredprivate WxMpService wxMpService;@Autowiredprivate 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)){// 校验成功原样返回echostrreturn echostr;}// 校验失败return null;
}

可以看出,与之前我们自己实现复杂的校验流程相比,代码简洁了很多

3.接收与响应消息

WxJava为了对不同类型的微信消息进行分类处理,避免出现很多if/else判断,设计了如下的消息处理流程:

  1. 针对不同类型的消息处理,我们需要自己实现消息处理器,消息处理器必须实现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;}
  1. 自定义路由,将不同类型的消息交给不同的消息处理器来处理。路由对象为WxMpMessageRouter

根据上述流程,我们首先定义2个消息处理器,分别实现对文本消息和图片消息的处理,并存入Spring容器

// 文本消息处理器
@Component
public class TextHandler implements WxMpMessageHandler {@Overridepublic 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 {@Overridepublic 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 {@Autowiredprivate WxMpService wxMpService;@Autowiredprivate TextHandler textHandler;@Autowiredprivate ImageHandler imageHandler;@Beanpublic 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 {@Overridepublic 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 {@Overridepublic 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();// 创建按钮1WxMenuButton button1 = new WxMenuButton();button1.setType(WxConsts.MenuButtonType.CLICK);button1.setName("今日歌曲");button1.setKey("V1001_TODAY_MUSIC");// 创建按钮2WxMenuButton button2 = new WxMenuButton();button2.setName("菜单");// 创建按钮2的子按钮1WxMenuButton button21 = new WxMenuButton();button21.setType(WxConsts.MenuButtonType.VIEW);button21.setName("搜索");button21.setUrl("https://www.baidu.com/");// 创建按钮2的子按钮2WxMenuButton button22 = new WxMenuButton();button22.setType(WxConsts.MenuButtonType.VIEW);button22.setName("视频");button22.setUrl("https://v.qq.com/");// 创建按钮2的子按钮3WxMenuButton button23 = new WxMenuButton();button23.setType(WxConsts.MenuButtonType.CLICK);button23.setName("赞一下我们");button23.setKey("V1001_GOOD");// 将子按钮添加到按钮2button2.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牛逼"));// 发送模板消息,返回消息idreturn 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.130port: 6379database: 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();// 构建授权urlreturn oAuth2Service.buildAuthorizationUrl("https://baobao.cn.utools.club/wxjava/mp/callback", WxConsts.OAuth2Scope.SNSAPI_USERINFO, null);
}

其中buildAuthorizationUrl的说明如下:

/*** <pre>* 构造oauth2授权的url连接.* 详情请见: http://mp.weixin.qq.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获取accessTokenWxOAuth2AccessToken accessToken = oAuth2Service.getAccessToken(code);// 利用accessToken获取用户信息WxOAuth2UserInfo userInfo = oAuth2Service.getUserInfo(accessToken, null);return userInfo;
}

测试:首先访问/buildAuthPage,获取授权url

将授权url复制到微信浏览器中打开,同意授权后进入回调

9.生成带参数的二维码

一般情况下用户如果需要关注公众号,只需扫描微信提供的固定二维码即可。但为了满足用户渠道推广分析和用户帐号绑定等场景的需要,公众平台提供了生成带参数二维码的接口。使用该接口可以获得多个带不同场景值的二维码,用户扫描后,公众号可以接收到事件推送

目前有2种类型的二维码:

  • 临时二维码,是有过期时间的,最长可以设置为在二维码生成后的30天(即2592000秒)后过期,但能够生成较多数量。临时二维码主要用于帐号绑定等不要求二维码永久保存的业务场景
  • 永久二维码,是无过期时间的,但数量较少(目前为最多10万个)。永久二维码主要用于适用于帐号绑定、用户来源统计等场景。

用户扫描带场景值二维码时,可能推送以下两种事件:

  • 如果用户还未关注公众号,则用户可以关注公众号,关注后微信会将带场景值关注事件推送给开发者。

  • 如果用户已经关注公众号,在用户扫描后会自动进入会话,微信也会将带场景值扫描事件推送给开发者。

获取带参数的二维码的过程包括两步,首先创建二维码ticket,然后凭借ticket到指定URL换取二维码,步骤说明如下:

  1. 创建二维码ticket:每次创建二维码ticket需要提供一个开发者自行设定的参数(scene_id),分别介绍临时二维码和永久二维码的创建二维码ticket过程:
    • 临时二维码请求说明:
      • http请求方式: POST URL: https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=TOKEN
      • POST数据格式:json
      • POST数据例子:{"expire_seconds": 604800, "action_name": "QR_SCENE", "action_info": {"scene": {"scene_id": 123}}} 或者也可以使用以下POST数据创建字符串形式的二维码参数:{"expire_seconds": 604800, "action_name": "QR_STR_SCENE", "action_info": {"scene": {"scene_str": "test"}}}
    • 永久二维码请求说明:
      • http请求方式: POST URL: https://api.weixin.qq.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该二维码有效时间,以秒为单位。 最大不超过2592000(即30天),此字段如果不填,则默认有效期为30秒。
action_name二维码类型,QR_SCENE为临时的整型参数值,QR_STR_SCENE为临时的字符串参数值,QR_LIMIT_SCENE为永久的整型参数值,QR_LIMIT_STR_SCENE为永久的字符串参数值
action_info二维码详细信息
scene_id场景值ID,临时二维码时为32位非0整型,永久二维码时最大值为100000(目前参数只支持1–100000)
scene_str场景值ID(字符串形式的ID),字符串类型,长度限制为1到64

正确的Json返回结果:

{"ticket":"gQH47joAAAAAAAAAASxodHRwOi8vd2VpeGluLnFxLmNvbS9xL2taZ2Z3TVRtNzJXV1Brb3ZhYmJJAAIEZ23sUwMEmm
3sUw==","expire_seconds":60,"url":"http://weixin.qq.com/q/kZgfwMTm72WWPkovabbI"}
参数说明
ticket获取的二维码ticket,凭借此ticket可以在有效时间内换取二维码。
expire_seconds该二维码有效时间,以秒为单位。 最大不超过2592000(即30天)。
url二维码图片解析后的地址,开发者可根据该地址自行生成需要的二维码图片
  1. 通过ticket换取二维码:获取二维码ticket后,开发者可用ticket换取二维码图片。请注意,本接口无须登录态即可调用
HTTP GET请求(请使用https协议)https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=TICKET # 提醒:TICKET记得进行UrlEncode

ticket正确情况下,http 返回码是200,是一张图片,可以直接展示或者下载

HTTP头(示例)如下: 
Accept-Ranges:bytes 
Cache-control:max-age=604800 
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>123456789</CreateTime><MsgType><![CDATA[event]]></MsgType><Event><![CDATA[subscribe]]></Event><EventKey><![CDATA[qrscene_123123]]></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>123456789</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);

一般实际开发中利用带参数二维码绑定自己应用的账号的流程如下:

  1. 前端请求后端生成二维码,后端生成uuid,将uuid作为二维码的参数(即场景码scene)。然后将ticket和场景码返回给前端
  2. 前端拿到ticket和场景码,用ticket向微信服务器请求获取二维码
  3. 用户扫码成功后,会发生事件推送到后端接口,后端判断如果是带参数的扫码事件,将场景码uuid作为key,扫码用户的openid作为value存入redis
  4. 前端展示完二维码后,可以携带场景码作为参数轮询后端接口,后端通过场景码去redis中获取对应的value,并返回相应的结果给前端,前端根据不同结果做不同处理
    • 如果redis中没有场景码对应的key,说明用户还没有扫码,前端不做任何处理
    • 如果redis中获取到了场景码对应的用户openid,那么后端判断该openid是否绑定过账号:
      • 如果没有绑定过,前端跳转到绑定页面,用户输入用户名密码后请求后端绑定接口完成绑定
      • 如果已经绑定过,前端提示登录成功并跳转到主页面

10.消息排重

微信官方推荐的公众号消息排重方式是:

  • 关于重试的消息排重,推荐使用msgid排重
  • 事件消息:关于重试的消息排重,推荐使用FromUserName + CreateTime排重

WxJava采用的是更简单的方式:如果有MsgId就用MsgId排重,如果没有就用FromUserName+CreateTime排重

WxJava消息排重的默认实现是基于内存的排重器WxMessageInMemoryDuplicateChecker,在WxMessageRouter构造的时候会创建

但是默认的基于内存的消息排重器,在应用重启后就无法识别重复的消息了,所以我们也可以实现自定义的WxMessageDuplicateChecker。 只要在WxMessageRouter.routesetMessageDuplicateChecker为自己的实现就行了

下面我们尝试实现一个基于redis的消息排重器:

  1. 定义一个消息排重器,实现WxMessageDuplicateChecker接口,并存入容器
@Component
@Slf4j
public class WxMessageRedisDuplicateChecker implements WxMessageDuplicateChecker {@Autowiredprivate StringRedisTemplate stringRedisTemplate;@Overridepublic boolean isDuplicate(String messageId) {log.info("isDuplicate调用,msgId:" + messageId);// 从redis中获取key为messageId的值String value = stringRedisTemplate.opsForValue().get(messageId);if (value == null){// 获取不到,说明不是重复消息,存入redis,返回falsestringRedisTemplate.opsForValue().set(messageId, "", 10, TimeUnit.SECONDS);return false;}log.info("消息重复");// 能获取到说明是重复消息,返回truereturn true;}
}
  1. 在配置类中,创建WxMessageRouter时为其设置自定义的WxMessageDuplicateChecker

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

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


http://chatgpt.dhexx.cn/article/bQIf0Z2O.shtml

相关文章

【程序源代码】微信公众号开发框架

关键字&#xff1a;java 管理系统 公众号开发框架 正文 | 内容 01 — 【概述】 本项目基于Spring Cloud 和Ant Design Pro实现前后端管理平台一站式脚手架&#xff0c;便于快速开发企业级应用。 02 — 【技术框架】 软件架构 核心框架&#xff1a;Spring Boot 2.2.5.RELEAS…

微信公众号开发(三)前端界面

有问题可以关注公众号&#xff1a;提着月亮去火星 由于该公众号主要的用途是用于公司售后服务,经沟通确定相关需求. 第一: 了解相关的UI框架,最终选定了Jquery-WeUI框架. 第二: 制作简单案例,在移动端进行测试,要达到移动端的自适应. 目前完成三个界面如下: 上面的就是 目前…

Java开发微信公众号之整合weixin-java-tools框架开发微信公众号

微信开发者接入文档 &#xff1a; https://mp.weixin.qq.com/wiki?tresource/res_main&idmp1421135319 微信公众平台测试账号申请&#xff1a; http://mp.weixin.qq.com/debug/cgi-bin/sandbox?tsandbox/login 一、前言 首先&#xff0c;在要做微信项目开发前&#xf…

微信公众号开发入门

实在是太折腾&#xff0c;太难懂了。也太坑了。 下面是这几天来&#xff0c;有关微信公众号的工作总结。算不上全面&#xff0c;只是作为一个初学者的记录&#xff0c;仅以备忘。 一、微信公众号开发&#xff0c;开发什么&#xff1f; 公众号与小程序不同。小程序类似手机AP…

微信公众平台快速开发框架源码

ASP.NET微信快速开发框架源码 微信公众平台快速开发框架源码 框架主要技术&#xff1a; ASP.NET MVC5、ASP.NET Identity、Bootstrap、KnockoutJs、Entity Framework等。 主要特色&#xff1a; 快速迭代开发&#xff0c;提供最好的威信开发框架&#xff1b; 基于ASP.NET MVC5Bo…

微信公众号开发,原来这么简单?[java框架]

终于有时间更新啦&#xff01;&#xff01;最新教程双手奉上&#xff01;公众号开发之wx-toolsspringboot应用实战-音乐爬虫推送[JAVA] 下面都是旧的教程&#xff0c;建议参考上面最新的教程~~ 可能你不知道&#xff0c;有个框架叫wx-tools&#xff08;小心翼翼地说&#xff09…

公众号开发(2) —— 盛派.net SDK + vue搭建微信公众号网页开发框架

需求&#xff1a;通过微信公众号菜单跳转到手机端网页&#xff0c;跳转后通过微信授权登录获取微信公众号用户的OpenId&#xff08;用户关注公众号后&#xff0c;用户在公众号的唯一凭证&#xff09;&#xff0c;通过OpenId和后台数据库用户信息绑定起来并实现一些业务逻辑。 技…

微信公众号开发框架讲解

微信公众号的开发在基于微信给的开发文档基础上&#xff0c;特别做微商城那底层用Java框架会相当方便。下面长沙Java培训给大家具体讲讲微信公众号开发框架&#xff1a; 核心框架&#xff1a;Spring Boot2 Spring Cloud Alibaba Spring Cloud Gateway 配置中心、注册中心&am…

人工智能博弈树极大极小搜索算法alpha-beta剪枝实现五子棋,带禁手

由于2020的特殊情况&#xff0c;导致了一个被拖了挺久的大作业。。。。 五子棋其实大家很多时候会在闲暇时刻和朋友随便玩玩&#xff0c;这不仅让我回忆起了高中时候摸鱼休息就喜欢和同学在自己打的格子中用铅笔来一盘五子棋&#xff0c;回想起来确实是至今以来最快乐的一段时光…

基于Python的人机交互的五子棋博弈树搜索

1. 算法原理 1.1 博弈树 博弈树针对的是二人零和博弈的问题&#xff0c;二人轮流行动&#xff0c;行动时令自己的优势最大。二人零和博弈有如下特点&#xff1a; 确定性&#xff1a;二人的行动有多种选择&#xff0c;但最终的行动是确定的信息完备性&#xff1a;博弈双方知道…

C++实现基于博弈树的5x5一子棋人机对战

基于博弈树的5x5一子棋人机对战 919106840637实验2 这是智能计算三个课程实验的第二个实验&#xff0c;即博弈树搜索 。我之前对博弈树的了解不多&#xff0c;所以实现起来比较的简略&#xff0c;仅仅是基本达到了要求 实验语言 C 实验内容 实践博弈树搜索——“5x5格子的一…

人工智能作业——搜索树博弈树一阶逻辑表达式CNF范式

1. 人工智能定义 1. 简述什么是人工智能 人工智能可分为两个维度:一个维度是从思维推理过程到行为结果(过程与结果);另一个维度是与人类表现的逼真度到数学与工程结合后的精确性(主观与客观)。 像人—样行动&#xff1a;图灵测试的途径&#xff1b;像人—样思考&#xff1a;认…

AlphaBeta剪枝算法求解博弈树最优选择 头歌实验平台

AlphaBeta剪枝算法求解博弈树最优选择 头歌实验平台 前言一、AlphaBeta剪枝是什么&#xff1f;1.由来, 最大最小决策树2.发展3. AlphaBeta剪枝 二、实验算法伪代码三、实验算法代码四、实验结果在这里插入图片描述 五、感谢 前言 产生本文的缘由 人工智能原理课程 可选实验 “…

并行博弈树搜索算法-第3篇 优秀的园丁:Alpha-Beta算法

3.1 Alpha-Beta算法 虽然博弈树的状态是有限的,但是状态个数却非常多.假设博弈树的深度为d,每个结点有b个分支,即分支因子&#xff08;branchingfactor&#xff09;为b,那么使用Min-Max方法搜索这个博弈树需要搜索个结点. 然而幸运的是,Min-Max方法的一些搜索是没有必要的,…

利用α-β搜索的博弈树算法编写一字棋游戏 python

游戏规则 “一字棋"游戏&#xff08;又叫"三子棋"或"井字棋”&#xff09;&#xff0c;是一款十分经典的益智小游戏。“井字棋"的棋盘很简单&#xff0c;是一个 33 的格子&#xff0c;很像中国文字中的"井"字&#xff0c;所以得名"井字…

博弈树 极小极大分析法

一、博弈概述 博弈特点&#xff1a; &#xff08;1&#xff09;博弈的初始格局是初始节点 &#xff08;2&#xff09;在博弈树中&#xff0c;“或”和“与”是交替出现的。自己一方的扩展节点是“或”关系&#xff0c;对方扩展的节点是“与”关系。双方轮流扩展节点。 &…

第6-2课:决策树、博弈树和行为树

在以各种“XX学习”为代表的人工智能技术普及之前,游戏里常见的角色 AI 都是各种预设的行为逻辑,比如博弈树和行为树,当然也会用到各种专家知识库。当这些预设的行为逻辑足够复杂的时候,往往会让游戏玩家觉得游戏里的人物很“智能”。从本质上来说,这些都还不算是真正的 A…

并行博弈树搜索算法-第4篇 更上一层楼:Alpha-Beta算法的改进

在Alpha-Beta算法被广泛运用后,对该算法的很多改进算法也相继被提出.这些改进算法主要在以下几个方面对Alpha-Beta算法进行改进[7]: 1. 择序&#xff08;ordering&#xff09;.在搜索博弈树时,内部结点有多个可能的移动.择序指的是搜索这些分支的顺序.择序影响着搜索叶结点的个…

博弈树的启发式搜索

什么是博弈树 在博弈过程中, 任何一方都希望自己取得胜利。因此,当某一方当前有多个行动方案可供选择时, 他总是挑选对自己最为有利而对对方最为不利的那个行动方案。 此时,如果我们站在A方的立场上,则可供A方选择的若干行动方案之间是“或”关系, 因为主动权操在A方手里,他或…

博弈树 α-β剪枝

由于搜索的复杂度有点高&#xff0c;所以在树上减少计算量肯定是剪枝了&#xff0c;这里我们把剪枝的办法称作的&#xff1a;α-β剪枝 我们在前面的文章中谈到&#xff0c;当第一次运作的是A&#xff0c;则所有的奇数深度的节点都是A做的选择&#xff0c;所有偶数深度的节点都…