From c35fd0741b8d1fc1a9b845d0e3fd762425623596 Mon Sep 17 00:00:00 2001 From: let5sne Date: Sat, 25 Jul 2026 14:19:03 +0800 Subject: [PATCH] feat(broadcast): add multi-role insight contract --- .../aihr/broadcast/AihrBroadcastDto.java | 46 ++++++++++++++- .../AihrBroadcastAttachmentContractTest.java | 4 ++ ...25_broadcast_multi_role_insight_mysql8.sql | 56 +++++++++++++++++++ docs/BRD_PRODUCTION_MIGRATION_RUNBOOK.md | 1 + scripts/tests/aihr-schema-migrations.test.sh | 8 +++ 5 files changed, 114 insertions(+), 1 deletion(-) create mode 100644 backend/script/sql/update/aihr_20260725_broadcast_multi_role_insight_mysql8.sql diff --git a/backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/broadcast/AihrBroadcastDto.java b/backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/broadcast/AihrBroadcastDto.java index e70acd8a..e44fc50d 100644 --- a/backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/broadcast/AihrBroadcastDto.java +++ b/backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/broadcast/AihrBroadcastDto.java @@ -61,8 +61,52 @@ public final class AihrBroadcastDto { String status, String summary, String errorMessage, - String downloadUrl + String downloadUrl, + String insightStatus, + String defaultPerspectiveCode, + List perspectiveLabels, + List perspectives ) { + public BroadcastAttachmentResponse { + perspectiveLabels = perspectiveLabels == null ? List.of() : List.copyOf(perspectiveLabels); + perspectives = perspectives == null ? List.of() : List.copyOf(perspectives); + } + + public BroadcastAttachmentResponse( + Long id, + String fileName, + long fileSize, + String contentType, + String status, + String summary, + String errorMessage, + String downloadUrl + ) { + this(id, fileName, fileSize, contentType, status, summary, errorMessage, downloadUrl, + "NOT_PROCESSED", null, List.of(), List.of()); + } + } + + public record BroadcastEvidence(int paragraphIndex, String quote) { + } + + public record BroadcastPerspective( + String code, + String label, + String summary, + List concerns, + List impacts, + List actions, + List risks, + List evidence + ) { + public BroadcastPerspective { + concerns = concerns == null ? List.of() : List.copyOf(concerns); + impacts = impacts == null ? List.of() : List.copyOf(impacts); + actions = actions == null ? List.of() : List.copyOf(actions); + risks = risks == null ? List.of() : List.copyOf(risks); + evidence = evidence == null ? List.of() : List.copyOf(evidence); + } } public record AdminBroadcastListResponse( diff --git a/backend/ruoyi-modules/ruoyi-aihr/src/test/java/org/dromara/aihr/broadcast/AihrBroadcastAttachmentContractTest.java b/backend/ruoyi-modules/ruoyi-aihr/src/test/java/org/dromara/aihr/broadcast/AihrBroadcastAttachmentContractTest.java index f6839a6f..1b7f0ff5 100644 --- a/backend/ruoyi-modules/ruoyi-aihr/src/test/java/org/dromara/aihr/broadcast/AihrBroadcastAttachmentContractTest.java +++ b/backend/ruoyi-modules/ruoyi-aihr/src/test/java/org/dromara/aihr/broadcast/AihrBroadcastAttachmentContractTest.java @@ -29,6 +29,10 @@ class AihrBroadcastAttachmentContractTest { assertTrue(context.contains("消息正文")); assertTrue(context.contains("原文件提取内容")); assertFalse(responseFields.contains("extractedText")); + assertFalse(responseFields.contains("insightsJson")); + assertTrue(responseFields.contains("insightStatus")); + assertTrue(responseFields.contains("defaultPerspectiveCode")); + assertTrue(responseFields.contains("perspectives")); } @Test diff --git a/backend/script/sql/update/aihr_20260725_broadcast_multi_role_insight_mysql8.sql b/backend/script/sql/update/aihr_20260725_broadcast_multi_role_insight_mysql8.sql new file mode 100644 index 00000000..c466ce2d --- /dev/null +++ b/backend/script/sql/update/aihr_20260725_broadcast_multi_role_insight_mysql8.sql @@ -0,0 +1,56 @@ +-- 公司消息 R4:同一公司文件的多岗位 AI 解读。 +-- 可重复执行,需在 aihr_20260724_broadcast_attachment_mysql8.sql 之后执行。 + +SET @has_broadcast_insight_status := ( + SELECT COUNT(*) FROM information_schema.columns + WHERE table_schema = DATABASE() AND table_name = 'aihr_broadcast_attachment' + AND column_name = 'insight_status' +); +SET @broadcast_insight_ddl := IF( + @has_broadcast_insight_status = 0, + 'ALTER TABLE aihr_broadcast_attachment ADD COLUMN insight_status varchar(20) NOT NULL DEFAULT ''NOT_PROCESSED'' COMMENT ''NOT_PROCESSED/PENDING/PROCESSING/READY/PARTIAL/FAILED'' AFTER summary', + 'SELECT 1' +); +PREPARE broadcast_insight_stmt FROM @broadcast_insight_ddl; EXECUTE broadcast_insight_stmt; DEALLOCATE PREPARE broadcast_insight_stmt; + +SET @has_broadcast_insights_json := ( + SELECT COUNT(*) FROM information_schema.columns + WHERE table_schema = DATABASE() AND table_name = 'aihr_broadcast_attachment' + AND column_name = 'insights_json' +); +SET @broadcast_insight_ddl := IF( + @has_broadcast_insights_json = 0, + 'ALTER TABLE aihr_broadcast_attachment ADD COLUMN insights_json longtext DEFAULT NULL COMMENT ''经原文引用校验的多岗位解读 JSON'' AFTER insight_status', + 'SELECT 1' +); +PREPARE broadcast_insight_stmt FROM @broadcast_insight_ddl; EXECUTE broadcast_insight_stmt; DEALLOCATE PREPARE broadcast_insight_stmt; + +SET @has_broadcast_insight_version := ( + SELECT COUNT(*) FROM information_schema.columns + WHERE table_schema = DATABASE() AND table_name = 'aihr_broadcast_attachment' + AND column_name = 'insight_version' +); +SET @broadcast_insight_ddl := IF( + @has_broadcast_insight_version = 0, + 'ALTER TABLE aihr_broadcast_attachment ADD COLUMN insight_version varchar(30) DEFAULT NULL COMMENT ''解读规则版本'' AFTER insights_json', + 'SELECT 1' +); +PREPARE broadcast_insight_stmt FROM @broadcast_insight_ddl; EXECUTE broadcast_insight_stmt; DEALLOCATE PREPARE broadcast_insight_stmt; + +SET @has_broadcast_insight_queue_index := ( + SELECT COUNT(*) FROM information_schema.statistics + WHERE table_schema = DATABASE() AND table_name = 'aihr_broadcast_attachment' + AND index_name = 'idx_aihr_broadcast_attachment_insight_queue' +); +SET @broadcast_insight_ddl := IF( + @has_broadcast_insight_queue_index = 0, + 'ALTER TABLE aihr_broadcast_attachment ADD KEY idx_aihr_broadcast_attachment_insight_queue (insight_status, status, update_time)', + 'SELECT 1' +); +PREPARE broadcast_insight_stmt FROM @broadcast_insight_ddl; EXECUTE broadcast_insight_stmt; DEALLOCATE PREPARE broadcast_insight_stmt; + +SET @has_broadcast_insight_status := NULL; +SET @has_broadcast_insights_json := NULL; +SET @has_broadcast_insight_version := NULL; +SET @has_broadcast_insight_queue_index := NULL; +SET @broadcast_insight_ddl := NULL; diff --git a/docs/BRD_PRODUCTION_MIGRATION_RUNBOOK.md b/docs/BRD_PRODUCTION_MIGRATION_RUNBOOK.md index 233e1f00..59daeb6e 100644 --- a/docs/BRD_PRODUCTION_MIGRATION_RUNBOOK.md +++ b/docs/BRD_PRODUCTION_MIGRATION_RUNBOOK.md @@ -102,6 +102,7 @@ mysql --default-character-set=utf8mb4 "$DB_NAME" < backend/script/sql/update/aih mysql --default-character-set=utf8mb4 "$DB_NAME" < backend/script/sql/update/aihr_20260723_broadcast_targeting_mysql8.sql mysql --default-character-set=utf8mb4 "$DB_NAME" < backend/script/sql/update/aihr_20260724_direct_feedback_mysql8.sql mysql --default-character-set=utf8mb4 "$DB_NAME" < backend/script/sql/update/aihr_20260724_broadcast_attachment_mysql8.sql +mysql --default-character-set=utf8mb4 "$DB_NAME" < backend/script/sql/update/aihr_20260725_broadcast_multi_role_insight_mysql8.sql mysql --default-character-set=utf8mb4 "$DB_NAME" < backend/script/sql/update/aihr_20260722_broadcast_question_context_mysql8.sql mysql --default-character-set=utf8mb4 "$DB_NAME" < backend/script/sql/update/aihr_20260724_agent_orchestration_mysql8.sql mysql --default-character-set=utf8mb4 "$DB_NAME" < backend/script/sql/update/aihr_20260725_deepseek_v4_model_mysql8.sql diff --git a/scripts/tests/aihr-schema-migrations.test.sh b/scripts/tests/aihr-schema-migrations.test.sh index 11fe3858..5c9ed9e6 100755 --- a/scripts/tests/aihr-schema-migrations.test.sh +++ b/scripts/tests/aihr-schema-migrations.test.sh @@ -462,11 +462,19 @@ direct_feedback_roles="$(mysql "$DB" -N -B -e "SELECT COUNT(*) FROM sys_role WHE test "$direct_feedback_roles" = '5' broadcast_attachment_migration="$ROOT_DIR/backend/script/sql/update/aihr_20260724_broadcast_attachment_mysql8.sql" +multi_role_insight_migration="$ROOT_DIR/backend/script/sql/update/aihr_20260725_broadcast_multi_role_insight_mysql8.sql" test -f "$broadcast_attachment_migration" +test -f "$multi_role_insight_migration" mysql "$DB" < "$broadcast_attachment_migration" mysql "$DB" < "$broadcast_attachment_migration" +mysql "$DB" < "$multi_role_insight_migration" +mysql "$DB" < "$multi_role_insight_migration" broadcast_attachment_columns="$(mysql "$DB" -N -B -e "SELECT COUNT(*) FROM information_schema.columns WHERE table_schema='$DB' AND table_name='aihr_broadcast_attachment' AND column_name IN ('tenant_id','message_id','oss_id','file_name','file_size','content_type','status','extracted_text','summary','error_message','uploaded_by');")" test "$broadcast_attachment_columns" = '11' +broadcast_insight_columns="$(mysql "$DB" -N -B -e "SELECT COUNT(*) FROM information_schema.columns WHERE table_schema='$DB' AND table_name='aihr_broadcast_attachment' AND column_name IN ('insight_status','insights_json','insight_version');")" +test "$broadcast_insight_columns" = '3' +broadcast_insight_queue_index="$(mysql "$DB" -N -B -e "SELECT GROUP_CONCAT(column_name ORDER BY seq_in_index) FROM information_schema.statistics WHERE table_schema='$DB' AND table_name='aihr_broadcast_attachment' AND index_name='idx_aihr_broadcast_attachment_insight_queue';")" +test "$broadcast_insight_queue_index" = 'insight_status,status,update_time' broadcast_attachment_message_column="$(mysql "$DB" -N -B -e "SELECT CONCAT(column_type,'|',is_nullable) FROM information_schema.columns WHERE table_schema='$DB' AND table_name='aihr_broadcast_message' AND column_name='attachment_id';")" test "$broadcast_attachment_message_column" = 'bigint|YES' broadcast_attachment_message_index="$(mysql "$DB" -N -B -e "SELECT GROUP_CONCAT(column_name ORDER BY seq_in_index) FROM information_schema.statistics WHERE table_schema='$DB' AND table_name='aihr_broadcast_message' AND index_name='idx_aihr_broadcast_message_attachment';")"