package excel import ( "electricity_bill_calc/logger" "electricity_bill_calc/model" "electricity_bill_calc/tools" "fmt" "io" "github.com/xuri/excelize/v2" "go.uber.org/zap" ) var meterReadingsRecognizers = []*ColumnRecognizer{ {Pattern: [][]string{{"表", "表计"}, {"编号"}}, Tag: "code", MatchIndex: -1, MustFill: true}, {Pattern: [][]string{{"抄表", "结束"}, {"时间", "日期"}}, Tag: "readAt", MatchIndex: -1, MustFill: true}, {Pattern: [][]string{{"用电", "有功", "表底", "底数"}, {"总", "量"}}, Tag: "overall", MatchIndex: -1, MustFill: true}, {Pattern: [][]string{{"有功", "表底", "底数"}, {"尖"}}, Tag: "critical", MatchIndex: -1}, {Pattern: [][]string{{"有功", "表底", "底数"}, {"峰"}}, Tag: "peak", MatchIndex: -1}, {Pattern: [][]string{{"有功", "表底", "底数"}, {"谷"}}, Tag: "valley", MatchIndex: -1}, } func NewMeterReadingsExcelAnalyzer(file io.Reader) (*ExcelAnalyzer[model.ReadingImportRow], error) { return NewExcelAnalyzer[model.ReadingImportRow](file, meterReadingsRecognizers) } type MeterReadingsExcelTemplateGenerator struct { file *excelize.File log *zap.Logger } func NewMeterReadingsExcelTemplateGenerator() *MeterReadingsExcelTemplateGenerator { return &MeterReadingsExcelTemplateGenerator{ file: excelize.NewFile(), log: logger.Named("Excel", "MeterReadings"), } } func (MeterReadingsExcelTemplateGenerator) titles() *[]interface{} { return &[]interface{}{ "抄表序号", "抄表时间", "表计编号", "表计名称", "商户名称", "倍率", "有功(总)", "有功(尖)", "有功(峰)", "有功(谷)", } } func (g MeterReadingsExcelTemplateGenerator) Close() { g.file.Close() } func (g MeterReadingsExcelTemplateGenerator) WriteTo(w io.Writer) (int64, error) { return g.file.WriteTo(w) } func (g MeterReadingsExcelTemplateGenerator) WriteTemplateData(meters []*model.SimpleMeterDocument) error { var err error defaultSheet := g.file.GetSheetName(0) g.log.Debug("选定默认输出表格", zap.String("sheet", defaultSheet)) err = g.file.SetColWidth(defaultSheet, "A", "E", 30) if err != nil { g.log.Error("未能设定长型单元格的宽度。", zap.Error(err)) return fmt.Errorf("未能设定长型单元格的宽度,%w", err) } err = g.file.SetColWidth(defaultSheet, "F", "F", 10) if err != nil { g.log.Error("未能设定倍率单元格的宽度。", zap.Error(err)) return fmt.Errorf("未能设定倍率单元格的宽度,%w", err) } err = g.file.SetColWidth(defaultSheet, "G", "J", 20) if err != nil { g.log.Error("未能设定短型单元格的宽度。", zap.Error(err)) return fmt.Errorf("未能设定短型单元格的宽度,%w", err) } err = g.file.SetSheetRow(defaultSheet, "A1", g.titles()) if err != nil { g.log.Error("未能输出模板标题。", zap.Error(err)) return fmt.Errorf("未能输出模板标题,%w", err) } err = g.file.SetRowHeight(defaultSheet, 1, 30) if err != nil { g.log.Error("未能设定标题行的高度。", zap.Error(err)) return fmt.Errorf("未能设定标题行的高度,%w", err) } dateTimeExp := "yyyy-mm-dd hh:mm" dateTimeColStyle, err := g.file.NewStyle(&excelize.Style{ CustomNumFmt: &dateTimeExp, }) if err != nil { g.log.Error("未能创建日期时间格式。", zap.Error(err)) return fmt.Errorf("未能创建日期时间格式,%w", err) } endCellCoord, _ := excelize.CoordinatesToCellName(2, len(meters)+1) g.file.SetCellStyle(defaultSheet, "B2", endCellCoord, dateTimeColStyle) numExp := "0.0000" numColStyle, err := g.file.NewStyle(&excelize.Style{ CustomNumFmt: &numExp, }) if err != nil { g.log.Error("未能创建抄表数字格式。", zap.Error(err)) return fmt.Errorf("未能创建抄表数字格式,%w", err) } endCellCoord, _ = excelize.CoordinatesToCellName(9, len(meters)+1) g.file.SetCellStyle(defaultSheet, "F2", endCellCoord, numColStyle) for i, meter := range meters { cellCoord, _ := excelize.CoordinatesToCellName(1, i+2) ratio, _ := meter.Ratio.Float64() if err := g.file.SetSheetRow(defaultSheet, cellCoord, &[]interface{}{ meter.Seq, "", meter.Code, tools.DefaultTo(meter.Address, ""), tools.DefaultTo(meter.TenementName, ""), ratio, }); err != nil { g.log.Error("向模板写入数据出现错误。", zap.Error(err)) return fmt.Errorf("向模板写入数据出现错误,%w", err) } } return err }