feat(biji-houdaun): 实现用户注册、登录、 Markdown 文件和图片上传功能

- 新增用户注册、登录接口及服务实现
- 添加 Markdown 文件创建、更新接口及服务实现
- 实现图片上传、获取接口及服务实现
- 集成 Snowflake ID 生成器
- 添加全局异常处理和统一返回结果封装
- 配置跨域访问和静态资源处理
- 实现基础的 XSS 防护
This commit is contained in:
ikmkj
2025-06-16 20:20:08 +08:00
parent 8a4bf2d245
commit 1ef2e116a6
27 changed files with 1049 additions and 0 deletions

View File

@@ -0,0 +1,49 @@
package com.test.bijihoudaun.util;
import cn.hutool.core.bean.BeanUtil;
import java.util.List;
import java.util.stream.Collectors;
/**
* 实体复制工具类
*/
public class EntityCopier {
/**
* 复制实体属性到另一个实体
* @param source 源实体
* @param target 目标实体
* @param <S> 源类型
* @param <T> 目标类型
*/
public static <S, T> void copyEntityToEntity(S source, T target) {
BeanUtil.copyProperties(source, target);
}
/**
* 复制实体属性到目标类的新实例
* @param source 源实体
* @param targetClass 目标类
* @param <S> 源类型
* @param <T> 目标类型
* @return 目标类的新实例
*/
public static <S, T> T copyEntityToClass(S source, Class<T> targetClass) {
return BeanUtil.copyProperties(source, targetClass);
}
/**
* 复制实体列表到目标类列表
* @param sourceList 源列表
* @param targetClass 目标类
* @param <S> 源类型
* @param <T> 目标类型
* @return 目标类的新列表
*/
public static <S, T> List<T> copyList(List<S> sourceList, Class<T> targetClass) {
return sourceList.stream()
.map(source -> copyEntityToClass(source, targetClass))
.collect(Collectors.toList());
}
}

View File

@@ -0,0 +1,16 @@
package com.test.bijihoudaun.util;
public class RandomString {
// 生成指定长度的随机字符串
public static String generateRandomString(int length) {
String chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
StringBuilder result = new StringBuilder();
for (int i = 0; i < length; i++) {
int randomIndex = (int) (Math.random() * chars.length());
result.append(chars.charAt(randomIndex));
}
return result.toString();
}
}

View File

@@ -0,0 +1,122 @@
package com.test.bijihoudaun.util;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class SnowflakeIdGenerator {
// 起始时间戳2023-01-01 00:00:00
private static final long EPOCH = 1672531200000L;
// 机器ID位数
private static final long WORKER_ID_BITS = 5L;
// 数据中心ID位数
private static final long DATACENTER_ID_BITS = 5L;
// 最大机器ID (0-31)
private static final long MAX_WORKER_ID = ~(-1L << WORKER_ID_BITS);
// 最大数据中心ID (0-31)
private static final long MAX_DATACENTER_ID = ~(-1L << DATACENTER_ID_BITS);
// 序列号位数
private static final long SEQUENCE_BITS = 12L;
// 机器ID左移位数
private static final long WORKER_ID_SHIFT = SEQUENCE_BITS;
// 数据中心ID左移位数
private static final long DATACENTER_ID_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS;
// 时间戳左移位数
private static final long TIMESTAMP_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS + DATACENTER_ID_BITS;
// 序列号掩码4095
private static final long SEQUENCE_MASK = ~(-1L << SEQUENCE_BITS);
private final long workerId;
private final long datacenterId;
private long sequence = 0L;
private long lastTimestamp = -1L;
/**
* 构造函数
*
* @param workerId 机器ID
* @param datacenterId 数据中心ID
*/
public SnowflakeIdGenerator(
@Value("${worker.id:0}") long workerId,
@Value("${datacenter.id:0}") long datacenterId) {
// 校验机器ID范围
if (workerId > MAX_WORKER_ID || workerId < 0) {
throw new IllegalArgumentException(
String.format("Worker ID 必须在 0 到 %d 之间", MAX_WORKER_ID));
}
// 校验数据中心ID范围
if (datacenterId > MAX_DATACENTER_ID || datacenterId < 0) {
throw new IllegalArgumentException(
String.format("Datacenter ID 必须在 0 到 %d 之间", MAX_DATACENTER_ID));
}
this.workerId = workerId;
this.datacenterId = datacenterId;
}
/**
* 生成雪花ID
*
* @return 雪花ID
*/
public synchronized long nextId() {
long timestamp = currentTime();
// 处理时钟回拨
if (timestamp < lastTimestamp) {
throw new RuntimeException(
String.format("时钟回拨拒绝生成ID。当前时间: %d, 最后时间: %d",
timestamp, lastTimestamp));
}
// 同一毫秒内生成ID
if (lastTimestamp == timestamp) {
sequence = (sequence + 1) & SEQUENCE_MASK;
// 当前毫秒序列号用完
if (sequence == 0) {
timestamp = nextMillis(lastTimestamp);
}
}
// 新毫秒重置序列号
else {
sequence = 0L;
}
lastTimestamp = timestamp;
// 组合ID各部分
return ((timestamp - EPOCH) << TIMESTAMP_SHIFT)
| (datacenterId << DATACENTER_ID_SHIFT)
| (workerId << WORKER_ID_SHIFT)
| sequence;
}
/**
* 生成雪花ID转化成字符串类型
*
* @return 雪花ID-字符串类型
*/
public synchronized String nextIdStr() {
return Long.toString(nextId());
}
// 等待下一毫秒
private long nextMillis(long lastTimestamp) {
long timestamp = currentTime();
while (timestamp <= lastTimestamp) {
timestamp = currentTime();
}
return timestamp;
}
// 获取当前毫秒时间戳
private long currentTime() {
return System.currentTimeMillis();
}
}

View File

@@ -0,0 +1,26 @@
package com.test.bijihoudaun.util;
import com.fasterxml.uuid.Generators;
import com.fasterxml.uuid.impl.TimeBasedGenerator;
/**
* uuidV7
*/
public class UuidV7 {
/**
* 生成uuidV7生成的uuid有-
*/
public static String uuid() {
TimeBasedGenerator generator = Generators.timeBasedGenerator();
return generator.generate().toString();
}
/**
* 获取uuidV7生成的uuid无-
*/
public static String uuidNoHyphen() {
return uuid().replace("-", "");
}
}