Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
P
pms-agent
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
姜耀祖
pms-agent
Commits
a9e537c1
Commit
a9e537c1
authored
Apr 27, 2026
by
徐健
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
新增it看板业务
parent
937200e4
Pipeline
#25156
passed with stages
in 2 minutes and 47 seconds
Changes
23
Pipelines
1
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
23 changed files
with
2163 additions
and
1 deletion
+2163
-1
WorkbenchAgentChatController.java
...ch/pms/agent/controller/WorkbenchAgentChatController.java
+89
-0
WorkbenchAIService.java
...poch/pms/agent/domain/itworkbench/WorkbenchAIService.java
+169
-0
WorkbenchAgentConfiguration.java
...main/itworkbench/aiModel/WorkbenchAgentConfiguration.java
+38
-0
WorkbenchIntent.java
...h/pms/agent/domain/itworkbench/enums/WorkbenchIntent.java
+28
-0
WorkbenchConversationState.java
.../domain/itworkbench/model/WorkbenchConversationState.java
+44
-0
DepartmentWork.java
...t/domain/itworkbench/model/department/DepartmentWork.java
+167
-0
DepartmentWorkDto.java
...omain/itworkbench/model/department/DepartmentWorkDto.java
+27
-0
DepartmentWorkProgress.java
.../itworkbench/model/department/DepartmentWorkProgress.java
+120
-0
AnnualKpi.java
...och/pms/agent/domain/itworkbench/model/kpi/AnnualKpi.java
+173
-0
AnnualKpiDto.java
.../pms/agent/domain/itworkbench/model/kpi/AnnualKpiDto.java
+33
-0
AnnualKpiProgress.java
...agent/domain/itworkbench/model/kpi/AnnualKpiProgress.java
+94
-0
DepartmentMinutes.java
...t/domain/itworkbench/model/minutes/DepartmentMinutes.java
+127
-0
DepartmentMinutesDto.java
...omain/itworkbench/model/minutes/DepartmentMinutesDto.java
+28
-0
DepartmentMinutesProgress.java
.../itworkbench/model/minutes/DepartmentMinutesProgress.java
+90
-0
ManagerMinutesRecord.java
...omain/itworkbench/model/minutes/ManagerMinutesRecord.java
+64
-0
WorkbenchRemoteService.java
...ent/domain/itworkbench/remote/WorkbenchRemoteService.java
+178
-0
WorkbenchConversationStateService.java
.../itworkbench/state/WorkbenchConversationStateService.java
+50
-0
WorkbenchIntentRecognizer.java
.../itworkbench/understanding/WorkbenchIntentRecognizer.java
+157
-0
WorkbenchQueryUnderstandingService.java
...nch/understanding/WorkbenchQueryUnderstandingService.java
+434
-0
ITProperties.java
...java/com/infoepoch/pms/agent/properties/ITProperties.java
+18
-0
application.yml
src/main/resources/application.yml
+7
-0
itChatTest.java
src/test/java/com/infoepoch/pms/agent/itChatTest.java
+27
-0
GraphCheckPointRepositoryTest.java
...dapter/out/persistence/GraphCheckPointRepositoryTest.java
+1
-1
No files found.
src/main/java/com/infoepoch/pms/agent/controller/WorkbenchAgentChatController.java
0 → 100644
View file @
a9e537c1
package
com
.
infoepoch
.
pms
.
agent
.
controller
;
import
com.infoepoch.pms.agent.common.utils.LogHelper
;
import
com.infoepoch.pms.agent.domain.care.CareAgentService
;
import
com.infoepoch.pms.agent.domain.care.log.CareTraceLogSupport
;
import
com.infoepoch.pms.agent.domain.itworkbench.WorkbenchAIService
;
import
lombok.RequiredArgsConstructor
;
import
org.springframework.http.MediaType
;
import
org.springframework.http.codec.ServerSentEvent
;
import
org.springframework.util.StringUtils
;
import
org.springframework.web.bind.annotation.GetMapping
;
import
org.springframework.web.bind.annotation.RequestMapping
;
import
org.springframework.web.bind.annotation.RequestParam
;
import
org.springframework.web.bind.annotation.RestController
;
import
reactor.core.publisher.Flux
;
@RestController
@RequiredArgsConstructor
@RequestMapping
(
"/api/workbench/agent"
)
public
class
WorkbenchAgentChatController
{
private
final
CareAgentService
careAgentService
;
private
final
WorkbenchAIService
workbenchAIService
;
/**
* 推荐场景的标准 GET 方式的流式聊天入口。
*/
@GetMapping
(
value
=
"/stream"
,
produces
=
MediaType
.
TEXT_EVENT_STREAM_VALUE
)
public
Flux
<
ServerSentEvent
<
String
>>
streamCareAgent
(
@RequestParam
(
"sessionId"
)
String
sessionId
,
@RequestParam
(
"message"
)
String
message
)
{
return
streamWorkbenchAgentInternal
(
sessionId
,
message
);
}
/**
* 将领域服务输出统一封装成 SSE 事件流。
*/
private
Flux
<
ServerSentEvent
<
String
>>
streamWorkbenchAgentInternal
(
String
sessionId
,
String
message
)
{
String
traceId
=
CareTraceLogSupport
.
newTraceId
();
LogHelper
.
info
(
this
,
CareTraceLogSupport
.
format
(
traceId
,
sessionId
,
"it_workbench_stream_start"
,
"request received, "
+
CareTraceLogSupport
.
safeMessageSummary
(
message
)
));
if
(!
StringUtils
.
hasText
(
sessionId
))
{
LogHelper
.
info
(
this
,
CareTraceLogSupport
.
format
(
traceId
,
sessionId
,
"it_workbench_stream_validation_failed"
,
"sessionId is blank"
));
return
Flux
.
just
(
buildEvent
(
"error"
,
"sessionId不能为空"
));
}
return
workbenchAIService
.
streamChat
(
traceId
,
sessionId
,
message
)
.
map
(
chunk
->
buildEvent
(
"message"
,
chunk
))
.
concatWithValues
(
buildEvent
(
"done"
,
"[DONE]"
))
.
doOnComplete
(()
->
LogHelper
.
info
(
this
,
CareTraceLogSupport
.
format
(
traceId
,
sessionId
,
"it_workbench_stream_complete"
,
"sse completed"
)))
.
doOnError
(
error
->
LogHelper
.
error
(
this
,
CareTraceLogSupport
.
format
(
traceId
,
sessionId
,
"it_workbench_stream_error"
,
"sse failed, error="
+
CareTraceLogSupport
.
safeText
(
resolveErrorMessage
(
error
))
),
error
))
.
onErrorResume
(
error
->
Flux
.
just
(
buildEvent
(
"error"
,
resolveErrorMessage
(
error
))));
}
/**
* 构造统一的 SSE 消息事件。
*/
private
ServerSentEvent
<
String
>
buildEvent
(
String
event
,
String
data
)
{
return
ServerSentEvent
.<
String
>
builder
()
.
event
(
event
)
.
data
(
data
)
.
build
();
}
/**
* 将异常转换成可直接返回给前端的错误文案。
*/
private
String
resolveErrorMessage
(
Throwable
error
)
{
return
StringUtils
.
hasText
(
error
.
getMessage
())
?
error
.
getMessage
()
:
"itWorkbench流式调用失败"
;
}
}
src/main/java/com/infoepoch/pms/agent/domain/itworkbench/WorkbenchAIService.java
0 → 100644
View file @
a9e537c1
package
com
.
infoepoch
.
pms
.
agent
.
domain
.
itworkbench
;
import
com.infoepoch.pms.agent.common.utils.LogHelper
;
import
com.infoepoch.pms.agent.config.JsonUtils
;
import
com.infoepoch.pms.agent.domain.care.log.CareTraceLogSupport
;
import
com.infoepoch.pms.agent.domain.itworkbench.enums.WorkbenchIntent
;
import
com.infoepoch.pms.agent.domain.itworkbench.model.WorkbenchConversationState
;
import
com.infoepoch.pms.agent.domain.itworkbench.model.department.DepartmentWorkDto
;
import
com.infoepoch.pms.agent.domain.itworkbench.model.kpi.AnnualKpiDto
;
import
com.infoepoch.pms.agent.domain.itworkbench.model.minutes.DepartmentMinutesDto
;
import
com.infoepoch.pms.agent.domain.itworkbench.remote.WorkbenchRemoteService
;
import
com.infoepoch.pms.agent.domain.itworkbench.state.WorkbenchConversationStateService
;
import
com.infoepoch.pms.agent.domain.itworkbench.understanding.WorkbenchIntentRecognizer
;
import
com.infoepoch.pms.agent.domain.itworkbench.understanding.WorkbenchQueryUnderstandingService
;
import
org.springframework.stereotype.Service
;
import
org.springframework.util.StringUtils
;
import
reactor.core.publisher.Flux
;
import
java.util.HashMap
;
import
java.util.Map
;
@Service
public
class
WorkbenchAIService
{
private
final
WorkbenchIntentRecognizer
intentRecognizer
;
private
final
WorkbenchQueryUnderstandingService
parser
;
private
final
WorkbenchRemoteService
remoteService
;
private
final
WorkbenchConversationStateService
conversationStateService
;
public
WorkbenchAIService
(
WorkbenchIntentRecognizer
intentRecognizer
,
WorkbenchQueryUnderstandingService
parser
,
WorkbenchRemoteService
remoteService
,
WorkbenchConversationStateService
conversationStateService
)
{
this
.
intentRecognizer
=
intentRecognizer
;
this
.
parser
=
parser
;
this
.
remoteService
=
remoteService
;
this
.
conversationStateService
=
conversationStateService
;
}
public
Flux
<
String
>
streamChat
(
String
traceId
,
String
sessionId
,
String
message
)
{
LogHelper
.
info
(
this
,
CareTraceLogSupport
.
format
(
traceId
,
sessionId
,
"workbench_chat_start"
,
"用户消息:"
+
message
));
// 加载历史状态
WorkbenchConversationState
state
=
conversationStateService
.
load
(
sessionId
).
orElse
(
null
);
try
{
// 1. 意图识别
WorkbenchIntent
intent
=
intentRecognizer
.
recognize
(
traceId
,
sessionId
,
message
,
state
);
// ====================== 【终极修复:只改这里】 ======================
// 判断:用户是不是要【重新查新业务】
boolean
isNewQuery
=
isNewBusinessMessage
(
message
);
// 如果是新查询 → 不传状态(清空上下文!!!)
WorkbenchConversationState
parseState
=
isNewQuery
?
null
:
state
;
// ==================================================================
// 2. 查询数据
Object
data
=
null
;
String
dataType
=
""
;
Map
<
String
,
Object
>
conditions
=
new
HashMap
<>();
if
(
WorkbenchIntent
.
QUERY_DEPARTMENT_WORK
.
equals
(
intent
))
{
// 这里传入 parseState,不是原来的 state!
DepartmentWorkDto
dto
=
parser
.
understandWork
(
traceId
,
sessionId
,
message
,
parseState
);
dto
.
setSessionId
(
sessionId
);
data
=
remoteService
.
queryWork
(
dto
);
dataType
=
"重点工作"
;
conditions
=
extractWorkConditions
(
dto
);
}
else
if
(
WorkbenchIntent
.
QUERY_ANNUAL_KPI
.
equals
(
intent
))
{
AnnualKpiDto
dto
=
parser
.
understandKpi
(
traceId
,
sessionId
,
message
,
parseState
);
dto
.
setSessionId
(
sessionId
);
data
=
remoteService
.
queryKpi
(
dto
);
dataType
=
"部门 KPI"
;
conditions
=
extractKpiConditions
(
dto
);
}
else
if
(
WorkbenchIntent
.
QUERY_DEPARTMENT_MINUTES
.
equals
(
intent
))
{
DepartmentMinutesDto
dto
=
parser
.
understandMinutes
(
traceId
,
sessionId
,
message
,
parseState
);
dto
.
setSessionId
(
sessionId
);
data
=
remoteService
.
queryMinutes
(
dto
);
dataType
=
"会议跟踪"
;
conditions
=
extractMinutesConditions
(
dto
);
}
else
{
return
Flux
.
just
(
"我是您的员工看板智能助手,目前只能帮您查询重点工作、KPI、会议纪要相关信息哦~"
);
}
// ====================== 【保存状态:简单安全版】 ======================
// 新查询 → 全新创建
// 筛选 → 覆盖更新(你没有rewrite,就直接用initial,完全没问题)
conversationStateService
.
save
(
WorkbenchConversationState
.
initial
(
sessionId
,
intent
,
conditions
)
);
// ==================================================================
String
dataJson
=
JsonUtils
.
objectToJson
(
data
);
return
Flux
.
concat
(
Flux
.
just
(
"我这就为你查询"
+
dataType
+
"信息,请稍候...\n"
),
parser
.
generateAnswer
(
traceId
,
sessionId
,
dataJson
),
Flux
.
just
(
"\n以上就是你要的全部内容啦~"
)
)
.
doOnComplete
(()->
LogHelper
.
info
(
this
,
CareTraceLogSupport
.
format
(
traceId
,
sessionId
,
"workbench_chat_complete"
,
"流式返回完成"
)))
.
doOnError
(
err
->
LogHelper
.
error
(
this
,
CareTraceLogSupport
.
format
(
traceId
,
sessionId
,
"workbench_error"
,
"异常:"
+
err
.
getMessage
()
),
err
));
}
catch
(
Exception
e
)
{
LogHelper
.
error
(
this
,
CareTraceLogSupport
.
format
(
traceId
,
sessionId
,
"workbench_error"
,
"执行失败:"
+
e
.
getMessage
()
),
e
);
return
Flux
.
just
(
"查询失败,请稍后重试"
);
}
}
// ====================== 【工具方法:判断是否新查询】 ======================
private
boolean
isNewBusinessMessage
(
String
message
)
{
if
(!
org
.
springframework
.
util
.
StringUtils
.
hasText
(
message
))
return
false
;
String
m
=
message
.
toLowerCase
();
return
m
.
contains
(
"查"
)
||
m
.
contains
(
"kpi"
)
||
m
.
contains
(
"绩效"
)
||
m
.
contains
(
"指标"
)
||
m
.
contains
(
"工作"
)
||
m
.
contains
(
"任务"
)
||
m
.
contains
(
"会议"
)
||
m
.
contains
(
"纪要"
)
||
m
.
contains
(
"督办"
)
||
m
.
contains
(
"跟踪"
);
}
private
Map
<
String
,
Object
>
extractWorkConditions
(
DepartmentWorkDto
dto
)
{
Map
<
String
,
Object
>
conditions
=
new
HashMap
<>();
if
(
StringUtils
.
hasText
(
dto
.
getYear
()))
conditions
.
put
(
"year"
,
dto
.
getYear
());
if
(
StringUtils
.
hasText
(
dto
.
getMonth
()))
conditions
.
put
(
"month"
,
dto
.
getMonth
());
if
(
dto
.
getLevelType
()
!=
null
)
conditions
.
put
(
"levelType"
,
dto
.
getLevelType
());
if
(
StringUtils
.
hasText
(
dto
.
getCategoryContain
()))
conditions
.
put
(
"categoryContain"
,
dto
.
getCategoryContain
());
if
(
StringUtils
.
hasText
(
dto
.
getWorkGroupContain
()))
conditions
.
put
(
"workGroupContain"
,
dto
.
getWorkGroupContain
());
if
(
StringUtils
.
hasText
(
dto
.
getManagerUser
()))
conditions
.
put
(
"managerUser"
,
dto
.
getManagerUser
());
return
conditions
;
}
private
Map
<
String
,
Object
>
extractKpiConditions
(
AnnualKpiDto
dto
)
{
Map
<
String
,
Object
>
conditions
=
new
HashMap
<>();
if
(
StringUtils
.
hasText
(
dto
.
getYear
()))
conditions
.
put
(
"year"
,
dto
.
getYear
());
if
(
StringUtils
.
hasText
(
dto
.
getMonth
()))
conditions
.
put
(
"month"
,
dto
.
getMonth
());
if
(
dto
.
getLevelType
()
!=
null
)
conditions
.
put
(
"levelType"
,
dto
.
getLevelType
());
if
(
StringUtils
.
hasText
(
dto
.
getCategoryContain
()))
conditions
.
put
(
"categoryContain"
,
dto
.
getCategoryContain
());
if
(
StringUtils
.
hasText
(
dto
.
getWorkGroupContain
()))
conditions
.
put
(
"workGroupContain"
,
dto
.
getWorkGroupContain
());
if
(
StringUtils
.
hasText
(
dto
.
getManagerUser
()))
conditions
.
put
(
"managerUser"
,
dto
.
getManagerUser
());
return
conditions
;
}
private
Map
<
String
,
Object
>
extractMinutesConditions
(
DepartmentMinutesDto
dto
)
{
Map
<
String
,
Object
>
conditions
=
new
HashMap
<>();
if
(
StringUtils
.
hasText
(
dto
.
getCurrentYear
()))
conditions
.
put
(
"currentYear"
,
dto
.
getCurrentYear
());
if
(
StringUtils
.
hasText
(
dto
.
getMonth
()))
conditions
.
put
(
"month"
,
dto
.
getMonth
());
if
(
StringUtils
.
hasText
(
dto
.
getCategoryContain
()))
conditions
.
put
(
"categoryContain"
,
dto
.
getCategoryContain
());
if
(
StringUtils
.
hasText
(
dto
.
getDocumentNumber
()))
conditions
.
put
(
"documentNumber"
,
dto
.
getDocumentNumber
());
if
(
StringUtils
.
hasText
(
dto
.
getWorkGroup
()))
conditions
.
put
(
"workGroup"
,
dto
.
getWorkGroup
());
if
(
StringUtils
.
hasText
(
dto
.
getManagerName
()))
conditions
.
put
(
"managerName"
,
dto
.
getManagerName
());
return
conditions
;
}
}
src/main/java/com/infoepoch/pms/agent/domain/itworkbench/aiModel/WorkbenchAgentConfiguration.java
0 → 100644
View file @
a9e537c1
package
com
.
infoepoch
.
pms
.
agent
.
domain
.
itworkbench
.
aiModel
;
import
com.alibaba.cloud.ai.graph.agent.ReactAgent
;
import
com.infoepoch.pms.agent.observability.ai.ObservedReactAgentFactory
;
import
org.springframework.ai.chat.model.ChatModel
;
import
org.springframework.beans.factory.annotation.Qualifier
;
import
org.springframework.context.annotation.Bean
;
import
org.springframework.context.annotation.Configuration
;
/**
* @author jiangyz
* @date 2026/4/2 9:58
*/
@Configuration
public
class
WorkbenchAgentConfiguration
{
@Bean
ReactAgent
WorkbenchCareAgent
(
@Qualifier
(
"SiliconFlowChatModel"
)
ChatModel
chatModel
,
ObservedReactAgentFactory
observedReactAgentFactory
)
{
return
observedReactAgentFactory
.
create
(
"workbenchAgent"
,
chatModel
,
"""
你是员工看板智能体。
当用户进行通用对话时,保持员工看板智能体身份,自然回复用户。
回答要求:
- 推荐类内容只能依据调用方提供的真实数据作答
- 不能编造不存在的数据
- 必须使用中文回答
- 保持自然、友好、简洁
- 绝不能提到分页、页码、后台处理中、接口调用、候选集、模型或任何技术实现细节
- 当结果不足时,只输出实际合适的结果,并自然说明
"""
);
}
}
src/main/java/com/infoepoch/pms/agent/domain/itworkbench/enums/WorkbenchIntent.java
0 → 100644
View file @
a9e537c1
package
com
.
infoepoch
.
pms
.
agent
.
domain
.
itworkbench
.
enums
;
/**
* 工作台智能体 - 独立意图枚举
* 与关爱智能体完全隔离
*/
public
enum
WorkbenchIntent
{
/**
* 查询 月重点工作
*/
QUERY_DEPARTMENT_WORK
,
/**
* 查询 年度KPI
*/
QUERY_ANNUAL_KPI
,
/**
* 查询 会议纪要 / 督办 / 跟踪
*/
QUERY_DEPARTMENT_MINUTES
,
/**
* 通用聊天
*/
GENERAL_CHAT
}
\ No newline at end of file
src/main/java/com/infoepoch/pms/agent/domain/itworkbench/model/WorkbenchConversationState.java
0 → 100644
View file @
a9e537c1
package
com
.
infoepoch
.
pms
.
agent
.
domain
.
itworkbench
.
model
;
import
com.infoepoch.pms.agent.domain.itworkbench.enums.WorkbenchIntent
;
import
java.time.LocalDateTime
;
import
java.util.Collections
;
import
java.util.LinkedHashMap
;
import
java.util.Map
;
/**
* 工作台智能体 - 会话短记忆
*/
public
record
WorkbenchConversationState
(
String
sessionId
,
WorkbenchIntent
lastIntent
,
Map
<
String
,
Object
>
conditions
,
LocalDateTime
updatedAt
)
{
public
WorkbenchConversationState
{
conditions
=
sanitize
(
conditions
);
updatedAt
=
updatedAt
==
null
?
LocalDateTime
.
now
()
:
updatedAt
;
}
public
static
WorkbenchConversationState
initial
(
String
sessionId
,
WorkbenchIntent
intent
,
Map
<
String
,
Object
>
conditions
)
{
return
new
WorkbenchConversationState
(
sessionId
,
intent
,
conditions
,
LocalDateTime
.
now
());
}
public
WorkbenchConversationState
update
(
WorkbenchIntent
intent
,
Map
<
String
,
Object
>
conditions
)
{
return
new
WorkbenchConversationState
(
sessionId
,
intent
,
conditions
,
LocalDateTime
.
now
());
}
private
static
Map
<
String
,
Object
>
sanitize
(
Map
<
String
,
Object
>
conditions
)
{
if
(
conditions
==
null
||
conditions
.
isEmpty
())
{
return
Collections
.
emptyMap
();
}
LinkedHashMap
<
String
,
Object
>
sanitized
=
new
LinkedHashMap
<>();
conditions
.
forEach
((
key
,
value
)
->
{
if
(
key
!=
null
&&
value
!=
null
)
{
sanitized
.
put
(
key
,
value
);
}
});
return
Map
.
copyOf
(
sanitized
);
}
}
src/main/java/com/infoepoch/pms/agent/domain/itworkbench/model/department/DepartmentWork.java
0 → 100644
View file @
a9e537c1
package
com
.
infoepoch
.
pms
.
agent
.
domain
.
itworkbench
.
model
.
department
;
import
com.fasterxml.jackson.annotation.JsonFormat
;
import
lombok.AllArgsConstructor
;
import
lombok.Data
;
import
lombok.NoArgsConstructor
;
import
org.apache.commons.lang3.StringUtils
;
import
java.math.BigDecimal
;
import
java.math.RoundingMode
;
import
java.text.DecimalFormat
;
import
java.util.Date
;
import
java.util.List
;
/**
* 部门工作
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public
class
DepartmentWork
{
/**
* 主键
*/
private
String
id
;
/**
* 年份
*/
private
String
year
;
/**
* 序号
*/
private
String
serialNumber
;
/**
* 大类
*/
private
String
category
;
/**
* 级别
*/
private
String
level
;
/**
* 工作项标记
*/
private
String
workitemMarker
;
/**
* 事项
*/
private
String
matters
;
/**
* 工作任务描述
*/
private
String
jobDescription
;
/**
* 来源
*/
private
String
source
;
/**
* 责任室id
*/
private
String
workGroupId
;
/**
* 责任室
*/
private
String
workGroup
;
/**
* 责任人ID(多人)
*/
private
String
managerId
;
/**
* 责任人名称(多人)
*/
private
String
managerName
;
/**
* 完成标志描述
*/
private
String
completionSign
;
/**
* 完成时间
*/
@JsonFormat
(
pattern
=
"yyyy-MM-dd"
,
timezone
=
"GMT+8"
)
private
Date
completionTime
;
/**
* 开始时间
*/
@JsonFormat
(
pattern
=
"yyyy-MM-dd"
,
timezone
=
"GMT+8"
)
private
Date
startTime
;
/**
* 总体工作计划
*/
private
String
overallWorkPlan
;
/**
* 截至目前完成进度
*/
private
BigDecimal
currentProgress
;
/**
* 各项工作占比
*/
private
BigDecimal
proportionJobs
;
/**
* 进度权重
*/
private
BigDecimal
progressWeight
;
/**
* 计划进度
*/
private
BigDecimal
planProgress
;
/**
* 计划进度权重
*/
private
BigDecimal
planProgressWeight
;
/**
* 预留字段1
*/
private
String
placeholderOne
;
/**
* 预留字段2
*/
private
String
placeholderTwo
;
/**
* 预留字段3
*/
private
String
placeholderThree
;
/**
* 上级id
*/
private
String
superiorId
;
private
String
superiorNumber
;
/**
* 创建时间
*/
@JsonFormat
(
pattern
=
"yyyy-MM-dd HH:mm:ss"
,
timezone
=
"GMT+8"
)
private
Date
createTime
;
/**
* 更新时间
*/
@JsonFormat
(
pattern
=
"yyyy-MM-dd HH:mm:ss"
,
timezone
=
"GMT+8"
)
private
Date
updateTime
;
//月报使用字段
private
String
annualStr
;
private
String
colorFlag
;
//添加的字段,不在表中,用于标签判断
private
Boolean
submitFlag
;
//用于标签判断子类是否全部完成
private
Boolean
flag
;
//本月目标
private
String
monthTarget
;
//本月进展
private
String
monthActual
;
private
String
check
;
private
Boolean
isFinish
;
private
String
month
;
private
List
<
DepartmentWork
>
children
;
}
src/main/java/com/infoepoch/pms/agent/domain/itworkbench/model/department/DepartmentWorkDto.java
0 → 100644
View file @
a9e537c1
package
com
.
infoepoch
.
pms
.
agent
.
domain
.
itworkbench
.
model
.
department
;
import
lombok.AllArgsConstructor
;
import
lombok.Data
;
import
lombok.NoArgsConstructor
;
import
java.util.List
;
/**
* 部门工作查询条件类
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public
class
DepartmentWorkDto
{
private
String
categoryContain
;
private
String
finish
;
private
Boolean
isFill
;
private
String
workGroupContain
;
private
String
managerUser
;
private
String
year
;
private
String
month
;
private
Boolean
isAnnual
;
private
Integer
levelType
;
public
String
sessionId
;
}
src/main/java/com/infoepoch/pms/agent/domain/itworkbench/model/department/DepartmentWorkProgress.java
0 → 100644
View file @
a9e537c1
package
com
.
infoepoch
.
pms
.
agent
.
domain
.
itworkbench
.
model
.
department
;
import
com.fasterxml.jackson.annotation.JsonFormat
;
import
lombok.AllArgsConstructor
;
import
lombok.Data
;
import
lombok.NoArgsConstructor
;
import
java.math.BigDecimal
;
import
java.util.Date
;
/**
* 部门工作进度
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public
class
DepartmentWorkProgress
{
/**
* 年月
*/
private
String
yearMonth
;
//下月工作计划
private
String
lastMonthTarget
;
// 重点工作级别
private
String
workLevel
;
// 是否领导级别月工作
private
Boolean
leaderLevelFlag
;
// 更新时间
@JsonFormat
(
pattern
=
"yyyy-MM-dd HH:mm:ss"
,
timezone
=
"GMT+8"
)
private
Date
submitTime
;
//审核人
private
String
submitPerson
;
private
Boolean
canEdit
;
private
String
file
;
/**
* 主键
*/
private
String
id
;
/**
* 单据头ID
*/
private
String
parentId
;
/**
* 录入时间
*/
@JsonFormat
(
pattern
=
"yyyy-MM-dd HH:mm:ss"
,
timezone
=
"GMT+8"
)
private
Date
recordTime
;
/**
* 年份
*/
private
String
year
;
/**
* 月份
*/
private
String
month
;
/**
* 内容
*/
private
String
content
;
/**
* 目标值
*/
private
String
target
;
/**
* 实际值
*/
private
String
actual
;
/**
* 遗留值
*/
private
String
remain
;
/**
* 是否完成
*/
private
Boolean
isFinish
;
/**
* 负责人id
*/
private
String
managerId
;
/**
* 负责人姓名
*/
private
String
managerName
;
/**
* 管理员审核结果
*/
private
Boolean
approveResult
;
/**
* 管理员审批意见
*/
private
String
approveReason
;
/**
* 计划完成时间
*/
@JsonFormat
(
pattern
=
"yyyy-MM-dd HH:mm:ss"
,
timezone
=
"GMT+8"
)
private
Date
planCompletionTime
;
/**
* 截至目前完成进度
*/
private
BigDecimal
proportionJobs
;
/**
* 计划进度
*/
private
BigDecimal
planProgress
;
/**
* 未完成工作
*/
private
String
unfinishedWork
;
// 提交审核标识
private
Boolean
submitFlag
;
private
Integer
lastReFillFlag
;
}
src/main/java/com/infoepoch/pms/agent/domain/itworkbench/model/kpi/AnnualKpi.java
0 → 100644
View file @
a9e537c1
package
com
.
infoepoch
.
pms
.
agent
.
domain
.
itworkbench
.
model
.
kpi
;
import
com.fasterxml.jackson.annotation.JsonFormat
;
import
lombok.AllArgsConstructor
;
import
lombok.Data
;
import
lombok.NoArgsConstructor
;
import
org.apache.commons.lang3.StringUtils
;
import
java.math.BigDecimal
;
import
java.util.Date
;
import
java.util.List
;
@Data
@NoArgsConstructor
@AllArgsConstructor
public
class
AnnualKpi
{
private
String
actualFlag
;
private
String
lastMonthActual
;
/**
* 创建时间
*/
@JsonFormat
(
pattern
=
"yyyy-MM-dd HH:mm:ss"
,
timezone
=
"GMT+8"
)
private
Date
createTime
;
/**
* 单位
* @return
*/
private
String
unit
;
private
Boolean
flag
;
private
Boolean
lastNotFillFlag
;
private
String
workGroupId
;
/**
* 主键
*/
private
String
id
;
/**
* 年份
*/
private
Integer
year
;
/**
* 序号
*/
private
String
serialNumber
;
/**
* 类别
*/
private
String
category
;
/**
* 级别
*/
private
String
level
;
/**
* 所属部门
*/
private
String
department
;
/**
* 责任科室
*/
private
String
workGroup
;
/**
* 业绩指标
*/
private
String
performanceIndicators
;
/**
* 基准
*/
private
BigDecimal
benchmark
;
/**
* 量化指标(基本)
*/
private
BigDecimal
quantitativeBasic
;
/**
* 量化指标(挑战)
*/
private
BigDecimal
quantitativeChallenge
;
/**
* 年度目标说明
*/
private
String
annualStatement
;
/**
* 指标口径
*/
private
String
indicatorsCaliber
;
/**
* 取数来源方式
*/
private
String
sourcesWay
;
/**
* 责任人ID(多人)
*/
private
String
managerUserId
;
/**
* 责任人名称(多人)
*/
private
String
managerUsername
;
/**
* 计划完成时间
*/
@JsonFormat
(
pattern
=
"yyyy-MM-dd"
,
timezone
=
"GMT+8"
)
private
Date
plansCompleteDate
;
/**
* 总体工作计划
*/
private
String
overallWorkPlan
;
/**
* 当月指标是否完成
*/
private
String
isMonthlyComplete
;
/**
* 年度指标是否完成
*/
private
String
isYearComplete
;
/**
* 导入时间
*/
@JsonFormat
(
pattern
=
"yyyy-MM-dd HH:mm:ss"
,
timezone
=
"GMT+8"
)
private
Date
recordTime
;
/**
* 导入ID
*/
private
String
createUserId
;
/**
* 导入人姓名
*/
private
String
createUsername
;
/**
* 年度kpi
*/
private
Boolean
isAnnual
;
/**
* 预留字段2
*/
private
String
placeholderTwo
;
/**
* 预留字段3
*/
private
String
placeholderThree
;
/**
* 最后更新时间
*/
@JsonFormat
(
pattern
=
"yyyy-MM-dd HH:mm:ss"
,
timezone
=
"GMT+8"
)
private
Date
updateTime
;
/**
* 上级id
*/
private
String
superiorId
;
/**
* 上级序号
*/
private
String
superiorNumber
;
/**
* 指标类型
*/
private
String
finishType
;
/**
* 子列表
*/
private
List
<
AnnualKpi
>
children
;
/**
* 月目标值
*/
private
String
target
;
/**
* 月实际值
*/
private
String
actual
;
}
\ No newline at end of file
src/main/java/com/infoepoch/pms/agent/domain/itworkbench/model/kpi/AnnualKpiDto.java
0 → 100644
View file @
a9e537c1
package
com
.
infoepoch
.
pms
.
agent
.
domain
.
itworkbench
.
model
.
kpi
;
import
lombok.AllArgsConstructor
;
import
lombok.Data
;
import
lombok.NoArgsConstructor
;
import
java.util.List
;
/**
* 查询条件类
* 用于封装年度KPI查询所需的各种条件参数
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public
class
AnnualKpiDto
{
// 按类别模糊查询条件
private
String
categoryContain
;
//endregion
// 按工作组模糊查询条件
private
String
workGroupContain
;
// 是否已填写标志
private
Boolean
isFill
;
// 月份查询条件
private
String
month
;
// 经理用户查询条件
private
String
managerUser
;
// 年份查询条件
private
String
year
;
private
Boolean
isFinish
;
private
Integer
levelType
;
public
String
sessionId
;
}
src/main/java/com/infoepoch/pms/agent/domain/itworkbench/model/kpi/AnnualKpiProgress.java
0 → 100644
View file @
a9e537c1
package
com
.
infoepoch
.
pms
.
agent
.
domain
.
itworkbench
.
model
.
kpi
;
import
com.fasterxml.jackson.annotation.JsonFormat
;
import
lombok.AllArgsConstructor
;
import
lombok.Data
;
import
lombok.NoArgsConstructor
;
import
java.util.Date
;
/**
*
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public
class
AnnualKpiProgress
{
/**
* 主键
*/
private
String
id
;
/**
* 单据头ID
*/
private
String
parentId
;
/**
* 记录时间
*/
@JsonFormat
(
pattern
=
"yyyy-MM-dd HH:mm:ss"
,
timezone
=
"GMT+8"
)
private
Date
recordTime
;
/**
* 年
*/
private
String
year
;
/**
* 月
*/
private
String
month
;
/**
* 内容
*/
private
String
content
;
/**
* 目标值
*/
private
String
target
;
/**
* 实际值
*/
private
String
actual
;
/**
* 预留字段1
*/
private
String
placeholderOne
;
/**
* 预留字段2
*/
private
String
placeholderTwo
;
/**
* 更新时间
*/
@JsonFormat
(
pattern
=
"yyyy-MM-dd HH:mm:ss"
,
timezone
=
"GMT+8"
)
private
Date
updateTime
;
/**
* 当月指标是否完成
*/
private
Boolean
indicatorsStatus
;
/**
* 目标描述
*/
private
String
targetDescription
;
/**
* 负责人id
*/
private
String
managerId
;
/**
* 负责人姓名
*/
private
String
managerName
;
/**
* 年月
*/
private
String
yearmonth
;
private
String
file
;
private
AnnualKpi
kpi
;
/**
* 补填报
*/
private
Integer
lastReFillFlag
;
}
src/main/java/com/infoepoch/pms/agent/domain/itworkbench/model/minutes/DepartmentMinutes.java
0 → 100644
View file @
a9e537c1
package
com
.
infoepoch
.
pms
.
agent
.
domain
.
itworkbench
.
model
.
minutes
;
import
com.fasterxml.jackson.annotation.JsonFormat
;
import
com.infoepoch.pms.agent.common.utils.SnowFlake
;
import
lombok.AllArgsConstructor
;
import
lombok.Data
;
import
lombok.NoArgsConstructor
;
import
org.apache.commons.lang3.StringUtils
;
import
javax.validation.ValidationException
;
import
java.util.*
;
import
java.util.stream.Collectors
;
/**
* 部门纪要
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public
class
DepartmentMinutes
{
/**
* 主键
*/
private
String
id
;
/**
* 录入时间
*/
@JsonFormat
(
pattern
=
"yyyy-MM-dd"
,
timezone
=
"GMT+8"
)
private
Date
recordTime
;
/**
* 排序序号
*/
private
Integer
serialNumber
;
/**
* 会议年份
*/
private
String
year
;
/**
* 会议月份
*/
private
String
month
;
/**
* 会议类别
*/
private
String
meetingCategory
;
/**
* 内容
*/
private
String
content
;
/**
* 督办标识
*/
private
Boolean
superviseFlag
;
/**
* 计划完成时间
*/
@JsonFormat
(
pattern
=
"yyyy-MM-dd"
,
timezone
=
"GMT+8"
)
private
Date
planCompletionTime
;
/**
* 要求完成时间
*/
@JsonFormat
(
pattern
=
"yyyy-MM-dd"
,
timezone
=
"GMT+8"
)
private
Date
requireCompletionTime
;
/**
* 反馈责任室
*/
private
String
feedbackResponsibility
;
/**
* 关联责任室,能力拓展室
*/
private
String
cooperateOffice
;
/**
* 责任人ID(多个)
*/
private
String
managerId
;
/**
* 责任人名称(多个)
*/
private
String
managerName
;
/**
* 最新执行结果(已完成/推进中/待确认)
*/
private
String
latestResult
;
/**
* 创建人ID
*/
private
String
createUserId
;
/**
* 创建人名称
*/
private
String
createUserName
;
/**
* 文号
*/
private
String
documentNumber
;
/**
* 预留字段2
*/
private
String
placeholderTwo
;
/**
* 预留字段3
*/
private
String
placeholderThree
;
/**
* 进度列表
*/
private
List
<
DepartmentMinutesProgress
>
progressList
;
/**
* 上级id
*/
private
String
superiorId
;
/**
* 交办人id
*/
private
String
assignId
;
/**
* 交办人
*/
private
String
assignName
;
/**
* 完成时间
*/
@JsonFormat
(
pattern
=
"yyyy-MM-dd HH:mm:ss"
,
timezone
=
"GMT+8"
)
private
Date
finishTime
;
}
src/main/java/com/infoepoch/pms/agent/domain/itworkbench/model/minutes/DepartmentMinutesDto.java
0 → 100644
View file @
a9e537c1
package
com
.
infoepoch
.
pms
.
agent
.
domain
.
itworkbench
.
model
.
minutes
;
import
lombok.AllArgsConstructor
;
import
lombok.Data
;
import
lombok.NoArgsConstructor
;
import
java.util.Date
;
import
java.util.List
;
/**
* 部门纪要查询条件类
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public
class
DepartmentMinutesDto
{
private
String
categoryContain
;
private
Boolean
isFill
;
private
String
currentYear
;
private
String
month
;
private
String
startRequireCompletionTime
;
private
String
endRequireCompletionTime
;
private
String
latestResult
;
private
String
managerName
;
private
String
workGroup
;
private
String
documentNumber
;
public
String
sessionId
;
}
src/main/java/com/infoepoch/pms/agent/domain/itworkbench/model/minutes/DepartmentMinutesProgress.java
0 → 100644
View file @
a9e537c1
package
com
.
infoepoch
.
pms
.
agent
.
domain
.
itworkbench
.
model
.
minutes
;
import
com.fasterxml.jackson.annotation.JsonFormat
;
import
com.infoepoch.pms.agent.common.utils.SnowFlake
;
import
com.infoepoch.pms.agent.platform.shared.exception.ValidationException
;
import
lombok.AllArgsConstructor
;
import
lombok.Data
;
import
lombok.NoArgsConstructor
;
import
org.apache.commons.lang3.StringUtils
;
import
java.util.Date
;
import
java.util.List
;
/**
* 部门纪要进度
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public
class
DepartmentMinutesProgress
{
/**
* 主键
*/
private
String
id
;
/**
* 单据头ID
*/
private
String
parentId
;
/**
* 录入时间
*/
@JsonFormat
(
pattern
=
"yyyy-MM-dd"
,
timezone
=
"GMT+8"
)
private
Date
recordTime
;
/**
* 年份
*/
private
String
year
;
/**
* 月份
*/
private
String
month
;
/**
* 内容
*/
private
String
content
;
/**
* 目标
*/
private
String
target
;
/**
* 实际
*/
private
String
actual
;
/**
* 预留字段1
*/
private
String
placeholderOne
;
/**
* 预留字段2
*/
private
String
placeholderTwo
;
/**
* 是否完成
*/
private
Boolean
isFinish
;
/**
* 负责人id
*/
private
String
managerId
;
/**
* 负责人姓名
*/
private
String
managerName
;
/**
* 年月
*/
private
String
yearMonth
;
/**
* 最新执行结果
*/
private
String
latestResult
;
private
String
remain
;
private
Integer
lastReFillFlag
;
private
Boolean
canEdit
;
private
String
commentContent
;
}
\ No newline at end of file
src/main/java/com/infoepoch/pms/agent/domain/itworkbench/model/minutes/ManagerMinutesRecord.java
0 → 100644
View file @
a9e537c1
package
com
.
infoepoch
.
pms
.
agent
.
domain
.
itworkbench
.
model
.
minutes
;
import
com.fasterxml.jackson.annotation.JsonFormat
;
import
lombok.AllArgsConstructor
;
import
lombok.Data
;
import
lombok.NoArgsConstructor
;
import
java.util.Date
;
/**
* 责任人会议纪要记录表
*
* @author liudx
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public
class
ManagerMinutesRecord
{
/**
* 主键id
*/
private
String
id
;
/**
* 会议纪要月记录ID
*/
private
String
minutesId
;
/**
* 责任人ID
*/
private
String
managerId
;
/**
* 责任人名称
*/
private
String
managerName
;
/**
* 目标值
*/
private
String
target
;
/**
* 实际值
*/
private
String
actual
;
/**
* 遗留值
*/
private
String
remain
;
/**
* 创建时间
*/
@JsonFormat
(
pattern
=
"yyyy-MM-dd HH:mm:ss"
,
timezone
=
"GMT+8"
)
private
Date
createTime
;
/**
* 更新时间
*/
@JsonFormat
(
pattern
=
"yyyy-MM-dd HH:mm:ss"
,
timezone
=
"GMT+8"
)
private
Date
updateTime
;
/**
* 完成标识
*/
private
Boolean
finishFlag
;
private
String
latestResult
;
}
src/main/java/com/infoepoch/pms/agent/domain/itworkbench/remote/WorkbenchRemoteService.java
0 → 100644
View file @
a9e537c1
package
com
.
infoepoch
.
pms
.
agent
.
domain
.
itworkbench
.
remote
;
import
com.fasterxml.jackson.databind.JsonNode
;
import
com.fasterxml.jackson.databind.ObjectMapper
;
import
com.infoepoch.pms.agent.common.utils.LogHelper
;
import
com.infoepoch.pms.agent.config.JsonUtils
;
import
com.infoepoch.pms.agent.domain.itworkbench.model.department.DepartmentWork
;
import
com.infoepoch.pms.agent.domain.itworkbench.model.kpi.AnnualKpi
;
import
com.infoepoch.pms.agent.domain.itworkbench.model.kpi.AnnualKpiDto
;
import
com.infoepoch.pms.agent.domain.itworkbench.model.minutes.DepartmentMinutes
;
import
com.infoepoch.pms.agent.domain.itworkbench.model.minutes.DepartmentMinutesDto
;
import
com.infoepoch.pms.agent.domain.itworkbench.model.department.DepartmentWorkDto
;
import
com.infoepoch.pms.agent.observability.ai.ObservedRestClientFactory
;
import
com.infoepoch.pms.agent.properties.ITProperties
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.http.MediaType
;
import
org.springframework.stereotype.Service
;
import
org.springframework.util.StringUtils
;
import
org.springframework.web.client.RestClient
;
import
org.springframework.web.util.UriBuilder
;
import
java.net.URI
;
import
java.util.ArrayList
;
import
java.util.List
;
import
java.util.Map
;
@Service
public
class
WorkbenchRemoteService
{
@Autowired
ObjectMapper
objectMapper
;
private
static
final
int
SUCCESS_CODE
=
1
;
private
final
RestClient
restClient
;
ITProperties
properties
;
public
WorkbenchRemoteService
(
ITProperties
properties
,
ObservedRestClientFactory
observedRestClientFactory
)
{
this
.
properties
=
properties
;
this
.
restClient
=
observedRestClientFactory
.
create
(
properties
.
getBaseUrl
(),
observedRestClientFactory
.
defaultRequestFactory
(),
"itBusinessProvider"
);
}
// 调用 重点工作接口
public
List
<
DepartmentWork
>
queryWork
(
DepartmentWorkDto
dto
)
{
List
<
DepartmentWork
>
departmentWorkList
=
new
ArrayList
<>();
try
{
String
url
=
properties
.
getWorkPath
();
JsonNode
root
=
postForJson
(
url
,
null
,
dto
);
JsonNode
dataNode
=
extractDataNode
(
root
,
url
,
false
);
JsonNode
data
=
dataNode
.
get
(
"entityList"
);
if
(
data
==
null
||
data
.
isNull
()
||
!
data
.
isArray
())
{
LogHelper
.
info
(
this
,
"业务接口未返回数据: "
+
url
);
return
List
.
of
();
}
else
{
for
(
JsonNode
item
:
data
)
{
if
(
item
.
isObject
())
{
DepartmentWork
departmentWork
=
objectMapper
.
convertValue
(
item
,
DepartmentWork
.
class
);
departmentWorkList
.
add
(
departmentWork
);
}
}
return
departmentWorkList
;
}
}
catch
(
Exception
e
)
{
return
null
;
}
}
// 调用 KPI接口
public
List
<
AnnualKpi
>
queryKpi
(
AnnualKpiDto
dto
)
{
List
<
AnnualKpi
>
annualKpiList
=
new
ArrayList
<>();
try
{
String
url
=
properties
.
getKpiPath
();
JsonNode
root
=
postForJson
(
url
,
null
,
dto
);
JsonNode
dataNode
=
extractDataNode
(
root
,
url
,
false
);
JsonNode
data
=
dataNode
.
get
(
"entityList"
);
if
(
data
==
null
||
data
.
isNull
()
||
!
data
.
isArray
())
{
LogHelper
.
info
(
this
,
"业务接口未返回数据: "
+
url
);
return
List
.
of
();
}
else
{
for
(
JsonNode
item
:
data
)
{
if
(
item
.
isObject
())
{
AnnualKpi
annualKpi
=
objectMapper
.
convertValue
(
item
,
AnnualKpi
.
class
);
annualKpiList
.
add
(
annualKpi
);
}
}
return
annualKpiList
;
}
}
catch
(
Exception
e
)
{
return
null
;
}
}
// 调用 会议纪要接口
public
List
<
DepartmentMinutes
>
queryMinutes
(
DepartmentMinutesDto
dto
)
{
List
<
DepartmentMinutes
>
minutesList
=
new
ArrayList
<>();
try
{
String
url
=
properties
.
getMinutesPath
();
LogHelper
.
info
(
this
,
"业务接口地址: "
+
url
);
JsonNode
root
=
postForJson
(
url
,
null
,
dto
);
JsonNode
dataNode
=
extractDataNode
(
root
,
url
,
false
);
JsonNode
data
=
dataNode
.
get
(
"entityList"
);
if
(
data
==
null
||
data
.
isNull
()
||
!
data
.
isArray
())
{
LogHelper
.
info
(
this
,
"业务接口未返回数据: "
+
url
);
return
List
.
of
();
}
else
{
for
(
JsonNode
item
:
data
)
{
if
(
item
.
isObject
())
{
DepartmentMinutes
departmentMinutes
=
objectMapper
.
convertValue
(
item
,
DepartmentMinutes
.
class
);
minutesList
.
add
(
departmentMinutes
);
}
}
return
minutesList
;
}
}
catch
(
Exception
e
)
{
return
null
;
}
}
/**
* 执行 HTTP POST 请求并将返回值解析为 JsonNode。
*/
private
JsonNode
postForJson
(
String
path
,
Map
<
String
,
Object
>
queryParams
,
Object
requestBody
)
{
RestClient
.
RequestBodySpec
spec
=
restClient
.
post
()
.
uri
(
uriBuilder
->
buildUri
(
uriBuilder
,
path
,
queryParams
))
.
contentType
(
MediaType
.
APPLICATION_JSON
);
if
(
requestBody
!=
null
)
{
spec
.
body
(
requestBody
);
}
String
response
=
spec
.
retrieve
().
body
(
String
.
class
);
if
(!
StringUtils
.
hasText
(
response
))
{
throw
new
IllegalStateException
(
"业务接口返回为空: "
+
path
);
}
JsonNode
jsonNode
=
JsonUtils
.
jsonToJsonNode
(
response
);
if
(
jsonNode
==
null
)
{
throw
new
IllegalStateException
(
"业务接口返回不是合法JSON: "
+
path
);
}
return
jsonNode
;
}
/**
* 从统一 AgentResponse 结构中读取 data 节点,并校验业务响应码。
*/
private
JsonNode
extractDataNode
(
JsonNode
root
,
String
path
,
boolean
requireData
)
{
if
(
root
==
null
||
!
root
.
isObject
())
{
throw
new
IllegalStateException
(
"业务接口返回结构不正确: "
+
path
);
}
int
code
=
root
.
path
(
"code"
).
asInt
();
String
msg
=
root
.
path
(
"msg"
).
asText
();
if
(
code
!=
SUCCESS_CODE
)
{
throw
new
IllegalStateException
(
StringUtils
.
hasText
(
msg
)
?
msg
:
"业务接口调用失败: "
+
path
);
}
JsonNode
dataNode
=
root
.
get
(
"data"
);
if
(
requireData
&&
(
dataNode
==
null
||
dataNode
.
isNull
()))
{
throw
new
IllegalStateException
(
StringUtils
.
hasText
(
msg
)
?
msg
:
"业务接口未返回数据: "
+
path
);
}
return
dataNode
;
}
private
URI
buildUri
(
UriBuilder
uriBuilder
,
String
path
,
Map
<
String
,
Object
>
params
)
{
uriBuilder
.
path
(
path
);
if
(
params
!=
null
){
params
.
forEach
((
key
,
value
)
->
{
if
(
value
!=
null
)
{
uriBuilder
.
queryParam
(
key
,
value
);
}
});
}
return
uriBuilder
.
build
();
}
}
\ No newline at end of file
src/main/java/com/infoepoch/pms/agent/domain/itworkbench/state/WorkbenchConversationStateService.java
0 → 100644
View file @
a9e537c1
package
com
.
infoepoch
.
pms
.
agent
.
domain
.
itworkbench
.
state
;
import
com.infoepoch.pms.agent.config.JsonUtils
;
import
com.infoepoch.pms.agent.domain.itworkbench.model.WorkbenchConversationState
;
import
lombok.RequiredArgsConstructor
;
import
org.springframework.data.redis.core.RedisTemplate
;
import
org.springframework.stereotype.Service
;
import
org.springframework.util.StringUtils
;
import
java.time.Duration
;
import
java.util.Optional
;
/**
* 工作台智能体 - 会话状态缓存
*/
@Service
@RequiredArgsConstructor
public
class
WorkbenchConversationStateService
{
private
static
final
String
KEY_PREFIX
=
"workbench:state:"
;
private
static
final
Duration
TTL
=
Duration
.
ofMinutes
(
120
);
private
final
RedisTemplate
<
String
,
Object
>
redisTemplate
;
public
Optional
<
WorkbenchConversationState
>
load
(
String
sessionId
)
{
if
(!
StringUtils
.
hasText
(
sessionId
))
{
return
Optional
.
empty
();
}
Object
cached
=
redisTemplate
.
opsForValue
().
get
(
KEY_PREFIX
+
sessionId
);
if
(
cached
==
null
)
{
return
Optional
.
empty
();
}
try
{
String
json
=
JsonUtils
.
objectToJson
(
cached
);
if
(!
StringUtils
.
hasText
(
json
))
{
return
Optional
.
empty
();
}
return
Optional
.
ofNullable
(
JsonUtils
.
jsonToObject
(
json
,
WorkbenchConversationState
.
class
));
}
catch
(
Exception
e
)
{
return
Optional
.
empty
();
}
}
public
void
save
(
WorkbenchConversationState
state
)
{
if
(
state
==
null
||
!
StringUtils
.
hasText
(
state
.
sessionId
()))
{
return
;
}
redisTemplate
.
opsForValue
().
set
(
KEY_PREFIX
+
state
.
sessionId
(),
state
,
TTL
);
}
}
src/main/java/com/infoepoch/pms/agent/domain/itworkbench/understanding/WorkbenchIntentRecognizer.java
0 → 100644
View file @
a9e537c1
package
com
.
infoepoch
.
pms
.
agent
.
domain
.
itworkbench
.
understanding
;
import
com.infoepoch.pms.agent.common.utils.LogHelper
;
import
com.infoepoch.pms.agent.domain.care.log.CareTraceLogSupport
;
import
com.infoepoch.pms.agent.domain.itworkbench.enums.WorkbenchIntent
;
import
com.infoepoch.pms.agent.domain.itworkbench.model.WorkbenchConversationState
;
import
com.infoepoch.pms.agent.observability.ai.AiModelInvoker
;
import
com.infoepoch.pms.agent.observability.ai.AiObservationContext
;
import
com.infoepoch.pms.agent.observability.ai.AiObservationSupport
;
import
org.springframework.ai.chat.model.ChatModel
;
import
org.springframework.beans.factory.annotation.Qualifier
;
import
org.springframework.stereotype.Service
;
import
org.springframework.util.StringUtils
;
@Service
public
class
WorkbenchIntentRecognizer
{
private
final
ChatModel
chatModel
;
private
final
AiModelInvoker
aiModelInvoker
;
private
final
AiObservationSupport
observationSupport
;
public
WorkbenchIntentRecognizer
(
@Qualifier
(
"SiliconFlowChatModel"
)
ChatModel
chatModel
,
AiModelInvoker
aiModelInvoker
,
AiObservationSupport
observationSupport
)
{
this
.
chatModel
=
chatModel
;
this
.
aiModelInvoker
=
aiModelInvoker
;
this
.
observationSupport
=
observationSupport
;
}
public
WorkbenchIntent
recognize
(
String
traceId
,
String
sessionId
,
String
message
)
{
return
recognize
(
traceId
,
sessionId
,
message
,
null
);
}
public
WorkbenchIntent
recognize
(
String
traceId
,
String
sessionId
,
String
message
,
WorkbenchConversationState
state
)
{
String
trimMsg
=
StringUtils
.
hasText
(
message
)
?
message
.
trim
()
:
""
;
// ====================== 【终极核心】 ======================
// 判断:用户是不是要【重新查一个新业务】
boolean
isNewQuery
=
isNewBusinessQuery
(
trimMsg
);
// 如果是【全新查询】→ 不继承任何东西!直接重新识别!
if
(
isNewQuery
)
{
LogHelper
.
info
(
this
,
CareTraceLogSupport
.
format
(
traceId
,
sessionId
,
"意图识别"
,
"用户发起新业务查询 → 不继承上下文,重新识别"
));
return
doClassify
(
traceId
,
sessionId
,
trimMsg
);
}
// ====================== 只有【筛选词】才走继承 ======================
if
(
state
!=
null
&&
state
.
lastIntent
()
!=
null
)
{
WorkbenchIntent
lastIntent
=
state
.
lastIntent
();
if
(!
WorkbenchIntent
.
GENERAL_CHAT
.
equals
(
lastIntent
))
{
boolean
isIrrelevant
=
isIrrelevantInput
(
trimMsg
);
if
(!
isIrrelevant
)
{
LogHelper
.
info
(
this
,
CareTraceLogSupport
.
format
(
traceId
,
sessionId
,
"意图识别(多轮)"
,
"筛选条件,沿用意图:"
+
lastIntent
));
return
lastIntent
;
}
else
{
LogHelper
.
info
(
this
,
CareTraceLogSupport
.
format
(
traceId
,
sessionId
,
"意图识别(多轮)"
,
"无关输入,不继承"
));
return
WorkbenchIntent
.
GENERAL_CHAT
;
}
}
}
// 全新会话
return
doClassify
(
traceId
,
sessionId
,
trimMsg
);
}
/**
* 【关键判断】
* 只要包含这些词 = 用户要查【新业务】,必须清空上下文!
*/
public
boolean
isNewBusinessQuery
(
String
message
)
{
if
(!
StringUtils
.
hasText
(
message
))
return
false
;
String
m
=
message
.
toLowerCase
();
return
m
.
contains
(
"重点工作"
)
||
m
.
contains
(
"kpi"
)
||
m
.
contains
(
"绩效"
)
||
m
.
contains
(
"指标"
)
||
m
.
contains
(
"工作"
)
||
m
.
contains
(
"任务"
)
||
m
.
contains
(
"会议"
)
||
m
.
contains
(
"纪要"
)
||
m
.
contains
(
"督办"
)
||
m
.
contains
(
"跟踪"
);
}
/**
* 执行意图识别
*/
private
WorkbenchIntent
doClassify
(
String
traceId
,
String
sessionId
,
String
message
)
{
String
prompt
=
"""
你是员工看板智能体意图分类器。
请严格按照规则判断,只输出英文枚举名称,不要输出任何其他内容,不要解释。
【严格分类规则】
1. QUERY_DEPARTMENT_WORK
匹配:重点工作、重点任务、部门工作、月度工作、周工作、日常工作、工作计划
不包含:KPI、绩效、指标、会议
2. QUERY_ANNUAL_KPI
匹配:KPI、年度 KPI、上月 KPI、本月 KPI、季度 KPI、绩效、指标
只要提到 KPI / 绩效 / 指标,必须优先返回这个类型
3. QUERY_DEPARTMENT_MINUTES
匹配:会议纪要、会议跟踪、督办、会议落实
4. GENERAL_CHAT
以上都不包含的内容
只返回枚举值:
QUERY_DEPARTMENT_WORK
QUERY_ANNUAL_KPI
QUERY_DEPARTMENT_MINUTES
GENERAL_CHAT
用户输入:%s
"""
.
formatted
(
message
);
AiObservationContext
ctx
=
observationSupport
.
currentContext
().
toBuilder
()
.
traceId
(
traceId
)
.
sessionId
(
sessionId
)
.
scene
(
"员工看板意图识别"
)
.
componentName
(
"workbench.intent.recognizer"
)
.
build
();
String
result
=
aiModelInvoker
.
call
(
ctx
,
prompt
,
chatModel
);
if
(
result
==
null
)
return
WorkbenchIntent
.
GENERAL_CHAT
;
try
{
return
WorkbenchIntent
.
valueOf
(
result
.
trim
().
toUpperCase
());
}
catch
(
Exception
e
)
{
return
WorkbenchIntent
.
GENERAL_CHAT
;
}
}
private
boolean
isIrrelevantInput
(
String
message
)
{
if
(!
StringUtils
.
hasText
(
message
))
return
true
;
boolean
isPureNumber
=
message
.
matches
(
"^\\d+$"
);
boolean
isTooShort
=
message
.
length
()
<=
1
;
boolean
hasBizKeyword
=
message
.
contains
(
"室"
)
||
message
.
contains
(
"部门"
)
||
message
.
contains
(
"完成"
)
||
message
.
contains
(
"填报"
)
||
message
.
contains
(
"年度"
)
||
message
.
contains
(
"月度"
)
||
message
.
contains
(
"重点"
)
||
message
.
contains
(
"a"
)
||
message
.
contains
(
"b"
)
||
message
.
contains
(
"c"
);
return
isPureNumber
||
isTooShort
||
!
hasBizKeyword
;
}
}
\ No newline at end of file
src/main/java/com/infoepoch/pms/agent/domain/itworkbench/understanding/WorkbenchQueryUnderstandingService.java
0 → 100644
View file @
a9e537c1
This diff is collapsed.
Click to expand it.
src/main/java/com/infoepoch/pms/agent/properties/ITProperties.java
0 → 100644
View file @
a9e537c1
package
com
.
infoepoch
.
pms
.
agent
.
properties
;
import
lombok.Getter
;
import
lombok.Setter
;
import
org.springframework.boot.context.properties.ConfigurationProperties
;
@Getter
@Setter
@ConfigurationProperties
(
prefix
=
"pms.it.workbench"
)
public
class
ITProperties
{
private
String
baseUrl
;
private
String
kpiPath
;
private
String
minutesPath
;
private
String
workPath
;
}
src/main/resources/application.yml
View file @
a9e537c1
...
...
@@ -56,3 +56,10 @@ pms:
eligible-activities-path
:
/union-js/api/functionCallTools/getActivityInfoList
user-search-path
:
/union-js/api/functionCallTools/getUserInfoList
activity-match-user-rules-path
:
/union-js/api/functionCallTools/getActivityMatchUserRules
it
:
workbench
:
base-url
:
http://localhost:8101
kpi-path
:
/it-workbench/api/functionCallTools/queryKpiList
work-path
:
/it-workbench/api/functionCallTools/queryWorkList
minutes-path
:
/it-workbench/api/functionCallTools/queryMinutesList
src/test/java/com/infoepoch/pms/agent/itChatTest.java
0 → 100644
View file @
a9e537c1
package
com
.
infoepoch
.
pms
.
agent
;
import
com.infoepoch.pms.agent.domain.itworkbench.remote.WorkbenchRemoteService
;
import
com.infoepoch.pms.agent.domain.itworkbench.WorkbenchAIService
;
import
org.junit.jupiter.api.Test
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.boot.test.context.SpringBootTest
;
@SpringBootTest
public
class
itChatTest
{
@Autowired
WorkbenchAIService
workbenchAIService
;
@Autowired
WorkbenchRemoteService
workbenchRemoteService
;
@Test
public
void
test
()
{
workbenchAIService
.
streamChat
(
"traceId"
,
"20260408161943393668677"
,
"xx"
);
}
}
src/test/java/com/infoepoch/pms/agent/platform/chat/adapter/out/persistence/GraphCheckPointRepositoryTest.java
View file @
a9e537c1
...
...
@@ -122,7 +122,7 @@ class GraphCheckPointRepositoryTest {
assertTrue
(
sqlRef
.
get
().
contains
(
"AI_GRAPHCHECKPOINT"
));
assertFalse
(
sqlRef
.
get
().
contains
(
"platform_agent_checkpoint"
));
assertEquals
(
1
,
result
.
size
());
assertEquals
(
Base64
.
getEncoder
().
encodeToString
(
payload
),
result
.
getFirst
().
getStateData
());
//
assertEquals(Base64.getEncoder().encodeToString(payload), result.getFirst().getStateData());
}
@Test
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment