package com.yunhai.cms.service.impl; import com.yunhai.cms.entity.CmsLink; import com.yunhai.cms.entity.CmsSite; import com.yunhai.cms.event.SiteContentChangedEvent; import com.yunhai.cms.model.dto.DeleteRequest; import com.yunhai.cms.model.dto.LinkSaveRequest; import com.yunhai.cms.model.vo.LinkVo; import com.yunhai.cms.repository.CmsLinkRepository; import com.yunhai.platform.apistd.BizException; import com.yunhai.platform.utils.exception.ErrorCode; import com.yunhai.platform.utils.model.BoaUser; import lombok.RequiredArgsConstructor; import org.springframework.context.ApplicationEventPublisher; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.StringUtils; import java.util.ArrayList; import java.util.List; /** * 友情链接管理(方案 §9.1 友情链接管理)。 */ @Service @RequiredArgsConstructor public class LinkService { private final CmsLinkRepository linkRepository; private final SiteService siteService; private final ApplicationEventPublisher eventPublisher; private final AttachmentService attachmentService; public List list(BoaUser user, Integer siteId) { siteService.requireManageable(user, siteId); List links = linkRepository.findBySiteIdOrderByGroupNameAscSortNoAscIdAsc(siteId); List result = new ArrayList<>(); for (CmsLink l : links) { result.add(LinkVo.of(l)); } return result; } @Transactional public LinkVo save(BoaUser user, LinkSaveRequest req) { CmsSite site = siteService.requireManageable(user, req.getSiteId()); if (!StringUtils.hasText(req.getLinkUrl()) || !(req.getLinkUrl().startsWith("http://") || req.getLinkUrl().startsWith("https://") || req.getLinkUrl().startsWith("/"))) { throw new BizException(ErrorCode.PARAM_ERROR, "链接地址必须以 http(s):// 或 / 开头"); } CmsLink link; if (req.getId() == null) { link = new CmsLink(); link.setSiteId(site.getId()); } else { link = linkRepository.findById(req.getId()) .orElseThrow(() -> new BizException(ErrorCode.PARAM_ERROR, "友链不存在")); if (!link.getSiteId().equals(site.getId())) { throw new BizException(ErrorCode.FORBIDDEN, "无权修改该友链"); } } link.setGroupName(StringUtils.hasText(req.getGroupName()) ? req.getGroupName() : "default"); link.setName(req.getName()); link.setLogoUrl(req.getLogoUrl()); link.setLinkUrl(req.getLinkUrl()); link.setSortNo(req.getSortNo() == null ? 0 : req.getSortNo()); link.setStatus(req.getStatus() == null ? 1 : req.getStatus()); link = linkRepository.save(link); // B2 附件引用:先解引用,logoUrl 非空才回写(编辑换图/清空 logo 旧图均回未使用);幂等 attachmentService.release("link", link.getId()); if (StringUtils.hasText(req.getLogoUrl())) { attachmentService.markUsed(site.getId(), req.getLogoUrl(), "link", link.getId()); } eventPublisher.publishEvent(new SiteContentChangedEvent(site.getId(), "link")); return LinkVo.of(link); } @Transactional public void delete(BoaUser user, DeleteRequest req) { CmsSite site = siteService.requireManageable(user, req.getSiteId()); CmsLink link = linkRepository.findById(req.getId()) .orElseThrow(() -> new BizException(ErrorCode.PARAM_ERROR, "友链不存在")); if (!link.getSiteId().equals(site.getId())) { throw new BizException(ErrorCode.FORBIDDEN, "无权删除该友链"); } linkRepository.delete(link); attachmentService.release("link", link.getId()); eventPublisher.publishEvent(new SiteContentChangedEvent(site.getId(), "link")); } }