forked from free-lancers/electricity_bill_calc_service
		
	[计算相关]获取所有的物业表计,然后对所有的物业表计电量进行计算。(完成)
This commit is contained in:
		| @@ -1,13 +0,0 @@ | ||||
| ## fiber | ||||
| #### fiber实例 | ||||
| - app(是fiber创建的实例通常用app表示,其中有可选配置选项) | ||||
|     - BodyLimit 设置请求正文允许的最大大小(默认为4 * 1024 * 1024) | ||||
|     - EnablePrintRoutes 不打印框架自带日志(默认false) | ||||
|     - EnableTrustedProxyCheck 禁用受信代理(默认false) | ||||
|     - Prefork 预处理配置(默认false) | ||||
|     - ErrorHandler 全局错误处理 (默认false) | ||||
|     - JSONEncoder json编码 (默认json.Marshal) | ||||
|     - JSONDecoder json解码  (默认json.Unmarshal) | ||||
|     - 。。。。。。。。(还有很多配置) | ||||
| - Use(中间件设置,一个或者多个) | ||||
| - Group(类似于gin框架中的路由分组) | ||||
| @@ -3,6 +3,7 @@ package calculate | ||||
| import ( | ||||
| 	"electricity_bill_calc/model" | ||||
| 	"electricity_bill_calc/types" | ||||
| 	"fmt" | ||||
|  | ||||
| 	"github.com/shopspring/decimal" | ||||
| ) | ||||
| @@ -42,6 +43,11 @@ type Meter struct { | ||||
| 	Poolings                []*Pooling | ||||
| } | ||||
|  | ||||
| type PrimaryTenementStatistics struct { | ||||
| 	Tenement model.Tenement | ||||
| 	Meters   []Meter | ||||
| } | ||||
|  | ||||
| type TenementCharge struct { | ||||
| 	Tenement     string | ||||
| 	Overall      model.ConsumptionUnit | ||||
| @@ -90,3 +96,118 @@ type PoolingSummary struct { | ||||
| 	OverallAmount     decimal.Decimal | ||||
| 	PoolingProportion decimal.Decimal | ||||
| } | ||||
|  | ||||
| func FromReportSummary(summary *model.ReportSummary, pricingMode *model.ReportIndex) Summary { | ||||
| 	var parkPrice float64 | ||||
| 	switch pricingMode.PricePolicy { | ||||
| 	case model.PRICING_POLICY_CONSUMPTION: | ||||
| 		parkPrice = summary.ConsumptionFee.Decimal.InexactFloat64() / summary.Overall.Amount.InexactFloat64() | ||||
| 	case model.PRICING_POLICY_ALL: | ||||
| 		parkPrice = summary.Overall.Fee.InexactFloat64() / summary.Overall.Amount.InexactFloat64() | ||||
| 	default: | ||||
| 		fmt.Println("无法识别类型") | ||||
| 	} | ||||
|  | ||||
| 	flatAmount := summary.Overall.Amount.InexactFloat64() - | ||||
| 		summary.Critical.Amount.InexactFloat64() - | ||||
| 		summary.Peak.Amount.InexactFloat64() - | ||||
| 		summary.Valley.Amount.InexactFloat64() | ||||
|  | ||||
| 	flatFee := summary.Overall.Amount.InexactFloat64() - | ||||
| 		summary.Critical.Fee.InexactFloat64() - | ||||
| 		summary.Peak.Fee.InexactFloat64() - | ||||
| 		summary.Valley.Fee.InexactFloat64() | ||||
|  | ||||
| 	var OverallPrice float64 | ||||
| 	if summary.Overall.Amount.GreaterThan(decimal.Zero) { | ||||
| 		OverallPrice = parkPrice | ||||
| 	} else { | ||||
| 		OverallPrice = decimal.Zero.InexactFloat64() | ||||
| 	} | ||||
|  | ||||
| 	var CriticalPrice float64 | ||||
| 	if summary.Critical.Amount.GreaterThan(decimal.Zero) { | ||||
| 		CriticalPrice = summary.Critical.Fee.InexactFloat64() / summary.Critical.Amount.InexactFloat64() | ||||
| 	} else { | ||||
| 		CriticalPrice = decimal.Zero.InexactFloat64() | ||||
| 	} | ||||
|  | ||||
| 	var PeakPrice float64 | ||||
| 	if summary.Peak.Amount.GreaterThan(decimal.Zero) { | ||||
| 		PeakPrice = summary.Peak.Fee.InexactFloat64() / summary.Peak.Amount.InexactFloat64() | ||||
| 	} else { | ||||
| 		PeakPrice = decimal.Zero.InexactFloat64() | ||||
| 	} | ||||
|  | ||||
| 	var FlatPrice float64 | ||||
| 	if decimal.NewFromFloat(flatAmount).GreaterThan(decimal.Zero) { | ||||
| 		FlatPrice = flatFee / flatAmount | ||||
| 	} else { | ||||
| 		FlatPrice = decimal.Zero.InexactFloat64() | ||||
| 	} | ||||
|  | ||||
| 	var ValleyPrice float64 | ||||
| 	if summary.Valley.Amount.GreaterThan(decimal.Zero) { | ||||
| 		ValleyPrice = summary.Valley.Fee.InexactFloat64() / summary.Valley.Amount.InexactFloat64() | ||||
| 	} else { | ||||
| 		ValleyPrice = decimal.Zero.InexactFloat64() | ||||
| 	} | ||||
|  | ||||
| 	var LossDilutedPrice float64 | ||||
| 	if summary.Overall.Amount.GreaterThan(decimal.Zero) { | ||||
| 		LossDilutedPrice = parkPrice | ||||
| 	} else { | ||||
| 		LossDilutedPrice = decimal.Zero.InexactFloat64() | ||||
| 	} | ||||
|  | ||||
| 	_ = parkPrice | ||||
|  | ||||
| 	return Summary{ | ||||
| 		ReportId:    summary.ReportId, | ||||
| 		OverallArea: decimal.Zero, | ||||
| 		Overall: model.ConsumptionUnit{ | ||||
| 			Amount:     summary.Overall.Amount, | ||||
| 			Fee:        summary.Overall.Fee, | ||||
| 			Price:      decimal.NewFromFloat(OverallPrice), | ||||
| 			Proportion: decimal.NewFromFloat(1.0), | ||||
| 		}, | ||||
| 		ConsumptionFee: summary.ConsumptionFee.Decimal, | ||||
| 		Critical: model.ConsumptionUnit{ | ||||
| 			Amount:     summary.Critical.Amount, | ||||
| 			Fee:        summary.Critical.Fee, | ||||
| 			Price:      decimal.NewFromFloat(CriticalPrice), | ||||
| 			Proportion: decimal.NewFromFloat(summary.Critical.Amount.InexactFloat64() / summary.Overall.Amount.InexactFloat64()), | ||||
| 		}, | ||||
| 		Peak: model.ConsumptionUnit{ | ||||
| 			Amount:     summary.Peak.Amount, | ||||
| 			Fee:        summary.Peak.Fee, | ||||
| 			Price:      decimal.NewFromFloat(PeakPrice), | ||||
| 			Proportion: decimal.NewFromFloat(summary.Peak.Amount.InexactFloat64() / summary.Overall.Amount.InexactFloat64()), | ||||
| 		}, | ||||
| 		Flat: model.ConsumptionUnit{ | ||||
| 			Amount:     decimal.NewFromFloat(flatAmount), | ||||
| 			Fee:        decimal.NewFromFloat(flatFee), | ||||
| 			Price:      decimal.NewFromFloat(FlatPrice), | ||||
| 			Proportion: decimal.NewFromFloat(flatAmount / summary.Overall.Amount.InexactFloat64()), | ||||
| 		}, | ||||
| 		Valley: model.ConsumptionUnit{ | ||||
| 			Amount:     summary.Valley.Amount, | ||||
| 			Fee:        summary.Valley.Fee, | ||||
| 			Price:      decimal.NewFromFloat(ValleyPrice), | ||||
| 			Proportion: decimal.NewFromFloat(summary.Valley.Amount.InexactFloat64() / summary.Overall.Amount.InexactFloat64()), | ||||
| 		}, | ||||
| 		Loss:                         decimal.Zero, | ||||
| 		LossFee:                      decimal.Zero, | ||||
| 		LossProportion:               decimal.Zero, | ||||
| 		AuthoizeLoss:                 model.ConsumptionUnit{}, | ||||
| 		BasicFee:                     summary.BasicFee, | ||||
| 		BasicPooledPriceConsumption:  decimal.Zero, | ||||
| 		BasicPooledPriceArea:         decimal.Zero, | ||||
| 		AdjustFee:                    summary.AdjustFee, | ||||
| 		AdjustPooledPriceConsumption: decimal.Zero, | ||||
| 		AdjustPooledPriceArea:        decimal.Zero, | ||||
| 		LossDilutedPrice:             decimal.NewFromFloat(LossDilutedPrice), | ||||
| 		TotalConsumption:             decimal.Zero, | ||||
| 		FinalDilutedOverall:          decimal.Zero, | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -21,3 +21,13 @@ type Tenement struct { | ||||
| 	LastModifiedAt types.DateTime  `json:"lastModifiedAt" db:"last_modified_at"` | ||||
| 	DeletedAt      *types.DateTime `json:"deletedAt" db:"deleted_at"` | ||||
| } | ||||
|  | ||||
| type TenementMeter struct { | ||||
| 	ParkId          string         `db:"park_id"` | ||||
| 	TenementId      string         `db:"tenement_id"` | ||||
| 	MeterId         string         `db:"meter_id"` | ||||
| 	ForeignRelation bool           `db:"foreign_relation"` | ||||
| 	AssociatedAt    types.DateTime `db:"associated_at"` | ||||
| 	DisassociatedAt types.DateTime `db:"disassociated_at"` | ||||
| 	SynchronizedAt  types.DateTime `db:"synchronized_at"` | ||||
| } | ||||
|   | ||||
| @@ -5,6 +5,7 @@ import ( | ||||
| 	"electricity_bill_calc/logger" | ||||
| 	"electricity_bill_calc/model" | ||||
| 	"electricity_bill_calc/types" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/doug-martin/goqu/v9" | ||||
| 	_ "github.com/doug-martin/goqu/v9/dialect/postgres" | ||||
| @@ -68,3 +69,173 @@ func (cr _CalculateRepository) UpdateReportTaskStatus(rid string, status int16, | ||||
| 	} | ||||
| 	return res.RowsAffected() > 0, nil | ||||
| } | ||||
|  | ||||
| //获取当前园区中所有公摊表计与商户表计之间的关联关系,包括已经解除的 | ||||
| func (cr _CalculateRepository) GetAllPoolingMeterRelations(pid string, revokedAfter time.Time) ([]model.MeterRelation, error) { | ||||
| 	cr.log.Info("获取当前园区中所有公摊表计与商户表计之间的关联关系,包括已经解除的", zap.String("pid", pid), zap.Time("revokedAfter", revokedAfter)) | ||||
|  | ||||
| 	ctx, cancel := global.TimeoutContext() | ||||
| 	defer cancel() | ||||
| 	relationsSql, relationsArgs, _ := cr.ds. | ||||
| 		From(goqu.T("meter_relations")). | ||||
| 		Where(goqu.I("park_id").Eq(pid)). | ||||
| 		Where(goqu.Or( | ||||
| 			goqu.I("revoked_at").IsNull(), | ||||
| 			goqu.I("revoked_at").Gte(revokedAfter), | ||||
| 		)).ToSQL() | ||||
|  | ||||
| 	var meterRelation []model.MeterRelation | ||||
|  | ||||
| 	err := pgxscan.Select(ctx, global.DB, meterRelation, relationsSql, relationsArgs...) | ||||
| 	if err != nil { | ||||
| 		cr.log.Error("获取当前园区中所有公摊表计与商户表计之间的关联关系,包括已经解除的出错", zap.Error(err)) | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return meterRelation, nil | ||||
| } | ||||
|  | ||||
| //获取当前园区中所有的商户与表计的关联关系,包括已经解除的 | ||||
| func (cr _CalculateRepository) GetAllTenementMeterRelations(pid string, associatedBefore time.Time, disassociatedAfter time.Time) ([]model.TenementMeter, error) { | ||||
| 	cr.log.Info("获取当前园区中所有的商户与表计的关联关系,包括已经解除的", zap.String("pid", pid), zap.Time("associatedBefore", associatedBefore), zap.Time("disassociatedAfter", disassociatedAfter)) | ||||
| 	ctx, cancel := global.TimeoutContext() | ||||
| 	defer cancel() | ||||
|  | ||||
| 	relationsQuerySql, relationsQueryArgs, _ := cr.ds. | ||||
| 		From(goqu.T("tenement_meter")). | ||||
| 		Where(goqu.I("park_id").Eq(pid)). | ||||
| 		Where(goqu.And( | ||||
| 			goqu.I("associated_at").IsNull(), | ||||
| 			goqu.I("associated_at").Lte(associatedBefore), | ||||
| 		)). | ||||
| 		Where(goqu.And( | ||||
| 			goqu.I("associated_at").IsNull(), | ||||
| 			goqu.I("associated_at").Gte(disassociatedAfter), | ||||
| 		)).ToSQL() | ||||
|  | ||||
| 	var tenementMeter []model.TenementMeter | ||||
|  | ||||
| 	err := pgxscan.Select(ctx, global.DB, tenementMeter, relationsQuerySql, relationsQueryArgs...) | ||||
| 	if err != nil { | ||||
| 		cr.log.Error("获取当前园区中所有的商户与表计的关联关系,包括已经解除的", zap.Error(err)) | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return tenementMeter, nil | ||||
|  | ||||
| } | ||||
|  | ||||
| //获取指定报表中所有涉及到的指定类型表计在核算时间段内的所有读数数据 | ||||
| func (cr _CalculateRepository) GetMeterReadings(rid string, meterType int16) ([]model.MeterReading, error) { | ||||
| 	cr.log.Info("获取指定报表中所有涉及到的指定类型表计在核算时间段内的所有读数数据", zap.String("rid", rid), zap.Int16("meterType", meterType)) | ||||
|  | ||||
| 	ctx, cancel := global.TimeoutContext() | ||||
| 	defer cancel() | ||||
|  | ||||
| 	readingsQuerySql, readingsQueryArgs, _ := cr.ds. | ||||
| 		From(goqu.T("meter_reading").As(goqu.I("mr"))). | ||||
| 		Join( | ||||
| 			goqu.T("report").As("r"), | ||||
| 			goqu.On(goqu.I("r.park_id").Eq(goqu.I("mr.park_id"))), | ||||
| 		). | ||||
| 		Where( | ||||
| 			goqu.I("r.id").Eq(rid), | ||||
| 			goqu.I("mr.meter_type").Eq(meterType), | ||||
| 			// TODO:2023.08.02 此方法出错优先查看是否这里出问题 | ||||
| 			goqu.I("mr.read_at::date <@ r.period"), | ||||
| 		). | ||||
| 		Order(goqu.I("mr.read_at").Asc()).Select(goqu.I("mr.*")).ToSQL() | ||||
|  | ||||
| 	var readings []model.MeterReading | ||||
|  | ||||
| 	err := pgxscan.Select(ctx, global.DB, readings, readingsQuerySql, readingsQueryArgs...) | ||||
| 	if err != nil { | ||||
| 		cr.log.Error("获取指定报表中所有涉及到的指定类型表计在核算时间段内的所有读数数据出错", zap.Error(err)) | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return readings, nil | ||||
| } | ||||
|  | ||||
| // 获取指定报表中所有涉及到的表计在核算起始日期前的最后一次读数 | ||||
| func (cr _CalculateRepository) GetLastPeriodReadings(rid string, meterType int16) ([]model.MeterReading, error) { | ||||
| 	cr.log.Info("获取指定报表中所有涉及到的表计在核算起始日期前的最后一次读数", zap.String("rid", rid), zap.Int16("meterType", meterType)) | ||||
|  | ||||
| 	ctx, cancel := global.TimeoutContext() | ||||
| 	defer cancel() | ||||
|  | ||||
| 	readingsSql, readingsArgs, _ := cr.ds.From(goqu.T("meter_reading").As("mr")). | ||||
| 		Select( | ||||
| 			goqu.MAX("mr.read_at").As("read_at"), | ||||
| 			goqu.I("mr.park_id"), | ||||
| 			goqu.I("mr.meter_id"), | ||||
| 			goqu.I("mr.meter_type"), | ||||
| 			goqu.I("mr.ratio"), | ||||
| 			goqu.I("mr.overall"), | ||||
| 			goqu.I("mr.critical"), | ||||
| 			goqu.I("mr.peak"), | ||||
| 			goqu.I("mr.flat"), | ||||
| 			goqu.I("mr.valley"), | ||||
| 		). | ||||
| 		Join( | ||||
| 			goqu.T("report").As("r"), | ||||
| 			goqu.On(goqu.I("r.park_id").Eq(goqu.I("mr.park_id"))), | ||||
| 		). | ||||
| 		Where( | ||||
| 			goqu.I("r.id").Eq(rid), | ||||
| 			goqu.I("mr.meter_type").Eq(meterType), | ||||
| 			goqu.I(" mr.read_at::date <= lower(r.period)"), | ||||
| 		). | ||||
| 		GroupBy( | ||||
| 			goqu.I("mr.park_id"), | ||||
| 			goqu.I("mr.meter_id"), | ||||
| 			goqu.I("mr.meter_type"), | ||||
| 			goqu.I("mr.ratio"), | ||||
| 			goqu.I("mr.overall"), | ||||
| 			goqu.I("mr.critical"), | ||||
| 			goqu.I("mr.peak"), | ||||
| 			goqu.I("mr.flat"), | ||||
| 			goqu.I("mr.valley"), | ||||
| 			goqu.I("r.period"), | ||||
| 		).ToSQL() | ||||
|  | ||||
| 	var readings []model.MeterReading | ||||
| 	err := pgxscan.Select(ctx, global.DB, readings, readingsSql, readingsArgs...) | ||||
| 	if err != nil { | ||||
| 		cr.log.Error("获取指定报表中所有涉及到的表计在核算起始日期前的最后一次读数出错", zap.Error(err)) | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return readings, nil | ||||
| } | ||||
|  | ||||
| // 取得指定报表所涉及的所有商户信息 | ||||
| func (cr _CalculateRepository) GetAllTenements(rid string) ([]model.Tenement, error) { | ||||
| 	cr.log.Info("取得指定报表所涉及的所有商户信息", zap.String("rid", rid)) | ||||
|  | ||||
| 	ctx, cancel := global.TimeoutContext() | ||||
| 	defer cancel() | ||||
|  | ||||
| 	tenementQuerySql, tenementQueryArgs, _ := cr.ds. | ||||
| 		From(goqu.T("tenement").As("t")). | ||||
| 		LeftJoin( | ||||
| 			goqu.T("park_building").As("b"), | ||||
| 			goqu.On(goqu.I("b.id").Eq(goqu.I("t.building"))), | ||||
| 		). | ||||
| 		Join( | ||||
| 			goqu.T("report").As("r"), | ||||
| 			goqu.On(goqu.I("r.park_id").Eq(goqu.I("t.park_id"))), | ||||
| 		). | ||||
| 		Select( | ||||
| 			goqu.I("t.*"), | ||||
| 			goqu.I("b.name").As("building_name"), | ||||
| 		). | ||||
| 		Where( | ||||
| 			goqu.I("r.id").Eq(rid), | ||||
| 			goqu.I("t.moved_in_at <= upper(r.period)"), | ||||
| 		).ToSQL() | ||||
|  | ||||
| 	var tenements []model.Tenement | ||||
| 	err := pgxscan.Select(ctx, global.DB, tenements, tenementQuerySql, tenementQueryArgs...) | ||||
| 	if err != nil { | ||||
| 		cr.log.Error("取得指定报表所涉及的所有商户信息出错", zap.Error(err)) | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return tenements, nil | ||||
| } | ||||
|   | ||||
| @@ -132,7 +132,7 @@ func (wd _WithdrawRepository) FindWithdraw(page uint, keyword *string) ([]model. | ||||
| 			PeriodBegin:           Begin, | ||||
| 			PeriodEnd:             End, | ||||
| 			Published:             v.Published, | ||||
| 			PublishedAt:           nil, | ||||
| 			PublishedAt:           tools.TimeToStringPtr(v.LastWithdrawAuditAt), | ||||
| 			Status:                0., | ||||
| 			Withdraw:              v.Withdraw, | ||||
| 		} | ||||
|   | ||||
							
								
								
									
										32
									
								
								service/calculate/checking.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								service/calculate/checking.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | ||||
| package calculate | ||||
| import ( | ||||
| 	"electricity_bill_calc/model" | ||||
| 	"fmt" | ||||
| 	"sync/atomic" | ||||
| ) | ||||
|  | ||||
| func CheckMeterArea(report *model.ReportIndex, meters []*model.MeterDetail) (bool, error) { | ||||
| 	anyAreaOptions := report.BasisPooled == model.POOLING_MODE_AREA || | ||||
| 		report.AdjustPooled == model.POOLING_MODE_AREA || | ||||
| 		report.PublicPooled == model.POOLING_MODE_AREA || | ||||
| 		report.LossPooled == model.POOLING_MODE_AREA | ||||
|  | ||||
| 	if anyAreaOptions { | ||||
| 		var meterWithoutArea int32 | ||||
|  | ||||
| 		for _, m := range meters { | ||||
| 			if (m.MeterType ==  model.METER_INSTALLATION_TENEMENT || m.MeterType ==  model.METER_INSTALLATION_POOLING) && | ||||
| 				m.Area == nil { | ||||
| 				atomic.AddInt32(&meterWithoutArea, 1) | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		if meterWithoutArea != 0 { | ||||
| 			return false, fmt.Errorf("园区中有 %d 个表计没有设置面积,无法进行按面积摊薄。", meterWithoutArea) | ||||
| 		} | ||||
|  | ||||
| 		return true, nil | ||||
| 	} | ||||
|  | ||||
| 	return false, nil | ||||
| } | ||||
							
								
								
									
										37
									
								
								service/calculate/park.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								service/calculate/park.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,37 @@ | ||||
| package calculate | ||||
|  | ||||
| import ( | ||||
| 	"electricity_bill_calc/model" | ||||
| 	"electricity_bill_calc/model/calculate" | ||||
| 	"electricity_bill_calc/repository" | ||||
| 	"time" | ||||
| ) | ||||
|  | ||||
| func MetersParkCalculate(report model.ReportIndex, periodStart time.Time, | ||||
| 	periodEnd time.Time, meterDetail []*model.MeterDetail, | ||||
| 	summary calculate.Summary) ([]calculate.Meter, error) { | ||||
| 	parkMeterReadings, err := repository.CalculateRepository.GetMeterReadings(report.Id, model.METER_INSTALLATION_PARK) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	lastTermParkMeterReadings, err := repository.CalculateRepository.GetLastPeriodReadings(report.Id, model.METER_INSTALLATION_PARK) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	parkMeterReadings = append(parkMeterReadings, lastTermParkMeterReadings...) | ||||
|  | ||||
| 	var parkMetersReports []calculate.Meter | ||||
| 	for _, meter := range meterDetail { | ||||
| 		if meter.MeterType == model.METER_INSTALLATION_PARK { | ||||
| 			parkMetersReport, err := determinePublicMeterConsumptions(meter.Code, periodStart, periodEnd, parkMeterReadings, *meter, summary) | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
| 			parkMetersReports = append(parkMetersReports, parkMetersReport) | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
| 	return parkMetersReports, nil | ||||
| } | ||||
							
								
								
									
										111
									
								
								service/calculate/pooled.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								service/calculate/pooled.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,111 @@ | ||||
| package calculate | ||||
|  | ||||
| import ( | ||||
| 	"electricity_bill_calc/model" | ||||
| 	"electricity_bill_calc/model/calculate" | ||||
| 	"electricity_bill_calc/repository" | ||||
| 	"github.com/shopspring/decimal" | ||||
| 	"time" | ||||
| 	"unsafe" | ||||
| ) | ||||
|  | ||||
| //核算园区中的全部公摊表计的电量用量 | ||||
| func PooledMetersCalculate(report *model.ReportIndex, periodStart time.Time, | ||||
| 	periodEnd time.Time, meterDetails []*model.MeterDetail, | ||||
| 	summary calculate.Summary) ([]calculate.Meter, error) { | ||||
| 	poolingMeterReadings, err := repository.CalculateRepository.GetMeterReadings(report.Id, model.METER_INSTALLATION_POOLING) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	lastTermPoolingMeterReadings, err := repository.CalculateRepository.GetLastPeriodReadings(report.Id, model.METER_INSTALLATION_POOLING) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	poolingMeterReadings = append(poolingMeterReadings, lastTermPoolingMeterReadings...) | ||||
|  | ||||
| 	var poolingMetersReports []calculate.Meter | ||||
| 	for _, meter := range meterDetails { | ||||
| 		poolingMetersReport, err := determinePublicMeterConsumptions(meter.Code, periodStart, periodEnd, poolingMeterReadings, *meter, summary) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		poolingMetersReports = append(poolingMetersReports, poolingMetersReport) | ||||
| 	} | ||||
| 	return poolingMetersReports, nil | ||||
| } | ||||
|  | ||||
| //  确定指定非商户表计在指定时间段内的全部电量 | ||||
| func determinePublicMeterConsumptions(meterId string, periodStart time.Time, | ||||
| 	periodEnd time.Time, readings []model.MeterReading, | ||||
| 	meterDetail model.MeterDetail, summary calculate.Summary) (calculate.Meter, error) { | ||||
| 	startReading, err := DeterminePublicMeterStartReading(meterId, periodStart, meterDetail.DetachedAt.Time, readings) | ||||
| 	if err != nil { | ||||
| 		return calculate.Meter{}, err | ||||
| 	} | ||||
|  | ||||
| 	endReading, err := DeterminePublicMeterEndReading(meterId, periodEnd, meterDetail.DetachedAt.Time, readings) | ||||
| 	if err != nil { | ||||
| 		return calculate.Meter{}, err | ||||
| 	} | ||||
|  | ||||
| 	overall, err := ComputeOverall(*startReading, *endReading, summary) | ||||
| 	if err != nil { | ||||
| 		return calculate.Meter{}, err | ||||
| 	} | ||||
|  | ||||
| 	critical, err := ComputeCritical(*startReading, *endReading, summary) | ||||
| 	if err != nil { | ||||
| 		return calculate.Meter{}, err | ||||
| 	} | ||||
|  | ||||
| 	peak, err := ComputePeak(*startReading, *endReading, summary) | ||||
| 	if err != nil { | ||||
| 		return calculate.Meter{}, err | ||||
| 	} | ||||
|  | ||||
| 	flat, err := ComputeFlat(*startReading, *endReading, summary) | ||||
| 	if err != nil { | ||||
| 		return calculate.Meter{}, err | ||||
| 	} | ||||
|  | ||||
| 	valley, err := ComputeValley(*startReading, *endReading, summary) | ||||
| 	if err != nil { | ||||
| 		return calculate.Meter{}, err | ||||
| 	} | ||||
|  | ||||
| 	return calculate.Meter{ | ||||
| 		Code:        meterId, | ||||
| 		Detail:      meterDetail, | ||||
| 		CoveredArea: meterDetail.Area.Decimal, | ||||
| 		LastTermReading: (*calculate.Reading)(unsafe.Pointer(&model.Reading{ | ||||
| 			Ratio:    startReading.Ratio, | ||||
| 			Overall:  startReading.Overall, | ||||
| 			Critical: startReading.Critical, | ||||
| 			Peak:     startReading.Peak, | ||||
| 			Flat:     startReading.Flat, | ||||
| 			Valley:   startReading.Valley, | ||||
| 		})), | ||||
| 		CurrentTermReading: (*calculate.Reading)(unsafe.Pointer(&model.Reading{ | ||||
| 			Ratio:    endReading.Ratio, | ||||
| 			Overall:  endReading.Overall, | ||||
| 			Critical: endReading.Critical, | ||||
| 			Peak:     endReading.Peak, | ||||
| 			Flat:     endReading.Flat, | ||||
| 			Valley:   endReading.Valley, | ||||
| 		})), | ||||
| 		Overall:                 overall, | ||||
| 		Critical:                critical, | ||||
| 		Peak:                    peak, | ||||
| 		Flat:                    flat, | ||||
| 		Valley:                  valley, | ||||
| 		AdjustLoss:              model.ConsumptionUnit{}, | ||||
| 		PooledBasic:             model.ConsumptionUnit{}, | ||||
| 		PooledAdjust:            model.ConsumptionUnit{}, | ||||
| 		PooledLoss:              model.ConsumptionUnit{}, | ||||
| 		PooledPublic:            model.ConsumptionUnit{}, | ||||
| 		SharedPoolingProportion: decimal.Decimal{}, | ||||
| 		Poolings:                nil, | ||||
| 	}, nil | ||||
| } | ||||
							
								
								
									
										105
									
								
								service/calculate/shared.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										105
									
								
								service/calculate/shared.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,105 @@ | ||||
| package calculate | ||||
|  | ||||
| import ( | ||||
| 	"electricity_bill_calc/model" | ||||
| 	"electricity_bill_calc/types" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"time" | ||||
| ) | ||||
|  | ||||
| // 确定指定非商户表计的起始读数 | ||||
| func DeterminePublicMeterStartReading(meterId string, periodStart time.Time, | ||||
| 	attachedAt time.Time, meterReadings []model.MeterReading) (*model.MeterReading, error) { | ||||
| 	periodBeginning := types.Date{Time: periodStart}.ToBeginningOfDate() | ||||
|  | ||||
| 	if len(meterReadings) <= 0 { | ||||
| 		return nil, errors.New(fmt.Sprintf("表计的抄表记录数据不足%s", meterId)) | ||||
| 	} | ||||
|  | ||||
| 	var minReading types.DateTime | ||||
| 	for _, reading := range meterReadings { | ||||
| 		if reading.ReadAt.Before(minReading.Time) { | ||||
| 			minReading = reading.ReadAt | ||||
| 		} | ||||
| 	} | ||||
| 	startTimes := []time.Time{ | ||||
| 		minReading.Time, | ||||
| 		periodBeginning.Time, | ||||
| 		ShiftToAsiaShanghai(attachedAt), | ||||
| 	} | ||||
| 	if len(startTimes) < 0 { | ||||
| 		return nil, errors.New(fmt.Sprintf("无法确定表计 {%s} 的计量的起始时间", meterId)) | ||||
| 	} | ||||
|  | ||||
| 	var startReading []model.MeterReading | ||||
| 	for _, reading := range meterReadings { | ||||
| 		readingAt := ShiftToAsiaShanghai(reading.ReadAt.UTC()) | ||||
| 		for _, startTime := range startTimes { | ||||
| 			if reading.Meter == meterId && readingAt.After(startTime) || readingAt.Equal(startTime) { | ||||
| 				startReading = append(startReading, reading) | ||||
| 				break | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if len(startReading) <= 0 { | ||||
| 		return nil, errors.New(fmt.Sprintf("无法确定表计 %s 的计量的起始读数", meterId)) | ||||
| 	} | ||||
|  | ||||
| 	var startReadings *model.MeterReading | ||||
| 	for _, readings := range startReading { | ||||
| 		if startReadings == nil || readings.ReadAt.Before(startReadings.ReadAt.Time) { | ||||
| 			startReadings = &readings | ||||
| 		} | ||||
| 	} | ||||
| 	return startReadings, nil | ||||
| } | ||||
|  | ||||
| // 确定指定非商户表计的结束读数 | ||||
| func DeterminePublicMeterEndReading(meterId string, periodEnd time.Time, | ||||
| 	detachedAt time.Time, meterReadings []model.MeterReading) (*model.MeterReading, error) { | ||||
| 	periodEnding := types.Date{Time: periodEnd}.ToEndingOfDate() | ||||
|  | ||||
| 	if len(meterReadings) <= 0 { | ||||
| 		return nil, errors.New(fmt.Sprintf("表计的抄表记录数据不足%s", meterId)) | ||||
| 	} | ||||
|  | ||||
| 	var minReading types.DateTime | ||||
| 	for _, reading := range meterReadings { | ||||
| 		if reading.ReadAt.Before(minReading.Time) { | ||||
| 			minReading = reading.ReadAt | ||||
| 		} | ||||
| 	} | ||||
| 	startTimes := []time.Time{ | ||||
| 		minReading.Time, | ||||
| 		periodEnding.Time, | ||||
| 		ShiftToAsiaShanghai(detachedAt), | ||||
| 	} | ||||
| 	if len(startTimes) < 0 { | ||||
| 		return nil, errors.New(fmt.Sprintf("无法确定表计 {%s} 的计量的终止时间", meterId)) | ||||
| 	} | ||||
|  | ||||
| 	var startReading []model.MeterReading | ||||
| 	for _, reading := range meterReadings { | ||||
| 		readingAt := ShiftToAsiaShanghai(reading.ReadAt.UTC()) | ||||
| 		for _, startTime := range startTimes { | ||||
| 			if reading.Meter == meterId && readingAt.After(startTime) || readingAt.Equal(startTime) { | ||||
| 				startReading = append(startReading, reading) | ||||
| 				break | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if len(startReading) <= 0 { | ||||
| 		return nil, errors.New(fmt.Sprintf("无法确定表计 %s 的计量的终止读数", meterId)) | ||||
| 	} | ||||
|  | ||||
| 	var startReadings *model.MeterReading | ||||
| 	for _, readings := range startReading { | ||||
| 		if startReadings == nil || readings.ReadAt.Before(startReadings.ReadAt.Time) { | ||||
| 			startReadings = &readings | ||||
| 		} | ||||
| 	} | ||||
| 	return startReadings, nil | ||||
| } | ||||
							
								
								
									
										1
									
								
								service/calculate/summary.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								service/calculate/summary.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| package calculate | ||||
							
								
								
									
										288
									
								
								service/calculate/tenement.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										288
									
								
								service/calculate/tenement.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,288 @@ | ||||
| package calculate | ||||
|  | ||||
| import ( | ||||
| 	"electricity_bill_calc/model" | ||||
| 	"electricity_bill_calc/model/calculate" | ||||
| 	"electricity_bill_calc/repository" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"github.com/shopspring/decimal" | ||||
| 	"strings" | ||||
| 	"time" | ||||
| 	"unsafe" | ||||
| ) | ||||
|  | ||||
| // 核算园区中的全部商户表计电量用电 | ||||
| func TenementMetersCalculate(report *model.ReportIndex, PeriodStart time.Time, | ||||
| 	PeriodEnd time.Time, meterDetails []*model.MeterDetail, | ||||
| 	summary calculate.Summary) ([]model.TenementMeter, error) { | ||||
| 	tenements, err := repository.CalculateRepository.GetAllTenements(report.Id) | ||||
| 	if err != nil { | ||||
| 		fmt.Println("tenement 0", err) | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	tenementMeterRelations, err := repository.CalculateRepository.GetAllTenementMeterRelations(report.Park, PeriodEnd, PeriodStart) | ||||
| 	if err != nil { | ||||
| 		fmt.Println("tenement 1", err) | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	tenementMeterReadings, err := repository.CalculateRepository.GetMeterReadings(report.Id, model.METER_INSTALLATION_TENEMENT) | ||||
| 	if err != nil { | ||||
| 		fmt.Println("tenement 2", err) | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	lastPeriodReadings, err := repository.CalculateRepository.GetLastPeriodReadings(report.Id, model.METER_INSTALLATION_TENEMENT) | ||||
| 	if err != nil { | ||||
| 		fmt.Println("tenement 3", err) | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	var tenementReports []model.Tenement | ||||
|  | ||||
| 	for _, tenement := range tenements { | ||||
| 		var meters []model.TenementMeter | ||||
|  | ||||
| 		for _, relation := range tenementMeterRelations { | ||||
| 			if strings.EqualFold(relation.TenementId, tenement.Id) { | ||||
| 				meters = append(meters, relation) | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		pt, err := determineTenementConsumptions( | ||||
| 			tenement, | ||||
| 			meters, | ||||
| 			PeriodStart, | ||||
| 			PeriodEnd, | ||||
| 			tenementMeterReadings, | ||||
| 			lastPeriodReadings, | ||||
| 			meterDetails, | ||||
| 			summary, | ||||
| 		) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
|  | ||||
| 		report := model.Tenement{ | ||||
| 			Id:             pt.Tenement.Id, | ||||
| 			Park:           pt.Tenement.Park, | ||||
| 			FullName:       pt.Tenement.FullName, | ||||
| 			ShortName:      pt.Tenement.ShortName, | ||||
| 			Abbr:           pt.Tenement.Abbr, | ||||
| 			Address:        pt.Tenement.Address, | ||||
| 			ContactName:    pt.Tenement.ContactName, | ||||
| 			ContactPhone:   pt.Tenement.ContactPhone, | ||||
| 			Building:       pt.Tenement.Building, | ||||
| 			BuildingName:   pt.Tenement.BuildingName, | ||||
| 			OnFloor:        pt.Tenement.OnFloor, | ||||
| 			InvoiceInfo:    pt.Tenement.InvoiceInfo, | ||||
| 			MovedInAt:      pt.Tenement.MovedInAt, | ||||
| 			MovedOutAt:     pt.Tenement.MovedOutAt, | ||||
| 			CreatedAt:      pt.Tenement.CreatedAt, | ||||
| 			LastModifiedAt: pt.Tenement.LastModifiedAt, | ||||
| 			DeletedAt:      pt.Tenement.DeletedAt, | ||||
| 		} | ||||
|  | ||||
| 		tenementReports = append(tenementReports, report) | ||||
| 	} | ||||
|  | ||||
| 	return tenementMeterRelations, nil | ||||
| } | ||||
|  | ||||
| //TODO: 2023.08.02 此方法未完成此方法主要用于。确定指定商户在指定时间段内的所有表计读数(完成) | ||||
| func determineTenementConsumptions(tenement model.Tenement, | ||||
| 	relatedMeters []model.TenementMeter, periodStart time.Time, | ||||
| 	periodEnd time.Time, currentTermReadings []model.MeterReading, lastPeriodReadings []model.MeterReading, | ||||
| 	meterDetails []*model.MeterDetail, summary calculate.Summary) (calculate.PrimaryTenementStatistics, error) { | ||||
| 	var meters []calculate.Meter | ||||
| 	for _, meter := range relatedMeters { | ||||
| 		startReading, err := determineTenementMeterStartReading(meter.MeterId, periodStart, ShiftToAsiaShanghai(tenement.MovedInAt.Time), meter, currentTermReadings, lastPeriodReadings) | ||||
| 		if err != nil { | ||||
| 			fmt.Println(err) | ||||
| 			return calculate.PrimaryTenementStatistics{}, err | ||||
| 		} | ||||
|  | ||||
| 		endReading, err := determineTenementMeterEndReading(meter.MeterId, periodEnd, ShiftToAsiaShanghai(tenement.MovedOutAt.Time), meter, currentTermReadings) | ||||
| 		if err != nil { | ||||
| 			fmt.Println(err) | ||||
| 			return calculate.PrimaryTenementStatistics{}, err | ||||
| 		} | ||||
|  | ||||
| 		detail, err := getMeterDetail(meterDetails, meter.MeterId) | ||||
| 		if err != nil { | ||||
| 			return calculate.PrimaryTenementStatistics{}, err | ||||
| 		} | ||||
|  | ||||
| 		overall, err := ComputeOverall(*startReading, *endReading, summary) | ||||
| 		if err != nil { | ||||
| 			return calculate.PrimaryTenementStatistics{}, err | ||||
| 		} | ||||
|  | ||||
| 		critical, err := ComputeCritical(*startReading, *endReading, summary) | ||||
| 		if err != nil { | ||||
| 			return calculate.PrimaryTenementStatistics{}, err | ||||
| 		} | ||||
|  | ||||
| 		peak, err := ComputePeak(*startReading, *endReading, summary) | ||||
| 		if err != nil { | ||||
| 			return calculate.PrimaryTenementStatistics{}, err | ||||
| 		} | ||||
|  | ||||
| 		flat, err := ComputeFlat(*startReading, *endReading, summary) | ||||
| 		if err != nil { | ||||
| 			return calculate.PrimaryTenementStatistics{}, err | ||||
| 		} | ||||
|  | ||||
| 		valley, err := ComputeValley(*startReading, *endReading, summary) | ||||
| 		if err != nil { | ||||
| 			return calculate.PrimaryTenementStatistics{}, err | ||||
| 		} | ||||
|  | ||||
| 		lastTermReading := model.Reading{ | ||||
| 			Ratio:    startReading.Ratio, | ||||
| 			Overall:  startReading.Overall, | ||||
| 			Critical: startReading.Critical, | ||||
| 			Peak:     startReading.Peak, | ||||
| 			Flat:     startReading.Flat, | ||||
| 			Valley:   startReading.Valley, | ||||
| 		} | ||||
|  | ||||
| 		lastTermReadingPtr := &lastTermReading | ||||
|  | ||||
| 		currentTermReading := model.Reading{ | ||||
| 			Ratio:    endReading.Ratio, | ||||
| 			Overall:  endReading.Overall, | ||||
| 			Critical: endReading.Critical, | ||||
| 			Peak:     endReading.Peak, | ||||
| 			Flat:     endReading.Flat, | ||||
| 			Valley:   endReading.Valley, | ||||
| 		} | ||||
|  | ||||
| 		currentTermReadingPtr := ¤tTermReading | ||||
| 		meter := calculate.Meter{ | ||||
| 			Code:        meter.MeterId, | ||||
| 			Detail:      detail, | ||||
| 			CoveredArea: decimal.NewFromFloat(detail.Area.Decimal.InexactFloat64()), | ||||
|  | ||||
| 			LastTermReading:    (*calculate.Reading)(unsafe.Pointer(lastTermReadingPtr)), | ||||
| 			CurrentTermReading: (*calculate.Reading)(unsafe.Pointer(currentTermReadingPtr)), | ||||
|  | ||||
| 			Overall:  overall, | ||||
| 			Critical: critical, | ||||
| 			Peak:     peak, | ||||
| 			Flat:     flat, | ||||
| 			Valley:   valley, | ||||
|  | ||||
| 			AdjustLoss:              model.ConsumptionUnit{}, | ||||
| 			PooledBasic:             model.ConsumptionUnit{}, | ||||
| 			PooledAdjust:            model.ConsumptionUnit{}, | ||||
| 			PooledLoss:              model.ConsumptionUnit{}, | ||||
| 			PooledPublic:            model.ConsumptionUnit{}, | ||||
| 			SharedPoolingProportion: decimal.Decimal{}, | ||||
| 			Poolings:                nil, | ||||
| 		} | ||||
|  | ||||
| 		meters = append(meters, meter) | ||||
| 	} | ||||
|  | ||||
| 	return calculate.PrimaryTenementStatistics{ | ||||
| 		Tenement: tenement, | ||||
| 		Meters:   meters, | ||||
| 	}, nil | ||||
| } | ||||
|  | ||||
| func getMeterDetail(meterDetails []*model.MeterDetail, code string) (model.MeterDetail, error) { | ||||
| 	for _, detail := range meterDetails { | ||||
| 		if detail.Code == code { | ||||
| 			return *detail, nil | ||||
| 		} | ||||
| 	} | ||||
| 	return model.MeterDetail{}, errors.New(fmt.Sprintf("表计 %s 的详细信息不存在", code)) | ||||
| } | ||||
|  | ||||
| //确定指定表计的起始读数 | ||||
| func determineTenementMeterStartReading(meterId string, periodStart time.Time, tenementMovedInAt time.Time, | ||||
| 	meterRelation model.TenementMeter, currentTermReadings []model.MeterReading, | ||||
| 	lastPeriodReadings []model.MeterReading) (*model.MeterReading, error) { | ||||
| 	var startTime time.Time | ||||
| 	timeList := []time.Time{ | ||||
| 		periodStart, | ||||
| 		tenementMovedInAt, | ||||
| 		meterRelation.AssociatedAt.Time, | ||||
| 	} | ||||
|  | ||||
| 	for _, t := range timeList { | ||||
| 		if t.After(startTime) { | ||||
| 			startTime = t | ||||
| 		} | ||||
| 	} | ||||
| 	if startTime.IsZero() { | ||||
| 		return nil, fmt.Errorf("无法确定表计 %s 的计量的起始时间", meterId) | ||||
| 	} | ||||
|  | ||||
| 	var startReading *model.MeterReading | ||||
| 	if startTime.Equal(periodStart) { | ||||
| 		for _, reading := range lastPeriodReadings { | ||||
| 			if reading.Meter == meterId { | ||||
| 				if startReading == nil || reading.ReadAt.After(startReading.ReadAt.Time) { | ||||
| 					startReading = &reading | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} else { | ||||
| 		for _, reading := range currentTermReadings { | ||||
| 			readingAt := ShiftToAsiaShanghai(reading.ReadAt.Time) | ||||
| 			if reading.Meter == meterId && readingAt.After(startTime) { | ||||
| 				if startReading == nil || readingAt.Before(startReading.ReadAt.Time) { | ||||
| 					startReading = &reading | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	if startReading == nil { | ||||
| 		return nil, errors.New("无法确定表计 " + meterId + " 的计量的起始读数") | ||||
| 	} | ||||
| 	return startReading, nil | ||||
| } | ||||
|  | ||||
| // 确定指定表计的终止读书 | ||||
| func determineTenementMeterEndReading(meterId string, periodEnd time.Time, | ||||
| 	TenementMovedOutAt time.Time, meterRelation model.TenementMeter, | ||||
| 	currentTermReadings []model.MeterReading) (*model.MeterReading, error) { | ||||
| 	var endTime time.Time | ||||
| 	timeList := []time.Time{ | ||||
| 		periodEnd, | ||||
| 		TenementMovedOutAt, | ||||
| 		ShiftToAsiaShanghai(meterRelation.DisassociatedAt.Time), | ||||
| 	} | ||||
| 	for _, t := range timeList { | ||||
| 		if t.After(endTime) { | ||||
| 			endTime = t | ||||
| 		} | ||||
| 	} | ||||
| 	if endTime.IsZero() { | ||||
| 		return nil, fmt.Errorf("无法确定表计 %s 的计量的结束时间", meterId) | ||||
| 	} | ||||
|  | ||||
| 	var endReading *model.MeterReading | ||||
|  | ||||
| 	for _, reading := range currentTermReadings { | ||||
| 		readingAt := ShiftToAsiaShanghai(reading.ReadAt.Time) | ||||
| 		if reading.Meter == meterId && readingAt.Before(endTime) { | ||||
| 			if endReading == nil || readingAt.After(ShiftToAsiaShanghai(endReading.ReadAt.Time)) { | ||||
| 				endReading = &reading | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	if endReading == nil { | ||||
| 		return nil, errors.New(fmt.Sprintf("无法确定表计 %s 的计量的结束读数", meterId)) | ||||
| 	} | ||||
| 	return endReading, nil | ||||
| } | ||||
|  | ||||
| func ShiftToAsiaShanghai(t time.Time) time.Time { | ||||
| 	location, _ := time.LoadLocation("Asia/Shanghai") | ||||
| 	return t.In(location) | ||||
| } | ||||
							
								
								
									
										141
									
								
								service/calculate/utils.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										141
									
								
								service/calculate/utils.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,141 @@ | ||||
| package calculate | ||||
|  | ||||
| import ( | ||||
| 	"electricity_bill_calc/model" | ||||
| 	"electricity_bill_calc/model/calculate" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"github.com/shopspring/decimal" | ||||
| ) | ||||
|  | ||||
| // 计算两个读书之间的有功(总)电量 | ||||
| func ComputeOverall(startReading model.MeterReading, endReading model.MeterReading, summary calculate.Summary) (model.ConsumptionUnit, error) { | ||||
| 	start := startReading.Overall.InexactFloat64() * startReading.Ratio.InexactFloat64() | ||||
| 	end := endReading.Overall.InexactFloat64() * endReading.Ratio.InexactFloat64() | ||||
|  | ||||
| 	if start > end { | ||||
| 		return model.ConsumptionUnit{}, errors.New(fmt.Sprintf("表计 {%s} 有功(总)开始读数 {%x} 大于结束读数 {%x}", startReading.Meter, start, end)) | ||||
| 	} | ||||
|  | ||||
| 	amount := end - start | ||||
|  | ||||
| 	var summaryAmount float64 | ||||
| 	if summary.Overall.Amount == decimal.Zero { | ||||
| 		summaryAmount = decimal.NewFromFloat(1.0).InexactFloat64() | ||||
| 	} else { | ||||
| 		summaryAmount = summary.Overall.Amount.InexactFloat64() | ||||
| 	} | ||||
|  | ||||
| 	return model.ConsumptionUnit{ | ||||
| 		Amount:     decimal.NewFromFloat(amount), | ||||
| 		Fee:        decimal.NewFromFloat(amount * summary.Overall.Price.InexactFloat64()), | ||||
| 		Price:      decimal.NewFromFloat(summary.Overall.Price.InexactFloat64()), | ||||
| 		Proportion: decimal.NewFromFloat(amount / summaryAmount), | ||||
| 	}, nil | ||||
| } | ||||
|  | ||||
| //计算两个读书之间的尖峰电量 | ||||
| func ComputeCritical(startReading model.MeterReading, endReading model.MeterReading, summary calculate.Summary) (model.ConsumptionUnit, error) { | ||||
| 	start := startReading.Critical.InexactFloat64() * startReading.Ratio.InexactFloat64() | ||||
| 	end := endReading.Critical.InexactFloat64() * endReading.Ratio.InexactFloat64() | ||||
|  | ||||
| 	if start > end { | ||||
| 		return model.ConsumptionUnit{}, errors.New(fmt.Sprintf("尖峰开始读数 {%x} 大于结束读数 {%x}", start, end)) | ||||
| 	} | ||||
|  | ||||
| 	amount := end - start | ||||
| 	var summaryAmount float64 | ||||
|  | ||||
| 	if summary.Critical.Amount.Equal(decimal.Zero) { | ||||
| 		summaryAmount = decimal.NewFromFloat(1.0).InexactFloat64() | ||||
| 	} else { | ||||
| 		summaryAmount = summary.Critical.Amount.InexactFloat64() | ||||
| 	} | ||||
|  | ||||
| 	return model.ConsumptionUnit{ | ||||
| 		Amount:     decimal.NewFromFloat(amount), | ||||
| 		Fee:        decimal.NewFromFloat(amount * summary.Critical.Amount.InexactFloat64()), | ||||
| 		Price:      decimal.NewFromFloat(summary.Critical.Price.InexactFloat64()), | ||||
| 		Proportion: decimal.NewFromFloat(amount / summaryAmount), | ||||
| 	}, nil | ||||
| } | ||||
|  | ||||
| // 计算两个读数之间的峰电量 | ||||
| func ComputePeak(startReading model.MeterReading, endReading model.MeterReading, summary calculate.Summary) (model.ConsumptionUnit, error) { | ||||
| 	start := startReading.Peak.InexactFloat64() * startReading.Ratio.InexactFloat64() | ||||
| 	end := startReading.Peak.InexactFloat64() * endReading.Ratio.InexactFloat64() | ||||
|  | ||||
| 	if start > end { | ||||
| 		return model.ConsumptionUnit{}, errors.New(fmt.Sprintf("峰开始读数 {%x} 大于结束读数 {%x}", start, end)) | ||||
| 	} | ||||
|  | ||||
| 	amount := end - start | ||||
| 	var summaryAmount float64 | ||||
|  | ||||
| 	if summary.Peak.Amount.Equal(decimal.Zero) { | ||||
| 		summaryAmount = decimal.NewFromFloat(1.0).InexactFloat64() | ||||
| 	} else { | ||||
| 		summaryAmount = summary.Peak.Amount.InexactFloat64() | ||||
| 	} | ||||
|  | ||||
| 	return model.ConsumptionUnit{ | ||||
| 		Amount:     decimal.NewFromFloat(amount), | ||||
| 		Fee:        decimal.NewFromFloat(amount * summary.Peak.Price.InexactFloat64()), | ||||
| 		Price:      decimal.NewFromFloat(summary.Peak.Price.InexactFloat64()), | ||||
| 		Proportion: decimal.NewFromFloat(amount / summaryAmount), | ||||
| 	}, nil | ||||
|  | ||||
| } | ||||
|  | ||||
| //计算两个读数之间的平电量 | ||||
| func ComputeFlat(startReading model.MeterReading, endReading model.MeterReading, summary calculate.Summary) (model.ConsumptionUnit, error) { | ||||
| 	start := startReading.Flat.InexactFloat64() * startReading.Ratio.InexactFloat64() | ||||
| 	end := endReading.Flat.InexactFloat64() * endReading.Ratio.InexactFloat64() | ||||
|  | ||||
| 	if start > end { | ||||
| 		return model.ConsumptionUnit{}, errors.New(fmt.Sprintf("平开始读数 {%x} 大于结束读数 {%x}", start, end)) | ||||
| 	} | ||||
|  | ||||
| 	amount := end - start | ||||
| 	var summaryAmount float64 | ||||
|  | ||||
| 	if summary.Flat.Amount.Equal(decimal.Zero) { | ||||
| 		summaryAmount = decimal.NewFromFloat(1.0).InexactFloat64() | ||||
| 	} else { | ||||
| 		summaryAmount = summary.Flat.Amount.InexactFloat64() | ||||
| 	} | ||||
|  | ||||
| 	return model.ConsumptionUnit{ | ||||
| 		Amount:     decimal.NewFromFloat(amount), | ||||
| 		Fee:        decimal.NewFromFloat(amount * summary.Flat.Price.InexactFloat64()), | ||||
| 		Price:      decimal.NewFromFloat(summary.Flat.Price.InexactFloat64()), | ||||
| 		Proportion: decimal.NewFromFloat(amount / summaryAmount), | ||||
| 	}, nil | ||||
| } | ||||
|  | ||||
| //计算两个读数之间的谷电量 | ||||
| func ComputeValley(startReading model.MeterReading, endReading model.MeterReading, summary calculate.Summary) (model.ConsumptionUnit, error) { | ||||
| 	start := startReading.Valley.InexactFloat64() * startReading.Ratio.InexactFloat64() | ||||
| 	end := endReading.Valley.InexactFloat64() * endReading.Ratio.InexactFloat64() | ||||
|  | ||||
| 	if start > end { | ||||
| 		return model.ConsumptionUnit{}, errors.New(fmt.Sprintf("谷开始读数 {%x} 大于结束读数 {%x}", start, end)) | ||||
| 	} | ||||
|  | ||||
| 	amount := end - start | ||||
|  | ||||
| 	var summaryAmount float64 | ||||
|  | ||||
| 	if summary.Valley.Amount.Equal(decimal.Zero) { | ||||
| 		summaryAmount = decimal.NewFromFloat(1.0).InexactFloat64() | ||||
| 	} else { | ||||
| 		summaryAmount = summary.Valley.Amount.InexactFloat64() | ||||
| 	} | ||||
|  | ||||
| 	return model.ConsumptionUnit{ | ||||
| 		Amount:     decimal.NewFromFloat(amount), | ||||
| 		Fee:        decimal.NewFromFloat(amount * summary.Valley.Price.InexactFloat64()), | ||||
| 		Price:      decimal.NewFromFloat(summary.Valley.Price.InexactFloat64()), | ||||
| 		Proportion: decimal.NewFromFloat(amount / summaryAmount), | ||||
| 	}, nil | ||||
| } | ||||
							
								
								
									
										66
									
								
								service/calculate/wattCost.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								service/calculate/wattCost.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,66 @@ | ||||
| package calculate | ||||
|  | ||||
| import ( | ||||
| 	"electricity_bill_calc/model/calculate" | ||||
| 	"electricity_bill_calc/repository" | ||||
| 	"fmt" | ||||
| ) | ||||
|  | ||||
| func MainCalculateProcess(rid string) { | ||||
| 	report, err := repository.ReportRepository.GetReportIndex(rid) | ||||
| 	if err != nil { | ||||
| 		fmt.Println("1", err.Error()+"指定报表不存在") | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	reportSummary, err := repository.ReportRepository.RetrieveReportSummary(rid) | ||||
| 	if err != nil { | ||||
| 		fmt.Println("2", err.Error()+"指定报表的基本电量电费数据不存在") | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	summary := calculate.FromReportSummary(reportSummary, report) | ||||
|  | ||||
| 	periodStart := report.Period.SafeLower() | ||||
| 	periodEnd := report.Period.SafeUpper() | ||||
|  | ||||
| 	meterDetails, err := repository.MeterRepository.AllUsedMetersInReport(report.Id) | ||||
| 	if err != nil { | ||||
| 		fmt.Println("3", err) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	meterRelations, err := repository.CalculateRepository.GetAllPoolingMeterRelations(report.Park, periodStart.Time) | ||||
| 	if err != nil { | ||||
| 		fmt.Println("4", err) | ||||
| 		return | ||||
| 	} | ||||
| 	_, err = CheckMeterArea(report, meterDetails) | ||||
| 	if err != nil { | ||||
| 		fmt.Println("5", err) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	// 寻找每一个商户的所有表计读数,然后对分配到各个商户的表计读数进行初步的计算. | ||||
| 	tenementReports, err := TenementMetersCalculate(report, periodStart.Time, periodEnd.Time, meterDetails, summary) | ||||
| 	if err != nil { | ||||
| 		fmt.Println("6", err) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	//取得所有公摊表计的读数,以及公摊表计对应的分摊表计 | ||||
| 	poolingMetersReports, err := PooledMetersCalculate(report, periodStart.Time, periodEnd.Time, meterDetails, summary) | ||||
| 	if err != nil { | ||||
| 		fmt.Println("7", err) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	parkMetersReports, err := MetersParkCalculate(*report, periodStart.Time, periodEnd.Time, meterDetails, summary) | ||||
| 	if err != nil { | ||||
| 		fmt.Println("8", err) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	fmt.Println(meterRelations, tenementReports, poolingMetersReports, parkMetersReports) | ||||
|  | ||||
| } | ||||
| @@ -160,7 +160,6 @@ func NullTime2PointerString(nullTime sql.NullTime) *string { | ||||
| 	} | ||||
| } | ||||
|  | ||||
|  | ||||
| //该方法用于将时间解析为字符串指针 | ||||
| func TimeToStringPtr(t *time.Time) *string { | ||||
| 	if t == nil { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user