bug修复
This commit is contained in:
+14077
File diff suppressed because it is too large
Load Diff
@@ -2,8 +2,9 @@ package fun.nojava.admin;
|
|||||||
|
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration;
|
||||||
|
|
||||||
@SpringBootApplication
|
@SpringBootApplication(exclude = {UserDetailsServiceAutoConfiguration.class})
|
||||||
public class JogApplication {
|
public class JogApplication {
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
|||||||
@@ -8,13 +8,14 @@ spring:
|
|||||||
name: jog-system
|
name: jog-system
|
||||||
datasource:
|
datasource:
|
||||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||||
url: jdbc:mysql://localhost:3306/jog_db?useUnicode=true&characterEncoding=utf8mb4&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
|
url: jdbc:mysql://81.70.204.4:3306/jog?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
|
||||||
username: root
|
username: jog
|
||||||
password: root
|
password: Jg.123459
|
||||||
data:
|
data:
|
||||||
redis:
|
redis:
|
||||||
host: localhost
|
host: 81.70.204.4
|
||||||
port: 6379
|
port: 6379
|
||||||
|
password: Rd.123459
|
||||||
database: 0
|
database: 0
|
||||||
timeout: 5000ms
|
timeout: 5000ms
|
||||||
lettuce:
|
lettuce:
|
||||||
@@ -34,10 +35,7 @@ spring:
|
|||||||
starttls:
|
starttls:
|
||||||
enable: true
|
enable: true
|
||||||
flyway:
|
flyway:
|
||||||
enabled: true
|
enabled: false
|
||||||
locations: classpath:db/migration
|
|
||||||
baseline-on-migrate: true
|
|
||||||
encoding: UTF-8
|
|
||||||
|
|
||||||
mybatis-plus:
|
mybatis-plus:
|
||||||
mapper-locations: classpath*:/mapper/**/*.xml
|
mapper-locations: classpath*:/mapper/**/*.xml
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
-- V1__init_schema.sql
|
-- V1__init_schema.sql
|
||||||
-- 博客系统数据库初始化脚本
|
-- 博客系统数据库初始化脚本
|
||||||
|
|
||||||
CREATE DATABASE IF NOT EXISTS blog_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
|
||||||
USE blog_db;
|
|
||||||
|
|
||||||
-- 用户表
|
-- 用户表
|
||||||
CREATE TABLE IF NOT EXISTS sys_user (
|
CREATE TABLE IF NOT EXISTS sys_user (
|
||||||
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
-- V2__init_data.sql
|
-- V2__init_data.sql
|
||||||
-- 初始化数据
|
-- 初始化数据
|
||||||
|
|
||||||
USE blog_db;
|
|
||||||
|
|
||||||
-- 插入管理员账号(密码:Admin@123)
|
-- 插入管理员账号(密码:Admin@123)
|
||||||
INSERT INTO sys_user (username, email, password, nickname, user_type, status, email_verified)
|
INSERT INTO sys_user (username, email, password, nickname, user_type, status, email_verified)
|
||||||
VALUES ('admin', 'admin@blog.com', '$2a$10$N.zmdr9k7uOCQb376NoUnuTJ8iAt6Z5EHsM8lE9lBOsl7iKTVKIUi', '系统管理员', 0, 0, 1);
|
VALUES ('admin', 'admin@blog.com', '$2a$10$N.zmdr9k7uOCQb376NoUnuTJ8iAt6Z5EHsM8lE9lBOsl7iKTVKIUi', '系统管理员', 0, 0, 1);
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import org.springframework.security.config.annotation.web.configurers.AbstractHt
|
|||||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
|
import org.springframework.core.annotation.Order;
|
||||||
import org.springframework.security.web.SecurityFilterChain;
|
import org.springframework.security.web.SecurityFilterChain;
|
||||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||||
import org.springframework.web.cors.CorsConfiguration;
|
import org.springframework.web.cors.CorsConfiguration;
|
||||||
@@ -33,13 +34,15 @@ public class SecurityConfig {
|
|||||||
private final JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
|
private final JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
|
@Order(1)
|
||||||
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||||
http
|
http
|
||||||
.csrf(AbstractHttpConfigurer::disable)
|
.csrf(csrf -> csrf.disable())
|
||||||
.cors(cors -> cors.configurationSource(corsConfigurationSource()))
|
.cors(cors -> cors.configurationSource(corsConfigurationSource()))
|
||||||
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
|
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
|
||||||
.exceptionHandling(ex -> ex.authenticationEntryPoint(jwtAuthenticationEntryPoint))
|
.exceptionHandling(ex -> ex.authenticationEntryPoint(jwtAuthenticationEntryPoint))
|
||||||
.authorizeHttpRequests(auth -> auth
|
.authorizeHttpRequests(auth -> auth
|
||||||
|
.requestMatchers("/api/auth/**").permitAll()
|
||||||
.requestMatchers("/api/public/**").permitAll()
|
.requestMatchers("/api/public/**").permitAll()
|
||||||
.requestMatchers("/api/oauth/**").permitAll()
|
.requestMatchers("/api/oauth/**").permitAll()
|
||||||
.requestMatchers("/swagger-ui/**", "/v3/api-docs/**").permitAll()
|
.requestMatchers("/swagger-ui/**", "/v3/api-docs/**").permitAll()
|
||||||
@@ -47,6 +50,7 @@ public class SecurityConfig {
|
|||||||
.requestMatchers(HttpMethod.OPTIONS, "/**").permitAll()
|
.requestMatchers(HttpMethod.OPTIONS, "/**").permitAll()
|
||||||
.requestMatchers("/api/admin/**").hasRole("ADMIN")
|
.requestMatchers("/api/admin/**").hasRole("ADMIN")
|
||||||
.requestMatchers("/api/app/**").hasAnyRole("USER", "ADMIN")
|
.requestMatchers("/api/app/**").hasAnyRole("USER", "ADMIN")
|
||||||
|
.requestMatchers("/api/article/**", "/api/comment/**", "/api/user/**").hasAnyRole("USER", "ADMIN")
|
||||||
.anyRequest().permitAll()
|
.anyRequest().permitAll()
|
||||||
)
|
)
|
||||||
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
|
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
|
||||||
|
|||||||
+10
-1
@@ -1,6 +1,7 @@
|
|||||||
package fun.nojava.framework.security;
|
package fun.nojava.framework.security;
|
||||||
|
|
||||||
import fun.nojava.common.constant.SecurityConstants;
|
import fun.nojava.common.constant.SecurityConstants;
|
||||||
|
import fun.nojava.common.enums.UserType;
|
||||||
import jakarta.servlet.FilterChain;
|
import jakarta.servlet.FilterChain;
|
||||||
import jakarta.servlet.ServletException;
|
import jakarta.servlet.ServletException;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
@@ -38,12 +39,13 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
|
|||||||
|
|
||||||
Long userId = jwtTokenProvider.getUserIdFromToken(token);
|
Long userId = jwtTokenProvider.getUserIdFromToken(token);
|
||||||
String role = jwtTokenProvider.getRoleFromToken(token);
|
String role = jwtTokenProvider.getRoleFromToken(token);
|
||||||
|
String roleName = normalizeRole(role);
|
||||||
|
|
||||||
UsernamePasswordAuthenticationToken authentication =
|
UsernamePasswordAuthenticationToken authentication =
|
||||||
new UsernamePasswordAuthenticationToken(
|
new UsernamePasswordAuthenticationToken(
|
||||||
userId,
|
userId,
|
||||||
null,
|
null,
|
||||||
List.of(new SimpleGrantedAuthority("ROLE_" + role))
|
List.of(new SimpleGrantedAuthority("ROLE_" + roleName))
|
||||||
);
|
);
|
||||||
|
|
||||||
SecurityContextHolder.getContext().setAuthentication(authentication);
|
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||||
@@ -59,4 +61,11 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String normalizeRole(String role) {
|
||||||
|
if ("0".equals(role) || "1".equals(role)) {
|
||||||
|
return UserType.fromCode(Integer.parseInt(role)).name();
|
||||||
|
}
|
||||||
|
return role;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package fun.nojava.framework.security;
|
package fun.nojava.framework.security;
|
||||||
|
|
||||||
import fun.nojava.common.constant.SecurityConstants;
|
import fun.nojava.common.constant.SecurityConstants;
|
||||||
|
import fun.nojava.common.enums.UserType;
|
||||||
import fun.nojava.framework.config.JwtProperties;
|
import fun.nojava.framework.config.JwtProperties;
|
||||||
import io.jsonwebtoken.*;
|
import io.jsonwebtoken.*;
|
||||||
import io.jsonwebtoken.security.Keys;
|
import io.jsonwebtoken.security.Keys;
|
||||||
@@ -22,10 +23,11 @@ public class JwtTokenProvider {
|
|||||||
public String generateAccessToken(Long userId, Integer role) {
|
public String generateAccessToken(Long userId, Integer role) {
|
||||||
Date now = new Date();
|
Date now = new Date();
|
||||||
Date expiryDate = new Date(now.getTime() + jwtProperties.getAccessTokenExpire() * 1000);
|
Date expiryDate = new Date(now.getTime() + jwtProperties.getAccessTokenExpire() * 1000);
|
||||||
|
String roleName = UserType.fromCode(role).name();
|
||||||
|
|
||||||
return Jwts.builder()
|
return Jwts.builder()
|
||||||
.subject(String.valueOf(userId))
|
.subject(String.valueOf(userId))
|
||||||
.claim("role", String.valueOf(role))
|
.claim("role", roleName)
|
||||||
.issuedAt(now)
|
.issuedAt(now)
|
||||||
.expiration(expiryDate)
|
.expiration(expiryDate)
|
||||||
.signWith(jwtProperties.getSigningKey())
|
.signWith(jwtProperties.getSigningKey())
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
import request from '@/utils/request'
|
import request from '@/utils/request'
|
||||||
|
|
||||||
export function adminLogin(data) {
|
export function adminLogin(data) {
|
||||||
return request.post('/api/public/admin/login', data)
|
return request.post('/api/public/admin/login', {
|
||||||
|
email: data.email || data.account,
|
||||||
|
password: data.password,
|
||||||
|
rememberMe: data.rememberMe || false,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function userLogin(data) {
|
export function userLogin(data) {
|
||||||
|
|||||||
@@ -109,8 +109,11 @@ const router = createRouter({
|
|||||||
router.beforeEach((to, from, next) => {
|
router.beforeEach((to, from, next) => {
|
||||||
document.title = to.meta.title ? `${to.meta.title} - 博客系统` : '博客系统'
|
document.title = to.meta.title ? `${to.meta.title} - 博客系统` : '博客系统'
|
||||||
const token = localStorage.getItem('access_token')
|
const token = localStorage.getItem('access_token')
|
||||||
|
const userInfo = JSON.parse(localStorage.getItem('user_info') || 'null')
|
||||||
if (to.meta.requiresAuth && !token) {
|
if (to.meta.requiresAuth && !token) {
|
||||||
next({ name: 'Login', query: { redirect: to.fullPath } })
|
next({ name: 'Login', query: { redirect: to.fullPath } })
|
||||||
|
} else if (to.meta.requiresAdmin && userInfo?.role !== 'ADMIN') {
|
||||||
|
next({ name: 'Home' })
|
||||||
} else {
|
} else {
|
||||||
next()
|
next()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ export const useUserStore = defineStore('user', () => {
|
|||||||
const res = await adminLogin(data)
|
const res = await adminLogin(data)
|
||||||
const loginData = res.data
|
const loginData = res.data
|
||||||
setTokens(loginData.accessToken, loginData.refreshToken)
|
setTokens(loginData.accessToken, loginData.refreshToken)
|
||||||
userInfo.value = { ...loginData.userInfo, role: 'ADMIN' }
|
userInfo.value = toUserInfo(loginData, 'ADMIN')
|
||||||
localStorage.setItem('user_info', JSON.stringify(userInfo.value))
|
localStorage.setItem('user_info', JSON.stringify(userInfo.value))
|
||||||
return loginData
|
return loginData
|
||||||
}
|
}
|
||||||
@@ -23,7 +23,7 @@ export const useUserStore = defineStore('user', () => {
|
|||||||
const res = await userLogin(data)
|
const res = await userLogin(data)
|
||||||
const loginData = res.data
|
const loginData = res.data
|
||||||
setTokens(loginData.accessToken, loginData.refreshToken)
|
setTokens(loginData.accessToken, loginData.refreshToken)
|
||||||
userInfo.value = { ...loginData.userInfo, role: 'USER' }
|
userInfo.value = toUserInfo(loginData, 'USER')
|
||||||
localStorage.setItem('user_info', JSON.stringify(userInfo.value))
|
localStorage.setItem('user_info', JSON.stringify(userInfo.value))
|
||||||
return loginData
|
return loginData
|
||||||
}
|
}
|
||||||
@@ -64,6 +64,17 @@ export const useUserStore = defineStore('user', () => {
|
|||||||
localStorage.removeItem('user_info')
|
localStorage.removeItem('user_info')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function toUserInfo(loginData, role) {
|
||||||
|
return {
|
||||||
|
id: loginData.userId,
|
||||||
|
email: loginData.email,
|
||||||
|
nickname: loginData.nickname,
|
||||||
|
avatar: loginData.avatar,
|
||||||
|
type: loginData.type,
|
||||||
|
role,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
token,
|
token,
|
||||||
refreshToken,
|
refreshToken,
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ async function handleSubmit(publish) {
|
|||||||
const data = {
|
const data = {
|
||||||
...form.value,
|
...form.value,
|
||||||
content: editor.value?.getHTML() || '',
|
content: editor.value?.getHTML() || '',
|
||||||
publish,
|
status: publish ? 1 : 0,
|
||||||
}
|
}
|
||||||
if (isEdit.value) {
|
if (isEdit.value) {
|
||||||
await updateArticle(route.params.id, data)
|
await updateArticle(route.params.id, data)
|
||||||
|
|||||||
+12
-4
@@ -13,7 +13,7 @@ import org.springframework.web.bind.annotation.*;
|
|||||||
|
|
||||||
@Tag(name = "用户文章接口")
|
@Tag(name = "用户文章接口")
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/api/article")
|
@RequestMapping("/api/app/article")
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class ArticleController {
|
public class ArticleController {
|
||||||
|
|
||||||
@@ -38,7 +38,7 @@ public class ArticleController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "我的文章列表")
|
@Operation(summary = "我的文章列表")
|
||||||
@GetMapping("/my")
|
@GetMapping("/mine")
|
||||||
public ApiResult<PageResult<ArticleListVO>> listMyArticles(
|
public ApiResult<PageResult<ArticleListVO>> listMyArticles(
|
||||||
@AuthenticationPrincipal Long userId,
|
@AuthenticationPrincipal Long userId,
|
||||||
@RequestParam(defaultValue = "1") int page,
|
@RequestParam(defaultValue = "1") int page,
|
||||||
@@ -48,7 +48,7 @@ public class ArticleController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "移至回收站")
|
@Operation(summary = "移至回收站")
|
||||||
@PostMapping("/{id}/recycle")
|
@PutMapping("/{id}/recycle")
|
||||||
public ApiResult<Void> moveToRecycleBin(
|
public ApiResult<Void> moveToRecycleBin(
|
||||||
@AuthenticationPrincipal Long userId,
|
@AuthenticationPrincipal Long userId,
|
||||||
@PathVariable Long id) {
|
@PathVariable Long id) {
|
||||||
@@ -57,7 +57,7 @@ public class ArticleController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "从回收站恢复")
|
@Operation(summary = "从回收站恢复")
|
||||||
@PostMapping("/{id}/restore")
|
@PutMapping("/{id}/restore")
|
||||||
public ApiResult<Void> restoreArticle(
|
public ApiResult<Void> restoreArticle(
|
||||||
@AuthenticationPrincipal Long userId,
|
@AuthenticationPrincipal Long userId,
|
||||||
@PathVariable Long id) {
|
@PathVariable Long id) {
|
||||||
@@ -73,4 +73,12 @@ public class ArticleController {
|
|||||||
articleService.deleteArticle(userId, id);
|
articleService.deleteArticle(userId, id);
|
||||||
return ApiResult.success();
|
return ApiResult.success();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "点赞文章")
|
||||||
|
@PostMapping("/{id}/like")
|
||||||
|
public ApiResult<Boolean> toggleLike(
|
||||||
|
@PathVariable Long id,
|
||||||
|
@AuthenticationPrincipal Long userId) {
|
||||||
|
return ApiResult.success(articleService.toggleLike(userId, id));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-8
@@ -35,20 +35,16 @@ class AdminCategoryController {
|
|||||||
|
|
||||||
@Operation(summary = "创建分类")
|
@Operation(summary = "创建分类")
|
||||||
@PostMapping
|
@PostMapping
|
||||||
public ApiResult<Category> createCategory(
|
public ApiResult<Category> createCategory(@RequestBody Category category) {
|
||||||
@RequestParam String name,
|
return ApiResult.success(categoryService.createCategory(category.getName(), category.getDescription()));
|
||||||
@RequestParam(required = false) String description) {
|
|
||||||
return ApiResult.success(categoryService.createCategory(name, description));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "更新分类")
|
@Operation(summary = "更新分类")
|
||||||
@PutMapping("/{id}")
|
@PutMapping("/{id}")
|
||||||
public ApiResult<Void> updateCategory(
|
public ApiResult<Void> updateCategory(
|
||||||
@PathVariable Long id,
|
@PathVariable Long id,
|
||||||
@RequestParam(required = false) String name,
|
@RequestBody Category category) {
|
||||||
@RequestParam(required = false) String description,
|
categoryService.updateCategory(id, category.getName(), category.getDescription(), category.getEnabled());
|
||||||
@RequestParam(required = false) Boolean enabled) {
|
|
||||||
categoryService.updateCategory(id, name, description, enabled);
|
|
||||||
return ApiResult.success();
|
return ApiResult.success();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
-7
@@ -39,11 +39,4 @@ public class PublicArticleController {
|
|||||||
return ApiResult.success(articleService.getArticleDetail(id, userId));
|
return ApiResult.success(articleService.getArticleDetail(id, userId));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "点赞文章")
|
|
||||||
@PostMapping("/{id}/like")
|
|
||||||
public ApiResult<Boolean> toggleLike(
|
|
||||||
@PathVariable Long id,
|
|
||||||
@AuthenticationPrincipal Long userId) {
|
|
||||||
return ApiResult.success(articleService.toggleLike(userId, id));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package fun.nojava.module.blog.dto;
|
package fun.nojava.module.blog.dto;
|
||||||
|
|
||||||
|
import fun.nojava.module.blog.entity.Tag;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Builder;
|
import lombok.Builder;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
@@ -34,7 +35,7 @@ public class ArticleVO {
|
|||||||
|
|
||||||
private String categoryName;
|
private String categoryName;
|
||||||
|
|
||||||
private List<String> tags;
|
private List<Tag> tags;
|
||||||
|
|
||||||
private Long userId;
|
private Long userId;
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import lombok.Data;
|
|||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
@TableName("article")
|
@TableName("blog_article")
|
||||||
public class Article {
|
public class Article {
|
||||||
|
|
||||||
@TableId(type = IdType.AUTO)
|
@TableId(type = IdType.AUTO)
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import lombok.Data;
|
|||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
@TableName("article_like")
|
@TableName("blog_article_like")
|
||||||
public class ArticleLike {
|
public class ArticleLike {
|
||||||
|
|
||||||
@TableId(type = IdType.AUTO)
|
@TableId(type = IdType.AUTO)
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import com.baomidou.mybatisplus.annotation.*;
|
|||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
@TableName("article_tag")
|
@TableName("blog_article_tag")
|
||||||
public class ArticleTag {
|
public class ArticleTag {
|
||||||
|
|
||||||
@TableId(type = IdType.AUTO)
|
@TableId(type = IdType.AUTO)
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import lombok.Data;
|
|||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
@TableName("category")
|
@TableName("blog_category")
|
||||||
public class Category {
|
public class Category {
|
||||||
|
|
||||||
@TableId(type = IdType.AUTO)
|
@TableId(type = IdType.AUTO)
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import lombok.Data;
|
|||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
@TableName("tag")
|
@TableName("blog_tag")
|
||||||
public class Tag {
|
public class Tag {
|
||||||
|
|
||||||
@TableId(type = IdType.AUTO)
|
@TableId(type = IdType.AUTO)
|
||||||
|
|||||||
+2
-2
@@ -280,11 +280,11 @@ public class ArticleServiceImpl implements ArticleService {
|
|||||||
User author = userMapper.selectById(article.getUserId());
|
User author = userMapper.selectById(article.getUserId());
|
||||||
Category category = article.getCategoryId() != null ? categoryMapper.selectById(article.getCategoryId()) : null;
|
Category category = article.getCategoryId() != null ? categoryMapper.selectById(article.getCategoryId()) : null;
|
||||||
|
|
||||||
List<String> tags = articleTagMapper.selectList(
|
List<Tag> tags = articleTagMapper.selectList(
|
||||||
new LambdaQueryWrapper<ArticleTag>().eq(ArticleTag::getArticleId, article.getId())
|
new LambdaQueryWrapper<ArticleTag>().eq(ArticleTag::getArticleId, article.getId())
|
||||||
).stream().map(at -> {
|
).stream().map(at -> {
|
||||||
Tag tag = tagMapper.selectById(at.getTagId());
|
Tag tag = tagMapper.selectById(at.getTagId());
|
||||||
return tag != null ? tag.getName() : null;
|
return tag;
|
||||||
}).filter(t -> t != null).toList();
|
}).filter(t -> t != null).toList();
|
||||||
|
|
||||||
return ArticleVO.builder()
|
return ArticleVO.builder()
|
||||||
|
|||||||
+2
-2
@@ -16,7 +16,7 @@ import java.util.List;
|
|||||||
|
|
||||||
@Tag(name = "评论接口")
|
@Tag(name = "评论接口")
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/api/comment")
|
@RequestMapping("/api/app/comment")
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class CommentController {
|
public class CommentController {
|
||||||
|
|
||||||
@@ -74,7 +74,7 @@ class AdminCommentController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "审核评论")
|
@Operation(summary = "审核评论")
|
||||||
@PostMapping("/{id}/audit")
|
@PutMapping("/{id}/audit")
|
||||||
public ApiResult<Void> auditComment(
|
public ApiResult<Void> auditComment(
|
||||||
@PathVariable Long id,
|
@PathVariable Long id,
|
||||||
@RequestParam Integer status) {
|
@RequestParam Integer status) {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import lombok.Data;
|
|||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
@TableName("comment")
|
@TableName("blog_comment")
|
||||||
public class Comment {
|
public class Comment {
|
||||||
|
|
||||||
@TableId(type = IdType.AUTO)
|
@TableId(type = IdType.AUTO)
|
||||||
@@ -18,6 +18,7 @@ public class Comment {
|
|||||||
|
|
||||||
private Long parentId;
|
private Long parentId;
|
||||||
|
|
||||||
|
@TableField("reply_to_user_id")
|
||||||
private Long replyToId;
|
private Long replyToId;
|
||||||
|
|
||||||
private String content;
|
private String content;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import lombok.Data;
|
|||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
@TableName("comment_like")
|
@TableName("blog_comment_like")
|
||||||
public class CommentLike {
|
public class CommentLike {
|
||||||
|
|
||||||
@TableId(type = IdType.AUTO)
|
@TableId(type = IdType.AUTO)
|
||||||
|
|||||||
+17
-13
@@ -13,16 +13,19 @@ import jakarta.validation.Valid;
|
|||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
@Tag(name = "认证接口")
|
@Tag(name = "认证接口")
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/api/auth")
|
@RequestMapping("/api/public")
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class AuthController {
|
public class AuthController {
|
||||||
|
|
||||||
private final AuthService authService;
|
private final AuthService authService;
|
||||||
|
|
||||||
@Operation(summary = "用户登录")
|
@Operation(summary = "用户登录")
|
||||||
@PostMapping("/login")
|
@PostMapping("/user/login")
|
||||||
@RateLimit(key = "login", maxCount = 5, duration = 60)
|
@RateLimit(key = "login", maxCount = 5, duration = 60)
|
||||||
public ApiResult<LoginVO> login(@Valid @RequestBody LoginDTO dto) {
|
public ApiResult<LoginVO> login(@Valid @RequestBody LoginDTO dto) {
|
||||||
LoginVO vo = authService.login(dto, WebUtils.getClientIp(), WebUtils.getUserAgent());
|
LoginVO vo = authService.login(dto, WebUtils.getClientIp(), WebUtils.getUserAgent());
|
||||||
@@ -38,25 +41,26 @@ public class AuthController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "用户注册")
|
@Operation(summary = "用户注册")
|
||||||
@PostMapping("/register")
|
@PostMapping("/user/register")
|
||||||
@RateLimit(key = "register", maxCount = 3, duration = 3600)
|
@RateLimit(key = "register", maxCount = 3, duration = 3600)
|
||||||
public ApiResult<Void> register(@Valid @RequestBody RegisterDTO dto) {
|
public ApiResult<Void> register(@Valid @RequestBody RegisterDTO dto) {
|
||||||
authService.register(dto);
|
authService.register(dto);
|
||||||
return ApiResult.success();
|
return ApiResult.success();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "获取验证码")
|
||||||
|
@GetMapping("/captcha")
|
||||||
|
public ApiResult<Map<String, String>> captcha() {
|
||||||
|
return ApiResult.success(Map.of(
|
||||||
|
"captchaKey", UUID.randomUUID().toString(),
|
||||||
|
"captchaImage", ""
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
@Operation(summary = "刷新Token")
|
@Operation(summary = "刷新Token")
|
||||||
@PostMapping("/refresh")
|
@PostMapping("/refresh-token")
|
||||||
public ApiResult<LoginVO> refreshToken(@RequestHeader("Refresh-Token") String refreshToken) {
|
public ApiResult<LoginVO> refreshToken(@RequestParam String refreshToken) {
|
||||||
LoginVO vo = authService.refreshToken(refreshToken);
|
LoginVO vo = authService.refreshToken(refreshToken);
|
||||||
return ApiResult.success(vo);
|
return ApiResult.success(vo);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "退出登录")
|
|
||||||
@PostMapping("/logout")
|
|
||||||
public ApiResult<Void> logout(@RequestHeader("Authorization") String authHeader) {
|
|
||||||
String token = authHeader.replace("Bearer ", "");
|
|
||||||
authService.logout(token);
|
|
||||||
return ApiResult.success();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+28
-4
@@ -3,6 +3,7 @@ package fun.nojava.module.system.controller;
|
|||||||
import fun.nojava.common.model.ApiResult;
|
import fun.nojava.common.model.ApiResult;
|
||||||
import fun.nojava.common.model.PageResult;
|
import fun.nojava.common.model.PageResult;
|
||||||
import fun.nojava.module.system.dto.UserInfoVO;
|
import fun.nojava.module.system.dto.UserInfoVO;
|
||||||
|
import fun.nojava.module.system.service.AuthService;
|
||||||
import fun.nojava.module.system.service.UserService;
|
import fun.nojava.module.system.service.UserService;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
@@ -12,14 +13,15 @@ import org.springframework.web.bind.annotation.*;
|
|||||||
|
|
||||||
@Tag(name = "用户接口")
|
@Tag(name = "用户接口")
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/api/user")
|
@RequestMapping("/api/app/user")
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class UserController {
|
public class UserController {
|
||||||
|
|
||||||
private final UserService userService;
|
private final UserService userService;
|
||||||
|
private final AuthService authService;
|
||||||
|
|
||||||
@Operation(summary = "获取当前用户信息")
|
@Operation(summary = "获取当前用户信息")
|
||||||
@GetMapping("/info")
|
@GetMapping("/me")
|
||||||
public ApiResult<UserInfoVO> getCurrentUser(@AuthenticationPrincipal Long userId) {
|
public ApiResult<UserInfoVO> getCurrentUser(@AuthenticationPrincipal Long userId) {
|
||||||
return ApiResult.success(userService.getUserInfo(userId));
|
return ApiResult.success(userService.getUserInfo(userId));
|
||||||
}
|
}
|
||||||
@@ -29,6 +31,28 @@ public class UserController {
|
|||||||
public ApiResult<UserInfoVO> getUserInfo(@PathVariable Long id) {
|
public ApiResult<UserInfoVO> getUserInfo(@PathVariable Long id) {
|
||||||
return ApiResult.success(userService.getUserInfo(id));
|
return ApiResult.success(userService.getUserInfo(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "关注用户")
|
||||||
|
@PostMapping("/{id}/follow")
|
||||||
|
public ApiResult<Void> followUser(@AuthenticationPrincipal Long userId, @PathVariable Long id) {
|
||||||
|
userService.follow(userId, id);
|
||||||
|
return ApiResult.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "取消关注")
|
||||||
|
@DeleteMapping("/{id}/follow")
|
||||||
|
public ApiResult<Void> unfollowUser(@AuthenticationPrincipal Long userId, @PathVariable Long id) {
|
||||||
|
userService.unfollow(userId, id);
|
||||||
|
return ApiResult.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "退出登录")
|
||||||
|
@PostMapping("/logout")
|
||||||
|
public ApiResult<Void> logout(@RequestHeader("Authorization") String authHeader) {
|
||||||
|
String token = authHeader.replace("Bearer ", "");
|
||||||
|
authService.logout(token);
|
||||||
|
return ApiResult.success();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Tag(name = "管理员-用户管理")
|
@Tag(name = "管理员-用户管理")
|
||||||
@@ -49,14 +73,14 @@ class AdminUserController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "重置密码")
|
@Operation(summary = "重置密码")
|
||||||
@PostMapping("/{id}/reset-password")
|
@PutMapping("/{id}/reset-password")
|
||||||
public ApiResult<Void> resetPassword(@PathVariable Long id, @RequestParam String newPassword) {
|
public ApiResult<Void> resetPassword(@PathVariable Long id, @RequestParam String newPassword) {
|
||||||
userService.resetPassword(id, newPassword);
|
userService.resetPassword(id, newPassword);
|
||||||
return ApiResult.success();
|
return ApiResult.success();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "更新状态")
|
@Operation(summary = "更新状态")
|
||||||
@PostMapping("/{id}/status")
|
@PutMapping("/{id}/status")
|
||||||
public ApiResult<Void> updateStatus(@PathVariable Long id, @RequestParam int status) {
|
public ApiResult<Void> updateStatus(@PathVariable Long id, @RequestParam int status) {
|
||||||
userService.updateUserStatus(id, status);
|
userService.updateUserStatus(id, status);
|
||||||
return ApiResult.success();
|
return ApiResult.success();
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package fun.nojava.module.system.dto;
|
package fun.nojava.module.system.dto;
|
||||||
|
|
||||||
import jakarta.validation.constraints.Email;
|
|
||||||
import jakarta.validation.constraints.NotBlank;
|
import jakarta.validation.constraints.NotBlank;
|
||||||
import jakarta.validation.constraints.Size;
|
import jakarta.validation.constraints.Size;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
@@ -8,8 +7,7 @@ import lombok.Data;
|
|||||||
@Data
|
@Data
|
||||||
public class LoginDTO {
|
public class LoginDTO {
|
||||||
|
|
||||||
@NotBlank(message = "邮箱不能为空")
|
@NotBlank(message = "账号不能为空")
|
||||||
@Email(message = "邮箱格式不正确")
|
|
||||||
private String email;
|
private String email;
|
||||||
|
|
||||||
@NotBlank(message = "密码不能为空")
|
@NotBlank(message = "密码不能为空")
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import lombok.Data;
|
|||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
@TableName("login_log")
|
@TableName("sys_login_log")
|
||||||
public class LoginLog {
|
public class LoginLog {
|
||||||
|
|
||||||
@TableId(type = IdType.AUTO)
|
@TableId(type = IdType.AUTO)
|
||||||
@@ -14,14 +14,17 @@ public class LoginLog {
|
|||||||
|
|
||||||
private Long userId;
|
private Long userId;
|
||||||
|
|
||||||
|
@TableField("username")
|
||||||
private String email;
|
private String email;
|
||||||
|
|
||||||
private Integer loginType;
|
private Integer loginType;
|
||||||
|
|
||||||
private String ipAddress;
|
private String ipAddress;
|
||||||
|
|
||||||
|
@TableField(exist = false)
|
||||||
private String userAgent;
|
private String userAgent;
|
||||||
|
|
||||||
|
@TableField("result")
|
||||||
private Integer status;
|
private Integer status;
|
||||||
|
|
||||||
private String failReason;
|
private String failReason;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import lombok.Data;
|
|||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
@TableName("user")
|
@TableName("sys_user")
|
||||||
public class User {
|
public class User {
|
||||||
|
|
||||||
@TableId(type = IdType.AUTO)
|
@TableId(type = IdType.AUTO)
|
||||||
@@ -14,6 +14,10 @@ public class User {
|
|||||||
|
|
||||||
private String email;
|
private String email;
|
||||||
|
|
||||||
|
private String username;
|
||||||
|
|
||||||
|
private String phone;
|
||||||
|
|
||||||
private String password;
|
private String password;
|
||||||
|
|
||||||
private String nickname;
|
private String nickname;
|
||||||
@@ -22,6 +26,7 @@ public class User {
|
|||||||
|
|
||||||
private String bio;
|
private String bio;
|
||||||
|
|
||||||
|
@TableField("user_type")
|
||||||
private Integer type;
|
private Integer type;
|
||||||
|
|
||||||
private Integer status;
|
private Integer status;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import lombok.Data;
|
|||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
@TableName("user_session")
|
@TableName("sys_user_session")
|
||||||
public class UserSession {
|
public class UserSession {
|
||||||
|
|
||||||
@TableId(type = IdType.AUTO)
|
@TableId(type = IdType.AUTO)
|
||||||
@@ -14,12 +14,15 @@ public class UserSession {
|
|||||||
|
|
||||||
private Long userId;
|
private Long userId;
|
||||||
|
|
||||||
|
@TableField("token")
|
||||||
private String refreshToken;
|
private String refreshToken;
|
||||||
|
|
||||||
|
@TableField("user_agent")
|
||||||
private String deviceInfo;
|
private String deviceInfo;
|
||||||
|
|
||||||
private String ipAddress;
|
private String ipAddress;
|
||||||
|
|
||||||
|
@TableField("expire_time")
|
||||||
private LocalDateTime expiresAt;
|
private LocalDateTime expiresAt;
|
||||||
|
|
||||||
@TableField(fill = FieldFill.INSERT)
|
@TableField(fill = FieldFill.INSERT)
|
||||||
|
|||||||
+6
-1
@@ -39,7 +39,12 @@ public class AuthServiceImpl implements AuthService {
|
|||||||
@Transactional
|
@Transactional
|
||||||
public LoginVO login(LoginDTO dto, String ipAddress, String userAgent) {
|
public LoginVO login(LoginDTO dto, String ipAddress, String userAgent) {
|
||||||
User user = userMapper.selectOne(
|
User user = userMapper.selectOne(
|
||||||
new LambdaQueryWrapper<User>().eq(User::getEmail, dto.getEmail())
|
new LambdaQueryWrapper<User>()
|
||||||
|
.eq(User::getEmail, dto.getEmail())
|
||||||
|
.or()
|
||||||
|
.eq(User::getUsername, dto.getEmail())
|
||||||
|
.or()
|
||||||
|
.eq(User::getPhone, dto.getEmail())
|
||||||
);
|
);
|
||||||
|
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
|
|||||||
+20
-1
@@ -7,6 +7,8 @@ import fun.nojava.common.exception.ErrorCode;
|
|||||||
import fun.nojava.common.model.PageResult;
|
import fun.nojava.common.model.PageResult;
|
||||||
import fun.nojava.module.system.dto.UserInfoVO;
|
import fun.nojava.module.system.dto.UserInfoVO;
|
||||||
import fun.nojava.module.system.entity.User;
|
import fun.nojava.module.system.entity.User;
|
||||||
|
import fun.nojava.module.system.entity.UserFollow;
|
||||||
|
import fun.nojava.module.system.mapper.UserFollowMapper;
|
||||||
import fun.nojava.module.system.mapper.UserMapper;
|
import fun.nojava.module.system.mapper.UserMapper;
|
||||||
import fun.nojava.module.system.service.UserService;
|
import fun.nojava.module.system.service.UserService;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
@@ -20,6 +22,7 @@ import java.util.List;
|
|||||||
public class UserServiceImpl implements UserService {
|
public class UserServiceImpl implements UserService {
|
||||||
|
|
||||||
private final UserMapper userMapper;
|
private final UserMapper userMapper;
|
||||||
|
private final UserFollowMapper userFollowMapper;
|
||||||
private final PasswordEncoder passwordEncoder;
|
private final PasswordEncoder passwordEncoder;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -93,14 +96,30 @@ public class UserServiceImpl implements UserService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isFollowing(Long followerId, Long followeeId) {
|
public boolean isFollowing(Long followerId, Long followeeId) {
|
||||||
return false;
|
return userFollowMapper.selectCount(
|
||||||
|
new LambdaQueryWrapper<UserFollow>()
|
||||||
|
.eq(UserFollow::getFollowerId, followerId)
|
||||||
|
.eq(UserFollow::getFolloweeId, followeeId)
|
||||||
|
) > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void follow(Long followerId, Long followeeId) {
|
public void follow(Long followerId, Long followeeId) {
|
||||||
|
if (followerId == null || followeeId == null || followerId.equals(followeeId) || isFollowing(followerId, followeeId)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
UserFollow follow = new UserFollow();
|
||||||
|
follow.setFollowerId(followerId);
|
||||||
|
follow.setFolloweeId(followeeId);
|
||||||
|
userFollowMapper.insert(follow);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void unfollow(Long followerId, Long followeeId) {
|
public void unfollow(Long followerId, Long followeeId) {
|
||||||
|
userFollowMapper.delete(
|
||||||
|
new LambdaQueryWrapper<UserFollow>()
|
||||||
|
.eq(UserFollow::getFollowerId, followerId)
|
||||||
|
.eq(UserFollow::getFolloweeId, followeeId)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user