union-ark-web 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/PROJECT_OVERVIEW.md +405 -0
- package/README.md +177 -0
- package/ark-control/pom.xml +142 -0
- package/ark-control/src/local-mock/java/com/epcc/commons/securityproxy/api/SecurityResult.java +14 -0
- package/ark-control/src/local-mock/java/com/epcc/commons/securityproxy/api/SymmetricalSecurityService.java +11 -0
- package/ark-control/src/local-mock/java/com/epcc/dubbo/result/Result.java +29 -0
- package/ark-control/src/local-mock/java/com/nucc/channel/ark/common/annotation/EnDecryptField.java +16 -0
- package/ark-control/src/local-mock/java/com/nucc/channel/ark/common/annotation/EnDecryptFieldLong.java +17 -0
- package/ark-control/src/local-mock/java/com/nucc/channel/ark/common/annotation/EnDecryptFieldWithTag.java +16 -0
- package/ark-control/src/local-mock/java/com/nucc/channel/ark/common/exception/BaseDataErrorCode.java +26 -0
- package/ark-control/src/local-mock/java/com/nucc/channel/ark/common/exception/CheckException.java +33 -0
- package/ark-control/src/local-mock/java/com/nucc/channel/ark/common/exception/ErrorCode.java +9 -0
- package/ark-control/src/local-mock/java/com/nucc/channel/ark/common/redis/RedisCacheService.java +131 -0
- package/ark-control/src/local-mock/java/com/nucc/channel/ark/common/util/Constant.java +24 -0
- package/ark-control/src/local-mock/java/com/nucc/channel/ark/common/util/ResultUtil.java +9 -0
- package/ark-control/src/local-mock/java/com/union/control/local/sensitive/LocalMockSensitiveProxy.java +40 -0
- package/ark-control/src/local-mock/java/com/union/control/local/sensitive/LocalRedisCacheService.java +45 -0
- package/ark-control/src/local-mock/java/com/union/control/local/sensitive/LocalSensitiveHostConfiguration.java +27 -0
- package/ark-control/src/local-mock/java/com/union/control/local/sensitive/LocalSensitiveProxyConfiguration.java +16 -0
- package/ark-control/src/main/java/com/union/control/ArkControlApplication.java +17 -0
- package/ark-control/src/main/java/com/union/control/dubbo/ProviderAccessLogFilter.java +45 -0
- package/ark-control/src/main/java/com/union/control/mapper/AgentExecutionMapper.java +35 -0
- package/ark-control/src/main/java/com/union/control/mapper/ConversationMapper.java +44 -0
- package/ark-control/src/main/java/com/union/control/mapper/MemoryStoreMapper.java +41 -0
- package/ark-control/src/main/java/com/union/control/mapper/ScheduledTaskMapper.java +96 -0
- package/ark-control/src/main/java/com/union/control/mapper/SensitiveAddressBookDemo.java +31 -0
- package/ark-control/src/main/java/com/union/control/mapper/SensitiveDataDemoMapper.java +24 -0
- package/ark-control/src/main/java/com/union/control/mapper/interceptor/AESInterceptor.java +265 -0
- package/ark-control/src/main/java/com/union/control/mapper/interceptor/AddressBookHandler.java +105 -0
- package/ark-control/src/main/java/com/union/control/schedule/ScheduledExecutionToken.java +55 -0
- package/ark-control/src/main/java/com/union/control/schedule/ScheduledTaskScheduler.java +206 -0
- package/ark-control/src/main/java/com/union/control/service/impl/AgentExecutionServiceImpl.java +212 -0
- package/ark-control/src/main/java/com/union/control/service/impl/AgentProxyServiceImpl.java +143 -0
- package/ark-control/src/main/java/com/union/control/service/impl/ConversationServiceImpl.java +245 -0
- package/ark-control/src/main/java/com/union/control/service/impl/MemoryStoreServiceImpl.java +252 -0
- package/ark-control/src/main/java/com/union/control/service/impl/RunningAnalysisMockServiceImpl.java +301 -0
- package/ark-control/src/main/java/com/union/control/service/impl/ScheduledTaskServiceImpl.java +576 -0
- package/ark-control/src/main/java/com/union/control/service/impl/SensitiveDataDemoServiceImpl.java +144 -0
- package/ark-control/src/main/java/com/union/control/service/sensitive/RedisRevealTokenStore.java +90 -0
- package/ark-control/src/main/java/com/union/control/service/sensitive/SensitiveRevealProcessor.java +319 -0
- package/ark-control/src/main/java/com/union/control/service/sensitive/SensitiveRevealServiceImpl.java +72 -0
- package/ark-control/src/main/java/com/union/control/utils/AgentSupport.java +103 -0
- package/ark-control/src/main/java/com/union/control/utils/security/SymmetricalSecurityUtils.java +160 -0
- package/ark-control/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Filter +1 -0
- package/ark-control/src/main/resources/application.properties +4 -0
- package/ark-control/src/main/resources/application.yml +32 -0
- package/ark-control/src/main/resources/dubbo-provider.xml +20 -0
- package/ark-control/src/main/resources/mapper/AgentExecutionMapper.xml +75 -0
- package/ark-control/src/main/resources/mapper/ConversationMapper.xml +110 -0
- package/ark-control/src/main/resources/mapper/MemoryStoreMapper.xml +74 -0
- package/ark-control/src/main/resources/mapper/ScheduledTaskMapper.xml +286 -0
- package/ark-control/src/main/resources/mapper/SensitiveDataDemoMapper.xml +55 -0
- package/ark-control/src/main/resources/schema.sql +146 -0
- package/ark-control/src/test/java/com/union/control/AgentExecutionServiceTest.java +170 -0
- package/ark-control/src/test/java/com/union/control/ArkControlStructureContractTest.java +107 -0
- package/ark-control/src/test/java/com/union/control/ConversationServiceTest.java +89 -0
- package/ark-control/src/test/java/com/union/control/MemoryStoreServiceTest.java +26 -0
- package/ark-control/src/test/java/com/union/control/PydanticAiControlContractTest.java +52 -0
- package/ark-control/src/test/java/com/union/control/TestJson.java +45 -0
- package/ark-control/src/test/java/com/union/control/dubbo/ProviderAccessLogFilterTest.java +53 -0
- package/ark-control/src/test/java/com/union/control/local/sensitive/LocalSensitiveProxyConfigurationTest.java +23 -0
- package/ark-control/src/test/java/com/union/control/mapper/interceptor/AESInterceptorCompatibilityTest.java +505 -0
- package/ark-control/src/test/java/com/union/control/schedule/ScheduledExecutionTokenTest.java +27 -0
- package/ark-control/src/test/java/com/union/control/schedule/ScheduledTaskSchedulerTest.java +51 -0
- package/ark-control/src/test/java/com/union/control/service/AgentProxyServiceTest.java +111 -0
- package/ark-control/src/test/java/com/union/control/service/ScheduledTaskServiceTest.java +483 -0
- package/ark-control/src/test/java/com/union/control/service/impl/SensitiveDataDemoServiceImplTest.java +104 -0
- package/ark-control/src/test/java/com/union/control/service/sensitive/RedisRevealTokenStoreTest.java +65 -0
- package/ark-control/src/test/java/com/union/control/service/sensitive/SensitiveRevealBoundaryTest.java +99 -0
- package/ark-control/src/test/java/com/union/control/service/sensitive/SensitiveRevealProcessorCodecTest.java +111 -0
- package/ark-control/src/test/java/com/union/control/service/sensitive/SensitiveRevealProcessorTest.java +111 -0
- package/ark-control/src/test/java/com/union/control/utils/security/SymmetricalSecurityUtilsCompatibilityTest.java +87 -0
- package/ark-control-facade/pom.xml +16 -0
- package/ark-control-facade/src/main/java/com/union/control/service/AgentExecutionService.java +8 -0
- package/ark-control-facade/src/main/java/com/union/control/service/AgentProxyService.java +7 -0
- package/ark-control-facade/src/main/java/com/union/control/service/AgentResponse.java +22 -0
- package/ark-control-facade/src/main/java/com/union/control/service/ConversationService.java +12 -0
- package/ark-control-facade/src/main/java/com/union/control/service/MemoryStoreService.java +12 -0
- package/ark-control-facade/src/main/java/com/union/control/service/RunningAnalysisMockService.java +10 -0
- package/ark-control-facade/src/main/java/com/union/control/service/ScheduledTaskService.java +40 -0
- package/ark-control-facade/src/main/java/com/union/control/service/SensitiveDataDemoService.java +13 -0
- package/ark-control-facade/src/main/java/com/union/control/service/sensitive/SensitiveRevealService.java +18 -0
- package/ark-web/README.md +80 -0
- package/ark-web/pom.xml +105 -0
- package/ark-web/src/local-mock/java/com/epcc/arkweb/mock/ArkAuthServiceMock.java +105 -0
- package/ark-web/src/local-mock/java/com/epcc/arkweb/mock/LocalKaptchaConfiguration.java +26 -0
- package/ark-web/src/main/java/com/epcc/arkweb/Application.java +13 -0
- package/ark-web/src/main/java/com/epcc/arkweb/config/InterceptorConfig.java +20 -0
- package/ark-web/src/main/java/com/epcc/arkweb/config/Realm.java +64 -0
- package/ark-web/src/main/java/com/epcc/arkweb/config/ShiroConfig.java +129 -0
- package/ark-web/src/main/java/com/epcc/arkweb/filter/LoginFormFilter.java +43 -0
- package/ark-web/src/main/java/com/epcc/arkweb/helper/AuthContextHolder.java +39 -0
- package/ark-web/src/main/java/com/epcc/arkweb/helper/AuthenticatedRequest.java +91 -0
- package/ark-web/src/main/java/com/epcc/arkweb/model/ShiroUser.java +21 -0
- package/ark-web/src/main/java/com/epcc/arkweb/utils/ResultMsg.java +19 -0
- package/ark-web/src/main/java/com/epcc/arkweb/vo/llm/ScheduledTaskCommandVO.java +30 -0
- package/ark-web/src/main/java/com/epcc/arkweb/vo/llm/ScheduledTaskQueryVO.java +20 -0
- package/ark-web/src/main/java/com/epcc/arkweb/web/CommonController.java +25 -0
- package/ark-web/src/main/java/com/epcc/arkweb/web/llm/AgentAuthorizationInterceptor.java +229 -0
- package/ark-web/src/main/java/com/epcc/arkweb/web/llm/AgentController.java +165 -0
- package/ark-web/src/main/java/com/epcc/arkweb/web/llm/AgentPermission.java +13 -0
- package/ark-web/src/main/java/com/epcc/arkweb/web/llm/LlmController.java +209 -0
- package/ark-web/src/main/java/com/epcc/arkweb/web/llm/ScheduledTaskController.java +160 -0
- package/ark-web/src/main/java/com/epcc/arkweb/web/sensitive/reveal/SensitiveCaptchaService.java +146 -0
- package/ark-web/src/main/java/com/epcc/arkweb/web/sensitive/reveal/SensitiveRevealController.java +163 -0
- package/ark-web/src/main/resources/application.properties +1 -0
- package/ark-web/src/main/resources/application.yml +23 -0
- package/ark-web/src/main/resources/dubbo-consumer.xml +19 -0
- package/ark-web/src/production-overlay/java/com/epcc/arkweb/config/CasShiroConfig.java +229 -0
- package/ark-web/src/production-overlay/java/com/epcc/arkweb/config/InterceptorConfig.java +35 -0
- package/ark-web/src/production-overlay/java/com/epcc/arkweb/config/ShiroConfig.java +161 -0
- package/ark-web/src/test/java/com/epcc/arkweb/ArkWebStructureContractTest.java +290 -0
- package/ark-web/src/test/java/com/epcc/arkweb/ShiroTestSupport.java +32 -0
- package/ark-web/src/test/java/com/epcc/arkweb/config/LocalShiroIntegrationTest.java +121 -0
- package/ark-web/src/test/java/com/epcc/arkweb/helper/AuthenticatedRequestTest.java +131 -0
- package/ark-web/src/test/java/com/epcc/arkweb/local/LocalArkAuthServiceMockTest.java +17 -0
- package/ark-web/src/test/java/com/epcc/arkweb/web/llm/AgentAuthorizationInterceptorTest.java +134 -0
- package/ark-web/src/test/java/com/epcc/arkweb/web/llm/AgentControllerAuthorizationTest.java +97 -0
- package/ark-web/src/test/java/com/epcc/arkweb/web/llm/LlmControllerTest.java +113 -0
- package/ark-web/src/test/java/com/epcc/arkweb/web/sensitive/reveal/SensitiveRevealControllerTest.java +273 -0
- package/package.json +18 -0
- package/pom.xml +16 -0
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" ?>
|
|
2
|
+
<!DOCTYPE mapper
|
|
3
|
+
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|
4
|
+
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
|
5
|
+
<mapper namespace="com.union.control.mapper.ScheduledTaskMapper">
|
|
6
|
+
|
|
7
|
+
<sql id="taskFields">
|
|
8
|
+
t.id,t.title,t.prompt,t.schedule_type AS scheduleType,
|
|
9
|
+
DATE_FORMAT(t.run_at,'%Y-%m-%dT%H:%i:%s.%fZ') AS runAt,
|
|
10
|
+
t.cron_expression AS cronExpression,t.interval_seconds AS intervalSeconds,
|
|
11
|
+
t.timezone,t.status,
|
|
12
|
+
DATE_FORMAT(t.next_run_at,'%Y-%m-%dT%H:%i:%s.%fZ') AS nextRunAt,
|
|
13
|
+
DATE_FORMAT(t.created_at,'%Y-%m-%dT%H:%i:%s.%fZ') AS createdAt,
|
|
14
|
+
DATE_FORMAT(t.updated_at,'%Y-%m-%dT%H:%i:%s.%fZ') AS updatedAt
|
|
15
|
+
</sql>
|
|
16
|
+
|
|
17
|
+
<sql id="runSummaryFields">
|
|
18
|
+
r.id,r.task_id AS taskId,
|
|
19
|
+
DATE_FORMAT(r.scheduled_at,'%Y-%m-%dT%H:%i:%s.%fZ') AS scheduledAt,
|
|
20
|
+
r.status,r.result_conversation_id AS resultConversationId,r.read_flag AS readFlag,
|
|
21
|
+
r.error_code AS errorCode,r.error_message AS errorMessage,
|
|
22
|
+
DATE_FORMAT(r.started_at,'%Y-%m-%dT%H:%i:%s.%fZ') AS startedAt,
|
|
23
|
+
DATE_FORMAT(r.finished_at,'%Y-%m-%dT%H:%i:%s.%fZ') AS finishedAt,
|
|
24
|
+
DATE_FORMAT(r.created_at,'%Y-%m-%dT%H:%i:%s.%fZ') AS createdAt
|
|
25
|
+
</sql>
|
|
26
|
+
|
|
27
|
+
<sql id="taskFilter">
|
|
28
|
+
WHERE t.user_id=#{userId} AND t.delete_flag=1
|
|
29
|
+
<if test="keyword != null">
|
|
30
|
+
AND (t.title LIKE #{keyword} ESCAPE '!' OR t.prompt LIKE #{keyword} ESCAPE '!')
|
|
31
|
+
</if>
|
|
32
|
+
<if test="status != null">AND t.status=#{status}</if>
|
|
33
|
+
</sql>
|
|
34
|
+
|
|
35
|
+
<select id="lockUserTasks" resultType="long">
|
|
36
|
+
SELECT id FROM agent_scheduled_task
|
|
37
|
+
WHERE user_id=#{userId} AND delete_flag=1 FOR UPDATE
|
|
38
|
+
</select>
|
|
39
|
+
|
|
40
|
+
<select id="countRunnableTasks" resultType="int">
|
|
41
|
+
SELECT COUNT(*) FROM agent_scheduled_task
|
|
42
|
+
WHERE user_id=#{userId} AND delete_flag=1 AND status IN ('ACTIVE','PAUSED')
|
|
43
|
+
</select>
|
|
44
|
+
|
|
45
|
+
<insert id="insertTask" parameterType="map" useGeneratedKeys="true" keyProperty="id">
|
|
46
|
+
INSERT INTO agent_scheduled_task
|
|
47
|
+
(user_id,org_code,role_id,title,prompt,schedule_type,run_at,cron_expression,interval_seconds,
|
|
48
|
+
timezone,status,next_run_at,delete_flag,created_at,updated_at)
|
|
49
|
+
VALUES
|
|
50
|
+
(#{userId},#{orgCode},#{roleId},#{title},#{prompt},#{scheduleType},#{runAt},#{cronExpression},
|
|
51
|
+
#{intervalSeconds},#{timezone},'ACTIVE',#{nextRunAt},1,
|
|
52
|
+
UTC_TIMESTAMP(3),UTC_TIMESTAMP(3))
|
|
53
|
+
</insert>
|
|
54
|
+
|
|
55
|
+
<update id="updateTask" parameterType="map">
|
|
56
|
+
UPDATE agent_scheduled_task
|
|
57
|
+
SET org_code=#{orgCode},role_id=#{roleId},title=#{title},prompt=#{prompt},schedule_type=#{scheduleType},run_at=#{runAt},
|
|
58
|
+
cron_expression=#{cronExpression},interval_seconds=#{intervalSeconds},timezone=#{timezone},
|
|
59
|
+
next_run_at=CASE WHEN status='ACTIVE' THEN #{nextRunAt} ELSE NULL END,
|
|
60
|
+
updated_at=UTC_TIMESTAMP(3)
|
|
61
|
+
WHERE id=#{taskId} AND user_id=#{userId} AND delete_flag=1
|
|
62
|
+
</update>
|
|
63
|
+
|
|
64
|
+
<select id="countTasks" resultType="long">
|
|
65
|
+
SELECT COUNT(*) FROM agent_scheduled_task t
|
|
66
|
+
<include refid="taskFilter"/>
|
|
67
|
+
</select>
|
|
68
|
+
|
|
69
|
+
<select id="findTasks" resultType="map">
|
|
70
|
+
SELECT <include refid="taskFields"/>,
|
|
71
|
+
(SELECT r.status FROM agent_scheduled_task_run r
|
|
72
|
+
WHERE r.task_id=t.id AND r.delete_flag=1
|
|
73
|
+
ORDER BY r.scheduled_at DESC,r.id DESC LIMIT 1) AS latestRunStatus
|
|
74
|
+
FROM agent_scheduled_task t
|
|
75
|
+
<include refid="taskFilter"/>
|
|
76
|
+
ORDER BY t.updated_at DESC,t.id DESC LIMIT #{limit} OFFSET #{offset}
|
|
77
|
+
</select>
|
|
78
|
+
|
|
79
|
+
<select id="findTaskDetail" resultType="map">
|
|
80
|
+
SELECT <include refid="taskFields"/>,
|
|
81
|
+
(SELECT r.status FROM agent_scheduled_task_run r
|
|
82
|
+
WHERE r.task_id=t.id AND r.delete_flag=1
|
|
83
|
+
ORDER BY r.scheduled_at DESC,r.id DESC LIMIT 1) AS latestRunStatus
|
|
84
|
+
FROM agent_scheduled_task t
|
|
85
|
+
WHERE t.id=#{taskId} AND t.user_id=#{userId} AND t.delete_flag=1
|
|
86
|
+
</select>
|
|
87
|
+
|
|
88
|
+
<select id="findTaskOwner" resultType="map">
|
|
89
|
+
SELECT id,schedule_type AS scheduleType,run_at AS runAt,
|
|
90
|
+
cron_expression AS cronExpression,interval_seconds AS intervalSeconds,
|
|
91
|
+
timezone,status
|
|
92
|
+
FROM agent_scheduled_task
|
|
93
|
+
WHERE id=#{taskId} AND user_id=#{userId} AND delete_flag=1
|
|
94
|
+
<if test="lock">FOR UPDATE</if>
|
|
95
|
+
</select>
|
|
96
|
+
|
|
97
|
+
<select id="countRuns" resultType="long">
|
|
98
|
+
SELECT COUNT(*)
|
|
99
|
+
FROM agent_scheduled_task_run r
|
|
100
|
+
JOIN agent_scheduled_task t ON t.id=r.task_id
|
|
101
|
+
WHERE r.task_id=#{taskId} AND r.delete_flag=1
|
|
102
|
+
AND t.user_id=#{userId} AND t.delete_flag=1
|
|
103
|
+
</select>
|
|
104
|
+
|
|
105
|
+
<select id="findRuns" resultType="map">
|
|
106
|
+
SELECT <include refid="runSummaryFields"/>
|
|
107
|
+
FROM agent_scheduled_task_run r
|
|
108
|
+
JOIN agent_scheduled_task t ON t.id=r.task_id
|
|
109
|
+
WHERE r.task_id=#{taskId} AND r.delete_flag=1
|
|
110
|
+
AND t.user_id=#{userId} AND t.delete_flag=1
|
|
111
|
+
ORDER BY r.scheduled_at DESC,r.id DESC LIMIT #{limit} OFFSET #{offset}
|
|
112
|
+
</select>
|
|
113
|
+
|
|
114
|
+
<select id="findOwnedRun" resultType="map">
|
|
115
|
+
SELECT <include refid="runSummaryFields"/>,r.result_payload AS resultPayload,
|
|
116
|
+
t.title,t.prompt,t.user_id AS userId
|
|
117
|
+
FROM agent_scheduled_task_run r
|
|
118
|
+
JOIN agent_scheduled_task t ON t.id=r.task_id
|
|
119
|
+
WHERE r.id=#{runId} AND r.delete_flag=1
|
|
120
|
+
AND t.user_id=#{userId} AND t.delete_flag=1
|
|
121
|
+
<if test="lock">FOR UPDATE</if>
|
|
122
|
+
</select>
|
|
123
|
+
|
|
124
|
+
<select id="countUnread" resultType="long">
|
|
125
|
+
SELECT COUNT(*)
|
|
126
|
+
FROM agent_scheduled_task_run r
|
|
127
|
+
JOIN agent_scheduled_task t ON t.id=r.task_id
|
|
128
|
+
WHERE t.user_id=#{userId} AND t.delete_flag=1 AND r.delete_flag=1
|
|
129
|
+
AND r.status IN ('SUCCEEDED','FAILED') AND r.read_flag=0
|
|
130
|
+
</select>
|
|
131
|
+
|
|
132
|
+
<select id="findUnread" resultType="map">
|
|
133
|
+
SELECT <include refid="runSummaryFields"/>,t.title
|
|
134
|
+
FROM agent_scheduled_task_run r
|
|
135
|
+
JOIN agent_scheduled_task t ON t.id=r.task_id
|
|
136
|
+
WHERE t.user_id=#{userId} AND t.delete_flag=1 AND r.delete_flag=1
|
|
137
|
+
AND r.status IN ('SUCCEEDED','FAILED') AND r.read_flag=0
|
|
138
|
+
ORDER BY r.finished_at DESC,r.id DESC LIMIT 100
|
|
139
|
+
</select>
|
|
140
|
+
|
|
141
|
+
<update id="activateTask">
|
|
142
|
+
UPDATE agent_scheduled_task
|
|
143
|
+
SET status='ACTIVE',next_run_at=#{nextRunAt},updated_at=UTC_TIMESTAMP(3)
|
|
144
|
+
WHERE id=#{taskId} AND user_id=#{userId} AND delete_flag=1 AND status='PAUSED'
|
|
145
|
+
</update>
|
|
146
|
+
|
|
147
|
+
<update id="pauseTask">
|
|
148
|
+
UPDATE agent_scheduled_task
|
|
149
|
+
SET status='PAUSED',next_run_at=NULL,updated_at=UTC_TIMESTAMP(3)
|
|
150
|
+
WHERE id=#{taskId} AND user_id=#{userId} AND delete_flag=1 AND status='ACTIVE'
|
|
151
|
+
</update>
|
|
152
|
+
|
|
153
|
+
<update id="discardTask">
|
|
154
|
+
UPDATE agent_scheduled_task t
|
|
155
|
+
LEFT JOIN agent_scheduled_task_run r
|
|
156
|
+
ON r.task_id=t.id AND r.delete_flag=1 AND r.status IN ('PENDING','RUNNING')
|
|
157
|
+
SET t.delete_flag=0,t.next_run_at=NULL,t.updated_at=UTC_TIMESTAMP(3),
|
|
158
|
+
r.status='FAILED',r.error_code='scheduled_task_discarded',
|
|
159
|
+
r.error_message='定时任务已删除',r.finished_at=UTC_TIMESTAMP(3),
|
|
160
|
+
r.updated_at=UTC_TIMESTAMP(3),r.execution_token_hash=NULL,
|
|
161
|
+
r.execution_token_expires_at=NULL
|
|
162
|
+
WHERE t.id=#{taskId} AND t.user_id=#{userId} AND t.delete_flag=1
|
|
163
|
+
</update>
|
|
164
|
+
|
|
165
|
+
<select id="findDueTask" resultType="map">
|
|
166
|
+
SELECT id,schedule_type AS scheduleType,run_at AS runAt,
|
|
167
|
+
cron_expression AS cronExpression,interval_seconds AS intervalSeconds,
|
|
168
|
+
timezone,next_run_at AS nextRunAt
|
|
169
|
+
FROM agent_scheduled_task
|
|
170
|
+
WHERE delete_flag=1 AND status='ACTIVE' AND next_run_at<=UTC_TIMESTAMP(3)
|
|
171
|
+
ORDER BY next_run_at,id LIMIT 1 FOR UPDATE SKIP LOCKED
|
|
172
|
+
</select>
|
|
173
|
+
|
|
174
|
+
<insert id="insertRun" parameterType="map" useGeneratedKeys="true" keyProperty="id">
|
|
175
|
+
INSERT INTO agent_scheduled_task_run
|
|
176
|
+
(task_id,scheduled_at,status,read_flag,delete_flag,created_at,updated_at)
|
|
177
|
+
VALUES (#{taskId},#{scheduledAt},'PENDING',0,1,UTC_TIMESTAMP(3),UTC_TIMESTAMP(3))
|
|
178
|
+
</insert>
|
|
179
|
+
|
|
180
|
+
<update id="completeOnceTask">
|
|
181
|
+
UPDATE agent_scheduled_task
|
|
182
|
+
SET status='COMPLETED',next_run_at=NULL,updated_at=UTC_TIMESTAMP(3)
|
|
183
|
+
WHERE id=#{taskId} AND schedule_type='ONCE' AND delete_flag=1
|
|
184
|
+
</update>
|
|
185
|
+
|
|
186
|
+
<update id="updateNextRun">
|
|
187
|
+
UPDATE agent_scheduled_task
|
|
188
|
+
SET next_run_at=#{nextRunAt},updated_at=UTC_TIMESTAMP(3)
|
|
189
|
+
WHERE id=#{taskId}
|
|
190
|
+
</update>
|
|
191
|
+
|
|
192
|
+
<select id="findPendingRunIds" resultType="long">
|
|
193
|
+
SELECT id FROM agent_scheduled_task_run
|
|
194
|
+
WHERE delete_flag=1 AND status='PENDING'
|
|
195
|
+
ORDER BY created_at,id LIMIT #{limit}
|
|
196
|
+
</select>
|
|
197
|
+
|
|
198
|
+
<update id="beginRun">
|
|
199
|
+
UPDATE agent_scheduled_task_run
|
|
200
|
+
SET status='RUNNING',started_at=UTC_TIMESTAMP(3),updated_at=UTC_TIMESTAMP(3),
|
|
201
|
+
execution_token_hash=#{tokenHash},
|
|
202
|
+
execution_token_expires_at=#{tokenExpiresAt}
|
|
203
|
+
WHERE id=#{runId} AND delete_flag=1 AND status='PENDING'
|
|
204
|
+
AND EXISTS (SELECT 1 FROM agent_scheduled_task t
|
|
205
|
+
WHERE t.id=agent_scheduled_task_run.task_id AND t.delete_flag=1)
|
|
206
|
+
</update>
|
|
207
|
+
|
|
208
|
+
<select id="findPendingRunContext" resultType="map">
|
|
209
|
+
SELECT r.id AS runId,r.task_id AS taskId,t.user_id AS userId,t.org_code AS orgCode,
|
|
210
|
+
t.role_id AS roleId
|
|
211
|
+
FROM agent_scheduled_task_run r
|
|
212
|
+
JOIN agent_scheduled_task t ON t.id=r.task_id
|
|
213
|
+
WHERE r.id=#{runId} AND r.delete_flag=1 AND r.status='PENDING' AND t.delete_flag=1
|
|
214
|
+
</select>
|
|
215
|
+
|
|
216
|
+
<select id="resolveScheduledToken" resultType="map">
|
|
217
|
+
SELECT r.id AS runId,r.task_id AS taskId,t.user_id AS userId,t.org_code AS orgCode,
|
|
218
|
+
t.role_id AS roleId,t.prompt,t.timezone,
|
|
219
|
+
DATE_FORMAT(r.scheduled_at,'%Y-%m-%dT%H:%i:%s.%fZ') AS scheduledAt,
|
|
220
|
+
DATE_FORMAT(r.execution_token_expires_at,'%Y-%m-%dT%H:%i:%s.%fZ') AS expiresAt,
|
|
221
|
+
r.execution_token_hash AS tokenHash
|
|
222
|
+
FROM agent_scheduled_task_run r
|
|
223
|
+
JOIN agent_scheduled_task t ON t.id=r.task_id
|
|
224
|
+
WHERE r.execution_token_hash=#{tokenHash}
|
|
225
|
+
AND r.delete_flag=1 AND r.status='RUNNING'
|
|
226
|
+
AND r.execution_token_expires_at>UTC_TIMESTAMP(3)
|
|
227
|
+
AND t.delete_flag=1 AND t.user_id IS NOT NULL AND t.user_id<>''
|
|
228
|
+
AND t.org_code IS NOT NULL AND t.org_code<>''
|
|
229
|
+
AND t.role_id IS NOT NULL AND t.role_id<>''
|
|
230
|
+
</select>
|
|
231
|
+
|
|
232
|
+
<select id="findRunForUpdate" resultType="map">
|
|
233
|
+
SELECT id,task_id AS taskId,status
|
|
234
|
+
FROM agent_scheduled_task_run
|
|
235
|
+
WHERE id=#{runId} AND delete_flag=1 FOR UPDATE
|
|
236
|
+
</select>
|
|
237
|
+
|
|
238
|
+
<update id="completeRun">
|
|
239
|
+
UPDATE agent_scheduled_task_run
|
|
240
|
+
SET status='SUCCEEDED',result_payload=#{resultPayload},
|
|
241
|
+
finished_at=UTC_TIMESTAMP(3),updated_at=UTC_TIMESTAMP(3),
|
|
242
|
+
execution_token_hash=NULL,execution_token_expires_at=NULL
|
|
243
|
+
WHERE id=#{runId} AND delete_flag=1 AND status='RUNNING'
|
|
244
|
+
</update>
|
|
245
|
+
|
|
246
|
+
<update id="failRun">
|
|
247
|
+
UPDATE agent_scheduled_task_run
|
|
248
|
+
SET status='FAILED',error_code=#{errorCode},error_message=#{errorMessage},
|
|
249
|
+
finished_at=UTC_TIMESTAMP(3),updated_at=UTC_TIMESTAMP(3),
|
|
250
|
+
execution_token_hash=NULL,execution_token_expires_at=NULL
|
|
251
|
+
WHERE id=#{runId} AND delete_flag=1 AND status IN ('PENDING','RUNNING')
|
|
252
|
+
</update>
|
|
253
|
+
|
|
254
|
+
<update id="failStaleRuns">
|
|
255
|
+
UPDATE agent_scheduled_task_run r
|
|
256
|
+
JOIN agent_scheduled_task t ON t.id=r.task_id
|
|
257
|
+
SET r.status='FAILED',r.error_code='scheduled_run_timeout',
|
|
258
|
+
r.error_message='定时任务执行超时',r.finished_at=UTC_TIMESTAMP(3),
|
|
259
|
+
r.updated_at=UTC_TIMESTAMP(3),r.execution_token_hash=NULL,
|
|
260
|
+
r.execution_token_expires_at=NULL,
|
|
261
|
+
t.status=CASE WHEN t.schedule_type='ONCE' THEN 'COMPLETED' ELSE t.status END,
|
|
262
|
+
t.updated_at=UTC_TIMESTAMP(3)
|
|
263
|
+
WHERE r.delete_flag=1 AND r.status='RUNNING'
|
|
264
|
+
AND r.started_at<DATE_SUB(UTC_TIMESTAMP(3),INTERVAL #{maxRunSeconds} SECOND)
|
|
265
|
+
</update>
|
|
266
|
+
|
|
267
|
+
<update id="markRunRead">
|
|
268
|
+
UPDATE agent_scheduled_task_run
|
|
269
|
+
SET read_flag=1,updated_at=UTC_TIMESTAMP(3) WHERE id=#{runId}
|
|
270
|
+
</update>
|
|
271
|
+
|
|
272
|
+
<update id="attachConversation">
|
|
273
|
+
UPDATE agent_scheduled_task_run
|
|
274
|
+
SET result_conversation_id=#{conversationId},read_flag=1,updated_at=UTC_TIMESTAMP(3)
|
|
275
|
+
WHERE id=#{runId} AND result_conversation_id IS NULL
|
|
276
|
+
</update>
|
|
277
|
+
|
|
278
|
+
<update id="softDeleteByConversation">
|
|
279
|
+
UPDATE agent_scheduled_task_run r
|
|
280
|
+
JOIN agent_scheduled_task t ON t.id=r.task_id
|
|
281
|
+
SET r.delete_flag=0,r.updated_at=UTC_TIMESTAMP(3)
|
|
282
|
+
WHERE r.result_conversation_id=#{conversationId} AND r.delete_flag=1
|
|
283
|
+
AND t.user_id=#{userId} AND t.delete_flag=1
|
|
284
|
+
</update>
|
|
285
|
+
|
|
286
|
+
</mapper>
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" ?>
|
|
2
|
+
<!DOCTYPE mapper
|
|
3
|
+
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|
4
|
+
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
|
5
|
+
<mapper namespace="com.union.control.mapper.SensitiveDataDemoMapper">
|
|
6
|
+
<insert id="insert">
|
|
7
|
+
INSERT INTO sensitive_data_demo.sensitive_data_demo (phone_number)
|
|
8
|
+
VALUES (#{phoneNumber})
|
|
9
|
+
</insert>
|
|
10
|
+
|
|
11
|
+
<select id="query" resultType="map">
|
|
12
|
+
SELECT id, phone_number AS phoneNumber,
|
|
13
|
+
DATE_FORMAT(created_at, '%Y-%m-%dT%H:%i:%s') AS createdAt
|
|
14
|
+
FROM sensitive_data_demo.sensitive_data_demo
|
|
15
|
+
ORDER BY id DESC
|
|
16
|
+
</select>
|
|
17
|
+
|
|
18
|
+
<insert id="insertAddressBook" useGeneratedKeys="true" keyProperty="id">
|
|
19
|
+
INSERT INTO t_m_announce_address_book
|
|
20
|
+
(name, role, email, telephone, mobile_number)
|
|
21
|
+
VALUES
|
|
22
|
+
(#{name}, #{role}, #{email}, #{telephone}, #{mobileNumber})
|
|
23
|
+
</insert>
|
|
24
|
+
|
|
25
|
+
<select id="queryAddressBook"
|
|
26
|
+
resultType="com.union.control.mapper.SensitiveAddressBookDemo">
|
|
27
|
+
SELECT id, name, role, email, telephone,
|
|
28
|
+
mobile_number AS mobileNumber,
|
|
29
|
+
DATE_FORMAT(created_at, '%Y-%m-%dT%H:%i:%s') AS createdAt
|
|
30
|
+
FROM t_m_announce_address_book
|
|
31
|
+
ORDER BY id DESC
|
|
32
|
+
</select>
|
|
33
|
+
<insert id="insertSaved" useGeneratedKeys="true" keyProperty="id">
|
|
34
|
+
INSERT INTO sensitive_data_demo.sensitive_data_demo (phone_number) VALUES (#{phoneNumber})
|
|
35
|
+
</insert>
|
|
36
|
+
<update id="update">
|
|
37
|
+
UPDATE sensitive_data_demo.sensitive_data_demo SET phone_number = #{phoneNumber} WHERE id = #{id}
|
|
38
|
+
</update>
|
|
39
|
+
<select id="queryById" resultType="map">
|
|
40
|
+
SELECT id, phone_number AS phoneNumber,
|
|
41
|
+
DATE_FORMAT(created_at, '%Y-%m-%dT%H:%i:%s') AS createdAt
|
|
42
|
+
FROM sensitive_data_demo.sensitive_data_demo WHERE id = #{id}
|
|
43
|
+
</select>
|
|
44
|
+
<update id="updateAddressBook">
|
|
45
|
+
UPDATE t_m_announce_address_book
|
|
46
|
+
SET name = #{name}, role = #{role}, email = #{email},
|
|
47
|
+
telephone = #{telephone}, mobile_number = #{mobileNumber}
|
|
48
|
+
WHERE id = #{id}
|
|
49
|
+
</update>
|
|
50
|
+
<select id="queryAddressBookById" resultType="com.union.control.mapper.SensitiveAddressBookDemo">
|
|
51
|
+
SELECT id, name, role, email, telephone, mobile_number AS mobileNumber,
|
|
52
|
+
DATE_FORMAT(created_at, '%Y-%m-%dT%H:%i:%s') AS createdAt
|
|
53
|
+
FROM t_m_announce_address_book WHERE id = #{id}
|
|
54
|
+
</select>
|
|
55
|
+
</mapper>
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
CREATE TABLE IF NOT EXISTS `ai_conversation` (
|
|
2
|
+
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '数据库自增主键',
|
|
3
|
+
`conversation_id` VARCHAR(64) COLLATE utf8mb4_bin NOT NULL COMMENT '业务会话标识,对应 AG-UI threadId',
|
|
4
|
+
`user_id` VARCHAR(64) COLLATE utf8mb4_bin NOT NULL COMMENT '会话所属用户标识',
|
|
5
|
+
`title` VARCHAR(255) DEFAULT NULL COMMENT '会话标题',
|
|
6
|
+
`status` VARCHAR(32) NOT NULL DEFAULT 'active' COMMENT '会话状态:active-活跃,archived-已归档',
|
|
7
|
+
`is_pinned` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否置顶:1-是,0-否',
|
|
8
|
+
`delete_flag` TINYINT(1) NOT NULL DEFAULT 1 COMMENT '删除标记:1-有效,0-已删除',
|
|
9
|
+
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
|
10
|
+
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后更新时间',
|
|
11
|
+
PRIMARY KEY (`id`),
|
|
12
|
+
UNIQUE KEY `uk_conversation_id` (`conversation_id`),
|
|
13
|
+
KEY `idx_conversation_user_list` (`user_id`, `delete_flag`, `status`, `updated_at`)
|
|
14
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
|
15
|
+
|
|
16
|
+
CREATE TABLE IF NOT EXISTS `ai_agent_execution` (
|
|
17
|
+
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '数据库自增主键',
|
|
18
|
+
`run_id` VARCHAR(64) COLLATE utf8mb4_bin NOT NULL COMMENT 'Agent 执行业务标识',
|
|
19
|
+
`conversation_id` VARCHAR(64) COLLATE utf8mb4_bin NOT NULL COMMENT '所属业务会话标识',
|
|
20
|
+
`user_id` VARCHAR(64) COLLATE utf8mb4_bin NOT NULL COMMENT '执行所属用户标识',
|
|
21
|
+
`parent_execution_id` BIGINT DEFAULT NULL COMMENT '父执行数据库主键,根执行为空',
|
|
22
|
+
`agent_name` VARCHAR(128) COLLATE utf8mb4_bin DEFAULT NULL COMMENT 'py-app 最终提交的 Agent 名称',
|
|
23
|
+
`delegation_tool_call_id` VARCHAR(128) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '父执行发起委派的工具调用标识',
|
|
24
|
+
`task` VARCHAR(1000) DEFAULT NULL COMMENT '委派给 Agent 的任务说明',
|
|
25
|
+
`status` VARCHAR(32) NOT NULL COMMENT 'py-app 提交的执行状态',
|
|
26
|
+
`finished_at` DATETIME DEFAULT NULL COMMENT '执行结束时间',
|
|
27
|
+
`error_code` VARCHAR(64) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '执行失败错误码',
|
|
28
|
+
`delete_flag` TINYINT(1) NOT NULL DEFAULT 1 COMMENT '删除标记:1-有效,0-已删除',
|
|
29
|
+
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
|
30
|
+
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后更新时间',
|
|
31
|
+
PRIMARY KEY (`id`),
|
|
32
|
+
UNIQUE KEY `uk_agent_execution_run` (`run_id`),
|
|
33
|
+
UNIQUE KEY `uk_parent_delegation_call` (`parent_execution_id`, `delegation_tool_call_id`),
|
|
34
|
+
KEY `idx_execution_conversation` (`conversation_id`, `delete_flag`, `created_at`),
|
|
35
|
+
KEY `idx_execution_parent` (`parent_execution_id`, `delete_flag`, `created_at`),
|
|
36
|
+
KEY `idx_execution_user_status` (`user_id`, `delete_flag`, `status`),
|
|
37
|
+
CONSTRAINT `fk_execution_conversation` FOREIGN KEY (`conversation_id`)
|
|
38
|
+
REFERENCES `ai_conversation` (`conversation_id`),
|
|
39
|
+
CONSTRAINT `fk_execution_parent` FOREIGN KEY (`parent_execution_id`)
|
|
40
|
+
REFERENCES `ai_agent_execution` (`id`)
|
|
41
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
|
42
|
+
|
|
43
|
+
CREATE TABLE IF NOT EXISTS `ai_conversation_message` (
|
|
44
|
+
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '数据库自增主键',
|
|
45
|
+
`message_id` VARCHAR(128) COLLATE utf8mb4_bin NOT NULL COMMENT '消息业务标识',
|
|
46
|
+
`conversation_id` VARCHAR(64) COLLATE utf8mb4_bin NOT NULL COMMENT '所属业务会话标识',
|
|
47
|
+
`user_id` VARCHAR(64) COLLATE utf8mb4_bin NOT NULL COMMENT '消息所属用户标识',
|
|
48
|
+
`agent_execution_id` BIGINT NOT NULL COMMENT '消息所属执行数据库主键',
|
|
49
|
+
`role` VARCHAR(32) COLLATE utf8mb4_bin NOT NULL COMMENT '消息角色:user-用户,assistant-助手,tool-工具结果,system-系统,developer-开发者,reasoning-推理',
|
|
50
|
+
`sequence_no` BIGINT NOT NULL COMMENT '会话范围内的消息顺序号',
|
|
51
|
+
`payload` JSON NOT NULL COMMENT '消息角色特有字段 JSON',
|
|
52
|
+
`delete_flag` TINYINT(1) NOT NULL DEFAULT 1 COMMENT '删除标记:1-有效,0-已删除',
|
|
53
|
+
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
|
54
|
+
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后更新时间',
|
|
55
|
+
PRIMARY KEY (`id`),
|
|
56
|
+
UNIQUE KEY `uk_conversation_message` (`conversation_id`, `message_id`),
|
|
57
|
+
UNIQUE KEY `uk_conversation_sequence` (`conversation_id`, `sequence_no`),
|
|
58
|
+
KEY `idx_message_history` (`conversation_id`, `user_id`, `delete_flag`, `sequence_no`),
|
|
59
|
+
KEY `idx_message_execution` (`agent_execution_id`, `delete_flag`, `sequence_no`),
|
|
60
|
+
CONSTRAINT `fk_message_conversation` FOREIGN KEY (`conversation_id`)
|
|
61
|
+
REFERENCES `ai_conversation` (`conversation_id`),
|
|
62
|
+
CONSTRAINT `fk_message_execution` FOREIGN KEY (`agent_execution_id`)
|
|
63
|
+
REFERENCES `ai_agent_execution` (`id`)
|
|
64
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
|
65
|
+
|
|
66
|
+
CREATE TABLE IF NOT EXISTS `ai_memory_file` (
|
|
67
|
+
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '数据库自增主键',
|
|
68
|
+
`user_id` VARCHAR(64) COLLATE utf8mb4_bin NOT NULL COMMENT '记忆文件所属用户标识',
|
|
69
|
+
`path` VARCHAR(512) COLLATE utf8mb4_bin NOT NULL COMMENT '用户命名空间内的文件路径',
|
|
70
|
+
`content` MEDIUMTEXT NOT NULL COMMENT '记忆文件内容',
|
|
71
|
+
`version` BIGINT NOT NULL COMMENT '文件内容版本号',
|
|
72
|
+
`last_operation_id` VARCHAR(128) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '最后一次修改文件的幂等操作标识',
|
|
73
|
+
`delete_flag` TINYINT(1) NOT NULL DEFAULT 1 COMMENT '删除标记:1-有效,0-已删除',
|
|
74
|
+
`active_path` VARCHAR(512) COLLATE utf8mb4_bin GENERATED ALWAYS AS (
|
|
75
|
+
CASE WHEN `delete_flag` = 1 THEN `path` ELSE NULL END
|
|
76
|
+
) STORED COMMENT '有效文件路径,用于实现软删除后的路径唯一约束',
|
|
77
|
+
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
|
78
|
+
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后更新时间',
|
|
79
|
+
PRIMARY KEY (`id`),
|
|
80
|
+
UNIQUE KEY `uk_memory_file_active_path` (`user_id`, `active_path`),
|
|
81
|
+
KEY `idx_memory_file_prefix` (`user_id`, `delete_flag`, `path`(191))
|
|
82
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
|
83
|
+
|
|
84
|
+
CREATE TABLE IF NOT EXISTS `ai_memory_operation` (
|
|
85
|
+
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '数据库自增主键',
|
|
86
|
+
`user_id` VARCHAR(64) COLLATE utf8mb4_bin NOT NULL COMMENT '操作所属用户标识',
|
|
87
|
+
`operation_id` VARCHAR(128) COLLATE utf8mb4_bin NOT NULL COMMENT '客户端幂等操作标识',
|
|
88
|
+
`fingerprint` VARCHAR(128) COLLATE utf8mb4_bin NOT NULL COMMENT '操作请求内容指纹',
|
|
89
|
+
`result_version` BIGINT DEFAULT NULL COMMENT '操作结果版本号,删除操作为空',
|
|
90
|
+
`existed` TINYINT(1) NOT NULL COMMENT '操作前文件是否存在:1-存在,0-不存在',
|
|
91
|
+
`delete_flag` TINYINT(1) NOT NULL DEFAULT 1 COMMENT '删除标记:1-有效,0-已删除',
|
|
92
|
+
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
|
93
|
+
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后更新时间',
|
|
94
|
+
PRIMARY KEY (`id`),
|
|
95
|
+
UNIQUE KEY `uk_memory_operation` (`user_id`, `operation_id`)
|
|
96
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
|
97
|
+
|
|
98
|
+
CREATE TABLE IF NOT EXISTS `agent_scheduled_task` (
|
|
99
|
+
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '数据库自增主键',
|
|
100
|
+
`user_id` VARCHAR(64) COLLATE utf8mb4_bin NOT NULL COMMENT '任务所属用户标识',
|
|
101
|
+
`org_code` VARCHAR(64) COLLATE utf8mb4_bin NOT NULL COMMENT '任务创建时的机构快照',
|
|
102
|
+
`role_id` VARCHAR(64) COLLATE utf8mb4_bin NOT NULL COMMENT '任务创建或编辑时的Ark系统角色快照',
|
|
103
|
+
`title` VARCHAR(255) NOT NULL COMMENT '任务标题',
|
|
104
|
+
`prompt` TEXT NOT NULL COMMENT '每次执行使用的自然语言任务提示',
|
|
105
|
+
`schedule_type` VARCHAR(16) COLLATE utf8mb4_bin NOT NULL COMMENT '调度类型:ONCE、CRON、INTERVAL',
|
|
106
|
+
`run_at` DATETIME(3) DEFAULT NULL COMMENT '单次执行时间或间隔锚点,UTC',
|
|
107
|
+
`cron_expression` VARCHAR(128) COLLATE utf8mb4_bin DEFAULT NULL COMMENT 'Spring 六字段 cron 表达式',
|
|
108
|
+
`interval_seconds` BIGINT UNSIGNED DEFAULT NULL COMMENT '固定执行间隔秒数',
|
|
109
|
+
`timezone` VARCHAR(64) COLLATE utf8mb4_bin NOT NULL COMMENT '任务 IANA 时区',
|
|
110
|
+
`status` VARCHAR(16) COLLATE utf8mb4_bin NOT NULL COMMENT '任务状态:ACTIVE、PAUSED、COMPLETED',
|
|
111
|
+
`next_run_at` DATETIME(3) DEFAULT NULL COMMENT '下一次计划执行时间,UTC',
|
|
112
|
+
`delete_flag` TINYINT(1) NOT NULL DEFAULT 1 COMMENT '删除标记:1-有效,0-已删除',
|
|
113
|
+
`created_at` DATETIME(3) NOT NULL COMMENT '创建时间,UTC',
|
|
114
|
+
`updated_at` DATETIME(3) NOT NULL COMMENT '最后更新时间,UTC',
|
|
115
|
+
PRIMARY KEY (`id`),
|
|
116
|
+
KEY `idx_scheduled_task_due` (`delete_flag`, `status`, `next_run_at`),
|
|
117
|
+
KEY `idx_scheduled_task_user` (`user_id`, `delete_flag`, `updated_at`)
|
|
118
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
|
119
|
+
|
|
120
|
+
CREATE TABLE IF NOT EXISTS `agent_scheduled_task_run` (
|
|
121
|
+
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '数据库自增主键',
|
|
122
|
+
`task_id` BIGINT UNSIGNED NOT NULL COMMENT '所属定时任务主键',
|
|
123
|
+
`scheduled_at` DATETIME(3) NOT NULL COMMENT '本次计划执行时间,UTC',
|
|
124
|
+
`status` VARCHAR(16) COLLATE utf8mb4_bin NOT NULL COMMENT '运行状态:PENDING、RUNNING、SUCCEEDED、FAILED',
|
|
125
|
+
`result_payload` LONGTEXT DEFAULT NULL COMMENT 'py-app 返回的标准结果 JSON',
|
|
126
|
+
`result_conversation_id` VARCHAR(64) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '用户打开结果后创建的会话标识',
|
|
127
|
+
`read_flag` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '结果读取标记:1-已打开,0-未打开',
|
|
128
|
+
`error_code` VARCHAR(64) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '稳定错误码',
|
|
129
|
+
`error_message` VARCHAR(512) DEFAULT NULL COMMENT '用户安全错误信息',
|
|
130
|
+
`execution_token_hash` CHAR(64) CHARACTER SET ascii COLLATE ascii_bin DEFAULT NULL COMMENT '单次运行 token 的 SHA-256 小写十六进制哈希',
|
|
131
|
+
`execution_token_expires_at` DATETIME(3) DEFAULT NULL COMMENT '单次运行 token 的失效时间,UTC',
|
|
132
|
+
`started_at` DATETIME(3) DEFAULT NULL COMMENT '实际开始时间,UTC',
|
|
133
|
+
`finished_at` DATETIME(3) DEFAULT NULL COMMENT '实际结束时间,UTC',
|
|
134
|
+
`delete_flag` TINYINT(1) NOT NULL DEFAULT 1 COMMENT '删除标记:1-有效,0-已删除',
|
|
135
|
+
`created_at` DATETIME(3) NOT NULL COMMENT '创建时间,UTC',
|
|
136
|
+
`updated_at` DATETIME(3) NOT NULL COMMENT '最后更新时间,UTC',
|
|
137
|
+
PRIMARY KEY (`id`),
|
|
138
|
+
UNIQUE KEY `uk_scheduled_run_occurrence` (`task_id`, `scheduled_at`),
|
|
139
|
+
UNIQUE KEY `uk_scheduled_run_token_hash` (`execution_token_hash`),
|
|
140
|
+
KEY `idx_scheduled_run_task` (`task_id`, `delete_flag`, `scheduled_at`),
|
|
141
|
+
KEY `idx_scheduled_run_status` (`delete_flag`, `status`, `created_at`),
|
|
142
|
+
KEY `idx_scheduled_run_unread` (`delete_flag`, `status`, `read_flag`, `finished_at`),
|
|
143
|
+
KEY `idx_scheduled_run_conversation` (`result_conversation_id`),
|
|
144
|
+
CONSTRAINT `fk_scheduled_run_task` FOREIGN KEY (`task_id`)
|
|
145
|
+
REFERENCES `agent_scheduled_task` (`id`)
|
|
146
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
package com.union.control;
|
|
2
|
+
|
|
3
|
+
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
4
|
+
import com.union.control.mapper.AgentExecutionMapper;
|
|
5
|
+
import com.union.control.mapper.ConversationMapper;
|
|
6
|
+
import com.union.control.service.impl.AgentExecutionServiceImpl;
|
|
7
|
+
import org.junit.Before;
|
|
8
|
+
import org.junit.Test;
|
|
9
|
+
import org.mockito.InOrder;
|
|
10
|
+
|
|
11
|
+
import java.util.ArrayList;
|
|
12
|
+
import java.util.Arrays;
|
|
13
|
+
import java.util.Collections;
|
|
14
|
+
import java.util.LinkedHashMap;
|
|
15
|
+
import java.util.List;
|
|
16
|
+
import java.util.Map;
|
|
17
|
+
|
|
18
|
+
import static org.mockito.Mockito.mock;
|
|
19
|
+
import static org.mockito.Mockito.inOrder;
|
|
20
|
+
import static org.mockito.Mockito.never;
|
|
21
|
+
import static org.mockito.Mockito.verify;
|
|
22
|
+
import static org.mockito.Mockito.when;
|
|
23
|
+
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
|
24
|
+
|
|
25
|
+
public class AgentExecutionServiceTest {
|
|
26
|
+
private AgentExecutionServiceImpl service;
|
|
27
|
+
private AgentExecutionMapper executions;
|
|
28
|
+
private ConversationMapper conversations;
|
|
29
|
+
|
|
30
|
+
@Before
|
|
31
|
+
public void setUp() {
|
|
32
|
+
executions = mock(AgentExecutionMapper.class);
|
|
33
|
+
conversations = mock(ConversationMapper.class);
|
|
34
|
+
service = new AgentExecutionServiceImpl(executions, conversations, new ObjectMapper());
|
|
35
|
+
when(conversations.requireOwned("thread-1", TestJson.USER_ID, true)).thenReturn(1L);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
@Test
|
|
39
|
+
public void aguiRunLeavesFrontendFeatureValidationToPyApp() {
|
|
40
|
+
String body = "{\"threadId\":\"thread-1\",\"runId\":\"run-1\"," +
|
|
41
|
+
"\"messages\":[{\"id\":\"m1\",\"role\":\"user\",\"content\":\"hello\"}]," +
|
|
42
|
+
"\"state\":{},\"context\":[],\"forwardedProps\":{}," +
|
|
43
|
+
"\"tools\":[{\"name\":\"unsafe\"}]}";
|
|
44
|
+
when(conversations.requireOwnedActive("thread-1", TestJson.USER_ID)).thenReturn(1L);
|
|
45
|
+
when(executions.findExecution(TestJson.USER_ID, "thread-1", "run-1", false))
|
|
46
|
+
.thenReturn(Collections.singletonList(root("running", null)));
|
|
47
|
+
|
|
48
|
+
service.claimAguiRun(TestJson.request(body));
|
|
49
|
+
|
|
50
|
+
verify(executions).insertRootExecution("run-1", "thread-1", TestJson.USER_ID);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
@Test
|
|
54
|
+
public void aguiRunRejectsAnExistingConversationOwnedByAnotherUser() {
|
|
55
|
+
String body = "{\"threadId\":\"foreign-thread\",\"runId\":\"run-1\"," +
|
|
56
|
+
"\"messages\":[],\"state\":{},\"context\":[],\"forwardedProps\":{}}";
|
|
57
|
+
when(conversations.requireOwnedActive("foreign-thread", TestJson.USER_ID))
|
|
58
|
+
.thenReturn(null);
|
|
59
|
+
|
|
60
|
+
assertThatThrownBy(() -> service.claimAguiRun(TestJson.request(body)))
|
|
61
|
+
.isInstanceOf(java.util.NoSuchElementException.class);
|
|
62
|
+
verify(executions, never()).insertRootExecution(
|
|
63
|
+
"run-1", "foreign-thread", TestJson.USER_ID);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
@Test
|
|
67
|
+
public void completionPersistsTheStatusChosenByPyApp() {
|
|
68
|
+
when(executions.findExecution(TestJson.USER_ID, "thread-1", "run-1", true))
|
|
69
|
+
.thenReturn(Collections.singletonList(root("running", null)));
|
|
70
|
+
when(executions.finishExecution(7L, "sdk_terminal", null)).thenReturn(1);
|
|
71
|
+
Map<String, Object> rootExecution =
|
|
72
|
+
execution("run-1", null, "UnionCoordinatorAgent", null, null);
|
|
73
|
+
rootExecution.put("status", "sdk_terminal");
|
|
74
|
+
Map<String, Object> payload = completion(rootExecution);
|
|
75
|
+
|
|
76
|
+
service.completeRun(TestJson.request(payload));
|
|
77
|
+
|
|
78
|
+
verify(executions).finishExecution(7L, "sdk_terminal", null);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
@Test
|
|
82
|
+
public void completionLocksTheConversationBeforeAllocatingMessageSequences() {
|
|
83
|
+
when(executions.findExecution(TestJson.USER_ID, "thread-1", "run-1", true))
|
|
84
|
+
.thenReturn(Collections.singletonList(root("running", null)));
|
|
85
|
+
when(executions.finishExecution(7L, "completed", null)).thenReturn(1);
|
|
86
|
+
|
|
87
|
+
service.completeRun(TestJson.request(completion(
|
|
88
|
+
execution("run-1", null, "KnowledgeAgent", null, null))));
|
|
89
|
+
|
|
90
|
+
InOrder order = inOrder(conversations, executions);
|
|
91
|
+
order.verify(conversations).requireOwned("thread-1", TestJson.USER_ID, true);
|
|
92
|
+
order.verify(executions).findExecution(
|
|
93
|
+
TestJson.USER_ID, "thread-1", "run-1", true);
|
|
94
|
+
order.verify(conversations).currentMessageSequence("thread-1");
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
@Test
|
|
98
|
+
public void completionSelectsTheRootAgentWithoutAnExtraCallback() {
|
|
99
|
+
when(executions.findExecution(TestJson.USER_ID, "thread-1", "run-1", true))
|
|
100
|
+
.thenReturn(Collections.singletonList(root("running", null)));
|
|
101
|
+
when(executions.finishExecution(7L, "completed", null)).thenReturn(1);
|
|
102
|
+
Map<String, Object> payload = completion(
|
|
103
|
+
execution("run-1", null, "KnowledgeAgent", null, null));
|
|
104
|
+
|
|
105
|
+
service.completeRun(TestJson.request(payload));
|
|
106
|
+
|
|
107
|
+
verify(executions).updateRootAgent(7L, "KnowledgeAgent");
|
|
108
|
+
verify(executions).finishExecution(7L, "completed", null);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
@Test
|
|
112
|
+
public void completionDoesNotLimitMessageCount() {
|
|
113
|
+
when(executions.findExecution(TestJson.USER_ID, "thread-1", "run-1", true))
|
|
114
|
+
.thenReturn(Collections.singletonList(root("running", null)));
|
|
115
|
+
when(executions.finishExecution(7L, "completed", null)).thenReturn(1);
|
|
116
|
+
Map<String, Object> payload = completion(
|
|
117
|
+
execution("run-1", null, "UnionCoordinatorAgent", null, null));
|
|
118
|
+
List<Map<String, Object>> messages = new ArrayList<>();
|
|
119
|
+
for (int index = 0; index < 1001; index++) {
|
|
120
|
+
Map<String, Object> message = new LinkedHashMap<>();
|
|
121
|
+
message.put("id", "message-" + index);
|
|
122
|
+
message.put("role", "assistant");
|
|
123
|
+
message.put("content", "value");
|
|
124
|
+
Map<String, Object> envelope = new LinkedHashMap<>();
|
|
125
|
+
envelope.put("runId", "run-1");
|
|
126
|
+
envelope.put("message", message);
|
|
127
|
+
messages.add(envelope);
|
|
128
|
+
}
|
|
129
|
+
payload.put("messages", messages);
|
|
130
|
+
|
|
131
|
+
service.completeRun(TestJson.request(payload));
|
|
132
|
+
|
|
133
|
+
verify(conversations).touchConversation("thread-1", TestJson.USER_ID);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
private static Map<String, Object> completion(Map<String, Object>... executions) {
|
|
137
|
+
Map<String, Object> payload = new LinkedHashMap<>();
|
|
138
|
+
payload.put("conversationId", "thread-1");
|
|
139
|
+
payload.put("runId", "run-1");
|
|
140
|
+
payload.put("executions", Arrays.asList(executions));
|
|
141
|
+
payload.put("messages", Collections.emptyList());
|
|
142
|
+
return payload;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
private static Map<String, Object> execution(
|
|
146
|
+
String runId, String parentRunId, String agentName,
|
|
147
|
+
String callId, String task) {
|
|
148
|
+
Map<String, Object> execution = new LinkedHashMap<>();
|
|
149
|
+
execution.put("runId", runId);
|
|
150
|
+
execution.put("parentRunId", parentRunId);
|
|
151
|
+
execution.put("agentName", agentName);
|
|
152
|
+
execution.put("delegationToolCallId", callId);
|
|
153
|
+
execution.put("task", task);
|
|
154
|
+
execution.put("status", "completed");
|
|
155
|
+
execution.put("errorCode", null);
|
|
156
|
+
return execution;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
private static Map<String, Object> root(String status, String errorCode) {
|
|
160
|
+
Map<String, Object> root = new LinkedHashMap<>();
|
|
161
|
+
root.put("id", 7L);
|
|
162
|
+
root.put("runId", "run-1");
|
|
163
|
+
root.put("conversationId", "thread-1");
|
|
164
|
+
root.put("parentExecutionId", null);
|
|
165
|
+
root.put("agentName", null);
|
|
166
|
+
root.put("status", status);
|
|
167
|
+
root.put("errorCode", errorCode);
|
|
168
|
+
return root;
|
|
169
|
+
}
|
|
170
|
+
}
|