65 Commits

Author SHA1 Message Date
0f91b8a332 doc(calculate): 优化代码格式,删除部分无用打印语句 2023-08-15 15:20:59 +08:00
59a604bab0 fix(park): 修复创建报表中核定线损未显示问题 2023-08-15 15:13:10 +08:00
eaf45331f1 fix(calculate_tenement):修复计算相关赋值问题 2023-08-15 14:46:04 +08:00
2844db1a86 fix(#27): 修复用户电量电费详细为空 2023-08-15 14:13:25 +08:00
7d3fafeb04 fix(#27): 未完全修复成功 2023-08-15 11:05:41 +08:00
032d38181a fix(#26): 修复获取公摊表计下的摊薄表计失败[如果没有公摊关系,则无提示,无显示信息] 2023-08-14 17:26:58 +08:00
292a50029f Merge branch '0.2' of https://git.archgrid.xyz/free-lancers/electricity_bill_calc_service into 0.2 2023-08-14 16:34:16 +08:00
de176ce61d fix(#23): 修复获取园区公共电费概况有问题 2023-08-14 16:34:12 +08:00
8a383515d3 fix(#22):修复部分字段读数问题 2023-08-14 14:42:15 +08:00
37bd0da1f2 Merge branch '0.2' of https://git.archgrid.xyz/free-lancers/electricity_bill_calc_service into 0.2 2023-08-14 13:49:48 +08:00
bfa7ac1508 fix(#24):修复获取到指定核算报表中的分页公摊表计的核算信息问题 2023-08-14 13:48:58 +08:00
3f36153968 fix(#22):修复无法获取报表的总览信息错误 2023-08-14 13:24:25 +08:00
845bd75348 fix(#10): 修复创建报表错误 2023-08-14 10:05:09 +08:00
559c2d439d Merge branch '0.2' of https://git.archgrid.xyz/free-lancers/electricity_bill_calc_service into 0.2 2023-08-11 15:31:37 +08:00
d6829fce27 fix(#10):修复空指针 2023-08-11 15:31:24 +08:00
d1e8c63ec9 fix(park): 修复删除园区返回信息错误 2023-08-11 15:19:06 +08:00
e229f3976d fix(#20): 修复在删除充值记录的时候出现问题 2023-08-11 14:52:15 +08:00
683907b363 fix(#10): 计算相关读取数据部分debug完成 2023-08-11 13:13:45 +08:00
c1b84d1fbb Merge branch '0.2' of https://git.archgrid.xyz/free-lancers/electricity_bill_calc_service into 0.2 2023-08-11 11:00:32 +08:00
2021d67d03 fix(#10):修复空指针 2023-08-11 10:59:53 +08:00
b988ffcb75 fix(pooled): 增加缺少字段loss 2023-08-11 10:57:45 +08:00
361b302407 Merge branch '0.2' of https://git.archgrid.xyz/free-lancers/electricity_bill_calc_service into 0.2 2023-08-11 10:37:40 +08:00
d8f470e660 fix(#10): 修复部分计算错误,遗留问题在pooled中的deteminePublicMeterConsumpyions里时间为空错误 2023-08-11 10:37:17 +08:00
c472eb4196 fix(#17):修复有绑定表计的商户迁出时无法迁出的错误 2023-08-11 09:02:57 +08:00
98866a8de5 fix(#16):修复商户入驻时间为指定日期,并非当前日期 2023-08-10 16:33:41 +08:00
c89a5f20ad fix(doc): 注释优化 2023-08-10 16:13:12 +08:00
01e944cb5a fix(meter): 修改结构体MeterCreationForm中的字段名称,和此结构体的调用处 2023-08-10 16:12:13 +08:00
c2d43bd98e fix(#15): 修复: 更换表计的时候,在代码中接收了参数未作任何处理 2023-08-10 15:27:59 +08:00
a543b33276 Merge branch '0.2' of https://git.archgrid.xyz/free-lancers/electricity_bill_calc_service into 0.2 2023-08-10 15:03:49 +08:00
6289257ac1 fix(#13):修复历史电费核算时未选择园区时显示园区中的数据,选中某一园区时显示不同园区的数据 2023-08-10 14:54:12 +08:00
5d997c14ee Merge branch '0.2' of https://git.archgrid.xyz/free-lancers/electricity_bill_calc_service into 0.2 2023-08-10 10:49:47 +08:00
b50eabbca6 fix(meter): 修复:更换电表时的修改逻辑 2023-08-10 10:49:40 +08:00
a34aa6ad07 fix(#12):修复抄表记录与表记管理处无论选择任何表记类型只会显示全部错误 2023-08-10 09:54:57 +08:00
3d918eea85 fix(tenement): 解决向园区中指定商户下绑定一个新的表计时时间异常问题 2023-08-10 09:32:38 +08:00
7f46f2f36b fix(calculate): 修复创建报表时的状态错误 2023-08-09 16:24:16 +08:00
ed10996a06 Merge branch '0.2' of https://git.archgrid.xyz/free-lancers/electricity_bill_calc_service into 0.2 2023-08-09 15:44:10 +08:00
2aa3729e03 fix(#12):修复抄表记录与表记管理处无论选择任何表记类型只会显示全部错误 2023-08-09 15:42:37 +08:00
7d59121c2f fix(report): 调试,历史电费核算部分 2023-08-09 15:35:02 +08:00
b2efb972dc fix(report): 调试,历史电费核算部分 2023-08-09 15:28:17 +08:00
5f750dd0e0 Merge branch '0.2' of https://git.archgrid.xyz/free-lancers/electricity_bill_calc_service into 0.7 2023-08-09 10:51:57 +08:00
dd6becc994 fix(#10):修复创建报表错误,但查出列出园区bug 2023-08-09 10:48:33 +08:00
b59e24333e fix(report): 修复核算报表中园区获取错误 2023-08-09 09:18:51 +08:00
00cf35ec2a fix(#10):修复创建报表错误,但查出列出园区bug 2023-08-08 17:41:16 +08:00
ed5a3f6c0a fix(#5):修复为前端下列表记提供的接口 2023-08-08 15:21:12 +08:00
a593666542 fix(authorized_loss_rate): 修复获取系统中定义的基准核定线损率错误 2023-08-08 14:43:45 +08:00
8a4be5328e fix(#8): 合并分支处理冲突时候遗留问题,并且注释中的问题未处理。 2023-08-08 14:02:20 +08:00
0135b81080 doc(settings): 设置基准线损率为7% 2023-08-08 13:57:05 +08:00
fb328514d1 fix(config): 优化redis的连接,同一调取配置文件内的 2023-08-08 13:51:00 +08:00
ad1b816795 fix(#5):修复抄表管理页面显示的表计信息多余数据库中的数据 2023-08-08 13:47:02 +08:00
fe7f8bd692 Merge branch '0.2' of https://git.archgrid.xyz/free-lancers/electricity_bill_calc_service into 0.2 2023-08-08 13:34:29 +08:00
f55cc07b1c doc(gitignore): 2023-08-08 13:34:23 +08:00
a3d4dbfca5 fix:#6 解决表号为空时候的sql错误 2023-08-08 13:24:58 +08:00
2bab62512a doc(gitignore):禁止goland个性化配置上传 2023-08-08 10:33:48 +08:00
e3c34c96e2 enhance(sync):同步管理路由添加 2023-08-08 10:05:37 +08:00
a34fee8339 fix(meterDetail): 增加缺少字段display_ratio 2023-08-08 08:54:19 +08:00
69357f77dd fix(report):修复更新指定的核算任务与初始化一个新的核算任务 2023-08-07 17:33:38 +08:00
27a499b8cc fix(report): 修复启动指定核算任务的计算功能 2023-08-07 17:05:29 +08:00
568deaf972 enhance(report): 2023-08-07 17:00:46 +08:00
f995b89ea9 test(config):合并分时提交的配置文件 2023-08-07 15:32:10 +08:00
cd56f2b444 test(user): 用于合并分支 2023-08-07 15:30:50 +08:00
8163fd99ee Merge pull request 'enhance(calculate): 完善计算部分' (#3) from Deka_123/electricity_bill_calc_service:newBranch into 0.2
Reviewed-on: free-lancers/electricity_bill_calc_service#3
2023-08-07 15:24:21 +08:00
0e365cdb6b enhance(calculate): 完善计算部分 2023-08-07 15:15:11 +08:00
020e76b901 合并分支 2023-08-04 17:11:10 +08:00
79d55505a7 Merge pull request 'ZiHangQinBranch' (#2) from ZiHangQin/electricity_bill_calc_service:ZiHangQinBranch into 0.2
Reviewed-on: free-lancers/electricity_bill_calc_service#2
2023-08-04 15:01:24 +08:00
94e19f4c5a Merge pull request 'ZiHangQinBranch' (#1) from ZiHangQin/electricity_bill_calc_service:ZiHangQinBranch into 0.2
Reviewed-on: free-lancers/electricity_bill_calc_service#1
2023-08-04 10:27:19 +08:00
56 changed files with 2380 additions and 453 deletions

1
.gitignore vendored
View File

@@ -161,6 +161,7 @@ atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
.idea/electricity_bill_calc_service.iml
# SonarLint plugin
.idea/sonarlint/

9
.idea/0.2.iml generated 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>

View File

@@ -1,5 +1,6 @@
<?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" />

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<module version="4">
<component name="Go" enabled="true" />
</module>

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

@@ -19,6 +19,6 @@ func getNormAuthorizedLossRate(c *fiber.Ctx) error {
result := response.NewResult(c)
BaseLoss := config.BaseLoss
return result.Success("已经获取到系统设置的基准核定线损率。",
fiber.Map{"normAuthorizedLossRate": BaseLoss},
fiber.Map{"normAuthorizedLossRate": BaseLoss.Base},
)
}

View File

@@ -12,12 +12,11 @@ import (
"electricity_bill_calc/types"
"electricity_bill_calc/vo"
"fmt"
"net/http"
"github.com/gofiber/fiber/v2"
"github.com/jinzhu/copier"
"github.com/samber/lo"
"go.uber.org/zap"
"net/http"
)
var meterLog = logger.Named("Handler", "Meter")
@@ -41,6 +40,7 @@ func InitializeMeterHandlers(router *fiber.App) {
router.Get("/reading/:pid/template", security.EnterpriseAuthorize, downloadMeterReadingsTemplate)
router.Post("/reading/:pid/batch", security.EnterpriseAuthorize, uploadMeterReadings)
router.Post("/reading/:pid/:code", security.EnterpriseAuthorize, recordMeterReading)
router.Get("/reading/:pid/choice", security.EnterpriseAuthorize, listReadableMeters)
}
// 查询指定园区下的表计信息
@@ -53,7 +53,8 @@ func searchMetersWithinPark(c *fiber.Ctx) error {
}
keyword := c.Query("keyword")
page := c.QueryInt("page", 1)
meters, total, err := repository.MeterRepository.MetersIn(parkId, uint(page), &keyword)
mtype := c.QueryInt("type", 3)
meters, total, err := repository.MeterRepository.MetersIn(parkId, uint(page), &keyword, mtype)
if err != nil {
meterLog.Error("无法查询指定园区下的表计信息,无法获取表计列表", zap.Error(err))
return result.Error(http.StatusInternalServerError, err.Error())
@@ -99,6 +100,7 @@ func createNewMeterManually(c *fiber.Ctx) error {
meterLog.Error("无法手动添加一条0.4kV表计记录,无法解析表计创建表单", zap.Error(err))
return result.NotAccept(err.Error())
}
if err := service.MeterService.CreateMeterRecord(parkId, &creationForm); err != nil {
meterLog.Error("无法手动添加一条0.4kV表计记录,无法创建表计记录", zap.Error(err))
return result.NotAccept(err.Error())
@@ -200,7 +202,12 @@ func replaceMeter(c *fiber.Ctx) error {
meterLog.Error("无法更换系统中的表计,无法解析表计更换表单", zap.Error(err))
return result.NotAccept(err.Error())
}
return nil
err := service.MeterService.ReplaceMeter(parkId, meterId, &replacementForm.OldReading, replacementForm.NewMeter.Code, replacementForm.NewMeter.Ratio, &replacementForm.NewMeter.Reading)
if err != nil {
meterLog.Error("更换表计出错1111", zap.Error(err))
return result.Error(500, err.Error())
}
return result.Success("更换成功")
}
// 列出指定公摊表计下的所有关联表计
@@ -348,6 +355,7 @@ func listUnboundTenementMeters(c *fiber.Ctx) error {
func queryMeterReadings(c *fiber.Ctx) error {
result := response.NewResult(c)
parkId := c.Params("pid")
mtype := c.QueryInt("type", 3)
if pass, err := checkParkBelongs(parkId, meterLog, c, &result); !pass {
return err
}
@@ -372,7 +380,7 @@ func queryMeterReadings(c *fiber.Ctx) error {
endDate = &parsedDate
}
}
readings, total, err := service.MeterService.SearchMeterReadings(parkId, building, startDate, endDate, uint(page), keyword)
readings, total, err := service.MeterService.SearchMeterReadings(parkId, building, startDate, endDate, uint(page), keyword, uint(mtype))
if err != nil {
meterLog.Error("查询指定园区中的表计读数,无法获取表计读数列表", zap.Error(err))
return result.Error(http.StatusInternalServerError, err.Error())
@@ -500,3 +508,31 @@ func uploadMeterReadings(c *fiber.Ctx) error {
}
return result.Success("表计档案已经导入完成。", fiber.Map{"errors": errs})
}
// 列出可以作为抄表目标的表计,主要用于创建抄表记录时的下拉选择。
func listReadableMeters(c *fiber.Ctx) error {
parkId := c.Params("pid")
meterLog.Info("列出可以作为抄表目标的表计", zap.String("parkId", parkId))
result := response.NewResult(c)
if len(parkId) == 0 {
meterLog.Error("无法列出指定园区中尚未绑定商户的表计未指定要访问的园区ID")
return result.NotAccept("未指定要访问的园区。")
}
if pass, err := checkParkBelongs(parkId, meterLog, c, &result); !pass {
return err
}
limit := c.QueryInt("limit", 10)
keyword := tools.EmptyToNil(c.Query("keyword"))
meters, err := repository.MeterRepository.ListReadableMeters(parkId, keyword, uint(limit))
if err != nil {
meterLog.Error(
"无法列出可以作为抄表目标的表计,无法获取表计列表", zap.Error(err))
return result.Error(http.StatusInternalServerError, err.Error())
}
var simplifiedMeters = make([]*vo.ReadableMeterQueryResponse, 0)
copier.Copy(&simplifiedMeters, &meters)
return result.Success(
"指定园区中的可作为抄表目标的表计已经列出。",
fiber.Map{"meters": simplifiedMeters},
)
}

View File

@@ -187,7 +187,7 @@ func deleteSpecificPark(c *fiber.Ctx) error {
parkLog.Error("无法删除园区。", zap.String("park id", parkId), zap.String("user id", session.Uid), zap.Error(err))
return result.Error(http.StatusInternalServerError, err.Error())
}
return result.Deleted("已删除指定的园区")
return result.Success("已删除指定的园区")
}
// 列出指定园区中已经登记的建筑

View File

@@ -11,6 +11,9 @@ import (
"electricity_bill_calc/types"
"electricity_bill_calc/vo"
"log"
"strconv"
"github.com/gofiber/fiber/v2"
"github.com/jinzhu/copier"
"github.com/samber/lo"
@@ -23,6 +26,7 @@ func InitializeReportHandlers(router *fiber.App) {
router.Get("/reports", security.MustAuthenticated, reportComprehensiveSearch)
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)
@@ -95,7 +99,7 @@ func initNewReportCalculateTask(c *fiber.Ctx) error {
if pass, err := checkParkBelongs(form.Park, reportLog, c, &result); !pass {
return err
}
ok, err := repository.ReportRepository.CreateReport(&form)
ok, err := service.ReportService.CreateNewReport(&form)
if err != nil {
reportLog.Error("无法创建核算报表", zap.Error(err))
return result.Error(fiber.StatusInternalServerError, "无法创建核算报表。")
@@ -119,7 +123,7 @@ func updateReportCalculateTask(c *fiber.Ctx) error {
reportLog.Error("无法解析更新核算报表的请求数据。", zap.Error(err))
return result.BadRequest("无法解析更新核算报表的请求数据。")
}
ok, err := repository.ReportRepository.UpdateReportSummary(reportId, &form)
ok, err := service.ReportService.UpdateRepoet(reportId, &form)
if err != nil {
reportLog.Error("无法更新核算报表", zap.Error(err))
return result.Error(fiber.StatusInternalServerError, "无法更新核算报表。")
@@ -143,6 +147,11 @@ func initiateCalculateTask(c *fiber.Ctx) error {
reportLog.Error("无法启动核算报表计算任务", zap.Error(err))
return result.Error(fiber.StatusInternalServerError, "无法启动核算报表计算任务。")
}
//开启核算任务
err = service.ReportService.ReportCalcuateDispatch(reportId)
if err != nil {
return result.Error(500, "核算任务启动失败"+err.Error())
}
return result.Success("已经成功启动核算报表计算任务。")
}
@@ -238,8 +247,28 @@ func getReportSummary(c *fiber.Ctx) error {
reportLog.Error("未找到核算报表的总览信息")
return result.NotFound("未找到核算报表的总览信息。")
}
var summaryResponse vo.ParkSummaryResponse
copier.Copy(&summaryResponse, report)
summaryResponse := vo.ParkSummaryResponse{
ReportId: report.ReportId,
OverallDisplay: vo.ConsumptionDisplay{
AmountStr: strconv.FormatFloat(report.Overall.Amount.InexactFloat64(), 'f', -1, 64),
FeeStr: strconv.FormatFloat(report.Overall.Fee.InexactFloat64(), 'f', -1, 64),
PriceStr: strconv.FormatFloat(report.Overall.Price.InexactFloat64(), 'f', -1, 64),
ProportionStr: strconv.FormatFloat(report.Overall.Proportion.InexactFloat64(), 'f', -1, 64),
},
Area: report.OverallArea,
BasicFee: report.BasicFee,
PooledBasicFeeByAmount: report.BasicPooledPriceConsumption.Decimal,
PooledBasicFeeByArea: report.BasicPooledPriceArea.Decimal,
AdjustFee: report.AdjustFee,
PooledAdjustFeeByAmount: report.AdjustPooledPriceConsumption.Decimal,
PooledAdjustFeeByArea: report.AdjustPooledPriceArea.Decimal,
Consumption: report.ConsumptionFee.Decimal,
//Loss: report.Loss.Decimal,
Loss: report.AuthorizeLoss.Amount,
//LossRate: report.LossFee.Decimal,
LossRate: report.AuthorizeLoss.Proportion,
}
//copier.Copy(&summaryResponse, report)
return result.Success(
"已经获取到核算报表的总览信息。",
fiber.Map{"summary": summaryResponse},
@@ -258,15 +287,45 @@ func listPublicMetersInReport(c *fiber.Ctx) error {
reportLog.Error("无法获取核算报表中的公共表计信息", zap.Error(err))
return result.Error(fiber.StatusInternalServerError, "无法获取核算报表中的公共表计信息。")
}
meterResponse := lo.Map(meters, func(meter *model.ReportDetailedPublicConsumption, _ int) *vo.ReportPublicQueryResponse {
m := &vo.ReportPublicQueryResponse{}
m.FromReportDetailPublicConsumption(meter)
return m
})
var meterResponses []vo.Public
for _, meter := range meters {
meterResponse := vo.Public{
Address: meter.Address,
AdjustLoss: model.ConsumptionUnit{
Amount: meter.LossAdjust.Amount,
Fee: meter.LossAdjust.Fee,
Price: meter.LossAdjust.Price,
Proportion: meter.LossAdjust.Proportion,
},
Area: meter.Area.Decimal.String(),
AttachedAt: meter.AttachedAt,
Building: meter.Building,
BuildingName: meter.BuildingName,
Code: meter.ParkMeterID,
DetachedAt: meter.DetachedAt,
DisplayRatio: strconv.FormatFloat(meter.DisplayRatio, 'f', -1, 64),
Enabled: meter.Enabled,
OnFloor: meter.OnFloor,
Overall: model.ConsumptionUnit{
Amount: meter.Overall.Amount,
Fee: meter.Overall.Fee,
Price: meter.Overall.Price,
Proportion: meter.Overall.Proportion,
},
ParkID: meter.ParkID,
Ratio: meter.Ratio.String(),
Seq: meter.Seq,
Type: float64(meter.MeterType),
}
meterResponses = append(meterResponses, meterResponse)
}
return result.Success(
"已经获取到指定核算报表中的分页公共表计的核算信息。",
response.NewPagedResponse(page, total).ToMap(),
fiber.Map{"public": meterResponse},
fiber.Map{"public": meterResponses},
)
}
@@ -388,7 +447,7 @@ func reportComprehensiveSearch(c *fiber.Ctx) error {
reportLog.Error("无法获取当前用户的会话信息", zap.Error(err))
return result.Unauthorized("无法获取当前用户的会话信息。")
}
park := tools.EmptyToNil(c.Query("park_id"))
park := tools.EmptyToNil(c.Query("park"))
if session.Type == model.USER_TYPE_ENT && park != nil && len(*park) > 0 {
if pass, err := checkParkBelongs(*park, reportLog, c, &result); !pass {
return err

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

@@ -25,12 +25,12 @@ func InitializeTenementHandler(router *fiber.App) {
router.Put("/tenement/:pid/:tid", security.EnterpriseAuthorize, updateTenement)
router.Get("/tenement/:pid/:tid", security.EnterpriseAuthorize, getTenementDetail)
router.Get("/tenement/:pid/:tid/meter", security.EnterpriseAuthorize, listMeters)
//TODO: 2023-07-19再apiFox上该请求是个PUT请求后端接收是个POST请求不知道是否有误或是缺少对应请求apiFox测试请求返回值为405
router.Post("/tenement/:pid/:tid/move/out", security.EnterpriseAuthorize, moveOutTenement)
//TODO: 2023-07-19再apiFox上该请求是个PUT请求后端接收是个POST请求不知道是否有误或是缺少对应请求apiFox测试请求返回值为405[完成]
router.Put("/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)
//TODO: 2023-07-19再apiFox上该请求是个PUT请求后端接收是个POST请求不知道是否有误或是缺少对应请求apiFox测试请求返回值为405[完成]
router.Put("/tenement/:pid/:tid/binding/:code/unbind", security.EnterpriseAuthorize, unbindMeterFromTenement)
}
// 列出园区中的商户

View File

@@ -8,6 +8,7 @@ import (
"electricity_bill_calc/tools"
"electricity_bill_calc/types"
"electricity_bill_calc/vo"
"fmt"
"github.com/gofiber/fiber/v2"
"github.com/jinzhu/copier"
@@ -136,7 +137,8 @@ func deleteTopUp(c *fiber.Ctx) error {
topUpLog.Error("删除一条指定的商户充值记录,删除失败", zap.Error(err))
return result.NotAccept("商户充值记录删除不成功")
}
return result.Deleted(
fmt.Println("已经删除一条指定的商户充值记录")
return result.Success(
"已经删除一条指定的商户充值记录",
)
}

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"`
@@ -42,37 +42,35 @@ type _LoginForm struct {
}
func doLogin(c *fiber.Ctx) error {
result := response.NewResult(c) //创建一个相应结果对象
loginData := new(_LoginForm) //创建一个解析登录表单数据的实体
if err := c.BodyParser(loginData); err != nil { //解析请求体中的Json数据到loginData里如果解析出错就返回错误
result := response.NewResult(c)
loginData := new(_LoginForm)
if err := c.BodyParser(loginData); err != nil {
userLog.Error("表单解析失败!", zap.Error(err))
return result.Error(http.StatusInternalServerError, "表单解析失败。") //返回一个内部服务器错误的相应结果
return result.Error(http.StatusInternalServerError, "表单解析失败。")
}
var (
session *model.Session
err error
)
userLog.Info("有用户请求登录。", zap.String("username", loginData.Username), zap.Int16("type", loginData.Type)) //记录日志相关信息
if loginData.Type == model.USER_TYPE_ENT { //根据登录类型选择不同的处理方法
session, err = service.UserService.ProcessEnterpriseUserLogin(loginData.Username, loginData.Password) //企业用户
userLog.Info("有用户请求登录。", zap.String("username", loginData.Username), zap.Int16("type", loginData.Type))
if loginData.Type == model.USER_TYPE_ENT {
session, err = service.UserService.ProcessEnterpriseUserLogin(loginData.Username, loginData.Password)
} else {
userLog.Info("该用户是管理用户")
session, err = service.UserService.ProcessManagementUserLogin(loginData.Username, loginData.Password) //管理用户
session, err = service.UserService.ProcessManagementUserLogin(loginData.Username, loginData.Password)
}
if err != nil {
if authError, ok := err.(*exceptions.AuthenticationError); ok { //检查错误是否为身份验证错误
if authError.NeedReset { //如果需要重置密码则返回对应结果
if authError, ok := err.(*exceptions.AuthenticationError); ok {
if authError.NeedReset {
return result.LoginNeedReset()
}
return result.Error(int(authError.Code), authError.Message) //返回身份验证错误相应
return result.Error(int(authError.Code), authError.Message)
} else {
userLog.Error("用户登录请求处理失败!", zap.Error(err))
return result.Error(http.StatusInternalServerError, err.Error()) //返回内部服务器错误
return result.Error(http.StatusInternalServerError, err.Error())
}
}
return result.LoginSuccess(session) //返回登录成功相应结果,包含会话信息
return result.LoginSuccess(session)
}
func doLogout(c *fiber.Ctx) error {
result := response.NewResult(c)
session, err := _retreiveSession(c)

View File

@@ -2,6 +2,7 @@ package controller
import (
"electricity_bill_calc/logger"
"electricity_bill_calc/repository"
"electricity_bill_calc/response"
"electricity_bill_calc/security"

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,9 +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)
})...)
// for index, arg := range data.Args {
// ql.logger.Info(fmt.Sprintf("[Arg %d]: %v", index, arg))
// }
return ctx
}

View File

@@ -18,8 +18,8 @@ func SetupRedisConnection() error {
a := fmt.Sprintf("%s:%d", config.RedisSettings.Host, config.RedisSettings.Port)
fmt.Println(a)
Rd, err = rueidis.NewClient(rueidis.ClientOption{
InitAddress: []string{"127.0.0.1:6379"},
Password: "",
InitAddress: []string{a},
Password: config.RedisSettings.Password,
SelectDB: config.RedisSettings.DB,
DisableCache: true,
})

17
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
@@ -12,6 +11,7 @@ require (
github.com/jinzhu/copier v0.3.5
github.com/liamylian/jsontime/v2 v2.0.0
github.com/mozillazg/go-pinyin v0.20.0
github.com/pkg/errors v0.9.1
github.com/rueian/rueidis v0.0.100
github.com/samber/lo v1.38.1
github.com/shopspring/decimal v1.3.1
@@ -19,6 +19,7 @@ require (
github.com/valyala/fasthttp v1.47.0
github.com/xuri/excelize/v2 v2.7.1
go.uber.org/zap v1.24.0
golang.org/x/sync v0.2.0
gopkg.in/natefinch/lumberjack.v2 v2.2.1
)
@@ -27,26 +28,17 @@ 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 +51,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 +59,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 +69,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

@@ -24,23 +24,23 @@ type Pooling struct {
}
type Meter struct {
Code string
Detail model.MeterDetail
CoveredArea decimal.Decimal
LastTermReading *Reading
CurrentTermReading *Reading
Overall model.ConsumptionUnit
Critical model.ConsumptionUnit
Peak model.ConsumptionUnit
Flat model.ConsumptionUnit
Valley model.ConsumptionUnit
AdjustLoss model.ConsumptionUnit
PooledBasic model.ConsumptionUnit
PooledAdjust model.ConsumptionUnit
PooledLoss model.ConsumptionUnit
PooledPublic model.ConsumptionUnit
SharedPoolingProportion decimal.Decimal
Poolings []*Pooling
Code string `json:"code"`
Detail model.MeterDetail `json:"detail"`
CoveredArea decimal.Decimal `json:"covered_area"`
LastTermReading *Reading `json:"last_term_reading"`
CurrentTermReading *Reading `json:"current_term_reading"`
Overall model.ConsumptionUnit `json:"overall"`
Critical model.ConsumptionUnit `json:"critical"`
Peak model.ConsumptionUnit `json:"peak"`
Flat model.ConsumptionUnit `json:"flat"`
Valley model.ConsumptionUnit `json:"valley"`
AdjustLoss model.ConsumptionUnit `json:"adjust_loss"`
PooledBasic model.ConsumptionUnit `json:"pooled_basic"`
PooledAdjust model.ConsumptionUnit `json:"pooled_adjust"`
PooledLoss model.ConsumptionUnit `json:"pooled_loss"`
PooledPublic model.ConsumptionUnit `json:"pooled_public"`
SharedPoolingProportion decimal.Decimal `json:"shared_pooling_proportion"`
Poolings []*Pooling `json:"poolings"`
}
type PrimaryTenementStatistics struct {
@@ -49,19 +49,20 @@ type PrimaryTenementStatistics struct {
}
type TenementCharge struct {
Tenement string
Overall model.ConsumptionUnit
Critical model.ConsumptionUnit
Peak model.ConsumptionUnit
Flat model.ConsumptionUnit
Valley model.ConsumptionUnit
BasicFee decimal.Decimal
AdjustFee decimal.Decimal
LossPooled decimal.Decimal
PublicPooled decimal.Decimal
FinalCharges decimal.Decimal
Submeters []*Meter
Poolings []*Meter
Tenement string `json:"tenement"`
Overall model.ConsumptionUnit `json:"overall"`
Critical model.ConsumptionUnit `json:"critical"`
Peak model.ConsumptionUnit `json:"peak"`
Flat model.ConsumptionUnit `json:"flat"`
Valley model.ConsumptionUnit `json:"valley"`
BasicFee decimal.Decimal `json:"basic_fee"`
AdjustFee decimal.Decimal `json:"adjust_fee"`
LossPooled decimal.Decimal `json:"loss_pooled"`
PublicPooled decimal.Decimal `json:"public_pooled"`
FinalCharges decimal.Decimal `json:"final_charges"`
Loss model.ConsumptionUnit `json:"loss"`
Submeters []*Meter `json:"submeters"`
Poolings []*Meter `json:"poolings"`
}
type Summary struct {

View File

@@ -22,6 +22,7 @@ type MeterDetail struct {
DetachedAt *types.DateTime `json:"detachedAt" db:"detached_at"`
CreatedAt types.DateTime `json:"createdAt" db:"created_at"`
LastModifiedAt types.DateTime `json:"lastModifiedAt" db:"last_modified_at"`
DisplayRatio float64 `json:"display_ratio" db:"display_ratio"`
}
type MeterRelation struct {
@@ -104,3 +105,24 @@ type ReadingImportRow struct {
Peak decimal.NullDecimal `json:"peak" excel:"peak"`
Valley decimal.NullDecimal `json:"valley" excel:"valley"`
}
type ReadAbleMeter struct {
CreatedAt types.DateTime `json:"createdAt" db:"created_at"`
LastModifiedAt types.DateTime `json:"lastModifiedAt" db:"last_modified_at"`
Code string `json:"code" db:"code"`
Park string `json:"parkId" db:"park_id"`
Address *string `json:"address" db:"address"`
Ratio decimal.Decimal `json:"ratio" db:"ratio"`
Seq int64 `json:"seq" db:"seq"`
Enabled bool `json:"enabled" db:"enabled"`
MeterType int16 `json:"type" db:"meter_type"`
Building *string `json:"building" db:"building"`
OnFloor *string `json:"onFloor" db:"on_floor" `
Area decimal.NullDecimal `json:"area" db:"area"`
AttachedAt *types.DateTime `json:"attachedAt" db:"attached_at"`
DetachedAt *types.DateTime `json:"detachedAt" db:"detached_at"`
DisplayRatio decimal.Decimal `db:"display_ratio"`
BuildingName *string `db:"building_name"`
}

View File

@@ -2,6 +2,7 @@ package model
import (
"electricity_bill_calc/types"
"time"
"github.com/shopspring/decimal"
@@ -31,11 +32,7 @@ type Park struct {
CreatedAt time.Time `json:"createdAt"`
LastModifiedAt time.Time `json:"lastModifiedAt"`
DeletedAt *time.Time `json:"deletedAt"`
}
type Parks struct {
Park
NormAuthorizedLossRate float64 `json:"norm_authorized_loss_rate"`
NormAuthorizedLossRate float64 `json:"normAuthorizedLoss"db:"norm_authorized_loss_rate"`
}
type ParkPeriodStatistics struct {

View File

@@ -122,6 +122,7 @@ type ReportTenement struct {
Invoice []string `json:"invoice" db:"invoice"`
Meters []NestedMeter `json:"meters" db:"meters"`
Pooled []NestedMeter `json:"pooled" db:"pooled"`
Loss ConsumptionUnit `json:"loss"` //TODO: 2023.08.11 测试时发现少一个字段(已补全)
}
type ReportTask 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

@@ -7,7 +7,7 @@ type Tenement struct {
Park string `json:"parkId" db:"park_id"`
FullName string `json:"fullName" db:"full_name"`
ShortName *string `json:"shortName" db:"short_name"`
Abbr string `json:"-"`
Abbr string `json:"abbr"`
Address string `json:"address"`
ContactName string `json:"contactName" db:"contact_name"`
ContactPhone string `json:"contactPhone" db:"contact_phone"`

View File

@@ -1,10 +1,18 @@
package repository
import (
"context"
"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"
"time"
"github.com/doug-martin/goqu/v9"
@@ -23,6 +31,43 @@ var CalculateRepository = _CalculateRepository{
ds: goqu.Dialect("postgres"),
}
// 更新当前报表的核算状态
func (cr _CalculateRepository) UpdateReportCalculateStatus(rid string, status string,
message string) (bool, error) {
ctx, cancel := global.TimeoutContext()
defer cancel()
var atio int
var err error
currentTime := time.Now()
if status == "success" {
atio = 1 //创建报表成功
} else {
atio = 2 // 数据不足
}
updateResultSql, updateResultArgs, _ := cr.ds.
Update(goqu.T("report_task")).
Set(goqu.Record{
"status": int16(atio),
"last_modified_at": currentTime,
"message": message,
}).Where(goqu.I("id").Eq(rid)).
ToSQL()
res, err := global.DB.Exec(ctx, updateResultSql, updateResultArgs...)
if err != nil {
cr.log.Error("未能更新当前报表的核算状态", zap.Error(err))
return false, err
}
if res.RowsAffected() == 0 {
cr.log.Warn("未能保存当前报表的核算状态", zap.String("Report", rid))
return false, nil
}
return true, nil
}
// 获取当前正在等待计算的核算任务ID列表
func (cr _CalculateRepository) ListPendingTasks() ([]string, error) {
cr.log.Info("获取当前正在等待计算的核算任务ID列表")
@@ -86,7 +131,7 @@ func (cr _CalculateRepository) GetAllPoolingMeterRelations(pid string, revokedAf
var meterRelation []model.MeterRelation
err := pgxscan.Select(ctx, global.DB, meterRelation, relationsSql, relationsArgs...)
err := pgxscan.Select(ctx, global.DB, &meterRelation, relationsSql, relationsArgs...)
if err != nil {
cr.log.Error("获取当前园区中所有公摊表计与商户表计之间的关联关系,包括已经解除的出错", zap.Error(err))
return nil, err
@@ -104,21 +149,24 @@ func (cr _CalculateRepository) GetAllTenementMeterRelations(pid string, associat
From(goqu.T("tenement_meter")).
Where(goqu.I("park_id").Eq(pid)).
Where(goqu.And(
goqu.I("associated_at").IsNull(),
goqu.I("associated_at").IsNotNull(),
goqu.I("associated_at").Lte(associatedBefore),
)).
Where(goqu.And(
goqu.I("associated_at").IsNull(),
goqu.I("associated_at").Gte(disassociatedAfter),
goqu.Or(
goqu.I("disassociated_at").IsNull(),
goqu.I("disassociated_at").Gte(disassociatedAfter),
),
)).ToSQL()
var tenementMeter []model.TenementMeter
err := pgxscan.Select(ctx, global.DB, tenementMeter, relationsQuerySql, relationsQueryArgs...)
err := pgxscan.Select(ctx, global.DB, &tenementMeter, relationsQuerySql, relationsQueryArgs...)
if err != nil {
cr.log.Error("获取当前园区中所有的商户与表计的关联关系,包括已经解除的", zap.Error(err))
return nil, err
}
fmt.Println("==", tenementMeter)
return tenementMeter, nil
}
@@ -140,13 +188,13 @@ func (cr _CalculateRepository) GetMeterReadings(rid string, meterType int16) ([]
goqu.I("r.id").Eq(rid),
goqu.I("mr.meter_type").Eq(meterType),
// TODO2023.08.02 此方法出错优先查看是否这里出问题
goqu.I("mr.read_at::date <@ r.period"),
goqu.L("?::date <@ ?", goqu.I("mr.read_at"), goqu.I("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...)
err := pgxscan.Select(ctx, global.DB, &readings, readingsQuerySql, readingsQueryArgs...)
if err != nil {
cr.log.Error("获取指定报表中所有涉及到的指定类型表计在核算时间段内的所有读数数据出错", zap.Error(err))
return nil, err
@@ -161,7 +209,8 @@ func (cr _CalculateRepository) GetLastPeriodReadings(rid string, meterType int16
ctx, cancel := global.TimeoutContext()
defer cancel()
readingsSql, readingsArgs, _ := cr.ds.From(goqu.T("meter_reading").As("mr")).
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"),
@@ -181,7 +230,7 @@ func (cr _CalculateRepository) GetLastPeriodReadings(rid string, meterType int16
Where(
goqu.I("r.id").Eq(rid),
goqu.I("mr.meter_type").Eq(meterType),
goqu.I(" mr.read_at::date <= lower(r.period)"),
goqu.L(" read_at <= lower(r.period)"),
).
GroupBy(
goqu.I("mr.park_id"),
@@ -197,11 +246,12 @@ func (cr _CalculateRepository) GetLastPeriodReadings(rid string, meterType int16
).ToSQL()
var readings []model.MeterReading
err := pgxscan.Select(ctx, global.DB, readings, readingsSql, readingsArgs...)
err := pgxscan.Select(ctx, global.DB, &readings, readingsSql, readingsArgs...)
if err != nil {
cr.log.Error("获取指定报表中所有涉及到的表计在核算起始日期前的最后一次读数出错", zap.Error(err))
return nil, err
}
return readings, nil
}
@@ -228,14 +278,299 @@ func (cr _CalculateRepository) GetAllTenements(rid string) ([]model.Tenement, er
).
Where(
goqu.I("r.id").Eq(rid),
goqu.I("t.moved_in_at <= upper(r.period)"),
goqu.L("t.moved_in_at <= upper(r.period)"),
).ToSQL()
fmt.Println(tenementQuerySql)
var tenements []model.Tenement
err := pgxscan.Select(ctx, global.DB, tenements, tenementQuerySql, tenementQueryArgs...)
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, ctx context.Context, rid string, meters []calculate.Meter) error {
if len(meters) == 0 {
// 如果没有公共表计则直接返回
return nil
}
for _, meter := range meters {
// 准备插入表达式
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",
)
// 添加值到插入表达式中
overall, _ := json.Marshal(meter.Overall)
criyical, _ := json.Marshal(meter.Critical)
peak, _ := json.Marshal(meter.Peak)
flat, _ := json.Marshal(meter.Flat)
valley, _ := json.Marshal(meter.Valley)
adjustLoss, _ := json.Marshal(meter.AdjustLoss)
insertExpr = insertExpr.Vals(goqu.Vals{
rid,
meter.Code,
overall,
criyical,
peak,
flat,
valley,
adjustLoss,
meter.Overall.Fee,
meter.AdjustLoss.Fee,
meter.Overall.Fee.Add(meter.AdjustLoss.Fee),
})
// 执行插入语句
inserSql, insertArgs, _ := insertExpr.ToSQL()
_, err := tx.Exec(ctx, inserSql, insertArgs...)
if err != nil {
_ = tx.Rollback(ctx)
return fmt.Errorf("保存报表核算概要失败: %w", err)
}
}
return nil
}
func (cr _CalculateRepository) SaveReportSummary(tx pgx.Tx, ctx context.Context, summary calculate.Summary) error {
// 构建插入表达式
Overall, _ := json.Marshal(summary.Overall)
Critical, _ := json.Marshal(summary.Critical)
Peak, _ := json.Marshal(summary.Peak)
Flat, _ := json.Marshal(summary.Flat)
Valley, _ := json.Marshal(summary.Valley)
AuthoizeLoss, _ := json.Marshal(summary.AuthoizeLoss)
insertsql, insertArgs, err := cr.ds.Insert(goqu.T("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, Overall, Critical, Peak, Flat,
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, AuthoizeLoss, summary.OverallArea, summary.TotalConsumption,
}).ToSQL()
if err != nil {
fmt.Println(err)
return err
}
// 执行插入语句
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
}
overall, _ := json.Marshal(meter.Overall)
criyical, _ := json.Marshal(meter.Critical)
peak, _ := json.Marshal(meter.Peak)
flat, _ := json.Marshal(meter.Flat)
valley, _ := json.Marshal(meter.Valley)
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, overall, criyical, peak, flat, 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")
var rows []goqu.Record
for _, tenement := range tenements {
tenementCharge := findTenementCharge(tenementCharges, tenement.Id)
tenementDetail, _ := json.Marshal(tenement)
overallJSON, _ := json.Marshal(tenementCharge.Overall)
criticalJSON, _ := json.Marshal(tenementCharge.Critical)
peakJSON, _ := json.Marshal(tenementCharge.Peak)
flatJSON, _ := json.Marshal(tenementCharge.Flat)
valleyJSON, _ := json.Marshal(tenementCharge.Valley)
lossJSON, _ := json.Marshal(tenementCharge.Loss)
submetersJSON, _ := json.Marshal(convertToNestedMeters(tenementCharge.Submeters))
poolingsJSON, _ := json.Marshal(convertToNestedMeters(tenementCharge.Poolings))
row := goqu.Record{
"report_id": report.Id,
"tenement_id": tenement.Id,
"tenement_detail": tenementDetail,
"calc_period": report.Period,
"overall": overallJSON,
"critical": criticalJSON,
"peak": peakJSON,
"flat": flatJSON,
"valley": valleyJSON,
"loss": lossJSON,
"basic_fee_pooled": tenementCharge.BasicFee,
"adjust_fee_pooled": tenementCharge.AdjustFee,
"loss_fee_pooled": tenementCharge.LossPooled,
"final_pooled": tenementCharge.PublicPooled,
"final_charge": tenementCharge.FinalCharges,
"meters": submetersJSON,
"pooled": poolingsJSON,
}
rows = append(rows, row)
}
sql, params, err := insertQuery.Rows(rows).Prepared(true).ToSQL()
if err != nil {
fmt.Println(err)
}
_, err = tx.Exec(ctx, sql, params...)
if err != nil {
fmt.Println(err.Error())
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
}

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

View File

@@ -12,13 +12,13 @@ import (
"electricity_bill_calc/types"
"electricity_bill_calc/vo"
"fmt"
"github.com/doug-martin/goqu/v9"
_ "github.com/doug-martin/goqu/v9/dialect/postgres"
"github.com/georgysavva/scany/v2/pgxscan"
"github.com/jackc/pgx/v5"
"github.com/shopspring/decimal"
"go.uber.org/zap"
"log"
)
type _MeterRepository struct {
@@ -121,11 +121,10 @@ func (mr _MeterRepository) AllUsedMetersInReport(rid string) ([]*model.MeterDeta
}
// 分页列出指定园区下的表计信息
func (mr _MeterRepository) MetersIn(pid string, page uint, keyword *string) ([]*model.MeterDetail, int64, error) {
func (mr _MeterRepository) MetersIn(pid string, page uint, keyword *string, mtype int) ([]*model.MeterDetail, int64, error) {
mr.log.Info("分页列出指定园区下的表计信息", zap.String("park id", pid), zap.Uint("page", page), zap.String("keyword", tools.DefaultTo(keyword, "")))
ctx, cancel := global.TimeoutContext()
defer cancel()
meterQuery := mr.ds.
From(goqu.T("meter_04kv").As("m")).
LeftJoin(goqu.T("park_building").As("b"), goqu.On(goqu.I("m.building").Eq(goqu.I("b.id")))).
@@ -143,7 +142,10 @@ func (mr _MeterRepository) MetersIn(pid string, page uint, keyword *string) ([]*
goqu.I("m.park_id").Eq(pid),
goqu.I("m.detached_at").IsNull(),
)
if mtype != 3 {
meterQuery = meterQuery.Where(goqu.I("m.meter_type").Eq(uint(mtype)))
countQuery = countQuery.Where(goqu.I("m.meter_type").Eq(uint(mtype)))
}
if keyword != nil && len(*keyword) > 0 {
pattern := fmt.Sprintf("%%%s%%", *keyword)
meterQuery = meterQuery.Where(
@@ -174,11 +176,11 @@ func (mr _MeterRepository) MetersIn(pid string, page uint, keyword *string) ([]*
mr.log.Error("查询表计信息失败", zap.Error(err))
return make([]*model.MeterDetail, 0), 0, err
}
if err := pgxscan.Get(ctx, global.DB, &total, countSql, countArgs...); err != nil {
mr.log.Error("查询表计数量失败", zap.Error(err))
return make([]*model.MeterDetail, 0), 0, err
}
return meters, total, nil
}
@@ -209,12 +211,12 @@ func (mr _MeterRepository) ListMetersByIDs(pid string, ids []string) ([]*model.M
mr.log.Error("查询表计信息失败", zap.Error(err))
return make([]*model.MeterDetail, 0), err
}
return meters, nil
}
// 获取指定表计的详细信息
func (mr _MeterRepository) FetchMeterDetail(pid, code string) (*model.MeterDetail, error) {
log.Println("获取指定标记的详细信息1111111111111111")
mr.log.Info("获取指定表计的详细信息", zap.String("park id", pid), zap.String("meter code", code))
ctx, cancel := global.TimeoutContext()
defer cancel()
@@ -231,7 +233,7 @@ func (mr _MeterRepository) FetchMeterDetail(pid, code string) (*model.MeterDetai
goqu.I("m.code").Eq(code),
).
Prepared(true).ToSQL()
log.Println("111111111111111111111111", meterSql)
if err := pgxscan.Get(ctx, global.DB, &meter, meterSql, meterArgs...); err != nil {
mr.log.Error("查询表计信息失败", zap.Error(err))
return nil, err
@@ -242,8 +244,10 @@ func (mr _MeterRepository) FetchMeterDetail(pid, code string) (*model.MeterDetai
// 创建一条新的表计信息
func (mr _MeterRepository) CreateMeter(tx pgx.Tx, ctx context.Context, pid string, meter vo.MeterCreationForm) (bool, error) {
log.Println("创建一条新的表记55555555555555555555")
mr.log.Info("创建一条新的表计信息", zap.String("park id", pid), zap.String("meter code", meter.Code))
timeNow := types.Now()
//timeNow := types.Now()
meterSql, meterArgs, _ := mr.ds.
Insert(goqu.T("meter_04kv")).
Cols(
@@ -251,8 +255,9 @@ func (mr _MeterRepository) CreateMeter(tx pgx.Tx, ctx context.Context, pid strin
"attached_at", "created_at", "last_modified_at",
).
Vals(
goqu.Vals{pid, meter.Code, meter.Address, meter.Ratio, meter.Seq, meter.MeterType, meter.Building, meter.OnFloor, meter.Area, meter.Enabled,
timeNow, timeNow, timeNow,
goqu.Vals{pid, meter.Code, meter.Address, meter.Ratio, meter.Seq, meter.MeterType, meter.Building, meter.OnFloor,
meter.Area, meter.Enabled,
meter.Reading.ReadAt, meter.Reading.ReadAt, meter.Reading.ReadAt,
},
).
Prepared(true).ToSQL()
@@ -262,6 +267,7 @@ func (mr _MeterRepository) CreateMeter(tx pgx.Tx, ctx context.Context, pid strin
mr.log.Error("创建表计信息失败", zap.Error(err))
return false, err
}
log.Println("555555555555", meterSql)
return ok.RowsAffected() > 0, nil
}
@@ -304,6 +310,7 @@ func (mr _MeterRepository) CreateOrUpdateMeter(tx pgx.Tx, ctx context.Context, p
// 记录一条表计的抄表信息
func (mr _MeterRepository) RecordReading(tx pgx.Tx, ctx context.Context, pid, code string, meterType int16, ratio decimal.Decimal, reading *vo.MeterReadingForm) (bool, error) {
log.Println("记录一条表记的抄表信息22222222222222222222")
mr.log.Info("记录一条表计的抄表信息", zap.String("park id", pid), zap.String("meter code", code))
readAt := tools.DefaultTo(reading.ReadAt, types.Now())
readingSql, readingArgs, _ := mr.ds.
@@ -315,6 +322,7 @@ func (mr _MeterRepository) RecordReading(tx pgx.Tx, ctx context.Context, pid, co
goqu.Vals{pid, code, readAt, meterType, ratio, reading.Overall, reading.Critical, reading.Peak, reading.Flat, reading.Valley},
).
Prepared(true).ToSQL()
log.Println("22222222222222222222", readingSql)
ok, err := tx.Exec(ctx, readingSql, readingArgs...)
if err != nil {
@@ -390,6 +398,7 @@ func (mr _MeterRepository) ListMeterCodes(pid string) ([]string, error) {
// 解除指定园区中指定表计的使用
func (mr _MeterRepository) DetachMeter(tx pgx.Tx, ctx context.Context, pid, code string) (bool, error) {
log.Println("解除指定园区的指定表记使用33333333333333333333")
mr.log.Info("解除指定园区中指定表计的使用", zap.String("park id", pid), zap.String("meter code", code))
timeNow := types.Now()
meterSql, meterArgs, _ := mr.ds.
@@ -405,7 +414,7 @@ func (mr _MeterRepository) DetachMeter(tx pgx.Tx, ctx context.Context, pid, code
goqu.I("code").Eq(code),
).
Prepared(true).ToSQL()
log.Println("3333333333333333", meterSql)
ok, err := tx.Exec(ctx, meterSql, meterArgs...)
if err != nil {
mr.log.Error("解除表计使用失败", zap.Error(err))
@@ -467,6 +476,7 @@ func (mr _MeterRepository) BindMeter(tx pgx.Tx, ctx context.Context, pid, master
// 解除两个表计之间的关联
func (mr _MeterRepository) UnbindMeter(tx pgx.Tx, ctx context.Context, pid, masterMeter, slaveMeter string) (bool, error) {
log.Println("解除两个标记之间的关系》》》》》》》》》》》》》》》》》》》》》》》4444444444")
mr.log.Info("解除两个表计之间的关联", zap.String("master meter code", masterMeter), zap.String("slave meter code", slaveMeter))
relationSql, relationArgs, _ := mr.ds.
Update(goqu.T("meter_relations")).
@@ -482,14 +492,14 @@ func (mr _MeterRepository) UnbindMeter(tx pgx.Tx, ctx context.Context, pid, mast
goqu.I("revoked_at").IsNull(),
).
Prepared(true).ToSQL()
log.Println("4444444444444444", relationSql)
ok, err := tx.Exec(ctx, relationSql, relationArgs...)
if err != nil {
mr.log.Error("解除表计关系失败", zap.Error(err))
return false, err
}
return ok.RowsAffected() > 0, nil
return ok.RowsAffected() >= 0, nil
}
// 列出指定公摊表计的所有关联表计关系
@@ -524,6 +534,9 @@ func (mr _MeterRepository) ListPooledMeterRelationsByCodes(pid string, codes []s
defer cancel()
var relations []*model.MeterRelation
if len(codes) <= 0 {
return relations, nil
}
relationsSql, relationsArgs, _ := mr.ds.
From(goqu.T("meter_relations").As("r")).
Select("r.*").
@@ -550,7 +563,7 @@ func (mr _MeterRepository) ListMeterRelations(pid, code string) ([]*model.MeterR
var relations []*model.MeterRelation
relationsSql, relationsArgs, _ := mr.ds.
From(goqu.T("meter_relations")).
From(goqu.T("meter_relations").As("r")).
Select("*").
Where(
goqu.I("r.park_id").Eq(pid),
@@ -774,25 +787,31 @@ func (mr _MeterRepository) ListUnboundTenementMeters(uid string, pid *string, ke
}
// 查询指定园区中的符合条件的抄表记录
func (mr _MeterRepository) ListMeterReadings(pid string, keyword *string, page uint, start, end *types.Date, buidling *string) ([]*model.MeterReading, int64, error) {
func (mr _MeterRepository) ListMeterReadings(pid string, keyword *string, page uint, start, end *types.Date, buidling *string, mtype uint) ([]*model.MeterReading, int64, error) {
mr.log.Info("查询指定园区中的符合条件的抄表记录", zap.String("park id", pid), zap.String("keyword", tools.DefaultTo(keyword, "")), zap.Uint("page", page), logger.DateFieldp("start", start), logger.DateFieldp("end", end), zap.String("building", tools.DefaultTo(buidling, "")))
ctx, cancel := global.TimeoutContext()
defer cancel()
readingQuery := mr.ds.
From(goqu.T("meter_reading").As("r")).
LeftJoin(goqu.T("meter_04kv").As("m"), goqu.On(goqu.I("r.meter_id").Eq(goqu.I("m.code")))).
LeftJoin(goqu.T("meter_04kv").As("m"), goqu.On(goqu.I("r.meter_id").Eq(goqu.I("m.code")),
goqu.I("m.park_id").Eq(goqu.I("r.park_id")))).
Select("r.*").
Where(
goqu.I("r.park_id").Eq(pid),
)
countQuery := mr.ds.
From(goqu.T("meter_reading").As("r")).
LeftJoin(goqu.T("meter_04kv").As("m"), goqu.On(goqu.I("r.meter_id").Eq(goqu.I("m.code")))).
LeftJoin(goqu.T("meter_04kv").As("m"), goqu.On(goqu.I("r.meter_id").Eq(goqu.I("m.code")),
goqu.I("m.park_id").Eq(goqu.I("r.park_id")))).
Select(goqu.COUNT("*")).
Where(
goqu.I("r.park_id").Eq(pid),
)
if mtype != 3 {
readingQuery = readingQuery.Where(goqu.I("m.meter_type").Eq(mtype))
countQuery = countQuery.Where(goqu.I("m.meter_type").Eq(mtype))
}
if keyword != nil && len(*keyword) > 0 {
pattern := fmt.Sprintf("%%%s%%", *keyword)
@@ -838,7 +857,8 @@ func (mr _MeterRepository) ListMeterReadings(pid string, keyword *string, page u
}
startRow := (page - 1) * config.ServiceSettings.ItemsPageSize
readingQuery = readingQuery.Order(goqu.I("r.read_at").Desc()).Offset(startRow).Limit(config.ServiceSettings.ItemsPageSize)
readingQuery = readingQuery.Order(goqu.I("r.read_at").Desc()).Offset(startRow).
Limit(config.ServiceSettings.ItemsPageSize)
readingSql, readingArgs, _ := readingQuery.Prepared(true).ToSQL()
countSql, countArgs, _ := countQuery.Prepared(true).ToSQL()
@@ -855,7 +875,6 @@ func (mr _MeterRepository) ListMeterReadings(pid string, keyword *string, page u
mr.log.Error("查询抄表记录数量失败", zap.Error(err))
return make([]*model.MeterReading, 0), 0, err
}
return readings, total, nil
}
@@ -989,3 +1008,43 @@ func (mr _MeterRepository) ListMeterDocForTemplate(pid string) ([]*model.SimpleM
return docs, nil
}
// 列出目前园区中可以作为抄表目标的表计
func (mr _MeterRepository) ListReadableMeters(pid string, keyword *string, limit uint) ([]*model.ReadAbleMeter, error) {
mr.log.Info("列出目前园区中可以作为抄表目标的表计",
zap.String("park_id", pid),
zap.Uint("limit", limit),
zap.Stringp("keyword", keyword))
ctx, cancel := global.TimeoutContext()
defer cancel()
//SELECT *
// FROM "meter_04kv" AS "m"
//LEFT JOIN "park_building" AS "b" ON ("b"."id" = "m"."building")
//WHERE (("m"."park_id" = "b"."id") AND ("m"."detached_at" IS NULL))
//ORDER BY "m"."attached_at" DESC
//LIMIT 20;
//query:=`SELECT m.*, b.name as building_name FROM meter_04kv as m LEFT JOIN park_building as b ON b.id = m.building WHERE m.park_id=$1 AND m.detached_at IS NULL`
meterQuery := mr.ds.From(goqu.T("meter_04kv").As("m")).
LeftJoin(goqu.T("park_building").As("b"), goqu.On(goqu.I("m.building").Eq(goqu.I("b.id")))).
Select("m.*", goqu.I("b.name").As("building_name")).
Where(goqu.I("m.park_id").
Eq(pid), goqu.I("m.detached_at").IsNull())
if keyword != nil && len(*keyword) > 0 {
pattern := fmt.Sprintf("%%%s%%", *keyword)
meterQuery = meterQuery.Where(
goqu.Or(
goqu.I("m.code").ILike(pattern),
goqu.I("m.address").ILike(pattern),
),
)
}
meterQuery = meterQuery.Order(goqu.I("m.attached_at").Desc()).Limit(limit)
meterSql, meterArgs, _ := meterQuery.Prepared(true).ToSQL()
var meters []*model.ReadAbleMeter
if err := pgxscan.Select(ctx, global.DB, &meters, meterSql, meterArgs...); err != nil {
mr.log.Error("查询商户表计信息失败", zap.Error(err))
return make([]*model.ReadAbleMeter, 0), err
}
return meters, nil
}

View File

@@ -8,7 +8,6 @@ import (
"electricity_bill_calc/tools"
"electricity_bill_calc/tools/serial"
"electricity_bill_calc/types"
"github.com/doug-martin/goqu/v9"
_ "github.com/doug-martin/goqu/v9/dialect/postgres"
"github.com/georgysavva/scany/v2/pgxscan"
@@ -39,7 +38,7 @@ func (pr _ParkRepository) ListAllParks(uid string) ([]*model.Park, error) {
"id", "user_id", "name", "area", "tenement_quantity", "capacity", "category",
"meter_04kv_type", "region", "address", "contact", "phone", "enabled", "price_policy", "tax_rate",
"basic_pooled", "adjust_pooled", "loss_pooled", "public_pooled", "created_at", "last_modified_at",
"deleted_at",
"deleted_at","norm_authorized_loss_rate",
).
Where(
goqu.I("user_id").Eq(uid),

View File

@@ -9,7 +9,11 @@ import (
"electricity_bill_calc/tools/serial"
"electricity_bill_calc/types"
"electricity_bill_calc/vo"
"encoding/json"
"errors"
"fmt"
"log"
"github.com/doug-martin/goqu/v9"
_ "github.com/doug-martin/goqu/v9/dialect/postgres"
@@ -100,7 +104,7 @@ func (rr _ReportRepository) GetReportIndex(rid string) (*model.ReportIndex, erro
}
// 为指园区创建一个新的核算报表
func (rr _ReportRepository) CreateReport(form *vo.ReportCreationForm) (bool, error) {
func (rr _ReportRepository) CreateReport(form *vo.ReportCreationForm) (bool, string, error) {
rr.log.Info("为指定园区创建一个新的核算报表", zap.String("Park", form.Park))
ctx, cancel := global.TimeoutContext()
defer cancel()
@@ -108,13 +112,13 @@ func (rr _ReportRepository) CreateReport(form *vo.ReportCreationForm) (bool, err
tx, err := global.DB.Begin(ctx)
if err != nil {
rr.log.Error("未能开始一个数据库事务", zap.Error(err))
return false, err
return false, "", err
}
park, err := ParkRepository.RetrieveParkDetail(form.Park)
if err != nil || park == nil {
rr.log.Error("未能获取指定园区的详细信息", zap.Error(err))
tx.Rollback(ctx)
return false, exceptions.NewNotFoundErrorFromError("未能获取指定园区的详细信息", err)
return false, "", exceptions.NewNotFoundErrorFromError("未能获取指定园区的详细信息", err)
}
createTime := types.Now()
periodRange := types.NewDateRange(&form.PeriodBegin, &form.PeriodEnd)
@@ -124,8 +128,8 @@ func (rr _ReportRepository) CreateReport(form *vo.ReportCreationForm) (bool, err
createSql, createArgs, _ := rr.ds.
Insert(goqu.T("report")).
Cols(
"id", "park_id", "period", "category", "meter_o4kv_type", "price_policy",
"basic_pooled", "adjust_pooled", "loss_pooled", "public_pooled", "created_at",
"id", "park_id", "period", "category", "meter_04kv_type", "price_policy",
"basis_pooled", "adjust_pooled", "loss_pooled", "public_pooled", "created_at",
"last_modified_at",
).
Vals(goqu.Vals{
@@ -134,38 +138,45 @@ func (rr _ReportRepository) CreateReport(form *vo.ReportCreationForm) (bool, err
createTime,
}).
Prepared(true).ToSQL()
summarySql, summaryArgs, _ := rr.ds.
Insert(goqu.T("report_summary")).
Cols(
"report_id", "overall", "critical", "peak", "flat", "valley", "basic_fee",
"adjust_fee",
).
Vals(goqu.Vals{
reportId,
model.ConsumptionUnit{
Amount: form.Overall,
Fee: form.OverallFee,
},
model.ConsumptionUnit{
critical := model.ConsumptionUnit{
Amount: form.Critical,
Fee: form.CriticalFee,
},
model.ConsumptionUnit{
}
criticalData, _ := json.Marshal(critical)
overall := model.ConsumptionUnit{
Amount: form.Overall,
Fee: form.OverallFee,
}
overallData, _ := json.Marshal(overall)
peak := model.ConsumptionUnit{
Amount: form.Peak,
Fee: form.PeakFee,
},
model.ConsumptionUnit{
}
peakData, _ := json.Marshal(peak)
flat := model.ConsumptionUnit{
Amount: form.Flat,
Fee: form.FlatFee,
},
model.ConsumptionUnit{
}
flatData, _ := json.Marshal(flat)
valley := model.ConsumptionUnit{
Amount: form.Valley,
Fee: form.ValleyFee,
},
form.BasicFee,
form.AdjustFee,
}).
Prepared(true).ToSQL()
}
valleyData, _ := json.Marshal(valley)
summarySql, summaryArgs, err5 := rr.ds.
Insert(goqu.T("report_summary")).
//Cols("report_id", "overall", "critical", "peak", "flat", "valley", "basic_fee", "adjust_fee").
Rows(goqu.Record{
"report_id": reportId,
"overall": string(overallData),
"critical": string(criticalData),
"peak": string(peakData),
"flat": string(flatData),
"valley": string(valleyData),
"basic_fee": form.BasicFee,
"adjust_fee": form.AdjustFee,
}).Prepared(true).ToSQL()
log.Println("errrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr:", err5)
taskSql, taskArgs, _ := rr.ds.
Insert(goqu.T("report_task")).
Cols("id", "status", "last_modified_at").
@@ -176,42 +187,45 @@ func (rr _ReportRepository) CreateReport(form *vo.ReportCreationForm) (bool, err
if err != nil {
rr.log.Error("创建核算报表索引时出现错误", zap.Error(err))
tx.Rollback(ctx)
return false, err
return false, "", err
}
if resIndex.RowsAffected() == 0 {
rr.log.Error("保存核算报表索引时出现错误", zap.Error(err))
tx.Rollback(ctx)
return false, exceptions.NewUnsuccessCreateError("创建核算报表索引时出现错误")
return false, "", exceptions.NewUnsuccessCreateError("创建核算报表索引时出现错误")
}
resSummary, err := tx.Exec(ctx, summarySql, summaryArgs...)
if err != nil {
rr.log.Error("创建核算报表汇总时出现错误", zap.Error(err))
tx.Rollback(ctx)
return false, err
return false, "", err
}
log.Println("?????????", summarySql, summaryArgs)
log.Println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>", resSummary.RowsAffected())
if resSummary.RowsAffected() == 0 {
rr.log.Error("保存核算报表汇总时出现错误", zap.Error(err))
tx.Rollback(ctx)
return false, exceptions.NewUnsuccessCreateError("创建核算报表汇总时出现错误")
return false, "", exceptions.NewUnsuccessCreateError("创建核算报表汇总时出现错误")
}
resTask, err := tx.Exec(ctx, taskSql, taskArgs...)
if err != nil {
rr.log.Error("创建核算报表任务时出现错误", zap.Error(err))
tx.Rollback(ctx)
return false, err
return false, "", err
}
if resTask.RowsAffected() == 0 {
rr.log.Error("保存核算报表任务时出现错误", zap.Error(err))
tx.Rollback(ctx)
return false, exceptions.NewUnsuccessCreateError("创建核算报表任务时出现错误")
return false, "", exceptions.NewUnsuccessCreateError("创建核算报表任务时出现错误")
}
err = tx.Commit(ctx)
if err != nil {
rr.log.Error("提交核算报表创建事务时出现错误", zap.Error(err))
tx.Rollback(ctx)
return false, err
return false, "", err
}
return resIndex.RowsAffected() > 0 && resSummary.RowsAffected() > 0 && resTask.RowsAffected() > 0, nil
return resIndex.RowsAffected() > 0 && resSummary.RowsAffected() > 0 && resTask.RowsAffected() > 0, reportId, nil
}
// 更新报表的基本信息
@@ -295,7 +309,6 @@ func (rr _ReportRepository) RetrieveReportSummary(rid string) (*model.ReportSumm
Select("*").
Where(goqu.I("report_id").Eq(rid)).
Prepared(true).ToSQL()
var summary model.ReportSummary
if err := pgxscan.Get(ctx, global.DB, &summary, querySql, queryParams...); err != nil {
rr.log.Error("获取指定报表的总览信息时出现错误", zap.Error(err))
@@ -332,23 +345,31 @@ func (rr _ReportRepository) GetReportTaskStatus(uid string) ([]*model.ReportTask
}
// 检索指定核算报表中园区公共表计的核算记录
func (rr _ReportRepository) ListPublicMetersInReport(rid string, page uint, keyword *string) ([]*model.ReportDetailedPublicConsumption, int64, error) {
func (rr _ReportRepository) ListPublicMetersInReport(rid string, page uint, keyword *string) ([]*vo.ReportPublishResponse, int64, error) {
rr.log.Info("检索指定核算报表中园区公共表计的核算记录", zap.String("Report", rid))
ctx, cancel := global.TimeoutContext()
defer cancel()
reportQuery := rr.ds.
From(goqu.T("report_public_consumption").As("r")).
Join(goqu.T("meter_04kv").As("m"), goqu.On(goqu.I("m.code").Eq(goqu.I("r.park_meter_id")))).
Join(goqu.T("report").As("ri"), goqu.On(goqu.I("ri.id").Eq(goqu.I("r.report_id")))).
Join(goqu.T("meter_04kv").As("m"), goqu.On(
goqu.I("m.code").Eq(goqu.I("r.park_meter_id")),
goqu.I("m.park_id").Eq(goqu.I("ri.park_id")),
)).
Join(goqu.T("park").As("p"), goqu.On(goqu.I("p.id").Eq(goqu.I("m.park_id")))).
LeftJoin(goqu.T("park_building").As("b"), goqu.On(goqu.I("b.id").Eq(goqu.I("m.building")))).
Select(
goqu.I("r.*"), goqu.I("b.name").As("building_name"), goqu.I("p.public_pooled"),
goqu.I("r.*"), goqu.I("m.*"), goqu.I("b.name").As("building_name"),
).
Where(goqu.I("r.report_id").Eq(rid))
countQuery := rr.ds.
From(goqu.T("report_public_consumption").As("r")).
Join(goqu.T("meter_04kv").As("m"), goqu.On(goqu.I("m.code").Eq(goqu.I("r.park_meter_id")))).
Join(goqu.T("report").As("ri"), goqu.On(goqu.I("ri.id").Eq(goqu.I("r.report_id")))).
Join(goqu.T("meter_04kv").As("m"), goqu.On(
goqu.I("m.code").Eq(goqu.I("r.park_meter_id")),
goqu.I("m.park_id").Eq(goqu.I("ri.park_id")),
)).
Join(goqu.T("park").As("p"), goqu.On(goqu.I("p.id").Eq(goqu.I("m.park_id")))).
LeftJoin(goqu.T("park_building").As("b"), goqu.On(goqu.I("b.id").Eq(goqu.I("m.building")))).
Select(goqu.COUNT(goqu.I("r.*"))).
@@ -372,7 +393,7 @@ func (rr _ReportRepository) ListPublicMetersInReport(rid string, page uint, keyw
Offset(startRow).Limit(config.ServiceSettings.ItemsPageSize)
var (
consumptions []*model.ReportDetailedPublicConsumption = make([]*model.ReportDetailedPublicConsumption, 0)
consumptions = make([]*vo.ReportPublishResponse, 0)
count int64
)
querySql, queryArgs, _ := reportQuery.Prepared(true).ToSQL()
@@ -390,19 +411,23 @@ func (rr _ReportRepository) ListPublicMetersInReport(rid string, page uint, keyw
// 检索指定核算报表中公摊表计的核算记录
func (rr _ReportRepository) ListPooledMetersInReport(rid string, page uint, keyword *string) ([]*model.ReportDetailedPooledConsumption, int64, error) {
rr.log.Info("检索指定核算报表中公摊表计的核算记录", zap.String("Report", rid))
ctx, cancel := global.TimeoutContext()
defer cancel()
reportQuery := rr.ds.
From(goqu.T("report_pooled_consumption").As("r")).
Join(
goqu.T("report").As("ri"), goqu.On(goqu.I("ri.id").Eq(goqu.I("r.report_id")))).
Join(goqu.T("meter_04kv").As("m"), goqu.On(goqu.I("m.code").Eq(goqu.I("r.pooled_meter_id")))).
Join(goqu.T("park").As("p"), goqu.On(goqu.I("p.id").Eq(goqu.I("m.park_id")))).
Join(goqu.T("park").As("p"), goqu.On(goqu.I("p.id").Eq(goqu.I("ri.park_id")))).
LeftJoin(goqu.T("park_building").As("b"), goqu.On(goqu.I("b.id").Eq(goqu.I("m.building")))).
Select(
goqu.I("r.*"), goqu.I("m.*"), goqu.I("b.name").As("building_name"), goqu.I("p.public_pooled"),
).
Where(goqu.I("r.report_id").Eq(rid))
reportQuery = reportQuery.Where(goqu.I("m.park_id").Eq(goqu.I("p.id")))
countQuery := rr.ds.
From(goqu.T("report_pooled_consumption").As("r")).
Join(goqu.T("meter_04kv").As("m"), goqu.On(goqu.I("m.code").Eq(goqu.I("r.pooled_meter_id")))).
@@ -479,6 +504,10 @@ func (rr _ReportRepository) ListPooledMeterDetailInReport(rid, mid string) ([]*m
rr.log.Error("列出指定核算报表中指定公共表计下参与公共表计费用分摊的表计详细时出现错误", zap.Error(err))
return make([]*model.ReportDetailNestedMeterConsumption, 0), err
}
if len(meterDetails) <= 0 {
return make([]*model.ReportDetailNestedMeterConsumption, 0), errors.New("暂无分摊关系")
}
assembled := lo.Map(meter.Diluted, func(m model.NestedMeter, _ int) *model.ReportDetailNestedMeterConsumption {
meterDetail, _ := lo.Find(meterDetails, func(elem *model.MeterDetail) bool {
return elem.Code == m.MeterId
@@ -534,15 +563,17 @@ func (rr _ReportRepository) ListTenementInReport(rid string, page uint, keyword
Offset(startRow).Limit(config.ServiceSettings.ItemsPageSize)
var (
tenements []*model.ReportTenement = make([]*model.ReportTenement, 0)
tenements = make([]*model.ReportTenement, 0)
count int64
)
querySql, queryArgs, _ := reportQuery.Prepared(true).ToSQL()
countSql, countArgs, _ := countQuery.Prepared(true).ToSQL()
fmt.Println(querySql, rid)
if err := pgxscan.Select(ctx, global.DB, &tenements, querySql, queryArgs...); err != nil {
rr.log.Error("查询指定核算报表下的商户简要计费信息时出现错误", zap.Error(err))
return tenements, 0, err
}
if err := pgxscan.Get(ctx, global.DB, &count, countSql, countArgs...); err != nil {
rr.log.Error("查询指定核算报表下的商户简要计费信息总数量时出现错误", zap.Error(err))
return tenements, 0, err
@@ -618,7 +649,7 @@ func (rr _ReportRepository) ComprehensiveReportSearch(uid, pid *string, page uin
countQuery = countQuery.Where(goqu.I("ud.id").Eq(*uid))
}
if pid != nil && len(*pid) > 0 {
if pid != nil && *pid != "" {
reportQuery = reportQuery.Where(goqu.I("p.id").Eq(*pid))
countQuery = countQuery.Where(goqu.I("p.id").Eq(*pid))
}
@@ -626,7 +657,8 @@ func (rr _ReportRepository) ComprehensiveReportSearch(uid, pid *string, page uin
queryDateRange := types.NewDateRange(start, end)
reportQuery = reportQuery.Where(goqu.L("r.period <@ ?", queryDateRange))
countQuery = countQuery.Where(goqu.L("r.period <@ ?", queryDateRange))
reportQuery = reportQuery.Where(goqu.I("r.published").Eq(true))
countQuery = countQuery.Where(goqu.I("r.published").Eq(true))
if keyword != nil && len(*keyword) > 0 {
pattern := fmt.Sprintf("%%%s%%", *keyword)
reportQuery = reportQuery.Where(goqu.Or(

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
}

View File

@@ -226,7 +226,7 @@ func (tr _TenementRepository) AddTenement(tx pgx.Tx, ctx context.Context, pid st
Bank: tools.DefaultOrEmptyStr(tenement.Bank, ""),
Account: tools.DefaultOrEmptyStr(tenement.Account, ""),
},
currentTime,
tenement.MoveIn,
currentTime,
currentTime,
}...,
@@ -238,7 +238,7 @@ func (tr _TenementRepository) AddTenement(tx pgx.Tx, ctx context.Context, pid st
}
// 向园区中指定商户下绑定一个新的表计
func (tr _TenementRepository) BindMeter(tx pgx.Tx, ctx context.Context, pid, tid, meter string) error {
func (tr _TenementRepository) BindMeter(tx pgx.Tx, ctx context.Context, pid, tid, meter string, form *vo.MeterReadingForm) error {
tr.log.Info("向园区中指定商户下绑定一个新的表计", zap.String("Park", pid), zap.String("Tenement", tid), zap.String("Meter", meter))
createSql, createArgs, _ := tr.ds.
@@ -251,7 +251,7 @@ func (tr _TenementRepository) BindMeter(tx pgx.Tx, ctx context.Context, pid, tid
pid,
tid,
meter,
types.Now(),
form.ReadAt,
},
).
Prepared(true).ToSQL()

View File

@@ -9,6 +9,7 @@ import (
"electricity_bill_calc/tools"
"electricity_bill_calc/types"
"fmt"
"log"
"time"
"github.com/doug-martin/goqu/v9"
@@ -238,8 +239,11 @@ func (ur _UserRepository) FindUser(keyword *string, userType int16, state *bool,
}
if state != nil {
userQuery = userQuery.Where(goqu.Ex{"u.enabled": state})
countQuery = countQuery.Where(goqu.Ex{"u.enabled": state})
//userQuery = userQuery.Where(goqu.C("u.enabled").Eq(*state))
userQuery = userQuery.Where(goqu.Ex{"u.enabled": *state})
countQuery = countQuery.Where(goqu.Ex{"u.enabled": *state})
//countQuery = countQuery.Where(goqu.C("u.enabled").Eq(*state))
}
userQuery.Order(goqu.I("u.created_at").Desc())
@@ -249,8 +253,9 @@ func (ur _UserRepository) FindUser(keyword *string, userType int16, state *bool,
userSql, userParams, _ := userQuery.Prepared(true).ToSQL()
countSql, countParams, _ := countQuery.Prepared(true).ToSQL()
log.Println(">>>>>>>>>>>", userSql)
if err := pgxscan.Select(ctx, global.DB, &userWithDetails, userSql, userParams...); err != nil {
ur.log.Error("从数据库查询用户列表失败。", zap.Error(err))
ur.log.Error("从数据库查询用户列表失败。》》》》》》》》》》》》》》", zap.Error(err))
return make([]*model.UserWithDetail, 0), 0, err
}
if err := pgxscan.Get(ctx, global.DB, &userCount, countSql, countParams...); err != nil {

View File

@@ -53,7 +53,7 @@ func App() *fiber.App {
controller.InitializeInvoiceHandler(app)
controller.InitializeTopUpHandlers(app)
controller.InitializeReportHandlers(app)
controller.InitializeSynchronizeHandlers(app)
controller.InitializeWithdrawHandlers(app) // 公示撤回
controller.InitializeFoundationHandlers(app) // 基础数据
controller.InitializeStatisticsController(app) // 首页信息

View File

@@ -1,7 +1,9 @@
package calculate
import (
"electricity_bill_calc/model"
"fmt"
"github.com/shopspring/decimal"
"sync/atomic"
)
@@ -16,7 +18,7 @@ func CheckMeterArea(report *model.ReportIndex, meters []*model.MeterDetail) (boo
for _, m := range meters {
if (m.MeterType == model.METER_INSTALLATION_TENEMENT || m.MeterType == model.METER_INSTALLATION_POOLING) &&
m.Area == nil {
m.Area.Decimal == decimal.Zero {
atomic.AddInt32(&meterWithoutArea, 1)
}
}

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

@@ -0,0 +1,356 @@
package calculate
import (
"electricity_bill_calc/model"
"electricity_bill_calc/model/calculate"
"errors"
"fmt"
"github.com/shopspring/decimal"
"log"
)
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 {
log.Println("m000000000000000000000000000", m.Code)
key := Key{TenementID: t.Tenement.Id, Code: m.Code}
meters[key] = m
}
}
// Collect poolings
for _, m := range poolings {
log.Println("m111111111111111111111111", m.Code)
key := Key{TenementID: "", Code: m.Code}
meters[key] = m
}
// Collect publics
for _, m := range publics {
log.Println("m222222222222222222222222222", m.Code)
key := Key{TenementID: "", Code: m.Code}
meters[key] = m
}
log.Println("m33333333333333333333333333333333333333", meters[Key{Code: "yq00001"}])
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 {
switch report.PublicPooled {
case model.POOLING_MODE_AREA:
for _, meter := range meters {
if meter.Detail.MeterType == model.METER_INSTALLATION_TENEMENT {
pooleds := make([]struct {
Electricity decimal.Decimal
ParentElectricity decimal.Decimal
Code string
}, 0)
for _, relation := range meterRelations {
if relation.SlaveMeter == meter.Code {
key := Key{
Code: relation.MasterMeter,
TenementID: "",
}
parentMeter, ok := meters[key]
if !ok {
continue
}
area := parentMeter.CoveredArea
coveredArea := area
overall := parentMeter.Overall
proportion := meter.SharedPoolingProportion
electricity := area.InexactFloat64() / coveredArea.InexactFloat64() * overall.Amount.InexactFloat64() * proportion.InexactFloat64()
pooleds = append(pooleds, struct {
Electricity decimal.Decimal
ParentElectricity decimal.Decimal
Code string
}{
Electricity: decimal.NewFromFloat(electricity),
ParentElectricity: overall.Amount,
Code: parentMeter.Code,
})
}
}
consumptions := 0.00
total := 0.00
var pooled []*calculate.Pooling
for _, p := range pooleds {
consumptions += p.Electricity.InexactFloat64()
total += p.ParentElectricity.InexactFloat64()
unit := model.ConsumptionUnit{
Amount: p.Electricity,
Fee: decimal.NewFromFloat(p.Electricity.InexactFloat64() * summary.Overall.Price.InexactFloat64()),
Price: summary.Overall.Price,
Proportion: decimal.Zero,
}
if p.ParentElectricity != decimal.Zero {
unit.Proportion = decimal.NewFromFloat(p.Electricity.InexactFloat64() / p.ParentElectricity.InexactFloat64())
}
pooling := calculate.Pooling{
Code: p.Code,
Detail: unit,
}
pooled = append(pooled, &pooling)
}
meter.PooledPublic = model.ConsumptionUnit{
Amount: decimal.NewFromFloat(consumptions),
Fee: decimal.NewFromFloat(consumptions * summary.Overall.Price.InexactFloat64()),
Price: summary.Overall.Price,
Proportion: decimal.NewFromFloat(consumptions / total),
}
meter.Poolings = pooled
}
}
case model.PRICING_POLICY_CONSUMPTION:
for _, meter := range meters {
if meter.Detail.MeterType == model.METER_INSTALLATION_TENEMENT {
pooled := make([]struct {
Electricity decimal.Decimal
ParentElectricity decimal.Decimal
Code string
}, 0)
for _, relation := range meterRelations {
if relation.SlaveMeter == meter.Code {
key := Key{
Code: relation.MasterMeter,
TenementID: "",
}
parentMeter, ok := meters[key]
if !ok {
continue
}
overall := parentMeter.Overall
if overall.Amount == decimal.Zero {
pooled = append(pooled, struct {
Electricity decimal.Decimal
ParentElectricity decimal.Decimal
Code string
}{
Electricity: decimal.Zero,
ParentElectricity: decimal.Zero,
Code: parentMeter.Code,
})
} else { //TODO: 2023.08.11 计算处理流程修改到此处,以下问题未作修改还存在问题
electricity := meter.Overall.Amount.InexactFloat64() / overall.Amount.InexactFloat64() * overall.Amount.InexactFloat64()
pooled = append(pooled, struct {
Electricity decimal.Decimal
ParentElectricity decimal.Decimal
Code string
}{
Electricity: decimal.NewFromFloat(electricity),
ParentElectricity: overall.Amount,
Code: parentMeter.Code,
})
}
}
}
consumptions := decimal.Zero.InexactFloat64()
total := decimal.Zero.InexactFloat64()
var poolings []*calculate.Pooling
for _, p := range pooled {
consumptions += p.Electricity.InexactFloat64()
total += p.ParentElectricity.InexactFloat64()
unit := model.ConsumptionUnit{
Amount: p.Electricity,
Fee: decimal.NewFromFloat(p.Electricity.InexactFloat64() * summary.Overall.Price.InexactFloat64()),
Price: summary.Overall.Price,
Proportion: decimal.Zero,
}
if p.ParentElectricity != decimal.Zero {
unit.Proportion = decimal.NewFromFloat(p.Electricity.InexactFloat64() / p.ParentElectricity.InexactFloat64())
}
pooling := calculate.Pooling{
Code: p.Code,
Detail: unit,
}
poolings = append(poolings, &pooling)
}
meter.PooledPublic = model.ConsumptionUnit{
Amount: decimal.NewFromFloat(consumptions),
Fee: decimal.NewFromFloat(consumptions * summary.Overall.Price.InexactFloat64()),
Price: summary.Overall.Price,
Proportion: decimal.Zero,
}
if total != decimal.Zero.InexactFloat64() {
meter.SharedPoolingProportion = decimal.NewFromFloat(consumptions / total)
}
meter.Poolings = poolings
}
}
default:
return nil
}
return nil
}

View File

@@ -4,6 +4,7 @@ import (
"electricity_bill_calc/model"
"electricity_bill_calc/model/calculate"
"electricity_bill_calc/repository"
"fmt"
"time"
)
@@ -22,6 +23,11 @@ func MetersParkCalculate(report model.ReportIndex, periodStart time.Time,
parkMeterReadings = append(parkMeterReadings, lastTermParkMeterReadings...)
if len(parkMeterReadings) <= 0 {
fmt.Println(parkMeterReadings)
return []calculate.Meter{}, nil
}
var parkMetersReports []calculate.Meter
for _, meter := range meterDetail {
if meter.MeterType == model.METER_INSTALLATION_PARK {

View File

@@ -0,0 +1,68 @@
package calculate
import (
"context"
"electricity_bill_calc/model"
"electricity_bill_calc/model/calculate"
"electricity_bill_calc/repository"
"fmt"
"github.com/jackc/pgx/v5"
)
// 向数据库保存核算概况结果
func SaveSummary(tx pgx.Tx, ctx context.Context, summary calculate.Summary) error {
// 保存核算概况结果到数据库
err := repository.CalculateRepository.SaveReportSummary(tx,ctx, summary)
if err != nil {
return err
}
return nil
}
// type MeterMap map[string]map[string]calculate.Meter
// 向数据库保存公共表计的计算结果
func SavePublics(tx pgx.Tx, ctx context.Context, report model.ReportIndex, meters MeterMap) error {
var filteredMeters []calculate.Meter
for _, m := range meters {
if m.Detail.MeterType == model.METER_INSTALLATION_PARK {
filteredMeters = append(filteredMeters, m)
}
}
fmt.Println(tx)
err := repository.CalculateRepository.SaveReportPublics(tx, ctx, report.Id, filteredMeters)
if err != nil {
return err
}
return nil
}
func SavePoolings(tx pgx.Tx, report model.ReportIndex, meters MeterMap, relations []model.MeterRelation) error {
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
}
return nil
}
func SaveTenements(tx pgx.Tx, report model.ReportIndex, tenement []calculate.PrimaryTenementStatistics, tc []calculate.TenementCharge) error {
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
}
return nil
}

View File

@@ -4,6 +4,7 @@ import (
"electricity_bill_calc/model"
"electricity_bill_calc/model/calculate"
"electricity_bill_calc/repository"
"electricity_bill_calc/types"
"github.com/shopspring/decimal"
"time"
"unsafe"
@@ -24,6 +25,9 @@ func PooledMetersCalculate(report *model.ReportIndex, periodStart time.Time,
}
poolingMeterReadings = append(poolingMeterReadings, lastTermPoolingMeterReadings...)
if len(poolingMeterReadings) <= 0 {
return nil, nil
}
var poolingMetersReports []calculate.Meter
for _, meter := range meterDetails {
@@ -40,6 +44,10 @@ func PooledMetersCalculate(report *model.ReportIndex, periodStart time.Time,
func determinePublicMeterConsumptions(meterId string, periodStart time.Time,
periodEnd time.Time, readings []model.MeterReading,
meterDetail model.MeterDetail, summary calculate.Summary) (calculate.Meter, error) {
if meterDetail.DetachedAt == nil {
zeroTime := time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC)
meterDetail.DetachedAt = &types.DateTime{Time: zeroTime}
}
startReading, err := DeterminePublicMeterStartReading(meterId, periodStart, meterDetail.DetachedAt.Time, readings)
if err != nil {
return calculate.Meter{}, err

View File

@@ -36,13 +36,15 @@ func DeterminePublicMeterStartReading(meterId string, periodStart time.Time,
for _, reading := range meterReadings {
readingAt := ShiftToAsiaShanghai(reading.ReadAt.UTC())
for _, startTime := range startTimes {
if reading.Meter == meterId && readingAt.After(startTime) || readingAt.Equal(startTime) {
if reading.Meter == meterId && readingAt.Truncate(24*time.Hour).Equal(startTime.Truncate(24*time.Hour)) || readingAt.After(startTime) {
startReading = append(startReading, reading)
break
}
}
}
fmt.Println(startReading)
if len(startReading) <= 0 {
return nil, errors.New(fmt.Sprintf("无法确定表计 %s 的计量的起始读数", meterId))
}
@@ -71,20 +73,20 @@ func DeterminePublicMeterEndReading(meterId string, periodEnd time.Time,
minReading = reading.ReadAt
}
}
startTimes := []time.Time{
endTimes := []time.Time{
minReading.Time,
periodEnding.Time,
ShiftToAsiaShanghai(detachedAt),
}
if len(startTimes) < 0 {
if len(endTimes) < 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) {
for _, endTime := range endTimes {
if reading.Meter == meterId && readingAt.Truncate(24*time.Hour).Equal(endTime.Truncate(24*time.Hour)) || readingAt.Before(endTime) {
startReading = append(startReading, reading)
break
}

View File

@@ -4,15 +4,16 @@ import (
"electricity_bill_calc/model"
"electricity_bill_calc/model/calculate"
"errors"
"fmt"
"github.com/shopspring/decimal"
"fmt"
)
// 计算已经启用的商铺面积和
func TotalConsumptionCalculate(tenements []calculate.PrimaryTenementStatistics,
summary calculate.Summary) decimal.Decimal {
func TotalConsumptionCalculate(tenements []calculate.PrimaryTenementStatistics, summary calculate.Summary) decimal.Decimal {
var areaMaters []calculate.Meter
for _, t := range tenements {
fmt.Println(t.Meters)
areaMaters = append(areaMaters, t.Meters...)
}
@@ -42,7 +43,7 @@ func removeDuplicates(meters []calculate.Meter) []calculate.Meter {
}
// 计算线损以及调整线损
func LossCalculate(report *model.ReportIndex, Public *[]calculate.Meter,
func LossCalculate(report *model.ReportIndex, Public []calculate.Meter,
publicTotal *decimal.Decimal, summary *calculate.Summary) error {
summary.Loss = summary.Overall.Amount.Sub(summary.TotalConsumption)
@@ -72,11 +73,15 @@ func LossCalculate(report *model.ReportIndex, Public *[]calculate.Meter,
differentialLoss := summary.LossDilutedPrice.Sub(summary.AuthoizeLoss.Amount)
fmt.Println(publicTotal.InexactFloat64())
if publicTotal.InexactFloat64() <= decimal.Zero.InexactFloat64() {
return errors.New("园区公共表计的电量总和为非正值,或者园区未设置公共表计,无法计算核定线损")
}
for _, meter := range *Public {
if Public == nil {
return nil
}
for _, meter := range Public {
amountProportion := meter.Overall.Amount.InexactFloat64() / publicTotal.InexactFloat64()
adjustAmount := differentialLoss.InexactFloat64() * decimal.NewFromFloat(-1.0).InexactFloat64()
meter.AdjustLoss = model.ConsumptionUnit{
@@ -91,7 +96,7 @@ func LossCalculate(report *model.ReportIndex, Public *[]calculate.Meter,
// 计算已经启用的商铺面积和
func EnabledAreaCalculate(tenements *[]calculate.PrimaryTenementStatistics,
summary *calculate.Summary) error {
summary *calculate.Summary) (*decimal.Decimal, error) {
var areaMeters []calculate.Meter
for _, t := range *tenements {
areaMeters = append(areaMeters, t.Meters...)
@@ -108,13 +113,16 @@ func EnabledAreaCalculate(tenements *[]calculate.PrimaryTenementStatistics,
if summary != nil {
summary.OverallArea = areaTotal
} else {
return errors.New("summary is nil")
return nil, errors.New("summary is nil")
}
return nil
return &areaTotal, nil
}
// 计算基本电费分摊、调整电费分摊以及电费摊薄单价。
func PricesCalculate(summary *calculate.Summary) error {
// =================================================================================
// / 计算基本电费分摊、调整电费分摊以及电费摊薄单价。
// /
// / - `summary`:核算报表的摘要信息
func CalculatePrices(summary *calculate.Summary) error {
if summary.TotalConsumption.IsZero() {
return nil
}
@@ -131,4 +139,5 @@ func PricesCalculate(summary *calculate.Summary) error {
summary.AdjustPooledPriceArea = summary.AdjustFee.Div(summary.OverallArea)
}
return nil
}

View File

@@ -4,6 +4,7 @@ import (
"electricity_bill_calc/model"
"electricity_bill_calc/model/calculate"
"electricity_bill_calc/repository"
"electricity_bill_calc/types"
"errors"
"fmt"
"github.com/shopspring/decimal"
@@ -14,9 +15,9 @@ import (
)
// 核算园区中的全部商户表计电量用电
func TenementMetersCalculate(report *model.ReportIndex, PeriodStart time.Time,
PeriodEnd time.Time, meterDetails []*model.MeterDetail,
summary calculate.Summary) ([]calculate.PrimaryTenementStatistics, error) {
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)
@@ -42,7 +43,6 @@ func TenementMetersCalculate(report *model.ReportIndex, PeriodStart time.Time,
}
var tenementReports []calculate.PrimaryTenementStatistics
for _, tenement := range tenements {
var meters []model.TenementMeter
@@ -79,13 +79,24 @@ func determineTenementConsumptions(tenement model.Tenement,
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)
movedInAt := tenement.MovedInAt
if movedInAt == nil {
shiftedTime := ShiftToAsiaShanghai(time.Time{})
movedInAt = &types.DateTime{Time: shiftedTime}
}
startReading, err := determineTenementMeterStartReading(meter.MeterId, periodStart, ShiftToAsiaShanghai(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)
moveOutAt := tenement.MovedOutAt
if moveOutAt == nil {
shiftedTime := ShiftToAsiaShanghai(time.Time{})
moveOutAt = &types.DateTime{Time: shiftedTime} // 使用 types.DateTime 的零值表示时间的最大值
}
endReading, err := determineTenementMeterEndReading(meter.MeterId, periodEnd, ShiftToAsiaShanghai(moveOutAt.Time), meter, currentTermReadings)
if err != nil {
fmt.Println(err)
return calculate.PrimaryTenementStatistics{}, err
@@ -142,7 +153,7 @@ func determineTenementConsumptions(tenement model.Tenement,
}
currentTermReadingPtr := &currentTermReading
meter := calculate.Meter{
m := calculate.Meter{
Code: meter.MeterId,
Detail: detail,
CoveredArea: decimal.NewFromFloat(detail.Area.Decimal.InexactFloat64()),
@@ -165,7 +176,7 @@ func determineTenementConsumptions(tenement model.Tenement,
Poolings: nil,
}
meters = append(meters, meter)
meters = append(meters, m)
}
return calculate.PrimaryTenementStatistics{
@@ -272,6 +283,7 @@ func ShiftToAsiaShanghai(t time.Time) time.Time {
func TenementChargeCalculate(tenements []calculate.PrimaryTenementStatistics,
summary calculate.Summary, meters MeterMap) []calculate.TenementCharge {
result := make(map[string][]string)
var tc []calculate.TenementCharge
for _, t := range tenements {
meterCodes := make([]string, 0)
for _, m := range t.Meters {
@@ -281,11 +293,10 @@ func TenementChargeCalculate(tenements []calculate.PrimaryTenementStatistics,
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
Key.Code = code
meter, ok := meters[Key]
if ok {
relatedMeters = append(relatedMeters, meter)
@@ -390,55 +401,77 @@ func TenementChargeCalculate(tenements []calculate.PrimaryTenementStatistics,
}
var CriticalProportion decimal.Decimal
if summary.Critical.Amount == decimal.Zero {
if summary.Critical.Amount.InexactFloat64() == decimal.Zero.InexactFloat64() {
CriticalProportion = decimal.Zero
} else {
CriticalProportion = decimal.NewFromFloat(critical.Amount.InexactFloat64() / summary.Critical.Amount.InexactFloat64())
}
var PeakProportion decimal.Decimal
if summary.Peak.Amount == decimal.Zero {
if summary.Peak.Amount.InexactFloat64() == decimal.Zero.InexactFloat64() {
PeakProportion = decimal.Zero
} else {
PeakProportion = decimal.NewFromFloat(peak.Amount.InexactFloat64() / summary.Peak.Amount.InexactFloat64())
}
var FlatProportion decimal.Decimal
if summary.Flat.Amount == decimal.Zero {
if summary.Flat.Amount.InexactFloat64() == decimal.Zero.InexactFloat64() {
FlatProportion = decimal.Zero
} else {
FlatProportion = decimal.NewFromFloat(flat.Amount.InexactFloat64() / summary.Flat.Amount.InexactFloat64())
}
var ValleyProportion decimal.Decimal
if summary.Valley.Amount == decimal.Zero {
if summary.Valley.Amount.InexactFloat64() == decimal.Zero.InexactFloat64() {
ValleyProportion = decimal.Zero
} else {
ValleyProportion = decimal.NewFromFloat(valley.Amount.InexactFloat64() / summary.Valley.Amount.InexactFloat64())
}
var lossProportion decimal.Decimal
if summary.AuthoizeLoss.Amount == decimal.Zero {
lossProportion = decimal.Zero
} else {
lossProportion = decimal.NewFromFloat(lossAmount.InexactFloat64() / summary.AuthoizeLoss.Amount.InexactFloat64())
}
tenementCharge := calculate.TenementCharge{
Tenement: tCode,
Overall: model.ConsumptionUnit{
Amount: overall.Amount,
Fee: overall.Fee,
Price: summary.Overall.Price,
Proportion: OverallProportion,
},
Critical: model.ConsumptionUnit{
Amount: critical.Amount,
Fee: critical.Fee,
Price: summary.Critical.Price,
Proportion: CriticalProportion,
},
Peak: model.ConsumptionUnit{
Price: summary.Overall.Price,
Amount: peak.Amount,
Fee: peak.Fee,
Price: summary.Peak.Price,
Proportion: PeakProportion,
},
Flat: model.ConsumptionUnit{
Price: summary.Overall.Price,
Amount: flat.Amount,
Fee: flat.Fee,
Price: summary.Flat.Price,
Proportion: FlatProportion,
},
Valley: model.ConsumptionUnit{
Price: summary.Overall.Price,
Amount: valley.Amount,
Fee: valley.Fee,
Price: summary.Valley.Price,
Proportion: ValleyProportion,
},
Loss: model.ConsumptionUnit{
Amount: lossAmount,
Fee: lossPooled,
Price: summary.AuthoizeLoss.Price,
Proportion: lossProportion,
},
BasicFee: basicPooled,
AdjustFee: adjustPooled,
LossPooled: lossPooled,
@@ -450,9 +483,9 @@ func TenementChargeCalculate(tenements []calculate.PrimaryTenementStatistics,
Submeters: nil,
Poolings: nil,
}
tc = append(tc, tenementCharge)
}
}
//fmt.Println(len(tc))
return tc
}

View File

@@ -1,24 +1,26 @@
package calculate
import (
"electricity_bill_calc/global"
"electricity_bill_calc/model/calculate"
"electricity_bill_calc/repository"
"fmt"
)
func MainCalculateProcess(rid string) {
func MainCalculateProcess(rid string) error {
report, err := repository.ReportRepository.GetReportIndex(rid)
if err != nil {
fmt.Println("1", err.Error()+"指定报表不存在")
return
return err
}
reportSummary, err := repository.ReportRepository.RetrieveReportSummary(rid)
if err != nil {
fmt.Println("2", err.Error()+"指定报表的基本电量电费数据不存在")
return
return err
}
fmt.Println(report, reportSummary)
summary := calculate.FromReportSummary(reportSummary, report)
periodStart := report.Period.SafeLower()
@@ -27,69 +29,137 @@ func MainCalculateProcess(rid string) {
meterDetails, err := repository.MeterRepository.AllUsedMetersInReport(report.Id)
if err != nil {
fmt.Println("3", err)
return
return err
}
meterRelations, err := repository.CalculateRepository.GetAllPoolingMeterRelations(report.Park, periodStart.Time)
if err != nil {
fmt.Println("4", err)
return
return err
}
_, err = CheckMeterArea(report, meterDetails)
if err != nil {
fmt.Println("5", err)
return
return err
}
// 寻找每一个商户的所有表计读数,然后对分配到各个商户的表计读数进行初步的计算.
tenementReports, err := TenementMetersCalculate(report, periodStart.Time, periodEnd.Time, meterDetails, summary)
if err != nil {
fmt.Println("6", err)
return
return err
}
// 取得所有公摊表计的读数,以及公摊表计对应的分摊表计
poolingMetersReports, err := PooledMetersCalculate(report, periodStart.Time, periodEnd.Time, meterDetails, summary)
if err != nil {
fmt.Println("7", err)
return
return err
}
// 获取所有的物业表计,然后对所有的物业表计电量进行计算。
parkMetersReports, err := MetersParkCalculate(*report, periodStart.Time, periodEnd.Time, meterDetails, summary)
if err != nil {
fmt.Println("8", err)
return
return err
}
// 计算所有表计的总电量
parkTotal := TotalConsumptionCalculate(tenementReports, summary)
//计算线损以及调整线损
err = LossCalculate(report, &parkMetersReports, &parkTotal, &summary)
err = LossCalculate(report, parkMetersReports, &parkTotal, &summary)
if err != nil {
fmt.Println("9", err)
return
return err
}
// 计算所有已经启用的商铺面积总和,仅计算所有未迁出的商户的所有表计对应的商铺面积。
err = EnabledAreaCalculate(&tenementReports, &summary)
_, err = EnabledAreaCalculate(&tenementReports, &summary)
if err != nil {
fmt.Println("10", err)
return
return err
}
err = PricesCalculate(&summary)
// 计算基本电费分摊、调整电费分摊、电费摊薄单价。
err = CalculatePrices(&summary)
if err != nil {
fmt.Println("11", err)
return
return err
}
fmt.Println("计算数据读取完成=======================================================================================================================================================================================")
// 收集目前所有已经处理的表计,统一对其进行摊薄计算。
meters, err := CollectMeters(tenementReports, poolingMetersReports, parkMetersReports)
if err != nil {
fmt.Println("12", err)
return err
}
//为获取值初始化一个空的,合并分支时可忽略
var meters MeterMap
// 根据核算报表中设置的摊薄内容,逐个表计进行计算
err = CalculateBasicPooling(report, &summary, &meters)
if err != nil {
fmt.Println("13", err)
return err
}
err = CalculateAdjustPooling(*report, summary, meters)
if err != nil {
fmt.Println("14", err)
return err
}
err = CalculateLossPooling(*report, summary, meters)
if err != nil {
fmt.Println("15", err)
return err
}
// 计算所有商户类型表计的全周期电量,并根据全周期电量计算共用过同一表计的商户的二次分摊比例。
_, err = CalculateTenementConsumptions(&meters)
if err != nil {
fmt.Println("16", err)
return err
}
err = CalculateTenementPoolings(*report, summary, meters, meterRelations)
if err != nil {
fmt.Println("17", err)
return err
}
// 计算商户的合计电费信息,并归总与商户相关联的表计记录
tenementCharges := TenementChargeCalculate(tenementReports, summary, meters)
fmt.Println(meterRelations, poolingMetersReports, tenementCharges)
// 从此处开始向数据库保存全部计算结果。
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)
return err
}
err = SaveSummary(tx, ctx, summary)
if err != nil {
_ = tx.Rollback(ctx)
fmt.Println("19", err)
return err
}
err = SavePublics(tx, ctx, *report, meters)
if err != nil {
_ = tx.Rollback(ctx)
fmt.Println("20", err)
return err
}
err = SavePoolings(tx, *report, meters, meterRelations)
if err != nil {
_ = tx.Rollback(ctx)
fmt.Println("21", err)
return err
}
err = SaveTenements(tx, *report, tenementReports, tenementCharges)
if err != nil {
_ = tx.Rollback(ctx)
fmt.Println("22", err)
return err
}
fmt.Println("商户分摊关系保存成功")
_ = tx.Commit(ctx)
return nil
}

View File

@@ -49,7 +49,7 @@ func (ms _MeterService) CreateMeterRecord(pid string, form *vo.MeterCreationForm
return err
}
ok, err = repository.MeterRepository.RecordReading(tx, ctx, pid, form.Code, form.MeterType, form.Ratio, &form.MeterReadingForm)
ok, err = repository.MeterRepository.RecordReading(tx, ctx, pid, form.Code, form.MeterType, form.Ratio, &form.Reading)
if err != nil {
ms.log.Error("无法记录表计读数。", zap.Error(err))
tx.Rollback(ctx)
@@ -222,7 +222,7 @@ func (ms _MeterService) BatchImportMeters(pid string, file *multipart.FileHeader
Building: element.Building,
OnFloor: element.OnFloor,
Area: element.Area,
MeterReadingForm: vo.MeterReadingForm{
Reading: vo.MeterReadingForm{
ReadAt: &element.ReadAt,
Overall: element.Overall,
Critical: element.Critical.Decimal,
@@ -257,7 +257,7 @@ func (ms _MeterService) BatchImportMeters(pid string, file *multipart.FileHeader
}
// 步骤5将全部抄表信息保存进入数据库
for _, record := range meterCreationForms {
_, err := repository.MeterRepository.RecordReading(tx, ctx, pid, record.Code, record.MeterType, record.Ratio, &record.MeterReadingForm)
_, err := repository.MeterRepository.RecordReading(tx, ctx, pid, record.Code, record.MeterType, record.Ratio, &record.Reading)
if err != nil {
ms.log.Error("无法在数据插入阶段保存抄表信息。", zap.String("meter code", record.Code), zap.Error(err))
tx.Rollback(ctx)
@@ -372,7 +372,7 @@ func (ms _MeterService) ReplaceMeter(
Building: oldMeter.Building,
OnFloor: oldMeter.OnFloor,
Area: oldMeter.Area,
MeterReadingForm: *newMeterReading,
Reading: *newMeterReading,
}
// 步骤7将新表计写入系统
@@ -389,7 +389,7 @@ func (ms _MeterService) ReplaceMeter(
}
// 步骤8将新表计的读数写入系统
ok, err = repository.MeterRepository.RecordReading(tx, ctx, pid, newMeterCode, newMeterCreationForm.MeterType, newMeterCreationForm.Ratio, &newMeterCreationForm.MeterReadingForm)
ok, err = repository.MeterRepository.RecordReading(tx, ctx, pid, newMeterCode, newMeterCreationForm.MeterType, newMeterCreationForm.Ratio, &newMeterCreationForm.Reading)
switch {
case err != nil:
ms.log.Error("无法将新表计的读数写入系统。", zap.Error(err))
@@ -444,6 +444,118 @@ func (ms _MeterService) ReplaceMeter(
return nil
}
//func replaceMeter(
// pid string,
// oldMeterCode string,
// oldMeterReading *MeterReadingForm,
// newMeterCode string,
// newMeterRatio *big.Float,
// newMeterReading *MeterReadingForm,
//) (bool, error) {
// ctx := context.Background()
// tx, err := PostgresPool{}.begin(ctx)
// if err != nil {
// return false, err
// }
//
// // 步骤1获取旧表计的信息
// oldMeter, err := repositories{}.meter_fetch_meter_detail(pid, oldMeterCode)
// if err != nil {
// tx.rollback(ctx)
// return false, err
// }
// if oldMeter == nil {
// tx.rollback(ctx)
// return false, fmt.Errorf("要替换的旧表计不存在")
// }
//
// // 步骤2将旧表计的读数写入读数表
// err = repositories{}.meter_record_reading(tx, pid, oldMeterCode, oldMeter.MeterType, oldMeter.Ratio, oldMeterReading)
// if err != nil {
// tx.rollback(ctx)
// return false, err
// }
//
// // 步骤3: 将旧表计从系统中移除
// err = repositories{}.meter_detach_meter(tx, pid, oldMeterCode)
// if err != nil {
// tx.rollback(ctx)
// return false, err
// }
//
// // 步骤4: 获取旧表计的关联信息
// var oldMeterAssociations []*MeterRelation
// if oldMeter.MeterType == Pooling {
// oldMeterAssociations, err = repositories{}.meter_list_pooled_meter_relations(pid, oldMeterCode)
// } else {
// oldMeterAssociations, err = repositories{}.meter_list_meter_relations(pid, oldMeterCode)
// }
// if err != nil {
// tx.rollback(ctx)
// return false, err
// }
//
// // 步骤5: 将旧表计的关联信息全部置为解除
// for _, relation := range oldMeterAssociations {
// err = repositories{}.meter_unbind_meter(tx, pid, relation.MasterMeter, relation.SlaveMeter)
// if err != nil {
// tx.rollback(ctx)
// return false, err
// }
// }
//
// // 步骤6: 将旧表计的部分信息赋予新表计
// newMeterCreationForm := &MeterCreationForm{
// Code: newMeterCode,
// Address: oldMeter.Address,
// MeterType: oldMeter.MeterType,
// Ratio: newMeterRatio,
// Seq: oldMeter.Seq,
// // Assign other fields
// Reading: newMeterReading,
// }
//
// // 步骤7: 将新表计写入系统
// err = repositories{}.meter_create_meter(tx, pid, newMeterCreationForm)
// if err != nil {
// tx.rollback(ctx)
// return false, err
// }
//
// // 步骤8: 将新表计的读数写入读数表
// err = repositories{}.meter_record_reading(tx, pid, newMeterCode, oldMeter.MeterType, newMeterRatio, newMeterReading)
// if err != nil {
// tx.rollback(ctx)
// return false, err
// }
//
// // 步骤9: 将旧表计的关联信息复制一份并赋予新表计
// if oldMeter.MeterType == Pooling {
// for _, relation := range oldMeterAssociations {
// err = repositories{}.meter_bind_meter(tx, pid, newMeterCode, relation.SlaveMeter)
// if err != nil {
// tx.rollback(ctx)
// return false, err
// }
// }
// } else {
// for _, relation := range oldMeterAssociations {
// err = repositories{}.meter_bind_meter(tx, pid, relation.MasterMeter, newMeterCode)
// if err != nil {
// tx.rollback(ctx)
// return false, err
// }
// }
// }
//
// err = tx.commit(ctx)
// if err != nil {
// return false, err
// }
//
// return true, nil
//}
// 列出园区中指定公摊表计下的所有关联表计
func (ms _MeterService) ListPooledMeterRelations(pid, masterMeter string) ([]*model.MeterDetail, error) {
ms.log.Info("列出园区中指定公摊表计下的所有关联表计", zap.String("park id", pid), zap.String("meter code", masterMeter))
@@ -587,7 +699,7 @@ func (ms _MeterService) UnbindMeter(pid, masterMeter string, slaveMeters []strin
}
// 查询符合条件的表计读数记录
func (ms _MeterService) SearchMeterReadings(pid string, building *string, start, end *types.Date, page uint, keyword *string) ([]*model.DetailedMeterReading, int64, error) {
func (ms _MeterService) SearchMeterReadings(pid string, building *string, start, end *types.Date, page uint, keyword *string, mtype uint) ([]*model.DetailedMeterReading, int64, error) {
ms.log.Info(
"查询符合条件的表计读数记录",
zap.String("park id", pid),
@@ -597,7 +709,7 @@ func (ms _MeterService) SearchMeterReadings(pid string, building *string, start,
zap.Uint("page", page),
zap.Stringp("keyword", keyword),
)
readings, total, err := repository.MeterRepository.ListMeterReadings(pid, keyword, page, start, end, building)
readings, total, err := repository.MeterRepository.ListMeterReadings(pid, keyword, page, start, end, building, mtype)
if err != nil {
ms.log.Error("无法查询符合条件的表计读数记录。", zap.Error(err))
return make([]*model.DetailedMeterReading, 0), 0, err
@@ -777,3 +889,10 @@ func (ms _MeterService) BatchImportReadings(pid string, file *multipart.FileHead
}
return make([]excel.ExcelAnalysisError, 0), nil
}
//// 更换系统中的表计
//func (ms _MeterService) ReplaceMeter(pid string, oldMeterCode string, oldMeterReading vo.MeterReadingForm,
// newMeterCode string, newMeterRatio decimal.Decimal, newMeterDisplayRatio decimal.Decimal,
// newMeterReading vo.NewMeterForReplacingForm) {
//
//}

View File

@@ -5,8 +5,12 @@ import (
"electricity_bill_calc/logger"
"electricity_bill_calc/model"
"electricity_bill_calc/repository"
"electricity_bill_calc/service/calculate"
"electricity_bill_calc/types"
"electricity_bill_calc/vo"
"github.com/pkg/errors"
"log"
"sync"
"github.com/doug-martin/goqu/v9"
_ "github.com/doug-martin/goqu/v9/dialect/postgres"
@@ -196,3 +200,92 @@ func (rs _ReportService) ListWithdrawalRequests(page uint, keyword *string) ([]*
)
return assembled, total, nil
}
// 定时检测执行电费核算任务的分发
func (rs _ReportService) ReportCalcuateDispatch(rid string) error {
err := rs.CalculateReport(rid)
if err != nil {
return errors.Wrap(err, "报表计算调度时发生错误")
}
return nil
}
// 创建一个新的核算报表,并同时完成核算报表的计算
func (rs _ReportService) CreateNewReport(createFrom *vo.ReportCreationForm) (bool, error) {
state, report, err := repository.ReportRepository.CreateReport(createFrom)
if err != nil {
rs.log.Error("创建核算报表错误", zap.Error(err))
return false, err
}
if !state {
status, err := repository.CalculateRepository.UpdateReportCalculateStatus(report, "InsufficientData",
"创建报表时发生错误,需手动再次计算")
if err != nil {
rs.log.Error("创建报表时发生错误,需手动再次计算", zap.Error(err))
return false, err
}
return status, nil
}
err = rs.CalculateReport(report)
if err != nil {
rs.log.Error("计算时出错", zap.Error(err))
return false, err
}
return true, nil
}
// 更新一个核算报表中的数据,并同时完成计算
func (rs _ReportService) UpdateRepoet(rid string, updateForm *vo.ReportModifyForm) (bool, error) {
state, err := repository.ReportRepository.UpdateReportSummary(rid, updateForm)
if err != nil {
rs.log.Error("更新摘要出错", zap.Error(err))
return false, err
}
if !state {
return false, nil
}
err = rs.CalculateReport(rid)
if err != nil {
rs.log.Error("计算时出错", zap.Error(err))
return false, err
}
return true, nil
}
var CALCULATE_TASK_PARALLEL_CONTROL = func() *sync.Mutex {
s := sync.Mutex{}
s.Lock()
return &s
}()
// 执行一个核算报表的计算任务
func (rs _ReportService) CalculateReport(rid string) error {
//semaphore := CALCULATE_TASK_PARALLEL_CONTROL
//
//semaphore.Lock()
//defer semaphore.Unlock()
errs := calculate.MainCalculateProcess(rid)
if errs == nil {
_, err := repository.CalculateRepository.UpdateReportCalculateStatus(rid, "success", "")
if err != nil {
rs.log.Error("执行核算报表计算任务失败", zap.Error(err))
return err
}
} else {
_, err := repository.CalculateRepository.UpdateReportCalculateStatus(rid, "InsufficientData", errs.Error())
if err != nil {
rs.log.Error("执行核算报表计算任务失败", zap.Error(err))
return err
}
rs.log.Error("核算报表"+rid+"时发生错误:"+errs.Error()+"", zap.Error(errs))
return err
}
return 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

@@ -7,7 +7,6 @@ import (
"electricity_bill_calc/repository"
"electricity_bill_calc/vo"
"fmt"
"github.com/samber/lo"
"go.uber.org/zap"
)
@@ -82,7 +81,7 @@ func (ts _TenementService) BindMeter(pid, tid, meterCode string, reading *vo.Met
tx.Rollback(ctx)
return fmt.Errorf("未能获取表计详细信息,%w", err)
}
err = repository.TenementRepository.BindMeter(tx, ctx, pid, tid, meterCode)
err = repository.TenementRepository.BindMeter(tx, ctx, pid, tid, meterCode, reading)
if err != nil {
ts.log.Error("向商户绑定一个新表计失败,未能绑定表计", zap.Error(err))
tx.Rollback(ctx)
@@ -201,7 +200,7 @@ func (ts _TenementService) MoveOutTenement(pid, tid string, reading []*vo.MeterR
tx.Rollback(ctx)
return fmt.Errorf("找不到指定表计[%s]的抄表信息,%w", meterCode, err)
}
if reading.Validate() {
if !reading.Validate() {
ts.log.Error("迁出指定商户失败,表计读数不能正确配平,尖锋电量、峰电量、谷电量之和超过总电量。", zap.String("Meter", meterCode))
tx.Rollback(ctx)
return fmt.Errorf("表计[%s]读数不能正确配平,尖锋电量、峰电量、谷电量之和超过总电量。", meterCode)

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() //生成uuid作为会话的token使用
userSession := &model.Session{
Uid: user.Id,
Name: user.Username,

View File

@@ -12,14 +12,16 @@ Server:
ReadTimeout: 60
WriteTimeout: 60
Redis:
Host: redis
Host: 127.0.0.1
Port: 6379
Password: TmFRS0w6BIrAPA1Raj
Password:
DB: 1
Service:
MaxSessionLife: 2h
ItemsPageSize: 20
CacheLifeTime: 5m
HostSerial: 5
BaselineLineLossRatio:
Base: 基准线损率
Base: 0.07

View File

@@ -169,4 +169,3 @@ func TimeToStringPtr(t *time.Time) *string {
timeStr := t.Format("2006-01-02 15:04:05")
return &timeStr
}

View File

@@ -1,7 +1,9 @@
package vo
import (
"electricity_bill_calc/model"
"electricity_bill_calc/types"
"time"
"github.com/shopspring/decimal"
)
@@ -16,7 +18,7 @@ type MeterCreationForm struct {
OnFloor *string `json:"onFloor"`
Area decimal.NullDecimal `json:"area"`
Enabled bool `json:"enabled"`
MeterReadingForm `json:"-"`
Reading MeterReadingForm `json:"reading"`
}
type MeterModificationForm struct {
@@ -59,6 +61,62 @@ type SimplifiedMeterDetailResponse struct {
Area decimal.Decimal `json:"area"`
Enabled bool `json:"enabled"`
MeterType int16 `json:"type"`
AttachedAt types.DateTime `json:"attachedAt"`
AttachedAt *types.DateTime `json:"attachedAt"`
DetachedAt *types.DateTime `json:"detachedAt"`
}
type ReadableMeterQueryResponse struct {
Code string `json:"code"`
Address *string `json:"address"`
Park string `json:"park"`
}
type ReportPublishResponse struct {
//ID string
ReportID string `json:"report_id" db:"report_id"` // 报告ID
ParkMeterID string `json:"park_meter_id" db:"park_meter_id"` // 停车计费ID
Overall model.ConsumptionUnit `json:"overall" db:"overall"` // 总体信息
Critical model.ConsumptionUnit `json:"critical" db:"critical"` // 关键信息
Peak model.ConsumptionUnit `json:"peak" db:"peak"` // 高峰信息
Flat model.ConsumptionUnit `json:"flat" db:"flat"` // 平峰信息
Valley model.ConsumptionUnit `json:"valley" db:"valley"` // 谷峰信息
LossAdjust model.ConsumptionUnit `json:"loss_adjust" db:"loss_adjust"` // 损耗调整信息
ConsumptionTotal float64 `json:"consumption_total" db:"consumption_total"` // 总消费量
LossAdjustTotal float64 `json:"loss_adjust_total" db:"loss_adjust_total"` // 总损耗调整
FinalTotal float64 `json:"final_total" db:"final_total"` // 最终总量
CreatedAt time.Time `json:"created_at" db:"created_at"` // 创建时间
LastModifiedAt time.Time `json:"last_modified_at" db:"last_modified_at"` // 最后修改时间
Code string `json:"code" db:"code"` // 代码
ParkID string `json:"park_id" db:"park_id"` // 停车场ID
Address *string `json:"address" db:"address"` // 地址
Ratio decimal.Decimal `json:"ratio" db:"ratio"` // 比率
Seq int64 `json:"seq" db:"seq"` // 序列号
Enabled bool `json:"enabled" db:"enabled"` // 是否启用
MeterType int16 `json:"meter_type" db:"meter_type"` // 计量类型
Building *string `json:"building" db:"building"` // 建筑物
OnFloor *string `json:"on_floor" db:"on_floor"` // 楼层
Area decimal.NullDecimal `json:"area" db:"area"` // 面积
AttachedAt *types.DateTime `json:"attached_at" db:"attached_at"` // 附加时间
DetachedAt *types.DateTime `json:"detached_at" db:"detached_at"` // 分离时间
DisplayRatio float64 `json:"display_ratio" db:"display_ratio"` // 显示比例
BuildingName *string `json:"building_name" db:"building_name"` // 建筑物名称
PublicPooled int16 `json:"public_pooled" db:"public_pooled"` // 公共汇总
}
type Public struct {
Address *string `json:"address"` // 户址
AdjustLoss model.ConsumptionUnit `json:"adjustLoss"` // 调整线损数据,仅运维可见,仅使用其中`amount`内容
Area string `json:"area"` // 所辖面积
AttachedAt *types.DateTime `json:"attachedAt"` // 接入系统时间,挂表
Building *string `json:"building"` // 所在建筑ID
BuildingName *string `json:"buildingName"` // 所在建筑名称
Code string `json:"code"` // 表计表号
DetachedAt *types.DateTime `json:"detachedAt"` // 从系统移除时间,拆表
DisplayRatio string `json:"displayRatio"` // 表计表显倍率,仅用于展示。
Enabled bool `json:"enabled"` // 是否可用
OnFloor *string `json:"onFloor"` // 所在楼层
Overall model.ConsumptionUnit `json:"overall"` // 总电量部分
ParkID string `json:"parkId"` // 所属园区ID
Ratio string `json:"ratio"` // 表计计算倍率,参与表计读数计算。
Seq int64 `json:"seq"` // 抄表序号
Type float64 `json:"type"` // 表计类型0商户电表1园区电表2公摊电表
}

View File

@@ -4,7 +4,6 @@ import (
"electricity_bill_calc/model"
"electricity_bill_calc/types"
"fmt"
"github.com/shopspring/decimal"
)
@@ -19,7 +18,8 @@ type MeterReadingForm struct {
func (r MeterReadingForm) Validate() bool {
flat := r.Overall.Sub(r.Critical).Sub(r.Peak).Sub(r.Valley)
return flat.GreaterThanOrEqual(decimal.Zero)
b := flat.GreaterThanOrEqual(decimal.Zero)
return b
}
type MeterReadingFormWithCode struct {

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"` // 重试间隔,基础间隔时间,根据间隔算法不同会产生不同的间隔
}

View File

@@ -18,6 +18,7 @@ type TenementCreationForm struct {
InvoicePhone *string `json:"invoicePhone"`
Bank *string `json:"bank"`
Account *string `json:"bankAccount"`
MoveIn *string `json:"moveIn"`
}
type TenementQueryResponse struct {

View File

@@ -22,4 +22,5 @@ type TopUpDetailQueryResponse struct {
Amount decimal.Decimal `json:"amount"`
PaymentType int16 `json:"paymentType"`
SyncStatus int16 `json:"syncStatus" copier:"SyncStatus"`
CancelledAt *types.DateTime `json:"cancelledAt"` // TODO: 2023.08.11 在查询充值记录的时候发现缺少该字段(已添加)
}