Files
writer-work-flow/apps/web/lib/style/locateSegment.ts
Yaojia Wang bf39f50b2f fix(qa): 实施 4 组设计型 QA 项——规则删除/codex 角色/大纲卷过滤/文风回炉锚点
#5 规则 DELETE + id:暴露 RuleView.id(PK);新增 DELETE /projects/{id}/rules/{rule_id}
  (项目/规则不存在→404,成功→204,按 (id,project_id) 限定);rule_repo 加
  list_for_project/delete;RulesPage 每条加删除(乐观删+回滚+toast)。assemble 侧
  RuleView(缓存前缀)不动,列表另立 RuleListItemView。
#7 codex 角色 relations:写侧本已持久化、读端点 _existing_characters 硬编码 []。
  加 _relations_from_jsonb 解析 {name,kind,note},CodexPage 渲染关系 chip。
#8 角色入库幂等:SqlCharacterWriteRepo.create 改 (project_id,name) app 层 upsert——
  重复入库改更新而非插入;不加 UNIQUE/迁移(线上已有重复行会让约束迁移失败)。
#1 大纲卷过滤:GET /outline 支持可选 ?volume(无参=全部,向后兼容);OutlineEditor
  加「查看:全部/卷N」筛选,与生成目标卷解耦。
H3/#9 文风回炉锚点:StyleDriftSegment 加 text(逐字命中段),style.md 指示审稿输出;
  前端按内容锚点定位回炉目标(idx 仅排序),命中失败 → 提示「无法定位该段」而非
  静默 no-op。style golden fixture 已重生成。

契约变更已 pnpm gen:api(RuleView.id / DELETE rules / outline ?volume)。无迁移
(alembic 无漂移)。门禁绿:ruff/mypy(210)/alembic/pytest 760 · 前端 tsc/lint/vitest 329。
2026-06-25 12:53:03 +02:00

27 lines
1.5 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 文风漂移段的「内容锚」定位QA H3 / #9用漂移段携带的原文 `text` 在终稿里做
// 内容匹配定位回炉目标,而不是用位置索引 idx——因为审稿端的分段方式与前端按空行切段
// 不一定一致,靠 idx 取段会命中错段、越界则静默落空。纯逻辑,便于 node 环境单测。
import type { StyleDriftSegment } from "@/lib/review/sse";
// 在终稿里定位漂移段原文:
// 1) 段自带 text 且能在终稿里逐字搜到 → 返回该原文(最可靠,内容锚)。
// 2) text 为空/搜不到(旧数据、或作者已手改导致原文不在)→ 回退到位置 idx 取段;
// idx 越界或取到空段 → 返回 null由调用方就 #9 给出「无法定位」提示,不静默 no-op
export function locateDriftSegment(
finalParas: readonly string[],
segment: Pick<StyleDriftSegment, "idx" | "text">,
): string | null {
const anchor = segment.text.trim();
if (anchor.length > 0) {
// 内容匹配:原文需在某一段里出现(终稿整体含该原文即视为可定位)。
const found = finalParas.some((p) => p.includes(anchor));
if (found) return anchor;
// 自带原文但终稿里搜不到(已被手改)→ 不回退到可能错位的 idx直接判定为定位失败。
return null;
}
// 无内容锚(旧数据)→ 退回位置 idx 取段(兼容旧留痕)。
const para = finalParas[segment.idx]?.trim() ?? "";
return para.length > 0 ? para : null;
}