Files
knowledge-governance/internal/qaopsmirror/weekly_template.go
T
姚章浩 226d47a72d
build / build (push) Successful in 1m13s
fix(qa): 周报正文移除中英文质量统计口径说明
仅删除周报说明段及模板占位,保留自然月规则、指标数值和来源审计。
历史已保存周报不因模板变更自动改写,保留中英文各六节。
验证:移除说明断言先红后绿,自然月指标/来源及历史证据保留测试通过,完整门禁与原生备份恢复通过。
2026-10-25 17:15:35 +08:00

188 lines
6.5 KiB
Go

package qaopsmirror
import (
"encoding/json"
"fmt"
"knowledge-governance/internal/dobotaiops"
"math"
"sort"
"strings"
"time"
)
func weeklyMarkdownCell(value string) string {
return strings.NewReplacer("&", "&amp;", "<", "&lt;", ">", "&gt;", "\\", "\\\\", "*", "\\*", "_", "\\_", "[", "\\[", "]", "\\]", "|", "\\|", "`", "\\`", "\r", " ", "\n", " ").Replace(value)
}
func weeklyPercent(value *float64) string {
if value == nil {
return "—"
}
return fmt.Sprintf("%.2f%%", *value)
}
func weeklyNumber(value *float64) string {
if value == nil {
return "—"
}
return fmt.Sprintf("%.2f", *value)
}
func validWeeklyValue(v *float64, percent bool) bool {
return v == nil || (!math.IsNaN(*v) && !math.IsInf(*v, 0) && *v >= 0 && (!percent || *v <= 100))
}
func weeklyRegionRanking(regions []DashboardRegion, coverage, english bool) string {
rows := append([]DashboardRegion(nil), regions...)
metric := func(r DashboardRegion) *float64 {
if coverage {
return r.UsageCoverage.Percent
}
return r.Frequency
}
sort.SliceStable(rows, func(i, j int) bool {
a, b := metric(rows[i]), metric(rows[j])
if a == nil {
return false
}
if b == nil {
return true
}
if *a == *b {
return rows[i].ID < rows[j].ID
}
return *a > *b
})
values := make([]string, 0, len(rows))
for i, r := range rows {
name := r.Name
if english {
if translated := map[string]string{"国内工业": "China Industrial", "America": "Americas", "APAC": "APAC", "EMEA": "EMEA"}[name]; translated != "" {
name = translated
}
}
value := weeklyNumber(metric(r))
if coverage {
value = weeklyPercent(metric(r))
}
values = append(values, fmt.Sprintf("%d. %s: %s", i+1, weeklyMarkdownCell(name), value))
}
if len(values) == 0 {
return "—"
}
return strings.Join(values, "\n")
}
func renderWeeklyTemplate(week string, data Dashboard, now time.Time) (string, json.RawMessage, error) {
start, end, err := weeklyDraftBounds(week, now)
if err != nil || !data.Start.Equal(start) || !data.End.Equal(end) || data.TotalIssues < 0 || data.NewThisWeek < 0 || data.NewThisWeek > data.TotalIssues || data.MonthStart.IsZero() {
return "", nil, dobotaiops.ErrInvalidQuery
}
coverage, accuracy := data.Quality.Rates.Coverage.Percent, data.Quality.Rates.Correctness.Percent
qualitySource := "live-week"
qualitySourceWeekKey := ""
for _, f := range data.FrozenWeeks {
if f.WeekStart.Equal(start) && f.FrozenAt != nil {
coverage, accuracy = f.Coverage.Percent, f.Correctness.Percent
qualitySource = "frozen-week"
}
}
for _, p := range data.ReportedWeeks {
if p.WeekStart.Equal(start) && p.WeekEnd.Equal(start.AddDate(0, 0, 7)) && !p.WeekEnd.After(end) {
c, a := p.CoveragePercent, p.CorrectnessPercent
coverage, accuracy = &c, &a
qualitySource = "reported-history"
qualitySourceWeekKey = p.Week
}
}
if data.QualityPeriod != nil || data.MonthlyQuality != nil {
period, rates := data.QualityPeriod, data.MonthlyQuality
if period == nil || rates == nil || period.Source != dashboardMonthlySource || period.PolicyVersion != dashboardMonthlyPolicy || rates.Records < 0 {
return "", nil, dobotaiops.ErrInvalidQuery
}
wantStart, wantEnd := dashboardQualityMonth(start, end, period.CalculatedAt)
if !period.Start.Equal(wantStart) || !period.CutoffAt.Equal(wantEnd) || period.Month != wantStart.Format("2006-01") {
return "", nil, dobotaiops.ErrInvalidQuery
}
coverage, accuracy = rates.Coverage.Percent, rates.Correctness.Percent
qualitySource, qualitySourceWeekKey = dashboardMonthlySource, ""
}
if !validWeeklyValue(coverage, true) || !validWeeklyValue(accuracy, true) {
return "", nil, dobotaiops.ErrInvalidQuery
}
for _, r := range data.Regions {
if !validWeeklyValue(r.Frequency, false) || !validWeeklyValue(r.UsageCoverage.Percent, true) {
return "", nil, dobotaiops.ErrInvalidQuery
}
}
month := data.MonthStart.In(dashboardLocation)
replacements := []string{
"{{YEAR}}", week[:4], "{{WEEK}}", week[6:],
"{{TOTAL_QUESTIONS}}", fmt.Sprint(data.TotalIssues), "{{NEW_THIS_WEEK}}", fmt.Sprint(data.NewThisWeek),
"{{COVERAGE_RATE}}", weeklyPercent(coverage), "{{ACCURACY_RATE}}", weeklyPercent(accuracy),
"{{MONTH_EN}}", month.Format("January 2006"), "{{MONTH_ZH}}", fmt.Sprintf("%d年%d月", month.Year(), month.Month()),
"{{AVERAGE_USAGE_RANKING_EN}}", weeklyRegionRanking(data.Regions, false, true), "{{AVERAGE_USAGE_RANKING_ZH}}", weeklyRegionRanking(data.Regions, false, false),
"{{USAGE_COVERAGE_RANKING_EN}}", weeklyRegionRanking(data.Regions, true, true), "{{USAGE_COVERAGE_RANKING_ZH}}", weeklyRegionRanking(data.Regions, true, false),
"{{WEEKLY_HIGHLIGHTS_EN}}", "[To be completed]", "{{NEXT_WEEK_PRIORITIES_EN}}", "[To be completed]",
"{{WEEKLY_HIGHLIGHTS_ZH}}", "[待填写]", "{{NEXT_WEEK_PRIORITIES_ZH}}", "[待填写]"}
markdown := strings.NewReplacer(replacements...).Replace(weeklySkillTemplate)
seed, _ := json.Marshal(map[string]any{"template_version": "dobot-weekly-six-sections-v1", "week": week, "start": start, "end": end, "generated_at": now, "source": "governance-master", "local_revision": data.LocalRevision, "source_observed_at": data.AsOf, "organization": data.Organization, "quality_source": qualitySource, "quality_source_week_key": qualitySourceWeekKey, "quality_period": data.QualityPeriod, "quality_counts": data.MonthlyQuality, "total_issues": data.TotalIssues, "new_this_week": data.NewThisWeek, "coverage_percent": coverage, "correctness_percent": accuracy, "month": month.Format("2006-01"), "regions": data.Regions})
return markdown, seed, nil
}
const weeklySkillTemplate = `# Knowledge Base Weekly Report - {{YEAR}} WK{{WEEK}}
## 1. Weekly Summary
Total Questions: {{TOTAL_QUESTIONS}}
New This Week: {{NEW_THIS_WEEK}}
## 2. Key Metrics
Coverage Rate: {{COVERAGE_RATE}}
Accuracy Rate: {{ACCURACY_RATE}}
## 3. {{MONTH_EN}} Regional Average Usage Ranking
{{AVERAGE_USAGE_RANKING_EN}}
## 4. {{MONTH_EN}} Regional Usage Coverage Ranking
{{USAGE_COVERAGE_RANKING_EN}}
## 5. Weekly Highlights
{{WEEKLY_HIGHLIGHTS_EN}}
## 6. Next Week Priorities
{{NEXT_WEEK_PRIORITIES_EN}}
# 知识库周报 - {{YEAR}}年第{{WEEK}}周
## 一、本周概况
问题总数:{{TOTAL_QUESTIONS}}
本周新增:{{NEW_THIS_WEEK}}
## 二、核心指标
覆盖率:{{COVERAGE_RATE}}
正确率:{{ACCURACY_RATE}}
## 三、{{MONTH_ZH}}区域人均使用次数排名
{{AVERAGE_USAGE_RANKING_ZH}}
## 四、{{MONTH_ZH}}区域使用覆盖率排名
{{USAGE_COVERAGE_RANKING_ZH}}
## 五、本周重点
{{WEEKLY_HIGHLIGHTS_ZH}}
## 六、下周重点
{{NEXT_WEEK_PRIORITIES_ZH}}
`