feat(project): 新增里程碑与任务管理控制器
- 创建ProjectMilestoneController,实现里程碑的增删改查及进度状态更新 - 创建TaskController,实现任务的增删改查及进度状态更新 - 添加里程碑关键任务的延期及即将到期查询接口 - 添加任务我的待办及依赖关系查询接口 - 在SysUserMapper新增根据真实姓名查询用户接口 - 在ProjectServiceImpl中根据成员真实姓名匹配用户ID,完善成员信息关联 - 更新SysUserMapper.xml添加根据真实姓名查询SQL语句
This commit is contained in:
@@ -0,0 +1,212 @@
|
||||
package cn.yinlihupo.controller.project;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import cn.yinlihupo.common.core.BaseResponse;
|
||||
import cn.yinlihupo.common.util.ResultUtils;
|
||||
import cn.yinlihupo.domain.entity.ProjectMilestone;
|
||||
import cn.yinlihupo.mapper.ProjectMilestoneMapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 项目里程碑管理控制器
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/milestone")
|
||||
@RequiredArgsConstructor
|
||||
public class ProjectMilestoneController {
|
||||
|
||||
private final ProjectMilestoneMapper milestoneMapper;
|
||||
|
||||
/**
|
||||
* 分页查询里程碑列表
|
||||
*/
|
||||
@SaCheckPermission("project:milestone:list")
|
||||
@GetMapping("/list")
|
||||
public BaseResponse<Page<Map<String, Object>>> list(
|
||||
@RequestParam(defaultValue = "1") Integer pageNum,
|
||||
@RequestParam(defaultValue = "10") Integer pageSize,
|
||||
@RequestParam(required = false) Long projectId,
|
||||
@RequestParam(required = false) String status) {
|
||||
|
||||
Page<Map<String, Object>> page = new Page<>(pageNum, pageSize);
|
||||
List<Map<String, Object>> records = milestoneMapper.selectMilestoneListWithStats(projectId, status);
|
||||
|
||||
page.setRecords(records);
|
||||
page.setTotal(records.size());
|
||||
|
||||
return ResultUtils.success("查询成功", page);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID查询里程碑详情
|
||||
*/
|
||||
@SaCheckPermission("project:milestone:view")
|
||||
@GetMapping("/{id}")
|
||||
public BaseResponse<ProjectMilestone> getById(@PathVariable Long id) {
|
||||
ProjectMilestone milestone = milestoneMapper.selectById(id);
|
||||
if (milestone == null || milestone.getDeleted() == 1) {
|
||||
return ResultUtils.error("里程碑不存在");
|
||||
}
|
||||
return ResultUtils.success("查询成功", milestone);
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增里程碑
|
||||
*/
|
||||
@SaCheckPermission("project:milestone:create")
|
||||
@PostMapping
|
||||
public BaseResponse<Long> create(@RequestBody ProjectMilestone milestone) {
|
||||
if (milestone.getProjectId() == null) {
|
||||
return ResultUtils.error("项目ID不能为空");
|
||||
}
|
||||
if (!StringUtils.hasText(milestone.getMilestoneName())) {
|
||||
return ResultUtils.error("里程碑名称不能为空");
|
||||
}
|
||||
|
||||
// 设置默认值
|
||||
if (milestone.getStatus() == null) {
|
||||
milestone.setStatus("pending");
|
||||
}
|
||||
if (milestone.getProgress() == null) {
|
||||
milestone.setProgress(0);
|
||||
}
|
||||
if (milestone.getIsKey() == null) {
|
||||
milestone.setIsKey(0);
|
||||
}
|
||||
|
||||
milestoneMapper.insert(milestone);
|
||||
log.info("新增里程碑成功, id: {}, milestoneName: {}", milestone.getId(), milestone.getMilestoneName());
|
||||
return ResultUtils.success("新增成功", milestone.getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改里程碑
|
||||
*/
|
||||
@SaCheckPermission("project:milestone:update")
|
||||
@PutMapping
|
||||
public BaseResponse<Void> update(@RequestBody ProjectMilestone milestone) {
|
||||
if (milestone.getId() == null) {
|
||||
return ResultUtils.error("里程碑ID不能为空");
|
||||
}
|
||||
|
||||
ProjectMilestone exist = milestoneMapper.selectById(milestone.getId());
|
||||
if (exist == null || exist.getDeleted() == 1) {
|
||||
return ResultUtils.error("里程碑不存在");
|
||||
}
|
||||
|
||||
// 不更新敏感字段
|
||||
milestone.setCreateTime(null);
|
||||
milestone.setDeleted(null);
|
||||
|
||||
milestoneMapper.updateById(milestone);
|
||||
log.info("修改里程碑成功, id: {}", milestone.getId());
|
||||
return ResultUtils.success("修改成功", null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除里程碑
|
||||
*/
|
||||
@SaCheckPermission("project:milestone:delete")
|
||||
@DeleteMapping("/{id}")
|
||||
public BaseResponse<Void> delete(@PathVariable Long id) {
|
||||
ProjectMilestone milestone = milestoneMapper.selectById(id);
|
||||
if (milestone == null || milestone.getDeleted() == 1) {
|
||||
return ResultUtils.error("里程碑不存在");
|
||||
}
|
||||
|
||||
milestoneMapper.deleteById(id);
|
||||
log.info("删除里程碑成功, id: {}", id);
|
||||
return ResultUtils.success("删除成功", null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新里程碑进度
|
||||
*/
|
||||
@SaCheckPermission("project:milestone:update")
|
||||
@PutMapping("/{id}/progress")
|
||||
public BaseResponse<Void> updateProgress(@PathVariable Long id, @RequestParam Integer progress) {
|
||||
if (progress < 0 || progress > 100) {
|
||||
return ResultUtils.error("进度值必须在0-100之间");
|
||||
}
|
||||
|
||||
ProjectMilestone milestone = milestoneMapper.selectById(id);
|
||||
if (milestone == null || milestone.getDeleted() == 1) {
|
||||
return ResultUtils.error("里程碑不存在");
|
||||
}
|
||||
|
||||
milestone.setProgress(progress);
|
||||
if (progress == 100) {
|
||||
milestone.setStatus("completed");
|
||||
milestone.setActualDate(LocalDate.now());
|
||||
} else if (progress > 0) {
|
||||
milestone.setStatus("in_progress");
|
||||
}
|
||||
|
||||
milestoneMapper.updateById(milestone);
|
||||
log.info("更新里程碑进度成功, id: {}, progress: {}", id, progress);
|
||||
return ResultUtils.success("更新成功", null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新里程碑状态
|
||||
*/
|
||||
@SaCheckPermission("project:milestone:update")
|
||||
@PutMapping("/{id}/status")
|
||||
public BaseResponse<Void> updateStatus(@PathVariable Long id, @RequestParam String status) {
|
||||
ProjectMilestone milestone = milestoneMapper.selectById(id);
|
||||
if (milestone == null || milestone.getDeleted() == 1) {
|
||||
return ResultUtils.error("里程碑不存在");
|
||||
}
|
||||
|
||||
milestone.setStatus(status);
|
||||
if ("completed".equals(status)) {
|
||||
milestone.setProgress(100);
|
||||
milestone.setActualDate(LocalDate.now());
|
||||
}
|
||||
|
||||
milestoneMapper.updateById(milestone);
|
||||
log.info("更新里程碑状态成功, id: {}, status: {}", id, status);
|
||||
return ResultUtils.success("更新成功", null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询已延期的关键里程碑
|
||||
*/
|
||||
@SaCheckPermission("project:milestone:view")
|
||||
@GetMapping("/delayed-key")
|
||||
public BaseResponse<List<ProjectMilestone>> getDelayedKeyMilestones(@RequestParam Long projectId) {
|
||||
List<ProjectMilestone> milestones = milestoneMapper.selectDelayedKeyMilestones(projectId);
|
||||
return ResultUtils.success("查询成功", milestones);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询即将到期的里程碑
|
||||
*/
|
||||
@SaCheckPermission("project:milestone:view")
|
||||
@GetMapping("/upcoming")
|
||||
public BaseResponse<List<ProjectMilestone>> getUpcomingMilestones(
|
||||
@RequestParam Long projectId,
|
||||
@RequestParam(defaultValue = "7") int days) {
|
||||
List<ProjectMilestone> milestones = milestoneMapper.selectUpcomingMilestones(projectId, days);
|
||||
return ResultUtils.success("查询成功", milestones);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询里程碑完成进度统计
|
||||
*/
|
||||
@SaCheckPermission("project:milestone:stats")
|
||||
@GetMapping("/stats/progress")
|
||||
public BaseResponse<Map<String, Object>> getProgressSummary(@RequestParam Long projectId) {
|
||||
Map<String, Object> stats = milestoneMapper.selectMilestoneProgressSummary(projectId);
|
||||
return ResultUtils.success("查询成功", stats);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,213 @@
|
||||
package cn.yinlihupo.controller.project;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import cn.yinlihupo.common.core.BaseResponse;
|
||||
import cn.yinlihupo.common.util.ResultUtils;
|
||||
import cn.yinlihupo.domain.entity.Task;
|
||||
import cn.yinlihupo.mapper.TaskMapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 任务管理控制器
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/task")
|
||||
@RequiredArgsConstructor
|
||||
public class TaskController {
|
||||
|
||||
private final TaskMapper taskMapper;
|
||||
|
||||
/**
|
||||
* 分页查询任务列表
|
||||
*/
|
||||
@SaCheckPermission("project:task:list")
|
||||
@GetMapping("/list")
|
||||
public BaseResponse<Page<Map<String, Object>>> list(
|
||||
@RequestParam(defaultValue = "1") Integer pageNum,
|
||||
@RequestParam(defaultValue = "10") Integer pageSize,
|
||||
@RequestParam(required = false) Long projectId,
|
||||
@RequestParam(required = false) Long milestoneId,
|
||||
@RequestParam(required = false) Long assigneeId,
|
||||
@RequestParam(required = false) String status,
|
||||
@RequestParam(required = false) String priority,
|
||||
@RequestParam(required = false) String keyword) {
|
||||
|
||||
Page<Map<String, Object>> page = new Page<>(pageNum, pageSize);
|
||||
List<Map<String, Object>> records = taskMapper.selectTaskPageWithAssignee(
|
||||
projectId, milestoneId, assigneeId, status, priority, keyword);
|
||||
|
||||
page.setRecords(records);
|
||||
page.setTotal(records.size());
|
||||
|
||||
return ResultUtils.success("查询成功", page);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID查询任务详情
|
||||
*/
|
||||
@SaCheckPermission("project:task:view")
|
||||
@GetMapping("/{id}")
|
||||
public BaseResponse<Task> getById(@PathVariable Long id) {
|
||||
Task task = taskMapper.selectById(id);
|
||||
if (task == null || task.getDeleted() == 1) {
|
||||
return ResultUtils.error("任务不存在");
|
||||
}
|
||||
return ResultUtils.success("查询成功", task);
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增任务
|
||||
*/
|
||||
@SaCheckPermission("project:task:create")
|
||||
@PostMapping
|
||||
public BaseResponse<Long> create(@RequestBody Task task) {
|
||||
if (task.getProjectId() == null) {
|
||||
return ResultUtils.error("项目ID不能为空");
|
||||
}
|
||||
if (!StringUtils.hasText(task.getTaskName())) {
|
||||
return ResultUtils.error("任务名称不能为空");
|
||||
}
|
||||
|
||||
// 设置默认值
|
||||
if (task.getStatus() == null) {
|
||||
task.setStatus("pending");
|
||||
}
|
||||
if (task.getProgress() == null) {
|
||||
task.setProgress(0);
|
||||
}
|
||||
|
||||
taskMapper.insert(task);
|
||||
log.info("新增任务成功, id: {}, taskName: {}", task.getId(), task.getTaskName());
|
||||
return ResultUtils.success("新增成功", task.getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改任务
|
||||
*/
|
||||
@SaCheckPermission("project:task:update")
|
||||
@PutMapping
|
||||
public BaseResponse<Void> update(@RequestBody Task task) {
|
||||
if (task.getId() == null) {
|
||||
return ResultUtils.error("任务ID不能为空");
|
||||
}
|
||||
|
||||
Task exist = taskMapper.selectById(task.getId());
|
||||
if (exist == null || exist.getDeleted() == 1) {
|
||||
return ResultUtils.error("任务不存在");
|
||||
}
|
||||
|
||||
// 不更新敏感字段
|
||||
task.setCreateTime(null);
|
||||
task.setDeleted(null);
|
||||
task.setTaskCode(null);
|
||||
|
||||
taskMapper.updateById(task);
|
||||
log.info("修改任务成功, id: {}", task.getId());
|
||||
return ResultUtils.success("修改成功", null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除任务
|
||||
*/
|
||||
@SaCheckPermission("project:task:delete")
|
||||
@DeleteMapping("/{id}")
|
||||
public BaseResponse<Void> delete(@PathVariable Long id) {
|
||||
Task task = taskMapper.selectById(id);
|
||||
if (task == null || task.getDeleted() == 1) {
|
||||
return ResultUtils.error("任务不存在");
|
||||
}
|
||||
|
||||
taskMapper.deleteById(id);
|
||||
log.info("删除任务成功, id: {}", id);
|
||||
return ResultUtils.success("删除成功", null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询我的待办任务
|
||||
*/
|
||||
@SaCheckPermission("project:task:my")
|
||||
@GetMapping("/my-tasks")
|
||||
public BaseResponse<List<Task>> getMyTasks(
|
||||
@RequestParam Long userId,
|
||||
@RequestParam(required = false) Long projectId) {
|
||||
List<Task> tasks = taskMapper.selectMyTasks(userId, projectId);
|
||||
return ResultUtils.success("查询成功", tasks);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新任务进度
|
||||
*/
|
||||
@SaCheckPermission("project:task:update")
|
||||
@PutMapping("/{id}/progress")
|
||||
public BaseResponse<Void> updateProgress(@PathVariable Long id, @RequestParam Integer progress) {
|
||||
if (progress < 0 || progress > 100) {
|
||||
return ResultUtils.error("进度值必须在0-100之间");
|
||||
}
|
||||
|
||||
Task task = taskMapper.selectById(id);
|
||||
if (task == null || task.getDeleted() == 1) {
|
||||
return ResultUtils.error("任务不存在");
|
||||
}
|
||||
|
||||
task.setProgress(progress);
|
||||
if (progress == 100) {
|
||||
task.setStatus("completed");
|
||||
} else if (progress > 0) {
|
||||
task.setStatus("in_progress");
|
||||
}
|
||||
|
||||
taskMapper.updateById(task);
|
||||
log.info("更新任务进度成功, id: {}, progress: {}", id, progress);
|
||||
return ResultUtils.success("更新成功", null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新任务状态
|
||||
*/
|
||||
@SaCheckPermission("project:task:update")
|
||||
@PutMapping("/{id}/status")
|
||||
public BaseResponse<Void> updateStatus(@PathVariable Long id, @RequestParam String status) {
|
||||
Task task = taskMapper.selectById(id);
|
||||
if (task == null || task.getDeleted() == 1) {
|
||||
return ResultUtils.error("任务不存在");
|
||||
}
|
||||
|
||||
task.setStatus(status);
|
||||
if ("completed".equals(status)) {
|
||||
task.setProgress(100);
|
||||
}
|
||||
|
||||
taskMapper.updateById(task);
|
||||
log.info("更新任务状态成功, id: {}, status: {}", id, status);
|
||||
return ResultUtils.success("更新成功", null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询任务依赖关系
|
||||
*/
|
||||
@SaCheckPermission("project:task:view")
|
||||
@GetMapping("/{id}/dependencies")
|
||||
public BaseResponse<List<Map<String, Object>>> getDependencies(@PathVariable Long id) {
|
||||
List<Map<String, Object>> dependencies = taskMapper.selectDependencies(id);
|
||||
return ResultUtils.success("查询成功", dependencies);
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计项目任务状态分布
|
||||
*/
|
||||
@SaCheckPermission("project:task:stats")
|
||||
@GetMapping("/stats/status")
|
||||
public BaseResponse<List<Map<String, Object>>> countByStatus(@RequestParam Long projectId) {
|
||||
List<Map<String, Object>> stats = taskMapper.countTasksByStatus(projectId);
|
||||
return ResultUtils.success("查询成功", stats);
|
||||
}
|
||||
}
|
||||
@@ -39,4 +39,9 @@ public interface SysUserMapper extends BaseMapper<SysUser> {
|
||||
* 查询项目成员用户详情列表
|
||||
*/
|
||||
List<SysUser> selectUsersByProjectId(@Param("projectId") Long projectId);
|
||||
|
||||
/**
|
||||
* 根据真实姓名查询用户
|
||||
*/
|
||||
SysUser selectByRealName(@Param("realName") String realName);
|
||||
}
|
||||
|
||||
@@ -467,7 +467,18 @@ public class ProjectServiceImpl implements ProjectService {
|
||||
member.setWeeklyHours(BigDecimal.valueOf(info.getWeeklyHours()));
|
||||
}
|
||||
member.setStatus(1);
|
||||
// TODO: 需要根据name查找或创建用户,暂时留空
|
||||
|
||||
// 根据成员名字匹配 sys_user 用户
|
||||
if (info.getName() != null && !info.getName().isEmpty()) {
|
||||
SysUser user = sysUserMapper.selectByRealName(info.getName());
|
||||
if (user != null) {
|
||||
member.setUserId(user.getId());
|
||||
log.debug("成员 '{}' 匹配到用户 ID: {}", info.getName(), user.getId());
|
||||
} else {
|
||||
log.warn("成员 '{}' 未在系统中找到匹配的用户", info.getName());
|
||||
}
|
||||
}
|
||||
|
||||
return member;
|
||||
}
|
||||
|
||||
|
||||
@@ -88,4 +88,13 @@
|
||||
ORDER BY pm.create_time ASC
|
||||
</select>
|
||||
|
||||
<!-- 根据真实姓名查询用户 -->
|
||||
<select id="selectByRealName" resultMap="BaseResultMap">
|
||||
SELECT *
|
||||
FROM sys_user
|
||||
WHERE real_name = #{realName}
|
||||
AND deleted = 0
|
||||
AND status = 1
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
|
||||
Reference in New Issue
Block a user