feat: 工单添加解决方案逻辑

This commit is contained in:
曹鹏飞 2025-03-19 19:04:39 +08:00
parent 13bfc612bf
commit eed33c0ef1
44 changed files with 1100 additions and 77 deletions

View File

@ -99,6 +99,12 @@ public class TicketController extends ControllerBase {
@Resource
private ITBasePartService partService;
@Resource
private ITicketSolutionService ticketSolutionService;
@Resource
private ITicketSolutionAuditService ticketSolutionAuditService;
/**
* 获取问题类型
* @return 问题类型列表
@ -276,6 +282,19 @@ public class TicketController extends ControllerBase {
.setExperienceEvaluation(vo.getExperienceEvaluation());
}
/**
* 获取工单根本原因分析
* @param ticketId 工单id
* @return 根本原因分析
*/
@GetMapping("getReason")
@ApiMark(moduleName = "工单管理", apiName = "获取工单根本原因分析",isPublic = true)
public ApiResult<String> getReason(@Valid @RequestParam @NotNull Integer ticketId){
Ticket ticket=ticketService.getById(ticketId);
VUtils.trueThrowBusinessError(Objects.isNull(ticket)).throwMessage("工单不存在");
return ApiResult.success(ticket.getReason());
}
/**
* 导出搜索工单
* @param request 请求参数
@ -399,30 +418,30 @@ public class TicketController extends ControllerBase {
return ApiResult.success();
}
/**
* 关闭工单
* @param request 请求参数
*/
@PostMapping("closeTicket")
@MethodInfoMark(value = "关闭工单", menuName = "工单管理")
@ApiMark(moduleName = "工单管理", apiName = "关闭工单")
public ApiResult<Void> closeTicket(@Valid @RequestBody TicketCloseRequest request) {
Ticket ticket=ticketService.closeTicket(request);
ticketEventPublisher.publishTicketCloseEvent(ticket);
ChatMessageDTO message = new ChatMessageDTO()
.setId(cn.hutool.core.util.IdUtil.getSnowflakeNextIdStr())
.setFrom("system")
.setTicketState(ticket.getState())
.setSenderId(0)
.setSenderName("服务助手")
.setContent("<b>工单已关闭</b><br/>感谢你的使用,如有问题,请重新提交新的工单。")
.setCreateTime(Instant.now());
ticketChatService.addMessage(request.getTicketId(), message);
//推送消息
ssePushService.sendTicketMessageToAdmin(ticket.getId(),message);
ssePushService.sendTicketMessageToApp(ticket.getId(),message);
return ApiResult.success();
}
// /**
// * 关闭工单
// * @param request 请求参数
// */
// @PostMapping("closeTicket")
// @MethodInfoMark(value = "关闭工单", menuName = "工单管理")
// @ApiMark(moduleName = "工单管理", apiName = "关闭工单")
// public ApiResult<Void> closeTicket(@Valid @RequestBody TicketCloseRequest request) {
// Ticket ticket=ticketService.closeTicket(request);
// ticketEventPublisher.publishTicketCloseEvent(ticket);
// ChatMessageDTO message = new ChatMessageDTO()
// .setId(cn.hutool.core.util.IdUtil.getSnowflakeNextIdStr())
// .setFrom("system")
// .setTicketState(ticket.getState())
// .setSenderId(0)
// .setSenderName("服务助手")
// .setContent("<b>工单已关闭</b><br/>感谢你的使用,如有问题,请重新提交新的工单。")
// .setCreateTime(Instant.now());
// ticketChatService.addMessage(request.getTicketId(), message);
// //推送消息
// ssePushService.sendTicketMessageToAdmin(ticket.getId(),message);
// ssePushService.sendTicketMessageToApp(ticket.getId(),message);
// return ApiResult.success();
// }
/**
* 导出工单为pdf
@ -490,7 +509,7 @@ public class TicketController extends ControllerBase {
.setUrgency(ticket.getUrgency())
.setUpdateTime(DateTimeUtil.format(ticket.getUpdateTime()))
.setHandleUserName(handle)
.setSolution(ticket.getSolution())
.setSolution(ticket.getReason())
.setImages(images)
.setFiles(files);
Map<String, Object> variables = new HashMap<>();
@ -579,6 +598,7 @@ public class TicketController extends ControllerBase {
.setNo(ticket.getNo())
.setTitle(ticket.getTitle())
.setDeviceNo(ticket.getDeviceNo())
.setDeviceAddress(ticket.getDeviceAddress())
.setModelNo(device.getModelNo())
.setDeviceType(device.getDeviceType())
.setComponent(ticket.getComponent())
@ -603,8 +623,7 @@ public class TicketController extends ControllerBase {
.setAreaName(areaName)
.setCompanyName(device.getCustomerName())
.setHandle(handle)
.setSolution(ticket.getSolution())
.setSolutionAttachments(StrUtil.isNotBlank(ticket.getSolutionAttachments()) ? StrUtil.split(ticket.getSolutionAttachments(), ",") : Collections.emptyList())
.setSolution(ticket.getReason())
.setEvaluate(getTicketEvaluate(ticket.getId()));
return ApiResult.success(vo);
}
@ -727,4 +746,48 @@ public class TicketController extends ControllerBase {
ticketEventPublisher.publishTicketReplyEvent(ticket);
return ApiResult.success();
}
/**
* 获取工单解决方案措施
* @param ticketId 工单编号
* @return 解决方案措施
**/
@GetMapping("getSolutionMeasures")
@ApiMark(moduleName = "工单管理", apiName = "获取工单解决方案措施")
public ApiResult<List<SolutionMeasuresVO>> getSolutionMeasures(@Valid @RequestParam @NotNull Integer ticketId){
return ApiResult.success(ticketSolutionService.getSolutionMeasures(ticketId));
}
/**
* 保存工单解决方案措施
* @param request 请求信息
**/
@PostMapping("saveSolutionMeasures")
@ApiMark(moduleName = "工单管理", apiName = "保存工单解决方案措施")
public ApiResult<Void> saveSolutionMeasures(@Valid @RequestBody SolutionMeasuresSaveRequest request){
ticketSolutionService.saveSolutionMeasures(request);
return ApiResult.success();
}
/**
* 获取工单解决方案评审部门
* @param ticketId 工单编号
* @return 评审部门
**/
@GetMapping("getSolutionReviewDepartment")
@ApiMark(moduleName = "工单管理", apiName = "获取工单解决方案评审部门")
public ApiResult<List<SolutionReviewDepartmentVO>> getSolutionReviewDepartment(@Valid @RequestParam @NotNull Integer ticketId){
return ApiResult.success(ticketSolutionAuditService.getByTicket(ticketId));
}
/**
* 保存工单解决方案评审部门
* @param request 请求信息
**/
@PostMapping("saveSolutionReviewDepartment")
@ApiMark(moduleName = "工单管理", apiName = "保存工单解决方案评审部门")
public ApiResult<Void> saveSolutionReviewDepartment(@Valid @RequestBody SolutionReviewDepartmentSaveRequest request){
ticketSolutionAuditService.saveSolutionReviewDepartment(request);
return ApiResult.success();
}
}

View File

@ -5,9 +5,12 @@ import cn.hutool.core.date.DatePattern;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.nflg.mobilebroken.admin.pojo.dto.TiketTimeoutDTO;
import com.nflg.mobilebroken.admin.publisher.TicketEventPublisher;
import com.nflg.mobilebroken.admin.service.SsePushService;
import com.nflg.mobilebroken.common.constant.Constant;
import com.nflg.mobilebroken.common.constant.MessageSubType;
import com.nflg.mobilebroken.common.constant.UserState;
import com.nflg.mobilebroken.common.pojo.dto.ChatMessageDTO;
import com.nflg.mobilebroken.common.pojo.request.TicketEvaluateAddRequest;
import com.nflg.mobilebroken.common.pojo.vo.TicketEvaluateAddVO;
import com.nflg.mobilebroken.common.pojo.vo.TicketEvaluateItemVO;
@ -22,6 +25,7 @@ import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import javax.mail.MessagingException;
import java.math.BigDecimal;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
@ -68,6 +72,15 @@ public class TicketScheduledTasks {
@Resource
private IDeviceService deviceService;
@Resource
private TicketEventPublisher ticketEventPublisher;
@Resource
private TicketChatService ticketChatService;
@Resource
private SsePushService ssePushService;
/**
* 工单评论邀请邮件
* 每天午夜12点执行一次
@ -126,6 +139,21 @@ public class TicketScheduledTasks {
tickets.forEach(ticket -> {
request.setTicketId(ticket.getId());
ticketEvaluateService.add(request);
if (ticketService.close(ticket)){
ticketEventPublisher.publishTicketCloseEvent(ticket);
ChatMessageDTO message = new ChatMessageDTO()
.setId(cn.hutool.core.util.IdUtil.getSnowflakeNextIdStr())
.setFrom("system")
.setTicketState(ticket.getState())
.setSenderId(0)
.setSenderName("服务助手")
.setContent("<b>工单已关闭</b><br/>感谢你的使用,如有问题,请重新提交新的工单。")
.setCreateTime(Instant.now());
ticketChatService.addMessage(request.getTicketId(), message);
//推送消息
ssePushService.sendTicketMessageToAdmin(ticket.getId(),message);
ssePushService.sendTicketMessageToApp(ticket.getId(),message);
}
});
log.info("【工单自动评论】执行完成");
}

View File

@ -0,0 +1,15 @@
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
public class Test {
@org.junit.jupiter.api.Test
public void test1(){
LocalDate t1=LocalDateTime.now().toLocalDate();
LocalDate t2=LocalDateTime.now().toLocalDate().minusDays(1);
System.out.println(t1);
System.out.println(t2);
System.out.println(ChronoUnit.DAYS.between(null, t1));
}
}

View File

@ -206,6 +206,7 @@ public class TiketController extends ControllerBase {
.setNo(ticket.getNo())
.setTitle(ticket.getTitle())
.setDeviceNo(ticket.getDeviceNo())
.setDeviceAddress(ticket.getDeviceAddress())
.setModelNo(device.getModelNo())
.setComponent(ticket.getComponent())
.setUseTime(ticket.getUseTime())
@ -221,8 +222,7 @@ public class TiketController extends ControllerBase {
.setSolveTime(ticket.getSolveTime())
.setCompanyName(StrUtil.join(",",companys.stream().map(TBaseCustomer::getAgencyCompanyName).collect(Collectors.toList())))
.setHandle(handle)
.setSolution(ticket.getSolution())
.setSolutionAttachments(StrUtil.isNotBlank(ticket.getSolutionAttachments()) ? StrUtil.split(ticket.getSolutionAttachments(), ",") : Collections.emptyList())
.setSolution(ticket.getReason())
.setEvaluate(getTicketEvaluate(ticket.getId()));
return ApiResult.success(vo);
}
@ -386,7 +386,20 @@ public class TiketController extends ControllerBase {
**/
@PostMapping("addTicketEvaluate")
public ApiResult<Void> addTicketEvaluate(@Valid @RequestBody TicketEvaluateAddRequest request) {
ticketEvaluateService.add(request);
Ticket ticket=ticketEvaluateService.add(request);
ticketEventPublisher.publishTicketEvaluateEvent(ticket, MultilingualUtil.getLanguage(), MultilingualUtil.getZone());
ChatMessageDTO message = new ChatMessageDTO()
.setId(cn.hutool.core.util.IdUtil.getSnowflakeNextIdStr())
.setFrom("system")
.setTicketState(ticket.getState())
.setSenderId(0)
.setSenderName("服务助手")
.setContent("<b>工单已评价</b><br/>感谢你的评价。")
.setCreateTime(Instant.now());
ticketChatService.addMessage(request.getTicketId(), message);
//推送消息
ssePushService.sendTicketMessageToAdmin(ticket.getId(),message);
ssePushService.sendTicketMessageToApp(ticket.getId(),message);
return ApiResult.success();
}
}

View File

@ -0,0 +1,80 @@
package com.nflg.mobilebroken.cfs.event;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DatePattern;
import com.nflg.mobilebroken.common.constant.MessageSubType;
import com.nflg.mobilebroken.common.constant.MessageType;
import com.nflg.mobilebroken.repository.entity.AdminMessage;
import com.nflg.mobilebroken.repository.entity.AdminUser;
import com.nflg.mobilebroken.repository.entity.Ticket;
import com.nflg.mobilebroken.repository.service.*;
import com.nflg.mobilebroken.starter.service.EmailService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationEvent;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
@Slf4j
public class TicketEvaluateEvent extends ApplicationEvent implements ApplicationContextAware {
private static final DateTimeFormatter FORMATTER= DateTimeFormatter.ofPattern(DatePattern.NORM_DATETIME_PATTERN);
private final Ticket ticket;
private final String language;
private final String zone;
private IDictionaryItemTranslateService dictionaryItemTranslateService;
private IAppUserService appUserService;
private EmailService emailService;
private ITBaseDeviceTypeService deviceTypeService;
private IAdminUserService adminUserService;
private IAdminMessageService adminMessageService;
public TicketEvaluateEvent(Object source, Ticket ticket,String language,String zone) {
super(source);
this.ticket = ticket;
this.language = language;
this.zone = zone;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
this.dictionaryItemTranslateService = applicationContext.getBean(IDictionaryItemTranslateService.class);
this.appUserService = applicationContext.getBean(IAppUserService.class);
this.emailService = applicationContext.getBean(EmailService.class);
this.adminUserService = applicationContext.getBean(IAdminUserService.class);
this.adminMessageService = applicationContext.getBean(IAdminMessageService.class);
this.deviceTypeService = applicationContext.getBean(ITBaseDeviceTypeService.class);
}
public void send(){
sendUserMessage();
}
private void sendUserMessage(){
//我的待办
List<Integer> cqms=deviceTypeService.getCqmsByDeviceType(ticket.getDeviceNo());
if (CollectionUtil.isEmpty(cqms)){
log.warn("该设备尚未设置CQM负责人:{}", ticket.getDeviceNo());
}else {
List<AdminUser> cqmUsers = adminUserService.listByIds(cqms);
if (CollectionUtil.isNotEmpty(cqmUsers)) {
cqmUsers.forEach(c -> adminMessageService.add(
new AdminMessage()
.setNo(ticket.getNo())
.setTitle(ticket.getTitle())
.setUserId(c.getId())
.setSourceId(ticket.getId())
.setSource(0)
.setType(MessageType.WorkOrderAssignment.getState())
.setSubType(MessageSubType.TicketCompletion.getState())
.setIsRead(false)
.setCreateTime(LocalDateTime.now()))
);
}
}
}
}

View File

@ -1,6 +1,7 @@
package com.nflg.mobilebroken.cfs.listener;
import com.nflg.mobilebroken.cfs.event.TicketCreateEvent;
import com.nflg.mobilebroken.cfs.event.TicketEvaluateEvent;
import com.nflg.mobilebroken.cfs.event.TicketReplyEvent;
import com.nflg.mobilebroken.cfs.event.TicketRevokeEvent;
import org.springframework.context.event.EventListener;
@ -27,4 +28,10 @@ public class TicketEventListener {
public void handleTicketRevokeEvent(TicketRevokeEvent event) {
event.send();
}
@Async
@EventListener
public void handleTicketEvaluateEvent(TicketEvaluateEvent event) {
event.send();
}
}

View File

@ -1,9 +1,6 @@
package com.nflg.mobilebroken.cfs.publisher;
import com.nflg.mobilebroken.cfs.event.TicketCreateEvent;
import com.nflg.mobilebroken.cfs.event.TicketReopenEvent;
import com.nflg.mobilebroken.cfs.event.TicketReplyEvent;
import com.nflg.mobilebroken.cfs.event.TicketRevokeEvent;
import com.nflg.mobilebroken.cfs.event.*;
import com.nflg.mobilebroken.repository.entity.Ticket;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEventPublisher;
@ -43,4 +40,10 @@ public class TicketEventPublisher {
event.setApplicationContext(applicationContext);
eventPublisher.publishEvent(event);
}
public void publishTicketEvaluateEvent(Ticket ticket, String language, String zone) {
TicketEvaluateEvent event = new TicketEvaluateEvent(this, ticket, language, zone);
event.setApplicationContext(applicationContext);
eventPublisher.publishEvent(event);
}
}

View File

@ -111,4 +111,7 @@ public class Constant {
public static final String DICTIONARY_ITEM_ACCOUNT_HAS_EXPIRED="AccountHasExpired";
public static final List<String> ROLE_CODE_TICKET_MANAGERS = ListUtil.of(TITLE_DIRECTOROF_BUSINESS_UNIT,TITLE_TECHNICAL_MANAGER,TITLE_SALES_MANAGER,TITLE_TEST_MANAGER,TITLE_QUALITY_MANAGER,DICTIONARY_TYPE_TITLE_CQM);
public static final String DICTIONARY_SOLUTION_MEASURES = "SolutionMeasures";
public static final String DICTIONARY_SOLUTION_REVIEW_DEPARTMENT = "SolutionReviewDepartment";
}

View File

@ -17,7 +17,8 @@ public enum MessageSubType {
TicketClosed(7, "工单关闭"),
TicketRevoke(8, "工单撤销"),
TicketReopen(9, "工单重启"),
TicketTimeout(10, "工单超时");
TicketTimeout(10, "工单超时"),
TicketSolutionAudit(11, "工单解决方案审核");
private final Integer state;
private final String description;

View File

@ -0,0 +1,33 @@
package com.nflg.mobilebroken.common.pojo.request;
import com.nflg.mobilebroken.common.pojo.vo.SolutionMeasuresVO;
import lombok.Data;
import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.util.List;
@Data
public class SolutionMeasuresSaveRequest {
/**
* 工单编号
*/
@NotNull
private Integer ticketId;
/**
* 根本原因
*/
@NotBlank
private String reason;
/**
* 解决措施
*/
@Valid
@NotEmpty
private List<SolutionMeasuresVO> solutionMeasures;
}

View File

@ -0,0 +1,18 @@
package com.nflg.mobilebroken.common.pojo.request;
import com.nflg.mobilebroken.common.pojo.vo.SolutionReviewDepartmentVO;
import lombok.Data;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.util.List;
@Data
public class SolutionReviewDepartmentSaveRequest {
@NotNull
private Integer ticketId;
@NotEmpty
private List<SolutionReviewDepartmentVO> departments;
}

View File

@ -34,4 +34,8 @@ public class TicketAddRequest {
//附件
private List<String> attachments;
//设备地址
@Size(max = 300, message = "设备地址长度不能超过300")
private String deviceAddress;
}

View File

@ -1,5 +1,6 @@
package com.nflg.mobilebroken.common.pojo.vo;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.nflg.mobilebroken.common.constant.TicketState;
import com.nflg.mobilebroken.common.constant.TicketUrgency;
@ -8,6 +9,7 @@ import org.ttzero.excel.annotation.ExcelColumn;
import org.ttzero.excel.annotation.IgnoreExport;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.Objects;
@Data
@ -51,9 +53,21 @@ public class AdminTicketVO {
@ExcelColumn("问题类型")
private String question;
//解决方案
@ExcelColumn("解决方案")
private String solution;
//根本原因分析
@IgnoreExport
private String reason;
public String getReason() {
return StrUtil.subWithLength(reason, 0, 10);
}
//根本原因分析
@ExcelColumn("根本原因分析")
private String reason1;
public String getReason1() {
return reason;
}
//代理区域
@ExcelColumn("代理区域")
@ -164,4 +178,18 @@ public class AdminTicketVO {
//评价
@IgnoreExport
private TicketEvaluateVO evaluate;
//处理时长
@ExcelColumn("处理时长")
private Long processingTime;
public Long getProcessingTime() {
if (TicketState.Processing.getState().compareTo(state)>=0){
return ChronoUnit.DAYS.between(createTime.toLocalDate(),LocalDateTime.now().toLocalDate())+1;
}
if (TicketState.Closed.getState().compareTo(state)>=0) {
return ChronoUnit.DAYS.between(completeTime, LocalDateTime.now()) + 1;
}
return null;
}
}

View File

@ -0,0 +1,47 @@
package com.nflg.mobilebroken.common.pojo.vo;
import lombok.Data;
import lombok.experimental.Accessors;
import javax.validation.constraints.NotBlank;
@Data
@Accessors(chain = true)
public class SolutionMeasuresItemVO {
private Integer id;
/**
* 措施名称
*/
@NotBlank
private String name;
/**
* 负责人
*/
@NotBlank
private String superintendent;
/**
* 计划日期
*/
@NotBlank
private String scheduleDate;
/**
* 确认日期
*/
@NotBlank
private String confirmedDate;
/**
* 备注
*/
private String remark;
/**
* 创建人id
*/
private Integer createUserId;
}

View File

@ -0,0 +1,23 @@
package com.nflg.mobilebroken.common.pojo.vo;
import lombok.Data;
import lombok.experimental.Accessors;
import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.util.List;
@Data
@Accessors(chain = true)
public class SolutionMeasuresVO {
@NotNull
private Integer id;
@NotBlank
private String name;
@Valid
private List<SolutionMeasuresItemVO> items;
}

View File

@ -0,0 +1,23 @@
package com.nflg.mobilebroken.common.pojo.vo;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
@Data
public class SolutionReviewDepartmentVO {
private Integer id;
//部门名称
@NotBlank
private String deptName;
//用户id
@NotNull
private Integer userId;
//用户名称
private String userName;
}

View File

@ -98,11 +98,13 @@ public class TicketInfoVO {
//解决方案
private String solution;
//解决方案附件
private List<String> solutionAttachments;
/**
* 解决时间
*/
private LocalDateTime solveTime;
/**
* 设备地址
*/
private String deviceAddress;
}

View File

@ -67,6 +67,11 @@ public class Ticket implements Serializable {
*/
private String attachments;
/**
* 设备地址
*/
private String deviceAddress;
/**
* 状态0待处理1处理中2处理完成3已关闭
*/
@ -95,12 +100,7 @@ public class Ticket implements Serializable {
/**
* 解决方案
*/
private String solution;
/**
* 解决方案附件
*/
private String solutionAttachments;
private String reason;
/**
* 解决时间

View File

@ -0,0 +1,76 @@
package com.nflg.mobilebroken.repository.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* <p>
*
* </p>
*
* @author 代码生成器生成
* @since 2025
*/
@Getter
@Setter
@Accessors(chain = true)
@TableName("ticket_solution")
public class TicketSolution implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
/**
* 工单id
*/
private Integer ticketId;
/**
* 字典项id
*/
private Integer dictionaryId;
/**
* 字典项值
*/
private String dictionaryName;
/**
* 负责人
*/
private String superintendent;
/**
* 计划日期
*/
private String scheduleDate;
/**
* 确认日期
*/
private String confirmedDate;
/**
* 备注
*/
private String remark;
/**
* 创建人id
*/
private Integer createUserid;
/**
* 创建时间
*/
private LocalDateTime createTime;
}

View File

@ -0,0 +1,48 @@
package com.nflg.mobilebroken.repository.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* <p>
*
* </p>
*
* @author 代码生成器生成
* @since 2025
*/
@Getter
@Setter
@Accessors(chain = true)
@TableName("ticket_solution_audit")
public class TicketSolutionAudit implements Serializable {
private static final long serialVersionUID = 1L;
private Integer id;
/**
* 工单id
*/
private Integer ticketId;
/**
* 部门名称
*/
private String deptName;
/**
* 用户id
*/
private Integer userId;
/**
* 创建时间
*/
private LocalDateTime createTime;
}

View File

@ -0,0 +1,53 @@
package com.nflg.mobilebroken.repository.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* <p>
*
* </p>
*
* @author 代码生成器生成
* @since 2025
*/
@Getter
@Setter
@Accessors(chain = true)
@TableName("ticket_solution_audit_state")
public class TicketSolutionAuditState implements Serializable {
private static final long serialVersionUID = 1L;
private Integer id;
/**
* 工单id
*/
private Integer ticketId;
/**
* 用户id
*/
private Integer userId;
/**
* 审核状态0不通过1通过
*/
private Integer state;
/**
* 创建时间
*/
private LocalDateTime createTime;
/**
* 审核不通过的原因
*/
private String reason;
}

View File

@ -5,6 +5,8 @@ import com.nflg.mobilebroken.repository.entity.DictionaryItem;
import org.apache.ibatis.annotations.Select;
import org.springframework.data.repository.query.Param;
import java.util.List;
/**
* <p>
* 字典值 Mapper 接口
@ -19,4 +21,6 @@ public interface DictionaryItemMapper extends BaseMapper<DictionaryItem> {
Integer getId(@Param("dictionaryName") String dictionaryName, @Param("dictionaryItemName") String dictionaryItemName);
String getName(String dictionaryCode, String itemCode);
List<DictionaryItem> getListByDictionaryCode(String code);
}

View File

@ -0,0 +1,20 @@
package com.nflg.mobilebroken.repository.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.nflg.mobilebroken.common.pojo.vo.SolutionReviewDepartmentVO;
import com.nflg.mobilebroken.repository.entity.TicketSolutionAudit;
import java.util.List;
/**
* <p>
* Mapper 接口
* </p>
*
* @author 代码生成器生成
* @since 2025
*/
public interface TicketSolutionAuditMapper extends BaseMapper<TicketSolutionAudit> {
List<SolutionReviewDepartmentVO> getByTicket(Integer ticketId);
}

View File

@ -0,0 +1,17 @@
package com.nflg.mobilebroken.repository.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.nflg.mobilebroken.repository.entity.TicketSolutionAuditState;
/**
* <p>
* Mapper 接口
* </p>
*
* @author 代码生成器生成
* @since 2025
*/
public interface TicketSolutionAuditStateMapper extends BaseMapper<TicketSolutionAuditState> {
boolean existsNotPass(Integer id);
}

View File

@ -0,0 +1,16 @@
package com.nflg.mobilebroken.repository.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.nflg.mobilebroken.repository.entity.TicketSolution;
/**
* <p>
* Mapper 接口
* </p>
*
* @author 代码生成器生成
* @since 2025
*/
public interface TicketSolutionMapper extends BaseMapper<TicketSolution> {
}

View File

@ -52,4 +52,6 @@ public interface IAdminUserService extends IService<AdminUser> {
void deleteAccount(Integer id);
List<Integer> getTickerMangagers();
List<Integer> getCQMIds();
}

View File

@ -6,6 +6,8 @@ import com.nflg.mobilebroken.common.pojo.request.DictionaryItemSearchRequest;
import com.nflg.mobilebroken.common.pojo.request.SaveDictionaryItemRequest;
import com.nflg.mobilebroken.repository.entity.DictionaryItem;
import java.util.List;
/**
* <p>
* 字典值 服务类
@ -21,4 +23,6 @@ public interface IDictionaryItemService extends IService<DictionaryItem> {
void save(SaveDictionaryItemRequest request);
Integer getId(String dictionaryName, String dictionaryItemName);
List<DictionaryItem> getListByDictionaryCode(String code);
}

View File

@ -2,6 +2,7 @@ package com.nflg.mobilebroken.repository.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.nflg.mobilebroken.common.pojo.request.TicketEvaluateAddRequest;
import com.nflg.mobilebroken.repository.entity.Ticket;
import com.nflg.mobilebroken.repository.entity.TicketEvaluate;
/**
@ -14,5 +15,5 @@ import com.nflg.mobilebroken.repository.entity.TicketEvaluate;
*/
public interface ITicketEvaluateService extends IService<TicketEvaluate> {
void add(TicketEvaluateAddRequest request);
Ticket add(TicketEvaluateAddRequest request);
}

View File

@ -31,7 +31,7 @@ public interface ITicketService extends IService<Ticket> {
Ticket completeTicket(Integer id);
Ticket closeTicket(TicketCloseRequest request);
// Ticket closeTicket(TicketCloseRequest request);
List<AdminTicketVO> exportSearch(AdminTicketSearchRequest request);
@ -54,4 +54,6 @@ public interface ITicketService extends IService<Ticket> {
List<TicketVO> getAdminFavorites(Integer userId,Integer favoritesId);
Ticket rejectTicket(Integer id);
boolean close(Ticket ticket);
}

View File

@ -0,0 +1,23 @@
package com.nflg.mobilebroken.repository.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.nflg.mobilebroken.common.pojo.request.SolutionReviewDepartmentSaveRequest;
import com.nflg.mobilebroken.common.pojo.vo.SolutionReviewDepartmentVO;
import com.nflg.mobilebroken.repository.entity.TicketSolutionAudit;
import java.util.List;
/**
* <p>
* 服务类
* </p>
*
* @author 代码生成器生成
* @since 2025
*/
public interface ITicketSolutionAuditService extends IService<TicketSolutionAudit> {
List<SolutionReviewDepartmentVO> getByTicket(Integer ticketId);
void saveSolutionReviewDepartment(SolutionReviewDepartmentSaveRequest request);
}

View File

@ -0,0 +1,17 @@
package com.nflg.mobilebroken.repository.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.nflg.mobilebroken.repository.entity.TicketSolutionAuditState;
/**
* <p>
* 服务类
* </p>
*
* @author 代码生成器生成
* @since 2025
*/
public interface ITicketSolutionAuditStateService extends IService<TicketSolutionAuditState> {
boolean existsNotPass(Integer id);
}

View File

@ -0,0 +1,23 @@
package com.nflg.mobilebroken.repository.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.nflg.mobilebroken.common.pojo.request.SolutionMeasuresSaveRequest;
import com.nflg.mobilebroken.common.pojo.vo.SolutionMeasuresVO;
import com.nflg.mobilebroken.repository.entity.TicketSolution;
import java.util.List;
/**
* <p>
* 服务类
* </p>
*
* @author 代码生成器生成
* @since 2025
*/
public interface ITicketSolutionService extends IService<TicketSolution> {
List<SolutionMeasuresVO> getSolutionMeasures(Integer ticketId);
void saveSolutionMeasures(SolutionMeasuresSaveRequest request);
}

View File

@ -1,6 +1,7 @@
package com.nflg.mobilebroken.repository.service.impl;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
@ -339,6 +340,11 @@ public class AdminUserServiceImpl extends ServiceImpl<AdminUserMapper, AdminUser
return baseMapper.getTickerMangagers(Constant.ROLE_CODE_TICKET_MANAGERS);
}
@Override
public List<Integer> getCQMIds() {
return baseMapper.getTickerMangagers(ListUtil.of(Constant.DICTIONARY_TYPE_TITLE_CQM));
}
private String getDepartmentName(Long departmentId) {
TBaseDepartment department = departmentService.lambdaQuery()
.eq(TBaseDepartment::getId, departmentId)

View File

@ -130,4 +130,9 @@ public class DictionaryItemServiceImpl extends ServiceImpl<DictionaryItemMapper,
public Integer getId(String dictionaryName, String dictionaryItemName) {
return baseMapper.getId(dictionaryName, dictionaryItemName);
}
@Override
public List<DictionaryItem> getListByDictionaryCode(String code) {
return baseMapper.getListByDictionaryCode(code);
}
}

View File

@ -31,7 +31,7 @@ public class TicketEvaluateServiceImpl extends ServiceImpl<TicketEvaluateMapper,
private ITicketService ticketService;
@Override
public void add(TicketEvaluateAddRequest request) {
public Ticket add(TicketEvaluateAddRequest request) {
Ticket ticket = ticketService.getById(request.getTicketId());
VUtils.trueThrowBusinessError(Objects.isNull(ticket)).throwMessage("工单不存在");
VUtils.trueThrowBusinessError(!Objects.equals(ticket.getState(), TicketState.ProcessingCompleted.getState())
@ -47,5 +47,6 @@ public class TicketEvaluateServiceImpl extends ServiceImpl<TicketEvaluateMapper,
.setFeedback(request.getFeedback())
.setCreateTime(LocalDateTime.now());
save(ticketEvaluate);
return ticket;
}
}

View File

@ -16,10 +16,7 @@ import com.nflg.mobilebroken.common.util.AdminUserUtil;
import com.nflg.mobilebroken.common.util.AppUserUtil;
import com.nflg.mobilebroken.common.util.TicketUtl;
import com.nflg.mobilebroken.common.util.VUtils;
import com.nflg.mobilebroken.repository.entity.AdminUser;
import com.nflg.mobilebroken.repository.entity.FileUploadRecord;
import com.nflg.mobilebroken.repository.entity.Ticket;
import com.nflg.mobilebroken.repository.entity.TicketEvaluate;
import com.nflg.mobilebroken.repository.entity.*;
import com.nflg.mobilebroken.repository.mapper.TicketMapper;
import com.nflg.mobilebroken.repository.service.*;
import org.springframework.stereotype.Service;
@ -52,6 +49,9 @@ public class TicketServiceImpl extends ServiceImpl<TicketMapper, Ticket> impleme
@Resource
private IFileUploadRecordService fileUploadRecordService;
@Resource
private ITicketSolutionAuditStateService ticketSolutionAuditStateService;
@Override
public Ticket add(TicketAddRequest request, Integer userId) {
Ticket lastTicket=lambdaQuery()
@ -63,6 +63,7 @@ public class TicketServiceImpl extends ServiceImpl<TicketMapper, Ticket> impleme
Ticket ticket = new Ticket()
.setNo(no)
.setDeviceNo(request.getDeviceNo())
.setDeviceAddress(request.getDeviceAddress())
.setComponent(request.getComponent())
.setUseTime(request.getUseTime())
.setTitle(request.getTitle())
@ -204,28 +205,27 @@ public class TicketServiceImpl extends ServiceImpl<TicketMapper, Ticket> impleme
return ticket;
}
@Override
public Ticket closeTicket(TicketCloseRequest request) {
Ticket ticket = getById(request.getTicketId());
VUtils.trueThrowBusinessError(Objects.isNull(ticket)).throwMessage("未找到工单");
VUtils.trueThrowBusinessError(!Objects.equals(ticket.getState(), TicketState.ProcessingCompleted.getState()))
.throwMessage("非处理完成状态不允许关闭");
VUtils.trueThrowBusinessError(!ticketEvaluateService.lambdaQuery()
.eq(TicketEvaluate::getTicketId, request.getTicketId())
.exists())
.throwMessage("工单尚未评价,不能关闭");
List<Integer> tickerMangagers = adminUserService.getTickerMangagers();
tickerMangagers.addAll(Arrays.stream(ticket.getHandle().split(",")).map(Integer::parseInt).collect(Collectors.toList()));
VUtils.trueThrowBusinessError(tickerMangagers.stream().noneMatch(uid -> Objects.equals(uid, AdminUserUtil.getUserId())))
.throwMessage("你无权关闭工单");
ticket.setState(TicketState.Closed.getState());
ticket.setSolution(request.getSolution());
ticket.setSolutionAttachments(StrUtil.join(",", request.getAttachments()));
ticket.setSolveTime(LocalDateTime.now());
ticket.setCurrentHandle(AdminUserUtil.getUserId());
updateById(ticket);
return ticket;
}
// @Override
// public Ticket closeTicket(TicketCloseRequest request) {
// Ticket ticket = getById(request.getTicketId());
// VUtils.trueThrowBusinessError(Objects.isNull(ticket)).throwMessage("未找到工单");
// VUtils.trueThrowBusinessError(!Objects.equals(ticket.getState(), TicketState.ProcessingCompleted.getState()))
// .throwMessage("非处理完成状态不允许关闭");
// VUtils.trueThrowBusinessError(!ticketEvaluateService.lambdaQuery()
// .eq(TicketEvaluate::getTicketId, request.getTicketId())
// .exists())
// .throwMessage("工单尚未评价,不能关闭");
// List<Integer> tickerMangagers = adminUserService.getTickerMangagers();
// tickerMangagers.addAll(Arrays.stream(ticket.getHandle().split(",")).map(Integer::parseInt).collect(Collectors.toList()));
// VUtils.trueThrowBusinessError(tickerMangagers.stream().noneMatch(uid -> Objects.equals(uid, AdminUserUtil.getUserId())))
// .throwMessage("你无权关闭工单");
// ticket.setState(TicketState.Closed.getState());
// ticket.setReason(request.getSolution());
// ticket.setSolveTime(LocalDateTime.now());
// ticket.setCurrentHandle(AdminUserUtil.getUserId());
// updateById(ticket);
// return ticket;
// }
@Override
public List<AdminTicketVO> exportSearch(AdminTicketSearchRequest request) {
@ -346,4 +346,28 @@ public class TicketServiceImpl extends ServiceImpl<TicketMapper, Ticket> impleme
updateById(ticket);
return ticket;
}
public boolean close(Ticket ticket){
if (!Objects.equals(ticket.getState(), TicketState.ProcessingCompleted.getState())){
return false;
}
if (!ticketEvaluateService.lambdaQuery()
.eq(TicketEvaluate::getTicketId, ticket.getId())
.exists()){
return false;
}
if (!ticketSolutionAuditStateService.lambdaQuery().eq(TicketSolutionAuditState::getTicketId, ticket.getId()).exists()) {
return false;
}
if (ticketSolutionAuditStateService.existsNotPass(ticket.getId())){
return false;
}
return lambdaUpdate()
.set(Ticket::getState, TicketState.Closed.getState())
.set(Ticket::getSolveTime, LocalDateTime.now())
.set(Ticket::getUpdateTime, LocalDateTime.now())
.eq(Ticket::getId, ticket.getId())
.eq(Ticket::getState, TicketState.ProcessingCompleted.getState())
.update();
}
}

View File

@ -0,0 +1,80 @@
package com.nflg.mobilebroken.repository.service.impl;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.nflg.mobilebroken.common.constant.Constant;
import com.nflg.mobilebroken.common.pojo.request.SolutionReviewDepartmentSaveRequest;
import com.nflg.mobilebroken.common.pojo.vo.SolutionReviewDepartmentVO;
import com.nflg.mobilebroken.repository.entity.DictionaryItem;
import com.nflg.mobilebroken.repository.entity.TicketSolutionAudit;
import com.nflg.mobilebroken.repository.mapper.TicketSolutionAuditMapper;
import com.nflg.mobilebroken.repository.service.IDictionaryItemService;
import com.nflg.mobilebroken.repository.service.ITicketSolutionAuditService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
/**
* <p>
* 服务实现类
* </p>
*
* @author 代码生成器生成
* @since 2025
*/
@Service
public class TicketSolutionAuditServiceImpl extends ServiceImpl<TicketSolutionAuditMapper, TicketSolutionAudit> implements ITicketSolutionAuditService {
@Resource
private IDictionaryItemService dictionaryItemService;
@Override
public List<SolutionReviewDepartmentVO> getByTicket(Integer ticketId) {
List<DictionaryItem> items=dictionaryItemService.getListByDictionaryCode(Constant.DICTIONARY_SOLUTION_REVIEW_DEPARTMENT);
List<SolutionReviewDepartmentVO> vos=baseMapper.getByTicket(ticketId);
items.forEach(item->{
if (vos.stream().noneMatch(vo->vo.getDeptName().equals(item.getName()))){
SolutionReviewDepartmentVO vo=new SolutionReviewDepartmentVO();
vo.setDeptName(item.getName());
vos.add(vo);
}
});
return vos;
}
@Transactional
@Override
public void saveSolutionReviewDepartment(SolutionReviewDepartmentSaveRequest request) {
remove(new LambdaQueryWrapper<TicketSolutionAudit>()
.eq(TicketSolutionAudit::getTicketId,request.getTicketId())
.notIn(TicketSolutionAudit::getDeptName,request.getDepartments().stream().map(SolutionReviewDepartmentVO::getDeptName).collect(Collectors.toList())));
List<TicketSolutionAudit> forAdd=new ArrayList<>();
List<TicketSolutionAudit> forUpdate=new ArrayList<>();
request.getDepartments().forEach(detp->{
TicketSolutionAudit audit=new TicketSolutionAudit()
.setTicketId(request.getTicketId())
.setDeptName(detp.getDeptName())
.setUserId(detp.getUserId());
if (Objects.isNull(detp.getId())){
audit.setCreateTime(LocalDateTime.now());
forAdd.add(audit);
}else {
audit.setId(detp.getId());
forUpdate.add(audit);
}
});
if (CollectionUtil.isNotEmpty(forAdd)){
saveBatch(forAdd);
}
if (CollectionUtil.isNotEmpty(forUpdate)){
updateBatchById(forUpdate);
}
}
}

View File

@ -0,0 +1,24 @@
package com.nflg.mobilebroken.repository.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.nflg.mobilebroken.repository.entity.TicketSolutionAuditState;
import com.nflg.mobilebroken.repository.mapper.TicketSolutionAuditStateMapper;
import com.nflg.mobilebroken.repository.service.ITicketSolutionAuditStateService;
import org.springframework.stereotype.Service;
/**
* <p>
* 服务实现类
* </p>
*
* @author 代码生成器生成
* @since 2025
*/
@Service
public class TicketSolutionAuditStateServiceImpl extends ServiceImpl<TicketSolutionAuditStateMapper, TicketSolutionAuditState> implements ITicketSolutionAuditStateService {
@Override
public boolean existsNotPass(Integer id) {
return baseMapper.existsNotPass(id);
}
}

View File

@ -0,0 +1,148 @@
package com.nflg.mobilebroken.repository.service.impl;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.nflg.mobilebroken.common.constant.Constant;
import com.nflg.mobilebroken.common.constant.TicketState;
import com.nflg.mobilebroken.common.pojo.request.SolutionMeasuresSaveRequest;
import com.nflg.mobilebroken.common.pojo.vo.SolutionMeasuresItemVO;
import com.nflg.mobilebroken.common.pojo.vo.SolutionMeasuresVO;
import com.nflg.mobilebroken.common.util.AdminUserUtil;
import com.nflg.mobilebroken.common.util.VUtils;
import com.nflg.mobilebroken.repository.entity.DictionaryItem;
import com.nflg.mobilebroken.repository.entity.Ticket;
import com.nflg.mobilebroken.repository.entity.TicketSolution;
import com.nflg.mobilebroken.repository.mapper.TicketSolutionMapper;
import com.nflg.mobilebroken.repository.service.IAdminUserService;
import com.nflg.mobilebroken.repository.service.IDictionaryItemService;
import com.nflg.mobilebroken.repository.service.ITicketService;
import com.nflg.mobilebroken.repository.service.ITicketSolutionService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
/**
* <p>
* 服务实现类
* </p>
*
* @author 代码生成器生成
* @since 2025
*/
@Service
public class TicketSolutionServiceImpl extends ServiceImpl<TicketSolutionMapper, TicketSolution> implements ITicketSolutionService {
@Resource
private ITicketService ticketService;
@Resource
private IDictionaryItemService dictionaryItemService;
@Resource
private IAdminUserService adminUserService;
@Override
public List<SolutionMeasuresVO> getSolutionMeasures(Integer ticketId) {
Ticket ticket = ticketService.getById(ticketId);
VUtils.trueThrowBusinessError(Objects.isNull(ticket)).throwMessage("工单不存在");
List<String> initialNames= new ArrayList<>();
if (Objects.equals(ticket.getState(), TicketState.Processing.getState())) {
List<DictionaryItem> items = dictionaryItemService.getListByDictionaryCode(Constant.DICTIONARY_SOLUTION_MEASURES);
initialNames=items.stream().map(DictionaryItem::getValue).collect(Collectors.toList());
}
List<TicketSolution> solutions = lambdaQuery().eq(TicketSolution::getTicketId, ticketId).orderByAsc(TicketSolution::getId).list();
Map<String, List<TicketSolution>> groupedSolutions = solutions.stream().collect(Collectors.groupingBy(TicketSolution::getDictionaryName, LinkedHashMap::new, Collectors.toList()));
if (CollectionUtil.isEmpty(groupedSolutions)) {
return initialNames.stream().map(ticketSolutions -> new SolutionMeasuresVO()
.setName(ticketSolutions)
.setItems(Collections.emptyList()))
.collect(Collectors.toList());
} else if (CollectionUtil.isEmpty(initialNames)) {
return groupedSolutions.entrySet().stream().map(ks -> new SolutionMeasuresVO()
.setName(ks.getKey())
.setItems(ks.getValue().stream().map(v -> new SolutionMeasuresItemVO()
.setName(v.getDictionaryName())
.setSuperintendent(v.getSuperintendent())
.setScheduleDate(v.getScheduleDate())
.setConfirmedDate(v.getConfirmedDate())
.setRemark(v.getRemark())
.setCreateUserId(v.getCreateUserid())).collect(Collectors.toList())
)).collect(Collectors.toList());
} else {
List<SolutionMeasuresVO> vos = new ArrayList<>();
initialNames.forEach(name -> {
List<TicketSolution> values = groupedSolutions.get(name);
if (CollectionUtil.isEmpty(values)) {
vos.add(new SolutionMeasuresVO().setName(name).setItems(Collections.emptyList()));
} else {
vos.add(new SolutionMeasuresVO()
.setName(name)
.setItems(values.stream().map(v -> new SolutionMeasuresItemVO()
.setName(v.getDictionaryName())
.setSuperintendent(v.getSuperintendent())
.setScheduleDate(v.getScheduleDate())
.setConfirmedDate(v.getConfirmedDate())
.setRemark(v.getRemark())
.setCreateUserId(v.getCreateUserid())).collect(Collectors.toList())
)
);
}
});
return vos;
}
}
@Transactional
@Override
public void saveSolutionMeasures(SolutionMeasuresSaveRequest request) {
Ticket ticket=ticketService.getById(request.getTicketId());
VUtils.trueThrowBusinessError(Objects.isNull(ticket)).throwMessage("工单不存在");
VUtils.trueThrowBusinessError(!Objects.equals(ticket.getState(), TicketState.Processing.getState())
&& !Objects.equals(ticket.getState(), TicketState.ProcessingCompleted.getState()))
.throwMessage("当前工单状态不允许修改解决方案");
List<Integer> cqmIds=adminUserService.getCQMIds();
cqmIds.addAll(Arrays.stream(ticket.getHandle().split(",")).map(Integer::parseInt).collect(Collectors.toList()));
VUtils.trueThrowBusinessError(cqmIds.stream().noneMatch(uid -> Objects.equals(uid, AdminUserUtil.getUserId())))
.throwMessage("无权修改解决方案");
ticketService.lambdaUpdate()
.set(Ticket::getReason, request.getReason())
.eq(Ticket::getId, request.getTicketId())
.update();
baseMapper.delete(new LambdaQueryWrapper<TicketSolution>()
.eq(TicketSolution::getTicketId,request.getTicketId())
.notIn(TicketSolution::getId, request.getSolutionMeasures().stream().flatMap(s -> s.getItems().stream()).map(SolutionMeasuresItemVO::getId).collect(Collectors.toList()))
);
List<TicketSolution> forAdd=new ArrayList<>();
List<TicketSolution> forUpdate=new ArrayList<>();
for (SolutionMeasuresVO solutionMeasuresVO : request.getSolutionMeasures()) {
for (SolutionMeasuresItemVO solutionMeasuresItemVO : solutionMeasuresVO.getItems()){
TicketSolution solution=new TicketSolution()
.setTicketId(request.getTicketId())
.setDictionaryId(solutionMeasuresVO.getId())
.setDictionaryName(solutionMeasuresVO.getName())
.setSuperintendent(solutionMeasuresItemVO.getSuperintendent())
.setScheduleDate(solutionMeasuresItemVO.getScheduleDate())
.setConfirmedDate(solutionMeasuresItemVO.getConfirmedDate());
if (Objects.isNull(solutionMeasuresItemVO.getId())){
solution.setCreateUserid(AdminUserUtil.getUserId())
.setCreateTime(LocalDateTime.now());
forAdd.add(solution);
}else {
solution.setId(solutionMeasuresItemVO.getId());
forUpdate.add(solution);
}
}
}
if (CollectionUtil.isNotEmpty(forAdd)){
saveBatch(forAdd);
}
if (CollectionUtil.isNotEmpty(forUpdate)){
updateBatchById(forUpdate);
}
}
}

View File

@ -8,4 +8,12 @@
INNER JOIN dictionary_item di ON d.id=di.dictionary_id
WHERE d.`code`=#{dictionaryCode} AND di.`code`=#{itemCode}
</select>
<select id="getListByDictionaryCode" resultType="com.nflg.mobilebroken.repository.entity.DictionaryItem">
SELECT di.*
FROM dictionary d
INNER JOIN dictionary_item di ON d.id=di.dictionary_id
WHERE d.`code`=#{code}
ORDER BY di.id
</select>
</mapper>

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.nflg.mobilebroken.repository.mapper.TicketSolutionAuditMapper">
<select id="getByTicket" resultType="com.nflg.mobilebroken.common.pojo.vo.SolutionReviewDepartmentVO">
SELECT tsa.id,tsa.dept_name,tsa.user_id,au.user_name
FROM ticket_solution_audit tsa
LEFT JOIN admin_user au ON tsa.user_id=au.id
WHERE tsa.ticket_id=#{ticketId}
</select>
</mapper>

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.nflg.mobilebroken.repository.mapper.TicketSolutionAuditStateMapper">
<select id="existsNotPass" resultType="boolean">
SELECT EXISTS
(SELECT 1
FROM ticket_solution_audit tsa
LEFT JOIN ticket_solution_audit_state tsas ON tsa.ticket_id = tsas.ticket_id
AND tsa.user_id = tsas.user_id
WHERE tsa.ticket_id = #{id}
AND tsas.state != 1)
</select>
</mapper>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.nflg.mobilebroken.repository.mapper.TicketSolutionMapper">
</mapper>

View File

@ -33,7 +33,7 @@ public class CodeGenerator {
, Paths.get(System.getProperty("user.dir")) + "/src/main/resources/mapper"))
)
.strategyConfig(builder -> {
builder.addInclude("ticket_favorites") //只生成指定表
builder.addInclude("ticket_solution_audit,ticket_solution_audit_state") //只生成指定表
.entityBuilder()
.enableLombok()
.enableChainModel()