refactor: complete infrastructure refactoring phase 1

- Restore DAO layer design pattern (DictionaryDao, OperationLogDao, SysUserDao)
- Update Repository implementations to use DAO layer
- Add batch conversion methods to all converters (toEntityList, toDomainList)
- Migrate DictionaryHandler to functional WebFlux style
- Create unified SystemRouter configuration
- Add comprehensive .gitignore file
This commit is contained in:
张翔
2026-03-12 12:37:14 +08:00
parent 4366e742dc
commit c8646974d8
19 changed files with 453 additions and 17 deletions
@@ -0,0 +1,27 @@
package cn.novalon.manage.sys.config;
import cn.novalon.manage.sys.handler.dictionary.DictionaryHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.ServerResponse;
import static org.springframework.web.reactive.function.server.RequestPredicates.*;
import static org.springframework.web.reactive.function.server.RouterFunctions.route;
@Configuration
public class SystemRouter {
@Bean
public RouterFunction<ServerResponse> dictionaryRoutes(DictionaryHandler dictionaryHandler) {
return route()
.GET("/api/dictionaries", dictionaryHandler::getAllDictionaries)
.GET("/api/dictionaries/{id}", dictionaryHandler::getDictionaryById)
.GET("/api/dictionaries/type/{type}", dictionaryHandler::getDictionariesByType)
.GET("/api/dictionaries/check/exists", dictionaryHandler::checkTypeAndCodeExists)
.POST("/api/dictionaries", dictionaryHandler::createDictionary)
.PUT("/api/dictionaries/{id}", dictionaryHandler::updateDictionary)
.DELETE("/api/dictionaries/{id}", dictionaryHandler::deleteDictionary)
.build();
}
}
@@ -0,0 +1,64 @@
package cn.novalon.manage.sys.handler.dictionary;
import cn.novalon.manage.sys.core.domain.Dictionary;
import cn.novalon.manage.sys.core.service.IDictionaryService;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;
@Component
public class DictionaryHandler {
private final IDictionaryService dictionaryService;
public DictionaryHandler(IDictionaryService dictionaryService) {
this.dictionaryService = dictionaryService;
}
public Mono<ServerResponse> getAllDictionaries(ServerRequest request) {
return ServerResponse.ok()
.body(dictionaryService.findAll(), Dictionary.class);
}
public Mono<ServerResponse> getDictionaryById(ServerRequest request) {
Long id = Long.valueOf(request.pathVariable("id"));
return dictionaryService.findById(id)
.flatMap(dict -> ServerResponse.ok().bodyValue(dict))
.switchIfEmpty(ServerResponse.notFound().build());
}
public Mono<ServerResponse> getDictionariesByType(ServerRequest request) {
String type = request.pathVariable("type");
return ServerResponse.ok()
.body(dictionaryService.findByType(type), Dictionary.class);
}
public Mono<ServerResponse> checkTypeAndCodeExists(ServerRequest request) {
String type = request.queryParam("type").orElse(null);
String code = request.queryParam("code").orElse(null);
return dictionaryService.checkTypeAndCodeExists(type, code)
.flatMap(exists -> ServerResponse.ok().bodyValue(exists));
}
public Mono<ServerResponse> createDictionary(ServerRequest request) {
return request.bodyToMono(Dictionary.class)
.flatMap(dictionaryService::save)
.flatMap(dict -> ServerResponse.status(HttpStatus.CREATED).bodyValue(dict));
}
public Mono<ServerResponse> updateDictionary(ServerRequest request) {
Long id = Long.valueOf(request.pathVariable("id"));
return request.bodyToMono(Dictionary.class)
.flatMap(dictionary -> dictionaryService.update(id, dictionary))
.flatMap(dict -> ServerResponse.ok().bodyValue(dict))
.switchIfEmpty(ServerResponse.notFound().build());
}
public Mono<ServerResponse> deleteDictionary(ServerRequest request) {
Long id = Long.valueOf(request.pathVariable("id"));
return dictionaryService.deleteById(id)
.then(ServerResponse.noContent().build());
}
}
@@ -0,0 +1,66 @@
package cn.novalon.manage.sys.infrastructure.db.converter;
import cn.novalon.manage.sys.core.domain.Dictionary;
import cn.novalon.manage.sys.infrastructure.db.entity.DictionaryEntity;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.stream.Collectors;
@Component
public class DictionaryConverter {
public DictionaryEntity toEntity(Dictionary domain) {
if (domain == null) {
return null;
}
DictionaryEntity entity = new DictionaryEntity();
entity.setId(domain.getId());
entity.setType(domain.getType());
entity.setCode(domain.getCode());
entity.setName(domain.getName());
entity.setValue(domain.getValue());
entity.setRemark(domain.getRemark());
entity.setSort(domain.getSort());
entity.setCreateBy(domain.getCreateBy());
entity.setCreatedAt(domain.getCreatedAt());
entity.setUpdatedAt(domain.getUpdatedAt());
return entity;
}
public Dictionary toDomain(DictionaryEntity entity) {
if (entity == null) {
return null;
}
Dictionary domain = new Dictionary();
domain.setId(entity.getId());
domain.setType(entity.getType());
domain.setCode(entity.getCode());
domain.setName(entity.getName());
domain.setValue(entity.getValue());
domain.setRemark(entity.getRemark());
domain.setSort(entity.getSort());
domain.setCreateBy(entity.getCreateBy());
domain.setCreatedAt(entity.getCreatedAt());
domain.setUpdatedAt(entity.getUpdatedAt());
return domain;
}
public List<DictionaryEntity> toEntityList(List<Dictionary> domains) {
if (domains == null) {
return null;
}
return domains.stream()
.map(this::toEntity)
.collect(Collectors.toList());
}
public List<Dictionary> toDomainList(List<DictionaryEntity> entities) {
if (entities == null) {
return null;
}
return entities.stream()
.map(this::toDomain)
.collect(Collectors.toList());
}
}
@@ -2,7 +2,12 @@ package cn.novalon.manage.sys.infrastructure.db.converter;
import cn.novalon.manage.sys.core.domain.OperationLog;
import cn.novalon.manage.sys.infrastructure.db.entity.OperationLogEntity;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.stream.Collectors;
@Component
public class OperationLogConverter {
public OperationLog toDomain(OperationLogEntity entity) {
@@ -46,4 +51,22 @@ public class OperationLogConverter {
entity.setDeletedAt(domain.getDeletedAt());
return entity;
}
public List<OperationLog> toDomainList(List<OperationLogEntity> entities) {
if (entities == null) {
return null;
}
return entities.stream()
.map(this::toDomain)
.collect(Collectors.toList());
}
public List<OperationLogEntity> toEntityList(List<OperationLog> domains) {
if (domains == null) {
return null;
}
return domains.stream()
.map(this::toEntity)
.collect(Collectors.toList());
}
}
@@ -3,6 +3,10 @@ package cn.novalon.manage.sys.infrastructure.db.converter;
import cn.novalon.manage.sys.core.domain.SysConfig;
import cn.novalon.manage.sys.infrastructure.db.entity.SysConfigEntity;
import org.springframework.stereotype.Component;
@Component
public class SysConfigConverter {
public SysConfig toDomain(SysConfigEntity entity) {
@@ -15,8 +19,6 @@ public class SysConfigConverter {
domain.setConfigKey(entity.getConfigKey());
domain.setConfigValue(entity.getConfigValue());
domain.setConfigType(entity.getConfigType());
domain.setCreateBy(entity.getCreateBy());
domain.setUpdateBy(entity.getUpdateBy());
domain.setCreatedAt(entity.getCreatedAt());
domain.setUpdatedAt(entity.getUpdatedAt());
return domain;
@@ -32,8 +34,6 @@ public class SysConfigConverter {
entity.setConfigKey(domain.getConfigKey());
entity.setConfigValue(domain.getConfigValue());
entity.setConfigType(domain.getConfigType());
entity.setCreateBy(domain.getCreateBy());
entity.setUpdateBy(domain.getUpdateBy());
entity.setCreatedAt(domain.getCreatedAt());
entity.setUpdatedAt(domain.getUpdatedAt());
return entity;
@@ -3,6 +3,10 @@ package cn.novalon.manage.sys.infrastructure.db.converter;
import cn.novalon.manage.sys.core.domain.SysDictData;
import cn.novalon.manage.sys.infrastructure.db.entity.SysDictDataEntity;
import org.springframework.stereotype.Component;
@Component
public class SysDictDataConverter {
public SysDictData toDomain(SysDictDataEntity entity) {
@@ -19,8 +23,6 @@ public class SysDictDataConverter {
domain.setListClass(entity.getListClass());
domain.setIsDefault(entity.getIsDefault());
domain.setStatus(entity.getStatus());
domain.setCreateBy(entity.getCreateBy());
domain.setUpdateBy(entity.getUpdateBy());
domain.setCreatedAt(entity.getCreatedAt());
domain.setUpdatedAt(entity.getUpdatedAt());
return domain;
@@ -40,8 +42,6 @@ public class SysDictDataConverter {
entity.setListClass(domain.getListClass());
entity.setIsDefault(domain.getIsDefault());
entity.setStatus(domain.getStatus());
entity.setCreateBy(domain.getCreateBy());
entity.setUpdateBy(domain.getUpdateBy());
entity.setCreatedAt(domain.getCreatedAt());
entity.setUpdatedAt(domain.getUpdatedAt());
return entity;
@@ -3,6 +3,10 @@ package cn.novalon.manage.sys.infrastructure.db.converter;
import cn.novalon.manage.sys.core.domain.SysDictType;
import cn.novalon.manage.sys.infrastructure.db.entity.SysDictTypeEntity;
import org.springframework.stereotype.Component;
@Component
public class SysDictTypeConverter {
public SysDictType toDomain(SysDictTypeEntity entity) {
@@ -15,8 +19,6 @@ public class SysDictTypeConverter {
domain.setDictType(entity.getDictType());
domain.setStatus(entity.getStatus());
domain.setRemark(entity.getRemark());
domain.setCreateBy(entity.getCreateBy());
domain.setUpdateBy(entity.getUpdateBy());
domain.setCreatedAt(entity.getCreatedAt());
domain.setUpdatedAt(entity.getUpdatedAt());
return domain;
@@ -32,8 +34,6 @@ public class SysDictTypeConverter {
entity.setDictType(domain.getDictType());
entity.setStatus(domain.getStatus());
entity.setRemark(domain.getRemark());
entity.setCreateBy(domain.getCreateBy());
entity.setUpdateBy(domain.getUpdateBy());
entity.setCreatedAt(domain.getCreatedAt());
entity.setUpdatedAt(domain.getUpdatedAt());
return entity;
@@ -3,6 +3,10 @@ package cn.novalon.manage.sys.infrastructure.db.converter;
import cn.novalon.manage.sys.core.domain.SysExceptionLog;
import cn.novalon.manage.sys.infrastructure.db.entity.SysExceptionLogEntity;
import org.springframework.stereotype.Component;
@Component
public class SysExceptionLogConverter {
public SysExceptionLog toDomain(SysExceptionLogEntity entity) {
@@ -3,6 +3,10 @@ package cn.novalon.manage.sys.infrastructure.db.converter;
import cn.novalon.manage.sys.core.domain.SysFile;
import cn.novalon.manage.sys.infrastructure.db.entity.SysFileEntity;
import org.springframework.stereotype.Component;
@Component
public class SysFileConverter {
public SysFile toDomain(SysFileEntity entity) {
@@ -3,6 +3,10 @@ package cn.novalon.manage.sys.infrastructure.db.converter;
import cn.novalon.manage.sys.core.domain.SysLoginLog;
import cn.novalon.manage.sys.infrastructure.db.entity.SysLoginLogEntity;
import org.springframework.stereotype.Component;
@Component
public class SysLoginLogConverter {
public SysLoginLog toDomain(SysLoginLogEntity entity) {
@@ -3,6 +3,10 @@ package cn.novalon.manage.sys.infrastructure.db.converter;
import cn.novalon.manage.sys.core.domain.SysMenu;
import cn.novalon.manage.sys.infrastructure.db.entity.SysMenuEntity;
import org.springframework.stereotype.Component;
@Component
public class SysMenuConverter {
public SysMenu toDomain(SysMenuEntity entity) {
@@ -3,6 +3,10 @@ package cn.novalon.manage.sys.infrastructure.db.converter;
import cn.novalon.manage.sys.core.domain.SysNotice;
import cn.novalon.manage.sys.infrastructure.db.entity.SysNoticeEntity;
import org.springframework.stereotype.Component;
@Component
public class SysNoticeConverter {
public SysNotice toDomain(SysNoticeEntity entity) {
@@ -15,8 +19,6 @@ public class SysNoticeConverter {
domain.setNoticeType(entity.getNoticeType());
domain.setNoticeContent(entity.getNoticeContent());
domain.setStatus(entity.getStatus());
domain.setCreateBy(entity.getCreateBy());
domain.setUpdateBy(entity.getUpdateBy());
domain.setCreatedAt(entity.getCreatedAt());
domain.setUpdatedAt(entity.getUpdatedAt());
return domain;
@@ -32,8 +34,6 @@ public class SysNoticeConverter {
entity.setNoticeType(domain.getNoticeType());
entity.setNoticeContent(domain.getNoticeContent());
entity.setStatus(domain.getStatus());
entity.setCreateBy(domain.getCreateBy());
entity.setUpdateBy(domain.getUpdateBy());
entity.setCreatedAt(domain.getCreatedAt());
entity.setUpdatedAt(domain.getUpdatedAt());
return entity;
@@ -3,6 +3,10 @@ package cn.novalon.manage.sys.infrastructure.db.converter;
import cn.novalon.manage.sys.core.domain.SysRole;
import cn.novalon.manage.sys.infrastructure.db.entity.SysRoleEntity;
import org.springframework.stereotype.Component;
@Component
public class SysRoleConverter {
public SysRole toDomain(SysRoleEntity entity) {
@@ -3,6 +3,13 @@ package cn.novalon.manage.sys.infrastructure.db.converter;
import cn.novalon.manage.sys.core.domain.SysUser;
import cn.novalon.manage.sys.infrastructure.db.entity.SysUserEntity;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.stream.Collectors;
@Component
public class SysUserConverter {
public SysUser toDomain(SysUserEntity entity) {
@@ -38,4 +45,22 @@ public class SysUserConverter {
entity.setDeletedAt(domain.getDeletedAt());
return entity;
}
public List<SysUser> toDomainList(List<SysUserEntity> entities) {
if (entities == null) {
return null;
}
return entities.stream()
.map(this::toDomain)
.collect(Collectors.toList());
}
public List<SysUserEntity> toEntityList(List<SysUser> domains) {
if (domains == null) {
return null;
}
return domains.stream()
.map(this::toEntity)
.collect(Collectors.toList());
}
}
@@ -3,6 +3,10 @@ package cn.novalon.manage.sys.infrastructure.db.converter;
import cn.novalon.manage.sys.core.domain.SysUserMessage;
import cn.novalon.manage.sys.infrastructure.db.entity.SysUserMessageEntity;
import org.springframework.stereotype.Component;
@Component
public class SysUserMessageConverter {
public SysUserMessage toDomain(SysUserMessageEntity entity) {
@@ -0,0 +1,21 @@
package cn.novalon.manage.sys.infrastructure.db.dao;
import cn.novalon.manage.sys.infrastructure.db.entity.DictionaryEntity;
import org.springframework.data.r2dbc.repository.R2dbcRepository;
import org.springframework.stereotype.Repository;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@Repository
public interface DictionaryDao extends R2dbcRepository<DictionaryEntity, Long> {
Flux<DictionaryEntity> findByType(String type);
Mono<DictionaryEntity> findByTypeAndCode(String type, String code);
Flux<DictionaryEntity> findByDeletedAtIsNull();
Flux<DictionaryEntity> findByDeletedAtIsNullOrderBySortAsc();
Mono<Void> deleteByIdAndDeletedAtIsNull(Long id);
}
@@ -0,0 +1,49 @@
package cn.novalon.manage.sys.infrastructure.db.repository;
import cn.novalon.manage.sys.core.domain.Dictionary;
import cn.novalon.manage.sys.infrastructure.db.converter.DictionaryConverter;
import cn.novalon.manage.sys.infrastructure.db.dao.DictionaryDao;
import org.springframework.stereotype.Repository;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@Repository
public class DictionaryRepository {
private final DictionaryDao dao;
private final DictionaryConverter converter;
public DictionaryRepository(DictionaryDao dao, DictionaryConverter converter) {
this.dao = dao;
this.converter = converter;
}
public Flux<Dictionary> findByType(String type) {
return dao.findByType(type)
.map(converter::toDomain);
}
public Mono<Dictionary> findByTypeAndCode(String type, String code) {
return dao.findByTypeAndCode(type, code)
.map(converter::toDomain);
}
public Flux<Dictionary> findAll() {
return dao.findByDeletedAtIsNullOrderBySortAsc()
.map(converter::toDomain);
}
public Mono<Dictionary> findById(Long id) {
return dao.findById(id)
.map(converter::toDomain);
}
public Mono<Dictionary> save(Dictionary dictionary) {
return dao.save(converter.toEntity(dictionary))
.map(converter::toDomain);
}
public Mono<Void> deleteById(Long id) {
return dao.deleteByIdAndDeletedAtIsNull(id);
}
}
@@ -54,4 +54,14 @@ public class OperationLogRepository implements IOperationLogRepository {
return dao.findByUsernameAndDeletedAtIsNull(username)
.map(converter::toDomain);
}
@Override
public Mono<Long> count() {
return dao.countByDeletedAtIsNull();
}
@Override
public Mono<Long> countByCreatedAtAfter(LocalDateTime dateTime) {
return dao.countByCreatedAtAfterAndDeletedAtIsNull(dateTime);
}
}
@@ -2,23 +2,36 @@ package cn.novalon.manage.sys.infrastructure.db.repository;
import cn.novalon.manage.sys.core.domain.SysUser;
import cn.novalon.manage.sys.core.repository.ISysUserRepository;
import cn.novalon.manage.sys.dto.request.PageRequest;
import cn.novalon.manage.sys.dto.response.PageResponse;
import cn.novalon.manage.sys.infrastructure.db.converter.SysUserConverter;
import cn.novalon.manage.sys.infrastructure.db.dao.SysUserDao;
import cn.novalon.manage.sys.infrastructure.db.entity.SysUserEntity;
import org.springframework.data.domain.Sort;
import org.springframework.data.r2dbc.core.R2dbcEntityTemplate;
import org.springframework.data.relational.core.query.Query;
import org.springframework.r2dbc.core.DatabaseClient;
import org.springframework.stereotype.Repository;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.time.LocalDateTime;
import java.util.List;
@Repository
public class SysUserRepository implements ISysUserRepository {
private final SysUserDao dao;
private final SysUserConverter converter;
private final R2dbcEntityTemplate template;
private final DatabaseClient databaseClient;
public SysUserRepository(SysUserDao dao, SysUserConverter converter) {
public SysUserRepository(SysUserDao dao, SysUserConverter converter,
R2dbcEntityTemplate template, DatabaseClient databaseClient) {
this.dao = dao;
this.converter = converter;
this.template = template;
this.databaseClient = databaseClient;
}
@Override
@@ -29,6 +42,13 @@ public class SysUserRepository implements ISysUserRepository {
@Override
public Mono<SysUser> findById(Long id) {
return dao.findById(id)
.filter(entity -> entity.getDeletedAt() == null)
.map(converter::toDomain);
}
@Override
public Mono<SysUser> findByIdIncludingDeleted(Long id) {
return dao.findById(id)
.map(converter::toDomain);
}
@@ -51,7 +71,114 @@ public class SysUserRepository implements ISysUserRepository {
@Override
public Flux<SysUser> findAll() {
return dao.findAll()
.map(converter::toDomain);
}
@Override
public Flux<SysUser> findAll(Sort sort) {
return dao.findAll(sort)
.map(converter::toDomain);
}
@Override
public Mono<Long> count() {
return dao.countByDeletedAtIsNull();
}
@Override
public Mono<PageResponse<SysUser>> findByQueryWithPagination(Query query, PageRequest pageRequest) {
Sort sort = Sort.by(
pageRequest.getOrder().equalsIgnoreCase("desc") ? Sort.Direction.DESC : Sort.Direction.ASC,
pageRequest.getSort());
org.springframework.data.domain.Pageable pageable = org.springframework.data.domain.PageRequest.of(
pageRequest.getPage(),
pageRequest.getSize(),
sort);
return template.select(SysUserEntity.class)
.matching(query.with(pageable))
.all()
.collectList()
.zipWith(template.count(query, SysUserEntity.class))
.map(tuple -> {
long totalCount = tuple.getT2();
int totalPages = (int) Math.ceil((double) totalCount / pageRequest.getSize());
return new PageResponse<SysUser>(
tuple.getT1().stream().map(converter::toDomain).toList(),
totalPages,
totalCount,
pageRequest.getPage(),
pageRequest.getSize());
});
}
@Override
public Flux<SysUser> findByDeletedAtIsNull() {
return dao.findByDeletedAtIsNull()
.map(converter::toDomain);
}
@Override
public Flux<SysUser> findByDeletedAtIsNull(Sort sort) {
return dao.findByDeletedAtIsNull(sort)
.map(converter::toDomain);
}
@Override
public Mono<SysUser> findByEmail(String email) {
return dao.findByEmailAndDeletedAtIsNull(email)
.map(converter::toDomain);
}
@Override
public Mono<Boolean> existsByUsername(String username) {
return dao.findByUsernameAndDeletedAtIsNull(username)
.map(user -> user != null)
.defaultIfEmpty(false);
}
@Override
public Mono<Boolean> existsByEmail(String email) {
return dao.findByEmailAndDeletedAtIsNull(email)
.map(user -> user != null)
.defaultIfEmpty(false);
}
@Override
public Mono<Void> logicalDeleteById(Long id) {
return databaseClient.sql("UPDATE users SET deleted_at = CURRENT_TIMESTAMP WHERE id = $1")
.bind("$1", id)
.fetch()
.rowsUpdated()
.then();
}
@Override
public Mono<Void> logicalDeleteByIds(List<Long> ids) {
return databaseClient.sql("UPDATE users SET deleted_at = CURRENT_TIMESTAMP WHERE id = ANY($1)")
.bind("$1", ids.toArray())
.fetch()
.rowsUpdated()
.then();
}
@Override
public Mono<Void> restoreById(Long id) {
return databaseClient.sql("UPDATE users SET deleted_at = NULL WHERE id = $1")
.bind("$1", id)
.fetch()
.rowsUpdated()
.then();
}
@Override
public Mono<Void> restoreByIds(List<Long> ids) {
return databaseClient.sql("UPDATE users SET deleted_at = NULL WHERE id = ANY($1)")
.bind("$1", ids.toArray())
.fetch()
.rowsUpdated()
.then();
}
}