SpringBoot集成微信小程序 (一)【搭建】_weixin-java-miniapp
一、背景
小程序是一种全新的连接用户与服务的方式,它可以在微信内被便捷地获取和传播,同时具有出色的使用体验。
微信小程序官方文档:https://developers.weixin.qq.com/miniprogram/dev/framework/
二、技术栈
- SpringBoot 2.0
- MyBatis-Plus
- MySQL
- Redis
- Sa-Token
- weixin-java-miniapp
三、实现
3.1 引入依赖
<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd\"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.7.17</version> <relativePath/> </parent> <groupId>com.qiangesoft</groupId> <artifactId>wechat-miniapp</artifactId> <version>1.0.0</version> <name>wechat-miniapp</name> <packaging>jar</packaging> <description>微信小程序</description> <properties> <java.version>1.8</java.version> <maven.compiler.source>${java.version}</maven.compiler.source> <maven.compiler.target>${java.version}</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <dependency> <groupId>cn.dev33</groupId> <artifactId>sa-token-spring-boot-starter</artifactId> <version>1.37.0</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.11</version> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.3</version> </dependency> <dependency> <groupId>com.github.xiaoymin</groupId> <artifactId>knife4j-openapi2-spring-boot-starter</artifactId> <version>4.4.0</version> </dependency> <dependency> <groupId>com.alibaba.fastjson2</groupId> <artifactId>fastjson2</artifactId> <version>2.0.34</version> </dependency> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-core</artifactId> <version>5.8.27</version> </dependency> <dependency> <groupId>com.github.binarywang</groupId> <artifactId>weixin-java-miniapp</artifactId> <version>4.5.0</version> </dependency> </dependencies> <build> <finalName>wxminiapp</finalName> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <mainClass>com.qiangesoft.miniapp.WxMaApplication</mainClass> </configuration> <executions> <execution> <goals> <goal>repackage</goal> </goals> </execution> </executions> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.10.1</version> <configuration> <source>${maven.compiler.source}</source> <target>${maven.compiler.target}</target> </configuration> </plugin> </plugins> </build></project>
3.2 yml配置
server: port: 8087spring: # 数据库配置 datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/wechat_ma?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8 username: root password: root # redis配置 redis: host: localhost port: 6379 username: password: timeout: 2000 lettuce: pool: max-active: 8 max-idle: 8 max-wait: 0 min-idle: 0# mybatis-plus配置mybatis-plus: # MyBaits别名包扫描路径 type-aliases-package: com.qiangesoft.miniapp.entity # Mapper所对应的XML文件位置 默认【classpath*:/mapper/**/*.xml】 mapper-locations: classpath*:/mapper/*Mapper.xml configuration: # 日志打印 log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 是否开启自动驼峰命名规则 map-underscore-to-camel-case: true global-config: db-config: # 全局默认主键类型 id-type: auto # 逻辑删除配置 logic-delete-field: deleted logic-delete-value: 1 logic-not-delete-value: 0# Sa-Token配置sa-token: # token名称(同时也是 cookie 名称) token-name: satoken # token有效期(单位:秒)默认30天 timeout: 2592000 # token最低活跃频率(单位:秒) active-timeout: -1 # 是否允许同一账号多地同时登录 is-concurrent: true # 在多人登录同一账号时,是否共用一个 token is-share: true # token 风格(默认可取值:uuid、simple-uuid、random-32、random-64、random-128、tik) token-style: uuid # 是否输出操作日志 is-log: true# 微信小程序配置wx: miniapp: appid: xxx secret: xxx token: aesKey: msg-data-format: JSON config-storage: type: redis key-prefix: wa http-client-type: httpclient http-proxy-host: http-proxy-username: http-proxy-password: retry-sleep-millis: max-retry-times:# 接口文档配置knife4j: enable: true openapi: title: 微信小程序 description: 微信小程序 email: xxx@qq.com concat: xxx url: https://www.baidu.com/ version: v1.0 license: Apache 2.0 license-url: https://stackoverflow.com/ terms-of-service-url: https://stackoverflow.com/ group: default: group-name: 默认 api-rule: package api-rule-resources: - com.qiangesoft.miniapp.controller
3.3 配置属性类
package com.qiangesoft.miniapp.properties;import com.qiangesoft.miniapp.enums.HttpClientType;import com.qiangesoft.miniapp.enums.StorageType;import lombok.Data;import org.springframework.boot.context.properties.ConfigurationProperties;/** * 属性配置类 * * @author qiangesoft * @since 2024-07-16 */@Data@ConfigurationProperties(prefix = WxMaProperties.PREFIX)public class WxMaProperties { public static final String PREFIX = \"wx.miniapp\"; /** * 微信小程序的appid. */ private String appid; /** * 微信小程序的Secret. */ private String secret; /** * 微信小程序消息服务器配置的token. */ private String token; /** * 微信小程序消息服务器配置的EncodingAESKey. */ private String aesKey; /** * 消息格式,XML或者JSON. */ private String msgDataFormat; /** * 存储策略 */ private final ConfigStorage configStorage = new ConfigStorage(); /** * 存储配置类 */ @Data public static class ConfigStorage { /** * 存储类型. */ private StorageType type = StorageType.Memory; /** * 指定key前缀. */ private String keyPrefix = \"wa\"; /** * http客户端类型. */ private HttpClientType httpClientType = HttpClientType.HttpClient; /** * http代理主机. */ private String httpProxyHost; /** * http代理端口. */ private Integer httpProxyPort; /** * http代理用户名. */ private String httpProxyUsername; /** * http代理密码. */ private String httpProxyPassword; /** * http 请求重试间隔 *
* {@link cn.binarywang.wx.miniapp.api.impl.BaseWxMaServiceImpl#setRetrySleepMillis(int)} **/
private Integer retrySleepMillis = 1000; /** * http 请求最大重试次数 ** {@link cn.binarywang.wx.miniapp.api.impl.BaseWxMaServiceImpl#setMaxRetryTimes(int)} **/
private Integer maxRetryTimes = 5; }}3.4 微信相关存储方式配置类
package com.qiangesoft.miniapp.config.ma.store;import cn.binarywang.wx.miniapp.config.impl.WxMaDefaultConfigImpl;import cn.hutool.core.util.StrUtil;import com.qiangesoft.miniapp.properties.WxMaProperties;/** * 微信配置抽象 * * @author qiangesoft * @since 2024-07-16 */public abstract class AbstractWxMaStorageConfiguration { /** * 配置 * * @param config * @param properties * @return */ protected WxMaDefaultConfigImpl config(WxMaDefaultConfigImpl config, WxMaProperties properties) { // 基本配置 config.setAppid(StrUtil.trimToNull(properties.getAppid())); config.setSecret(StrUtil.trimToNull(properties.getSecret())); config.setToken(StrUtil.trimToNull(properties.getToken())); config.setAesKey(StrUtil.trimToNull(properties.getAesKey())); config.setMsgDataFormat(StrUtil.trimToNull(properties.getMsgDataFormat())); // 代理配置 WxMaProperties.ConfigStorage configStorageProperties = properties.getConfigStorage(); config.setHttpProxyHost(configStorageProperties.getHttpProxyHost()); config.setHttpProxyUsername(configStorageProperties.getHttpProxyUsername()); config.setHttpProxyPassword(configStorageProperties.getHttpProxyPassword()); if (configStorageProperties.getHttpProxyPort() != null) { config.setHttpProxyPort(configStorageProperties.getHttpProxyPort()); } int maxRetryTimes = configStorageProperties.getMaxRetryTimes(); if (configStorageProperties.getMaxRetryTimes() < 0) { maxRetryTimes = 0; } int retrySleepMillis = configStorageProperties.getRetrySleepMillis(); if (retrySleepMillis < 0) { retrySleepMillis = 1000; } config.setRetrySleepMillis(retrySleepMillis); config.setMaxRetryTimes(maxRetryTimes); return config; }}
package com.qiangesoft.miniapp.config.ma.store;import cn.binarywang.wx.miniapp.config.WxMaConfig;import cn.binarywang.wx.miniapp.config.impl.WxMaDefaultConfigImpl;import com.qiangesoft.miniapp.properties.WxMaProperties;import lombok.RequiredArgsConstructor;import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;/** * 内存缓存配置 * * @author qiangesoft * @since 2024-07-16 */@Configuration@ConditionalOnProperty(prefix = WxMaProperties.PREFIX + \".config-storage\", name = \"type\", matchIfMissing = true, havingValue = \"memory\")@RequiredArgsConstructorpublic class MemoryWxMaStorageConfiguration extends AbstractWxMaStorageConfiguration { private final WxMaProperties wxMaProperties; @Bean @ConditionalOnMissingBean(WxMaConfig.class) public WxMaConfig wxMaConfig() { WxMaDefaultConfigImpl config = new WxMaDefaultConfigImpl(); return this.config(config, wxMaProperties); }}
package com.qiangesoft.miniapp.config.ma.store;import cn.binarywang.wx.miniapp.config.WxMaConfig;import cn.binarywang.wx.miniapp.config.impl.WxMaRedisBetterConfigImpl;import com.qiangesoft.miniapp.properties.WxMaProperties;import lombok.RequiredArgsConstructor;import me.chanjar.weixin.common.redis.RedisTemplateWxRedisOps;import me.chanjar.weixin.common.redis.WxRedisOps;import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;import org.springframework.context.ApplicationContext;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.data.redis.core.StringRedisTemplate;/** * redis缓存配置 * * @author qiangesoft * @since 2024-07-16 */@Configuration@ConditionalOnProperty(prefix = WxMaProperties.PREFIX + \".config-storage\", name = \"type\", havingValue = \"redis\")@ConditionalOnClass(StringRedisTemplate.class)@RequiredArgsConstructorpublic class RedisWxMaStorageConfiguration extends AbstractWxMaStorageConfiguration { private final WxMaProperties properties; private final ApplicationContext applicationContext; @Bean @ConditionalOnMissingBean(WxMaConfig.class) public WxMaConfig wxMaConfig() { WxMaRedisBetterConfigImpl config = getWxMaInRedisTemplateConfigStorage(); return this.config(config, properties); } private WxMaRedisBetterConfigImpl getWxMaInRedisTemplateConfigStorage() { StringRedisTemplate redisTemplate = applicationContext.getBean(StringRedisTemplate.class); WxRedisOps redisOps = new RedisTemplateWxRedisOps(redisTemplate); return new WxMaRedisBetterConfigImpl(redisOps, properties.getConfigStorage().getKeyPrefix()); }}
package com.qiangesoft.miniapp.config.ma;import com.qiangesoft.miniapp.config.ma.store.MemoryWxMaStorageConfiguration;import com.qiangesoft.miniapp.config.ma.store.RedisWxMaStorageConfiguration;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.Import;/** * 微信小程序存储策略自动配置 * * @author qiangesoft * @since 2024-07-16 */@Configuration@Import({MemoryWxMaStorageConfiguration.class, RedisWxMaStorageConfiguration.class})public class WxMaStorageAutoConfiguration {}
3.5 平台服务配置类
package com.qiangesoft.miniapp.config.ma;import cn.binarywang.wx.miniapp.api.WxMaService;import cn.binarywang.wx.miniapp.api.impl.WxMaServiceHttpClientImpl;import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl;import cn.binarywang.wx.miniapp.api.impl.WxMaServiceJoddHttpImpl;import cn.binarywang.wx.miniapp.api.impl.WxMaServiceOkHttpImpl;import cn.binarywang.wx.miniapp.config.WxMaConfig;import com.qiangesoft.miniapp.enums.HttpClientType;import com.qiangesoft.miniapp.properties.WxMaProperties;import lombok.AllArgsConstructor;import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;/** * 微信小程序平台相关服务自动注册 * * @author qiangesoft * @since 2024-07-16 */@Configuration@AllArgsConstructorpublic class WxMaServiceAutoConfiguration { private final WxMaProperties wxMaProperties; @Bean @ConditionalOnMissingBean(WxMaService.class) public WxMaService wxMaService(WxMaConfig wxMaConfig) { HttpClientType httpClientType = wxMaProperties.getConfigStorage().getHttpClientType(); WxMaService wxMaService; switch (httpClientType) { case OkHttp: wxMaService = new WxMaServiceOkHttpImpl(); break; case JoddHttp: wxMaService = new WxMaServiceJoddHttpImpl(); break; case HttpClient: wxMaService = new WxMaServiceHttpClientImpl(); break; default: wxMaService = new WxMaServiceImpl(); break; } wxMaService.setWxMaConfig(wxMaConfig); return wxMaService; }}
package com.qiangesoft.miniapp.config.ma;import com.qiangesoft.miniapp.properties.WxMaProperties;import org.springframework.boot.context.properties.EnableConfigurationProperties;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.Import;/** * 自动配置 * * @author qiangesoft * @since 2024-07-16 */@Configuration@EnableConfigurationProperties(WxMaProperties.class)@Import({WxMaStorageAutoConfiguration.class, WxMaServiceAutoConfiguration.class})public class WxMaAutoConfiguration {}
3.6 其他配置
package com.qiangesoft.miniapp.config;import cn.dev33.satoken.stp.StpUtil;import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;import org.apache.ibatis.reflection.MetaObject;import org.springframework.stereotype.Component;import java.time.LocalDateTime;/** * 自定义sql字段填充器,自动填充创建修改相关字段 * * @author qiangesoft * @date 2024-04-11 */@Componentpublic class CustomMetaObjectHandler implements MetaObjectHandler { @Override public void insertFill(MetaObject metaObject) { setFieldValByName(\"createUser\", StpUtil.getLoginIdAsLong(), metaObject); setFieldValByName(\"createTime\", LocalDateTime.now(), metaObject); } @Override public void updateFill(MetaObject metaObject) { setFieldValByName(\"updateUser\", StpUtil.getLoginIdAsLong(), metaObject); setFieldValByName(\"updateTime\", LocalDateTime.now(), metaObject); }}
package com.qiangesoft.miniapp.config;import com.baomidou.mybatisplus.annotation.DbType;import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;/** * mybatis-plus配置 * * @author qiangesoft * @date 2024-04-11 */@Configurationpublic class MybatisPlusConfig { /** * 分页插件 */ @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; }}
3.7 常量
package com.qiangesoft.miniapp.enums;/** * httpclient类型 * * @author qiangesoft * @since 2024-07-16 */public enum HttpClientType { /** * HttpClient. */ HttpClient, /** * OkHttp. */ OkHttp, /** * JoddHttp. */ JoddHttp,}
package com.qiangesoft.miniapp.enums; /** * storage类型 * * @author qiangesoft * @since 2024-07-16 */public enum StorageType { /** * 内存. */ Memory, /** * redis. */ Redis}
3.8 启动类
package com.qiangesoft.miniapp;import org.mybatis.spring.annotation.MapperScan;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;/** * 启动类 * * @author qiangesoft * @since 2024-07-16 */@MapperScan(\"com.qiangesoft.miniapp.mapper\")@SpringBootApplicationpublic class WxMaApplication { public static void main(String[] args) { SpringApplication.run(WxMaApplication.class, args); }}
四、至此本节结束