多模块重构 4:system 模块的创建,去除 Sys

This commit is contained in:
YunaiV
2022-01-30 00:49:31 +08:00
parent 117914d92b
commit ab6ec2f0ed
227 changed files with 1649 additions and 2252 deletions

View File

@@ -0,0 +1,16 @@
package cn.iocoder.yudao.coreservice.modules.infra.enums;
import cn.iocoder.yudao.framework.common.exception.ErrorCode;
/**
* System 错误码枚举类
*
* system 系统,使用 1-006-000-000 段
*/
public interface SysErrorCodeConstants {
// ========= 文件相关 1006001000=================
ErrorCode FILE_PATH_EXISTS = new ErrorCode(1006001000, "文件路径已存在");
ErrorCode FILE_NOT_EXISTS = new ErrorCode(1006001002, "文件不存在");
}

View File

@@ -1,4 +0,0 @@
/**
* 占位类,可以无视
*/
package cn.iocoder.yudao.coreservice.modules.infra.enums;

View File

@@ -10,7 +10,7 @@ import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.io.ByteArrayInputStream;
import static cn.iocoder.yudao.coreservice.modules.system.enums.SysErrorCodeConstants.*;
import static cn.iocoder.yudao.coreservice.modules.infra.enums.SysErrorCodeConstants.*;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
/**

View File

@@ -1,20 +0,0 @@
package cn.iocoder.yudao.coreservice.modules.system.convert.dict;
import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.dict.SysDictDataDO;
import cn.iocoder.yudao.framework.dict.core.dto.DictDataRespDTO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import java.util.Collection;
import java.util.List;
@Mapper
public interface SysDictDataCoreConvert {
SysDictDataCoreConvert INSTANCE = Mappers.getMapper(SysDictDataCoreConvert.class);
DictDataRespDTO convert02(SysDictDataDO bean);
List<DictDataRespDTO> convertList03(Collection<SysDictDataDO> list);
}

View File

@@ -1,15 +0,0 @@
package cn.iocoder.yudao.coreservice.modules.system.convert.logger;
import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.logger.SysLoginLogDO;
import cn.iocoder.yudao.coreservice.modules.system.service.logger.dto.SysLoginLogCreateReqDTO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
@Mapper
public interface SysLoginLogCoreConvert {
SysLoginLogCoreConvert INSTANCE = Mappers.getMapper(SysLoginLogCoreConvert.class);
SysLoginLogDO convert(SysLoginLogCreateReqDTO bean);
}

View File

@@ -1,6 +0,0 @@
/**
* 提供 POJO 类的实体转换
*
* 目前使用 MapStruct 框架
*/
package cn.iocoder.yudao.coreservice.modules.system.convert;

View File

@@ -1,68 +0,0 @@
package cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.auth;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.security.core.LoginUser;
import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.Date;
/**
* 在线用户表
*
* 我们已经将 {@link LoginUser} 缓存在 Redis 当中。
* 这里额外存储在线用户到 MySQL 中,目的是为了方便管理界面可以灵活查询。
* 同时,通过定时轮询 SysUserSessionDO 表,可以主动删除 Redis 的缓存,因为 Redis 的过期删除是延迟的。
*
* @author 芋道源码
*/
@TableName(value = "sys_user_session", autoResultMap = true)
@Data
@Builder
@EqualsAndHashCode(callSuper = true)
public class SysUserSessionDO extends TenantBaseDO {
/**
* 会话编号, 即 sessionId
*/
@TableId(type = IdType.INPUT)
private String id;
/**
* 用户编号
*
* 关联 SysUserDO.id 或者 MbrUserDO.id
*/
private Long userId;
/**
* 用户类型
*
* 枚举 {@link UserTypeEnum}
*/
private Integer userType;
/**
* 用户账号
*
* 冗余,因为账号可以变更
*/
private String username;
/**
* 用户 IP
*/
private String userIp;
/**
* 浏览器 UA
*/
private String userAgent;
/**
* 会话超时时间
*/
private Date sessionTimeout;
}

View File

@@ -1,62 +0,0 @@
package cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.dept;
import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.user.SysUserDO;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 部门表
*
* @author ruoyi
* @author 芋道源码
*/
@TableName("sys_dept")
@Data
@EqualsAndHashCode(callSuper = true)
public class SysDeptDO extends TenantBaseDO {
/**
* 部门ID
*/
@TableId
private Long id;
/**
* 部门名称
*/
private String name;
/**
* 父部门ID
*
* 关联 {@link #id}
*/
private Long parentId;
/**
* 显示顺序
*/
private Integer sort;
/**
* 负责人
*
* 关联 {@link SysUserDO#getId()}
*/
private Long leaderUserId;
/**
* 联系电话
*/
private String phone;
/**
* 邮箱
*/
private String email;
/**
* 部门状态
*
* 枚举 {@link CommonStatusEnum}
*/
private Integer status;
}

View File

@@ -1,48 +0,0 @@
package cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.dept;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 岗位表
*
* @author ruoyi
*/
@TableName("sys_post")
@Data
@EqualsAndHashCode(callSuper = true)
public class SysPostDO extends TenantBaseDO {
/**
* 岗位序号
*/
@TableId
private Long id;
/**
* 岗位名称
*/
private String name;
/**
* 岗位编码
*/
private String code;
/**
* 岗位排序
*/
private Integer sort;
/**
* 状态
*
* 枚举 {@link CommonStatusEnum}
*/
private Integer status;
/**
* 备注
*/
private String remark;
}

View File

@@ -1,54 +0,0 @@
package cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.dict;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 字典数据表
*
* @author ruoyi
*/
@TableName("sys_dict_data")
@Data
@EqualsAndHashCode(callSuper = true)
public class SysDictDataDO extends BaseDO {
/**
* 字典数据编号
*/
@TableId
private Long id;
/**
* 字典排序
*/
private Integer sort;
/**
* 字典标签
*/
private String label;
/**
* 字典值
*/
private String value;
/**
* 字典类型
*
* 冗余 {@link SysDictDataDO#getDictType()}
*/
private String dictType;
/**
* 状态
*
* 枚举 {@link CommonStatusEnum}
*/
private Integer status;
/**
* 备注
*/
private String remark;
}

View File

@@ -1,70 +0,0 @@
package cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.logger;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.coreservice.modules.system.enums.logger.SysLoginLogTypeEnum;
import cn.iocoder.yudao.coreservice.modules.system.enums.logger.SysLoginResultEnum;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
/**
* 登录日志表
*
* 注意,包括登录和登出两种行为
*
* @author 芋道源码
*/
@TableName("sys_login_log")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class SysLoginLogDO extends BaseDO {
/**
* 日志主键
*/
private Long id;
/**
* 日志类型
*
* 枚举 {@link SysLoginLogTypeEnum}
*/
private Integer logType;
/**
* 链路追踪编号
*/
private String traceId;
/**
* 用户编号
*/
private Long userId;
/**
* 用户类型
*
* 枚举 {@link UserTypeEnum}
*/
private Integer userType;
/**
* 用户账号
*
* 冗余,因为账号可以变更
*/
private String username;
/**
* 登录结果
*
* 枚举 {@link SysLoginResultEnum}
*/
private Integer result;
/**
* 用户 IP
*/
private String userIp;
/**
* 浏览器 UA
*/
private String userAgent;
}

View File

@@ -1 +0,0 @@
package cn.iocoder.yudao.coreservice.modules.system.dal.dataobject;

View File

@@ -1,75 +0,0 @@
package cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.permission;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.framework.mybatis.core.type.JsonLongSetTypeHandler;
import cn.iocoder.yudao.framework.security.core.enums.DataScopeEnum;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.Set;
/**
* 角色 DO
*
* @author ruoyi
*/
@TableName(value = "sys_role", autoResultMap = true)
@Data
@EqualsAndHashCode(callSuper = true)
public class SysRoleDO extends BaseDO {
/**
* 角色ID
*/
@TableId
private Long id;
/**
* 角色名称
*/
private String name;
/**
* 角色标识
*
* 枚举
*/
private String code;
/**
* 角色排序
*/
private Integer sort;
/**
* 角色状态
*
* 枚举 {@link CommonStatusEnum}
*/
private Integer status;
/**
* 角色类型
*
* 枚举
*/
private Integer type;
/**
* 备注
*/
private String remark;
/**
* 数据范围
*
* 枚举 {@link DataScopeEnum}
*/
private Integer dataScope;
/**
* 数据范围(指定部门数组)
*
* 适用于 {@link #dataScope} 的值为 {@link DataScopeEnum#DEPT_CUSTOM} 时
*/
@TableField(typeHandler = JsonLongSetTypeHandler.class)
private Set<Long> dataScopeDeptIds;
}

View File

@@ -1,33 +0,0 @@
package cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.permission;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 用户和角色关联
*
* @author ruoyi
*/
@TableName("sys_user_role")
@Data
@EqualsAndHashCode(callSuper = true)
public class SysUserRoleDO extends BaseDO {
/**
* 自增主键
*/
@TableId
private Long id;
/**
* 用户 ID
*/
private Long userId;
/**
* 角色 ID
*/
private Long roleId;
}

View File

@@ -1,60 +0,0 @@
package cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.sms;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.framework.sms.core.enums.SmsChannelEnum;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
/**
* 短信渠道 DO
*
* @author zzf
* @since 2021-01-25
*/
@TableName(value = "sys_sms_channel", autoResultMap = true)
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class SysSmsChannelDO extends BaseDO {
/**
* 渠道编号
*/
private Long id;
/**
* 短信签名
*/
private String signature;
/**
* 渠道编码
*
* 枚举 {@link SmsChannelEnum}
*/
private String code;
/**
* 启用状态
*
* 枚举 {@link CommonStatusEnum}
*/
private Integer status;
/**
* 备注
*/
private String remark;
/**
* 短信 API 的账号
*/
private String apiKey;
/**
* 短信 API 的秘钥
*/
private String apiSecret;
/**
* 短信发送回调 URL
*/
private String callbackUrl;
}

View File

@@ -1,173 +0,0 @@
package cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.sms;
import cn.iocoder.yudao.coreservice.modules.system.enums.sms.SysSmsReceiveStatusEnum;
import cn.iocoder.yudao.coreservice.modules.system.enums.sms.SysSmsSendStatusEnum;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.framework.sms.core.enums.SmsFrameworkErrorCodeConstants;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
import lombok.*;
import java.util.Date;
import java.util.Map;
/**
* 短信日志 DO
*
* @author zzf
* @since 2021-01-25
*/
@TableName(value = "sys_sms_log", autoResultMap = true)
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class SysSmsLogDO extends BaseDO {
/**
* 自增编号
*/
private Long id;
// ========= 渠道相关字段 =========
/**
* 短信渠道编号
*
* 关联 {@link SysSmsChannelDO#getId()}
*/
private Long channelId;
/**
* 短信渠道编码
*
* 冗余 {@link SysSmsChannelDO#getCode()}
*/
private String channelCode;
// ========= 模板相关字段 =========
/**
* 模板编号
*
* 关联 {@link SysSmsTemplateDO#getId()}
*/
private Long templateId;
/**
* 模板编码
*
* 冗余 {@link SysSmsTemplateDO#getCode()}
*/
private String templateCode;
/**
* 短信类型
*
* 冗余 {@link SysSmsTemplateDO#getType()}
*/
private Integer templateType;
/**
* 基于 {@link SysSmsTemplateDO#getContent()} 格式化后的内容
*/
private String templateContent;
/**
* 基于 {@link SysSmsTemplateDO#getParams()} 输入后的参数
*/
@TableField(typeHandler = JacksonTypeHandler.class)
private Map<String, Object> templateParams;
/**
* 短信 API 的模板编号
*
* 冗余 {@link SysSmsTemplateDO#getApiTemplateId()}
*/
private String apiTemplateId;
// ========= 手机相关字段 =========
/**
* 手机号
*/
private String mobile;
/**
* 用户编号
*/
private Long userId;
/**
* 用户类型
*
* 枚举 {@link UserTypeEnum}
*/
private Integer userType;
// ========= 发送相关字段 =========
/**
* 发送状态
*
* 枚举 {@link SysSmsSendStatusEnum}
*/
private Integer sendStatus;
/**
* 发送时间
*/
private Date sendTime;
/**
* 发送结果的编码
*
* 枚举 {@link SmsFrameworkErrorCodeConstants}
*/
private Integer sendCode;
/**
* 发送结果的提示
*
* 一般情况下,使用 {@link SmsFrameworkErrorCodeConstants}
* 异常情况下,通过格式化 Exception 的提示存储
*/
private String sendMsg;
/**
* 短信 API 发送结果的编码
*
* 由于第三方的错误码可能是字符串,所以使用 String 类型
*/
private String apiSendCode;
/**
* 短信 API 发送失败的提示
*/
private String apiSendMsg;
/**
* 短信 API 发送返回的唯一请求 ID
*
* 用于和短信 API 进行定位于排错
*/
private String apiRequestId;
/**
* 短信 API 发送返回的序号
*
* 用于和短信 API 平台的发送记录关联
*/
private String apiSerialNo;
// ========= 接收相关字段 =========
/**
* 接收状态
*
* 枚举 {@link SysSmsReceiveStatusEnum}
*/
private Integer receiveStatus;
/**
* 接收时间
*/
private Date receiveTime;
/**
* 短信 API 接收结果的编码
*/
private String apiReceiveCode;
/**
* 短信 API 接收结果的提示
*/
private String apiReceiveMsg;
}

View File

@@ -1,89 +0,0 @@
package cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.sms;
import cn.iocoder.yudao.coreservice.modules.system.enums.sms.SysSmsTemplateTypeEnum;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import java.util.List;
/**
* 短信模板 DO
*
* @author zzf
* @since 2021-01-25
*/
@TableName(value = "sys_sms_template", autoResultMap = true)
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class SysSmsTemplateDO extends BaseDO {
/**
* 自增编号
*/
private Long id;
// ========= 模板相关字段 =========
/**
* 短信类型
*
* 枚举 {@link SysSmsTemplateTypeEnum}
*/
private Integer type;
/**
* 启用状态
*
* 枚举 {@link CommonStatusEnum}
*/
private Integer status;
/**
* 模板编码,保证唯一
*/
private String code;
/**
* 模板名称
*/
private String name;
/**
* 模板内容
*
* 内容的参数,使用 {} 包括,例如说 {name}
*/
private String content;
/**
* 参数数组(自动根据内容生成)
*/
@TableField(typeHandler = JacksonTypeHandler.class)
private List<String> params;
/**
* 备注
*/
private String remark;
/**
* 短信 API 的模板编号
*/
private String apiTemplateId;
// ========= 渠道相关字段 =========
/**
* 短信渠道编号
*
* 关联 {@link SysSmsChannelDO#getId()}
*/
private Long channelId;
/**
* 短信渠道编码
*
* 冗余 {@link SysSmsChannelDO#getCode()}
*/
private String channelCode;
}

View File

@@ -1,82 +0,0 @@
package cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.social;
import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.user.SysUserDO;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
/**
* 社交用户
* 通过 {@link SysSocialUserDO#getUserId()} 关联到对应的 {@link SysUserDO}
*
* @author weir
*/
@TableName(value = "sys_social_user", autoResultMap = true)
@Data
@EqualsAndHashCode(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class SysSocialUserDO extends BaseDO {
/**
* 自增主键
*/
@TableId
private Long id;
/**
* 关联的用户编号
*/
private Long userId;
/**
* 用户类型
*
* 枚举 {@link UserTypeEnum}
*/
private Integer userType;
/**
* 社交平台的类型
*
* 枚举 {@link UserTypeEnum}
*/
private Integer type;
/**
* 社交 openid
*/
private String openid;
/**
* 社交 token
*/
private String token;
/**
* 社交的全局编号
*
* 例如说,微信平台的 https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/union-id.html
* 如果没有 unionId 的平台,直接使用 openid 作为该字段的值
*/
private String unionId;
/**
* 原始 Token 数据,一般是 JSON 格式
*/
private String rawTokenInfo;
/**
* 用户昵称
*/
private String nickname;
/**
* 用户头像
*/
private String avatar;
/**
* 原始用户数据,一般是 JSON 格式
*/
private String rawUserInfo;
}

View File

@@ -1,45 +0,0 @@
package cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.tenant;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
/**
* 租户 DO
*
* @author 芋道源码
*/
@TableName(value = "sys_tenant", autoResultMap = true)
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class SysTenantDO extends BaseDO {
/**
* 租户编号,自增
*/
private Long id;
/**
* 租户名,唯一
*/
private String name;
/**
* 联系人
*/
private String contactName;
/**
* 联系手机
*/
private String contactMobile;
/**
* 帐号状态
*
* 枚举 {@link CommonStatusEnum}
*/
private Integer status;
}

View File

@@ -1,94 +0,0 @@
package cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.user;
import cn.iocoder.yudao.coreservice.modules.system.enums.common.SysSexEnum;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.mybatis.core.type.JsonLongSetTypeHandler;
import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import java.util.Date;
import java.util.Set;
/**
* 管理后台的用户 DO
*
* @author 芋道源码
*/
@TableName(value = "sys_user", autoResultMap = true)
@Data
@EqualsAndHashCode(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class SysUserDO extends TenantBaseDO {
/**
* 用户ID
*/
@TableId
private Long id;
/**
* 用户账号
*/
private String username;
/**
* 加密后的密码
*
* 因为目前使用 {@link BCryptPasswordEncoder} 加密器,所以无需自己处理 salt 盐
*/
private String password;
/**
* 用户昵称
*/
private String nickname;
/**
* 备注
*/
private String remark;
/**
* 部门ID
*/
private Long deptId;
/**
* 岗位编号数组
*/
@TableField(typeHandler = JsonLongSetTypeHandler.class)
private Set<Long> postIds;
/**
* 用户邮箱
*/
private String email;
/**
* 手机号码
*/
private String mobile;
/**
* 用户性别
*
* 枚举类 {@link SysSexEnum}
*/
private Integer sex;
/**
* 用户头像
*/
private String avatar;
/**
* 帐号状态
*
* 枚举 {@link CommonStatusEnum}
*/
private Integer status;
/**
* 最后登录IP
*/
private String loginIp;
/**
* 最后登录时间
*/
private Date loginDate;
}

View File

@@ -1,10 +0,0 @@
package cn.iocoder.yudao.coreservice.modules.system.dal.mysql.auth;
import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.auth.SysUserSessionDO;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface SysUserSessionCoreMapper extends BaseMapperX<SysUserSessionDO> {
}

View File

@@ -1,9 +0,0 @@
package cn.iocoder.yudao.coreservice.modules.system.dal.mysql.dept;
import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.dept.SysDeptDO;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface SysDeptCoreMapper extends BaseMapperX<SysDeptDO> {
}

View File

@@ -1,9 +0,0 @@
package cn.iocoder.yudao.coreservice.modules.system.dal.mysql.dept;
import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.dept.SysPostDO;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface SysPostCoreMapper extends BaseMapperX<SysPostDO> {
}

View File

@@ -1,19 +0,0 @@
package cn.iocoder.yudao.coreservice.modules.system.dal.mysql.dict;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.dict.SysDictDataDO;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.apache.ibatis.annotations.Mapper;
import java.util.Date;
@Mapper
public interface SysDictDataCoreMapper extends BaseMapperX<SysDictDataDO> {
default boolean selectExistsByUpdateTimeAfter(Date maxUpdateTime) {
return selectOne(new QueryWrapper<SysDictDataDO>().select("id")
.gt("update_time", maxUpdateTime).last("LIMIT 1")) != null;
}
}

View File

@@ -1,10 +0,0 @@
package cn.iocoder.yudao.coreservice.modules.system.dal.mysql.logger;
import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.logger.SysLoginLogDO;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface SysLoginLogCoreMapper extends BaseMapperX<SysLoginLogDO> {
}

View File

@@ -1 +0,0 @@
package cn.iocoder.yudao.coreservice.modules.system.dal.mysql;

View File

@@ -1,9 +0,0 @@
package cn.iocoder.yudao.coreservice.modules.system.dal.mysql.permission;
import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.permission.SysRoleDO;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface SysRoleCoreMapper extends BaseMapperX<SysRoleDO> {
}

View File

@@ -1,17 +0,0 @@
package cn.iocoder.yudao.coreservice.modules.system.dal.mysql.permission;
import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.permission.SysUserRoleDO;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import org.apache.ibatis.annotations.Mapper;
import java.util.Collection;
import java.util.List;
@Mapper
public interface SysUserRoleCoreMapper extends BaseMapperX<SysUserRoleDO> {
default List<SysUserRoleDO> selectListByRoleIds(Collection<Long> roleIds) {
return selectList(SysUserRoleDO::getRoleId, roleIds);
}
}

View File

@@ -1,13 +0,0 @@
package cn.iocoder.yudao.coreservice.modules.system.dal.mysql.sms;
import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.sms.SysSmsLogDO;
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.QueryWrapperX;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface SysSmsLogCoreMapper extends BaseMapperX<SysSmsLogDO> {
}

View File

@@ -1,16 +0,0 @@
package cn.iocoder.yudao.coreservice.modules.system.dal.mysql.sms;
import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.sms.SysSmsTemplateDO;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import java.util.Date;
@Mapper
public interface SysSmsTemplateCoreMapper extends BaseMapperX<SysSmsTemplateDO> {
@Select("SELECT id FROM sys_sms_template WHERE update_time > #{maxUpdateTime} LIMIT 1")
Long selectExistsByUpdateTimeAfter(Date maxUpdateTime);
}

View File

@@ -1,28 +0,0 @@
package cn.iocoder.yudao.coreservice.modules.system.dal.mysql.social;
import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.social.SysSocialUserDO;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.apache.ibatis.annotations.Mapper;
import java.util.Collection;
import java.util.List;
@Mapper
public interface SysSocialUserCoreMapper extends BaseMapperX<SysSocialUserDO> {
default List<SysSocialUserDO> selectListByTypeAndUnionId(Integer userType, Collection<Integer> types, String unionId) {
return selectList(new QueryWrapper<SysSocialUserDO>().eq("user_type", userType)
.in("type", types).eq("union_id", unionId));
}
default List<SysSocialUserDO> selectListByTypeAndUserId(Integer userType, Collection<Integer> types, Long userId) {
return selectList(new QueryWrapper<SysSocialUserDO>().eq("user_type", userType)
.in("type", types).eq("user_id", userId));
}
default List<SysSocialUserDO> selectListByUserId(Integer userType, Long userId) {
return selectList(new QueryWrapper<SysSocialUserDO>().eq("user_type", userType).eq("user_id", userId));
}
}

View File

@@ -1,9 +0,0 @@
package cn.iocoder.yudao.coreservice.modules.system.dal.mysql.tenant;
import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.tenant.SysTenantDO;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface SysTenantCoreMapper extends BaseMapperX<SysTenantDO> {
}

View File

@@ -1,17 +0,0 @@
package cn.iocoder.yudao.coreservice.modules.system.dal.mysql.user;
import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.user.SysUserDO;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.apache.ibatis.annotations.Mapper;
import java.util.Collection;
import java.util.List;
@Mapper
public interface SysUserCoreMapper extends BaseMapperX<SysUserDO> {
default List<SysUserDO> selectListByDeptIds(Collection<Long> deptIds) {
return selectList(SysUserDO::getDeptId, deptIds);
}
}

View File

@@ -1,29 +0,0 @@
package cn.iocoder.yudao.coreservice.modules.system.dal.redis;
import cn.iocoder.yudao.framework.redis.core.RedisKeyDefine;
import cn.iocoder.yudao.framework.security.core.LoginUser;
import me.zhyd.oauth.model.AuthUser;
import java.time.Duration;
import static cn.iocoder.yudao.framework.redis.core.RedisKeyDefine.KeyTypeEnum.STRING;
/**
* System Redis Key 枚举类
*
* @author 芋道源码
*/
public interface SysRedisKeyCoreConstants {
RedisKeyDefine LOGIN_USER = new RedisKeyDefine("登录用户的缓存",
"login_user:%s", // 参数为 sessionId
STRING, LoginUser.class, RedisKeyDefine.TimeoutTypeEnum.DYNAMIC);
RedisKeyDefine SOCIAL_AUTH_USER = new RedisKeyDefine("社交登陆的授权用户",
"social_auth_user:%d:%s", // 参数为 typecode
STRING, AuthUser.class, Duration.ofDays(1));
RedisKeyDefine SOCIAL_AUTH_STATE = new RedisKeyDefine("社交登陆的 state",
"social_auth_state:%s", // 参数为 state
STRING, String.class, Duration.ofHours(24)); // 值为 state
}

View File

@@ -1,47 +0,0 @@
package cn.iocoder.yudao.coreservice.modules.system.dal.redis.auth;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.framework.security.config.SecurityProperties;
import cn.iocoder.yudao.framework.security.core.LoginUser;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Repository;
import javax.annotation.Resource;
import static cn.iocoder.yudao.coreservice.modules.system.dal.redis.SysRedisKeyCoreConstants.LOGIN_USER;
/**
* {@link LoginUser} 的 RedisDAO
*
* @author 芋道源码
*/
@Repository
public class SysLoginUserCoreRedisDAO {
@Resource
private StringRedisTemplate stringRedisTemplate;
@Resource
private SecurityProperties securityProperties;
public LoginUser get(String sessionId) {
String redisKey = formatKey(sessionId);
return JsonUtils.parseObject(stringRedisTemplate.opsForValue().get(redisKey), LoginUser.class);
}
public void set(String sessionId, LoginUser loginUser) {
String redisKey = formatKey(sessionId);
stringRedisTemplate.opsForValue().set(redisKey, JsonUtils.toJsonString(loginUser),
securityProperties.getSessionTimeout());
}
public void delete(String sessionId) {
String redisKey = formatKey(sessionId);
stringRedisTemplate.delete(redisKey);
}
private static String formatKey(String sessionId) {
return String.format(LOGIN_USER.getKeyTemplate(), sessionId);
}
}

View File

@@ -1,38 +0,0 @@
package cn.iocoder.yudao.coreservice.modules.system.dal.redis.social;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthUser;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Repository;
import javax.annotation.Resource;
import static cn.iocoder.yudao.coreservice.modules.system.dal.redis.SysRedisKeyCoreConstants.SOCIAL_AUTH_USER;
/**
* 社交 {@link me.zhyd.oauth.model.AuthUser} 的 RedisDAO
*
* @author 芋道源码
*/
@Repository
public class SysSocialAuthUserRedisDAO {
@Resource
private StringRedisTemplate stringRedisTemplate;
public AuthUser get(Integer type, AuthCallback authCallback) {
String redisKey = formatKey(type, authCallback.getCode());
return JsonUtils.parseObject(stringRedisTemplate.opsForValue().get(redisKey), AuthUser.class);
}
public void set(Integer type, AuthCallback authCallback, AuthUser authUser) {
String redisKey = formatKey(type, authCallback.getCode());
stringRedisTemplate.opsForValue().set(redisKey, JsonUtils.toJsonString(authUser), SOCIAL_AUTH_USER.getTimeout());
}
private static String formatKey(Integer type, String code) {
return String.format(SOCIAL_AUTH_USER.getKeyTemplate(), type, code);
}
}

View File

@@ -1,53 +0,0 @@
package cn.iocoder.yudao.coreservice.modules.system.enums;
/**
* System 字典类型的枚举类
*
* @author 芋道源码
*/
public interface SysDictTypeConstants {
String USER_TYPE = "user_type"; // 用户类型
String COMMON_STATUS = "sys_common_status"; // 系统状态
String USER_SEX = "sys_user_sex"; // 用户性别
String OPERATE_TYPE = "sys_operate_type"; // 操作类型
String LOGIN_TYPE = "sys_login_type"; // 登录日志的类型
String LOGIN_RESULT = "sys_login_result"; // 登录结果
String CONFIG_TYPE = "sys_config_type"; // 参数配置类型
String BOOLEAN_STRING = "sys_boolean_string"; // Boolean 是否类型
String SMS_CHANNEL_CODE = "sys_sms_channel_code"; // 短信渠道编码
String SMS_TEMPLATE_TYPE = "sys_sms_template_type"; // 短信模板类型
String SMS_SEND_STATUS = "sys_sms_send_status"; // 短信发送状态
String SMS_RECEIVE_STATUS = "sys_sms_receive_status"; // 短信接收状态
/**
* 支付-订单-订单状态
*/
String PAY_ORDER_STATUS = "pay_order_status";
/**
* 支付-订单-订单回调商户状态
*/
String PAY_ORDER_NOTIFY_STATUS = "pay_order_notify_status";
/**
* 支付-订单-订单退款状态
*/
String PAY_ORDER_REFUND_STATUS = "pay_order_refund_status";
/**
* 支付-退款订单-退款状态
*/
String PAY_REFUND_ORDER_STATUS = "pay_refund_order_status";
/**
* 支付-退款订单-退款类别
*/
String PAY_REFUND_ORDER_TYPE = "pay_refund_order_type";
String BPM_TASK_ASSIGN_RULE_TYPE = "bpm_task_assign_rule_type"; // 任务分配规则类型
String BPM_TASK_ASSIGN_SCRIPT = "bpm_task_assign_script"; // 任务分配自定义脚本
}

View File

@@ -1,45 +0,0 @@
package cn.iocoder.yudao.coreservice.modules.system.enums;
import cn.iocoder.yudao.framework.common.exception.ErrorCode;
/**
* System 错误码枚举类
*
* system 系统,使用 1-006-000-000 段
*/
public interface SysErrorCodeConstants {
// ========== 短信发送 1006000000 ==========
ErrorCode SMS_SEND_MOBILE_NOT_EXISTS = new ErrorCode(1006000000, "手机号不存在");
ErrorCode SMS_SEND_MOBILE_TEMPLATE_PARAM_MISS = new ErrorCode(1006000001, "模板参数({})缺失");
ErrorCode SMS_SEND_TEMPLATE_NOT_EXISTS = new ErrorCode(1006000000, "短信模板不存在");
// ========= 文件相关 1006001000=================
ErrorCode FILE_PATH_EXISTS = new ErrorCode(1006001000, "文件路径已存在");
ErrorCode FILE_NOT_EXISTS = new ErrorCode(1006001002, "文件不存在");
// ========== 社交模块 1006002000 ==========
ErrorCode SOCIAL_AUTH_FAILURE = new ErrorCode(1006002000, "社交授权失败,原因是:{}");
ErrorCode SOCIAL_UNBIND_NOT_SELF = new ErrorCode(1006002001, "社交解绑失败,非当前用户绑定");
// ========== 用户模块 1006003000 ==========
ErrorCode USER_NOT_EXISTS = new ErrorCode(1006003000, "用户不存在");
ErrorCode USER_IS_DISABLE = new ErrorCode(1006003001, "名字为【{}】的用户已被禁用");
// ========== 部门模块 1006004000 ==========
ErrorCode DEPT_NOT_FOUND = new ErrorCode(1006004000, "当前部门不存在");
ErrorCode DEPT_NOT_ENABLE = new ErrorCode(1006004001, "部门不处于开启状态,不允许选择");
// ========== 角色模块 1006005000 ==========
ErrorCode ROLE_NOT_EXISTS = new ErrorCode(1006005000, "角色不存在");
ErrorCode ROLE_IS_DISABLE = new ErrorCode(1006005001, "名字为【{}】的角色已被禁用");
// ========== 字典类型 1006006000 ==========
ErrorCode DICT_DATA_NOT_EXISTS = new ErrorCode(1006006000, "当前字典数据不存在");
ErrorCode DICT_DATA_NOT_ENABLE = new ErrorCode(1006006001, "字典数据({})不处于开启状态,不允许选择");
// ========== 岗位模块 1006007000 ==========
ErrorCode POST_NOT_FOUND = new ErrorCode(1006007000, "当前岗位不存在");
ErrorCode POST_NOT_ENABLE = new ErrorCode(1006007001, "岗位({}) 不处于开启状态,不允许选择");
}

View File

@@ -1,27 +0,0 @@
package cn.iocoder.yudao.coreservice.modules.system.enums.common;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 性别的枚举值
*
* @author 芋道源码
*/
@Getter
@AllArgsConstructor
public enum SysSexEnum {
/** 男 */
MALE(1),
/** 女 */
FEMALE(2),
/* 未知 */
UNKNOWN(3);
/**
* 性别
*/
private final Integer sex;
}

View File

@@ -1,29 +0,0 @@
package cn.iocoder.yudao.coreservice.modules.system.enums.logger;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 登录日志的类型枚举
*/
@Getter
@AllArgsConstructor
public enum SysLoginLogTypeEnum {
LOGIN_USERNAME(100), // 使用账号登录
LOGIN_SOCIAL(101), // 使用社交登录
LOGIN_MOCK(102), // 使用 Mock 登录
LOGIN_MOBILE(103), // 使用手机登陆
LOGIN_SMS(104), // 使用短信登陆
LOGOUT_SELF(200), // 自己主动登出
LOGOUT_TIMEOUT(201), // 超时登出
LOGOUT_DELETE(202), // 强制退出
;
/**
* 日志类型
*/
private final Integer type;
}

View File

@@ -1,27 +0,0 @@
package cn.iocoder.yudao.coreservice.modules.system.enums.logger;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 登录结果的枚举类
*/
@Getter
@AllArgsConstructor
public enum SysLoginResultEnum {
SUCCESS(0), // 成功
BAD_CREDENTIALS(10), // 账号或密码不正确
USER_DISABLED(20), // 用户被禁用
CAPTCHA_NOT_FOUND(30), // 图片验证码不存在
CAPTCHA_CODE_ERROR(31), // 图片验证码不正确
UNKNOWN_ERROR(100), // 未知异常
;
/**
* 结果
*/
private final Integer result;
}

View File

@@ -1,23 +0,0 @@
package cn.iocoder.yudao.coreservice.modules.system.enums.sms;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 短信的接收状态枚举
*
* @author 芋道源码
* @date 2021/2/1 13:39
*/
@Getter
@AllArgsConstructor
public enum SysSmsReceiveStatusEnum {
INIT(0), // 初始化
SUCCESS(10), // 接收成功
FAILURE(20), // 接收失败
;
private final int status;
}

View File

@@ -1,24 +0,0 @@
package cn.iocoder.yudao.coreservice.modules.system.enums.sms;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 短信的发送状态枚举
*
* @author zzf
* @date 2021/2/1 13:39
*/
@Getter
@AllArgsConstructor
public enum SysSmsSendStatusEnum {
INIT(0), // 初始化
SUCCESS(10), // 发送成功
FAILURE(20), // 发送失败
IGNORE(30), // 忽略,即不发送
;
private final int status;
}

View File

@@ -1,25 +0,0 @@
package cn.iocoder.yudao.coreservice.modules.system.enums.sms;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 短信的模板类型枚举
*
* @author 芋道源码
*/
@Getter
@AllArgsConstructor
public enum SysSmsTemplateTypeEnum {
VERIFICATION_CODE(1), // 验证码
NOTICE(2), // 通知
PROMOTION(3), // 营销
;
/**
* 类型
*/
private final int type;
}

View File

@@ -1,84 +0,0 @@
package cn.iocoder.yudao.coreservice.modules.system.enums.social;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
import java.util.List;
/**
* 社交平台的类型枚举
*
* @author 芋道源码
*/
@Getter
@AllArgsConstructor
public enum SysSocialTypeEnum implements IntArrayValuable {
/**
* Gitee
* 文档链接https://gitee.com/api/v5/oauth_doc#/
*/
GITEE(10, "GITEE"),
/**
* 钉钉
* 文档链接https://developers.dingtalk.com/document/app/obtain-identity-credentials
*/
DINGTALK(20, "DINGTALK"),
/**
* 企业微信
* 文档链接https://xkcoding.com/2019/08/06/use-justauth-integration-wechat-enterprise.html
*/
WECHAT_ENTERPRISE(30, "WECHAT_ENTERPRISE"),
/**
* 微信公众平台 - 移动端 H5
* 文档链接https://www.cnblogs.com/juewuzhe/p/11905461.html
*/
WECHAT_MP(31, "WECHAT_MP"),
/**
* 微信开放平台 - 网站应用 PC 端扫码授权登录
* 文档链接https://justauth.wiki/guide/oauth/wechat_open/#_2-申请开发者资质认证
*/
WECHAT_OPEN(32, "WECHAT_OPEN"),
/**
* 微信小程序
* 文档链接https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/login.html
*/
WECHAT_MINI_PROGRAM(33, "WECHAT_MINI_PROGRAM"),
;
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(SysSocialTypeEnum::getType).toArray();
public static final List<Integer> WECHAT_ALL = ListUtil.toList(WECHAT_ENTERPRISE.type, WECHAT_MP.type, WECHAT_OPEN.type,
WECHAT_MINI_PROGRAM.type);
/**
* 类型
*/
private final Integer type;
/**
* 类型的标识
*/
private final String source;
@Override
public int[] array() {
return ARRAYS;
}
public static SysSocialTypeEnum valueOfType(Integer type) {
return ArrayUtil.firstMatch(o -> o.getType().equals(type), values());
}
public static List<Integer> getRelationTypes(Integer type) {
if (WECHAT_ALL.contains(type)) {
return WECHAT_ALL;
}
return ListUtil.toList(type);
}
}

View File

@@ -1,4 +0,0 @@
/**
* 占位
*/
package cn.iocoder.yudao.coreservice.modules.system.mq.message;

View File

@@ -1,50 +0,0 @@
package cn.iocoder.yudao.coreservice.modules.system.mq.message.sms;
import cn.iocoder.yudao.framework.common.core.KeyValue;
import cn.iocoder.yudao.framework.mq.core.stream.AbstractStreamMessage;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.validation.constraints.NotNull;
import java.util.List;
/**
* 短信发送消息
*
* @author 芋道源码
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class SysSmsSendMessage extends AbstractStreamMessage {
/**
* 短信日志编号
*/
@NotNull(message = "短信日志编号不能为空")
private Long logId;
/**
* 手机号
*/
@NotNull(message = "手机号不能为空")
private String mobile;
/**
* 短信渠道编号
*/
@NotNull(message = "短信渠道编号不能为空")
private Long channelId;
/**
* 短信 API 的模板编号
*/
@NotNull(message = "短信 API 的模板编号不能为空")
private String apiTemplateId;
/**
* 短信模板参数
*/
private List<KeyValue<String, Object>> templateParams;
@Override
public String getStreamKey() {
return "system.sms.send";
}
}

View File

@@ -1,4 +0,0 @@
/**
* 占位
*/
package cn.iocoder.yudao.coreservice.modules.system.mq.producer;

View File

@@ -1,41 +0,0 @@
package cn.iocoder.yudao.coreservice.modules.system.mq.producer.sms;
import cn.iocoder.yudao.coreservice.modules.system.mq.message.sms.SysSmsSendMessage;
import cn.iocoder.yudao.framework.common.core.KeyValue;
import cn.iocoder.yudao.framework.mq.core.RedisMQTemplate;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.List;
/**
* Sms 短信相关消息的 Core Producer
*
* @author zzf
* @date 2021/3/9 16:35
*/
@Slf4j
@Component
public class SysSmsCoreProducer {
@Resource
private RedisMQTemplate redisMQTemplate;
/**
* 发送 {@link SysSmsSendMessage} 消息
*
* @param logId 短信日志编号
* @param mobile 手机号
* @param channelId 渠道编号
* @param apiTemplateId 短信模板编号
* @param templateParams 短信模板参数
*/
public void sendSmsSendMessage(Long logId, String mobile,
Long channelId, String apiTemplateId, List<KeyValue<String, Object>> templateParams) {
SysSmsSendMessage message = new SysSmsSendMessage().setLogId(logId).setMobile(mobile);
message.setChannelId(channelId).setApiTemplateId(apiTemplateId).setTemplateParams(templateParams);
redisMQTemplate.send(message);
}
}

View File

@@ -1,7 +0,0 @@
/**
* system 包下,我们放通用业务,支撑上层的核心业务。
* 例如说:用户、部门、权限、数据字典等等
*
* 缩写sys
*/
package cn.iocoder.yudao.coreservice.modules.system;

View File

@@ -1,52 +0,0 @@
package cn.iocoder.yudao.coreservice.modules.system.service.auth;
import cn.iocoder.yudao.framework.security.core.LoginUser;
/**
* 在线用户 Session Core Service 接口
*
* @author 芋道源码
*/
public interface SysUserSessionCoreService {
/**
* 创建在线用户 Session
*
* @param loginUser 登录用户
* @param userIp 用户 IP
* @param userAgent 用户 UA
* @return Session 编号
*/
String createUserSession(LoginUser loginUser, String userIp, String userAgent);
/**
* 刷新在线用户 Session 的更新时间
*
* @param sessionId Session 编号
* @param loginUser 登录用户
*/
void refreshUserSession(String sessionId, LoginUser loginUser);
/**
* 删除在线用户 Session
*
* @param sessionId Session 编号
*/
void deleteUserSession(String sessionId);
/**
* 获得 Session 编号对应的在线用户
*
* @param sessionId Session 编号
* @return 在线用户
*/
LoginUser getLoginUser(String sessionId);
/**
* 获得 Session 超时时间,单位:毫秒
*
* @return 超时时间
*/
Long getSessionTimeoutMillis();
}

View File

@@ -1,93 +0,0 @@
package cn.iocoder.yudao.coreservice.modules.system.service.auth.impl;
import cn.hutool.core.util.IdUtil;
import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.auth.SysUserSessionDO;
import cn.iocoder.yudao.coreservice.modules.system.dal.mysql.auth.SysUserSessionCoreMapper;
import cn.iocoder.yudao.coreservice.modules.system.dal.redis.auth.SysLoginUserCoreRedisDAO;
import cn.iocoder.yudao.coreservice.modules.system.service.auth.SysUserSessionCoreService;
import cn.iocoder.yudao.framework.security.config.SecurityProperties;
import cn.iocoder.yudao.framework.security.core.LoginUser;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.time.Duration;
import java.util.Date;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.addTime;
/**
* 在线用户 Session Core Service 实现类
*
* @author 芋道源码
*/
@Service
public class SysUserSessionCoreServiceImpl implements SysUserSessionCoreService {
@Resource
private SysUserSessionCoreMapper userSessionCoreMapper;
@Resource
private SysLoginUserCoreRedisDAO loginUserCoreRedisDAO;
@Resource
private SecurityProperties securityProperties;
@Override
public String createUserSession(LoginUser loginUser, String userIp, String userAgent) {
// 生成 Session 编号
String sessionId = generateSessionId();
// 写入 Redis 缓存
loginUser.setUpdateTime(new Date());
loginUserCoreRedisDAO.set(sessionId, loginUser);
// 写入 DB 中
SysUserSessionDO userSession = SysUserSessionDO.builder().id(sessionId)
.userId(loginUser.getId()).userType(loginUser.getUserType())
.userIp(userIp).userAgent(userAgent).username(loginUser.getUsername())
.sessionTimeout(addTime(Duration.ofMillis(getSessionTimeoutMillis())))
.build();
userSessionCoreMapper.insert(userSession);
// 返回 Session 编号
return sessionId;
}
@Override
public void refreshUserSession(String sessionId, LoginUser loginUser) {
// 写入 Redis 缓存
loginUser.setUpdateTime(new Date());
loginUserCoreRedisDAO.set(sessionId, loginUser);
// 更新 DB 中
SysUserSessionDO updateObj = SysUserSessionDO.builder().id(sessionId).build();
updateObj.setUsername(loginUser.getUsername());
updateObj.setUpdateTime(new Date());
updateObj.setSessionTimeout(addTime(Duration.ofMillis(getSessionTimeoutMillis())));
userSessionCoreMapper.updateById(updateObj);
}
@Override
public void deleteUserSession(String sessionId) {
// 删除 Redis 缓存
loginUserCoreRedisDAO.delete(sessionId);
// 删除 DB 记录
userSessionCoreMapper.deleteById(sessionId);
}
@Override
public LoginUser getLoginUser(String sessionId) {
return loginUserCoreRedisDAO.get(sessionId);
}
@Override
public Long getSessionTimeoutMillis() {
return securityProperties.getSessionTimeout().toMillis();
}
/**
* 生成 Session 编号,目前采用 UUID 算法
*
* @return Session 编号
*/
private static String generateSessionId() {
return IdUtil.fastSimpleUUID();
}
}

View File

@@ -1,59 +0,0 @@
package cn.iocoder.yudao.coreservice.modules.system.service.dept;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.dept.SysDeptDO;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
public interface SysDeptCoreService {
/**
* 获得部门信息数组
*
* @param ids 部门编号数组
* @return 部门信息数组
*/
List<SysDeptDO> getDepts(Collection<Long> ids);
/**
* 获得部门信息
*
* @param id 部门编号
* @return 部门信息
*/
SysDeptDO getDept(Long id);
/**
* 校验部门们是否有效。如下情况,视为无效:
* 1. 部门编号不存在
* 2. 部门被禁用
*
* @param ids 角色编号数组
*/
void validDepts(Collection<Long> ids);
/**
* 获得指定编号的部门列表
*
* @param ids 部门编号数组
* @return 部门列表
*/
List<SysDeptDO> getSimpleDepts(Collection<Long> ids);
/**
* 获得指定编号的部门 Map
*
* @param ids 部门编号数组
* @return 部门 Map
*/
default Map<Long, SysDeptDO> getDeptMap(Collection<Long> ids) {
if (CollUtil.isEmpty(ids)) {
return Collections.emptyMap();
}
List<SysDeptDO> list = getSimpleDepts(ids);
return CollectionUtils.convertMap(list, SysDeptDO::getId);
}
}

View File

@@ -1,19 +0,0 @@
package cn.iocoder.yudao.coreservice.modules.system.service.dept;
import java.util.Collection;
/**
* 岗位 Core Service 接口
*
* @author 芋道源码
*/
public interface SysPostCoreService {
/**
* 校验岗位们是否有效。如下情况,视为无效:
* 1. 岗位编号不存在
* 2. 岗位被禁用
*
* @param ids 岗位编号数组
*/
void validPosts(Collection<Long> ids);
}

View File

@@ -1,69 +0,0 @@
package cn.iocoder.yudao.coreservice.modules.system.service.dept.impl;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.dept.SysDeptDO;
import cn.iocoder.yudao.coreservice.modules.system.dal.mysql.dept.SysDeptCoreMapper;
import cn.iocoder.yudao.coreservice.modules.system.service.dept.SysDeptCoreService;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import static cn.iocoder.yudao.coreservice.modules.system.enums.SysErrorCodeConstants.DEPT_NOT_ENABLE;
import static cn.iocoder.yudao.coreservice.modules.system.enums.SysErrorCodeConstants.DEPT_NOT_FOUND;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
/**
* 部门 Core Service 实现类
*
* @author 芋道源码
*/
@Service
@Slf4j
public class SysDeptCoreServiceImpl implements SysDeptCoreService {
@Resource
private SysDeptCoreMapper deptCoreMapper;
@Override
public List<SysDeptDO> getDepts(Collection<Long> ids) {
return deptCoreMapper.selectBatchIds(ids);
}
@Override
public SysDeptDO getDept(Long id) {
return deptCoreMapper.selectById(id);
}
@Override
public void validDepts(Collection<Long> ids) {
if (CollUtil.isEmpty(ids)) {
return;
}
// 获得科室信息
List<SysDeptDO> depts = deptCoreMapper.selectBatchIds(ids);
Map<Long, SysDeptDO> deptMap = CollectionUtils.convertMap(depts, SysDeptDO::getId);
// 校验
ids.forEach(id -> {
SysDeptDO dept = deptMap.get(id);
if (dept == null) {
throw exception(DEPT_NOT_FOUND);
}
if (!CommonStatusEnum.ENABLE.getStatus().equals(dept.getStatus())) {
throw exception(DEPT_NOT_ENABLE, dept.getName());
}
});
}
@Override
public List<SysDeptDO> getSimpleDepts(Collection<Long> ids) {
return deptCoreMapper.selectBatchIds(ids);
}
}

View File

@@ -1,50 +0,0 @@
package cn.iocoder.yudao.coreservice.modules.system.service.dept.impl;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.dept.SysPostDO;
import cn.iocoder.yudao.coreservice.modules.system.dal.mysql.dept.SysPostCoreMapper;
import cn.iocoder.yudao.coreservice.modules.system.service.dept.SysPostCoreService;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import static cn.iocoder.yudao.coreservice.modules.system.enums.SysErrorCodeConstants.POST_NOT_ENABLE;
import static cn.iocoder.yudao.coreservice.modules.system.enums.SysErrorCodeConstants.POST_NOT_FOUND;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
/**
* 岗位 Core Service 实现类
*
* @author 芋道源码
*/
@Service
public class SysPostCoreServiceImpl implements SysPostCoreService {
@Resource
private SysPostCoreMapper sysPostCoreMapper;
@Override
public void validPosts(Collection<Long> ids) {
if (CollUtil.isEmpty(ids)) {
return;
}
// 获得岗位信息
List<SysPostDO> posts = sysPostCoreMapper.selectBatchIds(ids);
Map<Long, SysPostDO> postMap = CollectionUtils.convertMap(posts, SysPostDO::getId);
// 校验
ids.forEach(id -> {
SysPostDO post = postMap.get(id);
if (post == null) {
throw exception(POST_NOT_FOUND);
}
if (!CommonStatusEnum.ENABLE.getStatus().equals(post.getStatus())) {
throw exception(POST_NOT_ENABLE, post.getName());
}
});
}
}

View File

@@ -1,29 +0,0 @@
package cn.iocoder.yudao.coreservice.modules.system.service.dict;
import cn.iocoder.yudao.framework.dict.core.service.DictDataFrameworkService;
import java.util.Collection;
/**
* 字典数据 Service 接口
*
* @author 芋道源码
*/
public interface SysDictDataCoreService extends DictDataFrameworkService {
/**
* 初始化字典数据的本地缓存
*/
void initLocalCache();
/**
* 校验字典数据们是否有效。如下情况,视为无效:
* 1. 字典数据不存在
* 2. 字典数据被禁用
*
* @param dictType 字典类型
* @param values 字典数据值的数组
*/
void validDictDatas(String dictType, Collection<String> values);
}

View File

@@ -1,145 +0,0 @@
package cn.iocoder.yudao.coreservice.modules.system.service.dict.impl;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.coreservice.modules.system.convert.dict.SysDictDataCoreConvert;
import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.dict.SysDictDataDO;
import cn.iocoder.yudao.coreservice.modules.system.dal.mysql.dict.SysDictDataCoreMapper;
import cn.iocoder.yudao.coreservice.modules.system.service.dict.SysDictDataCoreService;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.dict.core.dto.DictDataRespDTO;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableTable;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.*;
import static cn.iocoder.yudao.coreservice.modules.system.enums.SysErrorCodeConstants.DICT_DATA_NOT_ENABLE;
import static cn.iocoder.yudao.coreservice.modules.system.enums.SysErrorCodeConstants.DICT_DATA_NOT_EXISTS;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
/**
* 字典数据 Service 实现类
*
* @author 芋道源码
*/
@Service
@Slf4j
public class SysDictDataCoreServiceImpl implements SysDictDataCoreService {
/**
* 定时执行 {@link #schedulePeriodicRefresh()} 的周期
* 因为已经通过 Redis Pub/Sub 机制,所以频率不需要高
*/
private static final long SCHEDULER_PERIOD = 5 * 60 * 1000L;
/**
* 字典数据缓存,第二个 key 使用 label
*
* key1字典类型 dictType
* key2字典标签 label
*/
private ImmutableTable<String, String, SysDictDataDO> labelDictDataCache;
/**
* 字典数据缓存,第二个 key 使用 value
*
* key1字典类型 dictType
* key2字典值 value
*/
private ImmutableTable<String, String, SysDictDataDO> valueDictDataCache;
/**
* 缓存字典数据的最大更新时间,用于后续的增量轮询,判断是否有更新
*/
private volatile Date maxUpdateTime;
@Resource
private SysDictDataCoreMapper dictDataCoreMapper;
@Override
@PostConstruct
public synchronized void initLocalCache() {
// 获取字典数据列表,如果有更新
List<SysDictDataDO> dataList = this.loadDictDataIfUpdate(maxUpdateTime);
if (CollUtil.isEmpty(dataList)) {
return;
}
// 构建缓存
ImmutableTable.Builder<String, String, SysDictDataDO> labelDictDataBuilder = ImmutableTable.builder();
ImmutableTable.Builder<String, String, SysDictDataDO> valueDictDataBuilder = ImmutableTable.builder();
dataList.forEach(dictData -> {
labelDictDataBuilder.put(dictData.getDictType(), dictData.getLabel(), dictData);
valueDictDataBuilder.put(dictData.getDictType(), dictData.getValue(), dictData);
});
labelDictDataCache = labelDictDataBuilder.build();
valueDictDataCache = valueDictDataBuilder.build();
assert dataList.size() > 0; // 断言,避免告警
maxUpdateTime = dataList.stream().max(Comparator.comparing(BaseDO::getUpdateTime)).get().getUpdateTime();
log.info("[initLocalCache][缓存字典数据,数量为:{}]", dataList.size());
}
@Scheduled(fixedDelay = SCHEDULER_PERIOD, initialDelay = SCHEDULER_PERIOD)
public void schedulePeriodicRefresh() {
initLocalCache();
}
/**
* 如果字典数据发生变化,从数据库中获取最新的全量字典数据。
* 如果未发生变化,则返回空
*
* @param maxUpdateTime 当前字典数据的最大更新时间
* @return 字典数据列表
*/
private List<SysDictDataDO> loadDictDataIfUpdate(Date maxUpdateTime) {
// 第一步,判断是否要更新。
if (maxUpdateTime == null) { // 如果更新时间为空,说明 DB 一定有新数据
log.info("[loadDictDataIfUpdate][首次加载全量字典数据]");
} else { // 判断数据库中是否有更新的字典数据
if (!dictDataCoreMapper.selectExistsByUpdateTimeAfter(maxUpdateTime)) {
return null;
}
log.info("[loadDictDataIfUpdate][增量加载全量字典数据]");
}
// 第二步,如果有更新,则从数据库加载所有字典数据
return dictDataCoreMapper.selectList();
}
@Override
public DictDataRespDTO getDictDataFromCache(String type, String value) {
return SysDictDataCoreConvert.INSTANCE.convert02(valueDictDataCache.get(type, value));
}
@Override
public DictDataRespDTO parseDictDataFromCache(String type, String label) {
return SysDictDataCoreConvert.INSTANCE.convert02(labelDictDataCache.get(type, label));
}
@Override
public List<DictDataRespDTO> listDictDatasFromCache(String type) {
return SysDictDataCoreConvert.INSTANCE.convertList03(labelDictDataCache.row(type).values());
}
@Override
public void validDictDatas(String dictType, Collection<String> values) {
if (CollUtil.isEmpty(values)) {
return;
}
ImmutableMap<String, SysDictDataDO> dictDataMap = valueDictDataCache.row(dictType);
// 校验
values.forEach(value -> {
SysDictDataDO dictData = dictDataMap.get(value);
if (dictData == null) {
throw exception(DICT_DATA_NOT_EXISTS);
}
if (!CommonStatusEnum.ENABLE.getStatus().equals(dictData.getStatus())) {
throw exception(DICT_DATA_NOT_ENABLE, dictData.getLabel());
}
});
}
}

View File

@@ -1,17 +0,0 @@
package cn.iocoder.yudao.coreservice.modules.system.service.logger;
import cn.iocoder.yudao.coreservice.modules.system.service.logger.dto.SysLoginLogCreateReqDTO;
/**
* 登录日志 Core Service 接口
*/
public interface SysLoginLogCoreService {
/**
* 创建登录日志
*
* @param reqDTO 日志信息
*/
void createLoginLog(SysLoginLogCreateReqDTO reqDTO);
}

View File

@@ -1,62 +0,0 @@
package cn.iocoder.yudao.coreservice.modules.system.service.logger.dto;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
/**
* 登录日志创建 Request DTO
*
* @author 芋道源码
*/
@Data
public class SysLoginLogCreateReqDTO {
/**
* 日志类型
*/
@NotNull(message = "日志类型不能为空")
private Integer logType;
/**
* 链路追踪编号
*/
@NotEmpty(message = "链路追踪编号不能为空")
private String traceId;
/**
* 用户编号
*/
private Long userId;
/**
* 用户类型
*/
@NotNull(message = "用户类型不能为空")
private Integer userType;
/**
* 用户账号
*/
@NotBlank(message = "用户账号不能为空")
@Size(max = 30, message = "用户账号长度不能超过30个字符")
private String username;
/**
* 登录结果
*/
@NotNull(message = "登录结果不能为空")
private Integer result;
/**
* 用户 IP
*/
@NotEmpty(message = "用户 IP 不能为空")
private String userIp;
/**
* 浏览器 UserAgent
*/
@NotEmpty(message = "浏览器 UserAgent 不能为空")
private String userAgent;
}

View File

@@ -1,30 +0,0 @@
package cn.iocoder.yudao.coreservice.modules.system.service.logger.impl;
import cn.iocoder.yudao.coreservice.modules.system.convert.logger.SysLoginLogCoreConvert;
import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.logger.SysLoginLogDO;
import cn.iocoder.yudao.coreservice.modules.system.dal.mysql.logger.SysLoginLogCoreMapper;
import cn.iocoder.yudao.coreservice.modules.system.service.logger.SysLoginLogCoreService;
import cn.iocoder.yudao.coreservice.modules.system.service.logger.dto.SysLoginLogCreateReqDTO;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
/**
* 登录日志 Service Core 实现
*
* @author 芋道源码
*/
@Service
public class SysLoginLogCoreServiceImpl implements SysLoginLogCoreService {
@Resource
private SysLoginLogCoreMapper loginLogMapper;
@Override
public void createLoginLog(SysLoginLogCreateReqDTO reqDTO) {
SysLoginLogDO loginLog = SysLoginLogCoreConvert.INSTANCE.convert(reqDTO);
// 插入
loginLogMapper.insert(loginLog);
}
}

View File

@@ -1,20 +0,0 @@
package cn.iocoder.yudao.coreservice.modules.system.service.permission;
import java.util.Collection;
import java.util.Set;
/**
* 权限 Core Service 接口
*
* 提供用户-角色、角色-菜单、角色-部门的关联权限处理
*
* @author 芋道源码
*/
public interface SysPermissionCoreService {
/**
* 获得拥有多个角色的用户编号集合
*
* @param roleIds 角色编号集合
* @return 用户编号集合
*/
Set<Long> getUserRoleIdListByRoleIds(Collection<Long> roleIds);
}

View File

@@ -1,19 +0,0 @@
package cn.iocoder.yudao.coreservice.modules.system.service.permission;
import java.util.Collection;
/**
* 角色 Core Service 接口
*
* @author 芋道源码
*/
public interface SysRoleCoreService {
/**
* 校验角色们是否有效。如下情况,视为无效:
* 1. 角色编号不存在
* 2. 角色被禁用
*
* @param ids 角色编号数组
*/
void validRoles(Collection<Long> ids);
}

View File

@@ -1,28 +0,0 @@
package cn.iocoder.yudao.coreservice.modules.system.service.permission.impl;
import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.permission.SysUserRoleDO;
import cn.iocoder.yudao.coreservice.modules.system.dal.mysql.permission.SysUserRoleCoreMapper;
import cn.iocoder.yudao.coreservice.modules.system.service.permission.SysPermissionCoreService;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.Collection;
import java.util.Set;
/**
* 权限 Core Service 实现类
*
* @author 芋道源码
*/
@Service
public class SysPermissionCoreServiceImpl implements SysPermissionCoreService {
@Resource
private SysUserRoleCoreMapper userRoleCoreMapper;
@Override
public Set<Long> getUserRoleIdListByRoleIds(Collection<Long> roleIds) {
return CollectionUtils.convertSet(userRoleCoreMapper.selectListByRoleIds(roleIds),
SysUserRoleDO::getRoleId);
}
}

View File

@@ -1,50 +0,0 @@
package cn.iocoder.yudao.coreservice.modules.system.service.permission.impl;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.permission.SysRoleDO;
import cn.iocoder.yudao.coreservice.modules.system.dal.mysql.permission.SysRoleCoreMapper;
import cn.iocoder.yudao.coreservice.modules.system.service.permission.SysRoleCoreService;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import static cn.iocoder.yudao.coreservice.modules.system.enums.SysErrorCodeConstants.ROLE_IS_DISABLE;
import static cn.iocoder.yudao.coreservice.modules.system.enums.SysErrorCodeConstants.ROLE_NOT_EXISTS;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
/**
* 角色 Core Service 实现类
*
* @author 芋道源码
*/
@Service
public class SysRoleCoreServiceImpl implements SysRoleCoreService {
@Resource
private SysRoleCoreMapper sysRoleCoreMapper;
@Override
public void validRoles(Collection<Long> ids) {
if (CollUtil.isEmpty(ids)) {
return;
}
// 获得角色信息
List<SysRoleDO> roles = sysRoleCoreMapper.selectBatchIds(ids);
Map<Long, SysRoleDO> roleMap = CollectionUtils.convertMap(roles, SysRoleDO::getId);
// 校验
ids.forEach(id -> {
SysRoleDO role = roleMap.get(id);
if (role == null) {
throw exception(ROLE_NOT_EXISTS);
}
if (!CommonStatusEnum.ENABLE.getStatus().equals(role.getStatus())) {
throw exception(ROLE_IS_DISABLE, role.getName());
}
});
}
}

View File

@@ -1,63 +0,0 @@
package cn.iocoder.yudao.coreservice.modules.system.service.sms;
import cn.iocoder.yudao.coreservice.modules.system.mq.message.sms.SysSmsSendMessage;
import java.util.List;
import java.util.Map;
/**
* 短信 Service Core 接口
*
* 接入方,通过调用 send 开头的方法,创建发送短信的任务到 MQ 中
* 后续yudao-admin-server 监听 MQ执行真正的短信发送逻辑
*
* @author 芋道源码
*/
public interface SysSmsCoreService {
/**
* 发送单条短信给后台用户
*
* 在 mobile 为空时,使用 userId 加载对应管理员的手机号
*
* @param mobile 手机号
* @param userId 用户编号
* @param templateCode 短信模板编号
* @param templateParams 短信模板参数
* @return 发送日志编号
*/
Long sendSingleSmsToAdmin(String mobile, Long userId,
String templateCode, Map<String, Object> templateParams);
/**
* 发送单条短信给前台用户
*
* 在 mobile 为空时,使用 userId 加载对应会员的手机号
*
* @param mobile 手机号
* @param userId 用户编号
* @param templateCode 短信模板编号
* @param templateParams 短信模板参数
* @return 发送日志编号
*/
Long sendSingleSmsToMember(String mobile, Long userId,
String templateCode, Map<String, Object> templateParams);
Long sendSingleSms(String mobile, Long userId, Integer userType,
String templateCode, Map<String, Object> templateParams);
void sendBatchSms(List<String> mobiles, List<Long> userIds, Integer userType,
String templateCode, Map<String, Object> templateParams);
void doSendSms(SysSmsSendMessage message);
/**
* 接收短信的接收结果
*
* @param channelCode 渠道编码
* @param text 结果内容
* @throws Throwable 处理失败时,抛出异常
*/
void receiveSmsStatus(String channelCode, String text) throws Throwable;
}

View File

@@ -1,56 +0,0 @@
package cn.iocoder.yudao.coreservice.modules.system.service.sms;
import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.sms.SysSmsTemplateDO;
import java.util.Date;
import java.util.Map;
/**
* 短信日志 Core Service 接口
*
* @author zzf
* @date 13:48 2021/3/2
*/
public interface SysSmsLogCoreService {
/**
* 创建短信日志
*
* @param mobile 手机号
* @param userId 用户编号
* @param userType 用户类型
* @param isSend 是否发送
* @param template 短信模板
* @param templateContent 短信内容
* @param templateParams 短信参数
* @return 发送日志编号
*/
Long createSmsLog(String mobile, Long userId, Integer userType, Boolean isSend,
SysSmsTemplateDO template, String templateContent, Map<String, Object> templateParams);
/**
* 更新日志的发送结果
*
* @param id 日志编号
* @param sendCode 发送结果的编码
* @param sendMsg 发送结果的提示
* @param apiSendCode 短信 API 发送结果的编码
* @param apiSendMsg 短信 API 发送失败的提示
* @param apiRequestId 短信 API 发送返回的唯一请求 ID
* @param apiSerialNo 短信 API 发送返回的序号
*/
void updateSmsSendResult(Long id, Integer sendCode, String sendMsg,
String apiSendCode, String apiSendMsg, String apiRequestId, String apiSerialNo);
/**
* 更新日志的接收结果
*
* @param id 日志编号
* @param success 是否接收成功
* @param receiveTime 用户接收时间
* @param apiReceiveCode API 接收结果的编码
* @param apiReceiveMsg API 接收结果的说明
*/
void updateSmsReceiveResult(Long id, Boolean success, Date receiveTime, String apiReceiveCode, String apiReceiveMsg);
}

View File

@@ -1,36 +0,0 @@
package cn.iocoder.yudao.coreservice.modules.system.service.sms;
import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.sms.SysSmsTemplateDO;
import java.util.Map;
/**
* 短信模板 Core Service 接口
*
* @author 芋道源码
*/
public interface SysSmsTemplateCoreService {
/**
* 初始化短信模板的本地缓存
*/
void initLocalCache();
/**
* 获得短信模板,从缓存中
*
* @param code 模板编码
* @return 短信模板
*/
SysSmsTemplateDO getSmsTemplateByCodeFromCache(String code);
/**
* 格式化短信内容
*
* @param content 短信模板的内容
* @param params 内容的参数
* @return 格式化后的内容
*/
String formatSmsTemplateContent(String content, Map<String, Object> params);
}

View File

@@ -1,178 +0,0 @@
package cn.iocoder.yudao.coreservice.modules.system.service.sms.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.sms.SysSmsTemplateDO;
import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.user.SysUserDO;
import cn.iocoder.yudao.coreservice.modules.system.mq.message.sms.SysSmsSendMessage;
import cn.iocoder.yudao.coreservice.modules.system.mq.producer.sms.SysSmsCoreProducer;
import cn.iocoder.yudao.coreservice.modules.system.service.sms.SysSmsCoreService;
import cn.iocoder.yudao.coreservice.modules.system.service.sms.SysSmsLogCoreService;
import cn.iocoder.yudao.coreservice.modules.system.service.sms.SysSmsTemplateCoreService;
import cn.iocoder.yudao.coreservice.modules.system.service.user.SysUserCoreService;
import cn.iocoder.yudao.framework.common.core.KeyValue;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.sms.core.client.SmsClient;
import cn.iocoder.yudao.framework.sms.core.client.SmsClientFactory;
import cn.iocoder.yudao.framework.sms.core.client.SmsCommonResult;
import cn.iocoder.yudao.framework.sms.core.client.dto.SmsReceiveRespDTO;
import cn.iocoder.yudao.framework.sms.core.client.dto.SmsSendRespDTO;
import com.google.common.annotations.VisibleForTesting;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import static cn.iocoder.yudao.coreservice.modules.system.enums.SysErrorCodeConstants.*;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
/**
* 短信 Service Core 实现
*
* @author 芋道源码
*/
@Service
public class SysSmsCoreServiceImpl implements SysSmsCoreService {
@Resource
private SysUserCoreService sysUserCoreService;
@Resource
private SysSmsTemplateCoreService smsTemplateCoreService;
@Resource
private SysSmsLogCoreService smsLogCoreService;
@Resource
private SmsClientFactory smsClientFactory;
@Resource
private SysSmsCoreProducer smsCoreProducer;
@Override
public Long sendSingleSmsToAdmin(String mobile, Long userId, String templateCode, Map<String, Object> templateParams) {
// 如果 mobile 为空,则加载用户编号对应的手机号
if (StrUtil.isEmpty(mobile)) {
SysUserDO user = sysUserCoreService.getUser(userId);
if (user != null) {
mobile = user.getMobile();
}
}
// 执行发送
return this.sendSingleSms(mobile, userId, UserTypeEnum.ADMIN.getValue(), templateCode, templateParams);
}
@Override
public Long sendSingleSmsToMember(String mobile, Long userId, String templateCode, Map<String, Object> templateParams) {
// 如果 mobile 为空,则加载用户编号对应的手机号
if (StrUtil.isEmpty(mobile)) {
// MbrUserDO user = mbrUserCoreService.getUser(userId);
// if (user != null) {
// mobile = user.getMobile();
// }
// TODO 芋艿:重构
}
// 执行发送
return this.sendSingleSms(mobile, userId, UserTypeEnum.MEMBER.getValue(), templateCode, templateParams);
}
@Override
public Long sendSingleSms(String mobile, Long userId, Integer userType,
String templateCode, Map<String, Object> templateParams) {
// 校验短信模板是否合法
SysSmsTemplateDO template = this.checkSmsTemplateValid(templateCode);
// 校验手机号码是否存在
mobile = this.checkMobile(mobile);
// 构建有序的模板参数。为什么放在这个位置,是提前保证模板参数的正确性,而不是到了插入发送日志
List<KeyValue<String, Object>> newTemplateParams = this.buildTemplateParams(template, templateParams);
// 创建发送日志
Boolean isSend = CommonStatusEnum.ENABLE.getStatus().equals(template.getStatus()); // 如果模板被禁用,则不发送短信,只记录日志
String content = smsTemplateCoreService.formatSmsTemplateContent(template.getContent(), templateParams);
Long sendLogId = smsLogCoreService.createSmsLog(mobile, userId, userType, isSend, template, content, templateParams);
// 发送 MQ 消息,异步执行发送短信
if (isSend) {
smsCoreProducer.sendSmsSendMessage(sendLogId, mobile, template.getChannelId(),
template.getApiTemplateId(), newTemplateParams);
}
return sendLogId;
}
@Override
public void sendBatchSms(List<String> mobiles, List<Long> userIds, Integer userType,
String templateCode, Map<String, Object> templateParams) {
throw new UnsupportedOperationException("暂时不支持该操作,感兴趣可以实现该功能哟!");
}
@VisibleForTesting
public SysSmsTemplateDO checkSmsTemplateValid(String templateCode) {
// 获得短信模板。考虑到效率,从缓存中获取
SysSmsTemplateDO template = smsTemplateCoreService.getSmsTemplateByCodeFromCache(templateCode);
// 短信模板不存在
if (template == null) {
throw exception(SMS_SEND_TEMPLATE_NOT_EXISTS);
}
return template;
}
/**
* 将参数模板,处理成有序的 KeyValue 数组
*
* 原因是,部分短信平台并不是使用 key 作为参数,而是数组下标,例如说腾讯云 https://cloud.tencent.com/document/product/382/39023
*
* @param template 短信模板
* @param templateParams 原始参数
* @return 处理后的参数
*/
@VisibleForTesting
public List<KeyValue<String, Object>> buildTemplateParams(SysSmsTemplateDO template, Map<String, Object> templateParams) {
return template.getParams().stream().map(key -> {
Object value = templateParams.get(key);
if (value == null) {
throw exception(SMS_SEND_MOBILE_TEMPLATE_PARAM_MISS, key);
}
return new KeyValue<>(key, value);
}).collect(Collectors.toList());
}
@VisibleForTesting
public String checkMobile(String mobile) {
if (StrUtil.isEmpty(mobile)) {
throw exception(SMS_SEND_MOBILE_NOT_EXISTS);
}
return mobile;
}
@Override
public void doSendSms(SysSmsSendMessage message) {
// 获得渠道对应的 SmsClient 客户端
SmsClient smsClient = smsClientFactory.getSmsClient(message.getChannelId());
Assert.notNull(smsClient, String.format("短信客户端(%d) 不存在", message.getChannelId()));
// 发送短信
SmsCommonResult<SmsSendRespDTO> sendResult = smsClient.sendSms(message.getLogId(), message.getMobile(),
message.getApiTemplateId(), message.getTemplateParams());
smsLogCoreService.updateSmsSendResult(message.getLogId(), sendResult.getCode(), sendResult.getMsg(),
sendResult.getApiCode(), sendResult.getApiMsg(), sendResult.getApiRequestId(),
sendResult.getData() != null ? sendResult.getData().getSerialNo() : null);
}
@Override
public void receiveSmsStatus(String channelCode, String text) throws Throwable {
// 获得渠道对应的 SmsClient 客户端
SmsClient smsClient = smsClientFactory.getSmsClient(channelCode);
Assert.notNull(smsClient, String.format("短信客户端(%s) 不存在", channelCode));
// 解析内容
List<SmsReceiveRespDTO> receiveResults = smsClient.parseSmsReceiveStatus(text);
if (CollUtil.isEmpty(receiveResults)) {
return;
}
// 更新短信日志的接收结果. 因为量一般不大,所以先使用 for 循环更新
receiveResults.forEach(result -> smsLogCoreService.updateSmsReceiveResult(result.getLogId(),
result.getSuccess(), result.getReceiveTime(), result.getErrorCode(), result.getErrorCode()));
}
}

View File

@@ -1,72 +0,0 @@
package cn.iocoder.yudao.coreservice.modules.system.service.sms.impl;
import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.sms.SysSmsLogDO;
import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.sms.SysSmsTemplateDO;
import cn.iocoder.yudao.coreservice.modules.system.dal.mysql.sms.SysSmsLogCoreMapper;
import cn.iocoder.yudao.coreservice.modules.system.enums.sms.SysSmsReceiveStatusEnum;
import cn.iocoder.yudao.coreservice.modules.system.enums.sms.SysSmsSendStatusEnum;
import cn.iocoder.yudao.coreservice.modules.system.service.sms.SysSmsLogCoreService;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.Date;
import java.util.Map;
import java.util.Objects;
/**
* 短信日志 Core Service 实现类
*
* @author zzf
* @date 2021/1/25 9:25
*/
@Slf4j
@Service
public class SysSmsLogCoreServiceImpl implements SysSmsLogCoreService {
@Resource
private SysSmsLogCoreMapper smsLogCoreMapper;
@Override
public Long createSmsLog(String mobile, Long userId, Integer userType, Boolean isSend,
SysSmsTemplateDO template, String templateContent, Map<String, Object> templateParams) {
SysSmsLogDO.SysSmsLogDOBuilder logBuilder = SysSmsLogDO.builder();
// 根据是否要发送,设置状态
logBuilder.sendStatus(Objects.equals(isSend, true) ? SysSmsSendStatusEnum.INIT.getStatus()
: SysSmsSendStatusEnum.IGNORE.getStatus());
// 设置手机相关字段
logBuilder.mobile(mobile).userId(userId).userType(userType);
// 设置模板相关字段
logBuilder.templateId(template.getId()).templateCode(template.getCode()).templateType(template.getType());
logBuilder.templateContent(templateContent).templateParams(templateParams).apiTemplateId(template.getApiTemplateId());
// 设置渠道相关字段
logBuilder.channelId(template.getChannelId()).channelCode(template.getChannelCode());
// 设置接收相关字段
logBuilder.receiveStatus(SysSmsReceiveStatusEnum.INIT.getStatus());
// 插入数据库
SysSmsLogDO logDO = logBuilder.build();
smsLogCoreMapper.insert(logDO);
return logDO.getId();
}
@Override
public void updateSmsSendResult(Long id, Integer sendCode, String sendMsg,
String apiSendCode, String apiSendMsg, String apiRequestId, String apiSerialNo) {
SysSmsSendStatusEnum sendStatus = CommonResult.isSuccess(sendCode) ? SysSmsSendStatusEnum.SUCCESS
: SysSmsSendStatusEnum.FAILURE;
smsLogCoreMapper.updateById(SysSmsLogDO.builder().id(id).sendStatus(sendStatus.getStatus()).sendTime(new Date())
.sendCode(sendCode).sendMsg(sendMsg).apiSendCode(apiSendCode).apiSendMsg(apiSendMsg)
.apiRequestId(apiRequestId).apiSerialNo(apiSerialNo).build());
}
@Override
public void updateSmsReceiveResult(Long id, Boolean success, Date receiveTime, String apiReceiveCode, String apiReceiveMsg) {
SysSmsReceiveStatusEnum receiveStatus = Objects.equals(success, true) ? SysSmsReceiveStatusEnum.SUCCESS
: SysSmsReceiveStatusEnum.FAILURE;
smsLogCoreMapper.updateById(SysSmsLogDO.builder().id(id).receiveStatus(receiveStatus.getStatus()).receiveTime(receiveTime)
.apiReceiveCode(apiReceiveCode).apiReceiveMsg(apiReceiveMsg).build());
}
}

View File

@@ -1,105 +0,0 @@
package cn.iocoder.yudao.coreservice.modules.system.service.sms.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.sms.SysSmsTemplateDO;
import cn.iocoder.yudao.coreservice.modules.system.dal.mysql.sms.SysSmsTemplateCoreMapper;
import cn.iocoder.yudao.coreservice.modules.system.service.sms.SysSmsTemplateCoreService;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import com.google.common.collect.ImmutableMap;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Map;
/**
* 短信模板 Core Service 接口
*
* @author 芋道源码
*/
@Service
@Slf4j
public class SysSmsTemplateCoreServiceImpl implements SysSmsTemplateCoreService {
/**
* 定时执行 {@link #schedulePeriodicRefresh()} 的周期
* 因为已经通过 Redis Pub/Sub 机制,所以频率不需要高
*/
private static final long SCHEDULER_PERIOD = 5 * 60 * 1000L;
/**
* 短信模板缓存
* key短信模板编码 {@link SysSmsTemplateDO#getCode()}
*
* 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向
*/
private volatile Map<String, SysSmsTemplateDO> smsTemplateCache;
/**
* 缓存短信模板的最大更新时间,用于后续的增量轮询,判断是否有更新
*/
private volatile Date maxUpdateTime;
@Resource
private SysSmsTemplateCoreMapper smsTemplateCoreMapper;
@Override
@PostConstruct
public void initLocalCache() {
// 获取短信模板列表,如果有更新
List<SysSmsTemplateDO> smsTemplateList = this.loadSmsTemplateIfUpdate(maxUpdateTime);
if (CollUtil.isEmpty(smsTemplateList)) {
return;
}
// 写入缓存
ImmutableMap.Builder<String, SysSmsTemplateDO> builder = ImmutableMap.builder();
smsTemplateList.forEach(sysSmsTemplateDO -> builder.put(sysSmsTemplateDO.getCode(), sysSmsTemplateDO));
smsTemplateCache = builder.build();
assert smsTemplateList.size() > 0; // 断言,避免告警
maxUpdateTime = smsTemplateList.stream().max(Comparator.comparing(BaseDO::getUpdateTime)).get().getUpdateTime();
log.info("[initLocalCache][初始化 SmsTemplate 数量为 {}]", smsTemplateList.size());
}
/**
* 如果短信模板发生变化,从数据库中获取最新的全量短信模板。
* 如果未发生变化,则返回空
*
* @param maxUpdateTime 当前短信模板的最大更新时间
* @return 短信模板列表
*/
private List<SysSmsTemplateDO> loadSmsTemplateIfUpdate(Date maxUpdateTime) {
// 第一步,判断是否要更新。
if (maxUpdateTime == null) { // 如果更新时间为空,说明 DB 一定有新数据
log.info("[loadSmsTemplateIfUpdate][首次加载全量短信模板]");
} else { // 判断数据库中是否有更新的短信模板
if (smsTemplateCoreMapper.selectExistsByUpdateTimeAfter(maxUpdateTime) == null) {
return null;
}
log.info("[loadSmsTemplateIfUpdate][增量加载全量短信模板]");
}
// 第二步,如果有更新,则从数据库加载所有短信模板
return smsTemplateCoreMapper.selectList();
}
@Scheduled(fixedDelay = SCHEDULER_PERIOD, initialDelay = SCHEDULER_PERIOD)
public void schedulePeriodicRefresh() {
initLocalCache();
}
@Override
public SysSmsTemplateDO getSmsTemplateByCodeFromCache(String code) {
return smsTemplateCache.get(code);
}
@Override
public String formatSmsTemplateContent(String content, Map<String, Object> params) {
return StrUtil.format(content, params);
}
}

View File

@@ -1,85 +0,0 @@
package cn.iocoder.yudao.coreservice.modules.system.service.social;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.social.SysSocialUserDO;
import cn.iocoder.yudao.coreservice.modules.system.enums.social.SysSocialTypeEnum;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.common.exception.ServiceException;
import me.zhyd.oauth.model.AuthUser;
import javax.validation.constraints.NotNull;
import java.util.List;
/**
* 社交 Service 接口,例如说社交平台的授权登录
*
* @author 芋道源码
*/
public interface SysSocialCoreService {
/**
* 获得社交平台的授权 URL
*
* @param type 社交平台的类型 {@link SysSocialTypeEnum}
* @param redirectUri 重定向 URL
* @return 社交平台的授权 URL
*/
String getAuthorizeUrl(Integer type, String redirectUri);
/**
* 获得授权的用户
* 如果授权失败,则会抛出 {@link ServiceException} 异常
*
* @param type 社交平台的类型 {@link SysSocialTypeEnum}
* @param code 授权码
* @param state state
* @return 授权用户
*/
@NotNull
AuthUser getAuthUser(Integer type, String code, String state);
default String getAuthUserUnionId(AuthUser authUser) {
return StrUtil.blankToDefault(authUser.getToken().getUnionId(), authUser.getUuid());
}
/**
* 获得 unionId 对应的某个社交平台的“所有”社交用户
* 注意这里的“所有”指的是类似【微信】平台包括了小程序、公众号、PC 网站,他们的 unionId 是一致的
*
* @param type 社交平台的类型 {@link SysSocialTypeEnum}
* @param unionId 社交平台的 unionId
* @return 社交用户列表
* @param userTypeEnum 全局用户类型
*/
List<SysSocialUserDO> getAllSocialUserList(Integer type, String unionId, UserTypeEnum userTypeEnum);
/**
* 获得指定用户的社交用户列表
*
* @param userId 用户编号
* @return 社交用户列表
* @param userTypeEnum 全局用户类型
*/
List<SysSocialUserDO> getSocialUserList(Long userId, UserTypeEnum userTypeEnum);
/**
* 绑定社交用户
*
* @param userId 用户编号
* @param type 社交平台的类型 {@link SysSocialTypeEnum}
* @param authUser 授权用户
* @param userTypeEnum 全局用户类型
*/
void bindSocialUser(Long userId, Integer type, AuthUser authUser, UserTypeEnum userTypeEnum);
/**
* 取消绑定社交用户
*
* @param userId 用户编号
* @param type 社交平台的类型 {@link SysSocialTypeEnum}
* @param unionId 社交平台的 unionId
* @param userTypeEnum 全局用户类型
*/
void unbindSocialUser(Long userId, Integer type, String unionId, UserTypeEnum userTypeEnum);
}

View File

@@ -1,178 +0,0 @@
package cn.iocoder.yudao.coreservice.modules.system.service.social.impl;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.social.SysSocialUserDO;
import cn.iocoder.yudao.coreservice.modules.system.dal.mysql.social.SysSocialUserCoreMapper;
import cn.iocoder.yudao.coreservice.modules.system.dal.redis.social.SysSocialAuthUserRedisDAO;
import cn.iocoder.yudao.coreservice.modules.system.enums.social.SysSocialTypeEnum;
import cn.iocoder.yudao.coreservice.modules.system.service.social.SysSocialCoreService;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.common.util.http.HttpUtils;
import com.google.common.annotations.VisibleForTesting;
import com.xkcoding.justauth.AuthRequestFactory;
import lombok.extern.slf4j.Slf4j;
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.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
import java.util.List;
import java.util.Objects;
import static cn.iocoder.yudao.coreservice.modules.system.enums.SysErrorCodeConstants.SOCIAL_AUTH_FAILURE;
import static cn.iocoder.yudao.coreservice.modules.system.enums.SysErrorCodeConstants.SOCIAL_UNBIND_NOT_SELF;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
/**
* 社交 Service 实现类
*
* @author 芋道源码
*/
@Service
@Validated
@Slf4j
public class SysSocialCoreServiceImpl implements SysSocialCoreService {
@Resource
private AuthRequestFactory authRequestFactory;
@Resource
private SysSocialAuthUserRedisDAO authSocialUserRedisDAO;
@Resource
private SysSocialUserCoreMapper socialUserMapper;
@Override
public String getAuthorizeUrl(Integer type, String redirectUri) {
// 获得对应的 AuthRequest 实现
AuthRequest authRequest = authRequestFactory.get(SysSocialTypeEnum.valueOfType(type).getSource());
// 生成跳转地址
String authorizeUri = authRequest.authorize(AuthStateUtils.createState());
return HttpUtils.replaceUrlQuery(authorizeUri, "redirect_uri", redirectUri);
}
@Override
public AuthUser getAuthUser(Integer type, String code, String state) {
AuthCallback authCallback = buildAuthCallback(code, state);
// 从缓存中获取
AuthUser authUser = authSocialUserRedisDAO.get(type, authCallback);
if (authUser != null) {
return authUser;
}
// 请求获取
authUser = this.getAuthUser0(type, authCallback);
// 缓存。原因是 code 有且可以使用一次。在社交登录时,当未绑定 User 时,需要绑定登录,此时需要 code 使用两次
authSocialUserRedisDAO.set(type, authCallback, authUser);
return authUser;
}
@Override
public List<SysSocialUserDO> getAllSocialUserList(Integer type, String unionId,UserTypeEnum userTypeEnum) {
List<Integer> types = SysSocialTypeEnum.getRelationTypes(type);
return socialUserMapper.selectListByTypeAndUnionId(userTypeEnum.getValue(), types, unionId);
}
@Override
public List<SysSocialUserDO> getSocialUserList(Long userId,UserTypeEnum userTypeEnum) {
return socialUserMapper.selectListByUserId(userTypeEnum.getValue(), userId);
}
@Override
@Transactional
public void bindSocialUser(Long userId, Integer type, AuthUser authUser, UserTypeEnum userTypeEnum) {
// 获得 unionId 对应的 SysSocialUserDO 列表
String unionId = getAuthUserUnionId(authUser);
List<SysSocialUserDO> socialUsers = this.getAllSocialUserList(type, unionId, userTypeEnum);
// 逻辑一:如果 userId 之前绑定过该 type 的其它账号,需要进行解绑
this.unbindOldSocialUser(userId, type, unionId, userTypeEnum);
// 逻辑二:如果 socialUsers 指定的 userId 改变,需要进行更新
// 例如说,一个微信 unionId 对应了多个社交账号,结果其中有个关联了新的 userId则其它也要跟着修改
// 考虑到 socialUsers 一般比较少,直接 for 循环更新即可
socialUsers.forEach(socialUser -> {
if (Objects.equals(socialUser.getUserId(), userId)) {
return;
}
socialUserMapper.updateById(new SysSocialUserDO().setId(socialUser.getId()).setUserId(userId));
});
// 逻辑三:如果 authUser 不存在于 socialUsers 中,则进行新增;否则,进行更新
SysSocialUserDO socialUser = CollUtil.findOneByField(socialUsers, "openid", authUser.getUuid());
SysSocialUserDO saveSocialUser = SysSocialUserDO.builder() // 新增和更新的通用属性
.token(authUser.getToken().getAccessToken()).rawTokenInfo(toJsonString(authUser.getToken()))
.nickname(authUser.getNickname()).avatar(authUser.getAvatar()).rawUserInfo(toJsonString(authUser.getRawUserInfo()))
.build();
if (socialUser == null) {
saveSocialUser.setUserId(userId).setUserType(userTypeEnum.getValue())
.setType(type).setOpenid(authUser.getUuid()).setUnionId(unionId);
socialUserMapper.insert(saveSocialUser);
} else {
saveSocialUser.setId(socialUser.getId());
socialUserMapper.updateById(saveSocialUser);
}
}
@Override
public void unbindSocialUser(Long userId, Integer type, String unionId, UserTypeEnum userTypeEnum) {
// 获得 unionId 对应的所有 SysSocialUserDO 社交用户
List<SysSocialUserDO> socialUsers = this.getAllSocialUserList(type, unionId, userTypeEnum);
if (CollUtil.isEmpty(socialUsers)) {
return;
}
// 校验,是否解绑的是非自己的
socialUsers.forEach(socialUser -> {
if (!Objects.equals(socialUser.getUserId(), userId)) {
throw exception(SOCIAL_UNBIND_NOT_SELF);
}
});
// 解绑
socialUserMapper.deleteBatchIds(CollectionUtils.convertSet(socialUsers, SysSocialUserDO::getId));
}
@VisibleForTesting
public void unbindOldSocialUser(Long userId, Integer type, String newUnionId, UserTypeEnum userTypeEnum) {
List<Integer> types = SysSocialTypeEnum.getRelationTypes(type);
List<SysSocialUserDO> oldSocialUsers = socialUserMapper.selectListByTypeAndUserId(
userTypeEnum.getValue(), types, userId);
// 如果新老的 unionId 是一致的,说明无需解绑
if (CollUtil.isEmpty(oldSocialUsers) || Objects.equals(newUnionId, oldSocialUsers.get(0).getUnionId())) {
return;
}
// 解绑
socialUserMapper.deleteBatchIds(CollectionUtils.convertSet(oldSocialUsers, SysSocialUserDO::getId));
}
/**
* 请求社交平台,获得授权的用户
*
* @param type 社交平台的类型
* @param authCallback 授权回调
* @return 授权的用户
*/
private AuthUser getAuthUser0(Integer type, AuthCallback authCallback) {
AuthRequest authRequest = authRequestFactory.get(SysSocialTypeEnum.valueOfType(type).getSource());
AuthResponse<?> authResponse = authRequest.login(authCallback);
log.info("[getAuthUser0][请求社交平台 type({}) request({}) response({})]", type, toJsonString(authCallback),
toJsonString(authResponse));
if (!authResponse.ok()) {
throw exception(SOCIAL_AUTH_FAILURE, authResponse.getMsg());
}
return (AuthUser) authResponse.getData();
}
private static AuthCallback buildAuthCallback(String code, String state) {
return AuthCallback.builder().code(code).state(state).build();
}
}

View File

@@ -1,11 +0,0 @@
package cn.iocoder.yudao.coreservice.modules.system.service.tenant;
import cn.iocoder.yudao.framework.tenant.core.service.TenantFrameworkService;
/**
* 租户 Service 接口
*
* @author 芋道源码
*/
public interface SysTenantCoreService extends TenantFrameworkService {
}

View File

@@ -1,29 +0,0 @@
package cn.iocoder.yudao.coreservice.modules.system.service.tenant.impl;
import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.tenant.SysTenantDO;
import cn.iocoder.yudao.coreservice.modules.system.dal.mysql.tenant.SysTenantCoreMapper;
import cn.iocoder.yudao.coreservice.modules.system.service.tenant.SysTenantCoreService;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
/**
* 租户 Service 实现类
*
* @author 芋道源码
*/
@Service
public class SysTenantCoreServiceImpl implements SysTenantCoreService {
@Resource
private SysTenantCoreMapper tenantCoreMapper;
@Override
public List<Long> getTenantIds() {
List<SysTenantDO> tenants = tenantCoreMapper.selectList();
return CollectionUtils.convertList(tenants, SysTenantDO::getId);
}
}

View File

@@ -1,70 +0,0 @@
package cn.iocoder.yudao.coreservice.modules.system.service.user;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.user.SysUserDO;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import java.util.*;
/**
* 后台用户 Service Core 接口
*
* @author 芋道源码
*/
public interface SysUserCoreService {
/**
* 通过用户 ID 查询用户
*
* @param id 用户ID
* @return 用户对象信息
*/
SysUserDO getUser(Long id);
/**
* 获得指定部门的用户数组
*
* @param deptIds 部门数组
* @return 用户数组
*/
List<SysUserDO> getUsersByDeptIds(Collection<Long> deptIds);
/**
* 获得指定岗位的用户数组
*
* @param postIds 岗位数组
* @return 用户数组
*/
List<SysUserDO> getUsersByPostIds(Collection<Long> postIds);
/**
* 获得用户列表
*
* @param ids 用户编号数组
* @return 用户列表
*/
List<SysUserDO> getUsers(Collection<Long> ids);
/**
* 校验用户们是否有效。如下情况,视为无效:
* 1. 用户编号不存在
* 2. 用户被禁用
*
* @param ids 用户编号数组
*/
void validUsers(Set<Long> ids);
/**
* 获得用户 Map
*
* @param ids 用户编号数组
* @return 用户 Map
*/
default Map<Long, SysUserDO> getUserMap(Collection<Long> ids) {
if (CollUtil.isEmpty(ids)) {
return new HashMap<>();
}
return CollectionUtils.convertMap(getUsers(ids), SysUserDO::getId);
}
}

View File

@@ -1,82 +0,0 @@
package cn.iocoder.yudao.coreservice.modules.system.service.user.impl;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.user.SysUserDO;
import cn.iocoder.yudao.coreservice.modules.system.dal.mysql.user.SysUserCoreMapper;
import cn.iocoder.yudao.coreservice.modules.system.service.user.SysUserCoreService;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.*;
import static cn.iocoder.yudao.coreservice.modules.system.enums.SysErrorCodeConstants.USER_IS_DISABLE;
import static cn.iocoder.yudao.coreservice.modules.system.enums.SysErrorCodeConstants.USER_NOT_EXISTS;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
/**
* 后台用户 Service Core 实现
*
* @author 芋道源码
*/
@Service
public class SysUserCoreServiceImpl implements SysUserCoreService {
@Resource
private SysUserCoreMapper userCoreMapper;
@Override
public SysUserDO getUser(Long id) {
return userCoreMapper.selectById(id);
}
@Override
public List<SysUserDO> getUsersByDeptIds(Collection<Long> deptIds) {
if (CollUtil.isEmpty(deptIds)) {
return Collections.emptyList();
}
return userCoreMapper.selectListByDeptIds(deptIds);
}
@Override
public List<SysUserDO> getUsersByPostIds(Collection<Long> postIds) {
if (CollUtil.isEmpty(postIds)) {
return Collections.emptyList();
}
// 过滤不符合条件的
// TODO 芋艿暂时只能内存过滤。解决方案1、新建一个关联表2、基于 where + 函数3、json 字段,适合 mysql 8+ 版本
List<SysUserDO> users = userCoreMapper.selectList();
users.removeIf(user -> !CollUtil.containsAny(user.getPostIds(), postIds));
return users;
}
@Override
public List<SysUserDO> getUsers(Collection<Long> ids) {
if (CollUtil.isEmpty(ids)) {
return Collections.emptyList();
}
return userCoreMapper.selectBatchIds(ids);
}
@Override
public void validUsers(Set<Long> ids) {
if (CollUtil.isEmpty(ids)) {
return;
}
// 获得岗位信息
List<SysUserDO> users = userCoreMapper.selectBatchIds(ids);
Map<Long, SysUserDO> userMap = CollectionUtils.convertMap(users, SysUserDO::getId);
// 校验
ids.forEach(id -> {
SysUserDO user = userMap.get(id);
if (user == null) {
throw exception(USER_NOT_EXISTS);
}
if (!CommonStatusEnum.ENABLE.getStatus().equals(user.getStatus())) {
throw exception(USER_IS_DISABLE, user.getNickname());
}
});
}
}

View File

@@ -12,8 +12,7 @@ import org.springframework.context.annotation.Import;
import javax.annotation.Resource;
import static cn.iocoder.yudao.coreservice.modules.system.enums.SysErrorCodeConstants.FILE_NOT_EXISTS;
import static cn.iocoder.yudao.coreservice.modules.system.enums.SysErrorCodeConstants.FILE_PATH_EXISTS;
import static cn.iocoder.yudao.coreservice.modules.infra.enums.SysErrorCodeConstants.*;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString;

View File

@@ -1,125 +0,0 @@
package cn.iocoder.yudao.coreservice.modules.system.service.auth;
import cn.iocoder.yudao.coreservice.BaseDbAndRedisUnitTest;
import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.auth.SysUserSessionDO;
import cn.iocoder.yudao.coreservice.modules.system.dal.mysql.auth.SysUserSessionCoreMapper;
import cn.iocoder.yudao.coreservice.modules.system.dal.redis.auth.SysLoginUserCoreRedisDAO;
import cn.iocoder.yudao.coreservice.modules.system.service.auth.impl.SysUserSessionCoreServiceImpl;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.security.config.SecurityProperties;
import cn.iocoder.yudao.framework.security.core.LoginUser;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import;
import javax.annotation.Resource;
import java.time.Duration;
import java.util.Date;
import static cn.hutool.core.util.RandomUtil.randomEle;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.addTime;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.when;
@Import({SysUserSessionCoreServiceImpl.class, SysLoginUserCoreRedisDAO.class})
public class SysUserSessionCoreServiceTest extends BaseDbAndRedisUnitTest {
@Resource
private SysUserSessionCoreServiceImpl userSessionCoreService;
@Resource
private SysUserSessionCoreMapper userSessionCoreMapper;
@Resource
private SysLoginUserCoreRedisDAO loginUserCoreRedisDAO;
@MockBean
private SecurityProperties securityProperties;
@Test
public void testCreateUserSession_success() {
// 准备参数
String userIp = randomString();
String userAgent = randomString();
LoginUser loginUser = randomPojo(LoginUser.class, o -> {
o.setUserType(randomEle(UserTypeEnum.values()).getValue());
o.setTenantId(0L); // 租户设置为 0因为暂未启用多租户组件
});
// mock 方法
when(securityProperties.getSessionTimeout()).thenReturn(Duration.ofDays(1));
// 调用
String sessionId = userSessionCoreService.createUserSession(loginUser, userIp, userAgent);
// 校验 SysUserSessionDO 记录
SysUserSessionDO userSessionDO = userSessionCoreMapper.selectById(sessionId);
assertPojoEquals(loginUser, userSessionDO, "id", "updateTime");
assertEquals(sessionId, userSessionDO.getId());
assertEquals(userIp, userSessionDO.getUserIp());
assertEquals(userAgent, userSessionDO.getUserAgent());
// 校验 LoginUser 缓存
LoginUser redisLoginUser = loginUserCoreRedisDAO.get(sessionId);
assertPojoEquals(loginUser, redisLoginUser, "username", "password");
}
@Test
public void testCreateRefreshUserSession_success() {
// 准备参数
String sessionId = randomString();
String userIp = randomString();
String userAgent = randomString();
long timeLong = randomLongId();
String userName = randomString();
Date date = randomDate();
LoginUser loginUser = randomPojo(LoginUser.class, o -> o.setUserType(randomEle(UserTypeEnum.values()).getValue()));
// mock 方法
when(securityProperties.getSessionTimeout()).thenReturn(Duration.ofDays(1));
// mock 数据
loginUser.setUpdateTime(date);
loginUserCoreRedisDAO.set(sessionId, loginUser);
SysUserSessionDO userSession = SysUserSessionDO.builder().id(sessionId)
.userId(loginUser.getId()).userType(loginUser.getUserType())
.userIp(userIp).userAgent(userAgent).username(userName)
.sessionTimeout(addTime(Duration.ofMillis(timeLong)))
.build();
userSessionCoreMapper.insert(userSession);
// 调用
userSessionCoreService.refreshUserSession(sessionId, loginUser);
// 校验 LoginUser 缓存
LoginUser redisLoginUser = loginUserCoreRedisDAO.get(sessionId);
assertNotEquals(redisLoginUser.getUpdateTime(), date);
// 校验 SysUserSessionDO 记录
SysUserSessionDO updateDO = userSessionCoreMapper.selectById(sessionId);
assertEquals(updateDO.getUsername(), loginUser.getUsername());
assertNotEquals(updateDO.getUpdateTime(), userSession.getUpdateTime());
assertNotEquals(updateDO.getSessionTimeout(), addTime(Duration.ofMillis(timeLong)));
}
@Test
public void testDeleteUserSession_success() {
// 准备参数
String sessionId = randomString();
String userIp = randomString();
String userAgent = randomString();
Long timeLong = randomLongId();
LoginUser loginUser = randomPojo(LoginUser.class, o -> o.setUserType(randomEle(UserTypeEnum.values()).getValue()));
// mock 存入 Redis
when(securityProperties.getSessionTimeout()).thenReturn(Duration.ofDays(1));
// mock 数据
loginUserCoreRedisDAO.set(sessionId, loginUser);
SysUserSessionDO userSession = SysUserSessionDO.builder().id(sessionId)
.userId(loginUser.getId()).userType(loginUser.getUserType())
.userIp(userIp).userAgent(userAgent).username(loginUser.getUsername())
.sessionTimeout(addTime(Duration.ofMillis(timeLong)))
.build();
userSessionCoreMapper.insert(userSession);
// 调用
userSessionCoreService.deleteUserSession(sessionId);
// 校验数据不存在了
assertNull(loginUserCoreRedisDAO.get(sessionId));
assertNull(userSessionCoreMapper.selectById(sessionId));
}
}

View File

@@ -1,78 +0,0 @@
package cn.iocoder.yudao.coreservice.modules.system.service.dict;
import cn.iocoder.yudao.coreservice.BaseDbUnitTest;
import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.dict.SysDictDataDO;
import cn.iocoder.yudao.coreservice.modules.system.dal.mysql.dict.SysDictDataCoreMapper;
import cn.iocoder.yudao.coreservice.modules.system.service.dict.impl.SysDictDataCoreServiceImpl;
import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils;
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
import com.google.common.collect.ImmutableTable;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Import;
import javax.annotation.Resource;
import java.util.Date;
import java.util.function.Consumer;
import static cn.hutool.core.bean.BeanUtil.getFieldValue;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomCommonStatus;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
import static org.junit.jupiter.api.Assertions.assertEquals;
/**
* {@link SysDictDataCoreServiceImpl} 的单元测试类
*
* @author 芋道源码
*/
@Import(SysDictDataCoreServiceImpl.class)
public class SysDictDataCoreServiceTest extends BaseDbUnitTest {
@Resource
private SysDictDataCoreServiceImpl dictDataCoreService;
@Resource
private SysDictDataCoreMapper dictDataMapper;
/**
* 测试加载到新的字典数据的情况
*/
@Test
@SuppressWarnings("unchecked")
public void testInitLocalCache() {
// mock 数据
SysDictDataDO dictData01 = randomDictDataDO();
dictDataMapper.insert(dictData01);
SysDictDataDO dictData02 = randomDictDataDO();
dictDataMapper.insert(dictData02);
// 调用
dictDataCoreService.initLocalCache();
// 断言 labelDictDataCache 缓存
ImmutableTable<String, String, SysDictDataDO> labelDictDataCache =
(ImmutableTable<String, String, SysDictDataDO>) getFieldValue(dictDataCoreService, "labelDictDataCache");
assertEquals(2, labelDictDataCache.size());
assertPojoEquals(dictData01, labelDictDataCache.get(dictData01.getDictType(), dictData01.getLabel()));
assertPojoEquals(dictData02, labelDictDataCache.get(dictData02.getDictType(), dictData02.getLabel()));
// 断言 valueDictDataCache 缓存
ImmutableTable<String, String, SysDictDataDO> valueDictDataCache =
(ImmutableTable<String, String, SysDictDataDO>) getFieldValue(dictDataCoreService, "valueDictDataCache");
assertEquals(2, valueDictDataCache.size());
assertPojoEquals(dictData01, valueDictDataCache.get(dictData01.getDictType(), dictData01.getValue()));
assertPojoEquals(dictData02, valueDictDataCache.get(dictData02.getDictType(), dictData02.getValue()));
// 断言 maxUpdateTime 缓存
Date maxUpdateTime = (Date) getFieldValue(dictDataCoreService, "maxUpdateTime");
assertEquals(ObjectUtils.max(dictData01.getUpdateTime(), dictData02.getUpdateTime()), maxUpdateTime);
}
// ========== 随机对象 ==========
@SafeVarargs
private static SysDictDataDO randomDictDataDO(Consumer<SysDictDataDO>... consumers) {
Consumer<SysDictDataDO> consumer = (o) -> {
o.setStatus(randomCommonStatus()); // 保证 status 的范围
};
return randomPojo(SysDictDataDO.class, ArrayUtils.append(consumer, consumers));
}
}

View File

@@ -1,47 +0,0 @@
package cn.iocoder.yudao.coreservice.modules.system.service.logger;
import cn.iocoder.yudao.coreservice.BaseDbUnitTest;
import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.logger.SysLoginLogDO;
import cn.iocoder.yudao.coreservice.modules.system.dal.mysql.logger.SysLoginLogCoreMapper;
import cn.iocoder.yudao.coreservice.modules.system.enums.logger.SysLoginLogTypeEnum;
import cn.iocoder.yudao.coreservice.modules.system.enums.logger.SysLoginResultEnum;
import cn.iocoder.yudao.coreservice.modules.system.service.logger.dto.SysLoginLogCreateReqDTO;
import cn.iocoder.yudao.coreservice.modules.system.service.logger.impl.SysLoginLogCoreServiceImpl;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils;
import cn.iocoder.yudao.framework.test.core.util.RandomUtils;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Import;
import javax.annotation.Resource;
import static cn.hutool.core.util.RandomUtil.randomEle;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
@Import(SysLoginLogCoreServiceImpl.class)
public class SysLoginLogServiceImplTest extends BaseDbUnitTest {
@Resource
private SysLoginLogCoreServiceImpl loginLogCoreService;
@Resource
private SysLoginLogCoreMapper loginLogCoreMapper;
@Test
public void testCreateLoginLog() {
SysLoginLogCreateReqDTO reqDTO = RandomUtils.randomPojo(SysLoginLogCreateReqDTO.class, vo -> {
// 指定随机的范围,避免超出范围入库失败
vo.setUserType(randomEle(UserTypeEnum.values()).getValue());
vo.setLogType(randomEle(SysLoginLogTypeEnum.values()).getType());
vo.setResult(randomEle(SysLoginResultEnum.values()).getResult());
vo.setTraceId(TracerUtils.getTraceId());
});
// 调用
loginLogCoreService.createLoginLog(reqDTO);
// 断言,忽略基本字段
SysLoginLogDO sysLoginLogDO = loginLogCoreMapper.selectOne(null);
assertPojoEquals(reqDTO, sysLoginLogDO);
}
}

View File

@@ -1 +0,0 @@
package cn.iocoder.yudao.coreservice.modules.system.service;

View File

@@ -1,199 +0,0 @@
package cn.iocoder.yudao.coreservice.modules.system.service.sms;
import cn.hutool.core.map.MapUtil;
import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.sms.SysSmsTemplateDO;
import cn.iocoder.yudao.coreservice.modules.system.mq.message.sms.SysSmsSendMessage;
import cn.iocoder.yudao.coreservice.modules.system.mq.producer.sms.SysSmsCoreProducer;
import cn.iocoder.yudao.coreservice.modules.system.service.sms.impl.SysSmsCoreServiceImpl;
import cn.iocoder.yudao.framework.common.core.KeyValue;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.sms.core.client.SmsClient;
import cn.iocoder.yudao.framework.sms.core.client.SmsClientFactory;
import cn.iocoder.yudao.framework.sms.core.client.SmsCommonResult;
import cn.iocoder.yudao.framework.sms.core.client.dto.SmsReceiveRespDTO;
import cn.iocoder.yudao.framework.sms.core.client.dto.SmsSendRespDTO;
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
import org.assertj.core.util.Lists;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static cn.hutool.core.util.RandomUtil.randomEle;
import static cn.iocoder.yudao.coreservice.modules.system.enums.SysErrorCodeConstants.*;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.*;
/**
* {@link SysSmsCoreService} 的单元测试类
*
* @author 芋道源码
*/
public class SysSmsCoreServiceTest extends BaseMockitoUnitTest {
@InjectMocks
private SysSmsCoreServiceImpl smsCoreService;
@Mock
private SysSmsTemplateCoreService smsTemplateCoreService;
@Mock
private SysSmsLogCoreService smsLogCoreService;
@Mock
private SysSmsCoreProducer smsCoreProducer;
@Mock
private SmsClientFactory smsClientFactory;
/**
* 发送成功,当短信模板开启时
*/
@Test
public void testSendSingleSms_successWhenSmsTemplateEnable() {
// 准备参数
String mobile = randomString();
Long userId = randomLongId();
Integer userType = randomEle(UserTypeEnum.values()).getValue();
String templateCode = randomString();
Map<String, Object> templateParams = MapUtil.<String, Object>builder().put("code", "1234")
.put("op", "login").build();
// mock SmsTemplateService 的方法
SysSmsTemplateDO template = randomPojo(SysSmsTemplateDO.class, o -> {
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
o.setContent("验证码为{code}, 操作为{op}");
o.setParams(Lists.newArrayList("code", "op"));
});
when(smsTemplateCoreService.getSmsTemplateByCodeFromCache(eq(templateCode))).thenReturn(template);
String content = randomString();
when(smsTemplateCoreService.formatSmsTemplateContent(eq(template.getContent()), eq(templateParams)))
.thenReturn(content);
// mock SmsLogService 的方法
Long smsLogId = randomLongId();
when(smsLogCoreService.createSmsLog(eq(mobile), eq(userId), eq(userType), eq(Boolean.TRUE), eq(template),
eq(content), eq(templateParams))).thenReturn(smsLogId);
// 调用
Long resultSmsLogId = smsCoreService.sendSingleSms(mobile, userId, userType, templateCode, templateParams);
// 断言
assertEquals(smsLogId, resultSmsLogId);
// 断言调用
verify(smsCoreProducer, times(1)).sendSmsSendMessage(eq(smsLogId), eq(mobile),
eq(template.getChannelId()), eq(template.getApiTemplateId()),
eq(Lists.newArrayList(new KeyValue<>("code", "1234"), new KeyValue<>("op", "login"))));
}
/**
* 发送成功,当短信模板关闭时
*/
@Test
public void testSendSingleSms_successWhenSmsTemplateDisable() {
// 准备参数
String mobile = randomString();
Long userId = randomLongId();
Integer userType = randomEle(UserTypeEnum.values()).getValue();
String templateCode = randomString();
Map<String, Object> templateParams = MapUtil.<String, Object>builder().put("code", "1234")
.put("op", "login").build();
// mock SmsTemplateService 的方法
SysSmsTemplateDO template = randomPojo(SysSmsTemplateDO.class, o -> {
o.setStatus(CommonStatusEnum.DISABLE.getStatus());
o.setContent("验证码为{code}, 操作为{op}");
o.setParams(Lists.newArrayList("code", "op"));
});
when(smsTemplateCoreService.getSmsTemplateByCodeFromCache(eq(templateCode))).thenReturn(template);
String content = randomString();
when(smsTemplateCoreService.formatSmsTemplateContent(eq(template.getContent()), eq(templateParams)))
.thenReturn(content);
// mock SmsLogService 的方法
Long smsLogId = randomLongId();
when(smsLogCoreService.createSmsLog(eq(mobile), eq(userId), eq(userType), eq(Boolean.FALSE), eq(template),
eq(content), eq(templateParams))).thenReturn(smsLogId);
// 调用
Long resultSmsLogId = smsCoreService.sendSingleSms(mobile, userId, userType, templateCode, templateParams);
// 断言
assertEquals(smsLogId, resultSmsLogId);
// 断言调用
verify(smsCoreProducer, times(0)).sendSmsSendMessage(anyLong(), anyString(),
anyLong(), any(), anyList());
}
@Test
public void testCheckSmsTemplateValid_notExists() {
// 准备参数
String templateCode = randomString();
// mock 方法
// 调用,并断言异常
assertServiceException(() -> smsCoreService.checkSmsTemplateValid(templateCode),
SMS_SEND_TEMPLATE_NOT_EXISTS);
}
@Test
public void testBuildTemplateParams_paramMiss() {
// 准备参数
SysSmsTemplateDO template = randomPojo(SysSmsTemplateDO.class,
o -> o.setParams(Lists.newArrayList("code")));
Map<String, Object> templateParams = new HashMap<>();
// mock 方法
// 调用,并断言异常
assertServiceException(() -> smsCoreService.buildTemplateParams(template, templateParams),
SMS_SEND_MOBILE_TEMPLATE_PARAM_MISS, "code");
}
@Test
public void testCheckMobile_notExists() {
// 准备参数
// mock 方法
// 调用,并断言异常
assertServiceException(() -> smsCoreService.checkMobile(null),
SMS_SEND_MOBILE_NOT_EXISTS);
}
@Test
@SuppressWarnings("unchecked")
public void testDoSendSms() {
// 准备参数
SysSmsSendMessage message = randomPojo(SysSmsSendMessage.class);
// mock SmsClientFactory 的方法
SmsClient smsClient = spy(SmsClient.class);
when(smsClientFactory.getSmsClient(eq(message.getChannelId()))).thenReturn(smsClient);
// mock SmsClient 的方法
SmsCommonResult<SmsSendRespDTO> sendResult = randomPojo(SmsCommonResult.class, SmsSendRespDTO.class);
when(smsClient.sendSms(eq(message.getLogId()), eq(message.getMobile()), eq(message.getApiTemplateId()),
eq(message.getTemplateParams()))).thenReturn(sendResult);
// 调用
smsCoreService.doSendSms(message);
// 断言
verify(smsLogCoreService, times(1)).updateSmsSendResult(eq(message.getLogId()),
eq(sendResult.getCode()), eq(sendResult.getMsg()), eq(sendResult.getApiCode()),
eq(sendResult.getApiMsg()), eq(sendResult.getApiRequestId()), eq(sendResult.getData().getSerialNo()));
}
@Test
public void testReceiveSmsStatus() throws Throwable {
// 准备参数
String channelCode = randomString();
String text = randomString();
// mock SmsClientFactory 的方法
SmsClient smsClient = spy(SmsClient.class);
when(smsClientFactory.getSmsClient(eq(channelCode))).thenReturn(smsClient);
// mock SmsClient 的方法
List<SmsReceiveRespDTO> receiveResults = randomPojoList(SmsReceiveRespDTO.class);
// 调用
smsCoreService.receiveSmsStatus(channelCode, text);
// 断言
receiveResults.forEach(result -> smsLogCoreService.updateSmsReceiveResult(eq(result.getLogId()), eq(result.getSuccess()),
eq(result.getReceiveTime()), eq(result.getErrorCode()), eq(result.getErrorCode())));
}
}

View File

@@ -1,149 +0,0 @@
package cn.iocoder.yudao.coreservice.modules.system.service.sms;
import cn.hutool.core.map.MapUtil;
import cn.iocoder.yudao.coreservice.BaseDbUnitTest;
import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.sms.SysSmsLogDO;
import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.sms.SysSmsTemplateDO;
import cn.iocoder.yudao.coreservice.modules.system.dal.mysql.sms.SysSmsLogCoreMapper;
import cn.iocoder.yudao.coreservice.modules.system.enums.sms.SysSmsReceiveStatusEnum;
import cn.iocoder.yudao.coreservice.modules.system.enums.sms.SysSmsSendStatusEnum;
import cn.iocoder.yudao.coreservice.modules.system.enums.sms.SysSmsTemplateTypeEnum;
import cn.iocoder.yudao.coreservice.modules.system.service.sms.impl.SysSmsLogCoreServiceImpl;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Import;
import javax.annotation.Resource;
import java.util.Date;
import java.util.Map;
import java.util.function.Consumer;
import static cn.hutool.core.util.RandomUtil.randomBoolean;
import static cn.hutool.core.util.RandomUtil.randomEle;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
/**
* {@link SysSmsLogCoreServiceImpl} 的单元测试类
*
* @author 芋道源码
*/
@Import(SysSmsLogCoreServiceImpl.class)
public class SysSmsLogCoreServiceTest extends BaseDbUnitTest {
@Resource
private SysSmsLogCoreServiceImpl smsLogCoreService;
@Resource
private SysSmsLogCoreMapper smsLogCoreMapper;
@Test
public void testCreateSmsLog() {
// 准备参数
String mobile = randomString();
Long userId = randomLongId();
Integer userType = randomEle(UserTypeEnum.values()).getValue();
Boolean isSend = randomBoolean();
SysSmsTemplateDO templateDO = randomPojo(SysSmsTemplateDO.class,
o -> o.setType(randomEle(SysSmsTemplateTypeEnum.values()).getType()));
String templateContent = randomString();
Map<String, Object> templateParams = randomTemplateParams();
// mock 方法
// 调用
Long logId = smsLogCoreService.createSmsLog(mobile, userId, userType, isSend,
templateDO, templateContent, templateParams);
// 断言
SysSmsLogDO logDO = smsLogCoreMapper.selectById(logId);
assertEquals(isSend ? SysSmsSendStatusEnum.INIT.getStatus() : SysSmsSendStatusEnum.IGNORE.getStatus(),
logDO.getSendStatus());
assertEquals(mobile, logDO.getMobile());
assertEquals(userType, logDO.getUserType());
assertEquals(userId, logDO.getUserId());
assertEquals(templateDO.getId(), logDO.getTemplateId());
assertEquals(templateDO.getCode(), logDO.getTemplateCode());
assertEquals(templateDO.getType(), logDO.getTemplateType());
assertEquals(templateDO.getChannelId(), logDO.getChannelId());
assertEquals(templateDO.getChannelCode(), logDO.getChannelCode());
assertEquals(templateContent, logDO.getTemplateContent());
assertEquals(templateParams, logDO.getTemplateParams());
assertEquals(SysSmsReceiveStatusEnum.INIT.getStatus(), logDO.getReceiveStatus());
}
@Test
public void testUpdateSmsSendResult() {
// mock 数据
SysSmsLogDO dbSmsLog = randomSmsLogDO(
o -> o.setSendStatus(SysSmsSendStatusEnum.IGNORE.getStatus()));
smsLogCoreMapper.insert(dbSmsLog);
// 准备参数
Long id = dbSmsLog.getId();
Integer sendCode = randomInteger();
String sendMsg = randomString();
String apiSendCode = randomString();
String apiSendMsg = randomString();
String apiRequestId = randomString();
String apiSerialNo = randomString();
// 调用
smsLogCoreService.updateSmsSendResult(id, sendCode, sendMsg,
apiSendCode, apiSendMsg, apiRequestId, apiSerialNo);
// 断言
dbSmsLog = smsLogCoreMapper.selectById(id);
assertEquals(CommonResult.isSuccess(sendCode) ? SysSmsSendStatusEnum.SUCCESS.getStatus()
: SysSmsSendStatusEnum.FAILURE.getStatus(), dbSmsLog.getSendStatus());
assertNotNull(dbSmsLog.getSendTime());
assertEquals(sendMsg, dbSmsLog.getSendMsg());
assertEquals(apiSendCode, dbSmsLog.getApiSendCode());
assertEquals(apiSendMsg, dbSmsLog.getApiSendMsg());
assertEquals(apiRequestId, dbSmsLog.getApiRequestId());
assertEquals(apiSerialNo, dbSmsLog.getApiSerialNo());
}
@Test
public void testUpdateSmsReceiveResult() {
// mock 数据
SysSmsLogDO dbSmsLog = randomSmsLogDO(
o -> o.setReceiveStatus(SysSmsReceiveStatusEnum.INIT.getStatus()));
smsLogCoreMapper.insert(dbSmsLog);
// 准备参数
Long id = dbSmsLog.getId();
Boolean success = randomBoolean();
Date receiveTime = randomDate();
String apiReceiveCode = randomString();
String apiReceiveMsg = randomString();
// 调用
smsLogCoreService.updateSmsReceiveResult(id, success, receiveTime, apiReceiveCode, apiReceiveMsg);
// 断言
dbSmsLog = smsLogCoreMapper.selectById(id);
assertEquals(success ? SysSmsReceiveStatusEnum.SUCCESS.getStatus()
: SysSmsReceiveStatusEnum.FAILURE.getStatus(), dbSmsLog.getReceiveStatus());
assertEquals(receiveTime, dbSmsLog.getReceiveTime());
assertEquals(apiReceiveCode, dbSmsLog.getApiReceiveCode());
assertEquals(apiReceiveMsg, dbSmsLog.getApiReceiveMsg());
}
// ========== 随机对象 ==========
@SafeVarargs
private static SysSmsLogDO randomSmsLogDO(Consumer<SysSmsLogDO>... consumers) {
Consumer<SysSmsLogDO> consumer = (o) -> {
o.setTemplateParams(randomTemplateParams());
o.setTemplateType(randomEle(SysSmsTemplateTypeEnum.values()).getType()); // 保证 templateType 的范围
o.setUserType(randomEle(UserTypeEnum.values()).getValue()); // 保证 userType 的范围
o.setSendStatus(randomEle(SysSmsSendStatusEnum.values()).getStatus()); // 保证 sendStatus 的范围
o.setReceiveStatus(randomEle(SysSmsReceiveStatusEnum.values()).getStatus()); // 保证 receiveStatus 的范围
};
return randomPojo(SysSmsLogDO.class, ArrayUtils.append(consumer, consumers));
}
private static Map<String, Object> randomTemplateParams() {
return MapUtil.<String, Object>builder().put(randomString(), randomString())
.put(randomString(), randomString()).build();
}
}

View File

@@ -1,71 +0,0 @@
package cn.iocoder.yudao.coreservice.modules.system.service.sms;
import cn.iocoder.yudao.coreservice.BaseDbUnitTest;
import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.sms.SysSmsTemplateDO;
import cn.iocoder.yudao.coreservice.modules.system.dal.mysql.sms.SysSmsTemplateCoreMapper;
import cn.iocoder.yudao.coreservice.modules.system.enums.sms.SysSmsTemplateTypeEnum;
import cn.iocoder.yudao.coreservice.modules.system.service.sms.impl.SysSmsTemplateCoreServiceImpl;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Import;
import javax.annotation.Resource;
import java.util.Date;
import java.util.Map;
import java.util.function.Consumer;
import static cn.hutool.core.bean.BeanUtil.getFieldValue;
import static cn.hutool.core.util.RandomUtil.randomEle;
import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.max;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
import static org.junit.jupiter.api.Assertions.assertEquals;
/**
* {@link SysSmsTemplateCoreServiceImpl} 的单元测试类
*
* @author 芋道源码
*/
@Import(SysSmsTemplateCoreServiceImpl.class)
public class SysSmsTemplateServiceTest extends BaseDbUnitTest {
@Resource
private SysSmsTemplateCoreServiceImpl smsTemplateCoreService;
@Resource
private SysSmsTemplateCoreMapper smsTemplateCoreMapper;
@Test
@SuppressWarnings("unchecked")
void testInitLocalCache() {
// mock 数据
SysSmsTemplateDO smsTemplate01 = randomSmsTemplateDO();
smsTemplateCoreMapper.insert(smsTemplate01);
SysSmsTemplateDO smsTemplate02 = randomSmsTemplateDO();
smsTemplateCoreMapper.insert(smsTemplate02);
// 调用
smsTemplateCoreService.initLocalCache();
// 断言 deptCache 缓存
Map<String, SysSmsTemplateDO> smsTemplateCache = (Map<String, SysSmsTemplateDO>) getFieldValue(smsTemplateCoreService, "smsTemplateCache");
assertEquals(2, smsTemplateCache.size());
assertPojoEquals(smsTemplate01, smsTemplateCache.get(smsTemplate01.getCode()));
assertPojoEquals(smsTemplate02, smsTemplateCache.get(smsTemplate02.getCode()));
// 断言 maxUpdateTime 缓存
Date maxUpdateTime = (Date) getFieldValue(smsTemplateCoreService, "maxUpdateTime");
assertEquals(max(smsTemplate01.getUpdateTime(), smsTemplate02.getUpdateTime()), maxUpdateTime);
}
// ========== 随机对象 ==========
@SafeVarargs
private static SysSmsTemplateDO randomSmsTemplateDO(Consumer<SysSmsTemplateDO>... consumers) {
Consumer<SysSmsTemplateDO> consumer = (o) -> {
o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围
o.setType(randomEle(SysSmsTemplateTypeEnum.values()).getType()); // 保证 type 的 范围
};
return randomPojo(SysSmsTemplateDO.class, ArrayUtils.append(consumer, consumers));
}
}

View File

@@ -1,171 +0,0 @@
package cn.iocoder.yudao.coreservice.modules.system.service.social;
import cn.iocoder.yudao.coreservice.BaseDbAndRedisUnitTest;
import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.social.SysSocialUserDO;
import cn.iocoder.yudao.coreservice.modules.system.dal.mysql.social.SysSocialUserCoreMapper;
import cn.iocoder.yudao.coreservice.modules.system.dal.redis.social.SysSocialAuthUserRedisDAO;
import cn.iocoder.yudao.coreservice.modules.system.enums.social.SysSocialTypeEnum;
import cn.iocoder.yudao.coreservice.modules.system.service.social.impl.SysSocialCoreServiceImpl;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import com.xkcoding.justauth.AuthRequestFactory;
import me.zhyd.oauth.model.AuthUser;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import;
import javax.annotation.Resource;
import java.util.List;
import static cn.hutool.core.util.RandomUtil.randomEle;
import static cn.hutool.core.util.RandomUtil.randomString;
import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
import static org.junit.jupiter.api.Assertions.assertEquals;
/**
* {@link SysSocialCoreServiceImpl} 的单元测试类
*
* @author 芋道源码
*/
@Import({SysSocialCoreServiceImpl.class, SysSocialAuthUserRedisDAO.class})
public class SysSocialCoreServiceTest extends BaseDbAndRedisUnitTest {
@Resource
private SysSocialCoreServiceImpl socialService;
@Resource
private SysSocialUserCoreMapper socialUserMapper;
@MockBean
private AuthRequestFactory authRequestFactory;
/**
* 情况一,创建 SysSocialUserDO 的情况
*/
@Test
public void testBindSocialUser_create() {
// mock 数据
// 准备参数
Long userId = randomLongId();
Integer type = randomEle(SysSocialTypeEnum.values()).getType();
AuthUser authUser = randomPojo(AuthUser.class);
// mock 方法
// 调用
socialService.bindSocialUser(userId, type, authUser, UserTypeEnum.ADMIN);
// 断言
List<SysSocialUserDO> socialUsers = socialUserMapper.selectList("user_id", userId);
assertEquals(1, socialUsers.size());
assertBindSocialUser(socialUsers.get(0), authUser, userId, type);
}
/**
* 情况二,更新 SysSocialUserDO 的情况
*/
@Test
public void testBindSocialUser_update() {
// mock 数据
SysSocialUserDO dbSocialUser = randomPojo(SysSocialUserDO.class, socialUserDO -> {
socialUserDO.setUserType(UserTypeEnum.ADMIN.getValue());
socialUserDO.setType(randomEle(SysSocialTypeEnum.values()).getType());
});
socialUserMapper.insert(dbSocialUser);
// 准备参数
Long userId = dbSocialUser.getUserId();
Integer type = dbSocialUser.getType();
AuthUser authUser = randomPojo(AuthUser.class);
// mock 方法
// 调用
socialService.bindSocialUser(userId, type, authUser, UserTypeEnum.ADMIN);
// 断言
List<SysSocialUserDO> socialUsers = socialUserMapper.selectList("user_id", userId);
assertEquals(1, socialUsers.size());
assertBindSocialUser(socialUsers.get(0), authUser, userId, type);
}
/**
* 情况一和二都存在的,逻辑二的场景
*/
@Test
public void testBindSocialUser_userId() {
// mock 数据
SysSocialUserDO dbSocialUser = randomPojo(SysSocialUserDO.class, socialUserDO -> {
socialUserDO.setUserType(UserTypeEnum.ADMIN.getValue());
socialUserDO.setType(randomEle(SysSocialTypeEnum.values()).getType());
});
socialUserMapper.insert(dbSocialUser);
// 准备参数
Long userId = randomLongId();
Integer type = dbSocialUser.getType();
AuthUser authUser = randomPojo(AuthUser.class);
// mock 方法
// 调用
socialService.bindSocialUser(userId, type, authUser, UserTypeEnum.ADMIN);
// 断言
List<SysSocialUserDO> socialUsers = socialUserMapper.selectList("user_id", userId);
assertEquals(1, socialUsers.size());
}
private void assertBindSocialUser(SysSocialUserDO socialUser, AuthUser authUser, Long userId,
Integer type) {
assertEquals(authUser.getToken().getAccessToken(), socialUser.getToken());
assertEquals(toJsonString(authUser.getToken()), socialUser.getRawTokenInfo());
assertEquals(authUser.getNickname(), socialUser.getNickname());
assertEquals(authUser.getAvatar(), socialUser.getAvatar());
assertEquals(toJsonString(authUser.getRawUserInfo()), socialUser.getRawUserInfo());
assertEquals(userId, socialUser.getUserId());
assertEquals(UserTypeEnum.ADMIN.getValue(), socialUser.getUserType());
assertEquals(type, socialUser.getType());
assertEquals(authUser.getUuid(), socialUser.getOpenid());
assertEquals(socialService.getAuthUserUnionId(authUser), socialUser.getUnionId());
}
/**
* 情况一,如果新老的 unionId 是一致的,无需解绑
*/
@Test
public void testUnbindOldSocialUser_no() {
// mock 数据
SysSocialUserDO oldSocialUser = randomPojo(SysSocialUserDO.class, socialUserDO -> {
socialUserDO.setUserType(UserTypeEnum.ADMIN.getValue());
socialUserDO.setType(randomEle(SysSocialTypeEnum.values()).getType());
});
socialUserMapper.insert(oldSocialUser);
// 准备参数
Long userId = oldSocialUser.getUserId();
Integer type = oldSocialUser.getType();
String newUnionId = oldSocialUser.getUnionId();
// 调用
socialService.unbindOldSocialUser(userId, type, newUnionId, UserTypeEnum.ADMIN);
// 断言
assertEquals(1L, socialUserMapper.selectCount(null).longValue());
}
/**
* 情况二,如果新老的 unionId 不一致的,需解绑
*/
@Test
public void testUnbindOldSocialUser_yes() {
// mock 数据
SysSocialUserDO oldSocialUser = randomPojo(SysSocialUserDO.class, socialUserDO -> {
socialUserDO.setUserType(UserTypeEnum.ADMIN.getValue());
socialUserDO.setType(randomEle(SysSocialTypeEnum.values()).getType());
});
socialUserMapper.insert(oldSocialUser);
// 准备参数
Long userId = oldSocialUser.getUserId();
Integer type = oldSocialUser.getType();
String newUnionId = randomString(10);
// 调用
socialService.unbindOldSocialUser(userId, type, newUnionId, UserTypeEnum.ADMIN);
// 断言
assertEquals(0L, socialUserMapper.selectCount(null).longValue());
}
}