diff --git a/src/main/java/cn/yinlihupo/controller/project/ProjectMilestoneController.java b/src/main/java/cn/yinlihupo/controller/project/ProjectMilestoneController.java new file mode 100644 index 0000000..4057a86 --- /dev/null +++ b/src/main/java/cn/yinlihupo/controller/project/ProjectMilestoneController.java @@ -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>> list( + @RequestParam(defaultValue = "1") Integer pageNum, + @RequestParam(defaultValue = "10") Integer pageSize, + @RequestParam(required = false) Long projectId, + @RequestParam(required = false) String status) { + + Page> page = new Page<>(pageNum, pageSize); + List> 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 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 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 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 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 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 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> getDelayedKeyMilestones(@RequestParam Long projectId) { + List milestones = milestoneMapper.selectDelayedKeyMilestones(projectId); + return ResultUtils.success("查询成功", milestones); + } + + /** + * 查询即将到期的里程碑 + */ + @SaCheckPermission("project:milestone:view") + @GetMapping("/upcoming") + public BaseResponse> getUpcomingMilestones( + @RequestParam Long projectId, + @RequestParam(defaultValue = "7") int days) { + List milestones = milestoneMapper.selectUpcomingMilestones(projectId, days); + return ResultUtils.success("查询成功", milestones); + } + + /** + * 查询里程碑完成进度统计 + */ + @SaCheckPermission("project:milestone:stats") + @GetMapping("/stats/progress") + public BaseResponse> getProgressSummary(@RequestParam Long projectId) { + Map stats = milestoneMapper.selectMilestoneProgressSummary(projectId); + return ResultUtils.success("查询成功", stats); + } +} diff --git a/src/main/java/cn/yinlihupo/controller/project/TaskController.java b/src/main/java/cn/yinlihupo/controller/project/TaskController.java new file mode 100644 index 0000000..0f13f27 --- /dev/null +++ b/src/main/java/cn/yinlihupo/controller/project/TaskController.java @@ -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>> 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> page = new Page<>(pageNum, pageSize); + List> 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 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 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 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 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> getMyTasks( + @RequestParam Long userId, + @RequestParam(required = false) Long projectId) { + List tasks = taskMapper.selectMyTasks(userId, projectId); + return ResultUtils.success("查询成功", tasks); + } + + /** + * 更新任务进度 + */ + @SaCheckPermission("project:task:update") + @PutMapping("/{id}/progress") + public BaseResponse 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 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>> getDependencies(@PathVariable Long id) { + List> dependencies = taskMapper.selectDependencies(id); + return ResultUtils.success("查询成功", dependencies); + } + + /** + * 统计项目任务状态分布 + */ + @SaCheckPermission("project:task:stats") + @GetMapping("/stats/status") + public BaseResponse>> countByStatus(@RequestParam Long projectId) { + List> stats = taskMapper.countTasksByStatus(projectId); + return ResultUtils.success("查询成功", stats); + } +} diff --git a/src/main/java/cn/yinlihupo/mapper/SysUserMapper.java b/src/main/java/cn/yinlihupo/mapper/SysUserMapper.java index 8ff4eaa..dffad8e 100644 --- a/src/main/java/cn/yinlihupo/mapper/SysUserMapper.java +++ b/src/main/java/cn/yinlihupo/mapper/SysUserMapper.java @@ -39,4 +39,9 @@ public interface SysUserMapper extends BaseMapper { * 查询项目成员用户详情列表 */ List selectUsersByProjectId(@Param("projectId") Long projectId); + + /** + * 根据真实姓名查询用户 + */ + SysUser selectByRealName(@Param("realName") String realName); } diff --git a/src/main/java/cn/yinlihupo/service/project/impl/ProjectServiceImpl.java b/src/main/java/cn/yinlihupo/service/project/impl/ProjectServiceImpl.java index d71812b..88d9218 100644 --- a/src/main/java/cn/yinlihupo/service/project/impl/ProjectServiceImpl.java +++ b/src/main/java/cn/yinlihupo/service/project/impl/ProjectServiceImpl.java @@ -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; } diff --git a/src/main/resources/mapper/SysUserMapper.xml b/src/main/resources/mapper/SysUserMapper.xml index ad58c26..fbba8a9 100644 --- a/src/main/resources/mapper/SysUserMapper.xml +++ b/src/main/resources/mapper/SysUserMapper.xml @@ -88,4 +88,13 @@ ORDER BY pm.create_time ASC + + +