dsh-plugin-daily-report 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +100 -97
- package/client.js +1851 -1716
- package/cordis.patch.yml +3 -3
- package/icon.svg +14 -14
- package/index.js +789 -770
- package/lib/ai.js +493 -493
- package/lib/collect.js +1047 -1037
- package/lib/formats.js +190 -0
- package/lib/report.js +377 -376
- package/lib/xlsx.js +416 -416
- package/locale/en.json +6 -6
- package/locale/zh.json +6 -6
- package/package.json +50 -50
package/lib/formats.js
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plain-text and HTML renderings of the same report result used by the XLSX
|
|
3
|
+
* writer. Keeping them here means every export format follows the same AI
|
|
4
|
+
* summaries, manual items and source data.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
function manualMap(result) {
|
|
8
|
+
const map = new Map();
|
|
9
|
+
for (const item of result.manualItems || []) {
|
|
10
|
+
if (!item || typeof item.date !== "string" || typeof item.text !== "string") continue;
|
|
11
|
+
if (!map.has(item.date)) map.set(item.date, []);
|
|
12
|
+
map.get(item.date).push(item.text);
|
|
13
|
+
}
|
|
14
|
+
return map;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function aiDayMap(result) {
|
|
18
|
+
const map = new Map();
|
|
19
|
+
for (const day of result.ai && result.ai.ok && Array.isArray(result.ai.days) ? result.ai.days : []) {
|
|
20
|
+
map.set(day.date, day);
|
|
21
|
+
}
|
|
22
|
+
return map;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function cellText(value) {
|
|
26
|
+
return value === null || value === undefined ? "" : String(value);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function markdownCell(value) {
|
|
30
|
+
return cellText(value).replace(/\|/g, "\\|").replace(/\r?\n/g, "<br>");
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function csvCell(value) {
|
|
34
|
+
return '"' + cellText(value).replace(/"/g, '""') + '"';
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function htmlEscape(value) {
|
|
38
|
+
return cellText(value)
|
|
39
|
+
.replace(/&/g, "&")
|
|
40
|
+
.replace(/</g, "<")
|
|
41
|
+
.replace(/>/g, ">")
|
|
42
|
+
.replace(/"/g, """);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function summaryRows(result) {
|
|
46
|
+
const manual = manualMap(result);
|
|
47
|
+
const ai = aiDayMap(result);
|
|
48
|
+
return result.days.map((day) => {
|
|
49
|
+
const dayAi = ai.get(day.date);
|
|
50
|
+
return [
|
|
51
|
+
day.date,
|
|
52
|
+
day.weekday,
|
|
53
|
+
day.dayType,
|
|
54
|
+
day.commitCount,
|
|
55
|
+
day.docCount,
|
|
56
|
+
day.sources || "—",
|
|
57
|
+
day.summary,
|
|
58
|
+
(manual.get(day.date) || []).join(";"),
|
|
59
|
+
dayAi ? dayAi.summary || "" : "",
|
|
60
|
+
];
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function buildMarkdownReport(result) {
|
|
65
|
+
const ai = result.ai && result.ai.ok ? result.ai : null;
|
|
66
|
+
const rows = summaryRows(result);
|
|
67
|
+
const lines = [
|
|
68
|
+
"# 日报汇总 " + result.range.start + " ~ " + result.range.end,
|
|
69
|
+
"",
|
|
70
|
+
"- Git 提交:" + result.stats.commits + " 条",
|
|
71
|
+
"- 文档:" + result.stats.docs + " 份",
|
|
72
|
+
"- 有记录天数:" + result.stats.activeDays + " 天",
|
|
73
|
+
"- 数据源:" + result.stats.okSources + "/" + result.stats.sources + " 个正常",
|
|
74
|
+
"",
|
|
75
|
+
];
|
|
76
|
+
if (ai) {
|
|
77
|
+
lines.push("## 总体 AI 总结", "", ai.overall || "—", "");
|
|
78
|
+
} else if (result.ai && result.ai.error) {
|
|
79
|
+
lines.push("> AI 分析未完成:" + result.ai.error, "");
|
|
80
|
+
}
|
|
81
|
+
lines.push(
|
|
82
|
+
"## 每日汇总",
|
|
83
|
+
"",
|
|
84
|
+
"| 日期 | 星期 | 类型 | Git提交 | 文档 | 涉及仓库/文档 | 当日摘要 | 手动补充 | AI 分析 |",
|
|
85
|
+
"|---|---|---|---:|---:|---|---|---|---|",
|
|
86
|
+
);
|
|
87
|
+
for (const [date, weekday, dayType, commitCount, docCount, sources, summary, manual, aiSummary] of rows) {
|
|
88
|
+
lines.push(
|
|
89
|
+
"| " +
|
|
90
|
+
[date, weekday, dayType, commitCount, docCount, sources, summary, manual, aiSummary]
|
|
91
|
+
.map(markdownCell)
|
|
92
|
+
.join(" | ") +
|
|
93
|
+
" |",
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
if (result.commits.length > 0) {
|
|
97
|
+
lines.push("", "## Git 明细", "", "| 日期 | 仓库 | 分支 | 提交 | 作者 | 说明 |", "|---|---|---|---|---|---|");
|
|
98
|
+
for (const commit of result.commits) {
|
|
99
|
+
lines.push(
|
|
100
|
+
"| " +
|
|
101
|
+
[commit.date, commit.repo, commit.branch, commit.shortHash, commit.author, commit.subject]
|
|
102
|
+
.map(markdownCell)
|
|
103
|
+
.join(" | ") +
|
|
104
|
+
" |",
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
if (result.docs.length > 0) {
|
|
109
|
+
lines.push("", "## 文档明细", "", "| 日期 | 来源 | 文件 | 标题 | 摘要 |", "|---|---|---|---|---|");
|
|
110
|
+
for (const doc of result.docs) {
|
|
111
|
+
lines.push("| " + [doc.date, doc.source, doc.file, doc.title, doc.excerpt].map(markdownCell).join(" | ") + " |");
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return lines.join("\n") + "\n";
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export function buildCsvReport(result) {
|
|
118
|
+
const header = ["日期", "星期", "日类型", "Git提交", "文档", "涉及仓库/文档", "当日摘要", "手动补充", "AI 分析"];
|
|
119
|
+
const rows = summaryRows(result);
|
|
120
|
+
return "\ufeff" + [header, ...rows].map((row) => row.map(csvCell).join(",")).join("\r\n") + "\r\n";
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export function buildHtmlReport(result) {
|
|
124
|
+
const ai = result.ai && result.ai.ok ? result.ai : null;
|
|
125
|
+
const rows = summaryRows(result);
|
|
126
|
+
const tableRows = rows
|
|
127
|
+
.map(
|
|
128
|
+
(row) =>
|
|
129
|
+
"<tr>" +
|
|
130
|
+
row
|
|
131
|
+
.map((value, index) => {
|
|
132
|
+
const cls = index >= 3 && index <= 4 ? ' class="num"' : "";
|
|
133
|
+
return "<td" + cls + ">" + htmlEscape(value) + "</td>";
|
|
134
|
+
})
|
|
135
|
+
.join("") +
|
|
136
|
+
"</tr>",
|
|
137
|
+
)
|
|
138
|
+
.join("\n");
|
|
139
|
+
const gitRows = result.commits
|
|
140
|
+
.map(
|
|
141
|
+
(commit) =>
|
|
142
|
+
"<tr>" +
|
|
143
|
+
[commit.date, commit.repo, commit.branch, commit.shortHash, commit.author, commit.subject, commit.fileCount]
|
|
144
|
+
.map((value) => "<td>" + htmlEscape(value) + "</td>")
|
|
145
|
+
.join("") +
|
|
146
|
+
"</tr>",
|
|
147
|
+
)
|
|
148
|
+
.join("\n");
|
|
149
|
+
return `<!doctype html>
|
|
150
|
+
<html lang="zh-CN">
|
|
151
|
+
<head>
|
|
152
|
+
<meta charset="utf-8">
|
|
153
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
154
|
+
<title>日报汇总 ${htmlEscape(result.range.start)} ~ ${htmlEscape(result.range.end)}</title>
|
|
155
|
+
<style>
|
|
156
|
+
:root { color-scheme: light dark; }
|
|
157
|
+
body { margin: 0; padding: 32px; font-family: "Microsoft YaHei", "Segoe UI", sans-serif; background: #f5f7fa; color: #1f2933; }
|
|
158
|
+
main { max-width: 1400px; margin: 0 auto; background: #fff; border-radius: 12px; padding: 28px 32px; box-shadow: 0 2px 16px rgba(0,0,0,.08); }
|
|
159
|
+
h1 { margin: 0 0 8px; font-size: 26px; }
|
|
160
|
+
.meta { color: #617080; font-size: 13px; margin-bottom: 18px; }
|
|
161
|
+
.stats { display: flex; flex-wrap: wrap; gap: 10px; margin: 16px 0 22px; }
|
|
162
|
+
.stat { border: 1px solid #d9e1ea; border-radius: 8px; padding: 8px 12px; min-width: 110px; }
|
|
163
|
+
.stat b { display: block; font-size: 20px; }
|
|
164
|
+
.stat span { color: #617080; font-size: 12px; }
|
|
165
|
+
h2 { margin: 26px 0 10px; font-size: 18px; }
|
|
166
|
+
p { line-height: 1.75; margin: 0 0 12px; white-space: pre-wrap; }
|
|
167
|
+
table { width: 100%; border-collapse: collapse; font-size: 12px; }
|
|
168
|
+
th, td { border: 1px solid #d9e1ea; padding: 6px 8px; text-align: left; vertical-align: top; }
|
|
169
|
+
th { background: #1f4e79; color: #fff; font-weight: 600; }
|
|
170
|
+
tr:nth-child(even) td { background: #f7f9fc; }
|
|
171
|
+
td.num { text-align: center; white-space: nowrap; }
|
|
172
|
+
.note { color: #617080; font-size: 12px; }
|
|
173
|
+
@media print { body { padding: 0; background: #fff; } main { box-shadow: none; border-radius: 0; } }
|
|
174
|
+
</style>
|
|
175
|
+
</head>
|
|
176
|
+
<body>
|
|
177
|
+
<main>
|
|
178
|
+
<h1>日报汇总 ${htmlEscape(result.range.start)} ~ ${htmlEscape(result.range.end)}</h1>
|
|
179
|
+
<div class="meta">Git 提交 ${result.stats.commits} 条 · 文档 ${result.stats.docs} 份 · 有记录 ${result.stats.activeDays} 天 · 数据源 ${result.stats.okSources}/${result.stats.sources} 个正常</div>
|
|
180
|
+
${ai ? "<h2>总体 AI 总结</h2><p>" + htmlEscape(ai.overall || "—") + "</p>" : result.ai && result.ai.error ? '<p class="note">AI 分析未完成:' + htmlEscape(result.ai.error) + "</p>" : ""}
|
|
181
|
+
<h2>每日汇总</h2>
|
|
182
|
+
<table>
|
|
183
|
+
<thead><tr><th>日期</th><th>星期</th><th>类型</th><th>Git提交</th><th>文档</th><th>涉及仓库/文档</th><th>当日摘要</th><th>手动补充</th><th>AI 分析</th></tr></thead>
|
|
184
|
+
<tbody>${tableRows}</tbody>
|
|
185
|
+
</table>
|
|
186
|
+
${gitRows ? "<h2>Git 明细</h2><table><thead><tr><th>日期</th><th>仓库</th><th>分支</th><th>提交</th><th>作者</th><th>说明</th><th>变更文件数</th></tr></thead><tbody>" + gitRows + "</tbody></table>" : ""}
|
|
187
|
+
</main>
|
|
188
|
+
</body>
|
|
189
|
+
</html>`;
|
|
190
|
+
}
|