2026/1/7 16:08:08
网站建设
项目流程
新农村建设官方网站,网站建设英文方案,昆山建设银行网站首页,软文案例大全JIRA 工时自动填报 Agent
xsun_workflow_agent
项目简介
这是一个基于 AI 的 JIRA 工时自动填报系统#xff0c;能够根据用户的 Git 提交记录自动分析并填写 JIRA 工作日志。该系统通过集成 LangChain4j 框架#xff0c;利用大语言模型的能力#xff0c;智能地将 Git 提交…JIRA 工时自动填报 Agentxsun_workflow_agent项目简介这是一个基于 AI 的 JIRA 工时自动填报系统能够根据用户的 Git 提交记录自动分析并填写 JIRA 工作日志。该系统通过集成 LangChain4j 框架利用大语言模型的能力智能地将 Git 提交内容与 JIRA 问题进行匹配并自动生成符合要求的工作日志。功能特性自动获取用户当天的 Git 提交记录查询用户在 JIRA 上未完成的任务智能匹配 Git 提交与 JIRA 问题自动计算所需填写的工时自动生成工作日志并提交至 JIRA 系统技术架构本项目采用以下技术栈Java 21Spring Boot 3.2.3LangChain4j 0.27.1JIRA REST Java Client 7.0.1Eclipse JGit 6.8.0项目结构src/main/java/com/xsun/jira/jira_workflor_agent/ ├── JiraWorkflorAgentApplication.java // 应用入口类 ├── config/ │ ├── AgentConfig.java // AI Agent 配置类 │ └── GitConfig.java // Git 配置类 ├── controller/ │ └── WorklogController.java // 工作日志控制器 ├── model/dto/ │ ├── GitCommit.java // Git 提交数据传输对象 │ ├── JiraIssue.java // JIRA 问题数据传输对象 │ └── WorklogEntry.java // 工作日志条目数据传输对象 ├── service/ │ ├── WorklogAgent.java // 工作日志 AI Agent 接口 │ └── WorklogService.java // 工作日志服务类 └── tools/ ├── DateTimeTools.java // 日期时间工具类 ├── GitTools.java // Git 操作工具类 └── JiraTools.java // JIRA 操作工具类核心代码展示主应用类类名JiraWorkflorAgentApplication.javapackagecom.xsun.jira.jira_workflor_agent;importcom.xsun.jira.jira_workflor_agent.service.WorklogService;importlombok.RequiredArgsConstructor;importlombok.extern.slf4j.Slf4j;importorg.springframework.boot.CommandLineRunner;importorg.springframework.boot.SpringApplication;importorg.springframework.boot.autoconfigure.SpringBootApplication;importorg.springframework.scheduling.annotation.EnableScheduling;importorg.springframework.scheduling.annotation.Scheduled;Slf4jSpringBootApplicationEnableSchedulingRequiredArgsConstructorpublicclassJiraWorkflorAgentApplicationimplementsCommandLineRunner{publicstaticvoidmain(String[]args){SpringApplication.run(JiraWorkflorAgentApplication.class,args);}Overridepublicvoidrun(String...args)throwsException{log.info(工作日志Agent启动成功);}}AI Agent 配置类名AgentConfig.javapackagecom.xsun.jira.jira_workflor_agent.config;importcom.xsun.jira.jira_workflor_agent.service.WorklogAgent;importcom.xsun.jira.jira_workflor_agent.tools.DateTimeTools;importcom.xsun.jira.jira_workflor_agent.tools.GitTools;importcom.xsun.jira.jira_workflor_agent.tools.JiraTools;importdev.langchain4j.memory.chat.MessageWindowChatMemory;importdev.langchain4j.model.chat.ChatLanguageModel;importdev.langchain4j.model.openai.OpenAiChatModel;importdev.langchain4j.service.AiServices;importlombok.RequiredArgsConstructor;importorg.springframework.beans.factory.annotation.Value;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importjava.time.Duration;/** * program: AgentConfig.java * description: * author: sunmouren * create: 2025-12-14 **/ConfigurationRequiredArgsConstructorpublicclassAgentConfig{Value(${openai.api-key})privateStringopenaiApiKey;Value(${openai.model})privateStringopenaiModel;Value(${openai.base_url})privateStringbaseUrl;privatefinalJiraToolsjiraTools;privatefinalGitToolsgitTools;privatefinalDateTimeToolsdateTimeTools;BeanpublicChatLanguageModelchatLanguageModel(){returnOpenAiChatModel.builder().apiKey(openaiApiKey).modelName(openaiModel).baseUrl(baseUrl).temperature(0.5).timeout(Duration.ofSeconds(60)).build();}BeanpublicWorklogAgentworklogAgent(ChatLanguageModelchatLanguageModel){returnAiServices.builder(WorklogAgent.class).chatLanguageModel(chatLanguageModel).chatMemory(MessageWindowChatMemory.withMaxMessages(10)).tools(jiraTools,gitTools,dateTimeTools).build();}}工作日志 AI Agent 接口类名WorklogAgent.javapackagecom.xsun.jira.jira_workflor_agent.service;importdev.langchain4j.service.SystemMessage;importdev.langchain4j.service.UserMessage;publicinterfaceWorklogAgent{SystemMessage( 你是一个工作日志助手。你的任务是帮助用户自动记录JIRA工作时间。 工作流程 1. 获取当前日期判断今天是星期几 2. 根据星期判断需要工作的时间周一/三/五8小时周二/四10小时 3. 获取用户今天已经记录的JIRA工作时间 4. 如果工作时间不足需要 - 获取用户今天的Git提交记录 - 获取用户未完成的JIRA问题列表 - 使用这些信息匹配并补充工作时间(git提交记录最匹配的jira问题使用余弦相似度) 5. 确认后记录工作时间 注意 - 时间以0.5小时为最小单位 - 最终的总工作时间必须等于要求的时间 - 工作说明要基于Git提交的comment总结 - 工作说明不需要明写是根据git总结的只需要返回具体的总结信息即可 - 如果不需要记录只需要返回已经填满不缺时间 - 选择一个你觉得最好的方案进行填写jira不必找我二次确认直接填写 )Stringchat(UserMessageStringmessage);}Git 操作工具类名GitTools.javapackagecom.xsun.jira.jira_workflor_agent.tools;importcom.xsun.jira.jira_workflor_agent.config.GitConfig;importcom.xsun.jira.jira_workflor_agent.model.dto.GitCommit;importdev.langchain4j.agent.tool.Tool;importlombok.extern.slf4j.Slf4j;importorg.eclipse.jgit.api.Git;importorg.eclipse.jgit.lib.PersonIdent;importorg.eclipse.jgit.revwalk.RevCommit;importorg.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.beans.factory.annotation.Value;importorg.springframework.stereotype.Component;importjava.io.File;importjava.time.LocalDate;importjava.time.LocalDateTime;importjava.time.ZoneId;importjava.util.ArrayList;importjava.util.List;/** * program: GitTools.java * description: * author: sunmouren * create: 2025-12-14 **/Slf4jComponentpublicclassGitTools{Value(${git.username})privateStringgitUsername;AutowiredprivateGitConfiggitConfig;Tool(从Git仓库获取指定用户今天的提交记录)publicListGitCommitgetTodayCommitsByUser(){log.info(正在获取用户 {} 今天的Git提交记录...,gitUsername);ListGitCommitcommitsnewArrayList();LocalDatetodayLocalDate.now();for(GitConfig.Repositoryrepo:gitConfig.getRepositories()){try{FiletempDirFile.createTempFile(git-,-repo);tempDir.delete();tempDir.mkdirs();log.info(克隆仓库: {},repo.getUrl());GitgitGit.cloneRepository().setURI(repo.getUrl()).setDirectory(tempDir).setCredentialsProvider(newUsernamePasswordCredentialsProvider(gitUsername,repo.getToken())).setBranch(repo.getBranch()).call();IterableRevCommitlogsgit.log().call();for(RevCommitcommit:logs){PersonIdentauthorcommit.getAuthorIdent();LocalDateTimecommitTimeLocalDateTime.ofInstant(author.getWhen().toInstant(),ZoneId.systemDefault());// 只获取今天的提交if(commitTime.toLocalDate().equals(today)author.getName().equals(gitUsername)){GitCommitgitCommitnewGitCommit();gitCommit.setCommitId(commit.getName());gitCommit.setAuthor(author.getName());gitCommit.setMessage(commit.getFullMessage());gitCommit.setCommitTime(commitTime);gitCommit.setRepository(repo.getUrl());commits.add(gitCommit);}}git.close();deleteDirectory(tempDir);}catch(Exceptione){log.error(获取仓库 {} 的提交记录失败,repo.getUrl(),e);}}log.info(共找到 {} 条今天的提交记录,commits.size());returncommits;}privatevoiddeleteDirectory(Filedirectory){File[]filesdirectory.listFiles();if(files!null){for(Filefile:files){if(file.isDirectory()){deleteDirectory(file);}else{file.delete();}}}directory.delete();}}JIRA 操作工具类名JiraTools.javapackagecom.xsun.jira.jira_workflor_agent.tools;importai.djl.util.JsonUtils;importcom.atlassian.jira.rest.client.api.JiraRestClient;importcom.atlassian.jira.rest.client.api.JiraRestClientFactory;importcom.atlassian.jira.rest.client.api.domain.SearchResult;importcom.atlassian.jira.rest.client.api.domain.input.WorklogInput;importcom.atlassian.jira.rest.client.api.domain.input.WorklogInputBuilder;importcom.atlassian.jira.rest.client.internal.async.AsynchronousJiraRestClientFactory;importcom.google.gson.JsonNull;importcom.xsun.jira.jira_workflor_agent.model.dto.JiraIssue;importcom.xsun.jira.jira_workflor_agent.model.dto.WorklogEntry;importdev.langchain4j.agent.tool.Tool;importlombok.extern.slf4j.Slf4j;importorg.springframework.beans.factory.annotation.Value;importorg.springframework.stereotype.Component;importjava.net.URI;importjava.time.LocalDate;importjava.time.LocalDateTime;importjava.time.ZoneId;importjava.util.ArrayList;importjava.util.List;importjava.util.stream.StreamSupport;/** * program: JiraTools.java * description: * author: sunmouren * create: 2025-12-14 **/Slf4jComponentpublicclassJiraTools{Value(${jira.url})privateStringjiraUrl;Value(${jira.username})privateStringusername;Value(${jira.api-token})privateStringapiToken;privateJiraRestClientjiraClient;privateJiraRestClientgetClient(){if(jiraClientnull){JiraRestClientFactoryfactorynewAsynchronousJiraRestClientFactory();URIjiraServerUriURI.create(jiraUrl);jiraClientfactory.createWithBasicHttpAuthentication(jiraServerUri,username,apiToken);}returnjiraClient;}Tool(从JIRA获取我未完成的问题列表)publicListJiraIssuegetMyUnfinishedIssues(){log.info(正在获取未完成的JIRA问题...);ListJiraIssueissuesnewArrayList();try{StringjqlString.format(assignee currentUser() AND resolution Unresolved );varsearchResultgetClient().getSearchClient().searchJql(jql).claim();searchResult.getIssues().forEach(issue-{JiraIssuejiraIssuenewJiraIssue();jiraIssue.setKey(issue.getKey());jiraIssue.setSummary(issue.getSummary());jiraIssue.setDescription(issue.getDescription()!null?issue.getDescription():);jiraIssue.setStatus(issue.getStatus().getName());jiraIssue.setCreated(LocalDateTime.ofInstant(issue.getCreationDate().toDate().toInstant(),ZoneId.systemDefault()));jiraIssue.setUpdated(LocalDateTime.ofInstant(issue.getUpdateDate().toDate().toInstant(),ZoneId.systemDefault()));issues.add(jiraIssue);});log.info(找到 {} 个未完成的问题,issues.size());}catch(Exceptione){log.error(获取JIRA问题失败,e);}returnissues;}Tool(从JIRA获取我今天的工作记录)publicListWorklogEntrygetMyTodayWorklogs(){log.info(正在获取今天的工作记录...);ListWorklogEntryworklogsnewArrayList();try{LocalDatetodayLocalDate.now();StringjqlString.format(worklogAuthor currentUser() AND worklogDate %s,today.toString());SearchResultsearchResultgetClient().getSearchClient().searchJql(jql).claim();log.info(找到 {} 个问题,searchResult.getTotal());searchResult.getIssues().forEach(issue-{varissueWorklogsgetClient().getIssueClient().getIssue(issue.getKey()).claim().getWorklogs();StreamSupport.stream(issueWorklogs.spliterator(),false).filter(wl-{LocalDateworklogDateLocalDateTime.ofInstant(wl.getUpdateDate().toDate().toInstant(),ZoneId.systemDefault()).toLocalDate();returnworklogDate.equals(today);}).forEach(wl-{WorklogEntryentrynewWorklogEntry();entry.setIssueKey(issue.getKey());entry.setTimeSpentHours(wl.getMinutesSpent()/60.0);entry.setComment(wl.getComment());entry.setStarted(LocalDateTime.ofInstant(wl.getStartDate().toDate().toInstant(),ZoneId.systemDefault()));worklogs.add(entry);});});log.info(找到 {} 条今天的工作记录,worklogs.size());}catch(Exceptione){log.error(获取工作记录失败,e);}returnworklogs;}Tool(记录JIRA工作时间参数issueKey-问题编号, timeSpentHours-工作时长(小时), comment-工作说明)publicbooleanlogWork(StringissueKey,doubletimeSpentHours,Stringcomment){log.info(正在记录工作时间: {} - {}小时 - {},issueKey,timeSpentHours,comment);try{intminutes(int)(timeSpentHours*60);WorklogInputBuilderworklogInputBuildernewWorklogInputBuilder(URI.create(jiraUrl/rest/api/2/issue/issueKey/worklog));WorklogInputbuildworklogInputBuilder.setMinutesSpent(minutes).setComment(comment).build();getClient().getIssueClient().addWorklog(URI.create(jiraUrl/rest/api/2/issue/issueKey/worklog),build).claim();log.info(工作时间记录成功);returntrue;}catch(Exceptione){log.error(记录工作时间失败,e);returnfalse;}}}数据传输对象类名GitCommit.javapackagecom.xsun.jira.jira_workflor_agent.model.dto;importlombok.Getter;importlombok.Setter;importlombok.experimental.Accessors;importjava.time.LocalDateTime;/** * program: GitCommit.java * description: * author: sunmouren * create: 2025-12-14 **/GetterSetterAccessors(chaintrue)publicclassGitCommit{privateStringcommitId;privateStringauthor;privateStringmessage;privateLocalDateTimecommitTime;privateStringrepository;privateintfilesChanged;privateintadditions;privateintdeletions;}类名JiraIssue.javapackagecom.xsun.jira.jira_workflor_agent.model.dto;importlombok.Getter;importlombok.Setter;importlombok.experimental.Accessors;importjava.time.LocalDateTime;/** * program: JiraIssue.java * description: * author: sunmouren * create: 2025-12-14 **/GetterSetterAccessors(chaintrue)publicclassJiraIssue{privateStringkey;privateStringsummary;privateStringdescription;privateStringstatus;privateLocalDateTimecreated;privateLocalDateTimeupdated;}类名WorklogEntry.javapackagecom.xsun.jira.jira_workflor_agent.model.dto;importlombok.Getter;importlombok.Setter;importlombok.experimental.Accessors;importjava.time.LocalDateTime;/** * program: WorklogEntry.java * description: * author: sunmouren * create: 2025-12-14 **/GetterSetterAccessors(chaintrue)publicclassWorklogEntry{privateStringissueKey;privatedoubletimeSpentHours;privateStringcomment;privateLocalDateTimestarted;/** * 相似度分数 */privatedoublesimilarityScore;}配置文件配置文件路径application.ymljira:url:http://jira***# 用户名username:xxx# 密码api-token:xxxgit:repositories:# 仓库地址-url:https://gitlab.xsun.com/***# 密码token:xxx# 统计分支branch:xxx# 账号username:xxx# 相关ai的key等openai:api-key:sk-YOUR-API-KEYmodel:qwen-plusbase_url:https://dashscope.aliyuncs.com/compatible-mode/v1# 每天需要填写时长work-hours:monday:8.0tuesday:10.0wednesday:8.0thursday:10.0friday:8.0minimum-unit:0.5# 最小时间单位小时使用方法配置 JIRA 和 Git 相关信息到 application.yml 文件中启动应用程序访问/api/worklog/chat?message请帮我填写今日工时接口触发工时填报流程工作原理当用户请求填写工时时系统首先判断今天是星期几确定应填写的工时数周一/三/五为8小时周二/四为10小时检查用户今天已填写的工时总数如果还需要补填工时则获取用户今天的所有 Git 提交记录获取用户所有的未完成 JIRA 任务利用 AI 模型对 Git 提交内容和 JIRA 任务描述进行语义匹配自动分配剩余工时到最相关的 JIRA 任务上自动生成合适的工作说明并提交到 JIRA依赖管理类名pom.xml部分!-- LangChain4j Core --dependencygroupIddev.langchain4j/groupIdartifactIdlangchain4j/artifactIdversion${langchain4j.version}/version/dependency!-- LangChain4j OpenAI --dependencygroupIddev.langchain4j/groupIdartifactIdlangchain4j-open-ai/artifactIdversion${langchain4j.version}/version/dependency!-- JIRA REST Client --dependencygroupIdcom.atlassian.jira/groupIdartifactIdjira-rest-java-client-core/artifactIdversion7.0.1/version/dependency!-- JGit for Git operations --dependencygroupIdorg.eclipse.jgit/groupIdartifactIdorg.eclipse.jgit/artifactIdversion6.8.0.202311291450-r/version/dependency