feat(admin): implement category management functionality
- Added AddCategoryReqVO for category creation with validation - Created AdminCategoryController with endpoints for add, list, delete and select operations - Implemented AdminCategoryService interface and its methods - Added Category entity with JPA annotations and logical delete support - Created CategoryRepository extending JpaRepository with custom query methods - Added SQL table creation script for t_category with indexes and constraints - Implemented PageResponse utility for handling paginated results - Added FindCategoryPageListReqVO and FindCategoryPageListRspVO for pagination - Included DeleteCategoryReqVO for category deletion requests - Updated Jackson configuration to ignore unknown properties - Added base page query model and user info response VO - Fixed typo in response code enum for user not exist error
This commit is contained in:
@@ -8,6 +8,7 @@ dependencies {
|
||||
implementation(project(":weblog-module-common"))
|
||||
|
||||
api(project(":weblog-module-jwt"))
|
||||
implementation("org.springframework.boot:spring-boot-starter-validation")
|
||||
|
||||
testImplementation("org.springframework.boot:spring-boot-starter-test")
|
||||
testImplementation("org.springframework.security:spring-security-test")
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
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.service.AdminCategoryService;
|
||||
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 AdminCategoryController {
|
||||
|
||||
@Resource
|
||||
private AdminCategoryService adminCategoryService;
|
||||
|
||||
/**
|
||||
* 添加分类
|
||||
*/
|
||||
@PostMapping("/category/add")
|
||||
@ApiOperationLog(description = "添加分类")
|
||||
public Response<?> addCategory(@RequestBody @Validated AddCategoryReqVO addCategoryReqVO) {
|
||||
return adminCategoryService.addCategory(addCategoryReqVO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分类分页数据获取
|
||||
*/
|
||||
@PostMapping("/category/list")
|
||||
@ApiOperationLog(description = "分类分页数据获取")
|
||||
public PageResponse<FindCategoryPageListRspVO> findCategoryList(@RequestBody @Validated FindCategoryPageListReqVO findCategoryPageListReqVO) {
|
||||
return adminCategoryService.findCategoryList(findCategoryPageListReqVO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除分类
|
||||
*/
|
||||
@PostMapping("/category/delete")
|
||||
@ApiOperationLog(description = "删除分类")
|
||||
public Response<?> deleteCategory(@RequestBody @Validated DeleteCategoryReqVO deleteCategoryReqVO) {
|
||||
return adminCategoryService.deleteCategory(deleteCategoryReqVO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分类下拉列表
|
||||
*/
|
||||
@PostMapping("/category/select/list")
|
||||
@ApiOperationLog(description = "分类 Select 下拉列表数据获取")
|
||||
public Response<List<SelectRspVO>> findCategorySelectList() {
|
||||
return adminCategoryService.findCategorySelectList();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.hanserwei.admin.controller;
|
||||
|
||||
import com.hanserwei.admin.model.vo.FindUserInfoRspVO;
|
||||
import com.hanserwei.admin.model.vo.UpdateAdminUserPasswordReqVO;
|
||||
import com.hanserwei.admin.service.AdminUserService;
|
||||
import com.hanserwei.common.aspect.ApiOperationLog;
|
||||
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;
|
||||
|
||||
/**
|
||||
* 管理端用户控制器
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/admin")
|
||||
public class AdminUserController {
|
||||
|
||||
@Resource
|
||||
private AdminUserService adminUserService;
|
||||
|
||||
/**
|
||||
* 修改用户密码
|
||||
*/
|
||||
@PostMapping("/password/update")
|
||||
@ApiOperationLog(description = "修改用户密码")
|
||||
public Response<?> updatePassword(@RequestBody @Validated UpdateAdminUserPasswordReqVO updateAdminUserPasswordReqVO) {
|
||||
return adminUserService.updatePassword(updateAdminUserPasswordReqVO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户信息
|
||||
*/
|
||||
@PostMapping("/user/info")
|
||||
@ApiOperationLog(description = "获取用户信息")
|
||||
public Response<FindUserInfoRspVO> findUserInfo() {
|
||||
return adminUserService.findUserInfo();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.hanserwei.admin.model.vo;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.hibernate.validator.constraints.Length;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Builder
|
||||
public class AddCategoryReqVO {
|
||||
|
||||
/**
|
||||
* 分类名称
|
||||
*/
|
||||
@NotBlank(message = "分类名称不能为空")
|
||||
@Length(min = 1, max = 10, message = "分类名称字数限制 1 ~ 10 之间")
|
||||
private String name;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.hanserwei.admin.model.vo;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Builder
|
||||
public class DeleteCategoryReqVO {
|
||||
|
||||
@NotNull(message = "分类 ID 不能为空")
|
||||
private Long id;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.hanserwei.admin.model.vo;
|
||||
|
||||
import com.hanserwei.common.model.BasePageQuery;
|
||||
import lombok.*;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Builder
|
||||
public class FindCategoryPageListReqVO extends BasePageQuery {
|
||||
|
||||
/**
|
||||
* 分类名称
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 创建的起始日期
|
||||
*/
|
||||
private LocalDate startDate;
|
||||
|
||||
/**
|
||||
* 创建的结束日期
|
||||
*/
|
||||
private LocalDate endDate;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.hanserwei.admin.model.vo;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Builder
|
||||
public class FindCategoryPageListRspVO {
|
||||
/**
|
||||
* 分类 ID
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 分类名称
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private LocalDateTime createTime;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.hanserwei.admin.model.vo;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Builder
|
||||
public class FindUserInfoRspVO {
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
private String username;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.hanserwei.admin.model.vo;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Builder
|
||||
public class UpdateAdminUserPasswordReqVO {
|
||||
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
@NotBlank(message = "用户名不能为空")
|
||||
private String username;
|
||||
|
||||
/**
|
||||
* 新密码
|
||||
*/
|
||||
@NotBlank(message = "密码不能为空")
|
||||
private String password;
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
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.common.model.vo.SelectRspVO;
|
||||
import com.hanserwei.common.utils.PageResponse;
|
||||
import com.hanserwei.common.utils.Response;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface AdminCategoryService {
|
||||
/**
|
||||
* 添加分类
|
||||
*
|
||||
* @param addCategoryReqVO 添加分类请求参数
|
||||
* @return 添加结果
|
||||
*/
|
||||
Response<?> addCategory(AddCategoryReqVO addCategoryReqVO);
|
||||
|
||||
/**
|
||||
* 分类分页数据查询
|
||||
*
|
||||
* @param findCategoryPageListReqVO 分页查询分类参数
|
||||
* @return 查询结果
|
||||
*/
|
||||
PageResponse<FindCategoryPageListRspVO> findCategoryList(FindCategoryPageListReqVO findCategoryPageListReqVO);
|
||||
|
||||
/**
|
||||
* 删除分类
|
||||
*
|
||||
* @param deleteCategoryReqVO 删除分类参数
|
||||
* @return 删除结果
|
||||
*/
|
||||
Response<?> deleteCategory(DeleteCategoryReqVO deleteCategoryReqVO);
|
||||
|
||||
/**
|
||||
* 获取文章分类的 Select 列表数据
|
||||
*
|
||||
* @return Select 列表数据
|
||||
*/
|
||||
Response<List<SelectRspVO>> findCategorySelectList();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.hanserwei.admin.service;
|
||||
|
||||
import com.hanserwei.admin.model.vo.FindUserInfoRspVO;
|
||||
import com.hanserwei.admin.model.vo.UpdateAdminUserPasswordReqVO;
|
||||
import com.hanserwei.common.utils.Response;
|
||||
|
||||
public interface AdminUserService {
|
||||
/**
|
||||
* 修改密码
|
||||
* @param updateAdminUserPasswordReqVO 修改密码参数
|
||||
* @return 修改密码结果
|
||||
*/
|
||||
Response<?> updatePassword(UpdateAdminUserPasswordReqVO updateAdminUserPasswordReqVO);
|
||||
|
||||
/**
|
||||
* 获取当前登录用户信息
|
||||
* @return 当前登录用户信息
|
||||
*/
|
||||
Response<FindUserInfoRspVO> findUserInfo();
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
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.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.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 {
|
||||
|
||||
@Resource
|
||||
private CategoryRepository categoryRepository;
|
||||
|
||||
@Override
|
||||
public Response<?> addCategory(AddCategoryReqVO addCategoryReqVO) {
|
||||
String categoryName = addCategoryReqVO.getName();
|
||||
// 先判断是否存在
|
||||
if (categoryRepository.existsCategoryByName(categoryName)) {
|
||||
throw new BizException(ResponseCodeEnum.CATEGORY_NAME_IS_EXISTED);
|
||||
}
|
||||
// 构造Category对象
|
||||
Category category = Category.builder()
|
||||
.name(categoryName)
|
||||
.build();
|
||||
categoryRepository.save(category);
|
||||
return Response.success();
|
||||
}
|
||||
|
||||
@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()
|
||||
.id(category.getId())
|
||||
.name(category.getName())
|
||||
.createTime(LocalDateTime.ofInstant(category.getCreateTime(), ZoneId.systemDefault()))
|
||||
.build())
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return PageResponse.success(categoryDOPage, vos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response<?> deleteCategory(DeleteCategoryReqVO deleteCategoryReqVO) {
|
||||
Long id = deleteCategoryReqVO.getId();
|
||||
categoryRepository.deleteById(id);
|
||||
return Response.success();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response<List<SelectRspVO>> findCategorySelectList() {
|
||||
List<Category> categoryList = categoryRepository.findAll();
|
||||
// DO 转 VO
|
||||
List<SelectRspVO> selectRspVOS = null;
|
||||
if (!CollectionUtils.isEmpty(categoryList)){
|
||||
selectRspVOS = categoryList.stream()
|
||||
.map(category -> SelectRspVO.builder()
|
||||
.label(category.getName())
|
||||
.value(category.getId())
|
||||
.build())
|
||||
.toList();
|
||||
}
|
||||
return Response.success(selectRspVOS);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.hanserwei.admin.service.impl;
|
||||
|
||||
import com.hanserwei.admin.model.vo.FindUserInfoRspVO;
|
||||
import com.hanserwei.admin.model.vo.UpdateAdminUserPasswordReqVO;
|
||||
import com.hanserwei.admin.service.AdminUserService;
|
||||
import com.hanserwei.common.domain.repository.UserRepository;
|
||||
import com.hanserwei.common.enums.ResponseCodeEnum;
|
||||
import com.hanserwei.common.exception.BizException;
|
||||
import com.hanserwei.common.utils.Response;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class AdminUserServiceImpl implements AdminUserService {
|
||||
|
||||
@Resource
|
||||
private UserRepository userRepository;
|
||||
@Resource
|
||||
private PasswordEncoder passwordEncoder;
|
||||
|
||||
@Override
|
||||
public Response<?> updatePassword(UpdateAdminUserPasswordReqVO updateAdminUserPasswordReqVO) {
|
||||
// 拿到用户名密码
|
||||
String username = updateAdminUserPasswordReqVO.getUsername();
|
||||
String password = updateAdminUserPasswordReqVO.getPassword();
|
||||
|
||||
// 加密密码
|
||||
String encodePassword = passwordEncoder.encode(password);
|
||||
|
||||
int updatedRows = userRepository.updatePasswordByUsername(username, encodePassword);
|
||||
|
||||
if (updatedRows == 0) {
|
||||
// 如果更新行数为 0,说明用户名不存在
|
||||
throw new BizException(ResponseCodeEnum.USER_NOT_EXIST);
|
||||
}
|
||||
|
||||
return Response.success();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response<FindUserInfoRspVO> findUserInfo() {
|
||||
// 获取存储在 ThreadLocal 中的用户信息
|
||||
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||
// 拿到用户名
|
||||
String username = authentication.getName();
|
||||
return Response.success(new FindUserInfoRspVO(username));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user