package cn.iocoder.yudao.module.aftersales.service.ticket; import cn.hutool.core.collection.CollUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.aftersales.controller.admin.ticket.vo.ticket.AfterSaleTicketPageReqVO; import cn.iocoder.yudao.module.aftersales.controller.admin.ticket.vo.ticket.AfterSaleTicketSaveReqVO; import cn.iocoder.yudao.module.aftersales.dal.dataobject.ticket.AfterSaleTicketDO; import cn.iocoder.yudao.module.aftersales.dal.dataobject.ticket.AfterSaleTicketItemDO; import cn.iocoder.yudao.module.aftersales.dal.mysql.ticket.AfterSaleTicketItemMapper; import cn.iocoder.yudao.module.aftersales.dal.mysql.ticket.AfterSaleTicketMapper; import cn.iocoder.yudao.module.aftersales.dal.redis.no.AfterSaleNoRedisDAO; import cn.iocoder.yudao.module.aftersales.enums.AfterSaleBizTypeEnum; import cn.iocoder.yudao.module.aftersales.enums.AfterSaleTicketStatusEnum; import cn.iocoder.yudao.module.aftersales.enums.permission.AfterSalePermissionLevelEnum; import cn.iocoder.yudao.module.aftersales.service.permission.AfterSalePermissionService; import cn.iocoder.yudao.module.aftersales.service.permission.bo.AfterSalePermissionCreateReqBO; 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.util.Collection; import java.util.List; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.module.aftersales.enums.ErrorCodeConstants.*; /** * 售后工单 Service 实现类 */ @Service @Validated @Slf4j public class AfterSaleTicketServiceImpl implements AfterSaleTicketService { @Resource private AfterSaleTicketMapper ticketMapper; @Resource private AfterSaleTicketItemMapper ticketItemMapper; @Resource private AfterSaleNoRedisDAO noRedisDAO; @Resource private AfterSalePermissionService permissionService; @Override @Transactional(rollbackFor = Exception.class) public Long createTicket(AfterSaleTicketSaveReqVO createReqVO, Long userId) { // 1. 生成单号 String no = noRedisDAO.generate(AfterSaleNoRedisDAO.TICKET_NO_PREFIX); if (ticketMapper.selectByNo(no) != null) { throw exception(TICKET_NO_EXISTS); } // 2. 插入工单 AfterSaleTicketDO ticket = BeanUtils.toBean(createReqVO, AfterSaleTicketDO.class) .setNo(no) .setStatus(AfterSaleTicketStatusEnum.DRAFT.getStatus()) .setOwnerUserId(userId); ticketMapper.insert(ticket); // 3. 插入明细行 if (CollUtil.isNotEmpty(createReqVO.getItems())) { createReqVO.getItems().forEach(item -> { AfterSaleTicketItemDO itemDO = BeanUtils.toBean(item, AfterSaleTicketItemDO.class); itemDO.setTicketId(ticket.getId()); ticketItemMapper.insert(itemDO); }); } // 4. 创建数据权限(负责人) AfterSalePermissionCreateReqBO permBO = new AfterSalePermissionCreateReqBO(); permBO.setBizType(AfterSaleBizTypeEnum.AFTER_SALE_TICKET.getType()); permBO.setBizId(ticket.getId()); permBO.setUserId(userId); permBO.setLevel(AfterSalePermissionLevelEnum.OWNER.getLevel()); permissionService.createPermission(permBO); return ticket.getId(); } @Override @Transactional(rollbackFor = Exception.class) public void updateTicket(AfterSaleTicketSaveReqVO updateReqVO) { // 1. 校验存在 AfterSaleTicketDO ticket = validateTicketExists(updateReqVO.getId()); validateTicketStatus(ticket, AfterSaleTicketStatusEnum.DRAFT); // 2. 更新工单 AfterSaleTicketDO updateObj = BeanUtils.toBean(updateReqVO, AfterSaleTicketDO.class); ticketMapper.updateById(updateObj); // 3. 更新明细行(先删后增) ticketItemMapper.deleteByTicketId(ticket.getId()); if (CollUtil.isNotEmpty(updateReqVO.getItems())) { updateReqVO.getItems().forEach(item -> { AfterSaleTicketItemDO itemDO = BeanUtils.toBean(item, AfterSaleTicketItemDO.class); itemDO.setTicketId(ticket.getId()); ticketItemMapper.insert(itemDO); }); } } @Override @Transactional(rollbackFor = Exception.class) public void deleteTicket(Long id) { AfterSaleTicketDO ticket = validateTicketExists(id); validateTicketStatus(ticket, AfterSaleTicketStatusEnum.DRAFT, AfterSaleTicketStatusEnum.CANCELED); // 软删除 ticketMapper.deleteById(id); ticketItemMapper.deleteByTicketId(id); permissionService.deletePermission(AfterSaleBizTypeEnum.AFTER_SALE_TICKET.getType(), id); } @Override public AfterSaleTicketDO getTicket(Long id) { return validateTicketExists(id); } @Override public List getTicketList(Collection ids) { if (CollUtil.isEmpty(ids)) { return List.of(); } return ticketMapper.selectBatchIds(ids); } @Override public PageResult getTicketPage(AfterSaleTicketPageReqVO pageReqVO, Long userId) { return ticketMapper.selectPage(pageReqVO, userId, pageReqVO.getSceneType(), pageReqVO.getNo(), pageReqVO.getName(), pageReqVO.getCustomerId(), pageReqVO.getSaleOrderId(), pageReqVO.getStatus(), pageReqVO.getIssueType()); } @Override public List getTicketItemListByTicketId(Long ticketId) { return ticketItemMapper.selectListByTicketId(ticketId); } @Override @Transactional(rollbackFor = Exception.class) public void assignTicket(Long id, Long handlerUserId) { AfterSaleTicketDO ticket = validateTicketExists(id); validateTicketStatus(ticket, AfterSaleTicketStatusEnum.DRAFT, AfterSaleTicketStatusEnum.PROCESSING); ticketMapper.updateById(new AfterSaleTicketDO().setId(id).setHandlerUserId(handlerUserId) .setStatus(AfterSaleTicketStatusEnum.PROCESSING.getStatus())); } @Override @Transactional(rollbackFor = Exception.class) public void classifyTicket(Long id, Integer issueType, String issueCategory, Integer severityLevel) { AfterSaleTicketDO ticket = validateTicketExists(id); validateTicketStatus(ticket, AfterSaleTicketStatusEnum.PROCESSING); ticketMapper.updateById(new AfterSaleTicketDO().setId(id) .setIssueType(issueType).setIssueCategory(issueCategory).setSeverityLevel(severityLevel)); } @Override @Transactional(rollbackFor = Exception.class) public void resolveTicket(Long id) { AfterSaleTicketDO ticket = validateTicketExists(id); validateTicketStatus(ticket, AfterSaleTicketStatusEnum.PROCESSING, AfterSaleTicketStatusEnum.WAITING_RETURN, AfterSaleTicketStatusEnum.WAITING_REFUND); ticketMapper.updateById(new AfterSaleTicketDO().setId(id) .setStatus(AfterSaleTicketStatusEnum.COMPLETED.getStatus()) .setResolvedTime(LocalDateTime.now())); } @Override @Transactional(rollbackFor = Exception.class) public void closeTicket(Long id) { AfterSaleTicketDO ticket = validateTicketExists(id); validateTicketStatus(ticket, AfterSaleTicketStatusEnum.COMPLETED); ticketMapper.updateById(new AfterSaleTicketDO().setId(id) .setStatus(AfterSaleTicketStatusEnum.CLOSED.getStatus()) .setClosedTime(LocalDateTime.now())); } @Override @Transactional(rollbackFor = Exception.class) public void cancelTicket(Long id) { AfterSaleTicketDO ticket = validateTicketExists(id); validateTicketStatus(ticket, AfterSaleTicketStatusEnum.DRAFT, AfterSaleTicketStatusEnum.PROCESSING); ticketMapper.updateById(new AfterSaleTicketDO().setId(id) .setStatus(AfterSaleTicketStatusEnum.CANCELED.getStatus())); } // ========== 内部方法 ========== private AfterSaleTicketDO validateTicketExists(Long id) { AfterSaleTicketDO ticket = ticketMapper.selectById(id); if (ticket == null) { throw exception(TICKET_NOT_EXISTS); } return ticket; } private void validateTicketStatus(AfterSaleTicketDO ticket, AfterSaleTicketStatusEnum... allowedStatuses) { for (AfterSaleTicketStatusEnum status : allowedStatuses) { if (status.getStatus().equals(ticket.getStatus())) { return; } } throw exception(TICKET_STATUS_ERROR); } }