enhance(meter):完成大部分表计相关的接口。

This commit is contained in:
徐涛
2023-06-11 22:31:32 +08:00
parent e366888608
commit 2339e4c725
10 changed files with 700 additions and 36 deletions

View File

@@ -2,6 +2,7 @@ package service
import (
"electricity_bill_calc/cache"
"electricity_bill_calc/excel"
"electricity_bill_calc/global"
"electricity_bill_calc/logger"
"electricity_bill_calc/model"
@@ -11,6 +12,7 @@ import (
"electricity_bill_calc/vo"
"fmt"
"mime/multipart"
"strings"
"github.com/samber/lo"
"github.com/shopspring/decimal"
@@ -108,8 +110,146 @@ func (ms _MeterService) UpdateMeterRecord(pid string, code string, form *vo.Mete
}
// 处理上传的Excel格式表计档案文件根据表号自动更新数据库
func (ms _MeterService) BatchImportMeters(pid string, file multipart.FileHeader) error {
return nil
func (ms _MeterService) BatchImportMeters(pid string, file *multipart.FileHeader) ([]excel.ExcelAnalysisError, error) {
ms.log.Info("处理上传的Excel格式表计档案文件", zap.String("park id", pid))
ctx, cancel := global.TimeoutContext(10)
defer cancel()
archiveFile, err := file.Open()
if err != nil {
ms.log.Error("无法打开上传的Excel格式表计档案文件。", zap.Error(err))
return make([]excel.ExcelAnalysisError, 0), fmt.Errorf("无法打开上传的文件,%w", err)
}
analyzer, err := excel.NewMeterArchiveExcelAnalyzer(archiveFile)
if err != nil {
ms.log.Error("无法根据上传的 Excel 文件创建表计档案分析器。", zap.Error(err))
return make([]excel.ExcelAnalysisError, 0), fmt.Errorf("无法创建表计档案解析器,%w", err)
}
records, errs := analyzer.Analysis(*new(model.MeterImportRow))
if len(errs) > 0 {
ms.log.Error("表计档案分析器在解析上传的 Excel 文件时发生错误。", zap.Int("error count", len(errs)))
return errs, fmt.Errorf("表计档案分析器在解析上传的 Excel 文件时发生错误。")
}
// 步骤1对目前已经解析到的数据进行重复检测记录重复内容并直接返回
var codeStat = make(map[string]int, 0)
for _, record := range records {
if _, ok := codeStat[record.Code]; !ok {
codeStat[record.Code] = 0
}
codeStat[record.Code]++
}
duplicatedCodes := make([]string, 0)
for code, count := range codeStat {
if count > 1 {
duplicatedCodes = append(duplicatedCodes, code)
}
}
if len(duplicatedCodes) > 0 {
ms.log.Error("表计档案分析器在解析上传的 Excel 文件时发现重复的表计编号。", zap.Strings("duplicated codes", duplicatedCodes))
return []excel.ExcelAnalysisError{
{Row: 0, Col: 0, Err: excel.AnalysisError{Err: fmt.Errorf("表计档案分析器在解析上传的 Excel 文件时发现重复的表计编号。(%s)", strings.Join(duplicatedCodes, ", "))}},
}, fmt.Errorf("表计档案分析器在解析上传的 Excel 文件时发现重复的表计编号。(%s)", strings.Join(duplicatedCodes, ", "))
}
// 步骤2获取指定园区下的所有建筑信息
buildings, err := repository.ParkRepository.RetrieveParkBuildings(pid)
if err != nil {
ms.log.Error("无法获取指定园区下的所有建筑信息。", zap.Error(err))
return make([]excel.ExcelAnalysisError, 0), fmt.Errorf("无法获取指定园区下的所有建筑信息,%w", err)
}
buildingNames := lo.Map(buildings, func(element *model.ParkBuilding, _ int) string {
return element.Name
})
// 步骤2.1:获取表计档案中出现的所有建筑,并对档案中新出现的建筑进行创建操作
unexistsBuildingNames := make([]string, 0)
for _, record := range records {
if !lo.Contains(buildingNames, *record.Building) {
unexistsBuildingNames = append(unexistsBuildingNames, *record.Building)
}
}
tx, err := global.DB.Begin(ctx)
if err != nil {
ms.log.Error("无法在自动导入建筑阶段启动数据库事务。", zap.Error(err))
return make([]excel.ExcelAnalysisError, 0), fmt.Errorf("无法在自动导入建筑阶段启动数据库事务,%w", err)
}
for _, name := range unexistsBuildingNames {
_, err := repository.ParkRepository.CreateParkBuildingWithTransaction(tx, ctx, pid, name, nil)
if err != nil {
ms.log.Error("无法在自动导入建筑阶段创建新的建筑。", zap.String("building name", name), zap.Error(err))
tx.Rollback(ctx)
return make([]excel.ExcelAnalysisError, 0), fmt.Errorf("无法在自动导入建筑阶段创建新的建筑,%w", err)
}
}
err = tx.Commit(ctx)
if err != nil {
ms.log.Error("无法在自动导入建筑阶段提交数据库事务。", zap.Error(err))
tx.Rollback(ctx)
return make([]excel.ExcelAnalysisError, 0), fmt.Errorf("无法在自动导入建筑阶段提交数据库事务,%w", err)
}
buildings, err = repository.ParkRepository.RetrieveParkBuildings(pid)
if err != nil {
ms.log.Error("无法重新获取指定园区下的所有建筑信息。", zap.Error(err))
return make([]excel.ExcelAnalysisError, 0), fmt.Errorf("无法重新获取指定园区下的所有建筑信息,%w", err)
}
// 步骤2.3检测并替换表计档案中的建筑ID
for _, record := range records {
for _, building := range buildings {
if building.Name == *record.Building {
record.Building = &building.Id
break
}
}
}
// 步骤3启动数据库事务直接构建表计插入语句但提供On Conflict Do Update功能
tx, err = global.DB.Begin(ctx)
if err != nil {
ms.log.Error("无法启动数据插入阶段的数据库事务。", zap.Error(err))
return make([]excel.ExcelAnalysisError, 0), fmt.Errorf("无法启动数据插入阶段的数据库事务,%w", err)
}
meterCreationForms := lo.Map(records, func(element model.MeterImportRow, _ int) vo.MeterCreationForm {
return vo.MeterCreationForm{
Code: element.Code,
Address: element.Address,
MeterType: element.MeterType,
Ratio: element.Ratio,
Seq: element.Seq,
Enabled: true,
Building: element.Building,
OnFloor: element.OnFloor,
Area: element.Area,
MeterReadingForm: vo.MeterReadingForm{
ReadAt: &element.ReadAt,
Overall: element.Overall,
Critical: element.Critical.Decimal,
Peak: element.Peak.Decimal,
Flat: element.Flat.Decimal,
Valley: element.Valley.Decimal,
},
}
})
for _, record := range meterCreationForms {
_, err := repository.MeterRepository.CreateOrUpdateMeter(tx, ctx, pid, record)
if err != nil {
ms.log.Error("无法在数据插入阶段创建或更新表计。", zap.String("meter code", record.Code), zap.Error(err))
tx.Rollback(ctx)
return make([]excel.ExcelAnalysisError, 0), fmt.Errorf("无法在数据插入阶段创建或更新表计,%w", err)
}
}
// 步骤5将全部抄表信息保存进入数据库
for _, record := range meterCreationForms {
_, err := repository.MeterRepository.RecordReading(tx, ctx, pid, record.Code, record.MeterType, record.Ratio, &record.MeterReadingForm)
if err != nil {
ms.log.Error("无法在数据插入阶段保存抄表信息。", zap.String("meter code", record.Code), zap.Error(err))
tx.Rollback(ctx)
return make([]excel.ExcelAnalysisError, 0), fmt.Errorf("无法在数据插入阶段保存抄表信息,%w", err)
}
}
// 步骤6执行事务更新数据库
err = tx.Commit(ctx)
if err != nil {
ms.log.Error("无法在数据插入阶段提交数据库事务。", zap.Error(err))
tx.Rollback(ctx)
return make([]excel.ExcelAnalysisError, 0), fmt.Errorf("无法在数据插入阶段提交数据库事务,%w", err)
}
return make([]excel.ExcelAnalysisError, 0), nil
}
// 更换系统中的表计