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

This commit is contained in:
owen
2023-09-29 21:08:49 +08:00
663 changed files with 3566 additions and 48238 deletions

View File

@@ -9,47 +9,47 @@ import cn.iocoder.yudao.framework.common.exception.ErrorCode;
*/
public interface ErrorCodeConstants {
// ========== 商品分类相关 1008001000 ============
ErrorCode CATEGORY_NOT_EXISTS = new ErrorCode(1008001000, "商品分类不存在");
ErrorCode CATEGORY_PARENT_NOT_EXISTS = new ErrorCode(1008001001, "父分类不存在");
ErrorCode CATEGORY_PARENT_NOT_FIRST_LEVEL = new ErrorCode(1008001002, "父分类不能是二级分类");
ErrorCode CATEGORY_EXISTS_CHILDREN = new ErrorCode(1008001003, "存在子分类,无法删除");
ErrorCode CATEGORY_DISABLED = new ErrorCode(1008001004, "商品分类({})已禁用,无法使用");
ErrorCode CATEGORY_HAVE_BIND_SPU = new ErrorCode(1008001005, "类别下存在商品,无法删除");
// ========== 商品分类相关 1-008-001-000 ============
ErrorCode CATEGORY_NOT_EXISTS = new ErrorCode(1_008_001_000, "商品分类不存在");
ErrorCode CATEGORY_PARENT_NOT_EXISTS = new ErrorCode(1_008_001_001, "父分类不存在");
ErrorCode CATEGORY_PARENT_NOT_FIRST_LEVEL = new ErrorCode(1_008_001_002, "父分类不能是二级分类");
ErrorCode CATEGORY_EXISTS_CHILDREN = new ErrorCode(1_008_001_003, "存在子分类,无法删除");
ErrorCode CATEGORY_DISABLED = new ErrorCode(1_008_001_004, "商品分类({})已禁用,无法使用");
ErrorCode CATEGORY_HAVE_BIND_SPU = new ErrorCode(1_008_001_005, "类别下存在商品,无法删除");
// ========== 商品品牌相关编号 1008002000 ==========
ErrorCode BRAND_NOT_EXISTS = new ErrorCode(1008002000, "品牌不存在");
ErrorCode BRAND_DISABLED = new ErrorCode(1008002001, "品牌已禁用");
ErrorCode BRAND_NAME_EXISTS = new ErrorCode(1008002002, "品牌名称已存在");
// ========== 商品品牌相关编号 1-008-002-000 ==========
ErrorCode BRAND_NOT_EXISTS = new ErrorCode(1_008_002_000, "品牌不存在");
ErrorCode BRAND_DISABLED = new ErrorCode(1_008_002_001, "品牌已禁用");
ErrorCode BRAND_NAME_EXISTS = new ErrorCode(1_008_002_002, "品牌名称已存在");
// ========== 商品属性项 1008003000 ==========
ErrorCode PROPERTY_NOT_EXISTS = new ErrorCode(1008003000, "属性项不存在");
ErrorCode PROPERTY_EXISTS = new ErrorCode(1008003001, "属性项的名称已存在");
ErrorCode PROPERTY_DELETE_FAIL_VALUE_EXISTS = new ErrorCode(1008003002, "属性项下存在属性值,无法删除");
// ========== 商品属性项 1-008-003-000 ==========
ErrorCode PROPERTY_NOT_EXISTS = new ErrorCode(1_008_003_000, "属性项不存在");
ErrorCode PROPERTY_EXISTS = new ErrorCode(1_008_003_001, "属性项的名称已存在");
ErrorCode PROPERTY_DELETE_FAIL_VALUE_EXISTS = new ErrorCode(1_008_003_002, "属性项下存在属性值,无法删除");
// ========== 商品属性值 1008004000 ==========
ErrorCode PROPERTY_VALUE_NOT_EXISTS = new ErrorCode(1008004000, "属性值不存在");
ErrorCode PROPERTY_VALUE_EXISTS = new ErrorCode(1008004001, "属性值的名称已存在");
// ========== 商品属性值 1-008-004-000 ==========
ErrorCode PROPERTY_VALUE_NOT_EXISTS = new ErrorCode(1_008_004_000, "属性值不存在");
ErrorCode PROPERTY_VALUE_EXISTS = new ErrorCode(1_008_004_001, "属性值的名称已存在");
// ========== 商品 SPU 1008005000 ==========
ErrorCode SPU_NOT_EXISTS = new ErrorCode(1008005000, "商品 SPU 不存在");
ErrorCode SPU_SAVE_FAIL_CATEGORY_LEVEL_ERROR = new ErrorCode(1008005001, "商品分类不正确,原因:必须使用第二级的商品分类及以下");
ErrorCode SPU_NOT_ENABLE = new ErrorCode(1008005002, "商品 SPU 不处于上架状态");
ErrorCode SPU_NOT_RECYCLE = new ErrorCode(1008005003, "商品 SPU 不处于回收站状态");
// ========== 商品 SPU 1-008-005-000 ==========
ErrorCode SPU_NOT_EXISTS = new ErrorCode(1_008_005_000, "商品 SPU 不存在");
ErrorCode SPU_SAVE_FAIL_CATEGORY_LEVEL_ERROR = new ErrorCode(1_008_005_001, "商品分类不正确,原因:必须使用第二级的商品分类及以下");
ErrorCode SPU_NOT_ENABLE = new ErrorCode(1_008_005_002, "商品 SPU 不处于上架状态");
ErrorCode SPU_NOT_RECYCLE = new ErrorCode(1_008_005_003, "商品 SPU 不处于回收站状态");
// ========== 商品 SKU 1008006000 ==========
ErrorCode SKU_NOT_EXISTS = new ErrorCode(1008006000, "商品 SKU 不存在");
ErrorCode SKU_PROPERTIES_DUPLICATED = new ErrorCode(1008006001, "商品 SKU 的属性组合存在重复");
ErrorCode SPU_ATTR_NUMBERS_MUST_BE_EQUALS = new ErrorCode(1008006002, "一个 SPU 下的每个 SKU其属性项必须一致");
ErrorCode SPU_SKU_NOT_DUPLICATE = new ErrorCode(1008006003, "一个 SPU 下的每个 SKU必须不重复");
ErrorCode SKU_STOCK_NOT_ENOUGH = new ErrorCode(1008006004, "商品 SKU 库存不足");
// ========== 商品 SKU 1-008-006-000 ==========
ErrorCode SKU_NOT_EXISTS = new ErrorCode(1_008_006_000, "商品 SKU 不存在");
ErrorCode SKU_PROPERTIES_DUPLICATED = new ErrorCode(1_008_006_001, "商品 SKU 的属性组合存在重复");
ErrorCode SPU_ATTR_NUMBERS_MUST_BE_EQUALS = new ErrorCode(1_008_006_002, "一个 SPU 下的每个 SKU其属性项必须一致");
ErrorCode SPU_SKU_NOT_DUPLICATE = new ErrorCode(1_008_006_003, "一个 SPU 下的每个 SKU必须不重复");
ErrorCode SKU_STOCK_NOT_ENOUGH = new ErrorCode(1_008_006_004, "商品 SKU 库存不足");
// ========== 商品 评价 1008007000 ==========
ErrorCode COMMENT_NOT_EXISTS = new ErrorCode(1008007000, "商品评价不存在");
ErrorCode COMMENT_ORDER_EXISTS = new ErrorCode(1008007001, "订单的商品评价已存在");
// ========== 商品 评价 1-008-007-000 ==========
ErrorCode COMMENT_NOT_EXISTS = new ErrorCode(1_008_007_000, "商品评价不存在");
ErrorCode COMMENT_ORDER_EXISTS = new ErrorCode(1_008_007_001, "订单的商品评价已存在");
// ========== 商品 收藏 1008008000 ==========
ErrorCode FAVORITE_EXISTS = new ErrorCode(1008008000, "该商品已经被收藏");
ErrorCode FAVORITE_NOT_EXISTS = new ErrorCode(1008008001, "商品收藏不存在");
// ========== 商品 收藏 1-008-008-000 ==========
ErrorCode FAVORITE_EXISTS = new ErrorCode(1_008_008_000, "该商品已经被收藏");
ErrorCode FAVORITE_NOT_EXISTS = new ErrorCode(1_008_008_001, "商品收藏不存在");
}

View File

@@ -140,4 +140,5 @@ public class AppProductSpuController {
return price - newPrice;
}
// TODO 芋艿:商品的浏览记录;
}

View File

@@ -16,7 +16,6 @@ import cn.iocoder.yudao.module.product.dal.mysql.comment.ProductCommentMapper;
import cn.iocoder.yudao.module.product.enums.comment.ProductCommentScoresEnum;
import cn.iocoder.yudao.module.product.service.sku.ProductSkuService;
import cn.iocoder.yudao.module.product.service.spu.ProductSpuService;
import cn.iocoder.yudao.module.trade.api.order.TradeOrderApi;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import;
@@ -48,8 +47,6 @@ public class ProductCommentServiceImplTest extends BaseDbUnitTest {
@Lazy
private ProductCommentServiceImpl productCommentService;
@MockBean
private TradeOrderApi tradeOrderApi;
@MockBean
private ProductSpuService productSpuService;
@MockBean

View File

@@ -9,47 +9,47 @@ import cn.iocoder.yudao.framework.common.exception.ErrorCode;
*/
public interface ErrorCodeConstants {
// ========== 促销活动相关 1013001000 ============
ErrorCode DISCOUNT_ACTIVITY_NOT_EXISTS = new ErrorCode(1013001000, "限时折扣活动不存在");
ErrorCode DISCOUNT_ACTIVITY_SPU_CONFLICTS = new ErrorCode(1013001001, "存在商品参加了其它限时折扣活动");
ErrorCode DISCOUNT_ACTIVITY_UPDATE_FAIL_STATUS_CLOSED = new ErrorCode(1013001002, "限时折扣活动已关闭,不能修改");
ErrorCode DISCOUNT_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED = new ErrorCode(1013001003, "限时折扣活动未关闭,不能删除");
ErrorCode DISCOUNT_ACTIVITY_CLOSE_FAIL_STATUS_CLOSED = new ErrorCode(1013001004, "限时折扣活动已关闭,不能重复关闭");
ErrorCode DISCOUNT_ACTIVITY_CLOSE_FAIL_STATUS_END = new ErrorCode(1013001005, "限时折扣活动已结束,不能关闭");
// ========== 促销活动相关 1-013-001-000 ============
ErrorCode DISCOUNT_ACTIVITY_NOT_EXISTS = new ErrorCode(1_013_001_000, "限时折扣活动不存在");
ErrorCode DISCOUNT_ACTIVITY_SPU_CONFLICTS = new ErrorCode(1_013_001_001, "存在商品参加了其它限时折扣活动");
ErrorCode DISCOUNT_ACTIVITY_UPDATE_FAIL_STATUS_CLOSED = new ErrorCode(1_013_001_002, "限时折扣活动已关闭,不能修改");
ErrorCode DISCOUNT_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED = new ErrorCode(1_013_001_003, "限时折扣活动未关闭,不能删除");
ErrorCode DISCOUNT_ACTIVITY_CLOSE_FAIL_STATUS_CLOSED = new ErrorCode(1_013_001_004, "限时折扣活动已关闭,不能重复关闭");
ErrorCode DISCOUNT_ACTIVITY_CLOSE_FAIL_STATUS_END = new ErrorCode(1_013_001_005, "限时折扣活动已结束,不能关闭");
// ========== Banner 相关 1013002000 ============
ErrorCode BANNER_NOT_EXISTS = new ErrorCode(1013002000, "Banner 不存在");
// ========== Banner 相关 1-013-002-000 ============
ErrorCode BANNER_NOT_EXISTS = new ErrorCode(1_013_002_000, "Banner 不存在");
// ========== Coupon 相关 1013003000 ============
ErrorCode COUPON_NO_MATCH_SPU = new ErrorCode(1013003000, "优惠劵没有可使用的商品!");
ErrorCode COUPON_NO_MATCH_MIN_PRICE = new ErrorCode(1013003001, "所结算的商品中未满足使用的金额");
// ========== Coupon 相关 1-013-003-000 ============
ErrorCode COUPON_NO_MATCH_SPU = new ErrorCode(1_013_003_000, "优惠劵没有可使用的商品!");
ErrorCode COUPON_NO_MATCH_MIN_PRICE = new ErrorCode(1_013_003_001, "所结算的商品中未满足使用的金额");
// ========== 优惠劵模板 1013004000 ==========
ErrorCode COUPON_TEMPLATE_NOT_EXISTS = new ErrorCode(1013004000, "优惠劵模板不存在");
ErrorCode COUPON_TEMPLATE_TOTAL_COUNT_TOO_SMALL = new ErrorCode(1013004001, "发放数量不能小于已领取数量({})");
ErrorCode COUPON_TEMPLATE_NOT_ENOUGH = new ErrorCode(1013004002, "当前剩余数量不够领取");
ErrorCode COUPON_TEMPLATE_USER_ALREADY_TAKE = new ErrorCode(1013004003, "用户已领取过此优惠券");
ErrorCode COUPON_TEMPLATE_EXPIRED = new ErrorCode(1013004004, "优惠券已过期");
ErrorCode COUPON_TEMPLATE_CANNOT_TAKE = new ErrorCode(1013004005, "领取方式不正确");
// ========== 优惠劵模板 1-013-004-000 ==========
ErrorCode COUPON_TEMPLATE_NOT_EXISTS = new ErrorCode(1_013_004_000, "优惠劵模板不存在");
ErrorCode COUPON_TEMPLATE_TOTAL_COUNT_TOO_SMALL = new ErrorCode(1_013_004_001, "发放数量不能小于已领取数量({})");
ErrorCode COUPON_TEMPLATE_NOT_ENOUGH = new ErrorCode(1_013_004_002, "当前剩余数量不够领取");
ErrorCode COUPON_TEMPLATE_USER_ALREADY_TAKE = new ErrorCode(1_013_004_003, "用户已领取过此优惠券");
ErrorCode COUPON_TEMPLATE_EXPIRED = new ErrorCode(1_013_004_004, "优惠券已过期");
ErrorCode COUPON_TEMPLATE_CANNOT_TAKE = new ErrorCode(1_013_004_005, "领取方式不正确");
// ========== 优惠劵 1013005000 ==========
ErrorCode COUPON_NOT_EXISTS = new ErrorCode(1013005000, "优惠券不存在");
ErrorCode COUPON_DELETE_FAIL_USED = new ErrorCode(1013005001, "回收优惠劵失败,优惠劵已被使用");
ErrorCode COUPON_STATUS_NOT_UNUSED = new ErrorCode(1013005002, "优惠劵不处于待使用状态");
ErrorCode COUPON_VALID_TIME_NOT_NOW = new ErrorCode(1013005003, "优惠券不在使用时间范围内");
ErrorCode COUPON_STATUS_NOT_USED = new ErrorCode(1013005004, "优惠劵不是已使用状态");
// ========== 优惠劵 1-013-005-000 ==========
ErrorCode COUPON_NOT_EXISTS = new ErrorCode(1_013_005_000, "优惠券不存在");
ErrorCode COUPON_DELETE_FAIL_USED = new ErrorCode(1_013_005_001, "回收优惠劵失败,优惠劵已被使用");
ErrorCode COUPON_STATUS_NOT_UNUSED = new ErrorCode(1_013_005_002, "优惠劵不处于待使用状态");
ErrorCode COUPON_VALID_TIME_NOT_NOW = new ErrorCode(1_013_005_003, "优惠券不在使用时间范围内");
ErrorCode COUPON_STATUS_NOT_USED = new ErrorCode(1_013_005_004, "优惠劵不是已使用状态");
// ========== 满减送活动 1013006000 ==========
ErrorCode REWARD_ACTIVITY_NOT_EXISTS = new ErrorCode(1013006000, "满减送活动不存在");
ErrorCode REWARD_ACTIVITY_SPU_CONFLICTS = new ErrorCode(1013006001, "存在商品参加了其它满减送活动");
ErrorCode REWARD_ACTIVITY_UPDATE_FAIL_STATUS_CLOSED = new ErrorCode(1013006002, "满减送活动已关闭,不能修改");
ErrorCode REWARD_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED = new ErrorCode(1013006003, "满减送活动未关闭,不能删除");
ErrorCode REWARD_ACTIVITY_CLOSE_FAIL_STATUS_CLOSED = new ErrorCode(1013006004, "满减送活动已关闭,不能重复关闭");
ErrorCode REWARD_ACTIVITY_CLOSE_FAIL_STATUS_END = new ErrorCode(1013006005, "满减送活动已结束,不能关闭");
// ========== 满减送活动 1-013-006-000 ==========
ErrorCode REWARD_ACTIVITY_NOT_EXISTS = new ErrorCode(1_013_006_000, "满减送活动不存在");
ErrorCode REWARD_ACTIVITY_SPU_CONFLICTS = new ErrorCode(1_013_006_001, "存在商品参加了其它满减送活动");
ErrorCode REWARD_ACTIVITY_UPDATE_FAIL_STATUS_CLOSED = new ErrorCode(1_013_006_002, "满减送活动已关闭,不能修改");
ErrorCode REWARD_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED = new ErrorCode(1_013_006_003, "满减送活动未关闭,不能删除");
ErrorCode REWARD_ACTIVITY_CLOSE_FAIL_STATUS_CLOSED = new ErrorCode(1_013_006_004, "满减送活动已关闭,不能重复关闭");
ErrorCode REWARD_ACTIVITY_CLOSE_FAIL_STATUS_END = new ErrorCode(1_013_006_005, "满减送活动已结束,不能关闭");
// ========== TODO 空着 1013007000 ============
// ========== TODO 空着 1-013-007-000 ============
// ========== 秒杀活动 1013008000 ==========
// ========== 秒杀活动 1-013-008-000 ==========
ErrorCode SECKILL_ACTIVITY_NOT_EXISTS = new ErrorCode(1013008000, "秒杀活动不存在");
ErrorCode SECKILL_ACTIVITY_SPU_CONFLICTS = new ErrorCode(1013008002, "存在商品参加了其它秒杀活动,秒杀时段冲突");
ErrorCode SECKILL_ACTIVITY_UPDATE_FAIL_STATUS_CLOSED = new ErrorCode(1013008003, "秒杀活动已关闭,不能修改");
@@ -57,19 +57,19 @@ public interface ErrorCodeConstants {
ErrorCode SECKILL_ACTIVITY_CLOSE_FAIL_STATUS_CLOSED = new ErrorCode(1013008005, "秒杀活动已关闭,不能重复关闭");
ErrorCode SECKILL_ACTIVITY_UPDATE_STOCK_FAIL = new ErrorCode(1013008006, "秒杀失败,原因秒杀库存不足");
// ========== 秒杀时段 1013009000 ==========
ErrorCode SECKILL_CONFIG_NOT_EXISTS = new ErrorCode(1013009000, "秒杀时段不存在");
ErrorCode SECKILL_CONFIG_TIME_CONFLICTS = new ErrorCode(1013009001, "秒杀时段冲突");
ErrorCode SECKILL_CONFIG_DISABLE = new ErrorCode(1013009004, "秒杀时段已关闭");
// ========== 秒杀时段 1-013-009-000 ==========
ErrorCode SECKILL_CONFIG_NOT_EXISTS = new ErrorCode(1_013_009_000, "秒杀时段不存在");
ErrorCode SECKILL_CONFIG_TIME_CONFLICTS = new ErrorCode(1_013_009_001, "秒杀时段冲突");
ErrorCode SECKILL_CONFIG_DISABLE = new ErrorCode(1_013_009_004, "秒杀时段已关闭");
// ========== 拼团活动 1013010000 ==========
// ========== 拼团活动 1-013-010-000 ==========
ErrorCode COMBINATION_ACTIVITY_NOT_EXISTS = new ErrorCode(1013010000, "拼团活动不存在");
ErrorCode COMBINATION_ACTIVITY_SPU_CONFLICTS = new ErrorCode(1013010001, "存在商品参加了其它拼团活动");
ErrorCode COMBINATION_ACTIVITY_STATUS_DISABLE_NOT_UPDATE = new ErrorCode(1013010002, "拼团活动已关闭不能修改");
ErrorCode COMBINATION_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED_OR_END = new ErrorCode(1013010003, "拼团活动未关闭或未结束,不能删除");
ErrorCode COMBINATION_ACTIVITY_STATUS_DISABLE = new ErrorCode(1013010004, "拼团失败,原因:拼团活动已关闭");
// ========== 拼团记录 1013011000 ==========
// ========== 拼团记录 1-013-011-000 ==========
ErrorCode COMBINATION_RECORD_NOT_EXISTS = new ErrorCode(1013011000, "拼团不存在");
ErrorCode COMBINATION_RECORD_EXISTS = new ErrorCode(1013011001, "拼团失败,已参与过该拼团");
ErrorCode COMBINATION_RECORD_HEAD_NOT_EXISTS = new ErrorCode(1013011002, "拼团失败,父拼团不存在");
@@ -79,17 +79,17 @@ public interface ErrorCodeConstants {
ErrorCode COMBINATION_RECORD_FAILED_SINGLE_LIMIT_COUNT_EXCEED = new ErrorCode(1013011006, "拼团失败,原因:单次限购超出");
ErrorCode COMBINATION_RECORD_FAILED_TOTAL_LIMIT_COUNT_EXCEED = new ErrorCode(1013011007, "拼团失败,原因:超出总购买次数");
// ========== 砍价活动 1013012000 ==========
// ========== 砍价活动 1-013-012-000 ==========
ErrorCode BARGAIN_ACTIVITY_NOT_EXISTS = new ErrorCode(1013012000, "砍价活动不存在");
ErrorCode BARGAIN_ACTIVITY_SPU_CONFLICTS = new ErrorCode(1013012001, "存在商品参加了其它砍价活动");
ErrorCode BARGAIN_ACTIVITY_STATUS_DISABLE = new ErrorCode(1013012002, "砍价活动已关闭不能修改");
ErrorCode BARGAIN_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED_OR_END = new ErrorCode(1013012003, "砍价活动未关闭或未结束,不能删除");
ErrorCode BARGAIN_ACTIVITY_UPDATE_STOCK_FAIL = new ErrorCode(1013012004, "砍价失败,原因:该砍价活动库存不足");
// ========== 砍价记录 1013013000 ==========
ErrorCode BARGAIN_RECORD_NOT_EXISTS = new ErrorCode(1013013000, "砍价记录不存在");
ErrorCode BARGAIN_RECORD_EXISTS = new ErrorCode(1013013001, "砍价失败,已参与过该砍价");
ErrorCode BARGAIN_RECORD_HEAD_NOT_EXISTS = new ErrorCode(1013013002, "砍价失败,父砍价不存在");
ErrorCode BARGAIN_RECORD_USER_FULL = new ErrorCode(1013013003, "砍价失败,砍价人数已满");
// ========== 砍价记录 1-013-013-000 ==========
ErrorCode BARGAIN_RECORD_NOT_EXISTS = new ErrorCode(1_013_013_000, "砍价记录不存在");
ErrorCode BARGAIN_RECORD_EXISTS = new ErrorCode(1_013_013_001, "砍价失败,已参与过该砍价");
ErrorCode BARGAIN_RECORD_HEAD_NOT_EXISTS = new ErrorCode(1_013_013_002, "砍价失败,父砍价不存在");
ErrorCode BARGAIN_RECORD_USER_FULL = new ErrorCode(1_013_013_003, "砍价失败,砍价人数已满");
}

View File

@@ -37,15 +37,6 @@ public class CouponController {
@Resource
private MemberUserApi memberUserApi;
// @GetMapping("/get")
// @Operation(summary = "获得优惠劵")
// @Parameter(name = "id", description = "编号", required = true, example = "1024")
// @PreAuthorize("@ss.hasPermission('promotion:coupon:query')")
// public CommonResult<CouponRespVO> getCoupon(@RequestParam("id") Long id) {
// CouponDO coupon = couponService.getCoupon(id);
// return success(CouponConvert.INSTANCE.convert(coupon));
// }
@DeleteMapping("/delete")
@Operation(summary = "回收优惠劵")
@Parameter(name = "id", description = "编号", required = true)

View File

@@ -44,4 +44,5 @@ public class CouponTemplatePageReqVO extends PageParam {
@Schema(description = "商品范围编号", example = "1")
private Long productScopeValue;
}

View File

@@ -9,41 +9,41 @@ import java.time.LocalDateTime;
@Data
public class AppArticleRespVO {
@Schema(description = "文章编号", required = true, example = "1")
@Schema(description = "文章编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long id;
@Schema(description = "文章标题", required = true, example = "芋道源码 - 促销模块")
@Schema(description = "文章标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道源码 - 促销模块")
private String title;
@Schema(description = "文章作者", required = true, example = "芋道源码")
@Schema(description = "文章作者", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道源码")
private String author;
@Schema(description = "分类编号", required = true, example = "2048")
@Schema(description = "分类编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
private Long categoryId;
@Schema(description = "图文封面", required = true, example = "https://www.iocoder.cn/1.png")
@Schema(description = "图文封面", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png")
private String picUrl;
@Schema(description = "文章简介", required = true, example = "我是简介")
@Schema(description = "文章简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是简介")
private String introduction;
@Schema(description = "文章内容", required = true, example = "我是详细")
@Schema(description = "文章内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是详细")
private String description;
@Schema(description = "发布时间", required = true)
@Schema(description = "发布时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;
@Schema(description = "浏览量", required = true, example = "1024")
@Schema(description = "浏览量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Integer browseCount;
@Schema(description = "关联的商品 SPU 编号", example = "1024")
private Long spuId;
// TODO 芋艿:下面 2 个字段,后端要存储,前端不用返回;
// @Schema(description = "是否热卖推荐", required = true, example = "true")
// @Schema(description = "是否热卖推荐", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
// private Boolean recommendHot;
//
// @Schema(description = "是否 Banner 推荐", required = true, example = "true")
// @Schema(description = "是否 Banner 推荐", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
// private Boolean recommendBanner;
}

View File

@@ -7,20 +7,20 @@ import lombok.Data;
@Data
public class AppArticleCategoryRespVO {
@Schema(description = "分类编号", required = true, example = "1")
@Schema(description = "分类编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long id;
@Schema(description = "分类名称", required = true, example = "技术")
@Schema(description = "分类名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "技术")
private String name;
@Schema(description = "分类图标", required = true, example = "https://www.iocoder.cn/1.png")
@Schema(description = "分类图标", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png")
private String picUrl;
// TODO 芋艿:下面 2 个字段,后端要存储,前端不用返回;
// @Schema(description = "状态", required = true, example = "1")
// @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
// private Integer status;
//
// @Schema(description = "排序", required = true, example = "1024")
// @Schema(description = "排序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
// private Integer sort;
}

View File

@@ -9,46 +9,46 @@ import java.time.LocalDateTime;
@Data
public class AppBargainActivityDetailRespVO {
@Schema(description = "砍价活动编号", required = true, example = "1024")
@Schema(description = "砍价活动编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long id;
@Schema(description = "砍价活动名称", required = true, example = "618 大砍价")
@Schema(description = "砍价活动名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "618 大砍价")
private String name;
@Schema(description = "活动开始时间", required = true)
@Schema(description = "活动开始时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime startTime;
@Schema(description = "活动结束时间", required = true)
@Schema(description = "活动结束时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime endTime;
@Schema(description = "商品 SPU 编号", required = true, example = "2048")
@Schema(description = "商品 SPU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
private Long spuId;
@Schema(description = "商品 SKU 编号", required = true, example = "1024")
@Schema(description = "商品 SKU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long skuId;
@Schema(description = "商品价格,单位:分", required = true, example = "100")
@Schema(description = "商品价格,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
private Integer price;
@Schema(description = "商品描述", required = true, example = "我要吃西红柿")
@Schema(description = "商品描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "我要吃西红柿")
private String description;
@Schema(description = "砍价库存", required = true, example = "512")
@Schema(description = "砍价库存", requiredMode = Schema.RequiredMode.REQUIRED, example = "512")
private Integer stock;
@Schema(description = "商品图片", required = true, example = "4096") // 从 SPU 的 picUrl 读取
@Schema(description = "商品图片", requiredMode = Schema.RequiredMode.REQUIRED, example = "4096") // 从 SPU 的 picUrl 读取
private String picUrl;
@Schema(description = "商品市场价,单位:分", required = true, example = "50") // 从 SPU 的 marketPrice 读取
@Schema(description = "商品市场价,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "50") // 从 SPU 的 marketPrice 读取
private Integer marketPrice;
@Schema(description = "商品单位", required = true, example = "") // 从 SPU 的 unit 读取,然后转换
@Schema(description = "商品单位", requiredMode = Schema.RequiredMode.REQUIRED, example = "") // 从 SPU 的 unit 读取,然后转换
private String unitName;
@Schema(description = "砍价最低金额,单位:分", required = true, example = "100")
@Schema(description = "砍价最低金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
private Integer bargainPrice;
@Schema(description = "砍价成功数量", required = true, example = "100")
@Schema(description = "砍价成功数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
private Integer successCount;
}

View File

@@ -9,34 +9,34 @@ import java.time.LocalDateTime;
@Data
public class AppBargainActivityRespVO {
@Schema(description = "砍价活动编号", required = true, example = "1024")
@Schema(description = "砍价活动编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long id;
@Schema(description = "砍价活动名称", required = true, example = "618 大砍价")
@Schema(description = "砍价活动名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "618 大砍价")
private String name;
@Schema(description = "活动开始时间", required = true)
@Schema(description = "活动开始时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime startTime;
@Schema(description = "活动结束时间", required = true)
@Schema(description = "活动结束时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime endTime;
@Schema(description = "商品 SPU 编号", required = true, example = "2048")
@Schema(description = "商品 SPU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
private Long spuId;
@Schema(description = "商品 SKU 编号", required = true, example = "1024")
@Schema(description = "商品 SKU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long skuId;
@Schema(description = "砍价库存", required = true, example = "512")
@Schema(description = "砍价库存", requiredMode = Schema.RequiredMode.REQUIRED, example = "512")
private Integer stock;
@Schema(description = "商品图片", required = true, example = "4096") // 从 SPU 的 picUrl 读取
@Schema(description = "商品图片", requiredMode = Schema.RequiredMode.REQUIRED, example = "4096") // 从 SPU 的 picUrl 读取
private String picUrl;
@Schema(description = "商品市场价,单位:分", required = true, example = "50") // 从 SPU 的 marketPrice 读取
@Schema(description = "商品市场价,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "50") // 从 SPU 的 marketPrice 读取
private Integer marketPrice;
@Schema(description = "砍价最低金额,单位:分", required = true, example = "100")
@Schema(description = "砍价最低金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
private Integer bargainPrice;
}

View File

@@ -9,7 +9,7 @@ import javax.validation.constraints.NotNull;
@Data
public class AppBargainHelpCreateReqVO {
@Schema(description = "砍价记录编号", required = true, example = "1024")
@Schema(description = "砍价记录编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@NotNull(message = "砍价记录编号不能为空")
private Long recordId;

View File

@@ -9,16 +9,16 @@ import java.time.LocalDateTime;
@Data
public class AppBargainHelpRespVO {
@Schema(description = "助力用户的昵称", required = true, example = "1024")
@Schema(description = "助力用户的昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private String nickname;
@Schema(description = "助力用户的头像", required = true, example = "1024")
@Schema(description = "助力用户的头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private String avatar;
@Schema(description = "助力用户的砍价金额", required = true, example = "1024")
@Schema(description = "助力用户的砍价金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Integer reducePrice;
@Schema(description = "创建时间", required = true, example = "1024")
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private LocalDateTime createTime;
}

View File

@@ -9,7 +9,7 @@ import javax.validation.constraints.NotNull;
@Data
public class AppBargainRecordCreateReqVO {
@Schema(description = "砍价活动编号", required = true, example = "1024")
@Schema(description = "砍价活动编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@NotNull(message = "砍价活动编号不能为空")
private Long activityId;

View File

@@ -9,23 +9,23 @@ import java.util.List;
@Data
public class AppBargainRecordSummaryRespVO {
@Schema(description = "砍价用户数量", required = true, example = "1024")
@Schema(description = "砍价用户数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Integer userCount;
@Schema(description = "成功砍价的记录", required = true) // 只返回最近的 7 个
@Schema(description = "成功砍价的记录", requiredMode = Schema.RequiredMode.REQUIRED) // 只返回最近的 7 个
private List<Record> successRecords;
@Schema(description = "成功砍价记录")
@Data
public static class Record {
@Schema(description = "用户昵称", required = true, example = "王**")
@Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "王**")
private String nickname;
@Schema(description = "用户头像", required = true, example = "https://www.iocoder.cn/xxx.jpg")
@Schema(description = "用户头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/xxx.jpg")
private String avatar;
@Schema(description = "活动名称", required = true, example = "天蚕土豆")
@Schema(description = "活动名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "天蚕土豆")
private String activityName;
}

View File

@@ -40,14 +40,17 @@ public class AppCouponController {
@PostMapping("/take")
@Operation(summary = "领取优惠劵")
@Parameter(name = "templateId", description = "优惠券模板编号", required = true, example = "1024")
@PreAuthenticated
public CommonResult<Boolean> takeCoupon(@Valid @RequestBody AppCouponTakeReqVO reqVO) {
// 1. 领取优惠劵
Long userId = getLoginUserId();
// 领取
couponService.takeCoupon(reqVO.getTemplateId(), CollUtil.newHashSet(userId), CouponTakeTypeEnum.USER);
// 检查是否可以继续领取
// 2. 检查是否可以继续领取
CouponTemplateDO couponTemplate = couponTemplateService.getCouponTemplate(reqVO.getTemplateId());
boolean canTakeAgain = true;
if (couponTemplate.getTakeLimitCount() != null && couponTemplate.getTakeLimitCount() > 0) {
// TODO @疯狂:要不要搞个 getTakeCount 方法?
Integer takeCount = MapUtil.getInt(couponService.getTakeCountMapByTemplateIds(
Collections.singleton(reqVO.getTemplateId()), userId), reqVO.getTemplateId(), 0);
canTakeAgain = takeCount < couponTemplate.getTakeLimitCount();
@@ -56,15 +59,16 @@ public class AppCouponController {
}
@GetMapping("/match-list")
@Operation(summary = "获得匹配指定商品的优惠劵列表")
@Operation(summary = "获得匹配指定商品的优惠劵列表", description = "用于下单页,展示优惠劵列表")
public CommonResult<List<AppCouponMatchRespVO>> getMatchCouponList(AppCouponMatchReqVO matchReqVO) {
// todo: 优惠金额倒序
// todo: 优化:优惠金额倒序
return success(CouponConvert.INSTANCE.convertList(couponService.getMatchCouponList(getLoginUserId(), matchReqVO)));
}
@GetMapping("/page")
@Operation(summary = "优惠劵列表", description = "我的优惠劵")
public CommonResult<PageResult<AppCouponRespVO>> takeCoupon(AppCouponPageReqVO pageReqVO) {
@Operation(summary = "我的优惠劵列表")
@PreAuthenticated
public CommonResult<PageResult<AppCouponRespVO>> getCouponPage(AppCouponPageReqVO pageReqVO) {
PageResult<CouponDO> pageResult = couponService.getCouponPage(
CouponConvert.INSTANCE.convert(pageReqVO, Collections.singleton(getLoginUserId())));
return success(CouponConvert.INSTANCE.convertAppPage(pageResult));

View File

@@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.promotion.controller.app.coupon;
import cn.hutool.core.util.ObjUtil;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;
import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.template.AppCouponTemplatePageReqVO;
@@ -14,13 +15,17 @@ import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTakeTypeEnum;
import cn.iocoder.yudao.module.promotion.service.coupon.CouponService;
import cn.iocoder.yudao.module.promotion.service.coupon.CouponTemplateService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.*;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@@ -41,16 +46,63 @@ public class AppCouponTemplateController {
@Resource
private ProductSpuApi productSpuApi;
// TODO 疯狂:这里应该还有个 list 接口哈;获得优惠劵模版列表,用于首页、商品页的优惠劵
@GetMapping("/list")
@Operation(summary = "获得优惠劵模版列表")
@Parameters({
@Parameter(name = "spuId", description = "商品 SPU 编号"), // 目前主要给商品详情使用
@Parameter(name = "useType", description = "使用类型"),
@Parameter(name = "count", description = "数量", required = true)
})
public CommonResult<List<AppCouponTemplateRespVO>> getCouponTemplateList(
@RequestParam(value = "spuId", required = false) Long spuId,
@RequestParam(value = "useType", required = false) Integer useType,
@RequestParam(value = "count", required = false, defaultValue = "10") Integer count) {
List<AppCouponTemplateRespVO> list = new ArrayList<>();
Random random = new Random();
for (int i = 0; i < 10; i++) {
AppCouponTemplateRespVO vo = new AppCouponTemplateRespVO();
vo.setId(i + 1L);
vo.setName("优惠劵" + (i + 1));
vo.setTakeLimitCount(random.nextInt(10) + 1);
vo.setUsePrice(random.nextInt(100) * 100);
vo.setValidityType(random.nextInt(2) + 1);
if (vo.getValidityType() == 1) {
vo.setValidStartTime(LocalDateTime.now().plusDays(random.nextInt(10)));
vo.setValidEndTime(LocalDateTime.now().plusDays(random.nextInt(20) + 10));
} else {
vo.setFixedStartTerm(random.nextInt(10));
vo.setFixedEndTerm(random.nextInt(10) + vo.getFixedStartTerm() + 1);
}
vo.setDiscountType(random.nextInt(2) + 1);
if (vo.getDiscountType() == 1) {
vo.setDiscountPercent(null);
vo.setDiscountPrice(random.nextInt(50) * 100);
vo.setDiscountLimitPrice(null);
} else {
vo.setDiscountPercent(random.nextInt(90) + 10);
vo.setDiscountPrice(null);
vo.setDiscountLimitPrice(random.nextInt(200) * 100);
}
// TODO @疯狂:是否已领取,要不在 TemplateService 搞个 static 方法,让它基于 countMap 这种去计算,这样好点?
vo.setTakeStatus(random.nextBoolean());
list.add(vo);
}
return success(list);
}
@GetMapping("/page")
@Operation(summary = "获得优惠劵模版分页")
public CommonResult<PageResult<AppCouponTemplateRespVO>> getCouponTemplatePage(AppCouponTemplatePageReqVO pageReqVO) {
// 1.1 处理查询条件:商品范围编号
Long productScopeValue = getaProductScopeValue(pageReqVO);
// 1.2 处理查询条件:领取方式=直接领取
Long productScopeValue = getProductScopeValue(pageReqVO);
// 1.2 处理查询条件:领取方式 = 直接领取
List<Integer> canTakeTypes = Collections.singletonList(CouponTakeTypeEnum.USER.getValue());
// 2. 分页查询
PageResult<CouponTemplateDO> pageResult = couponTemplateService.getCouponTemplatePage(
CouponTemplateConvert.INSTANCE.convert(pageReqVO, canTakeTypes, pageReqVO.getProductScope(), productScopeValue));
// 3.1 领取数量
Map<Long, Integer> couponTakeCountMap = new HashMap<>(0);
Long userId = getLoginUserId();
@@ -63,17 +115,24 @@ public class AppCouponTemplateController {
return success(CouponTemplateConvert.INSTANCE.convertAppPage(pageResult, couponTakeCountMap));
}
private Long getaProductScopeValue(AppCouponTemplatePageReqVO pageReqVO) {
Long productScopeValue = pageReqVO.getSpuId();
if (pageReqVO.getProductScope() == null || Objects.equals(pageReqVO.getProductScope(), PromotionProductScopeEnum.ALL.getScope())) {
// 通用券:清除商品范围
productScopeValue = null;
} else if (Objects.equals(pageReqVO.getProductScope(), PromotionProductScopeEnum.CATEGORY.getScope()) && pageReqVO.getSpuId() != null) {
// 品类券:查询商品的品类
productScopeValue = Optional.ofNullable(productSpuApi.getSpu(pageReqVO.getSpuId()))
/**
* 获得分页查询的商品范围
*
* @param pageReqVO 分页查询
* @return 商品范围
*/
private Long getProductScopeValue(AppCouponTemplatePageReqVO pageReqVO) {
// 通用券:清除商品范围
if (pageReqVO.getProductScope() == null || ObjectUtils.equalsAny(pageReqVO.getProductScope(), PromotionProductScopeEnum.ALL.getScope(), null)) {
return null;
}
// 品类券:查询商品的品类
if (Objects.equals(pageReqVO.getProductScope(), PromotionProductScopeEnum.CATEGORY.getScope()) && pageReqVO.getSpuId() != null) {
return Optional.ofNullable(productSpuApi.getSpu(pageReqVO.getSpuId()))
.map(ProductSpuRespDTO::getCategoryId).orElse(null);
}
return productScopeValue;
// 商品卷:直接返回
return pageReqVO.getSpuId();
}
}

View File

@@ -20,4 +20,5 @@ public class AppCouponTemplatePageReqVO extends PageParam {
@Schema(description = "商品标号", example = "1")
private Long spuId;
}

View File

@@ -23,14 +23,6 @@ public class AppCouponTemplateRespVO {
// 单位0 - 不限制
private Integer usePrice;
// TODO 芋艿:这两要改的
// @Schema(description = "商品范围", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
// @InEnum(PromotionProductScopeEnum.class)
// private Integer productScope;
//
// @Schema(description = "商品范围编号的数组", example = "1,3")
// private List<Long> productScopeValues;
@Schema(description = "生效日期类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer validityType;

View File

@@ -61,4 +61,5 @@ public interface CouponConvert {
PageResult<AppCouponRespVO> convertAppPage(PageResult<CouponDO> pageResult);
List<AppCouponMatchRespVO> convertList(List<CouponDO> list);
}

View File

@@ -42,16 +42,16 @@ public interface CouponTemplateConvert {
if (MapUtil.isEmpty(couponTakeCountMap)) {
return result;
}
for (AppCouponTemplateRespVO vo : result.getList()) {
for (AppCouponTemplateRespVO template : result.getList()) {
// 每人领取数量无限制
if (vo.getTakeLimitCount() == -1) {
vo.setTakeStatus(false);
if (template.getTakeLimitCount() == -1) {
template.setTakeStatus(false);
continue;
}
// 检查已领取数量是否超过限领数量
vo.setTakeStatus(MapUtil.getInt(couponTakeCountMap, vo.getId(), 0) >= vo.getTakeLimitCount());
template.setTakeStatus(MapUtil.getInt(couponTakeCountMap, template.getId(), 0) >= template.getTakeLimitCount());
}
return result;
}
}

View File

@@ -70,6 +70,7 @@ public interface CouponMapper extends BaseMapperX<CouponDO> {
);
}
// TODO @疯狂:这个是不是搞个 Map 就可以呀?
default List<CouponTakeCountBO> selectCountByUserIdAndTemplateIdIn(Long userId, Collection<Long> templateIds) {
return BeanUtil.copyToList(selectMaps(MPJWrappers.lambdaJoin(CouponDO.class)
.select(CouponDO::getTemplateId)
@@ -81,18 +82,17 @@ public interface CouponMapper extends BaseMapperX<CouponDO> {
default List<CouponDO> selectListByUserIdAndStatusAndUsePriceLeAndProductScope(
Long userId, Integer status, Integer usePrice, List<Long> spuIds, List<Long> categoryIds) {
Function<List<Long>, String> productScopeValuesFindInSetFunc = ids -> ids.stream()
.map(id -> StrUtil.format("FIND_IN_SET({}, product_scope_values) ", id))
.collect(Collectors.joining(" OR "));
return selectList(new LambdaQueryWrapperX<CouponDO>()
.eq(CouponDO::getUserId, userId)
.eq(CouponDO::getStatus, status)
.le(CouponDO::getUsePrice, usePrice)
.and(w -> w.eq(CouponDO::getProductScope, PromotionProductScopeEnum.ALL.getScope())
.or(ww -> ww.eq(CouponDO::getProductScope, PromotionProductScopeEnum.SPU.getScope())
.le(CouponDO::getUsePrice, usePrice) // 价格小于等于,满足价格使用条件
.and(w -> w.eq(CouponDO::getProductScope, PromotionProductScopeEnum.ALL.getScope()) // 商品范围一:全部
.or(ww -> ww.eq(CouponDO::getProductScope, PromotionProductScopeEnum.SPU.getScope()) // 商品范围二:满足指定商品
.apply(productScopeValuesFindInSetFunc.apply(spuIds)))
.or(ww -> ww.eq(CouponDO::getProductScope, PromotionProductScopeEnum.CATEGORY.getScope())
.or(ww -> ww.eq(CouponDO::getProductScope, PromotionProductScopeEnum.CATEGORY.getScope()) // 商品范围三:满足指定分类
.apply(productScopeValuesFindInSetFunc.apply(categoryIds)))));
}
@@ -102,4 +102,5 @@ public interface CouponMapper extends BaseMapperX<CouponDO> {
.le(CouponDO::getValidEndTime, validEndTime)
);
}
}

View File

@@ -1,4 +1,4 @@
package cn.iocoder.yudao.module.promotion.job;
package cn.iocoder.yudao.module.promotion.job.coupon;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler;
@@ -8,19 +8,20 @@ import org.springframework.stereotype.Component;
import javax.annotation.Resource;
// TODO 芋艿配置一个 Job
/**
* 优惠券过期 Job
*
* @author owen
*/
@Component
@TenantJob
public class CouponExpireJob implements JobHandler {
@Resource
private CouponService couponService;
@Override
@TenantJob
public String execute(String param) {
int count = couponService.expireCoupon();
return StrUtil.format("过期优惠券 {} 个", count);

View File

@@ -0,0 +1,4 @@
/**
* TODO 占位,无具体含义
*/
package cn.iocoder.yudao.module.promotion.job;

View File

@@ -137,7 +137,8 @@ public interface CouponService {
* @return 领取优惠券的数量
*/
default Map<Long, Integer> getTakeCountMapByTemplateIds(Collection<Long> templateIds, Long userId) {
return convertMap(getTakeCountListByTemplateIds(templateIds, userId), CouponTakeCountBO::getTemplateId, CouponTakeCountBO::getCount);
return convertMap(getTakeCountListByTemplateIds(templateIds, userId),
CouponTakeCountBO::getTemplateId, CouponTakeCountBO::getCount);
}
/**
@@ -164,4 +165,5 @@ public interface CouponService {
* @return 过期数量
*/
int expireCoupon();
}

View File

@@ -40,6 +40,7 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.
import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*;
import static java.util.Arrays.asList;
// TODO @疯狂:注册时,赠送用户优惠劵;为了解耦,可以考虑注册时发个 MQ 消息;然后营销这里监听后消费;
/**
* 优惠劵 Service 实现类
*
@@ -192,7 +193,8 @@ public class CouponServiceImpl implements CouponService {
@Override
public List<CouponDO> getMatchCouponList(Long userId, AppCouponMatchReqVO matchReqVO) {
return couponMapper.selectListByUserIdAndStatusAndUsePriceLeAndProductScope(userId, CouponStatusEnum.UNUSED.getStatus(),
return couponMapper.selectListByUserIdAndStatusAndUsePriceLeAndProductScope(userId,
CouponStatusEnum.UNUSED.getStatus(),
matchReqVO.getPrice(), matchReqVO.getSpuIds(), matchReqVO.getCategoryIds());
}
@@ -220,15 +222,20 @@ public class CouponServiceImpl implements CouponService {
return count;
}
/**
* 过期单个优惠劵
*
* @param coupon 优惠劵
* @return 是否过期成功
*/
private boolean expireCoupon(CouponDO coupon) {
// 更新记录状态
CouponDO updateObj = new CouponDO().setStatus(CouponStatusEnum.EXPIRE.getStatus());
int updateRows = couponMapper.updateByIdAndStatus(coupon.getId(), CouponStatusEnum.UNUSED.getStatus(), updateObj);
int updateRows = couponMapper.updateByIdAndStatus(coupon.getId(), CouponStatusEnum.UNUSED.getStatus(),
new CouponDO().setStatus(CouponStatusEnum.EXPIRE.getStatus()));
if (updateRows == 0) {
log.error("[expireCoupon][coupon({}) 更新为已过期失败]", coupon.getId());
return false;
}
log.info("[expireCoupon][coupon({}) 更新为已过期成功]", coupon.getId());
return true;
}

View File

@@ -29,6 +29,7 @@ public class CouponTemplateServiceImpl implements CouponTemplateService {
@Resource
private CouponTemplateMapper couponTemplateMapper;
// TODO @疯狂:新增/修改时,需要校验对应的商品、分类是否存在
@Override
public Long createCouponTemplate(CouponTemplateCreateReqVO createReqVO) {
// 插入

View File

@@ -9,6 +9,7 @@ import lombok.Data;
*/
@Data
public class CouponTakeCountBO {
/**
* 优惠劵模板编号
*/
@@ -17,4 +18,5 @@ public class CouponTakeCountBO {
* 领取数量
*/
private Integer count;
}

View File

@@ -11,7 +11,7 @@ import cn.iocoder.yudao.framework.common.exception.ErrorCode;
*/
public interface ErrorCodeConstants {
// ========== Order 模块 1011000000 ==========
// ========== Order 模块 1-011-000-000 ==========
ErrorCode ORDER_ITEM_NOT_FOUND = new ErrorCode(1011000010, "交易订单项不存在");
ErrorCode ORDER_NOT_FOUND = new ErrorCode(1011000011, "交易订单不存在");
ErrorCode ORDER_ITEM_UPDATE_AFTER_SALE_STATUS_FAIL = new ErrorCode(1011000012, "交易订单项更新售后状态失败,请重试");
@@ -33,45 +33,44 @@ public interface ErrorCodeConstants {
ErrorCode ORDER_UPDATE_PRICE_FAIL_PRICE_ERROR = new ErrorCode(1011000028, "支付订单调价失败,原因:调整后支付价格不能小于 0.01 元");
ErrorCode ORDER_DELETE_FAIL_STATUS_NOT_CANCEL = new ErrorCode(1011000029, "交易订单删除失败,订单不是【已取消】状态");
// ========== After Sale 模块 1011000100 ==========
ErrorCode AFTER_SALE_NOT_FOUND = new ErrorCode(1011000100, "售后单不存在");
ErrorCode AFTER_SALE_CREATE_FAIL_REFUND_PRICE_ERROR = new ErrorCode(1011000101, "申请退款金额错误");
ErrorCode AFTER_SALE_CREATE_FAIL_ORDER_STATUS_CANCELED = new ErrorCode(1011000102, "订单已关闭,无法申请售后");
ErrorCode AFTER_SALE_CREATE_FAIL_ORDER_STATUS_NO_PAID = new ErrorCode(1011000103, "订单未支付,无法申请售后");
ErrorCode AFTER_SALE_CREATE_FAIL_ORDER_STATUS_NO_DELIVERED = new ErrorCode(1011000104, "订单未发货,无法申请【退货退款】售后");
ErrorCode AFTER_SALE_CREATE_FAIL_ORDER_ITEM_APPLIED = new ErrorCode(1011000105, "订单项已申请售后,无法重复申请");
ErrorCode AFTER_SALE_AUDIT_FAIL_STATUS_NOT_APPLY = new ErrorCode(1011000106, "审批失败,售后状态不处于审批中");
ErrorCode AFTER_SALE_UPDATE_STATUS_FAIL = new ErrorCode(1011000107, "操作售后单失败,请刷新后重试");
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, "退款失败,售后单状态不是【待退款】");
// ========== After Sale 模块 1-011-000-100 ==========
ErrorCode AFTER_SALE_NOT_FOUND = new ErrorCode(1_011_000_100, "售后单不存在");
ErrorCode AFTER_SALE_CREATE_FAIL_REFUND_PRICE_ERROR = new ErrorCode(1_011_000_101, "申请退款金额错误");
ErrorCode AFTER_SALE_CREATE_FAIL_ORDER_STATUS_CANCELED = new ErrorCode(1_011_000_102, "订单已关闭,无法申请售后");
ErrorCode AFTER_SALE_CREATE_FAIL_ORDER_STATUS_NO_PAID = new ErrorCode(1_011_000_103, "订单未支付,无法申请售后");
ErrorCode AFTER_SALE_CREATE_FAIL_ORDER_STATUS_NO_DELIVERED = new ErrorCode(1_011_000_104, "订单未发货,无法申请【退货退款】售后");
ErrorCode AFTER_SALE_CREATE_FAIL_ORDER_ITEM_APPLIED = new ErrorCode(1_011_000_105, "订单项已申请售后,无法重复申请");
ErrorCode AFTER_SALE_AUDIT_FAIL_STATUS_NOT_APPLY = new ErrorCode(1_011_000_106, "审批失败,售后状态不处于审批中");
ErrorCode AFTER_SALE_UPDATE_STATUS_FAIL = new ErrorCode(1_011_000_107, "操作售后单失败,请刷新后重试");
ErrorCode AFTER_SALE_DELIVERY_FAIL_STATUS_NOT_SELLER_AGREE = new ErrorCode(1_011_000_108, "退货失败,售后单状态不处于【待买家退货】");
ErrorCode AFTER_SALE_CONFIRM_FAIL_STATUS_NOT_BUYER_DELIVERY = new ErrorCode(1_011_000_109, "确认收货失败,售后单状态不处于【待确认收货】");
ErrorCode AFTER_SALE_REFUND_FAIL_STATUS_NOT_WAIT_REFUND = new ErrorCode(1_011_000_110, "退款失败,售后单状态不是【待退款】");
ErrorCode AFTER_SALE_CANCEL_FAIL_STATUS_NOT_APPLY_OR_AGREE_OR_BUYER_DELIVERY =
new ErrorCode(1011000111, "取消售后单失败,售后单状态不是【待审核】或【卖家同意】或【商家待收货】");
new ErrorCode(1_011_000_111, "取消售后单失败,售后单状态不是【待审核】或【卖家同意】或【商家待收货】");
// ========== Cart 模块 1011002000 ==========
ErrorCode CARD_ITEM_NOT_FOUND = new ErrorCode(1011002000, "购物车项不存在");
// ========== Cart 模块 1-011-002-000 ==========
ErrorCode CARD_ITEM_NOT_FOUND = new ErrorCode(1_011_002_000, "购物车项不存在");
// ========== Price 相关 1011003000 ============
// ========== Price 相关 1-011-003-000 ============
ErrorCode PRICE_CALCULATE_PAY_PRICE_ILLEGAL = new ErrorCode(1011003000, "支付价格计算异常,原因:价格小于等于 0");
ErrorCode PRICE_CALCULATE_DELIVERY_PRICE_TEMPLATE_NOT_FOUND = new ErrorCode(1011003002, "计算快递运费异常,找不到对应的运费模板");
ErrorCode PRICE_CALCULATE_COUPON_NOT_MATCH_NORMAL_ORDER = new ErrorCode(1011003004, "参与秒杀、拼团、砍价的营销商品,无法使用优惠劵");
// ========== 物流 Express 模块 1011004000 ==========
ErrorCode EXPRESS_NOT_EXISTS = new ErrorCode(1011004000, "快递公司不存在");
ErrorCode EXPRESS_CODE_DUPLICATE = new ErrorCode(1011004001, "已经存在该编码的快递公司");
ErrorCode EXPRESS_CLIENT_NOT_PROVIDE = new ErrorCode(1011004002, "需要接入快递服务商比如【快递100】");
ErrorCode EXPRESS_STATUS_NOT_ENABLE = new ErrorCode(1011004003, "快递公司未启用");
// ========== 物流 Express 模块 1-011-004-000 ==========
ErrorCode EXPRESS_NOT_EXISTS = new ErrorCode(1_011_004_000, "快递公司不存在");
ErrorCode EXPRESS_CODE_DUPLICATE = new ErrorCode(1_011_004_001, "已经存在该编码的快递公司");
ErrorCode EXPRESS_CLIENT_NOT_PROVIDE = new ErrorCode(1_011_004_002, "需要接入快递服务商比如【快递100】");
ErrorCode EXPRESS_STATUS_NOT_ENABLE = new ErrorCode(1_011_004_003, "快递公司未启用");
ErrorCode EXPRESS_API_QUERY_ERROR = new ErrorCode(1011004101, "快递查询接口异常");
ErrorCode EXPRESS_API_QUERY_FAILED = new ErrorCode(1011004102, "快递查询返回失败,原因:{}");
ErrorCode EXPRESS_API_QUERY_ERROR = new ErrorCode(1_011_004_101, "快递查询接口异常");
ErrorCode EXPRESS_API_QUERY_FAILED = new ErrorCode(1_011_004_102, "快递查询返回失败,原因:{}");
// ========== 物流 Template 模块 1011005000 ==========
ErrorCode EXPRESS_TEMPLATE_NAME_DUPLICATE = new ErrorCode(1011005000, "已经存在该运费模板名");
ErrorCode EXPRESS_TEMPLATE_NOT_EXISTS = new ErrorCode(1011005001, "运费模板不存在");
// ========== 物流 PICK_UP 模块 1011006000 ==========
ErrorCode PICK_UP_STORE_NOT_EXISTS = new ErrorCode(1011006000, "自提门店不存在");
// ========== 物流 Template 模块 1-011-005-000 ==========
ErrorCode EXPRESS_TEMPLATE_NAME_DUPLICATE = new ErrorCode(1_011_005_000, "已经存在该运费模板名");
ErrorCode EXPRESS_TEMPLATE_NOT_EXISTS = new ErrorCode(1_011_005_001, "运费模板不存在");
// ========== 物流 PICK_UP 模块 1-011-006-000 ==========
ErrorCode PICK_UP_STORE_NOT_EXISTS = new ErrorCode(1_011_006_000, "自提门店不存在");
// ========== 分销用户 模块 1011007000 ==========
ErrorCode BROKERAGE_USER_NOT_EXISTS = new ErrorCode(1011007000, "分销用户不存在");

View File

@@ -1,28 +1,35 @@
package cn.iocoder.yudao.module.trade.enums.aftersale;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
/**
* 售后操作类型的枚举
*
* @author 陈賝
* @since 2023/6/13 13:53
*/
// TODO @chenchen可以 lombok 简化构造方法,和 get 方法
@RequiredArgsConstructor
@Getter
public enum AfterSaleOperateTypeEnum {
/**
* 用户申请
*/
APPLY("用户申请"),
MEMBER_CREATE(10, "会员申请退款"),
ADMIN_AGREE_APPLY(11, "商家同意退款"),
ADMIN_DISAGREE_APPLY(12, "商家拒绝退款"),
MEMBER_DELIVERY(20, "会员填写退货物流信息,快递公司:{deliveryName},快递单号:{logisticsNo}"),
ADMIN_AGREE_RECEIVE(21, "商家收货"),
ADMIN_DISAGREE_RECEIVE(22, "商家拒绝收货,原因:{reason}"),
ADMIN_REFUND(30, "商家退款"),
MEMBER_CANCEL(40, "会员取消退款"),
;
private final String description;
AfterSaleOperateTypeEnum(String description) {
this.description = description;
}
public String description() {
return description;
}
/**
* 操作类型
*/
private final Integer type;
/**
* 操作描述
*/
private final String content;
}

View File

@@ -18,15 +18,15 @@ public enum BrokerageBindModeEnum implements IntArrayValuable {
/**
* 只要用户没有推广人,随时都可以绑定分销关系
*/
ANYTIME(1, "没有推广人"),
ANYTIME(1, "首次绑定"),
/**
* 仅新用户注册时才能绑定推广关系
*/
REGISTER(2, "新用户"),
REGISTER(2, "注册绑定"),
/**
* 每次扫码都覆盖
*/
OVERRIDE(3, "扫码覆盖"),
OVERRIDE(3, "覆盖绑定"),
;
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(BrokerageBindModeEnum::getMode).toArray();

View File

@@ -17,7 +17,7 @@ public enum BrokerageRecordBizTypeEnum implements IntArrayValuable {
ORDER(1, "获得推广佣金", "获得推广佣金 {}", true),
WITHDRAW(2, "提现申请", "提现申请扣除佣金 {}", false),
WITHDRAW_REJECT(3, "提现申请驳回", "提现申请驳回返还佣金 {}", true),
WITHDRAW_REJECT(3, "提现申请驳回", "提现申请驳回返还佣金 {}", true),
;
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(BrokerageRecordBizTypeEnum::getType).toArray();

View File

@@ -6,6 +6,7 @@ import lombok.Getter;
import java.util.Arrays;
// TODO 芋艿:提现的打通,在纠结下;
/**
* 佣金提现状态枚举
*

View File

@@ -24,15 +24,16 @@ public enum TradeOrderOperateTypeEnum {
MEMBER_CANCEL(40, "取消订单"),
SYSTEM_CANCEL(41, "到期未支付,系统自动取消订单"),
// 42 预留:管理员取消订单
MEMBER_DELETE(43, "删除订单"),
ADMIN_CANCEL_AFTER_SALE(43, "订单全部售后,管理员自动取消订单"),
MEMBER_DELETE(49, "删除订单"),
;
/**
* 类型
* 操作类型
*/
private final Integer type;
/**
* 类型
* 操作描述
*/
private final String content;

View File

@@ -13,8 +13,8 @@ import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageWithdrawStatusEnum
import cn.iocoder.yudao.module.trade.service.brokerage.BrokerageRecordService;
import cn.iocoder.yudao.module.trade.service.brokerage.BrokerageUserService;
import cn.iocoder.yudao.module.trade.service.brokerage.BrokerageWithdrawService;
import cn.iocoder.yudao.module.trade.service.brokerage.bo.UserBrokerageSummaryBO;
import cn.iocoder.yudao.module.trade.service.brokerage.bo.UserWithdrawSummaryBO;
import cn.iocoder.yudao.module.trade.service.brokerage.bo.UserBrokerageSummaryRespBO;
import cn.iocoder.yudao.module.trade.service.brokerage.bo.BrokerageWithdrawSummaryRespBO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
@@ -88,24 +88,24 @@ public class BrokerageUserController {
// 分页查询
PageResult<BrokerageUserDO> pageResult = brokerageUserService.getBrokerageUserPage(pageVO);
// 涉及到的用户
Set<Long> userIds = convertSet(pageResult.getList(), BrokerageUserDO::getId);
// 查询用户信息
Set<Long> userIds = convertSet(pageResult.getList(), BrokerageUserDO::getId);
Map<Long, MemberUserRespDTO> userMap = memberUserApi.getUserMap(userIds);
// 合计分佣订单
Map<Long, UserBrokerageSummaryBO> userOrderSummaryMap = brokerageRecordService.getUserBrokerageSummaryMapByUserId(
// 合计分佣的推广订单
Map<Long, UserBrokerageSummaryRespBO> brokerageOrderSummaryMap = brokerageRecordService.getUserBrokerageSummaryMapByUserId(
userIds, BrokerageRecordBizTypeEnum.ORDER.getType(), BrokerageRecordStatusEnum.SETTLEMENT.getStatus());
// TODO @芋艿:看看下面 getBrokerageUserCountByBindUserId 有没可能一次性出结果,不然 n 次有点太花性能了;
// 合计推广用户数量
// 合计分佣的推广用户
// TODO @疯狂:转成 map 批量读取
Map<Long, Long> brokerageUserCountMap = convertMap(userIds,
userId -> userId,
userId -> brokerageUserService.getBrokerageUserCountByBindUserId(userId, null));
// 合计提现
Map<Long, UserWithdrawSummaryBO> withdrawMap = brokerageWithdrawService.getWithdrawSummaryMapByUserId(userIds,
BrokerageWithdrawStatusEnum.AUDIT_SUCCESS);
// 合计分佣的提现
// TODO @疯狂:如果未来支持了打款这个动作,可能 status 会不对;
Map<Long, BrokerageWithdrawSummaryRespBO> withdrawMap = brokerageWithdrawService.getWithdrawSummaryMapByUserId(
userIds, BrokerageWithdrawStatusEnum.AUDIT_SUCCESS);
// 拼接返回
return success(BrokerageUserConvert.INSTANCE.convertPage(pageResult, userMap, brokerageUserCountMap,
userOrderSummaryMap, withdrawMap));
brokerageOrderSummaryMap, withdrawMap));
}
}

View File

@@ -21,25 +21,25 @@ public class BrokerageUserRespVO extends BrokerageUserBaseVO {
// ========== 用户信息 ==========
@Schema(description = "用户头像", example = "https://www.iocoder.cn/xxx.png")
@Schema(description = "用户头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/xxx.png")
private String avatar;
@Schema(description = "用户昵称", example = "李四")
@Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四")
private String nickname;
// ========== 推广信息 ==========
@Schema(description = "推广用户数量", example = "20019")
@Schema(description = "推广用户数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "20019")
private Integer brokerageUserCount;
@Schema(description = "推广订单数量", example = "20019")
@Schema(description = "推广订单数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "20019")
private Integer brokerageOrderCount;
@Schema(description = "推广订单金额", example = "20019")
@Schema(description = "推广订单金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "20019")
private Integer brokerageOrderPrice;
// ========== 提现信息 ==========
@Schema(description = "已提现金额", example = "20019")
@Schema(description = "已提现金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "20019")
private Integer withdrawPrice;
@Schema(description = "已提现次数", example = "20019")
@Schema(description = "已提现次数", requiredMode = Schema.RequiredMode.REQUIRED, example = "20019")
private Integer withdrawCount;
}

View File

@@ -19,6 +19,19 @@ import java.util.List;
*/
@Data
public class TradeConfigBaseVO {
// ========== 售后相关 ==========
@Schema(description = "售后的退款理由", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "售后的退款理由不能为空")
private List<String> afterSaleRefundReasons;
@Schema(description = "售后的退货理由", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "售后的退货理由不能为空")
private List<String> afterSaleReturnReasons;
// ========== 配送相关 ==========
/**
* 是否启用全场包邮
*/
@@ -80,7 +93,7 @@ public class TradeConfigBaseVO {
private Integer brokerageFrozenDays;
@Schema(description = "提现方式", requiredMode = Schema.RequiredMode.REQUIRED, example = "[0, 1]")
@NotNull(message = "提现方式不能为空")
@NotEmpty(message = "提现方式不能为空")
@InEnum(value = BrokerageWithdrawTypeEnum.class, message = "提现方式必须是 {value}")
private List<Integer> brokerageWithdrawTypes;

View File

@@ -9,7 +9,6 @@ import cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppTradeAfterSa
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.TradeAfterSaleStatusEnum;
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.framework.aftersalelog.core.util.AfterSaleLogUtils;
import cn.iocoder.yudao.module.trade.service.aftersale.TradeAfterSaleService;
@@ -21,9 +20,6 @@ import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
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;
@@ -58,20 +54,9 @@ public class AppTradeAfterSaleController {
return success(afterSaleService.getApplyingAfterSaleCount(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")
@Operation(summary = "申请售后")
@AfterSaleLog(id = "#info.data", content = "'申请售后:售后编号['+#info.data+'],订单编号['+#createReqVO.orderItemId+'], '", operateType = AfterSaleOperateTypeEnum.APPLY)
@AfterSaleLog(id = "#info.data", content = "'申请售后:售后编号['+#info.data+'],订单编号['+#createReqVO.orderItemId+'], '", operateType = AfterSaleOperateTypeEnum.MEMBER_CREATE)
public CommonResult<Long> createAfterSale(@RequestBody AppTradeAfterSaleCreateReqVO createReqVO) {
AfterSaleLogUtils.setBeforeStatus(0);
AfterSaleLogUtils.setAfterStatus(TradeAfterSaleStatusEnum.APPLY.getStatus());

View File

@@ -45,7 +45,7 @@ public class AppBrokerageRecordController {
@GetMapping("/get-product-brokerage-price")
@Operation(summary = "获得商品的分销金额")
public CommonResult<AppBrokerageProductPriceRespVO> getProductBrokeragePrice(@RequestParam("spuId") Long spuId) {
return success(brokerageRecordService.calculateProductBrokeragePrice(spuId, getLoginUserId()));
return success(brokerageRecordService.calculateProductBrokeragePrice(getLoginUserId(), spuId));
}
}

View File

@@ -15,7 +15,7 @@ import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageWithdrawStatusEnum
import cn.iocoder.yudao.module.trade.service.brokerage.BrokerageRecordService;
import cn.iocoder.yudao.module.trade.service.brokerage.BrokerageUserService;
import cn.iocoder.yudao.module.trade.service.brokerage.BrokerageWithdrawService;
import cn.iocoder.yudao.module.trade.service.brokerage.bo.UserWithdrawSummaryBO;
import cn.iocoder.yudao.module.trade.service.brokerage.bo.BrokerageWithdrawSummaryRespBO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
@@ -42,6 +42,7 @@ import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUti
@Validated
@Slf4j
public class AppBrokerageUserController {
@Resource
private BrokerageUserService brokerageUserService;
@Resource
@@ -69,8 +70,7 @@ public class AppBrokerageUserController {
@Operation(summary = "绑定推广员")
@PreAuthenticated
public CommonResult<Boolean> bindBrokerageUser(@Valid @RequestBody AppBrokerageUserBindReqVO reqVO) {
MemberUserRespDTO user = memberUserApi.getUser(getLoginUserId());
return success(brokerageUserService.bindBrokerageUser(user.getId(), reqVO.getBindUserId(), user.getCreateTime()));
return success(brokerageUserService.bindBrokerageUser(getLoginUserId(), reqVO.getBindUserId()));
}
@GetMapping("/get-summary")
@@ -86,9 +86,9 @@ public class AppBrokerageUserController {
Integer yesterdayPrice = brokerageRecordService.getSummaryPriceByUserId(brokerageUser.getId(),
BrokerageRecordBizTypeEnum.ORDER.getType(), beginTime, endTime);
// 统计用户提现的佣金
Integer withdrawPrice = brokerageWithdrawService.getWithdrawSummaryByUserId(Collections.singleton(brokerageUser.getId()),
Integer withdrawPrice = brokerageWithdrawService.getWithdrawSummaryListByUserId(Collections.singleton(brokerageUser.getId()),
BrokerageWithdrawStatusEnum.AUDIT_SUCCESS).stream()
.findFirst().map(UserWithdrawSummaryBO::getPrice).orElse(0);
.findFirst().map(BrokerageWithdrawSummaryRespBO::getPrice).orElse(0);
// 统计分销用户数量(一级)
Long firstBrokerageUserCount = brokerageUserService.getBrokerageUserCountByBindUserId(brokerageUser.getId(), 1);
// 统计分销用户数量(二级)

View File

@@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.trade.controller.app.brokerage;
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.system.api.dict.DictDataApi;
import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.withdraw.AppBrokerageWithdrawCreateReqVO;
import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.withdraw.AppBrokerageWithdrawPageReqVO;
import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.withdraw.AppBrokerageWithdrawRespVO;
@@ -32,14 +31,10 @@ public class AppBrokerageWithdrawController {
@Resource
private BrokerageWithdrawService brokerageWithdrawService;
@Resource
private DictDataApi dictDataApi;
@GetMapping("/page")
@Operation(summary = "获得分销提现分页")
@PreAuthenticated
public CommonResult<PageResult<AppBrokerageWithdrawRespVO>> getBrokerageWithdrawPage(AppBrokerageWithdrawPageReqVO pageReqVO) {
// 分页查询
PageResult<BrokerageWithdrawDO> pageResult = brokerageWithdrawService.getBrokerageWithdrawPage(
BrokerageWithdrawConvert.INSTANCE.convert(pageReqVO, getLoginUserId()));
return success(BrokerageWithdrawConvert.INSTANCE.convertPage03(pageResult));
@@ -49,7 +44,7 @@ public class AppBrokerageWithdrawController {
@Operation(summary = "创建分销提现")
@PreAuthenticated
public CommonResult<Long> createBrokerageWithdraw(@RequestBody @Valid AppBrokerageWithdrawCreateReqVO createReqVO) {
return success(brokerageWithdrawService.createBrokerageWithdraw(createReqVO, getLoginUserId()));
return success(brokerageWithdrawService.createBrokerageWithdraw(getLoginUserId(), createReqVO));
}
}

View File

@@ -4,6 +4,9 @@ import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.SortingField;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.hibernate.validator.constraints.Range;
import javax.validation.constraints.NotNull;
@Schema(description = "用户 App - 下级分销统计分页 Request VO")
@Data
@@ -19,7 +22,9 @@ public class AppBrokerageUserChildSummaryPageReqVO extends PageParam {
@Schema(description = "排序字段", example = "userCount")
private SortingField sortingField;
@Schema(description = "下级的级别", example = "1") // 1 - 直接下级2 - 间接下级
@Schema(description = "下级的级别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") // 1 - 直接下级2 - 间接下级
@NotNull(message = "下级的级别不能为空")
@Range(min = 1, max = 2, message = "下级的级别只能是 {min} 或者 {max}")
private Integer level;
}

View File

@@ -9,6 +9,16 @@ import java.util.List;
@Data
public class AppTradeConfigRespVO {
// ========== 售后相关 ==========
@Schema(description = "售后的退款理由", requiredMode = Schema.RequiredMode.REQUIRED)
private List<String> afterSaleRefundReasons;
@Schema(description = "售后的退货理由", requiredMode = Schema.RequiredMode.REQUIRED)
private List<String> afterSaleReturnReasons;
// ========== 分销相关 ==========
@Schema(description = "分销海报地址数组", requiredMode = Schema.RequiredMode.REQUIRED)
private List<String> brokeragePosterUrls;

View File

@@ -8,10 +8,10 @@ import lombok.Data;
@Data
public class AppDeliveryConfigRespVO {
@Schema(description = "腾讯地图 KEY", required = true, example = "123456")
@Schema(description = "腾讯地图 KEY", requiredMode = Schema.RequiredMode.REQUIRED, example = "123456")
private String tencentLbsKey;
@Schema(description = "是否开启自提", required = true, example = "true")
@Schema(description = "是否开启自提", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
private Boolean pickUpEnable;
}

View File

@@ -7,10 +7,10 @@ import lombok.Data;
@Data
public class AppDeliveryExpressRespVO {
@Schema(description = "编号", required = true, example = "1")
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long id;
@Schema(description = "门店名称", required = true, example = "顺丰")
@Schema(description = "门店名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "顺丰")
private String name;
}

View File

@@ -25,12 +25,12 @@ public class AppTradeOrderSettlementReqVO {
@Schema(description = "优惠劵编号", example = "1024")
private Long couponId;
@Schema(description = "是否使用积分", required = true, example = "true")
@Schema(description = "是否使用积分", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
@NotNull(message = "是否使用积分不能为空")
private Boolean pointStatus;
// ========== 配送相关相关字段 ==========
@Schema(description = "配送方式", example = "1")
@Schema(description = "配送方式", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@InEnum(value = DeliveryTypeEnum.class, message = "配送方式不正确")
private Integer deliveryType;

View File

@@ -13,7 +13,7 @@ import java.util.List;
@Data
public class AppTradeOrderSettlementRespVO {
@Schema(description = "交易类型", required = true, example = "1") // 对应 TradeOrderTypeEnum 枚举
@Schema(description = "交易类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") // 对应 TradeOrderTypeEnum 枚举
private Integer type = 1; // TODO 芋艿:改成计算
@Schema(description = "购物项数组", requiredMode = Schema.RequiredMode.REQUIRED)

View File

@@ -8,8 +8,8 @@ import cn.iocoder.yudao.module.trade.controller.admin.brokerage.vo.user.Brokerag
import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.user.AppBrokerageUserMySummaryRespVO;
import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.user.AppBrokerageUserRankByUserCountRespVO;
import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.BrokerageUserDO;
import cn.iocoder.yudao.module.trade.service.brokerage.bo.UserBrokerageSummaryBO;
import cn.iocoder.yudao.module.trade.service.brokerage.bo.UserWithdrawSummaryBO;
import cn.iocoder.yudao.module.trade.service.brokerage.bo.UserBrokerageSummaryRespBO;
import cn.iocoder.yudao.module.trade.service.brokerage.bo.BrokerageWithdrawSummaryRespBO;
import org.mapstruct.Mapper;
import org.mapstruct.MappingTarget;
import org.mapstruct.factory.Mappers;
@@ -32,29 +32,27 @@ public interface BrokerageUserConvert {
List<BrokerageUserRespVO> convertList(List<BrokerageUserDO> list);
PageResult<BrokerageUserRespVO> convertPage(PageResult<BrokerageUserDO> page, Map<Long, MemberUserRespDTO> userMap, Map<Long, Long> brokerageUserCountMap, Map<Long, UserBrokerageSummaryBO> userOrderSummaryMap);
PageResult<BrokerageUserRespVO> convertPage(PageResult<BrokerageUserDO> page, Map<Long, MemberUserRespDTO> userMap, Map<Long, Long> brokerageUserCountMap, Map<Long, UserBrokerageSummaryRespBO> userOrderSummaryMap);
default PageResult<BrokerageUserRespVO> convertPage(PageResult<BrokerageUserDO> pageResult,
Map<Long, MemberUserRespDTO> userMap,
Map<Long, Long> brokerageUserCountMap,
Map<Long, UserBrokerageSummaryBO> userOrderSummaryMap,
Map<Long, UserWithdrawSummaryBO> withdrawMap) {
Map<Long, UserBrokerageSummaryRespBO> userOrderSummaryMap,
Map<Long, BrokerageWithdrawSummaryRespBO> withdrawMap) {
PageResult<BrokerageUserRespVO> result = convertPage(pageResult, userMap, brokerageUserCountMap, userOrderSummaryMap);
for (BrokerageUserRespVO userVO : result.getList()) {
// 用户信息
copyTo(userMap.get(userVO.getId()), userVO);
// 推广用户数量
userVO.setBrokerageUserCount(MapUtil.getInt(brokerageUserCountMap, userVO.getId(), 0));
// 推广订单数量、推广订单金额
Optional<UserBrokerageSummaryBO> orderSummaryOptional = Optional.ofNullable(userOrderSummaryMap.get(userVO.getId()));
userVO.setBrokerageOrderCount(orderSummaryOptional.map(UserBrokerageSummaryBO::getCount).orElse(0))
.setBrokerageOrderPrice(orderSummaryOptional.map(UserBrokerageSummaryBO::getPrice).orElse(0));
Optional<UserBrokerageSummaryRespBO> orderSummaryOptional = Optional.ofNullable(userOrderSummaryMap.get(userVO.getId()));
userVO.setBrokerageOrderCount(orderSummaryOptional.map(UserBrokerageSummaryRespBO::getCount).orElse(0))
.setBrokerageOrderPrice(orderSummaryOptional.map(UserBrokerageSummaryRespBO::getPrice).orElse(0));
// 已提现次数、已提现金额
Optional<UserWithdrawSummaryBO> withdrawSummaryOptional = Optional.ofNullable(withdrawMap.get(userVO.getId()));
userVO.setWithdrawCount(withdrawSummaryOptional.map(UserWithdrawSummaryBO::getCount).orElse(0))
.setWithdrawPrice(withdrawSummaryOptional.map(UserWithdrawSummaryBO::getPrice).orElse(0));
userVO.setWithdrawCount(0).setWithdrawPrice(0);
Optional<BrokerageWithdrawSummaryRespBO> withdrawSummaryOptional = Optional.ofNullable(withdrawMap.get(userVO.getId()));
userVO.setWithdrawCount(withdrawSummaryOptional.map(BrokerageWithdrawSummaryRespBO::getCount).orElse(0))
.setWithdrawPrice(withdrawSummaryOptional.map(BrokerageWithdrawSummaryRespBO::getPrice).orElse(0));
}
return result;
}

View File

@@ -35,6 +35,19 @@ public class TradeConfigDO extends BaseDO {
@TableId
private Long id;
// ========== 售后相关 ==========
/**
* 售后的退款理由
*/
@TableField(typeHandler = JacksonTypeHandler.class)
private List<String> afterSaleRefundReasons;
/**
* 售后的退货理由
*/
@TableField(typeHandler = JacksonTypeHandler.class)
private List<String> afterSaleReturnReasons;
// ========== 配送相关 ==========
/**

View File

@@ -105,6 +105,8 @@ public class TradeOrderDO extends BaseDO {
*/
private Boolean commentStatus;
// TODO @疯狂:加一个推广人编号;
// ========== 价格 + 支付基本信息 ==========
// 价格文档 - 淘宝https://open.taobao.com/docV3.htm?docId=108471&docType=1

View File

@@ -7,7 +7,7 @@ import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.trade.controller.admin.brokerage.vo.record.BrokerageRecordPageReqVO;
import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.user.AppBrokerageUserRankByPriceRespVO;
import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.BrokerageRecordDO;
import cn.iocoder.yudao.module.trade.service.brokerage.bo.UserBrokerageSummaryBO;
import cn.iocoder.yudao.module.trade.service.brokerage.bo.UserBrokerageSummaryRespBO;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.github.yulichang.toolkit.MPJWrappers;
@@ -56,18 +56,19 @@ public interface BrokerageRecordMapper extends BaseMapperX<BrokerageRecordDO> {
BrokerageRecordDO::getUserId, userId);
}
default List<UserBrokerageSummaryBO> selectCountAndSumPriceByUserIdInAndBizTypeAndStatus(Collection<Long> userIds,
Integer bizType,
Integer status) {
default List<UserBrokerageSummaryRespBO> selectCountAndSumPriceByUserIdInAndBizTypeAndStatus(Collection<Long> userIds,
Integer bizType,
Integer status) {
List<Map<String, Object>> list = selectMaps(MPJWrappers.lambdaJoin(BrokerageRecordDO.class)
.select(BrokerageRecordDO::getUserId)
.selectCount(BrokerageRecordDO::getId, UserBrokerageSummaryBO::getCount)
.selectCount(BrokerageRecordDO::getId, UserBrokerageSummaryRespBO::getCount)
.selectSum(BrokerageRecordDO::getPrice)
.in(BrokerageRecordDO::getUserId, userIds)
.eq(BrokerageRecordDO::getBizId, bizType)
.eq(BrokerageRecordDO::getStatus, status)
.groupBy(BrokerageRecordDO::getUserId));
return BeanUtil.copyToList(list, UserBrokerageSummaryBO.class);
return BeanUtil.copyToList(list, UserBrokerageSummaryRespBO.class);
// selectJoinList有BUG会与租户插件冲突解析SQL时发生异常 https://gitee.com/best_handsome/mybatis-plus-join/issues/I84GYW
// return selectJoinList(UserBrokerageSummaryBO.class, MPJWrappers.lambdaJoin(BrokerageRecordDO.class)
// .select(BrokerageRecordDO::getUserId)
// .selectCount(BrokerageRecordDO::getId, UserBrokerageSummaryBO::getCount)

View File

@@ -6,7 +6,7 @@ import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.trade.controller.admin.brokerage.vo.withdraw.BrokerageWithdrawPageReqVO;
import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.BrokerageWithdrawDO;
import cn.iocoder.yudao.module.trade.service.brokerage.bo.UserWithdrawSummaryBO;
import cn.iocoder.yudao.module.trade.service.brokerage.bo.BrokerageWithdrawSummaryRespBO;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import org.apache.ibatis.annotations.Mapper;
@@ -41,15 +41,15 @@ public interface BrokerageWithdrawMapper extends BaseMapperX<BrokerageWithdrawDO
.eq(BrokerageWithdrawDO::getStatus, status));
}
default List<UserWithdrawSummaryBO> selectCountAndSumPriceByUserIdAndStatus(Collection<Long> userIds, Integer status) {
default List<BrokerageWithdrawSummaryRespBO> selectCountAndSumPriceByUserIdAndStatus(Collection<Long> userIds, Integer status) {
List<Map<String, Object>> list = selectMaps(new MPJLambdaWrapper<BrokerageWithdrawDO>()
.select(BrokerageWithdrawDO::getUserId)
.selectCount(BrokerageWithdrawDO::getId, UserWithdrawSummaryBO::getCount)
.selectCount(BrokerageWithdrawDO::getId, BrokerageWithdrawSummaryRespBO::getCount)
.selectSum(BrokerageWithdrawDO::getPrice)
.in(BrokerageWithdrawDO::getUserId, userIds)
.eq(BrokerageWithdrawDO::getStatus, status)
.groupBy(BrokerageWithdrawDO::getUserId));
return BeanUtil.copyToList(list, UserWithdrawSummaryBO.class);
return BeanUtil.copyToList(list, BrokerageWithdrawSummaryRespBO.class);
// selectJoinList有BUG会与租户插件冲突解析SQL时发生异常 https://gitee.com/best_handsome/mybatis-plus-join/issues/I84GYW
// return selectJoinList(UserWithdrawSummaryBO.class, new MPJLambdaWrapper<BrokerageWithdrawDO>()
// .select(BrokerageWithdrawDO::getUserId)

View File

@@ -21,7 +21,8 @@ public @interface AfterSaleLog {
/**
* 售后 ID
*/
String id();
@Deprecated
String id() default "";
/**
* 操作类型
@@ -31,6 +32,7 @@ public @interface AfterSaleLog {
/**
* 日志内容
*/
@Deprecated
String content() default "";
}

View File

@@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.trade.framework.aftersalelog.core.aop;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.yudao.framework.common.util.spring.SpringExpressionUtils;
import cn.iocoder.yudao.framework.operatelog.core.service.OperateLog;
import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
import cn.iocoder.yudao.module.trade.framework.aftersalelog.core.annotations.AfterSaleLog;
import cn.iocoder.yudao.module.trade.framework.aftersalelog.core.dto.TradeAfterSaleLogCreateReqDTO;
@@ -96,7 +95,7 @@ public class AfterSaleLogAspect {
String id = MapUtil.getStr(spelMap, afterSaleLogPoint.id());
result.put(ID, id);
// 操作类型
String operateType = afterSaleLogPoint.operateType().description();
String operateType = afterSaleLogPoint.operateType().getContent();
result.put(OPERATE_TYPE, operateType);
// 日志内容
String content = MapUtil.getStr(spelMap, afterSaleLogPoint.content());

View File

@@ -3,6 +3,8 @@ package cn.iocoder.yudao.module.trade.framework.aftersalelog.core.util;
import cn.iocoder.yudao.module.trade.framework.aftersalelog.core.aop.AfterSaleLogAspect;
import java.util.Map;
/**
* 操作日志工具类
* 目前主要的作用,是提供给业务代码,记录操作明细和拓展字段
@@ -19,4 +21,13 @@ public class AfterSaleLogUtils {
AfterSaleLogAspect.setAfterStatus(status);
}
public static void setAfterSaleInfo(Long id, Integer beforeStatus, Integer afterStatus) {
setAfterSaleInfo(id, beforeStatus, afterStatus, null);
}
public static void setAfterSaleInfo(Long id, Integer beforeStatus, Integer afterStatus,
Map<String, Object> exts) {
// TODO 待实现
}
}

View File

@@ -14,13 +14,13 @@ import javax.annotation.Resource;
* @author owen
*/
@Component
@TenantJob
public class BrokerageRecordUnfreezeJob implements JobHandler {
@Resource
private BrokerageRecordService brokerageRecordService;
@Override
@TenantJob
public String execute(String param) {
int count = brokerageRecordService.unfreezeRecord();
return StrUtil.format("解冻佣金 {} 个", count);

View File

@@ -13,13 +13,13 @@ import javax.annotation.Resource;
* @author 芋道源码
*/
@Component
@TenantJob
public class TradeOrderAutoCancelJob implements JobHandler {
@Resource
private TradeOrderUpdateService tradeOrderUpdateService;
@Override
@TenantJob
public String execute(String param) {
int count = tradeOrderUpdateService.cancelOrderBySystem();
return String.format("过期订单 %s 个", count);

View File

@@ -13,13 +13,13 @@ import javax.annotation.Resource;
* @author 芋道源码
*/
@Component
@TenantJob
public class TradeOrderAutoCommentJob implements JobHandler {
@Resource
private TradeOrderUpdateService tradeOrderUpdateService;
@Override
@TenantJob
public String execute(String param) {
int count = tradeOrderUpdateService.createOrderItemCommentBySystem();
return String.format("评论订单 %s 个", count);

View File

@@ -13,13 +13,13 @@ import javax.annotation.Resource;
* @author 芋道源码
*/
@Component
@TenantJob
public class TradeOrderAutoReceiveJob implements JobHandler {
@Resource
private TradeOrderUpdateService tradeOrderUpdateService;
@Override
@TenantJob
public String execute(String param) {
int count = tradeOrderUpdateService.receiveOrderBySystem();
return String.format("自动收货 %s 个", count);

View File

@@ -1,4 +0,0 @@
/**
* 占位文件,无特殊用途
*/
package cn.iocoder.yudao.module.trade.job;

View File

@@ -1,8 +1,8 @@
package cn.iocoder.yudao.module.trade.service.aftersale;
import cn.hutool.core.map.MapUtil;
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;
@@ -17,18 +17,23 @@ import cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppTradeAfterSa
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.dal.dataobject.aftersale.TradeAfterSaleLogDO;
import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressDO;
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
import cn.iocoder.yudao.module.trade.dal.mysql.aftersale.TradeAfterSaleLogMapper;
import cn.iocoder.yudao.module.trade.dal.mysql.aftersale.TradeAfterSaleMapper;
import cn.iocoder.yudao.module.trade.enums.aftersale.AfterSaleOperateTypeEnum;
import cn.iocoder.yudao.module.trade.enums.aftersale.TradeAfterSaleStatusEnum;
import cn.iocoder.yudao.module.trade.enums.aftersale.TradeAfterSaleTypeEnum;
import cn.iocoder.yudao.module.trade.enums.aftersale.TradeAfterSaleWayEnum;
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderItemAfterSaleStatusEnum;
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum;
import cn.iocoder.yudao.module.trade.framework.aftersalelog.core.annotations.AfterSaleLog;
import cn.iocoder.yudao.module.trade.framework.aftersalelog.core.dto.TradeAfterSaleLogCreateReqDTO;
import cn.iocoder.yudao.module.trade.framework.aftersalelog.core.service.AfterSaleLogService;
import cn.iocoder.yudao.module.trade.framework.aftersalelog.core.util.AfterSaleLogUtils;
import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties;
import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressService;
import cn.iocoder.yudao.module.trade.service.order.TradeOrderQueryService;
import cn.iocoder.yudao.module.trade.service.order.TradeOrderUpdateService;
import lombok.extern.slf4j.Slf4j;
@@ -61,6 +66,8 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService, AfterSa
private TradeOrderUpdateService tradeOrderUpdateService;
@Resource
private TradeOrderQueryService tradeOrderQueryService;
@Resource
private DeliveryExpressService deliveryExpressService;
@Resource
private TradeAfterSaleMapper tradeAfterSaleMapper;
@@ -93,10 +100,9 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService, AfterSa
return tradeAfterSaleMapper.selectById(id);
}
// TODO 芋艿:拼团失败,要不要发起售后的方式退款?还是走取消逻辑?
@Override
@Transactional(rollbackFor = Exception.class)
@AfterSaleLog(operateType = AfterSaleOperateTypeEnum.MEMBER_CREATE)
public Long createAfterSale(Long userId, AppTradeAfterSaleCreateReqVO createReqVO) {
// 第一步,前置校验
TradeOrderItemDO tradeOrderItem = validateOrderItemApplicable(userId, createReqVO);
@@ -119,12 +125,10 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService, AfterSa
if (orderItem == null) {
throw exception(ORDER_ITEM_NOT_FOUND);
}
// 已申请售后,不允许再发起售后申请
if (!TradeOrderItemAfterSaleStatusEnum.isNone(orderItem.getAfterSaleStatus())) {
throw exception(AFTER_SALE_CREATE_FAIL_ORDER_ITEM_APPLIED);
}
// 申请的退款金额,不能超过商品的价格
if (createReqVO.getRefundPrice() > orderItem.getPayPrice()) {
throw exception(AFTER_SALE_CREATE_FAIL_REFUND_PRICE_ERROR);
@@ -163,15 +167,14 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService, AfterSa
afterSale.setOrderNo(order.getNo()); // 记录 orderNo 订单流水,方便后续检索
afterSale.setType(TradeOrderStatusEnum.isCompleted(order.getStatus())
? TradeAfterSaleTypeEnum.AFTER_SALE.getType() : TradeAfterSaleTypeEnum.IN_SALE.getType());
// TODO 退还积分
tradeAfterSaleMapper.insert(afterSale);
// 更新交易订单项的售后状态
tradeOrderUpdateService.updateOrderItemWhenAfterSaleCreate(orderItem.getId(), afterSale.getId());
// 记录售后日志
createAfterSaleLog(orderItem.getUserId(), UserTypeEnum.MEMBER.getValue(),
afterSale, null, afterSale.getStatus());
AfterSaleLogUtils.setAfterSaleInfo(afterSale.getId(), null,
TradeAfterSaleStatusEnum.APPLY.getStatus());
// TODO 发送售后消息
return afterSale;
@@ -179,6 +182,7 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService, AfterSa
@Override
@Transactional(rollbackFor = Exception.class)
@AfterSaleLog(operateType = AfterSaleOperateTypeEnum.ADMIN_AGREE_APPLY)
public void agreeAfterSale(Long userId, Long id) {
// 校验售后单存在,并状态未审批
TradeAfterSaleDO afterSale = validateAfterSaleAuditable(id);
@@ -192,14 +196,14 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService, AfterSa
.setStatus(newStatus).setAuditUserId(userId).setAuditTime(LocalDateTime.now()));
// 记录售后日志
createAfterSaleLog(userId, UserTypeEnum.ADMIN.getValue(),
afterSale, afterSale.getStatus(), newStatus);
AfterSaleLogUtils.setAfterSaleInfo(afterSale.getId(), afterSale.getStatus(), newStatus);
// TODO 发送售后消息
}
@Override
@Transactional(rollbackFor = Exception.class)
@AfterSaleLog(operateType = AfterSaleOperateTypeEnum.ADMIN_DISAGREE_APPLY)
public void disagreeAfterSale(Long userId, TradeAfterSaleDisagreeReqVO auditReqVO) {
// 校验售后单存在,并状态未审批
TradeAfterSaleDO afterSale = validateAfterSaleAuditable(auditReqVO.getId());
@@ -211,8 +215,7 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService, AfterSa
.setAuditReason(auditReqVO.getAuditReason()));
// 记录售后日志
createAfterSaleLog(userId, UserTypeEnum.ADMIN.getValue(),
afterSale, afterSale.getStatus(), newStatus);
AfterSaleLogUtils.setAfterSaleInfo(afterSale.getId(), afterSale.getStatus(), newStatus);
// TODO 发送售后消息
@@ -246,6 +249,7 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService, AfterSa
@Override
@Transactional(rollbackFor = Exception.class)
@AfterSaleLog(operateType = AfterSaleOperateTypeEnum.MEMBER_DELIVERY)
public void deliveryAfterSale(Long userId, AppTradeAfterSaleDeliveryReqVO deliveryReqVO) {
// 校验售后单存在,并状态未退货
TradeAfterSaleDO afterSale = tradeAfterSaleMapper.selectById(deliveryReqVO.getId());
@@ -255,6 +259,7 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService, AfterSa
if (ObjectUtil.notEqual(afterSale.getStatus(), TradeAfterSaleStatusEnum.SELLER_AGREE.getStatus())) {
throw exception(AFTER_SALE_DELIVERY_FAIL_STATUS_NOT_SELLER_AGREE);
}
DeliveryExpressDO express = deliveryExpressService.validateDeliveryExpress(deliveryReqVO.getLogisticsId());
// 更新售后单的物流信息
updateAfterSaleStatus(afterSale.getId(), TradeAfterSaleStatusEnum.SELLER_AGREE.getStatus(), new TradeAfterSaleDO()
@@ -263,14 +268,17 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService, AfterSa
.setDeliveryTime(LocalDateTime.now()));
// 记录售后日志
createAfterSaleLog(userId, UserTypeEnum.MEMBER.getValue(),
afterSale, afterSale.getStatus(), TradeAfterSaleStatusEnum.BUYER_DELIVERY.getStatus());
AfterSaleLogUtils.setAfterSaleInfo(afterSale.getId(), afterSale.getStatus(),
TradeAfterSaleStatusEnum.BUYER_DELIVERY.getStatus(),
MapUtil.<String, Object>builder().put("expressName", express.getName())
.put("logisticsNo", deliveryReqVO.getLogisticsNo()).build());
// TODO 发送售后消息
}
@Override
@Transactional(rollbackFor = Exception.class)
@AfterSaleLog(operateType = AfterSaleOperateTypeEnum.ADMIN_AGREE_RECEIVE)
public void receiveAfterSale(Long userId, Long id) {
// 校验售后单存在,并状态为已退货
TradeAfterSaleDO afterSale = validateAfterSaleReceivable(id);
@@ -280,14 +288,15 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService, AfterSa
.setStatus(TradeAfterSaleStatusEnum.WAIT_REFUND.getStatus()).setReceiveTime(LocalDateTime.now()));
// 记录售后日志
createAfterSaleLog(userId, UserTypeEnum.ADMIN.getValue(),
afterSale, afterSale.getStatus(), TradeAfterSaleStatusEnum.WAIT_REFUND.getStatus());
AfterSaleLogUtils.setAfterSaleInfo(afterSale.getId(), afterSale.getStatus(),
TradeAfterSaleStatusEnum.WAIT_REFUND.getStatus());
// TODO 发送售后消息
}
@Override
@Transactional(rollbackFor = Exception.class)
@AfterSaleLog(operateType = AfterSaleOperateTypeEnum.ADMIN_DISAGREE_RECEIVE)
public void refuseAfterSale(Long userId, TradeAfterSaleRefuseReqVO refuseReqVO) {
// 校验售后单存在,并状态为已退货
TradeAfterSaleDO afterSale = tradeAfterSaleMapper.selectById(refuseReqVO.getId());
@@ -304,8 +313,9 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService, AfterSa
.setReceiveReason(refuseReqVO.getRefuseMemo()));
// 记录售后日志
createAfterSaleLog(userId, UserTypeEnum.ADMIN.getValue(),
afterSale, afterSale.getStatus(), TradeAfterSaleStatusEnum.SELLER_REFUSE.getStatus());
AfterSaleLogUtils.setAfterSaleInfo(afterSale.getId(), afterSale.getStatus(),
TradeAfterSaleStatusEnum.SELLER_REFUSE.getStatus(),
MapUtil.of("reason", refuseReqVO.getRefuseMemo()));
// TODO 发送售后消息
@@ -332,6 +342,7 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService, AfterSa
@Override
@Transactional(rollbackFor = Exception.class)
@AfterSaleLog(operateType = AfterSaleOperateTypeEnum.ADMIN_REFUND)
public void refundAfterSale(Long userId, String userIp, Long id) {
// 校验售后单的状态,并状态待退款
TradeAfterSaleDO afterSale = tradeAfterSaleMapper.selectById(id);
@@ -350,8 +361,8 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService, AfterSa
.setStatus(TradeAfterSaleStatusEnum.COMPLETE.getStatus()).setRefundTime(LocalDateTime.now()));
// 记录售后日志
createAfterSaleLog(userId, UserTypeEnum.ADMIN.getValue(),
afterSale, afterSale.getStatus(), TradeAfterSaleStatusEnum.COMPLETE.getStatus());
AfterSaleLogUtils.setAfterSaleInfo(afterSale.getId(), afterSale.getStatus(),
TradeAfterSaleStatusEnum.COMPLETE.getStatus());
// TODO 发送售后消息
@@ -374,6 +385,8 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService, AfterSa
}
@Override
@Transactional(rollbackFor = Exception.class)
@AfterSaleLog(operateType = AfterSaleOperateTypeEnum.MEMBER_CANCEL)
public void cancelAfterSale(Long userId, Long id) {
// 校验售后单的状态,并状态待退款
TradeAfterSaleDO afterSale = tradeAfterSaleMapper.selectById(id);
@@ -391,8 +404,8 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService, AfterSa
.setStatus(TradeAfterSaleStatusEnum.BUYER_CANCEL.getStatus()));
// 记录售后日志
createAfterSaleLog(userId, UserTypeEnum.MEMBER.getValue(),
afterSale, afterSale.getStatus(), TradeAfterSaleStatusEnum.BUYER_CANCEL.getStatus());
AfterSaleLogUtils.setAfterSaleInfo(afterSale.getId(), afterSale.getStatus(),
TradeAfterSaleStatusEnum.BUYER_CANCEL.getStatus());
// TODO 发送售后消息

View File

@@ -8,7 +8,7 @@ import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.user.AppBrokera
import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.BrokerageRecordDO;
import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordBizTypeEnum;
import cn.iocoder.yudao.module.trade.service.brokerage.bo.BrokerageAddReqBO;
import cn.iocoder.yudao.module.trade.service.brokerage.bo.UserBrokerageSummaryBO;
import cn.iocoder.yudao.module.trade.service.brokerage.bo.UserBrokerageSummaryRespBO;
import javax.validation.Valid;
import java.time.LocalDateTime;
@@ -61,6 +61,19 @@ public interface BrokerageRecordService {
*/
void addBrokerage(Long userId, BrokerageRecordBizTypeEnum bizType, String bizId, Integer brokeragePrice, String title);
/**
* 减少佣金【只针对自己】
*
* @param userId 会员编号
* @param bizType 业务类型
* @param bizId 业务编号
* @param brokeragePrice 佣金
* @param title 标题
*/
default void reduceBrokerage(Long userId, BrokerageRecordBizTypeEnum bizType, String bizId, Integer brokeragePrice, String title) {
addBrokerage(userId, bizType, bizId, -brokeragePrice, title);
}
/**
* 取消佣金:将佣金记录,状态修改为已失效
*
@@ -78,25 +91,28 @@ public interface BrokerageRecordService {
int unfreezeRecord();
/**
* 汇总用户佣金
* 按照 userId汇总每个用户佣金
*
* @param userIds 用户编号
* @param bizType 业务类型
* @param status 佣金状态
* @return 用户佣金汇总
* @return 用户佣金汇总 List
*/
List<UserBrokerageSummaryBO> getUserBrokerageSummaryByUserId(Collection<Long> userIds, Integer bizType, Integer status);
List<UserBrokerageSummaryRespBO> getUserBrokerageSummaryListByUserId(Collection<Long> userIds,
Integer bizType, Integer status);
/**
* 汇总用户佣金
* 按照 userId汇总每个用户佣金
*
* @param userIds 用户编号
* @param bizType 业务类型
* @param status 佣金状态
* @return 用户佣金汇总
* @return 用户佣金汇总 Map
*/
default Map<Long, UserBrokerageSummaryBO> getUserBrokerageSummaryMapByUserId(Collection<Long> userIds, Integer bizType, Integer status) {
return convertMap(getUserBrokerageSummaryByUserId(userIds, bizType, status), UserBrokerageSummaryBO::getUserId);
default Map<Long, UserBrokerageSummaryRespBO> getUserBrokerageSummaryMapByUserId(Collection<Long> userIds,
Integer bizType, Integer status) {
return convertMap(getUserBrokerageSummaryListByUserId(userIds, bizType, status),
UserBrokerageSummaryRespBO::getUserId);
}
/**
@@ -116,7 +132,8 @@ public interface BrokerageRecordService {
* @param pageReqVO 分页查询
* @return 排行榜分页
*/
PageResult<AppBrokerageUserRankByPriceRespVO> getBrokerageUserChildSummaryPageByPrice(AppBrokerageUserRankPageReqVO pageReqVO);
PageResult<AppBrokerageUserRankByPriceRespVO> getBrokerageUserChildSummaryPageByPrice(
AppBrokerageUserRankPageReqVO pageReqVO);
/**
* 获取用户的排名(基于佣金总数)
@@ -130,9 +147,10 @@ public interface BrokerageRecordService {
/**
* 计算商品被购买后,推广员可以得到的佣金
*
* @param spuId 商品编号
* @param userId 用户编号
* @param spuId 商品编号
* @return 用户佣金
*/
AppBrokerageProductPriceRespVO calculateProductBrokeragePrice(Long spuId, Long userId);
AppBrokerageProductPriceRespVO calculateProductBrokeragePrice(Long userId, Long spuId);
}

View File

@@ -26,7 +26,7 @@ import cn.iocoder.yudao.module.trade.dal.mysql.brokerage.BrokerageRecordMapper;
import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordBizTypeEnum;
import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordStatusEnum;
import cn.iocoder.yudao.module.trade.service.brokerage.bo.BrokerageAddReqBO;
import cn.iocoder.yudao.module.trade.service.brokerage.bo.UserBrokerageSummaryBO;
import cn.iocoder.yudao.module.trade.service.brokerage.bo.UserBrokerageSummaryRespBO;
import cn.iocoder.yudao.module.trade.service.config.TradeConfigService;
import com.baomidou.mybatisplus.core.metadata.IPage;
import lombok.extern.slf4j.Slf4j;
@@ -231,24 +231,55 @@ public class BrokerageRecordServiceImpl implements BrokerageRecordService {
return count;
}
/**
* 激动单条佣金记录
*
* @param record 佣金记录
* @return 解冻是否成功
*/
@Transactional(rollbackFor = Exception.class)
public boolean unfreezeRecord(BrokerageRecordDO record) {
// 更新记录状态
BrokerageRecordDO updateObj = new BrokerageRecordDO()
.setStatus(BrokerageRecordStatusEnum.SETTLEMENT.getStatus())
.setUnfreezeTime(LocalDateTime.now());
int updateRows = brokerageRecordMapper.updateByIdAndStatus(record.getId(), record.getStatus(), updateObj);
if (updateRows == 0) {
log.error("[unfreezeRecord][record({}) 更新为已结算失败]", record.getId());
return false;
}
// 更新用户冻结佣金
brokerageUserService.updateFrozenPriceDecrAndPriceIncr(record.getUserId(), -record.getPrice());
log.info("[unfreezeRecord][record({}) 更新为已结算成功]", record.getId());
return true;
}
@Override
public List<UserBrokerageSummaryBO> getUserBrokerageSummaryByUserId(Collection<Long> userIds, Integer bizType, Integer status) {
public List<UserBrokerageSummaryRespBO> getUserBrokerageSummaryListByUserId(Collection<Long> userIds,
Integer bizType, Integer status) {
if (CollUtil.isEmpty(userIds)) {
return Collections.emptyList();
}
return brokerageRecordMapper.selectCountAndSumPriceByUserIdInAndBizTypeAndStatus(userIds, bizType, status);
}
@Override
public Integer getSummaryPriceByUserId(Long userId, Integer bizType, LocalDateTime beginTime, LocalDateTime endTime) {
return brokerageRecordMapper.selectSummaryPriceByUserIdAndBizTypeAndCreateTimeBetween(userId, bizType, beginTime, endTime);
return brokerageRecordMapper.selectSummaryPriceByUserIdAndBizTypeAndCreateTimeBetween(userId, bizType,
beginTime, endTime);
}
@Override
public PageResult<AppBrokerageUserRankByPriceRespVO> getBrokerageUserChildSummaryPageByPrice(AppBrokerageUserRankPageReqVO pageReqVO) {
IPage<AppBrokerageUserRankByPriceRespVO> pageResult = brokerageRecordMapper.selectSummaryPricePageGroupByUserId(MyBatisUtils.buildPage(pageReqVO),
IPage<AppBrokerageUserRankByPriceRespVO> pageResult = brokerageRecordMapper.selectSummaryPricePageGroupByUserId(
MyBatisUtils.buildPage(pageReqVO),
BrokerageRecordBizTypeEnum.ORDER.getType(), BrokerageRecordStatusEnum.SETTLEMENT.getStatus(),
ArrayUtil.get(pageReqVO.getTimes(), 0), ArrayUtil.get(pageReqVO.getTimes(), 1));
return new PageResult<>(pageResult.getRecords(), pageResult.getTotal());
}
// TODO @疯狂:这个要不我们先做精准的?先查询自己的推广金额,然后查询比该金额多的有多少人?
@Override
public Integer getUserRankByPrice(Long userId, LocalDateTime[] times) {
AppBrokerageUserRankPageReqVO pageParam = new AppBrokerageUserRankPageReqVO().setTimes(times);
@@ -285,26 +316,8 @@ public class BrokerageRecordServiceImpl implements BrokerageRecordService {
brokerageRecordMapper.insert(record);
}
@Transactional(rollbackFor = Exception.class)
public boolean unfreezeRecord(BrokerageRecordDO record) {
// 更新记录状态
BrokerageRecordDO updateObj = new BrokerageRecordDO()
.setStatus(BrokerageRecordStatusEnum.SETTLEMENT.getStatus())
.setUnfreezeTime(LocalDateTime.now());
int updateRows = brokerageRecordMapper.updateByIdAndStatus(record.getId(), record.getStatus(), updateObj);
if (updateRows == 0) {
log.error("[unfreezeRecord][record({}) 更新为已结算失败]", record.getId());
return false;
}
// 更新用户冻结佣金
brokerageUserService.updateFrozenPriceDecrAndPriceIncr(record.getUserId(), -record.getPrice());
log.info("[unfreezeRecord][record({}) 更新为已结算成功]", record.getId());
return true;
}
@Override
public AppBrokerageProductPriceRespVO calculateProductBrokeragePrice(Long spuId, Long userId) {
public AppBrokerageProductPriceRespVO calculateProductBrokeragePrice(Long userId, Long spuId) {
// 1. 构建默认的返回值
AppBrokerageProductPriceRespVO respVO = new AppBrokerageProductPriceRespVO().setEnabled(false)
.setBrokerageMinPrice(0).setBrokerageMaxPrice(0);

View File

@@ -9,7 +9,6 @@ import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.user.AppBrokera
import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.BrokerageUserDO;
import javax.validation.constraints.NotNull;
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.List;
@@ -107,10 +106,9 @@ public interface BrokerageUserService {
*
* @param userId 用户编号
* @param bindUserId 推广员编号
* @param registerTime 用户注册时间
* @return 是否绑定
*/
boolean bindBrokerageUser(@NotNull Long userId, @NotNull Long bindUserId, @NotNull LocalDateTime registerTime);
boolean bindBrokerageUser(@NotNull Long userId, @NotNull Long bindUserId);
/**
* 获取用户是否有分销资格

View File

@@ -7,6 +7,8 @@ import cn.hutool.core.util.BooleanUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;
import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
import cn.iocoder.yudao.module.trade.controller.admin.brokerage.vo.user.BrokerageUserPageReqVO;
import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.user.AppBrokerageUserChildSummaryPageReqVO;
import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.user.AppBrokerageUserChildSummaryRespVO;
@@ -24,10 +26,7 @@ import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.*;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
@@ -48,6 +47,9 @@ public class BrokerageUserServiceImpl implements BrokerageUserService {
@Resource
private TradeConfigService tradeConfigService;
@Resource
private MemberUserApi memberUserApi;
@Override
public BrokerageUserDO getBrokerageUser(Long id) {
return brokerageUserMapper.selectById(id);
@@ -155,7 +157,7 @@ public class BrokerageUserServiceImpl implements BrokerageUserService {
}
@Override
public boolean bindBrokerageUser(Long userId, Long bindUserId, LocalDateTime registerTime) {
public boolean bindBrokerageUser(Long userId, Long bindUserId) {
// 1. 获得分销用户
boolean isNewBrokerageUser = false;
BrokerageUserDO brokerageUser = brokerageUserMapper.selectById(userId);
@@ -165,7 +167,7 @@ public class BrokerageUserServiceImpl implements BrokerageUserService {
}
// 2.1 校验是否能绑定用户
boolean validated = isUserCanBind(brokerageUser, registerTime);
boolean validated = isUserCanBind(brokerageUser);
if (!validated) {
return false;
}
@@ -223,7 +225,7 @@ public class BrokerageUserServiceImpl implements BrokerageUserService {
return new PageResult<>(pageResult.getRecords(), pageResult.getTotal());
}
private boolean isUserCanBind(BrokerageUserDO user, LocalDateTime registerTime) {
private boolean isUserCanBind(BrokerageUserDO user) {
// 校验分销功能是否启用
TradeConfigDO tradeConfig = tradeConfigService.getTradeConfig();
if (tradeConfig == null || !BooleanUtil.isTrue(tradeConfig.getBrokerageEnabled())) {
@@ -237,9 +239,8 @@ public class BrokerageUserServiceImpl implements BrokerageUserService {
// 校验分销关系绑定模式
if (BrokerageBindModeEnum.REGISTER.getMode().equals(tradeConfig.getBrokerageBindMode())) {
// 判断是否为新用户注册时间在30秒内的都算新用户
boolean isNotNewUser = LocalDateTimeUtils.beforeNow(registerTime.plusSeconds(30));
if (isNotNewUser) {
// 判断是否为新用户:注册时间在 30 秒内的,都算新用户
if (!isNewRegisterUser(user.getId())) {
throw exception(BROKERAGE_BIND_MODE_REGISTER); // 只有在注册时可以绑定
}
} else if (BrokerageBindModeEnum.ANYTIME.getMode().equals(tradeConfig.getBrokerageBindMode())) {
@@ -247,14 +248,29 @@ public class BrokerageUserServiceImpl implements BrokerageUserService {
throw exception(BROKERAGE_BIND_OVERRIDE); // 已绑定了推广人
}
}
return true;
}
/**
* 判断是否为新用户
*
* 标准:注册时间在 30 秒内的,都算新用户
*
* 疑问:为什么通过这样的方式实现?
* 回答:因为注册在 member 模块,希望它和 trade 模块解耦,所以只能用这种约定的逻辑。
*
* @param userId 用户编号
* @return 是否新用户
*/
private boolean isNewRegisterUser(Long userId) {
MemberUserRespDTO user = memberUserApi.getUser(userId);
return user != null && LocalDateTimeUtils.beforeNow(user.getCreateTime().plusSeconds(30));
}
private void validateCanBindUser(BrokerageUserDO user, Long bindUserId) {
// 校验要绑定的用户有无推广资格
BrokerageUserDO bindUser = brokerageUserMapper.selectById(bindUserId);
if (bindUser == null || !BooleanUtil.isTrue(bindUser.getBrokerageEnabled())) {
if (bindUser == null || BooleanUtil.isFalse(bindUser.getBrokerageEnabled())) {
throw exception(BROKERAGE_BIND_USER_NOT_ENABLED);
}
@@ -285,6 +301,9 @@ public class BrokerageUserServiceImpl implements BrokerageUserService {
* @return 绑定用户编号列表
*/
private List<Long> buildBindUserIdsByLevel(Long bindUserId, Integer level) {
if (bindUserId == null) {
return Collections.emptyList();
}
Assert.isTrue(level == null || level <= 2, "目前只支持 level 小于等于 2");
List<Long> bindUserIds = CollUtil.newArrayList();
if (level == null || level == 1) {

View File

@@ -5,7 +5,7 @@ import cn.iocoder.yudao.module.trade.controller.admin.brokerage.vo.withdraw.Brok
import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.withdraw.AppBrokerageWithdrawCreateReqVO;
import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.BrokerageWithdrawDO;
import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageWithdrawStatusEnum;
import cn.iocoder.yudao.module.trade.service.brokerage.bo.UserWithdrawSummaryBO;
import cn.iocoder.yudao.module.trade.service.brokerage.bo.BrokerageWithdrawSummaryRespBO;
import java.util.Collection;
import java.util.List;
@@ -49,29 +49,32 @@ public interface BrokerageWithdrawService {
/**
* 【会员】创建佣金提现
*
* @param createReqVO 创建信息
* @param userId 会员用户编号
* @param createReqVO 创建信息
* @return 佣金提现编号
*/
Long createBrokerageWithdraw(AppBrokerageWithdrawCreateReqVO createReqVO, Long userId);
Long createBrokerageWithdraw(Long userId, AppBrokerageWithdrawCreateReqVO createReqVO);
/**
* 汇总用户提现
* 按照 userId汇总每个用户提现
*
* @param userIds 用户编号
* @param status 提现状态
* @return 用户提现汇总
* @return 用户提现汇总 List
*/
List<UserWithdrawSummaryBO> getWithdrawSummaryByUserId(Collection<Long> userIds, BrokerageWithdrawStatusEnum status);
List<BrokerageWithdrawSummaryRespBO> getWithdrawSummaryListByUserId(Collection<Long> userIds,
BrokerageWithdrawStatusEnum status);
/**
* 汇总用户提现
* 按照 userId汇总每个用户提现
*
* @param userIds 用户编号
* @param status 提现状态
* @return 用户提现汇总
* @return 用户提现汇总 Map
*/
default Map<Long, UserWithdrawSummaryBO> getWithdrawSummaryMapByUserId(Set<Long> userIds, BrokerageWithdrawStatusEnum status) {
return convertMap(getWithdrawSummaryByUserId(userIds, status), UserWithdrawSummaryBO::getUserId);
default Map<Long, BrokerageWithdrawSummaryRespBO> getWithdrawSummaryMapByUserId(Set<Long> userIds,
BrokerageWithdrawStatusEnum status) {
return convertMap(getWithdrawSummaryListByUserId(userIds, status), BrokerageWithdrawSummaryRespBO::getUserId);
}
}

View File

@@ -1,5 +1,6 @@
package cn.iocoder.yudao.module.trade.service.brokerage;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
@@ -17,7 +18,7 @@ import cn.iocoder.yudao.module.trade.enums.MessageTemplateConstants;
import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordBizTypeEnum;
import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageWithdrawStatusEnum;
import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageWithdrawTypeEnum;
import cn.iocoder.yudao.module.trade.service.brokerage.bo.UserWithdrawSummaryBO;
import cn.iocoder.yudao.module.trade.service.brokerage.bo.BrokerageWithdrawSummaryRespBO;
import cn.iocoder.yudao.module.trade.service.config.TradeConfigService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -27,6 +28,7 @@ import javax.annotation.Resource;
import javax.validation.Validator;
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -67,29 +69,27 @@ public class BrokerageWithdrawServiceImpl implements BrokerageWithdrawService {
}
// 2. 更新
BrokerageWithdrawDO updateObj = new BrokerageWithdrawDO()
.setStatus(status.getStatus())
.setAuditReason(auditReason)
.setAuditTime(LocalDateTime.now());
int rows = brokerageWithdrawMapper.updateByIdAndStatus(id, BrokerageWithdrawStatusEnum.AUDITING.getStatus(), updateObj);
int rows = brokerageWithdrawMapper.updateByIdAndStatus(id, BrokerageWithdrawStatusEnum.AUDITING.getStatus(),
new BrokerageWithdrawDO().setStatus(status.getStatus()).setAuditReason(auditReason).setAuditTime(LocalDateTime.now()));
if (rows == 0) {
throw exception(BROKERAGE_WITHDRAW_STATUS_NOT_AUDITING);
}
String templateCode = MessageTemplateConstants.BROKERAGE_WITHDRAW_AUDIT_APPROVE;
String templateCode;
if (BrokerageWithdrawStatusEnum.AUDIT_SUCCESS.equals(status)) {
templateCode = MessageTemplateConstants.BROKERAGE_WITHDRAW_AUDIT_APPROVE;
// 3.1 通过时佣金转余额
if (BrokerageWithdrawTypeEnum.WALLET.getType().equals(withdraw.getType())) {
// todo
// todo 疯狂:
}
// TODO 疯狂:调用转账接口
} else if (BrokerageWithdrawStatusEnum.AUDIT_FAIL.equals(status)) {
templateCode = MessageTemplateConstants.BROKERAGE_WITHDRAW_AUDIT_REJECT;
// 3.2 驳回时需要退还用户佣金
brokerageRecordService.addBrokerage(withdraw.getUserId(), BrokerageRecordBizTypeEnum.WITHDRAW_REJECT,
String.valueOf(withdraw.getId()), withdraw.getPrice(), BrokerageRecordBizTypeEnum.WITHDRAW_REJECT.getTitle());
} else {
throw new IllegalArgumentException("不支持的提现状态");
throw new IllegalArgumentException("不支持的提现状态" + status);
}
// 4. 通知用户
@@ -98,10 +98,8 @@ public class BrokerageWithdrawServiceImpl implements BrokerageWithdrawService {
.put("price", MoneyUtils.fenToYuanStr(withdraw.getPrice()))
.put("reason", withdraw.getAuditReason())
.build();
NotifySendSingleToUserReqDTO reqDTO = new NotifySendSingleToUserReqDTO()
.setUserId(withdraw.getUserId())
.setTemplateCode(templateCode).setTemplateParams(templateParams);
notifyMessageSendApi.sendSingleMessageToMember(reqDTO);
notifyMessageSendApi.sendSingleMessageToMember(new NotifySendSingleToUserReqDTO()
.setUserId(withdraw.getUserId()).setTemplateCode(templateCode).setTemplateParams(templateParams));
}
private BrokerageWithdrawDO validateBrokerageWithdrawExists(Integer id) {
@@ -124,27 +122,31 @@ public class BrokerageWithdrawServiceImpl implements BrokerageWithdrawService {
@Override
@Transactional(rollbackFor = Exception.class)
public Long createBrokerageWithdraw(AppBrokerageWithdrawCreateReqVO createReqVO, Long userId) {
// 校验提现金额
public Long createBrokerageWithdraw(Long userId, AppBrokerageWithdrawCreateReqVO createReqVO) {
// 1.1 校验提现金额
TradeConfigDO tradeConfig = validateWithdrawPrice(createReqVO.getPrice());
// 校验提现参数
// 1.2 校验提现参数
createReqVO.validate(validator);
// 计算手续费
// 2.1 计算手续费
Integer feePrice = calculateFeePrice(createReqVO.getPrice(), tradeConfig.getBrokerageWithdrawFeePercent());
// 创建佣金提现记录
// 2.2 创建佣金提现记录
BrokerageWithdrawDO withdraw = BrokerageWithdrawConvert.INSTANCE.convert(createReqVO, userId, feePrice);
brokerageWithdrawMapper.insert(withdraw);
// 创建用户佣金记录
brokerageRecordService.addBrokerage(userId, BrokerageRecordBizTypeEnum.WITHDRAW, String.valueOf(withdraw.getId()),
-createReqVO.getPrice(), BrokerageRecordBizTypeEnum.WITHDRAW.getTitle());
// 3. 创建用户佣金记录
// 注意佣金是否充足reduceBrokerage 已经进行校验
brokerageRecordService.reduceBrokerage(userId, BrokerageRecordBizTypeEnum.WITHDRAW, String.valueOf(withdraw.getId()),
createReqVO.getPrice(), BrokerageRecordBizTypeEnum.WITHDRAW.getTitle());
return withdraw.getId();
}
@Override
public List<UserWithdrawSummaryBO> getWithdrawSummaryByUserId(Collection<Long> userIds, BrokerageWithdrawStatusEnum status) {
public List<BrokerageWithdrawSummaryRespBO> getWithdrawSummaryListByUserId(Collection<Long> userIds,
BrokerageWithdrawStatusEnum status) {
if (CollUtil.isEmpty(userIds)) {
return Collections.emptyList();
}
return brokerageWithdrawMapper.selectCountAndSumPriceByUserIdAndStatus(userIds, status.getStatus());
}

View File

@@ -5,14 +5,14 @@ import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 用户佣金提现合计 BO
* 佣金提现合计 BO
*
* @author owen
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserWithdrawSummaryBO {
public class BrokerageWithdrawSummaryRespBO {
/**
* 用户编号

View File

@@ -12,13 +12,14 @@ import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserBrokerageSummaryBO {
public class UserBrokerageSummaryRespBO {
/**
* 用户编号
*/
private Long userId;
/**
* 佣金数量
* 推广数量
*/
private Integer count;
/**

View File

@@ -26,7 +26,7 @@ import static java.util.Collections.emptyList;
/**
* 购物车 Service 实现类
*
* // TODO 芋艿:未来优化:购物车的价格计算,支持营销信息;目前不支持的原因,前端界面需要前端 pr 支持下;
* // TODO 芋艿:未来优化:购物车的价格计算,支持营销信息;目前不支持的原因,前端界面需要前端 pr 支持下;例如说:会员价格;
*
* @author 芋道源码
*/

View File

@@ -125,7 +125,6 @@ public interface TradeOrderUpdateService {
*/
void updateOrderItemWhenAfterSaleCreate(@NotNull Long id, @NotNull Long afterSaleId);
/**
* 当售后完成后,更新交易订单项的售后状态
*

View File

@@ -663,6 +663,27 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
TradeOrderLogUtils.setOrderInfo(order.getId(), order.getStatus(), TradeOrderStatusEnum.CANCELED.getStatus());
}
/**
* 如果金额全部被退款,则取消订单
* 如果还有未被退款的金额,则无需取消订单
*
* @param order 订单
* @param refundPrice 退款金额
*/
@TradeOrderLog(operateType = TradeOrderOperateTypeEnum.ADMIN_CANCEL_AFTER_SALE)
public void cancelOrderByAfterSale(TradeOrderDO order, Integer refundPrice) {
// 1. 更新订单
if (refundPrice < order.getPayPrice()) {
return;
}
tradeOrderMapper.updateById(new TradeOrderDO().setId(order.getId())
.setStatus(TradeOrderStatusEnum.CANCELED.getStatus())
.setCancelType(TradeOrderCancelTypeEnum.AFTER_SALE_CLOSE.getType()).setCancelTime(LocalDateTime.now()));
// 2. 退还优惠券
couponApi.returnUsedCoupon(order.getCouponId());
}
@Override
@Transactional(rollbackFor = Exception.class)
@TradeOrderLog(operateType = TradeOrderOperateTypeEnum.MEMBER_DELETE)
@@ -753,6 +774,7 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
@Override
public void updateOrderItemWhenAfterSaleCreate(Long id, Long afterSaleId) {
// TODO @疯狂:这个可以直接在接口上,写 @Null 参数校验;
if (afterSaleId == null) {
throw new IllegalArgumentException(StrUtil.format("id({}) 退款发起,售后单编号不能为空", id));
}
@@ -765,6 +787,7 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
@Override
@Transactional(rollbackFor = Exception.class)
public void updateOrderItemWhenAfterSaleSuccess(Long id, Integer refundPrice) {
// TODO @疯狂:这个可以直接在接口上,写 @Null 参数校验;
if (refundPrice == null) {
throw new IllegalArgumentException(StrUtil.format("id({}) 退款成功,退款金额不能为空", id));
}
@@ -773,29 +796,35 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
updateOrderItemAfterSaleStatus(id, TradeOrderItemAfterSaleStatusEnum.APPLY.getStatus(),
TradeOrderItemAfterSaleStatusEnum.SUCCESS.getStatus(), null);
// 2. 计算总的退款金额、退回积分
// 2.1 更新订单的退款金额、积分
TradeOrderItemDO orderItem = tradeOrderItemMapper.selectById(id);
TradeOrderDO order = tradeOrderMapper.selectById(orderItem.getOrderId());
Integer orderRefundPrice = order.getRefundPrice() + refundPrice;
Integer orderRefundPoint = order.getRefundPoint() + orderItem.getUsePoint();
if (isAllOrderItemAfterSaleSuccess(order.getId())) { // 如果都售后成功,则需要取消订单
cancelOrderByAfterSale(order, orderRefundPrice, orderRefundPoint);
} else { // 如果部分售后,则更新退款金额
tradeOrderMapper.updateById(new TradeOrderDO().setId(order.getId())
.setRefundStatus(TradeOrderRefundStatusEnum.PART.getStatus())
.setRefundPrice(orderRefundPrice).setRefundPoint(orderRefundPoint));
}
Integer refundStatus = isAllOrderItemAfterSaleSuccess(order.getId()) ?
TradeOrderRefundStatusEnum.ALL.getStatus() // 如果都售后成功,则需要取消订单
: TradeOrderRefundStatusEnum.PART.getStatus();
tradeOrderMapper.updateById(new TradeOrderDO().setId(order.getId())
.setRefundStatus(refundStatus)
.setRefundPrice(orderRefundPrice).setRefundPoint(orderRefundPoint));
// 2.2 如果全部退款,则进行取消订单
getSelf().cancelOrderByAfterSale(order, orderRefundPrice);
// TODO 芋艿:这块扣减规则,需要在考虑下
// 3.1 回滚数据:增加 SKU 库存
// TODO @puhui999活动相关的回滚
// 3. 回滚库存
productSkuApi.updateSkuStock(TradeOrderConvert.INSTANCE.convert(Collections.singletonList(orderItem)));
// 3.2 回滚数据:扣减用户积分(赠送的)
// 4.1 回滚积分:扣减用户积分(赠送的)
reduceUserPoint(order.getUserId(), orderItem.getGivePoint(), MemberPointBizTypeEnum.AFTER_SALE_DEDUCT_GIVE, orderItem.getAfterSaleId());
// 3.3 回滚数据:增加用户积分(返还抵扣)
// 4.2 回滚积分:增加用户积分(返还抵扣)
addUserPoint(order.getUserId(), orderItem.getUsePoint(), MemberPointBizTypeEnum.AFTER_SALE_REFUND_USED, orderItem.getAfterSaleId());
// 3.4 回滚数据:扣减用户经验
// 5. 回滚经验:扣减用户经验
// TODO @疯狂orderRefundPrice 是不是改成 refundPrice应该只退这个售后对应的经验
getSelf().reduceUserExperienceAsync(order.getUserId(), orderRefundPrice, orderItem.getAfterSaleId());
// 3.5 回滚数据:更新分佣记录为已失效
// 6. 回滚佣金:更新分佣记录为已失效
getSelf().cancelBrokerageAsync(order.getUserId(), id);
}
@@ -815,18 +844,6 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
}
}
@TradeOrderLog(operateType = TradeOrderOperateTypeEnum.MEMBER_CANCEL)
private void cancelOrderByAfterSale(TradeOrderDO order, Integer orderRefundPrice, Integer refundPoint) {
// 1. 更新订单
tradeOrderMapper.updateById(new TradeOrderDO().setId(order.getId())
.setRefundStatus(TradeOrderRefundStatusEnum.ALL.getStatus())
.setRefundPrice(orderRefundPrice).setRefundPoint(refundPoint)
.setCancelType(TradeOrderCancelTypeEnum.AFTER_SALE_CLOSE.getType()).setCancelTime(LocalDateTime.now()));
// 2. 退还优惠券
couponApi.returnUsedCoupon(order.getCouponId());
}
/**
* 判断指定订单的所有订单项,是不是都售后成功
*

View File

@@ -87,10 +87,11 @@ public class TradePointUsePriceCalculator implements TradePriceCalculator {
if (config.getTradeDeductMaxPrice() != null && config.getTradeDeductMaxPrice() > 0) {
usePoint = Math.min(usePoint, config.getTradeDeductMaxPrice());
}
// TODO @疯狂:这里应该是,抵扣到只剩下 0.01
// 积分优惠金额(分)
int pointPrice = usePoint * config.getTradeDeductUnitPrice();
if (result.getPrice().getPayPrice() <= pointPrice) {
// 禁止0元购
// 禁止 0 元购
throw exception(PRICE_CALCULATE_PAY_PRICE_ILLEGAL);
}
// // 允许0 元购!!!:用户积分比较多时,积分可以抵扣的金额要大于支付金额,这时需要根据支付金额反推使用多少积分

View File

@@ -5,6 +5,7 @@
<select id="selectSummaryPageByUserId"
resultType="cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.user.AppBrokerageUserChildSummaryRespVO">
SELECT bu.id, bu.bind_user_time AS brokerageTime, u.nickname, u.avatar,
<!-- TODO @疯狂biz_type、status 可以通过参数传入哇? -->
(SELECT SUM(price) FROM trade_brokerage_record r
WHERE r.user_id = u.id AND biz_type = 1 AND r.status = 1 AND r.deleted = FALSE) AS brokeragePrice,
(SELECT COUNT(1) FROM trade_brokerage_record r
@@ -13,7 +14,8 @@
WHERE c.bind_user_id = u.id AND c.deleted = FALSE) AS brokerageUserCount
FROM member_user AS u
JOIN trade_brokerage_user AS bu ON bu.id = u.id
<where>
<!-- TODO @疯狂1bind_user_id 是不是可以先改成内存查询出 userId 的集合,然后去 in 哈2nickname 是不是可以基于查询出来的 userId 对应的 nickname 在内存里过滤,主要是避免和 member_user 链表 -->
<where>
<if test="param.nickname != null and param.nickname != ''">
AND u.nickname LIKE concat('', #{param.nickname}, '')
</if>