新增admin工资结算逻辑
This commit is contained in:
parent
eb5a5193ed
commit
0a21af783a
@ -0,0 +1,89 @@
|
||||
package com.red.circle.other.app.scheduler;
|
||||
|
||||
import com.red.circle.component.redis.annotation.TaskCacheLock;
|
||||
import com.red.circle.other.app.service.AdminSalarySettlementService;
|
||||
import com.red.circle.other.infra.database.mongo.service.team.TeamBillCycleUtils;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
/**
|
||||
* 管理员工资结算定时任务.
|
||||
* 每月1号和16号 2点执行半月结算(在BD和BD Leader结算之后)
|
||||
*
|
||||
* @author AI Assistant
|
||||
* @since 2025-12-13
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class AdminSalarySettlementTask {
|
||||
|
||||
private final AdminSalarySettlementService adminSalarySettlementService;
|
||||
|
||||
/**
|
||||
* 每月1号和16号 2点执行半月结算.
|
||||
*/
|
||||
@Scheduled(cron = "0 0 2 1,16 * ?", zone = "Asia/Riyadh")
|
||||
@TaskCacheLock(key = "ADMIN_SALARY_SETTLEMENT", expireSecond = 86400)
|
||||
public void processAdminSalarySettlement() {
|
||||
long startTime = System.currentTimeMillis();
|
||||
log.warn("========== 管理员工资结算定时任务开始 ==========");
|
||||
|
||||
try {
|
||||
// 获取当前日期的账期
|
||||
Integer currentBillBelong = TeamBillCycleUtils.getCalcBillBelong();
|
||||
|
||||
// 计算需要结算的账期(往前推一个账期)
|
||||
Integer billBelong = calculateSettlementBillBelong(currentBillBelong);
|
||||
String billTitle = TeamBillCycleUtils.parseBillBelongToDateRangeStr(billBelong);
|
||||
|
||||
log.warn("结算周期:{} - {}", billBelong, billTitle);
|
||||
|
||||
// 执行管理员工资结算
|
||||
adminSalarySettlementService.processAllAdminSettlement(billBelong, billTitle);
|
||||
|
||||
log.warn("========== 管理员工资结算定时任务完成,耗时:{} ms ==========",
|
||||
System.currentTimeMillis() - startTime);
|
||||
} catch (Exception e) {
|
||||
log.error("========== 管理员工资结算定时任务异常 ==========", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算需要结算的账期.
|
||||
* 规则:
|
||||
* - 如果当前账期是本月1号(如20250101),则结算上个月16号的账期(如20241216)
|
||||
* - 如果当前账期是本月16号(如20250116),则结算本月1号的账期(如20250101)
|
||||
*/
|
||||
private Integer calculateSettlementBillBelong(Integer currentBillBelong) {
|
||||
String billStr = String.valueOf(currentBillBelong);
|
||||
int year = Integer.parseInt(billStr.substring(0, 4));
|
||||
int month = Integer.parseInt(billStr.substring(4, 6));
|
||||
int day = Integer.parseInt(billStr.substring(6, 8));
|
||||
|
||||
LocalDate settlementDate;
|
||||
|
||||
if (day == 1) {
|
||||
// 当前是本月1号,结算上个月16号
|
||||
settlementDate = LocalDate.of(year, month, 1).minusMonths(1).withDayOfMonth(16);
|
||||
} else {
|
||||
// 当前是本月16号,结算本月1号
|
||||
settlementDate = LocalDate.of(year, month, 1);
|
||||
}
|
||||
|
||||
return Integer.parseInt(settlementDate.format(DateTimeFormatter.ofPattern("yyyyMMdd")));
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试方法(手动触发).
|
||||
*/
|
||||
public void processAdminSalarySettlementTest(Integer billBelong) {
|
||||
String billTitle = TeamBillCycleUtils.parseBillBelongToDateRangeStr(billBelong);
|
||||
adminSalarySettlementService.processAllAdminSettlement(billBelong, billTitle);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,408 @@
|
||||
package com.red.circle.other.app.service;
|
||||
|
||||
import com.red.circle.common.business.core.enums.SysOriginPlatformEnum;
|
||||
import com.red.circle.common.business.dto.cmd.AppExtCommand;
|
||||
import com.red.circle.other.app.command.bd.query.BdTeamSummaryQryExe;
|
||||
import com.red.circle.other.app.dto.clientobject.AdminSalarySettlementCO;
|
||||
import com.red.circle.other.app.dto.clientobject.BdTeamSummaryCO;
|
||||
import com.red.circle.other.app.dto.h5.RechargeSummaryVO;
|
||||
import com.red.circle.other.app.service.recharge.RechargeUserService;
|
||||
import com.red.circle.other.infra.database.mongo.entity.bd.AdminSalaryPolicy;
|
||||
import com.red.circle.other.infra.database.mongo.entity.bd.AdminSalarySettlementRecord;
|
||||
import com.red.circle.other.infra.database.mongo.entity.bd.BdSalarySettlementRecord;
|
||||
import com.red.circle.other.infra.database.mongo.service.bd.AdminSalaryPolicyService;
|
||||
import com.red.circle.other.infra.database.mongo.service.bd.AdminSalarySettlementRecordService;
|
||||
import com.red.circle.other.infra.database.mongo.service.bd.BdSalarySettlementRecordService;
|
||||
import com.red.circle.other.infra.enums.AdminPolicyType;
|
||||
import com.red.circle.other.infra.enums.BdSettlementStatus;
|
||||
import com.red.circle.tool.core.collection.CollectionUtils;
|
||||
import com.red.circle.tool.core.sequence.IdWorkerUtils;
|
||||
import com.red.circle.wallet.inner.endpoint.salary.UserSalaryAccountClient;
|
||||
import com.red.circle.wallet.inner.model.cmd.SalaryIssueInnerCmd;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 管理员工资结算服务实现.
|
||||
*
|
||||
* @author AI Assistant
|
||||
* @since 2025-12-13
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class AdminSalarySettlementServiceImpl implements AdminSalarySettlementService {
|
||||
|
||||
private final AdminSalaryPolicyService adminSalaryPolicyService;
|
||||
private final AdminSalarySettlementRecordService adminSalarySettlementRecordService;
|
||||
private final BdSalarySettlementRecordService bdSalarySettlementRecordService;
|
||||
private final BdTeamSummaryQryExe bdTeamSummaryQryExe;
|
||||
private final RechargeUserService rechargeUserService;
|
||||
private final UserSalaryAccountClient userSalaryAccountClient;
|
||||
|
||||
@Override
|
||||
public void processAllAdminSettlement(Integer billBelong, String billTitle) {
|
||||
log.warn("开始处理所有管理员工资结算,结算周期:{}", billBelong);
|
||||
|
||||
// TODO: 查询所有管理员列表
|
||||
List<Long> adminUserIds = List.of(); // 需要从数据库查询所有管理员ID
|
||||
|
||||
if (CollectionUtils.isEmpty(adminUserIds)) {
|
||||
log.warn("没有找到任何管理员数据");
|
||||
return;
|
||||
}
|
||||
|
||||
log.warn("共找到 {} 个管理员", adminUserIds.size());
|
||||
|
||||
int successCount = 0;
|
||||
int failCount = 0;
|
||||
|
||||
for (Long adminUserId : adminUserIds) {
|
||||
try {
|
||||
processSingleAdminSettlement(adminUserId, billBelong, billTitle);
|
||||
successCount++;
|
||||
} catch (Exception e) {
|
||||
log.error("管理员 {} 工资结算失败", adminUserId, e);
|
||||
failCount++;
|
||||
}
|
||||
}
|
||||
|
||||
log.warn("管理员工资结算完成,成功:{},失败:{}", successCount, failCount);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public AdminSalarySettlementCO processSingleAdminSettlement(Long adminUserId, Integer billBelong, String billTitle) {
|
||||
log.info("开始处理管理员 {} 的工资结算", adminUserId);
|
||||
|
||||
// 1. 生成幂等业务号
|
||||
String bizNo = generateBizNo(adminUserId, billBelong);
|
||||
|
||||
// 2. 幂等性检查
|
||||
if (adminSalarySettlementRecordService.existsByBizNo(bizNo)) {
|
||||
log.warn("管理员 {} 在周期 {} 已经结算过,跳过", adminUserId, billBelong);
|
||||
AdminSalarySettlementRecord existingRecord = adminSalarySettlementRecordService.getByBizNo(bizNo);
|
||||
return convertToCO(existingRecord);
|
||||
}
|
||||
|
||||
// 3. 初始化结算记录
|
||||
AdminSalarySettlementRecord record = new AdminSalarySettlementRecord();
|
||||
record.setId(IdWorkerUtils.getIdStr());
|
||||
record.setTimeId(IdWorkerUtils.getId());
|
||||
record.setSysOrigin(SysOriginPlatformEnum.LIKEI.name());
|
||||
record.setAdminUserId(adminUserId);
|
||||
record.setBillBelong(billBelong);
|
||||
record.setBillTitle(billTitle);
|
||||
record.setBizNo(bizNo);
|
||||
record.setStatus(BdSettlementStatus.PENDING);
|
||||
|
||||
// 4. 计算各个政策的工资
|
||||
try {
|
||||
// 4.1 RECHARGE_AGENT 政策
|
||||
calculateRechargeAgentPolicy(adminUserId, record);
|
||||
|
||||
// 4.2 BD_LEADER 政策
|
||||
calculateBdLeaderPolicy(adminUserId, billBelong, record);
|
||||
|
||||
// 4.3 BD_TEAM_SALARY 和 BD_TEAM_RECHARGE 政策(取最高值)
|
||||
calculateBdTeamPolicies(adminUserId, billBelong, record);
|
||||
|
||||
// 4.4 计算总工资
|
||||
BigDecimal totalSalary = BigDecimal.ZERO;
|
||||
if (record.getRechargeAgentSalary() != null) {
|
||||
totalSalary = totalSalary.add(record.getRechargeAgentSalary());
|
||||
}
|
||||
if (record.getBdLeaderSalary() != null) {
|
||||
totalSalary = totalSalary.add(record.getBdLeaderSalary());
|
||||
}
|
||||
if (record.getBdPolicyFinalSalary() != null) {
|
||||
totalSalary = totalSalary.add(record.getBdPolicyFinalSalary());
|
||||
}
|
||||
record.setTotalSalary(totalSalary.setScale(2, RoundingMode.DOWN));
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("管理员 {} 工资计算失败", adminUserId, e);
|
||||
record.setStatus(BdSettlementStatus.FAILED);
|
||||
record.setFailureReason(e.getMessage());
|
||||
adminSalarySettlementRecordService.create(record);
|
||||
throw e;
|
||||
}
|
||||
|
||||
// 5. 保存结算记录
|
||||
adminSalarySettlementRecordService.create(record);
|
||||
|
||||
// 6. 如果工资大于 0,则发放工资
|
||||
if (record.getTotalSalary().compareTo(BigDecimal.ZERO) > 0) {
|
||||
try {
|
||||
issueSalary(adminUserId, record);
|
||||
log.info("管理员 {} 工资发放成功,金额:{} USD", adminUserId, record.getTotalSalary());
|
||||
} catch (Exception e) {
|
||||
log.error("管理员 {} 工资发放失败", adminUserId, e);
|
||||
adminSalarySettlementRecordService.updateStatus(record.getId(), BdSettlementStatus.FAILED, e.getMessage());
|
||||
record.setStatus(BdSettlementStatus.FAILED);
|
||||
record.setFailureReason(e.getMessage());
|
||||
throw e;
|
||||
}
|
||||
} else {
|
||||
log.info("管理员 {} 本周期未达到结算标准,无工资发放", adminUserId);
|
||||
}
|
||||
|
||||
// 7. 更新状态为成功
|
||||
adminSalarySettlementRecordService.updateStatus(record.getId(), BdSettlementStatus.SUCCESS, null);
|
||||
record.setStatus(BdSettlementStatus.SUCCESS);
|
||||
|
||||
return convertToCO(record);
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算 RECHARGE_AGENT 政策工资.
|
||||
*/
|
||||
private void calculateRechargeAgentPolicy(Long adminUserId, AdminSalarySettlementRecord record) {
|
||||
// 构建命令对象
|
||||
AppExtCommand cmd = new AppExtCommand();
|
||||
cmd.setReqUserId(adminUserId);
|
||||
|
||||
// 获取充值汇总数据
|
||||
RechargeSummaryVO rechargeSummary = rechargeUserService.getRechargeSummary(cmd);
|
||||
|
||||
if (rechargeSummary == null || rechargeSummary.getCurrentMonthRecharge() == null) {
|
||||
record.setRechargeAgentAmount(BigDecimal.ZERO);
|
||||
record.setRechargeAgentSalary(BigDecimal.ZERO);
|
||||
return;
|
||||
}
|
||||
|
||||
BigDecimal rechargeAmount = rechargeSummary.getCurrentMonthRecharge();
|
||||
record.setRechargeAgentAmount(rechargeAmount.setScale(2, RoundingMode.DOWN));
|
||||
|
||||
// 查询 RECHARGE_AGENT 政策
|
||||
List<AdminSalaryPolicy> policies = adminSalaryPolicyService.listEnabledByType(
|
||||
AdminPolicyType.RECHARGE_AGENT.getCode());
|
||||
|
||||
if (CollectionUtils.isEmpty(policies)) {
|
||||
record.setRechargeAgentSalary(BigDecimal.ZERO);
|
||||
return;
|
||||
}
|
||||
|
||||
// 按充值金额倒序匹配
|
||||
policies.sort((p1, p2) -> p2.getRequiredCoinSellerRecharge().compareTo(p1.getRequiredCoinSellerRecharge()));
|
||||
|
||||
for (AdminSalaryPolicy policy : policies) {
|
||||
if (rechargeAmount.compareTo(policy.getRequiredCoinSellerRecharge()) >= 0) {
|
||||
// 计算工资:充值金额 × 百分比
|
||||
BigDecimal salary = rechargeAmount.multiply(policy.getAdminPercentage())
|
||||
.divide(new BigDecimal("100"), 2, RoundingMode.DOWN);
|
||||
record.setRechargeAgentSalary(salary);
|
||||
record.setRechargeAgentPercentage(policy.getAdminPercentage());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
record.setRechargeAgentSalary(BigDecimal.ZERO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算 BD_LEADER 政策工资.
|
||||
*/
|
||||
private void calculateBdLeaderPolicy(Long adminUserId, Integer billBelong, AdminSalarySettlementRecord record) {
|
||||
// 查询 BD Leader 团队数据
|
||||
BdTeamSummaryCO teamSummary = bdTeamSummaryQryExe.execute(adminUserId);
|
||||
|
||||
if (teamSummary == null || teamSummary.getBdLeaderTeams() == null) {
|
||||
record.setBdLeaderTeamSalary(BigDecimal.ZERO);
|
||||
record.setBdLeaderSalary(BigDecimal.ZERO);
|
||||
return;
|
||||
}
|
||||
|
||||
BigDecimal teamSalary = teamSummary.getBdLeaderTeams().getTotalIncome();
|
||||
if (teamSalary == null) {
|
||||
teamSalary = BigDecimal.ZERO;
|
||||
}
|
||||
record.setBdLeaderTeamSalary(teamSalary.setScale(2, RoundingMode.DOWN));
|
||||
|
||||
// 查询 BD_LEADER 政策
|
||||
List<AdminSalaryPolicy> policies = adminSalaryPolicyService.listEnabledByType(
|
||||
AdminPolicyType.BD_LEADER.getCode());
|
||||
|
||||
if (CollectionUtils.isEmpty(policies)) {
|
||||
record.setBdLeaderSalary(BigDecimal.ZERO);
|
||||
return;
|
||||
}
|
||||
|
||||
// 按等级倒序匹配
|
||||
policies.sort((p1, p2) -> p2.getLevel().compareTo(p1.getLevel()));
|
||||
|
||||
for (AdminSalaryPolicy policy : policies) {
|
||||
if (teamSalary.compareTo(policy.getRequiredTeamSalary()) >= 0) {
|
||||
// 工资 = Basic Salary + Reward Amount
|
||||
BigDecimal salary = policy.getBasicSalary();
|
||||
if (policy.getRewardAmount() != null) {
|
||||
salary = salary.add(policy.getRewardAmount());
|
||||
}
|
||||
record.setBdLeaderSalary(salary.setScale(2, RoundingMode.DOWN));
|
||||
record.setBdLeaderHitLevel(policy.getLevel());
|
||||
record.setBdLeaderBasicSalary(policy.getBasicSalary());
|
||||
record.setBdLeaderRewardAmount(policy.getRewardAmount());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
record.setBdLeaderSalary(BigDecimal.ZERO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算 BD_TEAM_SALARY 和 BD_TEAM_RECHARGE 政策工资(取最高值).
|
||||
*/
|
||||
private void calculateBdTeamPolicies(Long adminUserId, Integer billBelong, AdminSalarySettlementRecord record) {
|
||||
// 从 BD 结算记录查询数据
|
||||
BdSalarySettlementRecord bdRecord = bdSalarySettlementRecordService.getByUserIdAndBillBelong(
|
||||
adminUserId, billBelong);
|
||||
|
||||
if (bdRecord == null) {
|
||||
record.setBdTeamSalaryAmount(BigDecimal.ZERO);
|
||||
record.setBdTeamSalarySalary(BigDecimal.ZERO);
|
||||
record.setBdTeamRechargeAmount(BigDecimal.ZERO);
|
||||
record.setBdTeamRechargeSalary(BigDecimal.ZERO);
|
||||
record.setBdPolicyFinalSalary(BigDecimal.ZERO);
|
||||
return;
|
||||
}
|
||||
|
||||
// 计算 BD_TEAM_SALARY 政策
|
||||
BigDecimal teamSalaryAmount = bdRecord.getTeamSalaryAmount() != null ?
|
||||
bdRecord.getTeamSalaryAmount() : BigDecimal.ZERO;
|
||||
record.setBdTeamSalaryAmount(teamSalaryAmount.setScale(2, RoundingMode.DOWN));
|
||||
|
||||
BigDecimal teamSalarySalary = calculateBdTeamSalaryPolicy(teamSalaryAmount, record);
|
||||
|
||||
// 计算 BD_TEAM_RECHARGE 政策
|
||||
BigDecimal teamRechargeAmount = bdRecord.getTeamRechargeAmount() != null ?
|
||||
bdRecord.getTeamRechargeAmount() : BigDecimal.ZERO;
|
||||
record.setBdTeamRechargeAmount(teamRechargeAmount.setScale(2, RoundingMode.DOWN));
|
||||
|
||||
BigDecimal teamRechargeSalary = calculateBdTeamRechargePolicy(teamRechargeAmount, record);
|
||||
|
||||
// 取最高值
|
||||
if (teamSalarySalary.compareTo(teamRechargeSalary) >= 0) {
|
||||
record.setBdPolicyFinalSalary(teamSalarySalary);
|
||||
record.setBdPolicyHitType(AdminPolicyType.BD_TEAM_SALARY.getCode());
|
||||
} else {
|
||||
record.setBdPolicyFinalSalary(teamRechargeSalary);
|
||||
record.setBdPolicyHitType(AdminPolicyType.BD_TEAM_RECHARGE.getCode());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算 BD_TEAM_SALARY 政策.
|
||||
*/
|
||||
private BigDecimal calculateBdTeamSalaryPolicy(BigDecimal teamSalaryAmount, AdminSalarySettlementRecord record) {
|
||||
List<AdminSalaryPolicy> policies = adminSalaryPolicyService.listEnabledByType(
|
||||
AdminPolicyType.BD_TEAM_SALARY.getCode());
|
||||
|
||||
if (CollectionUtils.isEmpty(policies)) {
|
||||
record.setBdTeamSalarySalary(BigDecimal.ZERO);
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
|
||||
// 按等级倒序匹配
|
||||
policies.sort((p1, p2) -> p2.getLevel().compareTo(p1.getLevel()));
|
||||
|
||||
for (AdminSalaryPolicy policy : policies) {
|
||||
if (teamSalaryAmount.compareTo(policy.getRequiredTeamAmount()) >= 0) {
|
||||
// 工资 = 团队工资 × 提成比例
|
||||
BigDecimal salary = teamSalaryAmount.multiply(policy.getCommissionRate())
|
||||
.divide(new BigDecimal("100"), 2, RoundingMode.DOWN);
|
||||
record.setBdTeamSalarySalary(salary);
|
||||
record.setBdTeamSalaryHitLevel(policy.getLevel());
|
||||
record.setBdTeamSalaryCommissionRate(policy.getCommissionRate());
|
||||
return salary;
|
||||
}
|
||||
}
|
||||
|
||||
record.setBdTeamSalarySalary(BigDecimal.ZERO);
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算 BD_TEAM_RECHARGE 政策.
|
||||
*/
|
||||
private BigDecimal calculateBdTeamRechargePolicy(BigDecimal teamRechargeAmount, AdminSalarySettlementRecord record) {
|
||||
List<AdminSalaryPolicy> policies = adminSalaryPolicyService.listEnabledByType(
|
||||
AdminPolicyType.BD_TEAM_RECHARGE.getCode());
|
||||
|
||||
if (CollectionUtils.isEmpty(policies)) {
|
||||
record.setBdTeamRechargeSalary(BigDecimal.ZERO);
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
|
||||
// 按等级倒序匹配
|
||||
policies.sort((p1, p2) -> p2.getLevel().compareTo(p1.getLevel()));
|
||||
|
||||
for (AdminSalaryPolicy policy : policies) {
|
||||
if (teamRechargeAmount.compareTo(policy.getRequiredTeamAmount()) >= 0) {
|
||||
// 工资 = 团队充值 × 提成比例
|
||||
BigDecimal salary = teamRechargeAmount.multiply(policy.getCommissionRate())
|
||||
.divide(new BigDecimal("100"), 2, RoundingMode.DOWN);
|
||||
record.setBdTeamRechargeSalary(salary);
|
||||
record.setBdTeamRechargeHitLevel(policy.getLevel());
|
||||
record.setBdTeamRechargeCommissionRate(policy.getCommissionRate());
|
||||
return salary;
|
||||
}
|
||||
}
|
||||
|
||||
record.setBdTeamRechargeSalary(BigDecimal.ZERO);
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
|
||||
/**
|
||||
* 发放工资到工资账户.
|
||||
*/
|
||||
private void issueSalary(Long adminUserId, AdminSalarySettlementRecord record) {
|
||||
SalaryIssueInnerCmd innerCmd = new SalaryIssueInnerCmd();
|
||||
innerCmd.setBillBelong(record.getBillBelong());
|
||||
innerCmd.setUserId(adminUserId);
|
||||
innerCmd.setSysOrigin(record.getSysOrigin());
|
||||
innerCmd.setSalaryType("ADMIN_SALARY");
|
||||
innerCmd.setEventDesc("SALARY_ADMIN");
|
||||
innerCmd.setAmount(record.getTotalSalary());
|
||||
innerCmd.setTrackId(record.getId());
|
||||
innerCmd.setBdId(adminUserId);
|
||||
userSalaryAccountClient.issueSalary(innerCmd);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成业务号(幂等性保证).
|
||||
*/
|
||||
private String generateBizNo(Long adminUserId, Integer billBelong) {
|
||||
return String.format("ADMIN_SALARY_%d_%d", adminUserId, billBelong);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为 CO 对象.
|
||||
*/
|
||||
private AdminSalarySettlementCO convertToCO(AdminSalarySettlementRecord record) {
|
||||
AdminSalarySettlementCO co = new AdminSalarySettlementCO();
|
||||
co.setAdminUserId(record.getAdminUserId());
|
||||
co.setBillBelong(record.getBillBelong());
|
||||
co.setBillTitle(record.getBillTitle());
|
||||
co.setRechargeAgentAmount(record.getRechargeAgentAmount());
|
||||
co.setRechargeAgentSalary(record.getRechargeAgentSalary());
|
||||
co.setBdLeaderTeamSalary(record.getBdLeaderTeamSalary());
|
||||
co.setBdLeaderSalary(record.getBdLeaderSalary());
|
||||
co.setBdTeamSalaryAmount(record.getBdTeamSalaryAmount());
|
||||
co.setBdTeamSalarySalary(record.getBdTeamSalarySalary());
|
||||
co.setBdTeamRechargeAmount(record.getBdTeamRechargeAmount());
|
||||
co.setBdTeamRechargeSalary(record.getBdTeamRechargeSalary());
|
||||
co.setBdPolicyFinalSalary(record.getBdPolicyFinalSalary());
|
||||
co.setBdPolicyHitType(record.getBdPolicyHitType());
|
||||
co.setTotalSalary(record.getTotalSalary());
|
||||
co.setStatus(record.getStatus() != null ? record.getStatus().name() : null);
|
||||
co.setFailureReason(record.getFailureReason());
|
||||
return co;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,95 @@
|
||||
package com.red.circle.other.app.dto.clientobject;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 管理员工资结算结果.
|
||||
*
|
||||
* @author AI Assistant
|
||||
* @since 2025-12-13
|
||||
*/
|
||||
@Data
|
||||
public class AdminSalarySettlementCO {
|
||||
|
||||
/**
|
||||
* 管理员用户 ID.
|
||||
*/
|
||||
private Long adminUserId;
|
||||
|
||||
/**
|
||||
* 账期.
|
||||
*/
|
||||
private Integer billBelong;
|
||||
|
||||
/**
|
||||
* 账期标题.
|
||||
*/
|
||||
private String billTitle;
|
||||
|
||||
/**
|
||||
* 充值代理充值金额.
|
||||
*/
|
||||
private BigDecimal rechargeAgentAmount;
|
||||
|
||||
/**
|
||||
* RECHARGE_AGENT 政策工资.
|
||||
*/
|
||||
private BigDecimal rechargeAgentSalary;
|
||||
|
||||
/**
|
||||
* BD Leader 团队工资总和.
|
||||
*/
|
||||
private BigDecimal bdLeaderTeamSalary;
|
||||
|
||||
/**
|
||||
* BD_LEADER 政策工资.
|
||||
*/
|
||||
private BigDecimal bdLeaderSalary;
|
||||
|
||||
/**
|
||||
* BD 团队工资总和.
|
||||
*/
|
||||
private BigDecimal bdTeamSalaryAmount;
|
||||
|
||||
/**
|
||||
* BD_TEAM_SALARY 政策工资.
|
||||
*/
|
||||
private BigDecimal bdTeamSalarySalary;
|
||||
|
||||
/**
|
||||
* BD 团队充值总和.
|
||||
*/
|
||||
private BigDecimal bdTeamRechargeAmount;
|
||||
|
||||
/**
|
||||
* BD_TEAM_RECHARGE 政策工资.
|
||||
*/
|
||||
private BigDecimal bdTeamRechargeSalary;
|
||||
|
||||
/**
|
||||
* BD 政策最终工资.
|
||||
*/
|
||||
private BigDecimal bdPolicyFinalSalary;
|
||||
|
||||
/**
|
||||
* 命中的 BD 政策类型.
|
||||
*/
|
||||
private String bdPolicyHitType;
|
||||
|
||||
/**
|
||||
* 管理员总工资.
|
||||
*/
|
||||
private BigDecimal totalSalary;
|
||||
|
||||
/**
|
||||
* 结算状态.
|
||||
*/
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 失败原因.
|
||||
*/
|
||||
private String failureReason;
|
||||
}
|
||||
@ -0,0 +1,30 @@
|
||||
package com.red.circle.other.app.service;
|
||||
|
||||
import com.red.circle.other.app.dto.clientobject.AdminSalarySettlementCO;
|
||||
|
||||
/**
|
||||
* 管理员工资结算服务.
|
||||
*
|
||||
* @author AI Assistant
|
||||
* @since 2025-12-13
|
||||
*/
|
||||
public interface AdminSalarySettlementService {
|
||||
|
||||
/**
|
||||
* 处理所有管理员的工资结算.
|
||||
*
|
||||
* @param billBelong 结算周期
|
||||
* @param billTitle 结算周期标题
|
||||
*/
|
||||
void processAllAdminSettlement(Integer billBelong, String billTitle);
|
||||
|
||||
/**
|
||||
* 处理单个管理员的工资结算.
|
||||
*
|
||||
* @param adminUserId 管理员用户 ID
|
||||
* @param billBelong 结算周期
|
||||
* @param billTitle 结算周期标题
|
||||
* @return 结算结果
|
||||
*/
|
||||
AdminSalarySettlementCO processSingleAdminSettlement(Long adminUserId, Integer billBelong, String billTitle);
|
||||
}
|
||||
@ -0,0 +1,192 @@
|
||||
package com.red.circle.other.infra.database.mongo.entity.bd;
|
||||
|
||||
import com.red.circle.other.infra.enums.BdSettlementStatus;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.mongodb.core.index.Indexed;
|
||||
import org.springframework.data.mongodb.core.mapping.Document;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
import java.sql.Timestamp;
|
||||
|
||||
/**
|
||||
* 管理员工资结算记录.
|
||||
*
|
||||
* @author AI Assistant
|
||||
* @since 2025-12-13
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@Document("admin_salary_settlement_record")
|
||||
public class AdminSalarySettlementRecord implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* ID.
|
||||
*/
|
||||
@Id
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* 时间ID(用于排序).
|
||||
*/
|
||||
private Long timeId;
|
||||
|
||||
/**
|
||||
* 系统来源.
|
||||
*/
|
||||
private String sysOrigin;
|
||||
|
||||
/**
|
||||
* 管理员用户 ID.
|
||||
*/
|
||||
@Indexed
|
||||
private Long adminUserId;
|
||||
|
||||
/**
|
||||
* 账期(yyyyMMdd 格式,如 20250101).
|
||||
*/
|
||||
@Indexed
|
||||
private Integer billBelong;
|
||||
|
||||
/**
|
||||
* 账期标题(如 "2025-01-01 ~ 2025-01-15").
|
||||
*/
|
||||
private String billTitle;
|
||||
|
||||
/**
|
||||
* 业务号(幂等性保证).
|
||||
*/
|
||||
@Indexed(unique = true)
|
||||
private String bizNo;
|
||||
|
||||
// ==================== RECHARGE_AGENT 政策数据 ====================
|
||||
|
||||
/**
|
||||
* 充值代理充值金额(美元).
|
||||
*/
|
||||
private BigDecimal rechargeAgentAmount;
|
||||
|
||||
/**
|
||||
* RECHARGE_AGENT 政策计算工资.
|
||||
*/
|
||||
private BigDecimal rechargeAgentSalary;
|
||||
|
||||
/**
|
||||
* RECHARGE_AGENT 命中的百分比.
|
||||
*/
|
||||
private BigDecimal rechargeAgentPercentage;
|
||||
|
||||
// ==================== BD_LEADER 政策数据 ====================
|
||||
|
||||
/**
|
||||
* BD Leader 团队工资总和(美元).
|
||||
*/
|
||||
private BigDecimal bdLeaderTeamSalary;
|
||||
|
||||
/**
|
||||
* BD Leader 政策计算工资.
|
||||
*/
|
||||
private BigDecimal bdLeaderSalary;
|
||||
|
||||
/**
|
||||
* BD Leader 命中的等级.
|
||||
*/
|
||||
private Integer bdLeaderHitLevel;
|
||||
|
||||
/**
|
||||
* BD Leader Basic Salary.
|
||||
*/
|
||||
private BigDecimal bdLeaderBasicSalary;
|
||||
|
||||
/**
|
||||
* BD Leader Reward Amount.
|
||||
*/
|
||||
private BigDecimal bdLeaderRewardAmount;
|
||||
|
||||
// ==================== BD_TEAM_SALARY 政策数据 ====================
|
||||
|
||||
/**
|
||||
* BD 团队工资总和(美元).
|
||||
*/
|
||||
private BigDecimal bdTeamSalaryAmount;
|
||||
|
||||
/**
|
||||
* BD_TEAM_SALARY 政策计算工资.
|
||||
*/
|
||||
private BigDecimal bdTeamSalarySalary;
|
||||
|
||||
/**
|
||||
* BD_TEAM_SALARY 命中的等级.
|
||||
*/
|
||||
private Integer bdTeamSalaryHitLevel;
|
||||
|
||||
/**
|
||||
* BD_TEAM_SALARY 提成比例.
|
||||
*/
|
||||
private BigDecimal bdTeamSalaryCommissionRate;
|
||||
|
||||
// ==================== BD_TEAM_RECHARGE 政策数据 ====================
|
||||
|
||||
/**
|
||||
* BD 团队充值总和(美元).
|
||||
*/
|
||||
private BigDecimal bdTeamRechargeAmount;
|
||||
|
||||
/**
|
||||
* BD_TEAM_RECHARGE 政策计算工资.
|
||||
*/
|
||||
private BigDecimal bdTeamRechargeSalary;
|
||||
|
||||
/**
|
||||
* BD_TEAM_RECHARGE 命中的等级.
|
||||
*/
|
||||
private Integer bdTeamRechargeHitLevel;
|
||||
|
||||
/**
|
||||
* BD_TEAM_RECHARGE 提成比例.
|
||||
*/
|
||||
private BigDecimal bdTeamRechargeCommissionRate;
|
||||
|
||||
// ==================== 最终结算结果 ====================
|
||||
|
||||
/**
|
||||
* BD 政策最终工资(max(BD_TEAM_SALARY, BD_TEAM_RECHARGE)).
|
||||
*/
|
||||
private BigDecimal bdPolicyFinalSalary;
|
||||
|
||||
/**
|
||||
* 命中的 BD 政策类型.
|
||||
*/
|
||||
private String bdPolicyHitType;
|
||||
|
||||
/**
|
||||
* 管理员总工资.
|
||||
*/
|
||||
private BigDecimal totalSalary;
|
||||
|
||||
/**
|
||||
* 结算状态.
|
||||
*/
|
||||
private BdSettlementStatus status;
|
||||
|
||||
/**
|
||||
* 失败原因.
|
||||
*/
|
||||
private String failureReason;
|
||||
|
||||
/**
|
||||
* 创建时间.
|
||||
*/
|
||||
private Timestamp createTime;
|
||||
|
||||
/**
|
||||
* 更新时间.
|
||||
*/
|
||||
private Timestamp updateTime;
|
||||
}
|
||||
@ -0,0 +1,29 @@
|
||||
package com.red.circle.other.infra.database.mongo.service.bd;
|
||||
|
||||
import com.red.circle.other.infra.database.mongo.entity.bd.AdminSalaryPolicy;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 管理员工资政策服务.
|
||||
*
|
||||
* @author AI Assistant
|
||||
* @since 2025-12-13
|
||||
*/
|
||||
public interface AdminSalaryPolicyService {
|
||||
|
||||
/**
|
||||
* 根据政策类型查询启用的政策列表.
|
||||
*/
|
||||
List<AdminSalaryPolicy> listEnabledByType(String policyType);
|
||||
|
||||
/**
|
||||
* 创建政策.
|
||||
*/
|
||||
void create(AdminSalaryPolicy policy);
|
||||
|
||||
/**
|
||||
* 批量创建政策.
|
||||
*/
|
||||
void batchCreate(List<AdminSalaryPolicy> policies);
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
package com.red.circle.other.infra.database.mongo.service.bd;
|
||||
|
||||
import com.red.circle.other.infra.database.mongo.entity.bd.AdminSalarySettlementRecord;
|
||||
import com.red.circle.other.infra.enums.BdSettlementStatus;
|
||||
|
||||
/**
|
||||
* 管理员工资结算记录服务.
|
||||
*
|
||||
* @author AI Assistant
|
||||
* @since 2025-12-13
|
||||
*/
|
||||
public interface AdminSalarySettlementRecordService {
|
||||
|
||||
/**
|
||||
* 创建结算记录.
|
||||
*/
|
||||
void create(AdminSalarySettlementRecord record);
|
||||
|
||||
/**
|
||||
* 根据业务号查询.
|
||||
*/
|
||||
AdminSalarySettlementRecord getByBizNo(String bizNo);
|
||||
|
||||
/**
|
||||
* 根据业务号判断是否存在.
|
||||
*/
|
||||
boolean existsByBizNo(String bizNo);
|
||||
|
||||
/**
|
||||
* 根据用户ID和账期查询.
|
||||
*/
|
||||
AdminSalarySettlementRecord getByUserIdAndBillBelong(Long adminUserId, Integer billBelong);
|
||||
|
||||
/**
|
||||
* 更新状态.
|
||||
*/
|
||||
void updateStatus(String id, BdSettlementStatus status, String failureReason);
|
||||
}
|
||||
@ -0,0 +1,51 @@
|
||||
package com.red.circle.other.infra.database.mongo.service.bd.impl;
|
||||
|
||||
import com.red.circle.other.infra.database.mongo.entity.bd.AdminSalaryPolicy;
|
||||
import com.red.circle.other.infra.database.mongo.service.bd.AdminSalaryPolicyService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.data.mongodb.core.MongoTemplate;
|
||||
import org.springframework.data.mongodb.core.query.Criteria;
|
||||
import org.springframework.data.mongodb.core.query.Query;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 管理员工资政策服务实现.
|
||||
*
|
||||
* @author AI Assistant
|
||||
* @since 2025-12-13
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class AdminSalaryPolicyServiceImpl implements AdminSalaryPolicyService {
|
||||
|
||||
private final MongoTemplate mongoTemplate;
|
||||
|
||||
@Override
|
||||
public List<AdminSalaryPolicy> listEnabledByType(String policyType) {
|
||||
Query query = new Query(Criteria.where("policyType").is(policyType)
|
||||
.and("enabled").is(true));
|
||||
return mongoTemplate.find(query, AdminSalaryPolicy.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void create(AdminSalaryPolicy policy) {
|
||||
policy.setCreateTime(new Timestamp(System.currentTimeMillis()));
|
||||
policy.setUpdateTime(new Timestamp(System.currentTimeMillis()));
|
||||
mongoTemplate.insert(policy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void batchCreate(List<AdminSalaryPolicy> policies) {
|
||||
Timestamp now = new Timestamp(System.currentTimeMillis());
|
||||
policies.forEach(policy -> {
|
||||
policy.setCreateTime(now);
|
||||
policy.setUpdateTime(now);
|
||||
});
|
||||
mongoTemplate.insertAll(policies);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,64 @@
|
||||
package com.red.circle.other.infra.database.mongo.service.bd.impl;
|
||||
|
||||
import com.red.circle.other.infra.database.mongo.entity.bd.AdminSalarySettlementRecord;
|
||||
import com.red.circle.other.infra.database.mongo.service.bd.AdminSalarySettlementRecordService;
|
||||
import com.red.circle.other.infra.enums.BdSettlementStatus;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.data.mongodb.core.MongoTemplate;
|
||||
import org.springframework.data.mongodb.core.query.Criteria;
|
||||
import org.springframework.data.mongodb.core.query.Query;
|
||||
import org.springframework.data.mongodb.core.query.Update;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
|
||||
/**
|
||||
* 管理员工资结算记录服务实现.
|
||||
*
|
||||
* @author AI Assistant
|
||||
* @since 2025-12-13
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class AdminSalarySettlementRecordServiceImpl implements AdminSalarySettlementRecordService {
|
||||
|
||||
private final MongoTemplate mongoTemplate;
|
||||
|
||||
@Override
|
||||
public void create(AdminSalarySettlementRecord record) {
|
||||
record.setCreateTime(new Timestamp(System.currentTimeMillis()));
|
||||
record.setUpdateTime(new Timestamp(System.currentTimeMillis()));
|
||||
mongoTemplate.insert(record);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AdminSalarySettlementRecord getByBizNo(String bizNo) {
|
||||
Query query = new Query(Criteria.where("bizNo").is(bizNo));
|
||||
return mongoTemplate.findOne(query, AdminSalarySettlementRecord.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean existsByBizNo(String bizNo) {
|
||||
Query query = new Query(Criteria.where("bizNo").is(bizNo));
|
||||
return mongoTemplate.exists(query, AdminSalarySettlementRecord.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AdminSalarySettlementRecord getByUserIdAndBillBelong(Long adminUserId, Integer billBelong) {
|
||||
Query query = new Query(Criteria.where("adminUserId").is(adminUserId)
|
||||
.and("billBelong").is(billBelong));
|
||||
return mongoTemplate.findOne(query, AdminSalarySettlementRecord.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateStatus(String id, BdSettlementStatus status, String failureReason) {
|
||||
Query query = new Query(Criteria.where("id").is(id));
|
||||
Update update = new Update()
|
||||
.set("status", status)
|
||||
.set("failureReason", failureReason)
|
||||
.set("updateTime", new Timestamp(System.currentTimeMillis()));
|
||||
mongoTemplate.updateFirst(query, update, AdminSalarySettlementRecord.class);
|
||||
}
|
||||
}
|
||||
@ -14,6 +14,8 @@ public enum SalaryEvent {
|
||||
|
||||
SALARY_BD_LEADER("SALARY_BD_LEADER", "BD_LEADER工资"),
|
||||
|
||||
SALARY_ADMIN("SALARY_ADMIN", "ADMIN工资"),
|
||||
|
||||
/**
|
||||
* 工资调整.
|
||||
*/
|
||||
|
||||
@ -23,7 +23,7 @@ public enum SalaryType {
|
||||
/**
|
||||
* 管理员工资
|
||||
*/
|
||||
ADMIN_SALARY("ADMIN_SALARY", "ADMIN工资"),
|
||||
ADMIN_SALARY(" ", "ADMIN工资"),
|
||||
|
||||
;
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user