!616 完善分销功能

Merge pull request !616 from 疯狂的世界/brokerate
This commit is contained in:
芋道源码 2023-09-11 14:20:02 +00:00 committed by Gitee
commit 18c9f22560
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
25 changed files with 352 additions and 100 deletions

View File

@ -48,7 +48,7 @@ create table trade_brokerage_record
primary key, primary key,
user_id bigint not null comment '用户编号', user_id bigint not null comment '用户编号',
biz_id varchar(64) default '' not null comment '业务编号', biz_id varchar(64) default '' not null comment '业务编号',
biz_type tinyint default 0 not null comment '业务类型0-订单1-提现', biz_type tinyint default 0 not null comment '业务类型1-订单2-提现',
title varchar(64) default '' not null comment '标题', title varchar(64) default '' not null comment '标题',
price int default 0 not null comment '金额', price int default 0 not null comment '金额',
total_price int default 0 not null comment '当前总佣金', total_price int default 0 not null comment '当前总佣金',
@ -56,6 +56,8 @@ create table trade_brokerage_record
status tinyint default 0 not null comment '状态0-待结算1-已结算2-已取消', status tinyint default 0 not null comment '状态0-待结算1-已结算2-已取消',
frozen_days int default 0 not null comment '冻结时间', frozen_days int default 0 not null comment '冻结时间',
unfreeze_time datetime null comment '解冻时间', unfreeze_time datetime null comment '解冻时间',
source_user_type tinyint not null comment '来源用户类型1-一级推广用户2-二级推广用户',
source_user_id bigint not null comment '来源用户编号',
creator varchar(64) collate utf8mb4_general_ci default '' null comment '创建者', creator varchar(64) collate utf8mb4_general_ci default '' null comment '创建者',
create_time datetime default CURRENT_TIMESTAMP not null comment '创建时间', create_time datetime default CURRENT_TIMESTAMP not null comment '创建时间',
updater varchar(64) collate utf8mb4_general_ci default '' null comment '更新者', updater varchar(64) collate utf8mb4_general_ci default '' null comment '更新者',
@ -192,9 +194,9 @@ VALUES ('分销用户推广订单查询', 'trade:brokerage-user:order-query', 3,
INSERT INTO system_menu(name, permission, type, sort, parent_id, path, icon, component, status) INSERT INTO system_menu(name, permission, type, sort, parent_id, path, icon, component, status)
VALUES ('分销用户修改推广资格', 'trade:brokerage-user:update-brokerage-enable', 3, 4, @parentId, '', '', '', 0); VALUES ('分销用户修改推广资格', 'trade:brokerage-user:update-brokerage-enable', 3, 4, @parentId, '', '', '', 0);
INSERT INTO system_menu(name, permission, type, sort, parent_id, path, icon, component, status) INSERT INTO system_menu(name, permission, type, sort, parent_id, path, icon, component, status)
VALUES ('分销用户修改推广员', 'trade:brokerage-user:update-brokerage-user', 3, 5, @parentId, '', '', '', 0); VALUES ('分销用户修改推广员', 'trade:brokerage-user:update-bind-user', 3, 5, @parentId, '', '', '', 0);
INSERT INTO system_menu(name, permission, type, sort, parent_id, path, icon, component, status) INSERT INTO system_menu(name, permission, type, sort, parent_id, path, icon, component, status)
VALUES ('分销用户清除推广员', 'trade:brokerage-user:clear-brokerage-user', 3, 6, @parentId, '', '', '', 0); VALUES ('分销用户清除推广员', 'trade:brokerage-user:clear-bind-user', 3, 6, @parentId, '', '', '', 0);
-- 增加菜单佣金记录 -- 增加菜单佣金记录
INSERT INTO system_menu(name, permission, type, sort, parent_id, path, icon, component, status, component_name) INSERT INTO system_menu(name, permission, type, sort, parent_id, path, icon, component, status, component_name)

View File

@ -15,6 +15,9 @@ import ${basePackage}.module.${table.moduleName}.dal.mysql.${table.businessName}
import static ${ServiceExceptionUtilClassName}.exception; import static ${ServiceExceptionUtilClassName}.exception;
import static ${basePackage}.module.${table.moduleName}.enums.ErrorCodeConstants.*; import static ${basePackage}.module.${table.moduleName}.enums.ErrorCodeConstants.*;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
/** /**
* ${table.classComment} Service 实现类 * ${table.classComment} Service 实现类
* *
@ -61,6 +64,9 @@ public class ${table.className}ServiceImpl implements ${table.className}Service
@Override @Override
public ${table.className}DO get${simpleClassName}(${primaryColumn.javaType} id) { public ${table.className}DO get${simpleClassName}(${primaryColumn.javaType} id) {
if (CollUtil.isEmpty(ids)) {
return ListUtil.empty();
}
return ${classNameVar}Mapper.selectById(id); return ${classNameVar}Mapper.selectById(id);
} }

View File

@ -21,6 +21,13 @@
<groupId>cn.iocoder.boot</groupId> <groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-common</artifactId> <artifactId>yudao-common</artifactId>
</dependency> </dependency>
<!-- 参数校验 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
<optional>true</optional>
</dependency>
</dependencies> </dependencies>
</project> </project>

View File

@ -1,7 +1,11 @@
package cn.iocoder.yudao.module.trade.api.brokerage; package cn.iocoder.yudao.module.trade.api.brokerage;
import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
import cn.iocoder.yudao.module.trade.api.brokerage.dto.BrokerageUserDTO; import cn.iocoder.yudao.module.trade.api.brokerage.dto.BrokerageUserDTO;
import javax.validation.constraints.NotNull;
import java.time.LocalDateTime;
/** /**
* 分销 API 接口 * 分销 API 接口
* *
@ -17,6 +21,20 @@ public interface BrokerageApi {
*/ */
BrokerageUserDTO getBrokerageUser(Long userId); BrokerageUserDTO getBrokerageUser(Long userId);
/**
* 会员绑定推广员
*
* @param userId 用户编号
* @param bindUserId 推广员编号
* @param registerTime 用户注册时间
* @return 是否绑定
*/
default boolean bindUser(@NotNull Long userId, @NotNull Long bindUserId, @NotNull LocalDateTime registerTime) {
// 注册时间在30秒内的都算新用户
boolean isNewUser = LocalDateTimeUtils.afterNow(registerTime.minusSeconds(30));
return bindUser(userId, bindUserId, isNewUser);
}
/** /**
* 绑定推广员 * 绑定推广员
* *
@ -25,5 +43,5 @@ public interface BrokerageApi {
* @param isNewUser 是否为新用户 * @param isNewUser 是否为新用户
* @return 是否绑定 * @return 是否绑定
*/ */
boolean bindUser(Long userId, Long bindUserId, Boolean isNewUser); boolean bindUser(@NotNull Long userId, @NotNull Long bindUserId, @NotNull Boolean isNewUser);
} }

View File

@ -0,0 +1,39 @@
package cn.iocoder.yudao.module.trade.enums.brokerage;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
/**
* 分销用户类型枚举
*
* @author owen
*/
@AllArgsConstructor
@Getter
public enum BrokerageUserTypeEnum implements IntArrayValuable {
ALL(0, "全部"),
FIRST(1, "一级推广人"),
SECOND(2, "二级推广人"),
;
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(BrokerageUserTypeEnum::getType).toArray();
/**
* 类型
*/
private final Integer type;
/**
* 名字
*/
private final String name;
@Override
public int[] array() {
return ARRAYS;
}
}

View File

@ -2,6 +2,8 @@ package cn.iocoder.yudao.module.trade.controller.admin.brokerage.record;
import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
import cn.iocoder.yudao.module.trade.controller.admin.brokerage.record.vo.BrokerageRecordPageReqVO; import cn.iocoder.yudao.module.trade.controller.admin.brokerage.record.vo.BrokerageRecordPageReqVO;
import cn.iocoder.yudao.module.trade.controller.admin.brokerage.record.vo.BrokerageRecordRespVO; import cn.iocoder.yudao.module.trade.controller.admin.brokerage.record.vo.BrokerageRecordRespVO;
import cn.iocoder.yudao.module.trade.convert.brokerage.record.BrokerageRecordConvert; import cn.iocoder.yudao.module.trade.convert.brokerage.record.BrokerageRecordConvert;
@ -19,8 +21,12 @@ import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource; import javax.annotation.Resource;
import javax.validation.Valid; import javax.validation.Valid;
import java.util.Map;
import java.util.Set;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
@Tag(name = "管理后台 - 佣金记录") @Tag(name = "管理后台 - 佣金记录")
@RestController @RestController
@ -31,6 +37,9 @@ public class BrokerageRecordController {
@Resource @Resource
private BrokerageRecordService brokerageRecordService; private BrokerageRecordService brokerageRecordService;
@Resource
private MemberUserApi memberUserApi;
@GetMapping("/get") @GetMapping("/get")
@Operation(summary = "获得佣金记录") @Operation(summary = "获得佣金记录")
@Parameter(name = "id", description = "编号", required = true, example = "1024") @Parameter(name = "id", description = "编号", required = true, example = "1024")
@ -45,7 +54,12 @@ public class BrokerageRecordController {
@PreAuthorize("@ss.hasPermission('trade:brokerage-record:query')") @PreAuthorize("@ss.hasPermission('trade:brokerage-record:query')")
public CommonResult<PageResult<BrokerageRecordRespVO>> getBrokerageRecordPage(@Valid BrokerageRecordPageReqVO pageVO) { public CommonResult<PageResult<BrokerageRecordRespVO>> getBrokerageRecordPage(@Valid BrokerageRecordPageReqVO pageVO) {
PageResult<BrokerageRecordDO> pageResult = brokerageRecordService.getBrokerageRecordPage(pageVO); PageResult<BrokerageRecordDO> pageResult = brokerageRecordService.getBrokerageRecordPage(pageVO);
return success(BrokerageRecordConvert.INSTANCE.convertPage(pageResult));
Set<Long> userIds = convertSet(pageResult.getList(), BrokerageRecordDO::getUserId);
userIds.addAll(convertList(pageResult.getList(), BrokerageRecordDO::getSourceUserId));
Map<Long, MemberUserRespDTO> userMap = memberUserApi.getUserMap(userIds);
return success(BrokerageRecordConvert.INSTANCE.convertPage(pageResult, userMap));
} }
} }

View File

@ -57,4 +57,9 @@ public class BrokerageRecordBaseVO {
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime unfreezeTime; private LocalDateTime unfreezeTime;
@Schema(description = "来源用户类型")
private Integer sourceUserType;
@Schema(description = "来源用户编号")
private Long sourceUserId;
} }

View File

@ -1,6 +1,8 @@
package cn.iocoder.yudao.module.trade.controller.admin.brokerage.record.vo; package cn.iocoder.yudao.module.trade.controller.admin.brokerage.record.vo;
import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageUserTypeEnum;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
@ -30,4 +32,8 @@ public class BrokerageRecordPageReqVO extends PageParam {
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime; private LocalDateTime[] createTime;
@Schema(description = "用户类型")
@InEnum(value = BrokerageUserTypeEnum.class, message = "用户类型必须是 {value}")
private Integer sourceUserType;
} }

View File

@ -19,4 +19,19 @@ public class BrokerageRecordRespVO extends BrokerageRecordBaseVO {
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime; private LocalDateTime createTime;
// ========== 用户信息 ==========
@Schema(description = "用户头像", example = "https://www.iocoder.cn/xxx.png")
private String userAvatar;
@Schema(description = "用户昵称", example = "李四")
private String userNickname;
// ========== 来源用户信息 ==========
@Schema(description = "来源用户头像", example = "https://www.iocoder.cn/xxx.png")
private String sourceUserAvatar;
@Schema(description = "来源用户昵称", example = "李四")
private String sourceUserNickname;
} }

View File

@ -9,8 +9,9 @@ import cn.iocoder.yudao.module.trade.convert.brokerage.user.BrokerageUserConvert
import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.user.BrokerageUserDO; import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.user.BrokerageUserDO;
import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordBizTypeEnum; import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordBizTypeEnum;
import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordStatusEnum; import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordStatusEnum;
import cn.iocoder.yudao.module.trade.service.brokerage.record.BrokerageRecordService; import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageUserTypeEnum;
import cn.iocoder.yudao.module.trade.service.brokerage.bo.UserBrokerageSummaryBO; import cn.iocoder.yudao.module.trade.service.brokerage.bo.UserBrokerageSummaryBO;
import cn.iocoder.yudao.module.trade.service.brokerage.record.BrokerageRecordService;
import cn.iocoder.yudao.module.trade.service.brokerage.user.BrokerageUserService; import cn.iocoder.yudao.module.trade.service.brokerage.user.BrokerageUserService;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameter;
@ -42,18 +43,18 @@ public class BrokerageUserController {
@Resource @Resource
private MemberUserApi memberUserApi; private MemberUserApi memberUserApi;
@PutMapping("/update-brokerage-user") @PutMapping("/update-bind-user")
@Operation(summary = "修改推广员") @Operation(summary = "修改推广员")
@PreAuthorize("@ss.hasPermission('trade:brokerage-user:update-brokerage-user')") @PreAuthorize("@ss.hasPermission('trade:brokerage-user:update-bind-user')")
public CommonResult<Boolean> updateBrokerageUser(@Valid @RequestBody BrokerageUserUpdateBrokerageUserReqVO updateReqVO) { public CommonResult<Boolean> updateBindUser(@Valid @RequestBody BrokerageUserUpdateBrokerageUserReqVO updateReqVO) {
brokerageUserService.updateBrokerageUserId(updateReqVO.getId(), updateReqVO.getBindUserId()); brokerageUserService.updateBrokerageUserId(updateReqVO.getId(), updateReqVO.getBindUserId());
return success(true); return success(true);
} }
@PutMapping("/clear-brokerage-user") @PutMapping("/clear-bind-user")
@Operation(summary = "清除推广员") @Operation(summary = "清除推广员")
@PreAuthorize("@ss.hasPermission('trade:brokerage-user:clear-brokerage-user')") @PreAuthorize("@ss.hasPermission('trade:brokerage-user:clear-bind-user')")
public CommonResult<Boolean> clearBrokerageUser(@Valid @RequestBody BrokerageUserClearBrokerageUserReqVO updateReqVO) { public CommonResult<Boolean> clearBindUser(@Valid @RequestBody BrokerageUserClearBrokerageUserReqVO updateReqVO) {
brokerageUserService.updateBrokerageUserId(updateReqVO.getId(), null); brokerageUserService.updateBrokerageUserId(updateReqVO.getId(), null);
return success(true); return success(true);
} }
@ -72,7 +73,8 @@ public class BrokerageUserController {
@PreAuthorize("@ss.hasPermission('trade:brokerage-user:query')") @PreAuthorize("@ss.hasPermission('trade:brokerage-user:query')")
public CommonResult<BrokerageUserRespVO> getBrokerageUser(@RequestParam("id") Long id) { public CommonResult<BrokerageUserRespVO> getBrokerageUser(@RequestParam("id") Long id) {
BrokerageUserDO brokerageUser = brokerageUserService.getBrokerageUser(id); BrokerageUserDO brokerageUser = brokerageUserService.getBrokerageUser(id);
return success(BrokerageUserConvert.INSTANCE.convert(brokerageUser)); BrokerageUserRespVO respVO = BrokerageUserConvert.INSTANCE.convert(brokerageUser);
return success(BrokerageUserConvert.INSTANCE.copyTo(memberUserApi.getUser(id), respVO));
} }
@GetMapping("/page") @GetMapping("/page")
@ -94,7 +96,7 @@ public class BrokerageUserController {
// 合计推广用户数量 // 合计推广用户数量
Map<Long, Long> brokerageUserCountMap = convertMap(userIds, Map<Long, Long> brokerageUserCountMap = convertMap(userIds,
userId -> userId, userId -> userId,
userId -> brokerageUserService.getBrokerageUserCountByBindUserId(userId)); userId -> brokerageUserService.getBrokerageUserCountByBindUserId(userId, BrokerageUserTypeEnum.ALL));
// todo 合计提现 // todo 合计提现

View File

@ -1,6 +1,8 @@
package cn.iocoder.yudao.module.trade.controller.admin.brokerage.user.vo; package cn.iocoder.yudao.module.trade.controller.admin.brokerage.user.vo;
import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageUserTypeEnum;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
@ -27,4 +29,11 @@ public class BrokerageUserPageReqVO extends PageParam {
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime; private LocalDateTime[] createTime;
@Schema(description = "用户类型")
@InEnum(value = BrokerageUserTypeEnum.class, message = "用户类型必须是 {value}")
private Integer userType;
@Schema(description = "绑定时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] bindUserTime;
} }

View File

@ -28,7 +28,7 @@ public class BrokerageUserRespVO extends BrokerageUserBaseVO {
// ========== 推广信息 ========== // ========== 推广信息 ==========
@Schema(description = "推广用户数量(一级)", example = "20019") @Schema(description = "推广用户数量", example = "20019")
private Integer brokerageUserCount; private Integer brokerageUserCount;
@Schema(description = "推广订单数量", example = "20019") @Schema(description = "推广订单数量", example = "20019")
private Integer brokerageOrderCount; private Integer brokerageOrderCount;

View File

@ -6,6 +6,7 @@ import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated;
import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.record.AppBrokerageProductPriceRespVO; import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.record.AppBrokerageProductPriceRespVO;
import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.record.AppBrokerageRecordPageReqVO; import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.record.AppBrokerageRecordPageReqVO;
import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.record.AppBrokerageRecordRespVO; import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.record.AppBrokerageRecordRespVO;
import cn.iocoder.yudao.module.trade.service.brokerage.user.BrokerageUserService;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@ -15,10 +16,12 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.validation.Valid; import javax.validation.Valid;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getLoginUserId;
import static java.util.Arrays.asList; import static java.util.Arrays.asList;
@Tag(name = "用户 APP - 分销用户") @Tag(name = "用户 APP - 分销用户")
@ -27,6 +30,8 @@ import static java.util.Arrays.asList;
@Validated @Validated
@Slf4j @Slf4j
public class AppBrokerageRecordController { public class AppBrokerageRecordController {
@Resource
private BrokerageUserService brokerageUserService;
// TODO 芋艿临时 mock => // TODO 芋艿临时 mock =>
@GetMapping("/page") @GetMapping("/page")
@ -46,7 +51,7 @@ public class AppBrokerageRecordController {
@Operation(summary = "获得商品的分销金额") @Operation(summary = "获得商品的分销金额")
public CommonResult<AppBrokerageProductPriceRespVO> getProductBrokeragePrice(@RequestParam("spuId") Long spuId) { public CommonResult<AppBrokerageProductPriceRespVO> getProductBrokeragePrice(@RequestParam("spuId") Long spuId) {
AppBrokerageProductPriceRespVO respVO = new AppBrokerageProductPriceRespVO(); AppBrokerageProductPriceRespVO respVO = new AppBrokerageProductPriceRespVO();
respVO.setEnabled(true); // TODO @疯狂需要开启分销 + 人允许分销 respVO.setEnabled(brokerageUserService.getUserBrokerageEnabled(getLoginUserId()));
respVO.setBrokerageMinPrice(1); respVO.setBrokerageMinPrice(1);
respVO.setBrokerageMaxPrice(2); respVO.setBrokerageMaxPrice(2);
return success(respVO); return success(respVO);

View File

@ -19,7 +19,7 @@ public class AppBrokerageUserMySummaryRespVO {
@Schema(description = "冻结的佣金,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "234") @Schema(description = "冻结的佣金,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "234")
private Integer frozenPrice; private Integer frozenPrice;
@Schema(description = "分销用户数量(一级)", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") @Schema(description = "分销用户数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
private Integer firstBrokerageUserCount; private Integer firstBrokerageUserCount;
@Schema(description = "分销用户数量(二级)", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") @Schema(description = "分销用户数量(二级)", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")

View File

@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.trade.convert.brokerage.record;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
import cn.iocoder.yudao.module.trade.controller.admin.brokerage.record.vo.BrokerageRecordRespVO; import cn.iocoder.yudao.module.trade.controller.admin.brokerage.record.vo.BrokerageRecordRespVO;
import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.record.BrokerageRecordDO; import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.record.BrokerageRecordDO;
import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.user.BrokerageUserDO; import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.user.BrokerageUserDO;
@ -13,6 +14,8 @@ import org.mapstruct.factory.Mappers;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Optional;
/** /**
* 佣金记录 Convert * 佣金记录 Convert
@ -30,10 +33,9 @@ public interface BrokerageRecordConvert {
PageResult<BrokerageRecordRespVO> convertPage(PageResult<BrokerageRecordDO> page); PageResult<BrokerageRecordRespVO> convertPage(PageResult<BrokerageRecordDO> page);
// TODO @疯狂可能 title 不是很固化会存在类似沐晴成功购买XXX JVM 实战
default BrokerageRecordDO convert(BrokerageUserDO user, BrokerageRecordBizTypeEnum bizType, String bizId, default BrokerageRecordDO convert(BrokerageUserDO user, BrokerageRecordBizTypeEnum bizType, String bizId,
Integer brokerageFrozenDays, int brokeragePrice, LocalDateTime unfreezeTime, Integer brokerageFrozenDays, int brokeragePrice, LocalDateTime unfreezeTime,
String title) { String title, Long sourceUserId, Integer sourceUserType) {
brokerageFrozenDays = ObjectUtil.defaultIfNull(brokerageFrozenDays, 0); brokerageFrozenDays = ObjectUtil.defaultIfNull(brokerageFrozenDays, 0);
// 不冻结时佣金直接就是结算状态 // 不冻结时佣金直接就是结算状态
Integer status = brokerageFrozenDays > 0 Integer status = brokerageFrozenDays > 0
@ -43,8 +45,22 @@ public interface BrokerageRecordConvert {
.setBizType(bizType.getType()).setBizId(bizId) .setBizType(bizType.getType()).setBizId(bizId)
.setPrice(brokeragePrice).setTotalPrice(user.getBrokeragePrice()) .setPrice(brokeragePrice).setTotalPrice(user.getBrokeragePrice())
.setTitle(title) .setTitle(title)
.setDescription(StrUtil.format(bizType.getDescription(), String.valueOf(brokeragePrice / 100.0))) .setDescription(StrUtil.format(bizType.getDescription(), String.format("¥%.2f", brokeragePrice / 100d)))
.setStatus(status).setFrozenDays(brokerageFrozenDays).setUnfreezeTime(unfreezeTime); .setStatus(status).setFrozenDays(brokerageFrozenDays).setUnfreezeTime(unfreezeTime)
.setSourceUserType(sourceUserType).setSourceUserId(sourceUserId);
} }
default PageResult<BrokerageRecordRespVO> convertPage(PageResult<BrokerageRecordDO> pageResult, Map<Long, MemberUserRespDTO> userMap) {
PageResult<BrokerageRecordRespVO> result = convertPage(pageResult);
for (BrokerageRecordRespVO respVO : result.getList()) {
Optional.ofNullable(userMap.get(respVO.getUserId())).ifPresent(user ->
respVO.setUserNickname(user.getNickname()).setUserAvatar(user.getAvatar()));
Optional.ofNullable(userMap.get(respVO.getSourceUserId())).ifPresent(user ->
respVO.setSourceUserNickname(user.getNickname()).setSourceUserAvatar(user.getAvatar()));
}
return result;
}
} }

View File

@ -37,20 +37,25 @@ public interface BrokerageUserConvert {
PageResult<BrokerageUserRespVO> result = convertPage(pageResult); PageResult<BrokerageUserRespVO> result = convertPage(pageResult);
for (BrokerageUserRespVO vo : result.getList()) { for (BrokerageUserRespVO vo : result.getList()) {
// 用户信息 // 用户信息
Optional.ofNullable(userMap.get(vo.getId())).ifPresent( copyTo(userMap.get(vo.getId()), vo);
user -> vo.setNickname(user.getNickname()).setAvatar(user.getAvatar()));
// 推广用户数量一级 // 推广用户数量
vo.setBrokerageUserCount(MapUtil.getInt(brokerageUserCountMap, vo.getId(), 0)); vo.setBrokerageUserCount(MapUtil.getInt(brokerageUserCountMap, vo.getId(), 0));
// 推广订单数量推广订单金额 // 推广订单数量推广订单金额
Optional<UserBrokerageSummaryBO> orderSummaryOptional = Optional.ofNullable(userOrderSummaryMap.get(vo.getId())); Optional<UserBrokerageSummaryBO> orderSummaryOptional = Optional.ofNullable(userOrderSummaryMap.get(vo.getId()));
vo.setBrokerageOrderCount(orderSummaryOptional.map(UserBrokerageSummaryBO::getCount).orElse(0)) vo.setBrokerageOrderCount(orderSummaryOptional.map(UserBrokerageSummaryBO::getCount).orElse(0))
.setBrokerageOrderPrice(orderSummaryOptional.map(UserBrokerageSummaryBO::getPrice).orElse(0)); .setBrokerageOrderPrice(orderSummaryOptional.map(UserBrokerageSummaryBO::getPrice).orElse(0));
// todo 已提现次数已提现金额 // todo 已提现次数已提现金额
vo.setWithdrawCount(0); vo.setWithdrawCount(0).setWithdrawPrice(0);
vo.setWithdrawPrice(0);
} }
return result; return result;
} }
default BrokerageUserRespVO copyTo(MemberUserRespDTO source, BrokerageUserRespVO target) {
Optional.ofNullable(source).ifPresent(
user -> target.setNickname(user.getNickname()).setAvatar(user.getAvatar()));
return target;
}
BrokerageUserDTO convertDTO(BrokerageUserDO brokerageUser); BrokerageUserDTO convertDTO(BrokerageUserDO brokerageUser);
} }

View File

@ -7,7 +7,6 @@ import cn.iocoder.yudao.framework.common.util.string.StrUtils;
import cn.iocoder.yudao.framework.dict.core.util.DictFrameworkUtils; import cn.iocoder.yudao.framework.dict.core.util.DictFrameworkUtils;
import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils; import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils;
import cn.iocoder.yudao.module.member.api.address.dto.AddressRespDTO; import cn.iocoder.yudao.module.member.api.address.dto.AddressRespDTO;
import cn.iocoder.yudao.module.trade.service.brokerage.bo.BrokerageAddReqBO;
import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO; import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO; import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO;
import cn.iocoder.yudao.module.pay.enums.DictTypeConstants; import cn.iocoder.yudao.module.pay.enums.DictTypeConstants;
@ -28,9 +27,11 @@ import cn.iocoder.yudao.module.trade.dal.dataobject.cart.CartDO;
import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressDO; 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.TradeOrderDO;
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO; import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordBizTypeEnum;
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderItemAfterSaleStatusEnum; import cn.iocoder.yudao.module.trade.enums.order.TradeOrderItemAfterSaleStatusEnum;
import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.ExpressTrackRespDTO; import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.ExpressTrackRespDTO;
import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties; import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties;
import cn.iocoder.yudao.module.trade.service.brokerage.bo.BrokerageAddReqBO;
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO; import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO;
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO; import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
import org.mapstruct.Mapper; import org.mapstruct.Mapper;
@ -279,8 +280,10 @@ public interface TradeOrderConvert {
default BrokerageAddReqBO convert(TradeOrderItemDO item, ProductSkuRespDTO sku) { default BrokerageAddReqBO convert(TradeOrderItemDO item, ProductSkuRespDTO sku) {
return new BrokerageAddReqBO().setBizId(String.valueOf(item.getId())) return new BrokerageAddReqBO().setBizId(String.valueOf(item.getId()))
.setSourceUserId(item.getUserId())
.setBasePrice(item.getPayPrice() * item.getCount()) .setBasePrice(item.getPayPrice() * item.getCount())
.setFirstFixedPrice(sku.getSubCommissionFirstPrice()) .setFirstFixedPrice(sku.getSubCommissionFirstPrice())
.setSecondFixedPrice(sku.getSubCommissionSecondPrice()); .setSecondFixedPrice(sku.getSubCommissionSecondPrice())
.setTitle(BrokerageRecordBizTypeEnum.ORDER.getTitle());
} }
} }

View File

@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.record;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordBizTypeEnum; import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordBizTypeEnum;
import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordStatusEnum; import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordStatusEnum;
import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageUserTypeEnum;
import com.baomidou.mybatisplus.annotation.KeySequence; import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.annotation.TableName;
@ -32,6 +33,8 @@ public class BrokerageRecordDO extends BaseDO {
private Integer id; private Integer id;
/** /**
* 用户编号 * 用户编号
* <p>
* 关联 MemberUserDO.id
*/ */
private Long userId; private Long userId;
/** /**
@ -79,4 +82,17 @@ public class BrokerageRecordDO extends BaseDO {
*/ */
private LocalDateTime unfreezeTime; private LocalDateTime unfreezeTime;
/**
* 来源用户类型
* <p>
* 枚举 {@link BrokerageUserTypeEnum}
*/
private Integer sourceUserType;
/**
* 来源用户编号
* <p>
* 关联 MemberUserDO.id
*/
private Long sourceUserId;
} }

View File

@ -5,6 +5,7 @@ import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.trade.controller.admin.brokerage.record.vo.BrokerageRecordPageReqVO; import cn.iocoder.yudao.module.trade.controller.admin.brokerage.record.vo.BrokerageRecordPageReqVO;
import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.record.BrokerageRecordDO; import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.record.BrokerageRecordDO;
import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageUserTypeEnum;
import cn.iocoder.yudao.module.trade.service.brokerage.bo.UserBrokerageSummaryBO; import cn.iocoder.yudao.module.trade.service.brokerage.bo.UserBrokerageSummaryBO;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
@ -23,10 +24,14 @@ import java.util.List;
public interface BrokerageRecordMapper extends BaseMapperX<BrokerageRecordDO> { public interface BrokerageRecordMapper extends BaseMapperX<BrokerageRecordDO> {
default PageResult<BrokerageRecordDO> selectPage(BrokerageRecordPageReqVO reqVO) { default PageResult<BrokerageRecordDO> selectPage(BrokerageRecordPageReqVO reqVO) {
boolean sourceUserTypeCondition = reqVO.getSourceUserType() != null &&
!BrokerageUserTypeEnum.ALL.getType().equals(reqVO.getSourceUserType());
return selectPage(reqVO, new LambdaQueryWrapperX<BrokerageRecordDO>() return selectPage(reqVO, new LambdaQueryWrapperX<BrokerageRecordDO>()
.eqIfPresent(BrokerageRecordDO::getUserId, reqVO.getUserId()) .eqIfPresent(BrokerageRecordDO::getUserId, reqVO.getUserId())
.eqIfPresent(BrokerageRecordDO::getBizType, reqVO.getBizType()) .eqIfPresent(BrokerageRecordDO::getBizType, reqVO.getBizType())
.eqIfPresent(BrokerageRecordDO::getStatus, reqVO.getStatus()) .eqIfPresent(BrokerageRecordDO::getStatus, reqVO.getStatus())
.eq(sourceUserTypeCondition, BrokerageRecordDO::getSourceUserType, reqVO.getSourceUserType())
.betweenIfPresent(BrokerageRecordDO::getCreateTime, reqVO.getCreateTime()) .betweenIfPresent(BrokerageRecordDO::getCreateTime, reqVO.getCreateTime())
.orderByDesc(BrokerageRecordDO::getId)); .orderByDesc(BrokerageRecordDO::getId));
} }
@ -43,13 +48,13 @@ public interface BrokerageRecordMapper extends BaseMapperX<BrokerageRecordDO> {
.eq(BrokerageRecordDO::getStatus, status)); .eq(BrokerageRecordDO::getStatus, status));
} }
default BrokerageRecordDO selectByBizTypeAndBizId(Integer bizType, String bizId) { default BrokerageRecordDO selectByBizTypeAndBizIdAndUserId(Integer bizType, String bizId, Long userId) {
return selectOne(BrokerageRecordDO::getBizType, bizType, return selectOne(BrokerageRecordDO::getBizType, bizType,
BrokerageRecordDO::getBizId, bizId); BrokerageRecordDO::getBizId, bizId,
BrokerageRecordDO::getUserId, userId);
} }
// TODO @疯狂mysql 关键字大写哈这样看起来清晰点例如说 SELECT COUNT(1) @Select("SELECT COUNT(1), SUM(price) FROM trade_brokerage_record WHERE user_id = #{userId} AND biz_type = #{bizType} AND status = #{status}")
@Select("select count(1), sum(price) from trade_brokerage_record where user_id = #{userId} and biz_type = #{bizType} and status = #{status}")
UserBrokerageSummaryBO selectCountAndSumPriceByUserIdAndBizTypeAndStatus(@Param("userId") Long userId, UserBrokerageSummaryBO selectCountAndSumPriceByUserIdAndBizTypeAndStatus(@Param("userId") Long userId,
@Param("bizType") Integer bizType, @Param("bizType") Integer bizType,
@Param("status") Integer status); @Param("status") Integer status);

View File

@ -1,13 +1,17 @@
package cn.iocoder.yudao.module.trade.dal.mysql.brokerage.user; package cn.iocoder.yudao.module.trade.dal.mysql.brokerage.user;
import cn.hutool.core.lang.Assert; import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.trade.controller.admin.brokerage.user.vo.BrokerageUserPageReqVO; import cn.iocoder.yudao.module.trade.controller.admin.brokerage.user.vo.BrokerageUserPageReqVO;
import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.user.BrokerageUserDO; import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.user.BrokerageUserDO;
import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageUserTypeEnum;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
/** /**
* 分销用户 Mapper * 分销用户 Mapper
@ -19,12 +23,32 @@ public interface BrokerageUserMapper extends BaseMapperX<BrokerageUserDO> {
default PageResult<BrokerageUserDO> selectPage(BrokerageUserPageReqVO reqVO) { default PageResult<BrokerageUserDO> selectPage(BrokerageUserPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<BrokerageUserDO>() return selectPage(reqVO, new LambdaQueryWrapperX<BrokerageUserDO>()
.eqIfPresent(BrokerageUserDO::getBindUserId, reqVO.getBindUserId())
.eqIfPresent(BrokerageUserDO::getBrokerageEnabled, reqVO.getBrokerageEnabled()) .eqIfPresent(BrokerageUserDO::getBrokerageEnabled, reqVO.getBrokerageEnabled())
.betweenIfPresent(BrokerageUserDO::getCreateTime, reqVO.getCreateTime()) .betweenIfPresent(BrokerageUserDO::getCreateTime, reqVO.getCreateTime())
.betweenIfPresent(BrokerageUserDO::getBindUserTime, reqVO.getBindUserTime())
.and(reqVO.getBindUserId() != null, w -> buildBindUserCondition(reqVO, w))
.orderByDesc(BrokerageUserDO::getId)); .orderByDesc(BrokerageUserDO::getId));
} }
static void buildBindUserCondition(BrokerageUserPageReqVO reqVO, LambdaQueryWrapper<BrokerageUserDO> wrapper) {
if (BrokerageUserTypeEnum.FIRST.getType().equals(reqVO.getUserType())) {
buildFirstBindUserCondition(reqVO.getBindUserId(), wrapper);
} else if (BrokerageUserTypeEnum.SECOND.getType().equals(reqVO.getUserType())) {
buildSecondBindUserCondition(reqVO.getBindUserId(), wrapper);
} else {
buildFirstBindUserCondition(reqVO.getBindUserId(), wrapper);
buildSecondBindUserCondition(reqVO.getBindUserId(), wrapper.or());
}
}
static void buildFirstBindUserCondition(Long bindUserId, LambdaQueryWrapper<BrokerageUserDO> wrapper) {
wrapper.eq(BrokerageUserDO::getBindUserId, bindUserId);
}
static void buildSecondBindUserCondition(Long bindUserId, LambdaQueryWrapper<BrokerageUserDO> w) {
w.inSql(BrokerageUserDO::getBindUserId, StrUtil.format("SELECT id FROM trade_brokerage_user WHERE bind_user_id = {}", bindUserId));
}
/** /**
* 更新用户可用佣金增加 * 更新用户可用佣金增加
* *
@ -112,4 +136,10 @@ public interface BrokerageUserMapper extends BaseMapperX<BrokerageUserDO> {
.set(BrokerageUserDO::getBrokerageEnabled, false).set(BrokerageUserDO::getBrokerageTime, null)); .set(BrokerageUserDO::getBrokerageEnabled, false).set(BrokerageUserDO::getBrokerageTime, null));
} }
default Long selectCountByBindUserId(Long bindUserId) {
return selectCount(BrokerageUserDO::getBindUserId, bindUserId);
}
@Select("SELECT COUNT(1) from trade_brokerage_user WHERE bind_user_id IN (SELECT id FROM trade_brokerage_user WHERE bind_user_id = #{bindUserId})")
Long selectCountByBindUserIdInBindUserId(Long bindUserId);
} }

View File

@ -36,4 +36,13 @@ public class BrokerageAddReqBO {
*/ */
private Integer secondFixedPrice; private Integer secondFixedPrice;
/**
* 来源用户编号
*/
private Long sourceUserId;
/**
* 佣金记录标题
*/
private String title;
} }

View File

@ -14,6 +14,7 @@ import cn.iocoder.yudao.module.trade.dal.dataobject.config.TradeConfigDO;
import cn.iocoder.yudao.module.trade.dal.mysql.brokerage.record.BrokerageRecordMapper; import cn.iocoder.yudao.module.trade.dal.mysql.brokerage.record.BrokerageRecordMapper;
import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordBizTypeEnum; import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordBizTypeEnum;
import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordStatusEnum; import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordStatusEnum;
import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageUserTypeEnum;
import cn.iocoder.yudao.module.trade.service.brokerage.bo.BrokerageAddReqBO; 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.UserBrokerageSummaryBO;
import cn.iocoder.yudao.module.trade.service.brokerage.user.BrokerageUserService; import cn.iocoder.yudao.module.trade.service.brokerage.user.BrokerageUserService;
@ -27,7 +28,6 @@ import javax.annotation.Resource;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.function.Function;
/** /**
* 佣金记录 Service 实现类 * 佣金记录 Service 实现类
@ -72,7 +72,8 @@ public class BrokerageRecordServiceImpl implements BrokerageRecordService {
return; return;
} }
// 1.2 计算一级分佣 // 1.2 计算一级分佣
addBrokerage(firstUser, list, memberConfig.getBrokerageFrozenDays(), memberConfig.getBrokerageFirstPercent(), BrokerageAddReqBO::getFirstFixedPrice, bizType); addBrokerage(firstUser, list, memberConfig.getBrokerageFrozenDays(), memberConfig.getBrokerageFirstPercent(),
bizType, BrokerageUserTypeEnum.FIRST);
// 2.1 获得二级推广员 // 2.1 获得二级推广员
if (firstUser.getBindUserId() == null) { if (firstUser.getBindUserId() == null) {
@ -83,15 +84,15 @@ public class BrokerageRecordServiceImpl implements BrokerageRecordService {
return; return;
} }
// 2.2 计算二级分佣 // 2.2 计算二级分佣
addBrokerage(secondUser, list, memberConfig.getBrokerageFrozenDays(), memberConfig.getBrokerageSecondPercent(), BrokerageAddReqBO::getSecondFixedPrice, bizType); addBrokerage(secondUser, list, memberConfig.getBrokerageFrozenDays(), memberConfig.getBrokerageSecondPercent(),
bizType, BrokerageUserTypeEnum.SECOND);
} }
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public void cancelBrokerage(Long userId, BrokerageRecordBizTypeEnum bizType, String bizId) { public void cancelBrokerage(Long userId, BrokerageRecordBizTypeEnum bizType, String bizId) {
// TODO @疯狂userId 加进去查询会不会更好一点万一穿错参数 BrokerageRecordDO record = brokerageRecordMapper.selectByBizTypeAndBizIdAndUserId(bizType.getType(), bizId, userId);
BrokerageRecordDO record = brokerageRecordMapper.selectByBizTypeAndBizId(bizType.getType(), bizId); if (record == null) {
if (record == null || ObjectUtil.notEqual(record.getUserId(), userId)) {
log.error("[cancelBrokerage][userId({})][bizId({}) 更新为已失效失败:记录不存在]", userId, bizId); log.error("[cancelBrokerage][userId({})][bizId({}) 更新为已失效失败:记录不存在]", userId, bizId);
return; return;
} }
@ -139,12 +140,11 @@ public class BrokerageRecordServiceImpl implements BrokerageRecordService {
* @param list 佣金增加参数列表 * @param list 佣金增加参数列表
* @param brokerageFrozenDays 冻结天数 * @param brokerageFrozenDays 冻结天数
* @param brokeragePercent 佣金比例 * @param brokeragePercent 佣金比例
* @param fixedPriceFun 固定佣金 // TODO 疯狂这里是不是可以直接传递 fixedPrice
* @param bizType 业务类型 * @param bizType 业务类型
* @param sourceUserType 来源用户类型
*/ */
private void addBrokerage(BrokerageUserDO user, List<BrokerageAddReqBO> list, Integer brokerageFrozenDays, private void addBrokerage(BrokerageUserDO user, List<BrokerageAddReqBO> list, Integer brokerageFrozenDays,
Integer brokeragePercent, Function<BrokerageAddReqBO, Integer> fixedPriceFun, Integer brokeragePercent, BrokerageRecordBizTypeEnum bizType, BrokerageUserTypeEnum sourceUserType) {
BrokerageRecordBizTypeEnum bizType) {
// 1.1 处理冻结时间 // 1.1 处理冻结时间
LocalDateTime unfreezeTime = null; LocalDateTime unfreezeTime = null;
if (brokerageFrozenDays != null && brokerageFrozenDays > 0) { if (brokerageFrozenDays != null && brokerageFrozenDays > 0) {
@ -154,12 +154,20 @@ public class BrokerageRecordServiceImpl implements BrokerageRecordService {
int totalBrokerage = 0; int totalBrokerage = 0;
List<BrokerageRecordDO> records = new ArrayList<>(); List<BrokerageRecordDO> records = new ArrayList<>();
for (BrokerageAddReqBO item : list) { for (BrokerageAddReqBO item : list) {
int brokeragePerItem = calculatePrice(item.getBasePrice(), brokeragePercent, fixedPriceFun.apply(item)); Integer fixedPrice = 0;
if (BrokerageUserTypeEnum.FIRST.equals(sourceUserType)) {
fixedPrice = item.getFirstFixedPrice();
} else if (BrokerageUserTypeEnum.SECOND.equals(sourceUserType)) {
fixedPrice = item.getSecondFixedPrice();
}
int brokeragePerItem = calculatePrice(item.getBasePrice(), brokeragePercent, fixedPrice);
if (brokeragePerItem <= 0) { if (brokeragePerItem <= 0) {
continue; continue;
} }
records.add(BrokerageRecordConvert.INSTANCE.convert(user, bizType, item.getBizId(), records.add(BrokerageRecordConvert.INSTANCE.convert(user, bizType, item.getBizId(),
brokerageFrozenDays, brokeragePerItem, unfreezeTime, bizType.getTitle())); brokerageFrozenDays, brokeragePerItem, unfreezeTime, item.getTitle(),
item.getSourceUserId(), sourceUserType.getType()));
totalBrokerage += brokeragePerItem; totalBrokerage += brokeragePerItem;
} }
if (CollUtil.isEmpty(records)) { if (CollUtil.isEmpty(records)) {

View File

@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.trade.service.brokerage.user;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.trade.controller.admin.brokerage.user.vo.BrokerageUserPageReqVO; import cn.iocoder.yudao.module.trade.controller.admin.brokerage.user.vo.BrokerageUserPageReqVO;
import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.user.BrokerageUserDO; import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.user.BrokerageUserDO;
import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageUserTypeEnum;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
@ -86,14 +87,14 @@ public interface BrokerageUserService {
*/ */
void updateFrozenPriceDecrAndPriceIncr(Long id, Integer frozenPrice); void updateFrozenPriceDecrAndPriceIncr(Long id, Integer frozenPrice);
// TODO @疯狂这个后面可能要支持下二级
/** /**
* 获得推广用户数量一级 * 获得推广用户数量
* *
* @param bindUserId 绑定的推广员编号 * @param bindUserId 绑定的推广员编号
* @param userType 用户类型
* @return 推广用户数量 * @return 推广用户数量
*/ */
Long getBrokerageUserCountByBindUserId(Long bindUserId); Long getBrokerageUserCountByBindUserId(Long bindUserId, BrokerageUserTypeEnum userType);
/** /**
* 会员绑定推广员 * 会员绑定推广员
@ -105,4 +106,11 @@ public interface BrokerageUserService {
*/ */
boolean bindBrokerageUser(Long userId, Long bindUserId, Boolean isNewUser); boolean bindBrokerageUser(Long userId, Long bindUserId, Boolean isNewUser);
/**
* 获取用户是否有分销资格
*
* @param userId 用户编号
* @return 是否有分销资格
*/
Boolean getUserBrokerageEnabled(Long userId);
} }

View File

@ -9,6 +9,7 @@ import cn.iocoder.yudao.module.trade.dal.dataobject.config.TradeConfigDO;
import cn.iocoder.yudao.module.trade.dal.mysql.brokerage.user.BrokerageUserMapper; import cn.iocoder.yudao.module.trade.dal.mysql.brokerage.user.BrokerageUserMapper;
import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageBindModeEnum; import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageBindModeEnum;
import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageEnabledConditionEnum; import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageEnabledConditionEnum;
import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageUserTypeEnum;
import cn.iocoder.yudao.module.trade.service.config.TradeConfigService; import cn.iocoder.yudao.module.trade.service.config.TradeConfigService;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
@ -56,7 +57,7 @@ public class BrokerageUserServiceImpl implements BrokerageUserService {
@Override @Override
public void updateBrokerageUserId(Long id, Long bindUserId) { public void updateBrokerageUserId(Long id, Long bindUserId) {
// 校验存在 // 校验存在
validateBrokerageUserExists(id); BrokerageUserDO brokerageUser = validateBrokerageUserExists(id);
// 情况一清除推广员 // 情况一清除推广员
if (bindUserId == null) { if (bindUserId == null) {
@ -66,7 +67,7 @@ public class BrokerageUserServiceImpl implements BrokerageUserService {
} }
// 情况二修改推广员 // 情况二修改推广员
// TODO @疯狂要复用一些 validateCanBindUser 的校验哈 validateCanBindUser(brokerageUser, bindUserId);
brokerageUserMapper.updateById(new BrokerageUserDO().setId(id) brokerageUserMapper.updateById(new BrokerageUserDO().setId(id)
.setBindUserId(bindUserId).setBindUserTime(LocalDateTime.now())); .setBindUserId(bindUserId).setBindUserTime(LocalDateTime.now()));
} }
@ -85,10 +86,13 @@ public class BrokerageUserServiceImpl implements BrokerageUserService {
} }
} }
private void validateBrokerageUserExists(Long id) { private BrokerageUserDO validateBrokerageUserExists(Long id) {
if (brokerageUserMapper.selectById(id) == null) { BrokerageUserDO brokerageUserDO = brokerageUserMapper.selectById(id);
if (brokerageUserDO == null) {
throw exception(BROKERAGE_USER_NOT_EXISTS); throw exception(BROKERAGE_USER_NOT_EXISTS);
} }
return brokerageUserDO;
} }
@Override @Override
@ -128,19 +132,23 @@ public class BrokerageUserServiceImpl implements BrokerageUserService {
} }
@Override @Override
public Long getBrokerageUserCountByBindUserId(Long bindUserId) { public Long getBrokerageUserCountByBindUserId(Long bindUserId, BrokerageUserTypeEnum userType) {
// TODO @疯狂mapper 封装下哈不直接在 service 调用这种基础 mapper 的基础方法 switch (userType) {
return brokerageUserMapper.selectCount(BrokerageUserDO::getBindUserId, bindUserId); case ALL:
Long firstCount = brokerageUserMapper.selectCountByBindUserId(bindUserId);
Long secondCount = brokerageUserMapper.selectCountByBindUserIdInBindUserId(bindUserId);
return firstCount + secondCount;
case FIRST:
return brokerageUserMapper.selectCountByBindUserId(bindUserId);
case SECOND:
return brokerageUserMapper.selectCountByBindUserIdInBindUserId(bindUserId);
default:
return 0L;
}
} }
// TODO @疯狂因为现在 user 会存在使用验证码直接注册所以 isNewUser 不太好传递我们是不是可以约定绑定的时间createTime 30 秒内就认为新用户
@Override @Override
public boolean bindBrokerageUser(Long userId, Long bindUserId, Boolean isNewUser) { public boolean bindBrokerageUser(Long userId, Long bindUserId, Boolean isNewUser) {
// TODO @疯狂userId 为空搞到参数校验里哇
if (userId == null) {
throw exception(0);
}
// 1. 获得分销用户 // 1. 获得分销用户
boolean isNewBrokerageUser = false; boolean isNewBrokerageUser = false;
BrokerageUserDO brokerageUser = brokerageUserMapper.selectById(userId); BrokerageUserDO brokerageUser = brokerageUserMapper.selectById(userId);
@ -149,20 +157,20 @@ public class BrokerageUserServiceImpl implements BrokerageUserService {
brokerageUser = new BrokerageUserDO().setId(userId).setBrokerageEnabled(false).setBrokeragePrice(0).setFrozenPrice(0); brokerageUser = new BrokerageUserDO().setId(userId).setBrokerageEnabled(false).setBrokeragePrice(0).setFrozenPrice(0);
} }
// 2.1 校验绑定 // 2.1 校验是否能绑定用户
boolean validated = validateCanBindUser(brokerageUser, bindUserId, isNewUser); boolean validated = isUserCanBind(brokerageUser, isNewUser);
if (!validated) { if (!validated) {
return false; return false;
} }
// 2.3 校验能否绑定
// 2.2 绑定用户 validateCanBindUser(brokerageUser, bindUserId);
// 2.3 绑定用户
if (isNewBrokerageUser) { if (isNewBrokerageUser) {
Integer enabledCondition = tradeConfigService.getTradeConfig().getBrokerageEnabledCondition(); Integer enabledCondition = tradeConfigService.getTradeConfig().getBrokerageEnabledCondition();
if (BrokerageEnabledConditionEnum.ALL.getCondition().equals(enabledCondition)) { // 人人分销用户默认就有分销资格 if (BrokerageEnabledConditionEnum.ALL.getCondition().equals(enabledCondition)) { // 人人分销用户默认就有分销资格
// TODO @疯狂应该设置下 brokerageTime而不是 bindUserTime brokerageUser.setBrokerageEnabled(true).setBrokerageTime(LocalDateTime.now());
brokerageUser.setBrokerageEnabled(true).setBindUserTime(LocalDateTime.now());
} }
// TODO @疯狂这里是不是要设置 bindUserIdbindUserTime 字段哈 brokerageUser.setBindUserId(bindUserId).setBindUserTime(LocalDateTime.now());
brokerageUserMapper.insert(brokerageUser); brokerageUserMapper.insert(brokerageUser);
} else { } else {
brokerageUserMapper.updateById(new BrokerageUserDO().setId(userId) brokerageUserMapper.updateById(new BrokerageUserDO().setId(userId)
@ -171,30 +179,27 @@ public class BrokerageUserServiceImpl implements BrokerageUserService {
return true; return true;
} }
// TODO @疯狂validate 方法一般不返回 true false而是抛出异常如果要返回 true false 这种方法名字可以改成 isUserCanBind @Override
private boolean validateCanBindUser(BrokerageUserDO user, Long bindUserId, Boolean isNewUser) { public Boolean getUserBrokerageEnabled(Long userId) {
// TODO @疯狂bindUserId 为空搞到参数校验里哇 // 全局分销功能是否开启
if (bindUserId == null) { TradeConfigDO tradeConfig = tradeConfigService.getTradeConfig();
if (tradeConfig == null || !BooleanUtil.isTrue(tradeConfig.getBrokerageEnabled())) {
return false; return false;
} }
// 用户是否有分销资格
return Optional.ofNullable(getBrokerageUser(userId))
.map(BrokerageUserDO::getBrokerageEnabled)
.orElse(false);
}
private boolean isUserCanBind(BrokerageUserDO user, Boolean isNewUser) {
// 校验分销功能是否启用 // 校验分销功能是否启用
TradeConfigDO tradeConfig = tradeConfigService.getTradeConfig(); TradeConfigDO tradeConfig = tradeConfigService.getTradeConfig();
if (tradeConfig == null || BooleanUtil.isFalse(tradeConfig.getBrokerageEnabled())) { if (tradeConfig == null || !BooleanUtil.isTrue(tradeConfig.getBrokerageEnabled())) {
return false; return false;
} }
// 校验绑定自己
if (Objects.equals(user.getId(), bindUserId)) {
throw exception(BROKERAGE_BIND_SELF);
}
// 校验要绑定的用户有无推广资格
BrokerageUserDO bindUser = brokerageUserMapper.selectById(bindUserId);
if (bindUser == null || BooleanUtil.isFalse(bindUser.getBrokerageEnabled())) {
throw exception(BROKERAGE_BIND_USER_NOT_ENABLED);
}
// 校验分佣模式仅可后台手动设置推广员 // 校验分佣模式仅可后台手动设置推广员
if (BrokerageEnabledConditionEnum.ADMIN.getCondition().equals(tradeConfig.getBrokerageEnabledCondition())) { if (BrokerageEnabledConditionEnum.ADMIN.getCondition().equals(tradeConfig.getBrokerageEnabledCondition())) {
throw exception(BROKERAGE_BIND_CONDITION_ADMIN); throw exception(BROKERAGE_BIND_CONDITION_ADMIN);
@ -211,12 +216,26 @@ public class BrokerageUserServiceImpl implements BrokerageUserService {
} }
} }
return true;
}
private void validateCanBindUser(BrokerageUserDO user, Long bindUserId) {
// 校验要绑定的用户有无推广资格
BrokerageUserDO bindUser = brokerageUserMapper.selectById(bindUserId);
if (bindUser == null || !BooleanUtil.isTrue(bindUser.getBrokerageEnabled())) {
throw exception(BROKERAGE_BIND_USER_NOT_ENABLED);
}
// 校验绑定自己
if (Objects.equals(user.getId(), bindUserId)) {
throw exception(BROKERAGE_BIND_SELF);
}
// TODO @疯狂这块是不是一直查询到根节点中间不允许出现自己就是不能形成环虽然目前是 2 但是未来可能会改多级 = = 环的话就会存在问题哈 // TODO @疯狂这块是不是一直查询到根节点中间不允许出现自己就是不能形成环虽然目前是 2 但是未来可能会改多级 = = 环的话就会存在问题哈
// A->B->A下级不能绑定自己的上级, A->B->C->A可以!! // A->B->A下级不能绑定自己的上级, A->B->C->A可以!!
if (Objects.equals(user.getId(), bindUser.getBindUserId())) { if (Objects.equals(user.getId(), bindUser.getBindUserId())) {
throw exception(BROKERAGE_BIND_LOOP); throw exception(BROKERAGE_BIND_LOOP);
} }
return true;
} }
} }

View File

@ -1,5 +1,7 @@
package cn.iocoder.yudao.module.member.service.user; package cn.iocoder.yudao.module.member.service.user;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
@ -103,6 +105,9 @@ public class MemberUserServiceImpl implements MemberUserService {
@Override @Override
public List<MemberUserDO> getUserList(Collection<Long> ids) { public List<MemberUserDO> getUserList(Collection<Long> ids) {
if (CollUtil.isEmpty(ids)) {
return ListUtil.empty();
}
return memberUserMapper.selectBatchIds(ids); return memberUserMapper.selectBatchIds(ids);
} }