Skip to content

fix(quick-start): 使用可读项目名称 - #284

Merged
minorcell merged 3 commits into
1024XEngineer:mainfrom
xyh202131:fix/quick-start-project-name
Aug 13, 2026
Merged

fix(quick-start): 使用可读项目名称#284
minorcell merged 3 commits into
1024XEngineer:mainfrom
xyh202131:fix/quick-start-project-name

Conversation

@xyh202131

@xyh202131 xyh202131 commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

改动内容

  • Quick Start 自动创建项目时,直接使用整理后的角色描述作为项目名
  • 项目名超过后端 20 字限制时进行可读截断
  • 项目名称重复时使用 项目名 2项目名 3 这样的可读编号
  • 删除时间戳和随机字符串组成的哈希式后缀
  • 将后端项目重名响应在 entities/project 适配器中转换为稳定的 ProjectNameConflictError
  • 将连续重名时的创建尝试限制为最多 5 次,避免大量串行写请求

问题原因

原实现为了满足后端“同一用户项目名称唯一”的约束,始终给项目名追加时间戳和随机串,导致用户在项目列表中看到难以理解的哈希式名称。

后端当前对项目重名只提供通用 BAD_REQUEST=400,没有重名专属业务码。中文契约文案的识别现已收口在 Project HTTP 适配器,Quick Start 只依赖稳定错误类型,不再直接匹配后端文案。

用户影响

Quick Start 创建的项目现在具有可识别、可复述的名称;重复创建相同描述时仍能正常创建。普通业务错误不会被误判为重名并重复请求。

长名称首次创建会尽量保留更多内容;发生重名后才缩短前缀,为可读编号让出后端 20 字限制。

验证

  • 5 个改动文件格式检查通过
  • npm run lint
  • npm run typecheck
  • npm run test:coverage(41 个测试文件、448 项测试通过)
  • npm run build

@vercel

vercel Bot commented Aug 13, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
windup Ready Ready Preview Aug 13, 2026 10:08am

@codecov

codecov Bot commented Aug 13, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 95.65217% with 1 line in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
frontend/src/entities/project/index.ts 90.90% 0 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

fennoai[bot]

This comment was marked as outdated.

@xyh202131

Copy link
Copy Markdown
Contributor Author

@fennoai -claude

@fennoai

fennoai Bot commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

PR 审查:Quick Start 使用可读项目名称

frontend/src/pages/quick-start/service.ts 与其测试的改动做了完整审查。整体是一次干净、聚焦的改进:用可读的截断名称 + 递增编号替代了哈希式后缀,createAutoPrepareProject 是唯一改动点,只被 service.ts 内部消费,无外部调用方受影响。

优点

  • 逻辑自洽:maxBaseLength = 20 - suffix.length 保证任意 sequence 下最终名称都 ≤ 20 字(后端限制),例如 序号 2(2 字)→ base 截到 18 字,序号 10(3 字)→ base 截到 17 字。
  • 边界处理完善:空白/纯空格提示回退到 未命名项目\s+ 归一化去除多余空白;非重名错误直接向上抛出、不重试;重名重试上限 100 次后抛出 lastConflict
  • 测试覆盖到位,新增了重名编号、空提示回退、非重名错误不重试、耗尽序列等用例。

建议(非阻塞)

  1. 重名判定依赖精确匹配后端中文文案service.ts:626error.message !== '项目名称已存在' 判断是否为重名冲突。这是脆弱耦合:一旦后端改动这条文案(甚至加个标点),重试逻辑会静默失效并直接抛错,而不会走编号回退。ApiError 已带 code/kind: 'business' 字段,若后端对该冲突有稳定的业务 code,用 code 判定会更健壮;若确实只能靠文案,建议在该处加注释说明这是与后端约定的契约字符串,便于日后同步维护。

  2. 长名称编号变体的 base 会被重新截短 — 当提示较长触发截断时,冲突后 sequence 增大导致 suffix 变长、maxBaseLength 变小,于是 序号 2 的 base 可能比序号 1 的名称更短一位,二者可读前缀略有差异。属于可接受的小瑕疵,仅提示知悉。

验证

  • 已锁定 diff 范围审查(base 2702271...head 1edb190)。
  • 未能在本沙箱运行 vitest:依赖未安装(Cannot find package 'vitest'),故测试为静态审查而非实跑。PR 描述中的 test:coverage/lint/typecheck/build 结果未在此环境复核。

未发现阻塞性问题。

@xyh202131

Copy link
Copy Markdown
Contributor Author

/review

@fennoai fennoai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Found one performance concern in the duplicate-name fallback.

Validation: src/pages/quick-start/service.test.ts (19 tests), TypeScript typecheck, and formatting checks passed.

Comment thread frontend/src/pages/quick-start/service.ts
@huyanxius

Copy link
Copy Markdown
Collaborator
  1. 重名判定依赖精确匹配后端中文文案service.ts:626error.message !== '项目名称已存在' 判断是否为重名冲突。这是脆弱耦合:一旦后端改动这条文案(甚至加个标点),重试逻辑会静默失效并直接抛错,而不会走编号回退。ApiError 已带 code/kind: 'business' 字段,若后端对该冲突有稳定的业务 code,用 code 判定会更健壮;若确实只能靠文案,建议在该处加注释说明这是与后端约定的契约字符串,便于日后同步维护。

这个问题我也发现了,而且是这个 PR 引入的。建议修改

@huyanxius

Copy link
Copy Markdown
Collaborator

@fennoai -claude 把重试的判据从 error.message === '项目名称已存在' 换成 error instanceof ApiError && error.kind === 'business',再顺手把循环上限从 100 降到 5,这样能否解决你提出的问题?

@fennoai

fennoai Bot commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

回复:能否用 kind === 'business' + 上限降到 5

结论:只做这两处改动不行,反而会引入一个更隐蔽的正确性问题。 它解决了「耦合中文文案」的表象,但把重试判据放得太宽了。

为什么 kind === 'business' 太宽

kind: 'business'shared/api/index.ts:163assertSuccessfulEnvelope 里,是对所有 response.ok && envelope.code !== 200 的后端业务错误统一赋的值——重名冲突、参数校验失败、配额限制等都是 business。测试后端里重名返回的也是通用的 code: 400project-assets-backend.ts:218),和其它校验错误无法区分。

所以改成 error instanceof ApiError && error.kind === 'business' 后:

  • 任何业务错误(比如后端因别的原因拒绝创建)都会被当成「重名」触发换名重试,循环 5 次全失败后抛出 lastConflict
  • 用户/日志看到的会是「换了 5 个名字都冲突」式的失败,真正的根因(参数错误、配额等)被掩盖了。这比当前「精确匹配文案」更难排查。

换句话说:当前判据是过窄且脆弱(文案一改就失效),你的提案是过宽(吞掉并重试了不该重试的错误)。两者都不理想。

更稳妥的方向

按稳健度排序:

  1. 最佳 —— 用重名专属的业务 code。 若后端对「项目名称已存在」有独立的 code(不是通用 400),判据写成 error instanceof ApiError && error.code === <该 code>。这既摆脱文案耦合,又精确锁定重名。需要向后端确认是否有这样的 code(本仓库 API_CONTRACT.md 未记录,测试桩用的是通用 400)。
  2. 次选 —— code 与文案组合,并注明契约。 若后端确实只有通用 400,则 error instanceof ApiError && error.kind === 'business' && error.message === '项目名称已存在',并在该行加注释声明这是与后端约定的契约字符串。这样至少缩小了误命中面,也标注了维护点。
  3. 不建议单用 kind === 'business'

上限 100 → 5

单看这一项没问题、甚至更合理(同一可读名连撞 5 次已属异常)。但注意两点:

  • 这是行为收窄:极端场景下第 6 个及以后的编号名不再尝试。对 Quick Start 场景可以接受。
  • 必须同步改测试service.test.ts:320 的「exhausting the readable project name sequence」用例断言 toHaveBeenCalledTimes(100),改上限后需改成 5,否则测试挂。

另一处连带影响(重要)

现有测试用的是 new Error('项目名称已存在')(纯 Error,见 service.test.ts:273/315),不是 ApiError。一旦判据加上 error instanceof ApiError,这些用例抛出的普通 Error不再被识别为重名,会直接抛出、重试逻辑走不到。所以改判据的同时,重名相关测试需改用 ApiError 构造,否则「重名编号」「耗尽序列」两个用例都会失效。

小结

  • 单纯 kind === 'business'不能采用,会误重试非重名业务错误。
  • 推荐用重名专属 code(需后端确认);退而用 kind === 'business' + 文案 + 注释。
  • 上限降到 5 可行,但要同步更新耗尽序列的测试。
  • 改判据须把重名相关测试改成抛 ApiError

(本次为分析答复,未改动代码。已锁定 diff 范围 2702271...1edb190 审阅;沙箱内 vitest 依赖未安装,结论基于静态分析。)

@xyh202131

Copy link
Copy Markdown
Contributor Author

已按评审更新:后端现阶段没有项目重名专属业务码,只有通用 400,因此没有采用“所有 business 错误都重试”。现在由 Project 真实接口适配器将约定的重名响应转换为 ProjectNameConflictError,Quick Start 只依赖该稳定类型;后端文案耦合被限制在一处并有注释。重试上限同时从 100 降为 5。

长名称编号时前缀会为编号略微缩短,这是后端 20 字上限下的明确取舍:首次名称不提前牺牲可读内容,仅在确实重名时为编号让位。对应行为已补测试。

@minorcell
minorcell merged commit 93ce8dc into 1024XEngineer:main Aug 13, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants