feat(core): 新增项目及相关功能的数据访问层和权限控制切面
- 添加多个Mapper接口及XML文件支持项目、成员、里程碑、任务、风险、资源、 文件附件等模块的数据操作和查询功能,支持复杂查询与统计 - 新增Sa-Token权限配置,集成统一认证管理 - 引入权限常量类,定义系统角色、项目角色及权限编码标准 - 新增项目权限校验切面,实现基于注解的项目权限和角色校验逻辑 - 更新配置文件和依赖,集成MyBatis Plus、MinIO、Spring AI及文档解析相关库 - 调整MyBatis配置的类型别名包路径,统一领域实体引用路径
This commit is contained in:
14
pom.xml
14
pom.xml
@@ -125,6 +125,20 @@
|
||||
<version>2.9.1</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Sa-Token 权限认证 -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-spring-boot3-starter</artifactId>
|
||||
<version>1.39.0</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Sa-Token Redis 集成(可选,用于分布式环境) -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-redis-jackson</artifactId>
|
||||
<version>1.39.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package cn.yinlihupo.common.annotation;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 项目权限校验注解
|
||||
* 用于标注需要校验项目权限的方法
|
||||
*/
|
||||
@Target({ElementType.METHOD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface RequireProjectPermission {
|
||||
|
||||
/**
|
||||
* 项目ID参数名
|
||||
* 默认从方法参数中获取名为"projectId"的参数值
|
||||
*/
|
||||
String projectIdParam() default "projectId";
|
||||
|
||||
/**
|
||||
* 需要的权限编码
|
||||
* 例如: "risk:manage", "task:assign"
|
||||
*/
|
||||
String value();
|
||||
|
||||
/**
|
||||
* 错误提示信息
|
||||
*/
|
||||
String message() default "无权访问该项目资源";
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package cn.yinlihupo.common.annotation;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 项目角色校验注解
|
||||
* 用于标注需要校验项目角色的方法
|
||||
*/
|
||||
@Target({ElementType.METHOD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface RequireProjectRole {
|
||||
|
||||
/**
|
||||
* 项目ID参数名
|
||||
* 默认从方法参数中获取名为"projectId"的参数值
|
||||
*/
|
||||
String projectIdParam() default "projectId";
|
||||
|
||||
/**
|
||||
* 需要的项目角色
|
||||
* 可选值: manager, leader, member, observer
|
||||
*/
|
||||
String[] value();
|
||||
|
||||
/**
|
||||
* 是否允许系统管理员绕过角色校验
|
||||
* 默认true,管理员拥有所有权限
|
||||
*/
|
||||
boolean allowAdmin() default true;
|
||||
|
||||
/**
|
||||
* 错误提示信息
|
||||
*/
|
||||
String message() default "无权执行该操作";
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
package cn.yinlihupo.common.aspect;
|
||||
|
||||
import cn.yinlihupo.common.annotation.RequireProjectPermission;
|
||||
import cn.yinlihupo.common.annotation.RequireProjectRole;
|
||||
import cn.yinlihupo.common.exception.BusinessException;
|
||||
import cn.yinlihupo.common.util.SecurityUtils;
|
||||
import cn.yinlihupo.service.system.ProjectPermissionService;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.aspectj.lang.JoinPoint;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.annotation.Before;
|
||||
import org.aspectj.lang.reflect.MethodSignature;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Parameter;
|
||||
|
||||
/**
|
||||
* 项目权限校验切面
|
||||
*/
|
||||
@Slf4j
|
||||
@Aspect
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class ProjectPermissionAspect {
|
||||
|
||||
private final ProjectPermissionService projectPermissionService;
|
||||
|
||||
/**
|
||||
* 校验项目权限
|
||||
*/
|
||||
@Before("@annotation(cn.yinlihupo.common.annotation.RequireProjectPermission)")
|
||||
public void checkProjectPermission(JoinPoint joinPoint) {
|
||||
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
|
||||
Method method = signature.getMethod();
|
||||
RequireProjectPermission annotation = method.getAnnotation(RequireProjectPermission.class);
|
||||
|
||||
Long userId = SecurityUtils.getCurrentUserId();
|
||||
if (userId == null) {
|
||||
throw new BusinessException(403, "用户未登录");
|
||||
}
|
||||
|
||||
// 管理员直接放行
|
||||
if (projectPermissionService.isAdmin(userId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取项目ID
|
||||
Long projectId = extractProjectId(joinPoint, annotation.projectIdParam());
|
||||
if (projectId == null) {
|
||||
throw new BusinessException(400, "无法获取项目ID");
|
||||
}
|
||||
|
||||
// 校验权限
|
||||
String requiredPermission = annotation.value();
|
||||
boolean hasPermission = projectPermissionService.hasPermission(userId, projectId, requiredPermission);
|
||||
|
||||
if (!hasPermission) {
|
||||
log.warn("用户 [{}] 没有项目 [{}] 的权限 [{}]", userId, projectId, requiredPermission);
|
||||
throw new BusinessException(403, annotation.message());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验项目角色
|
||||
*/
|
||||
@Before("@annotation(cn.yinlihupo.common.annotation.RequireProjectRole)")
|
||||
public void checkProjectRole(JoinPoint joinPoint) {
|
||||
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
|
||||
Method method = signature.getMethod();
|
||||
RequireProjectRole annotation = method.getAnnotation(RequireProjectRole.class);
|
||||
|
||||
Long userId = SecurityUtils.getCurrentUserId();
|
||||
if (userId == null) {
|
||||
throw new BusinessException(403, "用户未登录");
|
||||
}
|
||||
|
||||
// 管理员直接放行(如果允许)
|
||||
if (annotation.allowAdmin() && projectPermissionService.isAdmin(userId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取项目ID
|
||||
Long projectId = extractProjectId(joinPoint, annotation.projectIdParam());
|
||||
if (projectId == null) {
|
||||
throw new BusinessException(400, "无法获取项目ID");
|
||||
}
|
||||
|
||||
// 获取用户项目角色
|
||||
String userRole = projectPermissionService.getUserProjectRole(userId, projectId);
|
||||
if (userRole == null) {
|
||||
throw new BusinessException(403, "您不是该项目的成员");
|
||||
}
|
||||
|
||||
// 校验角色
|
||||
String[] requiredRoles = annotation.value();
|
||||
boolean hasRole = false;
|
||||
for (String requiredRole : requiredRoles) {
|
||||
if (userRole.equals(requiredRole)) {
|
||||
hasRole = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasRole) {
|
||||
log.warn("用户 [{}] 在项目 [{}] 中的角色 [{}] 不符合要求 {}",
|
||||
userId, projectId, userRole, requiredRoles);
|
||||
throw new BusinessException(403, annotation.message());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从方法参数中提取项目ID
|
||||
*/
|
||||
private Long extractProjectId(JoinPoint joinPoint, String paramName) {
|
||||
Object[] args = joinPoint.getArgs();
|
||||
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
|
||||
Parameter[] parameters = signature.getMethod().getParameters();
|
||||
|
||||
// 1. 先从方法参数中查找
|
||||
for (int i = 0; i < parameters.length; i++) {
|
||||
if (parameters[i].getName().equals(paramName) && args[i] instanceof Long) {
|
||||
return (Long) args[i];
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 尝试从请求参数中获取
|
||||
try {
|
||||
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
|
||||
if (attributes != null) {
|
||||
HttpServletRequest request = attributes.getRequest();
|
||||
String projectIdStr = request.getParameter(paramName);
|
||||
if (projectIdStr != null && !projectIdStr.isEmpty()) {
|
||||
return Long.valueOf(projectIdStr);
|
||||
}
|
||||
|
||||
// 尝试从路径变量中获取
|
||||
Object pathVar = request.getAttribute(paramName);
|
||||
if (pathVar instanceof Long) {
|
||||
return (Long) pathVar;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.debug("从请求中获取项目ID失败: {}", e.getMessage());
|
||||
}
|
||||
|
||||
// 3. 查找任何名为projectId或包含projectId的参数
|
||||
for (int i = 0; i < parameters.length; i++) {
|
||||
String paramNameLower = parameters[i].getName().toLowerCase();
|
||||
if ((paramNameLower.contains("projectid") || paramNameLower.contains("project_id"))
|
||||
&& args[i] instanceof Long) {
|
||||
return (Long) args[i];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
71
src/main/java/cn/yinlihupo/common/config/SaTokenConfig.java
Normal file
71
src/main/java/cn/yinlihupo/common/config/SaTokenConfig.java
Normal file
@@ -0,0 +1,71 @@
|
||||
package cn.yinlihupo.common.config;
|
||||
|
||||
import cn.dev33.satoken.context.SaHolder;
|
||||
import cn.dev33.satoken.filter.SaServletFilter;
|
||||
import cn.dev33.satoken.interceptor.SaInterceptor;
|
||||
import cn.dev33.satoken.router.SaRouter;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.yinlihupo.common.core.BaseResponse;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
/**
|
||||
* Sa-Token 配置类
|
||||
*/
|
||||
@Slf4j
|
||||
@Configuration
|
||||
public class SaTokenConfig implements WebMvcConfigurer {
|
||||
|
||||
/**
|
||||
* 注册 Sa-Token 拦截器,打开注解式鉴权功能
|
||||
*/
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
// 注册 Sa-Token 拦截器,校验规则为 StpUtil.checkLogin() 登录校验
|
||||
registry.addInterceptor(new SaInterceptor(handle -> StpUtil.checkLogin()))
|
||||
.addPathPatterns("/**")
|
||||
.excludePathPatterns(
|
||||
"/auth/login",
|
||||
"/auth/register",
|
||||
"/error",
|
||||
"/swagger-ui/**",
|
||||
"/v3/api-docs/**"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册 [Sa-Token 全局过滤器]
|
||||
*/
|
||||
@Bean
|
||||
public SaServletFilter getSaServletFilter() {
|
||||
return new SaServletFilter()
|
||||
// 指定 [拦截路由] 与 [放行路由]
|
||||
.addInclude("/**")
|
||||
.addExclude("/favicon.ico", "/swagger-ui/**", "/v3/api-docs/**")
|
||||
// 认证函数: 每次请求执行
|
||||
.setAuth(obj -> {
|
||||
log.debug("---------- 进入 Sa-Token 全局认证 -----------");
|
||||
})
|
||||
// 异常处理函数:每次认证函数发生异常时执行
|
||||
.setError(e -> {
|
||||
log.error("Sa-Token 认证异常: {}", e.getMessage());
|
||||
return new BaseResponse<>(403, e.getMessage(), null);
|
||||
})
|
||||
// 前置函数:在每次认证函数之前执行
|
||||
.setBeforeAuth(obj -> {
|
||||
// 设置跨域响应头
|
||||
SaHolder.getResponse()
|
||||
.setHeader("Access-Control-Allow-Origin", "*")
|
||||
.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
|
||||
.setHeader("Access-Control-Allow-Headers", "*")
|
||||
.setHeader("Access-Control-Max-Age", "3600");
|
||||
// 如果是预检请求,则立即返回到前端
|
||||
SaRouter.match("OPTIONS")
|
||||
.free(r -> log.debug("--------OPTIONS预检请求,不做处理"))
|
||||
.back();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package cn.yinlihupo.common.config;
|
||||
|
||||
import cn.dev33.satoken.stp.StpInterface;
|
||||
import cn.yinlihupo.mapper.SysPermissionMapper;
|
||||
import cn.yinlihupo.mapper.SysUserMapper;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Sa-Token 权限认证接口扩展
|
||||
* 实现 StpInterface 接口,自定义权限和角色获取逻辑
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class SaTokenPermissionExtension implements StpInterface {
|
||||
|
||||
private final SysUserMapper sysUserMapper;
|
||||
private final SysPermissionMapper sysPermissionMapper;
|
||||
|
||||
/**
|
||||
* 返回指定账号id所拥有的权限码集合
|
||||
*
|
||||
* @param loginId 账号id
|
||||
* @param loginType 账号类型
|
||||
* @return 权限码集合
|
||||
*/
|
||||
@Override
|
||||
public List<String> getPermissionList(Object loginId, String loginType) {
|
||||
// 项目权限通过 ProjectPermissionService 动态校验
|
||||
// 这里返回系统级别的权限
|
||||
try {
|
||||
Long userId = Long.valueOf(loginId.toString());
|
||||
List<String> roleCodes = sysUserMapper.selectRoleCodesByUserId(userId);
|
||||
if (roleCodes.isEmpty()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
// 根据角色查询权限
|
||||
return sysPermissionMapper.selectPermissionCodesByRoleCodes(roleCodes);
|
||||
} catch (Exception e) {
|
||||
log.error("获取用户权限列表失败: {}", e.getMessage());
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回指定账号id所拥有的角色标识集合
|
||||
*
|
||||
* @param loginId 账号id
|
||||
* @param loginType 账号类型
|
||||
* @return 角色标识集合
|
||||
*/
|
||||
@Override
|
||||
public List<String> getRoleList(Object loginId, String loginType) {
|
||||
try {
|
||||
Long userId = Long.valueOf(loginId.toString());
|
||||
return sysUserMapper.selectRoleCodesByUserId(userId);
|
||||
} catch (Exception e) {
|
||||
log.error("获取用户角色列表失败: {}", e.getMessage());
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package cn.yinlihupo.common.constant;
|
||||
|
||||
/**
|
||||
* 权限常量类
|
||||
* 定义系统角色、项目角色和权限编码
|
||||
*/
|
||||
public class PermissionConstants {
|
||||
|
||||
// ==================== 系统角色 ====================
|
||||
/** 系统管理员 */
|
||||
public static final String ROLE_ADMIN = "admin";
|
||||
/** 项目经理 */
|
||||
public static final String ROLE_PROJECT_MANAGER = "project_manager";
|
||||
/** 团队负责人 */
|
||||
public static final String ROLE_TEAM_LEADER = "team_leader";
|
||||
/** 普通成员 */
|
||||
public static final String ROLE_MEMBER = "member";
|
||||
|
||||
// ==================== 项目角色 ====================
|
||||
/** 项目经理 - 拥有项目全部权限 */
|
||||
public static final String PROJECT_ROLE_MANAGER = "manager";
|
||||
/** 负责人 - 协助管理项目 */
|
||||
public static final String PROJECT_ROLE_LEADER = "leader";
|
||||
/** 普通成员 */
|
||||
public static final String PROJECT_ROLE_MEMBER = "member";
|
||||
/** 观察者 - 只读权限 */
|
||||
public static final String PROJECT_ROLE_OBSERVER = "observer";
|
||||
|
||||
// ==================== 项目权限编码 ====================
|
||||
// 项目查看权限
|
||||
public static final String PERM_PROJECT_VIEW = "project:view";
|
||||
// 项目管理权限(编辑、删除)
|
||||
public static final String PERM_PROJECT_MANAGE = "project:manage";
|
||||
|
||||
// 风险查看权限
|
||||
public static final String PERM_RISK_VIEW = "risk:view";
|
||||
// 风险管控权限(创建、编辑、删除)
|
||||
public static final String PERM_RISK_MANAGE = "risk:manage";
|
||||
|
||||
// 资源查看权限
|
||||
public static final String PERM_RESOURCE_VIEW = "resource:view";
|
||||
// 资源管控权限(申请、审批、分配)
|
||||
public static final String PERM_RESOURCE_MANAGE = "resource:manage";
|
||||
|
||||
// 里程碑查看权限
|
||||
public static final String PERM_MILESTONE_VIEW = "milestone:view";
|
||||
// 里程碑推进权限
|
||||
public static final String PERM_MILESTONE_PUSH = "milestone:push";
|
||||
|
||||
// 任务工单查看权限
|
||||
public static final String PERM_TASK_VIEW = "task:view";
|
||||
// 任务工单分配权限
|
||||
public static final String PERM_TASK_ASSIGN = "task:assign";
|
||||
// 任务工单处理权限
|
||||
public static final String PERM_TASK_PROCESS = "task:process";
|
||||
|
||||
// 日报查看权限
|
||||
public static final String PERM_REPORT_VIEW = "report:view";
|
||||
// 日报上传权限
|
||||
public static final String PERM_REPORT_CREATE = "report:create";
|
||||
|
||||
// 知识库查看权限
|
||||
public static final String PERM_KB_VIEW = "kb:view";
|
||||
// 知识库上传权限
|
||||
public static final String PERM_KB_CREATE = "kb:create";
|
||||
|
||||
// ==================== 数据权限范围 ====================
|
||||
/** 全部数据 */
|
||||
public static final int DATA_SCOPE_ALL = 1;
|
||||
/** 本部门数据 */
|
||||
public static final int DATA_SCOPE_DEPT = 2;
|
||||
/** 本人数据 */
|
||||
public static final int DATA_SCOPE_SELF = 3;
|
||||
/** 本项目数据 */
|
||||
public static final int DATA_SCOPE_PROJECT = 4;
|
||||
|
||||
private PermissionConstants() {
|
||||
}
|
||||
}
|
||||
125
src/main/java/cn/yinlihupo/common/util/SecurityUtils.java
Normal file
125
src/main/java/cn/yinlihupo/common/util/SecurityUtils.java
Normal file
@@ -0,0 +1,125 @@
|
||||
package cn.yinlihupo.common.util;
|
||||
|
||||
import cn.dev33.satoken.session.SaSession;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.yinlihupo.common.constant.PermissionConstants;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 安全工具类 (Sa-Token 版本)
|
||||
* 提供获取当前用户、权限校验等方法
|
||||
*/
|
||||
public class SecurityUtils {
|
||||
|
||||
/**
|
||||
* 获取当前登录用户ID
|
||||
*/
|
||||
public static Long getCurrentUserId() {
|
||||
try {
|
||||
Object loginId = StpUtil.getLoginIdDefaultNull();
|
||||
if (loginId == null) {
|
||||
return null;
|
||||
}
|
||||
return Long.valueOf(loginId.toString());
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前用户角色列表
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Set<String> getCurrentUserRoles() {
|
||||
try {
|
||||
SaSession session = StpUtil.getSession();
|
||||
Object roles = session.get("roles");
|
||||
if (roles instanceof Set) {
|
||||
return (Set<String>) roles;
|
||||
}
|
||||
if (roles instanceof List) {
|
||||
return Set.copyOf((List<String>) roles);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// 未登录或会话不存在
|
||||
}
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断当前用户是否为系统管理员
|
||||
*/
|
||||
public static boolean isAdmin() {
|
||||
return hasRole(PermissionConstants.ROLE_ADMIN);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断当前用户是否拥有指定角色
|
||||
*/
|
||||
public static boolean hasRole(String role) {
|
||||
return StpUtil.hasRole(role);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断当前用户是否拥有任意一个指定角色
|
||||
*/
|
||||
public static boolean hasAnyRole(String... roles) {
|
||||
for (String role : roles) {
|
||||
if (StpUtil.hasRole(role)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断当前用户是否拥有指定权限
|
||||
*/
|
||||
public static boolean hasPermission(String permission) {
|
||||
return StpUtil.hasPermission(permission);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断当前用户是否拥有任意一个指定权限
|
||||
*/
|
||||
public static boolean hasAnyPermission(String... permissions) {
|
||||
for (String permission : permissions) {
|
||||
if (StpUtil.hasPermission(permission)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录并缓存用户角色
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param roles 角色列表
|
||||
*/
|
||||
public static void login(Long userId, Set<String> roles) {
|
||||
StpUtil.login(userId);
|
||||
SaSession session = StpUtil.getSession();
|
||||
session.set("roles", roles);
|
||||
}
|
||||
|
||||
/**
|
||||
* 登出
|
||||
*/
|
||||
public static void logout() {
|
||||
StpUtil.logout();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前登录Token
|
||||
*/
|
||||
public static String getTokenValue() {
|
||||
return StpUtil.getTokenValue();
|
||||
}
|
||||
|
||||
private SecurityUtils() {
|
||||
}
|
||||
}
|
||||
66
src/main/java/cn/yinlihupo/domain/entity/SysPermission.java
Normal file
66
src/main/java/cn/yinlihupo/domain/entity/SysPermission.java
Normal file
@@ -0,0 +1,66 @@
|
||||
package cn.yinlihupo.domain.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 系统权限实体类
|
||||
* 对应数据库表: sys_permission
|
||||
*/
|
||||
@Data
|
||||
@TableName("sys_permission")
|
||||
public class SysPermission {
|
||||
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
private Long id;
|
||||
|
||||
/** 父级权限ID */
|
||||
private Long parentId;
|
||||
|
||||
/** 权限编码 */
|
||||
private String permissionCode;
|
||||
|
||||
/** 权限名称 */
|
||||
private String permissionName;
|
||||
|
||||
/** 权限类型: 1-菜单, 2-按钮, 3-接口 */
|
||||
private Integer permissionType;
|
||||
|
||||
/** 路由路径 */
|
||||
private String path;
|
||||
|
||||
/** 组件路径 */
|
||||
private String component;
|
||||
|
||||
/** 图标 */
|
||||
private String icon;
|
||||
|
||||
/** 接口URL */
|
||||
private String apiUrl;
|
||||
|
||||
/** 接口方法 */
|
||||
private String apiMethod;
|
||||
|
||||
/** 排序 */
|
||||
private Integer sortOrder;
|
||||
|
||||
/** 是否可见: 1-是, 0-否 */
|
||||
private Integer visible;
|
||||
|
||||
/** 状态: 1-正常, 0-禁用 */
|
||||
private Integer status;
|
||||
|
||||
/** 创建时间 */
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/** 更新时间 */
|
||||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
/** 删除标记 */
|
||||
@TableLogic
|
||||
private Integer deleted;
|
||||
}
|
||||
57
src/main/java/cn/yinlihupo/domain/entity/SysRole.java
Normal file
57
src/main/java/cn/yinlihupo/domain/entity/SysRole.java
Normal file
@@ -0,0 +1,57 @@
|
||||
package cn.yinlihupo.domain.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 系统角色实体类
|
||||
* 对应数据库表: sys_role
|
||||
*/
|
||||
@Data
|
||||
@TableName("sys_role")
|
||||
public class SysRole {
|
||||
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
private Long id;
|
||||
|
||||
/** 角色编码 */
|
||||
private String roleCode;
|
||||
|
||||
/** 角色名称 */
|
||||
private String roleName;
|
||||
|
||||
/** 角色类型: system-系统角色, custom-自定义角色 */
|
||||
private String roleType;
|
||||
|
||||
/** 角色描述 */
|
||||
private String description;
|
||||
|
||||
/** 数据权限: 1-全部, 2-本部门, 3-本人 */
|
||||
private Integer dataScope;
|
||||
|
||||
/** 排序 */
|
||||
private Integer sortOrder;
|
||||
|
||||
/** 状态: 1-正常, 0-禁用 */
|
||||
private Integer status;
|
||||
|
||||
/** 创建人 */
|
||||
private Long createBy;
|
||||
|
||||
/** 创建时间 */
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/** 更新人 */
|
||||
private Long updateBy;
|
||||
|
||||
/** 更新时间 */
|
||||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
/** 删除标记 */
|
||||
@TableLogic
|
||||
private Integer deleted;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package cn.yinlihupo.domain.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 角色权限关联实体类
|
||||
* 对应数据库表: sys_role_permission
|
||||
*/
|
||||
@Data
|
||||
@TableName("sys_role_permission")
|
||||
public class SysRolePermission {
|
||||
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
private Long id;
|
||||
|
||||
/** 角色ID */
|
||||
private Long roleId;
|
||||
|
||||
/** 权限ID */
|
||||
private Long permissionId;
|
||||
|
||||
/** 创建时间 */
|
||||
private LocalDateTime createTime;
|
||||
}
|
||||
82
src/main/java/cn/yinlihupo/domain/entity/SysUser.java
Normal file
82
src/main/java/cn/yinlihupo/domain/entity/SysUser.java
Normal file
@@ -0,0 +1,82 @@
|
||||
package cn.yinlihupo.domain.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 系统用户实体类
|
||||
* 对应数据库表: sys_user
|
||||
*/
|
||||
@Data
|
||||
@TableName("sys_user")
|
||||
public class SysUser {
|
||||
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
private Long id;
|
||||
|
||||
/** 用户名 */
|
||||
private String username;
|
||||
|
||||
/** 密码(加密) */
|
||||
private String password;
|
||||
|
||||
/** 真实姓名 */
|
||||
private String realName;
|
||||
|
||||
/** 昵称 */
|
||||
private String nickname;
|
||||
|
||||
/** 头像URL */
|
||||
private String avatar;
|
||||
|
||||
/** 性别: 0-未知, 1-男, 2-女 */
|
||||
private Integer gender;
|
||||
|
||||
/** 手机号 */
|
||||
private String phone;
|
||||
|
||||
/** 邮箱 */
|
||||
private String email;
|
||||
|
||||
/** 所属部门ID */
|
||||
private Long deptId;
|
||||
|
||||
/** 职位 */
|
||||
private String position;
|
||||
|
||||
/** 工号 */
|
||||
private String employeeNo;
|
||||
|
||||
/** 入职日期 */
|
||||
private LocalDate entryDate;
|
||||
|
||||
/** 状态: 1-正常, 0-禁用, 2-锁定 */
|
||||
private Integer status;
|
||||
|
||||
/** 最后登录时间 */
|
||||
private LocalDateTime lastLoginTime;
|
||||
|
||||
/** 最后登录IP */
|
||||
private String lastLoginIp;
|
||||
|
||||
/** 创建人 */
|
||||
private Long createBy;
|
||||
|
||||
/** 创建时间 */
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/** 更新人 */
|
||||
private Long updateBy;
|
||||
|
||||
/** 更新时间 */
|
||||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
/** 删除标记 */
|
||||
@TableLogic
|
||||
private Integer deleted;
|
||||
}
|
||||
29
src/main/java/cn/yinlihupo/domain/entity/SysUserRole.java
Normal file
29
src/main/java/cn/yinlihupo/domain/entity/SysUserRole.java
Normal file
@@ -0,0 +1,29 @@
|
||||
package cn.yinlihupo.domain.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 用户角色关联实体类
|
||||
* 对应数据库表: sys_user_role
|
||||
*/
|
||||
@Data
|
||||
@TableName("sys_user_role")
|
||||
public class SysUserRole {
|
||||
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
private Long id;
|
||||
|
||||
/** 用户ID */
|
||||
private Long userId;
|
||||
|
||||
/** 角色ID */
|
||||
private Long roleId;
|
||||
|
||||
/** 创建时间 */
|
||||
private LocalDateTime createTime;
|
||||
}
|
||||
@@ -3,10 +3,31 @@ package cn.yinlihupo.mapper;
|
||||
import cn.yinlihupo.domain.entity.FileAttachment;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 文件附件Mapper接口
|
||||
*/
|
||||
@Mapper
|
||||
public interface FileAttachmentMapper extends BaseMapper<FileAttachment> {
|
||||
|
||||
/**
|
||||
* 查询某业务实体的附件列表(含上传者信息)
|
||||
*/
|
||||
List<Map<String, Object>> selectAttachmentsByRelated(@Param("relatedType") String relatedType,
|
||||
@Param("relatedId") Long relatedId);
|
||||
|
||||
/**
|
||||
* 分页查询项目下所有附件(支持文件类型筛选)
|
||||
*/
|
||||
List<Map<String, Object>> selectAttachmentsByProject(@Param("projectId") Long projectId,
|
||||
@Param("fileType") String fileType);
|
||||
|
||||
/**
|
||||
* 统计用户上传文件数量和总大小
|
||||
*/
|
||||
List<Map<String, Object>> selectUploaderStats(@Param("projectId") Long projectId);
|
||||
}
|
||||
|
||||
@@ -3,10 +3,46 @@ package cn.yinlihupo.mapper;
|
||||
import cn.yinlihupo.domain.entity.Project;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 项目Mapper接口
|
||||
*/
|
||||
@Mapper
|
||||
public interface ProjectMapper extends BaseMapper<Project> {
|
||||
|
||||
/**
|
||||
* 分页查询项目列表(支持多条件筛选)
|
||||
*/
|
||||
List<Project> selectPageList(@Param("status") String status,
|
||||
@Param("priority") String priority,
|
||||
@Param("riskLevel") String riskLevel,
|
||||
@Param("managerId") Long managerId,
|
||||
@Param("keyword") String keyword,
|
||||
@Param("planStartDateFrom") LocalDate planStartDateFrom,
|
||||
@Param("planStartDateTo") LocalDate planStartDateTo);
|
||||
|
||||
/**
|
||||
* 查询指定用户有权限的项目列表(仅自己管理或参与的)
|
||||
*/
|
||||
List<Project> selectProjectsByUserId(@Param("userId") Long userId);
|
||||
|
||||
/**
|
||||
* 查询项目详情
|
||||
*/
|
||||
Project selectDetailById(@Param("projectId") Long projectId);
|
||||
|
||||
/**
|
||||
* 统计各状态项目数量
|
||||
*/
|
||||
List<Map<String, Object>> countByStatus();
|
||||
|
||||
/**
|
||||
* 查询即将超期的项目(N天内)
|
||||
*/
|
||||
List<Project> selectAboutToExpire(@Param("days") int days);
|
||||
}
|
||||
|
||||
@@ -3,10 +3,30 @@ package cn.yinlihupo.mapper;
|
||||
import cn.yinlihupo.domain.entity.ProjectMember;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 项目成员Mapper接口
|
||||
*/
|
||||
@Mapper
|
||||
public interface ProjectMemberMapper extends BaseMapper<ProjectMember> {
|
||||
|
||||
/**
|
||||
* 查询项目成员列表(含用户详情)
|
||||
*/
|
||||
List<Map<String, Object>> selectMembersWithUser(@Param("projectId") Long projectId);
|
||||
|
||||
/**
|
||||
* 查询用户在某项目中的角色
|
||||
*/
|
||||
String selectRoleByUserAndProject(@Param("projectId") Long projectId,
|
||||
@Param("userId") Long userId);
|
||||
|
||||
/**
|
||||
* 统计某用户参与的项目数量(按角色分组)
|
||||
*/
|
||||
List<Map<String, Object>> countUserProjects(@Param("userId") Long userId);
|
||||
}
|
||||
|
||||
@@ -3,10 +3,36 @@ package cn.yinlihupo.mapper;
|
||||
import cn.yinlihupo.domain.entity.ProjectMilestone;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 项目里程碑Mapper接口
|
||||
*/
|
||||
@Mapper
|
||||
public interface ProjectMilestoneMapper extends BaseMapper<ProjectMilestone> {
|
||||
|
||||
/**
|
||||
* 查询项目里程碑列表(含任务统计)
|
||||
*/
|
||||
List<Map<String, Object>> selectMilestoneListWithStats(@Param("projectId") Long projectId,
|
||||
@Param("status") String status);
|
||||
|
||||
/**
|
||||
* 查询已延期的关键里程碑
|
||||
*/
|
||||
List<ProjectMilestone> selectDelayedKeyMilestones(@Param("projectId") Long projectId);
|
||||
|
||||
/**
|
||||
* 查询即将到期的里程碑(N天内)
|
||||
*/
|
||||
List<ProjectMilestone> selectUpcomingMilestones(@Param("projectId") Long projectId,
|
||||
@Param("days") int days);
|
||||
|
||||
/**
|
||||
* 查询里程碑完成进度统计
|
||||
*/
|
||||
Map<String, Object> selectMilestoneProgressSummary(@Param("projectId") Long projectId);
|
||||
}
|
||||
|
||||
@@ -3,10 +3,38 @@ package cn.yinlihupo.mapper;
|
||||
import cn.yinlihupo.domain.entity.Resource;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 资源Mapper接口
|
||||
*/
|
||||
@Mapper
|
||||
public interface ResourceMapper extends BaseMapper<Resource> {
|
||||
|
||||
/**
|
||||
* 分页查询资源列表(含负责人信息,支持多条件筛选)
|
||||
*/
|
||||
List<Map<String, Object>> selectResourcePageWithResponsible(@Param("projectId") Long projectId,
|
||||
@Param("resourceType") String resourceType,
|
||||
@Param("status") String status,
|
||||
@Param("keyword") String keyword);
|
||||
|
||||
/**
|
||||
* 查询资源预算汇总(按类型统计)
|
||||
*/
|
||||
List<Map<String, Object>> selectResourceBudgetSummary(@Param("projectId") Long projectId);
|
||||
|
||||
/**
|
||||
* 查询即将到位但尚未到位的资源(N天内)
|
||||
*/
|
||||
List<Resource> selectPendingArrivalResources(@Param("projectId") Long projectId,
|
||||
@Param("days") int days);
|
||||
|
||||
/**
|
||||
* 查询待审批的资源申请
|
||||
*/
|
||||
List<Map<String, Object>> selectPendingApprovalResources(@Param("projectId") Long projectId);
|
||||
}
|
||||
|
||||
@@ -3,10 +3,38 @@ package cn.yinlihupo.mapper;
|
||||
import cn.yinlihupo.domain.entity.Risk;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 风险Mapper接口
|
||||
*/
|
||||
@Mapper
|
||||
public interface RiskMapper extends BaseMapper<Risk> {
|
||||
|
||||
/**
|
||||
* 分页查询风险列表(含负责人信息,支持多条件筛选)
|
||||
*/
|
||||
List<Map<String, Object>> selectRiskPageWithOwner(@Param("projectId") Long projectId,
|
||||
@Param("category") String category,
|
||||
@Param("riskLevel") String riskLevel,
|
||||
@Param("status") String status,
|
||||
@Param("keyword") String keyword);
|
||||
|
||||
/**
|
||||
* 查询未解决的高/严重风险(用于预警)
|
||||
*/
|
||||
List<Map<String, Object>> selectHighRisksUnresolved(@Param("projectId") Long projectId);
|
||||
|
||||
/**
|
||||
* 按风险等级统计各分类数量
|
||||
*/
|
||||
List<Map<String, Object>> countRiskByCategoryAndLevel(@Param("projectId") Long projectId);
|
||||
|
||||
/**
|
||||
* 查询风险处理趋势(近6个月)
|
||||
*/
|
||||
List<Map<String, Object>> selectRiskTrend(@Param("projectId") Long projectId);
|
||||
}
|
||||
|
||||
30
src/main/java/cn/yinlihupo/mapper/SysPermissionMapper.java
Normal file
30
src/main/java/cn/yinlihupo/mapper/SysPermissionMapper.java
Normal file
@@ -0,0 +1,30 @@
|
||||
package cn.yinlihupo.mapper;
|
||||
|
||||
import cn.yinlihupo.domain.entity.SysPermission;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 系统权限Mapper接口
|
||||
*/
|
||||
@Mapper
|
||||
public interface SysPermissionMapper extends BaseMapper<SysPermission> {
|
||||
|
||||
/**
|
||||
* 根据角色编码列表查询权限编码列表
|
||||
*/
|
||||
List<String> selectPermissionCodesByRoleCodes(@Param("roleCodes") List<String> roleCodes);
|
||||
|
||||
/**
|
||||
* 查询菜单树(含按钮权限)
|
||||
*/
|
||||
List<SysPermission> selectMenuTree();
|
||||
|
||||
/**
|
||||
* 根据角色ID查询权限列表
|
||||
*/
|
||||
List<SysPermission> selectPermsByRoleId(@Param("roleId") Long roleId);
|
||||
}
|
||||
12
src/main/java/cn/yinlihupo/mapper/SysRoleMapper.java
Normal file
12
src/main/java/cn/yinlihupo/mapper/SysRoleMapper.java
Normal file
@@ -0,0 +1,12 @@
|
||||
package cn.yinlihupo.mapper;
|
||||
|
||||
import cn.yinlihupo.domain.entity.SysRole;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* 系统角色Mapper接口
|
||||
*/
|
||||
@Mapper
|
||||
public interface SysRoleMapper extends BaseMapper<SysRole> {
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package cn.yinlihupo.mapper;
|
||||
|
||||
import cn.yinlihupo.domain.entity.SysRolePermission;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* 角色权限关联Mapper接口
|
||||
*/
|
||||
@Mapper
|
||||
public interface SysRolePermissionMapper extends BaseMapper<SysRolePermission> {
|
||||
}
|
||||
37
src/main/java/cn/yinlihupo/mapper/SysUserMapper.java
Normal file
37
src/main/java/cn/yinlihupo/mapper/SysUserMapper.java
Normal file
@@ -0,0 +1,37 @@
|
||||
package cn.yinlihupo.mapper;
|
||||
|
||||
import cn.yinlihupo.domain.entity.SysUser;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 系统用户Mapper接口
|
||||
*/
|
||||
@Mapper
|
||||
public interface SysUserMapper extends BaseMapper<SysUser> {
|
||||
|
||||
/**
|
||||
* 根据用户ID查询角色编码列表
|
||||
*/
|
||||
List<String> selectRoleCodesByUserId(@Param("userId") Long userId);
|
||||
|
||||
/**
|
||||
* 根据用户名查询用户
|
||||
*/
|
||||
SysUser selectByUsername(@Param("username") String username);
|
||||
|
||||
/**
|
||||
* 分页查询用户列表(支持按部门、状态、关键字筛选)
|
||||
*/
|
||||
List<SysUser> selectPageList(@Param("deptId") Long deptId,
|
||||
@Param("status") Integer status,
|
||||
@Param("keyword") String keyword);
|
||||
|
||||
/**
|
||||
* 查询项目成员用户详情列表
|
||||
*/
|
||||
List<SysUser> selectUsersByProjectId(@Param("projectId") Long projectId);
|
||||
}
|
||||
12
src/main/java/cn/yinlihupo/mapper/SysUserRoleMapper.java
Normal file
12
src/main/java/cn/yinlihupo/mapper/SysUserRoleMapper.java
Normal file
@@ -0,0 +1,12 @@
|
||||
package cn.yinlihupo.mapper;
|
||||
|
||||
import cn.yinlihupo.domain.entity.SysUserRole;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* 用户角色关联Mapper接口
|
||||
*/
|
||||
@Mapper
|
||||
public interface SysUserRoleMapper extends BaseMapper<SysUserRole> {
|
||||
}
|
||||
@@ -3,10 +3,45 @@ package cn.yinlihupo.mapper;
|
||||
import cn.yinlihupo.domain.entity.Task;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 任务Mapper接口
|
||||
*/
|
||||
@Mapper
|
||||
public interface TaskMapper extends BaseMapper<Task> {
|
||||
|
||||
/**
|
||||
* 分页查询任务列表(含负责人信息,支持多条件筛选)
|
||||
*/
|
||||
List<Map<String, Object>> selectTaskPageWithAssignee(@Param("projectId") Long projectId,
|
||||
@Param("milestoneId") Long milestoneId,
|
||||
@Param("assigneeId") Long assigneeId,
|
||||
@Param("status") String status,
|
||||
@Param("priority") String priority,
|
||||
@Param("keyword") String keyword);
|
||||
|
||||
/**
|
||||
* 查询我的待办任务(按用户ID)
|
||||
*/
|
||||
List<Task> selectMyTasks(@Param("userId") Long userId,
|
||||
@Param("projectId") Long projectId);
|
||||
|
||||
/**
|
||||
* 查询超期未完成的任务
|
||||
*/
|
||||
List<Map<String, Object>> selectOverdueTasks(@Param("projectId") Long projectId);
|
||||
|
||||
/**
|
||||
* 查询任务的前置依赖关系
|
||||
*/
|
||||
List<Map<String, Object>> selectDependencies(@Param("taskId") Long taskId);
|
||||
|
||||
/**
|
||||
* 统计项目任务状态分布
|
||||
*/
|
||||
List<Map<String, Object>> countTasksByStatus(@Param("projectId") Long projectId);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,160 @@
|
||||
package cn.yinlihupo.service.system;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 项目权限服务接口
|
||||
* 提供项目级别的权限校验功能
|
||||
*/
|
||||
public interface ProjectPermissionService {
|
||||
|
||||
/**
|
||||
* 判断用户是否为系统管理员
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return true-是管理员
|
||||
*/
|
||||
boolean isAdmin(Long userId);
|
||||
|
||||
/**
|
||||
* 获取用户在项目中的角色
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param projectId 项目ID
|
||||
* @return 项目角色编码(manager/leader/member/observer),未参与项目返回null
|
||||
*/
|
||||
String getUserProjectRole(Long userId, Long projectId);
|
||||
|
||||
/**
|
||||
* 判断用户是否参与项目
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param projectId 项目ID
|
||||
* @return true-是项目成员
|
||||
*/
|
||||
boolean isProjectMember(Long userId, Long projectId);
|
||||
|
||||
/**
|
||||
* 判断用户是否为项目经理
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param projectId 项目ID
|
||||
* @return true-是项目经理
|
||||
*/
|
||||
boolean isProjectManager(Long userId, Long projectId);
|
||||
|
||||
/**
|
||||
* 判断用户是否为项目负责人或经理
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param projectId 项目ID
|
||||
* @return true-是负责人或经理
|
||||
*/
|
||||
boolean isProjectManagerOrLeader(Long userId, Long projectId);
|
||||
|
||||
/**
|
||||
* 获取用户在项目中拥有的所有权限编码
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param projectId 项目ID
|
||||
* @return 权限编码集合
|
||||
*/
|
||||
Set<String> getUserProjectPermissions(Long userId, Long projectId);
|
||||
|
||||
/**
|
||||
* 校验用户是否拥有指定项目权限
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param projectId 项目ID
|
||||
* @param permission 权限编码
|
||||
* @return true-有权限
|
||||
*/
|
||||
boolean hasPermission(Long userId, Long projectId, String permission);
|
||||
|
||||
/**
|
||||
* 校验用户是否拥有任意一个指定权限
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param projectId 项目ID
|
||||
* @param permissions 权限编码数组
|
||||
* @return true-有权限
|
||||
*/
|
||||
boolean hasAnyPermission(Long userId, Long projectId, String... permissions);
|
||||
|
||||
// ==================== 便捷权限校验方法 ====================
|
||||
|
||||
/**
|
||||
* 是否有查看项目的权限
|
||||
*/
|
||||
boolean canViewProject(Long userId, Long projectId);
|
||||
|
||||
/**
|
||||
* 是否有管理项目的权限(编辑、删除)
|
||||
*/
|
||||
boolean canManageProject(Long userId, Long projectId);
|
||||
|
||||
/**
|
||||
* 是否有查看风险的权限
|
||||
*/
|
||||
boolean canViewRisk(Long userId, Long projectId);
|
||||
|
||||
/**
|
||||
* 是否有管控风险的权限(创建、编辑、删除)
|
||||
*/
|
||||
boolean canManageRisk(Long userId, Long projectId);
|
||||
|
||||
/**
|
||||
* 是否有查看资源的权限
|
||||
*/
|
||||
boolean canViewResource(Long userId, Long projectId);
|
||||
|
||||
/**
|
||||
* 是否有管控资源的权限(申请、审批、分配)
|
||||
*/
|
||||
boolean canManageResource(Long userId, Long projectId);
|
||||
|
||||
/**
|
||||
* 是否有查看里程碑的权限
|
||||
*/
|
||||
boolean canViewMilestone(Long userId, Long projectId);
|
||||
|
||||
/**
|
||||
* 是否有推进里程碑的权限
|
||||
*/
|
||||
boolean canPushMilestone(Long userId, Long projectId);
|
||||
|
||||
/**
|
||||
* 是否有查看任务工单的权限
|
||||
*/
|
||||
boolean canViewTask(Long userId, Long projectId);
|
||||
|
||||
/**
|
||||
* 是否有分配任务工单的权限
|
||||
*/
|
||||
boolean canAssignTask(Long userId, Long projectId);
|
||||
|
||||
/**
|
||||
* 是否有处理任务工单的权限
|
||||
*/
|
||||
boolean canProcessTask(Long userId, Long projectId);
|
||||
|
||||
/**
|
||||
* 是否有查看日报的权限
|
||||
*/
|
||||
boolean canViewReport(Long userId, Long projectId);
|
||||
|
||||
/**
|
||||
* 是否有上传日报的权限
|
||||
*/
|
||||
boolean canCreateReport(Long userId, Long projectId);
|
||||
|
||||
/**
|
||||
* 是否有查看知识库的权限
|
||||
*/
|
||||
boolean canViewKnowledgeBase(Long userId, Long projectId);
|
||||
|
||||
/**
|
||||
* 是否有上传知识库的权限
|
||||
*/
|
||||
boolean canCreateKnowledgeBase(Long userId, Long projectId);
|
||||
}
|
||||
@@ -0,0 +1,278 @@
|
||||
package cn.yinlihupo.service.system.impl;
|
||||
|
||||
import cn.yinlihupo.common.constant.PermissionConstants;
|
||||
import cn.yinlihupo.domain.entity.Project;
|
||||
import cn.yinlihupo.domain.entity.ProjectMember;
|
||||
import cn.yinlihupo.mapper.ProjectMapper;
|
||||
import cn.yinlihupo.mapper.ProjectMemberMapper;
|
||||
import cn.yinlihupo.mapper.SysUserMapper;
|
||||
import cn.yinlihupo.service.system.ProjectPermissionService;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 项目权限服务实现类
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class ProjectPermissionServiceImpl implements ProjectPermissionService {
|
||||
|
||||
private final SysUserMapper sysUserMapper;
|
||||
private final ProjectMemberMapper projectMemberMapper;
|
||||
private final ProjectMapper projectMapper;
|
||||
|
||||
@Override
|
||||
public boolean isAdmin(Long userId) {
|
||||
if (userId == null) {
|
||||
return false;
|
||||
}
|
||||
List<String> roleCodes = sysUserMapper.selectRoleCodesByUserId(userId);
|
||||
return roleCodes.contains(PermissionConstants.ROLE_ADMIN);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUserProjectRole(Long userId, Long projectId) {
|
||||
if (userId == null || projectId == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 先检查是否为项目经理(project.manager_id)
|
||||
Project project = projectMapper.selectById(projectId);
|
||||
if (project != null && userId.equals(project.getManagerId())) {
|
||||
return PermissionConstants.PROJECT_ROLE_MANAGER;
|
||||
}
|
||||
|
||||
// 查询项目成员表
|
||||
LambdaQueryWrapper<ProjectMember> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(ProjectMember::getProjectId, projectId)
|
||||
.eq(ProjectMember::getUserId, userId)
|
||||
.eq(ProjectMember::getStatus, 1)
|
||||
.eq(ProjectMember::getDeleted, 0);
|
||||
ProjectMember member = projectMemberMapper.selectOne(wrapper);
|
||||
|
||||
return member != null ? member.getRoleCode() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isProjectMember(Long userId, Long projectId) {
|
||||
return getUserProjectRole(userId, projectId) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isProjectManager(Long userId, Long projectId) {
|
||||
String role = getUserProjectRole(userId, projectId);
|
||||
return PermissionConstants.PROJECT_ROLE_MANAGER.equals(role);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isProjectManagerOrLeader(Long userId, Long projectId) {
|
||||
String role = getUserProjectRole(userId, projectId);
|
||||
return PermissionConstants.PROJECT_ROLE_MANAGER.equals(role)
|
||||
|| PermissionConstants.PROJECT_ROLE_LEADER.equals(role);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getUserProjectPermissions(Long userId, Long projectId) {
|
||||
Set<String> permissions = new HashSet<>();
|
||||
|
||||
if (userId == null || projectId == null) {
|
||||
return permissions;
|
||||
}
|
||||
|
||||
// 系统管理员拥有所有权限
|
||||
if (isAdmin(userId)) {
|
||||
permissions.add(PermissionConstants.PERM_PROJECT_VIEW);
|
||||
permissions.add(PermissionConstants.PERM_PROJECT_MANAGE);
|
||||
permissions.add(PermissionConstants.PERM_RISK_VIEW);
|
||||
permissions.add(PermissionConstants.PERM_RISK_MANAGE);
|
||||
permissions.add(PermissionConstants.PERM_RESOURCE_VIEW);
|
||||
permissions.add(PermissionConstants.PERM_RESOURCE_MANAGE);
|
||||
permissions.add(PermissionConstants.PERM_MILESTONE_VIEW);
|
||||
permissions.add(PermissionConstants.PERM_MILESTONE_PUSH);
|
||||
permissions.add(PermissionConstants.PERM_TASK_VIEW);
|
||||
permissions.add(PermissionConstants.PERM_TASK_ASSIGN);
|
||||
permissions.add(PermissionConstants.PERM_TASK_PROCESS);
|
||||
permissions.add(PermissionConstants.PERM_REPORT_VIEW);
|
||||
permissions.add(PermissionConstants.PERM_REPORT_CREATE);
|
||||
permissions.add(PermissionConstants.PERM_KB_VIEW);
|
||||
permissions.add(PermissionConstants.PERM_KB_CREATE);
|
||||
return permissions;
|
||||
}
|
||||
|
||||
String projectRole = getUserProjectRole(userId, projectId);
|
||||
if (projectRole == null) {
|
||||
return permissions;
|
||||
}
|
||||
|
||||
// 根据项目角色分配权限
|
||||
switch (projectRole) {
|
||||
case PermissionConstants.PROJECT_ROLE_MANAGER:
|
||||
// 项目经理拥有项目全部管理权限
|
||||
permissions.add(PermissionConstants.PERM_PROJECT_VIEW);
|
||||
permissions.add(PermissionConstants.PERM_PROJECT_MANAGE);
|
||||
permissions.add(PermissionConstants.PERM_RISK_VIEW);
|
||||
permissions.add(PermissionConstants.PERM_RISK_MANAGE);
|
||||
permissions.add(PermissionConstants.PERM_RESOURCE_VIEW);
|
||||
permissions.add(PermissionConstants.PERM_RESOURCE_MANAGE);
|
||||
permissions.add(PermissionConstants.PERM_MILESTONE_VIEW);
|
||||
permissions.add(PermissionConstants.PERM_MILESTONE_PUSH);
|
||||
permissions.add(PermissionConstants.PERM_TASK_VIEW);
|
||||
permissions.add(PermissionConstants.PERM_TASK_ASSIGN);
|
||||
permissions.add(PermissionConstants.PERM_TASK_PROCESS);
|
||||
permissions.add(PermissionConstants.PERM_REPORT_VIEW);
|
||||
permissions.add(PermissionConstants.PERM_REPORT_CREATE);
|
||||
permissions.add(PermissionConstants.PERM_KB_VIEW);
|
||||
permissions.add(PermissionConstants.PERM_KB_CREATE);
|
||||
break;
|
||||
|
||||
case PermissionConstants.PROJECT_ROLE_LEADER:
|
||||
// 负责人拥有大部分管理权限,但不能删除项目
|
||||
permissions.add(PermissionConstants.PERM_PROJECT_VIEW);
|
||||
permissions.add(PermissionConstants.PERM_RISK_VIEW);
|
||||
permissions.add(PermissionConstants.PERM_RISK_MANAGE);
|
||||
permissions.add(PermissionConstants.PERM_RESOURCE_VIEW);
|
||||
permissions.add(PermissionConstants.PERM_RESOURCE_MANAGE);
|
||||
permissions.add(PermissionConstants.PERM_MILESTONE_VIEW);
|
||||
permissions.add(PermissionConstants.PERM_MILESTONE_PUSH);
|
||||
permissions.add(PermissionConstants.PERM_TASK_VIEW);
|
||||
permissions.add(PermissionConstants.PERM_TASK_ASSIGN);
|
||||
permissions.add(PermissionConstants.PERM_TASK_PROCESS);
|
||||
permissions.add(PermissionConstants.PERM_REPORT_VIEW);
|
||||
permissions.add(PermissionConstants.PERM_REPORT_CREATE);
|
||||
permissions.add(PermissionConstants.PERM_KB_VIEW);
|
||||
permissions.add(PermissionConstants.PERM_KB_CREATE);
|
||||
break;
|
||||
|
||||
case PermissionConstants.PROJECT_ROLE_MEMBER:
|
||||
// 成员拥有基本操作权限
|
||||
permissions.add(PermissionConstants.PERM_PROJECT_VIEW);
|
||||
permissions.add(PermissionConstants.PERM_RISK_VIEW);
|
||||
permissions.add(PermissionConstants.PERM_RESOURCE_VIEW);
|
||||
permissions.add(PermissionConstants.PERM_MILESTONE_VIEW);
|
||||
permissions.add(PermissionConstants.PERM_TASK_VIEW);
|
||||
permissions.add(PermissionConstants.PERM_TASK_PROCESS);
|
||||
permissions.add(PermissionConstants.PERM_REPORT_VIEW);
|
||||
permissions.add(PermissionConstants.PERM_REPORT_CREATE);
|
||||
permissions.add(PermissionConstants.PERM_KB_VIEW);
|
||||
permissions.add(PermissionConstants.PERM_KB_CREATE);
|
||||
break;
|
||||
|
||||
case PermissionConstants.PROJECT_ROLE_OBSERVER:
|
||||
// 观察者只有查看权限
|
||||
permissions.add(PermissionConstants.PERM_PROJECT_VIEW);
|
||||
permissions.add(PermissionConstants.PERM_RISK_VIEW);
|
||||
permissions.add(PermissionConstants.PERM_RESOURCE_VIEW);
|
||||
permissions.add(PermissionConstants.PERM_MILESTONE_VIEW);
|
||||
permissions.add(PermissionConstants.PERM_TASK_VIEW);
|
||||
permissions.add(PermissionConstants.PERM_REPORT_VIEW);
|
||||
permissions.add(PermissionConstants.PERM_KB_VIEW);
|
||||
break;
|
||||
}
|
||||
|
||||
return permissions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(Long userId, Long projectId, String permission) {
|
||||
if (permission == null) {
|
||||
return false;
|
||||
}
|
||||
return getUserProjectPermissions(userId, projectId).contains(permission);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasAnyPermission(Long userId, Long projectId, String... permissions) {
|
||||
if (permissions == null || permissions.length == 0) {
|
||||
return false;
|
||||
}
|
||||
Set<String> userPermissions = getUserProjectPermissions(userId, projectId);
|
||||
for (String permission : permissions) {
|
||||
if (userPermissions.contains(permission)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// ==================== 便捷权限校验方法实现 ====================
|
||||
|
||||
@Override
|
||||
public boolean canViewProject(Long userId, Long projectId) {
|
||||
return hasPermission(userId, projectId, PermissionConstants.PERM_PROJECT_VIEW);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canManageProject(Long userId, Long projectId) {
|
||||
return hasPermission(userId, projectId, PermissionConstants.PERM_PROJECT_MANAGE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canViewRisk(Long userId, Long projectId) {
|
||||
return hasPermission(userId, projectId, PermissionConstants.PERM_RISK_VIEW);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canManageRisk(Long userId, Long projectId) {
|
||||
return hasPermission(userId, projectId, PermissionConstants.PERM_RISK_MANAGE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canViewResource(Long userId, Long projectId) {
|
||||
return hasPermission(userId, projectId, PermissionConstants.PERM_RESOURCE_VIEW);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canManageResource(Long userId, Long projectId) {
|
||||
return hasPermission(userId, projectId, PermissionConstants.PERM_RESOURCE_MANAGE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canViewMilestone(Long userId, Long projectId) {
|
||||
return hasPermission(userId, projectId, PermissionConstants.PERM_MILESTONE_VIEW);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canPushMilestone(Long userId, Long projectId) {
|
||||
return hasPermission(userId, projectId, PermissionConstants.PERM_MILESTONE_PUSH);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canViewTask(Long userId, Long projectId) {
|
||||
return hasPermission(userId, projectId, PermissionConstants.PERM_TASK_VIEW);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canAssignTask(Long userId, Long projectId) {
|
||||
return hasPermission(userId, projectId, PermissionConstants.PERM_TASK_ASSIGN);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canProcessTask(Long userId, Long projectId) {
|
||||
return hasPermission(userId, projectId, PermissionConstants.PERM_TASK_PROCESS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canViewReport(Long userId, Long projectId) {
|
||||
return hasPermission(userId, projectId, PermissionConstants.PERM_REPORT_VIEW);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canCreateReport(Long userId, Long projectId) {
|
||||
return hasPermission(userId, projectId, PermissionConstants.PERM_REPORT_CREATE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canViewKnowledgeBase(Long userId, Long projectId) {
|
||||
return hasPermission(userId, projectId, PermissionConstants.PERM_KB_VIEW);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canCreateKnowledgeBase(Long userId, Long projectId) {
|
||||
return hasPermission(userId, projectId, PermissionConstants.PERM_KB_CREATE);
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,7 @@ spring:
|
||||
# MyBatis Plus 配置
|
||||
mybatis-plus:
|
||||
mapper-locations: classpath*:/mapper/**/*.xml
|
||||
type-aliases-package: cn.yinlihupo.ylhpaiprojectmanager.entity
|
||||
type-aliases-package: cn.yinlihupo.domain.entity
|
||||
configuration:
|
||||
map-underscore-to-camel-case: true
|
||||
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
|
||||
|
||||
@@ -10,4 +10,27 @@ spring:
|
||||
|
||||
# 公共配置
|
||||
server:
|
||||
port: 8080
|
||||
port: 8080
|
||||
|
||||
# Sa-Token 配置
|
||||
sa-token:
|
||||
# Token 名称
|
||||
token-name: Authorization
|
||||
# Token 有效期(单位:秒)默认30天,-1代表永不过期
|
||||
timeout: 2592000
|
||||
# Token 临时有效期(指定时间内无操作就视为token过期)单位:秒
|
||||
activity-timeout: -1
|
||||
# 是否允许同一账号并发登录(为true时允许一起登录,为false时新登录挤掉旧登录)
|
||||
is-concurrent: true
|
||||
# 在多人登录同一账号时,是否共用一个token(为true时所有登录共用一个token,为false时每次登录新建一个token)
|
||||
is-share: false
|
||||
# Token 风格
|
||||
token-style: uuid
|
||||
# 是否输出操作日志
|
||||
is-log: true
|
||||
# 是否从cookie中读取token
|
||||
is-read-cookie: false
|
||||
# 是否从请求体中读取token
|
||||
is-read-body: false
|
||||
# 是否从header中读取token
|
||||
is-read-header: true
|
||||
75
src/main/resources/mapper/FileAttachmentMapper.xml
Normal file
75
src/main/resources/mapper/FileAttachmentMapper.xml
Normal file
@@ -0,0 +1,75 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="cn.yinlihupo.mapper.FileAttachmentMapper">
|
||||
|
||||
<!-- 通用 ResultMap -->
|
||||
<resultMap id="BaseResultMap" type="FileAttachment">
|
||||
<id column="id" property="id"/>
|
||||
<result column="file_name" property="fileName"/>
|
||||
<result column="original_name" property="originalName"/>
|
||||
<result column="file_path" property="filePath"/>
|
||||
<result column="file_url" property="fileUrl"/>
|
||||
<result column="file_type" property="fileType"/>
|
||||
<result column="file_size" property="fileSize"/>
|
||||
<result column="storage_type" property="storageType"/>
|
||||
<result column="related_type" property="relatedType"/>
|
||||
<result column="related_id" property="relatedId"/>
|
||||
<result column="uploader_id" property="uploaderId"/>
|
||||
<result column="exif_data" property="exifData" typeHandler="cn.yinlihupo.common.handler.JsonbTypeHandler"/>
|
||||
<result column="ai_analysis" property="aiAnalysis" typeHandler="cn.yinlihupo.common.handler.JsonbTypeHandler"/>
|
||||
<result column="extra_data" property="extraData" typeHandler="cn.yinlihupo.common.handler.JsonbTypeHandler"/>
|
||||
<result column="create_time" property="createTime"/>
|
||||
<result column="deleted" property="deleted"/>
|
||||
</resultMap>
|
||||
|
||||
<!-- 查询某业务实体的附件列表(含上传者信息) -->
|
||||
<select id="selectAttachmentsByRelated" resultType="map">
|
||||
SELECT fa.*,
|
||||
u.real_name AS uploader_name,
|
||||
u.avatar AS uploader_avatar
|
||||
FROM file_attachment fa
|
||||
LEFT JOIN sys_user u ON fa.uploader_id = u.id AND u.deleted = 0
|
||||
WHERE fa.related_type = #{relatedType}
|
||||
AND fa.related_id = #{relatedId}
|
||||
AND fa.deleted = 0
|
||||
ORDER BY fa.create_time DESC
|
||||
</select>
|
||||
|
||||
<!-- 分页查询项目下所有附件(支持文件类型筛选) -->
|
||||
<select id="selectAttachmentsByProject" resultType="map">
|
||||
SELECT fa.*,
|
||||
u.real_name AS uploader_name
|
||||
FROM file_attachment fa
|
||||
LEFT JOIN sys_user u ON fa.uploader_id = u.id AND u.deleted = 0
|
||||
WHERE fa.deleted = 0
|
||||
AND fa.related_type IN ('project', 'task', 'risk', 'report')
|
||||
AND fa.related_id IN (
|
||||
SELECT id FROM project WHERE id = #{projectId} AND deleted = 0
|
||||
UNION ALL
|
||||
SELECT id FROM task WHERE project_id = #{projectId} AND deleted = 0
|
||||
UNION ALL
|
||||
SELECT id FROM risk WHERE project_id = #{projectId} AND deleted = 0
|
||||
)
|
||||
<if test="fileType != null and fileType != ''">
|
||||
AND fa.file_type LIKE CONCAT(#{fileType}, '%')
|
||||
</if>
|
||||
ORDER BY fa.create_time DESC
|
||||
</select>
|
||||
|
||||
<!-- 统计用户上传文件数量和总大小 -->
|
||||
<select id="selectUploaderStats" resultType="map">
|
||||
SELECT uploader_id,
|
||||
COUNT(*) AS file_count,
|
||||
SUM(file_size) AS total_size
|
||||
FROM file_attachment
|
||||
WHERE deleted = 0
|
||||
<if test="projectId != null">
|
||||
AND related_id = #{projectId}
|
||||
AND related_type = 'project'
|
||||
</if>
|
||||
GROUP BY uploader_id
|
||||
ORDER BY file_count DESC
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
110
src/main/resources/mapper/ProjectMapper.xml
Normal file
110
src/main/resources/mapper/ProjectMapper.xml
Normal file
@@ -0,0 +1,110 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="cn.yinlihupo.mapper.ProjectMapper">
|
||||
|
||||
<!-- 通用 ResultMap -->
|
||||
<resultMap id="BaseResultMap" type="Project">
|
||||
<id column="id" property="id"/>
|
||||
<result column="project_code" property="projectCode"/>
|
||||
<result column="project_name" property="projectName"/>
|
||||
<result column="project_type" property="projectType"/>
|
||||
<result column="description" property="description"/>
|
||||
<result column="objectives" property="objectives"/>
|
||||
<result column="manager_id" property="managerId"/>
|
||||
<result column="sponsor_id" property="sponsorId"/>
|
||||
<result column="plan_start_date" property="planStartDate"/>
|
||||
<result column="plan_end_date" property="planEndDate"/>
|
||||
<result column="actual_start_date" property="actualStartDate"/>
|
||||
<result column="actual_end_date" property="actualEndDate"/>
|
||||
<result column="budget" property="budget"/>
|
||||
<result column="cost" property="cost"/>
|
||||
<result column="currency" property="currency"/>
|
||||
<result column="progress" property="progress"/>
|
||||
<result column="status" property="status"/>
|
||||
<result column="priority" property="priority"/>
|
||||
<result column="risk_level" property="riskLevel"/>
|
||||
<result column="visibility" property="visibility"/>
|
||||
<result column="tags" property="tags" typeHandler="cn.yinlihupo.common.handler.JsonbTypeHandler"/>
|
||||
<result column="extra_data" property="extraData" typeHandler="cn.yinlihupo.common.handler.JsonbTypeHandler"/>
|
||||
<result column="create_by" property="createBy"/>
|
||||
<result column="create_time" property="createTime"/>
|
||||
<result column="update_by" property="updateBy"/>
|
||||
<result column="update_time" property="updateTime"/>
|
||||
<result column="deleted" property="deleted"/>
|
||||
</resultMap>
|
||||
|
||||
<!-- 分页查询项目列表(支持多条件筛选) -->
|
||||
<select id="selectPageList" resultMap="BaseResultMap">
|
||||
SELECT p.*
|
||||
FROM project p
|
||||
<where>
|
||||
p.deleted = 0
|
||||
<if test="status != null and status != ''">
|
||||
AND p.status = #{status}
|
||||
</if>
|
||||
<if test="priority != null and priority != ''">
|
||||
AND p.priority = #{priority}
|
||||
</if>
|
||||
<if test="riskLevel != null and riskLevel != ''">
|
||||
AND p.risk_level = #{riskLevel}
|
||||
</if>
|
||||
<if test="managerId != null">
|
||||
AND p.manager_id = #{managerId}
|
||||
</if>
|
||||
<if test="keyword != null and keyword != ''">
|
||||
AND (
|
||||
p.project_name LIKE CONCAT('%', #{keyword}, '%')
|
||||
OR p.project_code LIKE CONCAT('%', #{keyword}, '%')
|
||||
)
|
||||
</if>
|
||||
<if test="planStartDateFrom != null">
|
||||
AND p.plan_start_date >= #{planStartDateFrom}
|
||||
</if>
|
||||
<if test="planStartDateTo != null">
|
||||
AND p.plan_start_date <= #{planStartDateTo}
|
||||
</if>
|
||||
</where>
|
||||
ORDER BY p.create_time DESC
|
||||
</select>
|
||||
|
||||
<!-- 查询指定用户有权限的项目列表(超管查全部,其他查自己参与的) -->
|
||||
<select id="selectProjectsByUserId" resultMap="BaseResultMap">
|
||||
SELECT DISTINCT p.*
|
||||
FROM project p
|
||||
LEFT JOIN project_member pm ON p.id = pm.project_id AND pm.deleted = 0 AND pm.status = 1
|
||||
WHERE p.deleted = 0
|
||||
AND (
|
||||
p.manager_id = #{userId}
|
||||
OR pm.user_id = #{userId}
|
||||
)
|
||||
ORDER BY p.create_time DESC
|
||||
</select>
|
||||
|
||||
<!-- 查询项目详情(含经理姓名) -->
|
||||
<select id="selectDetailById" resultMap="BaseResultMap">
|
||||
SELECT p.*
|
||||
FROM project p
|
||||
WHERE p.id = #{projectId}
|
||||
AND p.deleted = 0
|
||||
</select>
|
||||
|
||||
<!-- 统计各状态项目数量 -->
|
||||
<select id="countByStatus" resultType="map">
|
||||
SELECT status, COUNT(*) AS cnt
|
||||
FROM project
|
||||
WHERE deleted = 0
|
||||
GROUP BY status
|
||||
</select>
|
||||
|
||||
<!-- 查询即将超期的项目(计划结束日期在今天到N天以内) -->
|
||||
<select id="selectAboutToExpire" resultMap="BaseResultMap">
|
||||
SELECT *
|
||||
FROM project
|
||||
WHERE deleted = 0
|
||||
AND status NOT IN ('completed', 'cancelled')
|
||||
AND plan_end_date BETWEEN CURRENT_DATE AND CURRENT_DATE + INTERVAL '1 day' * #{days}
|
||||
ORDER BY plan_end_date ASC
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
92
src/main/resources/mapper/ProjectMemberMapper.xml
Normal file
92
src/main/resources/mapper/ProjectMemberMapper.xml
Normal file
@@ -0,0 +1,92 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="cn.yinlihupo.mapper.ProjectMemberMapper">
|
||||
|
||||
<!-- 通用 ResultMap -->
|
||||
<resultMap id="BaseResultMap" type="ProjectMember">
|
||||
<id column="id" property="id"/>
|
||||
<result column="project_id" property="projectId"/>
|
||||
<result column="user_id" property="userId"/>
|
||||
<result column="role_code" property="roleCode"/>
|
||||
<result column="join_date" property="joinDate"/>
|
||||
<result column="leave_date" property="leaveDate"/>
|
||||
<result column="responsibility" property="responsibility"/>
|
||||
<result column="weekly_hours" property="weeklyHours"/>
|
||||
<result column="status" property="status"/>
|
||||
<result column="create_by" property="createBy"/>
|
||||
<result column="create_time" property="createTime"/>
|
||||
<result column="update_time" property="updateTime"/>
|
||||
<result column="deleted" property="deleted"/>
|
||||
</resultMap>
|
||||
|
||||
<!-- 带用户信息的项目成员 ResultMap -->
|
||||
<resultMap id="MemberWithUserMap" type="map">
|
||||
<result column="id" property="id"/>
|
||||
<result column="project_id" property="projectId"/>
|
||||
<result column="user_id" property="userId"/>
|
||||
<result column="role_code" property="roleCode"/>
|
||||
<result column="join_date" property="joinDate"/>
|
||||
<result column="responsibility" property="responsibility"/>
|
||||
<result column="weekly_hours" property="weeklyHours"/>
|
||||
<result column="status" property="status"/>
|
||||
<result column="real_name" property="realName"/>
|
||||
<result column="nickname" property="nickname"/>
|
||||
<result column="avatar" property="avatar"/>
|
||||
<result column="phone" property="phone"/>
|
||||
<result column="position" property="position"/>
|
||||
</resultMap>
|
||||
|
||||
<!-- 查询项目成员列表(含用户详情) -->
|
||||
<select id="selectMembersWithUser" resultMap="MemberWithUserMap">
|
||||
SELECT pm.id,
|
||||
pm.project_id,
|
||||
pm.user_id,
|
||||
pm.role_code,
|
||||
pm.join_date,
|
||||
pm.responsibility,
|
||||
pm.weekly_hours,
|
||||
pm.status,
|
||||
u.real_name,
|
||||
u.nickname,
|
||||
u.avatar,
|
||||
u.phone,
|
||||
u.position
|
||||
FROM project_member pm
|
||||
INNER JOIN sys_user u ON pm.user_id = u.id AND u.deleted = 0
|
||||
WHERE pm.project_id = #{projectId}
|
||||
AND pm.deleted = 0
|
||||
AND pm.status = 1
|
||||
ORDER BY
|
||||
CASE pm.role_code
|
||||
WHEN 'manager' THEN 1
|
||||
WHEN 'leader' THEN 2
|
||||
WHEN 'member' THEN 3
|
||||
ELSE 4
|
||||
END,
|
||||
pm.join_date ASC
|
||||
</select>
|
||||
|
||||
<!-- 查询用户在某项目中的角色 -->
|
||||
<select id="selectRoleByUserAndProject" resultType="String">
|
||||
SELECT role_code
|
||||
FROM project_member
|
||||
WHERE project_id = #{projectId}
|
||||
AND user_id = #{userId}
|
||||
AND status = 1
|
||||
AND deleted = 0
|
||||
LIMIT 1
|
||||
</select>
|
||||
|
||||
<!-- 统计某用户参与的项目数量(按角色分组) -->
|
||||
<select id="countUserProjects" resultType="map">
|
||||
SELECT pm.role_code, COUNT(*) AS cnt
|
||||
FROM project_member pm
|
||||
INNER JOIN project p ON pm.project_id = p.id AND p.deleted = 0
|
||||
WHERE pm.user_id = #{userId}
|
||||
AND pm.deleted = 0
|
||||
AND pm.status = 1
|
||||
GROUP BY pm.role_code
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
85
src/main/resources/mapper/ProjectMilestoneMapper.xml
Normal file
85
src/main/resources/mapper/ProjectMilestoneMapper.xml
Normal file
@@ -0,0 +1,85 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="cn.yinlihupo.mapper.ProjectMilestoneMapper">
|
||||
|
||||
<!-- 通用 ResultMap -->
|
||||
<resultMap id="BaseResultMap" type="ProjectMilestone">
|
||||
<id column="id" property="id"/>
|
||||
<result column="project_id" property="projectId"/>
|
||||
<result column="milestone_name" property="milestoneName"/>
|
||||
<result column="description" property="description"/>
|
||||
<result column="plan_date" property="planDate"/>
|
||||
<result column="actual_date" property="actualDate"/>
|
||||
<result column="status" property="status"/>
|
||||
<result column="progress" property="progress"/>
|
||||
<result column="sort_order" property="sortOrder"/>
|
||||
<result column="is_key" property="isKey"/>
|
||||
<result column="deliverables" property="deliverables" typeHandler="cn.yinlihupo.common.handler.JsonbTypeHandler"/>
|
||||
<result column="extra_data" property="extraData" typeHandler="cn.yinlihupo.common.handler.JsonbTypeHandler"/>
|
||||
<result column="create_by" property="createBy"/>
|
||||
<result column="create_time" property="createTime"/>
|
||||
<result column="update_by" property="updateBy"/>
|
||||
<result column="update_time" property="updateTime"/>
|
||||
<result column="deleted" property="deleted"/>
|
||||
</resultMap>
|
||||
|
||||
<!-- 查询项目里程碑列表(支持状态筛选,包含任务统计) -->
|
||||
<select id="selectMilestoneListWithStats" resultType="map">
|
||||
SELECT m.*,
|
||||
COUNT(t.id) AS task_total,
|
||||
COUNT(CASE WHEN t.status = 'completed' THEN 1 END) AS task_completed,
|
||||
COUNT(CASE WHEN t.status = 'in_progress' THEN 1 END) AS task_in_progress
|
||||
FROM project_milestone m
|
||||
LEFT JOIN task t ON m.id = t.milestone_id AND t.deleted = 0
|
||||
WHERE m.project_id = #{projectId}
|
||||
AND m.deleted = 0
|
||||
<if test="status != null and status != ''">
|
||||
AND m.status = #{status}
|
||||
</if>
|
||||
GROUP BY m.id
|
||||
ORDER BY m.sort_order ASC, m.plan_date ASC
|
||||
</select>
|
||||
|
||||
<!-- 查询延期或即将延期的关键里程碑 -->
|
||||
<select id="selectDelayedKeyMilestones" resultMap="BaseResultMap">
|
||||
SELECT *
|
||||
FROM project_milestone
|
||||
WHERE deleted = 0
|
||||
AND is_key = 1
|
||||
AND status NOT IN ('completed')
|
||||
AND plan_date < CURRENT_DATE
|
||||
<if test="projectId != null">
|
||||
AND project_id = #{projectId}
|
||||
</if>
|
||||
ORDER BY plan_date ASC
|
||||
</select>
|
||||
|
||||
<!-- 查询即将到期的里程碑(N天内) -->
|
||||
<select id="selectUpcomingMilestones" resultMap="BaseResultMap">
|
||||
SELECT *
|
||||
FROM project_milestone
|
||||
WHERE deleted = 0
|
||||
AND status NOT IN ('completed')
|
||||
AND plan_date BETWEEN CURRENT_DATE AND CURRENT_DATE + INTERVAL '1 day' * #{days}
|
||||
<if test="projectId != null">
|
||||
AND project_id = #{projectId}
|
||||
</if>
|
||||
ORDER BY plan_date ASC
|
||||
</select>
|
||||
|
||||
<!-- 查询里程碑完成进度统计 -->
|
||||
<select id="selectMilestoneProgressSummary" resultType="map">
|
||||
SELECT
|
||||
COUNT(*) AS total,
|
||||
COUNT(CASE WHEN status = 'completed' THEN 1 END) AS completed,
|
||||
COUNT(CASE WHEN status = 'in_progress' THEN 1 END) AS in_progress,
|
||||
COUNT(CASE WHEN status = 'delayed' THEN 1 END) AS delayed,
|
||||
COUNT(CASE WHEN status = 'pending' THEN 1 END) AS pending,
|
||||
ROUND(AVG(progress), 1) AS avg_progress
|
||||
FROM project_milestone
|
||||
WHERE project_id = #{projectId}
|
||||
AND deleted = 0
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
96
src/main/resources/mapper/ResourceMapper.xml
Normal file
96
src/main/resources/mapper/ResourceMapper.xml
Normal file
@@ -0,0 +1,96 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="cn.yinlihupo.mapper.ResourceMapper">
|
||||
|
||||
<!-- 通用 ResultMap -->
|
||||
<resultMap id="BaseResultMap" type="Resource">
|
||||
<id column="id" property="id"/>
|
||||
<result column="resource_code" property="resourceCode"/>
|
||||
<result column="project_id" property="projectId"/>
|
||||
<result column="resource_type" property="resourceType"/>
|
||||
<result column="resource_name" property="resourceName"/>
|
||||
<result column="description" property="description"/>
|
||||
<result column="specification" property="specification"/>
|
||||
<result column="unit" property="unit"/>
|
||||
<result column="plan_quantity" property="planQuantity"/>
|
||||
<result column="actual_quantity" property="actualQuantity"/>
|
||||
<result column="unit_price" property="unitPrice"/>
|
||||
<result column="currency" property="currency"/>
|
||||
<result column="supplier" property="supplier"/>
|
||||
<result column="status" property="status"/>
|
||||
<result column="plan_arrive_date" property="planArriveDate"/>
|
||||
<result column="actual_arrive_date" property="actualArriveDate"/>
|
||||
<result column="responsible_id" property="responsibleId"/>
|
||||
<result column="location" property="location"/>
|
||||
<result column="tags" property="tags" typeHandler="cn.yinlihupo.common.handler.JsonbTypeHandler"/>
|
||||
<result column="extra_data" property="extraData" typeHandler="cn.yinlihupo.common.handler.JsonbTypeHandler"/>
|
||||
<result column="create_by" property="createBy"/>
|
||||
<result column="create_time" property="createTime"/>
|
||||
<result column="update_by" property="updateBy"/>
|
||||
<result column="update_time" property="updateTime"/>
|
||||
<result column="deleted" property="deleted"/>
|
||||
</resultMap>
|
||||
|
||||
<!-- 分页查询资源列表(含负责人信息,支持多条件筛选) -->
|
||||
<select id="selectResourcePageWithResponsible" resultType="map">
|
||||
SELECT r.*,
|
||||
u.real_name AS responsible_name
|
||||
FROM resource r
|
||||
LEFT JOIN sys_user u ON r.responsible_id = u.id AND u.deleted = 0
|
||||
WHERE r.project_id = #{projectId}
|
||||
AND r.deleted = 0
|
||||
<if test="resourceType != null and resourceType != ''">
|
||||
AND r.resource_type = #{resourceType}
|
||||
</if>
|
||||
<if test="status != null and status != ''">
|
||||
AND r.status = #{status}
|
||||
</if>
|
||||
<if test="keyword != null and keyword != ''">
|
||||
AND r.resource_name LIKE CONCAT('%', #{keyword}, '%')
|
||||
</if>
|
||||
ORDER BY r.resource_type ASC, r.create_time DESC
|
||||
</select>
|
||||
|
||||
<!-- 查询资源预算汇总(按类型统计计划金额和实际金额) -->
|
||||
<select id="selectResourceBudgetSummary" resultType="map">
|
||||
SELECT resource_type,
|
||||
COUNT(*) AS item_count,
|
||||
SUM(plan_quantity * unit_price) AS plan_amount,
|
||||
SUM(actual_quantity * unit_price) AS actual_amount,
|
||||
SUM((actual_quantity - plan_quantity) * unit_price) AS diff_amount
|
||||
FROM resource
|
||||
WHERE project_id = #{projectId}
|
||||
AND deleted = 0
|
||||
GROUP BY resource_type
|
||||
ORDER BY plan_amount DESC NULLS LAST
|
||||
</select>
|
||||
|
||||
<!-- 查询即将到位但尚未到位的资源 -->
|
||||
<select id="selectPendingArrivalResources" resultMap="BaseResultMap">
|
||||
SELECT *
|
||||
FROM resource
|
||||
WHERE project_id = #{projectId}
|
||||
AND deleted = 0
|
||||
AND status IN ('approved', 'procuring')
|
||||
AND plan_arrive_date BETWEEN CURRENT_DATE AND CURRENT_DATE + INTERVAL '1 day' * #{days}
|
||||
ORDER BY plan_arrive_date ASC
|
||||
</select>
|
||||
|
||||
<!-- 查询资源申请(待审批状态) -->
|
||||
<select id="selectPendingApprovalResources" resultType="map">
|
||||
SELECT r.*,
|
||||
u.real_name AS responsible_name,
|
||||
p.project_name
|
||||
FROM resource r
|
||||
LEFT JOIN sys_user u ON r.responsible_id = u.id AND u.deleted = 0
|
||||
LEFT JOIN project p ON r.project_id = p.id AND p.deleted = 0
|
||||
WHERE r.deleted = 0
|
||||
AND r.status = 'requested'
|
||||
<if test="projectId != null">
|
||||
AND r.project_id = #{projectId}
|
||||
</if>
|
||||
ORDER BY r.create_time ASC
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
110
src/main/resources/mapper/RiskMapper.xml
Normal file
110
src/main/resources/mapper/RiskMapper.xml
Normal file
@@ -0,0 +1,110 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="cn.yinlihupo.mapper.RiskMapper">
|
||||
|
||||
<!-- 通用 ResultMap -->
|
||||
<resultMap id="BaseResultMap" type="Risk">
|
||||
<id column="id" property="id"/>
|
||||
<result column="risk_code" property="riskCode"/>
|
||||
<result column="project_id" property="projectId"/>
|
||||
<result column="category" property="category"/>
|
||||
<result column="risk_name" property="riskName"/>
|
||||
<result column="description" property="description"/>
|
||||
<result column="risk_source" property="riskSource"/>
|
||||
<result column="risk_type" property="riskType"/>
|
||||
<result column="probability" property="probability"/>
|
||||
<result column="impact" property="impact"/>
|
||||
<result column="risk_score" property="riskScore"/>
|
||||
<result column="risk_level" property="riskLevel"/>
|
||||
<result column="status" property="status"/>
|
||||
<result column="owner_id" property="ownerId"/>
|
||||
<result column="mitigation_plan" property="mitigationPlan"/>
|
||||
<result column="contingency_plan" property="contingencyPlan"/>
|
||||
<result column="trigger_condition" property="triggerCondition"/>
|
||||
<result column="discover_time" property="discoverTime"/>
|
||||
<result column="due_date" property="dueDate"/>
|
||||
<result column="resolved_time" property="resolvedTime"/>
|
||||
<result column="ai_analysis" property="aiAnalysis" typeHandler="cn.yinlihupo.common.handler.JsonbTypeHandler"/>
|
||||
<result column="tags" property="tags" typeHandler="cn.yinlihupo.common.handler.JsonbTypeHandler"/>
|
||||
<result column="extra_data" property="extraData" typeHandler="cn.yinlihupo.common.handler.JsonbTypeHandler"/>
|
||||
<result column="create_by" property="createBy"/>
|
||||
<result column="create_time" property="createTime"/>
|
||||
<result column="update_by" property="updateBy"/>
|
||||
<result column="update_time" property="updateTime"/>
|
||||
<result column="deleted" property="deleted"/>
|
||||
</resultMap>
|
||||
|
||||
<!-- 分页查询风险列表(含负责人信息,支持多条件筛选) -->
|
||||
<select id="selectRiskPageWithOwner" resultType="map">
|
||||
SELECT r.*,
|
||||
u.real_name AS owner_name,
|
||||
u.avatar AS owner_avatar
|
||||
FROM risk r
|
||||
LEFT JOIN sys_user u ON r.owner_id = u.id AND u.deleted = 0
|
||||
WHERE r.project_id = #{projectId}
|
||||
AND r.deleted = 0
|
||||
<if test="category != null and category != ''">
|
||||
AND r.category = #{category}
|
||||
</if>
|
||||
<if test="riskLevel != null and riskLevel != ''">
|
||||
AND r.risk_level = #{riskLevel}
|
||||
</if>
|
||||
<if test="status != null and status != ''">
|
||||
AND r.status = #{status}
|
||||
</if>
|
||||
<if test="keyword != null and keyword != ''">
|
||||
AND r.risk_name LIKE CONCAT('%', #{keyword}, '%')
|
||||
</if>
|
||||
ORDER BY
|
||||
CASE r.risk_level
|
||||
WHEN 'critical' THEN 1
|
||||
WHEN 'high' THEN 2
|
||||
WHEN 'medium' THEN 3
|
||||
ELSE 4
|
||||
END,
|
||||
r.create_time DESC
|
||||
</select>
|
||||
|
||||
<!-- 查询未解决的高/严重风险(用于预警) -->
|
||||
<select id="selectHighRisksUnresolved" resultType="map">
|
||||
SELECT r.*,
|
||||
u.real_name AS owner_name,
|
||||
p.project_name
|
||||
FROM risk r
|
||||
LEFT JOIN sys_user u ON r.owner_id = u.id AND u.deleted = 0
|
||||
LEFT JOIN project p ON r.project_id = p.id AND p.deleted = 0
|
||||
WHERE r.deleted = 0
|
||||
AND r.risk_level IN ('critical', 'high')
|
||||
AND r.status NOT IN ('resolved', 'closed')
|
||||
<if test="projectId != null">
|
||||
AND r.project_id = #{projectId}
|
||||
</if>
|
||||
ORDER BY r.risk_score DESC NULLS LAST
|
||||
</select>
|
||||
|
||||
<!-- 按风险等级统计各分类数量 -->
|
||||
<select id="countRiskByCategoryAndLevel" resultType="map">
|
||||
SELECT category, risk_level, COUNT(*) AS cnt
|
||||
FROM risk
|
||||
WHERE project_id = #{projectId}
|
||||
AND deleted = 0
|
||||
GROUP BY category, risk_level
|
||||
ORDER BY category, risk_level
|
||||
</select>
|
||||
|
||||
<!-- 查询风险处理趋势(按月统计新增/关闭数量) -->
|
||||
<select id="selectRiskTrend" resultType="map">
|
||||
SELECT
|
||||
TO_CHAR(create_time, 'YYYY-MM') AS month,
|
||||
COUNT(*) AS created_cnt,
|
||||
COUNT(CASE WHEN status IN ('resolved','closed') THEN 1 END) AS closed_cnt
|
||||
FROM risk
|
||||
WHERE project_id = #{projectId}
|
||||
AND deleted = 0
|
||||
AND create_time >= NOW() - INTERVAL '6 months'
|
||||
GROUP BY month
|
||||
ORDER BY month ASC
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
41
src/main/resources/mapper/SysPermissionMapper.xml
Normal file
41
src/main/resources/mapper/SysPermissionMapper.xml
Normal file
@@ -0,0 +1,41 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="cn.yinlihupo.mapper.SysPermissionMapper">
|
||||
|
||||
<!-- 根据角色编码列表查询权限编码列表 -->
|
||||
<select id="selectPermissionCodesByRoleCodes" resultType="String">
|
||||
SELECT DISTINCT p.permission_code
|
||||
FROM sys_permission p
|
||||
INNER JOIN sys_role_permission rp ON p.id = rp.permission_id
|
||||
INNER JOIN sys_role r ON rp.role_id = r.id
|
||||
WHERE r.role_code IN
|
||||
<foreach collection="roleCodes" item="code" open="(" separator="," close=")">
|
||||
#{code}
|
||||
</foreach>
|
||||
AND p.deleted = 0
|
||||
AND r.deleted = 0
|
||||
AND p.status = 1
|
||||
</select>
|
||||
|
||||
<!-- 查询菜单树(含按钮权限) -->
|
||||
<select id="selectMenuTree" resultType="SysPermission">
|
||||
SELECT *
|
||||
FROM sys_permission
|
||||
WHERE deleted = 0
|
||||
AND status = 1
|
||||
AND permission_type IN (1, 2)
|
||||
ORDER BY sort_order ASC, id ASC
|
||||
</select>
|
||||
|
||||
<!-- 根据角色ID查询权限列表 -->
|
||||
<select id="selectPermsByRoleId" resultType="SysPermission">
|
||||
SELECT p.*
|
||||
FROM sys_permission p
|
||||
INNER JOIN sys_role_permission rp ON p.id = rp.permission_id
|
||||
WHERE rp.role_id = #{roleId}
|
||||
AND p.deleted = 0
|
||||
ORDER BY p.sort_order ASC
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
83
src/main/resources/mapper/SysUserMapper.xml
Normal file
83
src/main/resources/mapper/SysUserMapper.xml
Normal file
@@ -0,0 +1,83 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="cn.yinlihupo.mapper.SysUserMapper">
|
||||
|
||||
<!-- 通用ResultMap -->
|
||||
<resultMap id="BaseResultMap" type="SysUser">
|
||||
<id column="id" property="id"/>
|
||||
<result column="username" property="username"/>
|
||||
<result column="password" property="password"/>
|
||||
<result column="real_name" property="realName"/>
|
||||
<result column="nickname" property="nickname"/>
|
||||
<result column="avatar" property="avatar"/>
|
||||
<result column="gender" property="gender"/>
|
||||
<result column="phone" property="phone"/>
|
||||
<result column="email" property="email"/>
|
||||
<result column="dept_id" property="deptId"/>
|
||||
<result column="position" property="position"/>
|
||||
<result column="employee_no" property="employeeNo"/>
|
||||
<result column="entry_date" property="entryDate"/>
|
||||
<result column="status" property="status"/>
|
||||
<result column="last_login_time" property="lastLoginTime"/>
|
||||
<result column="last_login_ip" property="lastLoginIp"/>
|
||||
<result column="create_by" property="createBy"/>
|
||||
<result column="create_time" property="createTime"/>
|
||||
<result column="update_by" property="updateBy"/>
|
||||
<result column="update_time" property="updateTime"/>
|
||||
<result column="deleted" property="deleted"/>
|
||||
</resultMap>
|
||||
|
||||
<!-- 根据用户ID查询角色编码列表 -->
|
||||
<select id="selectRoleCodesByUserId" resultType="String">
|
||||
SELECT r.role_code
|
||||
FROM sys_role r
|
||||
INNER JOIN sys_user_role ur ON r.id = ur.role_id
|
||||
WHERE ur.user_id = #{userId}
|
||||
AND r.deleted = 0
|
||||
</select>
|
||||
|
||||
<!-- 根据用户名查询用户(含角色) -->
|
||||
<select id="selectByUsername" resultMap="BaseResultMap">
|
||||
SELECT *
|
||||
FROM sys_user
|
||||
WHERE username = #{username}
|
||||
AND deleted = 0
|
||||
</select>
|
||||
|
||||
<!-- 分页查询用户列表(支持按部门、状态、关键字筛选) -->
|
||||
<select id="selectPageList" resultMap="BaseResultMap">
|
||||
SELECT u.*
|
||||
FROM sys_user u
|
||||
<where>
|
||||
u.deleted = 0
|
||||
<if test="deptId != null">
|
||||
AND u.dept_id = #{deptId}
|
||||
</if>
|
||||
<if test="status != null">
|
||||
AND u.status = #{status}
|
||||
</if>
|
||||
<if test="keyword != null and keyword != ''">
|
||||
AND (
|
||||
u.username LIKE CONCAT('%', #{keyword}, '%')
|
||||
OR u.real_name LIKE CONCAT('%', #{keyword}, '%')
|
||||
OR u.phone LIKE CONCAT('%', #{keyword}, '%')
|
||||
)
|
||||
</if>
|
||||
</where>
|
||||
ORDER BY u.create_time DESC
|
||||
</select>
|
||||
|
||||
<!-- 查询项目成员用户详情(关联 project_member) -->
|
||||
<select id="selectUsersByProjectId" resultMap="BaseResultMap">
|
||||
SELECT u.*
|
||||
FROM sys_user u
|
||||
INNER JOIN project_member pm ON u.id = pm.user_id
|
||||
WHERE pm.project_id = #{projectId}
|
||||
AND pm.status = 1
|
||||
AND pm.deleted = 0
|
||||
AND u.deleted = 0
|
||||
ORDER BY pm.create_time ASC
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
125
src/main/resources/mapper/TaskMapper.xml
Normal file
125
src/main/resources/mapper/TaskMapper.xml
Normal file
@@ -0,0 +1,125 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="cn.yinlihupo.mapper.TaskMapper">
|
||||
|
||||
<!-- 通用 ResultMap -->
|
||||
<resultMap id="BaseResultMap" type="Task">
|
||||
<id column="id" property="id"/>
|
||||
<result column="task_code" property="taskCode"/>
|
||||
<result column="project_id" property="projectId"/>
|
||||
<result column="milestone_id" property="milestoneId"/>
|
||||
<result column="task_name" property="taskName"/>
|
||||
<result column="description" property="description"/>
|
||||
<result column="task_type" property="taskType"/>
|
||||
<result column="assignee_id" property="assigneeId"/>
|
||||
<result column="plan_start_date" property="planStartDate"/>
|
||||
<result column="plan_end_date" property="planEndDate"/>
|
||||
<result column="actual_start_date" property="actualStartDate"/>
|
||||
<result column="actual_end_date" property="actualEndDate"/>
|
||||
<result column="plan_hours" property="planHours"/>
|
||||
<result column="actual_hours" property="actualHours"/>
|
||||
<result column="progress" property="progress"/>
|
||||
<result column="priority" property="priority"/>
|
||||
<result column="status" property="status"/>
|
||||
<result column="sort_order" property="sortOrder"/>
|
||||
<result column="tags" property="tags" typeHandler="cn.yinlihupo.common.handler.JsonbTypeHandler"/>
|
||||
<result column="attachments" property="attachments" typeHandler="cn.yinlihupo.common.handler.JsonbTypeHandler"/>
|
||||
<result column="extra_data" property="extraData" typeHandler="cn.yinlihupo.common.handler.JsonbTypeHandler"/>
|
||||
<result column="create_by" property="createBy"/>
|
||||
<result column="create_time" property="createTime"/>
|
||||
<result column="update_by" property="updateBy"/>
|
||||
<result column="update_time" property="updateTime"/>
|
||||
<result column="deleted" property="deleted"/>
|
||||
</resultMap>
|
||||
|
||||
<!-- 分页查询任务列表(含负责人信息,支持多条件筛选) -->
|
||||
<select id="selectTaskPageWithAssignee" resultType="map">
|
||||
SELECT t.*,
|
||||
u.real_name AS assignee_name,
|
||||
u.avatar AS assignee_avatar,
|
||||
m.milestone_name
|
||||
FROM task t
|
||||
LEFT JOIN sys_user u ON t.assignee_id = u.id AND u.deleted = 0
|
||||
LEFT JOIN project_milestone m ON t.milestone_id = m.id AND m.deleted = 0
|
||||
WHERE t.project_id = #{projectId}
|
||||
AND t.deleted = 0
|
||||
<if test="milestoneId != null">
|
||||
AND t.milestone_id = #{milestoneId}
|
||||
</if>
|
||||
<if test="assigneeId != null">
|
||||
AND t.assignee_id = #{assigneeId}
|
||||
</if>
|
||||
<if test="status != null and status != ''">
|
||||
AND t.status = #{status}
|
||||
</if>
|
||||
<if test="priority != null and priority != ''">
|
||||
AND t.priority = #{priority}
|
||||
</if>
|
||||
<if test="keyword != null and keyword != ''">
|
||||
AND t.task_name LIKE CONCAT('%', #{keyword}, '%')
|
||||
</if>
|
||||
ORDER BY t.sort_order ASC, t.plan_start_date ASC
|
||||
</select>
|
||||
|
||||
<!-- 查询我的任务(按用户ID) -->
|
||||
<select id="selectMyTasks" resultMap="BaseResultMap">
|
||||
SELECT t.*
|
||||
FROM task t
|
||||
WHERE t.assignee_id = #{userId}
|
||||
AND t.deleted = 0
|
||||
AND t.status NOT IN ('completed', 'cancelled')
|
||||
<if test="projectId != null">
|
||||
AND t.project_id = #{projectId}
|
||||
</if>
|
||||
ORDER BY
|
||||
CASE t.priority
|
||||
WHEN 'critical' THEN 1
|
||||
WHEN 'high' THEN 2
|
||||
WHEN 'medium' THEN 3
|
||||
ELSE 4
|
||||
END,
|
||||
t.plan_end_date ASC NULLS LAST
|
||||
</select>
|
||||
|
||||
<!-- 查询超期未完成的任务 -->
|
||||
<select id="selectOverdueTasks" resultType="map">
|
||||
SELECT t.*,
|
||||
u.real_name AS assignee_name,
|
||||
p.project_name,
|
||||
CURRENT_DATE - t.plan_end_date AS overdue_days
|
||||
FROM task t
|
||||
LEFT JOIN sys_user u ON t.assignee_id = u.id AND u.deleted = 0
|
||||
LEFT JOIN project p ON t.project_id = p.id AND p.deleted = 0
|
||||
WHERE t.deleted = 0
|
||||
AND t.status NOT IN ('completed', 'cancelled')
|
||||
AND t.plan_end_date < CURRENT_DATE
|
||||
<if test="projectId != null">
|
||||
AND t.project_id = #{projectId}
|
||||
</if>
|
||||
ORDER BY overdue_days DESC
|
||||
</select>
|
||||
|
||||
<!-- 查询任务依赖关系(前置任务) -->
|
||||
<select id="selectDependencies" resultType="map">
|
||||
SELECT t.id,
|
||||
t.task_name,
|
||||
t.status,
|
||||
t.progress,
|
||||
td.dependency_type,
|
||||
td.lag_days
|
||||
FROM task_dependency td
|
||||
INNER JOIN task t ON td.depends_on_task_id = t.id AND t.deleted = 0
|
||||
WHERE td.task_id = #{taskId}
|
||||
</select>
|
||||
|
||||
<!-- 统计项目任务状态分布 -->
|
||||
<select id="countTasksByStatus" resultType="map">
|
||||
SELECT status, COUNT(*) AS cnt
|
||||
FROM task
|
||||
WHERE project_id = #{projectId}
|
||||
AND deleted = 0
|
||||
GROUP BY status
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
Reference in New Issue
Block a user