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
This commit is contained in:
lintsinghua 2025-10-31 22:36:43 +08:00
parent d8f6b2470a
commit a26552c86b
4 changed files with 134 additions and 21 deletions

View File

@ -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];
}
/**

View File

@ -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<String>?) {
val items = data!! // 强制非空断言
for (item in items) {
println(item)
}
}
// 性能问题:循环中重复计算
fun inefficientLoop(items: List<String>) {
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 {
<input
ref={fileInputRef}
type="file"
accept=".js,.jsx,.ts,.tsx,.py,.java,.go,.rs,.cpp,.c,.cc,.h,.hh,.cs,.php,.rb"
accept=".js,.jsx,.ts,.tsx,.py,.java,.go,.rs,.cpp,.c,.cc,.h,.hh,.cs,.php,.rb,.swift,.kt"
onChange={handleFileUpload}
className="hidden"
/>
@ -562,6 +623,24 @@ public class Example {
>
Java
</Button>
<Button
variant="outline"
size="sm"
onClick={() => loadExampleCode('swift')}
disabled={analyzing}
className="h-7 px-2 text-xs"
>
Swift
</Button>
<Button
variant="outline"
size="sm"
onClick={() => loadExampleCode('kotlin')}
disabled={analyzing}
className="h-7 px-2 text-xs"
>
Kotlin
</Button>
</div>
{/* 代码编辑器 */}

View File

@ -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<string, string> = {
'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文件失败请检查项目配置');
}
}
};

View File

@ -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<Project[]>([]);
@ -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<string, string> = {
'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();