7:创建用户后默认进行短链接分组
首先对短链接组进行分表,
用户已经进行分表了,一个用户可能默认有10条短链接,所以分组的表数量至少大于用户表数量。
短链接组 是为了方便用户与其创建的短链接进行对齐。
这里使用username作为分片元素,因为创建用户后,根据用户名来打到默认的分组表上。
dataSources:
ds_0:
dataSourceClassName: com.zaxxer.hikari.HikariDataSource #指定数据源 HikariCP连接池,这是一个高性能的 JDBC 连接池
driverClassName: com.mysql.cj.jdbc.Driver # 指定jabc驱动
jdbcUrl: jdbc:mysql://127.0.0.1:3306/link?useUnicode=true&characterEncoding=UTF-8&rewriteBatchedStatements=true&allowMultiQueries=true&serverTimezone=Asia/Shanghai #指定数据库位置以及一些参数
username: root #数据库连接的用户名与密码
password: 171612Cgj.
rules: # 这部分配置了两个主要的功能:数据分片和数据加密。
- !SHARDING # 数据分片
tables:
t_user: # 对用户进行分表
actualDataNodes: ds_0.t_user_${0..15} #数据实际存储节点: user_0 -> user_15 这16个表 存储在ds_0这个库下
tableStrategy: #定义表的分片策略
standard:
shardingColumn: username #分片依据的字段
shardingAlgorithmName: user_table_hash_mod #分片算法
t_group: # 对短链接分组进行分表
actualDataNodes: ds_0.t_group_${0..15}
tableStrategy:
standard:
shardingColumn: username
shardingAlgorithmName: group_table_hash_mod
shardingAlgorithms: # 具体的分片算法
user_table_hash_mod:
type: HASH_MOD #具体的分片算法是:HASH_MOD
props:
sharding-count: 16 #分片数量 数据将根据分片算法被分散到 16 个不同的分表中
group_table_hash_mod:
type: HASH_MOD
props:
sharding-count: 16
- !ENCRYPT #数据加密
tables:
t_user:
columns: #需要加密的字段 t_user表中的phone字段和mail字段
phone:
cipherColumn: phone # 加密后存储的字段名这里直接用原列名表示加密覆盖原数据。
encryptorName: common_encryptor # 使用的加密器名称。
mail:
cipherColumn: mail
encryptorName: common_encryptor
queryWithCipherColumn: true #是否在查询时使用加密列。
encryptors:
common_encryptor:
type: AES # 使用的加密算法是AES加密算法 注意这个加密是可逆的,这是选它的一个原因
props:
aes-key-value: d6oadClrrb9A3GWo # 这是 AES 加密算法使用的密钥
props:
sql-show: true #ShardingSphere 会在日志中显示实际执行的 SQL 语句以及逻辑sql
controller - 查询短链接分组集合 - listGroup
/**
* 查询短链接分组集合 / 当前用户有那些分组,每组里面有几个短链接
*/
@GetMapping("/api/short-link/admin/v1/group")
public Result<List<ShortLinkGroupRespDTO>> listGroup() {
return Results.success(groupService.listGroup());
}
返回的响应 - ShortLinkGroupRespDTO
@Data
public class ShortLinkGroupRespDTO {
/**
* 分组标识
*/
private String gid;
/**
* 分组名称
*/
private String name;
/**
* 分组排序
*/
private Integer sortOrder;
/**
* 分组下短链接数量
*/
private Integer shortLinkCount;
}
当前用户下,所有分组:如下
service - 查询短链接分组集合 - listGroup
根据登录的当前用户名 -> 查询当前用户的短链接分组集合
@Override
public List<ShortLinkGroupRespDTO> listGroup() {
LambdaQueryWrapper<GroupDO> queryWrapper = Wrappers.lambdaQuery(GroupDO.class)
.eq(GroupDO::getDelFlag, 0)
.eq(GroupDO::getUsername, UserContext.getUsername())
.orderByDesc(GroupDO::getSortOrder, GroupDO::getUpdateTime);
List<GroupDO> groupDOList = baseMapper.selectList(queryWrapper);//拿到当前用户的所有短链接分组的gid信息
Result<List<ShortLinkGroupCountQueryRespDTO>> listResult = shortLinkActualRemoteService
.listGroupShortLinkCount(groupDOList.stream().map(GroupDO::getGid).toList());//通过微服务间的远程调用,将gid集合发送到project服务中。返回数据是一个list,里面的每个元素是gid和分组的数量
List<ShortLinkGroupRespDTO> shortLinkGroupRespDTOList = BeanUtil.copyToList(groupDOList, ShortLinkGroupRespDTO.class);
//ShortLinkGroupRespDTO内容是:分组标识gid,分组名称 name,分组排序sortOrder,分组下短链接数量:shortLinkCount
shortLinkGroupRespDTOList.forEach(each -> {
Optional<ShortLinkGroupCountQueryRespDTO> first = listResult.getData().stream()
.filter(item -> Objects.equals(item.getGid(), each.getGid()))
.findFirst();
first.ifPresent(item -> each.setShortLinkCount(first.get().getShortLinkCount()));
});
//对于每一个 ShortLinkGroupRespDTO 对象,尝试从远程服务返回的 listResult 数据中找到匹配的 gid。如果找到匹配的 gid,则将对应的短链接数量 (shortLinkCount) 设置到当前的 ShortLinkGroupRespDTO 对象中。
return shortLinkGroupRespDTOList;
}
流程:
1:根据当前用户的username查询数据库,找到 List< GroupDO > 即一个用户短链接分组的集合,里面的主要内容是username下的gid
2:通过微服务的远程调用,获取到 List< ShortLinkGroupCountQueryRespDTO> 即一个集合,里面的每个元素是gid和分组的数量
3:对List< ShortLinkGroupCountQueryRespDTO> 这个集合中gid进行遍历找到匹配的gid,然后将短链接总数设置到 List< GroupDO > 中
短链接中台远程调用服务 - ShortLinkActualRemoteService
/**
* 短链接中台远程调用服务
*/
@FeignClient(
value = "short-link-project",
url = "${aggregation.remote-url:}",
configuration = OpenFeignConfiguration.class
)
public interface ShortLinkActualRemoteService {
/**
* 查询分组短链接总量
*
* @param requestParam 分组短链接总量请求参数
* @return 查询分组短链接总量响应
*/
@GetMapping("/api/short-link/v1/count")
Result<List<ShortLinkGroupCountQueryRespDTO>> listGroupShortLinkCount(@RequestParam("requestParam") List<String> requestParam);
短链接分组查询返回参数 - ShortLinkGroupCountQueryRespDTO
/**
* 短链接分组查询返回参数
*/
@Data
public class ShortLinkGroupCountQueryRespDTO {
/**
* 分组标识
*/
private String gid;
/**
* 短链接数量
*/
private Integer shortLinkCount;
}
注册用户以后 会对用户进行默认的分组
@Transactional(rollbackFor = Exception.class) //数据库事务操作,只要出现异常就回滚
@Override
public void register(UserRegisterReqDTO requestParam) {
//判断用户名是否存在
if (!hasUsername(requestParam.getUsername())) {
throw new ClientException(USER_NAME_EXIST); //如果用户名已存在,就抛出异常-用户名已存在
}
//调用redissonClient获取分布式锁RLock,锁的名字是LOCK_USER_REGISTER_KEY + 用户名 主要是为了防止大量请求注册同一个用户名
RLock lock = redissonClient.getLock(LOCK_USER_REGISTER_KEY + requestParam.getUsername());
if (!lock.tryLock()) { //尝试获取锁,如果获取锁失败,说明已经有人抢占这个用户名注册了
throw new ClientException(USER_NAME_EXIST); //用户名已存在
}
try {
int inserted = baseMapper.insert(BeanUtil.toBean(requestParam, UserDO.class));
if (inserted < 1) { //插入操作成功会返回1
throw new ClientException(USER_SAVE_ERROR);
}
groupService.saveGroup(requestParam.getUsername(), "默认分组"); //将当前的用户名保1却6存在一个分组中,便于管理
userRegisterCachePenetrationBloomFilter.add(requestParam.getUsername()); //将当前的用户名放入布隆过滤器中,防止缓存穿透
} catch (DuplicateKeyException ex) { //DuplicateKeyException -> 唯一键冲突 -> 用户名已存在 之所以还会出现这样的问题是因为:1:布隆过滤器有误判 2:高并发情况下那面会有极少数并发出现
throw new ClientException(USER_EXIST);
} finally {
lock.unlock(); //最后不敢什么情况必须释放锁
}
}
用户注册以后会创建一个名为“默认分组”的分组,方便用户后续创建短链接的时候,放在默认分组中。