package service import ( "electricity_bill_calc/global" "electricity_bill_calc/logger" "electricity_bill_calc/model" "electricity_bill_calc/repository" "electricity_bill_calc/vo" "fmt" "github.com/samber/lo" "go.uber.org/zap" ) type _TenementService struct { log *zap.Logger } var TenementService = _TenementService{ log: logger.Named("Service", "Tenement"), } // 列出指定商户下的全部计量表计,不包含公摊表计 func (ts _TenementService) ListMeter(pid, tid string) ([]*model.MeterDetail, error) { ts.log.Info("列出指定商户下的全部表计", zap.String("Park", pid), zap.String("Tenement", tid)) meterCodes, err := repository.TenementRepository.ListMeterCodesBelongsTo(pid, tid) if err != nil { ts.log.Error("列出指定商户下的全部表计失败,未能获取属于商户的表计编号", zap.Error(err)) return make([]*model.MeterDetail, 0), err } meters, err := repository.MeterRepository.ListMetersByIDs(pid, meterCodes) if err != nil { ts.log.Error("列出指定商户下的全部表计失败,未能获取表计编号对应的表计详细信息", zap.Error(err)) return make([]*model.MeterDetail, 0), err } return meters, nil } // 增加一个新的商户 func (ts _TenementService) CreateTenementRecord(pid string, creationForm *vo.TenementCreationForm) error { ts.log.Info("增加一个新的商户", zap.String("Park", pid), zap.Any("Form", creationForm)) ctx, cancel := global.TimeoutContext() defer cancel() tx, err := global.DB.Begin(ctx) if err != nil { ts.log.Error("增加一个新商户失败的,未能启动数据库事务", zap.Error(err)) return fmt.Errorf("未能启动数据库事务,%w", err) } err = repository.TenementRepository.AddTenement(tx, ctx, pid, creationForm) if err != nil { ts.log.Error("增加一个新商户失败的,未能增加商户记录", zap.Error(err)) tx.Rollback(ctx) return fmt.Errorf("未能增加商户记录,%w", err) } err = tx.Commit(ctx) if err != nil { ts.log.Error("增加一个新商户失败的,未能提交数据库事务", zap.Error(err)) tx.Rollback(ctx) return fmt.Errorf("未能提交数据库事务,%w", err) } return nil } // 向商户绑定一个新表计 func (ts _TenementService) BindMeter(pid, tid, meterCode string, reading *vo.MeterReadingForm) error { ts.log.Info("向商户绑定一个新表计", zap.String("Park", pid), zap.String("Tenement", tid), zap.String("Meter", meterCode)) ctx, cancel := global.TimeoutContext() defer cancel() tx, err := global.DB.Begin(ctx) if err != nil { ts.log.Error("向商户绑定一个新表计失败,未能启动数据库事务", zap.Error(err)) return fmt.Errorf("未能启动数据库事务,%w", err) } meterDetail, err := repository.MeterRepository.FetchMeterDetail(pid, meterCode) if err != nil { ts.log.Error("向商户绑定一个新表计失败,未能获取表计详细信息", zap.Error(err)) tx.Rollback(ctx) return fmt.Errorf("未能获取表计详细信息,%w", err) } err = repository.TenementRepository.BindMeter(tx, ctx, pid, tid, meterCode, reading) if err != nil { ts.log.Error("向商户绑定一个新表计失败,未能绑定表计", zap.Error(err)) tx.Rollback(ctx) return fmt.Errorf("未能绑定表计,%w", err) } ok, err := repository.MeterRepository.RecordReading(tx, ctx, pid, meterCode, meterDetail.MeterType, meterDetail.Ratio, reading) if err != nil { ts.log.Error("向商户绑定一个新表计失败,记录表计读数出现错误", zap.Error(err)) tx.Rollback(ctx) return fmt.Errorf("记录表计读数出现错误,%w", err) } if !ok { ts.log.Error("向商户绑定一个新表计失败,记录表计读数失败") tx.Rollback(ctx) return fmt.Errorf("记录表计读数失败") } err = tx.Commit(ctx) if err != nil { ts.log.Error("向商户绑定一个新表计失败,未能提交数据库事务", zap.Error(err)) tx.Rollback(ctx) return fmt.Errorf("未能提交数据库事务,%w", err) } return nil } // 解除商户与指定表计的绑定 func (ts _TenementService) UnbindMeter(pid, tid, meterCode string, reading *vo.MeterReadingForm) error { ts.log.Info("解除商户与指定表计的绑定", zap.String("Park", pid), zap.String("Tenement", tid), zap.String("Meter", meterCode)) ctx, cancel := global.TimeoutContext() defer cancel() tx, err := global.DB.Begin(ctx) if err != nil { ts.log.Error("解除商户与指定表计的绑定失败,未能启动数据库事务", zap.Error(err)) return fmt.Errorf("未能启动数据库事务,%w", err) } meterDetail, err := repository.MeterRepository.FetchMeterDetail(pid, meterCode) if err != nil { ts.log.Error("解除商户与指定表计的绑定失败,未能获取表计详细信息", zap.Error(err)) tx.Rollback(ctx) return fmt.Errorf("未能获取表计详细信息,%w", err) } err = repository.TenementRepository.UnbindMeter(tx, ctx, pid, tid, meterCode) if err != nil { ts.log.Error("解除商户与指定表计的绑定失败,未能解除绑定", zap.Error(err)) tx.Rollback(ctx) return fmt.Errorf("未能解除绑定,%w", err) } ok, err := repository.MeterRepository.RecordReading(tx, ctx, pid, meterCode, meterDetail.MeterType, meterDetail.Ratio, reading) if err != nil { ts.log.Error("解除商户与指定表计的绑定失败,记录表计读数出现错误", zap.Error(err)) tx.Rollback(ctx) return fmt.Errorf("记录表计读数出现错误,%w", err) } if !ok { ts.log.Error("解除商户与指定表计的绑定失败,记录表计读数失败") tx.Rollback(ctx) return fmt.Errorf("记录表计读数失败") } err = tx.Commit(ctx) if err != nil { ts.log.Error("解除商户与指定表计的绑定失败,未能提交数据库事务", zap.Error(err)) tx.Rollback(ctx) return fmt.Errorf("未能提交数据库事务,%w", err) } return nil } // 迁出指定商户 func (ts _TenementService) MoveOutTenement(pid, tid string, reading []*vo.MeterReadingFormWithCode) error { ts.log.Info("迁出指定商户", zap.String("Park", pid), zap.String("Tenement", tid)) ctx, cancel := global.TimeoutContext() defer cancel() tx, err := global.DB.Begin(ctx) if err != nil { ts.log.Error("迁出指定商户失败,未能启动数据库事务", zap.Error(err)) return fmt.Errorf("未能启动数据库事务,%w", err) } meterCodes, err := repository.TenementRepository.ListMeterCodesBelongsTo(pid, tid) if err != nil { ts.log.Error("迁出指定商户失败,未能获取属于商户的表计编号", zap.Error(err)) tx.Rollback(ctx) return fmt.Errorf("未能获取属于商户的表计编号,%w", err) } meters, err := repository.MeterRepository.ListMetersByIDs(pid, meterCodes) if err != nil { ts.log.Error("迁出指定商户失败,未能获取表涉及计编号对应的表计详细信息", zap.Error(err)) tx.Rollback(ctx) return fmt.Errorf("未能获取涉及表计编号对应的表计详细信息,%w", err) } for _, meterCode := range meterCodes { meterDetail, exists := lo.Find(meters, func(m *model.MeterDetail) bool { return m.Code == meterCode }) if !exists { ts.log.Error("迁出指定商户失败,找不到指定表计的详细信息", zap.String("Meter", meterCode)) tx.Rollback(ctx) return fmt.Errorf("找不到指定表计[%s]的详细信息,%w", meterCode, err) } if meterDetail.MeterType != model.METER_INSTALLATION_TENEMENT { ts.log.Error("迁出指定商户失败,需要解绑的表计不是商户表计", zap.String("Meter", meterCode)) tx.Rollback(ctx) return fmt.Errorf("需要解绑的表计[%s]不是商户表计,%w", meterCode, err) } reading, exists := lo.Find(reading, func(r *vo.MeterReadingFormWithCode) bool { return r.Code == meterCode }) if !exists { ts.log.Error("迁出指定商户失败,找不到指定表计的抄表信息", zap.String("Meter", meterCode)) tx.Rollback(ctx) return fmt.Errorf("找不到指定表计[%s]的抄表信息,%w", meterCode, err) } if !reading.Validate() { ts.log.Error("迁出指定商户失败,表计读数不能正确配平,尖锋电量、峰电量、谷电量之和超过总电量。", zap.String("Meter", meterCode)) tx.Rollback(ctx) return fmt.Errorf("表计[%s]读数不能正确配平,尖锋电量、峰电量、谷电量之和超过总电量。", meterCode) } err = repository.TenementRepository.UnbindMeter(tx, ctx, pid, tid, meterCode) if err != nil { ts.log.Error("迁出指定商户失败,未能解除表计绑定", zap.Error(err)) tx.Rollback(ctx) return fmt.Errorf("未能解除表计[%s]绑定,%w", meterCode, err) } ok, err := repository.MeterRepository.RecordReading(tx, ctx, pid, meterCode, meterDetail.MeterType, meterDetail.Ratio, &reading.MeterReadingForm) if err != nil { ts.log.Error("迁出指定商户失败,记录表计抄表信息出现错误", zap.String("Meter", meterCode), zap.Error(err)) tx.Rollback(ctx) return fmt.Errorf("记录表计[%s]抄表信息出现错误,%w", meterCode, err) } if !ok { ts.log.Error("迁出指定商户失败,记录表计抄表数据失败", zap.String("Meter", meterCode)) tx.Rollback(ctx) return fmt.Errorf("记录表计[%s]抄表数据失败", meterCode) } } err = repository.TenementRepository.MoveOut(tx, ctx, pid, tid) if err != nil { ts.log.Error("迁出指定商户失败,未能迁出指定商户", zap.Error(err)) tx.Rollback(ctx) return fmt.Errorf("未能迁出指定商户,%w", err) } err = tx.Commit(ctx) if err != nil { ts.log.Error("迁出指定商户失败,未能提交数据库事务", zap.Error(err)) tx.Rollback(ctx) return fmt.Errorf("未能提交数据库事务,%w", err) } return nil }