fix(日志管理): 修复日志管理的一些问题。
fix(项目任务): 任务完成后需要依然能够修改工作日志,但是只能修改工作内容和上传附件;任务完成后,协办人的工作日志不应该能删除、所有任务里的成员不能新增工作日志。
This commit is contained in:
@@ -184,8 +184,10 @@ public interface ErrorCodeConstants {
|
||||
ErrorCode PROJECT_TASK_WORKLOG_DELETE_FORBIDDEN = new ErrorCode(1_008_006_006, "仅记录填报人或任务负责人可删除该工时记录");
|
||||
ErrorCode PROJECT_TASK_WORKLOG_DATE_RANGE_INVALID = new ErrorCode(1_008_006_007, "段起始日期不能晚于段结束日期");
|
||||
ErrorCode PROJECT_TASK_WORKLOG_DATE_OVERLAP = new ErrorCode(1_008_006_008, "日期范围与该任务下您已有的工时记录重叠");
|
||||
ErrorCode PROJECT_TASK_WORKLOG_DELETE_NOT_ALLOWED_BY_TASK_STATUS = new ErrorCode(1_008_006_009, "当前任务状态不允许删除工作日志");
|
||||
ErrorCode PROJECT_TASK_WORKLOG_PROGRESS_NOT_MONOTONIC = new ErrorCode(1_008_006_010, "工时进度与日期顺序不一致:早段进度不得高于晚段、晚段进度不得低于早段");
|
||||
ErrorCode PROJECT_TASK_WORKLOG_DIFFICULTY_INVALID = new ErrorCode(1_008_006_011, "完成难度不在字典范围内");
|
||||
ErrorCode PROJECT_TASK_WORKLOG_CREATE_NOT_ALLOWED_BY_TASK_STATUS = new ErrorCode(1_008_006_012, "当前任务状态不允许新增工作日志");
|
||||
|
||||
// ========== 任务 / 工时附件 1_008_010_xxx(原 1_008_007 与下方项目需求段撞号,迁至独立号段;新增错误码域请从 1_008_011 起) ==========
|
||||
ErrorCode PROJECT_TASK_ATTACHMENT_TOO_MANY = new ErrorCode(1_008_010_001, "附件数量不能超过 {} 个");
|
||||
|
||||
@@ -105,6 +105,7 @@ public class TaskWorklogServiceImpl implements TaskWorklogService {
|
||||
permission = ProjectTaskConstants.PERMISSION_WORKLOG)
|
||||
public Long createWorklog(Long projectId, Long executionId, Long taskId, TaskWorklogSaveReqVO reqVO) {
|
||||
ProjectTaskDO task = validateEditableContext(projectId, executionId, taskId);
|
||||
validateCreateAllowed(task);
|
||||
validateLeafTask(taskId);
|
||||
Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
|
||||
validateFileWorklogPermission(taskId, task.getOwnerId(), loginUserId);
|
||||
@@ -177,6 +178,7 @@ public class TaskWorklogServiceImpl implements TaskWorklogService {
|
||||
permission = ProjectTaskConstants.PERMISSION_WORKLOG)
|
||||
public void deleteWorklog(Long projectId, Long executionId, Long taskId, Long worklogId) {
|
||||
ProjectTaskDO task = validateEditableContext(projectId, executionId, taskId);
|
||||
validateDeleteAllowed(task);
|
||||
TaskWorklogDO worklog = loadWorklog(worklogId, taskId);
|
||||
Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
|
||||
boolean isFiler = Objects.equals(worklog.getUserId(), loginUserId);
|
||||
@@ -240,24 +242,30 @@ public class TaskWorklogServiceImpl implements TaskWorklogService {
|
||||
ProjectExecutionDO execution = validateExecutionExists(projectId, executionId);
|
||||
validateAllowEdit(ProjectExecutionConstants.OBJECT_TYPE, execution.getStatusCode());
|
||||
ProjectTaskDO task = validateTaskExists(projectId, executionId, taskId);
|
||||
// 任务层放宽:completed 状态下非 owner(即协办人)允许继续维护自己的工时(§4.2.4 矩阵)。
|
||||
// 其他状态(pending / active / paused / cancelled)仍按 allow_edit 判定,owner 在 completed 下被拦截,
|
||||
// 避免与"完成时硬置进度 100%"冲突。
|
||||
if (!isCompletedAssigneeWorklogContext(task)) {
|
||||
// 工作日志维护在任务 completed 状态下统一放行;新增/删除是否允许由各自接口单独判定。
|
||||
if (!isCompletedWorklogContext(task)) {
|
||||
validateAllowEdit(ProjectTaskConstants.OBJECT_TYPE, task.getStatusCode());
|
||||
}
|
||||
return task;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否处于"任务已完成、协办人维护工时"的放行场景。
|
||||
* 是否处于“任务已完成”的工作日志维护场景。
|
||||
*/
|
||||
private boolean isCompletedAssigneeWorklogContext(ProjectTaskDO task) {
|
||||
if (!"completed".equals(task.getStatusCode())) {
|
||||
return false;
|
||||
private boolean isCompletedWorklogContext(ProjectTaskDO task) {
|
||||
return "completed".equals(task.getStatusCode());
|
||||
}
|
||||
|
||||
private void validateCreateAllowed(ProjectTaskDO task) {
|
||||
if (isCompletedWorklogContext(task)) {
|
||||
throw exception(ErrorCodeConstants.PROJECT_TASK_WORKLOG_CREATE_NOT_ALLOWED_BY_TASK_STATUS);
|
||||
}
|
||||
}
|
||||
|
||||
private void validateDeleteAllowed(ProjectTaskDO task) {
|
||||
if (isCompletedWorklogContext(task)) {
|
||||
throw exception(ErrorCodeConstants.PROJECT_TASK_WORKLOG_DELETE_NOT_ALLOWED_BY_TASK_STATUS);
|
||||
}
|
||||
Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
|
||||
return loginUserId != null && !Objects.equals(loginUserId, task.getOwnerId());
|
||||
}
|
||||
|
||||
private void validateExecutionAndTaskExists(Long projectId, Long executionId, Long taskId) {
|
||||
|
||||
@@ -4,14 +4,15 @@ import com.njcn.rdms.framework.apilog.core.annotation.ApiAccessLog;
|
||||
import com.njcn.rdms.framework.common.pojo.CommonResult;
|
||||
import com.njcn.rdms.framework.common.pojo.PageParam;
|
||||
import com.njcn.rdms.framework.common.pojo.PageResult;
|
||||
import com.njcn.rdms.framework.common.util.collection.CollectionUtils;
|
||||
import com.njcn.rdms.framework.common.util.object.BeanUtils;
|
||||
import com.njcn.rdms.framework.excel.core.util.ExcelUtils;
|
||||
import com.njcn.rdms.framework.translate.core.TranslateUtils;
|
||||
import com.njcn.rdms.module.system.controller.admin.logger.vo.operatelog.OperateLogPageReqVO;
|
||||
import com.njcn.rdms.module.system.controller.admin.logger.vo.operatelog.OperateLogRespVO;
|
||||
import com.njcn.rdms.module.system.dal.dataobject.logger.OperateLogDO;
|
||||
import com.njcn.rdms.module.system.dal.dataobject.user.AdminUserDO;
|
||||
import com.njcn.rdms.module.system.service.logger.OperateLogService;
|
||||
import com.fhs.core.trans.anno.TransMethodResult;
|
||||
import com.njcn.rdms.module.system.service.user.AdminUserService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
@@ -26,7 +27,9 @@ import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static com.njcn.rdms.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
|
||||
import static com.njcn.rdms.framework.common.pojo.CommonResult.success;
|
||||
@@ -40,34 +43,53 @@ public class OperateLogController {
|
||||
@Resource
|
||||
private OperateLogService operateLogService;
|
||||
|
||||
@Resource
|
||||
private AdminUserService adminUserService;
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "查看操作日志")
|
||||
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('system:operate-log:query')")
|
||||
public CommonResult<OperateLogRespVO> getOperateLog(@RequestParam("id") Long id) {
|
||||
OperateLogDO operateLog = operateLogService.getOperateLog(id);
|
||||
return success(BeanUtils.toBean(operateLog, OperateLogRespVO.class));
|
||||
OperateLogRespVO respVO = BeanUtils.toBean(operateLogService.getOperateLog(id), OperateLogRespVO.class);
|
||||
fillUserNames(Collections.singletonList(respVO));
|
||||
return success(respVO);
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "查看操作日志分页列表")
|
||||
@PreAuthorize("@ss.hasPermission('system:operate-log:query')")
|
||||
@TransMethodResult
|
||||
public CommonResult<PageResult<OperateLogRespVO>> pageOperateLog(@Valid OperateLogPageReqVO pageReqVO) {
|
||||
PageResult<OperateLogDO> pageResult = operateLogService.getOperateLogPage(pageReqVO);
|
||||
return success(BeanUtils.toBean(pageResult, OperateLogRespVO.class));
|
||||
PageResult<OperateLogRespVO> respPageResult = BeanUtils.toBean(pageResult, OperateLogRespVO.class);
|
||||
fillUserNames(respPageResult.getList());
|
||||
return success(respPageResult);
|
||||
}
|
||||
|
||||
@Operation(summary = "导出操作日志")
|
||||
@GetMapping("/export-excel")
|
||||
@PreAuthorize("@ss.hasPermission('system:operate-log:export')")
|
||||
@TransMethodResult
|
||||
@ApiAccessLog(operateType = EXPORT)
|
||||
public void exportOperateLog(HttpServletResponse response, @Valid OperateLogPageReqVO exportReqVO) throws IOException {
|
||||
exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
|
||||
List<OperateLogDO> list = operateLogService.getOperateLogPage(exportReqVO).getList();
|
||||
ExcelUtils.write(response, "操作日志.xls", "数据列表", OperateLogRespVO.class,
|
||||
TranslateUtils.translate(BeanUtils.toBean(list, OperateLogRespVO.class)));
|
||||
List<OperateLogRespVO> respVOList = BeanUtils.toBean(
|
||||
operateLogService.getOperateLogPage(exportReqVO).getList(), OperateLogRespVO.class);
|
||||
fillUserNames(respVOList);
|
||||
ExcelUtils.write(response, "操作日志.xls", "数据列表", OperateLogRespVO.class, respVOList);
|
||||
}
|
||||
|
||||
private void fillUserNames(List<OperateLogRespVO> respVOList) {
|
||||
if (respVOList == null || respVOList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
Map<Long, AdminUserDO> userMap = adminUserService.getUserMap(
|
||||
CollectionUtils.convertSet(respVOList, OperateLogRespVO::getUserId));
|
||||
respVOList.forEach(respVO -> {
|
||||
AdminUserDO user = userMap.get(respVO.getUserId());
|
||||
if (user != null) {
|
||||
respVO.setUserName(user.getNickname());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -13,21 +13,24 @@ import static com.njcn.rdms.framework.common.util.date.DateUtils.FORMAT_YEAR_MON
|
||||
@Data
|
||||
public class OperateLogPageReqVO extends PageParam {
|
||||
|
||||
@Schema(description = "用户编号", example = "灿能")
|
||||
@Schema(description = "用户编号", example = "1")
|
||||
private Long userId;
|
||||
|
||||
@Schema(description = "操作模块业务编号", example = "1")
|
||||
private Long bizId;
|
||||
|
||||
@Schema(description = "操作模块,模拟匹配", example = "订单")
|
||||
@Schema(description = "操作模块,模糊匹配", example = "订单")
|
||||
private String type;
|
||||
|
||||
@Schema(description = "操作名,模拟匹配", example = "创建订单")
|
||||
@Schema(description = "操作名称,模糊匹配", example = "创建订单")
|
||||
private String subType;
|
||||
|
||||
@Schema(description = "操作明细,模拟匹配", example = "修改编号为 1 的用户信息")
|
||||
@Schema(description = "操作明细,模糊匹配", example = "修改编号为 1 的用户信息")
|
||||
private String action;
|
||||
|
||||
@Schema(description = "请求方式", example = "GET")
|
||||
private String requestMethod;
|
||||
|
||||
@Schema(description = "开始时间", example = "[2022-07-01 00:00:00,2022-07-01 23:59:59]")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime[] createTime;
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
package com.njcn.rdms.module.system.controller.admin.logger.vo.operatelog;
|
||||
|
||||
import com.njcn.rdms.framework.excel.core.annotations.DictFormat;
|
||||
import com.njcn.rdms.module.system.dal.dataobject.user.AdminUserDO;
|
||||
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
|
||||
import cn.idev.excel.annotation.ExcelProperty;
|
||||
import com.njcn.rdms.module.system.enums.DictTypeConstants;
|
||||
import com.fhs.core.trans.anno.Trans;
|
||||
import com.fhs.core.trans.constant.TransType;
|
||||
import com.fhs.core.trans.vo.VO;
|
||||
import com.njcn.rdms.framework.excel.core.annotations.DictFormat;
|
||||
import com.njcn.rdms.module.system.enums.DictTypeConstants;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import lombok.Data;
|
||||
@@ -27,8 +24,8 @@ public class OperateLogRespVO implements VO {
|
||||
private String traceId;
|
||||
|
||||
@Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
@Trans(type = TransType.SIMPLE, target = AdminUserDO.class, fields = "nickname", ref = "userName")
|
||||
private Long userId;
|
||||
|
||||
@Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "awen")
|
||||
@ExcelProperty("操作人")
|
||||
private String userName;
|
||||
@@ -42,18 +39,18 @@ public class OperateLogRespVO implements VO {
|
||||
@ExcelProperty("操作模块类型")
|
||||
private String type;
|
||||
|
||||
@Schema(description = "操作名", requiredMode = Schema.RequiredMode.REQUIRED, example = "创建订单")
|
||||
@ExcelProperty("操作名")
|
||||
@Schema(description = "操作名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "创建订单")
|
||||
@ExcelProperty("操作名称")
|
||||
private String subType;
|
||||
|
||||
@Schema(description = "操作模块业务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@ExcelProperty("操作模块业务编号")
|
||||
private Long bizId;
|
||||
|
||||
@Schema(description = "操作明细", example = "修改编号为 1 的用户信息,将性别从男改成女,将姓名从灿能改成源码。")
|
||||
@Schema(description = "操作明细", example = "修改编号为 1 的用户信息,将性别从男改成女,将姓名从芋艿改成源码。")
|
||||
private String action;
|
||||
|
||||
@Schema(description = "拓展字段", example = "{'orderId': 1}")
|
||||
@Schema(description = "扩展字段", example = "{'orderId': 1}")
|
||||
private String extra;
|
||||
|
||||
@Schema(description = "请求方法名", requiredMode = Schema.RequiredMode.REQUIRED, example = "GET")
|
||||
|
||||
@@ -18,6 +18,7 @@ public interface OperateLogMapper extends BaseMapperX<OperateLogDO> {
|
||||
.likeIfPresent(OperateLogDO::getType, pageReqDTO.getType())
|
||||
.likeIfPresent(OperateLogDO::getSubType, pageReqDTO.getSubType())
|
||||
.likeIfPresent(OperateLogDO::getAction, pageReqDTO.getAction())
|
||||
.eqIfPresent(OperateLogDO::getRequestMethod, pageReqDTO.getRequestMethod())
|
||||
.betweenIfPresent(OperateLogDO::getCreateTime, pageReqDTO.getCreateTime())
|
||||
.orderByDesc(OperateLogDO::getId));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user