This commit is contained in:
daikon
2024-04-24 11:38:29 +08:00
committed by GitHub
parent 01d37eeef0
commit 872d3ec518
16 changed files with 596 additions and 4 deletions

View File

@@ -0,0 +1,16 @@
package com.tencent.supersonic.headless.api.pojo.request;
import com.tencent.supersonic.headless.api.pojo.SchemaItem;
import lombok.Data;
import java.util.List;
@Data
public class ClassReq extends SchemaItem {
private Long domainId;
private Long tagObjectId;
private Long parentId;
private List<Long> itemIds;
}

View File

@@ -20,11 +20,13 @@ public class SqlExecuteReq {
private List<SqlVariable> sqlVariables;
private Integer limit = 1000;
public String getSql() {
if (StringUtils.isNotBlank(sql) && sql.endsWith(";")) {
sql = sql.substring(0, sql.length() - 1);
}
return String.format(LIMIT_WRAPPER, sql);
return String.format(LIMIT_WRAPPER, sql, limit);
}
}

View File

@@ -0,0 +1,39 @@
package com.tencent.supersonic.headless.api.pojo.response;
import lombok.Data;
import java.util.Date;
import java.util.List;
@Data
public class ClassResp {
private Long id;
private Long domainId;
private String domainName;
private Long dataSetId;
private String name;
private String bizName;
private String description;
private String fullPath;
/**
* 分类状态
*/
private Integer status;
/**
* METRIC、DIMENSION、TAG
*/
private String type;
private List<Long> itemIds;
private Date createdAt;
private String createdBy;
private Date updatedAt;
private String updatedBy;
}

View File

@@ -118,6 +118,12 @@
<artifactId>postgresql</artifactId>
<version>${postgresql.version}</version>
</dependency>
<dependency>
<groupId>com.tencent.supersonic</groupId>
<artifactId>headless-api</artifactId>
<version>0.9.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
</dependencies>

View File

@@ -0,0 +1,42 @@
package com.tencent.supersonic.headless.server.persistence.dataobject;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.util.Date;
@Data
@TableName("s2_class")
public class ClassDO {
@TableId(type = IdType.AUTO)
private Long id;
private Long domainId;
private Long dataSetId;
private String name;
private String bizName;
private String description;
private Long parentId;
/**
* 分类状态
*/
private Integer status;
/**
* METRIC、DIMENSION、TAG
*/
private String type;
private String itemIds;
private Date createdAt;
private String createdBy;
private Date updatedAt;
private String updatedBy;
}

View File

@@ -0,0 +1,9 @@
package com.tencent.supersonic.headless.server.persistence.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.tencent.supersonic.headless.server.persistence.dataobject.ClassDO;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface ClassMapper extends BaseMapper<ClassDO> {
}

View File

@@ -0,0 +1,21 @@
package com.tencent.supersonic.headless.server.persistence.repository;
import com.tencent.supersonic.headless.server.persistence.dataobject.ClassDO;
import com.tencent.supersonic.headless.server.pojo.ClassFilter;
import java.util.List;
public interface ClassRepository {
Long create(ClassDO classDO);
Long update(ClassDO classDO);
Integer delete(List<Long> ids);
ClassDO getClassById(Long id);
List<ClassDO> getClassDOList(ClassFilter filter);
List<ClassDO> getAllClassDOList();
}

View File

@@ -0,0 +1,78 @@
package com.tencent.supersonic.headless.server.persistence.repository.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.tencent.supersonic.headless.server.persistence.dataobject.ClassDO;
import com.tencent.supersonic.headless.server.persistence.mapper.ClassMapper;
import com.tencent.supersonic.headless.server.persistence.repository.ClassRepository;
import com.tencent.supersonic.headless.server.pojo.ClassFilter;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.logging.log4j.util.Strings;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Objects;
@Repository
public class ClassRepositoryImpl implements ClassRepository {
private final ClassMapper mapper;
public ClassRepositoryImpl(ClassMapper mapper) {
this.mapper = mapper;
}
@Override
public Long create(ClassDO classDO) {
mapper.insert(classDO);
return classDO.getId();
}
@Override
public Long update(ClassDO classDO) {
mapper.updateById(classDO);
return classDO.getId();
}
@Override
public Integer delete(List<Long> ids) {
return mapper.deleteBatchIds(ids);
}
@Override
public ClassDO getClassById(Long id) {
return mapper.selectById(id);
}
@Override
public List<ClassDO> getClassDOList(ClassFilter filter) {
QueryWrapper<ClassDO> wrapper = new QueryWrapper();
if (Objects.nonNull(filter.getDomainId())) {
wrapper.lambda().eq(ClassDO::getDomainId, filter.getDomainId());
}
if (Objects.nonNull(filter.getDataSetId())) {
wrapper.lambda().eq(ClassDO::getDataSetId, filter.getDataSetId());
}
if (Strings.isNotEmpty(filter.getType())) {
wrapper.lambda().eq(ClassDO::getType, filter.getType());
}
if (CollectionUtils.isNotEmpty(filter.getIds())) {
wrapper.lambda().in(ClassDO::getId, filter.getIds());
}
if (Objects.nonNull(filter.getCreatedBy())) {
wrapper.lambda().eq(ClassDO::getCreatedBy, filter.getCreatedBy());
}
if (Objects.nonNull(filter.getStatus())) {
wrapper.lambda().eq(ClassDO::getStatus, filter.getStatus());
}
if (Objects.nonNull(filter.getBizName())) {
wrapper.lambda().eq(ClassDO::getBizName, filter.getBizName());
}
return mapper.selectList(wrapper);
}
@Override
public List<ClassDO> getAllClassDOList() {
QueryWrapper<ClassDO> wrapper = new QueryWrapper();
return mapper.selectList(wrapper);
}
}

View File

@@ -0,0 +1,11 @@
package com.tencent.supersonic.headless.server.pojo;
import lombok.Data;
@Data
public class ClassFilter extends MetaFilter {
private String type;
private Long dataSetId;
private Long classId;
}

View File

@@ -0,0 +1,100 @@
package com.tencent.supersonic.headless.server.rest;
import com.tencent.supersonic.auth.api.authentication.pojo.User;
import com.tencent.supersonic.auth.api.authentication.utils.UserHolder;
import com.tencent.supersonic.headless.api.pojo.request.ClassReq;
import com.tencent.supersonic.headless.api.pojo.response.ClassResp;
import com.tencent.supersonic.headless.server.pojo.ClassFilter;
import com.tencent.supersonic.headless.server.service.ClassService;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.util.List;
@RestController
@RequestMapping("/api/semantic/class")
public class ClassController {
private final ClassService classService;
public ClassController(ClassService classService) {
this.classService = classService;
}
/**
* 新建目录
*
* @param classReq
* @param request
* @param response
* @return
*/
@PostMapping("/create")
public ClassResp create(@RequestBody @Valid ClassReq classReq,
HttpServletRequest request,
HttpServletResponse response) {
User user = UserHolder.findUser(request, response);
return classService.create(classReq, user);
}
/**
* 修改目录
*
* @param classReq
* @param request
* @param response
* @return
*/
@PutMapping("/update")
public ClassResp update(@RequestBody @Valid ClassReq classReq,
HttpServletRequest request,
HttpServletResponse response) {
User user = UserHolder.findUser(request, response);
return classService.update(classReq, user);
}
/**
* 删除目录
*
* @param id
* @param request
* @param response
* @return
* @throws Exception
*/
@DeleteMapping("delete/{id}/{force}")
public Boolean delete(@PathVariable("id") Long id,
@PathVariable("force") Boolean force,
HttpServletRequest request,
HttpServletResponse response) throws Exception {
User user = UserHolder.findUser(request, response);
return classService.delete(id, force, user);
}
/**
* 删除目录
*
* @param filter
* @param request
* @param response
* @return
* @throws Exception
*/
@GetMapping("delete/{id}/{force}")
public List<ClassResp> get(@RequestBody @Valid ClassFilter filter,
HttpServletRequest request,
HttpServletResponse response) {
User user = UserHolder.findUser(request, response);
return classService.getClassList(filter, user);
}
}

View File

@@ -0,0 +1,19 @@
package com.tencent.supersonic.headless.server.service;
import com.tencent.supersonic.auth.api.authentication.pojo.User;
import com.tencent.supersonic.headless.api.pojo.request.ClassReq;
import com.tencent.supersonic.headless.api.pojo.response.ClassResp;
import com.tencent.supersonic.headless.server.pojo.ClassFilter;
import java.util.List;
public interface ClassService {
ClassResp create(ClassReq classReq, User user);
ClassResp update(ClassReq classReq, User user);
Boolean delete(Long id, Boolean force, User user) throws Exception;
List<ClassResp> getClassList(ClassFilter filter, User user);
}

View File

@@ -6,6 +6,7 @@ import com.tencent.supersonic.headless.api.pojo.response.TagObjectResp;
import com.tencent.supersonic.headless.server.pojo.TagObjectFilter;
import java.util.List;
import java.util.Map;
public interface TagObjectService {
@@ -18,4 +19,7 @@ public interface TagObjectService {
TagObjectResp getTagObject(Long id, User user);
List<TagObjectResp> getTagObjects(TagObjectFilter filter, User user);
Map<Long, TagObjectResp> getAllTagObjectMap();
}

View File

@@ -0,0 +1,111 @@
package com.tencent.supersonic.headless.server.service.impl;
import com.tencent.supersonic.auth.api.authentication.pojo.User;
import com.tencent.supersonic.common.pojo.enums.StatusEnum;
import com.tencent.supersonic.common.util.BeanMapper;
import com.tencent.supersonic.headless.api.pojo.request.ClassReq;
import com.tencent.supersonic.headless.api.pojo.response.ClassResp;
import com.tencent.supersonic.headless.server.persistence.dataobject.ClassDO;
import com.tencent.supersonic.headless.server.persistence.repository.ClassRepository;
import com.tencent.supersonic.headless.server.pojo.ClassFilter;
import com.tencent.supersonic.headless.server.service.ClassService;
import com.tencent.supersonic.headless.server.utils.ClassConverter;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@Service
public class ClassServiceImpl implements ClassService {
private final ClassRepository classRepository;
private final ClassConverter converter;
public ClassServiceImpl(ClassRepository classRepository, ClassConverter converter) {
this.classRepository = classRepository;
this.converter = converter;
}
@Override
public ClassResp create(ClassReq classReq, User user) {
ClassDO classDO = converter.convert(classReq);
classDO.setId(null);
Date date = new Date();
classDO.setCreatedBy(user.getName());
classDO.setCreatedAt(date);
classDO.setUpdatedBy(user.getName());
classDO.setUpdatedAt(date);
classDO.setStatus(StatusEnum.ONLINE.getCode());
classRepository.create(classDO);
ClassDO classDOById = classRepository.getClassById(classDO.getId());
return converter.convert2Resp(classDOById);
}
@Override
public ClassResp update(ClassReq classReq, User user) {
ClassDO classDO = classRepository.getClassById(classReq.getId());
BeanMapper.mapper(classReq, classDO);
classDO.setUpdatedAt(new Date());
classDO.setUpdatedBy(user.getName());
classRepository.update(classDO);
return converter.convert2Resp(classRepository.getClassById(classReq.getId()));
}
@Override
public Boolean delete(Long id, Boolean force, User user) throws Exception {
ClassDO classDO = classRepository.getClassById(id);
checkDeletePermission(classDO, user);
checkDeleteValid(classDO, force);
classRepository.delete(new ArrayList<>(Arrays.asList(id)));
if (force) {
// 删除子分类
List<ClassDO> classDOList = classRepository.getAllClassDOList();
Set<Long> deleteClassList = extractSubClass(id, classDOList);
classRepository.delete(new ArrayList<>(deleteClassList));
}
return true;
}
private Set<Long> extractSubClass(Long id, List<ClassDO> classDOList) {
Set<Long> classIdSet = new HashSet<>();
for (ClassDO classDO : classDOList) {
if (id.equals(classDO.getParentId())) {
classIdSet.add(classDO.getId());
classIdSet.addAll(extractSubClass(classDO.getId(), classDOList));
}
}
return classIdSet;
}
private void checkDeleteValid(ClassDO classDelete, Boolean force) {
List<ClassDO> classDOList = classRepository.getAllClassDOList();
for (ClassDO classDO : classDOList) {
if (classDO.getParentId().equals(classDelete.getId()) && !force) {
throw new RuntimeException("该分类下还存在子分类, 暂不能删除, 请确认");
}
}
}
private void checkDeletePermission(ClassDO classDO, User user) throws Exception {
if (user.getName().equalsIgnoreCase(classDO.getCreatedBy()) || user.isSuperAdmin()) {
return;
}
throw new Exception("delete operation is not supported at the moment. Please contact the admin.");
}
@Override
public List<ClassResp> getClassList(ClassFilter filter, User user) {
List<ClassDO> classDOList = classRepository.getClassDOList(filter);
return converter.convert2RespList(classDOList);
}
}

View File

@@ -22,6 +22,7 @@ import com.tencent.supersonic.headless.server.service.DatabaseService;
import com.tencent.supersonic.headless.server.service.ModelService;
import com.tencent.supersonic.headless.server.utils.DatabaseConverter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
@@ -35,6 +36,8 @@ import java.util.stream.Collectors;
@Slf4j
@Service
public class DatabaseServiceImpl implements DatabaseService {
@Value("${inMemoryEmbeddingStore.persistent.path:/tmp}")
private String embeddingStorePersistentPath;
private final SqlUtils sqlUtils;
private DatabaseRepository databaseRepository;

View File

@@ -15,6 +15,7 @@ import org.springframework.util.CollectionUtils;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Service
@@ -37,7 +38,7 @@ public class TagObjectServiceImpl implements TagObjectService {
tagObjectDO.setUpdatedBy(user.getName());
tagObjectDO.setUpdatedAt(date);
tagObjectDO.setStatus(StatusEnum.ONLINE.getCode());
tagObjectRepository.create(tagObjectDO).longValue();
tagObjectRepository.create(tagObjectDO);
TagObjectDO tagObjectById = tagObjectRepository.getTagObjectById(tagObjectDO.getId());
return TagObjectConverter.convert2Resp(tagObjectById);
}
@@ -54,10 +55,10 @@ public class TagObjectServiceImpl implements TagObjectService {
.collect(Collectors.toList());
for (TagObjectResp tagObject : tagObjectRespList) {
if (tagObject.getBizName().equalsIgnoreCase(tagObjectReq.getBizName())) {
throw new Exception(String.format("the bizName %s is exit", tagObjectReq.getBizName()));
throw new Exception(String.format("the bizName %s is exist", tagObjectReq.getBizName()));
}
if (tagObject.getName().equalsIgnoreCase(tagObjectReq.getName())) {
throw new Exception(String.format("the name %s is exit", tagObjectReq.getName()));
throw new Exception(String.format("the name %s is exist", tagObjectReq.getName()));
}
}
}
@@ -102,4 +103,14 @@ public class TagObjectServiceImpl implements TagObjectService {
List<TagObjectDO> tagObjectDOList = tagObjectRepository.query(filter);
return TagObjectConverter.convert2RespList(tagObjectDOList);
}
@Override
public Map<Long, TagObjectResp> getAllTagObjectMap() {
TagObjectFilter filter = new TagObjectFilter();
List<TagObjectDO> tagObjectDOList = tagObjectRepository.query(filter);
List<TagObjectResp> tagObjectRespList = TagObjectConverter.convert2RespList(tagObjectDOList);
Map<Long, TagObjectResp> map =
tagObjectRespList.stream().collect(Collectors.toMap(TagObjectResp::getId, a -> a, (k1, k2) -> k1));
return map;
}
}

View File

@@ -0,0 +1,120 @@
package com.tencent.supersonic.headless.server.utils;
import com.tencent.supersonic.common.util.JsonUtil;
import com.tencent.supersonic.headless.api.pojo.request.ClassReq;
import com.tencent.supersonic.headless.api.pojo.response.ClassResp;
import com.tencent.supersonic.headless.api.pojo.response.DomainResp;
import com.tencent.supersonic.headless.api.pojo.response.TagObjectResp;
import com.tencent.supersonic.headless.server.persistence.dataobject.ClassDO;
import com.tencent.supersonic.headless.server.persistence.repository.ClassRepository;
import com.tencent.supersonic.headless.server.service.DomainService;
import com.tencent.supersonic.headless.server.service.TagObjectService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
@Component
@Slf4j
public class ClassConverter {
private final ClassRepository classRepository;
private final DomainService domainService;
private final TagObjectService tagObjectService;
public ClassConverter(ClassRepository classRepository, DomainService domainService,
TagObjectService tagObjectService) {
this.classRepository = classRepository;
this.domainService = domainService;
this.tagObjectService = tagObjectService;
}
public ClassDO convert(ClassReq classReq) {
ClassDO classDO = new ClassDO();
BeanUtils.copyProperties(classReq, classDO);
classDO.setType(classReq.getTypeEnum().name());
List<Long> itemIds = new ArrayList<>();
if (CollectionUtils.isNotEmpty(classReq.getItemIds())) {
itemIds = classReq.getItemIds();
}
classDO.setItemIds(JsonUtil.toString(itemIds));
return classDO;
}
public ClassResp convert2Resp(ClassDO classDO) {
Map<Long, DomainResp> idAndDomain = getIdAndDomain();
Map<Long, String> classFullPathMap = getClassFullPathMap();
return convert2RespInternal(classDO, idAndDomain, classFullPathMap);
}
private ClassResp convert2RespInternal(ClassDO classDO, Map<Long, DomainResp> idAndDomain,
Map<Long, String> classFullPathMap) {
ClassResp classResp = new ClassResp();
BeanUtils.copyProperties(classDO, classResp);
Long domainId = classResp.getDomainId();
if (Objects.nonNull(idAndDomain) && idAndDomain.containsKey(domainId)
&& Objects.nonNull(idAndDomain.get(domainId))) {
classResp.setDomainName(idAndDomain.get(domainId).getName());
}
if (Objects.nonNull(classFullPathMap) && classFullPathMap.containsKey(classResp.getId())) {
classResp.setFullPath(classFullPathMap.get(classResp.getId()));
}
return classResp;
}
public List<ClassResp> convert2RespList(List<ClassDO> classDOList) {
List<ClassResp> classRespList = new ArrayList<>();
Map<Long, DomainResp> idAndDomain = getIdAndDomain();
Map<Long, String> classFullPathMap = getClassFullPathMap();
for (ClassDO classDO : classDOList) {
ClassResp classResp = convert2RespInternal(classDO, idAndDomain, classFullPathMap);
if (Objects.nonNull(classResp)) {
classRespList.add(classResp);
}
}
return classRespList;
}
public Map<Long, String> getClassFullPathMap() {
Map<Long, String> classFullPathMap = new HashMap<>();
List<ClassDO> classDOList = classRepository.getAllClassDOList();
Map<Long, ClassDO> classDOMap = classDOList.stream()
.collect(Collectors.toMap(ClassDO::getId, a -> a, (k1, k2) -> k1));
for (ClassDO classDO : classDOList) {
final Long domainId = classDO.getId();
StringBuilder fullPath = new StringBuilder(classDO.getBizName() + "/");
Long parentId = classDO.getParentId();
while (parentId != null && parentId > 0) {
classDO = classDOMap.get(parentId);
if (classDO == null) {
String message = String.format("get domain : %s failed", parentId);
throw new RuntimeException(message);
}
fullPath.insert(0, classDO.getBizName() + "/");
parentId = classDO.getParentId();
}
classFullPathMap.put(domainId, fullPath.toString());
}
return classFullPathMap;
}
public Map<Long, DomainResp> getIdAndDomain() {
return domainService.getDomainMap();
}
public Map<Long, TagObjectResp> getIdAndTagSet() {
return tagObjectService.getAllTagObjectMap();
}
}