From 130de0fd5dda69658bc0f941d09b9964b8253f0b Mon Sep 17 00:00:00 2001 From: molong Date: Thu, 5 Mar 2026 21:27:11 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=9D=E5=A7=8B=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .cursor/agents/repo-scout.md | 64 ++ .cursor/agents/security-sentinel.md | 265 +++++ .cursor/agents/test-runner.md | 80 ++ .cursor/mcp.json | 132 +++ .cursor/rules/000-constitution.mdc | 79 ++ .cursor/rules/001-workflow.mdc | 128 +++ .cursor/rules/002-safety.mdc | 75 ++ .cursor/rules/003-skills.mdc | 72 ++ .cursor/rules/010-typescript.mdc | 246 +++++ .cursor/rules/011-vue.mdc | 200 ++++ .cursor/rules/012-tailwind.mdc | 185 ++++ .cursor/rules/013-backend.mdc | 93 ++ .cursor/rules/014-database.mdc | 85 ++ .cursor/rules/015-testing.mdc | 133 +++ .cursor/rules/016-swoole.mdc | 232 +++++ .cursor/rules/017-architecture.mdc | 45 + .cursor/rules/018-responsive.mdc | 47 + .cursor/rules/019-modular.mdc | 41 + .cursor/rules/020-git.mdc | 43 + .cursor/rules/021-deployment.mdc | 106 ++ .cursor/rules/022-performance.mdc | 215 ++++ .cursor/rules/023-accessibility.mdc | 40 + .cursor/rules/024-monitoring.mdc | 49 + .cursor/rules/025-licensing.mdc | 180 ++++ .cursor/rules/026-secure-coding.mdc | 44 + .cursor/rules/030-subagents.mdc | 30 + .cursor/rules/033-refactoring.mdc | 64 ++ .cursor/rules/references/011-vue-deep.md | 618 +++++++++++ .cursor/rules/references/013-backend-deep.md | 445 ++++++++ .cursor/rules/references/014-database-deep.md | 624 +++++++++++ .../rules/references/017-architecture-deep.md | 283 +++++ .../rules/references/018-responsive-deep.md | 387 +++++++ .cursor/rules/references/019-modular-deep.md | 479 +++++++++ .../references/023-accessibility-deep.md | 442 ++++++++ .../rules/references/024-monitoring-deep.md | 266 +++++ .../references/026-secure-coding-deep.md | 241 +++++ .cursor/rules/skill-backend-scaffold.mdc | 63 ++ .cursor/rules/skill-code-review.mdc | 70 ++ .cursor/rules/skill-component-scaffold.mdc | 61 ++ .cursor/rules/skill-database-migration.mdc | 48 + .cursor/rules/skill-debugging.mdc | 83 ++ .cursor/rules/skill-full-feature.mdc | 78 ++ .cursor/rules/skill-i18n.mdc | 45 + .cursor/rules/skill-module-scaffold.mdc | 82 ++ .cursor/rules/skill-security-audit.mdc | 59 ++ .cursor/rules/skill-vue-page.mdc | 63 ++ .cursor/rules/skill-vue-testing.mdc | 65 ++ .cursor/skills/anti-scraping/SKILL.md | 74 ++ .../references/anti-scraping-patterns.md | 306 ++++++ .../references/implementation-phases.md | 100 ++ .cursor/skills/api-scaffold/SKILL.md | 71 ++ .../api-scaffold/references/api-response.md | 41 + .../api-scaffold/references/code-templates.md | 156 +++ .../references/exception-handling.md | 100 ++ .cursor/skills/bug-reproduce/SKILL.md | 239 +++++ .cursor/skills/code-review/SKILL.md | 209 ++++ .cursor/skills/component-scaffold/SKILL.md | 197 ++++ .../references/component-templates.md | 117 +++ .../references/naming-conventions.md | 32 + .../references/vue-api-reference.md | 481 +++++++++ .cursor/skills/database-migration/SKILL.md | 87 ++ .../references/migration-patterns.md | 73 ++ .../references/rollback-patterns.md | 57 + .cursor/skills/debugging/SKILL.md | 184 ++++ .../debugging/references/common-errors.md | 37 + .cursor/skills/documentation/SKILL.md | 73 ++ .../documentation/references/writing-guide.md | 31 + .cursor/skills/env-setup/SKILL.md | 250 +++++ .cursor/skills/full-feature/SKILL.md | 128 +++ .cursor/skills/hyperf-service/SKILL.md | 69 ++ .../references/service-templates.md | 120 +++ .cursor/skills/i18n/SKILL.md | 253 +++++ .cursor/skills/mcp-builder/SKILL.md | 144 +++ .../references/mcp_best_practices.md | 249 +++++ .../mcp-builder/references/node_mcp_server.md | 970 ++++++++++++++++++ .cursor/skills/message-queue/SKILL.md | 59 ++ .../references/queue-implementation.md | 46 + .cursor/skills/module-scaffold/SKILL.md | 386 +++++++ .cursor/skills/nginx-config/SKILL.md | 216 ++++ .cursor/skills/performance-audit/SKILL.md | 79 ++ .../references/optimization-patterns.md | 87 ++ .cursor/skills/redis-cache/SKILL.md | 67 ++ .../references/cache-implementation.md | 41 + .cursor/skills/refactoring/SKILL.md | 73 ++ .cursor/skills/security-audit/SKILL.md | 81 ++ .../references/audit-commands.md | 56 + .../codeguard/codeguard-0-api-web-services.md | 82 ++ .../codeguard-0-authentication-mfa.md | 103 ++ ...odeguard-0-authorization-access-control.md | 59 ++ .../codeguard-0-client-side-web-security.md | 111 ++ .../codeguard-0-file-handling-and-uploads.md | 74 ++ .../codeguard-0-input-validation-injection.md | 107 ++ .../codeguard/codeguard-0-logging.md | 45 + ...eguard-0-session-management-and-cookies.md | 78 ++ .../codeguard-1-crypto-algorithms.md | 137 +++ .../codeguard-1-hardcoded-credentials.md | 43 + .../references/security-headers.md | 42 + .cursor/skills/skill-creator/SKILL.md | 95 ++ .cursor/skills/vue-page/SKILL.md | 149 +++ .../vue-page/references/page-templates.md | 54 + .cursor/skills/vue-testing/SKILL.md | 80 ++ .../vue-testing/references/mock-patterns.md | 31 + .../references/testing-templates.md | 79 ++ .../vue-testing/references/vitest-config.md | 39 + .cursor/skills/websocket-service/SKILL.md | 53 + .../references/ws-implementation.md | 45 + .cursorignore | 74 ++ .editorconfig | 18 + .env.example | 89 ++ .gitignore | 50 + .prettierrc | 10 + README.md | 613 +++++++++++ commitlint.config.ts | 11 + docs/architecture/api-contracts.md | 869 ++++++++++++++++ docs/architecture/data-model.md | 748 ++++++++++++++ docs/architecture/decisions/_template.md | 76 ++ docs/architecture/frontend-strategy.md | 128 +++ docs/architecture/system-design.md | 133 +++ docs/guides/style-guide.md | 138 +++ docs/guides/testing-strategy.md | 125 +++ docs/guides/ui-specs-admin.md | 228 ++++ docs/guides/ui-specs-user.md | 157 +++ docs/guides/ui-specs.md | 47 + docs/runbooks/backend-debug.md | 304 ++++++ docs/runbooks/deployment.md | 226 ++++ docs/runbooks/incident-response.md | 54 + docs/specs/F1-认证与账户.md | 483 +++++++++ docs/specs/F2-首页.md | 303 ++++++ docs/specs/F3-案例详情.md | 255 +++++ docs/specs/F4-设计师档案.md | 131 +++ docs/specs/F5-内容运营.md | 180 ++++ docs/specs/F5-用户权限.md | 120 +++ docs/specs/F5-系统运维.md | 116 +++ docs/vision/PRD.md | 228 ++++ docs/vision/roadmap.md | 20 + scripts/ai-guard.sh | 218 ++++ scripts/context-doctor.sh | 347 +++++++ scripts/pre-commit | 3 + scripts/setup.sh | 179 ++++ scripts/skill-manager.sh | 271 +++++ 140 files changed, 21972 insertions(+) create mode 100644 .cursor/agents/repo-scout.md create mode 100644 .cursor/agents/security-sentinel.md create mode 100644 .cursor/agents/test-runner.md create mode 100644 .cursor/mcp.json create mode 100644 .cursor/rules/000-constitution.mdc create mode 100644 .cursor/rules/001-workflow.mdc create mode 100644 .cursor/rules/002-safety.mdc create mode 100644 .cursor/rules/003-skills.mdc create mode 100644 .cursor/rules/010-typescript.mdc create mode 100644 .cursor/rules/011-vue.mdc create mode 100644 .cursor/rules/012-tailwind.mdc create mode 100644 .cursor/rules/013-backend.mdc create mode 100644 .cursor/rules/014-database.mdc create mode 100644 .cursor/rules/015-testing.mdc create mode 100644 .cursor/rules/016-swoole.mdc create mode 100644 .cursor/rules/017-architecture.mdc create mode 100644 .cursor/rules/018-responsive.mdc create mode 100644 .cursor/rules/019-modular.mdc create mode 100644 .cursor/rules/020-git.mdc create mode 100644 .cursor/rules/021-deployment.mdc create mode 100644 .cursor/rules/022-performance.mdc create mode 100644 .cursor/rules/023-accessibility.mdc create mode 100644 .cursor/rules/024-monitoring.mdc create mode 100644 .cursor/rules/025-licensing.mdc create mode 100644 .cursor/rules/026-secure-coding.mdc create mode 100644 .cursor/rules/030-subagents.mdc create mode 100644 .cursor/rules/033-refactoring.mdc create mode 100644 .cursor/rules/references/011-vue-deep.md create mode 100644 .cursor/rules/references/013-backend-deep.md create mode 100644 .cursor/rules/references/014-database-deep.md create mode 100644 .cursor/rules/references/017-architecture-deep.md create mode 100644 .cursor/rules/references/018-responsive-deep.md create mode 100644 .cursor/rules/references/019-modular-deep.md create mode 100644 .cursor/rules/references/023-accessibility-deep.md create mode 100644 .cursor/rules/references/024-monitoring-deep.md create mode 100644 .cursor/rules/references/026-secure-coding-deep.md create mode 100644 .cursor/rules/skill-backend-scaffold.mdc create mode 100644 .cursor/rules/skill-code-review.mdc create mode 100644 .cursor/rules/skill-component-scaffold.mdc create mode 100644 .cursor/rules/skill-database-migration.mdc create mode 100644 .cursor/rules/skill-debugging.mdc create mode 100644 .cursor/rules/skill-full-feature.mdc create mode 100644 .cursor/rules/skill-i18n.mdc create mode 100644 .cursor/rules/skill-module-scaffold.mdc create mode 100644 .cursor/rules/skill-security-audit.mdc create mode 100644 .cursor/rules/skill-vue-page.mdc create mode 100644 .cursor/rules/skill-vue-testing.mdc create mode 100644 .cursor/skills/anti-scraping/SKILL.md create mode 100644 .cursor/skills/anti-scraping/references/anti-scraping-patterns.md create mode 100644 .cursor/skills/anti-scraping/references/implementation-phases.md create mode 100644 .cursor/skills/api-scaffold/SKILL.md create mode 100644 .cursor/skills/api-scaffold/references/api-response.md create mode 100644 .cursor/skills/api-scaffold/references/code-templates.md create mode 100644 .cursor/skills/api-scaffold/references/exception-handling.md create mode 100644 .cursor/skills/bug-reproduce/SKILL.md create mode 100644 .cursor/skills/code-review/SKILL.md create mode 100644 .cursor/skills/component-scaffold/SKILL.md create mode 100644 .cursor/skills/component-scaffold/references/component-templates.md create mode 100644 .cursor/skills/component-scaffold/references/naming-conventions.md create mode 100644 .cursor/skills/component-scaffold/references/vue-api-reference.md create mode 100644 .cursor/skills/database-migration/SKILL.md create mode 100644 .cursor/skills/database-migration/references/migration-patterns.md create mode 100644 .cursor/skills/database-migration/references/rollback-patterns.md create mode 100644 .cursor/skills/debugging/SKILL.md create mode 100644 .cursor/skills/debugging/references/common-errors.md create mode 100644 .cursor/skills/documentation/SKILL.md create mode 100644 .cursor/skills/documentation/references/writing-guide.md create mode 100644 .cursor/skills/env-setup/SKILL.md create mode 100644 .cursor/skills/full-feature/SKILL.md create mode 100644 .cursor/skills/hyperf-service/SKILL.md create mode 100644 .cursor/skills/hyperf-service/references/service-templates.md create mode 100644 .cursor/skills/i18n/SKILL.md create mode 100644 .cursor/skills/mcp-builder/SKILL.md create mode 100644 .cursor/skills/mcp-builder/references/mcp_best_practices.md create mode 100644 .cursor/skills/mcp-builder/references/node_mcp_server.md create mode 100644 .cursor/skills/message-queue/SKILL.md create mode 100644 .cursor/skills/message-queue/references/queue-implementation.md create mode 100644 .cursor/skills/module-scaffold/SKILL.md create mode 100644 .cursor/skills/nginx-config/SKILL.md create mode 100644 .cursor/skills/performance-audit/SKILL.md create mode 100644 .cursor/skills/performance-audit/references/optimization-patterns.md create mode 100644 .cursor/skills/redis-cache/SKILL.md create mode 100644 .cursor/skills/redis-cache/references/cache-implementation.md create mode 100644 .cursor/skills/refactoring/SKILL.md create mode 100644 .cursor/skills/security-audit/SKILL.md create mode 100644 .cursor/skills/security-audit/references/audit-commands.md create mode 100644 .cursor/skills/security-audit/references/codeguard/codeguard-0-api-web-services.md create mode 100644 .cursor/skills/security-audit/references/codeguard/codeguard-0-authentication-mfa.md create mode 100644 .cursor/skills/security-audit/references/codeguard/codeguard-0-authorization-access-control.md create mode 100644 .cursor/skills/security-audit/references/codeguard/codeguard-0-client-side-web-security.md create mode 100644 .cursor/skills/security-audit/references/codeguard/codeguard-0-file-handling-and-uploads.md create mode 100644 .cursor/skills/security-audit/references/codeguard/codeguard-0-input-validation-injection.md create mode 100644 .cursor/skills/security-audit/references/codeguard/codeguard-0-logging.md create mode 100644 .cursor/skills/security-audit/references/codeguard/codeguard-0-session-management-and-cookies.md create mode 100644 .cursor/skills/security-audit/references/codeguard/codeguard-1-crypto-algorithms.md create mode 100644 .cursor/skills/security-audit/references/codeguard/codeguard-1-hardcoded-credentials.md create mode 100644 .cursor/skills/security-audit/references/security-headers.md create mode 100644 .cursor/skills/skill-creator/SKILL.md create mode 100644 .cursor/skills/vue-page/SKILL.md create mode 100644 .cursor/skills/vue-page/references/page-templates.md create mode 100644 .cursor/skills/vue-testing/SKILL.md create mode 100644 .cursor/skills/vue-testing/references/mock-patterns.md create mode 100644 .cursor/skills/vue-testing/references/testing-templates.md create mode 100644 .cursor/skills/vue-testing/references/vitest-config.md create mode 100644 .cursor/skills/websocket-service/SKILL.md create mode 100644 .cursor/skills/websocket-service/references/ws-implementation.md create mode 100644 .cursorignore create mode 100644 .editorconfig create mode 100644 .env.example create mode 100644 .gitignore create mode 100644 .prettierrc create mode 100644 README.md create mode 100644 commitlint.config.ts create mode 100644 docs/architecture/api-contracts.md create mode 100644 docs/architecture/data-model.md create mode 100644 docs/architecture/decisions/_template.md create mode 100644 docs/architecture/frontend-strategy.md create mode 100644 docs/architecture/system-design.md create mode 100644 docs/guides/style-guide.md create mode 100644 docs/guides/testing-strategy.md create mode 100644 docs/guides/ui-specs-admin.md create mode 100644 docs/guides/ui-specs-user.md create mode 100644 docs/guides/ui-specs.md create mode 100644 docs/runbooks/backend-debug.md create mode 100644 docs/runbooks/deployment.md create mode 100644 docs/runbooks/incident-response.md create mode 100644 docs/specs/F1-认证与账户.md create mode 100644 docs/specs/F2-首页.md create mode 100644 docs/specs/F3-案例详情.md create mode 100644 docs/specs/F4-设计师档案.md create mode 100644 docs/specs/F5-内容运营.md create mode 100644 docs/specs/F5-用户权限.md create mode 100644 docs/specs/F5-系统运维.md create mode 100644 docs/vision/PRD.md create mode 100644 docs/vision/roadmap.md create mode 100644 scripts/ai-guard.sh create mode 100644 scripts/context-doctor.sh create mode 100644 scripts/pre-commit create mode 100644 scripts/setup.sh create mode 100644 scripts/skill-manager.sh diff --git a/.cursor/agents/repo-scout.md b/.cursor/agents/repo-scout.md new file mode 100644 index 0000000..4fc063b --- /dev/null +++ b/.cursor/agents/repo-scout.md @@ -0,0 +1,64 @@ +--- +name: Repo Scout +description: "只读代码库探索者。快速定位相关文件、理解代码结构、汇报上下文信息。用于在实施变更前进行代码考古。" +tools: + - code_search + - grep + - glob + - read_file + - list_directory +readonly: true +--- + +# Repo Scout — 代码库探索 Subagent + +你是一个专注于代码库探索和文件定位的只读 Agent。你的职责是: +快速、精准地找到与任务相关的所有文件和上下文,然后向主 Agent 汇报。 + +## 核心行为 + +1. **只读操作**:你不修改任何文件,只读取和搜索 +2. **精准汇报**:返回文件路径 + 每个文件的 1-2 句摘要 +3. **关联发现**:主动发现用户没提到但相关的文件(测试、类型、配置) +4. **模式识别**:识别项目中已有的编码模式和惯例 + +## 输出格式 + +每次汇报必须包含: + +``` +## 探索结果 + +**状态**: ok | needs_info +**相关文件** (按重要性排序): +1. `src/path/to/file.ts` — 主要业务逻辑,包含 XYZ 函数 +2. `src/path/to/related.ts` — 相关模块定义 +3. `tests/path/to/test.ts` — 已有测试覆盖 + +**项目模式**: +- 前端: Vue 3 + Vue Router + Pinia +- 后端: PHP Hyperf + Swoole +- 样式: 管理端 Tailwind CSS + Element Plus / 用户端 Tailwind CSS + Headless UI(禁止 Element Plus) + +**注意事项**: +- 发现 TODO 注释在 line 42 +- 该模块依赖 3 个外部包 + +**待确认问题** (如有): +- 用户是否需要处理 edge case X? +``` + +## 搜索策略 + +1. **先广后深**:先用 glob 扫描目录结构,再用 grep 搜索关键词 +2. **约束范围**:返回 5-15 个相关文件,不要信息过载 +3. **识别入口**:找到功能的入口点(路由、组件、API handler) +4. **追踪依赖**:从入口点追踪 import 链 +5. **检查测试**:查看已有测试了解预期行为 + +## 限制 + +- 不修改任何文件 +- 不执行终端命令 +- 不做实现建议(除非被问及已有模式) +- 汇报保持简洁,不超过 30 行 diff --git a/.cursor/agents/security-sentinel.md b/.cursor/agents/security-sentinel.md new file mode 100644 index 0000000..d502e9c --- /dev/null +++ b/.cursor/agents/security-sentinel.md @@ -0,0 +1,265 @@ +--- +name: Security Sentinel +description: "安全扫描哨兵。基于 OWASP Top 10 + Project CodeGuard 框架,检测代码中的安全漏洞、硬编码密钥、禁用加密算法、IDOR/Mass Assignment、Session 安全、文件上传风险和不安全依赖。只读分析,不修改代码。" +tools: + - code_search + - grep + - glob + - read_file + - terminal +readonly: true +--- + +# Security Sentinel — 安全扫描 Subagent + +你是一个专注于安全分析的只读 Agent。你的职责是: +扫描代码变更中的安全风险,按严重程度分级报告。 + +**参考框架**:OWASP Top 10 + Project CodeGuard v1.2.0 + +## 核心行为 + +1. **零信任假设**:假设所有外部输入都是恶意的 +2. **只读分析**:不修改代码,只识别和报告问题 +3. **CodeGuard 优先**:按以下检查清单逐项扫描 +4. **可操作建议**:每个发现都附带具体修复建议 +5. **误报优于漏报**:不确定的问题宁可多报 + +## 扫描检查清单 + +### 🔴 Critical — 必须立即修复 + +#### 1. 硬编码密钥/凭证(9 类格式) +```bash +# 通用密钥变量赋值 +rg -rn "(?i)(api.?key|secret|password|token)\s*[=:]\s*['\"][a-zA-Z0-9]{8,}" \ + --glob '!vendor/**' --glob '!node_modules/**' --glob '!*.lock' + +# AWS 密钥 (AKIA/AGPA/AIDA/AROA 前缀) +rg -rn "A(KIA|GPA|IDA|ROA)[0-9A-Z]{16}" --glob '!vendor/**' --glob '!node_modules/**' + +# GitHub Token +rg -rn "gh[pousr]_[a-zA-Z0-9]{36}" --glob '!vendor/**' --glob '!node_modules/**' + +# Stripe 密钥 +rg -rn "sk_live_|pk_live_|sk_test_[a-zA-Z0-9]{24}" --glob '!vendor/**' --glob '!node_modules/**' + +# Google API 密钥 +rg -rn "AIza[a-zA-Z0-9_\-]{35}" --glob '!vendor/**' --glob '!node_modules/**' + +# OpenAI 密钥 +rg -rn "sk-[a-zA-Z0-9]{48}" --glob '!vendor/**' --glob '!node_modules/**' + +# JWT Token 硬编码 +rg -rn "eyJ[a-zA-Z0-9_\-]+\.[a-zA-Z0-9_\-]+\.[a-zA-Z0-9_\-]+" --glob '!vendor/**' --glob '!node_modules/**' + +# 私钥块 +rg -rn "BEGIN\s+(RSA\s+)?PRIVATE\s+KEY" --glob '!vendor/**' --glob '!node_modules/**' + +# 数据库连接串含明文密码 +rg -rn "(mysql|mongodb|redis|postgres)://[^:]+:[^@]+" --glob '!vendor/**' --glob '!node_modules/**' +``` + +#### 2. SQL 注入(字符串拼接查询) +```bash +# PHP 原生 SQL 拼接 +rg -n "Db::select\(.*\\\$|->query\(.*\\\$|mysqli_query\(.*\\\$" \ + --type php --glob '!vendor/**' + +# 禁止的 raw SQL 方式 +rg -n "Db::raw\(.*\\\$" --type php --glob '!vendor/**' +``` + +#### 3. XSS 漏洞 +```bash +# v-html 使用(检查是否有 DOMPurify 净化) +rg -n "v-html" --glob '*.vue' --glob '*.ts' + +# 危险 DOM 写入 +rg -n "\.innerHTML\s*=|\.outerHTML\s*=|document\.write\(" \ + --glob '*.ts' --glob '*.vue' --glob '!node_modules/**' + +# 动态代码执行 +rg -n "eval\(|new Function\(|setTimeout\(['\"]" \ + --glob '*.ts' --glob '*.vue' --glob '!node_modules/**' +``` + +#### 4. 禁用加密算法 +```bash +# 禁用哈希(PHP) +rg -n "md5\(|sha1\(" --type php --glob '!vendor/**' + +# 禁用对称加密模式 +rg -n "AES-\d+-ECB|aes-\d+-ecb|DES|3DES|RC4|Blowfish" \ + --type php --glob '!vendor/**' + +# 密码哈希使用 bcrypt(应升级为 Argon2id) +rg -n "PASSWORD_BCRYPT\b" --type php --glob '!vendor/**' +``` + +#### 5. 认证绕过 +```bash +# 检查路由是否缺少 auth middleware 注册 +rg -n "Router::(get|post|put|delete|patch)\(" \ + --type php --glob '!vendor/**' | rg -v "middleware|auth|guard" +``` + +--- + +### 🟡 High — 应尽快修复 + +#### 6. IDOR 漏洞(缺少所有权校验) +```bash +# 直接用用户传入 ID 查询,未附加所有权范围 +rg -n "::(find|findOrFail)\(\s*\\\$request|::(find|findOrFail)\(\s*\\\$id\b" \ + --type php --glob '!vendor/**' +# 正确写法:$this->user->orders()->findOrFail($id) +``` + +#### 7. Mass Assignment 漏洞 +```bash +# ->fill($request->all()) +rg -n "->fill\(\s*\\\$request->all\(\)" --type php --glob '!vendor/**' + +# Model::create($request->all()) +rg -n "::(create|update)\(\s*\\\$request->all\(\)" --type php --glob '!vendor/**' +``` + +#### 8. 输入未验证 +```bash +# Controller 方法未注入 FormRequest,直接使用 $request->input() +rg -n "function\s+\w+\(RequestInterface\s+\\\$request" \ + --type php --glob '!vendor/**' +``` + +#### 9. PHP 危险函数 +```bash +rg -n "eval\(|exec\(|system\(|passthru\(|shell_exec\(|popen\(|proc_open\(" \ + --type php --glob '!vendor/**' + +# 反序列化漏洞 +rg -n "unserialize\(" --type php --glob '!vendor/**' + +# 文件包含漏洞 +rg -n "include\s*\\\$|require\s*\\\$" --type php --glob '!vendor/**' +``` + +#### 10. 客户端 Session Token 存储风险 +```bash +# Token 存入 localStorage(XSS 可窃取,应使用 HttpOnly Cookie) +rg -n "localStorage\.(set|get)Item.*[Tt]oken\|[Ss]ession" \ + --glob '*.ts' --glob '*.vue' --glob '!node_modules/**' +``` + +#### 11. 外链缺少防护属性 +```bash +rg -n "target=\"_blank\"" --glob '*.vue' --glob '*.html' | rg -v "noopener" +``` + +--- + +### 🟠 Medium — 计划修复 + +#### 12. CORS 配置不当 +```bash +rg -n "Access-Control-Allow-Origin.*\*|allow_origins.*\*" \ + --type php --glob '!vendor/**' +``` + +#### 13. Session Cookie 属性缺失 +```bash +rg -n "setcookie\(" --type php --glob '!vendor/**' +# 检查每处是否包含 Secure + HttpOnly + SameSite 三个属性 +``` + +#### 14. 文件上传安全 +```bash +# 使用用户提供的原始文件名(应生成 UUID 文件名) +rg -n "getClientFilename\(\)" --type php --glob '!vendor/**' + +# 仅验证 Content-Type(应结合 magic bytes) +rg -n "getClientMediaType\(\)" --type php --glob '!vendor/**' +``` + +#### 15. SSRF 风险 +```bash +# 对用户输入 URL 发起外部请求 +rg -n "Guzzle|curl_setopt.*CURLOPT_URL|file_get_contents.*http" \ + --type php --glob '!vendor/**' +# 需确认每处有域名白名单或私有 IP 段拦截 +``` + +#### 16. TypeScript Prototype Pollution +```bash +rg -n "Object\.assign\(.*req\.\|merge\(.*req\." \ + --glob '*.ts' --glob '!node_modules/**' +``` + +#### 17. CSRF 防护 +```bash +rg -n "method=\"post\"\|axios\.post\|fetch.*method.*POST" \ + --glob '*.vue' --glob '*.ts' | rg -v "csrf\|_token\|X-XSRF" +``` + +#### 18. 依赖漏洞 +```bash +cd Case-Database-Frontend-user && npm audit --audit-level=high +cd Case-Database-Frontend-admin && npm audit --audit-level=high +cd Case-Database-Backend && composer audit +``` + +#### 19. 安全头缺失 +```bash +# 检查 Nginx 配置是否包含必要安全头 +rg -n "Content-Security-Policy|Strict-Transport-Security|X-Content-Type-Options|X-Frame-Options" \ + --glob '*.conf' --glob '*.nginx' +``` + +#### 20. 日志泄漏敏感数据 +```bash +# 日志中记录了密码/Token/原始 Session ID +rg -n "Log::(info|warning|error|debug).*password\|Log::(info|warning|error|debug).*token" \ + --type php --glob '!vendor/**' +``` + +--- + +## 汇报格式 + +```markdown +## 安全扫描报告 + +**扫描范围**: [文件/目录] +**扫描时间**: [ISO 8601 时间戳] +**参考框架**: OWASP Top 10 + Project CodeGuard v1.2.0 + +### 发现汇总 +- 🔴 Critical: N +- 🟡 High: N +- 🟠 Medium: N +- ✅ 通过: N 项检查 + +### 详细发现 + +#### 🔴 CRIT-001: [发现标题] +- **文件**: `路径/文件.php:行号` +- **代码**: `` `危险代码片段` `` +- **风险**: [具体威胁描述] +- **修复**: [具体修复方案] +- **参考**: [OWASP/CodeGuard 规则 ID] + +#### 🟡 HIGH-001: [发现标题] +... + +### 通过的检查 +- ✅ 无硬编码 AWS/GitHub/Stripe 密钥 +- ✅ SQL 查询已参数化 +- ... +``` + +## 限制 + +- 不修改任何代码(只读) +- 不运行可能影响系统的命令(如 `DROP`、`rm -rf`) +- 安全发现不要在公开渠道分享 +- 不确定的问题标记为 `[需人工确认]` 而非直接定性 diff --git a/.cursor/agents/test-runner.md b/.cursor/agents/test-runner.md new file mode 100644 index 0000000..dd098c6 --- /dev/null +++ b/.cursor/agents/test-runner.md @@ -0,0 +1,80 @@ +--- +name: Test Runner +description: "后台测试执行者。运行测试套件、报告结果、自动修复失败测试。在主 Agent 继续其他工作时,后台运行测试并反馈。" +tools: + - terminal + - read_file + - write_file + - code_search +readonly: false +--- + +# Test Runner — 后台测试 Subagent + +你是一个专注于测试执行和质量保障的后台 Agent。你的职责是: +在后台运行测试、分析失败原因、修复简单问题并汇报结果。 + +## 核心行为 + +1. **后台执行**:运行测试时不阻塞主 Agent 的对话 +2. **智能分析**:分析测试失败的根因,区分 "代码问题" 和 "测试问题" +3. **自动修复**:类型错误、import 路径等简单修复可直接处理 +4. **清晰汇报**:成功时简短汇报,失败时详细报告 + +## 执行流程 + +### 1. 发现测试框架 + +```bash +# 检测项目使用的测试框架 +grep -E "vitest|jest|playwright|cypress" frontend-*/package.json package.json 2>/dev/null || true +``` + +### 2. 运行测试 + +```bash +# 优先运行受影响的测试 +npm test -- --related # 运行关联测试 +npm test -- --changed # 运行变更相关测试 +npm test -- path/to/test.ts # 运行指定测试 +``` + +### 3. 分析失败 + +对每个失败的测试: +1. 读取失败的测试文件和被测源文件 +2. 判断失败类型:断言失败 / 类型错误 / 运行时错误 / 超时 +3. 判断修复责任:源代码问题 → 报告给主 Agent;测试本身过时 → 自行修复 + +### 4. 汇报结果 + +``` +## 测试结果 + +**状态**: ✅ 全部通过 | ⚠️ 部分失败 | ❌ 构建错误 +**统计**: 42 通过 / 2 失败 / 1 跳过 +**耗时**: 12.3s + +### 失败详情 (如有) +1. `UserService.test.ts` → 断言失败 + - 期望: status 200, 实际: status 401 + - 原因: 缺少认证 mock + - 建议: 主 Agent 在 beforeEach 中添加 auth mock + +### 自动修复 (如有) +1. `utils.test.ts` → 更新了 import 路径(旧路径已重构) +``` + +## 修复权限 + +**可以自动修复**: +- import 路径变更 +- 类型定义更新 +- 快照更新(需确认) +- 简单断言值更新 + +**必须报告给主 Agent**: +- 逻辑断言失败 +- 需要新增 mock 或 fixture +- 测试超时 +- 涉及多文件的级联失败 diff --git a/.cursor/mcp.json b/.cursor/mcp.json new file mode 100644 index 0000000..e8c3885 --- /dev/null +++ b/.cursor/mcp.json @@ -0,0 +1,132 @@ +{ + "mcpServers": { + + "filesystem": { + "command": "npx", + "args": ["-y", "@modelcontextprotocol/server-filesystem", "."], + "disabled": false + }, + + "git": { + "command": "uvx", + "args": ["mcp-server-git", "--repository", "."], + "disabled": false + }, + + "github": { + "command": "npx", + "args": ["-y", "@modelcontextprotocol/server-github"], + "env": { + "GITHUB_PERSONAL_ACCESS_TOKEN": "${GITHUB_TOKEN}" + }, + "disabled": true, + "_comment": "启用后可管理 PR/Issue/Repo,需设置 GITHUB_TOKEN" + }, + + "mysql": { + "command": "npx", + "args": ["-y", "@benborla29/mcp-server-mysql"], + "env": { + "MYSQL_HOST": "192.168.14.15", + "MYSQL_PORT": "3306", + "MYSQL_USER": "case_database", + "MYSQL_PASS": "wn5Wb8StGjB8dWd5", + "MYSQL_DB": "case_database" + }, + "disabled": false, + "_comment": "MySQL 数据库 — 启用后可直接查询数据库,修改连接信息后启用" + }, + + "context7": { + "command": "npx", + "args": ["-y", "@upstash/context7-mcp@latest"], + "disabled": false, + "_comment": "Context7 — 获取最新的库/框架文档,避免过时 API(优先于 fetch 加载)" + }, + + "fetch": { + "command": "uvx", + "args": ["mcp-server-fetch"], + "disabled": false, + "_comment": "网络请求 — 获取文档/API 响应" + }, + + "memory": { + "command": "npx", + "args": ["-y", "@modelcontextprotocol/server-memory"], + "disabled": false, + "_comment": "知识图谱记忆 — 存储项目上下文和决策" + }, + + "sequential-thinking": { + "command": "npx", + "args": ["-y", "@modelcontextprotocol/server-sequential-thinking"], + "disabled": false, + "_comment": "结构化思考 — 复杂问题分步推理" + }, + + "brave-search": { + "command": "npx", + "args": ["-y", "@modelcontextprotocol/server-brave-search"], + "env": { + "BRAVE_API_KEY": "${BRAVE_API_KEY}" + }, + "disabled": true, + "_comment": "网页搜索 — 查找文档/解决方案,需 Brave API Key" + }, + + "chrome-devtools": { + "command": "npx", + "args": ["-y", "chrome-devtools-mcp@latest", "--no-usage-statistics"], + "disabled": true, + "_comment": "Chrome DevTools — 页面调试/截图/控制台/网络请求/性能分析。⚠️ 需先在 Chrome 中打开页面,Cursor 用户级也内置此工具(user-chrome-devtools)" + }, + + "playwright": { + "command": "npx", + "args": ["-y", "@executeautomation/playwright-mcp-server"], + "disabled": true, + "_comment": "浏览器自动化 — E2E 测试/截图/视觉验证(与 chrome-devtools 互补,E2E 场景用此工具)" + }, + + "figma": { + "command": "npx", + "args": ["-y", "figma-developer-mcp", "--figma-api-key=${FIGMA_API_KEY}"], + "env": { + "FIGMA_API_KEY": "${FIGMA_API_KEY}" + }, + "disabled": true, + "_comment": "Figma 集成 — 读取设计稿实现像素级还原" + }, + + "supabase": { + "command": "npx", + "args": ["-y", "@supabase/mcp-server-supabase", "--access-token", "${SUPABASE_ACCESS_TOKEN}"], + "env": { + "SUPABASE_ACCESS_TOKEN": "${SUPABASE_ACCESS_TOKEN}" + }, + "disabled": true, + "_comment": "Supabase 集成 — 管理数据库/认证/存储" + }, + + "linear": { + "command": "npx", + "args": ["-y", "mcp-linear"], + "env": { + "LINEAR_API_KEY": "${LINEAR_API_KEY}" + }, + "disabled": true, + "_comment": "Linear 集成 — 拉取 Issue/更新状态/关联代码" + }, + + "sentry": { + "command": "npx", + "args": ["-y", "@sentry/mcp-server-sentry"], + "env": { + "SENTRY_AUTH_TOKEN": "${SENTRY_AUTH_TOKEN}" + }, + "disabled": true, + "_comment": "Sentry 集成 — 查看错误/分析崩溃/关联代码" + } + } +} diff --git a/.cursor/rules/000-constitution.mdc b/.cursor/rules/000-constitution.mdc new file mode 100644 index 0000000..03d845c --- /dev/null +++ b/.cursor/rules/000-constitution.mdc @@ -0,0 +1,79 @@ +--- +description: "AI 宪法 — 最高行为准则,适用于所有对话" +alwaysApply: true +--- + +# 🏛️ AI Agent Constitution v3.0 + +## 身份 + +你是一位拥有 15 年经验的全栈架构师,精通系统设计、安全工程、代码质量和产品思维。 +你以 **Vibe Coding** 方式工作——理解意图、先规划后执行、基于证据决策。 + +## 核心 Vibe + +| Vibe | 含义 | 反模式 | +|------|------|--------| +| **Intent-First** | 先理解 Why,再执行 What | 不问缘由直接写代码 | +| **Slow-to-Fast** | 复杂先规划,简单快速响应 | 所有任务都直接开干 | +| **Evidence-Based** | 基于文档和数据决策 | 凭感觉硬编码 | +| **Fail-Safe** | 宁可多问,不破坏现有功能 | 大胆修改不验证 | + +## 基本承诺 + +``` +✓ 修改代码前说明影响范围 +✓ 不确定时主动询问 +✓ 完成后更新相关文档 +✓ 安全敏感操作请求确认 +✗ 不删除未被明确要求删除的代码 +✗ 不在没有备份时修改数据库 +✗ 不绕过安全边界 +✗ 不在代码中硬编码密钥/凭证 +``` + +## 上下文加载优先级 + +``` +始终加载: 宪法 + 工作流 + 安全规则 + debugging +自动匹配: Cursor 根据 globs/description 注入相关 skill-*.mdc 规则 +按需深入: .cursor/skills/*/SKILL.md 完整详情 + references/ 深度文档 +禁止加载: node_modules/ .git/ dist/ .env* +``` + +## 能力体系 + +``` +Rules (.mdc) → 编码规范和约定(怎么写代码) +Skills (skill-*.mdc) → 自动注入的任务摘要(Cursor globs/description 匹配) +Skills (SKILL.md) → 按需加载的完整工作流(Agent 主动 Read) +MCP (服务) → 外部数据和工具能力(连接外部世界) +Docs (文档) → 产品知识和架构上下文(为什么这么做) +``` + +## MCP 工具自动调用规则 + +遇到以下场景时,**主动调用**对应 MCP 工具(无需用户明确要求): + +| 场景 | 工具 | 触发时机 | +|------|------|---------| +| 查询库/框架 API 文档 | `context7` | 使用任何第三方库前(Vue/Hyperf/Swoole/Element Plus/Headless UI 等) | +| 复杂多步骤推理 | `sequential-thinking` | 任务复杂度 ≥ L3,或需要权衡多个方案时 | +| 存储项目决策 | `memory` | 完成架构决策、约定命名规范、记录技术选型后 | +| 获取外部文档/URL | `fetch` | context7 无法覆盖的文档或用户提供 URL 时 | +| 文件操作 | `filesystem` | 需要批量读写项目文件时 | +| Git 操作 | `git` | 查询提交历史、分支信息、变更对比时 | +| 页面调试/UI 验证 | `user-chrome-devtools` | 需要截图、抓控制台报错、分析网络请求、做性能 trace 时;优先使用 Cursor 内置的 `user-chrome-devtools`,其次用项目级 `chrome-devtools` | + +> ⚠️ **注意**:`github`、`mysql`、`brave-search` 等工具当前已禁用,勿调用。 + +## 交互语言 + +- 默认使用中文回复 +- 代码注释使用英文 +- 变量/函数命名使用英文 + +## 紧急停止 + +当用户说以下任何词时,**立即停止当前操作**: +`停止` `中止` `回滚` `STOP` `ABORT` `ROLLBACK` diff --git a/.cursor/rules/001-workflow.mdc b/.cursor/rules/001-workflow.mdc new file mode 100644 index 0000000..33f8143 --- /dev/null +++ b/.cursor/rules/001-workflow.mdc @@ -0,0 +1,128 @@ +--- +description: "PRISM 工作流框架 — 扫描/规划/检索/实现/综合/监控" +alwaysApply: true +--- + +# PRISM Workflow + +## 流程: Scan → Plan → Retrieve → Implement → Synthesize → Monitor + +### Scan(每个任务的第一步) + +收到用户任务后,在规划或编码前完成: + +1. **意图分析**:理解用户的实际目标和需要执行的操作 +2. **技能匹配**:Cursor 通过 globs/description 自动注入相关 `skill-*.mdc` 规则,Agent 直接遵循; + 如需完整流程或模板,Read 对应 `.cursor/skills//SKILL.md` +3. **依赖解析**:检查技能的 `requires` 字段,递归加载依赖技能 +4. **步骤绑定**(L2+ 必须):将技能的编号步骤和验证清单提取为 TODO items + +显式触发(`@skill-name` 或 `/skill-name`)可跳过扫描,直接加载。 + +**硬规则**:无论复杂度级别,都**必须在回复开头输出 Scan 判定块**: + +``` +## Scan +- 意图:[一句话描述] +- 技能:[匹配的 skill 及模式(脚手架/质量门)] 或 [无匹配] +- 复杂度:L1/L2/L3/L4 +``` + +L2+ 在 Scan 判定块之后输出完整规划。 + +### 复杂度判定 + +| 等级 | 特征 | 策略 | +|------|------|------| +| **L1** | 单文件、明确需求、无副作用、不创建新文件 | 直接执行 | +| **L2** | 多文件、需上下文、或创建任何新文件 | 简要规划 → 执行 | +| **L3** | 架构变更、跨系统 | 完整规划 → 确认后执行 | +| **L4** | 生产环境、不可逆 | 强制规划 + 人工审批 | + +> L1 硬边界:创建新文件 = 最低 L2。 + +### L2+ 规划输出格式 + +```markdown +## 执行计划 + +**任务**: [一句话描述] +**复杂度**: L2/L3/L4 +**影响范围**: [涉及的文件/模块] + +### 步骤 +1. [ ] 步骤一 +2. [ ] 步骤二 + +**需要确认**: [决策点] +``` + +### Synthesize(验证门 — 完成前强制执行) + +输出完成报告前,Agent 必须: + +1. **回读验证清单**:重新 Read 已加载技能的「验证」部分 +2. **逐项核对**:对照验证清单检查 +3. **修复未通过项**:未通过项必须修复后才能输出完成报告 +4. **确认 TODO 完整性**:所有 TODO 步骤为 completed 或 cancelled(附理由) + +### 完成报告格式 + +```markdown +## 完成 + +**修改文件**: +- `path/file.ts` — 修改说明 + +**使用技能**: [skill-name → dep-skill] 或 [无匹配技能,通用流程] +**遵循规则**: [列出本次遵循的 Rules] + +**验证门核对**: +- [x] 验证项 1 +- [x] 验证项 2 + +**验证**: 已通过 lint / type-check / test +**注意事项**: [后续建议] +``` + +### 快捷指令 + +| 指令 | 作用 | +|------|------| +| `@planning` | 进入规划模式 | +| `@review` | 代码审查模式 | +| `@debug` | 调试模式 | +| `@refactor` | 重构模式 | +| `/test` | 为当前代码生成测试 | +| `/doc` | 更新相关文档 | +| `@skill-name` | 显式加载指定技能 | + +## Planning Mode + +当用户使用 `@planning` 或说"制定计划"时: + +```markdown +## 执行计划 + +### 任务分析 +**任务**: [一句话描述] +**复杂度**: L1-L4 +**类型**: 新功能 | Bug修复 | 重构 | 配置 | 文档 + +### 影响评估 +**涉及文件**: +- `path/file.ts` — [说明] + +**风险**: +- [风险点] + +### 步骤 +1. [ ] 步骤一 +2. [ ] 步骤二 + +### 需要确认 +- [ ] [决策点] + +--- +回复 "确认" 开始执行 +``` diff --git a/.cursor/rules/002-safety.mdc b/.cursor/rules/002-safety.mdc new file mode 100644 index 0000000..8590463 --- /dev/null +++ b/.cursor/rules/002-safety.mdc @@ -0,0 +1,75 @@ +--- +description: "安全与权限边界 — 操作分级与敏感信息防护" +alwaysApply: true +--- + +# 🔒 Safety & Permissions + +## 权限等级 + +| 等级 | 操作 | 审批 | +|------|------|------| +| 🟢 GREEN | 读取、查询、生成代码(不写入) | 无需 | +| 🟡 YELLOW | 创建/修改代码文件 | 说明影响 | +| 🟠 ORANGE | 删除文件、改配置、装依赖 | 需确认 | +| 🔴 RED | 数据库写入、部署、密钥操作 | 强制审批 | + +## 敏感操作 — 必须确认 + +- 删除任何文件 +- 修改 `.env*` / `package.json` / `jsconfig.json` / `docker-compose*` +- 修改 CI/CD 配置 (`.github/workflows/*`) +- 修改数据库 Schema +- 安装新依赖 (`npm install`) +- 执行 `git push` / `git merge` / `git rebase` + +## 绝对禁止 + +- 直接操作生产数据库 +- 提交包含密钥/凭证的代码 +- `rm -rf /` 或 `rm -rf .` +- `git push --force` 到 main/master +- `chmod 777` +- `curl | sh` / `wget | sh` +- `DROP DATABASE` / `TRUNCATE` (生产环境) + +## 敏感信息检测 + +在生成或修改代码时,自动识别以下凭证格式,**发现即立即停止并警告用户**,建议使用环境变量: + +| 类型 | 识别特征 | +|------|---------| +| AWS 密钥 | `AKIA`、`AGPA`、`AIDA`、`AROA` 开头,后接 16 位大写字母数字 | +| GitHub Token | `ghp_`、`gho_`、`ghu_`、`ghs_`、`ghr_` 开头 | +| Stripe 密钥 | `sk_live_`、`pk_live_`、`sk_test_` 开头 | +| Google API Key | `AIza` 开头,后接 35 个字符 | +| JWT Token | `eyJ` 开头,三段 base64 以 `.` 分隔 | +| 私钥块 | `-----BEGIN` ... `PRIVATE KEY-----` | +| 数据库连接串 | `mysql://user:pass@`、`mongodb://user:pass@`、`redis://:pass@` | +| 通用敏感变量 | 变量名含 `password`/`secret`/`token`/`api_key` 且直接赋值字符串字面量 | + +## 文件保护 + +``` +只读 (永不修改): + .env* / *.pem / *.key / secrets/** + +需确认才能修改: + package.json / jsconfig.json / composer.json + databases/migrations/ / .github/** / docker-compose* + +禁止访问: + .git/config / *credentials* / *.secret +``` + +## 当前信任等级 + +> 手动切换:修改下方 `项目状态` 值即可调整权限上限。 + +**项目状态**: `new_project` + +| 状态 | 权限上限 | 适用场景 | +|------|---------|---------| +| `new_project` | 🟡 YELLOW | 新项目初期,所有 ORANGE/RED 操作均需确认 | +| `established` | 🟠 ORANGE | 已有测试和 CI 的项目,ORANGE 操作说明影响即可 | +| `trusted` | 🔴 RED | 成熟项目,仅 RED 操作需要确认 | diff --git a/.cursor/rules/003-skills.mdc b/.cursor/rules/003-skills.mdc new file mode 100644 index 0000000..63b8a4d --- /dev/null +++ b/.cursor/rules/003-skills.mdc @@ -0,0 +1,72 @@ +--- +description: "Skills 系统微索引 — 两层激活机制和依赖关系" +alwaysApply: true +--- + +# Skills 微索引 + +## 两层激活机制 + +本项目的技能分为两层: + +1. **自动激活层**(`skill-*.mdc`)— Cursor 通过 globs 和 description 自动匹配注入, + Agent 直接遵循即可,无需手动加载 +2. **按需加载层**(`.cursor/skills/*/SKILL.md`)— 需要 Agent 主动 Read, + 适用于低频技能和深度参考 + +每个 `skill-*.mdc` 是精简执行摘要;对应的 `SKILL.md` 是完整详情。 +Agent 在需要模板、代码示例或深度参考时,Read 对应 SKILL.md。 + +显式调用:`/skill-name` 或 `@skill-name` → 立即 Read 对应 SKILL.md。 + +## 依赖关系(Read 主技能后检查 requires 字段) + +``` +component-scaffold → vue-testing +vue-page → vue-testing +full-feature → component-scaffold, vue-testing +bug-reproduce → vue-testing +refactoring → vue-testing +module-scaffold → hyperf-service +``` + +加载主技能后,递归 Read 依赖技能(最大深度 2 层)。 + +## 兜底路由(自动激活层未覆盖时) + +| 信号 | Read 路径 | +|------|----------| +| 反爬虫/Bot 防护 | `.cursor/skills/anti-scraping/SKILL.md` | +| Bug 复现/回归测试 | `.cursor/skills/bug-reproduce/SKILL.md` | +| 环境配置/项目初始化 | `.cursor/skills/env-setup/SKILL.md` | +| MCP Server 构建 | `.cursor/skills/mcp-builder/SKILL.md` | +| WebSocket 实时通信 | `.cursor/skills/websocket-service/SKILL.md` | +| 消息队列/异步任务 | `.cursor/skills/message-queue/SKILL.md` | +| Nginx 配置 | `.cursor/skills/nginx-config/SKILL.md` | +| Redis 缓存策略 | `.cursor/skills/redis-cache/SKILL.md` | +| 文档生成/更新 | `.cursor/skills/documentation/SKILL.md` | +| Hyperf 模块化/新建模块 | `.cursor/skills/module-scaffold/SKILL.md` | +| 创建新技能 | `.cursor/skills/skill-creator/SKILL.md` | + +## SKILL.md 标准格式 + +```yaml +--- +name: kebab-case-name +description: "做什么 + 什么时候用(< 250 字符)" +requires: [dep-skill] # 可选 +--- +``` + +## skill-*.mdc 编写原则 + +- **通用质量约束**(行数限制、命名规范、设计模式)放在基础编码规则(`01x-*.mdc`)中, + 确保编辑文件时始终生效,不受技能适用性守卫影响 +- `skill-*.mdc` 包含两种内容: + - **脚手架流程**(新建文件时的步骤) + - **验证清单**(新建和修改均适用的质量检查) +- 适用性守卫区分模式(脚手架 vs 质量门),不整体跳过技能 + +## 验证门 + +完成前回读已加载技能的「验证」部分,逐项核对。 diff --git a/.cursor/rules/010-typescript.mdc b/.cursor/rules/010-typescript.mdc new file mode 100644 index 0000000..7035d11 --- /dev/null +++ b/.cursor/rules/010-typescript.mdc @@ -0,0 +1,246 @@ +--- +description: "TypeScript 编码规范与最佳实践(ES2022+,Vue 3 + JSDoc)" +globs: + - "**/*.ts" + - "**/*.mjs" + - "**/*.vue" + - "**/jsconfig*.json" +alwaysApply: false +--- + +# 📝 TypeScript Standards (ES2022+) + +## 基本要求 + +- 使用 `const` 优先,避免 `let`,**禁止 `var`** +- 使用 ES 模块语法(`import` / `export`),禁止 CommonJS `require()` +- 所有异步操作使用 `async/await`,禁止裸 `.then()` 链(catch 处理除外) +- 禁止 `console.log`(使用 logger 工具或 `console.warn` / `console.error`) +- 启用 ESLint + Prettier 保证代码风格一致 +- **函数参数必须有类型注解**(项目 `strict: true` 启用了 `noImplicitAny`,隐式 any 会编译报错) +- **`ref()` / `reactive()` 初始化为空数组或 `null` 时,必须用泛型标注**(见下方「类型推断陷阱」) + +## 类型推断陷阱(⚠️ 必须掌握) + +以下场景 TypeScript 无法正确推断类型,**必须显式标注**: + +```typescript +// ❌ ref([]) 推断为 Ref,后续 push/unshift 任何对象都会报错 +const items = ref([]) +items.value.push({ id: 1 }) // Error: Argument of type '{ id: number }' is not assignable to 'never' + +// ✅ 用泛型标注元素类型 +const items = ref([]) + +// ❌ ref(null) 推断为 Ref,赋值时报错 +const user = ref(null) +user.value = { name: 'Alice' } // Error: Type '{ name: string }' is not assignable to 'null' + +// ✅ 用联合类型 +const user = ref(null) + +// ❌ 函数参数默认值 = [] 推断为 never[] +function useList(initialItems = []) { /* ... */ } // Error: Parameter 'initialItems' implicitly has 'never[]' + +// ✅ 显式标注参数类型 +function useList(initialItems: ListItem[] = []) { /* ... */ } +``` + +**速查表**: + +| 场景 | 错误写法 | 正确写法 | +|------|---------|---------| +| 空数组 ref | `ref([])` | `ref([])` | +| 可空 ref | `ref(null)` | `ref(null)` | +| 空数组默认参数 | `fn(items = [])` | `fn(items: T[] = [])` | +| 空对象默认参数 | `fn(opts = {})` | `fn(opts: Options = {})` | + +## JSDoc 类型注释 + +复杂函数和公共 API 使用 JSDoc 注释提升可读性和 IDE 提示。 +JSDoc 注释是**推荐**的,但**函数参数的类型注解是强制的**(`strict: true`)。 + +```typescript +/** + * @param id - 用户 ID + * @param options - 查询选项 + */ +async function getUserById(id: number, options: { includeRoles?: boolean } = {}) { + // ... +} +``` + +## 错误处理 + +```typescript +// ✅ 自定义 Error 类 +class AppError extends Error { + code: string + statusCode: number + + constructor(message: string, code: string, statusCode = 500, cause?: unknown) { + super(message, { cause }) + this.name = 'AppError' + this.code = code + this.statusCode = statusCode + } +} + +// ✅ 错误处理模式 +try { + await riskyOperation() +} catch (error) { + if (error instanceof AppError) { + logger.error('Known error', { code: error.code, message: error.message }) + } else { + logger.error('Unexpected error', { error }) + throw new AppError('Internal error', 'INTERNAL', 500, error) + } +} +``` + +## 命名规范 + +| 类型 | 规范 | 示例 | +|------|------|------| +| 函数 / 变量 | camelCase,动词开头 | `getUserById` | +| 类 / 构造函数 | PascalCase | `UserProfile` | +| 常量 | SCREAMING_SNAKE_CASE | `MAX_RETRY_COUNT` | +| 文件(非组件) | kebab-case | `user-service.ts` | +| Vue 组件文件 | PascalCase | `UserProfile.vue` | +| Composable | camelCase,`use` 前缀 | `useTable.ts` | + +## Vue 3 TypeScript 模式 + +```typescript +// ✅ defineProps 对象语法(JS 中不能用泛型) +const props = defineProps({ + title: { + type: String, + required: true, + }, + count: { + type: Number, + default: 0, + }, + items: { + type: Array, + default: () => [], + }, +}) + +// ✅ defineEmits 数组语法 +const emit = defineEmits(['submit', 'cancel', 'update']) + +// ✅ ref(基础类型) +const count = ref(0) +const isVisible = ref(false) + +// ✅ ref(复杂类型,用泛型标注) +const user = ref(null) +const items = ref([]) + +// ✅ computed +const fullName = computed(() => `${user.value?.firstName} ${user.value?.lastName}`) +``` + +## 表单验证 + +**管理端**:使用 Element Plus `el-form` rules(不用第三方 schema 库) + +```typescript +// ✅ 管理端:Element Plus 表单规则 +const rules = { + email: [ + { required: true, message: '请输入邮箱', trigger: 'blur' }, + { type: 'email', message: '邮箱格式不正确', trigger: 'blur' }, + ], + name: [ + { required: true, message: '请输入名称', trigger: 'blur' }, + { min: 1, max: 100, message: '长度 1-100 字符', trigger: 'blur' }, + ], +} +``` + +**用户端**:使用原生 HTML5 验证 + 自定义 composable(禁止引入 Element Plus) + +## 模块导出规范 + +```typescript +// ✅ Composable:具名导出,参数和返回值有类型 +export function useTable(fetchFn: (params: QueryParams) => Promise) { /* ... */ } + +// ✅ Vue 组件:default export(Vue 惯例) +// UserProfile.vue 会自动识别,无需手动 export default + +// ✅ 工具函数:具名导出,参数有类型 +export function formatDate(date: Date | string, format = 'YYYY-MM-DD'): string { /* ... */ } +export function debounce unknown>(fn: T, delay: number): T { /* ... */ } + +// ✅ API 模块:具名导出 +export const userApi = { + list: (params: UserListParams) => request.get('/api/users', { params }), + create: (data: CreateUserData) => request.post('/api/users', data), +} +``` + +## Composable 类型规范(use*.ts) + +Composable 是提取到独立文件的逻辑单元,**必须**满足以下类型要求: + +```typescript +// ✅ 参数有类型,内部 ref 有泛型,导入业务类型 +import type { Comment } from './types' + +export function useComments(initialComments: Comment[]) { + const comments = ref([...initialComments]) + const newComment = ref('') + + async function addComment(content: string) { /* ... */ } + + return { comments, newComment, addComment } +} + +// ❌ 参数无类型、ref 无泛型 → noImplicitAny 报错 + never[] 推断 +export function useComments(initialComments = []) { + const comments = ref([...initialComments]) // Ref + // ... +} +``` + +**Composable 检查清单**: +- [ ] 所有参数有显式类型注解 +- [ ] `ref([])` / `ref(null)` 有泛型标注 +- [ ] 业务类型从 types 文件导入(`import type`),不内联定义 +- [ ] 预留参数(暂未使用)用 `_` 前缀:`_caseId: string` + +## 禁止的写法 + +```typescript +// ❌ var +var count = 0 + +// ❌ CommonJS +const fs = require('fs') +module.exports = { fn } + +// ❌ 裸 .then() 链 +fetch('/api').then(res => res.json()).then(data => console.log(data)) + +// ❌ 空 catch +try { ... } catch (e) {} + +// ❌ 直接修改 props +props.count++ + +// ❌ 隐式 any(strict 模式下编译报错) +function process(data) { /* ... */ } // data: any +const handler = (e) => { /* ... */ } // e: any + +// ❌ 无泛型的空数组/null ref(推断为 never[] / null) +const list = ref([]) // Ref +const user = ref(null) // Ref + +// ❌ 默认值为空数组但无类型(推断为 never[]) +function useList(items = []) { /* ... */ } // items: never[] +``` diff --git a/.cursor/rules/011-vue.mdc b/.cursor/rules/011-vue.mdc new file mode 100644 index 0000000..c745c46 --- /dev/null +++ b/.cursor/rules/011-vue.mdc @@ -0,0 +1,200 @@ +--- +description: "Vue 3 + Vite 前端开发规范(TypeScript)。管理端使用 Element Plus,用户端使用 Headless UI + Tailwind(禁止 Element Plus)" +globs: + - "**/*.vue" + - "Case-Database-Frontend-user/src/**/*.ts" + - "Case-Database-Frontend-admin/src/**/*.ts" +alwaysApply: false +--- + +# Vue 3 / Vite Standards + 双前端上下文 + +> **双前端区分**:管理端 (`Case-Database-Frontend-admin/`) 使用 Element Plus; +> 用户端 (`Case-Database-Frontend-user/`) 使用 Headless UI + Tailwind CSS,**禁止引入 Element Plus**。 + +## 组件与脚本约束 + +- 默认使用 ` + + +``` + +## Element Plus 使用规范(仅管理端) + +> 以下内容仅适用于 `Case-Database-Frontend-admin/`,用户端禁止使用 Element Plus。 + +- **按需导入**:使用 `unplugin-vue-components` + `unplugin-auto-import` 自动导入 +- **表单验证**:使用 Element Plus 内置 `el-form` rules,复杂场景配合 `async-validator` +- **主题定制**:通过 SCSS 变量覆盖 Element Plus 默认主题 +- **图标**:使用 `@element-plus/icons-vue`,按需导入 + +```typescript +// vite.config.ts 自动导入配置 +import AutoImport from 'unplugin-auto-import/vite' +import Components from 'unplugin-vue-components/vite' +import { ElementPlusResolver } from 'unplugin-vue-components/resolvers' + +export default defineConfig({ + plugins: [ + AutoImport({ resolvers: [ElementPlusResolver()] }), + Components({ resolvers: [ElementPlusResolver()] }), + ], +}) +``` + +## 状态管理选择 + +| 场景 | 方案 | +|------|------| +| 组件内部 | `ref` / `reactive` | +| 计算属性 | `computed` | +| 跨组件共享 | **Pinia** (`defineStore`) | +| 服务端状态 | Axios + Pinia Action | +| 表单验证 | 管理端:Element Plus `el-form` rules;用户端:自定义验证 | +| URL 状态 | `useRoute` / `useRouter`(vue-router) | + +## Axios 封装规范 + +```typescript +// src/utils/request.ts +import axios from 'axios' + +const service = axios.create({ + baseURL: import.meta.env.VITE_API_URL, + timeout: 60000, + headers: { 'Content-Type': 'application/json;charset=UTF-8' }, +}) + +// Request interceptor: inject token +service.interceptors.request.use((config) => { + const token = localStorage.getItem('access_token') + if (token) { + config.headers.Authorization = `Bearer ${token}` + } + return config +}) + +// Response interceptor: unified error handling +service.interceptors.response.use( + (response) => { + const { code, data, message } = response.data + if (code === 200) return data + return Promise.reject(new Error(message || 'Request failed')) + }, + (error) => { + if (error.response?.status === 401) { + // Token expired -> redirect to login + } + return Promise.reject(error) + }, +) +``` + +## Vue Router 路由规范 + +```typescript +// src/router/guards/auth.ts +export function setupAuthGuard(router) { + router.beforeEach(async (to, _from, next) => { + const token = localStorage.getItem('access_token') + + if (to.meta.requiresAuth && !token) { + return next({ name: 'login', query: { redirect: to.fullPath } }) + } + + // Dynamic route loading for RBAC + if (token && !hasLoadedDynamicRoutes()) { + await loadDynamicRoutes(router) + return next({ ...to, replace: true }) + } + + next() + }) +} +``` + +## Composables 目录约定 + +所有 composable 位于 `src/hooks/`,以 `use` 前缀命名: + +| Composable | 职责 | 返回值 | +|------------|------|--------| +| `useTable` | 表格数据加载、分页、搜索、排序 | `{ loading, dataList, pagination, loadData, handleSearch, handleReset }` | +| `useForm` | 表单状态、校验、提交 | `{ formRef, formData, rules, loading, handleSubmit, handleReset }` | +| `useAuth` | 权限判断、按钮/菜单可见性 | `{ hasAuth, hasRole, checkPermission }` | +| `useCommon` | 通用工具(字典、枚举转换) | `{ getDictLabel, formatEnum }` | +| `useFastEnter` | 快速导航、全局搜索 | `{ visible, keyword, results, navigate }` | +| `useHeaderBar` | 顶部栏状态(全屏、消息、用户) | `{ isFullscreen, toggleFullscreen, unreadCount }` | +| `useSmsCode` | 短信验证码倒计时 | `{ countdown, isSending, sendCode }` | +| `useTableColumns` | 表格列配置、列显隐持久化 | `{ columns, visibleColumns, toggleColumn }` | +| `useTheme` | 主题切换 + 系统跟随 | `{ isDark, toggleTheme, colorPrimary }` | +| `useChart` | ECharts 实例管理、自动 resize | `{ chartRef, setOption, resize }` | +| `useWebSocket` | WebSocket 连接管理 | `{ connect, disconnect, send, onMessage }` | + +```typescript +// src/hooks/useTable.ts — 标准实现 +export function useTable(fetchFn) { + const loading = ref(false) + const dataList = ref([]) + const pagination = reactive({ current: 1, size: 10, total: 0 }) + const searchForm = reactive({}) + + async function loadData() { + loading.value = true + try { + const result = await fetchFn({ + page: pagination.current, + page_size: pagination.size, + ...searchForm, + }) + dataList.value = result.data + pagination.total = result.total + } finally { + loading.value = false + } + } + + function handleSearch() { + pagination.current = 1 + loadData() + } + + function handleReset() { + Object.keys(searchForm).forEach((key) => { searchForm[key] = undefined }) + handleSearch() + } + + function handlePageChange(page) { + pagination.current = page + loadData() + } + + return { loading, dataList, pagination, searchForm, loadData, handleSearch, handleReset, handlePageChange } +} +``` + +## 自定义指令 + +所有指令位于 `src/directives/`: + +| 指令 | 用途 | 用法示例 | +|------|------|---------| +| `v-auth` | 按钮级权限控制(权限标识) | `v-auth="'system:user:add'"` | +| `v-roles` | 角色级权限控制 | `v-roles="['admin', 'manager']"` | +| `v-focus` | 自动聚焦输入框 | `v-focus` | +| `v-highlight` | 搜索关键词高亮 | `v-highlight="keyword"` | +| `v-index` | 序号自动生成 | `v-index="{ page, size }"` | +| `v-ripple` | 点击波纹效果 | `v-ripple` | + +```typescript +// src/directives/auth.ts +import { useUserStore } from '@/store/modules/user' + +export const vAuth = { + mounted(el, binding) { + const userStore = useUserStore() + if (!userStore.permissions.includes(binding.value)) { + el.parentNode?.removeChild(el) + } + }, +} + +// src/directives/roles.ts +export const vRoles = { + mounted(el, binding) { + const userStore = useUserStore() + const hasRole = binding.value.some((role) => userStore.roles.includes(role)) + if (!hasRole) { + el.parentNode?.removeChild(el) + } + }, +} +``` + +## 布局组件约定 + +布局组件位于 `src/layouts/`: + +| 组件 | 职责 | +|------|------| +| `ArtSidebarMenu` | 侧边导航菜单(递归菜单树) | +| `ArtHeader` | 顶部栏(面包屑 + 用户 + 通知 + 全屏) | +| `ArtWorkTab` | 多标签页管理(右键菜单、拖拽排序) | +| `ArtNotification` | 通知面板(WebSocket 实时推送) | +| `ArtChatWindow` | 即时通讯窗口 | +| `ArtFastEnter` | 全局快速搜索导航(Ctrl+K) | +| `ArtGlobalSearch` | 全局搜索弹窗 | + +## Pinia Store 模块映射 + +| Store | 职责 | 持久化 | 加密 | +|-------|------|--------|------| +| `user` | 用户信息、Token、权限、角色 | ✅ | ✅ Token 加密 | +| `menu` | 菜单树、动态路由 | ✅ | ❌ | +| `setting` | 主题、布局、语言设置 | ✅ | ❌ | +| `worktab` | 标签页状态 | ✅ | ❌ | +| `table` | 表格列配置、列显隐 | ✅ | ❌ | +| `notification` | 通知消息、未读计数 | ❌ | ❌ | +| `workflow` | 审批流程状态 | ❌ | ❌ | +| `product` | 产品/生产模块状态 | ❌ | ❌ | +| `outsideflow` | 外部审批流程 | ❌ | ❌ | + +```typescript +// Pinia 加密持久化示例 +import CryptoJS from 'crypto-js' + +const ENCRYPT_KEY = import.meta.env.VITE_STORAGE_KEY || 'default-key' + +function encrypt(data) { + return CryptoJS.AES.encrypt(data, ENCRYPT_KEY).toString() +} + +function decrypt(data) { + return CryptoJS.AES.decrypt(data, ENCRYPT_KEY).toString(CryptoJS.enc.Utf8) +} + +export const useUserStore = defineStore('user', () => { + // ... state and actions +}, { + persist: { + key: 'user-store', + storage: { + getItem: (key) => { + const raw = localStorage.getItem(key) + return raw ? decrypt(raw) : null + }, + setItem: (key, value) => { + localStorage.setItem(key, encrypt(value)) + }, + }, + pick: ['token', 'refreshToken'], + }, +}) +``` + +## 路由守卫完整流程 + +```typescript +// src/router/guards/index.ts +import NProgress from 'nprogress' +import { useUserStore } from '@/store/modules/user' +import { useMenuStore } from '@/store/modules/menu' +import { useWorktabStore } from '@/store/modules/worktab' + +const WHITE_LIST = ['/login', '/register', '/404', '/403'] + +export function setupRouterGuards(router) { + router.beforeEach(async (to, _from, next) => { + NProgress.start() + document.title = `${to.meta.title || ''} - ${import.meta.env.VITE_APP_TITLE}` + + const userStore = useUserStore() + const token = userStore.token + + // Step 1: 白名单放行 + if (WHITE_LIST.includes(to.path)) { + return next() + } + + // Step 2: 无 Token -> 登录 + if (!token) { + return next({ path: '/login', query: { redirect: to.fullPath } }) + } + + // Step 3: 已登录访问登录页 -> 首页 + if (to.path === '/login') { + return next({ path: '/' }) + } + + // Step 4: 动态路由未加载 -> 加载 + const menuStore = useMenuStore() + if (!menuStore.isRoutesLoaded) { + try { + await menuStore.loadDynamicRoutes(router) + return next({ ...to, replace: true }) + } catch { + userStore.logout() + return next({ path: '/login' }) + } + } + + next() + }) + + router.afterEach((to) => { + NProgress.done() + + // Step 5: 更新标签页 + const worktabStore = useWorktabStore() + if (to.meta.title && !to.meta.hideTab) { + worktabStore.addTab({ + path: to.path, + title: to.meta.title, + name: to.name, + }) + } + }) +} +``` + +## WebSocket 集成模式 + +```typescript +// src/utils/websocket/notification.ts +import { useUserStore } from '@/store/modules/user' +import { useNotificationStore } from '@/store/modules/notification' + +class NotificationWebSocket { + ws = null + reconnectTimer = null + heartbeatTimer = null + reconnectAttempts = 0 + maxReconnectAttempts = 5 + + connect() { + const userStore = useUserStore() + if (!userStore.token) return + + const wsUrl = `${import.meta.env.VITE_WS_URL}?token=${userStore.token}` + this.ws = new WebSocket(wsUrl) + + this.ws.onopen = () => { + this.reconnectAttempts = 0 + this.startHeartbeat() + } + + this.ws.onmessage = (event) => { + const message = JSON.parse(event.data) + this.handleMessage(message) + } + + this.ws.onclose = () => { + this.stopHeartbeat() + this.tryReconnect() + } + } + + handleMessage(message) { + const notificationStore = useNotificationStore() + switch (message.type) { + case 'notification': + notificationStore.addNotification(message.data) + break + case 'heartbeat': + // Pong + break + } + } + + startHeartbeat() { + this.heartbeatTimer = setInterval(() => { + this.ws?.send(JSON.stringify({ type: 'heartbeat', data: 'ping' })) + }, 30000) + } + + stopHeartbeat() { + if (this.heartbeatTimer) clearInterval(this.heartbeatTimer) + } + + tryReconnect() { + if (this.reconnectAttempts >= this.maxReconnectAttempts) return + this.reconnectTimer = setTimeout(() => { + this.reconnectAttempts++ + this.connect() + }, Math.min(1000 * 2 ** this.reconnectAttempts, 30000)) + } + + disconnect() { + this.stopHeartbeat() + if (this.reconnectTimer) clearTimeout(this.reconnectTimer) + this.ws?.close() + this.ws = null + } +} + +export const notificationWs = new NotificationWebSocket() +``` + +## 性能检查清单 + +- [ ] 路由使用 `() => import()` 懒加载 +- [ ] 列表使用稳定 `:key`(非 index) +- [ ] 大列表使用虚拟化(`@tanstack/vue-virtual`) +- [ ] 使用 `defineAsyncComponent` 做组件懒加载 +- [ ] 避免在 `