Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
K
konke-ai-alibaba-mcp
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
何金镒
konke-ai-alibaba-mcp
Commits
f81c13d4
Commit
f81c13d4
authored
Sep 15, 2025
by
何金镒
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
思必驰的兜底技能4:重新梳理逻辑
parent
25e14727
Changes
13
Hide whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
486 additions
and
14 deletions
+486
-14
pom.xml
pom.xml
+4
-4
src/main/java/com/ikonke/konkeaialibabamcp/config/AsyncConfig.java
...java/com/ikonke/konkeaialibabamcp/config/AsyncConfig.java
+31
-0
src/main/java/com/ikonke/konkeaialibabamcp/constant/RedisKeys.java
...java/com/ikonke/konkeaialibabamcp/constant/RedisKeys.java
+2
-0
src/main/java/com/ikonke/konkeaialibabamcp/controller/CDCController.java
...om/ikonke/konkeaialibabamcp/controller/CDCController.java
+25
-4
src/main/java/com/ikonke/konkeaialibabamcp/controller/DifyController.java
...m/ikonke/konkeaialibabamcp/controller/DifyController.java
+29
-1
src/main/java/com/ikonke/konkeaialibabamcp/controller/DifyControllerV2.java
...ikonke/konkeaialibabamcp/controller/DifyControllerV2.java
+282
-0
src/main/java/com/ikonke/konkeaialibabamcp/event/listener/DifyChatSteamEventListener.java
...alibabamcp/event/listener/DifyChatSteamEventListener.java
+62
-0
src/main/java/com/ikonke/konkeaialibabamcp/event/mode/DifyChatSteamEvent.java
...onke/konkeaialibabamcp/event/mode/DifyChatSteamEvent.java
+24
-0
src/main/java/com/ikonke/konkeaialibabamcp/mapper/mysql/CDCTokenMapper.java
...ikonke/konkeaialibabamcp/mapper/mysql/CDCTokenMapper.java
+4
-0
src/main/java/com/ikonke/konkeaialibabamcp/service/cdc/CdcHttpUtils.java
...om/ikonke/konkeaialibabamcp/service/cdc/CdcHttpUtils.java
+2
-1
src/main/java/com/ikonke/konkeaialibabamcp/service/mysqlservice/ICDCTokenService.java
...keaialibabamcp/service/mysqlservice/ICDCTokenService.java
+2
-0
src/main/java/com/ikonke/konkeaialibabamcp/service/mysqlservice/impl/ICDCTokenServiceImpl.java
...bamcp/service/mysqlservice/impl/ICDCTokenServiceImpl.java
+4
-3
src/main/java/com/ikonke/konkeaialibabamcp/timedtasks/TemporarySceneTask.java
...onke/konkeaialibabamcp/timedtasks/TemporarySceneTask.java
+15
-1
No files found.
pom.xml
View file @
f81c13d4
...
@@ -66,10 +66,10 @@
...
@@ -66,10 +66,10 @@
<!-- <artifactId>spring-boot-starter-web</artifactId>-->
<!-- <artifactId>spring-boot-starter-web</artifactId>-->
<!-- </dependency>-->
<!-- </dependency>-->
<dependency
>
<!-- <dependency>--
>
<groupId>
org.springframework.boot
</groupId
>
<!-- <groupId>org.springframework.boot</groupId>--
>
<artifactId>
spring-boot-starter-webflux
</artifactId
>
<!-- <artifactId>spring-boot-starter-webflux</artifactId>--
>
</dependency
>
<!-- </dependency>--
>
<dependency>
<dependency>
<groupId>
org.projectlombok
</groupId>
<groupId>
org.projectlombok
</groupId>
...
...
src/main/java/com/ikonke/konkeaialibabamcp/config/AsyncConfig.java
0 → 100644
View file @
f81c13d4
package
com.ikonke.konkeaialibabamcp.config
;
import
org.springframework.context.annotation.Bean
;
import
org.springframework.context.annotation.Configuration
;
import
org.springframework.scheduling.annotation.EnableAsync
;
import
org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor
;
import
java.util.concurrent.Executor
;
@Configuration
@EnableAsync
// 开启异步支持
public
class
AsyncConfig
{
// 配置线程池
@Bean
(
name
=
"taskExecutor"
)
public
Executor
taskExecutor
()
{
ThreadPoolTaskExecutor
executor
=
new
ThreadPoolTaskExecutor
();
// 核心线程数
executor
.
setCorePoolSize
(
5
);
// 最大线程数
executor
.
setMaxPoolSize
(
10
);
// 队列容量
executor
.
setQueueCapacity
(
25
);
// 线程名称前缀
executor
.
setThreadNamePrefix
(
"Async-"
);
// 当达到最大线程数时如何处理新任务
executor
.
setRejectedExecutionHandler
(
new
java
.
util
.
concurrent
.
ThreadPoolExecutor
.
CallerRunsPolicy
());
executor
.
initialize
();
return
executor
;
}
}
src/main/java/com/ikonke/konkeaialibabamcp/constant/RedisKeys.java
View file @
f81c13d4
...
@@ -13,4 +13,6 @@ public class RedisKeys {
...
@@ -13,4 +13,6 @@ public class RedisKeys {
public
static
final
String
KONKE_DIFY_CONVERSATION_ID
=
"konke:dify:conversationid:"
;
public
static
final
String
KONKE_DIFY_CONVERSATION_ID
=
"konke:dify:conversationid:"
;
public
static
final
String
KONKE_DIFY_API_KEY
=
"konke:dify:api:key"
;
public
static
final
String
KONKE_DIFY_API_KEY
=
"konke:dify:api:key"
;
public
static
final
String
KONKE_DIFY_LAST_STEAM_CHAT_TASK_ID
=
"konke:dify:last:taskid:"
;
}
}
src/main/java/com/ikonke/konkeaialibabamcp/controller/CDCController.java
View file @
f81c13d4
...
@@ -59,18 +59,40 @@ public class CDCController {
...
@@ -59,18 +59,40 @@ public class CDCController {
List
<
CDCToken
>
list
=
tokenService
.
list
(
wrapper
);
List
<
CDCToken
>
list
=
tokenService
.
list
(
wrapper
);
if
(
list
.
size
()
==
1
){
if
(
list
.
size
()
==
1
){
CDCToken
token
=
list
.
get
(
0
);
CDCToken
token
=
list
.
get
(
0
);
// 判断CDCToken是否过期
LocalDateTime
localDateTime
=
token
.
getCreateTime
().
plusDays
(
6
);
if
(
localDateTime
.
isBefore
(
LocalDateTime
.
now
())){
log
.
info
(
"getCDCToken..CDCToken已过期..sn:{}"
,
sn
);
token
.
setState
(
CDCToken
.
STATE_DISABLED
);
tokenService
.
updateById
(
token
);
cdc_token
=
cdcHttpUtils
.
getCDCToken
(
ccuId
,
ownerId
);
token
.
setAccessToken
(
cdc_token
);
token
.
setState
(
CDCToken
.
STATE_ENABLED
);
token
.
setCreateTime
(
LocalDateTime
.
now
());
tokenService
.
updateById
(
token
);
}
if
(
StrUtil
.
isNotBlank
(
token
.
getSn
())
&&
!
token
.
getSn
().
equalsIgnoreCase
(
sn
)){
if
(
StrUtil
.
isNotBlank
(
token
.
getSn
())
&&
!
token
.
getSn
().
equalsIgnoreCase
(
sn
)){
//更新sn
//更新sn
-sn不存在或者更换了sn
token
.
setSn
(
sn
);
token
.
setSn
(
sn
);
tokenService
.
save
(
token
);
tokenService
.
updateById
(
token
);
}
}
cdc_token
=
token
.
getAccessToken
();
cdc_token
=
token
.
getAccessToken
();
}
else
if
(
list
.
size
()
>
1
){
}
else
if
(
list
.
size
()
>
1
){
log
.
error
(
"获取CDCToken失败,CDCToken数量大于1"
);
log
.
error
(
"获取CDCToken失败,CDCToken数量大于1"
);
return
null
;
return
null
;
}
else
{
}
else
{
//去建发获取CDCToken
//去建发获取CDCToken
,有效期7天
cdc_token
=
cdcHttpUtils
.
getCDCToken
(
ccuId
,
ownerId
);
cdc_token
=
cdcHttpUtils
.
getCDCToken
(
ccuId
,
ownerId
);
CDCToken
token
=
new
CDCToken
();
token
.
setSn
(
sn
);
token
.
setCcuId
(
ccuId
);
token
.
setAccessToken
(
cdc_token
);
token
.
setState
(
CDCToken
.
STATE_ENABLED
);
token
.
setCreateTime
(
LocalDateTime
.
now
());
tokenService
.
save
(
token
);
}
}
JSONObject
result
=
new
JSONObject
();
JSONObject
result
=
new
JSONObject
();
...
@@ -109,7 +131,6 @@ public class CDCController {
...
@@ -109,7 +131,6 @@ public class CDCController {
bySn
.
setAccessToken
(
token
);
bySn
.
setAccessToken
(
token
);
bySn
.
setRefreshToken
(
bySn
.
getAccessToken
());
bySn
.
setRefreshToken
(
bySn
.
getAccessToken
());
bySn
.
setState
(
CDCToken
.
STATE_ENABLED
);
bySn
.
setState
(
CDCToken
.
STATE_ENABLED
);
bySn
.
setCreateTime
(
LocalDateTime
.
now
());
bySn
.
setCcuId
(
CcuUtils
.
getCcuName
(
ccuName
));
bySn
.
setCcuId
(
CcuUtils
.
getCcuName
(
ccuName
));
tokenService
.
updateById
(
bySn
);
tokenService
.
updateById
(
bySn
);
}
}
...
...
src/main/java/com/ikonke/konkeaialibabamcp/controller/DifyController.java
View file @
f81c13d4
...
@@ -114,6 +114,7 @@ public class DifyController {
...
@@ -114,6 +114,7 @@ public class DifyController {
CDCToken
token
=
list
.
get
(
0
);
CDCToken
token
=
list
.
get
(
0
);
String
url
=
difyChatSteamMessages
+
"/dify/chatSteamMessages?query="
+
input
;
String
url
=
difyChatSteamMessages
+
"/dify/chatSteamMessages?query="
+
input
;
log
.
info
(
"开始 dify 请求:{}"
,
url
);
ai_result
=
HttpRequest
.
get
(
url
)
ai_result
=
HttpRequest
.
get
(
url
)
.
header
(
"ccuName"
,
token
.
getCcuId
()
)
.
header
(
"ccuName"
,
token
.
getCcuId
()
)
.
header
(
"token"
,
token
.
getAccessToken
())
.
header
(
"token"
,
token
.
getAccessToken
())
...
@@ -274,6 +275,29 @@ public class DifyController {
...
@@ -274,6 +275,29 @@ public class DifyController {
});
});
}
}
/**
* 停止响应
* 仅支持流式模式。
*/
@GetMapping
(
"/stopChatSteamMessages"
)
public
String
stopChatSteamMessages
(
@RequestHeader
(
"sn"
)
String
sn
){
log
.
info
(
"结束 流式 对话, sn = {}"
,
sn
);
String
redis_task_key
=
RedisKeys
.
KONKE_DIFY_LAST_STEAM_CHAT_TASK_ID
+
sn
;
Object
redis_task_id
=
redisTemplate
.
opsForValue
().
get
(
redis_task_key
);
if
(
redis_task_id
!=
null
){
String
url
=
"http://172.17.12.12:8088/chat-messages/"
+
redis_task_id
+
"/stop"
;
JSONObject
body
=
new
JSONObject
();
body
.
set
(
"user"
,
sn
);
String
body1
=
HttpRequest
.
post
(
url
)
.
header
(
"Authorization"
,
"Bearer "
+
dify_api_key
)
.
header
(
"Content-Type"
,
"application/json"
)
.
body
(
body
.
toString
())
.
timeout
(
1000000
)
.
execute
().
body
();
return
body1
;
}
return
"{\"result\": \"error\"}"
;
}
@GetMapping
(
"/chatSteamMessages"
)
@GetMapping
(
"/chatSteamMessages"
)
public
Flux
<
String
>
chatSteamMessages
(
@RequestParam
(
name
=
"query"
)
String
query
,
public
Flux
<
String
>
chatSteamMessages
(
@RequestParam
(
name
=
"query"
)
String
query
,
@RequestHeader
(
"ccuName"
)
String
ccuName
,
@RequestHeader
(
"ccuName"
)
String
ccuName
,
...
@@ -334,8 +358,12 @@ public class DifyController {
...
@@ -334,8 +358,12 @@ public class DifyController {
private
String
convertToCustomResponseAsync
(
DifyStreamResponse
difyStreamResponse
,
String
sn
,
String
ccuName
,
String
token
,
Object
redis_conversationId
)
{
private
String
convertToCustomResponseAsync
(
DifyStreamResponse
difyStreamResponse
,
String
sn
,
String
ccuName
,
String
token
,
Object
redis_conversationId
)
{
if
(
redis_conversationId
==
null
&&
StrUtil
.
isNotBlank
(
difyStreamResponse
.
getConversation_id
())){
//conversationId为null表示第一次对话
if
(
redis_conversationId
==
null
&&
StrUtil
.
isNotBlank
(
difyStreamResponse
.
getConversation_id
())){
//conversationId为null表示第一次对话
String
conversationId
=
difyStreamResponse
.
getConversation_id
();
// 保存最后一次对话的task_id,后续可暂停对话
String
redis_task_key
=
RedisKeys
.
KONKE_DIFY_LAST_STEAM_CHAT_TASK_ID
+
sn
;
redisTemplate
.
opsForValue
().
set
(
redis_task_key
,
difyStreamResponse
.
getTask_id
());
// 保存conversationId
String
conversationId
=
difyStreamResponse
.
getConversation_id
();
String
redis_key
=
RedisKeys
.
KONKE_DIFY_CONVERSATION_ID
+
sn
;
String
redis_key
=
RedisKeys
.
KONKE_DIFY_CONVERSATION_ID
+
sn
;
redisTemplate
.
opsForValue
().
set
(
redis_key
,
conversationId
);
redisTemplate
.
opsForValue
().
set
(
redis_key
,
conversationId
);
...
...
src/main/java/com/ikonke/konkeaialibabamcp/controller/DifyControllerV2.java
0 → 100644
View file @
f81c13d4
package
com.ikonke.konkeaialibabamcp.controller
;
import
cn.hutool.core.util.StrUtil
;
import
cn.hutool.http.HttpRequest
;
import
cn.hutool.json.JSONArray
;
import
cn.hutool.json.JSONObject
;
import
cn.hutool.json.JSONUtil
;
import
com.baomidou.mybatisplus.core.conditions.query.QueryWrapper
;
import
com.ikonke.konkeaialibabamcp.constant.RedisKeys
;
import
com.ikonke.konkeaialibabamcp.controller.param.DifyStreamResponse
;
import
com.ikonke.konkeaialibabamcp.entity.mysql.CDCToken
;
import
com.ikonke.konkeaialibabamcp.event.mode.DifyChatSteamEvent
;
import
com.ikonke.konkeaialibabamcp.service.cdc.CdcHttpUtils
;
import
com.ikonke.konkeaialibabamcp.service.mysqlservice.ICDCTokenService
;
import
com.ikonke.konkeaialibabamcp.utils.CcuUtils
;
import
jakarta.annotation.PostConstruct
;
import
lombok.extern.slf4j.Slf4j
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.beans.factory.annotation.Value
;
import
org.springframework.context.ApplicationEventPublisher
;
import
org.springframework.data.redis.core.RedisTemplate
;
import
org.springframework.http.MediaType
;
import
org.springframework.web.bind.annotation.*
;
import
org.springframework.web.reactive.function.client.WebClient
;
import
reactor.core.publisher.Flux
;
import
java.time.Duration
;
import
java.time.LocalDateTime
;
import
java.util.List
;
@Slf4j
@RestController
@RequestMapping
(
"/dify/v2"
)
public
class
DifyControllerV2
{
@Autowired
private
ICDCTokenService
tokenService
;
@Autowired
private
WebClient
webClient
;
@Autowired
private
CdcHttpUtils
cdcHttpUtils
;
private
final
ApplicationEventPublisher
eventPublisher
;
@Autowired
private
RedisTemplate
<
String
,
Object
>
redisTemplate
;
@Value
(
"${cdc.difyChatSteamMessages}"
)
private
String
difyChatSteamMessages
;
private
String
dify_api_key
;
public
DifyControllerV2
(
ApplicationEventPublisher
eventPublisher
)
{
this
.
eventPublisher
=
eventPublisher
;
}
@PostConstruct
public
void
init
()
{
Object
redisValue
=
redisTemplate
.
opsForValue
().
get
(
RedisKeys
.
KONKE_DIFY_API_KEY
);
// 如果Redis中有值,则使用Redis中的值覆盖配置文件的值
if
(
redisValue
!=
null
)
{
this
.
dify_api_key
=
redisValue
.
toString
();
log
.
info
(
"Using Dify API key from Redis: {}"
,
this
.
dify_api_key
);
}
else
{
log
.
info
(
"Using Dify API key from configuration file: {}"
,
this
.
dify_api_key
);
}
}
@GetMapping
(
"/synAiSpeech"
)
public
JSONObject
synAiSpeech
(
@RequestParam
(
name
=
"aiSpeechUserId"
)
String
aiSpeechUserId
,
@RequestParam
(
name
=
"sn"
)
String
sn
){
log
.
info
(
"synAiSpeech..同步AiSpeechUserId..aiSpeechUserId:{},sn:{}"
,
aiSpeechUserId
,
sn
);
Long
timestamp
=
System
.
currentTimeMillis
();
JSONObject
result
=
new
JSONObject
();
QueryWrapper
<
CDCToken
>
wrapper
=
new
QueryWrapper
<>();
wrapper
.
eq
(
"sn"
,
sn
);
List
<
CDCToken
>
list
=
tokenService
.
list
(
wrapper
);
if
(
list
.
size
()
!=
1
){
result
.
set
(
"msg"
,
"操作失败,先获取建发的token"
);
result
.
set
(
"code"
,
300
);
}
CDCToken
token
=
list
.
get
(
0
);
if
(
StrUtil
.
isBlank
(
token
.
getAiSpeechUserId
())){
token
.
setAiSpeechUserId
(
aiSpeechUserId
);
tokenService
.
updateById
(
token
);
}
else
{
if
(!
token
.
getAiSpeechUserId
().
equals
(
aiSpeechUserId
)){
token
.
setAiSpeechUserId
(
aiSpeechUserId
);
tokenService
.
updateById
(
token
);
}
}
result
.
set
(
"msg"
,
"操作成功"
);
result
.
set
(
"code"
,
200
);
log
.
info
(
"操作成功,time={}"
,
System
.
currentTimeMillis
()
-
timestamp
);
return
result
;
}
/**
* 思必驰 demo
* https://www.duiopen.com/docs/ct_dsk_protocol
*
*/
@RequestMapping
(
value
=
"/bottomSkill"
,
method
=
RequestMethod
.
POST
,
consumes
=
"application/json"
)
@ResponseBody
public
JSONObject
bottomSkill
(
@RequestBody
JSONObject
jsonObject
)
{
Long
timestamp
=
System
.
currentTimeMillis
();
log
.
info
(
"思必驰 demo..请求参数:{}"
,
jsonObject
.
toString
());
JSONObject
result
=
jsonObject
.
getJSONObject
(
"request"
);
JSONObject
context
=
jsonObject
.
getJSONObject
(
"context"
);
JSONObject
device
=
context
.
getJSONObject
(
"device"
);
String
deviceName
=
device
.
getStr
(
"deviceName"
);
JSONObject
user
=
context
.
getJSONObject
(
"user"
);
String
userId
=
user
.
getStr
(
"userId"
);
String
type
=
result
.
get
(
"type"
).
toString
();
String
input
;
//请求文本
String
res
=
"{'version': '1.0','response': {'speak': {'type': 'text','text': '%s','ssml': 'SSML markup text string to speak'}},'shouldEndSession': %b}"
;
//忽略了部分DSK协议字段,shouldEndSession字段决定是否结束本轮对话
JSONObject
response
=
JSONUtil
.
parseObj
(
String
.
format
(
res
,
"error"
,
true
));
// 响应报文
JSONArray
slots
;
try
{
slots
=
result
.
getJSONArray
(
"inputs"
);
input
=
slots
.
getJSONObject
(
0
).
get
(
"input"
).
toString
();
//根据userId获取到对应的主机号等信息
QueryWrapper
<
CDCToken
>
wrapper
=
new
QueryWrapper
<>();
wrapper
.
eq
(
"aiSpeechUserId"
,
userId
);
List
<
CDCToken
>
list
=
tokenService
.
list
(
wrapper
);
if
(
list
.
size
()
==
1
){
CDCToken
token
=
list
.
get
(
0
);
if
(
StrUtil
.
isBlank
(
token
.
getCcuId
())){
res
=
String
.
format
(
res
,
"未绑定主机绑定"
,
false
);
response
=
JSONUtil
.
parseObj
(
res
);
}
// 异步执行对话????
eventPublisher
.
publishEvent
(
new
DifyChatSteamEvent
(
this
,
token
.
getSn
(),
input
));
res
=
String
.
format
(
res
,
"正在思考中"
,
false
);
response
=
JSONUtil
.
parseObj
(
res
);
}
else
if
(
list
.
size
()
>
1
){
log
.
error
(
"获取CDCToken失败,CDCToken数量大于1"
);
res
=
String
.
format
(
res
,
"绑定异常"
,
false
);
response
=
JSONUtil
.
parseObj
(
res
);
}
else
{
res
=
String
.
format
(
res
,
"未绑定主机绑定"
,
false
);
response
=
JSONUtil
.
parseObj
(
res
);
}
}
catch
(
Exception
e
)
{
res
=
String
.
format
(
res
,
"服务器异常,稍后重试."
,
false
);
response
=
JSONUtil
.
parseObj
(
res
);
e
.
printStackTrace
();
}
finally
{
log
.
info
(
"思必驰 demo..响应时间="
+(
System
.
currentTimeMillis
()-
timestamp
));
return
response
;
}
}
@GetMapping
(
"/chatSteamMessages"
)
public
Flux
<
String
>
chatSteamMessages
(
@RequestParam
(
name
=
"query"
)
String
query
,
@RequestHeader
(
"sn"
)
String
sn
){
log
.
info
(
"开始 流式 对话 sn:{}, query:{}"
,
sn
,
query
);
QueryWrapper
<
CDCToken
>
wrapper
=
new
QueryWrapper
<>();
wrapper
.
eq
(
"sn"
,
sn
);
List
<
CDCToken
>
list
=
tokenService
.
list
(
wrapper
);
if
(
list
.
size
()
!=
1
){
return
Flux
.
just
(
"未绑定主机"
);
}
CDCToken
token
=
list
.
get
(
0
);
if
(
StrUtil
.
isBlank
(
token
.
getCcuId
())){
return
Flux
.
just
(
"未绑定主机"
);
}
if
(
StrUtil
.
isBlank
(
token
.
getRefreshToken
())){
// 没有建发的token,有效期7天
String
cdc_token
=
cdcHttpUtils
.
getCDCToken
(
token
.
getCcuId
(),
token
.
getCDCOwnerId
());
token
.
setAccessToken
(
cdc_token
);
tokenService
.
updateById
(
token
);
}
//workflows/run
String
url
=
"http://172.17.12.12:8088/v1/chat-messages"
;
JSONObject
inputs
=
new
JSONObject
();
inputs
.
set
(
"ccuName"
,
token
.
getCcuId
());
inputs
.
set
(
"sn"
,
sn
);
inputs
.
set
(
"token"
,
token
.
getAccessToken
());
JSONObject
body
=
new
JSONObject
();
body
.
set
(
"inputs"
,
inputs
);
body
.
set
(
"query"
,
query
);
body
.
set
(
"response_mode"
,
"streaming"
);
//blocking 阻塞模式,streaming 流式模式
body
.
set
(
"user"
,
sn
);
String
redis_key
=
RedisKeys
.
KONKE_DIFY_CONVERSATION_ID
+
sn
;
Object
redis_conversationId
=
redisTemplate
.
opsForValue
().
get
(
redis_key
);
String
conversationId
=
null
;
if
(
redis_conversationId
!=
null
){
conversationId
=
redis_conversationId
.
toString
();
}
CDCToken
bySn
=
null
;
if
(
StrUtil
.
isBlank
(
conversationId
)){
bySn
=
tokenService
.
findBySn
(
sn
);
if
(
bySn
!=
null
&&
StrUtil
.
isNotBlank
(
bySn
.
getConversationId
())){
conversationId
=
bySn
.
getConversationId
();
body
.
set
(
"conversation_id"
,
conversationId
);
//一个user一个会话
}
}
else
{
body
.
set
(
"conversation_id"
,
conversationId
);
}
log
.
info
(
"dify key:{} ,流式 对话 body:{}"
,
dify_api_key
,
body
);
return
webClient
.
post
()
.
uri
(
url
)
.
headers
(
httpHeaders
->
{
httpHeaders
.
setContentType
(
MediaType
.
APPLICATION_JSON
);
httpHeaders
.
setBearerAuth
(
dify_api_key
);
})
.
bodyValue
(
body
)
.
retrieve
()
.
bodyToFlux
(
DifyStreamResponse
.
class
)
//实体转换
.
timeout
(
Duration
.
ofMinutes
(
1
))
//设置超时时间1分钟
.
filter
(
this
::
shouldInclude
)
// 过滤掉不需要的数据【根据需求增加】
.
map
((
DifyStreamResponse
difyStreamResponse
)
->
convertToCustomResponseAsync
(
difyStreamResponse
,
sn
,
token
.
getCcuId
(),
token
.
getAccessToken
(),
redis_conversationId
))
// 异步转换【如果返回格式自定义则通过异步转换实现】
.
onErrorResume
(
throwable
->
{
log
.
info
(
"异常输出:"
+
throwable
.
getMessage
());
return
null
;
});
}
private
String
convertToCustomResponseAsync
(
DifyStreamResponse
difyStreamResponse
,
String
sn
,
String
ccuName
,
String
token
,
Object
redis_conversationId
)
{
if
(
redis_conversationId
==
null
&&
StrUtil
.
isNotBlank
(
difyStreamResponse
.
getConversation_id
())){
//conversationId为null表示第一次对话
// 保存最后一次对话的task_id,后续可暂停对话
String
redis_task_key
=
RedisKeys
.
KONKE_DIFY_LAST_STEAM_CHAT_TASK_ID
+
sn
;
redisTemplate
.
opsForValue
().
set
(
redis_task_key
,
difyStreamResponse
.
getTask_id
());
// 保存conversationId
String
conversationId
=
difyStreamResponse
.
getConversation_id
();
String
redis_key
=
RedisKeys
.
KONKE_DIFY_CONVERSATION_ID
+
sn
;
redisTemplate
.
opsForValue
().
set
(
redis_key
,
conversationId
);
CDCToken
bySn
=
tokenService
.
findBySn
(
sn
);
if
(
bySn
!=
null
){
bySn
.
setConversationId
(
conversationId
);
tokenService
.
updateById
(
bySn
);
}
else
{
log
.
error
(
"这一步应该从不会触发............"
);
bySn
=
new
CDCToken
();
bySn
.
setSn
(
sn
);
bySn
.
setCcuId
(
CcuUtils
.
getCcuName
(
ccuName
));
bySn
.
setAccessToken
(
token
);
bySn
.
setCreateTime
(
LocalDateTime
.
now
());
bySn
.
setState
(
CDCToken
.
STATE_ENABLED
);
bySn
.
setConversationId
(
conversationId
);
tokenService
.
save
(
bySn
);
}
}
String
answer
=
difyStreamResponse
.
getAnswer
();
if
(
StrUtil
.
isBlank
(
answer
)){
return
""
;
}
else
{
return
answer
;
}
}
private
boolean
shouldInclude
(
DifyStreamResponse
streamResponse
)
{
// 示例:只要message节点的数据和message_end节点的数据
if
(
streamResponse
.
getEvent
().
equals
(
"message"
)
||
streamResponse
.
getEvent
().
equals
(
"message_end"
))
{
return
true
;
}
return
false
;
}
}
src/main/java/com/ikonke/konkeaialibabamcp/event/listener/DifyChatSteamEventListener.java
0 → 100644
View file @
f81c13d4
package
com.ikonke.konkeaialibabamcp.event.listener
;
import
cn.hutool.http.HttpRequest
;
import
com.baomidou.mybatisplus.core.conditions.query.QueryWrapper
;
import
com.ikonke.konkeaialibabamcp.entity.mysql.CDCToken
;
import
com.ikonke.konkeaialibabamcp.event.mode.DifyChatSteamEvent
;
import
com.ikonke.konkeaialibabamcp.service.mysqlservice.ICDCTokenService
;
import
lombok.extern.slf4j.Slf4j
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.beans.factory.annotation.Value
;
import
org.springframework.context.event.EventListener
;
import
org.springframework.scheduling.annotation.Async
;
import
org.springframework.stereotype.Component
;
import
java.util.List
;
@Slf4j
@Component
public
class
DifyChatSteamEventListener
{
@Value
(
"${cdc.difyChatSteamMessages}"
)
private
String
difyChatSteamMessages
;
@Autowired
private
ICDCTokenService
tokenService
;
@Async
@EventListener
public
void
handleUserRegisteredEvent
(
DifyChatSteamEvent
event
)
{
long
startTime
=
System
.
currentTimeMillis
();
log
.
info
(
"处理dify AI 聊天:sn:{},input:{}"
,
event
.
getSn
(),
event
.
getInput
());
try
{
QueryWrapper
<
CDCToken
>
wrapper
=
new
QueryWrapper
<>();
wrapper
.
eq
(
"sn"
,
event
.
getSn
());
List
<
CDCToken
>
list
=
tokenService
.
list
(
wrapper
);
CDCToken
token
=
list
.
get
(
0
);
String
url
=
difyChatSteamMessages
+
"/dify/v2/chatSteamMessages?query="
+
event
.
getInput
();
log
.
info
(
"开始 dify 请求:{}"
,
url
);
String
ai_result
=
HttpRequest
.
get
(
url
)
.
header
(
"ccuName"
,
token
.
getCcuId
()
)
.
header
(
"token"
,
token
.
getAccessToken
())
.
header
(
"sn"
,
token
.
getSn
())
.
timeout
(
600000
)
.
execute
().
body
();
log
.
info
(
"dify请求返回:{}"
,
ai_result
);
String
result_end
=
ai_result
.
replaceAll
(
"(?m)^\\s*$[\r\n]*"
,
""
).
replaceAll
(
"[\r\n]+"
,
"\n"
).
replaceAll
(
"[\r\n]"
,
""
).
trim
();
log
.
info
(
"dify请求返回2--->:{}"
,
result_end
);
// 通过WebSocket发送消息给屏端
log
.
info
(
"dify请求耗时--->:{}"
,
System
.
currentTimeMillis
()
-
startTime
);
}
catch
(
Exception
e
)
{
Thread
.
currentThread
().
interrupt
();
e
.
printStackTrace
();
}
}
}
src/main/java/com/ikonke/konkeaialibabamcp/event/mode/DifyChatSteamEvent.java
0 → 100644
View file @
f81c13d4
package
com.ikonke.konkeaialibabamcp.event.mode
;
import
org.springframework.context.ApplicationEvent
;
public
class
DifyChatSteamEvent
extends
ApplicationEvent
{
private
final
String
sn
;
private
final
String
input
;
public
DifyChatSteamEvent
(
Object
source
,
String
sn
,
String
input
)
{
super
(
source
);
this
.
sn
=
sn
;
this
.
input
=
input
;
}
public
String
getSn
()
{
return
sn
;
}
public
String
getInput
()
{
return
input
;
}
}
src/main/java/com/ikonke/konkeaialibabamcp/mapper/mysql/CDCTokenMapper.java
View file @
f81c13d4
...
@@ -5,6 +5,7 @@ import com.ikonke.konkeaialibabamcp.entity.mysql.CDCToken;
...
@@ -5,6 +5,7 @@ import com.ikonke.konkeaialibabamcp.entity.mysql.CDCToken;
import
org.apache.ibatis.annotations.Mapper
;
import
org.apache.ibatis.annotations.Mapper
;
import
org.apache.ibatis.annotations.Param
;
import
org.apache.ibatis.annotations.Param
;
import
org.apache.ibatis.annotations.Select
;
import
org.apache.ibatis.annotations.Select
;
import
org.apache.ibatis.annotations.Update
;
@Mapper
@Mapper
public
interface
CDCTokenMapper
extends
BaseMapper
<
CDCToken
>
{
public
interface
CDCTokenMapper
extends
BaseMapper
<
CDCToken
>
{
...
@@ -14,4 +15,7 @@ public interface CDCTokenMapper extends BaseMapper<CDCToken> {
...
@@ -14,4 +15,7 @@ public interface CDCTokenMapper extends BaseMapper<CDCToken> {
@Select
(
"SELECT * FROM cdc_token WHERE sn = #{sn} and `state` = #{state}"
)
@Select
(
"SELECT * FROM cdc_token WHERE sn = #{sn} and `state` = #{state}"
)
CDCToken
findBySnAndState
(
@Param
(
"sn"
)
String
sn
,
@Param
(
"state"
)
Integer
state
);
CDCToken
findBySnAndState
(
@Param
(
"sn"
)
String
sn
,
@Param
(
"state"
)
Integer
state
);
@Update
(
"UPDATE cdc_token SET accessToken = NULL WHERE DATE(createTime) < DATE_SUB(CURDATE(), INTERVAL #{num} DAY);"
)
int
examineCDCToken
(
@Param
(
"num"
)
int
num
);
}
}
src/main/java/com/ikonke/konkeaialibabamcp/service/cdc/CdcHttpUtils.java
View file @
f81c13d4
...
@@ -49,7 +49,8 @@ public class CdcHttpUtils {
...
@@ -49,7 +49,8 @@ public class CdcHttpUtils {
.
execute
().
body
();
.
execute
().
body
();
JSONObject
jsonObject
=
JSONUtil
.
parseObj
(
body1
);
JSONObject
jsonObject
=
JSONUtil
.
parseObj
(
body1
);
if
(
jsonObject
.
getInt
(
"code"
)
==
200
){
if
(
jsonObject
.
getInt
(
"code"
)
==
200
){
return
jsonObject
.
getStr
(
"token"
);
JSONObject
jsonObject_data
=
jsonObject
.
getJSONObject
(
"data"
);
return
jsonObject_data
.
getStr
(
"access_token"
);
}
}
return
null
;
return
null
;
}
}
...
...
src/main/java/com/ikonke/konkeaialibabamcp/service/mysqlservice/ICDCTokenService.java
View file @
f81c13d4
...
@@ -7,4 +7,6 @@ public interface ICDCTokenService extends IService<CDCToken> {
...
@@ -7,4 +7,6 @@ public interface ICDCTokenService extends IService<CDCToken> {
CDCToken
findBySn
(
String
sn
);
CDCToken
findBySn
(
String
sn
);
CDCToken
findBySn
(
String
sn
,
Integer
state
);
CDCToken
findBySn
(
String
sn
,
Integer
state
);
int
examineCDCToken
(
int
num
);
}
}
src/main/java/com/ikonke/konkeaialibabamcp/service/mysqlservice/impl/ICDCTokenServiceImpl.java
View file @
f81c13d4
...
@@ -16,9 +16,6 @@ import java.util.List;
...
@@ -16,9 +16,6 @@ import java.util.List;
@Service
@Service
public
class
ICDCTokenServiceImpl
extends
ServiceImpl
<
CDCTokenMapper
,
CDCToken
>
implements
ICDCTokenService
{
public
class
ICDCTokenServiceImpl
extends
ServiceImpl
<
CDCTokenMapper
,
CDCToken
>
implements
ICDCTokenService
{
@Autowired
private
CDCTokenMapper
cdcTokenMapper
;
@Override
@Override
public
CDCToken
findBySn
(
String
sn
)
{
public
CDCToken
findBySn
(
String
sn
)
{
return
baseMapper
.
findBySn
(
sn
);
return
baseMapper
.
findBySn
(
sn
);
...
@@ -29,5 +26,9 @@ public class ICDCTokenServiceImpl extends ServiceImpl<CDCTokenMapper, CDCToken>
...
@@ -29,5 +26,9 @@ public class ICDCTokenServiceImpl extends ServiceImpl<CDCTokenMapper, CDCToken>
return
baseMapper
.
findBySnAndState
(
sn
,
state
);
return
baseMapper
.
findBySnAndState
(
sn
,
state
);
}
}
@Override
public
int
examineCDCToken
(
int
num
){
return
baseMapper
.
examineCDCToken
(
num
);
}
}
}
src/main/java/com/ikonke/konkeaialibabamcp/timedtasks/TemporarySceneTask.java
View file @
f81c13d4
package
com.ikonke.konkeaialibabamcp.timedtasks
;
package
com.ikonke.konkeaialibabamcp.timedtasks
;
import
com.ikonke.konkeaialibabamcp.entity.mysql.TemporaryScene
;
import
com.ikonke.konkeaialibabamcp.entity.mysql.TemporaryScene
;
import
com.ikonke.konkeaialibabamcp.service.mysqlservice.ICDCTokenService
;
import
com.ikonke.konkeaialibabamcp.service.mysqlservice.ITemporarySceneService
;
import
com.ikonke.konkeaialibabamcp.service.mysqlservice.ITemporarySceneService
;
import
com.ikonke.konkeaialibabamcp.utils.KonkeIotUtils
;
import
com.ikonke.konkeaialibabamcp.utils.KonkeIotUtils
;
import
lombok.extern.slf4j.Slf4j
;
import
lombok.extern.slf4j.Slf4j
;
...
@@ -21,9 +22,11 @@ public class TemporarySceneTask {
...
@@ -21,9 +22,11 @@ public class TemporarySceneTask {
private
ITemporarySceneService
temporarySceneService
;
private
ITemporarySceneService
temporarySceneService
;
@Autowired
@Autowired
private
KonkeIotUtils
konkeIotServer
;
private
KonkeIotUtils
konkeIotServer
;
@Autowired
private
ICDCTokenService
cdcTokenService
;
/**
/**
* 每5分钟再执行一次该定时器。
* 每5分钟
、300秒
再执行一次该定时器。
*/
*/
@Scheduled
(
fixedRate
=
300000
)
@Scheduled
(
fixedRate
=
300000
)
public
void
task
()
{
public
void
task
()
{
...
@@ -40,4 +43,15 @@ public class TemporarySceneTask {
...
@@ -40,4 +43,15 @@ public class TemporarySceneTask {
}
}
/**
* 每 43200 秒 、 720 分钟 、 12 小时 执行一次该定时器。
*/
@Scheduled
(
fixedRate
=
43200000
)
public
void
examineCDCToken
()
{
log
.
info
(
"开始定时任务:检查建发的token是否过期"
);
int
i
=
cdcTokenService
.
examineCDCToken
(
6
);
log
.
info
(
"结束定时任务:检查建发的token是否过期"
+
i
);
}
}
}
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