This commit is contained in:
六如
2024-12-22 23:09:46 +08:00
parent 02fb5a9e85
commit 403e8111f4
1239 changed files with 4764 additions and 702 deletions

View File

@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.gitee.sop</groupId>
<artifactId>sop-admin-backend</artifactId>
<version>5.0.0-SNAPSHOT</version>
</parent>
<artifactId>admin-service</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>com.gitee.sop</groupId>
<artifactId>admin-common</artifactId>
<version>5.0.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.gitee.sop</groupId>
<artifactId>admin-dao</artifactId>
<version>5.0.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,52 @@
package com.gitee.sop.admin.service.doc;
import com.gitee.sop.admin.common.support.ServiceSupport;
import com.gitee.sop.admin.common.user.User;
import com.gitee.sop.admin.common.util.CopyUtil;
import com.gitee.sop.admin.dao.entity.DocApp;
import com.gitee.sop.admin.dao.mapper.DocAppMapper;
import com.gitee.sop.admin.service.doc.dto.DocAppDTO;
import com.gitee.sop.admin.service.doc.dto.torna.TornaModuleDTO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* @author 六如
*/
@Service
public class DocAppService implements ServiceSupport<DocApp, DocAppMapper> {
@Autowired
private TornaClient tornaClient;
@Autowired
private DocInfoService docInfoService;
@Autowired
private DocInfoSyncService docInfoSyncService;
public Long addDocApp(String token, User user) {
TornaModuleDTO tornaModuleDTO = tornaClient.execute("module.get", null, token, TornaModuleDTO.class);
DocApp docApp = this.get(DocApp::getToken, token);
if (docApp == null) {
docApp = new DocApp();
docApp.setAppName(tornaModuleDTO.getName());
docApp.setToken(token);
this.save(docApp);
} else {
docApp.setAppName(tornaModuleDTO.getName());
this.update(docApp);
}
// 同步文档
docInfoSyncService.syncDocInfo(docApp, null, user);
return docApp.getId();
}
public List<DocAppDTO> listDocApp() {
List<DocApp> docApps = this.listAll();
return CopyUtil.copyList(docApps, DocAppDTO::new);
}
}

View File

@@ -0,0 +1,39 @@
package com.gitee.sop.admin.service.doc;
import com.gitee.sop.admin.common.support.ServiceSupport;
import com.gitee.sop.admin.dao.entity.DocContent;
import com.gitee.sop.admin.dao.mapper.DocContentMapper;
import org.springframework.stereotype.Service;
/**
* @author 六如
*/
@Service
public class DocContentService implements ServiceSupport<DocContent, DocContentMapper> {
public void saveContent(Long docInfoId, String content) {
DocContent docContent = this.get(DocContent::getDocInfoId, docInfoId);
boolean save = false;
if (docContent == null) {
save = true;
docContent = new DocContent();
}
docContent.setDocInfoId(docInfoId);
docContent.setContent(content);
if (save) {
this.save(docContent);
} else {
this.update(docContent);
}
}
public String getContent(Long docInfoId) {
return this.query()
.eq(DocContent::getDocInfoId, docInfoId)
.getValueOptional(DocContent::getContent)
.orElse("");
}
}

View File

@@ -0,0 +1,115 @@
package com.gitee.sop.admin.service.doc;
import com.alibaba.fastjson2.JSON;
import com.gitee.fastmybatis.core.util.TreeUtil;
import com.gitee.sop.admin.common.constants.YesOrNo;
import com.gitee.sop.admin.common.exception.BizException;
import com.gitee.sop.admin.common.support.ServiceSupport;
import com.gitee.sop.admin.common.util.CopyUtil;
import com.gitee.sop.admin.dao.entity.DocApp;
import com.gitee.sop.admin.dao.entity.DocInfo;
import com.gitee.sop.admin.dao.mapper.DocAppMapper;
import com.gitee.sop.admin.dao.mapper.DocInfoMapper;
import com.gitee.sop.admin.service.doc.dto.DocInfoPublishUpdateDTO;
import com.gitee.sop.admin.service.doc.dto.DocInfoTreeDTO;
import com.gitee.sop.admin.service.doc.dto.torna.TornaDocInfoViewDTO;
import com.gitee.sop.admin.service.sys.SysUserService;
import com.google.common.collect.Lists;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
/**
* @author 六如
*/
@Service
public class DocInfoService implements ServiceSupport<DocInfo, DocInfoMapper> {
@Autowired
private DocAppMapper docAppMapper;
@Autowired
private DocContentService docContentService;
@Autowired
private SysUserService sysUserService;
public List<DocInfo> listChildDoc(Long parentId) {
return this.list(DocInfo::getParentId, parentId);
}
public List<DocInfoTreeDTO> listDocTree(Long docAppId) {
List<DocInfo> list = this.list(DocInfo::getDocAppId, docAppId);
if (CollectionUtils.isEmpty(list)) {
return new ArrayList<>(0);
}
Set<Long> userIds = list.stream().flatMap(docInfo -> Lists.newArrayList(docInfo.getAddBy(), docInfo.getUpdateBy()).stream())
.filter(val -> val != null && val > 0)
.collect(Collectors.toSet());
List<DocInfoTreeDTO> docInfoTreeDTOS = CopyUtil.copyList(list, DocInfoTreeDTO::new);
sysUserService.fillShowName(docInfoTreeDTOS);
return TreeUtil.convertTree(docInfoTreeDTOS, 0L);
}
public int publish(DocInfoPublishUpdateDTO docInfoUpdateDTO) {
DocInfo docInfo = this.getById(docInfoUpdateDTO.getId());
Integer isPublish = docInfoUpdateDTO.getIsPublish();
// 如果是文件夹,发布下面所有的文档
int i;
if (YesOrNo.yes(docInfo.getIsFolder())) {
List<DocInfo> children = this.listChildDoc(docInfo.getDocId());
Set<Long> ids = children.stream().map(DocInfo::getId).collect(Collectors.toSet());
i = this.query()
.in(DocInfo::getId, ids)
.set(DocInfo::getIsPublish, isPublish)
.update();
} else {
// 发布单个文档
i = this.query()
.eq(DocInfo::getId, docInfoUpdateDTO.getId())
.set(DocInfo::getIsPublish, isPublish)
.update();
}
// 发布一个接口自动发布所属应用
Long docAppId = docInfo.getDocAppId();
if (YesOrNo.yes(isPublish)) {
docAppMapper.query()
.eq(DocApp::getId, docAppId)
.set(DocApp::getIsPublish, isPublish)
.update();
} else {
// 如果应用下的接口都未发布,应用也改成未发布
long count = this.query()
.eq(DocInfo::getDocAppId, docAppId)
.eq(DocInfo::getIsFolder, YesOrNo.NO)
.eq(DocInfo::getIsPublish, YesOrNo.YES)
.getCount();
if (count == 0) {
docAppMapper.query()
.eq(DocApp::getId, docAppId)
.set(DocApp::getIsPublish, YesOrNo.NO)
.update();
}
}
return i;
}
public TornaDocInfoViewDTO getDocDetail(Long id) {
DocInfo docInfo = this.getById(id);
if (docInfo == null || !YesOrNo.yes(docInfo.getIsPublish())) {
throw new BizException("文档不存在");
}
String content = docContentService.getContent(docInfo.getId());
return JSON.parseObject(content, TornaDocInfoViewDTO.class);
}
}

View File

@@ -0,0 +1,222 @@
package com.gitee.sop.admin.service.doc;
import com.alibaba.fastjson2.JSON;
import com.gitee.fastmybatis.core.util.TreeUtil;
import com.gitee.sop.admin.common.constants.YesOrNo;
import com.gitee.sop.admin.common.enums.DocSourceTypeEnum;
import com.gitee.sop.admin.common.user.User;
import com.gitee.sop.admin.dao.entity.DocApp;
import com.gitee.sop.admin.dao.entity.DocInfo;
import com.gitee.sop.admin.dao.mapper.DocAppMapper;
import com.gitee.sop.admin.service.doc.dto.torna.DocIdParam;
import com.gitee.sop.admin.service.doc.dto.torna.DocIdsParam;
import com.gitee.sop.admin.service.doc.dto.torna.TornaDocDTO;
import com.gitee.sop.admin.service.doc.dto.torna.TornaDocInfoDTO;
import com.gitee.sop.admin.service.doc.dto.torna.TornaDocInfoViewDTO;
import com.gitee.sop.admin.service.doc.dto.torna.TornaDocParamDTO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* 接口信息同步
*
* @author 六如
*/
@Service
public class DocInfoSyncService {
@Autowired
private TornaClient tornaClient;
@Autowired
private DocAppMapper docAppMapper;
@Autowired
private DocInfoService docInfoService;
@Autowired
private DocContentService docContentService;
public void syncAppDoc(Long docAppId, User user) {
DocApp docApp = docAppMapper.getById(docAppId);
this.syncDocInfo(docApp, null, user);
}
public void syncDoc(Long docInfoId, User user) {
DocInfo docInfo = docInfoService.getById(docInfoId);
DocApp docApp = docAppMapper.getById(docInfo.getDocAppId());
this.syncDocInfo(docApp, docInfoId, user);
}
public void syncDocInfo(DocApp docApp, Long docInfoId, User user) {
Long docAppId = docApp.getId();
Map<String, DocInfo> nameVersionMap = docInfoService.list(DocInfo::getDocAppId, docAppId)
.stream()
.collect(Collectors.toMap(docInfo -> docInfo.getDocName() + ":" + docInfo.getDocVersion(), Function.identity(), (v1, v2) -> v2));
String token = docApp.getToken();
// add doc
DocIdsParam docIdsParam = buildSearchParam(docInfoId);
TornaDocDTO tornaDocDTO = tornaClient.execute("doc.list", docIdsParam, token, TornaDocDTO.class);
List<TornaDocInfoDTO> docList = tornaDocDTO.getDocList();
if (CollectionUtils.isEmpty(docList)) {
return;
}
List<DocInfo> updateList = new ArrayList<>();
for (TornaDocInfoDTO tornaDocInfoDTO : docList) {
String key = buildKey(tornaDocInfoDTO);
DocInfo docInfo = nameVersionMap.get(key);
// 需要修改的文档
if (docInfo != null) {
docInfo.setDocId(tornaDocInfoDTO.getId());
docInfo.setDocTitle(tornaDocInfoDTO.getName());
docInfo.setDocCode("");
if (YesOrNo.yes(tornaDocInfoDTO.getIsFolder())) {
docInfo.setIsPublish(YesOrNo.YES);
docInfo.setDocName(tornaDocInfoDTO.getName());
}
docInfo.setDocId(tornaDocInfoDTO.getId());
docInfo.setDocType(tornaDocInfoDTO.getType().intValue());
docInfo.setDescription(tornaDocInfoDTO.getDescription());
docInfo.setIsFolder(tornaDocInfoDTO.getIsFolder().intValue());
docInfo.setParentId(tornaDocInfoDTO.getParentId());
docInfo.setUpdateBy(user.getUserId());
updateList.add(docInfo);
}
}
for (DocInfo docInfo : updateList) {
docInfoService.update(docInfo);
}
// 新增的文档
List<DocInfo> saveList = docList.stream()
.filter(tornaDocInfoDTO -> {
String key = buildKey(tornaDocInfoDTO);
return !nameVersionMap.containsKey(key);
})
.map(tornaDocInfoDTO -> {
DocInfo docInfo = new DocInfo();
docInfo.setDocAppId(docAppId);
docInfo.setDocId(tornaDocInfoDTO.getId());
docInfo.setDocTitle(tornaDocInfoDTO.getName());
docInfo.setDocCode("");
docInfo.setDocType(tornaDocInfoDTO.getType().intValue());
docInfo.setSourceType(DocSourceTypeEnum.TORNA.getValue());
if (YesOrNo.yes(tornaDocInfoDTO.getIsFolder())) {
docInfo.setIsPublish(YesOrNo.YES);
docInfo.setDocName(tornaDocInfoDTO.getName());
} else {
docInfo.setIsPublish(YesOrNo.NO);
docInfo.setDocName(tornaDocInfoDTO.getUrl());
}
docInfo.setDocVersion(tornaDocInfoDTO.getVersion());
docInfo.setDescription(tornaDocInfoDTO.getDescription());
docInfo.setIsFolder(tornaDocInfoDTO.getIsFolder().intValue());
docInfo.setParentId(tornaDocInfoDTO.getParentId());
docInfo.setAddBy(user.getUserId());
return docInfo;
})
.collect(Collectors.toList());
docInfoService.saveBatch(saveList);
Set<Long> docIds = docList.stream().map(TornaDocInfoDTO::getId).collect(Collectors.toSet());
this.syncContent(docApp, docIds);
}
private void syncContent(DocApp docApp, Set<Long> docIds) {
List<DocInfo> list = docInfoService.query()
.eq(DocInfo::getDocAppId, docApp.getId())
.in(DocInfo::getDocId, docIds)
.list();
Map<Long, String> docIdMap = this.getContentMap(docApp.getToken(), docIds);
for (DocInfo docInfo : list) {
String content = docIdMap.getOrDefault(docInfo.getDocId(), "");
docContentService.saveContent(
docInfo.getId(),
content
);
}
}
/**
* 批量获取Torna文档内容
*
* @param token token
* @param docIds Torna文档id
* @return key:文档id, value:文档内容
*/
private Map<Long, String> getContentMap(String token, Collection<Long> docIds) {
// 获取torna文档信息
List<TornaDocInfoViewDTO> tornaDocInfoViewList = tornaClient.executeList(
"doc.details",
new DocIdsParam(docIds),
token,
TornaDocInfoViewDTO.class
);
for (TornaDocInfoViewDTO docInfoViewDTO : tornaDocInfoViewList) {
convertTree(docInfoViewDTO);
}
return tornaDocInfoViewList.stream()
.collect(Collectors.toMap(TornaDocInfoViewDTO::getId, JSON::toJSONString, (v1, v2) -> v1));
}
private String getContent(String token, Long docId) {
// 获取torna文档信息
TornaDocInfoViewDTO tornaDocInfoViewDTO = tornaClient.execute(
"doc.detail",
new DocIdParam(docId),
token,
TornaDocInfoViewDTO.class
);
convertTree(tornaDocInfoViewDTO);
return JSON.toJSONString(tornaDocInfoViewDTO);
}
private void convertTree(TornaDocInfoViewDTO tornaDocInfoViewDTO) {
List<TornaDocParamDTO> requestParams = tornaDocInfoViewDTO.getRequestParams();
List<TornaDocParamDTO> responseParams = tornaDocInfoViewDTO.getResponseParams();
List<TornaDocParamDTO> requestTree = TreeUtil.convertTree(requestParams, 0L);
List<TornaDocParamDTO> responseTree = TreeUtil.convertTree(responseParams, 0L);
tornaDocInfoViewDTO.setRequestParams(requestTree);
tornaDocInfoViewDTO.setResponseParams(responseTree);
}
private DocIdsParam buildSearchParam(Long docInfoId) {
if (docInfoId == null) {
return null;
}
DocIdsParam docIdsParam = new DocIdsParam();
DocInfo docInfo = docInfoService.getById(docInfoId);
List<Long> docIdList = new ArrayList<>();
docIdList.add(docInfo.getDocId());
// 如果是文件夹,找下面的子文档
if (YesOrNo.yes(docInfo.getIsFolder())) {
List<Long> docIds = this.listChildrenDocId(docInfo.getDocId());
docIdList.addAll(docIds);
}
docIdsParam.setDocIds(docIdList);
return docIdsParam;
}
private List<Long> listChildrenDocId(Long parentId) {
return docInfoService.query()
.eq(DocInfo::getParentId, parentId)
.listValue(DocInfo::getDocId);
}
private String buildKey(TornaDocInfoDTO tornaDocInfoDTO) {
return YesOrNo.yes(tornaDocInfoDTO.getIsFolder()) ?
tornaDocInfoDTO.getName() + ":" + tornaDocInfoDTO.getVersion()
: tornaDocInfoDTO.getUrl() + ":" + tornaDocInfoDTO.getVersion();
}
}

View File

@@ -0,0 +1,40 @@
package com.gitee.sop.admin.service.doc;
import com.gitee.sop.admin.common.enums.ConfigKeyEnum;
import com.gitee.sop.admin.service.doc.dto.DocSettingDTO;
import com.gitee.sop.admin.service.sys.SysConfigService;
import com.gitee.sop.admin.service.sys.dto.SystemConfigDTO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Collection;
/**
* @author 六如
*/
@Service
public class DocSettingService {
@Autowired
private SysConfigService sysConfigService;
public DocSettingDTO getDocSetting() {
DocSettingDTO docSettingDTO = new DocSettingDTO();
docSettingDTO.setTornaServerAddr(ConfigKeyEnum.TORNA_SERVER_ADDR.getValue());
docSettingDTO.setOpenProdUrl(ConfigKeyEnum.OPEN_PROD_URL.getValue());
docSettingDTO.setOpenSandboxUrl(ConfigKeyEnum.OPEN_SANDBOX_URL.getValue());
return docSettingDTO;
}
public void save(DocSettingDTO docSettingDTO) {
Collection<SystemConfigDTO> systemConfigDTOS = new ArrayList<>();
systemConfigDTOS.add(new SystemConfigDTO(ConfigKeyEnum.TORNA_SERVER_ADDR.getKey(), docSettingDTO.getTornaServerAddr(), "Torna服务器地址"));
systemConfigDTOS.add(new SystemConfigDTO(ConfigKeyEnum.OPEN_PROD_URL.getKey(), docSettingDTO.getOpenProdUrl(), "开放平台线上地址"));
systemConfigDTOS.add(new SystemConfigDTO(ConfigKeyEnum.OPEN_SANDBOX_URL.getKey(), docSettingDTO.getOpenSandboxUrl(), "开放平台沙箱地址"));
sysConfigService.save(systemConfigDTOS);
}
}

View File

@@ -0,0 +1,67 @@
package com.gitee.sop.admin.service.doc;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.gitee.httphelper.HttpHelper;
import com.gitee.sop.admin.common.enums.ConfigKeyEnum;
import com.gitee.sop.admin.common.exception.BizException;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.util.UriUtils;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
* @author 六如
*/
@Component
public class TornaClient {
public <T> T execute(String name, Object param, String token, Class<T> respClass) {
JSONObject data = request(name, param, token).getJSONObject("data");
return data.toJavaObject(respClass);
}
public <T> List<T> executeList(String name, Object param, String token, Class<T> respClass) {
JSONArray data = request(name, param, token).getJSONArray("data");
return data.toList(respClass);
}
private JSONObject request(String name, Object param, String token) {
Map<String, Object> params = new HashMap<>();
params.put("name", name);
params.put("access_token", token);
if (param != null) {
String json = JSON.toJSONString(param);
params.put("data", UriUtils.encode(json, StandardCharsets.UTF_8));
}
try {
String body = HttpHelper.postJson(getTornaApiUrl(), JSON.toJSONString(params))
.execute()
.asString();
JSONObject jsonObject = JSON.parseObject(body);
if (!Objects.equals("0", jsonObject.getString("code"))) {
throw new BizException(jsonObject.getString("msg"));
}
return jsonObject;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public String getTornaApiUrl() {
String value = ConfigKeyEnum.TORNA_SERVER_ADDR.getValue();
if (ObjectUtils.isEmpty(value)) {
throw new BizException("Torna服务器地址未配置");
}
return StringUtils.trimTrailingCharacter(value, '/') + "/api";
}
}

View File

@@ -0,0 +1,52 @@
package com.gitee.sop.admin.service.doc.dto;
import lombok.Data;
import java.time.LocalDateTime;
/**
* 备注:文档应用
*
* @author 六如
*/
@Data
public class DocAppDTO {
/**
* id
*/
private Long id;
/**
* 应用名称
*/
private String appName;
/**
* Torna应用token
*/
private String token;
/**
* 添加时间
*/
private LocalDateTime addTime;
/**
* 修改时间
*/
private LocalDateTime updateTime;
/**
* 创建人id
*/
private Long addBy;
/**
* 修改人id
*/
private Long updateBy;
}

View File

@@ -0,0 +1,14 @@
package com.gitee.sop.admin.service.doc.dto;
import lombok.Data;
/**
* @author 六如
*/
@Data
public class DocInfoConfigDTO {
private String openProdUrl;
private String openSandboxUrl;
}

View File

@@ -0,0 +1,21 @@
package com.gitee.sop.admin.service.doc.dto;
import lombok.Data;
import javax.validation.constraints.NotNull;
/**
* @author 六如
*/
@Data
public class DocInfoPublishUpdateDTO {
@NotNull
private Long id;
/**
* 1-发布,0-下线
*/
private Integer isPublish;
}

View File

@@ -0,0 +1,107 @@
package com.gitee.sop.admin.service.doc.dto;
import com.gitee.fastmybatis.core.support.TreeNode;
import com.gitee.sop.admin.common.dto.UserDTO;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
/**
* 备注:文档信息
*
* @author 六如
*/
@Data
public class DocInfoTreeDTO extends UserDTO implements TreeNode<DocInfoTreeDTO, Long> {
/**
* id
*/
private Long id;
/**
* doc_app.id
*/
private Long docAppId;
/**
* 文档id
*/
private Long docId;
/**
* 文档标题
*/
private String docTitle;
/**
* 文档code
*/
private String docCode;
/**
* 文档类型,1-dubbo,2-富文本,3-Markdown
*/
private Integer docType;
/**
* 来源类型,1-torna,2-自建
*/
private Integer sourceType;
/**
* 文档名称
*/
private String docName;
/**
* 版本号
*/
private String docVersion;
/**
* 描述
*/
private String description;
/**
* 是否分类
*/
private Integer isFolder;
/**
* 状态, 0-未发布,1-已发布
*/
private Integer isPublish;
/**
* 父节点id
*/
private Long parentId;
/**
* 添加时间
*/
private LocalDateTime addTime;
/**
* 修改时间
*/
private LocalDateTime updateTime;
private List<DocInfoTreeDTO> children;
@Override
public Long takeId() {
return docId;
}
@Override
public Long takeParentId() {
return parentId;
}
}

View File

@@ -0,0 +1,16 @@
package com.gitee.sop.admin.service.doc.dto;
import com.gitee.sop.admin.service.doc.dto.torna.TornaDocInfoViewDTO;
import lombok.Data;
/**
* @author 六如
*/
@Data
public class DocInfoViewDTO {
private TornaDocInfoViewDTO docInfoView;
private DocInfoConfigDTO docInfoConfig;
}

View File

@@ -0,0 +1,20 @@
package com.gitee.sop.admin.service.doc.dto;
import lombok.Data;
import org.hibernate.validator.constraints.Length;
/**
* @author 六如
*/
@Data
public class DocSettingDTO {
@Length(max = 256)
private String tornaServerAddr;
@Length(max = 256)
private String openProdUrl;
@Length(max = 256)
private String openSandboxUrl;
}

View File

@@ -0,0 +1,13 @@
package com.gitee.sop.admin.service.doc.dto.torna;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class DocIdParam {
private Long docId;
}

View File

@@ -0,0 +1,22 @@
package com.gitee.sop.admin.service.doc.dto.torna;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.validation.constraints.NotNull;
import java.util.Collection;
/**
* @author 六如
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class DocIdsParam {
@NotNull
private Collection<Long> docIds;
}

View File

@@ -0,0 +1,15 @@
package com.gitee.sop.admin.service.doc.dto.torna;
import lombok.Data;
import java.util.List;
/**
* @author 六如
*/
@Data
public class TornaDocDTO {
private List<TornaDocInfoDTO> docList;
}

View File

@@ -0,0 +1,62 @@
package com.gitee.sop.admin.service.doc.dto.torna;
import lombok.Data;
/**
* @author tanghc
*/
@Data
public class TornaDocInfoDTO {
private Long id;
/**
* 文档名称
*/
private String name;
/**
* 文档概述
*/
private String description;
/**
* 访问URL
*/
private String url;
/**
* 版本号
*/
private String version;
/**
* http方法
*/
private String httpMethod;
/**
* contentType
*/
private String contentType;
/**
* 文档类型,0:http,1:dubbo,2:富文本,3:Markdown
*/
private Byte type;
/**
* 是否是分类0不是1
*/
private Byte isFolder;
/**
* 父节点
*/
private Long parentId;
/**
* 是否显示
*/
private Byte isShow;
}

View File

@@ -0,0 +1,178 @@
package com.gitee.sop.admin.service.doc.dto.torna;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
/**
* @author tanghc
*/
@Data
public class TornaDocInfoViewDTO {
private Long id;
/**
* 文档名称
*/
private String name;
/**
* 文档概述
*/
private String description;
/**
* 0:http,1:dubbo,2:富文本,3:Markdown
*/
private Byte type;
/**
* 访问URL
*/
private String url;
/**
* 版本号
*/
private String version = "";
private String docKey;
/**
* http方法
*/
private String httpMethod;
/**
* contentType
*/
private String contentType;
/**
* 是否是分类0不是1
*/
private Byte isFolder;
/**
* 父节点
*/
private Long parentId;
/**
* 模块idmodule.id
*/
private Long moduleId;
/**
* 项目id
*/
private Long projectId;
/**
* 是否使用全局请求参数
*/
private Byte isUseGlobalHeaders;
/**
* 是否使用全局请求参数
*/
private Byte isUseGlobalParams;
/**
* 是否使用全局返回参数
*/
private Byte isUseGlobalReturns;
/**
* 是否请求数组
*/
private Byte isRequestArray;
/**
* 是否返回数组
*/
private Byte isResponseArray;
/**
* 请求数组时元素类型
*/
private String requestArrayType;
/**
* 返回数组时元素类型
*/
private String responseArrayType;
/**
* 文档状态
*/
private Byte status;
private String remark;
private Integer orderIndex;
/**
* 数据库字段gmt_create
*/
private LocalDateTime gmtCreate;
/**
* 数据库字段gmt_modified
*/
private LocalDateTime gmtModified;
private List<TornaDocParamDTO> pathParams = Collections.emptyList();
private List<TornaDocParamDTO> headerParams = Collections.emptyList();
private List<TornaDocParamDTO> headerParamsRaw = Collections.emptyList();
private List<TornaDocParamDTO> queryParams = Collections.emptyList();
private List<TornaDocParamDTO> requestParams = Collections.emptyList();
private List<TornaDocParamDTO> responseParams = Collections.emptyList();
private List<TornaDocParamDTO> errorCodeParams = Collections.emptyList();
private List<TornaDocParamDTO> globalHeaders = Collections.emptyList();
private List<TornaDocParamDTO> globalParams = Collections.emptyList();
private List<TornaDocParamDTO> globalReturns = Collections.emptyList();
private String errorCodeInfo;
private List<TornaDocInfoViewDTO> children = Collections.emptyList();
public String getDocName() {
return name;
}
public String getDocTitle() {
return name;
}
}

View File

@@ -0,0 +1,128 @@
package com.gitee.sop.admin.service.doc.dto.torna;
import com.gitee.fastmybatis.core.support.TreeNode;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;
/**
* @author tanghc
*/
@Data
public class TornaDocParamDTO implements TreeNode<TornaDocParamDTO, Long> {
private Long id;
/**
* 字段名称
*/
private String name;
/**
* 字段类型
*/
private String type;
/**
* 是否必须10
*/
private Byte required;
/**
* 最大长度
*/
private String maxLength;
/**
* 示例值
*/
private String example;
/**
* 描述
*/
private String description;
private Long enumId;
/**
* doc_info.id
*/
private Long docId;
/**
* 父节点
*/
private Long parentId;
/**
* 0header, 1请求参数2返回参数3错误码
*/
private Byte style;
/**
* 新增操作方式0人工操作1开放平台推送
*/
private Byte createMode;
/**
* 修改操作方式0人工操作1开放平台推送
*/
private Byte modifyMode;
/**
* 创建人
*/
private String creatorName;
/**
* 修改人
*/
private String modifierName;
/**
* 排序
*/
private Integer orderIndex;
private Byte isDeleted;
/**
* 数据库字段gmt_create
*/
private LocalDateTime gmtCreate;
/**
* 数据库字段gmt_modified
*/
private LocalDateTime gmtModified;
private boolean global;
private List<TornaDocParamDTO> children;
public boolean getRequire() {
return Objects.equals(this.required, 1);
}
@Override
public Long takeId() {
return id;
}
@Override
public Long takeParentId() {
return parentId;
}
}

View File

@@ -0,0 +1,15 @@
package com.gitee.sop.admin.service.doc.dto.torna;
import lombok.Data;
/**
* @author 六如
*/
@Data
public class TornaModuleDTO {
private Long id;
private String name;
}

View File

@@ -0,0 +1,166 @@
package com.gitee.sop.admin.service.isv;
import com.gitee.fastmybatis.core.PageInfo;
import com.gitee.fastmybatis.core.query.LambdaQuery;
import com.gitee.sop.admin.common.constants.YesOrNo;
import com.gitee.sop.admin.common.context.SpringContext;
import com.gitee.sop.admin.common.dto.StatusUpdateDTO;
import com.gitee.sop.admin.common.enums.StatusEnum;
import com.gitee.sop.admin.common.support.ServiceSupport;
import com.gitee.sop.admin.common.util.CopyUtil;
import com.gitee.sop.admin.common.util.IdGen;
import com.gitee.sop.admin.common.util.RSATool;
import com.gitee.sop.admin.dao.entity.IsvInfo;
import com.gitee.sop.admin.dao.entity.IsvKeys;
import com.gitee.sop.admin.dao.mapper.IsvInfoMapper;
import com.gitee.sop.admin.service.isv.dto.IsvInfoAddDTO;
import com.gitee.sop.admin.service.isv.dto.IsvInfoDTO;
import com.gitee.sop.admin.service.isv.dto.IsvInfoUpdateDTO;
import com.gitee.sop.admin.service.isv.dto.IsvInfoUpdateKeysDTO;
import com.gitee.sop.admin.service.isv.dto.IsvKeysDTO;
import com.gitee.sop.admin.service.isv.event.ChangeIsvInfoEvent;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* @author 六如
*/
@Service
public class IsvInfoService implements ServiceSupport<IsvInfo, IsvInfoMapper> {
@Autowired
private IsvKeysService isvKeysService;
@Autowired
private PermIsvGroupService permIsvGroupService;
public PageInfo<IsvInfoDTO> doPage(LambdaQuery<IsvInfo> query) {
query.orderByDesc(IsvInfo::getId);
PageInfo<IsvInfo> page = this.page(query);
List<IsvInfo> list = page.getList();
if (CollectionUtils.isEmpty(list)) {
return page.convert(isvInfo -> new IsvInfoDTO());
}
List<Long> idList = list.stream()
.map(IsvInfo::getId).collect(Collectors.toList());
Map<Long, IsvKeys> isvIdMap = isvKeysService.query()
.in(IsvKeys::getIsvId, idList)
.map(IsvKeys::getIsvId, Function.identity());
Map<Long, List<String>> isvGroupMap = permIsvGroupService.getIsvGroupNameMap(idList);
// 格式转换
return page.convert(isvInfo -> {
IsvInfoDTO isvInfoDTO = CopyUtil.copyBean(isvInfo, IsvInfoDTO::new);
boolean hasKey = false;
Optional<IsvKeys> isvKeysOpt = Optional.ofNullable(isvIdMap.get(isvInfo.getId()));
if (isvKeysOpt.isPresent()) {
IsvKeys isvKeys = isvKeysOpt.get();
hasKey = !StringUtils.isAllBlank(
isvKeys.getPrivateKeyIsv(),
isvKeys.getPrivateKeyPlatform(),
isvKeys.getPublicKeyIsv(),
isvKeys.getPublicKeyPlatform()
);
}
isvInfoDTO.setHasKeys(YesOrNo.of(hasKey));
List<String> groupNames = isvGroupMap.getOrDefault(isvInfo.getId(), Collections.emptyList());
isvInfoDTO.setGroupNames(String.join("/", groupNames));
return isvInfoDTO;
});
}
public RSATool.KeyStore createKeys(RSATool.KeyFormat keyFormat) throws Exception {
if (keyFormat == null) {
keyFormat = RSATool.KeyFormat.PKCS8;
}
RSATool rsaTool = new RSATool(keyFormat, RSATool.KeyLength.LENGTH_2048);
return rsaTool.createKeys();
}
/**
* 添加ISV
*
* @param isvInfoAddDTO
* @return 返回id
*/
public long add(IsvInfoAddDTO isvInfoAddDTO) {
IsvInfo rec = CopyUtil.copyBean(isvInfoAddDTO, IsvInfo::new);
String appKey = new SimpleDateFormat("yyyyMMdd").format(new Date()) + IdGen.nextId();
rec.setAppId(appKey);
rec.setStatus(StatusEnum.ENABLE.getValue());
this.save(rec);
this.sendChangeEvent(rec.getId());
return rec.getId();
}
public int update(IsvInfoUpdateDTO isvInfoUpdateDTO) {
IsvInfo isvInfo = new IsvInfo();
isvInfo.setId(isvInfoUpdateDTO.getId());
isvInfo.setStatus(isvInfoUpdateDTO.getStatus());
isvInfo.setRemark(isvInfoUpdateDTO.getRemark());
int cnt = this.update(isvInfo);
sendChangeEvent(isvInfoUpdateDTO.getId());
return cnt;
}
public int updateKeys(IsvInfoUpdateKeysDTO isvInfoUpdateKeysDTO) {
return isvKeysService.saveKeys(isvInfoUpdateKeysDTO);
}
public IsvKeysDTO getKeys(Long isvId) {
IsvKeys isvKeys = isvKeysService.get(IsvKeys::getIsvId, isvId);
IsvKeysDTO isvKeysDTO;
if (isvKeys == null) {
isvKeysDTO = new IsvKeysDTO();
isvKeysDTO.setIsvId(isvId);
isvKeysDTO.setKeyFormat(RSATool.KeyFormat.PKCS8.getValue());
isvKeysDTO.setPublicKeyIsv("");
isvKeysDTO.setPrivateKeyIsv("");
isvKeysDTO.setPublicKeyPlatform("");
isvKeysDTO.setPrivateKeyPlatform("");
} else {
isvKeysDTO = CopyUtil.copyBean(isvKeys, IsvKeysDTO::new);
}
IsvInfo isvInfo = this.getById(isvId);
isvKeysDTO.setAppId(isvInfo.getAppId());
return isvKeysDTO;
}
/**
* 修改状态
*
* @param statusUpdateDTO 修改值
* @return 返回影响行数
*/
public int updateStatus(StatusUpdateDTO statusUpdateDTO) {
Long isvId = statusUpdateDTO.getId();
int cnt = this.query()
.eq(IsvInfo::getId, isvId)
.set(IsvInfo::getStatus, statusUpdateDTO.getStatus())
.update();
// 同步isv信息
this.sendChangeEvent(isvId);
return cnt;
}
private void sendChangeEvent(Long isvId) {
SpringContext.publishEvent(new ChangeIsvInfoEvent(Collections.singletonList(isvId)));
}
}

View File

@@ -0,0 +1,33 @@
package com.gitee.sop.admin.service.isv;
import com.gitee.sop.admin.common.context.SpringContext;
import com.gitee.sop.admin.common.support.ServiceSupport;
import com.gitee.sop.admin.common.util.CopyUtil;
import com.gitee.sop.admin.dao.entity.IsvKeys;
import com.gitee.sop.admin.dao.mapper.IsvKeysMapper;
import com.gitee.sop.admin.service.isv.dto.IsvInfoUpdateKeysDTO;
import com.gitee.sop.admin.service.isv.event.ChangeIsvKeyEvent;
import org.springframework.stereotype.Service;
import java.util.Collections;
/**
* @author 六如
*/
@Service
public class IsvKeysService implements ServiceSupport<IsvKeys, IsvKeysMapper> {
public int saveKeys(IsvInfoUpdateKeysDTO isvInfoUpdateKeysDTO) {
IsvKeys isvKeys = this.get(IsvKeys::getIsvId, isvInfoUpdateKeysDTO.getIsvId());
if (isvKeys == null) {
isvKeys = new IsvKeys();
}
CopyUtil.copyPropertiesIgnoreNull(isvInfoUpdateKeysDTO, isvKeys);
int cnt = this.saveOrUpdate(isvKeys);
// 发送变更事件
SpringContext.publishEvent(new ChangeIsvKeyEvent(Collections.singletonList(isvInfoUpdateKeysDTO.getIsvId())));
return cnt;
}
}

View File

@@ -0,0 +1,114 @@
package com.gitee.sop.admin.service.isv;
import com.gitee.fastmybatis.core.PageInfo;
import com.gitee.fastmybatis.core.query.LambdaQuery;
import com.gitee.sop.admin.common.context.SpringContext;
import com.gitee.sop.admin.common.support.ServiceSupport;
import com.gitee.sop.admin.dao.entity.ApiInfo;
import com.gitee.sop.admin.dao.entity.PermGroupPermission;
import com.gitee.sop.admin.dao.entity.PermIsvGroup;
import com.gitee.sop.admin.dao.mapper.ApiInfoMapper;
import com.gitee.sop.admin.dao.mapper.PermGroupPermissionMapper;
import com.gitee.sop.admin.dao.mapper.PermIsvGroupMapper;
import com.gitee.sop.admin.service.isv.dto.PermGroupPermissionDTO;
import com.gitee.sop.admin.service.isv.event.ChangeIsvPermEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
/**
* @author 六如
*/
@Service
public class PermGroupPermissionService implements ServiceSupport<PermGroupPermission, PermGroupPermissionMapper> {
@Autowired
private ApiInfoMapper apiInfoMapper;
@Autowired
private PermIsvGroupMapper permIsvGroupMapper;
public PageInfo<PermGroupPermission> doPage(LambdaQuery<PermGroupPermission> query) {
query.orderByDesc(PermGroupPermission::getId);
PageInfo<PermGroupPermission> page = this.page(query);
// 格式转换
return page.convert(isvInfo -> {
return isvInfo;
});
}
public PageInfo<ApiInfo> pageGroupApiId(Long groupId, LambdaQuery<ApiInfo> query) {
List<Long> apiIds = this.query()
.eq(PermGroupPermission::getGroupId, groupId)
.listUniqueValue(PermGroupPermission::getApiId);
if (apiIds.isEmpty()) {
return new PageInfo<>();
}
query.in(ApiInfo::getId, apiIds);
return apiInfoMapper.page(query);
}
/**
* 设置组权限
*
* @param param
* @return
*/
public int setting(PermGroupPermissionDTO param) {
Long groupId = param.getGroupId();
List<Long> apiIdList = param.getApiIdList();
if (CollectionUtils.isEmpty(apiIdList)) {
return 0;
}
List<Long> existApiIdList = this.query()
.eq(PermGroupPermission::getGroupId, groupId)
.listUniqueValue(PermGroupPermission::getApiId);
List<PermGroupPermission> saveList = apiIdList.stream()
// 已存在的不添加
.filter(apiId -> !existApiIdList.contains(apiId))
.map(apiId -> {
PermGroupPermission permGroupPermission = new PermGroupPermission();
permGroupPermission.setGroupId(groupId);
permGroupPermission.setApiId(apiId);
return permGroupPermission;
})
.collect(Collectors.toList());
int cnt = this.saveBatch(saveList);
// 刷新isv权限
this.refreshIsvPerm(groupId);
return cnt;
}
private void refreshIsvPerm(Long groupId) {
List<Long> isvIds = permIsvGroupMapper.query()
.eq(PermIsvGroup::getGroupId, groupId)
.listUniqueValue(PermIsvGroup::getIsvId);
SpringContext.publishEvent(new ChangeIsvPermEvent(isvIds));
}
public int delete(Long groupId, Collection<Long> apiIds) {
int cnt = this.query()
.eq(PermGroupPermission::getGroupId, groupId)
.in(PermGroupPermission::getApiId, apiIds)
.delete();
this.refreshIsvPerm(groupId);
return cnt;
}
}

View File

@@ -0,0 +1,55 @@
package com.gitee.sop.admin.service.isv;
import com.gitee.fastmybatis.core.PageInfo;
import com.gitee.fastmybatis.core.query.LambdaQuery;
import com.gitee.sop.admin.common.exception.BizException;
import com.gitee.sop.admin.common.support.ServiceSupport;
import com.gitee.sop.admin.dao.entity.PermGroup;
import com.gitee.sop.admin.dao.entity.PermIsvGroup;
import com.gitee.sop.admin.dao.mapper.PermGroupMapper;
import com.gitee.sop.admin.dao.mapper.PermIsvGroupMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Collection;
import java.util.Map;
/**
* @author 六如
*/
@Service
public class PermGroupService implements ServiceSupport<PermGroup, PermGroupMapper> {
@Autowired
PermIsvGroupMapper permIsvGroupMapper;
public PageInfo<PermGroup> doPage(LambdaQuery<PermGroup> query) {
query.orderByDesc(PermGroup::getId);
PageInfo<PermGroup> page = this.page(query);
// 格式转换
return page.convert(isvInfo -> {
return isvInfo;
});
}
public Map<Long, String> getCodeNameMap(Collection<Long> groupIdList) {
return this.query()
.in(PermGroup::getId, groupIdList)
.map(PermGroup::getId, PermGroup::getGroupName);
}
public int delete(Long groupId) {
boolean used = permIsvGroupMapper.query()
.eq(PermIsvGroup::getGroupId, groupId)
.get() != null;
if (used) {
throw new BizException("无法删除:分组已被使用");
}
return this.deleteById(groupId);
}
}

View File

@@ -0,0 +1,90 @@
package com.gitee.sop.admin.service.isv;
import com.gitee.fastmybatis.core.PageInfo;
import com.gitee.fastmybatis.core.query.LambdaQuery;
import com.gitee.sop.admin.common.support.ServiceSupport;
import com.gitee.sop.admin.dao.entity.PermIsvGroup;
import com.gitee.sop.admin.dao.mapper.PermIsvGroupMapper;
import com.gitee.sop.admin.service.isv.dto.IsvGroupSettingDTO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
/**
* @author 六如
*/
@Service
public class PermIsvGroupService implements ServiceSupport<PermIsvGroup, PermIsvGroupMapper> {
@Autowired
private PermGroupService permGroupService;
public PageInfo<PermIsvGroup> doPage(LambdaQuery<PermIsvGroup> query) {
query.orderByDesc(PermIsvGroup::getId);
PageInfo<PermIsvGroup> page = this.page(query);
// 格式转换
return page.convert(isvInfo -> {
return isvInfo;
});
}
public Map<Long, List<String>> getIsvGroupNameMap(Collection<Long> isvIds) {
List<PermIsvGroup> list = this.list(PermIsvGroup::getIsvId, isvIds);
if (list.isEmpty()) {
return new HashMap<>();
}
Set<Long> groupIds = list.stream().map(PermIsvGroup::getGroupId).collect(Collectors.toSet());
Map<Long, String> groupCodeMap = permGroupService.getCodeNameMap(groupIds);
return this.query()
.in(PermIsvGroup::getIsvId, isvIds)
.group(PermIsvGroup::getIsvId,
permIsvGroup -> groupCodeMap.getOrDefault(permIsvGroup.getGroupId(), ""));
}
@Transactional(rollbackFor = Exception.class)
public int updateIsvGroup(IsvGroupSettingDTO isvGroupSettingDTO) {
// 先删除所有
int i = this.deleteByColumn(PermIsvGroup::getIsvId, isvGroupSettingDTO.getIsvId());
List<Long> groupIds = isvGroupSettingDTO.getGroupIds();
if (CollectionUtils.isEmpty(groupIds)) {
return i;
}
List<PermIsvGroup> saveList = groupIds
.stream()
.map(groupId -> {
PermIsvGroup permIsvGroup = new PermIsvGroup();
permIsvGroup.setIsvId(isvGroupSettingDTO.getIsvId());
permIsvGroup.setGroupId(groupId);
return permIsvGroup;
}).collect(Collectors.toList());
return this.saveBatch(saveList);
}
public List<Long> listIsvGroupId(Long isvId) {
List<PermIsvGroup> list = this.list(PermIsvGroup::getIsvId, isvId);
if (list.isEmpty()) {
return new ArrayList<>(0);
}
return list.stream().map(PermIsvGroup::getGroupId).distinct().collect(Collectors.toList());
}
public boolean isUsed(Long groupId) {
return this.query()
.eq(PermIsvGroup::getGroupId, groupId)
.getCount() > 0;
}
}

View File

@@ -0,0 +1,19 @@
package com.gitee.sop.admin.service.isv.dto;
import lombok.Data;
import javax.validation.constraints.NotNull;
import java.util.List;
/**
* @author 六如
*/
@Data
public class IsvGroupSettingDTO {
@NotNull
private Long isvId;
private List<Long> groupIds;
}

View File

@@ -0,0 +1,27 @@
package com.gitee.sop.admin.service.isv.dto;
import lombok.Data;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.NotNull;
/**
* @author 六如
*/
@Data
public class IsvInfoAddDTO {
/**
* 1启用2禁用
*/
@NotNull
private Integer status;
/**
* 备注
*/
@Length(max = 500)
private String remark;
}

View File

@@ -0,0 +1,56 @@
package com.gitee.sop.admin.service.isv.dto;
import com.gitee.sop.admin.service.jackson.convert.annotation.UserFormat;
import lombok.Data;
import java.time.LocalDateTime;
/**
*
* @author 六如
*/
@Data
public class IsvInfoDTO {
private Long id;
/**
* appKey
*/
private String appId;
/**
* 1启用2禁用
*/
private Integer status;
/**
* 是否有秘钥
*/
private Integer hasKeys;
/**
* 备注
*/
private String remark;
private String groupNames;
private LocalDateTime addTime;
private LocalDateTime updateTime;
/**
* 创建人id
*/
@UserFormat
private Long addBy;
/**
* 修改人id
*/
@UserFormat
private Long updateBy;
}

View File

@@ -0,0 +1,14 @@
package com.gitee.sop.admin.service.isv.dto;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* @author 六如
*/
@EqualsAndHashCode(callSuper = true)
@Data
public class IsvInfoUpdateDTO extends IsvInfoAddDTO {
private Long id;
}

View File

@@ -0,0 +1,38 @@
package com.gitee.sop.admin.service.isv.dto;
import lombok.Data;
/**
* @author 六如
*/
@Data
public class IsvInfoUpdateKeysDTO {
private Long isvId;
/**
* 秘钥格式1PKCS8(JAVA适用)2PKCS1(非JAVA适用)
*/
private Integer keyFormat;
/**
* 开发者生成的公钥, 数据库字段public_key_isv
*/
private String publicKeyIsv;
/**
* 开发者生成的私钥(交给开发者), 数据库字段private_key_isv
*/
private String privateKeyIsv;
/**
* 平台生成的公钥(交给开发者), 数据库字段public_key_platform
*/
private String publicKeyPlatform;
/**
* 平台生成的私钥, 数据库字段private_key_platform
*/
private String privateKeyPlatform;
}

View File

@@ -0,0 +1,40 @@
package com.gitee.sop.admin.service.isv.dto;
import lombok.Data;
/**
* @author 六如
*/
@Data
public class IsvKeysDTO {
private String appId;
private Long isvId;
/**
* 秘钥格式1PKCS8(JAVA适用)2PKCS1(非JAVA适用)
*/
private Integer keyFormat;
/**
* 开发者生成的公钥, 数据库字段public_key_isv
*/
private String publicKeyIsv;
/**
* 开发者生成的私钥(交给开发者), 数据库字段private_key_isv
*/
private String privateKeyIsv;
/**
* 平台生成的公钥(交给开发者), 数据库字段public_key_platform
*/
private String publicKeyPlatform;
/**
* 平台生成的私钥, 数据库字段private_key_platform
*/
private String privateKeyPlatform;
}

View File

@@ -0,0 +1,31 @@
package com.gitee.sop.admin.service.isv.dto;
import lombok.Data;
/**
* @author 六如
*/
@Data
public class IsvKeysGenDTO {
/**
* 开发者生成的公钥, 数据库字段public_key_isv
*/
private String publicKeyIsv;
/**
* 开发者生成的私钥(交给开发者), 数据库字段private_key_isv
*/
private String privateKeyIsv;
/**
* 平台生成的公钥(交给开发者), 数据库字段public_key_platform
*/
private String publicKeyPlatform;
/**
* 平台生成的私钥, 数据库字段private_key_platform
*/
private String privateKeyPlatform;
}

View File

@@ -0,0 +1,19 @@
package com.gitee.sop.admin.service.isv.dto;
import lombok.Data;
import javax.validation.constraints.NotNull;
import java.util.List;
/**
* @author 六如
*/
@Data
public class PermGroupPermissionDTO {
@NotNull
private Long groupId;
private List<Long> apiIdList;
}

View File

@@ -0,0 +1,17 @@
package com.gitee.sop.admin.service.isv.event;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Collection;
/**
* @author 六如
*/
@Getter
@AllArgsConstructor
public class ChangeApiInfoEvent {
private final Collection<Long> apiIds;
}

View File

@@ -0,0 +1,17 @@
package com.gitee.sop.admin.service.isv.event;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Collection;
/**
* @author 六如
*/
@Getter
@AllArgsConstructor
public class ChangeIsvInfoEvent {
private final Collection<Long> isvIds;
}

View File

@@ -0,0 +1,17 @@
package com.gitee.sop.admin.service.isv.event;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.List;
/**
* @author 六如
*/
@Getter
@AllArgsConstructor
public class ChangeIsvKeyEvent {
private final List<Long> isvIds;
}

View File

@@ -0,0 +1,17 @@
package com.gitee.sop.admin.service.isv.event;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.List;
/**
* @author 六如
*/
@Getter
@AllArgsConstructor
public class ChangeIsvPermEvent {
private final List<Long> isvIds;
}

View File

@@ -0,0 +1,79 @@
package com.gitee.sop.admin.service.isv.listener;
import com.gitee.sop.admin.dao.entity.IsvInfo;
import com.gitee.sop.admin.dao.mapper.IsvInfoMapper;
import com.gitee.sop.admin.service.isv.event.ChangeApiInfoEvent;
import com.gitee.sop.admin.service.isv.event.ChangeIsvInfoEvent;
import com.gitee.sop.admin.service.isv.event.ChangeIsvKeyEvent;
import com.gitee.sop.admin.service.isv.event.ChangeIsvPermEvent;
import com.gitee.sop.support.service.RefreshService;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import java.util.Collection;
import java.util.List;
/**
* 变更监听
*
* @author 六如
*/
@Component
public class ChangeListener {
@DubboReference
private RefreshService refreshService;
@Autowired
private IsvInfoMapper isvInfoMapper;
/**
* 监听isv信息变更
*
* @param event
*/
@EventListener
public void onChangeApiInfoEvent(ChangeApiInfoEvent event) {
Collection<Long> apiIds = event.getApiIds();
refreshService.refreshApi(apiIds);
}
/**
* 监听isv信息变更
*
* @param event
*/
@EventListener
public void onChangeIsvInfoEvent(ChangeIsvInfoEvent event) {
Collection<Long> isvIds = event.getIsvIds();
Collection<String> appIds = isvInfoMapper.query()
.in(IsvInfo::getId, isvIds)
.listUniqueValue(IsvInfo::getAppId);
refreshService.refreshIsv(appIds);
}
/**
* 监听isv秘钥变更
*
* @param event
*/
@EventListener
public void onChangeIsvKeyEvent(ChangeIsvKeyEvent event) {
List<Long> isvIds = event.getIsvIds();
refreshService.refreshSecret(isvIds);
}
/**
* 监听isv分组变更
*
* @param event
*/
@EventListener
public void onChangeIsvGroupEvent(ChangeIsvPermEvent event) {
List<Long> isvIds = event.getIsvIds();
refreshService.refreshIsvPerm(isvIds);
}
}

View File

@@ -0,0 +1,31 @@
package com.gitee.sop.admin.service.jackson.convert.annotation;
import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.gitee.sop.admin.service.jackson.convert.serde.UserFormatSerializer;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 序列化自动转换成用户名称
*
* @author 六如
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@JacksonAnnotationsInside
@JsonSerialize(using = UserFormatSerializer.class)
public @interface UserFormat {
/**
* 显示用户名称
* @return
*/
boolean showName() default true;
}

View File

@@ -0,0 +1,43 @@
package com.gitee.sop.admin.service.jackson.convert.serde;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.gitee.sop.admin.common.context.SpringContext;
import com.gitee.sop.admin.common.resp.UserVO;
import com.gitee.sop.admin.common.user.User;
import com.gitee.sop.admin.common.util.CopyUtil;
import com.gitee.sop.admin.service.sys.UserCacheService;
import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
import java.util.Objects;
import java.util.Optional;
/**
* @author 六如
*/
@Slf4j
public class UserFormatSerializer extends JsonSerializer<Object> {
@Override
public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
if (value == null) {
gen.writeNull();
return;
}
Long userId = (Long) value;
if (Objects.equals(userId, 0L)) {
gen.writeNumber(userId);
return;
}
Optional<User> userOpt = SpringContext.getBean(UserCacheService.class).getUser(userId);
if (userOpt.isPresent()) {
User user = userOpt.get();
UserVO userVO = CopyUtil.copyBean(user, UserVO::new);
gen.writeObject(userVO);
} else {
gen.writeObject(value);
}
}
}

View File

@@ -0,0 +1 @@
package com.gitee.sop.admin.service.jackson;

View File

@@ -0,0 +1,47 @@
package com.gitee.sop.admin.service.serve;
import com.gitee.sop.admin.common.context.SpringContext;
import com.gitee.sop.admin.common.dto.StatusUpdateDTO;
import com.gitee.sop.admin.common.support.ServiceSupport;
import com.gitee.sop.admin.dao.entity.ApiInfo;
import com.gitee.sop.admin.dao.mapper.ApiInfoMapper;
import com.gitee.sop.admin.service.isv.event.ChangeApiInfoEvent;
import org.springframework.stereotype.Service;
import java.util.Collections;
/**
* @author 六如
*/
@Service
public class ApiInfoService implements ServiceSupport<ApiInfo, ApiInfoMapper> {
/**
* 修改状态
*
* @param statusUpdateDTO 修改值
* @return 返回影响行数
*/
public int updateStatus(StatusUpdateDTO statusUpdateDTO) {
int cnt = this.query()
.eq(ApiInfo::getId, statusUpdateDTO.getId())
.set(ApiInfo::getStatus, statusUpdateDTO.getStatus())
.update();
this.sendChangeEvent(statusUpdateDTO.getId());
return cnt;
}
@Override
public int update(ApiInfo entity) {
int cnt = ServiceSupport.super.update(entity);
sendChangeEvent(entity.getId());
return cnt;
}
private void sendChangeEvent(Long id) {
SpringContext.publishEvent(new ChangeApiInfoEvent(Collections.singletonList(id)));
}
}

View File

@@ -0,0 +1,107 @@
package com.gitee.sop.admin.service.sys;
import com.gitee.fastmybatis.core.support.BaseLambdaService;
import com.gitee.sop.admin.common.config.IConfig;
import com.gitee.sop.admin.common.util.CopyUtil;
import com.gitee.sop.admin.dao.entity.SysConfig;
import com.gitee.sop.admin.dao.mapper.SysConfigMapper;
import com.gitee.sop.admin.service.sys.dto.SystemConfigDTO;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Service;
import java.util.Collection;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
/**
* @author 六如
*/
@Service
public class SysConfigService extends BaseLambdaService<SysConfig, SysConfigMapper> implements IConfig {
@Autowired
private Environment environment;
// key: configKey, value: configValue
private final LoadingCache<String, Optional<String>> configCache = CacheBuilder.newBuilder()
.expireAfterAccess(15, TimeUnit.MINUTES)
.build(new CacheLoader<String, Optional<String>>() {
@Override
public Optional<String> load(String key) throws Exception {
return Optional.ofNullable(getConfigValue(key, null));
}
});
public void save(Collection<SystemConfigDTO> configs) {
configs.forEach(this::setConfig);
}
public String getRawValue(String key) {
return this.query()
.eq(SysConfig::getConfigKey, key)
.getValue(SysConfig::getConfigValue);
}
public void setConfig(String key, String value) {
setConfig(key, value, "");
}
public void setConfig(String key, String value, String remark) {
SystemConfigDTO systemConfigDTO = new SystemConfigDTO();
systemConfigDTO.setConfigKey(key);
systemConfigDTO.setConfigValue(value);
systemConfigDTO.setRemark(remark);
setConfig(systemConfigDTO);
}
public void setConfig(SystemConfigDTO systemConfigDTO) {
Objects.requireNonNull(systemConfigDTO.getConfigKey(), "need key");
Objects.requireNonNull(systemConfigDTO.getConfigValue(), "need value");
SysConfig systemConfig = get(SysConfig::getConfigKey, systemConfigDTO.getConfigKey());
if (systemConfig == null) {
systemConfig = CopyUtil.copyBean(systemConfigDTO, SysConfig::new);
this.save(systemConfig);
} else {
CopyUtil.copyPropertiesIgnoreNull(systemConfigDTO, systemConfig);
this.update(systemConfig);
}
configCache.invalidate(systemConfigDTO.getConfigKey());
}
/**
* 获取配置信息
* <pre>
* 优先级:
* 数据库
* Environment
* 默认配置
* </pre>
*
* @param key 配置key
* @param defaultValue 没有获取到返回的默认值
* @return 返回配置信息,如果没有获取到值,则返回默认值
*/
public String getConfigValue(String key, String defaultValue) {
Objects.requireNonNull(key, "need key");
SysConfig systemConfig = get(SysConfig::getConfigKey, key);
return Optional.ofNullable(systemConfig)
.map(SysConfig::getConfigValue)
.orElseGet(() -> environment.getProperty(key, defaultValue));
}
@Override
public String getConfig(String key) {
return configCache.getUnchecked(key).orElse(null);
}
@Override
public String getConfig(String key, String defaultValue) {
return configCache.getUnchecked(key).orElse(defaultValue);
}
}

View File

@@ -0,0 +1,54 @@
package com.gitee.sop.admin.service.sys;
import com.gitee.fastmybatis.core.query.LambdaQuery;
import com.gitee.fastmybatis.core.util.TreeUtil;
import com.gitee.sop.admin.common.dto.StatusUpdateDTO;
import com.gitee.sop.admin.common.exception.BizException;
import com.gitee.sop.admin.common.support.ServiceSupport;
import com.gitee.sop.admin.common.util.CopyUtil;
import com.gitee.sop.admin.dao.entity.SysDept;
import com.gitee.sop.admin.dao.mapper.SysDeptMapper;
import com.gitee.sop.admin.service.sys.dto.SysDeptDTO;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Objects;
/**
* @author 六如
*/
@Service
public class SysDeptService implements ServiceSupport<SysDept, SysDeptMapper> {
public Long addDept(SysDept sysDept) {
long count = this.query()
.eq(SysDept::getParentId, 0)
.getCount();
if (count > 0 && Objects.equals(sysDept.getParentId(), 0L)) {
throw new BizException("只能有一个根节点");
}
this.save(sysDept);
return sysDept.getId();
}
public List<SysDeptDTO> listTree(LambdaQuery<SysDept> query) {
List<SysDept> list = this.list(query);
List<SysDeptDTO> sysDeptList = CopyUtil.copyList(list, SysDeptDTO::new);
return TreeUtil.convertTree(sysDeptList, 0L);
}
/**
* 修改状态
*
* @param statusUpdateDTO 修改值
* @return 返回影响行数
*/
public int updateStatus(StatusUpdateDTO statusUpdateDTO) {
return this.query()
.eq(SysDept::getId, statusUpdateDTO.getId())
.set(SysDept::getStatus, statusUpdateDTO.getStatus())
.update();
}
}

View File

@@ -0,0 +1,15 @@
package com.gitee.sop.admin.service.sys;
import com.gitee.sop.admin.common.support.ServiceSupport;
import com.gitee.sop.admin.dao.entity.SysResource;
import com.gitee.sop.admin.dao.mapper.SysResourceMapper;
import org.springframework.stereotype.Service;
/**
* @author 六如
*/
@Service
public class SysResourceService implements ServiceSupport<SysResource, SysResourceMapper> {
}

View File

@@ -0,0 +1,60 @@
package com.gitee.sop.admin.service.sys;
import com.gitee.sop.admin.common.support.ServiceSupport;
import com.gitee.sop.admin.dao.entity.SysRoleResource;
import com.gitee.sop.admin.dao.mapper.SysRoleResourceMapper;
import com.gitee.sop.admin.service.sys.dto.SysRoleResourceDTO;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
/**
* @author 六如
*/
@Service
public class SysRoleResourceService implements ServiceSupport<SysRoleResource, SysRoleResourceMapper> {
/**
* 保存角色菜单配置
*
* @param sysRoleResourceDTO 入参
* @return 返回影响行数
*/
@Transactional(rollbackFor = Exception.class)
public int saveRoleResource(SysRoleResourceDTO sysRoleResourceDTO) {
Long roleId = sysRoleResourceDTO.getRoleId();
// 删除之前的
this.deleteByColumn(SysRoleResource::getRoleId, roleId);
List<SysRoleResource> saveList = sysRoleResourceDTO.getResourceIds().stream()
.map(resourceId -> {
SysRoleResource sysRoleResource = new SysRoleResource();
sysRoleResource.setRoleId(roleId);
sysRoleResource.setResourceId(resourceId);
sysRoleResource.setAddBy(sysRoleResourceDTO.getAddBy());
sysRoleResource.setUpdateBy(sysRoleResourceDTO.getAddBy());
return sysRoleResource;
})
.collect(Collectors.toList());
return this.saveBatch(saveList);
}
public List<Long> listRoleResource(Long roleId) {
return this.query()
.eq(SysRoleResource::getRoleId, roleId)
.listUniqueValue(SysRoleResource::getResourceId);
}
public List<Long> listRoleResource(Collection<Long> roleIds) {
return this.query()
.in(SysRoleResource::getRoleId, roleIds)
.listUniqueValue(SysRoleResource::getResourceId);
}
}

View File

@@ -0,0 +1,32 @@
package com.gitee.sop.admin.service.sys;
import com.gitee.sop.admin.common.dto.StatusUpdateDTO;
import com.gitee.sop.admin.common.support.ServiceSupport;
import com.gitee.sop.admin.dao.entity.SysRole;
import com.gitee.sop.admin.dao.mapper.SysRoleMapper;
import org.springframework.stereotype.Service;
/**
* @author 六如
*/
@Service
public class SysRoleService implements ServiceSupport<SysRole, SysRoleMapper> {
/**
* 修改状态
*
* @param statusUpdateDTO 修改值
* @return 返回影响行数
*/
public int updateStatus(StatusUpdateDTO statusUpdateDTO) {
return this.query()
.eq(SysRole::getId, statusUpdateDTO.getId())
.set(SysRole::getStatus, statusUpdateDTO.getStatus())
.update();
}
}

View File

@@ -0,0 +1,116 @@
package com.gitee.sop.admin.service.sys;
import com.gitee.sop.admin.common.support.ServiceSupport;
import com.gitee.sop.admin.common.util.CopyUtil;
import com.gitee.sop.admin.dao.entity.SysDept;
import com.gitee.sop.admin.dao.entity.SysUserDept;
import com.gitee.sop.admin.dao.mapper.SysDeptMapper;
import com.gitee.sop.admin.dao.mapper.SysUserDeptMapper;
import com.gitee.sop.admin.service.sys.dto.DeptUserResultDTO;
import com.gitee.sop.admin.service.sys.dto.SysDeptDTO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* @author 六如
*/
@Service
public class SysUserDeptService implements ServiceSupport<SysUserDept, SysUserDeptMapper> {
@Autowired
SysDeptMapper sysDeptMapper;
public Map<Long, SysDeptDTO> getUserDeptId(Collection<Long> userIds) {
Map<Long, SysDept> deptIdMap = sysDeptMapper.query()
.map(SysDept::getId, Function.identity());
return this.query()
.in(SysUserDept::getUserId, userIds)
.map(SysUserDept::getUserId, sysUserDept -> {
SysDept sysDept = deptIdMap.get(sysUserDept.getDeptId());
return CopyUtil.copyBean(sysDept, SysDeptDTO::new);
}, (v1, v2) -> v2);
}
/**
* 查找部门及子部门下所有用户
*
* @param deptId 部门
* @return 返回空表示选中了根节点或没有选
*/
public DeptUserResultDTO listDeptUserIdsDeep(Long deptId) {
if (deptId == null) {
return new DeptUserResultDTO(false, null);
}
SysDept sysDept = sysDeptMapper.getById(deptId);
// 选中了根节点或没有选
if (sysDept == null || Objects.equals(sysDept.getParentId(), 0L)) {
return new DeptUserResultDTO(false, null);
}
List<Long> userIdsAll = new ArrayList<>();
List<Long> userIds = this.query()
.eq(SysUserDept::getDeptId, deptId)
.listUniqueValue(SysUserDept::getUserId);
appendChildren(userIdsAll, deptId);
userIdsAll.addAll(userIds);
return new DeptUserResultDTO(true, userIdsAll);
}
private void appendChildren(List<Long> userIdsAll, Long deptId) {
List<Long> deptIds = sysDeptMapper.query()
.eq(SysDept::getParentId, deptId)
.listUniqueValue(SysDept::getId);
if (deptIds.isEmpty()) {
return;
}
List<Long> userIds = this.query()
.in(SysUserDept::getDeptId, deptIds)
.listUniqueValue(SysUserDept::getUserId);
userIdsAll.addAll(userIds);
for (Long childDeptId : deptIds) {
appendChildren(userIdsAll, childDeptId);
}
}
/**
* 保存用户部门
*
* @param userId 用户id
* @param deptIds 部门id
* @return 返回影响行数
*/
public int saveUserDept(Long userId, List<Long> deptIds) {
this.query()
.eq(SysUserDept::getUserId, userId)
.delete();
if (CollectionUtils.isEmpty(deptIds)) {
return 0;
}
List<SysUserDept> saveList = deptIds.stream()
.map(deptId -> {
SysUserDept sysUserDept = new SysUserDept();
sysUserDept.setUserId(userId);
sysUserDept.setDeptId(deptId);
return sysUserDept;
})
.collect(Collectors.toList());
return this.saveBatch(saveList);
}
}

View File

@@ -0,0 +1,74 @@
package com.gitee.sop.admin.service.sys;
import com.gitee.sop.admin.common.support.ServiceSupport;
import com.gitee.sop.admin.common.user.User;
import com.gitee.sop.admin.common.util.CopyUtil;
import com.gitee.sop.admin.dao.entity.SysRole;
import com.gitee.sop.admin.dao.entity.SysUserRole;
import com.gitee.sop.admin.dao.mapper.SysRoleMapper;
import com.gitee.sop.admin.dao.mapper.SysUserRoleMapper;
import com.gitee.sop.admin.service.sys.dto.SysRoleDTO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.stream.Collectors;
/**
* @author 六如
*/
@Service
public class SysUserRoleService implements ServiceSupport<SysUserRole, SysUserRoleMapper> {
@Autowired
private SysRoleMapper sysRoleMapper;
/**
* 设置用户角色
*
* @param userId 用户id
* @param roleIds 角色id
* @param operator 操作人
*/
public void setUserRole(Long userId, List<Long> roleIds, User operator) {
this.query()
.eq(SysUserRole::getUserId, userId)
.delete();
if (CollectionUtils.isEmpty(roleIds)) {
return;
}
List<SysUserRole> saveList = new LinkedHashSet<>(roleIds).stream()
.map(roleId -> {
SysUserRole sysUserRole = new SysUserRole();
sysUserRole.setRoleId(roleId);
sysUserRole.setUserId(userId);
sysUserRole.setAddBy(operator.getUserId());
return sysUserRole;
})
.collect(Collectors.toList());
this.saveBatch(saveList);
}
public List<Long> listUserRoleIds(Long userId) {
return this.query()
.eq(SysUserRole::getUserId, userId)
.listUniqueValue(SysUserRole::getRoleId);
}
public List<SysRoleDTO> listUserRoles(Long userId) {
List<Long> roleIds = this.listUserRoleIds(userId);
if (roleIds.isEmpty()) {
return new ArrayList<>(0);
}
return sysRoleMapper.query()
.in(SysRole::getId, roleIds)
.list(sysRole -> CopyUtil.copyBean(sysRole, SysRoleDTO::new));
}
}

View File

@@ -0,0 +1,180 @@
package com.gitee.sop.admin.service.sys;
import com.gitee.fastmybatis.core.PageInfo;
import com.gitee.fastmybatis.core.query.LambdaQuery;
import com.gitee.sop.admin.common.config.Configs;
import com.gitee.sop.admin.common.dto.StatusUpdateBatchDTO;
import com.gitee.sop.admin.common.dto.StatusUpdateDTO;
import com.gitee.sop.admin.common.dto.UserDTO;
import com.gitee.sop.admin.common.enums.ConfigKeyEnum;
import com.gitee.sop.admin.common.support.ServiceSupport;
import com.gitee.sop.admin.common.util.CopyUtil;
import com.gitee.sop.admin.common.util.GenerateUtil;
import com.gitee.sop.admin.dao.entity.SysUser;
import com.gitee.sop.admin.dao.mapper.SysUserMapper;
import com.gitee.sop.admin.service.sys.dto.DeptUserResultDTO;
import com.gitee.sop.admin.service.sys.dto.SysDeptDTO;
import com.gitee.sop.admin.service.sys.dto.SysUserAddDTO;
import com.gitee.sop.admin.service.sys.dto.SysUserDTO;
import com.gitee.sop.admin.service.sys.dto.SysUserSearchDTO;
import com.gitee.sop.admin.service.sys.dto.SysUserUpdateDTO;
import com.gitee.sop.admin.service.sys.login.enums.RegTypeEnum;
import com.google.common.collect.Lists;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCrypt;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
/**
* @author 六如
*/
@Service
public class SysUserService implements ServiceSupport<SysUser, SysUserMapper> {
@Autowired
SysUserDeptService sysUserDeptService;
public <T extends UserDTO> void fillShowName(Collection<T> userDTOS) {
if (CollectionUtils.isEmpty(userDTOS)) {
return;
}
Set<Long> userIds = userDTOS.stream().flatMap(docInfo -> Lists.newArrayList(docInfo.getAddBy(), docInfo.getUpdateBy()).stream())
.filter(val -> val != null && val > 0)
.collect(Collectors.toSet());
Map<Long, SysUserDTO> userIdMap = this.getUserInfo(userIds);
for (T userDTO : userDTOS) {
SysUserDTO addUser = userIdMap.get(userDTO.getAddBy());
if (addUser != null) {
userDTO.setAddByName(addUser.getShowName());
}
SysUserDTO updateUser = userIdMap.get(userDTO.getUpdateBy());
if (updateUser != null) {
userDTO.setUpdateByName(updateUser.getShowName());
}
}
}
public Map<Long, SysUserDTO> getUserInfo(Collection<Long> userIds) {
if (CollectionUtils.isEmpty(userIds)) {
return Collections.emptyMap();
}
return this.query()
.in(SysUser::getId, userIds)
.map(SysUser::getId, sysUser -> CopyUtil.copyBean(sysUser, SysUserDTO::new));
}
public PageInfo<SysUserDTO> doPage(SysUserSearchDTO sysUserSearchDTO) {
LambdaQuery<SysUser> query = sysUserSearchDTO.toLambdaQuery(SysUser.class);
Long deptId = sysUserSearchDTO.getDeptId();
DeptUserResultDTO deptUserResultDTO = sysUserDeptService.listDeptUserIdsDeep(deptId);
if (deptUserResultDTO.getSelect()) {
List<Long> userIds = deptUserResultDTO.getUserIds();
// 查不到返回空
if (CollectionUtils.isEmpty(userIds)) {
return new PageInfo<>();
}
query.in(SysUser::getId, userIds);
}
query.orderByDesc(SysUser::getId);
// 格式转换
return this.pageAndConvert(query, records -> {
if (records.isEmpty()) {
return Collections.emptyList();
}
Set<Long> userIds = records.stream().map(SysUser::getId).collect(Collectors.toSet());
Map<Long, SysDeptDTO> userIdDeptMap = sysUserDeptService.getUserDeptId(userIds);
return CopyUtil.copyList(records, SysUserDTO::new, after -> {
SysDeptDTO sysDeptDTO = userIdDeptMap.get(after.getId());
after.setDept(sysDeptDTO);
});
});
}
public Long addUser(SysUserAddDTO sysUserAddDTO) {
SysUser sysUser = CopyUtil.copyBean(sysUserAddDTO, SysUser::new);
String encodedPassword = BCrypt.hashpw(sysUserAddDTO.getPassword(), BCrypt.gensalt());
sysUser.setPassword(encodedPassword);
sysUser.setRegType(RegTypeEnum.BACKEND.getValue());
this.save(sysUser);
// 添加部门
Long deptId = sysUserAddDTO.getParentId();
if (deptId != null && deptId > 0) {
sysUserDeptService.saveUserDept(sysUser.getId(), Lists.newArrayList(deptId));
}
return sysUser.getId();
}
public int updateUser(SysUserUpdateDTO sysUserUpdateDTO) {
SysUser sysUser = CopyUtil.copyBean(sysUserUpdateDTO, SysUser::new);
int cnt = this.update(sysUser);
// 添加部门
Long deptId = sysUserUpdateDTO.getParentId();
if (deptId != null && deptId > 0) {
sysUserDeptService.saveUserDept(sysUser.getId(), Lists.newArrayList(deptId));
}
return cnt;
}
/**
* 修改状态
*
* @param statusUpdateDTO 修改值
* @return 返回影响行数
*/
public int updateStatus(StatusUpdateDTO statusUpdateDTO) {
return this.query()
.eq(SysUser::getId, statusUpdateDTO.getId())
.set(SysUser::getStatus, statusUpdateDTO.getStatus())
.update();
}
public int updateStatus(StatusUpdateBatchDTO statusUpdateDTO) {
return this.query()
.in(SysUser::getId, statusUpdateDTO.getIds())
.set(SysUser::getStatus, statusUpdateDTO.getStatus())
.update();
}
public void resetUserPassword(Long userId, String passwordHash) {
String encodedPassword = BCrypt.hashpw(passwordHash, BCrypt.gensalt());
this.query()
.eq(SysUser::getId, userId)
.set(SysUser::getPassword, encodedPassword)
.update();
}
public SysUser getByUsername(String username) {
return this.get(SysUser::getUsername, username);
}
public String getDbPassword(String username, String password) {
return getDbPassword(username, password, getPasswordSalt());
}
public String getDbPassword(String username) {
return this.query().eq(SysUser::getUsername, username).getValue(SysUser::getPassword);
}
public String getDbPassword(String username, String password, String salt) {
return GenerateUtil.getUserPassword(username, password, salt);
}
private String getPasswordSalt() {
return Configs.getValue(ConfigKeyEnum.PASSWORD_SALT);
}
}

View File

@@ -0,0 +1,67 @@
package com.gitee.sop.admin.service.sys;
import com.gitee.sop.admin.common.enums.ConfigKeyEnum;
import com.gitee.sop.admin.common.util.PasswordUtil;
import com.gitee.sop.admin.dao.entity.SysUser;
import com.gitee.sop.admin.dao.mapper.UpgradeMapper;
import com.gitee.sop.admin.service.sys.login.enums.RegTypeEnum;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCrypt;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
/**
* @author 六如
*/
@Service
public class UpgradeService {
@Autowired
private SysConfigService sysConfigService;
@Autowired
private SysUserService sysUserService;
@Autowired
private UpgradeMapper upgradeMapper;
@PostConstruct
public void init() {
this.initJwtSecret();
this.insertAdmin();
}
private void initJwtSecret() {
String configKey = ConfigKeyEnum.JWT_SECRET.getKey();
String value = sysConfigService.getRawValue(configKey);
if (StringUtils.isBlank(value)) {
value = PasswordUtil.getRandomSimplePassword(30);
sysConfigService.setConfig(configKey, value);
}
}
public void insertAdmin() {
SysUser userInfo = sysUserService.getByUsername("admin");
if (userInfo != null) {
return;
}
String username = "admin";
String tpl = "INSERT INTO `sys_user` ( `username`, `password`, `nickname`, `reg_type`) VALUES \n" +
"\t('%s','%s','%s','%s');";
// 初始密码
String defPassword = "123456";
defPassword = DigestUtils.sha256Hex(defPassword);
String encodedPassword = BCrypt.hashpw(defPassword, BCrypt.gensalt());
String sql = String.format(tpl, username, encodedPassword, username, RegTypeEnum.BACKEND.getValue());
runSql(sql);
}
protected void runSql(String sql) {
upgradeMapper.runSql(sql);
}
}

View File

@@ -0,0 +1,14 @@
package com.gitee.sop.admin.service.sys;
import com.gitee.sop.admin.common.user.User;
import java.util.Optional;
/**
* @author 六如
*/
public interface UserCacheService {
Optional<User> getUser(Long userId);
}

View File

@@ -0,0 +1,99 @@
package com.gitee.sop.admin.service.sys;
import com.gitee.fastmybatis.core.util.TreeUtil;
import com.gitee.sop.admin.common.enums.MenuTypeEnum;
import com.gitee.sop.admin.common.util.CopyUtil;
import com.gitee.sop.admin.dao.entity.SysResource;
import com.gitee.sop.admin.dao.entity.SysRoleResource;
import com.gitee.sop.admin.service.sys.dto.MenuTreeDTO;
import com.gitee.sop.admin.service.sys.dto.MenuTreeDTO.Meta;
import com.gitee.sop.admin.service.sys.dto.SysRoleDTO;
import com.gitee.sop.admin.service.sys.dto.UserPermissionDTO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
/**
* @author 六如
*/
@Service
public class UserPermissionService {
@Autowired
private SysResourceService sysResourceService;
@Autowired
private SysRoleResourceService sysRoleResourceService;
@Autowired
private SysUserRoleService sysUserRoleService;
public UserPermissionDTO getUserPermission(Long userId) {
List<SysRoleDTO> sysRoleDTOS = sysUserRoleService.listUserRoles(userId);
if (sysRoleDTOS.isEmpty()) {
return UserPermissionDTO.empty();
}
Set<Long> roleIds = sysRoleDTOS.stream().map(SysRoleDTO::getId).collect(Collectors.toSet());
List<String> roleCodes = sysRoleDTOS.stream().map(SysRoleDTO::getCode).distinct().collect(Collectors.toList());
// 角色对应的资源
List<Long> resourceIds = sysRoleResourceService.query()
.in(SysRoleResource::getRoleId, roleIds)
.listUniqueValue(SysRoleResource::getResourceId);
// 获取按钮权限
List<String> permissions = sysResourceService.query()
.eq(SysResource::getMenuType, MenuTypeEnum.BUTTON.getValue())
.in(SysResource::getId, resourceIds)
.listUniqueValue(SysResource::getAuths);
UserPermissionDTO userPermissionDTO = new UserPermissionDTO();
userPermissionDTO.setRoles(roleCodes);
userPermissionDTO.setPermissions(permissions);
return userPermissionDTO;
}
/**
* 获取用户菜单
*
* @param userId 用户id
* @return 返回菜单树
*/
public List<MenuTreeDTO> listUserMenuTree(Long userId) {
List<Long> roleIds = sysUserRoleService.listUserRoleIds(userId);
if (roleIds.isEmpty()) {
return Collections.emptyList();
}
List<Long> resourceIds = sysRoleResourceService.listRoleResource(roleIds);
if (resourceIds.isEmpty()) {
return Collections.emptyList();
}
List<SysResource> list = sysResourceService.query()
.in(SysResource::getId, resourceIds)
.eq(SysResource::getMenuType, MenuTypeEnum.MENU)
.list();
return convert(list);
}
private List<MenuTreeDTO> convert(List<SysResource> list) {
List<MenuTreeDTO> menuTreeDTOS = list.stream()
.map(sysResource -> {
MenuTreeDTO menuTreeDTO = CopyUtil.copyBean(sysResource, MenuTreeDTO::new);
Meta meta = CopyUtil.copyBean(sysResource, Meta::new);
menuTreeDTO.setMeta(meta);
return menuTreeDTO;
})
.collect(Collectors.toList());
return TreeUtil.convertTree(menuTreeDTOS, 0L);
}
}

View File

@@ -0,0 +1,19 @@
package com.gitee.sop.admin.service.sys.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import java.util.List;
/**
* @author 六如
*/
@Data
@AllArgsConstructor
public class DeptUserResultDTO {
private Boolean select = true;
private List<Long> userIds;
}

View File

@@ -0,0 +1,130 @@
package com.gitee.sop.admin.service.sys.dto;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.gitee.fastmybatis.core.support.TreeNode;
import com.gitee.sop.admin.common.jackson.convert.annotation.Bool;
import lombok.Data;
import java.util.List;
/**
* 菜单资源
*
* @author 六如
* @see <a href="https://pure-admin.cn/pages/routerMenu/#%E8%B7%AF%E7%94%B1%E5%92%8C%E8%8F%9C%E5%8D%95%E9%85%8D%E7%BD%AE">...</a>
*/
@Data
@JsonInclude(JsonInclude.Include.NON_EMPTY)
public class MenuTreeDTO implements TreeNode<MenuTreeDTO, Long> {
/**
* id
*/
private Long id;
/**
* 路由路径
*/
private String path;
/**
* 路由名称(必须保持唯一)
*/
private String name;
/**
* 路由重定向(默认跳转地址)
*/
private String redirect;
/**
* 父级id
*/
private Long parentId;
/**
* 路由元信息
*/
private Meta meta;
private List<MenuTreeDTO> children;
@Override
public Long takeId() {
return id;
}
@Override
public Long takeParentId() {
return parentId;
}
/**
* 路由元信息,通俗来说就是路由上的额外信息 https://router.vuejs.org/zh/guide/advanced/meta.html#%E8%B7%AF%E7%94%B1%E5%85%83%E4%BF%A1%E6%81%AF
*/
@Data
@JsonInclude(JsonInclude.Include.NON_EMPTY)
public static class Meta {
/**
* 菜单名称兼容国际化、非国际化如果用国际化的写法就必须在根目录的locales文件夹下对应添加
*/
private String title;
/**
* 菜单图标
*/
private String icon;
/**
* 右侧图标
*/
private String extraIcon;
/**
* 菜单(是否显示该菜单)
*/
@Bool
private Integer showLink;
/**
* 父级菜单(是否显示父级菜单
*/
@Bool
private Integer showParent;
/**
* 链接地址(需要内嵌的`iframe`链接地址)
*/
private String frameSrc;
/**
* 加载动画(内嵌的`iframe`页面是否开启首次加载动画)
*/
@Bool
private Integer frameLoading;
/**
* 缓存页面
*/
@Bool
private Integer keepAlive;
/**
* 标签页(当前菜单名称或自定义信息禁止添加到标签页)
*/
@Bool
private Integer hiddenTag;
/**
* 菜单激活
*/
private String activePath;
/**
* 排序
*/
private Integer rank;
}
}

View File

@@ -0,0 +1,81 @@
package com.gitee.sop.admin.service.sys.dto;
import com.gitee.fastmybatis.core.support.TreeNode;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
/**
* 备注:部门表
*
* @author 六如
*/
@Data
public class SysDeptDTO implements TreeNode<SysDeptDTO, Long> {
/**
* id
*/
private Long id;
/**
* 部门名称
*/
private String name;
/**
* 排序
*/
private Integer sort;
/**
* 状态1启用2禁用
*/
private Integer status;
/**
* 备注
*/
private String remark;
/**
* 父级id
*/
private Long parentId;
/**
* 添加时间
*/
private LocalDateTime addTime;
/**
* 修改时间
*/
private LocalDateTime updateTime;
/**
* 创建人id
*/
private Long addBy;
/**
* 修改人id
*/
private Long updateBy;
private List<SysDeptDTO> children;
@Override
public Long takeId() {
return id;
}
@Override
public Long takeParentId() {
return parentId;
}
}

View File

@@ -0,0 +1,29 @@
package com.gitee.sop.admin.service.sys.dto;
import lombok.Data;
/**
* @author 六如
*/
@Data
public class SysRoleDTO {
/**
* id
*/
private Long id;
/**
* 角色名称
*/
private String name;
/**
* 角色code
*/
private String code;
/**
* 备注
*/
private String remark;
}

View File

@@ -0,0 +1,28 @@
package com.gitee.sop.admin.service.sys.dto;
import lombok.Data;
import java.util.List;
/**
* @author 六如
*/
@Data
public class SysRoleResourceDTO {
/**
* 角色id
*/
private Long roleId;
/**
* 资源id
*/
private List<Long> resourceIds;
/**
* 添加人
*/
private Long addBy;
}

View File

@@ -0,0 +1,88 @@
package com.gitee.sop.admin.service.sys.dto;
import lombok.Data;
import java.time.LocalDateTime;
/**
* @author 六如
*/
@Data
public class SysUserAddDTO {
/**
* 用户名
*/
private String username;
/**
* 密码
*/
private String password;
/**
* 昵称
*/
private String nickname;
/**
* 邮箱
*/
private String phone;
/**
* 邮箱
*/
private String email;
/**
* 头像
*/
private String avatar;
/**
* 性别,0-未知,1-男,2-女
*/
private Integer gender;
/**
* 状态1启用2禁用
*/
private Integer status;
/**
* 注册类型1-系统2-手动
*/
private String regType;
/**
* 备注
*/
private String remark;
/**
* 添加时间
*/
private LocalDateTime addTime;
/**
* 修改时间
*/
private LocalDateTime updateTime;
/**
* 创建人id
*/
private Long addBy;
/**
* 修改人id
*/
private Long updateBy;
/**
* 部门id
*/
private Long parentId;
}

View File

@@ -0,0 +1,109 @@
package com.gitee.sop.admin.service.sys.dto;
import com.gitee.sop.admin.common.user.User;
import lombok.Data;
import java.time.LocalDateTime;
/**
*
* @author 六如
*/
@Data
public class SysUserDTO implements User {
/**
* id
*/
private Long id;
/**
* 用户名
*/
private String username;
/**
* 密码
*/
private String password;
/**
* 昵称
*/
private String nickname;
/**
* 邮箱
*/
private String phone;
/**
* 邮箱
*/
private String email;
/**
* 头像
*/
private String avatar;
/**
* 性别,0-未知,1-男,2-女
*/
private Integer gender;
/**
* 状态1启用2禁用
*/
private Integer status;
/**
* 注册类型1-系统2-手动
*/
private String regType;
/**
* 备注
*/
private String remark;
/**
* 添加时间
*/
private LocalDateTime addTime;
/**
* 修改时间
*/
private LocalDateTime updateTime;
/**
* 创建人id
*/
private Long addBy;
/**
* 修改人id
*/
private Long updateBy;
/**
* 部门
*/
private SysDeptDTO dept;
@Override
public Long getUserId() {
return id;
}
@Override
public String getToken() {
return "";
}
public String getShowName() {
return nickname != null && !nickname.isEmpty() ? nickname : username;
}
}

View File

@@ -0,0 +1,42 @@
package com.gitee.sop.admin.service.sys.dto;
import com.gitee.fastmybatis.core.query.Operator;
import com.gitee.fastmybatis.core.query.annotation.Condition;
import com.gitee.fastmybatis.core.query.param.PageParam;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 备注:角色表
*
* @author 六如
*/
@EqualsAndHashCode(callSuper = true)
@Data
public class SysUserSearchDTO extends PageParam {
private static final long serialVersionUID = 7794265174728302379L;
/**
* 用户名
*/
@Condition(operator = Operator.like)
private String username;
/**
* 邮箱
*/
@Condition(operator = Operator.like)
private String phone;
/**
* 状态1启用2禁用
*/
@Condition
private Integer status;
@Condition(ignore = true)
private Long deptId;
}

View File

@@ -0,0 +1,75 @@
package com.gitee.sop.admin.service.sys.dto;
import lombok.Data;
import javax.validation.constraints.NotNull;
/**
* @author 六如
*/
@Data
public class SysUserUpdateDTO {
/**
* id
*/
@NotNull(message = "id必填")
private Long id;
/**
* 用户名
*/
private String username;
/**
* 昵称
*/
private String nickname;
/**
* 邮箱
*/
private String phone;
/**
* 邮箱
*/
private String email;
/**
* 头像
*/
private String avatar;
/**
* 性别,0-未知,1-男,2-女
*/
private Integer gender;
/**
* 状态1启用2禁用
*/
private Integer status;
/**
* 注册类型1-系统2-手动
*/
private String regType;
/**
* 备注
*/
private String remark;
/**
* 修改人id
*/
private Long updateBy;
/**
* 部门id
*/
private Long parentId;
}

View File

@@ -0,0 +1,19 @@
package com.gitee.sop.admin.service.sys.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author tanghc
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class SystemConfigDTO {
private String configKey;
private String configValue;
private String remark;
}

View File

@@ -0,0 +1,32 @@
package com.gitee.sop.admin.service.sys.dto;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
/**
* @author 六如
*/
@Data
public class UserPermissionDTO {
/**
* 用户角色code
*/
private List<String> roles;
/**
* 用户按钮级别code
*/
private List<String> permissions;
public static UserPermissionDTO empty() {
UserPermissionDTO userPermissionDTO = new UserPermissionDTO();
userPermissionDTO.setRoles(new ArrayList<>());
userPermissionDTO.setPermissions(new ArrayList<>());
return userPermissionDTO;
}
}

View File

@@ -0,0 +1,20 @@
package com.gitee.sop.admin.service.sys.dto;
import lombok.Data;
import java.util.List;
/**
* 用户角色信息
* @author 六如
*/
@Data
public class UserRoleInfoDTO {
private Long userId;
private List<Long> roleIds;
private List<String> roleCodes;
}

View File

@@ -0,0 +1,56 @@
package com.gitee.sop.admin.service.sys.impl;
import com.gitee.sop.admin.common.user.User;
import com.gitee.sop.admin.common.util.CopyUtil;
import com.gitee.sop.admin.dao.entity.SysUser;
import com.gitee.sop.admin.service.sys.SysUserService;
import com.gitee.sop.admin.service.sys.UserCacheService;
import com.gitee.sop.admin.service.sys.dto.SysUserDTO;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import org.springframework.beans.factory.annotation.Autowired;
import javax.annotation.PostConstruct;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
/**
* @author 六如
*/
public class LocalUserCacheService implements UserCacheService {
@Autowired
private SysUserService sysUserService;
// key: configKey, value: configValue
private final LoadingCache<Long, Optional<User>> configCache = CacheBuilder.newBuilder()
.expireAfterAccess(30, TimeUnit.MINUTES)
.build(new CacheLoader<Long, Optional<User>>() {
@Override
public Optional<User> load(Long key) throws Exception {
return Optional.ofNullable(loadFromDb(key));
}
});
@Override
public Optional<User> getUser(Long userId) {
return configCache.getUnchecked(userId);
}
private User loadFromDb(Long userId) {
SysUser sysUser = sysUserService.getById(userId);
return CopyUtil.copyBean(sysUser, SysUserDTO::new);
}
@PostConstruct
public void init() {
List<SysUser> sysUsers = sysUserService.listAll();
List<SysUserDTO> sysUserDTOS = CopyUtil.copyList(sysUsers, SysUserDTO::new);
for (SysUserDTO sysUserDTO : sysUserDTOS) {
configCache.put(sysUserDTO.getUserId(), Optional.of(sysUserDTO));
}
}
}

View File

@@ -0,0 +1,148 @@
package com.gitee.sop.admin.service.sys.login;
import com.gitee.sop.admin.common.config.Configs;
import com.gitee.sop.admin.common.enums.ConfigKeyEnum;
import com.gitee.sop.admin.common.enums.StatusEnum;
import com.gitee.sop.admin.common.exception.BizException;
import com.gitee.sop.admin.common.manager.UserCacheManager;
import com.gitee.sop.admin.common.util.CopyUtil;
import com.gitee.sop.admin.common.util.DateUtil;
import com.gitee.sop.admin.common.util.GenerateUtil;
import com.gitee.sop.admin.common.util.JwtUtil;
import com.gitee.sop.admin.dao.entity.SysUser;
import com.gitee.sop.admin.service.sys.SysUserService;
import com.gitee.sop.admin.service.sys.UserPermissionService;
import com.gitee.sop.admin.service.sys.dto.UserPermissionDTO;
import com.gitee.sop.admin.service.sys.login.dto.LoginDTO;
import com.gitee.sop.admin.service.sys.login.dto.LoginForm;
import com.gitee.sop.admin.service.sys.login.dto.LoginResult;
import com.gitee.sop.admin.service.sys.login.dto.LoginUser;
import com.gitee.sop.admin.service.sys.login.enums.RegTypeEnum;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.math.NumberUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCrypt;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
/**
* @author 六如
*/
@Service
@Slf4j
public class LoginService {
@Autowired
private SysUserService sysUserService;
@Autowired
private UserCacheManager userCacheManager;
@Autowired
private UserPermissionService userPermissionService;
public LoginUser login(LoginDTO loginDTO) {
String username = loginDTO.getUsername();
String password = loginDTO.getPassword();
RegTypeEnum regType = loginDTO.getRegType();
SysUser userInfo;
switch (regType) {
case FORM:
throw new UnsupportedOperationException("第三方登录暂未支持");
case LDAP:
// LDAP登录
throw new UnsupportedOperationException("LDAP登录登录暂未支持");
default:
// 默认注册账号登录
userInfo = this.doDatabaseLogin(username, password);
}
LoginUser loginUser = buildLoginUser(userInfo);
// 保存到缓存
userCacheManager.saveUser(loginUser);
return loginUser;
}
private LoginUser buildLoginUser(SysUser sysUser) {
if (StatusEnum.of(sysUser.getStatus()) == StatusEnum.DISABLED) {
throw new BizException("账号已禁用,请联系管理员");
}
// 登录成功
LoginUser loginUser = CopyUtil.copyBean(sysUser, LoginUser::new);
// 创建token
String token = this.createToken(sysUser.getId());
loginUser.setAccessToken(token);
UserPermissionDTO userPermission = userPermissionService.getUserPermission(loginUser.getUserId());
// 角色权限
loginUser.setRoles(userPermission.getRoles());
loginUser.setPermissions(userPermission.getPermissions());
// 设置token过期时间
String value = Configs.getValue(ConfigKeyEnum.JWT_TIMEOUT_DAYS);
LocalDateTime expireDate = LocalDateTime.now().plusDays(NumberUtils.toInt(value));
loginUser.setExpires(DateUtil.formatFrontDate(expireDate));
return loginUser;
}
private String createToken(long userId) {
Map<String, String> data = new HashMap<>(4);
data.put("id", String.valueOf(userId));
String value = Configs.getValue(ConfigKeyEnum.JWT_TIMEOUT_DAYS);
return JwtUtil.createJwt(data, NumberUtils.toInt(value), getJwtSecret());
}
public static String getJwtSecret() {
return Configs.getValue(ConfigKeyEnum.JWT_SECRET);
}
private SysUser doThirdPartyLogin(ThirdPartyLoginManager thirdPartyLoginManager, String username, String password) {
LoginForm loginForm = new LoginForm();
loginForm.setUsername(username);
loginForm.setPassword(password);
LoginResult loginResult;
try {
loginResult = thirdPartyLoginManager.login(loginForm);
} catch (Exception e) {
log.error("第三方登录失败", e);
throw new BizException(e.getMessage());
}
SysUser userInfo = sysUserService.getByUsername(username);
// 用户第一次登录则插入到user_info表
if (userInfo == null) {
userInfo = new SysUser();
userInfo.setUsername(username);
userInfo.setPassword(GenerateUtil.getUUID());
userInfo.setNickname(loginResult.getNickname());
userInfo.setAvatar("");
userInfo.setStatus(StatusEnum.ENABLE.getValue());
userInfo.setRegType(loginResult.getRegTypeEnum().getValue());
userInfo.setEmail(loginResult.getEmail());
sysUserService.save(userInfo);
} else {
String email = loginResult.getEmail();
// 如果更改了邮箱
if (StringUtils.hasText(email) && !Objects.equals(email, userInfo.getEmail())) {
userInfo.setEmail(email);
sysUserService.update(userInfo);
}
}
return userInfo;
}
private SysUser doDatabaseLogin(String username, String password) {
SysUser sysUser = sysUserService.getByUsername(username);
Assert.notNull(sysUser, () -> "用户名密码不正确");
String encodedPasswordDb = sysUser.getPassword();
// 校验
boolean flag = BCrypt.checkpw(password, encodedPasswordDb);
Assert.isTrue(flag, () -> "用户名密码不正确");
Assert.isTrue(Objects.equals(sysUser.getStatus(), StatusEnum.ENABLE.getValue()), () -> "用户已禁用");
return sysUser;
}
}

View File

@@ -0,0 +1,40 @@
package com.gitee.sop.admin.service.sys.login;
/**
* @author thc
*/
public class NotNullStringBuilder {
private final StringBuilder stringBuilder = new StringBuilder();
public NotNullStringBuilder append(Number o) {
stringBuilder.append(formatValue(o));
return this;
}
public NotNullStringBuilder append(String o) {
stringBuilder.append(formatValue(o));
return this;
}
public NotNullStringBuilder append(String o, String defaultValue) {
if (o == null || o.length() == 0) {
o = defaultValue;
}
stringBuilder.append(formatValue(o));
return this;
}
private static String formatValue(Number o) {
return o == null ? "0" : String.valueOf(o);
}
private static String formatValue(String o) {
return o == null ? "" : o;
}
@Override
public String toString() {
return stringBuilder.toString();
}
}

View File

@@ -0,0 +1,17 @@
package com.gitee.sop.admin.service.sys.login;
import com.gitee.sop.admin.service.sys.login.dto.LoginForm;
import com.gitee.sop.admin.service.sys.login.dto.LoginResult;
/**
* @author 六如
*/
public interface ThirdPartyLoginManager {
/**
* 第三方登录
* @param loginForm 登录表单
* @return 返回登录结果
*/
LoginResult login(LoginForm loginForm) throws Exception;
}

View File

@@ -0,0 +1,11 @@
package com.gitee.sop.admin.service.sys.login.dto;
import com.gitee.sop.admin.service.sys.login.enums.RegTypeEnum;
import lombok.Data;
@Data
public class LoginDTO {
private String username;
private String password;
private RegTypeEnum regType = RegTypeEnum.BACKEND;
}

View File

@@ -0,0 +1,12 @@
package com.gitee.sop.admin.service.sys.login.dto;
import lombok.Data;
/**
* @author 六如
*/
@Data
public class LoginForm {
private String username;
private String password;
}

View File

@@ -0,0 +1,20 @@
package com.gitee.sop.admin.service.sys.login.dto;
import com.gitee.sop.admin.service.sys.login.enums.RegTypeEnum;
import lombok.Data;
/**
* @author 六如
*/
@Data
public class LoginResult {
private String username;
private String nickname;
private String email;
private RegTypeEnum regTypeEnum = RegTypeEnum.FORM;
}

View File

@@ -0,0 +1,83 @@
package com.gitee.sop.admin.service.sys.login.dto;
import com.gitee.sop.admin.common.user.User;
import lombok.Data;
import java.util.List;
/**
* @author 六如
*/
@Data
public class LoginUser implements User {
/*
avatar: "https://avatars.githubusercontent.com/u/44761321",
username: "admin",
nickname: "小铭",
// 一个用户可能有多个角色
roles: ["admin"],
// 按钮级别权限
permissions: ["*:*:*"],
accessToken: "eyJhbGciOiJIUzUxMiJ9.admin",
refreshToken: "eyJhbGciOiJIUzUxMiJ9.adminRefresh",
expires: "2030/10/30 00:00:00"
*/
private Long id;
/**
* 用户名
*/
private String username;
/**
* 用户名
*/
private String nickname;
/**
* 密码
*/
private String password;
/**
* 头像
*/
private String avatar;
/**
* 注册类型
*/
private Integer regType;
/**
* 状态1启用0禁用
*/
private Integer status;
private List<String> roles;
private List<String> permissions;
private String accessToken;
private String refreshToken;
private String expires;
@Override
public Long getUserId() {
return id;
}
@Override
public Integer getStatus() {
return status;
}
@Override
public String getToken() {
return accessToken;
}
}

View File

@@ -0,0 +1,29 @@
package com.gitee.sop.admin.service.sys.login.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Objects;
/**
* @author 六如
*/
@AllArgsConstructor
@Getter
public enum RegTypeEnum {
BACKEND("backend"),
FORM("form"),
OAUTH("oauth"),
LDAP("ldap");
public static RegTypeEnum of(String source) {
for (RegTypeEnum value : RegTypeEnum.values()) {
if (Objects.equals(source, value.value)) {
return value;
}
}
return RegTypeEnum.BACKEND;
}
private final String value;
}

View File

@@ -0,0 +1,90 @@
package com.gitee.sop.admin.service.sys.login.impl;
import com.gitee.sop.admin.common.enums.StatusEnum;
import com.gitee.sop.admin.common.manager.UserCacheManager;
import com.gitee.sop.admin.common.user.User;
import com.gitee.sop.admin.common.util.CopyUtil;
import com.gitee.sop.admin.dao.entity.SysUser;
import com.gitee.sop.admin.service.sys.SysUserService;
import com.gitee.sop.admin.service.sys.login.dto.LoginUser;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
/**
* @author 六如
*/
@Service
@Slf4j
public class DefaultUserCacheManager implements UserCacheManager, InitializingBean {
@Autowired
private SysUserService sysUserService;
@Value("${admin.user-cache-timeout-minutes:15}")
private int timeoutMinutes;
// key: userId
private LoadingCache<Long, Optional<User>> userCache;
@Override
public User getUser(long userId) {
return userCache.getUnchecked(userId).orElse(null);
}
@Override
public void saveUser(User user) {
if (user == null) {
return;
}
userCache.put(user.getUserId(), Optional.of(user));
}
private LoadingCache<Long, Optional<User>> buildCache(int timeout) {
if (timeout <= 0) {
throw new IllegalArgumentException("timeout must be gt 0");
}
CacheBuilder<Object, Object> cacheBuilder = CacheBuilder.newBuilder();
cacheBuilder.expireAfterAccess(timeout, TimeUnit.MINUTES);
return cacheBuilder
.build(new CacheLoader<Long, Optional<User>>() {
@Override
public Optional<User> load(Long id) throws Exception {
User user = getLoginUser(id);
return Optional.ofNullable(user);
}
});
}
/**
* 获取登陆用户
* @param id
* @return
*/
private User getLoginUser(long id) {
SysUser userInfo = sysUserService.getById(id);
if (userInfo == null) {
log.warn("登录用户不存在userId{}", id);
return null;
}
if (userInfo.getStatus() == StatusEnum.DISABLED.getValue()) {
log.warn("用户被禁用, userId:{}, username:{}, nickname:{}", userInfo.getId(), userInfo.getUsername(), userInfo.getNickname());
return null;
}
return CopyUtil.copyBean(userInfo, LoginUser::new);
}
@Override
public void afterPropertiesSet() throws Exception {
userCache = buildCache(timeoutMinutes);
}
}

View File

@@ -0,0 +1,64 @@
package com.gitee.sop.admin.service.website;
import com.gitee.fastmybatis.core.util.TreeUtil;
import com.gitee.sop.admin.common.constants.YesOrNo;
import com.gitee.sop.admin.common.util.CopyUtil;
import com.gitee.sop.admin.dao.entity.DocApp;
import com.gitee.sop.admin.dao.entity.DocInfo;
import com.gitee.sop.admin.service.doc.DocAppService;
import com.gitee.sop.admin.service.doc.DocInfoService;
import com.gitee.sop.admin.service.doc.DocSettingService;
import com.gitee.sop.admin.service.doc.dto.DocAppDTO;
import com.gitee.sop.admin.service.doc.dto.DocInfoConfigDTO;
import com.gitee.sop.admin.service.doc.dto.DocInfoTreeDTO;
import com.gitee.sop.admin.service.doc.dto.DocInfoViewDTO;
import com.gitee.sop.admin.service.doc.dto.DocSettingDTO;
import com.gitee.sop.admin.service.doc.dto.torna.TornaDocInfoViewDTO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* @author 六如
*/
@Service
public class WebsiteService {
@Autowired
private DocAppService docAppService;
@Autowired
private DocInfoService docInfoService;
@Autowired
private DocSettingService docSettingService;
public List<DocAppDTO> listDocApp() {
List<DocApp> docApps = docAppService.list(DocApp::getIsPublish, YesOrNo.YES);
return CopyUtil.copyList(docApps, DocAppDTO::new);
}
public List<DocInfoTreeDTO> listDocMenuTree(Long docAppId) {
List<DocInfo> list = docInfoService.query()
.eq(DocInfo::getDocAppId, docAppId)
.eq(DocInfo::getIsPublish, YesOrNo.YES)
.list();
List<DocInfoTreeDTO> treeList = CopyUtil.copyList(list, DocInfoTreeDTO::new);
return TreeUtil.convertTree(treeList, 0L);
}
public DocInfoViewDTO getDocDetail(Long id) {
TornaDocInfoViewDTO tornaDocInfoViewDTO = docInfoService.getDocDetail(id);
DocInfoConfigDTO docInfoConfigDTO = buildDocInfoConfig();
DocInfoViewDTO docInfoViewDTO = new DocInfoViewDTO();
docInfoViewDTO.setDocInfoView(tornaDocInfoViewDTO);
docInfoViewDTO.setDocInfoConfig(docInfoConfigDTO);
return docInfoViewDTO;
}
private DocInfoConfigDTO buildDocInfoConfig() {
DocSettingDTO docSetting = docSettingService.getDocSetting();
return CopyUtil.copyBean(docSetting, DocInfoConfigDTO::new);
}
}