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/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/modules.xml b/.idea/modules.xml
index 95c0f54..4ea8900 100644
--- a/.idea/modules.xml
+++ b/.idea/modules.xml
@@ -2,7 +2,11 @@
+<<<<<<< HEAD
+=======
+
+>>>>>>> 8163fd99ee4261a308318b55be08e30d123776e3
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
index 94a25f7..828619c 100644
--- a/.idea/vcs.xml
+++ b/.idea/vcs.xml
@@ -1,6 +1,10 @@
+<<<<<<< HEAD
+=======
+
+>>>>>>> 8163fd99ee4261a308318b55be08e30d123776e3
\ No newline at end of file
diff --git a/_1.gitignore b/_1.gitignore
new file mode 100644
index 0000000..45e75ee
--- /dev/null
+++ b/_1.gitignore
@@ -0,0 +1,183 @@
+# If you prefer the allow list template instead of the deny list, see community template:
+# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
+#
+# Binaries for programs and plugins
+*.exe
+*.exe~
+*.dll
+*.so
+*.dylib
+
+# Test binary, built with `go test -c`
+*.test
+
+# Output of the go coverage tool, specifically when used with LiteIDE
+*.out
+
+# Dependency directories (remove the comment below to include it)
+# vendor/
+
+# Go workspace file
+go.work
+
+# Windows thumbnail cache files
+Thumbs.db
+Thumbs.db:encryptable
+ehthumbs.db
+ehthumbs_vista.db
+
+# Dump file
+*.stackdump
+
+# Folder config file
+[Dd]esktop.ini
+
+# Recycle Bin used on file shares
+$RECYCLE.BIN/
+
+# Windows Installer files
+*.cab
+*.msi
+*.msix
+*.msm
+*.msp
+
+# Windows shortcuts
+*.lnk
+
+# General
+.DS_Store
+.AppleDouble
+.LSOverride
+
+# Icon must end with two \r
+Icon
+
+# Thumbnails
+._*
+
+# Files that might appear in the root of a volume
+.DocumentRevisions-V100
+.fseventsd
+.Spotlight-V100
+.TemporaryItems
+.Trashes
+.VolumeIcon.icns
+.com.apple.timemachine.donotpresent
+
+# Directories potentially created on remote AFP share
+.AppleDB
+.AppleDesktop
+Network Trash Folder
+Temporary Items
+.apdisk
+
+*~
+
+# temporary files which can be created if a process still has a handle open of a deleted file
+.fuse_hidden*
+
+# KDE directory preferences
+.directory
+
+# Linux trash folder which might appear on any partition or disk
+.Trash-*
+
+# .nfs files are created when an open file is removed but is still being accessed
+.nfs*
+
+.vscode/*
+!.vscode/settings.json
+!.vscode/tasks.json
+!.vscode/launch.json
+!.vscode/extensions.json
+!.vscode/*.code-snippets
+
+# Local History for Visual Studio Code
+.history/
+
+# Built Visual Studio Code Extensions
+*.vsix
+
+# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
+# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
+
+# User-specific stuff
+.idea/**/workspace.xml
+.idea/**/tasks.xml
+.idea/**/usage.statistics.xml
+.idea/**/dictionaries
+.idea/**/shelf
+
+# AWS User-specific
+.idea/**/aws.xml
+
+# Generated files
+.idea/**/contentModel.xml
+
+# Sensitive or high-churn files
+.idea/**/dataSources/
+.idea/**/dataSources.ids
+.idea/**/dataSources.local.xml
+.idea/**/sqlDataSources.xml
+.idea/**/dynamic.xml
+.idea/**/uiDesigner.xml
+.idea/**/dbnavigator.xml
+
+# Gradle
+.idea/**/gradle.xml
+.idea/**/libraries
+
+# Gradle and Maven with auto-import
+# When using Gradle or Maven with auto-import, you should exclude module files,
+# since they will be recreated, and may cause churn. Uncomment if using
+# auto-import.
+# .idea/artifacts
+# .idea/compiler.xml
+# .idea/jarRepositories.xml
+# .idea/modules.xml
+# .idea/*.iml
+# .idea/modules
+# *.iml
+# *.ipr
+
+# CMake
+cmake-build-*/
+
+# Mongo Explorer plugin
+.idea/**/mongoSettings.xml
+
+# File-based project format
+*.iws
+
+# IntelliJ
+out/
+
+# mpeltonen/sbt-idea plugin
+.idea_modules/
+
+# JIRA plugin
+atlassian-ide-plugin.xml
+
+# Cursive Clojure plugin
+.idea/replstate.xml
+
+# SonarLint plugin
+.idea/sonarlint/
+
+# Crashlytics plugin (for Android Studio and IntelliJ)
+com_crashlytics_export_strings.xml
+crashlytics.properties
+crashlytics-build.properties
+fabric.properties
+
+# Editor-based Rest Client
+.idea/httpRequests
+
+# Android studio 3.1+ serialized cache file
+.idea/caches/build_file_checksums.ser
+
+# Block sensitive configuration files
+settings.local.yaml
+log/
+__debug_bin
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/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
new file mode 100644
index 0000000..db1a426
--- /dev/null
+++ b/controller/god_mode.go
@@ -0,0 +1,132 @@
+package controller
+
+import (
+ "electricity_bill_calc/logger"
+ "electricity_bill_calc/response"
+ "electricity_bill_calc/security"
+ "electricity_bill_calc/service"
+ "errors"
+ "github.com/gofiber/fiber/v2"
+ "go.uber.org/zap"
+ "net/http"
+)
+
+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 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)
+ }
+ return result
+}
+
+func deleteTenement(c *fiber.Ctx) error {
+ park := c.Query("park", "")
+ tenements := getQueryValues(c, "tenements")
+ result := response.NewResult(c)
+ GmLog.Info("[天神模式]删除指定园区中的商户", zap.String("park", park), zap.Strings("tenements", tenements))
+
+ err := service.GMService.DeleteTenements(park, tenements)
+ if err != nil {
+ GmLog.Error("[天神模式]删除指定园区中的商户失败", zap.Error(err))
+ return result.Error(500, err.Error())
+ }
+
+ return result.Success("指定商户已经删除。")
+}
+
+func deletePark(c *fiber.Ctx) error {
+ parks := getQueryValues(c, "parks")
+ result := response.NewResult(c)
+ 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 {
+ GmLog.Error("[天神模式]删除指定园区失败", zap.Error(err))
+ return result.Error(500, err.Error())
+ }
+ return result.Success("指定园区已经删除。")
+}
+
+func deleteReports(c *fiber.Ctx) error {
+ pid := c.Query("park")
+ reports := getQueryValues(c, "reports")
+ result := response.NewResult(c)
+ GmLog.Info("[天神模式]删除符合条件的报表。", zap.Strings("reports", reports))
+
+ err := service.GMService.DeleteReports(pid, reports)
+ if err != nil {
+ GmLog.Error("[天神模式]删除指定园区中的报表失败。", zap.Error(err))
+ return result.Error(500, err.Error())
+ }
+ return result.Success("指定报表已经删除。")
+}
+
+func deleteEnterprise(c *fiber.Ctx) error {
+ uid := c.Query("uid")
+ result := response.NewResult(c)
+ GmLog.Info("[天神模式]删除指定企业用户", zap.String("uid", uid))
+
+ err := service.GMService.DeleteEnterprises(uid)
+ if err != nil {
+ GmLog.Error("[天神模式]删除指定企业用户失败", zap.Error(err))
+ return result.Error(500, "删除指定企业用户失败。")
+ }
+ return result.Success("指定企业用户已经删除。")
+}
+
+func deleteTenementMeterRelations(c *fiber.Ctx) error {
+ result := response.NewResult(c)
+ 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())
+ }
+ return result.Success("删除成功")
+}
+
+func deleteMeterPoolingRelations(c *fiber.Ctx) error {
+ result := response.NewResult(c)
+ 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, "删除指定园区中的表计公摊关系失败。")
+ }
+ return result.Success("指定表计公摊关系已经删除。")
+
+}
+
+func deleteMeters(c *fiber.Ctx) error {
+ result := response.NewResult(c)
+ 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, "删除指定园区中的表计失败。")
+ }
+ return result.Success("指定表计已经删除。")
+}
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
new file mode 100644
index 0000000..fd0555c
--- /dev/null
+++ b/controller/statistics.go
@@ -0,0 +1,85 @@
+package controller
+
+import (
+ "electricity_bill_calc/logger"
+ "electricity_bill_calc/model"
+ "electricity_bill_calc/response"
+ "electricity_bill_calc/security"
+ "electricity_bill_calc/service"
+ "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.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.Success("已经获取到指定的统计信息",
+ fiber.Map{"withdraw": amount})
+}
+
+func statReports(c *fiber.Ctx) error {
+ result := response.NewResult(c)
+ session, err := _retreiveSession(c)
+ if err != nil {
+ return result.Unauthorized(err.Error())
+ }
+ var (
+ enterprises int64 = 0
+ parks int64 = 0
+ reports []model.ParkPeriodStatistics
+ )
+ 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())
+ }
+ //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())
+ }
+ //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.Success("已经完成园区报告的统计。", fiber.Map{
+ "statistics": fiber.Map{
+ "enterprises": enterprises,
+ "parks": parks,
+ "reports": reports,
+ },
+ })
+}
diff --git a/controller/sync.go b/controller/sync.go
new file mode 100644
index 0000000..dd95206
--- /dev/null
+++ b/controller/sync.go
@@ -0,0 +1,121 @@
+package controller
+
+import (
+ "electricity_bill_calc/logger"
+ "electricity_bill_calc/model"
+ "electricity_bill_calc/repository"
+ "electricity_bill_calc/response"
+ "electricity_bill_calc/security"
+ "electricity_bill_calc/service"
+ "electricity_bill_calc/tools"
+ "electricity_bill_calc/vo"
+ "fmt"
+ "github.com/gofiber/fiber/v2"
+ "go.uber.org/zap"
+)
+
+var synchronizeLog = logger.Named("Handler", "Synchronize")
+
+func InitializeSynchronizeHandlers(router *fiber.App) {
+ router.Get("/synchronize/task", security.EnterpriseAuthorize, searchSynchronizeSchedules)
+ router.Get("/synchronize/configuration", security.EnterpriseAuthorize, getSynchronizeConfiguration)
+ router.Post("/synchronize/configuration", security.EnterpriseAuthorize, recordsynchronizeConfiguration)
+}
+
+// 查询当前平台中符合查询条件的同步任务,企业用户无论传入什么用户ID条件,都仅能看到自己的同步任务
+func searchSynchronizeSchedules(c *fiber.Ctx) error {
+ result := response.NewResult(c)
+ session, err := _retreiveSession(c)
+ if err != nil {
+ synchronizeLog.Error("查询同步任务失败,未能获取当前用户会话信息", zap.Error(err))
+ return result.Unauthorized("未能获取当前用户会话信息。")
+ }
+ parkId := tools.EmptyToNil(c.Params("park"))
+ if parkId != nil && len(*parkId) > 0 {
+ if pass, err := checkParkBelongs(*parkId, reportLog, c, &result); !pass {
+ return err
+ }
+ }
+ userId := tools.EmptyToNil(c.Params("user"))
+ keyword := tools.EmptyToNil(c.Query("keyword"))
+ page := c.QueryInt("page", 1)
+ synchronizeLog.Info("查询当前平台中符合查询条件的同步任务。", zap.String("Ent", session.Uid), zap.Stringp("Park", parkId))
+ schedules, total, err := repository.SynchronizeRepository.SearchSynchronizeSchedules(userId, parkId, uint(page), keyword)
+ if err != nil {
+ reportLog.Error("无法获取同步任务", zap.Error(err))
+ return result.Error(fiber.StatusInternalServerError, "无法获取同步任务")
+ }
+ return result.Success(
+ " ",
+ response.NewPagedResponse(page, total).ToMap(),
+ fiber.Map{"tasks": schedules},
+ )
+}
+
+// 获取指定的同步任务配置
+func getSynchronizeConfiguration(c *fiber.Ctx) error {
+ result := response.NewResult(c)
+ parkId := c.Query("park")
+ userId := c.Query("user")
+ session, err := _retreiveSession(c)
+ if err != nil {
+ reportLog.Error("无法获取当前用户的会话信息", zap.Error(err))
+ return result.Unauthorized("无法获取当前用户的会话信息。")
+ }
+ var user_id string
+ if session.Type == model.USER_TYPE_ENT {
+ user_id = session.Uid
+ } else {
+ if userId != "" {
+ user_id = userId
+ } else {
+ return result.NotAccept(fmt.Sprintf("必须指定要记录同步任务的用户,%s", err.Error()))
+ }
+ }
+ fmt.Println("pppppppppppppppppppppppppppp", parkId, len(parkId))
+ if parkId == "" {
+ return result.NotAccept("必须指定要获取同步任务的园区。")
+ }
+
+ fmt.Printf(user_id)
+ configurations, err := repository.SynchronizeRepository.RetrieveSynchronizeConfiguration(user_id, parkId)
+ if err != nil {
+ reportLog.Error("无法获取同步任务", zap.Error(err))
+ return result.Error(fiber.StatusInternalServerError, "无法获取同步任务")
+ }
+ return result.Success(
+ " 123",
+ fiber.Map{"setup": configurations},
+ )
+}
+func recordsynchronizeConfiguration(c *fiber.Ctx) error {
+ userId := c.Query("user")
+ synchronizeLog.Info("记录一个新的同步任务配置", zap.String("user id", userId))
+ session, err := _retreiveSession(c)
+ result := response.NewResult(c)
+ if err != nil {
+ reportLog.Error("无法获取当前用户的会话信息", zap.Error(err))
+ return result.Unauthorized("无法获取当前用户的会话信息。")
+ }
+ var Form vo.SynchronizeConfigurationCreateForm
+ if err := c.BodyParser(&Form); err != nil {
+ meterLog.Error("无法更新同步配置,无法解析表计更新表单", zap.Error(err))
+ return result.NotAccept(err.Error())
+ }
+ var user_id string
+ if session.Type == model.USER_TYPE_ENT {
+ user_id = session.Uid
+ } else {
+ if userId != "" {
+ user_id = userId
+ } else {
+ return result.NotAccept(fmt.Sprintf("必须指定更新同步任务的用户,%s", err.Error()))
+ }
+ }
+ //configurations, err := repository.SynchronizeRepository.CreateSynchronizeConfiguration
+ if err := service.SynchronizeService.CreateSynchronizeConfiguration(user_id, &Form); err != nil {
+ synchronizeLog.Error("无法更新同步配置", zap.Error(err))
+ return result.NotAccept(err.Error())
+ }
+ return result.Success("更新完成。")
+}
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
new file mode 100644
index 0000000..6a41d72
--- /dev/null
+++ b/controller/withdraw.go
@@ -0,0 +1,135 @@
+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", "Withdraw")
+
+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 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/tenement.go b/excel/tenement.go
new file mode 100644
index 0000000..25137b8
--- /dev/null
+++ b/excel/tenement.go
@@ -0,0 +1,22 @@
+package excel
+
+var tenementRecognizers = []*ColumnRecognizer{
+ {Pattern: [][]string{{"商户全称"}}, Tag: "fullName", MatchIndex: -1},
+ {Pattern: [][]string{{"联系地址"}}, Tag: "address", MatchIndex: -1},
+ {Pattern: [][]string{{"入驻时间"}}, Tag: "movedInAt", MatchIndex: -1},
+ {Pattern: [][]string{{"商铺名称"}}, Tag: "shortName", MatchIndex: -1},
+ {Pattern: [][]string{{"联系人"}}, Tag: "contactName", MatchIndex: -1},
+ {Pattern: [][]string{{"电话"}}, Tag: "contactPhone", MatchIndex: -1},
+ {Pattern: [][]string{{"USCI"}}, Tag: "usci", MatchIndex: -1},
+ {Pattern: [][]string{{"开票地址"}}, Tag: "invoiceAddress", MatchIndex: -1},
+ {Pattern: [][]string{{"账号"}}, Tag: "account", MatchIndex: -1},
+ {Pattern: [][]string{{"开户行"}}, Tag: "bank", MatchIndex: -1},
+}
+
+//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/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/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/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/synchronize.go b/model/synchronize.go
new file mode 100644
index 0000000..a42e5e9
--- /dev/null
+++ b/model/synchronize.go
@@ -0,0 +1,38 @@
+package model
+
+import (
+ "electricity_bill_calc/types"
+ _ "github.com/shopspring/decimal"
+ "time"
+)
+
+type SynchronizeConfiguration struct {
+ User string `json:"user" db:"user_id"`
+ Park string `json:"park" db:"park_id"`
+ MeterReadingType int16 `json:"meter_reading_type"`
+ ImrsType string `json:"imrs_type"`
+ AuthorizationAccount string `json:"authorization_account" db:"imrs_authorization_account"`
+ AuthorizationSecret string `json:"authorization_secret" db:"imrs_authorization_secret"`
+ AuthorizationKey []byte `json:"authorization_key,omitempty" db:"imrs_authorization_key"`
+ Interval int16 `json:"interval"`
+ CollectAt time.Time `json:"collect_at" db:"-"`
+ MaxRetries int16 `json:"max_retries"`
+ RetryInterval int16 `json:"retry_interval"`
+ RetryIntervalAlgorithm int16 `json:"retry_interval_algorithm"`
+}
+
+type SynchronizeSchedule struct {
+ User string `json:"userId" db:"user_id"`
+ UserName string `json:"userName" db:"user_name"`
+ Park string `json:"parkId" db:"park_id"`
+ ParkName string `json:"parkName" db:"park_name"`
+ TaskIdentity string `json:"taskIdentity" db:"task_identity"`
+ TaskName string `json:"taskName" db:"task_name"`
+ TaskDescription string `json:"taskDescription" db:"task_description"`
+ CreatedAt types.DateTime `json:"createdAt" db:"created_at"`
+ LastModifiedAt types.DateTime `json:"lastModifiedAt" db:"last_modified_at"`
+ LastDispatchedAt types.DateTime `json:"lastDispatchedAt" db:"last_dispatched_at"`
+ LastDispatchStatus int16 `json:"lastDispatchStatus" db:"last_dispatch_status"`
+ NextDispatchAt types.DateTime `json:"nextDispatchAt" db:"next_dispatch_at"`
+ CurrentRetries int16 `json:"currentRetries" db:"current_retries"`
+}
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/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.go b/repository/god.go
new file mode 100644
index 0000000..87855ff
--- /dev/null
+++ b/repository/god.go
@@ -0,0 +1,19 @@
+package repository
+
+import (
+ "electricity_bill_calc/logger"
+ "github.com/doug-martin/goqu/v9"
+ "go.uber.org/zap"
+)
+
+type _GodModRepository struct {
+ log *zap.Logger
+ ds goqu.DialectWrapper
+}
+
+var GodModRepository = _GodModRepository{
+ log: logger.Named("Repository", "GodMod"),
+ ds: goqu.Dialect("postgres"),
+}
+
+// 删除指定园区中的表计和商户的绑定关系
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/synchronize.go b/repository/synchronize.go
new file mode 100644
index 0000000..c4b462d
--- /dev/null
+++ b/repository/synchronize.go
@@ -0,0 +1,208 @@
+package repository
+
+import (
+ "context"
+ "electricity_bill_calc/config"
+ "electricity_bill_calc/global"
+ "electricity_bill_calc/logger"
+ "electricity_bill_calc/model"
+ "electricity_bill_calc/tools"
+ "electricity_bill_calc/vo"
+ "fmt"
+ "github.com/doug-martin/goqu/v9"
+ "github.com/georgysavva/scany/v2/pgxscan"
+ "github.com/jackc/pgx/v5"
+ "go.uber.org/zap"
+ "strconv"
+)
+
+type _SynchronizeRepository struct {
+ log *zap.Logger
+ ds goqu.DialectWrapper
+}
+
+var SynchronizeRepository = _SynchronizeRepository{
+ log: logger.Named("Repository", "Synchronize"),
+ ds: goqu.Dialect("postgres"),
+}
+
+func (sr _SynchronizeRepository) SearchSynchronizeSchedules(userId *string, parkId *string, page uint, keyword *string) ([]*model.SynchronizeSchedule, int64, error) {
+ sr.log.Info("检索符合指定条件的同步记录", zap.String("user id", tools.DefaultTo(userId, "")),
+ zap.String("park id", tools.DefaultTo(parkId, "")), zap.Uint("page", page),
+ zap.String("keyword", tools.DefaultTo(keyword, "")))
+ ctx, cancelFunc := global.TimeoutContext()
+ defer cancelFunc()
+ //scheduleQuery := "select ss.*, ud.name as user_name, p.name as park_name from synchronize_schedule as ss
+ //join park as p on p.id=ss.park_id join user_detail as ud on ud.id=ss.user_id where 1=1"
+ schedulequery := sr.ds.From(goqu.T("synchronize_schedule").As("ss")).
+ Join(goqu.T("park").As("p"), goqu.On(goqu.I("p.id").Eq(goqu.I("ss.park_id")))).
+ Join(goqu.T("user_detail").As("ud"), goqu.On(goqu.I("ud.id").Eq(goqu.I("ss.user_id")))).
+ Select("ss.*", goqu.I("ud.name").As("user_name"), goqu.I("p.name").As("park_name"))
+ //countQuery := "select count(ss.*) from synchronize_schedule as ss
+ //join park as p on p.id=ss.park_id join user_detail as ud on ud.id=ss.user_id where 1=1"
+ countquery := sr.ds.From(goqu.T("synchronize_schedule").As("ss")).
+ Join(goqu.T("park").As("p"), goqu.On(goqu.I("p.id").Eq(goqu.I("ss.park_id")))).
+ Join(goqu.T("user_detail").As("ud"), goqu.On(goqu.I("ud.id").Eq(goqu.I("ss.user_id")))).
+ Select(goqu.COUNT(goqu.I("ss.*")))
+ if userId != nil && len(*userId) > 0 {
+ schedulequery = schedulequery.Where(goqu.I("ss.user_id").Eq(*userId))
+ countquery = countquery.Where(goqu.I("ss.user_id").Eq(*userId))
+ }
+ if parkId != nil && len(*parkId) > 0 {
+ schedulequery = schedulequery.Where(goqu.I("ss.park_id").Eq(*parkId))
+ countquery = countquery.Where(goqu.I("ss.park_id").Eq(*parkId))
+ }
+ if keyword != nil && len(*keyword) > 0 {
+ pattern := fmt.Sprintf("%%%s%%", *keyword)
+ schedulequery = schedulequery.Where(goqu.Or(
+ goqu.I("p.name").ILike(pattern),
+ goqu.I("p.abbr").ILike(pattern),
+ goqu.I("p.address").ILike(pattern),
+ goqu.I("p.contact").ILike(pattern),
+ goqu.I("p.phone").ILike(pattern),
+ goqu.I("ud.name").ILike(pattern),
+ goqu.I("ud.abbr").ILike(pattern),
+ goqu.I("ud.contact").ILike(pattern),
+ goqu.I("ud.phone").ILike(pattern),
+ goqu.I("ss.task_name").ILike(pattern),
+ goqu.I("ss.task_description").ILike(pattern),
+ ))
+ countquery = countquery.Where(goqu.Or(
+ goqu.I("p.name").ILike(pattern),
+ goqu.I("p.abbr").ILike(pattern),
+ goqu.I("p.address").ILike(pattern),
+ goqu.I("p.contact").ILike(pattern),
+ goqu.I("ud.name").ILike(pattern),
+ goqu.I("ud.abbr").ILike(pattern),
+ goqu.I("ud.contact").ILike(pattern),
+ goqu.I("ud.phone").ILike(pattern),
+ goqu.I("ss.task_name").ILike(pattern),
+ goqu.I("ss.task_description").ILike(pattern),
+ ))
+ }
+ startRow := (page - 1) * config.ServiceSettings.ItemsPageSize
+ schedulequery = schedulequery.
+ Order(goqu.I("ss.created_at").Desc()).
+ Offset(startRow).Limit(config.ServiceSettings.ItemsPageSize)
+ var (
+ schedule []*model.SynchronizeSchedule = make([]*model.SynchronizeSchedule, 0)
+ count int64
+ )
+ querySql, queryArgs, _ := schedulequery.Prepared(true).ToSQL()
+ countSql, countArgs, _ := countquery.Prepared(true).ToSQL()
+ if err := pgxscan.Select(ctx, global.DB, &schedule, querySql, queryArgs...); err != nil {
+ sr.log.Error("获取同步任务时出现错误", zap.Error(err))
+ return schedule, 0, err
+ }
+ if err := pgxscan.Get(ctx, global.DB, &count, countSql, countArgs...); err != nil {
+ sr.log.Error("检索同步任务总数量时出现错误", zap.Error(err))
+ return schedule, 0, err
+ }
+ return schedule, count, nil
+}
+
+// From("synchronize_schedule").
+//
+// Select(
+// goqu.I("synchronize_schedule.*"),
+// goqu.I("user_detail.name").As("user_name"),
+// goqu.I("park.name").As("park_name"),
+// ).
+// Join(
+// goqu.T("park").On(goqu.I("park.id").Eq(goqu.I("synchronize_schedule.park_id"))),
+// goqu.T("user_detail").On(goqu.I("user_detail.id").Eq(goqu.I("synchronize_schedule.user_id"))),
+// ).
+// Where(goqu.C("1").Eq(1))
+//
+// SELECT count(ss.*)
+// FROM synchronize_schedule AS ss
+// JOIN park AS p ON p.id = ss.park_id
+// JOIN user_detail AS ud ON ud.id = ss.user_id
+// WHERE true`
+//
+// var args []interface{}
+//
+// if uid != nil {
+// scheduleQuery += " AND ss.user_id = $1"
+// countQuery += " AND ss.user_id = $1"
+// args = append(args, *uid)
+// }
+//
+// if pid != nil {
+// scheduleQuery += " AND ss.park_id = $2"
+// countQuery += " AND ss.park_id = $2"
+// args = append(args, *pid)
+// }
+//
+// if keyword != nil {
+// pattern := "%" + *keyword + "%"
+// scheduleQuery += ` AND (p.name LIKE $3 OR p.abbr LIKE $3 OR p.address LIKE $3 OR p.contact LIKE $3 OR
+//
+// p.phone LIKE $3 OR ud.name LIKE $3 OR ud.abbr LIKE $3 OR ud.contact LIKE $3 OR
+// ud.phone LIKE $3 OR ss.task_name LIKE $3 OR ss.task_description LIKE $3)`
+//
+// args = append(args, pattern)
+// }
+func (sr _SynchronizeRepository) RetrieveSynchronizeConfiguration(uId, pId string) (vo.SynchronizeConfiguration, error) {
+ sr.log.Info("检索符合指定条件的同步记录", zap.String("user id", uId), zap.String("park id", pId))
+ ctx, cancelFunc := global.TimeoutContext()
+ defer cancelFunc()
+ //select * from synchronize_config where user_id=$1 and park_id=$2
+ configSql, configArgs, _ := sr.ds.
+ From(goqu.T("synchronize_config")).
+ Where(goqu.I("user_id").Eq(uId)).
+ Where(goqu.I("park_id").Eq(pId)).
+ Prepared(true).Select("*").ToSQL()
+ fmt.Println(configSql)
+ var configs []model.SynchronizeConfiguration
+ if err := pgxscan.Select(ctx, global.DB, &configs, configSql, configArgs...); err != nil {
+ fmt.Println(err)
+ sr.log.Error("获取同步任务时出现错误", zap.Error(err))
+ return vo.SynchronizeConfiguration{}, err
+ }
+ if len(configs) <= 0 {
+ return vo.SynchronizeConfiguration{}, nil
+ }
+ maxr := strconv.Itoa(int(configs[0].MaxRetries))
+ retry := strconv.Itoa(int(configs[0].RetryInterval))
+ synconfig := vo.SynchronizeConfiguration{
+ CollectAt: configs[0].CollectAt.Format("15:04:05"),
+ EntID: configs[0].User,
+ Imrs: configs[0].ImrsType,
+ ImrsAccount: configs[0].AuthorizationAccount,
+ ImrsKey: string(configs[0].AuthorizationKey),
+ ImrsSecret: configs[0].AuthorizationSecret,
+ Interval: float64(configs[0].Interval),
+ MaxRetries: maxr,
+ ParkID: configs[0].Park,
+ ReadingType: float64(configs[0].MeterReadingType),
+ RetryAlgorithm: float64(configs[0].RetryIntervalAlgorithm),
+ RetryInterval: retry,
+ }
+ return synconfig, nil
+}
+func (sr _SynchronizeRepository) CreateSynchronizeConfiguration(tx pgx.Tx, ctx context.Context, uId string, form *vo.SynchronizeConfigurationCreateForm) (bool, error) {
+ sr.log.Info("创建新的同步用户配置", zap.String("user Id", uId))
+ ctx, cancel := global.TimeoutContext()
+ defer cancel()
+ //insert into synchronize_config (user_id, park_id, meter_reading_type, imrs_type, imrs_authorization_account,
+ // imrs_authorization_secret, imrs_authorization_key, interval, collect_at, max_retries, retry_interval, retry_interval_algorithm) values
+ configSql, configArgs, _ := sr.ds.
+ Insert(goqu.T("synchronize_config")).
+ Cols(
+ "user_id", "park_id", "meter_reading_type", "imrs_type", "imrs_authorization_account", "imrs_authorization_secret",
+ "imrs_authorization_key", "interval", "collect_at", "max_retries",
+ "retry_interval", "retry_interval_algorithm").
+ Vals(
+ goqu.Vals{uId, form.ParkID, form.ReadingType, form.Imrs, form.ImrsAccount, form.ImrsSecret, form.ImrsKey, form.Interval,
+ form.CollectAt, form.MaxRetries, form.RetryInterval, form.RetryAlgorithm,
+ },
+ ).
+ Prepared(true).ToSQL()
+ ok, err := tx.Exec(ctx, configSql, configArgs...)
+ if err != nil {
+ sr.log.Error("创建同步配置信息失败", zap.Error(err))
+ return false, err
+ }
+ return ok.RowsAffected() > 0, nil
+}
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/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
new file mode 100644
index 0000000..7dbaade
--- /dev/null
+++ b/service/calculate/meters.go
@@ -0,0 +1,448 @@
+package calculate
+
+import (
+ "electricity_bill_calc/model"
+ "electricity_bill_calc/model/calculate"
+ "electricity_bill_calc/repository"
+ "errors"
+ "fmt"
+ "github.com/shopspring/decimal"
+)
+
+func CollectMeters(tenements []calculate.PrimaryTenementStatistics, poolings []calculate.Meter, publics []calculate.Meter) (MeterMap, error) {
+ meters := make(MeterMap)
+ // Collect tenement meters
+ for _, t := range tenements {
+ for _, m := range t.Meters {
+ key := Key{TenementID: t.Tenement.Id, Code: m.Code}
+ meters[key] = m
+ }
+ }
+ // Collect poolings
+ for _, m := range poolings {
+ key := Key{TenementID: "", Code: m.Code}
+ meters[key] = m
+ }
+ // Collect publics
+ for _, m := range publics {
+ key := Key{TenementID: "", Code: m.Code}
+ meters[key] = m
+ }
+ return meters, nil
+}
+
+// / 计算基本电费摊薄
+func CalculateBasicPooling(report *model.ReportIndex, summary *calculate.Summary, meters *MeterMap) error {
+ switch report.BasisPooled {
+ case model.POOLING_MODE_AREA:
+ if summary.OverallArea.IsZero() {
+ return fmt.Errorf("园区中表计覆盖总面积为零,无法按面积摊薄")
+ }
+ for _, meter := range *meters {
+ meterFee := meter.Overall.Amount.InexactFloat64() * summary.BasicPooledPriceArea.InexactFloat64()
+ meter.PooledBasic = model.ConsumptionUnit{
+ Amount: meter.Overall.Amount,
+ Fee: decimal.NewFromFloat(meterFee),
+ Price: summary.BasicPooledPriceArea,
+ Proportion: summary.BasicFee,
+ }
+ }
+ case model.POOLING_MODE_CONSUMPTION:
+ for _, meter := range *meters {
+ meterFee := meter.Overall.Amount.InexactFloat64() * summary.BasicPooledPriceConsumption.InexactFloat64()
+ meter.PooledBasic = model.ConsumptionUnit{
+ Amount: meter.Overall.Amount,
+ Fee: decimal.NewFromFloat(meterFee),
+ Price: summary.BasicPooledPriceConsumption,
+ Proportion: summary.BasicFee,
+ }
+ }
+ default:
+ }
+ return nil
+}
+
+/// 计算调整电费摊薄
+
+func CalculateAdjustPooling(report model.ReportIndex, summary calculate.Summary, meters MeterMap) error {
+ var p decimal.Decimal
+ switch report.AdjustPooled {
+ case model.POOLING_MODE_AREA:
+ if summary.OverallArea.IsZero() {
+ return fmt.Errorf("园区中表计覆盖总面积为零,无法按面积摊薄")
+ }
+
+ for _, meter := range meters {
+ meterFee := meter.Overall.Amount.Mul(summary.AdjustPooledPriceArea)
+ if summary.AdjustFee.IsZero() {
+ p = decimal.Zero
+ } else {
+ p = meterFee.Div(summary.AdjustFee)
+ }
+ meter.PooledAdjust = model.ConsumptionUnit{
+ Amount: meter.Overall.Amount,
+ Fee: meterFee,
+ Price: summary.AdjustPooledPriceArea,
+ Proportion: p,
+ }
+ }
+ case model.POOLING_MODE_CONSUMPTION:
+ for _, meter := range meters {
+ meterFee := meter.Overall.Amount.Mul(summary.AdjustPooledPriceConsumption)
+ if summary.AdjustFee.IsZero() {
+ p = decimal.Zero
+ } else {
+ p = meterFee.Div(summary.AdjustFee)
+ }
+ meter.PooledAdjust = model.ConsumptionUnit{
+ Amount: meter.Overall.Amount,
+ Fee: meterFee,
+ Price: summary.AdjustPooledPriceConsumption,
+ Proportion: p,
+ }
+ }
+ default:
+
+ }
+ return nil
+}
+
+// 除数问题
+func CalculateLossPooling(report model.ReportIndex, summary calculate.Summary, meters MeterMap) error {
+ switch report.LossPooled {
+ case model.POOLING_MODE_AREA:
+ if summary.OverallArea.IsZero() {
+ return fmt.Errorf("园区中表计覆盖总面积为零,无法按面积摊薄")
+ }
+ for _, meter := range meters {
+ pooledLossAmount1 := meter.Detail.Area.Decimal.Div(summary.OverallArea)
+ pooledLossAmount := pooledLossAmount1.Mul(summary.AuthoizeLoss.Amount)
+ meter.PooledLoss = model.ConsumptionUnit{
+ Amount: pooledLossAmount,
+ Fee: pooledLossAmount.Mul(summary.LossDilutedPrice),
+ Price: summary.LossDilutedPrice,
+ Proportion: meter.Detail.Area.Decimal.Div(summary.OverallArea),
+ }
+ }
+ case model.POOLING_MODE_CONSUMPTION:
+ for _, meter := range meters {
+ pooledLossAmount1 := meter.Detail.Area.Decimal.Div(summary.OverallArea)
+ pooledLossAmount := pooledLossAmount1.Mul(summary.AuthoizeLoss.Amount)
+
+ meter.PooledLoss = model.ConsumptionUnit{
+ Amount: pooledLossAmount,
+ Fee: pooledLossAmount.Mul(summary.LossDilutedPrice),
+ Price: summary.LossDilutedPrice,
+ Proportion: meter.Overall.Amount.Div(summary.Overall.Amount),
+ }
+ }
+ default:
+ // 其他情况下不做处理
+ }
+ return nil
+}
+
+/// 计算所有商户类型表计的全周期电量。
+
+func CalculateTenementConsumptions(meters MeterMap) (map[string]decimal.Decimal, error) {
+ consumptions := make(map[string]decimal.Decimal)
+ for _, meter := range meters {
+ if meter.Detail.MeterType == model.METER_INSTALLATION_TENEMENT {
+ amount, ok := consumptions[meter.Code]
+ if !ok {
+ amount = decimal.Decimal{}
+ }
+ amount.Add(meter.Overall.Amount).Add(amount)
+ consumptions[meter.Code] = amount
+ }
+ }
+ for _, meter := range meters {
+ if meter.Detail.MeterType == model.METER_INSTALLATION_TENEMENT {
+ amount, ok := consumptions[meter.Code]
+ if !ok {
+ return nil, errors.New("meter code not found in consumptions")
+ }
+
+ if amount.GreaterThan(decimal.Zero) {
+ meter.SharedPoolingProportion = meter.Overall.Amount.Div(amount)
+ } else if amount.IsZero() {
+ meter.SharedPoolingProportion = decimal.NewFromFloat(1.0)
+ } else {
+ meter.SharedPoolingProportion = decimal.NewFromFloat(1.0)
+ }
+ }
+ }
+
+ return consumptions, nil
+}
+
+/*
+/// 计算商户表计的公摊分摊
+
+func CalculateTenementPoolings(report model.ReportIndex, summary calculate.Summary, meters MeterMap, meterRelations []model.MeterRelation) error {
+ for _, meter := range meters {
+ if meter.Detail.MeterType == model.METER_INSTALLATION_TENEMENT {
+ switch report.PublicPooled {
+ case model.POOLING_MODE_AREA:
+ for _, relation := range meterRelations {
+ if relation.SlaveMeter == meter.Code {
+ key := Key{
+ Code: relation.MasterMeter,
+ }
+ parentMeter, ok := meters[key]
+ if !ok {
+ return errors.New("父级表记未找到")
+ }
+
+ poolingAmount := meter.Detail.Area.Decimal.Div(parentMeter.CoveredArea).
+ Mul(meter.SharedPoolingProportion).
+ Mul(parentMeter.Overall.Amount).Mul(summary.Overall.Price)
+
+ pooling := calculate.Pooling{
+ Code: parentMeter.Code,
+ Detail: model.ConsumptionUnit{
+ Amount: poolingAmount,
+ Fee: poolingAmount.Mul(summary.Overall.Price),
+ Price: summary.Overall.Price,
+ //后续debug此处需要判断,
+ Proportion: poolingAmount.Div(parentMeter.Overall.Amount),
+ },
+ }
+
+ pooling := calculate.Pooling{
+ Code: parentMeter.Code,
+ Detail: model.ConsumptionUnit{
+ Amount: poolingAmount,
+ Fee: poolingAmount.Mul(summary.Overall.Price),
+ Price: summary.Overall.Price,
+ Proportion: poolingAmount.Div(parentMeter.Overall.Amount),
+ },
+ }
+ meter.PooledPublic = &ConsumptionUnit{
+ Amount: poolingAmount,
+ Fee: new(big.Rat).Mul(poolingAmount, summary.Overall.Price),
+ Price: summary.Overall.Price,
+ Proportion: new(big.Rat).Quo(poolingAmount, parentAmount),
+ }
+
+ meter.Poolings = append(meter.Poolings, pooling)
+ }
+ }
+
+ case Consumption:
+ for _, relation := range meterRelations {
+ if relation.SlaveMeter == meter.Code {
+ parentMeter, ok := meters[relation.MasterMeter]
+ if !ok {
+ return errors.New("parent meter not found")
+ }
+
+ if parentMeter.Overall.Amount.Cmp(new(big.Rat)) == 0 {
+ poolingAmount := new(big.Rat)
+ parentAmount := new(big.Rat)
+
+ pooling := &Pooling{
+ Code: parentMeter.Code,
+ Detail: &ConsumptionUnit{
+ Amount: poolingAmount,
+ Fee: new(big.Rat),
+ Price: summary.Overall.Price,
+ Proportion: new(big.Rat),
+ },
+ }
+
+ meter.PooledPublic = &ConsumptionUnit{
+ Amount: poolingAmount,
+ Fee: new(big.Rat),
+ Price: summary.Overall.Price,
+ Proportion: new(big.Rat),
+ }
+
+ meter.Poolings = append(meter.Poolings, pooling)
+ } else {
+ poolingAmount := new(big.Rat).Mul(meter.Overall.Amount, new(big.Rat).Quo(parentMeter.Overall.Amount, parentMeter.Overall.Amount))
+ parentAmount := parentMeter.Overall.Amount
+
+ pooling := &Pooling{
+ Code: parentMeter.Code,
+ Detail: &ConsumptionUnit{
+ Amount: poolingAmount,
+ Fee: new(big.Rat).Mul(poolingAmount, summary.Overall.Price),
+ Price: summary.Overall.Price,
+ Proportion: new(big.Rat).Quo(poolingAmount, parentAmount),
+ },
+ }
+
+ meter.PooledPublic = &ConsumptionUnit{
+ Amount: poolingAmount,
+ Fee: new(big.Rat).Mul(poolingAmount, summary.Overall.Price),
+ Price: summary.Overall.Price,
+ Proportion: new(big.Rat).Quo(poolingAmount, parentAmount),
+ }
+
+ meter.Poolings = append(meter.Poolings, pooling)
+ }
+ }
+ }
+
+ default:
+ // handle other pooling modes...
+ }
+ }
+ }
+
+ return nil
+}
+*/
+// 计算商户表计的公摊分摊
+func CalculateTenementPoolings(report model.ReportIndex, summary calculate.Summary, meters MeterMap, meterRelations []model.MeterRelation) error {
+
+ switch report.PublicPooled {
+ case model.POOLING_MODE_AREA:
+ for _, meter := range meters {
+ if meter.Detail.MeterType == model.METER_INSTALLATION_TENEMENT {
+ var pooleds []struct {
+ PooledAmount decimal.Decimal
+ ParentAmount decimal.Decimal
+ ParentCode string
+ }
+ for _, relation := range meterRelations {
+ if relation.SlaveMeter == meter.Code {
+ key := Key{
+ Code: relation.MasterMeter,
+ }
+ parentMeter, ok := meters[key]
+ if !ok {
+ // 处理未找到父级表计的情况
+ continue
+ }
+ // 计算分摊电量和父级表电量
+
+ pooledAmount := meter.Detail.Area.Decimal.Div(parentMeter.CoveredArea).Mul(parentMeter.Overall.Amount).Mul(meter.SharedPoolingProportion)
+ pooleds = append(pooleds, struct {
+ PooledAmount decimal.Decimal
+ ParentAmount decimal.Decimal
+ ParentCode string
+ }{
+ PooledAmount: pooledAmount,
+ ParentAmount: parentMeter.Overall.Amount,
+ ParentCode: parentMeter.Code,
+ })
+ }
+ }
+
+ // 计算总分摊电量和总父级电量
+ var consumptions, total decimal.Decimal
+ for _, p := range pooleds {
+ consumptions = consumptions.Add(p.PooledAmount)
+ total = total.Add(p.ParentAmount)
+ }
+
+ // 计算并更新公摊分摊信息
+ for _, p := range pooleds {
+ poolingAmount := p.PooledAmount
+ proportion := p.PooledAmount.Div(p.ParentAmount)
+ fee := poolingAmount.Mul(summary.Overall.Price)
+
+ // 更新父级表计的公摊分摊信息
+ key := Key{
+ Code: p.ParentCode,
+ }
+ parentMeter := meters[key]
+ parentMeter.PooledPublic.Amount = consumptions
+ parentMeter.PooledPublic.Fee = consumptions.Mul(summary.Overall.Price)
+ parentMeter.PooledPublic.Proportion = consumptions.Div(total)
+ meters[Key{Code: p.ParentCode}] = parentMeter
+ // 创建并更新分摊信息
+ pooling := calculate.Pooling{
+ Code: p.ParentCode,
+ Detail: model.ConsumptionUnit{
+ Amount: poolingAmount,
+ Fee: fee,
+ Price: summary.Overall.Price,
+ Proportion: proportion,
+ },
+ }
+ meter.Poolings = append(meter.Poolings, &pooling)
+ }
+ }
+ }
+
+ case model.POOLING_MODE_CONSUMPTION:
+ for _, meter := range meters {
+ if meter.Detail.MeterType == model.METER_INSTALLATION_TENEMENT {
+ var pooled []struct {
+ PooledAmount decimal.Decimal
+ ParentAmount decimal.Decimal
+ ParentCode string
+ }
+ for _, relation := range meterRelations {
+ if relation.SlaveMeter == meter.Code {
+ parentMeter, ok := meters[Key{Code: relation.MasterMeter}]
+ if !ok {
+ // 处理未找到父级表计的情况
+ continue
+ }
+ // 计算分摊电量和父级电量
+ var pooledAmount decimal.Decimal
+ if parentMeter.Overall.Amount.IsZero() {
+ relations, err := repository.MeterRepository.ListPooledMeterRelations(report.Park, meter.Code)
+ if err != nil {
+ return err
+ }
+ //此处rust版本有误,更新后的解决办法
+ pooledAmount = meter.Overall.Amount.Div(decimal.NewFromInt(int64(len(relations)))).Mul(parentMeter.Overall.Amount)
+ }
+ pooled = append(pooled, struct {
+ PooledAmount decimal.Decimal
+ ParentAmount decimal.Decimal
+ ParentCode string
+ }{
+ PooledAmount: pooledAmount,
+ ParentAmount: parentMeter.Overall.Amount,
+ ParentCode: parentMeter.Code,
+ })
+ }
+ }
+
+ // 计算总分摊电量和总父级表记电量
+ var consumptions, total decimal.Decimal
+ for _, p := range pooled {
+ consumptions = consumptions.Add(p.PooledAmount)
+ total = total.Add(p.ParentAmount)
+ }
+
+ // 计算并更新公摊分摊信息
+ for _, p := range pooled {
+ poolingAmount := p.PooledAmount
+ proportion := p.PooledAmount.Div(p.ParentAmount)
+ fee := poolingAmount.Mul(summary.Overall.Price)
+
+ // 更新父级表计的公摊分摊信息
+ parentMeter := meters[Key{Code: p.ParentCode}]
+ parentMeter.PooledPublic.Amount = consumptions
+ parentMeter.PooledPublic.Fee = consumptions.Mul(summary.Overall.Price)
+ parentMeter.PooledPublic.Proportion = consumptions.Div(total)
+ meters[Key{Code: p.ParentCode}] = parentMeter
+
+ // 创建并更新分摊信息
+ pooling := calculate.Pooling{
+ Code: p.ParentCode,
+ Detail: model.ConsumptionUnit{
+ Amount: poolingAmount,
+ Fee: fee,
+ Price: summary.Overall.Price,
+ Proportion: proportion,
+ },
+ }
+ meter.Poolings = append(meter.Poolings, &pooling)
+ }
+ }
+ }
+
+ default:
+ // 处理其他分摊模式
+ }
+
+ return nil
+}
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
new file mode 100644
index 0000000..913ff12
--- /dev/null
+++ b/service/calculate/persist.go
@@ -0,0 +1,81 @@
+package calculate
+
+import (
+ "electricity_bill_calc/global"
+ "electricity_bill_calc/model"
+ "electricity_bill_calc/model/calculate"
+ "electricity_bill_calc/repository"
+ "github.com/jackc/pgx/v5"
+)
+
+// 向数据库保存核算概况结果
+func SaveSummary(tx pgx.Tx, summary calculate.Summary) error {
+ ctx, cancel := global.TimeoutContext()
+ defer cancel()
+
+ // 保存核算概况结果到数据库
+ err := repository.CalculateRepository.SaveReportSummary(tx, summary)
+ if err != nil {
+ return err
+ }
+ tx.Commit(ctx)
+ return nil
+}
+
+// type MeterMap map[string]map[string]calculate.Meter
+// 向数据库保存公共表计的计算结果
+func SavePublics(tx pgx.Tx, report model.ReportIndex, meters MeterMap) error {
+ ctx, cancel := global.TimeoutContext()
+ defer cancel()
+
+ var filteredMeters []calculate.Meter
+
+ for _, m := range meters {
+ if m.Detail.MeterType == model.METER_INSTALLATION_PARK {
+ filteredMeters = append(filteredMeters, m)
+ }
+ }
+ err := repository.CalculateRepository.SaveReportPublics(tx, report.Id, filteredMeters)
+ if err != nil {
+ return err
+ }
+ tx.Commit(ctx)
+ return nil
+}
+
+func SavePoolings(tx pgx.Tx, report model.ReportIndex, meters MeterMap, relations []model.MeterRelation) error {
+ ctx, cancel := global.TimeoutContext()
+ defer cancel()
+ var poolingMeters []calculate.Meter
+ var tenementMeters []calculate.Meter
+ // 根据条件筛选 Meter 并保存到对应的数组中
+ for _, m := range meters {
+ if m.Detail.MeterType == model.METER_INSTALLATION_POOLING {
+ poolingMeters = append(poolingMeters, m)
+ } else if m.Detail.MeterType == model.METER_INSTALLATION_TENEMENT {
+ tenementMeters = append(tenementMeters, m)
+ }
+ }
+ err := repository.CalculateRepository.SaveReportPoolings(tx, report.Id, poolingMeters, relations, tenementMeters)
+ if err != nil {
+ return err
+ }
+ tx.Commit(ctx)
+
+ return nil
+}
+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)
+ }
+ err := repository.CalculateRepository.SaveReportTenement(tx, report, ts, tc)
+ 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
new file mode 100644
index 0000000..651566b
--- /dev/null
+++ b/service/calculate/summary.go
@@ -0,0 +1,138 @@
+package calculate
+
+import (
+ "electricity_bill_calc/model"
+ "electricity_bill_calc/model/calculate"
+ "errors"
+ "github.com/shopspring/decimal"
+
+ "fmt"
+)
+
+// 计算已经启用的商铺面积和
+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...)
+ }
+ // 去重
+ uniqueAreaMeters := make(map[string]calculate.Meter)
+ for _, meter := range areaMeters {
+ uniqueAreaMeters[meter.Code] = meter
+ }
+ var areaTotal decimal.Decimal
+ for _, meter := range uniqueAreaMeters {
+ areaTotal = areaTotal.Add(meter.Detail.Area.Decimal)
+ }
+ if summary != nil {
+ summary.OverallArea = areaTotal
+ } else {
+ return nil, errors.New("summary is nil")
+ }
+ return &areaTotal, nil
+}
+
+// =================================================================================
+// / 计算基本电费分摊、调整电费分摊以及电费摊薄单价。
+// /
+// / - `summary`:核算报表的摘要信息
+func CalculatePrices(summary *calculate.Summary) error {
+ if summary.TotalConsumption.IsZero() {
+ return nil
+ }
+ summary.BasicPooledPriceConsumption = summary.BasicFee.Div(summary.TotalConsumption)
+ if summary.OverallArea.IsZero() {
+ summary.BasicPooledPriceArea = decimal.Zero
+ } else {
+ summary.BasicPooledPriceArea = summary.BasicFee.Div(summary.OverallArea)
+ }
+ summary.AdjustPooledPriceConsumption = summary.AdjustFee.Div(summary.TotalConsumption)
+ if summary.OverallArea.IsZero() {
+ summary.AdjustPooledPriceArea = decimal.Zero
+ } else {
+ summary.AdjustPooledPriceArea = summary.AdjustFee.Div(summary.OverallArea)
+ }
+ return nil
+
+}
diff --git a/service/calculate/tenement.go b/service/calculate/tenement.go
new file mode 100644
index 0000000..1b84bd9
--- /dev/null
+++ b/service/calculate/tenement.go
@@ -0,0 +1,458 @@
+package calculate
+
+import (
+ "electricity_bill_calc/model"
+ "electricity_bill_calc/model/calculate"
+ "electricity_bill_calc/repository"
+ "errors"
+ "fmt"
+ "github.com/shopspring/decimal"
+ "sort"
+ "strings"
+ "time"
+ "unsafe"
+)
+
+// 核算园区中的全部商户表计电量用电
+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 {
+ var meters []model.TenementMeter
+
+ for _, relation := range tenementMeterRelations {
+ if strings.EqualFold(relation.TenementId, tenement.Id) {
+ meters = append(meters, relation)
+ }
+ }
+
+ pt, err := determineTenementConsumptions(
+ tenement,
+ meters,
+ PeriodStart,
+ PeriodEnd,
+ tenementMeterReadings,
+ lastPeriodReadings,
+ meterDetails,
+ summary,
+ )
+ if err != nil {
+ return nil, err
+ }
+
+ tenementReports = append(tenementReports, pt)
+ }
+
+ return tenementReports, nil
+}
+
+// TODO: 2023.08.02 此方法未完成此方法主要用于。确定指定商户在指定时间段内的所有表计读数(完成)
+func determineTenementConsumptions(tenement model.Tenement,
+ relatedMeters []model.TenementMeter, periodStart time.Time,
+ periodEnd time.Time, currentTermReadings []model.MeterReading, lastPeriodReadings []model.MeterReading,
+ meterDetails []*model.MeterDetail, summary calculate.Summary) (calculate.PrimaryTenementStatistics, error) {
+ var meters []calculate.Meter
+ for _, meter := range relatedMeters {
+ startReading, err := determineTenementMeterStartReading(meter.MeterId, periodStart, ShiftToAsiaShanghai(tenement.MovedInAt.Time), meter, currentTermReadings, lastPeriodReadings)
+ if err != nil {
+ fmt.Println(err)
+ return calculate.PrimaryTenementStatistics{}, err
+ }
+
+ endReading, err := determineTenementMeterEndReading(meter.MeterId, periodEnd, ShiftToAsiaShanghai(tenement.MovedOutAt.Time), meter, currentTermReadings)
+ if err != nil {
+ fmt.Println(err)
+ return calculate.PrimaryTenementStatistics{}, err
+ }
+
+ detail, err := getMeterDetail(meterDetails, meter.MeterId)
+ if err != nil {
+ return calculate.PrimaryTenementStatistics{}, err
+ }
+
+ overall, err := ComputeOverall(*startReading, *endReading, summary)
+ if err != nil {
+ return calculate.PrimaryTenementStatistics{}, err
+ }
+
+ critical, err := ComputeCritical(*startReading, *endReading, summary)
+ if err != nil {
+ return calculate.PrimaryTenementStatistics{}, err
+ }
+
+ peak, err := ComputePeak(*startReading, *endReading, summary)
+ if err != nil {
+ return calculate.PrimaryTenementStatistics{}, err
+ }
+
+ flat, err := ComputeFlat(*startReading, *endReading, summary)
+ if err != nil {
+ return calculate.PrimaryTenementStatistics{}, err
+ }
+
+ valley, err := ComputeValley(*startReading, *endReading, summary)
+ if err != nil {
+ return calculate.PrimaryTenementStatistics{}, err
+ }
+
+ lastTermReading := model.Reading{
+ Ratio: startReading.Ratio,
+ Overall: startReading.Overall,
+ Critical: startReading.Critical,
+ Peak: startReading.Peak,
+ Flat: startReading.Flat,
+ Valley: startReading.Valley,
+ }
+
+ lastTermReadingPtr := &lastTermReading
+
+ currentTermReading := model.Reading{
+ Ratio: endReading.Ratio,
+ Overall: endReading.Overall,
+ Critical: endReading.Critical,
+ Peak: endReading.Peak,
+ Flat: endReading.Flat,
+ Valley: endReading.Valley,
+ }
+
+ currentTermReadingPtr := ¤tTermReading
+ meter := calculate.Meter{
+ Code: meter.MeterId,
+ Detail: detail,
+ CoveredArea: decimal.NewFromFloat(detail.Area.Decimal.InexactFloat64()),
+
+ LastTermReading: (*calculate.Reading)(unsafe.Pointer(lastTermReadingPtr)),
+ CurrentTermReading: (*calculate.Reading)(unsafe.Pointer(currentTermReadingPtr)),
+
+ Overall: overall,
+ Critical: critical,
+ Peak: peak,
+ Flat: flat,
+ Valley: valley,
+
+ AdjustLoss: model.ConsumptionUnit{},
+ PooledBasic: model.ConsumptionUnit{},
+ PooledAdjust: model.ConsumptionUnit{},
+ PooledLoss: model.ConsumptionUnit{},
+ PooledPublic: model.ConsumptionUnit{},
+ SharedPoolingProportion: decimal.Decimal{},
+ Poolings: nil,
+ }
+
+ meters = append(meters, meter)
+ }
+
+ return calculate.PrimaryTenementStatistics{
+ Tenement: tenement,
+ Meters: meters,
+ }, nil
+}
+
+func getMeterDetail(meterDetails []*model.MeterDetail, code string) (model.MeterDetail, error) {
+ for _, detail := range meterDetails {
+ if detail.Code == code {
+ return *detail, nil
+ }
+ }
+ return model.MeterDetail{}, errors.New(fmt.Sprintf("表计 %s 的详细信息不存在", code))
+}
+
+// 确定指定表计的起始读数
+func determineTenementMeterStartReading(meterId string, periodStart time.Time, tenementMovedInAt time.Time,
+ meterRelation model.TenementMeter, currentTermReadings []model.MeterReading,
+ lastPeriodReadings []model.MeterReading) (*model.MeterReading, error) {
+ var startTime time.Time
+ timeList := []time.Time{
+ periodStart,
+ tenementMovedInAt,
+ meterRelation.AssociatedAt.Time,
+ }
+
+ for _, t := range timeList {
+ if t.After(startTime) {
+ startTime = t
+ }
+ }
+ if startTime.IsZero() {
+ return nil, fmt.Errorf("无法确定表计 %s 的计量的起始时间", meterId)
+ }
+
+ var startReading *model.MeterReading
+ if startTime.Equal(periodStart) {
+ for _, reading := range lastPeriodReadings {
+ if reading.Meter == meterId {
+ if startReading == nil || reading.ReadAt.After(startReading.ReadAt.Time) {
+ startReading = &reading
+ }
+ }
+ }
+ } else {
+ for _, reading := range currentTermReadings {
+ readingAt := ShiftToAsiaShanghai(reading.ReadAt.Time)
+ if reading.Meter == meterId && readingAt.After(startTime) {
+ if startReading == nil || readingAt.Before(startReading.ReadAt.Time) {
+ startReading = &reading
+ }
+ }
+ }
+ }
+ if startReading == nil {
+ return nil, errors.New("无法确定表计 " + meterId + " 的计量的起始读数")
+ }
+ return startReading, nil
+}
+
+// 确定指定表计的终止读书
+func determineTenementMeterEndReading(meterId string, periodEnd time.Time,
+ TenementMovedOutAt time.Time, meterRelation model.TenementMeter,
+ currentTermReadings []model.MeterReading) (*model.MeterReading, error) {
+ var endTime time.Time
+ timeList := []time.Time{
+ periodEnd,
+ TenementMovedOutAt,
+ ShiftToAsiaShanghai(meterRelation.DisassociatedAt.Time),
+ }
+ for _, t := range timeList {
+ if t.After(endTime) {
+ endTime = t
+ }
+ }
+ if endTime.IsZero() {
+ return nil, fmt.Errorf("无法确定表计 %s 的计量的结束时间", meterId)
+ }
+
+ var endReading *model.MeterReading
+
+ for _, reading := range currentTermReadings {
+ readingAt := ShiftToAsiaShanghai(reading.ReadAt.Time)
+ if reading.Meter == meterId && readingAt.Before(endTime) {
+ if endReading == nil || readingAt.After(ShiftToAsiaShanghai(endReading.ReadAt.Time)) {
+ endReading = &reading
+ }
+ }
+ }
+ if endReading == nil {
+ return nil, errors.New(fmt.Sprintf("无法确定表计 %s 的计量的结束读数", meterId))
+ }
+ return endReading, nil
+}
+
+func ShiftToAsiaShanghai(t time.Time) time.Time {
+ location, _ := time.LoadLocation("Asia/Shanghai")
+ return t.In(location)
+}
+
+// 计算各个商户的合计信息,并归总与商户关联的表计记录
+func TenementChargeCalculate(tenements []calculate.PrimaryTenementStatistics,
+ summary calculate.Summary, meters MeterMap) []calculate.TenementCharge {
+ result := make(map[string][]string)
+ for _, t := range tenements {
+ meterCodes := make([]string, 0)
+ for _, m := range t.Meters {
+ meterCodes = append(meterCodes, m.Code)
+ }
+ 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
+
+ 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/god_mode.go b/service/god_mode.go
new file mode 100644
index 0000000..a493def
--- /dev/null
+++ b/service/god_mode.go
@@ -0,0 +1,232 @@
+package service
+
+import (
+ "electricity_bill_calc/global"
+ "electricity_bill_calc/logger"
+ "electricity_bill_calc/repository"
+ "fmt"
+ "github.com/doug-martin/goqu/v9"
+ "go.uber.org/zap"
+)
+
+type _GMService struct {
+ l *zap.Logger
+ gm goqu.DialectWrapper
+}
+
+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()
+
+ tx, err := global.DB.Begin(ctx)
+ if err != nil {
+ gm.l.Error("未能启动数据库事务", zap.Error(err))
+ return fmt.Errorf("未能启动数据库事务,%w", err)
+ }
+
+ err = repository.GMRepository.DeleteMeterBinding(ctx, tx, pid, tenements)
+ if err != nil {
+ tx.Rollback(ctx)
+ return err
+ }
+
+ err = repository.GMRepository.DeleteTenements(ctx, tx, pid, tenements)
+ if err != nil {
+ tx.Rollback(ctx)
+ return err
+ }
+ tx.Commit(ctx)
+ return nil
+}
+
+func (gm _GMService) DeleteParks(parks []string) error {
+
+ var err error
+ ctx, cancel := global.TimeoutContext()
+ defer cancel()
+
+ tx, err := global.DB.Begin(ctx)
+ if err != nil {
+ gm.l.Error("未能启动数据库事务", zap.Error(err))
+ return fmt.Errorf("未能启动数据库事务,%w", err)
+ }
+ 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 {
+ gm.l.Error("删除关联表出错。", zap.Error(err))
+ break
+ return err
+ }
+ }
+ err = repository.GMRepository.DeleteParks(ctx, tx, parks)
+ if err != nil {
+ gm.l.Error("指定园区删除失败。", zap.Error(err))
+ return err
+ }
+ tx.Commit(ctx)
+ return nil
+}
+
+func (gm _GMService) DeleteReports(pid string, reports []string) error {
+ ctx, cancel := global.TimeoutContext()
+ defer cancel()
+
+ tx, err := global.DB.Begin(ctx)
+ if err != nil {
+ gm.l.Error("未能启动数据库事务", zap.Error(err))
+ return fmt.Errorf("未能启动数据库事务,%w", err)
+ }
+
+ err = repository.GMRepository.DeleteReports(ctx, tx, pid, reports)
+ if err != nil {
+ tx.Rollback(ctx)
+ return err
+ }
+ tx.Commit(ctx)
+ return nil
+}
+
+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()
+
+ tx, err := global.DB.Begin(ctx)
+ if err != nil {
+ gm.l.Error("未能启动数据库事务", zap.Error(err))
+ return fmt.Errorf("未能启动数据库事务,%w", err)
+ }
+ parks, err := repository.GMRepository.ListAllParkIdsInUser(ctx, tx, uid)
+ if err != nil {
+ gm.l.Error("查询园区错误", zap.Error(err))
+ tx.Rollback(ctx)
+ return 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)
+
+ if err != nil {
+ gm.l.Error("删除用户下关联出错", zap.Error(err))
+ return 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 (gm _GMService) DeleteMeterPooling(pId string, mId []string) error {
+ ctx, cancel := global.TimeoutContext()
+ defer cancel()
+ tx, err := global.DB.Begin(ctx)
+ if err != nil {
+ gm.l.Error("开启数据库事务失败。", zap.Error(err))
+ return err
+ }
+ 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 {
+ gm.l.Error("未能成功提交数据库事务。", zap.Error(err))
+ tx.Rollback(ctx)
+ return err
+ }
+ return nil
+}
+
+func (gm _GMService) DeleteMeters(pId string, mId []string) error {
+ ctx, cancel := global.TimeoutContext()
+ 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, []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
+ }
+
+ err = tx.Commit(ctx)
+ if err != nil {
+ gm.l.Error("未能成功提交数据库事务。", zap.Error(err))
+ tx.Rollback(ctx)
+ return err
+ }
+ return nil
+}
\ No newline at end of file
diff --git a/service/statistics.go b/service/statistics.go
new file mode 100644
index 0000000..7284984
--- /dev/null
+++ b/service/statistics.go
@@ -0,0 +1,74 @@
+package service
+
+import (
+ "electricity_bill_calc/global"
+ "electricity_bill_calc/logger"
+ "electricity_bill_calc/model"
+ "github.com/doug-martin/goqu/v9"
+ "github.com/georgysavva/scany/v2/pgxscan"
+ "go.uber.org/zap"
+)
+
+type _StatisticsService struct {
+ l *zap.Logger
+ ss goqu.DialectWrapper
+}
+
+var StatisticsService = _StatisticsService{
+ logger.Named("Service", "Stat"),
+ goqu.Dialect("postgres"),
+}
+
+//用于统计企业用户数量
+func (ss _StatisticsService) EnabledEnterprises() (int64, error) {
+ ss.l.Info("开始统计企业数量。")
+ ctx, cancel := global.TimeoutContext()
+ defer cancel()
+
+ 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()
+
+ var c int64
+ err := pgxscan.Get(ctx, global.DB, &c, UserCountQuery, UserCountQueryArgs...)
+ if err != nil {
+ ss.l.Error("统计企业数量出错", zap.Error(err))
+ return 0, err
+ }
+ 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/synchronize.go b/service/synchronize.go
new file mode 100644
index 0000000..bd2a810
--- /dev/null
+++ b/service/synchronize.go
@@ -0,0 +1,51 @@
+package service
+
+import (
+ "electricity_bill_calc/global"
+ "electricity_bill_calc/logger"
+ "electricity_bill_calc/repository"
+ "electricity_bill_calc/vo"
+ "github.com/doug-martin/goqu/v9"
+ "go.uber.org/zap"
+)
+
+type _SynchronizeService struct {
+ log *zap.Logger
+ ds goqu.DialectWrapper
+}
+
+var SynchronizeService = _SynchronizeService{
+ log: logger.Named("Service", "Synchronize"),
+ ds: goqu.Dialect("postgres"),
+}
+
+func (ss _SynchronizeService) CreateSynchronizeConfiguration(userId string, form *vo.SynchronizeConfigurationCreateForm) error {
+ ss.log.Info("创建一条新的同步配置", zap.String("user id", userId))
+ ctx, cancel := global.TimeoutContext()
+ defer cancel()
+ tx, err := global.DB.Begin(ctx)
+ if err != nil {
+ ss.log.Error("无法启动数据库事务。", zap.Error(err))
+ return err
+ }
+ ok, err := repository.SynchronizeRepository.CreateSynchronizeConfiguration(tx, ctx, userId, form)
+ if err != nil {
+ ss.log.Error("无法创建新的同步配置。", zap.Error(err))
+ tx.Rollback(ctx)
+ return err
+ }
+ if !ok {
+ ss.log.Error("数据库未能记录新的同步配置。")
+ tx.Rollback(ctx)
+ return err
+ }
+
+ err = tx.Commit(ctx)
+ if err != nil {
+ ss.log.Error("未能成功提交数据库事务。", zap.Error(err))
+ tx.Rollback(ctx)
+ return err
+ }
+ return 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
new file mode 100644
index 0000000..caa3f09
--- /dev/null
+++ b/service/withdraw.go
@@ -0,0 +1,39 @@
+package service
+
+import (
+ "electricity_bill_calc/global"
+ "electricity_bill_calc/logger"
+ "electricity_bill_calc/model"
+ "github.com/doug-martin/goqu/v9"
+ "github.com/georgysavva/scany/v2/pgxscan"
+ "go.uber.org/zap"
+)
+
+type _WithdrawService struct {
+ log *zap.Logger
+ ds goqu.DialectWrapper
+}
+
+var WithdrawService = _WithdrawService{
+ logger.Named("Service", "Withdraw"),
+ goqu.Dialect("postgres"),
+}
+
+func (wd _WithdrawService) AuditWaits() (int64, error) {
+ wd.log.Info("获取当前系统中待审核的内容数量。")
+ ctx, cancel := global.TimeoutContext()
+ defer cancel()
+
+ 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 {
+ wd.log.Error("获取当前系统中待审核的内容数量出错。")
+ return 0,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/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/synchronize.go b/vo/synchronize.go
new file mode 100644
index 0000000..39edecb
--- /dev/null
+++ b/vo/synchronize.go
@@ -0,0 +1,29 @@
+package vo
+
+type SynchronizeConfiguration struct {
+ CollectAt string `json:"collectAt"` // 采集时间,格式:HH:mm
+ EntID string `json:"entId"` // 企业ID
+ Imrs string `json:"imrs"` // 采集系统型号
+ ImrsAccount string `json:"imrsAccount"` // 同步登录账号
+ ImrsKey string `json:"imrsKey"` // 同步登录私钥,Base64或者私钥文件内容
+ ImrsSecret string `json:"imrsSecret"` // 同步登录密钥,加盐双向加密
+ Interval float64 `json:"interval"` // 采集周期,0:每小时,1:每日,2:每周,3:每月
+ MaxRetries string `json:"maxRetries"` // 最大重试次数
+ ParkID string `json:"parkId"` // 园区ID
+ ReadingType float64 `json:"readingType"` // 采集方式,0:自动+人工,1:自动,2:人工
+ RetryAlgorithm float64 `json:"retryAlgorithm"` // 重试间隔算法,0:指数退避,1:2倍线性间隔,2:3倍线性间隔,3:固定间隔
+ RetryInterval string `json:"retryInterval"` // 重试间隔,基础间隔时间,根据间隔算法不同会产生不同的间隔
+}
+type SynchronizeConfigurationCreateForm struct {
+ CollectAt string `json:"collectAt"` // 采集时间,格式:HH:mm
+ Imrs string `json:"imrs"` // 采集系统型号,为空的时候表示不同步
+ ImrsAccount string `json:"imrsAccount"` // 同步登录账号
+ ImrsKey string `json:"imrsKey"` // 同步登录私钥,Base64或者私钥文件内容
+ ImrsSecret string `json:"imrsSecret"` // 同步登录密钥,加盐双向加密
+ Interval float64 `json:"interval"` // 采集周期,0:每小时,1:每日,2:每周,3:每月
+ MaxRetries string `json:"maxRetries"` // 最大重试次数
+ ParkID string `json:"parkId"` // 园区ID
+ ReadingType float64 `json:"readingType"` // 采集方式,0:自动+人工,1:自动,2:人工
+ RetryAlgorithm float64 `json:"retryAlgorithm"` // 重试间隔算法,0:指数退避,1:2倍线性间隔,2:3倍线性间隔,3:固定间隔
+ RetryInterval string `json:"retryInterval"` // 重试间隔,基础间隔时间,根据间隔算法不同会产生不同的间隔
+}
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"`
+}