初始化提交

This commit is contained in:
2025-10-28 20:40:00 +08:00
commit cda3512456
26 changed files with 1314 additions and 0 deletions

View File

@@ -0,0 +1,19 @@
package com.yolo.keyborad;
import lombok.extern.slf4j.Slf4j;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@Slf4j
@SpringBootApplication
@MapperScan("com.yolo.keyborad.mapper")
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
log.info("info message");
log.error("error message");
}
}

View File

@@ -0,0 +1,32 @@
package com.yolo.keyborad.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 权限校验
*
* @author yupi
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AuthCheck {
/**
* 有任何一个角色
*
* @return
*/
String[] anyRole() default "";
/**
* 必须有某个角色
*
* @return
*/
String mustRole() default "";
}

View File

@@ -0,0 +1,56 @@
package com.yolo.keyborad.aop;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.util.UUID;
/**
* 请求响应日志 AOP
*
* @author yupi
**/
@Aspect
@Component
@Slf4j
public class LogInterceptor {
/**
* 执行拦截
*/
@Around("execution(* com.yupi.project.controller.*.*(..))")
public Object doInterceptor(ProceedingJoinPoint point) throws Throwable {
// 计时
StopWatch stopWatch = new StopWatch();
stopWatch.start();
// 获取请求路径
RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes();
HttpServletRequest httpServletRequest = ((ServletRequestAttributes) requestAttributes).getRequest();
// 生成请求唯一 id
String requestId = UUID.randomUUID().toString();
String url = httpServletRequest.getRequestURI();
// 获取请求参数
Object[] args = point.getArgs();
String reqParam = "[" + StringUtils.join(args, ", ") + "]";
// 输出请求日志
log.info("request startid: {}, path: {}, ip: {}, params: {}", requestId, url,
httpServletRequest.getRemoteHost(), reqParam);
// 执行原方法
Object result = point.proceed();
// 输出响应日志
stopWatch.stop();
long totalTimeMillis = stopWatch.getTotalTimeMillis();
log.info("request end, id: {}, cost: {}ms", requestId, totalTimeMillis);
return result;
}
}

View File

@@ -0,0 +1,35 @@
package com.yolo.keyborad.common;
import lombok.Data;
import java.io.Serializable;
/**
* 通用返回类
*
* @param <T>
* @author yupi
*/
@Data
public class BaseResponse<T> implements Serializable {
private int code;
private T data;
private String message;
public BaseResponse(int code, T data, String message) {
this.code = code;
this.data = data;
this.message = message;
}
public BaseResponse(int code, T data) {
this(code, data, "");
}
public BaseResponse(ErrorCode errorCode) {
this(errorCode.getCode(), null, errorCode.getMessage());
}
}

View File

@@ -0,0 +1,20 @@
package com.yolo.keyborad.common;
import lombok.Data;
import java.io.Serializable;
/**
* 删除请求
*
* @author yupi
*/
@Data
public class DeleteRequest implements Serializable {
/**
* id
*/
private Long id;
private static final long serialVersionUID = 1L;
}

View File

@@ -0,0 +1,42 @@
package com.yolo.keyborad.common;
/**
* 错误码
*
* @author yupi
*/
public enum ErrorCode {
SUCCESS(0, "ok"),
PARAMS_ERROR(40000, "请求参数错误"),
NOT_LOGIN_ERROR(40100, "未登录"),
NO_AUTH_ERROR(40101, "无权限"),
NOT_FOUND_ERROR(40400, "请求数据不存在"),
FORBIDDEN_ERROR(40300, "禁止访问"),
SYSTEM_ERROR(50000, "系统内部异常"),
OPERATION_ERROR(50001, "操作失败");
/**
* 状态码
*/
private final int code;
/**
* 信息
*/
private final String message;
ErrorCode(int code, String message) {
this.code = code;
this.message = message;
}
public int getCode() {
return code;
}
public String getMessage() {
return message;
}
}

View File

@@ -0,0 +1,33 @@
package com.yolo.keyborad.common;
import com.yolo.keyborad.constant.CommonConstant;
import lombok.Data;
/**
* 分页请求
*
* @author yupi
*/
@Data
public class PageRequest {
/**
* 当前页号
*/
private long current = 1;
/**
* 页面大小
*/
private long pageSize = 10;
/**
* 排序字段
*/
private String sortField;
/**
* 排序顺序(默认升序)
*/
private String sortOrder = CommonConstant.SORT_ORDER_ASC;
}

View File

@@ -0,0 +1,51 @@
package com.yolo.keyborad.common;
/**
* 返回工具类
*
* @author yupi
*/
public class ResultUtils {
/**
* 成功
*
* @param data
* @param <T>
* @return
*/
public static <T> BaseResponse<T> success(T data) {
return new BaseResponse<>(0, data, "ok");
}
/**
* 失败
*
* @param errorCode
* @return
*/
public static BaseResponse error(ErrorCode errorCode) {
return new BaseResponse<>(errorCode);
}
/**
* 失败
*
* @param code
* @param message
* @return
*/
public static BaseResponse error(int code, String message) {
return new BaseResponse(code, null, message);
}
/**
* 失败
*
* @param errorCode
* @return
*/
public static BaseResponse error(ErrorCode errorCode, String message) {
return new BaseResponse(errorCode.getCode(), null, message);
}
}

View File

@@ -0,0 +1,27 @@
package com.yolo.keyborad.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* 全局跨域配置
*
* @author yupi
*/
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
// 覆盖所有请求
registry.addMapping("/**")
// 允许发送 Cookie
.allowCredentials(true)
// 放行哪些域名(必须用 patterns否则 * 会和 allowCredentials 冲突)
.allowedOriginPatterns("*")
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
.allowedHeaders("*")
.exposedHeaders("*");
}
}

View File

@@ -0,0 +1,38 @@
package com.yolo.keyborad.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
/**
* Knife4j 接口文档配置
* https://doc.xiaominfo.com/knife4j/documentation/get_start.html
*
* @author yupi
*/
@Configuration
@EnableSwagger2
@Profile("dev")
public class Knife4jConfig {
@Bean
public Docket defaultApi2() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(new ApiInfoBuilder()
.title("project-backend")
.description("project-backend")
.version("1.0")
.build())
.select()
// 指定 Controller 扫描包路径
.apis(RequestHandlerSelectors.basePackage("com.yupi.project.controller"))
.paths(PathSelectors.any())
.build();
}
}

View File

@@ -0,0 +1,31 @@
package com.yolo.keyborad.config;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* MyBatis Plus 配置
*
* @author yupi
*/
@Configuration
@MapperScan("com.yupi.project.mapper")
public class MyBatisPlusConfig {
/**
* 拦截器配置
*
* @return
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 分页插件
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}

View File

@@ -0,0 +1,19 @@
package com.yolo.keyborad.constant;
/**
* 通用常量
*
* @author yupi
*/
public interface CommonConstant {
/**
* 升序
*/
String SORT_ORDER_ASC = "ascend";
/**
* 降序
*/
String SORT_ORDER_DESC = " descend";
}

View File

@@ -0,0 +1,33 @@
package com.yolo.keyborad.constant;
/**
* 用户常量
*
* @author yupi
*/
public interface UserConstant {
/**
* 用户登录态键
*/
String USER_LOGIN_STATE = "userLoginState";
/**
* 系统用户 id虚拟用户
*/
long SYSTEM_USER_ID = 0;
// region 权限
/**
* 默认权限
*/
String DEFAULT_ROLE = "user";
/**
* 管理员权限
*/
String ADMIN_ROLE = "admin";
// endregion
}

View File

@@ -0,0 +1,32 @@
package com.yolo.keyborad.exception;
import com.yolo.keyborad.common.ErrorCode;
/**
* 自定义异常类
*
* @author yupi
*/
public class BusinessException extends RuntimeException {
private final int code;
public BusinessException(int code, String message) {
super(message);
this.code = code;
}
public BusinessException(ErrorCode errorCode) {
super(errorCode.getMessage());
this.code = errorCode.getCode();
}
public BusinessException(ErrorCode errorCode, String message) {
super(message);
this.code = errorCode.getCode();
}
public int getCode() {
return code;
}
}

View File

@@ -0,0 +1,30 @@
package com.yolo.keyborad.exception;
import com.yolo.keyborad.common.BaseResponse;
import com.yolo.keyborad.common.ErrorCode;
import com.yolo.keyborad.common.ResultUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
/**
* 全局异常处理器
*
* @author yupi
*/
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
@ExceptionHandler(BusinessException.class)
public BaseResponse<?> businessExceptionHandler(BusinessException e) {
log.error("businessException: " + e.getMessage(), e);
return ResultUtils.error(e.getCode(), e.getMessage());
}
@ExceptionHandler(RuntimeException.class)
public BaseResponse<?> runtimeExceptionHandler(RuntimeException e) {
log.error("runtimeException", e);
return ResultUtils.error(ErrorCode.SYSTEM_ERROR, e.getMessage());
}
}

View File

@@ -0,0 +1,42 @@
package com.yolo.keyborad.model.enums;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
/**
* 帖子性别枚举
*
* @author yupi
*/
public enum PostGenderEnum {
MALE("", 0),
FEMALE("", 1);
private final String text;
private final int value;
PostGenderEnum(String text, int value) {
this.text = text;
this.value = value;
}
/**
* 获取值列表
*
* @return
*/
public static List<Integer> getValues() {
return Arrays.stream(values()).map(item -> item.value).collect(Collectors.toList());
}
public int getValue() {
return value;
}
public String getText() {
return text;
}
}

View File

@@ -0,0 +1,43 @@
package com.yolo.keyborad.model.enums;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
/**
* 帖子审核状态枚举
*
* @author yupi
*/
public enum PostReviewStatusEnum {
REVIEWING("待审核", 0),
PASS("通过", 1),
REJECT("拒绝", 2);
private final String text;
private final int value;
PostReviewStatusEnum(String text, int value) {
this.text = text;
this.value = value;
}
/**
* 获取值列表
*
* @return
*/
public static List<Integer> getValues() {
return Arrays.stream(values()).map(item -> item.value).collect(Collectors.toList());
}
public int getValue() {
return value;
}
public String getText() {
return text;
}
}

View File

@@ -0,0 +1,6 @@
spring:
datasource:
driver-class-name: org.postgresql.Driver
url: jdbc:postgresql://localhost:5432/postgres
username: root
password: 123asd

View File

@@ -0,0 +1,6 @@
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/my_db
username: root
password: 123456

View File

@@ -0,0 +1,36 @@
spring:
application:
name: springboot-init
profiles:
active: dev
# DataSource Config
datasource:
driver-class-name: org.postgresql.Driver
url: jdbc:postgresql://localhost:5432/postgres
username: root
password: 123asd
mvc:
pathmatch:
matching-strategy: ANT_PATH_MATCHER
# session 失效时间(分钟)
session:
timeout: 86400
store-type: redis
# redis 配置
redis:
port: 6379
host: localhost
database: 0
server:
port: 7529
servlet:
context-path: /api
mybatis-plus:
configuration:
map-underscore-to-camel-case: false
log-impl: org.apache.ibatis.logging.log4j2.Log4j2Impl
global-config:
db-config:
logic-delete-field: isDelete # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2)
logic-delete-value: 1 # 逻辑已删除值(默认为 1)
logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)

View File

@@ -0,0 +1 @@
我的项目 by 程序员鱼皮 https://github.com/liyupi