Merge remote-tracking branch 'origin/develop' into develop

This commit is contained in:
luolm 2025-02-21 22:33:24 +08:00
commit de9ad3337c
21 changed files with 176 additions and 27 deletions

View File

@ -455,13 +455,16 @@ public class TicketController extends ControllerBase {
public ApiResult<Void> addChatMessage(@Valid @RequestBody AddChatMessageRequest request) {
Ticket ticket = ticketService.getById(request.getTicketId());
VUtils.trueThrowBusinessError(Objects.isNull(ticket)).throwMessage("工单不存在");
VUtils.trueThrowBusinessError(Byte.compare(ticket.getState(), TicketState.Processing.getState()) > 1)
VUtils.trueThrowBusinessError(!Objects.equals(ticket.getState(), TicketState.Processing.getState()))
.throwMessage("当前工单状态不允许发送消息");
VUtils.trueThrowBusinessError(Arrays.stream(ticket.getHandle().split(","))
.noneMatch(uid -> StrUtil.equals(uid, AdminUserUtil.getUserId().toString())))
.throwMessage("只有工单处理人能发送消息");
ticket.setCurrentHandle(AdminUserUtil.getUserId());
ticketService.updateById(ticket);
AdminUser user = adminUserService.getById(AdminUserUtil.getUserId());
ChatMessageDTO message = new ChatMessageDTO()
.setId(cn.hutool.core.util.IdUtil.getSnowflakeNextId())
.setId(cn.hutool.core.util.IdUtil.getSnowflakeNextIdStr())
.setFrom("admin")
.setTicketState(ticket.getState())
.setSenderId(user.getId())

View File

@ -7,6 +7,10 @@ import cn.hutool.json.JSONUtil;
import com.nflg.mobilebroken.admin.pojo.dto.TiketTimeoutDTO;
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.request.TicketEvaluateAddRequest;
import com.nflg.mobilebroken.common.pojo.vo.TicketEvaluateAddVO;
import com.nflg.mobilebroken.common.pojo.vo.TicketEvaluateItemVO;
import com.nflg.mobilebroken.repository.entity.*;
import com.nflg.mobilebroken.repository.service.*;
import com.nflg.mobilebroken.starter.service.EmailService;
@ -16,12 +20,14 @@ import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import javax.mail.MessagingException;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
@Component
@ -48,15 +54,73 @@ public class TicketScheduledTasks {
@Resource
private IDictionaryItemTranslateService dictionaryItemTranslateService;
@Resource
private ITBaseDeviceTypeService deviceTypeService;
@Resource
private EmailService emailService;
@Resource
private IParamConfigService paramConfigService;
@Resource
private ITicketEvaluateService ticketEvaluateService;
/**
* 工单评论邀请邮件
* 每天午夜12点执行一次
*/
@Scheduled(cron = "0 0 0 * * ?")
public void sendInviteCommentEmail() {
List<Ticket> tickets=ticketService.getNonComment(7);
tickets.forEach(ticket -> {
AdminUser createUser = adminUserService.getById(ticket.getUserId());
if (Objects.equals(createUser.getState(), UserState.Activated.getState())) {
List<Integer> adminUserIds = Arrays.stream(ticket.getHandle().split(","))
.map(Integer::parseInt)
.collect(Collectors.toList());
List<AdminUser> adminUsers = adminUserService.listByIds(adminUserIds);
String subject = dictionaryItemTranslateService.getValueByCode(Constant.DICTIONARY_EMAIL_NOTIFY, Constant.DICTIONARY_ITEM_EMAIL_TITLE_TICKET_INVITE_COMMENT, Constant.DEFAULT_LANGUAGE_CODE);
String content = dictionaryItemTranslateService.getValueByCode(Constant.DICTIONARY_EMAIL_NOTIFY, Constant.DICTIONARY_ITEM_EMAIL_CONTENT_TICKET_INVITE_COMMENT, Constant.DEFAULT_LANGUAGE_CODE)
.replace("${no}", ticket.getNo())
.replace("${title}", ticket.getTitle())
.replace("${createUser}", createUser.getUserName())
.replace("${handleUser}", StrUtil.join(",", adminUsers.stream().map(AdminUser::getUserName).collect(Collectors.toList())))
.replace("${createTime}", toTimeString(ticket.getCreateTime()))
.replace("${msg}", subject);
try {
sendEamilForAdminUser(adminUsers, subject, content);
} catch (Exception e) {
log.error("邮件发送失败", e);
}
}
});
}
/**
* 工单自动评论
* 每天午夜12点执行一次
*/
@Scheduled(cron = "0 0 0 * * ?")
public void autoComment() {
List<Ticket> tickets=ticketService.getNonComment(10);
TicketEvaluateAddVO vo=dictionaryItemTranslateService.getTicketEvaluateSelect(Constant.DEFAULT_LANGUAGE_CODE);
TicketEvaluateAddRequest request = new TicketEvaluateAddRequest();
request.setType(1);
request.setScore(new BigDecimal("5"));
TicketEvaluateItemVO se=vo.getServiceEvaluation().stream()
.filter(it->StrUtil.equals(it.getCode(), Constant.DICTIONARY_TYPE_SERVICE_EVALUATION+"Satisfied"))
.findFirst().get();
request.setServiceEvaluationId(se.getId());
request.setServiceEvaluationSelectIds(se.getChildren().stream().map(TicketEvaluateItemVO::getId).collect(Collectors.toList()));
TicketEvaluateItemVO pe=vo.getServiceEvaluation().stream()
.filter(it->StrUtil.equals(it.getCode(), Constant.DICTIONARY_TYPE_SERVICE_EVALUATION+"Satisfied"))
.findFirst().get();
request.setProductEvaluationId(pe.getId());
request.setProductEvaluationSelectIds(pe.getChildren().stream().map(TicketEvaluateItemVO::getId).collect(Collectors.toList()));
tickets.forEach(ticket -> {
request.setTicketId(ticket.getId());
ticketEvaluateService.add(request);
});
}
/**
* 工单超时提醒
* 每天午夜12点执行一次

View File

@ -290,9 +290,11 @@ public class TiketController extends ControllerBase {
VUtils.trueThrowBusinessError(Objects.isNull(ticket)).throwMessage("工单不存在");
VUtils.trueThrowBusinessError(Byte.compare(ticket.getState(), TicketState.Processing.getState()) > 1)
.throwMessage("当前工单状态不允许发送消息");
VUtils.trueThrowBusinessError(!Objects.equals(ticket.getUserId(),AppUserUtil.getUserId()))
.throwMessage("工单创建人才能发送消息");
AppUser user = appUserService.getById(AppUserUtil.getUserId());
ChatMessageDTO message = new ChatMessageDTO()
.setId(IdUtil.getSnowflakeNextId())
.setId(IdUtil.getSnowflakeNextIdStr())
.setFrom("app")
.setTicketState(ticket.getState())
.setSenderId(user.getId())

View File

@ -203,11 +203,12 @@ public class UserController extends ControllerBase {
public ApiResult<Void> changeUserEnable(@Valid @RequestBody EnableRequest request){
if (request.getEnable()){
AppUserApplyfor applyfor=appUserApplyforService.addEnable(request);
AppUser user=appUserService.getById(request.getId());
List<AdminUser> adminUsers=adminUserService.getForAccountReview();
if (CollectionUtil.isNotEmpty(adminUsers)){
adminUsers.forEach(c -> adminMessageService.add(
new AdminMessage()
.setNo(applyfor.getUserEmail())
.setNo(user.getEmail())
.setUserId(c.getId())
.setSourceId(applyfor.getId())
.setSource(1)

View File

@ -36,8 +36,12 @@ public class Constant {
public static final String DICTIONARY_ITEM_EMAIL_TITLE_TICKET_TIMEOUT="TitleTicketTimeout";
public static final String DICTIONARY_ITEM_EMAIL_TITLE_TICKET_INVITE_COMMENT="TitleInviteComment";
public static final String DICTIONARY_ITEM_EMAIL_CONTENT_TICKET_NOTIFY="TicketNotify";
public static final String DICTIONARY_ITEM_EMAIL_CONTENT_TICKET_INVITE_COMMENT="InviteComment";
public static final String DICTIONARY_ITEM_EMAIL_TITLE_ACCOUNT_RESET_PASSWORD="TitleResetPassword";
public static final String DICTIONARY_ITEM_EMAIL_TITLE_ACCOUNT_ACTIVATION="TitleAccountActivation";

View File

@ -11,7 +11,7 @@ import java.util.List;
@Accessors(chain = true)
public class ChatMessageDTO {
private Long id;
private String id;
//来源
private String from;

View File

@ -21,6 +21,9 @@ public class UserDTO {
//用户邮箱
private String email;
//是否是主账号
private Boolean isPrimary;
//公司id
private List<Integer> companyIds;
}

View File

@ -22,5 +22,5 @@ public class AddChatMessageRequest {
private List<String> images;
// 引用的消息
private Long quoteId;
private String quoteId;
}

View File

@ -1,5 +1,6 @@
package com.nflg.mobilebroken.common.pojo.request;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
import lombok.EqualsAndHashCode;
@ -12,4 +13,8 @@ public class AreaSearchRequest extends PageRequest {
//是否启用
private Boolean enabled;
//区域创建人id
@JsonIgnore
private Integer createBy;
}

View File

@ -1,5 +1,6 @@
package com.nflg.mobilebroken.common.pojo.request;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
import javax.validation.constraints.NotNull;
@ -32,4 +33,8 @@ public class TicketEvaluateAddRequest {
//反馈
private String feedback;
//来源0用户1系统
@JsonIgnore
private Integer type=0;
}

View File

@ -9,7 +9,7 @@ import java.util.List;
@Accessors(chain = true)
public class ChatMessageVO {
private Long id;
private String id;
//来源
private String from;

View File

@ -27,12 +27,18 @@ public class AppUserUtil {
return (List<Integer>) SaTokenAppUtil.getExtra("companyIds");
}
public static Boolean isPrimary() {
VUtils.trueThrow(!SaTokenAppUtil.isLogin()).throwMessage(STATE.LoginError,"请重新登录");
return (Boolean) SaTokenAppUtil.getExtra("isPrimary");
}
public static UserDTO getUser() {
UserDTO user = new UserDTO();
user.setId(getUserId());
user.setName(getUserName());
user.setEmail(getEmail());
user.setCompanyIds(getCompanyIds());
user.setIsPrimary(isPrimary());
return user;
}
}

View File

@ -3,12 +3,13 @@ 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 java.io.Serializable;
import java.time.LocalDateTime;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* <p>
* 用户端-区域
@ -48,6 +49,11 @@ public class AppArea implements Serializable {
*/
private String createBy;
/**
* 创建人id
*/
private Integer createById;
/**
* 创建时间
*/

View File

@ -35,6 +35,11 @@ public class TicketEvaluate implements Serializable {
*/
private Integer ticketId;
/**
* 来源0用户1系统
*/
private Integer type;
/**
* 售后服务评价
*/

View File

@ -45,4 +45,6 @@ public interface ITicketService extends IService<Ticket> {
Ticket reopen(Integer id);
Ticket addTicketHandle(TicketHandleAddRequest request);
List<Ticket> getNonComment(int days);
}

View File

@ -1,6 +1,7 @@
package com.nflg.mobilebroken.repository.service;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.util.StrUtil;
import com.nflg.mobilebroken.common.pojo.dto.ChatMessageDTO;
import com.nflg.mobilebroken.common.pojo.dto.TicketChatDTO;
import com.nflg.mobilebroken.common.pojo.vo.ChatMessageVO;
@ -69,13 +70,18 @@ public class TicketChatService {
public void addMessage(Integer ticketId, ChatMessageDTO newMessage) {
// 创建查询条件查找 ticketId = ticketId TicketChat
Query query = new Query(Criteria.where("ticketId").is(ticketId));
TicketChatDTO dto=mongoTemplate.findOne(query, TicketChatDTO.class);
if (Objects.isNull(dto)){
dto=new TicketChatDTO().setTicketId(ticketId);
mongoTemplate.save(dto);
}
// 创建更新操作 messages 列表中添加新消息
Update update = new Update().push("messages", newMessage);
// 执行更新操作
mongoTemplate.findAndModify(query, update, TicketChatDTO.class);
}
public ChatMessageDTO getMessage(Integer ticketId, Long messageId) {
public ChatMessageDTO getMessage(Integer ticketId, String messageId) {
// 构建查询条件
Query query = new Query();
query.addCriteria(Criteria.where("ticketId").is(ticketId));
@ -86,7 +92,7 @@ public class TicketChatService {
// TicketChatDTO 中提取 ChatMessageDTO
if (ticketChat != null) {
for (ChatMessageDTO message : ticketChat.getMessages()) {
if (messageId.equals(message.getId())) {
if (StrUtil.equals(messageId,message.getId())) {
return message;
}
}

View File

@ -14,9 +14,11 @@ import com.nflg.mobilebroken.common.util.AppUserUtil;
import com.nflg.mobilebroken.repository.entity.AppArea;
import com.nflg.mobilebroken.repository.mapper.AppAreaMapper;
import com.nflg.mobilebroken.repository.service.IAppAreaService;
import com.nflg.mobilebroken.repository.service.IAppUserService;
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.Iterator;
@ -35,10 +37,14 @@ import java.util.stream.Collectors;
@Service
public class AppAreaServiceImpl extends ServiceImpl<AppAreaMapper, AppArea> implements IAppAreaService {
@Resource
private IAppUserService appUserService;
@Override
public boolean saveOrUpdate(AppArea entity) {
if (Objects.isNull(entity.getId()) || entity.getId()<=0){
entity.setCreateBy(AppUserUtil.getUserName());
entity.setCreateById(AppUserUtil.getUserId());
entity.setCreateTime(LocalDateTime.now());
return save(entity);
}else {
@ -50,12 +56,18 @@ public class AppAreaServiceImpl extends ServiceImpl<AppAreaMapper, AppArea> impl
@Override
public IPage<AreaVO> search(AreaSearchRequest request) {
if (AppUserUtil.isPrimary()){
request.setCreateBy(AppUserUtil.getUserId());
}else {
request.setCreateBy(appUserService.getPrimaryByCompanyId(String.valueOf(AppUserUtil.getCompanyIds().get(0))).getId());
}
if (StrUtil.isBlank(request.getName()) && Objects.isNull(request.getEnabled())) {
return getPage(request);
}else {
LambdaQueryWrapper<AppArea> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.like(StrUtil.isNotBlank(request.getName()),AppArea::getName, request.getName());
queryWrapper.eq(AppArea::getCreateById, request.getCreateBy());
queryWrapper.eq(Objects.nonNull(request.getEnabled()),AppArea::getEnable, request.getEnabled());
queryWrapper.like(StrUtil.isNotBlank(request.getName()),AppArea::getName, request.getName());
queryWrapper.orderByDesc(AppArea::getId);
List<AppArea> list = baseMapper.selectList(queryWrapper);
//找出非根节点
@ -175,7 +187,9 @@ public class AppAreaServiceImpl extends ServiceImpl<AppAreaMapper, AppArea> impl
IPage<AppArea> page = new Page<>();
page.setCurrent(request.getPage());
page.setSize(request.getPageSize());
lambdaQuery().eq(AppArea::getParentId, 0)
lambdaQuery()
.eq(AppArea::getParentId, 0)
.eq(AppArea::getCreateById,request.getCreateBy())
.eq(Objects.nonNull(request.getEnabled()), AppArea::getEnable, request.getEnabled())
.orderByAsc(AppArea::getId)
.page(page);
@ -190,7 +204,8 @@ public class AppAreaServiceImpl extends ServiceImpl<AppAreaMapper, AppArea> impl
}
private List<AreaVO> getChildren(Integer parentId, Boolean enable) {
List<AreaVO> datas=convert(lambdaQuery().eq(AppArea::getParentId,parentId)
List<AreaVO> datas=convert(lambdaQuery()
.eq(AppArea::getParentId,parentId)
.eq(Objects.nonNull(enable), AppArea::getEnable, enable)
.orderByAsc(AppArea::getId)
.list());

View File

@ -127,8 +127,12 @@ public class AppUserApplyforServiceImpl extends ServiceImpl<AppUserApplyforMappe
AppUser appUser;
if (Objects.nonNull(applyfor.getUserId())) {
appUser = appUserService.getById(applyfor.getUserId());
if (Objects.isNull(applyfor.getAreaId())) {
applyfor.setAreaId(appUser.getAreaId());
}
if (Objects.isNull(applyfor.getTitleId())) {
applyfor.setTitleId(appUser.getTitleId());
}
List<TBaseCustomer> customers =customerService.listByIds(Arrays.stream(appUser.getCompanyId().split(",")).map(Integer::parseInt).collect(Collectors.toList()));
applyforInfo
.setCompanyName(StrUtil.join(",",customers.stream().map(TBaseCustomer::getAgencyCompanyName).collect(Collectors.toList())))

View File

@ -38,6 +38,7 @@ public class TicketEvaluateServiceImpl extends ServiceImpl<TicketEvaluateMapper,
&& !Objects.equals(ticket.getState(), TicketState.Closed.getState())).throwMessage("工单状态异常");
TicketEvaluate ticketEvaluate = new TicketEvaluate()
.setTicketId(request.getTicketId())
.setType(request.getType())
.setServiceEvaluation(request.getServiceEvaluationId())
.setServiceEvaluationSelect(StrUtil.join(",", request.getServiceEvaluationSelectIds()))
.setProductEvaluation(request.getProductEvaluationId())

View File

@ -112,8 +112,9 @@ public class TicketServiceImpl extends ServiceImpl<TicketMapper, Ticket> impleme
Ticket ticket = getById(request.getTicketId());
VUtils.trueThrowBusinessError(Objects.isNull(ticket)).throwMessage("未找到工单");
VUtils.trueThrowBusinessError(!Objects.equals(ticket.getState(), TicketState.PendingProcessing.getState())).throwMessage("工单状态异常");
// VUtils.trueThrowBusinessError(Objects.nonNull(ticket.getCqm()) && !Objects.equals(ticket.getCqm(), AdminUserUtil.getUserId()))
// .throwMessage("当前工单已归属别的CQM负责人");
VUtils.trueThrowBusinessError(adminUserService.getCQM().stream()
.noneMatch(u -> Objects.equals(u.getId(), AdminUserUtil.getUserId())))
.throwMessage("你不是CQM无权关闭工单");
ticket.setUrgency(TicketUrgency.findByValue(request.getUrgency()).getState());
ticket.setQuestion(request.getQuestion());
ticket.setState(TicketState.Processing.getState());
@ -168,7 +169,6 @@ public class TicketServiceImpl extends ServiceImpl<TicketMapper, Ticket> impleme
.noneMatch(uid->StrUtil.equals(uid, AdminUserUtil.getUserId().toString())))
.throwMessage("你无权操作该工单");
ticket.setState(TicketState.ProcessingCompleted.getState());
ticket.setSolveTime(LocalDateTime.now());
ticket.setCurrentHandle(AdminUserUtil.getUserId());
ticket.setUpdateTime(LocalDateTime.now());
updateById(ticket);
@ -181,8 +181,13 @@ public class TicketServiceImpl extends ServiceImpl<TicketMapper, Ticket> impleme
VUtils.trueThrowBusinessError(Objects.isNull(ticket)).throwMessage("未找到工单");
VUtils.trueThrowBusinessError(!Objects.equals(ticket.getState(), TicketState.ProcessingCompleted.getState()))
.throwMessage("工单状态不允许关闭");
// VUtils.trueThrowBusinessError(!Objects.equals(ticket.getCqm(), AdminUserUtil.getUserId()))
// .throwMessage("当前工单未归属当前CQM负责人");
VUtils.trueThrowBusinessError(!ticketEvaluateService.lambdaQuery()
.eq(TicketEvaluate::getTicketId, request.getTicketId())
.exists())
.throwMessage("工单尚未评价,不能关闭");
VUtils.trueThrowBusinessError(adminUserService.getCQM().stream()
.noneMatch(u -> Objects.equals(u.getId(), AdminUserUtil.getUserId())))
.throwMessage("你不是CQM无权关闭工单");
ticket.setState(TicketState.Closed.getState());
ticket.setSolution(request.getSolution());
ticket.setSolutionAttachments(StrUtil.join(",", request.getAttachments()));
@ -273,4 +278,13 @@ public class TicketServiceImpl extends ServiceImpl<TicketMapper, Ticket> impleme
updateById(ticket);
return ticket;
}
@Override
public List<Ticket> getNonComment(int days) {
return lambdaQuery()
.eq(Ticket::getState, TicketState.ProcessingCompleted.getState())
.ge(Ticket::getUpdateTime, LocalDateTime.now().minusDays(days))
.lt(Ticket::getUpdateTime, LocalDateTime.now().minusDays(days-1))
.list();
}
}

View File

@ -1,6 +1,6 @@
package com.nflg.mobilebroken.starter.config;
import com.aliyun.oss.ClientBuilderConfiguration;
import com.aliyun.oss.ClientConfiguration;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.common.auth.DefaultCredentialProvider;
@ -30,13 +30,16 @@ public class AliyunOSSConfig {
//@Resource
//private OSS ossClient;
@Bean
@Bean(destroyMethod = "shutdown")
public OSS ossClient() {
log.info("初始化阿里云OSS服务");
ClientConfiguration config = new ClientConfiguration();
config.setConnectionTimeout(10000); // 设置连接超时为 10
config.setSocketTimeout(600000); // 设置读取超时为 10 分钟
return OSSClientBuilder.create()
.endpoint(endpoint)
.credentialsProvider(new DefaultCredentialProvider(accessKeyId, accessKeySecret))
.clientConfiguration(new ClientBuilderConfiguration())
.clientConfiguration(config)
.region(region)
.build();
}