初始化

This commit is contained in:
2026-02-22 18:56:10 +08:00
commit 26677972a6
3112 changed files with 255972 additions and 0 deletions

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>tech.easyflow</groupId>
<artifactId>easyflow-api</artifactId>
<version>${revision}</version>
</parent>
<artifactId>easyflow-api-usercenter</artifactId>
<dependencies>
<dependency>
<groupId>tech.easyflow</groupId>
<artifactId>easyflow-module-auth</artifactId>
</dependency>
<dependency>
<groupId>tech.easyflow</groupId>
<artifactId>easyflow-module-ai</artifactId>
</dependency>
<dependency>
<groupId>tech.easyflow</groupId>
<artifactId>easyflow-common-captcha</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,23 @@
package tech.easyflow.usercenter.controller.ai;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import tech.easyflow.ai.entity.BotCategory;
import tech.easyflow.ai.service.BotCategoryService;
import tech.easyflow.common.annotation.UsePermission;
import tech.easyflow.common.web.controller.BaseCurdController;
/**
* bot分类 控制层。
*
* @author ArkLight
* @since 2025-12-18
*/
@RestController
@RequestMapping("/userCenter/botCategory")
@UsePermission(moduleName = "/api/v1/bot")
public class UcBotCategoryController extends BaseCurdController<BotCategoryService, BotCategory> {
public UcBotCategoryController(BotCategoryService service) {
super(service);
}
}

View File

@@ -0,0 +1,276 @@
package tech.easyflow.usercenter.controller.ai;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.annotation.SaIgnore;
import com.alicp.jetcache.Cache;
import com.mybatisflex.core.keygen.impl.SnowFlakeIDKeyGenerator;
import com.mybatisflex.core.query.QueryWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import tech.easyflow.ai.entity.*;
import tech.easyflow.ai.service.*;
import tech.easyflow.ai.service.impl.BotServiceImpl;
import tech.easyflow.common.annotation.UsePermission;
import tech.easyflow.common.audio.core.AudioServiceManager;
import tech.easyflow.common.domain.Result;
import tech.easyflow.common.satoken.util.SaTokenUtil;
import tech.easyflow.common.web.controller.BaseCurdController;
import tech.easyflow.common.web.exceptions.BusinessException;
import tech.easyflow.common.web.jsonbody.JsonBody;
import javax.annotation.Resource;
import java.io.Serializable;
import java.math.BigInteger;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 控制层。
*
* @author michael
* @since 2024-08-23
*/
@RestController
@RequestMapping("/userCenter/bot")
@UsePermission(moduleName = "/api/v1/bot")
public class UcBotController extends BaseCurdController<BotService, Bot> {
private final ModelService modelService;
private final BotWorkflowService botWorkflowService;
private final BotDocumentCollectionService botDocumentCollectionService;
@Resource
private BotService botService;
@Autowired
@Qualifier("defaultCache") // 指定 Bean 名称
private Cache<String, Object> cache;
@Resource
private AudioServiceManager audioServiceManager;
public UcBotController(BotService service, ModelService modelService, BotWorkflowService botWorkflowService,
BotDocumentCollectionService botDocumentCollectionService) {
super(service);
this.modelService = modelService;
this.botWorkflowService = botWorkflowService;
this.botDocumentCollectionService = botDocumentCollectionService;
}
@Resource
private BotPluginService botPluginService;
@Resource
private BotConversationService conversationMessageService;
@GetMapping("/generateConversationId")
public Result<Long> generateConversationId() {
long nextId = new SnowFlakeIDKeyGenerator().nextId();
return Result.ok(nextId);
}
@PostMapping("updateOptions")
@SaCheckPermission("/api/v1/bot/save")
public Result<Void> updateOptions(@JsonBody("id") BigInteger id,
@JsonBody("options") Map<String, Object> options) {
Bot aiBot = service.getById(id);
Map<String, Object> existOptions = aiBot.getOptions();
if (existOptions == null) {
existOptions = new HashMap<>();
}
if (options != null) {
existOptions.putAll(options);
}
aiBot.setOptions(existOptions);
service.updateById(aiBot);
return Result.ok();
}
@PostMapping("updateLlmOptions")
@SaCheckPermission("/api/v1/bot/save")
public Result<Void> updateLlmOptions(@JsonBody("id")
BigInteger id, @JsonBody("llmOptions")
Map<String, Object> llmOptions) {
Bot aiBot = service.getById(id);
Map<String, Object> existLlmOptions = aiBot.getModelOptions();
if (existLlmOptions == null) {
existLlmOptions = new HashMap<>();
}
if (llmOptions != null) {
existLlmOptions.putAll(llmOptions);
}
aiBot.setModelOptions(existLlmOptions);
service.updateById(aiBot);
return Result.ok();
}
@PostMapping("voiceInput")
@SaIgnore
public Result<String> voiceInput(@RequestParam("audio")
MultipartFile audioFile) {
String recognize = null;
try {
recognize = audioServiceManager.audioToText(audioFile.getInputStream());
} catch (Exception e) {
throw new RuntimeException(e);
}
return Result.ok("", recognize);
}
/**
* 处理聊天请求的接口方法
*
* @param prompt 用户输入的聊天内容,必须提供
* @param botId 聊天机器人的唯一标识符,必须提供
* @param conversationId 会话ID用于标识当前对话会话必须提供
* @param messages 历史消息,用于提供上下文,可选
* @return 返回SseEmitter对象用于服务器向客户端推送聊天响应数据
*/
@PostMapping("chat")
@SaIgnore
public SseEmitter chat(
@JsonBody(value = "prompt", required = true) String prompt,
@JsonBody(value = "botId", required = true) BigInteger botId,
@JsonBody(value = "conversationId", required = true) BigInteger conversationId,
@JsonBody(value = "messages") List<Map<String, String>> messages,
@JsonBody(value = "attachments") List<String> attachments
) {
BotServiceImpl.ChatCheckResult chatCheckResult = new BotServiceImpl.ChatCheckResult();
// 前置校验失败则直接返回错误SseEmitter
SseEmitter errorEmitter = botService.checkChatBeforeStart(botId, prompt, conversationId.toString(), chatCheckResult);
if (errorEmitter != null) {
return errorEmitter;
}
BotConversation conversation = conversationMessageService.getById(conversationId);
if (conversation == null) {
conversation = new BotConversation();
conversation.setId(conversationId);
if (prompt.length() > 200) {
conversation.setTitle(prompt.substring(0, 200));
} else {
conversation.setTitle(prompt);
}
conversation.setBotId(botId);
conversation.setAccountId(SaTokenUtil.getLoginAccount().getId());
commonFiled(conversation, SaTokenUtil.getLoginAccount().getId(), SaTokenUtil.getLoginAccount().getTenantId(), SaTokenUtil.getLoginAccount().getDeptId());
conversationMessageService.save(conversation);
}
return botService.startChat(botId, prompt, conversationId, messages, chatCheckResult, attachments);
}
@PostMapping("updateLlmId")
@SaCheckPermission("/api/v1/bot/save")
public Result<Void> updateBotLlmId(@RequestBody
Bot aiBot) {
service.updateBotLlmId(aiBot);
return Result.ok();
}
@GetMapping("getDetail")
@SaIgnore
public Result<Bot> getDetail(String id) {
return Result.ok(botService.getDetail(id));
}
@Override
@SaIgnore
public Result<Bot> detail(String id) {
Bot data = botService.getDetail(id);
if (data == null) {
return Result.ok(data);
}
Map<String, Object> llmOptions = data.getModelOptions();
if (llmOptions == null) {
llmOptions = new HashMap<>();
}
if (data.getModelId() == null) {
return Result.ok(data);
}
BigInteger llmId = data.getModelId();
Model llm = modelService.getById(llmId);
if (llm == null) {
data.setModelId(null);
return Result.ok(data);
}
Map<String, Object> options = llm.getOptions();
if (options != null && !options.isEmpty()) {
// 获取是否多模态
Boolean multimodal = (Boolean) options.get("multimodal");
llmOptions.put("multimodal", multimodal != null && multimodal);
}
return Result.ok(data);
}
@Override
protected Result<?> onSaveOrUpdateBefore(Bot entity, boolean isSave) {
String alias = entity.getAlias();
if (StringUtils.hasLength(alias)) {
Bot aiBot = service.getByAlias(alias);
if (aiBot != null && isSave) {
throw new BusinessException("别名已存在!");
}
if (aiBot != null && aiBot.getId().compareTo(entity.getId()) != 0) {
throw new BusinessException("别名已存在!");
}
}
if (isSave) {
// 设置默认值
entity.setModelOptions(getDefaultLlmOptions());
}
return super.onSaveOrUpdateBefore(entity, isSave);
}
private Map<String, Object> getDefaultLlmOptions() {
Map<String, Object> defaultLlmOptions = new HashMap<>();
defaultLlmOptions.put("temperature", 0.7);
defaultLlmOptions.put("topK", 4);
defaultLlmOptions.put("maxReplyLength", 2048);
defaultLlmOptions.put("topP", 0.7);
defaultLlmOptions.put("maxMessageCount", 10);
return defaultLlmOptions;
}
private Map<String, Object> errorRespnseMsg(int errorCode, String message) {
HashMap<String, Object> result = new HashMap<>();
result.put("error", errorCode);
result.put("message", message);
return result;
}
@Override
protected Result<?> onRemoveBefore(Collection<Serializable> ids) {
QueryWrapper queryWrapperKnowledge = QueryWrapper.create().in(BotDocumentCollection::getBotId, ids);
botDocumentCollectionService.remove(queryWrapperKnowledge);
QueryWrapper queryWrapperBotWorkflow = QueryWrapper.create().in(BotWorkflow::getBotId, ids);
botWorkflowService.remove(queryWrapperBotWorkflow);
QueryWrapper queryWrapperBotPlugins = QueryWrapper.create().in(BotPlugin::getBotId, ids);
botPluginService.remove(queryWrapperBotPlugins);
return super.onRemoveBefore(ids);
}
}

View File

@@ -0,0 +1,108 @@
package tech.easyflow.usercenter.controller.ai;
import cn.dev33.satoken.annotation.SaIgnore;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryWrapper;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import tech.easyflow.ai.entity.BotConversation;
import tech.easyflow.ai.service.BotConversationService;
import tech.easyflow.common.domain.Result;
import tech.easyflow.common.entity.LoginAccount;
import tech.easyflow.common.satoken.util.SaTokenUtil;
import tech.easyflow.common.web.controller.BaseCurdController;
import tech.easyflow.common.web.exceptions.BusinessException;
import javax.annotation.Resource;
import java.util.Date;
import java.util.List;
@RestController
@RequestMapping("/userCenter/botConversation")
@SaIgnore
public class UcBotConversationController extends BaseCurdController<BotConversationService, BotConversation> {
@Resource
private BotConversationService conversationMessageService;
public UcBotConversationController(BotConversationService service) {
super(service);
}
/**
* 删除指定会话
*/
@GetMapping("/deleteConversation")
public Result<Void> deleteConversation(String botId, String conversationId) {
LoginAccount account = SaTokenUtil.getLoginAccount();
conversationMessageService.deleteConversation(botId, conversationId, account.getId());
return Result.ok();
}
/**
* 更新会话标题
*/
@GetMapping("/updateConversation")
public Result<Void> updateConversation(String botId, String conversationId, String title) {
LoginAccount account = SaTokenUtil.getLoginAccount();
conversationMessageService.updateConversation(botId, conversationId, title, account.getId());
return Result.ok();
}
@Override
public Result<List<BotConversation>> list(BotConversation entity, Boolean asTree, String sortKey, String sortType) {
entity.setAccountId(SaTokenUtil.getLoginAccount().getId());
sortKey = "created";
sortType = "desc";
return super.list(entity, asTree, sortKey, sortType);
}
@Override
protected Result<?> onSaveOrUpdateBefore(BotConversation entity, boolean isSave) {
entity.setAccountId(SaTokenUtil.getLoginAccount().getId());
entity.setCreated(new Date());
return super.onSaveOrUpdateBefore(entity, isSave);
}
/**
* 分页查询会话列表
*
* @param request 查询数据
* @param sortKey 排序字段
* @param sortType 排序方式 asc | desc
* @param pageNumber 当前页码
* @param pageSize 每页的数据量
* @return
*/
@GetMapping("pageList")
public Result<Page<BotConversation>> page(HttpServletRequest request, String sortKey, String sortType, Long pageNumber, Long pageSize) {
if (pageNumber == null || pageNumber < 1) {
pageNumber = 1L;
}
if (pageSize == null || pageSize < 1) {
pageSize = 10L;
}
QueryWrapper queryWrapper = buildQueryWrapper(request);
queryWrapper.orderBy(buildOrderBy(sortKey, sortType, getDefaultOrderBy()));
Page<BotConversation> botConversationPage = service.getMapper().paginateWithRelations(pageNumber, pageSize, queryWrapper);
return Result.ok(botConversationPage);
}
/**
* 根据表主键查询数据详情。
*
* @param id 主键值
* @return 内容详情
*/
@GetMapping("detail")
@SaIgnore
public Result<BotConversation> detail(String id) {
if (tech.easyflow.common.util.StringUtil.noText(id)) {
throw new BusinessException("id must not be null");
}
return Result.ok(service.getMapper().selectOneWithRelationsById(id));
}
}

View File

@@ -0,0 +1,60 @@
package tech.easyflow.usercenter.controller.ai;
import cn.dev33.satoken.annotation.SaIgnore;
import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSON;
import com.mybatisflex.core.query.QueryWrapper;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import tech.easyflow.ai.entity.BotMessage;
import tech.easyflow.ai.service.BotMessageService;
import tech.easyflow.ai.vo.ChatMessageVO;
import tech.easyflow.common.annotation.UsePermission;
import tech.easyflow.common.domain.Result;
import tech.easyflow.common.satoken.util.SaTokenUtil;
import tech.easyflow.common.web.controller.BaseCurdController;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
/**
* Bot 消息记录表 控制层。
*
* @author michael
* @since 2024-11-04
*/
@RestController
@RequestMapping("/userCenter/botMessage")
@UsePermission(moduleName = "/api/v1/bot")
public class UcBotMessageController extends BaseCurdController<BotMessageService, BotMessage> {
private final BotMessageService botMessageService;
public UcBotMessageController(BotMessageService service, BotMessageService botMessageService) {
super(service);
this.botMessageService = botMessageService;
}
@GetMapping("/getMessages")
@SaIgnore
public Result<List<ChatMessageVO>> getMessages(BigInteger botId, BigInteger conversationId) {
List<ChatMessageVO> res = new ArrayList<>();
QueryWrapper w = QueryWrapper.create();
w.eq(BotMessage::getBotId, botId);
w.eq(BotMessage::getConversationId, conversationId);
List<BotMessage> list = botMessageService.list(w);
if (CollectionUtil.isNotEmpty(list)) {
for (BotMessage message : list) {
ChatMessageVO vo = new ChatMessageVO();
vo.setKey(message.getId().toString());
vo.setRole(message.getRole());
vo.setContent(JSON.parseObject(message.getContent()).getString("textContent"));
vo.setPlacement("user".equals(message.getRole()) ? "end" : "start");
vo.setCreated(message.getCreated());
res.add(vo);
}
}
return Result.ok(res);
}
}

View File

@@ -0,0 +1,84 @@
package tech.easyflow.usercenter.controller.ai;
import cn.hutool.core.collection.CollectionUtil;
import com.mybatisflex.core.query.QueryWrapper;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import tech.easyflow.ai.entity.Bot;
import tech.easyflow.ai.entity.BotRecentlyUsed;
import tech.easyflow.ai.service.BotRecentlyUsedService;
import tech.easyflow.ai.service.BotService;
import tech.easyflow.common.annotation.UsePermission;
import tech.easyflow.common.domain.Result;
import tech.easyflow.common.entity.LoginAccount;
import tech.easyflow.common.satoken.util.SaTokenUtil;
import tech.easyflow.common.web.controller.BaseCurdController;
import javax.annotation.Resource;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
/**
* 最近使用 控制层。
*
* @author ArkLight
* @since 2025-12-18
*/
@RestController
@RequestMapping("/userCenter/botRecentlyUsed")
@UsePermission(moduleName = "/api/v1/bot")
public class UcBotRecentlyUsedController extends BaseCurdController<BotRecentlyUsedService, BotRecentlyUsed> {
@Resource
private BotService botService;
public UcBotRecentlyUsedController(BotRecentlyUsedService service) {
super(service);
}
@GetMapping("/getRecentlyBot")
public Result<List<Bot>> getRecentlyBot() {
LoginAccount account = SaTokenUtil.getLoginAccount();
QueryWrapper w = QueryWrapper.create();
w.eq(BotRecentlyUsed::getCreatedBy,account.getId());
w.orderBy(BotRecentlyUsed::getSortNo,true);
List<BotRecentlyUsed> list = service.list(w);
if (CollectionUtil.isNotEmpty(list)) {
List<BigInteger> botIds = list.stream().map(BotRecentlyUsed::getBotId).collect(Collectors.toList());
QueryWrapper botQw = QueryWrapper.create();
botQw.in(Bot::getId,botIds);
List<Bot> listBot = botService.list(botQw);
listBot.sort(Comparator.comparing(bot -> botIds.indexOf(bot.getId())));
return Result.ok(listBot);
}
return Result.ok(new ArrayList<>());
}
@GetMapping("/removeByBotId")
public Result<Void> removeByBotId(BigInteger botId) {
QueryWrapper w = QueryWrapper.create();
w.eq(BotRecentlyUsed::getBotId,botId);
w.eq(BotRecentlyUsed::getCreatedBy,SaTokenUtil.getLoginAccount().getId());
service.remove(w);
return Result.ok();
}
@Override
public Result<List<BotRecentlyUsed>> list(BotRecentlyUsed entity, Boolean asTree, String sortKey, String sortType) {
LoginAccount account = SaTokenUtil.getLoginAccount();
entity.setCreatedBy(account.getId());
return super.list(entity, asTree, sortKey, sortType);
}
@Override
protected Result<?> onSaveOrUpdateBefore(BotRecentlyUsed entity, boolean isSave) {
entity.setCreated(new Date());
entity.setCreatedBy(SaTokenUtil.getLoginAccount().getId());
return super.onSaveOrUpdateBefore(entity, isSave);
}
}

View File

@@ -0,0 +1,58 @@
package tech.easyflow.usercenter.controller.ai;
import cn.hutool.core.io.FileTypeUtil;
import cn.hutool.http.HttpUtil;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryWrapper;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import tech.easyflow.ai.entity.Resource;
import tech.easyflow.ai.service.ResourceService;
import tech.easyflow.common.annotation.UsePermission;
import tech.easyflow.common.domain.Result;
import tech.easyflow.common.entity.LoginAccount;
import tech.easyflow.common.satoken.util.SaTokenUtil;
import tech.easyflow.common.web.controller.BaseCurdController;
import java.io.ByteArrayInputStream;
import java.math.BigInteger;
import java.util.Date;
/**
* 素材库
*
* @author ArkLight
* @since 2025-06-27
*/
@RestController
@RequestMapping("/userCenter/resource")
@UsePermission(moduleName = "/api/v1/resource")
public class UcResourceController extends BaseCurdController<ResourceService, Resource> {
public UcResourceController(ResourceService service) {
super(service);
}
@Override
protected Result<?> onSaveOrUpdateBefore(Resource entity, boolean isSave) {
LoginAccount loginUser = SaTokenUtil.getLoginAccount();
if (isSave) {
String resourceUrl = entity.getResourceUrl();
byte[] bytes = HttpUtil.downloadBytes(resourceUrl);
ByteArrayInputStream stream = new ByteArrayInputStream(bytes);
String suffix = FileTypeUtil.getType(stream, resourceUrl);
entity.setSuffix(suffix);
entity.setFileSize(BigInteger.valueOf(bytes.length));
commonFiled(entity,loginUser.getId(),loginUser.getTenantId(), loginUser.getDeptId());
} else {
entity.setModified(new Date());
entity.setModifiedBy(loginUser.getId());
}
return super.onSaveOrUpdateBefore(entity, isSave);
}
@Override
protected Page<Resource> queryPage(Page<Resource> page, QueryWrapper queryWrapper) {
queryWrapper.eq(Resource::getCreatedBy, SaTokenUtil.getLoginAccount().getId().toString());
return super.queryPage(page, queryWrapper);
}
}

View File

@@ -0,0 +1,38 @@
package tech.easyflow.usercenter.controller.ai;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import tech.easyflow.ai.entity.WorkflowCategory;
import tech.easyflow.ai.service.WorkflowCategoryService;
import tech.easyflow.common.annotation.UsePermission;
import tech.easyflow.common.domain.Result;
import tech.easyflow.common.web.controller.BaseCurdController;
import java.io.Serializable;
import java.util.Collection;
/**
* 工作流分类
*
* @author ArkLight
* @since 2025-12-11
*/
@RestController
@RequestMapping("/userCenter/workflowCategory")
@UsePermission(moduleName = "/api/v1/workflow")
public class UcWorkflowCategoryController extends BaseCurdController<WorkflowCategoryService, WorkflowCategory> {
public UcWorkflowCategoryController(WorkflowCategoryService service) {
super(service);
}
@Override
protected Result<?> onSaveOrUpdateBefore(WorkflowCategory entity, boolean isSave) {
return Result.fail("-");
}
@Override
protected Result<?> onRemoveBefore(Collection<Serializable> ids) {
return Result.fail("-");
}
}

View File

@@ -0,0 +1,139 @@
package tech.easyflow.usercenter.controller.ai;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.stp.StpUtil;
import com.easyagents.flow.core.chain.*;
import com.easyagents.flow.core.chain.runtime.ChainExecutor;
import com.easyagents.flow.core.parser.ChainParser;
import org.springframework.web.bind.annotation.*;
import tech.easyflow.ai.entity.Workflow;
import tech.easyflow.ai.service.WorkflowService;
import tech.easyflow.ai.easyagentsflow.entity.ChainInfo;
import tech.easyflow.ai.easyagentsflow.entity.NodeInfo;
import tech.easyflow.ai.easyagentsflow.service.TinyFlowService;
import tech.easyflow.common.annotation.UsePermission;
import tech.easyflow.common.constant.Constants;
import tech.easyflow.common.domain.Result;
import tech.easyflow.common.satoken.util.SaTokenUtil;
import tech.easyflow.common.web.controller.BaseCurdController;
import tech.easyflow.common.web.jsonbody.JsonBody;
import javax.annotation.Resource;
import java.io.Serializable;
import java.math.BigInteger;
import java.util.*;
/**
* 工作流
*/
@RestController
@RequestMapping("/userCenter/workflow")
@UsePermission(moduleName = "/api/v1/workflow")
public class UcWorkflowController extends BaseCurdController<WorkflowService, Workflow> {
@Resource
private ChainExecutor chainExecutor;
@Resource
private ChainParser chainParser;
@Resource
private TinyFlowService tinyFlowService;
public UcWorkflowController(WorkflowService service) {
super(service);
}
/**
* 节点单独运行
*/
@PostMapping("/singleRun")
@SaCheckPermission("/api/v1/workflow/save")
public Result<?> singleRun(
@JsonBody(value = "workflowId", required = true) BigInteger workflowId,
@JsonBody(value = "nodeId", required = true) String nodeId,
@JsonBody("variables") Map<String, Object> variables) {
Workflow workflow = service.getById(workflowId);
if (workflow == null) {
return Result.fail(1, "工作流不存在");
}
Map<String, Object> res = chainExecutor.executeNode(workflowId.toString(), nodeId, variables);
return Result.ok(res);
}
/**
* 运行工作流 - v2
*/
@PostMapping("/runAsync")
@SaCheckPermission("/api/v1/workflow/save")
public Result<String> runAsync(@JsonBody(value = "id", required = true) BigInteger id,
@JsonBody("variables") Map<String, Object> variables) {
if (variables == null) {
variables = new HashMap<>();
}
Workflow workflow = service.getById(id);
if (workflow == null) {
throw new RuntimeException("工作流不存在");
}
if (StpUtil.isLogin()) {
variables.put(Constants.LOGIN_USER_KEY, SaTokenUtil.getLoginAccount());
}
String executeId = chainExecutor.executeAsync(id.toString(), variables);
return Result.ok(executeId);
}
/**
* 获取工作流运行状态 - v2
*/
@PostMapping("/getChainStatus")
public Result<ChainInfo> getChainStatus(@JsonBody(value = "executeId") String executeId,
@JsonBody("nodes") List<NodeInfo> nodes) {
ChainInfo res = tinyFlowService.getChainStatus(executeId, nodes);
return Result.ok(res);
}
/**
* 恢复工作流运行 - v2
*/
@PostMapping("/resume")
@SaCheckPermission("/api/v1/workflow/save")
public Result<Void> resume(@JsonBody(value = "executeId", required = true) String executeId,
@JsonBody("confirmParams") Map<String, Object> confirmParams) {
chainExecutor.resumeAsync(executeId, confirmParams);
return Result.ok();
}
/**
* 获取工作流参数 - v2
*/
@GetMapping("getRunningParameters")
@SaCheckPermission("/api/v1/workflow/query")
public Result<?> getRunningParameters(@RequestParam BigInteger id) {
Workflow workflow = service.getById(id);
if (workflow == null) {
return Result.fail(1, "can not find the workflow by id: " + id);
}
ChainDefinition definition = chainParser.parse(workflow.getContent());
if (definition == null) {
return Result.fail(2, "节点配置错误,请检查! ");
}
List<Parameter> chainParameters = definition.getStartParameters();
Map<String, Object> res = new HashMap<>();
res.put("parameters", chainParameters);
res.put("title", workflow.getTitle());
res.put("description", workflow.getDescription());
res.put("icon", workflow.getIcon());
return Result.ok(res);
}
@Override
protected Result<?> onSaveOrUpdateBefore(Workflow entity, boolean isSave) {
return Result.fail("-");
}
@Override
protected Result<?> onRemoveBefore(Collection<Serializable> ids) {
return Result.fail("-");
}
}

View File

@@ -0,0 +1,92 @@
package tech.easyflow.usercenter.controller.ai;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.util.StrUtil;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryWrapper;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import tech.easyflow.ai.entity.WorkflowExecResult;
import tech.easyflow.ai.entity.WorkflowExecStep;
import tech.easyflow.ai.service.WorkflowExecResultService;
import tech.easyflow.ai.service.WorkflowExecStepService;
import tech.easyflow.ai.utils.WorkFlowUtil;
import tech.easyflow.common.annotation.UsePermission;
import tech.easyflow.common.domain.Result;
import tech.easyflow.common.entity.LoginAccount;
import tech.easyflow.common.satoken.util.SaTokenUtil;
import tech.easyflow.common.web.controller.BaseCurdController;
import javax.annotation.Resource;
import java.math.BigInteger;
/**
* 工作流执行记录
*/
@RestController
@RequestMapping("/userCenter/workflowExecResult")
@UsePermission(moduleName = "/api/v1/workflow")
public class UcWorkflowExecResultController extends BaseCurdController<WorkflowExecResultService, WorkflowExecResult> {
@Resource
private WorkflowExecStepService recordStepService;
public UcWorkflowExecResultController(WorkflowExecResultService service) {
super(service);
}
/**
* 删除
*/
@GetMapping("/del")
@Transactional(rollbackFor = Exception.class)
@SaCheckPermission("/api/v1/workflow/remove")
public Result<Void> del(BigInteger id) {
LoginAccount account = SaTokenUtil.getLoginAccount();
WorkflowExecResult record = service.getById(id);
if (!account.getId().toString().equals(record.getCreatedBy())) {
return Result.fail(1, "非法请求");
}
service.removeById(id);
QueryWrapper w = QueryWrapper.create();
w.eq(WorkflowExecStep::getRecordId, id);
recordStepService.remove(w);
return Result.ok();
}
@GetMapping("getPage")
public Result<Page<WorkflowExecResult>> getPage(HttpServletRequest request,
String sortKey,
String sortType,
Long pageNumber,
Long pageSize,
String queryBegin,
String queryEnd) {
if (pageNumber == null || pageNumber < 1) {
pageNumber = 1L;
}
if (pageSize == null || pageSize < 1) {
pageSize = 10L;
}
QueryWrapper queryWrapper = buildQueryWrapper(request);
if (StrUtil.isNotEmpty(queryBegin) && StrUtil.isNotEmpty(queryEnd)) {
queryWrapper.between(WorkflowExecResult::getStartTime, queryBegin, queryEnd);
}
queryWrapper.orderBy(buildOrderBy(sortKey, sortType, getDefaultOrderBy()));
return Result.ok(queryPage(new Page<>(pageNumber, pageSize), queryWrapper));
}
@Override
protected Page<WorkflowExecResult> queryPage(Page<WorkflowExecResult> page, QueryWrapper queryWrapper) {
queryWrapper.eq(WorkflowExecResult::getCreatedBy, SaTokenUtil.getLoginAccount().getId().toString());
Page<WorkflowExecResult> res = super.queryPage(page, queryWrapper);
for (WorkflowExecResult record : res.getRecords()) {
record.setWorkflowJson(WorkFlowUtil.removeSensitiveInfo(record.getWorkflowJson()));
}
return res;
}
}

View File

@@ -0,0 +1,66 @@
package tech.easyflow.usercenter.controller.ai;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.mybatisflex.core.query.QueryWrapper;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import tech.easyflow.ai.entity.WorkflowExecResult;
import tech.easyflow.ai.entity.WorkflowExecStep;
import tech.easyflow.ai.service.WorkflowExecResultService;
import tech.easyflow.ai.service.WorkflowExecStepService;
import tech.easyflow.common.annotation.UsePermission;
import tech.easyflow.common.domain.Result;
import tech.easyflow.common.web.controller.BaseCurdController;
import tech.easyflow.common.web.exceptions.BusinessException;
import javax.annotation.Resource;
import java.math.BigInteger;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 执行记录步骤
*/
@RestController
@RequestMapping("/userCenter/workflowExecStep")
@UsePermission(moduleName = "/api/v1/workflow")
public class UcWorkflowExecStepController extends BaseCurdController<WorkflowExecStepService, WorkflowExecStep> {
@Resource
private WorkflowExecResultService execRecordService;
public UcWorkflowExecStepController(WorkflowExecStepService service) {
super(service);
}
/**
* 根据执行记录id获取执行记录步骤列表
*/
@GetMapping("/getListByRecordId")
public Result<List<WorkflowExecStep>> getListByRecordId(BigInteger recordId) {
if (recordId == null) {
throw new BusinessException("recordId不能为空");
}
WorkflowExecResult record = execRecordService.getById(recordId);
String workflowJson = record.getWorkflowJson();
JSONObject workflow = JSON.parseObject(workflowJson);
Map<String, String> idTypeMap = new HashMap<>();
JSONArray nodes = workflow.getJSONArray("nodes");
for (Object node : nodes) {
JSONObject nodeObj = (JSONObject) node;
idTypeMap.put(nodeObj.getString("id"), nodeObj.getString("type"));
}
QueryWrapper w = QueryWrapper.create();
w.eq(WorkflowExecStep::getRecordId, recordId);
List<WorkflowExecStep> list = service.list(w);
for (WorkflowExecStep step : list) {
step.setNodeData(null);
step.setNodeType(idTypeMap.get(step.getNodeId()));
}
return Result.ok(list);
}
}

View File

@@ -0,0 +1,51 @@
package tech.easyflow.usercenter.controller.auth;
import cn.dev33.satoken.stp.StpUtil;
import org.springframework.web.bind.annotation.*;
import tech.easyflow.auth.entity.LoginDTO;
import tech.easyflow.auth.entity.LoginVO;
import tech.easyflow.auth.service.AuthService;
import tech.easyflow.common.domain.Result;
import tech.easyflow.common.web.jsonbody.JsonBody;
import javax.annotation.Resource;
import java.util.List;
/**
* 认证
*/
@RestController
@RequestMapping("/userCenter/auth/")
public class UcAuthController {
@Resource
private AuthService authService;
/**
* 登录
* @param loginDTO 登录参数
*/
@PostMapping("login")
public Result<LoginVO> login(@JsonBody LoginDTO loginDTO) {
LoginVO res = authService.login(loginDTO);
return Result.ok(res);
}
/**
* 登出
*/
@PostMapping("logout")
public Result<Void> logout() {
StpUtil.logout();
return Result.ok();
}
/**
* 获取权限
*/
@GetMapping("getPermissions")
public Result<List<String>> getPermissions() {
List<String> permissionList = StpUtil.getPermissionList();
return Result.ok(permissionList);
}
}

View File

@@ -0,0 +1,46 @@
package tech.easyflow.usercenter.controller.common;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import tech.easyflow.common.domain.Result;
import tech.easyflow.common.dict.Dict;
import tech.easyflow.common.dict.DictItem;
import tech.easyflow.common.dict.DictLoader;
import tech.easyflow.common.dict.DictManager;
import javax.annotation.Resource;
import java.util.Collections;
import java.util.List;
import java.util.Map;
/**
* 字典
*/
@RestController
@RequestMapping("/userCenter/dict/")
public class UcDictController {
@Resource
DictManager dictManager;
/**
* 获取字典项
*/
@GetMapping("/items/{code}")
public Result<List<DictItem>> items(@PathVariable("code") String code, String keyword, HttpServletRequest request) {
DictLoader loader = dictManager.getLoader(code);
if (loader == null) {
return Result.ok(Collections.emptyList());
}
Map<String, String[]> parameterMap = request.getParameterMap();
Dict dict = loader.load(keyword, parameterMap);
if (dict == null) {
return Result.ok(Collections.emptyList());
}
return Result.ok(dict.getItems());
}
}

View File

@@ -0,0 +1,44 @@
package tech.easyflow.usercenter.controller.common;
import cloud.tianai.captcha.application.ImageCaptchaApplication;
import cloud.tianai.captcha.application.vo.ImageCaptchaVO;
import cloud.tianai.captcha.common.constant.CaptchaTypeConstant;
import cloud.tianai.captcha.common.response.ApiResponse;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import tech.easyflow.common.captcha.tainai.CaptchaData;
import javax.annotation.Resource;
/**
* 公共接口
*/
@RestController
@RequestMapping("/userCenter/public")
public class UcPublicController {
@Resource
private ImageCaptchaApplication application;
/**
* 获取验证码
*/
@RequestMapping(value = "/getCaptcha", produces = "application/json")
public ApiResponse<ImageCaptchaVO> getCaptcha() {
return application.generateCaptcha(CaptchaTypeConstant.SLIDER);
}
/**
* 验证码校验
*/
@PostMapping(value = "/check", produces = "application/json")
public ApiResponse<String> checkCaptcha(@RequestBody CaptchaData data) {
ApiResponse<?> response = application.matching(data.getId(), data.getData());
if (!response.isSuccess()) {
return ApiResponse.ofError("验证码错误");
}
return ApiResponse.ofSuccess(data.getId());
}
}

View File

@@ -0,0 +1,58 @@
package tech.easyflow.usercenter.controller.common;
import cn.dev33.satoken.annotation.SaIgnore;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import tech.easyflow.common.domain.Result;
import tech.easyflow.common.vo.UploadResVo;
import tech.easyflow.common.filestorage.FileStorageService;
import javax.annotation.Resource;
/**
* 文件上传
*/
@RestController
@RequestMapping("/userCenter/commons/")
public class UcUploadController {
@Resource(name = "default")
private FileStorageService storageService;
/**
* 上传
*/
@PostMapping(value = "/upload", produces = MediaType.APPLICATION_JSON_VALUE)
public Result<UploadResVo> upload(MultipartFile file) {
String path = storageService.save(file);
UploadResVo resVo = new UploadResVo();
resVo.setPath(path);
return Result.ok(resVo);
}
/**
* @ignore
*/
@PostMapping(value = "/uploadAntd", produces = MediaType.APPLICATION_JSON_VALUE)
public Result<UploadResVo> uploadAntd(MultipartFile file) {
String path = storageService.save(file);
UploadResVo resVo = new UploadResVo();
resVo.setPath(path);
return Result.ok(resVo);
}
/**
* @ignore
*/
@PostMapping(value = "/uploadPrePath",produces = MediaType.APPLICATION_JSON_VALUE)
@SaIgnore
public Result<UploadResVo> uploadPrePath(MultipartFile file, String prePath) {
String path = storageService.save(file,prePath);
UploadResVo resVo = new UploadResVo();
resVo.setPath(path);
return Result.ok(resVo);
}
}

View File

@@ -0,0 +1,91 @@
package tech.easyflow.usercenter.controller.system;
import cn.hutool.crypto.digest.BCrypt;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import tech.easyflow.common.domain.Result;
import tech.easyflow.common.entity.LoginAccount;
import tech.easyflow.common.satoken.util.SaTokenUtil;
import tech.easyflow.common.web.jsonbody.JsonBody;
import tech.easyflow.system.entity.SysAccount;
import tech.easyflow.system.service.SysAccountService;
import javax.annotation.Resource;
import java.math.BigInteger;
import java.util.Date;
/**
* 用户
*
* @author ArkLight
* @since 2025-03-14
*/
@RestController
@RequestMapping("/userCenter/sysAccount")
public class UcSysAccountController {
@Resource
private SysAccountService service;
/**
* 获取用户的信息
*/
@GetMapping("/myProfile")
public Result<SysAccount> myProfile() {
LoginAccount account = SaTokenUtil.getLoginAccount();
SysAccount sysAccount = service.getById(account.getId());
return Result.ok(sysAccount);
}
/**
* 修改用户信息
*/
@PostMapping("/updateProfile")
public Result<Void> updateProfile(@JsonBody SysAccount account) {
BigInteger loginAccountId = SaTokenUtil.getLoginAccount().getId();
SysAccount update = new SysAccount();
update.setId(loginAccountId);
update.setNickname(account.getNickname());
update.setMobile(account.getMobile());
update.setEmail(account.getEmail());
update.setAvatar(account.getAvatar());
update.setModified(new Date());
update.setModifiedBy(loginAccountId);
service.updateById(update);
return Result.ok();
}
/**
* 修改密码
*
* @param password 用户的旧密码
* @param newPassword 新密码
* @param confirmPassword 确认密码
*/
@PostMapping("/updatePassword")
public Result<Void> updatePassword(@JsonBody(value = "password", required = true) String password,
@JsonBody(value = "newPassword", required = true) String newPassword,
@JsonBody(value = "confirmPassword", required = true) String confirmPassword) {
BigInteger loginAccountId = SaTokenUtil.getLoginAccount().getId();
SysAccount record = service.getById(loginAccountId);
if (record == null) {
return Result.fail("修改失败");
}
String pwdDb = record.getPassword();
if (!BCrypt.checkpw(password, pwdDb)) {
return Result.fail(1, "密码不正确");
}
if (!newPassword.equals(confirmPassword)) {
return Result.fail(2, "两次密码不一致");
}
SysAccount update = new SysAccount();
update.setId(loginAccountId);
update.setPassword(BCrypt.hashpw(newPassword));
update.setModified(new Date());
update.setModifiedBy(loginAccountId);
service.updateById(update);
return Result.ok();
}
}

View File

@@ -0,0 +1,36 @@
package tech.easyflow.usercenter.controller.system;
import cn.dev33.satoken.annotation.SaIgnore;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import tech.easyflow.common.annotation.UsePermission;
import tech.easyflow.common.domain.Result;
import tech.easyflow.common.web.controller.BaseCurdController;
import tech.easyflow.common.web.jsonbody.JsonBody;
import tech.easyflow.system.entity.SysUserFeedback;
import tech.easyflow.system.service.SysUserFeedbackService;
/**
* 控制层。
*
* @author 12076
* @since 2025-12-30
*/
@RestController
@RequestMapping("/userCenter/sysUserFeedback")
@UsePermission(moduleName = "/api/v1/sysUserFeedback")
public class UcUserFeedbackController extends BaseCurdController<SysUserFeedbackService, SysUserFeedback> {
@PostMapping("/save")
@SaIgnore
public Result<?> save(@JsonBody SysUserFeedback entity) {
return super.save(entity);
}
public UcUserFeedbackController(SysUserFeedbackService service) {
super(service);
}
}