forked from free-lancers/electricity_bill_calc_service
合并分支
This commit is contained in:
455
service/calculate/meters.go
Normal file
455
service/calculate/meters.go
Normal file
@@ -0,0 +1,455 @@
|
||||
package calculate
|
||||
|
||||
import (
|
||||
"electricity_bill_calc/model"
|
||||
"electricity_bill_calc/model/calculate"
|
||||
"electricity_bill_calc/repository"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/shopspring/decimal"
|
||||
)
|
||||
|
||||
// / 合并所有的表计
|
||||
type Key struct {
|
||||
Code string
|
||||
TenementID string
|
||||
}
|
||||
type MeterMap map[Key]calculate.Meter
|
||||
|
||||
func CollectMeters(tenements []calculate.PrimaryTenementStatistics, poolings []calculate.Meter, publics []calculate.Meter) (MeterMap, error) {
|
||||
meters := make(MeterMap)
|
||||
// Collect tenement meters
|
||||
for _, t := range tenements {
|
||||
for _, m := range t.Meters {
|
||||
key := Key{TenementID: t.Tenement.Id, Code: m.Code}
|
||||
meters[key] = m
|
||||
}
|
||||
}
|
||||
// Collect poolings
|
||||
for _, m := range poolings {
|
||||
key := Key{TenementID: "", Code: m.Code}
|
||||
meters[key] = m
|
||||
}
|
||||
// Collect publics
|
||||
for _, m := range publics {
|
||||
key := Key{TenementID: "", Code: m.Code}
|
||||
meters[key] = m
|
||||
}
|
||||
return meters, nil
|
||||
}
|
||||
|
||||
// / 计算基本电费摊薄
|
||||
func CalculateBasicPooling(report *model.ReportIndex, summary *calculate.Summary, meters *MeterMap) error {
|
||||
switch report.BasisPooled {
|
||||
case model.POOLING_MODE_AREA:
|
||||
if summary.OverallArea.IsZero() {
|
||||
return fmt.Errorf("园区中表计覆盖总面积为零,无法按面积摊薄")
|
||||
}
|
||||
for _, meter := range *meters {
|
||||
meterFee := meter.Overall.Amount.InexactFloat64() * summary.BasicPooledPriceArea.InexactFloat64()
|
||||
meter.PooledBasic = model.ConsumptionUnit{
|
||||
Amount: meter.Overall.Amount,
|
||||
Fee: decimal.NewFromFloat(meterFee),
|
||||
Price: summary.BasicPooledPriceArea,
|
||||
Proportion: summary.BasicFee,
|
||||
}
|
||||
}
|
||||
case model.POOLING_MODE_CONSUMPTION:
|
||||
for _, meter := range *meters {
|
||||
meterFee := meter.Overall.Amount.InexactFloat64() * summary.BasicPooledPriceConsumption.InexactFloat64()
|
||||
meter.PooledBasic = model.ConsumptionUnit{
|
||||
Amount: meter.Overall.Amount,
|
||||
Fee: decimal.NewFromFloat(meterFee),
|
||||
Price: summary.BasicPooledPriceConsumption,
|
||||
Proportion: summary.BasicFee,
|
||||
}
|
||||
}
|
||||
default:
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
/// 计算调整电费摊薄
|
||||
|
||||
func CalculateAdjustPooling(report model.ReportIndex, summary calculate.Summary, meters MeterMap) error {
|
||||
var p decimal.Decimal
|
||||
switch report.AdjustPooled {
|
||||
case model.POOLING_MODE_AREA:
|
||||
if summary.OverallArea.IsZero() {
|
||||
return fmt.Errorf("园区中表计覆盖总面积为零,无法按面积摊薄")
|
||||
}
|
||||
|
||||
for _, meter := range meters {
|
||||
meterFee := meter.Overall.Amount.Mul(summary.AdjustPooledPriceArea)
|
||||
if summary.AdjustFee.IsZero() {
|
||||
p = decimal.Zero
|
||||
} else {
|
||||
p = meterFee.Div(summary.AdjustFee)
|
||||
}
|
||||
meter.PooledAdjust = model.ConsumptionUnit{
|
||||
Amount: meter.Overall.Amount,
|
||||
Fee: meterFee,
|
||||
Price: summary.AdjustPooledPriceArea,
|
||||
Proportion: p,
|
||||
}
|
||||
}
|
||||
case model.POOLING_MODE_CONSUMPTION:
|
||||
for _, meter := range meters {
|
||||
meterFee := meter.Overall.Amount.Mul(summary.AdjustPooledPriceConsumption)
|
||||
if summary.AdjustFee.IsZero() {
|
||||
p = decimal.Zero
|
||||
} else {
|
||||
p = meterFee.Div(summary.AdjustFee)
|
||||
}
|
||||
meter.PooledAdjust = model.ConsumptionUnit{
|
||||
Amount: meter.Overall.Amount,
|
||||
Fee: meterFee,
|
||||
Price: summary.AdjustPooledPriceConsumption,
|
||||
Proportion: p,
|
||||
}
|
||||
}
|
||||
default:
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 除数问题
|
||||
func CalculateLossPooling(report model.ReportIndex, summary calculate.Summary, meters MeterMap) error {
|
||||
switch report.LossPooled {
|
||||
case model.POOLING_MODE_AREA:
|
||||
if summary.OverallArea.IsZero() {
|
||||
return fmt.Errorf("园区中表计覆盖总面积为零,无法按面积摊薄")
|
||||
}
|
||||
for _, meter := range meters {
|
||||
pooledLossAmount1 := meter.Detail.Area.Decimal.Div(summary.OverallArea)
|
||||
pooledLossAmount := pooledLossAmount1.Mul(summary.AuthoizeLoss.Amount)
|
||||
meter.PooledLoss = model.ConsumptionUnit{
|
||||
Amount: pooledLossAmount,
|
||||
Fee: pooledLossAmount.Mul(summary.LossDilutedPrice),
|
||||
Price: summary.LossDilutedPrice,
|
||||
Proportion: meter.Detail.Area.Decimal.Div(summary.OverallArea),
|
||||
}
|
||||
}
|
||||
case model.POOLING_MODE_CONSUMPTION:
|
||||
for _, meter := range meters {
|
||||
pooledLossAmount1 := meter.Detail.Area.Decimal.Div(summary.OverallArea)
|
||||
pooledLossAmount := pooledLossAmount1.Mul(summary.AuthoizeLoss.Amount)
|
||||
|
||||
meter.PooledLoss = model.ConsumptionUnit{
|
||||
Amount: pooledLossAmount,
|
||||
Fee: pooledLossAmount.Mul(summary.LossDilutedPrice),
|
||||
Price: summary.LossDilutedPrice,
|
||||
Proportion: meter.Overall.Amount.Div(summary.Overall.Amount),
|
||||
}
|
||||
}
|
||||
default:
|
||||
// 其他情况下不做处理
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
/// 计算所有商户类型表计的全周期电量。
|
||||
|
||||
func CalculateTenementConsumptions(meters MeterMap) (map[string]decimal.Decimal, error) {
|
||||
consumptions := make(map[string]decimal.Decimal)
|
||||
for _, meter := range meters {
|
||||
if meter.Detail.MeterType == model.METER_INSTALLATION_TENEMENT {
|
||||
amount, ok := consumptions[meter.Code]
|
||||
if !ok {
|
||||
amount = decimal.Decimal{}
|
||||
}
|
||||
amount.Add(meter.Overall.Amount).Add(amount)
|
||||
consumptions[meter.Code] = amount
|
||||
}
|
||||
}
|
||||
for _, meter := range meters {
|
||||
if meter.Detail.MeterType == model.METER_INSTALLATION_TENEMENT {
|
||||
amount, ok := consumptions[meter.Code]
|
||||
if !ok {
|
||||
return nil, errors.New("meter code not found in consumptions")
|
||||
}
|
||||
|
||||
if amount.GreaterThan(decimal.Zero) {
|
||||
meter.SharedPoolingProportion = meter.Overall.Amount.Div(amount)
|
||||
} else if amount.IsZero() {
|
||||
meter.SharedPoolingProportion = decimal.NewFromFloat(1.0)
|
||||
} else {
|
||||
meter.SharedPoolingProportion = decimal.NewFromFloat(1.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return consumptions, nil
|
||||
}
|
||||
|
||||
/*
|
||||
/// 计算商户表计的公摊分摊
|
||||
|
||||
func CalculateTenementPoolings(report model.ReportIndex, summary calculate.Summary, meters MeterMap, meterRelations []model.MeterRelation) error {
|
||||
for _, meter := range meters {
|
||||
if meter.Detail.MeterType == model.METER_INSTALLATION_TENEMENT {
|
||||
switch report.PublicPooled {
|
||||
case model.POOLING_MODE_AREA:
|
||||
for _, relation := range meterRelations {
|
||||
if relation.SlaveMeter == meter.Code {
|
||||
key := Key{
|
||||
Code: relation.MasterMeter,
|
||||
}
|
||||
parentMeter, ok := meters[key]
|
||||
if !ok {
|
||||
return errors.New("父级表记未找到")
|
||||
}
|
||||
|
||||
poolingAmount := meter.Detail.Area.Decimal.Div(parentMeter.CoveredArea).
|
||||
Mul(meter.SharedPoolingProportion).
|
||||
Mul(parentMeter.Overall.Amount).Mul(summary.Overall.Price)
|
||||
|
||||
pooling := calculate.Pooling{
|
||||
Code: parentMeter.Code,
|
||||
Detail: model.ConsumptionUnit{
|
||||
Amount: poolingAmount,
|
||||
Fee: poolingAmount.Mul(summary.Overall.Price),
|
||||
Price: summary.Overall.Price,
|
||||
//后续debug此处需要判断,
|
||||
Proportion: poolingAmount.Div(parentMeter.Overall.Amount),
|
||||
},
|
||||
}
|
||||
|
||||
pooling := calculate.Pooling{
|
||||
Code: parentMeter.Code,
|
||||
Detail: model.ConsumptionUnit{
|
||||
Amount: poolingAmount,
|
||||
Fee: poolingAmount.Mul(summary.Overall.Price),
|
||||
Price: summary.Overall.Price,
|
||||
Proportion: poolingAmount.Div(parentMeter.Overall.Amount),
|
||||
},
|
||||
}
|
||||
meter.PooledPublic = &ConsumptionUnit{
|
||||
Amount: poolingAmount,
|
||||
Fee: new(big.Rat).Mul(poolingAmount, summary.Overall.Price),
|
||||
Price: summary.Overall.Price,
|
||||
Proportion: new(big.Rat).Quo(poolingAmount, parentAmount),
|
||||
}
|
||||
|
||||
meter.Poolings = append(meter.Poolings, pooling)
|
||||
}
|
||||
}
|
||||
|
||||
case Consumption:
|
||||
for _, relation := range meterRelations {
|
||||
if relation.SlaveMeter == meter.Code {
|
||||
parentMeter, ok := meters[relation.MasterMeter]
|
||||
if !ok {
|
||||
return errors.New("parent meter not found")
|
||||
}
|
||||
|
||||
if parentMeter.Overall.Amount.Cmp(new(big.Rat)) == 0 {
|
||||
poolingAmount := new(big.Rat)
|
||||
parentAmount := new(big.Rat)
|
||||
|
||||
pooling := &Pooling{
|
||||
Code: parentMeter.Code,
|
||||
Detail: &ConsumptionUnit{
|
||||
Amount: poolingAmount,
|
||||
Fee: new(big.Rat),
|
||||
Price: summary.Overall.Price,
|
||||
Proportion: new(big.Rat),
|
||||
},
|
||||
}
|
||||
|
||||
meter.PooledPublic = &ConsumptionUnit{
|
||||
Amount: poolingAmount,
|
||||
Fee: new(big.Rat),
|
||||
Price: summary.Overall.Price,
|
||||
Proportion: new(big.Rat),
|
||||
}
|
||||
|
||||
meter.Poolings = append(meter.Poolings, pooling)
|
||||
} else {
|
||||
poolingAmount := new(big.Rat).Mul(meter.Overall.Amount, new(big.Rat).Quo(parentMeter.Overall.Amount, parentMeter.Overall.Amount))
|
||||
parentAmount := parentMeter.Overall.Amount
|
||||
|
||||
pooling := &Pooling{
|
||||
Code: parentMeter.Code,
|
||||
Detail: &ConsumptionUnit{
|
||||
Amount: poolingAmount,
|
||||
Fee: new(big.Rat).Mul(poolingAmount, summary.Overall.Price),
|
||||
Price: summary.Overall.Price,
|
||||
Proportion: new(big.Rat).Quo(poolingAmount, parentAmount),
|
||||
},
|
||||
}
|
||||
|
||||
meter.PooledPublic = &ConsumptionUnit{
|
||||
Amount: poolingAmount,
|
||||
Fee: new(big.Rat).Mul(poolingAmount, summary.Overall.Price),
|
||||
Price: summary.Overall.Price,
|
||||
Proportion: new(big.Rat).Quo(poolingAmount, parentAmount),
|
||||
}
|
||||
|
||||
meter.Poolings = append(meter.Poolings, pooling)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
// handle other pooling modes...
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
*/
|
||||
// 计算商户表计的公摊分摊
|
||||
func CalculateTenementPoolings(report model.ReportIndex, summary calculate.Summary, meters MeterMap, meterRelations []model.MeterRelation) error {
|
||||
|
||||
switch report.PublicPooled {
|
||||
case model.POOLING_MODE_AREA:
|
||||
for _, meter := range meters {
|
||||
if meter.Detail.MeterType == model.METER_INSTALLATION_TENEMENT {
|
||||
var pooleds []struct {
|
||||
PooledAmount decimal.Decimal
|
||||
ParentAmount decimal.Decimal
|
||||
ParentCode string
|
||||
}
|
||||
for _, relation := range meterRelations {
|
||||
if relation.SlaveMeter == meter.Code {
|
||||
key := Key{
|
||||
Code: relation.MasterMeter,
|
||||
}
|
||||
parentMeter, ok := meters[key]
|
||||
if !ok {
|
||||
// 处理未找到父级表计的情况
|
||||
continue
|
||||
}
|
||||
// 计算分摊电量和父级表电量
|
||||
|
||||
pooledAmount := meter.Detail.Area.Decimal.Div(parentMeter.CoveredArea).Mul(parentMeter.Overall.Amount).Mul(meter.SharedPoolingProportion)
|
||||
pooleds = append(pooleds, struct {
|
||||
PooledAmount decimal.Decimal
|
||||
ParentAmount decimal.Decimal
|
||||
ParentCode string
|
||||
}{
|
||||
PooledAmount: pooledAmount,
|
||||
ParentAmount: parentMeter.Overall.Amount,
|
||||
ParentCode: parentMeter.Code,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 计算总分摊电量和总父级电量
|
||||
var consumptions, total decimal.Decimal
|
||||
for _, p := range pooleds {
|
||||
consumptions = consumptions.Add(p.PooledAmount)
|
||||
total = total.Add(p.ParentAmount)
|
||||
}
|
||||
|
||||
// 计算并更新公摊分摊信息
|
||||
for _, p := range pooleds {
|
||||
poolingAmount := p.PooledAmount
|
||||
proportion := p.PooledAmount.Div(p.ParentAmount)
|
||||
fee := poolingAmount.Mul(summary.Overall.Price)
|
||||
|
||||
// 更新父级表计的公摊分摊信息
|
||||
key := Key{
|
||||
Code: p.ParentCode,
|
||||
}
|
||||
parentMeter := meters[key]
|
||||
parentMeter.PooledPublic.Amount = consumptions
|
||||
parentMeter.PooledPublic.Fee = consumptions.Mul(summary.Overall.Price)
|
||||
parentMeter.PooledPublic.Proportion = consumptions.Div(total)
|
||||
meters[Key{Code: p.ParentCode}] = parentMeter
|
||||
// 创建并更新分摊信息
|
||||
pooling := calculate.Pooling{
|
||||
Code: p.ParentCode,
|
||||
Detail: model.ConsumptionUnit{
|
||||
Amount: poolingAmount,
|
||||
Fee: fee,
|
||||
Price: summary.Overall.Price,
|
||||
Proportion: proportion,
|
||||
},
|
||||
}
|
||||
meter.Poolings = append(meter.Poolings, &pooling)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case model.POOLING_MODE_CONSUMPTION:
|
||||
for _, meter := range meters {
|
||||
if meter.Detail.MeterType == model.METER_INSTALLATION_TENEMENT {
|
||||
var pooled []struct {
|
||||
PooledAmount decimal.Decimal
|
||||
ParentAmount decimal.Decimal
|
||||
ParentCode string
|
||||
}
|
||||
for _, relation := range meterRelations {
|
||||
if relation.SlaveMeter == meter.Code {
|
||||
parentMeter, ok := meters[Key{Code: relation.MasterMeter}]
|
||||
if !ok {
|
||||
// 处理未找到父级表计的情况
|
||||
continue
|
||||
}
|
||||
// 计算分摊电量和父级电量
|
||||
var pooledAmount decimal.Decimal
|
||||
if parentMeter.Overall.Amount.IsZero() {
|
||||
relations, err := repository.MeterRepository.ListPooledMeterRelations(report.Park, meter.Code)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
//此处rust版本有误,更新后的解决办法
|
||||
pooledAmount = meter.Overall.Amount.Div(decimal.NewFromInt(int64(len(relations)))).Mul(parentMeter.Overall.Amount)
|
||||
}
|
||||
pooled = append(pooled, struct {
|
||||
PooledAmount decimal.Decimal
|
||||
ParentAmount decimal.Decimal
|
||||
ParentCode string
|
||||
}{
|
||||
PooledAmount: pooledAmount,
|
||||
ParentAmount: parentMeter.Overall.Amount,
|
||||
ParentCode: parentMeter.Code,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 计算总分摊电量和总父级表记电量
|
||||
var consumptions, total decimal.Decimal
|
||||
for _, p := range pooled {
|
||||
consumptions = consumptions.Add(p.PooledAmount)
|
||||
total = total.Add(p.ParentAmount)
|
||||
}
|
||||
|
||||
// 计算并更新公摊分摊信息
|
||||
for _, p := range pooled {
|
||||
poolingAmount := p.PooledAmount
|
||||
proportion := p.PooledAmount.Div(p.ParentAmount)
|
||||
fee := poolingAmount.Mul(summary.Overall.Price)
|
||||
|
||||
// 更新父级表计的公摊分摊信息
|
||||
parentMeter := meters[Key{Code: p.ParentCode}]
|
||||
parentMeter.PooledPublic.Amount = consumptions
|
||||
parentMeter.PooledPublic.Fee = consumptions.Mul(summary.Overall.Price)
|
||||
parentMeter.PooledPublic.Proportion = consumptions.Div(total)
|
||||
meters[Key{Code: p.ParentCode}] = parentMeter
|
||||
|
||||
// 创建并更新分摊信息
|
||||
pooling := calculate.Pooling{
|
||||
Code: p.ParentCode,
|
||||
Detail: model.ConsumptionUnit{
|
||||
Amount: poolingAmount,
|
||||
Fee: fee,
|
||||
Price: summary.Overall.Price,
|
||||
Proportion: proportion,
|
||||
},
|
||||
}
|
||||
meter.Poolings = append(meter.Poolings, &pooling)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
// 处理其他分摊模式
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
72
service/calculate/mod.go
Normal file
72
service/calculate/mod.go
Normal file
@@ -0,0 +1,72 @@
|
||||
package calculate
|
||||
|
||||
import (
|
||||
"electricity_bill_calc/global"
|
||||
"electricity_bill_calc/repository"
|
||||
"github.com/doug-martin/goqu/v9"
|
||||
)
|
||||
|
||||
type _ModService struct {
|
||||
ds goqu.DialectWrapper
|
||||
}
|
||||
|
||||
var ModService = _ModService{
|
||||
ds: goqu.Dialect("postgres"),
|
||||
}
|
||||
|
||||
func mainCalculateProcess(rid string) error {
|
||||
|
||||
// 计算所有已经启用的商铺面积总和,仅计算所有未迁出的商户的所有表计对应的商铺面积。
|
||||
err := CalculateEnabledArea(tenementreports, &summary)
|
||||
// 计算基本电费分摊、调整电费分摊、电费摊薄单价。
|
||||
err = CalculatePrices(&summary)
|
||||
// 收集目前所有已经处理的表计,统一对其进行摊薄计算。
|
||||
collectMeters, err := CollectMeters(tenementreports, poolingmetersreports, parkmetersreports)
|
||||
meters, err := collectMeters, err
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// 根据核算报表中设置的摊薄内容,逐个表计进行计算
|
||||
CalculateBasicPooling(report, summary, meters)
|
||||
CalculateAdjustPooling(report, summary, meters)
|
||||
CalculateLossPooling(report, summary, meters)
|
||||
// 计算所有商户类型表计的全周期电量,并根据全周期电量计算共用过同一表计的商户的二次分摊比例。
|
||||
CalculateTenementConsumptions(meters)
|
||||
CalculateTenementPoolings(report, summary, meters, metersrelations)
|
||||
// 计算商户的合计电费信息,并归总与商户相关联的表计记录
|
||||
tenementCharges, err := CalculateTenementCharge(tenementReports, summary, meters, meterRelations)
|
||||
if err != nil {
|
||||
// 处理错误
|
||||
}
|
||||
// 从此处开始向数据库保存全部计算结果。
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
tx, _ := global.DB.Begin(ctx)
|
||||
err = repository.CalculateRepository.ClearReportContent(tx, report.Id)
|
||||
if err != nil {
|
||||
tx.Rollback(ctx)
|
||||
return err
|
||||
}
|
||||
|
||||
err = SaveSummary(tx, summary)
|
||||
if err != nil {
|
||||
tx.Rollback(ctx)
|
||||
return err
|
||||
}
|
||||
err = SavePublics(tx, report, meters)
|
||||
if err != nil {
|
||||
tx.Rollback(ctx)
|
||||
return err
|
||||
}
|
||||
err = SavePoolings(tx)
|
||||
if err != nil {
|
||||
tx.Rollback(ctx)
|
||||
return err
|
||||
}
|
||||
err = SaveTenements(tx)
|
||||
if err != nil {
|
||||
tx.Rollback(ctx)
|
||||
return err
|
||||
}
|
||||
tx.Commit(ctx)
|
||||
}
|
75
service/calculate/persist.go
Normal file
75
service/calculate/persist.go
Normal file
@@ -0,0 +1,75 @@
|
||||
package calculate
|
||||
|
||||
import (
|
||||
"electricity_bill_calc/global"
|
||||
"electricity_bill_calc/model"
|
||||
"electricity_bill_calc/model/calculate"
|
||||
"electricity_bill_calc/repository"
|
||||
"github.com/jackc/pgx/v5"
|
||||
)
|
||||
|
||||
// 向数据库保存核算概况结果
|
||||
func SaveSummary(tx pgx.Tx, summary calculate.Summary) error {
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
// 保存核算概况结果到数据库
|
||||
err := repository.CalculateRepository.SaveReportSummary(tx, summary)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tx.Commit(ctx)
|
||||
return nil
|
||||
}
|
||||
|
||||
// type MeterMap map[string]map[string]calculate.Meter
|
||||
// 向数据库保存公共表计的计算结果
|
||||
func SavePublics(tx pgx.Tx, report model.ReportIndex, meters MeterMap) error {
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
var filteredMeters []calculate.Meter
|
||||
|
||||
for _, m := range meters {
|
||||
if m.Detail.MeterType == model.METER_INSTALLATION_PARK {
|
||||
filteredMeters = append(filteredMeters, m)
|
||||
}
|
||||
}
|
||||
err := repository.CalculateRepository.SaveReportPublics(tx, report.Id, filteredMeters)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tx.Commit(ctx)
|
||||
return nil
|
||||
}
|
||||
|
||||
func SavePoolings(tx pgx.Tx, report model.ReportIndex, meters MeterMap, relations []model.MeterRelation) error {
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
var poolingMeters []calculate.Meter
|
||||
var tenementMeters []calculate.Meter
|
||||
// 根据条件筛选 Meter 并保存到对应的数组中
|
||||
for _, m := range meters {
|
||||
if m.Detail.MeterType == model.METER_INSTALLATION_POOLING {
|
||||
poolingMeters = append(poolingMeters, m)
|
||||
} else if m.Detail.MeterType == model.METER_INSTALLATION_TENEMENT {
|
||||
tenementMeters = append(tenementMeters, m)
|
||||
}
|
||||
}
|
||||
err := repository.CalculateRepository.SaveReportPoolings(tx, report.Id, poolingMeters, relations, tenementMeters)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func SaveTenements(tx pgx.Tx, report model.ReportIndex, tenement []calculate.PrimaryTenementStatistics, tc []*calculate.TenementCharge) error {
|
||||
var ts []model.Tenement
|
||||
for _, r := range tenement {
|
||||
ts = append(ts, r.Tenement)
|
||||
}
|
||||
err := repository.CalculateRepository.SaveReportTenement(tx, report, ts, tc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
55
service/calculate/summary.go
Normal file
55
service/calculate/summary.go
Normal file
@@ -0,0 +1,55 @@
|
||||
package calculate
|
||||
|
||||
import (
|
||||
"electricity_bill_calc/model/calculate"
|
||||
"errors"
|
||||
"github.com/shopspring/decimal"
|
||||
)
|
||||
|
||||
// / 计算已经启用的商铺面积和
|
||||
// /
|
||||
// / - `tenements`:所有商户的电量信息
|
||||
// / - `summary`:核算报表的摘要信息
|
||||
func CalculateEnabledArea(tenements []calculate.PrimaryTenementStatistics, summary *calculate.Summary) error {
|
||||
var areaMeters []calculate.Meter
|
||||
for _, t := range tenements {
|
||||
areaMeters = append(areaMeters, t.Meters...)
|
||||
}
|
||||
// 去重
|
||||
uniqueAreaMeters := make(map[string]calculate.Meter)
|
||||
for _, meter := range areaMeters {
|
||||
uniqueAreaMeters[meter.Code] = meter
|
||||
}
|
||||
var areaTotal decimal.Decimal
|
||||
for _, meter := range uniqueAreaMeters {
|
||||
areaTotal = areaTotal.Add(meter.Detail.Area.Decimal)
|
||||
}
|
||||
if summary != nil {
|
||||
summary.OverallArea = areaTotal
|
||||
} else {
|
||||
return nil, errors.New("summary is nil")
|
||||
}
|
||||
return &areaTotal, nil
|
||||
}
|
||||
|
||||
// / 计算基本电费分摊、调整电费分摊以及电费摊薄单价。
|
||||
// /
|
||||
// / - `summary`:核算报表的摘要信息
|
||||
func CalculatePrices(summary *calculate.Summary) error {
|
||||
if summary.TotalConsumption.IsZero() {
|
||||
return nil
|
||||
}
|
||||
summary.BasicPooledPriceConsumption = summary.BasicFee.Div(summary.TotalConsumption)
|
||||
if summary.OverallArea.IsZero() {
|
||||
summary.BasicPooledPriceArea = decimal.Zero
|
||||
} else {
|
||||
summary.BasicPooledPriceArea = summary.BasicFee.Div(summary.OverallArea)
|
||||
}
|
||||
summary.AdjustPooledPriceConsumption = summary.AdjustFee.Div(summary.TotalConsumption)
|
||||
if summary.OverallArea.IsZero() {
|
||||
summary.AdjustPooledPriceArea = decimal.Zero
|
||||
} else {
|
||||
summary.AdjustPooledPriceArea = summary.AdjustFee.Div(summary.OverallArea)
|
||||
}
|
||||
return nil
|
||||
}
|
221
service/calculate/tenement.go
Normal file
221
service/calculate/tenement.go
Normal file
@@ -0,0 +1,221 @@
|
||||
package calculate
|
||||
|
||||
import (
|
||||
"electricity_bill_calc/model"
|
||||
"electricity_bill_calc/model/calculate"
|
||||
"errors"
|
||||
"github.com/shopspring/decimal"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
// / 计算各个商户的合计信息,并归总与商户关联的表计记录
|
||||
func CalculateTenementCharge(tenements []calculate.PrimaryTenementStatistics, summary calculate.Summary, meters MeterMap, relations []model.MeterRelation) ([]calculate.TenementCharge, error) {
|
||||
tenementCharges := make([]calculate.TenementCharge, 0)
|
||||
for _, tenement := range tenements {
|
||||
relatedMeters := make([]calculate.Meter, 0)
|
||||
for _, meter := range tenement.Meters {
|
||||
code := meter.Code
|
||||
if meter.Detail.MeterType == model.METER_INSTALLATION_TENEMENT {
|
||||
code = meter.Detail.Code
|
||||
}
|
||||
relatedMeter, ok := meters[Key{Code: code}]
|
||||
if !ok {
|
||||
return nil, errors.New("related meter not found")
|
||||
}
|
||||
relatedMeters = append(relatedMeters, relatedMeter)
|
||||
}
|
||||
|
||||
// Calculate overall, critical, peak, flat, valley, etc.
|
||||
//var overall, critical, peak, flat, valley model.ConsumptionUnit
|
||||
basicPooled, adjustPooled, lossPooled, publicPooled := new(big.Rat), new(big.Rat), new(big.Rat), new(big.Rat)
|
||||
lossAmount := new(big.Rat)
|
||||
for _, meter := range relatedMeters {
|
||||
overall.Add(overall, meter.Overall)
|
||||
critical.Add(critical, meter.Critical)
|
||||
peak.Add(peak, meter.Peak)
|
||||
flat.Add(flat, meter.Flat)
|
||||
valley.Add(valley, meter.Valley)
|
||||
basicPooled.Add(basicPooled, meter.PooledBasic.Fee)
|
||||
adjustPooled.Add(adjustPooled, meter.PooledAdjust.Fee)
|
||||
lossAmount.Add(lossAmount, meter.AdjustLoss.Amount)
|
||||
lossPooled.Add(lossPooled, meter.PooledLoss.Fee)
|
||||
publicPooled.Add(publicPooled, meter.PooledPublic.Fee)
|
||||
}
|
||||
|
||||
// Update proportions and other data for related meters
|
||||
for _, meter := range relatedMeters {
|
||||
meter.Overall.Proportion = new(big.Rat).Quo(meter.Overall.Amount, overall.Amount)
|
||||
meter.Critical.Proportion = new(big.Rat).Quo(meter.Critical.Amount, critical.Amount)
|
||||
meter.Peak.Proportion = new(big.Rat).Quo(meter.Peak.Amount, peak.Amount)
|
||||
meter.Flat.Proportion = new(big.Rat).Quo(meter.Flat.Amount, flat.Amount)
|
||||
meter.Valley.Proportion = new(big.Rat).Quo(meter.Valley.Amount, valley.Amount)
|
||||
meter.PooledBasic.Proportion = new(big.Rat).Quo(meter.PooledBasic.Fee, basicPooled)
|
||||
meter.PooledAdjust.Proportion = new(big.Rat).Quo(meter.PooledAdjust.Fee, adjustPooled)
|
||||
meter.PooledLoss.Proportion = new(big.Rat).Quo(meter.PooledLoss.Fee, lossPooled)
|
||||
meter.PooledPublic.Proportion = new(big.Rat).Quo(meter.PooledPublic.Fee, publicPooled)
|
||||
}
|
||||
|
||||
tenementCharges = append(tenementCharges, TenementCharges{
|
||||
Tenement: tenement.Tenement.ID,
|
||||
Overall: ConsumptionUnit{
|
||||
Amount: overall.Amount,
|
||||
Fee: new(big.Rat).Mul(overall.Amount, summary.Overall.Price),
|
||||
Price: summary.Overall.Price,
|
||||
Proportion: new(big.Rat).Quo(overall.Amount, summary.Overall.Amount),
|
||||
},
|
||||
Critical: ConsumptionUnit{
|
||||
Amount: critical.Amount,
|
||||
Fee: new(big.Rat).Mul(critical.Amount, summary.Critical.Price),
|
||||
Price: summary.Critical.Price,
|
||||
Proportion: new(big.Rat).Quo(critical.Amount, summary.Critical.Amount),
|
||||
},
|
||||
Peak: ConsumptionUnit{
|
||||
Amount: peak.Amount,
|
||||
Fee: new(big.Rat).Mul(peak.Amount, summary.Peak.Price),
|
||||
Price: summary.Peak.Price,
|
||||
Proportion: new(big.Rat).Quo(peak.Amount, summary.Peak.Amount),
|
||||
},
|
||||
Flat: ConsumptionUnit{
|
||||
Amount: flat.Amount,
|
||||
Fee: new(big.Rat).Mul(flat.Amount, summary.Flat.Price),
|
||||
Price: summary.Flat.Price,
|
||||
Proportion: new(big.Rat).Quo(flat.Amount, summary.Flat.Amount),
|
||||
},
|
||||
Valley: ConsumptionUnit{
|
||||
Amount: valley.Amount,
|
||||
Fee: new(big.Rat).Mul(valley.Amount, summary.Valley.Price),
|
||||
Price: summary.Valley.Price,
|
||||
Proportion: new(big.Rat).Quo(valley.Amount, summary.Valley.Amount),
|
||||
},
|
||||
Loss: ConsumptionUnit{
|
||||
Amount: lossAmount,
|
||||
Fee: new(big.Rat).Mul(lossPooled, summary.AuthorizeLoss.Price),
|
||||
Price: summary.AuthorizeLoss.Price,
|
||||
Proportion: new(big.Rat).Quo(lossAmount, summary.AuthorizeLoss.Amount),
|
||||
},
|
||||
BasicFee: basicPooled,
|
||||
AdjustFee: adjustPooled,
|
||||
LossPooled: lossPooled,
|
||||
PublicPooled: publicPooled,
|
||||
// ... 其他字段的初始化
|
||||
})
|
||||
}
|
||||
|
||||
return tenementCharges, nil
|
||||
}
|
||||
func calculateTenementCharge(
|
||||
tenements []*PrimaryTenementStatistics,
|
||||
summary *Summary,
|
||||
meters MeterMap,
|
||||
_relations []MeterRelation,
|
||||
) ([]TenementCharges, error) {
|
||||
var tenementCharges []TenementCharges
|
||||
|
||||
for _, t := range tenements {
|
||||
meterCodes := make([]string, len(t.Meters))
|
||||
for i, m := range t.Meters {
|
||||
meterCodes[i] = m.Code
|
||||
}
|
||||
|
||||
relatedMeters := make([]*Meter, len(meterCodes))
|
||||
for i, code := range meterCodes {
|
||||
relatedMeter, ok := meters[Key{Code: code, TenementID: t.Tenement.ID}]
|
||||
if !ok {
|
||||
// 处理未找到相关表计的情况
|
||||
continue
|
||||
}
|
||||
relatedMeters[i] = relatedMeter
|
||||
}
|
||||
|
||||
var overall, critical, peak, flat, valley ConsumptionUnit
|
||||
var basicPooled, adjustPooled, lossAmount, lossPooled, publicPooled decimal.Decimal
|
||||
|
||||
for _, meter := range relatedMeters {
|
||||
overall.Amount = overall.Amount.Add(meter.Overall.Amount)
|
||||
overall.Fee = overall.Fee.Add(meter.Overall.Fee)
|
||||
|
||||
critical.Amount = critical.Amount.Add(meter.Critical.Amount)
|
||||
critical.Fee = critical.Fee.Add(meter.Critical.Fee)
|
||||
|
||||
peak.Amount = peak.Amount.Add(meter.Peak.Amount)
|
||||
peak.Fee = peak.Fee.Add(meter.Peak.Fee)
|
||||
|
||||
flat.Amount = flat.Amount.Add(meter.Flat.Amount)
|
||||
flat.Fee = flat.Fee.Add(meter.Flat.Fee)
|
||||
|
||||
valley.Amount = valley.Amount.Add(meter.Valley.Amount)
|
||||
valley.Fee = valley.Fee.Add(meter.Valley.Fee)
|
||||
|
||||
basicPooled = basicPooled.Add(meter.PooledBasic.Fee)
|
||||
adjustPooled = adjustPooled.Add(meter.PooledAdjust.Fee)
|
||||
|
||||
lossAmount = lossAmount.Add(meter.AdjustLoss.Amount)
|
||||
lossPooled = lossPooled.Add(meter.PooledLoss.Fee)
|
||||
|
||||
publicPooled = publicPooled.Add(meter.PooledPublic.Fee)
|
||||
}
|
||||
|
||||
// 反写商户表计的统计数据
|
||||
for _, meter := range relatedMeters {
|
||||
meter.Overall.Proportion = meter.Overall.Amount.Div(overall.Amount)
|
||||
meter.Critical.Proportion = meter.Critical.Amount.Div(critical.Amount)
|
||||
meter.Peak.Proportion = meter.Peak.Amount.Div(peak.Amount)
|
||||
meter.Flat.Proportion = meter.Flat.Amount.Div(flat.Amount)
|
||||
meter.Valley.Proportion = meter.Valley.Amount.Div(valley.Amount)
|
||||
meter.PooledBasic.Proportion = meter.PooledBasic.Fee.Div(basicPooled)
|
||||
meter.PooledAdjust.Proportion = meter.PooledAdjust.Fee.Div(adjustPooled)
|
||||
meter.PooledLoss.Proportion = meter.PooledLoss.Fee.Div(lossPooled)
|
||||
meter.PooledPublic.Proportion = meter.PooledPublic.Fee.Div(publicPooled)
|
||||
}
|
||||
|
||||
// 构造并添加商户的合计信息
|
||||
tenementCharges = append(tenementCharges, TenementCharges{
|
||||
Tenement: t.Tenement.ID,
|
||||
Overall: ConsumptionUnit{
|
||||
Price: summary.Overall.Price,
|
||||
Proportion: overall.Amount.Div(summary.Overall.Amount),
|
||||
Amount: overall.Amount,
|
||||
Fee: overall.Fee,
|
||||
},
|
||||
Critical: ConsumptionUnit{
|
||||
Price: summary.Critical.Price,
|
||||
Proportion: critical.Amount.Div(summary.Critical.Amount),
|
||||
Amount: critical.Amount,
|
||||
Fee: critical.Fee,
|
||||
},
|
||||
Peak: ConsumptionUnit{
|
||||
Price: summary.Peak.Price,
|
||||
Proportion: peak.Amount.Div(summary.Peak.Amount),
|
||||
Amount: peak.Amount,
|
||||
Fee: peak.Fee,
|
||||
},
|
||||
Flat: ConsumptionUnit{
|
||||
Price: summary.Flat.Price,
|
||||
Proportion: flat.Amount.Div(summary.Flat.Amount),
|
||||
Amount: flat.Amount,
|
||||
Fee: flat.Fee,
|
||||
},
|
||||
Valley: ConsumptionUnit{
|
||||
Price: summary.Valley.Price,
|
||||
Proportion: valley.Amount.Div(summary.Valley.Amount),
|
||||
Amount: valley.Amount,
|
||||
Fee: valley.Fee,
|
||||
},
|
||||
Loss: ConsumptionUnit{
|
||||
Price: summary.AuthorizeLoss.Price,
|
||||
Proportion: lossAmount.Div(summary.AuthorizeLoss.Amount),
|
||||
Amount: lossAmount,
|
||||
Fee: lossPooled,
|
||||
},
|
||||
BasicFee: basicPooled,
|
||||
AdjustFee: adjustPooled,
|
||||
LossPooled: lossPooled,
|
||||
PublicPooled: publicPooled,
|
||||
FinalCharges: overall.Fee.Add(basicPooled).Add(adjustPooled).Add(lossPooled).Add(publicPooled),
|
||||
Submeters: relatedMeters,
|
||||
Poolings: make([]Meter, 0), // TODO: Add pooling logic here
|
||||
})
|
||||
}
|
||||
|
||||
return tenementCharges, nil
|
||||
}
|
Reference in New Issue
Block a user