enhance(meter):基本完成表计抄表记录上传解析,待测。

This commit is contained in:
徐涛 2023-06-13 21:01:14 +08:00
parent d4fbf86800
commit 7da5bd1112
2 changed files with 150 additions and 35 deletions

View File

@ -37,9 +37,10 @@ func InitializeMeterHandlers(router *fiber.App) {
router.Get("/meter/choice", security.EnterpriseAuthorize, listUnboundMeters)
router.Get("/meter/choice/tenement", security.EnterpriseAuthorize, listUnboundTenementMeters)
router.Get("/reading/:pid", security.EnterpriseAuthorize, queryMeterReadings)
router.Post("/reading/:pid/:code", security.EnterpriseAuthorize, recordMeterReading)
router.Put("/reading/:pid/:code/:reading", security.EnterpriseAuthorize, updateMeterReading)
router.Get("/reading/:pid/template", security.EnterpriseAuthorize, downloadMeterReadingsTemplate)
router.Post("/reading/:pid/batch", security.EnterpriseAuthorize, uploadMeterReadings)
router.Post("/reading/:pid/:code", security.EnterpriseAuthorize, recordMeterReading)
}
// 查询指定园区下的表计信息
@ -202,10 +203,10 @@ func uploadMeterArchive(c *fiber.Ctx) error {
ok, err := repository.ParkRepository.IsParkBelongs(parkId, session.Uid)
switch {
case err != nil:
parkLog.Error("无法判断园区是否隶属于当前用户。", zap.String("park id", parkId), zap.String("user id", session.Uid), zap.Error(err))
meterLog.Error("无法判断园区是否隶属于当前用户。", zap.String("park id", parkId), zap.String("user id", session.Uid), zap.Error(err))
return result.Error(http.StatusInternalServerError, err.Error())
case err == nil && !ok:
parkLog.Error("用户试图访问不属于自己的园区。", zap.String("park id", parkId), zap.String("user id", session.Uid))
meterLog.Error("用户试图访问不属于自己的园区。", zap.String("park id", parkId), zap.String("user id", session.Uid))
return result.Forbidden("您无权访问该园区。")
}
uploadFile, err := c.FormFile("data")
@ -255,10 +256,10 @@ func listAssociatedMeters(c *fiber.Ctx) error {
ok, err := repository.ParkRepository.IsParkBelongs(parkId, session.Uid)
switch {
case err != nil:
parkLog.Error("无法判断园区是否隶属于当前用户。", zap.String("park id", parkId), zap.String("user id", session.Uid), zap.Error(err))
meterLog.Error("无法判断园区是否隶属于当前用户。", zap.String("park id", parkId), zap.String("user id", session.Uid), zap.Error(err))
return result.Error(http.StatusInternalServerError, err.Error())
case err == nil && !ok:
parkLog.Error("用户试图访问不属于自己的园区。", zap.String("park id", parkId), zap.String("user id", session.Uid))
meterLog.Error("用户试图访问不属于自己的园区。", zap.String("park id", parkId), zap.String("user id", session.Uid))
return result.Forbidden("您无权访问该园区。")
}
meterId := c.Params("code")
@ -283,10 +284,10 @@ func bindAssociatedMeters(c *fiber.Ctx) error {
ok, err := repository.ParkRepository.IsParkBelongs(parkId, session.Uid)
switch {
case err != nil:
parkLog.Error("无法判断园区是否隶属于当前用户。", zap.String("park id", parkId), zap.String("user id", session.Uid), zap.Error(err))
meterLog.Error("无法判断园区是否隶属于当前用户。", zap.String("park id", parkId), zap.String("user id", session.Uid), zap.Error(err))
return result.Error(http.StatusInternalServerError, err.Error())
case err == nil && !ok:
parkLog.Error("用户试图访问不属于自己的园区。", zap.String("park id", parkId), zap.String("user id", session.Uid))
meterLog.Error("用户试图访问不属于自己的园区。", zap.String("park id", parkId), zap.String("user id", session.Uid))
return result.Forbidden("您无权访问该园区。")
}
meterId := c.Params("code")
@ -320,10 +321,10 @@ func unbindAssociatedMeters(c *fiber.Ctx) error {
ok, err := repository.ParkRepository.IsParkBelongs(parkId, session.Uid)
switch {
case err != nil:
parkLog.Error("无法判断园区是否隶属于当前用户。", zap.String("park id", parkId), zap.String("user id", session.Uid), zap.Error(err))
meterLog.Error("无法判断园区是否隶属于当前用户。", zap.String("park id", parkId), zap.String("user id", session.Uid), zap.Error(err))
return result.Error(http.StatusInternalServerError, err.Error())
case err == nil && !ok:
parkLog.Error("用户试图访问不属于自己的园区。", zap.String("park id", parkId), zap.String("user id", session.Uid))
meterLog.Error("用户试图访问不属于自己的园区。", zap.String("park id", parkId), zap.String("user id", session.Uid))
return result.Forbidden("您无权访问该园区。")
}
masterMeter := c.Params("master")
@ -356,10 +357,10 @@ func listPooledMeters(c *fiber.Ctx) error {
ok, err := repository.ParkRepository.IsParkBelongs(parkId, session.Uid)
switch {
case err != nil:
parkLog.Error("无法判断园区是否隶属于当前用户。", zap.String("park id", parkId), zap.String("user id", session.Uid), zap.Error(err))
meterLog.Error("无法判断园区是否隶属于当前用户。", zap.String("park id", parkId), zap.String("user id", session.Uid), zap.Error(err))
return result.Error(http.StatusInternalServerError, err.Error())
case err == nil && !ok:
parkLog.Error("用户试图访问不属于自己的园区。", zap.String("park id", parkId), zap.String("user id", session.Uid))
meterLog.Error("用户试图访问不属于自己的园区。", zap.String("park id", parkId), zap.String("user id", session.Uid))
return result.Forbidden("您无权访问该园区。")
}
page := c.QueryInt("page", 1)
@ -388,10 +389,10 @@ func listUnboundMeters(c *fiber.Ctx) error {
ok, err := repository.ParkRepository.IsParkBelongs(parkId, session.Uid)
switch {
case err != nil:
parkLog.Error("无法判断园区是否隶属于当前用户。", zap.String("park id", parkId), zap.String("user id", session.Uid), zap.Error(err))
meterLog.Error("无法判断园区是否隶属于当前用户。", zap.String("park id", parkId), zap.String("user id", session.Uid), zap.Error(err))
return result.Error(http.StatusInternalServerError, err.Error())
case err == nil && !ok:
parkLog.Error("用户试图访问不属于自己的园区。", zap.String("park id", parkId), zap.String("user id", session.Uid))
meterLog.Error("用户试图访问不属于自己的园区。", zap.String("park id", parkId), zap.String("user id", session.Uid))
return result.Forbidden("您无权访问该园区。")
}
keyword := c.Query("keyword")
@ -452,10 +453,10 @@ func queryMeterReadings(c *fiber.Ctx) error {
ok, err := repository.ParkRepository.IsParkBelongs(parkId, session.Uid)
switch {
case err != nil:
parkLog.Error("无法判断园区是否隶属于当前用户。", zap.String("park id", parkId), zap.String("user id", session.Uid), zap.Error(err))
meterLog.Error("无法判断园区是否隶属于当前用户。", zap.String("park id", parkId), zap.String("user id", session.Uid), zap.Error(err))
return result.Error(http.StatusInternalServerError, err.Error())
case err == nil && !ok:
parkLog.Error("用户试图访问不属于自己的园区。", zap.String("park id", parkId), zap.String("user id", session.Uid))
meterLog.Error("用户试图访问不属于自己的园区。", zap.String("park id", parkId), zap.String("user id", session.Uid))
return result.Forbidden("您无权访问该园区。")
}
keyword := tools.EmptyToNil(c.Query("keyword"))
@ -506,10 +507,10 @@ func recordMeterReading(c *fiber.Ctx) error {
ok, err := repository.ParkRepository.IsParkBelongs(parkId, session.Uid)
switch {
case err != nil:
parkLog.Error("无法判断园区是否隶属于当前用户。", zap.String("park id", parkId), zap.String("user id", session.Uid), zap.Error(err))
meterLog.Error("无法判断园区是否隶属于当前用户。", zap.String("park id", parkId), zap.String("user id", session.Uid), zap.Error(err))
return result.Error(http.StatusInternalServerError, err.Error())
case err == nil && !ok:
parkLog.Error("用户试图访问不属于自己的园区。", zap.String("park id", parkId), zap.String("user id", session.Uid))
meterLog.Error("用户试图访问不属于自己的园区。", zap.String("park id", parkId), zap.String("user id", session.Uid))
return result.Forbidden("您无权访问该园区。")
}
meterCode := c.Params("code")
@ -542,10 +543,10 @@ func updateMeterReading(c *fiber.Ctx) error {
ok, err := repository.ParkRepository.IsParkBelongs(parkId, session.Uid)
switch {
case err != nil:
parkLog.Error("无法判断园区是否隶属于当前用户。", zap.String("park id", parkId), zap.String("user id", session.Uid), zap.Error(err))
meterLog.Error("无法判断园区是否隶属于当前用户。", zap.String("park id", parkId), zap.String("user id", session.Uid), zap.Error(err))
return result.Error(http.StatusInternalServerError, err.Error())
case err == nil && !ok:
parkLog.Error("用户试图访问不属于自己的园区。", zap.String("park id", parkId), zap.String("user id", session.Uid))
meterLog.Error("用户试图访问不属于自己的园区。", zap.String("park id", parkId), zap.String("user id", session.Uid))
return result.Forbidden("您无权访问该园区。")
}
meterCode := c.Params("code")
@ -616,5 +617,32 @@ func downloadMeterReadingsTemplate(c *fiber.Ctx) error {
// 处理上传的抄表记录文件
func uploadMeterReadings(c *fiber.Ctx) error {
return nil
parkId := c.Params("pid")
meterLog.Info("从Excel文件中导入抄表档案", zap.String("park id", parkId))
result := response.NewResult(c)
session, err := _retreiveSession(c)
if err != nil {
meterLog.Error("无法从Excel文件中导入抄表档案无法获取当前用户会话", zap.Error(err))
return result.Unauthorized(err.Error())
}
ok, err := repository.ParkRepository.IsParkBelongs(parkId, session.Uid)
switch {
case err != nil:
meterLog.Error("无法判断园区是否隶属于当前用户。", zap.String("park id", parkId), zap.String("user id", session.Uid), zap.Error(err))
return result.Error(http.StatusInternalServerError, err.Error())
case err == nil && !ok:
meterLog.Error("用户试图访问不属于自己的园区。", zap.String("park id", parkId), zap.String("user id", session.Uid))
return result.Forbidden("您无权访问该园区。")
}
uploadFile, err := c.FormFile("data")
if err != nil {
meterLog.Error("无法从Excel文件中导入抄表档案无法获取上传的文件", zap.Error(err))
return result.NotAccept(fmt.Sprintf("没有接收到上传的文件,%s", err.Error()))
}
errs, err := service.MeterService.BatchImportReadings(parkId, uploadFile)
if err != nil {
meterLog.Error("无法从Excel文件中导入抄表档案无法导入抄表档案", zap.Error(err))
return result.Json(fiber.StatusNotAcceptable, "上传的抄表档案存在错误。", fiber.Map{"errors": errs})
}
return result.Success("表计档案已经导入完成。", fiber.Map{"errors": errs})
}

View File

@ -686,28 +686,115 @@ func (ms _MeterService) RecordReading(pid, meterCode string, form *vo.MeterReadi
return nil
}
// 获取指定园区的全部待抄表计列表并将其输出到Excel文件模板中提供生成文件的二进制内容
func (ms _MeterService) GenerateParkMeterReadingTemplate(pid string, meters []*model.SimpleMeterDocument) ([]byte, error) {
// 步骤1复制公用模板文件到临时文件夹文件以园区ID命名
// 步骤2打开复制后的园区文件
// 步骤3获取园区中需要抄表的表计列表
// 步骤4循环表计列表将表计信息写入到Excel文件中
// 步骤5将生成的临时文件的具体二进制内容返回给调用者
return nil, nil
}
// 处理上传的Excel格式的表计抄表记录所有满足审查条件的记录都将被保存到数据库中。
// 无论峰谷表计还是普通表计,只要抄表记录中不存在峰谷数据,都将自动使用平段配平。
func (ms _MeterService) BatchImportReadings(pid string, uploadContent []byte) error {
func (ms _MeterService) BatchImportReadings(pid string, file *multipart.FileHeader) ([]excel.ExcelAnalysisError, error) {
ms.log.Info("处理上传的Excel格式的表计抄表记录", zap.String("park id", pid))
ctx, cancel := global.TimeoutContext()
defer cancel()
// 步骤1将解析到的数据转换成创建表单数据
activeFile, err := file.Open()
if err != nil {
ms.log.Error("无法打开上传的抄表数据文件。", zap.Error(err))
return make([]excel.ExcelAnalysisError, 0), fmt.Errorf("无法打开上传的抄表数据文件,%w", err)
}
analyzer, err := excel.NewMeterReadingsExcelAnalyzer(activeFile)
if err != nil {
ms.log.Error("无法根据上传的 Excel 文件创建表计抄表数据解析器。", zap.Error(err))
return make([]excel.ExcelAnalysisError, 0), fmt.Errorf("无法根据上传的 Excel 文件创建表计抄表数据解析器,%w", err)
}
records, errs := analyzer.Analysis(*new(model.ReadingImportRow))
if len(errs) > 0 {
ms.log.Error("表计抄表数据解析器在解析上传的 Excel 文件时发生错误。", zap.Int("error count", len(errs)))
return errs, fmt.Errorf("表计抄表数据解析器在解析上传的 Excel 文件时发生错误。")
}
ms.log.Debug("已经解析到的上传数据", zap.Any("records", records))
// 步骤2对目前已经解析到的数据进行合法性检测检测包括表计编号在同一抄表时间是否重复
var collectRecords = make(map[types.DateTime][]string, 0)
for _, record := range records {
if _, ok := collectRecords[record.ReadAt]; !ok {
collectRecords[record.ReadAt] = []string{}
}
collectRecords[record.ReadAt] = append(collectRecords[record.ReadAt], record.Code)
}
for readAt, codes := range collectRecords {
valCounts := lo.CountValues(codes)
for code, count := range valCounts {
if count > 1 {
errs = append(errs, excel.ExcelAnalysisError{
Row: 0,
Col: 0,
Err: excel.AnalysisError{
Err: fmt.Errorf("表计编号 %s 在同一抄表时间 %s 内重复出现 %d 次", code, readAt.ToString(), count),
},
})
}
}
}
if len(errs) > 0 {
ms.log.Error("表计抄表数据解析器在解析上传的 Excel 文件时发生错误。", zap.Int("error count", len(errs)))
return errs, fmt.Errorf("表计抄表数据解析器在解析上传的 Excel 文件时发生错误。")
}
// 步骤3从数据库中获取当前园区中已有的表计编号
meters, err := repository.MeterRepository.AllMeters(pid)
if err != nil {
ms.log.Error("无法从数据库中获取当前园区中已有的表计编号。", zap.Error(err))
return make([]excel.ExcelAnalysisError, 0), fmt.Errorf("无法从数据库中获取当前园区中已有的表计编号,%w", err)
}
// 步骤4.0:启动数据库事务
tx, err := global.DB.Begin(ctx)
if err != nil {
ms.log.Error("无法启动数据库事务。", zap.Error(err))
return make([]excel.ExcelAnalysisError, 0), fmt.Errorf("无法启动数据库事务,%w", err)
}
// 步骤4.1:对比检查数据库中的表计编号与上传文件中的表计编号是否存在差异。非差异内容将直接保存
// 步骤4.1.1:抄表的表计在数据库中已经存在,可以直接保存起数据。
// 步骤4.1.2:抄表表计在数据库中不存在,需要将其记录进入错误。
for row, record := range records {
meter, exists := lo.Find(meters, func(element *model.MeterDetail) bool {
return element.Code == record.Code
})
if exists {
// 步骤4.1.1:抄表的表计在数据库中已经存在,可以直接保存起数据。
_, err := repository.MeterRepository.RecordReading(tx, ctx, pid, record.Code, meter.MeterType, meter.Ratio, &vo.MeterReadingForm{
ReadAt: lo.ToPtr(record.ReadAt),
Overall: record.Overall,
Critical: record.Critical.Decimal,
Peak: record.Peak.Decimal,
Flat: record.Overall.Sub(record.Peak.Decimal).Sub(record.Valley.Decimal).Sub(record.Critical.Decimal),
Valley: record.Valley.Decimal,
})
if err != nil {
ms.log.Error("无法在数据插入阶段保存抄表信息。", zap.String("meter code", record.Code), zap.Error(err))
errs = append(errs, excel.ExcelAnalysisError{
Row: row + 1,
Col: 0,
Err: excel.AnalysisError{
Err: fmt.Errorf("无法在数据插入阶段保存抄表信息,%w", err),
},
})
}
} else {
// 步骤4.1.2:抄表表计在数据库中不存在,需要将其记录进入错误。
errs = append(errs, excel.ExcelAnalysisError{
Row: row + 1,
Col: 0,
Err: excel.AnalysisError{
Err: fmt.Errorf("表计编号 %s 在系统中不存在", record.Code),
},
})
}
}
// 步骤4.3:如果批处理过程中存在错误,撤销全部导入动作。
if len(errs) > 0 {
ms.log.Error("表计抄表数据解析器在解析上传的 Excel 文件时发生错误。", zap.Int("error count", len(errs)))
tx.Rollback(ctx)
return errs, fmt.Errorf("表计抄表数据解析器在解析上传的 Excel 文件时发生错误。")
}
// 步骤5执行事务更新数据库获取完成更改的行数。
return nil
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
}