03-skill-constraints.md 4.8 KB

技能约束备忘录

本文档汇总 tag-system 与 fullstack-merge-deploy 两个技能的硬性约束,实施过程必须严格遵守。


一、tag-system 技能约束

1.1 三表结构固定

  • tag_group:标签组(维度)
  • tag:标签节点(自引用树)
  • tag_assignment:通用打标关联(entity_type + entity_id + tag_id)

字段命名:group_idparent_idsort_orderentity_typeentity_id 全部小写下划线。

1.2 根节点处理

  • 创建标签组时自动生成一个 parent_id=NULL 的同名根节点
  • 用户操作的标签都是根节点的子孙
  • 导出时跳过根节点,直接输出子节点
  • 前端 buildTree() 时遇到 parent_id=null 的视为根

1.3 写操作统一用 POST

非 RESTful 风格,所有写操作用 POST:

POST /api/tags/groups/{id}/edit    而非 PUT
POST /api/tags/groups/{id}/delete  而非 DELETE
POST /api/tags/{id}/edit
POST /api/tags/{id}/delete
POST /api/tags/{id}/move

请求体定义为 Controller 内部 static class + Lombok @Data

1.4 打标规范

  • 全量替换:每次保存提交完整 tagIds 列表,后端先 deleteAll 再 insert
  • 多标签:一个实体可打 0~N 个标签
  • 同组多标:同一组内可多选
  • 跨组打标:可选择不同组的标签
  • 非叶子可标:任意层级节点都可选中

1.5 防循环移动

moveTag 必须实现 isDescendant() 检查,向上遍历 parentId 链,防止形成环。

1.6 导入策略

四种:create / overwrite / merge / skip

  • merge 算法:递归按名称匹配,同名保留 + 递归子节点,新名追加
  • 幂等安全:同名标签不会重复创建

1.7 前端组件结构

api/tag.js                    所有标签 API 封装
stores/tag.js                 Pinia store(含 buildTree)
components/tag/
  TreeNodeItem.vue            递归树节点
  TagSelector.vue             标签选择器弹窗
views/TagsView.vue            标签管理页面(左右分栏)

1.8 扩展实体类型

新增业务对象打标(如 article、knowledge)只需:

  • 前端 TagSelector 传入 entity-type="article":entity-id="article.id"
  • 后端无需改动,通用 API 自动支持

二、fullstack-merge-deploy 技能约束

2.1 端口

application.ymlserver.port: 7945

2.2 前端构建输出

frontend/vite.config.jsbuild.outDir 必须直接指向后端 static:

build: {
  outDir: path.resolve(__dirname, '../backend/src/main/resources/static'),
  emptyOutDir: true
}

2.3 SPA Fallback(两文件配合)

绝对不能

  • addResourceHandlers("/**") — 会饿死 @Controller
  • addViewControllers + forward: — Spring Boot 3.x 返回空 body
  • /**/{var} 模式 — PathPatternParser 不支持 ** 后接路径变量

必须

  • WebMvcConfig.java 只配 CORS
  • SpaController.java@RequestMapping("/{x:[^.]+}") 多段模式返回 ClassPathResource("/static/index.html")

2.4 正则约束

@RequestMapping(value = {
    "/{x:[^.]+}",
    "/{x:[^.]+}/{y:[^.]+}",
    "/{x:[^.]+}/{y:[^.]+}/{z:[^.]+}"
})

注意:Java 字符串中 [^.] 不要写成 [^\.]

2.5 run 脚本要点

  • run.bat 开头必须 chcp 65001 >nul(中文 UTF-8 输出)
  • 严禁做"清理 static + 从 dist 复制"的步骤(Vite 已直出)
  • 流程仅两步:npm run buildmvn package -DskipTests

2.6 .gitattributes 必须创建

*.bat text eol=crlf
*.cmd text eol=crlf
*.sh text eol=lf

2.7 静态资源

  • spring-boot-starter-web 已内置 classpath:/static/ 处理
  • 无需额外依赖
  • Maven 自动将 static/ 打入 JAR

三、本项目特有约束

3.1 Windows 环境

  • 永远不用 >/dev/null>nul 重定向到 nul(除 chcp 65001 >nul 这种已知安全场景,cmd 内部识别)
  • $HOME 显式替换为 C:/Users/Administrator
  • 不批量杀进程,必须按端口精确筛选

3.2 LLM 调用

  • 必须支持 OpenAI 兼容协议:POST {base_url}/chat/completions
  • 请求体:{ model, messages, temperature, max_tokens }
  • 响应:{ choices: [{ message: { content } }] }
  • 超时:连接 5s,读取 60s(文章聚合可能较长)

3.3 链接抓取

  • Jsoup + 自实现 readability 规则(不引额外重型依赖)
  • 必须设置 User-Agent 与超时
  • 失败时返回友好错误,前端弹窗让用户手动贴入正文

3.4 Diff 计算

  • 使用 org.bitbucket.cowwoc:diff-match-patch:1.2org.eclipse.mylyn.github:org.eclipse.mylyn.wikitext:diff-match-patch
  • diff 按行计算(非按字符),便于阅读
  • 同时存全量快照(按用户决策)

3.5 不启动服务

按 AGENTS.md 规则:

  • 不要主动启动前后端服务
  • 测试需要时启动,完成后关闭
  • 由用户手动启动