From 2792959d1e2b038f5562ef2ec2c474f639877170 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BE=90=E6=B6=9B?= Date: Mon, 26 Jun 2023 13:08:34 +0800 Subject: [PATCH] =?UTF-8?q?feat(report):=E5=9F=BA=E6=9C=AC=E5=AE=8C?= =?UTF-8?q?=E6=88=90=E6=8A=A5=E8=A1=A8=E6=9F=A5=E7=9C=8B=E9=83=A8=E5=88=86?= =?UTF-8?q?=E5=86=85=E5=AE=B9=E7=9A=84=E8=BF=81=E7=A7=BB=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- controller/report.go | 424 +++++++++++++++++++++++++++++++++++++++++++ model/report.go | 7 + router/router.go | 1 + vo/meter.go | 22 ++- vo/report.go | 247 +++++++++++++++++++++++++ vo/shares.go | 40 ++++ vo/tenement.go | 14 ++ 7 files changed, 754 insertions(+), 1 deletion(-) create mode 100644 controller/report.go diff --git a/controller/report.go b/controller/report.go new file mode 100644 index 0000000..d22d9cd --- /dev/null +++ b/controller/report.go @@ -0,0 +1,424 @@ +package controller + +import ( + "electricity_bill_calc/logger" + "electricity_bill_calc/model" + "electricity_bill_calc/repository" + "electricity_bill_calc/response" + "electricity_bill_calc/security" + "electricity_bill_calc/service" + "electricity_bill_calc/tools" + "electricity_bill_calc/types" + "electricity_bill_calc/vo" + + "github.com/gofiber/fiber/v2" + "github.com/jinzhu/copier" + "github.com/samber/lo" + "go.uber.org/zap" +) + +var reportLog = logger.Named("Handler", "Report") + +func InitializeReportHandlers(router *fiber.App) { + router.Get("/repotrs", security.MustAuthenticated, reportComprehensiveSearch) + router.Post("/report", security.EnterpriseAuthorize, initNewReportCalculateTask) + router.Get("/report/draft", security.EnterpriseAuthorize, listDraftReportIndicies) + router.Post("/report/calcualte", security.EnterpriseAuthorize, testCalculateReportSummary) + router.Get("/report/calculate/status", security.EnterpriseAuthorize, listCalculateTaskStatus) + router.Get("/report/:rid", security.EnterpriseAuthorize, getReportDetail) + router.Put("/report/:rid", security.EnterpriseAuthorize, updateReportCalculateTask) + router.Post("/report/:rid/publish", security.EnterpriseAuthorize, publishReport) + router.Put("/report/:rid/calculate", security.EnterpriseAuthorize, initiateCalculateTask) + router.Get("/report/:rid/publics", security.MustAuthenticated, listPublicMetersInReport) + router.Get("/report/:rid/summary", security.MustAuthenticated, getReportSummary) + router.Get("/report/:rid/summary/filled", security.EnterpriseAuthorize, getParkFilledSummary) + router.Get("/report/:rid/pooled", security.MustAuthenticated, listPooledMetersInReport) + router.Get("/report/:rid/:code/submeters", security.MustAuthenticated, listSubmetersInPooledMeter) + router.Get("/report/:rid/tenement", security.MustAuthenticated, listTenementsInReport) + router.Get("/report/:rid/tenement/:tid", security.MustAuthenticated, getTenementDetailInReport) +} + +// 检查指定报表是否属于当前用户 +func checkReportBelongs(reportId string, log *zap.Logger, c *fiber.Ctx, result *response.Result) (bool, error) { + session, err := _retreiveSession(c) + if err != nil { + log.Error("无法获取当前用户的会话信息", zap.Error(err)) + return false, result.Unauthorized("无法获取当前用户的会话信息。") + } + ok, err := repository.ReportRepository.IsBelongsTo(reportId, session.Uid) + if err != nil { + log.Error("无法检查核算报表的所有权", zap.Error(err)) + return false, result.Error(fiber.StatusInternalServerError, "无法检查核算报表的所有权。") + } + if !ok { + log.Error("核算报表不属于当前用户") + return false, result.Forbidden("核算报表不属于当前用户。") + } + return true, nil +} + +// 获取当前登录用户下所有园区的尚未发布的核算报表索引 +func listDraftReportIndicies(c *fiber.Ctx) error { + result := response.NewResult(c) + session, err := _retreiveSession(c) + if err != nil { + reportLog.Error("无法获取当前用户的会话信息", zap.Error(err)) + return result.Unauthorized("无法获取当前用户的会话信息。") + } + reportLog.Info("检索指定用户下的未发布核算报表索引", zap.String("User", session.Uid)) + indicies, err := service.ReportService.ListDraftReportIndicies(session.Uid) + if err != nil { + reportLog.Error("无法获取当前用户的核算报表索引", zap.Error(err)) + return result.NotFound("当前用户下未找到核算报表索引。") + } + return result.Success( + "已经获取到指定用户的报表索引。", + fiber.Map{"reports": indicies}, + ) +} + +// 初始化一个新的核算任务 +func initNewReportCalculateTask(c *fiber.Ctx) error { + result := response.NewResult(c) + session, err := _retreiveSession(c) + if err != nil { + reportLog.Error("无法获取当前用户的会话信息", zap.Error(err)) + return result.Unauthorized("无法获取当前用户的会话信息。") + } + reportLog.Info("初始化指定用户的一个新核算任务", zap.String("User", session.Uid)) + var form vo.ReportCreationForm + if err := c.BodyParser(&form); err != nil { + reportLog.Error("无法解析创建核算报表的请求数据。", zap.Error(err)) + return result.BadRequest("无法解析创建核算报表的请求数据。") + } + if pass, err := checkParkBelongs(form.Park, reportLog, c, &result); !pass { + return err + } + ok, err := repository.ReportRepository.CreateReport(&form) + if err != nil { + reportLog.Error("无法创建核算报表", zap.Error(err)) + return result.Error(fiber.StatusInternalServerError, "无法创建核算报表。") + } + if !ok { + reportLog.Error("未能完成核算报表的保存。") + return result.NotAccept("未能完成核算报表的保存。") + } + return result.Success("已经成功创建核算报表。") +} + +// 更新指定的核算任务 +func updateReportCalculateTask(c *fiber.Ctx) error { + result := response.NewResult(c) + reportId := c.Params("rid") + if pass, err := checkReportBelongs(reportId, reportLog, c, &result); !pass { + return err + } + var form vo.ReportModifyForm + if err := c.BodyParser(&form); err != nil { + reportLog.Error("无法解析更新核算报表的请求数据。", zap.Error(err)) + return result.BadRequest("无法解析更新核算报表的请求数据。") + } + ok, err := repository.ReportRepository.UpdateReportSummary(reportId, &form) + if err != nil { + reportLog.Error("无法更新核算报表", zap.Error(err)) + return result.Error(fiber.StatusInternalServerError, "无法更新核算报表。") + } + if !ok { + reportLog.Error("未能完成核算报表的更新。") + return result.NotAccept("未能完成核算报表的更新。") + } + return result.Success("已经成功更新核算报表。") +} + +// 启动指定的核算任务 +func initiateCalculateTask(c *fiber.Ctx) error { + result := response.NewResult(c) + reportId := c.Params("rid") + if pass, err := checkReportBelongs(reportId, reportLog, c, &result); !pass { + return err + } + err := service.ReportService.DispatchReportCalculate(reportId) + if err != nil { + reportLog.Error("无法启动核算报表计算任务", zap.Error(err)) + return result.Error(fiber.StatusInternalServerError, "无法启动核算报表计算任务。") + } + return result.Success("已经成功启动核算报表计算任务。") +} + +// 获取自己园区的已经填写的园区电量信息 +func getParkFilledSummary(c *fiber.Ctx) error { + result := response.NewResult(c) + reportId := c.Params("rid") + if pass, err := checkReportBelongs(reportId, reportLog, c, &result); !pass { + return err + } + reportLog.Info("获取园区电量信息", zap.String("Report", reportId)) + summary, err := repository.ReportRepository.RetrieveReportSummary(reportId) + if err != nil { + reportLog.Error("无法获取核算报表的园区电量信息", zap.Error(err)) + return result.Error(fiber.StatusInternalServerError, "无法获取核算报表的园区电量信息。") + } + if summary == nil { + reportLog.Error("未找到核算报表的园区电量信息") + return result.NotFound("未找到核算报表的园区电量信息。") + } + var summaryResponse vo.SimplifiedReportSummary + copier.Copy(&summaryResponse, summary) + return result.Success( + "已经获取到核算报表的园区电量信息。", + fiber.Map{"summary": summaryResponse}, + ) +} + +// 对提供的园区电量信息进行试计算,返回试计算结果 +func testCalculateReportSummary(c *fiber.Ctx) error { + result := response.NewResult(c) + reportLog.Info("试计算园区电量信息") + var form vo.TestCalculateForm + if err := c.BodyParser(&form); err != nil { + reportLog.Error("无法解析试计算核算报表的请求数据。", zap.Error(err)) + return result.BadRequest("无法解析试计算核算报表的请求数据。") + } + return result.Success( + "电量电费试计算已经完成。", + fiber.Map{"summary": form.Calculate()}, + ) +} + +// 获取指定园区中尚未发布的核算报表计算状态 +func listCalculateTaskStatus(c *fiber.Ctx) error { + result := response.NewResult(c) + session, err := _retreiveSession(c) + if err != nil { + reportLog.Error("无法获取当前用户的会话信息", zap.Error(err)) + return result.Unauthorized("无法获取当前用户的会话信息。") + } + status, err := repository.ReportRepository.GetReportTaskStatus(session.Uid) + if err != nil { + reportLog.Error("无法获取核算报表计算状态", zap.Error(err)) + return result.Error(fiber.StatusInternalServerError, "无法获取核算报表计算状态。") + } + var statusResponse []*vo.ReportCalculateTaskStatusResponse + copier.Copy(&statusResponse, &status) + return result.Success( + "已经获取到核算报表计算状态。", + fiber.Map{"status": statusResponse}, + ) +} + +// 获取指定报表的详细信息 +func getReportDetail(c *fiber.Ctx) error { + result := response.NewResult(c) + reportId := c.Params("rid") + reportLog.Info("获取核算报表的详细信息", zap.String("Report", reportId)) + user, park, report, err := service.ReportService.RetrieveReportIndexDetail(reportId) + if err != nil { + reportLog.Error("无法获取核算报表的详细信息", zap.Error(err)) + return result.NotFound("无法获取核算报表的详细信息。") + } + return result.Success( + "已经获取到核算报表的详细信息。", + fiber.Map{ + "detail": vo.NewReportDetailQueryResponse(user, park, report), + }, + ) +} + +// 获取指定核算报表的总览信息 +func getReportSummary(c *fiber.Ctx) error { + result := response.NewResult(c) + reportId := c.Params("rid") + report, err := repository.ReportRepository.RetrieveReportSummary(reportId) + if err != nil { + reportLog.Error("无法获取核算报表的总览信息", zap.Error(err)) + return result.Error(fiber.StatusInternalServerError, "无法获取核算报表的总览信息。") + } + if report == nil { + reportLog.Error("未找到核算报表的总览信息") + return result.NotFound("未找到核算报表的总览信息。") + } + var summaryResponse vo.ParkSummaryResponse + copier.Copy(&summaryResponse, report) + return result.Success( + "已经获取到核算报表的总览信息。", + fiber.Map{"summary": summaryResponse}, + ) +} + +// 获取指定报表中分页的公共表计的核算摘要信息 +func listPublicMetersInReport(c *fiber.Ctx) error { + result := response.NewResult(c) + reportId := c.Params("rid") + reportLog.Info("获取核算报表中的公共表计信息", zap.String("Report", reportId)) + page := c.QueryInt("page", 1) + keyword := tools.EmptyToNil(c.Query("keyword")) + meters, total, err := repository.ReportRepository.ListPublicMetersInReport(reportId, uint(page), keyword) + if err != nil { + reportLog.Error("无法获取核算报表中的公共表计信息", zap.Error(err)) + return result.Error(fiber.StatusInternalServerError, "无法获取核算报表中的公共表计信息。") + } + meterResponse := lo.Map(meters, func(meter *model.ReportDetailedPublicConsumption, _ int) *vo.ReportPublicQueryResponse { + m := &vo.ReportPublicQueryResponse{} + m.FromReportDetailPublicConsumption(meter) + return m + }) + return result.Success( + "已经获取到指定核算报表中的分页公共表计的核算信息。", + response.NewPagedResponse(page, total).ToMap(), + fiber.Map{"public": meterResponse}, + ) +} + +// 获取指定报表中的分页的公摊表计的核算摘要信息 +func listPooledMetersInReport(c *fiber.Ctx) error { + result := response.NewResult(c) + reportId := c.Params("rid") + reportLog.Info("获取核算报表中的公摊表计信息", zap.String("Report", reportId)) + page := c.QueryInt("page", 1) + keyword := tools.EmptyToNil(c.Query("keyword")) + meters, total, err := repository.ReportRepository.ListPooledMetersInReport(reportId, uint(page), keyword) + if err != nil { + reportLog.Error("无法获取核算报表中的公摊表计信息", zap.Error(err)) + return result.Error(fiber.StatusInternalServerError, "无法获取核算报表中的公摊表计信息。") + } + meterResponse := lo.Map(meters, func(meter *model.ReportDetailedPooledConsumption, _ int) *vo.ReportPooledQueryResponse { + m := &vo.ReportPooledQueryResponse{} + m.FromReportDetailPooledConsumption(meter) + return m + }) + return result.Success( + "已经获取到指定核算报表中的分页公摊表计的核算信息。", + response.NewPagedResponse(page, total).ToMap(), + fiber.Map{"pooled": meterResponse}, + ) +} + +// 列出指定报表中指定公共表计下各个分摊表计的消耗数据 +func listSubmetersInPooledMeter(c *fiber.Ctx) error { + result := response.NewResult(c) + reportId := c.Params("rid") + meterId := c.Params("code") + if len(meterId) == 0 { + reportLog.Error("未提供公共表计的编号") + return result.BadRequest("未提供公共表计的编号。") + } + meters, err := repository.ReportRepository.ListPooledMeterDetailInReport(reportId, meterId) + if err != nil { + reportLog.Error("无法获取核算报表中的公共表计信息", zap.Error(err)) + return result.Error(fiber.StatusInternalServerError, "无法获取核算报表中的公共表计信息。") + } + meterResponse := lo.Map(meters, func(meter *model.ReportDetailNestedMeterConsumption, _ int) *vo.ReportPooledQueryResponse { + m := &vo.ReportPooledQueryResponse{} + m.FromReportDetailNestedMeterConsumption(meter) + return m + }) + return result.Success( + "已经获取到指定核算报表中的公共表计的核算信息。", + fiber.Map{"meters": meterResponse}, + ) +} + +// 获取指定报表中分页的商户核算电量电费概要数据 +func listTenementsInReport(c *fiber.Ctx) error { + result := response.NewResult(c) + reportId := c.Params("rid") + page := c.QueryInt("page", 1) + keyword := tools.EmptyToNil(c.Query("keyword")) + tenements, total, err := repository.ReportRepository.ListTenementInReport(reportId, uint(page), keyword) + if err != nil { + reportLog.Error("无法获取核算报表中的商户信息", zap.Error(err)) + return result.Error(fiber.StatusInternalServerError, "无法获取核算报表中的商户信息。") + } + tenementsResponse := lo.Map(tenements, func(tenement *model.ReportTenement, _ int) *vo.ReportTenementSummaryResponse { + t := &vo.ReportTenementSummaryResponse{} + t.FromReportTenement(tenement) + return t + }) + return result.Success( + "已经获取到指定核算报表中的分页商户的核算信息。", + response.NewPagedResponse(page, total).ToMap(), + fiber.Map{"tenements": tenementsResponse}, + ) +} + +// 获取指定报表中指定商户的详细核算信息 +func getTenementDetailInReport(c *fiber.Ctx) error { + result := response.NewResult(c) + reportId := c.Params("rid") + tenementId := c.Params("tid") + detail, err := repository.ReportRepository.GetTenementDetailInReport(reportId, tenementId) + if err != nil { + reportLog.Error("无法获取核算报表中的商户信息", zap.Error(err)) + return result.Error(fiber.StatusInternalServerError, "无法获取核算报表中的商户信息。") + } + var detailResponse vo.ReportTenementDetailResponse + detailResponse.FromReportTenement(detail) + return result.Success( + "已经获取到指定核算报表中的商户的详细核算信息。", + fiber.Map{"detail": detailResponse}, + ) +} + +// 发布指定的核算报表 +func publishReport(c *fiber.Ctx) error { + result := response.NewResult(c) + reportId := c.Params("rid") + if pass, err := checkReportBelongs(reportId, reportLog, c, &result); !pass { + return err + } + ok, err := repository.ReportRepository.PublishReport(reportId) + if err != nil { + reportLog.Error("无法发布核算报表", zap.Error(err)) + return result.Error(fiber.StatusInternalServerError, "发布核算报表出错。") + } + if !ok { + reportLog.Error("未能完成核算报表的发布。") + return result.NotAccept("未能完成核算报表的发布。") + } + return result.Success("已经成功发布核算报表。") +} + +// 对核算报表进行综合检索 +func reportComprehensiveSearch(c *fiber.Ctx) error { + result := response.NewResult(c) + user := tools.EmptyToNil(c.Query("user")) + session, err := _retreiveSession(c) + if err != nil { + reportLog.Error("无法获取当前用户的会话信息", zap.Error(err)) + return result.Unauthorized("无法获取当前用户的会话信息。") + } + park := tools.EmptyToNil(c.Query("park_id")) + if session.Type == model.USER_TYPE_ENT && park != nil && len(*park) > 0 { + if pass, err := checkParkBelongs(*park, reportLog, c, &result); !pass { + return err + } + } + var requestUser *string + if session.Type == model.USER_TYPE_ENT { + requestUser = lo.ToPtr(tools.DefaultTo(user, session.Uid)) + } else { + requestUser = user + } + page := c.QueryInt("page", 1) + keyword := tools.EmptyToNil(c.Query("keyword")) + startDate, err := types.ParseDatep(c.Query("period_start")) + if err != nil { + reportLog.Error("无法解析核算报表查询的开始日期", zap.Error(err)) + return result.BadRequest("无法解析核算报表查询的开始日期。") + } + endDate, err := types.ParseDatep(c.Query("period_end")) + if err != nil { + reportLog.Error("无法解析核算报表查询的结束日期", zap.Error(err)) + return result.BadRequest("无法解析核算报表查询的结束日期。") + } + reports, total, err := service.ReportService.QueryReports(requestUser, park, uint(page), keyword, startDate, endDate) + if err != nil { + reportLog.Error("无法查询核算报表", zap.Error(err)) + return result.Error(fiber.StatusInternalServerError, "无法查询核算报表。") + } + return result.Success( + "已经获取到指定核算报表的分页信息。", + response.NewPagedResponse(page, total).ToMap(), + fiber.Map{"reports": reports}, + ) +} diff --git a/model/report.go b/model/report.go index 5b58f60..fb296d2 100644 --- a/model/report.go +++ b/model/report.go @@ -52,6 +52,13 @@ type ReportSummary struct { FinalDilutedOverall decimal.NullDecimal `json:"finalDilutedOverall" db:"final_diluted_overall"` } +func (rs ReportSummary) GetConsumptionFee() decimal.Decimal { + if !rs.ConsumptionFee.Valid { + return rs.Overall.Fee.Sub(rs.BasicFee).Sub(rs.AdjustFee) + } + return rs.ConsumptionFee.Decimal +} + type ReportPublicConsumption struct { ReportId string `json:"reportId" db:"report_id"` MeterId string `json:"parkMeterId" db:"park_meter_id"` diff --git a/router/router.go b/router/router.go index ac51adb..1e268f6 100644 --- a/router/router.go +++ b/router/router.go @@ -51,6 +51,7 @@ func App() *fiber.App { controller.InitializeMeterHandlers(app) controller.InitializeInvoiceHandler(app) controller.InitializeTopUpHandlers(app) + controller.InitializeReportHandlers(app) return app } diff --git a/vo/meter.go b/vo/meter.go index 0c84e79..626bb89 100644 --- a/vo/meter.go +++ b/vo/meter.go @@ -1,6 +1,10 @@ package vo -import "github.com/shopspring/decimal" +import ( + "electricity_bill_calc/types" + + "github.com/shopspring/decimal" +) type MeterCreationForm struct { Code string `json:"code"` @@ -42,3 +46,19 @@ type SimplifiedMeterQueryResponse struct { Address *string `json:"address"` Park string `json:"parkId"` } + +type SimplifiedMeterDetailResponse struct { + Code string `json:"code"` + Park string `json:"parkId"` + Address *string `json:"address"` + Seq int64 `json:"seq"` + Ratio decimal.Decimal `json:"ratio"` + Building *string `json:"building"` + BuildingName *string `json:"buildingName"` + OnFloor *string `json:"onFloor"` + Area decimal.Decimal `json:"area"` + Enabled bool `json:"enabled"` + MeterType int16 `json:"meterType"` + AttachedAt types.DateTime `json:"attachedAt"` + DetachedAt *types.DateTime `json:"detachedAt"` +} diff --git a/vo/report.go b/vo/report.go index 9bb1c6f..6ad2f5f 100644 --- a/vo/report.go +++ b/vo/report.go @@ -1,8 +1,10 @@ package vo import ( + "electricity_bill_calc/model" "electricity_bill_calc/types" + "github.com/jinzhu/copier" "github.com/shopspring/decimal" ) @@ -70,3 +72,248 @@ type ComprehensiveReportQueryResponse struct { Park SimplifiedParkDetail `json:"park"` User SimplifiedUserDetail `json:"user"` } + +type BasicReportIndexResponse struct { + Id string `json:"id"` + Park string `json:"park_id"` + PeriodBegin types.Date `json:"period_begin"` + PeriodEnd types.Date `json:"period_end"` + Category int16 `json:"category"` + MeterType int16 `json:"meter04kvType"` + PricePolicy int16 `json:"pricePolicy"` + BasisPooled int16 `json:"basisDiluted"` + AdjustPooled int16 `json:"adjustDiluted"` + LossPooled int16 `json:"lossDiluted"` + PublicPooled int16 `json:"publicDiluted"` + Published bool `json:"published"` + PublishedAt *types.DateTime `json:"published_at"` + Withdraw int16 `json:"withdraw"` + LastWithdrawAppliedAt *types.DateTime `json:"last_withdraw_applied_at"` + LastWithdrawAuditAt *types.DateTime `json:"last_withdraw_audit_at"` + Status int16 `json:"status"` + Message *string `json:"message"` + CreatedAt types.DateTime `json:"created_at"` + LastModifiedAt types.DateTime `json:"last_modified_at"` +} + +func (bri *BasicReportIndexResponse) Period(p types.DateRange) { + bri.PeriodBegin = p.SafeLower() + bri.PeriodEnd = p.SafeUpper() +} + +type ReportDetailQueryResponse struct { + Enterprise SimplifiedUserDetail `json:"enterprise"` + Park SimplifiedParkDetail `json:"park"` + Report BasicReportIndexResponse `json:"report"` +} + +func NewReportDetailQueryResponse(user *model.UserDetail, park *model.Park, report *model.ReportIndex) ReportDetailQueryResponse { + var response ReportDetailQueryResponse + copier.Copy(&response.Enterprise, user) + copier.Copy(&response.Park, park) + copier.Copy(&response.Report, report) + return response +} + +type ParkSummaryResponse struct { + Id string `json:"id"` + Overall ConsumptionDisplay `json:"overall"` + Area decimal.Decimal `json:"area"` + BasicFee decimal.Decimal `json:"basicFee"` + PooledBasicFeeByAmount decimal.Decimal `json:"pooledBasicFeeByAmount"` + PooledBasicFeeByArea decimal.Decimal `json:"pooledBasicFeeByArea"` + AdjustFee decimal.Decimal `json:"adjustFee"` + PooledAdjustFeeByAmount decimal.Decimal `json:"pooledAdjustFeeByAmount"` + PooledAdjustFeeByArea decimal.Decimal `json:"pooledAdjustFeeByArea"` + Consumption decimal.Decimal `json:"consumption"` + Loss decimal.Decimal `json:"loss"` + LossRate decimal.Decimal `json:"lossRate"` +} + +type SimplifiedReportSummary struct { + Overall model.ConsumptionUnit `json:"overall"` + Critical model.ConsumptionUnit `json:"critical"` + Peak model.ConsumptionUnit `json:"peak"` + Flat model.ConsumptionUnit `json:"flat"` + Valley model.ConsumptionUnit `json:"valley"` + BasicFee decimal.Decimal `json:"basicFee"` + AdjustFee decimal.Decimal `json:"adjustFee"` + ConsumptionFee decimal.Decimal `json:"consumptionFee" copier:"GetConsumptionFee"` +} + +type TestCalculateForm struct { + Overall decimal.Decimal `json:"overall"` + OverallFee decimal.Decimal `json:"overallFee"` + Critical decimal.Decimal `json:"critical"` + CriticalFee decimal.Decimal `json:"criticalFee"` + Peak decimal.Decimal `json:"peak"` + PeakFee decimal.Decimal `json:"peakFee"` + Valley decimal.Decimal `json:"valley"` + ValleyFee decimal.Decimal `json:"valleyFee"` + BasicFee decimal.Decimal `json:"basicFee"` + AdjustFee decimal.Decimal `json:"adjustFee"` +} + +type TestCalculateResult struct { + OverallPrice decimal.Decimal `json:"overallPrice"` + CriticalPrice decimal.Decimal `json:"criticalPrice"` + PeakPrice decimal.Decimal `json:"peakPrice"` + Flat decimal.Decimal `json:"flat"` + FlatFee decimal.Decimal `json:"flatFee"` + FlatPrice decimal.Decimal `json:"flatPrice"` + ValleyPrice decimal.Decimal `json:"valleyPrice"` + ConsumptionFee decimal.Decimal `json:"consumptionFee"` +} + +func (t TestCalculateForm) Calculate() TestCalculateResult { + var r TestCalculateResult = TestCalculateResult{} + r.ConsumptionFee = t.OverallFee.Sub(t.BasicFee).Sub(t.AdjustFee) + if t.Overall.GreaterThan(decimal.Zero) { + r.OverallPrice = r.ConsumptionFee.Div(t.Overall).RoundBank(8) + } + if t.Critical.GreaterThan(decimal.Zero) { + r.CriticalPrice = t.CriticalFee.Div(t.Critical).RoundBank(8) + } + if t.Peak.GreaterThan(decimal.Zero) { + r.PeakPrice = t.PeakFee.Div(t.Peak).RoundBank(8) + } + r.Flat = t.Overall.Sub(t.Critical).Sub(t.Peak).Sub(t.Valley) + r.FlatFee = r.ConsumptionFee.Sub(t.CriticalFee).Sub(t.PeakFee).Sub(t.ValleyFee).RoundBank(8) + if r.Flat.GreaterThan(decimal.Zero) { + r.FlatPrice = r.FlatFee.Div(r.Flat).RoundBank(8) + } + r.ConsumptionFee = r.ConsumptionFee.RoundBank(8) + return r +} + +type ReportCalculateTaskStatusResponse struct { + Id string `json:"id"` + Status int16 `json:"status"` + Message *string `json:"message"` +} + +type ReportPublicQueryResponse struct { + SimplifiedMeterQueryResponse + Overall ConsumptionDisplay `json:"overall"` + AdjustLoss ConsumptionDisplay `json:"adjustLoss"` +} + +func (rpqr *ReportPublicQueryResponse) FromReportDetailPublicConsumption(value *model.ReportDetailedPublicConsumption) { + copier.Copy(&rpqr.SimplifiedMeterQueryResponse, &value.MeterDetail) + rpqr.Overall = FromConsumptionUnit(&value.ReportPublicConsumption.Overall) + rpqr.Overall.Amount(value.ReportPublicConsumption.Overall.Amount.Add(value.ReportPublicConsumption.LossAdjust.Amount)) + rpqr.AdjustLoss = FromConsumptionUnit(&value.ReportPublicConsumption.LossAdjust) +} + +type ReportPooledQueryResponse struct { + SimplifiedMeterQueryResponse + Overall ConsumptionDisplay `json:"overall"` + PoolMethod int16 `json:"poolMethod"` +} + +func (rpqr *ReportPooledQueryResponse) FromReportDetailPooledConsumption(value *model.ReportDetailedPooledConsumption) { + copier.Copy(&rpqr.SimplifiedMeterQueryResponse, &value.MeterDetail) + rpqr.Overall = FromConsumptionUnit(&value.ReportPooledConsumption.Overall) + rpqr.PoolMethod = value.PublicPooled +} + +func (rqpr *ReportPooledQueryResponse) FromReportDetailNestedMeterConsumption(value *model.ReportDetailNestedMeterConsumption) { + copier.Copy(&rqpr.SimplifiedMeterQueryResponse, &value.Meter) + rqpr.Overall = FromConsumptionUnit(&value.Consumption.Overall) + rqpr.PoolMethod = -1 +} + +type ReportTenementSummaryResponse struct { + SimplifiedTenementDetailResponse + Consumption decimal.Decimal `json:"consumption"` + Fee decimal.Decimal `json:"fee"` + Pooled decimal.Decimal `json:"pooled"` + Total decimal.Decimal `json:"final"` +} + +func (rtsr *ReportTenementSummaryResponse) FromReportTenement(value *model.ReportTenement) { + copier.Copy(&rtsr.SimplifiedTenementDetailResponse, &value.Detail) + fee := value.BasicFeePooled.Add(value.AdjustFeePooled).Add(value.LossFeePooled) + rtsr.Consumption = value.Overall.Amount + rtsr.Fee = fee + rtsr.Pooled = value.FinalPooled + rtsr.Total = value.FinalCharge +} + +type ReportTenementComprehensiveDetailResponse struct { + Consumption decimal.Decimal `json:"consumption"` + Fee decimal.Decimal `json:"fee"` + Price decimal.Decimal `json:"price"` + BasicPooled decimal.Decimal `json:"basicPooled"` + AdjustPooled decimal.Decimal `json:"adjustPooled"` + LossPooled decimal.Decimal `json:"lossPooled"` + PublicPooled decimal.Decimal `json:"publicPooled"` + Total decimal.Decimal `json:"total"` +} + +func (rtcdr *ReportTenementComprehensiveDetailResponse) FromReportTenement(value *model.ReportTenement) { + rtcdr.Consumption = value.Overall.Amount + rtcdr.Fee = value.Overall.Fee + rtcdr.Price = value.Overall.Price + rtcdr.BasicPooled = value.BasicFeePooled + rtcdr.AdjustPooled = value.AdjustFeePooled + rtcdr.LossPooled = value.LossFeePooled + rtcdr.PublicPooled = value.FinalPooled + rtcdr.Total = value.FinalCharge +} + +type ReportMeterDetailResponse struct { + SimplifiedMeterDetailResponse + Overall ConsumptionDisplay `json:"overall"` + Critical ConsumptionDisplay `json:"critical"` + Peak ConsumptionDisplay `json:"peak"` + Flat ConsumptionDisplay `json:"flat"` + Valley ConsumptionDisplay `json:"valley"` +} + +func (rmdr *ReportMeterDetailResponse) FromNestedMeter(value *model.NestedMeter) { + copier.Copy(&rmdr.SimplifiedMeterDetailResponse, &value.MeterDetail) + rmdr.Overall = FromConsumptionUnit(&value.Overall) + rmdr.Critical = FromConsumptionUnit(&value.Critical) + rmdr.Peak = FromConsumptionUnit(&value.Peak) + rmdr.Flat = FromConsumptionUnit(&value.Flat) + rmdr.Valley = FromConsumptionUnit(&value.Valley) +} + +type ReportMeterExtendedDetailResponse struct { + ReportMeterDetailResponse + BasicPooled decimal.Decimal `json:"basicPooled"` + AdjustPooled decimal.Decimal `json:"adjustPooled"` + LossPooled decimal.Decimal `json:"lossPooled"` + PublicPooled decimal.Decimal `json:"publicPooled"` + FinalTotal decimal.Decimal `json:"finalTotal"` +} + +func (rmedr *ReportMeterExtendedDetailResponse) FromNestedMeter(value *model.NestedMeter) { + rmedr.ReportMeterDetailResponse.FromNestedMeter(value) + rmedr.BasicPooled = value.BasicPooled + rmedr.AdjustPooled = value.AdjustPooled + rmedr.LossPooled = value.LossPooled + rmedr.PublicPooled = value.PublicPooled + rmedr.FinalTotal = value.FinalTotal +} + +type ReportTenementDetailResponse struct { + Tenement SimplifiedTenementDetailResponse `json:"tenement"` + Comprehensive ReportTenementComprehensiveDetailResponse `json:"comprehensive"` + Meters []ReportMeterExtendedDetailResponse `json:"meters"` + Pooled []ReportMeterDetailResponse `json:"pooled"` +} + +func (rtdr *ReportTenementDetailResponse) FromReportTenement(value *model.ReportTenement) { + copier.Copy(&rtdr.Tenement, &value.Detail) + rtdr.Comprehensive.FromReportTenement(value) + rtdr.Meters = make([]ReportMeterExtendedDetailResponse, len(value.Meters)) + for i, v := range value.Meters { + rtdr.Meters[i].FromNestedMeter(&v) + } + rtdr.Pooled = make([]ReportMeterDetailResponse, len(value.Pooled)) + for i, v := range value.Pooled { + rtdr.Pooled[i].FromNestedMeter(&v) + } +} diff --git a/vo/shares.go b/vo/shares.go index 92d6f95..5612904 100644 --- a/vo/shares.go +++ b/vo/shares.go @@ -1,5 +1,45 @@ package vo +import ( + "electricity_bill_calc/model" + + "github.com/jinzhu/copier" + "github.com/shopspring/decimal" +) + type StateForm struct { Enabled bool `json:"enabled"` } + +type ConsumptionDisplay struct { + AmountStr string `json:"amount"` + FeeStr string `json:"fee"` + PriceStr string `json:"price"` + ProportionStr string `json:"proportion"` +} + +func (cd ConsumptionDisplay) Amount(a decimal.Decimal) ConsumptionDisplay { + cd.AmountStr = a.StringFixedBank(4) + return cd +} + +func (cd ConsumptionDisplay) Fee(f decimal.Decimal) ConsumptionDisplay { + cd.FeeStr = f.StringFixedBank(4) + return cd +} + +func (cd ConsumptionDisplay) Price(p decimal.Decimal) ConsumptionDisplay { + cd.PriceStr = p.StringFixedBank(8) + return cd +} + +func (cd ConsumptionDisplay) Proportion(p decimal.Decimal) ConsumptionDisplay { + cd.ProportionStr = p.StringFixedBank(8) + return cd +} + +func FromConsumptionUnit(cu *model.ConsumptionUnit) ConsumptionDisplay { + cd := &ConsumptionDisplay{} + copier.Copy(cd, cu) + return *cd +} diff --git a/vo/tenement.go b/vo/tenement.go index e5524a4..5963dec 100644 --- a/vo/tenement.go +++ b/vo/tenement.go @@ -59,3 +59,17 @@ type TenementDetailResponse struct { CreatedAt types.DateTime `json:"createdAt"` LastModifiedAt *types.DateTime `json:"lastModifiedAt"` } + +type SimplifiedTenementDetailResponse struct { + Id string `json:"id"` + FullName string `json:"fullName"` + ShortName *string `json:"shortName"` + Address string `json:"address"` + Contact string `json:"contact" copier:"ContactName"` + Phone string `json:"phone" copier:"ContactPhone"` + Building string `json:"building"` + BuildingName *string `json:"buildingName"` + OnFloor *string `json:"onFloor"` + MovedInAt *types.Date `json:"movedInAt"` + MovedOutAt *types.Date `json:"movedOutAt"` +}