分国家发放工资

This commit is contained in:
hy001 2026-05-21 11:19:04 +08:00
parent a0cc63d784
commit a8424ad5e3
17 changed files with 1306 additions and 142 deletions

View File

@ -9,16 +9,20 @@ import com.red.circle.other.inner.model.cmd.team.TeamApplicationProcessApprovalC
import com.red.circle.other.inner.model.cmd.team.TeamApplicationProcessApprovalQryCmd;
import com.red.circle.other.inner.model.cmd.team.TeamApplicationProcessChangeCmd;
import com.red.circle.other.inner.model.cmd.team.TeamApplicationProcessQryCmd;
import com.red.circle.other.inner.model.cmd.team.TeamChangeOwnerCmd;
import com.red.circle.other.inner.model.cmd.team.TeamCreateCmd;
import com.red.circle.other.inner.model.cmd.team.TeamSalaryBackQryCmd;
import com.red.circle.other.inner.model.cmd.team.TeamTableQryCmd;
import com.red.circle.other.inner.model.cmd.team.TeamUpdateCmd;
import com.red.circle.other.inner.model.dto.agency.agency.TeamApplicationProcessApprovalDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamApplicationProcessDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamMemberDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamProfileDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamSalaryPaymentDTO;
import com.red.circle.other.inner.model.cmd.team.TeamChangeOwnerCmd;
import com.red.circle.other.inner.model.cmd.team.TeamCreateCmd;
import com.red.circle.other.inner.model.cmd.team.TeamManualSalaryPaymentCmd;
import com.red.circle.other.inner.model.cmd.team.TeamManualSalaryPaymentQryCmd;
import com.red.circle.other.inner.model.cmd.team.TeamSalaryBackQryCmd;
import com.red.circle.other.inner.model.cmd.team.TeamTableQryCmd;
import com.red.circle.other.inner.model.cmd.team.TeamUpdateCmd;
import com.red.circle.other.inner.model.dto.agency.agency.TeamApplicationProcessApprovalDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamApplicationProcessDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamManualSalaryPaymentDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamManualSalaryPaymentResultDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamMemberDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamProfileDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamSalaryPaymentDTO;
import java.util.List;
import java.util.Set;
@ -146,9 +150,23 @@ public interface TeamProfileClientApi {
/**
* 后台列表查询.
*/
@PostMapping("/listByCondition")
ResultResponse<PageResult<TeamSalaryPaymentDTO>> listByCondition(
@RequestBody TeamSalaryBackQryCmd cmd);
@PostMapping("/listByCondition")
ResultResponse<PageResult<TeamSalaryPaymentDTO>> listByCondition(
@RequestBody TeamSalaryBackQryCmd cmd);
/**
* 后台手动发放主播工资预览.
*/
@PostMapping("/manualSalary/page")
ResultResponse<PageResult<TeamManualSalaryPaymentDTO>> manualSalaryPage(
@RequestBody TeamManualSalaryPaymentQryCmd cmd);
/**
* 后台手动发放主播工资.
*/
@PostMapping("/manualSalary/pay")
ResultResponse<List<TeamManualSalaryPaymentResultDTO>> manualSalaryPay(
@RequestBody TeamManualSalaryPaymentCmd cmd);
/**
* 获得区域下的所有团队ID.

View File

@ -0,0 +1,40 @@
package com.red.circle.other.inner.model.cmd.team;
import com.red.circle.framework.dto.Command;
import java.io.Serial;
import java.util.List;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* 后台手动发放主播工资.
*/
@EqualsAndHashCode(callSuper = true)
@Data
@Accessors(chain = true)
public class TeamManualSalaryPaymentCmd extends Command {
@Serial
private static final long serialVersionUID = 1L;
/**
* 归属系统.
*/
private String sysOrigin;
/**
* 国家 code.
*/
private String countryCode;
/**
* 指定发放用户为空时按国家全量发放.
*/
private List<Long> userIds;
/**
* 后台操作人.
*/
private Long operationUserId;
}

View File

@ -0,0 +1,34 @@
package com.red.circle.other.inner.model.cmd.team;
import com.red.circle.framework.core.dto.PageCommand;
import java.io.Serial;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* 后台手动发放主播工资预览.
*/
@EqualsAndHashCode(callSuper = true)
@Data
@Accessors(chain = true)
public class TeamManualSalaryPaymentQryCmd extends PageCommand {
@Serial
private static final long serialVersionUID = 1L;
/**
* 归属系统.
*/
private String sysOrigin;
/**
* 国家 code.
*/
private String countryCode;
/**
* 用户 id.
*/
private Long userId;
}

View File

@ -0,0 +1,103 @@
package com.red.circle.other.inner.model.dto.agency.agency;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* 后台手动发放主播工资预览.
*/
@Data
@Accessors(chain = true)
public class TeamManualSalaryPaymentDTO implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 成员目标记录 id.
*/
private String id;
/**
* 归属系统.
*/
private String sysOrigin;
/**
* 国家 code.
*/
private String countryCode;
/**
* 区域 id.
*/
private String region;
/**
* 团队 id.
*/
@JsonSerialize(using = ToStringSerializer.class)
private Long teamId;
/**
* 团队拥有者用户 id.
*/
@JsonSerialize(using = ToStringSerializer.class)
private Long teamOwnId;
/**
* 主播用户 id.
*/
@JsonSerialize(using = ToStringSerializer.class)
private Long userId;
/**
* 当前账期.
*/
private Integer billBelong;
/**
* 上次结算的积分.
*/
private Long lastSettlementTarget;
/**
* 本次积分.
*/
private Long currentTarget;
/**
* 本次积分档位.
*/
private Integer policyLevel;
/**
* 是否满足任意档位.
*/
private Boolean satisfyLevel;
/**
* 应发工资.
*/
private BigDecimal payableSalary;
/**
* 当前账期总发放工资.
*/
private BigDecimal totalIssuedSalary;
/**
* 当前工资余额.
*/
private BigDecimal currentSalaryBalance;
/**
* 当前档位是否已发放.
*/
private Boolean paid;
}

View File

@ -0,0 +1,88 @@
package com.red.circle.other.inner.model.dto.agency.agency;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* 后台手动发放主播工资结果.
*/
@Data
@Accessors(chain = true)
public class TeamManualSalaryPaymentResultDTO implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 工资发放明细 id.
*/
private String id;
/**
* 归属系统.
*/
private String sysOrigin;
/**
* 国家 code.
*/
private String countryCode;
/**
* 区域 id.
*/
private String region;
/**
* 团队 id.
*/
@JsonSerialize(using = ToStringSerializer.class)
private Long teamId;
/**
* 团队拥有者用户 id.
*/
@JsonSerialize(using = ToStringSerializer.class)
private Long teamOwnId;
/**
* 主播用户 id.
*/
@JsonSerialize(using = ToStringSerializer.class)
private Long userId;
/**
* 当前账期.
*/
private Integer billBelong;
/**
* 发放前工资余额.
*/
private BigDecimal beforeSalaryBalance;
/**
* 发放后工资余额.
*/
private BigDecimal afterSalaryBalance;
/**
* 本次发放工资.
*/
private BigDecimal issuedSalary;
/**
* 本次积分.
*/
private Long currentTarget;
/**
* 发放档位.
*/
private Integer policyLevel;
}

View File

@ -1,14 +1,21 @@
package com.red.circle.console.adapter.app.team;
import com.red.circle.console.app.dto.clienobject.team.TeamSalaryCO;
import com.red.circle.console.app.service.app.team.TeamSalaryBackService;
import com.red.circle.framework.dto.PageResult;
import com.red.circle.framework.web.controller.BaseController;
import com.red.circle.other.inner.model.cmd.team.TeamSalaryBackQryCmd;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
package com.red.circle.console.adapter.app.team;
import com.red.circle.console.app.dto.clienobject.team.TeamManualSalaryCO;
import com.red.circle.console.app.dto.clienobject.team.TeamManualSalaryResultCO;
import com.red.circle.console.app.dto.clienobject.team.TeamSalaryCO;
import com.red.circle.console.app.service.app.team.TeamSalaryBackService;
import com.red.circle.framework.dto.PageResult;
import com.red.circle.framework.web.controller.BaseController;
import com.red.circle.other.inner.model.cmd.team.TeamManualSalaryPaymentCmd;
import com.red.circle.other.inner.model.cmd.team.TeamManualSalaryPaymentQryCmd;
import com.red.circle.other.inner.model.cmd.team.TeamSalaryBackQryCmd;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 团队自动发送工资凭证.
@ -26,8 +33,26 @@ public class TeamSalaryRestController extends BaseController {
* 工资凭证列表.
*/
@GetMapping("/list")
public PageResult<TeamSalaryCO> listByCondition(TeamSalaryBackQryCmd query) {
return teamSalaryBackService.listByCondition(query);
}
}
public PageResult<TeamSalaryCO> listByCondition(TeamSalaryBackQryCmd query) {
return teamSalaryBackService.listByCondition(query);
}
/**
* 手动工资发放预览.
*/
@GetMapping("/manual/page")
public PageResult<TeamManualSalaryCO> manualSalaryPage(TeamManualSalaryPaymentQryCmd query) {
return teamSalaryBackService.manualSalaryPage(query);
}
/**
* 确认手动发放工资.
*/
@PostMapping("/manual/pay")
public List<TeamManualSalaryResultCO> manualSalaryPay(
@RequestBody TeamManualSalaryPaymentCmd cmd) {
cmd.setOperationUserId(getReqUserId());
return teamSalaryBackService.manualSalaryPay(cmd);
}
}

View File

@ -1,25 +1,33 @@
package com.red.circle.console.app.service.app.team;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.red.circle.console.app.convertor.team.TeamAppConvertor;
import com.red.circle.console.app.dto.clienobject.team.TeamSalaryCO;
import com.red.circle.console.app.dto.clienobject.team.TeamSalaryDetailsCO;
import com.red.circle.framework.core.asserts.ResponseAssert;
import com.red.circle.framework.dto.PageResult;
import com.red.circle.other.inner.endpoint.team.target.TeamProfileClient;
import com.red.circle.other.inner.model.cmd.team.TeamSalaryBackQryCmd;
import com.red.circle.other.inner.model.dto.agency.agency.TeamSalaryPaymentDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamSalaryPaymentDetailsDTO;
import com.red.circle.tool.core.collection.CollectionUtils;
import com.red.circle.tool.core.text.StringUtils;
import com.red.circle.other.inner.model.dto.user.UserProfileDTO;
import com.red.circle.other.inner.endpoint.user.region.RegionConfigClient;
import com.red.circle.other.inner.endpoint.user.user.UserProfileClient;
import java.util.Comparator;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import com.google.common.collect.Sets;
import com.red.circle.console.app.dto.clienobject.team.TeamManualSalaryCO;
import com.red.circle.console.app.dto.clienobject.team.TeamManualSalaryResultCO;
import com.red.circle.console.app.convertor.team.TeamAppConvertor;
import com.red.circle.console.app.dto.clienobject.team.TeamSalaryCO;
import com.red.circle.console.app.dto.clienobject.team.TeamSalaryDetailsCO;
import com.red.circle.framework.core.asserts.ResponseAssert;
import com.red.circle.framework.dto.PageResult;
import com.red.circle.other.inner.endpoint.team.target.TeamProfileClient;
import com.red.circle.other.inner.model.cmd.team.TeamManualSalaryPaymentCmd;
import com.red.circle.other.inner.model.cmd.team.TeamManualSalaryPaymentQryCmd;
import com.red.circle.other.inner.model.cmd.team.TeamSalaryBackQryCmd;
import com.red.circle.other.inner.model.dto.agency.agency.TeamManualSalaryPaymentDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamManualSalaryPaymentResultDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamSalaryPaymentDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamSalaryPaymentDetailsDTO;
import com.red.circle.tool.core.collection.CollectionUtils;
import com.red.circle.tool.core.text.StringUtils;
import com.red.circle.other.inner.model.dto.user.UserProfileDTO;
import com.red.circle.other.inner.endpoint.user.region.RegionConfigClient;
import com.red.circle.other.inner.endpoint.user.user.UserProfileClient;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
@ -110,8 +118,41 @@ public class TeamSalaryBackServiceImpl implements TeamSalaryBackService {
return salaryVO;
}).filter(Objects::nonNull).collect(Collectors.toList()));
return page;
}
return page;
}
@Override
public PageResult<TeamManualSalaryCO> manualSalaryPage(TeamManualSalaryPaymentQryCmd query) {
PageResult<TeamManualSalaryPaymentDTO> pageResult = ResponseAssert.requiredSuccess(
teamProfileClient.manualSalaryPage(query));
PageResult<TeamManualSalaryCO> page = pageResult.convert(this::toManualSalaryCO);
if (page.checkRecordsEmpty()) {
return page;
}
Map<Long, UserProfileDTO> userProfileMap = getUserProfileMap(page.getRecords().stream()
.map(TeamManualSalaryCO::getUserId)
.collect(Collectors.toSet()));
page.getRecords().forEach(record -> record.setUserProfile(userProfileMap.get(record.getUserId())));
return page;
}
@Override
public List<TeamManualSalaryResultCO> manualSalaryPay(TeamManualSalaryPaymentCmd cmd) {
List<TeamManualSalaryPaymentResultDTO> results = ResponseAssert.requiredSuccess(
teamProfileClient.manualSalaryPay(cmd));
if (CollectionUtils.isEmpty(results)) {
return List.of();
}
Map<Long, UserProfileDTO> userProfileMap = getUserProfileMap(results.stream()
.map(TeamManualSalaryPaymentResultDTO::getUserId)
.collect(Collectors.toSet()));
return results.stream()
.map(result -> toManualSalaryResultCO(result)
.setUserProfile(userProfileMap.get(result.getUserId())))
.collect(Collectors.toList());
}
private Map<String, String> getMapRegionName(TeamSalaryBackQryCmd query) {
return ResponseAssert.requiredSuccess(
@ -144,7 +185,58 @@ public class TeamSalaryBackServiceImpl implements TeamSalaryBackService {
return Maps.newHashMap();
}
return ResponseAssert.requiredSuccess(userProfileClient.mapByUserIds(userIds));
}
}
return ResponseAssert.requiredSuccess(userProfileClient.mapByUserIds(userIds));
}
private Map<Long, UserProfileDTO> getUserProfileMap(Collection<Long> userIds) {
if (CollectionUtils.isEmpty(userIds)) {
return Maps.newHashMap();
}
Set<Long> userIdSet = userIds.stream()
.filter(Objects::nonNull)
.collect(Collectors.toSet());
if (CollectionUtils.isEmpty(userIdSet)) {
return Maps.newHashMap();
}
return ResponseAssert.requiredSuccess(userProfileClient.mapByUserIds(userIdSet));
}
private TeamManualSalaryCO toManualSalaryCO(TeamManualSalaryPaymentDTO dto) {
return new TeamManualSalaryCO()
.setId(dto.getId())
.setSysOrigin(dto.getSysOrigin())
.setCountryCode(dto.getCountryCode())
.setRegion(dto.getRegion())
.setTeamId(dto.getTeamId())
.setTeamOwnId(dto.getTeamOwnId())
.setUserId(dto.getUserId())
.setBillBelong(dto.getBillBelong())
.setLastSettlementTarget(dto.getLastSettlementTarget())
.setCurrentTarget(dto.getCurrentTarget())
.setPolicyLevel(dto.getPolicyLevel())
.setSatisfyLevel(dto.getSatisfyLevel())
.setPayableSalary(dto.getPayableSalary())
.setTotalIssuedSalary(dto.getTotalIssuedSalary())
.setCurrentSalaryBalance(dto.getCurrentSalaryBalance())
.setPaid(dto.getPaid());
}
private TeamManualSalaryResultCO toManualSalaryResultCO(
TeamManualSalaryPaymentResultDTO dto) {
return new TeamManualSalaryResultCO()
.setId(dto.getId())
.setSysOrigin(dto.getSysOrigin())
.setCountryCode(dto.getCountryCode())
.setRegion(dto.getRegion())
.setTeamId(dto.getTeamId())
.setTeamOwnId(dto.getTeamOwnId())
.setUserId(dto.getUserId())
.setBillBelong(dto.getBillBelong())
.setBeforeSalaryBalance(dto.getBeforeSalaryBalance())
.setAfterSalaryBalance(dto.getAfterSalaryBalance())
.setIssuedSalary(dto.getIssuedSalary())
.setCurrentTarget(dto.getCurrentTarget())
.setPolicyLevel(dto.getPolicyLevel());
}
}

View File

@ -0,0 +1,46 @@
package com.red.circle.console.app.dto.clienobject.team;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.red.circle.other.inner.model.dto.user.UserProfileDTO;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* 后台手动发放主播工资预览.
*/
@Data
@Accessors(chain = true)
public class TeamManualSalaryCO implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
private String id;
private String sysOrigin;
private String countryCode;
private String region;
@JsonSerialize(using = ToStringSerializer.class)
private Long teamId;
@JsonSerialize(using = ToStringSerializer.class)
private Long teamOwnId;
@JsonSerialize(using = ToStringSerializer.class)
private Long userId;
private UserProfileDTO userProfile;
private Integer billBelong;
private Long lastSettlementTarget;
private Long currentTarget;
private Integer policyLevel;
private Boolean satisfyLevel;
private BigDecimal payableSalary;
private BigDecimal totalIssuedSalary;
private BigDecimal currentSalaryBalance;
private Boolean paid;
}

View File

@ -0,0 +1,43 @@
package com.red.circle.console.app.dto.clienobject.team;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.red.circle.other.inner.model.dto.user.UserProfileDTO;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* 后台手动发放主播工资结果.
*/
@Data
@Accessors(chain = true)
public class TeamManualSalaryResultCO implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
private String id;
private String sysOrigin;
private String countryCode;
private String region;
@JsonSerialize(using = ToStringSerializer.class)
private Long teamId;
@JsonSerialize(using = ToStringSerializer.class)
private Long teamOwnId;
@JsonSerialize(using = ToStringSerializer.class)
private Long userId;
private UserProfileDTO userProfile;
private Integer billBelong;
private BigDecimal beforeSalaryBalance;
private BigDecimal afterSalaryBalance;
private BigDecimal issuedSalary;
private Long currentTarget;
private Integer policyLevel;
}

View File

@ -1,9 +1,14 @@
package com.red.circle.console.app.service.app.team;
import com.red.circle.console.app.dto.clienobject.team.TeamSalaryCO;
import com.red.circle.framework.dto.PageResult;
import com.red.circle.other.inner.model.cmd.team.TeamSalaryBackQryCmd;
import com.red.circle.console.app.dto.clienobject.team.TeamSalaryCO;
import com.red.circle.console.app.dto.clienobject.team.TeamManualSalaryCO;
import com.red.circle.console.app.dto.clienobject.team.TeamManualSalaryResultCO;
import com.red.circle.framework.dto.PageResult;
import com.red.circle.other.inner.model.cmd.team.TeamManualSalaryPaymentCmd;
import com.red.circle.other.inner.model.cmd.team.TeamManualSalaryPaymentQryCmd;
import com.red.circle.other.inner.model.cmd.team.TeamSalaryBackQryCmd;
import java.util.List;
/**
* 团队自动发送工资凭证.
@ -15,6 +20,16 @@ public interface TeamSalaryBackService {
/**
* 根据条件查询列表.
*/
PageResult<TeamSalaryCO> listByCondition(TeamSalaryBackQryCmd query);
}
PageResult<TeamSalaryCO> listByCondition(TeamSalaryBackQryCmd query);
/**
* 手动工资发放预览.
*/
PageResult<TeamManualSalaryCO> manualSalaryPage(TeamManualSalaryPaymentQryCmd query);
/**
* 确认手动发放工资.
*/
List<TeamManualSalaryResultCO> manualSalaryPay(TeamManualSalaryPaymentCmd cmd);
}

View File

@ -2,12 +2,13 @@ package com.red.circle.other.infra.database.mongo.service.team.team;
import com.red.circle.other.infra.database.mongo.entity.team.team.TeamMemberTarget;
import com.red.circle.other.infra.database.mongo.dto.team.query.TeamMemberTargetQuery;
import com.red.circle.other.inner.enums.team.*;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.red.circle.other.inner.enums.team.*;
import java.math.BigDecimal;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* 成员工作目标.
@ -243,11 +244,17 @@ public interface TeamMemberTargetService {
* @param monthlyBillBelong 月度账期202511
* @return 月度汇总后的成员目标dailyTargets 包含整月数据settlementResult 为累计值
*/
TeamMemberTarget aggregateMonthlyTarget(Long teamId, Long userId, Integer monthlyBillBelong);
/**
* 团队用户已结算的工资
*/
TeamMemberTarget aggregateMonthlyTarget(Long teamId, Long userId, Integer monthlyBillBelong);
/**
* 按国家查询当前可手动发放工资的成员目标.
*/
List<TeamMemberTarget> listManualSalaryTargets(String sysOrigin, String countryCode,
Collection<Long> userIds, Integer billBelong);
/**
* 团队用户已结算的工资
*/
BigDecimal listMemberSettledAndSumSalary(Long teamId, Long userId, TeamMemberRole role);
}

View File

@ -3,24 +3,27 @@ package com.red.circle.other.infra.database.mongo.service.team.team.impl;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.mongodb.BasicDBObject;
import com.mongodb.client.model.Filters;
import com.mongodb.client.result.UpdateResult;
import com.red.circle.component.mongodb.config.MongoPrimaryService;
import com.red.circle.framework.mybatis.constant.PageConstant;
import com.red.circle.other.infra.database.mongo.entity.team.team.TeamMemberTarget;
import com.red.circle.other.infra.database.mongo.dto.team.query.TeamMemberTargetQuery;
import com.red.circle.other.infra.database.mongo.service.team.team.TeamMemberTargetService;
import com.mongodb.client.model.Filters;
import com.mongodb.client.result.UpdateResult;
import com.red.circle.component.mongodb.config.MongoPrimaryService;
import com.red.circle.framework.mybatis.constant.PageConstant;
import com.red.circle.other.infra.common.sys.CountryCodeAliasSupport;
import com.red.circle.other.infra.database.mongo.entity.team.team.TeamMemberTarget;
import com.red.circle.other.infra.database.mongo.dto.team.query.TeamMemberTargetQuery;
import com.red.circle.other.infra.database.mongo.service.team.team.TeamMemberTargetService;
import com.red.circle.other.inner.enums.team.*;
import com.red.circle.tool.core.collection.CollectionUtils;
import com.red.circle.tool.core.date.TimestampUtils;
import com.red.circle.tool.core.date.ZonedDateTimeAsiaRiyadhUtils;
import com.red.circle.tool.core.date.ZonedId;
import com.red.circle.tool.core.date.ZonedDateTimeAsiaRiyadhUtils;
import com.red.circle.tool.core.date.ZonedId;
import com.red.circle.tool.core.sequence.IdWorkerUtils;
import com.red.circle.tool.core.text.StringUtils;
import java.math.BigDecimal;
import java.sql.Timestamp;
import java.time.ZonedDateTime;
import java.time.temporal.TemporalAdjusters;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@ -49,8 +52,9 @@ import static com.red.circle.other.infra.database.mongo.service.team.TeamBillCyc
@RequiredArgsConstructor
public class TeamMemberTargetServiceImpl implements TeamMemberTargetService {
private final MongoTemplate mongoTemplate;
private final MongoPrimaryService mongoPrimaryService;
private final MongoTemplate mongoTemplate;
private final MongoPrimaryService mongoPrimaryService;
private final CountryCodeAliasSupport countryCodeAliasSupport;
@Override
public void createMemberTarget(TeamMemberTarget param) {
@ -815,12 +819,32 @@ public class TeamMemberTargetServiceImpl implements TeamMemberTargetService {
aggregated.setRemarks(allRemarks);
}
return aggregated;
}
@Override
public BigDecimal listMemberSettledAndSumSalary(Long teamId, Long userId, TeamMemberRole role) {
Query query = new Query();
return aggregated;
}
@Override
public List<TeamMemberTarget> listManualSalaryTargets(String sysOrigin, String countryCode,
Collection<Long> userIds, Integer billBelong) {
Criteria criteria = Criteria.where("sysOrigin").is(sysOrigin)
.and("history").is(Boolean.FALSE)
.and("billBelong").is(billBelong);
if (StringUtils.isNotBlank(countryCode)) {
criteria.and("countryCode").in(countryCodeAliasSupport.getMatchCodes(countryCode));
}
if (CollectionUtils.isNotEmpty(userIds)) {
criteria.and("userId").in(userIds);
}
return mongoTemplate.find(Query.query(criteria)
.with(Sort.by(Order.desc("timeId"))),
TeamMemberTarget.class);
}
@Override
public BigDecimal listMemberSettledAndSumSalary(Long teamId, Long userId, TeamMemberRole role) {
Query query = new Query();
query.addCriteria(Criteria.where("teamId").is(teamId))
.addCriteria(Criteria.where("userId").is(userId))
.addCriteria(Criteria.where("status").is(TeamMemberTargetStatus.CREDITED));

View File

@ -11,16 +11,20 @@ import com.red.circle.other.inner.model.cmd.team.TeamApplicationProcessApprovalC
import com.red.circle.other.inner.model.cmd.team.TeamApplicationProcessApprovalQryCmd;
import com.red.circle.other.inner.model.cmd.team.TeamApplicationProcessChangeCmd;
import com.red.circle.other.inner.model.cmd.team.TeamApplicationProcessQryCmd;
import com.red.circle.other.inner.model.cmd.team.TeamChangeOwnerCmd;
import com.red.circle.other.inner.model.cmd.team.TeamCreateCmd;
import com.red.circle.other.inner.model.cmd.team.TeamSalaryBackQryCmd;
import com.red.circle.other.inner.model.cmd.team.TeamTableQryCmd;
import com.red.circle.other.inner.model.cmd.team.TeamUpdateCmd;
import com.red.circle.other.inner.model.dto.agency.agency.TeamApplicationProcessApprovalDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamApplicationProcessDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamMemberDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamProfileDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamSalaryPaymentDTO;
import com.red.circle.other.inner.model.cmd.team.TeamChangeOwnerCmd;
import com.red.circle.other.inner.model.cmd.team.TeamCreateCmd;
import com.red.circle.other.inner.model.cmd.team.TeamManualSalaryPaymentCmd;
import com.red.circle.other.inner.model.cmd.team.TeamManualSalaryPaymentQryCmd;
import com.red.circle.other.inner.model.cmd.team.TeamSalaryBackQryCmd;
import com.red.circle.other.inner.model.cmd.team.TeamTableQryCmd;
import com.red.circle.other.inner.model.cmd.team.TeamUpdateCmd;
import com.red.circle.other.inner.model.dto.agency.agency.TeamApplicationProcessApprovalDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamApplicationProcessDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamManualSalaryPaymentDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamManualSalaryPaymentResultDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamMemberDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamProfileDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamSalaryPaymentDTO;
import java.util.List;
import java.util.Set;
import lombok.RequiredArgsConstructor;
@ -162,13 +166,25 @@ public class TeamProfileClientEndpoint implements TeamProfileClientApi {
}
@Override
public ResultResponse<PageResult<TeamSalaryPaymentDTO>> listByCondition(
TeamSalaryBackQryCmd cmd) {
return ResultResponse.success(teamProfileClientService.listByCondition(cmd));
}
@Override
public ResultResponse<Set<Long>> listIdByRegion(String regionId) {
public ResultResponse<PageResult<TeamSalaryPaymentDTO>> listByCondition(
TeamSalaryBackQryCmd cmd) {
return ResultResponse.success(teamProfileClientService.listByCondition(cmd));
}
@Override
public ResultResponse<PageResult<TeamManualSalaryPaymentDTO>> manualSalaryPage(
TeamManualSalaryPaymentQryCmd cmd) {
return ResultResponse.success(teamProfileClientService.manualSalaryPage(cmd));
}
@Override
public ResultResponse<List<TeamManualSalaryPaymentResultDTO>> manualSalaryPay(
TeamManualSalaryPaymentCmd cmd) {
return ResultResponse.success(teamProfileClientService.manualSalaryPay(cmd));
}
@Override
public ResultResponse<Set<Long>> listIdByRegion(String regionId) {
return ResultResponse.success(teamProfileClientService.listIdByRegion(regionId));
}

View File

@ -0,0 +1,24 @@
package com.red.circle.other.app.inner.service.team;
import com.red.circle.framework.dto.PageResult;
import com.red.circle.other.inner.model.cmd.team.TeamManualSalaryPaymentCmd;
import com.red.circle.other.inner.model.cmd.team.TeamManualSalaryPaymentQryCmd;
import com.red.circle.other.inner.model.dto.agency.agency.TeamManualSalaryPaymentDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamManualSalaryPaymentResultDTO;
import java.util.List;
/**
* 后台手动发放主播工资.
*/
public interface TeamManualSalaryPaymentClientService {
/**
* 预览当前账期可发放工资.
*/
PageResult<TeamManualSalaryPaymentDTO> page(TeamManualSalaryPaymentQryCmd cmd);
/**
* 发放当前筛选国家的工资.
*/
List<TeamManualSalaryPaymentResultDTO> pay(TeamManualSalaryPaymentCmd cmd);
}

View File

@ -5,19 +5,23 @@ import com.red.circle.other.inner.model.cmd.team.StatusTeamBatchCmd;
import com.red.circle.other.inner.model.cmd.team.TeamApplicationProcessApprovalCmd;
import com.red.circle.other.inner.model.cmd.team.TeamApplicationProcessApprovalQryCmd;
import com.red.circle.other.inner.model.cmd.team.TeamApplicationProcessChangeCmd;
import com.red.circle.other.inner.model.cmd.team.TeamApplicationProcessQryCmd;
import com.red.circle.other.inner.model.cmd.team.TeamChangeOwnerCmd;
import com.red.circle.other.inner.model.cmd.team.TeamCreateCmd;
import com.red.circle.other.inner.model.cmd.team.TeamProfileCmd;
import com.red.circle.other.inner.model.cmd.team.TeamSalaryBackQryCmd;
import com.red.circle.other.inner.model.cmd.team.TeamTableQryCmd;
import com.red.circle.other.inner.model.cmd.team.TeamUpdateCmd;
import com.red.circle.other.inner.model.dto.agency.agency.TeamApplicationProcessApprovalDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamApplicationProcessDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamContact;
import com.red.circle.other.inner.model.dto.agency.agency.TeamMemberDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamPolicyManagerDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamProfileDTO;
import com.red.circle.other.inner.model.cmd.team.TeamApplicationProcessQryCmd;
import com.red.circle.other.inner.model.cmd.team.TeamChangeOwnerCmd;
import com.red.circle.other.inner.model.cmd.team.TeamCreateCmd;
import com.red.circle.other.inner.model.cmd.team.TeamManualSalaryPaymentCmd;
import com.red.circle.other.inner.model.cmd.team.TeamManualSalaryPaymentQryCmd;
import com.red.circle.other.inner.model.cmd.team.TeamProfileCmd;
import com.red.circle.other.inner.model.cmd.team.TeamSalaryBackQryCmd;
import com.red.circle.other.inner.model.cmd.team.TeamTableQryCmd;
import com.red.circle.other.inner.model.cmd.team.TeamUpdateCmd;
import com.red.circle.other.inner.model.dto.agency.agency.TeamApplicationProcessApprovalDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamApplicationProcessDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamContact;
import com.red.circle.other.inner.model.dto.agency.agency.TeamManualSalaryPaymentDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamManualSalaryPaymentResultDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamMemberDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamPolicyManagerDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamProfileDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamRemark;
import com.red.circle.other.inner.model.dto.agency.agency.TeamSalaryPaymentDTO;
import java.util.List;
@ -83,11 +87,15 @@ public interface TeamProfileClientService {
Map<String, TeamPolicyManagerDTO> mapRegionRelease(Set<String> regions);
Long changeTeamOwnUser(TeamChangeOwnerCmd param);
PageResult<TeamSalaryPaymentDTO> listByCondition(TeamSalaryBackQryCmd cmd);
Set<Long> listIdByRegion(String regionId);
Long changeTeamOwnUser(TeamChangeOwnerCmd param);
PageResult<TeamSalaryPaymentDTO> listByCondition(TeamSalaryBackQryCmd cmd);
PageResult<TeamManualSalaryPaymentDTO> manualSalaryPage(TeamManualSalaryPaymentQryCmd cmd);
List<TeamManualSalaryPaymentResultDTO> manualSalaryPay(TeamManualSalaryPaymentCmd cmd);
Set<Long> listIdByRegion(String regionId);
List<TeamProfileDTO> listRegionAll(String region);

View File

@ -0,0 +1,563 @@
package com.red.circle.other.app.inner.service.team.impl;
import com.red.circle.common.business.core.enums.ReceiptType;
import com.red.circle.component.redis.service.RedisService;
import com.red.circle.framework.core.asserts.ResponseAssert;
import com.red.circle.framework.core.response.CommonErrorCode;
import com.red.circle.framework.core.response.ResponseErrorCode;
import com.red.circle.framework.dto.PageResult;
import com.red.circle.other.app.inner.service.team.TeamManualSalaryPaymentClientService;
import com.red.circle.other.infra.database.mongo.dto.team.TeamBillMemberTarget;
import com.red.circle.other.infra.database.mongo.dto.team.TeamPolicy;
import com.red.circle.other.infra.database.mongo.dto.team.TeamSalaryPaymentDetails;
import com.red.circle.other.infra.database.mongo.entity.team.team.TeamMember;
import com.red.circle.other.infra.database.mongo.entity.team.team.TeamMemberTarget;
import com.red.circle.other.infra.database.mongo.entity.team.team.TeamPolicyManager;
import com.red.circle.other.infra.database.mongo.entity.team.team.TeamProfile;
import com.red.circle.other.infra.database.mongo.entity.team.team.TeamSalaryPayment;
import com.red.circle.other.infra.database.mongo.service.team.TeamBillCycleUtils;
import com.red.circle.other.infra.database.mongo.service.team.team.TeamMemberService;
import com.red.circle.other.infra.database.mongo.service.team.team.TeamMemberTargetService;
import com.red.circle.other.infra.database.mongo.service.team.team.TeamPolicyManagerService;
import com.red.circle.other.infra.database.mongo.service.team.team.TeamProfileService;
import com.red.circle.other.infra.database.mongo.service.team.team.TeamSalaryPaymentService;
import com.red.circle.other.inner.enums.team.TeamPolicyTypeEnum;
import com.red.circle.other.inner.model.cmd.team.TeamManualSalaryPaymentCmd;
import com.red.circle.other.inner.model.cmd.team.TeamManualSalaryPaymentQryCmd;
import com.red.circle.other.inner.model.dto.agency.agency.TeamManualSalaryPaymentDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamManualSalaryPaymentResultDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamSalaryPaymentRemark;
import com.red.circle.other.inner.model.dto.agency.agency.TeamStatus;
import com.red.circle.tool.core.collection.CollectionUtils;
import com.red.circle.tool.core.date.TimestampUtils;
import com.red.circle.tool.core.sequence.IdWorkerUtils;
import com.red.circle.tool.core.text.StringUtils;
import com.red.circle.tool.core.tuple.PennyAmount;
import com.red.circle.wallet.inner.endpoint.bank.UserBankBalanceClient;
import com.red.circle.wallet.inner.endpoint.bank.UserBankRunningWaterClient;
import com.red.circle.wallet.inner.model.cmd.UserBankBalanceIncrCmd;
import com.red.circle.wallet.inner.model.dto.BankBalanceDTO;
import com.red.circle.wallet.inner.model.dto.UserBankRunningWaterDTO;
import com.red.circle.wallet.inner.model.enums.UserBankWaterEvent;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
/**
* 后台手动发放主播工资.
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class TeamManualSalaryPaymentClientServiceImpl implements
TeamManualSalaryPaymentClientService {
private static final String MANUAL_SALARY_LOCK_PREFIX = "TeamManualSalaryPayment:";
private static final int LOCK_SECONDS = 300;
private final RedisService redisService;
private final TeamMemberService teamMemberService;
private final TeamProfileService teamProfileService;
private final UserBankBalanceClient userBankBalanceClient;
private final TeamMemberTargetService teamMemberTargetService;
private final TeamSalaryPaymentService teamSalaryPaymentService;
private final TeamPolicyManagerService teamPolicyManagerService;
private final UserBankRunningWaterClient userBankRunningWaterClient;
@Override
public PageResult<TeamManualSalaryPaymentDTO> page(TeamManualSalaryPaymentQryCmd cmd) {
validatePage(cmd);
List<ManualSalaryRow> rows = buildRows(cmd.getSysOrigin(), cmd.getCountryCode(),
singletonUserId(cmd.getUserId()), TeamBillCycleUtils.getCalcBillBelong());
int cursor = 1;
int limit = 20;
if (Objects.nonNull(cmd.getPageQuery())) {
cursor = Math.max(1, Objects.isNull(cmd.getPageQuery().getCursor()) ? 1
: cmd.getPageQuery().getCursor());
limit = Math.max(1, Objects.isNull(cmd.getPageQuery().getLimit()) ? 20
: cmd.getPageQuery().getLimit());
}
int fromIndex = Math.min((cursor - 1) * limit, rows.size());
int toIndex = Math.min(fromIndex + limit, rows.size());
PageResult<TeamManualSalaryPaymentDTO> pageResult = new PageResult<>();
pageResult.setCurrent(cursor);
pageResult.setSize(limit);
pageResult.setTotal((long) rows.size());
pageResult.setRecords(rows.subList(fromIndex, toIndex).stream()
.map(ManualSalaryRow::getDto)
.collect(Collectors.toList()));
return pageResult;
}
@Override
public List<TeamManualSalaryPaymentResultDTO> pay(TeamManualSalaryPaymentCmd cmd) {
validatePay(cmd);
Integer billBelong = TeamBillCycleUtils.getCalcBillBelong();
String lockKey = MANUAL_SALARY_LOCK_PREFIX + cmd.getSysOrigin() + ":" + cmd.getCountryCode()
+ ":" + billBelong;
ResponseAssert.isTrue(CommonErrorCode.OPERATION_CONFLICT,
redisService.lock(lockKey, LOCK_SECONDS));
try {
List<ManualSalaryRow> rows = buildRows(cmd.getSysOrigin(), cmd.getCountryCode(),
normalizeUserIds(cmd.getUserIds()), billBelong);
List<TeamManualSalaryPaymentResultDTO> results = new ArrayList<>();
for (ManualSalaryRow row : rows) {
if (CollectionUtils.isEmpty(row.getPayments())) {
continue;
}
TeamSalaryPayment payment = teamSalaryPaymentService.createIfAbsent(row.getUserId(),
billBelong, row.getSysOrigin());
for (PolicyPayment policyPayment : row.getPayments()) {
if (Boolean.TRUE.equals(teamSalaryPaymentService.checkRepeatPayment(payment.getId(),
row.getUserId(), row.getRegion(), policyPayment.getLevel(), Boolean.TRUE))) {
continue;
}
BigDecimal beforeBalance = getAccurateBalance(row.getUserId());
String detailsId = IdWorkerUtils.getIdStr();
Boolean incrSuccess = ResponseAssert.requiredSuccess(userBankBalanceClient.incr(new UserBankBalanceIncrCmd()
.setUserId(row.getUserId())
.setSysOrigin(row.getSysOrigin())
.setAmount(PennyAmount.ofDollar(policyPayment.getSalary()))));
ResponseAssert.isTrue(CommonErrorCode.OPERATION_CONFLICT, Boolean.TRUE.equals(incrSuccess));
BigDecimal afterBalance = getAccurateBalance(row.getUserId());
addRunningWater(detailsId, row.getUserId(), row.getSysOrigin(),
policyPayment.getSalary(), afterBalance, cmd.getOperationUserId());
teamSalaryPaymentService.paymentSalary(payment.getId(), new TeamSalaryPaymentDetails()
.setId(detailsId)
.setSysOrigin(row.getSysOrigin())
.setTeamId(row.getTeamId())
.setMemberTargetId(row.getMemberTargetId())
.setTeamOwnId(row.getTeamOwnId())
.setTeamMemberId(row.getUserId())
.setTeamBillId(Long.valueOf(billBelong))
.setTeamRegionId(row.getRegion())
.setRemarks(List.of(new TeamSalaryPaymentRemark()
.setRemark("后台手动发放主播工资")
.setCreateTime(TimestampUtils.now())))
.setAnchor(Boolean.TRUE)
.setHostSalaryToAgent(Boolean.FALSE)
.setTarget(row.getCurrentTarget())
.setSalary(policyPayment.getSalary())
.setPolicyLevel(policyPayment.getLevel())
.setPolicy(policyPayment.getPolicy()));
updatePaymentPolicyMap(payment.getId(), row.getRegion(), row.getPolicyManager());
results.add(new TeamManualSalaryPaymentResultDTO()
.setId(detailsId)
.setSysOrigin(row.getSysOrigin())
.setCountryCode(row.getCountryCode())
.setRegion(row.getRegion())
.setTeamId(row.getTeamId())
.setTeamOwnId(row.getTeamOwnId())
.setUserId(row.getUserId())
.setBillBelong(billBelong)
.setBeforeSalaryBalance(beforeBalance)
.setAfterSalaryBalance(afterBalance)
.setIssuedSalary(policyPayment.getSalary())
.setCurrentTarget(row.getCurrentTarget())
.setPolicyLevel(policyPayment.getLevel()));
}
}
return results;
} finally {
redisService.unlock(lockKey);
}
}
private List<ManualSalaryRow> buildRows(String sysOrigin, String countryCode, Set<Long> userIds,
Integer billBelong) {
List<TeamMemberTarget> targets = teamMemberTargetService.listManualSalaryTargets(sysOrigin,
countryCode, userIds, billBelong);
if (CollectionUtils.isEmpty(targets)) {
return List.of();
}
targets = targets.stream()
.filter(target -> CollectionUtils.isNotEmpty(target.getDailyTargets()))
.filter(target -> target.sumAcceptGiftValue() > 0)
.collect(Collectors.toList());
if (CollectionUtils.isEmpty(targets)) {
return List.of();
}
Set<Long> memberIds = targets.stream()
.map(TeamMemberTarget::getUserId)
.filter(Objects::nonNull)
.collect(Collectors.toSet());
Set<Long> teamIds = targets.stream()
.map(TeamMemberTarget::getTeamId)
.filter(Objects::nonNull)
.collect(Collectors.toSet());
Map<Long, TeamMember> memberMap = teamMemberService.mapByMemberIds(memberIds);
Map<Long, TeamProfile> teamProfileMap = teamProfileService.mapProfileByIds(teamIds);
Map<String, TeamPolicyManager> policyCache = CollectionUtils.newHashMap();
return targets.stream()
.map(target -> buildRow(target, memberMap, teamProfileMap, policyCache, billBelong,
countryCode))
.filter(Objects::nonNull)
.sorted(Comparator.comparing(ManualSalaryRow::getCurrentTarget).reversed()
.thenComparing(ManualSalaryRow::getUserId))
.collect(Collectors.toList());
}
private Set<Long> singletonUserId(Long userId) {
if (Objects.isNull(userId)) {
return Set.of();
}
return Set.of(userId);
}
private Set<Long> normalizeUserIds(List<Long> userIds) {
if (CollectionUtils.isEmpty(userIds)) {
return Set.of();
}
return userIds.stream()
.filter(Objects::nonNull)
.collect(Collectors.toSet());
}
private ManualSalaryRow buildRow(TeamMemberTarget target, Map<Long, TeamMember> memberMap,
Map<Long, TeamProfile> teamProfileMap, Map<String, TeamPolicyManager> policyCache,
Integer billBelong, String requestedCountryCode) {
TeamMember member = memberMap.get(target.getUserId());
TeamProfile teamProfile = teamProfileMap.get(target.getTeamId());
if (Objects.isNull(member) || Objects.isNull(teamProfile)
|| !Objects.equals(member.getTeamId(), target.getTeamId())
|| TeamStatus.CLOSE.equals(teamProfile.getStatus())) {
return null;
}
String region = StringUtils.isNotBlank(target.getRegion()) ? target.getRegion()
: teamProfile.getRegion();
String countryCode = StringUtils.isNotBlank(target.getCountryCode()) ? target.getCountryCode()
: Optional.ofNullable(teamProfile.getCountry()).map(c -> c.getCountryCode()).orElse(null);
if (StringUtils.isBlank(countryCode)) {
countryCode = requestedCountryCode;
}
TeamPolicyManager policyManager = getPolicyManager(target.getSysOrigin(), region, countryCode,
policyCache);
TeamBillMemberTarget billTarget = calculateBillTarget(target, policyManager);
TeamSalaryPayment payment = teamSalaryPaymentService.getOneById(paymentId(target.getUserId(),
billBelong));
List<PolicyPayment> policyPayments = listUnpaidPolicyPayments(policyManager, billTarget,
payment, target.getUserId(), region);
BigDecimal payableSalary = policyPayments.stream()
.map(PolicyPayment::getSalary)
.reduce(BigDecimal.ZERO, BigDecimal::add)
.setScale(2, RoundingMode.DOWN);
TeamManualSalaryPaymentDTO dto = new TeamManualSalaryPaymentDTO()
.setId(target.getId())
.setSysOrigin(target.getSysOrigin())
.setCountryCode(countryCode)
.setRegion(region)
.setTeamId(target.getTeamId())
.setTeamOwnId(teamProfile.getOwnUserId())
.setUserId(target.getUserId())
.setBillBelong(billBelong)
.setLastSettlementTarget(getLastSettlementTarget(payment))
.setCurrentTarget(target.sumAcceptGiftValue())
.setPolicyLevel(billTarget.getLevel())
.setSatisfyLevel(billTarget.getSatisfyLevel())
.setPayableSalary(payableSalary)
.setTotalIssuedSalary(getTotalIssuedSalary(payment))
.setCurrentSalaryBalance(getAccurateBalance(target.getUserId()))
.setPaid(Objects.nonNull(billTarget.getLevel())
&& BigDecimal.ZERO.compareTo(payableSalary) == 0);
return new ManualSalaryRow(dto, target, teamProfile, policyManager, billTarget, policyPayments);
}
private TeamPolicyManager getPolicyManager(String sysOrigin, String region, String countryCode,
Map<String, TeamPolicyManager> policyCache) {
if (StringUtils.isBlank(sysOrigin) || StringUtils.isBlank(region)) {
return null;
}
String key = sysOrigin + ":" + region + ":" + countryCode;
if (policyCache.containsKey(key)) {
return policyCache.get(key);
}
TeamPolicyManager policyManager = teamPolicyManagerService.getReleaseByRegionAndType(sysOrigin,
region, null, countryCode);
if (Objects.isNull(policyManager)) {
policyManager = teamPolicyManagerService.listReleaseTeamPolicy(sysOrigin, region).stream()
.filter(this::moneyPolicy)
.filter(policy -> StringUtils.isBlank(policy.getCountryCode()))
.findFirst()
.orElse(null);
}
policyCache.put(key, policyManager);
return policyManager;
}
private boolean moneyPolicy(TeamPolicyManager policyManager) {
return Objects.nonNull(policyManager)
&& (StringUtils.isBlank(policyManager.getPolicyType())
|| Objects.equals(TeamPolicyTypeEnum.MONEY.name(), policyManager.getPolicyType()));
}
private TeamBillMemberTarget calculateBillTarget(TeamMemberTarget target,
TeamPolicyManager policyManager) {
if (Objects.nonNull(policyManager) && CollectionUtils.isNotEmpty(policyManager.getPolicy())) {
return TeamBillCycleUtils.calculatorPolicyTarget(target, policyManager);
}
return new TeamBillMemberTarget()
.setTargetId(target.getTimeId())
.setSysOrigin(target.getSysOrigin())
.setUserId(target.getUserId())
.setAcceptGiftValue(target.sumAcceptGiftValue())
.setGiveGiftValue(target.sumGiveGiftValue())
.setRoomValue(target.sumRoomValue())
.setOnlineTime(target.sumAllTargetTime())
.setMemberSalary(BigDecimal.ZERO)
.setOwnSalary(BigDecimal.ZERO)
.setTotalSalary(BigDecimal.ZERO)
.setSatisfyLevel(Boolean.FALSE);
}
private List<PolicyPayment> listUnpaidPolicyPayments(TeamPolicyManager policyManager,
TeamBillMemberTarget billTarget, TeamSalaryPayment payment, Long userId, String region) {
if (Objects.isNull(policyManager) || CollectionUtils.isEmpty(policyManager.getPolicy())
|| Objects.isNull(billTarget.getLevel())) {
return List.of();
}
List<TeamPolicy> completePolicies = policyManager.getPolicy().stream()
.filter(policy -> Objects.nonNull(policy.getLevel()))
.filter(policy -> policy.getLevel() <= billTarget.getLevel())
.sorted(Comparator.comparing(TeamPolicy::getLevel))
.collect(Collectors.toList());
if (CollectionUtils.isEmpty(completePolicies)) {
return List.of();
}
List<PolicyPayment> payments = new ArrayList<>();
for (int i = 0; i < completePolicies.size(); i++) {
TeamPolicy policy = completePolicies.get(i);
if (Objects.nonNull(payment) && Boolean.TRUE.equals(
teamSalaryPaymentService.checkRepeatPayment(payment.getId(), userId, region,
policy.getLevel(), Boolean.TRUE))) {
continue;
}
BigDecimal salary = Optional.ofNullable(policy.getMemberSalary()).orElse(BigDecimal.ZERO);
if (i > 0) {
salary = salary.subtract(Optional.ofNullable(completePolicies.get(i - 1).getMemberSalary())
.orElse(BigDecimal.ZERO));
}
salary = salary.setScale(2, RoundingMode.DOWN);
if (salary.compareTo(BigDecimal.ZERO) <= 0) {
continue;
}
payments.add(new PolicyPayment(policy, policy.getLevel(), salary));
}
return payments;
}
private Long getLastSettlementTarget(TeamSalaryPayment payment) {
return Optional.ofNullable(payment)
.map(TeamSalaryPayment::getSalarySendDetails)
.stream()
.flatMap(List::stream)
.filter(this::validAnchorDetails)
.max(Comparator.comparing(details -> Optional.ofNullable(details.getCreateTime())
.orElse(new Timestamp(0))))
.map(TeamSalaryPaymentDetails::getTarget)
.orElse(0L);
}
private BigDecimal getTotalIssuedSalary(TeamSalaryPayment payment) {
return Optional.ofNullable(payment)
.map(TeamSalaryPayment::getSalarySendDetails)
.stream()
.flatMap(List::stream)
.filter(this::validAnchorDetails)
.map(details -> Optional.ofNullable(details.getSalary()).orElse(BigDecimal.ZERO))
.reduce(BigDecimal.ZERO, BigDecimal::add)
.setScale(2, RoundingMode.DOWN);
}
private boolean validAnchorDetails(TeamSalaryPaymentDetails details) {
return Objects.nonNull(details)
&& Boolean.TRUE.equals(details.getAnchor())
&& !Boolean.TRUE.equals(details.getRecycled());
}
private BigDecimal getAccurateBalance(Long userId) {
BankBalanceDTO balance = ResponseAssert.requiredSuccess(userBankBalanceClient.getBalance(userId));
return Optional.ofNullable(balance)
.map(BankBalanceDTO::getAccurateBalance)
.orElse(BigDecimal.ZERO)
.setScale(2, RoundingMode.DOWN);
}
private void addRunningWater(String trackId, Long userId, String sysOrigin, BigDecimal salary,
BigDecimal afterBalance, Long operationUserId) {
try {
userBankRunningWaterClient.add(new UserBankRunningWaterDTO()
.setId(IdWorkerUtils.getId())
.setUserId(userId)
.setSysOrigin(sysOrigin)
.setType(ReceiptType.INCOME.getType())
.setAmount(salary)
.setEvent(UserBankWaterEvent.SYSTEM_SETTLEMENT_WAGES.name())
.setEventDescribe(UserBankWaterEvent.SYSTEM_SETTLEMENT_WAGES.getDescribe())
.setTrackId(trackId)
.setRemark("后台手动发放主播工资")
.setBalance(afterBalance)
.setCreateTime(TimestampUtils.now())
.setCreateUser(Optional.ofNullable(operationUserId).orElse(0L))
.setCreateUserOrigin(1));
} catch (Exception e) {
log.error("[后台手动发放主播工资] 插入用户银行流水失败, userId:{}, trackId:{}", userId, trackId, e);
}
}
private void updatePaymentPolicyMap(String paymentId, String region,
TeamPolicyManager policyManager) {
if (StringUtils.isBlank(paymentId) || StringUtils.isBlank(region)
|| Objects.isNull(policyManager)) {
return;
}
Map<String, TeamPolicyManager> policyMap = teamSalaryPaymentService.mapPolicy(paymentId);
policyMap.put(region, policyManager);
teamSalaryPaymentService.updatePolicyMap(paymentId, policyMap);
}
private void validatePage(TeamManualSalaryPaymentQryCmd cmd) {
ResponseAssert.notNull(ResponseErrorCode.REQUEST_PARAMETER_ERROR, cmd);
ResponseAssert.notBlank(ResponseErrorCode.REQUEST_PARAMETER_ERROR, cmd.getSysOrigin());
ResponseAssert.notBlank(ResponseErrorCode.REQUEST_PARAMETER_ERROR, cmd.getCountryCode());
}
private void validatePay(TeamManualSalaryPaymentCmd cmd) {
ResponseAssert.notNull(ResponseErrorCode.REQUEST_PARAMETER_ERROR, cmd);
ResponseAssert.notBlank(ResponseErrorCode.REQUEST_PARAMETER_ERROR, cmd.getSysOrigin());
ResponseAssert.notBlank(ResponseErrorCode.REQUEST_PARAMETER_ERROR, cmd.getCountryCode());
}
private String paymentId(Long userId, Integer billBelong) {
return userId + "_" + billBelong;
}
private static class ManualSalaryRow {
private final TeamManualSalaryPaymentDTO dto;
private final TeamMemberTarget target;
private final TeamProfile teamProfile;
private final TeamPolicyManager policyManager;
private final TeamBillMemberTarget billTarget;
private final List<PolicyPayment> payments;
private ManualSalaryRow(TeamManualSalaryPaymentDTO dto, TeamMemberTarget target,
TeamProfile teamProfile, TeamPolicyManager policyManager, TeamBillMemberTarget billTarget,
List<PolicyPayment> payments) {
this.dto = dto;
this.target = target;
this.teamProfile = teamProfile;
this.policyManager = policyManager;
this.billTarget = billTarget;
this.payments = payments;
}
private TeamManualSalaryPaymentDTO getDto() {
return dto;
}
private String getMemberTargetId() {
return target.getId();
}
private String getSysOrigin() {
return target.getSysOrigin();
}
private String getCountryCode() {
return dto.getCountryCode();
}
private String getRegion() {
return dto.getRegion();
}
private Long getTeamId() {
return target.getTeamId();
}
private Long getTeamOwnId() {
return teamProfile.getOwnUserId();
}
private Long getUserId() {
return target.getUserId();
}
private Long getCurrentTarget() {
return dto.getCurrentTarget();
}
private TeamPolicyManager getPolicyManager() {
return policyManager;
}
private List<PolicyPayment> getPayments() {
return payments;
}
}
private static class PolicyPayment {
private final TeamPolicy policy;
private final Integer level;
private final BigDecimal salary;
private PolicyPayment(TeamPolicy policy, Integer level, BigDecimal salary) {
this.policy = policy;
this.level = level;
this.salary = salary;
}
private TeamPolicy getPolicy() {
return policy;
}
private Integer getLevel() {
return level;
}
private BigDecimal getSalary() {
return salary;
}
}
}

View File

@ -14,8 +14,9 @@ import com.red.circle.mq.business.model.event.BadgeOperateEvent;
import com.red.circle.mq.business.model.event.team.TeamSalaryCountEvent;
import com.red.circle.mq.rocket.business.producer.UserMqMessageService;
import com.red.circle.mq.rocket.business.service.MessageSenderService;
import com.red.circle.mq.rocket.business.streams.TeamSalaryCountSink;
import com.red.circle.other.app.inner.service.team.TeamProfileClientService;
import com.red.circle.mq.rocket.business.streams.TeamSalaryCountSink;
import com.red.circle.other.app.inner.service.team.TeamManualSalaryPaymentClientService;
import com.red.circle.other.app.inner.service.team.TeamProfileClientService;
import com.red.circle.other.domain.gateway.user.UserProfileGateway;
import com.red.circle.other.domain.gateway.user.ability.RegisterDeviceGateway;
import com.red.circle.other.domain.model.user.UserProfile;
@ -52,19 +53,23 @@ import com.red.circle.other.inner.model.cmd.team.TeamApplicationProcessApprovalQ
import com.red.circle.other.inner.model.cmd.team.TeamApplicationProcessChangeCmd;
import com.red.circle.other.inner.model.cmd.team.TeamApplicationProcessQryCmd;
import com.red.circle.other.inner.model.cmd.team.TeamBillCmd;
import com.red.circle.other.inner.model.cmd.team.TeamChangeOwnerCmd;
import com.red.circle.other.inner.model.cmd.team.TeamCreateCmd;
import com.red.circle.other.inner.model.cmd.team.TeamProfileCmd;
import com.red.circle.other.inner.model.cmd.team.TeamChangeOwnerCmd;
import com.red.circle.other.inner.model.cmd.team.TeamCreateCmd;
import com.red.circle.other.inner.model.cmd.team.TeamManualSalaryPaymentCmd;
import com.red.circle.other.inner.model.cmd.team.TeamManualSalaryPaymentQryCmd;
import com.red.circle.other.inner.model.cmd.team.TeamProfileCmd;
import com.red.circle.other.inner.model.cmd.team.TeamSalaryBackQryCmd;
import com.red.circle.other.inner.model.cmd.team.TeamTableQryCmd;
import com.red.circle.other.inner.model.cmd.team.TeamUpdateCmd;
import com.red.circle.other.inner.model.dto.agency.agency.TeamApplicationProcessApprovalDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamApplicationProcessDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamContact;
import com.red.circle.other.inner.model.dto.agency.agency.TeamContactDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamCounter;
import com.red.circle.other.inner.model.dto.agency.agency.TeamCountry;
import com.red.circle.other.inner.model.dto.agency.agency.TeamMemberDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamContactDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamCounter;
import com.red.circle.other.inner.model.dto.agency.agency.TeamCountry;
import com.red.circle.other.inner.model.dto.agency.agency.TeamManualSalaryPaymentDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamManualSalaryPaymentResultDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamMemberDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamPolicyManagerDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamProfileDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamRemark;
@ -122,7 +127,8 @@ public class TeamProfileClientServiceImpl implements TeamProfileClientService {
private final BillDiamondBalanceService billDiamondBalanceService;
private final BillDiamondBalanceDetailsService billDiamondBalanceDetailsService;
private final RegisterDeviceGateway registerDeviceGateway;
private final UserMqMessageService userMqMessageService;
private final UserMqMessageService userMqMessageService;
private final TeamManualSalaryPaymentClientService teamManualSalaryPaymentClientService;
@Override
public List<TeamProfileDTO> listByIds(Set<Long> ids) {
@ -696,14 +702,26 @@ public class TeamProfileClientServiceImpl implements TeamProfileClientService {
@Override
public PageResult<TeamSalaryPaymentDTO> listByCondition(TeamSalaryBackQryCmd cmd) {
return teamSalaryPaymentService.listByCondition(cmd.getSysOrigin(), cmd.getUserId(),
cmd.getDateNumber(), cmd.getPageQuery().getCursor(), cmd.getPageQuery().getLimit())
.convert(teamInfraConvertor::toTeamSalaryPaymentDTO);
}
@Override
public Set<Long> listIdByRegion(String regionId) {
public PageResult<TeamSalaryPaymentDTO> listByCondition(TeamSalaryBackQryCmd cmd) {
return teamSalaryPaymentService.listByCondition(cmd.getSysOrigin(), cmd.getUserId(),
cmd.getDateNumber(), cmd.getPageQuery().getCursor(), cmd.getPageQuery().getLimit())
.convert(teamInfraConvertor::toTeamSalaryPaymentDTO);
}
@Override
public PageResult<TeamManualSalaryPaymentDTO> manualSalaryPage(
TeamManualSalaryPaymentQryCmd cmd) {
return teamManualSalaryPaymentClientService.page(cmd);
}
@Override
public List<TeamManualSalaryPaymentResultDTO> manualSalaryPay(
TeamManualSalaryPaymentCmd cmd) {
return teamManualSalaryPaymentClientService.pay(cmd);
}
@Override
public Set<Long> listIdByRegion(String regionId) {
return teamProfileService.listIdByRegion(regionId);
}