chore(svn-log-tool): 移除svm日志工具及示例文件,更新编码配置

- 删除DeepSeekLogProcessor日志处理类及相关依赖文件
- 删除Main主程序及辅助工具类LogEntry和ExcelAnalyzer
- 移除示例日志文件example_log.md
- 更新.idea/encodings.xml,添加src/main/java和src/main/resources的UTF-8编码配置
- 添加标准.gitignore配置,忽略Maven、IDEA、日志、打包文件及部分系统文件
This commit is contained in:
liumangmang
2026-02-04 16:17:09 +08:00
parent af02989ecb
commit 25248a0275
30 changed files with 58 additions and 1581 deletions

49
.gitignore vendored Normal file
View File

@@ -0,0 +1,49 @@
# Maven
target/
pom.xml.tag
pom.xml.releaseBackup
pom.xml.versionsBackup
pom.xml.next
release.properties
dependency-reduced-pom.xml
buildNumber.properties
.mvn/timing.properties
.mvn/wrapper/maven-wrapper.jar
# IntelliJ IDEA
.idea/
*.iws
*.iml
*.ipr
# Compiled class files
*.class
# Log files
*.log
# Package Files
*.jar
*.war
*.nar
*.ear
*.zip
*.tar.gz
*.rar
# Generated files
md/
*.xlsx
# OS generated files
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db
# Virtual machine crash logs
hs_err_pid*
replay_pid*

2
.idea/encodings.xml generated
View File

@@ -1,6 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="Encoding"> <component name="Encoding">
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/svn-log-tool/src/main/java" charset="UTF-8" /> <file url="file://$PROJECT_DIR$/svn-log-tool/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/svn-log-tool/src/main/resources" charset="UTF-8" /> <file url="file://$PROJECT_DIR$/svn-log-tool/src/main/resources" charset="UTF-8" />
</component> </component>

1
.idea/misc.xml generated
View File

@@ -6,6 +6,7 @@
<option name="originalFiles"> <option name="originalFiles">
<list> <list>
<option value="$PROJECT_DIR$/svn-log-tool/pom.xml" /> <option value="$PROJECT_DIR$/svn-log-tool/pom.xml" />
<option value="$PROJECT_DIR$/pom.xml" />
</list> </list>
</option> </option>
</component> </component>

8
.idea/modules.xml generated
View File

@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/日志.iml" filepath="$PROJECT_DIR$/.idea/日志.iml" />
</modules>
</component>
</project>

6
.idea/vcs.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

View File

@@ -1 +0,0 @@
{"mcpServers": {"context7": {"command": "npx", "args": ["-y", "@iflow-mcp/context7-mcp@1.0.0"]}, "excel-edit-server": {"command": "uvx", "args": ["--python", "3.12.q7", "iflow-mcp_excel-edit-server@latest", "--workspace-path", "."]}}}

View File

@@ -1,121 +0,0 @@
# SVN日志工作量统计工具DeepSeek版
## 功能说明
这个工具可以根据SVN日志的markdown文件调用DeepSeek API分析日志内容并生成符合格式要求的工作量统计Excel文件。
## 使用步骤
### 1. 准备SVN日志markdown文件
使用原有的SVN日志工具生成markdown文件
```bash
java -jar svn-log-tool-1.0.0-jar-with-dependencies.jar
```
按照提示输入SVN仓库地址、账号、密码等信息生成markdown格式的日志文件。
### 2. 运行DeepSeek日志处理工具
```bash
java -cp target/svn-log-tool-1.0.0-jar-with-dependencies.jar com.svnlog.DeepSeekLogProcessor
```
或者使用Maven运行
```bash
mvn exec:java -Dexec.mainClass="com.svnlog.DeepSeekLogProcessor"
```
### 3. 按照提示输入信息
程序会依次提示输入:
- **markdown日志文件路径**:可以直接回车使用当前目录下最新的`svn_log_*.md`文件
- **DeepSeek API Key**请提供有效的DeepSeek API Key也可以直接在代码中修改`API_KEY`常量)
- **输出Excel文件名**:可以直接回车使用默认文件名(格式:`YYYYMM工作量统计.xlsx`
### 4. 等待处理完成
程序会自动:
1. 读取markdown日志文件
2. 调用DeepSeek API分析日志内容
3. 根据分析结果生成Excel文件
生成的Excel文件格式与`202512工作量统计_刘靖.xlsx`保持一致。
## Excel文件格式说明
生成的Excel文件包含以下列
- 序号
- 所属班组
- 技术对接
- 开发人员
- 工作周期
- 开发项目名称
- 具体工作内容
- 空列4个
## DeepSeek API配置
`DeepSeekLogProcessor.java`中,可以修改以下配置:
```java
private static final String DEEPSEEK_API_URL = "https://api.deepseek.com/chat/completions";
private static final String API_KEY = "YOUR_DEEPSEEK_API_KEY"; // 请替换为实际的API Key
```
## 提示词说明
工具会向DeepSeek发送以下提示词要求AI以JSON格式返回工作量统计
```json
{
"team": "所属班组",
"contact": "技术对接人",
"developer": "开发人员",
"period": "工作周期 (例如: 2025年12月)",
"records": [
{
"sequence": 1,
"project": "项目名称",
"content": "具体工作内容"
}
]
}
```
## 注意事项
1. **API Key安全**请妥善保管您的DeepSeek API Key不要将其提交到代码仓库中
2. **网络连接**需要能够访问DeepSeek API服务器
3. **日志格式**markdown文件需要由SVN日志工具生成包含完整的日志信息
4. **成本控制**DeepSeek API可能产生费用请注意控制使用频率
## 故障排查
### 编译错误
```bash
mvn clean package -DskipTests
```
### 运行时找不到主类
确保使用正确的jar文件
```bash
java -cp target/svn-log-tool-1.0.0-jar-with-dependencies.jar com.svnlog.DeepSeekLogProcessor
```
### API调用失败
- 检查API Key是否正确
- 检查网络连接是否正常
- 检查DeepSeek API服务是否可用
## 依赖说明
项目使用以下主要依赖:
- SVNKit 1.10.11SVN操作
- Apache POI 5.2.5Excel文件读写
- OkHttp 4.12.0HTTP客户端
- Gson 2.10.1JSON处理
## 许可证
本工具仅供内部使用。

View File

@@ -1,164 +0,0 @@
# SVN 日志报告
## 查询条件
- **SVN地址**: `https://svn.example.com/project`
- **账号**: `testuser`
- **版本范围**: r1000 - r1050
- **生成时间**: 2025-01-30 10:00:00
## 统计信息
- **总记录数**: 5 条
### 按作者统计
| 作者 | 提交次数 |
|------|----------|
| `zhangsan` | 3 |
| `lisi` | 2 |
## 日志详情
### r1050
**作者**: `zhangsan`
**时间**: 2025-01-30 09:30:00
**版本**: r1050
**变更文件**:
```
/src/main/java/com/example/Service.java
/src/test/java/com/example/ServiceTest.java
```
**提交信息**:
```
修复用户登录时的空指针异常问题
1. 修复用户服务中的空指针检查
2. 添加单元测试验证修复
3. 更新相关文档
```
---
### r1049
**作者**: `zhangsan`
**时间**: 2025-01-29 16:45:00
**版本**: r1049
**变更文件**:
```
/src/main/java/com/example/Controller.java
```
**提交信息**:
```
优化API接口响应速度
# 性能优化
1. 添加数据库查询缓存
2. 优化SQL查询语句
3. 减少不必要的对象创建
# 测试验证
- 响应时间从500ms降低到200ms
- 通过所有单元测试
```
---
### r1048
**作者**: `lisi`
**时间**: 2025-01-29 14:20:00
**版本**: r1048
**变更文件**:
```
/src/main/java/com/example/Dao.java
/src/main/resources/mapper/UserMapper.xml
```
**提交信息**:
```
实现用户数据批量导入功能
# 核心功能
1. 支持Excel文件上传
2. 数据验证和错误处理
3. 批量插入数据库
# 配置变更
- 添加文件上传大小限制
- 配置批量插入批次大小
```
---
### r1047
**作者**: `zhangsan`
**时间**: 2025-01-28 11:00:00
**版本**: r1047
**变更文件**:
```
/src/main/java/com/example/Util.java
/src/main/resources/application.yml
```
**提交信息**:
```
添加日志记录功能
# 新增功能
1. 集成Log4j2日志框架
2. 配置日志输出格式
3. 添加关键操作日志记录
# 配置更新
- 设置日志级别为INFO
- 配置日志文件滚动策略
```
---
### r1046
**作者**: `lisi`
**时间**: 2025-01-27 15:30:00
**版本**: r1046
**变更文件**:
```
/src/main/java/com/example/Model.java
```
**提交信息**:
```
重构数据模型类
# 重构内容
1. 优化字段命名规范
2. 添加数据验证注解
3. 实现序列化接口
# 兼容性
- 保持向后兼容
- 更新相关测试用例
```
---

View File

@@ -1,109 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.svnlog</groupId>
<artifactId>svn-log-tool</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<name>SVN Log Tool</name>
<description>SVN日志查询工具支持版本范围过滤和用户名过滤可导出Markdown格式</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<!-- SVNKit for SVN operations -->
<dependency>
<groupId>org.tmatesoft.svnkit</groupId>
<artifactId>svnkit</artifactId>
<version>1.10.11</version>
</dependency>
<!-- Apache POI for Excel operations -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>5.2.5</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>5.2.5</version>
</dependency>
<!-- HTTP Client for API calls -->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.12.0</version>
</dependency>
<!-- JSON parsing -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.10.1</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.2.0</version>
<configuration>
<archive>
<manifest>
<mainClass>com.svnlog.Main</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.3.0</version>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<manifest>
<mainClass>com.svnlog.Main</mainClass>
</manifest>
</archive>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@@ -1,587 +0,0 @@
package com.svnlog;
import okhttp3.*;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.*;
import java.nio.file.Files;
import java.text.SimpleDateFormat;
import java.util.*;
/**
* 使用DeepSeek API处理SVN日志并生成工作量统计Excel
*/
public class DeepSeekLogProcessor {
private static final String DEEPSEEK_API_URL = "https://api.deepseek.com/chat/completions";
private static final String API_KEY = "sk-48c59012c93b43a08fecbaf3e74799e7"; // 用户需要替换为实际的API Key
private static final OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(60, java.util.concurrent.TimeUnit.SECONDS)
.readTimeout(300, java.util.concurrent.TimeUnit.SECONDS) // 5分钟读取超时
.writeTimeout(60, java.util.concurrent.TimeUnit.SECONDS)
.build();
public static void main(String[] args) {
try {
Scanner scanner = new Scanner(System.in);
System.out.println("===========================================");
System.out.println(" SVN日志工作量统计工具DeepSeek版");
System.out.println(" 支持多项目汇总分析");
System.out.println("===========================================");
System.out.println();
// 读取markdown日志文件目录
System.out.print("请输入markdown日志文件所在目录路径 (回车使用当前目录): ");
String dirPath = scanner.nextLine().trim();
File dir;
if (dirPath.isEmpty()) {
dir = new File(".");
} else {
dir = new File(dirPath);
}
if (!dir.exists() || !dir.isDirectory()) {
System.err.println("错误: 目录不存在或不是有效目录!");
return;
}
// 扫描目录中的所有 .md 文件
File[] mdFiles = dir.listFiles((d, name) -> name.endsWith(".md"));
if (mdFiles == null || mdFiles.length == 0) {
System.err.println("错误: 目录中未找到任何 .md 文件!");
return;
}
System.out.println("找到 " + mdFiles.length + " 个日志文件:");
for (File file : mdFiles) {
System.out.println(" - " + file.getName());
}
System.out.println();
// 输入工作周期
SimpleDateFormat periodSdf = new SimpleDateFormat("yyyy年MM月");
String defaultPeriod = periodSdf.format(new Date());
System.out.print("请输入工作周期 (例如: 2025年12月回车使用默认: " + defaultPeriod + "): ");
String period = scanner.nextLine().trim();
if (period.isEmpty()) {
period = defaultPeriod;
System.out.println("使用默认工作周期: " + period);
}
// 读取并合并所有markdown文件
String combinedContent = readAndCombineMarkdownFiles(mdFiles);
System.out.println("成功读取并合并 " + mdFiles.length + " 个日志文件,总长度: " + combinedContent.length() + " 字符");
// 提示API Key
System.out.print("请输入DeepSeek API Key (留空使用代码中预设的): ");
String inputApiKey = scanner.nextLine().trim();
String apiKey = inputApiKey.isEmpty() ? API_KEY : inputApiKey;
if (apiKey.equals("YOUR_DEEPSEEK_API_KEY")) {
System.err.println("错误: 请提供有效的DeepSeek API Key");
return;
}
// 询问输出文件名
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMM");
String defaultOutput = sdf.format(new Date()) + "工作量统计.xlsx";
System.out.print("请输入输出Excel文件名 (回车使用默认: " + defaultOutput + "): ");
String outputPath = scanner.nextLine().trim();
if (outputPath.isEmpty()) {
outputPath = defaultOutput;
}
System.out.println();
System.out.println("正在调用DeepSeek API分析日志...");
// 调用DeepSeek API处理日志
String prompt = buildPrompt(combinedContent, period);
String aiResponse = callDeepSeekAPI(apiKey, prompt);
if (aiResponse == null) {
System.err.println("DeepSeek API调用失败请检查网络连接和API Key。");
return;
}
if (aiResponse.isEmpty()) {
System.err.println("DeepSeek API返回空响应请重试或联系技术支持。");
return;
}
System.out.println("DeepSeek分析完成正在生成Excel...");
// 生成Excel
generateExcel(outputPath, aiResponse);
System.out.println();
System.out.println("Excel文件生成成功: " + outputPath);
System.out.println();
} catch (Exception e) {
System.err.println("发生错误: " + e.getMessage());
e.printStackTrace();
}
}
/**
* 读取文件内容
*/
private static String readFile(String path) throws IOException {
return new String(Files.readAllBytes(new File(path).toPath()), "UTF-8");
}
/**
* 读取并合并多个markdown文件的内容
*/
private static String readAndCombineMarkdownFiles(File[] mdFiles) throws IOException {
StringBuilder combinedContent = new StringBuilder();
for (File file : mdFiles) {
String projectName = extractProjectName(file.getName());
String content = readFile(file.getAbsolutePath());
combinedContent.append("\n\n");
combinedContent.append("=== 项目: ").append(projectName).append(" ===\n");
combinedContent.append(content);
}
return combinedContent.toString();
}
/**
* 从文件名中提取项目名称
* 例如: svn_log_PRS-7050场站智慧管控_20260130_093348.md -> PRS-7050场站智慧管控
*/
private static String extractProjectName(String fileName) {
// 去掉 svn_log_ 前缀
if (fileName.startsWith("svn_log_")) {
fileName = fileName.substring(8);
}
// 去掉 .md 后缀
if (fileName.endsWith(".md")) {
fileName = fileName.substring(0, fileName.length() - 3);
}
// 去掉时间戳部分 (格式: _YYYYMMDD_HHMMSS)
int lastUnderscore = fileName.lastIndexOf('_');
if (lastUnderscore > 0) {
// 检查是否是时间戳格式
String timestampPart = fileName.substring(lastUnderscore + 1);
if (timestampPart.matches("\\d{8}_\\d{6}")) {
fileName = fileName.substring(0, lastUnderscore);
}
}
return fileName;
}
/**
* 构建发送给DeepSeek的提示词
*/
private static String buildPrompt(String markdownContent, String period) {
return "你是一个专业的项目管理助手。请分析以下多个项目的SVN日志并生成工作量统计数据。\n\n" +
"日志内容包含多个项目,每个项目之间用 === 项目: xxx === 标识。\n" +
"工作周期: " + period + "\n\n" +
"SVN日志内容:\n" + markdownContent + "\n\n" +
"请按照以下JSON格式返回工作量统计数据:\n" +
"{\n" +
" \"team\": \"所属班组\",\n" +
" \"contact\": \"技术对接人\",\n" +
" \"developer\": \"开发人员\",\n" +
" \"period\": \"" + period + "\",\n" +
" \"records\": [\n" +
" {\n" +
" \"sequence\": 1,\n" +
" \"project\": \"项目1/项目2/项目3\",\n" +
" \"content\": \"# 项目1\\n1.工作内容1\\n2.工作内容2\\n\\n# 项目2\\n1.工作内容1\\n2.工作内容2\\n\\n# 项目3\\n1.工作内容1\\n2.工作内容2\"\n" +
" }\n" +
" ]\n" +
"}\n\n" +
"重要要求:\n" +
"1. 根据日志作者确定开发人员\n" +
"2. 将所有项目的工作内容合并到一条记录中\n" +
"3. 项目名称字段project使用 / 分隔多个项目,例如:\"PRS7050场站系统/PRS7950智能巡视现场问题/PRS7950电科院测试\"\n" +
"4. 具体工作内容字段content使用 # 作为项目分类标识,格式为:\"# 项目名称\\n1.工作内容\\n2.工作内容\\n\\n# 下一个项目\\n1.工作内容\"\n" +
"5. 不同项目之间用空行分隔\n" +
"6. 只返回JSON不要有其他文字\n" +
"7. 提取具体工作内容,要详细和有条理\n" +
"8. 项目名称要简洁明确,去掉多余的前缀和后缀";
}
/**
* 调用DeepSeek API流式输出
*/
private static String callDeepSeekAPI(String apiKey, String prompt) throws IOException {
JSONObject requestBody = new JSONObject();
requestBody.put("model", "deepseek-chat");
// 创建消息对象,包含 role 和 content 字段
JSONObject messageObj = new JSONObject();
messageObj.put("role", "user");
messageObj.put("content", prompt);
// 创建消息数组
com.google.gson.JsonArray messagesArray = new com.google.gson.JsonArray();
messagesArray.add(messageObj.jsonObject);
requestBody.put("messages", messagesArray);
requestBody.put("temperature", 0.7);
requestBody.put("max_tokens", 4000);
requestBody.put("stream", true); // 启用流式输出
Request request = new Request.Builder()
.url(DEEPSEEK_API_URL)
.addHeader("Authorization", "Bearer " + apiKey)
.addHeader("Content-Type", "application/json")
.post(RequestBody.create(requestBody.toString(), MediaType.parse("application/json")))
.build();
StringBuilder fullResponse = new StringBuilder();
int chunkCount = 0;
try (Response response = client.newCall(request).execute()) {
if (!response.isSuccessful()) {
System.err.println("API调用失败: " + response.code() + " " + response.message());
String errorResponse = response.body().string();
System.err.println("响应: " + errorResponse);
return null;
}
// 读取流式响应
try (BufferedReader reader = new BufferedReader(new InputStreamReader(response.body().byteStream()))) {
String line;
while ((line = reader.readLine()) != null) {
if (line.startsWith("data: ")) {
String data = line.substring(6);
if (data.equals("[DONE]")) {
break;
}
try {
JSONObject chunk = new JSONObject(data);
if (chunk.has("choices") && chunk.getJSONArray("choices").size() > 0) {
JSONObject choice = chunk.getJSONArray("choices").get(0);
if (choice.has("delta")) {
JSONObject delta = choice.getJSONObject("delta");
if (delta.has("content")) {
String content = delta.optString("content", "");
fullResponse.append(content);
chunkCount++;
// 实时打印处理进度
System.out.print(content);
System.out.flush();
}
}
}
} catch (Exception e) {
// 忽略解析错误,继续处理下一行
}
}
}
}
} catch (Exception e) {
System.err.println("API调用过程中发生异常: " + e.getMessage());
e.printStackTrace();
return null;
}
System.out.println(); // 换行
System.out.println("收到 " + chunkCount + " 个数据块");
if (fullResponse.length() == 0) {
System.err.println("警告: 未收到任何响应内容");
}
return fullResponse.toString();
}
/**
* 从响应中提取纯 JSON 内容
*/
private static String extractJson(String response) {
String trimmed = response.trim();
// 去除 ```json 标记
if (trimmed.startsWith("```json")) {
trimmed = trimmed.substring(7);
} else if (trimmed.startsWith("```")) {
trimmed = trimmed.substring(3);
}
// 去除 ``` 结束标记
if (trimmed.endsWith("```")) {
trimmed = trimmed.substring(0, trimmed.length() - 3);
}
return trimmed.trim();
}
/**
* 生成Excel文件
*/
private static void generateExcel(String outputPath, String jsonResponse) throws IOException {
// 提取纯 JSON 内容(去除 ```json 和 ``` 标记)
String cleanJson = extractJson(jsonResponse);
// 解析JSON响应
JSONObject data = new JSONObject(cleanJson);
// 创建工作簿
Workbook workbook = new XSSFWorkbook();
Sheet sheet = workbook.createSheet("工作表1");
// 创建样式
CellStyle headerStyle = createHeaderStyle(workbook);
CellStyle contentStyle = createContentStyle(workbook);
CellStyle workContentStyle = createWorkContentStyle(workbook);
// 创建表头7列与参考文件一致
Row headerRow = sheet.createRow(0);
headerRow.setHeightInPoints(14.25f); // 表头行高
String[] headers = {"序号", "所属班组", "技术对接", "开发人员", "工作周期", "开发项目名称", "具体工作内容"};
for (int i = 0; i < headers.length; i++) {
Cell cell = headerRow.createCell(i);
cell.setCellValue(headers[i]);
cell.setCellStyle(headerStyle);
}
// 设置固定列宽(与参考文件一致)
sheet.setColumnWidth(0, 2048); // 序号8.00字符
sheet.setColumnWidth(1, 3328); // 所属班组13.00字符
sheet.setColumnWidth(2, 4608); // 技术对接18.00字符
sheet.setColumnWidth(3, 3840); // 开发人员15.00字符
sheet.setColumnWidth(4, 5888); // 工作周期23.00字符
sheet.setColumnWidth(5, 14080); // 开发项目名称55.00字符
sheet.setColumnWidth(6, 43991); // 具体工作内容171.84字符
// 获取记录
String team = data.optString("team", "");
String contact = data.optString("contact", "");
String developer = data.optString("developer", "");
String period = data.optString("period", "");
if (data.has("records")) {
JSONArray recordsArray = data.getJSONArray("records");
int rowNum = 1;
for (int i = 0; i < recordsArray.size(); i++) {
JSONObject record = recordsArray.get(i);
Row row = sheet.createRow(rowNum++);
row.setHeightInPoints(16.50f); // 内容行高
// 序号
Cell cell0 = row.createCell(0);
cell0.setCellValue(record.optDouble("sequence", i + 1));
cell0.setCellStyle(contentStyle);
// 所属班组
Cell cell1 = row.createCell(1);
cell1.setCellValue(team);
cell1.setCellStyle(contentStyle);
// 技术对接
Cell cell2 = row.createCell(2);
cell2.setCellValue(contact);
cell2.setCellStyle(contentStyle);
// 开发人员
Cell cell3 = row.createCell(3);
cell3.setCellValue(developer);
cell3.setCellStyle(contentStyle);
// 工作周期
Cell cell4 = row.createCell(4);
cell4.setCellValue(period);
cell4.setCellStyle(contentStyle);
// 项目名称(多个项目用 / 分隔)
Cell cell5 = row.createCell(5);
cell5.setCellValue(record.optString("project", ""));
cell5.setCellStyle(contentStyle);
// 工作内容(支持换行,用 # 标识不同项目)
Cell cell6 = row.createCell(6);
cell6.setCellValue(record.optString("content", ""));
cell6.setCellStyle(workContentStyle); // 使用工作内容样式
}
}
// 写入文件
try (FileOutputStream fos = new FileOutputStream(outputPath)) {
workbook.write(fos);
}
workbook.close();
}
/**
* 创建表头样式
*/
private static CellStyle createHeaderStyle(Workbook workbook) {
CellStyle style = workbook.createCellStyle();
Font font = workbook.createFont();
font.setFontName("SimSun"); // 字体名称SimSun
font.setFontHeightInPoints((short) 11); // 字体大小11磅
font.setBold(false); // 不粗体
font.setColor(IndexedColors.BLACK.getIndex()); // 黑色
style.setFont(font);
style.setAlignment(HorizontalAlignment.GENERAL); // 水平对齐:常规
style.setVerticalAlignment(VerticalAlignment.CENTER); // 垂直对齐:居中
style.setFillPattern(FillPatternType.NO_FILL); // 无填充
style.setBorderTop(BorderStyle.THIN);
style.setBorderBottom(BorderStyle.THIN);
style.setBorderLeft(BorderStyle.THIN);
style.setBorderRight(BorderStyle.THIN);
style.setTopBorderColor(IndexedColors.BLACK.getIndex());
style.setBottomBorderColor(IndexedColors.BLACK.getIndex());
style.setLeftBorderColor(IndexedColors.BLACK.getIndex());
style.setRightBorderColor(IndexedColors.BLACK.getIndex());
style.setWrapText(false); // 不换行
return style;
}
/**
* 创建普通内容样式列A-F
*/
private static CellStyle createContentStyle(Workbook workbook) {
CellStyle style = workbook.createCellStyle();
Font font = workbook.createFont();
font.setFontName("宋体"); // 字体名称:宋体
font.setFontHeightInPoints((short) 11); // 字体大小11磅
font.setBold(false); // 不粗体
style.setFont(font);
style.setAlignment(HorizontalAlignment.GENERAL); // 水平对齐:常规
style.setVerticalAlignment(VerticalAlignment.CENTER); // 垂直对齐:居中
style.setFillPattern(FillPatternType.NO_FILL); // 无填充
style.setBorderTop(BorderStyle.THIN);
style.setBorderBottom(BorderStyle.NONE);
style.setBorderLeft(BorderStyle.NONE);
style.setBorderRight(BorderStyle.NONE);
style.setTopBorderColor(IndexedColors.BLACK.getIndex());
style.setWrapText(false); // 不换行
return style;
}
/**
* 创建工作内容样式列G
*/
private static CellStyle createWorkContentStyle(Workbook workbook) {
CellStyle style = workbook.createCellStyle();
Font font = workbook.createFont();
font.setFontName("NSimSun"); // 字体名称:新宋体
font.setFontHeightInPoints((short) 14); // 字体大小14磅
font.setBold(true); // 粗体
font.setColor(IndexedColors.BLACK.getIndex()); // 黑色
style.setFont(font);
style.setAlignment(HorizontalAlignment.LEFT); // 水平对齐:左对齐
style.setVerticalAlignment(VerticalAlignment.TOP); // 垂直对齐:顶部
style.setFillForegroundColor(IndexedColors.YELLOW.getIndex()); // 黄色背景
style.setFillPattern(FillPatternType.SOLID_FOREGROUND); // 实心填充
style.setBorderTop(BorderStyle.THIN);
style.setBorderBottom(BorderStyle.NONE);
style.setBorderLeft(BorderStyle.NONE);
style.setBorderRight(BorderStyle.NONE);
style.setTopBorderColor(IndexedColors.BLACK.getIndex());
style.setWrapText(true); // 自动换行
return style;
}
/**
* 简单的JSON工具类
*/
static class JSONObject {
private final com.google.gson.JsonObject jsonObject;
public JSONObject() {
this.jsonObject = new com.google.gson.JsonObject();
}
public JSONObject(String jsonString) {
com.google.gson.Gson gson = new com.google.gson.Gson();
this.jsonObject = gson.fromJson(jsonString, com.google.gson.JsonObject.class);
}
public JSONObject(String key, String value) {
this();
put(key, value);
}
public void put(String key, String value) {
jsonObject.addProperty(key, value);
}
public void put(String key, int value) {
jsonObject.addProperty(key, value);
}
public void put(String key, double value) {
jsonObject.addProperty(key, value);
}
public void put(String key, Object value) {
com.google.gson.Gson gson = new com.google.gson.Gson();
jsonObject.add(key, gson.toJsonTree(value));
}
public String optString(String key, String defaultValue) {
if (jsonObject.has(key) && !jsonObject.get(key).isJsonNull()) {
return jsonObject.get(key).getAsString();
}
return defaultValue;
}
public double optDouble(String key, double defaultValue) {
if (jsonObject.has(key) && !jsonObject.get(key).isJsonNull()) {
return jsonObject.get(key).getAsDouble();
}
return defaultValue;
}
public boolean has(String key) {
return jsonObject.has(key);
}
public JSONArray getJSONArray(String key) {
return new JSONArray(jsonObject.get(key).getAsJsonArray());
}
public JSONObject getJSONObject(String key) {
return new JSONObject(jsonObject.get(key).getAsJsonObject().toString());
}
@Override
public String toString() {
return jsonObject.toString();
}
}
/**
* 简单的JSONArray工具类
*/
static class JSONArray {
private final com.google.gson.JsonArray jsonArray;
public JSONArray(com.google.gson.JsonArray jsonArray) {
this.jsonArray = jsonArray;
}
public int size() {
return jsonArray.size();
}
public JSONObject get(int index) {
return new JSONObject(jsonArray.get(index).getAsJsonObject().toString());
}
@SuppressWarnings("unchecked")
public <T> java.util.List<JSONObject> toList() {
java.util.List<JSONObject> list = new ArrayList<>();
for (int i = 0; i < jsonArray.size(); i++) {
list.add(get(i));
}
return list;
}
}
}

View File

@@ -1,84 +0,0 @@
package com.svnlog;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.FileInputStream;
import java.io.IOException;
/**
* 临时工具类用于分析现有Excel文件格式
*/
public class ExcelAnalyzer {
public static void main(String[] args) {
String excelPath = "/home/liumangmang/opencode/日志/202512工作量统计_刘靖.xlsx";
try (FileInputStream fis = new FileInputStream(excelPath);
Workbook workbook = new XSSFWorkbook(fis)) {
System.out.println("工作表数量: " + workbook.getNumberOfSheets());
for (int i = 0; i < workbook.getNumberOfSheets(); i++) {
System.out.println("工作表 " + i + ": " + workbook.getSheetName(i));
}
Sheet sheet = workbook.getSheetAt(0);
System.out.println("\n工作表名称: " + sheet.getSheetName());
System.out.println("总行数: " + sheet.getPhysicalNumberOfRows());
System.out.println("最后一行索引: " + sheet.getLastRowNum());
// 读取前20行数据
System.out.println("\n前20行数据:");
for (int i = 0; i <= Math.min(19, sheet.getLastRowNum()); i++) {
Row row = sheet.getRow(i);
if (row != null) {
System.out.print("" + (i + 1) + "行: ");
for (Cell cell : row) {
String value = getCellValueAsString(cell);
System.out.print("[" + value + "] ");
}
System.out.println();
}
}
// 读取表头
Row headerRow = sheet.getRow(0);
if (headerRow != null) {
System.out.println("\n表头列数: " + headerRow.getLastCellNum());
System.out.print("表头: ");
for (Cell cell : headerRow) {
System.out.print("[" + getCellValueAsString(cell) + "] ");
}
System.out.println();
}
} catch (IOException e) {
System.err.println("读取Excel文件出错: " + e.getMessage());
e.printStackTrace();
}
}
private static String getCellValueAsString(Cell cell) {
if (cell == null) {
return "";
}
switch (cell.getCellType()) {
case STRING:
return cell.getStringCellValue().trim();
case NUMERIC:
if (DateUtil.isCellDateFormatted(cell)) {
return cell.getDateCellValue().toString();
} else {
return String.valueOf(cell.getNumericCellValue());
}
case BOOLEAN:
return String.valueOf(cell.getBooleanCellValue());
case FORMULA:
return cell.getCellFormula();
case BLANK:
return "";
default:
return "";
}
}
}

View File

@@ -1,71 +0,0 @@
package com.svnlog;
import java.util.Date;
public class LogEntry {
private long revision;
private String author;
private Date date;
private String message;
private String[] changedPaths;
public LogEntry() {
}
public LogEntry(long revision, String author, Date date, String message) {
this.revision = revision;
this.author = author;
this.date = date;
this.message = message;
}
public long getRevision() {
return revision;
}
public void setRevision(long revision) {
this.revision = revision;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public String[] getChangedPaths() {
return changedPaths;
}
public void setChangedPaths(String[] changedPaths) {
this.changedPaths = changedPaths;
}
@Override
public String toString() {
return "LogEntry{" +
"revision=" + revision +
", author='" + author + '\'' +
", date=" + date +
", message='" + message + '\'' +
'}';
}
}

View File

@@ -1,215 +0,0 @@
package com.svnlog;
import org.tmatesoft.svn.core.SVNException;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.*;
public class Main {
private static final Scanner scanner = new Scanner(System.in);
private static final SimpleDateFormat fileNameDateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss");
// 预设项目列表
private static final Project[] PRESET_PROJECTS = {
new Project("PRS-7050场站智慧管控", "https://10.6.220.216:48080/svn/houtai/001_后台软件/PRS-7050场站智慧管控/01_开发库/V1.00/src_java"),
new Project("PRS-7950在线巡视", "https://10.6.220.216:48080/svn/houtai/001_后台软件/PRS-7950在线巡视/01_开发库/V2.00/src_java"),
new Project("PRS-7950在线巡视电科院测试版", "https://10.6.220.216:48080/svn/houtai/001_后台软件/PRS-7950在线巡视/01_开发库/V1.00_2024/src_java")
};
public static void main(String[] args) {
System.out.println("===========================================");
System.out.println(" SVN 日志查询工具 v1.0");
System.out.println("===========================================");
System.out.println();
try {
// 创建 md 目录
File mdDir = new File("md");
if (!mdDir.exists()) {
boolean created = mdDir.mkdir();
if (created) {
System.out.println("已创建 md 目录用于存放日志文件");
}
}
System.out.println();
// 选择项目
Project selectedProject = selectProject();
String url = selectedProject.getUrl();
System.out.println("已选择项目: " + selectedProject.getName());
System.out.println("SVN地址: " + url);
System.out.println();
String username = readInput("请输入SVN账号: ");
String password = readPassword("请输入SVN密码: ");
System.out.println("正在连接SVN仓库...");
SVNLogFetcher fetcher = new SVNLogFetcher(url, username, password);
fetcher.testConnection();
System.out.println("连接成功!");
System.out.println();
long latestRevision = fetcher.getLatestRevision();
System.out.println("最新版本号: " + latestRevision);
System.out.println();
long startRevision = readLongInput("请输入开始版本号 (回车使用最新版本): ", latestRevision);
long endRevision = readLongInput("请输入结束版本号 (回车使用最新版本): ", latestRevision);
String filterUser = readInput("请输入过滤用户名 (包含匹配,回车跳过过滤): ");
System.out.println();
System.out.println("正在获取日志...");
List<LogEntry> logs = fetcher.fetchLogs(startRevision, endRevision, filterUser);
if (logs.isEmpty()) {
System.out.println("没有找到符合条件的日志记录。");
return;
}
System.out.println("获取到 " + logs.size() + " 条日志记录。");
System.out.println();
// 生成Markdown文件保存到 md 目录)
String fileName = "md/svn_log_" + selectedProject.getName() + "_" + fileNameDateFormat.format(new Date()) + ".md";
generateMarkdown(fileName, url, username, startRevision, endRevision, filterUser, logs, fetcher);
System.out.println();
System.out.println("日志已成功导出到: " + fileName);
System.out.println();
} catch (SVNException e) {
System.err.println("SVN错误: " + e.getMessage());
} catch (Exception e) {
System.err.println("发生错误: " + e.getMessage());
e.printStackTrace();
}
}
/**
* 让用户选择项目
*/
private static Project selectProject() {
System.out.println("请选择SVN项目:");
for (int i = 0; i < PRESET_PROJECTS.length; i++) {
System.out.println(" " + (i + 1) + ". " + PRESET_PROJECTS[i].getName());
}
System.out.println(" 0. 自定义SVN地址");
System.out.println();
while (true) {
System.out.print("请输入项目编号 (1-" + PRESET_PROJECTS.length + ", 0为自定义): ");
String input = scanner.nextLine().trim();
try {
int choice = Integer.parseInt(input);
if (choice == 0) {
String customUrl = readInput("请输入SVN仓库地址: ");
return new Project("自定义项目", customUrl);
} else if (choice >= 1 && choice <= PRESET_PROJECTS.length) {
return PRESET_PROJECTS[choice - 1];
} else {
System.out.println("输入无效,请重新选择!");
}
} catch (NumberFormatException e) {
System.out.println("输入无效,请输入数字!");
}
}
}
private static String readInput(String prompt) {
System.out.print(prompt);
return scanner.nextLine().trim();
}
private static String readPassword(String prompt) {
if (System.console() != null) {
char[] password = System.console().readPassword("%s", prompt);
return new String(password);
} else {
System.out.print(prompt);
return scanner.nextLine();
}
}
private static long readLongInput(String prompt, long defaultValue) {
System.out.print(prompt);
String input = scanner.nextLine().trim();
if (input.isEmpty()) {
return defaultValue;
}
try {
return Long.parseLong(input);
} catch (NumberFormatException e) {
System.out.println("输入无效,使用默认值: " + defaultValue);
return defaultValue;
}
}
private static void generateMarkdown(String fileName, String url, String username,
long startRevision, long endRevision, String filterUser,
List<LogEntry> logs, SVNLogFetcher fetcher) throws IOException {
StringBuilder markdown = new StringBuilder();
// 标题
markdown.append("# SVN 日志报告\n\n");
// 查询条件(简化版)
markdown.append("## 查询条件\n\n");
markdown.append("- **SVN地址**: `").append(url).append("`\n");
markdown.append("- **版本范围**: r").append(startRevision).append(" - r").append(endRevision).append("\n");
if (filterUser != null && !filterUser.isEmpty()) {
markdown.append("- **过滤用户**: `").append(filterUser).append("`\n");
}
markdown.append("\n");
// 日志详情(简化版,只包含作者、时间、版本、提交信息)
markdown.append("## 日志详情\n\n");
for (LogEntry entry : logs) {
markdown.append("### r").append(entry.getRevision()).append("\n\n");
markdown.append("**作者**: `").append(entry.getAuthor()).append("` \n");
markdown.append("**时间**: ").append(fetcher.formatDate(entry.getDate())).append(" \n");
markdown.append("**版本**: r").append(entry.getRevision()).append("\n\n");
String message = entry.getMessage();
if (message != null && !message.isEmpty()) {
markdown.append("**提交信息**:\n\n");
markdown.append("```\n").append(message).append("\n```\n\n");
} else {
markdown.append("**提交信息**: (无)\n\n");
}
markdown.append("---\n\n");
}
// 写入文件
try (BufferedWriter writer = new BufferedWriter(new FileWriter(fileName))) {
writer.write(markdown.toString());
}
}
/**
* 项目信息类
*/
private static class Project {
private String name;
private String url;
public Project(String name, String url) {
this.name = name;
this.url = url;
}
public String getName() {
return name;
}
public String getUrl() {
return url;
}
}
}

View File

@@ -1,98 +0,0 @@
package com.svnlog;
import org.tmatesoft.svn.core.*;
import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager;
import org.tmatesoft.svn.core.internal.io.svn.SVNRepositoryFactoryImpl;
import org.tmatesoft.svn.core.io.SVNRepository;
import org.tmatesoft.svn.core.io.SVNRepositoryFactory;
import org.tmatesoft.svn.core.wc.SVNWCUtil;
import java.text.SimpleDateFormat;
import java.util.*;
public class SVNLogFetcher {
private String url;
private String username;
private String password;
private SVNRepository repository;
private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public SVNLogFetcher(String url, String username, String password) throws SVNException {
this.url = url;
this.username = username;
this.password = password;
SVNRepositoryFactoryImpl.setup();
this.repository = SVNRepositoryFactory.create(SVNURL.parseURIEncoded(url));
ISVNAuthenticationManager authManager = SVNWCUtil.createDefaultAuthenticationManager(username, password.toCharArray());
repository.setAuthenticationManager(authManager);
}
public List<LogEntry> fetchLogs(long startRevision, long endRevision) throws SVNException {
return fetchLogs(startRevision, endRevision, null);
}
public List<LogEntry> fetchLogs(long startRevision, long endRevision, String filterUser) throws SVNException {
List<LogEntry> entries = new ArrayList<>();
if (startRevision < 0) {
startRevision = repository.getLatestRevision();
}
if (endRevision < 0) {
endRevision = repository.getLatestRevision();
}
if (startRevision > endRevision) {
long temp = startRevision;
startRevision = endRevision;
endRevision = temp;
}
Collection<SVNLogEntry> logEntries = repository.log(new String[]{""}, null, startRevision, endRevision, true, true);
for (SVNLogEntry logEntry : logEntries) {
String author = logEntry.getAuthor();
// 如果设置了用户名过滤器,则跳过不匹配的记录(包含匹配,不区分大小写)
if (filterUser != null && !filterUser.isEmpty() && (author == null || !author.toLowerCase().contains(filterUser.toLowerCase()))) {
continue;
}
LogEntry entry = new LogEntry();
entry.setRevision(logEntry.getRevision());
entry.setAuthor(author != null ? author : "(无作者)");
entry.setDate(logEntry.getDate());
entry.setMessage(logEntry.getMessage() != null ? logEntry.getMessage().trim() : "");
// 获取变更的文件路径
if (logEntry.getChangedPaths() != null) {
List<String> paths = new ArrayList<>();
for (Map.Entry<String, SVNLogEntryPath> pathEntry : logEntry.getChangedPaths().entrySet()) {
paths.add(pathEntry.getKey());
}
entry.setChangedPaths(paths.toArray(new String[0]));
}
entries.add(entry);
}
// 按版本号降序排序
entries.sort((e1, e2) -> Long.compare(e2.getRevision(), e1.getRevision()));
return entries;
}
public long getLatestRevision() throws SVNException {
return repository.getLatestRevision();
}
public String formatDate(Date date) {
return dateFormat.format(date);
}
public void testConnection() throws SVNException {
repository.testConnection();
}
}

View File

@@ -1,3 +0,0 @@
artifactId=svn-log-tool
groupId=com.svnlog
version=1.0.0

View File

@@ -1,9 +0,0 @@
com/svnlog/DeepSeekLogProcessor.class
com/svnlog/ExcelAnalyzer.class
com/svnlog/DeepSeekLogProcessor$JSONArray.class
com/svnlog/SVNLogFetcher.class
com/svnlog/Main$Project.class
com/svnlog/DeepSeekLogProcessor$JSONObject.class
com/svnlog/Main.class
com/svnlog/LogEntry.class
com/svnlog/ExcelAnalyzer$1.class

View File

@@ -1,5 +0,0 @@
/home/liumangmang/opencode/日志/svn-log-tool/src/main/java/com/svnlog/LogEntry.java
/home/liumangmang/opencode/日志/svn-log-tool/src/main/java/com/svnlog/Main.java
/home/liumangmang/opencode/日志/svn-log-tool/src/main/java/com/svnlog/ExcelAnalyzer.java
/home/liumangmang/opencode/日志/svn-log-tool/src/main/java/com/svnlog/DeepSeekLogProcessor.java
/home/liumangmang/opencode/日志/svn-log-tool/src/main/java/com/svnlog/SVNLogFetcher.java

Binary file not shown.

View File

@@ -1,106 +0,0 @@
#!/bin/bash
# 测试多文件处理功能
# 注意:此脚本需要有效的 DeepSeek API Key 才能完成测试
cd /home/liumangmang/opencode/日志
echo "==========================================="
echo " 测试 DeepSeek 日志分析工具"
echo "==========================================="
echo ""
# 检查是否有日志文件
md_files=$(find . -maxdepth 1 -name "svn_log_*.md" | wc -l)
if [ "$md_files" -eq 0 ]; then
echo "警告: 当前目录没有找到 svn_log_*.md 文件"
echo "请先使用 Main.java 生成日志文件"
echo ""
echo "或者手动创建测试文件..."
# 创建测试文件
cat > test_project1.md << 'EOF'
# SVN 日志报告
## 查询条件
- **SVN地址**: `https://test.svn.com/project1`
- **账号**: `testuser`
- **版本范围**: r1 - r10
- **生成时间**: 2026-01-30
## 统计信息
- **总记录数**: 2 条
## 日志详情
### r10
**作者**: `liujing@SZNARI`
**时间**: 2026-01-27 10:00:00
**版本**: r10
**提交信息**:
feat: 添加用户登录功能
### r9
**作者**: `liujing@SZNARI`
**时间**: 2026-01-26 15:00:00
**版本**: r9
**提交信息**:
fix: 修复登录页面样式问题
EOF
cat > test_project2.md << 'EOF'
# SVN 日志报告
## 查询条件
- **SVN地址**: `https://test.svn.com/project2`
- **账号**: `testuser`
- **版本范围**: r1 - r10
- **生成时间**: 2026-01-30
## 统计信息
- **总记录数**: 1 条
## 日志详情
### r8
**作者**: `liujing@SZNARI`
**时间**: 2026-01-25 14:00:00
**版本**: r8
**提交信息**:
refactor: 优化数据库查询性能
EOF
echo "已创建测试文件: test_project1.md, test_project2.md"
fi
echo ""
echo "当前目录下的日志文件:"
ls -lh svn_log_*.md test_*.md 2>/dev/null || echo " (无文件)"
echo ""
echo "==========================================="
echo " 程序使用说明"
echo "==========================================="
echo ""
echo "要运行 DeepSeek 日志分析工具,请执行:"
echo ""
echo " cd /home/liumangmang/opencode/日志"
echo " java -jar svn-log-tool/target/svn-log-tool-1.0.0-jar-with-dependencies.jar"
echo ""
echo "然后按提示输入:"
echo " 1. 日志文件所在目录路径(回车使用当前目录)"
echo " 2. 工作周期例如2025年12月"
echo " 3. DeepSeek API Key"
echo " 4. 输出 Excel 文件名(回车使用默认)"
echo ""
echo "程序将自动读取目录中的所有 .md 文件,合并后发送给 DeepSeek API 分析,"
echo "并生成包含多项目工作内容的 Excel 文件。"
echo ""
echo "Excel 输出格式(与参考文件一致):"
echo " - 7列序号、所属班组、技术对接、开发人员、工作周期、开发项目名称、具体工作内容"
echo " - 项目名称用 / 分隔PRS7050/PRS7950"
echo " - 工作内容用 # 标识不同项目"
echo ""