feat: 新功能开发,监测历史记录。微信和物联网平台,模块搭建。

This commit is contained in:
tianyongbao
2025-11-05 00:35:23 +08:00
parent 4d10d291a3
commit 8fd28e727e
38 changed files with 2099 additions and 49 deletions

View File

@@ -0,0 +1,88 @@
<?xml version="1.0" encoding="UTF-8"?>
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>com.intc</groupId>
<artifactId>intc-modules</artifactId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>intc-weixin</artifactId>
<description>
微信对接模块
</description>
<dependencies>
<!-- 微信开发 Java SDK -->
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-mp</artifactId>
<version>4.6.0</version>
</dependency>
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-miniapp</artifactId>
<version>4.6.0</version>
</dependency>
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-pay</artifactId>
<version>4.6.0</version>
</dependency>
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-open</artifactId>
<version>4.6.0</version>
</dependency>
<!-- 通用工具-->
<dependency>
<groupId>com.intc</groupId>
<artifactId>intc-common-core</artifactId>
</dependency>
<dependency>
<groupId>com.intc</groupId>
<artifactId>intc-common-doc</artifactId>
</dependency>
<dependency>
<groupId>com.intc</groupId>
<artifactId>intc-common-redis</artifactId>
</dependency>
<dependency>
<groupId>com.intc</groupId>
<artifactId>intc-common-mybatis</artifactId>
</dependency>
<dependency>
<groupId>com.intc</groupId>
<artifactId>intc-common-log</artifactId>
</dependency>
<dependency>
<groupId>com.intc</groupId>
<artifactId>intc-common-security</artifactId>
</dependency>
<dependency>
<groupId>com.intc</groupId>
<artifactId>intc-common-web</artifactId>
</dependency>
<dependency>
<groupId>com.intc</groupId>
<artifactId>intc-common-tenant</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,37 @@
package com.intc.weixin.config;
import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl;
import cn.binarywang.wx.miniapp.config.impl.WxMaDefaultConfigImpl;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 微信小程序配置
*
* @author intc
*/
@Configuration
@RequiredArgsConstructor
@ConditionalOnProperty(prefix = "wx.miniapp", name = "app-id")
public class WxMaConfiguration {
private final WxMaProperties wxMaProperties;
@Bean
public WxMaService wxMaService() {
WxMaDefaultConfigImpl config = new WxMaDefaultConfigImpl();
config.setAppid(wxMaProperties.getAppId());
config.setSecret(wxMaProperties.getSecret());
config.setToken(wxMaProperties.getToken());
config.setAesKey(wxMaProperties.getAesKey());
config.setMsgDataFormat(wxMaProperties.getMsgDataFormat());
WxMaService service = new WxMaServiceImpl();
service.setWxMaConfig(config);
return service;
}
}

View File

@@ -0,0 +1,42 @@
package com.intc.weixin.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* 微信小程序配置属性
*
* @author intc
*/
@Data
@Component
@ConfigurationProperties(prefix = "wx.miniapp")
public class WxMaProperties {
/**
* 小程序appId
*/
private String appId;
/**
* 小程序Secret
*/
private String secret;
/**
* 小程序token
*/
private String token;
/**
* 小程序EncodingAESKey
*/
private String aesKey;
/**
* 消息格式XML或者JSON
*/
private String msgDataFormat;
}

View File

@@ -0,0 +1,36 @@
package com.intc.weixin.config;
import lombok.RequiredArgsConstructor;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl;
import me.chanjar.weixin.mp.config.impl.WxMpDefaultConfigImpl;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 微信公众号配置
*
* @author intc
*/
@Configuration
@RequiredArgsConstructor
@ConditionalOnProperty(prefix = "wx.mp", name = "app-id")
public class WxMpConfiguration {
private final WxMpProperties wxMpProperties;
@Bean
public WxMpService wxMpService() {
WxMpDefaultConfigImpl config = new WxMpDefaultConfigImpl();
config.setAppId(wxMpProperties.getAppId());
config.setSecret(wxMpProperties.getSecret());
config.setToken(wxMpProperties.getToken());
config.setAesKey(wxMpProperties.getAesKey());
WxMpService service = new WxMpServiceImpl();
service.setWxMpConfigStorage(config);
return service;
}
}

View File

@@ -0,0 +1,37 @@
package com.intc.weixin.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* 微信公众号配置属性
*
* @author intc
*/
@Data
@Component
@ConfigurationProperties(prefix = "wx.mp")
public class WxMpProperties {
/**
* 公众号appId
*/
private String appId;
/**
* 公众号Secret
*/
private String secret;
/**
* 公众号token
*/
private String token;
/**
* 公众号EncodingAESKey
*/
private String aesKey;
}

View File

@@ -0,0 +1,39 @@
package com.intc.weixin.config;
import com.github.binarywang.wxpay.config.WxPayConfig;
import com.github.binarywang.wxpay.service.WxPayService;
import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 微信支付配置
*
* @author intc
*/
@Configuration
@RequiredArgsConstructor
@ConditionalOnProperty(prefix = "wx.pay", name = "mch-id")
public class WxPayConfiguration {
private final WxPayProperties wxPayProperties;
@Bean
public WxPayService wxPayService() {
WxPayConfig config = new WxPayConfig();
config.setMchId(wxPayProperties.getMchId());
config.setMchKey(wxPayProperties.getMchKey());
config.setKeyPath(wxPayProperties.getKeyPath());
config.setApiV3Key(wxPayProperties.getApiV3Key());
config.setCertSerialNo(wxPayProperties.getCertSerialNo());
config.setPrivateKeyPath(wxPayProperties.getPrivateKeyPath());
config.setPrivateCertPath(wxPayProperties.getPrivateContent());
WxPayService service = new WxPayServiceImpl();
service.setConfig(config);
return service;
}
}

View File

@@ -0,0 +1,52 @@
package com.intc.weixin.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* 微信支付配置属性
*
* @author intc
*/
@Data
@Component
@ConfigurationProperties(prefix = "wx.pay")
public class WxPayProperties {
/**
* 商户号
*/
private String mchId;
/**
* 商户密钥
*/
private String mchKey;
/**
* apiclient_cert.p12文件的绝对路径或者以classpath:开头的类路径
*/
private String keyPath;
/**
* apiV3秘钥
*/
private String apiV3Key;
/**
* 证书序列号
*/
private String certSerialNo;
/**
* 私钥路径
*/
private String privateKeyPath;
/**
* 私钥内容
*/
private String privateContent;
}

View File

@@ -0,0 +1,109 @@
package com.intc.weixin.controller;
import com.intc.common.core.domain.R;
import com.intc.common.web.core.BaseController;
import com.intc.weixin.service.WxMaService;
import com.intc.weixin.service.WxMpService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.mp.bean.result.WxMpUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.Optional;
/**
* 微信对接控制器
*
* @author intc
*/
@Slf4j
@RequiredArgsConstructor
@RestController
@RequestMapping("/weixin")
@Tag(name = "微信对接管理", description = "微信对接相关接口")
public class WeixinController extends BaseController {
@Autowired(required = false)
private WxMpService wxMpService;
@Autowired(required = false)
private WxMaService wxMaService;
@Operation(summary = "测试接口")
@GetMapping("/test")
public R<String> test() {
return R.ok("微信模块测试成功!");
}
@Operation(summary = "获取公众号用户信息")
@GetMapping("/mp/user/{openId}")
public R<WxMpUser> getMpUserInfo(
@Parameter(description = "用户openId") @PathVariable String openId) {
try {
if (wxMpService == null) {
return R.fail("公众号配置未启用");
}
WxMpUser userInfo = wxMpService.getUserInfo(openId);
return R.ok(userInfo);
} catch (WxErrorException e) {
log.error("获取用户信息失败", e);
return R.fail("获取用户信息失败: " + e.getMessage());
}
}
@Operation(summary = "生成公众号二维码")
@GetMapping("/mp/qrcode")
public R<String> createMpQrCode(
@Parameter(description = "场景值") @RequestParam String scene) {
try {
if (wxMpService == null) {
return R.fail("公众号配置未启用");
}
String qrCodeUrl = wxMpService.createQrCode(scene);
return R.ok(qrCodeUrl);
} catch (WxErrorException e) {
log.error("生成二维码失败", e);
return R.fail("生成二维码失败: " + e.getMessage());
}
}
@Operation(summary = "小程序登录")
@GetMapping("/ma/login")
public R<String> maLogin(
@Parameter(description = "登录code") @RequestParam String code) {
try {
if (wxMaService == null) {
return R.fail("小程序配置未启用");
}
String openId = wxMaService.code2Session(code);
return R.ok(openId);
} catch (WxErrorException e) {
log.error("小程序登录失败", e);
return R.fail("小程序登录失败: " + e.getMessage());
}
}
@Operation(summary = "生成小程序码")
@GetMapping("/ma/qrcode")
public R<String> createMaQrCode(
@Parameter(description = "场景值") @RequestParam String scene,
@Parameter(description = "页面路径") @RequestParam(required = false) String page) {
try {
if (wxMaService == null) {
return R.fail("小程序配置未启用");
}
byte[] qrCodeBytes = wxMaService.getUnlimitedQrCode(scene, page);
// 这里可以将字节数组上传到OSS或者直接返回给前端
return R.ok("二维码生成成功,大小: " + qrCodeBytes.length + " bytes");
} catch (WxErrorException e) {
log.error("生成小程序码失败", e);
return R.fail("生成小程序码失败: " + e.getMessage());
}
}
}

View File

@@ -0,0 +1,74 @@
package com.intc.weixin.domain;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* 微信用户信息
*
* @author intc
*/
@Data
@TableName("wx_user")
public class WxUser implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
@TableId
private Long id;
/**
* 微信openId
*/
private String openId;
/**
* 微信unionId
*/
private String unionId;
/**
* 昵称
*/
private String nickname;
/**
* 头像
*/
private String avatar;
/**
* 性别 0-未知 1-男 2-女
*/
private Integer gender;
/**
* 手机号
*/
private String mobile;
/**
* 来源类型mp-公众号 ma-小程序
*/
private String sourceType;
/**
* 创建时间
*/
private LocalDateTime createTime;
/**
* 更新时间
*/
private LocalDateTime updateTime;
}

View File

@@ -0,0 +1,31 @@
package com.intc.weixin.service;
import me.chanjar.weixin.common.error.WxErrorException;
/**
* 微信小程序服务接口
*
* @author intc
*/
public interface WxMaService {
/**
* 登录凭证校验
*
* @param code 登录时获取的 code
* @return sessionKey和openId
* @throws WxErrorException 微信异常
*/
String code2Session(String code) throws WxErrorException;
/**
* 获取小程序码
*
* @param scene 场景值
* @param page 页面路径
* @return 二进制图片数据
* @throws WxErrorException 微信异常
*/
byte[] getUnlimitedQrCode(String scene, String page) throws WxErrorException;
}

View File

@@ -0,0 +1,31 @@
package com.intc.weixin.service;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.mp.bean.result.WxMpUser;
/**
* 微信公众号服务接口
*
* @author intc
*/
public interface WxMpService {
/**
* 获取用户信息
*
* @param openId 用户openId
* @return 用户信息
* @throws WxErrorException 微信异常
*/
WxMpUser getUserInfo(String openId) throws WxErrorException;
/**
* 生成二维码ticket
*
* @param sceneStr 场景值
* @return ticket
* @throws WxErrorException 微信异常
*/
String createQrCode(String sceneStr) throws WxErrorException;
}

View File

@@ -0,0 +1,38 @@
package com.intc.weixin.service.impl;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
import com.intc.weixin.service.WxMaService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.stereotype.Service;
/**
* 微信小程序服务实现
*
* @author intc
*/
@Slf4j
@Service
@RequiredArgsConstructor
@ConditionalOnBean(cn.binarywang.wx.miniapp.api.WxMaService.class)
public class WxMaServiceImpl implements WxMaService {
private final cn.binarywang.wx.miniapp.api.WxMaService wxMaService;
@Override
public String code2Session(String code) throws WxErrorException {
log.info("小程序登录, code: {}", code);
WxMaJscode2SessionResult session = wxMaService.getUserService().getSessionInfo(code);
return session.getOpenid();
}
@Override
public byte[] getUnlimitedQrCode(String scene, String page) throws WxErrorException {
log.info("生成小程序码, scene: {}, page: {}", scene, page);
return wxMaService.getQrcodeService().createWxaCodeUnlimitBytes(scene, page, false, null, 430, true, null, false);
}
}

View File

@@ -0,0 +1,39 @@
package com.intc.weixin.service.impl;
import com.intc.weixin.service.WxMpService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.mp.bean.result.WxMpQrCodeTicket;
import me.chanjar.weixin.mp.bean.result.WxMpUser;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.stereotype.Service;
/**
* 微信公众号服务实现
*
* @author intc
*/
@Slf4j
@Service
@RequiredArgsConstructor
@ConditionalOnBean(me.chanjar.weixin.mp.api.WxMpService.class)
public class WxMpServiceImpl implements WxMpService {
private final me.chanjar.weixin.mp.api.WxMpService wxMpService;
@Override
public WxMpUser getUserInfo(String openId) throws WxErrorException {
log.info("获取微信用户信息, openId: {}", openId);
return wxMpService.getUserService().userInfo(openId);
}
@Override
public String createQrCode(String sceneStr) throws WxErrorException {
log.info("创建微信二维码, sceneStr: {}", sceneStr);
WxMpQrCodeTicket ticket = wxMpService.getQrcodeService()
.qrCodeCreateTmpTicket(sceneStr, 2592000);
return ticket.getUrl();
}
}

View File

@@ -0,0 +1,42 @@
# 微信对接模块配置
wx:
# 微信公众号配置
mp:
# 公众号appId必填
app-id: wx1234567890abcdef
# 公众号Secret必填
secret: your_mp_secret_here_32_characters
# 公众号token选填用于消息加解密
token: your_token_here
# 公众号EncodingAESKey选填用于消息加解密
aes-key: your_aes_key_here_43_characters_base64
# 微信小程序配置
miniapp:
# 小程序appId必填
app-id: wx0987654321fedcba
# 小程序Secret必填
secret: your_miniapp_secret_here_32_chars
# 小程序token选填用于消息加解密
token: your_miniapp_token
# 小程序EncodingAESKey选填用于消息加解密
aes-key: your_miniapp_aes_key_43_characters_b64
# 消息格式XML或者JSON
msg-data-format: JSON
# 微信支付配置
pay:
# 商户号(必填)
mch-id: 1234567890
# 商户密钥V2版本必填
mch-key: your_mch_key_here_32_characters_md5
# 证书路径(退款等操作需要,选填)
key-path: classpath:cert/apiclient_cert.p12
# apiV3秘钥V3版本必填
api-v3-key: your_api_v3_key_here_32_characters
# 证书序列号V3版本必填
cert-serial-no: 1234567890ABCDEF1234567890ABCDEF12345678
# 私钥路径V3版本选填
private-key-path: classpath:cert/apiclient_key.pem
# 私钥内容V3版本选填与private-key-path二选一
private-content: