package service import ( "electricity_bill_calc/config" "electricity_bill_calc/exceptions" "electricity_bill_calc/global" "electricity_bill_calc/model" "electricity_bill_calc/tools" "fmt" "time" "github.com/fufuok/utils" "github.com/google/uuid" "github.com/samber/lo" "github.com/shopspring/decimal" "xorm.io/builder" ) type _ReportService struct{} var ReportService _ReportService func (_ReportService) FetchParksWithNewestReport(uid string) ([]model.ParkNewestReport, error) { parks := make([]model.ParkNewestReport, 0) err := global.DBConn. Alias("p"). Join("LEFT", []string{"report", "r"}, "r.park_id=p.id"). Where(builder.Eq{"p.user_id": uid, "p.enabled": true}). Find(&parks) if err != nil { return make([]model.ParkNewestReport, 0), err } reducedParks := lo.Reduce( parks, func(acc map[string]model.ParkNewestReport, elem model.ParkNewestReport, index int) map[string]model.ParkNewestReport { if v, ok := acc[elem.Park.Id]; ok { if elem.Report != nil { if v.Report == nil || (elem.Report.Period.After(v.Report.Period)) { acc[elem.Park.Id] = elem } } } else { acc[elem.Park.Id] = elem } return acc }, make(map[string]model.ParkNewestReport, 0), ) return lo.Values(reducedParks), nil } func (_ReportService) IsNewPeriodValid(uid string, period time.Time) (bool, error) { reports := make([]model.Report, 0) err := global.DBConn. Table("report").Alias("r"). Join("INNER", []string{"park", "p"}, "r.park_id=p.id"). Where(builder.Eq{"p.user_id": uid}). Find(&reports) if err != nil { return false, nil } // 检查给定的期数在目前的记录中是否已经存在 exists := lo.Reduce( reports, func(acc bool, elem model.Report, index int) bool { if elem.Period.Equal(period) { return acc || true } else { return acc || false } }, false, ) if exists { return false, nil } // 检查给定的期数与目前已发布的最大期数的关系 maxPublished := lo.Reduce( reports, func(acc *time.Time, elem model.Report, index int) *time.Time { if elem.Published { if acc == nil || (acc != nil && elem.Period.After(*acc)) { return &elem.Period } } return acc }, nil, ) // 检查给定的期数与目前未发布的最大期数的关系 maxUnpublished := lo.Reduce( reports, func(acc *time.Time, elem model.Report, index int) *time.Time { if acc == nil || (acc != nil && elem.Period.After(*acc)) { return &elem.Period } return acc }, nil, ) if maxUnpublished == nil { return true, nil } if maxPublished != nil && maxUnpublished.Equal(*maxPublished) { // 此时不存在未发布的报表 return tools.IsNextMonth(*maxPublished, period), nil } else { // 存在未发布的报表 return false, nil } } func (_ReportService) InitializeNewReport(parkId string, period time.Time) (string, error) { periods := make([]model.Report, 0) err := global.DBConn. Table("report"). Where(builder.Eq{"park_id": parkId, "published": true}). Asc("period"). Find(&periods) if err != nil { return "", err } // 获取上一期的报表索引信息 maxPublishedReport := lo.Reduce( periods, func(acc *model.Report, elem model.Report, index int) *model.Report { if acc == nil || (acc != nil && elem.Period.After(acc.Period)) { return &elem } return acc }, nil, ) var indexedLastPeriodCustomers map[string]model.EndUserDetail if maxPublishedReport != nil { // 获取上一期的所有户表信息,并获取当前已启用的所有用户 lastPeriodCustomers := make([]model.EndUserDetail, 0) err = global.DBConn.Where(builder.Eq{"report_id": maxPublishedReport.Id}).Find(&lastPeriodCustomers) if err != nil { return "", err } indexedLastPeriodCustomers = lo.Reduce( lastPeriodCustomers, func(acc map[string]model.EndUserDetail, elem model.EndUserDetail, index int) map[string]model.EndUserDetail { acc[elem.MeterId] = elem return acc }, make(map[string]model.EndUserDetail, 0), ) } else { indexedLastPeriodCustomers = make(map[string]model.EndUserDetail, 0) } currentActivatedCustomers := make([]model.Meter04KV, 0) err = global.DBConn.Where(builder.Eq{"park_id": parkId, "enabled": true}).Find(¤tActivatedCustomers) if err != nil { return "", err } var parkInfo = new(model.Park) has, err := global.DBConn.ID(parkId).NoAutoCondition().Get(parkInfo) if err != nil || !has { return "", exceptions.NewNotFoundError(fmt.Sprintf("指定园区未找到, %v", err)) } // 生成新一期的报表 tx := global.DBConn.NewSession() if err = tx.Begin(); err != nil { return "", err } defer tx.Close() // 插入已经生成的报表索引信息和园区概况信息 newReport := model.Report{ Id: uuid.New().String(), ParkId: parkId, Period: period, Category: parkInfo.Category, SubmeterType: parkInfo.SubmeterType, StepState: model.NewSteps(), Published: false, Withdraw: model.REPORT_NOT_WITHDRAW, } newReportSummary := model.ReportSummary{ ReportId: newReport.Id, } _, err = tx.Insert(newReport, newReportSummary) if err != nil { tx.Rollback() return "", err } // 生成并插入户表信息 for _, customer := range currentActivatedCustomers { newEndUser := model.EndUserDetail{ ReportId: newReport.Id, ParkId: parkId, MeterId: customer.Code, Seq: customer.Seq, Ratio: customer.Ratio, Address: customer.Address, CustomerName: customer.CustomerName, ContactName: customer.ContactName, ContactPhone: customer.ContactPhone, IsPublicMeter: customer.IsPublicMeter, WillDilute: customer.WillDilute, LastPeriodOverall: decimal.Zero, LastPeriodCritical: decimal.Zero, LastPeriodPeak: decimal.Zero, LastPeriodFlat: decimal.Zero, LastPeriodValley: decimal.Zero, } if lastPeriod, ok := indexedLastPeriodCustomers[customer.Code]; ok { newEndUser.LastPeriodOverall = lastPeriod.CurrentPeriodOverall newEndUser.LastPeriodCritical = lastPeriod.CurrentPeriodCritical newEndUser.LastPeriodPeak = lastPeriod.CurrentPeriodPeak newEndUser.LastPeriodFlat = lastPeriod.CurrentPeriodFlat newEndUser.LastPeriodValley = lastPeriod.CurrentPeriodValley } _, err = tx.Insert(newEndUser) if err != nil { tx.Rollback() return "", err } } err = tx.Commit() if err != nil { tx.Rollback() return "", err } return newReport.Id, nil } func (_ReportService) RetreiveReportIndex(rid string) (*model.Report, error) { reports := make([]model.Report, 0) err := global.DBConn.Where(builder.Eq{"id": rid}).Find(&reports) if err != nil { return nil, err } if len(reports) > 0 { return &reports[0], nil } else { return nil, nil } } func (_ReportService) RetreiveReportSummary(rid string) (*model.ReportSummary, error) { var summary = new(model.ReportSummary) _, err := global.DBConn.ID(rid).NoAutoCondition().Get(summary) if err != nil { return nil, err } return summary, nil } func (_ReportService) UpdateReportSummary(summary *model.ReportSummary) error { _, err := global.DBConn.ID(summary.ReportId).Cols("overall", "overall_fee", "critical", "critical_fee", "peak", "peak_fee", "valley", "valley_fee", "basic_fee", "adjust_fee").Update(summary) return err } func (_ReportService) CalculateSummaryAndFinishStep(reportId string) error { var report = new(model.Report) has, err := global.DBConn.ID(reportId).NoAutoCondition().Get(report) if err != nil { return err } if !has { return exceptions.NewNotFoundError("未找到指定报表") } var summary = new(model.ReportSummary) has, err = global.DBConn.ID(reportId).NoAutoCondition().Get(summary) if err != nil { return err } if !has { return exceptions.NewNotFoundError("未找到指定报表的园区概况") } tx := global.DBConn.NewSession() if err = tx.Begin(); err != nil { return err } defer tx.Close() summary.CalculatePrices() _, err = tx.ID(summary.ReportId).Cols("overall_price", "critical_price", "peak_price", "flat", "flat_fee", "flat_price", "valley_price").Update(summary) if err != nil { tx.Rollback() return err } report.StepState.Summary = true _, err = tx.ID(report.Id).Cols("step_state").Update(report) if err != nil { tx.Rollback() return err } err = tx.Commit() if err != nil { tx.Rollback() return err } return nil } func (_ReportService) FetchWillDulutedMaintenanceFees(reportId string) ([]model.WillDilutedFee, error) { fees := make([]model.WillDilutedFee, 0) err := global.DBConn.Where(builder.Eq{"report_id": reportId}).Asc("created_at").Find(&fees) if err != nil { return make([]model.WillDilutedFee, 0), nil } return fees, nil } func (_ReportService) CreateTemporaryWillDilutedMaintenanceFee(fee model.WillDilutedFee) error { fee.Id = utils.UUIDString() _, err := global.DBConn.Insert(fee) return err } func (_ReportService) BatchSaveMaintenanceFee(reportId string, fees []model.WillDilutedFee) error { tx := global.DBConn.NewSession() if err := tx.Begin(); err != nil { return err } defer tx.Close() // 首先删除所有预定义的部分,条件是指定报表ID,SourceID不为空。 cond := builder.Eq{"report_id": reportId}.And(builder.NotNull{"source_id"}) _, err := tx.Table(new(model.WillDilutedFee)).Where(cond).Delete() if err != nil { tx.Rollback() return err } // 然后插入新的记录 _, err = tx.Insert(fees) if err != nil { return err } err = tx.Commit() if err != nil { tx.Rollback() return err } return nil } func (_ReportService) UpdateMaintenanceFee(feeId string, updates map[string]interface{}) (err error) { _, err = global.DBConn.Table(new(model.WillDilutedFee)).ID(feeId).Update(updates) return } func (_ReportService) DeleteWillDilutedFee(fee string) (err error) { _, err = global.DBConn.ID(fee).NoAutoCondition().Delete(new(model.WillDilutedFee)) return } func (_ReportService) ProgressReportWillDilutedFee(report model.Report) (err error) { report.StepState.WillDiluted = true _, err = global.DBConn.ID(report.Id).Cols("step_state").Update(report) return } func (_ReportService) ProgressReportRegisterEndUser(report model.Report) (err error) { report.StepState.Submeter = true _, err = global.DBConn.ID(report.Id).Cols("step_state").Update(report) return } func (_ReportService) ProgressReportCalculate(report model.Report) (err error) { report.StepState.Calculate = true _, err = global.DBConn.ID(report.Id).Cols("step_state").Update(report) return } func (_ReportService) RetreiveParkEndUserMeterType(reportId string) (int, error) { var types = make([]int, 0) err := global.DBConn. Table("park").Alias("p"). Join("INNER", []string{"report", "r"}, "r.park_id=p.id"). Where(builder.Eq{"r.id": reportId}). Select("p.meter_04kv_type"). Find(&types) if err != nil { return -1, err } if len(types) == 0 { return -1, nil } return types[0], nil } func (_ReportService) PublishReport(report model.Report) (err error) { report.Published = true report.PublishedAt = lo.ToPtr(time.Now()) report.StepState.Publish = true _, err = global.DBConn.ID(report.Id).Cols("step_state", "published", "published_at").Update(report) return } func (_ReportService) SearchReport(requestUser, requestPark, requestKeyword string, requestPeriod *time.Time, requestPage int) ([]model.JoinedReportForWithdraw, int64, error) { cond := builder.NewCond().And(builder.Eq{"r.published": true}) if len(requestUser) > 0 { cond = cond.And(builder.Eq{"u.id": requestUser}) } if len(requestPark) > 0 { cond = cond.And(builder.Eq{"p.id": requestPark}) } if requestPeriod != nil { cond = cond.And(builder.Eq{"r.period": *requestPeriod}) } if len(requestKeyword) > 0 { cond = cond.And( builder.Like{"u.name", requestKeyword}. Or(builder.Like{"p.name", requestKeyword}). Or(builder.Like{"u.abbr", requestKeyword}). Or(builder.Like{"p.abbr", requestKeyword}). Or(builder.Like{"u.address", requestKeyword}). Or(builder.Like{"p.address", requestKeyword}), ) } total, err := global.DBConn. Table("report").Alias("r"). Join("INNER", []string{"park", "p"}, "p.id=r.park_id"). Join("INNER", []string{"user_detail", "u"}, "u.id=p.user_id"). Where(cond). Count() if err != nil { return make([]model.JoinedReportForWithdraw, 0), -1, err } startItem := (requestPage - 1) * config.ServiceSettings.ItemsPageSize records := make([]model.JoinedReportForWithdraw, 0) err = global.DBConn. Table("report").Alias("r"). Join("INNER", []string{"park", "p"}, "p.id=r.park_id"). Join("INNER", []string{"user_detail", "u"}, "u.id=p.user_id"). Where(cond). Limit(config.ServiceSettings.ItemsPageSize, startItem). Find(&records) return records, total, err } func (_ReportService) AssembleReportPublicity(reportId string) (*model.Publicity, error) { // 资料准备 var reportIndex = new(model.Report) has, err := global.DBConn.ID(reportId).NoAutoCondition().Get(reportIndex) if err != nil || !has { return nil, exceptions.NewNotFoundErrorFromError("未找到指定的公示报表", err) } var summary = new(model.ReportSummary) has, err = global.DBConn.ID(reportId).NoAutoCondition().Get(summary) if err != nil || !has { return nil, exceptions.NewNotFoundErrorFromError("未找到指定的公示报表概览", err) } var maintenanceFeeRecords = make([]model.WillDilutedFee, 0) err = global.DBConn.Where(builder.Eq{"report_id": reportId}).Find(&maintenanceFeeRecords) if err != nil { return nil, exceptions.NewNotFoundErrorFromError("未能获取到公示报表对应的待摊薄费用信息", err) } var endUserDetails = make([]model.EndUserDetail, 0) err = global.DBConn.Where(builder.Eq{"report_id": reportId}).Find(&endUserDetails) if err != nil { return nil, exceptions.NewNotFoundErrorFromError("未获取到公示报表对应的终端用户抄表信息", err) } parkDetail, err := ParkService.FetchParkDetail(reportIndex.ParkId) if err != nil { return nil, exceptions.NewNotFoundErrorFromError("未找到公示报表对应的园区信息", err) } userDetail, err := UserService.retreiveUserDetail(parkDetail.UserId) if err != nil { return nil, exceptions.NewNotFoundErrorFromError("未找到公示报表对应的企业信息", err) } // 组合数据 paidPart := model.PaidPart{ Overall: summary.Overall, OverallPrice: summary.OverallPrice.Decimal, ConsumptionFee: summary.ConsumptionFee.Decimal, OverallFee: summary.OverallFee, Critical: decimal.NewNullDecimal(summary.Critical), CriticalPrice: summary.CriticalPrice, CriticalFee: decimal.NewNullDecimal(summary.CriticalFee), Peak: decimal.NewNullDecimal(summary.Peak), PeakPrice: summary.PeakPrice, PeakFee: decimal.NewNullDecimal(summary.PeakFee), Flat: decimal.NewNullDecimal(summary.Flat), FlatPrice: summary.FlatPrice, FlatFee: decimal.NewNullDecimal(summary.FlatFee), Valley: decimal.NewNullDecimal(summary.Valley), ValleyPrice: summary.ValleyPrice, ValleyFee: decimal.NewNullDecimal(summary.ValleyFee), BasicFee: summary.BasicFee, AdjustFee: summary.AdjustFee, } endUserSummary := model.EndUserOverallPart{ Overall: summary.CustomerConsumption.Decimal, OverallPrice: summary.OverallPrice.Decimal, OverallFee: summary.CustomerConsumptionFee.Decimal, Critical: summary.CustomerConsumptionCritical, CriticalPrice: summary.CriticalPrice, CriticalFee: summary.CustomerConsumptionCriticalFee, Peak: summary.CustomerConsumptionPeak, PeakPrice: summary.PeakPrice, PeakFee: summary.CustomerConsumptionPeakFee, Flat: summary.CustomerConsumptionFlat, FlatPrice: summary.FlatPrice, FlatFee: summary.CustomerConsumptionFlatFee, Valley: summary.CustomerConsumptionValley, ValleyPrice: summary.ValleyPrice, ValleyFee: summary.CustomerConsumptionValleyFee, } lossPart := model.LossPart{ Quantity: summary.Loss.Decimal, Price: summary.OverallPrice.Decimal, ConsumptionFee: summary.LossFee.Decimal, Proportion: summary.LossProportion.Decimal, } publicSummary := model.PublicConsumptionOverallPart{ Overall: summary.PublicConsumption.Decimal, OverallPrice: summary.OverallPrice.Decimal, OverallFee: summary.PublicConsumptionFee.Decimal, Critical: summary.PublicConsumptionCritical, CriticalPrice: summary.CriticalPrice, CriticalFee: summary.PublicConsumptionCriticalFee, Peak: summary.PublicConsumptionPeak, PeakPrice: summary.PeakPrice, PeakFee: summary.PublicConsumptionPeakFee, Flat: summary.PublicConsumptionFlat, FlatPrice: summary.FlatPrice, FlatFee: summary.PublicConsumptionFlatFee, Valley: summary.PublicConsumptionValley, ValleyPrice: summary.ValleyPrice, ValleyFee: summary.PublicConsumptionValleyFee, } otherCollection := model.OtherShouldCollectionPart{ MaintenanceFee: summary.FinalDilutedOverall, BasicFees: summary.BasicFee.Add(summary.AdjustFee), } finalMaintenance := lossPart.ConsumptionFee.Add(publicSummary.ConsumptionFee).Add(otherCollection.MaintenanceFee.Decimal).Add(otherCollection.BasicFees) var maintenancePrice = decimal.Zero if !endUserSummary.Overall.Equal(decimal.Zero) { maintenancePrice = finalMaintenance.Div(endUserSummary.Overall).RoundBank(8) } var maintenanceProportion = decimal.Zero if !paidPart.OverallFee.Equal(decimal.Zero) || !otherCollection.MaintenanceFee.Decimal.Equal(decimal.Zero) { maintenanceProportion = finalMaintenance.Div(paidPart.OverallFee.Add(otherCollection.MaintenanceFee.Decimal)).RoundBank(8) } var priceRatio = decimal.Zero if !summary.OverallPrice.Decimal.Equal(decimal.Zero) { priceRatio = maintenancePrice.Div(summary.OverallPrice.Decimal).RoundBank(8) } maintenanceFees := model.MaintenancePart{ BasicFees: otherCollection.BasicFees, LossFee: lossPart.ConsumptionFee, PublicConsumptionFee: publicSummary.ConsumptionFee, MaintenanceFee: otherCollection.MaintenanceFee.Decimal, FinalMaintenance: finalMaintenance, MaintenanceProportion: maintenanceProportion, MaintenancePrice: maintenancePrice, PriceRatio: priceRatio, } endUsers := lo.Map( endUserDetails, func(elem model.EndUserDetail, index int) model.EndUserSummary { return model.EndUserSummary{ CustomerName: elem.CustomerName, Address: elem.Address, MeterId: elem.MeterId, Overall: elem.Overall.Decimal, OverallFee: elem.OverallFee.Decimal, Critical: elem.Critical, CriticalFee: elem.CriticalFee, Peak: elem.Peak, PeakFee: elem.PeakFee, Valley: elem.Valley, ValleyFee: elem.ValleyFee, Maintenance: elem.FinalDiluted.Decimal, } }, ) return &model.Publicity{ Report: *reportIndex, Park: *parkDetail, User: *userDetail, Paid: paidPart, EndUser: endUserSummary, Loss: lossPart, PublicConsumptionOverall: publicSummary, OtherCollections: otherCollection, Maintenance: maintenanceFees, EndUserDetails: endUsers, }, nil }