diff --git a/ruoyi-oa/src/main/java/com/ruoyi/oa/domain/vo/SysOaContractVo.java b/ruoyi-oa/src/main/java/com/ruoyi/oa/domain/vo/SysOaContractVo.java index 264d6f1..5da0d70 100644 --- a/ruoyi-oa/src/main/java/com/ruoyi/oa/domain/vo/SysOaContractVo.java +++ b/ruoyi-oa/src/main/java/com/ruoyi/oa/domain/vo/SysOaContractVo.java @@ -138,4 +138,9 @@ public class SysOaContractVo { @ExcelProperty(value = "备注") private String remark; + /** 审批状态:null=未提交审批,0=待审,1=通过,2=驳回,3=撤回 */ + private Integer approvalStatus; + /** 审批单 id(最新一次) */ + private Long approvalInstanceId; + } diff --git a/ruoyi-oa/src/main/java/com/ruoyi/oa/service/impl/OaRequirementsServiceImpl.java b/ruoyi-oa/src/main/java/com/ruoyi/oa/service/impl/OaRequirementsServiceImpl.java index 1967d06..8455c21 100644 --- a/ruoyi-oa/src/main/java/com/ruoyi/oa/service/impl/OaRequirementsServiceImpl.java +++ b/ruoyi-oa/src/main/java/com/ruoyi/oa/service/impl/OaRequirementsServiceImpl.java @@ -211,14 +211,15 @@ public class OaRequirementsServiceImpl implements IOaRequirementsService { public Boolean updateByBo(OaRequirementsBo bo) { OaRequirements update = BeanUtil.toBean(bo, OaRequirements.class); validEntityBeforeSave(update); - // 推进到采购中/完成 前必须审批通过 + // 推进到采购中/完成 前需审批通过; + // 若审批未启用/未配置 (无实例),则放行,避免「停用审批反而卡住业务」。 if (bo.getStatus() != null && (bo.getStatus() == 1 || bo.getStatus() == 2)) { OaApprovalInstance i = approvalInstanceMapper.selectOne( Wrappers.lambdaQuery() .eq(OaApprovalInstance::getBusinessType, BIZ_TYPE) .eq(OaApprovalInstance::getBusinessId, bo.getRequirementId()) .orderByDesc(OaApprovalInstance::getId).last("LIMIT 1")); - if (i == null || i.getStatus() == null || i.getStatus() != 1) { + if (i != null && (i.getStatus() == null || i.getStatus() != 1)) { throw new com.ruoyi.common.exception.ServiceException("该采购需求尚未审批通过,无法推进状态"); } } diff --git a/ruoyi-oa/src/main/java/com/ruoyi/oa/service/impl/SysOaContractServiceImpl.java b/ruoyi-oa/src/main/java/com/ruoyi/oa/service/impl/SysOaContractServiceImpl.java index a412e5f..ab93950 100644 --- a/ruoyi-oa/src/main/java/com/ruoyi/oa/service/impl/SysOaContractServiceImpl.java +++ b/ruoyi-oa/src/main/java/com/ruoyi/oa/service/impl/SysOaContractServiceImpl.java @@ -16,12 +16,19 @@ import org.springframework.stereotype.Service; import com.ruoyi.oa.domain.bo.SysOaContractBo; import com.ruoyi.oa.domain.vo.SysOaContractVo; import com.ruoyi.oa.domain.SysOaContract; +import com.ruoyi.oa.domain.OaApprovalInstance; import com.ruoyi.oa.mapper.SysOaContractMapper; +import com.ruoyi.oa.mapper.OaApprovalInstanceMapper; import com.ruoyi.oa.service.ISysOaContractService; +import com.ruoyi.oa.service.IOaApprovalService; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Collection; +import java.util.stream.Collectors; /** * 合同管理Service业务层处理 @@ -33,16 +40,26 @@ import java.util.Collection; @Service public class SysOaContractServiceImpl implements ISysOaContractService { + public static final String BIZ_TYPE = "contract"; + @Autowired private SysOaContractMapper contractMapper; + @Autowired + private IOaApprovalService approvalService; + + @Autowired + private OaApprovalInstanceMapper approvalInstanceMapper; + /** * 查询合同管理 */ @Override public SysOaContractVo queryById(Long contractId){ - return contractMapper.selectVoById(contractId); + SysOaContractVo vo = contractMapper.selectVoById(contractId); + enrichApproval(Collections.singletonList(vo)); + return vo; } /** @@ -52,6 +69,7 @@ public class SysOaContractServiceImpl implements ISysOaContractService { public TableDataInfo queryPageList(SysOaContractBo bo, PageQuery pageQuery) { LambdaQueryWrapper lqw = buildQueryWrapper(bo); Page result = contractMapper.selectVoPage(pageQuery.build(), lqw); + enrichApproval(result.getRecords()); return TableDataInfo.build(result); } @@ -61,7 +79,32 @@ public class SysOaContractServiceImpl implements ISysOaContractService { @Override public List queryList(SysOaContractBo bo) { LambdaQueryWrapper lqw = buildQueryWrapper(bo); - return contractMapper.selectVoList(lqw); + List list = contractMapper.selectVoList(lqw); + enrichApproval(list); + return list; + } + + /** 给 VO 列表注入审批状态(最新一条实例) */ + private void enrichApproval(List list) { + if (list == null || list.isEmpty()) return; + List ids = list.stream().filter(v -> v != null && v.getContractId() != null) + .map(SysOaContractVo::getContractId).collect(Collectors.toList()); + if (ids.isEmpty()) return; + List insts = approvalInstanceMapper.selectList( + Wrappers.lambdaQuery() + .eq(OaApprovalInstance::getBusinessType, BIZ_TYPE) + .in(OaApprovalInstance::getBusinessId, ids) + .orderByAsc(OaApprovalInstance::getId)); + Map latest = new HashMap<>(); + for (OaApprovalInstance i : insts) latest.put(i.getBusinessId(), i); + for (SysOaContractVo v : list) { + if (v == null) continue; + OaApprovalInstance i = latest.get(v.getContractId()); + if (i != null) { + v.setApprovalStatus(i.getStatus()); + v.setApprovalInstanceId(i.getId()); + } + } } /** @@ -119,6 +162,8 @@ public class SysOaContractServiceImpl implements ISysOaContractService { boolean flag = contractMapper.insert(add) > 0; if (flag) { bo.setContractId(add.getContractId()); + // 触发审批流(依赖 oa_approval_config 里 contract 配置;未配置/未启用会跳过) + approvalService.submit(BIZ_TYPE, add.getContractId(), add.getContractName()); } return flag; } @@ -146,6 +191,17 @@ public class SysOaContractServiceImpl implements ISysOaContractService { public Boolean updateByBo(SysOaContractBo bo) { SysOaContract update = BeanUtil.toBean(bo, SysOaContract.class); validEntityBeforeSave(update); + // 标记「完结」(1) 前必须审批通过;若审批未启用/未配置 (无实例),放行。 + if (bo.getContractStatus() != null && "1".equals(bo.getContractStatus())) { + OaApprovalInstance i = approvalInstanceMapper.selectOne( + Wrappers.lambdaQuery() + .eq(OaApprovalInstance::getBusinessType, BIZ_TYPE) + .eq(OaApprovalInstance::getBusinessId, bo.getContractId()) + .orderByDesc(OaApprovalInstance::getId).last("LIMIT 1")); + if (i != null && (i.getStatus() == null || i.getStatus() != 1)) { + throw new com.ruoyi.common.exception.ServiceException("该合同尚未审批通过,无法标记为完结"); + } + } return contractMapper.updateById(update) > 0; } diff --git a/ruoyi-ui/src/api/oa/approval.js b/ruoyi-ui/src/api/oa/approval.js new file mode 100644 index 0000000..fcf5d0d --- /dev/null +++ b/ruoyi-ui/src/api/oa/approval.js @@ -0,0 +1,43 @@ +import request from '@/utils/request' + +// ===== 审批配置 ===== +export function listApprovalConfig() { + return request({ url: '/oa/approval/config/list', method: 'get' }) +} +export function getApprovalConfig(businessType) { + return request({ url: '/oa/approval/config/' + businessType, method: 'get' }) +} +export function saveApprovalConfig(data) { + return request({ url: '/oa/approval/config', method: 'post', data }) +} +export function delApprovalConfig(id) { + return request({ url: '/oa/approval/config/' + id, method: 'delete' }) +} + +// ===== 我的审批 ===== +export function listMyPending(query) { + return request({ url: '/oa/approval/mine/pending', method: 'get', params: query }) +} +export function listMyDone(query) { + return request({ url: '/oa/approval/mine/done', method: 'get', params: query }) +} +export function listMySubmitted(query) { + return request({ url: '/oa/approval/mine/submitted', method: 'get', params: query }) +} +export function listAllApproval(query) { + return request({ url: '/oa/approval/list', method: 'get', params: query }) +} + +// ===== 操作 ===== +export function actApproval(data) { + return request({ url: '/oa/approval/act', method: 'post', data }) +} +export function withdrawApproval(instanceId) { + return request({ url: '/oa/approval/withdraw/' + instanceId, method: 'post' }) +} +export function getApprovalDetail(instanceId) { + return request({ url: '/oa/approval/detail/' + instanceId, method: 'get' }) +} +export function getLatestApproval(businessType, businessId) { + return request({ url: '/oa/approval/latest', method: 'get', params: { businessType, businessId } }) +} diff --git a/ruoyi-ui/src/layout/components/Navbar.vue b/ruoyi-ui/src/layout/components/Navbar.vue index 037417d..a09f5a9 100644 --- a/ruoyi-ui/src/layout/components/Navbar.vue +++ b/ruoyi-ui/src/layout/components/Navbar.vue @@ -3,8 +3,8 @@ - +
+ + + + + +