package cn.iocoder.yudao.module.research.service.guideline; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.research.controller.admin.guideline.vo.ResearchGuidelinePageReqVO; import cn.iocoder.yudao.module.research.controller.admin.guideline.vo.ResearchGuidelineSaveReqVO; import cn.iocoder.yudao.module.research.dal.dataobject.guideline.ResearchGuidelineDO; import cn.iocoder.yudao.module.research.dal.mysql.application.ResearchApplicationMapper; import cn.iocoder.yudao.module.research.dal.mysql.guideline.ResearchGuidelineMapper; import cn.iocoder.yudao.module.research.dal.redis.ResearchNoRedisDAO; import cn.iocoder.yudao.module.research.enums.ResearchGuidelineStatusEnum; import cn.iocoder.yudao.module.system.api.storage.StorageAttachmentApi; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.Collection; import java.util.List; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; import static cn.iocoder.yudao.module.research.enums.ErrorCodeConstants.*; @Service @Validated @Slf4j public class ResearchGuidelineServiceImpl implements ResearchGuidelineService { public static final String GUIDELINE_RECORD_TYPE = "research_guideline"; private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern(FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND); @Resource private ResearchGuidelineMapper guidelineMapper; @Resource private ResearchApplicationMapper applicationMapper; @Resource private ResearchNoRedisDAO noRedisDAO; @Resource private StorageAttachmentApi storageAttachmentApi; @Override @Transactional(rollbackFor = Exception.class) public Long createGuideline(ResearchGuidelineSaveReqVO createReqVO) { validateApplyTime(createReqVO); String no = noRedisDAO.generate(ResearchNoRedisDAO.GUIDELINE_NO_PREFIX); if (guidelineMapper.selectByNo(no) != null) { throw exception(GUIDELINE_NO_EXISTS); } ResearchGuidelineDO guideline = BeanUtils.toBean(createReqVO, ResearchGuidelineDO.class, in -> in .setNo(no) .setPublishStatus(ResearchGuidelineStatusEnum.DRAFT.getStatus())); guidelineMapper.insert(guideline); storageAttachmentApi.bindAttachments("file", GUIDELINE_RECORD_TYPE, guideline.getId(), createReqVO.getBlobIds()); return guideline.getId(); } @Override @Transactional(rollbackFor = Exception.class) public void updateGuideline(ResearchGuidelineSaveReqVO updateReqVO) { ResearchGuidelineDO guideline = validateGuidelineExists(updateReqVO.getId()); if (!ResearchGuidelineStatusEnum.DRAFT.getStatus().equals(guideline.getPublishStatus())) { throw exception(GUIDELINE_UPDATE_FAIL_NOT_DRAFT, guideline.getNo()); } validateApplyTime(updateReqVO); ResearchGuidelineDO updateObj = BeanUtils.toBean(updateReqVO, ResearchGuidelineDO.class); guidelineMapper.updateById(updateObj); storageAttachmentApi.updateAttachments("file", GUIDELINE_RECORD_TYPE, updateReqVO.getId(), updateReqVO.getBlobIds()); } @Override @Transactional(rollbackFor = Exception.class) public void deleteGuideline(Long id) { ResearchGuidelineDO guideline = validateGuidelineExists(id); if (!ResearchGuidelineStatusEnum.DRAFT.getStatus().equals(guideline.getPublishStatus())) { throw exception(GUIDELINE_DELETE_FAIL_NOT_DRAFT, guideline.getNo()); } // 已产生申报单时禁止删除,避免申报单悬挂在已删除指南上 Long applicationCount = applicationMapper.selectCountByGuidelineId(id); if (applicationCount != null && applicationCount > 0) { throw exception(GUIDELINE_HAS_APPLICATION, guideline.getNo(), applicationCount); } guidelineMapper.deleteById(id); storageAttachmentApi.deleteAttachmentsByRecord(GUIDELINE_RECORD_TYPE, id); } @Override public ResearchGuidelineDO getGuideline(Long id) { return guidelineMapper.selectById(id); } @Override public PageResult getGuidelinePage(ResearchGuidelinePageReqVO pageReqVO) { return guidelineMapper.selectPage(pageReqVO); } @Override public List getPublishedGuidelineList() { return guidelineMapper.selectPublishedList(); } @Override public List getGuidelineList(Collection ids) { return guidelineMapper.selectListByIds(ids); } @Override @Transactional(rollbackFor = Exception.class) public void publishGuideline(Long id) { ResearchGuidelineDO guideline = validateGuidelineExists(id); if (!ResearchGuidelineStatusEnum.DRAFT.getStatus().equals(guideline.getPublishStatus())) { throw exception(GUIDELINE_PUBLISH_FAIL_STATUS); } guidelineMapper.updateById(new ResearchGuidelineDO() .setId(id) .setPublishStatus(ResearchGuidelineStatusEnum.PUBLISHED.getStatus()) .setPublishTime(LocalDateTime.now())); } @Override @Transactional(rollbackFor = Exception.class) public void closeGuideline(Long id) { ResearchGuidelineDO guideline = validateGuidelineExists(id); if (!ResearchGuidelineStatusEnum.PUBLISHED.getStatus().equals(guideline.getPublishStatus())) { throw exception(GUIDELINE_CLOSE_FAIL_STATUS); } guidelineMapper.updateById(new ResearchGuidelineDO() .setId(id) .setPublishStatus(ResearchGuidelineStatusEnum.CLOSED.getStatus())); } @Override public ResearchGuidelineDO validateGuidelineSubmittable(Long guidelineId) { ResearchGuidelineDO guideline = guidelineMapper.selectById(guidelineId); if (guideline == null) { throw exception(GUIDELINE_NOT_EXISTS); } // 逐维度校验,提示里给出「指南名 + 实际状态/时间 + 下一步动作」 if (!ResearchGuidelineStatusEnum.PUBLISHED.getStatus().equals(guideline.getPublishStatus())) { throw exception(APPLICATION_SUBMIT_FAIL_GUIDELINE_STATUS, guideline.getTitle(), ResearchGuidelineStatusEnum.nameOf(guideline.getPublishStatus())); } LocalDateTime now = LocalDateTime.now(); boolean beforeStart = guideline.getApplyStartTime() != null && now.isBefore(guideline.getApplyStartTime()); boolean afterEnd = guideline.getApplyEndTime() != null && now.isAfter(guideline.getApplyEndTime()); if (beforeStart || afterEnd) { throw exception(APPLICATION_SUBMIT_FAIL_NOT_IN_PERIOD, format(now), guideline.getTitle(), format(guideline.getApplyStartTime()), format(guideline.getApplyEndTime())); } return guideline; } // =================== 内部方法 =================== private ResearchGuidelineDO validateGuidelineExists(Long id) { ResearchGuidelineDO guideline = guidelineMapper.selectById(id); if (guideline == null) { throw exception(GUIDELINE_NOT_EXISTS); } return guideline; } private void validateApplyTime(ResearchGuidelineSaveReqVO reqVO) { if (reqVO.getApplyStartTime() != null && reqVO.getApplyEndTime() != null && reqVO.getApplyEndTime().isBefore(reqVO.getApplyStartTime())) { throw exception(GUIDELINE_APPLY_TIME_INVALID, format(reqVO.getApplyEndTime()), format(reqVO.getApplyStartTime())); } } private static String format(LocalDateTime time) { return time == null ? "未设置" : time.format(DATE_TIME_FORMATTER); } }