feat: add salary settlement records
This commit is contained in:
parent
b46c2ac951
commit
2dfbc32786
@ -0,0 +1,98 @@
|
||||
-- Webconsole salary settlement records menu.
|
||||
-- Adds the second-level menu under 主播中心 and binds the list API resource.
|
||||
|
||||
INSERT INTO sys_resource (id, resource_name, mapping, method, auth_type, perm, update_time)
|
||||
VALUES (
|
||||
'team:salary:settlement:list',
|
||||
'工资记录列表',
|
||||
'/team/salary/settlement/list',
|
||||
'GET',
|
||||
3,
|
||||
'team:salary:record:list',
|
||||
NOW()
|
||||
)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
resource_name = VALUES(resource_name),
|
||||
mapping = VALUES(mapping),
|
||||
method = VALUES(method),
|
||||
auth_type = VALUES(auth_type),
|
||||
perm = VALUES(perm),
|
||||
update_time = NOW();
|
||||
|
||||
INSERT INTO sys_menu (
|
||||
parent_id,
|
||||
menu_name,
|
||||
path,
|
||||
router,
|
||||
menu_type,
|
||||
icon,
|
||||
alias,
|
||||
sort,
|
||||
status,
|
||||
create_time,
|
||||
update_time
|
||||
)
|
||||
SELECT
|
||||
parent.id,
|
||||
'工资记录',
|
||||
'team/salary-record/index',
|
||||
'team/salary-record',
|
||||
2,
|
||||
'form',
|
||||
'team:salary:record',
|
||||
880,
|
||||
0,
|
||||
NOW(),
|
||||
NOW()
|
||||
FROM sys_menu parent
|
||||
LEFT JOIN sys_menu existing ON existing.alias = 'team:salary:record'
|
||||
WHERE (parent.alias = 'team:manaber' OR parent.router = '/team' OR parent.menu_name = '主播中心')
|
||||
AND parent.menu_type = 1
|
||||
AND existing.id IS NULL
|
||||
LIMIT 1;
|
||||
|
||||
UPDATE sys_menu salary_menu
|
||||
JOIN (
|
||||
SELECT id
|
||||
FROM sys_menu
|
||||
WHERE (alias = 'team:manaber' OR router = '/team' OR menu_name = '主播中心')
|
||||
AND menu_type = 1
|
||||
LIMIT 1
|
||||
) parent ON 1 = 1
|
||||
SET
|
||||
salary_menu.parent_id = parent.id,
|
||||
salary_menu.menu_name = '工资记录',
|
||||
salary_menu.path = 'team/salary-record/index',
|
||||
salary_menu.router = 'team/salary-record',
|
||||
salary_menu.menu_type = 2,
|
||||
salary_menu.icon = 'form',
|
||||
salary_menu.sort = 880,
|
||||
salary_menu.status = 0,
|
||||
salary_menu.update_time = NOW()
|
||||
WHERE salary_menu.alias = 'team:salary:record';
|
||||
|
||||
INSERT INTO sys_menu_resource (menu_id, resource_id)
|
||||
SELECT menu.id, resource.id
|
||||
FROM sys_menu menu
|
||||
JOIN sys_resource resource ON resource.id = 'team:salary:settlement:list'
|
||||
LEFT JOIN sys_menu_resource existing
|
||||
ON existing.menu_id = menu.id
|
||||
AND existing.resource_id = resource.id
|
||||
WHERE menu.alias = 'team:salary:record'
|
||||
AND existing.id IS NULL;
|
||||
|
||||
-- Salary records include admin salary, so auto-grant only to roles that can manage system permissions.
|
||||
INSERT INTO sys_role_menu (role_id, menu_id)
|
||||
SELECT DISTINCT source_role.role_id, salary_menu.id
|
||||
FROM sys_role_menu source_role
|
||||
JOIN sys_menu source_menu ON source_menu.id = source_role.menu_id
|
||||
JOIN sys_menu salary_menu ON salary_menu.alias = 'team:salary:record'
|
||||
LEFT JOIN sys_role_menu existing
|
||||
ON existing.role_id = source_role.role_id
|
||||
AND existing.menu_id = salary_menu.id
|
||||
WHERE source_menu.alias IN (
|
||||
'sys:menu:list',
|
||||
'sys:role:list',
|
||||
'sys:resource:list'
|
||||
)
|
||||
AND existing.id IS NULL;
|
||||
@ -0,0 +1,98 @@
|
||||
-- Webconsole salary settlement records menu.
|
||||
-- Adds the second-level menu under 主播中心 and binds the list API resource.
|
||||
|
||||
INSERT INTO sys_resource (id, resource_name, mapping, method, auth_type, perm, update_time)
|
||||
VALUES (
|
||||
'team:salary:settlement:list',
|
||||
'工资记录列表',
|
||||
'/team/salary/settlement/list',
|
||||
'GET',
|
||||
3,
|
||||
'team:salary:record:list',
|
||||
NOW()
|
||||
)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
resource_name = VALUES(resource_name),
|
||||
mapping = VALUES(mapping),
|
||||
method = VALUES(method),
|
||||
auth_type = VALUES(auth_type),
|
||||
perm = VALUES(perm),
|
||||
update_time = NOW();
|
||||
|
||||
INSERT INTO sys_menu (
|
||||
parent_id,
|
||||
menu_name,
|
||||
path,
|
||||
router,
|
||||
menu_type,
|
||||
icon,
|
||||
alias,
|
||||
sort,
|
||||
status,
|
||||
create_time,
|
||||
update_time
|
||||
)
|
||||
SELECT
|
||||
parent.id,
|
||||
'工资记录',
|
||||
'team/salary-record/index',
|
||||
'team/salary-record',
|
||||
2,
|
||||
'form',
|
||||
'team:salary:record',
|
||||
880,
|
||||
0,
|
||||
NOW(),
|
||||
NOW()
|
||||
FROM sys_menu parent
|
||||
LEFT JOIN sys_menu existing ON existing.alias = 'team:salary:record'
|
||||
WHERE (parent.alias = 'team:manaber' OR parent.router = '/team' OR parent.menu_name = '主播中心')
|
||||
AND parent.menu_type = 1
|
||||
AND existing.id IS NULL
|
||||
LIMIT 1;
|
||||
|
||||
UPDATE sys_menu salary_menu
|
||||
JOIN (
|
||||
SELECT id
|
||||
FROM sys_menu
|
||||
WHERE (alias = 'team:manaber' OR router = '/team' OR menu_name = '主播中心')
|
||||
AND menu_type = 1
|
||||
LIMIT 1
|
||||
) parent ON 1 = 1
|
||||
SET
|
||||
salary_menu.parent_id = parent.id,
|
||||
salary_menu.menu_name = '工资记录',
|
||||
salary_menu.path = 'team/salary-record/index',
|
||||
salary_menu.router = 'team/salary-record',
|
||||
salary_menu.menu_type = 2,
|
||||
salary_menu.icon = 'form',
|
||||
salary_menu.sort = 880,
|
||||
salary_menu.status = 0,
|
||||
salary_menu.update_time = NOW()
|
||||
WHERE salary_menu.alias = 'team:salary:record';
|
||||
|
||||
INSERT INTO sys_menu_resource (menu_id, resource_id)
|
||||
SELECT menu.id, resource.id
|
||||
FROM sys_menu menu
|
||||
JOIN sys_resource resource ON resource.id = 'team:salary:settlement:list'
|
||||
LEFT JOIN sys_menu_resource existing
|
||||
ON existing.menu_id = menu.id
|
||||
AND existing.resource_id = resource.id
|
||||
WHERE menu.alias = 'team:salary:record'
|
||||
AND existing.id IS NULL;
|
||||
|
||||
-- Salary records include admin salary, so auto-grant only to roles that can manage system permissions.
|
||||
INSERT INTO sys_role_menu (role_id, menu_id)
|
||||
SELECT DISTINCT source_role.role_id, salary_menu.id
|
||||
FROM sys_role_menu source_role
|
||||
JOIN sys_menu source_menu ON source_menu.id = source_role.menu_id
|
||||
JOIN sys_menu salary_menu ON salary_menu.alias = 'team:salary:record'
|
||||
LEFT JOIN sys_role_menu existing
|
||||
ON existing.role_id = source_role.role_id
|
||||
AND existing.menu_id = salary_menu.id
|
||||
WHERE source_menu.alias IN (
|
||||
'sys:menu:list',
|
||||
'sys:role:list',
|
||||
'sys:resource:list'
|
||||
)
|
||||
AND existing.id IS NULL;
|
||||
@ -0,0 +1,15 @@
|
||||
package com.red.circle.other.inner.endpoint.team.bd;
|
||||
|
||||
import com.red.circle.other.inner.endpoint.team.bd.api.SalarySettlementRecordClientApi;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
|
||||
/**
|
||||
* BD/BD Leader/Admin 工资结算记录.
|
||||
*
|
||||
* @author AI Assistant
|
||||
*/
|
||||
@FeignClient(name = "salarySettlementRecordClient", url = "${feign.other.url}" +
|
||||
SalarySettlementRecordClientApi.API_PREFIX)
|
||||
public interface SalarySettlementRecordClient extends SalarySettlementRecordClientApi {
|
||||
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
package com.red.circle.other.inner.endpoint.team.bd.api;
|
||||
|
||||
import com.red.circle.framework.dto.PageResult;
|
||||
import com.red.circle.framework.dto.ResultResponse;
|
||||
import com.red.circle.other.inner.model.cmd.team.bd.SalarySettlementRecordPageQryCmd;
|
||||
import com.red.circle.other.inner.model.dto.agency.bd.SalarySettlementRecordDTO;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
|
||||
/**
|
||||
* BD/BD Leader/Admin 工资结算记录.
|
||||
*
|
||||
* @author AI Assistant
|
||||
*/
|
||||
public interface SalarySettlementRecordClientApi {
|
||||
|
||||
String API_PREFIX = "/bd/salary-settlement-record/client";
|
||||
|
||||
/**
|
||||
* 分页查询工资结算记录.
|
||||
*/
|
||||
@PostMapping("/page")
|
||||
ResultResponse<PageResult<SalarySettlementRecordDTO>> page(
|
||||
@RequestBody SalarySettlementRecordPageQryCmd qryCmd);
|
||||
}
|
||||
@ -0,0 +1,51 @@
|
||||
package com.red.circle.other.inner.model.cmd.team.bd;
|
||||
|
||||
import com.red.circle.framework.core.dto.PageCommand;
|
||||
import java.io.Serial;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* BD/BD Leader/Admin 工资结算记录分页查询.
|
||||
*
|
||||
* @author AI Assistant
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class SalarySettlementRecordPageQryCmd extends PageCommand {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 来源系统.
|
||||
*/
|
||||
private String sysOrigin;
|
||||
|
||||
/**
|
||||
* 身份:BD | BD_LEADER | ADMIN,空表示全部.
|
||||
*/
|
||||
private String identity;
|
||||
|
||||
/**
|
||||
* 用户 ID.
|
||||
*/
|
||||
private Long userId;
|
||||
|
||||
/**
|
||||
* 账期,yyyyMMdd 半月起始日,如 20260701 / 20260716.
|
||||
*/
|
||||
private Integer billBelong;
|
||||
|
||||
/**
|
||||
* 状态:PENDING | SUCCESS | FAILED.
|
||||
*/
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 幂等业务号.
|
||||
*/
|
||||
private String bizNo;
|
||||
}
|
||||
@ -0,0 +1,127 @@
|
||||
package com.red.circle.other.inner.model.dto.agency.bd;
|
||||
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||
import com.red.circle.framework.dto.DTO;
|
||||
import java.io.Serial;
|
||||
import java.math.BigDecimal;
|
||||
import java.sql.Timestamp;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* BD/BD Leader/Admin 工资结算记录.
|
||||
*
|
||||
* @author AI Assistant
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
public class SalarySettlementRecordDTO extends DTO {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private String id;
|
||||
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long timeId;
|
||||
|
||||
private String sysOrigin;
|
||||
|
||||
/**
|
||||
* 统一用户 ID,按身份分别对应 bdUserId/bdLeaderUserId/adminUserId.
|
||||
*/
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long userId;
|
||||
|
||||
private String identity;
|
||||
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long bdUserId;
|
||||
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long bdLeaderUserId;
|
||||
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long adminUserId;
|
||||
|
||||
private Boolean isAdmin;
|
||||
|
||||
private Integer billBelong;
|
||||
|
||||
private String billTitle;
|
||||
|
||||
private String bizNo;
|
||||
|
||||
private String status;
|
||||
|
||||
private String failureReason;
|
||||
|
||||
private Integer agencyNumber;
|
||||
|
||||
private Integer bdNumber;
|
||||
|
||||
private BigDecimal teamSalaryAmount;
|
||||
|
||||
private BigDecimal teamRechargeAmount;
|
||||
|
||||
private String hitPolicyType;
|
||||
|
||||
private Integer hitLevel;
|
||||
|
||||
private BigDecimal commissionRate;
|
||||
|
||||
private BigDecimal basicSalary;
|
||||
|
||||
private BigDecimal rewardAmount;
|
||||
|
||||
private Boolean rewardEligible;
|
||||
|
||||
private Integer newRechargeAgentCount;
|
||||
|
||||
private BigDecimal settlementSalary;
|
||||
|
||||
private BigDecimal rechargeAgentAmount;
|
||||
|
||||
private BigDecimal rechargeAgentSalary;
|
||||
|
||||
private BigDecimal rechargeAgentPercentage;
|
||||
|
||||
private BigDecimal bdLeaderTeamSalary;
|
||||
|
||||
private BigDecimal bdLeaderSalary;
|
||||
|
||||
private Integer bdLeaderHitLevel;
|
||||
|
||||
private BigDecimal bdLeaderBasicSalary;
|
||||
|
||||
private BigDecimal bdLeaderRewardAmount;
|
||||
|
||||
private BigDecimal bdTeamSalaryAmount;
|
||||
|
||||
private BigDecimal bdTeamSalarySalary;
|
||||
|
||||
private Integer bdTeamSalaryHitLevel;
|
||||
|
||||
private BigDecimal bdTeamSalaryCommissionRate;
|
||||
|
||||
private BigDecimal bdTeamRechargeAmount;
|
||||
|
||||
private BigDecimal bdTeamRechargeSalary;
|
||||
|
||||
private Integer bdTeamRechargeHitLevel;
|
||||
|
||||
private BigDecimal bdTeamRechargeCommissionRate;
|
||||
|
||||
private BigDecimal bdPolicyFinalSalary;
|
||||
|
||||
private String bdPolicyHitType;
|
||||
|
||||
private BigDecimal totalSalary;
|
||||
|
||||
private Timestamp createTime;
|
||||
|
||||
private Timestamp updateTime;
|
||||
}
|
||||
@ -1,10 +1,14 @@
|
||||
package com.red.circle.console.adapter.app.team;
|
||||
|
||||
import com.red.circle.console.app.dto.clienobject.team.TeamSalaryCO;
|
||||
import com.red.circle.console.app.dto.clienobject.team.SalarySettlementRecordCO;
|
||||
import com.red.circle.console.app.service.admin.UserAccountService;
|
||||
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 com.red.circle.other.inner.model.cmd.team.bd.SalarySettlementRecordPageQryCmd;
|
||||
import java.util.Objects;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
@ -20,6 +24,9 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
@RequestMapping("/team/salary")
|
||||
public class TeamSalaryRestController extends BaseController {
|
||||
|
||||
private static final String SALARY_RECORD_MENU_ALIAS = "team:salary:record";
|
||||
|
||||
private final UserAccountService userAccountService;
|
||||
private final TeamSalaryBackService teamSalaryBackService;
|
||||
|
||||
/**
|
||||
@ -30,4 +37,24 @@ public class TeamSalaryRestController extends BaseController {
|
||||
return teamSalaryBackService.listByCondition(query);
|
||||
}
|
||||
|
||||
/**
|
||||
* BD/BD Leader/Admin 工资结算记录.
|
||||
*/
|
||||
@GetMapping("/settlement/list")
|
||||
public PageResult<SalarySettlementRecordCO> listSettlementRecords(
|
||||
SalarySettlementRecordPageQryCmd query) {
|
||||
checkSalaryRecordPermission();
|
||||
query.setPageQuery(getPage());
|
||||
return teamSalaryBackService.listSettlementRecords(query);
|
||||
}
|
||||
|
||||
private void checkSalaryRecordPermission() {
|
||||
Long reqUserId = getReqUserId();
|
||||
// 工资记录包含 Admin 薪资,不能只依赖菜单隐藏,接口层也必须校验显式菜单权限。
|
||||
if (Objects.isNull(reqUserId)
|
||||
|| !userAccountService.hasButtonsAlias(reqUserId.intValue(), SALARY_RECORD_MENU_ALIAS)) {
|
||||
throw new RuntimeException("无权限访问工资记录");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -3,14 +3,18 @@ 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.SalarySettlementRecordCO;
|
||||
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.bd.SalarySettlementRecordClient;
|
||||
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.cmd.team.bd.SalarySettlementRecordPageQryCmd;
|
||||
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.other.inner.model.dto.agency.bd.SalarySettlementRecordDTO;
|
||||
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;
|
||||
@ -22,6 +26,7 @@ import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
@ -37,6 +42,7 @@ public class TeamSalaryBackServiceImpl implements TeamSalaryBackService {
|
||||
private final TeamProfileClient teamProfileClient;
|
||||
private final UserProfileClient userProfileClient;
|
||||
private final RegionConfigClient regionConfigClient;
|
||||
private final SalarySettlementRecordClient salarySettlementRecordClient;
|
||||
|
||||
@Override
|
||||
public PageResult<TeamSalaryCO> listByCondition(TeamSalaryBackQryCmd query) {
|
||||
@ -113,6 +119,25 @@ public class TeamSalaryBackServiceImpl implements TeamSalaryBackService {
|
||||
return page;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<SalarySettlementRecordCO> listSettlementRecords(
|
||||
SalarySettlementRecordPageQryCmd query) {
|
||||
PageResult<SalarySettlementRecordDTO> pageResult = ResponseAssert.requiredSuccess(
|
||||
salarySettlementRecordClient.page(query));
|
||||
|
||||
PageResult<SalarySettlementRecordCO> page = pageResult.convert(this::toSettlementRecordCO);
|
||||
if (pageResult.getTotal() <= 0) {
|
||||
return page;
|
||||
}
|
||||
|
||||
Map<Long, UserProfileDTO> userProfileMap = getSettlementUserProfileMap(pageResult);
|
||||
page.setRecords(page.getRecords().stream().peek(record -> {
|
||||
// 后台工资记录按身份统一展示,用户资料统一按结算用户 userId 补齐。
|
||||
record.setUserProfile(userProfileMap.get(record.getUserId()));
|
||||
}).collect(Collectors.toList()));
|
||||
return page;
|
||||
}
|
||||
|
||||
private Map<String, String> getMapRegionName(TeamSalaryBackQryCmd query) {
|
||||
return ResponseAssert.requiredSuccess(
|
||||
regionConfigClient.mapRegionNameBySysOrigin(query.getSysOrigin()));
|
||||
@ -147,4 +172,24 @@ public class TeamSalaryBackServiceImpl implements TeamSalaryBackService {
|
||||
return ResponseAssert.requiredSuccess(userProfileClient.mapByUserIds(userIds));
|
||||
}
|
||||
|
||||
private SalarySettlementRecordCO toSettlementRecordCO(SalarySettlementRecordDTO record) {
|
||||
SalarySettlementRecordCO co = new SalarySettlementRecordCO();
|
||||
BeanUtils.copyProperties(record, co);
|
||||
return co;
|
||||
}
|
||||
|
||||
private Map<Long, UserProfileDTO> getSettlementUserProfileMap(
|
||||
PageResult<SalarySettlementRecordDTO> pageResult) {
|
||||
Set<Long> userIds = pageResult.getRecords().stream()
|
||||
.map(SalarySettlementRecordDTO::getUserId)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
if (CollectionUtils.isEmpty(userIds)) {
|
||||
return Maps.newHashMap();
|
||||
}
|
||||
|
||||
return ResponseAssert.requiredSuccess(userProfileClient.mapByUserIds(userIds));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,27 @@
|
||||
package com.red.circle.console.app.dto.clienobject.team;
|
||||
|
||||
import com.red.circle.other.inner.model.dto.agency.bd.SalarySettlementRecordDTO;
|
||||
import com.red.circle.other.inner.model.dto.user.UserProfileDTO;
|
||||
import java.io.Serial;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* BD/BD Leader/Admin 工资结算记录.
|
||||
*
|
||||
* @author AI Assistant
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class SalarySettlementRecordCO extends SalarySettlementRecordDTO {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 结算用户资料.
|
||||
*/
|
||||
private UserProfileDTO userProfile;
|
||||
}
|
||||
@ -2,8 +2,10 @@ package com.red.circle.console.app.service.app.team;
|
||||
|
||||
|
||||
import com.red.circle.console.app.dto.clienobject.team.TeamSalaryCO;
|
||||
import com.red.circle.console.app.dto.clienobject.team.SalarySettlementRecordCO;
|
||||
import com.red.circle.framework.dto.PageResult;
|
||||
import com.red.circle.other.inner.model.cmd.team.TeamSalaryBackQryCmd;
|
||||
import com.red.circle.other.inner.model.cmd.team.bd.SalarySettlementRecordPageQryCmd;
|
||||
|
||||
/**
|
||||
* 团队自动发送工资凭证.
|
||||
@ -17,4 +19,10 @@ public interface TeamSalaryBackService {
|
||||
*/
|
||||
PageResult<TeamSalaryCO> listByCondition(TeamSalaryBackQryCmd query);
|
||||
|
||||
/**
|
||||
* 查询 BD/BD Leader/Admin 工资结算记录.
|
||||
*/
|
||||
PageResult<SalarySettlementRecordCO> listSettlementRecords(
|
||||
SalarySettlementRecordPageQryCmd query);
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,32 @@
|
||||
package com.red.circle.other.app.inner.endpoint.team.bd;
|
||||
|
||||
import com.red.circle.framework.dto.PageResult;
|
||||
import com.red.circle.framework.dto.ResultResponse;
|
||||
import com.red.circle.other.app.inner.service.team.SalarySettlementRecordClientService;
|
||||
import com.red.circle.other.inner.endpoint.team.bd.api.SalarySettlementRecordClientApi;
|
||||
import com.red.circle.other.inner.model.cmd.team.bd.SalarySettlementRecordPageQryCmd;
|
||||
import com.red.circle.other.inner.model.dto.agency.bd.SalarySettlementRecordDTO;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* BD/BD Leader/Admin 工资结算记录.
|
||||
*
|
||||
* @author AI Assistant
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping(value = SalarySettlementRecordClientApi.API_PREFIX,
|
||||
produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
@RequiredArgsConstructor
|
||||
public class SalarySettlementRecordClientEndpoint implements SalarySettlementRecordClientApi {
|
||||
|
||||
private final SalarySettlementRecordClientService salarySettlementRecordClientService;
|
||||
|
||||
@Override
|
||||
public ResultResponse<PageResult<SalarySettlementRecordDTO>> page(
|
||||
SalarySettlementRecordPageQryCmd qryCmd) {
|
||||
return ResultResponse.success(salarySettlementRecordClientService.page(qryCmd));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,18 @@
|
||||
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.bd.SalarySettlementRecordPageQryCmd;
|
||||
import com.red.circle.other.inner.model.dto.agency.bd.SalarySettlementRecordDTO;
|
||||
|
||||
/**
|
||||
* BD/BD Leader/Admin 工资结算记录.
|
||||
*
|
||||
* @author AI Assistant
|
||||
*/
|
||||
public interface SalarySettlementRecordClientService {
|
||||
|
||||
/**
|
||||
* 分页查询工资结算记录.
|
||||
*/
|
||||
PageResult<SalarySettlementRecordDTO> page(SalarySettlementRecordPageQryCmd qryCmd);
|
||||
}
|
||||
@ -0,0 +1,310 @@
|
||||
package com.red.circle.other.app.inner.service.team.impl;
|
||||
|
||||
import com.red.circle.framework.dto.PageQuery;
|
||||
import com.red.circle.framework.dto.PageResult;
|
||||
import com.red.circle.other.app.inner.service.team.SalarySettlementRecordClientService;
|
||||
import com.red.circle.other.infra.database.mongo.entity.bd.AdminSalarySettlementRecord;
|
||||
import com.red.circle.other.infra.database.mongo.entity.bd.BdLeaderSalarySettlementRecord;
|
||||
import com.red.circle.other.infra.database.mongo.entity.bd.BdSalarySettlementRecord;
|
||||
import com.red.circle.other.infra.enums.BdSettlementStatus;
|
||||
import com.red.circle.other.inner.model.cmd.team.bd.SalarySettlementRecordPageQryCmd;
|
||||
import com.red.circle.other.inner.model.dto.agency.bd.SalarySettlementRecordDTO;
|
||||
import com.red.circle.tool.core.text.StringUtils;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Function;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.data.domain.Sort;
|
||||
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;
|
||||
|
||||
/**
|
||||
* BD/BD Leader/Admin 工资结算记录.
|
||||
*
|
||||
* @author AI Assistant
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class SalarySettlementRecordClientServiceImpl implements SalarySettlementRecordClientService {
|
||||
|
||||
private static final String IDENTITY_BD = "BD";
|
||||
private static final String IDENTITY_BD_LEADER = "BD_LEADER";
|
||||
private static final String IDENTITY_ADMIN = "ADMIN";
|
||||
private static final int DEFAULT_CURSOR = 1;
|
||||
private static final int DEFAULT_LIMIT = 20;
|
||||
|
||||
private final MongoTemplate mongoTemplate;
|
||||
|
||||
@Override
|
||||
public PageResult<SalarySettlementRecordDTO> page(SalarySettlementRecordPageQryCmd qryCmd) {
|
||||
SalarySettlementRecordPageQryCmd query = Objects.requireNonNullElseGet(qryCmd,
|
||||
SalarySettlementRecordPageQryCmd::new);
|
||||
PageBounds pageBounds = getPageBounds(query);
|
||||
String identity = normalizeIdentity(query.getIdentity());
|
||||
if (StringUtils.isNotBlank(query.getIdentity()) && !isSupportedIdentity(identity)) {
|
||||
return buildPage(Collections.emptyList(), 0, pageBounds);
|
||||
}
|
||||
|
||||
if (StringUtils.isNotBlank(identity)) {
|
||||
return pageSingleIdentity(query, identity, pageBounds);
|
||||
}
|
||||
|
||||
List<SalarySettlementRecordDTO> records = new ArrayList<>();
|
||||
long total = 0;
|
||||
|
||||
total += count(query, "bdUserId", BdSalarySettlementRecord.class);
|
||||
total += count(query, "bdLeaderUserId", BdLeaderSalarySettlementRecord.class);
|
||||
total += count(query, "adminUserId", AdminSalarySettlementRecord.class);
|
||||
records.addAll(listRecords(query, "bdUserId", BdSalarySettlementRecord.class,
|
||||
this::toBdRecordDTO, 0, pageBounds.fetchSize()));
|
||||
records.addAll(listRecords(query, "bdLeaderUserId", BdLeaderSalarySettlementRecord.class,
|
||||
this::toBdLeaderRecordDTO, 0, pageBounds.fetchSize()));
|
||||
records.addAll(listRecords(query, "adminUserId", AdminSalarySettlementRecord.class,
|
||||
this::toAdminRecordDTO, 0, pageBounds.fetchSize()));
|
||||
|
||||
// 三类记录分散在不同 collection,全部身份查询只各取当前页所需前 N 条再合并排序。
|
||||
records.sort((left, right) -> compareTimestampDesc(left.getCreateTime(), right.getCreateTime()));
|
||||
Collection<SalarySettlementRecordDTO> pageRecords = slice(records, pageBounds);
|
||||
return buildPage(pageRecords, total, pageBounds);
|
||||
}
|
||||
|
||||
private PageResult<SalarySettlementRecordDTO> pageSingleIdentity(
|
||||
SalarySettlementRecordPageQryCmd query, String identity, PageBounds pageBounds) {
|
||||
if (Objects.equals(identity, IDENTITY_BD)) {
|
||||
return pageTypedRecords(query, "bdUserId", BdSalarySettlementRecord.class,
|
||||
this::toBdRecordDTO, pageBounds);
|
||||
}
|
||||
if (Objects.equals(identity, IDENTITY_BD_LEADER)) {
|
||||
return pageTypedRecords(query, "bdLeaderUserId", BdLeaderSalarySettlementRecord.class,
|
||||
this::toBdLeaderRecordDTO, pageBounds);
|
||||
}
|
||||
return pageTypedRecords(query, "adminUserId", AdminSalarySettlementRecord.class,
|
||||
this::toAdminRecordDTO, pageBounds);
|
||||
}
|
||||
|
||||
private <T> PageResult<SalarySettlementRecordDTO> pageTypedRecords(
|
||||
SalarySettlementRecordPageQryCmd query, String userIdField, Class<T> entityClass,
|
||||
Function<T, SalarySettlementRecordDTO> mapper, PageBounds pageBounds) {
|
||||
long total = count(query, userIdField, entityClass);
|
||||
List<SalarySettlementRecordDTO> records = listRecords(query, userIdField, entityClass, mapper,
|
||||
pageBounds.skip(), pageBounds.limit());
|
||||
return buildPage(records, total, pageBounds);
|
||||
}
|
||||
|
||||
private PageBounds getPageBounds(SalarySettlementRecordPageQryCmd query) {
|
||||
PageQuery pageQuery = query.getPageQuery();
|
||||
int cursor = pageQuery == null || pageQuery.getCursor() == null ? DEFAULT_CURSOR
|
||||
: pageQuery.getCursor();
|
||||
int limit = pageQuery == null || pageQuery.getLimit() == null ? DEFAULT_LIMIT
|
||||
: pageQuery.getLimit();
|
||||
int safeCursor = cursor <= 0 ? DEFAULT_CURSOR : cursor;
|
||||
int safeLimit = limit <= 0 ? DEFAULT_LIMIT : limit;
|
||||
return new PageBounds(safeCursor, safeLimit);
|
||||
}
|
||||
|
||||
private Collection<SalarySettlementRecordDTO> slice(List<SalarySettlementRecordDTO> records,
|
||||
PageBounds pageBounds) {
|
||||
int fromIndex = Math.max(pageBounds.skip(), 0);
|
||||
int toIndex = Math.min(fromIndex + pageBounds.limit(), records.size());
|
||||
return fromIndex >= records.size()
|
||||
? Collections.emptyList()
|
||||
: records.subList(fromIndex, toIndex);
|
||||
}
|
||||
|
||||
private PageResult<SalarySettlementRecordDTO> buildPage(
|
||||
Collection<SalarySettlementRecordDTO> records, long total, PageBounds pageBounds) {
|
||||
PageResult<SalarySettlementRecordDTO> pageResult = new PageResult<>();
|
||||
pageResult.setCurrent(pageBounds.cursor());
|
||||
pageResult.setSize(pageBounds.limit());
|
||||
pageResult.setTotal(total);
|
||||
pageResult.setRecords(records);
|
||||
return pageResult;
|
||||
}
|
||||
|
||||
private <T> long count(SalarySettlementRecordPageQryCmd query, String userIdField,
|
||||
Class<T> entityClass) {
|
||||
return mongoTemplate.count(buildQuery(query, userIdField), entityClass);
|
||||
}
|
||||
|
||||
private <T> List<SalarySettlementRecordDTO> listRecords(SalarySettlementRecordPageQryCmd query,
|
||||
String userIdField, Class<T> entityClass, Function<T, SalarySettlementRecordDTO> mapper,
|
||||
int skip, int limit) {
|
||||
Query mongoQuery = buildQuery(query, userIdField);
|
||||
mongoQuery.skip(skip);
|
||||
mongoQuery.limit(limit);
|
||||
return mongoTemplate.find(mongoQuery, entityClass)
|
||||
.stream()
|
||||
.map(mapper)
|
||||
.toList();
|
||||
}
|
||||
|
||||
private Query buildQuery(SalarySettlementRecordPageQryCmd query, String userIdField) {
|
||||
List<Criteria> criteriaList = new ArrayList<>();
|
||||
if (StringUtils.isNotBlank(query.getSysOrigin())) {
|
||||
criteriaList.add(Criteria.where("sysOrigin").is(query.getSysOrigin()));
|
||||
}
|
||||
if (Objects.nonNull(query.getUserId())) {
|
||||
criteriaList.add(Criteria.where(userIdField).is(query.getUserId()));
|
||||
}
|
||||
if (Objects.nonNull(query.getBillBelong())) {
|
||||
criteriaList.add(Criteria.where("billBelong").is(query.getBillBelong()));
|
||||
}
|
||||
if (StringUtils.isNotBlank(query.getStatus())) {
|
||||
BdSettlementStatus status = parseStatus(query.getStatus());
|
||||
criteriaList.add(Objects.isNull(status)
|
||||
? Criteria.where("_id").is("__invalid_status__")
|
||||
: Criteria.where("status").is(status));
|
||||
}
|
||||
if (StringUtils.isNotBlank(query.getBizNo())) {
|
||||
criteriaList.add(Criteria.where("bizNo").is(query.getBizNo()));
|
||||
}
|
||||
|
||||
Query mongoQuery = criteriaList.isEmpty()
|
||||
? new Query()
|
||||
: new Query(new Criteria().andOperator(criteriaList.toArray(new Criteria[0])));
|
||||
mongoQuery.with(Sort.by(Sort.Order.desc("createTime")));
|
||||
return mongoQuery;
|
||||
}
|
||||
|
||||
private BdSettlementStatus parseStatus(String status) {
|
||||
try {
|
||||
return BdSettlementStatus.valueOf(status.trim().toUpperCase(Locale.ROOT));
|
||||
} catch (IllegalArgumentException ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private String normalizeIdentity(String identity) {
|
||||
return StringUtils.isBlank(identity) ? null : identity.trim().toUpperCase(Locale.ROOT);
|
||||
}
|
||||
|
||||
private boolean isSupportedIdentity(String identity) {
|
||||
return Objects.equals(identity, IDENTITY_BD)
|
||||
|| Objects.equals(identity, IDENTITY_BD_LEADER)
|
||||
|| Objects.equals(identity, IDENTITY_ADMIN);
|
||||
}
|
||||
|
||||
private int compareTimestampDesc(Timestamp left, Timestamp right) {
|
||||
if (Objects.isNull(left) && Objects.isNull(right)) {
|
||||
return 0;
|
||||
}
|
||||
if (Objects.isNull(left)) {
|
||||
return 1;
|
||||
}
|
||||
if (Objects.isNull(right)) {
|
||||
return -1;
|
||||
}
|
||||
return right.compareTo(left);
|
||||
}
|
||||
|
||||
private SalarySettlementRecordDTO toBdRecordDTO(BdSalarySettlementRecord record) {
|
||||
String status = record.getStatus() == null ? null : record.getStatus().name();
|
||||
String hitPolicyType = record.getHitPolicyType() == null ? null : record.getHitPolicyType().name();
|
||||
return new SalarySettlementRecordDTO()
|
||||
.setId(record.getId())
|
||||
.setTimeId(record.getTimeId())
|
||||
.setSysOrigin(record.getSysOrigin())
|
||||
.setIdentity(IDENTITY_BD)
|
||||
.setUserId(record.getBdUserId())
|
||||
.setBdUserId(record.getBdUserId())
|
||||
.setIsAdmin(record.getIsAdmin())
|
||||
.setBillBelong(record.getBillBelong())
|
||||
.setBillTitle(record.getBillTitle())
|
||||
.setAgencyNumber(record.getAgencyNumber())
|
||||
.setTeamSalaryAmount(record.getTeamSalaryAmount())
|
||||
.setTeamRechargeAmount(record.getTeamRechargeAmount())
|
||||
.setHitPolicyType(hitPolicyType)
|
||||
.setHitLevel(record.getHitLevel())
|
||||
.setCommissionRate(record.getCommissionRate())
|
||||
.setSettlementSalary(record.getSettlementSalary())
|
||||
.setStatus(status)
|
||||
.setFailureReason(record.getFailureReason())
|
||||
.setBizNo(record.getBizNo())
|
||||
.setCreateTime(record.getCreateTime())
|
||||
.setUpdateTime(record.getUpdateTime());
|
||||
}
|
||||
|
||||
private SalarySettlementRecordDTO toBdLeaderRecordDTO(BdLeaderSalarySettlementRecord record) {
|
||||
String status = record.getStatus() == null ? null : record.getStatus().name();
|
||||
return new SalarySettlementRecordDTO()
|
||||
.setId(record.getId())
|
||||
.setTimeId(record.getTimeId())
|
||||
.setSysOrigin(record.getSysOrigin())
|
||||
.setIdentity(IDENTITY_BD_LEADER)
|
||||
.setUserId(record.getBdLeaderUserId())
|
||||
.setBdLeaderUserId(record.getBdLeaderUserId())
|
||||
.setIsAdmin(record.getIsAdmin())
|
||||
.setBillBelong(record.getBillBelong())
|
||||
.setBillTitle(record.getBillTitle())
|
||||
.setBdNumber(record.getBdNumber())
|
||||
.setTeamSalaryAmount(record.getTeamSalaryAmount())
|
||||
.setHitLevel(record.getHitLevel())
|
||||
.setBasicSalary(record.getBasicSalary())
|
||||
.setRewardAmount(record.getRewardAmount())
|
||||
.setRewardEligible(record.getRewardEligible())
|
||||
.setNewRechargeAgentCount(record.getNewRechargeAgentCount())
|
||||
.setSettlementSalary(record.getSettlementSalary())
|
||||
.setStatus(status)
|
||||
.setFailureReason(record.getFailureReason())
|
||||
.setBizNo(record.getBizNo())
|
||||
.setCreateTime(record.getCreateTime())
|
||||
.setUpdateTime(record.getUpdateTime());
|
||||
}
|
||||
|
||||
private SalarySettlementRecordDTO toAdminRecordDTO(AdminSalarySettlementRecord record) {
|
||||
String status = record.getStatus() == null ? null : record.getStatus().name();
|
||||
return new SalarySettlementRecordDTO()
|
||||
.setId(record.getId())
|
||||
.setTimeId(record.getTimeId())
|
||||
.setSysOrigin(record.getSysOrigin())
|
||||
.setIdentity(IDENTITY_ADMIN)
|
||||
.setUserId(record.getAdminUserId())
|
||||
.setAdminUserId(record.getAdminUserId())
|
||||
.setBillBelong(record.getBillBelong())
|
||||
.setBillTitle(record.getBillTitle())
|
||||
.setBizNo(record.getBizNo())
|
||||
.setRechargeAgentAmount(record.getRechargeAgentAmount())
|
||||
.setRechargeAgentSalary(record.getRechargeAgentSalary())
|
||||
.setRechargeAgentPercentage(record.getRechargeAgentPercentage())
|
||||
.setBdLeaderTeamSalary(record.getBdLeaderTeamSalary())
|
||||
.setBdLeaderSalary(record.getBdLeaderSalary())
|
||||
.setBdLeaderHitLevel(record.getBdLeaderHitLevel())
|
||||
.setBdLeaderBasicSalary(record.getBdLeaderBasicSalary())
|
||||
.setBdLeaderRewardAmount(record.getBdLeaderRewardAmount())
|
||||
.setBdTeamSalaryAmount(record.getBdTeamSalaryAmount())
|
||||
.setBdTeamSalarySalary(record.getBdTeamSalarySalary())
|
||||
.setBdTeamSalaryHitLevel(record.getBdTeamSalaryHitLevel())
|
||||
.setBdTeamSalaryCommissionRate(record.getBdTeamSalaryCommissionRate())
|
||||
.setBdTeamRechargeAmount(record.getBdTeamRechargeAmount())
|
||||
.setBdTeamRechargeSalary(record.getBdTeamRechargeSalary())
|
||||
.setBdTeamRechargeHitLevel(record.getBdTeamRechargeHitLevel())
|
||||
.setBdTeamRechargeCommissionRate(record.getBdTeamRechargeCommissionRate())
|
||||
.setBdPolicyFinalSalary(record.getBdPolicyFinalSalary())
|
||||
.setBdPolicyHitType(record.getBdPolicyHitType())
|
||||
.setTotalSalary(record.getTotalSalary())
|
||||
.setSettlementSalary(record.getTotalSalary())
|
||||
.setStatus(status)
|
||||
.setFailureReason(record.getFailureReason())
|
||||
.setCreateTime(record.getCreateTime())
|
||||
.setUpdateTime(record.getUpdateTime());
|
||||
}
|
||||
|
||||
private record PageBounds(int cursor, int limit) {
|
||||
|
||||
private int skip() {
|
||||
return Math.max((cursor - 1) * limit, 0);
|
||||
}
|
||||
|
||||
private int fetchSize() {
|
||||
long size = (long) cursor * limit;
|
||||
return size > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) size;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user