From a26552c86b9c07f837bee083eaa1e1149fb709c9 Mon Sep 17 00:00:00 2001 From: lintsinghua Date: Fri, 31 Oct 2025 22:36:43 +0800 Subject: [PATCH] feat(languages): Expand supported languages and improve code analysis - Add Swift and Kotlin as supported programming languages - Update language detection and file upload logic in InstantAnalysis page - Refactor language-related code to use centralized SUPPORTED_LANGUAGES constant - Enhance project detail page to handle repository scanning with more flexibility - Improve error handling and user guidance for project audits - Add language name formatting utility in ProjectDetail component - Update file input accept types to include new language extensions --- .../analysis/services/codeAnalysis.ts | 7 +- src/pages/InstantAnalysis.tsx | 83 ++++++++++++++++++- src/pages/ProjectDetail.tsx | 41 ++++++--- src/pages/Projects.tsx | 24 +++++- 4 files changed, 134 insertions(+), 21 deletions(-) diff --git a/src/features/analysis/services/codeAnalysis.ts b/src/features/analysis/services/codeAnalysis.ts index c6e1fc6..f401bbe 100644 --- a/src/features/analysis/services/codeAnalysis.ts +++ b/src/features/analysis/services/codeAnalysis.ts @@ -2,15 +2,12 @@ import type { CodeAnalysisResult } from "@/shared/types"; import { LLMService } from '@/shared/services/llm'; import { getCurrentLLMApiKey, getCurrentLLMModel, env } from '@/shared/config/env'; import type { LLMConfig } from '@/shared/services/llm/types'; +import { SUPPORTED_LANGUAGES } from '@/shared/constants'; // 基于 LLM 的代码分析引擎 export class CodeAnalysisEngine { - private static readonly SUPPORTED_LANGUAGES = [ - 'javascript', 'typescript', 'python', 'java', 'go', 'rust', 'cpp', 'csharp', 'php', 'ruby' - ]; - static getSupportedLanguages(): string[] { - return [...this.SUPPORTED_LANGUAGES]; + return [...SUPPORTED_LANGUAGES]; } /** diff --git a/src/pages/InstantAnalysis.tsx b/src/pages/InstantAnalysis.tsx index 0810cc4..c5e5488 100644 --- a/src/pages/InstantAnalysis.tsx +++ b/src/pages/InstantAnalysis.tsx @@ -146,6 +146,65 @@ public class Example { private String getData() { return "data"; } +}`, + swift: `// 示例Swift代码 - 包含多种问题 +import Foundation + +class UserManager { + var password = "admin123" // 硬编码密码 + + func validateUser(input: String) -> Bool { + if input == password { // 直接比较密码 + print("User validated") // 使用print而非日志 + return true + } + return false + } + + // 强制解包可能导致崩溃 + func processData(data: [String]?) { + let items = data! // 强制解包 + for item in items { + print(item) + } + } + + // 内存泄漏风险:循环引用 + var closure: (() -> Void)? + func setupClosure() { + closure = { + print(self.password) // 未使用 [weak self] + } + } +}`, + kotlin: `// 示例Kotlin代码 - 包含多种问题 +class UserManager { + private val password = "admin123" // 硬编码密码 + + fun validateUser(input: String): Boolean { + if (input == password) { // 直接比较密码 + println("User validated") // 使用println而非日志 + return true + } + return false + } + + // 空指针风险 + fun processData(data: List?) { + val items = data!! // 强制非空断言 + for (item in items) { + println(item) + } + } + + // 性能问题:循环中重复计算 + fun inefficientLoop(items: List) { + for (i in 0 until items.size) { + for (j in 0 until items.size) { // O(n²) 复杂度 + println(items[i] + items[j]) + } + } + } }` }; @@ -231,7 +290,9 @@ public class Example { 'hh': 'cpp', 'cs': 'csharp', 'php': 'php', - 'rb': 'ruby' + 'rb': 'ruby', + 'swift': 'swift', + 'kt': 'kotlin' }; if (extension && languageMap[extension]) { @@ -526,7 +587,7 @@ public class Example { @@ -562,6 +623,24 @@ public class Example { > Java + + {/* 代码编辑器 */} diff --git a/src/pages/ProjectDetail.tsx b/src/pages/ProjectDetail.tsx index 1c7a2f0..fd39b11 100644 --- a/src/pages/ProjectDetail.tsx +++ b/src/pages/ProjectDetail.tsx @@ -30,6 +30,7 @@ import { loadZipFile } from "@/shared/utils/zipStorage"; import { toast } from "sonner"; import CreateTaskDialog from "@/components/audit/CreateTaskDialog"; import TerminalProgressDialog from "@/components/audit/TerminalProgressDialog"; +import { SUPPORTED_LANGUAGES } from "@/shared/constants"; export default function ProjectDetail() { const { id } = useParams<{ id: string }>(); @@ -50,9 +51,26 @@ export default function ProjectDetail() { programming_languages: [] }); - const supportedLanguages = [ - 'JavaScript', 'TypeScript', 'Python', 'Java', 'Go', 'Rust', 'C++', 'C#', 'PHP', 'Ruby' - ]; + // 将小写语言名转换为显示格式 + const formatLanguageName = (lang: string): string => { + const nameMap: Record = { + 'javascript': 'JavaScript', + 'typescript': 'TypeScript', + 'python': 'Python', + 'java': 'Java', + 'go': 'Go', + 'rust': 'Rust', + 'cpp': 'C++', + 'csharp': 'C#', + 'php': 'PHP', + 'ruby': 'Ruby', + 'swift': 'Swift', + 'kotlin': 'Kotlin' + }; + return nameMap[lang] || lang.charAt(0).toUpperCase() + lang.slice(1); + }; + + const supportedLanguages = SUPPORTED_LANGUAGES.map(formatLanguageName); useEffect(() => { if (id) { @@ -83,16 +101,18 @@ export default function ProjectDetail() { const handleRunAudit = async () => { if (!project || !id) return; - // 如果是GitHub项目且有仓库地址,直接启动审计 - if (project.repository_type === 'github' && project.repository_url) { + // 检查是否有仓库地址 + if (project.repository_url) { + // 有仓库地址,启动仓库审计 try { setScanning(true); - console.log('开始启动审计任务...'); + console.log('开始启动仓库审计任务...'); const taskId = await runRepositoryAudit({ projectId: id, repoUrl: project.repository_url, branch: project.default_branch || 'main', githubToken: undefined, + gitlabToken: undefined, createdBy: undefined }); @@ -111,7 +131,7 @@ export default function ProjectDetail() { setScanning(false); } } else { - // 对于ZIP项目,尝试从IndexedDB加载保存的文件 + // 没有仓库地址,尝试从IndexedDB加载保存的ZIP文件 try { setScanning(true); const file = await loadZipFile(id); @@ -143,14 +163,13 @@ export default function ProjectDetail() { } } else { setScanning(false); - toast.error('未找到保存的ZIP文件,请通过"新建任务"上传'); - setShowCreateTaskDialog(true); + toast.warning('此项目未配置仓库地址,也未上传ZIP文件。请先在项目设置中配置仓库地址,或通过"新建任务"上传ZIP文件。'); + // 不自动打开对话框,让用户自己选择 } } catch (error) { console.error('启动审计失败:', error); setScanning(false); - toast.error('读取ZIP文件失败,请通过"新建任务"重新上传'); - setShowCreateTaskDialog(true); + toast.error('读取ZIP文件失败,请检查项目配置'); } } }; diff --git a/src/pages/Projects.tsx b/src/pages/Projects.tsx index 6dc113a..740c5d5 100644 --- a/src/pages/Projects.tsx +++ b/src/pages/Projects.tsx @@ -35,6 +35,7 @@ import { saveZipFile } from "@/shared/utils/zipStorage"; import { Link } from "react-router-dom"; import { toast } from "sonner"; import CreateTaskDialog from "@/components/audit/CreateTaskDialog"; +import { SUPPORTED_LANGUAGES } from "@/shared/constants"; export default function Projects() { const [projects, setProjects] = useState([]); @@ -67,9 +68,26 @@ export default function Projects() { programming_languages: [] }); - const supportedLanguages = [ - 'JavaScript', 'TypeScript', 'Python', 'Java', 'Go', 'Rust', 'C++', 'C#', 'PHP', 'Ruby' - ]; + // 将小写语言名转换为显示格式 + const formatLanguageName = (lang: string): string => { + const nameMap: Record = { + 'javascript': 'JavaScript', + 'typescript': 'TypeScript', + 'python': 'Python', + 'java': 'Java', + 'go': 'Go', + 'rust': 'Rust', + 'cpp': 'C++', + 'csharp': 'C#', + 'php': 'PHP', + 'ruby': 'Ruby', + 'swift': 'Swift', + 'kotlin': 'Kotlin' + }; + return nameMap[lang] || lang.charAt(0).toUpperCase() + lang.slice(1); + }; + + const supportedLanguages = SUPPORTED_LANGUAGES.map(formatLanguageName); useEffect(() => { loadProjects();