[新增][商品评价]管理后台

1. 显示 / 隐藏评论
2. 商家回复
pull/2/head
wangzhs 2023-03-19 22:07:07 +08:00
parent da53a041da
commit cbf5ef5953
9 changed files with 179 additions and 5 deletions

View File

@ -42,4 +42,7 @@ public interface ErrorCodeConstants {
ErrorCode SPU_SKU_NOT_DUPLICATE = new ErrorCode(1008006003, "一个 SPU 下的每个 SKU必须不重复"); ErrorCode SPU_SKU_NOT_DUPLICATE = new ErrorCode(1008006003, "一个 SPU 下的每个 SKU必须不重复");
ErrorCode SKU_STOCK_NOT_ENOUGH = new ErrorCode(1008006004, "商品 SKU 库存不足"); ErrorCode SKU_STOCK_NOT_ENOUGH = new ErrorCode(1008006004, "商品 SKU 库存不足");
// ========== 商品 评价 1008007000 ==========
ErrorCode COMMENT_NOT_EXISTS = new ErrorCode(1008007000, "商品 评价 不存在");
} }

View File

@ -3,7 +3,9 @@ package cn.iocoder.yudao.module.product.controller.admin.comment;
import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.product.controller.admin.comment.vo.ProductCommentPageReqVO; import cn.iocoder.yudao.module.product.controller.admin.comment.vo.ProductCommentPageReqVO;
import cn.iocoder.yudao.module.product.controller.admin.comment.vo.ProductCommentReplyVO;
import cn.iocoder.yudao.module.product.controller.admin.comment.vo.ProductCommentRespVO; import cn.iocoder.yudao.module.product.controller.admin.comment.vo.ProductCommentRespVO;
import cn.iocoder.yudao.module.product.controller.admin.comment.vo.ProductCommentUpdateVisibleReqVO;
import cn.iocoder.yudao.module.product.convert.comment.ProductCommentConvert; import cn.iocoder.yudao.module.product.convert.comment.ProductCommentConvert;
import cn.iocoder.yudao.module.product.dal.dataobject.comment.ProductCommentDO; import cn.iocoder.yudao.module.product.dal.dataobject.comment.ProductCommentDO;
import cn.iocoder.yudao.module.product.service.comment.ProductCommentService; import cn.iocoder.yudao.module.product.service.comment.ProductCommentService;
@ -11,14 +13,13 @@ import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource; import javax.annotation.Resource;
import javax.validation.Valid; import javax.validation.Valid;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
@Tag(name = "管理后台 - 商品评价") @Tag(name = "管理后台 - 商品评价")
@RestController @RestController
@ -37,4 +38,20 @@ public class ProductCommentController {
return success(ProductCommentConvert.INSTANCE.convertPage(pageResult)); return success(ProductCommentConvert.INSTANCE.convertPage(pageResult));
} }
@PutMapping("/update/visible")
@Operation(summary = "显示 / 隐藏评论")
@PreAuthorize("@ss.hasPermission('product:comment:update')")
public CommonResult<Boolean> updateCommentVisible(@Valid @RequestBody ProductCommentUpdateVisibleReqVO updateReqVO) {
productCommentService.updateCommentVisible(updateReqVO);
return success(true);
}
@PutMapping("/reply")
@Operation(summary = "商家回复")
@PreAuthorize("@ss.hasPermission('product:comment:update')")
public CommonResult<Boolean> commentReply(@Valid @RequestBody ProductCommentReplyVO replyVO) {
productCommentService.commentReply(replyVO, getLoginUserId());
return success(true);
}
} }

View File

@ -35,6 +35,9 @@ public class ProductCommentPageReqVO extends PageParam {
@InEnum(ProductCommentScoresEnum.class) @InEnum(ProductCommentScoresEnum.class)
private Integer scores; private Integer scores;
@Schema(description = "商家是否回复")
private Boolean replied;
@Schema(description = "创建时间") @Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime; private LocalDateTime[] createTime;

View File

@ -0,0 +1,22 @@
package cn.iocoder.yudao.module.product.controller.admin.comment.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.ToString;
import javax.validation.constraints.NotNull;
@Schema(description = "管理后台 - 商品评价可见修改 Request VO")
@Data
@ToString(callSuper = true)
public class ProductCommentReplyVO {
@Schema(description = "评价编号", required = true, example = "15721")
@NotNull(message = "评价编号不能为空")
private Long id;
@Schema(description = "商家回复内容", required = true, example = "谢谢亲")
@NotNull(message = "商家回复内容不能为空")
private String replyContent;
}

View File

@ -0,0 +1,22 @@
package cn.iocoder.yudao.module.product.controller.admin.comment.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.ToString;
import javax.validation.constraints.NotNull;
@Schema(description = "管理后台 - 商品评价可见修改 Request VO")
@Data
@ToString(callSuper = true)
public class ProductCommentUpdateVisibleReqVO {
@Schema(description = "评价编号", required = true, example = "15721")
@NotNull(message = "评价编号不能为空")
private Long id;
@Schema(description = "是否可见 true:显示 false:隐藏", required = true, example = "false")
@NotNull(message = "是否可见不能为空")
private Boolean visible;
}

View File

@ -5,9 +5,13 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.product.controller.admin.comment.vo.ProductCommentPageReqVO; import cn.iocoder.yudao.module.product.controller.admin.comment.vo.ProductCommentPageReqVO;
import cn.iocoder.yudao.module.product.controller.admin.comment.vo.ProductCommentReplyVO;
import cn.iocoder.yudao.module.product.dal.dataobject.comment.ProductCommentDO; import cn.iocoder.yudao.module.product.dal.dataobject.comment.ProductCommentDO;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import java.time.LocalDateTime;
/** /**
* Mapper * Mapper
@ -28,4 +32,20 @@ public interface ProductCommentMapper extends BaseMapperX<ProductCommentDO> {
.orderByDesc(ProductCommentDO::getId)); .orderByDesc(ProductCommentDO::getId));
} }
default void updateCommentVisible(Long id, Boolean visible) {
LambdaUpdateWrapper<ProductCommentDO> lambdaUpdateWrapper = new LambdaUpdateWrapper<ProductCommentDO>()
.set(ProductCommentDO::getVisible, visible)
.eq(ProductCommentDO::getId, id);
update(null, lambdaUpdateWrapper);
}
default void commentReply(ProductCommentReplyVO replyVO, Long loginUserId) {
LambdaUpdateWrapper<ProductCommentDO> lambdaUpdateWrapper = new LambdaUpdateWrapper<ProductCommentDO>()
.set(ProductCommentDO::getReplied, Boolean.TRUE)
.set(ProductCommentDO::getReplyTime, LocalDateTime.now())
.set(ProductCommentDO::getReplyUserId, loginUserId)
.set(ProductCommentDO::getReplyContent, replyVO.getReplyContent())
.eq(ProductCommentDO::getId, replyVO.getId());
update(null, lambdaUpdateWrapper);
}
} }

View File

@ -2,6 +2,8 @@ package cn.iocoder.yudao.module.product.service.comment;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.product.controller.admin.comment.vo.ProductCommentPageReqVO; import cn.iocoder.yudao.module.product.controller.admin.comment.vo.ProductCommentPageReqVO;
import cn.iocoder.yudao.module.product.controller.admin.comment.vo.ProductCommentReplyVO;
import cn.iocoder.yudao.module.product.controller.admin.comment.vo.ProductCommentUpdateVisibleReqVO;
import cn.iocoder.yudao.module.product.dal.dataobject.comment.ProductCommentDO; import cn.iocoder.yudao.module.product.dal.dataobject.comment.ProductCommentDO;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
@ -23,4 +25,19 @@ public interface ProductCommentService {
*/ */
PageResult<ProductCommentDO> getCommentPage(ProductCommentPageReqVO pageReqVO); PageResult<ProductCommentDO> getCommentPage(ProductCommentPageReqVO pageReqVO);
/**
*
*
* @param updateReqVO
*/
void updateCommentVisible(ProductCommentUpdateVisibleReqVO updateReqVO);
/**
*
*
* @param replyVO
* @param loginUserId ID
*/
void commentReply(ProductCommentReplyVO replyVO, Long loginUserId);
} }

View File

@ -2,6 +2,8 @@ package cn.iocoder.yudao.module.product.service.comment;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.product.controller.admin.comment.vo.ProductCommentPageReqVO; import cn.iocoder.yudao.module.product.controller.admin.comment.vo.ProductCommentPageReqVO;
import cn.iocoder.yudao.module.product.controller.admin.comment.vo.ProductCommentReplyVO;
import cn.iocoder.yudao.module.product.controller.admin.comment.vo.ProductCommentUpdateVisibleReqVO;
import cn.iocoder.yudao.module.product.dal.dataobject.comment.ProductCommentDO; import cn.iocoder.yudao.module.product.dal.dataobject.comment.ProductCommentDO;
import cn.iocoder.yudao.module.product.dal.mysql.comment.ProductCommentMapper; import cn.iocoder.yudao.module.product.dal.mysql.comment.ProductCommentMapper;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -9,6 +11,9 @@ import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource; import javax.annotation.Resource;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.COMMENT_NOT_EXISTS;
/** /**
* Service * Service
* *
@ -26,4 +31,27 @@ public class ProductCommentServiceImpl implements ProductCommentService {
return productCommentMapper.selectPage(pageReqVO); return productCommentMapper.selectPage(pageReqVO);
} }
@Override
public void updateCommentVisible(ProductCommentUpdateVisibleReqVO updateReqVO) {
// 校验评论是否存在
validateCommentExists(updateReqVO.getId());
productCommentMapper.updateCommentVisible(updateReqVO.getId(), updateReqVO.getVisible());
}
@Override
public void commentReply(ProductCommentReplyVO replyVO, Long loginUserId) {
// 校验评论是否存在
validateCommentExists(replyVO.getId());
productCommentMapper.commentReply(replyVO, loginUserId);
}
private void validateCommentExists(Long id) {
if (productCommentMapper.selectById(id) == null) {
throw exception(COMMENT_NOT_EXISTS);
}
}
} }

View File

@ -5,7 +5,9 @@ import cn.hutool.core.util.RandomUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
import cn.iocoder.yudao.module.product.controller.admin.comment.vo.ProductCommentPageReqVO; import cn.iocoder.yudao.module.product.controller.admin.comment.vo.ProductCommentPageReqVO;
import cn.iocoder.yudao.module.product.controller.admin.comment.vo.ProductCommentReplyVO;
import cn.iocoder.yudao.module.product.controller.admin.comment.vo.ProductCommentRespVO; import cn.iocoder.yudao.module.product.controller.admin.comment.vo.ProductCommentRespVO;
import cn.iocoder.yudao.module.product.controller.admin.comment.vo.ProductCommentUpdateVisibleReqVO;
import cn.iocoder.yudao.module.product.convert.comment.ProductCommentConvert; import cn.iocoder.yudao.module.product.convert.comment.ProductCommentConvert;
import cn.iocoder.yudao.module.product.dal.dataobject.comment.ProductCommentDO; import cn.iocoder.yudao.module.product.dal.dataobject.comment.ProductCommentDO;
import cn.iocoder.yudao.module.product.dal.mysql.comment.ProductCommentMapper; import cn.iocoder.yudao.module.product.dal.mysql.comment.ProductCommentMapper;
@ -19,7 +21,7 @@ import java.util.Date;
import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.*;
/** /**
* {@link ProductCommentServiceImpl} * {@link ProductCommentServiceImpl}
@ -62,6 +64,7 @@ public class ProductCommentServiceImplTest extends BaseDbUnitTest {
o.setUserNickname("王二狗"); o.setUserNickname("王二狗");
o.setSpuName("感冒药"); o.setSpuName("感冒药");
o.setScores(ProductCommentScoresEnum.FOUR.getScores()); o.setScores(ProductCommentScoresEnum.FOUR.getScores());
o.setReplied(Boolean.TRUE);
}); });
productCommentMapper.insert(productComment); productCommentMapper.insert(productComment);
@ -78,6 +81,8 @@ public class ProductCommentServiceImplTest extends BaseDbUnitTest {
productCommentMapper.insert(cloneIgnoreId(productComment, o -> o.setSpuName("感康"))); productCommentMapper.insert(cloneIgnoreId(productComment, o -> o.setSpuName("感康")));
// 测试 scores 不匹配 // 测试 scores 不匹配
productCommentMapper.insert(cloneIgnoreId(productComment, o -> o.setScores(ProductCommentScoresEnum.ONE.getScores()))); productCommentMapper.insert(cloneIgnoreId(productComment, o -> o.setScores(ProductCommentScoresEnum.ONE.getScores())));
// 测试 replied 不匹配
productCommentMapper.insert(cloneIgnoreId(productComment, o -> o.setReplied(Boolean.FALSE)));
// 调用 // 调用
ProductCommentPageReqVO productCommentPageReqVO = new ProductCommentPageReqVO(); ProductCommentPageReqVO productCommentPageReqVO = new ProductCommentPageReqVO();
@ -86,13 +91,50 @@ public class ProductCommentServiceImplTest extends BaseDbUnitTest {
productCommentPageReqVO.setSpuId(spuId); productCommentPageReqVO.setSpuId(spuId);
productCommentPageReqVO.setSpuName("感冒药"); productCommentPageReqVO.setSpuName("感冒药");
productCommentPageReqVO.setScores(ProductCommentScoresEnum.FOUR.getScores()); productCommentPageReqVO.setScores(ProductCommentScoresEnum.FOUR.getScores());
productCommentPageReqVO.setReplied(Boolean.TRUE);
PageResult<ProductCommentDO> commentPage = productCommentService.getCommentPage(productCommentPageReqVO); PageResult<ProductCommentDO> commentPage = productCommentService.getCommentPage(productCommentPageReqVO);
PageResult<ProductCommentRespVO> result = ProductCommentConvert.INSTANCE.convertPage(productCommentMapper.selectPage(productCommentPageReqVO)); PageResult<ProductCommentRespVO> result = ProductCommentConvert.INSTANCE.convertPage(productCommentMapper.selectPage(productCommentPageReqVO));
assertEquals(result.getTotal(), commentPage.getTotal()); assertEquals(result.getTotal(), commentPage.getTotal());
PageResult<ProductCommentDO> all = productCommentService.getCommentPage(new ProductCommentPageReqVO()); PageResult<ProductCommentDO> all = productCommentService.getCommentPage(new ProductCommentPageReqVO());
assertEquals(6, all.getTotal()); assertEquals(7, all.getTotal());
} }
@Test
public void testUpdateCommentVisible_success() {
// mock 测试
ProductCommentDO productComment = randomPojo(ProductCommentDO.class, o -> {
o.setVisible(Boolean.TRUE);
});
productCommentMapper.insert(productComment);
Long productCommentId = productComment.getId();
ProductCommentUpdateVisibleReqVO updateReqVO = new ProductCommentUpdateVisibleReqVO();
updateReqVO.setId(productCommentId);
updateReqVO.setVisible(Boolean.FALSE);
productCommentService.updateCommentVisible(updateReqVO);
ProductCommentDO productCommentDO = productCommentMapper.selectById(productCommentId);
assertFalse(productCommentDO.getVisible());
}
@Test
public void testCommentReply_success() {
// mock 测试
ProductCommentDO productComment = randomPojo(ProductCommentDO.class);
productCommentMapper.insert(productComment);
Long productCommentId = productComment.getId();
ProductCommentReplyVO replyVO = new ProductCommentReplyVO();
replyVO.setId(productCommentId);
replyVO.setReplyContent("测试");
productCommentService.commentReply(replyVO, 1L);
ProductCommentDO productCommentDO = productCommentMapper.selectById(productCommentId);
assertEquals("测试", productCommentDO.getReplyContent());
}
} }