重新修改盈亏逻辑
This commit is contained in:
@@ -236,6 +236,7 @@ public class SysOaProjectController extends BaseController {
|
|||||||
@RequestParam(required = false) String projectName,
|
@RequestParam(required = false) String projectName,
|
||||||
@RequestParam(required = false) String projectNum,
|
@RequestParam(required = false) String projectNum,
|
||||||
@RequestParam(required = false) String projectStatus,
|
@RequestParam(required = false) String projectStatus,
|
||||||
|
@RequestParam(required = false) String isDomestic,
|
||||||
@RequestParam(required = false) BigDecimal minContractAmount,
|
@RequestParam(required = false) BigDecimal minContractAmount,
|
||||||
@RequestParam(required = false) BigDecimal maxContractAmount,
|
@RequestParam(required = false) BigDecimal maxContractAmount,
|
||||||
@RequestParam(required = false) BigDecimal minProfitLoss,
|
@RequestParam(required = false) BigDecimal minProfitLoss,
|
||||||
@@ -251,7 +252,7 @@ public class SysOaProjectController extends BaseController {
|
|||||||
pageQuery.setIsAsc(sortOrder);
|
pageQuery.setIsAsc(sortOrder);
|
||||||
System.out.println("收到");
|
System.out.println("收到");
|
||||||
return iSysOaProjectService.getProjectProfitLossList(
|
return iSysOaProjectService.getProjectProfitLossList(
|
||||||
projectName, projectNum, projectStatus,
|
projectName, projectNum, projectStatus,isDomestic,
|
||||||
minContractAmount, maxContractAmount,
|
minContractAmount, maxContractAmount,
|
||||||
minProfitLoss, maxProfitLoss,
|
minProfitLoss, maxProfitLoss,
|
||||||
beginTimeStart, beginTimeEnd, profitType, pageQuery);
|
beginTimeStart, beginTimeEnd, profitType, pageQuery);
|
||||||
|
|||||||
@@ -16,17 +16,19 @@ public class ProjectProfitLossVO {
|
|||||||
@JsonFormat(shape = JsonFormat.Shape.STRING)
|
@JsonFormat(shape = JsonFormat.Shape.STRING)
|
||||||
private BigDecimal originalFunds; // 原始合同金额
|
private BigDecimal originalFunds; // 原始合同金额
|
||||||
private String projectRemark; // 项目备注(用于判断币种)
|
private String projectRemark; // 项目备注(用于判断币种)
|
||||||
private BigDecimal contractAmountUsd; // 美元合同金额
|
|
||||||
private Integer isUsd; // 是否美元(1是,0否)
|
private Integer isUsd; // 是否美元(1是,0否)
|
||||||
|
// 从财务明细中汇总的总收入(仅用于无合同额的项目)
|
||||||
|
@JsonFormat(shape = JsonFormat.Shape.STRING)
|
||||||
|
private BigDecimal detailIncome;
|
||||||
private BigDecimal exchangeRate; // 实时汇率
|
private BigDecimal exchangeRate; // 实时汇率
|
||||||
|
// 从财务明细中汇总的总支出(所有项目的成本来源)
|
||||||
@JsonFormat(shape = JsonFormat.Shape.STRING)
|
@JsonFormat(shape = JsonFormat.Shape.STRING)
|
||||||
private BigDecimal contractAmountCny; // 人民币合同金额
|
private BigDecimal totalExpenditure;
|
||||||
|
// 最终计算出的总收入(人民币,展示用)
|
||||||
@JsonFormat(shape = JsonFormat.Shape.STRING)
|
@JsonFormat(shape = JsonFormat.Shape.STRING)
|
||||||
private BigDecimal warehouseCost;
|
private BigDecimal totalIncomeCny;
|
||||||
@JsonFormat(shape = JsonFormat.Shape.STRING)
|
|
||||||
private BigDecimal hrCost;
|
|
||||||
@JsonFormat(shape = JsonFormat.Shape.STRING)
|
@JsonFormat(shape = JsonFormat.Shape.STRING)
|
||||||
private BigDecimal profitLoss; //盈亏金额
|
private BigDecimal profitLoss; //盈亏金额
|
||||||
// ...其他字段
|
// ...其他字段
|
||||||
// getter/setter
|
|
||||||
}
|
}
|
||||||
@@ -24,7 +24,7 @@ public interface ISysOaProjectService {
|
|||||||
/**
|
/**
|
||||||
* 项目盈亏排序
|
* 项目盈亏排序
|
||||||
*/
|
*/
|
||||||
TableDataInfo<ProjectProfitLossVO> getProjectProfitLossList(String projectName,String projectNum,String projectStatus,
|
TableDataInfo<ProjectProfitLossVO> getProjectProfitLossList(String projectName,String projectNum,String projectStatus,String isisDomestic,
|
||||||
BigDecimal minContractAmount,BigDecimal maxContractAmount,BigDecimal minProfitLoss,
|
BigDecimal minContractAmount,BigDecimal maxContractAmount,BigDecimal minProfitLoss,
|
||||||
BigDecimal maxProfitLoss,String beginTimeStart,String beginTimeEnd,String profitType,
|
BigDecimal maxProfitLoss,String beginTimeStart,String beginTimeEnd,String profitType,
|
||||||
PageQuery pageQuery);
|
PageQuery pageQuery);
|
||||||
|
|||||||
@@ -70,13 +70,13 @@ public class SysOaProjectServiceImpl implements ISysOaProjectService {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public TableDataInfo<ProjectProfitLossVO> getProjectProfitLossList(
|
public TableDataInfo<ProjectProfitLossVO> getProjectProfitLossList(
|
||||||
String projectName, String projectNum, String projectStatus,
|
String projectName, String projectNum, String projectStatus, String isDomestic,
|
||||||
BigDecimal minContractAmount, BigDecimal maxContractAmount,
|
BigDecimal minContractAmount, BigDecimal maxContractAmount,
|
||||||
BigDecimal minProfitLoss, BigDecimal maxProfitLoss,
|
BigDecimal minProfitLoss, BigDecimal maxProfitLoss,
|
||||||
String beginTimeStart, String beginTimeEnd, String profitType,
|
String beginTimeStart, String beginTimeEnd, String profitType,
|
||||||
PageQuery pageQuery
|
PageQuery pageQuery
|
||||||
) {
|
) {
|
||||||
// 1. 获取实时汇率,用于后续计算
|
// 1. 获取实时汇率
|
||||||
BigDecimal currentExchangeRate = exchangeRateService.getCurrentUsdExchangeRate();
|
BigDecimal currentExchangeRate = exchangeRateService.getCurrentUsdExchangeRate();
|
||||||
|
|
||||||
// 2. 构建基础查询条件 (WHERE子句)
|
// 2. 构建基础查询条件 (WHERE子句)
|
||||||
@@ -84,63 +84,51 @@ public class SysOaProjectServiceImpl implements ISysOaProjectService {
|
|||||||
wrapper.like(StringUtils.isNotBlank(projectName), "p.project_name", projectName);
|
wrapper.like(StringUtils.isNotBlank(projectName), "p.project_name", projectName);
|
||||||
wrapper.like(StringUtils.isNotBlank(projectNum), "p.project_num", projectNum);
|
wrapper.like(StringUtils.isNotBlank(projectNum), "p.project_num", projectNum);
|
||||||
wrapper.eq(StringUtils.isNotBlank(projectStatus), "p.project_status", projectStatus);
|
wrapper.eq(StringUtils.isNotBlank(projectStatus), "p.project_status", projectStatus);
|
||||||
// wrapper.eq(StringUtils.isNotBlank(isDomestic), "p.is_domestic", isDomestic); // 如果需要,可以放开此注释
|
|
||||||
|
|
||||||
// 对原始合同金额进行筛选
|
|
||||||
wrapper.ge(minContractAmount != null, "p.funds", minContractAmount);
|
wrapper.ge(minContractAmount != null, "p.funds", minContractAmount);
|
||||||
wrapper.le(maxContractAmount != null, "p.funds", maxContractAmount);
|
wrapper.le(maxContractAmount != null, "p.funds", maxContractAmount);
|
||||||
|
|
||||||
// 对项目时间进行筛选
|
|
||||||
wrapper.ge(StringUtils.isNotBlank(beginTimeStart), "p.begin_time", beginTimeStart);
|
wrapper.ge(StringUtils.isNotBlank(beginTimeStart), "p.begin_time", beginTimeStart);
|
||||||
wrapper.le(StringUtils.isNotBlank(beginTimeEnd), "p.begin_time", beginTimeEnd);
|
wrapper.le(StringUtils.isNotBlank(beginTimeEnd), "p.begin_time", beginTimeEnd);
|
||||||
|
|
||||||
// 3. 构建HAVING子句,用于筛选计算后的盈亏值
|
// 3. 构建HAVING子句, 用于筛选计算后的盈亏值
|
||||||
// HAVING子句允许我们在GROUP BY或聚合函数计算后进行条件筛选
|
|
||||||
StringBuilder havingClause = new StringBuilder();
|
StringBuilder havingClause = new StringBuilder();
|
||||||
|
|
||||||
// 盈亏类型筛选
|
|
||||||
if ("profit".equals(profitType)) {
|
if ("profit".equals(profitType)) {
|
||||||
havingClause.append("profit_loss > 0");
|
havingClause.append("profit_loss > 0");
|
||||||
} else if ("loss".equals(profitType)) {
|
} else if ("loss".equals(profitType)) {
|
||||||
havingClause.append("profit_loss < 0");
|
havingClause.append("profit_loss < 0");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 盈亏区间筛选
|
|
||||||
if (minProfitLoss != null) {
|
if (minProfitLoss != null) {
|
||||||
if (havingClause.length() > 0) {
|
if (havingClause.length() > 0) havingClause.append(" AND ");
|
||||||
havingClause.append(" AND ");
|
|
||||||
}
|
|
||||||
havingClause.append("profit_loss >= ").append(minProfitLoss);
|
havingClause.append("profit_loss >= ").append(minProfitLoss);
|
||||||
}
|
}
|
||||||
if (maxProfitLoss != null) {
|
if (maxProfitLoss != null) {
|
||||||
if (havingClause.length() > 0) {
|
if (havingClause.length() > 0) havingClause.append(" AND ");
|
||||||
havingClause.append(" AND ");
|
|
||||||
}
|
|
||||||
havingClause.append("profit_loss <= ").append(maxProfitLoss);
|
havingClause.append("profit_loss <= ").append(maxProfitLoss);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. 将HAVING子句应用到QueryWrapper
|
|
||||||
if (havingClause.length() > 0) {
|
if (havingClause.length() > 0) {
|
||||||
wrapper.having(havingClause.toString());
|
wrapper.having(havingClause.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5. 构建分页对象 (排序由PageQuery自动处理)
|
// 4. 构建分页对象 (排序由PageQuery自动处理)
|
||||||
Page<ProjectProfitLossVO> page = pageQuery.build();
|
Page<ProjectProfitLossVO> page = pageQuery.build();
|
||||||
|
|
||||||
// 6. 调用Mapper方法,传入汇率,让数据库完成计算、筛选和分页
|
// 5. 调用Mapper方法, 传入汇率
|
||||||
Page<ProjectProfitLossVO> result = baseMapper.selectProfitLossPage(page, wrapper, currentExchangeRate);
|
Page<ProjectProfitLossVO> result = baseMapper.selectProfitLossPage(page, wrapper, currentExchangeRate);
|
||||||
|
|
||||||
// 7. 再次填充VO中的展示字段,确保前端获取的数据是完整的
|
// 6. 再次填充VO中的展示字段, 确保前端获取的数据是完整的
|
||||||
// 虽然数据库已经计算了profit_loss,但在这里填充其他相关字段(如汇率、转换后的合同额)可以让API返回更丰富的信息
|
|
||||||
for (ProjectProfitLossVO vo : result.getRecords()) {
|
for (ProjectProfitLossVO vo : result.getRecords()) {
|
||||||
vo.setExchangeRate(currentExchangeRate); // 填充实时汇率
|
vo.setExchangeRate(currentExchangeRate); // 填充实时汇率
|
||||||
if (vo.getIsUsd() == 1) {
|
|
||||||
// 计算人民币合同额用于展示
|
// 计算最终的总收入(人民币)用于展示
|
||||||
vo.setContractAmountCny(vo.getOriginalFunds().multiply(currentExchangeRate));
|
if (vo.getOriginalFunds() != null && vo.getOriginalFunds().compareTo(BigDecimal.ZERO) > 0) {
|
||||||
|
if (vo.getIsUsd() == 1) {
|
||||||
|
vo.setTotalIncomeCny(vo.getOriginalFunds().multiply(currentExchangeRate));
|
||||||
|
} else {
|
||||||
|
vo.setTotalIncomeCny(vo.getOriginalFunds());
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
vo.setContractAmountCny(vo.getOriginalFunds());
|
vo.setTotalIncomeCny(vo.getDetailIncome());
|
||||||
}
|
}
|
||||||
// profit_loss 已经由数据库计算返回,无需再次计算
|
// profit_loss 已经由数据库根据最新逻辑计算返回, 无需再次处理
|
||||||
}
|
}
|
||||||
|
|
||||||
return TableDataInfo.build(result);
|
return TableDataInfo.build(result);
|
||||||
|
|||||||
@@ -815,6 +815,7 @@
|
|||||||
</select>
|
</select>
|
||||||
<!-- SysOaProjectMapper.xml -->
|
<!-- SysOaProjectMapper.xml -->
|
||||||
|
|
||||||
|
<!-- SysOaProjectMapper.xml -->
|
||||||
<select id="selectProfitLossPage" resultType="com.ruoyi.oa.domain.vo.ProjectProfitLossVO">
|
<select id="selectProfitLossPage" resultType="com.ruoyi.oa.domain.vo.ProjectProfitLossVO">
|
||||||
SELECT
|
SELECT
|
||||||
p.project_id,
|
p.project_id,
|
||||||
@@ -824,54 +825,46 @@
|
|||||||
p.project_status,
|
p.project_status,
|
||||||
p.funds AS original_funds,
|
p.funds AS original_funds,
|
||||||
p.remark AS project_remark,
|
p.remark AS project_remark,
|
||||||
COALESCE(wd.total_warehouse_cost, 0) AS warehouse_cost,
|
COALESCE(finance_details.total_income, 0) AS detail_income,
|
||||||
COALESCE(hr.total_hr_cost, 0) AS hr_cost,
|
COALESCE(finance_details.total_expenditure, 0) AS total_expenditure,
|
||||||
CASE
|
CASE
|
||||||
WHEN p.remark LIKE '%美元%' OR p.remark LIKE '%美金%' THEN 1
|
WHEN p.remark LIKE '%美元%' OR p.remark LIKE '%美金%' THEN 1
|
||||||
ELSE 0
|
ELSE 0
|
||||||
END AS is_usd,
|
END AS is_usd,
|
||||||
|
-- 核心盈亏计算逻辑
|
||||||
(
|
(
|
||||||
|
-- 首先计算总收入
|
||||||
CASE
|
CASE
|
||||||
WHEN p.remark LIKE '%美元%' OR p.remark LIKE '%美金%'
|
-- 场景1: 如果项目有合同额, 则总收入为合同额 (需考虑汇率)
|
||||||
THEN p.funds * #{exchangeRate}
|
WHEN p.funds IS NOT NULL AND p.funds > 0
|
||||||
ELSE p.funds
|
THEN
|
||||||
|
CASE
|
||||||
|
WHEN p.remark LIKE '%美元%' OR p.remark LIKE '%美金%'
|
||||||
|
THEN p.funds * #{exchangeRate}
|
||||||
|
ELSE p.funds
|
||||||
|
END
|
||||||
|
-- 场景2: 如果项目没有合同额, 则总收入为财务明细中的入账总和
|
||||||
|
ELSE COALESCE(finance_details.total_income, 0)
|
||||||
END
|
END
|
||||||
- COALESCE(wd.total_warehouse_cost, 0)
|
-- 减去总支出
|
||||||
- COALESCE(hr.total_hr_cost, 0)
|
- COALESCE(finance_details.total_expenditure, 0)
|
||||||
) AS profit_loss
|
) AS profit_loss
|
||||||
FROM
|
FROM
|
||||||
sys_oa_project p
|
sys_oa_project p
|
||||||
LEFT JOIN (
|
LEFT JOIN (
|
||||||
|
-- 将财务主表和明细表关联,一次性计算出每个项目的总收入和总支出
|
||||||
SELECT
|
SELECT
|
||||||
project_id,
|
f.project_id,
|
||||||
SUM(amount * sign_price) AS total_warehouse_cost
|
SUM(CASE WHEN f.finance_type = '1' THEN d.price ELSE 0 END) AS total_income,
|
||||||
|
SUM(CASE WHEN f.finance_type = '0' THEN d.price ELSE 0 END) AS total_expenditure
|
||||||
FROM
|
FROM
|
||||||
sys_oa_warehouse_detail
|
sys_oa_finance f
|
||||||
WHERE
|
|
||||||
del_flag = 0
|
|
||||||
GROUP BY
|
|
||||||
project_id
|
|
||||||
) wd ON p.project_id = wd.project_id
|
|
||||||
LEFT JOIN (
|
|
||||||
SELECT
|
|
||||||
a.project_id,
|
|
||||||
SUM(
|
|
||||||
CASE
|
|
||||||
-- 当小时数存在时,使用日薪/8计算小时薪资
|
|
||||||
WHEN a.hour > 0 THEN a.hour * (COALESCE(u.labor_cost, 0) DIV 8)
|
|
||||||
-- 当日数存在时,使用日薪×天数
|
|
||||||
ELSE a.day_length * COALESCE(u.labor_cost, 0)
|
|
||||||
END
|
|
||||||
) AS total_hr_cost
|
|
||||||
FROM
|
|
||||||
sys_oa_attendance a
|
|
||||||
JOIN
|
JOIN
|
||||||
sys_user u ON a.user_id = u.user_id
|
sys_oa_detail d ON f.finance_id = d.finance_id
|
||||||
WHERE
|
WHERE f.project_id > 0
|
||||||
a.del_flag = 0
|
|
||||||
GROUP BY
|
GROUP BY
|
||||||
a.project_id
|
f.project_id
|
||||||
) hr ON p.project_id = hr.project_id
|
) AS finance_details ON p.project_id = finance_details.project_id
|
||||||
${ew.customSqlSegment}
|
${ew.customSqlSegment}
|
||||||
</select>
|
</select>
|
||||||
</mapper>
|
</mapper>
|
||||||
|
|||||||
Reference in New Issue
Block a user