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:
2025-12-04 23:18:10 +08:00
parent b7afe9496a
commit 304c458436
29 changed files with 641 additions and 127 deletions

1
.gitignore vendored
View File

@@ -66,3 +66,4 @@ Desktop.ini
application-prod.yml
application-dev.yml
.env
/.idea/.cache/.Apifox_Helper/.toolWindow.db

View File

@@ -96,3 +96,49 @@ CREATE TRIGGER set_t_category_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();
-- ====================================================================================================================
-- ====================================================================================================================

View File

@@ -1,9 +1,9 @@
package com.hanserwei.admin.controller;
import com.hanserwei.admin.model.vo.AddCategoryReqVO;
import com.hanserwei.admin.model.vo.DeleteCategoryReqVO;
import com.hanserwei.admin.model.vo.FindCategoryPageListReqVO;
import com.hanserwei.admin.model.vo.FindCategoryPageListRspVO;
import com.hanserwei.admin.model.vo.category.AddCategoryReqVO;
import com.hanserwei.admin.model.vo.category.DeleteCategoryReqVO;
import com.hanserwei.admin.model.vo.category.FindCategoryPageListReqVO;
import com.hanserwei.admin.model.vo.category.FindCategoryPageListRspVO;
import com.hanserwei.admin.service.AdminCategoryService;
import com.hanserwei.common.aspect.ApiOperationLog;
import com.hanserwei.common.model.vo.SelectRspVO;

View File

@@ -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);
}
}

View File

@@ -1,7 +1,7 @@
package com.hanserwei.admin.controller;
import com.hanserwei.admin.model.vo.FindUserInfoRspVO;
import com.hanserwei.admin.model.vo.UpdateAdminUserPasswordReqVO;
import com.hanserwei.admin.model.vo.user.FindUserInfoRspVO;
import com.hanserwei.admin.model.vo.user.UpdateAdminUserPasswordReqVO;
import com.hanserwei.admin.service.AdminUserService;
import com.hanserwei.common.aspect.ApiOperationLog;
import com.hanserwei.common.utils.Response;

View File

@@ -1,4 +1,4 @@
package com.hanserwei.admin.model.vo;
package com.hanserwei.admin.model.vo.category;
import jakarta.validation.constraints.NotBlank;
import lombok.AllArgsConstructor;

View File

@@ -1,4 +1,4 @@
package com.hanserwei.admin.model.vo;
package com.hanserwei.admin.model.vo.category;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;

View File

@@ -1,4 +1,4 @@
package com.hanserwei.admin.model.vo;
package com.hanserwei.admin.model.vo.category;
import com.hanserwei.common.model.BasePageQuery;
import lombok.*;

View File

@@ -1,4 +1,4 @@
package com.hanserwei.admin.model.vo;
package com.hanserwei.admin.model.vo.category;
import lombok.AllArgsConstructor;
import lombok.Builder;

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -1,4 +1,4 @@
package com.hanserwei.admin.model.vo;
package com.hanserwei.admin.model.vo.user;
import lombok.AllArgsConstructor;
import lombok.Builder;

View File

@@ -1,4 +1,4 @@
package com.hanserwei.admin.model.vo;
package com.hanserwei.admin.model.vo.user;
import jakarta.validation.constraints.NotBlank;
import lombok.AllArgsConstructor;

View File

@@ -1,9 +1,9 @@
package com.hanserwei.admin.service;
import com.hanserwei.admin.model.vo.AddCategoryReqVO;
import com.hanserwei.admin.model.vo.DeleteCategoryReqVO;
import com.hanserwei.admin.model.vo.FindCategoryPageListReqVO;
import com.hanserwei.admin.model.vo.FindCategoryPageListRspVO;
import com.hanserwei.admin.model.vo.category.AddCategoryReqVO;
import com.hanserwei.admin.model.vo.category.DeleteCategoryReqVO;
import com.hanserwei.admin.model.vo.category.FindCategoryPageListReqVO;
import com.hanserwei.admin.model.vo.category.FindCategoryPageListRspVO;
import com.hanserwei.common.model.vo.SelectRspVO;
import com.hanserwei.common.utils.PageResponse;
import com.hanserwei.common.utils.Response;

View File

@@ -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);
}

View File

@@ -1,7 +1,7 @@
package com.hanserwei.admin.service;
import com.hanserwei.admin.model.vo.FindUserInfoRspVO;
import com.hanserwei.admin.model.vo.UpdateAdminUserPasswordReqVO;
import com.hanserwei.admin.model.vo.user.FindUserInfoRspVO;
import com.hanserwei.admin.model.vo.user.UpdateAdminUserPasswordReqVO;
import com.hanserwei.common.utils.Response;
public interface AdminUserService {

View File

@@ -1,34 +1,25 @@
package com.hanserwei.admin.service.impl;
import com.hanserwei.admin.model.vo.AddCategoryReqVO;
import com.hanserwei.admin.model.vo.DeleteCategoryReqVO;
import com.hanserwei.admin.model.vo.FindCategoryPageListReqVO;
import com.hanserwei.admin.model.vo.FindCategoryPageListRspVO;
import com.hanserwei.admin.model.vo.category.AddCategoryReqVO;
import com.hanserwei.admin.model.vo.category.DeleteCategoryReqVO;
import com.hanserwei.admin.model.vo.category.FindCategoryPageListReqVO;
import com.hanserwei.admin.model.vo.category.FindCategoryPageListRspVO;
import com.hanserwei.admin.service.AdminCategoryService;
import com.hanserwei.common.domain.dataobject.Category;
import com.hanserwei.common.domain.repository.CategoryRepository;
import com.hanserwei.common.enums.ResponseCodeEnum;
import com.hanserwei.common.exception.BizException;
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 io.jsonwebtoken.lang.Strings;
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.util.CollectionUtils;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
@Service
public class AdminCategoryServiceImpl implements AdminCategoryService {
@@ -53,53 +44,29 @@ public class AdminCategoryServiceImpl implements AdminCategoryService {
@Override
public PageResponse<FindCategoryPageListRspVO> findCategoryList(FindCategoryPageListReqVO findCategoryPageListReqVO) {
Long current = findCategoryPageListReqVO.getCurrent();
Long size = findCategoryPageListReqVO.getSize();
Pageable pageable = PageRequest.of(current.intValue() - 1,
size.intValue(),
Sort.by(Sort.Direction.DESC, "createTime"));
// 构建查询条件
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()
return PageHelper.findPageList(
categoryRepository,
findCategoryPageListReqVO,
findCategoryPageListReqVO.getName(),
findCategoryPageListReqVO.getStartDate(),
findCategoryPageListReqVO.getEndDate(),
category -> FindCategoryPageListRspVO.builder()
.id(category.getId())
.name(category.getName())
.createTime(LocalDateTime.ofInstant(category.getCreateTime(), ZoneId.systemDefault()))
.build())
.collect(Collectors.toList());
return PageResponse.success(categoryDOPage, vos);
.build()
);
}
@Override
public Response<?> deleteCategory(DeleteCategoryReqVO deleteCategoryReqVO) {
Long id = deleteCategoryReqVO.getId();
categoryRepository.deleteById(id);
return categoryRepository.findById(deleteCategoryReqVO.getId())
.map(tag -> {
tag.setIsDeleted(true);
categoryRepository.save(tag);
return Response.success();
})
.orElse(Response.fail(ResponseCodeEnum.CATEGORY_NOT_EXIST));
}
@Override
@@ -107,7 +74,7 @@ public class AdminCategoryServiceImpl implements AdminCategoryService {
List<Category> categoryList = categoryRepository.findAll();
// DO 转 VO
List<SelectRspVO> selectRspVOS = null;
if (!CollectionUtils.isEmpty(categoryList)){
if (!CollectionUtils.isEmpty(categoryList)) {
selectRspVOS = categoryList.stream()
.map(category -> SelectRspVO.builder()
.label(category.getName())

View File

@@ -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);
}
}

View File

@@ -1,7 +1,7 @@
package com.hanserwei.admin.service.impl;
import com.hanserwei.admin.model.vo.FindUserInfoRspVO;
import com.hanserwei.admin.model.vo.UpdateAdminUserPasswordReqVO;
import com.hanserwei.admin.model.vo.user.FindUserInfoRspVO;
import com.hanserwei.admin.model.vo.user.UpdateAdminUserPasswordReqVO;
import com.hanserwei.admin.service.AdminUserService;
import com.hanserwei.common.domain.repository.UserRepository;
import com.hanserwei.common.enums.ResponseCodeEnum;

View File

@@ -27,7 +27,6 @@ import java.time.Instant;
@Index(name = "idx_create_time", columnList = "create_time")
})
@SQLRestriction("is_deleted = false")
@SQLDelete(sql = "UPDATE t_category SET is_deleted = true WHERE id = ?")
public class Category implements Serializable {
@Serial

View File

@@ -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;
}

View File

@@ -5,8 +5,9 @@ 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;
public interface CategoryRepository extends JpaRepository<Category, Long> {
public interface CategoryRepository extends JpaRepository<Category, Long>, JpaSpecificationExecutor<Category> {
boolean existsCategoryByName(String name);
Page<Category> findAll(Specification<Category> specification, Pageable pageable);

View File

@@ -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);
}

View File

@@ -18,8 +18,9 @@ public enum ResponseCodeEnum implements BaseExceptionInterface {
UNAUTHORIZED("20002", "无访问权限,请先登录!"),
FORBIDDEN("20004", "演示账号仅支持查询操作!"),
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;

View File

@@ -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);
}
}

View File

@@ -44,6 +44,9 @@ public class TokenAuthenticationFilter extends OncePerRequestFilter {
// 1. 从请求头中获取 Authorization
String header = request.getHeader("Authorization");
String requestURI = request.getRequestURI();
log.info("Request URI: {}", requestURI);
if (requestURI.startsWith("/admin")) {
// 2. 校验头格式 (必须以 Bearer 开头)
if (header != null && header.startsWith("Bearer ")) {
String token = StringUtils.substring(header, 7);
@@ -97,5 +100,8 @@ public class TokenAuthenticationFilter extends OncePerRequestFilter {
// 8. 放行请求 (如果没有 Token 或者 Token 校验通过,继续执行下一个过滤器)
filterChain.doFilter(request, response);
} else {
filterChain.doFilter(request, response);
}
}
}