package com.estate.expert.admin.service.common;
|
|
import com.estate.expert.admin.domain.common.EntityBase;
|
import com.estate.expert.admin.domain.common.EntityBaseRepository;
|
import com.estate.expert.admin.domain.common.IDtoBase;
|
import com.estate.expert.admin.domain.common.mapper.EntityRefMapper;
|
import com.estate.expert.admin.domain.common.mapper.IMapperBase;
|
import com.estate.expert.admin.domain.user.User;
|
import com.estate.expert.admin.enums.BizResultCode;
|
import com.estate.expert.admin.utils.DeletedObjectUtil;
|
import lombok.extern.slf4j.Slf4j;
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
import javax.persistence.EntityManager;
|
import javax.transaction.Transactional;
|
import java.time.LocalDateTime;
|
import java.util.HashSet;
|
import java.util.List;
|
import java.util.Set;
|
import java.util.UUID;
|
import java.util.stream.StreamSupport;
|
|
/**
|
* 基础的Service实现
|
* 包含了增删改查的基本操作
|
* 避免重复编写基础的增删改查代码以及不必要的测试
|
*/
|
@Slf4j
|
@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
|
public abstract class CrudServiceBaseImpl<T extends EntityBase, D extends IDtoBase> implements CrudServiceBase<T, D> {
|
|
@Autowired
|
protected EntityBaseRepository<T> entityRepository;
|
|
@Autowired
|
protected IMapperBase<D, T> entityMapper;
|
|
@Autowired
|
protected EntityRefMapper entityRefMapper;
|
|
@Autowired
|
protected EntityManager entityManager;
|
|
/**
|
* 不要重载这个方法,这只是个壳
|
* 所有对于实体的操作放在 add(User creator, T entity) 方法中
|
* 重载它来实现对实体的操作
|
*/
|
@Override
|
public T add(User creator, D dto) {
|
return add(creator, toEntity(dto));
|
}
|
|
@Override
|
public void addDtos(User creator, List<D> dtoList) {
|
var entityList = dtoList.stream().map(this::toEntity).toList();
|
addEntities(creator, entityList);
|
}
|
|
@Override
|
public void addEntities(User creator, List<T> entityList) {
|
var now = LocalDateTime.now();
|
for(var entity : entityList) {
|
if(entity.getName() == null)
|
entity.setName(UUID.randomUUID().toString());
|
entity.setCreator(creator);
|
if(entity.getCreateTime() == null)
|
entity.setCreateTime(now);
|
if(entity.getUpdateTime() == null)
|
entity.setUpdateTime(now);
|
}
|
// 这块只有初始化的时候会用,所以不去检查字段,以提高性能
|
// 正常要批量导入的话,应该检查字段
|
// 可以循环调用add方法
|
entityRepository.saveAll(entityList);
|
}
|
|
/**
|
* 批量导入实体(自动处理重名)
|
* 专门用于 Excel 导入等批量导入场景
|
*
|
* @param creator 创建者
|
* @param entityList 实体列表
|
*/
|
@Transactional
|
public void importEntities(User creator, List<T> entityList) {
|
if(entityList == null || entityList.isEmpty()) {
|
return;
|
}
|
|
LocalDateTime now = LocalDateTime.now();
|
Set<String> usedNames = new HashSet<>();
|
|
for(T entity : entityList) {
|
String originalName = entity.getName();
|
if(originalName == null || originalName.trim().isEmpty()) {
|
originalName = "未命名";
|
}
|
|
// 如果名称重复,自动在后面加数字
|
String finalName = originalName;
|
int suffix = 1;
|
while(entityRepository.existsByName(finalName) || usedNames.contains(finalName)) {
|
finalName = originalName + "-" + suffix;
|
suffix++;
|
}
|
|
usedNames.add(finalName);
|
entity.setName(finalName);
|
entity.setId(null);
|
entity.setCreator(creator);
|
if(entity.getCreateTime() == null) {
|
entity.setCreateTime(now);
|
}
|
if(entity.getUpdateTime() == null) {
|
entity.setUpdateTime(now);
|
}
|
|
// 调用子类的钩子方法,用于处理特定逻辑(如更新地理位置)
|
beforeImport(entity);
|
}
|
|
// 批量保存
|
entityRepository.saveAll(entityList);
|
log.info("成功导入 {} 个 {}", entityList.size(), entityList.get(0).getClass().getSimpleName());
|
}
|
|
/**
|
* 导入前的钩子方法
|
* 子类可以重写此方法,在批量导入前对实体进行额外处理
|
*
|
* @param entity 待导入的实体
|
*/
|
protected void beforeImport(T entity) {
|
// 默认不做任何处理,子类可以重写
|
}
|
|
@Override
|
public T add(User creator, T entity) {
|
BizResultCode.ENTITY_NAME_ALREADY_EXISTS.check(entityRepository.existsByName(entity.getName()));
|
entity.setId(null);
|
entity.setCreator(creator);
|
entity.setCreateTime(LocalDateTime.now());
|
entity.setUpdateTime(LocalDateTime.now());
|
return save(entity);
|
}
|
|
@Override
|
public T edit(D dto) {
|
BizResultCode.ID_CANNOT_NULL.check(dto.getId() == null);
|
var entity = get(dto.getId());
|
BizResultCode.ENTITY_NOT_FOUND.check(entity == null);
|
entityMapper.partialUpdate(dto, entity);
|
return edit(entity);
|
}
|
|
@Override
|
public T edit(T entity) {
|
var otherEntity = entityRepository.findByName(entity.getName());
|
BizResultCode.ENTITY_NAME_ALREADY_EXISTS.check(otherEntity!=null && !otherEntity.getId().equals(entity.getId()));
|
entity.setUpdateTime(LocalDateTime.now());
|
return save(entity);
|
}
|
|
protected void beforeDelete(T entity) {
|
|
}
|
|
@Transactional
|
@Override
|
public void deleteAllById(Iterable<Integer> request) {
|
var entities = entityRepository.findAllById(request);
|
entities.forEach(entity -> {
|
beforeDelete(entity);
|
entity.setDeleted(true);
|
entity.setName(DeletedObjectUtil.toDeletedString(entity.getName()));
|
});
|
entityRepository.saveAll(entities);
|
}
|
|
@Override
|
public void deletePhysical(Integer id) {
|
entityRepository.deleteById(id);
|
}
|
|
@Override
|
public T get(Integer id) {
|
BizResultCode.ID_CANNOT_NULL.check(id == null);
|
var entity = entityRepository.findById(id);
|
if(entity.isEmpty() || entity.get().isDeleted())
|
return null;
|
return entity.get();
|
}
|
|
@Override
|
public T get(Integer id, boolean withDeleted) {
|
BizResultCode.ID_CANNOT_NULL.check(id == null);
|
var entity = entityRepository.findById(id);
|
if(entity.isEmpty() || (entity.get().isDeleted() && !withDeleted))
|
return null;
|
return entity.get();
|
}
|
|
@Override
|
public D detail(Integer id) {
|
var entity = get(id);
|
BizResultCode.ENTITY_NOT_FOUND.check(entity == null);
|
return toDto(entity);
|
}
|
|
@Override
|
public T save(T entity) {
|
BizResultCode.ENTITY_CANNOT_NULL.check(entity == null);
|
return entityRepository.save(entity);
|
}
|
|
@Override
|
public T clone(Integer id) {
|
var entity = get(id);
|
BizResultCode.ENTITY_NOT_FOUND.check(entity == null);
|
entityManager.detach(entity);
|
|
entity.setId(null);
|
var name = entity.getName();
|
do {
|
name = "%s - %d".formatted(name, System.currentTimeMillis());
|
} while (entityRepository.existsByName(name));
|
entity.setName(name);
|
|
return add(entity.getCreator(), entity);
|
}
|
|
@Override
|
public List<T> findAll() {
|
return StreamSupport.stream(entityRepository.findAll().spliterator(), false)
|
.filter(t -> !t.isDeleted()).toList();
|
}
|
|
@Override
|
public List<T> findAllById(Iterable<Integer> ids) {
|
return StreamSupport.stream(entityRepository.findAllById(ids).spliterator(), false)
|
.filter(t -> !t.isDeleted()).toList();
|
}
|
|
@Override
|
public List<D> toDto(List<T> entityList) {
|
return entityList.stream().map(this::toDto).toList();
|
}
|
|
@Override
|
public D toDto(T entity) {
|
if(entity == null)
|
return null;
|
entity.setName(DeletedObjectUtil.beautifyDeletedString(entity.getName()));
|
return entityMapper.toDto(entity);
|
}
|
|
@Override
|
public T toEntity(D dto) {
|
return entityMapper.toEntity(dto);
|
}
|
|
@Override
|
public T getByName(String name) {
|
return entityRepository.findByName(name);
|
}
|
|
@Override
|
public boolean existsById(Integer id) {
|
return entityRepository.existsById(id);
|
}
|
|
@Override
|
public boolean existsByName(String name) {
|
return entityRepository.existsByName(name);
|
}
|
}
|