18339543638 преди 7 месеца
родител
ревизия
65302bc34a
променени са 20 файла, в които са добавени 596 реда и са изтрити 31 реда
  1. 10 2
      tr-dependencies/pom.xml
  2. 1 0
      tr-modules/pom.xml
  3. 62 0
      tr-modules/tr-module-smartFollowUp/pom.xml
  4. 69 0
      tr-modules/tr-module-smartFollowUp/src/main/java/cn/tr/module/smart/wx/config/WxAppletOauth2UserOperator.java
  5. 131 0
      tr-modules/tr-module-smartFollowUp/src/main/java/cn/tr/module/smart/wx/config/WxMaConfiguration.java
  6. 46 0
      tr-modules/tr-module-smartFollowUp/src/main/java/cn/tr/module/smart/wx/config/WxMaProperties.java
  7. 77 0
      tr-modules/tr-module-smartFollowUp/src/main/java/cn/tr/module/smart/wx/controller/WxMaUserController.java
  8. 109 0
      tr-modules/tr-module-smartFollowUp/src/main/java/cn/tr/module/smart/wx/controller/WxPortalController.java
  9. 12 0
      tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/oauth2/LoginTypeConstant.java
  10. 1 1
      tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/oauth2/config/SaOAuth2TemplateImpl.java
  11. 23 0
      tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/oauth2/controller/OAuth2ServerController.java
  12. 0 10
      tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/oauth2/dto/AccountUserInfoEditDTO.java
  13. 15 12
      tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/oauth2/dto/OAuth2PswReqDTO.java
  14. 2 1
      tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/oauth2/operator/OAuth2UserOperator.java
  15. 2 1
      tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/oauth2/psw/operator/LoginOAuth2PswUserOperator.java
  16. 5 0
      tr-plugins/tr-spring-boot-starter-plugin-satoken/pom.xml
  17. 13 0
      tr-plugins/tr-spring-boot-starter-plugin-satoken/src/main/java/cn/tr/plugin/security/config/LoginUserStrategyConfig.java
  18. 6 0
      tr-test/pom.xml
  19. 9 1
      tr-test/src/main/resources/application-dev.yml
  20. 3 3
      tr-test/src/main/resources/application-doc.yml

+ 10 - 2
tr-dependencies/pom.xml

@@ -85,9 +85,9 @@
 
         <postgresql.version>42.7.3</postgresql.version>
 
-        <es.version>8.1.0</es.version>
-
         <jakarta-json.version>2.0.1</jakarta-json.version>
+
+        <wx.version>4.7.0</wx.version>
     </properties>
 
 
@@ -539,6 +539,14 @@
                 <artifactId>jakarta.json</artifactId>
                 <version>${jakarta-json.version}</version>
             </dependency>
+
+            <dependency>
+                <groupId>com.github.binarywang</groupId>
+                <artifactId>weixin-java-miniapp</artifactId>
+                <version>${wx.version}</version>
+            </dependency>
+
+
         </dependencies>
     </dependencyManagement>
 </project>

+ 1 - 0
tr-modules/pom.xml

@@ -17,6 +17,7 @@
         <module>tr-module-gen</module>
         <module>tr-module-export</module>
         <module>tr-module-quartz</module>
+        <module>tr-module-smartFollowUp</module>
     </modules>
 
 

+ 62 - 0
tr-modules/tr-module-smartFollowUp/pom.xml

@@ -0,0 +1,62 @@
+<?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>
+        <artifactId>tr-modules</artifactId>
+        <groupId>cn.tr</groupId>
+        <version>${revision}</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>tr-module-smartFollowUp</artifactId>
+    <packaging>jar</packaging>
+    <dependencies>
+        <dependency>
+            <groupId>cn.tr</groupId>
+            <artifactId>tr-framework</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>cn.tr</groupId>
+            <artifactId>tr-spring-boot-starter-plugin-doc</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>cn.tr</groupId>
+            <artifactId>tr-spring-boot-starter-plugin-web</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>cn.tr</groupId>
+            <artifactId>tr-spring-boot-starter-plugin-mybatis</artifactId>
+        </dependency>
+
+
+        <dependency>
+            <groupId>cn.tr</groupId>
+            <artifactId>tr-spring-boot-starter-plugin-satoken</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>cn.tr</groupId>
+            <artifactId>tr-module-system-api</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>cn.tr</groupId>
+            <artifactId>tr-spring-boot-starter-plugin-eventbus</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.github.binarywang</groupId>
+            <artifactId>weixin-java-miniapp</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>cn.tr</groupId>
+            <artifactId>tr-module-system</artifactId>
+            <version>0.0.9</version>
+        </dependency>
+    </dependencies>
+</project>

+ 69 - 0
tr-modules/tr-module-smartFollowUp/src/main/java/cn/tr/module/smart/wx/config/WxAppletOauth2UserOperator.java

@@ -0,0 +1,69 @@
+package cn.tr.module.smart.wx.config;
+
+import cn.binarywang.wx.miniapp.api.WxMaService;
+import cn.dev33.satoken.stp.StpLogic;
+import cn.hutool.core.util.StrUtil;
+import cn.tr.module.sys.oauth2.LoginTypeConstant;
+import cn.tr.module.sys.oauth2.dto.AccountUserInfoDTO;
+import cn.tr.module.sys.oauth2.dto.AccountUserInfoEditDTO;
+import cn.tr.module.sys.oauth2.dto.OAuth2PswLoginInfoDTO;
+import cn.tr.module.sys.oauth2.dto.OAuth2PswReqDTO;
+import cn.tr.module.sys.oauth2.psw.operator.AbstractOAuth2PswUserOperator;
+import cn.tr.plugin.security.utils.SaTokenUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+/**
+ * @ClassName : WxAppletOauth2UserOperator
+ * @see <a href ="https://i-blog.csdnimg.cn/blog_migrate/34d657a0a813d1d774a9bc7eb8908311.png"/>微信登录流程图</a>
+ * @see <a href="https://gitee.com/binary/weixin-java-miniapp-demo/blob/master/src/main/java/com/github/binarywang/demo/wx/miniapp/controller/WxMaUserController.java">调用接口示例</a>
+ * @Date: 2025年05月19日
+ */
+@Component
+public class WxAppletOauth2UserOperator extends AbstractOAuth2PswUserOperator {
+
+    @Autowired
+    private WxMaService wxMaService;
+    @Override
+    public String auth(OAuth2PswReqDTO source) {
+        String appId = source.getAppId();
+        String wxAppletCode = source.getWxAppletCode();
+        if (!wxMaService.switchover(appId)) {
+            throw new IllegalArgumentException(String.format("未找到对应appid=[%s]的配置,请核实!", appId));
+        }
+        if(StrUtil.isEmpty(wxAppletCode)){
+            throw new IllegalArgumentException("微信登陆码不能为空");
+        }
+        //todo
+
+        StpLogic stpUtil = SaTokenUtils.getStpUtil();
+        stpUtil.login("123");
+        String tokenValue = stpUtil.getTokenValue();
+        return tokenValue;
+    }
+
+    @Override
+    public boolean updatePsw(String oldPsw, String newPsw) {
+        throw new UnsupportedOperationException("不支持修改密码");
+    }
+
+    @Override
+    public OAuth2PswLoginInfoDTO getUserLoginInfo() {
+        return null;
+    }
+
+    @Override
+    public AccountUserInfoDTO getAccountInfo() {
+        return null;
+    }
+
+    @Override
+    public void updateAccountUserInfo(String userId, AccountUserInfoEditDTO source) {
+
+    }
+
+    @Override
+    public String matchLoginType() {
+        return LoginTypeConstant.WX_APPLET;
+    }
+}

+ 131 - 0
tr-modules/tr-module-smartFollowUp/src/main/java/cn/tr/module/smart/wx/config/WxMaConfiguration.java

@@ -0,0 +1,131 @@
+package cn.tr.module.smart.wx.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.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 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;
+    }
+
+    @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;
+    }
+
+    @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;
+    };
+
+}

+ 46 - 0
tr-modules/tr-module-smartFollowUp/src/main/java/cn/tr/module/smart/wx/config/WxMaProperties.java

@@ -0,0 +1,46 @@
+package cn.tr.module.smart.wx.config;
+
+import java.util.List;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+import lombok.Data;
+
+/**
+ * @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;
+    }
+
+}

+ 77 - 0
tr-modules/tr-module-smartFollowUp/src/main/java/cn/tr/module/smart/wx/controller/WxMaUserController.java

@@ -0,0 +1,77 @@
+package cn.tr.module.smart.wx.controller;
+
+import cn.binarywang.wx.miniapp.api.WxMaService;
+import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
+import cn.binarywang.wx.miniapp.bean.WxMaUserInfo;
+import cn.binarywang.wx.miniapp.util.WxMaConfigHolder;
+import cn.tr.core.exception.ServiceException;
+import cn.tr.core.exception.TRExcCode;
+import cn.tr.core.pojo.CommonResult;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+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;
+
+/**
+ * 微信小程序用户接口
+ *
+ * @author <a href="https://github.com/binarywang">Binary Wang</a>
+ */
+@RestController
+@AllArgsConstructor
+@Slf4j
+@RequestMapping("/wx/user/{appid}")
+public class WxMaUserController {
+    private final WxMaService wxMaService;
+
+    /**
+     * <pre>
+     * 获取用户信息接口
+     * </pre>
+     */
+    @GetMapping("/info")
+    public CommonResult<WxMaUserInfo> info(@PathVariable String appid, String sessionKey,
+                             String signature, String rawData, String encryptedData, String iv) {
+        if (!wxMaService.switchover(appid)) {
+            throw new IllegalArgumentException(String.format("未找到对应appid=[%s]的配置,请核实!", appid));
+        }
+
+        // 用户信息校验
+        if (!wxMaService.getUserService().checkUserInfo(sessionKey, rawData, signature)) {
+            WxMaConfigHolder.remove();//清理ThreadLocal
+            throw new ServiceException(TRExcCode.SYSTEM_ERROR_B0001,"获取用户信息失败");
+        }
+
+        // 解密用户信息
+        WxMaUserInfo userInfo = wxMaService.getUserService().getUserInfo(sessionKey, encryptedData, iv);
+        WxMaConfigHolder.remove();//清理ThreadLocal
+        return CommonResult.success(userInfo);
+    }
+
+    /**
+     * <pre>
+     * 获取用户绑定手机号信息
+     * </pre>
+     */
+    @GetMapping("/phone")
+    public CommonResult<WxMaPhoneNumberInfo> phone(@PathVariable String appid, String sessionKey, String signature,
+                        String rawData, String encryptedData, String iv) {
+        if (!wxMaService.switchover(appid)) {
+            throw new IllegalArgumentException(String.format("未找到对应appid=[%s]的配置,请核实!", appid));
+        }
+
+        // 用户信息校验
+        if (!wxMaService.getUserService().checkUserInfo(sessionKey, rawData, signature)) {
+            WxMaConfigHolder.remove();//清理ThreadLocal
+            throw new ServiceException(TRExcCode.SYSTEM_ERROR_B0001,"获取用户手机号失败");
+        }
+
+        // 解密
+        WxMaPhoneNumberInfo phoneNoInfo = wxMaService.getUserService().getPhoneNoInfo(sessionKey, encryptedData, iv);
+        WxMaConfigHolder.remove();//清理ThreadLocal
+        return CommonResult.success(phoneNoInfo);
+    }
+
+}

+ 109 - 0
tr-modules/tr-module-smartFollowUp/src/main/java/cn/tr/module/smart/wx/controller/WxPortalController.java

@@ -0,0 +1,109 @@
+package cn.tr.module.smart.wx.controller;
+
+import cn.binarywang.wx.miniapp.api.WxMaService;
+import cn.binarywang.wx.miniapp.bean.WxMaMessage;
+import cn.binarywang.wx.miniapp.constant.WxMaConstants;
+import cn.binarywang.wx.miniapp.message.WxMaMessageRouter;
+import cn.binarywang.wx.miniapp.util.WxMaConfigHolder;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.Objects;
+
+/**
+ * @author <a href="https://github.com/binarywang">Binary Wang</a>
+ */
+@RestController
+@AllArgsConstructor
+@RequestMapping("/wx/portal/{appid}")
+@Slf4j
+public class WxPortalController {
+    private final WxMaService wxMaService;
+    private final WxMaMessageRouter wxMaMessageRouter;
+
+    @GetMapping(produces = "text/plain;charset=utf-8")
+    public String authGet(@PathVariable String appid,
+                          @RequestParam(name = "signature", required = false) String signature,
+                          @RequestParam(name = "timestamp", required = false) String timestamp,
+                          @RequestParam(name = "nonce", required = false) String nonce,
+                          @RequestParam(name = "echostr", required = false) String echostr) {
+        log.info("\n接收到来自微信服务器的认证消息:signature = [{}], timestamp = [{}], nonce = [{}], echostr = [{}]",
+            signature, timestamp, nonce, echostr);
+
+        if (StringUtils.isAnyBlank(signature, timestamp, nonce, echostr)) {
+            throw new IllegalArgumentException("请求参数非法,请核实!");
+        }
+
+        if (!wxMaService.switchover(appid)) {
+            throw new IllegalArgumentException(String.format("未找到对应appid=[%s]的配置,请核实!", appid));
+        }
+
+        if (wxMaService.checkSignature(timestamp, nonce, signature)) {
+            WxMaConfigHolder.remove();//清理ThreadLocal
+            return echostr;
+        }
+        WxMaConfigHolder.remove();//清理ThreadLocal
+        return "非法请求";
+    }
+
+    @PostMapping(produces = "application/xml; charset=UTF-8")
+    public String post(@PathVariable String appid,
+                       @RequestBody String requestBody,
+                       @RequestParam(name = "msg_signature", required = false) String msgSignature,
+                       @RequestParam(name = "encrypt_type", required = false) String encryptType,
+                       @RequestParam(name = "signature", required = false) String signature,
+                       @RequestParam("timestamp") String timestamp,
+                       @RequestParam("nonce") String nonce) {
+        log.info("\n接收微信请求:[msg_signature=[{}], encrypt_type=[{}], signature=[{}]," +
+                " timestamp=[{}], nonce=[{}], requestBody=[\n{}\n] ",
+            msgSignature, encryptType, signature, timestamp, nonce, requestBody);
+
+        if (!wxMaService.switchover(appid)) {
+            throw new IllegalArgumentException(String.format("未找到对应appid=[%s]的配置,请核实!", appid));
+        }
+
+        final boolean isJson = Objects.equals(wxMaService.getWxMaConfig().getMsgDataFormat(),
+            WxMaConstants.MsgDataFormat.JSON);
+        if (StringUtils.isBlank(encryptType)) {
+            // 明文传输的消息
+            WxMaMessage inMessage;
+            if (isJson) {
+                inMessage = WxMaMessage.fromJson(requestBody);
+            } else {//xml
+                inMessage = WxMaMessage.fromXml(requestBody);
+            }
+
+            this.route(inMessage);
+            WxMaConfigHolder.remove();//清理ThreadLocal
+            return "success";
+        }
+
+        if ("aes".equals(encryptType)) {
+            // 是aes加密的消息
+            WxMaMessage inMessage;
+            if (isJson) {
+                inMessage = WxMaMessage.fromEncryptedJson(requestBody, wxMaService.getWxMaConfig());
+            } else {//xml
+                inMessage = WxMaMessage.fromEncryptedXml(requestBody, wxMaService.getWxMaConfig(),
+                    timestamp, nonce, msgSignature);
+            }
+
+            this.route(inMessage);
+            WxMaConfigHolder.remove();//清理ThreadLocal
+            return "success";
+        }
+        WxMaConfigHolder.remove();//清理ThreadLocal
+        throw new RuntimeException("不可识别的加密类型:" + encryptType);
+    }
+
+    private void route(WxMaMessage message) {
+        try {
+            wxMaMessageRouter.route(message);
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+        }
+    }
+
+}

+ 12 - 0
tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/oauth2/LoginTypeConstant.java

@@ -0,0 +1,12 @@
+package cn.tr.module.sys.oauth2;
+
+/**
+ * @ClassName : LoginTypeConstant
+ * @Description :
+ * @Author : LF
+ * @Date: 2025年05月19日
+ */
+
+public interface  LoginTypeConstant {
+     String WX_APPLET="WxApplet";
+}

+ 1 - 1
tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/oauth2/config/SaOAuth2TemplateImpl.java

@@ -15,7 +15,7 @@ import java.util.Map;
 
 @Configuration
 @Slf4j
-@EnableConfigurationProperties(TrOAuth2PswClientProperties.class)
+@EnableConfigurationProperties({TrOAuth2PswClientProperties.class,TrOAuth2PswClientProperties.class})
 public  class SaOAuth2TemplateImpl extends SaOAuth2Template implements CommandLineRunner {
     private final static Map<String,SaClientModel> clientMap=new HashMap();
     private final  TrOAuth2PswClientProperties pswClientProperties;

+ 23 - 0
tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/oauth2/controller/OAuth2ServerController.java

@@ -7,14 +7,20 @@ import cn.dev33.satoken.context.model.SaResponse;
 import cn.dev33.satoken.exception.SaTokenException;
 import cn.dev33.satoken.oauth2.SaOAuth2Manager;
 import cn.dev33.satoken.oauth2.config.SaOAuth2Config;
+import cn.dev33.satoken.oauth2.logic.SaOAuth2Consts;
 import cn.dev33.satoken.oauth2.logic.SaOAuth2Handle;
+import cn.dev33.satoken.oauth2.logic.SaOAuth2Util;
+import cn.dev33.satoken.oauth2.model.AccessTokenModel;
+import cn.dev33.satoken.oauth2.model.RequestAuthModel;
 import cn.dev33.satoken.oauth2.model.SaClientModel;
+import cn.dev33.satoken.stp.StpUtil;
 import cn.dev33.satoken.util.SaResult;
 import cn.hutool.core.util.StrUtil;
 import cn.tr.core.exception.ServiceException;
 import cn.tr.core.exception.TRExcCode;
 import cn.tr.core.pojo.CommonResult;
 import cn.tr.core.utils.PswUtils;
+import cn.tr.module.sys.oauth2.LoginTypeConstant;
 import cn.tr.module.sys.oauth2.dto.OAuth2UpdatePswDTO;
 import cn.tr.module.sys.oauth2.psw.operator.AbstractOAuth2PswUserOperator;
 import cn.tr.module.sys.oauth2.dto.OAuth2PswReqDTO;
@@ -54,12 +60,29 @@ public class OAuth2ServerController {
         SaOAuth2Config cfg = SaOAuth2Manager.getConfig();
         SaClientModel cm = SaOAuth2Handle.currClientModel();
         if(cfg.getIsPassword() && (cm.isPassword || cm.isAutoMode)) {
+            String stpType = LoginUserContextHolder.getStpType();
+            if(StrUtil.equals(LoginTypeConstant.WX_APPLET,stpType)){
+                Object retObj = cfg.getDoLoginHandle().apply(source.getAppId(), source.getWxAppletCode());
+                if(!SaTokenUtils.getStpUtil().isLogin()) {
+                    return CommonResult.success();
+                }
+                // 4、构建 ra对象
+                RequestAuthModel ra = new RequestAuthModel();
+                ra.clientId = req.getParam(SaOAuth2Consts.Param.client_id);
+                ra.loginId = SaTokenUtils.getStpUtil();
+                ra.scope = req.getParam(SaOAuth2Consts.Param.scope);
+                // 5、生成 Access-Token
+                AccessTokenModel at = SaOAuth2Util.generateAccessToken(ra, true);
+                // 6、返回 Access-Token
+                return CommonResult.success(at.toLineMap());
+            }
             Object result = SaOAuth2Handle.password(req, res, cfg);
             if(result instanceof SaResult){
                 SaResult saResult= (SaResult) result;
                 return CommonResult.success(saResult.getData());
             }
             throw new ServiceException(TRExcCode.USER_ERROR_A0200,"登录失败");
+
         }
         throw new ServiceException(TRExcCode.USER_ERROR_A0200,"暂未开放的授权模式");
     }

+ 0 - 10
tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/oauth2/dto/AccountUserInfoEditDTO.java

@@ -1,17 +1,7 @@
 package cn.tr.module.sys.oauth2.dto;
 
-import cn.tr.core.utils.UserUtil;
-import cn.tr.module.sys.user.dto.SysOrgSmallDTO;
-import cn.tr.module.sys.user.dto.SysPositionSmallDTO;
-import cn.tr.module.sys.user.dto.SysRoleSmallDTO;
 import io.swagger.annotations.ApiModelProperty;
-import lombok.AllArgsConstructor;
-import lombok.Builder;
 import lombok.Data;
-import lombok.NoArgsConstructor;
-
-import java.util.Date;
-import java.util.List;
 
 /**
  * @ClassName : AccountUserInfoDTO

+ 15 - 12
tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/oauth2/dto/OAuth2PswReqDTO.java

@@ -1,5 +1,6 @@
 package cn.tr.module.sys.oauth2.dto;
 
+import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
 import lombok.experimental.Accessors;
@@ -19,25 +20,27 @@ import javax.validation.constraints.Pattern;
 @Accessors(chain = true)
 public class OAuth2PswReqDTO extends OAuth2DTO {
 
-	@ApiModelProperty(value = "账号",required = true)
-	@NotBlank(message = "账号不能为空")
-	@Length(min = 4, max = 16, message = "账号长度为 4-16 位")
-	@Pattern(regexp = "^[A-Za-z0-9]+$", message = "账号格式为数字以及字母")
+	@ApiModelProperty(value = "账号")
 	private String username;
 
-	@ApiModelProperty(value = "授权范围,可传空字符串",required = true)
-	@NotNull(message = "授权范围不能null")
-	private String grant_type;
+	@ApiModelProperty(value = "授权范围,可传空字符串")
+	private String grant_type="";
 
 
-	@ApiModelProperty(value = "密码",required = true)
-	@NotBlank(message = "密码不能为空")
-	@Length(min = 4, max = 16, message = "密码长度为 4-16 位")
+	@ApiModelProperty(value = "密码")
 	private String password;
 
 	@ApiModelProperty("验证码")
-    private String captchaCode;
+	private String captchaCode;
 
 	@ApiModelProperty("验证码key")
-    private String captchaKey;
+	private String captchaKey;
+
+	@ApiModelProperty("小程序id")
+	private String appId;
+
+	@ApiModelProperty("微信登录码")
+	private String wxAppletCode;
+
+
 }

+ 2 - 1
tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/oauth2/operator/OAuth2UserOperator.java

@@ -20,6 +20,7 @@ import cn.dev33.satoken.session.TokenSign;
 import cn.tr.module.sys.oauth2.LoginTypeMatcher;
 import cn.tr.module.sys.oauth2.dto.AccountUserInfoDTO;
 import cn.tr.module.sys.oauth2.dto.AccountUserInfoEditDTO;
+import cn.tr.module.sys.oauth2.dto.OAuth2PswLoginInfoDTO;
 import cn.tr.module.sys.oauth2.dto.OAuth2PswReqDTO;
 import cn.tr.module.sys.oauth2.enums.OAuth2ModelEnum;
 import cn.tr.module.sys.user.dto.OnlineUserSessionDTO;
@@ -64,7 +65,7 @@ public interface OAuth2UserOperator extends LoginTypeMatcher {
 	 * 获取当前用户登录信息
 	 * @return
 	 */
-	Object getUserLoginInfo();
+	OAuth2PswLoginInfoDTO getUserLoginInfo();
 
 	/**
 	 * 当前用户的账户信息

+ 2 - 1
tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/oauth2/psw/operator/LoginOAuth2PswUserOperator.java

@@ -18,6 +18,7 @@ import cn.tr.module.sys.config.SysConfigProperties;
 import cn.tr.module.sys.oauth2.config.CaptchaOperator;
 import cn.tr.module.sys.oauth2.dto.AccountUserInfoDTO;
 import cn.tr.module.sys.oauth2.dto.AccountUserInfoEditDTO;
+import cn.tr.module.sys.oauth2.dto.OAuth2PswLoginInfoDTO;
 import cn.tr.module.sys.oauth2.mapper.OAuth2Mapper;
 import cn.tr.module.sys.oauth2.dto.OAuth2PswReqDTO;
 import cn.tr.module.sys.tenant.dto.SysTenantCommonDTO;
@@ -178,7 +179,7 @@ public class LoginOAuth2PswUserOperator extends AbstractOAuth2PswUserOperator{
     }
 
     @Override
-    public Object getUserLoginInfo() {
+    public OAuth2PswLoginInfoDTO getUserLoginInfo() {
         String loginId =String.valueOf( SaTokenUtils.getStpUtil().getLoginId());
         return OAuth2Mapper.INSTANCE.toPswLoginInfo(sysUserService.selectSysUserById(loginId));
     }

+ 5 - 0
tr-plugins/tr-spring-boot-starter-plugin-satoken/pom.xml

@@ -27,6 +27,11 @@
             <artifactId>sa-token-dao-redis</artifactId>
         </dependency>
 
+        <dependency>
+            <groupId>com.github.xiaoymin</groupId>
+            <artifactId>knife4j-openapi2-spring-boot-starter</artifactId>
+            <scope>compile</scope>
+        </dependency>
 
         <dependency>
             <groupId>org.springframework.boot</groupId>

+ 13 - 0
tr-plugins/tr-spring-boot-starter-plugin-satoken/src/main/java/cn/tr/plugin/security/config/LoginUserStrategyConfig.java

@@ -1,5 +1,6 @@
 package cn.tr.plugin.security.config;
 
+import cn.hutool.core.util.ObjectUtil;
 import cn.tr.core.exception.ServiceException;
 import cn.tr.core.exception.TRExcCode;
 import cn.tr.core.strategy.auth.ILoginUser;
@@ -30,21 +31,33 @@ public class LoginUserStrategyConfig {
             return new ILoginUser() {
                 @Override
                 public String getUserId() {
+                    if(ObjectUtil.isNull(finalUser)){
+                        return null;
+                    }
                     return finalUser.getUserId();
                 }
 
                 @Override
                 public String getUsername() {
+                    if(ObjectUtil.isNull(finalUser)){
+                        return null;
+                    }
                     return finalUser.getUsername();
                 }
 
                 @Override
                 public String getOrgId() {
+                    if(ObjectUtil.isNull(finalUser)){
+                        return null;
+                    }
                     return finalUser.getOrgId();
                 }
 
                 @Override
                 public String getTenantId() {
+                    if(ObjectUtil.isNull(finalUser)){
+                        return null;
+                    }
                     return finalUser.getTenantId();
                 }
             };

+ 6 - 0
tr-test/pom.xml

@@ -65,6 +65,12 @@
             <version>0.0.9</version>
         </dependency>
 
+        <dependency>
+            <groupId>cn.tr</groupId>
+            <artifactId>tr-module-smartFollowUp</artifactId>
+            <version>0.0.9</version>
+        </dependency>
+
         <dependency>
             <groupId>cn.tr</groupId>
             <artifactId>tr-spring-boot-starter-plugin-banner</artifactId>

+ 9 - 1
tr-test/src/main/resources/application-dev.yml

@@ -32,4 +32,12 @@ spring:
     host: 192.168.100.32
     port: 5672
     username: guest
-    password: guest
+    password: guest
+wx:
+  miniapp:
+    configs:
+      - appid: #微信小程序的appid
+        secret: #微信小程序的Secret
+        token: #微信小程序消息服务器配置的token
+        aesKey: #微信小程序消息服务器配置的EncodingAESKey
+        msgDataFormat: JSON

+ 3 - 3
tr-test/src/main/resources/application-doc.yml

@@ -20,11 +20,11 @@ knife4j:
         api-rule-resources:
           - cn.tr.module.gen.modular.basic
           - cn.tr.module.gen.modular.config
-      quartz:
-        group-name: 任务调度
+      smart:
+        group-name: 智慧随访
         api-rule: package
         api-rule-resources:
-          - cn.tr.module.quartz
+          - cn.tr.module.smart
   setting:
     enable-footer: false
     enable-footer-custom: true