Files
prop-ai-hr/docs/superpowers/plans/2026-07-25-broadcast-multi-role-document-insights.md

19 KiB
Raw Permalink Blame History

大喇叭公司文件多岗位解读 Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: 在现有“大喇叭消息 + 单个公司文件”链路中生成有原文依据的多岗位解读,并让员工默认看到自己岗位视角、可切换查看全部相关视角。

Architecture: 复用现有附件解析、摘要、鉴权下载和消息追问链路。新增一个职责单一的 AihrBroadcastInsightService,使用现有模型调用边界按自然段分块生成结构化岗位解读,服务端只保留引用能在原文块中精确命中的内容;附件文件状态与解读状态分离,解读失败不阻塞文件发布和阅读。

Tech Stack: Java 17、Spring Boot、JdbcTemplate、Jackson、MySQL 8、Vue 3、uni-app、TypeScript、Node test、JUnit 5/Mockito。


文件职责

  • Create backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/broadcast/AihrBroadcastInsightService.java
    • 固定岗位目录、岗位映射、自然段分块、模型调用、JSON 清洗、原文引用校验、跨块合并和响应反序列化。
  • Modify backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/broadcast/AihrBroadcastAttachmentService.java
    • 文件提取完成后先保存 READY,再独立领取和处理岗位解读任务;恢复卡死任务。
  • Modify backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/broadcast/AihrBroadcastService.java
    • 员工详情查询解读字段,基于认证手机号对应的在职组织岗位计算默认视角,不返回 extracted_text。
  • Modify backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/broadcast/AihrBroadcastDto.java
    • 增加岗位解读、来源依据和解读状态字段。
  • Create backend/script/sql/update/aihr_20260725_broadcast_multi_role_insight_mysql8.sql
    • 为附件表增加 insight_status、insights_json、insight_version 及解读队列索引。
  • Create backend/ruoyi-modules/ruoyi-aihr/src/test/java/org/dromara/aihr/broadcast/AihrBroadcastInsightServiceTest.java
    • 覆盖岗位映射、自然段分块、伪造引用剔除、跨块合并和部分成功状态。
  • Modify existing broadcast backend tests
    • 覆盖文件与解读状态解耦、DTO 不泄漏全文、租户/撤回/在职身份约束不回退。
  • Modify mobile-uni/src/services/broadcast.ts
    • 声明岗位解读响应类型。
  • Modify mobile-uni/src/pages/user/broadcast/detail.vue
    • 在文件摘要之后展示默认岗位、岗位切换、五类要点和折叠的原文依据。
  • Modify mobile-uni/src/pages/user/broadcast/index.vue
    • 将通知命中标签“与我有关”改为“定向提醒”。
  • Create mobile-uni/tests/broadcast-multi-role-insight.test.mjs
    • 覆盖类型契约、默认视角、切换、依据折叠、降级状态及术语改名。
  • Modify scripts/reset-dev-db.sh, scripts/tests/aihr-schema-migrations.test.sh, scripts/release-preflight.sh, scripts/demo-check.sh
    • 纳入正式迁移与远端只读 schema 合同。
  • Modify docs/API_INTEGRATION.md, docs/BRD_IMPLEMENTATION_AUDIT.md
    • 记录接口字段、生成状态、失败降级和验收边界。

Task 1: 数据库合同与 DTO

Files:

  • Create: backend/script/sql/update/aihr_20260725_broadcast_multi_role_insight_mysql8.sql

  • Modify: backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/broadcast/AihrBroadcastDto.java

  • Modify: scripts/tests/aihr-schema-migrations.test.sh

  • Test: backend/ruoyi-modules/ruoyi-aihr/src/test/java/org/dromara/aihr/broadcast/AihrBroadcastAttachmentContractTest.java

  • Step 1: 写失败的 schema 与 DTO 合同测试

在迁移测试中要求以下三列存在:

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');

在 AihrBroadcastAttachmentContractTest 中断言响应包含 insightStatus、defaultPerspectiveCode、perspectives,同时不包含 extractedText 和 insightsJson。

  • Step 2: 运行测试并确认失败

Run:

bash scripts/tests/aihr-schema-migrations.test.sh
mvn -f backend/pom.xml -pl ruoyi-modules/ruoyi-aihr -am \
  -Dtest=AihrBroadcastAttachmentContractTest \
  -Dsurefire.failIfNoSpecifiedTests=false test

Expected: schema 测试缺少 3 列,Java 测试缺少新的 record component。

  • Step 3: 写幂等迁移和最小 DTO

迁移增加:

ALTER TABLE aihr_broadcast_attachment
  ADD COLUMN insight_status varchar(20) NOT NULL DEFAULT 'NOT_PROCESSED' AFTER summary,
  ADD COLUMN insights_json longtext NULL AFTER insight_status,
  ADD COLUMN insight_version varchar(30) NULL AFTER insights_json;

迁移必须通过 information_schema + prepared statement 分列幂等执行,并增加:

KEY idx_aihr_broadcast_attachment_insight_queue
  (insight_status, status, update_time)

DTO 增加:

public record BroadcastEvidence(int paragraphIndex, String quote) {}

public record BroadcastPerspective(
    String code,
    String label,
    String summary,
    List<String> concerns,
    List<String> impacts,
    List<String> actions,
    List<String> risks,
    List<BroadcastEvidence> evidence
) {}

附件响应追加:

String insightStatus,
String defaultPerspectiveCode,
List<String> perspectiveLabels,
List<BroadcastPerspective> perspectives
  • Step 4: 运行测试并确认通过

Run the two Task 1 commands.

Expected: PASS,迁移可重复执行,DTO 没有原文和 JSON 存储字段。

  • Step 5: 提交
git add backend/script/sql/update/aihr_20260725_broadcast_multi_role_insight_mysql8.sql \
  backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/broadcast/AihrBroadcastDto.java \
  backend/ruoyi-modules/ruoyi-aihr/src/test/java/org/dromara/aihr/broadcast/AihrBroadcastAttachmentContractTest.java \
  scripts/tests/aihr-schema-migrations.test.sh
git commit -m "feat(broadcast): add multi-role insight contract"

Task 2: 岗位解读生成器

Files:

  • Create: backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/broadcast/AihrBroadcastInsightService.java

  • Create: backend/ruoyi-modules/ruoyi-aihr/src/test/java/org/dromara/aihr/broadcast/AihrBroadcastInsightServiceTest.java

  • Step 1: 写失败的核心逻辑测试

使用 Mockito 模拟 AihrModelSeedService.chat 返回 OpenAI-compatible JSON,覆盖:

assertEquals("finance", AihrBroadcastInsightService.defaultPerspectiveCode(List.of("财务经理")));
assertEquals("living_advisor", AihrBroadcastInsightService.defaultPerspectiveCode(List.of("物业管家")));
assertNull(AihrBroadcastInsightService.defaultPerspectiveCode(List.of("未知岗位")));
assertTrue(AihrBroadcastInsightService.chunks(longParagraphText).size() > 1);
assertTrue(result.perspectives().getFirst().evidence().stream()
    .allMatch(item -> source.contains(item.quote())));
assertFalse(resultJson.contains("模型编造但原文不存在"));

另测第二块模型失败时保留第一块结果并返回 PARTIAL,所有块失败时返回 FAILED。

  • Step 2: 运行测试并确认失败

Run:

mvn -f backend/pom.xml -pl ruoyi-modules/ruoyi-aihr -am \
  -Dtest=AihrBroadcastInsightServiceTest \
  -Dsurefire.failIfNoSpecifiedTests=false test

Expected: FAIL because the service does not exist.

  • Step 3: 实现最小生成器

固定九个角色:

private static final Map<String, String> ROLE_LABELS = Map.ofEntries(
    entry("living_advisor", "生活顾问/客服"),
    entry("cleaning", "保洁"),
    entry("security", "保安"),
    entry("engineering", "保修/工程"),
    entry("finance", "财务"),
    entry("hr", "人力"),
    entry("operations", "业务运营"),
    entry("audit_risk", "审计风控"),
    entry("management", "管理层")
);

自然段块目标 4,000 字、相邻重叠 300 字,最多 80 块。每块调用:

modelService.chat(new ChatRequest(
    prompt(fileName, chunk, paragraphOffset),
    null,
    "只根据给定原文返回 JSON;未提及的岗位不要输出;evidence.quote 必须逐字复制原文。"
));

只接受 mode == "openai-compatible" 且 error 为空的响应。解析后执行:

if (!chunk.text().contains(evidence.quote())) {
    continue;
}

每岗位最多 5 条 concerns/impacts/actions/risks、3 条 evidence;文本去空白、去重、限制长度;仅保留固定角色代码。成功块少于总块数或正文超过 80 块时为 PARTIAL,全部失败为 FAILED。

  • Step 4: 运行测试并确认通过

Run the Task 2 Maven command.

Expected: PASS.

  • Step 5: 提交
git add backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/broadcast/AihrBroadcastInsightService.java \
  backend/ruoyi-modules/ruoyi-aihr/src/test/java/org/dromara/aihr/broadcast/AihrBroadcastInsightServiceTest.java
git commit -m "feat(broadcast): generate grounded role insights"

Task 3: 附件异步链路与员工详情

Files:

  • Modify: backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/broadcast/AihrBroadcastAttachmentService.java

  • Modify: backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/broadcast/AihrBroadcastService.java

  • Modify: backend/ruoyi-modules/ruoyi-aihr/src/test/java/org/dromara/aihr/broadcast/AihrBroadcastServiceTest.java

  • Modify: backend/ruoyi-modules/ruoyi-aihr/src/test/java/org/dromara/aihr/broadcast/AihrBroadcastAttachmentContractTest.java

  • Step 1: 写失败的链路测试

测试源代码和 JDBC 参数,要求:

assertTrue(attachmentSource.contains("set status = 'READY', extracted_text = ?, summary = ?"));
assertTrue(attachmentSource.contains("insight_status = 'PENDING'"));
assertTrue(attachmentSource.contains("insight_status = 'FAILED'"));
assertTrue(detailSql.contains("a.insights_json as attachment_insights_json"));
assertTrue(detailSql.contains("m.status = 'PUBLISHED'"));
assertFalse(responseFields.contains("extractedText"));

新增详情测试:岗位 财务主管 默认代码为 finance;同一响应仍返回其他全部岗位;撤回、跨租户、非在职员工继续在查询前失败关闭。

  • Step 2: 运行测试并确认失败

Run:

mvn -f backend/pom.xml -pl ruoyi-modules/ruoyi-aihr -am \
  -Dtest=AihrBroadcastServiceTest,AihrBroadcastAttachmentContractTest \
  -Dsurefire.failIfNoSpecifiedTests=false test

Expected: FAIL on missing insight queue and response mapping.

  • Step 3: 分离文件状态和解读状态

附件解析成功后立即写:

status = 'READY',
extracted_text = ?,
summary = ?,
insight_status = 'PENDING',
insights_json = NULL,
insight_version = NULL

解读任务领取时把 PENDING 原子更新为内部 PROCESSING;API 将内部 PROCESSING 映射为 PENDING。成功保存 READY/PARTIAL 与 JSON、版本;异常只写 insight_status='FAILED',不得把附件 status 改回 FAILED。启动恢复将超过 30 分钟的内部 PROCESSING 重置为 PENDING。

  • Step 4: 安全映射员工详情

详情 SQL 只在既有租户、PUBLISHED、在职员工门禁通过后读取存储 JSON。服务端用当前 APP 用户手机号和 aihr_org_snapshot 的精确在职匹配读取 position_name,再计算 defaultPerspectiveCode;客户端不得提交岗位。返回全部已生成岗位,但绝不返回 extracted_text 或 insights_json。

  • Step 5: 运行测试并确认通过

Run the Task 3 Maven command.

Expected: PASS.

  • Step 6: 提交
git add backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/broadcast/AihrBroadcastAttachmentService.java \
  backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/broadcast/AihrBroadcastService.java \
  backend/ruoyi-modules/ruoyi-aihr/src/test/java/org/dromara/aihr/broadcast/AihrBroadcastServiceTest.java \
  backend/ruoyi-modules/ruoyi-aihr/src/test/java/org/dromara/aihr/broadcast/AihrBroadcastAttachmentContractTest.java
git commit -m "feat(broadcast): expose employee role insights"

Task 4: 员工端多岗位阅读

Files:

  • Modify: mobile-uni/src/services/broadcast.ts

  • Modify: mobile-uni/src/pages/user/broadcast/detail.vue

  • Modify: mobile-uni/src/pages/user/broadcast/index.vue

  • Create: mobile-uni/tests/broadcast-multi-role-insight.test.mjs

  • Modify: mobile-uni/tests/broadcast-attachment.test.mjs

  • Step 1: 写失败的移动端合同测试

断言源码包含:

assert.match(service, /interface BroadcastPerspective/);
assert.match(detail, /多岗位解读/);
assert.match(detail, /defaultPerspectiveCode/);
assert.match(detail, /原文依据/);
assert.match(detail, /insightStatus === 'FAILED'/);
assert.doesNotMatch(detail, /与我有关/);
assert.match(index, /定向提醒/);
  • Step 2: 运行测试并确认失败

Run:

npm --prefix mobile-uni run test:unit

Expected: new multi-role test FAIL.

  • Step 3: 增加类型和默认选择

类型与后端一致。详情加载后:

const perspectives = computed(() => item.value?.attachment?.perspectives || []);
const selectedCode = ref('');

const selectInitialPerspective = (attachment?: BroadcastAttachment) => {
  selectedCode.value = attachment?.perspectives.some(
    perspective => perspective.code === attachment.defaultPerspectiveCode
  )
    ? attachment.defaultPerspectiveCode || ''
    : attachment?.perspectives[0]?.code || '';
};

显示“我的岗位视角”仅当默认代码实际命中;否则显示“多岗位解读”。

  • Step 4: 实现移动端布局和降级状态

摘要保持在前。其后依次展示:

多岗位解读 / 我的岗位视角
[生活顾问] [财务] [人力] [业务运营] …
本岗位摘要
关注点 / 影响 / 建议行动 / 风险
原文依据(默认折叠)

PENDING 显示“AI 正在从不同岗位解读”;PARTIAL 显示“已生成部分岗位解读”;FAILED 或 NOT_PROCESSED 只显示轻量说明,不隐藏摘要、原文件和“问这条消息”。岗位 chips 允许横向滚动,按钮具备可识别选中态和可读文本。

  • Step 5: 运行移动端测试、类型检查和构建

Run:

npm --prefix mobile-uni run test:unit
npm --prefix mobile-uni run typecheck
npm --prefix mobile-uni run build:h5

Expected: all PASS.

  • Step 6: 提交
git add mobile-uni/src/services/broadcast.ts \
  mobile-uni/src/pages/user/broadcast/detail.vue \
  mobile-uni/src/pages/user/broadcast/index.vue \
  mobile-uni/tests/broadcast-multi-role-insight.test.mjs \
  mobile-uni/tests/broadcast-attachment.test.mjs
git commit -m "feat(mobile): show broadcast role perspectives"

Task 5: 开发重置、发布预检与文档

Files:

  • Modify: scripts/reset-dev-db.sh

  • Modify: scripts/release-preflight.sh

  • Modify: scripts/demo-check.sh

  • Modify: docs/API_INTEGRATION.md

  • Modify: docs/BRD_IMPLEMENTATION_AUDIT.md

  • Step 1: 写失败的发布合同

将远端附件合同从 11 个业务列扩展为 14 个,并明确要求:

insight_status
insights_json
insight_version

demo-check.sh 要求新迁移、解读服务、移动端“多岗位解读”和“定向提醒”存在。

  • Step 2: 运行静态预检并确认失败

Run:

bash scripts/demo-check.sh

Expected: FAIL on missing preflight/docs markers before implementation.

  • Step 3: 接入迁移与更新文档

reset-dev-db.sh 在附件基础迁移后执行新迁移。API 文档记录:

insightStatus: NOT_PROCESSED | PENDING | READY | PARTIAL | FAILED
defaultPerspectiveCode: 服务端按认证手机号对应在职岗位计算
perspectives[]: code/label/summary/concerns/impacts/actions/risks/evidence

同时记录:所有有原消息阅读权限的员工可切换全部生成岗位;引用必须逐字命中原文;失败不影响摘要、下载和追问;旧“与我有关”改名“定向提醒”且语义仍为通知定向。

  • Step 4: 运行静态预检

Run:

bash scripts/demo-check.sh
git diff --check

Expected: PASS.

  • Step 5: 提交
git add scripts/reset-dev-db.sh scripts/release-preflight.sh scripts/demo-check.sh \
  docs/API_INTEGRATION.md docs/BRD_IMPLEMENTATION_AUDIT.md
git commit -m "docs(broadcast): define role insight release contract"

Task 6: 集成、视觉和真实交互验收

Files:

  • Verify all modified files

  • Evidence: local browser screenshots at 390 × 844 and desktop responsive view

  • Step 1: 运行后端与移动端全量相关检查

Run:

mvn -f backend/pom.xml -pl ruoyi-modules/ruoyi-aihr -am \
  -DskipITs -Dsurefire.failIfNoSpecifiedTests=false test
npm --prefix mobile-uni run test:unit
npm --prefix mobile-uni run typecheck
npm --prefix mobile-uni run build:h5
bash scripts/demo-check.sh
git diff --check

Expected: PASS.

  • Step 2: 启动本地服务并造两份可识别测试文件

Run:

docker compose -f docker-compose.dev.yml up -d
portless run --name wygj-api ./scripts/dev-backend.sh
portless run --name wygj-mobile-uni npm --prefix mobile-uni run dev:h5

通过现有管理端上传并发布:

  1. 同时涉及财务、人力、业务运营的短文件;
  2. 超过 4,000 字且关键内容在尾部的长文件。

使用数据库中实际在职手机号和开发固定验证码登录,不在日志、截图或报告中保留手机号。

  • Step 3: 在 390 × 844 浏览器真实验证

逐项操作:

  1. 打开大喇叭详情,确认摘要位于多岗位解读之前;
  2. 确认默认选中当前员工岗位对应视角;
  3. 横向切换财务、人力、业务运营等全部返回岗位;
  4. 展开原文依据并核对引文存在于文件;
  5. 打开原文件;
  6. 点击“问这条消息”;
  7. 验证 PENDING/PARTIAL/FAILED 时主链路仍可用;
  8. 检查文字换行、chip 溢出、底部按钮、滚动与点击区域。

保存并检查截图,不以页面可加载替代视觉验收。

  • Step 4: 安全回归

用自动化或直接 HTTP 证据确认:

撤回消息 -> 404
跨租户消息 -> 404
非在职 APP 身份 -> 403
员工详情 JSON -> 不含 extracted_text / insights_json
  • Step 5: 代码审查与修复

分别进行 Java、TypeScript 和整体代码审查;修复所有高/中优先级问题后重跑 Task 6 Step 1。

  • Step 6: 提交最终修复
git add -u
git commit -m "fix(broadcast): close multi-role insight review findings"
  • Step 7: 交付分支

确认工作区干净,报告 implemented / locally verified / visually verified;生产部署与 production-verified 只有在执行正式迁移、静态发布、后端发布、完整 release-preflight 和线上真实账号验证后才能声明。