java springboot集成微信小程序【详细教程】

简介: java springboot集成微信小程序【详细教程】

申请小程序账号

注册小程序点我

https://mp.weixin.qq.com/cgi-bin/registermidpage?action=index&lang=zh_CN&token=

引入依赖

<!-- 小程序依赖 -->
<dependency>
    <groupId>com.github.binarywang</groupId>
    <artifactId>weixin-java-miniapp</artifactId>
    <version>4.3.0</version>
</dependency>
<dependency>
    <groupId>com.github.binarywang</groupId>
    <artifactId>weixin-java-common</artifactId>
    <version>4.3.0</version>
</dependency>
<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
</dependency>  

yml配置

wx:
  miniapp:
    configs:
      - appid: 12313#微信小程序的appid
        secret: 1231313#微信小程序的Secret
        token:  #微信小程序消息服务器配置的token
        aesKey:  #微信小程序消息服务器配置的EncodingAESKey
        msgDataFormat: JSON

读取配置文件类WxMaProperties

@Data
@ConfigurationProperties(prefix = "wx.miniapp")
public class WxMaProperties {
    private List<Config> configs;
    @Data
    public static class Config {
        /**
         * 设置微信小程序的appid
         */
        private String appid;
        /**
         * 设置微信小程序的Secret
         */
        private String secret;
        /**
         * 设置微信小程序消息服务器配置的token
         */
        private String token;
        /**
         * 设置微信小程序消息服务器配置的EncodingAESKey
         */
        private String aesKey;
        /**
         * 消息格式,XML或者JSON
         */
        private String msgDataFormat;
    }
}

配置文件加载

@Slf4j
@Configuration
@EnableConfigurationProperties(WxMaProperties.class)
public class WxMaConfiguration {
    private final WxMaProperties properties;
    private static final Map<String, WxMaMessageRouter> routers = Maps.newHashMap();
    private static Map<String, WxMaService> maServices;
    @Autowired
    public WxMaConfiguration(WxMaProperties properties) {
        this.properties = properties;
    }
    public static WxMaService getMaService(String appid) {
        WxMaService wxService = maServices.get(appid);
        if (wxService == null) {
            throw new IllegalArgumentException(String.format("未找到对应appid=[%s]的配置,请核实!", appid));
        }
        return wxService;
    }
    public static WxMaMessageRouter getRouter(String appid) {
        return routers.get(appid);
    }
    @PostConstruct
    public void init() {
        List<WxMaProperties.Config> configs = this.properties.getConfigs();
        if (configs == null) {
            throw new WxRuntimeException("大哥,拜托先看下项目首页的说明(readme文件),添加下相关配置,注意别配错了!");
        }
        maServices = configs.stream()
            .map(a -> {
                WxMaDefaultConfigImpl config = new WxMaDefaultConfigImpl();
//                WxMaDefaultConfigImpl config = new WxMaRedisConfigImpl(new JedisPool());
                // 使用上面的配置时,需要同时引入jedis-lock的依赖,否则会报类无法找到的异常
                config.setAppid(a.getAppid());
                config.setSecret(a.getSecret());
                config.setToken(a.getToken());
                config.setAesKey(a.getAesKey());
                config.setMsgDataFormat(a.getMsgDataFormat());
                WxMaService service = new WxMaServiceImpl();
                service.setWxMaConfig(config);
                routers.put(a.getAppid(), this.newRouter(service));
                return service;
            }).collect(Collectors.toMap(s -> s.getWxMaConfig().getAppid(), a -> a));
    }
    private WxMaMessageRouter newRouter(WxMaService service) {
        final WxMaMessageRouter router = new WxMaMessageRouter(service);
        router
            .rule().handler(logHandler).next()
            .rule().async(false).content("订阅消息").handler(subscribeMsgHandler).end()
            .rule().async(false).content("文本").handler(textHandler).end()
            .rule().async(false).content("图片").handler(picHandler).end()
            .rule().async(false).content("二维码").handler(qrcodeHandler).end();
        return router;
    }
    private final WxMaMessageHandler subscribeMsgHandler = (wxMessage, context, service, sessionManager) -> {
        service.getMsgService().sendSubscribeMsg(WxMaSubscribeMessage.builder()
            .templateId("此处更换为自己的模板id")
            .data(Lists.newArrayList(
                new WxMaSubscribeMessage.MsgData("keyword1", "339208499")))
            .toUser(wxMessage.getFromUser())
            .build());
        return null;
    };
    private final WxMaMessageHandler logHandler = (wxMessage, context, service, sessionManager) -> {
        log.info("收到消息:" + wxMessage.toString());
        service.getMsgService().sendKefuMsg(WxMaKefuMessage.newTextBuilder().content("收到信息为:" + wxMessage.toJson())
            .toUser(wxMessage.getFromUser()).build());
        return null;
    };
    private final WxMaMessageHandler textHandler = (wxMessage, context, service, sessionManager) -> {
        service.getMsgService().sendKefuMsg(WxMaKefuMessage.newTextBuilder().content("回复文本消息")
            .toUser(wxMessage.getFromUser()).build());
        return null;
    };
    private final WxMaMessageHandler picHandler = (wxMessage, context, service, sessionManager) -> {
        try {
            WxMediaUploadResult uploadResult = service.getMediaService()
                .uploadMedia("image", "png",
                    ClassLoader.getSystemResourceAsStream("tmp.png"));
            service.getMsgService().sendKefuMsg(
                WxMaKefuMessage
                    .newImageBuilder()
                    .mediaId(uploadResult.getMediaId())
                    .toUser(wxMessage.getFromUser())
                    .build());
        } catch (WxErrorException e) {
            e.printStackTrace();
        }
        return null;
    };
    private final WxMaMessageHandler qrcodeHandler = (wxMessage, context, service, sessionManager) -> {
        try {
            final File file = service.getQrcodeService().createQrcode("123", 430);
            WxMediaUploadResult uploadResult = service.getMediaService().uploadMedia("image", file);
            service.getMsgService().sendKefuMsg(
                WxMaKefuMessage
                    .newImageBuilder()
                    .mediaId(uploadResult.getMediaId())
                    .toUser(wxMessage.getFromUser())
                    .build());
        } catch (WxErrorException e) {
            e.printStackTrace();
        }
        return null;
    };
}

json返回工具类

public class JsonUtils {
    private static final ObjectMapper JSON = new ObjectMapper();
    static {
        JSON.setSerializationInclusion(Include.NON_NULL);
        JSON.configure(SerializationFeature.INDENT_OUTPUT, Boolean.TRUE);
    }
    public static String toJson(Object obj) {
        try {
            return JSON.writeValueAsString(obj);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        return null;
    }
}

控制层

小程序临时素材接口

@RestController
@RequestMapping("/wx/media/{appid}")
public class WxMaMediaController {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());
    /**
     * 上传临时素材
     *
     * @return 素材的media_id列表,实际上如果有的话,只会有一个
     */
    @PostMapping("/upload")
    public List<String> uploadMedia(@PathVariable String appid, HttpServletRequest request) throws WxErrorException {
        final WxMaService wxService = WxMaConfiguration.getMaService(appid);
        CommonsMultipartResolver resolver = new CommonsMultipartResolver(request.getSession().getServletContext());
        if (!resolver.isMultipart(request)) {
            return Lists.newArrayList();
        }
        MultipartHttpServletRequest multiRequest = (MultipartHttpServletRequest) request;
        Iterator<String> it = multiRequest.getFileNames();
        List<String> result = Lists.newArrayList();
        while (it.hasNext()) {
            try {
                MultipartFile file = multiRequest.getFile(it.next());
                File newFile = new File(Files.createTempDir(), file.getOriginalFilename());
                this.logger.info("filePath is :" + newFile.toString());
                file.transferTo(newFile);
                WxMediaUploadResult uploadResult = wxService.getMediaService().uploadMedia(WxMaConstants.KefuMsgType.IMAGE, newFile);
                this.logger.info("media_id : " + uploadResult.getMediaId());
                result.add(uploadResult.getMediaId());
            } catch (IOException e) {
                this.logger.error(e.getMessage(), e);
            }
        }
        return result;
    }
    /**
     * 下载临时素材
     */
    @GetMapping("/download/{mediaId}")
    public File getMedia(@PathVariable String appid, @PathVariable String mediaId) throws WxErrorException {
        final WxMaService wxService = WxMaConfiguration.getMaService(appid);
        return wxService.getMediaService().getMedia(mediaId);
    }
}


微信小程序用户接口

@RestController
@RequestMapping("/wx/user/{appid}")
public class WxMaUserController {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());
    /**
     * 登陆接口
     */
    @GetMapping("/login")
    public String login(@PathVariable String appid, String code) {
        if (StringUtils.isBlank(code)) {
            return "empty jscode";
        }
        final WxMaService wxService = WxMaConfiguration.getMaService(appid);
        try {
            WxMaJscode2SessionResult session = wxService.getUserService().getSessionInfo(code);
            this.logger.info(session.getSessionKey());
            this.logger.info(session.getOpenid());
            //TODO 可以增加自己的逻辑,关联业务相关数据
            return JsonUtils.toJson(session);
        } catch (WxErrorException e) {
            this.logger.error(e.getMessage(), e);
            return e.toString();
        }
    }
    /**
     * <pre>
     * 获取用户信息接口
     * </pre>
     */
    @PostMapping("/info")
    public String info(@PathVariable String appid, @RequestBody Map<String, String> map) {
        final WxMaService wxService = WxMaConfiguration.getMaService(appid);
        String sessionKey = map.get("sessionKey");
        String signature = map.get("signature");
        String rawData = map.get("rawData");
        String encryptedData = map.get("encryptedData");
        String iv = map.get("iv");
        // 用户信息校验
        if (!wxService.getUserService().checkUserInfo(sessionKey, rawData, signature)) {
            return "user check failed";
        }
        // 解密用户信息
        WxMaUserInfo userInfo = wxService.getUserService().getUserInfo(sessionKey, encryptedData, iv);
        return JsonUtils.toJson(userInfo);
    }
    /**
     * <pre>
     * 获取用户绑定手机号信息
     * </pre>
     */
    @PostMapping("/phone")
    public String phone(@PathVariable String appid, @RequestBody Map<String, String> map) {
        final WxMaService wxService = WxMaConfiguration.getMaService(appid);
        String sessionKey = map.get("sessionKey");
        String signature = map.get("signature");
        String rawData = map.get("rawData");
        String encryptedData = map.get("encryptedData");
        String iv = map.get("iv");
        // 用户信息校验
        if (!wxService.getUserService().checkUserInfo(sessionKey, rawData, signature)) {
            return "user check failed";
        }
        // 解密
        WxMaPhoneNumberInfo phoneNoInfo = wxService.getUserService().getPhoneNoInfo(sessionKey, encryptedData, iv);
        return JsonUtils.toJson(phoneNoInfo);
    }
}


前端代码-小程序项目哦,hubild直接创建

<template>
  <view class="content">
    <image class="logo" src="/static/logo.png"></image>
    <view class="text-area">
      <text class="title">{{title}}</text>
    </view>
    <view>
      <button @click="login">登录</button>
      <!-- 先点击 登录、获取头像昵称按钮才可以点击下面这两个按钮!!! -->
      <button @click="info">获取用户信息</button>
      <button @click="phone">获取手机号信息</button>
    </view>
    <view class="container">
      <view class="userinfo">
        <block>
          <button @click="getUserProfile"> 获取头像昵称 </button>
        </block>
      </view>
    </view>
  </view>
</template>
<script>
  export default {
    data() {
      return {
        title: 'Hello2',
        openId: "",
        session: "",
        userInfo: {},
        hasUserInfo: false,
        canIUseGetUserProfile: false,
        signature:'',
        rawData:'',
        encryptedData:'',
        iv:''
      }
    },
    onLoad() {
    },
    methods: {
      login() {
        let that = this;
        wx.login({
          success (res) {
            console.log("res::",res)
            if (res.code) {
              //发起网络请求
              wx.request({
                url: 'http://localhost:8080/wx/user/wx35784428afa871df/login?code='+res.code,
            method: 'GET',
            success(res) {
              console.log("success res :" ,res)
              that.openId = res.data.openid;
              that.session = res.data.sessionKey;
            },
            fail(res) {
              console.log("fail res : ", res)
            }
              })
            } else {
              console.log('登录失败!' + res.errMsg)
            }
          }
        })
      },
      info() {
        console.log("sessionKey::",this.session)
        wx.request({
          url: 'http://localhost:8080/wx/user/wx35784428afa871df/info',
          method: 'POST',
          data:{
            sessionKey: this.session,
            signature: this.signature,
            rawData: this.rawData,
            encryptedData: this.encryptedData,
            iv: this.iv
          },
          success(res) {
            console.log("后端获取:success res :" ,res)
          },
          fail(res) {
            console.log("后端获取:fail res : ", res)
          }
        })
      },
      phone() {
        wx.request({
          url: 'http://localhost:8080/wx/user/wx35784428afa871df/phone',
          method: 'POST',
          data:{
            sessionKey: this.session,
            signature: this.signature,
            rawData: this.rawData,
            encryptedData: this.encryptedData,
            iv: this.iv
          },
          success(res) {
            console.log("后端获取手机号:success res :" ,res)
          },
          fail(res) {
            console.log("后端获取手机号:fail res : ", res)
          }
        })
      },
      getUserProfile(e) {
        let that = this;
          // 推荐使用 wx.getUserProfile 获取用户信息,开发者每次通过该接口获取用户个人信息均需用户确认
          // 开发者妥善保管用户快速填写的头像昵称,避免重复弹窗
          wx.getUserProfile({
            desc: '用于完善会员资料', // 声明获取用户个人信息后的用途,后续会展示在弹窗中,请谨慎填写
            success: (res) => {
              console.log("user info res :", res)
          that.signature = res.signature;
          that.rawData = res.rawData;
          that.encryptedData = res.encryptedData;
          that.iv = res.iv;
            }
          })
        },
        getUserInfo(e) {
          // 不推荐使用 getUserInfo 获取用户信息,预计自2021年4月13日起,getUserInfo将不再弹出弹窗,并直接返回匿名的用户个人信息
          this.setData({
            userInfo: e.detail.userInfo,
            hasUserInfo: true
          })
        },
    }
  }
</script>
<style>
  .content {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
  }
  .logo {
    height: 200rpx;
    width: 200rpx;
    margin-top: 200rpx;
    margin-left: auto;
    margin-right: auto;
    margin-bottom: 50rpx;
  }
  .text-area {
    display: flex;
    justify-content: center;
  }
  .title {
    font-size: 36rpx;
    color: #8f8f94;
  }
</style>

界面效果图

效果

3958b4b3689e4d1585a1d68dac6e244c.png



点击登录

返回code码,我们拿code码进行获取openid


4844966aa6a74d90bcb2ae7ff738990f.png


获取头像昵称

拿到返回的信息进行获取用户信息


27c172f1ed7a4303a425b3dd359402b6.png


获取用户信息


f2907e19ec1945649f4032d26bbbbb1a.png

82be9a4aa77a4d6caff24b11427efe41.png



以上就完成了哦,简单教程


相关实践学习
消息队列+Serverless+Tablestore:实现高弹性的电商订单系统
基于消息队列以及函数计算,快速部署一个高弹性的商品订单系统,能够应对抢购场景下的高并发情况。
消息队列 MNS 入门课程
1、消息队列MNS简介 本节课介绍消息队列的MNS的基础概念 2、消息队列MNS特性 本节课介绍消息队列的MNS的主要特性 3、MNS的最佳实践及场景应用 本节课介绍消息队列的MNS的最佳实践及场景应用案例 4、手把手系列:消息队列MNS实操讲 本节课介绍消息队列的MNS的实际操作演示 5、动手实验:基于MNS,0基础轻松构建 Web Client 本节课带您一起基于MNS,0基础轻松构建 Web Client
相关文章
|
21天前
|
XML Java 数据库连接
SpringBoot集成Flowable:打造强大的工作流管理系统
在企业级应用开发中,工作流管理是一个核心组件,它能够帮助我们定义、执行和管理业务流程。Flowable是一个开源的工作流和业务流程管理(BPM)平台,它提供了强大的工作流引擎和建模工具。结合SpringBoot,我们可以快速构建一个高效、灵活的工作流管理系统。本文将探讨如何将Flowable集成到SpringBoot应用中,并展示其强大的功能。
71 1
|
27天前
|
小程序 前端开发 算法
|
1月前
|
JavaScript Java 项目管理
Java毕设学习 基于SpringBoot + Vue 的医院管理系统 持续给大家寻找Java毕设学习项目(附源码)
基于SpringBoot + Vue的医院管理系统,涵盖医院、患者、挂号、药物、检查、病床、排班管理和数据分析等功能。开发工具为IDEA和HBuilder X,环境需配置jdk8、Node.js14、MySQL8。文末提供源码下载链接。
|
1月前
|
JSON Java API
springboot集成ElasticSearch使用completion实现补全功能
springboot集成ElasticSearch使用completion实现补全功能
35 1
|
2月前
|
JSON Java 开发工具
Java服务端集成Google FCM推送的注意事项和实际经验
本文分享了作者在公司APP海外发布过程中,选择Google FCM进行消息推送的集成经验。文章详细解析了Java集成FCM推送的多种实现方式,包括HTTP请求和SDK集成,并指出了通知栏消息和透传消息的区别与应用场景。同时,作者还探讨了Firebase项目的创建、配置和服务端集成的注意事项,帮助读者解决文档混乱和选择困难的问题。
60 1
|
21天前
|
XML 存储 Java
SpringBoot集成Flowable:构建强大的工作流引擎
在企业级应用开发中,工作流管理是核心功能之一。Flowable是一个开源的工作流引擎,它提供了BPMN 2.0规范的实现,并且与SpringBoot框架完美集成。本文将探讨如何使用SpringBoot和Flowable构建一个强大的工作流引擎,并分享一些实践技巧。
58 0
|
2月前
|
前端开发 Java 程序员
springboot 学习十五:Spring Boot 优雅的集成Swagger2、Knife4j
这篇文章是关于如何在Spring Boot项目中集成Swagger2和Knife4j来生成和美化API接口文档的详细教程。
114 1
|
2月前
|
缓存 Java 程序员
Java|SpringBoot 项目开发时,让 FreeMarker 文件编辑后自动更新
在开发过程中,FreeMarker 文件编辑后,每次都需要重启应用才能看到效果,效率非常低下。通过一些配置后,可以让它们免重启自动更新。
30 0
|
2月前
|
安全 算法 Java
数据库信息/密码加盐加密 —— Java代码手写+集成两种方式,手把手教学!保证能用!
本文提供了在数据库中对密码等敏感信息进行加盐加密的详细教程,包括手写MD5加密算法和使用Spring Security的BCryptPasswordEncoder进行加密,并强调了使用BCryptPasswordEncoder时需要注意的Spring Security配置问题。
152 0
数据库信息/密码加盐加密 —— Java代码手写+集成两种方式,手把手教学!保证能用!
|
2月前
|
Java Spring
springboot 学习十一:Spring Boot 优雅的集成 Lombok
这篇文章是关于如何在Spring Boot项目中集成Lombok,以简化JavaBean的编写,避免冗余代码,并提供了相关的配置步骤和常用注解的介绍。
105 0