diff --git a/src/main/java/cn/yinlihupo/controller/project/ProjectQueryController.java b/src/main/java/cn/yinlihupo/controller/project/ProjectQueryController.java new file mode 100644 index 0000000..8839430 --- /dev/null +++ b/src/main/java/cn/yinlihupo/controller/project/ProjectQueryController.java @@ -0,0 +1,124 @@ +package cn.yinlihupo.controller.project; + +import cn.yinlihupo.common.core.BaseResponse; +import cn.yinlihupo.common.page.TableDataInfo; +import cn.yinlihupo.common.util.ResultUtils; +import cn.yinlihupo.common.util.SecurityUtils; +import cn.yinlihupo.domain.vo.ProjectGanttVO; +import cn.yinlihupo.domain.vo.ProjectListVO; +import cn.yinlihupo.domain.vo.ProjectStatisticsVO; +import cn.yinlihupo.service.project.ProjectService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.*; + +/** + * 项目查询控制器 + * 提供项目列表、甘特图、统计信息等查询接口 + */ +@Slf4j +@RestController +@RequestMapping("/api/v1/project") +@RequiredArgsConstructor +public class ProjectQueryController { + + private final ProjectService projectService; + + /** + * 获取项目列表 + * 管理员返回全部项目,普通用户返回自己管理或参与的项目 + * + * @param pageNum 页码,默认1 + * @param pageSize 每页大小,默认10 + * @param keyword 关键词搜索(项目名称/编号) + * @param status 状态筛选 + * @return 项目列表分页数据 + */ + @GetMapping("/list") + public BaseResponse> getProjectList( + @RequestParam(defaultValue = "1") Integer pageNum, + @RequestParam(defaultValue = "10") Integer pageSize, + @RequestParam(required = false) String keyword, + @RequestParam(required = false) String status) { + + Long userId = SecurityUtils.getCurrentUserId(); + if (userId == null) { + return ResultUtils.error("用户未登录"); + } + + // 判断是否为管理员 + boolean isAdmin = SecurityUtils.isAdmin(); + log.info("获取项目列表, userId: {}, isAdmin: {}, pageNum: {}, pageSize: {}", userId, isAdmin, pageNum, pageSize); + + try { + TableDataInfo result; + if (isAdmin) { + // 管理员返回全部项目 + result = projectService.getAllProjectList(pageNum, pageSize, keyword, status); + } else { + // 普通用户返回自己的项目 + result = projectService.getMyProjectList(userId, pageNum, pageSize, keyword, status); + } + return ResultUtils.success("查询成功", result); + } catch (Exception e) { + log.error("获取项目列表失败: {}", e.getMessage(), e); + return ResultUtils.error("查询失败: " + e.getMessage()); + } + } + + /** + * 获取项目甘特图数据 + * + * @param projectId 项目ID + * @return 甘特图数据 + */ + @GetMapping("/{projectId}/gantt") + public BaseResponse getProjectGantt(@PathVariable Long projectId) { + log.info("获取项目甘特图数据, projectId: {}", projectId); + + if (projectId == null || projectId <= 0) { + return ResultUtils.error("项目ID不能为空"); + } + + try { + ProjectGanttVO result = projectService.getProjectGantt(projectId); + return ResultUtils.success("查询成功", result); + } catch (Exception e) { + log.error("获取项目甘特图失败: {}", e.getMessage(), e); + return ResultUtils.error("查询失败: " + e.getMessage()); + } + } + + /** + * 获取项目统计信息 + * 管理员返回全局统计,普通用户返回自己的项目统计 + * + * @return 统计信息 + */ + @GetMapping("/statistics") + public BaseResponse getProjectStatistics() { + Long userId = SecurityUtils.getCurrentUserId(); + if (userId == null) { + return ResultUtils.error("用户未登录"); + } + + // 判断是否为管理员 + boolean isAdmin = SecurityUtils.isAdmin(); + log.info("获取项目统计信息, userId: {}, isAdmin: {}", userId, isAdmin); + + try { + ProjectStatisticsVO result; + if (isAdmin) { + // 管理员返回全局统计 + result = projectService.getProjectStatistics(null); + } else { + // 普通用户返回自己的项目统计 + result = projectService.getProjectStatistics(userId); + } + return ResultUtils.success("查询成功", result); + } catch (Exception e) { + log.error("获取项目统计信息失败: {}", e.getMessage(), e); + return ResultUtils.error("查询失败: " + e.getMessage()); + } + } +} diff --git a/src/main/java/cn/yinlihupo/domain/vo/GanttTaskVO.java b/src/main/java/cn/yinlihupo/domain/vo/GanttTaskVO.java new file mode 100644 index 0000000..4961c78 --- /dev/null +++ b/src/main/java/cn/yinlihupo/domain/vo/GanttTaskVO.java @@ -0,0 +1,84 @@ +package cn.yinlihupo.domain.vo; + +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDate; +import java.util.List; + +/** + * 甘特图任务VO + */ +@Data +public class GanttTaskVO { + + /** + * 任务ID + */ + private Long id; + + /** + * 任务名称 + */ + private String taskName; + + /** + * 任务类型: task-任务, milestone-里程碑 + */ + private String type; + + /** + * 计划开始日期 + */ + private LocalDate startDate; + + /** + * 计划结束日期 + */ + private LocalDate endDate; + + /** + * 实际开始日期 + */ + private LocalDate actualStartDate; + + /** + * 实际结束日期 + */ + private LocalDate actualEndDate; + + /** + * 进度百分比 + */ + private Integer progress; + + /** + * 状态 + */ + private String status; + + /** + * 优先级 + */ + private String priority; + + /** + * 父任务ID + */ + private Long parentId; + + /** + * 排序 + */ + private Integer sortOrder; + + /** + * 执行人名称 + */ + private String assigneeName; + + /** + * 依赖任务ID列表 + */ + private List dependencies; +} diff --git a/src/main/java/cn/yinlihupo/domain/vo/ProjectGanttVO.java b/src/main/java/cn/yinlihupo/domain/vo/ProjectGanttVO.java new file mode 100644 index 0000000..48284cb --- /dev/null +++ b/src/main/java/cn/yinlihupo/domain/vo/ProjectGanttVO.java @@ -0,0 +1,53 @@ +package cn.yinlihupo.domain.vo; + +import lombok.Data; + +import java.time.LocalDate; +import java.util.List; + +/** + * 项目甘特图数据VO + */ +@Data +public class ProjectGanttVO { + + /** + * 项目ID + */ + private Long projectId; + + /** + * 项目名称 + */ + private String projectName; + + /** + * 项目状态 + */ + private String projectStatus; + + /** + * 项目计划开始日期 + */ + private LocalDate projectStartDate; + + /** + * 项目计划结束日期 + */ + private LocalDate projectEndDate; + + /** + * 项目整体进度 + */ + private Integer projectProgress; + + /** + * 甘特图任务列表(包含里程碑和任务) + */ + private List tasks; + + /** + * 里程碑列表 + */ + private List milestones; +} diff --git a/src/main/java/cn/yinlihupo/domain/vo/ProjectListVO.java b/src/main/java/cn/yinlihupo/domain/vo/ProjectListVO.java new file mode 100644 index 0000000..ed06e30 --- /dev/null +++ b/src/main/java/cn/yinlihupo/domain/vo/ProjectListVO.java @@ -0,0 +1,100 @@ +package cn.yinlihupo.domain.vo; + +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.List; + +/** + * 项目列表VO + */ +@Data +public class ProjectListVO { + + /** + * 项目ID + */ + private Long id; + + /** + * 项目编号 + */ + private String projectCode; + + /** + * 项目名称 + */ + private String projectName; + + /** + * 项目类型 + */ + private String projectType; + + /** + * 项目经理ID + */ + private Long managerId; + + /** + * 项目经理名称 + */ + private String managerName; + + /** + * 计划开始日期 + */ + private LocalDate planStartDate; + + /** + * 计划结束日期 + */ + private LocalDate planEndDate; + + /** + * 进度百分比 + */ + private Integer progress; + + /** + * 状态 + */ + private String status; + + /** + * 优先级 + */ + private String priority; + + /** + * 风险等级 + */ + private String riskLevel; + + /** + * 标签列表 + */ + private List tags; + + /** + * 预算 + */ + private BigDecimal budget; + + /** + * 已花费金额 + */ + private BigDecimal cost; + + /** + * 创建时间 + */ + private LocalDateTime createTime; + + /** + * 我在项目中的角色 + */ + private String myRole; +} diff --git a/src/main/java/cn/yinlihupo/domain/vo/ProjectStatisticsVO.java b/src/main/java/cn/yinlihupo/domain/vo/ProjectStatisticsVO.java new file mode 100644 index 0000000..5347ca0 --- /dev/null +++ b/src/main/java/cn/yinlihupo/domain/vo/ProjectStatisticsVO.java @@ -0,0 +1,73 @@ +package cn.yinlihupo.domain.vo; + +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Map; + +/** + * 项目统计信息VO + */ +@Data +public class ProjectStatisticsVO { + + /** + * 项目总数 + */ + private Integer totalCount; + + /** + * 进行中项目数 + */ + private Integer ongoingCount; + + /** + * 已完成项目数 + */ + private Integer completedCount; + + /** + * 已暂停项目数 + */ + private Integer pausedCount; + + /** + * 规划中项目数 + */ + private Integer planningCount; + + /** + * 已取消项目数 + */ + private Integer cancelledCount; + + /** + * 草稿项目数 + */ + private Integer draftCount; + + /** + * 各状态项目数量统计 + */ + private Map statusCountMap; + + /** + * 本月新增项目数 + */ + private Integer newThisMonth; + + /** + * 即将到期项目数(7天内) + */ + private Integer aboutToExpireCount; + + /** + * 平均项目进度 + */ + private BigDecimal averageProgress; + + /** + * 高风险项目数 + */ + private Integer highRiskCount; +} diff --git a/src/main/java/cn/yinlihupo/service/project/ProjectService.java b/src/main/java/cn/yinlihupo/service/project/ProjectService.java index 6df373e..35db2be 100644 --- a/src/main/java/cn/yinlihupo/service/project/ProjectService.java +++ b/src/main/java/cn/yinlihupo/service/project/ProjectService.java @@ -1,8 +1,14 @@ package cn.yinlihupo.service.project; +import cn.yinlihupo.common.page.TableDataInfo; +import cn.yinlihupo.domain.vo.ProjectGanttVO; import cn.yinlihupo.domain.vo.ProjectInitResult; +import cn.yinlihupo.domain.vo.ProjectListVO; +import cn.yinlihupo.domain.vo.ProjectStatisticsVO; import org.springframework.web.multipart.MultipartFile; +import java.util.List; + /** * AI项目初始化服务接口 * 使用Spring AI结构化输出能力,从项目文档中提取结构化信息 @@ -41,4 +47,47 @@ public interface ProjectService { * @return 保存后的项目初始化结果(包含生成的ID等信息) */ ProjectInitResult saveProjectData(ProjectInitResult result); + + // ==================== 项目查询相关接口 ==================== + + /** + * 获取我的项目列表(我管理或参与的项目) + * + * @param userId 用户ID + * @param pageNum 页码 + * @param pageSize 每页大小 + * @param keyword 关键词搜索 + * @param status 状态筛选 + * @return 分页项目列表 + */ + TableDataInfo getMyProjectList(Long userId, Integer pageNum, Integer pageSize, + String keyword, String status); + + /** + * 获取项目甘特图数据 + * + * @param projectId 项目ID + * @return 甘特图数据 + */ + ProjectGanttVO getProjectGantt(Long projectId); + + /** + * 获取项目统计信息 + * + * @param userId 用户ID(为null则统计全部) + * @return 统计信息 + */ + ProjectStatisticsVO getProjectStatistics(Long userId); + + /** + * 获取所有项目列表(管理员用) + * + * @param pageNum 页码 + * @param pageSize 每页大小 + * @param keyword 关键词搜索 + * @param status 状态筛选 + * @return 分页项目列表 + */ + TableDataInfo getAllProjectList(Integer pageNum, Integer pageSize, + String keyword, String status); } 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 e0afcae..ca22cc1 100644 --- a/src/main/java/cn/yinlihupo/service/project/impl/ProjectServiceImpl.java +++ b/src/main/java/cn/yinlihupo/service/project/impl/ProjectServiceImpl.java @@ -1,28 +1,28 @@ package cn.yinlihupo.service.project.impl; import cn.hutool.core.util.IdUtil; +import cn.yinlihupo.common.page.TableDataInfo; import cn.yinlihupo.domain.entity.*; -import cn.yinlihupo.domain.vo.ProjectInitResult; +import cn.yinlihupo.domain.vo.*; import cn.yinlihupo.mapper.*; import cn.yinlihupo.service.oss.OssService; import cn.yinlihupo.service.project.ProjectService; +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.ai.chat.client.ChatClient; -import org.springframework.ai.chat.metadata.Usage; -import org.springframework.ai.converter.BeanOutputConverter; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; import java.math.BigDecimal; import java.math.RoundingMode; +import java.time.LocalDate; import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.time.YearMonth; +import java.util.*; +import java.util.stream.Collectors; /** * AI项目初始化服务实现类 @@ -551,4 +551,278 @@ public class ProjectServiceImpl implements ProjectService { timeline.setKbScope(info.getKbScope()); return timeline; } + + // ==================== 项目查询相关实现 ==================== + + @Override + public TableDataInfo getMyProjectList(Long userId, Integer pageNum, Integer pageSize, + String keyword, String status) { + log.info("查询我的项目列表, userId: {}, pageNum: {}, pageSize: {}", userId, pageNum, pageSize); + + // 使用分页插件 + Page page = new Page<>(pageNum, pageSize); + + // 构建查询条件 + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(Project::getDeleted, 0); + + // 关键词搜索 + if (keyword != null && !keyword.isEmpty()) { + wrapper.and(w -> w.like(Project::getProjectName, keyword) + .or() + .like(Project::getProjectCode, keyword)); + } + + // 状态筛选 + if (status != null && !status.isEmpty()) { + wrapper.eq(Project::getStatus, status); + } + + // 查询我管理或参与的项目 + // 先查询我参与的项目ID列表 + List myProjectIds = projectMemberMapper.selectList( + new LambdaQueryWrapper() + .eq(ProjectMember::getUserId, userId) + .eq(ProjectMember::getDeleted, 0) + .eq(ProjectMember::getStatus, 1) + ).stream().map(ProjectMember::getProjectId).collect(Collectors.toList()); + + // 查询条件:我管理的项目 或 我参与的项目 + wrapper.and(w -> w.eq(Project::getManagerId, userId) + .or() + .in(!myProjectIds.isEmpty(), Project::getId, myProjectIds)); + + wrapper.orderByDesc(Project::getCreateTime); + + Page projectPage = projectMapper.selectPage(page, wrapper); + + // 转换为VO + List voList = projectPage.getRecords().stream() + .map(this::convertToProjectListVO) + .collect(Collectors.toList()); + + return TableDataInfo.build(new Page(projectPage.getCurrent(), projectPage.getSize(), projectPage.getTotal()) + .setRecords(voList)); + } + + @Override + public ProjectGanttVO getProjectGantt(Long projectId) { + log.info("查询项目甘特图数据, projectId: {}", projectId); + + // 1. 查询项目基本信息 + Project project = projectMapper.selectById(projectId); + if (project == null || project.getDeleted() == 1) { + throw new RuntimeException("项目不存在"); + } + + ProjectGanttVO ganttVO = new ProjectGanttVO(); + ganttVO.setProjectId(project.getId()); + ganttVO.setProjectName(project.getProjectName()); + ganttVO.setProjectStatus(project.getStatus()); + ganttVO.setProjectStartDate(project.getPlanStartDate()); + ganttVO.setProjectEndDate(project.getPlanEndDate()); + ganttVO.setProjectProgress(project.getProgress()); + + // 2. 查询里程碑 + List milestones = projectMilestoneMapper.selectList( + new LambdaQueryWrapper() + .eq(ProjectMilestone::getProjectId, projectId) + .eq(ProjectMilestone::getDeleted, 0) + .orderByAsc(ProjectMilestone::getSortOrder) + ); + + List milestoneVOList = milestones.stream() + .map(this::convertMilestoneToGanttTask) + .collect(Collectors.toList()); + ganttVO.setMilestones(milestoneVOList); + + // 3. 查询任务 + List tasks = taskMapper.selectList( + new LambdaQueryWrapper() + .eq(Task::getProjectId, projectId) + .eq(Task::getDeleted, 0) + .orderByAsc(Task::getSortOrder) + ); + + List taskVOList = tasks.stream() + .map(this::convertTaskToGanttTask) + .collect(Collectors.toList()); + ganttVO.setTasks(taskVOList); + + return ganttVO; + } + + @Override + public ProjectStatisticsVO getProjectStatistics(Long userId) { + log.info("查询项目统计信息, userId: {}", userId); + + ProjectStatisticsVO statistics = new ProjectStatisticsVO(); + + // 构建基础查询条件 + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(Project::getDeleted, 0); + + // 如果指定了用户ID,只统计该用户的项目 + if (userId != null) { + List myProjectIds = projectMemberMapper.selectList( + new LambdaQueryWrapper() + .eq(ProjectMember::getUserId, userId) + .eq(ProjectMember::getDeleted, 0) + .eq(ProjectMember::getStatus, 1) + ).stream().map(ProjectMember::getProjectId).collect(Collectors.toList()); + + wrapper.and(w -> w.eq(Project::getManagerId, userId) + .or() + .in(!myProjectIds.isEmpty(), Project::getId, myProjectIds)); + } + + // 1. 查询所有符合条件的项目 + List projects = projectMapper.selectList(wrapper); + + // 2. 统计总数 + statistics.setTotalCount(projects.size()); + + // 3. 按状态统计 + Map statusCountMap = projects.stream() + .collect(Collectors.groupingBy( + Project::getStatus, + Collectors.collectingAndThen(Collectors.counting(), Long::intValue) + )); + statistics.setStatusCountMap(statusCountMap); + + // 设置各状态数量 + statistics.setDraftCount(statusCountMap.getOrDefault("draft", 0)); + statistics.setPlanningCount(statusCountMap.getOrDefault("planning", 0)); + statistics.setOngoingCount(statusCountMap.getOrDefault("ongoing", 0)); + statistics.setPausedCount(statusCountMap.getOrDefault("paused", 0)); + statistics.setCompletedCount(statusCountMap.getOrDefault("completed", 0)); + statistics.setCancelledCount(statusCountMap.getOrDefault("cancelled", 0)); + + // 4. 统计本月新增 + YearMonth thisMonth = YearMonth.now(); + int newThisMonth = (int) projects.stream() + .filter(p -> p.getCreateTime() != null && + YearMonth.from(p.getCreateTime()).equals(thisMonth)) + .count(); + statistics.setNewThisMonth(newThisMonth); + + // 5. 统计即将到期(7天内) + LocalDate now = LocalDate.now(); + LocalDate sevenDaysLater = now.plusDays(7); + int aboutToExpire = (int) projects.stream() + .filter(p -> !"completed".equals(p.getStatus()) && !"cancelled".equals(p.getStatus())) + .filter(p -> p.getPlanEndDate() != null && + !p.getPlanEndDate().isBefore(now) && + !p.getPlanEndDate().isAfter(sevenDaysLater)) + .count(); + statistics.setAboutToExpireCount(aboutToExpire); + + // 6. 计算平均进度 + double avgProgress = projects.stream() + .filter(p -> p.getProgress() != null) + .mapToInt(Project::getProgress) + .average() + .orElse(0.0); + statistics.setAverageProgress(BigDecimal.valueOf(avgProgress).setScale(2, RoundingMode.HALF_UP)); + + // 7. 统计高风险项目 + int highRiskCount = (int) projects.stream() + .filter(p -> "high".equals(p.getRiskLevel()) || "critical".equals(p.getRiskLevel())) + .count(); + statistics.setHighRiskCount(highRiskCount); + + return statistics; + } + + @Override + public TableDataInfo getAllProjectList(Integer pageNum, Integer pageSize, + String keyword, String status) { + log.info("查询所有项目列表, pageNum: {}, pageSize: {}", pageNum, pageSize); + + Page page = new Page<>(pageNum, pageSize); + + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(Project::getDeleted, 0); + + if (keyword != null && !keyword.isEmpty()) { + wrapper.and(w -> w.like(Project::getProjectName, keyword) + .or() + .like(Project::getProjectCode, keyword)); + } + + if (status != null && !status.isEmpty()) { + wrapper.eq(Project::getStatus, status); + } + + wrapper.orderByDesc(Project::getCreateTime); + + Page projectPage = projectMapper.selectPage(page, wrapper); + + List voList = projectPage.getRecords().stream() + .map(this::convertToProjectListVO) + .collect(Collectors.toList()); + + return TableDataInfo.build(new Page(projectPage.getCurrent(), projectPage.getSize(), projectPage.getTotal()) + .setRecords(voList)); + } + + /** + * 转换为项目列表VO + */ + private ProjectListVO convertToProjectListVO(Project project) { + ProjectListVO vo = new ProjectListVO(); + vo.setId(project.getId()); + vo.setProjectCode(project.getProjectCode()); + vo.setProjectName(project.getProjectName()); + vo.setProjectType(project.getProjectType()); + vo.setManagerId(project.getManagerId()); + vo.setPlanStartDate(project.getPlanStartDate()); + vo.setPlanEndDate(project.getPlanEndDate()); + vo.setProgress(project.getProgress()); + vo.setStatus(project.getStatus()); + vo.setPriority(project.getPriority()); + vo.setRiskLevel(project.getRiskLevel()); + vo.setTags(project.getTags()); + vo.setBudget(project.getBudget()); + vo.setCost(project.getCost()); + vo.setCreateTime(project.getCreateTime()); + return vo; + } + + /** + * 转换为甘特图任务VO(里程碑) + */ + private GanttTaskVO convertMilestoneToGanttTask(ProjectMilestone milestone) { + GanttTaskVO vo = new GanttTaskVO(); + vo.setId(milestone.getId()); + vo.setTaskName(milestone.getMilestoneName()); + vo.setType("milestone"); + vo.setStartDate(milestone.getPlanDate()); + vo.setEndDate(milestone.getPlanDate()); + vo.setActualStartDate(milestone.getActualDate()); + vo.setActualEndDate(milestone.getActualDate()); + vo.setProgress(milestone.getProgress()); + vo.setStatus(milestone.getStatus()); + vo.setSortOrder(milestone.getSortOrder()); + return vo; + } + + /** + * 转换为甘特图任务VO(任务) + */ + private GanttTaskVO convertTaskToGanttTask(Task task) { + GanttTaskVO vo = new GanttTaskVO(); + vo.setId(task.getId()); + vo.setTaskName(task.getTaskName()); + vo.setType("task"); + vo.setStartDate(task.getPlanStartDate()); + vo.setEndDate(task.getPlanEndDate()); + vo.setActualStartDate(task.getActualStartDate()); + vo.setActualEndDate(task.getActualEndDate()); + vo.setProgress(task.getProgress()); + vo.setStatus(task.getStatus()); + vo.setPriority(task.getPriority()); + vo.setSortOrder(task.getSortOrder()); + return vo; + } } diff --git a/src/main/java/cn/yinlihupo/service/system/impl/FeishuAuthServiceImpl.java b/src/main/java/cn/yinlihupo/service/system/impl/FeishuAuthServiceImpl.java index d54ee2a..e93a0fd 100644 --- a/src/main/java/cn/yinlihupo/service/system/impl/FeishuAuthServiceImpl.java +++ b/src/main/java/cn/yinlihupo/service/system/impl/FeishuAuthServiceImpl.java @@ -5,10 +5,15 @@ import cn.hutool.http.HttpResponse; import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; import cn.yinlihupo.common.config.FeishuConfig; +import cn.yinlihupo.common.constant.PermissionConstants; import cn.yinlihupo.common.util.PhoneUtils; +import cn.yinlihupo.domain.entity.SysRole; import cn.yinlihupo.domain.entity.SysUser; +import cn.yinlihupo.domain.entity.SysUserRole; import cn.yinlihupo.mapper.SysPermissionMapper; +import cn.yinlihupo.mapper.SysRoleMapper; import cn.yinlihupo.mapper.SysUserMapper; +import cn.yinlihupo.mapper.SysUserRoleMapper; import cn.yinlihupo.service.system.FeishuAuthService; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import lombok.RequiredArgsConstructor; @@ -33,6 +38,8 @@ public class FeishuAuthServiceImpl implements FeishuAuthService { private final FeishuConfig feishuConfig; private final SysUserMapper sysUserMapper; private final SysPermissionMapper sysPermissionMapper; + private final SysRoleMapper sysRoleMapper; + private final SysUserRoleMapper sysUserRoleMapper; /** * 飞书OAuth授权端点 @@ -207,11 +214,47 @@ public class FeishuAuthServiceImpl implements FeishuAuthService { // 飞书登录用户不需要密码,设置一个随机密码 user.setPassword("FEISHU_OAUTH_USER"); sysUserMapper.insert(user); + + // 新用户注册时,自动分配"普通成员"角色 + assignDefaultRole(user.getId(), now); } return user; } + /** + * 为新用户分配默认角色(普通成员) + * + * @param userId 用户ID + * @param now 当前时间 + */ + private void assignDefaultRole(Long userId, LocalDateTime now) { + try { + // 查询"普通成员"角色 + LambdaQueryWrapper roleWrapper = new LambdaQueryWrapper<>(); + roleWrapper.eq(SysRole::getRoleCode, PermissionConstants.ROLE_MEMBER) + .eq(SysRole::getDeleted, 0); + SysRole memberRole = sysRoleMapper.selectOne(roleWrapper); + + if (memberRole == null) { + log.warn("未找到'普通成员'角色,跳过角色分配"); + return; + } + + // 创建用户角色关联 + SysUserRole userRole = new SysUserRole(); + userRole.setUserId(userId); + userRole.setRoleId(memberRole.getId()); + userRole.setCreateTime(now); + sysUserRoleMapper.insert(userRole); + + log.info("为新用户 {} 分配默认角色 '普通成员' 成功", userId); + } catch (Exception e) { + log.error("为新用户 {} 分配默认角色失败", userId, e); + // 角色分配失败不影响用户创建,只记录日志 + } + } + @Override public SysUser getUserById(Long userId) { if (userId == null) { diff --git a/src/main/resources/application-dev.yaml b/src/main/resources/application-dev.yaml index 1f1a72d..07cace1 100644 --- a/src/main/resources/application-dev.yaml +++ b/src/main/resources/application-dev.yaml @@ -37,7 +37,7 @@ spring: base-url: https://sg1.proxy.yinlihupo.cc/proxy/https://openrouter.ai/api chat: options: - model: moonshotai/kimi-k2-thinking + model: google/gemini-3.1-pro-preview # MinIO 对象存储配置 minio: