diff --git a/.idea/0.2.iml b/.idea/0.2.iml new file mode 100644 index 0000000..5e764c4 --- /dev/null +++ b/.idea/0.2.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 0000000..3cdc6ae --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/dataSources.xml b/.idea/dataSources.xml new file mode 100644 index 0000000..0973c28 --- /dev/null +++ b/.idea/dataSources.xml @@ -0,0 +1,11 @@ + + + + + postgresql + true + org.postgresql.Driver + jdbc:postgresql://39.105.39.8:9432/postgres + + + \ No newline at end of file diff --git a/.idea/electricity_bill_calc_service.iml b/.idea/electricity_bill_calc_service.iml new file mode 100644 index 0000000..c956989 --- /dev/null +++ b/.idea/electricity_bill_calc_service.iml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..28a804d --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..92189f3 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/assets/meter_04kv_template.xlsx b/assets/meter_04kv_template.xlsx deleted file mode 100644 index c1cccde..0000000 Binary files a/assets/meter_04kv_template.xlsx and /dev/null differ diff --git a/config/settings.go b/config/settings.go index b8403ec..db3566c 100644 --- a/config/settings.go +++ b/config/settings.go @@ -36,12 +36,18 @@ type ServiceSetting struct { HostSerial int64 } +// 读取基准线损率 +type BaseLossSetting struct { + Base string +} + // 定义全局变量 var ( ServerSettings *ServerSetting DatabaseSettings *DatabaseSetting RedisSettings *RedisSetting ServiceSettings *ServiceSetting + BaseLoss *BaseLossSetting ) // 读取配置到全局变量 @@ -69,5 +75,10 @@ func SetupSetting() error { if err != nil { return err } + + err = s.ReadSection("BaselineLineLossRatio", &BaseLoss) + if err != nil { + return err + } return nil } diff --git a/controller/end_user.go b/controller/end_user.go deleted file mode 100644 index 9bb6449..0000000 --- a/controller/end_user.go +++ /dev/null @@ -1,237 +0,0 @@ -package controller - -import ( - "database/sql" - "electricity_bill_calc/excel" - "electricity_bill_calc/global" - "electricity_bill_calc/model" - "electricity_bill_calc/response" - "electricity_bill_calc/security" - "electricity_bill_calc/service" - "fmt" - "net/http" - "strconv" - - "github.com/gofiber/fiber/v2" - "github.com/samber/lo" - "github.com/shopspring/decimal" -) - -func InitializeEndUserController(router *fiber.App) { - router.Get("/report/:rid/submeter", security.EnterpriseAuthorize, fetchEndUserInReport) - router.Get("/report/:rid/meter/template", downloadEndUserRegisterTemplate) - router.Post("/report/:rid/meter/batch", security.EnterpriseAuthorize, uploadEndUserRegisterTemplate) - router.Put("/report/:rid/submeter/:pid/:mid", security.EnterpriseAuthorize, modifyEndUserRegisterRecord) - router.Get("/end/user/adjusts", security.MustAuthenticated, statEndUserInPeriod) -} - -func fetchEndUserInReport(c *fiber.Ctx) error { - result := response.NewResult(c) - requestReportId := c.Params("rid") - if ensure, err := ensureReportBelongs(c, &result, requestReportId); !ensure { - return err - } - keyword := c.Query("keyword") - requestPage, err := strconv.Atoi(c.Query("page", "1")) - if err != nil { - return result.NotAccept("查询参数[page]格式不正确。") - } - endUsers, totalItem, err := service.EndUserService.SearchEndUserRecord(requestReportId, keyword, requestPage) - if err != nil { - return result.NotFound(err.Error()) - } - return result.Json( - http.StatusOK, - "已获取到符合条件的终端用户集合", - response.NewPagedResponse(requestPage, totalItem).ToMap(), - fiber.Map{"meters": endUsers}, - ) -} - -func downloadEndUserRegisterTemplate(c *fiber.Ctx) error { - result := response.NewResult(c) - requestReportId := c.Params("rid") - users, err := service.EndUserService.AllEndUserRecord(requestReportId) - if err != nil { - return result.NotFound(err.Error()) - } - reportIndex, err := service.ReportService.RetreiveReportIndex(requestReportId) - if err != nil { - return result.NotFound(err.Error()) - } - park, err := service.ParkService.FetchParkDetail(reportIndex.ParkId) - if err != nil { - return result.NotFound(err.Error()) - } - meterType, err := service.ReportService.RetreiveParkEndUserMeterType(requestReportId) - if err != nil { - return result.Error(http.StatusInternalServerError, err.Error()) - } - - if meterType == -1 { - return result.NotFound("未能确定用户表计类型。") - } - - c.Status(http.StatusOK) - c.Set("Content-Type", "application/octet-stream") - c.Set("Content-Transfer-Encoding", "binary") - c.Set("Content-Disposition", fmt.Sprintf("attachment; filename=抄表记录-%s-%s.xlsx", park.Name, reportIndex.Period.Format("2006-01"))) - - gen := lo.Ternary[excel.ExcelTemplateGenerator]( - meterType == 0, - excel.NewMeterNonPVExcelTemplateGenerator(), - excel.NewMeterPVExcelTemplateGenerator(), - ) - defer gen.Close() - gen.WriteMeterData(users) - gen.WriteTo(c.Response().BodyWriter()) - return nil -} - -func uploadEndUserRegisterTemplate(c *fiber.Ctx) error { - result := response.NewResult(c) - requestReportId := c.Params("rid") - if ensure, err := ensureReportBelongs(c, &result, requestReportId); !ensure { - return err - } - meterType, err := service.ReportService.RetreiveParkEndUserMeterType(requestReportId) - if err != nil { - return result.Error(http.StatusInternalServerError, err.Error()) - } - if meterType == -1 { - return result.NotFound("未能确定用户表计类型。") - } - - uploadedFile, err := c.FormFile("data") - if err != nil { - return result.NotAccept("没有接收到上传的档案文件。") - } - archiveFile, err := uploadedFile.Open() - if err != nil { - return result.Error(http.StatusInternalServerError, err.Error()) - } - - if meterType == 0 { - errs := service.EndUserService.BatchImportNonPVRegister(requestReportId, archiveFile) - if errs.Len() > 0 { - return result.Json(http.StatusInternalServerError, "上传抄表文件存在解析错误", fiber.Map{"errors": errs.Errs}) - } - } else { - errs := service.EndUserService.BatchImportPVRegister(requestReportId, archiveFile) - if errs.Len() > 0 { - return result.Json(http.StatusInternalServerError, "上传抄表文件存在解析错误", fiber.Map{"errors": errs.Errs}) - } - } - return result.Json(http.StatusOK, "已经成功完成抄表记录的导入。", fiber.Map{"errors": make([]error, 0)}) -} - -type ModifyEndUserRegisterFormData struct { - CurrentPeriodOverall decimal.NullDecimal `json:"currentPeriodOverall" form:"currentPeriodOverall"` - CurrentPeriodCritical decimal.NullDecimal `json:"currentPeriodCritical" form:"currentPeriodCritical"` - CurrentPeriodPeak decimal.NullDecimal `json:"currentPeriodPeak" form:"currentPeriodPeak"` - CurrentPeriodValley decimal.NullDecimal `json:"currentPeriodValley" form:"currentPeriodValley"` - AdjustOverall decimal.NullDecimal `json:"adjustOverall" form:"adjustOverall"` - AdjustCritical decimal.NullDecimal `json:"adjustCritical" form:"adjustCritical"` - AdjustPeak decimal.NullDecimal `json:"adjustPeak" form:"adjustPeak"` - AdjustValley decimal.NullDecimal `json:"adjustValley" form:"adjustValley"` -} - -func modifyEndUserRegisterRecord(c *fiber.Ctx) error { - result := response.NewResult(c) - requestReportId := c.Params("rid") - if ensure, err := ensureReportBelongs(c, &result, requestReportId); !ensure { - return err - } - meterType, err := service.ReportService.RetreiveParkEndUserMeterType(requestReportId) - if err != nil { - return result.Error(http.StatusInternalServerError, err.Error()) - } - if meterType == -1 { - return result.NotFound("未能确定用户表计类型。") - } - requestParkId := c.Params("pid") - requestMeterId := c.Params("mid") - formData := new(ModifyEndUserRegisterFormData) - if err := c.BodyParser(formData); err != nil { - return result.UnableToParse("无法解析提交的数据。") - } - meter, err := service.EndUserService.FetchSpecificEndUserRecord(requestReportId, requestParkId, requestMeterId) - if err != nil { - return result.NotFound(err.Error()) - } - if formData.CurrentPeriodOverall.Valid { - meter.CurrentPeriodOverall = formData.CurrentPeriodOverall.Decimal - } - if formData.CurrentPeriodCritical.Valid { - meter.CurrentPeriodCritical = formData.CurrentPeriodCritical.Decimal - } - if formData.CurrentPeriodPeak.Valid { - meter.CurrentPeriodPeak = formData.CurrentPeriodPeak.Decimal - } - if formData.CurrentPeriodValley.Valid { - meter.CurrentPeriodValley = formData.CurrentPeriodValley.Decimal - } - if formData.AdjustOverall.Valid { - meter.AdjustOverall = formData.AdjustOverall.Decimal - } - if formData.AdjustCritical.Valid { - meter.AdjustCritical = formData.AdjustCritical.Decimal - } - if formData.AdjustPeak.Valid { - meter.AdjustPeak = formData.AdjustPeak.Decimal - } - if formData.AdjustValley.Valid { - meter.AdjustValley = formData.AdjustValley.Decimal - } - valid, err := meter.Validate() - if err != nil { - return result.Error(http.StatusInternalServerError, err.Error()) - } - if !valid { - return result.NotAccept("抄表数据合法性验证失败。") - } - ctx, cancel := global.TimeoutContext() - defer cancel() - tx, err := global.DB.BeginTx(ctx, &sql.TxOptions{}) - if err != nil { - return result.Error(http.StatusInternalServerError, err.Error()) - } - err = service.EndUserService.UpdateEndUserRegisterRecord(&tx, &ctx, *meter) - if err != nil { - tx.Rollback() - return result.Error(http.StatusInternalServerError, err.Error()) - } - err = tx.Commit() - if err != nil { - tx.Rollback() - return result.Error(http.StatusInternalServerError, err.Error()) - } - return result.Success("指定终端用户抄表记录已经更新。") -} - -func statEndUserInPeriod(c *fiber.Ctx) error { - result := response.NewResult(c) - session, err := _retreiveSession(c) - if err != nil { - return result.Unauthorized(err.Error()) - } - requestUser := lo. - If(session.Type == model.USER_TYPE_ENT, session.Uid). - Else(c.Query("user")) - requestPark := c.Query("park") - if len(requestPark) > 0 && session.Type == model.USER_TYPE_ENT { - if ensure, err := ensureParkBelongs(c, &result, requestPark); !ensure { - return err - } - } - startDate := c.Query("start") - endDate := c.Query("end") - stat, err := service.EndUserService.StatEndUserRecordInPeriod(requestUser, requestPark, startDate, endDate) - if err != nil { - return result.Error(http.StatusInternalServerError, err.Error()) - } - return result.Success( - "已经完成终端用户的费用统计", - fiber.Map{"details": stat}, - ) -} diff --git a/controller/foundation.go b/controller/foundation.go new file mode 100644 index 0000000..65cab6b --- /dev/null +++ b/controller/foundation.go @@ -0,0 +1,24 @@ +package controller + +import ( + "electricity_bill_calc/config" + "electricity_bill_calc/logger" + "electricity_bill_calc/response" + "electricity_bill_calc/security" + "github.com/gofiber/fiber/v2" +) + +var foundLog = logger.Named("Handler", "Foundation") + +func InitializeFoundationHandlers(router *fiber.App) { + router.Get("/norm/authorized/loss/rate", security.MustAuthenticated, getNormAuthorizedLossRate) +} + +func getNormAuthorizedLossRate(c *fiber.Ctx) error { + foundLog.Info("获取系统中定义的基准核定线损率") + result := response.NewResult(c) + BaseLoss := config.BaseLoss + return result.Success("已经获取到系统设置的基准核定线损率。", + fiber.Map{"normAuthorizedLossRate": BaseLoss}, + ) +} diff --git a/controller/god_mode.go b/controller/god_mode.go index e431348..db1a426 100644 --- a/controller/god_mode.go +++ b/controller/god_mode.go @@ -1,176 +1,132 @@ package controller import ( - "electricity_bill_calc/exceptions" + "electricity_bill_calc/logger" "electricity_bill_calc/response" "electricity_bill_calc/security" "electricity_bill_calc/service" - "net/http" - + "errors" "github.com/gofiber/fiber/v2" + "go.uber.org/zap" + "net/http" ) -func InitializeGodModeController(router *fiber.App) { - gmR := router.Group("/gm") - { - gmR.Delete("/report/:rid/summary", security.SingularityAuthorize, gmResetReportSummary) - gmR.Delete("/report/:rid/maintenance", security.SingularityAuthorize, gmResetReportMaintenance) - gmR.Delete("/report/:rid/meters", security.SingularityAuthorize, gmResetReportEndUserRecord) - gmR.Post("/report/:rid/meters", security.SingularityAuthorize, gmResynchronizeReportEndUserRecord) - gmR.Delete("/report/:rid", security.SingularityAuthorize, gmResetReport) - gmR.Delete("/report/:rid/force", security.SingularityAuthorize, gmDeleteReport) - gmR.Delete("/park/:pid/maintenance/:mid", security.SingularityAuthorize, gmDeleteSpecificMaintenance) - gmR.Delete("/park/:pid/maintenance", security.SingularityAuthorize, gmDeleteAllMaintenance) - gmR.Delete("/park/:pid/meters", security.SingularityAuthorize, gmDeleteAllMeters) - gmR.Delete("/park/:pid/force", security.SingularityAuthorize, gmDeletePark) - gmR.Delete("/enterprise/:uid/force", security.SingularityAuthorize, gmDeleteUser) - } +var GmLog = logger.Named("Handler", "GM") + +func InitializeGmController(router *fiber.App) { + router.Delete("/gm/tenement", security.SingularityAuthorize, deleteTenement) + router.Delete("/gm/park", security.SingularityAuthorize, deletePark) + router.Delete("/gm/report", security.SingularityAuthorize, deleteReports) + router.Delete("/gm/tenement/meter", security.SingularityAuthorize, deleteTenementMeterRelations) + router.Delete("/gm/enterprise", security.SingularityAuthorize, deleteEnterprise) + router.Delete("/gm/meter/pooling", security.SingularityAuthorize, deleteMeterPoolingRelations) + router.Delete("gm/meter", security.SingularityAuthorize, deleteMeters) } -func gmResetReportSummary(c *fiber.Ctx) error { - result := response.NewResult(c) - requestReportId := c.Params("rid") - done, err := service.GodModeService.ClearReportSummary(requestReportId) - if err != nil { - return result.Error(http.StatusInternalServerError, err.Error()) +//用于将参数转化为切片 +func getQueryValues(c *fiber.Ctx, paramName string) []string { + values := c.Request().URI().QueryArgs().PeekMulti(paramName) + result := make([]string, len(values)) + for i, v := range values { + result[i] = string(v) } - if !done { - return result.Error(http.StatusInternalServerError, "未能成功重置指定报表的园区总览部分。") - } - return result.Success("指定报表的园区总览已经重置。") + return result } -func gmResetReportMaintenance(c *fiber.Ctx) error { +func deleteTenement(c *fiber.Ctx) error { + park := c.Query("park", "") + tenements := getQueryValues(c, "tenements") result := response.NewResult(c) - requestReportId := c.Params("rid") - done, err := service.GodModeService.ClearReportMaintenances(requestReportId) + GmLog.Info("[天神模式]删除指定园区中的商户", zap.String("park", park), zap.Strings("tenements", tenements)) + + err := service.GMService.DeleteTenements(park, tenements) if err != nil { - return result.Error(http.StatusInternalServerError, err.Error()) + GmLog.Error("[天神模式]删除指定园区中的商户失败", zap.Error(err)) + return result.Error(500, err.Error()) } - if !done { - return result.Error(http.StatusInternalServerError, "未能成功重置指定报表的配电维护费部分。") - } - return result.Success("指定报表的配电维护费已经重置。") + + return result.Success("指定商户已经删除。") } -func gmResynchronizeReportEndUserRecord(c *fiber.Ctx) error { +func deletePark(c *fiber.Ctx) error { + parks := getQueryValues(c, "parks") result := response.NewResult(c) - requestReportId := c.Params("rid") - done, err := service.GodModeService.ResynchronizeEndUser(requestReportId) + GmLog.Info("[天神模式]删除指定园区", zap.Strings("parks", parks)) + + if len(parks) < 0 { + GmLog.Info("[天神模式]用户未指派园区参数或者未指定需要删除的园区。") + return result.Error(http.StatusBadRequest, error.Error(errors.New("必须至少指定一个需要删除的园区!"))) + } + + err := service.GMService.DeleteParks(parks) if err != nil { - return result.Error(http.StatusInternalServerError, err.Error()) + GmLog.Error("[天神模式]删除指定园区失败", zap.Error(err)) + return result.Error(500, err.Error()) } - if !done { - return result.Error(http.StatusInternalServerError, "未能成功重置指定报表的抄表记录基本档案。") - } - return result.Success("指定报表的抄表记录基本档案已经重新同步。") + return result.Success("指定园区已经删除。") } -func gmResetReportEndUserRecord(c *fiber.Ctx) error { +func deleteReports(c *fiber.Ctx) error { + pid := c.Query("park") + reports := getQueryValues(c, "reports") result := response.NewResult(c) - requestReportId := c.Params("rid") - done, err := service.GodModeService.ResetEndUserRegisterRecords(requestReportId) - if err != nil { - return result.Error(http.StatusInternalServerError, err.Error()) - } - if !done { - return result.Error(http.StatusInternalServerError, "未能成功重置指定报表的抄表记录部分。") - } - return result.Success("指定报表的抄表记录已经重置。") -} + GmLog.Info("[天神模式]删除符合条件的报表。", zap.Strings("reports", reports)) -func gmResetReport(c *fiber.Ctx) error { - result := response.NewResult(c) - requestReportId := c.Params("rid") - done, err := service.GodModeService.ResetReport(requestReportId) + err := service.GMService.DeleteReports(pid, reports) if err != nil { - return result.Error(http.StatusInternalServerError, err.Error()) - } - if !done { - return result.Error(http.StatusInternalServerError, "未能成功重置指定报表。") - } - return result.Success("指定报表已经重置。") -} - -func gmDeleteReport(c *fiber.Ctx) error { - result := response.NewResult(c) - requestReportId := c.Params("rid") - done, err := service.GodModeService.DeleteReport(requestReportId) - if err != nil { - if ipErr, ok := err.(exceptions.ImproperOperateError); ok { - return result.NotAccept(ipErr.Message) - } else { - return result.Error(http.StatusInternalServerError, err.Error()) - } - } - if !done { - return result.Error(http.StatusInternalServerError, "未能成功删除指定报表。") + GmLog.Error("[天神模式]删除指定园区中的报表失败。", zap.Error(err)) + return result.Error(500, err.Error()) } return result.Success("指定报表已经删除。") } -func gmDeleteSpecificMaintenance(c *fiber.Ctx) error { +func deleteEnterprise(c *fiber.Ctx) error { + uid := c.Query("uid") result := response.NewResult(c) - requestParkId := c.Params("pid") - requestMaintenanceId := c.Params("mid") - done, err := service.GodModeService.RemoveSpecificMaintenance(requestParkId, requestMaintenanceId) + GmLog.Info("[天神模式]删除指定企业用户", zap.String("uid", uid)) + + err := service.GMService.DeleteEnterprises(uid) if err != nil { - return result.Error(http.StatusInternalServerError, err.Error()) + GmLog.Error("[天神模式]删除指定企业用户失败", zap.Error(err)) + return result.Error(500, "删除指定企业用户失败。") } - if !done { - return result.Error(http.StatusInternalServerError, "未能成功删除指定的维护费用记录。") - } - return result.Success("指定维护费用记录已经删除。") + return result.Success("指定企业用户已经删除。") } -func gmDeleteAllMaintenance(c *fiber.Ctx) error { +func deleteTenementMeterRelations(c *fiber.Ctx) error { result := response.NewResult(c) - requestParkId := c.Params("pid") - done, err := service.GodModeService.RemoveAllMaintenance(requestParkId) - if err != nil { - return result.Error(http.StatusInternalServerError, err.Error()) + parkId := c.Query("park") + tId := getQueryValues(c, "tenements") + metersId := getQueryValues(c, "meters") + GmLog.Info("删除指定园区中的商户与表计的关联关系", zap.String("park id", parkId)) + if err := service.GMService.DeleteTenementMeterRelations(parkId, tId, metersId); err != nil { + meterLog.Error("无法删除指定园区中的商户与表计的关联关系", zap.Error(err)) + return result.NotAccept(err.Error()) } - if !done { - return result.Error(http.StatusInternalServerError, "未能成功删除全部维护费用记录。") - } - return result.Success("全部维护费用记录已经删除。") + return result.Success("删除成功") } -func gmDeleteAllMeters(c *fiber.Ctx) error { +func deleteMeterPoolingRelations(c *fiber.Ctx) error { result := response.NewResult(c) - requestParkId := c.Params("pid") - done, err := service.GodModeService.RemoveAllMeters(requestParkId) - if err != nil { - return result.Error(http.StatusInternalServerError, err.Error()) + parkId := c.Query("park") + mId := getQueryValues(c, "meters") + GmLog.Info("[天神模式]删除指定园区中的表计公摊关系", zap.String("park id", parkId)) + if err := service.GMService.DeleteMeterPooling(parkId, mId); err != nil { + meterLog.Error("[天神模式]删除指定园区中的表计公摊关系失败", zap.Error(err)) + return result.Error(500, "删除指定园区中的表计公摊关系失败。") } - if !done { - return result.Error(http.StatusInternalServerError, "未能成功删除全部终端表计档案记录。") - } - return result.Success("全部终端表计档案记录已经删除。") + return result.Success("指定表计公摊关系已经删除。") + } -func gmDeletePark(c *fiber.Ctx) error { +func deleteMeters(c *fiber.Ctx) error { result := response.NewResult(c) - requestParkId := c.Params("pid") - done, err := service.GodModeService.RemovePark(requestParkId) - if err != nil { - return result.Error(http.StatusInternalServerError, err.Error()) + parkId := c.Query("park") + mId := getQueryValues(c, "meters") + GmLog.Info("[天神模式]删除指定园区中的表计", zap.String("park id", parkId)) + if err := service.GMService.DeleteMeters(parkId, mId); err != nil { + meterLog.Error("[天神模式]删除指定园区中的表计失败", zap.Error(err)) + return result.Error(500, "删除指定园区中的表计失败。") } - if !done { - return result.Error(http.StatusInternalServerError, "未能成功删除指定的园区。") - } - return result.Success("指定的园区已经删除。") -} - -func gmDeleteUser(c *fiber.Ctx) error { - result := response.NewResult(c) - requestUserId := c.Params("uid") - done, err := service.GodModeService.DeleteUser(requestUserId) - if err != nil { - return result.Error(http.StatusInternalServerError, err.Error()) - } - if !done { - return result.Error(http.StatusInternalServerError, "未能成功删除指定的用户。") - } - return result.Success("指定的用户及其关联信息已经删除。") + return result.Success("指定表计已经删除。") } diff --git a/controller/maintenance_fee.go b/controller/maintenance_fee.go deleted file mode 100644 index 9249ae6..0000000 --- a/controller/maintenance_fee.go +++ /dev/null @@ -1,202 +0,0 @@ -package controller - -import ( - "electricity_bill_calc/model" - "electricity_bill_calc/response" - "electricity_bill_calc/security" - "electricity_bill_calc/service" - "net/http" - "strconv" - - "github.com/gofiber/fiber/v2" - "github.com/jinzhu/copier" - "github.com/samber/lo" - "github.com/shopspring/decimal" -) - -func InitializeMaintenanceFeeController(router *fiber.App) { - router.Get("/maintenance/fee", security.MustAuthenticated, listMaintenanceFees) - router.Post("/maintenance/fee", security.EnterpriseAuthorize, createMaintenanceFeeRecord) - router.Put("/maintenance/fee/:mid", security.EnterpriseAuthorize, modifyMaintenanceFeeRecord) - router.Put("/maintenance/fee/:mid/enabled", security.EnterpriseAuthorize, changeMaintenanceFeeState) - router.Delete("/maintenance/fee/:mid", security.EnterpriseAuthorize, deleteMaintenanceFee) - router.Get("/additional/charges", security.MustAuthenticated, statAdditionalCharges) -} - -func ensureMaintenanceFeeBelongs(c *fiber.Ctx, result *response.Result, requestMaintenanceFeeId string) (bool, error) { - userSession, err := _retreiveSession(c) - if err != nil { - return false, result.Unauthorized(err.Error()) - } - sure, err := service.MaintenanceFeeService.EnsureFeeBelongs(userSession.Uid, requestMaintenanceFeeId) - if err != nil { - return false, result.Error(http.StatusInternalServerError, err.Error()) - } - if !sure { - return false, result.Unauthorized("所操作维护费记录不属于当前用户。") - } - return true, nil -} - -func listMaintenanceFees(c *fiber.Ctx) error { - result := response.NewResult(c) - userSession, err := _retreiveSession(c) - if err != nil { - return result.Unauthorized(err.Error()) - } - requestPark := c.Query("park") - requestPeriod := c.Query("period") - requestPage, err := strconv.Atoi(c.Query("page", "1")) - if err != nil { - return result.Error(http.StatusInternalServerError, "不能解析给定的参数[page]。") - } - if len(requestPark) > 0 { - if userSession.Type == model.USER_TYPE_ENT { - if ensure, err := ensureParkBelongs(c, &result, requestPark); !ensure { - return err - } - } - fees, total, err := service.MaintenanceFeeService.ListMaintenanceFees([]string{requestPark}, requestPeriod, requestPage) - if err != nil { - return result.Error(http.StatusInternalServerError, err.Error()) - } - return result.Json( - http.StatusOK, - "已获取指定园区下的维护费记录", - response.NewPagedResponse(requestPage, total).ToMap(), - fiber.Map{"fees": fees}, - ) - } else { - parkIds, err := service.ParkService.AllParkIds(userSession.Uid) - if err != nil { - return result.Error(http.StatusInternalServerError, err.Error()) - } - fees, total, err := service.MaintenanceFeeService.ListMaintenanceFees(parkIds, requestPeriod, requestPage) - if err != nil { - return result.Error(http.StatusInternalServerError, err.Error()) - } - return result.Json( - http.StatusOK, - "已获取指定用户下的所有维护费记录。", - response.NewPagedResponse(requestPage, total).ToMap(), - fiber.Map{"fees": fees}, - ) - } -} - -type _FeeCreationFormData struct { - ParkId string `json:"parkId" form:"parkId"` - Name string `json:"name" form:"name"` - Period string `json:"period" form:"period"` - Fee decimal.Decimal `json:"fee" form:"fee"` - Memo *string `json:"memo" form:"memo"` -} - -func createMaintenanceFeeRecord(c *fiber.Ctx) error { - result := response.NewResult(c) - formData := new(_FeeCreationFormData) - if err := c.BodyParser(formData); err != nil { - return result.UnableToParse("无法解析提交的数据。") - } - if ensure, err := ensureParkBelongs(c, &result, formData.ParkId); !ensure { - return err - } - newMaintenanceFee := &model.MaintenanceFee{} - copier.Copy(newMaintenanceFee, formData) - err := service.MaintenanceFeeService.CreateMaintenanceFeeRecord(*newMaintenanceFee) - if err != nil { - return result.Error(http.StatusInternalServerError, err.Error()) - } - return result.Created("新维护费记录已经创建。") -} - -type _FeeModificationFormData struct { - Fee decimal.Decimal `json:"fee" form:"fee"` - Memo *string `json:"memo" form:"memo"` -} - -func modifyMaintenanceFeeRecord(c *fiber.Ctx) error { - result := response.NewResult(c) - requestFee := c.Params("mid") - formData := new(_FeeModificationFormData) - if err := c.BodyParser(formData); err != nil { - return result.UnableToParse("无法解析提交的数据。") - } - if ensure, err := ensureMaintenanceFeeBelongs(c, &result, requestFee); !ensure { - return err - } - newFeeState := new(model.MaintenanceFee) - copier.Copy(newFeeState, formData) - newFeeState.Id = requestFee - err := service.MaintenanceFeeService.ModifyMaintenanceFee(*newFeeState) - if err != nil { - return result.Error(http.StatusInternalServerError, err.Error()) - } - return result.Updated("指定维护费条目已更新。") -} - -type _FeeStateFormData struct { - Enabled bool `json:"enabled" form:"enabled"` -} - -func changeMaintenanceFeeState(c *fiber.Ctx) error { - result := response.NewResult(c) - requestFee := c.Params("mid") - formData := new(_FeeStateFormData) - if err := c.BodyParser(formData); err != nil { - return result.UnableToParse("无法解析提交的数据。") - } - if ensure, err := ensureMaintenanceFeeBelongs(c, &result, requestFee); !ensure { - return err - } - err := service.MaintenanceFeeService.ChangeMaintenanceFeeState(requestFee, formData.Enabled) - if err != nil { - return result.Error(http.StatusInternalServerError, err.Error()) - } - return result.Updated("指定维护费条目状态已更新。") -} - -func deleteMaintenanceFee(c *fiber.Ctx) error { - result := response.NewResult(c) - requestFee := c.Params("mid") - if ensure, err := ensureMaintenanceFeeBelongs(c, &result, requestFee); !ensure { - return err - } - err := service.MaintenanceFeeService.DeleteMaintenanceFee(requestFee) - if err != nil { - return result.Error(http.StatusInternalServerError, err.Error()) - } - return result.Deleted("指定维护费条目已删除。") -} - -func statAdditionalCharges(c *fiber.Ctx) error { - result := response.NewResult(c) - session, err := _retreiveSession(c) - if err != nil { - return result.Unauthorized(err.Error()) - } - requestUser := lo. - If(session.Type == model.USER_TYPE_ENT, session.Uid). - Else(c.Query("user")) - requestPark := c.Query("park") - if len(requestPark) > 0 && session.Type == model.USER_TYPE_ENT { - if ensure, err := ensureParkBelongs(c, &result, requestPark); !ensure { - return err - } - } - period := c.Query("period", "") - keyword := c.Query("keyword", "") - requestPage, err := strconv.Atoi(c.Query("page", "1")) - if err != nil { - return result.Error(http.StatusInternalServerError, "不能解析给定的参数[page]。") - } - fees, total, err := service.MaintenanceFeeService.QueryAdditionalCharges(requestUser, requestPark, period, keyword, requestPage) - if err != nil { - return result.Error(http.StatusInternalServerError, err.Error()) - } - return result.Success( - "已经成功获取到物业附加费的统计记录。", - response.NewPagedResponse(requestPage, total).ToMap(), - fiber.Map{"charges": fees}, - ) -} diff --git a/controller/meter04kv.go b/controller/meter04kv.go deleted file mode 100644 index b3fd7f4..0000000 --- a/controller/meter04kv.go +++ /dev/null @@ -1,188 +0,0 @@ -package controller - -import ( - "electricity_bill_calc/excel" - "electricity_bill_calc/model" - "electricity_bill_calc/response" - "electricity_bill_calc/security" - "electricity_bill_calc/service" - "fmt" - "net/http" - "strconv" - - "github.com/gofiber/fiber/v2" - "github.com/jinzhu/copier" - "github.com/samber/lo" - "github.com/shopspring/decimal" -) - -func InitializeMeter04kVController(router *fiber.App) { - router.Get("/park/:pid/meter/template", download04kvMeterArchiveTemplate) - router.Get("/park/:pid/meters", security.EnterpriseAuthorize, ListPaged04kVMeter) - router.Get("/park/:pid/meter/:code", security.EnterpriseAuthorize, fetch04kVMeterDetail) - router.Post("/park/:pid/meter", security.EnterpriseAuthorize, createSingle04kVMeter) - router.Post("/park/:pid/meter/batch", security.EnterpriseAuthorize, batchImport04kVMeterArchive) - router.Put("/park/:pid/meter/:code", security.EnterpriseAuthorize, modifySingle04kVMeter) -} - -func download04kvMeterArchiveTemplate(c *fiber.Ctx) error { - result := response.NewResult(c) - requestParkId := c.Params("pid") - parkDetail, err := service.ParkService.FetchParkDetail(requestParkId) - if err != nil { - return result.NotFound("未找到指定的园区信息。") - } - return c.Download("./assets/meter_04kv_template.xlsx", fmt.Sprintf("%s-户表档案.xlsx", parkDetail.Name)) -} - -func ListPaged04kVMeter(c *fiber.Ctx) error { - result := response.NewResult(c) - requestParkId := c.Params("pid") - if ensure, err := ensureParkBelongs(c, &result, requestParkId); !ensure { - return err - } - requestPage, err := strconv.Atoi(c.Query("page", "1")) - if err != nil { - return result.NotAccept("查询参数[page]格式不正确。") - } - requestKeyword := c.Query("keyword", "") - meters, totalItem, err := service.Meter04kVService.ListMeterDetail(requestParkId, requestKeyword, requestPage) - if err != nil { - return result.NotFound(err.Error()) - } - return result.Json( - http.StatusOK, - "已获取到符合条件的0.4kV表计集合。", - response.NewPagedResponse(requestPage, totalItem).ToMap(), - fiber.Map{"meters": meters}, - ) -} - -func fetch04kVMeterDetail(c *fiber.Ctx) error { - result := response.NewResult(c) - requestParkId := c.Params("pid") - if ensure, err := ensureParkBelongs(c, &result, requestParkId); !ensure { - return err - } - requestMeterCode := c.Params("code") - meter, err := service.Meter04kVService.Get04kVMeterDetail(requestParkId, requestMeterCode) - if err != nil { - return result.NotFound(err.Error()) - } - if meter == nil { - return result.Json(http.StatusNotFound, "指定的表计信息未能找到。", fiber.Map{"meter": nil}) - } - return result.Json(http.StatusOK, "指定的表计信息已找到。", fiber.Map{"meter": meter}) -} - -type _MeterModificationFormData struct { - Address *string `json:"address" form:"address"` - CustomerName *string `json:"customerName" form:"customerName"` - ContactName *string `json:"contactName" form:"contactName"` - ContactPhone *string `json:"contactPhone" form:"contactPhone"` - Ratio decimal.Decimal `json:"ratio" form:"ratio"` - Seq int `json:"seq" form:"seq"` - IsPublicMeter bool `json:"isPublicMeter" form:"isPublicMeter"` - Enabled bool `json:"enabled" form:"enabled"` -} - -type _MeterCreationFormData struct { - Code string `json:"code" form:"code"` - Address *string `json:"address" form:"address"` - CustomerName *string `json:"customerName" form:"customerName"` - ContactName *string `json:"contactName" form:"contactName"` - ContactPhone *string `json:"contactPhone" form:"contactPhone"` - Ratio decimal.Decimal `json:"ratio" form:"ratio"` - Seq int `json:"seq" form:"seq"` - IsPublicMeter bool `json:"isPublicMeter" form:"isPublicMeter"` - Enabled bool `json:"enabled" form:"enabled"` -} - -func createSingle04kVMeter(c *fiber.Ctx) error { - result := response.NewResult(c) - requestParkId := c.Params("pid") - if ensure, err := ensureParkBelongs(c, &result, requestParkId); !ensure { - return err - } - formData := new(_MeterCreationFormData) - if err := c.BodyParser(formData); err != nil { - return result.UnableToParse("无法解析提交的数据。") - } - newMeter := new(model.Meter04KV) - copier.Copy(newMeter, formData) - newMeter.ParkId = requestParkId - err := service.Meter04kVService.CreateSingleMeter(*newMeter) - if err != nil { - return result.Error(http.StatusInternalServerError, err.Error()) - } - return result.Created("新0.4kV表计已经添加完成。") -} - -func modifySingle04kVMeter(c *fiber.Ctx) error { - result := response.NewResult(c) - requestParkId := c.Params("pid") - if ensure, err := ensureParkBelongs(c, &result, requestParkId); !ensure { - return err - } - requestMeterCode := c.Params("code") - meterDetail, err := service.Meter04kVService.Get04kVMeterDetail(requestParkId, requestMeterCode) - if err != nil { - return result.NotFound(err.Error()) - } - if meterDetail == nil { - return result.NotFound("指定表计的信息为找到,不能修改。") - } - formData := new(_MeterModificationFormData) - if err := c.BodyParser(formData); err != nil { - return result.UnableToParse("无法解析提交的数据。") - } - copier.Copy(meterDetail, formData) - err = service.Meter04kVService.UpdateSingleMeter(meterDetail) - if err != nil { - return result.Error(http.StatusInternalServerError, err.Error()) - } - return result.Updated("指定0.4kV表计信息已经更新。") -} - -func batchImport04kVMeterArchive(c *fiber.Ctx) error { - result := response.NewResult(c) - requestParkId := c.Params("pid") - if ensure, err := ensureParkBelongs(c, &result, requestParkId); !ensure { - return err - } - uploadedFile, err := c.FormFile("data") - if err != nil { - return result.NotAccept("没有接收到上传的档案文件。") - } - archiveFile, err := uploadedFile.Open() - if err != nil { - return result.Error(http.StatusInternalServerError, err.Error()) - } - analyzer, err := excel.NewMeterArchiveExcelAnalyzer(archiveFile) - if err != nil { - return result.Error(http.StatusInternalServerError, err.Error()) - } - records, errs := analyzer.Analysis(*new(model.Meter04KV)) - if len(errs) > 0 { - return result.Json(http.StatusNotAcceptable, "上传的表计档案文件存在错误。", fiber.Map{"errors": errs}) - } - - mergedMeters := lo.Map(records, func(meter model.Meter04KV, index int) model.Meter04KV { - meter.ParkId = requestParkId - meter.Enabled = true - return meter - }) - errs = service.Meter04kVService.DuplicateMeterCodeValidate(mergedMeters) - if len(errs) > 0 { - return result.Json(http.StatusNotAcceptable, "上传的表计档案文件存在错误。", fiber.Map{"errors": errs}) - } - err = service.Meter04kVService.BatchCreateMeter(mergedMeters) - if err != nil { - return result.Error(http.StatusInternalServerError, err.Error()) - } - return result.Json( - http.StatusOK, - "上传的表计档案已经全部导入。", - fiber.Map{"errors": make([]excel.ExcelAnalysisError, 0)}, - ) -} diff --git a/controller/report.go b/controller/report.go index a99cabe..6d70c44 100644 --- a/controller/report.go +++ b/controller/report.go @@ -24,6 +24,8 @@ func InitializeReportHandlers(router *fiber.App) { router.Post("/report", security.EnterpriseAuthorize, initNewReportCalculateTask) router.Get("/report/draft", security.EnterpriseAuthorize, listDraftReportIndicies) router.Post("/report/calcualte", security.EnterpriseAuthorize, testCalculateReportSummary) + //TODO: 2023-07-20将calcualte错误请求改为正确的calculate请求 + router.Post("/report/calculate", 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) diff --git a/controller/statistics.go b/controller/statistics.go index 413efeb..fd0555c 100644 --- a/controller/statistics.go +++ b/controller/statistics.go @@ -1,31 +1,36 @@ package controller import ( + "electricity_bill_calc/logger" "electricity_bill_calc/model" "electricity_bill_calc/response" "electricity_bill_calc/security" "electricity_bill_calc/service" - "net/http" - "github.com/gofiber/fiber/v2" + "go.uber.org/zap" + "net/http" ) +var StatisticsWithdrawLog = logger.Named("Handler", "StatisticsWithdraw") + func InitializeStatisticsController(router *fiber.App) { router.Get("/audits", security.OPSAuthorize, currentAuditAmount) - router.Get("/stat/reports", security.MustAuthenticated, statReports) + router.Get("/stat/reports", security.OPSAuthorize, statReports) + } +//获取当前系统中待审核的内容数量 func currentAuditAmount(c *fiber.Ctx) error { + StatisticsWithdrawLog.Info("开始获取当前系统中待审核的内容数量") result := response.NewResult(c) amount, err := service.WithdrawService.AuditWaits() if err != nil { + StatisticsWithdrawLog.Error("获取当前系统中待审核的内容数量出错", zap.Error(err)) return result.Error(http.StatusInternalServerError, err.Error()) } - return result.Json(http.StatusOK, "已经获取到指定的统计信息。", fiber.Map{ - "amounts": map[string]int64{ - "withdraw": amount, - }, - }) + + return result.Success("已经获取到指定的统计信息", + fiber.Map{"withdraw": amount}) } func statReports(c *fiber.Ctx) error { @@ -42,28 +47,35 @@ func statReports(c *fiber.Ctx) error { if session.Type != 0 { enterprises, err = service.StatisticsService.EnabledEnterprises() if err != nil { + StatisticsWithdrawLog.Error(err.Error()) return result.Error(http.StatusInternalServerError, err.Error()) } parks, err = service.StatisticsService.EnabledParks() if err != nil { + StatisticsWithdrawLog.Error(err.Error()) return result.Error(http.StatusInternalServerError, err.Error()) } - reports, err = service.StatisticsService.ParksNewestState() + //TODO: 2023.07.26 报表数据库结构改变,此处逻辑复杂放在最后处理 + reports, err = service.StatisticsService.ParkNewestState() if err != nil { + StatisticsWithdrawLog.Error(err.Error()) return result.Error(http.StatusInternalServerError, err.Error()) } } else { parks, err = service.StatisticsService.EnabledParks(session.Uid) if err != nil { + StatisticsWithdrawLog.Error(err.Error()) return result.Error(http.StatusInternalServerError, err.Error()) } - reports, err = service.StatisticsService.ParksNewestState(session.Uid) + //TODO: 2023.07.26 报表数据库结构改变,此处逻辑复杂放在最后处理 + reports, err = service.StatisticsService.ParkNewestState(session.Uid) if err != nil { + StatisticsWithdrawLog.Error(err.Error()) return result.Error(http.StatusInternalServerError, err.Error()) } } - return result.Json(http.StatusOK, "已经完成园区报告的统计。", fiber.Map{ + return result.Success("已经完成园区报告的统计。", fiber.Map{ "statistics": fiber.Map{ "enterprises": enterprises, "parks": parks, diff --git a/controller/tenement.go b/controller/tenement.go index 04dab4c..077cf53 100644 --- a/controller/tenement.go +++ b/controller/tenement.go @@ -28,6 +28,11 @@ func InitializeTenementHandler(router *fiber.App) { router.Post("/tenement/:pid/:tid/move/out", security.EnterpriseAuthorize, moveOutTenement) router.Post("/tenement/:pid", security.EnterpriseAuthorize, addTenement) router.Post("/tenement/:pid/:tid/binding", security.EnterpriseAuthorize, bindMeterToTenement) + //TODO: 2023-07-19再apiFox上该请求是个PUT请求,后端接收是个POST请求,不知道是否有误或是缺少对应请求(apiFox测试请求返回值为405) + router.Post("/tenement/:pid/:tid/move/out", security.EnterpriseAuthorize, moveOutTenement) + router.Post("/tenement/:pid", security.EnterpriseAuthorize, addTenement) + router.Post("/tenement/:pid/:tid/binding", security.EnterpriseAuthorize, bindMeterToTenement) + //TODO: 2023-07-19再apiFox上该请求是个PUT请求,后端接收是个POST请求,不知道是否有误或是缺少对应请求(apiFox测试请求返回值为405) router.Post("/tenement/:pid/:tid/binding/:code/unbind", security.EnterpriseAuthorize, unbindMeterFromTenement) } diff --git a/controller/user.go b/controller/user.go index c79385e..e3bd2c3 100644 --- a/controller/user.go +++ b/controller/user.go @@ -18,8 +18,6 @@ import ( "go.uber.org/zap" ) -var userLog = logger.Named("Handler", "User") - func InitializeUserHandlers(router *fiber.App) { router.Delete("/login", security.MustAuthenticated, doLogout) router.Post("/login", doLogin) @@ -35,6 +33,8 @@ func InitializeUserHandlers(router *fiber.App) { router.Delete("/password/:uid", security.OPSAuthorize, invalidUserPassword) } +var userLog = logger.Named("Handler", "User") + type _LoginForm struct { Username string `json:"uname"` Password string `json:"upass"` @@ -71,7 +71,6 @@ func doLogin(c *fiber.Ctx) error { } return result.LoginSuccess(session) } - func doLogout(c *fiber.Ctx) error { result := response.NewResult(c) session, err := _retreiveSession(c) diff --git a/controller/withdraw.go b/controller/withdraw.go index 46bbcd0..6a41d72 100644 --- a/controller/withdraw.go +++ b/controller/withdraw.go @@ -2,18 +2,134 @@ package controller import ( "electricity_bill_calc/logger" + + "electricity_bill_calc/repository" + "electricity_bill_calc/response" "electricity_bill_calc/security" + "electricity_bill_calc/vo" "github.com/gofiber/fiber/v2" + "go.uber.org/zap" + "net/http" ) -var withdrawLog = logger.Named("Handler", "Report") +var withdrawLog = logger.Named("Handler", "Withdraw") -func InitializewithdrawHandlers(router *fiber.App) { - router.Put("/withdraw/:rid", security.EnterpriseAuthorize, changeReportWithdraw) +func InitializeWithdrawHandlers(router *fiber.App) { + router.Get("/withdraw", security.OPSAuthorize, withdraw) + router.Put("/withdraw/:rid", security.OPSAuthorize, reviewRequestWithdraw) + router.Delete("/withdraw/:rid", security.EnterpriseAuthorize, recallReport) } -func changeReportWithdraw(ctx *fiber.Ctx) error { - //result := response.NewResult(ctx) - //reportId := ctx.Params("rid") - return nil + +// 用于分页检索用户的核算报表 +func withdraw(c *fiber.Ctx) error { + //记录日志 + withdrawLog.Info("带分页的待审核的核算撤回申请列表") + //获取请求参数 + result := response.NewResult(c) + keyword := c.Query("keyword", "") + page := c.QueryInt("page", 1) + withdrawLog.Info("参数为: ", zap.String("keyword", keyword), zap.Int("page", page)) + //中间数据库操作暂且省略。。。。 + //首先进行核算报表的分页查询 + withdraws, total, err := repository.WithdrawRepository.FindWithdraw(uint(page), &keyword) + if err != nil { + withdrawLog.Error("检索用户核算报表失败。", zap.Error(err)) + return result.Error(http.StatusInternalServerError, err.Error()) + } + //TODO: 2023-07-18 此处返回值是个示例,具体返回值需要查询数据库(完成) + return result.Success( + "withdraw请求成功", + response.NewPagedResponse(page, total).ToMap(), + fiber.Map{"records": withdraws}, + ) +} + +// 用于审核撤回报表 +func reviewRequestWithdraw(c *fiber.Ctx) error { + Rid := c.Params("rid", "") + Data := new(vo.ReviewWithdraw) + result := response.NewResult(c) + + if err := c.BodyParser(&Data); err != nil { + withdrawLog.Error("无法解析审核指定报表的请求数据", zap.Error(err)) + return result.BadRequest("无法解析审核指定报表的请求数据。") + } + + if Data.Audit == true { //审核通过 + ok, err := repository.WithdrawRepository.ReviewTrueReportWithdraw(Rid) + if err != nil { + withdrawLog.Error("审核同意撤回报表失败") + return result.Error(http.StatusInternalServerError, err.Error()) + } + + if !ok { + withdrawLog.Error("审核同意撤回报表失败") + return result.NotAccept("审核同意撤回报表失败") + } else { + return result.Success("审核同意撤回报表成功!") + } + } else { //审核不通过 + ok, err := repository.WithdrawRepository.ReviewFalseReportWithdraw(Rid) + if err != nil { + withdrawLog.Error("审核拒绝撤回报表失败") + return result.Error(http.StatusInternalServerError, err.Error()) + } + + if !ok { + withdrawLog.Error("审核拒绝撤回报表失败") + return result.NotAccept("审核拒绝撤回报表失败") + } else { + return result.Success("审核拒绝撤回报表成功!") + } + } } + +// 用于撤回电费核算 +func recallReport(c *fiber.Ctx) error { + // 获取用户会话信息和参数 + rid := c.Params("rid", "") + result := response.NewResult(c) + session, err := _retreiveSession(c) + if err != nil { + withdrawLog.Error("无法获取当前用户的会话。") + return result.Unauthorized(err.Error()) + } + // 检查指定报表的所属情况 + isBelongsTo, err := repository.ReportRepository.IsBelongsTo(rid, session.Uid) + if err != nil { + withdrawLog.Error("检查报表所属情况出现错误。", zap.Error(err)) + return result.Error(http.StatusInternalServerError, err.Error()) + } + + if err == nil && isBelongsTo == true { + // 判断指定报表是否是当前园区的最后一张报表 + isLastReport, err := repository.ReportRepository.IsLastReport(rid) + if err != nil { + withdrawLog.Error("判断指定报表是否为当前园区的最后一张报表时出错", zap.Error(err)) + return result.Error(http.StatusInternalServerError, err.Error()) + } + + if err == nil && isLastReport == true { + // 申请撤回指定的核算报表 + //TODO: 2023.07.25 申请撤回指定核算报表,正确状态未处理(完成) + ok, err := repository.ReportRepository.ApplyWithdrawReport(rid) + if err != nil { + withdrawLog.Error("申请撤回指定核算报表出错", zap.Error(err)) + return result.Error(http.StatusInternalServerError, err.Error()) + } + if ok { + withdrawLog.Info("申请撤回指定核算报表成功") + return result.Success("申请撤回指定核算报表成功") + } + } else { + withdrawLog.Info("当前报表不是当前园区的最后一张报表") + return result.Error(http.StatusForbidden, "当前报表不是当前园区的最后一张报表") + } + } else { + withdrawLog.Info("指定的核算报表不属于当前用户。") + return result.Error(http.StatusForbidden, "指定的核算报表不属于当前用户") + } + + return result.Error(http.StatusInternalServerError, "其他错误") +} diff --git a/excel/end_user.go b/excel/end_user.go deleted file mode 100644 index c051a33..0000000 --- a/excel/end_user.go +++ /dev/null @@ -1,35 +0,0 @@ -package excel - -import ( - "electricity_bill_calc/model" - "io" -) - -var ( - endUserNonPVRecognizers = []*ColumnRecognizer{ - {Pattern: []string{"电表编号"}, Tag: "meterId", MatchIndex: -1, MustFill: true}, - {Pattern: []string{"上期", "(总)"}, Tag: "lastPeriodOverall", MatchIndex: -1, MustFill: true}, - {Pattern: []string{"本期", "(总)"}, Tag: "currentPeriodOverall", MatchIndex: -1, MustFill: true}, - {Pattern: []string{"退补", "(总)"}, Tag: "adjustOverall", MatchIndex: -1, MustFill: true}, - } - endUserPVRecognizers = append( - endUserNonPVRecognizers, - &ColumnRecognizer{Pattern: []string{"上期", "(尖峰)"}, Tag: "lastPeriodCritical", MatchIndex: -1}, - &ColumnRecognizer{Pattern: []string{"上期", "(峰)"}, Tag: "lastPeriodPeak", MatchIndex: -1}, - &ColumnRecognizer{Pattern: []string{"上期", "(谷)"}, Tag: "lastPeriodValley", MatchIndex: -1}, - &ColumnRecognizer{Pattern: []string{"本期", "(尖峰)"}, Tag: "currentPeriodCritical", MatchIndex: -1}, - &ColumnRecognizer{Pattern: []string{"本期", "(峰)"}, Tag: "currentPeriodPeak", MatchIndex: -1}, - &ColumnRecognizer{Pattern: []string{"本期", "(谷)"}, Tag: "currentPeriodValley", MatchIndex: -1}, - &ColumnRecognizer{Pattern: []string{"退补", "(尖峰)"}, Tag: "adjustCritical", MatchIndex: -1}, - &ColumnRecognizer{Pattern: []string{"退补", "(峰)"}, Tag: "adjustPeak", MatchIndex: -1}, - &ColumnRecognizer{Pattern: []string{"退补", "(谷)"}, Tag: "adjustValley", MatchIndex: -1}, - ) -) - -func NewEndUserNonPVExcelAnalyzer(file io.Reader) (*ExcelAnalyzer[model.EndUserImport], error) { - return NewExcelAnalyzer[model.EndUserImport](file, endUserNonPVRecognizers) -} - -func NewEndUserPVExcelAnalyzer(file io.Reader) (*ExcelAnalyzer[model.EndUserImport], error) { - return NewExcelAnalyzer[model.EndUserImport](file, endUserPVRecognizers) -} diff --git a/excel/meter_non_pv_template.go b/excel/meter_non_pv_template.go deleted file mode 100644 index 33392fd..0000000 --- a/excel/meter_non_pv_template.go +++ /dev/null @@ -1,90 +0,0 @@ -package excel - -import ( - "electricity_bill_calc/model" - "io" - - "github.com/xuri/excelize/v2" -) - -type MeterNonPVExcelTemplateGenerator struct { - file *excelize.File -} - -// 生成峰谷计量抄表Excel模板 -func NewMeterNonPVExcelTemplateGenerator() *MeterNonPVExcelTemplateGenerator { - return &MeterNonPVExcelTemplateGenerator{ - file: excelize.NewFile(), - } -} - -func (MeterNonPVExcelTemplateGenerator) titles() []interface{} { - return []interface{}{ - "序号", - "用户名称", - "户址", - "电表编号", - "倍率", - "上期表底(总)", - "本期表底(总)", - "退补电量(总)", - } -} - -func (t *MeterNonPVExcelTemplateGenerator) Close() { - t.file.Close() -} - -func (t MeterNonPVExcelTemplateGenerator) WriteTo(w io.Writer) (int64, error) { - return t.file.WriteTo(w) -} - -func (t *MeterNonPVExcelTemplateGenerator) WriteMeterData(meters []model.EndUserDetail) error { - defaultSheet := t.file.GetSheetName(0) - stream, err := t.file.NewStreamWriter(defaultSheet) - if err != nil { - return err - } - firstCell, err := excelize.CoordinatesToCellName(1, 1) - if err != nil { - return err - } - stream.SetColWidth(2, 4, 20) - stream.SetColWidth(6, 8, 15) - stream.SetRow(firstCell, t.titles(), excelize.RowOpts{Height: 20}) - - for index, meter := range meters { - firstCell, err := excelize.CoordinatesToCellName(1, index+2) - if err != nil { - return err - } - customerName := "" - if meter.CustomerName != nil { - customerName = *meter.CustomerName - } - customerAddress := "" - if meter.Address != nil { - customerAddress = *meter.Address - } - if err = stream.SetRow( - firstCell, - []interface{}{ - meter.Seq, - customerName, - customerAddress, - meter.MeterId, - meter.Ratio, - meter.LastPeriodOverall, - meter.CurrentPeriodOverall, - meter.AdjustOverall, - }, - excelize.RowOpts{Height: 15}, - ); err != nil { - return err - } - } - if err = stream.Flush(); err != nil { - return err - } - return nil -} diff --git a/excel/meter_pv_template.go b/excel/meter_pv_template.go deleted file mode 100644 index 0121699..0000000 --- a/excel/meter_pv_template.go +++ /dev/null @@ -1,108 +0,0 @@ -package excel - -import ( - "electricity_bill_calc/model" - "io" - - "github.com/xuri/excelize/v2" -) - -type MeterPVExcelTemplateGenerator struct { - file *excelize.File -} - -// 生成峰谷计量抄表Excel模板 -func NewMeterPVExcelTemplateGenerator() *MeterPVExcelTemplateGenerator { - return &MeterPVExcelTemplateGenerator{ - file: excelize.NewFile(), - } -} - -func (MeterPVExcelTemplateGenerator) titles() []interface{} { - return []interface{}{ - "序号", - "用户名称", - "户址", - "电表编号", - "倍率", - "上期表底(总)", - "本期表底(总)", - "上期表底(尖峰)", - "本期表底(尖峰)", - "上期表底(峰)", - "本期表底(峰)", - "上期表底(谷)", - "本期表底(谷)", - "退补电量(总)", - "退补电量(尖峰)", - "退补电量(峰)", - "退补电量(谷)", - } -} - -func (t *MeterPVExcelTemplateGenerator) Close() { - t.file.Close() -} - -func (t MeterPVExcelTemplateGenerator) WriteTo(w io.Writer) (int64, error) { - return t.file.WriteTo(w) -} - -func (t *MeterPVExcelTemplateGenerator) WriteMeterData(meters []model.EndUserDetail) error { - defaultSheet := t.file.GetSheetName(0) - stream, err := t.file.NewStreamWriter(defaultSheet) - if err != nil { - return err - } - firstCell, err := excelize.CoordinatesToCellName(1, 1) - if err != nil { - return err - } - stream.SetColWidth(2, 4, 20) - stream.SetColWidth(6, 17, 15) - stream.SetRow(firstCell, t.titles(), excelize.RowOpts{Height: 20}) - - for index, meter := range meters { - firstCell, err := excelize.CoordinatesToCellName(1, index+2) - if err != nil { - return err - } - customerName := "" - if meter.CustomerName != nil { - customerName = *meter.CustomerName - } - customerAddress := "" - if meter.Address != nil { - customerAddress = *meter.Address - } - if err = stream.SetRow( - firstCell, - []interface{}{ - meter.Seq, - customerName, - customerAddress, - meter.MeterId, - meter.Ratio, - meter.LastPeriodOverall, - meter.CurrentPeriodOverall, - meter.LastPeriodCritical, - meter.CurrentPeriodCritical, - meter.LastPeriodPeak, - meter.CurrentPeriodPeak, - meter.LastPeriodValley, - meter.CurrentPeriodValley, - meter.AdjustOverall, - meter.AdjustCritical, - meter.AdjustPeak, - meter.AdjustValley, - }, - excelize.RowOpts{Height: 15}, - ); err != nil { - return err - } - } - if err = stream.Flush(); err != nil { - return err - } - return nil -} diff --git a/excel/tenement.go b/excel/tenement.go index 107857d..25137b8 100644 --- a/excel/tenement.go +++ b/excel/tenement.go @@ -1,10 +1,5 @@ package excel -import ( - "electricity_bill_calc/model" - "io" -) - var tenementRecognizers = []*ColumnRecognizer{ {Pattern: [][]string{{"商户全称"}}, Tag: "fullName", MatchIndex: -1}, {Pattern: [][]string{{"联系地址"}}, Tag: "address", MatchIndex: -1}, @@ -18,9 +13,9 @@ var tenementRecognizers = []*ColumnRecognizer{ {Pattern: [][]string{{"开户行"}}, Tag: "bank", MatchIndex: -1}, } -func NewTenementExcelAnalyzer(file io.Reader) (*ExcelAnalyzer[model.TenementImportRow], error) { - return NewExcelAnalyzer[model.TenementImportRow](file, tenementRecognizers) -} +//func NewTenementExcelAnalyzer(file io.Reader) (*ExcelAnalyzer[model.TenementImportRow], error) { +// return NewExcelAnalyzer[model.TenementImportRow](file, tenementRecognizers) +//} //func NewMeterArchiveExcelAnalyzer(file io.Reader) (*ExcelAnalyzer[model.MeterImportRow], error) { // return NewExcelAnalyzer[model.MeterImportRow](file, meterArchiveRecognizers) diff --git a/global/db.go b/global/db.go index 1acdc74..0c77ff3 100644 --- a/global/db.go +++ b/global/db.go @@ -53,6 +53,7 @@ func (ql QueryLogger) TraceQueryStart(ctx context.Context, conn *pgx.Conn, data ql.logger.Info("查询参数", lo.Map(data.Args, func(elem any, index int) zap.Field { return zap.Any(fmt.Sprintf("[Arg %d]: ", index), elem) })...) + return ctx } diff --git a/global/redis.go b/global/redis.go index e99c19f..d28d8a9 100644 --- a/global/redis.go +++ b/global/redis.go @@ -15,6 +15,8 @@ var ( func SetupRedisConnection() error { var err error + a := fmt.Sprintf("%s:%d", config.RedisSettings.Host, config.RedisSettings.Port) + fmt.Println(a) Rd, err = rueidis.NewClient(rueidis.ClientOption{ InitAddress: []string{fmt.Sprintf("%s:%d", config.RedisSettings.Host, config.RedisSettings.Port)}, Password: config.RedisSettings.Password, diff --git a/go.mod b/go.mod index 9f93995..efa80f7 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,6 @@ module electricity_bill_calc go 1.19 require ( - github.com/deckarep/golang-set/v2 v2.1.0 github.com/fufuok/utils v0.10.2 github.com/georgysavva/scany/v2 v2.0.0 github.com/gofiber/fiber/v2 v2.46.0 @@ -27,26 +26,18 @@ require ( github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect github.com/jackc/puddle/v2 v2.2.0 // indirect - github.com/jinzhu/inflection v1.0.0 // indirect - github.com/jmoiron/sqlx v1.3.5 // indirect github.com/klauspost/compress v1.16.6 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.19 // indirect github.com/mattn/go-runewidth v0.0.14 // indirect github.com/philhofer/fwd v1.1.2 // indirect github.com/rivo/uniseg v0.4.4 // indirect - github.com/rogpeppe/go-internal v1.9.0 // indirect github.com/savsgio/dictpool v0.0.0-20221023140959-7bf2e61cea94 // indirect github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee // indirect github.com/tinylib/msgp v1.1.8 // indirect - github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/tcplisten v1.0.0 // indirect - github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect - github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect golang.org/x/sync v0.2.0 // indirect - gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect - mellium.im/sasl v0.3.0 // indirect ) require ( @@ -59,7 +50,6 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect - github.com/pelletier/go-toml v1.9.5 // indirect github.com/pelletier/go-toml/v2 v2.0.8 // indirect github.com/richardlehane/mscfb v1.0.4 // indirect github.com/richardlehane/msoleps v1.0.3 // indirect @@ -68,9 +58,6 @@ require ( github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/subosito/gotenv v1.4.2 // indirect - github.com/uptrace/bun v1.1.8 - github.com/uptrace/bun/dialect/pgdialect v1.1.8 - github.com/uptrace/bun/driver/pgdriver v1.1.8 github.com/xuri/efp v0.0.0-20230422071738-01f4e37c47e9 // indirect github.com/xuri/nfp v0.0.0-20230503010013-3f38cdbb0b83 // indirect go.uber.org/atomic v1.11.0 // indirect @@ -81,6 +68,5 @@ require ( golang.org/x/sys v0.9.0 // indirect golang.org/x/text v0.10.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 62deb92..ff47fcd 100644 --- a/go.sum +++ b/go.sum @@ -36,12 +36,10 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60= github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= -github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY= -github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs= github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= @@ -53,11 +51,10 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cockroachdb/cockroach-go/v2 v2.2.0 h1:/5znzg5n373N/3ESjHF5SMLxiW4RKB05Ql//KWfeTFs= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/deckarep/golang-set/v2 v2.1.0 h1:g47V4Or+DUdzbs8FxCCmgb6VYd+ptPAngjM6dtGktsI= -github.com/deckarep/golang-set/v2 v2.1.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= github.com/denisenkom/go-mssqldb v0.10.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= github.com/doug-martin/goqu/v9 v9.18.0 h1:/6bcuEtAe6nsSMVK/M+fOiXUNfyFF3yYtE07DBPFMYY= github.com/doug-martin/goqu/v9 v9.18.0/go.mod h1:nf0Wc2/hV3gYK9LiyqIrzBEVGlI8qW3GuDCEobC4wBQ= @@ -67,14 +64,9 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= -github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= -github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= -github.com/fufuok/utils v0.7.13 h1:FGx8Mnfg0ZB8HdVz1X60JJ2kFu1rtcsFDYUxUTzNKkU= -github.com/fufuok/utils v0.7.13/go.mod h1:ztIaorPqZGdbvmW3YlwQp80K8rKJmEy6xa1KwpJSsmk= github.com/fufuok/utils v0.10.2 h1:jXgE7yBSUW9z+sJs/VQq3o4MH+jN30PzIILVXFw73lE= github.com/fufuok/utils v0.10.2/go.mod h1:87MJq0gAZDYBgUOpxSGoLkdv8VCuRNOL9vK02F7JC3s= github.com/georgysavva/scany/v2 v2.0.0 h1:RGXqxDv4row7/FYoK8MRXAZXqoWF/NM+NP0q50k3DKU= @@ -83,10 +75,9 @@ github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= -github.com/gofiber/fiber/v2 v2.38.1 h1:GEQ/Yt3Wsf2a30iTqtLXlBYJZso0JXPovt/tmj5H9jU= -github.com/gofiber/fiber/v2 v2.38.1/go.mod h1:t0NlbaXzuGH7I+7M4paE848fNWInZ7mfxI/Er1fTth8= github.com/gofiber/fiber/v2 v2.46.0 h1:wkkWotblsGVlLjXj2dpgKQAYHtXumsK/HyFugQM68Ns= github.com/gofiber/fiber/v2 v2.46.0/go.mod h1:DNl0/c37WLe0g92U6lx1VMQuxGUQY5V7EIaVoEsUffc= +github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -124,7 +115,7 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= @@ -162,36 +153,24 @@ github.com/jackc/puddle/v2 v2.2.0 h1:RdcDk92EJBuBS55nQMMYFXTxwstHug4jkhT5pq8VxPk github.com/jackc/puddle/v2 v2.2.0/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/jinzhu/copier v0.3.5 h1:GlvfUwHk62RokgqVNvYsku0TATCF7bAHVwEXoBh3iJg= github.com/jinzhu/copier v0.3.5/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= -github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= -github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= -github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g= -github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.15.0 h1:xqfchp4whNFxn5A4XFyyYtitiWI8Hy5EW59jEwcyL6U= -github.com/klauspost/compress v1.15.0/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/klauspost/compress v1.16.5 h1:IFV2oUNUzZaz+XyusxpLzpzS8Pt5rh0Z16For/djlyI= -github.com/klauspost/compress v1.16.5/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/klauspost/compress v1.16.6 h1:91SKEy4K37vkp255cJ8QesJhjyRO0hn9i9G0GoUwLsk= github.com/klauspost/compress v1.16.6/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/liamylian/jsontime/v2 v2.0.0 h1:3if2kDW/boymUdO+4Qj/m4uaXMBSF6np9KEgg90cwH0= github.com/liamylian/jsontime/v2 v2.0.0/go.mod h1:UHp1oAPqCBfspokvGmaGe0IAl2IgOpgOgDaKPcvcGGY= -github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.10.1 h1:6VXZrLU0jHBYyAqrSPa+MgPfnSvTPuMgK+k0o5kVFWo= github.com/lib/pq v1.10.1/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= -github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= @@ -201,7 +180,6 @@ github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APP github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/mattn/go-sqlite3 v1.14.7/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= @@ -214,20 +192,14 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= -github.com/mozillazg/go-pinyin v0.19.0 h1:p+J8/kjJ558KPvVGYLvqBhxf8jbZA2exSLCs2uUVN8c= -github.com/mozillazg/go-pinyin v0.19.0/go.mod h1:iR4EnMMRXkfpFVV5FMi4FNB6wGq9NV6uDWbUuPhP4Yc= github.com/mozillazg/go-pinyin v0.20.0 h1:BtR3DsxpApHfKReaPO1fCqF4pThRwH9uwvXzm+GnMFQ= github.com/mozillazg/go-pinyin v0.20.0/go.mod h1:iR4EnMMRXkfpFVV5FMi4FNB6wGq9NV6uDWbUuPhP4Yc= -github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= -github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/pelletier/go-toml/v2 v2.0.2 h1:+jQXlF3scKIcSEKkdHzXhCTDLPFi5r1wnK6yPS+49Gw= -github.com/pelletier/go-toml/v2 v2.0.2/go.mod h1:MovirKjgVRESsAvNZlAjtFwV867yGuwRkXbG66OzopI= +github.com/onsi/gomega v1.27.5 h1:T/X6I0RNFw/kTqgfkZPcQ5KU6vCnWNBGdtrIx2dpGeQ= github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= github.com/philhofer/fwd v1.1.2 h1:bnDivRJ1EWPjUIRXV5KfORO897HTbpFAQddBdE8t7Gw= github.com/philhofer/fwd v1.1.2/go.mod h1:qkPdfjR2SIEbspLqpe1tO4n5yICnr2DY7mqEx2tUTP0= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= @@ -243,16 +215,9 @@ github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJ github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= -github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/rueian/rueidis v0.0.73 h1:+r0Z6C6HMnkquPgY3zaHVpTqmCyJL56Z36GSlyBrufk= -github.com/rueian/rueidis v0.0.73/go.mod h1:FwnfDILF2GETrvXcYFlhIiru/7NmSIm1f+7C5kutO0I= github.com/rueian/rueidis v0.0.100 h1:22yp/+8YHuWc/vcrp8bkjeE7baD3vygoh2gZ2+xu1KQ= github.com/rueian/rueidis v0.0.100/go.mod h1:ivvsRYRtAUcf9OnheuKc5Gpa8IebrkLT1P45Lr2jlXE= -github.com/samber/lo v1.27.0 h1:GOyDWxsblvqYobqsmUuMddPa2/mMzkKyojlXol4+LaQ= -github.com/samber/lo v1.27.0/go.mod h1:it33p9UtPMS7z72fP4gw/EIfQB2eI8ke7GR2wc6+Rhg= github.com/samber/lo v1.38.1 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM= github.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA= github.com/savsgio/dictpool v0.0.0-20221023140959-7bf2e61cea94 h1:rmMl4fXJhKMNWl+K+r/fq4FbbKI+Ia2m9hYBLm2h4G4= @@ -262,75 +227,45 @@ github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee h1:8Iv5m6xEo1NR1Avp github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee/go.mod h1:qwtSXrKuJh/zsFQ12yEE89xfCrGKK63Rr7ctU/uCo4g= github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/spf13/afero v1.8.2 h1:xehSyVa0YnHWsJ49JFljMpg1HX19V6NDZ1fkm1Xznbo= -github.com/spf13/afero v1.8.2/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo= github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM= github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= -github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= -github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.12.0 h1:CZ7eSOd3kZoaYDLbXnmzgQI5RlciuXBMA+18HwHRfZQ= -github.com/spf13/viper v1.12.0/go.mod h1:b6COn30jlNxbm/V2IqWiNWkJ+vZNiMNksliPCiuKtSI= github.com/spf13/viper v1.16.0 h1:rGGH0XDZhdUOryiDWjmIvUSWpbNqisK8Wk0Vyefw8hc= github.com/spf13/viper v1.16.0/go.mod h1:yg78JgCJcbrQOvV9YLXgkLaZqUidkY9K+Dd1FofRzQg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= -github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/subosito/gotenv v1.3.0 h1:mjC+YW8QpAdXibNi+vNWgzmgBH4+5l5dCXv8cNysBLI= -github.com/subosito/gotenv v1.3.0/go.mod h1:YzJjq/33h7nrwdY+iHMhEOEEbW0ovIz0tB6t6PwAXzs= github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= -github.com/thoas/go-funk v0.9.1 h1:O549iLZqPpTUQ10ykd26sZhzD+rmR5pWhuElrhbC20M= github.com/tinylib/msgp v1.1.6/go.mod h1:75BAfg2hauQhs3qedfdDZmWAPcFMAvJE5b9rGOMufyw= github.com/tinylib/msgp v1.1.8 h1:FCXC1xanKO4I8plpHGH2P7koL/RzZs12l/+r7vakfm0= github.com/tinylib/msgp v1.1.8/go.mod h1:qkpG+2ldGg4xRFmx+jfTvZPxfGFhi64BcnL9vkCm/Tw= -github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc h1:9lRDQMhESg+zvGYmW5DyG0UqvY96Bu5QYsTLvCHdrgo= -github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc/go.mod h1:bciPuU6GHm1iF1pBvUfxfsH0Wmnc2VbpgvbI9ZWuIRs= -github.com/uptrace/bun v1.1.8 h1:slxuaP4LYWFbPRUmTtQhfJN+6eX/6ar2HDKYTcI50SA= -github.com/uptrace/bun v1.1.8/go.mod h1:iT89ESdV3uMupD9ixt6Khidht+BK0STabK/LeZE+B84= -github.com/uptrace/bun/dialect/pgdialect v1.1.8 h1:wayJhjYDPGv8tgOBLolbBtSFQ0TihFoo8E1T129UdA8= -github.com/uptrace/bun/dialect/pgdialect v1.1.8/go.mod h1:nNbU8PHTjTUM+CRtGmqyBb9zcuRAB8I680/qoFSmBUk= -github.com/uptrace/bun/driver/pgdriver v1.1.8 h1:gyL22axRQfjJS2Umq0erzJnp0bLOdUE8/USKZHPQB8o= -github.com/uptrace/bun/driver/pgdriver v1.1.8/go.mod h1:4tHK0h7a/UoldBoe9J3GU4tEYjr3mkd62U3Kq3PVk3E= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasthttp v1.40.0 h1:CRq/00MfruPGFLTQKY8b+8SfdK60TxNztjRMnH0t1Yc= -github.com/valyala/fasthttp v1.40.0/go.mod h1:t/G+3rLek+CyY9bnIE+YlMRddxVAAGjhxndDB4i4C0I= github.com/valyala/fasthttp v1.47.0 h1:y7moDoxYzMooFpT5aHgNgVOQDrS3qlkfiP9mDtGGK9c= github.com/valyala/fasthttp v1.47.0/go.mod h1:k2zXd82h/7UZc3VOdJ2WaUqt1uZ/XpXAfE9i+HBC3lA= github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= -github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9znI5mJU= -github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc= -github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= -github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= -github.com/xuri/efp v0.0.0-20220603152613-6918739fd470 h1:6932x8ltq1w4utjmfMPVj09jdMlkY0aiA6+Skbtl3/c= github.com/xuri/efp v0.0.0-20220603152613-6918739fd470/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI= github.com/xuri/efp v0.0.0-20230422071738-01f4e37c47e9 h1:ge5g8vsTQclA5lXDi+PuiAFw5GMIlMHOB/5e1hsf96E= github.com/xuri/efp v0.0.0-20230422071738-01f4e37c47e9/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI= -github.com/xuri/excelize/v2 v2.6.1 h1:ICBdtw803rmhLN3zfvyEGH3cwSmZv+kde7LhTDT659k= -github.com/xuri/excelize/v2 v2.6.1/go.mod h1:tL+0m6DNwSXj/sILHbQTYsLi9IF4TW59H2EF3Yrx1AU= github.com/xuri/excelize/v2 v2.7.1 h1:gm8q0UCAyaTt3MEF5wWMjVdmthm2EHAWesGSKS9tdVI= github.com/xuri/excelize/v2 v2.7.1/go.mod h1:qc0+2j4TvAUrBw36ATtcTeC1VCM0fFdAXZOmcF4nTpY= -github.com/xuri/nfp v0.0.0-20220409054826-5e722a1d9e22 h1:OAmKAfT06//esDdpi/DZ8Qsdt4+M5+ltca05dA5bG2M= github.com/xuri/nfp v0.0.0-20220409054826-5e722a1d9e22/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ= github.com/xuri/nfp v0.0.0-20230503010013-3f38cdbb0b83 h1:xVwnvkzzi+OiwhIkWOXvh1skFI6bagk8OvGuazM80Rw= github.com/xuri/nfp v0.0.0-20230503010013-3f38cdbb0b83/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ= @@ -345,19 +280,11 @@ go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= -go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= -go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= -go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= -go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8= -go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/zap v1.23.0 h1:OjGQ5KQDEUawVHxNwQgPpiypGHOxo2mNZsOqTak4fFY= -go.uber.org/zap v1.23.0/go.mod h1:D+nX8jyLsMHMYrln8A0rJjFt/T/9/bGgIhAqxv5URuY= go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -368,18 +295,7 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220817201139-bc19a97f63c8/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d h1:3qF+Z8Hkrw9sOhrFHti9TlB1Hkac1x+DNRkv0XQiFjo= -golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 h1:Y/gsMcFOcR+6S6f3YeMKl5g+dZMEWqcz5Czj/GWYbkM= -golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc= -golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= -golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= -golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= golang.org/x/crypto v0.10.0 h1:LKqV2xt9+kDzSTfOhx4FrkEBcMrAgHSYgzywV9zcGmM= golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I= @@ -393,14 +309,10 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 h1:3MTrJm4PyNL9NBqvYDSj3DHl46qQakyfqfWo4jgfaEM= -golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE= golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc= golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20220413100746-70e8d0d3baa9 h1:LRtI4W37N+KFebI/qV0OFiLUv4GLOWeEW5hn/KEJvxE= -golang.org/x/image v0.0.0-20220413100746-70e8d0d3baa9/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= golang.org/x/image v0.5.0 h1:5JMiNunQeQw++mMOz48/ISeNu3Iweh/JaZU8ZLqHRrI= golang.org/x/image v0.5.0/go.mod h1:FVC7BI/5Ym8R25iw5OLsgshdUBbT1h5jZTpA+mvAdZ4= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -459,15 +371,9 @@ golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220812174116-3211cb980234 h1:RDqmgfe7SvlMWoqC3xwQ2blLO3fcWcxMa3eBLRdRW7E= -golang.org/x/net v0.0.0-20220812174116-3211cb980234/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= -golang.org/x/net v0.6.0 h1:L4ZwwTvKW9gr0ZMS1yrHD9GZhIuVjOBBnaKH+SPQK0Q= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= -golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= @@ -491,7 +397,6 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -530,23 +435,14 @@ golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220907062415-87db552b00fd h1:AZeIEzg+8RCELJYq8w+ODLVxFgLMMigSwO/ffKPEd9U= -golang.org/x/sys v0.0.0-20220907062415-87db552b00fd/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s= golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -561,13 +457,9 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= -golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58= golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= @@ -720,22 +612,13 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/ini.v1 v1.66.4 h1:SsAcf+mM7mRZo2nJNGt8mZCjG8ZRaNGMURJw7BsIST4= -gopkg.in/ini.v1 v1.66.4/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= -gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -745,8 +628,6 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -mellium.im/sasl v0.3.0 h1:0qoaTCTo5Py7u/g0cBIQZcMOgG/5LM71nshbXwznBh8= -mellium.im/sasl v0.3.0/go.mod h1:xm59PUYpZHhgQ9ZqoJ5QaCqzWMi8IeS49dhp6plPCzw= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/logger/bunhook.go b/logger/bunhook.go deleted file mode 100644 index e29ce1a..0000000 --- a/logger/bunhook.go +++ /dev/null @@ -1,163 +0,0 @@ -package logger - -import ( - "bytes" - "context" - "database/sql" - "fmt" - "strings" - "text/template" - "time" - - "github.com/uptrace/bun" - "go.uber.org/zap" - "go.uber.org/zap/zapcore" -) - -// QueryHookOptions logging options -type QueryHookOptions struct { - LogSlow time.Duration - Logger *zap.Logger - QueryLevel zapcore.Level - SlowLevel zapcore.Level - ErrorLevel zapcore.Level - MessageTemplate string - ErrorTemplate string -} - -// QueryHook wraps query hook -type QueryHook struct { - opts QueryHookOptions - errorTemplate *template.Template - messageTemplate *template.Template -} - -// LogEntryVars variables made available t otemplate -type LogEntryVars struct { - Timestamp time.Time - Query string - Operation string - Duration time.Duration - Error error -} - -// NewQueryHook returns new instance -func NewQueryHook(opts QueryHookOptions) *QueryHook { - h := new(QueryHook) - - if opts.ErrorTemplate == "" { - opts.ErrorTemplate = "{{.Operation}}[{{.Duration}}]: {{.Query}}: {{.Error}}" - } - if opts.MessageTemplate == "" { - opts.MessageTemplate = "{{.Operation}}[{{.Duration}}]: {{.Query}}" - } - h.opts = opts - errorTemplate, err := template.New("ErrorTemplate").Parse(h.opts.ErrorTemplate) - if err != nil { - panic(err) - } - messageTemplate, err := template.New("MessageTemplate").Parse(h.opts.MessageTemplate) - if err != nil { - panic(err) - } - - h.errorTemplate = errorTemplate - h.messageTemplate = messageTemplate - return h -} - -// BeforeQuery does nothing tbh -func (h *QueryHook) BeforeQuery(ctx context.Context, event *bun.QueryEvent) context.Context { - return ctx -} - -// AfterQuery convert a bun QueryEvent into a logrus message -func (h *QueryHook) AfterQuery(ctx context.Context, event *bun.QueryEvent) { - var level zapcore.Level - var isError bool - var msg bytes.Buffer - - now := time.Now() - dur := now.Sub(event.StartTime) - - switch event.Err { - case nil, sql.ErrNoRows: - isError = false - if h.opts.LogSlow > 0 && dur >= h.opts.LogSlow { - level = h.opts.SlowLevel - } else { - level = h.opts.QueryLevel - } - default: - isError = true - level = h.opts.ErrorLevel - } - if level == 0 { - return - } - - args := &LogEntryVars{ - Timestamp: now, - Query: string(event.Query), - Operation: eventOperation(event), - Duration: dur, - Error: event.Err, - } - - if isError { - if err := h.errorTemplate.Execute(&msg, args); err != nil { - panic(err) - } - } else { - if err := h.messageTemplate.Execute(&msg, args); err != nil { - panic(err) - } - } - - switch level { - case zapcore.DebugLevel: - h.opts.Logger.Debug(msg.String(), zap.String("query", event.Query), zap.Any("args", event.QueryArgs)) - case zapcore.InfoLevel: - h.opts.Logger.Info(msg.String(), zap.String("query", event.Query), zap.Any("args", event.QueryArgs)) - case zapcore.WarnLevel: - h.opts.Logger.Warn(msg.String(), zap.String("query", event.Query), zap.Any("args", event.QueryArgs)) - case zapcore.ErrorLevel: - h.opts.Logger.Error(msg.String(), zap.String("query", event.Query), zap.Any("args", event.QueryArgs), zap.Error(event.Err)) - case zapcore.FatalLevel: - h.opts.Logger.Fatal(msg.String(), zap.String("query", event.Query), zap.Any("args", event.QueryArgs), zap.Error(event.Err)) - case zapcore.PanicLevel: - h.opts.Logger.Panic(msg.String(), zap.String("query", event.Query), zap.Any("args", event.QueryArgs), zap.Error(event.Err)) - default: - panic(fmt.Errorf("unsupported level: %v", level)) - } -} - -// taken from bun -func eventOperation(event *bun.QueryEvent) string { - switch event.QueryAppender.(type) { - case *bun.SelectQuery: - return "SELECT" - case *bun.InsertQuery: - return "INSERT" - case *bun.UpdateQuery: - return "UPDATE" - case *bun.DeleteQuery: - return "DELETE" - case *bun.CreateTableQuery: - return "CREATE TABLE" - case *bun.DropTableQuery: - return "DROP TABLE" - } - return queryOperation(event.Query) -} - -// taken from bun -func queryOperation(name string) string { - if idx := strings.Index(name, " "); idx > 0 { - name = name[:idx] - } - if len(name) > 16 { - name = name[:16] - } - return string(name) -} diff --git a/migration/20220915101901_initialize.tx.up.sql b/migration/20220915101901_initialize.tx.up.sql deleted file mode 100644 index a8fb2ab..0000000 --- a/migration/20220915101901_initialize.tx.up.sql +++ /dev/null @@ -1,240 +0,0 @@ -create table if not exists region ( - code varchar(15) not null primary key, - name varchar(50) not null, - level smallint not null default 0, - parent varchar(15) not null default '0' -); - -create table if not exists `user` ( - created_at timestamptz not null, - id varchar(120) not null primary key, - username varchar(30) not null, - password varchar(256) not null, - reset_needed boolean not null default false, - type smallint not null, - enabled boolean not null default true -); - -create table if not exists user_detail ( - created_at timestamptz not null, - created_by varchar(100), - last_modified_at timestamptz, - last_modified_by varchar(100), - deleted_at timestamptz, - deleted_by varchar(100), - id varchar(120) not null primary key, - name varchar(100), - abbr varchar(50), - region varchar(10), - address varchar(120), - contact varchar(100), - phone varchar(50), - unit_service_fee numeric(8,2) not null default 0, - service_expiration date not null -); - -create table if not exists user_charge ( - created_at timestamptz not null, - seq bigserial not null primary key, - user_id varchar(120) not null, - fee numeric(12,2), - discount numeric(5,4), - amount numeric(12,2), - charge_to date not null, - settled boolean not null default false, - settled_at timestamptz, - cancelled boolean not null default false, - cancelled_at timestamptz, - refunded boolean not null default false, - refunded_at timestamptz -); - -create table if not exists park ( - created_at timestamptz not null, - created_by varchar(100), - last_modified_at timestamptz, - last_modified_by varchar(100), - deleted_at timestamptz, - deleted_by varchar(100), - id varchar(120) not null primary key, - user_id varchar(120) not null, - name varchar(70) not null, - abbr varchar(50), - area numeric(14,2), - tenement_quantity numeric(8,0), - capacity numeric(16,2), - category smallint not null default 0, - meter_04kv_type smallint not null default 0, - region varchar(10), - address varchar(120), - contact varchar(100), - phone varchar(50), - enabled boolean not null default true -); - -create table if not exists meter_04kv ( - created_at timestamptz not null, - created_by varchar(100), - last_modified_at timestamptz, - last_modified_by varchar(100), - code varchar(120) not null, - park_id varchar(120) not null, - address varchar(100), - customer_name varchar(100), - contact_name varchar(70), - contact_phone varchar(50), - ratio numeric(8,4) not null default 1, - seq bigint not null default 0, - public_meter boolean not null default false, - dilute boolean not null default false, - enabled boolean not null default true, - primary key (code, park_id) -); - -create table if not exists maintenance_fee ( - created_at timestamptz not null, - created_by varchar(100), - last_modified_at timestamptz, - last_modified_by varchar(100), - deleted_at timestamptz, - deleted_by varchar(100), - id varchar(120) not null primary key, - park_id varchar(120) not null, - name varchar(50) not null, - fee numeric(8,2) not null default 0, - memo text, - enabled boolean not null default true -); - -create table if not exists report ( - created_at timestamptz not null, - created_by varchar(100), - last_modified_at timestamptz, - last_modified_by varchar(100), - id varchar(120) not null primary key, - park_id varchar(120) not null, - period date not null, - category smallint not null default 0, - meter_04kv_type smallint not null default 0, - step_state jsonb not null, - published boolean not null default false, - published_at timestamptz, - withdraw smallint not null default 0, - last_withdraw_applied_at timestamptz, - last_withdraw_audit_at timestamptz -); - -create table if not exists report_summary ( - report_id varchar(120) not null primary key, - overall numeric(14,2) not null default 0, - overall_fee numeric(14,2) not null default 0, - consumption_fee numeric(14,2), - overall_price numeric(16,8), - critical numeric(14,2) not null default 0, - critical_fee numeric(14,2) not null default 0, - critical_price numeric(16,8), - peak numeric(14,2) not null default 0, - peak_fee numeric(14,2) not null default 0, - peak_price numeric(16,8), - flat numeric(14,2) not null default 0, - flat_fee numeric(14,2) not null default 0, - flat_price numeric(16,8), - valley numeric(14,2) not null default 0, - valley_fee numeric(14,2) not null default 0, - valley_price numeric(16,8), - loss numeric(14,2), - loss_fee numeric(16,2), - loss_proportion numeric(16,15), - customer_consumption numeric(16,2), - customer_consumption_fee numeric(14,2), - customer_consumption_critical numeric(16,2), - customer_consumption_critical_fee numeric(14,2), - customer_consumption_peak numeric(16,2), - customer_consumption_peak_fee numeric(14,2), - customer_consumption_flat numeric(16,2), - customer_consumption_flat_fee numeric(14,2), - customer_consumption_valley numeric(16,2), - customer_consumption_valley_fee numeric(14,2), - public_consumption numeric(16,2), - public_consumption_fee numeric(14,2), - public_consumption_proportion numeric(16,15), - public_consumption_critical numeric(16,2), - public_consumption_critical_fee numeric(14,2), - public_consumption_peak numeric(16,2), - public_consumption_peak_fee numeric(14,2), - public_consumption_flat numeric(16,2), - public_consumption_flat_fee numeric(14,2), - public_consumption_valley numeric(16,2), - public_consumption_valley_fee numeric(14,2), - basic_fee numeric(14,2) not null default 0, - basic_diluted_price numeric(18,8), - adjust_fee numeric(14,2) not null default 0, - adjust_diluted_price numeric(18,8), - maintenance_diluted_price numeric(16,8), - loss_diluted_price numeric(16,8), - public_consumption_diluted_price numeric(16,8), - maintenance_overall numeric(16,8), - final_diluted_overall numeric(14,2) -); - -create table if not exists will_diluted_fee ( - id varchar(120) not null primary key, - report_id varchar(120) not null, - source_id varchar(120), - name varchar(50) not null, - fee numeric(8,2) not null default 0, - memo text -); - -create table if not exists end_user_detail ( - created_at timestamptz not null, - created_by varchar(100), - last_modified_at timestamptz, - last_modified_by varchar(100), - report_id varchar(120) not null, - park_id varchar(120) not null, - meter_04kv_id varchar(120) not null, - seq bigint not null default 0, - ratio numeric(8,4) not null default 1, - address varchar(100), - customer_name varchar(100), - contact_name varchar(70), - contact_phone varcahar(50), - public_meter boolean not null default false, - dilute boolean not null default false, - last_period_overall numeric(14,2) not null default 0, - last_period_critical numeric(14,2) not null default 0, - last_period_peak numeric(14,2) not null default 0, - last_period_flat numeric(14,2) not null default 0, - last_period_valley numeric(14,2) not null default 0, - current_period_overall numeric(14,2) not null default 0, - current_period_critical numeric(14,2) not null default 0, - current_period_peak numeric(14,2) not null default 0, - current_period_flat numeric(14,2) not null default 0, - current_period_valley numeric(14,2) not null default 0, - adjust_overall numeric(14,2) not null default 0, - adjust_critical numeric(14,2) not null default 0, - adjust_peak numeric(14,2) not null default 0, - adjust_flat numeric(14,2) not null default 0, - adjust_valley numeric(14,2) not null default 0, - overall numeric(14,2), - overall_fee numeric(14,2), - overall_proportion numeric(16,15) not null default 0, - critical numeric(14,2), - critical_fee numeric(18,8), - peak numeric(14,2), - peak_fee numeric(18,8), - flat numeric(14,2), - flat_fee numeric(18,8), - valley numeric(14,2), - valley_fee numeric(18,8), - basic_fee_diluted numeric(18,8), - adjust_fee_diluted numeric(18,8), - loss_diluted numeric(18,8), - loss_fee_diluted numeric(18,8), - maintenance_fee_diluted numeric(18,8), - public_consumption_diluted numeric(18,8), - final_diluted numeric(14,2), - final_charge numeric(14,2), - primary key (report_id, park_id, meter_04kv_id) -); \ No newline at end of file diff --git a/migration/20220915101901_uninitialize.tx.down.sql b/migration/20220915101901_uninitialize.tx.down.sql deleted file mode 100644 index f117cdb..0000000 --- a/migration/20220915101901_uninitialize.tx.down.sql +++ /dev/null @@ -1,21 +0,0 @@ -drop table if exists region; - -drop table if exists user; - -drop table if exists user_detail; - -drop table if exists user_charge; - -drop table if exists park; - -drop table if exists meter_04kv; - -drop table if exists maintenance_fee; - -drop table if exists report; - -drop table if exists report_summary; - -drop table if exists will_diluted_fee; - -drop table if exists end_user_detail; \ No newline at end of file diff --git a/migration/20220915131901_add_constraints.up.sql b/migration/20220915131901_add_constraints.up.sql deleted file mode 100644 index 2289513..0000000 --- a/migration/20220915131901_add_constraints.up.sql +++ /dev/null @@ -1,31 +0,0 @@ -alter table if exists `user` add constraint user_type_check check (type in (0, 1, 2)); - -alter table if exists user_detail add constraint positive_service_fee check (unit_service_fee >= 0); - -alter table if exists user_charge add constraint positive_fee check (fee >= 0); - -alter table if exists user_charge add constraint positive_amount check (amount >= 0); - -alter table if exists park add constraint positive_tenement check (tenement_quantity >= 0); - -alter table if exists park add constraint positive_area check (area >= 0); - -alter table if exists park add constraint positive_capacity check (capacity >= 0); - -alter table if exists park add constraint category_check check (category in (0, 1, 2)); - -alter table if exists park add constraint meter_check check (meter_04kv_type in (0, 1)); - -alter table if exists meter_04kv add constraint positive_ratio check (ratio > 0); - -alter table if exists maintenance_fee add constraint positive_fee check (fee >= 0); - -alter table if exists report add constraint category_check check (category in (0, 1, 2)); - -alter table if exists report add constraint meter_check check (meter_04kv_type in (0, 1)); - -alter table if exists report add constraint withdraw_action_check check (withdraw in (0, 1, 2, 3)); - -alter table if exists will_diluted_fee add constraint positive_fee check (fee >= 0); - -alter table if exists end_user_detail add constraint positive_ratio check (ratio > 0); \ No newline at end of file diff --git a/migration/20220915131901_remove_constraints.down.sql b/migration/20220915131901_remove_constraints.down.sql deleted file mode 100644 index bf653a4..0000000 --- a/migration/20220915131901_remove_constraints.down.sql +++ /dev/null @@ -1,31 +0,0 @@ -alter table if exists user drop constraint user_type_check; - -alter table if exists user_detail drop constraint positive_service_fee; - -alter table if exists user_charge drop constraint positive_fee; - -alter table if exists user_charge drop constraint positive_amount; - -alter table if exists park drop constraint positive_tenement; - -alter table if exists park drop constraint positive_area; - -alter table if exists park drop constraint positive_capacity; - -alter table if exists park drop constraint category_check; - -alter table if exists park drop constraint meter_check; - -alter table if exists meter_04kv drop constraint positive_ratio; - -alter table if exists maintenance_fee drop constraint positive_fee; - -alter table if exists report drop constraint category_check; - -alter table if exists report drop constraint meter_check; - -alter table if exists report drop constraint withdraw_action_check; - -alter table if exists will_diluted_fee drop constraint positive_fee; - -alter table if exists end_user_detail drop constraint positive_ratio; \ No newline at end of file diff --git a/migration/20220920131201_jsonb_to_summary.tx.down.sql b/migration/20220920131201_jsonb_to_summary.tx.down.sql deleted file mode 100644 index ce25791..0000000 --- a/migration/20220920131201_jsonb_to_summary.tx.down.sql +++ /dev/null @@ -1,23 +0,0 @@ -update report_summary -set - customer_consumption = nullif(trim(customers->>'consumption'), '')::numeric(16,2), - customer_consumption_fee = nullif(trim(customers->>'consumptionFee'), '')::numeric(14,2), - customer_consumption_critical = nullif(trim(customers->>'critical'), '')::numeric(16,2), - customer_consumption_critical_fee = nullif(trim(customers->>'criticalFee'), '')::numeric(14,2), - customer_consumption_peak = nullif(trim(customers->>'peak'), '')::numeric(16,2), - customer_consumption_peak_fee = nullif(trim(customers->>'peakFee'), '')::numeric(14,2), - customer_consumption_flat = nullif(trim(customers->>'flat'), '')::numeric(16,2), - customer_consumption_flat_fee = nullif(trim(customers->>'flatFee'), '')::numeric(14,2), - customer_consumption_valley = nullif(trim(customers->>'valley'), '')::numeric(16,2), - customer_consumption_valley_fee = nullif(trim(customers->>'valleyFee'), '')::numeric(14,2), - public_consumption = nullif(trim(publics->>'consumption'), '')::numeric(16,2), - public_consumption_fee = nullif(trim(publics->>'consumptionFee'), '')::numeric(14,2), - public_consumption_critical = nullif(trim(publics->>'critical'), '')::numeric(16,2), - public_consumption_critical_fee = nullif(trim(publics->>'criticalFee'), '')::numeric(14,2), - public_consumption_peak = nullif(trim(publics->>'peak'), '')::numeric(16,2), - public_consumption_peak_fee = nullif(trim(publics->>'peakFee'), '')::numeric(14,2), - public_consumption_flat = nullif(trim(publics->>'flat'), '')::numeric(16,2), - public_consumption_flat_fee = nullif(trim(publics->>'flatFee'), '')::numeric(14,2), - public_consumption_valley = nullif(trim(publics->>'valley'), '')::numeric(16,2), - public_consumption_valley_fee = nullif(trim(publics->>'valleyFee'), '')::numeric(14,2), - public_consumption_proportion = nullif(trim(publics->>'proportion'), '')::numeric(16,15); \ No newline at end of file diff --git a/migration/20220920131201_summary_to_jsonb.tx.up.sql b/migration/20220920131201_summary_to_jsonb.tx.up.sql deleted file mode 100644 index 4497bf0..0000000 --- a/migration/20220920131201_summary_to_jsonb.tx.up.sql +++ /dev/null @@ -1,46 +0,0 @@ -alter table report_summary - add column if not exists customers jsonb, - add column if not exists publics jsonb, - add column if not exists diluteds jsonb; - -update report_summary -set - customers = jsonb_build_object( - 'consumption', customer_consumption, - 'fee', customer_consumption_fee, - 'critical', customer_consumption_critical, - 'criticalFee', customer_consumption_critical_fee, - 'peak', customer_consumption_peak, - 'peakFee', customer_consumption_peak_fee, - 'flat', customer_consumption_flat, - 'flatFee', customer_consumption_flat_fee, - 'valley', customer_consumption_valley, - 'valleyFee', customer_consumption_valley_fee, - 'proportion', null - ), - diluteds = jsonb_build_object( - 'consumption', public_consumption, - 'fee', public_consumption_fee, - 'critical', public_consumption_critical, - 'criticalFee', public_consumption_critical_fee, - 'peak', public_consumption_peak, - 'peakFee', public_consumption_peak_fee, - 'flat', public_consumption_flat, - 'flatFee', public_consumption_flat_fee, - 'valley', public_consumption_valley, - 'valleyFee', public_consumption_valley_fee, - 'proportion', public_consumption_proportion - ), - publics = jsonb_build_object( - 'consumption', null, - 'fee', null, - 'critical', null, - 'criticalFee', null, - 'peak', null, - 'peakFee', null, - 'flat', null, - 'flatFee', null, - 'valley', null, - 'valleyFee', null, - 'proportion', null - ); \ No newline at end of file diff --git a/migration/20220921143001_maintenance.down.sql b/migration/20220921143001_maintenance.down.sql deleted file mode 100644 index 8c30b46..0000000 --- a/migration/20220921143001_maintenance.down.sql +++ /dev/null @@ -1,15 +0,0 @@ -alter table meter_04kv - add column dilute boolean not null default false; - -alter table end_user_detail - add column dilute boolean not null default false, - add column maintenance_fee_diluted numeric(18,8), - add column public_consumption_diluted numeric(18,8); - -alter table maintenance_fee - drop column period; - -alter table report_summary - drop column authorize_loss, - drop column authorize_loss_fee, - drop column authorize_loss_proportion; \ No newline at end of file diff --git a/migration/20220921143001_maintenance.up.sql b/migration/20220921143001_maintenance.up.sql deleted file mode 100644 index 1eff678..0000000 --- a/migration/20220921143001_maintenance.up.sql +++ /dev/null @@ -1,37 +0,0 @@ -alter table meter_04kv - drop column dilute; - -alter table end_user_detail - drop column dilute, - drop column maintenance_fee_diluted, - drop column public_consumption_diluted; - -alter table maintenance_fee - add column period varchar(10); - -alter table report_summary - drop column customer_consumption, - drop column customer_consumption_fee, - drop column customer_consumption_critical, - drop column customer_consumption_critical_fee, - drop column customer_consumption_peak, - drop column customer_consumption_peak_fee, - drop column customer_consumption_flat, - drop column customer_consumption_flat_fee, - drop column customer_consumption_valley, - drop column customer_consumption_valley_fee, - drop column public_consumption, - drop column public_consumption_fee, - drop column public_consumption_critical, - drop column public_consumption_critical_fee, - drop column public_consumption_peak, - drop column public_consumption_peak_fee, - drop column public_consumption_flat, - drop column public_consumption_flat_fee, - drop column public_consumption_valley, - drop column public_consumption_valley_fee, - drop column public_consumption_proportion, - drop column diluteds, - add column authorize_loss numeric(14,2), - add column authorize_loss_fee numeric(16,2), - add column authorize_loss_proportion numeric(16,15); \ No newline at end of file diff --git a/migration/main.go b/migration/main.go deleted file mode 100644 index 09979af..0000000 --- a/migration/main.go +++ /dev/null @@ -1,21 +0,0 @@ -package migration - -import ( - "electricity_bill_calc/logger" - "embed" - - "github.com/uptrace/bun/migrate" - "go.uber.org/zap" -) - -var ( - //go:embed *.sql - sqlMigrations embed.FS - Migrations = migrate.NewMigrations() -) - -func init() { - if err := Migrations.Discover(sqlMigrations); err != nil { - logger.Named("Migrations").Fatal("Unable to load migrations.", zap.Error(err)) - } -} diff --git a/model/calculate/calculate.go b/model/calculate/calculate.go index da5177c..8036402 100644 --- a/model/calculate/calculate.go +++ b/model/calculate/calculate.go @@ -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 @@ -54,6 +60,7 @@ type TenementCharge struct { LossPooled decimal.Decimal PublicPooled decimal.Decimal FinalCharges decimal.Decimal + Loss decimal.Decimal Submeters []*Meter Poolings []*Meter } @@ -90,3 +97,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, + } +} diff --git a/model/end_user_detail.go b/model/end_user_detail.go deleted file mode 100644 index 4c93e52..0000000 --- a/model/end_user_detail.go +++ /dev/null @@ -1,133 +0,0 @@ -package model - -import ( - "context" - "errors" - "time" - - "github.com/shopspring/decimal" - "github.com/uptrace/bun" -) - -type EndUserDetail struct { - bun.BaseModel `bun:"table:end_user_detail,alias:eud"` - CreatedAndModified `bun:"extend"` - ReportId string `bun:",pk,notnull" json:"reportId"` - ParkId string `bun:",pk,notnull" json:"parkId"` - MeterId string `bun:"meter_04kv_id,pk,notnull" json:"meterId"` - Seq int64 `bun:"type:bigint,notnull" json:"seq"` - Ratio decimal.Decimal `bun:"type:numeric,notnull" json:"ratio"` - Address *string `json:"address"` - CustomerName *string `json:"customerName"` - ContactName *string `json:"contactName"` - ContactPhone *string `json:"contactPhone"` - IsPublicMeter bool `bun:"public_meter,notnull" json:"isPublicMeter"` - LastPeriodOverall decimal.Decimal `bun:"type:numeric,notnull" json:"lastPeriodOverall"` - LastPeriodCritical decimal.Decimal `bun:"type:numeric,notnull" json:"lastPeriodCritical"` - LastPeriodPeak decimal.Decimal `bun:"type:numeric,notnull" json:"lastPeriodPeak"` - LastPeriodFlat decimal.Decimal `bun:"type:numeric,notnull" json:"lastPeriodFlat"` - LastPeriodValley decimal.Decimal `bun:"type:numeric,notnull" json:"lastPeriodValley"` - CurrentPeriodOverall decimal.Decimal `bun:"type:numeric,notnull" json:"currentPeriodOverall"` - CurrentPeriodCritical decimal.Decimal `bun:"type:numeric,notnull" json:"currentPeriodCritical"` - CurrentPeriodPeak decimal.Decimal `bun:"type:numeric,notnull" json:"currentPeriodPeak"` - CurrentPeriodFlat decimal.Decimal `bun:"type:numeric,notnull" json:"currentPeriodFlat"` - CurrentPeriodValley decimal.Decimal `bun:"type:numeric,notnull" json:"currentPeriodValley"` - AdjustOverall decimal.Decimal `bun:"type:numeric,notnull" json:"adjustOverall"` - AdjustCritical decimal.Decimal `bun:"type:numeric,notnull" json:"adjustCritical"` - AdjustPeak decimal.Decimal `bun:"type:numeric,notnull" json:"adjustPeak"` - AdjustFlat decimal.Decimal `bun:"type:numeric,notnull" json:"adjustFlat"` - AdjustValley decimal.Decimal `bun:"type:numeric,notnull" json:"adjustValley"` - Overall decimal.NullDecimal `bun:"type:numeric" json:"overall"` - OverallFee decimal.NullDecimal `bun:"type:numeric" json:"overallFee"` - OverallProportion decimal.Decimal `bun:"type:numeric,notnull" json:"-"` - Critical decimal.NullDecimal `bun:"type:numeric" json:"critical"` - CriticalFee decimal.NullDecimal `bun:"type:numeric" json:"criticalFee"` - Peak decimal.NullDecimal `bun:"type:numeric" json:"peak"` - PeakFee decimal.NullDecimal `bun:"type:numeric" json:"peakFee"` - Flat decimal.NullDecimal `bun:"type:numeric" json:"flat"` - FlatFee decimal.NullDecimal `bun:"type:numeric" json:"flatFee"` - Valley decimal.NullDecimal `bun:"type:numeric" json:"valley"` - ValleyFee decimal.NullDecimal `bun:"type:numeric" json:"valleyFee"` - BasicFeeDiluted decimal.NullDecimal `bun:"type:numeric" json:"basicFeeDiluted"` - AdjustFeeDiluted decimal.NullDecimal `bun:"type:numeric" json:"adjustFeeDiluted"` - LossDiluted decimal.NullDecimal `bun:"type:numeric" json:"lossDiluted"` - LossFeeDiluted decimal.NullDecimal `bun:"type:numeric" json:"lossFeeDiluted"` - FinalDiluted decimal.NullDecimal `bun:"type:numeric" json:"finalDiluted"` - FinalCharge decimal.NullDecimal `bun:"type:numeric" json:"finalCharge"` - Initialize bool `bun:"-" json:"-"` - Origin *Meter04KV `bun:"rel:belongs-to,join:park_id=park_id,join:meter_04kv_id=code" json:"-"` - Report *Report `bun:"rel:belongs-to,join:report_id=id" json:"-"` - Park *Park `bun:"rel:belongs-to,join:park_id=id" json:"-"` -} - -var _ bun.BeforeAppendModelHook = (*EndUserDetail)(nil) - -func (d *EndUserDetail) BeforeAppendModel(ctx context.Context, query bun.Query) error { - oprTime := time.Now() - switch query.(type) { - case *bun.InsertQuery: - d.CreatedAt = oprTime - d.LastModifiedAt = &oprTime - case *bun.UpdateQuery: - d.LastModifiedAt = &oprTime - } - return nil -} - -func (d EndUserDetail) Validate() (bool, error) { - lastPeriodSum := decimal.Sum(d.LastPeriodCritical, d.LastPeriodPeak, d.LastPeriodValley) - if lastPeriodSum.GreaterThan(d.LastPeriodOverall) { - return false, errors.New("上期峰谷计量总量大于上期总计电量") - } - currentPeriodSum := decimal.Sum(d.CurrentPeriodCritical, d.CurrentPeriodPeak, d.CurrentPeriodValley) - if currentPeriodSum.GreaterThan(d.CurrentPeriodOverall) { - return false, errors.New("本期峰谷计量总量大于本期总计电量") - } - return true, nil -} - -func (d *EndUserDetail) CalculatePeriod() { - d.LastPeriodFlat = d.LastPeriodOverall.Sub(d.LastPeriodCritical).Sub(d.LastPeriodPeak).Sub(d.LastPeriodValley) - d.CurrentPeriodFlat = d.CurrentPeriodOverall.Sub(d.CurrentPeriodCritical).Sub(d.CurrentPeriodPeak).Sub(d.CurrentPeriodValley) - d.Overall = decimal.NewNullDecimal(d.CurrentPeriodOverall.Sub(d.LastPeriodOverall).Mul(d.Ratio).Add(d.AdjustOverall).RoundBank(2)) - d.Critical = decimal.NewNullDecimal(d.CurrentPeriodCritical.Sub(d.LastPeriodCritical).Mul(d.Ratio).Add(d.AdjustCritical).RoundBank(2)) - d.Peak = decimal.NewNullDecimal(d.CurrentPeriodPeak.Sub(d.LastPeriodPeak).Mul(d.Ratio).Add(d.AdjustPeak).RoundBank(2)) - d.Flat = decimal.NewNullDecimal(d.CurrentPeriodFlat.Sub(d.LastPeriodFlat).Mul(d.Ratio).Add(d.AdjustFlat).RoundBank(2)) - d.Valley = decimal.NewNullDecimal(d.CurrentPeriodValley.Sub(d.LastPeriodValley).Mul(d.Ratio).Add(d.AdjustValley).RoundBank(2)) -} - -type EndUserImport struct { - MeterId string `excel:"meterId"` - LastPeriodOverall decimal.Decimal `excel:"lastPeriodOverall"` - CurrentPeriodOverall decimal.Decimal `excel:"currentPeriodOverall"` - LastPeriodCritical decimal.NullDecimal `excel:"lastPeriodCritical"` - LastPeriodPeak decimal.NullDecimal `excel:"lastPeriodPeak"` - LastPeriodValley decimal.NullDecimal `excel:"lastPeriodValley"` - CurrentPeriodCritical decimal.NullDecimal `excel:"currentPeriodCritical"` - CurrentPeriodPeak decimal.NullDecimal `excel:"currentPeriodPeak"` - CurrentPeriodValley decimal.NullDecimal `excel:"currentPeriodValley"` - AdjustOverall decimal.Decimal `excel:"adjustOverall"` - AdjustCritical decimal.NullDecimal `excel:"adjustCritical"` - AdjustPeak decimal.NullDecimal `excel:"adjustPeak"` - AdjustFlat decimal.NullDecimal `excel:"adjustFlat"` - AdjustValley decimal.NullDecimal `excel:"adjustValley"` -} - -type EndUserPeriodStat struct { - CustomerName string `json:"customerName"` - Address string `json:"address"` - ParkId string `json:"parkId"` - MeterId string `bun:"meter_04kv_id" json:"meterId"` - IsPublicMeter bool `bun:"public_meter" json:"isPublicMeter"` - Kind int8 `bun:"-" json:"pvKind"` - Overall decimal.NullDecimal `json:"overall"` - Critical decimal.NullDecimal `json:"critical"` - Peak decimal.NullDecimal `json:"peak"` - Valley decimal.NullDecimal `json:"valley"` - OverallFee decimal.NullDecimal `json:"overallFee"` - CriticalFee decimal.NullDecimal `json:"criticalFee"` - PeakFee decimal.NullDecimal `json:"peakFee"` - ValleyFee decimal.NullDecimal `json:"valleyFee"` - AdjustFee decimal.NullDecimal `bun:"final_diluted" json:"adjustFee"` - AdjustProportion decimal.NullDecimal `bun:"-" json:"adjustProportion"` -} diff --git a/model/maintenance_fee.go b/model/maintenance_fee.go deleted file mode 100644 index 47dcb8a..0000000 --- a/model/maintenance_fee.go +++ /dev/null @@ -1,48 +0,0 @@ -package model - -import ( - "context" - "time" - - "github.com/shopspring/decimal" - "github.com/uptrace/bun" -) - -type MaintenanceFee struct { - bun.BaseModel `bun:"table:maintenance_fee,alias:m"` - CreatedAndModified `bun:"extend"` - Deleted `bun:"extend"` - Id string `bun:",pk,notnull" json:"id"` - ParkId string `bun:",notnull" json:"parkId"` - Name string `bun:",notnull" json:"name"` - Period string `bun:",notnull" json:"period"` - Fee decimal.Decimal `bun:"type:numeric,notnull" json:"fee"` - Memo *string `bun:"type:text" json:"memo"` - Enabled bool `bun:",notnull" json:"enabled"` - Park Park `bun:"rel:belongs-to,join:park_id=id"` -} - -var _ bun.BeforeAppendModelHook = (*MaintenanceFee)(nil) - -func (f *MaintenanceFee) BeforeAppendModel(ctx context.Context, query bun.Query) error { - oprTime := time.Now() - switch query.(type) { - case *bun.InsertQuery: - f.CreatedAt = oprTime - f.LastModifiedAt = &oprTime - case *bun.UpdateQuery: - f.LastModifiedAt = &oprTime - } - return nil -} - -type AdditionalCharge struct { - ParkId string `json:"parkId"` - Period string `json:"period"` - Fee decimal.Decimal `json:"fee"` - Price decimal.Decimal `json:"price"` - QuarterPrice decimal.Decimal `json:"quarterPrice"` - SemiAnnualPrice decimal.Decimal `json:"semiAnnualPrice"` - Enterprise UserDetailSimplified `json:"user"` - Park Park `json:"park"` -} diff --git a/model/meter_04kv.go b/model/meter_04kv.go deleted file mode 100644 index 407f79b..0000000 --- a/model/meter_04kv.go +++ /dev/null @@ -1,39 +0,0 @@ -package model - -import ( - "context" - "time" - - "github.com/shopspring/decimal" - "github.com/uptrace/bun" -) - -type Meter04KV struct { - bun.BaseModel `bun:"table:meter_04kv,alias:mt"` - CreatedAndModified `bun:"extend"` - Code string `bun:",pk,notnull" json:"code" excel:"code"` - ParkId string `bun:",pk,notnull" json:"parkId"` - Address *string `json:"address" excel:"address"` - CustomerName *string `json:"customerName" excel:"name"` - ContactName *string `json:"contactName" excel:"contact"` - ContactPhone *string `json:"contactPhone" excel:"phone"` - Ratio decimal.Decimal `bun:"type:numeric,notnull" json:"ratio" excel:"ratio"` - Seq int64 `bun:"type:bigint,notnull" json:"seq" excel:"seq"` - IsPublicMeter bool `bun:"public_meter,notnull" json:"isPublicMeter" excel:"public"` - Enabled bool `bun:",notnull" json:"enabled"` - ParkDetail *Park `bun:"rel:belongs-to,join:park_id=id" json:"-"` -} - -var _ bun.BeforeAppendModelHook = (*Meter04KV)(nil) - -func (m *Meter04KV) BeforeAppendModel(ctx context.Context, query bun.Query) error { - oprTime := time.Now() - switch query.(type) { - case *bun.InsertQuery: - m.CreatedAt = oprTime - m.LastModifiedAt = &oprTime - case *bun.UpdateQuery: - m.LastModifiedAt = &oprTime - } - return nil -} diff --git a/model/park.go b/model/park.go index 9dfd190..a210e83 100644 --- a/model/park.go +++ b/model/park.go @@ -1,6 +1,8 @@ package model import ( + "electricity_bill_calc/types" + "time" "github.com/shopspring/decimal" @@ -31,3 +33,14 @@ type Park struct { LastModifiedAt time.Time `json:"lastModifiedAt"` DeletedAt *time.Time `json:"deletedAt"` } + +type Parks struct { + Park + NormAuthorizedLossRate float64 `json:"norm_authorized_loss_rate"` +} + +type ParkPeriodStatistics struct { + Id string `json:"id"` + Name string `json:"name"` + Period *types.DateRange +} diff --git a/model/publicity.go b/model/publicity.go deleted file mode 100644 index 088e3cd..0000000 --- a/model/publicity.go +++ /dev/null @@ -1,116 +0,0 @@ -package model - -import "github.com/shopspring/decimal" - -type PaidPart struct { - Overall decimal.Decimal `json:"overall"` - OverallPrice decimal.Decimal `json:"overallPrice"` - ConsumptionFee decimal.Decimal `json:"consumptionFee"` - OverallFee decimal.Decimal `json:"overallFee"` - Critical decimal.NullDecimal `json:"critical"` - CriticalPrice decimal.NullDecimal `json:"criticalPrice"` - CriticalFee decimal.NullDecimal `json:"criticalFee"` - Peak decimal.NullDecimal `json:"peak"` - PeakPrice decimal.NullDecimal `json:"peakPrice"` - PeakFee decimal.NullDecimal `json:"peakFee"` - Flat decimal.NullDecimal `json:"flat"` - FlatPrice decimal.NullDecimal `json:"flatPrice"` - FlatFee decimal.NullDecimal `json:"flatFee"` - Valley decimal.NullDecimal `json:"valley"` - ValleyPrice decimal.NullDecimal `json:"valleyPrice"` - ValleyFee decimal.NullDecimal `json:"valleyFee"` - BasicFee decimal.Decimal `json:"basicFee"` - AdjustFee decimal.Decimal `json:"adjustFee"` -} - -type EndUserOverallPart struct { - Overall decimal.Decimal `json:"overall"` - OverallPrice decimal.Decimal `json:"overallPrice"` - OverallFee decimal.Decimal `json:"consumptionFee"` - Critical decimal.NullDecimal `json:"critical"` - CriticalPrice decimal.NullDecimal `json:"criticalPrice"` - CriticalFee decimal.NullDecimal `json:"criticalFee"` - Peak decimal.NullDecimal `json:"peak"` - PeakPrice decimal.NullDecimal `json:"peakPrice"` - PeakFee decimal.NullDecimal `json:"peakFee"` - Flat decimal.NullDecimal `json:"flat"` - FlatPrice decimal.NullDecimal `json:"flatPrice"` - FlatFee decimal.NullDecimal `json:"flatFee"` - Valley decimal.NullDecimal `json:"valley"` - ValleyPrice decimal.NullDecimal `json:"valleyPrice"` - ValleyFee decimal.NullDecimal `json:"valleyFee"` -} - -type ConsumptionOverallPart struct { - Overall decimal.Decimal `json:"overall"` - OverallPrice decimal.Decimal `json:"overallPrice"` - ConsumptionFee decimal.Decimal `json:"consumptionFee"` - OverallFee decimal.Decimal `json:"overallFee"` - Critical decimal.NullDecimal `json:"critical"` - CriticalPrice decimal.NullDecimal `json:"criticalPrice"` - CriticalFee decimal.NullDecimal `json:"criticalFee"` - Peak decimal.NullDecimal `json:"peak"` - PeakPrice decimal.NullDecimal `json:"peakPrice"` - PeakFee decimal.NullDecimal `json:"peakFee"` - Flat decimal.NullDecimal `json:"flat"` - FlatPrice decimal.NullDecimal `json:"flatPrice"` - FlatFee decimal.NullDecimal `json:"flatFee"` - Valley decimal.NullDecimal `json:"valley"` - ValleyPrice decimal.NullDecimal `json:"valleyPrice"` - ValleyFee decimal.NullDecimal `json:"valleyFee"` - Proportion decimal.Decimal `json:"proportion"` -} - -type LossPart struct { - Quantity decimal.Decimal `json:"quantity"` - Price decimal.Decimal `json:"price"` - ConsumptionFee decimal.Decimal `json:"consumptionFee"` - Proportion decimal.Decimal `json:"proportion"` - AuthorizeQuantity decimal.Decimal `json:"authorizeQuantity"` - AuthorizeConsumptionFee decimal.Decimal `json:"authorizeConsumptionFee"` -} - -type OtherShouldCollectionPart struct { - LossFee decimal.NullDecimal `json:"lossFee"` - BasicFees decimal.Decimal `json:"basicFees"` -} - -type MaintenancePart struct { - BasicFees decimal.Decimal `json:"basicFees"` - LossFee decimal.Decimal `json:"lossFee"` - AdjustFee decimal.Decimal `json:"adjustFee"` - LossProportion decimal.Decimal `json:"lossProportion"` - AdjustProportion decimal.Decimal `json:"adjustProportion"` - AdjustPrice decimal.Decimal `json:"adjustPrice"` -} - -type EndUserSummary struct { - CustomerName *string `json:"customerName"` - Address *string `json:"address"` - MeterId string `json:"meterId"` - IsPublicMeter bool `json:"isPublicMeter"` - Overall decimal.Decimal `json:"overall"` - OverallPrice decimal.Decimal `json:"overallPrice"` - OverallFee decimal.Decimal `json:"overallFee"` - Critical decimal.NullDecimal `json:"critical"` - CriticalFee decimal.NullDecimal `json:"criticalFee"` - Peak decimal.NullDecimal `json:"peak"` - PeakFee decimal.NullDecimal `json:"peakFee"` - Valley decimal.NullDecimal `json:"valley"` - ValleyFee decimal.NullDecimal `json:"valleyFee"` - Loss decimal.Decimal `json:"loss"` - LossFee decimal.Decimal `json:"lossFee"` -} - -type Publicity struct { - Report Report `json:"index"` - User UserDetail `json:"enterprise"` - Park Park `json:"park"` - Paid PaidPart `json:"paid"` - EndUser ConsumptionOverallPart `json:"endUserSum"` - Loss LossPart `json:"loss"` - PublicConsumptionOverall ConsumptionOverallPart `json:"public"` - OtherCollections OtherShouldCollectionPart `json:"others"` - Maintenance MaintenancePart `json:"maintenance"` - EndUserDetails []EndUserSummary `json:"endUser"` -} diff --git a/model/report.go b/model/report.go index 2972647..1d7d207 100644 --- a/model/report.go +++ b/model/report.go @@ -7,25 +7,27 @@ import ( ) type ReportIndex struct { - Id string `json:"id"` - Park string `json:"parkId" db:"park_id"` - Period types.DateRange `json:"period"` - Category int16 `json:"category"` - MeterType int16 `json:"meter04kvType" db:"meter_04kv_type"` - PricePolicy int16 `json:"pricePolicy"` - BasisPooled int16 `json:"basisPooled"` - AdjustPooled int16 `json:"adjustPooled"` - LossPooled int16 `json:"lossPooled"` - PublicPooled int16 `json:"publicPooled"` - Published bool `json:"published"` - PublishedAt *types.DateTime `json:"publishedAt" db:"published_at"` - Withdraw int16 `json:"withdraw"` - LastWithdrawAppliedAt *types.DateTime `json:"lastWithdrawAppliedAt" db:"last_withdraw_applied_at"` - LastWithdrawAuditAt *types.DateTime `json:"lastWithdrawAuditAt" db:"last_withdraw_audit_at"` - Status *int16 `json:"status"` - Message *string `json:"message"` - CreatedAt types.DateTime `json:"createdAt" db:"created_at"` - LastModifiedAt types.DateTime `json:"lastModifiedAt" db:"last_modified_at"` + Id string `json:"id"` + Park string `json:"parkId" db:"park_id"` + Period types.DateRange `json:"period"` + Category int16 `json:"category"` + MeterType int16 `json:"meter04kvType" db:"meter_04kv_type"` + PricePolicy int16 `json:"pricePolicy"` + BasisPooled int16 `json:"basisPooled"` + AdjustPooled int16 `json:"adjustPooled"` + LossPooled int16 `json:"lossPooled"` + PublicPooled int16 `json:"publicPooled"` + Published bool `json:"published"` + PublishedAt *types.DateTime `json:"publishedAt" db:"published_at"` + Withdraw int16 `json:"withdraw"` + LastWithdrawAppliedAt *types.DateTime `json:"lastWithdrawAppliedAt" db:"last_withdraw_applied_at"` + LastWithdrawAuditAt *types.DateTime `json:"lastWithdrawAuditAt" db:"last_withdraw_audit_at"` + Status *int16 `json:"status"` + Message *string `json:"message"` + CreatedAt types.DateTime `json:"createdAt" db:"created_at"` + LastModifiedAt types.DateTime `json:"lastModifiedAt" db:"last_modified_at"` + AuthorizedLossRate float64 `json:"authorized_loss_rate" db:"authorized_loss_rate"` + AuthorizedLossRateIncrement float64 `json:"authorized_loss_rate_increment" db:"authorized_loss_rate_increment"` } type ReportSummary struct { diff --git a/model/report_summary.go b/model/report_summary.go deleted file mode 100644 index 467c955..0000000 --- a/model/report_summary.go +++ /dev/null @@ -1,119 +0,0 @@ -package model - -import ( - "errors" - - "github.com/shopspring/decimal" - "github.com/uptrace/bun" -) - -type ReportSummary struct { - bun.BaseModel `bun:"table:report_summary,alias:rs"` - ReportId string `bun:",pk,notnull" json:"-"` - Overall decimal.Decimal `bun:"type:numeric,notnull" json:"overall"` - OverallFee decimal.Decimal `bun:"type:numeric,notnull" json:"overallFee"` - ConsumptionFee decimal.NullDecimal `bun:"type:numeric" json:"consumptionFee"` - OverallPrice decimal.NullDecimal `bun:"type:numeric" json:"overallPrice"` - Critical decimal.Decimal `bun:"type:numeric,notnull" json:"critical"` - CriticalFee decimal.Decimal `bun:"type:numeric,notnull" json:"criticalFee"` - CriticalPrice decimal.NullDecimal `bun:"type:numeric" json:"criticalPrice"` - Peak decimal.Decimal `bun:"type:numeric,notnull" json:"peak"` - PeakFee decimal.Decimal `bun:"type:numeric,notnull" json:"peakFee"` - PeakPrice decimal.NullDecimal `bun:"type:numeric" json:"peakPrice"` - Flat decimal.Decimal `bun:"type:numeric,notnull" json:"flat"` - FlatFee decimal.Decimal `bun:"type:numeric,notnull" json:"flatFee"` - FlatPrice decimal.NullDecimal `bun:"type:numeric" json:"flatPrice"` - Valley decimal.Decimal `bun:"type:numeric,notnull" json:"valley"` - ValleyFee decimal.Decimal `bun:"type:numeric,notnull" json:"valleyFee"` - ValleyPrice decimal.NullDecimal `bun:"type:numeric" json:"valleyPrice"` - Loss decimal.NullDecimal `bun:"type:numeric" json:"loss"` - LossFee decimal.NullDecimal `bun:"type:numeric" json:"lossFee"` - LossProportion decimal.NullDecimal `bun:"type:numeric" json:"lossProportion"` - AuthorizeLoss decimal.NullDecimal `bun:"type:numeric" json:"authorizeLoss"` - AuthorizeLossFee decimal.NullDecimal `bun:"type:numeric" json:"authorizeLossFee"` - AuthorizeLossProportion decimal.NullDecimal `bun:"type:numeric" json:"authorizeLossProportion"` - BasicFee decimal.Decimal `bun:"type:numeric,notnull" json:"basicFee"` - BasicDilutedPrice decimal.NullDecimal `bun:"type:numeric" json:"basicDilutedPrice"` - AdjustFee decimal.Decimal `bun:"type:numeric,notnull" json:"adjustFee"` - AdjustDilutedPrice decimal.NullDecimal `bun:"type:numeric" json:"adjustDilutedPrice"` - MaintenanceDilutedPrice decimal.NullDecimal `bun:"type:numeric" json:"maintencanceDilutedPrice"` - LossDilutedPrice decimal.NullDecimal `bun:"type:numeric" json:"lossDilutedPrice"` - PublicConsumptionDilutedPrice decimal.NullDecimal `bun:"type:numeric" json:"publicConsumptionDilutedPrice"` - MaintenanceOverall decimal.NullDecimal `bun:"type:numeric" json:"maintenanceOverall"` - FinalDilutedOverall decimal.NullDecimal `bun:"type:numeric" json:"finalDilutedOverall"` - Customers Consumptions `bun:"type:jsonb" json:"customers"` - Publics Consumptions `bun:"type:jsonb" json:"publics"` -} - -type Consumptions struct { - Consumption decimal.NullDecimal `json:"consumption"` - ConsumptionFee decimal.NullDecimal `json:"fee"` - Critical decimal.NullDecimal `json:"critical"` - CriticalFee decimal.NullDecimal `json:"criticalFee"` - Peak decimal.NullDecimal `json:"peak"` - PeakFee decimal.NullDecimal `json:"peakFee"` - Flat decimal.NullDecimal `json:"flat"` - FlatFee decimal.NullDecimal `json:"flatFee"` - Valley decimal.NullDecimal `json:"valley"` - ValleyFee decimal.NullDecimal `json:"valleyFee"` - Proportion decimal.NullDecimal `json:"proportion"` -} - -func NewConsumptions() Consumptions { - return Consumptions{ - Consumption: decimal.NewNullDecimal(decimal.Zero), - ConsumptionFee: decimal.NewNullDecimal(decimal.Zero), - Critical: decimal.NewNullDecimal(decimal.Zero), - Peak: decimal.NewNullDecimal(decimal.Zero), - PeakFee: decimal.NewNullDecimal(decimal.Zero), - Flat: decimal.NewNullDecimal(decimal.Zero), - CriticalFee: decimal.NewNullDecimal(decimal.Zero), - FlatFee: decimal.NewNullDecimal(decimal.Zero), - Valley: decimal.NewNullDecimal(decimal.Zero), - ValleyFee: decimal.NewNullDecimal(decimal.Zero), - Proportion: decimal.NewNullDecimal(decimal.Zero), - } -} - -func (s ReportSummary) Validate() (bool, error) { - amountSum := decimal.Sum(s.Critical, s.Peak, s.Valley) - if amountSum.GreaterThan(s.Overall) { - return false, errors.New("峰谷计量总量大于总计电量") - } - feeSum := decimal.Sum(s.CriticalFee, s.PeakFee, s.ValleyFee) - if feeSum.GreaterThan(s.OverallFee) { - return false, errors.New("峰谷计量费用大于总计费用") - } - return true, nil -} - -func (s *ReportSummary) CalculatePrices() { - s.ConsumptionFee = decimal.NewNullDecimal(s.OverallFee.Sub(s.BasicFee).Sub(s.AdjustFee)) - if s.Overall.GreaterThan(decimal.Zero) { - s.OverallPrice = decimal.NewNullDecimal(s.ConsumptionFee.Decimal.Div(s.Overall).RoundBank(8)) - } else { - s.OverallPrice = decimal.NewNullDecimal(decimal.Zero) - } - if s.Critical.GreaterThan(decimal.Zero) { - s.CriticalPrice = decimal.NewNullDecimal(s.CriticalFee.Div(s.Critical).RoundBank(8)) - } else { - s.CriticalPrice = decimal.NewNullDecimal(decimal.Zero) - } - if s.Peak.GreaterThan(decimal.Zero) { - s.PeakPrice = decimal.NewNullDecimal(s.PeakFee.Div(s.Peak).RoundBank(8)) - } else { - s.PeakPrice = decimal.NewNullDecimal(decimal.Zero) - } - if s.Valley.GreaterThan(decimal.Zero) { - s.ValleyPrice = decimal.NewNullDecimal(s.ValleyFee.Div(s.Valley).RoundBank(8)) - } else { - s.ValleyPrice = decimal.NewNullDecimal(decimal.Zero) - } - s.Flat = s.Overall.Sub(s.Critical).Sub(s.Peak).Sub(s.Valley) - s.FlatFee = s.ConsumptionFee.Decimal.Sub(s.CriticalFee).Sub(s.PeakFee).Sub(s.ValleyFee) - if s.Flat.GreaterThan(decimal.Zero) { - s.FlatPrice = decimal.NewNullDecimal(s.FlatFee.Div(s.Flat).RoundBank(8)) - } else { - s.FlatPrice = decimal.NewNullDecimal(decimal.Zero) - } -} diff --git a/model/shared.go b/model/shared.go deleted file mode 100644 index 84d2fe0..0000000 --- a/model/shared.go +++ /dev/null @@ -1,32 +0,0 @@ -package model - -import "time" - -type Created struct { - CreatedAt time.Time `bun:"type:timestamptz,notnull" json:"createdAt" time_format:"simple_datetime" time_location:"shanghai"` -} - -type CreatedWithUser struct { - Created `bun:"extend"` - CreatedBy *string `json:"createdBy"` -} - -type Deleted struct { - DeletedAt *time.Time `bun:"type:timestamptz,soft_delete,nullzero" json:"deletedAt" time_format:"simple_datetime" time_location:"shanghai"` -} - -type DeletedWithUser struct { - Deleted `bun:"extend"` - DeletedBy *string `json:"deletedBy"` -} - -type CreatedAndModified struct { - Created `bun:"extend"` - LastModifiedAt *time.Time `bun:"type:timestamptz,nullzero" json:"lastModifiedAt" time_format:"simple_datetime" time_location:"shanghai"` -} - -type CreatedAndModifiedWithUser struct { - CreatedAndModified `bun:"extend"` - CreatedBy *string `json:"createdBy"` - LastModifiedBy *string `json:"lastModifiedBy"` -} diff --git a/model/tenement.go b/model/tenement.go index eda08f0..c5c0282 100644 --- a/model/tenement.go +++ b/model/tenement.go @@ -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"` +} diff --git a/model/types.go b/model/types.go deleted file mode 100644 index 90f5713..0000000 --- a/model/types.go +++ /dev/null @@ -1,118 +0,0 @@ -package model - -import ( - "database/sql" - "database/sql/driver" - "encoding/json" - "fmt" - "time" -) - -type Date struct { - time.Time -} - -func NewDate(t time.Time) Date { - loc, err := time.LoadLocation("Asia/Shanghai") - if err != nil { - panic(err) - } - t = t.In(loc) - return Date{ - Time: time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, loc), - } -} - -func NewEmptyDate() Date { - loc, err := time.LoadLocation("Asia/Shanghai") - if err != nil { - panic(err) - } - return Date{ - Time: time.Time{}.In(loc), - } -} - -func ParseDate(t string) (Date, error) { - loc, err := time.LoadLocation("Asia/Shanghai") - if err != nil { - return NewEmptyDate(), fmt.Errorf("unable to load time zone, %w", err) - } - d, err := time.ParseInLocation("2006-01-02", t, loc) - if err != nil { - return NewEmptyDate(), fmt.Errorf("unable to parse given time, %w", err) - } - return Date{ - Time: d, - }, nil -} - -func (d Date) IsEmpty() bool { - return d.Time.IsZero() -} - -func (d Date) Format(fmt string) string { - return d.Time.Format(fmt) -} - -func (d Date) ToString() string { - return d.Time.Format("2006-01-02") -} - -var _ driver.Valuer = (*Date)(nil) - -func (d Date) Value() (driver.Value, error) { - loc, err := time.LoadLocation("Asia/Shanghai") - if err != nil { - panic(err) - } - return d.In(loc).Format("2006-01-02"), nil -} - -var _ sql.Scanner = (*Date)(nil) - -// Scan scans the time parsing it if necessary using timeFormat. -func (d *Date) Scan(src interface{}) (err error) { - loc, err := time.LoadLocation("Asia/Shanghai") - if err != nil { - panic(err) - } - switch src := src.(type) { - case time.Time: - *d = NewDate(src) - return nil - case string: - d.Time, err = time.ParseInLocation("2006-01-02", src, loc) - return err - case []byte: - d.Time, err = time.ParseInLocation("2006-01-02", string(src), loc) - return err - case nil: - d.Time = time.Time{} - return nil - default: - return fmt.Errorf("unsupported data type: %T", src) - } -} - -var _ json.Marshaler = (*Date)(nil) - -func (d Date) MarshalJSON() ([]byte, error) { - return json.Marshal(d.Time.Format("2006-01-02")) -} - -var _ json.Unmarshaler = (*Date)(nil) - -func (d *Date) UnmarshalJSON(raw []byte) error { - loc, err := time.LoadLocation("Asia/Shanghai") - if err != nil { - return fmt.Errorf("unable to load time zone, %w", err) - } - var s string - err = json.Unmarshal(raw, &s) - if err != nil { - return fmt.Errorf("unable to unmarshal value, %w", err) - } - d.Time, err = time.ParseInLocation("2006-01-02", s, loc) - return err -} diff --git a/model/user_charges.go b/model/user_charges.go deleted file mode 100644 index fc80c60..0000000 --- a/model/user_charges.go +++ /dev/null @@ -1,43 +0,0 @@ -package model - -import ( - "context" - "time" - - "github.com/shopspring/decimal" - "github.com/uptrace/bun" -) - -type UserCharge struct { - bun.BaseModel `bun:"table:user_charge,alias:c"` - Created `bun:"extend"` - Seq int64 `bun:"type:bigint,pk,notnull,autoincrement" json:"seq"` - UserId string `bun:",notnull" json:"userId"` - Fee decimal.NullDecimal `bun:"type:numeric" json:"fee"` - Discount decimal.NullDecimal `bun:"type:numeric" json:"discount"` - Amount decimal.NullDecimal `bun:"type:numeric" json:"amount"` - ChargeTo Date `bun:"type:date,notnull" json:"chargeTo"` - Settled bool `bun:",notnull" json:"settled"` - SettledAt *time.Time `bun:"type:timestamptz" json:"settledAt" time_format:"simple_datetime" time_location:"shanghai"` - Cancelled bool `bun:",notnull" json:"cancelled"` - CancelledAt *time.Time `bun:"type:timestamptz" json:"cancelledAt" time_format:"simple_datetime" time_location:"shanghai"` - Refunded bool `bun:",notnull" json:"refunded"` - RefundedAt *time.Time `bun:"type:timestamptz" json:"refundedAt" time_format:"simple_datetime" time_location:"shanghai"` - Detail *UserDetail `bun:"rel:belongs-to,join:user_id=id" json:"-"` -} - -type ChargeWithName struct { - UserDetail `bun:"extend"` - UserCharge `bun:"extend"` -} - -var _ bun.BeforeAppendModelHook = (*UserCharge)(nil) - -func (uc *UserCharge) BeforeAppendModel(ctx context.Context, query bun.Query) error { - oprTime := time.Now() - switch query.(type) { - case *bun.InsertQuery: - uc.CreatedAt = oprTime - } - return nil -} diff --git a/model/user_detail.go b/model/user_detail.go deleted file mode 100644 index 3cd0190..0000000 --- a/model/user_detail.go +++ /dev/null @@ -1,69 +0,0 @@ -package model - -import ( - "context" - "time" - - "github.com/jinzhu/copier" - "github.com/shopspring/decimal" - "github.com/uptrace/bun" -) - -type UserDetail struct { - bun.BaseModel `bun:"table:user_detail,alias:d"` - CreatedAndModifiedWithUser `bun:"extend"` - DeletedWithUser `bun:"extend"` - Id string `bun:",pk,notnull" json:"-"` - Name *string `json:"name"` - Abbr *string `json:"abbr"` - Region *string `json:"region"` - Address *string `json:"address"` - Contact *string `json:"contact"` - Phone *string `json:"phone"` - UnitServiceFee decimal.Decimal `bun:"type:numeric,notnull" json:"unitServiceFee"` - ServiceExpiration Date `bun:"type:date,notnull" json:"serviceExpiration"` -} - -type JoinedUserDetail struct { - UserDetail `bun:"extend"` - Id string `json:"id"` - Username string `json:"username"` - Type int8 `json:"type"` - Enabled bool `json:"enabled"` -} - -type FullJoinedUserDetail struct { - UserDetail `bun:"extend"` - User `bun:"extend"` -} - -type UserDetailSimplified struct { - bun.BaseModel `bun:"table:user_detail,alias:d"` - Id string `bun:",pk,notnull" json:"id"` - Name *string `json:"name"` - Abbr *string `json:"abbr"` - Region *string `json:"region"` - Address *string `json:"address"` - Contact *string `json:"contact"` - Phone *string `json:"phone"` -} - -func FromUserDetail(user UserDetail) UserDetailSimplified { - dest := UserDetailSimplified{} - copier.Copy(&dest, user) - return dest -} - -var _ bun.BeforeAppendModelHook = (*UserDetail)(nil) - -func (d *UserDetail) BeforeAppendModel(ctx context.Context, query bun.Query) error { - oprTime := time.Now() - switch query.(type) { - case *bun.InsertQuery: - d.CreatedAt = oprTime - d.LastModifiedAt = &oprTime - case *bun.UpdateQuery: - d.LastModifiedAt = &oprTime - } - return nil -} diff --git a/model/will_diluted_fee.go b/model/will_diluted_fee.go deleted file mode 100644 index fe8d641..0000000 --- a/model/will_diluted_fee.go +++ /dev/null @@ -1,35 +0,0 @@ -package model - -import ( - "context" - "time" - - "github.com/shopspring/decimal" - "github.com/uptrace/bun" -) - -type WillDilutedFee struct { - bun.BaseModel `bun:"table:will_diluted_fee,alias:w"` - CreatedAndModified `bun:"extend"` - Id string `bun:",pk,notnull" json:"diluteId"` - ReportId string `bun:",notnull" json:"reportId"` - SourceId *string `json:"sourceId"` - Name string `bun:",notnull" json:"name"` - Fee decimal.Decimal `bun:"type:numeric,notnull" json:"fee"` - Memo *string `bun:"type:text" json:"memo"` - Origin *MaintenanceFee `bun:"rel:belongs-to,join:source_id=id"` -} - -var _ bun.BeforeAppendModelHook = (*WillDilutedFee)(nil) - -func (d *WillDilutedFee) BeforeAppendModel(ctx context.Context, query bun.Query) error { - oprTime := time.Now() - switch query.(type) { - case *bun.InsertQuery: - d.CreatedAt = oprTime - d.LastModifiedAt = &oprTime - case *bun.UpdateQuery: - d.LastModifiedAt = &oprTime - } - return nil -} diff --git a/model/withdraw.go b/model/withdraw.go new file mode 100644 index 0000000..b1da5f1 --- /dev/null +++ b/model/withdraw.go @@ -0,0 +1,84 @@ +package model + +import ( + "electricity_bill_calc/types" + "github.com/shopspring/decimal" + "time" +) + +type Withdraw struct { + Park SimplifiedPark `json:"park"` + Report SimplifiedReport `json:"report"` + User UserInfos `json:"user"` // 简易用户详细信息 +} + +// 简易园区信息 +type SimplifiedPark struct { + Address *string `json:"address"` // 园区地址 + Area *string `json:"area"` // 园区面积 + Capacity *string `json:"capacity"` // 供电容量 + Category int16 `json:"category"` // 用电分类,0:两部制,1:单一峰谷,2:单一单一 + Contact *string `json:"contact"` // 园区联系人 + ID string `json:"id"` // 园区ID + Meter04KvType int16 `json:"meter04kvType"` // 户表计量类型,0:非峰谷,1:峰谷 + Name string `json:"name"` // 园区名称 + Phone *string `json:"phone"` // 园区联系人电话 + Region *string `json:"region"` // 园区所在行政区划 + Tenement *string `json:"tenement"` // 园区住户数量 + UserID string `json:"userId"` // 园区所属用户ID +} + +// 简易核算报表信息 +type SimplifiedReport struct { + ID string `json:"id"` // 报表ID + LastWithdrawAppliedAt *string `json:"lastWithdrawAppliedAt"` // 最后一次申请撤回的时间,格式为 yyyy-MM-dd HH:mm:ss + LastWithdrawAuditAt *string `json:"lastWithdrawAuditAt"` // 最后一次申请审核的时间,格式为 yyyy-MM-dd HH:mm:ss + Message *string `json:"message"` // 当前状态的错误提示 + ParkID string `json:"parkId"` // 所属园区ID + PeriodBegin string `json:"periodBegin"` // 核算起始日期,格式为 yyyy-MM-dd + PeriodEnd string `json:"periodEnd"` // 核算结束日期,格式为 yyyy-MM-dd + Published bool `json:"published"` // 是否已发布 + PublishedAt *string `json:"publishedAt"` // 发布时间 + Status float64 `json:"status,omitempty"` // 当前状态,0:计算任务已队列,1:计算任务已完成,2:计算数据不足 + Withdraw int16 `json:"withdraw"` // 报表撤回状态,0:未撤回,1:申请撤回中,2:申请拒绝,3:申请批准 +} + +// 简易用户信息 +type UserInfos struct { + Address *string `json:"address"` // 用户地址 + Contact *string `json:"contact"` // 用户联系人 + ID string `json:"id"` // 用户ID + Name *string `json:"name"` // 用户名称 + Phone *string `json:"phone"` // 用户联系人电话 + Region *string `json:"region"` // 用户所在行政区划 +} + +//用于映射数据库的报表结构体 +type ReportRes struct { + ReportId string `db:"report_id"` + LastWithdrawAppliedAt *time.Time `db:"last_withdraw_applied_at"` + LastWithdrawAuditAt *time.Time `db:"last_withdraw_audit_at"` + ParkID string `db:"report_park_id"` + Period types.DateRange `db:"period"` + Published bool `db:"published"` + PublishedAt *time.Time `db: "published_at"` + Withdraw int16 `db:"withdraw"` + ParkAddress *string `db:"park_address"` + Area decimal.NullDecimal `db:"area"` + Capacity decimal.NullDecimal `db:"capacity"` + Category int16 + ParkContact *string `db:"park_contact"` + ParkId string `db:"park_id"` + Meter04KVType int16 `db:"meter_04kv_type"` + ParkName string `db:"park_name"` + ParkPhone *string `db:"park_phone"` + ParkRegion string `db:"park_region"` + TenementQuantity decimal.NullDecimal `db:"tenement_quantity"` + UserID string `db:"user_id"` + Address *string + Contact string `db:"user_detail_contact"` + ID string `db:"ud_id"` + Name *string `db:"user_detail_name"` + Phone string `db:"user_detail_phone"` + Region *string `db:"user_detail_region"` +} diff --git a/repository/calculate.go b/repository/calculate.go index 440d9ef..eed635b 100644 --- a/repository/calculate.go +++ b/repository/calculate.go @@ -4,7 +4,17 @@ import ( "electricity_bill_calc/global" "electricity_bill_calc/logger" "electricity_bill_calc/model" + "electricity_bill_calc/model/calculate" "electricity_bill_calc/types" + "encoding/json" + "errors" + "fmt" + "github.com/jackc/pgx/v5" + "github.com/shopspring/decimal" + "golang.org/x/sync/errgroup" + "log" + "strings" + "time" "github.com/doug-martin/goqu/v9" _ "github.com/doug-martin/goqu/v9/dialect/postgres" @@ -68,3 +78,434 @@ 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 +} +func (cr _CalculateRepository) ClearReportContent(tx pgx.Tx, rid string) error { + ctx, cancel := global.TimeoutContext() + defer cancel() + querysql, querarg, _ := cr.ds.Delete("report_summary"). + Where(goqu.C("report_id").Eq(rid)).ToSQL() + _, err := tx.Exec(ctx, querysql, querarg...) + if err != nil { + return err + } + querysql, querarg, _ = cr.ds.Delete("report_public_consumption"). + Where(goqu.C("report_id").Eq(rid)).ToSQL() + _, err = tx.Exec(ctx, querysql, querarg...) + if err != nil { + return err + } + + querysql, querarg, _ = cr.ds.Delete("report_pooled_consumption"). + Where(goqu.C("report_id").Eq(rid)).ToSQL() + _, err = tx.Exec(ctx, querysql, querarg...) + if err != nil { + return err + } + + querysql, querarg, _ = cr.ds.Delete("report_tenement"). + Where(goqu.C("report_id").Eq(rid)).ToSQL() + _, err = tx.Exec(ctx, querysql, querarg...) + if err != nil { + return err + } + + return nil +} +func (cr _CalculateRepository) SaveReportPublics(tx pgx.Tx, rid string, meters []calculate.Meter) error { + ctx, cancel := global.TimeoutContext() + defer cancel() + if len(meters) == 0 { + // 如果没有公共表计则直接返回 + return nil + } + // 准备插入表达式 + insertExpr := cr.ds.Insert("report_public_consumption"). + Cols( + "report_id", "park_meter_id", "overall", "critical", "peak", "flat", "valley", + "loss_adjust", "consumption_total", "loss_adjust_total", "final_total", + ).Prepared(true) + // 添加值到插入表达式中 + for _, meter := range meters { + insertExpr = insertExpr.Vals([]interface{}{ + rid, + meter.Code, + meter.Overall.Fee, + meter.Critical.Fee, + meter.Peak.Fee, + meter.Flat.Fee, + meter.Valley.Fee, + meter.AdjustLoss.Fee, + meter.Overall.Fee, + meter.AdjustLoss.Fee, + meter.Overall.Fee.Add(meter.AdjustLoss.Fee), + }) + } + + // 执行插入语句 + inserSql, insertArgs, err := insertExpr.Prepared(true).ToSQL() + if err != nil { + return err + } + if _, err := tx.Exec(ctx, inserSql, insertArgs); err != nil { + return fmt.Errorf("保存报表核算概要失败: %w", err) + } + + return nil +} +func (cr _CalculateRepository) SaveReportSummary(tx pgx.Tx, summary calculate.Summary) error { + ctx, cancel := global.TimeoutContext() + defer cancel() + // 构建插入表达式 + insertsql, insertArgs, _ := cr.ds.Insert("report_summary"). + Cols( + "report_id", "overall", "critical", "peak", "flat", "valley", + "loss", "loss_fee", "basic_fee", "basic_pooled_price_consumption", "basic_pooled_price_area", + "adjust_fee", "adjust_pooled_price_consumption", "adjust_pooled_price_area", + "loss_diluted_price", "loss_proportion", "final_diluted_overall", + "consumption_fee", "authorize_loss", "overall_area", "total_consumption", + ). + Vals(goqu.Vals{ + summary.ReportId, summary.Overall, summary.Critical, summary.Peak, summary.Flat, + summary.Valley, summary.Loss, summary.LossFee, summary.BasicFee, + summary.BasicPooledPriceConsumption, summary.BasicPooledPriceArea, + summary.AdjustFee, summary.AdjustPooledPriceConsumption, summary.AdjustPooledPriceArea, + summary.LossDilutedPrice, summary.LossProportion, summary.FinalDilutedOverall, + summary.ConsumptionFee, summary.AuthoizeLoss, summary.OverallArea, summary.TotalConsumption, + }).Prepared(true).ToSQL() + + // 执行插入语句 + + if _, err := tx.Exec(ctx, insertsql, insertArgs...); err != nil { + cr.log.Error("保存报表核算概要失败。") + return err + } + + return nil +} + +type NestedMeter struct { + Overall model.ConsumptionUnit + Critical model.ConsumptionUnit + Peak model.ConsumptionUnit + Flat model.ConsumptionUnit + Valley model.ConsumptionUnit + CoveredArea decimal.Decimal + + // Add other fields here as needed +} + +func (cr _CalculateRepository) SaveReportPoolings(tx pgx.Tx, + rid string, + meters []calculate.Meter, + relations []model.MeterRelation, + tenements []calculate.Meter) error { + ctx, cancel := global.TimeoutContext() + defer cancel() + if len(meters) == 0 { + return nil + } + relationsSlaves := make(map[string]bool) + for _, r := range relations { + relationsSlaves[r.SlaveMeter] = true + } + + tenementCodes := make(map[string]bool) + for _, t := range tenements { + tenementCodes[t.Code] = true + } + + for _, r := range relations { + if _, ok := tenementCodes[r.SlaveMeter]; !ok { + return errors.New("unknown tenement meter in active meter relations") + } + } + + var insertQueries []goqu.InsertDataset + for _, meter := range meters { + submeters := make([]NestedMeter, 0) + for _, r := range relations { + if r.MasterMeter == meter.Code { + for _, t := range tenements { + if t.Code == r.SlaveMeter { + submeters = append(submeters, NestedMeter{ + Overall: t.Overall, + Critical: t.Critical, + Peak: t.Peak, + Flat: t.Flat, + Valley: t.Valley, + }) + } + } + } + } + + submetersJSON, err := json.Marshal(submeters) + if err != nil { + return err + } + + insertQuery := goqu.Insert("report_pooled_consumption"). + Cols("report_id", "pooled_meter_id", "overall", "critical", "peak", "flat", "valley", "pooled_area", "diluted"). + Vals(goqu.Vals{rid, meter.Code, meter.Overall, meter.Critical, meter.Peak, meter.Flat, meter.Valley, meter.CoveredArea, submetersJSON}) + + insertQueries = append(insertQueries, *insertQuery) + } + + eg, _ := errgroup.WithContext(ctx) + for _, insertQuery := range insertQueries { + insertQuery := insertQuery // Capture loop variable + eg.Go(func() error { + sql, args, err := insertQuery.ToSQL() + if err != nil { + return err + } + _, err = tx.Exec(ctx, sql, args...) + return err + }) + } + return eg.Wait() +} +func (cr _CalculateRepository) SaveReportTenement(tx pgx.Tx, report model.ReportIndex, tenements []model.Tenement, tenementCharges []calculate.TenementCharge) error { + if len(tenements) == 0 { + // 如果没有商户则直接返回 + return nil + } + cr.log.Info("保存商户报表。") + ctx, cancel := global.TimeoutContext() + defer cancel() + insertQuery := cr.ds.Insert("report_tenement").Prepared(true) + values := []goqu.Record{} + for _, tenement := range tenements { + charge := findTenementCharge(tenementCharges, tenement.Id) + values = append(values, goqu.Record{ + "report_id": report.Id, + "tenement_id": tenement.Id, + "tenement_detail": toJSONString(tenement), + "calc_period": report.Period, + "overall": toJSONString(charge.Overall), + "critical": toJSONString(charge.Critical), + "peak": toJSONString(charge.Peak), + "flat": toJSONString(charge.Flat), + "valley": toJSONString(charge.Valley), + "loss": toJSONString(charge.Loss), + "basic_fee_pooled": charge.BasicFee, + "adjust_fee_pooled": charge.AdjustFee, + "loss_fee_pooled": charge.LossPooled, + "final_pooled": charge.PublicPooled, + "final_charge": charge.FinalCharges, + "meters": toJSONString(convertToNestedMeters(charge.Submeters)), + "pooled": toJSONString(convertToNestedMeters(charge.Poolings)), + }) + } + + sql, params, err := insertQuery.Rows(values).Prepared(true).ToSQL() + if err != nil { + log.Println("sql出现问题................................") + return err + } + tx.Exec(ctx, sql, params...) + if err != nil { + return err + } + return nil +} + +// findTenementCharge 在 TenementCharges 切片中查找指定商户的核算内容 +func findTenementCharge(charges []calculate.TenementCharge, tenementID string) calculate.TenementCharge { + for _, charge := range charges { + if charge.Tenement == tenementID { + return charge + } + } + return calculate.TenementCharge{} +} + +// convertToNestedMeters 将 Meter 切片转换为 NestedMeter 切片 +func convertToNestedMeters(meters []*calculate.Meter) []NestedMeter { + nestedMeters := []NestedMeter{} + for _, meter := range meters { + nestedMeters = append(nestedMeters, NestedMeter{ + Overall: meter.Overall, + Critical: meter.Critical, + Peak: meter.Peak, + Flat: meter.Flat, + Valley: meter.Valley, + CoveredArea: meter.CoveredArea, + }) + } + return nestedMeters +} + +// toJSONString 将对象转换为 JSON 字符串 +func toJSONString(obj interface{}) string { + return `"` + strings.ReplaceAll(fmt.Sprintf("%#v", obj), `"`, `\"`) + `"` +} diff --git a/repository/god_mode.go b/repository/god_mode.go new file mode 100644 index 0000000..fed5fc8 --- /dev/null +++ b/repository/god_mode.go @@ -0,0 +1,449 @@ +package repository + +import ( + "context" + "electricity_bill_calc/global" + "electricity_bill_calc/logger" + "fmt" + "github.com/doug-martin/goqu/v9" + "github.com/georgysavva/scany/v2/pgxscan" + "github.com/jackc/pgx/v5" + "go.uber.org/zap" +) + +type _GMRepository struct { + log *zap.Logger + ds goqu.DialectWrapper +} + +var GMRepository = &_GMRepository{ + log: logger.Named("Repository", "GM"), + ds: goqu.Dialect("postgres"), +} + +func (gm _GMRepository) DeleteMeterBinding(ctx context.Context, tx pgx.Tx, pid string, tenements []string, meterCodes ...[]string) error { + DeleteQuery := gm.ds.From(goqu.T("tenement_meter")). + Where(goqu.I("park_id").Eq(pid)). + Delete() + + if len(tenements) > 0 { + DeleteQuery = DeleteQuery. + Where(goqu.I("tenement_id").In(tenements)) + } + + if len(meterCodes) > 0 { + DeleteQuery = DeleteQuery. + Where(goqu.I("meter_id").In(meterCodes)) + } + + DeleteQuerySql, DeleteQueryArgs, _ := DeleteQuery.ToSQL() + + _, err := tx.Exec(ctx, DeleteQuerySql, DeleteQueryArgs...) + if err != nil { + gm.log.Error("数据库在删除tenement_meter表数据中出错", zap.Error(err)) + tx.Rollback(ctx) + return err + } + return nil +} + +func (gm _GMRepository) DeleteTenements(ctx context.Context, tx pgx.Tx, pid string, tenements ...[]string) error { + DeleteTenements := gm.ds. + From("tenement"). + Where(goqu.I("park_id").Eq(pid)). + Delete() + + fmt.Println(len(tenements)) + if len(tenements) > 0 { + DeleteTenements = DeleteTenements. + Where(goqu.I("id").In(tenements)) + } + + DeleteTenementsSql, DeleteTenementsArgs, _ := DeleteTenements.ToSQL() + + _, err := tx.Exec(ctx, DeleteTenementsSql, DeleteTenementsArgs...) + if err != nil { + tx.Rollback(ctx) + gm.log.Error("删除商户信息出错", zap.Error(err)) + return err + } + return nil +} + +func (gm _GMRepository) DeleteInvoices(ctx context.Context, tx pgx.Tx, parks string, val ...[]string) error { + if len(val) > 0 { + updateQuery, updateQueryArgs, _ := gm.ds. + Update(goqu.T("report_tenement")). + Set(goqu.Record{"invoice": nil}). + Where(goqu.I("invoice").In(val)). + Where( + goqu.I("report_id"). + Eq( + gm.ds. + From(goqu.T("report")). + Where(goqu.I("park_id").Eq(parks)), + ), + ).ToSQL() + _, err := tx.Exec(ctx, updateQuery, updateQueryArgs...) + if err != nil { + tx.Rollback(ctx) + gm.log.Error("更新发票记录出错", zap.Error(err)) + return err + } + } else { + updateQuery, updateQueryArgs, _ := gm.ds. + Update(goqu.T("report_tenement")). + Set(goqu.Record{"invoice": nil}). + Where( + goqu.I("report_id"). + Eq(gm.ds. + From(goqu.T("report")). + Where(goqu.I("park_id").Eq(parks)), + )).ToSQL() + _, err := tx.Exec(ctx, updateQuery, updateQueryArgs...) + if err != nil { + tx.Rollback(ctx) + gm.log.Error("更新发票记录出错", zap.Error(err)) + return err + } + } + + deleteQuery := gm.ds. + From(goqu.T("invoices")). + Where(goqu.I("park_id").Eq(parks)). + Delete() + if len(val) > 0 { + deleteQuery.Where(goqu.I("invoice_code").In(val)) + } + deleteQuerySql, deleteQueryArgs, _ := deleteQuery.ToSQL() + + _, err := tx.Exec(ctx, deleteQuerySql, deleteQueryArgs...) + if err != nil { + tx.Rollback(ctx) + gm.log.Error("删除指定园区发票记录出错", zap.Error(err)) + return err + } + return nil + +} + +func (gm _GMRepository) DeleteMeterPoolings(ctx context.Context, tx pgx.Tx, parks string, val ...[]string) error { + deleteQuery := gm.ds. + Delete(goqu.T("meter_relations")). + Where(goqu.I("park_id").Eq(parks)) + + if len(val) > 0 { + deleteQuery = deleteQuery. + Where( + goqu.I("master_meter_id").In(val), + goqu.Or(goqu.I("slave_meter_id").In(val)), + ) + } + deleteQuerySql, deleteQueryArgs, _ := deleteQuery.ToSQL() + _, err := tx.Exec(ctx, deleteQuerySql, deleteQueryArgs...) + if err != nil { + tx.Rollback(ctx) + gm.log.Error("删除指定园区中的表计分摊关系失败", zap.Error(err)) + return err + } + return nil +} + +func (gm _GMRepository) DeleteMeters(ctx context.Context, tx pgx.Tx, parks string, val ...[]string) error { + deleteQuery := gm.ds. + Delete(goqu.T("meter_04kv")). + Where(goqu.I("park_id").Eq(parks)) + + if len(val) > 0 { + deleteQuery = deleteQuery.Where(goqu.I("code").In(val)) + } + + deleteQuerySql, deleteQueryArgs, _ := deleteQuery.ToSQL() + + _, err := tx.Exec(ctx, deleteQuerySql, deleteQueryArgs...) + if err != nil { + tx.Rollback(ctx) + gm.log.Error("删除指定园区的符合条件的标记出错", zap.Error(err)) + return err + } + return nil +} + +func (gm _GMRepository) DeleteReports(ctx context.Context, tx pgx.Tx, parks string, val ...[]string) error { + var err error + + if len(val) > 0 { + deleteReportTenementQuerySql, deleteReportTenementQueryArgs, _ := gm.ds. + Delete(goqu.T("report_tenement")). + Where(goqu.I("report_id").In( + gm.ds. + From(goqu.T("report")). + Where(goqu.I("park_id").Eq(parks)). + Where(goqu.I("id").In(val)), + )).ToSQL() + _, err = tx.Exec(ctx, deleteReportTenementQuerySql, deleteReportTenementQueryArgs...) + if err != nil { + tx.Rollback(ctx) + return err + } + + deleteReportPooledConsumptionQuerySql, deleteReportPooledConsumptionQueryArgs, _ := gm.ds. + Delete(goqu.T("report_pooled_consumption")). + Where(goqu.I("report_id").In( + gm.ds. + From(goqu.T("report")). + Where(goqu.I("park_id").Eq(parks)). + Where(goqu.I("id").In(val)), + )).ToSQL() + _, err = tx.Exec(ctx, deleteReportPooledConsumptionQuerySql, deleteReportPooledConsumptionQueryArgs...) + if err != nil { + tx.Rollback(ctx) + return err + } + + deleteReportPublicConsumptionQuerySql, deleteReportPublicConsumptionQueryArgs, _ := gm.ds. + Delete(goqu.T("report_public_consumption")). + Where(goqu.I("report_id").In( + gm.ds. + From(goqu.T("report")). + Where(goqu.I("park_id").Eq(parks)). + Where(goqu.I("id").In(val)), + )).ToSQL() + _, err = tx.Exec(ctx, deleteReportPublicConsumptionQuerySql, deleteReportPublicConsumptionQueryArgs...) + if err != nil { + tx.Rollback(ctx) + return err + } + + deleteReportSummaryQuerySql, deleteReportSummaryQueryArgs, _ := gm.ds. + Delete(goqu.T("report_summary")). + Where(goqu.I("report_id").In( + gm.ds. + From(goqu.T("report")). + Where(goqu.I("park_id").Eq(parks)). + Where(goqu.I("id").In(val)), + )).ToSQL() + _, err = tx.Exec(ctx, deleteReportSummaryQuerySql, deleteReportSummaryQueryArgs...) + if err != nil { + tx.Rollback(ctx) + return err + } + + deleteReportTaskQuerySql, deleteReportTaskQueryArgs, _ := gm.ds. + Delete(goqu.T("report_task")). + Where(goqu.I("report_id").In( + gm.ds. + From(goqu.T("report")). + Where(goqu.I("park_id").Eq(parks)). + Where(goqu.I("id").In(val)), + )).ToSQL() + _, err = tx.Exec(ctx, deleteReportTaskQuerySql, deleteReportTaskQueryArgs...) + if err != nil { + tx.Rollback(ctx) + return err + } + + deleteReportQuerySql, deleteReportQueryArgs, _ := gm.ds. + Delete(goqu.T("report")). + Where(goqu.I("park_id").Eq(parks)). + Where(goqu.I("id").In(val)).ToSQL() + _, err = tx.Exec(ctx, deleteReportQuerySql, deleteReportQueryArgs...) + if err != nil { + tx.Rollback(ctx) + return err + } + + } else { + deleteReportTenementQuerySql, deleteReportTenementQueryArgs, _ := gm.ds. + Delete(goqu.T("report_tenement")). + Where(goqu.I("report_id").In( + gm.ds. + From(goqu.T("report")). + Where(goqu.I("park_id").Eq(parks)), + )).ToSQL() + _, err = tx.Exec(ctx, deleteReportTenementQuerySql, deleteReportTenementQueryArgs...) + if err != nil { + tx.Rollback(ctx) + return err + } + + deleteReportPooledConsumptionQuerySql, deleteReportPooledConsumptionQueryArgs, _ := gm.ds. + Delete(goqu.T("report_pooled_consumption")). + Where(goqu.I("report_id").In( + gm.ds. + From(goqu.T("report")). + Where(goqu.I("park_id").Eq(parks)), + )).ToSQL() + _, err = tx.Exec(ctx, deleteReportPooledConsumptionQuerySql, deleteReportPooledConsumptionQueryArgs...) + if err != nil { + tx.Rollback(ctx) + return err + } + + deleteReportPublicConsumptionQuerySql, deleteReportPublicConsumptionQueryArgs, _ := gm.ds. + Delete(goqu.T("report_public_consumption")). + Where(goqu.I("report_id").In( + gm.ds. + From(goqu.T("report")). + Where(goqu.I("park_id").Eq(parks)), + )).ToSQL() + _, err = tx.Exec(ctx, deleteReportPublicConsumptionQuerySql, deleteReportPublicConsumptionQueryArgs...) + if err != nil { + tx.Rollback(ctx) + return err + } + + deleteReportSummaryQuerySql, deleteReportSummaryQueryArgs, _ := gm.ds. + Delete(goqu.T("report_summary")). + Where(goqu.I("report_id").In( + gm.ds. + From(goqu.T("report")). + Where(goqu.I("park_id").Eq(parks)), + )).ToSQL() + _, err = tx.Exec(ctx, deleteReportSummaryQuerySql, deleteReportSummaryQueryArgs...) + if err != nil { + tx.Rollback(ctx) + return err + } + + deleteReportTaskQuerySql, deleteReportTaskQueryArgs, _ := gm.ds. + Delete(goqu.T("report_task")). + Where(goqu.I("report_id").In( + gm.ds. + From(goqu.T("report")). + Where(goqu.I("park_id").Eq(parks)), + )).ToSQL() + _, err = tx.Exec(ctx, deleteReportTaskQuerySql, deleteReportTaskQueryArgs...) + if err != nil { + tx.Rollback(ctx) + return err + } + + deleteReportQuerySql, deleteReportQueryArgs, _ := gm.ds. + Delete(goqu.T("report")). + Where(goqu.I("park_id").Eq(parks)).ToSQL() + _, err = tx.Exec(ctx, deleteReportQuerySql, deleteReportQueryArgs...) + if err != nil { + tx.Rollback(ctx) + return err + } + } + + return nil +} + +func (gm _GMRepository) DeleteBuildings(ctx context.Context, tx pgx.Tx, parks string, val ...[]string) error { + if len(val) > 0 { + updateBulidingSql, updateBlidingArgs, _ := gm.ds. + Update(goqu.T("tenement")). + Set(goqu.Record{"building": nil}). + Where(goqu.I("park_id").Eq(parks)). + Where(goqu.I("building").In( + gm.ds. + From(goqu.I("park_building")). + Where(goqu.I("park_id").Eq(parks)). + Where(goqu.I("id").In(val)). + Select(goqu.I("id")), + )).ToSQL() + _, err := tx.Exec(ctx, updateBulidingSql, updateBlidingArgs...) + if err != nil { + tx.Rollback(ctx) + return err + } + } else { + updateBulidingSql, updateBlidingArgs, _ := gm.ds. + Update(goqu.T("tenement")). + Set(goqu.Record{"building": nil}). + Where(goqu.I("park_id").Eq(parks)).ToSQL() + _, err := tx.Exec(ctx, updateBulidingSql, updateBlidingArgs...) + if err != nil { + tx.Rollback(ctx) + return err + } + } + + deleteQuery := gm.ds. + Delete(goqu.I("park_building")). + Where(goqu.I("park_id").Eq(parks)) + + if len(val) > 0 { + deleteQuery = deleteQuery. + Where(goqu.I("id").In(val)) + } + + deleteQuerySql, deleteQueryArgs, _ := deleteQuery.ToSQL() + _, err := tx.Exec(ctx, deleteQuerySql, deleteQueryArgs...) + if err != nil { + tx.Rollback(ctx) + return err + } + return nil +} + +func (gm _GMRepository) DeleteParks(ctx context.Context, tx pgx.Tx, park []string) error { + deleteParksSql, deleteParksArgs, _ := gm.ds. + Delete(goqu.T("park")). + Where(goqu.I("id").In(park)).ToSQL() + + _, err := tx.Exec(ctx, deleteParksSql, deleteParksArgs...) + if err != nil { + tx.Rollback(ctx) + return err + } + return nil +} + +func (gm _GMRepository) ListAllParkIdsInUser(ctx context.Context, tx pgx.Tx, uid string) ([]string, error) { + SearchParkIdsSql, SearchParkIdsArgs, _ := gm.ds. + From(goqu.T("park")). + Where(goqu.I("user_id").Eq(uid)). + Select(goqu.I("id")).ToSQL() + var pids []string + err := pgxscan.Select(ctx, global.DB, &pids, SearchParkIdsSql, SearchParkIdsArgs...) + if err != nil { + gm.log.Error("查询["+uid+"]用户下的所有园区失败", zap.Error(err)) + tx.Rollback(ctx) + return nil, err + } + + return pids, nil +} + +func (gm _GMRepository) DeleteUsers(ctx context.Context, tx pgx.Tx, uid string) error { + var err error + //删除用户关联 + DeleteUserChargeSql, DeleteUserChargeArgs, _ := gm.ds. + Delete(goqu.T("user_charge")). + Where(goqu.I("id").Eq(uid)).ToSQL() + + _, err = tx.Exec(ctx,DeleteUserChargeSql,DeleteUserChargeArgs...) + if err != nil { + gm.log.Error("user_charge表关联出错",zap.Error(err)) + tx.Rollback(ctx) + return err + } + + //删除用户详细信息 + DeleteUserDetailSql, DeleteUserDetailArgs,_ := gm.ds. + Delete(goqu.T("user_detail")). + Where(goqu.I("id").Eq(uid)).ToSQL() + _, err = tx.Exec(ctx,DeleteUserDetailSql,DeleteUserDetailArgs...) + if err != nil { + gm.log.Error("user_detail表详细信息出错",zap.Error(err)) + tx.Rollback(ctx) + return err + } + + //删除用户基础信息 + DeleteUserSql, DeleteUserArgs,_ := gm.ds. + Delete(goqu.T("users")). + Where(goqu.I("id").Eq(uid)).ToSQL() + _, err = tx.Exec(ctx,DeleteUserSql,DeleteUserArgs...) + if err != nil { + gm.log.Error("user表基础信息出错",zap.Error(err)) + tx.Rollback(ctx) + return err + } + + return nil +} diff --git a/repository/report.go b/repository/report.go index a881c97..b8a4478 100644 --- a/repository/report.go +++ b/repository/report.go @@ -678,7 +678,7 @@ func (rr _ReportRepository) IsLastReport(rid string) (bool, error) { defer cancel() checkSql, checkArgs, _ := rr.ds. - From(goqu.T("report")). + From(goqu.T("report").As("r")). Select(goqu.COUNT("*")). Where( goqu.I("r.id").Eq(rid), diff --git a/repository/withdraw.go b/repository/withdraw.go new file mode 100644 index 0000000..c30bd19 --- /dev/null +++ b/repository/withdraw.go @@ -0,0 +1,242 @@ +package repository + +import ( + "electricity_bill_calc/config" + "electricity_bill_calc/global" + "electricity_bill_calc/logger" + "electricity_bill_calc/model" + "electricity_bill_calc/tools" + "electricity_bill_calc/types" + "fmt" + "github.com/doug-martin/goqu/v9" + "github.com/georgysavva/scany/v2/pgxscan" + "go.uber.org/zap" +) + +type _WithdrawRepository struct { + log *zap.Logger + ds goqu.DialectWrapper +} + +var WithdrawRepository = &_WithdrawRepository{ + log: logger.Named("Repository", "Withdraw"), + ds: goqu.Dialect("postgres"), +} + +//该方法用于分页查询核算报表 +func (wd _WithdrawRepository) FindWithdraw(page uint, keyword *string) ([]model.Withdraw, int64, error) { + wd.log.Info("查询核算报表", zap.Stringp("keyword", keyword), zap.Int("page", int(page))) + ctx, cancel := global.TimeoutContext() + defer cancel() + + /** + 如果访问数据库次数过多出现时间过长的话可以用这个尝试优化,未测试的sql语句 + + wd.ds.From(goqu.T("report")). + Where(goqu.I("withdraw").Eq(1)). + Select( + goqu.I("report.*"), + goqu.I("park.*"), + goqu.I("user_detail.*"), + ). + Join( + goqu.T("park"), goqu.On(goqu.I("report.park_id").Eq(goqu.I("park.id"))), + ). + Join( + goqu.T("user_detail"), goqu.On(goqu.I("park.user_id").Eq(goqu.I("user_detail.id"))), + ).ToSQL() + + SELECT report.*, park.*, user_detail.* + FROM report as r + JOIN park as p ON r.park_id = p.id + JOIN user_detail as ud ON p.user_id = ud.id + WHERE withdraw = 1 + AND p.name Like '%keyword%' + AND ud.name Like '%keyword%' + */ + reportQuery := wd.ds. + From(goqu.T("report").As("r")). + Where(goqu.I("withdraw").Eq(1)). + Join(goqu.T("park").As("p"), goqu.On(goqu.I("r.park_id").Eq(goqu.I("p.id")))). + Join(goqu.T("user_detail").As("ud"), goqu.On(goqu.I("p.user_id").Eq(goqu.I("ud.id")))). + Where(goqu.I("p.deleted_at").IsNull()). + Where(goqu.I("ud.deleted_at").IsNull()). + Select( + goqu.I("r.id").As("report_id"), goqu.I("r.last_withdraw_applied_at"), goqu.I("r.last_withdraw_audit_at"), + goqu.I("r.park_id").As("report_park_id"), goqu.I("r.period"), goqu.I("r.published"), goqu.I("r.published_at"), goqu.I("r.withdraw"), + goqu.I("p.address").As("park_address"), goqu.I("p.area"), goqu.I("p.capacity"), goqu.I("p.category"), goqu.I("p.contact").As("park_contact"), + goqu.I("p.id").As("park_id"), goqu.I("p.meter_04kv_type"), goqu.I("p.name").As("park_name"), goqu.I("p.phone").As("park_phone"), goqu.I("p.region").As("park_region"), + goqu.I("p.tenement_quantity"), goqu.I("p.user_id"), goqu.I("ud.address"), goqu.I("ud.contact").As("user_detail_contact"), + goqu.I("ud.id").As("ud_id"), goqu.I("ud.name").As("user_detail_name"), goqu.I("ud.phone").As("user_detail_phone"), goqu.I("ud.region").As("user_detail_region"), + ) + + countReportQuery := wd.ds. + From(goqu.T("report").As("r")). + Where(goqu.I("withdraw").Eq(1)). + Join(goqu.T("park").As("p"), goqu.On(goqu.I("r.park_id").Eq(goqu.I("p.id")))). + Join(goqu.T("user_detail").As("ud"), goqu.On(goqu.I("p.user_id").Eq(goqu.I("ud.id")))). + Select(goqu.COUNT("*")) + + if keyword != nil && len(*keyword) > 0 { + pattern := fmt.Sprintf("%%%s%%", *keyword) + reportQuery = reportQuery.Where(goqu.Or( + goqu.I("p.name").ILike(pattern), + goqu.I("ud.name").ILike(pattern), + )) + } + + reportQuery = reportQuery.Order(goqu.I("r.created_at").Desc()) + + currentPostion := (page - 1) * config.ServiceSettings.ItemsPageSize + reportQuery = reportQuery.Offset(currentPostion).Limit(config.ServiceSettings.ItemsPageSize) + + reportSql, reportArgs, _ := reportQuery.Prepared(true).ToSQL() + + countReportQuerySql, countReportQueryArgs, _ := countReportQuery.Prepared(true).ToSQL() + + var ( + reports []*model.ReportRes = make([]*model.ReportRes, 0) + total int64 + ) + var err error + + err = pgxscan.Select(ctx, global.DB, &reports, reportSql, reportArgs...) + if err != nil { + fmt.Println(err) + wd.log.Error("查询报表记录失败。", zap.Error(err)) + return make([]model.Withdraw, 0), 0, err + } + + if err = pgxscan.Get(ctx, global.DB, &total, countReportQuerySql, countReportQueryArgs...); err != nil { + wd.log.Error("查询报表记录总数失败。", zap.Error(err)) + return make([]model.Withdraw, 0), 0, err + } + + if len(reports) <= 0 { + return make([]model.Withdraw, 0), total, nil + } + + var withdrawReses []model.Withdraw + //TODO: 2023.07.24对查询到的数据进行拼接(完成) + for _, v := range reports { + Begin := v.Period.SafeLower().Format("2006-01-02") + End := v.Period.SafeUpper().Format("2006-01-02") + + var withdrawRes model.Withdraw + report := model.SimplifiedReport{ + ID: v.ReportId, + LastWithdrawAppliedAt: tools.TimeToStringPtr(v.LastWithdrawAppliedAt), + LastWithdrawAuditAt: tools.TimeToStringPtr(v.LastWithdrawAuditAt), + Message: nil, + ParkID: v.ParkID, + PeriodBegin: Begin, + PeriodEnd: End, + Published: v.Published, + PublishedAt: tools.TimeToStringPtr(v.LastWithdrawAuditAt), + Status: 0., + Withdraw: v.Withdraw, + } + park := model.SimplifiedPark{ + Address: v.ParkAddress, + Area: tools.NullDecimalToString(v.Area), + Capacity: tools.NullDecimalToString(v.Capacity), + Category: int16(v.Category), + Contact: v.ParkContact, + ID: v.ParkId, + Meter04KvType: v.Meter04KVType, + Name: v.ParkName, + Phone: v.ParkPhone, + Region: &v.ParkRegion, + Tenement: tools.NullDecimalToString(v.TenementQuantity), + UserID: v.UserID, + } + userInfo := model.UserInfos{ + Address: v.Address, + Contact: &v.Contact, + ID: v.ID, + Name: v.Name, + Phone: &v.Phone, + Region: v.Region, + } + + withdrawRes.Report = report + withdrawRes.Park = park + withdrawRes.User = userInfo + withdrawReses = append(withdrawReses, withdrawRes) + } + + return withdrawReses, total, nil +} + +//该方法用于审核同意报表撤回 +func (wd _WithdrawRepository) ReviewTrueReportWithdraw( rid string) (bool, error) { + wd.log.Info("审核指定的报表", zap.String("rid", rid)) + ctx, cancel := global.TimeoutContext() + defer cancel() + //update report set withdraw=$2, + //last_withdraw_audit_at=$3, published=false, + //published_at=null where id=$1 + + tx, err := global.DB.Begin(ctx) + if err != nil { + wd.log.Error("开启数据库事务失败", zap.Error(err)) + } + updateQuerySql, updateArgs, _ := wd.ds. + Update(goqu.T("report")). + Set(goqu.Record{ + "withdraw": model.REPORT_WITHDRAW_GRANTED, + "last_withdraw_audit_at": types.Now(), + "published": false, + "published_at": nil, + }). + Where(goqu.I("id").Eq(rid)). + Prepared(true).ToSQL() + + rs, err := tx.Exec(ctx, updateQuerySql, updateArgs...) + if err != nil { + wd.log.Error("审核报表失败", zap.Error(err)) + return false, err + } + err = tx.Commit(ctx) + if err != nil { + wd.log.Error("提交数据库事务失败", zap.Error(err)) + return false, err + } + + return rs.RowsAffected() > 0, nil +} + +//该方法用于审核拒绝报表撤回 +func (wd _WithdrawRepository) ReviewFalseReportWithdraw( rid string) (bool, error) { + wd.log.Info("审核指定的报表", zap.String("rid", rid)) + ctx, cancel := global.TimeoutContext() + defer cancel() + + tx, err := global.DB.Begin(ctx) + if err != nil { + wd.log.Error("开启数据库事务失败", zap.Error(err)) + } + updateQuerySql, updateArgs, _ := wd.ds. + Update(goqu.T("report")). + Set(goqu.Record{ + "withdraw": model.REPORT_WITHDRAW_DENIED, + "last_withdraw_audit_at": types.Now(), + "published": false, + "published_at": nil, + }). + Where(goqu.I("id").Eq(rid)). + Prepared(true).ToSQL() + + rs, err := tx.Exec(ctx, updateQuerySql, updateArgs...) + if err != nil { + wd.log.Error("审核报表失败", zap.Error(err)) + return false, err + } + err = tx.Commit(ctx) + if err != nil { + wd.log.Error("提交数据库事务失败", zap.Error(err)) + return false, err + } + + return rs.RowsAffected() > 0, nil +} \ No newline at end of file diff --git a/response/user_response.go b/response/user_response.go index 9eaf458..a60086e 100644 --- a/response/user_response.go +++ b/response/user_response.go @@ -16,7 +16,7 @@ type LoginResponse struct { func (r Result) LoginSuccess(session *model.Session) error { res := &LoginResponse{} res.Code = http.StatusOK - res.Message = "用户已成功登录。" + res.Message = "用户已成功登录。"+ "👋!" res.NeedReset = false res.Session = session return r.Ctx.Status(fiber.StatusOK).JSON(res) diff --git a/router/router.go b/router/router.go index fe8c98f..5359d0d 100644 --- a/router/router.go +++ b/router/router.go @@ -25,24 +25,24 @@ func init() { } func App() *fiber.App { - app := fiber.New(fiber.Config{ - BodyLimit: 30 * 1024 * 1024, - EnablePrintRoutes: true, - EnableTrustedProxyCheck: false, - Prefork: false, - ErrorHandler: errorHandler, - JSONEncoder: json.Marshal, - JSONDecoder: json.Unmarshal, + app := fiber.New(fiber.Config{ //创建fiber实例的时候选择配置选项 + BodyLimit: 30 * 1024 * 1024, //设置请求正文允许的最大大小。 + EnablePrintRoutes: true, //自定义方案,用于启动消息 + EnableTrustedProxyCheck: false, //禁用受信代理 + Prefork: false, //禁止预处理(如果要启用预处理则需要通过shell脚本运行) + ErrorHandler: errorHandler, //相应全局处理错误 + JSONEncoder: json.Marshal, //json编码 + JSONDecoder: json.Unmarshal, //json解码 }) - app.Use(compress.New()) + app.Use(compress.New()) //压缩中间件 app.Use(recover.New(recover.Config{ EnableStackTrace: true, StackTraceHandler: stackTraceHandler, - })) + })) //恢复中间件 app.Use(logger.NewLogMiddleware(logger.LogMiddlewareConfig{ Logger: logger.Named("App"), - })) - app.Use(security.SessionRecovery) + })) //日志中间件 + app.Use(security.SessionRecovery) //会话恢复中间件 controller.InitializeUserHandlers(app) controller.InitializeRegionHandlers(app) @@ -54,6 +54,11 @@ func App() *fiber.App { controller.InitializeTopUpHandlers(app) controller.InitializeReportHandlers(app) + controller.InitializeWithdrawHandlers(app) // 公示撤回 + controller.InitializeFoundationHandlers(app) // 基础数据 + controller.InitializeStatisticsController(app) // 首页信息 + controller.InitializeGmController(app) // 天神模式 + return app } diff --git a/service/calculate.go b/service/calculate.go deleted file mode 100644 index 6680aa4..0000000 --- a/service/calculate.go +++ /dev/null @@ -1,262 +0,0 @@ -package service - -import ( - "database/sql" - "electricity_bill_calc/cache" - "electricity_bill_calc/exceptions" - "electricity_bill_calc/global" - "electricity_bill_calc/model" - "fmt" - - "github.com/shopspring/decimal" -) - -type _CalculateService struct{} - -var CalculateService _CalculateService - -func (_CalculateService) ComprehensivelyCalculateReport(reportId string) (err error) { - ctx, cancel := global.TimeoutContext(12) - defer cancel() - - // 资料准备 - var report = new(model.Report) - err = global.DB.NewSelect().Model(report). - Relation("Summary"). - Relation("WillDilutedFees"). - Relation("EndUsers"). - Where("r.id = ?", reportId). - Scan(ctx) - if err != nil || report == nil { - return exceptions.NewNotFoundErrorFromError("未找到指定的公示报表", err) - } - - // 综合计算 - report.Summary.CalculatePrices() - - // 计算维护费总计 - maintenanceFeeTotal := decimal.NewFromInt(0) - for _, m := range report.WillDilutedFees { - maintenanceFeeTotal = maintenanceFeeTotal.Add(m.Fee) - } - - // 计算终端用户信息与概览中的合计 - report.Summary.Customers = model.NewConsumptions() - report.Summary.Publics = model.NewConsumptions() - for _, eu := range report.EndUsers { - eu.OverallFee = decimal.NewNullDecimal( - eu.Overall.Decimal.Mul(report.Summary.OverallPrice.Decimal).RoundBank(2), - ) - eu.CriticalFee = decimal.NewNullDecimal( - eu.Critical.Decimal.Mul(report.Summary.CriticalPrice.Decimal).RoundBank(2), - ) - eu.PeakFee = decimal.NewNullDecimal( - eu.Peak.Decimal.Mul(report.Summary.PeakPrice.Decimal).RoundBank(2), - ) - eu.FlatFee = decimal.NewNullDecimal( - eu.Flat.Decimal.Mul(report.Summary.FlatPrice.Decimal).RoundBank(2), - ) - eu.ValleyFee = decimal.NewNullDecimal( - eu.Valley.Decimal.Mul(report.Summary.ValleyPrice.Decimal).RoundBank(2), - ) - if eu.IsPublicMeter { - report.Summary.Publics.Consumption.Decimal = report.Summary.Publics.Consumption.Decimal.Add(eu.Overall.Decimal) - report.Summary.Publics.Critical.Decimal = report.Summary.Publics.Critical.Decimal.Add(eu.Critical.Decimal) - report.Summary.Publics.Peak.Decimal = report.Summary.Publics.Peak.Decimal.Add(eu.Peak.Decimal) - report.Summary.Publics.Flat.Decimal = report.Summary.Publics.Flat.Decimal.Add(eu.Flat.Decimal) - report.Summary.Publics.Valley.Decimal = report.Summary.Publics.Valley.Decimal.Add(eu.Valley.Decimal) - } else { - report.Summary.Customers.Consumption.Decimal = report.Summary.Customers.Consumption.Decimal.Add(eu.Overall.Decimal) - report.Summary.Customers.Critical.Decimal = report.Summary.Customers.Critical.Decimal.Add(eu.Critical.Decimal) - report.Summary.Customers.Peak.Decimal = report.Summary.Customers.Peak.Decimal.Add(eu.Peak.Decimal) - report.Summary.Customers.Flat.Decimal = report.Summary.Customers.Flat.Decimal.Add(eu.Flat.Decimal) - report.Summary.Customers.Valley.Decimal = report.Summary.Customers.Valley.Decimal.Add(eu.Valley.Decimal) - } - } - - // 计算户表总电费和公共总电费以及相应的摊薄 - if report.SubmeterType == model.CUSTOMER_METER_NON_PV { - // 计算终端用户部分 - report.Summary.Customers.ConsumptionFee = decimal.NewNullDecimal( - report.Summary.Customers.Consumption.Decimal.Mul(report.Summary.OverallPrice.Decimal).RoundBank(2), - ) - report.Summary.Customers.CriticalFee = decimal.NewNullDecimal( - report.Summary.Customers.Critical.Decimal.Mul(report.Summary.OverallPrice.Decimal).RoundBank(2), - ) - report.Summary.Customers.PeakFee = decimal.NewNullDecimal( - report.Summary.Customers.Peak.Decimal.Mul(report.Summary.OverallPrice.Decimal).RoundBank(2), - ) - report.Summary.Customers.FlatFee = decimal.NewNullDecimal( - report.Summary.Customers.Flat.Decimal.Mul(report.Summary.OverallPrice.Decimal).RoundBank(2), - ) - report.Summary.Customers.ValleyFee = decimal.NewNullDecimal( - report.Summary.Customers.Valley.Decimal.Mul(report.Summary.OverallPrice.Decimal).RoundBank(2), - ) - // 计算公共区域部分 - report.Summary.Publics.ConsumptionFee = decimal.NewNullDecimal( - report.Summary.Publics.Consumption.Decimal.Mul(report.Summary.OverallPrice.Decimal).RoundBank(2), - ) - report.Summary.Publics.CriticalFee = decimal.NewNullDecimal( - report.Summary.Publics.Critical.Decimal.Mul(report.Summary.OverallPrice.Decimal).RoundBank(2), - ) - report.Summary.Publics.PeakFee = decimal.NewNullDecimal( - report.Summary.Publics.Peak.Decimal.Mul(report.Summary.OverallPrice.Decimal).RoundBank(2), - ) - report.Summary.Publics.FlatFee = decimal.NewNullDecimal( - report.Summary.Publics.Flat.Decimal.Mul(report.Summary.OverallPrice.Decimal).RoundBank(2), - ) - report.Summary.Publics.ValleyFee = decimal.NewNullDecimal( - report.Summary.Publics.Valley.Decimal.Mul(report.Summary.OverallPrice.Decimal).RoundBank(2), - ) - } - if report.SubmeterType == model.CUSTOMER_METER_PV { - // 计算终端用户部分 - report.Summary.Customers.ConsumptionFee = decimal.NewNullDecimal( - report.Summary.Customers.Consumption.Decimal.Mul(report.Summary.OverallPrice.Decimal).RoundBank(2), - ) - report.Summary.Customers.CriticalFee = decimal.NewNullDecimal( - report.Summary.Customers.Critical.Decimal.Mul(report.Summary.CriticalPrice.Decimal).RoundBank(2), - ) - report.Summary.Customers.PeakFee = decimal.NewNullDecimal( - report.Summary.Customers.Peak.Decimal.Mul(report.Summary.PeakPrice.Decimal).RoundBank(2), - ) - report.Summary.Customers.FlatFee = decimal.NewNullDecimal( - report.Summary.Customers.Flat.Decimal.Mul(report.Summary.FlatPrice.Decimal).RoundBank(2), - ) - report.Summary.Customers.ValleyFee = decimal.NewNullDecimal( - report.Summary.Customers.Valley.Decimal.Mul(report.Summary.ValleyPrice.Decimal).RoundBank(2), - ) - // 计算公共区域部分 - report.Summary.Publics.ConsumptionFee = decimal.NewNullDecimal( - report.Summary.Publics.Consumption.Decimal.Mul(report.Summary.OverallPrice.Decimal).RoundBank(2), - ) - report.Summary.Publics.CriticalFee = decimal.NewNullDecimal( - report.Summary.Publics.Critical.Decimal.Mul(report.Summary.CriticalPrice.Decimal).RoundBank(2), - ) - report.Summary.Publics.PeakFee = decimal.NewNullDecimal( - report.Summary.Publics.Peak.Decimal.Mul(report.Summary.PeakPrice.Decimal).RoundBank(2), - ) - report.Summary.Publics.FlatFee = decimal.NewNullDecimal( - report.Summary.Publics.Flat.Decimal.Mul(report.Summary.FlatPrice.Decimal).RoundBank(2), - ) - report.Summary.Publics.ValleyFee = decimal.NewNullDecimal( - report.Summary.Publics.Valley.Decimal.Mul(report.Summary.ValleyPrice.Decimal).RoundBank(2), - ) - } - if report.Summary.Overall.Abs().GreaterThan(decimal.Zero) { - report.Summary.Customers.Proportion = decimal.NewNullDecimal( - report.Summary.Customers.Consumption.Decimal.Div(report.Summary.Overall).RoundBank(15), - ) - report.Summary.Publics.Proportion = decimal.NewNullDecimal( - report.Summary.Publics.Consumption.Decimal.Div(report.Summary.Overall).RoundBank(15), - ) - } - - // 计算线损 - report.Summary.Loss = decimal.NewNullDecimal( - report.Summary.Overall.Sub(report.Summary.Publics.Consumption.Decimal).Sub(report.Summary.Customers.Consumption.Decimal), - ) - report.Summary.LossFee = decimal.NewNullDecimal( - report.Summary.Loss.Decimal.Mul(report.Summary.OverallPrice.Decimal).RoundBank(8), - ) - if report.Summary.Overall.Abs().GreaterThan(decimal.Zero) { - report.Summary.LossProportion = decimal.NewNullDecimal( - report.Summary.Loss.Decimal.Div(report.Summary.Overall).RoundBank(15), - ) - if report.Summary.LossProportion.Decimal.GreaterThan(decimal.NewFromFloat(0.1)) { - report.Summary.AuthorizeLoss = decimal.NewNullDecimal( - report.Summary.Overall.Mul(decimal.NewFromFloat(0.1)).RoundBank(8), - ) - report.Summary.AuthorizeLossFee = decimal.NewNullDecimal( - report.Summary.AuthorizeLoss.Decimal.Mul(report.Summary.OverallPrice.Decimal).RoundBank(8), - ) - } else { - report.Summary.AuthorizeLoss = report.Summary.Loss - report.Summary.AuthorizeLossFee = report.Summary.LossFee - } - } - if report.Summary.Customers.Consumption.Decimal.Abs().GreaterThan(decimal.Zero) { - report.Summary.LossDilutedPrice = decimal.NewNullDecimal( - report.Summary.AuthorizeLossFee.Decimal.Div(report.Summary.Customers.Consumption.Decimal).RoundBank(8), - ) - } - - // 计算基本电费和调整电费等的摊薄 - if report.Summary.Customers.Consumption.Decimal.Abs().GreaterThan(decimal.Zero) { - report.Summary.BasicDilutedPrice = decimal.NewNullDecimal( - report.Summary.BasicFee.Div(report.Summary.Customers.Consumption.Decimal).RoundBank(8), - ) - report.Summary.AdjustDilutedPrice = decimal.NewNullDecimal( - report.Summary.AdjustFee.Div(report.Summary.Customers.Consumption.Decimal).RoundBank(8), - ) - report.Summary.MaintenanceDilutedPrice = decimal.NewNullDecimal( - maintenanceFeeTotal.Div(report.Summary.Customers.Consumption.Decimal).RoundBank(8), - ) - } - - // 计算摊薄总计 - report.Summary.MaintenanceOverall = decimal.NewNullDecimal(maintenanceFeeTotal) - report.Summary.FinalDilutedOverall = decimal.NewNullDecimal( - report.Summary.BasicFee. - Add(report.Summary.AdjustFee). - Add(report.Summary.AuthorizeLossFee.Decimal), - ) - - // 计算终端用户的全部摊薄内容 - for _, eu := range report.EndUsers { - // 计算户表表计的摊薄内容 - if report.Summary.Customers.Consumption.Decimal.Abs().GreaterThan(decimal.Zero) { - eu.OverallProportion = eu.Overall.Decimal.Div(report.Summary.Customers.Consumption.Decimal).RoundBank(15) - } else { - eu.OverallProportion = decimal.Zero - } - eu.BasicFeeDiluted = decimal.NewNullDecimal( - eu.Overall.Decimal.Mul(report.Summary.BasicDilutedPrice.Decimal).RoundBank(2), - ) - eu.AdjustFeeDiluted = decimal.NewNullDecimal( - eu.Overall.Decimal.Mul(report.Summary.AdjustDilutedPrice.Decimal).RoundBank(2), - ) - eu.LossDiluted = decimal.NewNullDecimal( - report.Summary.AuthorizeLoss.Decimal.Mul(eu.OverallProportion).RoundBank(8), - ) - eu.LossFeeDiluted = decimal.NewNullDecimal( - eu.Overall.Decimal.Mul(report.Summary.LossDilutedPrice.Decimal).RoundBank(8), - ) - eu.FinalDiluted = decimal.NewNullDecimal( - eu.BasicFeeDiluted.Decimal. - Add(eu.AdjustFeeDiluted.Decimal). - Add(eu.LossFeeDiluted.Decimal). - RoundBank(2), - ) - eu.FinalCharge = decimal.NewNullDecimal( - eu.OverallFee.Decimal.Add(eu.FinalDiluted.Decimal).RoundBank(2), - ) - } - - // 向数据库保存报表概况以及终端用户摊薄结果 - tx, err := global.DB.BeginTx(ctx, &sql.TxOptions{}) - if err != nil { - return - } - - _, err = tx.NewUpdate().Model(report.Summary).WherePK().Exec(ctx) - if err != nil { - tx.Rollback() - return - } - for _, eu := range report.EndUsers { - _, err = tx.NewUpdate().Model(eu).WherePK().Exec(ctx) - if err != nil { - tx.Rollback() - return - } - } - - err = tx.Commit() - if err != nil { - tx.Rollback() - return - } - cache.AbolishRelation(fmt.Sprintf("publicity:%s", reportId)) - return -} diff --git a/service/calculate/checking.go b/service/calculate/checking.go new file mode 100644 index 0000000..e715ea4 --- /dev/null +++ b/service/calculate/checking.go @@ -0,0 +1,33 @@ +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 +} diff --git a/service/calculate/meter.go b/service/calculate/meter.go new file mode 100644 index 0000000..b30cb72 --- /dev/null +++ b/service/calculate/meter.go @@ -0,0 +1,10 @@ +package calculate + +import "electricity_bill_calc/model/calculate" + +// / 合并所有的表计 +type Key struct { + Code string + TenementID string +} +type MeterMap map[Key]calculate.Meter diff --git a/service/calculate/meters.go b/service/calculate/meters.go index 0b9bb1a..7dbaade 100644 --- a/service/calculate/meters.go +++ b/service/calculate/meters.go @@ -9,13 +9,6 @@ import ( "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 diff --git a/service/calculate/mod.go b/service/calculate/mod.go deleted file mode 100644 index f735e21..0000000 --- a/service/calculate/mod.go +++ /dev/null @@ -1,72 +0,0 @@ -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) -} diff --git a/service/calculate/park.go b/service/calculate/park.go new file mode 100644 index 0000000..1af4ee2 --- /dev/null +++ b/service/calculate/park.go @@ -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 +} diff --git a/service/calculate/persist.go b/service/calculate/persist.go index 112a07f..913ff12 100644 --- a/service/calculate/persist.go +++ b/service/calculate/persist.go @@ -60,9 +60,13 @@ func SavePoolings(tx pgx.Tx, report model.ReportIndex, meters MeterMap, relation if err != nil { return err } + tx.Commit(ctx) + return nil } -func SaveTenements(tx pgx.Tx, report model.ReportIndex, tenement []calculate.PrimaryTenementStatistics, tc []*calculate.TenementCharge) error { +func SaveTenements(tx pgx.Tx, report model.ReportIndex, tenement []calculate.PrimaryTenementStatistics, tc []calculate.TenementCharge) error { + ctx, cancel := global.TimeoutContext() + defer cancel() var ts []model.Tenement for _, r := range tenement { ts = append(ts, r.Tenement) @@ -71,5 +75,7 @@ func SaveTenements(tx pgx.Tx, report model.ReportIndex, tenement []calculate.Pri if err != nil { return err } + tx.Commit(ctx) + return nil } diff --git a/service/calculate/pooled.go b/service/calculate/pooled.go new file mode 100644 index 0000000..12ddc91 --- /dev/null +++ b/service/calculate/pooled.go @@ -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 +} diff --git a/service/calculate/shared.go b/service/calculate/shared.go new file mode 100644 index 0000000..528d1fc --- /dev/null +++ b/service/calculate/shared.go @@ -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 +} diff --git a/service/calculate/summary.go b/service/calculate/summary.go index 53385b2..651566b 100644 --- a/service/calculate/summary.go +++ b/service/calculate/summary.go @@ -1,18 +1,99 @@ package calculate import ( + "electricity_bill_calc/model" "electricity_bill_calc/model/calculate" "errors" "github.com/shopspring/decimal" + + "fmt" ) -// / 计算已经启用的商铺面积和 -// / -// / - `tenements`:所有商户的电量信息 -// / - `summary`:核算报表的摘要信息 -func CalculateEnabledArea(tenements []calculate.PrimaryTenementStatistics, summary *calculate.Summary) error { - var areaMeters []calculate.Meter +// 计算已经启用的商铺面积和 +func TotalConsumptionCalculate(tenements []calculate.PrimaryTenementStatistics, summary calculate.Summary) decimal.Decimal { + var areaMaters []calculate.Meter for _, t := range tenements { + areaMaters = append(areaMaters, t.Meters...) + } + + areaMaters = removeDuplicates(areaMaters) + + var areaTotal float64 + for _, m := range areaMaters { + areaTotal += m.Detail.Area.Decimal.InexactFloat64() + } + + areaTotal += summary.OverallArea.InexactFloat64() + + return decimal.NewFromFloat(areaTotal) + +} + +func removeDuplicates(meters []calculate.Meter) []calculate.Meter { + result := make([]calculate.Meter, 0, len(meters)) + seen := make(map[string]bool) + for _, meter := range meters { + if !seen[meter.Code] { + seen[meter.Code] = true + result = append(result, meter) + } + } + return result +} + +// 计算线损以及调整线损 +func LossCalculate(report *model.ReportIndex, Public *[]calculate.Meter, + publicTotal *decimal.Decimal, summary *calculate.Summary) error { + summary.Loss = summary.Overall.Amount.Sub(summary.TotalConsumption) + + var summaryAmount decimal.Decimal + if summary.Overall.Amount == decimal.Zero { + summaryAmount = decimal.NewFromFloat(1.0) + } else { + summaryAmount = summary.Overall.Amount + } + + summary.LossProportion = summary.Loss.Div(summaryAmount) + + var authorizedLossRate decimal.Decimal + //TODO: 2023.08.04 在此发现reportIndex结构体与数据库中的report表字段不对应缺少两个相应字段,在此添加的,如在其他地方有错误优先查找这里 + if summary.LossProportion.InexactFloat64() > report.AuthorizedLossRate { + authorizedLossRate = summary.LossProportion + } else { + return errors.New(fmt.Sprintf("经过核算园区的线损率为:{%.8f}, 核定线损率为:{%.8f}", summary.LossProportion.InexactFloat64(), authorizedLossRate.InexactFloat64())) + } + + summary.AuthoizeLoss = model.ConsumptionUnit{ + Amount: decimal.NewFromFloat(summary.Overall.Amount.InexactFloat64() * authorizedLossRate.InexactFloat64()), + Fee: decimal.NewFromFloat((summary.Overall.Amount.InexactFloat64() * authorizedLossRate.InexactFloat64()) * summary.Overall.Price.InexactFloat64()), + Price: summary.Overall.Price, + Proportion: authorizedLossRate, + } + + differentialLoss := summary.LossDilutedPrice.Sub(summary.AuthoizeLoss.Amount) + + if publicTotal.InexactFloat64() <= decimal.Zero.InexactFloat64() { + return errors.New("园区公共表计的电量总和为非正值,或者园区未设置公共表计,无法计算核定线损") + } + + for _, meter := range *Public { + amountProportion := meter.Overall.Amount.InexactFloat64() / publicTotal.InexactFloat64() + adjustAmount := differentialLoss.InexactFloat64() * decimal.NewFromFloat(-1.0).InexactFloat64() + meter.AdjustLoss = model.ConsumptionUnit{ + Amount: decimal.NewFromFloat(adjustAmount), + Fee: decimal.NewFromFloat(adjustAmount * summary.LossDilutedPrice.InexactFloat64()), + Price: summary.LossDilutedPrice, + Proportion: decimal.NewFromFloat(amountProportion), + } + } + return nil +} + +// 计算已经启用的商铺面积和 +func EnabledAreaCalculate(tenements *[]calculate.PrimaryTenementStatistics, + summary *calculate.Summary) (*decimal.Decimal, error) { + var areaMeters []calculate.Meter + for _, t := range *tenements { areaMeters = append(areaMeters, t.Meters...) } // 去重 @@ -32,6 +113,7 @@ func CalculateEnabledArea(tenements []calculate.PrimaryTenementStatistics, summa return &areaTotal, nil } +// ================================================================================= // / 计算基本电费分摊、调整电费分摊以及电费摊薄单价。 // / // / - `summary`:核算报表的摘要信息 @@ -52,4 +134,5 @@ func CalculatePrices(summary *calculate.Summary) error { summary.AdjustPooledPriceArea = summary.AdjustFee.Div(summary.OverallArea) } return nil + } diff --git a/service/calculate/tenement.go b/service/calculate/tenement.go index 436fcb2..1b84bd9 100644 --- a/service/calculate/tenement.go +++ b/service/calculate/tenement.go @@ -3,219 +3,456 @@ package calculate import ( "electricity_bill_calc/model" "electricity_bill_calc/model/calculate" + "electricity_bill_calc/repository" "errors" + "fmt" "github.com/shopspring/decimal" - "math/big" + "sort" + "strings" + "time" + "unsafe" ) -// / 计算各个商户的合计信息,并归总与商户关联的表计记录 -func CalculateTenementCharge(tenements []calculate.PrimaryTenementStatistics, summary calculate.Summary, meters MeterMap, relations []model.MeterRelation) ([]calculate.TenementCharge, error) { - tenementCharges := make([]calculate.TenementCharge, 0) +// 核算园区中的全部商户表计电量用电 +func TenementMetersCalculate(report *model.ReportIndex, + PeriodStart time.Time, PeriodEnd time.Time, + meterDetails []*model.MeterDetail, summary calculate.Summary) ([]calculate.PrimaryTenementStatistics, 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 []calculate.PrimaryTenementStatistics + 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 + var meters []model.TenementMeter + + for _, relation := range tenementMeterRelations { + if strings.EqualFold(relation.TenementId, tenement.Id) { + meters = append(meters, relation) } - 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) + pt, err := determineTenementConsumptions( + tenement, + meters, + PeriodStart, + PeriodEnd, + tenementMeterReadings, + lastPeriodReadings, + meterDetails, + summary, + ) + if err != nil { + return nil, err } - // 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, - // ... 其他字段的初始化 - }) + tenementReports = append(tenementReports, pt) } - return tenementCharges, nil + return tenementReports, nil } -func calculateTenementCharge( - tenements []*PrimaryTenementStatistics, - summary *Summary, - meters MeterMap, - _relations []MeterRelation, -) ([]TenementCharges, error) { - var tenementCharges []TenementCharges +// 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) +} + +// 计算各个商户的合计信息,并归总与商户关联的表计记录 +func TenementChargeCalculate(tenements []calculate.PrimaryTenementStatistics, + summary calculate.Summary, meters MeterMap) []calculate.TenementCharge { + result := make(map[string][]string) for _, t := range tenements { - meterCodes := make([]string, len(t.Meters)) - for i, m := range t.Meters { - meterCodes[i] = m.Code + meterCodes := make([]string, 0) + for _, m := range t.Meters { + meterCodes = append(meterCodes, 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 - }) + sort.Strings(meterCodes) + result[t.Tenement.Id] = meterCodes } + var Key Key + var tc []calculate.TenementCharge + for tCode, meterCodes := range result { + relatedMeters := make([]calculate.Meter, 0) + for _, code := range meterCodes { + Key.Code = code + "_" + tCode + meter, ok := meters[Key] + if ok { + relatedMeters = append(relatedMeters, meter) + } + } + // 计算商户的合计电费信息 + var overall model.ConsumptionUnit + var critical model.ConsumptionUnit + var peak model.ConsumptionUnit + var flat model.ConsumptionUnit + var valley model.ConsumptionUnit - return tenementCharges, nil + var basicPooled decimal.Decimal + var adjustPooled decimal.Decimal + var lossAmount decimal.Decimal + var lossPooled decimal.Decimal + var publicPooled decimal.Decimal + + for _, meter := range relatedMeters { + overall.Amount.Add(meter.Overall.Amount) + overall.Fee.Add(meter.Overall.Fee) + + critical.Amount.Add(meter.Critical.Amount) + critical.Fee.Add(meter.Critical.Fee) + + peak.Amount.Add(meter.Peak.Amount) + peak.Fee.Add(meter.Peak.Fee) + + flat.Amount.Add(meter.Flat.Amount) + flat.Fee.Add(meter.Flat.Fee) + + valley.Amount.Add(meter.Valley.Amount) + valley.Fee.Add(meter.Valley.Fee) + + basicPooled.Add(meter.PooledBasic.Fee) + adjustPooled.Add(meter.PooledAdjust.Fee) + lossAmount.Add(meter.PooledLoss.Amount) + lossPooled.Add(meter.PooledLoss.Fee) + publicPooled.Add(meter.PooledPublic.Fee) + + // 反写商户表计的统计数据 + meter.Overall.Proportion = func() decimal.Decimal { + if overall.Amount.Equal(decimal.Zero) { + return decimal.Zero + } + return meter.Overall.Amount.Div(overall.Amount) + }() + meter.Critical.Proportion = func() decimal.Decimal { + if critical.Amount.Equal(decimal.Zero) { + return decimal.Zero + } + return meter.Critical.Amount.Div(critical.Amount) + }() + meter.Peak.Proportion = func() decimal.Decimal { + if peak.Amount.Equal(decimal.Zero) { + return decimal.Zero + } + return meter.Peak.Amount.Div(peak.Amount) + }() + meter.Flat.Proportion = func() decimal.Decimal { + if flat.Amount.Equal(decimal.Zero) { + return decimal.Zero + } + return meter.Flat.Amount.Div(flat.Amount) + }() + meter.Valley.Proportion = func() decimal.Decimal { + if valley.Amount.Equal(decimal.Zero) { + return decimal.Zero + } + return meter.Valley.Amount.Div(valley.Amount) + }() + meter.PooledBasic.Proportion = func() decimal.Decimal { + if basicPooled.Equal(decimal.Zero) { + return decimal.Zero + } + return meter.PooledBasic.Fee.Div(basicPooled) + }() + meter.PooledAdjust.Proportion = func() decimal.Decimal { + if adjustPooled.Equal(decimal.Zero) { + return decimal.Zero + } + return meter.PooledAdjust.Fee.Div(adjustPooled) + }() + meter.PooledLoss.Proportion = func() decimal.Decimal { + if lossPooled.Equal(decimal.Zero) { + return decimal.Zero + } + return meter.PooledLoss.Fee.Div(lossPooled) + }() + meter.PooledPublic.Proportion = func() decimal.Decimal { + if publicPooled.Equal(decimal.Zero) { + return decimal.Zero + } + return meter.PooledPublic.Fee.Div(publicPooled) + }() + + var OverallProportion decimal.Decimal + if summary.Overall.Amount == decimal.Zero { + OverallProportion = decimal.Zero + } else { + OverallProportion = decimal.NewFromFloat(overall.Amount.InexactFloat64() / summary.Overall.Amount.InexactFloat64()) + } + + var CriticalProportion decimal.Decimal + if summary.Critical.Amount == decimal.Zero { + CriticalProportion = decimal.Zero + } else { + CriticalProportion = decimal.NewFromFloat(critical.Amount.InexactFloat64() / summary.Critical.Amount.InexactFloat64()) + } + + var PeakProportion decimal.Decimal + if summary.Peak.Amount == decimal.Zero { + PeakProportion = decimal.Zero + } else { + PeakProportion = decimal.NewFromFloat(peak.Amount.InexactFloat64() / summary.Peak.Amount.InexactFloat64()) + } + + var FlatProportion decimal.Decimal + if summary.Flat.Amount == decimal.Zero { + FlatProportion = decimal.Zero + } else { + FlatProportion = decimal.NewFromFloat(flat.Amount.InexactFloat64() / summary.Flat.Amount.InexactFloat64()) + } + + var ValleyProportion decimal.Decimal + if summary.Valley.Amount == decimal.Zero { + ValleyProportion = decimal.Zero + } else { + ValleyProportion = decimal.NewFromFloat(valley.Amount.InexactFloat64() / summary.Valley.Amount.InexactFloat64()) + } + + tenementCharge := calculate.TenementCharge{ + Tenement: tCode, + Overall: model.ConsumptionUnit{ + Price: summary.Overall.Price, + Proportion: OverallProportion, + }, + Critical: model.ConsumptionUnit{ + Price: summary.Critical.Price, + Proportion: CriticalProportion, + }, + Peak: model.ConsumptionUnit{ + Price: summary.Overall.Price, + Proportion: PeakProportion, + }, + Flat: model.ConsumptionUnit{ + Price: summary.Overall.Price, + Proportion: FlatProportion, + }, + Valley: model.ConsumptionUnit{ + Price: summary.Overall.Price, + Proportion: ValleyProportion, + }, + BasicFee: basicPooled, + AdjustFee: adjustPooled, + LossPooled: lossPooled, + PublicPooled: publicPooled, + FinalCharges: decimal.NewFromFloat( + overall.Fee.InexactFloat64() + basicPooled.InexactFloat64() + + adjustPooled.InexactFloat64() + lossPooled.InexactFloat64() + + publicPooled.InexactFloat64()), + Submeters: nil, + Poolings: nil, + } + + tc = append(tc, tenementCharge) + } + } + return tc } diff --git a/service/calculate/utils.go b/service/calculate/utils.go new file mode 100644 index 0000000..801a5c7 --- /dev/null +++ b/service/calculate/utils.go @@ -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 +} diff --git a/service/calculate/wattCost.go b/service/calculate/wattCost.go new file mode 100644 index 0000000..f9f1d57 --- /dev/null +++ b/service/calculate/wattCost.go @@ -0,0 +1,168 @@ +package calculate + +import ( + "electricity_bill_calc/global" + "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 + } + + // 计算所有表计的总电量 + parkTotal := TotalConsumptionCalculate(tenementReports, summary) + + // 计算线损以及调整线损 + err = LossCalculate(report, &parkMetersReports, &parkTotal, &summary) + if err != nil { + fmt.Println("9", err) + return + } + + // 计算所有已经启用的商铺面积总和,仅计算所有未迁出的商户的所有表计对应的商铺面积。 + _, err = EnabledAreaCalculate(&tenementReports, &summary) + if err != nil { + fmt.Println("10", err) + return + } + err = CalculatePrices(&summary) + if err != nil { + fmt.Println("11", err) + return + } + + //=========================================================================== + // 计算基本电费分摊、调整电费分摊、电费摊薄单价。 + err = CalculatePrices(&summary) + // 收集目前所有已经处理的表计,统一对其进行摊薄计算。 + meters, err := CollectMeters(tenementReports, poolingMetersReports, parkMetersReports) + if err != nil { + fmt.Println("12", err) + return + } + + // 计算商户的合计电费信息,并归总与商户相关联的表计记录 + tenementCharges := TenementChargeCalculate(tenementReports, summary, meters) + + // 根据核算报表中设置的摊薄内容,逐个表计进行计算 + err = CalculateBasicPooling(report, &summary, &meters) + if err != nil { + fmt.Println("13", err) + return + } + err = CalculateAdjustPooling(*report, summary, meters) + if err != nil { + fmt.Println("14", err) + return + } + err = CalculateLossPooling(*report, summary, meters) + if err != nil { + fmt.Println("15", err) + return + } + // 计算所有商户类型表计的全周期电量,并根据全周期电量计算共用过同一表计的商户的二次分摊比例。 + _, err = CalculateTenementConsumptions(meters) + if err != nil { + fmt.Println("16", err) + return + } + err = CalculateTenementPoolings(*report, summary, meters, meterRelations) + if err != nil { + fmt.Println("17", err) + return + } + // 计算商户的合计电费信息,并归总与商户相关联的表计记录 + tenementCharges = TenementChargeCalculate(tenementReports, summary, meters) + + // 从此处开始向数据库保存全部计算结果。 + ctx, cancel := global.TimeoutContext() + defer cancel() + tx, _ := global.DB.Begin(ctx) + err = repository.CalculateRepository.ClearReportContent(tx, report.Id) + if err != nil { + tx.Rollback(ctx) + fmt.Println("18", err) + + } + + err = SaveSummary(tx, summary) + if err != nil { + tx.Rollback(ctx) + fmt.Println("19", err) + + } + err = SavePublics(tx, *report, meters) + if err != nil { + tx.Rollback(ctx) + fmt.Println("20", err) + + } + err = SavePoolings(tx, *report, meters, meterRelations) + if err != nil { + tx.Rollback(ctx) + fmt.Println("21", err) + + } + err = SaveTenements(tx, *report, tenementReports, tenementCharges) + if err != nil { + tx.Rollback(ctx) + fmt.Println("22", err) + + } + tx.Commit(ctx) + +} diff --git a/service/end_user.go b/service/end_user.go deleted file mode 100644 index 72fa950..0000000 --- a/service/end_user.go +++ /dev/null @@ -1,479 +0,0 @@ -package service - -import ( - "context" - "database/sql" - "electricity_bill_calc/cache" - "electricity_bill_calc/config" - "electricity_bill_calc/excel" - "electricity_bill_calc/exceptions" - "electricity_bill_calc/global" - "electricity_bill_calc/logger" - "electricity_bill_calc/model" - "fmt" - "io" - "strconv" - "time" - - mapset "github.com/deckarep/golang-set/v2" - "github.com/samber/lo" - "github.com/shopspring/decimal" - "github.com/uptrace/bun" - "go.uber.org/zap" -) - -type _EndUserService struct { - l *zap.Logger -} - -type MeterAppears struct { - Meter string - Appears int64 -} - -var EndUserService = _EndUserService{ - l: logger.Named("Service", "EndUser"), -} - -func (_EndUserService) SearchEndUserRecord(reportId, keyword string, page int) ([]model.EndUserDetail, int64, error) { - var ( - conditions = make([]string, 0) - endUsers = make([]model.EndUserDetail, 0) - cond = global.DB.NewSelect().Model(&endUsers) - ) - conditions = append(conditions, reportId, strconv.Itoa(page)) - cond = cond.Where("report_id = ?", reportId) - if len(keyword) > 0 { - keywordCond := "%" + keyword + "%" - cond = cond.WhereGroup(" and ", func(q *bun.SelectQuery) *bun.SelectQuery { - return q.Where("customer_name like ?", keywordCond). - WhereOr("contact_name like ?", keywordCond). - WhereOr("contact_phone like ?", keywordCond). - WhereOr("meter_04kv_id like ?", keywordCond) - }) - conditions = append(conditions, keyword) - } - if cachedTotal, err := cache.RetreiveCount("end_user_detail", conditions...); cachedTotal != -1 && err == nil { - if cachedEndUsers, _ := cache.RetreiveSearch[[]model.EndUserDetail]("end_user_detail", conditions...); cachedEndUsers != nil { - return *cachedEndUsers, cachedTotal, nil - } - } - - ctx, cancel := global.TimeoutContext() - defer cancel() - - startItem := (page - 1) * config.ServiceSettings.ItemsPageSize - total, err := cond.Limit(config.ServiceSettings.ItemsPageSize). - Offset(startItem). - Order("seq asc", "meter_04kv_id asc"). - ScanAndCount(ctx) - - relations := []string{"end_user", "report", "park"} - for _, eu := range endUsers { - relations = append(relations, fmt.Sprintf("end_user:%s:%s", eu.ReportId, eu.MeterId)) - } - cache.CacheCount(relations, "end_user_detail", int64(total), conditions...) - cache.CacheSearch(endUsers, relations, "end_user_detail", conditions...) - return endUsers, int64(total), err -} - -func (_EndUserService) AllEndUserRecord(reportId string) ([]model.EndUserDetail, error) { - if cachedEndUsers, _ := cache.RetreiveSearch[[]model.EndUserDetail]("end_user_detail", "report", reportId); cachedEndUsers != nil { - return *cachedEndUsers, nil - } - ctx, cancel := global.TimeoutContext() - defer cancel() - - users := make([]model.EndUserDetail, 0) - err := global.DB.NewSelect().Model(&users). - Where("report_id = ?", reportId). - Order("seq asc", "meter_04kv_id asc"). - Scan(ctx) - relations := lo.Map(users, func(eu model.EndUserDetail, _ int) string { - return fmt.Sprintf("end_user:%s:%s", eu.ReportId, eu.MeterId) - }) - relations = append(relations, "report", "park") - cache.CacheSearch(users, relations, "end_user_detail", "report", reportId) - return users, err -} - -func (_EndUserService) FetchSpecificEndUserRecord(reportId, parkId, meterId string) (*model.EndUserDetail, error) { - if cachedEndUser, _ := cache.RetreiveEntity[model.EndUserDetail]("end_user_detail", fmt.Sprintf("%s_%s_%s", reportId, parkId, meterId)); cachedEndUser != nil { - return cachedEndUser, nil - } - ctx, cancel := global.TimeoutContext() - defer cancel() - - record := new(model.EndUserDetail) - err := global.DB.NewSelect().Model(record). - Where("report_id = ?", reportId). - Where("park_id = ?", parkId). - Where("meter_04kv_id = ?", meterId). - Scan(ctx) - cache.CacheEntity(record, []string{fmt.Sprintf("end_user:%s:%s", reportId, meterId), "report", "park"}, "end_user_detail", fmt.Sprintf("%s_%s_%s", reportId, parkId, meterId)) - return record, err -} - -func (_EndUserService) UpdateEndUserRegisterRecord(tx *bun.Tx, ctx *context.Context, record model.EndUserDetail) (err error) { - record.CalculatePeriod() - updateColumns := []string{ - "current_period_overall", - "adjust_overall", - "current_period_critical", - "current_period_peak", - "current_period_flat", - "current_period_valley", - "adjust_critical", - "adjust_peak", - "adjust_flat", - "adjust_valley", - "overall", - "critical", - "peak", - "flat", - "valley", - } - if record.Initialize { - updateColumns = append(updateColumns, - "last_period_overall", - "last_period_critical", - "last_period_peak", - "last_period_flat", - "last_period_valley", - ) - } - - _, err = tx.NewUpdate().Model(&record). - WherePK(). - Column(updateColumns...). - Exec(*ctx) - cache.AbolishRelation(fmt.Sprintf("end_user:%s:%s", record.ReportId, record.MeterId)) - return -} - -func (_EndUserService) newVirtualExcelAnalysisError(err error) *excel.ExcelAnalysisError { - return &excel.ExcelAnalysisError{Col: -1, Row: -1, Err: excel.AnalysisError{Err: err}} -} - -func (es _EndUserService) BatchImportNonPVRegister(reportId string, file io.Reader) *exceptions.BatchError { - ctx, cancel := global.TimeoutContext(120) - defer cancel() - - errs := exceptions.NewBatchError() - users, err := es.AllEndUserRecord(reportId) - if err != nil { - errs.AddError(es.newVirtualExcelAnalysisError(err)) - return errs - } - var reportDetail = new(model.Report) - err = global.DB.NewSelect().Model(reportDetail). - Where("id = ?", reportId). - Scan(ctx) - if err != nil { - errs.AddError(es.newVirtualExcelAnalysisError(fmt.Errorf("未能找到相应的报表。%w", err))) - return errs - } - meterAppers := make([]MeterAppears, 0) - err = global.DB.NewSelect().Model((*model.EndUserDetail)(nil)). - ColumnExpr("meter_04kv_id as meter"). - ColumnExpr("count(*) as appears"). - Where("park_id = ?", reportDetail.ParkId). - Group("meter_04kv_id"). - Scan(ctx, &meterAppers) - if err != nil { - errs.AddError(es.newVirtualExcelAnalysisError(err)) - return errs - } - indexedUsers := lo.Reduce( - users, - 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), - ) - analyzer, err := excel.NewEndUserNonPVExcelAnalyzer(file) - if err != nil { - errs.AddError(es.newVirtualExcelAnalysisError(err)) - return errs - } - imports, excelErrs := analyzer.Analysis(*new(model.EndUserImport)) - if len(excelErrs) > 0 { - for _, e := range excelErrs { - errs.AddError(e) - } - return errs - } - tx, err := global.DB.BeginTx(ctx, &sql.TxOptions{}) - if err != nil { - errs.AddError(es.newVirtualExcelAnalysisError(err)) - return errs - } - - for _, im := range imports { - if elem, ok := indexedUsers[im.MeterId]; ok { - if appears, has := lo.Find(meterAppers, func(m MeterAppears) bool { - return m.Meter == elem.MeterId - }); has { - if appears.Appears <= 1 { - elem.LastPeriodOverall = im.LastPeriodOverall - elem.LastPeriodCritical = decimal.Zero - elem.LastPeriodPeak = decimal.Zero - elem.LastPeriodValley = decimal.Zero - elem.LastPeriodFlat = elem.LastPeriodOverall.Sub(elem.LastPeriodCritical).Sub(elem.LastPeriodPeak).Sub(elem.LastPeriodValley) - elem.Initialize = true - } - } - elem.CurrentPeriodOverall = im.CurrentPeriodOverall - elem.AdjustOverall = im.AdjustOverall - elem.CurrentPeriodCritical = decimal.Zero - elem.CurrentPeriodPeak = decimal.Zero - elem.CurrentPeriodValley = decimal.Zero - elem.CurrentPeriodFlat = elem.CurrentPeriodOverall.Sub(elem.CurrentPeriodCritical).Sub(elem.CurrentPeriodPeak).Sub(elem.CurrentPeriodValley) - elem.AdjustCritical = decimal.Zero - elem.AdjustPeak = decimal.Zero - elem.AdjustValley = decimal.Zero - elem.AdjustFlat = elem.AdjustOverall.Sub(elem.AdjustCritical).Sub(elem.AdjustPeak).Sub(elem.AdjustValley) - err := es.UpdateEndUserRegisterRecord(&tx, &ctx, elem) - if err != nil { - errs.AddError(es.newVirtualExcelAnalysisError(err)) - } - } else { - errs.AddError(exceptions.NewNotFoundError(fmt.Sprintf("表计 %s 未找到", im.MeterId))) - } - } - if errs.Len() > 0 { - tx.Rollback() - return errs - } - - err = tx.Commit() - if err != nil { - tx.Rollback() - errs.AddError(es.newVirtualExcelAnalysisError(err)) - } - cache.AbolishRelation("end_user_detail") - return errs -} - -func (es _EndUserService) BatchImportPVRegister(reportId string, file io.Reader) *exceptions.BatchError { - ctx, cancel := global.TimeoutContext(120) - defer cancel() - - errs := exceptions.NewBatchError() - users, err := es.AllEndUserRecord(reportId) - if err != nil { - errs.AddError(es.newVirtualExcelAnalysisError(err)) - return errs - } - var reportDetail = new(model.Report) - err = global.DB.NewSelect().Model(reportDetail).Where("id = ?", reportId).Scan(ctx) - if err != nil { - errs.AddError(es.newVirtualExcelAnalysisError(fmt.Errorf("未能找到相应的报表。%w", err))) - return errs - } - meterAppers := make([]MeterAppears, 0) - err = global.DB.NewSelect().Model((*model.EndUserDetail)(nil)). - ColumnExpr("meter_04kv_id as meter"). - ColumnExpr("count(*) as appears"). - Where("park_id = ?", reportDetail.Id). - Group("meter_04kv_id"). - Scan(ctx, &meterAppers) - if err != nil { - errs.AddError(es.newVirtualExcelAnalysisError(err)) - return errs - } - indexedUsers := lo.Reduce( - users, - 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), - ) - analyzer, err := excel.NewEndUserPVExcelAnalyzer(file) - if err != nil { - errs.AddError(es.newVirtualExcelAnalysisError(err)) - return errs - } - imports, excelErrs := analyzer.Analysis(*new(model.EndUserImport)) - if len(excelErrs) > 0 { - for _, e := range excelErrs { - errs.AddError(e) - } - return errs - } - - tx, err := global.DB.BeginTx(ctx, &sql.TxOptions{}) - if err != nil { - errs.AddError(es.newVirtualExcelAnalysisError(err)) - return errs - } - - for _, im := range imports { - if elem, ok := indexedUsers[im.MeterId]; ok { - if appears, has := lo.Find(meterAppers, func(m MeterAppears) bool { - return m.Meter == elem.MeterId - }); has { - if appears.Appears <= 1 { - elem.LastPeriodOverall = im.LastPeriodOverall - elem.LastPeriodCritical = im.LastPeriodCritical.Decimal - elem.LastPeriodPeak = im.LastPeriodPeak.Decimal - elem.LastPeriodValley = im.LastPeriodValley.Decimal - elem.LastPeriodFlat = elem.LastPeriodOverall.Sub(elem.LastPeriodCritical).Sub(elem.LastPeriodPeak).Sub(elem.LastPeriodValley) - elem.Initialize = true - } - } - elem.CurrentPeriodOverall = im.CurrentPeriodOverall - elem.AdjustOverall = im.AdjustOverall - elem.CurrentPeriodCritical = im.CurrentPeriodCritical.Decimal - elem.CurrentPeriodPeak = im.CurrentPeriodPeak.Decimal - elem.CurrentPeriodValley = im.CurrentPeriodValley.Decimal - elem.CurrentPeriodFlat = elem.CurrentPeriodOverall.Sub(elem.CurrentPeriodCritical).Sub(elem.CurrentPeriodPeak).Sub(elem.CurrentPeriodValley) - elem.AdjustCritical = im.AdjustCritical.Decimal - elem.AdjustPeak = im.AdjustPeak.Decimal - elem.AdjustValley = im.AdjustValley.Decimal - elem.AdjustFlat = elem.AdjustOverall.Sub(elem.AdjustCritical).Sub(elem.AdjustPeak).Sub(elem.AdjustValley) - err := es.UpdateEndUserRegisterRecord(&tx, &ctx, elem) - if err != nil { - errs.AddError(es.newVirtualExcelAnalysisError(err)) - } - } else { - errs.AddError(es.newVirtualExcelAnalysisError(exceptions.NewNotFoundError(fmt.Sprintf("表计 %s 未找到", im.MeterId)))) - } - } - if errs.Len() > 0 { - tx.Rollback() - return errs - } - - err = tx.Commit() - if err != nil { - tx.Rollback() - errs.AddError(es.newVirtualExcelAnalysisError(err)) - } - cache.AbolishRelation("end_user_detail") - return errs -} - -func (es _EndUserService) StatEndUserRecordInPeriod(requestUser, requestPark, startDate, endDate string) ([]model.EndUserPeriodStat, error) { - var ( - conditions = make([]string, 0) - relations = []string{ - fmt.Sprintf("park:%s", requestPark), - "end_user_detail", - } - cond = global.DB.NewSelect(). - Model((*model.EndUserDetail)(nil)). - Relation("Report", func(sq *bun.SelectQuery) *bun.SelectQuery { - return sq.ExcludeColumn("*") - }). - Relation("Park", func(sq *bun.SelectQuery) *bun.SelectQuery { - return sq.ExcludeColumn("*") - }) - ) - if len(requestUser) > 0 { - cond = cond.Where("park.user_id = ?", requestUser) - conditions = append(conditions, requestUser) - } else { - conditions = append(conditions, "_") - } - if len(requestPark) > 0 { - cond = cond.Where("eud.park_id = ?", requestPark) - conditions = append(conditions, requestPark) - } else { - conditions = append(conditions, "_") - } - if len(startDate) > 0 { - parseTime, err := time.Parse("2006-01", startDate) - if err != nil { - return make([]model.EndUserPeriodStat, 0), fmt.Errorf("不能解析给定的参数[startDate],%w", err) - } - start := model.NewDate(parseTime) - cond = cond.Where("report.period >= ?::date", start.ToString()) - conditions = append(conditions, startDate) - } else { - conditions = append(conditions, "_") - } - if len(endDate) > 0 { - parseTime, err := time.Parse("2006-01", endDate) - if err != nil { - return make([]model.EndUserPeriodStat, 0), fmt.Errorf("不能解析给定的参数[endDate],%w", err) - } - end := model.NewDate(parseTime) - cond = cond.Where("report.period <= ?::date", end.ToString()) - conditions = append(conditions, endDate) - } - if cached, err := cache.RetreiveSearch[[]model.EndUserPeriodStat]("end_user_stat", conditions...); cached != nil && err == nil { - return *cached, nil - } - ctx, cancel := global.TimeoutContext(120) - defer cancel() - var endUserSums []model.EndUserPeriodStat - err := cond.Column("eud.meter_04kv_id", "eud.park_id"). - ColumnExpr("sum(?) as overall", bun.Ident("eud.overall")). - ColumnExpr("sum(?) as overall_fee", bun.Ident("eud.overall_fee")). - ColumnExpr("sum(?) as critical", bun.Ident("eud.critical")). - ColumnExpr("sum(?) as critical_fee", bun.Ident("eud.critical_fee")). - ColumnExpr("sum(?) as peak", bun.Ident("eud.peak")). - ColumnExpr("sum(?) as peak_fee", bun.Ident("eud.peak_fee")). - ColumnExpr("sum(?) as valley", bun.Ident("eud.valley")). - ColumnExpr("sum(?) as valley_fee", bun.Ident("eud.valley_fee")). - ColumnExpr("sum(?) as final_diluted", bun.Ident("eud.final_diluted")). - Where("report.published = ?", true). - Group("eud.meter_04kv_id", "eud.park_id"). - Scan(ctx, &endUserSums) - if err != nil { - return make([]model.EndUserPeriodStat, 0), fmt.Errorf("未能完成终端用户在指定期限内的统计,%w", err) - } - parkIds := lo.Reduce( - endUserSums, - func(acc mapset.Set[string], elem model.EndUserPeriodStat, _ int) mapset.Set[string] { - acc.Add(elem.ParkId) - return acc - }, - mapset.NewSet[string](), - ) - meterArchives := make([]model.Meter04KV, 0) - if len(parkIds.ToSlice()) > 0 { - err = global.DB.NewSelect(). - Model(&meterArchives).Relation("ParkDetail"). - Where("park_id in (?)", bun.In(parkIds.ToSlice())). - Scan(ctx) - if err != nil { - return make([]model.EndUserPeriodStat, 0), fmt.Errorf("未能获取到终端表计的最新基础档案,%w", err) - } - } - filledStats := lo.Map( - endUserSums, - func(elem model.EndUserPeriodStat, _ int) model.EndUserPeriodStat { - archive, has := lo.Find(meterArchives, func(meter model.Meter04KV) bool { - return meter.Code == elem.MeterId - }) - if has { - if archive.Address != nil { - elem.Address = *archive.Address - } else { - elem.Address = "" - } - if archive.CustomerName != nil { - elem.CustomerName = *archive.CustomerName - } else { - elem.CustomerName = "" - } - elem.IsPublicMeter = archive.IsPublicMeter - elem.Kind = archive.ParkDetail.SubmeterType - } - if elem.OverallFee.Valid && elem.AdjustFee.Valid && !elem.OverallFee.Decimal.IsZero() { - elem.AdjustProportion = decimal.NewNullDecimal( - elem.AdjustFee.Decimal.Div(elem.OverallFee.Decimal).RoundBank(8), - ) - } else { - elem.AdjustProportion = decimal.NullDecimal{} - } - return elem - }, - ) - cache.CacheSearch(filledStats, relations, "end_user_stat", conditions...) - return filledStats, nil -} diff --git a/service/god_mode.go b/service/god_mode.go index 044b423..a493def 100644 --- a/service/god_mode.go +++ b/service/god_mode.go @@ -1,875 +1,232 @@ package service import ( - "context" - "database/sql" - "electricity_bill_calc/cache" - "electricity_bill_calc/exceptions" "electricity_bill_calc/global" "electricity_bill_calc/logger" - "electricity_bill_calc/model" + "electricity_bill_calc/repository" "fmt" - "time" - - "github.com/samber/lo" - "github.com/shopspring/decimal" - "github.com/uptrace/bun" + "github.com/doug-martin/goqu/v9" "go.uber.org/zap" ) -type _GodModeService struct { - l *zap.Logger +type _GMService struct { + l *zap.Logger + gm goqu.DialectWrapper } -var GodModeService = _GodModeService{ - l: logger.Named("Service", "GodMode"), +var GMService = _GMService{ + logger.Named("Service", "GM"), + goqu.Dialect("postgres"), } -// 从此处开始为删除报表相关的部分 +func (gm _GMService) DeleteTenements(pid string, tenements []string) error { + var err error + ctx, cancel := global.TimeoutContext() + defer cancel() -func (_GodModeService) resetReportIndex(tx *bun.Tx, ctx *context.Context, reportId string) (bool, error) { - var report = new(model.Report) - err := tx.NewSelect().Model(report).Where("id = ?", reportId).Scan(*ctx) + tx, err := global.DB.Begin(ctx) if err != nil { - tx.Rollback() - return false, exceptions.NewNotFoundError("指定报表索引未找到。") + gm.l.Error("未能启动数据库事务", zap.Error(err)) + return fmt.Errorf("未能启动数据库事务,%w", err) } - report.StepState.Summary = false - report.StepState.WillDiluted = false - report.StepState.Submeter = false - report.StepState.Calculate = false - report.StepState.Preview = false - report.StepState.Publish = false - report.Published = false - report.PublishedAt = nil - report.Withdraw = model.REPORT_NOT_WITHDRAW - report.LastWithdrawAppliedAt = nil - report.LastWithdrawAuditAt = nil - res, err := tx.NewUpdate().Model(report). - WherePK(). - Column( - "step_state", - "published", - "published_at", - "withdraw", - "last_withdraw_applied_at", - "last_withdraw_audit_at", - ). - Exec(*ctx) - - if affected, _ := res.RowsAffected(); err != nil || affected == 0 { - tx.Rollback() - return false, err + err = repository.GMRepository.DeleteMeterBinding(ctx, tx, pid, tenements) + if err != nil { + tx.Rollback(ctx) + return err } - return true, err + + err = repository.GMRepository.DeleteTenements(ctx, tx, pid, tenements) + if err != nil { + tx.Rollback(ctx) + return err + } + tx.Commit(ctx) + return nil } -func (_GodModeService) resetReportSummary(tx *bun.Tx, ctx *context.Context, reportId string) (bool, error) { - var summary = &model.ReportSummary{ - ReportId: reportId, - } - _, err := tx.NewUpdate().Model(summary).WherePK().Exec(*ctx) - if err != nil { - tx.Rollback() - return false, err - } - var report = new(model.Report) - err = tx.NewSelect().Model(report).Where("id = ?", reportId).Scan(*ctx) - if err != nil { - tx.Rollback() - return false, err - } - report.StepState.Summary = false - res, err := tx.NewUpdate().Model(report). - Column("step_state"). - WherePK(). - Exec(*ctx) - rows, _ := res.RowsAffected() - if err != nil { - tx.Rollback() - } - return rows >= 0, err -} +func (gm _GMService) DeleteParks(parks []string) error { -func (_GodModeService) flushReportMaintenances(tx *bun.Tx, ctx *context.Context, reportId string) (bool, error) { - _, err := tx.NewDelete().Model((*model.WillDilutedFee)(nil)). - Where("report_id = ?", reportId). - Exec(*ctx) - if err != nil { - tx.Rollback() - return false, err - } - var report = new(model.Report) - err = tx.NewSelect().Model(report).Where("id = ?", reportId).Scan(*ctx) - if err != nil { - tx.Rollback() - return false, err - } - report.StepState.WillDiluted = false - res, err := tx.NewUpdate().Model(report). - WherePK(). - Column("step_state"). - Exec(*ctx) - rows, _ := res.RowsAffected() - if err != nil { - tx.Rollback() - } - return rows >= 0, err -} + var err error + ctx, cancel := global.TimeoutContext() + defer cancel() -func (g _GodModeService) resetSingleEndUserRecord(tx *bun.Tx, ctx *context.Context, record model.EndUserDetail, additionalColumns ...string) (bool, error) { - record.CurrentPeriodOverall = decimal.Zero - record.CurrentPeriodCritical = decimal.Zero - record.CurrentPeriodPeak = decimal.Zero - record.CurrentPeriodFlat = decimal.Zero - record.CurrentPeriodValley = decimal.Zero - record.AdjustOverall = decimal.Zero - record.AdjustCritical = decimal.Zero - record.AdjustPeak = decimal.Zero - record.AdjustFlat = decimal.Zero - record.AdjustValley = decimal.Zero - record.Overall = decimal.NewNullDecimal(decimal.Zero) - record.Overall.Valid = false - record.OverallFee = decimal.NewNullDecimal(decimal.Zero) - record.OverallFee.Valid = false - record.OverallProportion = decimal.Zero - record.Critical = decimal.NewNullDecimal(decimal.Zero) - record.Critical.Valid = false - record.CriticalFee = decimal.NewNullDecimal(decimal.Zero) - record.CriticalFee.Valid = false - record.Peak = decimal.NewNullDecimal(decimal.Zero) - record.Peak.Valid = false - record.PeakFee = decimal.NewNullDecimal(decimal.Zero) - record.PeakFee.Valid = false - record.Flat = decimal.NewNullDecimal(decimal.Zero) - record.Flat.Valid = false - record.FlatFee = decimal.NewNullDecimal(decimal.Zero) - record.FlatFee.Valid = false - record.Valley = decimal.NewNullDecimal(decimal.Zero) - record.Valley.Valid = false - record.ValleyFee = decimal.NewNullDecimal(decimal.Zero) - record.ValleyFee.Valid = false - record.BasicFeeDiluted = decimal.NewNullDecimal(decimal.Zero) - record.BasicFeeDiluted.Valid = false - record.AdjustFeeDiluted = decimal.NewNullDecimal(decimal.Zero) - record.AdjustFeeDiluted.Valid = false - record.LossDiluted = decimal.NewNullDecimal(decimal.Zero) - record.LossDiluted.Valid = false - record.LossFeeDiluted = decimal.NewNullDecimal(decimal.Zero) - record.LossFeeDiluted.Valid = false - record.FinalDiluted = decimal.NewNullDecimal(decimal.Zero) - record.FinalDiluted.Valid = false - record.FinalCharge = decimal.NewNullDecimal(decimal.Zero) - record.FinalCharge.Valid = false - - columns := []string{ - "current_period_overall", - "current_period_critical", - "current_period_peak", - "current_period_flat", - "current_period_valley", - "adjust_overall", - "adjust_critical", - "adjust_peak", - "adjust_flat", - "adjust_valley", - "overall", - "overall_fee", - "overall_proportion", - "critical", - "critical_fee", - "peak", - "peak_fee", - "flat", - "flat_fee", - "valley", - "valley_fee", - "basic_fee_diluted", - "adjust_fee_diluted", - "loss_diluted", - "loss_fee_diluted", - "maintenance_fee_diluted", - "public_consumption_diluted", - "final_diluted", - "final_charge", - } - columns = append(columns, additionalColumns...) - - _, err := tx.NewUpdate().Model(&record). - WherePK(). - Column(columns...). - Exec(*ctx) + tx, err := global.DB.Begin(ctx) if err != nil { - tx.Rollback() - return false, err + gm.l.Error("未能启动数据库事务", zap.Error(err)) + return fmt.Errorf("未能启动数据库事务,%w", err) } - return true, nil -} - -func (g _GodModeService) resynchronizeEndUserArchives(tx *bun.Tx, ctx *context.Context, reportId string) (bool, error) { - var currentRecords = make([]*model.EndUserDetail, 0) - err := tx.NewSelect().Model(¤tRecords). - Where("report_id = ?", reportId). - Scan(*ctx) - if err != nil { - tx.Rollback() - return false, err - } - var report = new(model.Report) - err = tx.NewSelect().Model(report). - Where("id = ?", reportId). - Scan(*ctx) - if err != nil || report == nil { - tx.Rollback() - return false, err - } - var latestArchives = make([]model.Meter04KV, 0) - err = tx.NewSelect().Model(&latestArchives). - Where("park_id = ?", report.ParkId). - Where("enabled = ?", true). - Scan(*ctx) - if err != nil { - tx.Rollback() - return false, err - } - - for _, meter := range latestArchives { - record, has := lo.Find(currentRecords, func(rec *model.EndUserDetail) bool { - return rec.ParkId == meter.ParkId && rec.MeterId == meter.Code - }) - if has { - record.CustomerName = meter.CustomerName - record.Address = meter.Address - record.Ratio = meter.Ratio - record.ContactName = meter.ContactName - record.ContactPhone = meter.ContactPhone - record.Seq = meter.Seq - record.IsPublicMeter = meter.IsPublicMeter - success, err := g.resetSingleEndUserRecord( - tx, ctx, *record, - "customer_name", - "address", - "ratio", - "contact_name", - "contact_phone", - "seq", - "public_meter", - ) - if err != nil { - return success, err - } - } else { - newEndUser := model.EndUserDetail{ - ReportId: report.Id, - ParkId: report.ParkId, - MeterId: meter.Code, - Seq: meter.Seq, - Ratio: meter.Ratio, - Address: meter.Address, - CustomerName: meter.CustomerName, - ContactName: meter.ContactName, - ContactPhone: meter.ContactPhone, - IsPublicMeter: meter.IsPublicMeter, - LastPeriodOverall: decimal.Zero, - LastPeriodCritical: decimal.Zero, - LastPeriodPeak: decimal.Zero, - LastPeriodFlat: decimal.Zero, - LastPeriodValley: decimal.Zero, - } - _, err = tx.NewInsert().Model(&newEndUser).Exec(*ctx) - if err != nil { - tx.Rollback() - return false, err - } - } - } - - report.StepState.Submeter = false - res, err := tx.NewUpdate().Model(report). - WherePK(). - Column("step_state"). - Exec(*ctx) - rows, _ := res.RowsAffected() - if err != nil { - tx.Rollback() - } - return rows >= 0, nil -} - -func (g _GodModeService) resetEndUserRecords(tx *bun.Tx, ctx *context.Context, reportId string) (bool, error) { - var records = make([]model.EndUserDetail, 0) - err := tx.NewSelect().Model(&records). - Where("report_id = ?", reportId). - Scan(*ctx) - if err != nil { - tx.Rollback() - return false, err - } - for _, u := range records { - success, err := g.resetSingleEndUserRecord(tx, ctx, u) + for _, pid := range parks { + //删除invoices + err = repository.GMRepository.DeleteInvoices(ctx, tx, pid) + //删除meter_binding + err = repository.GMRepository.DeleteMeterBinding(ctx, tx, pid, []string{}) + //删除meter_poolings + err = repository.GMRepository.DeleteMeterPoolings(ctx, tx, pid) + //删除tenements + err = repository.GMRepository.DeleteTenements(ctx, tx, pid, []string{}) + //删除meters + err = repository.GMRepository.DeleteMeters(ctx, tx, pid) + //删除reports + err = repository.GMRepository.DeleteReports(ctx, tx, pid) + //删除buildings + err = repository.GMRepository.DeleteBuildings(ctx, tx, pid) if err != nil { - return success, err + gm.l.Error("删除关联表出错。", zap.Error(err)) + break + return err } } - var report = new(model.Report) - err = tx.NewSelect().Model(report). - Where("id = ?", reportId). - Scan(*ctx) + err = repository.GMRepository.DeleteParks(ctx, tx, parks) if err != nil { - tx.Rollback() - return false, err + gm.l.Error("指定园区删除失败。", zap.Error(err)) + return err } - report.StepState.Submeter = false - res, err := tx.NewUpdate().Model(report). - WherePK(). - Column("step_state"). - Exec(*ctx) - rows, _ := res.RowsAffected() - if err != nil { - tx.Rollback() - } - return rows >= 0, nil + tx.Commit(ctx) + return nil } -type ReportPeriod struct { - Id string - Period time.Time -} - -func (_GodModeService) isTheLatestReport(ctx *context.Context, reportId string) (bool, error) { - var report = new(model.Report) - err := global.DB.NewSelect().Model(report). - Where("id = ?", reportId). - Scan(*ctx) - if err != nil || report == nil { - return false, exceptions.NewNotFoundErrorFromError("指定报表索引未找到,", err) - } - var maxPeriod time.Time - err = global.DB.NewSelect().Model((*model.Report)(nil)). - ColumnExpr("max(?)", bun.Ident("period")). - Where("park_id = ?", report.ParkId). - Scan(*ctx, &maxPeriod) - if err != nil { - return false, err - } - return maxPeriod.Equal(report.Period), nil -} - -func (_GodModeService) forceDeleteReport(tx *bun.Tx, ctx *context.Context, reportId string) (bool, error) { - _, err := tx.NewDelete().Model((*model.EndUserDetail)(nil)). - Where("report_id = ?", reportId). - Exec(*ctx) - if err != nil { - tx.Rollback() - return false, err - } - _, err = tx.NewDelete().Model((*model.WillDilutedFee)(nil)). - Where("report_id = ?", reportId). - Exec(*ctx) - if err != nil { - tx.Rollback() - return false, err - } - _, err = tx.NewDelete().Model((*model.ReportSummary)(nil)). - Where("report_id = ?", reportId). - Exec(*ctx) - if err != nil { - tx.Rollback() - return false, err - } - _, err = tx.NewDelete().Model((*model.Report)(nil)). - Where("id = ?", reportId). - Exec(*ctx) - if err != nil { - tx.Rollback() - return false, err - } - return true, nil -} - -func (g _GodModeService) ClearReportSummary(reportId string) (bool, error) { - ctx, cancel := global.TimeoutContext(12) - defer cancel() - - isLatest, err := g.isTheLatestReport(&ctx, reportId) - if err != nil { - return false, err - } - if !isLatest { - return false, exceptions.NewImproperOperateError("不能操作非最新期数的报表。") - } - tx, err := global.DB.BeginTx(ctx, &sql.TxOptions{}) - if err != nil { - return false, err - } - - result, err := g.resetReportSummary(&tx, &ctx, reportId) - if err != nil { - return false, err - } - - err = tx.Commit() - if err != nil { - tx.Rollback() - return false, err - } - cache.AbolishRelation(fmt.Sprintf("report:%s", reportId)) - return result, nil -} - -func (g _GodModeService) ClearReportMaintenances(reportId string) (bool, error) { +func (gm _GMService) DeleteReports(pid string, reports []string) error { ctx, cancel := global.TimeoutContext() defer cancel() - isLatest, err := g.isTheLatestReport(&ctx, reportId) + tx, err := global.DB.Begin(ctx) if err != nil { - return false, err - } - if !isLatest { - return false, exceptions.NewImproperOperateError("不能操作非最新期数的报表。") - } - tx, err := global.DB.BeginTx(ctx, &sql.TxOptions{}) - if err != nil { - return false, err + gm.l.Error("未能启动数据库事务", zap.Error(err)) + return fmt.Errorf("未能启动数据库事务,%w", err) } - result, err := g.flushReportMaintenances(&tx, &ctx, reportId) + err = repository.GMRepository.DeleteReports(ctx, tx, pid, reports) if err != nil { - return false, err + tx.Rollback(ctx) + return err } - - err = tx.Commit() - if err != nil { - tx.Rollback() - return false, err - } - cache.AbolishRelation(fmt.Sprintf("report:%s", reportId)) - return result, nil + tx.Commit(ctx) + return nil } -func (g _GodModeService) ResynchronizeEndUser(reportId string) (bool, error) { +func (gm _GMService) DeleteTenementMeterRelations(pId string, tId []string, mId []string) error { + gm.l.Info("删除商户表记关系", zap.String("tenement", pId)) + ctx, cancel := global.TimeoutContext(10) + defer cancel() + + tx, err := global.DB.Begin(ctx) + if err != nil { + gm.l.Error("开启数据库事务失败。", zap.Error(err)) + return err + } + if err := repository.GMRepository.DeleteMeterBinding(ctx, tx, pId, tId, mId); err != nil { + gm.l.Error("无法删除商户与表记关系。", zap.Error(err)) + tx.Rollback(ctx) + return err + } + err = tx.Commit(ctx) + if err != nil { + gm.l.Error("未能成功提交数据库事务。", zap.Error(err)) + tx.Rollback(ctx) + return err + } + return nil +} + +func (gm _GMService) DeleteEnterprises(uid string) error { + var err error ctx, cancel := global.TimeoutContext() defer cancel() - isLatest, err := g.isTheLatestReport(&ctx, reportId) + tx, err := global.DB.Begin(ctx) if err != nil { - return false, err + gm.l.Error("未能启动数据库事务", zap.Error(err)) + return fmt.Errorf("未能启动数据库事务,%w", err) } - if !isLatest { - return false, exceptions.NewImproperOperateError("不能操作非最新期数的报表。") - } - tx, err := global.DB.BeginTx(ctx, &sql.TxOptions{}) + parks, err := repository.GMRepository.ListAllParkIdsInUser(ctx, tx, uid) if err != nil { - return false, err + gm.l.Error("查询园区错误", zap.Error(err)) + tx.Rollback(ctx) + return err } - result, err := g.resynchronizeEndUserArchives(&tx, &ctx, reportId) - if err != nil { - return false, err - } + for _, pid := range parks { + err = repository.GMRepository.DeleteInvoices(ctx, tx, pid) + err = repository.GMRepository.DeleteMeterBinding(ctx, tx, pid, []string{}) + err = repository.GMRepository.DeleteMeterPoolings(ctx, tx, pid) + err = repository.GMRepository.DeleteTenements(ctx, tx, pid) + err = repository.GMRepository.DeleteMeters(ctx, tx, pid) + err = repository.GMRepository.DeleteReports(ctx, tx, pid) + err = repository.GMRepository.DeleteBuildings(ctx, tx, pid) - err = tx.Commit() - if err != nil { - tx.Rollback() - return false, err - } - cache.AbolishRelation("end_user_detail") - cache.AbolishRelation(fmt.Sprintf("report:%s", reportId)) - return result, nil -} - -func (g _GodModeService) ResetEndUserRegisterRecords(reportId string) (bool, error) { - ctx, cancel := global.TimeoutContext(48) - defer cancel() - - isLatest, err := g.isTheLatestReport(&ctx, reportId) - if err != nil { - return false, err - } - if !isLatest { - return false, exceptions.NewImproperOperateError("不能操作非最新期数的报表。") - } - tx, err := global.DB.BeginTx(ctx, &sql.TxOptions{}) - if err != nil { - return false, err - } - - result, err := g.resetEndUserRecords(&tx, &ctx, reportId) - if err != nil { - return false, err - } - - err = tx.Commit() - if err != nil { - tx.Rollback() - return false, err - } - cache.AbolishRelation("end_user_detail") - cache.AbolishRelation(fmt.Sprintf("report:%s", reportId)) - return result, nil -} - -func (g _GodModeService) ResetReport(reportId string) (bool, error) { - ctx, cancel := global.TimeoutContext() - defer cancel() - - isLatest, err := g.isTheLatestReport(&ctx, reportId) - if err != nil { - return false, err - } - if !isLatest { - return false, exceptions.NewImproperOperateError("不能操作非最新期数的报表。") - } - tx, err := global.DB.BeginTx(ctx, &sql.TxOptions{}) - if err != nil { - return false, err - } - var result = true - r, err := g.resetEndUserRecords(&tx, &ctx, reportId) - if err != nil { - return false, err - } - result = result && r - r, err = g.flushReportMaintenances(&tx, &ctx, reportId) - if err != nil { - return false, err - } - result = result && r - r, err = g.resetReportSummary(&tx, &ctx, reportId) - if err != nil { - return false, err - } - result = result && r - r, err = g.resetReportIndex(&tx, &ctx, reportId) - if err != nil { - return false, err - } - result = result && r - - err = tx.Commit() - if err != nil { - tx.Rollback() - return false, err - } - cache.AbolishRelation("end_user_detail") - cache.AbolishRelation(fmt.Sprintf("report:%s", reportId)) - return result, nil -} - -func (g _GodModeService) DeleteReport(reportId string) (bool, error) { - ctx, cancel := global.TimeoutContext() - defer cancel() - - isLatest, err := g.isTheLatestReport(&ctx, reportId) - if err != nil { - return false, err - } - if !isLatest { - return false, exceptions.NewImproperOperateError("不能删除非最新期数的报表。") - } - tx, err := global.DB.BeginTx(ctx, &sql.TxOptions{}) - if err != nil { - return false, err - } - - result, err := g.forceDeleteReport(&tx, &ctx, reportId) - if err != nil { - return false, err - } - - err = tx.Commit() - if err != nil { - tx.Rollback() - return false, err - } - cache.AbolishRelation("end_user_detail") - cache.AbolishRelation(fmt.Sprintf("report:%s", reportId)) - - return result, nil -} - -// 从此处开始为删除园区相关的内容部分 - -func (_GodModeService) deleteSpecificMaintenance(tx *bun.Tx, ctx *context.Context, parkId, maintenanceId string) (bool, error) { - res, err := tx.NewDelete().Model((*model.MaintenanceFee)(nil)). - Where("park_id = ?", parkId). - Where("id = ?", maintenanceId). - Exec(*ctx) - - if err != nil { - tx.Rollback() - return false, nil - } - if rows, err := res.RowsAffected(); err != nil { - tx.Rollback() - return false, err - } else { - return rows >= 0, err - } -} - -func (_GodModeService) deleteAllMaintenance(tx *bun.Tx, ctx *context.Context, parkId string) (bool, error) { - res, err := tx.NewDelete().Model((*model.MaintenanceFee)(nil)). - Where("park_id = ?", parkId). - Exec(*ctx) - if err != nil { - tx.Rollback() - return false, nil - } - if rows, err := res.RowsAffected(); err != nil { - tx.Rollback() - return false, err - } else { - return rows >= 0, err - } -} - -func (_GodModeService) deleteAllMeters(tx *bun.Tx, ctx *context.Context, parkId string) (bool, error) { - res, err := tx.NewDelete().Model((*model.Meter04KV)(nil)). - Where("park_id = ?", parkId). - Exec(*ctx) - if err != nil { - tx.Rollback() - return false, nil - } - if rows, err := res.RowsAffected(); err != nil { - tx.Rollback() - return false, err - } else { - return rows >= 0, err - } -} - -func (_GodModeService) deletePark(tx *bun.Tx, ctx *context.Context, parkId string) (bool, error) { - res, err := tx.NewDelete().Model((*model.Park)(nil)). - Where("id = ?", parkId). - Exec(*ctx) - if err != nil { - tx.Rollback() - return false, nil - } - if rows, err := res.RowsAffected(); err != nil { - tx.Rollback() - return false, err - } else { - return rows >= 0, err - } -} - -func (g _GodModeService) RemoveSpecificMaintenance(parkId, maintenanceId string) (bool, error) { - ctx, cancel := global.TimeoutContext() - defer cancel() - - tx, err := global.DB.BeginTx(ctx, &sql.TxOptions{}) - if err != nil { - return false, err - } - - result, err := g.deleteSpecificMaintenance(&tx, &ctx, parkId, maintenanceId) - if err != nil { - return false, err - } - - err = tx.Commit() - if err != nil { - tx.Rollback() - return false, err - } - cache.AbolishRelation(fmt.Sprintf("maintenance_fee:%s", maintenanceId)) - return result, nil -} - -func (g _GodModeService) RemoveAllMaintenance(parkId string) (bool, error) { - ctx, cancel := global.TimeoutContext() - defer cancel() - - tx, err := global.DB.BeginTx(ctx, &sql.TxOptions{}) - if err != nil { - return false, err - } - - result, err := g.deleteAllMaintenance(&tx, &ctx, parkId) - if err != nil { - return false, err - } - - err = tx.Commit() - if err != nil { - tx.Rollback() - return false, err - } - cache.AbolishRelation("maintenance_fee") - return result, nil -} - -func (g _GodModeService) RemoveAllMeters(parkId string) (bool, error) { - ctx, cancel := global.TimeoutContext() - defer cancel() - - tx, err := global.DB.BeginTx(ctx, &sql.TxOptions{}) - if err != nil { - return false, err - } - - result, err := g.deleteAllMeters(&tx, &ctx, parkId) - if err != nil { - return false, err - } - - err = tx.Commit() - if err != nil { - tx.Rollback() - return false, err - } - cache.AbolishRelation("meter_04kv") - return result, nil -} - -func (g _GodModeService) erasePark(tx *bun.Tx, ctx *context.Context, parkId string) (bool, error) { - var reportIds = make([]string, 0) - err := tx.NewSelect().Model((*model.Report)(nil)). - Where("park_id = ?", parkId). - Column("id"). - Scan(*ctx, &reportIds) - if err != nil { - tx.Rollback() - return false, err - } - var result = true - for _, id := range reportIds { - r, err := g.forceDeleteReport(tx, ctx, id) if err != nil { - return false, err + gm.l.Error("删除用户下关联出错", zap.Error(err)) + return err } - result = result && r } - r, err := g.deleteAllMaintenance(tx, ctx, parkId) - if err != nil { - return false, err - } - result = result && r - r, err = g.deleteAllMeters(tx, ctx, parkId) - if err != nil { - return false, err - } - result = result && r - r, err = g.deletePark(tx, ctx, parkId) - if err != nil { - return false, err - } - result = result && r - return result, err + err = repository.GMRepository.DeleteParks(ctx, tx, parks) + if err != nil { + gm.l.Error("删除用户关联园区错误", zap.Error(err)) + tx.Rollback(ctx) + return err + } + err = repository.GMRepository.DeleteUsers(ctx, tx, uid) + if err != nil { + gm.l.Error("删除用户信息出错", zap.Error(err)) + tx.Rollback(ctx) + return err + } + return nil } -func (g _GodModeService) RemovePark(parkId string) (bool, error) { +func (gm _GMService) DeleteMeterPooling(pId string, mId []string) error { ctx, cancel := global.TimeoutContext() defer cancel() - - tx, err := global.DB.BeginTx(ctx, &sql.TxOptions{}) + tx, err := global.DB.Begin(ctx) if err != nil { - return false, err + gm.l.Error("开启数据库事务失败。", zap.Error(err)) + return err } - - result, err := g.erasePark(&tx, &ctx, parkId) + if err := repository.GMRepository.DeleteMeterPoolings(ctx, tx, pId, mId); err != nil { + gm.l.Error("无法删除指定表记公摊关系。", zap.Error(err)) + tx.Rollback(ctx) + return err + } + err = tx.Commit(ctx) if err != nil { - return false, err + gm.l.Error("未能成功提交数据库事务。", zap.Error(err)) + tx.Rollback(ctx) + return err } - - err = tx.Commit() - if err != nil { - tx.Rollback() - return false, err - } - cache.AbolishRelation(fmt.Sprintf("park:%s", parkId)) - return result, nil + return nil } -// 从此处开始为删除用户相关的部分 - -func (g _GodModeService) DeleteUser(userId string) (bool, error) { +func (gm _GMService) DeleteMeters(pId string, mId []string) error { ctx, cancel := global.TimeoutContext() defer cancel() - - tx, err := global.DB.BeginTx(ctx, &sql.TxOptions{}) + tx, err := global.DB.Begin(ctx) if err != nil { - return false, err + gm.l.Error("开启数据库事务失败。", zap.Error(err)) + return err + } + if err := repository.GMRepository.DeleteMeterBinding(ctx, tx, pId, []string{}, mId); err != nil { + gm.l.Error("删除指定园区中的表计和商户的绑定关系", zap.Error(err)) + tx.Rollback(ctx) + return err + } + if err := repository.GMRepository.DeleteMeterPoolings(ctx, tx, pId, mId); err != nil { + gm.l.Error("无法删除指定表记公摊关系。", zap.Error(err)) + tx.Rollback(ctx) + return err + } + if err := repository.GMRepository.DeleteMeters(ctx, tx, pId, mId); err != nil { + gm.l.Error("删除指定园区中符合条件的表计。", zap.Error(err)) + tx.Rollback(ctx) + return err } - var parkIds = make([]string, 0) - err = tx.NewSelect().Model((*model.Park)(nil)). - Where("user_id = ?", userId). - WhereAllWithDeleted(). - Column("id"). - Scan(ctx, &parkIds) + err = tx.Commit(ctx) if err != nil { - tx.Rollback() - return false, err + gm.l.Error("未能成功提交数据库事务。", zap.Error(err)) + tx.Rollback(ctx) + return err } - - var result = true - for _, p := range parkIds { - r, err := g.erasePark(&tx, &ctx, p) - if err != nil { - return false, err - } - result = result && r - } - - // 删除用户服务计费数据。 - res, err := tx.NewDelete().Model((*model.UserCharge)(nil)). - Where("user_id = ?", userId). - Exec(ctx) - if err != nil { - tx.Rollback() - return false, err - } - if rows, err := res.RowsAffected(); err != nil { - tx.Rollback() - return false, err - } else { - result = result && (rows >= 0) - } - // 删除用户详细信息数据 - res, err = tx.NewDelete().Model((*model.UserDetail)(nil)). - Where("id = ?", userId). - ForceDelete(). - Exec(ctx) - if err != nil { - tx.Rollback() - return false, err - } - if rows, err := res.RowsAffected(); err != nil { - tx.Rollback() - return false, err - } else { - result = result && (rows >= 0) - } - // 删除用户基本索引数据 - res, err = tx.NewDelete().Model((*model.User)(nil)). - Where("id = ?", userId). - Exec(ctx) - if err != nil { - tx.Rollback() - return false, err - } - if rows, err := res.RowsAffected(); err != nil { - tx.Rollback() - return false, err - } else { - result = result && (rows >= 0) - } - - err = tx.Commit() - if err != nil { - tx.Rollback() - return false, err - } - cache.AbolishRelation(fmt.Sprintf("user:%s", userId)) - cache.AbolishRelation("user") - cache.AbolishRelation("park") - cache.AbolishRelation("report") - cache.AbolishRelation("charge") - return result, nil -} + return nil +} \ No newline at end of file diff --git a/service/maintenance_fee.go b/service/maintenance_fee.go deleted file mode 100644 index e4453d5..0000000 --- a/service/maintenance_fee.go +++ /dev/null @@ -1,295 +0,0 @@ -package service - -import ( - "electricity_bill_calc/cache" - "electricity_bill_calc/config" - "electricity_bill_calc/exceptions" - "electricity_bill_calc/global" - "electricity_bill_calc/logger" - "electricity_bill_calc/model" - "fmt" - - mapset "github.com/deckarep/golang-set/v2" - "github.com/google/uuid" - "github.com/samber/lo" - "github.com/shopspring/decimal" - "github.com/uptrace/bun" - "go.uber.org/zap" -) - -type _MaintenanceFeeService struct { - l *zap.Logger -} - -var MaintenanceFeeService = _MaintenanceFeeService{ - l: logger.Named("Service", "maintenance"), -} - -func (_MaintenanceFeeService) ListMaintenanceFees(pid []string, period string, requestPage int) ([]model.MaintenanceFee, int64, error) { - conditions := []string{fmt.Sprintf("%d", requestPage)} - conditions = append(conditions, pid...) - conditions = append(conditions, period) - if cachedTotal, err := cache.RetreiveCount("maintenance_fee", conditions...); cachedTotal != -1 && err == nil { - if fees, _ := cache.RetreiveSearch[[]model.MaintenanceFee]("maintenance_fee", conditions...); fees != nil { - return *fees, cachedTotal, nil - } - } - - var ( - fees = make([]model.MaintenanceFee, 0) - cond = global.DB.NewSelect().Model(&fees) - ) - if len(pid) > 0 { - cond = cond.Where("park_id in (?)", bun.In(pid)) - } else { - return make([]model.MaintenanceFee, 0), 0, exceptions.NewIllegalArgumentsError("必须给定所要请求的至少一个园区", "park_id") - } - if len(period) > 0 { - cond = cond.Where("period = ?", period) - } - - ctx, cancel := global.TimeoutContext() - defer cancel() - startItem := (requestPage - 1) * config.ServiceSettings.ItemsPageSize - total, err := cond.Order("period desc", "created_at desc"). - Limit(config.ServiceSettings.ItemsPageSize). - Offset(startItem). - ScanAndCount(ctx) - if err != nil { - return make([]model.MaintenanceFee, 0), 0, fmt.Errorf("附加费查询出现错误,%w", err) - } - relations := lo.Map(fees, func(f model.MaintenanceFee, _ int) string { - return fmt.Sprintf("maintenance_fee:%s", f.Id) - }) - relations = append(relations, "maintenance_fee", "park") - - cache.CacheCount(relations, "maintenance_fee", int64(total), conditions...) - cache.CacheSearch(fees, relations, "maintenance_fee", conditions...) - return fees, int64(total), nil -} - -func (_MaintenanceFeeService) CreateMaintenanceFeeRecord(fee model.MaintenanceFee) error { - ctx, cancel := global.TimeoutContext() - defer cancel() - - fee.Id = uuid.New().String() - fee.Enabled = true - _, err := global.DB.NewInsert().Model(&fee).Exec(ctx) - if err != nil { - return err - } - cache.AbolishRelation("maintenance_fee") - return nil -} - -func (_MaintenanceFeeService) ModifyMaintenanceFee(fee model.MaintenanceFee) error { - ctx, cancel := global.TimeoutContext() - defer cancel() - - res, err := global.DB.NewUpdate().Model(&fee). - WherePK(). - Column("fee", "memo"). - Exec(ctx) - if err != nil { - if rows, _ := res.RowsAffected(); rows == 0 { - return exceptions.NewNotFoundError("未能找到匹配的附加费记录。") - } else { - return err - } - } - cache.AbolishRelation("maintenance_fee") - cache.AbolishRelation(fmt.Sprintf("maintenance_fee:%s", fee.Id)) - return nil -} - -func (_MaintenanceFeeService) ChangeMaintenanceFeeState(fid string, state bool) error { - ctx, cancel := global.TimeoutContext() - defer cancel() - - res, err := global.DB.NewUpdate().Model((*model.MaintenanceFee)(nil)). - Where("id = ?", fid). - Set("enabled = ?", state). - Exec(ctx) - if err != nil { - if rows, err := res.RowsAffected(); rows == 0 { - return exceptions.NewNotFoundError("未能找到匹配的附加费记录。") - } else { - return err - } - } - cache.AbolishRelation("maintenance_fee") - cache.AbolishRelation(fmt.Sprintf("maintenance_fee:%s", fid)) - return nil -} - -func (_MaintenanceFeeService) DeleteMaintenanceFee(fid string) error { - ctx, cancel := global.TimeoutContext() - defer cancel() - - res, err := global.DB.NewDelete().Model((*model.MaintenanceFee)(nil)). - Where("id = ?", fid). - Exec(ctx) - if err != nil { - if rows, err := res.RowsAffected(); rows == 0 { - return exceptions.NewNotFoundError("未能找到匹配的附加费记录。") - } else { - return err - } - } - cache.AbolishRelation("maintenance_fee") - cache.AbolishRelation(fmt.Sprintf("maintenance_fee:%s", fid)) - return nil -} - -func (_MaintenanceFeeService) EnsureFeeBelongs(uid, mid string) (bool, error) { - if has, _ := cache.CheckExists("maintenance_fee", mid, uid); has { - return true, nil - } - - ctx, cancel := global.TimeoutContext() - defer cancel() - - parks := make([]model.Park, 0) - err := global.DB.NewSelect().Model(&parks). - Relation("MaintenanceFees"). - Where("p.user_id = ?", uid). - Scan(ctx) - if err != nil { - return false, err - } - exists := lo.Reduce(parks, func(acc bool, elem model.Park, _ int) bool { - for _, e := range elem.MaintenanceFees { - if e.Id == mid { - return acc || true - } - } - return acc || false - }, false) - if !exists { - return false, exceptions.NewNotFoundError("指定附加费所属园区未找到。") - } - cache.CacheExists([]string{fmt.Sprintf("maintenance_fee:%s", mid), "maintenance_fee", "park"}, "maintenance_fee", mid, uid) - return exists, nil -} - -type _FeeStat struct { - ParkId string - Period string - Total decimal.Decimal -} - -func (f _MaintenanceFeeService) QueryAdditionalCharges(uid, pid, period, keyword string, requestPage int) ([]model.AdditionalCharge, int64, error) { - var ( - conditions = []string{fmt.Sprintf("%d", requestPage)} - statFees = make([]_FeeStat, 0) - cond = global.DB.NewSelect(). - Model((*model.MaintenanceFee)(nil)). - Relation("Park", func(q *bun.SelectQuery) *bun.SelectQuery { - return q.ExcludeColumn("*") - }). - Relation("Park.Enterprise", func(q *bun.SelectQuery) *bun.SelectQuery { - return q.ExcludeColumn("*") - }). - Where("m.enabled = ?", true) - ) - if len(uid) > 0 { - cond = cond.Where("park__enterprise.id = ?", uid) - conditions = append(conditions, uid) - } else { - conditions = append(conditions, "_") - } - if len(pid) > 0 { - cond = cond.Where("park.id = ?", pid) - conditions = append(conditions, pid) - } else { - conditions = append(conditions, "_") - } - if len(period) > 0 { - cond = cond.Where("m.period = ?", period) - conditions = append(conditions, period) - } else { - conditions = append(conditions, "_") - } - if len(keyword) > 0 { - keywordCond := "%" + keyword + "%" - cond = cond.WhereGroup(" and ", func(q *bun.SelectQuery) *bun.SelectQuery { - return q.Where("park__enterprise.name like ?", keywordCond). - WhereOr("park__enterprise.abbr like ?", keywordCond). - WhereOr("park.name like ?", keywordCond). - WhereOr("park.abbr like ?", keywordCond). - WhereOr("park.address like ?", keywordCond) - }) - conditions = append(conditions, keyword) - } else { - conditions = append(conditions, "_") - } - - if cachedTotal, err := cache.RetreiveCount("additional_charge", conditions...); cachedTotal != -1 && err == nil { - if cachedData, _ := cache.RetreiveSearch[[]model.AdditionalCharge]("additional_charge", conditions...); cachedData != nil { - return *cachedData, cachedTotal, nil - } - } - ctx, cancel := global.TimeoutContext(24) - defer cancel() - - startItem := (requestPage - 1) * config.ServiceSettings.ItemsPageSize - - total, err := cond.ColumnExpr("sum(?) as total", bun.Ident("fee")). - Column("park_id", "period"). - Group("park_id", "period"). - Order("period desc"). - Limit(config.ServiceSettings.ItemsPageSize). - Offset(startItem). - ScanAndCount(ctx, &statFees) - if err != nil { - return make([]model.AdditionalCharge, 0), 0, fmt.Errorf("获取附加费统计信息出现错误,%w", err) - } - parkIds := lo.Reduce( - statFees, - func(acc mapset.Set[string], elem _FeeStat, _ int) mapset.Set[string] { - acc.Add(elem.ParkId) - return acc - }, - mapset.NewSet[string](), - ) - parks := make([]model.Park, 0) - if len(parkIds.ToSlice()) > 0 { - err = global.DB.NewSelect().Model(&parks).Relation("Enterprise"). - Where("p.id in (?)", bun.In(parkIds.ToSlice())). - Scan(ctx) - if err != nil { - return make([]model.AdditionalCharge, 0), 0, fmt.Errorf("获取园区信息出现错误,%w", err) - } - } - assembledStat := lo.Reduce( - statFees, - func(acc []model.AdditionalCharge, elem _FeeStat, _ int) []model.AdditionalCharge { - park, has := lo.Find(parks, func(p model.Park) bool { - return p.Id == elem.ParkId - }) - f.l.Debug("Park detection.", zap.Bool("has", has), zap.Any("park", park)) - if has { - if !park.Area.Valid || park.Area.Decimal.Equal(decimal.Zero) { - return acc - } - price := elem.Total.Div(park.Area.Decimal).RoundBank(8) - return append(acc, model.AdditionalCharge{ - ParkId: elem.ParkId, - Period: elem.Period, - Fee: elem.Total, - Price: price, - QuarterPrice: price.Div(decimal.NewFromInt(4)), - SemiAnnualPrice: price.Div(decimal.NewFromInt(2)), - Enterprise: model.FromUserDetail(*park.Enterprise), - Park: park, - }) - } else { - return acc - } - }, - make([]model.AdditionalCharge, 0), - ) - cache.CacheCount([]string{"maintenance_fee"}, "additional_charge", int64(total), conditions...) - cache.CacheSearch(assembledStat, []string{"maintenance_fee"}, "additional_charge", conditions...) - return assembledStat, int64(total), nil -} diff --git a/service/meter04kv.go b/service/meter04kv.go deleted file mode 100644 index 04c901a..0000000 --- a/service/meter04kv.go +++ /dev/null @@ -1,231 +0,0 @@ -package service - -import ( - "context" - "database/sql" - "electricity_bill_calc/cache" - "electricity_bill_calc/config" - "electricity_bill_calc/excel" - "electricity_bill_calc/global" - "electricity_bill_calc/logger" - "electricity_bill_calc/model" - "fmt" - "strconv" - - mapset "github.com/deckarep/golang-set/v2" - "github.com/samber/lo" - "github.com/uptrace/bun" - "go.uber.org/zap" -) - -type _Meter04kVService struct { - l *zap.Logger -} - -var Meter04kVService = _Meter04kVService{ - l: logger.Named("Service", "Meter04KV"), -} - -func (_Meter04kVService) ListMeterDetail(park, keyword string, page int) ([]model.Meter04KV, int64, error) { - var ( - condition = make([]string, 0) - meters = make([]model.Meter04KV, 0) - ) - cond := global.DB.NewSelect().Model(&meters). - Where("park_id = ?", park) - condition = append(condition, park, strconv.Itoa(page)) - if len(keyword) > 0 { - keywordCond := "%" + keyword + "%" - cond = cond.WhereGroup(" and ", func(q *bun.SelectQuery) *bun.SelectQuery { - return q.Where("address like ?", keywordCond). - WhereOr("code like ?", keywordCond). - WhereOr("customer_name like ?", keywordCond). - WhereOr("contact_name like ?", keywordCond). - WhereOr("contact_phone like ?", keywordCond) - }) - condition = append(condition, keyword) - } - if cachedTotal, err := cache.RetreiveCount("meter_04kv", condition...); cachedTotal != -1 && err == nil { - if cachedMeters, _ := cache.RetreiveSearch[[]model.Meter04KV]("meter_04kv", condition...); cachedMeters != nil { - return *cachedMeters, cachedTotal, nil - } - } - - ctx, cancel := global.TimeoutContext() - defer cancel() - startItem := (page - 1) * config.ServiceSettings.ItemsPageSize - total, err := cond. - Order("seq asc", "code asc"). - Limit(config.ServiceSettings.ItemsPageSize). - Offset(startItem). - ScanAndCount(ctx) - - relations := lo.Map(meters, func(m model.Meter04KV, _ int) string { - return fmt.Sprintf("meter_04kv:%s:%s", m.ParkId, m.Code) - }) - relations = append(relations, "meter_04kv", "park") - - cache.CacheCount(relations, "meter_04kv", int64(total), condition...) - cache.CacheSearch(meters, relations, "meter_04kv", condition...) - return meters, int64(total), err -} - -func (_Meter04kVService) Get04kVMeterDetail(park, code string) (*model.Meter04KV, error) { - if cachedMeter, _ := cache.RetreiveEntity[model.Meter04KV]("meter_04kv", fmt.Sprintf("%s_%s", park, code)); cachedMeter != nil { - return cachedMeter, nil - } - var meter = new(model.Meter04KV) - ctx, cancel := global.TimeoutContext() - defer cancel() - err := global.DB.NewSelect().Model(meter). - Where("code = ?", code). - Where("park_id = ?", park). - Scan(ctx) - if err != nil { - return nil, err - } - cache.CacheEntity(meter, []string{fmt.Sprintf("meter_04kv:%s:%s", park, code), "park"}, "meter_04kv", fmt.Sprintf("%s_%s", park, code)) - return meter, nil -} - -func (_Meter04kVService) insertNewMeter(tx *bun.Tx, ctx *context.Context, meter model.Meter04KV) error { - _, err := tx.NewInsert().Model(&meter).Exec(*ctx) - if err != nil { - tx.Rollback() - } - cache.AbolishRelation("meter_04kv") - return err -} - -func (_Meter04kVService) updateMeter(tx *bun.Tx, ctx *context.Context, meter model.Meter04KV) error { - _, err := tx.NewUpdate().Model(&meter). - Where("code = ?", meter.Code). - Where("park_id = ?", meter.ParkId). - Column("address", "customer_name", "contact_name", "contact_phone", "ratio", "seq", "public_meter", "enabled"). - Exec(*ctx) - if err != nil { - tx.Rollback() - } - cache.AbolishRelation(fmt.Sprintf("meter_04kv:%s:%s", meter.ParkId, meter.Code)) - return err -} - -func (m _Meter04kVService) CreateSingleMeter(meter model.Meter04KV) error { - ctx, cancel := global.TimeoutContext() - defer cancel() - tx, err := global.DB.BeginTx(ctx, &sql.TxOptions{}) - if err != nil { - return err - } - - err = m.insertNewMeter(&tx, &ctx, meter) - if err != nil { - return err - } - err = tx.Commit() - if err != nil { - tx.Rollback() - return err - } - cache.AbolishRelation("meter_04kv") - cache.AbolishRelation(fmt.Sprintf("meter_04kv:%s:%s", meter.ParkId, meter.Code)) - return nil -} - -func (m _Meter04kVService) UpdateSingleMeter(meter *model.Meter04KV) error { - ctx, cancel := global.TimeoutContext() - defer cancel() - tx, err := global.DB.BeginTx(ctx, &sql.TxOptions{}) - if err != nil { - return err - } - - err = m.updateMeter(&tx, &ctx, *meter) - if err != nil { - return err - } - err = tx.Commit() - if err != nil { - tx.Rollback() - return err - } - cache.AbolishRelation(fmt.Sprintf("meter_04kv:%s:%s", meter.ParkId, meter.Code)) - return nil -} - -func (_Meter04kVService) DuplicateMeterCodeValidate(meters []model.Meter04KV) []excel.ExcelAnalysisError { - errs := make([]excel.ExcelAnalysisError, 0) - for i := 0; i < len(meters); i++ { - for j := i + 1; j < len(meters); j++ { - if meters[j].Code == meters[i].Code { - errs = append(errs, excel.ExcelAnalysisError{Row: j + 1, Col: 0, Err: excel.AnalysisError{Err: fmt.Errorf("第 %d 行表计表号与第 %d 行表计表号重复!", j+1, i+1)}}) - } - } - } - return errs -} - -func (m _Meter04kVService) BatchCreateMeter(meters []model.Meter04KV) error { - parkIds := lo.Reduce(meters, func(acc mapset.Set[string], elem model.Meter04KV, index int) mapset.Set[string] { - acc.Add(elem.ParkId) - return acc - }, mapset.NewSet[string]()) - if parkIds.Cardinality() > 1 { - return fmt.Errorf("一次只能向同一个园区中添加0.4kV表计。") - } - parkId, _ := parkIds.Pop() - - ctx, cancel := global.TimeoutContext(120) - defer cancel() - - allMeterCodes := make([]string, 0) - err := global.DB.NewSelect().Model((*model.Meter04KV)(nil)). - Where("park_id = ?", parkId). - Column("code"). - Scan(ctx, &allMeterCodes) - if err != nil { - return err - } - meterCodes := mapset.NewSet(allMeterCodes...) - - tx, err := global.DB.BeginTx(ctx, &sql.TxOptions{}) - if err != nil { - return err - } - - var ( - updates = make([]model.Meter04KV, 0) - inserts = make([]model.Meter04KV, 0) - ) - for _, meter := range meters { - if meterCodes.Contains(meter.Code) { - updates = append(updates, meter) - } else { - inserts = append(inserts, meter) - } - } - if len(updates) > 0 { - _, err = tx.NewUpdate().Model(&updates). - Column("address", "customer_name", "contact_name", "contact_phone", "ratio", "seq", "public_meter", "enabled"). - Bulk(). - Exec(ctx) - if err != nil { - tx.Rollback() - return err - } - } - if len(inserts) > 0 { - _, err = tx.NewInsert().Model(&inserts).Exec(ctx) - if err != nil { - tx.Rollback() - return err - } - } - err = tx.Commit() - if err != nil { - tx.Rollback() - return err - } - cache.AbolishRelation("meter_04kv") - return nil -} diff --git a/service/park.go b/service/park.go deleted file mode 100644 index ab58527..0000000 --- a/service/park.go +++ /dev/null @@ -1,171 +0,0 @@ -package service - -import ( - "electricity_bill_calc/cache" - "electricity_bill_calc/exceptions" - "electricity_bill_calc/global" - "electricity_bill_calc/logger" - "electricity_bill_calc/model" - "fmt" - - "github.com/uptrace/bun" - "go.uber.org/zap" -) - -type _ParkService struct { - l *zap.Logger -} - -var ParkService = _ParkService{ - l: logger.Named("Service", "Park"), -} - -func (_ParkService) SaveNewPark(park model.Park) error { - ctx, cancel := global.TimeoutContext() - defer cancel() - _, err := global.DB.NewInsert().Model(&park).Exec(ctx) - if err != nil { - return err - } - cache.AbolishRelation("park") - return nil -} - -func (_ParkService) UpdateParkInfo(park *model.Park) error { - ctx, cancel := global.TimeoutContext() - defer cancel() - res, err := global.DB.NewUpdate().Model(park). - Where("id = ?", park.Id). - Where("user_id = ?", park.UserId). - Column("name", "abbr", "region", "area", "address", "contact", "phone", "capacity", "tenement_quantity", "category", "meter_04kv_type"). - Exec(ctx) - if err != nil { - if rows, _ := res.RowsAffected(); rows == 0 { - return exceptions.NewNotFoundError("未能找到符合条件的园区。") - } else { - return err - } - } - cache.AbolishRelation(fmt.Sprintf("park:%s", park.Id)) - return nil -} - -func (_ParkService) ChangeParkState(uid, pid string, state bool) error { - ctx, cancel := global.TimeoutContext() - defer cancel() - res, err := global.DB.NewUpdate().Model((*model.Park)(nil)). - Where("id = ?", pid). - Where("user_id = ?", uid). - Set("enabled = ?", state). - Exec(ctx) - if err != nil { - if rows, _ := res.RowsAffected(); rows == 0 { - return exceptions.NewNotFoundError("未能找到符合条件的园区。") - } else { - return err - } - } - cache.AbolishRelation(fmt.Sprintf("park:%s", pid)) - return nil -} - -func (_ParkService) DeletePark(uid, pid string) error { - ctx, cancel := global.TimeoutContext() - defer cancel() - res, err := global.DB.NewDelete().Model((*model.Park)(nil)). - Where("id = ?", pid). - Where("user_id = ?", uid). - Exec(ctx) - if err != nil { - if rows, _ := res.RowsAffected(); rows == 0 { - return exceptions.NewNotFoundError("未能找到符合条件的园区。") - } else { - return err - } - } - cache.AbolishRelation("park") - cache.AbolishRelation(fmt.Sprintf("park:%s", pid)) - return nil -} - -func (_ParkService) ListAllParkBelongsTo(uid, keyword string) ([]model.Park, error) { - if parks, _ := cache.RetreiveSearch[[]model.Park]("park", "belong", uid, keyword); parks != nil { - return *parks, nil - } - parks := make([]model.Park, 0) - cond := global.DB.NewSelect().Model(&parks). - Where("user_id = ?", uid) - if len(keyword) > 0 { - keywordCond := "%" + keyword + "%" - cond = cond.WhereGroup(" and ", func(q *bun.SelectQuery) *bun.SelectQuery { - return q.Where("name like ?", keywordCond). - WhereOr("abbr like ?", keywordCond). - WhereOr("address like ?", keywordCond). - WhereOr("contact like ?", keywordCond). - WhereOr("phone like ?", keywordCond) - }) - } - ctx, cancel := global.TimeoutContext() - defer cancel() - err := cond.Scan(ctx) - if err != nil { - return make([]model.Park, 0), err - } - relations := []string{"park"} - for _, p := range parks { - relations = append(relations, fmt.Sprintf("park:%s", p.Id)) - } - cache.CacheSearch(parks, relations, "park", "belong", uid, keyword) - return parks, nil -} - -func (_ParkService) FetchParkDetail(pid string) (*model.Park, error) { - if park, _ := cache.RetreiveEntity[model.Park]("park", pid); park != nil { - return park, nil - } - ctx, cancel := global.TimeoutContext() - defer cancel() - var park = new(model.Park) - err := global.DB.NewSelect().Model(park). - Where("id = ?", pid). - Scan(ctx) - if err != nil { - return nil, exceptions.NewNotFoundErrorFromError("未找到符合条件的园区记录。", err) - } - cache.CacheEntity(*park, []string{fmt.Sprintf("park:%s", pid)}, "park", pid) - return park, nil -} - -func (_ParkService) EnsurePark(uid, pid string) (bool, error) { - if has, _ := cache.CheckExists("park", pid, uid); has { - return has, nil - } - ctx, cancel := global.TimeoutContext() - defer cancel() - has, err := global.DB.NewSelect().Model((*model.Park)(nil)). - Where("id = ?", pid). - Where("user_id = ?", uid). - Exists(ctx) - if has { - cache.CacheExists([]string{fmt.Sprintf("park:%s", pid)}, "park", pid, uid) - } - return has, err -} - -func (_ParkService) AllParkIds(uid string) ([]string, error) { - if ids, _ := cache.RetreiveSearch[[]string]("park", "belong", uid); ids != nil { - return *ids, nil - } - ctx, cancel := global.TimeoutContext() - defer cancel() - var ids = make([]string, 0) - err := global.DB.NewSelect().Model((*model.Park)(nil)). - Where("user_id = ?", uid). - Column("id"). - Scan(ctx, &ids) - if err != nil { - return make([]string, 0), err - } - cache.CacheSearch(ids, []string{"park"}, "park", "belong", uid) - return ids, nil -} diff --git a/service/region.go b/service/region.go deleted file mode 100644 index 4e6f0ff..0000000 --- a/service/region.go +++ /dev/null @@ -1,70 +0,0 @@ -package service - -import ( - "electricity_bill_calc/cache" - "electricity_bill_calc/global" - "electricity_bill_calc/logger" - "electricity_bill_calc/model" - "fmt" - - "github.com/samber/lo" - "go.uber.org/zap" -) - -type _RegionService struct { - l *zap.Logger -} - -var RegionService = _RegionService{ - l: logger.Named("Service", "Region"), -} - -func (_RegionService) FetchSubRegions(parent string) ([]model.Region, error) { - ctx, cancel := global.TimeoutContext() - defer cancel() - if regions, _ := cache.RetreiveSearch[[]model.Region]("region", "parent", parent); regions != nil { - return *regions, nil - } - regions := make([]model.Region, 0) - err := global.DB.NewSelect().Model(®ions).Where("parent = ?", parent).Order("code asc").Scan(ctx) - if err != nil { - return make([]model.Region, 0), err - } - relationNames := lo.Map(regions, func(r model.Region, index int) string { - return fmt.Sprintf("region:%s", r.Code) - }) - cache.CacheSearch(regions, relationNames, "region", "parent", parent) - return regions, err -} - -func (r _RegionService) FetchAllParentRegions(code string) ([]model.Region, error) { - regions := make([]model.Region, 0) - region, err := r.fetchRegion(code) - if err != nil { - return regions, err - } - regions = append(regions, *region) - for region.Level > 1 { - region, err = r.fetchRegion(region.Parent) - if err != nil { - return make([]model.Region, 0), nil - } - regions = append(regions, *region) - } - return regions, nil -} - -func (_RegionService) fetchRegion(code string) (*model.Region, error) { - ctx, cancel := global.TimeoutContext() - defer cancel() - if cachedRegion, _ := cache.RetreiveSearch[model.Region]("region", code); cachedRegion != nil { - return cachedRegion, nil - } - region := new(model.Region) - err := global.DB.NewSelect().Model(region).Where("code = ?", code).Scan(ctx) - if err != nil { - relationName := fmt.Sprintf("region:%s", code) - cache.CacheSearch(region, []string{relationName}, "region", code) - } - return region, err -} diff --git a/service/statistics.go b/service/statistics.go index d500881..7284984 100644 --- a/service/statistics.go +++ b/service/statistics.go @@ -1,94 +1,74 @@ package service import ( - "electricity_bill_calc/cache" "electricity_bill_calc/global" "electricity_bill_calc/logger" "electricity_bill_calc/model" - - "github.com/samber/lo" - "github.com/uptrace/bun" + "github.com/doug-martin/goqu/v9" + "github.com/georgysavva/scany/v2/pgxscan" "go.uber.org/zap" ) type _StatisticsService struct { - l *zap.Logger + l *zap.Logger + ss goqu.DialectWrapper } var StatisticsService = _StatisticsService{ - l: logger.Named("Service", "Stat"), + logger.Named("Service", "Stat"), + goqu.Dialect("postgres"), } -func (_StatisticsService) EnabledEnterprises() (int64, error) { - if cachedCount, err := cache.RetreiveCount("enabled_ent"); cachedCount != -1 && err == nil { - return cachedCount, nil - } +//用于统计企业用户数量 +func (ss _StatisticsService) EnabledEnterprises() (int64, error) { + ss.l.Info("开始统计企业数量。") ctx, cancel := global.TimeoutContext() defer cancel() - c, err := global.DB.NewSelect().Model((*model.User)(nil)). - Where("type = ?", model.USER_TYPE_ENT). - Where("enabled = ?", true). - Count(ctx) - if err == nil { - cache.CacheCount([]string{"user"}, "enabled_ent", int64(c)) - } - return int64(c), err -} + UserCountQuery, UserCountQueryArgs, _ := ss.ss. + From(goqu.T("user")). + Where(goqu.I("type").Eq(model.USER_TYPE_ENT)). + Where(goqu.I("enabled").Eq(true)). + Select(goqu.COUNT("*")).ToSQL() -func (_StatisticsService) EnabledParks(userIds ...string) (int64, error) { - if cachedParks, err := cache.RetreiveCount("enabled_parks", userIds...); cachedParks != -1 && err == nil { - return cachedParks, nil - } - ctx, cancel := global.TimeoutContext() - defer cancel() - - query := global.DB.NewSelect().Model((*model.Park)(nil)). - Where("enabled = ?", true) - if len(userIds) > 0 { - query = query.Where("user_id in (?)", bun.In(userIds)) - } - c, err := query.Count(ctx) - if err == nil { - cache.CacheCount([]string{"user", "park"}, "enabled_parks", int64(c), userIds...) - } - return int64(c), err -} - -func (_StatisticsService) ParksNewestState(userIds ...string) ([]model.ParkPeriodStatistics, error) { - if cachedState, _ := cache.RetreiveSearch[[]model.ParkPeriodStatistics]("park_period_stat", userIds...); cachedState != nil { - return *cachedState, nil - } - ctx, cancel := global.TimeoutContext() - defer cancel() - - query := global.DB.NewSelect().Model((*model.Report)(nil)). - Relation("Park", func(q *bun.SelectQuery) *bun.SelectQuery { - return q.Column("id", "name") - }). - Where("park.enabled = ?", true). - Where("r.published = ?", true) - if len(userIds) > 0 { - query = query.Where("park.user_id in (?)", bun.In(userIds)) - } - parks := make([]model.ParkPeriodStatistics, 0) - groupedParks := make(map[string]model.ParkPeriodStatistics, 0) - err := query.Column("period").Scan(ctx, &parks) + var c int64 + err := pgxscan.Get(ctx, global.DB, &c, UserCountQuery, UserCountQueryArgs...) if err != nil { - return make([]model.ParkPeriodStatistics, 0), err + ss.l.Error("统计企业数量出错", zap.Error(err)) + return 0, err } - for _, p := range parks { - if c, ok := groupedParks[p.Id]; ok { - if c.Period != nil && p.Period != nil && p.Period.After(c.Period.Time) { - groupedParks[p.Id] = p - } - if c.Period == nil && p.Period != nil { - groupedParks[p.Id] = p - } - } else { - groupedParks[p.Id] = p - } - } - cache.CacheSearch(lo.Values(groupedParks), []string{"user", "park"}, "park_period_stat", userIds...) - return lo.Values(groupedParks), nil + return c, nil +} + +//用于统计园区数量 +func (ss _StatisticsService) EnabledParks(userIds ...string) (int64, error) { + ss.l.Info("开始统计园区数量", zap.Strings("userId", userIds)) + ctx, cancel := global.TimeoutContext() + defer cancel() + + ParkCountQuery := ss.ss. + From(goqu.T("park")). + Where(goqu.I("enabled").Eq(true)) + + if len(userIds) > 0 { + ParkCountQuery = ParkCountQuery.Where(goqu.I("user_id").In(userIds)) + } + + ParkCountQuerySql, ParkCountQueryArgs, _ := ParkCountQuery.Select(goqu.COUNT("*")).ToSQL() + + var c int64 + err := pgxscan.Get(ctx, global.DB, &c, ParkCountQuerySql, ParkCountQueryArgs...) + if err != nil { + ss.l.Error("园区数量统计错误", zap.Error(err)) + return 0, err + } + + return c, nil +} + +//用户统计报表 +func (ss _StatisticsService) ParkNewestState(userIds ...string) ([]model.ParkPeriodStatistics, error) { + //TODO: 2023.07.26 报表数据库结构改变,此处逻辑复杂放在最后处理 + //return nil,errors.New("还未处理逻辑") + return []model.ParkPeriodStatistics{}, nil } diff --git a/service/user.go b/service/user.go index a81dc3f..08824b9 100644 --- a/service/user.go +++ b/service/user.go @@ -80,7 +80,9 @@ func (us _UserService) ProcessEnterpriseUserLogin(username, password string) (*m us.log.Error("处理企业用户登录失败。", zap.String("username", username), zap.Error(err)) return nil, err } - token, _ := uuid.NewRandom() + + token, _ := uuid.NewRandom() //生成uuid作为会话的token使用 + userSession := &model.Session{ Uid: user.Id, Name: user.Username, diff --git a/service/withdraw.go b/service/withdraw.go index dfb5066..caa3f09 100644 --- a/service/withdraw.go +++ b/service/withdraw.go @@ -1,160 +1,39 @@ package service import ( - "electricity_bill_calc/cache" - "electricity_bill_calc/config" - "electricity_bill_calc/exceptions" "electricity_bill_calc/global" "electricity_bill_calc/logger" "electricity_bill_calc/model" - "fmt" - "strconv" - "time" - - "github.com/samber/lo" - "github.com/uptrace/bun" + "github.com/doug-martin/goqu/v9" + "github.com/georgysavva/scany/v2/pgxscan" "go.uber.org/zap" ) type _WithdrawService struct { - l *zap.Logger + log *zap.Logger + ds goqu.DialectWrapper } var WithdrawService = _WithdrawService{ - l: logger.Named("Service", "Withdraw"), + logger.Named("Service", "Withdraw"), + goqu.Dialect("postgres"), } -func (_WithdrawService) ApplyWithdraw(reportId string) (bool, error) { +func (wd _WithdrawService) AuditWaits() (int64, error) { + wd.log.Info("获取当前系统中待审核的内容数量。") ctx, cancel := global.TimeoutContext() defer cancel() - var report = new(model.Report) - err := global.DB.NewSelect().Model(report). - Where("id = ?", reportId). - Scan(ctx) - if err != nil || report == nil { - return false, exceptions.NewNotFoundErrorFromError("指定报表未能找到", err) - } - if !report.Published { - return false, exceptions.NewImproperOperateError("指定报表尚未发布。") - } - var maxPublished time.Time - err = global.DB.NewSelect().Model((*model.Report)(nil)). - ColumnExpr("max(period)"). - Where("park_id = ?", report.ParkId). - Where("published = ?", true). - Scan(ctx, &maxPublished) + CountWithdrawQuery, CountWithdrawQueryArgs, _ := wd.ds. + From(goqu.T("report")). + Where(goqu.I("withdraw").Eq(model.REPORT_WITHDRAW_APPLYING)). + Select(goqu.COUNT("*")).ToSQL() + + var total int64 + err := pgxscan.Get(ctx, global.DB, &total, CountWithdrawQuery,CountWithdrawQueryArgs...) if err != nil { - return false, exceptions.NewNotFoundError("未能找到匹配的系列报表。") + wd.log.Error("获取当前系统中待审核的内容数量出错。") + return 0,err } - if !report.Period.Equal(maxPublished) { - return false, exceptions.NewImproperOperateError("申请撤回的报表必须是最新已发布的报表。") - } - - report.Withdraw = model.REPORT_WITHDRAW_APPLIED - report.LastWithdrawAppliedAt = lo.ToPtr(time.Now()) - _, err = global.DB.NewUpdate().Model(report). - WherePK(). - Column("withdraw", "last_withdraw_applied_at"). - Exec(ctx) - if err != nil { - return false, err - } - cache.AbolishRelation("withdraw_stat") - cache.AbolishRelation(fmt.Sprintf("report:%s", reportId)) - cache.AbolishRelation(fmt.Sprintf("publicity:%s", reportId)) - return true, nil -} - -func (_WithdrawService) FetchPagedWithdrawApplies(page int, keyword string) ([]model.JoinedReportForWithdraw, int64, error) { - var ( - conditions = make([]string, 0) - reports = make([]model.Report, 0) - cond = global.DB.NewSelect().Model(&reports). - Relation("Park").Relation("Park.Enterprise") - ) - conditions = append(conditions, strconv.Itoa(int(model.REPORT_WITHDRAW_APPLIED)), strconv.Itoa(page)) - cond = cond.Where("r.withdraw = ?", model.REPORT_WITHDRAW_APPLIED) - if len(keyword) > 0 { - keywordCond := "%" + keyword + "%" - cond = cond.WhereGroup(" and ", func(q *bun.SelectQuery) *bun.SelectQuery { - return q.Where("p.name like ?", keywordCond). - WhereOr("p.abbr like ?", keywordCond). - WhereOr("d.name like ?", keywordCond). - WhereOr("d.abbr like ?", keywordCond) - }) - conditions = append(conditions, keyword) - } - if cachedTotal, err := cache.RetreiveCount("join_report_for_withdraw", conditions...); cachedTotal != -1 && err == nil { - if cachedReports, _ := cache.RetreiveSearch[[]model.JoinedReportForWithdraw]("join_user_detail", conditions...); cachedReports != nil { - return *cachedReports, cachedTotal, err - } - } - - ctx, cancel := global.TimeoutContext() - defer cancel() - startItem := (page - 1) * config.ServiceSettings.ItemsPageSize - total, err := cond.Limit(config.ServiceSettings.ItemsPageSize). - Offset(startItem). - ScanAndCount(ctx) - - var ( - joinedReports = make([]model.JoinedReportForWithdraw, 0) - relations = []string{"report", "park"} - ) - for _, r := range reports { - joinedReports = append(joinedReports, model.JoinedReportForWithdraw{ - Report: r, - Park: model.FromPark(*r.Park), - User: model.FromUserDetail(*r.Park.Enterprise), - }) - relations = append(relations, fmt.Sprintf("report:%s", r.Id), fmt.Sprintf("publicity:%s", r.Id)) - } - - cache.CacheCount(relations, "join_report_for_withdraw", int64(total), conditions...) - cache.CacheSearch(joinedReports, relations, "join_report_for_withdraw", conditions...) - return joinedReports, int64(total), err -} - -func (_WithdrawService) AuditWithdraw(reportId string, granted bool) error { - ctx, cancel := global.TimeoutContext() - defer cancel() - - var report = new(model.Report) - err := global.DB.NewSelect().Model(report). - Where("id = ?", reportId). - Scan(ctx) - if err != nil { - return exceptions.NewNotFoundErrorFromError("指定公示报表未找到。", err) - } - report.Withdraw = lo.If(granted, model.REPORT_WITHDRAW_GRANTED).Else(model.REPORT_WITHDRAW_DENIED) - report.LastWithdrawAuditAt = lo.ToPtr(time.Now()) - if granted { - report.Published = false - } - _, err = global.DB.NewUpdate().Model(report). - WherePK(). - Column("withdraw", "last_withdraw_audit_at", "published"). - Exec(ctx) - if err == nil { - cache.AbolishRelation("withdraw_stat") - cache.AbolishRelation(fmt.Sprintf("report:%s", reportId)) - } - return err -} - -func (_WithdrawService) AuditWaits() (int64, error) { - if cachedWaits, err := cache.RetreiveCount("withdraw_waits"); cachedWaits != -1 && err == nil { - return cachedWaits, nil - } - ctx, cancel := global.TimeoutContext() - defer cancel() - - total, err := global.DB.NewSelect().Model((*model.Report)(nil)). - Where("withdraw = ?", model.REPORT_WITHDRAW_APPLIED). - Count(ctx) - if err == nil { - cache.CacheCount([]string{"withdraw_stat"}, "withdraw_waits", int64(total)) - } - return int64(total), err + return total,nil } diff --git a/settings.yaml b/settings.yaml index 4a0e548..fb5ec11 100644 --- a/settings.yaml +++ b/settings.yaml @@ -1,8 +1,8 @@ Database: User: electricity Pass: nLgxPO5s8gK2tR0OL0Q - Host: postgres - Port: 5432 + Host: 39.105.39.8 + Port: 9432 DB: electricity MaxIdleConns: 0 MaxOpenConns: 20 @@ -12,12 +12,16 @@ Server: ReadTimeout: 60 WriteTimeout: 60 Redis: - Host: redis + Host: 192.168.88.129 Port: 6379 - Password: TmFRS0w6BIrAPA1Raj + Password: 123456 DB: 1 Service: MaxSessionLife: 2h ItemsPageSize: 20 CacheLifeTime: 5m HostSerial: 5 + +BaselineLineLossRatio: + Base: 基准线损率 + diff --git a/tools/time.go b/tools/time.go deleted file mode 100644 index c69b0fb..0000000 --- a/tools/time.go +++ /dev/null @@ -1,16 +0,0 @@ -package tools - -import ( - "time" -) - -func DifferenceInMonth(t1, t2 time.Time) int { - var differYear, differMonth int - differYear = t1.Year() - t2.Year() - differMonth = int(t1.Month() - t2.Month()) - return differYear*12 + differMonth -} - -func IsNextMonth(origin, t time.Time) bool { - return DifferenceInMonth(t, origin) == 1 -} diff --git a/tools/utils.go b/tools/utils.go index 6312dd5..10411d8 100644 --- a/tools/utils.go +++ b/tools/utils.go @@ -1,9 +1,11 @@ package tools import ( + "database/sql" "encoding/json" "fmt" "strings" + "time" "github.com/mozillazg/go-pinyin" "github.com/samber/lo" @@ -144,3 +146,26 @@ func NullDecimalToString(d decimal.NullDecimal, precision ...int32) *string { } return lo.ToPtr(d.Decimal.StringFixedBank(precision[0])) } + +// 将sql.NullTime转换为*string +func NullTime2PointerString(nullTime sql.NullTime) *string { + var strPtr *string + if nullTime.Valid { + str := nullTime.Time.String() + strPtr = &str + return strPtr + } else { + strPtr = nil + return strPtr + } +} + +// 该方法用于将时间解析为字符串指针 +func TimeToStringPtr(t *time.Time) *string { + if t == nil { + return nil + } + + timeStr := t.Format("2006-01-02 15:04:05") + return &timeStr +} diff --git a/vo/withdraw.go b/vo/withdraw.go new file mode 100644 index 0000000..1aa6019 --- /dev/null +++ b/vo/withdraw.go @@ -0,0 +1,6 @@ +package vo + +//用于接收审核报表的参数 +type ReviewWithdraw struct { + Audit bool `json:"audit"` +}