mirror of
				https://gitee.com/hhyykk/ipms-sjy.git
				synced 2025-11-04 04:08:43 +08:00 
			
		
		
		
	Merge branch 'feature/mall_product' of https://gitee.com/zhijiantianya/ruoyi-vue-pro
This commit is contained in:
		@@ -0,0 +1,43 @@
 | 
			
		||||
package cn.iocoder.yudao.module.system.api.social;
 | 
			
		||||
 | 
			
		||||
import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
 | 
			
		||||
import cn.iocoder.yudao.module.system.api.social.dto.SocialWxJsapiSignatureRespDTO;
 | 
			
		||||
import cn.iocoder.yudao.module.system.api.social.dto.SocialWxPhoneNumberInfoRespDTO;
 | 
			
		||||
import cn.iocoder.yudao.module.system.convert.social.SocialClientConvert;
 | 
			
		||||
import cn.iocoder.yudao.module.system.service.social.SocialClientService;
 | 
			
		||||
import me.chanjar.weixin.common.bean.WxJsapiSignature;
 | 
			
		||||
import org.springframework.stereotype.Service;
 | 
			
		||||
import org.springframework.validation.annotation.Validated;
 | 
			
		||||
 | 
			
		||||
import javax.annotation.Resource;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 社交应用的 API 实现类
 | 
			
		||||
 *
 | 
			
		||||
 * @author 芋道源码
 | 
			
		||||
 */
 | 
			
		||||
@Service
 | 
			
		||||
@Validated
 | 
			
		||||
public class SocialClientApiImpl implements SocialClientApi {
 | 
			
		||||
 | 
			
		||||
    @Resource
 | 
			
		||||
    private SocialClientService socialClientService;
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String getAuthorizeUrl(Integer type, Integer userType, String redirectUri) {
 | 
			
		||||
        return socialClientService.getAuthorizeUrl(type, userType, redirectUri);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public SocialWxJsapiSignatureRespDTO createWxMpJsapiSignature(Integer userType, String url) {
 | 
			
		||||
        WxJsapiSignature signature = socialClientService.createWxMpJsapiSignature(userType, url);
 | 
			
		||||
        return SocialClientConvert.INSTANCE.convert(signature);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public SocialWxPhoneNumberInfoRespDTO getWxMaPhoneNumberInfo(Integer userType, String phoneCode) {
 | 
			
		||||
        WxMaPhoneNumberInfo info = socialClientService.getWxMaPhoneNumberInfo(userType, phoneCode);
 | 
			
		||||
        return SocialClientConvert.INSTANCE.convert(info);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -21,11 +21,6 @@ public class SocialUserApiImpl implements SocialUserApi {
 | 
			
		||||
    @Resource
 | 
			
		||||
    private SocialUserService socialUserService;
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String getAuthorizeUrl(Integer type, String redirectUri) {
 | 
			
		||||
        return socialUserService.getAuthorizeUrl(type, redirectUri);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String bindSocialUser(SocialUserBindReqDTO reqDTO) {
 | 
			
		||||
        return socialUserService.bindSocialUser(reqDTO);
 | 
			
		||||
@@ -34,7 +29,7 @@ public class SocialUserApiImpl implements SocialUserApi {
 | 
			
		||||
    @Override
 | 
			
		||||
    public void unbindSocialUser(SocialUserUnbindReqDTO reqDTO) {
 | 
			
		||||
        socialUserService.unbindSocialUser(reqDTO.getUserId(), reqDTO.getUserType(),
 | 
			
		||||
                reqDTO.getType(), reqDTO.getUnionId());
 | 
			
		||||
                reqDTO.getSocialType(), reqDTO.getOpenid());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
 
 | 
			
		||||
@@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.system.controller.admin.auth;
 | 
			
		||||
import cn.hutool.core.collection.CollUtil;
 | 
			
		||||
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.pojo.CommonResult;
 | 
			
		||||
import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
 | 
			
		||||
import cn.iocoder.yudao.framework.security.config.SecurityProperties;
 | 
			
		||||
@@ -16,12 +17,12 @@ 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.PermissionService;
 | 
			
		||||
import cn.iocoder.yudao.module.system.service.permission.RoleService;
 | 
			
		||||
import cn.iocoder.yudao.module.system.service.social.SocialUserService;
 | 
			
		||||
import cn.iocoder.yudao.module.system.service.social.SocialClientService;
 | 
			
		||||
import cn.iocoder.yudao.module.system.service.user.AdminUserService;
 | 
			
		||||
import io.swagger.v3.oas.annotations.tags.Tag;
 | 
			
		||||
import io.swagger.v3.oas.annotations.Operation;
 | 
			
		||||
import io.swagger.v3.oas.annotations.Parameter;
 | 
			
		||||
import io.swagger.v3.oas.annotations.Parameters;
 | 
			
		||||
import io.swagger.v3.oas.annotations.Operation;
 | 
			
		||||
import io.swagger.v3.oas.annotations.tags.Tag;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import org.springframework.validation.annotation.Validated;
 | 
			
		||||
import org.springframework.web.bind.annotation.*;
 | 
			
		||||
@@ -30,6 +31,7 @@ import javax.annotation.Resource;
 | 
			
		||||
import javax.annotation.security.PermitAll;
 | 
			
		||||
import javax.servlet.http.HttpServletRequest;
 | 
			
		||||
import javax.validation.Valid;
 | 
			
		||||
import java.util.Collections;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
 | 
			
		||||
@@ -56,7 +58,7 @@ public class AuthController {
 | 
			
		||||
    @Resource
 | 
			
		||||
    private PermissionService permissionService;
 | 
			
		||||
    @Resource
 | 
			
		||||
    private SocialUserService socialUserService;
 | 
			
		||||
    private SocialClientService socialClientService;
 | 
			
		||||
 | 
			
		||||
    @Resource
 | 
			
		||||
    private SecurityProperties securityProperties;
 | 
			
		||||
@@ -101,6 +103,9 @@ public class AuthController {
 | 
			
		||||
 | 
			
		||||
        // 1.2 获得角色列表
 | 
			
		||||
        Set<Long> roleIds = permissionService.getUserRoleIdListByUserId(getLoginUserId());
 | 
			
		||||
        if (CollUtil.isEmpty(roleIds)) {
 | 
			
		||||
            return success(AuthConvert.INSTANCE.convert(user, Collections.emptyList(), Collections.emptyList()));
 | 
			
		||||
        }
 | 
			
		||||
        List<RoleDO> roles = roleService.getRoleList(roleIds);
 | 
			
		||||
        roles.removeIf(role -> !CommonStatusEnum.ENABLE.getStatus().equals(role.getStatus())); // 移除禁用的角色
 | 
			
		||||
 | 
			
		||||
@@ -143,7 +148,7 @@ public class AuthController {
 | 
			
		||||
    })
 | 
			
		||||
    public CommonResult<String> socialLogin(@RequestParam("type") Integer type,
 | 
			
		||||
                                            @RequestParam("redirectUri") String redirectUri) {
 | 
			
		||||
        return CommonResult.success(socialUserService.getAuthorizeUrl(type, redirectUri));
 | 
			
		||||
        return success(socialClientService.getAuthorizeUrl(type, UserTypeEnum.ADMIN.getValue(), redirectUri));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @PostMapping("/social-login")
 | 
			
		||||
@@ -154,4 +159,4 @@ public class AuthController {
 | 
			
		||||
        return success(authService.socialLogin(reqVO));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
package cn.iocoder.yudao.module.system.controller.admin.notify;
 | 
			
		||||
 | 
			
		||||
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.module.system.controller.admin.notify.vo.template.*;
 | 
			
		||||
@@ -76,8 +77,12 @@ public class NotifyTemplateController {
 | 
			
		||||
    @Operation(summary = "发送站内信")
 | 
			
		||||
    @PreAuthorize("@ss.hasPermission('system:notify-template:send-notify')")
 | 
			
		||||
    public CommonResult<Long> sendNotify(@Valid @RequestBody NotifyTemplateSendReqVO sendReqVO) {
 | 
			
		||||
        return success(notifySendService.sendSingleNotifyToAdmin(sendReqVO.getUserId(),
 | 
			
		||||
                sendReqVO.getTemplateCode(), sendReqVO.getTemplateParams()));
 | 
			
		||||
        if (UserTypeEnum.MEMBER.getValue().equals(sendReqVO.getUserType())) {
 | 
			
		||||
            return success(notifySendService.sendSingleNotifyToMember(sendReqVO.getUserId(),
 | 
			
		||||
                    sendReqVO.getTemplateCode(), sendReqVO.getTemplateParams()));
 | 
			
		||||
        } else {
 | 
			
		||||
            return success(notifySendService.sendSingleNotifyToAdmin(sendReqVO.getUserId(),
 | 
			
		||||
                    sendReqVO.getTemplateCode(), sendReqVO.getTemplateParams()));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -15,6 +15,10 @@ public class NotifyTemplateSendReqVO {
 | 
			
		||||
    @NotNull(message = "用户id不能为空")
 | 
			
		||||
    private Long userId;
 | 
			
		||||
 | 
			
		||||
    @Schema(description = "用户类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
 | 
			
		||||
    @NotNull(message = "用户类型不能为空")
 | 
			
		||||
    private Integer userType;
 | 
			
		||||
 | 
			
		||||
    @Schema(description = "模板编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "01")
 | 
			
		||||
    @NotEmpty(message = "模板编码不能为空")
 | 
			
		||||
    private String templateCode;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +0,0 @@
 | 
			
		||||
/**
 | 
			
		||||
 * 占位,避免 package 无法提交到 Git 仓库
 | 
			
		||||
 */
 | 
			
		||||
package cn.iocoder.yudao.module.system.controller.app;
 | 
			
		||||
@@ -1,4 +0,0 @@
 | 
			
		||||
### 请求 /login 接口 => 成功
 | 
			
		||||
POST {{appApi}}/system/wx-mp/create-jsapi-signature?url=http://www.iocoder.cn
 | 
			
		||||
Authorization: Bearer {{appToken}}
 | 
			
		||||
tenant-id: {{appTenentId}}
 | 
			
		||||
@@ -1,38 +0,0 @@
 | 
			
		||||
package cn.iocoder.yudao.module.system.controller.app.weixin;
 | 
			
		||||
 | 
			
		||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
 | 
			
		||||
import io.swagger.v3.oas.annotations.tags.Tag;
 | 
			
		||||
import io.swagger.v3.oas.annotations.Operation;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import me.chanjar.weixin.common.bean.WxJsapiSignature;
 | 
			
		||||
import me.chanjar.weixin.common.error.WxErrorException;
 | 
			
		||||
import me.chanjar.weixin.mp.api.WxMpService;
 | 
			
		||||
import org.springframework.validation.annotation.Validated;
 | 
			
		||||
import org.springframework.web.bind.annotation.PostMapping;
 | 
			
		||||
import org.springframework.web.bind.annotation.RequestMapping;
 | 
			
		||||
import org.springframework.web.bind.annotation.RequestParam;
 | 
			
		||||
import org.springframework.web.bind.annotation.RestController;
 | 
			
		||||
 | 
			
		||||
import javax.annotation.Resource;
 | 
			
		||||
 | 
			
		||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
 | 
			
		||||
 | 
			
		||||
@Tag(name = "微信公众号")
 | 
			
		||||
@RestController
 | 
			
		||||
@RequestMapping("/system/wx-mp")
 | 
			
		||||
@Validated
 | 
			
		||||
@Slf4j
 | 
			
		||||
public class AppWxMpController {
 | 
			
		||||
 | 
			
		||||
    @Resource
 | 
			
		||||
    private WxMpService mpService;
 | 
			
		||||
 | 
			
		||||
    // TODO @芋艿:需要额外考虑个问题;多租户下,如果每个小程序一个微信公众号,则会存在多个 appid;
 | 
			
		||||
    @PostMapping("/create-jsapi-signature")
 | 
			
		||||
    @Operation(summary = "创建微信 JS SDK 初始化所需的签名",
 | 
			
		||||
        description = "参考 https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html 文档")
 | 
			
		||||
    public CommonResult<WxJsapiSignature> createJsapiSignature(@RequestParam("url") String url) throws WxErrorException {
 | 
			
		||||
        return success(mpService.createJsapiSignature(url));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
package cn.iocoder.yudao.module.system.convert.auth;
 | 
			
		||||
 | 
			
		||||
import cn.hutool.core.collection.CollUtil;
 | 
			
		||||
import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeSendReqDTO;
 | 
			
		||||
import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeUseReqDTO;
 | 
			
		||||
import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO;
 | 
			
		||||
@@ -46,6 +47,9 @@ public interface AuthConvert {
 | 
			
		||||
     * @return 菜单树
 | 
			
		||||
     */
 | 
			
		||||
    default List<AuthPermissionInfoRespVO.MenuVO> buildMenuTree(List<MenuDO> menuList) {
 | 
			
		||||
        if (CollUtil.isEmpty(menuList)) {
 | 
			
		||||
            return Collections.emptyList();
 | 
			
		||||
        }
 | 
			
		||||
        // 移除按钮
 | 
			
		||||
        menuList.removeIf(menu -> menu.getType().equals(MenuTypeEnum.BUTTON.getType()));
 | 
			
		||||
        // 排序,保证菜单的有序性
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,19 @@
 | 
			
		||||
package cn.iocoder.yudao.module.system.convert.social;
 | 
			
		||||
 | 
			
		||||
import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
 | 
			
		||||
import cn.iocoder.yudao.module.system.api.social.dto.SocialWxJsapiSignatureRespDTO;
 | 
			
		||||
import cn.iocoder.yudao.module.system.api.social.dto.SocialWxPhoneNumberInfoRespDTO;
 | 
			
		||||
import me.chanjar.weixin.common.bean.WxJsapiSignature;
 | 
			
		||||
import org.mapstruct.Mapper;
 | 
			
		||||
import org.mapstruct.factory.Mappers;
 | 
			
		||||
 | 
			
		||||
@Mapper
 | 
			
		||||
public interface SocialClientConvert {
 | 
			
		||||
 | 
			
		||||
    SocialClientConvert INSTANCE = Mappers.getMapper(SocialClientConvert.class);
 | 
			
		||||
 | 
			
		||||
    SocialWxJsapiSignatureRespDTO convert(WxJsapiSignature bean);
 | 
			
		||||
 | 
			
		||||
    SocialWxPhoneNumberInfoRespDTO convert(WxMaPhoneNumberInfo bean);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,68 @@
 | 
			
		||||
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 com.xingyuv.jushauth.config.AuthConfig;
 | 
			
		||||
import lombok.*;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 社交客户端 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;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,15 @@
 | 
			
		||||
package cn.iocoder.yudao.module.system.dal.mysql.social;
 | 
			
		||||
 | 
			
		||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
 | 
			
		||||
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);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,58 @@
 | 
			
		||||
package cn.iocoder.yudao.module.system.service.social;
 | 
			
		||||
 | 
			
		||||
import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
 | 
			
		||||
import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum;
 | 
			
		||||
import com.xingyuv.jushauth.model.AuthUser;
 | 
			
		||||
import me.chanjar.weixin.common.bean.WxJsapiSignature;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 社交应用 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);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,258 @@
 | 
			
		||||
package cn.iocoder.yudao.module.system.service.social;
 | 
			
		||||
 | 
			
		||||
import cn.binarywang.wx.miniapp.api.WxMaService;
 | 
			
		||||
import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl;
 | 
			
		||||
import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
 | 
			
		||||
import cn.binarywang.wx.miniapp.config.impl.WxMaRedisBetterConfigImpl;
 | 
			
		||||
import cn.hutool.core.bean.BeanUtil;
 | 
			
		||||
import cn.hutool.core.lang.Assert;
 | 
			
		||||
import cn.hutool.core.util.ReflectUtil;
 | 
			
		||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
 | 
			
		||||
import cn.iocoder.yudao.framework.common.util.cache.CacheUtils;
 | 
			
		||||
import cn.iocoder.yudao.framework.common.util.http.HttpUtils;
 | 
			
		||||
import cn.iocoder.yudao.framework.social.core.YudaoAuthRequestFactory;
 | 
			
		||||
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.enums.social.SocialTypeEnum;
 | 
			
		||||
import com.binarywang.spring.starter.wxjava.miniapp.properties.WxMaProperties;
 | 
			
		||||
import com.binarywang.spring.starter.wxjava.mp.properties.WxMpProperties;
 | 
			
		||||
import com.google.common.cache.CacheLoader;
 | 
			
		||||
import com.google.common.cache.LoadingCache;
 | 
			
		||||
import com.xingyuv.jushauth.config.AuthConfig;
 | 
			
		||||
import com.xingyuv.jushauth.model.AuthCallback;
 | 
			
		||||
import com.xingyuv.jushauth.model.AuthResponse;
 | 
			
		||||
import com.xingyuv.jushauth.model.AuthUser;
 | 
			
		||||
import com.xingyuv.jushauth.request.AuthRequest;
 | 
			
		||||
import com.xingyuv.jushauth.utils.AuthStateUtils;
 | 
			
		||||
import lombok.SneakyThrows;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import me.chanjar.weixin.common.bean.WxJsapiSignature;
 | 
			
		||||
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 org.springframework.data.redis.core.StringRedisTemplate;
 | 
			
		||||
import org.springframework.stereotype.Service;
 | 
			
		||||
 | 
			
		||||
import javax.annotation.Resource;
 | 
			
		||||
import java.time.Duration;
 | 
			
		||||
import java.util.Objects;
 | 
			
		||||
 | 
			
		||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
 | 
			
		||||
import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
 | 
			
		||||
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SOCIAL_APP_WEIXIN_MINI_APP_PHONE_CODE_ERROR;
 | 
			
		||||
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SOCIAL_USER_AUTH_FAILURE;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 社交应用 Service 实现类
 | 
			
		||||
 *
 | 
			
		||||
 * @author 芋道源码
 | 
			
		||||
 */
 | 
			
		||||
@Service
 | 
			
		||||
@Slf4j
 | 
			
		||||
public class SocialClientServiceImpl implements SocialClientService {
 | 
			
		||||
 | 
			
		||||
    @Resource // 由于自定义了 YudaoAuthRequestFactory 无法覆盖默认的 AuthRequestFactory,所以只能注入它
 | 
			
		||||
    private YudaoAuthRequestFactory yudaoAuthRequestFactory;
 | 
			
		||||
 | 
			
		||||
    @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 对象
 | 
			
		||||
     */
 | 
			
		||||
    private AuthRequest buildAuthRequest(Integer socialType, Integer userType) {
 | 
			
		||||
        // 1. 先查找默认的配置项,从 application-*.yaml 中读取
 | 
			
		||||
        AuthRequest request = yudaoAuthRequestFactory.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());
 | 
			
		||||
            // 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 对象
 | 
			
		||||
     */
 | 
			
		||||
    private 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 对象
 | 
			
		||||
     */
 | 
			
		||||
    private 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().getPhoneNoInfo(phoneCode);
 | 
			
		||||
        } catch (WxErrorException e) {
 | 
			
		||||
            log.error("[getPhoneNoInfo][userType({}) phoneCode({}) 获得手机号失败]", userType, phoneCode, e);
 | 
			
		||||
            throw exception(SOCIAL_APP_WEIXIN_MINI_APP_PHONE_CODE_ERROR);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获得 clientId + clientSecret 对应的 WxMpService 对象
 | 
			
		||||
     *
 | 
			
		||||
     * @param userType 用户类型
 | 
			
		||||
     * @return WxMpService 对象
 | 
			
		||||
     */
 | 
			
		||||
    private WxMaService getWxMaService(Integer userType) {
 | 
			
		||||
        // 第一步,查询 DB 的配置项,获得对应的 WxMaService 对象
 | 
			
		||||
        SocialClientDO client = socialClientMapper.selectBySocialTypeAndUserType(
 | 
			
		||||
                SocialTypeEnum.WECHAT_MINI_APP.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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -7,7 +7,6 @@ import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserDO;
 | 
			
		||||
import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum;
 | 
			
		||||
 | 
			
		||||
import javax.validation.Valid;
 | 
			
		||||
import javax.validation.constraints.NotNull;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -18,27 +17,6 @@ import java.util.List;
 | 
			
		||||
public interface SocialUserService {
 | 
			
		||||
 | 
			
		||||
    // TODO @芋艿:需要传递 userType
 | 
			
		||||
    /**
 | 
			
		||||
     * 获得社交平台的授权 URL
 | 
			
		||||
     *
 | 
			
		||||
     * @param type 社交平台的类型 {@link SocialTypeEnum}
 | 
			
		||||
     * @param redirectUri 重定向 URL
 | 
			
		||||
     * @return 社交平台的授权 URL
 | 
			
		||||
     */
 | 
			
		||||
    String getAuthorizeUrl(Integer type, String redirectUri);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 授权获得对应的社交用户
 | 
			
		||||
     * 如果授权失败,则会抛出 {@link ServiceException} 异常
 | 
			
		||||
     *
 | 
			
		||||
     * @param type 社交平台的类型 {@link SocialTypeEnum}
 | 
			
		||||
     * @param code 授权码
 | 
			
		||||
     * @param state state
 | 
			
		||||
     * @return 授权用户
 | 
			
		||||
     */
 | 
			
		||||
    @NotNull
 | 
			
		||||
    SocialUserDO authSocialUser(Integer type, String code, String state);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获得指定用户的社交用户列表
 | 
			
		||||
     *
 | 
			
		||||
 
 | 
			
		||||
@@ -2,8 +2,7 @@ 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.util.http.HttpUtils;
 | 
			
		||||
import cn.iocoder.yudao.framework.social.core.YudaoAuthRequestFactory;
 | 
			
		||||
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.dal.dataobject.social.SocialUserBindDO;
 | 
			
		||||
@@ -11,24 +10,22 @@ 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 com.xingyuv.jushauth.model.AuthCallback;
 | 
			
		||||
import com.xingyuv.jushauth.model.AuthResponse;
 | 
			
		||||
import com.xingyuv.jushauth.model.AuthUser;
 | 
			
		||||
import com.xingyuv.jushauth.request.AuthRequest;
 | 
			
		||||
import com.xingyuv.jushauth.utils.AuthStateUtils;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import org.springframework.stereotype.Service;
 | 
			
		||||
import org.springframework.transaction.annotation.Transactional;
 | 
			
		||||
import org.springframework.validation.annotation.Validated;
 | 
			
		||||
 | 
			
		||||
import javax.annotation.Resource;
 | 
			
		||||
import javax.validation.constraints.NotNull;
 | 
			
		||||
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.*;
 | 
			
		||||
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.AUTH_THIRD_LOGIN_NOT_BIND;
 | 
			
		||||
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SOCIAL_USER_NOT_FOUND;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 社交用户 Service 实现类
 | 
			
		||||
@@ -40,51 +37,13 @@ import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
 | 
			
		||||
@Slf4j
 | 
			
		||||
public class SocialUserServiceImpl implements SocialUserService {
 | 
			
		||||
 | 
			
		||||
    @Resource// 由于自定义了 YudaoAuthRequestFactory 无法覆盖默认的 AuthRequestFactory,所以只能注入它
 | 
			
		||||
    private YudaoAuthRequestFactory yudaoAuthRequestFactory;
 | 
			
		||||
 | 
			
		||||
    @Resource
 | 
			
		||||
    private SocialUserBindMapper socialUserBindMapper;
 | 
			
		||||
    @Resource
 | 
			
		||||
    private SocialUserMapper socialUserMapper;
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String getAuthorizeUrl(Integer type, String redirectUri) {
 | 
			
		||||
        // 获得对应的 AuthRequest 实现
 | 
			
		||||
        AuthRequest authRequest = yudaoAuthRequestFactory.get(SocialTypeEnum.valueOfType(type).getSource());
 | 
			
		||||
        // 生成跳转地址
 | 
			
		||||
        String authorizeUri = authRequest.authorize(AuthStateUtils.createState());
 | 
			
		||||
        return HttpUtils.replaceUrlQuery(authorizeUri, "redirect_uri", redirectUri);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public SocialUserDO authSocialUser(Integer type, String code, String state) {
 | 
			
		||||
        // 优先从 DB 中获取,因为 code 有且可以使用一次。
 | 
			
		||||
        // 在社交登录时,当未绑定 User 时,需要绑定登录,此时需要 code 使用两次
 | 
			
		||||
        SocialUserDO socialUser = socialUserMapper.selectByTypeAndCodeAnState(type, code, state);
 | 
			
		||||
        if (socialUser != null) {
 | 
			
		||||
            return socialUser;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 请求获取
 | 
			
		||||
        AuthUser authUser = getAuthUser(type, code, state);
 | 
			
		||||
        Assert.notNull(authUser, "三方用户不能为空");
 | 
			
		||||
 | 
			
		||||
        // 保存到 DB 中
 | 
			
		||||
        socialUser = socialUserMapper.selectByTypeAndOpenid(type, authUser.getUuid());
 | 
			
		||||
        if (socialUser == null) {
 | 
			
		||||
            socialUser = new SocialUserDO();
 | 
			
		||||
        }
 | 
			
		||||
        socialUser.setType(type).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;
 | 
			
		||||
    }
 | 
			
		||||
    @Resource
 | 
			
		||||
    private SocialClientService socialClientService;
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public List<SocialUserDO> getSocialUserList(Long userId, Integer userType) {
 | 
			
		||||
@@ -101,7 +60,8 @@ public class SocialUserServiceImpl implements SocialUserService {
 | 
			
		||||
    @Transactional
 | 
			
		||||
    public String bindSocialUser(SocialUserBindReqDTO reqDTO) {
 | 
			
		||||
        // 获得社交用户
 | 
			
		||||
        SocialUserDO socialUser = authSocialUser(reqDTO.getType(), reqDTO.getCode(), reqDTO.getState());
 | 
			
		||||
        SocialUserDO socialUser = authSocialUser(reqDTO.getSocialType(), reqDTO.getUserType(),
 | 
			
		||||
                reqDTO.getCode(), reqDTO.getState());
 | 
			
		||||
        Assert.notNull(socialUser, "社交用户不能为空");
 | 
			
		||||
 | 
			
		||||
        // 社交用户可能之前绑定过别的用户,需要进行解绑
 | 
			
		||||
@@ -134,7 +94,7 @@ public class SocialUserServiceImpl implements SocialUserService {
 | 
			
		||||
    @Override
 | 
			
		||||
    public SocialUserRespDTO getSocialUser(Integer userType, Integer type, String code, String state) {
 | 
			
		||||
        // 获得社交用户
 | 
			
		||||
        SocialUserDO socialUser = authSocialUser(type, code, state);
 | 
			
		||||
        SocialUserDO socialUser = authSocialUser(type, userType, code, state);
 | 
			
		||||
        Assert.notNull(socialUser, "社交用户不能为空");
 | 
			
		||||
 | 
			
		||||
        // 如果未绑定的社交用户,则无法自动登录,进行报错
 | 
			
		||||
@@ -146,24 +106,44 @@ public class SocialUserServiceImpl implements SocialUserService {
 | 
			
		||||
        return new SocialUserRespDTO(socialUser.getOpenid(), socialUserBind.getUserId());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // TODO 芋艿:调整下单测
 | 
			
		||||
    /**
 | 
			
		||||
     * 请求社交平台,获得授权的用户
 | 
			
		||||
     * 授权获得对应的社交用户
 | 
			
		||||
     * 如果授权失败,则会抛出 {@link ServiceException} 异常
 | 
			
		||||
     *
 | 
			
		||||
     * @param type 社交平台的类型
 | 
			
		||||
     * @param socialType 社交平台的类型 {@link SocialTypeEnum}
 | 
			
		||||
     * @param userType 用户类型
 | 
			
		||||
     * @param code 授权码
 | 
			
		||||
     * @param state 授权 state
 | 
			
		||||
     * @return 授权的用户
 | 
			
		||||
     * @param state state
 | 
			
		||||
     * @return 授权用户
 | 
			
		||||
     */
 | 
			
		||||
    private AuthUser getAuthUser(Integer type, String code, String state) {
 | 
			
		||||
        AuthRequest authRequest = yudaoAuthRequestFactory.get(SocialTypeEnum.valueOfType(type).getSource());
 | 
			
		||||
        AuthCallback authCallback = AuthCallback.builder().code(code).state(state).build();
 | 
			
		||||
        AuthResponse<?> authResponse = authRequest.login(authCallback);
 | 
			
		||||
        log.info("[getAuthUser][请求社交平台 type({}) request({}) response({})]", type,
 | 
			
		||||
                toJsonString(authCallback), toJsonString(authResponse));
 | 
			
		||||
        if (!authResponse.ok()) {
 | 
			
		||||
            throw exception(SOCIAL_USER_AUTH_FAILURE, authResponse.getMsg());
 | 
			
		||||
    @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;
 | 
			
		||||
        }
 | 
			
		||||
        return (AuthUser) authResponse.getData();
 | 
			
		||||
 | 
			
		||||
        // 请求获取
 | 
			
		||||
        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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user