feat(dify): 实现工作流与部门关联功能
- 新增 WorkflowDept 实体类和相关 DTO - 实现 AppEntity 和 WorkflowDept 的关联查询 - 添加工作流与部门关联的接口和实现 - 优化应用列表查询接口,支持按类型筛选
This commit is contained in:
parent
0d30f40f0e
commit
795fabf823
@ -29,7 +29,7 @@ public class CustomAccessDeniedHandler implements AccessDeniedHandler {
|
||||
@Override
|
||||
public void handle(HttpServletRequest request, HttpServletResponse response,
|
||||
AccessDeniedException ex) throws IOException, ServletException {
|
||||
// ex.printStackTrace();
|
||||
ex.printStackTrace();
|
||||
response.setContentType("application/json;charset=utf-8");
|
||||
ExceptionResult result;
|
||||
if (ex instanceof MissingCsrfTokenException) {
|
||||
@ -39,9 +39,9 @@ public class CustomAccessDeniedHandler implements AccessDeniedHandler {
|
||||
result = new ExceptionResult("凭证已过期,请重新登录", HttpStatus.UNAUTHORIZED.value(),
|
||||
LocalDateTime.now());
|
||||
} else if (ex instanceof AuthorizationDeniedException) {
|
||||
// 403
|
||||
response.setStatus(HttpStatus.FORBIDDEN.value());
|
||||
result = new ExceptionResult("当前账号已在其他设备登录,请先退出再尝试登录", HttpStatus.FORBIDDEN.value(),
|
||||
// 会话已存在,禁止重复登录,返回401
|
||||
response.setStatus(HttpStatus.UNAUTHORIZED.value());
|
||||
result = new ExceptionResult("当前账号已在其他设备登录,请先退出再尝试登录", HttpStatus.UNAUTHORIZED.value(),
|
||||
LocalDateTime.now());
|
||||
} else {
|
||||
// 403
|
||||
|
@ -55,3 +55,4 @@ public class JpaUserDetailsServiceImpl implements UserDetailsService {
|
||||
return UserDetailsImpl.from(user, permissions);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,6 @@ import org.springframework.security.authentication.ProviderManager;
|
||||
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
|
||||
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
|
||||
import org.springframework.security.core.session.SessionRegistry;
|
||||
import org.springframework.security.core.session.SessionRegistryImpl;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
@ -39,6 +38,7 @@ public class SpringSecurityConfig {
|
||||
private final CustomAuthenticationSuccessHandler customAuthenticationSuccessHandler;
|
||||
private final CustomAuthenticationEntryPoint customAuthenticationEntryPoint;
|
||||
private final CustomAccessDeniedHandler customAccessDeniedHandler;
|
||||
private final CustomSessionInformationExpiredStrategy customSessionInformationExpiredStrategy;
|
||||
// private final SessionRegistry sessionRegistry;
|
||||
|
||||
@Resource
|
||||
@ -59,6 +59,14 @@ public class SpringSecurityConfig {
|
||||
return new HttpSessionEventPublisher();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ConcurrentSessionControlAuthenticationStrategy concurrentSessionControlAuthenticationStrategy() {
|
||||
ConcurrentSessionControlAuthenticationStrategy concurrentSessionControlAuthenticationStrategy = new ConcurrentSessionControlAuthenticationStrategy(sessionRegistry());
|
||||
concurrentSessionControlAuthenticationStrategy.setMaximumSessions(1);
|
||||
concurrentSessionControlAuthenticationStrategy.setExceptionIfMaximumExceeded(true);
|
||||
return concurrentSessionControlAuthenticationStrategy;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public PersistentTokenRepository persistentTokenRepository() {
|
||||
JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
|
||||
@ -75,7 +83,7 @@ public class SpringSecurityConfig {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public JsonAuthenticationFilter jsonAuthenticationFilter() throws Exception {
|
||||
public JsonAuthenticationFilter jsonAuthenticationFilter() {
|
||||
JsonAuthenticationFilter filter = new JsonAuthenticationFilter();
|
||||
filter.setAuthenticationSuccessHandler(customAuthenticationSuccessHandler);
|
||||
filter.setAuthenticationFailureHandler(customAuthenticationFailureHandler);
|
||||
@ -89,13 +97,11 @@ public class SpringSecurityConfig {
|
||||
|
||||
@Bean
|
||||
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||
|
||||
return http
|
||||
.authorizeHttpRequests(auth -> auth
|
||||
.requestMatchers(HttpMethod.GET, "/api/rest/user/menu","/api/rest/user/register","/api/rest/user/send-email").permitAll()
|
||||
.requestMatchers(HttpMethod.POST, "/api/rest/user/login","/api/rest/user/register").permitAll()
|
||||
.requestMatchers("/api/rest/user/me").permitAll()
|
||||
.requestMatchers("/v1/**").authenticated()
|
||||
.requestMatchers("/api/**").authenticated()
|
||||
)
|
||||
.addFilterAt(jsonAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
|
||||
@ -115,13 +121,14 @@ public class SpringSecurityConfig {
|
||||
.rememberMe(rememberMe -> rememberMe
|
||||
.userDetailsService(userDetailsService)
|
||||
.tokenRepository(persistentTokenRepository()))
|
||||
.csrf(csrf ->
|
||||
csrf.ignoringRequestMatchers("v1/**","/api/internal/**", "/api/rest/user/logout","/api/rest/user/register"))
|
||||
.csrf(csrf -> csrf
|
||||
.sessionAuthenticationStrategy(concurrentSessionControlAuthenticationStrategy())
|
||||
.ignoringRequestMatchers("v1/**","/api/internal/**", "/api/rest/user/logout","/api/rest/user/register"))
|
||||
.sessionManagement(session -> session
|
||||
.maximumSessions(1)
|
||||
.maxSessionsPreventsLogin(true)
|
||||
.sessionRegistry(sessionRegistry())
|
||||
// .expiredSessionStrategy(customSessionInformationExpiredStrategy)
|
||||
.expiredSessionStrategy(customSessionInformationExpiredStrategy)
|
||||
).build();
|
||||
}
|
||||
}
|
||||
|
@ -130,3 +130,5 @@ public class UserDetailsImpl implements UserDetails {
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -2,28 +2,37 @@ package com.zsc.edu.dify.modules.dify.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.zsc.edu.dify.framework.mybatisplus.DataPermission;
|
||||
import com.zsc.edu.dify.modules.dify.dto.WorkflowDeptDto;
|
||||
import com.zsc.edu.dify.modules.dify.entity.AppEntity;
|
||||
import com.zsc.edu.dify.modules.dify.entity.WorkflowDept;
|
||||
import com.zsc.edu.dify.modules.dify.service.AppEntityService;
|
||||
import com.zsc.edu.dify.modules.dify.service.WorkflowDeptService;
|
||||
import com.zsc.edu.dify.modules.operationLog.entity.OperationLogAnnotation;
|
||||
import io.github.guoshiqiufeng.dify.server.DifyServer;
|
||||
import io.github.guoshiqiufeng.dify.server.dto.response.ApiKeyResponseVO;
|
||||
import io.github.guoshiqiufeng.dify.server.dto.response.AppsResponseVO;
|
||||
import io.github.guoshiqiufeng.dify.server.dto.response.DatasetApiKeyResponseVO;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@AllArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/server")
|
||||
public class V1ServerController {
|
||||
|
||||
@Resource
|
||||
private DifyServer difyServer;
|
||||
@Resource
|
||||
|
||||
private AppEntityService appEntityService;
|
||||
|
||||
private final WorkflowDeptService workflowDeptService;
|
||||
|
||||
/**
|
||||
* 获取应用列表
|
||||
* @param mode 模式 chat\agent-chat\completion\advanced-chat\workflow
|
||||
@ -31,9 +40,8 @@ public class V1ServerController {
|
||||
* @return
|
||||
*/
|
||||
@GetMapping("/apps")
|
||||
@DataPermission
|
||||
public List<AppsResponseVO> getApps(String mode, String name) {
|
||||
return appEntityService.getApps(mode, name);
|
||||
public List<AppEntity> getApps(String mode, String name, Integer type) {
|
||||
return appEntityService.getApps(mode, name, type);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -114,8 +122,21 @@ public class V1ServerController {
|
||||
* @return
|
||||
*/
|
||||
@GetMapping("/apps/type")
|
||||
@DataPermission
|
||||
// @DataPermission
|
||||
public List<AppEntity> getAppsByAppType(Integer appType){
|
||||
return appEntityService.selectByAppType(appType);
|
||||
}
|
||||
|
||||
@PostMapping("/link")
|
||||
public ResponseEntity<String> link(@RequestBody WorkflowDeptDto workflowDeptDto) {
|
||||
List<WorkflowDept> workflowDepts = new ArrayList<>();
|
||||
for (Long deptId: workflowDeptDto.getDeptIds()) {
|
||||
workflowDepts.add(new WorkflowDept(workflowDeptDto.getWorkflowId(), deptId));
|
||||
}
|
||||
// 删除旧的关联关系
|
||||
workflowDeptService.remove(new LambdaQueryWrapper<WorkflowDept>().eq(WorkflowDept::getWorkflowId, workflowDeptDto.getWorkflowId()));
|
||||
return workflowDeptService.saveBatch(workflowDepts) ?
|
||||
ResponseEntity.ok("关联成功") :
|
||||
ResponseEntity.status(HttpStatus.BAD_REQUEST).body("关联失败");
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,12 @@
|
||||
package com.zsc.edu.dify.modules.dify.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class WorkflowDeptDto {
|
||||
@NotNull
|
||||
private String workflowId;
|
||||
@NotNull
|
||||
private Long[] deptIds;
|
||||
}
|
@ -19,7 +19,6 @@ import java.util.Map;
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Getter
|
||||
@Setter
|
||||
@Data
|
||||
@TableName("apps_entity")
|
||||
public class AppEntity extends AppsResponseVO {
|
||||
|
||||
@ -70,4 +69,9 @@ public class AppEntity extends AppsResponseVO {
|
||||
return this.value;
|
||||
}
|
||||
}
|
||||
|
||||
public AppEntity(String id, Long deptId) {
|
||||
this.setId(id);
|
||||
this.deptId = deptId;
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,6 @@ import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@Data
|
||||
@TableName("workflow_data")
|
||||
public class WorkflowData{
|
||||
@TableId
|
||||
|
@ -0,0 +1,13 @@
|
||||
package com.zsc.edu.dify.modules.dify.entity;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.*;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class WorkflowDept {
|
||||
private String workflowId;
|
||||
private Long deptId;
|
||||
}
|
@ -2,10 +2,15 @@ package com.zsc.edu.dify.modules.dify.repo;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.zsc.edu.dify.modules.dify.entity.AppEntity;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface AppEntityRepository extends BaseMapper<AppEntity> {
|
||||
|
||||
@Select("select api_key from apps_entity where id = #{appId}")
|
||||
String selectApiKey(String appId);
|
||||
|
||||
List<AppEntity> selectByAppType(@Param("deptId") Long deptId, @Param("appType") Integer appType);
|
||||
}
|
||||
|
@ -0,0 +1,7 @@
|
||||
package com.zsc.edu.dify.modules.dify.repo;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.zsc.edu.dify.modules.dify.entity.WorkflowDept;
|
||||
|
||||
public interface WorkflowDeptRepository extends BaseMapper<WorkflowDept> {
|
||||
}
|
@ -2,12 +2,13 @@ package com.zsc.edu.dify.modules.dify.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.zsc.edu.dify.modules.dify.entity.AppEntity;
|
||||
import com.zsc.edu.dify.modules.dify.entity.WorkflowDept;
|
||||
import io.github.guoshiqiufeng.dify.server.dto.response.AppsResponseVO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface AppEntityService extends IService<AppEntity> {
|
||||
List<AppsResponseVO> getApps(String mode, String name);
|
||||
List<AppEntity> getApps(String mode, String name, Integer appType);
|
||||
|
||||
boolean enabledApp(String id);
|
||||
|
||||
|
@ -3,9 +3,12 @@ package com.zsc.edu.dify.modules.dify.service.Impl;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.zsc.edu.dify.exception.ConstraintException;
|
||||
import com.zsc.edu.dify.framework.security.SecurityUtil;
|
||||
import com.zsc.edu.dify.modules.dify.entity.AppEntity;
|
||||
import com.zsc.edu.dify.modules.dify.entity.WorkflowDept;
|
||||
import com.zsc.edu.dify.modules.dify.mapper.AppEntityMapper;
|
||||
import com.zsc.edu.dify.modules.dify.repo.AppEntityRepository;
|
||||
import com.zsc.edu.dify.modules.dify.repo.WorkflowDeptRepository;
|
||||
import com.zsc.edu.dify.modules.dify.service.AppEntityService;
|
||||
import io.github.guoshiqiufeng.dify.server.DifyServer;
|
||||
import io.github.guoshiqiufeng.dify.server.dto.response.ApiKeyResponseVO;
|
||||
@ -14,6 +17,7 @@ import jakarta.annotation.Resource;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@ -23,16 +27,23 @@ import java.util.*;
|
||||
@AllArgsConstructor
|
||||
@Service
|
||||
public class AppEntityServiceImpl extends ServiceImpl<AppEntityRepository, AppEntity> implements AppEntityService {
|
||||
@Resource
|
||||
private AppEntityMapper appEntityMapper;
|
||||
@Resource
|
||||
private DifyServer difyServer;
|
||||
@Resource
|
||||
private AppEntityRepository appEntityRepository;
|
||||
|
||||
private final AppEntityMapper appEntityMapper;
|
||||
|
||||
private final DifyServer difyServer;
|
||||
|
||||
private final AppEntityRepository appEntityRepository;
|
||||
|
||||
private final WorkflowDeptRepository workflowDeptRepository;
|
||||
|
||||
|
||||
@Override
|
||||
public List<AppsResponseVO> getApps(String mode, String name) {
|
||||
return addApps(difyServer.apps(mode, name));
|
||||
public List<AppEntity> getApps(String mode, String name, Integer type) {
|
||||
return this.lambdaQuery()
|
||||
.eq(StringUtils.hasText(mode), AppEntity::getMode, mode)
|
||||
.eq(StringUtils.hasText(name), AppEntity::getName, name)
|
||||
.eq(Objects.nonNull(type), AppEntity::getAppType, type)
|
||||
.list();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -97,9 +108,8 @@ public class AppEntityServiceImpl extends ServiceImpl<AppEntityRepository, AppEn
|
||||
*/
|
||||
@Override
|
||||
public List<AppEntity> selectByAppType(Integer appType) {
|
||||
return this.lambdaQuery()
|
||||
.eq(AppEntity::getAppType, appType)
|
||||
.list();
|
||||
Long deptId = SecurityUtil.getUserInfo().getDept().getId();
|
||||
return baseMapper.selectByAppType(deptId, appType);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,11 @@
|
||||
package com.zsc.edu.dify.modules.dify.service.Impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.zsc.edu.dify.modules.dify.entity.WorkflowDept;
|
||||
import com.zsc.edu.dify.modules.dify.repo.WorkflowDeptRepository;
|
||||
import com.zsc.edu.dify.modules.dify.service.WorkflowDeptService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class WorkflowDeptServiceImpl extends ServiceImpl<WorkflowDeptRepository, WorkflowDept> implements WorkflowDeptService {
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package com.zsc.edu.dify.modules.dify.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.zsc.edu.dify.modules.dify.entity.WorkflowDept;
|
||||
|
||||
public interface WorkflowDeptService extends IService<WorkflowDept> {
|
||||
}
|
21
src/main/resources/mappers/dify/AppEntityMapper.xml
Normal file
21
src/main/resources/mappers/dify/AppEntityMapper.xml
Normal file
@ -0,0 +1,21 @@
|
||||
<?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="com.zsc.edu.dify.modules.dify.repo.AppEntityRepository">
|
||||
|
||||
|
||||
<select id="selectByAppType" resultType="appEntity">
|
||||
select ae.* from apps_entity ae
|
||||
left join workflow_dept wd on wd.workflow_id = ae.id
|
||||
<where>
|
||||
<if test="deptId != null">
|
||||
and wd.dept_id = #{deptId}
|
||||
</if>
|
||||
<if test="appType != null">
|
||||
and ae.app_type = #{appType}
|
||||
</if>
|
||||
</where>
|
||||
</select>
|
||||
|
||||
</mapper>
|
@ -0,0 +1,19 @@
|
||||
package com.zsc.edu.dify.modules.dify.repo;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
@SpringBootTest
|
||||
class AppEntityRepositoryTest {
|
||||
|
||||
@Autowired
|
||||
private AppEntityRepository appEntityRepository;
|
||||
|
||||
@Test
|
||||
void selectByAppType() {
|
||||
appEntityRepository.selectByAppType(1L,1);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user