refactor(admin): 重构管理模块VO包结构并新增标签管理功能
- 将分类相关VO移至com.hanserwei.admin.model.vo.category包下,用户相关VO移至user包 - 新增标签管理相关VO,包括AddTagReqVO、DeleteTagReqVO、FindTagPageListReqVO、FindTagPageListRspVO、SearchTagReqVO - 增加AdminTagController,实现标签的增删查和分页查询接口 - 实现AdminTagService及其Impl,完成标签的增删查分页功能 - 新增Tag实体及TagRepository,支持标签数据的持久化及模糊查询 - 优化AdminCategoryServiceImpl分页查询逻辑,提取公共分页查询工具类PageHelper - 修改CategoryRepository继承JpaSpecificationExecutor,支持动态查询 - 修改TokenAuthenticationFilter,限制JWT认证仅校验/admin路径请求 - 修改Category实体删除注解,调整逻辑删除实现 - 新增数据库脚本,创建t_tag标签表及相关索引和触发器 - 更新ResponseCodeEnum,增加TAG_NOT_EXIST和CATEGORY_NOT_EXIST错误码 - 调整.gitignore,忽略.idea下Apifox相关缓存文件
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -66,3 +66,4 @@ Desktop.ini
|
|||||||
application-prod.yml
|
application-prod.yml
|
||||||
application-dev.yml
|
application-dev.yml
|
||||||
.env
|
.env
|
||||||
|
/.idea/.cache/.Apifox_Helper/.toolWindow.db
|
||||||
|
|||||||
@@ -38,8 +38,8 @@ COMMENT ON COLUMN t_user.is_deleted IS '逻辑删除:FALSE:未删除 TRUE:
|
|||||||
CREATE TABLE t_user_role
|
CREATE TABLE t_user_role
|
||||||
(
|
(
|
||||||
id BIGSERIAL PRIMARY KEY,
|
id BIGSERIAL PRIMARY KEY,
|
||||||
username VARCHAR(60) NOT NULL,
|
username VARCHAR(60) NOT NULL,
|
||||||
role_name VARCHAR(60) NOT NULL, -- 重命名为 role_name 避免关键字冲突
|
role_name VARCHAR(60) NOT NULL, -- 重命名为 role_name 避免关键字冲突
|
||||||
create_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
|
create_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -63,7 +63,7 @@ CREATE TABLE t_category
|
|||||||
id BIGSERIAL PRIMARY KEY,
|
id BIGSERIAL PRIMARY KEY,
|
||||||
|
|
||||||
-- 分类名称:VARCHAR(60) NOT NULL DEFAULT '',同时是 UNIQUE 约束
|
-- 分类名称:VARCHAR(60) NOT NULL DEFAULT '',同时是 UNIQUE 约束
|
||||||
"name" VARCHAR(60) NOT NULL DEFAULT '',
|
"name" VARCHAR(60) NOT NULL DEFAULT '',
|
||||||
|
|
||||||
-- 创建时间
|
-- 创建时间
|
||||||
create_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
create_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
||||||
@@ -72,7 +72,7 @@ CREATE TABLE t_category
|
|||||||
update_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
update_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
||||||
|
|
||||||
-- 逻辑删除标志位:tinyint(2) NOT NULL DEFAULT '0',改为 BOOLEAN
|
-- 逻辑删除标志位:tinyint(2) NOT NULL DEFAULT '0',改为 BOOLEAN
|
||||||
is_deleted BOOLEAN NOT NULL DEFAULT FALSE,
|
is_deleted BOOLEAN NOT NULL DEFAULT FALSE,
|
||||||
|
|
||||||
-- UNIQUE KEY uk_name (`name`)
|
-- UNIQUE KEY uk_name (`name`)
|
||||||
CONSTRAINT uk_name UNIQUE ("name")
|
CONSTRAINT uk_name UNIQUE ("name")
|
||||||
@@ -96,3 +96,49 @@ CREATE TRIGGER set_t_category_update_time
|
|||||||
EXECUTE FUNCTION set_update_time();
|
EXECUTE FUNCTION set_update_time();
|
||||||
-- ====================================================================================================================
|
-- ====================================================================================================================
|
||||||
-- ====================================================================================================================
|
-- ====================================================================================================================
|
||||||
|
-- ====================================================================================================================
|
||||||
|
-- ====================================================================================================================
|
||||||
|
-- 1. 创建表结构
|
||||||
|
CREATE TABLE t_tag
|
||||||
|
(
|
||||||
|
-- id: 使用 BIG SERIAL,自动创建序列,性能优异
|
||||||
|
id BIGSERIAL PRIMARY KEY,
|
||||||
|
|
||||||
|
-- name: 保持 VARCHAR(60),但在 PG 中 TEXT 和 VARCHAR 性能一样,
|
||||||
|
-- 这里为了保留原表 "60字符限制" 的业务逻辑,继续使用 VARCHAR(60)
|
||||||
|
name VARCHAR(60) NOT NULL DEFAULT '',
|
||||||
|
|
||||||
|
-- create_time: 使用带时区的时间戳,更标准严谨
|
||||||
|
create_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
|
||||||
|
-- update_time: 同上
|
||||||
|
update_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
|
||||||
|
-- is_deleted: 使用原生 BOOLEAN 类型,存储效率高且语义明确
|
||||||
|
is_deleted BOOLEAN NOT NULL DEFAULT FALSE,
|
||||||
|
|
||||||
|
-- 约束:显式命名约束,方便后续维护(如报错时能看到具体约束名)
|
||||||
|
CONSTRAINT uk_tag_name UNIQUE (name)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 2. 创建普通索引
|
||||||
|
-- 对应 MySQL 的 KEY `idx_create_time`
|
||||||
|
CREATE INDEX idx_tag_create_time ON t_tag (create_time);
|
||||||
|
|
||||||
|
-- 3. 添加注释 (PostgreSQL 标准方式)
|
||||||
|
COMMENT ON TABLE t_tag IS '文章标签表';
|
||||||
|
COMMENT ON COLUMN t_tag.id IS '标签id';
|
||||||
|
COMMENT ON COLUMN t_tag.name IS '标签名称';
|
||||||
|
COMMENT ON COLUMN t_tag.create_time IS '创建时间';
|
||||||
|
COMMENT ON COLUMN t_tag.update_time IS '最后一次更新时间';
|
||||||
|
COMMENT ON COLUMN t_tag.is_deleted IS '逻辑删除标志位:FALSE:未删除 TRUE:已删除';
|
||||||
|
|
||||||
|
-- 4. 应用自动更新时间戳触发器 (体现 PostgreSQL 强大的过程语言优势)
|
||||||
|
-- 前提:您之前已经执行过 CREATE FUNCTION set_update_time() ...
|
||||||
|
CREATE TRIGGER set_t_tag_update_time
|
||||||
|
BEFORE UPDATE
|
||||||
|
ON t_tag
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE FUNCTION set_update_time();
|
||||||
|
-- ====================================================================================================================
|
||||||
|
-- ====================================================================================================================
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
package com.hanserwei.admin.controller;
|
package com.hanserwei.admin.controller;
|
||||||
|
|
||||||
import com.hanserwei.admin.model.vo.AddCategoryReqVO;
|
import com.hanserwei.admin.model.vo.category.AddCategoryReqVO;
|
||||||
import com.hanserwei.admin.model.vo.DeleteCategoryReqVO;
|
import com.hanserwei.admin.model.vo.category.DeleteCategoryReqVO;
|
||||||
import com.hanserwei.admin.model.vo.FindCategoryPageListReqVO;
|
import com.hanserwei.admin.model.vo.category.FindCategoryPageListReqVO;
|
||||||
import com.hanserwei.admin.model.vo.FindCategoryPageListRspVO;
|
import com.hanserwei.admin.model.vo.category.FindCategoryPageListRspVO;
|
||||||
import com.hanserwei.admin.service.AdminCategoryService;
|
import com.hanserwei.admin.service.AdminCategoryService;
|
||||||
import com.hanserwei.common.aspect.ApiOperationLog;
|
import com.hanserwei.common.aspect.ApiOperationLog;
|
||||||
import com.hanserwei.common.model.vo.SelectRspVO;
|
import com.hanserwei.common.model.vo.SelectRspVO;
|
||||||
|
|||||||
@@ -0,0 +1,67 @@
|
|||||||
|
package com.hanserwei.admin.controller;
|
||||||
|
|
||||||
|
import com.hanserwei.admin.model.vo.category.FindCategoryPageListReqVO;
|
||||||
|
import com.hanserwei.admin.model.vo.category.FindCategoryPageListRspVO;
|
||||||
|
import com.hanserwei.admin.model.vo.tag.*;
|
||||||
|
import com.hanserwei.admin.service.AdminTagService;
|
||||||
|
import com.hanserwei.common.aspect.ApiOperationLog;
|
||||||
|
import com.hanserwei.common.model.vo.SelectRspVO;
|
||||||
|
import com.hanserwei.common.utils.PageResponse;
|
||||||
|
import com.hanserwei.common.utils.Response;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 管理端标签控制器
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/admin")
|
||||||
|
public class AdminTagController {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private AdminTagService adminTagService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加标签
|
||||||
|
*/
|
||||||
|
@PostMapping("/tag/add")
|
||||||
|
@ApiOperationLog(description = "添加标签")
|
||||||
|
public Response<?> addTag(@RequestBody @Validated AddTagReqVO addTagReqVO) {
|
||||||
|
return adminTagService.addTag(addTagReqVO);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标签分页数据获取
|
||||||
|
*/
|
||||||
|
@PostMapping("/tag/list")
|
||||||
|
@ApiOperationLog(description = "标签分页数据获取")
|
||||||
|
public PageResponse<FindTagPageListRspVO> findTagList(@RequestBody @Validated FindTagPageListReqVO findTagPageListReqVO) {
|
||||||
|
return adminTagService.findTagList(findTagPageListReqVO);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除标签
|
||||||
|
*/
|
||||||
|
@PostMapping("/tag/delete")
|
||||||
|
@ApiOperationLog(description = "删除标签")
|
||||||
|
public Response<?> deleteTag(@RequestBody @Validated DeleteTagReqVO deleteTagReqVO) {
|
||||||
|
return adminTagService.deleteTag(deleteTagReqVO);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标签模糊查询
|
||||||
|
*/
|
||||||
|
@PostMapping("/tag/search")
|
||||||
|
@ApiOperationLog(description = "标签模糊查询")
|
||||||
|
public Response<List<SelectRspVO>> searchTag(@RequestBody @Validated SearchTagReqVO searchTagReqVO) {
|
||||||
|
return adminTagService.searchTag(searchTagReqVO);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.hanserwei.admin.controller;
|
package com.hanserwei.admin.controller;
|
||||||
|
|
||||||
import com.hanserwei.admin.model.vo.FindUserInfoRspVO;
|
import com.hanserwei.admin.model.vo.user.FindUserInfoRspVO;
|
||||||
import com.hanserwei.admin.model.vo.UpdateAdminUserPasswordReqVO;
|
import com.hanserwei.admin.model.vo.user.UpdateAdminUserPasswordReqVO;
|
||||||
import com.hanserwei.admin.service.AdminUserService;
|
import com.hanserwei.admin.service.AdminUserService;
|
||||||
import com.hanserwei.common.aspect.ApiOperationLog;
|
import com.hanserwei.common.aspect.ApiOperationLog;
|
||||||
import com.hanserwei.common.utils.Response;
|
import com.hanserwei.common.utils.Response;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.hanserwei.admin.model.vo;
|
package com.hanserwei.admin.model.vo.category;
|
||||||
|
|
||||||
import jakarta.validation.constraints.NotBlank;
|
import jakarta.validation.constraints.NotBlank;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.hanserwei.admin.model.vo;
|
package com.hanserwei.admin.model.vo.category;
|
||||||
|
|
||||||
import jakarta.validation.constraints.NotNull;
|
import jakarta.validation.constraints.NotNull;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.hanserwei.admin.model.vo;
|
package com.hanserwei.admin.model.vo.category;
|
||||||
|
|
||||||
import com.hanserwei.common.model.BasePageQuery;
|
import com.hanserwei.common.model.BasePageQuery;
|
||||||
import lombok.*;
|
import lombok.*;
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.hanserwei.admin.model.vo;
|
package com.hanserwei.admin.model.vo.category;
|
||||||
|
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Builder;
|
import lombok.Builder;
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package com.hanserwei.admin.model.vo.tag;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotEmpty;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
@Builder
|
||||||
|
public class AddTagReqVO {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标签集合
|
||||||
|
*/
|
||||||
|
@NotEmpty(message = "标签集合 不能为空")
|
||||||
|
private List<String> tags;
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package com.hanserwei.admin.model.vo.tag;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
@Builder
|
||||||
|
public class DeleteTagReqVO {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标签 ID
|
||||||
|
*/
|
||||||
|
@NotNull(message = "标签 ID 不能为空")
|
||||||
|
private Long id;
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package com.hanserwei.admin.model.vo.tag;
|
||||||
|
|
||||||
|
import com.hanserwei.common.model.BasePageQuery;
|
||||||
|
import lombok.*;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
@Builder
|
||||||
|
public class FindTagPageListReqVO extends BasePageQuery {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标签名称
|
||||||
|
*/
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建的起始日期
|
||||||
|
*/
|
||||||
|
private LocalDate startDate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建的结束日期
|
||||||
|
*/
|
||||||
|
private LocalDate endDate;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package com.hanserwei.admin.model.vo.tag;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
@Builder
|
||||||
|
public class FindTagPageListRspVO {
|
||||||
|
/**
|
||||||
|
* 标签 ID
|
||||||
|
*/
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标签名称
|
||||||
|
*/
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package com.hanserwei.admin.model.vo.tag;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotEmpty;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
@Builder
|
||||||
|
public class SearchTagReqVO {
|
||||||
|
|
||||||
|
@NotEmpty(message = "标签查询关键词不能为空!")
|
||||||
|
private String key;
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.hanserwei.admin.model.vo;
|
package com.hanserwei.admin.model.vo.user;
|
||||||
|
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Builder;
|
import lombok.Builder;
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.hanserwei.admin.model.vo;
|
package com.hanserwei.admin.model.vo.user;
|
||||||
|
|
||||||
import jakarta.validation.constraints.NotBlank;
|
import jakarta.validation.constraints.NotBlank;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
package com.hanserwei.admin.service;
|
package com.hanserwei.admin.service;
|
||||||
|
|
||||||
import com.hanserwei.admin.model.vo.AddCategoryReqVO;
|
import com.hanserwei.admin.model.vo.category.AddCategoryReqVO;
|
||||||
import com.hanserwei.admin.model.vo.DeleteCategoryReqVO;
|
import com.hanserwei.admin.model.vo.category.DeleteCategoryReqVO;
|
||||||
import com.hanserwei.admin.model.vo.FindCategoryPageListReqVO;
|
import com.hanserwei.admin.model.vo.category.FindCategoryPageListReqVO;
|
||||||
import com.hanserwei.admin.model.vo.FindCategoryPageListRspVO;
|
import com.hanserwei.admin.model.vo.category.FindCategoryPageListRspVO;
|
||||||
import com.hanserwei.common.model.vo.SelectRspVO;
|
import com.hanserwei.common.model.vo.SelectRspVO;
|
||||||
import com.hanserwei.common.utils.PageResponse;
|
import com.hanserwei.common.utils.PageResponse;
|
||||||
import com.hanserwei.common.utils.Response;
|
import com.hanserwei.common.utils.Response;
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package com.hanserwei.admin.service;
|
||||||
|
|
||||||
|
import com.hanserwei.admin.model.vo.tag.*;
|
||||||
|
import com.hanserwei.common.model.vo.SelectRspVO;
|
||||||
|
import com.hanserwei.common.utils.PageResponse;
|
||||||
|
import com.hanserwei.common.utils.Response;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface AdminTagService {
|
||||||
|
/**
|
||||||
|
* 添加标签
|
||||||
|
*
|
||||||
|
* @param addTagReqVO 添加标签请求参数
|
||||||
|
* @return 响应结果
|
||||||
|
*/
|
||||||
|
Response<?> addTag(AddTagReqVO addTagReqVO);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标签分页数据获取
|
||||||
|
*
|
||||||
|
* @param findTagPageListReqVO 标签分页数据获取请求参数
|
||||||
|
* @return 响应结果
|
||||||
|
*/
|
||||||
|
PageResponse<FindTagPageListRspVO> findTagList(FindTagPageListReqVO findTagPageListReqVO);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除标签
|
||||||
|
*
|
||||||
|
* @param deleteTagReqVO 删除标签请求参数
|
||||||
|
* @return 响应结果
|
||||||
|
*/
|
||||||
|
Response<?> deleteTag(DeleteTagReqVO deleteTagReqVO);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标签下拉列表数据获取
|
||||||
|
*
|
||||||
|
* @param searchTagReqVO 搜索标签请求参数
|
||||||
|
* @return 响应结果
|
||||||
|
*/
|
||||||
|
Response<List<SelectRspVO>> searchTag(SearchTagReqVO searchTagReqVO);
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.hanserwei.admin.service;
|
package com.hanserwei.admin.service;
|
||||||
|
|
||||||
import com.hanserwei.admin.model.vo.FindUserInfoRspVO;
|
import com.hanserwei.admin.model.vo.user.FindUserInfoRspVO;
|
||||||
import com.hanserwei.admin.model.vo.UpdateAdminUserPasswordReqVO;
|
import com.hanserwei.admin.model.vo.user.UpdateAdminUserPasswordReqVO;
|
||||||
import com.hanserwei.common.utils.Response;
|
import com.hanserwei.common.utils.Response;
|
||||||
|
|
||||||
public interface AdminUserService {
|
public interface AdminUserService {
|
||||||
|
|||||||
@@ -1,34 +1,25 @@
|
|||||||
package com.hanserwei.admin.service.impl;
|
package com.hanserwei.admin.service.impl;
|
||||||
|
|
||||||
import com.hanserwei.admin.model.vo.AddCategoryReqVO;
|
import com.hanserwei.admin.model.vo.category.AddCategoryReqVO;
|
||||||
import com.hanserwei.admin.model.vo.DeleteCategoryReqVO;
|
import com.hanserwei.admin.model.vo.category.DeleteCategoryReqVO;
|
||||||
import com.hanserwei.admin.model.vo.FindCategoryPageListReqVO;
|
import com.hanserwei.admin.model.vo.category.FindCategoryPageListReqVO;
|
||||||
import com.hanserwei.admin.model.vo.FindCategoryPageListRspVO;
|
import com.hanserwei.admin.model.vo.category.FindCategoryPageListRspVO;
|
||||||
import com.hanserwei.admin.service.AdminCategoryService;
|
import com.hanserwei.admin.service.AdminCategoryService;
|
||||||
import com.hanserwei.common.domain.dataobject.Category;
|
import com.hanserwei.common.domain.dataobject.Category;
|
||||||
import com.hanserwei.common.domain.repository.CategoryRepository;
|
import com.hanserwei.common.domain.repository.CategoryRepository;
|
||||||
import com.hanserwei.common.enums.ResponseCodeEnum;
|
import com.hanserwei.common.enums.ResponseCodeEnum;
|
||||||
import com.hanserwei.common.exception.BizException;
|
import com.hanserwei.common.exception.BizException;
|
||||||
import com.hanserwei.common.model.vo.SelectRspVO;
|
import com.hanserwei.common.model.vo.SelectRspVO;
|
||||||
|
import com.hanserwei.common.utils.PageHelper;
|
||||||
import com.hanserwei.common.utils.PageResponse;
|
import com.hanserwei.common.utils.PageResponse;
|
||||||
import com.hanserwei.common.utils.Response;
|
import com.hanserwei.common.utils.Response;
|
||||||
import io.jsonwebtoken.lang.Strings;
|
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import jakarta.persistence.criteria.Predicate;
|
|
||||||
import org.springframework.data.domain.Page;
|
|
||||||
import org.springframework.data.domain.PageRequest;
|
|
||||||
import org.springframework.data.domain.Pageable;
|
|
||||||
import org.springframework.data.domain.Sort;
|
|
||||||
import org.springframework.data.jpa.domain.Specification;
|
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.util.CollectionUtils;
|
import org.springframework.util.CollectionUtils;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.time.ZoneId;
|
import java.time.ZoneId;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class AdminCategoryServiceImpl implements AdminCategoryService {
|
public class AdminCategoryServiceImpl implements AdminCategoryService {
|
||||||
@@ -53,53 +44,29 @@ public class AdminCategoryServiceImpl implements AdminCategoryService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PageResponse<FindCategoryPageListRspVO> findCategoryList(FindCategoryPageListReqVO findCategoryPageListReqVO) {
|
public PageResponse<FindCategoryPageListRspVO> findCategoryList(FindCategoryPageListReqVO findCategoryPageListReqVO) {
|
||||||
Long current = findCategoryPageListReqVO.getCurrent();
|
return PageHelper.findPageList(
|
||||||
Long size = findCategoryPageListReqVO.getSize();
|
categoryRepository,
|
||||||
|
findCategoryPageListReqVO,
|
||||||
Pageable pageable = PageRequest.of(current.intValue() - 1,
|
findCategoryPageListReqVO.getName(),
|
||||||
size.intValue(),
|
findCategoryPageListReqVO.getStartDate(),
|
||||||
Sort.by(Sort.Direction.DESC, "createTime"));
|
findCategoryPageListReqVO.getEndDate(),
|
||||||
|
category -> FindCategoryPageListRspVO.builder()
|
||||||
// 构建查询条件
|
|
||||||
Specification<Category> specification = (root, query, criteriaBuilder) -> {
|
|
||||||
List<Predicate> predicates = new ArrayList<>();
|
|
||||||
String name = findCategoryPageListReqVO.getName();
|
|
||||||
if (Strings.hasText(name)) {
|
|
||||||
predicates.add(
|
|
||||||
criteriaBuilder.like(root.get("name"), "%" + name.trim() + "%")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (Objects.nonNull(findCategoryPageListReqVO.getStartDate())){
|
|
||||||
predicates.add(
|
|
||||||
criteriaBuilder.greaterThanOrEqualTo(root.get("createTime"), findCategoryPageListReqVO.getStartDate())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (Objects.nonNull(findCategoryPageListReqVO.getEndDate())) {
|
|
||||||
predicates.add(
|
|
||||||
criteriaBuilder.lessThan(root.get("createTime"), findCategoryPageListReqVO.getEndDate().plusDays(1).atStartOfDay())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
|
|
||||||
};
|
|
||||||
|
|
||||||
Page<Category> categoryDOPage = categoryRepository.findAll(specification, pageable);
|
|
||||||
|
|
||||||
List<FindCategoryPageListRspVO> vos = categoryDOPage.getContent().stream()
|
|
||||||
.map(category -> FindCategoryPageListRspVO.builder()
|
|
||||||
.id(category.getId())
|
.id(category.getId())
|
||||||
.name(category.getName())
|
.name(category.getName())
|
||||||
.createTime(LocalDateTime.ofInstant(category.getCreateTime(), ZoneId.systemDefault()))
|
.createTime(LocalDateTime.ofInstant(category.getCreateTime(), ZoneId.systemDefault()))
|
||||||
.build())
|
.build()
|
||||||
.collect(Collectors.toList());
|
);
|
||||||
|
|
||||||
return PageResponse.success(categoryDOPage, vos);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Response<?> deleteCategory(DeleteCategoryReqVO deleteCategoryReqVO) {
|
public Response<?> deleteCategory(DeleteCategoryReqVO deleteCategoryReqVO) {
|
||||||
Long id = deleteCategoryReqVO.getId();
|
return categoryRepository.findById(deleteCategoryReqVO.getId())
|
||||||
categoryRepository.deleteById(id);
|
.map(tag -> {
|
||||||
return Response.success();
|
tag.setIsDeleted(true);
|
||||||
|
categoryRepository.save(tag);
|
||||||
|
return Response.success();
|
||||||
|
})
|
||||||
|
.orElse(Response.fail(ResponseCodeEnum.CATEGORY_NOT_EXIST));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -107,7 +74,7 @@ public class AdminCategoryServiceImpl implements AdminCategoryService {
|
|||||||
List<Category> categoryList = categoryRepository.findAll();
|
List<Category> categoryList = categoryRepository.findAll();
|
||||||
// DO 转 VO
|
// DO 转 VO
|
||||||
List<SelectRspVO> selectRspVOS = null;
|
List<SelectRspVO> selectRspVOS = null;
|
||||||
if (!CollectionUtils.isEmpty(categoryList)){
|
if (!CollectionUtils.isEmpty(categoryList)) {
|
||||||
selectRspVOS = categoryList.stream()
|
selectRspVOS = categoryList.stream()
|
||||||
.map(category -> SelectRspVO.builder()
|
.map(category -> SelectRspVO.builder()
|
||||||
.label(category.getName())
|
.label(category.getName())
|
||||||
|
|||||||
@@ -0,0 +1,106 @@
|
|||||||
|
package com.hanserwei.admin.service.impl;
|
||||||
|
|
||||||
|
import com.hanserwei.admin.model.vo.tag.*;
|
||||||
|
import com.hanserwei.admin.service.AdminTagService;
|
||||||
|
import com.hanserwei.common.domain.dataobject.Tag;
|
||||||
|
import com.hanserwei.common.domain.repository.TagRepository;
|
||||||
|
import com.hanserwei.common.enums.ResponseCodeEnum;
|
||||||
|
import com.hanserwei.common.model.vo.SelectRspVO;
|
||||||
|
import com.hanserwei.common.utils.PageHelper;
|
||||||
|
import com.hanserwei.common.utils.PageResponse;
|
||||||
|
import com.hanserwei.common.utils.Response;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.ZoneId;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class AdminTagServiceImpl implements AdminTagService {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private TagRepository tagRepository;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加标签
|
||||||
|
*
|
||||||
|
* @param addTagReqVO 添加标签请求对象
|
||||||
|
* @return 响应结果
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Response<?> addTag(AddTagReqVO addTagReqVO) {
|
||||||
|
// 获取标签列表
|
||||||
|
List<String> tagList = addTagReqVO.getTags();
|
||||||
|
|
||||||
|
// 对标签进行清洗:去除空格、过滤空字符串、去重
|
||||||
|
List<String> names = tagList.stream()
|
||||||
|
.map(String::trim) // 去除首尾空格
|
||||||
|
.filter(s -> !s.isEmpty()) // 过滤空字符串
|
||||||
|
.distinct() // 去重
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
// 查询数据库中已存在的标签名称
|
||||||
|
List<String> exists = tagRepository.findByNameIn(names).stream()
|
||||||
|
.map(Tag::getName)
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
// 筛选出需要新建的标签,并构建标签对象
|
||||||
|
List<Tag> toCreate = names.stream()
|
||||||
|
.filter(n -> !exists.contains(n)) // 过滤掉已存在的标签
|
||||||
|
.map(n -> Tag.builder().name(n).build()) // 构建标签对象
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
// 批量保存新标签到数据库
|
||||||
|
if (!toCreate.isEmpty()) {
|
||||||
|
tagRepository.saveAllAndFlush(toCreate);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Response.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PageResponse<FindTagPageListRspVO> findTagList(FindTagPageListReqVO findTagPageListReqVO) {
|
||||||
|
return PageHelper.findPageList(
|
||||||
|
tagRepository,
|
||||||
|
findTagPageListReqVO,
|
||||||
|
findTagPageListReqVO.getName(),
|
||||||
|
findTagPageListReqVO.getStartDate(),
|
||||||
|
findTagPageListReqVO.getEndDate(),
|
||||||
|
tag -> FindTagPageListRspVO.builder()
|
||||||
|
.id(tag.getId())
|
||||||
|
.name(tag.getName())
|
||||||
|
.createTime(LocalDateTime.ofInstant(tag.getCreateTime(), ZoneId.systemDefault()))
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Response<?> deleteTag(DeleteTagReqVO deleteTagReqVO) {
|
||||||
|
|
||||||
|
return tagRepository.findById(deleteTagReqVO.getId())
|
||||||
|
.map(tag -> {
|
||||||
|
tag.setIsDeleted(true);
|
||||||
|
tagRepository.save(tag);
|
||||||
|
return Response.success();
|
||||||
|
})
|
||||||
|
.orElse(Response.fail(ResponseCodeEnum.TAG_NOT_EXIST));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Response<List<SelectRspVO>> searchTag(SearchTagReqVO searchTagReqVO) {
|
||||||
|
// 使用模糊查询获取标签列表
|
||||||
|
List<Tag> tags = tagRepository.findByNameContaining(searchTagReqVO.getKey());
|
||||||
|
|
||||||
|
// 将标签转换为下拉列表格式
|
||||||
|
List<SelectRspVO> vos = tags.stream()
|
||||||
|
.map(tag -> SelectRspVO.builder()
|
||||||
|
.label(tag.getName())
|
||||||
|
.value(tag.getId())
|
||||||
|
.build())
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
return Response.success(vos);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.hanserwei.admin.service.impl;
|
package com.hanserwei.admin.service.impl;
|
||||||
|
|
||||||
import com.hanserwei.admin.model.vo.FindUserInfoRspVO;
|
import com.hanserwei.admin.model.vo.user.FindUserInfoRspVO;
|
||||||
import com.hanserwei.admin.model.vo.UpdateAdminUserPasswordReqVO;
|
import com.hanserwei.admin.model.vo.user.UpdateAdminUserPasswordReqVO;
|
||||||
import com.hanserwei.admin.service.AdminUserService;
|
import com.hanserwei.admin.service.AdminUserService;
|
||||||
import com.hanserwei.common.domain.repository.UserRepository;
|
import com.hanserwei.common.domain.repository.UserRepository;
|
||||||
import com.hanserwei.common.enums.ResponseCodeEnum;
|
import com.hanserwei.common.enums.ResponseCodeEnum;
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ import java.time.Instant;
|
|||||||
@Index(name = "idx_create_time", columnList = "create_time")
|
@Index(name = "idx_create_time", columnList = "create_time")
|
||||||
})
|
})
|
||||||
@SQLRestriction("is_deleted = false")
|
@SQLRestriction("is_deleted = false")
|
||||||
@SQLDelete(sql = "UPDATE t_category SET is_deleted = true WHERE id = ?")
|
|
||||||
public class Category implements Serializable {
|
public class Category implements Serializable {
|
||||||
|
|
||||||
@Serial
|
@Serial
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package com.hanserwei.common.domain.dataobject;
|
||||||
|
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
import lombok.*;
|
||||||
|
import org.hibernate.annotations.CreationTimestamp;
|
||||||
|
import org.hibernate.annotations.SQLRestriction;
|
||||||
|
import org.hibernate.annotations.UpdateTimestamp;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.time.Instant;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Builder
|
||||||
|
@Entity
|
||||||
|
@Table(name = "t_tag", indexes = {
|
||||||
|
@Index(name = "idx_tag_create_time", columnList = "create_time")
|
||||||
|
})
|
||||||
|
@SQLRestriction("is_deleted = false")
|
||||||
|
public class Tag implements Serializable {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Column(nullable = false, length = 60, unique = true)
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@CreationTimestamp
|
||||||
|
@Column(name = "create_time", nullable = false, updatable = false)
|
||||||
|
private Instant createTime;
|
||||||
|
|
||||||
|
@UpdateTimestamp
|
||||||
|
@Column(name = "update_time", nullable = false)
|
||||||
|
private Instant updateTime;
|
||||||
|
|
||||||
|
@Column(name = "is_deleted", nullable = false)
|
||||||
|
@Builder.Default
|
||||||
|
private Boolean isDeleted = false;
|
||||||
|
}
|
||||||
@@ -5,8 +5,9 @@ import org.springframework.data.domain.Page;
|
|||||||
import org.springframework.data.domain.Pageable;
|
import org.springframework.data.domain.Pageable;
|
||||||
import org.springframework.data.jpa.domain.Specification;
|
import org.springframework.data.jpa.domain.Specification;
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||||
|
|
||||||
public interface CategoryRepository extends JpaRepository<Category, Long> {
|
public interface CategoryRepository extends JpaRepository<Category, Long>, JpaSpecificationExecutor<Category> {
|
||||||
boolean existsCategoryByName(String name);
|
boolean existsCategoryByName(String name);
|
||||||
|
|
||||||
Page<Category> findAll(Specification<Category> specification, Pageable pageable);
|
Page<Category> findAll(Specification<Category> specification, Pageable pageable);
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package com.hanserwei.common.domain.repository;
|
||||||
|
|
||||||
|
import com.hanserwei.common.domain.dataobject.Tag;
|
||||||
|
import org.springframework.data.domain.Page;
|
||||||
|
import org.springframework.data.domain.Pageable;
|
||||||
|
import org.springframework.data.jpa.domain.Specification;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface TagRepository extends JpaRepository<Tag, Long>, JpaSpecificationExecutor<Tag> {
|
||||||
|
Collection<Tag> findByNameIn(List<String> names);
|
||||||
|
|
||||||
|
Page<Tag> findAll(Specification<Tag> specification, Pageable pageable);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据标签名称模糊查询
|
||||||
|
* @param name 标签名称关键词
|
||||||
|
* @return 标签列表
|
||||||
|
*/
|
||||||
|
List<Tag> findByNameContaining(String name);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -18,8 +18,9 @@ public enum ResponseCodeEnum implements BaseExceptionInterface {
|
|||||||
UNAUTHORIZED("20002", "无访问权限,请先登录!"),
|
UNAUTHORIZED("20002", "无访问权限,请先登录!"),
|
||||||
FORBIDDEN("20004", "演示账号仅支持查询操作!"),
|
FORBIDDEN("20004", "演示账号仅支持查询操作!"),
|
||||||
USER_NOT_EXIST("2005", "有户不存在!"),
|
USER_NOT_EXIST("2005", "有户不存在!"),
|
||||||
CATEGORY_NAME_IS_EXISTED("20005", "该分类已存在,请勿重复添加!")
|
CATEGORY_NAME_IS_EXISTED("20005", "该分类已存在,请勿重复添加!"),
|
||||||
;
|
TAG_NOT_EXIST("20006", "标签不存在!"),
|
||||||
|
CATEGORY_NOT_EXIST("20007", "分类不存在!" );
|
||||||
|
|
||||||
// 异常码
|
// 异常码
|
||||||
private final String errorCode;
|
private final String errorCode;
|
||||||
|
|||||||
@@ -0,0 +1,91 @@
|
|||||||
|
package com.hanserwei.common.utils;
|
||||||
|
|
||||||
|
import com.hanserwei.common.model.BasePageQuery;
|
||||||
|
import jakarta.persistence.criteria.Predicate;
|
||||||
|
import org.springframework.data.domain.Page;
|
||||||
|
import org.springframework.data.domain.PageRequest;
|
||||||
|
import org.springframework.data.domain.Pageable;
|
||||||
|
import org.springframework.data.domain.Sort;
|
||||||
|
import org.springframework.data.jpa.domain.Specification;
|
||||||
|
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页查询工具类
|
||||||
|
* 用于抽取通用的分页查询逻辑
|
||||||
|
*/
|
||||||
|
public class PageHelper {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行带条件的分页查询
|
||||||
|
*
|
||||||
|
* @param repository JPA Repository
|
||||||
|
* @param pageQuery 分页查询参数
|
||||||
|
* @param name 名称(用于模糊查询)
|
||||||
|
* @param startDate 开始日期
|
||||||
|
* @param endDate 结束日期
|
||||||
|
* @param converter DO 到 VO 的转换函数
|
||||||
|
* @param <T> 实体类型
|
||||||
|
* @param <R> 响应VO类型
|
||||||
|
* @return 分页响应
|
||||||
|
*/
|
||||||
|
public static <T, R> PageResponse<R> findPageList(
|
||||||
|
JpaSpecificationExecutor<T> repository,
|
||||||
|
BasePageQuery pageQuery,
|
||||||
|
String name,
|
||||||
|
LocalDate startDate,
|
||||||
|
LocalDate endDate,
|
||||||
|
Function<T, R> converter) {
|
||||||
|
|
||||||
|
// 构建分页参数
|
||||||
|
Pageable pageable = PageRequest.of(
|
||||||
|
pageQuery.getCurrent().intValue() - 1,
|
||||||
|
pageQuery.getSize().intValue(),
|
||||||
|
Sort.by(Sort.Direction.DESC, "createTime")
|
||||||
|
);
|
||||||
|
|
||||||
|
// 构建查询条件
|
||||||
|
Specification<T> specification = (root, query, criteriaBuilder) -> {
|
||||||
|
List<Predicate> predicates = new ArrayList<>();
|
||||||
|
|
||||||
|
// 名称模糊查询
|
||||||
|
if (StringUtils.hasText(name)) {
|
||||||
|
predicates.add(
|
||||||
|
criteriaBuilder.like(root.get("name"), "%" + name.trim() + "%")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 开始日期查询
|
||||||
|
if (Objects.nonNull(startDate)) {
|
||||||
|
predicates.add(
|
||||||
|
criteriaBuilder.greaterThanOrEqualTo(root.get("createTime"), startDate)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 结束日期查询
|
||||||
|
if (Objects.nonNull(endDate)) {
|
||||||
|
predicates.add(
|
||||||
|
criteriaBuilder.lessThan(root.get("createTime"), endDate.plusDays(1).atStartOfDay())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
|
||||||
|
};
|
||||||
|
|
||||||
|
// 执行查询
|
||||||
|
Page<T> page = repository.findAll(specification, pageable);
|
||||||
|
|
||||||
|
// DO 转 VO
|
||||||
|
List<R> vos = page.getContent().stream()
|
||||||
|
.map(converter)
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
return PageResponse.success(page, vos);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -44,58 +44,64 @@ public class TokenAuthenticationFilter extends OncePerRequestFilter {
|
|||||||
// 1. 从请求头中获取 Authorization
|
// 1. 从请求头中获取 Authorization
|
||||||
String header = request.getHeader("Authorization");
|
String header = request.getHeader("Authorization");
|
||||||
|
|
||||||
// 2. 校验头格式 (必须以 Bearer 开头)
|
String requestURI = request.getRequestURI();
|
||||||
if (header != null && header.startsWith("Bearer ")) {
|
log.info("Request URI: {}", requestURI);
|
||||||
String token = StringUtils.substring(header, 7);
|
if (requestURI.startsWith("/admin")) {
|
||||||
log.info("JWT Token: {}", token);
|
// 2. 校验头格式 (必须以 Bearer 开头)
|
||||||
if (StringUtils.isNotBlank(token)) {
|
if (header != null && header.startsWith("Bearer ")) {
|
||||||
try {
|
String token = StringUtils.substring(header, 7);
|
||||||
// 3. 解析 Token (核心步骤)
|
log.info("JWT Token: {}", token);
|
||||||
// 注意:JwtTokenHelper.parseToken 方法内部已经处理了 JWT 格式校验和过期校验,
|
if (StringUtils.isNotBlank(token)) {
|
||||||
// 并将 JJWT 异常转换为了 Spring Security 的 AuthenticationException 抛出。
|
try {
|
||||||
Jws<Claims> claimsJws = jwtTokenHelper.parseToken(token);
|
// 3. 解析 Token (核心步骤)
|
||||||
|
// 注意:JwtTokenHelper.parseToken 方法内部已经处理了 JWT 格式校验和过期校验,
|
||||||
|
// 并将 JJWT 异常转换为了 Spring Security 的 AuthenticationException 抛出。
|
||||||
|
Jws<Claims> claimsJws = jwtTokenHelper.parseToken(token);
|
||||||
|
|
||||||
// 4. 获取用户名
|
// 4. 获取用户名
|
||||||
// JJWT 0.12+ 建议使用 getPayload() 替代 getBody()
|
// JJWT 0.12+ 建议使用 getPayload() 替代 getBody()
|
||||||
// 在 Helper 中生成 Token 时使用的是 .subject(username),所以这里取 Subject
|
// 在 Helper 中生成 Token 时使用的是 .subject(username),所以这里取 Subject
|
||||||
String username = claimsJws.getPayload().getSubject();
|
String username = claimsJws.getPayload().getSubject();
|
||||||
|
|
||||||
// 5. 组装认证信息 (如果当前上下文没有认证信息)
|
// 5. 组装认证信息 (如果当前上下文没有认证信息)
|
||||||
if (StringUtils.isNotBlank(username) && Objects.isNull(SecurityContextHolder.getContext().getAuthentication())) {
|
if (StringUtils.isNotBlank(username) && Objects.isNull(SecurityContextHolder.getContext().getAuthentication())) {
|
||||||
|
|
||||||
// 查询数据库获取用户完整信息 (包含权限)
|
// 查询数据库获取用户完整信息 (包含权限)
|
||||||
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
|
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
|
||||||
|
|
||||||
// 构建 Spring Security 的认证 Token
|
// 构建 Spring Security 的认证 Token
|
||||||
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
|
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
|
||||||
userDetails,
|
userDetails,
|
||||||
null,
|
null,
|
||||||
userDetails.getAuthorities()
|
userDetails.getAuthorities()
|
||||||
);
|
);
|
||||||
|
|
||||||
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
|
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
|
||||||
|
|
||||||
// 6. 将认证信息存入 SecurityContext
|
// 6. 将认证信息存入 SecurityContext
|
||||||
SecurityContextHolder.getContext().setAuthentication(authentication);
|
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||||
|
}
|
||||||
|
} catch (AuthenticationException e) {
|
||||||
|
// 7. 异常处理
|
||||||
|
// 捕获 JwtTokenHelper 抛出的 BadCredentialsException 或 CredentialsExpiredException
|
||||||
|
// 如果 Token 存在但是无效/过期,直接交给 EntryPoint 处理响应 (通常返回 401)
|
||||||
|
// 并 return 结束当前过滤器,不再往下执行
|
||||||
|
authenticationEntryPoint.commence(request, response, e);
|
||||||
|
return;
|
||||||
|
} catch (Exception e) {
|
||||||
|
// 处理其他未预料到的异常
|
||||||
|
log.error("JWT处理过程中发生未知错误", e);
|
||||||
|
authenticationEntryPoint.commence(request, response, new AuthenticationException("系统内部认证错误") {
|
||||||
|
});
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
} catch (AuthenticationException e) {
|
|
||||||
// 7. 异常处理
|
|
||||||
// 捕获 JwtTokenHelper 抛出的 BadCredentialsException 或 CredentialsExpiredException
|
|
||||||
// 如果 Token 存在但是无效/过期,直接交给 EntryPoint 处理响应 (通常返回 401)
|
|
||||||
// 并 return 结束当前过滤器,不再往下执行
|
|
||||||
authenticationEntryPoint.commence(request, response, e);
|
|
||||||
return;
|
|
||||||
} catch (Exception e) {
|
|
||||||
// 处理其他未预料到的异常
|
|
||||||
log.error("JWT处理过程中发生未知错误", e);
|
|
||||||
authenticationEntryPoint.commence(request, response, new AuthenticationException("系统内部认证错误") {
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// 8. 放行请求 (如果没有 Token 或者 Token 校验通过,继续执行下一个过滤器)
|
// 8. 放行请求 (如果没有 Token 或者 Token 校验通过,继续执行下一个过滤器)
|
||||||
filterChain.doFilter(request, response);
|
filterChain.doFilter(request, response);
|
||||||
|
} else {
|
||||||
|
filterChain.doFilter(request, response);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user