| | |
| | | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| | | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; |
| | | import cn.iocoder.yudao.framework.operatelog.core.util.OperateLogUtils; |
| | | import cn.iocoder.yudao.module.srm.controller.admin.supplier.vo.SrmSupplierImportExcelVO; |
| | | import cn.iocoder.yudao.module.srm.controller.admin.supplier.vo.SrmSupplierImportRespVO; |
| | | import cn.iocoder.yudao.module.srm.controller.admin.supplier.vo.SrmSupplierPageReqVO; |
| | | import cn.iocoder.yudao.module.srm.controller.admin.supplier.vo.SrmSupplierSaveReqVO; |
| | | import cn.iocoder.yudao.module.srm.dal.dataobject.*; |
| | | import cn.iocoder.yudao.module.srm.dal.mysql.SrmBidAwardMapper; |
| | | import cn.iocoder.yudao.module.srm.dal.mysql.SrmBidEvaluationMapper; |
| | | import cn.iocoder.yudao.module.srm.dal.mysql.SrmSupplierApplyMapper; |
| | | import cn.iocoder.yudao.module.srm.controller.admin.supplier.vo.*; |
| | | import cn.iocoder.yudao.module.srm.dal.dataobject.SrmSupplierBankDO; |
| | | import cn.iocoder.yudao.module.srm.dal.dataobject.SrmSupplierContactDO; |
| | | import cn.iocoder.yudao.module.srm.dal.dataobject.SrmSupplierDO; |
| | | import cn.iocoder.yudao.module.erp.api.purchase.ErpSupplierReferenceApi; |
| | | import cn.iocoder.yudao.module.srm.dal.mysql.SrmBidBondMapper; |
| | | import cn.iocoder.yudao.module.srm.dal.mysql.SrmInquiryOrderMapper; |
| | | import cn.iocoder.yudao.module.srm.dal.mysql.SrmInquiryQuoteMapper; |
| | | import cn.iocoder.yudao.module.srm.dal.mysql.SrmInquirySupplierMapper; |
| | | import cn.iocoder.yudao.module.srm.dal.mysql.SrmSupplierBankMapper; |
| | | import cn.iocoder.yudao.module.srm.dal.mysql.SrmSupplierCategoryMapper; |
| | | import cn.iocoder.yudao.module.srm.dal.mysql.SrmSupplierContactMapper; |
| | | import cn.iocoder.yudao.module.srm.dal.mysql.SrmSupplierCertificateMapper; |
| | | import cn.iocoder.yudao.module.srm.dal.mysql.SrmSupplierMapper; |
| | | import cn.iocoder.yudao.module.srm.dal.mysql.SrmTenderBidMapper; |
| | | import cn.iocoder.yudao.module.srm.enums.ErrorCodeConstants; |
| | | import cn.iocoder.yudao.module.srm.enums.SupplierTypeEnum; |
| | | import com.mzt.logapi.starter.annotation.LogRecord; |
| | | import jakarta.annotation.Resource; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.validation.annotation.Validated; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | import org.springframework.validation.annotation.Validated; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.util.ArrayList; |
| | |
| | | import java.util.Collections; |
| | | import java.util.LinkedHashMap; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.Objects; |
| | | |
| | | import static cn.iocoder.yudao.module.srm.enums.LogRecordConstants.*; |
| | | |
| | |
| | | private SrmSupplierContactMapper supplierContactMapper; |
| | | |
| | | @Resource |
| | | private SrmSupplierBankMapper supplierBankMapper; |
| | | |
| | | @Resource |
| | | private SrmSupplierCategoryMapper supplierCategoryMapper; |
| | | |
| | | @Resource |
| | | private SrmSupplierApplyMapper supplierApplyMapper; |
| | | private SrmInquirySupplierMapper inquirySupplierMapper; |
| | | |
| | | @Resource |
| | | private SrmSupplierCertificateMapper supplierCertificateMapper; |
| | | private SrmInquiryQuoteMapper inquiryQuoteMapper; |
| | | |
| | | @Resource |
| | | private SrmTenderBidMapper tenderBidMapper; |
| | | private SrmInquiryOrderMapper inquiryOrderMapper; |
| | | |
| | | @Resource |
| | | private SrmBidEvaluationMapper bidEvaluationMapper; |
| | | private SrmBidBondMapper bidBondMapper; |
| | | |
| | | @Resource |
| | | private SrmBidAwardMapper bidAwardMapper; |
| | | private ErpSupplierReferenceApi erpSupplierReferenceApi; |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public Long createSupplier(SrmSupplierSaveReqVO createReqVO) { |
| | | // 校验供应商编码唯一性 |
| | | validateCodeUnique(createReqVO.getCode(), null); |
| | | // 插入 |
| | | SrmSupplierDO supplier = BeanUtils.toBean(createReqVO, SrmSupplierDO.class); |
| | | fillDefaultContactAndBankFromFlatFields(createReqVO); |
| | | supplierMapper.insert(supplier); |
| | | saveContacts(supplier.getId(), createReqVO.getContacts()); |
| | | saveBanks(supplier.getId(), createReqVO.getBanks()); |
| | | syncSupplierDefaultFields(supplier.getId(), createReqVO.getContacts(), createReqVO.getBanks()); |
| | | return supplier.getId(); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void updateSupplier(SrmSupplierSaveReqVO updateReqVO) { |
| | | // 校验存在性 |
| | | validateSupplierExists(updateReqVO.getId()); |
| | |
| | | // 更新 |
| | | SrmSupplierDO updateObj = BeanUtils.toBean(updateReqVO, SrmSupplierDO.class); |
| | | supplierMapper.updateById(updateObj); |
| | | // 列表传 null 表示不修改;传空列表表示清空 |
| | | if (updateReqVO.getContacts() != null) { |
| | | saveContacts(updateReqVO.getId(), updateReqVO.getContacts()); |
| | | } |
| | | if (updateReqVO.getBanks() != null) { |
| | | saveBanks(updateReqVO.getId(), updateReqVO.getBanks()); |
| | | } |
| | | syncSupplierDefaultFields(updateReqVO.getId(), updateReqVO.getContacts(), updateReqVO.getBanks()); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void deleteSupplier(Long id) { |
| | | // 校验存在性 |
| | | validateSupplierExists(id); |
| | | // 关联数据存在时,不允许删除 |
| | | validateNoReference(id); |
| | | SrmSupplierDO supplier = validateSupplierExists(id); |
| | | // 校验业务引用:询价、保证金、ERP 采购/付款等单据 |
| | | List<String> references = new ArrayList<>(); |
| | | if (inquirySupplierMapper.selectCountBySupplierId(id) > 0) { |
| | | references.add("SRM询价单"); |
| | | } |
| | | if (inquiryQuoteMapper.selectCountBySupplierId(id) > 0) { |
| | | references.add("SRM询价报价"); |
| | | } |
| | | if (inquiryOrderMapper.selectCountBySupplierId(id) > 0) { |
| | | references.add("SRM询价转采购订单记录"); |
| | | } |
| | | if (bidBondMapper.selectCountBySupplierId(id) > 0) { |
| | | references.add("SRM投标保证金"); |
| | | } |
| | | List<String> erpSources = erpSupplierReferenceApi.getSupplierReferenceSources(id).getCheckedData(); |
| | | if (CollUtil.isNotEmpty(erpSources)) { |
| | | references.addAll(erpSources); |
| | | } |
| | | if (!references.isEmpty()) { |
| | | throw new ServiceException(ErrorCodeConstants.SUPPLIER_EXISTS_REFERENCE.getCode(), |
| | | "供应商【" + supplier.getName() + "】已被业务引用(" + String.join("、", references) |
| | | + "),无法删除。请先处理相关单据,或将供应商状态改为停用。"); |
| | | } |
| | | // 可直接清理的关联数据 |
| | | supplierContactMapper.deleteBySupplierId(id); |
| | | supplierBankMapper.deleteBySupplierId(id); |
| | | supplierCategoryMapper.deleteBySupplierId(id); |
| | | // 逻辑删除 |
| | | supplierMapper.deleteById(id); |
| | |
| | | @Override |
| | | public SrmSupplierDO getSupplier(Long id) { |
| | | return supplierMapper.selectById(id); |
| | | } |
| | | |
| | | @Override |
| | | public List<SrmSupplierContactDO> getSupplierContactList(Long supplierId) { |
| | | return supplierContactMapper.selectListBySupplierId(supplierId); |
| | | } |
| | | |
| | | @Override |
| | | public List<SrmSupplierBankDO> getSupplierBankList(Long supplierId) { |
| | | return supplierBankMapper.selectListBySupplierId(supplierId); |
| | | } |
| | | |
| | | @Override |
| | |
| | | |
| | | @Override |
| | | public List<SrmSupplierDO> getSupplierSimpleList() { |
| | | return supplierMapper.selectApprovedList(); |
| | | // 准入功能已移除,下拉选择统一返回启用的供应商 |
| | | return supplierMapper.selectListByStatus(CommonStatusEnum.ENABLE.getStatus()); |
| | | } |
| | | |
| | | @Override |
| | | public List<SrmSupplierDO> getSupplierAllList() { |
| | | return supplierMapper.selectList(new LambdaQueryWrapperX<SrmSupplierDO>() |
| | | // 过滤停用供应商,保留未设置状态与启用状态的档案供应商(仅用于招投标场景) |
| | | // 过滤停用供应商,保留未设置状态与启用状态的档案供应商 |
| | | .and(w -> w.isNull(SrmSupplierDO::getStatus) |
| | | .or() |
| | | .ne(SrmSupplierDO::getStatus, CommonStatusEnum.DISABLE.getStatus())) |
| | |
| | | return Collections.emptyList(); |
| | | } |
| | | return supplierMapper.selectBatchIds(ids); |
| | | } |
| | | |
| | | // ========== 联系人 / 银行账户 ========== |
| | | |
| | | /** |
| | | * 导入场景没有联系人列表时,用主表扁平字段生成一条默认联系人 |
| | | */ |
| | | private void fillDefaultContactAndBankFromFlatFields(SrmSupplierSaveReqVO reqVO) { |
| | | if (reqVO.getContacts() == null && StrUtil.isNotBlank(reqVO.getContactName())) { |
| | | SrmSupplierContactSaveReqVO contact = new SrmSupplierContactSaveReqVO(); |
| | | contact.setName(reqVO.getContactName()); |
| | | contact.setMobile(reqVO.getContactMobile()); |
| | | contact.setEmail(reqVO.getContactEmail()); |
| | | contact.setIsDefault(Boolean.TRUE); |
| | | reqVO.setContacts(Collections.singletonList(contact)); |
| | | } |
| | | if (reqVO.getBanks() == null |
| | | && (StrUtil.isNotBlank(reqVO.getBankName()) || StrUtil.isNotBlank(reqVO.getBankAccount()))) { |
| | | SrmSupplierBankSaveReqVO bank = new SrmSupplierBankSaveReqVO(); |
| | | bank.setBankName(reqVO.getBankName()); |
| | | bank.setBankAccount(reqVO.getBankAccount()); |
| | | bank.setIsDefault(Boolean.TRUE); |
| | | reqVO.setBanks(Collections.singletonList(bank)); |
| | | } |
| | | } |
| | | |
| | | private void saveContacts(Long supplierId, List<SrmSupplierContactSaveReqVO> contacts) { |
| | | supplierContactMapper.deleteBySupplierId(supplierId); |
| | | if (CollUtil.isEmpty(contacts)) { |
| | | return; |
| | | } |
| | | for (int i = 0; i < contacts.size(); i++) { |
| | | SrmSupplierContactSaveReqVO vo = contacts.get(i); |
| | | if (StrUtil.isBlank(vo.getName())) { |
| | | throw new ServiceException(ErrorCodeConstants.SUPPLIER_CONTACT_NAME_REQUIRED.getCode(), |
| | | "第 " + (i + 1) + " 个联系人的姓名不能为空,请填写联系人姓名后重新保存"); |
| | | } |
| | | } |
| | | int defaultIndex = resolveDefaultIndex(contacts.size(), |
| | | contacts.stream().map(SrmSupplierContactSaveReqVO::getIsDefault).toList()); |
| | | for (int i = 0; i < contacts.size(); i++) { |
| | | SrmSupplierContactDO contact = BeanUtils.toBean(contacts.get(i), SrmSupplierContactDO.class); |
| | | contact.setId(null); |
| | | contact.setSupplierId(supplierId); |
| | | contact.setSort(i); |
| | | contact.setIsDefault(i == defaultIndex); |
| | | supplierContactMapper.insert(contact); |
| | | } |
| | | } |
| | | |
| | | private void saveBanks(Long supplierId, List<SrmSupplierBankSaveReqVO> banks) { |
| | | supplierBankMapper.deleteBySupplierId(supplierId); |
| | | if (CollUtil.isEmpty(banks)) { |
| | | return; |
| | | } |
| | | for (int i = 0; i < banks.size(); i++) { |
| | | SrmSupplierBankSaveReqVO vo = banks.get(i); |
| | | if (StrUtil.isBlank(vo.getBankName()) || StrUtil.isBlank(vo.getBankAccount())) { |
| | | throw new ServiceException(ErrorCodeConstants.SUPPLIER_BANK_INFO_INCOMPLETE.getCode(), |
| | | "第 " + (i + 1) + " 个银行账户的开户行或银行账号为空,请补全开户行和银行账号后重新保存"); |
| | | } |
| | | } |
| | | int defaultIndex = resolveDefaultIndex(banks.size(), |
| | | banks.stream().map(SrmSupplierBankSaveReqVO::getIsDefault).toList()); |
| | | for (int i = 0; i < banks.size(); i++) { |
| | | SrmSupplierBankDO bank = BeanUtils.toBean(banks.get(i), SrmSupplierBankDO.class); |
| | | bank.setId(null); |
| | | bank.setSupplierId(supplierId); |
| | | bank.setSort(i); |
| | | bank.setIsDefault(i == defaultIndex); |
| | | supplierBankMapper.insert(bank); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 解析默认项:优先取标记了默认的第一条,否则取第一条 |
| | | */ |
| | | private int resolveDefaultIndex(int size, List<Boolean> defaults) { |
| | | for (int i = 0; i < size; i++) { |
| | | if (Boolean.TRUE.equals(defaults.get(i))) { |
| | | return i; |
| | | } |
| | | } |
| | | return 0; |
| | | } |
| | | |
| | | /** |
| | | * 把默认联系人/默认银行回写主表扁平字段,兼容列表、搜索与导入导出 |
| | | */ |
| | | private void syncSupplierDefaultFields(Long supplierId, List<SrmSupplierContactSaveReqVO> contacts, |
| | | List<SrmSupplierBankSaveReqVO> banks) { |
| | | SrmSupplierDO updateObj = new SrmSupplierDO(); |
| | | updateObj.setId(supplierId); |
| | | boolean changed = false; |
| | | if (contacts != null) { |
| | | SrmSupplierContactSaveReqVO defaultContact = pickDefault(contacts, SrmSupplierContactSaveReqVO::getIsDefault); |
| | | updateObj.setContactName(defaultContact == null ? null : defaultContact.getName()); |
| | | updateObj.setContactMobile(defaultContact == null ? null : defaultContact.getMobile()); |
| | | updateObj.setContactEmail(defaultContact == null ? null : defaultContact.getEmail()); |
| | | changed = true; |
| | | } |
| | | if (banks != null) { |
| | | SrmSupplierBankSaveReqVO defaultBank = pickDefault(banks, SrmSupplierBankSaveReqVO::getIsDefault); |
| | | updateObj.setBankName(defaultBank == null ? null : defaultBank.getBankName()); |
| | | updateObj.setBankAccount(defaultBank == null ? null : defaultBank.getBankAccount()); |
| | | changed = true; |
| | | } |
| | | if (changed) { |
| | | supplierMapper.updateById(updateObj); |
| | | } |
| | | } |
| | | |
| | | private <T> T pickDefault(List<T> list, java.util.function.Function<T, Boolean> isDefault) { |
| | | if (CollUtil.isEmpty(list)) { |
| | | return null; |
| | | } |
| | | return list.stream().filter(item -> Boolean.TRUE.equals(isDefault.apply(item))) |
| | | .findFirst().orElse(list.get(0)); |
| | | } |
| | | |
| | | // ========== 导入辅助方法 ========== |
| | |
| | | |
| | | // ========== 校验方法 ========== |
| | | |
| | | private void validateSupplierExists(Long id) { |
| | | if (supplierMapper.selectById(id) == null) { |
| | | private SrmSupplierDO validateSupplierExists(Long id) { |
| | | SrmSupplierDO supplier = supplierMapper.selectById(id); |
| | | if (supplier == null) { |
| | | throw ServiceExceptionUtil.exception(ErrorCodeConstants.SUPPLIER_NOT_EXISTS); |
| | | } |
| | | return supplier; |
| | | } |
| | | |
| | | private void validateCodeUnique(String code, Long id) { |
| | | SrmSupplierDO existing = supplierMapper.selectByCode(code); |
| | | if (existing != null && !existing.getId().equals(id)) { |
| | | if (existing != null && !Objects.equals(existing.getId(), id)) { |
| | | throw ServiceExceptionUtil.exception(ErrorCodeConstants.SUPPLIER_CODE_EXISTS); |
| | | } |
| | | } |
| | | |
| | | private void validateNoReference(Long id) { |
| | | boolean hasReference = supplierApplyMapper.existsApprovedBySupplierId(id) |
| | | || supplierCertificateMapper.selectCount(SrmSupplierCertificateDO::getSupplierId, id) > 0 |
| | | || tenderBidMapper.selectCount(SrmTenderBidDO::getSupplierId, id) > 0 |
| | | || bidEvaluationMapper.selectCount(SrmBidEvaluationDO::getSupplierId, id) > 0 |
| | | || bidAwardMapper.selectCount(SrmBidAwardDO::getSupplierId, id) > 0; |
| | | if (hasReference) { |
| | | throw ServiceExceptionUtil.exception(ErrorCodeConstants.SUPPLIER_EXISTS_REFERENCE); |
| | | } |
| | | } |
| | | |