mirror of
https://github.com/continew-org/continew-admin.git
synced 2025-12-09 04:57:11 +08:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d81340d007 | |||
| 342ab57842 | |||
| c5ae9f3cc9 | |||
| f44e0b0605 |
@@ -59,6 +59,11 @@ public class RegexConstants {
|
|||||||
*/
|
*/
|
||||||
public static final String PACKAGE_NAME = "^(?:[a-zA-Z_][a-zA-Z0-9_]*\\.)*[a-zA-Z_][a-zA-Z0-9_]*$";
|
public static final String PACKAGE_NAME = "^(?:[a-zA-Z_][a-zA-Z0-9_]*\\.)*[a-zA-Z_][a-zA-Z0-9_]*$";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP 域名 URL 正则
|
||||||
|
*/
|
||||||
|
public static final String HTTP_DOMAIN_URL = "^(https?:\\/\\/)([a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,}(\\/[^\\s]*)?$";
|
||||||
|
|
||||||
private RegexConstants() {
|
private RegexConstants() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,9 +16,11 @@
|
|||||||
|
|
||||||
package top.continew.admin.system.enums;
|
package top.continew.admin.system.enums;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.ReUtil;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import top.continew.admin.common.constant.RegexConstants;
|
||||||
import top.continew.admin.system.model.req.StorageReq;
|
import top.continew.admin.system.model.req.StorageReq;
|
||||||
import top.continew.admin.system.validation.ValidationGroup;
|
import top.continew.admin.system.validation.ValidationGroup;
|
||||||
import top.continew.starter.core.constant.StringConstants;
|
import top.continew.starter.core.constant.StringConstants;
|
||||||
@@ -62,8 +64,8 @@ public enum StorageTypeEnum implements BaseEnum<Integer> {
|
|||||||
@Override
|
@Override
|
||||||
public void validate(StorageReq req) {
|
public void validate(StorageReq req) {
|
||||||
ValidationUtils.validate(req, ValidationGroup.Storage.OSS.class);
|
ValidationUtils.validate(req, ValidationGroup.Storage.OSS.class);
|
||||||
ValidationUtils.throwIf(StrUtil.isNotBlank(req.getDomain()) && !URLUtils.isHttpUrl(req
|
ValidationUtils.throwIf(StrUtil.isNotBlank(req.getDomain()) && !ReUtil
|
||||||
.getDomain()), "域名格式不正确");
|
.isMatch(RegexConstants.HTTP_DOMAIN_URL, req.getDomain()), "域名格式不正确");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
package top.continew.admin.system.model.resp.role;
|
package top.continew.admin.system.model.resp.role;
|
||||||
|
|
||||||
import cn.crane4j.annotation.Assemble;
|
import cn.crane4j.annotation.Assemble;
|
||||||
|
import cn.crane4j.annotation.Mapping;
|
||||||
import cn.crane4j.core.executor.handler.ManyToManyAssembleOperationHandler;
|
import cn.crane4j.core.executor.handler.ManyToManyAssembleOperationHandler;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
@@ -114,7 +115,7 @@ public class RoleUserResp implements Serializable {
|
|||||||
* 角色 ID 列表
|
* 角色 ID 列表
|
||||||
*/
|
*/
|
||||||
@Schema(description = "角色 ID 列表", example = "2")
|
@Schema(description = "角色 ID 列表", example = "2")
|
||||||
@Assemble(prop = ":roleNames", container = ContainerConstants.USER_ROLE_NAME_LIST, handlerType = ManyToManyAssembleOperationHandler.class)
|
@Assemble(props = @Mapping(src = "name", ref = "roleNames"), container = ContainerConstants.USER_ROLE_NAME_LIST, handlerType = ManyToManyAssembleOperationHandler.class)
|
||||||
private List<Long> roleIds;
|
private List<Long> roleIds;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ public class UserDetailResp extends BaseDetailResp {
|
|||||||
* 角色 ID 列表
|
* 角色 ID 列表
|
||||||
*/
|
*/
|
||||||
@Schema(description = "角色 ID 列表", example = "2")
|
@Schema(description = "角色 ID 列表", example = "2")
|
||||||
@Assemble(prop = ":roleNames", container = ContainerConstants.USER_ROLE_NAME_LIST, handlerType = ManyToManyAssembleOperationHandler.class)
|
@Assemble(props = @Mapping(src = "name", ref = "roleNames"), container = ContainerConstants.USER_ROLE_NAME_LIST, handlerType = ManyToManyAssembleOperationHandler.class)
|
||||||
@ExcelProperty(value = "角色 ID 列表", converter = ExcelListConverter.class, order = 8)
|
@ExcelProperty(value = "角色 ID 列表", converter = ExcelListConverter.class, order = 8)
|
||||||
private List<Long> roleIds;
|
private List<Long> roleIds;
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
package top.continew.admin.system.model.resp.user;
|
package top.continew.admin.system.model.resp.user;
|
||||||
|
|
||||||
import cn.crane4j.annotation.Assemble;
|
import cn.crane4j.annotation.Assemble;
|
||||||
|
import cn.crane4j.annotation.Mapping;
|
||||||
import cn.crane4j.core.executor.handler.ManyToManyAssembleOperationHandler;
|
import cn.crane4j.core.executor.handler.ManyToManyAssembleOperationHandler;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
@@ -118,7 +119,7 @@ public class UserResp extends BaseDetailResp {
|
|||||||
* 角色 ID 列表
|
* 角色 ID 列表
|
||||||
*/
|
*/
|
||||||
@Schema(description = "角色 ID 列表", example = "2")
|
@Schema(description = "角色 ID 列表", example = "2")
|
||||||
@Assemble(prop = ":roleNames", container = ContainerConstants.USER_ROLE_NAME_LIST, handlerType = ManyToManyAssembleOperationHandler.class)
|
@Assemble(props = @Mapping(src = "name", ref = "roleNames"), container = ContainerConstants.USER_ROLE_NAME_LIST, handlerType = ManyToManyAssembleOperationHandler.class)
|
||||||
private List<Long> roleIds;
|
private List<Long> roleIds;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ public interface RoleService extends BaseService<RoleResp, RoleDetailResp, RoleQ
|
|||||||
* @param ids ID 列表
|
* @param ids ID 列表
|
||||||
* @return 名称列表
|
* @return 名称列表
|
||||||
*/
|
*/
|
||||||
List<String> listNameByIds(List<Long> ids);
|
List<RoleDO> listNameByIds(List<Long> ids);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据用户 ID 查询角色编码
|
* 根据用户 ID 查询角色编码
|
||||||
|
|||||||
@@ -48,7 +48,6 @@ import top.continew.starter.core.validation.ValidationUtils;
|
|||||||
import top.continew.starter.extension.crud.service.BaseServiceImpl;
|
import top.continew.starter.extension.crud.service.BaseServiceImpl;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
@@ -91,65 +90,13 @@ public class FileServiceImpl extends BaseServiceImpl<FileMapper, FileDO, FileRes
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public FileInfo upload(MultipartFile file, String parentPath, String storageCode) throws IOException {
|
public FileInfo upload(MultipartFile file, String parentPath, String storageCode) {
|
||||||
// 校验文件格式
|
return this.upload(file, parentPath, storageCode, FileNameUtil.extName(file.getOriginalFilename()));
|
||||||
String extName = FileNameUtil.extName(file.getOriginalFilename());
|
|
||||||
return getFileInfo(file, parentPath, storageCode, extName);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 上传文件并返回上传后的文件信息
|
|
||||||
*
|
|
||||||
* @param file
|
|
||||||
* @param parentPath
|
|
||||||
* @param storageCode
|
|
||||||
* @param extName
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
private FileInfo getFileInfo(Object file, String parentPath, String storageCode, String extName) {
|
|
||||||
List<String> allExtensions = FileTypeEnum.getAllExtensions();
|
|
||||||
CheckUtils.throwIf(!allExtensions.contains(extName), "不支持的文件类型,仅支持 {} 格式的文件", String
|
|
||||||
.join(StringConstants.COMMA, allExtensions));
|
|
||||||
// 构建上传预处理对象
|
|
||||||
StorageDO storage = storageService.getByCode(storageCode);
|
|
||||||
CheckUtils.throwIf(DisEnableStatusEnum.DISABLE.equals(storage.getStatus()), "请先启用存储 [{}]", storage.getCode());
|
|
||||||
UploadPretreatment uploadPretreatment = fileStorageService.of(file)
|
|
||||||
.setPlatform(storage.getCode())
|
|
||||||
.setHashCalculatorSha256(true)
|
|
||||||
.putAttr(ClassUtil.getClassName(StorageDO.class, false), storage)
|
|
||||||
.setPath(this.pretreatmentPath(parentPath));
|
|
||||||
// 图片文件生成缩略图
|
|
||||||
if (FileTypeEnum.IMAGE.getExtensions().contains(extName)) {
|
|
||||||
uploadPretreatment.setIgnoreThumbnailException(true, true);
|
|
||||||
uploadPretreatment.thumbnail(img -> img.size(100, 100));
|
|
||||||
}
|
|
||||||
uploadPretreatment.setProgressMonitor(new ProgressListener() {
|
|
||||||
@Override
|
|
||||||
public void start() {
|
|
||||||
log.info("开始上传");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void progress(long progressSize, Long allSize) {
|
|
||||||
log.info("已上传 [{}],总大小 [{}]", progressSize, allSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void finish() {
|
|
||||||
log.info("上传结束");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
// 创建父级目录
|
|
||||||
this.createParentDir(parentPath, storage);
|
|
||||||
// 上传
|
|
||||||
return uploadPretreatment.upload();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public FileInfo upload(File file, String parentPath, String storageCode) throws IOException {
|
public FileInfo upload(File file, String parentPath, String storageCode) {
|
||||||
// 校验文件格式
|
return this.upload(file, parentPath, storageCode, FileNameUtil.extName(file.getName()));
|
||||||
String extName = FileNameUtil.extName(file.getName());
|
|
||||||
return getFileInfo(file, parentPath, storageCode, extName);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -251,6 +198,54 @@ public class FileServiceImpl extends BaseServiceImpl<FileMapper, FileDO, FileRes
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传文件并返回上传后的文件信息
|
||||||
|
*
|
||||||
|
* @param file 文件
|
||||||
|
* @param parentPath 上级目录
|
||||||
|
* @param storageCode 存储引擎编码
|
||||||
|
* @param extName 文件扩展名
|
||||||
|
* @return 文件信息
|
||||||
|
*/
|
||||||
|
private FileInfo upload(Object file, String parentPath, String storageCode, String extName) {
|
||||||
|
List<String> allExtensions = FileTypeEnum.getAllExtensions();
|
||||||
|
CheckUtils.throwIf(!allExtensions.contains(extName), "不支持的文件类型,仅支持 {} 格式的文件", String
|
||||||
|
.join(StringConstants.COMMA, allExtensions));
|
||||||
|
// 构建上传预处理对象
|
||||||
|
StorageDO storage = storageService.getByCode(storageCode);
|
||||||
|
CheckUtils.throwIf(DisEnableStatusEnum.DISABLE.equals(storage.getStatus()), "请先启用存储 [{}]", storage.getCode());
|
||||||
|
UploadPretreatment uploadPretreatment = fileStorageService.of(file)
|
||||||
|
.setPlatform(storage.getCode())
|
||||||
|
.setHashCalculatorSha256(true)
|
||||||
|
.putAttr(ClassUtil.getClassName(StorageDO.class, false), storage)
|
||||||
|
.setPath(this.pretreatmentPath(parentPath));
|
||||||
|
// 图片文件生成缩略图
|
||||||
|
if (FileTypeEnum.IMAGE.getExtensions().contains(extName)) {
|
||||||
|
uploadPretreatment.setIgnoreThumbnailException(true, true);
|
||||||
|
uploadPretreatment.thumbnail(img -> img.size(100, 100));
|
||||||
|
}
|
||||||
|
uploadPretreatment.setProgressMonitor(new ProgressListener() {
|
||||||
|
@Override
|
||||||
|
public void start() {
|
||||||
|
log.info("开始上传");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void progress(long progressSize, Long allSize) {
|
||||||
|
log.info("已上传 [{}],总大小 [{}]", progressSize, allSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void finish() {
|
||||||
|
log.info("上传结束");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// 创建父级目录
|
||||||
|
this.createParentDir(parentPath, storage);
|
||||||
|
// 上传
|
||||||
|
return uploadPretreatment.upload();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理路径
|
* 处理路径
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -76,7 +76,9 @@ public class MessageServiceImpl implements MessageService {
|
|||||||
// 查询当前用户的未读消息
|
// 查询当前用户的未读消息
|
||||||
List<MessageDO> list = baseMapper.selectUnreadListByUserId(userId);
|
List<MessageDO> list = baseMapper.selectUnreadListByUserId(userId);
|
||||||
List<Long> unreadIds = list.stream().map(MessageDO::getId).toList();
|
List<Long> unreadIds = list.stream().map(MessageDO::getId).toList();
|
||||||
messageLogService.addWithUserId(CollUtil.intersection(unreadIds, ids).stream().toList(), userId);
|
messageLogService.addWithUserId(CollUtil.isNotEmpty(ids)
|
||||||
|
? CollUtil.intersection(unreadIds, ids).stream().toList()
|
||||||
|
: unreadIds, userId);
|
||||||
WebSocketUtils.sendMessage(StpUtil.getTokenValueByLoginId(userId), String.valueOf(baseMapper
|
WebSocketUtils.sendMessage(StpUtil.getTokenValueByLoginId(userId), String.valueOf(baseMapper
|
||||||
.selectUnreadListByUserId(userId)
|
.selectUnreadListByUserId(userId)
|
||||||
.size()));
|
.size()));
|
||||||
|
|||||||
@@ -17,7 +17,6 @@
|
|||||||
package top.continew.admin.system.service.impl;
|
package top.continew.admin.system.service.impl;
|
||||||
|
|
||||||
import cn.crane4j.annotation.ContainerMethod;
|
import cn.crane4j.annotation.ContainerMethod;
|
||||||
import cn.crane4j.annotation.MappingType;
|
|
||||||
import cn.hutool.core.collection.CollUtil;
|
import cn.hutool.core.collection.CollUtil;
|
||||||
import cn.hutool.core.util.ObjectUtil;
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
import com.alicp.jetcache.anno.CacheInvalidate;
|
import com.alicp.jetcache.anno.CacheInvalidate;
|
||||||
@@ -167,13 +166,12 @@ public class RoleServiceImpl extends BaseServiceImpl<RoleMapper, RoleDO, RoleRes
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ContainerMethod(namespace = ContainerConstants.USER_ROLE_NAME_LIST, type = MappingType.ORDER_OF_KEYS)
|
@ContainerMethod(namespace = ContainerConstants.USER_ROLE_NAME_LIST, resultType = RoleDO.class)
|
||||||
public List<String> listNameByIds(List<Long> ids) {
|
public List<RoleDO> listNameByIds(List<Long> ids) {
|
||||||
if (CollUtil.isEmpty(ids)) {
|
if (CollUtil.isEmpty(ids)) {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
List<RoleDO> roleList = baseMapper.lambdaQuery().select(RoleDO::getName).in(RoleDO::getId, ids).list();
|
return baseMapper.lambdaQuery().select(RoleDO::getName, RoleDO::getId).in(RoleDO::getId, ids).list();
|
||||||
return roleList.stream().map(RoleDO::getName).toList();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -198,7 +198,6 @@ public class StorageServiceImpl extends BaseServiceImpl<StorageMapper, StorageDO
|
|||||||
config.setSecretKey(storage.getSecretKey());
|
config.setSecretKey(storage.getSecretKey());
|
||||||
config.setEndPoint(storage.getEndpoint());
|
config.setEndPoint(storage.getEndpoint());
|
||||||
config.setBucketName(storage.getBucketName());
|
config.setBucketName(storage.getBucketName());
|
||||||
config.setDomain(StrUtil.emptyIfNull(storage.getDomain()));
|
|
||||||
fileStorageList.addAll(FileStorageServiceBuilder.buildAmazonS3FileStorage(Collections
|
fileStorageList.addAll(FileStorageServiceBuilder.buildAmazonS3FileStorage(Collections
|
||||||
.singletonList(config), null));
|
.singletonList(config), null));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,13 @@
|
|||||||
<a-drawer v-model:visible="visible" title="${businessName}详情" :width="width >= 600 ? 600 : '100%'" :footer="false">
|
<a-drawer v-model:visible="visible" title="${businessName}详情" :width="width >= 600 ? 600 : '100%'" :footer="false">
|
||||||
<a-descriptions :column="2" size="large" class="general-description">
|
<a-descriptions :column="2" size="large" class="general-description">
|
||||||
<#list fieldConfigs as fieldConfig>
|
<#list fieldConfigs as fieldConfig>
|
||||||
|
<#if fieldConfig.dictCode?? && fieldConfig.dictCode != "">
|
||||||
|
<a-descriptions-item label="${fieldConfig.comment}">
|
||||||
|
<GiCellTag :value="dataDetail?.${fieldConfig.fieldName}" :dict="${fieldConfig.dictCode}" />
|
||||||
|
</a-descriptions-item>
|
||||||
|
<#else>
|
||||||
<a-descriptions-item label="${fieldConfig.comment}">{{ dataDetail?.${fieldConfig.fieldName} }}</a-descriptions-item>
|
<a-descriptions-item label="${fieldConfig.comment}">{{ dataDetail?.${fieldConfig.fieldName} }}</a-descriptions-item>
|
||||||
|
</#if>
|
||||||
<#if fieldConfig.fieldName = 'createUser'>
|
<#if fieldConfig.fieldName = 'createUser'>
|
||||||
<a-descriptions-item label="创建人">{{ dataDetail?.createUserString }}</a-descriptions-item>
|
<a-descriptions-item label="创建人">{{ dataDetail?.createUserString }}</a-descriptions-item>
|
||||||
<#elseif fieldConfig.fieldName = 'updateUser'>
|
<#elseif fieldConfig.fieldName = 'updateUser'>
|
||||||
@@ -16,6 +22,11 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useWindowSize } from '@vueuse/core'
|
import { useWindowSize } from '@vueuse/core'
|
||||||
import { type ${classNamePrefix}DetailResp, get${classNamePrefix} as getDetail } from '@/apis/${apiModuleName}/${apiName}'
|
import { type ${classNamePrefix}DetailResp, get${classNamePrefix} as getDetail } from '@/apis/${apiModuleName}/${apiName}'
|
||||||
|
import { useDict } from '@/hooks/app'
|
||||||
|
|
||||||
|
<#if hasDictField>
|
||||||
|
const { <#list dictCodes as dictCode>${dictCode}<#if dictCode_has_next>,</#if></#list> } = useDict(<#list dictCodes as dictCode>'${dictCode}'<#if dictCode_has_next>,</#if></#list>)
|
||||||
|
</#if>
|
||||||
|
|
||||||
const { width } = useWindowSize()
|
const { width } = useWindowSize()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user