Merge remote-tracking branch 'yudao/feature/mall_product' into feature/mall_product

This commit is contained in:
puhui999 2023-06-18 13:17:36 +08:00
commit cd4940d710
47 changed files with 719 additions and 184 deletions

View File

@ -31,6 +31,7 @@
<module>yudao-spring-boot-starter-biz-sms</module>
<module>yudao-spring-boot-starter-biz-pay</module>
<module>yudao-spring-boot-starter-biz-trade</module>
<module>yudao-spring-boot-starter-biz-weixin</module>
<module>yudao-spring-boot-starter-biz-social</module>
<module>yudao-spring-boot-starter-biz-tenant</module>

View File

@ -0,0 +1,82 @@
package cn.iocoder.yudao.framework.common.util.spel;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
// TODO @Chopper SpringExpressionUtils 合并下
/**
* SpelUtil
*
* @author Chopper
* @version v1.0
* @since 2021-01-11 10:45
*/
public class SpelUtil {
/**
* spel表达式解析器
*/
private static SpelExpressionParser spelExpressionParser = new SpelExpressionParser();
/**
* 参数名发现器
*/
private static DefaultParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
/**
* 转换 jspl参数
*
* @param joinPoint
* @param spel
* @return
*/
public static String compileParams(JoinPoint joinPoint, String spel) { //Spel表达式解析日志信息
//获得方法参数名数组
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
String[] parameterNames = parameterNameDiscoverer.getParameterNames(signature.getMethod());
if (parameterNames != null && parameterNames.length > 0) {
EvaluationContext context = new StandardEvaluationContext();
//获取方法参数值
Object[] args = joinPoint.getArgs();
for (int i = 0; i < args.length; i++) {
//替换spel里的变量值为实际值 比如 #user --> user对象
context.setVariable(parameterNames[i], args[i]);
}
return spelExpressionParser.parseExpression(spel).getValue(context).toString();
}
return "";
}
/**
* 转换 jspl参数
*
* @param joinPoint
* @param spel
* @return
*/
public static String compileParams(JoinPoint joinPoint, Object rvt, String spel) { //Spel表达式解析日志信息
//获得方法参数名数组
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
String[] parameterNames = parameterNameDiscoverer.getParameterNames(signature.getMethod());
if (parameterNames != null && parameterNames.length > 0) {
EvaluationContext context = new StandardEvaluationContext();
//获取方法参数值
Object[] args = joinPoint.getArgs();
for (int i = 0; i < args.length; i++) {
//替换spel里的变量值为实际值 比如 #user --> user对象
context.setVariable(parameterNames[i], args[i]);
}
context.setVariable("rvt", rvt);
return spelExpressionParser.parseExpression(spel).getValue(context).toString();
}
return "";
}
}

View File

@ -37,7 +37,7 @@
<!-- 业务组件 -->
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-module-system-api</artifactId> <!-- 需要使用它,进行操作日志的记录 -->
<artifactId>yudao-module-system-api</artifactId>
<version>${revision}</version>
</dependency>

View File

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-framework</artifactId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>yudao-spring-boot-starter-biz-trade</artifactId>
<packaging>jar</packaging>
<name>${project.artifactId}</name>
<description>交易模块</description>
<url>https://github.com/YunaiV/ruoyi-vue-pro</url>
<dependencies>
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-common</artifactId>
</dependency>
<!-- Spring 核心 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!-- Web 相关 -->
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-spring-boot-starter-web</artifactId>
<scope>provided</scope>
</dependency>
<!-- 业务组件 -->
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-module-system-api</artifactId> <!-- 需要使用它,进行操作日志的记录 -->
<version>${revision}</version>
</dependency>
<!-- 工具类相关 -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-spring-boot-starter-security</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,16 @@
package cn.iocoder.yudao.framework.trade.config;
import cn.iocoder.yudao.framework.trade.core.aop.AfterSaleLogAspect;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.context.annotation.Bean;
// TODO @Chopper yudao-module-trade-biz framework
@AutoConfiguration
public class YudaoAfterSaleLogAutoConfiguration {
@Bean
public AfterSaleLogAspect afterSaleLogAspect() {
return new AfterSaleLogAspect();
}
}

View File

@ -0,0 +1,32 @@
package cn.iocoder.yudao.framework.trade.core.annotations;
import java.lang.annotation.*;
/**
* 售后日志
*
* @author 陈賝
* @since 2023/6/8 17:04
*/
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface AfterSaleLog {
/**
* 售后 ID
*/
String id();
// TODO @陈賝是不是改成一个操作的枚举
/**
* 操作类型
*/
String operateType() default "";
/**
* 日志内容
*/
String content() default "";
}

View File

@ -0,0 +1,73 @@
package cn.iocoder.yudao.framework.trade.core.aop;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.iocoder.yudao.framework.common.util.spel.SpelUtil;
import cn.iocoder.yudao.framework.trade.core.annotations.AfterSaleLog;
import cn.iocoder.yudao.framework.trade.core.dto.TradeAfterSaleLogCreateReqDTO;
import cn.iocoder.yudao.framework.trade.core.enums.AfterSaleStatusEnum;
import cn.iocoder.yudao.framework.trade.core.service.AfterSaleLogService;
import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import java.util.HashMap;
import java.util.Map;
/**
* 记录售后日志的 AOP 切面
*
* @author 陈賝
* @since 2023/6/13 13:54
*/
@Slf4j
@Aspect
public class AfterSaleLogAspect {
@AfterReturning(pointcut = "@annotation(afterSaleLog)", returning = "info")
public void doAfterReturning(JoinPoint joinPoint, AfterSaleLog afterSaleLog, Object info) {
try {
// 日志对象拼接
Integer userType = WebFrameworkUtils.getLoginUserType();
Long id = WebFrameworkUtils.getLoginUserId();
Map<String, String> formatObj = spelFormat(joinPoint, info);
TradeAfterSaleLogCreateReqDTO dto = new TradeAfterSaleLogCreateReqDTO()
.setUserId(id).setUserType(userType)
// MapUtil.getLong() TODO @陈賝试试这个方法
.setAfterSaleId(Long.valueOf(formatObj.get("id")))
.setContent(formatObj.get("content"))
.setOperateType(formatObj.get("operateType"));
// 异步存入数据库 TODO 可以注入哈
SpringUtil.getBean(AfterSaleLogService.class).insert(dto);
} catch (Exception exception) {
// TODO @陈賝日志要记录下参数哈不然排查问题不好搞
log.error("日志记录错误", exception);
}
}
/**
* 获取描述信息
*/
public static Map<String, String> spelFormat(JoinPoint joinPoint, Object info) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
AfterSaleLog afterSaleLogPoint = signature.getMethod().getAnnotation(AfterSaleLog.class);
Map<String, String> result = new HashMap<>(2); // TODO @陈賝Maps.newExpectedXXX(3)
// 售后ID
String id = SpelUtil.compileParams(joinPoint, info, afterSaleLogPoint.id());
result.put("id", id);
// 操作类型
String operateType = SpelUtil.compileParams(joinPoint, info, afterSaleLogPoint.operateType());
result.put("operateType", operateType);
// 日志内容
String content = SpelUtil.compileParams(joinPoint, info, afterSaleLogPoint.content());
if (StrUtil.isNotEmpty(afterSaleLogPoint.operateType())) {
content += AfterSaleStatusEnum.valueOf(SpelUtil.compileParams(joinPoint, info, afterSaleLogPoint.operateType())).description();
}
result.put("content", content);
return result;
}
}

View File

@ -0,0 +1,43 @@
package cn.iocoder.yudao.framework.trade.core.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 售后日志的创建 Request DTO
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class TradeAfterSaleLogCreateReqDTO {
/**
* 编号
*/
private Long id;
/**
* 用户编号
*
* 关联 1AdminUserDO id 字段
* 关联 2MemberUserDO id 字段
*/
private Long userId;
/**
* 用户类型
*/
private Integer userType;
/**
* 售后编号
*/
private Long afterSaleId;
/**
* 操作类型
*/
private String operateType;
/**
* 操作明细
*/
private String content;
}

View File

@ -0,0 +1,26 @@
package cn.iocoder.yudao.framework.trade.core.enums;
/**
* 售后状态的枚举
*
* @author 陈賝
* @since 2023/6/13 13:53
*/
public enum AfterSaleStatusEnum {
/**
* 申请中
*/
APPLY("申请中");
private final String description;
AfterSaleStatusEnum(String description) {
this.description = description;
}
public String description() {
return description;
}
}

View File

@ -0,0 +1,18 @@
package cn.iocoder.yudao.framework.trade.core.service;
import cn.iocoder.yudao.framework.trade.core.dto.TradeAfterSaleLogCreateReqDTO;
// TODO @陈賝类注释
public interface AfterSaleLogService {
/**
* 创建售后日志
*
* @param logDTO 日志记录
* @author 陈賝
* @since 2023/6/12 14:18
*/
// TODO @陈賝createLog 方法名
void insert(TradeAfterSaleLogCreateReqDTO logDTO);
}

View File

@ -0,0 +1 @@
cn.iocoder.yudao.framework.trade.config.YudaoAfterSaleLogAutoConfiguration

View File

@ -1,6 +1,6 @@
package cn.iocoder.yudao.module.product.api.comment;
import cn.iocoder.yudao.module.product.api.comment.dto.CommentCreateReqDTO;
import cn.iocoder.yudao.module.product.api.comment.dto.ProductCommentCreateReqDTO;
/**
* 产品评论 API 接口
@ -9,6 +9,7 @@ import cn.iocoder.yudao.module.product.api.comment.dto.CommentCreateReqDTO;
*/
public interface ProductCommentApi {
// TODO @puhuiLong orderId 放到 createReqDTO 里噶
/**
* 创建评论
*
@ -16,6 +17,6 @@ public interface ProductCommentApi {
* @param orderId 订单 id
* @return 返回评论创建后的 id
*/
Long createComment(CommentCreateReqDTO createReqDTO, Long orderId);
Long createComment(ProductCommentCreateReqDTO createReqDTO, Long orderId);
}

View File

@ -10,71 +10,64 @@ import java.util.List;
* @author HUIHUI
*/
@Data
public class CommentCreateReqDTO {
public class ProductCommentCreateReqDTO {
/**
* 是否匿名
* 商品 SKU 编号
*/
private Boolean anonymous;
private Long skuId;
/**
* 交易订单项编号
*/
private Long orderItemId;
// TODO @huihuispuIdspuName 去查询哇通过 skuId
/**
* 商品 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张
* 评论图片地址数组以逗号分隔最多上传 9
*/
private List<String> picUrls;
/**
* 是否匿名
*/
private Boolean anonymous;
/**
* 评价人
*/
private Long userId;
// TODO @puhui999是不是 userNicknameuserAvatar 去掉通过 userId 查询
/**
* 评价人名称
*/
private String userNickname;
/**
* 评价人头像
*/
private String userAvatar;
/**
* 评价人
*/
private Long userId;
}

View File

@ -1,6 +1,6 @@
package cn.iocoder.yudao.module.product.api.comment;
import cn.iocoder.yudao.module.product.api.comment.dto.CommentCreateReqDTO;
import cn.iocoder.yudao.module.product.api.comment.dto.ProductCommentCreateReqDTO;
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;
@ -17,12 +17,14 @@ import javax.annotation.Resource;
@Service
@Validated
public class ProductCommentApiImpl implements ProductCommentApi {
@Resource
private ProductCommentService productCommentService;
@Override
public Long createComment(CommentCreateReqDTO createReqDTO, Long orderId) {
public Long createComment(ProductCommentCreateReqDTO createReqDTO, Long orderId) {
ProductCommentDO commentDO = ProductCommentConvert.INSTANCE.convert(createReqDTO, orderId);
return productCommentService.createComment(commentDO);
}
}

View File

@ -30,15 +30,15 @@ public class ProductCommentBaseVO {
@NotNull(message = "商品 SKU 编号不能为空")
private Long skuId;
@Schema(description = "评分星级 1-5", required = true, example = "5")
@Schema(description = "评分星级 1-5 ", required = true, example = "5")
@NotNull(message = "评分星级不能为空")
private Integer scores;
@Schema(description = "描述星级 1-5", required = true, example = "5")
@Schema(description = "描述星级 1-5 ", required = true, example = "5")
@NotNull(message = "描述星级不能为空")
private Integer descriptionScores;
@Schema(description = "服务星级 1-5", required = true, example = "5")
@Schema(description = "服务星级 1-5 ", required = true, example = "5")
@NotNull(message = "服务星级分不能为空")
private Integer benefitScores;
@ -46,8 +46,8 @@ public class ProductCommentBaseVO {
@NotNull(message = "评论内容不能为空")
private String content;
@Schema(description = "评论图片地址数组,以逗号分隔最多上传9张", required = true, example = "[https://www.iocoder.cn/xx.png, https://www.iocoder.cn/xxx.png]")
@Size(max = 9, message = "评论图片地址数组长度不能超过9张")
@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

@ -13,6 +13,7 @@ import javax.validation.constraints.NotNull;
@ToString(callSuper = true)
public class ProductCommentCreateReqVO extends ProductCommentBaseVO {
// TODO @puhui999是不是也放到父类里
@Schema(description = "评价人", required = true, example = "16868")
@NotNull(message = "评价人不能为空")
private Long userId;

View File

@ -31,7 +31,7 @@ public class ProductCommentPageReqVO extends PageParam {
@Schema(description = "商品SPU名称", example = "感冒药")
private String spuName;
@Schema(description = "评分星级 1-5", example = "5")
@Schema(description = "评分星级 1-5 ", example = "5")
@InEnum(ProductCommentScoresEnum.class)
private Integer scores;

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, example = "false")
@Schema(description = "是否匿名", required = true, example = "false")
private Boolean anonymous;
@Schema(description = "交易订单编号", required = true, example = "24428")

View File

@ -36,6 +36,7 @@ public class AppProductCommentController {
@Resource
private ProductCommentService productCommentService;
// TODO @puhui999可以实现下
@GetMapping("/list")
@Operation(summary = "获得最近的 n 条商品评价")
@Parameters({
@ -44,7 +45,6 @@ public class AppProductCommentController {
})
public CommonResult<List<AppProductCommentRespVO>> getCommentList(@RequestParam("spuId") Long spuId,
@RequestParam(value = "count", defaultValue = "10") Integer count) {
List<AppProductPropertyValueDetailRespVO> list = new ArrayList<>();
AppProductPropertyValueDetailRespVO item1 = new AppProductPropertyValueDetailRespVO();
@ -101,6 +101,7 @@ public class AppProductCommentController {
return success(productCommentService.getCommentPage(pageVO, Boolean.TRUE));
}
// TODO @puhuiget-statistics方法改成 getCommentStatisticsgetCommentPageTabsCount 也改掉哈
@GetMapping("/getCommentStatistics")
@Operation(summary = "获得商品的评价统计")
public CommonResult<AppCommentStatisticsRespVO> getCommentPage(@Valid @RequestParam("spuId") Long spuId) {

View File

@ -8,11 +8,6 @@ import lombok.ToString;
import javax.validation.constraints.NotNull;
/**
* 用户 APP - 商品评价分页 Request VO
*
* @author HUIHUI
*/
@Schema(description = "用户APP - 商品评价分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ -23,12 +18,10 @@ public class AppCommentPageReqVO extends PageParam {
* 好评
*/
public static final Integer GOOD_COMMENT = 1;
/**
* 中评
*/
public static final Integer MEDIOCRE_COMMENT = 2;
/**
* 差评
*/

View File

@ -4,11 +4,6 @@ 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)

View File

@ -8,11 +8,6 @@ import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.util.List;
/**
* 用户APP - 商品评价创建 Request VO
*
* @author HUIHUI
*/
@Schema(description = "用户APP - 商品评价创建 Request VO")
@Data
@ToString(callSuper = true)
@ -26,36 +21,36 @@ public class AppProductCommentCreateReqVO {
@NotNull(message = "交易订单项编号不能为空")
private Long orderItemId;
@Schema(description = "商品SPU编号", required = true, example = "91192")
@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 = "81192")
@Schema(description = "商品 SKU 编号", required = true, example = "81192")
@NotNull(message = "商品SKU编号不能为空")
private Long skuId;
@Schema(description = "评分星级 1-5", required = true, example = "5")
@NotNull(message = "评分星级 1-5分不能为空")
@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分不能为空")
@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分不能为空")
@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/xxx.png]")
@Size(max = 9, message = "评论图片地址数组长度不能超过9张")
@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

@ -60,7 +60,7 @@ public class AppProductCommentRespVO {
@Schema(description = "追加评价内容", example = "穿了很久都很丝滑诶")
private String additionalContent;
@Schema(description = "追评评价图片地址数组,以逗号分隔最多上传9张", example = "[https://www.iocoder.cn/xx.png, https://www.iocoder.cn/xxx.png]")
@Schema(description = "追评评价图片地址数组,以逗号分隔最多上传 9 ", example = "[https://www.iocoder.cn/xx.png, https://www.iocoder.cn/xxx.png]")
private List<String> additionalPicUrls;
@Schema(description = "追加评价时间")
@ -86,24 +86,24 @@ public class AppProductCommentRespVO {
@Schema(description = "商品 SKU 属性", required = true)
private List<AppProductPropertyValueDetailRespVO> skuProperties;
@Schema(description = "评分星级 1-5", required = true, example = "5")
@NotNull(message = "评分星级 1-5分不能为空")
@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分不能为空")
@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分不能为空")
@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/xxx.png]")
@Size(max = 9, message = "评论图片地址数组长度不能超过9张")
@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,7 @@
package cn.iocoder.yudao.module.product.convert.comment;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.product.api.comment.dto.CommentCreateReqDTO;
import cn.iocoder.yudao.module.product.api.comment.dto.ProductCommentCreateReqDTO;
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.AppCommentStatisticsRespVO;
@ -49,7 +49,7 @@ public interface ProductCommentConvert {
*
* @param descriptionScores 描述星级
* @param benefitScores 服务星级
* @return {@link Integer}
* @return 综合评分
*/
@Named("convertScores")
default Integer convertScores(Integer descriptionScores, Integer benefitScores) {
@ -61,7 +61,7 @@ public interface ProductCommentConvert {
@Mapping(target = "orderId", source = "orderId")
@Mapping(target = "scores", expression = "java(convertScores(createReqDTO.getDescriptionScores(), createReqDTO.getBenefitScores()))")
ProductCommentDO convert(CommentCreateReqDTO createReqDTO, Long orderId);
ProductCommentDO convert(ProductCommentCreateReqDTO createReqDTO, Long orderId);
@Mapping(target = "userId", constant = "0L")
@Mapping(target = "orderId", constant = "0L")

View File

@ -97,7 +97,7 @@ public class ProductCommentDO extends BaseDO {
/**
* 评分星级
*
* 1-5
* 1-5
*/
private Integer scores;
/**

View File

@ -25,6 +25,7 @@ public interface ProductCommentMapper extends BaseMapperX<ProductCommentDO> {
}
// TODO 芋艿在看看这块
// TODO @puhui999直接使用 scores 来算好评中评差评
static void appendTabQuery(LambdaQueryWrapperX<ProductCommentDO> queryWrapper, Integer type) {
// 构建好评查询语句好评计算 (商品评分星级+服务评分星级) >= 8
if (ObjectUtil.equal(type, AppCommentPageReqVO.GOOD_COMMENT)) {

View File

@ -54,7 +54,8 @@ public interface ProductCommentService {
PageResult<AppProductCommentRespVO> getCommentPage(AppCommentPageReqVO pageVO, Boolean visible);
/**
* 创建商品评论 后台管理员创建评论使用
* 创建商品评论
* 后台管理员创建评论使用
*
* @param createReqVO 商品评价创建 Request VO 对象
*/

View File

@ -86,6 +86,7 @@ public class ProductCommentServiceImpl implements ProductCommentService {
@Transactional(rollbackFor = Exception.class)
public void createComment(ProductCommentCreateReqVO createReqVO) {
// 校验订单
// TODO @puhui999不校验哈尽可能解耦
Long orderId = tradeOrderApi.validateOrder(createReqVO.getUserId(), createReqVO.getOrderItemId());
// 校验评论
validateComment(createReqVO.getSpuId(), createReqVO.getUserId(), orderId);
@ -104,8 +105,6 @@ public class ProductCommentServiceImpl implements ProductCommentService {
return commentDO.getId();
}
// TODO 只有创建和更新诶 要不要删除接口
private void validateComment(Long spuId, Long userId, Long orderId) {
ProductSpuDO spu = productSpuService.getSpu(spuId);
if (null == spu) {
@ -144,6 +143,7 @@ public class ProductCommentServiceImpl implements ProductCommentService {
public PageResult<AppProductCommentRespVO> getCommentPage(AppCommentPageReqVO pageVO, Boolean visible) {
PageResult<AppProductCommentRespVO> result = ProductCommentConvert.INSTANCE.convertPage02(
productCommentMapper.selectPage(pageVO, visible));
// TODO @puhui999要不这块放到 controller 里拼接
Set<Long> skuIds = result.getList().stream().map(AppProductCommentRespVO::getSkuId).collect(Collectors.toSet());
List<ProductSkuDO> skuList = productSkuService.getSkuList(skuIds);
Map<Long, ProductSkuDO> skuDOMap = new HashMap<>(skuIds.size());

View File

@ -126,6 +126,7 @@ CREATE TABLE IF NOT EXISTS `product_property_value` (
PRIMARY KEY("id")
) COMMENT '规格值';
-- TODO @puhui999格式不太对哈
CREATE TABLE IF NOT EXISTS `product_comment`
(
`id`
@ -166,7 +167,7 @@ CREATE TABLE IF NOT EXISTS `product_comment`
(
1
) DEFAULT NULL COMMENT '是否可见true:显示false:隐藏',
`scores` tinyint DEFAULT NULL COMMENT '评分星级1-5',
`scores` tinyint DEFAULT NULL COMMENT '评分星级1-5 ',
`description_scores` tinyint DEFAULT NULL COMMENT '描述星级1-5 ',
`benefit_scores` tinyint DEFAULT NULL COMMENT '服务星级1-5 ',
`content` varchar

View File

@ -38,7 +38,7 @@ public class AppCombinationActivityController {
activity1.setName("618 大拼团");
activity1.setUserSize(3);
activity1.setSpuId(2048L);
activity1.setPicUrl("商品图片地址");
activity1.setPicUrl("https://demo26.crmeb.net/uploads/attach/2021/11/15/a79f5d2ea6bf0c3c11b2127332dfe2df.jpg");
activity1.setMarketPrice(50);
activity1.setCombinationPrice(100);
activityList.add(activity1);
@ -48,7 +48,7 @@ public class AppCombinationActivityController {
activity2.setName("双十一拼团");
activity2.setUserSize(5);
activity2.setSpuId(4096L);
activity2.setPicUrl("商品图片地址");
activity2.setPicUrl("https://thirdwx.qlogo.cn/mmopen/vi_32/Q0j4TwGTfTKXMYJOomfp7cebz3cIeb8sHk3GGSIJtWEgREe3j7J1WoAbTvIOicpcNdFkWAziatBSMod8b5RyS4CQ/132");
activity2.setMarketPrice(100);
activity2.setCombinationPrice(200);
activityList.add(activity2);

View File

@ -1,7 +1,11 @@
package cn.iocoder.yudao.module.promotion.controller.app.seckill;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.module.promotion.controller.app.seckill.vo.AppSeckillActivitiDetailRespVO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.promotion.controller.app.seckill.vo.activity.AppSeckillActivityDetailRespVO;
import cn.iocoder.yudao.module.promotion.controller.app.seckill.vo.activity.AppSeckillActivityNowRespVO;
import cn.iocoder.yudao.module.promotion.controller.app.seckill.vo.activity.AppSeckillActivityRespVO;
import cn.iocoder.yudao.module.promotion.controller.app.seckill.vo.config.AppSeckillConfigRespVO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
@ -23,12 +27,65 @@ import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@Validated
public class AppSeckillActivityController {
@GetMapping("/get-now")
@Operation(summary = "获得当前秒杀活动") // 提供给首页使用
// TODO 芋艿需要增加 spring cache
public CommonResult<AppSeckillActivityNowRespVO> getNowSeckillActivity() {
AppSeckillActivityNowRespVO respVO = new AppSeckillActivityNowRespVO();
respVO.setConfig(new AppSeckillConfigRespVO().setId(10L).setStartTime("01:00").setEndTime("09:59"));
List<AppSeckillActivityRespVO> activityList = new ArrayList<>();
AppSeckillActivityRespVO activity1 = new AppSeckillActivityRespVO();
activity1.setId(1L);
activity1.setName("618 大秒杀");
activity1.setSpuId(2048L);
activity1.setPicUrl("https://demo26.crmeb.net/uploads/attach/2021/11/15/a79f5d2ea6bf0c3c11b2127332dfe2df.jpg");
activity1.setMarketPrice(50);
activity1.setSeckillPrice(100);
activityList.add(activity1);
AppSeckillActivityRespVO activity2 = new AppSeckillActivityRespVO();
activity2.setId(2L);
activity2.setName("双十一大秒杀");
activity2.setSpuId(4096L);
activity2.setPicUrl("https://thirdwx.qlogo.cn/mmopen/vi_32/Q0j4TwGTfTKXMYJOomfp7cebz3cIeb8sHk3GGSIJtWEgREe3j7J1WoAbTvIOicpcNdFkWAziatBSMod8b5RyS4CQ/132");
activity2.setMarketPrice(100);
activity2.setSeckillPrice(200);
activityList.add(activity2);
respVO.setActivities(activityList);
return success(respVO);
}
@GetMapping("/page")
@Operation(summary = "获得秒杀活动分页")
// TODO @芋艿分页参数
public CommonResult<PageResult<AppSeckillActivityRespVO>> getSeckillActivityPage() {
List<AppSeckillActivityRespVO> activityList = new ArrayList<>();
AppSeckillActivityRespVO activity1 = new AppSeckillActivityRespVO();
activity1.setId(1L);
activity1.setName("618 大秒杀");
activity1.setSpuId(2048L);
activity1.setPicUrl("https://demo26.crmeb.net/uploads/attach/2021/11/15/a79f5d2ea6bf0c3c11b2127332dfe2df.jpg");
activity1.setMarketPrice(50);
activity1.setSeckillPrice(100);
activityList.add(activity1);
AppSeckillActivityRespVO activity2 = new AppSeckillActivityRespVO();
activity2.setId(2L);
activity2.setName("双十一大秒杀");
activity2.setSpuId(4096L);
activity2.setPicUrl("https://thirdwx.qlogo.cn/mmopen/vi_32/Q0j4TwGTfTKXMYJOomfp7cebz3cIeb8sHk3GGSIJtWEgREe3j7J1WoAbTvIOicpcNdFkWAziatBSMod8b5RyS4CQ/132");
activity2.setMarketPrice(100);
activity2.setSeckillPrice(200);
activityList.add(activity2);
return success(new PageResult<>(activityList, 100L));
}
@GetMapping("/get-detail")
@Operation(summary = "获得秒杀活动明细")
@Parameter(name = "id", description = "活动编号", required = true, example = "1024")
public CommonResult<AppSeckillActivitiDetailRespVO> getSeckillActivity(@RequestParam("id") Long id) {
public CommonResult<AppSeckillActivityDetailRespVO> getSeckillActivity(@RequestParam("id") Long id) {
// TODO 芋艿如果禁用的时候需要抛出异常
AppSeckillActivitiDetailRespVO obj = new AppSeckillActivitiDetailRespVO();
AppSeckillActivityDetailRespVO obj = new AppSeckillActivityDetailRespVO();
// 设置其属性的值
obj.setId(id);
obj.setName("晚九点限时秒杀");
@ -37,9 +94,9 @@ public class AppSeckillActivityController {
obj.setEndTime(LocalDateTime.of(2023, 6, 11, 23, 59, 0));
obj.setSpuId(633L);
// 创建一个Product对象的列表
List<AppSeckillActivitiDetailRespVO.Product> productList = new ArrayList<>();
List<AppSeckillActivityDetailRespVO.Product> productList = new ArrayList<>();
// 创建三个新的Product对象并设置其属性的值
AppSeckillActivitiDetailRespVO.Product product1 = new AppSeckillActivitiDetailRespVO.Product();
AppSeckillActivityDetailRespVO.Product product1 = new AppSeckillActivityDetailRespVO.Product();
product1.setSkuId(1L);
product1.setSeckillPrice(100);
product1.setQuota(50);
@ -47,7 +104,7 @@ public class AppSeckillActivityController {
// 将第一个Product对象添加到列表中
productList.add(product1);
// 创建第二个Product对象并设置其属性的值
AppSeckillActivitiDetailRespVO.Product product2 = new AppSeckillActivitiDetailRespVO.Product();
AppSeckillActivityDetailRespVO.Product product2 = new AppSeckillActivityDetailRespVO.Product();
product2.setSkuId(2L);
product2.setSeckillPrice(200);
product2.setQuota(100);
@ -55,7 +112,7 @@ public class AppSeckillActivityController {
// 将第二个Product对象添加到列表中
productList.add(product2);
// 创建第三个Product对象并设置其属性的值
AppSeckillActivitiDetailRespVO.Product product3 = new AppSeckillActivitiDetailRespVO.Product();
AppSeckillActivityDetailRespVO.Product product3 = new AppSeckillActivityDetailRespVO.Product();
product3.setSkuId(3L);
product3.setSeckillPrice(300);
product3.setQuota(150);

View File

@ -0,0 +1,33 @@
package cn.iocoder.yudao.module.promotion.controller.app.seckill;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.module.promotion.controller.app.seckill.vo.config.AppSeckillConfigRespVO;
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.RestController;
import java.util.Arrays;
import java.util.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@Tag(name = "用户 App - 秒杀时间段")
@RestController
@RequestMapping("/promotion/seckill-config")
@Validated
public class AppSeckillConfigController {
@GetMapping("/list")
@Operation(summary = "获得秒杀时间段列表")
public CommonResult<List<AppSeckillConfigRespVO>> getConfigList() {
return success(Arrays.asList(
new AppSeckillConfigRespVO().setId(1L).setStartTime("00:00").setEndTime("09:59"),
new AppSeckillConfigRespVO().setId(2L).setStartTime("10:00").setEndTime("12:59"),
new AppSeckillConfigRespVO().setId(2L).setStartTime("13:00").setEndTime("23:59")
));
}
}

View File

@ -1,4 +1,4 @@
package cn.iocoder.yudao.module.promotion.controller.app.seckill.vo;
package cn.iocoder.yudao.module.promotion.controller.app.seckill.vo.activity;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@ -6,9 +6,9 @@ import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
@Schema(description = "用户 App - 秒杀活动 Response VO")
@Schema(description = "用户 App - 秒杀活动的详细 Response VO")
@Data
public class AppSeckillActivitiDetailRespVO {
public class AppSeckillActivityDetailRespVO {
@Schema(description = "秒杀活动编号", required = true, example = "1024")
private Long id;

View File

@ -0,0 +1,19 @@
package cn.iocoder.yudao.module.promotion.controller.app.seckill.vo.activity;
import cn.iocoder.yudao.module.promotion.controller.app.seckill.vo.config.AppSeckillConfigRespVO;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.List;
@Schema(description = "用户 App - 当前秒杀活动 Response VO")
@Data
public class AppSeckillActivityNowRespVO {
@Schema(description = "秒杀时间段", required = true)
private AppSeckillConfigRespVO config;
@Schema(description = "秒杀活动数组", required = true)
private List<AppSeckillActivityRespVO> activities;
}

View File

@ -0,0 +1,28 @@
package cn.iocoder.yudao.module.promotion.controller.app.seckill.vo.activity;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "用户 App - 秒杀活动 Response VO")
@Data
public class AppSeckillActivityRespVO {
@Schema(description = "秒杀活动编号", required = true, example = "1024")
private Long id;
@Schema(description = "秒杀活动名称", required = true, example = "晚九点限时秒杀")
private String name;
@Schema(description = "商品 SPU 编号", required = true, example = "2048")
private Long spuId;
@Schema(description = "商品图片", required = true, example = "4096") // SPU picUrl 读取
private String picUrl;
@Schema(description = "商品市场价,单位:分", required = true, example = "50") // SPU marketPrice 读取
private Integer marketPrice;
@Schema(description = "秒杀金额,单位:分", required = true, example = "100") // 从秒杀商品里取最低价
private Integer seckillPrice;
}

View File

@ -0,0 +1,18 @@
package cn.iocoder.yudao.module.promotion.controller.app.seckill.vo.config;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "用户 App - 秒杀时间段 Response VO")
@Data
public class AppSeckillConfigRespVO {
@Schema(description = "秒杀时间段编号", required = true, example = "1024")
private Long id;
@Schema(description = "开始时间点", required = true, example = "09:00")
private String startTime;
@Schema(description = "结束时间点", required = true, example = "09:59")
private String endTime;
}

View File

@ -19,23 +19,38 @@ import static cn.hutool.core.util.ArrayUtil.firstMatch;
@Getter
public enum TradeAfterSaleStatusEnum implements IntArrayValuable {
APPLY(10,"申请中", // 申请售后
"会员申请退款"),
SELLER_AGREE(20, "卖家通过", // 卖家通过售后商品待退货
"商家同意退款"),
BUYER_DELIVERY(30,"待卖家收货", // 买家已退货等待卖家收货商家待收货
"会员填写退货物流信息"),
WAIT_REFUND(40, "等待平台退款", // 卖家已收货等待平台退款等待退款等待退款
"商家收货"),
COMPLETE(50, "完成", // 完成退款退款成功
"商家确认退款"),
BUYER_CANCEL(61, "买家取消售后", // 买家取消
"会员取消退款"),
SELLER_DISAGREE(62,"卖家拒绝", // 卖家拒绝售后商家拒绝商家拒绝
"商家拒绝退款"),
SELLER_REFUSE(63,"卖家拒绝收货", // 卖家拒绝收货终止售后商家拒收货
"商家拒绝收货"),
/**
* 申请售后
*/
APPLY(10,"申请中", "会员申请退款"),
/**
* 卖家通过售后商品待退货
*/
SELLER_AGREE(20, "卖家通过", "商家同意退款"),
/**
* 买家已退货等待卖家收货商家待收货
*/
BUYER_DELIVERY(30,"待卖家收货", "会员填写退货物流信息"),
/**
* 卖家已收货等待平台退款等待退款等待退款
*/
WAIT_REFUND(40, "等待平台退款", "商家收货"),
/**
* 完成退款退款成功
*/
COMPLETE(50, "完成", "商家确认退款"),
/**
* 买家取消
*/
BUYER_CANCEL(61, "买家取消售后", "会员取消退款"),
/**
* 卖家拒绝售后商家拒绝商家拒绝
*/
SELLER_DISAGREE(62,"卖家拒绝", "商家拒绝退款"),
/**
* 卖家拒绝收货终止售后商家拒收货
*/
SELLER_REFUSE(63,"卖家拒绝收货", "商家拒绝收货"),
;
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(TradeAfterSaleStatusEnum::getStatus).toArray();

View File

@ -58,6 +58,11 @@
<groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-spring-boot-starter-biz-ip</artifactId>
</dependency>
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-spring-boot-starter-biz-trade</artifactId>
<version>${revision}</version>
</dependency>
<!-- Web 相关 -->
<dependency>
@ -93,6 +98,7 @@
<groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-spring-boot-starter-excel</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.trade.controller.admin.aftersale;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.trade.core.annotations.AfterSaleLog;
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.api.property.ProductPropertyValueApi;
@ -11,6 +12,7 @@ import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.TradeAfterSal
import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.TradeAfterSalePageReqVO;
import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.TradeAfterSaleRefuseReqVO;
import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.TradeAfterSaleRespPageItemVO;
import cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppTradeAfterSaleCreateReqVO;
import cn.iocoder.yudao.module.trade.convert.aftersale.TradeAfterSaleConvert;
import cn.iocoder.yudao.module.trade.dal.dataobject.aftersale.TradeAfterSaleDO;
import cn.iocoder.yudao.module.trade.service.aftersale.TradeAfterSaleService;
@ -110,4 +112,20 @@ public class TradeAfterSaleController {
return success(true);
}
// TODO @陈賝后续要删除下
/**
* 售后日志测试
*
* @param createReqVO
* @return cn.iocoder.yudao.framework.common.pojo.CommonResult<java.lang.Long>
* @author 陈賝
* @date 2023/6/14 21:39
*/
@PostMapping(value = "/create")
@AfterSaleLog(id = "#createReqVO.orderItemId", content = "'申请售后:售后编号['+#createReqVO.orderItemId+'] , '", operateType = "#createReqVO.operateType")
public CommonResult<Long> createAfterSale(@RequestBody AppTradeAfterSaleCreateReqVO createReqVO) {
return success(1L);
// return success(afterSaleService.createAfterSale(getLoginUserId(), createReqVO));
}
}

View File

@ -1,6 +1,7 @@
package cn.iocoder.yudao.module.trade.controller.app.aftersale.vo;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import cn.iocoder.yudao.framework.trade.core.enums.AfterSaleStatusEnum;
import cn.iocoder.yudao.module.trade.enums.aftersale.TradeAfterSaleWayEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@ -31,6 +32,14 @@ public class AppTradeAfterSaleCreateReqVO {
@NotNull(message = "申请原因不能为空")
private String applyReason;
// TODO @陈賝这个参数不应该有呀
/**
* @see AfterSaleStatusEnum
*/
@Schema(description = "操作类型", required = true, example = "1")
@NotNull(message = "操作类型不能为空")
private String operateType;
@Schema(description = "补充描述", example = "商品质量不好")
private String applyDescription;

View File

@ -2,7 +2,6 @@ package cn.iocoder.yudao.module.trade.controller.app.order;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
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;
@ -49,13 +48,12 @@ public class AppTradeOrderController {
@Resource
private ProductPropertyValueApi productPropertyValueApi;
@Resource
private ProductCommentApi productCommentApi;
@Resource
private TradeOrderProperties tradeOrderProperties;
@Resource
private ProductCommentApi productCommentApi;
@GetMapping("/settlement")
@Operation(summary = "获得订单结算信息")
@PreAuthenticated
@ -141,6 +139,7 @@ public class AppTradeOrderController {
@Operation(summary = "创建交易订单项的评价")
public CommonResult<Long> createOrderItemComment(@RequestBody AppTradeOrderItemCommentCreateReqVO createReqVO) {
// 校验订单项订单项存在订单就存在
// TODO @puhui999要去查询订单是不是自己的不然别人模拟请求哈
TradeOrderItemDO item = tradeOrderService.getOrderItem(createReqVO.getUserId(), createReqVO.getOrderItemId());
if (item == null) {
throw exception(ORDER_ITEM_NOT_FOUND);
@ -149,6 +148,6 @@ public class AppTradeOrderController {
return success(productCommentApi.createComment(TradeOrderConvert.INSTANCE.convert04(createReqVO), item.getOrderId()));
}
// TODO 合并代码后发现只有商家回复功能 用户追评不要了吗
// TODO 合并代码后发现只有商家回复功能 用户追评不要了吗不要了哈
}

View File

@ -7,11 +7,6 @@ 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 {
@ -24,6 +19,7 @@ public class AppTradeOrderItemCommentCreateReqVO {
@NotNull(message = "交易订单项编号不能为空")
private Long orderItemId;
// TODO @puhuispuIdspuNameskuId 直接查询出来
@Schema(description = "商品SPU编号", required = true, example = "29502")
@NotNull(message = "商品SPU编号不能为空")
private Long spuId;
@ -36,30 +32,32 @@ public class AppTradeOrderItemCommentCreateReqVO {
@NotNull(message = "商品SKU编号不能为空")
private Long skuId;
@Schema(description = "评分星级 1-5", required = true, example = "5")
@NotNull(message = "评分星级 1-5分不能为空")
@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分不能为空")
@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分不能为空")
@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张")
@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;
// TODO @puhuiuserAvataruserAvataruserId 直接查询出来
@Schema(description = "评价人头像", required = true, example = "https://www.iocoder.cn/xx.png")
@NotNull(message = "评价人头像不能为空")
private String userAvatar;

View File

@ -8,7 +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.comment.dto.ProductCommentCreateReqDTO;
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;
@ -227,6 +227,7 @@ public interface TradeOrderConvert {
AppProductPropertyValueDetailRespVO convert02(ProductPropertyValueDetailRespDTO bean);
// TODO 芋艿可简化
default AppTradeOrderDetailRespVO convert02(TradeOrderDO order, List<TradeOrderItemDO> orderItems,
List<ProductPropertyValueDetailRespDTO> propertyValueDetails, TradeOrderProperties tradeOrderProperties) {
AppTradeOrderDetailRespVO orderVO = convert3(order, orderItems);
@ -258,7 +259,7 @@ public interface TradeOrderConvert {
AppTradeOrderItemRespVO convert03(TradeOrderItemDO bean);
CommentCreateReqDTO convert04(AppTradeOrderItemCommentCreateReqVO createReqVO);
ProductCommentCreateReqDTO convert04(AppTradeOrderItemCommentCreateReqVO createReqVO);
default TradePriceCalculateReqBO convert(Long userId, AppTradeOrderSettlementReqVO settlementReqVO,
List<TradeCartDO> cartList) {
@ -302,5 +303,4 @@ public interface TradeOrderConvert {
AppTradeOrderSettlementRespVO convert0(TradePriceCalculateRespBO calculate, AddressRespDTO address);
}

View File

@ -52,29 +52,9 @@ public class TradeAfterSaleLogDO extends BaseDO {
*/
private Long afterSaleId;
/**
* 订单编号
*
* 关联 {@link TradeOrderDO#getId()}
* 操作类型 {@link TradeAfterSaleStatusEnum}
*/
private Long orderId;
/**
* 订单项编号
*
* 关联 {@link TradeOrderItemDO#getId()}
*/
private Long orderItemId;
/**
* 售后状态之前
*
* 枚举 {@link TradeAfterSaleStatusEnum}
*/
private Integer beforeStatus;
/**
* 售后状态之后
*
* 枚举 {@link TradeAfterSaleStatusEnum}
*/
private Integer afterStatus;
private String operateType;
/**
* 操作明细
*/

View File

@ -5,6 +5,8 @@ import cn.hutool.core.util.RandomUtil;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
import cn.iocoder.yudao.framework.trade.core.dto.TradeAfterSaleLogCreateReqDTO;
import cn.iocoder.yudao.framework.trade.core.service.AfterSaleLogService;
import cn.iocoder.yudao.module.pay.api.refund.PayRefundApi;
import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundCreateReqDTO;
import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.TradeAfterSaleDisagreeReqVO;
@ -26,6 +28,8 @@ import cn.iocoder.yudao.module.trade.enums.order.TradeOrderItemAfterSaleStatusEn
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 lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronization;
@ -43,9 +47,10 @@ import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.*;
*
* @author 芋道源码
*/
@Slf4j
@Service
@Validated
public class TradeAfterSaleServiceImpl implements TradeAfterSaleService {
public class TradeAfterSaleServiceImpl implements TradeAfterSaleService, AfterSaleLogService {
@Resource
private TradeOrderService tradeOrderService;
@ -80,7 +85,7 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService {
/**
* 校验交易订单项是否可以申请售后
*
* @param userId 用户编号
* @param userId 用户编号
* @param createReqVO 售后创建信息
* @return 交易订单项
*/
@ -117,7 +122,7 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService {
}
// 如果是退货退款的情况需要额外校验是否发货
if (createReqVO.getWay().equals(TradeAfterSaleWayEnum.RETURN_AND_REFUND.getWay())
&& !TradeOrderStatusEnum.haveDelivered(order.getStatus())) {
&& !TradeOrderStatusEnum.haveDelivered(order.getStatus())) {
throw exception(AFTER_SALE_CREATE_FAIL_ORDER_STATUS_NO_DELIVERED);
}
return orderItem;
@ -133,7 +138,7 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService {
TradeOrderDO order = tradeOrderService.getOrder(orderItem.getUserId(), orderItem.getOrderId());
afterSale.setOrderNo(order.getNo()); // 记录 orderNo 订单流水方便后续检索
afterSale.setType(TradeOrderStatusEnum.isCompleted(order.getStatus())
? TradeAfterSaleTypeEnum.AFTER_SALE.getType() : TradeAfterSaleTypeEnum.IN_SALE.getType());
? TradeAfterSaleTypeEnum.AFTER_SALE.getType() : TradeAfterSaleTypeEnum.IN_SALE.getType());
// TODO 退还积分
tradeAfterSaleMapper.insert(afterSale);
@ -380,13 +385,38 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService {
TradeOrderItemAfterSaleStatusEnum.NONE.getStatus(), null);
}
@Deprecated
private void createAfterSaleLog(Long userId, Integer userType, TradeAfterSaleDO afterSale,
Integer beforeStatus, Integer afterStatus) {
TradeAfterSaleLogDO afterSaleLog = new TradeAfterSaleLogDO().setUserId(userId).setUserType(userType)
.setAfterSaleId(afterSale.getId()).setOrderId(afterSale.getOrderId())
.setOrderItemId(afterSale.getOrderItemId()).setBeforeStatus(beforeStatus).setAfterStatus(afterStatus)
.setContent(TradeAfterSaleStatusEnum.valueOf(afterStatus).getContent());
tradeAfterSaleLogMapper.insert(afterSaleLog);
TradeAfterSaleLogCreateReqDTO logDTO = new TradeAfterSaleLogCreateReqDTO()
.setUserId(userId)
.setUserType(userType)
.setAfterSaleId(afterSale.getId())
.setOperateType(afterStatus.toString());
// TODO 废弃待删除
this.insert(logDTO);
}
/**
* 日志记录
*
* @param logDTO 日志记录
* @author 陈賝
* @date 2023/6/12 14:18
*/
@Override
@Async
public void insert(TradeAfterSaleLogCreateReqDTO logDTO) {
try {
TradeAfterSaleLogDO afterSaleLog = new TradeAfterSaleLogDO()
.setUserId(logDTO.getUserId())
.setUserType(logDTO.getUserType())
.setAfterSaleId(logDTO.getAfterSaleId())
.setOperateType(logDTO.getOperateType())
.setContent(logDTO.getContent());
tradeAfterSaleLogMapper.insert(afterSaleLog);
}catch (Exception exception){
log.error("日志记录错误", exception);
}
}
}

View File

@ -66,6 +66,7 @@ public class TradeAfterSaleServiceTest extends BaseDbUnitTest {
AppTradeAfterSaleCreateReqVO createReqVO = new AppTradeAfterSaleCreateReqVO()
.setOrderItemId(1L).setRefundPrice(100).setWay(TradeAfterSaleWayEnum.RETURN_AND_REFUND.getWay())
.setApplyReason("退钱").setApplyDescription("快退")
.setOperateType("APPLY")
.setApplyPicUrls(asList("https://www.baidu.com/1.png", "https://www.baidu.com/2.png"));
// mock 方法交易订单项
TradeOrderItemDO orderItem = randomPojo(TradeOrderItemDO.class, o -> {
@ -102,8 +103,8 @@ public class TradeAfterSaleServiceTest extends BaseDbUnitTest {
assertEquals(afterSaleLog.getUserType(), UserTypeEnum.MEMBER.getValue());
assertEquals(afterSaleLog.getAfterSaleId(), afterSaleId);
assertPojoEquals(afterSale, orderItem, "id", "creator", "createTime", "updater", "updateTime");
assertNull(afterSaleLog.getBeforeStatus());
assertEquals(afterSaleLog.getAfterStatus(), TradeAfterSaleStatusEnum.APPLY.getStatus());
// assertNull(afterSaleLog.getBeforeStatus());
// assertEquals(afterSaleLog.getAfterStatus(), TradeAfterSaleStatusEnum.APPLY.getStatus());
assertEquals(afterSaleLog.getContent(), TradeAfterSaleStatusEnum.APPLY.getContent());
}

View File

@ -54,11 +54,11 @@
<!-- <version>${revision}</version>-->
<!-- </dependency>-->
<!-- 支付服务 -->
<!-- <dependency>-->
<!-- <groupId>cn.iocoder.boot</groupId>-->
<!-- <artifactId>yudao-module-pay-biz</artifactId>-->
<!-- <version>${revision}</version>-->
<!-- </dependency>-->
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-module-pay-biz</artifactId>
<version>${revision}</version>
</dependency>
<!-- 微信公众号模块。默认注释,保证编译速度 -->
<!-- <dependency>-->
@ -68,25 +68,19 @@
<!-- </dependency>-->
<!-- 商城相关模块。默认注释,保证编译速度 -->
<!-- <dependency>-->
<!-- <groupId>cn.iocoder.boot</groupId>-->
<!-- <artifactId>yudao-module-promotion-biz</artifactId>-->
<!-- <version>${revision}</version>-->
<!-- </dependency>-->
<!-- <dependency>-->
<!-- <groupId>cn.iocoder.boot</groupId>-->
<!-- <artifactId>yudao-module-product-biz</artifactId>-->
<!-- <version>${revision}</version>-->
<!-- </dependency>-->
<!-- <dependency>-->
<!-- <groupId>cn.iocoder.boot</groupId>-->
<!-- <artifactId>yudao-module-trade-biz</artifactId>-->
<!-- <version>${revision}</version>-->
<!-- </dependency>-->
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-module-point-biz</artifactId>
<artifactId>yudao-module-promotion-biz</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-module-product-biz</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-module-trade-biz</artifactId>
<version>${revision}</version>
</dependency>