mirror of
https://github.com/continew-org/continew-admin.git
synced 2025-09-15 01:01:36 +08:00
新增:新增获取图片验证码 API(引入 Redisson、Hutool、Easy Captcha 依赖,详情可见 README 介绍)
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.charles7c.cnadmin.common.config.jackson;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||
import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
|
||||
import com.fasterxml.jackson.databind.ser.std.NumberSerializer;
|
||||
|
||||
/**
|
||||
* 大数值序列化器(针对数值超出 JS 最大或最小值的情况,将其转换为字符串)
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 2022/12/11 13:22
|
||||
*/
|
||||
@JacksonStdImpl
|
||||
public class BigNumberSerializer extends NumberSerializer {
|
||||
|
||||
/** 静态实例 */
|
||||
public static final BigNumberSerializer SERIALIZER_INSTANCE = new BigNumberSerializer(Number.class);
|
||||
/** JS:Number.MAX_SAFE_INTEGER */
|
||||
private static final long MAX_SAFE_INTEGER = 9007199254740991L;
|
||||
/** JS:Number.MIN_SAFE_INTEGER */
|
||||
private static final long MIN_SAFE_INTEGER = -9007199254740991L;
|
||||
|
||||
public BigNumberSerializer(Class<? extends Number> rawType) {
|
||||
super(rawType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(Number value, JsonGenerator gen, SerializerProvider provider) throws IOException {
|
||||
// 序列化为字符串
|
||||
if (value.longValue() > MIN_SAFE_INTEGER && value.longValue() < MAX_SAFE_INTEGER) {
|
||||
super.serialize(value, gen, provider);
|
||||
} else {
|
||||
gen.writeString(value.toString());
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.charles7c.cnadmin.common.config.jackson;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Locale;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
|
||||
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
|
||||
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
|
||||
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
|
||||
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
|
||||
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
|
||||
|
||||
/**
|
||||
* Jackson 配置
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 2022/12/11 13:23
|
||||
*/
|
||||
@Slf4j
|
||||
@Configuration
|
||||
public class JacksonConfiguration {
|
||||
|
||||
/**
|
||||
* 全局配置序列化返回 JSON 处理
|
||||
*/
|
||||
@Bean
|
||||
public Jackson2ObjectMapperBuilderCustomizer customizer() {
|
||||
String dateTimeFormatPattern = "yyyy-MM-dd HH:mm:ss";
|
||||
String dateFormatPattern = "yyyy-MM-dd";
|
||||
String timeFormatPattern = "HH:mm:ss";
|
||||
|
||||
return builder -> {
|
||||
// 针对 java.util.Date 的转换
|
||||
builder.locale(Locale.CHINA);
|
||||
builder.timeZone(TimeZone.getDefault());
|
||||
builder.simpleDateFormat(dateTimeFormatPattern);
|
||||
|
||||
// 针对 Long、BigInteger、BigDecimal 的转换
|
||||
JavaTimeModule javaTimeModule = new JavaTimeModule();
|
||||
javaTimeModule.addSerializer(Long.class, BigNumberSerializer.SERIALIZER_INSTANCE);
|
||||
javaTimeModule.addSerializer(Long.TYPE, BigNumberSerializer.SERIALIZER_INSTANCE);
|
||||
javaTimeModule.addSerializer(BigInteger.class, BigNumberSerializer.SERIALIZER_INSTANCE);
|
||||
javaTimeModule.addSerializer(BigDecimal.class, ToStringSerializer.instance);
|
||||
|
||||
// 针对 LocalDateTime、LocalDate、LocalTime 的转换
|
||||
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(dateTimeFormatPattern);
|
||||
javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(dateTimeFormatter));
|
||||
javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(dateTimeFormatter));
|
||||
|
||||
DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern(dateFormatPattern);
|
||||
javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(dateFormatter));
|
||||
javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(dateFormatter));
|
||||
|
||||
DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern(timeFormatPattern);
|
||||
javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(timeFormatter));
|
||||
javaTimeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(timeFormatter));
|
||||
builder.modules(javaTimeModule);
|
||||
log.info(">>>初始化 Jackson 配置<<<");
|
||||
};
|
||||
}
|
||||
}
|
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.charles7c.cnadmin.common.model.vo;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
|
||||
/**
|
||||
* 响应信息
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 2022/12/10 23:31
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class R<V extends Serializable> implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** 是否成功 */
|
||||
private boolean success;
|
||||
/** 状态码 */
|
||||
private int code;
|
||||
/** 状态信息 */
|
||||
private String msg;
|
||||
/** 返回数据 */
|
||||
private V data;
|
||||
/** 时间戳 */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private LocalDateTime timestamp = LocalDateTime.now();
|
||||
|
||||
/** 成功状态码 */
|
||||
private static final int SUCCESS_CODE = HttpStatus.OK.value();
|
||||
/** 失败状态码 */
|
||||
private static final int FAIL_CODE = HttpStatus.INTERNAL_SERVER_ERROR.value();
|
||||
|
||||
private R(boolean success, int code, String msg, V data) {
|
||||
this.success = success;
|
||||
this.code = code;
|
||||
this.msg = msg;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public static <V extends Serializable> R<V> ok() {
|
||||
return new R<>(true, SUCCESS_CODE, "操作成功", null);
|
||||
}
|
||||
|
||||
public static <V extends Serializable> R<V> ok(V data) {
|
||||
return new R<>(true, SUCCESS_CODE, "操作成功", data);
|
||||
}
|
||||
|
||||
public static <V extends Serializable> R<V> ok(String msg) {
|
||||
return new R<>(true, SUCCESS_CODE, msg, null);
|
||||
}
|
||||
|
||||
public static <V extends Serializable> R<V> ok(String msg, V data) {
|
||||
return new R<>(true, SUCCESS_CODE, msg, data);
|
||||
}
|
||||
|
||||
public static <V extends Serializable> R<V> fail() {
|
||||
return new R<>(false, FAIL_CODE, "操作失败", null);
|
||||
}
|
||||
|
||||
public static <V extends Serializable> R<V> fail(String msg) {
|
||||
return new R<>(false, FAIL_CODE, msg, null);
|
||||
}
|
||||
|
||||
public static <V extends Serializable> R<V> fail(V data) {
|
||||
return new R<>(false, FAIL_CODE, "操作失败", data);
|
||||
}
|
||||
|
||||
public static <V extends Serializable> R<V> fail(String msg, V data) {
|
||||
return new R<>(false, FAIL_CODE, msg, data);
|
||||
}
|
||||
|
||||
public static <V extends Serializable> R<V> fail(int code, String msg) {
|
||||
return new R<>(false, code, msg, null);
|
||||
}
|
||||
}
|
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.charles7c.cnadmin.common.util;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Collection;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import org.redisson.api.*;
|
||||
import org.redisson.config.Config;
|
||||
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
|
||||
/**
|
||||
* Redis 工具类
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 2022/12/11 12:00
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class RedisUtils {
|
||||
|
||||
private static final RedissonClient REDISSON_CLIENT = SpringUtil.getBean(RedissonClient.class);
|
||||
|
||||
/* ################ 查询操作 ################ */
|
||||
/**
|
||||
* 获取缓存的基本对象列表
|
||||
*
|
||||
* @param keyPattern
|
||||
* 缓存键表达式
|
||||
* @return 基本对象列表
|
||||
*/
|
||||
public static Collection<String> keys(final String keyPattern) {
|
||||
Stream<String> stream = REDISSON_CLIENT.getKeys().getKeysStreamByPattern(getNameMapper().map(keyPattern));
|
||||
return stream.map(key -> getNameMapper().unmap(key)).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否存在指定缓存
|
||||
*
|
||||
* @param key
|
||||
* 缓存键
|
||||
*/
|
||||
public static Boolean hasKey(String key) {
|
||||
RKeys rKeys = REDISSON_CLIENT.getKeys();
|
||||
return rKeys.countExists(getNameMapper().map(key)) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取缓存剩余存活时间
|
||||
*
|
||||
* @param key
|
||||
* 缓存键
|
||||
* @return 剩余存活时间
|
||||
*/
|
||||
public static <T> long getTimeToLive(final String key) {
|
||||
RBucket<T> rBucket = REDISSON_CLIENT.getBucket(key);
|
||||
return rBucket.remainTimeToLive();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取缓存的基本对象
|
||||
*
|
||||
* @param key
|
||||
* 缓存键
|
||||
* @return 缓存值
|
||||
*/
|
||||
public static <T> T getCacheObject(final String key) {
|
||||
RBucket<T> rBucket = REDISSON_CLIENT.getBucket(key);
|
||||
return rBucket.get();
|
||||
}
|
||||
|
||||
/* ################ 操作有效期 ################ */
|
||||
/**
|
||||
* 设置过期时间
|
||||
*
|
||||
* @param key
|
||||
* 缓存键
|
||||
* @param timeout
|
||||
* 过期时间
|
||||
* @return true 设置成功;false 设置失败
|
||||
*/
|
||||
public static boolean expire(final String key, final long timeout) {
|
||||
return expire(key, Duration.ofSeconds(timeout));
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置过期时间
|
||||
*
|
||||
* @param key
|
||||
* 缓存键
|
||||
* @param duration
|
||||
* 过期时间
|
||||
* @return true 设置成功;false 设置失败
|
||||
*/
|
||||
public static boolean expire(final String key, final Duration duration) {
|
||||
RBucket rBucket = REDISSON_CLIENT.getBucket(key);
|
||||
return rBucket.expire(duration);
|
||||
}
|
||||
|
||||
/* ################ 操作基本对象 ################ */
|
||||
/**
|
||||
* 缓存基本对象(Integer、String、实体类等)
|
||||
*
|
||||
* @param key
|
||||
* 缓存键
|
||||
* @param value
|
||||
* 缓存值
|
||||
*/
|
||||
public static <T> void setCacheObject(final String key, final T value) {
|
||||
setCacheObject(key, value, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 缓存基本对象,保留当前对象 TTL 有效期
|
||||
*
|
||||
* @param key
|
||||
* 缓存键
|
||||
* @param value
|
||||
* 缓存值
|
||||
* @param isSaveTtl
|
||||
* 是否保留 TTL 有效期(例如: set 之前 ttl 剩余 90,set 之后还是为 90)
|
||||
* @since Redis 6.X 以上使用 setAndKeepTTL 兼容 5.X 方案
|
||||
*/
|
||||
public static <T> void setCacheObject(final String key, final T value, final boolean isSaveTtl) {
|
||||
RBucket<T> bucket = REDISSON_CLIENT.getBucket(key);
|
||||
if (isSaveTtl) {
|
||||
try {
|
||||
bucket.setAndKeepTTL(value);
|
||||
} catch (Exception e) {
|
||||
long timeToLive = bucket.remainTimeToLive();
|
||||
setCacheObject(key, value, Duration.ofMillis(timeToLive));
|
||||
}
|
||||
} else {
|
||||
bucket.set(value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 缓存基本对象(Integer、String、实体类等)
|
||||
*
|
||||
* @param key
|
||||
* 缓存键
|
||||
* @param value
|
||||
* 缓存值
|
||||
* @param duration
|
||||
* 时间
|
||||
*/
|
||||
public static <T> void setCacheObject(final String key, final T value, final Duration duration) {
|
||||
RBatch batch = REDISSON_CLIENT.createBatch();
|
||||
RBucketAsync<T> bucket = batch.getBucket(key);
|
||||
bucket.setAsync(value);
|
||||
bucket.expireAsync(duration);
|
||||
batch.execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除缓存的基本对象
|
||||
*
|
||||
* @param key
|
||||
* 缓存键
|
||||
*/
|
||||
public static boolean deleteCacheObject(final String key) {
|
||||
return REDISSON_CLIENT.getBucket(key).delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除缓存的基本对象列表
|
||||
*
|
||||
* @param keyPattern
|
||||
* 缓存键表达式
|
||||
*/
|
||||
public static void deleteKeys(final String keyPattern) {
|
||||
REDISSON_CLIENT.getKeys().deleteByPattern(getNameMapper().map(keyPattern));
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化缓存键,将各子键用 : 拼接起来
|
||||
*
|
||||
* @param subKeys
|
||||
* 子键列表
|
||||
* @return 缓存键
|
||||
*/
|
||||
public static String formatKey(String... subKeys) {
|
||||
return String.join(":", subKeys);
|
||||
}
|
||||
|
||||
public static NameMapper getNameMapper() {
|
||||
Config config = REDISSON_CLIENT.getConfig();
|
||||
if (config.isClusterConfig()) {
|
||||
return config.useClusterServers().getNameMapper();
|
||||
}
|
||||
return config.useSingleServer().getNameMapper();
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user