refactor(meter):表计档案模板的下载改为使用Excel 生成的方法实现。

This commit is contained in:
徐涛 2023-06-13 10:23:47 +08:00
parent 46ae943653
commit 7bac667c2a
5 changed files with 129 additions and 4 deletions

Binary file not shown.

View File

@ -1,6 +1,7 @@
package controller package controller
import ( import (
"electricity_bill_calc/excel"
"electricity_bill_calc/logger" "electricity_bill_calc/logger"
"electricity_bill_calc/model" "electricity_bill_calc/model"
"electricity_bill_calc/repository" "electricity_bill_calc/repository"
@ -160,10 +161,24 @@ func downloadMeterArchiveTemplate(c *fiber.Ctx) error {
meterLog.Error("无法下载指定的园区表计登记模板,无法获取园区信息", zap.Error(err)) meterLog.Error("无法下载指定的园区表计登记模板,无法获取园区信息", zap.Error(err))
return result.NotFound(err.Error()) return result.NotFound(err.Error())
} }
return c.Download( buildings, err := repository.ParkRepository.RetrieveParkBuildings(parkId)
"./assets/meter_04kv_template.xlsx", if err != nil {
fmt.Sprintf("%s_表计档案.xlsx", parkDetail.Name), meterLog.Error("无法下载指定的园区表计登记模板,无法获取园区建筑列表", zap.Error(err))
) return result.NotFound(fmt.Sprintf("无法获取园区建筑列表,%s", err.Error()))
}
if err != nil {
meterLog.Error("无法下载指定的园区表计登记模板,无法生成表计登记模板", zap.Error(err))
return result.NotFound(fmt.Sprintf("无法生成表计登记模板,%s", err.Error()))
}
c.Status(fiber.StatusOK)
c.Set(fiber.HeaderContentType, fiber.MIMEOctetStream)
c.Set("Content-Transfer-Encoding", "binary")
c.Set(fiber.HeaderContentDisposition, fmt.Sprintf("attachment; filename=%s-表计登记模板.xlsx", parkDetail.Name))
templateGenerator := excel.NewMeterArchiveExcelTemplateGenerator()
defer templateGenerator.Close()
err = templateGenerator.WriteTemplateData(buildings)
templateGenerator.WriteTo(c.Response().BodyWriter())
return nil
} }
// 从Excel文件中导入表计档案 // 从Excel文件中导入表计档案

View File

@ -1,8 +1,14 @@
package excel package excel
import ( import (
"electricity_bill_calc/logger"
"electricity_bill_calc/model" "electricity_bill_calc/model"
"fmt"
"io" "io"
"github.com/samber/lo"
"github.com/xuri/excelize/v2"
"go.uber.org/zap"
) )
var meterArchiveRecognizers = []*ColumnRecognizer{ var meterArchiveRecognizers = []*ColumnRecognizer{
@ -25,3 +31,89 @@ var meterArchiveRecognizers = []*ColumnRecognizer{
func NewMeterArchiveExcelAnalyzer(file io.Reader) (*ExcelAnalyzer[model.MeterImportRow], error) { func NewMeterArchiveExcelAnalyzer(file io.Reader) (*ExcelAnalyzer[model.MeterImportRow], error) {
return NewExcelAnalyzer[model.MeterImportRow](file, meterArchiveRecognizers) return NewExcelAnalyzer[model.MeterImportRow](file, meterArchiveRecognizers)
} }
type MeterArchiveExcelTemplateGenerator struct {
file *excelize.File
log *zap.Logger
}
func NewMeterArchiveExcelTemplateGenerator() *MeterArchiveExcelTemplateGenerator {
return &MeterArchiveExcelTemplateGenerator{
file: excelize.NewFile(),
log: logger.Named("Excel", "MeterArchive"),
}
}
func (MeterArchiveExcelTemplateGenerator) titles() *[]interface{} {
return &[]interface{}{
"序号",
"表址",
"表号",
"表计类型",
"倍率",
"所在建筑",
"所在楼层",
"辖盖面积",
"抄表时间",
"有功(总)",
"有功(尖)",
"有功(峰)",
"有功(平)",
"有功(谷)",
}
}
func (g *MeterArchiveExcelTemplateGenerator) Close() {
g.file.Close()
}
func (g MeterArchiveExcelTemplateGenerator) WriteTo(w io.Writer) (int64, error) {
return g.file.WriteTo(w)
}
func (g *MeterArchiveExcelTemplateGenerator) WriteTemplateData(buildings []*model.ParkBuilding) error {
var err error
defaultSheet := g.file.GetSheetName(0)
g.log.Debug("Select default template sheet", zap.String("sheet", defaultSheet))
err = g.file.SetColWidth(defaultSheet, "B", "I", 20)
if err != nil {
g.log.Error("未能设定长型列宽。", zap.Error(err))
return fmt.Errorf("未能设定长型列宽,%w", err)
}
err = g.file.SetColWidth(defaultSheet, "J", "N", 15)
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, 20)
if err != nil {
g.log.Error("未能设定标题行高度。", zap.Error(err))
return fmt.Errorf("未能设定标题行高度,%w", err)
}
meterInstallationTypeValidation := excelize.NewDataValidation(false)
meterInstallationTypeValidation.SetDropList([]string{"商户表", "公共表", "楼道表"})
meterInstallationTypeValidation.Sqref = "D2:D1048576"
err = g.file.AddDataValidation(defaultSheet, meterInstallationTypeValidation)
if err != nil {
g.log.Error("未能设定表计类型选择器。", zap.Error(err))
return fmt.Errorf("未能设定表计类型选择器,%w", err)
}
buildingValidation := excelize.NewDataValidation(true)
buildingNames := lo.Map(buildings, func(b *model.ParkBuilding, _ int) string {
return b.Name
})
buildingValidation.SetDropList(buildingNames)
buildingValidation.Sqref = "F2:F1048576"
err = g.file.AddDataValidation(defaultSheet, buildingValidation)
if err != nil {
g.log.Error("未能设定所在建筑选择器。", zap.Error(err))
return fmt.Errorf("未能设定所在建筑选择器,%w", err)
}
return nil
}

View File

@ -3,6 +3,8 @@ package excel
import ( import (
"electricity_bill_calc/model" "electricity_bill_calc/model"
"io" "io"
"github.com/xuri/excelize/v2"
) )
var meterReadingsRecognizers = []*ColumnRecognizer{ var meterReadingsRecognizers = []*ColumnRecognizer{
@ -17,3 +19,13 @@ var meterReadingsRecognizers = []*ColumnRecognizer{
func NewMeterReadingsExcelAnalyzer(file io.Reader) (*ExcelAnalyzer[model.ReadingImportRow], error) { func NewMeterReadingsExcelAnalyzer(file io.Reader) (*ExcelAnalyzer[model.ReadingImportRow], error) {
return NewExcelAnalyzer[model.ReadingImportRow](file, meterReadingsRecognizers) return NewExcelAnalyzer[model.ReadingImportRow](file, meterReadingsRecognizers)
} }
type MeterReadingsExcelTemplateGenerator struct {
file *excelize.File
}
func NewMeterReadingsExcelTemplateGenerator() *MeterReadingsExcelTemplateGenerator {
return &MeterReadingsExcelTemplateGenerator{
file: excelize.NewFile(),
}
}

View File

@ -660,6 +660,12 @@ func (ms _MeterService) RecordReading(pid, meterCode string, form *vo.MeterReadi
// 获取指定园区的全部待抄表计列表并将其输出到Excel文件模板中提供生成文件的二进制内容 // 获取指定园区的全部待抄表计列表并将其输出到Excel文件模板中提供生成文件的二进制内容
func (ms _MeterService) GenerateParkMeterReadingTemplate(pid string, meters []*model.SimpleMeterDocument) ([]byte, error) { func (ms _MeterService) GenerateParkMeterReadingTemplate(pid string, meters []*model.SimpleMeterDocument) ([]byte, error) {
// 步骤1复制公用模板文件到临时文件夹文件以园区ID命名
// 步骤2打开复制后的园区文件
// 步骤3获取园区中需要抄表的表计列表
// 步骤4循环表计列表将表计信息写入到Excel文件中
// 步骤5将生成的临时文件的具体二进制内容返回给调用者
return nil, nil return nil, nil
} }