Browse Source

企业微信回调

lock-dev
wangwei_123 2 days ago
parent
commit
7df74a7613
  1. 81
      cc-admin-master/dependencies/pom.xml
  2. 10
      cc-admin-master/yudao-module-lock/pom.xml
  3. 65
      cc-admin-master/yudao-module-lock/src/main/java/cn/iocoder/yudao/module/lock/controller/admin/JsSdkController.java
  4. 3
      cc-admin-master/yudao-module-lock/src/main/java/cn/iocoder/yudao/module/lock/controller/admin/LockController.java
  5. 16
      cc-admin-master/yudao-module-lock/src/main/java/cn/iocoder/yudao/module/lock/controller/admin/PlanController.java
  6. 1
      cc-admin-master/yudao-module-lock/src/main/java/cn/iocoder/yudao/module/lock/dal/PlanDO.java
  7. 21
      cc-admin-master/yudao-module-lock/src/main/java/cn/iocoder/yudao/module/lock/dal/entity/PlanEntity.java
  8. 36
      cc-admin-master/yudao-module-lock/src/main/java/cn/iocoder/yudao/module/lock/dal/entity/PlanItemDetailEntity.java
  9. 52
      cc-admin-master/yudao-module-lock/src/main/java/cn/iocoder/yudao/module/lock/dal/entity/PlanItemEntity.java
  10. 48
      cc-admin-master/yudao-module-lock/src/main/java/cn/iocoder/yudao/module/lock/dal/entity/PlanLifeLockEntity.java
  11. 3
      cc-admin-master/yudao-module-lock/src/main/java/cn/iocoder/yudao/module/lock/service/PlanService.java
  12. 103
      cc-admin-master/yudao-module-lock/src/main/java/cn/iocoder/yudao/module/lock/service/impl/PlanServiceImpl.java
  13. 16
      cc-admin-master/yudao-module-lock/src/main/java/cn/iocoder/yudao/module/lock/vo/PlanSelectVo.java
  14. 19
      cc-admin-master/yudao-module-system/pom.xml
  15. 84
      cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/social/SocialClientApi.java
  16. 107
      cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/social/SocialClientApiImpl.java
  17. 54
      cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/social/SocialUserApi.java
  18. 44
      cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/social/SocialUserApiImpl.java
  19. 51
      cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialUserBindReqDTO.java
  20. 35
      cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialUserRespDTO.java
  21. 47
      cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialUserUnbindReqDTO.java
  22. 34
      cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialWxJsapiSignatureRespDTO.java
  23. 27
      cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialWxPhoneNumberInfoRespDTO.java
  24. 66
      cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialWxQrcodeReqDTO.java
  25. 30
      cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialWxaOrderNotifyConfirmReceiveReqDTO.java
  26. 67
      cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialWxaOrderUploadShippingInfoReqDTO.java
  27. 61
      cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialWxaSubscribeMessageSendReqDTO.java
  28. 42
      cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialWxaSubscribeTemplateRespDTO.java
  29. 22
      cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.java
  30. 20
      cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/SocialClientController.http
  31. 94
      cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/SocialClientController.java
  32. 82
      cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/SocialUserController.java
  33. 30
      cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/vo/client/SocialClientPageReqVO.java
  34. 39
      cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/vo/client/SocialClientRespVO.java
  35. 61
      cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/vo/client/SocialClientSaveReqVO.java
  36. 34
      cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/vo/user/SocialUserBindReqVO.java
  37. 33
      cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/vo/user/SocialUserPageReqVO.java
  38. 48
      cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/vo/user/SocialUserRespVO.java
  39. 30
      cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/vo/user/SocialUserUnbindReqVO.java
  40. 2
      cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/TenantController.java
  41. 76
      cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/social/SocialClientDO.java
  42. 56
      cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/social/SocialUserBindDO.java
  43. 73
      cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/social/SocialUserDO.java
  44. 28
      cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/social/SocialClientMapper.java
  45. 44
      cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/social/SocialUserBindMapper.java
  46. 33
      cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/social/SocialUserMapper.java
  47. 39
      cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/justauth/config/YudaoJustAuthConfiguration.java
  48. 322
      cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/justauth/core/AuthRequestFactory.java
  49. 6
      cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/justauth/package-info.java
  50. 8
      cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthService.java
  51. 23
      cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java
  52. 160
      cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialClientService.java
  53. 510
      cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialClientServiceImpl.java
  54. 89
      cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialUserService.java
  55. 173
      cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialUserServiceImpl.java
  56. 10
      cc-admin-master/yudao-server/src/main/resources/application-local.yaml

81
cc-admin-master/dependencies/pom.xml

@ -14,7 +14,7 @@
<url>https://github.com/YunaiV/ruoyi-vue-pro</url> <url>https://github.com/YunaiV/ruoyi-vue-pro</url>
<properties> <properties>
<revision>2.6.1-SNAPSHOT</revision>
<revision>2025.08-SNAPSHOT</revision>
<flatten-maven-plugin.version>1.6.0</flatten-maven-plugin.version> <flatten-maven-plugin.version>1.6.0</flatten-maven-plugin.version>
<!-- 统一依赖管理 --> <!-- 统一依赖管理 -->
<spring.boot.version>3.4.5</spring.boot.version> <spring.boot.version>3.4.5</spring.boot.version>
@ -24,9 +24,9 @@
<!-- DB 相关 --> <!-- DB 相关 -->
<druid.version>1.2.24</druid.version> <druid.version>1.2.24</druid.version>
<mybatis.version>3.5.19</mybatis.version> <mybatis.version>3.5.19</mybatis.version>
<mybatis-plus.version>3.5.10.1</mybatis-plus.version>
<mybatis-plus.version>3.5.12</mybatis-plus.version>
<mybatis-plus-join.version>1.5.4</mybatis-plus-join.version>
<dynamic-datasource.version>4.3.1</dynamic-datasource.version> <dynamic-datasource.version>4.3.1</dynamic-datasource.version>
<mybatis-plus-join.version>1.4.13</mybatis-plus-join.version>
<easy-trans.version>3.0.6</easy-trans.version> <easy-trans.version>3.0.6</easy-trans.version>
<redisson.version>3.41.0</redisson.version> <redisson.version>3.41.0</redisson.version>
<dm8.jdbc.version>8.1.3.140</dm8.jdbc.version> <dm8.jdbc.version>8.1.3.140</dm8.jdbc.version>
@ -54,7 +54,7 @@
<mapstruct.version>1.6.3</mapstruct.version> <mapstruct.version>1.6.3</mapstruct.version>
<hutool-5.version>5.8.35</hutool-5.version> <hutool-5.version>5.8.35</hutool-5.version>
<hutool-6.version>6.0.0-M19</hutool-6.version> <hutool-6.version>6.0.0-M19</hutool-6.version>
<easyexcel.version>4.0.3</easyexcel.version>
<fastexcel.version>1.2.0</fastexcel.version>
<velocity.version>2.4.1</velocity.version> <velocity.version>2.4.1</velocity.version>
<fastjson.version>1.2.83</fastjson.version> <fastjson.version>1.2.83</fastjson.version>
<guava.version>33.4.8-jre</guava.version> <guava.version>33.4.8-jre</guava.version>
@ -69,18 +69,12 @@
<pf4j-spring.version>0.9.0</pf4j-spring.version> <pf4j-spring.version>0.9.0</pf4j-spring.version>
<vertx.version>4.5.13</vertx.version> <vertx.version>4.5.13</vertx.version>
<!-- 三方云服务相关 --> <!-- 三方云服务相关 -->
<commons-io.version>2.17.0</commons-io.version>
<commons-compress.version>1.27.1</commons-compress.version>
<awssdk.version>2.30.14</awssdk.version> <awssdk.version>2.30.14</awssdk.version>
<!--
<justauth.version>1.16.7</justauth.version> <justauth.version>1.16.7</justauth.version>
-->
<justauth-starter.version>1.4.0</justauth-starter.version> <justauth-starter.version>1.4.0</justauth-starter.version>
<jimureport.version>2.0.0</jimureport.version> <jimureport.version>2.0.0</jimureport.version>
<jimubi.version>1.9.5</jimubi.version> <jimubi.version>1.9.5</jimubi.version>
<!--
<weixin-java.version>4.7.5.B</weixin-java.version> <weixin-java.version>4.7.5.B</weixin-java.version>
-->
</properties> </properties>
<dependencyManagement> <dependencyManagement>
@ -255,23 +249,23 @@
</exclusions> </exclusions>
</dependency> </dependency>
<!-- <dependency>
<dependency>
<groupId>com.dameng</groupId> <groupId>com.dameng</groupId>
<artifactId>DmJdbcDriver18</artifactId> <artifactId>DmJdbcDriver18</artifactId>
<version>${dm8.jdbc.version}</version> <version>${dm8.jdbc.version}</version>
</dependency>-->
<!--
</dependency>
<dependency> <dependency>
<groupId>org.opengauss</groupId> <groupId>org.opengauss</groupId>
<artifactId>opengauss-jdbc</artifactId> <artifactId>opengauss-jdbc</artifactId>
<version>${opengauss.jdbc.version}</version> <version>${opengauss.jdbc.version}</version>
</dependency>-->
<!--
</dependency>
<dependency> <dependency>
<groupId>cn.com.kingbase</groupId> <groupId>cn.com.kingbase</groupId>
<artifactId>kingbase8</artifactId> <artifactId>kingbase8</artifactId>
<version>${kingbase.jdbc.version}</version> <version>${kingbase.jdbc.version}</version>
</dependency>-->
</dependency>
<dependency> <dependency>
<groupId>com.taosdata.jdbc</groupId> <groupId>com.taosdata.jdbc</groupId>
@ -412,6 +406,18 @@
</exclusions> </exclusions>
</dependency> </dependency>
<dependency>
<groupId>com.github.fppt</groupId> <!-- 单元测试,我们采用内嵌的 Redis 数据库 -->
<artifactId>jedis-mock</artifactId>
<version>${jedis-mock.version}</version>
</dependency>
<dependency>
<groupId>uk.co.jemos.podam</groupId> <!-- 单元测试,随机生成 POJO 类 -->
<artifactId>podam</artifactId>
<version>${podam.version}</version>
</dependency>
<!-- 工作流相关 --> <!-- 工作流相关 -->
<dependency> <dependency>
<groupId>org.flowable</groupId> <groupId>org.flowable</groupId>
@ -443,17 +449,17 @@
<artifactId>lombok</artifactId> <artifactId>lombok</artifactId>
<version>${lombok.version}</version> <version>${lombok.version}</version>
</dependency> </dependency>
<!--
<dependency> <dependency>
<groupId>org.mapstruct</groupId> <groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId> &lt;!&ndash; use mapstruct-jdk8 for Java 8 or higher &ndash;&gt;
<artifactId>mapstruct</artifactId> <!-- use mapstruct-jdk8 for Java 8 or higher -->
<version>${mapstruct.version}</version> <version>${mapstruct.version}</version>
</dependency>-->
<!-- <dependency>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId> <groupId>org.mapstruct</groupId>
<artifactId>mapstruct-jdk8</artifactId> <artifactId>mapstruct-jdk8</artifactId>
<version>${mapstruct.version}</version> <version>${mapstruct.version}</version>
</dependency>-->
</dependency>
<dependency> <dependency>
<groupId>org.mapstruct</groupId> <groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId> <artifactId>mapstruct-processor</artifactId>
@ -472,20 +478,11 @@
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>${easyexcel.version}</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>${commons-io.version}</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-compress</artifactId>
<version>${commons-compress.version}</version>
<groupId>cn.idev.excel</groupId>
<artifactId>fastexcel</artifactId>
<version>${fastexcel.version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.apache.tika</groupId> <groupId>org.apache.tika</groupId>
<artifactId>tika-core</artifactId> <!-- 文件类型的识别 --> <artifactId>tika-core</artifactId> <!-- 文件类型的识别 -->
@ -553,25 +550,25 @@
<version>${awssdk.version}</version> <version>${awssdk.version}</version>
</dependency> </dependency>
<!-- <dependency>
<dependency>
<groupId>me.zhyd.oauth</groupId> <groupId>me.zhyd.oauth</groupId>
<artifactId>JustAuth</artifactId> &lt;!&ndash; 社交登陆(例如说,个人微信、企业微信等等) &ndash;&gt;
<artifactId>JustAuth</artifactId> <!-- 社交登陆(例如说,个人微信、企业微信等等) -->
<version>${justauth.version}</version> <version>${justauth.version}</version>
</dependency>&ndash;&gt;-->
<!-- <dependency>
</dependency>
<dependency>
<groupId>com.xkcoding.justauth</groupId> <groupId>com.xkcoding.justauth</groupId>
<artifactId>justauth-spring-boot-starter</artifactId> <artifactId>justauth-spring-boot-starter</artifactId>
<version>${justauth-starter.version}</version> <version>${justauth-starter.version}</version>
<exclusions> <exclusions>
&lt;!&ndash; 移除,避免和项目里的 hutool-all 冲突 &ndash;&gt;
<!-- 移除,避免和项目里的 hutool-all 冲突 -->
<exclusion> <exclusion>
<groupId>cn.hutool</groupId> <groupId>cn.hutool</groupId>
<artifactId>hutool-core</artifactId> <artifactId>hutool-core</artifactId>
</exclusion> </exclusion>
</exclusions> </exclusions>
</dependency>-->
</dependency>
<!-- <dependency>
<dependency>
<groupId>com.github.binarywang</groupId> <groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-pay</artifactId> <artifactId>weixin-java-pay</artifactId>
<version>${weixin-java.version}</version> <version>${weixin-java.version}</version>
@ -585,7 +582,7 @@
<groupId>com.github.binarywang</groupId> <groupId>com.github.binarywang</groupId>
<artifactId>wx-java-miniapp-spring-boot-starter</artifactId> <artifactId>wx-java-miniapp-spring-boot-starter</artifactId>
<version>${weixin-java.version}</version> <version>${weixin-java.version}</version>
</dependency>-->
</dependency>
<!-- 积木报表--> <!-- 积木报表-->
<dependency> <dependency>

10
cc-admin-master/yudao-module-lock/pom.xml

@ -77,7 +77,15 @@
<groupId>org.dromara.hutool</groupId> <groupId>org.dromara.hutool</groupId>
<artifactId>hutool-extra</artifactId> <!-- 邮件 --> <artifactId>hutool-extra</artifactId> <!-- 邮件 -->
</dependency> </dependency>
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>wx-java-mp-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>wx-java-cp-spring-boot-starter</artifactId>
<version>4.6.0</version>
</dependency>
</dependencies> </dependencies>
</project> </project>

65
cc-admin-master/yudao-module-lock/src/main/java/cn/iocoder/yudao/module/lock/controller/admin/JsSdkController.java

@ -0,0 +1,65 @@
package cn.iocoder.yudao.module.lock.controller.admin;
import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission;
import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.security.PermitAll;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.bean.WxJsapiSignature;
import me.chanjar.weixin.cp.bean.WxCpAgentJsapiSignature;
import me.chanjar.weixin.mp.api.WxMpService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import me.chanjar.weixin.cp.api.WxCpService;
import java.net.http.HttpClient;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Formatter;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
@Tag(name = "企业微信回调")
@RestController
@RequestMapping("/js/weixin")
@Slf4j
public class JsSdkController {
@Autowired
private WxCpService wxCpService;
@GetMapping("/getConfigSignature")
@PermitAll
@TenantIgnore
public WxJsapiSignature getConfigSignature(@RequestParam(required = false) String url) {
try {
return wxCpService.createJsapiSignature(url);
} catch (Exception e) {
log.error("[getConfigSignature][url({}) 发生异常]", url, e);
}
return null;
}
@GetMapping("/getAgentConfigSignature")
@PermitAll
@TenantIgnore
public WxCpAgentJsapiSignature getAgentConfigSignature(@RequestParam(required = false) String url) {
try {
return wxCpService.createAgentJsapiSignature(url);
} catch (Exception e) {
log.error("[getConfigSignature][url({}) 发生异常]", url, e);
}
return null;
}
}

3
cc-admin-master/yudao-module-lock/src/main/java/cn/iocoder/yudao/module/lock/controller/admin/LockController.java

@ -87,8 +87,7 @@ public class LockController {
@GetMapping("/page") @GetMapping("/page")
@Operation(summary = "获得电子锁分页") @Operation(summary = "获得电子锁分页")
//@PreAuthorize("@ss.hasPermission('electron:lock:query')")
@PermitAll
@PreAuthorize("@ss.hasPermission('electron:lock:query')")
public CommonResult<PageResult<LockRespVO>> getLockPage(@Valid LockPageReqVO pageReqVO) { public CommonResult<PageResult<LockRespVO>> getLockPage(@Valid LockPageReqVO pageReqVO) {
PageResult<LockDO> pageResult = lockService.getLockPage(pageReqVO); PageResult<LockDO> pageResult = lockService.getLockPage(pageReqVO);
return success(BeanUtils.toBean(pageResult, LockRespVO.class)); return success(BeanUtils.toBean(pageResult, LockRespVO.class));

16
cc-admin-master/yudao-module-lock/src/main/java/cn/iocoder/yudao/module/lock/controller/admin/PlanController.java

@ -1,10 +1,16 @@
package cn.iocoder.yudao.module.lock.controller.admin; package cn.iocoder.yudao.module.lock.controller.admin;
import cn.iocoder.yudao.module.lock.dal.PlanDO; import cn.iocoder.yudao.module.lock.dal.PlanDO;
import cn.iocoder.yudao.module.lock.dal.entity.PlanEntity;
import cn.iocoder.yudao.module.lock.service.PlanItemDetailService;
import cn.iocoder.yudao.module.lock.service.PlanItemService;
import cn.iocoder.yudao.module.lock.service.PlanLifeLockService;
import cn.iocoder.yudao.module.lock.service.PlanService; import cn.iocoder.yudao.module.lock.service.PlanService;
import cn.iocoder.yudao.module.lock.vo.PlanPageReqVO; import cn.iocoder.yudao.module.lock.vo.PlanPageReqVO;
import cn.iocoder.yudao.module.lock.vo.PlanRespVO; import cn.iocoder.yudao.module.lock.vo.PlanRespVO;
import cn.iocoder.yudao.module.lock.vo.PlanSaveReqVO; import cn.iocoder.yudao.module.lock.vo.PlanSaveReqVO;
import cn.iocoder.yudao.module.lock.vo.PlanSelectVo;
import jakarta.annotation.security.PermitAll;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
@ -86,6 +92,8 @@ public class PlanController {
@PreAuthorize("@ss.hasPermission('isolation:plan:query')") @PreAuthorize("@ss.hasPermission('isolation:plan:query')")
public CommonResult<PageResult<PlanRespVO>> getPlanPage(@Valid PlanPageReqVO pageReqVO) { public CommonResult<PageResult<PlanRespVO>> getPlanPage(@Valid PlanPageReqVO pageReqVO) {
PageResult<PlanDO> pageResult = planService.getPlanPage(pageReqVO); PageResult<PlanDO> pageResult = planService.getPlanPage(pageReqVO);
return success(BeanUtils.toBean(pageResult, PlanRespVO.class)); return success(BeanUtils.toBean(pageResult, PlanRespVO.class));
} }
@ -102,4 +110,12 @@ public class PlanController {
BeanUtils.toBean(list, PlanRespVO.class)); BeanUtils.toBean(list, PlanRespVO.class));
} }
@GetMapping("/planListAll")
@Operation(summary = "获得所有隔离计划")
@PermitAll
public CommonResult<List<PlanEntity>> getPlanListAll(@Valid PlanSelectVo pageReqVO) {
return success(planService.listAll(pageReqVO));
}
} }

1
cc-admin-master/yudao-module-lock/src/main/java/cn/iocoder/yudao/module/lock/dal/PlanDO.java

@ -32,5 +32,4 @@ public class PlanDO extends BaseDO {
*/ */
private String ipName; private String ipName;
} }

21
cc-admin-master/yudao-module-lock/src/main/java/cn/iocoder/yudao/module/lock/dal/entity/PlanEntity.java

@ -0,0 +1,21 @@
package cn.iocoder.yudao.module.lock.dal.entity;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
import java.util.List;
@Data
public class PlanEntity {
@TableId
private Long id;
/**
* 计划名称
*/
private String ipName;
private List<PlanItemEntity> planItemEntityList;
}

36
cc-admin-master/yudao-module-lock/src/main/java/cn/iocoder/yudao/module/lock/dal/entity/PlanItemDetailEntity.java

@ -0,0 +1,36 @@
package cn.iocoder.yudao.module.lock.dal.entity;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
import java.util.List;
/**
* 隔离计划子项详情 DO
*
* @author 超级管理员
*/
@Data
public class PlanItemDetailEntity {
@TableId
private Long id;
/**
* 隔离计划子项ID
*/
private Long isolationPlanItemId;
/**
* 隔离点ID
*/
private Long isolationPointId;
/**
* 电子锁ID
*/
private Long lockId;
/**
* 锁状态: 0=未上锁, 1=已上锁, 2=已解锁
*/
private Boolean lockStatus;
private List<PlanLifeLockEntity> lockEntities;
}

52
cc-admin-master/yudao-module-lock/src/main/java/cn/iocoder/yudao/module/lock/dal/entity/PlanItemEntity.java

@ -0,0 +1,52 @@
package cn.iocoder.yudao.module.lock.dal.entity;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
import java.util.List;
/**
* 隔离计划子项 DO
*
* @author 超级管理员
*/
@Data
public class PlanItemEntity {
@TableId
private Long id;
/**
* 隔离计划ID
*/
private Long isolationPlanId;
/**
* 隔离指导书ID
*/
private Long guideId;
/**
* 集中挂牌人ID
*/
private Long operatorId;
/**
* 集中挂牌协助人ID
*/
private Long operatorHelperId;
/**
* 验证人ID
*/
private Long verifierId;
/**
* 验证协助人ID
*/
private Long verifierHelperId;
/**
* 子项状态: 0=未完成, 1=已完成
*/
private Boolean status;
private List<PlanItemDetailEntity> planItemDetailEntityList;
}

48
cc-admin-master/yudao-module-lock/src/main/java/cn/iocoder/yudao/module/lock/dal/entity/PlanLifeLockEntity.java

@ -0,0 +1,48 @@
package cn.iocoder.yudao.module.lock.dal.entity;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
import java.time.LocalDateTime;
/**
* 个人生命锁 DO
*
* @author 超级管理员
*/
@Data
public class PlanLifeLockEntity {
@TableId
private Long id;
/**
* 子项详情ID
*/
private Long isolationPlanItemDetailId;
/**
* 上锁人ID
*/
private Long userId;
/**
* 生命锁类型
*/
private String lockType;
/**
* 锁定状态: 0=未上锁, 1=已上锁
*/
private Boolean lockStatus;
/**
* 上锁时间
*/
private LocalDateTime lockTime;
/**
* 解锁时间
*/
private LocalDateTime unlockTime;
}

3
cc-admin-master/yudao-module-lock/src/main/java/cn/iocoder/yudao/module/lock/service/PlanService.java

@ -3,8 +3,10 @@ package cn.iocoder.yudao.module.lock.service;
import java.util.*; import java.util.*;
import cn.iocoder.yudao.module.lock.dal.PlanDO; import cn.iocoder.yudao.module.lock.dal.PlanDO;
import cn.iocoder.yudao.module.lock.dal.entity.PlanEntity;
import cn.iocoder.yudao.module.lock.vo.PlanPageReqVO; import cn.iocoder.yudao.module.lock.vo.PlanPageReqVO;
import cn.iocoder.yudao.module.lock.vo.PlanSaveReqVO; import cn.iocoder.yudao.module.lock.vo.PlanSaveReqVO;
import cn.iocoder.yudao.module.lock.vo.PlanSelectVo;
import jakarta.validation.*; import jakarta.validation.*;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
@ -60,4 +62,5 @@ public interface PlanService {
*/ */
PageResult<PlanDO> getPlanPage(PlanPageReqVO pageReqVO); PageResult<PlanDO> getPlanPage(PlanPageReqVO pageReqVO);
List<PlanEntity> listAll(PlanSelectVo pageReqVO);
} }

103
cc-admin-master/yudao-module-lock/src/main/java/cn/iocoder/yudao/module/lock/service/impl/PlanServiceImpl.java

@ -1,23 +1,44 @@
package cn.iocoder.yudao.module.lock.service.impl; package cn.iocoder.yudao.module.lock.service.impl;
import cn.hutool.core.collection.CollectionUtil;
import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil; import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
import cn.iocoder.yudao.module.lock.dal.PlanDO; import cn.iocoder.yudao.module.lock.dal.PlanDO;
import cn.iocoder.yudao.module.lock.dal.PlanItemDO;
import cn.iocoder.yudao.module.lock.dal.PlanItemDetailDO;
import cn.iocoder.yudao.module.lock.dal.PlanLifeLockDO;
import cn.iocoder.yudao.module.lock.dal.entity.PlanEntity;
import cn.iocoder.yudao.module.lock.dal.entity.PlanItemDetailEntity;
import cn.iocoder.yudao.module.lock.dal.entity.PlanItemEntity;
import cn.iocoder.yudao.module.lock.dal.entity.PlanLifeLockEntity;
import cn.iocoder.yudao.module.lock.enums.ErrorCodeConstants; import cn.iocoder.yudao.module.lock.enums.ErrorCodeConstants;
import cn.iocoder.yudao.module.lock.mapper.PlanItemDetailMapper;
import cn.iocoder.yudao.module.lock.mapper.PlanItemMapper;
import cn.iocoder.yudao.module.lock.mapper.PlanLifeLockMapper;
import cn.iocoder.yudao.module.lock.mapper.PlanMapper; import cn.iocoder.yudao.module.lock.mapper.PlanMapper;
import cn.iocoder.yudao.module.lock.service.PlanItemDetailService;
import cn.iocoder.yudao.module.lock.service.PlanItemService;
import cn.iocoder.yudao.module.lock.service.PlanLifeLockService;
import cn.iocoder.yudao.module.lock.service.PlanService; import cn.iocoder.yudao.module.lock.service.PlanService;
import cn.iocoder.yudao.module.lock.vo.PlanPageReqVO; import cn.iocoder.yudao.module.lock.vo.PlanPageReqVO;
import cn.iocoder.yudao.module.lock.vo.PlanSaveReqVO; import cn.iocoder.yudao.module.lock.vo.PlanSaveReqVO;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.lock.vo.PlanSelectVo;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import java.util.*; import java.util.*;
import java.util.stream.Collectors;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static java.util.stream.Collectors.groupingBy;
/** /**
* 隔离计划 Service 实现类 * 隔离计划 Service 实现类
@ -30,6 +51,12 @@ public class PlanServiceImpl implements PlanService {
@Resource @Resource
private PlanMapper planMapper; private PlanMapper planMapper;
@Resource
private PlanItemMapper planItemMapper;
@Resource
private PlanLifeLockMapper planLifeLockMapper;
@Resource
private PlanItemDetailMapper planItemDetailMapper;
@Override @Override
public Long createPlan(PlanSaveReqVO createReqVO) { public Long createPlan(PlanSaveReqVO createReqVO) {
@ -59,10 +86,10 @@ public class PlanServiceImpl implements PlanService {
} }
@Override @Override
public void deletePlanListByIds(List<Long> ids) {
public void deletePlanListByIds(List<Long> ids) {
// 删除 // 删除
planMapper.deleteByIds(ids); planMapper.deleteByIds(ids);
}
}
private void validatePlanExists(Long id) { private void validatePlanExists(Long id) {
@ -81,4 +108,76 @@ public class PlanServiceImpl implements PlanService {
return planMapper.selectPage(pageReqVO); return planMapper.selectPage(pageReqVO);
} }
@Override
public List<PlanEntity> listAll(PlanSelectVo pageReqVO) {
// 1. 查询顶层 Plan
var queryWrapper = new QueryWrapper<PlanDO>();
if (StringUtils.isNotBlank(pageReqVO.getIpName())) {
queryWrapper.like("ip_name", pageReqVO.getIpName());
}
List<PlanDO> planDOS = planMapper.selectList(queryWrapper);
if (CollectionUtil.isEmpty(planDOS)) {
return new ArrayList<>();
}
// --- 核心优化:使用通用方法批量查询所有子层级 ---
var planIds = planDOS.stream().map(PlanDO::getId).toList();
// 批量查询所有 PlanItem
List<PlanItemDO> planItemDOS = selectInBatch(planIds, "isolation_plan_id", planItemMapper);
var planItemIds = planItemDOS.stream().map(PlanItemDO::getId).toList();
// 批量查询所有 PlanItemDetail
List<PlanItemDetailDO> planItemDetailDOS = selectInBatch(planItemIds, "isolation_plan_item_id", planItemDetailMapper);
var planItemDetailIds = planItemDetailDOS.stream().map(PlanItemDetailDO::getId).toList();
// 批量查询所有 PlanLifeLock
List<PlanLifeLockDO> planLifeLockDOS = selectInBatch(planItemDetailIds, "isolation_plan_item_detail_id", planLifeLockMapper);
// --- 高效组装:一次性转换并使用 Map 关联数据 ---
// 1. 将所有查询到的 DO 列表转换为 Entity 列表
List<PlanEntity> planEntities = BeanUtils.toBean(planDOS, PlanEntity.class);
List<PlanItemEntity> itemEntities = BeanUtils.toBean(planItemDOS, PlanItemEntity.class);
List<PlanItemDetailEntity> detailEntities = BeanUtils.toBean(planItemDetailDOS, PlanItemDetailEntity.class);
List<PlanLifeLockEntity> lockEntities = BeanUtils.toBean(planLifeLockDOS, PlanLifeLockEntity.class);
// 2. 将子 Entity 列表转换为以其父ID为键的 Map,用于 O(1) 快速查找
Map<Long, List<PlanLifeLockEntity>> locksByDetailId = lockEntities.stream()
.collect(groupingBy(PlanLifeLockEntity::getIsolationPlanItemDetailId));
Map<Long, List<PlanItemDetailEntity>> detailsByItemId = detailEntities.stream()
.collect(groupingBy(PlanItemDetailEntity::getIsolationPlanItemId));
Map<Long, List<PlanItemEntity>> itemsByPlanId = itemEntities.stream()
.collect(groupingBy(PlanItemEntity::getIsolationPlanId));
// 3. 遍历顶层列表,高效地组装整个对象树
planEntities.forEach(plan -> {
List<PlanItemEntity> items = itemsByPlanId.getOrDefault(plan.getId(), Collections.emptyList());
items.forEach(item -> {
List<PlanItemDetailEntity> details = detailsByItemId.getOrDefault(item.getId(), Collections.emptyList());
details.forEach(detail ->
detail.setLockEntities(locksByDetailId.getOrDefault(detail.getId(), Collections.emptyList()))
);
item.setPlanItemDetailEntityList(details);
});
plan.setPlanItemEntityList(items);
});
return planEntities;
}
/**
* 抽取出的通用方法根据父ID列表批量查询子记录
* @param ids 父ID列表
* @param columnName "in" 查询的列名
* @param mapper 对应的 MyBatis Mapper
* @return 查询到的子记录列表
*/
private <T, M extends BaseMapper<T>> List<T> selectInBatch(List<Long> ids, String columnName, M mapper) {
if (CollectionUtil.isEmpty(ids)) {
return new ArrayList<>();
}
return mapper.selectList(new QueryWrapper<T>().in(columnName, ids));
}
} }

16
cc-admin-master/yudao-module-lock/src/main/java/cn/iocoder/yudao/module/lock/vo/PlanSelectVo.java

@ -0,0 +1,16 @@
package cn.iocoder.yudao.module.lock.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotEmpty;
import lombok.Data;
@Schema(description = "管理后台 - 隔离计划查询")
@Data
public class PlanSelectVo {
@Schema(description = "计划名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "张三")
@NotEmpty(message = "计划名称不能为空")
private String ipName;
}

19
cc-admin-master/yudao-module-system/pom.xml

@ -91,23 +91,24 @@
</dependency> </dependency>
<!-- 三方云服务相关 --> <!-- 三方云服务相关 -->
<!-- <dependency>
<dependency>
<groupId>me.zhyd.oauth</groupId> <groupId>me.zhyd.oauth</groupId>
<artifactId>JustAuth</artifactId> &lt;!&ndash; 社交登陆(例如说,个人微信、企业微信等等) &ndash;&gt;
</dependency>-->
<!-- <dependency>
<artifactId>JustAuth</artifactId> <!-- 社交登陆(例如说,个人微信、企业微信等等) ;-->
</dependency>
<dependency>
<groupId>com.xkcoding.justauth</groupId> <groupId>com.xkcoding.justauth</groupId>
<artifactId>justauth-spring-boot-starter</artifactId> <artifactId>justauth-spring-boot-starter</artifactId>
</dependency>-->
</dependency>
<!-- <dependency>
<dependency>
<groupId>com.github.binarywang</groupId> <groupId>com.github.binarywang</groupId>
<artifactId>wx-java-mp-spring-boot-starter</artifactId> &lt;!&ndash; 微信登录(公众号) &ndash;&gt;
<artifactId>wx-java-mp-spring-boot-starter</artifactId> <!--微信登录(公众号)-->
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.github.binarywang</groupId> <groupId>com.github.binarywang</groupId>
<artifactId>wx-java-miniapp-spring-boot-starter</artifactId> &lt;!&ndash; 微信登录(小程序) &ndash;&gt;
</dependency>-->
<artifactId>wx-java-miniapp-spring-boot-starter</artifactId><!--微信登录(小程序) -->
</dependency>
<dependency> <dependency>
<groupId>com.anji-plus</groupId> <groupId>com.anji-plus</groupId>

84
cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/social/SocialClientApi.java

@ -0,0 +1,84 @@
package cn.iocoder.yudao.module.system.api.social;
import cn.iocoder.yudao.module.system.api.social.dto.*;
import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum;
import jakarta.validation.Valid;
import java.util.List;
/**
* 社交应用的 API 接口
*
* @author 芋道源码
*/
public interface SocialClientApi {
/**
* 获得社交平台的授权 URL
*
* @param socialType 社交平台的类型 {@link SocialTypeEnum}
* @param userType 用户类型
* @param redirectUri 重定向 URL
* @return 社交平台的授权 URL
*/
String getAuthorizeUrl(Integer socialType, Integer userType, String redirectUri);
/**
* 创建微信公众号 JS SDK 初始化所需的签名
*
* @param userType 用户类型
* @param url 访问的 URL 地址
* @return 签名
*/
SocialWxJsapiSignatureRespDTO createWxMpJsapiSignature(Integer userType, String url);
//======================= 微信小程序独有 =======================
/**
* 获得微信小程序的手机信息
*
* @param userType 用户类型
* @param phoneCode 手机授权码
* @return 手机信息
*/
SocialWxPhoneNumberInfoRespDTO getWxMaPhoneNumberInfo(Integer userType, String phoneCode);
/**
* 获得小程序二维码
*
* @param reqVO 请求信息
* @return 小程序二维码
*/
byte[] getWxaQrcode(@Valid SocialWxQrcodeReqDTO reqVO);
/**
* 获得微信小程订阅模板
*
* @return 小程序订阅消息模版
*/
List<SocialWxaSubscribeTemplateRespDTO> getWxaSubscribeTemplateList(Integer userType);
/**
* 发送微信小程序订阅消息
*
* @param reqDTO 请求
*/
void sendWxaSubscribeMessage(SocialWxaSubscribeMessageSendReqDTO reqDTO);
/**
* 上传订单发货到微信小程序
*
* @param userType 用户类型
* @param reqDTO 请求
*/
void uploadWxaOrderShippingInfo(Integer userType, SocialWxaOrderUploadShippingInfoReqDTO reqDTO);
/**
* 通知订单收货到微信小程序
*
* @param userType 用户类型
* @param reqDTO 请求
*/
void notifyWxaOrderConfirmReceive(Integer userType, SocialWxaOrderNotifyConfirmReceiveReqDTO reqDTO);
}

107
cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/social/SocialClientApiImpl.java

@ -0,0 +1,107 @@
package cn.iocoder.yudao.module.system.api.social;
import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.system.api.social.dto.*;
import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum;
import cn.iocoder.yudao.module.system.service.social.SocialClientService;
import cn.iocoder.yudao.module.system.service.social.SocialUserService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.bean.WxJsapiSignature;
import me.chanjar.weixin.common.bean.subscribemsg.TemplateInfo;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import java.util.List;
import static cn.hutool.core.collection.CollUtil.findOne;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
/**
* 社交应用的 API 实现类
*
* @author 芋道源码
*/
@Service
@Validated
@Slf4j
public class SocialClientApiImpl implements SocialClientApi {
@Resource
private SocialClientService socialClientService;
@Resource
private SocialUserService socialUserService;
@Override
public String getAuthorizeUrl(Integer socialType, Integer userType, String redirectUri) {
return socialClientService.getAuthorizeUrl(socialType, userType, redirectUri);
}
@Override
public SocialWxJsapiSignatureRespDTO createWxMpJsapiSignature(Integer userType, String url) {
WxJsapiSignature signature = socialClientService.createWxMpJsapiSignature(userType, url);
return BeanUtils.toBean(signature, SocialWxJsapiSignatureRespDTO.class);
}
//======================= 微信小程序独有 =======================
@Override
public SocialWxPhoneNumberInfoRespDTO getWxMaPhoneNumberInfo(Integer userType, String phoneCode) {
WxMaPhoneNumberInfo info = socialClientService.getWxMaPhoneNumberInfo(userType, phoneCode);
return BeanUtils.toBean(info, SocialWxPhoneNumberInfoRespDTO.class);
}
@Override
public byte[] getWxaQrcode(SocialWxQrcodeReqDTO reqVO) {
return socialClientService.getWxaQrcode(reqVO);
}
@Override
public List<SocialWxaSubscribeTemplateRespDTO> getWxaSubscribeTemplateList(Integer userType) {
List<TemplateInfo> list = socialClientService.getSubscribeTemplateList(userType);
return convertList(list, item -> BeanUtils.toBean(item, SocialWxaSubscribeTemplateRespDTO.class).setId(item.getPriTmplId()));
}
@Override
public void sendWxaSubscribeMessage(SocialWxaSubscribeMessageSendReqDTO reqDTO) {
// 1.1 获得订阅模版列表
List<TemplateInfo> templateList = socialClientService.getSubscribeTemplateList(reqDTO.getUserType());
if (CollUtil.isEmpty(templateList)) {
log.warn("[sendSubscribeMessage][reqDTO({}) 发送订阅消息失败,原因:没有找到订阅模板]", reqDTO);
return;
}
// 1.2 获得需要使用的模版
TemplateInfo template = findOne(templateList, item ->
ObjUtil.equal(item.getTitle(), reqDTO.getTemplateTitle()));
if (template == null) {
log.warn("[sendWxaSubscribeMessage][reqDTO({}) 发送订阅消息失败,原因:没有找到订阅模板]", reqDTO);
return;
}
// 2. 获得社交用户
SocialUserRespDTO socialUser = socialUserService.getSocialUserByUserId(reqDTO.getUserType(), reqDTO.getUserId(),
SocialTypeEnum.WECHAT_MINI_PROGRAM.getType());
if (StrUtil.isBlankIfStr(socialUser.getOpenid())) {
log.warn("[sendWxaSubscribeMessage][reqDTO({}) 发送订阅消息失败,原因:会员 openid 缺失]", reqDTO);
return;
}
// 3. 发送订阅消息
socialClientService.sendSubscribeMessage(reqDTO, template.getPriTmplId(), socialUser.getOpenid());
}
@Override
public void uploadWxaOrderShippingInfo(Integer userType, SocialWxaOrderUploadShippingInfoReqDTO reqDTO) {
socialClientService.uploadWxaOrderShippingInfo(userType, reqDTO);
}
@Override
public void notifyWxaOrderConfirmReceive(Integer userType, SocialWxaOrderNotifyConfirmReceiveReqDTO reqDTO) {
socialClientService.notifyWxaOrderConfirmReceive(userType, reqDTO);
}
}

54
cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/social/SocialUserApi.java

@ -0,0 +1,54 @@
package cn.iocoder.yudao.module.system.api.social;
import cn.iocoder.yudao.framework.common.exception.ServiceException;
import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO;
import cn.iocoder.yudao.module.system.api.social.dto.SocialUserRespDTO;
import cn.iocoder.yudao.module.system.api.social.dto.SocialUserUnbindReqDTO;
import jakarta.validation.Valid;
/**
* 社交用户的 API 接口
*
* @author 芋道源码
*/
public interface SocialUserApi {
/**
* 绑定社交用户
*
* @param reqDTO 绑定信息
* @return 社交用户 openid
*/
String bindSocialUser(@Valid SocialUserBindReqDTO reqDTO);
/**
* 取消绑定社交用户
*
* @param reqDTO 解绑
*/
void unbindSocialUser(@Valid SocialUserUnbindReqDTO reqDTO);
/**
* 获得社交用户基于 userId
*
* @param userType 用户类型
* @param userId 用户编号
* @param socialType 社交平台的类型
* @return 社交用户
*/
SocialUserRespDTO getSocialUserByUserId(Integer userType, Long userId, Integer socialType);
/**
* 获得社交用户
*
* 在认证信息不正确的情况下也会抛出 {@link ServiceException} 业务异常
*
* @param userType 用户类型
* @param socialType 社交平台的类型
* @param code 授权码
* @param state state
* @return 社交用户
*/
SocialUserRespDTO getSocialUserByCode(Integer userType, Integer socialType, String code, String state);
}

44
cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/social/SocialUserApiImpl.java

@ -0,0 +1,44 @@
package cn.iocoder.yudao.module.system.api.social;
import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO;
import cn.iocoder.yudao.module.system.api.social.dto.SocialUserRespDTO;
import cn.iocoder.yudao.module.system.api.social.dto.SocialUserUnbindReqDTO;
import cn.iocoder.yudao.module.system.service.social.SocialUserService;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
/**
* 社交用户的 API 实现类
*
* @author 芋道源码
*/
@Service
@Validated
public class SocialUserApiImpl implements SocialUserApi {
@Resource
private SocialUserService socialUserService;
@Override
public String bindSocialUser(SocialUserBindReqDTO reqDTO) {
return socialUserService.bindSocialUser(reqDTO);
}
@Override
public void unbindSocialUser(SocialUserUnbindReqDTO reqDTO) {
socialUserService.unbindSocialUser(reqDTO.getUserId(), reqDTO.getUserType(),
reqDTO.getSocialType(), reqDTO.getOpenid());
}
@Override
public SocialUserRespDTO getSocialUserByUserId(Integer userType, Long userId, Integer socialType) {
return socialUserService.getSocialUserByUserId(userType, userId, socialType);
}
@Override
public SocialUserRespDTO getSocialUserByCode(Integer userType, Integer socialType, String code, String state) {
return socialUserService.getSocialUserByCode(userType, socialType, code, state);
}
}

51
cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialUserBindReqDTO.java

@ -0,0 +1,51 @@
package cn.iocoder.yudao.module.system.api.social.dto;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 取消绑定社交用户 Request DTO
*
* @author 芋道源码
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class SocialUserBindReqDTO {
/**
* 用户编号
*/
@NotNull(message = "用户编号不能为空")
private Long userId;
/**
* 用户类型
*/
@InEnum(UserTypeEnum.class)
@NotNull(message = "用户类型不能为空")
private Integer userType;
/**
* 社交平台的类型
*/
@InEnum(SocialTypeEnum.class)
@NotNull(message = "社交平台的类型不能为空")
private Integer socialType;
/**
* 授权码
*/
@NotEmpty(message = "授权码不能为空")
private String code;
/**
* state
*/
@NotNull(message = "state 不能为空")
private String state;
}

35
cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialUserRespDTO.java

@ -0,0 +1,35 @@
package cn.iocoder.yudao.module.system.api.social.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 社交用户 Response DTO
*
* @author 芋道源码
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class SocialUserRespDTO {
/**
* 社交用户的 openid
*/
private String openid;
/**
* 社交用户的昵称
*/
private String nickname;
/**
* 社交用户的头像
*/
private String avatar;
/**
* 关联的用户编号
*/
private Long userId;
}

47
cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialUserUnbindReqDTO.java

@ -0,0 +1,47 @@
package cn.iocoder.yudao.module.system.api.social.dto;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 社交绑定 Request DTO使用 code 授权码
*
* @author 芋道源码
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class SocialUserUnbindReqDTO {
/**
* 用户编号
*/
@NotNull(message = "用户编号不能为空")
private Long userId;
/**
* 用户类型
*/
@InEnum(UserTypeEnum.class)
@NotNull(message = "用户类型不能为空")
private Integer userType;
/**
* 社交平台的类型
*/
@InEnum(SocialTypeEnum.class)
@NotNull(message = "社交平台的类型不能为空")
private Integer socialType;
/**
* 社交平台的 openid
*/
@NotEmpty(message = "社交平台的 openid 不能为空")
private String openid;
}

34
cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialWxJsapiSignatureRespDTO.java

@ -0,0 +1,34 @@
package cn.iocoder.yudao.module.system.api.social.dto;
import lombok.Data;
/**
* 微信公众号 JSAPI 签名 Response DTO
*
* @author 芋道源码
*/
@Data
public class SocialWxJsapiSignatureRespDTO {
/**
* 微信公众号的 appId
*/
private String appId;
/**
* 匿名串
*/
private String nonceStr;
/**
* 时间戳
*/
private Long timestamp;
/**
* URL
*/
private String url;
/**
* 签名
*/
private String signature;
}

27
cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialWxPhoneNumberInfoRespDTO.java

@ -0,0 +1,27 @@
package cn.iocoder.yudao.module.system.api.social.dto;
import lombok.Data;
/**
* 微信小程序的手机信息 Response DTO
*
* @author 芋道源码
*/
@Data
public class SocialWxPhoneNumberInfoRespDTO {
/**
* 用户绑定的手机号国外手机号会有区号
*/
private String phoneNumber;
/**
* 没有区号的手机号
*/
private String purePhoneNumber;
/**
* 区号
*/
private String countryCode;
}

66
cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialWxQrcodeReqDTO.java

@ -0,0 +1,66 @@
package cn.iocoder.yudao.module.system.api.social.dto;
import jakarta.validation.constraints.NotEmpty;
import lombok.Data;
/**
* 获取小程序码 Request DTO
*
* @author HUIHUI
* @see <a href="https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/qrcode-link/qr-code/getUnlimitedQRCode.html">获取不限制的小程序码</a>
*/
@Data
public class SocialWxQrcodeReqDTO {
/**
* 页面路径不能携带参数参数请放在scene字段里
*/
public static final String SCENE = "";
/**
* 二维码宽度
*/
public static final Integer WIDTH = 430;
/**
* 自动配置线条颜色如果颜色依然是黑色则说明不建议配置主色调
*/
public static final Boolean AUTO_COLOR = true;
/**
* 检查 page 是否存在
*/
public static final Boolean CHECK_PATH = true;
/**
* 是否需要透明底色
*
* hyaline true 生成透明底色的小程序码
*/
public static final Boolean HYALINE = true;
/**
* 场景
*/
@NotEmpty(message = "场景不能为空")
private String scene;
/**
* 页面路径
*/
@NotEmpty(message = "页面路径不能为空")
private String path;
/**
* 二维码宽度
*/
private Integer width;
/**
* 是否需要透明底色
*/
private Boolean autoColor;
/**
* 是否检查 page 是否存在
*/
private Boolean checkPath;
/**
* 是否需要透明底色
*/
private Boolean hyaline;
}

30
cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialWxaOrderNotifyConfirmReceiveReqDTO.java

@ -0,0 +1,30 @@
package cn.iocoder.yudao.module.system.api.social.dto;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.time.LocalDateTime;
/**
* 小程序订单上传购物详情
*
* @see <a href="https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/shopping-order/normal-shopping-detail/uploadShoppingInfo.html">上传购物详情</a>
* @author 芋道源码
*/
@Data
public class SocialWxaOrderNotifyConfirmReceiveReqDTO {
/**
* 原支付交易对应的微信订单号
*/
@NotEmpty(message = "原支付交易对应的微信订单号不能为空")
private String transactionId;
/**
* 快递签收时间
*/
@NotNull(message = "快递签收时间不能为空")
private LocalDateTime receivedTime;
}

67
cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialWxaOrderUploadShippingInfoReqDTO.java

@ -0,0 +1,67 @@
package cn.iocoder.yudao.module.system.api.social.dto;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
/**
* 小程序订单上传购物详情
*
* @see <a href="https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/shopping-order/normal-shopping-detail/uploadShoppingInfo.html">上传购物详情</a>
* @author 芋道源码
*/
@Data
public class SocialWxaOrderUploadShippingInfoReqDTO {
/**
* 物流模式 - 实体物流配送采用快递公司进行实体物流配送形式
*/
public static final Integer LOGISTICS_TYPE_EXPRESS = 1;
/**
* 物流模式 - 虚拟商品虚拟商品例如话费充值点卡等无实体配送形式
*/
public static final Integer LOGISTICS_TYPE_VIRTUAL = 3;
/**
* 物流模式 - 用户自提
*/
public static final Integer LOGISTICS_TYPE_PICK_UP = 4;
/**
* 支付者支付者信息(openid)
*/
@NotEmpty(message = "支付者,支付者信息(openid)不能为空")
private String openid;
/**
* 原支付交易对应的微信订单号
*/
@NotEmpty(message = "原支付交易对应的微信订单号不能为空")
private String transactionId;
/**
* 物流模式
*/
@NotNull(message = "物流模式不能为空")
private Integer logisticsType;
/**
* 物流发货单号
*/
private String logisticsNo;
/**
* 物流公司编号
*
* @see <a href="https://developers.weixin.qq.com/miniprogram/dev/platform-capabilities/industry/express/business/express_search.html#%E8%8E%B7%E5%8F%96%E8%BF%90%E5%8A%9Bid%E5%88%97%E8%A1%A8get-delivery-list">物流查询插件简介</a>
*/
private String expressCompany;
/**
* 商品信息
*/
@NotEmpty(message = "商品信息不能为空")
private String itemDesc;
/**
* 收件人手机号
*/
@NotEmpty(message = "收件人手机号")
private String receiverContact;
}

61
cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialWxaSubscribeMessageSendReqDTO.java

@ -0,0 +1,61 @@
package cn.iocoder.yudao.module.system.api.social.dto;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.util.HashMap;
import java.util.Map;
/**
* 微信小程序订阅消息发送 Request DTO
*
* @author HUIHUI
*/
@Data
public class SocialWxaSubscribeMessageSendReqDTO {
/**
* 用户编号
*
* 关联 MemberUserDO id 编号
* 关联 AdminUserDO id 编号
*/
@NotNull(message = "用户编号不能为空")
private Long userId;
/**
* 用户类型
*
* 关联 {@link UserTypeEnum}
*/
@NotNull(message = "用户类型不能为空")
private Integer userType;
/**
* 消息模版标题
*/
@NotEmpty(message = "消息模版标题不能为空")
private String templateTitle;
/**
* 点击模板卡片后的跳转页面仅限本小程序内的页面
*
* 支持带参数示例 index?foo=bar 该字段不填则模板无跳转
*/
private String page;
/**
* 模板内容的参数
*/
private Map<String, String> messages;
public SocialWxaSubscribeMessageSendReqDTO addMessage(String key, String value) {
if (messages == null) {
messages = new HashMap<>();
}
messages.put(key, value);
return this;
}
}

42
cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialWxaSubscribeTemplateRespDTO.java

@ -0,0 +1,42 @@
package cn.iocoder.yudao.module.system.api.social.dto;
import lombok.Data;
/**
* 小程序订阅消息模版 Response DTO
*
* @author HUIHUI
*/
@Data
public class SocialWxaSubscribeTemplateRespDTO {
/**
* 模版编号
*/
private String id;
/**
* 模版标题
*/
private String title;
/**
* 模版内容
*/
private String content;
/**
* 模板内容示例
*/
private String example;
/**
* 模版类型
*
* 2为一次性订阅
* 3为长期订阅
*/
private Integer type;
}

22
cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.java

@ -17,6 +17,7 @@ import cn.iocoder.yudao.module.system.service.auth.AdminAuthService;
import cn.iocoder.yudao.module.system.service.permission.MenuService; import cn.iocoder.yudao.module.system.service.permission.MenuService;
import cn.iocoder.yudao.module.system.service.permission.PermissionService; import cn.iocoder.yudao.module.system.service.permission.PermissionService;
import cn.iocoder.yudao.module.system.service.permission.RoleService; import cn.iocoder.yudao.module.system.service.permission.RoleService;
import cn.iocoder.yudao.module.system.service.social.SocialClientService;
import cn.iocoder.yudao.module.system.service.user.AdminUserService; import cn.iocoder.yudao.module.system.service.user.AdminUserService;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameter;
@ -59,6 +60,8 @@ public class AuthController {
@Resource @Resource
private SecurityProperties securityProperties; private SecurityProperties securityProperties;
@Resource
private SocialClientService socialClientService;
@PostMapping("/login") @PostMapping("/login")
@PermitAll @PermitAll
@Operation(summary = "使用账号密码登录") @Operation(summary = "使用账号密码登录")
@ -147,5 +150,24 @@ public class AuthController {
} }
@GetMapping("/social-auth-redirect")
@PermitAll
@Operation(summary = "社交授权的跳转")
@Parameters({
@Parameter(name = "type", description = "社交类型", required = true),
@Parameter(name = "redirectUri", description = "回调路径")
})
public CommonResult<String> socialLogin(@RequestParam("type") Integer type,
@RequestParam("redirectUri") String redirectUri) {
return success(socialClientService.getAuthorizeUrl(
type, UserTypeEnum.ADMIN.getValue(), redirectUri));
}
@PostMapping("/social-login")
@PermitAll
@Operation(summary = "社交快捷登录,使用 code 授权码", description = "适合未登录的用户,但是社交账号已绑定用户")
public CommonResult<AuthLoginRespVO> socialQuickLogin(@RequestBody @Valid AuthSocialLoginReqVO reqVO) {
return success(authService.socialLogin(reqVO));
}
} }

20
cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/SocialClientController.http

@ -0,0 +1,20 @@
### 请求 /system/social-client/send-subscribe-message 接口 => 发送测试订阅消息
POST {{baseUrl}}/system/social-client/send-subscribe-message
Authorization: Bearer {{token}}
Content-Type: application/json
#Authorization: Bearer test100
tenant-id: {{adminTenantId}}
{
"userId": 247,
"userType": 1,
"socialType": 34,
"templateTitle": "充值成功通知",
"page": "",
"messages": {
"character_string1":"5616122165165",
"amount2":"1000.00",
"time3":"2024-01-01 10:10:10",
"phrase4": "充值成功"
}
}

94
cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/SocialClientController.java

@ -0,0 +1,94 @@
package cn.iocoder.yudao.module.system.controller.admin.socail;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.system.api.social.SocialClientApi;
import cn.iocoder.yudao.module.system.api.social.dto.SocialWxaSubscribeMessageSendReqDTO;
import cn.iocoder.yudao.module.system.controller.admin.socail.vo.client.SocialClientPageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.socail.vo.client.SocialClientRespVO;
import cn.iocoder.yudao.module.system.controller.admin.socail.vo.client.SocialClientSaveReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialClientDO;
import cn.iocoder.yudao.module.system.service.social.SocialClientService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - 社交客户端")
@RestController
@RequestMapping("/system/social-client")
@Validated
public class SocialClientController {
@Resource
private SocialClientService socialClientService;
@Resource
private SocialClientApi socialClientApi;
@PostMapping("/create")
@Operation(summary = "创建社交客户端")
@PreAuthorize("@ss.hasPermission('system:social-client:create')")
public CommonResult<Long> createSocialClient(@Valid @RequestBody SocialClientSaveReqVO createReqVO) {
return success(socialClientService.createSocialClient(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新社交客户端")
@PreAuthorize("@ss.hasPermission('system:social-client:update')")
public CommonResult<Boolean> updateSocialClient(@Valid @RequestBody SocialClientSaveReqVO updateReqVO) {
socialClientService.updateSocialClient(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除社交客户端")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('system:social-client:delete')")
public CommonResult<Boolean> deleteSocialClient(@RequestParam("id") Long id) {
socialClientService.deleteSocialClient(id);
return success(true);
}
@DeleteMapping("/delete-list")
@Parameter(name = "ids", description = "编号列表", required = true)
@Operation(summary = "批量删除社交客户端")
@PreAuthorize("@ss.hasPermission('system:social-client:delete')")
public CommonResult<Boolean> deleteSocialClientList(@RequestParam("ids") List<Long> ids) {
socialClientService.deleteSocialClientList(ids);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得社交客户端")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('system:social-client:query')")
public CommonResult<SocialClientRespVO> getSocialClient(@RequestParam("id") Long id) {
SocialClientDO client = socialClientService.getSocialClient(id);
return success(BeanUtils.toBean(client, SocialClientRespVO.class));
}
@GetMapping("/page")
@Operation(summary = "获得社交客户端分页")
@PreAuthorize("@ss.hasPermission('system:social-client:query')")
public CommonResult<PageResult<SocialClientRespVO>> getSocialClientPage(@Valid SocialClientPageReqVO pageVO) {
PageResult<SocialClientDO> pageResult = socialClientService.getSocialClientPage(pageVO);
return success(BeanUtils.toBean(pageResult, SocialClientRespVO.class));
}
@PostMapping("/send-subscribe-message")
@Operation(summary = "发送订阅消息") // 用于测试
@PreAuthorize("@ss.hasPermission('system:social-client:query')")
public void sendSubscribeMessage(@RequestBody SocialWxaSubscribeMessageSendReqDTO reqDTO) {
socialClientApi.sendWxaSubscribeMessage(reqDTO);
}
}

82
cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/SocialUserController.java

@ -0,0 +1,82 @@
package cn.iocoder.yudao.module.system.controller.admin.socail;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO;
import cn.iocoder.yudao.module.system.controller.admin.socail.vo.user.SocialUserBindReqVO;
import cn.iocoder.yudao.module.system.controller.admin.socail.vo.user.SocialUserPageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.socail.vo.user.SocialUserRespVO;
import cn.iocoder.yudao.module.system.controller.admin.socail.vo.user.SocialUserUnbindReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserDO;
import cn.iocoder.yudao.module.system.service.social.SocialUserService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
@Tag(name = "管理后台 - 社交用户")
@RestController
@RequestMapping("/system/social-user")
@Validated
public class SocialUserController {
@Resource
private SocialUserService socialUserService;
@PostMapping("/bind")
@Operation(summary = "社交绑定,使用 code 授权码")
public CommonResult<Boolean> socialBind(@RequestBody @Valid SocialUserBindReqVO reqVO) {
socialUserService.bindSocialUser(new SocialUserBindReqDTO().setSocialType(reqVO.getType())
.setCode(reqVO.getCode()).setState(reqVO.getState())
.setUserId(getLoginUserId()).setUserType(UserTypeEnum.ADMIN.getValue()));
return CommonResult.success(true);
}
@DeleteMapping("/unbind")
@Operation(summary = "取消社交绑定")
public CommonResult<Boolean> socialUnbind(@RequestBody SocialUserUnbindReqVO reqVO) {
socialUserService.unbindSocialUser(getLoginUserId(), UserTypeEnum.ADMIN.getValue(), reqVO.getType(), reqVO.getOpenid());
return CommonResult.success(true);
}
@GetMapping("/get-bind-list")
@Operation(summary = "获得绑定社交用户列表")
public CommonResult<List<SocialUserRespVO>> getBindSocialUserList() {
List<SocialUserDO> list = socialUserService.getSocialUserList(getLoginUserId(), UserTypeEnum.ADMIN.getValue());
return success(convertList(list, socialUser -> new SocialUserRespVO() // 返回精简信息
.setId(socialUser.getId()).setType(socialUser.getType()).setOpenid(socialUser.getOpenid())
.setNickname(socialUser.getNickname()).setAvatar(socialUser.getNickname())));
}
// ==================== 社交用户 CRUD ====================
@GetMapping("/get")
@Operation(summary = "获得社交用户")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('system:social-user:query')")
public CommonResult<SocialUserRespVO> getSocialUser(@RequestParam("id") Long id) {
SocialUserDO socialUser = socialUserService.getSocialUser(id);
return success(BeanUtils.toBean(socialUser, SocialUserRespVO.class));
}
@GetMapping("/page")
@Operation(summary = "获得社交用户分页")
@PreAuthorize("@ss.hasPermission('system:social-user:query')")
public CommonResult<PageResult<SocialUserRespVO>> getSocialUserPage(@Valid SocialUserPageReqVO pageVO) {
PageResult<SocialUserDO> pageResult = socialUserService.getSocialUserPage(pageVO);
return success(BeanUtils.toBean(pageResult, SocialUserRespVO.class));
}
}

30
cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/vo/client/SocialClientPageReqVO.java

@ -0,0 +1,30 @@
package cn.iocoder.yudao.module.system.controller.admin.socail.vo.client;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@Schema(description = "管理后台 - 社交客户端分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class SocialClientPageReqVO extends PageParam {
@Schema(description = "应用名", example = "yudao商城")
private String name;
@Schema(description = "社交平台的类型", example = "31")
private Integer socialType;
@Schema(description = "用户类型", example = "2")
private Integer userType;
@Schema(description = "客户端编号", example = "145442115")
private String clientId;
@Schema(description = "状态", example = "1")
private Integer status;
}

39
cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/vo/client/SocialClientRespVO.java

@ -0,0 +1,39 @@
package cn.iocoder.yudao.module.system.controller.admin.socail.vo.client;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 社交客户端 Response VO")
@Data
public class SocialClientRespVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "27162")
private Long id;
@Schema(description = "应用名", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudao商城")
private String name;
@Schema(description = "社交平台的类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "31")
private Integer socialType;
@Schema(description = "用户类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
private Integer userType;
@Schema(description = "客户端编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "wwd411c69a39ad2e54")
private String clientId;
@Schema(description = "客户端密钥", requiredMode = Schema.RequiredMode.REQUIRED, example = "peter")
private String clientSecret;
@Schema(description = "授权方的网页应用编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2000045")
private String agentId;
@Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer status;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;
}

61
cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/vo/client/SocialClientSaveReqVO.java

@ -0,0 +1,61 @@
package cn.iocoder.yudao.module.system.controller.admin.socail.vo.client;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import jakarta.validation.constraints.AssertTrue;
import jakarta.validation.constraints.NotNull;
import java.util.Objects;
@Schema(description = "管理后台 - 社交客户端创建/修改 Request VO")
@Data
public class SocialClientSaveReqVO {
@Schema(description = "编号", example = "27162")
private Long id;
@Schema(description = "应用名", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudao商城")
@NotNull(message = "应用名不能为空")
private String name;
@Schema(description = "社交平台的类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "31")
@NotNull(message = "社交平台的类型不能为空")
@InEnum(SocialTypeEnum.class)
private Integer socialType;
@Schema(description = "用户类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
@NotNull(message = "用户类型不能为空")
@InEnum(UserTypeEnum.class)
private Integer userType;
@Schema(description = "客户端编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "wwd411c69a39ad2e54")
@NotNull(message = "客户端编号不能为空")
private String clientId;
@Schema(description = "客户端密钥", requiredMode = Schema.RequiredMode.REQUIRED, example = "peter")
@NotNull(message = "客户端密钥不能为空")
private String clientSecret;
@Schema(description = "授权方的网页应用编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2000045")
private String agentId;
@Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "状态不能为空")
@InEnum(CommonStatusEnum.class)
private Integer status;
@AssertTrue(message = "agentId 不能为空")
@JsonIgnore
public boolean isAgentIdValid() {
// 如果是企业微信,必须填写 agentId 属性
return !Objects.equals(socialType, SocialTypeEnum.WECHAT_ENTERPRISE.getType())
|| !StrUtil.isEmpty(agentId);
}
}

34
cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/vo/user/SocialUserBindReqVO.java

@ -0,0 +1,34 @@
package cn.iocoder.yudao.module.system.controller.admin.socail.vo.user;
import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
@Schema(description = "管理后台 - 社交绑定 Request VO,使用 code 授权码")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class SocialUserBindReqVO {
@Schema(description = "社交平台的类型,参见 UserSocialTypeEnum 枚举值", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
@InEnum(SocialTypeEnum.class)
@NotNull(message = "社交平台的类型不能为空")
private Integer type;
@Schema(description = "授权码", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@NotEmpty(message = "授权码不能为空")
private String code;
@Schema(description = "state", requiredMode = Schema.RequiredMode.REQUIRED, example = "9b2ffbc1-7425-4155-9894-9d5c08541d62")
@NotEmpty(message = "state 不能为空")
private String state;
}

33
cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/vo/user/SocialUserPageReqVO.java

@ -0,0 +1,33 @@
package cn.iocoder.yudao.module.system.controller.admin.socail.vo.user;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 社交用户分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class SocialUserPageReqVO extends PageParam {
@Schema(description = "社交平台的类型", example = "30")
private Integer type;
@Schema(description = "用户昵称", example = "李四")
private String nickname;
@Schema(description = "社交 openid", example = "oz-Jdt0kd_jdhUxJHQdBJMlOFN7w")
private String openid;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}

48
cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/vo/user/SocialUserRespVO.java

@ -0,0 +1,48 @@
package cn.iocoder.yudao.module.system.controller.admin.socail.vo.user;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 社交用户 Response VO")
@Data
public class SocialUserRespVO {
@Schema(description = "主键(自增策略)", requiredMode = Schema.RequiredMode.REQUIRED, example = "14569")
private Long id;
@Schema(description = "社交平台的类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "30")
private Integer type;
@Schema(description = "社交 openid", requiredMode = Schema.RequiredMode.REQUIRED, example = "666")
private String openid;
@Schema(description = "社交 token", requiredMode = Schema.RequiredMode.REQUIRED, example = "666")
private String token;
@Schema(description = "原始 Token 数据,一般是 JSON 格式", requiredMode = Schema.RequiredMode.REQUIRED, example = "{}")
private String rawTokenInfo;
@Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿")
private String nickname;
@Schema(description = "用户头像", example = "https://www.iocoder.cn/xxx.png")
private String avatar;
@Schema(description = "原始用户数据,一般是 JSON 格式", requiredMode = Schema.RequiredMode.REQUIRED, example = "{}")
private String rawUserInfo;
@Schema(description = "最后一次的认证 code", requiredMode = Schema.RequiredMode.REQUIRED, example = "666666")
private String code;
@Schema(description = "最后一次的认证 state", requiredMode = Schema.RequiredMode.REQUIRED, example = "123456")
private String state;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;
@Schema(description = "更新时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime updateTime;
}

30
cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/vo/user/SocialUserUnbindReqVO.java

@ -0,0 +1,30 @@
package cn.iocoder.yudao.module.system.controller.admin.socail.vo.user;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
@Schema(description = "管理后台 - 取消社交绑定 Request VO")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class SocialUserUnbindReqVO {
@Schema(description = "社交平台的类型,参见 UserSocialTypeEnum 枚举值", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
@InEnum(SocialTypeEnum.class)
@NotNull(message = "社交平台的类型不能为空")
private Integer type;
@Schema(description = "社交用户的 openid", requiredMode = Schema.RequiredMode.REQUIRED, example = "IPRmJ0wvBptiPIlGEZiPewGwiEiE")
@NotEmpty(message = "社交用户的 openid 不能为空")
private String openid;
}

2
cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/TenantController.java

@ -52,7 +52,7 @@ public class TenantController {
@PermitAll @PermitAll
@TenantIgnore @TenantIgnore
@Operation(summary = "获取租户精简信息列表", description = "只包含被开启的租户,用于【首页】功能的选择租户选项") @Operation(summary = "获取租户精简信息列表", description = "只包含被开启的租户,用于【首页】功能的选择租户选项")
public CommonResult<List<TenantRespVO>> getTenantSimpleList() {
public CommonResult<List<TenantRespVO>> getTenantSimpleList() {
List<TenantDO> list = tenantService.getTenantListByStatus(CommonStatusEnum.ENABLE.getStatus()); List<TenantDO> list = tenantService.getTenantListByStatus(CommonStatusEnum.ENABLE.getStatus());
return success(convertList(list, tenantDO -> return success(convertList(list, tenantDO ->
new TenantRespVO().setId(tenantDO.getId()).setName(tenantDO.getName()))); new TenantRespVO().setId(tenantDO.getId()).setName(tenantDO.getName())));

76
cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/social/SocialClientDO.java

@ -0,0 +1,76 @@
package cn.iocoder.yudao.module.system.dal.dataobject.social;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
import me.zhyd.oauth.config.AuthConfig;
/**
* 社交客户端 DO
*
* 对应 {@link AuthConfig} 配置满足不同租户有自己的客户端配置实现社交三方登录
*
* @author 芋道源码
*/
@TableName(value = "system_social_client", autoResultMap = true)
@KeySequence("system_social_client_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@EqualsAndHashCode(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class SocialClientDO extends TenantBaseDO {
/**
* 编号自增
*/
@TableId
private Long id;
/**
* 应用名
*/
private String name;
/**
* 社交类型
*
* 枚举 {@link SocialTypeEnum}
*/
private Integer socialType;
/**
* 用户类型
*
* 目的不同用户类型对应不同的小程序需要自己的配置
*
* 枚举 {@link UserTypeEnum}
*/
private Integer userType;
/**
* 状态
*
* 枚举 {@link CommonStatusEnum}
*/
private Integer status;
/**
* 客户端 id
*/
private String clientId;
/**
* 客户端 Secret
*/
private String clientSecret;
/**
* 代理编号
*
* 目前只有部分社交类型在使用
* 1. 企业微信对应授权方的网页应用 ID
*/
private String agentId;
}

56
cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/social/SocialUserBindDO.java

@ -0,0 +1,56 @@
package cn.iocoder.yudao.module.system.dal.dataobject.social;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
/**
* 社交用户的绑定
* {@link SocialUserDO} UserDO 的关联表
*
* @author 芋道源码
*/
@TableName(value = "system_social_user_bind", autoResultMap = true)
@KeySequence("system_social_user_bind_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@EqualsAndHashCode(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class SocialUserBindDO extends BaseDO {
/**
* 编号
*/
@TableId
private Long id;
/**
* 关联的用户编号
*
* 关联 UserDO 的编号
*/
private Long userId;
/**
* 用户类型
*
* 枚举 {@link UserTypeEnum}
*/
private Integer userType;
/**
* 社交平台的用户编号
*
* 关联 {@link SocialUserDO#getId()}
*/
private Long socialUserId;
/**
* 社交平台的类型
*
* 冗余 {@link SocialUserDO#getType()}
*/
private Integer socialType;
}

73
cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/social/SocialUserDO.java

@ -0,0 +1,73 @@
package cn.iocoder.yudao.module.system.dal.dataobject.social;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
/**
* 社交三方用户
*
* @author weir
*/
@TableName(value = "system_social_user", autoResultMap = true)
@KeySequence("system_social_user_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@EqualsAndHashCode(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class SocialUserDO extends BaseDO {
/**
* 自增主键
*/
@TableId
private Long id;
/**
* 社交平台的类型
*
* 枚举 {@link SocialTypeEnum}
*/
private Integer type;
/**
* 社交 openid
*/
private String openid;
/**
* 社交 token
*/
private String token;
/**
* 原始 Token 数据一般是 JSON 格式
*/
private String rawTokenInfo;
/**
* 用户昵称
*/
private String nickname;
/**
* 用户头像
*/
private String avatar;
/**
* 原始用户数据一般是 JSON 格式
*/
private String rawUserInfo;
/**
* 最后一次的认证 code
*/
private String code;
/**
* 最后一次的认证 state
*/
private String state;
}

28
cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/social/SocialClientMapper.java

@ -0,0 +1,28 @@
package cn.iocoder.yudao.module.system.dal.mysql.social;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.system.controller.admin.socail.vo.client.SocialClientPageReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialClientDO;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface SocialClientMapper extends BaseMapperX<SocialClientDO> {
default SocialClientDO selectBySocialTypeAndUserType(Integer socialType, Integer userType) {
return selectOne(SocialClientDO::getSocialType, socialType,
SocialClientDO::getUserType, userType);
}
default PageResult<SocialClientDO> selectPage(SocialClientPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<SocialClientDO>()
.likeIfPresent(SocialClientDO::getName, reqVO.getName())
.eqIfPresent(SocialClientDO::getSocialType, reqVO.getSocialType())
.eqIfPresent(SocialClientDO::getUserType, reqVO.getUserType())
.likeIfPresent(SocialClientDO::getClientId, reqVO.getClientId())
.eqIfPresent(SocialClientDO::getStatus, reqVO.getStatus())
.orderByDesc(SocialClientDO::getId));
}
}

44
cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/social/SocialUserBindMapper.java

@ -0,0 +1,44 @@
package cn.iocoder.yudao.module.system.dal.mysql.social;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserBindDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface SocialUserBindMapper extends BaseMapperX<SocialUserBindDO> {
default void deleteByUserTypeAndUserIdAndSocialType(Integer userType, Long userId, Integer socialType) {
delete(new LambdaQueryWrapperX<SocialUserBindDO>()
.eq(SocialUserBindDO::getUserType, userType)
.eq(SocialUserBindDO::getUserId, userId)
.eq(SocialUserBindDO::getSocialType, socialType));
}
default void deleteByUserTypeAndSocialUserId(Integer userType, Long socialUserId) {
delete(new LambdaQueryWrapperX<SocialUserBindDO>()
.eq(SocialUserBindDO::getUserType, userType)
.eq(SocialUserBindDO::getSocialUserId, socialUserId));
}
default SocialUserBindDO selectByUserTypeAndSocialUserId(Integer userType, Long socialUserId) {
return selectOne(SocialUserBindDO::getUserType, userType,
SocialUserBindDO::getSocialUserId, socialUserId);
}
default List<SocialUserBindDO> selectListByUserIdAndUserType(Long userId, Integer userType) {
return selectList(new LambdaQueryWrapperX<SocialUserBindDO>()
.eq(SocialUserBindDO::getUserId, userId)
.eq(SocialUserBindDO::getUserType, userType));
}
default SocialUserBindDO selectByUserIdAndUserTypeAndSocialType(Long userId, Integer userType, Integer socialType) {
return selectOne(new LambdaQueryWrapperX<SocialUserBindDO>()
.eq(SocialUserBindDO::getUserId, userId)
.eq(SocialUserBindDO::getUserType, userType)
.eq(SocialUserBindDO::getSocialType, socialType));
}
}

33
cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/social/SocialUserMapper.java

@ -0,0 +1,33 @@
package cn.iocoder.yudao.module.system.dal.mysql.social;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.system.controller.admin.socail.vo.user.SocialUserPageReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserDO;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface SocialUserMapper extends BaseMapperX<SocialUserDO> {
default SocialUserDO selectByTypeAndCodeAnState(Integer type, String code, String state) {
return selectOne(SocialUserDO::getType, type,
SocialUserDO::getCode, code,
SocialUserDO::getState, state);
}
default SocialUserDO selectByTypeAndOpenid(Integer type, String openid) {
return selectFirstOne(SocialUserDO::getType, type,
SocialUserDO::getOpenid, openid);
}
default PageResult<SocialUserDO> selectPage(SocialUserPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<SocialUserDO>()
.eqIfPresent(SocialUserDO::getType, reqVO.getType())
.likeIfPresent(SocialUserDO::getNickname, reqVO.getNickname())
.likeIfPresent(SocialUserDO::getOpenid, reqVO.getOpenid())
.betweenIfPresent(SocialUserDO::getCreateTime, reqVO.getCreateTime())
.orderByDesc(SocialUserDO::getId));
}
}

39
cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/justauth/config/YudaoJustAuthConfiguration.java

@ -0,0 +1,39 @@
package cn.iocoder.yudao.module.system.framework.justauth.config;
import cn.iocoder.yudao.module.system.framework.justauth.core.AuthRequestFactory;
import com.xkcoding.justauth.autoconfigure.JustAuthProperties;
import com.xkcoding.justauth.support.cache.RedisStateCache;
import me.zhyd.oauth.cache.AuthStateCache;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
/**
* JustAuth 配置类 TODO 芋艿 justauth 1.4.1 版本发布
*
* @author 芋道源码
*/
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties({JustAuthProperties.class})
public class YudaoJustAuthConfiguration {
@Bean
@ConditionalOnProperty(
prefix = "justauth",
value = {"enabled"},
havingValue = "true",
matchIfMissing = true
)
public AuthRequestFactory authRequestFactory(JustAuthProperties properties, AuthStateCache authStateCache) {
return new AuthRequestFactory(properties, authStateCache);
}
@Bean
public AuthStateCache authStateCache(RedisTemplate<String, String> justAuthRedisCacheTemplate,
JustAuthProperties justAuthProperties) {
return new RedisStateCache(justAuthRedisCacheTemplate, justAuthProperties.getCache());
}
}

322
cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/justauth/core/AuthRequestFactory.java

@ -0,0 +1,322 @@
/*
* Copyright (c) 2019-2029, xkcoding & Yangkai.Shen & 沈扬凯 (237497819@qq.com & xkcoding.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package cn.iocoder.yudao.module.system.framework.justauth.core;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.EnumUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.xkcoding.http.config.HttpConfig;
import com.xkcoding.justauth.autoconfigure.ExtendProperties;
import com.xkcoding.justauth.autoconfigure.JustAuthProperties;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.zhyd.oauth.cache.AuthStateCache;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.config.AuthDefaultSource;
import me.zhyd.oauth.config.AuthSource;
import me.zhyd.oauth.enums.AuthResponseStatus;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.request.*;
import org.springframework.util.CollectionUtils;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
// TODO @芋艿:等官方发布 1.4.1!!!
/**
* <p>
* AuthRequest工厂类
* </p>
*
* @author yangkai.shen
* @date Created in 2019-07-22 14:21
*/
@Slf4j
@RequiredArgsConstructor
public class AuthRequestFactory {
private final JustAuthProperties properties;
private final AuthStateCache authStateCache;
/**
* 返回当前Oauth列表
*
* @return Oauth列表
*/
@SuppressWarnings({"unchecked", "rawtypes"})
public List<String> oauthList() {
// 默认列表
List<String> defaultList = new ArrayList<>(properties.getType().keySet());
// 扩展列表
List<String> extendList = new ArrayList<>();
ExtendProperties extend = properties.getExtend();
if (null != extend) {
Class enumClass = extend.getEnumClass();
List<String> names = EnumUtil.getNames(enumClass);
// 扩展列表
extendList = extend.getConfig()
.keySet()
.stream()
.filter(x -> names.contains(x.toUpperCase()))
.map(String::toUpperCase)
.collect(Collectors.toList());
}
// 合并
return (List<String>) CollUtil.addAll(defaultList, extendList);
}
/**
* 返回AuthRequest对象
*
* @param source {@link AuthSource}
* @return {@link AuthRequest}
*/
public AuthRequest get(String source) {
if (StrUtil.isBlank(source)) {
throw new AuthException(AuthResponseStatus.NO_AUTH_SOURCE);
}
// 获取 JustAuth 中已存在的
AuthRequest authRequest = getDefaultRequest(source);
// 如果获取不到则尝试取自定义的
if (authRequest == null) {
authRequest = getExtendRequest(properties.getExtend().getEnumClass(), source);
}
if (authRequest == null) {
throw new AuthException(AuthResponseStatus.UNSUPPORTED);
}
return authRequest;
}
/**
* 获取自定义的 request
*
* @param clazz 枚举类 {@link AuthSource}
* @param source {@link AuthSource}
* @return {@link AuthRequest}
*/
@SuppressWarnings({"unchecked", "rawtypes"})
private AuthRequest getExtendRequest(Class clazz, String source) {
String upperSource = source.toUpperCase();
try {
EnumUtil.fromString(clazz, upperSource);
} catch (IllegalArgumentException e) {
// 无自定义匹配
return null;
}
Map<String, ExtendProperties.ExtendRequestConfig> extendConfig = properties.getExtend().getConfig();
// key 转大写
Map<String, ExtendProperties.ExtendRequestConfig> upperConfig = new HashMap<>(6);
extendConfig.forEach((k, v) -> upperConfig.put(k.toUpperCase(), v));
ExtendProperties.ExtendRequestConfig extendRequestConfig = upperConfig.get(upperSource);
if (extendRequestConfig != null) {
// 配置 http config
configureHttpConfig(upperSource, extendRequestConfig, properties.getHttpConfig());
Class<? extends AuthRequest> requestClass = extendRequestConfig.getRequestClass();
if (requestClass != null) {
// 反射获取 Request 对象,所以必须实现 2 个参数的构造方法
return ReflectUtil.newInstance(requestClass, (AuthConfig) extendRequestConfig, authStateCache);
}
}
return null;
}
/**
* 获取默认的 Request
*
* @param source {@link AuthSource}
* @return {@link AuthRequest}
*/
private AuthRequest getDefaultRequest(String source) {
AuthDefaultSource authDefaultSource;
try {
authDefaultSource = EnumUtil.fromString(AuthDefaultSource.class, source.toUpperCase());
} catch (IllegalArgumentException e) {
// 无自定义匹配
return null;
}
AuthConfig config = properties.getType().get(authDefaultSource.name());
// 找不到对应关系,直接返回空
if (config == null) {
return null;
}
// 配置 http config
configureHttpConfig(authDefaultSource.name(), config, properties.getHttpConfig());
switch (authDefaultSource) {
case GITHUB:
return new AuthGithubRequest(config, authStateCache);
case WEIBO:
return new AuthWeiboRequest(config, authStateCache);
case GITEE:
return new AuthGiteeRequest(config, authStateCache);
case DINGTALK:
return new AuthDingTalkRequest(config, authStateCache);
case DINGTALK_V2:
return new AuthDingTalkV2Request(config, authStateCache);
case DINGTALK_ACCOUNT:
return new AuthDingTalkAccountRequest(config, authStateCache);
case BAIDU:
return new AuthBaiduRequest(config, authStateCache);
case CSDN:
return new AuthCsdnRequest(config, authStateCache);
case CODING:
return new AuthCodingRequest(config, authStateCache);
case OSCHINA:
return new AuthOschinaRequest(config, authStateCache);
case ALIPAY:
return new AuthAlipayRequest(config, authStateCache);
case QQ:
return new AuthQqRequest(config, authStateCache);
case WECHAT_OPEN:
return new AuthWeChatOpenRequest(config, authStateCache);
case WECHAT_MP:
return new AuthWeChatMpRequest(config, authStateCache);
case TAOBAO:
return new AuthTaobaoRequest(config, authStateCache);
case GOOGLE:
return new AuthGoogleRequest(config, authStateCache);
case FACEBOOK:
return new AuthFacebookRequest(config, authStateCache);
case DOUYIN:
return new AuthDouyinRequest(config, authStateCache);
case LINKEDIN:
return new AuthLinkedinRequest(config, authStateCache);
case MICROSOFT:
return new AuthMicrosoftRequest(config, authStateCache);
case MICROSOFT_CN:
return new AuthMicrosoftCnRequest(config, authStateCache);
case MI:
return new AuthMiRequest(config, authStateCache);
case TOUTIAO:
return new AuthToutiaoRequest(config, authStateCache);
case TEAMBITION:
return new AuthTeambitionRequest(config, authStateCache);
case RENREN:
return new AuthRenrenRequest(config, authStateCache);
case PINTEREST:
return new AuthPinterestRequest(config, authStateCache);
case STACK_OVERFLOW:
return new AuthStackOverflowRequest(config, authStateCache);
case HUAWEI:
return new AuthHuaweiRequest(config, authStateCache);
case HUAWEI_V3:
return new AuthHuaweiV3Request(config, authStateCache);
case WECHAT_ENTERPRISE:
return new AuthWeChatEnterpriseQrcodeRequest(config, authStateCache);
case WECHAT_ENTERPRISE_V2:
return new AuthWeChatEnterpriseQrcodeV2Request(config, authStateCache);
case WECHAT_ENTERPRISE_QRCODE_THIRD:
return new AuthWeChatEnterpriseThirdQrcodeRequest(config, authStateCache);
case WECHAT_ENTERPRISE_WEB:
return new AuthWeChatEnterpriseWebRequest(config, authStateCache);
case KUJIALE:
return new AuthKujialeRequest(config, authStateCache);
case GITLAB:
return new AuthGitlabRequest(config, authStateCache);
case MEITUAN:
return new AuthMeituanRequest(config, authStateCache);
case ELEME:
return new AuthElemeRequest(config, authStateCache);
case TWITTER:
return new AuthTwitterRequest(config, authStateCache);
case FEISHU:
return new AuthFeishuRequest(config, authStateCache);
case JD:
return new AuthJdRequest(config, authStateCache);
case ALIYUN:
return new AuthAliyunRequest(config, authStateCache);
case XMLY:
return new AuthXmlyRequest(config, authStateCache);
case AMAZON:
return new AuthAmazonRequest(config, authStateCache);
case SLACK:
return new AuthSlackRequest(config, authStateCache);
case LINE:
return new AuthLineRequest(config, authStateCache);
case OKTA:
return new AuthOktaRequest(config, authStateCache);
case PROGINN:
return new AuthProginnRequest(config,authStateCache);
case AFDIAN:
return new AuthAfDianRequest(config,authStateCache);
case APPLE:
return new AuthAppleRequest(config,authStateCache);
case FIGMA:
return new AuthFigmaRequest(config,authStateCache);
case WECHAT_MINI_PROGRAM:
config.setIgnoreCheckRedirectUri(true);
config.setIgnoreCheckState(true);
return new AuthWechatMiniProgramRequest(config, authStateCache);
case QQ_MINI_PROGRAM:
config.setIgnoreCheckRedirectUri(true);
config.setIgnoreCheckState(true);
return new AuthQQMiniProgramRequest(config, authStateCache);
default:
return null;
}
}
/**
* 配置 http 相关的配置
*
* @param authSource {@link AuthSource}
* @param authConfig {@link AuthConfig}
*/
private void configureHttpConfig(String authSource, AuthConfig authConfig, JustAuthProperties.JustAuthHttpConfig httpConfig) {
if (null == httpConfig) {
return;
}
Map<String, JustAuthProperties.JustAuthProxyConfig> proxyConfigMap = httpConfig.getProxy();
if (CollectionUtils.isEmpty(proxyConfigMap)) {
return;
}
JustAuthProperties.JustAuthProxyConfig proxyConfig = proxyConfigMap.get(authSource);
if (null == proxyConfig) {
return;
}
authConfig.setHttpConfig(HttpConfig.builder()
.timeout(httpConfig.getTimeout())
.proxy(new Proxy(Proxy.Type.valueOf(proxyConfig.getType()), new InetSocketAddress(proxyConfig.getHostname(), proxyConfig.getPort())))
.build());
}
}

6
cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/justauth/package-info.java

@ -0,0 +1,6 @@
/**
* justauth 三方登录的拓展
*
* @author 芋道源码
*/
package cn.iocoder.yudao.module.system.framework.justauth;

8
cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthService.java

@ -56,6 +56,14 @@ public interface AdminAuthService {
/** /**
* 社交快捷登录使用 code 授权码
*
* @param reqVO 登录信息
* @return 登录结果
*/
AuthLoginRespVO socialLogin(@Valid AuthSocialLoginReqVO reqVO);
/**
* 刷新访问令牌 * 刷新访问令牌
* *
* @param refreshToken 刷新令牌 * @param refreshToken 刷新令牌

23
cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java

@ -9,6 +9,7 @@ import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
import cn.iocoder.yudao.module.system.api.logger.dto.LoginLogCreateReqDTO; import cn.iocoder.yudao.module.system.api.logger.dto.LoginLogCreateReqDTO;
import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi; import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi;
import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeUseReqDTO; import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeUseReqDTO;
import cn.iocoder.yudao.module.system.api.social.dto.SocialUserRespDTO;
import cn.iocoder.yudao.module.system.controller.admin.auth.vo.*; import cn.iocoder.yudao.module.system.controller.admin.auth.vo.*;
import cn.iocoder.yudao.module.system.convert.auth.AuthConvert; import cn.iocoder.yudao.module.system.convert.auth.AuthConvert;
import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO; import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO;
@ -20,6 +21,7 @@ import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum;
import cn.iocoder.yudao.module.system.service.logger.LoginLogService; import cn.iocoder.yudao.module.system.service.logger.LoginLogService;
import cn.iocoder.yudao.module.system.service.member.MemberService; import cn.iocoder.yudao.module.system.service.member.MemberService;
import cn.iocoder.yudao.module.system.service.oauth2.OAuth2TokenService; import cn.iocoder.yudao.module.system.service.oauth2.OAuth2TokenService;
import cn.iocoder.yudao.module.system.service.social.SocialUserService;
import cn.iocoder.yudao.module.system.service.user.AdminUserService; import cn.iocoder.yudao.module.system.service.user.AdminUserService;
import com.anji.captcha.model.common.ResponseModel; import com.anji.captcha.model.common.ResponseModel;
import com.anji.captcha.model.vo.CaptchaVO; import com.anji.captcha.model.vo.CaptchaVO;
@ -63,6 +65,8 @@ public class AdminAuthServiceImpl implements AdminAuthService {
private CaptchaService captchaService; private CaptchaService captchaService;
@Resource @Resource
private SmsCodeApi smsCodeApi; private SmsCodeApi smsCodeApi;
@Resource
private SocialUserService socialUserService;
/** /**
* 验证码的开关默认为 true * 验证码的开关默认为 true
@ -138,6 +142,25 @@ public class AdminAuthServiceImpl implements AdminAuthService {
return createTokenAfterLoginSuccess(user.getId(), reqVO.getMobile(), LoginLogTypeEnum.LOGIN_MOBILE); return createTokenAfterLoginSuccess(user.getId(), reqVO.getMobile(), LoginLogTypeEnum.LOGIN_MOBILE);
} }
@Override
public AuthLoginRespVO socialLogin(AuthSocialLoginReqVO reqVO) {
// 使用 code 授权码,进行登录。然后,获得到绑定的用户编号
SocialUserRespDTO socialUser = socialUserService.getSocialUserByCode(UserTypeEnum.ADMIN.getValue(), reqVO.getType(),
reqVO.getCode(), reqVO.getState());
if (socialUser == null || socialUser.getUserId() == null) {
throw exception(AUTH_THIRD_LOGIN_NOT_BIND);
}
// 获得用户
AdminUserDO user = userService.getUser(socialUser.getUserId());
if (user == null) {
throw exception(USER_NOT_EXISTS);
}
// 创建 Token 令牌,记录登录日志
return createTokenAfterLoginSuccess(user.getId(), user.getUsername(), LoginLogTypeEnum.LOGIN_SOCIAL);
}
private void createLoginLog(Long userId, String username, private void createLoginLog(Long userId, String username,
LoginLogTypeEnum logTypeEnum, LoginResultEnum loginResult) { LoginLogTypeEnum logTypeEnum, LoginResultEnum loginResult) {
// 插入登录日志 // 插入登录日志

160
cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialClientService.java

@ -0,0 +1,160 @@
package cn.iocoder.yudao.module.system.service.social;
import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.system.api.social.dto.SocialWxQrcodeReqDTO;
import cn.iocoder.yudao.module.system.api.social.dto.SocialWxaOrderNotifyConfirmReceiveReqDTO;
import cn.iocoder.yudao.module.system.api.social.dto.SocialWxaOrderUploadShippingInfoReqDTO;
import cn.iocoder.yudao.module.system.api.social.dto.SocialWxaSubscribeMessageSendReqDTO;
import cn.iocoder.yudao.module.system.controller.admin.socail.vo.client.SocialClientPageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.socail.vo.client.SocialClientSaveReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialClientDO;
import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum;
import jakarta.validation.Valid;
import me.chanjar.weixin.common.bean.WxJsapiSignature;
import me.chanjar.weixin.common.bean.subscribemsg.TemplateInfo;
import me.zhyd.oauth.model.AuthUser;
import java.util.List;
/**
* 社交应用 Service 接口
*
* @author 芋道源码
*/
public interface SocialClientService {
/**
* 获得社交平台的授权 URL
*
* @param socialType 社交平台的类型 {@link SocialTypeEnum}
* @param userType 用户类型
* @param redirectUri 重定向 URL
* @return 社交平台的授权 URL
*/
String getAuthorizeUrl(Integer socialType, Integer userType, String redirectUri);
/**
* 请求社交平台获得授权的用户
*
* @param socialType 社交平台的类型
* @param userType 用户类型
* @param code 授权码
* @param state 授权 state
* @return 授权的用户
*/
AuthUser getAuthUser(Integer socialType, Integer userType, String code, String state);
// =================== 微信公众号独有 ===================
/**
* 创建微信公众号的 JS SDK 初始化所需的签名
*
* @param userType 用户类型
* @param url 访问的 URL 地址
* @return 签名
*/
WxJsapiSignature createWxMpJsapiSignature(Integer userType, String url);
// =================== 微信小程序独有 ===================
/**
* 获得微信小程序的手机信息
*
* @param userType 用户类型
* @param phoneCode 手机授权码
* @return 手机信息
*/
WxMaPhoneNumberInfo getWxMaPhoneNumberInfo(Integer userType, String phoneCode);
/**
* 获得小程序二维码
*
* @param reqVO 请求信息
* @return 小程序二维码
*/
byte[] getWxaQrcode(SocialWxQrcodeReqDTO reqVO);
/**
* 获得微信小程订阅模板
*
* 缓存的目的考虑到微信小程序订阅消息选择好模版后几乎不会变动缓存增加查询效率
*
* @param userType 用户类型
* @return 微信小程订阅模板
*/
List<TemplateInfo> getSubscribeTemplateList(Integer userType);
/**
* 发送微信小程序订阅消息
*
* @param reqDTO 请求
* @param templateId 模版编号
* @param openId 会员 openId
*/
void sendSubscribeMessage(SocialWxaSubscribeMessageSendReqDTO reqDTO, String templateId, String openId);
/**
* 上传订单发货到微信小程序
*
* @param userType 用户类型
* @param reqDTO 请求
*/
void uploadWxaOrderShippingInfo(Integer userType, SocialWxaOrderUploadShippingInfoReqDTO reqDTO);
/**
* 通知订单收货到微信小程序
*
* @param userType 用户类型
* @param reqDTO 请求
*/
void notifyWxaOrderConfirmReceive(Integer userType, SocialWxaOrderNotifyConfirmReceiveReqDTO reqDTO);
// =================== 客户端管理 ===================
/**
* 创建社交客户端
*
* @param createReqVO 创建信息
* @return 编号
*/
Long createSocialClient(@Valid SocialClientSaveReqVO createReqVO);
/**
* 更新社交客户端
*
* @param updateReqVO 更新信息
*/
void updateSocialClient(@Valid SocialClientSaveReqVO updateReqVO);
/**
* 删除社交客户端
*
* @param id 编号
*/
void deleteSocialClient(Long id);
/**
* 批量删除社交客户端
*
* @param ids 编号数组
*/
void deleteSocialClientList(List<Long> ids);
/**
* 获得社交客户端
*
* @param id 编号
* @return 社交客户端
*/
SocialClientDO getSocialClient(Long id);
/**
* 获得社交客户端分页
*
* @param pageReqVO 分页查询
* @return 社交客户端分页
*/
PageResult<SocialClientDO> getSocialClientPage(SocialClientPageReqVO pageReqVO);
}

510
cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialClientServiceImpl.java

@ -0,0 +1,510 @@
package cn.iocoder.yudao.module.system.service.social;
import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.api.WxMaSubscribeService;
import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl;
import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
import cn.binarywang.wx.miniapp.bean.WxMaSubscribeMessage;
import cn.binarywang.wx.miniapp.bean.shop.request.shipping.*;
import cn.binarywang.wx.miniapp.bean.shop.response.WxMaOrderShippingInfoBaseResponse;
import cn.binarywang.wx.miniapp.config.impl.WxMaRedisBetterConfigImpl;
import cn.binarywang.wx.miniapp.constant.WxMaConstants;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.DesensitizedUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.cache.CacheUtils;
import cn.iocoder.yudao.framework.common.util.http.HttpUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.system.api.social.dto.SocialWxQrcodeReqDTO;
import cn.iocoder.yudao.module.system.api.social.dto.SocialWxaOrderNotifyConfirmReceiveReqDTO;
import cn.iocoder.yudao.module.system.api.social.dto.SocialWxaOrderUploadShippingInfoReqDTO;
import cn.iocoder.yudao.module.system.api.social.dto.SocialWxaSubscribeMessageSendReqDTO;
import cn.iocoder.yudao.module.system.controller.admin.socail.vo.client.SocialClientPageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.socail.vo.client.SocialClientSaveReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialClientDO;
import cn.iocoder.yudao.module.system.dal.mysql.social.SocialClientMapper;
import cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants;
import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum;
import cn.iocoder.yudao.module.system.framework.justauth.core.AuthRequestFactory;
import com.binarywang.spring.starter.wxjava.miniapp.properties.WxMaProperties;
import com.binarywang.spring.starter.wxjava.mp.properties.WxMpProperties;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import jakarta.annotation.Resource;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.bean.WxJsapiSignature;
import me.chanjar.weixin.common.bean.subscribemsg.TemplateInfo;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.redis.RedisTemplateWxRedisOps;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl;
import me.chanjar.weixin.mp.config.impl.WxMpRedisConfigImpl;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthResponse;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.request.AuthRequest;
import me.zhyd.oauth.utils.AuthStateUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import java.time.Duration;
import java.time.ZonedDateTime;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.UTC_MS_WITH_XXX_OFFSET_FORMATTER;
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.toEpochSecond;
import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
import static java.util.Collections.singletonList;
/**
* 社交应用 Service 实现类
*
* @author 芋道源码
*/
@Service
@Slf4j
public class SocialClientServiceImpl implements SocialClientService {
/**
* 小程序码要打开的小程序版本
*
* 1. release正式版
* 2. trial体验版
* 3. developer开发版
*/
@Value("${yudao.wxa-code.env-version:release}")
public String envVersion;
/**
* 订阅消息跳转小程序类型
*
* 1. developer开发版
* 2. trial体验版
* 3. formal正式版
*/
@Value("${yudao.wxa-subscribe-message.miniprogram-state:formal}")
public String miniprogramState;
@Resource
private AuthRequestFactory authRequestFactory;
@Resource
private WxMpService wxMpService;
@Resource
private WxMpProperties wxMpProperties;
@Resource
private StringRedisTemplate stringRedisTemplate; // WxMpService 需要使用到,所以在 Service 注入了它
/**
* 缓存 WxMpService 对象
*
* key使用微信公众号的 appId + secret 拼接 {@link SocialClientDO} clientId clientSecret 属性
* 为什么 key 使用这种格式因为 {@link SocialClientDO} 在管理后台可以变更通过这个 key 存储它的单例
*
* 为什么要做 WxMpService 缓存因为 WxMpService 构建成本比较大所以尽量保证它是单例
*/
private final LoadingCache<String, WxMpService> wxMpServiceCache = CacheUtils.buildAsyncReloadingCache(
Duration.ofSeconds(10L),
new CacheLoader<String, WxMpService>() {
@Override
public WxMpService load(String key) {
String[] keys = key.split(":");
return buildWxMpService(keys[0], keys[1]);
}
});
@Resource
private WxMaService wxMaService;
@Resource
private WxMaProperties wxMaProperties;
/**
* 缓存 WxMaService 对象
*
* 说明同 {@link #wxMpServiceCache} 变量
*/
private final LoadingCache<String, WxMaService> wxMaServiceCache = CacheUtils.buildAsyncReloadingCache(
Duration.ofSeconds(10L),
new CacheLoader<String, WxMaService>() {
@Override
public WxMaService load(String key) {
String[] keys = key.split(":");
return buildWxMaService(keys[0], keys[1]);
}
});
@Resource
private SocialClientMapper socialClientMapper;
@Override
public String getAuthorizeUrl(Integer socialType, Integer userType, String redirectUri) {
// 获得对应的 AuthRequest 实现
AuthRequest authRequest = buildAuthRequest(socialType, userType);
// 生成跳转地址
String authorizeUri = authRequest.authorize(AuthStateUtils.createState());
return HttpUtils.replaceUrlQuery(authorizeUri, "redirect_uri", redirectUri);
}
@Override
public AuthUser getAuthUser(Integer socialType, Integer userType, String code, String state) {
// 构建请求
AuthRequest authRequest = buildAuthRequest(socialType, userType);
AuthCallback authCallback = AuthCallback.builder().code(code).state(state).build();
// 执行请求
AuthResponse<?> authResponse = authRequest.login(authCallback);
log.info("[getAuthUser][请求社交平台 type({}) request({}) response({})]", socialType,
toJsonString(authCallback), toJsonString(authResponse));
if (!authResponse.ok()) {
throw exception(SOCIAL_USER_AUTH_FAILURE, authResponse.getMsg());
}
return (AuthUser) authResponse.getData();
}
/**
* 构建 AuthRequest 对象支持多租户配置
*
* @param socialType 社交类型
* @param userType 用户类型
* @return AuthRequest 对象
*/
@VisibleForTesting
AuthRequest buildAuthRequest(Integer socialType, Integer userType) {
// 1. 先查找默认的配置项,从 application-*.yaml 中读取
AuthRequest request = authRequestFactory.get(SocialTypeEnum.valueOfType(socialType).getSource());
Assert.notNull(request, String.format("社交平台(%d) 不存在", socialType));
// 2. 查询 DB 的配置项,如果存在则进行覆盖
SocialClientDO client = socialClientMapper.selectBySocialTypeAndUserType(socialType, userType);
if (client != null && Objects.equals(client.getStatus(), CommonStatusEnum.ENABLE.getStatus())) {
// 2.1 构造新的 AuthConfig 对象
AuthConfig authConfig = (AuthConfig) ReflectUtil.getFieldValue(request, "config");
AuthConfig newAuthConfig = ReflectUtil.newInstance(authConfig.getClass());
BeanUtil.copyProperties(authConfig, newAuthConfig);
// 2.2 修改对应的 clientId + clientSecret 密钥
newAuthConfig.setClientId(client.getClientId());
newAuthConfig.setClientSecret(client.getClientSecret());
if (client.getAgentId() != null) { // 如果有 agentId 则修改 agentId
newAuthConfig.setAgentId(client.getAgentId());
}
// 2.3 设置会 request 里,进行后续使用
ReflectUtil.setFieldValue(request, "config", newAuthConfig);
}
return request;
}
// =================== 微信公众号独有 ===================
@Override
@SneakyThrows
public WxJsapiSignature createWxMpJsapiSignature(Integer userType, String url) {
WxMpService service = getWxMpService(userType);
return service.createJsapiSignature(url);
}
/**
* 获得 clientId + clientSecret 对应的 WxMpService 对象
*
* @param userType 用户类型
* @return WxMpService 对象
*/
@VisibleForTesting
WxMpService getWxMpService(Integer userType) {
// 第一步,查询 DB 的配置项,获得对应的 WxMpService 对象
SocialClientDO client = socialClientMapper.selectBySocialTypeAndUserType(
SocialTypeEnum.WECHAT_MP.getType(), userType);
if (client != null && Objects.equals(client.getStatus(), CommonStatusEnum.ENABLE.getStatus())) {
return wxMpServiceCache.getUnchecked(client.getClientId() + ":" + client.getClientSecret());
}
// 第二步,不存在 DB 配置项,则使用 application-*.yaml 对应的 WxMpService 对象
return wxMpService;
}
/**
* 创建 clientId + clientSecret 对应的 WxMpService 对象
*
* @param clientId 微信公众号 appId
* @param clientSecret 微信公众号 secret
* @return WxMpService 对象
*/
public WxMpService buildWxMpService(String clientId, String clientSecret) {
// 第一步,创建 WxMpRedisConfigImpl 对象
WxMpRedisConfigImpl configStorage = new WxMpRedisConfigImpl(
new RedisTemplateWxRedisOps(stringRedisTemplate),
wxMpProperties.getConfigStorage().getKeyPrefix());
configStorage.setAppId(clientId);
configStorage.setSecret(clientSecret);
// 第二步,创建 WxMpService 对象
WxMpService service = new WxMpServiceImpl();
service.setWxMpConfigStorage(configStorage);
return service;
}
// =================== 微信小程序独有 ===================
@Override
public WxMaPhoneNumberInfo getWxMaPhoneNumberInfo(Integer userType, String phoneCode) {
WxMaService service = getWxMaService(userType);
try {
return service.getUserService().getPhoneNumber(phoneCode);
} catch (WxErrorException e) {
log.error("[getPhoneNumber][userType({}) phoneCode({}) 获得手机号失败]", userType, phoneCode, e);
throw exception(SOCIAL_CLIENT_WEIXIN_MINI_APP_PHONE_CODE_ERROR);
}
}
@Override
public byte[] getWxaQrcode(SocialWxQrcodeReqDTO reqVO) {
WxMaService service = getWxMaService(UserTypeEnum.MEMBER.getValue());
try {
return service.getQrcodeService().createWxaCodeUnlimitBytes(
ObjUtil.defaultIfEmpty(reqVO.getScene(), SocialWxQrcodeReqDTO.SCENE),
reqVO.getPath(),
ObjUtil.defaultIfNull(reqVO.getCheckPath(), SocialWxQrcodeReqDTO.CHECK_PATH),
envVersion,
ObjUtil.defaultIfNull(reqVO.getWidth(), SocialWxQrcodeReqDTO.WIDTH),
ObjUtil.defaultIfNull(reqVO.getAutoColor(), SocialWxQrcodeReqDTO.AUTO_COLOR),
null,
ObjUtil.defaultIfNull(reqVO.getHyaline(), SocialWxQrcodeReqDTO.HYALINE));
} catch (WxErrorException e) {
log.error("[getWxQrcode][reqVO({}) 获得小程序码失败]", reqVO, e);
throw exception(SOCIAL_CLIENT_WEIXIN_MINI_APP_QRCODE_ERROR);
}
}
@Override
@Cacheable(cacheNames = RedisKeyConstants.WXA_SUBSCRIBE_TEMPLATE, key = "#userType",
unless = "#result == null")
public List<TemplateInfo> getSubscribeTemplateList(Integer userType) {
WxMaService service = getWxMaService(userType);
try {
WxMaSubscribeService subscribeService = service.getSubscribeService();
return subscribeService.getTemplateList();
} catch (WxErrorException e) {
log.error("[getSubscribeTemplate][userType({}) 获得小程序订阅消息模版]", userType, e);
throw exception(SOCIAL_CLIENT_WEIXIN_MINI_APP_SUBSCRIBE_TEMPLATE_ERROR);
}
}
@Override
public void sendSubscribeMessage(SocialWxaSubscribeMessageSendReqDTO reqDTO, String templateId, String openId) {
WxMaService service = getWxMaService(reqDTO.getUserType());
try {
WxMaSubscribeService subscribeService = service.getSubscribeService();
subscribeService.sendSubscribeMsg(buildMessageSendReqDTO(reqDTO, templateId, openId));
} catch (WxErrorException e) {
log.error("[sendSubscribeMessage][reqVO({}) templateId({}) openId({}) 发送小程序订阅消息失败]", reqDTO, templateId, openId, e);
throw exception(SOCIAL_CLIENT_WEIXIN_MINI_APP_SUBSCRIBE_MESSAGE_ERROR);
}
}
/**
* 构建发送消息请求参数
*
* @param reqDTO 请求
* @param templateId 模版编号
* @param openId 会员 openId
* @return 微信小程序订阅消息请求参数
*/
private WxMaSubscribeMessage buildMessageSendReqDTO(SocialWxaSubscribeMessageSendReqDTO reqDTO,
String templateId, String openId) {
// 设置订阅消息基本参数
WxMaSubscribeMessage subscribeMessage = new WxMaSubscribeMessage().setLang(WxMaConstants.MiniProgramLang.ZH_CN)
.setMiniprogramState(miniprogramState).setTemplateId(templateId).setToUser(openId).setPage(reqDTO.getPage());
// 设置具体消息参数
Map<String, String> messages = reqDTO.getMessages();
if (CollUtil.isNotEmpty(messages)) {
reqDTO.getMessages().keySet().forEach(key -> findAndThen(messages, key, value ->
subscribeMessage.addData(new WxMaSubscribeMessage.MsgData(key, value))));
}
return subscribeMessage;
}
@Override
public void uploadWxaOrderShippingInfo(Integer userType, SocialWxaOrderUploadShippingInfoReqDTO reqDTO) {
WxMaService service = getWxMaService(userType);
List<ShippingListBean> shippingList;
if (Objects.equals(reqDTO.getLogisticsType(), SocialWxaOrderUploadShippingInfoReqDTO.LOGISTICS_TYPE_EXPRESS)) {
shippingList = singletonList(ShippingListBean.builder()
.trackingNo(reqDTO.getLogisticsNo())
.expressCompany(reqDTO.getExpressCompany())
.itemDesc(reqDTO.getItemDesc())
.contact(ContactBean.builder().receiverContact(DesensitizedUtil.mobilePhone(reqDTO.getReceiverContact())).build())
.build());
} else {
shippingList = singletonList(ShippingListBean.builder().itemDesc(reqDTO.getItemDesc()).build());
}
WxMaOrderShippingInfoUploadRequest request = WxMaOrderShippingInfoUploadRequest.builder()
.orderKey(OrderKeyBean.builder()
.orderNumberType(2) // 使用原支付交易对应的微信订单号,即渠道单号
.transactionId(reqDTO.getTransactionId())
.build())
.logisticsType(reqDTO.getLogisticsType()) // 配送方式
.deliveryMode(1) // 统一发货
.shippingList(shippingList)
.payer(PayerBean.builder().openid(reqDTO.getOpenid()).build())
.uploadTime(ZonedDateTime.now().format(UTC_MS_WITH_XXX_OFFSET_FORMATTER))
.build();
try {
WxMaOrderShippingInfoBaseResponse response = service.getWxMaOrderShippingService().upload(request);
if (response.getErrCode() != 0) {
log.error("[uploadWxaOrderShippingInfo][上传微信小程序发货信息失败:request({}) response({})]", request, response);
throw exception(SOCIAL_CLIENT_WEIXIN_MINI_APP_ORDER_UPLOAD_SHIPPING_INFO_ERROR, response.getErrMsg());
}
log.info("[uploadWxaOrderShippingInfo][上传微信小程序发货信息成功:request({}) response({})]", request, response);
} catch (WxErrorException ex) {
log.error("[uploadWxaOrderShippingInfo][上传微信小程序发货信息失败:request({})]", request, ex);
throw exception(SOCIAL_CLIENT_WEIXIN_MINI_APP_ORDER_UPLOAD_SHIPPING_INFO_ERROR, ex.getError().getErrorMsg());
}
}
@Override
public void notifyWxaOrderConfirmReceive(Integer userType, SocialWxaOrderNotifyConfirmReceiveReqDTO reqDTO) {
WxMaService service = getWxMaService(userType);
WxMaOrderShippingInfoNotifyConfirmRequest request = WxMaOrderShippingInfoNotifyConfirmRequest.builder()
.transactionId(reqDTO.getTransactionId())
.receivedTime(toEpochSecond(reqDTO.getReceivedTime()))
.build();
try {
WxMaOrderShippingInfoBaseResponse response = service.getWxMaOrderShippingService().notifyConfirmReceive(request);
if (response.getErrCode() != 0) {
log.error("[notifyWxaOrderConfirmReceive][确认收货提醒到微信小程序失败:request({}) response({})]", request, response);
throw exception(SOCIAL_CLIENT_WEIXIN_MINI_APP_ORDER_NOTIFY_CONFIRM_RECEIVE_ERROR, response.getErrMsg());
}
log.info("[notifyWxaOrderConfirmReceive][确认收货提醒到微信小程序成功:request({}) response({})]", request, response);
} catch (WxErrorException ex) {
log.error("[notifyWxaOrderConfirmReceive][确认收货提醒到微信小程序失败:request({})]", request, ex);
throw exception(SOCIAL_CLIENT_WEIXIN_MINI_APP_ORDER_NOTIFY_CONFIRM_RECEIVE_ERROR, ex.getError().getErrorMsg());
}
}
/**
* 获得 clientId + clientSecret 对应的 WxMpService 对象
*
* @param userType 用户类型
* @return WxMpService 对象
*/
@VisibleForTesting
WxMaService getWxMaService(Integer userType) {
// 第一步,查询 DB 的配置项,获得对应的 WxMaService 对象
SocialClientDO client = socialClientMapper.selectBySocialTypeAndUserType(
SocialTypeEnum.WECHAT_MINI_PROGRAM.getType(), userType);
if (client != null && Objects.equals(client.getStatus(), CommonStatusEnum.ENABLE.getStatus())) {
return wxMaServiceCache.getUnchecked(client.getClientId() + ":" + client.getClientSecret());
}
// 第二步,不存在 DB 配置项,则使用 application-*.yaml 对应的 WxMaService 对象
return wxMaService;
}
/**
* 创建 clientId + clientSecret 对应的 WxMaService 对象
*
* @param clientId 微信小程序 appId
* @param clientSecret 微信小程序 secret
* @return WxMaService 对象
*/
private WxMaService buildWxMaService(String clientId, String clientSecret) {
// 第一步,创建 WxMaRedisBetterConfigImpl 对象
WxMaRedisBetterConfigImpl configStorage = new WxMaRedisBetterConfigImpl(
new RedisTemplateWxRedisOps(stringRedisTemplate),
wxMaProperties.getConfigStorage().getKeyPrefix());
configStorage.setAppid(clientId);
configStorage.setSecret(clientSecret);
// 第二步,创建 WxMpService 对象
WxMaService service = new WxMaServiceImpl();
service.setWxMaConfig(configStorage);
return service;
}
// =================== 客户端管理 ===================
@Override
public Long createSocialClient(SocialClientSaveReqVO createReqVO) {
// 校验重复
validateSocialClientUnique(null, createReqVO.getUserType(), createReqVO.getSocialType());
// 插入
SocialClientDO client = BeanUtils.toBean(createReqVO, SocialClientDO.class);
socialClientMapper.insert(client);
return client.getId();
}
@Override
public void updateSocialClient(SocialClientSaveReqVO updateReqVO) {
// 校验存在
validateSocialClientExists(updateReqVO.getId());
// 校验重复
validateSocialClientUnique(updateReqVO.getId(), updateReqVO.getUserType(), updateReqVO.getSocialType());
// 更新
SocialClientDO updateObj = BeanUtils.toBean(updateReqVO, SocialClientDO.class);
socialClientMapper.updateById(updateObj);
}
@Override
public void deleteSocialClient(Long id) {
// 校验存在
validateSocialClientExists(id);
// 删除
socialClientMapper.deleteById(id);
}
@Override
public void deleteSocialClientList(List<Long> ids) {
socialClientMapper.deleteByIds(ids);
}
private void validateSocialClientExists(Long id) {
if (socialClientMapper.selectById(id) == null) {
throw exception(SOCIAL_CLIENT_NOT_EXISTS);
}
}
/**
* 校验社交应用是否重复需要保证 userType + socialType 唯一
* 原因是不同端userType选择某个社交登录socialType需要通过 {@link #buildAuthRequest(Integer, Integer)} 构建对应的请求
*
* @param id 编号
* @param userType 用户类型
* @param socialType 社交类型
*/
private void validateSocialClientUnique(Long id, Integer userType, Integer socialType) {
SocialClientDO client = socialClientMapper.selectBySocialTypeAndUserType(
socialType, userType);
if (client == null) {
return;
}
if (id == null // 新增时,说明重复
|| ObjUtil.notEqual(id, client.getId())) { // 更新时,如果 id 不一致,说明重复
throw exception(SOCIAL_CLIENT_UNIQUE);
}
}
@Override
public SocialClientDO getSocialClient(Long id) {
return socialClientMapper.selectById(id);
}
@Override
public PageResult<SocialClientDO> getSocialClientPage(SocialClientPageReqVO pageReqVO) {
return socialClientMapper.selectPage(pageReqVO);
}
}

89
cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialUserService.java

@ -0,0 +1,89 @@
package cn.iocoder.yudao.module.system.service.social;
import cn.iocoder.yudao.framework.common.exception.ServiceException;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO;
import cn.iocoder.yudao.module.system.api.social.dto.SocialUserRespDTO;
import cn.iocoder.yudao.module.system.controller.admin.socail.vo.user.SocialUserPageReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserDO;
import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum;
import jakarta.validation.Valid;
import java.util.List;
/**
* 社交用户 Service 接口例如说社交平台的授权登录
*
* @author 芋道源码
*/
public interface SocialUserService {
/**
* 获得指定用户的社交用户列表
*
* @param userId 用户编号
* @param userType 用户类型
* @return 社交用户列表
*/
List<SocialUserDO> getSocialUserList(Long userId, Integer userType);
/**
* 绑定社交用户
*
* @param reqDTO 绑定信息
* @return 社交用户 openid
*/
String bindSocialUser(@Valid SocialUserBindReqDTO reqDTO);
/**
* 取消绑定社交用户
*
* @param userId 用户编号
* @param userType 全局用户类型
* @param socialType 社交平台的类型 {@link SocialTypeEnum}
* @param openid 社交平台的 openid
*/
void unbindSocialUser(Long userId, Integer userType, Integer socialType, String openid);
/**
* 获得社交用户基于 userId
*
* @param userType 用户类型
* @param userId 用户编号
* @param socialType 社交平台的类型
* @return 社交用户
*/
SocialUserRespDTO getSocialUserByUserId(Integer userType, Long userId, Integer socialType);
/**
* 获得社交用户
*
* 在认证信息不正确的情况下也会抛出 {@link ServiceException} 业务异常
*
* @param userType 用户类型
* @param socialType 社交平台的类型
* @param code 授权码
* @param state state
* @return 社交用户
*/
SocialUserRespDTO getSocialUserByCode(Integer userType, Integer socialType, String code, String state);
// ==================== 社交用户 CRUD ====================
/**
* 获得社交用户
*
* @param id 编号
* @return 社交用户
*/
SocialUserDO getSocialUser(Long id);
/**
* 获得社交用户分页
*
* @param pageReqVO 分页查询
* @return 社交用户分页
*/
PageResult<SocialUserDO> getSocialUserPage(SocialUserPageReqVO pageReqVO);
}

173
cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialUserServiceImpl.java

@ -0,0 +1,173 @@
package cn.iocoder.yudao.module.system.service.social;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.iocoder.yudao.framework.common.exception.ServiceException;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO;
import cn.iocoder.yudao.module.system.api.social.dto.SocialUserRespDTO;
import cn.iocoder.yudao.module.system.controller.admin.socail.vo.user.SocialUserPageReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserBindDO;
import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserDO;
import cn.iocoder.yudao.module.system.dal.mysql.social.SocialUserBindMapper;
import cn.iocoder.yudao.module.system.dal.mysql.social.SocialUserMapper;
import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum;
import jakarta.annotation.Resource;
import jakarta.validation.constraints.NotNull;
import lombok.extern.slf4j.Slf4j;
import me.zhyd.oauth.model.AuthUser;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import java.util.Collections;
import java.util.List;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SOCIAL_USER_NOT_FOUND;
/**
* 社交用户 Service 实现类
*
* @author 芋道源码
*/
@Service
@Validated
@Slf4j
public class SocialUserServiceImpl implements SocialUserService {
@Resource
private SocialUserBindMapper socialUserBindMapper;
@Resource
private SocialUserMapper socialUserMapper;
@Resource
private SocialClientService socialClientService;
@Override
public List<SocialUserDO> getSocialUserList(Long userId, Integer userType) {
// 获得绑定
List<SocialUserBindDO> socialUserBinds = socialUserBindMapper.selectListByUserIdAndUserType(userId, userType);
if (CollUtil.isEmpty(socialUserBinds)) {
return Collections.emptyList();
}
// 获得社交用户
return socialUserMapper.selectByIds(convertSet(socialUserBinds, SocialUserBindDO::getSocialUserId));
}
@Override
@Transactional(rollbackFor = Exception.class)
public String bindSocialUser(SocialUserBindReqDTO reqDTO) {
// 获得社交用户
SocialUserDO socialUser = authSocialUser(reqDTO.getSocialType(), reqDTO.getUserType(),
reqDTO.getCode(), reqDTO.getState());
Assert.notNull(socialUser, "社交用户不能为空");
// 社交用户可能之前绑定过别的用户,需要进行解绑
socialUserBindMapper.deleteByUserTypeAndSocialUserId(reqDTO.getUserType(), socialUser.getId());
// 用户可能之前已经绑定过该社交类型,需要进行解绑
socialUserBindMapper.deleteByUserTypeAndUserIdAndSocialType(reqDTO.getUserType(), reqDTO.getUserId(),
socialUser.getType());
// 绑定当前登录的社交用户
SocialUserBindDO socialUserBind = SocialUserBindDO.builder()
.userId(reqDTO.getUserId()).userType(reqDTO.getUserType())
.socialUserId(socialUser.getId()).socialType(socialUser.getType()).build();
socialUserBindMapper.insert(socialUserBind);
return socialUser.getOpenid();
}
@Override
public void unbindSocialUser(Long userId, Integer userType, Integer socialType, String openid) {
// 获得 openid 对应的 SocialUserDO 社交用户
SocialUserDO socialUser = socialUserMapper.selectByTypeAndOpenid(socialType, openid);
if (socialUser == null) {
throw exception(SOCIAL_USER_NOT_FOUND);
}
// 获得对应的社交绑定关系
socialUserBindMapper.deleteByUserTypeAndUserIdAndSocialType(userType, userId, socialUser.getType());
}
@Override
public SocialUserRespDTO getSocialUserByUserId(Integer userType, Long userId, Integer socialType) {
// 获得绑定用户
SocialUserBindDO socialUserBind = socialUserBindMapper.selectByUserIdAndUserTypeAndSocialType(userId, userType, socialType);
if (socialUserBind == null) {
return null;
}
// 获得社交用户
SocialUserDO socialUser = socialUserMapper.selectById(socialUserBind.getSocialUserId());
Assert.notNull(socialUser, "社交用户不能为空");
return new SocialUserRespDTO(socialUser.getOpenid(), socialUser.getNickname(), socialUser.getAvatar(),
socialUserBind.getUserId());
}
@Override
public SocialUserRespDTO getSocialUserByCode(Integer userType, Integer socialType, String code, String state) {
// 获得社交用户
SocialUserDO socialUser = authSocialUser(socialType, userType, code, state);
Assert.notNull(socialUser, "社交用户不能为空");
// 获得绑定用户
SocialUserBindDO socialUserBind = socialUserBindMapper.selectByUserTypeAndSocialUserId(userType,
socialUser.getId());
return new SocialUserRespDTO(socialUser.getOpenid(), socialUser.getNickname(), socialUser.getAvatar(),
socialUserBind != null ? socialUserBind.getUserId() : null);
}
/**
* 授权获得对应的社交用户
* 如果授权失败则会抛出 {@link ServiceException} 异常
*
* @param socialType 社交平台的类型 {@link SocialTypeEnum}
* @param userType 用户类型
* @param code 授权码
* @param state state
* @return 授权用户
*/
@NotNull
public SocialUserDO authSocialUser(Integer socialType, Integer userType, String code, String state) {
// 优先从 DB 中获取,因为 code 有且可以使用一次。
// 在社交登录时,当未绑定 User 时,需要绑定登录,此时需要 code 使用两次
SocialUserDO socialUser = socialUserMapper.selectByTypeAndCodeAnState(socialType, code, state);
if (socialUser != null) {
return socialUser;
}
// 请求获取
AuthUser authUser = socialClientService.getAuthUser(socialType, userType, code, state);
Assert.notNull(authUser, "三方用户不能为空");
// 保存到 DB 中
socialUser = socialUserMapper.selectByTypeAndOpenid(socialType, authUser.getUuid());
if (socialUser == null) {
socialUser = new SocialUserDO();
}
socialUser.setType(socialType).setCode(code).setState(state) // 需要保存 code + state 字段,保证后续可查询
.setOpenid(authUser.getUuid()).setToken(authUser.getToken().getAccessToken()).setRawTokenInfo((toJsonString(authUser.getToken())))
.setNickname(authUser.getNickname()).setAvatar(authUser.getAvatar()).setRawUserInfo(toJsonString(authUser.getRawUserInfo()));
if (socialUser.getId() == null) {
socialUserMapper.insert(socialUser);
} else {
socialUserMapper.updateById(socialUser);
}
return socialUser;
}
// ==================== 社交用户 CRUD ====================
@Override
public SocialUserDO getSocialUser(Long id) {
return socialUserMapper.selectById(id);
}
@Override
public PageResult<SocialUserDO> getSocialUserPage(SocialUserPageReqVO pageReqVO) {
return socialUserMapper.selectPage(pageReqVO);
}
}

10
cc-admin-master/yudao-server/src/main/resources/application-local.yaml

@ -48,9 +48,9 @@ spring:
primary: master primary: master
datasource: datasource:
master: master:
url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例
url: jdbc:mysql://192.168.0.180:3306/lock-dev?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例
username: root username: root
password: roomasd111
password: Gsking164411
driver-class-name: com.mysql.cj.jdbc.Driver # MySQL Connector/J 8.X 连接的示例 driver-class-name: com.mysql.cj.jdbc.Driver # MySQL Connector/J 8.X 连接的示例
# tdengine: # tdengine:
# url: jdbc:TAOS-RS://192.168.0.180:6041/test # url: jdbc:TAOS-RS://192.168.0.180:6041/test
@ -180,6 +180,12 @@ wx:
app-id: wxf56b1542b9e85f8a # 测试号(Kongdy 提供的) app-id: wxf56b1542b9e85f8a # 测试号(Kongdy 提供的)
secret: 496379dcef1ba869e9234de8d598cfd3 secret: 496379dcef1ba869e9234de8d598cfd3
# 存储配置,解决 AccessToken 的跨节点的共享 # 存储配置,解决 AccessToken 的跨节点的共享
cp:
# 你的企业ID
corpId: ww6e1eee0a8ae45397
agentId: 1000002
corpSecret: ITbfuoZkmUifGoDL5ZB8SyuMzVM8VXZNkfZJzYn5sGo
config-storage: config-storage:
type: RedisTemplate # 采用 RedisTemplate 操作 Redis,会自动从 Spring 中获取 type: RedisTemplate # 采用 RedisTemplate 操作 Redis,会自动从 Spring 中获取
key-prefix: wx # Redis Key 的前缀 key-prefix: wx # Redis Key 的前缀

Loading…
Cancel
Save