4.26 ShardingSphere分库分表

This commit is contained in:
zhangsan 2025-04-26 20:06:51 +08:00
parent 012e66bfc8
commit 6a5fb45cc2
13 changed files with 303 additions and 24 deletions

View File

@ -96,6 +96,12 @@
<groupId>org.apache.commons</groupId> <groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId> <artifactId>commons-pool2</artifactId>
</dependency> </dependency>
<!-- 分库分表 -->
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
<version>5.2.0</version>
</dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId> <artifactId>spring-boot-configuration-processor</artifactId>

View File

@ -1,11 +1,11 @@
package edu.whut.smilepicturebackend; package edu.whut.smilepicturebackend;
import org.apache.shardingsphere.spring.boot.ShardingSphereAutoConfiguration;
import org.mybatis.spring.annotation.MapperScan; import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.annotation.EnableAsync;
@SpringBootApplication(exclude = {ShardingSphereAutoConfiguration.class})
@SpringBootApplication
@EnableAsync @EnableAsync
@MapperScan("edu.whut.smilepicturebackend.mapper") @MapperScan("edu.whut.smilepicturebackend.mapper")
public class SmilePictureBackendApplication { public class SmilePictureBackendApplication {

View File

@ -15,6 +15,7 @@ import edu.whut.smilepicturebackend.constant.UserConstant;
import edu.whut.smilepicturebackend.exception.BusinessException; import edu.whut.smilepicturebackend.exception.BusinessException;
import edu.whut.smilepicturebackend.exception.ErrorCode; import edu.whut.smilepicturebackend.exception.ErrorCode;
import edu.whut.smilepicturebackend.exception.ThrowUtils; import edu.whut.smilepicturebackend.exception.ThrowUtils;
import edu.whut.smilepicturebackend.manager.auth.SpaceUserAuthManager;
import edu.whut.smilepicturebackend.manager.auth.StpKit; import edu.whut.smilepicturebackend.manager.auth.StpKit;
import edu.whut.smilepicturebackend.manager.auth.annotation.SaSpaceCheckPermission; import edu.whut.smilepicturebackend.manager.auth.annotation.SaSpaceCheckPermission;
import edu.whut.smilepicturebackend.manager.auth.model.SpaceUserPermissionConstant; import edu.whut.smilepicturebackend.manager.auth.model.SpaceUserPermissionConstant;
@ -49,6 +50,7 @@ public class PictureController {
private final PictureService pictureService; private final PictureService pictureService;
private final SpaceService spaceService; private final SpaceService spaceService;
private final AliYunAiApi aliYunAiApi; private final AliYunAiApi aliYunAiApi;
private final SpaceUserAuthManager spaceUserAuthManager;
/** /**
@ -176,15 +178,22 @@ public class PictureController {
ThrowUtils.throwIf(PictureReviewStatusEnum.PASS.getValue()!=picture.getReviewStatus(),ErrorCode.NOT_FOUND_ERROR); ThrowUtils.throwIf(PictureReviewStatusEnum.PASS.getValue()!=picture.getReviewStatus(),ErrorCode.NOT_FOUND_ERROR);
// 空间权限校验 // 空间权限校验
Long spaceId = picture.getSpaceId(); Long spaceId = picture.getSpaceId();
Space space = null;
// 权限判断 只对团队空间才要求登录 & 权限
if (spaceId != null) { if (spaceId != null) {
boolean hasPermission = StpKit.SPACE.hasPermission(SpaceUserPermissionConstant.PICTURE_VIEW); //编程式鉴权 boolean hasPermission = StpKit.SPACE.hasPermission(SpaceUserPermissionConstant.PICTURE_VIEW); //编程式鉴权
ThrowUtils.throwIf(!hasPermission, ErrorCode.NO_AUTH_ERROR); ThrowUtils.throwIf(!hasPermission, ErrorCode.NO_AUTH_ERROR);
//User loginUser = userService.getLoginUser(request); //User loginUser = userService.getLoginUser(request);
//改为使用注解鉴权 //已经改为使用sa-token鉴权
//pictureService.checkPictureAuth(loginUser, picture); //pictureService.checkPictureAuth(loginUser, picture);
} }
// 获取权限列表
User loginUser = userService.getLoginUser(request);
List<String> permissionList = spaceUserAuthManager.getPermissionList(space, loginUser);
PictureVO pictureVO = pictureService.getPictureVO(picture, request);
pictureVO.setPermissionList(permissionList);
// 获取封装类 // 获取封装类
return ResultUtils.success(pictureService.getPictureVO(picture, request)); return ResultUtils.success(pictureVO);
} }
/** /**

View File

@ -6,7 +6,6 @@ import edu.whut.smilepicturebackend.exception.ThrowUtils;
import edu.whut.smilepicturebackend.model.dto.space.analyze.*; import edu.whut.smilepicturebackend.model.dto.space.analyze.*;
import edu.whut.smilepicturebackend.model.entity.Space; import edu.whut.smilepicturebackend.model.entity.Space;
import edu.whut.smilepicturebackend.model.entity.User; import edu.whut.smilepicturebackend.model.entity.User;
import edu.whut.smilepicturebackend.model.vo.analyze.*;
import edu.whut.smilepicturebackend.model.vo.space.analyze.*; import edu.whut.smilepicturebackend.model.vo.space.analyze.*;
import edu.whut.smilepicturebackend.service.SpaceAnalyzeService; import edu.whut.smilepicturebackend.service.SpaceAnalyzeService;
import edu.whut.smilepicturebackend.service.UserService; import edu.whut.smilepicturebackend.service.UserService;

View File

@ -9,6 +9,7 @@ import edu.whut.smilepicturebackend.constant.UserConstant;
import edu.whut.smilepicturebackend.exception.BusinessException; import edu.whut.smilepicturebackend.exception.BusinessException;
import edu.whut.smilepicturebackend.exception.ErrorCode; import edu.whut.smilepicturebackend.exception.ErrorCode;
import edu.whut.smilepicturebackend.exception.ThrowUtils; import edu.whut.smilepicturebackend.exception.ThrowUtils;
import edu.whut.smilepicturebackend.manager.auth.SpaceUserAuthManager;
import edu.whut.smilepicturebackend.model.dto.space.*; import edu.whut.smilepicturebackend.model.dto.space.*;
import edu.whut.smilepicturebackend.model.entity.Space; import edu.whut.smilepicturebackend.model.entity.Space;
import edu.whut.smilepicturebackend.model.entity.User; import edu.whut.smilepicturebackend.model.entity.User;
@ -41,6 +42,7 @@ public class SpaceController {
private final SpaceService spaceService; private final SpaceService spaceService;
private final SpaceUserAuthManager spaceUserAuthManager;
@PostMapping("/add") @PostMapping("/add")
public BaseResponse<Long> addSpace(@RequestBody SpaceAddRequest spaceAddRequest, HttpServletRequest request) { public BaseResponse<Long> addSpace(@RequestBody SpaceAddRequest spaceAddRequest, HttpServletRequest request) {
@ -123,7 +125,11 @@ public class SpaceController {
// 查询数据库 // 查询数据库
Space space = spaceService.getById(id); Space space = spaceService.getById(id);
ThrowUtils.throwIf(space == null, ErrorCode.NOT_FOUND_ERROR); ThrowUtils.throwIf(space == null, ErrorCode.NOT_FOUND_ERROR);
return ResultUtils.success(spaceService.getSpaceVO(space,request)); SpaceVO spaceVO = spaceService.getSpaceVO(space, request);
User loginUser = userService.getLoginUser(request);
List<String> permissionList = spaceUserAuthManager.getPermissionList(space, loginUser);
spaceVO.setPermissionList(permissionList);
return ResultUtils.success(spaceVO);
} }
/** /**

View File

@ -1,5 +1,7 @@
package edu.whut.smilepicturebackend.exception; package edu.whut.smilepicturebackend.exception;
import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.exception.NotPermissionException;
import edu.whut.smilepicturebackend.common.BaseResponse; import edu.whut.smilepicturebackend.common.BaseResponse;
import edu.whut.smilepicturebackend.common.ResultUtils; import edu.whut.smilepicturebackend.common.ResultUtils;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@ -12,6 +14,17 @@ import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice @RestControllerAdvice
@Slf4j @Slf4j
public class GlobalExceptionHandler { public class GlobalExceptionHandler {
@ExceptionHandler(NotLoginException.class)
public BaseResponse<?> notLoginException(NotLoginException e) {
log.error("NotLoginException", e);
return ResultUtils.error(ErrorCode.NOT_LOGIN_ERROR, e.getMessage());
}
@ExceptionHandler(NotPermissionException.class)
public BaseResponse<?> notPermissionExceptionHandler(NotPermissionException e) {
log.error("NotPermissionException", e);
return ResultUtils.error(ErrorCode.NO_AUTH_ERROR, e.getMessage());
}
@ExceptionHandler(BusinessException.class) @ExceptionHandler(BusinessException.class)
public BaseResponse<?> businessExceptionHandler(BusinessException e) { public BaseResponse<?> businessExceptionHandler(BusinessException e) {

View File

@ -0,0 +1,142 @@
package edu.whut.smilepicturebackend.manager.sharding;
import com.baomidou.mybatisplus.extension.toolkit.SqlRunner;
import edu.whut.smilepicturebackend.model.entity.Space;
import edu.whut.smilepicturebackend.model.enums.SpaceLevelEnum;
import edu.whut.smilepicturebackend.model.enums.SpaceTypeEnum;
import edu.whut.smilepicturebackend.service.SpaceService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.shardingsphere.driver.jdbc.core.connection.ShardingSphereConnection;
import org.apache.shardingsphere.infra.metadata.database.rule.ShardingSphereRuleMetaData;
import org.apache.shardingsphere.mode.manager.ContextManager;
import org.apache.shardingsphere.sharding.api.config.ShardingRuleConfiguration;
import org.apache.shardingsphere.sharding.api.config.rule.ShardingTableRuleConfiguration;
import org.apache.shardingsphere.sharding.rule.ShardingRule;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
//@Component
@Slf4j
@RequiredArgsConstructor
public class DynamicShardingManager {
private final DataSource dataSource;
private final SpaceService spaceService;
private static final String LOGIC_TABLE_NAME = "picture";
private static final String DATABASE_NAME = "logic_db"; // 配置文件中的数据库名称
@PostConstruct
public void initialize() {
log.info("初始化动态分表配置...");
updateShardingTableNodes();
}
/**
* 获取所有动态表名包括初始表 picture 和分表 picture_{spaceId}
*/
private Set<String> fetchAllPictureTableNames() {
// 为了测试方便直接对所有团队空间分表实际上线改为仅对团队空间的旗舰版生效
Set<Long> spaceIds = spaceService.lambdaQuery()
.eq(Space::getSpaceType, SpaceTypeEnum.TEAM.getValue())
.list()
.stream()
.map(Space::getId)
.collect(Collectors.toSet());
Set<String> tableNames = spaceIds.stream()
.map(spaceId -> LOGIC_TABLE_NAME + "_" + spaceId)
.collect(Collectors.toSet());
tableNames.add(LOGIC_TABLE_NAME); // 添加初始逻辑表
return tableNames;
}
/**
* 更新 ShardingSphere actual-data-nodes 动态表名配置
*/
private void updateShardingTableNodes() {
Set<String> tableNames = fetchAllPictureTableNames();
// smile-picture.picture_112321321,smile-picture.picture_1123213123
String newActualDataNodes = tableNames.stream()
.map(tableName -> "smile-picture." + tableName) // 确保前缀合法
.collect(Collectors.joining(","));
log.info("动态分表 actual-data-nodes 配置: {}", newActualDataNodes);
ContextManager contextManager = getContextManager();
ShardingSphereRuleMetaData ruleMetaData = contextManager.getMetaDataContexts()
.getMetaData()
.getDatabases()
.get(DATABASE_NAME)
.getRuleMetaData();
Optional<ShardingRule> shardingRule = ruleMetaData.findSingleRule(ShardingRule.class);
if (shardingRule.isPresent()) {
ShardingRuleConfiguration ruleConfig = (ShardingRuleConfiguration) shardingRule.get().getConfiguration();
List<ShardingTableRuleConfiguration> updatedRules = ruleConfig.getTables()
.stream()
.map(oldTableRule -> {
if (LOGIC_TABLE_NAME.equals(oldTableRule.getLogicTable())) {
ShardingTableRuleConfiguration newTableRuleConfig = new ShardingTableRuleConfiguration(LOGIC_TABLE_NAME, newActualDataNodes);
newTableRuleConfig.setDatabaseShardingStrategy(oldTableRule.getDatabaseShardingStrategy());
newTableRuleConfig.setTableShardingStrategy(oldTableRule.getTableShardingStrategy());
newTableRuleConfig.setKeyGenerateStrategy(oldTableRule.getKeyGenerateStrategy());
newTableRuleConfig.setAuditStrategy(oldTableRule.getAuditStrategy());
return newTableRuleConfig;
}
return oldTableRule;
})
.collect(Collectors.toList());
ruleConfig.setTables(updatedRules);
contextManager.alterRuleConfiguration(DATABASE_NAME, Collections.singleton(ruleConfig));
contextManager.reloadDatabase(DATABASE_NAME);
log.info("动态分表规则更新成功!");
} else {
log.error("未找到 ShardingSphere 的分片规则配置,动态分表更新失败。");
}
}
/**
* 动态创建空间图片分表
*
* @param space
*/
public void createSpacePictureTable(Space space) {
// 仅为旗舰版团队空间创建分表
if (space.getSpaceType() == SpaceTypeEnum.TEAM.getValue() && space.getSpaceLevel() == SpaceLevelEnum.FLAGSHIP.getValue()) {
Long spaceId = space.getId();
String tableName = LOGIC_TABLE_NAME + "_" + spaceId;
// 创建新表
String createTableSql = "CREATE TABLE " + tableName + " LIKE " + LOGIC_TABLE_NAME; //like创建与逻辑表一模一样的表
try {
SqlRunner.db().update(createTableSql);
// 更新分表
updateShardingTableNodes();
} catch (Exception e) {
e.printStackTrace();
log.error("创建图片空间分表失败,空间 id = {}", space.getId());
}
}
}
/**
* 获取 ShardingSphere ContextManager
*/
private ContextManager getContextManager() {
try (ShardingSphereConnection connection = dataSource.getConnection().unwrap(ShardingSphereConnection.class)) {
return connection.getContextManager();
} catch (SQLException e) {
throw new RuntimeException("获取 ShardingSphere ContextManager 失败", e);
}
}
}

View File

@ -0,0 +1,47 @@
package edu.whut.smilepicturebackend.manager.sharding;
import org.apache.shardingsphere.sharding.api.sharding.standard.PreciseShardingValue;
import org.apache.shardingsphere.sharding.api.sharding.standard.RangeShardingValue;
import org.apache.shardingsphere.sharding.api.sharding.standard.StandardShardingAlgorithm;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Properties;
/**
* 图片分表算法
*/
public class PictureShardingAlgorithm implements StandardShardingAlgorithm<Long> {
//availableTargetNames指实际表名集合 preciseShardingValue这里指spaceid
@Override
public String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<Long> preciseShardingValue) {
Long spaceId = preciseShardingValue.getValue();
String logicTableName = preciseShardingValue.getLogicTableName();
// spaceId null 表示查询所有图片
if (spaceId == null) {
return logicTableName;
}
// 根据 spaceId 动态生成分表名
String realTableName = "picture_" + spaceId;
if (availableTargetNames.contains(realTableName)) {
return realTableName;
} else {
return logicTableName;
}
}
@Override
public Collection<String> doSharding(Collection<String> collection, RangeShardingValue<Long> rangeShardingValue) {
return new ArrayList<>();
}
@Override
public Properties getProps() {
return null;
}
@Override
public void init(Properties properties) {
}
}

View File

@ -10,6 +10,7 @@ import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil; import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import edu.whut.smilepicturebackend.api.aliyunai.AliYunAiApi; import edu.whut.smilepicturebackend.api.aliyunai.AliYunAiApi;
@ -72,13 +73,21 @@ import java.util.stream.Collectors;
public class PictureServiceImpl extends ServiceImpl<PictureMapper, Picture> public class PictureServiceImpl extends ServiceImpl<PictureMapper, Picture>
implements PictureService { implements PictureService {
private final FileManager fileManager; private final FileManager fileManager;
private final UserService userService; private final UserService userService;
private final FilePictureUpload filePictureUpload; private final FilePictureUpload filePictureUpload;
private final UrlPictureUpload urlPictureUpload; private final UrlPictureUpload urlPictureUpload;
private final MyCacheManager cacheManager; private final MyCacheManager cacheManager;
private final CosManager cosManager; private final CosManager cosManager;
private final SpaceService spaceService; private final SpaceService spaceService;
private final TransactionTemplate transactionTemplate; private final TransactionTemplate transactionTemplate;
private final AliYunAiApi aliYunAiApi; private final AliYunAiApi aliYunAiApi;
@Override @Override
@ -180,7 +189,6 @@ public class PictureServiceImpl extends ServiceImpl<PictureMapper, Picture>
picture.setUserId(loginUser.getId()); picture.setUserId(loginUser.getId());
picture.setSpaceId(spaceId); picture.setSpaceId(spaceId);
// 转换为标准颜色 // 转换为标准颜色
//TODO:不知道为什么没有正确设置到数据库
log.info("颜色"+uploadPictureResult.getPicColor()); log.info("颜色"+uploadPictureResult.getPicColor());
picture.setPicColor(ColorTransformUtils.getStandardColor(uploadPictureResult.getPicColor())); picture.setPicColor(ColorTransformUtils.getStandardColor(uploadPictureResult.getPicColor()));
// 补充审核参数 // 补充审核参数
@ -198,21 +206,34 @@ public class PictureServiceImpl extends ServiceImpl<PictureMapper, Picture>
// 开启事务,图片上传成功和修改额度一定要同时成功或失败 // 开启事务,图片上传成功和修改额度一定要同时成功或失败
Long finalSpaceId = spaceId; Long finalSpaceId = spaceId;
transactionTemplate.execute(status -> { transactionTemplate.execute(status -> {
// 插入数据 log.info("uploadPicture | spaceId={}, sizeDelta={}, countDelta={}",
boolean result = this.saveOrUpdate(picture); finalSpaceId, sizeDelta, countDelta);
ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR, "图片上传失败,数据库操作失败");
if (finalSpaceId != null) { /* ---------- 1. 保存 / 更新图片记录 ---------- */
// 更新空间的使用额度 boolean saved = this.saveOrUpdate(picture);
boolean update = spaceService.lambdaUpdate() ThrowUtils.throwIf(!saved, ErrorCode.OPERATION_ERROR, "图片上传失败,数据库操作失败");
.eq(Space::getId, finalSpaceId)
// 更新 total_size /* ---------- 2. 更新空间额度(只有有变化时才执行 UPDATE ---------- */
.apply(sizeDelta != 0, "total_size = total_size + {0}", sizeDelta) // 占位符安全绑定[1][2] if (finalSpaceId != null && (sizeDelta != 0 || countDelta != 0)) {
// 更新 total_count只有新增才加 1 LambdaUpdateChainWrapper<Space> wrapper = spaceService.lambdaUpdate()
.apply(countDelta != 0, "total_count = total_count + {0}", countDelta) .eq(Space::getId, finalSpaceId);
.update();
ThrowUtils.throwIf(!update, ErrorCode.OPERATION_ERROR, "额度更新失败"); // 组装可变 SQL不生成空 SET
StringBuilder setSql = new StringBuilder();
if (sizeDelta != 0) {
setSql.append("total_size = total_size + ").append(sizeDelta);
}
if (countDelta != 0) {
if (setSql.length() > 0) setSql.append(", ");
setSql.append("total_count = total_count + ").append(countDelta);
}
wrapper.setSql(setSql.toString());
boolean updated = wrapper.update();
ThrowUtils.throwIf(!updated, ErrorCode.OPERATION_ERROR, "额度更新失败");
} }
return picture;
return picture; // 事务块返回结果
}); });
//如果是更新清理旧的图片 //如果是更新清理旧的图片
if (oldPicture != null) { if (oldPicture != null) {

View File

@ -26,6 +26,7 @@ import edu.whut.smilepicturebackend.model.vo.UserVO;
import edu.whut.smilepicturebackend.service.SpaceService; import edu.whut.smilepicturebackend.service.SpaceService;
import edu.whut.smilepicturebackend.service.SpaceUserService; import edu.whut.smilepicturebackend.service.SpaceUserService;
import edu.whut.smilepicturebackend.service.UserService; import edu.whut.smilepicturebackend.service.UserService;
import org.springframework.context.annotation.Lazy;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils; import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -51,8 +52,13 @@ public class SpaceServiceImpl extends ServiceImpl<SpaceMapper, Space>
private final UserService userService; private final UserService userService;
// 静态锁表JVM 级别共享 // 静态锁表JVM 级别共享
private static final ConcurrentHashMap<Long, Object> USER_LOCKS = new ConcurrentHashMap<>(); private static final ConcurrentHashMap<Long, Object> USER_LOCKS = new ConcurrentHashMap<>();
private final SpaceUserService spaceUserService; private final SpaceUserService spaceUserService;
private final TransactionTemplate transactionTemplate; private final TransactionTemplate transactionTemplate;
// @Resource
// @Lazy
// private DynamicShardingManager dynamicShardingManager;
/** /**
* 创建空间 加锁和事务 * 创建空间 加锁和事务
@ -110,6 +116,8 @@ public class SpaceServiceImpl extends ServiceImpl<SpaceMapper, Space>
result = spaceUserService.save(spaceUser); result = spaceUserService.save(spaceUser);
ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR, "创建团队成员记录失败"); ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR, "创建团队成员记录失败");
} }
//创建分表仅对团队空间生效为方便部署暂时不使用
// dynamicShardingManager.createSpacePictureTable(space);
return space.getId(); return space.getId();
}); });
return Optional.ofNullable(newSpaceId).orElse(-1L); return Optional.ofNullable(newSpaceId).orElse(-1L);

View File

@ -26,6 +26,8 @@ import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils; import org.springframework.beans.BeanUtils;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@ -43,8 +45,9 @@ public class SpaceUserServiceImpl extends ServiceImpl<SpaceUserMapper, SpaceUser
implements SpaceUserService { implements SpaceUserService {
private final UserService userService; private final UserService userService;
@Resource
@Lazy @Lazy
private final SpaceService spaceService; private SpaceService spaceService;
@Override @Override
public long addSpaceUser(SpaceUserAddRequest spaceUserAddRequest) { public long addSpaceUser(SpaceUserAddRequest spaceUserAddRequest) {

View File

@ -129,7 +129,6 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User>
// 4. 保存用户的登录态 // 4. 保存用户的登录态
request.getSession().setAttribute(UserConstant.USER_LOGIN_STATE, user); request.getSession().setAttribute(UserConstant.USER_LOGIN_STATE, user);
// 记录用户登录态到 Sa-token便于空间鉴权时使用注意保证该用户信息与 SpringSession 中的信息过期时间一致 // 记录用户登录态到 Sa-token便于空间鉴权时使用注意保证该用户信息与 SpringSession 中的信息过期时间一致
StpKit.SPACE.login(user.getId()); StpKit.SPACE.login(user.getId());
StpKit.SPACE.getSession().set(UserConstant.USER_LOGIN_STATE, user); StpKit.SPACE.getSession().set(UserConstant.USER_LOGIN_STATE, user);
return this.getLoginUserVO(user); return this.getLoginUserVO(user);

View File

@ -34,7 +34,33 @@ spring:
multipart: multipart:
max-file-size: 10MB max-file-size: 10MB
max-request-size: 10MB max-request-size: 10MB
# 空间图片分表
shardingsphere:
datasource:
names: smile-picture
smile-picture:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/smile-picture
username: root
password: 123456
rules:
sharding:
tables:
picture:
actual-data-nodes: smile-picture.picture # 动态分表
table-strategy:
standard:
sharding-column: space_id
sharding-algorithm-name: picture_sharding_algorithm # 使用自定义分片算法
sharding-algorithms:
picture_sharding_algorithm:
type: CLASS_BASED
props:
strategy: standard
algorithmClassName: edu.whut.smilepicturebackend.manager.sharding.PictureShardingAlgorithm
props:
sql-show: true #打印实际执行的sql
mybatis-plus: mybatis-plus:
type-aliases-package: edu.whut.smilepicturebackend.model.entity type-aliases-package: edu.whut.smilepicturebackend.model.entity
configuration: configuration: