新增bd相关接口

This commit is contained in:
tianfeng 2025-10-28 16:14:57 +08:00
parent 674c88f4a6
commit 4f728d1f5d
15 changed files with 1110 additions and 2 deletions

View File

@ -10,12 +10,17 @@ import com.red.circle.other.app.dto.cmd.team.DbInviteAgentCmd;
import com.red.circle.other.app.dto.cmd.team.DbInviteMessageProcessCmd;
import com.red.circle.other.app.dto.cmd.team.DbLeaderInviteBdCmd;
import com.red.circle.other.app.service.team.TeamBdService;
import com.red.circle.other.app.service.TeamBdBillService;
import com.red.circle.other.app.dto.cmd.TeamBdMemberBillCmd;
import com.red.circle.other.app.dto.cmd.TeamBdMemberBillDetailCmd;
import com.red.circle.other.app.dto.cmd.TeamBdMonthlyIncomeCmd;
import com.red.circle.other.app.dto.clientobject.TeamBdMemberBillCO;
import com.red.circle.other.app.dto.clientobject.TeamBdMemberBillDetailCO;
import com.red.circle.other.app.dto.clientobject.TeamBdMonthlyIncomeCO;
import com.red.circle.other.infra.database.mongo.entity.team.bd.MessageInviteGroup;
import java.util.List;
import com.red.circle.other.inner.model.cmd.team.bd.BdTeamPageQryCmd;
import com.red.circle.other.inner.model.dto.agency.bd.BdTeamInfoDTO;
import com.red.circle.other.inner.model.dto.agency.bd.BdTeamMemberDTO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
@ -40,6 +45,7 @@ import org.springframework.web.bind.annotation.*;
public class TeamBdRestController extends BaseController {
private final TeamBdService teamBdService;
private final TeamBdBillService teamBdBillService;
/**
* 获得bd角色.
@ -232,4 +238,51 @@ public class TeamBdRestController extends BaseController {
return teamBdService.bdPage(qryCmd);
}
// ==================== BD Center 账单相关接口 ====================
/**
* 查询BD成员账单列表.
*
* @param cmd 查询命令
* @return 成员账单列表
* @eo.name 查询BD成员账单列表
* @eo.url /member-bill/list
* @eo.method get
* @eo.request-type formdata
*/
@GetMapping("/member-bill/list")
public List<TeamBdMemberBillCO> queryMemberBillList(@Validated TeamBdMemberBillCmd cmd) {
return teamBdBillService.queryMemberBillList(cmd);
}
/**
* 查询BD成员账单明细列表.
*
* @param cmd 查询命令
* @return 成员账单明细列表
* @eo.name 查询BD成员账单明细列表
* @eo.url /member-bill/detail-list
* @eo.method get
* @eo.request-type formdata
*/
@GetMapping("/member-bill/detail-list")
public List<TeamBdMemberBillDetailCO> queryMemberBillDetailList(@Validated TeamBdMemberBillDetailCmd cmd) {
return teamBdBillService.queryMemberBillDetailList(cmd);
}
/**
* 查询BD团队月度收入汇总.
*
* @param cmd 查询命令
* @return 月度收入汇总
* @eo.name 查询BD团队月度收入
* @eo.url /monthly-income
* @eo.method get
* @eo.request-type formdata
*/
@GetMapping("/monthly-income")
public TeamBdMonthlyIncomeCO queryMonthlyIncome(@Validated TeamBdMonthlyIncomeCmd cmd) {
return teamBdBillService.queryMonthlyIncome(cmd);
}
}

View File

@ -0,0 +1,229 @@
package com.red.circle.other.app.command.bd.query;
import com.red.circle.other.app.convertor.user.UserProfileAppConvertor;
import com.red.circle.other.app.dto.clientobject.TeamBdMemberBillAnchorDetailCO;
import com.red.circle.other.app.dto.clientobject.TeamBdMemberBillDetailCO;
import com.red.circle.other.app.dto.cmd.TeamBdMemberBillDetailCmd;
import com.red.circle.other.domain.gateway.user.UserProfileGateway;
import com.red.circle.other.infra.database.mongo.dto.team.TeamBillSettleResult;
import com.red.circle.other.infra.database.mongo.entity.team.team.TeamBillCycle;
import com.red.circle.other.infra.database.mongo.entity.team.team.TeamMemberTarget;
import com.red.circle.other.infra.database.mongo.entity.team.team.TeamProfile;
import com.red.circle.other.infra.database.mongo.service.team.team.TeamBillCycleService;
import com.red.circle.other.infra.database.mongo.service.team.team.TeamMemberTargetService;
import com.red.circle.other.infra.database.mongo.service.team.team.TeamProfileService;
import com.red.circle.other.infra.database.rds.entity.team.BusinessDevelopmentTeam;
import com.red.circle.other.infra.database.rds.service.team.BusinessDevelopmentTeamService;
import com.red.circle.other.inner.model.dto.agency.agency.TeamCounter;
import com.red.circle.other.inner.model.dto.user.UserProfileDTO;
import com.red.circle.other.inner.enums.team.TeamMemberTargetSettlementResult;
import com.red.circle.tool.core.collection.CollectionUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;
/**
* 查询 BD 成员账单明细执行器.
*
* @author tf
* @since 2025-10-28
*/
@Slf4j
@Component
@RequiredArgsConstructor
public class TeamBdMemberBillDetailQryExe {
private final BusinessDevelopmentTeamService businessDevelopmentTeamService;
private final TeamBillCycleService teamBillCycleService;
private final TeamProfileService teamProfileService;
private final TeamMemberTargetService teamMemberTargetService;
private final UserProfileGateway userProfileGateway;
private final UserProfileAppConvertor userProfileAppConvertor;
/**
* 执行查询.
*
* @param cmd 查询命令
* @return 账单明细列表
*/
public List<TeamBdMemberBillDetailCO> execute(TeamBdMemberBillDetailCmd cmd) {
Long teamId = cmd.getTeamId();
// 2. 查询团队的最新账单列表使用 listLatestBill 获取最近的账单记录
List<TeamBillCycle> billCycleList = teamBillCycleService.listLatestBill(teamId, 10);
if (CollectionUtils.isEmpty(billCycleList)) {
return Collections.emptyList();
}
// 3. 获取团队Profile信息
TeamProfile teamProfile = teamProfileService.getById(teamId);
Integer teamMemberCount = Optional.ofNullable(teamProfile)
.map(TeamProfile::getCounter)
.map(TeamCounter::getMemberQuantity)
.map(Long::intValue)
.orElse(0);
// 4. 遍历每个账单周期组装明细数据
return billCycleList.stream()
.map(billCycle -> buildBillDetailCO(billCycle, teamMemberCount))
.collect(Collectors.toList());
}
/**
* 构建账单明细CO.
*/
private TeamBdMemberBillDetailCO buildBillDetailCO(TeamBillCycle billCycle, Integer teamMemberCount) {
TeamBdMemberBillDetailCO co = new TeamBdMemberBillDetailCO();
// 1. 账单周期日期
String[] dates = parseBillBelongToDateRange(billCycle.getBillBelong());
co.setStartDate(dates[0]);
co.setEndDate(dates[1]);
// 2. 团队人数
co.setTeamMemberCount(teamMemberCount);
// 3. 账单状态
co.setStatus(billCycle.getStatus());
// 4. 团队薪资和充值
TeamBillSettleResult settleResult = billCycle.getSettleResult();
if (settleResult != null) {
co.setTeamSalaryAmount(settleResult.getTotalSalary());
co.setTeamRechargeAmount(BigDecimal.ZERO);
// BD收益计算
BigDecimal salaryRatio = new BigDecimal("0.03"); // 3%
BigDecimal rechargeRatio = new BigDecimal("0.05"); // 5%
co.setBdRatioSalary(salaryRatio.multiply(new BigDecimal("100")));
co.setBdRatioRecharge(rechargeRatio.multiply(new BigDecimal("100")));
BigDecimal bdSalaryIncome = Optional.ofNullable(settleResult.getTotalSalary())
.orElse(BigDecimal.ZERO)
.multiply(salaryRatio);
BigDecimal bdRechargeIncome = Optional.ofNullable(BigDecimal.ZERO)
.orElse(BigDecimal.ZERO)
.multiply(rechargeRatio);
co.setBdIncomeSalary(bdSalaryIncome);
co.setBdIncomeRecharge(bdRechargeIncome);
} else {
co.setTeamSalaryAmount(BigDecimal.ZERO);
co.setTeamRechargeAmount(BigDecimal.ZERO);
co.setBdIncomeSalary(BigDecimal.ZERO);
co.setBdIncomeRecharge(BigDecimal.ZERO);
co.setBdRatioSalary(BigDecimal.ZERO);
co.setBdRatioRecharge(BigDecimal.ZERO);
}
// 5. 查询主播明细列表
List<TeamBdMemberBillAnchorDetailCO> anchorDetails = buildAnchorDetails(
billCycle.getTeamId(), billCycle.getBillBelong());
co.setAnchorDetails(anchorDetails);
return co;
}
/**
* 构建主播明细列表.
*/
private List<TeamBdMemberBillAnchorDetailCO> buildAnchorDetails(Long teamId, Integer billBelong) {
// 1. 查询该账单周期下的所有成员Target
List<TeamMemberTarget> memberTargets = teamMemberTargetService.listByBillBelong(teamId, billBelong);
if (CollectionUtils.isEmpty(memberTargets)) {
return Collections.emptyList();
}
// 2. 获取所有成员的用户信息
Set<Long> userIdSet = memberTargets.stream()
.map(TeamMemberTarget::getUserId)
.collect(Collectors.toSet());
Map<Long, UserProfileDTO> userProfileMap = userProfileAppConvertor.toMapUserProfileDTO(
userProfileGateway.mapByUserIds(userIdSet));
// 3. 组装主播明细
return memberTargets.stream()
.map(target -> buildAnchorDetailCO(target, userProfileMap))
.collect(Collectors.toList());
}
/**
* 构建主播明细CO.
*/
private TeamBdMemberBillAnchorDetailCO buildAnchorDetailCO(
TeamMemberTarget target,
Map<Long, UserProfileDTO> userProfileMap) {
TeamBdMemberBillAnchorDetailCO co = new TeamBdMemberBillAnchorDetailCO();
// 用户信息
UserProfileDTO userProfile = userProfileMap.get(target.getUserId());
if (userProfile != null) {
co.setUserId(target.getUserId());
co.setUserName(userProfile.getUserNickname());
co.setAvatar(userProfile.getUserAvatar());
}
// settlementResult 中获取薪资数据
TeamMemberTargetSettlementResult settlementResult = target.getSettlementResult();
if (settlementResult != null) {
// 薪资totalSalary
co.setSalary(settlementResult.getTotalSalary());
// 充值acceptGiftValue
co.setRecharge(new BigDecimal(Optional.ofNullable(settlementResult.getAcceptGiftValue()).orElse(0L)));
} else {
co.setSalary(BigDecimal.ZERO);
co.setRecharge(BigDecimal.ZERO);
}
// 开播次数 TODO: 从直播记录中统计
co.setHostCount(0);
return co;
}
/**
* 解析 billBelong 为日期范围.
*
* @param billBelong 账单归属格式20251001 20251016
* @return 日期范围数组 [startDate, endDate]格式yyyy.MM.dd
*/
private String[] parseBillBelongToDateRange(Integer billBelong) {
if (billBelong == null) {
return new String[]{"", ""};
}
String billBelongStr = String.valueOf(billBelong);
int year = Integer.parseInt(billBelongStr.substring(0, 4));
int month = Integer.parseInt(billBelongStr.substring(4, 6));
int day = Integer.parseInt(billBelongStr.substring(6, 8));
LocalDate startDate = LocalDate.of(year, month, day);
LocalDate endDate;
if (day == 1) {
// 1-15号的账单周期
endDate = LocalDate.of(year, month, 15);
} else {
// 16-月末的账单周期
endDate = startDate.withDayOfMonth(startDate.lengthOfMonth());
}
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy.MM.dd");
return new String[]{
startDate.format(formatter),
endDate.format(formatter)
};
}
}

View File

@ -0,0 +1,208 @@
package com.red.circle.other.app.command.bd.query;
import com.red.circle.other.app.convertor.user.UserProfileAppConvertor;
import com.red.circle.other.app.dto.clientobject.TeamBdMemberBillCO;
import com.red.circle.other.app.dto.cmd.TeamBdMemberBillCmd;
import com.red.circle.other.domain.gateway.user.UserProfileGateway;
import com.red.circle.other.infra.database.mongo.dto.team.TeamBillSettleResult;
import com.red.circle.other.infra.database.mongo.entity.team.team.TeamBillCycle;
import com.red.circle.other.infra.database.mongo.entity.team.team.TeamProfile;
import com.red.circle.other.infra.database.mongo.service.team.TeamBillCycleUtils;
import com.red.circle.other.infra.database.mongo.service.team.team.TeamBillCycleService;
import com.red.circle.other.infra.database.mongo.service.team.team.TeamProfileService;
import com.red.circle.other.infra.database.rds.entity.team.BusinessDevelopmentTeam;
import com.red.circle.other.infra.database.rds.service.team.BusinessDevelopmentBaseInfoService;
import com.red.circle.other.infra.database.rds.service.team.BusinessDevelopmentTeamService;
import com.red.circle.other.inner.endpoint.team.bd.BdTeamLeaderClient;
import com.red.circle.other.inner.enums.team.TeamBillCycleStatus;
import com.red.circle.other.inner.model.dto.agency.agency.TeamCounter;
import com.red.circle.other.inner.model.dto.user.UserProfileDTO;
import com.red.circle.tool.core.collection.CollectionUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;
/**
* 查询 BD 成员账单列表执行器.
*
* @author tf
* @since 2025-10-28
*/
@Slf4j
@Component
@RequiredArgsConstructor
public class TeamBdMemberBillListQryExe {
private final BusinessDevelopmentTeamService businessDevelopmentTeamService;
private final TeamBillCycleService teamBillCycleService;
private final TeamProfileService teamProfileService;
private final UserProfileGateway userProfileGateway;
private final UserProfileAppConvertor userProfileAppConvertor;
private final BdTeamLeaderClient bdTeamLeaderClient;
/**
* 执行查询.
*
* @param cmd 查询命令
* @return 账单列表
*/
public List<TeamBdMemberBillCO> execute(TeamBdMemberBillCmd cmd) {
List<BusinessDevelopmentTeam> bdTeamList = new ArrayList<>();
Boolean bdLeader = bdTeamLeaderClient.check(cmd.getReqUserId()).getBody();
if (Boolean.TRUE.equals(bdLeader)) {
bdTeamList.addAll(businessDevelopmentTeamService.listByUserId(cmd.getReqUserId()));
} else {
bdTeamList.add(businessDevelopmentTeamService.getByAgentId(cmd.getReqUserId()));
}
// 1. 查询BD关联的所有代理
if (CollectionUtils.isEmpty(bdTeamList)) {
return Collections.emptyList();
}
// 2. 获取团队ID集合
Set<Long> teamIdSet = bdTeamList.stream()
.map(BusinessDevelopmentTeam::getTeamId)
.collect(Collectors.toSet());
// 3. 获取当前账单周期 billBelong
Integer currentBillBelong = TeamBillCycleUtils.getCalcBillBelong();
// 4. 查询当前周期的账单
Map<Long, TeamBillCycle> billCycleMap = teamBillCycleService.mapByTeamIds(
teamIdSet, currentBillBelong, TeamBillCycleStatus.SETTLED);
// 5. 获取团队Profile信息包含团队人数
Map<Long, TeamProfile> teamProfileMap = teamProfileService.mapProfileByIds(teamIdSet);
// 6. 获取代理用户信息
Set<Long> agentIdSet = bdTeamList.stream()
.map(BusinessDevelopmentTeam::getAgentId)
.collect(Collectors.toSet());
Map<Long, UserProfileDTO> userProfileMap = userProfileAppConvertor.toMapUserProfileDTO(
userProfileGateway.mapByUserIds(agentIdSet));
// 7. 组装返回结果
return bdTeamList.stream()
.map(bdTeam -> buildMemberBillCO(bdTeam, billCycleMap, teamProfileMap, userProfileMap, currentBillBelong))
.collect(Collectors.toList());
}
/**
* 构建成员账单CO.
*/
private TeamBdMemberBillCO buildMemberBillCO(
BusinessDevelopmentTeam bdTeam,
Map<Long, TeamBillCycle> billCycleMap,
Map<Long, TeamProfile> teamProfileMap,
Map<Long, UserProfileDTO> userProfileMap,
Integer billBelong) {
TeamBdMemberBillCO co = new TeamBdMemberBillCO();
// 代理用户信息
UserProfileDTO userProfile = userProfileMap.get(bdTeam.getAgentId());
if (userProfile != null) {
co.setAgentId(bdTeam.getAgentId());
co.setAgentName(userProfile.getUserNickname());
co.setAgentAvatar(userProfile.getUserAvatar());
}
// 团队信息
co.setTeamId(bdTeam.getTeamId());
TeamProfile teamProfile = teamProfileMap.get(bdTeam.getTeamId());
if (teamProfile != null) {
Long memberCount = Optional.ofNullable(teamProfile.getCounter())
.map(TeamCounter::getMemberQuantity)
.orElse(0L);
co.setTeamMemberCount(memberCount.intValue());
} else {
co.setTeamMemberCount(0);
}
// 账单周期日期 billBelong 解析
String[] dates = parseBillBelongToDateRange(billBelong);
co.setStartDate(dates[0]);
co.setEndDate(dates[1]);
// 账单周期信息
TeamBillCycle billCycle = billCycleMap.get(bdTeam.getTeamId());
if (billCycle != null && billCycle.getSettleResult() != null) {
TeamBillSettleResult settleResult = billCycle.getSettleResult();
// 团队薪资和充值
co.setTeamSalaryAmount(settleResult.getTotalSalary());
co.setTeamRechargeAmount(BigDecimal.ZERO);
// BD收益计算这里使用默认比例实际应该从配置读取
BigDecimal salaryRatio = new BigDecimal("0.03"); // 3%
BigDecimal rechargeRatio = new BigDecimal("0.05"); // 5%
co.setBdRatioSalary(salaryRatio.multiply(new BigDecimal("100"))); // 转换为百分比
co.setBdRatioRecharge(rechargeRatio.multiply(new BigDecimal("100")));
BigDecimal bdSalaryIncome = Optional.ofNullable(settleResult.getTotalSalary())
.orElse(BigDecimal.ZERO)
.multiply(salaryRatio);
BigDecimal bdRechargeIncome = Optional.ofNullable(BigDecimal.ZERO)
.orElse(BigDecimal.ZERO)
.multiply(rechargeRatio);
co.setBdIncomeSalary(bdSalaryIncome);
co.setBdIncomeRecharge(bdRechargeIncome);
} else {
// 如果没有账单周期设置默认值
co.setTeamSalaryAmount(BigDecimal.ZERO);
co.setTeamRechargeAmount(BigDecimal.ZERO);
co.setBdIncomeSalary(BigDecimal.ZERO);
co.setBdIncomeRecharge(BigDecimal.ZERO);
co.setBdRatioSalary(BigDecimal.ZERO);
co.setBdRatioRecharge(BigDecimal.ZERO);
}
return co;
}
/**
* 解析 billBelong 为日期范围.
*
* @param billBelong 账单归属格式20251001 20251016
* @return 日期范围数组 [startDate, endDate]格式yyyy.MM.dd
*/
private String[] parseBillBelongToDateRange(Integer billBelong) {
if (billBelong == null) {
return new String[]{"", ""};
}
String billBelongStr = String.valueOf(billBelong);
int year = Integer.parseInt(billBelongStr.substring(0, 4));
int month = Integer.parseInt(billBelongStr.substring(4, 6));
int day = Integer.parseInt(billBelongStr.substring(6, 8));
LocalDate startDate = LocalDate.of(year, month, day);
LocalDate endDate;
if (day == 1) {
// 1-15号的账单周期
endDate = LocalDate.of(year, month, 15);
} else {
// 16-月末的账单周期
endDate = startDate.withDayOfMonth(startDate.lengthOfMonth());
}
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy.MM.dd");
return new String[]{
startDate.format(formatter),
endDate.format(formatter)
};
}
}

View File

@ -0,0 +1,190 @@
package com.red.circle.other.app.command.bd.query;
import com.red.circle.other.app.dto.clientobject.TeamBdMonthlyIncomeCO;
import com.red.circle.other.app.dto.cmd.TeamBdMonthlyIncomeCmd;
import com.red.circle.other.infra.database.mongo.dto.team.TeamBillSettleResult;
import com.red.circle.other.infra.database.mongo.entity.team.team.TeamBillCycle;
import com.red.circle.other.infra.database.mongo.service.team.TeamBillCycleUtils;
import com.red.circle.other.infra.database.mongo.service.team.team.TeamBillCycleService;
import com.red.circle.other.infra.database.rds.entity.team.BusinessDevelopmentTeam;
import com.red.circle.other.infra.database.rds.service.team.BusinessDevelopmentTeamService;
import com.red.circle.other.inner.endpoint.team.bd.BdTeamLeaderClient;
import com.red.circle.other.inner.enums.team.TeamBillCycleStatus;
import com.red.circle.tool.core.collection.CollectionUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;
/**
* 查询 BD 团队月度收入执行器.
*
* @author tf
* @since 2025-10-28
*/
@Slf4j
@Component
@RequiredArgsConstructor
public class TeamBdMonthlyIncomeQryExe {
private final BusinessDevelopmentTeamService businessDevelopmentTeamService;
private final TeamBillCycleService teamBillCycleService;
private final BdTeamLeaderClient bdTeamLeaderClient;
/**
* 执行查询.
*
* @param cmd 查询命令
* @return 月度收入汇总
*/
public TeamBdMonthlyIncomeCO execute(TeamBdMonthlyIncomeCmd cmd) {
TeamBdMonthlyIncomeCO co = new TeamBdMonthlyIncomeCO();
// 1. 查询BD关联的所有代理
List<BusinessDevelopmentTeam> bdTeamList = new ArrayList<>();
Boolean bdLeader = bdTeamLeaderClient.check(cmd.getReqUserId()).getBody();
if (Boolean.TRUE.equals(bdLeader)) {
bdTeamList.addAll(businessDevelopmentTeamService.listByUserId(cmd.getReqUserId()));
} else {
bdTeamList.add(businessDevelopmentTeamService.getByAgentId(cmd.getReqUserId()));
}
if (CollectionUtils.isEmpty(bdTeamList)) {
co.setTotalIncome(BigDecimal.ZERO);
co.setPreviousPeriodIncome(BigDecimal.ZERO);
return co;
}
// 2. 获取所有团队ID
Set<Long> teamIdSet = bdTeamList.stream()
.map(BusinessDevelopmentTeam::getTeamId)
.collect(Collectors.toSet());
// 3. 获取当前账单周期 billBelong
Integer currentBillBelong = TeamBillCycleUtils.getCalcBillBelong();
// 4. 获取上一个账单周期 billBelong
Integer previousBillBelong = calculatePreviousBillBelong(currentBillBelong);
// 5. 查询当前周期的账单当月总收入
Map<Long, TeamBillCycle> currentBillCycleMap = teamBillCycleService.mapByTeamIds(
teamIdSet, currentBillBelong, TeamBillCycleStatus.SETTLED);
// 6. 查询上一个周期的账单上期收入
Map<Long, TeamBillCycle> previousBillCycleMap = teamBillCycleService.mapByTeamIds(
teamIdSet, previousBillBelong, TeamBillCycleStatus.SETTLED);
// 8. 计算当月总收入
BigDecimal totalIncome = calculateTotalIncome(currentBillCycleMap);
// 9. 计算上期收入
BigDecimal previousPeriodIncome = calculateTotalIncome(previousBillCycleMap);
co.setTotalIncome(totalIncome);
co.setPreviousPeriodIncome(previousPeriodIncome);
return co;
}
/**
* 计算总收入薪资收入 + 充值收入.
*/
private BigDecimal calculateTotalIncome(Map<Long, TeamBillCycle> billCycleMap) {
if (CollectionUtils.isEmpty(billCycleMap)) {
return BigDecimal.ZERO;
}
BigDecimal salaryRatio = new BigDecimal("0.03"); // 3%
BigDecimal rechargeRatio = new BigDecimal("0.05"); // 5%
BigDecimal totalIncome = BigDecimal.ZERO;
for (TeamBillCycle billCycle : billCycleMap.values()) {
TeamBillSettleResult settleResult = billCycle.getSettleResult();
if (settleResult != null) {
// BD薪资收益
BigDecimal salaryIncome = Optional.ofNullable(settleResult.getTotalSalary())
.orElse(BigDecimal.ZERO)
.multiply(salaryRatio);
// BD充值收益
BigDecimal rechargeIncome = Optional.ofNullable(BigDecimal.ZERO)
.orElse(BigDecimal.ZERO)
.multiply(rechargeRatio);
totalIncome = totalIncome.add(salaryIncome).add(rechargeIncome);
}
}
return totalIncome;
}
/**
* 计算可提现收入所有已完成账单的收入总和.
*/
private BigDecimal calculateAvailableIncome(List<TeamBillCycle> completedBills) {
if (CollectionUtils.isEmpty(completedBills)) {
return BigDecimal.ZERO;
}
BigDecimal salaryRatio = new BigDecimal("0.03"); // 3%
BigDecimal rechargeRatio = new BigDecimal("0.05"); // 5%
BigDecimal availableIncome = BigDecimal.ZERO;
for (TeamBillCycle billCycle : completedBills) {
TeamBillSettleResult settleResult = billCycle.getSettleResult();
if (settleResult != null) {
// BD薪资收益
BigDecimal salaryIncome = Optional.ofNullable(settleResult.getTotalSalary())
.orElse(BigDecimal.ZERO)
.multiply(salaryRatio);
// BD充值收益
BigDecimal rechargeIncome = Optional.ofNullable(BigDecimal.ZERO)
.orElse(BigDecimal.ZERO)
.multiply(rechargeRatio);
availableIncome = availableIncome.add(salaryIncome).add(rechargeIncome);
}
}
return availableIncome;
}
/**
* 计算上一个账单周期.
*
* @param currentBillBelong 当前账单周期格式20251001 20251016
* @return 上一个账单周期
*/
private Integer calculatePreviousBillBelong(Integer currentBillBelong) {
if (currentBillBelong == null) {
return null;
}
String billBelongStr = String.valueOf(currentBillBelong);
int year = Integer.parseInt(billBelongStr.substring(0, 4));
int month = Integer.parseInt(billBelongStr.substring(4, 6));
int day = Integer.parseInt(billBelongStr.substring(6, 8));
LocalDate currentDate = LocalDate.of(year, month, day);
LocalDate previousDate;
if (day == 1) {
// 当前是1号上一个周期是上个月的16号
previousDate = currentDate.minusMonths(1).withDayOfMonth(16);
} else {
// 当前是16号上一个周期是本月的1号
previousDate = currentDate.withDayOfMonth(1);
}
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
return Integer.parseInt(previousDate.format(formatter));
}
}

View File

@ -0,0 +1,48 @@
package com.red.circle.other.app.service;
import com.red.circle.other.app.command.bd.query.TeamBdMemberBillDetailQryExe;
import com.red.circle.other.app.command.bd.query.TeamBdMemberBillListQryExe;
import com.red.circle.other.app.command.bd.query.TeamBdMonthlyIncomeQryExe;
import com.red.circle.other.app.dto.clientobject.TeamBdMemberBillCO;
import com.red.circle.other.app.dto.clientobject.TeamBdMemberBillDetailCO;
import com.red.circle.other.app.dto.clientobject.TeamBdMonthlyIncomeCO;
import com.red.circle.other.app.dto.cmd.TeamBdMemberBillCmd;
import com.red.circle.other.app.dto.cmd.TeamBdMemberBillDetailCmd;
import com.red.circle.other.app.dto.cmd.TeamBdMonthlyIncomeCmd;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* BD 账单服务实现.
*
* @author tf
* @since 2025-10-28
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class TeamBdBillServiceImpl implements TeamBdBillService {
private final TeamBdMemberBillListQryExe teamBdMemberBillListQryExe;
private final TeamBdMemberBillDetailQryExe teamBdMemberBillDetailQryExe;
private final TeamBdMonthlyIncomeQryExe teamBdMonthlyIncomeQryExe;
@Override
public List<TeamBdMemberBillCO> queryMemberBillList(TeamBdMemberBillCmd cmd) {
return teamBdMemberBillListQryExe.execute(cmd);
}
@Override
public List<TeamBdMemberBillDetailCO> queryMemberBillDetailList(TeamBdMemberBillDetailCmd cmd) {
return teamBdMemberBillDetailQryExe.execute(cmd);
}
@Override
public TeamBdMonthlyIncomeCO queryMonthlyIncome(TeamBdMonthlyIncomeCmd cmd) {
return teamBdMonthlyIncomeQryExe.execute(cmd);
}
}

View File

@ -0,0 +1,52 @@
package com.red.circle.other.app.dto.clientobject;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
/**
* BD 成员账单明细 - 主播明细对象.
*
* @author tf
* @since 2025-10-28
*/
@Data
public class TeamBdMemberBillAnchorDetailCO implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 主播用户ID.
*/
@JsonSerialize(using = ToStringSerializer.class)
private Long userId;
/**
* 主播用户名.
*/
private String userName;
/**
* 主播头像.
*/
private String avatar;
/**
* 主播薪资单位.
*/
private BigDecimal salary;
/**
* 主播充值单位.
*/
private BigDecimal recharge;
/**
* 主播开播次数.
*/
private Integer hostCount;
}

View File

@ -0,0 +1,88 @@
package com.red.circle.other.app.dto.clientobject;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
/**
* BD 成员账单客户端对象.
*
* @author tf
* @since 2025-10-28
*/
@Data
public class TeamBdMemberBillCO implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 代理用户ID.
*/
@JsonSerialize(using = ToStringSerializer.class)
private Long agentId;
/**
* 代理用户名.
*/
private String agentName;
/**
* 代理头像.
*/
private String agentAvatar;
/**
* 团队ID.
*/
@JsonSerialize(using = ToStringSerializer.class)
private Long teamId;
/**
* 团队人数.
*/
private Integer teamMemberCount;
/**
* 账单周期开始日期.
*/
private String startDate;
/**
* 账单周期结束日期.
*/
private String endDate;
/**
* 团队薪资总额单位.
*/
private BigDecimal teamSalaryAmount;
/**
* 团队充值总额单位.
*/
private BigDecimal teamRechargeAmount;
/**
* BD收益-薪资部分单位.
*/
private BigDecimal bdIncomeSalary;
/**
* BD收益-充值部分单位.
*/
private BigDecimal bdIncomeRecharge;
/**
* BD收益比例-薪资%.
*/
private BigDecimal bdRatioSalary;
/**
* BD收益比例-充值%.
*/
private BigDecimal bdRatioRecharge;
}

View File

@ -0,0 +1,76 @@
package com.red.circle.other.app.dto.clientobject;
import com.red.circle.other.inner.enums.team.TeamBillCycleStatus;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.List;
/**
* BD 成员账单明细客户端对象.
*
* @author tf
* @since 2025-10-28
*/
@Data
public class TeamBdMemberBillDetailCO implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 账单周期开始日期.
*/
private String startDate;
/**
* 账单周期结束日期.
*/
private String endDate;
/**
* 团队人数.
*/
private Integer teamMemberCount;
/**
* 团队薪资总额单位.
*/
private BigDecimal teamSalaryAmount;
/**
* 团队充值总额单位.
*/
private BigDecimal teamRechargeAmount;
/**
* BD收益-薪资部分单位.
*/
private BigDecimal bdIncomeSalary;
/**
* BD收益-充值部分单位.
*/
private BigDecimal bdIncomeRecharge;
/**
* BD收益比例-薪资%.
*/
private BigDecimal bdRatioSalary;
/**
* BD收益比例-充值%.
*/
private BigDecimal bdRatioRecharge;
/**
* 账单状态.
*/
private TeamBillCycleStatus status;
/**
* 主播明细列表.
*/
private List<TeamBdMemberBillAnchorDetailCO> anchorDetails;
}

View File

@ -0,0 +1,28 @@
package com.red.circle.other.app.dto.clientobject;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
/**
* BD 团队月度收入客户端对象.
*
* @author tf
* @since 2025-10-28
*/
@Data
public class TeamBdMonthlyIncomeCO implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 当月总收入单位.
*/
private BigDecimal totalIncome;
/**
* 上期收入单位.
*/
private BigDecimal previousPeriodIncome;
}

View File

@ -0,0 +1,41 @@
package com.red.circle.other.app.dto.cmd;
import com.red.circle.common.business.dto.PageQueryCmd;
import com.red.circle.common.business.dto.cmd.AppExtCommand;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.time.LocalDate;
/**
* BD 成员账单查询命令.
*
* @author tf
* @since 2025-10-28
*/
@EqualsAndHashCode(callSuper = true)
@Data
public class TeamBdMemberBillCmd extends AppExtCommand {
/**
* 查询开始日期.
*/
private LocalDate startDate;
/**
* 查询结束日期.
*/
private LocalDate endDate;
/**
* 页码.
*/
private Integer pageNum;
/**
* 每页大小.
*/
private Integer pageSize;
}

View File

@ -0,0 +1,22 @@
package com.red.circle.other.app.dto.cmd;
import com.red.circle.common.business.dto.cmd.AppExtCommand;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* BD 成员账单明细查询命令.
*
* @author tf
* @since 2025-10-28
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class TeamBdMemberBillDetailCmd extends AppExtCommand {
@NotNull(message = "teamId不能为空")
private Long teamId;
}

View File

@ -0,0 +1,17 @@
package com.red.circle.other.app.dto.cmd;
import com.red.circle.common.business.dto.cmd.AppExtCommand;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* BD 团队月度收入查询命令.
*
* @author tf
* @since 2025-10-28
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class TeamBdMonthlyIncomeCmd extends AppExtCommand {
}

View File

@ -0,0 +1,44 @@
package com.red.circle.other.app.service;
import com.red.circle.other.app.dto.clientobject.TeamBdMemberBillCO;
import com.red.circle.other.app.dto.clientobject.TeamBdMemberBillDetailCO;
import com.red.circle.other.app.dto.clientobject.TeamBdMonthlyIncomeCO;
import com.red.circle.other.app.dto.cmd.TeamBdMemberBillCmd;
import com.red.circle.other.app.dto.cmd.TeamBdMemberBillDetailCmd;
import com.red.circle.other.app.dto.cmd.TeamBdMonthlyIncomeCmd;
import java.util.List;
/**
* BD 账单服务接口.
*
* @author tf
* @since 2025-10-28
*/
public interface TeamBdBillService {
/**
* 查询 BD 成员账单列表.
*
* @param cmd 查询命令
* @return 账单列表
*/
List<TeamBdMemberBillCO> queryMemberBillList(TeamBdMemberBillCmd cmd);
/**
* 查询 BD 成员账单明细列表.
*
* @param cmd 查询命令
* @return 账单明细列表
*/
List<TeamBdMemberBillDetailCO> queryMemberBillDetailList(TeamBdMemberBillDetailCmd cmd);
/**
* 查询 BD 团队月度收入汇总.
*
* @param cmd 查询命令
* @return 月度收入
*/
TeamBdMonthlyIncomeCO queryMonthlyIncome(TeamBdMonthlyIncomeCmd cmd);
}

View File

@ -84,6 +84,11 @@ public interface BusinessDevelopmentTeamService extends
*/
List<BusinessDevelopmentTeam> listByUserId(Long userId);
/**
* 获得bd的成员.
*/
BusinessDevelopmentTeam getByAgentId(Long agentId);
/**
* 根据bd用户id解散这个bd团队.
*/

View File

@ -174,6 +174,13 @@ public class BusinessDevelopmentTeamServiceImpl extends
.list();
}
@Override
public BusinessDevelopmentTeam getByAgentId(Long agentId) {
return query().eq(BusinessDevelopmentTeam::getAgentId, agentId)
.last("LIMIT 1")
.getOne();
}
@Override
public void deleteAllByUserId(Long userId) {
delete()