feat(project): 添加项目查询相关接口与功能

- 新增ProjectQueryController,提供项目列表、甘特图和统计接口
- 支持管理员和普通用户分别查询对应项目数据
- 新增GanttTaskVO、ProjectGanttVO、ProjectListVO和ProjectStatisticsVO数据模型
- ProjectService接口扩展项目查询相关方法定义
- 实现ProjectServiceImpl中项目列表、甘特图、统计信息的业务逻辑
- 项目查询支持分页、关键词和状态筛选
- 甘特图数据包含任务和里程碑详细信息
- 项目统计包括总数、状态分布、本月新增、即将到期和风险统计
- FeishuAuthServiceImpl中新增新用户自动分配“普通成员”角色功能
- 修改开发环境配置,更新Chat模型为google/gemini-3.1-pro-preview
This commit is contained in:
2026-03-28 15:22:00 +08:00
parent 1e930e6302
commit a7bb054e6e
9 changed files with 809 additions and 9 deletions

View File

@@ -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<TableDataInfo<ProjectListVO>> 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<ProjectListVO> 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<ProjectGanttVO> 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<ProjectStatisticsVO> 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());
}
}
}

View File

@@ -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<Long> dependencies;
}

View File

@@ -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<GanttTaskVO> tasks;
/**
* 里程碑列表
*/
private List<GanttTaskVO> milestones;
}

View File

@@ -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<String> tags;
/**
* 预算
*/
private BigDecimal budget;
/**
* 已花费金额
*/
private BigDecimal cost;
/**
* 创建时间
*/
private LocalDateTime createTime;
/**
* 我在项目中的角色
*/
private String myRole;
}

View File

@@ -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<String, Integer> statusCountMap;
/**
* 本月新增项目数
*/
private Integer newThisMonth;
/**
* 即将到期项目数7天内
*/
private Integer aboutToExpireCount;
/**
* 平均项目进度
*/
private BigDecimal averageProgress;
/**
* 高风险项目数
*/
private Integer highRiskCount;
}

View File

@@ -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<ProjectListVO> 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<ProjectListVO> getAllProjectList(Integer pageNum, Integer pageSize,
String keyword, String status);
}

View File

@@ -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<ProjectListVO> getMyProjectList(Long userId, Integer pageNum, Integer pageSize,
String keyword, String status) {
log.info("查询我的项目列表, userId: {}, pageNum: {}, pageSize: {}", userId, pageNum, pageSize);
// 使用分页插件
Page<Project> page = new Page<>(pageNum, pageSize);
// 构建查询条件
LambdaQueryWrapper<Project> 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<Long> myProjectIds = projectMemberMapper.selectList(
new LambdaQueryWrapper<ProjectMember>()
.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<Project> projectPage = projectMapper.selectPage(page, wrapper);
// 转换为VO
List<ProjectListVO> voList = projectPage.getRecords().stream()
.map(this::convertToProjectListVO)
.collect(Collectors.toList());
return TableDataInfo.build(new Page<ProjectListVO>(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<ProjectMilestone> milestones = projectMilestoneMapper.selectList(
new LambdaQueryWrapper<ProjectMilestone>()
.eq(ProjectMilestone::getProjectId, projectId)
.eq(ProjectMilestone::getDeleted, 0)
.orderByAsc(ProjectMilestone::getSortOrder)
);
List<GanttTaskVO> milestoneVOList = milestones.stream()
.map(this::convertMilestoneToGanttTask)
.collect(Collectors.toList());
ganttVO.setMilestones(milestoneVOList);
// 3. 查询任务
List<Task> tasks = taskMapper.selectList(
new LambdaQueryWrapper<Task>()
.eq(Task::getProjectId, projectId)
.eq(Task::getDeleted, 0)
.orderByAsc(Task::getSortOrder)
);
List<GanttTaskVO> 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<Project> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(Project::getDeleted, 0);
// 如果指定了用户ID只统计该用户的项目
if (userId != null) {
List<Long> myProjectIds = projectMemberMapper.selectList(
new LambdaQueryWrapper<ProjectMember>()
.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<Project> projects = projectMapper.selectList(wrapper);
// 2. 统计总数
statistics.setTotalCount(projects.size());
// 3. 按状态统计
Map<String, Integer> 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<ProjectListVO> getAllProjectList(Integer pageNum, Integer pageSize,
String keyword, String status) {
log.info("查询所有项目列表, pageNum: {}, pageSize: {}", pageNum, pageSize);
Page<Project> page = new Page<>(pageNum, pageSize);
LambdaQueryWrapper<Project> 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<Project> projectPage = projectMapper.selectPage(page, wrapper);
List<ProjectListVO> voList = projectPage.getRecords().stream()
.map(this::convertToProjectListVO)
.collect(Collectors.toList());
return TableDataInfo.build(new Page<ProjectListVO>(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;
}
}

View File

@@ -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<SysRole> 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) {

View File

@@ -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: