package export import ( "io" "os" "path/filepath" "strings" "testing" "time" ) // 导出件的文档属性不能用 excelize 的默认值(作者 xuri、创建/修改时间 2006-09-16): // 这几个字段会直接出现在 Excel 的「文件 → 信息」里。作者用系统名,时间用导出当下。 func TestExportDocPropsAreNeutral(t *testing.T) { db := openEventDB(t) seedEvent(t, db) dst := filepath.Join(t.TempDir(), "总表.xlsx") before := time.Now() if err := ExportStandingsXLSX(db, dst, Options{}); err != nil { t.Fatalf("导出总表: %v", err) } after := time.Now() f := openXLSX(t, dst) defer func() { _ = f.Close() }() props, err := f.GetDocProps() if err != nil { t.Fatalf("读取文档属性: %v", err) } if props.Creator != docAuthor { t.Errorf("作者 = %q,期望 %q", props.Creator, docAuthor) } if props.LastModifiedBy != docAuthor { t.Errorf("最后修改者 = %q,期望 %q", props.LastModifiedBy, docAuthor) } if strings.Contains(props.Creator+props.LastModifiedBy, "xuri") { t.Errorf("文档属性里不该出现 excelize 的作者名:%+v", props) } created, err := time.Parse(time.RFC3339, props.Created) if err != nil { t.Fatalf("创建时间 %q 不是 RFC3339: %v", props.Created, err) } if created.Before(before.Add(-time.Minute)) || created.After(after.Add(time.Minute)) { t.Errorf("创建时间 = %v,不在本次导出的时间窗内", created) } if props.Modified != props.Created { t.Errorf("修改时间 = %q,期望与创建时间同为 %q", props.Modified, props.Created) } app, err := f.GetAppProps() if err != nil { t.Fatalf("读取应用属性: %v", err) } if app.Application != docApplication { t.Errorf("Application = %q,期望 %q", app.Application, docApplication) } } // writeWorkbookFile 把「写 xlsx 流」的导出落到磁盘上,供本文件核对文档属性。 // 落盘是给测试读回用的,生产一律直接写响应体(见 saveWorkbook 的说明)。 func writeWorkbookFile(dst string, write func(io.Writer) error) error { f, err := os.Create(dst) if err != nil { return err } if err := write(f); err != nil { _ = f.Close() return err } return f.Close() } // 所有 xlsx 导出都走 buildWorkbook,文档属性必须一处设好、处处生效。 func TestEveryWorkbookCarriesDocProps(t *testing.T) { db := openEventDB(t) seedEvent(t, db) dir := t.TempDir() // 表里必须把 9 个落盘导出函数**全部**覆盖到:漏掉哪一个,那份工作簿的文档属性就没人守。 exports := map[string]func(string) error{ "总表.xlsx": func(dst string) error { return ExportStandingsXLSX(db, dst, Options{}) }, "全部.xlsx": func(dst string) error { return ExportAllXLSX(db, dst, Options{}) }, "分项.xlsx": func(dst string) error { return ExportEventXLSX(db, 1, dst, Options{}) }, "单页.xlsx": func(dst string) error { return ExportFlatXLSX(db, dst, Options{}) }, "奖牌.xlsx": func(dst string) error { return ExportMedalDetailXLSX(db, dst, Options{}) }, "个人明细.xlsx": func(dst string) error { return ExportAthleteDetailXLSX(db, dst, Options{}) }, "单位明细.xlsx": func(dst string) error { return ExportTeamDetailXLSX(db, 1, dst, Options{}) }, "单位总明细.xlsx": func(dst string) error { return ExportAllTeamDetailsXLSX(db, dst, Options{}) }, "报名名单.xlsx": func(dst string) error { return ExportEntriesXLSX(db, dst, EntriesFilter{}, Options{}) }, // 基础数据包没有落盘版包装函数(生产与这里都走 Load + Write),用流写盘补齐这一项。 "基础数据包.xlsx": func(dst string) error { return writeWorkbookFile(dst, func(w io.Writer) error { wb, err := LoadBaseDataWorkbook(db, Options{}) if err != nil { return err } return WriteBaseDataXLSX(w, wb) }) }, } for name, run := range exports { dst := filepath.Join(dir, name) if err := run(dst); err != nil { t.Fatalf("导出 %s: %v", name, err) } f := openXLSX(t, dst) props, err := f.GetDocProps() if err != nil { t.Fatalf("读取 %s 的文档属性: %v", name, err) } if props.Creator != docAuthor { t.Errorf("%s 作者 = %q,期望 %q", name, props.Creator, docAuthor) } if err := f.Close(); err != nil { t.Fatalf("关闭 %s: %v", name, err) } } }