核心模块

This commit is contained in:
DB 2023-10-09 14:44:51 +08:00
parent 402ac5340d
commit 84d5e8fe13
71 changed files with 4993 additions and 16 deletions

View File

@ -1,6 +1,6 @@
package com.cpop.common.utils;
import com.pupu.common.enums.CycleEnum;
import com.cpop.common.enums.CycleEnum;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;

View File

@ -1,7 +1,7 @@
package com.cpop.common.utils;
import com.alibaba.fastjson.JSONObject;
import com.pupu.common.utils.text.Convert;
import com.cpop.common.utils.text.Convert;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

View File

@ -1,7 +1,7 @@
package com.cpop.common.utils;
import com.pupu.common.constant.Constants;
import com.pupu.common.utils.text.StrFormatter;
import com.cpop.common.constant.Constants;
import com.cpop.common.utils.text.StrFormatter;
import org.springframework.util.AntPathMatcher;
import java.util.*;

View File

@ -1,7 +1,7 @@
package com.cpop.common.utils.html;
import com.pupu.common.utils.StringUtils;
import com.cpop.common.utils.StringUtils;
/**
* 转义和反转义工具类

View File

@ -1,7 +1,7 @@
package com.cpop.common.utils.http;
import com.pupu.common.constant.Constants;
import com.pupu.common.utils.StringUtils;
import com.cpop.common.constant.Constants;
import com.cpop.common.utils.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

View File

@ -1,9 +1,9 @@
package com.cpop.common.utils.ip;
import com.alibaba.fastjson.JSONObject;
import com.pupu.common.constant.Constants;
import com.pupu.common.utils.StringUtils;
import com.pupu.common.utils.http.HttpUtils;
import com.cpop.common.constant.Constants;
import com.cpop.common.utils.StringUtils;
import com.cpop.common.utils.http.HttpUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;

View File

@ -1,7 +1,7 @@
package com.cpop.common.utils.ip;
import com.pupu.common.utils.StringUtils;
import com.pupu.common.utils.html.EscapeUtil;
import com.cpop.common.utils.StringUtils;
import com.cpop.common.utils.html.EscapeUtil;
import javax.servlet.http.HttpServletRequest;
import java.net.InetAddress;

View File

@ -1,6 +1,6 @@
package com.cpop.common.utils.text;
import com.pupu.common.utils.StringUtils;
import com.cpop.common.utils.StringUtils;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;

View File

@ -1,6 +1,6 @@
package com.cpop.common.utils.text;
import com.pupu.common.utils.StringUtils;
import com.cpop.common.utils.StringUtils;
import org.apache.commons.lang3.ArrayUtils;
import java.math.BigDecimal;

View File

@ -1,6 +1,6 @@
package com.cpop.common.utils.text;
import com.pupu.common.utils.StringUtils;
import com.cpop.common.utils.StringUtils;
/**
* 字符串格式化

62
Cpop-Core/pom.xml Normal file
View File

@ -0,0 +1,62 @@
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.cpop</groupId>
<artifactId>Cpop-Union</artifactId>
<version>1.0.0</version>
<relativePath>../pom.xml</relativePath> <!-- lookup parent from repository -->
</parent>
<artifactId>Cpop-Core</artifactId>
<name>Cpop-Core</name>
<description>核心包</description>
<packaging>jar</packaging>
<dependencies>
<!--Cpop公共包-->
<dependency>
<groupId>com.cpop</groupId>
<artifactId>Cpop-Common</artifactId>
</dependency>
<!--AOP-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!--Mybatis-flex-->
<dependency>
<groupId>com.mybatis-flex</groupId>
<artifactId>mybatis-flex-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.mybatis-flex</groupId>
<artifactId>mybatis-flex-processor</artifactId>
</dependency>
<!--Security-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!--Redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--redis分布式锁依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-integration</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-redis</artifactId>
</dependency>
<!--easyExcel-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
</dependency>
</dependencies>
</project>

100
Cpop-Core/sql/System.sql Normal file
View File

@ -0,0 +1,100 @@
/*
Navicat Premium Data Transfer
Source Server : Localhost
Source Server Type : MySQL
Source Server Version : 80032
Source Host : localhost:3306
Source Schema : pupu-union
Target Server Type : MySQL
Target Server Version : 80032
File Encoding : 65001
Date: 15/09/2023 15:03:53
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for pp_sys_config
-- ----------------------------
DROP TABLE IF EXISTS `cp_sys_config`;
CREATE TABLE `cp_sys_config` (
`config_key` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '参数键',
`config_name` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '参数名',
`config_value` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '参数值',
`config_type` tinyint(1) NULL DEFAULT NULL COMMENT '系统内置(0否1是)',
`remarks` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '备注',
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
`create_user_id` varchar(48) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '创建人id',
`update_time` datetime(0) NULL DEFAULT NULL COMMENT '更新时间',
`update_user_id` varchar(48) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '更新人id',
PRIMARY KEY (`config_key`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '系统配置表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of pp_sys_config
-- ----------------------------
-- ----------------------------
-- Table structure for pp_sys_operation_log
-- ----------------------------
DROP TABLE IF EXISTS `cp_sys_operation_log`;
CREATE TABLE `cp_sys_operation_log` (
`id` varchar(48) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT 'ID',
`operation_user_id` varchar(48) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '用户ID',
`operation_user_name` varchar(48) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '操作用户',
`operation_user_type` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '用户类型',
`code` int(0) NULL DEFAULT NULL COMMENT '操作码',
`info` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '操作信息',
`dev_ip` varchar(48) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '操作终端的IP地址',
`result` tinyint(1) NULL DEFAULT NULL COMMENT '结果 0:失败 1:成功',
`level` int(0) NULL DEFAULT NULL COMMENT '日志级别,0-提示1-一般2-危险3-高危',
`action_type` int(0) NULL DEFAULT NULL COMMENT '行为类别1-一般行为2-异常行为',
`fail_reason` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '失败原因',
`description` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '操作描述',
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
`create_user_id` varchar(48) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '创建人id',
`update_time` datetime(0) NULL DEFAULT NULL COMMENT '更新时间',
`update_user_id` varchar(48) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '更新人id',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '操作日志表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of pp_sys_operation_log
-- ----------------------------
-- ----------------------------
-- Table structure for pp_sys_user
-- ----------------------------
DROP TABLE IF EXISTS `cp_sys_user`;
CREATE TABLE `cp_sys_user` (
`id` varchar(48) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '主键',
`user_name` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '用户名',
`password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '密码',
`nick_name` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '昵称',
`email` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '邮箱',
`phone_number` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '手机号',
`sex` tinyint(1) NULL DEFAULT NULL COMMENT '性别(0:男;1:女)',
`avatar` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '头像',
`salt` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '',
`status` tinyint(1) NULL DEFAULT NULL COMMENT '状态(0:停用;1:启用)',
`login_ip` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '登录地IP',
`user_type` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '用户类型',
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
`create_user_id` varchar(48) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '创建人id',
`update_time` datetime(0) NULL DEFAULT NULL COMMENT '更新时间',
`update_user_id` varchar(48) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '更新人id',
`is_delete` tinyint(1) NULL DEFAULT 0 COMMENT '逻辑删除0否1是',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `uni_user_name_type`(`user_name`, `user_type`) USING BTREE COMMENT '用户名和用户类型联合唯一'
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '系统用户' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of pp_sys_user
-- ----------------------------
INSERT INTO `cp_sys_user` VALUES ('1', 'Cpop', '$2a$10$6CA0M3iyO8u8zSVtmufYGO3KfLvjaE5fxdHCqTQ2NpxYH/Dxi/fBu', 'SuperAdmin', 'cpop@qq.com', '18900000000', 1, NULL, '$2a$10$6CA0M3iyO8u8zSVtmufYGO', 1, '127.0.0.1', 'OAM_USER', '2022-10-01 19:26:49', '1', '2023-08-24 17:47:30', '1', 0);
SET FOREIGN_KEY_CHECKS = 1;

View File

@ -0,0 +1,21 @@
package com.cpop.core.annontation;
import com.cpop.core.base.enums.OperationLogEnum;
import java.lang.annotation.*;
/**
* @author: DB
* @date: 2022-08-12 9:41
* @Description: 操作日志注解
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Documented
public @interface OperationLog {
/**
* 操作枚举
*/
OperationLogEnum operationLogEnumType();
}

View File

@ -0,0 +1,22 @@
package com.cpop.core.annontation;
import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.cpop.core.strategy.ArrayToStringDeserializer;
import com.cpop.core.strategy.StringToArraySerializer;
import java.lang.annotation.*;
/**
* 数组和逗号拼接转换注解
* @author DB
*/
@Documented
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
@JsonSerialize(using = StringToArraySerializer.class)
@JsonDeserialize(using = ArrayToStringDeserializer.class)
public @interface StringArrayConvert {
}

View File

@ -0,0 +1,121 @@
package com.cpop.core.aspect;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.cpop.common.constant.HttpStatus;
import com.cpop.common.enums.ErrorCodeEnum;
import com.cpop.core.base.R;
import com.cpop.core.base.entity.LoginUser;
import com.cpop.core.base.enums.OperationLogEnum;
import com.cpop.core.base.exception.ServiceException;
import com.cpop.core.base.table.SysOperationLog;
import com.cpop.core.event.OperationLogEvent;
import com.cpop.core.utils.MessageUtils;
import com.cpop.core.utils.SecurityUtils;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
/**
* @author: DB
* @date: 2022-08-12 9:45
* @Description:
*/
@Slf4j
@Aspect
@Component
public class OperationLogAspect {
private final SysOperationLog operationLog = new SysOperationLog();
private OperationLogEnum operationLogEnum;
/**
* 事件发布是由ApplicationContext对象管控的我们发布事件前需要注入ApplicationContext对象调用publishEvent方法完成事件发布
*/
@Autowired
private ApplicationContext applicationContext;
/**
* 定义controller切入点拦截规则拦截SysLog注解的方法
*/
@Pointcut("@annotation(com.cpop.core.annontation.OperationLog)")
public void operationLogAspect() {
}
/**
* @param joinPoint 切入点
* @author LOST.yuan
* @Description: 拦截控制层的操作日志
* @date 16:37 2022/10/12
**/
@Before(value = "operationLogAspect()")
public void recordLog(JoinPoint joinPoint) {
//设置枚举信息
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
com.cpop.core.annontation.OperationLog annotation = signature.getMethod().getAnnotation(com.cpop.core.annontation.OperationLog.class);
operationLogEnum = annotation.operationLogEnumType();
try {
LoginUser loginUser = SecurityUtils.getInstance().getLoginUser();
operationLog.setOperationUserName(loginUser.getUsername())
.setOperationUserId(loginUser.getUserId())
.setOperationUserType(loginUser.getUserType())
.setDevIp(loginUser.getIpAddr())
.setCode(operationLogEnum.getCode())
.setInfo(MessageUtils.message(operationLogEnum.getInfo()))
.setCreateUserId(loginUser.getUserId());
operationLog.setUpdateUserId(loginUser.getUserId());
} catch (Exception e) {
throw new ServiceException(ErrorCodeEnum.OPERATION_LOG_EXCEPTION.getInfo(), ErrorCodeEnum.OPERATION_LOG_EXCEPTION.getCode());
}
}
/**
* @param ret 返回
* @author LOST.yuan
* @Description 返回通知
* @date 16:43 2022/10/12
**/
@AfterReturning(returning = "ret", pointcut = "operationLogAspect()")
public void doAfterReturning(Object ret) {
// 处理完请求返回内容
R r = new ObjectMapper().convertValue(ret, R.class);
operationLog.setActionType(1);
if (r.getCode() == HttpStatus.SUCCESS) {
// 正常返回
operationLog.setResult(true);
operationLog.setLevel(operationLogEnum.getSuccessLevel());
operationLog.setDescription(MessageUtils.message(operationLogEnum.getInfo()) + "成功");
} else {
operationLog.setResult(false);
operationLog.setFailReason(r.getMsg());
operationLog.setLevel(operationLogEnum.getExceptionLevel());
operationLog.setDescription(MessageUtils.message(operationLogEnum.getInfo()) + "失败");
}
// 发布事件
applicationContext.publishEvent(new OperationLogEvent(operationLog));
}
/**
* @param e 异常
* @author LOST.yuan
* @Description 异常通知
* @date 16:37 2022/10/12
**/
@AfterThrowing(pointcut = "operationLogAspect()", throwing = "e")
public void doAfterThrowable(Throwable e) {
// 异常
operationLog.setResult(false)
.setActionType(2)
.setFailReason(e.getMessage())
.setDescription(MessageUtils.message(operationLogEnum.getInfo()) + "异常")
.setLevel(operationLogEnum.getExceptionLevel());
// 发布事件
applicationContext.publishEvent(new OperationLogEvent(operationLog));
}
}

View File

@ -0,0 +1,95 @@
package com.cpop.core.base;
import com.cpop.common.constant.Constants;
import java.io.Serializable;
/**
* 响应信息主体
*
* @author DB
*/
public class R<T> implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 成功
*/
public static final int SUCCESS = Constants.SUCCESS;
/**
* 失败
*/
public static final int FAIL = Constants.FAIL;
private int code;
private String msg;
private T result;
public static <T> R<T> ok() {
return restResult(null, SUCCESS, "成功");
}
public static <T> R<T> ok(T result) {
return restResult(result, SUCCESS, "成功");
}
public static <T> R<T> ok(T result, String msg) {
return restResult(result, SUCCESS, msg);
}
public static <T> R<T> fail() {
return restResult(null, FAIL, "失败");
}
public static <T> R<T> fail(String msg) {
return restResult(null, FAIL, msg);
}
public static <T> R<T> fail(T result) {
return restResult(result, FAIL, "失败");
}
public static <T> R<T> fail(T result, String msg) {
return restResult(result, FAIL, msg);
}
public static <T> R<T> fail(int code, String msg) {
return restResult(null, code, msg);
}
private static <T> R<T> restResult(T result, int code, String msg) {
R<T> apiResult = new R<>();
apiResult.setCode(code);
apiResult.setResult(result);
apiResult.setMsg(msg);
return apiResult;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public T getResult() {
return result;
}
public void setResult(T result) {
this.result = result;
}
}

View File

@ -0,0 +1,31 @@
package com.cpop.core.base.entity;
import com.mybatisflex.annotation.Column;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.Map;
/**
* @author LOST.yuan
*/
@Data
public class BaseEntity implements Serializable {
private static final long serialVersionUID = -5809782578272943991L;
private LocalDateTime createTime;
private String createUserId;
private LocalDateTime updateTime;
private String updateUserId;
/**
* 请求参数
*/
@Column(ignore = true)
private Map<String, Object> params;
}

View File

@ -0,0 +1,25 @@
package com.cpop.core.base.entity;
import com.cpop.core.utils.SecurityUtils;
import com.mybatisflex.annotation.InsertListener;
import java.time.LocalDateTime;
/**
* @author: DB
* @Date: 2023/08/04/15:12
* @Description: 添加自动填充
*/
public class BaseInsertListener implements InsertListener {
@Override
public void onInsert(Object entity) {
BaseEntity baseEntity = (BaseEntity) entity;
LoginUser loginUser = SecurityUtils.getInstance().getLoginUser();
//设置 account 被新增时的一些默认数据
baseEntity.setCreateTime(LocalDateTime.now());
baseEntity.setCreateUserId(null == loginUser ? "1" : loginUser.getUserId());
baseEntity.setUpdateTime(LocalDateTime.now());
baseEntity.setUpdateUserId(null == loginUser ? "1" : loginUser.getUserId());
}
}

View File

@ -0,0 +1,23 @@
package com.cpop.core.base.entity;
import com.cpop.core.utils.SecurityUtils;
import com.mybatisflex.annotation.UpdateListener;
import java.time.LocalDateTime;
/**
* @author: DB
* @Date: 2023/08/04/15:13
* @Description:
*/
public class BaseUpdateListener implements UpdateListener {
@Override
public void onUpdate(Object entity) {
BaseEntity baseEntity = (BaseEntity) entity;
LoginUser loginUser = SecurityUtils.getInstance().getLoginUser();
//设置 account 被更新时的一些默认数据
baseEntity.setUpdateTime(LocalDateTime.now());
baseEntity.setUpdateUserId(null == loginUser ? "1" : loginUser.getUserId());
}
}

View File

@ -0,0 +1,64 @@
package com.cpop.core.base.entity;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.type.TypeFactory;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.SerializationException;
import org.springframework.util.Assert;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
/**
* Description:
* date: 2023/5/10 17:39
* @Author: DB
*/
public class FastJson2JsonRedisSerializer<T> implements RedisSerializer<T> {
private ObjectMapper objectMapper = new ObjectMapper();
public static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;
private final Class<T> clazz;
static {
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
//如果遇到反序列化autoType is not support错误请添加并修改一下包名到bean文件路径
// ParserConfig.getGlobalInstance().addAccept("com.xxxxx.xxx");
}
public FastJson2JsonRedisSerializer(Class<T> clazz) {
super();
this.clazz = clazz;
}
@Override
public byte[] serialize(T t) throws SerializationException {
if (t == null) {
return new byte[0];
}
return JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(DEFAULT_CHARSET);
}
@Override
public T deserialize(byte[] bytes) throws SerializationException {
if (bytes == null || bytes.length <= 0) {
return null;
}
String str = new String(bytes, DEFAULT_CHARSET);
return JSON.parseObject(str, clazz);
}
public void setObjectMapper(ObjectMapper objectMapper) {
Assert.notNull(objectMapper, "'objectMapper' must not be null");
this.objectMapper = objectMapper;
}
protected JavaType getJavaType(Class<?> clazz) {
return TypeFactory.defaultInstance().constructType(clazz);
}
}

View File

@ -0,0 +1,155 @@
package com.cpop.core.base.entity;
import com.alibaba.fastjson.annotation.JSONField;
import com.cpop.common.utils.StringUtils;
import com.cpop.core.base.enums.UserType;
import com.cpop.core.base.table.SysUser;
import lombok.Data;
import lombok.experimental.Accessors;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
import java.util.Set;
/**
* 登录用户身份权限
*
* @author DB
*/
@Data
@Accessors(chain = true)
public class LoginUser implements UserDetails {
/**
* 用户ID
*/
private String userId;
/**
* 用户名
*/
private String userName;
/**
* 用户唯一标识
*/
private String identification;
/**
* 登录时间
*/
private Long loginTime = System.currentTimeMillis();
/**
* 过期时间
*/
private Long expireTime;
/**
* 登录IP地址
*/
private String ipAddr;
/**
* 登录地点
*/
private String loginLocation;
/**
* 权限列表
*/
private Set<String> permissions;
/**
* 用户信息
*/
private SysUser user;
/**
* 消息
*/
private String msg;
/**
* 登录状态
*/
private Boolean status;
/**
* 用户类型
*/
private UserType userType = UserType.OAM_USER;
public LoginUser() {
}
public LoginUser(SysUser user, Set<String> permissions) {
this.user = user;
this.permissions = permissions;
}
public LoginUser(String userId, SysUser user, Set<String> permissions) {
this.userId = userId;
this.user = user;
this.permissions = permissions;
}
@JSONField(serialize = false)
@Override
public String getPassword() {
return user.getPassword();
}
@Override
public String getUsername() {
if (StringUtils.isBlank(this.userName)){
return user.getUserName();
}else {
return this.userName;
}
}
/**
* 账户是否未过期,过期无法验证
*/
@JSONField(serialize = false)
@Override
public boolean isAccountNonExpired() {
return true;
}
/**
* 指定用户是否解锁,锁定的用户无法进行身份验证
*/
@JSONField(serialize = false)
@Override
public boolean isAccountNonLocked() {
return true;
}
/**
* 指示是否已过期的用户的凭据(密码),过期的凭据防止认证
*/
@JSONField(serialize = false)
@Override
public boolean isCredentialsNonExpired() {
return true;
}
/**
* 是否可用 ,禁用的用户不能身份验证
*/
@JSONField(serialize = false)
@Override
public boolean isEnabled() {
return true;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
//根据自定义逻辑来返回用户权限如果用户权限返回空或者和拦截路径对应权限不同验证不通过;自定义权限注解此处设定为null
return null;
}
}

View File

@ -0,0 +1,93 @@
package com.cpop.core.base.entity;
import com.cpop.common.utils.StringUtils;
/**
* 分页数据
*
* @author DB
*/
public class PageDomain {
/**
* 当前记录起始索引
*/
private Integer page;
/**
* 每页显示记录数
*/
private Integer pageSize;
/**
* 排序列
*/
private String orderByColumn;
/**
* 排序的方向desc或者asc
*/
private String isAsc = "asc";
/**
* 分页参数合理化
*/
private Boolean reasonable = true;
public String getOrderBy() {
if (StringUtils.isEmpty(orderByColumn)) {
return "";
}
return StringUtils.toUnderScoreCase(orderByColumn) + " " + isAsc;
}
public Integer getPageNum() {
return page;
}
public void setPageNum(Integer pageNum) {
this.page = pageNum;
}
public Integer getPageSize() {
return pageSize;
}
public void setPageSize(Integer pageSize) {
this.pageSize = pageSize;
}
public String getOrderByColumn() {
return orderByColumn;
}
public void setOrderByColumn(String orderByColumn) {
this.orderByColumn = orderByColumn;
}
public String getIsAsc() {
return isAsc;
}
public void setIsAsc(String isAsc) {
if (StringUtils.isNotEmpty(isAsc)) {
// 兼容前端排序类型
if ("ascending".equals(isAsc)) {
isAsc = "asc";
} else if ("descending".equals(isAsc)) {
isAsc = "desc";
}
this.isAsc = isAsc;
}
}
public Boolean getReasonable() {
if (StringUtils.isNull(reasonable)) {
return Boolean.TRUE;
}
return reasonable;
}
public void setReasonable(Boolean reasonable) {
this.reasonable = reasonable;
}
}

View File

@ -0,0 +1,48 @@
package com.cpop.core.base.enums;
import lombok.Getter;
/**
* @author DB
* @Description:
* @create: 2023-08-26 18:52
*/
@Getter
public enum OperationLogEnum {
;
/**
* 操作码
*/
private final Integer code;
/**
* 操作信息
*/
private final String info;
/**
* 操作模块
*/
private final String operationModel;
/**
* 7-调试6-信息5-通知4-警告,3-错误2-关键1-报警,0-紧急
* 成功行为等级
*/
private final Integer successLevel;
/**
* 7-调试6-信息5-通知4-警告,3-错误2-关键1-报警,0-紧急
* 异常行为等级
*/
private final Integer exceptionLevel;
OperationLogEnum(Integer code, String info, String operationModel, Integer successLevel, Integer exceptionLevel) {
this.code = code;
this.info = info;
this.operationModel = operationModel;
this.successLevel = successLevel;
this.exceptionLevel = exceptionLevel;
}
}

View File

@ -0,0 +1,36 @@
package com.cpop.core.base.enums;
import lombok.Getter;
/**
* 用户类型
* @author DB
*/
@Getter
public enum UserType {
/**
* oam系统员工
*/
OAM_USER(0, "oam:loginUser:"),
/**
* 小程序用户
*/
MINI_USER(1, "mini:loginUser:");
/**
* code
*/
private final Integer code;
/**
* redisKey
*/
private final String key;
UserType(Integer code, String key) {
this.code = code;
this.key = key;
}
}

View File

@ -0,0 +1,85 @@
package com.cpop.core.base.exception;
import com.cpop.common.utils.StringUtils;
import com.cpop.core.utils.MessageUtils;
/**
* 基础异常
*
* @author DB
*/
public class BaseException extends RuntimeException {
private static final long serialVersionUID = 1L;
/**
* 所属模块
*/
private final String module;
/**
* 错误码
*/
private final String code;
/**
* 错误码对应的参数
*/
private final Object[] args;
/**
* 错误消息
*/
private final String defaultMessage;
public BaseException(String module, String code, Object[] args, String defaultMessage) {
this.module = module;
this.code = code;
this.args = args;
this.defaultMessage = defaultMessage;
}
public BaseException(String module, String code, Object[] args) {
this(module, code, args, null);
}
public BaseException(String module, String defaultMessage) {
this(module, null, null, defaultMessage);
}
public BaseException(String code, Object[] args) {
this(null, code, args, null);
}
public BaseException(String defaultMessage) {
this(null, null, null, defaultMessage);
}
@Override
public String getMessage() {
String message = null;
if (!StringUtils.isEmpty(code)) {
message = MessageUtils.message(code, args);
}
if (message == null) {
message = defaultMessage;
}
return message;
}
public String getModule() {
return module;
}
public String getCode() {
return code;
}
public Object[] getArgs() {
return args;
}
public String getDefaultMessage() {
return defaultMessage;
}
}

View File

@ -0,0 +1,64 @@
package com.cpop.core.base.exception;
/**
* 业务异常
*
* @author DB
*/
public final class ServiceException extends RuntimeException {
private static final long serialVersionUID = 1L;
/**
* 错误码
*/
private Integer code;
/**
* 错误提示
*/
private String message;
/**
* 错误明细内部调试错误
*/
private String detailMessage;
/**
* 空构造方法避免反序列化问题
*/
public ServiceException() {
}
public ServiceException(String message) {
this.message = message;
}
public ServiceException(String message, Integer code) {
this.message = message;
this.code = code;
}
public String getDetailMessage() {
return detailMessage;
}
@Override
public String getMessage() {
return message;
}
public Integer getCode() {
return code;
}
public ServiceException setMessage(String message) {
this.message = message;
return this;
}
public ServiceException setDetailMessage(String detailMessage) {
this.detailMessage = detailMessage;
return this;
}
}

View File

@ -0,0 +1,23 @@
package com.cpop.core.base.exception;
/**
* 工具类异常
*
* @author DB
*/
public class UtilException extends RuntimeException {
private static final long serialVersionUID = 8247610319171014183L;
public UtilException(Throwable e) {
super(e.getMessage(), e);
}
public UtilException(String message) {
super(message);
}
public UtilException(String message, Throwable throwable) {
super(message, throwable);
}
}

View File

@ -0,0 +1,18 @@
package com.cpop.core.base.exception.file;
import com.cpop.core.base.exception.BaseException;
/**
* 文件信息异常类
*
* @author DB
*/
public class FileException extends BaseException {
private static final long serialVersionUID = 1L;
public FileException(String code, Object[] args) {
super("file", code, args, null);
}
}

View File

@ -0,0 +1,17 @@
package com.cpop.core.base.exception.file;
/**
* 文件名称超长限制异常类
*
* @author DB
*/
public class FileNameLengthLimitExceededException extends FileException {
private static final long serialVersionUID = 1L;
public FileNameLengthLimitExceededException(int defaultFileNameLength) {
super("upload.filename.exceed.length", new Object[]{defaultFileNameLength});
}
}

View File

@ -0,0 +1,17 @@
package com.cpop.core.base.exception.file;
/**
* 文件名大小限制异常类
*
* @author DB
*/
public class FileSizeLimitExceededException extends FileException {
private static final long serialVersionUID = 1L;
public FileSizeLimitExceededException(long defaultMaxSize) {
super("upload.exceed.maxSize", new Object[]{defaultMaxSize});
}
}

View File

@ -0,0 +1,53 @@
package com.cpop.core.base.exception.file;
import java.io.PrintStream;
import java.io.PrintWriter;
/**
* 文件上传异常类
*
* @author DB
*/
public class FileUploadException extends Exception {
private static final long serialVersionUID = 1L;
private final Throwable cause;
public FileUploadException() {
this(null, null);
}
public FileUploadException(final String msg) {
this(msg, null);
}
public FileUploadException(String msg, Throwable cause) {
super(msg);
this.cause = cause;
}
@Override
public void printStackTrace(PrintStream stream) {
super.printStackTrace(stream);
if (cause != null) {
stream.println("Caused by:");
cause.printStackTrace(stream);
}
}
@Override
public void printStackTrace(PrintWriter writer) {
super.printStackTrace(writer);
if (cause != null) {
writer.println("Caused by:");
cause.printStackTrace(writer);
}
}
@Override
public Throwable getCause() {
return cause;
}
}

View File

@ -0,0 +1,69 @@
package com.cpop.core.base.exception.file;
import java.util.Arrays;
/**
* 文件上传 误异常类
*
* @author DB
*/
public class InvalidExtensionException extends FileUploadException {
private static final long serialVersionUID = 1L;
private final String[] allowedExtension;
private final String extension;
private final String filename;
public InvalidExtensionException(String[] allowedExtension, String extension, String filename) {
super("文件[" + filename + "]后缀[" + extension + "]不正确,请上传" + Arrays.toString(allowedExtension) + "格式");
this.allowedExtension = allowedExtension;
this.extension = extension;
this.filename = filename;
}
public String[] getAllowedExtension() {
return allowedExtension;
}
public String getExtension() {
return extension;
}
public String getFilename() {
return filename;
}
public static class InvalidImageExtensionException extends InvalidExtensionException {
private static final long serialVersionUID = 1L;
public InvalidImageExtensionException(String[] allowedExtension, String extension, String filename) {
super(allowedExtension, extension, filename);
}
}
public static class InvalidFlashExtensionException extends InvalidExtensionException {
private static final long serialVersionUID = 1L;
public InvalidFlashExtensionException(String[] allowedExtension, String extension, String filename) {
super(allowedExtension, extension, filename);
}
}
public static class InvalidMediaExtensionException extends InvalidExtensionException {
private static final long serialVersionUID = 1L;
public InvalidMediaExtensionException(String[] allowedExtension, String extension, String filename) {
super(allowedExtension, extension, filename);
}
}
public static class InvalidVideoExtensionException extends InvalidExtensionException {
private static final long serialVersionUID = 1L;
public InvalidVideoExtensionException(String[] allowedExtension, String extension, String filename) {
super(allowedExtension, extension, filename);
}
}
}

View File

@ -0,0 +1,54 @@
package com.cpop.core.base.table;
import com.cpop.core.base.entity.BaseEntity;
import com.cpop.core.base.entity.BaseInsertListener;
import com.cpop.core.base.entity.BaseUpdateListener;
import com.mybatisflex.annotation.Id;
import com.mybatisflex.annotation.Table;
import lombok.*;
import lombok.experimental.Accessors;
import java.io.Serializable;
/**
* 系统配置表 实体类
*
* @author DB
* @since 2023-08-11
*/
@Data
@EqualsAndHashCode(callSuper=false)
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
@Table(value = "pp_sys_config", onInsert = BaseInsertListener.class, onUpdate = BaseUpdateListener.class, mapperGenerateEnable = false)
public class SysConfig extends BaseEntity implements Serializable {
/**
* 参数键
*/
@Id
private String configKey;
/**
* 参数名
*/
private String configName;
/**
* 参数值
*/
private String configValue;
/**
* 系统内置(0否1是)
*/
private Boolean configType;
/**
* 备注
*/
private String remarks;
}

View File

@ -0,0 +1,90 @@
package com.cpop.core.base.table;
import com.cpop.core.base.entity.BaseEntity;
import com.cpop.core.base.entity.BaseInsertListener;
import com.cpop.core.base.entity.BaseUpdateListener;
import com.cpop.core.base.enums.UserType;
import com.mybatisflex.annotation.Id;
import com.mybatisflex.annotation.Table;
import lombok.*;
import lombok.experimental.Accessors;
import java.io.Serializable;
/**
* 操作日志表 实体类
*
* @author DB
* @since 2023-08-05
*/
@Data
@EqualsAndHashCode(callSuper=false)
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
@Table(value = "pp_sys_operation_log", onInsert = BaseInsertListener.class, onUpdate = BaseUpdateListener.class, mapperGenerateEnable = false)
public class SysOperationLog extends BaseEntity implements Serializable {
/**
* ID
*/
@Id
private String id;
/**
* 用户ID
*/
private String operationUserId;
/**
* 操作用户
*/
private String operationUserName;
/**
* 操作用户类型
*/
private UserType operationUserType;
/**
* 操作码
*/
private Integer code;
/**
* 操作信息
*/
private String info;
/**
* 操作终端的IP地址
*/
private String devIp;
/**
* 结果 -1:失败 1:成功
*/
private Boolean result;
/**
* 日志级别,0-提示1-一般2-危险3-高危
*/
private Integer level;
/**
* 行为类别1-一般行为2-异常行为
*/
private Integer actionType;
/**
* 失败原因
*/
private String failReason;
/**
* 操作描述
*/
private String description;
}

View File

@ -0,0 +1,96 @@
package com.cpop.core.base.table;
import com.cpop.core.base.entity.BaseEntity;
import com.cpop.core.base.entity.BaseInsertListener;
import com.cpop.core.base.entity.BaseUpdateListener;
import com.mybatisflex.annotation.Column;
import com.mybatisflex.annotation.Id;
import com.mybatisflex.annotation.Table;
import com.cpop.core.base.enums.UserType;
import lombok.*;
import lombok.experimental.Accessors;
import java.io.Serializable;
/**
* 系统用户
*
* @author DB.lost
* @Date: 2023-05-05
*/
@Data
@EqualsAndHashCode(callSuper=false)
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
@Table(value = "pp_sys_user", onInsert = BaseInsertListener.class, onUpdate = BaseUpdateListener.class, mapperGenerateEnable = false)
public class SysUser extends BaseEntity implements Serializable {
/**
* 主键
*/
@Id
private String id;
/**
* 用户名
*/
private String userName;
/**
* 密码
*/
private String password;
/**
* 昵称
*/
private String nickName;
/**
* 邮箱
*/
private String email;
/**
* 手机号
*/
private String phoneNumber;
/**
* 性别(0:;1:)
*/
private Boolean sex;
/**
* 头像
*/
private String avatar;
/**
*
*/
private String salt;
/**
* 状态(0:停用;1:启用)
*/
private Boolean status;
/**
* 登录地IP
*/
private String loginIp;
/**
* 用户类型
*/
private UserType userType;
/**
* 逻辑删除0否1是
*/
@Column(isLogicDelete = true)
private Boolean isDelete;
}

View File

@ -0,0 +1,64 @@
package com.cpop.core.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
/**
* @author: DB
* @date: 2022-10-13 11:45
* @Description:
*/
@Configuration
public class AsyncScheduledTaskConfig {
@Autowired
private TaskThreadPoolConfig config;
/**
* 1.这种形式的线程池配置是需要在使用的方法上面添加@Async("customAsyncThreadPool")注解的
* 2.如果在使用的方法上不添加该注解那么spring就会使用默认的线程池
* 3.所以如果添加@Async注解但是不指定使用的线程池又想自己自定义线程池那么就可以重写spring默认的线程池
* 4.所以第二个方法就是重写spring默认的线程池
* @return Executor
*/
@Bean("customAsyncThreadPool")
public Executor customAsyncThreadPool() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
//最大线程数
executor.setMaxPoolSize(config.getMaxPoolSize());
//核心线程数
executor.setCorePoolSize(config.getCorePoolSize());
//任务队列的大小
executor.setQueueCapacity(config.getQueueCapacity());
//线程池名的前缀
executor.setThreadNamePrefix(config.getThreadNamePrefix());
//允许线程的空闲时间30秒
executor.setKeepAliveSeconds(config.getKeepAliveSeconds());
//设置线程池关闭的时候等待所有任务都完成再继续销毁其他的Bean
executor.setWaitForTasksToCompleteOnShutdown(true);
//设置线程池中任务的等待时间如果超过这个时候还没有销毁就强制销毁以确保应用最后能够被关闭而不是阻塞住
executor.setAwaitTerminationSeconds(config.getAwaitTerminationSeconds());
/**
* 拒绝处理策略
* CallerRunsPolicy()交由调用方线程运行比如 main 线程
* AbortPolicy()直接抛出异常
* DiscardPolicy()直接丢弃
* DiscardOldestPolicy()丢弃队列中最老的任务
* 特殊说明
* 1. 这里演示环境拒绝策略咱们采用抛出异常
* 2.真实业务场景会把缓存队列的大小会设置大一些
* 如果提交的任务数量超过最大线程数量或将任务环缓存到本地redismysql中,保证消息不丢失
* 3.如果项目比较大的话异步通知种类很多的话建议采用MQ做异步通知方案
*/
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
//线程初始化
executor.initialize();
return executor;
}
}

View File

@ -0,0 +1,94 @@
package com.cpop.core.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* 读取项目相关配置
*
* @author DB
*/
@Component
@ConfigurationProperties(prefix = "cpop")
public class CpopConfig {
/**
* 项目名称
*/
private String name;
/**
* 版本
*/
private String version;
/**
* 上传路径
*/
private static String profile;
/**
* 验证码类型
*/
private static String captchaType;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
public static String getProfile() {
return profile;
}
public void setProfile(String profile) {
CpopConfig.profile = profile;
}
public static String getCaptchaType() {
return captchaType;
}
public void setCaptchaType(String captchaType) {
CpopConfig.captchaType = captchaType;
}
/**
* 获取导入上传路径
*/
public static String getImportPath() {
return getProfile() + "/import";
}
/**
* 获取头像上传路径
*/
public static String getAvatarPath() {
return getProfile() + "/avatar";
}
/**
* 获取下载路径
*/
public static String getDownloadPath() {
return getProfile() + "/download/";
}
/**
* 获取上传路径
*/
public static String getUploadPath() {
return getProfile() + "/upload";
}
}

View File

@ -0,0 +1,63 @@
package com.cpop.core.config;
import com.cpop.core.base.entity.FastJson2JsonRedisSerializer;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.serializer.StringRedisSerializer;
/**
* redis配置
*
* @author DB
*/
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {
@Bean
@SuppressWarnings(value = {"unchecked", "rawtypes"})
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);
FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer(Object.class);
// 使用StringRedisSerializer来序列化和反序列化redis的key值
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(serializer);
// Hash的key也采用StringRedisSerializer的序列化方式
template.setHashKeySerializer(new StringRedisSerializer());
template.setHashValueSerializer(serializer);
template.afterPropertiesSet();
return template;
}
@Bean
public DefaultRedisScript<Long> limitScript() {
DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
redisScript.setScriptText(limitScriptText());
redisScript.setResultType(Long.class);
return redisScript;
}
/**
* 限流脚本
*/
private String limitScriptText() {
return "local key = KEYS[1]\n" +
"local count = tonumber(ARGV[1])\n" +
"local time = tonumber(ARGV[2])\n" +
"local current = redis.call('get', key);\n" +
"if current and tonumber(current) > count then\n" +
" return tonumber(current);\n" +
"end\n" +
"current = redis.call('incr', key)\n" +
"if tonumber(current) == 1 then\n" +
" redis.call('expire', key, time)\n" +
"end\n" +
"return tonumber(current);";
}
}

View File

@ -0,0 +1,21 @@
package com.cpop.core.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.integration.redis.util.RedisLockRegistry;
/**
* @author DB
*/
@Configuration
public class RedisLockConfig {
@Bean
public RedisLockRegistry redisLockRegistry(RedisConnectionFactory redisConnectionFactory) {
//第一个参数redisConnectionFactory
//第二个参数registryKey分布式锁前缀设置为项目名称会好些
//该构造方法对应的分布式锁默认有效期是60秒.可以自定义
return new RedisLockRegistry(redisConnectionFactory, "Cpop-");
}
}

View File

@ -0,0 +1,31 @@
package com.cpop.core.config;
import com.cpop.common.utils.ServletUtils;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
/**
* 服务相关配置
*
* @author DB
*/
@Component
public class ServerConfig {
/**
* 获取完整的请求路径包括域名端口上下文访问路径
*
* @return 服务地址
*/
public String getUrl() {
HttpServletRequest request = ServletUtils.getRequest();
return getDomain(request);
}
public static String getDomain(HttpServletRequest request) {
StringBuffer url = request.getRequestURL();
String contextPath = request.getServletContext().getContextPath();
return url.delete(url.length() - request.getRequestURI().length(), url.length()).append(contextPath).toString();
}
}

View File

@ -0,0 +1,42 @@
package com.cpop.core.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* @author: DB
* @date: 2022-10-13 11:40
* @Description: 线程配置属性类
*/
@Data
@Component
@ConfigurationProperties(prefix = "task.pool")
public class TaskThreadPoolConfig {
/**
* 设置核心线程数
*/
private Integer corePoolSize;
/**
* 设置最大线程数
*/
private Integer maxPoolSize;
/**
* 设置空闲线程存活时间
*/
private Integer keepAliveSeconds;
/**
* 设置队列容量
*/
private Integer queueCapacity;
/**
* 设置线程名称前缀
*/
private Integer awaitTerminationSeconds;
/**
* 设置线程池等待终止时间()
*/
private String threadNamePrefix;
}

View File

@ -0,0 +1,20 @@
package com.cpop.core.event;
import org.springframework.context.ApplicationEvent;
/**
* @author: DB
* @date: 2022-08-12 10:10
* @Description: 操作日志事件
*/
public class OperationLogEvent extends ApplicationEvent {
/**
* @author LOST.yuan
* @date 10:10 2022/8/12
* @param source
**/
public OperationLogEvent(Object source) {
super(source);
}
}

View File

@ -0,0 +1,43 @@
package com.cpop.core.filter;
import com.cpop.common.utils.StringUtils;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
;
/**
* Repeatable 过滤器
*
* @author DB
*/
@Component
public class RepeatableFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
ServletRequest requestWrapper = null;
if (request instanceof HttpServletRequest && StringUtils.startsWithIgnoreCase(request.getContentType(), MediaType.APPLICATION_JSON_VALUE)) {
requestWrapper = new RepeatedlyRequestWrapper((HttpServletRequest) request, response);
}
if (null == requestWrapper) {
chain.doFilter(request, response);
} else {
chain.doFilter(requestWrapper, response);
}
}
@Override
public void destroy() {
}
}

View File

@ -0,0 +1,67 @@
package com.cpop.core.filter;
import com.cpop.common.utils.http.HttpHelper;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
/**
* 构建可重复读取inputStream的request
*
* @author DB
*/
public class RepeatedlyRequestWrapper extends HttpServletRequestWrapper {
private final byte[] body;
public RepeatedlyRequestWrapper(HttpServletRequest request, ServletResponse response) throws IOException {
super(request);
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
body = HttpHelper.getBodyString(request).getBytes(StandardCharsets.UTF_8);
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(getInputStream()));
}
@Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream bais = new ByteArrayInputStream(body);
return new ServletInputStream() {
@Override
public int read() throws IOException {
return bais.read();
}
@Override
public int available() throws IOException {
return body.length;
}
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) {
}
};
}
}

View File

@ -0,0 +1,61 @@
package com.cpop.core.filter;
import com.cpop.common.utils.StringUtils;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* 防止XSS攻击的过滤器
*
* @author DB
*/
public class XssFilter implements Filter {
/**
* 排除链接
*/
public List<String> excludes = new ArrayList<>();
@Override
public void init(FilterConfig filterConfig) throws ServletException {
String tempExcludes = filterConfig.getInitParameter("excludes");
if (StringUtils.isNotEmpty(tempExcludes)) {
String[] url = tempExcludes.split(",");
for (int i = 0; url != null && i < url.length; i++) {
excludes.add(url[i]);
}
}
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
if (handleExcludeURL(req, resp)) {
chain.doFilter(request, response);
return;
}
XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper((HttpServletRequest) request);
chain.doFilter(xssRequest, response);
}
private boolean handleExcludeURL(HttpServletRequest request, HttpServletResponse response) {
String url = request.getServletPath();
String method = request.getMethod();
// GET DELETE 不过滤
if (method == null || method.matches("GET") || method.matches("DELETE")) {
return true;
}
return StringUtils.matches(url, excludes);
}
@Override
public void destroy() {
}
}

View File

@ -0,0 +1,97 @@
package com.cpop.core.filter;
import com.cpop.common.utils.StringUtils;
import com.cpop.common.utils.html.EscapeUtil;
import org.apache.commons.io.IOUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
/**
* XSS过滤处理
*
* @author DB
*/
public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
/**
* @param request
*/
public XssHttpServletRequestWrapper(HttpServletRequest request) {
super(request);
}
@Override
public String[] getParameterValues(String name) {
String[] values = super.getParameterValues(name);
if (values != null) {
int length = values.length;
String[] escapseValues = new String[length];
for (int i = 0; i < length; i++) {
// 防xss攻击和过滤前后空格
escapseValues[i] = EscapeUtil.clean(values[i]).trim();
}
return escapseValues;
}
return super.getParameterValues(name);
}
@Override
public ServletInputStream getInputStream() throws IOException {
// 非json类型直接返回
if (!isJsonRequest()) {
return super.getInputStream();
}
// 为空直接返回
String json = IOUtils.toString(super.getInputStream(), StandardCharsets.UTF_8);
if (StringUtils.isEmpty(json)) {
return super.getInputStream();
}
// xss过滤
json = EscapeUtil.clean(json).trim();
byte[] jsonBytes = json.getBytes(StandardCharsets.UTF_8);
final ByteArrayInputStream bis = new ByteArrayInputStream(jsonBytes);
return new ServletInputStream() {
@Override
public boolean isFinished() {
return true;
}
@Override
public boolean isReady() {
return true;
}
@Override
public int available() throws IOException {
return jsonBytes.length;
}
@Override
public void setReadListener(ReadListener readListener) {
}
@Override
public int read() throws IOException {
return bis.read();
}
};
}
/**
* 是否是Json请求
*/
public boolean isJsonRequest() {
String header = super.getHeader(HttpHeaders.CONTENT_TYPE);
return StringUtils.startsWithIgnoreCase(header, MediaType.APPLICATION_JSON_VALUE);
}
}

View File

@ -0,0 +1,93 @@
package com.cpop.core.handler;
import com.cpop.core.base.R;
import com.cpop.core.base.exception.ServiceException;
import com.cpop.common.enums.ErrorCodeEnum;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.validation.BindException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import javax.servlet.http.HttpServletRequest;
import java.util.Objects;
/**
* @author: DB
* @date: 2022-10-13 17:08
* @Description: 全局异常处理器
*/
@RestControllerAdvice
public class GlobalExceptionHandler {
private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
/**
* 业务异常
*/
@ExceptionHandler(ServiceException.class)
public R handleServiceException(ServiceException e) {
log.error(e.getMessage(), e);
Integer code = e.getCode();
return null != code ? R.fail(code, e.getMessage()) : R.fail(e.getMessage());
}
/**
* 拦截未知的运行时异常
* @param e
* @param request
* @return
*/
@ExceptionHandler(RuntimeException.class)
public R handleRuntimeException(RuntimeException e, HttpServletRequest request) {
String requestUrl = request.getRequestURI();
log.error("请求地址'{}',发生未知异常.", requestUrl, e);
return R.fail(e.getMessage());
}
/**
* 系统异常
* @param e
* @param request
* @return
*/
@ExceptionHandler(Exception.class)
public R handleException(Exception e, HttpServletRequest request) {
String requestUrl = request.getRequestURI();
log.error("请求地址'{}',发生系统异常.", requestUrl, e);
return R.fail(e.getMessage());
}
/**
* 权限不足
* @param e 权限不足异常
*/
@ExceptionHandler(AccessDeniedException.class)
public R handleException(AccessDeniedException e) {
return R.fail(ErrorCodeEnum.HTTP_401.getInfo());
}
/**
* 自定义验证异常
*/
@ExceptionHandler(BindException.class)
public R handleBindException(BindException e) {
log.error(e.getMessage(), e);
String message = e.getAllErrors().get(0).getDefaultMessage();
return R.fail(message);
}
/**
* 自定义验证异常
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
public R handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
log.error(e.getMessage(), e);
String message = Objects.requireNonNull(e.getBindingResult().getFieldError()).getDefaultMessage();
return R.fail(message);
}
//TODO:可以自行后续拓展
}

View File

@ -0,0 +1,37 @@
package com.cpop.core.listen;
import com.cpop.core.base.table.SysOperationLog;
import com.cpop.core.event.OperationLogEvent;
import com.cpop.core.utils.uuid.IdUtils;
import com.cpop.core.service.CoreService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.event.EventListener;
import org.springframework.core.annotation.Order;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
/**
* @author: DB
* @date: 2022-08-12 10:25
* @Description: 操作日志监听
*/
@Slf4j
@Component
public class SysOperationLogListen {
@Autowired
private CoreService coreService;
@Async
@Order
@EventListener(OperationLogEvent.class)
public void saveSysOperationLog(OperationLogEvent event) {
SysOperationLog operationLog = (SysOperationLog) event.getSource();
// 保存日志
operationLog.setId(IdUtils.fastSimpleUUID());
if (coreService.saveOperationLog(operationLog)) {
//TODO后续可自定义其他操作
}
}
}

View File

@ -0,0 +1,124 @@
package com.cpop.core.mapper;
import com.cpop.core.base.enums.UserType;
import com.cpop.core.base.table.SysConfig;
import com.cpop.core.base.table.SysOperationLog;
import com.cpop.core.base.table.SysUser;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* @author DB
* @Description:
* @create 2023-08-27 10:54
*/
public interface CoreMapper {
/**
* @Description: 插入操作日志
* @param log
* @return Integer
* @Author DB
* @Date: 2023/8/27 10:56
*/
Boolean insertOperationLog(SysOperationLog log);
/**
* @Description: 根据用户名获取用户信息
* @param username 用户名
* @param userType 用户类型
* @return SysUser
* @Author DB
* @Date: 2023/8/27 23:39
*/
SysUser getSysUser(@Param("username") String username, @Param("userType") UserType userType);
/**
* @Description: 更新登录地址
* @param ipAddr
* @param username
* @return
* @author DB
* @Date: 2023/8/28 0028 13:49
*/
void updateSysUserLoginIp(String ipAddr, String username);
/**
* @Description: 加载参数缓存数据
* @param
* @return List<SysConfig>
* @Author DB
* @Date: 2023/8/28 22:33
*/
List<SysConfig> loadingConfigCache();
/**
* @Description: 根据键名查询参数配置信息
* @param configKey
* @return SysConfig
* @Author DB
* @Date: 2023/8/28 22:49
*/
SysConfig selectConfigByKey(String configKey);
/**
* @Description: 新增参数配置
* @param config
* @return boolean
* @Author DB
* @Date: 2023/8/28 23:03
*/
boolean insertConfig(SysConfig config);
/**
* @Description: 修改参数配置
* @param config
* @return boolean
* @Author DB
* @Date: 2023/8/28 23:17
*/
boolean updateConfig(SysConfig config);
/**
* @Description: 根据键查询配置
* @param list
* @return List<SysConfig>
* @Author DB
* @Date: 2023/8/28 23:25
*/
List<SysConfig> selectConfigByKeys(List<String> list);
/**
* @Description: 需要删除的参数ID
* @param list
* @return
* @Author DB
* @Date: 2023/8/28 23:29
*/
void deleteConfigByKeys(List<String> list);
/**
* @descriptions 新增系统用户
* @author DB
* @date 2023/09/08 15:04
* @param sysUser 系统用户参数
*/
void insertSysUser(SysUser sysUser);
/**
* @descriptions 修改系统用户
* @author DB
* @date 2023/09/08 17:21
* @param sysUser 系统用户参数d
*/
void updateSysUser(SysUser sysUser);
/**
* @descriptions 根据主键删除用户(逻辑删除)
* @author DB
* @date 2023/09/08 17:50
* @param id 主键
*/
void removeSysUserById(String id);
}

View File

@ -0,0 +1,111 @@
package com.cpop.core.service;
import com.cpop.core.base.entity.LoginUser;
import com.cpop.core.base.enums.OperationLogEnum;
import com.cpop.core.base.enums.UserType;
import com.cpop.core.base.table.SysConfig;
import com.cpop.core.base.table.SysOperationLog;
import com.cpop.core.base.table.SysUser;
/**
* @author DB
* @Description:
* @create: 2023-08-27 11:04
*/
public interface CoreService {
/**
* @Description: 添加操作日志
* @param status 状态
* @param loginUser 登陆用户
* @param failReason 失败原因
* @return String
* @Author: DB
* @Date: 2023/8/27 11:08
*/
String insertOperationLog(Integer status, OperationLogEnum operationLogEnum, LoginUser loginUser, String failReason);
/**
* @Description: 添加操作日志
* @param operationLog 日志
* @return boolean
* @Author: DB
* @Date: 2023/8/29 23:46
*/
boolean saveOperationLog(SysOperationLog operationLog);
/**
* @Description: 根据用户名获取用户信息
* @param username 用户名
* @return SysUser
* @Author: DB
* @Date: 2023/8/27 23:37
*/
SysUser getSysUser(String username, UserType userType);
/**
* @Description: 更新登录地址
* @param ipAddr ip地址
* @param username 用户名
* @author DB
* @Date: 2023/8/28 0028 13:48
*/
void updateSysUserLoginIp(String ipAddr, String username);
/**
* 根据键名查询参数配置信息
*
* @param configKey 参数键名
* @return 参数键值
*/
SysConfig selectConfigByKey(String configKey);
/**
* @Description: 新增参数配置
* @param config
* @author DB
* @Date: 2023/6/7 0007 16:17
*/
void insertConfig(SysConfig config);
/**
* @Description: 修改参数配置
* @param config
* @author DB
* @Date: 2023/6/7 0007 16:19
*/
void updateConfig(SysConfig config);
/**
* @Description: 批量删除参数信息
* @param keys
* @author DB
* @Date: 2023/6/8 0008 9:58
*/
void deleteConfigByKeys(String[] keys);
/**
* @descriptions 新增系统用户
* @author DB
* @date 2023/09/08 15:04
* @param sysUser 系统用户参数
*/
void insertSysUser(SysUser sysUser);
/**
* @descriptions 修改系统用户
* @author DB
* @date 2023/09/08 17:20
* @param sysUser 系统用户参数
*/
void updateSysUser(SysUser sysUser);
/**
* @descriptions 根据主键删除用户
* @author DB
* @date 2023/09/08 17:49
* @param id 主键
* @return void
*/
void removeSysUserById(String id);
}

View File

@ -0,0 +1,188 @@
package com.cpop.core.service;
import org.springframework.data.redis.core.BoundSetOperations;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
/**
* spring redis 工具类
*
* @author DB
**/
public interface RedisService {
/**
* 缓存基本的对象IntegerString实体类等
*
* @param key 缓存的键值
* @param value 缓存的值
*/
<T> void setCacheObject(final String key, final T value);
/**
* 缓存基本的对象IntegerString实体类等
*
* @param key 缓存的键值
* @param value 缓存的值
* @param timeout 时间
* @param timeUnit 时间颗粒度
*/
<T> void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit);
/**
* 设置有效时间
*
* @param key Redis键
* @param timeout 超时时间
* @return true=设置成功false=设置失败
*/
boolean expire(final String key, final long timeout);
/**
* 设置有效时间
*
* @param key Redis键
* @param timeout 超时时间
* @param unit 时间单位
* @return true=设置成功false=设置失败
*/
boolean expire(final String key, final long timeout, final TimeUnit unit);
/**
* 获取有效时间
*
* @param key Redis键
* @return 有效时间
*/
long getExpire(final String key);
/**
* 判断 key是否存在
*
* @param key
* @return true 存在 false不存在
*/
Boolean hasKey(String key);
/**
* 获得缓存的基本对象
*
* @param key 缓存键值
* @return 缓存键值对应的数据
*/
<T> T getCacheObject(final String key);
/**
* 删除单个对象
*
* @param key
*/
boolean deleteObject(final String key);
/**
* 删除集合对象
*
* @param collection 多个对象
* @return
*/
long deleteObject(final Collection collection);
/**
* 缓存List数据
*
* @param key 缓存的键值
* @param dataList 待缓存的List数据
* @return 缓存的对象
*/
<T> long setCacheList(final String key, final List<T> dataList);
/**
* 获得缓存的list对象
*
* @param key 缓存的键值
* @return 缓存键值对应的数据
*/
<T> List<T> getCacheList(final String key);
/**
* 缓存Set
*
* @param key 缓存键值
* @param dataSet 缓存的数据
* @return 缓存数据的对象
*/
<T> BoundSetOperations<String, T> setCacheSet(final String key, final Set<T> dataSet);
/**
* 获得缓存的set
*
* @param key
* @return
*/
<T> Set<T> getCacheSet(final String key);
/**
* 缓存Map
*
* @param key
* @param dataMap
*/
<T> void setCacheMap(final String key, final Map<String, T> dataMap);
/**
* 获得缓存的Map
*
* @param key
* @return
*/
<T> Map<String, T> getCacheMap(final String key);
/**
* 往Hash中存入数据
*
* @param key Redis键
* @param hKey Hash键
* @param value
*/
<T> void setCacheMapValue(final String key, final String hKey, final T value);
/**
* 获取Hash中的数据
*
* @param key Redis键
* @param hKey Hash键
* @return Hash中的对象
*/
<T> T getCacheMapValue(final String key, final String hKey);
/**
* 获取多个Hash中的数据
*
* @param key Redis键
* @param hKeys Hash键集合
* @return Hash对象集合
*/
<T> List<T> getMultiCacheMapValue(final String key, final Collection<Object> hKeys);
/**
* 获得缓存的基本对象列表
*
* @param pattern 字符串前缀
* @return 对象列表
*/
Collection<String> keys(final String pattern);
/**
* @param key
* @return
* @Description: 分布式锁
* @Author DB
* @Date: 2023/4/12 22:23
*/
Lock distributedLock(String key);
}

View File

@ -0,0 +1,195 @@
package com.cpop.core.service.impl;
import com.cpop.core.base.entity.LoginUser;
import com.cpop.core.base.enums.OperationLogEnum;
import com.cpop.core.base.enums.UserType;
import com.cpop.core.base.exception.ServiceException;
import com.cpop.core.base.table.SysConfig;
import com.cpop.core.base.table.SysOperationLog;
import com.cpop.core.base.table.SysUser;
import com.cpop.core.mapper.CoreMapper;
import com.cpop.core.service.CoreService;
import com.cpop.core.utils.MessageUtils;
import com.cpop.core.utils.SecurityUtils;
import com.cpop.core.utils.uuid.IdUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Arrays;
import java.util.List;
/**
* @author DB
* @Description:
* @create: 2023-08-27 11:04
*/
@Service("coreService")
public class CoreServiceImpl implements CoreService {
@Autowired
private CoreMapper coreMapper;
/**
* @Description: 添加操作日志
* @param status 状态
* @param operationLogEnum 日志枚举
* @param loginUser 登陆用户
* @param failReason 失败原因
* @return String
* @Author: DB
* @Date: 2023/8/27 11:08
*/
@Override
public String insertOperationLog(Integer status, OperationLogEnum operationLogEnum, LoginUser loginUser, String failReason) {
SysOperationLog operationLog = new SysOperationLog();
operationLog.setId(IdUtils.fastSimpleUUID())
.setCode(operationLogEnum.getCode())
.setInfo(MessageUtils.message(operationLogEnum.getInfo()))
.setDevIp(loginUser.getIpAddr())
.setOperationUserName(loginUser.getUsername());
if (200 == status) {
operationLog.setOperationUserId(loginUser.getUserId())
.setLevel(operationLogEnum.getSuccessLevel())
.setDescription(MessageUtils.message(operationLogEnum.getInfo()) + MessageUtils.message("i18n_baseInfo_success"))
.setResult(true)
.setActionType(1);
} else {
operationLog.setLevel(operationLogEnum.getExceptionLevel())
.setDescription(MessageUtils.message(operationLogEnum.getInfo()) + MessageUtils.message("i18n_baseInfo_failed"))
.setFailReason(failReason)
.setResult(false)
.setActionType(2);
}
operationLog.setCreateUserId(loginUser.getUserId());
operationLog.setUpdateUserId(loginUser.getUserId());
if (coreMapper.insertOperationLog(operationLog)) {
//TODO:后续可自定义其他操作
}
return operationLog.getId();
}
/**
* @Description: 添加操作日志
* @param operationLog 操作日志
* @return boolean
* @Author DB
* @Date: 2023/8/29 23:46
*/
@Override
public boolean saveOperationLog(SysOperationLog operationLog) {
return coreMapper.insertOperationLog(operationLog);
}
/**
* @Description: 根据用户名获取用户信息
* @param username 用户名
* @return SysUser
* @Author DB
* @Date: 2023/8/27 23:37
*/
@Override
public SysUser getSysUser(String username, UserType userType) {
return coreMapper.getSysUser(username, userType);
}
/**
* @Description: 更新登录地址
* @param ipAddr 地址
* @param username 用户名
* @author DB
* @Date: 2023/8/28 0028 13:48
*/
@Override
public void updateSysUserLoginIp(String ipAddr, String username) {
coreMapper.updateSysUserLoginIp(ipAddr, username);
}
/**
* @Description: 根据键名查询参数配置信息
* @param configKey
* @return String
* @author DB
* @Date: 2023/6/7 0007 15:59
*/
@Override
public SysConfig selectConfigByKey(String configKey) {
return coreMapper.selectConfigByKey(configKey);
}
/**
* 新增参数配置
*
* @param config 参数配置信息
*/
@Override
public void insertConfig(SysConfig config) {
LoginUser loginUser = SecurityUtils.getInstance().getLoginUser();
config.setCreateUserId(null == loginUser ? "1" : loginUser.getUserId());
config.setUpdateUserId(null == loginUser ? "1" : loginUser.getUserId());
coreMapper.insertConfig(config);
}
/**
* 修改参数配置
*
* @param config 参数配置信息
*/
@Override
public void updateConfig(SysConfig config) {
LoginUser loginUser = SecurityUtils.getInstance().getLoginUser();
config.setUpdateUserId(null == loginUser ? "1" : loginUser.getUserId());
coreMapper.updateConfig(config);
}
/**
* 批量删除参数信息
*
* @param keys 需要删除的参数ID
*/
@Override
@Transactional(rollbackFor = Exception.class)
public void deleteConfigByKeys(String[] keys) {
List<String> keyList = Arrays.asList(keys);
List<SysConfig> sysConfigList = coreMapper.selectConfigByKeys(keyList);
sysConfigList.forEach(item -> {
if (item.getConfigType()) {
throw new ServiceException(String.format("内置参数【%1$s】不能删除 ", item.getConfigKey()));
}
});
coreMapper.deleteConfigByKeys(keyList);
}
/**
* @descriptions 新增系统用户
* @author DB
* @date 2023/09/08 15:04
* @param sysUser 系统用户参数
*/
@Override
public void insertSysUser(SysUser sysUser) {
coreMapper.insertSysUser(sysUser);
}
/**
* @descriptions 修改系统用户
* @author DB
* @date 2023/09/08 17:20
* @param sysUser 系统用户参数
*/
@Override
public void updateSysUser(SysUser sysUser) {
coreMapper.updateSysUser(sysUser);
}
/**
* @descriptions 根据主键删除用户
* @author DB
* @date 2023/09/08 17:50
* @param id 主键
*/
@Override
public void removeSysUserById(String id) {
coreMapper.removeSysUserById(id);
}
}

View File

@ -0,0 +1,149 @@
package com.cpop.core.service.impl;
import com.cpop.core.service.RedisService;
import org.springframework.data.redis.core.BoundSetOperations;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.integration.redis.util.RedisLockRegistry;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
/**
* spring redis 工具类
*
* @author DB
**/
@SuppressWarnings(value = {"unchecked", "rawtypes"})
@Service("redisService")
public class RedisServiceImpl implements RedisService {
@Resource
public RedisTemplate redisTemplate;
@Resource
private RedisLockRegistry redisLockRegistry;
@Override
public <T> void setCacheObject(final String key, final T value) {
redisTemplate.opsForValue().set(key, value);
}
@Override
public <T> void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit) {
redisTemplate.opsForValue().set(key, value, timeout, timeUnit);
}
@Override
public boolean expire(final String key, final long timeout) {
return expire(key, timeout, TimeUnit.SECONDS);
}
@Override
public boolean expire(final String key, final long timeout, final TimeUnit unit) {
return Boolean.TRUE.equals(redisTemplate.expire(key, timeout, unit));
}
@Override
public long getExpire(final String key) {
return redisTemplate.getExpire(key);
}
@Override
public Boolean hasKey(String key) {
return redisTemplate.hasKey(key);
}
@Override
public <T> T getCacheObject(final String key) {
ValueOperations<String, T> operation = redisTemplate.opsForValue();
return operation.get(key);
}
@Override
public boolean deleteObject(final String key) {
return Boolean.TRUE.equals(redisTemplate.delete(key));
}
@Override
public long deleteObject(final Collection collection) {
return redisTemplate.delete(collection);
}
@Override
public <T> long setCacheList(final String key, final List<T> dataList) {
Long count = redisTemplate.opsForList().rightPushAll(key, dataList);
return count == null ? 0 : count;
}
@Override
public <T> List<T> getCacheList(final String key) {
return redisTemplate.opsForList().range(key, 0, -1);
}
@Override
public <T> BoundSetOperations<String, T> setCacheSet(final String key, final Set<T> dataSet) {
BoundSetOperations<String, T> setOperation = redisTemplate.boundSetOps(key);
for (T t : dataSet) {
setOperation.add(t);
}
return setOperation;
}
@Override
public <T> Set<T> getCacheSet(final String key) {
return redisTemplate.opsForSet().members(key);
}
@Override
public <T> void setCacheMap(final String key, final Map<String, T> dataMap) {
if (dataMap != null) {
redisTemplate.opsForHash().putAll(key, dataMap);
}
}
@Override
public <T> Map<String, T> getCacheMap(final String key) {
return redisTemplate.opsForHash().entries(key);
}
@Override
public <T> void setCacheMapValue(final String key, final String hKey, final T value) {
redisTemplate.opsForHash().put(key, hKey, value);
}
@Override
public <T> T getCacheMapValue(final String key, final String hKey) {
HashOperations<String, String, T> opsForHash = redisTemplate.opsForHash();
return opsForHash.get(key, hKey);
}
@Override
public <T> List<T> getMultiCacheMapValue(final String key, final Collection<Object> hKeys) {
return redisTemplate.opsForHash().multiGet(key, hKeys);
}
@Override
public Collection<String> keys(final String pattern) {
return redisTemplate.keys(pattern);
}
/**
* @Description: 分布式锁
* @param key
* @return
* @Author DB
* @Date: 2023/4/12 22:23
*/
@Override
public Lock distributedLock(String key) {
return redisLockRegistry.obtain(key);
}
}

View File

@ -0,0 +1,41 @@
package com.cpop.core.strategy;
import com.cpop.common.utils.StringUtils;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.TreeNode;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.node.*;
import lombok.NoArgsConstructor;
import java.io.IOException;
/**
* 反序列化时 将数组转为逗号拼接 ["0","1","2"] -> "0","1","2"
*
* @author Lynn
* @date 2022/11/8 14:36
*/
@NoArgsConstructor
public class ArrayToStringDeserializer extends JsonDeserializer<String> {
@Override
public String deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
TreeNode treeNode = jsonParser.getCodec().readTree(jsonParser);
if (treeNode instanceof IntNode) {
return ((IntNode) treeNode).asText();
} else if (treeNode instanceof LongNode) {
return ((LongNode) treeNode).asText();
} else if (treeNode instanceof DoubleNode) {
return ((DoubleNode) treeNode).asText();
} else if (treeNode instanceof ArrayNode) {
//字符串数组会多出两个引号需要手动去除
return StringUtils.replace(StringUtils.join((ArrayNode) treeNode, ","), "\"", "");
} else if (treeNode instanceof TextNode) {
return ((TextNode) treeNode).asText();
}
return "";
}
}

View File

@ -0,0 +1,28 @@
package com.cpop.core.strategy;
import com.cpop.common.utils.StringUtils;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import lombok.NoArgsConstructor;
import java.io.IOException;
/**
* 序列化时 逗号拼接转数组 "0","1","2" -> ["0","1","2"]
*
* @author Lynn
* @date 2022/11/8 14:45
*/
@NoArgsConstructor
public class StringToArraySerializer extends JsonSerializer<String> {
@Override
public void serialize(String str, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
if (StringUtils.isNotBlank(str)) {
String[] split = str.split(",");
jsonGenerator.writeArray(split, 0, split.length);
}
}
}

View File

@ -0,0 +1,25 @@
package com.cpop.core.utils;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
/**
* 获取i18n资源文件
*
* @author DB
*/
public class MessageUtils {
/**
* 根据消息键和参数 获取消息 委托给spring messageSource
*
* @param code 消息键
* @param args 参数
* @return 获取国际化翻译值
*/
public static String message(String code, Object... args) {
MessageSource messageSource = SpringUtils.getBean(MessageSource.class);
return messageSource.getMessage(code, args, LocaleContextHolder.getLocale());
}
}

View File

@ -0,0 +1,44 @@
package com.cpop.core.utils;
import com.cpop.core.base.entity.LoginUser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
/**
* @author: DB
* @date: 2022-09-26 23:06
* @Description:
*/
public class SecurityUtils {
private final static Logger logger = LoggerFactory.getLogger(SecurityUtils.class);
private SecurityUtils() {
}
private static class securityUtilsInstance {
private static final SecurityUtils INSTANCE = new SecurityUtils();
}
public static SecurityUtils getInstance() {
return securityUtilsInstance.INSTANCE;
}
/**
* @author LOST.yuan
* @Description: 获取登录用户信息
* @date 23:07 2022/9/26
* @return {@link LoginUser}
**/
public LoginUser getLoginUser() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (null != authentication.getPrincipal()) {
return (LoginUser) authentication.getPrincipal();
} else {
return (LoginUser) authentication.getDetails();
}
}
}

View File

@ -0,0 +1,189 @@
package com.cpop.core.utils;
import com.cpop.common.utils.StringUtils;
import com.cpop.core.base.entity.BaseEntity;
import com.cpop.core.base.exception.BaseException;
import com.mybatisflex.core.service.IService;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
/**
* spring工具类 方便在非spring管理环境中获取bean
*
* @author DB
*/
@Component
public final class SpringUtils implements BeanFactoryPostProcessor, ApplicationContextAware {
/**
* Spring应用上下文环境
*/
private static ConfigurableListableBeanFactory beanFactory;
private static ApplicationContext applicationContext;
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
SpringUtils.beanFactory = beanFactory;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
SpringUtils.applicationContext = applicationContext;
}
/**
* 获取对象
*
* @param name
* @return Object 一个以所给名字注册的bean的实例
* @throws BeansException
*/
@SuppressWarnings("unchecked")
public static <T> T getBean(String name) throws BeansException {
return (T) beanFactory.getBean(name);
}
/**
* 获取类型为requiredType的对象
*
* @param clz
* @return
* @throws BeansException
*/
public static <T> T getBean(Class<T> clz) throws BeansException {
return (T) beanFactory.getBean(clz);
}
/**
* 如果BeanFactory包含一个与所给名称匹配的bean定义则返回true
*
* @param name
* @return boolean
*/
public static boolean containsBean(String name) {
return beanFactory.containsBean(name);
}
/**
* 判断以给定名字注册的bean定义是一个singleton还是一个prototype 如果与给定名字相应的bean定义没有被找到将会抛出一个异常NoSuchBeanDefinitionException
*
* @param name
* @return boolean
* @throws NoSuchBeanDefinitionException
*/
public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException {
return beanFactory.isSingleton(name);
}
/**
* @param name
* @return Class 注册对象的类型
* @throws NoSuchBeanDefinitionException
*/
public static Class<?> getType(String name) throws NoSuchBeanDefinitionException {
return beanFactory.getType(name);
}
/**
* 如果给定的bean名字在bean定义中有别名则返回这些别名
*
* @param name
* @return
* @throws NoSuchBeanDefinitionException
*/
public static String[] getAliases(String name) throws NoSuchBeanDefinitionException {
return beanFactory.getAliases(name);
}
/**
* 获取aop代理对象
*
* @param invoker
* @return
*/
@SuppressWarnings("unchecked")
public static <T> T getAopProxy(T invoker) {
return (T) AopContext.currentProxy();
}
/**
* 获取当前的环境配置无配置返回null
*
* @return 当前的环境配置
*/
public static String[] getActiveProfiles() {
return applicationContext.getEnvironment().getActiveProfiles();
}
/**
* 获取当前的环境配置当有多个环境配置时只获取第一个
*
* @return 当前的环境配置
*/
public static String getActiveProfile() {
final String[] activeProfiles = getActiveProfiles();
return StringUtils.isNotEmpty(activeProfiles) ? activeProfiles[0] : null;
}
/**
* @param clazz
* @param <T>
* @return 返回Spring容器中service对象防止Service循环调用出现的异常
*/
public <T extends BaseEntity> IService<T> getServiceBean(Class<T> clazz) {
String name = clazz.getSimpleName();
String service = nameToService(name);
return getBean(service);
}
private String nameToService(String name) {
String entity = name.substring(0, 1).toLowerCase() + name.substring(1);
return entity.replaceAll("", "") + "Service";
}
public <T extends Object, E> List<T> copyProperties(List<E> sources, Class<T> clazz) {
List<T> list = new ArrayList<>();
if (sources == null || sources.isEmpty()) {
return list;
}
for (E item : sources) {
T t = null;
try {
t = clazz.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
throw new BaseException(e.getMessage());
}
BeanUtils.copyProperties(item, t);
list.add(t);
}
return list;
}
/**
* StringToList
*
* @param longs
* @return
*/
public List<Long> stringToLongList(String longs) {
if (StringUtils.isBlank(longs)) {
return null;
}
String[] split = longs.split(",");
return Arrays.stream(split).map(Long::valueOf).collect(Collectors.toList());
}
}

View File

@ -0,0 +1,71 @@
package com.cpop.core.utils.file;
import org.apache.commons.lang3.StringUtils;
import java.io.File;
/**
* 文件类型工具类
*
* @author DB
*/
public class FileTypeUtils {
public FileTypeUtils() {
}
private static class FileTypeUtilsInstance {
private static final FileTypeUtils INSTANCE = new FileTypeUtils();
}
public static FileTypeUtils getInstance() {
return FileTypeUtilsInstance.INSTANCE;
}
/**
* 获取文件类型
*
* @param file 文件名
* @return 后缀不含".")
*/
public String getFileType(File file) {
if (null == file) {
return StringUtils.EMPTY;
}
return getFileType(file.getName());
}
/**
* 获取文件类型
* @param fileName 文件名
* @return 后缀不含".")
*/
public String getFileType(String fileName) {
int separatorIndex = fileName.lastIndexOf(".");
if (separatorIndex < 0) {
return "";
}
return fileName.substring(separatorIndex + 1).toLowerCase();
}
/**
* 获取文件类型
*
* @param photoByte 文件字节码
* @return 后缀不含".")
*/
public String getFileExtendName(byte[] photoByte) {
String strFileExtendName = "JPG";
if ((photoByte[0] == 71) && (photoByte[1] == 73) && (photoByte[2] == 70) && (photoByte[3] == 56)
&& ((photoByte[4] == 55) || (photoByte[4] == 57)) && (photoByte[5] == 97)) {
strFileExtendName = "GIF";
} else if ((photoByte[6] == 74) && (photoByte[7] == 70) && (photoByte[8] == 73) && (photoByte[9] == 70)) {
strFileExtendName = "JPG";
} else if ((photoByte[0] == 66) && (photoByte[1] == 77)) {
strFileExtendName = "BMP";
} else if ((photoByte[1] == 80) && (photoByte[2] == 78) && (photoByte[3] == 71)) {
strFileExtendName = "PNG";
}
return strFileExtendName;
}
}

View File

@ -0,0 +1,207 @@
package com.cpop.core.utils.file;
import com.cpop.core.config.CpopConfig;
import com.cpop.common.constant.Constants;
import com.cpop.common.utils.DateUtils;
import com.cpop.common.utils.StringUtils;
import com.cpop.core.base.exception.file.FileNameLengthLimitExceededException;
import com.cpop.core.base.exception.file.FileSizeLimitExceededException;
import com.cpop.core.base.exception.file.InvalidExtensionException;
import com.cpop.core.utils.uuid.Seq;
import org.apache.commons.io.FilenameUtils;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.Objects;
/**
* 文件上传工具类
*
* @author DB
*/
public class FileUploadUtils {
public FileUploadUtils() {
}
private static class FileUploadUtilsInstance {
private static final FileUploadUtils INSTANCE = new FileUploadUtils();
}
public static FileUploadUtils getInstance() {
return FileUploadUtilsInstance.INSTANCE;
}
/**
* 默认大小 50M
*/
public static final long DEFAULT_MAX_SIZE = 50 * 1024 * 1024;
/**
* 默认的文件名最大长度 100
*/
public static final int DEFAULT_FILE_NAME_LENGTH = 100;
/**
* 默认上传的地址
*/
private static String defaultBaseDir = CpopConfig.getProfile();
public void setDefaultBaseDir(String defaultBaseDir) {
FileUploadUtils.defaultBaseDir = defaultBaseDir;
}
public String getDefaultBaseDir() {
return defaultBaseDir;
}
/**
* 以默认配置进行文件上传
*
* @param file 上传的文件
* @return 文件名称
* @throws Exception
*/
public final String upload(MultipartFile file) throws IOException {
try {
return upload(getDefaultBaseDir(), file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION);
} catch (Exception e) {
throw new IOException(e.getMessage(), e);
}
}
/**
* 根据文件路径上传
*
* @param baseDir 相对应用的基目录
* @param file 上传的文件
* @return 文件名称
* @throws IOException
*/
public final String upload(String baseDir, MultipartFile file) throws IOException {
try {
return upload(baseDir, file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION);
} catch (Exception e) {
throw new IOException(e.getMessage(), e);
}
}
/**
* 文件上传
*
* @param baseDir 相对应用的基目录
* @param file 上传的文件
* @param allowedExtension 上传文件类型
* @return 返回上传成功的文件名
* @throws FileSizeLimitExceededException 如果超出最大大小
* @throws FileNameLengthLimitExceededException 文件名太长
* @throws IOException 比如读写文件出错时
* @throws InvalidExtensionException 文件校验异常
*/
public final String upload(String baseDir, MultipartFile file, String[] allowedExtension)
throws FileSizeLimitExceededException, IOException, FileNameLengthLimitExceededException,
InvalidExtensionException {
int fileNameLength = Objects.requireNonNull(file.getOriginalFilename()).length();
if (fileNameLength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH) {
throw new FileNameLengthLimitExceededException(FileUploadUtils.DEFAULT_FILE_NAME_LENGTH);
}
assertAllowed(file, allowedExtension);
String fileName = extractFilename(file);
String absPath = getAbsoluteFile(baseDir, fileName).getAbsolutePath();
file.transferTo(Paths.get(absPath));
return getPathFileName(baseDir, fileName);
}
/**
* 编码文件名
*/
public final String extractFilename(MultipartFile file) {
return StringUtils.format("{}/{}_{}.{}", DateUtils.datePath(),
FilenameUtils.getBaseName(file.getOriginalFilename()), Seq.getId(Seq.uploadSeqType), getExtension(file));
}
public final File getAbsoluteFile(String uploadDir, String fileName) throws IOException {
File desc = new File(uploadDir + File.separator + fileName);
if (!desc.exists()) {
if (!desc.getParentFile().exists()) {
desc.getParentFile().mkdirs();
}
}
return desc;
}
public final String getPathFileName(String uploadDir, String fileName) throws IOException {
int dirLastIndex = CpopConfig.getProfile().length() + 1;
String currentDir = StringUtils.substring(uploadDir, dirLastIndex);
return Constants.RESOURCE_PREFIX + "/" + currentDir + "/" + fileName;
}
/**
* 文件大小校验
*
* @param file 上传的文件
* @return
* @throws FileSizeLimitExceededException 如果超出最大大小
* @throws InvalidExtensionException
*/
public final void assertAllowed(MultipartFile file, String[] allowedExtension)
throws FileSizeLimitExceededException, InvalidExtensionException {
long size = file.getSize();
if (size > DEFAULT_MAX_SIZE) {
throw new FileSizeLimitExceededException(DEFAULT_MAX_SIZE / 1024 / 1024);
}
String fileName = file.getOriginalFilename();
String extension = getExtension(file);
if (allowedExtension != null && !isAllowedExtension(extension, allowedExtension)) {
if (allowedExtension == MimeTypeUtils.IMAGE_EXTENSION) {
throw new InvalidExtensionException.InvalidImageExtensionException(allowedExtension, extension,
fileName);
} else if (allowedExtension == MimeTypeUtils.FLASH_EXTENSION) {
throw new InvalidExtensionException.InvalidFlashExtensionException(allowedExtension, extension,
fileName);
} else if (allowedExtension == MimeTypeUtils.MEDIA_EXTENSION) {
throw new InvalidExtensionException.InvalidMediaExtensionException(allowedExtension, extension,
fileName);
} else if (allowedExtension == MimeTypeUtils.VIDEO_EXTENSION) {
throw new InvalidExtensionException.InvalidVideoExtensionException(allowedExtension, extension,
fileName);
} else {
throw new InvalidExtensionException(allowedExtension, extension, fileName);
}
}
}
/**
* 判断MIME类型是否是允许的MIME类型
*
* @param extension
* @param allowedExtension
* @return
*/
public final boolean isAllowedExtension(String extension, String[] allowedExtension) {
for (String str : allowedExtension) {
if (str.equalsIgnoreCase(extension)) {
return true;
}
}
return false;
}
/**
* 获取文件名的后缀
*
* @param file 表单文件
* @return 后缀名
*/
public final String getExtension(MultipartFile file) {
String extension = FilenameUtils.getExtension(file.getOriginalFilename());
if (StringUtils.isEmpty(extension)) {
extension = MimeTypeUtils.getExtension(Objects.requireNonNull(file.getContentType()));
}
return extension;
}
}

View File

@ -0,0 +1,312 @@
package com.cpop.core.utils.file;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.read.listener.ReadListener;
import com.cpop.common.utils.DateUtils;
import com.cpop.common.utils.StringUtils;
import com.cpop.core.base.exception.UtilException;
import com.cpop.core.config.CpopConfig;
import com.cpop.core.utils.uuid.IdUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.poi.ss.formula.functions.T;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.List;
/**
* 文件处理工具类
*
* @author DB
*/
public class FileUtils {
private static final Logger LOGGER = LoggerFactory.getLogger(FileUtils.class);
public FileUtils() {
}
private static class FileUtilsInstance {
private static final FileUtils INSTANCE = new FileUtils();
}
public static FileUtils getInstance() {
return FileUtilsInstance.INSTANCE;
}
public static String FILENAME_PATTERN = "[a-zA-Z0-9_\\-\\|\\.\\u4e00-\\u9fa5]+";
/**
* 输出指定文件的byte数组
*
* @param filePath 文件路径
* @param os 输出流
* @return
*/
public void writeBytes(String filePath, OutputStream os) throws IOException {
FileInputStream fis = null;
try {
File file = new File(filePath);
if (!file.exists()) {
throw new FileNotFoundException(filePath);
}
fis = new FileInputStream(file);
byte[] b = new byte[1024];
int length;
while ((length = fis.read(b)) > 0) {
os.write(b, 0, length);
}
} catch (IOException e) {
throw e;
} finally {
IOUtils.close(os);
IOUtils.close(fis);
}
}
/**
* 写数据到文件中
*
* @param data 数据
* @return 目标文件
* @throws IOException IO异常
*/
public String writeImportBytes(byte[] data) throws IOException {
return writeBytes(data, CpopConfig.getImportPath());
}
/**
* 写数据到文件中
*
* @param data 数据
* @param uploadDir 目标文件
* @return 目标文件
* @throws IOException IO异常
*/
public String writeBytes(byte[] data, String uploadDir) throws IOException {
FileOutputStream fos = null;
String pathName = "";
try {
String extension = getFileExtendName(data);
pathName = DateUtils.datePath() + "/" + IdUtils.fastUUID() + "." + extension;
File file = FileUploadUtils.getInstance().getAbsoluteFile(uploadDir, pathName);
fos = new FileOutputStream(file);
fos.write(data);
} finally {
IOUtils.close(fos);
}
return FileUploadUtils.getInstance().getPathFileName(uploadDir, pathName);
}
/**
* 删除文件
*
* @param filePath 文件
* @return
*/
public boolean deleteFile(String filePath) {
boolean flag = false;
File file = new File(filePath);
// 路径为文件且不为空则进行删除
if (file.isFile() && file.exists()) {
flag = file.delete();
}
return flag;
}
/**
* 文件名称验证
*
* @param filename 文件名称
* @return true 正常 false 非法
*/
public boolean isValidFilename(String filename) {
return filename.matches(FILENAME_PATTERN);
}
/**
* 检查文件是否可下载
*
* @param resource 需要下载的文件
* @return true 正常 false 非法
*/
public boolean checkAllowDownload(String resource) {
// 禁止目录上跳级别
if (StringUtils.contains(resource, "..")) {
return false;
}
// 检查允许下载的文件规则
if (ArrayUtils.contains(MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION, FileTypeUtils.getInstance().getFileType(resource))) {
return true;
}
// 不在允许下载的文件规则
return false;
}
/**
* 下载文件名重新编码
*
* @param request 请求对象
* @param fileName 文件名
* @return 编码后的文件名
*/
public String setFileDownloadHeader(HttpServletRequest request, String fileName) throws UnsupportedEncodingException {
final String agent = request.getHeader("USER-AGENT");
String filename = fileName;
if (agent.contains("MSIE")) {
// IE浏览器
filename = URLEncoder.encode(filename, "utf-8");
filename = filename.replace("+", " ");
} else if (agent.contains("Firefox")) {
// 火狐浏览器
filename = new String(fileName.getBytes(), "ISO8859-1");
} else if (agent.contains("Chrome")) {
// google浏览器
filename = URLEncoder.encode(filename, "utf-8");
} else {
// 其它浏览器
filename = URLEncoder.encode(filename, "utf-8");
}
return filename;
}
/**
* 下载文件名重新编码
*
* @param response 响应对象
* @param realFileName 真实文件名
*/
public void setAttachmentResponseHeader(HttpServletResponse response, String realFileName) throws UnsupportedEncodingException {
String percentEncodedFileName = percentEncode(realFileName);
StringBuilder contentDispositionValue = new StringBuilder();
contentDispositionValue.append("attachment; filename=")
.append(percentEncodedFileName)
.append(";")
.append("filename*=")
.append("utf-8''")
.append(percentEncodedFileName);
response.addHeader("Access-Control-Expose-Headers", "Content-Disposition,download-filename");
response.setHeader("Content-disposition", contentDispositionValue.toString());
response.setHeader("download-filename", percentEncodedFileName);
}
/**
* 百分号编码工具方法
*
* @param s 需要百分号编码的字符串
* @return 百分号编码后的字符串
*/
public String percentEncode(String s) throws UnsupportedEncodingException {
String encode = URLEncoder.encode(s, StandardCharsets.UTF_8.toString());
return encode.replaceAll("\\+", "%20");
}
/**
* 获取图像后缀
*
* @param photoByte 图像数据
* @return 后缀名
*/
public String getFileExtendName(byte[] photoByte) {
String strFileExtendName = "jpg";
if ((photoByte[0] == 71) && (photoByte[1] == 73) && (photoByte[2] == 70) && (photoByte[3] == 56)
&& ((photoByte[4] == 55) || (photoByte[4] == 57)) && (photoByte[5] == 97)) {
strFileExtendName = "gif";
} else if ((photoByte[6] == 74) && (photoByte[7] == 70) && (photoByte[8] == 73) && (photoByte[9] == 70)) {
strFileExtendName = "jpg";
} else if ((photoByte[0] == 66) && (photoByte[1] == 77)) {
strFileExtendName = "bmp";
} else if ((photoByte[1] == 80) && (photoByte[2] == 78) && (photoByte[3] == 71)) {
strFileExtendName = "png";
}
return strFileExtendName;
}
/**
* 获取文件名称 /profile/upload/2022/04/16/ruoyi.png -- ruoyi.png
*
* @param fileName 路径名称
* @return 没有文件路径的名称
*/
public String getName(String fileName) {
if (fileName == null) {
return null;
}
int lastUnixPos = fileName.lastIndexOf('/');
int lastWindowsPos = fileName.lastIndexOf('\\');
int index = Math.max(lastUnixPos, lastWindowsPos);
return fileName.substring(index + 1);
}
/**
* 获取不带后缀文件名称 /profile/upload/2022/04/16/ruoyi.png -- ruoyi
*
* @param fileName 路径名称
* @return 没有文件路径和后缀的名称
*/
public String getNameNotSuffix(String fileName) {
if (fileName == null) {
return null;
}
String baseName = FilenameUtils.getBaseName(fileName);
return baseName;
}
/**
* @Description: 下载Excel
* @param response
* @return
* @author DB
* @Date: 2023/7/14 0014 13:42
*/
public void downloadExcel(HttpServletResponse response, List<T> list) {
// 写入数据
try {
response.setContentType("application/vnd.excel");
response.setCharacterEncoding("utf-8");
String fileName = URLEncoder.encode("file", "UTF-8").replaceAll("\\+", "%20");
response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
EasyExcel.write(response.getOutputStream(), T.class).sheet("sheet1").doWrite(list);
} catch (IOException e) {
throw new UtilException(e);
}
}
/**
* @Description: 读取Excel
* @param filePath
* @param list
* @return
* @author DB
* @Date: 2023/7/14 0014 13:53
*/
public void readExcel(String filePath, List<T> list) {
//excel文件路径
File file = new File(filePath);
EasyExcel.read(file, T.class, new ReadListener<T>() {
@Override
public void invoke(T t, AnalysisContext analysisContext) {
list.add(t);
}
@Override
public void doAfterAllAnalysed(AnalysisContext analysisContext) {
LOGGER.info("read finished");
}
}).sheet("sheet1").doRead();
}
}

View File

@ -0,0 +1,57 @@
package com.cpop.core.utils.file;
/**
* 媒体类型工具类
*
* @author DB
*/
public class MimeTypeUtils {
public static final String IMAGE_PNG = "image/png";
public static final String IMAGE_JPG = "image/jpg";
public static final String IMAGE_JPEG = "image/jpeg";
public static final String IMAGE_BMP = "image/bmp";
public static final String IMAGE_GIF = "image/gif";
public static final String[] IMAGE_EXTENSION = {"bmp", "gif", "jpg", "jpeg", "png"};
public static final String[] FLASH_EXTENSION = {"swf", "flv"};
public static final String[] MEDIA_EXTENSION = {"swf", "flv", "mp3", "wav", "wma", "wmv", "mid", "avi", "mpg",
"asf", "rm", "rmvb"};
public static final String[] VIDEO_EXTENSION = {"mp4", "avi", "rmvb"};
public static final String[] DEFAULT_ALLOWED_EXTENSION = {
// 图片
"bmp", "gif", "jpg", "jpeg", "png",
// word excel powerpoint
"doc", "docx", "xls", "xlsx", "ppt", "pptx", "html", "htm", "txt",
// 压缩文件
"rar", "zip", "gz", "bz2",
// 视频格式
"mp4", "avi", "rmvb",
// pdf
"pdf"};
public static String getExtension(String prefix) {
switch (prefix) {
case IMAGE_PNG:
return "png";
case IMAGE_JPG:
return "jpg";
case IMAGE_JPEG:
return "jpeg";
case IMAGE_BMP:
return "bmp";
case IMAGE_GIF:
return "gif";
default:
return "";
}
}
}

View File

@ -0,0 +1,101 @@
package com.cpop.core.utils.sql;
import com.cpop.common.utils.ServletUtils;
import com.cpop.common.utils.StringUtils;
import com.cpop.core.base.entity.PageDomain;
import com.cpop.core.base.exception.UtilException;
/**
* sql操作工具类
*
* @author DB
*/
public class SqlUtils {
public SqlUtils() {
}
private static class SqlUtilInstance {
private static final SqlUtils INSTANCE = new SqlUtils();
}
public static SqlUtils getInstance() {
return SqlUtilInstance.INSTANCE;
}
/**
* 当前记录起始索引
*/
public final String PAGE_NUM = "page";
/**
* 每页显示记录数
*/
public final String PAGE_SIZE = "pageSize";
/**
* 排序列
*/
public final String ORDER_BY_COLUMN = "orderByColumn";
/**
* 排序的方向 "desc" 或者 "asc".
*/
public final String IS_ASC = "isAsc";
/**
* 分页参数合理化
*/
public final String REASONABLE = "reasonable";
/**
* 仅支持字母数字下划线空格逗号小数点支持多个字段排序
*/
public final String SQL_PATTERN = "[a-zA-Z0-9_\\ \\,\\.]+";
/**
* 封装分页对象
*/
public PageDomain getPageDomain() {
PageDomain pageDomain = new PageDomain();
pageDomain.setPageNum(ServletUtils.getParameterToInt(PAGE_NUM));
pageDomain.setPageSize(ServletUtils.getParameterToInt(PAGE_SIZE));
pageDomain.setOrderByColumn(null == ServletUtils.getParameter(ORDER_BY_COLUMN) ? null : SqlUtils.getInstance().escapeOrderBySql(ServletUtils.getParameter(ORDER_BY_COLUMN)));
pageDomain.setIsAsc(ServletUtils.getParameter(IS_ASC));
pageDomain.setReasonable(ServletUtils.getParameterToBool(REASONABLE));
return pageDomain;
}
public PageDomain buildPageRequest() {
return getPageDomain();
}
/**
* 检查字符防止注入绕过
*/
public String escapeOrderBySql(String value) {
if (StringUtils.isNotEmpty(value) && !isValidOrderBySql(value)) {
throw new UtilException("参数不符合规范,不能进行查询");
}
return value;
}
/**
* 验证 order by 语法是否符合规范
*/
public boolean isValidOrderBySql(String value) {
return value.matches(SQL_PATTERN);
}
/**
* 设置请求排序数据
*/
public String startOrderBy() {
PageDomain pageDomain = getPageDomain();
if (StringUtils.isNotEmpty(pageDomain.getOrderBy())) {
return escapeOrderBySql(pageDomain.getOrderBy());
}else {
throw new UtilException("获取排序参数失败");
}
}
}

View File

@ -0,0 +1,44 @@
package com.cpop.core.utils.uuid;
/**
* ID生成器工具类
*
* @author DB
*/
public class IdUtils {
/**
* 获取随机UUID
*
* @return 随机UUID
*/
public static String randomUUID() {
return UUID.randomUUID().toString();
}
/**
* 简化的UUID去掉了横线
*
* @return 简化的UUID去掉了横线
*/
public static String simpleUUID() {
return UUID.randomUUID().toString(true);
}
/**
* 获取随机UUID使用性能更好的ThreadLocalRandom生成UUID
*
* @return 随机UUID
*/
public static String fastUUID() {
return UUID.fastUUID().toString();
}
/**
* 简化的UUID去掉了横线使用性能更好的ThreadLocalRandom生成UUID
*
* @return 简化的UUID去掉了横线
*/
public static String fastSimpleUUID() {
return UUID.fastUUID().toString(true);
}
}

View File

@ -0,0 +1,92 @@
package com.cpop.core.utils.uuid;
import com.cpop.common.utils.DateUtils;
import com.cpop.common.utils.StringUtils;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @author DB 序列生成类
*/
public class Seq {
/**
* 通用序列类型
*/
public static final String commSeqType = "COMMON";
/**
* 上传序列类型
*/
public static final String uploadSeqType = "UPLOAD";
/**
* 通用接口序列数
*/
private static AtomicInteger commSeq = new AtomicInteger(1);
/**
* 上传接口序列数
*/
private static AtomicInteger uploadSeq = new AtomicInteger(1);
/**
* 机器标识
*/
private static final String machineCode = "A";
/**
* 获取通用序列号
*
* @return 序列值
*/
public static String getId() {
return getId(commSeqType);
}
/**
* 默认16位序列号 yyMMddHHmmss + 一位机器标识 + 3长度循环递增字符串
*
* @return 序列值
*/
public static String getId(String type) {
AtomicInteger atomicInt = commSeq;
if (uploadSeqType.equals(type)) {
atomicInt = uploadSeq;
}
return getId(atomicInt, 3);
}
/**
* 通用接口序列号 yyMMddHHmmss + 一位机器标识 + length长度循环递增字符串
*
* @param atomicInt 序列数
* @param length 数值长度
* @return 序列值
*/
public static String getId(AtomicInteger atomicInt, int length) {
String result = DateUtils.dateTimeNow();
result += machineCode;
result += getSeq(atomicInt, length);
return result;
}
/**
* 序列循环递增字符串[1, 10 (length)幂次方), 用0左补齐length位数
*
* @return 序列值
*/
private synchronized static String getSeq(AtomicInteger atomicInt, int length) {
// 先取值再+1
int value = atomicInt.getAndIncrement();
// 如果更新后值>=10 (length)幂次方则重置为1
int maxSeq = (int) Math.pow(10, length);
if (atomicInt.get() >= maxSeq) {
atomicInt.set(1);
}
// 转字符串用0左补齐
return StringUtils.padL(value, length);
}
}

View File

@ -0,0 +1,447 @@
package com.cpop.core.utils.uuid;
import com.cpop.core.base.exception.UtilException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Random;
import java.util.concurrent.ThreadLocalRandom;
/**
* 提供通用唯一识别码universally unique identifierUUID实现
*
* @author DB
*/
public final class UUID implements java.io.Serializable, Comparable<UUID> {
private static final long serialVersionUID = -1185015143654744140L;
/**
* SecureRandom 的单例
*/
private static class Holder {
static final SecureRandom numberGenerator = getSecureRandom();
}
/**
* 此UUID的最高64有效位
*/
private final long mostSigBits;
/**
* 此UUID的最低64有效位
*/
private final long leastSigBits;
/**
* 私有构造
*
* @param data 数据
*/
private UUID(byte[] data) {
long msb = 0;
long lsb = 0;
assert data.length == 16 : "data must be 16 bytes in length";
for (int i = 0; i < 8; i++) {
msb = (msb << 8) | (data[i] & 0xff);
}
for (int i = 8; i < 16; i++) {
lsb = (lsb << 8) | (data[i] & 0xff);
}
this.mostSigBits = msb;
this.leastSigBits = lsb;
}
/**
* 使用指定的数据构造新的 UUID
*
* @param mostSigBits 用于 {@code UUID} 的最高有效 64
* @param leastSigBits 用于 {@code UUID} 的最低有效 64
*/
public UUID(long mostSigBits, long leastSigBits) {
this.mostSigBits = mostSigBits;
this.leastSigBits = leastSigBits;
}
/**
* 获取类型 4伪随机生成的UUID 的静态工厂 使用加密的本地线程伪随机数生成器生成该 UUID
*
* @return 随机生成的 {@code UUID}
*/
public static UUID fastUUID() {
return randomUUID(false);
}
/**
* 获取类型 4伪随机生成的UUID 的静态工厂 使用加密的强伪随机数生成器生成该 UUID
*
* @return 随机生成的 {@code UUID}
*/
public static UUID randomUUID() {
return randomUUID(true);
}
/**
* 获取类型 4伪随机生成的UUID 的静态工厂 使用加密的强伪随机数生成器生成该 UUID
*
* @param isSecure 是否使用{@link SecureRandom}如果是可以获得更安全的随机码否则可以得到更好的性能
* @return 随机生成的 {@code UUID}
*/
public static UUID randomUUID(boolean isSecure) {
final Random ng = isSecure ? Holder.numberGenerator : getRandom();
byte[] randomBytes = new byte[16];
ng.nextBytes(randomBytes);
//clear version
randomBytes[6] &= 0x0f;
//set to version 4
randomBytes[6] |= 0x40;
//clear variant
randomBytes[8] &= 0x3f;
//set to IETF variant
randomBytes[8] |= 0x80;
return new UUID(randomBytes);
}
/**
* 根据指定的字节数组获取类型 3基于名称的UUID 的静态工厂
*
* @param name 用于构造 UUID 的字节数组
* @return 根据指定数组生成的 {@code UUID}
*/
public static UUID nameUUIDFromBytes(byte[] name) {
MessageDigest md;
try {
md = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException nsae) {
throw new InternalError("MD5 not supported");
}
byte[] md5Bytes = md.digest(name);
//clear version
md5Bytes[6] &= 0x0f;
//set to version 3
md5Bytes[6] |= 0x30;
//clear variant
md5Bytes[8] &= 0x3f;
//set to IETF variant
md5Bytes[8] |= 0x80;
return new UUID(md5Bytes);
}
/**
* 根据 {@link #toString()} 方法中描述的字符串标准表示形式创建{@code UUID}
*
* @param name 指定 {@code UUID} 字符串
* @return 具有指定值的 {@code UUID}
* @throws IllegalArgumentException 如果 name {@link #toString} 中描述的字符串表示形式不符抛出此异常
*/
public static UUID fromString(String name) {
String[] components = name.split("-");
if (components.length != 5) {
throw new IllegalArgumentException("Invalid UUID string: " + name);
}
for (int i = 0; i < 5; i++) {
components[i] = "0x" + components[i];
}
long mostSigBits = Long.decode(components[0]).longValue();
mostSigBits <<= 16;
mostSigBits |= Long.decode(components[1]).longValue();
mostSigBits <<= 16;
mostSigBits |= Long.decode(components[2]).longValue();
long leastSigBits = Long.decode(components[3]).longValue();
leastSigBits <<= 48;
leastSigBits |= Long.decode(components[4]).longValue();
return new UUID(mostSigBits, leastSigBits);
}
/**
* 返回此 UUID 128 位值中的最低有效 64
*
* @return UUID 128 位值中的最低有效 64
*/
public long getLeastSignificantBits() {
return leastSigBits;
}
/**
* 返回此 UUID 128 位值中的最高有效 64
*
* @return UUID 128 位值中最高有效 64
*/
public long getMostSignificantBits() {
return mostSigBits;
}
/**
* 与此 {@code UUID} 相关联的版本号. 版本号描述此 {@code UUID} 是如何生成的
* <p>
* 版本号具有以下含意:
* <ul>
* <li>1 基于时间的 UUID
* <li>2 DCE 安全 UUID
* <li>3 基于名称的 UUID
* <li>4 随机生成的 UUID
* </ul>
*
* @return {@code UUID} 的版本号
*/
public int version() {
// Version is bits masked by 0x000000000000F000 in MS long
return (int) ((mostSigBits >> 12) & 0x0f);
}
/**
* 与此 {@code UUID} 相关联的变体号变体号描述 {@code UUID} 的布局
* <p>
* 变体号具有以下含意
* <ul>
* <li>0 NCS 向后兼容保留
* <li>2 <a href="http://www.ietf.org/rfc/rfc4122.txt">IETF&nbsp;RFC&nbsp;4122</a>(Leach-Salz), 用于此类
* <li>6 保留微软向后兼容
* <li>7 保留供以后定义使用
* </ul>
*
* @return {@code UUID} 相关联的变体号
*/
public int variant() {
// This field is composed of a varying number of bits.
// 0 - - Reserved for NCS backward compatibility
// 1 0 - The IETF aka Leach-Salz variant (used by this class)
// 1 1 0 Reserved, Microsoft backward compatibility
// 1 1 1 Reserved for future definition.
return (int) ((leastSigBits >>> (64 - (leastSigBits >>> 62))) & (leastSigBits >> 63));
}
/**
* 与此 UUID 相关联的时间戳值
*
* <p>
* 60 位的时间戳值根据此 {@code UUID} time_lowtime_mid time_hi 字段构造<br>
* 所得到的时间戳以 100 毫微秒为单位 UTC通用协调时间 1582 10 15 日零时开始
*
* <p>
* 时间戳值仅在在基于时间的 UUID version 类型为 1中才有意义<br>
* 如果此 {@code UUID} 不是基于时间的 UUID则此方法抛出 UnsupportedOperationException
*
* @throws UnsupportedOperationException 如果此 {@code UUID} 不是 version 1 UUID
*/
public long timestamp() throws UnsupportedOperationException {
checkTimeBase();
return (mostSigBits & 0x0FFFL) << 48
| ((mostSigBits >> 16) & 0x0FFFFL) << 32
| mostSigBits >>> 32;
}
/**
* 与此 UUID 相关联的时钟序列值
*
* <p>
* 14 位的时钟序列值根据此 UUID clock_seq 字段构造clock_seq 字段用于保证在基于时间的 UUID 中的时间唯一性
* <p>
* {@code clockSequence} 值仅在基于时间的 UUID version 类型为 1中才有意义 如果此 UUID 不是基于时间的 UUID则此方法抛出
* UnsupportedOperationException
*
* @return {@code UUID} 的时钟序列
* @throws UnsupportedOperationException 如果此 UUID version 不为 1
*/
public int clockSequence() throws UnsupportedOperationException {
checkTimeBase();
return (int) ((leastSigBits & 0x3FFF000000000000L) >>> 48);
}
/**
* 与此 UUID 相关的节点值
*
* <p>
* 48 位的节点值根据此 UUID node 字段构造此字段旨在用于保存机器的 IEEE 802 地址该地址用于生成此 UUID 以保证空间唯一性
* <p>
* 节点值仅在基于时间的 UUID version 类型为 1中才有意义<br>
* 如果此 UUID 不是基于时间的 UUID则此方法抛出 UnsupportedOperationException
*
* @return {@code UUID} 的节点值
* @throws UnsupportedOperationException 如果此 UUID version 不为 1
*/
public long node() throws UnsupportedOperationException {
checkTimeBase();
return leastSigBits & 0x0000FFFFFFFFFFFFL;
}
/**
* 返回此{@code UUID} 的字符串表现形式
*
* <p>
* UUID 的字符串表示形式由此 BNF 描述
*
* <pre>
* {@code
* UUID = <time_low>-<time_mid>-<time_high_and_version>-<variant_and_sequence>-<node>
* time_low = 4*<hexOctet>
* time_mid = 2*<hexOctet>
* time_high_and_version = 2*<hexOctet>
* variant_and_sequence = 2*<hexOctet>
* node = 6*<hexOctet>
* hexOctet = <hexDigit><hexDigit>
* hexDigit = [0-9a-fA-F]
* }
* </pre>
*
* </blockquote>
*
* @return {@code UUID} 的字符串表现形式
* @see #toString(boolean)
*/
@Override
public String toString() {
return toString(false);
}
/**
* 返回此{@code UUID} 的字符串表现形式
*
* <p>
* UUID 的字符串表示形式由此 BNF 描述
*
* <pre>
* {@code
* UUID = <time_low>-<time_mid>-<time_high_and_version>-<variant_and_sequence>-<node>
* time_low = 4*<hexOctet>
* time_mid = 2*<hexOctet>
* time_high_and_version = 2*<hexOctet>
* variant_and_sequence = 2*<hexOctet>
* node = 6*<hexOctet>
* hexOctet = <hexDigit><hexDigit>
* hexDigit = [0-9a-fA-F]
* }
* </pre>
*
* </blockquote>
*
* @param isSimple 是否简单模式简单模式为不带'-'的UUID字符串
* @return {@code UUID} 的字符串表现形式
*/
public String toString(boolean isSimple) {
final StringBuilder builder = new StringBuilder(isSimple ? 32 : 36);
// time_low
builder.append(digits(mostSigBits >> 32, 8));
if (!isSimple) {
builder.append('-');
}
// time_mid
builder.append(digits(mostSigBits >> 16, 4));
if (!isSimple) {
builder.append('-');
}
// time_high_and_version
builder.append(digits(mostSigBits, 4));
if (!isSimple) {
builder.append('-');
}
// variant_and_sequence
builder.append(digits(leastSigBits >> 48, 4));
if (!isSimple) {
builder.append('-');
}
// node
builder.append(digits(leastSigBits, 12));
return builder.toString();
}
/**
* 返回此 UUID 的哈希码
*
* @return UUID 的哈希码值
*/
@Override
public int hashCode() {
long hilo = mostSigBits ^ leastSigBits;
return ((int) (hilo >> 32)) ^ (int) hilo;
}
/**
* 将此对象与指定对象比较
* <p>
* 当且仅当参数不为 {@code null}而是一个 UUID 对象具有与此 UUID 相同的 varriant包含相同的值每一位均相同结果才为 {@code true}
*
* @param obj 要与之比较的对象
* @return 如果对象相同则返回 {@code true}否则返回 {@code false}
*/
@Override
public boolean equals(Object obj) {
if ((null == obj) || (obj.getClass() != UUID.class)) {
return false;
}
UUID id = (UUID) obj;
return (mostSigBits == id.mostSigBits && leastSigBits == id.leastSigBits);
}
// Comparison Operations
/**
* 将此 UUID 与指定的 UUID 比较
*
* <p>
* 如果两个 UUID 不同且第一个 UUID 的最高有效字段大于第二个 UUID 的对应字段则第一个 UUID 大于第二个 UUID
*
* @param val 与此 UUID 比较的 UUID
* @return 在此 UUID 小于等于或大于 val 分别返回 -10 1
*/
@Override
public int compareTo(UUID val) {
// The ordering is intentionally set up so that the UUIDs
// can simply be numerically compared as two numbers
return (this.mostSigBits < val.mostSigBits ? -1 :
(this.mostSigBits > val.mostSigBits ? 1 :
(Long.compare(this.leastSigBits, val.leastSigBits))));
}
// -------------------------------------------------------------------------------------------------------------------
// Private method start
/**
* 返回指定数字对应的hex值
*
* @param val
* @param digits
* @return
*/
private static String digits(long val, int digits) {
long hi = 1L << (digits * 4);
return Long.toHexString(hi | (val & (hi - 1))).substring(1);
}
/**
* 检查是否为time-based版本UUID
*/
private void checkTimeBase() {
if (version() != 1) {
throw new UnsupportedOperationException("Not a time-based UUID");
}
}
/**
* 获取{@link SecureRandom}类提供加密的强随机数生成器 (RNG)
*
* @return {@link SecureRandom}
*/
public static SecureRandom getSecureRandom() {
try {
return SecureRandom.getInstance("SHA1PRNG");
} catch (NoSuchAlgorithmException e) {
throw new UtilException(e);
}
}
/**
* 获取随机数生成器对象<br>
* ThreadLocalRandom是JDK 7之后提供并发产生随机数能够解决多个线程发生的竞争争夺
*
* @return {@link ThreadLocalRandom}
*/
public static ThreadLocalRandom getRandom() {
return ThreadLocalRandom.current();
}
}

View File

@ -0,0 +1 @@

View File

@ -0,0 +1,303 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.cpop.core.mapper.CoreMapper">
<!--插入操作日志-->
<insert id="insertOperationLog">
INSERT INTO cp_sys_operation_log
(id, operation_user_id, operation_user_name, operation_user_type, code, info, dev_ip, result, level, action_type, fail_reason, description,
create_time, update_time, create_user_id, update_user_id)
VALUES
(#{id}, #{operationUserId}, #{operationUserName}, #{operationUserType}, #{code}, #{info}, #{devIp}, #{result}, #{level}, #{actionType}, #{failReason}, #{description},
now(), now(), #{createUserId}, #{updateUserId})
</insert>
<!--更新系统用户登录地址-->
<update id="updateSysUserLoginIp">
UPDATE
cp_sys_user
SET
login_ip = #{ipAddr}
WHERE
user_name = #{username}
</update>
<!--根据用户名获取用户信息-->
<select id="getSysUser" resultType="com.cpop.core.base.table.SysUser">
SELECT
id,
user_name AS userName,
password,
nick_name AS nickName,
email,
phone_number AS phoneNumber,
sex,
avatar,
salt,
status,
login_ip AS loginIp,
user_type AS userType,
create_time AS createTime,
create_user_id AS createUserId,
update_time AS updateTime,
update_user_id AS updateUserId
FROM
cp_sys_user
WHERE
is_delete = 0
AND
user_name = #{username}
AND
user_type = #{userType}
</select>
<!--加载参数缓存数据-->
<select id="loadingConfigCache" resultType="com.cpop.core.base.table.SysConfig">
SELECT
config_key AS configKey,
config_name AS configName,
config_value AS configValue,
config_type AS configType,
remarks,
create_time AS createTime,
create_user_id AS createUserId,
update_time AS updateTime,
update_user_id AS updateUserId
FROM
cp_sys_config
</select>
<!--根据键名查询参数配置信息-->
<select id="selectConfigByKey" resultType="com.cpop.core.base.table.SysConfig">
SELECT
config_key AS configKey,
config_name AS configName,
config_value AS configValue,
config_type AS configType,
remarks,
create_time AS createTime,
create_user_id AS createUserId,
update_time AS updateTime,
update_user_id AS updateUserId
FROM
cp_sys_config
WHERE
config_key = #{configKey}
</select>
<!--新增参数配置-->
<insert id="insertConfig">
INSERT INTO
cp_sys_config
(config_key, config_name, config_value, config_type, remarks, create_time, create_user_id, update_time, update_user_id)
VALUES
(#{configKey}, #{configName}, #{configValue}, #{configType}, #{remarks}, now(), #{createUserId}, now(), #{updateUserId})
</insert>
<!--修改参数配置-->
<update id="updateConfig">
UPDATE cp_sys_config
SET
<if test="configName != null and configName != ''">
config_name = #{configName},
</if>
<if test="config_value != null and config_value != ''">
config_value = #{config_value},
</if>
<if test="configType != null and configType != ''">
config_type = #{configType},
</if>
<if test="remarks != null and remarks != ''">
remarks = #{remarks},
</if>
<if test="updateUserId != null and updateUserId != ''">
update_user_id = #{updateUserId},
</if>
update_time = now()
WHERE
config_key = #{configKey}
</update>
<!--根据键查询配置-->
<select id="selectConfigByKeys" resultType="com.pupu.core.base.table.SysConfig">
SELECT
config_key AS configKey,
config_name AS configName,
config_value AS configValue,
config_type AS configType,
remarks,
create_time AS createTime,
create_user_id AS createUserId,
update_time AS updateTime,
update_user_id AS updateUserId
FROM
cp_sys_config
WHERE
config_key IN
<foreach collection="list" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</select>
<!--需要删除的参数ID-->
<delete id="deleteConfigByKeys">
DELETE FROM cp_sys_config
WHERE
config_key IN
<foreach collection="list" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</delete>
<!--新增系统用户-->
<insert id="insertSysUser">
INSERT INTO cp_sys_user
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="id != null and id != ''">
id,
</if>
<if test="userName != null and userName != ''">
user_name,
</if>
<if test="password != null and password != ''">
password,
</if>
<if test="nickName != null and nickName != ''">
nick_name,
</if>
<if test="email != null and email != ''">
email,
</if>
<if test="phoneNumber != null and phoneNumber != ''">
phone_number,
</if>
<if test="sex != null">
sex,
</if>
<if test="avatar != null and avatar != ''">
avatar,
</if>
<if test="salt != null and salt != ''">
salt,
</if>
<if test="status != null">
status,
</if>
<if test="loginIp != null and loginIp != ''">
dev_ip,
</if>
<if test="userType != null">
user_type,
</if>
<if test="createUserId != null and createUserId != ''">
create_user_id,
</if>
<if test="updateUserId != null and updateUserId != ''">
update_user_id,
</if>
create_time,
update_time,
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="id != null and id != ''">
#{id},
</if>
<if test="userName != null and userName != ''">
#{userName},
</if>
<if test="password != null and password != ''">
#{password},
</if>
<if test="nickName != null and nickName != ''">
#{nickName},
</if>
<if test="email != null and email != ''">
#{email},
</if>
<if test="phoneNumber != null and phoneNumber != ''">
#{phoneNumber},
</if>
<if test="sex != null">
#{sex},
</if>
<if test="avatar != null and avatar != ''">
#{avatar},
</if>
<if test="salt != null and salt != ''">
#{salt},
</if>
<if test="status != null">
#{status},
</if>
<if test="loginIp != null and loginIp != ''">
#{loginIp},
</if>
<if test="userType != null">
#{userType},
</if>
<if test="createUserId != null and createUserId != ''">
#{createUserId},
</if>
<if test="updateUserId != null and updateUserId != ''">
#{updateUserId},
</if>
now(),
now(),
</trim>
</insert>
<!--修改系统用户-->
<update id="updateSysUser">
UPDATE cp_sys_user
<set>
<if test="userName != null and userName != ''">
user_name = #{userName},
</if>
<if test="password != null and password != ''">
password = #{password},
</if>
<if test="nickName != null and nickName != ''">
nick_name = #{nickName},
</if>
<if test="email != null and email != ''">
email = #{email},
</if>
<if test="phoneNumber != null and phoneNumber != ''">
phone_number = #{phoneNumber},
</if>
<if test="sex != null">
sex = #{sex},
</if>
<if test="avatar != null and avatar != ''">
avatar = #{avatar},
</if>
<if test="salt != null and salt != ''">
salt = #{salt},
</if>
<if test="status != null">
status = #{status},
</if>
<if test="loginIp != null and loginIp != ''">
dev_ip = #{loginIp},
</if>
<if test="updateUserId != null and updateUserId != ''">
update_user_id = #{updateUserId},
</if>
update_time = now(),
</set>
WHERE
is_delete = 0
AND id = #{id}
</update>
<!--根据主键删除用户(逻辑删除)-->
<update id="removeSysUserById">
UPDATE cp_sys_user
SET
is_delete = 1
WHERE
id = #{id}
</update>
</mapper>

Binary file not shown.

Binary file not shown.

35
pom.xml
View File

@ -17,15 +17,19 @@
<modules>
<module>Cpop-Common</module>
<module>Cpop-Core</module>
</modules>
<properties>
<java.version>1.8</java.version>
<cpop.version>1.0.0</cpop.version>
<snakeyaml.version>2.0</snakeyaml.version>
<common-text.version>1.10.0</common-text.version>
<commons-IO.version>2.13.0</commons-IO.version>
<joda.time.version>2.12.5</joda.time.version>
<fastjson.version>2.0.38</fastjson.version>
<mybatis-flex.version>1.6.4</mybatis-flex.version>
<easyexcel.version>3.3.2</easyexcel.version>
</properties>
<dependencyManagement>
@ -54,6 +58,29 @@
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
<!--Cpop公共包-->
<dependency>
<groupId>com.cpop</groupId>
<artifactId>Cpop-Common</artifactId>
<version>${cpop.version}</version>
</dependency>
<!--Mybatis-flex-->
<dependency>
<groupId>com.mybatis-flex</groupId>
<artifactId>mybatis-flex-spring-boot-starter</artifactId>
<version>${mybatis-flex.version}</version>
</dependency>
<dependency>
<groupId>com.mybatis-flex</groupId>
<artifactId>mybatis-flex-processor</artifactId>
<version>${mybatis-flex.version}</version>
</dependency>
<!--easyExcel-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>${easyexcel.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
@ -62,7 +89,7 @@
<!--Web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--Test-->
<dependency>
@ -76,5 +103,11 @@
<artifactId>snakeyaml</artifactId>
<version>${snakeyaml.version}</version>
</dependency>
<!--Lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
</project>