新增admin工资结算逻辑

This commit is contained in:
tianfeng 2025-12-12 15:51:18 +08:00
parent eb5a5193ed
commit 0a21af783a
11 changed files with 999 additions and 1 deletions

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -14,6 +14,8 @@ public enum SalaryEvent {
SALARY_BD_LEADER("SALARY_BD_LEADER", "BD_LEADER工资"),
SALARY_ADMIN("SALARY_ADMIN", "ADMIN工资"),
/**
* 工资调整.
*/

View File

@ -23,7 +23,7 @@ public enum SalaryType {
/**
* 管理员工资
*/
ADMIN_SALARY("ADMIN_SALARY", "ADMIN工资"),
ADMIN_SALARY(" ", "ADMIN工资"),
;