Skip to content

Commit 02b00fc

Browse files
🔧 chore(config): 重构周报功能并添加 Gemini AI 支持
- 移除原有周报的配置项,重新设计周报生成功能 - 添加 Gemini AI 提供商支持及相关配置项 - 重构周报生成服务,拆分业务逻辑为独立策略类 - 新增 Commit 日志和作者信息获取策略类 - 优化周报 WebView UI,支持富文本编辑 - 添加相关国际化文案内容
1 parent 04e999e commit 02b00fc

14 files changed

+560
-443
lines changed

i18n/zh-cn.json

+10-1
Original file line numberDiff line numberDiff line change
@@ -64,5 +64,14 @@
6464
"command.register.failed": "注册命令失败: {0}",
6565
"command.generate.failed": "生成提交信息失败: {0}",
6666
"command.select.model.failed": "选择模型失败: {0}",
67-
"ai.model.loading": "正在加载 AI 模型列表..."
67+
"ai.model.loading": "正在加载 AI 模型列表...",
68+
"weeklyReport.generating": "正在生成周报...",
69+
"weeklyReport.empty.response": "AI 生成内容为空",
70+
"weeklyReport.generation.success": "周报生成成功",
71+
"weeklyReport.generation.failed": "生成周报失败: {0}",
72+
"weeklyReport.copy.success": "内容已复制到剪贴板",
73+
"weeklyReport.copy.failed": "复制失败: {0}",
74+
"author.svn.not.found": "无法获取 SVN 作者信息",
75+
"author.manual.input.prompt": "无法自动获取用户名,请手动输入",
76+
"author.manual.input.placeholder": "请输入用户名"
6877
}

package.json

+6-31
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,11 @@
168168
"default": "http://localhost:11434",
169169
"description": "Ollama API 基础 URL"
170170
},
171+
"dish-ai-commit.providers.gemini.apiKey": {
172+
"type": "string",
173+
"default": "",
174+
"description": "Gemini AI API 密钥"
175+
},
171176
"dish-ai-commit.features.codeAnalysis.simplifyDiff": {
172177
"type": "boolean",
173178
"default": false,
@@ -192,36 +197,6 @@
192197
"type": "boolean",
193198
"default": true,
194199
"description": "在提交信息中使用 emoji"
195-
},
196-
"dish-ai-commit.weeklyReport.template": {
197-
"type": "string",
198-
"default": "",
199-
"description": "周报模板"
200-
},
201-
"dish-ai-commit.weeklyReport.autoSave": {
202-
"type": "boolean",
203-
"default": true,
204-
"description": "自动保存周报"
205-
},
206-
"dish-ai-commit.weeklyReport": {
207-
"type": "object",
208-
"properties": {
209-
"totalHours": {
210-
"type": "number",
211-
"default": 40,
212-
"description": "每周工作总时长"
213-
},
214-
"totalDays": {
215-
"type": "number",
216-
"default": 7,
217-
"description": "统计天数"
218-
},
219-
"minUnit": {
220-
"type": "number",
221-
"default": 0.5,
222-
"description": "最小工时单位"
223-
}
224-
}
225200
}
226201
}
227202
},
@@ -332,4 +307,4 @@
332307
]
333308
}
334309
}
335-
}
310+
}

src/ai/AIProviderFactory.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { LocalizationManager } from "../utils/LocalizationManager";
88
import { ZhipuAIProvider } from "./providers/ZhipuAIProvider";
99
import { DashScopeProvider } from "./providers/DashScopeProvider";
1010
import { DoubaoProvider } from "./providers/DoubaoProvider";
11+
import { GeminiAIProvider } from "./providers/GeminiAIProvider";
1112

1213
export class AIProviderFactory {
1314
private static providers: Map<string, AIProviderInterface> = new Map();
@@ -34,7 +35,7 @@ export class AIProviderFactory {
3435
let provider = this.providers.get(providerType);
3536
console.log("AIProvider", AIProvider);
3637
console.log("providerType", providerType.toLowerCase());
37-
console.log("AIProvider.VSCODE", AIProvider.ZHIPU);
38+
console.log("AIProvider.VSCODE", AIProvider.ZHIPUAI);
3839
if (!provider) {
3940
switch (providerType.toLowerCase()) {
4041
case AIProvider.OPENAI:
@@ -55,6 +56,9 @@ export class AIProviderFactory {
5556
case AIProvider.DOUBAO:
5657
provider = new DoubaoProvider();
5758
break;
59+
case AIProvider.Gemini:
60+
provider = new GeminiAIProvider();
61+
break;
5862
default:
5963
throw new Error(
6064
LocalizationManager.getInstance().format(
@@ -78,6 +82,7 @@ export class AIProviderFactory {
7882
new ZhipuAIProvider(),
7983
new DashScopeProvider(),
8084
new DoubaoProvider(),
85+
new GeminiAIProvider(),
8186
];
8287
}
8388

src/ai/providers/GeminiAIProvider.ts

+74
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import { BaseOpenAIProvider } from "./BaseOpenAIProvider";
2+
import { ConfigurationManager } from "../../config/ConfigurationManager";
3+
import { AIModel } from "../types";
4+
5+
const geminiModels: AIModel[] = [
6+
{
7+
id: "gemini-1.5-flash",
8+
name: "Gemini 1.5 Flash - 在各种任务中提供快速、多样化的性能",
9+
maxTokens: { input: 100000, output: 8000 },
10+
provider: { id: "gemini", name: "Gemini AI" },
11+
default: true,
12+
capabilities: {
13+
streaming: true,
14+
functionCalling: false,
15+
},
16+
},
17+
{
18+
id: "gemini-1.5-flash-8b",
19+
name: "Gemini 1.5 Flash-8B - 适用于量大且智能程度较低的任务",
20+
maxTokens: { input: 100000, output: 8000 },
21+
provider: { id: "gemini", name: "Gemini AI" },
22+
capabilities: {
23+
streaming: true,
24+
functionCalling: false,
25+
},
26+
},
27+
{
28+
id: "gemini-1.5-pro",
29+
name: "Gemini 1.5 Pro - 适用于需要更多智能的复杂推理任务",
30+
maxTokens: { input: 100000, output: 8000 },
31+
provider: { id: "gemini", name: "Gemini AI" },
32+
capabilities: {
33+
streaming: true,
34+
functionCalling: false,
35+
},
36+
},
37+
{
38+
id: "gemini-2.0-flash-exp",
39+
name: "Gemini 2.0 Flash Experimental - 提供多模态理解和生成,支持原生工具使用",
40+
maxTokens: { input: 128000, output: 8000 },
41+
provider: { id: "gemini", name: "Gemini AI" },
42+
capabilities: {
43+
streaming: true,
44+
functionCalling: true,
45+
},
46+
},
47+
// 可以根据需要添加更多模型
48+
];
49+
50+
export class GeminiAIProvider extends BaseOpenAIProvider {
51+
constructor() {
52+
const configManager = ConfigurationManager.getInstance();
53+
super({
54+
apiKey: configManager.getConfig("PROVIDERS_GEMINI_APIKEY", false),
55+
baseURL: "https://generativelanguage.googleapis.com/v1beta/openai/",
56+
providerId: "gemini",
57+
providerName: "Gemini AI",
58+
models: geminiModels,
59+
defaultModel: "gemini-1",
60+
});
61+
}
62+
63+
async isAvailable(): Promise<boolean> {
64+
try {
65+
return !!this.config.apiKey;
66+
} catch {
67+
return false;
68+
}
69+
}
70+
71+
async refreshModels(): Promise<string[]> {
72+
return Promise.resolve(geminiModels.map((m) => m.id));
73+
}
74+
}

src/ai/types.ts

+10-1
Original file line numberDiff line numberDiff line change
@@ -147,14 +147,21 @@ export type DoubaoModels =
147147
| "doubao-pro-256k"
148148
| "doubao-vision-pro-32k";
149149

150+
export type GeminiAIModels =
151+
| "gemini-1.5-flash"
152+
| "gemini-1.5-flash-8b"
153+
| "gemini-1.5-pro"
154+
| "gemini-2.0-flash-exp";
155+
150156
export type AIProviders =
151157
| "anthropic"
152158
| "github"
153159
| "openai"
154160
| "vscode"
155161
| "zhipu"
156162
| "dashscope"
157-
| "doubao"; // 添加doubao
163+
| "doubao"
164+
| "gemini";
158165

159166
export type AIModels<Provider extends AIProviders = AIProviders> =
160167
Provider extends "github"
@@ -169,6 +176,8 @@ export type AIModels<Provider extends AIProviders = AIProviders> =
169176
? DashScopeModels
170177
: Provider extends "doubao"
171178
? DoubaoModels
179+
: Provider extends "gemini"
180+
? GeminiAIModels
172181
: OpenAIModels;
173182

174183
export type SupportedAIModels =
+2-109
Original file line numberDiff line numberDiff line change
@@ -1,117 +1,10 @@
11
import * as vscode from "vscode";
22
import { BaseCommand } from "./BaseCommand";
3-
import { NotificationHandler } from "../utils/NotificationHandler";
4-
import { ProgressHandler } from "../utils/ProgressHandler";
5-
import { LocalizationManager } from "../utils/LocalizationManager";
63
import { WeeklyReportPanel } from "../webview/WeeklyReportPanel";
7-
import { SCMFactory } from "../scm/SCMProvider";
8-
import { AIProviderFactory } from "../ai/AIProviderFactory";
9-
import { exec } from "child_process";
10-
import { ConfigurationManager } from "../config/ConfigurationManager";
114

125
export class GenerateWeeklyReportCommand extends BaseCommand {
13-
async validateConfig(): Promise<boolean> {
14-
const scmProvider = await SCMFactory.detectSCM();
15-
if (!scmProvider) {
16-
const locManager = LocalizationManager.getInstance();
17-
await NotificationHandler.error(
18-
locManager.getMessage("scm.not.detected")
19-
);
20-
return false;
21-
}
22-
return true;
23-
}
24-
25-
async getPeriod(): Promise<string | undefined> {
26-
const options = ["本周", "上一周", "上两周"];
27-
const selection = await vscode.window.showQuickPick(options, {
28-
placeHolder: "选择一个时间段",
29-
});
30-
31-
switch (selection) {
32-
case "本周":
33-
return "1 week ago";
34-
case "上一周":
35-
return "2 weeks ago";
36-
case "上两周":
37-
return "3 weeks ago";
38-
default:
39-
return undefined;
40-
}
41-
}
42-
436
async execute(): Promise<void> {
44-
try {
45-
const period = await this.getPeriod();
46-
if (!period) {
47-
return;
48-
}
49-
50-
await ProgressHandler.withProgress(
51-
LocalizationManager.getInstance().getMessage("weeklyReport.generating"),
52-
async () => {
53-
const scmProvider = await SCMFactory.detectSCM();
54-
if (!scmProvider) {
55-
await NotificationHandler.error(
56-
LocalizationManager.getInstance().getMessage("scm.not.detected")
57-
);
58-
return;
59-
}
60-
61-
const config = ConfigurationManager.getInstance();
62-
const configuration = config.getConfiguration();
63-
64-
// 检查是否已配置 AI 提供商和模型
65-
let provider = configuration.base.provider;
66-
let model = configuration.base.model;
67-
68-
const commits = await this.getCommits(period);
69-
const aiProvider = AIProviderFactory.getProvider("ZHIPUAI");
70-
const response = await aiProvider.generateWeeklyReport(commits);
71-
72-
if (response?.content) {
73-
vscode.window.showInformationMessage("周报生成成功");
74-
WeeklyReportPanel.createOrShow(this.context.extensionUri);
75-
WeeklyReportPanel.currentPanel?._panel.webview.postMessage({
76-
command: "report",
77-
data: response.content,
78-
});
79-
} else {
80-
vscode.window.showErrorMessage("周报生成失败");
81-
}
82-
}
83-
);
84-
} catch (error) {
85-
if (error instanceof Error) {
86-
await NotificationHandler.error(
87-
LocalizationManager.getInstance().format(
88-
"weeklyReport.generation.failed",
89-
error.message
90-
)
91-
);
92-
}
93-
}
94-
}
95-
96-
private async getCommits(period: string): Promise<string[]> {
97-
return new Promise((resolve, reject) => {
98-
const workspaceFolders = vscode.workspace.workspaceFolders;
99-
if (!workspaceFolders || workspaceFolders.length === 0) {
100-
return reject("没有打开的工作区");
101-
}
102-
103-
const command = `git log --since="${period}" --pretty=format:"%h - %an, %ar : %s"`;
104-
exec(
105-
command,
106-
{ cwd: workspaceFolders[0].uri.fsPath },
107-
(error, stdout, stderr) => {
108-
if (error) {
109-
reject(`获取commit历史记录失败: ${stderr}`);
110-
} else {
111-
resolve(stdout.split("\n"));
112-
}
113-
}
114-
);
115-
});
7+
// 只负责打开WebView面板
8+
WeeklyReportPanel.createOrShow(this.context.extensionUri);
1169
}
11710
}

src/config/ConfigSchema.ts

+8
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ export const CONFIG_SCHEMA = {
6363
"ZhipuAI",
6464
"DashScope",
6565
"Doubao",
66+
"Gemini",
6667
],
6768
description: "AI provider",
6869
},
@@ -115,6 +116,13 @@ export const CONFIG_SCHEMA = {
115116
description: "Ollama API 基础 URL",
116117
},
117118
},
119+
gemini: {
120+
apiKey: {
121+
type: "string",
122+
default: "",
123+
description: "Gemini AI API 密钥",
124+
},
125+
},
118126
},
119127
features: {
120128
// 代码分析功能

src/config/generated/configKeys.ts

+2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ export const CONFIG_KEYS = {
1717
"PROVIDERS_DOUBAO_APIKEY": "dish-ai-commit.providers.doubao.apiKey",
1818
"PROVIDERS_OLLAMA": "dish-ai-commit.providers.ollama",
1919
"PROVIDERS_OLLAMA_BASEURL": "dish-ai-commit.providers.ollama.baseUrl",
20+
"PROVIDERS_GEMINI": "dish-ai-commit.providers.gemini",
21+
"PROVIDERS_GEMINI_APIKEY": "dish-ai-commit.providers.gemini.apiKey",
2022
"FEATURES": "dish-ai-commit.features",
2123
"FEATURES_CODEANALYSIS": "dish-ai-commit.features.codeAnalysis",
2224
"FEATURES_CODEANALYSIS_SIMPLIFYDIFF": "dish-ai-commit.features.codeAnalysis.simplifyDiff",

0 commit comments

Comments
 (0)