fix:完善商品评论

This commit is contained in:
puhui999 2023-06-12 12:08:55 +08:00
parent dde89d51d5
commit 20100aa78b
27 changed files with 536 additions and 326 deletions

View File

@ -0,0 +1,21 @@
package cn.iocoder.yudao.module.product.api.comment;
import cn.iocoder.yudao.module.product.api.comment.dto.CommentCreateReqDTO;
/**
* 产品评论 API 接口
*
* @author HUIHUI
*/
public interface ProductCommentApi {
/**
* 创建评论
*
* @param createReqDTO 评论参数
* @param orderId 订单 id
* @return 返回评论创建后的 id
*/
Long createComment(CommentCreateReqDTO createReqDTO, Long orderId);
}

View File

@ -0,0 +1,80 @@
package cn.iocoder.yudao.module.product.api.comment.dto;
import lombok.Data;
import java.util.List;
/**
* 评论创建请求 DTO
*
* @author HUIHUI
*/
@Data
public class CommentCreateReqDTO {
/**
* 是否匿名
*/
private Boolean anonymous;
/**
* 交易订单项编号
*/
private Long orderItemId;
/**
* 商品 SPU 编号
*/
private Long spuId;
/**
* 商品 SPU 名称
*/
private String spuName;
/**
* 商品 SKU 编号
*/
private Long skuId;
/**
* 评分星级 1-5
*/
private Integer scores;
/**
* 描述星级 1-5
*/
private Integer descriptionScores;
/**
* 服务星级 1-5
*/
private Integer benefitScores;
/**
* 评论内容
*/
private String content;
/**
* 评论图片地址数组以逗号分隔最多上传9张
*/
private List<String> picUrls;
/**
* 评价人名称
*/
private String userNickname;
/**
* 评价人头像
*/
private String userAvatar;
/**
* 评价人
*/
private Long userId;
}

View File

@ -0,0 +1,28 @@
package cn.iocoder.yudao.module.product.api.comment;
import cn.iocoder.yudao.module.product.api.comment.dto.CommentCreateReqDTO;
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.service.comment.ProductCommentService;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
/**
* 商品评论 API 实现类
*
* @author HUIHUI
*/
@Service
@Validated
public class ProductCommentApiImpl implements ProductCommentApi {
@Resource
private ProductCommentService productCommentService;
@Override
public Long createComment(CommentCreateReqDTO createReqDTO, Long orderId) {
ProductCommentDO commentDO = ProductCommentConvert.INSTANCE.convert(createReqDTO, orderId);
return productCommentService.createComment(commentDO);
}
}

View File

@ -35,8 +35,7 @@ public class ProductCommentController {
return success(ProductCommentConvert.INSTANCE.convertPage(pageResult));
}
// TODO @puhui999update-visible
@PutMapping("/update/visible")
@PutMapping("/update-visible")
@Operation(summary = "显示 / 隐藏评论")
@PreAuthorize("@ss.hasPermission('product:comment:update')")
public CommonResult<Boolean> updateCommentVisible(@Valid @RequestBody ProductCommentUpdateVisibleReqVO updateReqVO) {
@ -48,7 +47,7 @@ public class ProductCommentController {
@Operation(summary = "商家回复")
@PreAuthorize("@ss.hasPermission('product:comment:update')")
public CommonResult<Boolean> commentReply(@Valid @RequestBody ProductCommentReplyVO replyVO) {
productCommentService.commentReply(replyVO, getLoginUserId());
productCommentService.replyComment(replyVO, getLoginUserId());
return success(true);
}
@ -56,8 +55,7 @@ public class ProductCommentController {
@Operation(summary = "添加自评")
@PreAuthorize("@ss.hasPermission('product:comment:update')")
public CommonResult<Boolean> createComment(@Valid @RequestBody ProductCommentCreateReqVO createReqVO) {
// TODO @puhui999不用 ProductCommentConvert.INSTANCE.convert(createReqVO) 多写一个 create 方法即可
productCommentService.createComment(ProductCommentConvert.INSTANCE.convert(createReqVO), Boolean.TRUE);
productCommentService.createComment(createReqVO);
return success(true);
}

View File

@ -10,17 +10,15 @@ import java.util.List;
@Data
public class ProductCommentBaseVO {
// TODO @puhui999 example 补充下
@Schema(description = "评价人名称", required = true, example = "张三")
@Schema(description = "评价人名称", required = true, example = "小姑凉")
@NotNull(message = "评价人名称不能为空")
private String userNickname;
@Schema(description = "评价人头像", required = true)
@Schema(description = "评价人头像", required = true, example = "https://www.iocoder.cn/xx.png")
@NotNull(message = "评价人头像不能为空")
private String userAvatar;
@Schema(description = "商品 SPU 编号", required = true, example = "29502")
@Schema(description = "商品 SPU 编号", required = true, example = "清凉丝滑透气小短袖")
@NotNull(message = "商品 SPU 编号不能为空")
private Long spuId;
@ -28,27 +26,27 @@ public class ProductCommentBaseVO {
@NotNull(message = "商品 SPU 名称不能为空")
private String spuName;
@Schema(description = "商品 SKU 编号", required = true, example = "3082")
@Schema(description = "商品 SKU 编号", required = true, example = "1")
@NotNull(message = "商品 SKU 编号不能为空")
private Long skuId;
@Schema(description = "评分星级 1-5分", required = true)
@Schema(description = "评分星级 1-5分", required = true, example = "5")
@NotNull(message = "评分星级不能为空")
private Integer scores;
@Schema(description = "描述星级 1-5分", required = true)
@Schema(description = "描述星级 1-5分", required = true, example = "5")
@NotNull(message = "描述星级不能为空")
private Integer descriptionScores;
@Schema(description = "服务星级 1-5分", required = true)
@Schema(description = "服务星级 1-5分", required = true, example = "5")
@NotNull(message = "服务星级分不能为空")
private Integer benefitScores;
@Schema(description = "评论内容", required = true)
@Schema(description = "评论内容", required = true, example = "穿起来非常丝滑凉快")
@NotNull(message = "评论内容不能为空")
private String content;
@Schema(description = "评论图片地址数组以逗号分隔最多上传9张", required = true)
@Schema(description = "评论图片地址数组以逗号分隔最多上传9张", required = true, example = "[https://www.iocoder.cn/xx.png, https://www.iocoder.cn/xxx.png]")
@Size(max = 9, message = "评论图片地址数组长度不能超过9张")
private List<String> picUrls;

View File

@ -1,7 +1,11 @@
package cn.iocoder.yudao.module.product.controller.admin.comment.vo;
import lombok.*;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import javax.validation.constraints.NotNull;
@Schema(description = "管理后台 - 商品评价创建 Request VO")
@Data
@ -9,4 +13,12 @@ import io.swagger.v3.oas.annotations.media.Schema;
@ToString(callSuper = true)
public class ProductCommentCreateReqVO extends ProductCommentBaseVO {
@Schema(description = "评价人", required = true, example = "16868")
@NotNull(message = "评价人不能为空")
private Long userId;
@Schema(description = "评价订单项", required = true, example = "19292")
@NotNull(message = "评价订单项不能为空")
private Long orderItemId;
}

View File

@ -31,11 +31,11 @@ public class ProductCommentPageReqVO extends PageParam {
@Schema(description = "商品SPU名称", example = "感冒药")
private String spuName;
@Schema(description = "评分星级 1-5分")
@Schema(description = "评分星级 1-5分", example = "5")
@InEnum(ProductCommentScoresEnum.class)
private Integer scores;
@Schema(description = "商家是否回复")
@Schema(description = "商家是否回复", example = "true")
private Boolean replied;
@Schema(description = "创建时间")

View File

@ -16,7 +16,7 @@ public class ProductCommentRespVO extends ProductCommentBaseVO {
@Schema(description = "订单项编号", required = true, example = "24965")
private Long id;
@Schema(description = "是否匿名:[false:不匿名 true:匿名]", required = true)
@Schema(description = "是否匿名:[false:不匿名 true:匿名]", required = true, example = "false")
private Boolean anonymous;
@Schema(description = "交易订单编号", required = true, example = "24428")

View File

@ -1,60 +0,0 @@
package cn.iocoder.yudao.module.product.controller.app.comment;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
import cn.iocoder.yudao.module.product.controller.app.comment.vo.AppCommentCreateReqVO;
import cn.iocoder.yudao.module.product.controller.app.comment.vo.AppCommentPageReqVO;
import cn.iocoder.yudao.module.product.controller.app.comment.vo.AppCommentRespVO;
import cn.iocoder.yudao.module.product.convert.comment.ProductCommentConvert;
import cn.iocoder.yudao.module.product.service.comment.ProductCommentService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.Map;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
// TODO @puhui999AppCommentController = AppProductCommentController
@Tag(name = "用户 APP - 商品评价")
@RestController
@RequestMapping("/product/comment")
@Validated
public class AppCommentController {
@Resource
private ProductCommentService productCommentService;
@Resource
private MemberUserApi memberUserApi;
@GetMapping("/page")
@Operation(summary = "获得商品评价分页")
public CommonResult<PageResult<AppCommentRespVO>> getCommentPage(@Valid AppCommentPageReqVO pageVO) {
return success(productCommentService.getCommentPage(pageVO, Boolean.TRUE));
}
// TODO @puhui999方法名改成 getCommentStatistics然后搞个对应的 vo想了下这样更优雅
@GetMapping("/get-count")
@Operation(summary = "获得商品的评价统计") // TODO @puhui999@RequestParam 针对 spuId
public CommonResult<Map<String, Long>> getCommentPage(@Valid Long spuId) {
return success(productCommentService.getCommentPageTabsCount(spuId, Boolean.TRUE));
}
@PostMapping(value = "/create")
@Operation(summary = "创建商品评价")
public CommonResult<Boolean> createComment(@RequestBody AppCommentCreateReqVO createReqVO) {
// TODO: 2023/3/20 要不要判断订单商品是否存在
// TODO @ouhui999这个接口搞到交易那比较合适
MemberUserRespDTO user = memberUserApi.getUser(getLoginUserId());
productCommentService.createComment(ProductCommentConvert.INSTANCE.convert(user, createReqVO), Boolean.FALSE);
return success(true);
}
}

View File

@ -0,0 +1,52 @@
package cn.iocoder.yudao.module.product.controller.app.comment;
import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.product.controller.app.comment.vo.AppCommentPageReqVO;
import cn.iocoder.yudao.module.product.controller.app.comment.vo.AppCommentStatisticsRespVO;
import cn.iocoder.yudao.module.product.controller.app.comment.vo.AppProductCommentRespVO;
import cn.iocoder.yudao.module.product.dal.dataobject.comment.ProductCommentDO;
import cn.iocoder.yudao.module.product.service.comment.ProductCommentService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.validation.Valid;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@Tag(name = "用户 APP - 商品评价")
@RestController
@RequestMapping("/product/comment")
@Validated
public class AppProductCommentController {
@Resource
private ProductCommentService productCommentService;
@GetMapping("/page")
@Operation(summary = "获得商品评价分页")
public CommonResult<PageResult<AppProductCommentRespVO>> getCommentPage(@Valid AppCommentPageReqVO pageVO) {
PageResult<AppProductCommentRespVO> result = productCommentService.getCommentPage(pageVO, Boolean.TRUE);
result.getList().forEach(item -> {
// 判断用户是否选择匿名
if (ObjectUtil.equal(item.getAnonymous(), true)) {
item.setUserNickname(ProductCommentDO.NICKNAME_ANONYMOUS);
}
});
return success(result);
}
@GetMapping("/getCommentStatistics")
@Operation(summary = "获得商品的评价统计")
public CommonResult<AppCommentStatisticsRespVO> getCommentPage(@Valid @RequestParam("spuId") Long spuId) {
return success(productCommentService.getCommentPageTabsCount(spuId, Boolean.TRUE));
}
}

View File

@ -14,55 +14,21 @@ import javax.validation.constraints.NotNull;
@ToString(callSuper = true)
public class AppCommentPageReqVO extends PageParam {
// TODO @puhui999不传递就是 all
/**
* 所有
*/
public static final Integer ALL = 0;
/**
* 所有数量 key
*/
public static final String ALL_COUNT = "allCount";
// TODO @puhui999good 好评
/**
* 好评
*/
public static final Integer FAVOURABLE_COMMENT = 1;
// TODO @puhui999medium 中评然后 mediumCount 就好啦
/**
* 好评数量 key
*/
public static final String FAVOURABLE_COMMENT_COUNT = "favourableCommentCount";
public static final Integer GOOD_COMMENT = 1;
/**
* 中评
*/
public static final Integer MEDIOCRE_COMMENT = 2;
/**
* 中评数量 key
*/
public static final String MEDIOCRE_COMMENT_COUNT = "mediocreCommentCount";
/**
* 差评
*/
public static final Integer NEGATIVE_COMMENT = 3;
/**
* 差评数量 key
*/
public static final String NEGATIVE_COMMENT_COUNT = "negativeCommentCount";
// TODO @puhui999这个挪到 DO 那没问题的哈NICKNAME_ANONYMOUS
/**
* 默认匿名昵称
*/
public static final String ANONYMOUS_NICKNAME = "匿名用户";
@Schema(description = "商品SPU编号", example = "29502")
@NotNull(message = "商品SPU编号不能为空")
private Long spuId;

View File

@ -0,0 +1,29 @@
package cn.iocoder.yudao.module.product.controller.app.comment.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.ToString;
/**
* APP 商品评价页评论分类数统计 Response VO
*
* @author HUIHUI
*/
@Schema(description = "APP - 商品评价页评论分类数统计 Response VO")
@Data
@ToString(callSuper = true)
public class AppCommentStatisticsRespVO {
@Schema(description = "所有评论数量", required = true, example = "15721")
private Long allCount;
@Schema(description = "好评数量", required = true, example = "15721")
private Long goodCount;
@Schema(description = "中评数量", required = true, example = "15721")
private Long mediocreCount;
@Schema(description = "差评数量", required = true, example = "15721")
private Long negativeCount;
}

View File

@ -7,39 +7,43 @@ import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.util.List;
// TODO @puhui999 Product 前缀给补下哈
/**
* 商品评论 Base VO
*
* @author HUIHUI
*/
@Data
public class AppCommentBaseVO {
public class AppProductCommentBaseVO {
@Schema(description = "商品SPU编号", required = true, example = "29502")
@Schema(description = "商品SPU编号", required = true, example = "91192")
@NotNull(message = "商品SPU编号不能为空")
private Long spuId;
@Schema(description = "商品SPU名称", required = true, example = "赵六")
@Schema(description = "商品SPU名称", required = true, example = "清凉丝滑小短袖")
@NotNull(message = "商品SPU名称不能为空")
private String spuName;
@Schema(description = "商品SKU编号", required = true, example = "3082")
@Schema(description = "商品SKU编号", required = true, example = "81192")
@NotNull(message = "商品SKU编号不能为空")
private Long skuId;
@Schema(description = "评分星级 1-5分", required = true)
@Schema(description = "评分星级 1-5分", required = true, example = "5")
@NotNull(message = "评分星级 1-5分不能为空")
private Integer scores;
@Schema(description = "描述星级 1-5分", required = true)
@Schema(description = "描述星级 1-5分", required = true, example = "5")
@NotNull(message = "描述星级 1-5分不能为空")
private Integer descriptionScores;
@Schema(description = "服务星级 1-5分", required = true)
@Schema(description = "服务星级 1-5分", required = true, example = "5")
@NotNull(message = "服务星级 1-5分不能为空")
private Integer benefitScores;
@Schema(description = "评论内容", required = true)
@Schema(description = "评论内容", required = true, example = "哇,真的很丝滑凉快诶,好评")
@NotNull(message = "评论内容不能为空")
private String content;
@Schema(description = "评论图片地址数组以逗号分隔最多上传9张", required = true)
@Schema(description = "评论图片地址数组以逗号分隔最多上传9张", required = true, example = "[https://www.iocoder.cn/xx.png, https://www.iocoder.cn/xxx.png]")
@Size(max = 9, message = "评论图片地址数组长度不能超过9张")
private List<String> picUrls;

View File

@ -7,23 +7,17 @@ import lombok.ToString;
import javax.validation.constraints.NotNull;
// TODO @puhui999不应该继承 AppCommentCreateReqVO
// TODO @puhui999不应该继承 AppProductCommentCreateReqVO
@Schema(description = "用户APP - 商品评价创建 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class AppCommentCreateReqVO extends AppCommentBaseVO {
public class AppProductCommentCreateReqVO extends AppProductCommentBaseVO {
@Schema(description = "是否匿名", required = true, example = "true")
@NotNull(message = "是否匿名不能为空")
private Boolean anonymous;
// TODO @puhui999:不应该传递 orderId
@Schema(description = "交易订单编号", required = true, example = "12312")
@NotNull(message = "交易订单编号不能为空")
private Long orderId;
@Schema(description = "交易订单项编号", required = true, example = "2312312")
@NotNull(message = "交易订单项编号不能为空")
private Long orderItemId;

View File

@ -4,17 +4,18 @@ import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import java.util.List;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "用户APP - 商品评价 Response VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class AppCommentRespVO extends AppCommentBaseVO {
// TODO puhui999 example 也补充下哈
public class AppProductCommentRespVO extends AppProductCommentBaseVO {
@Schema(description = "评价人的用户编号", required = true, example = "15721")
private Long userId;
@ -22,13 +23,13 @@ public class AppCommentRespVO extends AppCommentBaseVO {
@Schema(description = "评价人名称", required = true, example = "张三")
private String userNickname;
@Schema(description = "评价人头像", required = true)
@Schema(description = "评价人头像", required = true, example = "https://www.iocoder.cn/xx.png")
private String userAvatar;
@Schema(description = "订单项编号", required = true, example = "24965")
private Long id;
@Schema(description = "是否匿名", required = true)
@Schema(description = "是否匿名", required = true, example = "false")
private Boolean anonymous;
@Schema(description = "交易订单编号", required = true, example = "24428")
@ -37,31 +38,31 @@ public class AppCommentRespVO extends AppCommentBaseVO {
@Schema(description = "交易订单项编号", required = true, example = "8233")
private Long orderItemId;
@Schema(description = "商家是否回复", required = true)
@Schema(description = "商家是否回复", required = true, example = "true")
private Boolean replyStatus;
@Schema(description = "回复管理员编号", example = "22212")
private Long replyUserId;
@Schema(description = "商家回复内容")
@Schema(description = "商家回复内容", example = "亲,你的好评就是我的动力(*^▽^*)")
private String replyContent;
@Schema(description = "商家回复时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime replyTime;
@Schema(description = "追加评价内容")
@Schema(description = "追加评价内容", example = "穿了很久都很丝滑诶")
private String additionalContent;
@Schema(description = "追评评价图片地址数组以逗号分隔最多上传9张")
@Schema(description = "追评评价图片地址数组以逗号分隔最多上传9张", example = "[https://www.iocoder.cn/xx.png, https://www.iocoder.cn/xxx.png]")
private List<String> additionalPicUrls;
@Schema(description = "追加评价时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime additionalTime;
@Schema(description = "创建时间", required = true)
@Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime createTime;
@Schema(description = "最终评分", required = true)
private Integer finalScore;
}

View File

@ -9,7 +9,6 @@ import cn.iocoder.yudao.module.product.convert.spu.ProductSpuConvert;
import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO;
import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO;
import cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum;
import cn.iocoder.yudao.module.product.service.property.ProductPropertyValueService;
import cn.iocoder.yudao.module.product.service.sku.ProductSkuService;
import cn.iocoder.yudao.module.product.service.spu.ProductSpuService;
import io.swagger.v3.oas.annotations.Operation;
@ -40,8 +39,6 @@ public class AppProductSpuController {
private ProductSpuService productSpuService;
@Resource
private ProductSkuService productSkuService;
@Resource
private ProductPropertyValueService productPropertyValueService;
@GetMapping("/page")
@Operation(summary = "获得商品 SPU 分页")

View File

@ -1,15 +1,19 @@
package cn.iocoder.yudao.module.product.convert.comment;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
import cn.iocoder.yudao.module.product.api.comment.dto.CommentCreateReqDTO;
import cn.iocoder.yudao.module.product.controller.admin.comment.vo.ProductCommentCreateReqVO;
import cn.iocoder.yudao.module.product.controller.admin.comment.vo.ProductCommentRespVO;
import cn.iocoder.yudao.module.product.controller.app.comment.vo.AppCommentCreateReqVO;
import cn.iocoder.yudao.module.product.controller.app.comment.vo.AppCommentRespVO;
import cn.iocoder.yudao.module.product.controller.app.comment.vo.AppCommentStatisticsRespVO;
import cn.iocoder.yudao.module.product.controller.app.comment.vo.AppProductCommentRespVO;
import cn.iocoder.yudao.module.product.dal.dataobject.comment.ProductCommentDO;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Named;
import org.mapstruct.factory.Mappers;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List;
/**
@ -24,50 +28,42 @@ public interface ProductCommentConvert {
ProductCommentRespVO convert(ProductCommentDO bean);
@Mapping(target = "allCount", source = "allCount")
@Mapping(target = "goodCount", source = "goodCount")
@Mapping(target = "mediocreCount", source = "mediocreCount")
@Mapping(target = "negativeCount", source = "negativeCount")
AppCommentStatisticsRespVO convert(Long allCount, Long goodCount, Long mediocreCount, Long negativeCount);
List<ProductCommentRespVO> convertList(List<ProductCommentDO> list);
PageResult<ProductCommentRespVO> convertPage(PageResult<ProductCommentDO> page);
PageResult<AppCommentRespVO> convertPage02(PageResult<ProductCommentDO> pageResult);
PageResult<AppProductCommentRespVO> convertPage02(PageResult<ProductCommentDO> pageResult);
// TODO @puhui999 mapstruct 的映射
default ProductCommentDO convert(MemberUserRespDTO user, AppCommentCreateReqVO createReqVO) {
ProductCommentDO productComment = new ProductCommentDO();
productComment.setUserId(user.getId());
productComment.setUserNickname(user.getNickname());
productComment.setUserAvatar(user.getAvatar());
productComment.setAnonymous(createReqVO.getAnonymous());
productComment.setOrderId(createReqVO.getOrderId());
productComment.setOrderItemId(createReqVO.getOrderItemId());
productComment.setSpuId(createReqVO.getSpuId());
productComment.setSpuName(createReqVO.getSpuName());
productComment.setSkuId(createReqVO.getSkuId());
productComment.setScores(createReqVO.getScores());
productComment.setDescriptionScores(createReqVO.getDescriptionScores());
productComment.setBenefitScores(createReqVO.getBenefitScores());
productComment.setContent(createReqVO.getContent());
productComment.setPicUrls(createReqVO.getPicUrls());
return productComment;
/**
* 计算综合评分
*
* @param descriptionScores 描述星级
* @param benefitScores 服务星级
* @return {@link Integer}
*/
@Named("convertScores")
default Integer convertScores(Integer descriptionScores, Integer benefitScores) {
// 计算评价最终综合评分 最终星数 = 商品评星 + 服务评星 / 2
BigDecimal sumScore = new BigDecimal(descriptionScores + benefitScores);
BigDecimal divide = sumScore.divide(BigDecimal.valueOf(2L), 0, RoundingMode.DOWN);
return divide.intValue();
}
// TODO @puhui999 mapstruct 的映射
default ProductCommentDO convert(ProductCommentCreateReqVO createReq) {
ProductCommentDO productComment = new ProductCommentDO();
productComment.setUserId(0L);
productComment.setUserNickname(createReq.getUserNickname());
productComment.setUserAvatar(createReq.getUserAvatar());
productComment.setAnonymous(Boolean.FALSE);
productComment.setOrderId(0L);
productComment.setOrderItemId(0L);
productComment.setSpuId(createReq.getSpuId());
productComment.setSpuName(createReq.getSpuName());
productComment.setSkuId(createReq.getSkuId());
productComment.setScores(createReq.getScores());
productComment.setDescriptionScores(createReq.getDescriptionScores());
productComment.setBenefitScores(createReq.getBenefitScores());
productComment.setContent(createReq.getContent());
productComment.setPicUrls(createReq.getPicUrls());
return productComment;
}
@Mapping(target = "orderId", source = "orderId")
@Mapping(target = "scores", expression = "java(convertScores(createReqDTO.getDescriptionScores(), createReqDTO.getBenefitScores()))")
ProductCommentDO convert(CommentCreateReqDTO createReqDTO, Long orderId);
@Mapping(target = "userId", constant = "0L")
@Mapping(target = "orderId", constant = "0L")
@Mapping(target = "orderItemId", constant = "0L")
@Mapping(target = "anonymous", expression = "java(Boolean.FALSE)")
@Mapping(target = "scores", expression = "java(convertScores(createReq.getDescriptionScores(), createReq.getBenefitScores()))")
ProductCommentDO convert(ProductCommentCreateReqVO createReq);
}

View File

@ -28,6 +28,11 @@ import java.util.List;
@AllArgsConstructor
public class ProductCommentDO extends BaseDO {
/**
* 默认匿名昵称
*/
public static final String NICKNAME_ANONYMOUS = "匿名用户";
/**
* 评论编号主键自增
*/

View File

@ -31,7 +31,7 @@ public interface ProductCommentMapper extends BaseMapperX<ProductCommentDO> {
// TODO 芋艿在看看这块
static void appendTabQuery(LambdaQueryWrapperX<ProductCommentDO> queryWrapper, Integer type) {
// 构建好评查询语句好评计算 (商品评分星级+服务评分星级) >= 8
if (ObjectUtil.equal(type, AppCommentPageReqVO.FAVOURABLE_COMMENT)) {
if (ObjectUtil.equal(type, AppCommentPageReqVO.GOOD_COMMENT)) {
queryWrapper.apply("(scores + benefit_scores) >= 8");
}
// 构建中评查询语句中评计算 (商品评分星级+服务评分星级) > 4 (商品评分星级+服务评分星级) < 8
@ -72,16 +72,14 @@ public interface ProductCommentMapper extends BaseMapperX<ProductCommentDO> {
update(null, lambdaUpdateWrapper);
}
// TODO @puhui999使用 select 替代 find
default ProductCommentDO findByUserIdAndOrderIdAndSpuId(Long userId, Long orderId, Long spuId) {
default ProductCommentDO selectByUserIdAndOrderIdAndSpuId(Long userId, Long orderId, Long spuId) {
return selectOne(new LambdaQueryWrapperX<ProductCommentDO>()
.eq(ProductCommentDO::getUserId, userId)
.eq(ProductCommentDO::getOrderId, orderId)
.eq(ProductCommentDO::getSpuId, spuId));
}
// TODO @puhui999selectCountBySpuId 即可
default Long selectTabCount(Long spuId, Boolean visible, Integer type) {
default Long selectCountBySpuId(Long spuId, Boolean visible, Integer type) {
LambdaQueryWrapperX<ProductCommentDO> queryWrapper = new LambdaQueryWrapperX<ProductCommentDO>()
.eqIfPresent(ProductCommentDO::getSpuId, spuId)
.eqIfPresent(ProductCommentDO::getVisible, visible);

View File

@ -1,17 +1,17 @@
package cn.iocoder.yudao.module.product.service.comment;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.product.controller.admin.comment.vo.ProductCommentCreateReqVO;
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.controller.app.comment.vo.AppCommentPageReqVO;
import cn.iocoder.yudao.module.product.controller.app.comment.vo.AppCommentRespVO;
import cn.iocoder.yudao.module.product.controller.app.comment.vo.AppCommentStatisticsRespVO;
import cn.iocoder.yudao.module.product.controller.app.comment.vo.AppProductCommentRespVO;
import cn.iocoder.yudao.module.product.dal.dataobject.comment.ProductCommentDO;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import java.util.Map;
/**
* 商品评论 Service 接口
*
@ -37,13 +37,14 @@ public interface ProductCommentService {
void updateCommentVisible(ProductCommentUpdateVisibleReqVO updateReqVO);
// TODO @puhui999replyComment
/**
* 商家回复
*
* @param replyVO 商家回复
* @param loginUserId 管理后台商家登陆人 ID
*/
void commentReply(ProductCommentReplyVO replyVO, Long loginUserId);
void replyComment(ProductCommentReplyVO replyVO, Long loginUserId);
/**
* 获得商品评价分页
@ -52,15 +53,23 @@ public interface ProductCommentService {
* @param visible 是否可见
* @return 商品评价分页
*/
PageResult<AppCommentRespVO> getCommentPage(AppCommentPageReqVO pageVO, Boolean visible);
PageResult<AppProductCommentRespVO> getCommentPage(AppCommentPageReqVO pageVO, Boolean visible);
/**
* 创建商品评论
* 创建商品评论 后台管理员创建评论使用
*
* @param productComment 创建实体
* @param system 是否系统评价
* @param createReqVO 商品评价创建 Request VO 对象
*/
void createComment(ProductCommentDO productComment, Boolean system);
void createComment(ProductCommentCreateReqVO createReqVO);
/**
* 创建评论
* 创建商品评论 APP 端创建商品评论使用
*
* @param commentDO 评论对象
* @return 返回评论 id
*/
Long createComment(ProductCommentDO commentDO);
/**
* 获得商品的评价统计
@ -69,6 +78,6 @@ public interface ProductCommentService {
* @param visible 是否可见
* @return 评价统计
*/
Map<String, Long> getCommentPageTabsCount(Long spuId, Boolean visible);
AppCommentStatisticsRespVO getCommentPageTabsCount(Long spuId, Boolean visible);
}

View File

@ -1,31 +1,28 @@
package cn.iocoder.yudao.module.product.service.comment;
import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.product.controller.admin.comment.vo.ProductCommentCreateReqVO;
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.controller.app.comment.vo.AppCommentPageReqVO;
import cn.iocoder.yudao.module.product.controller.app.comment.vo.AppCommentRespVO;
import cn.iocoder.yudao.module.product.controller.app.comment.vo.AppCommentStatisticsRespVO;
import cn.iocoder.yudao.module.product.controller.app.comment.vo.AppProductCommentRespVO;
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.spu.ProductSpuDO;
import cn.iocoder.yudao.module.product.dal.mysql.comment.ProductCommentMapper;
import cn.iocoder.yudao.module.product.service.spu.ProductSpuService;
import cn.iocoder.yudao.module.trade.api.order.TradeOrderApi;
import cn.iocoder.yudao.module.trade.api.order.dto.TradeOrderRespDTO;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.HashMap;
import java.util.Map;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.*;
import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.ORDER_NOT_FOUND;
/**
* 商品评论 Service 实现类
@ -46,94 +43,96 @@ public class ProductCommentServiceImpl implements ProductCommentService {
private ProductSpuService productSpuService;
@Override
public PageResult<ProductCommentDO> getCommentPage(ProductCommentPageReqVO pageReqVO) {
return productCommentMapper.selectPage(pageReqVO);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void updateCommentVisible(ProductCommentUpdateVisibleReqVO updateReqVO) {
// 校验评论是否存在
validateCommentExists(updateReqVO.getId());
ProductCommentDO productCommentDO = validateCommentExists(updateReqVO.getId());
productCommentDO.setVisible(updateReqVO.getVisible());
// 更新可见状态
// TODO @puhui999直接使用 update 操作
productCommentMapper.updateCommentVisible(updateReqVO.getId(), updateReqVO.getVisible());
productCommentMapper.updateById(productCommentDO);
}
@Override
public void commentReply(ProductCommentReplyVO replyVO, Long loginUserId) {
@Transactional(rollbackFor = Exception.class)
public void replyComment(ProductCommentReplyVO replyVO, Long loginUserId) {
// 校验评论是否存在
validateCommentExists(replyVO.getId());
ProductCommentDO productCommentDO = validateCommentExists(replyVO.getId());
productCommentDO.setReplyTime(LocalDateTime.now());
productCommentDO.setReplyUserId(loginUserId);
productCommentDO.setReplyStatus(Boolean.TRUE);
productCommentDO.setReplyContent(replyVO.getReplyContent());
// 回复评论
// TODO @puhui999直接使用 update 操作
productCommentMapper.commentReply(replyVO, loginUserId);
productCommentMapper.updateById(productCommentDO);
}
@Override
public Map<String, Long> getCommentPageTabsCount(Long spuId, Boolean visible) {
Map<String, Long> countMap = new HashMap<>(4);
// 查询商品 id = spuId 的所有评论数量
countMap.put(AppCommentPageReqVO.ALL_COUNT,
productCommentMapper.selectTabCount(spuId, visible, AppCommentPageReqVO.ALL));
// 查询商品 id = spuId 的所有好评数量
countMap.put(AppCommentPageReqVO.FAVOURABLE_COMMENT_COUNT,
productCommentMapper.selectTabCount(spuId, visible, AppCommentPageReqVO.FAVOURABLE_COMMENT));
// 查询商品 id = spuId 的所有中评数量
countMap.put(AppCommentPageReqVO.MEDIOCRE_COMMENT_COUNT,
productCommentMapper.selectTabCount(spuId, visible, AppCommentPageReqVO.MEDIOCRE_COMMENT));
// 查询商品 id = spuId 的所有差评数量
countMap.put(AppCommentPageReqVO.NEGATIVE_COMMENT_COUNT,
productCommentMapper.selectTabCount(spuId, visible, AppCommentPageReqVO.NEGATIVE_COMMENT));
return countMap;
@Transactional(rollbackFor = Exception.class)
public void createComment(ProductCommentCreateReqVO createReqVO) {
// 校验订单
Long orderId = tradeOrderApi.validateOrder(createReqVO.getUserId(), createReqVO.getOrderItemId());
// 校验评论
validateComment(createReqVO.getSpuId(), createReqVO.getUserId(), orderId);
ProductCommentDO commentDO = ProductCommentConvert.INSTANCE.convert(createReqVO);
productCommentMapper.insert(commentDO);
}
@Override
public PageResult<AppCommentRespVO> getCommentPage(AppCommentPageReqVO pageVO, Boolean visible) {
// TODO @puhui999逻辑可以在 controller 做哈 service 简介一点因为是 view 需要不展示昵称
PageResult<AppCommentRespVO> result = ProductCommentConvert.INSTANCE.convertPage02(productCommentMapper.selectPage(pageVO, visible));
result.getList().forEach(item -> {
// 判断用户是否选择匿名
if (ObjectUtil.equal(item.getAnonymous(), true)) {
item.setUserNickname(AppCommentPageReqVO.ANONYMOUS_NICKNAME);
}
// TODO @puhui999直接插入的时候计算到 scores 字段里这样就去掉 finalScore 字段哈
// 计算评价最终综合评分 最终星数 = 商品评星 + 服务评星 / 2
BigDecimal sumScore = new BigDecimal(item.getScores() + item.getBenefitScores());
BigDecimal divide = sumScore.divide(BigDecimal.valueOf(2L), 0, RoundingMode.DOWN);
item.setFinalScore(divide.intValue());
});
return result;
@Transactional(rollbackFor = Exception.class)
public Long createComment(ProductCommentDO commentDO) {
// 校验评论
validateComment(commentDO.getSpuId(), commentDO.getUserId(), commentDO.getOrderId());
productCommentMapper.insert(commentDO);
return commentDO.getId();
}
@Override
public void createComment(ProductCommentDO productComment, Boolean system) {
// TODO @puhui999这里不区分是否为 system直接都校验
if (!system) {
// TODO 判断订单是否存在 fix
// TODO @puhui999改成 order 那有个 comment 接口哪里校验下商品评论这里不校验订单是否存在哈
TradeOrderRespDTO order = tradeOrderApi.getOrder(productComment.getOrderId());
if (null == order) {
throw exception(ORDER_NOT_FOUND);
}
ProductSpuDO spu = productSpuService.getSpu(productComment.getSpuId());
if (null == spu) {
throw exception(SPU_NOT_EXISTS);
}
// 判断当前订单的当前商品用户是否评价过
ProductCommentDO exist = productCommentMapper.findByUserIdAndOrderIdAndSpuId(productComment.getId(), productComment.getOrderId(), productComment.getSpuId());
if (null != exist) {
throw exception(ORDER_SPU_COMMENT_EXISTS);
}
// TODO 只有创建和更新诶 要不要删除接口
private void validateComment(Long spuId, Long userId, Long orderId) {
ProductSpuDO spu = productSpuService.getSpu(spuId);
if (null == spu) {
throw exception(SPU_NOT_EXISTS);
}
// 判断当前订单的当前商品用户是否评价过
ProductCommentDO exist = productCommentMapper.selectByUserIdAndOrderIdAndSpuId(userId, orderId, spuId);
if (null != exist) {
throw exception(ORDER_SPU_COMMENT_EXISTS);
}
productCommentMapper.insert(productComment);
}
private void validateCommentExists(Long id) {
private ProductCommentDO validateCommentExists(Long id) {
ProductCommentDO productComment = productCommentMapper.selectById(id);
if (productComment == null) {
throw exception(COMMENT_NOT_EXISTS);
}
return productComment;
}
@Override
public AppCommentStatisticsRespVO getCommentPageTabsCount(Long spuId, Boolean visible) {
return ProductCommentConvert.INSTANCE.convert(
// 查询商品 id = spuId 的所有评论数量
productCommentMapper.selectCountBySpuId(spuId, visible, null),
// 查询商品 id = spuId 的所有好评数量
productCommentMapper.selectCountBySpuId(spuId, visible, AppCommentPageReqVO.GOOD_COMMENT),
// 查询商品 id = spuId 的所有中评数量
productCommentMapper.selectCountBySpuId(spuId, visible, AppCommentPageReqVO.MEDIOCRE_COMMENT),
// 查询商品 id = spuId 的所有差评数量
productCommentMapper.selectCountBySpuId(spuId, visible, AppCommentPageReqVO.NEGATIVE_COMMENT)
);
}
@Override
public PageResult<AppProductCommentRespVO> getCommentPage(AppCommentPageReqVO pageVO, Boolean visible) {
return ProductCommentConvert.INSTANCE.convertPage02(productCommentMapper.selectPage(pageVO, visible));
}
@Override
public PageResult<ProductCommentDO> getCommentPage(ProductCommentPageReqVO pageReqVO) {
return productCommentMapper.selectPage(pageReqVO);
}
}

View File

@ -4,14 +4,13 @@ import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.RandomUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
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.ProductCommentUpdateVisibleReqVO;
import cn.iocoder.yudao.module.product.controller.app.comment.vo.AppCommentAdditionalReqVO;
import cn.iocoder.yudao.module.product.controller.app.comment.vo.AppCommentPageReqVO;
import cn.iocoder.yudao.module.product.controller.app.comment.vo.AppCommentRespVO;
import cn.iocoder.yudao.module.product.controller.app.comment.vo.AppCommentStatisticsRespVO;
import cn.iocoder.yudao.module.product.controller.app.comment.vo.AppProductCommentRespVO;
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.mysql.comment.ProductCommentMapper;
@ -26,7 +25,6 @@ import org.springframework.context.annotation.Lazy;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.Map;
import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
@ -93,12 +91,10 @@ public class ProductCommentServiceImplTest extends BaseDbUnitTest {
o.setSkuId(generateId());
o.setDescriptionScores(ProductCommentScoresEnum.FOUR.getScores());
o.setBenefitScores(ProductCommentScoresEnum.FOUR.getScores());
o.setDeliveryScores(ProductCommentScoresEnum.FOUR.getScores());
o.setContent("真好吃");
o.setReplyUserId(generateId());
o.setReplyContent("确实");
o.setReplyTime(LocalDateTime.now());
o.setAdditionalTime(LocalDateTime.now());
o.setCreateTime(LocalDateTime.now());
o.setUpdateTime(LocalDateTime.now());
});
@ -139,23 +135,23 @@ public class ProductCommentServiceImplTest extends BaseDbUnitTest {
assertEquals(8, all.getTotal());
// 测试获取所有商品分页评论数据
PageResult<AppCommentRespVO> result1 = productCommentService.getCommentPage(new AppCommentPageReqVO(), Boolean.TRUE);
PageResult<AppProductCommentRespVO> result1 = productCommentService.getCommentPage(new AppCommentPageReqVO(), Boolean.TRUE);
assertEquals(7, result1.getTotal());
// 测试获取所有商品分页中评数据
PageResult<AppCommentRespVO> result2 = productCommentService.getCommentPage(new AppCommentPageReqVO().setType(AppCommentPageReqVO.MEDIOCRE_COMMENT), Boolean.TRUE);
PageResult<AppProductCommentRespVO> result2 = productCommentService.getCommentPage(new AppCommentPageReqVO().setType(AppCommentPageReqVO.MEDIOCRE_COMMENT), Boolean.TRUE);
assertEquals(2, result2.getTotal());
// 测试获取指定 spuId 商品分页中评数据
PageResult<AppCommentRespVO> result3 = productCommentService.getCommentPage(new AppCommentPageReqVO().setSpuId(spuId).setType(AppCommentPageReqVO.MEDIOCRE_COMMENT), Boolean.TRUE);
PageResult<AppProductCommentRespVO> result3 = productCommentService.getCommentPage(new AppCommentPageReqVO().setSpuId(spuId).setType(AppCommentPageReqVO.MEDIOCRE_COMMENT), Boolean.TRUE);
assertEquals(2, result3.getTotal());
// 测试分页 tab count
Map<String, Long> tabsCount = productCommentService.getCommentPageTabsCount(spuId, Boolean.TRUE);
assertEquals(6, tabsCount.get(AppCommentPageReqVO.ALL_COUNT));
assertEquals(4, tabsCount.get(AppCommentPageReqVO.FAVOURABLE_COMMENT_COUNT));
assertEquals(2, tabsCount.get(AppCommentPageReqVO.MEDIOCRE_COMMENT_COUNT));
assertEquals(0, tabsCount.get(AppCommentPageReqVO.NEGATIVE_COMMENT_COUNT));
AppCommentStatisticsRespVO tabsCount = productCommentService.getCommentPageTabsCount(spuId, Boolean.TRUE);
assertEquals(6, tabsCount.getAllCount());
assertEquals(4, tabsCount.getGoodCount());
assertEquals(2, tabsCount.getMediocreCount());
assertEquals(0, tabsCount.getNegativeCount());
}
@ -190,7 +186,7 @@ public class ProductCommentServiceImplTest extends BaseDbUnitTest {
ProductCommentReplyVO replyVO = new ProductCommentReplyVO();
replyVO.setId(productCommentId);
replyVO.setReplyContent("测试");
productCommentService.commentReply(replyVO, 1L);
productCommentService.replyComment(replyVO, 1L);
ProductCommentDO productCommentDO = productCommentMapper.selectById(productCommentId);
assertEquals("测试", productCommentDO.getReplyContent());
@ -199,24 +195,6 @@ public class ProductCommentServiceImplTest extends BaseDbUnitTest {
@Test
public void testCreateComment_success() {
// mock 测试
ProductCommentDO productComment = randomPojo(ProductCommentDO.class, o -> {
o.setAdditionalContent("");
});
productCommentService.createComment(productComment, Boolean.TRUE);
MemberUserRespDTO user = new MemberUserRespDTO();
user.setId(productComment.getUserId());
AppCommentAdditionalReqVO createReqVO = new AppCommentAdditionalReqVO();
createReqVO.setId(productComment.getId());
createReqVO.setAdditionalContent("追加");
createReqVO.setAdditionalPicUrls(productComment.getAdditionalPicUrls());
productCommentService.additionalComment(user, createReqVO);
ProductCommentDO exist = productCommentMapper.selectById(productComment.getId());
assertEquals("追加", exist.getAdditionalContent());
}
}

View File

@ -1,7 +1,5 @@
package cn.iocoder.yudao.module.trade.api.order;
import cn.iocoder.yudao.module.trade.api.order.dto.TradeOrderRespDTO;
/**
* 订单 API 接口
*
@ -10,11 +8,12 @@ import cn.iocoder.yudao.module.trade.api.order.dto.TradeOrderRespDTO;
public interface TradeOrderApi {
/**
* 获取订单通过订单 id
* 验证订单
*
* @param id id
* @return 订单信息 Response DTO
* @param userId 用户 id
* @param orderItemId 订单项 id
* @return 校验通过返回订单 id
*/
TradeOrderRespDTO getOrder(Long id);
Long validateOrder(Long userId, Long orderItemId);
}

View File

@ -1,13 +1,15 @@
package cn.iocoder.yudao.module.trade.api.order;
import cn.iocoder.yudao.module.trade.api.order.dto.TradeOrderRespDTO;
import cn.iocoder.yudao.module.trade.convert.order.TradeOrderConvert;
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
import cn.iocoder.yudao.module.trade.service.order.TradeOrderService;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.ORDER_ITEM_NOT_FOUND;
/**
* 订单 API 接口实现类
*
@ -21,8 +23,14 @@ public class TradeOrderApiImpl implements TradeOrderApi {
private TradeOrderService tradeOrderService;
@Override
public TradeOrderRespDTO getOrder(Long id) {
return TradeOrderConvert.INSTANCE.convert(tradeOrderService.getOrder(id));
public Long validateOrder(Long userId, Long orderItemId) {
// 校验订单项订单项存在订单就存在
TradeOrderItemDO item = tradeOrderService.getOrderItem(userId, orderItemId);
if (item == null) {
throw exception(ORDER_ITEM_NOT_FOUND);
}
return item.getOrderId();
}
}

View File

@ -4,9 +4,11 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated;
import cn.iocoder.yudao.module.pay.api.notify.dto.PayOrderNotifyReqDTO;
import cn.iocoder.yudao.module.product.api.comment.ProductCommentApi;
import cn.iocoder.yudao.module.product.api.property.ProductPropertyValueApi;
import cn.iocoder.yudao.module.product.api.property.dto.ProductPropertyValueDetailRespDTO;
import cn.iocoder.yudao.module.trade.controller.app.order.vo.*;
import cn.iocoder.yudao.module.trade.controller.app.order.vo.item.AppTradeOrderItemCommentCreateReqVO;
import cn.iocoder.yudao.module.trade.controller.app.order.vo.item.AppTradeOrderItemRespVO;
import cn.iocoder.yudao.module.trade.convert.order.TradeOrderConvert;
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
@ -14,6 +16,7 @@ import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum;
import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties;
import cn.iocoder.yudao.module.trade.service.order.TradeOrderService;
import com.google.common.collect.Maps;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
@ -23,14 +26,15 @@ import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.ORDER_ITEM_NOT_FOUND;
@Tag(name = "用户 App - 交易订单")
@RestController
@ -48,6 +52,9 @@ public class AppTradeOrderController {
@Resource
private TradeOrderProperties tradeOrderProperties;
@Resource
private ProductCommentApi productCommentApi;
@GetMapping("/settlement")
@Operation(summary = "获得订单结算信息")
@PreAuthenticated
@ -105,7 +112,7 @@ public class AppTradeOrderController {
@GetMapping("/get-count")
@Operation(summary = "获得交易订单数量")
public CommonResult<Map<String, Long>> getOrderCount() {
Map<String, Long> orderCount = new HashMap<>();
Map<String, Long> orderCount = Maps.newLinkedHashMapWithExpectedSize(5);
// 全部
orderCount.put("allCount", tradeOrderService.getOrderCount(getLoginUserId(), null, null));
// 待付款未支付
@ -129,11 +136,16 @@ public class AppTradeOrderController {
return success(TradeOrderConvert.INSTANCE.convert03(item));
}
// TODO 芋艿待实现
@PostMapping("/item/create-comment")
@Operation(summary = "创建交易订单项的评价")
public CommonResult<Long> createOrderItemComment() {
return success(0L);
public CommonResult<Long> createOrderItemComment(@RequestBody AppTradeOrderItemCommentCreateReqVO createReqVO) {
// 校验订单项订单项存在订单就存在
TradeOrderItemDO item = tradeOrderService.getOrderItem(createReqVO.getUserId(), createReqVO.getOrderItemId());
if (item == null) {
throw exception(ORDER_ITEM_NOT_FOUND);
}
return success(productCommentApi.createComment(TradeOrderConvert.INSTANCE.convert04(createReqVO), item.getOrderId()));
}
}

View File

@ -0,0 +1,71 @@
package cn.iocoder.yudao.module.trade.controller.app.order.vo.item;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.util.List;
/**
* 商品评价创建 Request VO
*
* @author HUIHUI
*/
@Schema(description = "用户APP - 商品评价创建 Request VO")
@Data
public class AppTradeOrderItemCommentCreateReqVO {
@Schema(description = "是否匿名", required = true, example = "true")
@NotNull(message = "是否匿名不能为空")
private Boolean anonymous;
@Schema(description = "交易订单项编号", required = true, example = "2312312")
@NotNull(message = "交易订单项编号不能为空")
private Long orderItemId;
@Schema(description = "商品SPU编号", required = true, example = "29502")
@NotNull(message = "商品SPU编号不能为空")
private Long spuId;
@Schema(description = "商品SPU名称", required = true, example = "丝滑飘逸小短裙")
@NotNull(message = "商品SPU名称不能为空")
private String spuName;
@Schema(description = "商品SKU编号", required = true, example = "3082")
@NotNull(message = "商品SKU编号不能为空")
private Long skuId;
@Schema(description = "评分星级 1-5分", required = true, example = "5")
@NotNull(message = "评分星级 1-5分不能为空")
private Integer scores;
@Schema(description = "描述星级 1-5分", required = true, example = "5")
@NotNull(message = "描述星级 1-5分不能为空")
private Integer descriptionScores;
@Schema(description = "服务星级 1-5分", required = true, example = "5")
@NotNull(message = "服务星级 1-5分不能为空")
private Integer benefitScores;
@Schema(description = "评论内容", required = true, example = "穿身上很漂亮诶(*^▽^*)")
@NotNull(message = "评论内容不能为空")
private String content;
@Schema(description = "评论图片地址数组以逗号分隔最多上传9张", required = true, example = "[https://www.iocoder.cn/xx.png, https://www.iocoder.cn/xx.png]")
@Size(max = 9, message = "评论图片地址数组长度不能超过9张")
private List<String> picUrls;
@Schema(description = "评价人名称", required = true, example = "小姑凉")
@NotNull(message = "评价人名称不能为空")
private String userNickname;
@Schema(description = "评价人头像", required = true, example = "https://www.iocoder.cn/xx.png")
@NotNull(message = "评价人头像不能为空")
private String userAvatar;
@Schema(description = "评价人", required = true, example = "16868")
@NotNull(message = "评价人不能为空")
private Long userId;
}

View File

@ -8,6 +8,7 @@ import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils;
import cn.iocoder.yudao.module.member.api.address.dto.AddressRespDTO;
import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO;
import cn.iocoder.yudao.module.product.api.comment.dto.CommentCreateReqDTO;
import cn.iocoder.yudao.module.product.api.property.dto.ProductPropertyValueDetailRespDTO;
import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuUpdateStockReqDTO;
import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateReqDTO;
@ -18,6 +19,7 @@ import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderDetailR
import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderPageItemRespVO;
import cn.iocoder.yudao.module.trade.controller.app.base.property.AppProductPropertyValueDetailRespVO;
import cn.iocoder.yudao.module.trade.controller.app.order.vo.*;
import cn.iocoder.yudao.module.trade.controller.app.order.vo.item.AppTradeOrderItemCommentCreateReqVO;
import cn.iocoder.yudao.module.trade.controller.app.order.vo.item.AppTradeOrderItemRespVO;
import cn.iocoder.yudao.module.trade.dal.dataobject.cart.TradeCartDO;
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
@ -75,9 +77,10 @@ public interface TradeOrderConvert {
return orderItem;
});
}
TradeOrderItemDO convert(TradePriceCalculateRespBO.OrderItem item);
@Mapping(source = "userId" , target = "userId")
@Mapping(source = "userId", target = "userId")
PriceCalculateReqDTO convert(AppTradeOrderCreateReqVO createReqVO, Long userId);
@Mappings({
@ -85,6 +88,7 @@ public interface TradeOrderConvert {
@Mapping(source = "count", target = "incrCount"),
})
ProductSkuUpdateStockReqDTO.Item convert(TradeOrderItemDO bean);
List<ProductSkuUpdateStockReqDTO.Item> convertList(List<TradeOrderItemDO> list);
default PayOrderCreateReqDTO convert(TradeOrderDO order, List<TradeOrderItemDO> orderItems,
@ -136,7 +140,7 @@ public interface TradeOrderConvert {
properties.forEach(property -> {
ProductPropertyValueDetailRespDTO propertyValueDetail = propertyValueDetailMap.get(property.getValueId());
if (propertyValueDetail == null) {
return;
return;
}
item.getProperties().add(convert(propertyValueDetail));
});
@ -148,7 +152,9 @@ public interface TradeOrderConvert {
});
return new PageResult<>(orderVOs, pageResult.getTotal());
}
TradeOrderPageItemRespVO convert(TradeOrderDO order, List<TradeOrderItemDO> items);
ProductPropertyValueDetailRespVO convert(ProductPropertyValueDetailRespDTO bean);
// TODO 芋艿可简化
@ -179,7 +185,9 @@ public interface TradeOrderConvert {
orderVO.setUser(convert(user));
return orderVO;
}
TradeOrderDetailRespVO convert2(TradeOrderDO order, List<TradeOrderItemDO> items);
MemberUserRespVO convert(MemberUserRespDTO bean);
// TODO 芋艿可简化
@ -214,7 +222,9 @@ public interface TradeOrderConvert {
});
return new PageResult<>(orderVOs, pageResult.getTotal());
}
AppTradeOrderPageItemRespVO convert02(TradeOrderDO order, List<TradeOrderItemDO> items);
AppProductPropertyValueDetailRespVO convert02(ProductPropertyValueDetailRespDTO bean);
default AppTradeOrderDetailRespVO convert02(TradeOrderDO order, List<TradeOrderItemDO> orderItems,
@ -243,10 +253,13 @@ public interface TradeOrderConvert {
orderVO.setReceiverAreaName(AreaUtils.format(order.getReceiverAreaId()));
return orderVO;
}
AppTradeOrderDetailRespVO convert3(TradeOrderDO order, List<TradeOrderItemDO> items);
AppTradeOrderItemRespVO convert03(TradeOrderItemDO bean);
CommentCreateReqDTO convert04(AppTradeOrderItemCommentCreateReqVO createReqVO);
default TradePriceCalculateReqBO convert(Long userId, AppTradeOrderSettlementReqVO settlementReqVO,
List<TradeCartDO> cartList) {
TradePriceCalculateReqBO reqBO = new TradePriceCalculateReqBO();
@ -286,6 +299,8 @@ public interface TradeOrderConvert {
}
return respVO;
}
AppTradeOrderSettlementRespVO convert0(TradePriceCalculateRespBO calculate, AddressRespDTO address);
}