采购云 QA 周报自动生成技能
技能简介
本技能封装了采购云产品部 QA 周报的完整生成流程,包括:
- 通过 IMAP 协议读取指定邮件及下载 Excel 附件
- 解析多张 Excel 工作表数据
- 自动与上周数据对比
- 生成结构化 HTML 数据分析报告(含每节分析说明)
- 将 HTML 报告截图导出为 PNG 图片
配置前提
邮箱连接信息存储在工作区根目录的 mail_config.json,格式如下:
{
"host": "mail.yonyou.com",
"port": 993,
"email": "chengmingm@yonyou.com",
"password": "授权码"
}
注意:需在公司内网环境下运行,服务器
mail.yonyou.com为内网地址(10.16.16.x)。
工作流程
Phase 1:读取目标邮件
- 读取
mail_config.json获取连接信息 - 通过 IMAP SSL 连接邮件服务器
- 在所有文件夹中搜索主题含"采购云QA报告"、日期匹配的邮件
- 下载全部 Excel 附件到工作区(命名格式保持原始文件名)
- 同时搜索上一周的 QA 报告邮件,下载附件并加
lastweek_前缀
使用脚本:scripts/fetch_email.py
Phase 2:解析 Excel 数据
解析以下工作表(文件名与 Sheet 名可能不同,按实际内容匹配):
| 文件/Sheet 名 | 内容说明 | |---|---| | 产品+组织 | 各三级目标有效特性数、开发/测试完成率 | | 评审统计-按组织 | 各评审项计划数、关闭率、及时率 | | 评审计划明细表 | 全部未关闭评审明细 | | 报表1 | 本周计划评审明细(含未及时标识) | | 迭代进度 | 各迭代各组织故事总数、提测率、完成率 | | 缺陷处理中-按部门 | 515未关闭缺陷,按版本统计开发/测试未完成数 | | 承诺解决-按二级组织 | 承诺需求数量,关注公有云处理中 | | 09-预警明细 | 承诺需求预警明细 | | 采购云-提出待分析 | 协作需求(采购云作为提出方,协作方未分析) | | 采购云-协作待分析 | 协作需求(采购云作为协作方,需分析) | | 需求处理情况 | 原始需求的处理情况明细 | | 支持问题按客户-一级组织 | 支持问题按客户/一级组织统计,含提出问题数量 | | 含类型-按二级 | 支持问题首次响应耗时分析,按二级分类统计(整合进第7节7.2子节) | | 处理中-按一二三级业务组织 | 支持问题处理中结存数量(本周从邮件附件获取),用于第7.3节趋势折线图 | | 处理中结存历史(pending_history.json) | 所有历史周的处理中结存数据,持久化存储。脚本自动追加当前周数据,图表展示全部历史周期,表格仅展示近三周。历史数据缺失时可从往期报告HTML中提取补全 |
使用脚本:scripts/parse_excel.py(输出 all_data.json,包含本周和上周所有Excel数据的 current/previous 双份数据)
Phase 3:读取邮件正文
从邮件正文中提取:
- 整体趋势分析链接(URL)
- 按客户提出统计的前10名客户信息
- 其他补充文字内容
使用脚本:scripts/fetch_email.py(同步输出 qa_email_content.json)
Phase 4:生成 HTML 报告
🔒 样式锁定:必须以 采购云QA报告-20260426.html 为唯一样式基准。
📦 额外依赖:第7.3节折线图使用 Chart.js,需在 <head> 中引入 CDN:
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
报告结构:12 个专题 + 7.3 子节(已去掉旧版第13/14/15节:支持问题按客户独立节、首次响应耗时独立节、上线后工单分析节;相关内容已整合入第7节子节)
所有生成的报告在 HTML 结构、CSS 样式、布局格式、组件类名上,必须与基准文件 完全一致。具体规范详见 references/report_template_guide.md(该文档已从 0426 基准文件中完整提取)。
参考 references/report_template_guide.md 中的报告结构规范,生成完整 HTML 文件。
必须包含的12个专题,每个专题结构为:
标题(section-title)
↓
分析说明框(info-box,放置在标题正下方、表格之前)
↓
数据表格(部分专题不输出表格,见各专题说明)
↓
结论框(warn-box / ok-box)
各专题核心要求:
- 产品+组织 — 不输出数据表格,只输出分析文字,结构如下:
- 总量变化:本周有效特性数总数较上周变化(增/减/持平),整体开发完成率、测试通过率、特性发布率及较上周变化
- 三级目标特性变化:列表格式,减少项红色数字标注,新增项蓝色 NEW 标签,按变动值排序
- 有效特性数为0的三级目标:⚠️ 红色警告列出所有零特性目标
- 底部总结:✅ 绿色成功框总结
- 标题标签使用
tag info"总览"
- 评审统计-按组织 — 含两个对比子模块:
- 各评审项计划数对比表:评审项 / 上周评审计划数 / 本周评审计划数 / 增加数(↑+X / ↓X / →0)
- 环比分析框:蓝色
info-box,评审计划总数较上周变化 + 评审关闭率较上周变化(含百分点) - 完整明细表:表格新增"较上周"列,每行显示评审计划数增减;关闭率/及时率带颜色标记
- 评审计划明细表 — 保留全部未关闭评审,超期行标红
- 报表1 — 保留全部明细,未及时评审行标红
- 迭代进度 — 按迭代分组展示(跳过已上线的迭代,如 20260615),每组包含提测率、测试完成率等关键指标:
- 分析规则由用户在使用技能时提供,每次执行时根据用户给出的规则进行数据分析
- 数据表格和结构保持不变,仅分析逻辑/关注点/判断标准由用户提供
- 每个迭代块包含:
iter-head标题 + 分析说明 + 末级组织达标/未达标表格 + 结论框
- 缺陷处理中-按部门 — 不输出数据表格,只输出分析文字,分析内容包含以下两部分:
- 分析规则由用户在使用技能时提供,每次执行时根据用户给出的规则进行数据分析
- 两部分分析内容的具体关注点、判断标准和表达方式均以用户提供的规则为准
- 支持问题整体 — 该节包含三个子节,全部在同一个
<div class="section">容器内:- 7.1 支持问题整体趋势:分析本周新增/关闭/未关闭支持问题数量及与上周对比,保留按客户提出前10名表格
- 7.2 支持问题首次响应耗时分析:来源于
含类型-按二级.xlsx,不输出表格,纯文字,只列出平均首次响应时长 >10小时 的部门与问题类型(含问题数量和平均时长),按时长从高到低排序 - 7.3 支持问题处理中结存情况:来源于邮件附件
处理中-按一二三级业务组织.xlsx+pending_history.json历史数据,输出:- 采购云处理中支持问题总数,各部门处理中支持问题数,按从多到少排列,标注与上周对比变化量
- Chart.js 折线图:展示全部历史周期(如 0614 / 0621 / 0628 / 0705 / 本周0712)各部门结存趋势,X轴为日期,Y轴为结存数量,每条部门线一种颜色,不含合计线,鼠标悬停显示数值,底部有图例
- 明细表格:仅展示近三周数据(如 0628 / 0705 / 本周),本周列高亮蓝色,含"较上周变动"列
- 承诺解决-按二级组织 — 关注公有云处理中的待规划和实现中数量
- 09-预警明细 — 保留完整明细
- 采购云-提出待分析 — 标签
tag warn"待处理",展示提出待分析数量和较上周对比。若数据为空(含仅URL模板的占位行)则显示"暂无",表格列精简(9列:协作方组织/产品/级次/分析人/提出方组织/提出方产品/提出人/协作需求编码→查看链接/期望时间)。有数据时底部 ⚠️ 红色警告框提示及时处理。空数据检测:需过滤以https://pm.yyrd.com/或https://gfjira.yyrd.com/开头的URL模板占位值,仅当行中存在非URL模板的有效数据时才视为有数据。 - 采购云-协作待分析 — 标签
tag ok"协作",展示协作待分析数量和较上周对比。表格 10列(在提出待分析9列基础上增加"协作需求名称"列,截断50字),无警告框。 - 需求处理情况 — 标签
tag info"汇总",不输出数据表格,只输出分析文字:
- 原始需求总数(较上周变化)、已处理数、及时处理率(含上周对比)
- 待分析待规划数量及占比
- 大于1个月的待分析待规划个数(含上周对比)
- 底部 💡 蓝色提示框
7.3 节实现指南(支持问题处理中结存)
数据来源与持久化:
- 本周:邮件附件中的
处理中-按一二三级业务组织.xlsx(Row1=合计,Row2+=各部门) - 历史:
pending_history.json(工作区根目录),持久化存储所有历史周数据 - Excel 结构:col1=部门名称,col2=处理中数量;Row1 通常为合计行,部门名称为"采购云合计"
- 历史数据缺失补全:若
pending_history.json中缺少某周数据,可从往期报告HTML的7.3节明细表格中提取(表头含"MMDD结存"列,合计行有<tr class="total-row">)
pending_history.json 格式:
{
"weeks": [
{
"week": "0614",
"total": 143,
"depts": {
"供应链协同开发部": 42,
"采购寻源开发部": 34,
"供应商关系管理开发部": 32,
"招投标与超市开发部": 28,
"采购云公共与数智开发部": 6
}
}
]
}
核心逻辑:
- 读取
pending_history.json获取所有历史周数据 - 解析本周
处理中-按一二三级业务组织.xlsx获取当前周数据 - 将当前周数据追加到历史(如已存在同周码则更新),按周码排序后保存回
pending_history.json - 图表:展示全部历史周(所有
history_weeks),每条部门线一种颜色,不含合计线 - 表格:仅展示最近三周(
history_weeks[-3:]),本周列高亮,含"较上周变动"列
解析函数(openpyxl):
def parse_pending_dept(filepath, label):
"""解析处理中Excel,返回 (total, [{org, count}])"""
wb = openpyxl.load_workbook(filepath, data_only=True)
ws = wb.active
total = int(float(ws.cell(1, 2).value or 0))
depts = []
for r in range(2, ws.max_row + 1):
org = ws.cell(r, 1).value
cnt = ws.cell(r, 2).value
if org and cnt:
depts.append({'org': str(org).strip(), 'count': int(float(cnt))})
wb.close()
return total, depts
历史数据追加与持久化:
HISTORY_PATH = 'd:/Download/cunchu/pending_history.json'
report_week_label = '0712' # 每次修改为当前周码
# 读取历史
try:
with open(HISTORY_PATH, 'r', encoding='utf-8') as f:
pending_history = json.load(f)
except FileNotFoundError:
pending_history = {'weeks': []}
history_weeks = pending_history.get('weeks', [])
cur_depts_map = {d['org']: d['count'] for d in cur_depts}
current_week_entry = {
'week': report_week_label,
'total': cur_total,
'depts': cur_depts_map
}
# 已存在则更新,不存在则追加
existing_idx = next((i for i, w in enumerate(history_weeks) if w.get('week') == report_week_label), None)
if existing_idx is not None:
history_weeks[existing_idx] = current_week_entry
else:
history_weeks.append(current_week_entry)
history_weeks.sort(key=lambda w: w['week'])
# 保存
with open(HISTORY_PATH, 'w', encoding='utf-8') as f:
json.dump(pending_history, f, ensure_ascii=False, indent=2)
图表与表格分离构建:
chart_depts = ['供应链协同开发部', '供应商关系管理开发部', '招投标与超市开发部', '采购寻源开发部', '采购云公共与数智开发部']
chart_colors = ['#e74c3c', '#3498db', '#2ecc71', '#f39c12', '#9b59b6']
# 图表:全部历史周
history_labels = [w['week'] for w in history_weeks]
display_labels = [f"本周{w}" if w == report_week_label else w for w in history_labels]
chart_datasets = ''
for i, dept in enumerate(chart_depts):
data_vals = [w['depts'].get(dept, 0) for w in history_weeks]
data_str = ', '.join(str(v) for v in data_vals)
chart_datasets += f"""{{
label: "{dept}",
data: [{data_str}],
borderColor: "{chart_colors[i]}",
backgroundColor: "{chart_colors[i]}11",
borderWidth: 2,
tension: 0.3,
pointRadius: 4,
pointHoverRadius: 6,
}},"""
# 表格:仅最近三周
table_weeks = history_weeks[-3:]
table_labels = [w['week'] for w in table_weeks]
header_cols = ''
for w in table_labels:
header_cols += '<th>本周结存</th>' if w == report_week_label else f'<th>{w}结存</th>'
# 趋势链文字:全部历史周
trend_chain = ' → '.join(
f'<strong>{display_labels[i]}</strong>期 <strong>{history_weeks[i]["total"]}</strong>'
for i in range(len(history_weeks))
)
Chart.js 折线图实现要点:
- 在 HTML
<head>中通过 CDN 引入:<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script> - 使用
<canvas id="pending_chart">作为图表容器 - X 轴为日期标签(如
["0614", "0621", "0628", "0705", "本周0712"]),Y 轴为结存数量 - 每条部门线一种颜色(5个部门,无合计线)
- 使用
new Chart(ctx, { type: 'line', data: {...}, options: {...} })渲染 - 图表
init脚本放在window.onload中,或使用DOMContentLoaded事件
图表配置关键参数:
const chartColors = [
'#e74c3c', // 红 - 供应链协同开发部
'#3498db', // 蓝 - 供应商关系管理开发部
'#2ecc71', // 绿 - 招投标与超市开发部
'#f39c12', // 橙 - 采购寻源开发部
'#9b59b6', // 紫 - 采购云公共与数智开发部
];
// 注意:不再包含合计线
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: { position: 'bottom', labels: { padding: 16, usePointStyle: true, pointStyleWidth: 10, font: { size: 12 } } },
tooltip: { callbacks: { label: function(ctx) { return ctx.dataset.label + ": " + ctx.parsed.y + " 个"; } } }
},
scales: {
y: { beginAtZero: true, title: { display: true, text: "处理中数量(个)" }, ticks: { stepSize: 10 } },
x: { title: { display: true, text: "统计周期" } }
}
}
第7.3节完整 HTML 结构:
<div class="section" style="border-left: 4px solid #6f42c1;">
<div class="section-title">📋 7.3 支持问题处理中结存情况<span class="tag info">趋势对比</span></div>
<div class="info-box">
<strong>分析:</strong>采购云处理中支持问题总数:0614期 143 → 0621期 149 → ... → 本周0712期 155(较上周 ↑+39)。
本周各部门处理中结存按从多到少排列:...
</div>
<div style="font-weight:600;color:#1a6fc4;margin:14px 0 8px">处理中结存趋势对比</div>
<div style="position:relative;width:100%;max-width:900px;margin:0 auto 16px;background:#fff;border:1px solid #e0e0e0;border-radius:8px;padding:16px;">
<canvas id="pending_chart" style="width:100%;height:380px;"></canvas>
</div>
<script>
(function() {
var ctx = document.getElementById("pending_chart").getContext("2d");
new Chart(ctx, {
type: "line",
data: { labels: [...], datasets: [...] },
options: { ... }
});
})();
</script>
<div style="font-weight:600;color:#1a6fc4;margin:10px 0 6px">各部门结存明细</div>
<div class="table-wrap">
<table>
<thead><tr><th>排名</th><th>部门</th><th>0628结存</th><th>0705结存</th><th>本周结存</th><th>较上周变动</th></tr></thead>
<tbody>
<tr class="total-row"><td></td><td>合计</td><td>125</td><td>116</td><td style="font-weight:600;color:#1a6fc4">155</td><td>↑+39</td></tr>
<!-- 各部门行,按本周结存从多到少 -->
</tbody>
</table>
</div>
</div>
注意: 图表初始化脚本必须放在 {sec7_3} 变量之后、页面底部之前,使用独立的 <script> 标签。
输出文件:采购云QA报告-YYYYMMDD.html
样式基准: 采购云QA报告-20260426.html(固定不变)
模板规范: references/report_template_guide.md(从基准文件提取的完整规范)
Phase 5:截图导出 PNG
将 HTML 报告截图导出为全页 PNG 图片。使用 Node.js playwright(非 Python playwright,因托管 Python 环境下 pip install playwright 可能卡死)。
前置条件:
- Chromium headless shell 已安装于
%LOCALAPPDATA%/ms-playwright/chromium_headless_shell-1224/ - Node.js playwright 包已可用(npm 全局或本地)
截图脚本模板 screenshot_report.js(放在工作区根目录):
const { chromium } = require('playwright');
const path = require('path');
const fs = require('fs');
// ⚠️ 每次使用时按实际日期修改文件名
const htmlPath = path.resolve('d:/Download/cunchu/采购云QA报告-YYYYMMDD.html');
const outputPath = path.resolve('d:/Download/cunchu/采购云QA报告-YYYYMMDD.png');
const fileUrl = 'file:///' + htmlPath.replace(/\\/g, '/');
(async () => {
// ⚠️ 必须指定 executablePath:Node playwright 版本期望 chromium-1217,
// 但实际已安装 chromium_headless_shell-1224,不指定会报错找不到浏览器
const exePath = process.env.LOCALAPPDATA + '/ms-playwright/chromium_headless_shell-1224/chrome-headless-shell-win64/chrome-headless-shell.exe';
const browser = await chromium.launch({ headless: true, executablePath: exePath });
const page = await browser.newPage({ viewport: { width: 1400, height: 900 } });
await page.goto(fileUrl, { waitUntil: 'networkidle' });
await page.screenshot({ path: outputPath, fullPage: true });
await browser.close();
const size = fs.statSync(outputPath).size / 1024;
console.log(`✅ 截图已保存: ${outputPath}`);
console.log(` 大小: ${size.toFixed(1)} KB`);
})();
运行命令:
cd /d/Download/cunchu && /c/Users/wojia/.workbuddy/binaries/node/versions/22.12.0/node.exe screenshot_report.js
常见问题:
- 若报
Executable doesn't exist at ... chromium_headless_shell-1217:确认脚本中executablePath指向正确的chromium_headless_shell-1224路径 - 若报
Cannot find module 'playwright':运行npm install playwright安装
使用示例
读取邮件 chengmingm@yonyou.com 中主题为"采购云QA报告"、
收件时间 2026-04-19 14:05 的邮件,依照附件和邮件内容生成HTML报告
注意事项
- Excel 文件编码统一使用 utf-8,读取时用
openpyxl引擎 - Windows 环境下脚本需用
py -X utf8 script.py运行,避免 emoji 编码问题 $字符在密码中需写入 JSON 文件,避免 PowerShell 解析错误- IMAP 搜索日期格式:
DD-Mon-YYYY(如19-Apr-2026) - 邮件可能在归档文件夹(非 INBOX),需遍历所有文件夹搜索
- 搜索上周邮件时可用相邻 ID 定位,或按日期范围搜索
- Chart.js CDN:第7.3节折线图依赖 Chart.js,HTML 须在
<head>中引入 CDN 脚本 - 处理中结存历史数据:使用
pending_history.json持久化存储,脚本自动追加当前周数据。图表展示全部历史周期,表格仅展示近三周。历史数据缺失时可从往期报告HTML的7.3节明细表格中提取补全 - 图表不含合计线:第7.3节折线图仅展示5个部门线,不再包含合计线
- 处理中结存Excel结构:
处理中-按一二三级业务组织.xlsx的 col1=部门名(含"采购云合计"),col2=数量 - 提出待分析空数据检测:需过滤以
https://pm.yyrd.com/或https://gfjira.yyrd.com/开头的URL模板占位值,仅当行中存在非URL模板的有效数据时才视为有数据 - 迭代进度过滤:已上线的迭代(如 20260615)应从第5节中排除,仅展示进行中的迭代
微信扫一扫