feat(projects-assign): P1+P2+P3 修复 (含服务端超额校验)

P1:
- 截止天数 max=100 + min=1 (前端拦超界输入)
- 合同文件区实装 (support/execute_contract_url 走 Preview.vue, 单条模式显示)

P2:
- 可用金额公式实时显示 (总金额×(1-管理费/总金额)-累计劳务-累计会务)
- 服务端二次校验: BizProjectAssignServiceImpl.validateSum 防前端绕过

P3:
- 命名统一: 支持方→支持单位 / 执行方→执行单位
- 行内删除按钮: 仅剩1 行时禁用第一个
- loading 态: el-descriptions + 批量 info-bar 加 v-loading
- 批量模式加载失败提示: failed 数组 + ElMessageBox.alert

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
郭庆泰
2026-08-20 23:31:37 +08:00
co-authored by Claude
parent bf1b4a3010
commit 79d7849a27
4 changed files with 142 additions and 10 deletions
@@ -172,6 +172,7 @@ public class BizProjectController extends BaseController
public AjaxResult saveAssigns(@PathVariable("projectId") Long projectId, @RequestBody List<BizProjectAssign> assigns)
{
if (assigns == null) assigns = new ArrayList<>();
bizProjectAssignService.validateSum(projectId, assigns);
bizProjectAssignService.deleteByProjectId(projectId);
for (BizProjectAssign a : assigns) {
a.setProjectId(projectId);
@@ -15,4 +15,9 @@ public interface IBizProjectAssignService
int updateByPrimaryKey(BizProjectAssign entity);
int deleteByPrimaryKey(String assignId);
int deleteByProjectId(Long projectId);
/**
* 校验多个执行方总金额不超过项目总金额 (服务端二次校验, 防前端绕过)
* 超额抛 ServiceException
*/
void validateSum(Long projectId, List<BizProjectAssign> assigns);
}
@@ -1,13 +1,16 @@
package com.ruoyi.business.service.impl;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ruoyi.business.domain.BizOrg;
import com.ruoyi.business.domain.BizProject;
import com.ruoyi.business.domain.BizProjectAssign;
import com.ruoyi.business.mapper.BizOrgMapper;
import com.ruoyi.business.mapper.BizProjectAssignMapper;
import com.ruoyi.business.mapper.BizProjectMapper;
import com.ruoyi.business.service.IBizProjectAssignService;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.SecurityUtils;
@@ -20,6 +23,8 @@ public class BizProjectAssignServiceImpl implements IBizProjectAssignService
private BizProjectAssignMapper bizProjectAssignMapper;
@Autowired
private BizOrgMapper bizOrgMapper;
@Autowired
private BizProjectMapper bizProjectMapper;
@Override
public BizProjectAssign getById(String assignId) { return bizProjectAssignMapper.selectByPrimaryKey(assignId); }
@@ -84,4 +89,27 @@ public class BizProjectAssignServiceImpl implements IBizProjectAssignService
@Override
public int deleteByProjectId(Long projectId) { return bizProjectAssignMapper.deleteByProjectId(projectId); }
/**
* 校验多个执行方总金额不超过项目总金额 (服务端二次校验, 防前端绕过)
* 超额抛 ServiceException, 由 axios 拦截器统一弹 ElMessage.error
*/
@Override
public void validateSum(Long projectId, List<BizProjectAssign> assigns) {
if (assigns == null || assigns.isEmpty()) return;
BigDecimal sum = BigDecimal.ZERO;
for (BizProjectAssign a : assigns) {
if (a.getAmount() != null) sum = sum.add(a.getAmount());
}
if (sum.signum() <= 0) return;
BizProject p = bizProjectMapper.selectByPrimaryKey(projectId);
if (p == null) throw new ServiceException("项目 " + projectId + " 不存在");
BigDecimal total = p.getTotalAmount();
if (total == null || total.signum() <= 0) return;
if (sum.compareTo(total) > 0) {
throw new ServiceException(
"多个执行方的总金额(" + sum.toPlainString() + ")超过项目总金额(" + total.toPlainString() + ")"
);
}
}
}