集成小程序登录和微信支付

This commit is contained in:
cjw 2024-07-29 16:29:01 +08:00
parent cc85130b76
commit 221947f966
27 changed files with 647 additions and 61 deletions

13
pom.xml
View File

@ -60,6 +60,8 @@
<maven-compiler-plugin.verison>3.11.0</maven-compiler-plugin.verison>
<maven-surefire-plugin.version>3.1.2</maven-surefire-plugin.version>
<flatten-maven-plugin.version>1.3.0</flatten-maven-plugin.version>
<!-- 微信支付 -->
<weixin-java.version>4.6.0</weixin-java.version>
</properties>
<profiles>
@ -381,6 +383,17 @@
<artifactId>rouyi-scale</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-pay</artifactId>
<version>${weixin-java.version}</version>
</dependency>
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-miniapp</artifactId>
<version>${weixin-java.version}</version>
</dependency>
</dependencies>
</dependencyManagement>

View File

@ -23,20 +23,20 @@
<artifactId>mysql-connector-j</artifactId>
</dependency>
<!-- Oracle -->
<dependency>
<groupId>com.oracle.database.jdbc</groupId>
<artifactId>ojdbc8</artifactId>
</dependency>
<!-- <dependency>-->
<!-- <groupId>com.oracle.database.jdbc</groupId>-->
<!-- <artifactId>ojdbc8</artifactId>-->
<!-- </dependency>-->
<!-- PostgreSql -->
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
</dependency>
<!-- <dependency>-->
<!-- <groupId>org.postgresql</groupId>-->
<!-- <artifactId>postgresql</artifactId>-->
<!-- </dependency>-->
<!-- SqlServer -->
<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>mssql-jdbc</artifactId>
</dependency>
<!-- <dependency>-->
<!-- <groupId>com.microsoft.sqlserver</groupId>-->
<!-- <artifactId>mssql-jdbc</artifactId>-->
<!-- </dependency>-->
<dependency>
<groupId>org.dromara</groupId>
@ -58,6 +58,11 @@
<artifactId>ruoyi-common-mail</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-wxlogin</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-system</artifactId>
@ -90,6 +95,8 @@
<scope>test</scope>
</dependency>
<!-- skywalking 整合 logback -->
<!-- <dependency>-->
<!-- <groupId>org.apache.skywalking</groupId>-->
@ -105,7 +112,7 @@
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>
<finalName>mental-tenant-api</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>

View File

@ -110,6 +110,39 @@ public class AuthController {
return R.ok(loginVo);
}
/**
* 微信登录方法
*
* @param body 登录信息
* @return 结果
*/
@PostMapping("/wx/login")
public R<LoginVo> wxLogin(@RequestBody String body) {
LoginBody loginBody = JsonUtils.parseObject(body, LoginBody.class);
ValidatorUtils.validate(loginBody);
// 授权类型和客户端id
String clientId = loginBody.getClientId();
String grantType = loginBody.getGrantType();
SysClientVo client = clientService.queryByClientId(clientId);
// 查询不到 client client 内不包含 grantType
if (ObjectUtil.isNull(client) || !StringUtils.contains(client.getGrantType(), grantType)) {
log.info("客户端id: {} 认证类型:{} 异常!.", clientId, grantType);
return R.fail(MessageUtils.message("auth.grant.type.error"));
} else if (!UserConstants.NORMAL.equals(client.getStatus())) {
return R.fail(MessageUtils.message("auth.grant.type.blocked"));
}
// 校验租户
if (StringUtils.isEmpty(loginBody.getTenantId())) {
//没有则默认注册在主租户下
loginBody.setTenantId("000000");
} else {
loginService.checkTenant(loginBody.getTenantId());
}
// 登录
LoginVo loginVo = IAuthStrategy.login(body, client, grantType);
return R.ok(loginVo);
}
/**
* 第三方登录请求
*
@ -142,8 +175,8 @@ public class AuthController {
public R<Void> socialCallback(@RequestBody SocialLoginBody loginBody) {
// 获取第三方登录信息
AuthResponse<AuthUser> response = SocialUtils.loginAuth(
loginBody.getSource(), loginBody.getSocialCode(),
loginBody.getSocialState(), socialProperties);
loginBody.getSource(), loginBody.getSocialCode(),
loginBody.getSocialState(), socialProperties);
AuthUser authUserData = response.getData();
// 判断授权响应是否成功
if (!response.ok()) {
@ -226,7 +259,7 @@ public class AuthController {
}
// 根据域名进行筛选
List<TenantListVo> list = StreamUtils.filter(voList, vo ->
StringUtils.equals(vo.getDomain(), host));
StringUtils.equals(vo.getDomain(), host));
result.setVoList(CollUtil.isNotEmpty(list) ? list : voList);
return R.ok(result);
}

View File

@ -1,22 +1,29 @@
package org.dromara.web.service.impl;
import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
import cn.binarywang.wx.miniapp.util.WxMaConfigHolder;
import cn.dev33.satoken.secure.BCrypt;
import cn.dev33.satoken.stp.SaLoginModel;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import org.dromara.common.core.domain.model.XcxLoginBody;
import org.dromara.common.core.domain.model.XcxLoginUser;
import org.dromara.common.core.enums.UserStatus;
import org.dromara.common.core.exception.user.UserException;
import org.dromara.common.core.utils.ValidatorUtils;
import org.dromara.common.json.utils.JsonUtils;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.system.domain.SysClient;
import org.dromara.common.tenant.helper.TenantHelper;
import org.dromara.system.domain.SysUser;
import org.dromara.system.domain.vo.SysClientVo;
import org.dromara.system.domain.vo.SysUserVo;
import org.dromara.system.mapper.SysUserMapper;
import org.dromara.web.domain.vo.LoginVo;
import org.dromara.web.service.IAuthStrategy;
import org.dromara.web.service.SysLoginService;
import org.springframework.stereotype.Service;
/**
@ -29,22 +36,39 @@ import org.springframework.stereotype.Service;
@RequiredArgsConstructor
public class XcxAuthStrategy implements IAuthStrategy {
private final SysLoginService loginService;
//private final SysLoginService loginService;
private final WxMaService wxMaService;
private final SysUserMapper userMapper;
@Override
public LoginVo login(String body, SysClientVo client) {
XcxLoginBody loginBody = JsonUtils.parseObject(body, XcxLoginBody.class);
ValidatorUtils.validate(loginBody);
// xcxCode 小程序调用 wx.login 授权后获取
String xcxCode = loginBody.getXcxCode();
// 多个小程序识别使用
String appid = loginBody.getAppid();
// todo 以下自行实现
// 校验 appid + appsrcret + xcxCode 调用登录凭证校验接口 获取 session_key openid
String openid = "";
String tenantId = loginBody.getTenantId();
// code 小程序调用 wx.login 授权后获取
String code = loginBody.getCode();
String encryptedData = loginBody.getEncryptedData();
String iv = loginBody.getIv();
String openid;
String phoneNumber;
try {
WxMaJscode2SessionResult session = wxMaService.getUserService().getSessionInfo(code);
String sessionKey = session.getSessionKey();
openid = session.getOpenid();
WxMaPhoneNumberInfo phoneNoInfo = wxMaService.getUserService().getPhoneNoInfo(sessionKey, encryptedData, iv);
phoneNumber = phoneNoInfo.getPhoneNumber();
} catch (WxErrorException e) {
log.error(e.getMessage(), e);
return new LoginVo();
} finally {
WxMaConfigHolder.remove();//清理ThreadLocal
}
// 框架登录不限制从什么表查询 只要最终构建出 LoginUser 即可
SysUserVo user = loadUserByOpenid(openid);
SysUser user = loadUserByOpenidAndPhoneNumber(openid, phoneNumber, tenantId);
// 此处可根据登录用户的数据不同 自行创建 loginUser 属性不够用继承扩展就行了
XcxLoginUser loginUser = new XcxLoginUser();
@ -75,18 +99,30 @@ public class XcxAuthStrategy implements IAuthStrategy {
return loginVo;
}
private SysUserVo loadUserByOpenid(String openid) {
private SysUser loadUserByOpenidAndPhoneNumber(String openid, String phoneNumber, String tenantId) {
// 使用 openid 查询绑定用户 如未绑定用户 则根据业务自行处理 例如 创建默认用户
// todo 自行实现 userService.selectUserByOpenid(openid);
SysUserVo user = new SysUserVo();
if (ObjectUtil.isNull(user)) {
log.info("登录用户:{} 不存在.", openid);
// todo 用户不存在 业务逻辑自行实现
} else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) {
log.info("登录用户:{} 已被停用.", openid);
// todo 用户已被停用 业务逻辑自行实现
}
return user;
return TenantHelper.ignore(() -> {
SysUser user = userMapper.selectOne(new LambdaQueryWrapper<SysUser>().eq(SysUser::getUserName, phoneNumber));
if (ObjectUtil.isNull(user)) {
//newUser.setDeptId(100L);
SysUser newUser = new SysUser();
newUser.setPassword(BCrypt.hashpw("123456"));
newUser.setTenantId(tenantId);
newUser.setUserName(phoneNumber);
newUser.setNickName("健康土豆");
newUser.setPhonenumber(phoneNumber);
newUser.setOpenId(openid);
newUser.setUserType("sys_user");
userMapper.insert(newUser);
return newUser;
} else {
if (user.getOpenId().equals(openid)) {
return user;
} else {
throw new UserException("此用户已被其他微信号绑定", phoneNumber);
}
}
});
}
}

View File

@ -256,3 +256,19 @@ justauth:
client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=gitlab
wx:
pay:
appId: wx2e09db4124332242
mchId: #微信支付商户号
mchKey: #微信支付商户密钥
subAppId: #服务商模式下的子商户公众账号ID
subMchId: #服务商模式下的子商户号
keyPath: # p12证书的位置可以指定绝对路径也可以指定类路径以classpath:开头)
miniapp:
configs:
- appid: wx2e09db4124332242
secret: #微信小程序的Secret
token: #微信小程序消息服务器配置的token
aesKey: #微信小程序消息服务器配置的EncodingAESKey
msgDataFormat: JSON

View File

@ -56,24 +56,24 @@ spring:
url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
username:
password:
# oracle:
# type: ${spring.datasource.type}
# driverClassName: oracle.jdbc.OracleDriver
# url: jdbc:oracle:thin:@//localhost:1521/XE
# username: ROOT
# password: root
# postgres:
# type: ${spring.datasource.type}
# driverClassName: org.postgresql.Driver
# url: jdbc:postgresql://localhost:5432/postgres?useUnicode=true&characterEncoding=utf8&useSSL=true&autoReconnect=true&reWriteBatchedInserts=true
# username: root
# password: root
# sqlserver:
# type: ${spring.datasource.type}
# driverClassName: com.microsoft.sqlserver.jdbc.SQLServerDriver
# url: jdbc:sqlserver://localhost:1433;DatabaseName=tempdb;SelectMethod=cursor;encrypt=false;rewriteBatchedStatements=true
# username: SA
# password: root
# oracle:
# type: ${spring.datasource.type}
# driverClassName: oracle.jdbc.OracleDriver
# url: jdbc:oracle:thin:@//localhost:1521/XE
# username: ROOT
# password: root
# postgres:
# type: ${spring.datasource.type}
# driverClassName: org.postgresql.Driver
# url: jdbc:postgresql://localhost:5432/postgres?useUnicode=true&characterEncoding=utf8&useSSL=true&autoReconnect=true&reWriteBatchedInserts=true
# username: root
# password: root
# sqlserver:
# type: ${spring.datasource.type}
# driverClassName: com.microsoft.sqlserver.jdbc.SQLServerDriver
# url: jdbc:sqlserver://localhost:1433;DatabaseName=tempdb;SelectMethod=cursor;encrypt=false;rewriteBatchedStatements=true
# username: SA
# password: root
hikari:
# 最大连接池数量
maxPoolSize: 20
@ -258,3 +258,19 @@ justauth:
client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=gitlab
wx:
pay:
appId: #微信公众号或者小程序等的appid
mchId: #微信支付商户号
mchKey: #微信支付商户密钥
subAppId: #服务商模式下的子商户公众账号ID
subMchId: #服务商模式下的子商户号
keyPath: # p12证书的位置可以指定绝对路径也可以指定类路径以classpath:开头)
miniapp:
configs:
- appid: #微信小程序的appid
secret: #微信小程序的Secret
token: #微信小程序消息服务器配置的token
aesKey: #微信小程序消息服务器配置的EncodingAESKey
msgDataFormat: JSON

View File

@ -256,3 +256,19 @@ justauth:
client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=gitlab
wx:
pay:
appId: #微信公众号或者小程序等的appid
mchId: #微信支付商户号
mchKey: #微信支付商户密钥
subAppId: #服务商模式下的子商户公众账号ID
subMchId: #服务商模式下的子商户号
keyPath: # p12证书的位置可以指定绝对路径也可以指定类路径以classpath:开头)
miniapp:
configs:
- appid: #微信小程序的appid
secret: #微信小程序的Secret
token: #微信小程序消息服务器配置的token
aesKey: #微信小程序消息服务器配置的EncodingAESKey
msgDataFormat: JSON

View File

@ -33,6 +33,8 @@
<module>ruoyi-common-encrypt</module>
<module>ruoyi-common-tenant</module>
<module>ruoyi-common-websocket</module>
<module>ruoyi-common-wxpay</module>
<module>ruoyi-common-wxlogin</module>
</modules>
<artifactId>ruoyi-common</artifactId>

View File

@ -172,6 +172,18 @@
<version>${revision}</version>
</dependency>
<!-- 微信支付模块 -->
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-wxpay</artifactId>
<version>${revision}</version>
</dependency>
<!-- 微信登录模块 -->
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-wxlogin</artifactId>
<version>${revision}</version>
</dependency>
</dependencies>
</dependencyManagement>

View File

@ -23,6 +23,12 @@ public class XcxLoginBody extends LoginBody {
* 小程序code
*/
@NotBlank(message = "{xcx.code.not.blank}")
private String xcxCode;
private String code;
@NotBlank(message = "小程序[encryptedData]不能为空")
private String encryptedData;
@NotBlank(message = "小程序[iv]不能为空")
private String iv;
}

View File

@ -190,5 +190,4 @@ public class TenantHelper {
return false;
}
}
}

View File

@ -0,0 +1,31 @@
<?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>org.dromara</groupId>
<artifactId>ruoyi-common</artifactId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>ruoyi-common-wxlogin</artifactId>
<description>
ruoyi-common-wxlogin 小程序登录功能
</description>
<dependencies>
<!-- RuoYi Common Core-->
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-core</artifactId>
</dependency>
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-miniapp</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,135 @@
package org.dromara.common.wxlogin.config;
import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl;
import cn.binarywang.wx.miniapp.bean.WxMaKefuMessage;
import cn.binarywang.wx.miniapp.bean.WxMaSubscribeMessage;
import cn.binarywang.wx.miniapp.config.impl.WxMaDefaultConfigImpl;
import cn.binarywang.wx.miniapp.message.WxMaMessageHandler;
import cn.binarywang.wx.miniapp.message.WxMaMessageRouter;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.bean.result.WxMediaUploadResult;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.error.WxRuntimeException;
import org.dromara.common.wxlogin.config.properties.WxMaProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import java.io.File;
import java.util.List;
import java.util.stream.Collectors;
/**
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
@Slf4j
@Configuration
@EnableConfigurationProperties(WxMaProperties.class)
public class WxMaConfiguration {
private final WxMaProperties properties;
@Autowired
public WxMaConfiguration(WxMaProperties properties) {
this.properties = properties;
}
@Lazy
@Bean
public WxMaService wxMaService() {
List<WxMaProperties.Config> configs = this.properties.getConfigs();
if (configs == null) {
throw new WxRuntimeException("大哥拜托先看下项目首页的说明readme文件添加下相关配置注意别配错了");
}
WxMaService maService = new WxMaServiceImpl();
maService.setMultiConfigs(
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());
return config;
}).collect(Collectors.toMap(WxMaDefaultConfigImpl::getAppid, a -> a, (o, n) -> o)));
return maService;
}
@Lazy
@Bean
public WxMaMessageRouter wxMaMessageRouter(WxMaService wxMaService) {
final WxMaMessageRouter router = new WxMaMessageRouter(wxMaService);
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;
};
}

View File

@ -0,0 +1,45 @@
package org.dromara.common.wxlogin.config.properties;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.List;
/**
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
@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;
}
}

View File

@ -0,0 +1,31 @@
<?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>org.dromara</groupId>
<artifactId>ruoyi-common</artifactId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>ruoyi-common-wxpay</artifactId>
<description>
ruoyi-common-pay 支付功能
</description>
<dependencies>
<!-- RuoYi Common Core-->
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-core</artifactId>
</dependency>
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-pay</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,45 @@
package org.dromara.common.wxpay.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.AllArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.dromara.common.wxpay.config.properties.WxPayProperties;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Lazy;
/**
* @author Binary Wang
*/
//@Configuration
@ConditionalOnClass(WxPayService.class)
@EnableConfigurationProperties(WxPayProperties.class)
@AllArgsConstructor
public class WxPayConfiguration {
private WxPayProperties properties;
@Lazy
@Bean
@ConditionalOnMissingBean
public WxPayService wxService() {
WxPayConfig payConfig = new WxPayConfig();
payConfig.setAppId(StringUtils.trimToNull(this.properties.getAppId()));
payConfig.setMchId(StringUtils.trimToNull(this.properties.getMchId()));
payConfig.setMchKey(StringUtils.trimToNull(this.properties.getMchKey()));
payConfig.setSubAppId(StringUtils.trimToNull(this.properties.getSubAppId()));
payConfig.setSubMchId(StringUtils.trimToNull(this.properties.getSubMchId()));
payConfig.setKeyPath(StringUtils.trimToNull(this.properties.getKeyPath()));
// 可以指定是否使用沙箱环境
payConfig.setUseSandboxEnv(false);
WxPayService wxPayService = new WxPayServiceImpl();
wxPayService.setConfig(payConfig);
return wxPayService;
}
}

View File

@ -0,0 +1,45 @@
package org.dromara.common.wxpay.config.properties;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* wxpay pay properties.
*
* @author Binary Wang
*/
@Data
@ConfigurationProperties(prefix = "wx.pay")
public class WxPayProperties {
/**
* 设置微信公众号或者小程序等的appid
*/
private String appId;
/**
* 微信支付商户号
*/
private String mchId;
/**
* 微信支付商户密钥
*/
private String mchKey;
/**
* 服务商模式下的子商户公众账号ID普通模式请不要配置请在配置文件中将对应项删除
*/
private String subAppId;
/**
* 服务商模式下的子商户号普通模式请不要配置最好是请在配置文件中将对应项删除
*/
private String subMchId;
/**
* apiclient_cert.p12文件的绝对路径或者如果放在项目中请以classpath:开头指定
*/
private String keyPath;
}

View File

@ -84,6 +84,11 @@
<artifactId>ruoyi-common-websocket</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-wxpay</artifactId>
</dependency>
<dependency>
<groupId>com.deepoove</groupId>
<artifactId>poi-tl</artifactId>

View File

@ -10,7 +10,6 @@ import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.web.core.BaseController;
import org.dromara.scale.domain.bo.SubmitAnswerBo;
import org.dromara.scale.domain.vo.QuestionAnswerVo;
import org.dromara.scale.domain.vo.SysScalePublishVo;
import org.dromara.scale.domain.vo.SysScaleVo;
import org.dromara.scale.service.IWebService;
import org.springframework.validation.annotation.Validated;
@ -29,7 +28,7 @@ import java.util.Map;
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/web/scale")
@RequestMapping("/wx")
public class WebController extends BaseController {
private final IWebService webService;

View File

@ -0,0 +1,21 @@
package org.dromara.scale.controller.wx;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* <p>TODO<p>
*
* @author cjw
* @version V1.0.0
* @date 2024/7/29 14:08
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/wx/pay")
public class WxPayController {
}

View File

@ -0,0 +1,50 @@
package org.dromara.scale.controller.wx;
import jakarta.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.domain.R;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.web.core.BaseController;
import org.dromara.scale.domain.bo.SysScaleBo;
import org.dromara.scale.domain.vo.SysScaleVo;
import org.dromara.scale.service.ISysScaleService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* <p>TODO<p>
*
* @author cjw
* @version V1.0.0
* @date 2024/7/29 10:01
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/wx/scale")
public class WxScaleController extends BaseController {
private final ISysScaleService sysScaleService;
/**
* 查询心理测评量列表
*/
@GetMapping("/pageList")
public TableDataInfo<SysScaleVo> list(SysScaleBo bo, PageQuery pageQuery) {
return sysScaleService.queryPageList(bo, pageQuery);
}
/**
* 获取心理测评量详细信息
*
* @param scaleId 主键
*/
@GetMapping("/{scaleId}")
public R<SysScaleVo> getInfo(@NotNull(message = "主键不能为空") @PathVariable Long scaleId) {
//todo
return R.ok(sysScaleService.queryById(scaleId));
}
}

View File

@ -10,6 +10,7 @@ import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.scale.domain.vo.SysScaleVo;
import java.io.Serial;
import java.math.BigDecimal;
/**
* 心理测评量对象 sys_scale
@ -110,4 +111,8 @@ public class SysScale extends BaseEntity {
*/
private String scaleTime;
private Integer freeFlag;
private BigDecimal price;
}

View File

@ -13,6 +13,7 @@ import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.scale.domain.SysScale;
import java.math.BigDecimal;
import java.util.List;
/**
@ -116,4 +117,9 @@ public class SysScaleBo extends BaseEntity {
*/
@NotBlank(message = "测评时长不能为空", groups = {AddGroup.class, EditGroup.class})
private String scaleTime;
@NotNull(message = "价格类型不能为空", groups = {AddGroup.class, EditGroup.class})
private Integer freeFlag;
private BigDecimal price;
}

View File

@ -4,6 +4,7 @@ import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.List;
@ -118,4 +119,8 @@ public class SysScaleVo implements Serializable {
private Long recordId;
private Integer freeFlag;
private BigDecimal price;
}

View File

@ -1,6 +1,7 @@
package org.dromara.scale.starter;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.redis.utils.QueueUtils;
import org.dromara.scale.constant.StatusEnum;
import org.dromara.scale.domain.SysScalePublish;
@ -16,6 +17,7 @@ import org.springframework.stereotype.Component;
* @version V1.0.0
* @date 2024/4/15 15:08
*/
@Slf4j
@Component
@RequiredArgsConstructor
@Order(value = 99)
@ -29,6 +31,7 @@ public class ApplicationStarter implements CommandLineRunner {
}
private void subscribeRedisDelayedQueue(){
log.info("=======================延迟队列回调开始===============");
// 项目初始化设置一次即可
QueueUtils.subscribeBlockingQueue("endPublish-delay-queue", (Long batchNo) -> {
SysScalePublish publish = new SysScalePublish();

View File

@ -42,6 +42,8 @@ public class SysUser extends TenantEntity {
*/
private String nickName;
private String openId;
/**
* 用户类型sys_user系统用户
*/

View File

@ -48,6 +48,8 @@ public class SysUserVo implements Serializable {
*/
private String userName;
private String openId;
/**
* 用户昵称
*/