test(config):合并分时提交的配置文件

This commit is contained in:
ZiHangQin 2023-08-07 15:32:10 +08:00
commit f995b89ea9
53 changed files with 4623 additions and 181 deletions

9
.idea/0.2.iml Normal file
View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="Go" enabled="true" />
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

11
.idea/dataSources.xml Normal file
View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
<data-source source="LOCAL" name="PostgreSQL - postgres@39.105.39.8" uuid="996b1b9f-5c40-4bd6-8c3c-0af67aaaa15d">
<driver-ref>postgresql</driver-ref>
<synchronize>true</synchronize>
<jdbc-driver>org.postgresql.Driver</jdbc-driver>
<jdbc-url>jdbc:postgresql://39.105.39.8:9432/postgres</jdbc-url>
</data-source>
</component>
</project>

View File

@ -2,7 +2,11 @@
<project version="4">
<component name="ProjectModuleManager">
<modules>
<<<<<<< HEAD
<module fileurl="file://$PROJECT_DIR$/.idea/electricity_bill_calc_service.iml" filepath="$PROJECT_DIR$/.idea/electricity_bill_calc_service.iml" />
=======
<module fileurl="file://$PROJECT_DIR$/.idea/0.2.iml" filepath="$PROJECT_DIR$/.idea/0.2.iml" />
>>>>>>> 8163fd99ee4261a308318b55be08e30d123776e3
</modules>
</component>
</project>

View File

@ -1,6 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<<<<<<< HEAD
<mapping directory="$PROJECT_DIR$" vcs="Git" />
=======
<mapping directory="" vcs="Git" />
>>>>>>> 8163fd99ee4261a308318b55be08e30d123776e3
</component>
</project>

183
_1.gitignore Normal file
View File

@ -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

View File

@ -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
}

24
controller/foundation.go Normal file
View File

@ -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},
)
}

132
controller/god_mode.go Normal file
View File

@ -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("指定表计已经删除。")
}

View File

@ -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)

85
controller/statistics.go Normal file
View File

@ -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,
},
})
}

121
controller/sync.go Normal file
View File

@ -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("更新完成。")
}

View File

@ -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)
}

View File

@ -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)

135
controller/withdraw.go Normal file
View File

@ -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, "其他错误")
}

22
excel/tenement.go Normal file
View File

@ -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)
//}

View File

@ -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
}

View File

@ -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,

14
go.mod
View File

@ -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
)

133
go.sum
View File

@ -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=

View File

@ -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,
}
}

View File

@ -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
}

View File

@ -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 {

38
model/synchronize.go Normal file
View File

@ -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"`
}

View File

@ -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"`
}

84
model/withdraw.go Normal file
View File

@ -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"`
}

View File

@ -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),
// TODO2023.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), `"`, `\"`) + `"`
}

19
repository/god.go Normal file
View File

@ -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"),
}
// 删除指定园区中的表计和商户的绑定关系

449
repository/god_mode.go Normal file
View File

@ -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
}

View File

@ -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),

208
repository/synchronize.go Normal file
View File

@ -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
}

242
repository/withdraw.go Normal file
View File

@ -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
}

View File

@ -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)

View File

@ -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
}

View File

@ -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
}

View File

@ -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

448
service/calculate/meters.go Normal file
View File

@ -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
}

37
service/calculate/park.go Normal file
View File

@ -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
}

View File

@ -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
}

111
service/calculate/pooled.go Normal file
View File

@ -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
}

105
service/calculate/shared.go Normal file
View File

@ -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
}

View File

@ -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
}

View File

@ -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 := &currentTermReading
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
}

141
service/calculate/utils.go Normal file
View File

@ -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
}

View File

@ -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)
}

232
service/god_mode.go Normal file
View File

@ -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
}

74
service/statistics.go Normal file
View File

@ -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
}

51
service/synchronize.go Normal file
View File

@ -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
}

View File

@ -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,

39
service/withdraw.go Normal file
View File

@ -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
}

View File

@ -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: 基准线损率

View File

@ -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
}

29
vo/synchronize.go Normal file
View File

@ -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指数退避12倍线性间隔23倍线性间隔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指数退避12倍线性间隔23倍线性间隔3固定间隔
RetryInterval string `json:"retryInterval"` // 重试间隔,基础间隔时间,根据间隔算法不同会产生不同的间隔
}

6
vo/withdraw.go Normal file
View File

@ -0,0 +1,6 @@
package vo
//用于接收审核报表的参数
type ReviewWithdraw struct {
Audit bool `json:"audit"`
}