Kaynağa Gözat

微信公众号免登录及发送通知代码上传

wangqiang 8 ay önce
ebeveyn
işleme
34d09e02ff
24 değiştirilmiş dosya ile 969 ekleme ve 5 silme
  1. 7 0
      jeeplus-api/jeeplus-system-api/src/main/java/com/jeeplus/sys/domain/User.java
  2. 10 0
      jeeplus-api/jeeplus-system-api/src/main/java/com/jeeplus/sys/factory/UserApiFallbackFactory.java
  3. 18 0
      jeeplus-api/jeeplus-system-api/src/main/java/com/jeeplus/sys/feign/IUserApi.java
  4. 7 0
      jeeplus-api/jeeplus-system-api/src/main/java/com/jeeplus/sys/service/dto/UserDTO.java
  5. 62 1
      jeeplus-auth/src/main/java/com/jeeplus/auth/controller/LoginController.java
  6. 6 0
      jeeplus-auth/src/main/java/com/jeeplus/auth/model/LoginForm.java
  7. 7 0
      jeeplus-modules/jeeplus-flowable/pom.xml
  8. 36 0
      jeeplus-modules/jeeplus-flowable/src/main/java/com/jeeplus/flowable/controller/FlowableTaskController.java
  9. 57 0
      jeeplus-modules/jeeplus-flowable/src/main/java/com/jeeplus/weChat/config/WechatAccountConfig.java
  10. 51 0
      jeeplus-modules/jeeplus-flowable/src/main/java/com/jeeplus/weChat/config/WechatMpConfig.java
  11. 90 0
      jeeplus-modules/jeeplus-flowable/src/main/java/com/jeeplus/weChat/controller/ProxyController.java
  12. 127 0
      jeeplus-modules/jeeplus-flowable/src/main/java/com/jeeplus/weChat/controller/WeChatController.java
  13. 27 0
      jeeplus-modules/jeeplus-flowable/src/main/java/com/jeeplus/weChat/domain/TemplateInfo.java
  14. 21 0
      jeeplus-modules/jeeplus-flowable/src/main/java/com/jeeplus/weChat/domain/TranTemplate.java
  15. 29 0
      jeeplus-modules/jeeplus-flowable/src/main/java/com/jeeplus/weChat/domain/WXTransportTemplate.java
  16. 105 0
      jeeplus-modules/jeeplus-flowable/src/main/java/com/jeeplus/weChat/service/WxSendService.java
  17. 114 0
      jeeplus-modules/jeeplus-flowable/src/main/java/com/jeeplus/weChat/util/HttpUtils.java
  18. 45 0
      jeeplus-modules/jeeplus-flowable/src/main/java/com/jeeplus/weChat/util/SendMessageUtil.java
  19. 73 0
      jeeplus-modules/jeeplus-flowable/src/main/java/com/jeeplus/weChat/util/SignUtil.java
  20. 8 0
      jeeplus-modules/jeeplus-system/src/main/java/com/jeeplus/sys/controller/UserController.java
  21. 26 4
      jeeplus-modules/jeeplus-system/src/main/java/com/jeeplus/sys/feign/UserApiImpl.java
  22. 9 0
      jeeplus-modules/jeeplus-system/src/main/java/com/jeeplus/sys/mapper/UserMapper.java
  23. 19 0
      jeeplus-modules/jeeplus-system/src/main/java/com/jeeplus/sys/mapper/xml/UserMapper.xml
  24. 15 0
      jeeplus-modules/jeeplus-system/src/main/java/com/jeeplus/sys/service/UserService.java

+ 7 - 0
jeeplus-api/jeeplus-system-api/src/main/java/com/jeeplus/sys/domain/User.java

@@ -6,6 +6,7 @@ package com.jeeplus.sys.domain;
 import com.baomidou.mybatisplus.annotation.TableName;
 import com.jeeplus.core.domain.BaseEntity;
 import com.jeeplus.core.query.Query;
+import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 
@@ -25,6 +26,12 @@ public class User extends BaseEntity {
     private static final long serialVersionUID = 1L;
 
     /**
+     * openId
+     */
+    @ApiModelProperty("用户关注公众号的唯一id")
+    private String openId;
+
+    /**
      * 归属公司
      */
     private String companyId;

+ 10 - 0
jeeplus-api/jeeplus-system-api/src/main/java/com/jeeplus/sys/factory/UserApiFallbackFactory.java

@@ -92,6 +92,16 @@ public class UserApiFallbackFactory implements FallbackFactory <IUserApi> {
             }
 
             @Override
+            public UserDTO getByOpenId(String openId) {
+                return null;
+            }
+
+            @Override
+            public String getOpenIdsByIds(String userIds) {
+                return null;
+            }
+
+            @Override
             public String getByIdForXXL(String id) {
                 return null;
             }

+ 18 - 0
jeeplus-api/jeeplus-system-api/src/main/java/com/jeeplus/sys/feign/IUserApi.java

@@ -124,6 +124,24 @@ public interface IUserApi {
     /**
      * 根据id获取用户
      *
+     * @param openId
+     * @return
+     */
+    @GetMapping(value = BASE_URL + "/getByOpenId")
+    UserDTO getByOpenId(@RequestParam("openId") String openId);
+
+    /**
+     * 根据id获取用户
+     *
+     * @param userIds
+     * @return
+     */
+    @GetMapping(value = BASE_URL + "/getOpenIdsByIds")
+    String getOpenIdsByIds(@RequestParam("userIds") String userIds);
+
+    /**
+     * 根据id获取用户
+     *
      * @param id
      * @return
      */

+ 7 - 0
jeeplus-api/jeeplus-system-api/src/main/java/com/jeeplus/sys/service/dto/UserDTO.java

@@ -20,6 +20,7 @@ import com.jeeplus.core.query.Query;
 import com.jeeplus.core.query.QueryType;
 import com.jeeplus.core.service.dto.BaseDTO;
 import com.jeeplus.sys.feign.ITenantApi;
+import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 import org.hibernate.validator.constraints.Length;
@@ -44,6 +45,12 @@ public class UserDTO extends BaseDTO implements Serializable {
     private static final long serialVersionUID = 1L;
 
     /**
+     * openId
+     */
+    @ApiModelProperty("用户关注公众号的唯一id")
+    private String openId;
+
+    /**
      * 财务公司签字注师被选择次数
      */
     private Integer accountantUserFlag;

+ 62 - 1
jeeplus-auth/src/main/java/com/jeeplus/auth/controller/LoginController.java

@@ -118,7 +118,6 @@ public class LoginController {
             }
         }
 
-
         //登录成功,生成token
         UserDTO userDTO = userApi.getByLoginName ( username, tenantApi.getCurrentTenantId ( ) );
         String token = TokenProvider.createAccessToken ( username );
@@ -126,6 +125,14 @@ public class LoginController {
         //更新登录信息
         updateUserLoginInfo ( responseUtil, userDTO, token );
 
+        // 微信公众号第一次登录,将openId存入 user表中
+        if (StringUtils.isBlank(userDTO.getOpenId())) {
+            if (StringUtils.isNotBlank(loginForm.getOpenId())) {
+                userDTO.setOpenId(loginForm.getOpenId());
+                SpringUtil.getBean(IUserApi.class).saveOrUpdate(userDTO);
+            }
+        }
+
         //删除redis中登录次数的信息
         RedisUtils.getInstance ().delete ( CacheNames.USER_CACHE_LOGIN_CODE + loginUserName );
 
@@ -137,6 +144,60 @@ public class LoginController {
         return responseUtil.ok ( );
     }
 
+    /**
+     * 用户登录
+     *
+     * @param loginForm
+     * @return
+     */
+    @PostMapping("/sys/wxLogin")
+    @ApiLog(value = "用户登录", type = LogTypeEnum.LOGIN)
+    @ApiOperation("登录接口")
+    public ResponseEntity wxLogin(@RequestBody LoginForm loginForm) {
+        ResponseUtil responseUtil = new ResponseUtil ( );
+
+        UserDTO user = SpringUtil.getBean(IUserApi.class).getByOpenId(loginForm.getOpenId());
+
+        String username = user.getLoginName( );
+
+        Integer redisLoginNumber = (Integer) RedisUtils.getInstance ().get ( CacheNames.USER_CACHE_LOGIN_CODE + username );
+        if(null == redisLoginNumber){
+            redisLoginNumber = 0;
+        }else{
+            redisLoginNumber ++ ;
+        }
+        RedisUtils.getInstance().set(CacheNames.USER_CACHE_LOGIN_CODE + username , redisLoginNumber);
+        //给登录次数记录设置6小时的过期时间
+        RedisUtils.getInstance().expire(CacheNames.USER_CACHE_LOGIN_CODE + username , 21600);
+
+        AuthenticationManager authenticationManager = SpringUtil.getBean ( AuthenticationManager.class );
+        SecurityUtils.login ( username, "jsxgpassword", authenticationManager ); //登录操作spring security
+
+        String domain = RequestUtils.getHeader ( "domain" );
+        if (domain.contains("ydddl")){
+
+        } else {
+            /**
+             * 单一登录判断
+             */
+            if ( !userApi.isEnableLogin ( tenantApi.getCurrentTenantId ( ), username ) ) {
+                throw new DisabledException ( ErrorConstants.LOGIN_ERROR_FORBID_LOGGED_IN_ELSEWHERE );
+            }
+        }
+
+        //登录成功,生成token
+        UserDTO userDTO = userApi.getByLoginName ( username, tenantApi.getCurrentTenantId ( ) );
+        String token = TokenProvider.createAccessToken ( username );
+        responseUtil.add ( TokenProvider.TOKEN, token );
+        //更新登录信息
+        updateUserLoginInfo ( responseUtil, userDTO, token );
+
+        //删除redis中登录次数的信息
+        RedisUtils.getInstance ().delete ( CacheNames.USER_CACHE_LOGIN_CODE + username );
+
+        return responseUtil.ok ( );
+    }
+
 
     /**
      * cas登录

+ 6 - 0
jeeplus-auth/src/main/java/com/jeeplus/auth/model/LoginForm.java

@@ -31,4 +31,10 @@ public class LoginForm {
      */
     @ApiModelProperty("验证码对应的唯一UUID")
     private String uuid;
+
+    /**
+     * openId
+     */
+    @ApiModelProperty("用户关注公众号的唯一id")
+    private String openId;
 }

+ 7 - 0
jeeplus-modules/jeeplus-flowable/pom.xml

@@ -156,6 +156,13 @@
             <artifactId>commons-httpclient</artifactId>
             <version>3.1</version>
         </dependency>
+
+        <!--微信开发sdk-->
+        <dependency>
+            <groupId>com.github.binarywang</groupId>
+            <artifactId>weixin-java-mp</artifactId>
+            <version>2.7.0</version>
+        </dependency>
     </dependencies>
 
     <build>

+ 36 - 0
jeeplus-modules/jeeplus-flowable/src/main/java/com/jeeplus/flowable/controller/FlowableTaskController.java

@@ -34,6 +34,7 @@ import com.jeeplus.flowable.vo.ProcessVo;
 import com.jeeplus.mail.feign.IMailApi;
 import com.jeeplus.sys.feign.IUserApi;
 import com.jeeplus.sys.service.dto.UserDTO;
+import com.jeeplus.weChat.util.SendMessageUtil;
 import io.swagger.annotations.ApiOperation;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.ibatis.annotations.Param;
@@ -47,6 +48,7 @@ import org.flowable.engine.RuntimeService;
 import org.flowable.engine.TaskService;
 import org.flowable.engine.runtime.ProcessInstance;
 import org.flowable.task.api.Task;
+import org.flowable.task.api.TaskQuery;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.cloud.openfeign.SpringQueryMap;
 import org.springframework.http.ResponseEntity;
@@ -419,6 +421,7 @@ public class FlowableTaskController {
                 }
             }
         }
+        sendMsgToOpenIds(procInsId);
         return ResponseEntity.ok(procInsId);
     }
 
@@ -642,9 +645,42 @@ public class FlowableTaskController {
                 taskService.setAssignee(task.getId(), flow.getAssignee());
             }
         }
+        sendMsgToOpenIds(flow.getProcInsId());
         return ResponseEntity.ok(flow.getProcInsId());
     }
 
+    // 向审核人发送 公众号通知
+    public void sendMsgToOpenIds(String procInsId){
+
+        TaskQuery todoTaskQuery = taskService.createTaskQuery()
+                .active()
+                .processInstanceId(procInsId) // 添加过滤条件
+                .includeProcessVariables()
+                .orderByTaskCreateTime()
+                .desc();
+
+        StringBuilder allUsersStringBuilder = new StringBuilder();
+        List<Task> list = todoTaskQuery.list();
+        for (int i = 0; i < list.size(); i++) {
+            List<String> users = flowTaskService.getTaskAuditUsers(list.get(i).getId());
+            // 根据得到的审核人id,查询出对应的openId
+
+            for (String user : users) {
+                if (allUsersStringBuilder.length() > 0) {
+                    allUsersStringBuilder.append(","); // 添加逗号作为分隔符
+                }
+                allUsersStringBuilder.append(user);
+            }
+            String userIds = allUsersStringBuilder.toString();
+
+            String openIds = SpringUtil.getBean(IUserApi.class).getOpenIdsByIds(userIds);
+            String[] openIdArray = openIds.split(",");
+            List<String> openIdsList = Arrays.asList(openIdArray);
+
+            SendMessageUtil.sendMessage(list.get(i), openIdsList);
+        }
+    }
+
     /**
      * 取回已经执行的任务,只有在下一任务节点未执行或者未签收时才能取回
      */

+ 57 - 0
jeeplus-modules/jeeplus-flowable/src/main/java/com/jeeplus/weChat/config/WechatAccountConfig.java

@@ -0,0 +1,57 @@
+package com.jeeplus.weChat.config;
+
+import lombok.Data;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+import java.util.Map;
+
+/**
+ * @author 王强
+ * @version 1.0
+ * @date 2024-07-15 17:07
+ */
+@Data
+@Component
+//从配置文件里获取
+@ConfigurationProperties(prefix = "wechat")
+public class WechatAccountConfig {
+
+    /**
+     * 公众平台id
+     */
+    private String mpAppId;
+
+    /**
+     * 公众平台密钥
+     */
+    private String mpAppSecret;
+
+    /**
+     * 微信模版id
+     */
+    private Map<String, String> templateId;
+
+    //appID
+    public static String appID;
+    //appsecret
+    public static String appsecret;
+    //获取access_token的url
+    public static String accesstokenURL;
+
+    @Value("${wechat.mpAppId}")
+    public void setAppID(String appID) {
+        WechatAccountConfig.appID = appID;
+    }
+
+    @Value("${wechat.mpAppSecret}")
+    public void setAppsecret(String appsecret) {
+        WechatAccountConfig.appsecret = appsecret;
+    }
+
+    @Value("${wechat.accesstokenURL}")
+    public void setAccesstokenURL(String accesstokenURL) {
+        WechatAccountConfig.accesstokenURL = accesstokenURL;
+    }
+}

+ 51 - 0
jeeplus-modules/jeeplus-flowable/src/main/java/com/jeeplus/weChat/config/WechatMpConfig.java

@@ -0,0 +1,51 @@
+package com.jeeplus.weChat.config;
+
+import me.chanjar.weixin.mp.api.WxMpConfigStorage;
+import me.chanjar.weixin.mp.api.WxMpInMemoryConfigStorage;
+import me.chanjar.weixin.mp.api.WxMpService;
+import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.stereotype.Component;
+
+/**
+ * @author 王强
+ * @version 1.0
+ * @date 2024-07-15 17:08
+ */
+@Component
+public class WechatMpConfig {
+
+    @Autowired
+    private WechatAccountConfig accountConfig;
+
+    /**
+     * @author :tao
+     * @date :Created in 2021/3/12 10:15
+     * @param: :
+     * @return: WxMpService 对象
+     * 配置wxMpConfigStorage,返回 WxMpService 对象
+     */
+    @Bean
+    public WxMpService wxMpService() {
+        WxMpService wxMpService = new WxMpServiceImpl();
+        wxMpService.setWxMpConfigStorage(wxMpConfigStorage());
+        return wxMpService;
+    }
+
+    /**
+     * @author :tao
+     * @date :Created in 2021/3/12 10:20
+     * @param: :
+     * @return: WxMpConfigStorage 对象
+     * 配置AppId、和AppSecret,获取WxMpConfigStorage 对象
+     */
+    @Bean
+    public WxMpConfigStorage wxMpConfigStorage() {
+        WxMpInMemoryConfigStorage wxMpConfigStorage = new WxMpInMemoryConfigStorage();
+        wxMpConfigStorage.setAppId(accountConfig.getMpAppId());
+        wxMpConfigStorage.setSecret(accountConfig.getMpAppSecret());
+        return wxMpConfigStorage;
+    }
+
+}

+ 90 - 0
jeeplus-modules/jeeplus-flowable/src/main/java/com/jeeplus/weChat/controller/ProxyController.java

@@ -0,0 +1,90 @@
+package com.jeeplus.weChat.controller;
+
+import com.alibaba.fastjson.JSONObject;
+import com.jeeplus.weChat.domain.WXTransportTemplate;
+import com.jeeplus.weChat.service.WxSendService;
+import com.jeeplus.weChat.util.HttpUtils;
+import lombok.extern.slf4j.Slf4j;
+import me.chanjar.weixin.common.exception.WxErrorException;
+import me.chanjar.weixin.mp.api.WxMpService;
+import me.chanjar.weixin.mp.bean.result.WxMpOAuth2AccessToken;
+import me.chanjar.weixin.mp.bean.result.WxMpUser;
+import org.flowable.ui.common.service.exception.BadRequestException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import java.io.IOException;
+import java.util.Map;
+
+/**
+ * @author 王强
+ * @version 1.0
+ * @date 2024-07-17 9:57
+ */
+@RestController
+@Slf4j
+public class ProxyController {
+
+    @Autowired
+    private WxMpService wxMpService;
+
+    @Resource
+    private WxSendService wxSendService;
+
+    @GetMapping("/app/proxy")
+    public ResponseEntity<?> proxy(@RequestParam String appid,
+                                   @RequestParam String secret,
+                                   @RequestParam String code) {
+        try {
+            // 获取 access token
+            WxMpOAuth2AccessToken wxMpOAuth2AccessToken = wxMpService.oauth2getAccessToken(code);
+            log.info("【AccessToken:】{}", wxMpOAuth2AccessToken.getAccessToken());
+
+            // 获取 openid
+            String openId = wxMpOAuth2AccessToken.getOpenId();
+            log.info("【openid:】{}", openId);
+
+            // 获取用户信息
+            WxMpUser wxMpUser = wxMpService.oauth2getUserInfo(wxMpOAuth2AccessToken, "zh_CN");
+            log.info("【用户信息:】{}", wxMpUser.toString());
+
+            // 返回用户信息
+            return ResponseEntity.ok(wxMpUser);
+
+        } catch (WxErrorException e) {
+            log.error("【微信网页授权错误】{}", e.getMessage(), e);
+            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("微信授权失败: " + e.getMessage());
+        }
+    }
+
+    // 获取微信的access_token(这个token使用一次及失效)
+    @GetMapping("/app/getToken")
+    public String getAccess_token(){
+        String token = HttpUtils.dogetToken();
+        return token;
+    }
+
+    @PostMapping("/app/sendTemplate")
+    public String sendTranTemplate( @RequestBody WXTransportTemplate resource) throws IOException {
+        //templateid,模板id,就是发送信息的格式模板id
+        String result = wxSendService.sendTranTemplate(resource);
+        //判断发送模板消息结果
+        JSONObject obj = JSONObject.parseObject(result);
+        Map<String, Object> map =obj;
+        Integer code = (Integer)map.get("errcode");
+        String msg =map.get("errmsg").toString();
+        if (0  != code  ||  !"ok".equals(msg)){
+            throw new BadRequestException("微信公众号模板消息推送失败,错误代码为:+"+code+"错误消息为:"+msg);
+        }
+        return result;
+    }
+
+
+
+
+//    这是测试号在微信开发者工具里面的 地址
+//    https://open.weixin.qq.com/connect/oauth2/authorize?appid=wxa79f618dcaf992f7&redirect_uri=http%3A%2F%2Fk2e6ew.natappfree.cc%2Fh5%2F&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect
+}

+ 127 - 0
jeeplus-modules/jeeplus-flowable/src/main/java/com/jeeplus/weChat/controller/WeChatController.java

@@ -0,0 +1,127 @@
+package com.jeeplus.weChat.controller;
+
+import com.jeeplus.weChat.util.SignUtil;
+import lombok.extern.slf4j.Slf4j;
+import me.chanjar.weixin.common.api.WxConsts;
+import me.chanjar.weixin.common.exception.WxErrorException;
+import me.chanjar.weixin.mp.api.WxMpService;
+import me.chanjar.weixin.mp.bean.result.WxMpOAuth2AccessToken;
+import me.chanjar.weixin.mp.bean.result.WxMpUser;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+
+/**
+ * @author 王强
+ * @version 1.0
+ * @date 2024-07-15 16:45
+ */
+@RestController
+@Slf4j
+public class WeChatController {
+
+    private final String TOKEN = "wxs1314520"; // 你设置的Token
+
+    @GetMapping("/wechat")
+    public void sendMsgTest(HttpServletRequest request, HttpServletResponse response) throws IOException {
+        // 微信加密签名.
+        String signature = request.getParameter("signature");
+        // 时间戳.
+        String timestamp = request.getParameter("timestamp");
+        // 随机数.
+        String nonce = request.getParameter("nonce");
+        // 随机字符串.
+        String echostr = request.getParameter("echostr");
+        // 请求校验,若校验成功则原样返回echostr,表示接入成功,否则接入失败.
+        PrintWriter out = response.getWriter();
+
+        // 请求校验,若校验成功则原样返回echostr,表示接入成功,否则接入失败.
+        if (SignUtil.checkSignature(signature, timestamp, nonce)) {
+            out.print(echostr);
+        }
+
+        out.close();
+        out = null;
+    }
+
+    @Autowired
+    private WxMpService wxMpService;
+
+    @Value("${wechat.projecturl}")
+    private String projectUrl;
+
+    @GetMapping("/auth")
+    @ResponseBody
+    public String auth(@RequestParam(value = "echostr", defaultValue = "没有获取到") String echostr) {
+        return echostr;
+    }
+
+    /**
+     * @author :tao
+     * @param: :
+     * @return: 重定向到获取用户信息的类
+     * 微信授权登录
+     */
+    @GetMapping( value = "/authorize")
+    public String authorize(@RequestParam(value = "returnUrl", defaultValue = "http://k2e6ew.natappfree.cc/") String returnUrl) throws UnsupportedEncodingException {
+        log.info("【微信网页授权】进来了,参数={}", returnUrl);
+        System.out.println("进来了:" + returnUrl);
+        //1. 配置
+        //2. 调用方法
+        String url = projectUrl + "api/userInfo";
+        /*
+         * 相当于这种形式
+         * URLEncoder.decode(returnUrl,"UTF-8"
+         * https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect
+         */
+        String redirectUrl = wxMpService.oauth2buildAuthorizationUrl(url, WxConsts.OAUTH2_SCOPE_USER_INFO, URLEncoder.encode(returnUrl, "utf-8"));
+        log.info("【微信网页授权】获取code,result={}", redirectUrl);
+        return "redirect:" + redirectUrl;
+
+    }
+
+    /**
+     * @author :tao
+     * @param: :
+     * @return: 重定向
+     * 获取用户信息类,最后重定向到指定url
+     */
+    @GetMapping("/userInfo")
+    public String userInfo(@RequestParam("code") String code,
+                           @RequestParam("state") String returnUrl) throws WxErrorException {
+        /*当用户同意授权后,会回调所设置的url并把authorization code传过来,
+        然后用这个code获得access token,其中也包含用户的openid等信息*/
+        WxMpOAuth2AccessToken wxMpOAuth2AccessToken = new WxMpOAuth2AccessToken();
+        try {
+            //获取access token
+            wxMpOAuth2AccessToken = wxMpService.oauth2getAccessToken(code);
+            log.info("【AccessToken:】{}", wxMpOAuth2AccessToken.getAccessToken());
+        } catch (WxErrorException e) {
+            log.error("【微信网页授权】{}", e);
+        }
+
+        // 拿到openid
+        String openId = wxMpOAuth2AccessToken.getOpenId();
+        log.info("【openid:】{}", openId);
+        log.info("【我是前端要回调的地址:】{}", returnUrl + "&openid=" + openId);
+        // 顺便获取一下用户信息
+        WxMpUser wxMpUser = wxMpService.oauth2getUserInfo(wxMpOAuth2AccessToken, "zh_CN");
+        log.info("【用户信息:】{}", wxMpUser.toString());
+
+        //注意拼接参数,第一个参数需要加问号,之后参数使用&拼接的问题
+        //return "redirect:" + returnUrl + "/#/?openid=" + openId;
+//        return "redirect:" + returnUrl;
+        return wxMpUser.getOpenId();
+    }
+}
+

+ 27 - 0
jeeplus-modules/jeeplus-flowable/src/main/java/com/jeeplus/weChat/domain/TemplateInfo.java

@@ -0,0 +1,27 @@
+package com.jeeplus.weChat.domain;
+
+import lombok.Data;
+
+/**
+ * 模板内容类
+ * @author 王强
+ * @version 1.0
+ * @date 2024-07-22 10:24
+ */
+@Data
+public class TemplateInfo {
+
+    //内容
+    private String value;
+    //字体颜色
+    private String color;
+
+    public TemplateInfo(String value, String color) {
+        this.value = value;
+        this.color = color;
+    }
+
+    public TemplateInfo(){
+
+    }
+}

+ 21 - 0
jeeplus-modules/jeeplus-flowable/src/main/java/com/jeeplus/weChat/domain/TranTemplate.java

@@ -0,0 +1,21 @@
+package com.jeeplus.weChat.domain;
+
+import lombok.Data;
+
+import java.util.Map;
+
+/**
+ * 模板所需信息类
+ * @author 王强
+ * @version 1.0
+ * @date 2024-07-22 10:23
+ */
+@Data
+public class TranTemplate {
+    //模板信息
+    Map<String , TemplateInfo> data;
+    //模板ID
+    String template_id;
+    //接收人ID
+    String touser;
+}

+ 29 - 0
jeeplus-modules/jeeplus-flowable/src/main/java/com/jeeplus/weChat/domain/WXTransportTemplate.java

@@ -0,0 +1,29 @@
+package com.jeeplus.weChat.domain;
+
+import lombok.Data;
+
+/**
+ * 入参类
+ * @author 王强
+ * @version 1.0
+ * @date 2024-07-22 10:23
+ */
+@Data
+public class WXTransportTemplate {
+
+    private String openid;//目标客户
+
+    private String transNum;  //运输单号
+
+    private String engiName;  //工程名称
+
+    private String titleName; //实例标题
+
+    private String processName; //流程名称
+
+    private String currentStage; //当前环节
+
+    private String processInitiator; //流程发起人
+
+    private String createTime; //创建时间
+}

+ 105 - 0
jeeplus-modules/jeeplus-flowable/src/main/java/com/jeeplus/weChat/service/WxSendService.java

@@ -0,0 +1,105 @@
+package com.jeeplus.weChat.service;
+
+import cn.hutool.json.JSONObject;
+import cn.hutool.json.JSONUtil;
+import com.jeeplus.weChat.domain.TemplateInfo;
+import com.jeeplus.weChat.domain.TranTemplate;
+import com.jeeplus.weChat.domain.WXTransportTemplate;
+import com.jeeplus.weChat.util.HttpUtils;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author 王强
+ * @version 1.0
+ * @date 2024-07-22 10:31
+ */
+@Service
+@Transactional
+public class WxSendService {
+
+    @Value("${wechat.sendTemplateUrl}")
+    private String sendTemplateUrl;
+
+    //发送运输单模板信息给司机
+    public String sendTranTemplate(WXTransportTemplate resource){
+        //拿到accessToken
+        String accessToken = HttpUtils.access_token;
+        if (null == accessToken){
+            accessToken = HttpUtils.dogetToken();
+        }
+        System.out.println("现在取出的token是:"+accessToken);
+
+        //发送模板消息开始
+        TranTemplate tranTemplate = new TranTemplate();
+        //设置接收司机微信ID
+        tranTemplate.setTouser(resource.getOpenid());
+        //设置模板ID
+        tranTemplate.setTemplate_id("3b5rKQ1nTjvML1Xyj0OSMV0CnMNt7WjdzPksAHhvhRI");
+        //给模板的内容赋值
+        Map<String , TemplateInfo> dataMap = new HashMap<>();
+        TemplateInfo first = new TemplateInfo("您好,您有一条运输单需要运输。","#DC143C");
+        dataMap.put("first",first);
+        TemplateInfo keyword1 = new TemplateInfo(resource.getTransNum(),"#173177");
+        dataMap.put("keyword1",keyword1);
+        TemplateInfo keyword2 = new TemplateInfo(resource.getEngiName(),"#173177");
+        dataMap.put("keyword2",keyword2);
+        TemplateInfo remark = new TemplateInfo("\n请及时运输","#DC143C");
+        dataMap.put("remark",remark);
+        tranTemplate.setData(dataMap);
+
+        JSONObject jsonObject = JSONUtil.parseObj(tranTemplate);
+        //将入参转为字符串
+        String jsonParam = jsonObject.toString();
+        //发起请求发送模板信息
+        String result = HttpUtils.requestPost(sendTemplateUrl + accessToken, jsonParam);
+
+        return  result;
+    }
+
+    //微信公众号-发送流程通知
+    public String sendProcessTemplate(WXTransportTemplate resource){
+        //拿到accessToken
+        String accessToken = HttpUtils.dogetToken();
+
+        //发送模板消息开始
+        TranTemplate tranTemplate = new TranTemplate();
+        //设置接收人的openid(当前登录人的openid)
+
+        tranTemplate.setTouser(resource.getOpenid());
+
+        //设置模板ID
+        tranTemplate.setTemplate_id("ehTUuyDFL7f2RApsMXLsJEPlCgt82aTj2vtmO2l9Cso");
+        //给模板的内容赋值
+        Map<String , TemplateInfo> dataMap = new HashMap<>();
+
+        TemplateInfo title = new TemplateInfo(resource.getTitleName(),"#173177");
+        dataMap.put("title",title);
+
+        TemplateInfo processName = new TemplateInfo(resource.getProcessName(),"#173177");
+        dataMap.put("processName",processName);
+
+        TemplateInfo currentStage = new TemplateInfo(resource.getCurrentStage(),"#173177");
+        dataMap.put("currentStage",currentStage);
+
+        TemplateInfo processInitiator = new TemplateInfo(resource.getProcessInitiator(),"#173177");
+        dataMap.put("processInitiator",processInitiator);
+
+        TemplateInfo createTime = new TemplateInfo(resource.getCreateTime(),"#173177");
+        dataMap.put("createTime",createTime);
+
+        tranTemplate.setData(dataMap);
+
+        JSONObject jsonObject = JSONUtil.parseObj(tranTemplate);
+        //将入参转为字符串
+        String jsonParam = jsonObject.toString();
+        //发起请求发送模板信息
+        String result = HttpUtils.requestPost(sendTemplateUrl + accessToken, jsonParam);
+
+        return  result;
+    }
+}

+ 114 - 0
jeeplus-modules/jeeplus-flowable/src/main/java/com/jeeplus/weChat/util/HttpUtils.java

@@ -0,0 +1,114 @@
+package com.jeeplus.weChat.util;
+
+import com.alibaba.fastjson.JSONObject;
+import com.jeeplus.weChat.config.WechatAccountConfig;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.http.HttpEntity;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.util.EntityUtils;
+import org.flowable.ui.common.service.exception.BadRequestException;
+
+import java.io.IOException;
+import java.util.Map;
+import java.util.Timer;
+
+/**
+ * @author 王强
+ * @version 1.0
+ * @date 2024-07-22 9:26
+ */
+@Slf4j
+public class HttpUtils {
+
+    public static String access_token;
+    public static String requestPost(String url,String jsonParam){
+
+        System.out.println(jsonParam);
+
+        // 获取连接客户端工具
+        CloseableHttpClient httpClient = HttpClients.createDefault();
+
+        String entityStr = null;
+        CloseableHttpResponse response = null;
+
+        try {
+            // 创建POST请求对象s
+            HttpPost httpPost = new HttpPost(url);
+            if (!"".equals(jsonParam)){
+                // 创建请求参数
+                StringEntity s = new StringEntity(jsonParam, "utf-8");
+                //设置参数到请求对象中
+                httpPost.setEntity(s);
+            }
+            //添加请求头信息
+            httpPost.addHeader("User-Agent", "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.6)");
+            httpPost.addHeader("Content-Type", "application/json");
+            // 执行请求
+            response = httpClient.execute(httpPost);
+            // 获得响应
+            HttpEntity entity = response.getEntity();
+            // 将响应结果转换成字符串
+            entityStr = EntityUtils.toString(entity, "UTF-8");
+
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            // 释放连接
+            if (null != response) {
+                try {
+                    response.close();
+                    httpClient.close();
+                } catch (IOException e) {
+                    log.info("释放连接出错");
+                    e.printStackTrace();
+                }
+            }
+        }
+
+        // 打印响应内容
+        System.out.println(entityStr);
+        return entityStr;
+    }
+
+    public static void getAccessToken(){
+        Timer timer=new Timer();
+        timer.schedule(new java.util.TimerTask() {
+            @Override
+            public void run() {
+                dogetToken();
+            }
+        },3000,60*100*1000);    // 三秒后执行,一百分钟执行一次
+    }
+
+    //请求access_token的方法
+    public static String dogetToken(){
+        String url = WechatAccountConfig.accesstokenURL;
+        //将配置文件中的appid和appsecret值填到url中
+        String realURL = url.replaceAll("APPID", WechatAccountConfig.appID).replaceAll("APPSECRET",WechatAccountConfig.appsecret);
+        System.out.println(realURL);
+        //调用获得access_token的接口
+        String result = requestPost(realURL, "");
+        System.out.println(result);
+
+        JSONObject obj = JSONObject.parseObject(result);
+        Map<String, Object> map =obj;
+        try {
+            //从结果中取出access_token
+            String  access_token  = map.get("access_token").toString();
+            HttpUtils.access_token = access_token;  //把得到的token存在静态变量中
+            return access_token;
+        }catch (Exception e){
+            //如果返回的结果中有errcode和errmsg,说明接口调用失败
+            String errcode = map.get("errcode").toString();
+            String errmsg = map.get("errmsg").toString();
+            throw new BadRequestException("微信公众平台获得access_token失败,错误码为:"+errcode+"错误信息为:"+errmsg);
+        }
+
+    }
+
+}
+

+ 45 - 0
jeeplus-modules/jeeplus-flowable/src/main/java/com/jeeplus/weChat/util/SendMessageUtil.java

@@ -0,0 +1,45 @@
+package com.jeeplus.weChat.util;
+
+import cn.hutool.extra.spring.SpringUtil;
+import com.jeeplus.flowable.utils.ProcessDefCache;
+import com.jeeplus.weChat.domain.WXTransportTemplate;
+import com.jeeplus.weChat.service.WxSendService;
+import org.apache.commons.lang3.StringUtils;
+import org.flowable.task.api.Task;
+
+import java.text.SimpleDateFormat;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author 王强
+ * @version 1.0
+ * @date 2024-07-24 14:30
+ */
+public class SendMessageUtil {
+
+    // 给下一节点审核人发送通知
+    public static void sendMessage(Task task, List<String> openIds){
+
+        for (int i = 0; i < openIds.size(); i++) {
+
+            if (StringUtils.isNotBlank(openIds.get(i))) {
+                WXTransportTemplate template = new WXTransportTemplate();
+                SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+
+                Map<String, Object> map = task.getProcessVariables();
+
+                template.setOpenid(openIds.get(i));
+                template.setTitleName((String) map.get("title"));
+                template.setProcessName(ProcessDefCache.get ( task.getProcessDefinitionId ( ) ).getName ( ));
+                template.setCurrentStage(task.getName());
+                template.setProcessInitiator((String) map.get("userName"));
+                template.setCreateTime(format.format(task.getCreateTime()));
+
+                SpringUtil.getBean ( WxSendService.class ).sendProcessTemplate(template);
+            }
+
+        }
+
+    }
+}

+ 73 - 0
jeeplus-modules/jeeplus-flowable/src/main/java/com/jeeplus/weChat/util/SignUtil.java

@@ -0,0 +1,73 @@
+package com.jeeplus.weChat.util;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
+
+/**
+ * @author 王强
+ * @version 1.0
+ * @date 2024-07-22 9:14
+ */
+public class SignUtil {
+
+    // 与开发模式接口配置信息中的Token保持一致.
+    private static String token = "wxs1314520";
+
+    /**
+     * 校验签名
+     * @param signature 微信加密签名.
+     * @param timestamp 时间戳.
+     * @param nonce 随机数.
+     * @return
+     */
+    public static boolean checkSignature(String signature, String timestamp, String nonce) {
+        // 对token、timestamp、和nonce按字典排序.
+        String[] paramArr = new String[] {token, timestamp, nonce};
+        Arrays.sort(paramArr);
+
+        // 将排序后的结果拼接成一个字符串.
+        String content  = paramArr[0].concat(paramArr[1]).concat(paramArr[2]);
+
+        String ciphertext = null;
+        try {
+            MessageDigest md = MessageDigest.getInstance("SHA-1");
+            // 对拼接后的字符串进行sha1加密.
+            byte[] digest = md.digest(content.toString().getBytes());
+            ciphertext = byteToStr(digest);
+        } catch (NoSuchAlgorithmException e) {
+            e.printStackTrace();
+        }
+
+        // 将sha1加密后的字符串与signature进行对比.
+        return ciphertext != null ? ciphertext.equals(signature.toUpperCase()) : false;
+    }
+
+    /**
+     * 将字节数组转换为十六进制字符串.
+     * @param byteArray
+     * @return
+     */
+    private static String byteToStr(byte[] byteArray) {
+        String strDigest = "";
+        for (int i = 0; i < byteArray.length; i++) {
+            strDigest += byteToHexStr(byteArray[i]);
+        }
+        return strDigest;
+    }
+
+    /**
+     * 将字节转换为十六进制字符串.
+     * @param mByte
+     * @return
+     */
+    private static String byteToHexStr(byte mByte) {
+        char[] Digit = { '0', '1' , '2', '3', '4' , '5', '6', '7' , '8', '9', 'A' , 'B', 'C', 'D' , 'E', 'F'};
+        char[] tempArr = new char[2];
+        tempArr[0] = Digit[(mByte >>> 4) & 0X0F];
+        tempArr[1] = Digit[mByte & 0X0F];
+
+        String s = new String(tempArr);
+        return s;
+    }
+}

+ 8 - 0
jeeplus-modules/jeeplus-system/src/main/java/com/jeeplus/sys/controller/UserController.java

@@ -80,6 +80,14 @@ public class UserController {
     @Autowired
     private RedisUtils redisUtils;
 
+    @ApiLog("获取所有的openId信息")
+    @GetMapping("/allOpenIds")
+    @ApiOperation(value = "判断当前用户是否是管理员")
+    public List<String> getAllOpenIds(){
+        List<String> openIds = userService.getAllOpenIds();
+        return openIds;
+    }
+
     /**
      * 根据ids查询用户基本信息(姓名、手机、角色、部门)
      *

+ 26 - 4
jeeplus-modules/jeeplus-system/src/main/java/com/jeeplus/sys/feign/UserApiImpl.java

@@ -21,10 +21,7 @@ import org.springframework.security.core.context.SecurityContextHolder;
 import org.springframework.web.bind.annotation.RestController;
 
 import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
+import java.util.*;
 import java.util.stream.Collectors;
 
 @RestController
@@ -111,6 +108,31 @@ public class UserApiImpl implements IUserApi {
     }
 
     @Override
+    public UserDTO getByOpenId(String openId) {
+        return userService.getByOpenId(openId);
+    }
+
+    @Override
+    public String getOpenIdsByIds(String userIds) {
+        // 使用逗号分隔字符串并转换为数组
+        String[] usersArray = userIds.split(",");
+
+        StringBuilder allUsersStringBuilder = new StringBuilder();
+        // 将数组转换为 List
+        List<String> usersList = Arrays.asList(usersArray);
+        List<String> openIds = userService.getOpenIdsByIds(usersList);
+
+        for (String openId : openIds) {
+            if (allUsersStringBuilder.length() > 0) {
+                allUsersStringBuilder.append(","); // 添加逗号作为分隔符
+            }
+            allUsersStringBuilder.append(openId);
+        }
+        String userId = allUsersStringBuilder.toString();
+        return userId;
+    }
+
+    @Override
     public String getByIdForXXL(String id) {
         UserDTO userDTO = UserUtils.get(id);
         String s = JSON.toJSONString(userDTO);

+ 9 - 0
jeeplus-modules/jeeplus-system/src/main/java/com/jeeplus/sys/mapper/UserMapper.java

@@ -27,6 +27,15 @@ import java.util.List;
  */
 public interface UserMapper extends BaseMapper <User> {
 
+    @InterceptorIgnore(tenantLine = "true")
+    List<String> getAllOpenIds();
+
+    @InterceptorIgnore(tenantLine = "true")
+    List<String> getOpenIdsByIds(@Param("ids") List<String> ids);
+
+    @InterceptorIgnore(tenantLine = "true")
+    UserDTO getByOpenId(@Param("openId") String openId);
+
     /**
      * 获取岗位列表
      *

+ 19 - 0
jeeplus-modules/jeeplus-system/src/main/java/com/jeeplus/sys/mapper/xml/UserMapper.xml

@@ -65,6 +65,7 @@
 		a.sign,
 		a.create_by_id AS "createBy.id",
 		a.create_time,
+		a.open_id as openId,
 		a.update_by_id AS "updateBy.id",
 		a.manage_office_ids AS "manageOfficeIds",
 		a.update_time,
@@ -612,6 +613,24 @@ select a.id, a.company_id as "companyDTO.id", a.office_id as "officeDTO.id", a.l
             office_id
         from sys_user_manage_office where user_id = #{id}
     </select>
+    <select id="getOpenIdsByIds" resultType="java.lang.String">
+        select open_id from sys_user
+        where del_flag = 0
+        <if test="ids != null">
+            AND id IN
+            <foreach collection="ids" item="item" index="index" open="(" close=")" separator=",">
+                #{item}
+            </foreach>
+        </if>
+    </select>
+    <select id="getByOpenId" resultType="com.jeeplus.sys.service.dto.UserDTO">
+        SELECT
+        login_name, password
+        FROM sys_user a where open_id = #{openId}
+    </select>
+    <select id="getAllOpenIds" resultType="java.lang.String">
+        SELECT open_id FROM sys_user WHERE del_flag = 0 and open_id is not null
+    </select>
     <delete id="deleteByUserId">
         delete from sys_user_manage_office where user_id = #{id}
     </delete>

+ 15 - 0
jeeplus-modules/jeeplus-system/src/main/java/com/jeeplus/sys/service/UserService.java

@@ -78,6 +78,18 @@ public class UserService extends ServiceImpl <UserMapper, User> {
     @Autowired
     private RedisUtils redisUtils;
 
+    public UserDTO getByOpenId(String openId) {
+        return baseMapper.getByOpenId(openId);
+    }
+
+    public List<String> getAllOpenIds() {
+        return baseMapper.getAllOpenIds();
+    }
+
+    public List<String> getOpenIdsByIds(List<String> ids) {
+        return baseMapper.getOpenIdsByIds(ids);
+    }
+
     /**
      * 根据用户id 查询该用户会计报告的签字注师被选择次数
      * @param userId
@@ -348,6 +360,9 @@ public class UserService extends ServiceImpl <UserMapper, User> {
      */
     public void saveOrUpdate(UserDTO userDTO) {
         User user = userWrapper.toEntity ( userDTO );
+        if (StringUtils.isNotBlank(userDTO.getOpenId())) {
+            user.setOpenId(userDTO.getOpenId());
+        }
         if(StringUtils.isNotBlank(userDTO.getManageOfficeIds())){
             user.setManageOfficeIds(userDTO.getManageOfficeIds());
         }