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

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 Response.success();
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;