refactor(admin-device): 优化设备数据有效状态和数据类型处理

- 更新设备时将相同设备编号其他数据设置为无效状态
- 在设备列表实体中添加数据有效状态字段
- 统一设备删除方法参数类型从Integer改为Long
- 调整设备新增校验逻辑及删除接口参数类型
- 修改部件相关接口ID字段及参数类型为Long
- 迁移延期相关字段到派工实体和接口
- 优化派工单权限校验逻辑
- 修复消息发送中用户类型不符导致的问题

feat(file): 支持HEIC格式图片上传并转换为PNG

- 新增依赖支持HEIC图片转换
- 上传单个及多个文件时自动转换HEIC格式文件为PNG
- 调整文件上传路径生成规范
- 新增分片上传支持和相关接口调整

fix(redis): 修复工单消息未读标识的Redis键名错误

- 修正聊天消息未读标识存储的Redis key前缀错误
- 修复工单拒绝记录的Redis key存储逻辑错误

feat(global-advice): 添加文件上传大小超过限制的异常处理

- 优化全局异常处理类,新增MaxUploadSizeExceededException捕获
- 返回明确的上传文件大小限制提示信息

refactor(user): 统一执行人及创建更新人ID类型为Long或String

- 将外部用户、派工单及相关VO和实体中执行人ID改为Long
- 调整创建人、更新人字段类型为String,方便展示操作人员姓名
- 添加部门查询支持,递归查询子部门ID集合

chore(config): 调整文件上传配置提高最大文件大小及阈值

- 将单文件最大上传大小由100MB提升至500MB
- 设置多文件最大请求大小为500MB
- 增加文件阈值和上传临时路径配置

style(code): 统一代码格式与注释规范

- 统一空格和注解风格
- 修正代码缩进及多余空行
- 优化日志和异常信息输出格式
This commit is contained in:
曹鹏飞 2025-12-12 17:57:13 +08:00
parent 91c8edd53e
commit 73e4f4d0ee
32 changed files with 234 additions and 102 deletions

View File

@ -141,6 +141,17 @@
</exclusion>
</exclusions>
</dependency>
<!--处理heic图片文件-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-imaging</artifactId>
<version>1.0.0-alpha6</version>
</dependency>
<dependency>
<groupId>com.github.gotson.nightmonkeys</groupId>
<artifactId>imageio-heif</artifactId>
<version>1.1.0</version>
</dependency>
</dependencies>
<build>

View File

@ -22,6 +22,7 @@ import com.nflg.mobilebroken.starter.service.FileUploadService;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.apache.commons.imaging.Imaging;
import org.apache.commons.io.FilenameUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
@ -31,13 +32,13 @@ import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import javax.imageio.ImageIO;
import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.awt.image.BufferedImage;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.LocalDateTime;
@ -52,7 +53,6 @@ import java.util.zip.ZipOutputStream;
/**
* 文件管理相关接口
*
* @author 曹鹏飞
**/
@RestController
@ -69,25 +69,32 @@ public class FileController extends ControllerBase {
@Resource
private IFileUploadRecordService fileUploadRecordService;
private static final Map<String,String> FILE_PATH_MAP=new HashMap<>();
private static final Map<String, String> FILE_PATH_MAP = new HashMap<>();
/**
* 上传单个文件
* @param file 要上传的文件
* @param source 文件来源 0工单1代理商2管理端账户3匿名工单4派工单
* @param file 要上传的文件
* @param source 文件来源 0工单1代理商2管理端账户3匿名工单4派工单
* @param sourceId 文件来源id 对应来源的数据id比如工单id代理商id管理端账户id
* @return 可访问的文件url
*/
@PostMapping("uploadSingleFile")
@ApiMark(moduleName = "文件管理", apiName = "上传单个文件")
public ApiResult<FileUploadVO> uploadSingleFile(@Valid @NotNull @RequestParam("file") MultipartFile file
,@Valid @NotNull @RequestParam("source") Byte source
, @Valid @NotNull @RequestParam("source") Byte source
, @Valid @NotNull @RequestParam("sourceId") Long sourceId) {
try {
String fileName=file.getOriginalFilename();
String fileType=getFileType(fileName);
String url=fileUploadService.upload(buildFilePath(fileType), file);
FileUploadRecord record=buildFileUploadRecord(source,sourceId,fileName,fileType,url);
String fileName = file.getOriginalFilename();
String fileType = getFileType(fileName);
InputStream is;
if (fileType.equals(".heic")) {
is = convertHeic(file);
fileType = ".jpg";
} else {
is = file.getInputStream();
}
String url = fileUploadService.upload(buildFilePath(fileType), is);
FileUploadRecord record = buildFileUploadRecord(source, sourceId, fileName, fileType, url);
fileUploadRecordService.save(record);
return ApiResult.success(new FileUploadVO(record.getId(), fileName, url, file.getSize()));
} catch (Exception ex) {
@ -95,6 +102,14 @@ public class FileController extends ControllerBase {
}
}
private InputStream convertHeic(MultipartFile file) throws IOException {
BufferedImage image = Imaging.getBufferedImage(file.getBytes());
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
ImageIO.write(image, "PNG", outputStream);
byte[] imageBytes = outputStream.toByteArray();
return new ByteArrayInputStream(imageBytes);
}
private String buildFilePath(String fileType) {
return StrUtil.format("admin/{}/{}/{}/{}{}", LocalDateTime.now().format(FORMATTER), AdminUserUtil.getUserId()
, RandomUtil.randomString(4), IdUtil.fastUUID(), fileType);
@ -102,8 +117,8 @@ public class FileController extends ControllerBase {
/**
* 上传多个文件
* @param files 要上传的文件列表
* @param source 文件来源 0工单1代理商2管理端账户3匿名工单4派工单
* @param files 要上传的文件列表
* @param source 文件来源 0工单1代理商2管理端账户3匿名工单4派工单
* @param sourceId 文件来源id 对应来源的数据id比如工单id代理商id管理端账户id
* @return 可访问的文件url列表
*/
@ -111,15 +126,22 @@ public class FileController extends ControllerBase {
@PostMapping("uploadMultipleFiles")
@ApiMark(moduleName = "文件管理", apiName = "上传多个文件")
public ApiResult<List<FileUploadVO>> uploadMultipleFiles(@Valid @RequestParam("files") @NotEmpty List<MultipartFile> files
,@Valid @NotNull @RequestParam("source") Byte source
, @Valid @NotNull @RequestParam("source") Byte source
, @Valid @NotNull @RequestParam("sourceId") Long sourceId) {
try {
List<FileUploadVO> list = new ArrayList<>();
for (MultipartFile file : files) {
String fileName=file.getOriginalFilename();
String fileType=getFileType(fileName);
String url=fileUploadService.upload(buildFilePath(fileType), file);
FileUploadRecord record=buildFileUploadRecord(source,sourceId,fileName,fileType,url);
String fileName = file.getOriginalFilename();
String fileType = getFileType(fileName);
InputStream is;
if (fileType.equals(".heic")) {
is = convertHeic(file);
fileType = ".jpg";
} else {
is = file.getInputStream();
}
String url = fileUploadService.upload(buildFilePath(fileType), is);
FileUploadRecord record = buildFileUploadRecord(source, sourceId, fileName, fileType, url);
fileUploadRecordService.save(record);
list.add(new FileUploadVO(record.getId(), fileName, url, file.getSize()));
}
@ -137,17 +159,17 @@ public class FileController extends ControllerBase {
@PostMapping("uploadSingleFile1")
public ApiResult<FileUploadVO> uploadSingleFile1(@Valid @NotNull @RequestParam("file") MultipartFile file) {
try {
String fileName=file.getOriginalFilename();
String fileType=getFileType(fileName);
String url=fileUploadService.upload(buildFilePath(fileType), file);
String fileName = file.getOriginalFilename();
String fileType = getFileType(fileName);
String url = fileUploadService.upload(buildFilePath(fileType), file);
return ApiResult.success(new FileUploadVO(0, fileName, url, file.getSize()));
}catch (Exception ex){
throw new NflgException(STATE.BusinessError,"上传文件失败:"+ex.getMessage());
} catch (Exception ex) {
throw new NflgException(STATE.BusinessError, "上传文件失败:" + ex.getMessage());
}
}
private String getFileType(String fileName){
return "."+FilenameUtils.getExtension(fileName);
private String getFileType(String fileName) {
return "." + FilenameUtils.getExtension(fileName);
}
private FileUploadRecord buildFileUploadRecord(Byte source, Long sourceId, String fileName, String fileType, String url) {
@ -190,13 +212,12 @@ public class FileController extends ControllerBase {
*/
@PostMapping("getFileTypes")
@ApiMark(moduleName = "文件管理", apiName = "获取文件类型列表")
public ApiResult<List<String>> getFileTypes(){
public ApiResult<List<String>> getFileTypes() {
return ApiResult.success(fileUploadRecordService.getFileTypes());
}
/**
* 文件压缩下载
*
* @param request 请求参数
*/
@PostMapping("zipDownload")
@ -228,7 +249,7 @@ public class FileController extends ControllerBase {
byte[] zipContent = Files.readAllBytes(tempZipFile);
Files.deleteIfExists(tempZipFile);
HttpHeaders headers = new HttpHeaders();
headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename="+ DateTimeUtil.format(LocalDateTime.now(),"yyyyMMddHHmmss")+".zip");
headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + DateTimeUtil.format(LocalDateTime.now(), "yyyyMMddHHmmss") + ".zip");
return ResponseEntity.ok()
.headers(headers)
.contentType(MediaType.APPLICATION_OCTET_STREAM)
@ -242,25 +263,25 @@ public class FileController extends ControllerBase {
*/
@GetMapping("/multipart/init")
public ApiResult<String> initMultipartUpload(@Valid @RequestParam @NotBlank String fileName) {
String filePath=buildFilePath(getFileType(fileName));
String uploadId=fileUploadService.initMultipartUpload(filePath);
FILE_PATH_MAP.put(uploadId,filePath);
String filePath = buildFilePath(getFileType(fileName));
String uploadId = fileUploadService.initMultipartUpload(filePath);
FILE_PATH_MAP.put(uploadId, filePath);
return ApiResult.success(uploadId);
}
/**
* 上传分片
* @param file 要上传的分片文件名称为file
* @param uploadId 上传id
* @param file 要上传的分片文件名称为file
* @param uploadId 上传id
* @param chunkNumber 分片编号
*/
@PostMapping("/multipart/uploadChunk")
public ApiResult<Void> uploadChunk(@Valid @RequestParam("file") @NotNull MultipartFile file
,@Valid @RequestParam @NotBlank String uploadId
,@Valid @RequestParam @NotNull Integer chunkNumber) throws IOException {
, @Valid @RequestParam @NotBlank String uploadId
, @Valid @RequestParam @NotNull Integer chunkNumber) throws IOException {
String filePath = FILE_PATH_MAP.get(uploadId);
VUtils.trueThrowBusinessError(StrUtil.isBlank(filePath)).throwMessage("文件不存在");
fileUploadService.uploadChunk(file,filePath, uploadId, chunkNumber);
fileUploadService.uploadChunk(file, filePath, uploadId, chunkNumber);
return ApiResult.success();
}

View File

@ -739,7 +739,7 @@ public class TicketController extends ControllerBase {
MessageVO vo = new MessageVO();
List<ChatMessageVO> messageVOS = ticketChatService.getMessages(Long.valueOf(ticketId), userId);
vo.setMessages(messageVOS);
String key = "chatMessage:readed:" + ticketId + ":admin:" + userId;
String key = "chatMessage:notreaded:" + ticketId + ":admin:" + userId;
Set<String> readeds = stringRedisTemplate.opsForSet().members(key);
Set<String> notReadeds = new LinkedHashSet<>();
if (CollectionUtil.isEmpty(readeds)) {

View File

@ -387,7 +387,7 @@ public class TicketController extends ControllerBase {
MessageVO vo = new MessageVO();
List<ChatMessageVO> messageVOS = ticketChatService.getMessages(ticketId, userId);
vo.setMessages(messageVOS);
String key = "chatMessage:readed:" + ticketId + ":app:" + userId;
String key = "chatMessage:notreaded:" + ticketId + ":app:" + userId;
Set<String> readeds = stringRedisTemplate.opsForSet().members(key);
Set<String> notReadeds = new LinkedHashSet<>();
if (CollectionUtil.isEmpty(readeds)) {
@ -497,7 +497,7 @@ public class TicketController extends ControllerBase {
ssePushService.sendTicketMessageToAdmin(id, message);
ssePushService.sendTicketMessageToApp(id, message);
uniPushService.sendTodoMessageFromApp(String.valueOf(ticket.getId()), ticket.getType(), ticket.getCurrentHandle(), "工单被重启");
stringRedisTemplate.opsForValue().set(StrUtil.format(Constant.REDIS_KEY_TICKET_REJECT_RECORD, id), DateTimeUtil.format(LocalDateTime.now()));
stringRedisTemplate.opsForValue().set(StrUtil.format(Constant.REDIS_KEY_TICKET_REJECT_RECORD, ticket.getId()), DateTimeUtil.format(LocalDateTime.now()));
return ApiResult.success();
}

View File

@ -2,9 +2,11 @@ spring.application.name=cfs-app
spring.profiles.active=dev
server.port=8083
# 设置最大文件大小 (默认为1MB)
spring.servlet.multipart.max-file-size=100MB
spring.servlet.multipart.max-file-size=500MB
# 设置所有文件总大小 (默认为10MB)
spring.servlet.multipart.max-request-size=500MB
spring.servlet.multipart.file-size-threshold=10MB
spring.servlet.multipart.location=/tmp
#spring.config.import=classpath:application-${spring.profiles.active}.properties,nacos:
#spring.config.import=nacos:
logging.level.root=info

View File

@ -1,8 +1,11 @@
package com.nflg.mobilebroken.common.pojo.query;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.nflg.mobilebroken.common.pojo.request.PageRequest;
import lombok.Data;
import java.util.List;
@Data
public class ExternalUserSearchQuery extends PageRequest {
@ -20,4 +23,7 @@ public class ExternalUserSearchQuery extends PageRequest {
* 状态0已离职1在职
*/
private Integer state;
@JsonIgnore
private List<Long> departmentIds;
}

View File

@ -0,0 +1,9 @@
package com.nflg.mobilebroken.common.pojo.request;
import lombok.Data;
@Data
public class DeleteRequest1 {
private Long id;
}

View File

@ -43,7 +43,7 @@ public class DispatchAddRequest {
* 执行人id
*/
@NotNull
private Integer handlerUserId;
private Long handlerUserId;
/**
* 执行人

View File

@ -61,7 +61,7 @@ public class DispatchVO {
/**
* 执行人id
*/
private Integer handlerUserId;
private Long handlerUserId;
/**
* 执行人
@ -189,4 +189,9 @@ public class DispatchVO {
* 附件
*/
private List<GongfuFileVO> files;
/**
* 延期申请id
*/
private Long applyforId;
}

View File

@ -50,9 +50,9 @@ public class GongfuExternalUserVO {
private String remark;
/**
* 创建人id
* 创建人
*/
private Integer createBy;
private String createBy;
/**
* 创建时间
@ -60,9 +60,9 @@ public class GongfuExternalUserVO {
private LocalDateTime createTime;
/**
* 最后更新人id
* 最后更新人
*/
private Integer updateBy;
private String updateBy;
/**
* 最后更新时间

View File

@ -88,10 +88,8 @@ public class BasePartController extends ControllerBase {
*/
@GetMapping("getDetail")
@ApiMark(moduleName = "部件管理", apiName = "获取部件详情")
public ApiResult<BasePartDTO> getDetail(@RequestParam("id") Integer id) {
public ApiResult<BasePartDTO> getDetail(@RequestParam("id") Long id) {
GongfuDevicePart tBasePart = partService.getBaseMapper().selectById(id);
VUtils.trueThrow(tBasePart == null).throwMessage(STATE.ParamErr, "部件不存在");
BasePartDTO result = Convert.convert(BasePartDTO.class, tBasePart);
result.setLanguage(new ArrayList<>());
@ -123,7 +121,7 @@ public class BasePartController extends ControllerBase {
@PostMapping("del")
@MethodInfoMark(value = "删除", menuName = "部件管理")
public ApiResult<Boolean> del(@RequestBody List<Integer> ids) {
public ApiResult<Boolean> del(@RequestBody List<Long> ids) {
VUtils.trueThrow(CollUtil.isEmpty(ids)).throwMessage(STATE.ParamErr, "请选择要删除的行");
//检查部件是否已挂载到设备机型下
List<GongfuDeviceComponentDetail> deviceParts = componentDetailService.lambdaQuery()
@ -144,7 +142,6 @@ public class BasePartController extends ControllerBase {
* @throws IOException
*/
@PostMapping("exportData")
public void exportData(@RequestBody PartQuery query, HttpServletResponse response) throws IOException {
EecExcelUtil.setResponseExcelHeader(response, "部件列表");
List<ExportPartDTO> result = partService.exportPart(query.getPartNo(), query.getPartName());
@ -190,7 +187,7 @@ public class BasePartController extends ControllerBase {
*/
@PostMapping("enable")
@MethodInfoMark(value = "启用", menuName = "部件管理")
public ApiResult<Boolean> enable(@RequestBody List<Integer> ids) {
public ApiResult<Boolean> enable(@RequestBody List<Long> ids) {
VUtils.trueThrowBusinessError(CollUtil.isEmpty(ids)).throwMessage("请选择要启用的数据");
partService.lambdaUpdate().in(GongfuDevicePart::getId, ids).set(GongfuDevicePart::getEnable, 1).update();
return ApiResult.success(true);
@ -204,11 +201,9 @@ public class BasePartController extends ControllerBase {
*/
@PostMapping("disable")
@MethodInfoMark(value = "禁用", menuName = "部件管理")
public ApiResult<Boolean> disable(@RequestBody List<Integer> ids) {
public ApiResult<Boolean> disable(@RequestBody List<Long> ids) {
VUtils.trueThrowBusinessError(CollUtil.isEmpty(ids)).throwMessage("请选择要禁用的数据");
partService.lambdaUpdate().in(GongfuDevicePart::getId, ids).set(GongfuDevicePart::getEnable, 0).update();
return ApiResult.success(true);
}

View File

@ -169,10 +169,12 @@ public class DeviceController extends ControllerBase {
@ApiMark(moduleName = "设备管理", apiName = "新增")
public ApiResult<Boolean> add(@Valid @RequestBody DeviceDTO deviceDTO) {
VUtils.trueThrow(deviceService.lambdaQuery()
.eq(GongfuDevice::getDeviceNo, deviceDTO.getDeviceNo())
.eq(GongfuDevice::getDeviceState, deviceDTO.getDeviceState())
.eq(GongfuDevice::getDataValidState, true)
.exists()).throwMessage(STATE.ParamErr, deviceDTO.getDeviceNo() + "设备编号已存在");
.eq(GongfuDevice::getDeviceNo, deviceDTO.getDeviceNo())
.eq(GongfuDevice::getDeviceState, deviceDTO.getDeviceState())
.eq(GongfuDevice::getDataValidState, true)
.exists()
)
.throwMessage(STATE.ParamErr, deviceDTO.getDeviceNo() + "设备编号已存在");
adminDeviceService.add(deviceDTO);
return ApiResult.success(true);
}
@ -189,7 +191,7 @@ public class DeviceController extends ControllerBase {
@PostMapping("del")
@MethodInfoMark(value = "删除", menuName = "设备管理")
@ApiMark(moduleName = "设备管理", apiName = "删除")
public ApiResult<Boolean> del(@RequestBody List<Integer> ids) {
public ApiResult<Boolean> del(@RequestBody List<Long> ids) {
VUtils.trueThrow(CollUtil.isEmpty(ids)).throwMessage(STATE.ParamErr, "请选择要删除的数据行");
deviceService.batchDelByIds(ids);
return ApiResult.success(true);

View File

@ -105,6 +105,8 @@ public class DispatchController extends ControllerBase {
*/
@PostMapping("/update")
public ApiResult<Void> add(@Valid @RequestBody DispatchUpdateRequest request) {
GongfuDispatch old = dispatchService.getById(request.getId());
VUtils.trueThrowBusinessError(Objects.isNull(old)).throwMessage("无效的数据");
GongfuDispatch dispatch = new GongfuDispatch()
.setId(request.getId())
.setTitle(request.getTitle())
@ -116,13 +118,15 @@ public class DispatchController extends ControllerBase {
.setHandlerUserName(request.getHandlerUserName())
.setCategory(request.getCategory())
.setPlanStartDate(DateTimeUtil.format(request.getPlanStartDate(), "yyyy-MM-dd"))
.setState(request.getPlanStartDate().isBefore(LocalDate.now().plusDays(1)) ? 1 : 0)
.setPlanEndDate(DateTimeUtil.format(request.getPlanEndDate(), "yyyy-MM-dd"))
.setAddress(request.getAddress())
.setRemark(request.getRemark())
.setUpdateById(AdminUserUtil.getUserId())
.setUpdateBy(AdminUserUtil.getUserName())
.setUpdateTime(LocalDateTime.now());
if (!Objects.equals(old.getState(), 2)) {
dispatch.setState(request.getPlanStartDate().isBefore(LocalDate.now().plusDays(1)) ? 1 : 0);
}
if (Objects.nonNull(request.getActualStartDate())) {
dispatch.setActualStartDate(DateTimeUtil.format(request.getActualStartDate(), "yyyy-MM-dd"));
} else {
@ -153,7 +157,7 @@ public class DispatchController extends ControllerBase {
GongfuDispatch dispatch = dispatchService.getById(request.getTicketId());
VUtils.trueThrowBusinessError(Objects.isNull(dispatch)).throwMessage("派工单不存在");
VUtils.trueThrowBusinessError(Objects.equals(dispatch.getState(), 0)).throwMessage("当前派工单状态不允许完成");
VUtils.trueThrowBusinessError(!Objects.equals(dispatch.getHandlerUserId(), AdminUserUtil.getUserId())
VUtils.trueThrowBusinessError(!Objects.equals(dispatch.getHandlerUserId(), AdminUserUtil.getUserId().longValue())
&& !Objects.equals(dispatch.getCreateById(), AdminUserUtil.getUserId()))
.throwMessage("无权限完成派工单");
VUtils.trueThrowBusinessError(dispatchApplyforService.lambdaQuery()
@ -228,7 +232,7 @@ public class DispatchController extends ControllerBase {
@PostMapping("/applyfor/add")
public ApiResult<Void> applyforAdd(@Valid @RequestBody DispatchApplyForAddRequest request) {
GongfuDispatch dispatch = dispatchService.getById(request.getTicketId());
VUtils.trueThrowBusinessError(!Objects.equals(dispatch.getHandlerUserId(), AdminUserUtil.getUserId()))
VUtils.trueThrowBusinessError(!Objects.equals(dispatch.getHandlerUserId(), AdminUserUtil.getUserId().longValue()))
.throwMessage("你不是派工单处理人,无法申请延期");
VUtils.trueThrowBusinessError(Objects.equals(dispatch.getState(), 2)).throwMessage("已完成状态不能申请延期");
VUtils.trueThrowBusinessError(dispatchApplyforService.lambdaQuery()
@ -276,6 +280,7 @@ public class DispatchController extends ControllerBase {
vo.setApplyUser(adminUserService.getInfo(applyfor.getCreateById()));
DispatchApplyforAuditVO ao = Convert.convert(DispatchApplyforAuditVO.class, dispatch);
ao.setReason(applyfor.getReason());
ao.setDelayStartDate(applyfor.getStartDate());
ao.setDelayEndDate(applyfor.getEndDate());
vo.setApplyfor(ao);
return ApiResult.success(vo);

View File

@ -742,7 +742,7 @@ public class TicketController extends ControllerBase {
MessageVO vo = new MessageVO();
List<ChatMessageVO> messageVOS = ticketChatService.getMessages(ticketId, userId);
vo.setMessages(messageVOS);
String key = "chatMessage:readed:" + ticketId + ":admin:" + userId;
String key = "chatMessage:notreaded:" + ticketId + ":admin:" + userId;
Set<String> readeds = stringRedisTemplate.opsForSet().members(key);
Set<String> notReadeds = new LinkedHashSet<>();
if (CollectionUtil.isEmpty(readeds)) {
@ -898,6 +898,7 @@ public class TicketController extends ControllerBase {
.setIsRead(false)
.setCreateTime(LocalDateTime.now())
);
stringRedisTemplate.opsForValue().set(StrUtil.format(Constant.REDIS_KEY_TICKET_REJECT_RECORD, ticket.getId()), DateTimeUtil.format(LocalDateTime.now()));
}
return ApiResult.success();
}

View File

@ -4,7 +4,7 @@ import cn.hutool.core.convert.Convert;
import com.nflg.mobilebroken.common.pojo.ApiResult;
import com.nflg.mobilebroken.common.pojo.PageData;
import com.nflg.mobilebroken.common.pojo.query.ExternalUserSearchQuery;
import com.nflg.mobilebroken.common.pojo.request.DeleteRequest;
import com.nflg.mobilebroken.common.pojo.request.DeleteRequest1;
import com.nflg.mobilebroken.common.pojo.vo.GongfuExternalUserVO;
import com.nflg.mobilebroken.common.util.AdminUserUtil;
import com.nflg.mobilebroken.gongfu.annotation.ApiMark;
@ -43,7 +43,7 @@ public class UserController extends ControllerBase {
@ApiMark(moduleName = "外部用户管理", apiName = "新增用户")
public ApiResult<Void> add(@Valid @RequestBody ExternalUserAddQuery request) {
GongfuExternalUser user = Convert.convert(GongfuExternalUser.class, request);
user.setCreateBy(AdminUserUtil.getUserId());
user.setCreateBy(AdminUserUtil.getUserName());
user.setCreateTime(LocalDateTime.now());
gongfuExternalUserService.save(user);
return ApiResult.success();
@ -59,7 +59,7 @@ public class UserController extends ControllerBase {
public ApiResult<Void> update(@Valid @RequestBody ExternalUserUpdateQuery request) {
GongfuExternalUser user = Convert.convert(GongfuExternalUser.class, request);
user.setId(request.getId());
user.setUpdateBy(AdminUserUtil.getUserId());
user.setUpdateBy(AdminUserUtil.getUserName());
user.setUpdateTime(LocalDateTime.now());
gongfuExternalUserService.updateById(user);
return ApiResult.success();
@ -71,7 +71,7 @@ public class UserController extends ControllerBase {
**/
@PostMapping("delete")
@ApiMark(moduleName = "账号管理", apiName = "删除用户")
public ApiResult<Void> delete(@Valid @RequestBody DeleteRequest request) {
public ApiResult<Void> delete(@Valid @RequestBody DeleteRequest1 request) {
gongfuExternalUserService.removeById(request.getId());
return ApiResult.success();
}

View File

@ -11,6 +11,7 @@ import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationEvent;
import java.time.LocalDateTime;
import java.util.Objects;
@Slf4j
public class DispatchCreateEvent extends ApplicationEvent implements ApplicationContextAware {
@ -34,17 +35,19 @@ public class DispatchCreateEvent extends ApplicationEvent implements Application
private void sendUserMessage() {
//我的待办
adminMessageService.add(
new AdminMessage()
.setNo(dispatch.getNo())
.setTitle(dispatch.getTitle())
.setUserId(dispatch.getHandlerUserId())
.setSourceId(dispatch.getId())
.setSource(2)
.setType(MessageType.Dispatch.getState())
.setSubType(MessageSubType.DispatchCreate.getState())
.setIsRead(false)
.setCreateTime(LocalDateTime.now())
);
if (Objects.equals(dispatch.getHandlerUserType(), 1)) {
adminMessageService.add(
new AdminMessage()
.setNo(dispatch.getNo())
.setTitle(dispatch.getTitle())
.setUserId(Math.toIntExact(dispatch.getHandlerUserId()))
.setSourceId(dispatch.getId())
.setSource(2)
.setType(MessageType.Dispatch.getState())
.setSubType(MessageSubType.DispatchCreate.getState())
.setIsRead(false)
.setCreateTime(LocalDateTime.now())
);
}
}
}

View File

@ -14,7 +14,7 @@ public class BasePartDTO {
* id 自增id
*/
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
private Long id;
/**
* 部件编码

View File

@ -3,6 +3,7 @@ package com.nflg.mobilebroken.gongfu.pojo.query;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
@Data
public class ExternalUserAddQuery {
@ -19,4 +20,10 @@ public class ExternalUserAddQuery {
//职位id
private Integer titleId;
/**
* 状态0已离职1在职
*/
@NotNull
private Integer state = 1;
}

View File

@ -50,6 +50,11 @@ public class DispatchApplyforAuditVO {
*/
private String planEndDate;
/**
* 延期开始日期
*/
private String delayStartDate;
/**
* 延期结束日期
*/

View File

@ -73,6 +73,11 @@ public class AdminDeviceService {
device.setUpdateBy(AdminUserUtil.getUserName());
device.setUpdateTime(LocalDateTime.now());
deviceService.updateById(device);
deviceService.lambdaUpdate()
.set(GongfuDevice::getDataValidState, 0)
.eq(GongfuDevice::getDeviceNo, deviceDTO.getDeviceNo())
.ne(GongfuDevice::getId, device.getId())
.update();
//将设备类型放入-设备类型表维护客户质量管理人
Map<String, Set<String>> dataTypes = new HashMap<>();
dataTypes.put(deviceDTO.getProductLine(), Set.of(deviceDTO.getModelNo()));
@ -211,6 +216,7 @@ public class AdminDeviceService {
ent.setAgentName(customer.getAgencyCompanyName());
}
ent.setAddress(u.getSpecificAddress__c());
ent.setDataValidState(false);
result.add(ent);
}
}

View File

@ -53,7 +53,7 @@ public class TicketScheduledTasks {
private IAppUserService appUserService;
@Resource
private IGongfuTicketFollowService ticketFollowService;
private ITicketFollowService ticketFollowService;
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern(DatePattern.NORM_DATETIME_PATTERN);
@ -70,7 +70,7 @@ public class TicketScheduledTasks {
private IParamConfigService paramConfigService;
@Resource
private IGongfuTicketEvaluateService ticketEvaluateService;
private ITicketEvaluateService ticketEvaluateService;
@Resource
private IGongfuDeviceService deviceService;
@ -140,16 +140,18 @@ public class TicketScheduledTasks {
request.setScore(new BigDecimal("5"));
TicketEvaluateItemVO se = vo.getServiceEvaluation().stream()
.filter(it -> StrUtil.equals(it.getCode(), Constant.DICTIONARY_TYPE_SERVICE_EVALUATION + "Satisfied"))
.findFirst().get();
.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();
.findFirst()
.get();
request.setProductEvaluationId(pe.getId());
request.setProductEvaluationSelectIds(pe.getChildren().stream().map(TicketEvaluateItemVO::getId).collect(Collectors.toList()));
tickets.forEach(ticket -> {
request.setTicketId(Long.valueOf(ticket.getId()));
request.setTicketId(ticket.getId());
ticketEvaluateService.add(request);
if (ticketService.close(ticket)) {
ticketEventPublisher.publishTicketCloseEvent(ticket);
@ -259,11 +261,11 @@ public class TicketScheduledTasks {
adminUserIds.addAll(cqms);
//管理端关注人
List<Integer> followUserIds = ticketFollowService.lambdaQuery()
.eq(GongfuTicketFollow::getTicketId, ticket.getId())
.eq(GongfuTicketFollow::getFrom, (byte) 1)
.eq(TicketFollow::getTicketId, ticket.getId())
.eq(TicketFollow::getFrom, (byte) 1)
.list()
.stream()
.map(GongfuTicketFollow::getUserId)
.map(TicketFollow::getUserId)
.collect(Collectors.toList());
if (CollectionUtil.isNotEmpty(followUserIds)) {
adminUserIds.addAll(followUserIds);
@ -286,11 +288,11 @@ public class TicketScheduledTasks {
}
//客户端关注人
Set<Integer> appUserIds = ticketFollowService.lambdaQuery()
.eq(GongfuTicketFollow::getTicketId, ticket.getId())
.eq(GongfuTicketFollow::getFrom, (byte) 0)
.eq(TicketFollow::getTicketId, ticket.getId())
.eq(TicketFollow::getFrom, (byte) 0)
.list()
.stream()
.map(GongfuTicketFollow::getUserId)
.map(TicketFollow::getUserId)
.collect(Collectors.toSet());
appUserIds.add(ticket.getUserId());
List<AppUser> appUsers = appUserService.listByIds(appUserIds);

View File

@ -60,7 +60,7 @@ public class GongfuDispatch implements Serializable {
/**
* 执行人id
*/
private Integer handlerUserId;
private Long handlerUserId;
/**
* 执行人

View File

@ -56,9 +56,9 @@ public class GongfuExternalUser implements Serializable {
private String remark;
/**
* 创建人id
* 创建人
*/
private Integer createBy;
private String createBy;
/**
* 创建时间
@ -66,9 +66,9 @@ public class GongfuExternalUser implements Serializable {
private LocalDateTime createTime;
/**
* 最后更新人id
* 最后更新人
*/
private Integer updateBy;
private String updateBy;
/**
* 最后更新时间

View File

@ -33,7 +33,7 @@ public interface GongfuDeviceMapper extends BaseMapper<GongfuDevice> {
*/
Page<GongfuDevice> getList(@Param("page") Page<PageBaseQuery> page, PageBaseQuery query);
void batchDelByIds(@Param("ids") List<Integer> ids);
void batchDelByIds(@Param("ids") List<Long> ids);
Page<DeviceVO> searchDevice(SearchDeviceRequest request, List<Integer> companyIds, String language, Page<?> page);

View File

@ -26,7 +26,7 @@ public interface IGongfuDeviceService extends IService<GongfuDevice> {
Page<GongfuDevice> getList(@Param("page") Page<PageBaseQuery> page, PageBaseQuery query);
void batchDelByIds(@Param("ids") List<Integer> ids);
void batchDelByIds(@Param("ids") List<Long> ids);
Page<DeviceVO> searchDevice(SearchDeviceRequest request, String language);

View File

@ -315,6 +315,7 @@ public class AdminUserServiceImpl extends ServiceImpl<AdminUserMapper, AdminUser
.setPhone(user.getPhone())
.setUserName(user.getUserName())
.setTitleName(Objects.nonNull(title) ? title.getPositionName() : "")
.setDepartmentId(user.getDepartmentId())
.setDepartmentName(getDepartmentName(user.getDepartmentId()));
}

View File

@ -46,7 +46,7 @@ public class GongfuDeviceServiceImpl extends ServiceImpl<GongfuDeviceMapper, Gon
return this.getBaseMapper().getList(page, query);
}
public void batchDelByIds(@Param("ids") List<Integer> ids) {
public void batchDelByIds(@Param("ids") List<Long> ids) {
this.getBaseMapper().batchDelByIds(ids);
}

View File

@ -1,15 +1,24 @@
package com.nflg.mobilebroken.repository.service.impl;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.nflg.mobilebroken.common.pojo.query.ExternalUserSearchQuery;
import com.nflg.mobilebroken.common.pojo.vo.GongfuExternalUserVO;
import com.nflg.mobilebroken.repository.entity.GongfuExternalUser;
import com.nflg.mobilebroken.repository.entity.TBaseDepartment;
import com.nflg.mobilebroken.repository.mapper.GongfuExternalUserMapper;
import com.nflg.mobilebroken.repository.service.IGongfuExternalUserService;
import com.nflg.mobilebroken.repository.service.ITBaseDepartmentService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
/**
* <p>
* 服务实现类
@ -20,8 +29,31 @@ import org.springframework.stereotype.Service;
@Service
public class GongfuExternalUserServiceImpl extends ServiceImpl<GongfuExternalUserMapper, GongfuExternalUser> implements IGongfuExternalUserService {
@Resource
private ITBaseDepartmentService departmentService;
@Override
public IPage<GongfuExternalUserVO> search(ExternalUserSearchQuery request) {
if (Objects.nonNull(request.getDepartmentId())) {
List<Long> ids = new ArrayList<>();
ids.add(request.getDepartmentId());
getChildrenDepartments(request.getDepartmentId(), ids);
request.setDepartmentIds(ids);
}
return baseMapper.search(request, new Page<>(request.getPage(), request.getPageSize()));
}
private void getChildrenDepartments(Long deptId, List<Long> ids) {
List<Long> tids = departmentService.lambdaQuery()
.select(TBaseDepartment::getId)
.eq(TBaseDepartment::getDeptParentId, deptId)
.list()
.stream()
.map(TBaseDepartment::getId)
.collect(Collectors.toList());
if (CollectionUtil.isNotEmpty(tids)) {
ids.addAll(tids);
tids.forEach(id -> getChildrenDepartments(id, ids));
}
}
}

View File

@ -276,6 +276,7 @@ public class TicketSolutionServiceImpl extends ServiceImpl<TicketSolutionMapper,
.set(FileUploadRecord::getTag, "案例照片")
.eq(FileUploadRecord::getSource, (byte) 0)
.eq(FileUploadRecord::getSourceId, request.getTicketId())
.in(FileUploadRecord::getId, request.getImageIds())
.update();
}
return ticket;

View File

@ -3,11 +3,12 @@
<mapper namespace="com.nflg.mobilebroken.repository.mapper.GongfuDispatchMapper">
<select id="search" resultType="com.nflg.mobilebroken.common.pojo.vo.DispatchVO">
SELECT da.*,dv.customer_name,bc.agency_company_name as agent_name,af.reason as "delayReason"
SELECT da.*,dv.customer_name,bc.agency_company_name as agent_name,af.reason as "delayReason",af2.id as "applyforId"
FROM gongfu_dispatch da
LEFT JOIN v_gongfu_device dv ON da.device_no=dv.device_no
LEFT JOIN t_base_customer bc ON dv.agent_code=bc.agency_company_code
left join v_dispatch_applyfor af on da.id=af.ticket_id
left join gongfu_dispatch_applyfor af2 on da.id=af2.ticket_id and af2.audit_state=0
<where>
<if test="request.deviceNo!=null and request.deviceNo!=''">
AND da.device_no=#{request.deviceNo}

View File

@ -17,6 +17,12 @@
<if test="request.state!=null">
and geu.state = #{request.state}
</if>
<if test="request.departmentIds!=null">
and geu.department_id in
<foreach item="item" index="index" collection="request.departmentIds" open="(" close=")" separator=",">
#{item}
</foreach>
</if>
</where>
order by geu.id desc
</select>

View File

@ -8,13 +8,16 @@ import com.nflg.mobilebroken.common.exception.NflgException;
import com.nflg.mobilebroken.common.pojo.ApiResult;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.util.unit.DataSize;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.multipart.MaxUploadSizeExceededException;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
@ -27,6 +30,9 @@ import java.util.stream.Collectors;
@Slf4j
public class GlobalRestControllerAdvice {
@Value("${spring.servlet.multipart.max-file-size}")
private DataSize maxFileSize;
@ExceptionHandler(SQLException.class)
public ApiResult<Void> handleSQLException(SQLException ex) {
log.error("数据库错误: ", ex);
@ -73,4 +79,9 @@ public class GlobalRestControllerAdvice {
public String handleNotLoginException(NotLoginException e) {
return "请重新登录";
}
@ExceptionHandler(MaxUploadSizeExceededException.class)
public ApiResult<Void> handleMaxSizeException(MaxUploadSizeExceededException e) {
return ApiResult.error(STATE.UploadError, "上传文件不能超过" + maxFileSize.toMegabytes() + "MB");
}
}