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

# Conflicts:
#	yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/order/TradeOrderController.java
#	yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/delivery/DeliveryExpressConvert.java
#	yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/delivery/DeliveryExpressService.java
#	yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/delivery/DeliveryExpressServiceImpl.java
This commit is contained in:
puhui999
2023-08-22 00:46:47 +08:00
255 changed files with 5475 additions and 1275 deletions

View File

@@ -20,7 +20,6 @@ import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.List;
import java.util.Objects;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
@@ -84,8 +83,15 @@ public class AppFavoriteController {
@Operation(summary = "检查是否收藏过商品")
@PreAuthenticated
public CommonResult<Boolean> isFavoriteExists(AppFavoriteReqVO reqVO) {
ProductFavoriteDO favoriteDO = productFavoriteService.getFavorite(getLoginUserId(), reqVO.getSpuId());
return success(Objects.nonNull(favoriteDO));
ProductFavoriteDO favorite = productFavoriteService.getFavorite(getLoginUserId(), reqVO.getSpuId());
return success(favorite != null);
}
@GetMapping(value = "/get-count")
@Operation(summary = "获得商品收藏数量")
@PreAuthenticated
public CommonResult<Long> getFavoriteCount() {
return success(productFavoriteService.getFavoriteCount(getLoginUserId()));
}
}

View File

@@ -21,4 +21,8 @@ public interface ProductFavoriteMapper extends BaseMapperX<ProductFavoriteDO> {
.orderByDesc(ProductFavoriteDO::getId));
}
default Long selectCountByUserId(Long userId) {
return selectCount(ProductFavoriteDO::getUserId, userId);
}
}

View File

@@ -16,7 +16,7 @@ public interface ProductFavoriteService {
/**
* 创建商品收藏
*
* @param userId 用户 id
* @param userId 用户编号
* @param spuId SPU 编号
*/
Long createFavorite(Long userId, Long spuId);
@@ -24,7 +24,7 @@ public interface ProductFavoriteService {
/**
* 取消商品收藏
*
* @param userId 用户 id
* @param userId 用户编号
* @param spuId SPU 编号
*/
void deleteFavorite(Long userId, Long spuId);
@@ -32,7 +32,7 @@ public interface ProductFavoriteService {
/**
* 分页查询用户收藏列表
*
* @param userId 用户 id
* @param userId 用户编号
* @param reqVO 请求 vo
*/
PageResult<ProductFavoriteDO> getFavoritePage(Long userId, @Valid AppFavoritePageReqVO reqVO);
@@ -40,9 +40,17 @@ public interface ProductFavoriteService {
/**
* 获取收藏过商品
*
* @param userId 用户id
* @param userId 用户编号
* @param spuId SPU 编号
*/
ProductFavoriteDO getFavorite(Long userId, Long spuId);
/**
* 获取用户收藏数量
*
* @param userId 用户编号
* @return 数量
*/
Long getFavoriteCount(Long userId);
}

View File

@@ -10,7 +10,6 @@ import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.Objects;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.FAVORITE_EXISTS;
@@ -31,7 +30,7 @@ public class ProductFavoriteServiceImpl implements ProductFavoriteService {
@Override
public Long createFavorite(Long userId, Long spuId) {
ProductFavoriteDO favorite = productFavoriteMapper.selectByUserIdAndSpuId(userId, spuId);
if (Objects.nonNull(favorite)) {
if (favorite != null) {
throw exception(FAVORITE_EXISTS);
}
@@ -43,7 +42,7 @@ public class ProductFavoriteServiceImpl implements ProductFavoriteService {
@Override
public void deleteFavorite(Long userId, Long spuId) {
ProductFavoriteDO favorite = productFavoriteMapper.selectByUserIdAndSpuId(userId, spuId);
if (Objects.isNull(favorite)) {
if (favorite == null) {
throw exception(FAVORITE_NOT_EXISTS);
}
@@ -60,4 +59,9 @@ public class ProductFavoriteServiceImpl implements ProductFavoriteService {
return productFavoriteMapper.selectByUserIdAndSpuId(userId, spuId);
}
@Override
public Long getFavoriteCount(Long userId) {
return productFavoriteMapper.selectCountByUserId(userId);
}
}

View File

@@ -1,5 +1,6 @@
package cn.iocoder.yudao.module.promotion.enums.decorate;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
@@ -8,11 +9,44 @@ import lombok.Getter;
* @author jason
*/
@Getter
@AllArgsConstructor
@SuppressWarnings("JavadocLinkAsPlainText")
public enum DecorateComponentEnum {
NAV_MENU("nav-menu", "导航菜单"),
ROLLING_BANNER("rolling-banner", "滚动横幅广告"),
PRODUCT_CATEGORY("product-category", "商品分类");
/**
* 格式:[{
* "name": "标题"
* "picUrl": "https://www.iocoder.cn/xxx.png",
* "url": "/pages/users/index"
* }]
*
* 最多 10 个
*/
MENU("menu", "菜单"),
/**
* 格式:[{
* "name": "标题"
* "url": "/pages/users/index"
* }]
*/
ROLLING_NEWS("scrolling-news", "滚动新闻"),
/**
* 格式:[{
* "picUrl": "https://www.iocoder.cn/xxx.png",
* "url": "/pages/users/index"
* }]
*/
SLIDE_SHOW("slide-show", "轮播图"),
/**
* 格式:[{
* "name": "标题"
* "type": "类型", // best、hot、new、benefit、good
* "tag": "标签" // 例如说:多买多省
* }]
*
* 最多 4 个
*/
PRODUCT_RECOMMEND("product-recommend", "商品推荐");
/**
* 页面组件代码
@@ -24,9 +58,4 @@ public enum DecorateComponentEnum {
*/
private final String desc;
DecorateComponentEnum(String code, String desc) {
this.code = code;
this.desc = desc;
}
}

View File

@@ -15,14 +15,17 @@ import java.util.Arrays;
@Getter
public enum DecoratePageEnum implements IntArrayValuable {
INDEX(1, "首页");
INDEX(1, "首页"),
MY(2, "个人中心"),
;
private static final int[] ARRAYS = Arrays.stream(values()).mapToInt(DecoratePageEnum::getId).toArray();
private static final int[] ARRAYS = Arrays.stream(values()).mapToInt(DecoratePageEnum::getPage).toArray();
/**
* 页面 id
* 页面编号
*/
private final Integer id;
private final Integer page;
/**
* 页面名称
*/

View File

@@ -0,0 +1,18 @@
### /promotion/decorate/save 保存页面装修组件
POST {{baseUrl}}/promotion/decorate/save
Content-Type: application/json
Authorization: Bearer {{token}}
tenant-id: {{adminTenentId}}
{
"page": 1,
"code": "slide-show",
"status": 0,
"value": "null"
}
### /promotion/decorate/list 获取指定页面的组件列表
GET {{baseUrl}}/promotion/decorate/list?page=1
Content-Type: application/json
Authorization: Bearer {{token}}
tenant-id: {{adminTenentId}}

View File

@@ -2,45 +2,49 @@ package cn.iocoder.yudao.module.promotion.controller.admin.decorate;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import cn.iocoder.yudao.module.promotion.controller.admin.decorate.vo.DecorateComponentSaveReqVO;
import cn.iocoder.yudao.module.promotion.controller.admin.decorate.vo.DecorateComponentRespVO;
import cn.iocoder.yudao.module.promotion.controller.admin.decorate.vo.DecorateComponentSaveReqVO;
import cn.iocoder.yudao.module.promotion.convert.decorate.DecorateComponentConvert;
import cn.iocoder.yudao.module.promotion.enums.decorate.DecoratePageEnum;
import cn.iocoder.yudao.module.promotion.service.decorate.DecorateComponentService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.module.promotion.convert.decorate.DecorateComponentConvert.INSTANCE;
@Tag(name = "管理后台 - 店铺页面装修")
@RestController
@RequestMapping("/promotion/decorate")
@Validated
public class DecorateComponentController {
@Resource
private DecorateComponentService decorateComponentService;
@PostMapping("/page-save")
@Operation(summary = "保存页面装修")
// TODO 加权限
public CommonResult<Boolean> savePageComponents(@Valid @RequestBody DecorateComponentSaveReqVO reqVO) {
decorateComponentService.savePageComponents(reqVO);
@PostMapping("/save")
@Operation(summary = "保存页面装修组件")
@PreAuthorize("@ss.hasPermission('promotion:decorate:save')")
public CommonResult<Boolean> saveDecorateComponent(@Valid @RequestBody DecorateComponentSaveReqVO reqVO) {
decorateComponentService.saveDecorateComponent(reqVO);
return success(true);
}
@GetMapping("/get-page-components")
@Operation(summary = "获取装修页面组件")
@Parameter(name = "pageId", description = "页面 id", required = true)
// TODO 加权限
public CommonResult<DecorateComponentRespVO> getPageComponents(
@RequestParam("pageId") @InEnum(DecoratePageEnum.class) Integer pageId) {
return success(INSTANCE.convert2(pageId, decorateComponentService.getPageComponents(pageId)));
@GetMapping("/list")
@Operation(summary = "获取指定页面组件列表")
@Parameter(name = "page", description = "页面 id", required = true)
@PreAuthorize("@ss.hasPermission('promotion:decorate:query')")
public CommonResult<List<DecorateComponentRespVO>> getDecorateComponentListByPage(
@RequestParam("page") @InEnum(DecoratePageEnum.class) Integer page) {
return success(DecorateComponentConvert.INSTANCE.convertList02(
decorateComponentService.getDecorateComponentListByPage(page, null)));
}
}

View File

@@ -3,31 +3,17 @@ package cn.iocoder.yudao.module.promotion.controller.admin.decorate.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.List;
@Schema(description = "管理后台 - 页面装修 Resp VO")
@Data
public class DecorateComponentRespVO {
@Schema(description = "页面 id ", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer pageId;
@Schema(description = "组件编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "nav-menu")
private String code;
@Schema(description = "页面组件", requiredMode = Schema.RequiredMode.REQUIRED, example = "TODO")
private List<ComponentRespVO> components;
@Schema(description = "组件的内容配置项", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "TODO")
private String value;
@Schema(description = "管理后台 - 页面组件 Resp VO")
@Data
public static class ComponentRespVO {
@Schema(description = "组件编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long id;
@Schema(description = "组件编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "nav-menu")
private String code;
@Schema(description = "组件的内容配置项", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "TODO")
private String value;
}
@Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer status;
}

View File

@@ -5,10 +5,8 @@ import cn.iocoder.yudao.module.promotion.enums.decorate.DecoratePageEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.Valid;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.util.List;
@Schema(description = "管理后台 - 页面装修的保存 Request VO ")
@Data
@@ -17,28 +15,17 @@ public class DecorateComponentSaveReqVO {
@Schema(description = "页面 id ", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "页面 id 不能为空")
@InEnum(DecoratePageEnum.class)
private Integer pageId;
private Integer page;
@Schema(description = "页面组件列表", requiredMode = Schema.RequiredMode.REQUIRED, example = "TODO")
@NotEmpty(message = "页面组件列表不能为空")
@Valid
private List<ComponentReqVO> components;
@Schema(description = "组件编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "nav-menu")
@NotEmpty(message = "组件编码不能为空")
private String code;
@Schema(description = "管理后台 - 页面装修组件 Request VO")
@Data
public static class ComponentReqVO {
@Schema(description = "组件对应值, json 字符串, 含内容配置,具体数据", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "组件值为空")
private String value;
@Schema(description = "组件编码", example = "1")
private Long id;
@Schema(description = "组件编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "nav-menu")
@NotEmpty(message = "组件编码不能为空")
private String code;
@Schema(description = "组件对应值, json 字符串, 含内容配置,具体数据", requiredMode = Schema.RequiredMode.REQUIRED, example = "TODO")
@NotEmpty(message = "组件值为空")
private String value;
}
@Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer status;
}

View File

@@ -2,22 +2,26 @@ package cn.iocoder.yudao.module.promotion.controller.app.coupon;
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.promotion.controller.app.coupon.vo.coupon.AppCouponMatchReqVO;
import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon.AppCouponMatchRespVO;
import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon.AppCouponPageReqVO;
import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon.AppCouponRespVO;
import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.template.AppCouponTemplatePageReqVO;
import cn.iocoder.yudao.module.promotion.service.coupon.CouponService;
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 java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
@Tag(name = "用户 App - 优惠劵")
@RestController
@@ -25,6 +29,9 @@ import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@Validated
public class AppCouponController {
@Resource
private CouponService couponService;
// TODO 芋艿:待实现
@PostMapping("/take")
@Operation(summary = "领取优惠劵")
@@ -93,4 +100,11 @@ public class AppCouponController {
return success(new PageResult<>(list, 20L));
}
@GetMapping(value = "/get-unused-count")
@Operation(summary = "获得未使用的优惠劵数量")
@PreAuthenticated
public CommonResult<Long> getUnusedCouponCount() {
return success(couponService.getUnusedCouponCount(getLoginUserId()));
}
}

View File

@@ -1,8 +1,10 @@
package cn.iocoder.yudao.module.promotion.controller.app.decorate;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import cn.iocoder.yudao.module.promotion.controller.app.decorate.vo.AppDecorateComponentRespVO;
import cn.iocoder.yudao.module.promotion.convert.decorate.DecorateComponentConvert;
import cn.iocoder.yudao.module.promotion.enums.decorate.DecoratePageEnum;
import cn.iocoder.yudao.module.promotion.service.decorate.DecorateComponentService;
import io.swagger.v3.oas.annotations.Operation;
@@ -15,9 +17,9 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.module.promotion.convert.decorate.DecorateComponentConvert.INSTANCE;
@Tag(name = "用户 APP - 店铺装修")
@RestController
@@ -28,12 +30,13 @@ public class AppDecorateController {
@Resource
private DecorateComponentService decorateComponentService;
@GetMapping("/get-page-components")
@Operation(summary = "获取装修页面组件")
@Parameter(name = "pageId", description = "页面 id", required = true)
public CommonResult<AppDecorateComponentRespVO> getPageComponents(
@RequestParam("pageId") @InEnum(DecoratePageEnum.class) Integer pageId) {
return success(INSTANCE.appConvert(pageId, decorateComponentService.getPageComponents(pageId)));
@GetMapping("/list")
@Operation(summary = "获取指定页面组件列表")
@Parameter(name = "page", description = "页面编号", required = true)
public CommonResult<List<AppDecorateComponentRespVO>> getDecorateComponentListByPage(
@RequestParam("page") @InEnum(DecoratePageEnum.class) Integer page) {
return success(DecorateComponentConvert.INSTANCE.convertList(
decorateComponentService.getDecorateComponentListByPage(page, CommonStatusEnum.ENABLE.getStatus())));
}
}

View File

@@ -3,28 +3,14 @@ package cn.iocoder.yudao.module.promotion.controller.app.decorate.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.List;
@Schema(description = "用户 App - 页面装修 Resp VO")
@Schema(description = "用户 App - 页面组件 Resp VO")
@Data
public class AppDecorateComponentRespVO {
@Schema(description = "页面 id ", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer pageId;
@Schema(description = "组件编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "nav-menu")
private String code;
@Schema(description = "页面组件", requiredMode = Schema.RequiredMode.REQUIRED, example = "TODO")
private List<AppComponentRespVO> components;
@Schema(description = "用户 App - 页面组件 Resp VO")
@Data
public static class AppComponentRespVO {
@Schema(description = "组件编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "nav-menu")
private String code;
@Schema(description = "组件的内容配置项", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "TODO")
private String value;
}
@Schema(description = "组件的内容配置项", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "TODO")
private String value;
}

View File

@@ -1,8 +1,7 @@
package cn.iocoder.yudao.module.promotion.convert.decorate;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.module.promotion.controller.admin.decorate.vo.DecorateComponentSaveReqVO;
import cn.iocoder.yudao.module.promotion.controller.admin.decorate.vo.DecorateComponentRespVO;
import cn.iocoder.yudao.module.promotion.controller.admin.decorate.vo.DecorateComponentSaveReqVO;
import cn.iocoder.yudao.module.promotion.controller.app.decorate.vo.AppDecorateComponentRespVO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.decorate.DecorateComponentDO;
import org.mapstruct.Mapper;
@@ -10,33 +9,15 @@ import org.mapstruct.factory.Mappers;
import java.util.List;
import static cn.iocoder.yudao.module.promotion.controller.admin.decorate.vo.DecorateComponentRespVO.*;
import static cn.iocoder.yudao.module.promotion.controller.app.decorate.vo.AppDecorateComponentRespVO.*;
@Mapper
public interface DecorateComponentConvert {
DecorateComponentConvert INSTANCE = Mappers.getMapper(DecorateComponentConvert.class);
default List<DecorateComponentDO> convertList(Integer pageId, List<DecorateComponentSaveReqVO.ComponentReqVO> components) {
return CollectionUtils.convertList(components, c -> convert(pageId, c));
}
List<DecorateComponentRespVO> convertList02(List<DecorateComponentDO> list);
default DecorateComponentRespVO convert2(Integer pageId, List<DecorateComponentDO> list) {
List<ComponentRespVO> components = CollectionUtils.convertList(list, this::convert3);
return new DecorateComponentRespVO().setPageId(pageId).setComponents(components);
}
DecorateComponentDO convert(DecorateComponentSaveReqVO bean);
DecorateComponentDO convert(Integer pageId, DecorateComponentSaveReqVO.ComponentReqVO reqVO);
ComponentRespVO convert3(DecorateComponentDO componentDO);
// ========== App convert ==========
default AppDecorateComponentRespVO appConvert(Integer pageId, List<DecorateComponentDO> list) {
List<AppComponentRespVO> components = CollectionUtils.convertList(list, this::appConvert2);
return new AppDecorateComponentRespVO().setPageId(pageId).setComponents(components);
}
AppComponentRespVO appConvert2(DecorateComponentDO bean);
List<AppDecorateComponentRespVO> convertList(List<DecorateComponentDO> list);
}

View File

@@ -26,13 +26,14 @@ public class DecorateComponentDO extends BaseDO {
/**
* 所属页面 id
* 枚举 {@link DecoratePageEnum#getId()}
*
* 枚举 {@link DecoratePageEnum#getPage()}
*/
private Integer pageId;
private Integer page;
/**
* 组件编码
* 枚举 {@link DecorateComponentEnum#getCode()}
* 组件编码
* 枚举 {@link DecorateComponentEnum#getCode()}
*/
private String code;
@@ -47,4 +48,5 @@ public class DecorateComponentDO extends BaseDO {
* 枚举 {@link CommonStatusEnum}
*/
private Integer status;
}

View File

@@ -49,4 +49,10 @@ public interface CouponMapper extends BaseMapperX<CouponDO> {
.eq(CouponDO::getId, id).eq(CouponDO::getStatus, status));
}
default Long selectCountByUserIdAndStatus(Long userId, Integer status) {
return selectCount(new LambdaQueryWrapperX<CouponDO>()
.eq(CouponDO::getUserId, userId)
.eq(CouponDO::getStatus, status));
}
}

View File

@@ -1,7 +1,8 @@
package cn.iocoder.yudao.module.promotion.dal.mysql.decorate;
import cn.iocoder.yudao.module.promotion.dal.dataobject.decorate.DecorateComponentDO;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.promotion.dal.dataobject.decorate.DecorateComponentDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@@ -9,8 +10,15 @@ import java.util.List;
@Mapper
public interface DecorateComponentMapper extends BaseMapperX<DecorateComponentDO> {
default List<DecorateComponentDO> selectByPage(Integer pageId){
return selectList(DecorateComponentDO::getPageId, pageId);
default List<DecorateComponentDO> selectListByPageAndStatus(Integer page, Integer status) {
return selectList(new LambdaQueryWrapperX<DecorateComponentDO>()
.eq(DecorateComponentDO::getPage, page)
.eqIfPresent(DecorateComponentDO::getStatus, status));
}
default DecorateComponentDO selectByPageAndCode(Integer page, String code) {
return selectOne(DecorateComponentDO::getPage, page,
DecorateComponentDO::getCode, code);
}
}

View File

@@ -67,4 +67,12 @@ public interface CouponService {
*/
List<CouponDO> getCouponList(Long userId, Integer status);
/**
* 获得未使用的优惠劵数量
*
* @param userId 用户编号
* @return 未使用的优惠劵数量
*/
Long getUnusedCouponCount(Long userId);
}

View File

@@ -120,4 +120,9 @@ public class CouponServiceImpl implements CouponService {
}
}
@Override
public Long getUnusedCouponCount(Long userId) {
return couponMapper.selectCountByUserIdAndStatus(userId, CouponStatusEnum.UNUSED.getStatus());
}
}

View File

@@ -14,17 +14,18 @@ import java.util.List;
public interface DecorateComponentService {
/**
* 店铺装修页面保存
* 保存页面的组件信息
*
* @param reqVO 请求 VO
*/
void savePageComponents(DecorateComponentSaveReqVO reqVO);
void saveDecorateComponent(DecorateComponentSaveReqVO reqVO);
/**
* 根据页面 id获取页面的组件信息
* 根据页面 id获取页面的组件信息
*
* @param pageId 页面类型 {@link DecoratePageEnum#getId()}
* @param page 页面编号 {@link DecoratePageEnum#getPage()}
* @param status 状态
*/
List<DecorateComponentDO> getPageComponents(Integer pageId);
List<DecorateComponentDO> getDecorateComponentListByPage(Integer page, Integer status);
}

View File

@@ -1,19 +1,13 @@
package cn.iocoder.yudao.module.promotion.service.decorate;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.module.promotion.controller.admin.decorate.vo.DecorateComponentSaveReqVO;
import cn.iocoder.yudao.module.promotion.controller.admin.decorate.vo.DecorateComponentSaveReqVO.ComponentReqVO;
import cn.iocoder.yudao.module.promotion.convert.decorate.DecorateComponentConvert;
import cn.iocoder.yudao.module.promotion.dal.dataobject.decorate.DecorateComponentDO;
import cn.iocoder.yudao.module.promotion.dal.mysql.decorate.DecorateComponentMapper;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
import static cn.iocoder.yudao.module.promotion.convert.decorate.DecorateComponentConvert.INSTANCE;
/**
* 装修组件 Service 实现
@@ -27,22 +21,20 @@ public class DecorateComponentServiceImpl implements DecorateComponentService {
private DecorateComponentMapper decorateComponentMapper;
@Override
public void savePageComponents(DecorateComponentSaveReqVO reqVO) {
// 1.新增或修改页面组件
List<DecorateComponentDO> oldList = decorateComponentMapper.selectByPage(reqVO.getPageId());
decorateComponentMapper.saveOrUpdateBatch(INSTANCE.convertList(reqVO.getPageId(), reqVO.getComponents()));
// 2.删除相关组件
Set<Long> deleteIds = convertSet(oldList, DecorateComponentDO::getId);
deleteIds.removeAll(convertSet(reqVO.getComponents(), ComponentReqVO::getId, vo->Objects.nonNull(vo.getId())));
if (CollUtil.isNotEmpty(deleteIds)) {
decorateComponentMapper.deleteBatchIds(deleteIds);
public void saveDecorateComponent(DecorateComponentSaveReqVO reqVO) {
// 1. 如果存在,则进行更新
DecorateComponentDO dbComponent = decorateComponentMapper.selectByPageAndCode(reqVO.getPage(), reqVO.getCode());
if (dbComponent != null) {
decorateComponentMapper.updateById(DecorateComponentConvert.INSTANCE.convert(reqVO).setId(dbComponent.getId()));
return;
}
// 2. 不存在,则进行新增
decorateComponentMapper.insert(DecorateComponentConvert.INSTANCE.convert(reqVO));
}
@Override
public List<DecorateComponentDO> getPageComponents(Integer pageId) {
return decorateComponentMapper.selectByPage(pageId);
public List<DecorateComponentDO> getDecorateComponentListByPage(Integer page, Integer status) {
return decorateComponentMapper.selectListByPageAndStatus(page, status);
}
}

View File

@@ -1,20 +1,10 @@
package cn.iocoder.yudao.module.promotion.service.decorate;
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
import cn.iocoder.yudao.module.promotion.dal.dataobject.decorate.DecorateComponentDO;
import cn.iocoder.yudao.module.promotion.dal.mysql.decorate.DecorateComponentMapper;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import java.util.ArrayList;
import java.util.List;
import static cn.iocoder.yudao.module.promotion.enums.decorate.DecorateComponentEnum.ROLLING_BANNER;
import static cn.iocoder.yudao.module.promotion.enums.decorate.DecoratePageEnum.INDEX;
import static org.mockito.ArgumentMatchers.eq;
// TODO @芋艿:后续 review 下
/**
@@ -33,14 +23,14 @@ public class DecorateComponentServiceImplTest extends BaseMockitoUnitTest {
}
@Test
void testResp(){
List<DecorateComponentDO> list = new ArrayList<>(1);
DecorateComponentDO decorateDO = new DecorateComponentDO()
.setPageId(INDEX.getId()).setValue("")
.setCode(ROLLING_BANNER.getCode()).setId(1L);
list.add(decorateDO);
//mock 方法
Mockito.when(decorateComponentMapper.selectByPage(eq(1))).thenReturn(list);
}
// @Test
// void testResp(){
// List<DecorateComponentDO> list = new ArrayList<>(1);
// DecorateComponentDO decorateDO = new DecorateComponentDO()
// .setPage(INDEX.getPage()).setValue("")
// .setCode(ROLLING_NEWS.getCode()).setId(1L);
// list.add(decorateDO);
// //mock 方法
// Mockito.when(decorateComponentMapper.selectListByPageAndStatus(eq(1))).thenReturn(list);
// }
}

View File

@@ -45,7 +45,8 @@ public interface ErrorCodeConstants {
ErrorCode AFTER_SALE_DELIVERY_FAIL_STATUS_NOT_SELLER_AGREE = new ErrorCode(1011000108, "退货失败,售后单状态不处于【待买家退货】");
ErrorCode AFTER_SALE_CONFIRM_FAIL_STATUS_NOT_BUYER_DELIVERY = new ErrorCode(1011000109, "确认收货失败,售后单状态不处于【待确认收货】");
ErrorCode AFTER_SALE_REFUND_FAIL_STATUS_NOT_WAIT_REFUND = new ErrorCode(1011000110, "退款失败,售后单状态不是【待退款】");
ErrorCode AFTER_SALE_CANCEL_FAIL_STATUS_NOT_APPLY_OR_AGREE = new ErrorCode(1011000111, "取消售后单失败,售后单状态不是【待审核】或【卖家同意】");
ErrorCode AFTER_SALE_CANCEL_FAIL_STATUS_NOT_APPLY_OR_AGREE_OR_BUYER_DELIVERY =
new ErrorCode(1011000111, "取消售后单失败,售后单状态不是【待审核】或【卖家同意】或【商家待收货】");
// ========== Cart 模块 1011002000 ==========
ErrorCode CARD_ITEM_NOT_FOUND = new ErrorCode(1011002000, "购物车项不存在");

View File

@@ -5,6 +5,7 @@ import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
import java.util.Collection;
import static cn.hutool.core.util.ArrayUtil.firstMatch;
@@ -22,39 +23,51 @@ public enum TradeAfterSaleStatusEnum implements IntArrayValuable {
/**
* 【申请售后】
*/
APPLY(10,"申请中", "会员申请退款"),
APPLY(10,"申请中", "会员申请退款"), // 有赞的状态提示:退款申请待商家处理
/**
* 卖家通过售后;【商品待退货】
*/
SELLER_AGREE(20, "卖家通过", "商家同意退款"),
SELLER_AGREE(20, "卖家通过", "商家同意退款"), // 有赞的状态提示:请退货并填写物流信息
/**
* 买家已退货,等待卖家收货;【商家待收货】
*/
BUYER_DELIVERY(30,"待卖家收货", "会员填写退货物流信息"),
BUYER_DELIVERY(30,"待卖家收货", "会员填写退货物流信息"), // 有赞的状态提示:退货退款申请待商家处理
/**
* 卖家已收货,等待平台退款;等待退款【等待退款】
*/
WAIT_REFUND(40, "等待平台退款", "商家收货"),
WAIT_REFUND(40, "等待平台退款", "商家收货"), // 有赞的状态提示:无(有赞无该状态)
/**
* 完成退款【退款成功】
*/
COMPLETE(50, "完成", "商家确认退款"),
COMPLETE(50, "完成", "商家确认退款"), // 有赞的状态提示:退款成功
/**
* 【买家取消】
*/
BUYER_CANCEL(61, "买家取消售后", "会员取消退款"),
BUYER_CANCEL(61, "买家取消售后", "会员取消退款"), // 有赞的状态提示:退款关闭
/**
* 卖家拒绝售后;商家拒绝【商家拒绝】
*/
SELLER_DISAGREE(62,"卖家拒绝", "商家拒绝退款"),
SELLER_DISAGREE(62,"卖家拒绝", "商家拒绝退款"), // 有赞的状态提示:商家不同意退款申请
/**
* 卖家拒绝收货,终止售后;【商家拒收货】
*/
SELLER_REFUSE(63,"卖家拒绝收货", "商家拒绝收货"),
SELLER_REFUSE(63,"卖家拒绝收货", "商家拒绝收货"), // 有赞的状态提示:商家拒绝收货,不同意退款
;
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(TradeAfterSaleStatusEnum::getStatus).toArray();
/**
* 进行中的售后状态
*
* 不包括已经结束的状态
*/
public static final Collection<Integer> APPLYING_STATUSES = Arrays.asList(
APPLY.getStatus(),
SELLER_AGREE.getStatus(),
BUYER_DELIVERY.getStatus(),
WAIT_REFUND.getStatus()
);
/**
* 状态
*/

View File

@@ -2,3 +2,32 @@
GET {{baseUrl}}/trade/after-sale/page?pageNo=1&pageSize=10
Authorization: Bearer {{token}}
tenant-id: {{adminTenentId}}
### 同意售后 => 成功
PUT {{baseUrl}}/trade/after-sale/agree?id=7
Authorization: Bearer {{token}}
tenant-id: {{adminTenentId}}
Content-Type: application/json
### 拒绝售后 => 成功
PUT {{baseUrl}}/trade/after-sale/disagree
Authorization: Bearer {{token}}
tenant-id: {{adminTenentId}}
Content-Type: application/json
{
"id": 6,
"auditReason": "阿巴巴"
}
### 确认退款 => 成功
PUT {{baseUrl}}/trade/after-sale/refund?id=6
Authorization: Bearer {{token}}
tenant-id: {{adminTenentId}}
Content-Type: application/json
### 确认收货 => 成功
PUT {{baseUrl}}/trade/after-sale/receive?id=7
Authorization: Bearer {{token}}
tenant-id: {{adminTenentId}}
Content-Type: application/json

View File

@@ -3,10 +3,10 @@ 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.operatelog.core.annotations.OperateLog;
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;
import cn.iocoder.yudao.module.product.api.property.dto.ProductPropertyValueDetailRespDTO;
import cn.iocoder.yudao.module.pay.api.notify.dto.PayRefundNotifyReqDTO;
import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.TradeAfterSaleDisagreeReqVO;
import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.TradeAfterSalePageReqVO;
import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.TradeAfterSaleRefuseReqVO;
@@ -23,8 +23,8 @@ import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.annotation.security.PermitAll;
import javax.validation.Valid;
import java.util.List;
import java.util.Map;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@@ -32,7 +32,7 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.
import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
@Tag(name = "管理后台 - 交易售后")
@Tag(name = "管理后台 - 售后订单")
@RestController
@RequestMapping("/trade/after-sale")
@Validated
@@ -44,11 +44,9 @@ public class TradeAfterSaleController {
@Resource
private MemberUserApi memberUserApi;
@Resource
private ProductPropertyValueApi productPropertyValueApi;
@GetMapping("/page")
@Operation(summary = "获得交易售后分页")
@Operation(summary = "获得售后订单分页")
@PreAuthorize("@ss.hasPermission('trade:after-sale:query')")
public CommonResult<PageResult<TradeAfterSaleRespPageItemVO>> getAfterSalePage(@Valid TradeAfterSalePageReqVO pageVO) {
// 查询售后
@@ -57,13 +55,10 @@ public class TradeAfterSaleController {
return success(PageResult.empty());
}
// 查询商品属性
List<ProductPropertyValueDetailRespDTO> propertyValueDetails = productPropertyValueApi
.getPropertyValueDetailList(TradeAfterSaleConvert.INSTANCE.convertPropertyValueIds(pageResult.getList()));
// 查询会员
Map<Long, MemberUserRespDTO> memberUsers = memberUserApi.getUserMap(
convertSet(pageResult.getList(), TradeAfterSaleDO::getUserId));
return success(TradeAfterSaleConvert.INSTANCE.convertPage(pageResult, memberUsers, propertyValueDetails));
return success(TradeAfterSaleConvert.INSTANCE.convertPage(pageResult, memberUsers));
}
@PutMapping("/agree")
@@ -101,7 +96,7 @@ public class TradeAfterSaleController {
return success(true);
}
@PostMapping("/refund")
@PutMapping("/refund")
@Operation(summary = "确认退款")
@Parameter(name = "id", description = "售后编号", required = true, example = "1")
@PreAuthorize("@ss.hasPermission('trade:after-sale:refund')")
@@ -110,4 +105,15 @@ public class TradeAfterSaleController {
return success(true);
}
@PostMapping("/update-refunded")
@Operation(summary = "更新售后订单为已退款") // 由 pay-module 支付服务,进行回调,可见 PayNotifyJob
@PermitAll // 无需登录,安全由 PayDemoOrderService 内部校验实现
@OperateLog(enable = false) // 禁用操作日志,因为没有操作人
public CommonResult<Boolean> updateAfterRefund(@RequestBody PayRefundNotifyReqDTO notifyReqDTO) {
// 目前业务逻辑,不需要做任何事情
// 当然,退款会有小概率会失败的情况,可以监控失败状态,进行告警
log.info("[updateAfterRefund][notifyReqDTO({})]", notifyReqDTO);
return success(true);
}
}

View File

@@ -5,9 +5,10 @@ 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.api.property.ProductPropertyValueApi;
import cn.iocoder.yudao.module.product.api.property.dto.ProductPropertyValueDetailRespDTO;
import cn.iocoder.yudao.module.trade.controller.admin.order.vo.*;
import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderDeliveryReqVO;
import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderDetailRespVO;
import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderPageItemRespVO;
import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderPageReqVO;
import cn.iocoder.yudao.module.trade.convert.order.TradeOrderConvert;
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
@@ -41,8 +42,6 @@ public class TradeOrderController {
@Resource
private TradeOrderQueryService tradeOrderQueryService;
@Resource
private ProductPropertyValueApi productPropertyValueApi;
@Resource
private MemberUserApi memberUserApi;
@@ -61,11 +60,8 @@ public class TradeOrderController {
// 查询订单项
List<TradeOrderItemDO> orderItems = tradeOrderQueryService.getOrderItemListByOrderId(
convertSet(pageResult.getList(), TradeOrderDO::getId));
// 查询商品属性
List<ProductPropertyValueDetailRespDTO> propertyValueDetails = productPropertyValueApi
.getPropertyValueDetailList(TradeOrderConvert.INSTANCE.convertPropertyValueIds(orderItems));
// 最终组合
return success(TradeOrderConvert.INSTANCE.convertPage(pageResult, orderItems, propertyValueDetails, userMap));
return success(TradeOrderConvert.INSTANCE.convertPage(pageResult, orderItems, userMap));
}
@GetMapping("/get-detail")
@@ -77,13 +73,10 @@ public class TradeOrderController {
TradeOrderDO order = tradeOrderQueryService.getOrder(id);
// 查询订单项
List<TradeOrderItemDO> orderItems = tradeOrderQueryService.getOrderItemListByOrderId(id);
// 查询商品属性
List<ProductPropertyValueDetailRespDTO> propertyValueDetails = productPropertyValueApi
.getPropertyValueDetailList(TradeOrderConvert.INSTANCE.convertPropertyValueIds(orderItems));
// 查询会员
// 拼接数据
MemberUserRespDTO user = memberUserApi.getUser(order.getUserId());
// 最终组合
return success(TradeOrderConvert.INSTANCE.convert(order, orderItems, propertyValueDetails, user));
return success(TradeOrderConvert.INSTANCE.convert(order, orderItems, user));
}
@PostMapping("/delivery")

View File

@@ -1,12 +1,14 @@
package cn.iocoder.yudao.module.trade.controller.app.aftersale;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppTradeAfterSaleCreateReqVO;
import cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppTradeAfterSaleDeliveryReqVO;
import cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppTradeAfterSalePageItemRespVO;
import cn.iocoder.yudao.module.trade.controller.app.base.property.AppProductPropertyValueDetailRespVO;
import cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppTradeAfterSaleRespVO;
import cn.iocoder.yudao.module.trade.convert.aftersale.TradeAfterSaleConvert;
import cn.iocoder.yudao.module.trade.enums.aftersale.AfterSaleOperateTypeEnum;
import cn.iocoder.yudao.module.trade.enums.aftersale.TradeAfterSaleWayEnum;
import cn.iocoder.yudao.module.trade.framework.aftersalelog.core.annotations.AfterSaleLog;
import cn.iocoder.yudao.module.trade.service.aftersale.TradeAfterSaleService;
import io.swagger.v3.oas.annotations.Operation;
@@ -17,8 +19,9 @@ import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
@@ -33,49 +36,35 @@ public class AppTradeAfterSaleController {
@Resource
private TradeAfterSaleService afterSaleService;
// TODO 芋艿:待实现
@GetMapping(value = "/page")
@Operation(summary = "获得售后分页")
public CommonResult<PageResult<AppTradeAfterSalePageItemRespVO>> getAfterSalePage() {
AppTradeAfterSalePageItemRespVO vo = new AppTradeAfterSalePageItemRespVO();
vo.setId(1L);
vo.setNo("1146347329394184195");
vo.setStatus(61);
vo.setWay(10);
vo.setType(10);
vo.setApplyReason("不想要了");
vo.setApplyDescription("这个商品我不喜欢,想退款");
vo.setApplyPicUrls(Arrays.asList("pic_url_1", "pic_url_2", "pic_url_3"));
public CommonResult<PageResult<AppTradeAfterSaleRespVO>> getAfterSalePage(PageParam pageParam) {
return success(TradeAfterSaleConvert.INSTANCE.convertPage02(
afterSaleService.getAfterSalePage(getLoginUserId(), pageParam)));
}
// 设置订单相关信息
vo.setOrderId(2001L);
vo.setOrderNo("23456789009876");
vo.setOrderItemId(3001L);
vo.setSpuId(4001L);
vo.setSpuName("商品名");
vo.setSkuId(5001L);
vo.setProperties(Arrays.asList(
new AppProductPropertyValueDetailRespVO().setPropertyId(6001L).setPropertyName("颜色").setValueId(7001L).setValueName("红色"),
new AppProductPropertyValueDetailRespVO().setPropertyId(6002L).setPropertyName("尺寸").setValueId(7002L).setValueName("XL")));
vo.setPicUrl("https://cdn.pixabay.com/photo/2022/12/06/06/21/lavender-7638368_1280.jpg");
vo.setCount(2);
@GetMapping(value = "/get")
@Operation(summary = "获得售后订单")
@Parameter(name = "id", description = "售后编号", required = true, example = "1")
public CommonResult<AppTradeAfterSaleRespVO> getAfterSale(@RequestParam("id") Long id) {
return success(TradeAfterSaleConvert.INSTANCE.convert(afterSaleService.getAfterSale(getLoginUserId(), id)));
}
// 设置审批相关信息
vo.setAuditReason("审核通过");
@GetMapping(value = "/get-applying-count")
@Operation(summary = "获得进行中的售后订单数量")
public CommonResult<Long> getApplyingAfterSaleCount() {
return success(afterSaleService.getApplyingAfterSaleCount(getLoginUserId()));
}
// 设置退款相关信息
vo.setRefundPrice(1000);
vo.setRefundTime(LocalDateTime.now());
// 设置退货相关信息
vo.setLogisticsId(7001L);
vo.setLogisticsNo("LAGN101010101001");
vo.setDeliveryTime(LocalDateTime.now());
vo.setReceiveTime(LocalDateTime.now());
vo.setReceiveReason("收货正常");
return success(new PageResult<>(Arrays.asList(vo), 1L));
// return success(afterSaleService.getAfterSalePage(getLoginUserId()));
// TODO 芋艿:待实现
@GetMapping(value = "/get-reason-list")
@Operation(summary = "获得售后原因")
@Parameter(name = "way", description = "售后类型", required = true, example = "10")
public CommonResult<List<String>> getAfterSaleReasonList(@RequestParam("way") Integer way) {
if (Objects.equals(TradeAfterSaleWayEnum.REFUND.getWay(), way)) {
return success(Arrays.asList("不想要了", "商品质量问题", "商品描述不符"));
}
return success(Arrays.asList("不想要了", "商品质量问题", "商品描述不符", "商品错发漏发", "商品包装破损"));
}
@PostMapping(value = "/create")
@@ -85,7 +74,7 @@ public class AppTradeAfterSaleController {
return success(afterSaleService.createAfterSale(getLoginUserId(), createReqVO));
}
@PostMapping(value = "/delivery")
@PutMapping(value = "/delivery")
@Operation(summary = "退回货物")
public CommonResult<Boolean> deliveryAfterSale(@RequestBody AppTradeAfterSaleDeliveryReqVO deliveryReqVO) {
afterSaleService.deliveryAfterSale(getLoginUserId(), deliveryReqVO);

View File

@@ -3,9 +3,7 @@ package cn.iocoder.yudao.module.trade.controller.app.aftersale.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.time.LocalDateTime;
@Schema(description = "用户 App - 交易售后退回货物 Request VO")
@Data
@@ -23,8 +21,4 @@ public class AppTradeAfterSaleDeliveryReqVO {
@NotNull(message = "退货物流单号不能为空")
private String logisticsNo;
@Schema(description = "退货时间", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "退货时间不能为空")
private LocalDateTime deliveryTime;
}

View File

@@ -7,9 +7,9 @@ import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
@Schema(description = "用户 App - 交易售后的分页项 Request VO")
@Schema(description = "用户 App - 交易售后 Response VO")
@Data
public class AppTradeAfterSalePageItemRespVO {
public class AppTradeAfterSaleRespVO {
@Schema(description = "售后编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long id;

View File

@@ -0,0 +1,39 @@
package cn.iocoder.yudao.module.trade.controller.app.delivery;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.module.trade.controller.app.delivery.vo.express.AppDeliveryExpressRespVO;
import cn.iocoder.yudao.module.trade.convert.delivery.DeliveryExpressConvert;
import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressDO;
import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressService;
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 javax.annotation.Resource;
import java.util.Comparator;
import java.util.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@Tag(name = "用户 App - 快递公司")
@RestController
@RequestMapping("/trade/delivery/express")
@Validated
public class AppDeliverExpressController {
@Resource
private DeliveryExpressService deliveryExpressService;
@GetMapping("/list")
@Operation(summary = "获得快递公司列表")
public CommonResult<List<AppDeliveryExpressRespVO>> getDeliveryExpressList() {
List<DeliveryExpressDO> list = deliveryExpressService.getDeliveryExpressList(CommonStatusEnum.ENABLE.getStatus());
list.sort(Comparator.comparing(DeliveryExpressDO::getSort));
return success(DeliveryExpressConvert.INSTANCE.convertList03(list));
}
}

View File

@@ -0,0 +1,16 @@
package cn.iocoder.yudao.module.trade.controller.app.delivery.vo.express;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "用户 App - 快递公司 Response VO")
@Data
public class AppDeliveryExpressRespVO {
@Schema(description = "编号", required = true, example = "1")
private Long id;
@Schema(description = "门店名称", required = true, example = "顺丰")
private String name;
}

View File

@@ -118,11 +118,8 @@ public class AppTradeOrderController {
// 查询订单项
List<TradeOrderItemDO> orderItems = tradeOrderQueryService.getOrderItemListByOrderId(
convertSet(pageResult.getList(), TradeOrderDO::getId));
// 查询商品属性
List<ProductPropertyValueDetailRespDTO> propertyValueDetails = productPropertyValueApi
.getPropertyValueDetailList(TradeOrderConvert.INSTANCE.convertPropertyValueIds(orderItems));
// 最终组合
return success(TradeOrderConvert.INSTANCE.convertPage02(pageResult, orderItems, propertyValueDetails));
return success(TradeOrderConvert.INSTANCE.convertPage02(pageResult, orderItems));
}
@GetMapping("/get-count")

View File

@@ -43,12 +43,18 @@ public class AppTradeOrderItemRespVO {
@Schema(description = "商品原价(单)", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
private Integer price;
@Schema(description = "应付金额(总),单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "50")
private Integer payPrice;
// ========== 营销基本信息 ==========
// TODO 芋艿:在捉摸一下
// ========== 售后基本信息 ==========
@Schema(description = "售后编号", example = "1024")
private Long afterSaleId;
@Schema(description = "售后状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer afterSaleStatus;

View File

@@ -1,6 +1,5 @@
package cn.iocoder.yudao.module.trade.convert.aftersale;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundCreateReqDTO;
@@ -9,6 +8,7 @@ import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.TradeAfterSal
import cn.iocoder.yudao.module.trade.controller.admin.base.member.user.MemberUserRespVO;
import cn.iocoder.yudao.module.trade.controller.admin.base.product.property.ProductPropertyValueDetailRespVO;
import cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppTradeAfterSaleCreateReqVO;
import cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppTradeAfterSaleRespVO;
import cn.iocoder.yudao.module.trade.dal.dataobject.aftersale.TradeAfterSaleDO;
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties;
@@ -17,10 +17,7 @@ import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;
import java.util.*;
import java.util.stream.Collectors;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
import java.util.Map;
@Mapper
public interface TradeAfterSaleConvert {
@@ -37,6 +34,8 @@ public interface TradeAfterSaleConvert {
TradeAfterSaleDO convert(AppTradeAfterSaleCreateReqVO createReqVO, TradeOrderItemDO tradeOrderItem);
@Mappings({
@Mapping(source = "afterSale.orderId", target = "merchantOrderId"),
@Mapping(source = "afterSale.id", target = "merchantRefundId"),
@Mapping(source = "afterSale.applyReason", target = "reason"),
@Mapping(source = "afterSale.refundPrice", target = "price")
})
@@ -48,41 +47,18 @@ public interface TradeAfterSaleConvert {
PageResult<TradeAfterSaleRespPageItemVO> convertPage(PageResult<TradeAfterSaleDO> page);
default PageResult<TradeAfterSaleRespPageItemVO> convertPage(PageResult<TradeAfterSaleDO> pageResult,
Map<Long, MemberUserRespDTO> memberUsers, List<ProductPropertyValueDetailRespDTO> propertyValueDetails) {
PageResult<TradeAfterSaleRespPageItemVO> pageVOResult = convertPage(pageResult);
// 处理会员 + 商品属性等关联信息
Map<Long, ProductPropertyValueDetailRespDTO> propertyValueDetailMap = convertMap(propertyValueDetails, ProductPropertyValueDetailRespDTO::getValueId);
for (int i = 0; i < pageResult.getList().size(); i++) {
TradeAfterSaleRespPageItemVO afterSaleVO = pageVOResult.getList().get(i);
TradeAfterSaleDO afterSaleDO = pageResult.getList().get(i);
// 会员
afterSaleVO.setUser(convert(memberUsers.get(afterSaleDO.getUserId())));
// 商品属性
if (CollUtil.isNotEmpty(afterSaleDO.getProperties())) {
afterSaleVO.setProperties(new ArrayList<>(afterSaleDO.getProperties().size()));
// 遍历每个 properties设置到 TradeOrderPageItemRespVO.Item 中
afterSaleDO.getProperties().forEach(property -> {
ProductPropertyValueDetailRespDTO propertyValueDetail = propertyValueDetailMap.get(property.getValueId());
if (propertyValueDetail == null) {
return;
}
afterSaleVO.getProperties().add(convert(propertyValueDetail));
});
}
}
return pageVOResult;
Map<Long, MemberUserRespDTO> memberUsers) {
PageResult<TradeAfterSaleRespPageItemVO> voPageResult = convertPage(pageResult);
// 处理会员
voPageResult.getList().forEach(afterSale -> afterSale.setUser(
convert(memberUsers.get(afterSale.getUserId()))));
return voPageResult;
}
ProductPropertyValueDetailRespVO convert(ProductPropertyValueDetailRespDTO bean);
default Set<Long> convertPropertyValueIds(List<TradeAfterSaleDO> list) {
if (CollUtil.isEmpty(list)) {
return new HashSet<>();
}
return list.stream().filter(item -> item.getProperties() != null)
.flatMap(p -> p.getProperties().stream()) // 遍历多个 Property 属性
.map(TradeOrderItemDO.Property::getValueId) // 将每个 Property 转换成对应的 propertyId最后形成集合
.collect(Collectors.toSet());
}
AppTradeAfterSaleRespVO convert(TradeAfterSaleDO bean);
PageResult<AppTradeAfterSaleRespVO> convertPage02(PageResult<TradeAfterSaleDO> page);
}

View File

@@ -2,6 +2,11 @@ package cn.iocoder.yudao.module.trade.convert.delivery;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.express.*;
import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.express.DeliveryExpressCreateReqVO;
import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.express.DeliveryExpressExcelVO;
import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.express.DeliveryExpressRespVO;
import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.express.DeliveryExpressUpdateReqVO;
import cn.iocoder.yudao.module.trade.controller.app.delivery.vo.express.AppDeliveryExpressRespVO;
import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressDO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
@@ -26,4 +31,6 @@ public interface DeliveryExpressConvert {
List<DeliveryExpressExcelVO> convertList02(List<DeliveryExpressDO> list);
List<DeliveryExpressSimpleRespVO> convertList1(List<DeliveryExpressDO> list);
List<AppDeliveryExpressRespVO> convertList03(List<DeliveryExpressDO> list);
}

View File

@@ -14,7 +14,6 @@ import cn.iocoder.yudao.module.product.api.comment.dto.ProductCommentCreateReqDT
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.combination.dto.CombinationRecordCreateReqDTO;
import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateReqDTO;
import cn.iocoder.yudao.module.trade.api.order.dto.TradeOrderRespDTO;
import cn.iocoder.yudao.module.trade.controller.admin.base.member.user.MemberUserRespVO;
import cn.iocoder.yudao.module.trade.controller.admin.base.product.property.ProductPropertyValueDetailRespVO;
@@ -84,9 +83,6 @@ public interface TradeOrderConvert {
TradeOrderItemDO convert(TradePriceCalculateRespBO.OrderItem item);
@Mapping(source = "userId", target = "userId")
PriceCalculateReqDTO convert(AppTradeOrderCreateReqVO createReqVO, Long userId);
@Mappings({
@Mapping(source = "skuId", target = "id"),
@Mapping(source = "count", target = "incrCount"),
@@ -123,38 +119,18 @@ public interface TradeOrderConvert {
}
// TODO 芋艿:可简化
default PageResult<TradeOrderPageItemRespVO> convertPage(PageResult<TradeOrderDO> pageResult, List<TradeOrderItemDO> orderItems,
List<ProductPropertyValueDetailRespDTO> propertyValueDetails,
Map<Long, MemberUserRespDTO> memberUserRespDTOMap) {
default PageResult<TradeOrderPageItemRespVO> convertPage(PageResult<TradeOrderDO> pageResult,
List<TradeOrderItemDO> orderItems,
Map<Long, MemberUserRespDTO> memberUserMap) {
Map<Long, List<TradeOrderItemDO>> orderItemMap = convertMultiMap(orderItems, TradeOrderItemDO::getOrderId);
Map<Long, ProductPropertyValueDetailRespDTO> propertyValueDetailMap = convertMap(propertyValueDetails, ProductPropertyValueDetailRespDTO::getValueId);
// 转化 List
List<TradeOrderPageItemRespVO> orderVOs = CollectionUtils.convertList(pageResult.getList(), order -> {
List<TradeOrderItemDO> xOrderItems = orderItemMap.get(order.getId());
TradeOrderPageItemRespVO orderVO = convert(order, xOrderItems);
if (CollUtil.isNotEmpty(xOrderItems)) {
// 处理商品属性
for (int i = 0; i < xOrderItems.size(); i++) {
List<TradeOrderItemDO.Property> properties = xOrderItems.get(i).getProperties();
if (CollUtil.isEmpty(properties)) {
continue;
}
TradeOrderPageItemRespVO.Item item = orderVO.getItems().get(i);
item.setProperties(new ArrayList<>(properties.size()));
// 遍历每个 properties设置到 TradeOrderPageItemRespVO.Item 中
properties.forEach(property -> {
ProductPropertyValueDetailRespDTO propertyValueDetail = propertyValueDetailMap.get(property.getValueId());
if (propertyValueDetail == null) {
return;
}
item.getProperties().add(convert(propertyValueDetail));
});
}
}
// 处理收货地址
orderVO.setReceiverAreaName(AreaUtils.format(order.getReceiverAreaId()));
// 增加用户昵称
orderVO.setUser(memberUserRespDTOMap.get(orderVO.getUserId()));
orderVO.setUser(memberUserMap.get(orderVO.getUserId()));
return orderVO;
});
return new PageResult<>(orderVOs, pageResult.getTotal());
@@ -164,28 +140,9 @@ public interface TradeOrderConvert {
ProductPropertyValueDetailRespVO convert(ProductPropertyValueDetailRespDTO bean);
// TODO 芋艿:可简化
default TradeOrderDetailRespVO convert(TradeOrderDO order, List<TradeOrderItemDO> orderItems,
List<ProductPropertyValueDetailRespDTO> propertyValueDetails, MemberUserRespDTO user) {
MemberUserRespDTO user) {
TradeOrderDetailRespVO orderVO = convert2(order, orderItems);
// 处理商品属性
Map<Long, ProductPropertyValueDetailRespDTO> propertyValueDetailMap = convertMap(propertyValueDetails, ProductPropertyValueDetailRespDTO::getValueId);
for (int i = 0; i < orderItems.size(); i++) {
List<TradeOrderItemDO.Property> properties = orderItems.get(i).getProperties();
if (CollUtil.isEmpty(properties)) {
continue;
}
TradeOrderDetailRespVO.Item item = orderVO.getItems().get(i);
item.setProperties(new ArrayList<>(properties.size()));
// 遍历每个 properties设置到 TradeOrderPageItemRespVO.Item 中
properties.forEach(property -> {
ProductPropertyValueDetailRespDTO propertyValueDetail = propertyValueDetailMap.get(property.getValueId());
if (propertyValueDetail == null) {
return;
}
item.getProperties().add(convert(propertyValueDetail));
});
}
// 处理收货地址
orderVO.setReceiverAreaName(AreaUtils.format(order.getReceiverAreaId()));
// 处理用户信息
@@ -197,35 +154,13 @@ public interface TradeOrderConvert {
MemberUserRespVO convert(MemberUserRespDTO bean);
// TODO 芋艿:可简化
default PageResult<AppTradeOrderPageItemRespVO> convertPage02(PageResult<TradeOrderDO> pageResult, List<TradeOrderItemDO> orderItems,
List<ProductPropertyValueDetailRespDTO> propertyValueDetails) {
default PageResult<AppTradeOrderPageItemRespVO> convertPage02(PageResult<TradeOrderDO> pageResult,
List<TradeOrderItemDO> orderItems) {
Map<Long, List<TradeOrderItemDO>> orderItemMap = convertMultiMap(orderItems, TradeOrderItemDO::getOrderId);
Map<Long, ProductPropertyValueDetailRespDTO> propertyValueDetailMap = convertMap(propertyValueDetails, ProductPropertyValueDetailRespDTO::getValueId);
// 转化 List
List<AppTradeOrderPageItemRespVO> orderVOs = CollectionUtils.convertList(pageResult.getList(), order -> {
List<TradeOrderItemDO> xOrderItems = orderItemMap.get(order.getId());
AppTradeOrderPageItemRespVO orderVO = convert02(order, xOrderItems);
if (CollUtil.isNotEmpty(xOrderItems)) {
// 处理商品属性
for (int i = 0; i < xOrderItems.size(); i++) {
List<TradeOrderItemDO.Property> properties = xOrderItems.get(i).getProperties();
if (CollUtil.isEmpty(properties)) {
continue;
}
AppTradeOrderItemRespVO item = orderVO.getItems().get(i);
item.setProperties(new ArrayList<>(properties.size()));
// 遍历每个 properties设置到 TradeOrderPageItemRespVO.Item 中
properties.forEach(property -> {
ProductPropertyValueDetailRespDTO propertyValueDetail = propertyValueDetailMap.get(property.getValueId());
if (propertyValueDetail == null) {
return;
}
item.getProperties().add(convert02(propertyValueDetail));
});
}
}
return orderVO;
return convert02(order, xOrderItems);
});
return new PageResult<>(orderVOs, pageResult.getTotal());
}

View File

@@ -17,7 +17,7 @@ import java.time.LocalDateTime;
import java.util.List;
/**
* 交易售后,用于处理 {@link TradeOrderDO} 交易订单的退款退货流程
* 售后订单,用于处理 {@link TradeOrderDO} 交易订单的退款退货流程
*
* @author 芋道源码
*/
@@ -32,7 +32,7 @@ public class TradeAfterSaleDO extends BaseDO {
*/
private Long id;
/**
* 售后流水
* 售后
*
* 例如说1146347329394184195
*/

View File

@@ -145,12 +145,17 @@ public class TradeOrderItemDO extends BaseDO {
private Integer pointPrice;
// ========== 售后基本信息 ==========
/**
* 售后单编号
*
* 关联 {@link TradeAfterSaleDO#getId()} 字段
*/
private Long afterSaleId;
/**
* 售后状态
*
* 枚举 {@link TradeOrderItemAfterSaleStatusEnum}
*
* @see TradeAfterSaleDO
*/
private Integer afterSaleStatus;

View File

@@ -1,5 +1,6 @@
package cn.iocoder.yudao.module.trade.dal.mysql.aftersale;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
@@ -8,6 +9,8 @@ import cn.iocoder.yudao.module.trade.dal.dataobject.aftersale.TradeAfterSaleDO;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import org.apache.ibatis.annotations.Mapper;
import java.util.Collection;
@Mapper
public interface TradeAfterSaleMapper extends BaseMapperX<TradeAfterSaleDO> {
@@ -23,13 +26,26 @@ public interface TradeAfterSaleMapper extends BaseMapperX<TradeAfterSaleDO> {
.orderByDesc(TradeAfterSaleDO::getId));
}
default PageResult<TradeAfterSaleDO> selectPage(Long userId, PageParam pageParam) {
return selectPage(pageParam, new LambdaQueryWrapperX<TradeAfterSaleDO>()
.eqIfPresent(TradeAfterSaleDO::getUserId, userId)
.orderByDesc(TradeAfterSaleDO::getId));
}
default int updateByIdAndStatus(Long id, Integer status, TradeAfterSaleDO update) {
return update(update, new LambdaUpdateWrapper<TradeAfterSaleDO>()
.eq(TradeAfterSaleDO::getId, id).eq(TradeAfterSaleDO::getStatus, status));
}
default TradeAfterSaleDO selectByPayRefundId(Long payRefundId) {
return selectOne(TradeAfterSaleDO::getPayRefundId, payRefundId);
default TradeAfterSaleDO selectByIdAndUserId(Long id, Long userId) {
return selectOne(TradeAfterSaleDO::getId, id,
TradeAfterSaleDO::getUserId, userId);
}
default Long selectCountByUserIdAndStatus(Long userId, Collection<Integer> statuses) {
return selectCount(new LambdaQueryWrapperX<TradeAfterSaleDO>()
.eq(TradeAfterSaleDO::getUserId, userId)
.in(TradeAfterSaleDO::getStatus, statuses));
}
}

View File

@@ -12,8 +12,9 @@ import java.util.List;
@Mapper
public interface TradeOrderItemMapper extends BaseMapperX<TradeOrderItemDO> {
default int updateAfterSaleStatus(Long id, Integer oldAfterSaleStatus, Integer newAfterSaleStatus) {
return update(new TradeOrderItemDO().setAfterSaleStatus(newAfterSaleStatus),
default int updateAfterSaleStatus(Long id, Integer oldAfterSaleStatus, Integer newAfterSaleStatus,
Long afterSaleId) {
return update(new TradeOrderItemDO().setAfterSaleStatus(newAfterSaleStatus).setAfterSaleId(afterSaleId),
new LambdaUpdateWrapper<>(new TradeOrderItemDO().setId(id).setAfterSaleStatus(oldAfterSaleStatus)));
}

View File

@@ -1,5 +1,6 @@
package cn.iocoder.yudao.module.trade.service.aftersale;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.TradeAfterSaleDisagreeReqVO;
import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.TradeAfterSalePageReqVO;
@@ -9,41 +10,57 @@ import cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppTradeAfterSa
import cn.iocoder.yudao.module.trade.dal.dataobject.aftersale.TradeAfterSaleDO;
/**
* 交易售后 Service 接口
* 售后订单 Service 接口
*
* @author 芋道源码
*/
public interface TradeAfterSaleService {
/**
* 获得交易售后分页
* 【管理员】获得售后订单分页
*
* @param pageReqVO 分页查询
* @return 交易售后分页
* @return 售后订单分页
*/
PageResult<TradeAfterSaleDO> getAfterSalePage(TradeAfterSalePageReqVO pageReqVO);
/**
* 【会员】创建交易售后
* <p>
* 一般是用户发起售后请求
* 【会员】获得售后订单分页
*
* @param userId 用户编号
* @param pageParam 分页参数
* @return 售后订单分页
*/
PageResult<TradeAfterSaleDO> getAfterSalePage(Long userId, PageParam pageParam);
/**
* 【会员】获得售后单
*
* @param userId 用户编号
* @param id 售后编号
* @return 售后订单
*/
TradeAfterSaleDO getAfterSale(Long userId, Long id);
/**
* 【会员】创建售后订单
*
* @param userId 会员用户编号
* @param createReqVO 创建 Request 信息
* @return 交易售后编号
* @return 售后编号
*/
Long createAfterSale(Long userId, AppTradeAfterSaleCreateReqVO createReqVO);
/**
* 【管理员】同意交易售后
* 【管理员】同意售后订单
*
* @param userId 管理员用户编号
* @param id 交易售后编号
* @param id 售后编号
*/
void agreeAfterSale(Long userId, Long id);
/**
* 【管理员】拒绝交易售后
* 【管理员】拒绝售后订单
*
* @param userId 管理员用户编号
* @param auditReqVO 审批 Request 信息
@@ -62,7 +79,7 @@ public interface TradeAfterSaleService {
* 【管理员】确认收货
*
* @param userId 管理员编号
* @param id 交易售后编号
* @param id 售后编号
*/
void receiveAfterSale(Long userId, Long id);
@@ -87,8 +104,16 @@ public interface TradeAfterSaleService {
* 【会员】取消售后
*
* @param userId 会员用户编号
* @param id 交易售后编号
* @param id 售后编号
*/
void cancelAfterSale(Long userId, Long id);
/**
* 【会员】获得正在进行中的售后订单数量
*
* @param userId
* @return 数量
*/
Long getApplyingAfterSaleCount(Long userId);
}

View File

@@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.trade.service.aftersale;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
import cn.iocoder.yudao.module.pay.api.refund.PayRefundApi;
@@ -45,7 +46,7 @@ import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString
import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.*;
/**
* 交易售后 Service 实现类
* 售后订单 Service 实现类
*
* @author 芋道源码
*/
@@ -75,13 +76,23 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService, AfterSa
return tradeAfterSaleMapper.selectPage(pageReqVO);
}
@Override
public PageResult<TradeAfterSaleDO> getAfterSalePage(Long userId, PageParam pageParam) {
return tradeAfterSaleMapper.selectPage(userId, pageParam);
}
@Override
public TradeAfterSaleDO getAfterSale(Long userId, Long id) {
return tradeAfterSaleMapper.selectByIdAndUserId(id, userId);
}
@Override
@Transactional(rollbackFor = Exception.class)
public Long createAfterSale(Long userId, AppTradeAfterSaleCreateReqVO createReqVO) {
// 第一步,前置校验
TradeOrderItemDO tradeOrderItem = validateOrderItemApplicable(userId, createReqVO);
// 第二步,存储交易售后
// 第二步,存储售后订单
TradeAfterSaleDO afterSale = createAfterSale(createReqVO, tradeOrderItem);
return afterSale.getId();
}
@@ -148,8 +159,8 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService, AfterSa
// 更新交易订单项的售后状态
tradeOrderUpdateService.updateOrderItemAfterSaleStatus(orderItem.getId(),
TradeOrderItemAfterSaleStatusEnum.NONE.getStatus(),
TradeOrderItemAfterSaleStatusEnum.APPLY.getStatus(), null);
TradeOrderItemAfterSaleStatusEnum.NONE.getStatus(), TradeOrderItemAfterSaleStatusEnum.APPLY.getStatus(),
afterSale.getId(), null);
// 记录售后日志
createAfterSaleLog(orderItem.getUserId(), UserTypeEnum.MEMBER.getValue(),
@@ -168,7 +179,7 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService, AfterSa
// 更新售后单的状态
// 情况一:退款:标记为 WAIT_REFUND 状态。后续等退款发起成功后,在标记为 COMPLETE 状态
// 情况二:退货退款:需要等用户退货后,才能发起退款
Integer newStatus = afterSale.getType().equals(TradeAfterSaleWayEnum.REFUND.getWay()) ?
Integer newStatus = afterSale.getWay().equals(TradeAfterSaleWayEnum.REFUND.getWay()) ?
TradeAfterSaleStatusEnum.WAIT_REFUND.getStatus() : TradeAfterSaleStatusEnum.SELLER_AGREE.getStatus();
updateAfterSaleStatus(afterSale.getId(), TradeAfterSaleStatusEnum.APPLY.getStatus(), new TradeAfterSaleDO()
.setStatus(newStatus).setAuditUserId(userId).setAuditTime(LocalDateTime.now()));
@@ -200,8 +211,7 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService, AfterSa
// 更新交易订单项的售后状态为【未申请】
tradeOrderUpdateService.updateOrderItemAfterSaleStatus(afterSale.getOrderItemId(),
TradeOrderItemAfterSaleStatusEnum.APPLY.getStatus(),
TradeOrderItemAfterSaleStatusEnum.NONE.getStatus(), null);
TradeOrderItemAfterSaleStatusEnum.APPLY.getStatus(), TradeOrderItemAfterSaleStatusEnum.NONE.getStatus());
}
/**
@@ -244,7 +254,7 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService, AfterSa
updateAfterSaleStatus(afterSale.getId(), TradeAfterSaleStatusEnum.SELLER_AGREE.getStatus(), new TradeAfterSaleDO()
.setStatus(TradeAfterSaleStatusEnum.BUYER_DELIVERY.getStatus())
.setLogisticsId(deliveryReqVO.getLogisticsId()).setLogisticsNo(deliveryReqVO.getLogisticsNo())
.setDeliveryTime(deliveryReqVO.getDeliveryTime()));
.setDeliveryTime(LocalDateTime.now()));
// 记录售后日志
createAfterSaleLog(userId, UserTypeEnum.MEMBER.getValue(),
@@ -295,8 +305,7 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService, AfterSa
// 更新交易订单项的售后状态为【未申请】
tradeOrderUpdateService.updateOrderItemAfterSaleStatus(afterSale.getOrderItemId(),
TradeOrderItemAfterSaleStatusEnum.APPLY.getStatus(),
TradeOrderItemAfterSaleStatusEnum.NONE.getStatus(), null);
TradeOrderItemAfterSaleStatusEnum.APPLY.getStatus(), TradeOrderItemAfterSaleStatusEnum.NONE.getStatus());
}
/**
@@ -320,7 +329,7 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService, AfterSa
@Transactional(rollbackFor = Exception.class)
public void refundAfterSale(Long userId, String userIp, Long id) {
// 校验售后单的状态,并状态待退款
TradeAfterSaleDO afterSale = tradeAfterSaleMapper.selectByPayRefundId(id);
TradeAfterSaleDO afterSale = tradeAfterSaleMapper.selectById(id);
if (afterSale == null) {
throw exception(AFTER_SALE_NOT_FOUND);
}
@@ -343,8 +352,8 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService, AfterSa
// 更新交易订单项的售后状态为【已完成】
tradeOrderUpdateService.updateOrderItemAfterSaleStatus(afterSale.getOrderItemId(),
TradeOrderItemAfterSaleStatusEnum.APPLY.getStatus(),
TradeOrderItemAfterSaleStatusEnum.SUCCESS.getStatus(), afterSale.getRefundPrice());
TradeOrderItemAfterSaleStatusEnum.APPLY.getStatus(), TradeOrderItemAfterSaleStatusEnum.SUCCESS.getStatus(),
null, afterSale.getRefundPrice());
}
private void createPayRefund(String userIp, TradeAfterSaleDO afterSale) {
@@ -364,13 +373,14 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService, AfterSa
@Override
public void cancelAfterSale(Long userId, Long id) {
// 校验售后单的状态,并状态待退款
TradeAfterSaleDO afterSale = tradeAfterSaleMapper.selectByPayRefundId(id);
TradeAfterSaleDO afterSale = tradeAfterSaleMapper.selectById(id);
if (afterSale == null) {
throw exception(AFTER_SALE_NOT_FOUND);
}
if (ObjectUtils.equalsAny(afterSale.getStatus(), TradeAfterSaleStatusEnum.APPLY.getStatus(),
TradeAfterSaleStatusEnum.SELLER_AGREE.getStatus())) {
throw exception(AFTER_SALE_CANCEL_FAIL_STATUS_NOT_APPLY_OR_AGREE);
if (!ObjectUtils.equalsAny(afterSale.getStatus(), TradeAfterSaleStatusEnum.APPLY.getStatus(),
TradeAfterSaleStatusEnum.SELLER_AGREE.getStatus(),
TradeAfterSaleStatusEnum.BUYER_DELIVERY.getStatus())) {
throw exception(AFTER_SALE_CANCEL_FAIL_STATUS_NOT_APPLY_OR_AGREE_OR_BUYER_DELIVERY);
}
// 更新售后单的状态为【已取消】
@@ -385,8 +395,12 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService, AfterSa
// 更新交易订单项的售后状态为【未申请】
tradeOrderUpdateService.updateOrderItemAfterSaleStatus(afterSale.getOrderItemId(),
TradeOrderItemAfterSaleStatusEnum.APPLY.getStatus(),
TradeOrderItemAfterSaleStatusEnum.NONE.getStatus(), null);
TradeOrderItemAfterSaleStatusEnum.APPLY.getStatus(), TradeOrderItemAfterSaleStatusEnum.NONE.getStatus());
}
@Override
public Long getApplyingAfterSaleCount(Long userId) {
return tradeAfterSaleMapper.selectCountByUserIdAndStatus(userId, TradeAfterSaleStatusEnum.APPLYING_STATUSES);
}
@Deprecated

View File

@@ -1,5 +1,8 @@
package cn.iocoder.yudao.module.trade.service.delivery;
import java.util.*;
import javax.validation.*;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.express.DeliveryExpressCreateReqVO;
import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.express.DeliveryExpressExportReqVO;
@@ -48,14 +51,6 @@ public interface DeliveryExpressService {
*/
DeliveryExpressDO getDeliveryExpress(Long id);
/**
* 获得快递公司列表
*
* @param ids 编号
* @return 快递公司列表
*/
List<DeliveryExpressDO> getDeliveryExpressList(Collection<Long> ids);
/**
* 获得快递公司分页
*

View File

@@ -85,11 +85,6 @@ public class DeliveryExpressServiceImpl implements DeliveryExpressService {
return deliveryExpressMapper.selectById(id);
}
@Override
public List<DeliveryExpressDO> getDeliveryExpressList(Collection<Long> ids) {
return deliveryExpressMapper.selectBatchIds(ids);
}
@Override
public PageResult<DeliveryExpressDO> getDeliveryExpressPage(DeliveryExpressPageReqVO pageReqVO) {
return deliveryExpressMapper.selectPage(pageReqVO);

View File

@@ -77,10 +77,22 @@ public interface TradeOrderUpdateService {
* @param id 交易订单项编号
* @param oldAfterSaleStatus 当前售后状态;如果不符,更新后会抛出异常
* @param newAfterSaleStatus 目标售后状态
*/
default void updateOrderItemAfterSaleStatus(Long id, Integer oldAfterSaleStatus, Integer newAfterSaleStatus) {
updateOrderItemAfterSaleStatus(id, oldAfterSaleStatus, newAfterSaleStatus, null, null);
}
/**
* 更新交易订单项的售后状态
*
* @param id 交易订单项编号
* @param oldAfterSaleStatus 当前售后状态;如果不符,更新后会抛出异常
* @param newAfterSaleStatus 目标售后状态
* @param afterSaleId 售后单编号;当订单项发起售后时,必须传递该字段
* @param refundPrice 退款金额;当订单项退款成功时,必须传递该值
*/
void updateOrderItemAfterSaleStatus(Long id, Integer oldAfterSaleStatus,
Integer newAfterSaleStatus, Integer refundPrice);
void updateOrderItemAfterSaleStatus(Long id, Integer oldAfterSaleStatus, Integer newAfterSaleStatus,
Long afterSaleId, Integer refundPrice);
/**
* 创建订单项的评论

View File

@@ -524,15 +524,22 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
// =================== Order Item ===================
@Override
public void updateOrderItemAfterSaleStatus(Long id, Integer oldAfterSaleStatus, Integer newAfterSaleStatus, Integer refundPrice) {
@Transactional(rollbackFor = Exception.class)
public void updateOrderItemAfterSaleStatus(Long id, Integer oldAfterSaleStatus, Integer newAfterSaleStatus,
Long afterSaleId, Integer refundPrice) {
// 如果退款成功,则 refundPrice 非空
if (Objects.equals(newAfterSaleStatus, TradeOrderItemAfterSaleStatusEnum.SUCCESS.getStatus())
&& refundPrice == null) {
throw new IllegalArgumentException(StrUtil.format("id({}) 退款成功,退款金额不能为空", id));
}
// 如果退款发起,则 afterSaleId 非空
if (Objects.equals(newAfterSaleStatus, TradeOrderItemAfterSaleStatusEnum.APPLY.getStatus())
&& afterSaleId == null) {
throw new IllegalArgumentException(StrUtil.format("id({}) 退款发起,售后单编号不能为空", id));
}
// 更新订单项
int updateCount = tradeOrderItemMapper.updateAfterSaleStatus(id, oldAfterSaleStatus, newAfterSaleStatus);
int updateCount = tradeOrderItemMapper.updateAfterSaleStatus(id, oldAfterSaleStatus, newAfterSaleStatus, afterSaleId);
if (updateCount <= 0) {
throw exception(ORDER_ITEM_UPDATE_AFTER_SALE_STATUS_FAIL);
}

View File

@@ -53,7 +53,9 @@ public class TradeAfterSaleServiceTest extends BaseDbUnitTest {
private TradeAfterSaleLogMapper tradeAfterSaleLogMapper;
@MockBean
private TradeOrderUpdateService tradeOrderService;
private TradeOrderUpdateService tradeOrderUpdateService;
@Resource
private TradeOrderQueryService tradeOrderQueryService;
@MockBean
private TradeOrderQueryService tradeOrderQueryService;
@MockBean