forked from free-lancers/electricity_bill_calc_service
Compare commits
302 Commits
0.1.19
...
ZiHangQinB
| Author | SHA1 | Date | |
|---|---|---|---|
| af359f4429 | |||
| ce4c483bcb | |||
| c916301f6b | |||
| 5710a640e8 | |||
| 6b3d3dd93c | |||
| 8fc463bd9d | |||
| f688f50ecb | |||
| f254ec1f3a | |||
| 9b899be33d | |||
| c36bfff05a | |||
| b84c51b18e | |||
| 1dd5f1049d | |||
| 18d48c7fea | |||
| 8ab89bca34 | |||
| b64929c10a | |||
| 1099a7c335 | |||
| 5866882c2d | |||
| 9ad3415cdb | |||
| 39e404451e | |||
| 251c44049a | |||
| b3032638fc | |||
| d8a29e7d17 | |||
| 6fece99e00 | |||
| ab44ff5cc4 | |||
| 61edef5c92 | |||
| 648fc0f370 | |||
|
|
7f2ec68197 | ||
|
|
9dc846c044 | ||
|
|
b7eaaffc3a | ||
|
|
1db60a0e4f | ||
|
|
06f86e3cd4 | ||
|
|
ccc6cac4df | ||
|
|
3ab505e446 | ||
|
|
26a951f970 | ||
|
|
0a9d5cd121 | ||
|
|
4df3efacd8 | ||
|
|
0c389e440a | ||
|
|
a1a72b7204 | ||
|
|
a626869f14 | ||
|
|
b4ce754c0d | ||
|
|
7806f07766 | ||
|
|
877b8304c3 | ||
|
|
b224f51613 | ||
|
|
ac36c158c0 | ||
|
|
037e6258d1 | ||
|
|
aec1655f1c | ||
|
|
0246eaba27 | ||
|
|
e6d9435c14 | ||
|
|
062cc6a9ea | ||
|
|
2792959d1e | ||
|
|
fa03bf5dbd | ||
|
|
fbe4036389 | ||
|
|
b2e4fb809f | ||
|
|
0d73665313 | ||
|
|
316553d81a | ||
|
|
2b5272dad9 | ||
|
|
234e811324 | ||
|
|
541932d62e | ||
|
|
e88477a710 | ||
|
|
11bd661e79 | ||
|
|
986562c2c2 | ||
|
|
e5dadf06be | ||
|
|
db52a140b1 | ||
|
|
ce2bb160e1 | ||
|
|
24f2c26d86 | ||
|
|
fb388c53c7 | ||
|
|
b244fd5823 | ||
|
|
2c303cfba7 | ||
|
|
2d6bff5828 | ||
|
|
577ac9d1dc | ||
|
|
7531a6a5a1 | ||
|
|
be270597f5 | ||
|
|
77587b8157 | ||
|
|
d97db0cf50 | ||
|
|
2558a83024 | ||
|
|
7da5bd1112 | ||
|
|
d4fbf86800 | ||
|
|
c9b7dc3aec | ||
|
|
424ba2e839 | ||
|
|
0cffc1b6d1 | ||
|
|
ef325aede5 | ||
|
|
e2a61d58ac | ||
|
|
542efdac86 | ||
|
|
7c6f211931 | ||
|
|
23e9e2ec4d | ||
|
|
6f0edc54bf | ||
|
|
7bac667c2a | ||
|
|
46ae943653 | ||
|
|
2339e4c725 | ||
|
|
e366888608 | ||
|
|
cadb2db8c7 | ||
|
|
7476278c52 | ||
|
|
bfa9da4a03 | ||
|
|
c2a56c8253 | ||
|
|
5d938da53e | ||
|
|
686890b5d8 | ||
|
|
50418f2e30 | ||
|
|
0020776218 | ||
|
|
48753eb3f0 | ||
|
|
ab2bcb3cf6 | ||
|
|
005f9020a1 | ||
|
|
2d17bd5f0d | ||
|
|
2f17853dc0 | ||
|
|
ea1c1d7829 | ||
|
|
c302b32367 | ||
|
|
7f43965f1c | ||
|
|
0c725e99a4 | ||
|
|
33ccd7e0b6 | ||
|
|
40abbcfb8c | ||
|
|
62560e4eeb | ||
|
|
28b1478e9a | ||
|
|
6bf4009338 | ||
|
|
bfb59a3626 | ||
|
|
8aa3a054b0 | ||
|
|
1fd5e7b9aa | ||
|
|
85f4d04a7f | ||
|
|
c22e7e7dc0 | ||
|
|
98f3bdec0a | ||
|
|
f8ef6aba98 | ||
|
|
097e25f070 | ||
|
|
cd723e98e3 | ||
|
|
919883f521 | ||
|
|
04dc9c51aa | ||
|
|
ee55507e05 | ||
|
|
e5b5322e0d | ||
|
|
bca0fd777d | ||
|
|
6efe16e2fe | ||
|
|
afc0114181 | ||
|
|
9aa32d99b9 | ||
|
|
71f39c8c2f | ||
|
|
73737c3753 | ||
|
|
61fef8d0fa | ||
|
|
523e6215f4 | ||
|
|
47cd27c968 | ||
|
|
0d5457fca3 | ||
|
|
28609df9ec | ||
|
|
31ec847ab2 | ||
|
|
83b0cd89f4 | ||
|
|
1ccd19d78b | ||
|
|
2b30481419 | ||
|
|
ac94c578d6 | ||
|
|
12ec8d26bf | ||
|
|
e67b9afa68 | ||
|
|
201af1cc25 | ||
|
|
183092b670 | ||
|
|
8caf069b8a | ||
|
|
11cc6f0de1 | ||
|
|
00e2664007 | ||
|
|
746a9bcaa9 | ||
|
|
7a8e8014ad | ||
|
|
c028056458 | ||
|
|
5c1b26c77f | ||
|
|
122bc228bf | ||
|
|
ba9c87d2b1 | ||
|
|
e13193de6d | ||
|
|
4b08952916 | ||
|
|
df9bf83bb8 | ||
|
|
0169419707 | ||
|
|
2ba64227d0 | ||
|
|
c56e1128ff | ||
|
|
7e861ba4e6 | ||
|
|
712d704004 | ||
|
|
502a8bbcee | ||
|
|
c306a749aa | ||
|
|
1385f0bbcb | ||
|
|
0e7333b104 | ||
|
|
de6e24dcd3 | ||
|
|
f3457c9ab2 | ||
|
|
022788bb44 | ||
|
|
f4ee7cf8a4 | ||
|
|
f8f8a0ced1 | ||
|
|
38ec847f55 | ||
|
|
d47a052d39 | ||
|
|
9f68d3adea | ||
|
|
c716de21aa | ||
|
|
fa61f83c6a | ||
|
|
f66782c87b | ||
|
|
875b013c2b | ||
|
|
8ec217534e | ||
|
|
2c182496fa | ||
|
|
c7569fbc2c | ||
|
|
6740ab3f48 | ||
|
|
aa3a4dc44e | ||
|
|
d7f3e0f096 | ||
|
|
efc0a605b7 | ||
|
|
b5c2455af7 | ||
|
|
a13193cfa4 | ||
|
|
794054b831 | ||
|
|
aa5c43e51a | ||
|
|
8a070d4396 | ||
|
|
4620271fa5 | ||
|
|
7f585c0452 | ||
|
|
d98e782f12 | ||
|
|
cafed0c7f0 | ||
|
|
8bd9c2f46f | ||
|
|
0355300908 | ||
|
|
3742a445b7 | ||
|
|
01953132ca | ||
|
|
ed83fbc77c | ||
|
|
308c9e959c | ||
|
|
1e04922cee | ||
|
|
a9f93d5239 | ||
|
|
d885538500 | ||
|
|
f13ba3fca0 | ||
|
|
720bdd54fe | ||
|
|
b638bc5f75 | ||
|
|
3f8ced5453 | ||
|
|
ecd2238c9e | ||
|
|
2218e27838 | ||
|
|
710d285733 | ||
|
|
7b8ee5ddbd | ||
|
|
e40ba55825 | ||
|
|
2aa6939186 | ||
|
|
1c0ebc9559 | ||
|
|
0d062560da | ||
|
|
9bbb58ae55 | ||
|
|
d0d6c9f721 | ||
|
|
3f60376061 | ||
|
|
cea90c5b29 | ||
|
|
ffaccc4c88 | ||
|
|
60d2db6cdd | ||
|
|
805911f72b | ||
|
|
df08c31278 | ||
|
|
fc3f931362 | ||
|
|
e250ef6792 | ||
|
|
d778c4d3f3 | ||
|
|
7ce9abe1de | ||
|
|
dba74ecd49 | ||
|
|
398e67a7bd | ||
|
|
ab7ce6d0c6 | ||
|
|
e2767501fb | ||
|
|
3d20ceb35a | ||
|
|
c6c1423364 | ||
|
|
cb2908435a | ||
|
|
92e8d312dd | ||
|
|
ce7b69923d | ||
|
|
f8025c5dea | ||
|
|
8687b462ff | ||
|
|
b262042244 | ||
|
|
ae6e6490b2 | ||
|
|
9380879ab5 | ||
|
|
4254d020b9 | ||
|
|
769882dce5 | ||
|
|
a3a00d162a | ||
|
|
a8430e012f | ||
|
|
85b9890168 | ||
|
|
e3f6af886b | ||
|
|
ac70adf92a | ||
|
|
e1463de00d | ||
|
|
ca5d306b88 | ||
|
|
0bd6c27ab9 | ||
|
|
5c0c77202c | ||
|
|
54b680f757 | ||
|
|
146ef4b7ef | ||
|
|
e619d27559 | ||
|
|
dce83d7e49 | ||
|
|
6d28740a51 | ||
|
|
8d190a3478 | ||
|
|
46494dd46e | ||
|
|
d38b6ab064 | ||
|
|
4f11249b94 | ||
|
|
c433652326 | ||
|
|
62a9fec43f | ||
|
|
466d21e8b4 | ||
|
|
ab92f22c85 | ||
|
|
2e8bbf2b99 | ||
|
|
c3324128d0 | ||
|
|
7be45d3ffc | ||
|
|
86de5fd3ad | ||
|
|
b0c4984b21 | ||
|
|
97bad80784 | ||
|
|
0ec7f5fba1 | ||
|
|
0d2b1431b6 | ||
|
|
8aa4e38760 | ||
|
|
954285e426 | ||
|
|
a4a9938675 | ||
|
|
8a21a2f469 | ||
|
|
a1e9167cdf | ||
|
|
2a07db75c7 | ||
|
|
60280d0e06 | ||
|
|
f0c22db31f | ||
|
|
2ea8443409 | ||
|
|
bd1033ac47 | ||
|
|
ae056f612a | ||
|
|
a512773f11 | ||
|
|
901cbf23bb | ||
|
|
c4ab500235 | ||
|
|
5332fc9b4f | ||
|
|
37971f6875 | ||
|
|
008ebcee79 | ||
|
|
41c4dcea2e | ||
|
|
13368eace9 | ||
|
|
b162844159 | ||
|
|
c9f8235339 | ||
|
|
e1d55e4fc7 | ||
|
|
17bde54c7c | ||
|
|
7abf35ca97 | ||
|
|
e150a22174 | ||
|
|
e9a122fcda | ||
|
|
b09b3f4f2d | ||
|
|
50c9195797 | ||
|
|
bdddcec922 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -179,3 +179,5 @@ fabric.properties
|
||||
|
||||
# Block sensitive configuration files
|
||||
settings.local.yaml
|
||||
log/
|
||||
__debug_bin
|
||||
|
||||
28
.idea/codeStyles/Project.xml
generated
Normal file
28
.idea/codeStyles/Project.xml
generated
Normal file
@@ -0,0 +1,28 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<code_scheme name="Project" version="173">
|
||||
<DBN-PSQL>
|
||||
<case-options enabled="true">
|
||||
<option name="KEYWORD_CASE" value="lower" />
|
||||
<option name="FUNCTION_CASE" value="lower" />
|
||||
<option name="PARAMETER_CASE" value="lower" />
|
||||
<option name="DATATYPE_CASE" value="lower" />
|
||||
<option name="OBJECT_CASE" value="preserve" />
|
||||
</case-options>
|
||||
<formatting-settings enabled="false" />
|
||||
</DBN-PSQL>
|
||||
<DBN-SQL>
|
||||
<case-options enabled="true">
|
||||
<option name="KEYWORD_CASE" value="lower" />
|
||||
<option name="FUNCTION_CASE" value="lower" />
|
||||
<option name="PARAMETER_CASE" value="lower" />
|
||||
<option name="DATATYPE_CASE" value="lower" />
|
||||
<option name="OBJECT_CASE" value="preserve" />
|
||||
</case-options>
|
||||
<formatting-settings enabled="false">
|
||||
<option name="STATEMENT_SPACING" value="one_line" />
|
||||
<option name="CLAUSE_CHOP_DOWN" value="chop_down_if_statement_long" />
|
||||
<option name="ITERATION_ELEMENTS_WRAPPING" value="chop_down_if_not_single" />
|
||||
</formatting-settings>
|
||||
</DBN-SQL>
|
||||
</code_scheme>
|
||||
</component>
|
||||
11
.idea/dataSources.xml
generated
Normal file
11
.idea/dataSources.xml
generated
Normal file
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
|
||||
<data-source source="LOCAL" name="PostgreSQL - postgres@39.105.39.8" uuid="996b1b9f-5c40-4bd6-8c3c-0af67aaaa15d">
|
||||
<driver-ref>postgresql</driver-ref>
|
||||
<synchronize>true</synchronize>
|
||||
<jdbc-driver>org.postgresql.Driver</jdbc-driver>
|
||||
<jdbc-url>jdbc:postgresql://39.105.39.8:9432/postgres</jdbc-url>
|
||||
</data-source>
|
||||
</component>
|
||||
</project>
|
||||
8
.idea/electricity_bill_calc_service.iml
generated
Normal file
8
.idea/electricity_bill_calc_service.iml
generated
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="WEB_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
6
.idea/misc.xml
generated
Normal file
6
.idea/misc.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="JavaScriptSettings">
|
||||
<option name="languageLevel" value="ES6" />
|
||||
</component>
|
||||
</project>
|
||||
8
.idea/modules.xml
generated
Normal file
8
.idea/modules.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/electricity_bill_calc_service.iml" filepath="$PROJECT_DIR$/.idea/electricity_bill_calc_service.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
6
.idea/vcs.xml
generated
Normal file
6
.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
19
.vscode/launch.json
vendored
Normal file
19
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
// 使用 IntelliSense 了解相关属性。
|
||||
// 悬停以查看现有属性的描述。
|
||||
// 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Launch Service",
|
||||
"type": "go",
|
||||
"request": "launch",
|
||||
"mode": "auto",
|
||||
"buildFlags": "-tags=jsoniter",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"program": "${workspaceRoot}/main.go",
|
||||
"args": [
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
14
.vscode/settings.json
vendored
Normal file
14
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"sqltools.connections": [
|
||||
{
|
||||
"previewLimit": 50,
|
||||
"server": "39.105.39.8",
|
||||
"port": 9432,
|
||||
"driver": "PostgreSQL",
|
||||
"name": "Electricity@Archgrid",
|
||||
"database": "electricity",
|
||||
"username": "electricity",
|
||||
"password": "nLgxPO5s8gK2tR0OL0Q"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM golang:1.19-alpine AS builder
|
||||
FROM dockerproxy.com/library/golang:1.20-alpine AS builder
|
||||
|
||||
ENV GO111MODULE=on
|
||||
ENV GOPROXY="https://goproxy.io"
|
||||
@@ -9,7 +9,7 @@ RUN go mod download && go mod verify
|
||||
ADD . /app
|
||||
RUN CGO_ENABLED=0 GOOS=linux go build -tags=jsoniter -v -o server .
|
||||
|
||||
FROM alpine:latest AS production
|
||||
FROM dockerproxy.com/library/alpine:latest AS production
|
||||
RUN echo "https://mirror.tuna.tsinghua.edu.cn/alpine/v3.4/main/" > /etc/apk/repositories
|
||||
RUN apk add --no-cache tzdata
|
||||
RUN apk update \
|
||||
@@ -21,6 +21,7 @@ RUN apk update \
|
||||
ENV TZ=Asia/Shanghai
|
||||
RUN mkdir /app
|
||||
WORKDIR /app
|
||||
VOLUME ["/app/log"]
|
||||
COPY --from=builder /app/server .
|
||||
COPY settings.yaml .
|
||||
COPY regions.csv .
|
||||
|
||||
15
Readme.md
15
Readme.md
@@ -11,3 +11,18 @@
|
||||
项目详细设计方案见[详细设计方案](https://kdocs.cn/l/cawe22YUV3bJ),该设计方案未经许可,禁止私自修改。
|
||||
|
||||
项目任务分配与状态概览表见[任务概况](https://kdocs.cn/l/camrXvBMlCNs)。
|
||||
|
||||
## 项目代码编写要点
|
||||
|
||||
### 数据库访问
|
||||
|
||||
项目所式的数据库框架采用的是Bun,该框架采用贴近SQL语句的数据库方式,但是在使用过程中需注意以下几点。
|
||||
|
||||
1. 要执行一个语句,必须提供一个`context.Context`类型的上下文,推荐采用`context.WithTimeout()`生成。上下文中携带的超时时间应该能覆盖超时时间所需要运行的全部语句。
|
||||
1. 语句中用来指定操作目标数据表的`Model()`方法,其接受的是一个目标变量的地址,如果传入的是一个slice,那么就一定需要使用`&`获取其地址,如果是使用`&struct{}`或者`new()`初始化的,则可以直接作为参数传入。
|
||||
1. 数据库中的时间字段都是带有时区的,但是bun中都是使用UTC时间的,对于数据库中`timestamptz`类型的字段,可以正常的按照数据库配置的时区保存时间,但是对于没有携带时区的`date`类型,就不能直接向其中传入`time.Time`类型的参数了,必须手工将其转化为字符串形式。
|
||||
1. 使用Relation关联获取其他数据表内容的时候,被`Relation()`提及的数据表中配置的`alias`名称将不起作用,语句中的数据别名实际上是被关联字段名称的snake_case形式。
|
||||
1. 需要进行嵌套Relation选择的时候,嵌套的Relation可以采用`Relation("A.B")`的形式来指示使用数据模型A中的Relation B。
|
||||
1. 如果需要只从Relation关联数据表中选择一部分字段,不能直接在主查询语句中使用`Column()`方法,必须在`Relation()`方法的第二个参数中声明。
|
||||
1. 如果需要对Relation关联表中的字段设置`Where`条件子句,那么就必须使用`relation_name__column_name`的双下划线字段选择形式,直接在`Relation()`方法中设定`Where`子句行不通。
|
||||
1. 定义数据模型的时候,数据字段尽可能不要提供默认值,如果确实需要默认值,要首先考虑在数据库中定义默认值约束,其次选择使用Hook来赋予默认值。直接定义在struct tag中的默认值可能会造成bun形成查询语句时意外的空值。
|
||||
Binary file not shown.
72
cache/abstract.go
vendored
72
cache/abstract.go
vendored
@@ -3,6 +3,7 @@ package cache
|
||||
import (
|
||||
"electricity_bill_calc/global"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -23,24 +24,25 @@ const (
|
||||
func Cache[T interface{}](key string, value *T, expires time.Duration) error {
|
||||
var err error
|
||||
if expires > 0 {
|
||||
setCmd := global.RedisConn.B().Set().
|
||||
realExpires := expires + time.Duration(rand.Int63n(60))*time.Second
|
||||
setCmd := global.Rd.B().Set().
|
||||
Key(key).Value(rueidis.JSON(value)).
|
||||
ExSeconds(int64(expires.Seconds())).
|
||||
ExSeconds(int64(realExpires.Seconds())).
|
||||
Build()
|
||||
err = global.RedisConn.Do(global.Ctx, setCmd).Error()
|
||||
err = global.Rd.Do(global.Ctx, setCmd).Error()
|
||||
} else {
|
||||
setCmd := global.RedisConn.B().Set().
|
||||
setCmd := global.Rd.B().Set().
|
||||
Key(key).Value(rueidis.JSON(value)).
|
||||
Build()
|
||||
err = global.RedisConn.Do(global.Ctx, setCmd).Error()
|
||||
err = global.Rd.Do(global.Ctx, setCmd).Error()
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// 从Redis缓存中获取一个数据
|
||||
func Retreive[T interface{}](key string) (*T, error) {
|
||||
getCmd := global.RedisConn.B().Get().Key(key).Build()
|
||||
result := global.RedisConn.Do(global.Ctx, getCmd)
|
||||
func Retrieve[T interface{}](key string) (*T, error) {
|
||||
getCmd := global.Rd.B().Get().Key(key).Build()
|
||||
result := global.Rd.Do(global.Ctx, getCmd)
|
||||
if result.Error() != nil {
|
||||
if rueidis.IsRedisNil(result.Error()) {
|
||||
return nil, nil
|
||||
@@ -58,8 +60,8 @@ func Retreive[T interface{}](key string) (*T, error) {
|
||||
|
||||
// 检查Redis缓存中是否存在指定键的记录
|
||||
func Exists(key string) (bool, error) {
|
||||
existsCmd := global.RedisConn.B().Exists().Key(key).Build()
|
||||
result := global.RedisConn.Do(global.Ctx, existsCmd)
|
||||
existsCmd := global.Rd.B().Exists().Key(key).Build()
|
||||
result := global.Rd.Do(global.Ctx, existsCmd)
|
||||
if result.Error() != nil {
|
||||
return false, result.Error()
|
||||
}
|
||||
@@ -70,8 +72,8 @@ func Exists(key string) (bool, error) {
|
||||
// 从Redis缓存中删除指定键
|
||||
// ! 如果指定键已不存在,那么本函数一样会返回false
|
||||
func Delete(key string) (bool, error) {
|
||||
deleteCmd := global.RedisConn.B().Del().Key(key).Build()
|
||||
result := global.RedisConn.Do(global.Ctx, deleteCmd)
|
||||
deleteCmd := global.Rd.B().Del().Key(key).Build()
|
||||
result := global.Rd.Do(global.Ctx, deleteCmd)
|
||||
if result.Error() != nil {
|
||||
return false, result.Error()
|
||||
}
|
||||
@@ -79,23 +81,23 @@ func Delete(key string) (bool, error) {
|
||||
return count > 0, err
|
||||
}
|
||||
|
||||
func dissembleScan(result rueidis.RedisResult) (int64, []string, error) {
|
||||
func dissembleScan(result rueidis.RedisResult) (uint64, []string, error) {
|
||||
var (
|
||||
err error
|
||||
cursor int64
|
||||
cursor uint64
|
||||
keys = make([]string, 0)
|
||||
)
|
||||
results, err := result.ToArray()
|
||||
if err != nil {
|
||||
return -1, keys, err
|
||||
return 0, keys, err
|
||||
}
|
||||
cursor, err = results[0].AsInt64()
|
||||
cursor, err = results[0].AsUint64()
|
||||
if err != nil {
|
||||
return -1, keys, err
|
||||
return 0, keys, err
|
||||
}
|
||||
keys, err = results[1].AsStrSlice()
|
||||
if err != nil {
|
||||
return -1, keys, err
|
||||
return 0, keys, err
|
||||
}
|
||||
return cursor, keys, err
|
||||
}
|
||||
@@ -104,13 +106,13 @@ func dissembleScan(result rueidis.RedisResult) (int64, []string, error) {
|
||||
func DeleteAll(pattern string) error {
|
||||
var (
|
||||
err error
|
||||
cursor int64
|
||||
cursor uint64
|
||||
keys = make([]string, 0)
|
||||
sKeys []string
|
||||
)
|
||||
for {
|
||||
scanCmd := global.RedisConn.B().Scan().Cursor(cursor).Match(pattern).Count(20).Build()
|
||||
results := global.RedisConn.Do(global.Ctx, scanCmd)
|
||||
scanCmd := global.Rd.B().Scan().Cursor(cursor).Match(pattern).Count(20).Build()
|
||||
results := global.Rd.Do(global.Ctx, scanCmd)
|
||||
cursor, sKeys, err = dissembleScan(results)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -121,8 +123,8 @@ func DeleteAll(pattern string) error {
|
||||
}
|
||||
}
|
||||
|
||||
delCmd := global.RedisConn.B().Del().Key(keys...).Build()
|
||||
err = global.RedisConn.Do(global.Ctx, delCmd).Error()
|
||||
delCmd := global.Rd.B().Del().Key(keys...).Build()
|
||||
err = global.Rd.Do(global.Ctx, delCmd).Error()
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -135,3 +137,27 @@ func CacheKey(category string, ids ...string) string {
|
||||
}
|
||||
return b.String()
|
||||
}
|
||||
|
||||
type ToString[T any] interface {
|
||||
ToString() string
|
||||
*T
|
||||
}
|
||||
|
||||
// 用于生成一个内容可以为空的Redis缓存键,这个键的类型必须实现了`ToString`接口。
|
||||
func NullableConditionKey[P any, T ToString[P]](value T, defaultStr ...string) string {
|
||||
defaultStr = append(defaultStr, "UNDEF")
|
||||
if value == nil {
|
||||
return defaultStr[0]
|
||||
} else {
|
||||
return value.ToString()
|
||||
}
|
||||
}
|
||||
|
||||
// 用于生成一个内容可以为空的字符串指针类型的Redis缓存键。
|
||||
func NullableStringKey(value *string, defaultStr ...string) string {
|
||||
defaultStr = append(defaultStr, "UNDEF")
|
||||
if value == nil {
|
||||
return defaultStr[0]
|
||||
}
|
||||
return *value
|
||||
}
|
||||
|
||||
57
cache/count.go
vendored
57
cache/count.go
vendored
@@ -1,49 +1,58 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"electricity_bill_calc/global"
|
||||
"strconv"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func assembleCountKey(entityName string) string {
|
||||
var keys = make([]string, 0)
|
||||
keys = append(keys, strings.ToUpper(entityName))
|
||||
return CacheKey(TAG_COUNT, keys...)
|
||||
type _CountRecord struct {
|
||||
Count int64
|
||||
}
|
||||
|
||||
func assembleCountIdentification(additional ...string) string {
|
||||
return strings.Join(additional, ":")
|
||||
func AssembleCountKey(entityName string, additional ...string) string {
|
||||
var keys = make([]string, 0)
|
||||
keys = append(keys, strings.ToUpper(entityName))
|
||||
keys = append(keys, additional...)
|
||||
var b strings.Builder
|
||||
b.WriteString(TAG_COUNT)
|
||||
for _, s := range keys {
|
||||
fmt.Fprintf(&b, ":%s", s)
|
||||
}
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// 向缓存中缓存模型名称明确的包含指定条件的实体记录数量
|
||||
func CacheCount(relationNames []string, entityName string, count int64, conditions ...string) error {
|
||||
countKey := assembleCountKey(entityName)
|
||||
identification := assembleCountIdentification(conditions...)
|
||||
cmd := global.RedisConn.B().Hset().Key(countKey).FieldValue().FieldValue(identification, strconv.FormatInt(count, 10)).Build()
|
||||
result := global.RedisConn.Do(global.Ctx, cmd)
|
||||
countKey := AssembleCountKey(entityName, conditions...)
|
||||
cacheInstance := &_CountRecord{Count: count}
|
||||
err := Cache(countKey, cacheInstance, 5*time.Minute)
|
||||
for _, relationName := range relationNames {
|
||||
CacheRelation(relationName, STORE_TYPE_HASH, countKey, identification)
|
||||
CacheRelation(relationName, STORE_TYPE_KEY, countKey)
|
||||
}
|
||||
return result.Error()
|
||||
return err
|
||||
}
|
||||
|
||||
// 从缓存中获取模型名称明确的,包含指定条件的实体记录数量
|
||||
func RetreiveCount(entityName string, condtions ...string) (int64, error) {
|
||||
countKey := assembleCountKey(entityName)
|
||||
identification := assembleCountIdentification(condtions...)
|
||||
cmd := global.RedisConn.B().Hget().Key(countKey).Field(identification).Build()
|
||||
result, err := global.RedisConn.Do(global.Ctx, cmd).AsInt64()
|
||||
func RetrieveCount(entityName string, condtions ...string) (int64, error) {
|
||||
countKey := AssembleCountKey(entityName, condtions...)
|
||||
exist, err := Exists(countKey)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
return result, nil
|
||||
if !exist {
|
||||
return -1, nil
|
||||
}
|
||||
instance, err := Retrieve[_CountRecord](countKey)
|
||||
if instance != nil && err == nil {
|
||||
return instance.Count, nil
|
||||
} else {
|
||||
return -1, err
|
||||
}
|
||||
}
|
||||
|
||||
// 删除指定模型名称的数量缓存
|
||||
func AbolishCountEntity(entityName string) error {
|
||||
countKey := assembleCountKey(entityName)
|
||||
cmd := global.RedisConn.B().Del().Key(countKey).Build()
|
||||
err := global.RedisConn.Do(global.Ctx, cmd).Error()
|
||||
return err
|
||||
pattern := fmt.Sprintf("%s:%s:*", TAG_COUNT, strings.ToUpper(entityName))
|
||||
return DeleteAll(pattern)
|
||||
}
|
||||
|
||||
15
cache/entity.go
vendored
15
cache/entity.go
vendored
@@ -3,9 +3,10 @@ package cache
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func assembleEntityKey(entityName, id string) string {
|
||||
func AssembleEntityKey(entityName, id string) string {
|
||||
var keys = make([]string, 0)
|
||||
keys = append(keys, strings.ToUpper(entityName), id)
|
||||
var b strings.Builder
|
||||
@@ -18,8 +19,8 @@ func assembleEntityKey(entityName, id string) string {
|
||||
|
||||
// 缓存模型名称明确的,使用ID进行检索的实体内容。
|
||||
func CacheEntity[T any](instance T, relationNames []string, entityName, id string) error {
|
||||
entityKey := assembleEntityKey(entityName, id)
|
||||
err := Cache(entityKey, &instance, -1)
|
||||
entityKey := AssembleEntityKey(entityName, id)
|
||||
err := Cache(entityKey, &instance, 5*time.Minute)
|
||||
for _, relationName := range relationNames {
|
||||
CacheRelation(relationName, STORE_TYPE_KEY, entityKey)
|
||||
}
|
||||
@@ -27,15 +28,15 @@ func CacheEntity[T any](instance T, relationNames []string, entityName, id strin
|
||||
}
|
||||
|
||||
// 从缓存中取出模型名称明确的,使用ID进行检索的实体内容。
|
||||
func RetreiveEntity[T any](entityName, id string) (*T, error) {
|
||||
entityKey := assembleEntityKey(entityName, id)
|
||||
instance, err := Retreive[T](entityKey)
|
||||
func RetrieveEntity[T any](entityName, id string) (*T, error) {
|
||||
entityKey := AssembleEntityKey(entityName, id)
|
||||
instance, err := Retrieve[T](entityKey)
|
||||
return instance, err
|
||||
}
|
||||
|
||||
// 精确的从缓存中删除指定模型名称、指定ID的实体内容。
|
||||
func AbolishSpecificEntity(entityName, id string) (bool, error) {
|
||||
entityKey := assembleEntityKey(entityName, id)
|
||||
entityKey := AssembleEntityKey(entityName, id)
|
||||
return Delete(entityKey)
|
||||
}
|
||||
|
||||
|
||||
63
cache/exists.go
vendored
63
cache/exists.go
vendored
@@ -1,72 +1,49 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"electricity_bill_calc/global"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
func assembleExistsKey(entityName string) string {
|
||||
func AssembleExistsKey(entityName string, additional ...string) string {
|
||||
var keys = make([]string, 0)
|
||||
keys = append(keys, strings.ToUpper(entityName))
|
||||
return CacheKey(TAG_EXISTS, keys...)
|
||||
}
|
||||
|
||||
func assembleExistsIdentification(additional ...string) string {
|
||||
return strings.Join(additional, ":")
|
||||
keys = append(keys, additional...)
|
||||
var b strings.Builder
|
||||
b.WriteString(TAG_EXISTS)
|
||||
for _, s := range keys {
|
||||
fmt.Fprintf(&b, ":%s", s)
|
||||
}
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// 缓存模型名称明确的、包含指定ID以及一些附加条件的记录
|
||||
func CacheExists(relationNames []string, entityName string, conditions ...string) error {
|
||||
existskey := assembleExistsKey(entityName)
|
||||
identification := assembleExistsIdentification(conditions...)
|
||||
cmd := global.RedisConn.B().Sadd().Key(existskey).Member(identification).Build()
|
||||
err := global.RedisConn.Do(global.Ctx, cmd).Error()
|
||||
existskey := AssembleExistsKey(entityName, conditions...)
|
||||
err := Cache(existskey, lo.ToPtr(true), 5*time.Minute)
|
||||
for _, relationName := range relationNames {
|
||||
CacheRelation(relationName, STORE_TYPE_SET, existskey, identification)
|
||||
CacheRelation(relationName, STORE_TYPE_KEY, existskey)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// 从缓存中获取模型名称明确、包含指定ID以及一些附加条件的实体是否存在的标记,函数在返回false时不保证数据库中相关记录也不存在
|
||||
func CheckExists(entityName string, condtions ...string) (bool, error) {
|
||||
existsKey := assembleExistsKey(entityName)
|
||||
identification := assembleExistsIdentification(condtions...)
|
||||
cmd := global.RedisConn.B().Sismember().Key(existsKey).Member(identification).Build()
|
||||
result, err := global.RedisConn.Do(global.Ctx, cmd).AsBool()
|
||||
return result, err
|
||||
existsKey := AssembleExistsKey(entityName, condtions...)
|
||||
return Exists(existsKey)
|
||||
}
|
||||
|
||||
// 从缓存中删除模型名称明确、包含指定ID的全部实体存在标记
|
||||
func AbolishExists(entityName, id string) error {
|
||||
existsKey := assembleExistsKey(entityName)
|
||||
pattern := fmt.Sprintf("%s*", id)
|
||||
var (
|
||||
err error
|
||||
cursor int64
|
||||
elems = make([]string, 0)
|
||||
sElem []string
|
||||
)
|
||||
for {
|
||||
cmd := global.RedisConn.B().Sscan().Key(existsKey).Cursor(cursor).Match(pattern).Count(20).Build()
|
||||
result := global.RedisConn.Do(global.Ctx, cmd)
|
||||
cursor, sElem, err = dissembleScan(result)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
elems = append(elems, sElem...)
|
||||
if cursor == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
cmd := global.RedisConn.B().Srem().Key(existsKey).Member(elems...).Build()
|
||||
err = global.RedisConn.Do(global.Ctx, cmd).Error()
|
||||
return err
|
||||
pattern := fmt.Sprintf("%s:%s:%s:*", TAG_EXISTS, strings.ToUpper(entityName), id)
|
||||
return DeleteAll(pattern)
|
||||
}
|
||||
|
||||
// 从缓存中删除指定模型名称的全部存在标记
|
||||
func AbolishExistsEntity(entityName string) error {
|
||||
existskey := assembleExistsKey(entityName)
|
||||
_, err := Delete(existskey)
|
||||
return err
|
||||
pattern := fmt.Sprintf("%s:%s:*", TAG_EXISTS, strings.ToUpper(entityName))
|
||||
return DeleteAll(pattern)
|
||||
}
|
||||
|
||||
77
cache/relation.go
vendored
77
cache/relation.go
vendored
@@ -2,6 +2,7 @@ package cache
|
||||
|
||||
import (
|
||||
"electricity_bill_calc/global"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/rueian/rueidis"
|
||||
@@ -14,13 +15,13 @@ const (
|
||||
STORE_TYPE_HASH = "HASH"
|
||||
)
|
||||
|
||||
func assembleRelationKey(relationName string) string {
|
||||
func AssembleRelationKey(relationName string) string {
|
||||
var keys = make([]string, 0)
|
||||
keys = append(keys, strings.ToUpper(relationName))
|
||||
return CacheKey(TAG_RELATION, keys...)
|
||||
}
|
||||
|
||||
func assembleRelationIdentity(storeType, key string, field ...string) string {
|
||||
func AssembleRelationIdentity(storeType, key string, field ...string) string {
|
||||
var identity = make([]string, 0)
|
||||
identity = append(identity, storeType, key)
|
||||
identity = append(identity, field...)
|
||||
@@ -29,18 +30,18 @@ func assembleRelationIdentity(storeType, key string, field ...string) string {
|
||||
|
||||
// 向缓存中保存与指定关联名称相关联的键的名称以及键的类型和子字段的组成。
|
||||
func CacheRelation(relationName, storeType, key string, field ...string) error {
|
||||
relationKey := assembleRelationKey(relationName)
|
||||
relationIdentity := assembleRelationIdentity(storeType, key, field...)
|
||||
cmd := global.RedisConn.B().Sadd().Key(relationKey).Member(relationIdentity).Build()
|
||||
result := global.RedisConn.Do(global.Ctx, cmd)
|
||||
relationKey := AssembleRelationKey(relationName)
|
||||
relationIdentity := AssembleRelationIdentity(storeType, key, field...)
|
||||
cmd := global.Rd.B().Sadd().Key(relationKey).Member(relationIdentity).Build()
|
||||
result := global.Rd.Do(global.Ctx, cmd)
|
||||
return result.Error()
|
||||
}
|
||||
|
||||
// 从缓存中清理指定的关联键
|
||||
func AbolishRelation(relationName string) error {
|
||||
relationKey := assembleRelationKey(relationName)
|
||||
cmd := global.RedisConn.B().Smembers().Key(relationKey).Build()
|
||||
relationItems, err := global.RedisConn.Do(global.Ctx, cmd).AsStrSlice()
|
||||
relationKey := AssembleRelationKey(relationName)
|
||||
cmd := global.Rd.B().Smembers().Key(relationKey).Build()
|
||||
relationItems, err := global.Rd.Do(global.Ctx, cmd).AsStrSlice()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -49,17 +50,67 @@ func AbolishRelation(relationName string) error {
|
||||
separated := strings.Split(item, ";")
|
||||
switch separated[0] {
|
||||
case STORE_TYPE_KEY:
|
||||
cmd := global.RedisConn.B().Del().Key(separated[1]).Build()
|
||||
cmd := global.Rd.B().Del().Key(separated[1]).Build()
|
||||
cmds = append(cmds, cmd)
|
||||
case STORE_TYPE_HASH:
|
||||
cmd := global.RedisConn.B().Hdel().Key(separated[1]).Field(separated[2:]...).Build()
|
||||
cmd := global.Rd.B().Hdel().Key(separated[1]).Field(separated[2:]...).Build()
|
||||
cmds = append(cmds, cmd)
|
||||
case STORE_TYPE_SET:
|
||||
cmd := global.RedisConn.B().Srem().Key(separated[1]).Member(separated[2:]...).Build()
|
||||
cmd := global.Rd.B().Srem().Key(separated[1]).Member(separated[2:]...).Build()
|
||||
cmds = append(cmds, cmd)
|
||||
}
|
||||
}
|
||||
errs := global.RedisConn.DoMulti(global.Ctx, cmds...)
|
||||
errs := global.Rd.DoMulti(global.Ctx, cmds...)
|
||||
firstErr, has := lo.Find(errs, func(elem rueidis.RedisResult) bool {
|
||||
return elem.Error() != nil
|
||||
})
|
||||
if has {
|
||||
return firstErr.Error()
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func ClearOrphanRelationItems() error {
|
||||
var (
|
||||
err error
|
||||
cursor uint64
|
||||
keys = make([]string, 0)
|
||||
sKeys []string
|
||||
)
|
||||
for {
|
||||
scanCmd := global.Rd.B().Scan().Cursor(cursor).Match(fmt.Sprintf("%s:*", TAG_RELATION)).Count(20).Build()
|
||||
results := global.Rd.Do(global.Ctx, scanCmd)
|
||||
cursor, sKeys, err = dissembleScan(results)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
keys = append(keys, sKeys...)
|
||||
if cursor == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
var cmds = make(rueidis.Commands, 0)
|
||||
for _, key := range keys {
|
||||
relationItemsCmd := global.Rd.B().Smembers().Key(key).Build()
|
||||
results := global.Rd.Do(global.Ctx, relationItemsCmd)
|
||||
relationItems, err := results.AsStrSlice()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, item := range relationItems {
|
||||
separated := strings.Split(item, ";")
|
||||
exist, err := Exists(separated[1])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !exist {
|
||||
cmd := global.Rd.B().Srem().Key(key).Member(item).Build()
|
||||
cmds = append(cmds, cmd)
|
||||
}
|
||||
}
|
||||
}
|
||||
errs := global.Rd.DoMulti(global.Ctx, cmds...)
|
||||
firstErr, has := lo.Find(errs, func(elem rueidis.RedisResult) bool {
|
||||
return elem.Error() != nil
|
||||
})
|
||||
|
||||
33
cache/repository.go
vendored
33
cache/repository.go
vendored
@@ -1,33 +0,0 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"electricity_bill_calc/config"
|
||||
)
|
||||
|
||||
func CacheData[T interface{}](instance T, category string, key ...string) error {
|
||||
var keys = make([]string, 0)
|
||||
keys = append(keys, category)
|
||||
keys = append(keys, key...)
|
||||
cacheKey := CacheKey("cache", keys...)
|
||||
if exists, _ := Exists(cacheKey); exists {
|
||||
Delete(cacheKey)
|
||||
}
|
||||
return Cache(cacheKey, &instance, config.ServiceSettings.CacheLifeTime)
|
||||
}
|
||||
|
||||
func RetreiveData[T interface{}](category string, key ...string) (*T, error) {
|
||||
var keys = make([]string, 0)
|
||||
keys = append(keys, category)
|
||||
keys = append(keys, key...)
|
||||
return Retreive[T](CacheKey("cache", keys...))
|
||||
}
|
||||
|
||||
func AbolishCacheData(category string, key ...string) {
|
||||
var keys = make([]string, 0)
|
||||
keys = append(keys, category)
|
||||
keys = append(keys, key...)
|
||||
cacheKey := CacheKey("cache", keys...)
|
||||
if exists, _ := Exists(cacheKey); exists {
|
||||
Delete(cacheKey)
|
||||
}
|
||||
}
|
||||
65
cache/search.go
vendored
65
cache/search.go
vendored
@@ -1,11 +1,17 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"electricity_bill_calc/logger"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
func assembleSearchKey(entityName string, additional ...string) string {
|
||||
var log = logger.Named("Cache")
|
||||
|
||||
func AssembleSearchKey(entityName string, additional ...string) string {
|
||||
var keys = make([]string, 0)
|
||||
keys = append(keys, strings.ToUpper(entityName))
|
||||
keys = append(keys, additional...)
|
||||
@@ -19,8 +25,8 @@ func assembleSearchKey(entityName string, additional ...string) string {
|
||||
|
||||
// 缓存模型名称明确的,使用或者包含非ID检索条件的实体内容。
|
||||
func CacheSearch[T any](instance T, relationNames []string, entityName string, conditions ...string) error {
|
||||
searchKey := assembleSearchKey(entityName, conditions...)
|
||||
err := Cache(searchKey, &instance, -1)
|
||||
searchKey := AssembleSearchKey(entityName, conditions...)
|
||||
err := Cache(searchKey, &instance, 5*time.Minute)
|
||||
for _, relationName := range relationNames {
|
||||
CacheRelation(relationName, STORE_TYPE_KEY, searchKey)
|
||||
}
|
||||
@@ -28,9 +34,9 @@ func CacheSearch[T any](instance T, relationNames []string, entityName string, c
|
||||
}
|
||||
|
||||
// 从缓存中取得模型名称明确的,使用或者包含非ID检索条件的实体内容。
|
||||
func RetreiveSearch[T any](entityName string, conditions ...string) (*T, error) {
|
||||
searchKey := assembleSearchKey(entityName, conditions...)
|
||||
instance, err := Retreive[T](searchKey)
|
||||
func RetrieveSearch[T any](entityName string, conditions ...string) (*T, error) {
|
||||
searchKey := AssembleSearchKey(entityName, conditions...)
|
||||
instance, err := Retrieve[T](searchKey)
|
||||
return instance, err
|
||||
}
|
||||
|
||||
@@ -39,3 +45,50 @@ func AbolishSearch(entityName string) error {
|
||||
pattern := fmt.Sprintf("%s:%s:*", TAG_SEARCH, strings.ToUpper(entityName))
|
||||
return DeleteAll(pattern)
|
||||
}
|
||||
|
||||
// 向缓存中保存指定模型名称的分页检索结果,会同时采用`CacheCount`中的方法保存检索结果的总数量。
|
||||
func CachePagedSearch[T any](instance T, total int64, relationNames []string, entityName string, conditions ...string) error {
|
||||
searchKey := AssembleSearchKey(entityName, conditions...)
|
||||
countKey := AssembleCountKey(entityName, conditions...)
|
||||
err := Cache(searchKey, &instance, 5*time.Minute)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cacheInstance := &_CountRecord{Count: total}
|
||||
err = Cache(countKey, cacheInstance, 5*time.Minute)
|
||||
for _, relationName := range relationNames {
|
||||
CacheRelation(relationName, STORE_TYPE_KEY, searchKey)
|
||||
CacheRelation(relationName, STORE_TYPE_KEY, countKey)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// 从缓存中获取指定模型名称的分页检索结果,会同时采用`RetrieveCount`中的方法获取检索结果的总数量。
|
||||
func RetrievePagedSearch[T any](entityName string, conditions ...string) (*T, int64, error) {
|
||||
searchKey := AssembleSearchKey(entityName, conditions...)
|
||||
countKey := AssembleCountKey(entityName, conditions...)
|
||||
instance, err := Retrieve[T](searchKey)
|
||||
if err != nil {
|
||||
return nil, -1, err
|
||||
}
|
||||
count, err := Retrieve[_CountRecord](countKey)
|
||||
if err != nil {
|
||||
return nil, -1, err
|
||||
}
|
||||
if instance == nil || count == nil {
|
||||
log.Warn("检索结果或者检索总数为空。", zap.String("searchKey", searchKey), zap.String("countKey", countKey))
|
||||
return nil, -1, nil
|
||||
}
|
||||
return instance, count.Count, nil
|
||||
}
|
||||
|
||||
// 从缓存中删除指定模型名称的分页检索结果,会同时采用`AbolishCountEntity`中的方法删除检索结果的总数量。
|
||||
func AbolishPagedSearch(entityName string) error {
|
||||
pattern := fmt.Sprintf("%s:%s:*", TAG_SEARCH, strings.ToUpper(entityName))
|
||||
err := DeleteAll(pattern)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pattern = fmt.Sprintf("%s:%s:*", TAG_COUNT, strings.ToUpper(entityName))
|
||||
return DeleteAll(pattern)
|
||||
}
|
||||
|
||||
4
cache/session.go
vendored
4
cache/session.go
vendored
@@ -21,9 +21,9 @@ func CacheSession(session *model.Session) error {
|
||||
return Cache(key, session, config.ServiceSettings.MaxSessionLife)
|
||||
}
|
||||
|
||||
func RetreiveSession(token string) (*model.Session, error) {
|
||||
func RetrieveSession(token string) (*model.Session, error) {
|
||||
key := SessionKey(token)
|
||||
return Retreive[model.Session](key)
|
||||
return Retrieve[model.Session](key)
|
||||
}
|
||||
|
||||
func HasSession(token string) (bool, error) {
|
||||
|
||||
@@ -31,8 +31,14 @@ type RedisSetting struct {
|
||||
|
||||
type ServiceSetting struct {
|
||||
MaxSessionLife time.Duration
|
||||
ItemsPageSize int
|
||||
ItemsPageSize uint
|
||||
CacheLifeTime time.Duration
|
||||
HostSerial int64
|
||||
}
|
||||
|
||||
//读取基准线损率
|
||||
type BaseLossSetting struct {
|
||||
Base string
|
||||
}
|
||||
|
||||
// 定义全局变量
|
||||
@@ -41,6 +47,7 @@ var (
|
||||
DatabaseSettings *DatabaseSetting
|
||||
RedisSettings *RedisSetting
|
||||
ServiceSettings *ServiceSetting
|
||||
BaseLoss *BaseLossSetting
|
||||
)
|
||||
|
||||
// 读取配置到全局变量
|
||||
@@ -68,5 +75,10 @@ func SetupSetting() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = s.ReadSection("BaselineLineLossRatio", &BaseLoss)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -3,13 +3,17 @@ package controller
|
||||
import (
|
||||
"electricity_bill_calc/exceptions"
|
||||
"electricity_bill_calc/model"
|
||||
"electricity_bill_calc/repository"
|
||||
"electricity_bill_calc/response"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
func _retreiveSession(c *gin.Context) (*model.Session, error) {
|
||||
session, exists := c.Get("session")
|
||||
if !exists {
|
||||
func _retreiveSession(c *fiber.Ctx) (*model.Session, error) {
|
||||
session := c.Locals("session")
|
||||
if session == nil {
|
||||
return nil, exceptions.NewUnauthorizedError("用户会话不存在")
|
||||
}
|
||||
userSession, ok := session.(*model.Session)
|
||||
@@ -18,3 +22,26 @@ func _retreiveSession(c *gin.Context) (*model.Session, error) {
|
||||
}
|
||||
return userSession, nil
|
||||
}
|
||||
|
||||
// 检查当前用户是否拥有指定园区,在判断完成之后直接产生响应
|
||||
func checkParkBelongs(parkId string, logger *zap.Logger, c *fiber.Ctx, result *response.Result) (bool, error) {
|
||||
session := c.Locals("session")
|
||||
if session == nil {
|
||||
logger.Error("用户会话不存在。")
|
||||
return false, result.Unauthorized("用户会话不存在。")
|
||||
}
|
||||
userSession, ok := session.(*model.Session)
|
||||
if !ok {
|
||||
return false, result.Unauthorized("用户会话格式不正确,需要重新登录")
|
||||
}
|
||||
ok, err := repository.ParkRepository.IsParkBelongs(parkId, userSession.Uid)
|
||||
switch {
|
||||
case err != nil:
|
||||
logger.Error("无法判断园区是否隶属于当前用户。", zap.String("park id", parkId), zap.String("user id", userSession.Uid), zap.Error(err))
|
||||
return false, result.Error(http.StatusInternalServerError, err.Error())
|
||||
case err == nil && !ok:
|
||||
logger.Error("用户试图访问不属于自己的园区。", zap.String("park id", parkId), zap.String("user id", userSession.Uid))
|
||||
return false, result.Forbidden("您无权访问该园区。")
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
@@ -1,94 +1,97 @@
|
||||
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/types"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/shopspring/decimal"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
func InitializeChargesController(router *gin.Engine) {
|
||||
router.GET("/charges", security.OPSAuthorize, listAllCharges)
|
||||
router.POST("/charge", security.OPSAuthorize, recordNewCharge)
|
||||
router.PUT("/charge/:uid/:seq", security.OPSAuthorize, modifyChargeState)
|
||||
var chargeLog = logger.Named("Handler", "Charge")
|
||||
|
||||
func InitializeChargeHandlers(router *fiber.App) {
|
||||
router.Get("/charge", searchCharges)
|
||||
router.Post("/charge", createNewUserChargeRecord)
|
||||
router.Put("/charge/:uid/:seq", modifyUserChargeState)
|
||||
}
|
||||
|
||||
func listAllCharges(c *gin.Context) {
|
||||
// 检索用户的充值记录列表
|
||||
func searchCharges(c *fiber.Ctx) error {
|
||||
chargeLog.Info("检索用户的充值记录列表。")
|
||||
result := response.NewResult(c)
|
||||
requestPage, err := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||
keyword := c.Query("keyword", "")
|
||||
page := c.QueryInt("page", 1)
|
||||
beginTime := types.ParseDateWithDefault(c.Query("begin"), types.NewEmptyDate())
|
||||
endTime := types.ParseDateWithDefault(c.Query("end"), types.MaxDate())
|
||||
charges, total, err := repository.ChargeRepository.FindCharges(uint(page), &beginTime, &endTime, &keyword)
|
||||
if err != nil {
|
||||
result.NotAccept("查询参数[page]格式不正确。")
|
||||
return
|
||||
chargeLog.Error("检索用户的充值记录列表失败。", zap.Error(err))
|
||||
return result.Error(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
requestKeyword := c.DefaultQuery("keyword", "")
|
||||
requestBeginDate := c.DefaultQuery("begin", "")
|
||||
requestEndDate := c.DefaultQuery("end", "")
|
||||
charges, total, err := service.ChargeService.ListPagedChargeRecord(requestKeyword, requestBeginDate, requestEndDate, requestPage)
|
||||
if err != nil {
|
||||
result.NotFound(err.Error())
|
||||
return
|
||||
}
|
||||
result.Json(
|
||||
http.StatusOK, "已获取到符合条件的计费记录。",
|
||||
response.NewPagedResponse(requestPage, total).ToMap(),
|
||||
gin.H{"records": charges},
|
||||
return result.Success(
|
||||
"已经获取到符合条件的计费记录。",
|
||||
response.NewPagedResponse(page, total).ToMap(),
|
||||
fiber.Map{"records": charges},
|
||||
)
|
||||
}
|
||||
|
||||
type _NewChargeFormData struct {
|
||||
UserId string `json:"userId" form:"userId"`
|
||||
Fee decimal.NullDecimal `json:"fee" form:"fee"`
|
||||
Discount decimal.NullDecimal `json:"discount" form:"discount"`
|
||||
Amount decimal.NullDecimal `json:"amount" form:"amount"`
|
||||
ChargeTo time.Time `json:"chargeTo" form:"chargeTo" time_format:"simple_date" time_location:"shanghai"`
|
||||
}
|
||||
|
||||
func recordNewCharge(c *gin.Context) {
|
||||
// 创建一条新的用户充值记录
|
||||
func createNewUserChargeRecord(c *fiber.Ctx) error {
|
||||
chargeLog.Info("创建一条新的用户充值记录。")
|
||||
result := response.NewResult(c)
|
||||
formData := new(_NewChargeFormData)
|
||||
c.BindJSON(formData)
|
||||
currentTime := time.Now()
|
||||
newRecord := &model.UserCharge{
|
||||
UserId: formData.UserId,
|
||||
Fee: formData.Fee,
|
||||
Discount: formData.Discount,
|
||||
Amount: formData.Amount,
|
||||
Settled: true,
|
||||
SettledAt: ¤tTime,
|
||||
ChargeTo: formData.ChargeTo,
|
||||
createionForm := new(model.ChargeRecordCreationForm)
|
||||
if err := c.BodyParser(createionForm); err != nil {
|
||||
chargeLog.Error("无法解析创建充值记录的请求数据。", zap.Error(err))
|
||||
return result.Error(http.StatusBadRequest, err.Error())
|
||||
}
|
||||
err := service.ChargeService.CreateChargeRecord(newRecord, true)
|
||||
fee, _ := createionForm.Fee.Decimal.Float64()
|
||||
discount, _ := createionForm.Discount.Decimal.Float64()
|
||||
amount, _ := createionForm.Amount.Decimal.Float64()
|
||||
ok, err := service.ChargeService.RecordUserCharge(
|
||||
createionForm.UserId,
|
||||
&fee,
|
||||
&discount,
|
||||
&amount,
|
||||
createionForm.ChargeTo,
|
||||
true,
|
||||
)
|
||||
if err != nil {
|
||||
result.Error(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
chargeLog.Error("创建用户充值记录失败。", zap.Error(err))
|
||||
return result.Error(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
if !ok {
|
||||
chargeLog.Error("创建用户充值记录失败。")
|
||||
return result.NotAccept("创建用户充值记录失败。")
|
||||
} else {
|
||||
return result.Success("创建用户充值记录成功, 指定用户的服务已延期。")
|
||||
}
|
||||
result.Created("指定用户的服务已延期。")
|
||||
}
|
||||
|
||||
type _StateChangeFormData struct {
|
||||
Cancelled bool `json:"cancelled"`
|
||||
}
|
||||
|
||||
func modifyChargeState(c *gin.Context) {
|
||||
// 改变用户充值记录的状态
|
||||
func modifyUserChargeState(c *fiber.Ctx) error {
|
||||
chargeLog.Info("改变用户充值记录的状态。")
|
||||
result := response.NewResult(c)
|
||||
formData := new(_StateChangeFormData)
|
||||
c.BindJSON(formData)
|
||||
requestUserID := c.Param("uid")
|
||||
requestChargeSeq, err := strconv.Atoi(c.Param("seq"))
|
||||
uid := c.Params("uid")
|
||||
seq, err := c.ParamsInt("seq")
|
||||
if err != nil {
|
||||
result.Error(http.StatusNotAcceptable, "参数[记录流水号]解析错误。")
|
||||
return
|
||||
chargeLog.Error("无法解析请求参数。", zap.Error(err))
|
||||
return result.Error(http.StatusBadRequest, err.Error())
|
||||
}
|
||||
err = service.ChargeService.CancelCharge(int64(requestChargeSeq), requestUserID)
|
||||
ok, err := service.ChargeService.CancelUserCharge(uid, int64(seq))
|
||||
if err != nil {
|
||||
result.Error(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
chargeLog.Error("取消用户充值记录失败。", zap.Error(err))
|
||||
return result.Error(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
if !ok {
|
||||
chargeLog.Error("取消用户充值记录失败。")
|
||||
return result.NotAccept("取消用户充值记录失败。")
|
||||
} else {
|
||||
return result.Success("取消用户充值记录成功。")
|
||||
}
|
||||
result.Updated("指定用户服务延期记录状态已经更新。")
|
||||
}
|
||||
|
||||
@@ -1,215 +0,0 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"electricity_bill_calc/excel"
|
||||
"electricity_bill_calc/global"
|
||||
"electricity_bill_calc/response"
|
||||
"electricity_bill_calc/security"
|
||||
"electricity_bill_calc/service"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/samber/lo"
|
||||
"github.com/shopspring/decimal"
|
||||
)
|
||||
|
||||
func InitializeEndUserController(router *gin.Engine) {
|
||||
router.GET("/report/:rid/submeter", security.EnterpriseAuthorize, fetchEndUserInReport)
|
||||
router.GET("/report/:rid/meter/template", security.EnterpriseAuthorize, downloadEndUserRegisterTemplate)
|
||||
router.POST("/report/:rid/meter/batch", security.EnterpriseAuthorize, uploadEndUserRegisterTemplate)
|
||||
router.PUT("/report/:rid/submeter/:pid/:mid", security.EnterpriseAuthorize, modifyEndUserRegisterRecord)
|
||||
}
|
||||
|
||||
func fetchEndUserInReport(c *gin.Context) {
|
||||
result := response.NewResult(c)
|
||||
requestReportId := c.Param("rid")
|
||||
if !ensureReportBelongs(c, result, requestReportId) {
|
||||
return
|
||||
}
|
||||
keyword := c.DefaultQuery("keyword", "")
|
||||
requestPage, err := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||
if err != nil {
|
||||
result.NotAccept("查询参数[page]格式不正确。")
|
||||
return
|
||||
}
|
||||
endUsers, totalItem, err := service.EndUserService.SearchEndUserRecord(requestReportId, keyword, requestPage)
|
||||
if err != nil {
|
||||
result.NotFound(err.Error())
|
||||
return
|
||||
}
|
||||
result.Json(
|
||||
http.StatusOK,
|
||||
"已获取到符合条件的终端用户集合",
|
||||
response.NewPagedResponse(requestPage, totalItem).ToMap(),
|
||||
gin.H{"meters": endUsers},
|
||||
)
|
||||
}
|
||||
|
||||
func downloadEndUserRegisterTemplate(c *gin.Context) {
|
||||
result := response.NewResult(c)
|
||||
requestReportId := c.Param("rid")
|
||||
if !ensureReportBelongs(c, result, requestReportId) {
|
||||
return
|
||||
}
|
||||
users, err := service.EndUserService.AllEndUserRecord(requestReportId)
|
||||
if err != nil {
|
||||
result.NotFound(err.Error())
|
||||
return
|
||||
}
|
||||
meterType, err := service.ReportService.RetreiveParkEndUserMeterType(requestReportId)
|
||||
if err != nil {
|
||||
result.Error(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if meterType == -1 {
|
||||
result.NotFound("未能确定用户表计类型。")
|
||||
return
|
||||
}
|
||||
|
||||
c.Status(http.StatusOK)
|
||||
c.Header("Content-Type", "application/octet-stream")
|
||||
c.Header("Content-Transfer-Encoding", "binary")
|
||||
c.Header("Content-Disposition", "attachment; filename=抄表记录.xlsx")
|
||||
|
||||
gen := lo.Ternary[excel.ExcelTemplateGenerator](
|
||||
meterType == 0,
|
||||
excel.NewMeterNonPVExcelTemplateGenerator(),
|
||||
excel.NewMeterPVExcelTemplateGenerator(),
|
||||
)
|
||||
defer gen.Close()
|
||||
gen.WriteMeterData(users)
|
||||
gen.WriteTo(c.Writer)
|
||||
}
|
||||
|
||||
func uploadEndUserRegisterTemplate(c *gin.Context) {
|
||||
result := response.NewResult(c)
|
||||
requestReportId := c.Param("rid")
|
||||
if !ensureReportBelongs(c, result, requestReportId) {
|
||||
return
|
||||
}
|
||||
meterType, err := service.ReportService.RetreiveParkEndUserMeterType(requestReportId)
|
||||
if err != nil {
|
||||
result.Error(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
if meterType == -1 {
|
||||
result.NotFound("未能确定用户表计类型。")
|
||||
return
|
||||
}
|
||||
|
||||
uploadedFile, err := c.FormFile("data")
|
||||
if err != nil {
|
||||
result.NotAccept("没有接收到上传的档案文件。")
|
||||
return
|
||||
}
|
||||
archiveFile, err := uploadedFile.Open()
|
||||
if err != nil {
|
||||
result.Error(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
if meterType == 0 {
|
||||
errs := service.EndUserService.BatchImportNonPVRegister(requestReportId, archiveFile)
|
||||
if errs.Len() > 0 {
|
||||
result.Json(http.StatusInternalServerError, "上传抄表文件存在解析错误", gin.H{"errors": errs.Errs})
|
||||
return
|
||||
}
|
||||
} else {
|
||||
errs := service.EndUserService.BatchImportPVRegister(requestReportId, archiveFile)
|
||||
if errs.Len() > 0 {
|
||||
result.Json(http.StatusInternalServerError, "上传抄表文件存在解析错误", gin.H{"errors": errs.Errs})
|
||||
return
|
||||
}
|
||||
}
|
||||
result.Json(http.StatusOK, "已经成功完成抄表记录的导入。", gin.H{"errors": make([]error, 0)})
|
||||
}
|
||||
|
||||
type ModifyEndUserRegisterFormData struct {
|
||||
CurrentPeriodOverall decimal.NullDecimal `json:"currentPeriodOverall" form:"currentPeriodOverall"`
|
||||
CurrentPeriodCritical decimal.NullDecimal `json:"currentPeriodCritical" form:"currentPeriodCritical"`
|
||||
CurrentPeriodPeak decimal.NullDecimal `json:"currentPeriodPeak" form:"currentPeriodPeak"`
|
||||
CurrentPeriodValley decimal.NullDecimal `json:"currentPeriodValley" form:"currentPeriodValley"`
|
||||
AdjustOverall decimal.NullDecimal `json:"adjustOverall" form:"adjustOverall"`
|
||||
AdjustCritical decimal.NullDecimal `json:"adjustCritical" form:"adjustCritical"`
|
||||
AdjustPeak decimal.NullDecimal `json:"adjustPeak" form:"adjustPeak"`
|
||||
AdjustValley decimal.NullDecimal `json:"adjustValley" form:"adjustValley"`
|
||||
}
|
||||
|
||||
func modifyEndUserRegisterRecord(c *gin.Context) {
|
||||
result := response.NewResult(c)
|
||||
requestReportId := c.Param("rid")
|
||||
if !ensureReportBelongs(c, result, requestReportId) {
|
||||
return
|
||||
}
|
||||
meterType, err := service.ReportService.RetreiveParkEndUserMeterType(requestReportId)
|
||||
if err != nil {
|
||||
result.Error(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
if meterType == -1 {
|
||||
result.NotFound("未能确定用户表计类型。")
|
||||
return
|
||||
}
|
||||
requestParkId := c.Param("pid")
|
||||
requestMeterId := c.Param("mid")
|
||||
formData := new(ModifyEndUserRegisterFormData)
|
||||
c.BindJSON(formData)
|
||||
meter, err := service.EndUserService.FetchSpecificEndUserRecord(requestReportId, requestParkId, requestMeterId)
|
||||
if err != nil {
|
||||
result.NotFound(err.Error())
|
||||
return
|
||||
}
|
||||
if formData.CurrentPeriodOverall.Valid {
|
||||
meter.CurrentPeriodOverall = formData.CurrentPeriodOverall.Decimal
|
||||
}
|
||||
if formData.CurrentPeriodCritical.Valid {
|
||||
meter.CurrentPeriodCritical = formData.CurrentPeriodCritical.Decimal
|
||||
}
|
||||
if formData.CurrentPeriodPeak.Valid {
|
||||
meter.CurrentPeriodPeak = formData.CurrentPeriodPeak.Decimal
|
||||
}
|
||||
if formData.CurrentPeriodValley.Valid {
|
||||
meter.CurrentPeriodValley = formData.CurrentPeriodValley.Decimal
|
||||
}
|
||||
if formData.AdjustOverall.Valid {
|
||||
meter.AdjustOverall = formData.AdjustOverall.Decimal
|
||||
}
|
||||
if formData.AdjustCritical.Valid {
|
||||
meter.AdjustCritical = formData.AdjustCritical.Decimal
|
||||
}
|
||||
if formData.AdjustPeak.Valid {
|
||||
meter.AdjustPeak = formData.AdjustPeak.Decimal
|
||||
}
|
||||
if formData.AdjustValley.Valid {
|
||||
meter.AdjustValley = formData.AdjustValley.Decimal
|
||||
}
|
||||
valid, err := meter.Validate()
|
||||
if err != nil {
|
||||
result.Error(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
if !valid {
|
||||
result.NotAccept("抄表数据合法性验证失败。")
|
||||
return
|
||||
}
|
||||
tx := global.DBConn.NewSession()
|
||||
if err = tx.Begin(); err != nil {
|
||||
result.Error(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
defer tx.Close()
|
||||
err = service.EndUserService.UpdateEndUserRegisterRecord(tx, *meter)
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
result.Error(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
err = tx.Commit()
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
result.Error(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
result.Success("指定终端用户抄表记录已经更新。")
|
||||
}
|
||||
24
controller/foundation.go
Normal file
24
controller/foundation.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"electricity_bill_calc/config"
|
||||
"electricity_bill_calc/logger"
|
||||
"electricity_bill_calc/response"
|
||||
"electricity_bill_calc/security"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
var foundLog = logger.Named("Handler", "Foundation")
|
||||
|
||||
func InitializeFoundationHandlers(router *fiber.App) {
|
||||
router.Get("/norm/authorized/loss/rate", security.MustAuthenticated, getNormAuthorizedLossRate)
|
||||
}
|
||||
|
||||
func getNormAuthorizedLossRate(c *fiber.Ctx) error {
|
||||
foundLog.Info("获取系统中定义的基准核定线损率")
|
||||
result := response.NewResult(c)
|
||||
BaseLoss := config.BaseLoss
|
||||
return result.Success("已经获取到系统设置的基准核定线损率。",
|
||||
fiber.Map{"normAuthorizedLossRate": BaseLoss},
|
||||
)
|
||||
}
|
||||
132
controller/god_mode.go
Normal file
132
controller/god_mode.go
Normal file
@@ -0,0 +1,132 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"electricity_bill_calc/logger"
|
||||
"electricity_bill_calc/response"
|
||||
"electricity_bill_calc/security"
|
||||
"electricity_bill_calc/service"
|
||||
"errors"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"go.uber.org/zap"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
var GmLog = logger.Named("Handler", "GM")
|
||||
|
||||
func InitializeGmController(router *fiber.App) {
|
||||
router.Delete("/gm/tenement", security.SingularityAuthorize, deleteTenement)
|
||||
router.Delete("/gm/park", security.SingularityAuthorize, deletePark)
|
||||
router.Delete("/gm/report", security.SingularityAuthorize, deleteReports)
|
||||
router.Delete("/gm/tenement/meter", security.SingularityAuthorize, deleteTenementMeterRelations)
|
||||
router.Delete("/gm/enterprise", security.SingularityAuthorize, deleteEnterprise)
|
||||
router.Delete("/gm/meter/pooling", security.SingularityAuthorize, deleteMeterPoolingRelations)
|
||||
router.Delete("gm/meter", security.SingularityAuthorize, deleteMeters)
|
||||
}
|
||||
|
||||
//用于将参数转化为切片
|
||||
func getQueryValues(c *fiber.Ctx, paramName string) []string {
|
||||
values := c.Request().URI().QueryArgs().PeekMulti(paramName)
|
||||
result := make([]string, len(values))
|
||||
for i, v := range values {
|
||||
result[i] = string(v)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func deleteTenement(c *fiber.Ctx) error {
|
||||
park := c.Query("park", "")
|
||||
tenements := getQueryValues(c, "tenements")
|
||||
result := response.NewResult(c)
|
||||
GmLog.Info("[天神模式]删除指定园区中的商户", zap.String("park", park), zap.Strings("tenements", tenements))
|
||||
|
||||
err := service.GMService.DeleteTenements(park, tenements)
|
||||
if err != nil {
|
||||
GmLog.Error("[天神模式]删除指定园区中的商户失败", zap.Error(err))
|
||||
return result.Error(500, err.Error())
|
||||
}
|
||||
|
||||
return result.Success("指定商户已经删除。")
|
||||
}
|
||||
|
||||
func deletePark(c *fiber.Ctx) error {
|
||||
parks := getQueryValues(c, "parks")
|
||||
result := response.NewResult(c)
|
||||
GmLog.Info("[天神模式]删除指定园区", zap.Strings("parks", parks))
|
||||
|
||||
if len(parks) < 0 {
|
||||
GmLog.Info("[天神模式]用户未指派园区参数或者未指定需要删除的园区。")
|
||||
return result.Error(http.StatusBadRequest, error.Error(errors.New("必须至少指定一个需要删除的园区!")))
|
||||
}
|
||||
|
||||
err := service.GMService.DeleteParks(parks)
|
||||
if err != nil {
|
||||
GmLog.Error("[天神模式]删除指定园区失败", zap.Error(err))
|
||||
return result.Error(500, err.Error())
|
||||
}
|
||||
return result.Success("指定园区已经删除。")
|
||||
}
|
||||
|
||||
func deleteReports(c *fiber.Ctx) error {
|
||||
pid := c.Query("park")
|
||||
reports := getQueryValues(c, "reports")
|
||||
result := response.NewResult(c)
|
||||
GmLog.Info("[天神模式]删除符合条件的报表。", zap.Strings("reports", reports))
|
||||
|
||||
err := service.GMService.DeleteReports(pid, reports)
|
||||
if err != nil {
|
||||
GmLog.Error("[天神模式]删除指定园区中的报表失败。", zap.Error(err))
|
||||
return result.Error(500, err.Error())
|
||||
}
|
||||
return result.Success("指定报表已经删除。")
|
||||
}
|
||||
|
||||
func deleteEnterprise(c *fiber.Ctx) error {
|
||||
uid := c.Query("uid")
|
||||
result := response.NewResult(c)
|
||||
GmLog.Info("[天神模式]删除指定企业用户", zap.String("uid", uid))
|
||||
|
||||
err := service.GMService.DeleteEnterprises(uid)
|
||||
if err != nil {
|
||||
GmLog.Error("[天神模式]删除指定企业用户失败", zap.Error(err))
|
||||
return result.Error(500, "删除指定企业用户失败。")
|
||||
}
|
||||
return result.Success("指定企业用户已经删除。")
|
||||
}
|
||||
|
||||
func deleteTenementMeterRelations(c *fiber.Ctx) error {
|
||||
result := response.NewResult(c)
|
||||
parkId := c.Query("park")
|
||||
tId := getQueryValues(c, "tenements")
|
||||
metersId := getQueryValues(c, "meters")
|
||||
GmLog.Info("删除指定园区中的商户与表计的关联关系", zap.String("park id", parkId))
|
||||
if err := service.GMService.DeleteTenementMeterRelations(parkId, tId, metersId); err != nil {
|
||||
meterLog.Error("无法删除指定园区中的商户与表计的关联关系", zap.Error(err))
|
||||
return result.NotAccept(err.Error())
|
||||
}
|
||||
return result.Success("删除成功")
|
||||
}
|
||||
|
||||
func deleteMeterPoolingRelations(c *fiber.Ctx) error {
|
||||
result := response.NewResult(c)
|
||||
parkId := c.Query("park")
|
||||
mId := getQueryValues(c, "meters")
|
||||
GmLog.Info("[天神模式]删除指定园区中的表计公摊关系", zap.String("park id", parkId))
|
||||
if err := service.GMService.DeleteMeterPooling(parkId, mId); err != nil {
|
||||
meterLog.Error("[天神模式]删除指定园区中的表计公摊关系失败", zap.Error(err))
|
||||
return result.Error(500, "删除指定园区中的表计公摊关系失败。")
|
||||
}
|
||||
return result.Success("指定表计公摊关系已经删除。")
|
||||
|
||||
}
|
||||
|
||||
func deleteMeters(c *fiber.Ctx) error {
|
||||
result := response.NewResult(c)
|
||||
parkId := c.Query("park")
|
||||
mId := getQueryValues(c, "meters")
|
||||
GmLog.Info("[天神模式]删除指定园区中的表计", zap.String("park id", parkId))
|
||||
if err := service.GMService.DeleteMeters(parkId, mId); err != nil {
|
||||
meterLog.Error("[天神模式]删除指定园区中的表计失败", zap.Error(err))
|
||||
return result.Error(500, "删除指定园区中的表计失败。")
|
||||
}
|
||||
return result.Success("指定表计已经删除。")
|
||||
}
|
||||
227
controller/invoice.go
Normal file
227
controller/invoice.go
Normal file
@@ -0,0 +1,227 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"electricity_bill_calc/logger"
|
||||
"electricity_bill_calc/model"
|
||||
"electricity_bill_calc/repository"
|
||||
"electricity_bill_calc/response"
|
||||
"electricity_bill_calc/security"
|
||||
"electricity_bill_calc/service"
|
||||
"electricity_bill_calc/tools"
|
||||
"electricity_bill_calc/types"
|
||||
"electricity_bill_calc/vo"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/jinzhu/copier"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
var invoiceLog = logger.Named("Controller", "Invoice")
|
||||
|
||||
func InitializeInvoiceHandler(router *fiber.App) {
|
||||
router.Get("/invoice", security.MustAuthenticated, listInvoices)
|
||||
router.Post("/invoice", security.EnterpriseAuthorize, createNewInvoiceRecord)
|
||||
router.Post("/invoice/precalculate", security.EnterpriseAuthorize, testCalculateInvoice)
|
||||
router.Get("/invoice/:code", security.EnterpriseAuthorize, getInvoiceDetail)
|
||||
router.Delete("/invoice/:code", security.EnterpriseAuthorize, deleteInvoiceRecord)
|
||||
router.Get("/uninvoiced/tenemennt/:tid/report", security.EnterpriseAuthorize, getUninvoicedTenementReports)
|
||||
}
|
||||
|
||||
// 列出指定园区中的符合条件的发票记录
|
||||
func listInvoices(c *fiber.Ctx) error {
|
||||
invoiceLog.Info("列出指定园区中的符合条件的发票记录")
|
||||
result := response.NewResult(c)
|
||||
session, err := _retreiveSession(c)
|
||||
if err != nil {
|
||||
invoiceLog.Error("列出指定园区中的符合条件的发票记录失败,不能获取到有效的用户会话。", zap.Error(err))
|
||||
return result.Unauthorized("未能获取到有效的用户会话。")
|
||||
}
|
||||
park := tools.EmptyToNil(c.Query("park"))
|
||||
if session.Type == model.USER_TYPE_ENT && park != nil && len(*park) > 0 {
|
||||
pass, err := checkParkBelongs(*park, invoiceLog, c, &result)
|
||||
if err != nil || !pass {
|
||||
return err
|
||||
}
|
||||
}
|
||||
startDate, err := types.ParseDatep(c.Query("start_date"))
|
||||
if err != nil {
|
||||
invoiceLog.Error("列出指定园区中的符合条件的发票记录失败,开始日期参数解析错误。", zap.Error(err))
|
||||
return result.BadRequest("开始日期参数解析错误。")
|
||||
}
|
||||
endDate, err := types.ParseDatep(c.Query("end_date"))
|
||||
if err != nil {
|
||||
invoiceLog.Error("列出指定园区中的符合条件的发票记录失败,结束日期参数解析错误。", zap.Error(err))
|
||||
return result.BadRequest("结束日期参数解析错误。")
|
||||
}
|
||||
keyword := tools.EmptyToNil(c.Query("keyword"))
|
||||
page := c.QueryInt("page", 1)
|
||||
invoices, total, err := repository.InvoiceRepository.ListInvoice(park, startDate, endDate, keyword, uint(page))
|
||||
if err != nil {
|
||||
invoiceLog.Error("列出指定园区中的符合条件的发票记录失败,检索符合条件的发票记录出现错误。", zap.Error(err))
|
||||
return result.Error(fiber.StatusInternalServerError, "检索符合条件的发票记录出现错误。")
|
||||
}
|
||||
invoiceResponse := make([]*vo.InvoiceResponse, 0)
|
||||
copier.Copy(&invoiceResponse, &invoices)
|
||||
return result.Success(
|
||||
"已经获取到符合条件的发票列表。",
|
||||
response.NewPagedResponse(page, total).ToMap(),
|
||||
fiber.Map{
|
||||
"invoices": invoiceResponse,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// 获取指定发票的详细信息
|
||||
func getInvoiceDetail(c *fiber.Ctx) error {
|
||||
result := response.NewResult(c)
|
||||
invoiceNo := tools.EmptyToNil(c.Params("code"))
|
||||
invoiceLog.Info("获取指定发票的详细信息", zap.Stringp("InvoiceNo", invoiceNo))
|
||||
if invoiceNo == nil {
|
||||
invoiceLog.Error("获取指定发票的详细信息失败,未指定发票编号。")
|
||||
return result.BadRequest("未指定发票编号。")
|
||||
}
|
||||
session, err := _retreiveSession(c)
|
||||
if err != nil {
|
||||
invoiceLog.Error("获取指定发票的详细信息失败,不能获取到有效的用户会话。", zap.Error(err))
|
||||
return result.Unauthorized("未能获取到有效的用户会话。")
|
||||
}
|
||||
pass, err := repository.InvoiceRepository.IsBelongsTo(*invoiceNo, session.Uid)
|
||||
if err != nil {
|
||||
invoiceLog.Error("获取指定发票的详细信息失败,检查发票所属权时出现错误。", zap.Error(err))
|
||||
return result.Error(fiber.StatusInternalServerError, "检查发票所属权时出现错误。")
|
||||
}
|
||||
if !pass {
|
||||
invoiceLog.Error("获取指定发票的详细信息失败,发票不属于当前用户。")
|
||||
return result.Forbidden("不能访问不属于自己的发票。")
|
||||
}
|
||||
invoice, err := repository.InvoiceRepository.GetInvoiceDetail(*invoiceNo)
|
||||
if err != nil {
|
||||
invoiceLog.Error("获取指定发票的详细信息失败,检索发票信息时出现错误。", zap.Error(err))
|
||||
return result.Error(fiber.StatusInternalServerError, "检索发票信息时出现错误。")
|
||||
}
|
||||
if invoice == nil {
|
||||
invoiceLog.Error("获取指定发票的详细信息失败,指定发票不存在。")
|
||||
return result.NotFound("指定发票不存在。")
|
||||
}
|
||||
var invoiceResponse vo.ExtendedInvoiceResponse
|
||||
copier.Copy(&invoiceResponse, &invoice)
|
||||
return result.Success(
|
||||
"已经获取到指定发票的详细信息。",
|
||||
fiber.Map{
|
||||
"invoice": invoiceResponse,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// 获取指定商户下所有尚未开票的核算项目
|
||||
func getUninvoicedTenementReports(c *fiber.Ctx) error {
|
||||
result := response.NewResult(c)
|
||||
tenement := tools.EmptyToNil(c.Params("tid"))
|
||||
invoiceLog.Info("获取指定商户下所有尚未开票的核算项目", zap.Stringp("Tenement", tenement))
|
||||
if tenement == nil {
|
||||
invoiceLog.Error("获取指定商户下所有尚未开票的核算项目失败,未指定商户。")
|
||||
return result.BadRequest("未指定商户。")
|
||||
}
|
||||
session, err := _retreiveSession(c)
|
||||
if err != nil {
|
||||
invoiceLog.Error("获取指定商户下所有尚未开票的核算项目失败,不能获取到有效的用户会话。", zap.Error(err))
|
||||
return result.Unauthorized("未能获取到有效的用户会话。")
|
||||
}
|
||||
pass, err := repository.TenementRepository.IsTenementBelongs(*tenement, session.Uid)
|
||||
if err != nil {
|
||||
invoiceLog.Error("获取指定商户下所有尚未开票的核算项目失败,检查商户所属权时出现错误。", zap.Error(err))
|
||||
return result.Error(fiber.StatusInternalServerError, "检查商户所属权时出现错误。")
|
||||
}
|
||||
if !pass {
|
||||
invoiceLog.Error("获取指定商户下所有尚未开票的核算项目失败,商户不属于当前用户。")
|
||||
return result.Forbidden("不能访问不属于自己的商户。")
|
||||
}
|
||||
reports, err := repository.InvoiceRepository.ListUninvoicedTenementCharges(*tenement)
|
||||
if err != nil {
|
||||
invoiceLog.Error("获取指定商户下所有尚未开票的核算项目失败,检索核算项目时出现错误。", zap.Error(err))
|
||||
return result.Error(fiber.StatusInternalServerError, "检索核算项目时出现错误。")
|
||||
}
|
||||
return result.Success(
|
||||
"已经获取到指定商户下所有尚未开票的核算项目。",
|
||||
fiber.Map{
|
||||
"records": reports,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// 试计算指定发票的票面信息
|
||||
func testCalculateInvoice(c *fiber.Ctx) error {
|
||||
result := response.NewResult(c)
|
||||
var form vo.InvoiceCreationForm
|
||||
if err := c.BodyParser(&form); err != nil {
|
||||
invoiceLog.Error("试计算指定发票的票面信息失败,请求表单数据解析错误。", zap.Error(err))
|
||||
return result.BadRequest("请求表单数据解析错误。")
|
||||
}
|
||||
invoiceLog.Info("试计算指定发票的票面信息", zap.String("Park", form.Park), zap.String("Tenement", form.Tenement))
|
||||
if pass, err := checkParkBelongs(form.Park, invoiceLog, c, &result); err != nil || !pass {
|
||||
return err
|
||||
}
|
||||
total, cargos, err := service.InvoiceService.TestCalculateInvoice(form.Park, form.Tenement, form.TaxMethod, form.TaxRate, form.Covers)
|
||||
if err != nil {
|
||||
invoiceLog.Error("试计算指定发票的票面信息失败,试计算发票时出现错误。", zap.Error(err))
|
||||
return result.Error(fiber.StatusInternalServerError, "试计算发票时出现错误。")
|
||||
}
|
||||
return result.Success(
|
||||
"已经计算出指定发票的票面信息。",
|
||||
fiber.Map{
|
||||
"total": total,
|
||||
"cargos": cargos,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// 创建一个新的发票记录
|
||||
func createNewInvoiceRecord(c *fiber.Ctx) error {
|
||||
result := response.NewResult(c)
|
||||
var form vo.ExtendedInvoiceCreationForm
|
||||
if err := c.BodyParser(&form); err != nil {
|
||||
invoiceLog.Error("创建一个新的发票记录失败,请求表单数据解析错误。", zap.Error(err))
|
||||
return result.BadRequest("请求表单数据解析错误。")
|
||||
}
|
||||
invoiceLog.Info("创建一个新的发票记录", zap.String("Park", form.Park), zap.String("Tenement", form.Tenement))
|
||||
if pass, err := checkParkBelongs(form.Park, invoiceLog, c, &result); err != nil || !pass {
|
||||
return err
|
||||
}
|
||||
err := service.InvoiceService.SaveInvoice(form.Park, form.Tenement, form.InvoiceNo, form.InvoiceType, form.TaxMethod, form.TaxRate, form.Covers)
|
||||
if err != nil {
|
||||
invoiceLog.Error("创建一个新的发票记录失败,保存发票时出现错误。", zap.Error(err))
|
||||
return result.Error(fiber.StatusInternalServerError, "保存发票时出现错误。")
|
||||
}
|
||||
return result.Created("已经创建了一个新的发票记录。")
|
||||
}
|
||||
|
||||
// 删除指定的发票记录
|
||||
func deleteInvoiceRecord(c *fiber.Ctx) error {
|
||||
result := response.NewResult(c)
|
||||
invoiceNo := tools.EmptyToNil(c.Params("code"))
|
||||
invoiceLog.Info("删除指定的发票记录", zap.Stringp("InvoiceNo", invoiceNo))
|
||||
if invoiceNo == nil {
|
||||
invoiceLog.Error("删除指定的发票记录失败,未指定发票编号。")
|
||||
return result.BadRequest("未指定发票编号。")
|
||||
}
|
||||
session, err := _retreiveSession(c)
|
||||
if err != nil {
|
||||
invoiceLog.Error("删除指定的发票记录失败,不能获取到有效的用户会话。", zap.Error(err))
|
||||
return result.Unauthorized("未能获取到有效的用户会话。")
|
||||
}
|
||||
pass, err := repository.InvoiceRepository.IsBelongsTo(*invoiceNo, session.Uid)
|
||||
if err != nil {
|
||||
invoiceLog.Error("删除指定的发票记录失败,检查发票所属权时出现错误。", zap.Error(err))
|
||||
return result.Error(fiber.StatusInternalServerError, "检查发票所属权时出现错误。")
|
||||
}
|
||||
if !pass {
|
||||
invoiceLog.Error("删除指定的发票记录失败,发票不属于当前用户。")
|
||||
return result.Forbidden("不能删除不属于自己的发票。")
|
||||
}
|
||||
err = service.InvoiceService.DeleteInvoice(*invoiceNo)
|
||||
if err != nil {
|
||||
invoiceLog.Error("删除指定的发票记录失败,删除发票时出现错误。", zap.Error(err))
|
||||
return result.Error(fiber.StatusInternalServerError, "删除发票时出现错误。")
|
||||
}
|
||||
return result.Success("已经删除了指定的发票记录。")
|
||||
}
|
||||
@@ -1,153 +0,0 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"electricity_bill_calc/model"
|
||||
"electricity_bill_calc/response"
|
||||
"electricity_bill_calc/security"
|
||||
"electricity_bill_calc/service"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/shopspring/decimal"
|
||||
)
|
||||
|
||||
func InitializeMaintenanceFeeController(router *gin.Engine) {
|
||||
router.GET("/maintenance/fee", security.EnterpriseAuthorize, listMaintenanceFees)
|
||||
router.POST("/maintenance/fee", security.EnterpriseAuthorize, createMaintenanceFeeRecord)
|
||||
router.PUT("/maintenance/fee/:mid", security.EnterpriseAuthorize, modifyMaintenanceFeeRecord)
|
||||
router.PUT("/maintenance/fee/:mid/enabled", security.EnterpriseAuthorize, changeMaintenanceFeeState)
|
||||
router.DELETE("/maintenance/fee/:mid", security.EnterpriseAuthorize, deleteMaintenanceFee)
|
||||
}
|
||||
|
||||
func ensureMaintenanceFeeBelongs(c *gin.Context, result *response.Result, requestMaintenanceFeeId string) bool {
|
||||
userSession, err := _retreiveSession(c)
|
||||
if err != nil {
|
||||
result.Unauthorized(err.Error())
|
||||
return false
|
||||
}
|
||||
sure, err := service.MaintenanceFeeService.EnsureFeeBelongs(userSession.Uid, requestMaintenanceFeeId)
|
||||
if err != nil {
|
||||
result.Error(http.StatusInternalServerError, err.Error())
|
||||
return false
|
||||
}
|
||||
if !sure {
|
||||
result.Unauthorized("所操作维护费记录不属于当前用户。")
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func listMaintenanceFees(c *gin.Context) {
|
||||
result := response.NewResult(c)
|
||||
userSession, err := _retreiveSession(c)
|
||||
if err != nil {
|
||||
result.Unauthorized(err.Error())
|
||||
return
|
||||
}
|
||||
requestPark := c.DefaultQuery("park", "")
|
||||
if len(requestPark) > 0 {
|
||||
if !ensureParkBelongs(c, result, requestPark) {
|
||||
return
|
||||
}
|
||||
fees, err := service.MaintenanceFeeService.ListMaintenanceFees([]string{requestPark})
|
||||
if err != nil {
|
||||
result.Error(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
result.Json(http.StatusOK, "已获取指定园区下的维护费记录", gin.H{"fees": fees})
|
||||
} else {
|
||||
parkIds, err := service.ParkService.AllParkIds(userSession.Uid)
|
||||
if err != nil {
|
||||
result.Error(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
fees, err := service.MaintenanceFeeService.ListMaintenanceFees(parkIds)
|
||||
if err != nil {
|
||||
result.Error(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
result.Json(http.StatusOK, "已获取指定用户下的所有维护费记录。", gin.H{"fees": fees})
|
||||
}
|
||||
}
|
||||
|
||||
type _FeeCreationFormData struct {
|
||||
ParkId string `json:"parkId" form:"parkId"`
|
||||
Name string `json:"name" form:"name"`
|
||||
Fee decimal.Decimal `json:"fee" form:"fee"`
|
||||
Memo *string `json:"memo" form:"memo"`
|
||||
}
|
||||
|
||||
func createMaintenanceFeeRecord(c *gin.Context) {
|
||||
result := response.NewResult(c)
|
||||
formData := new(_FeeCreationFormData)
|
||||
c.BindJSON(formData)
|
||||
if !ensureParkBelongs(c, result, formData.ParkId) {
|
||||
return
|
||||
}
|
||||
newMaintenanceFee := &model.MaintenanceFee{}
|
||||
copier.Copy(newMaintenanceFee, formData)
|
||||
err := service.MaintenanceFeeService.CreateMaintenanceFeeRecord(*newMaintenanceFee)
|
||||
if err != nil {
|
||||
result.Error(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
result.Created("新维护费记录已经创建。")
|
||||
}
|
||||
|
||||
type _FeeModificationFormData struct {
|
||||
Fee decimal.Decimal `json:"fee" form:"fee"`
|
||||
Memo *string `json:"memo" form:"memo"`
|
||||
}
|
||||
|
||||
func modifyMaintenanceFeeRecord(c *gin.Context) {
|
||||
result := response.NewResult(c)
|
||||
requestFee := c.Param("mid")
|
||||
formData := new(_FeeModificationFormData)
|
||||
c.BindJSON(formData)
|
||||
if !ensureMaintenanceFeeBelongs(c, result, requestFee) {
|
||||
return
|
||||
}
|
||||
newFeeState := new(model.MaintenanceFee)
|
||||
copier.Copy(newFeeState, formData)
|
||||
err := service.MaintenanceFeeService.ModifyMaintenanceFee(*newFeeState)
|
||||
if err != nil {
|
||||
result.Error(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
result.Updated("指定维护费条目已更新。")
|
||||
}
|
||||
|
||||
type _FeeStateFormData struct {
|
||||
Enabled bool `json:"enabled" form:"enabled"`
|
||||
}
|
||||
|
||||
func changeMaintenanceFeeState(c *gin.Context) {
|
||||
result := response.NewResult(c)
|
||||
requestFee := c.Param("mid")
|
||||
formData := new(_FeeStateFormData)
|
||||
c.BindJSON(formData)
|
||||
if !ensureMaintenanceFeeBelongs(c, result, requestFee) {
|
||||
return
|
||||
}
|
||||
err := service.MaintenanceFeeService.ChangeMaintenanceFeeState(requestFee, formData.Enabled)
|
||||
if err != nil {
|
||||
result.Error(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
result.Updated("指定维护费条目状态已更新。")
|
||||
}
|
||||
|
||||
func deleteMaintenanceFee(c *gin.Context) {
|
||||
result := response.NewResult(c)
|
||||
requestFee := c.Param("mid")
|
||||
if !ensureMaintenanceFeeBelongs(c, result, requestFee) {
|
||||
return
|
||||
}
|
||||
err := service.MaintenanceFeeService.DeleteMaintenanceFee(requestFee)
|
||||
if err != nil {
|
||||
result.Error(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
result.Deleted("指定维护费条目已删除。")
|
||||
}
|
||||
502
controller/meter.go
Normal file
502
controller/meter.go
Normal file
@@ -0,0 +1,502 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"electricity_bill_calc/excel"
|
||||
"electricity_bill_calc/logger"
|
||||
"electricity_bill_calc/model"
|
||||
"electricity_bill_calc/repository"
|
||||
"electricity_bill_calc/response"
|
||||
"electricity_bill_calc/security"
|
||||
"electricity_bill_calc/service"
|
||||
"electricity_bill_calc/tools"
|
||||
"electricity_bill_calc/types"
|
||||
"electricity_bill_calc/vo"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/samber/lo"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
var meterLog = logger.Named("Handler", "Meter")
|
||||
|
||||
func InitializeMeterHandlers(router *fiber.App) {
|
||||
router.Get("/meter/choice", security.EnterpriseAuthorize, listUnboundMeters)
|
||||
router.Get("/meter/choice/tenement", security.EnterpriseAuthorize, listUnboundTenementMeters)
|
||||
router.Get("/meter/:pid", security.EnterpriseAuthorize, searchMetersWithinPark)
|
||||
router.Post("/meter/:pid", security.EnterpriseAuthorize, createNewMeterManually)
|
||||
router.Get("/meter/:pid/template", security.EnterpriseAuthorize, downloadMeterArchiveTemplate)
|
||||
router.Post("/meter/:pid/batch", security.EnterpriseAuthorize, uploadMeterArchive)
|
||||
router.Get("/meter/:pid/pooled", security.EnterpriseAuthorize, listPooledMeters)
|
||||
router.Get("/meter/:pid/:code", security.EnterpriseAuthorize, retrieveSpecificMeterDetail)
|
||||
router.Put("/meter/:pid/:code", security.EnterpriseAuthorize, updateMeterManually)
|
||||
router.Patch("/meter/:pid/:code", security.EnterpriseAuthorize, replaceMeter)
|
||||
router.Get("/meter/:pid/:code/binding", security.EnterpriseAuthorize, listAssociatedMeters)
|
||||
router.Post("/meter/:pid/:code/binding", security.EnterpriseAuthorize, bindAssociatedMeters)
|
||||
router.Delete("/meter/:pid/:code/binding/:slave", security.EnterpriseAuthorize, unbindAssociatedMeters)
|
||||
router.Get("/reading/:pid", security.EnterpriseAuthorize, queryMeterReadings)
|
||||
router.Put("/reading/:pid/:code/:reading", security.EnterpriseAuthorize, updateMeterReading)
|
||||
router.Get("/reading/:pid/template", security.EnterpriseAuthorize, downloadMeterReadingsTemplate)
|
||||
router.Post("/reading/:pid/batch", security.EnterpriseAuthorize, uploadMeterReadings)
|
||||
router.Post("/reading/:pid/:code", security.EnterpriseAuthorize, recordMeterReading)
|
||||
}
|
||||
|
||||
// 查询指定园区下的表计信息
|
||||
func searchMetersWithinPark(c *fiber.Ctx) error {
|
||||
parkId := c.Params("pid")
|
||||
meterLog.Info("查询指定园区下的表计信息", zap.String("park id", parkId))
|
||||
result := response.NewResult(c)
|
||||
if pass, err := checkParkBelongs(parkId, meterLog, c, &result); !pass {
|
||||
return err
|
||||
}
|
||||
keyword := c.Query("keyword")
|
||||
page := c.QueryInt("page", 1)
|
||||
meters, total, err := repository.MeterRepository.MetersIn(parkId, uint(page), &keyword)
|
||||
if err != nil {
|
||||
meterLog.Error("无法查询指定园区下的表计信息,无法获取表计列表", zap.Error(err))
|
||||
return result.Error(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
return result.Success(
|
||||
"已经取得符合条件的0.4kV表计列表。",
|
||||
response.NewPagedResponse(page, total).ToMap(),
|
||||
fiber.Map{"meters": meters},
|
||||
)
|
||||
}
|
||||
|
||||
// 查询指定园区中指定表计的详细信息
|
||||
func retrieveSpecificMeterDetail(c *fiber.Ctx) error {
|
||||
parkId := c.Params("pid")
|
||||
meterId := c.Params("code")
|
||||
meterLog.Info("查询指定园区中指定表计的详细信息", zap.String("park id", parkId), zap.String("meter id", meterId))
|
||||
result := response.NewResult(c)
|
||||
if pass, err := checkParkBelongs(parkId, meterLog, c, &result); !pass {
|
||||
return err
|
||||
}
|
||||
meter, err := repository.MeterRepository.FetchMeterDetail(parkId, meterId)
|
||||
if err != nil {
|
||||
meterLog.Error("无法查询指定园区中指定表计的详细信息,无法获取表计信息", zap.Error(err))
|
||||
return result.Error(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
if meter == nil {
|
||||
meterLog.Warn("无法查询指定园区中指定表计的详细信息,表计不存在")
|
||||
return result.NotFound("指定的表计不存在。")
|
||||
}
|
||||
return result.Success("指定表计信息已经找到。", fiber.Map{"meter": meter})
|
||||
}
|
||||
|
||||
// 手动添加一条0.4kV表计记录
|
||||
func createNewMeterManually(c *fiber.Ctx) error {
|
||||
parkId := c.Params("pid")
|
||||
meterLog.Info("手动添加一条0.4kV表计记录", zap.String("park id", parkId))
|
||||
result := response.NewResult(c)
|
||||
if pass, err := checkParkBelongs(parkId, meterLog, c, &result); !pass {
|
||||
return err
|
||||
}
|
||||
var creationForm vo.MeterCreationForm
|
||||
if err := c.BodyParser(&creationForm); err != nil {
|
||||
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())
|
||||
}
|
||||
return result.Created("新0.4kV表计已经添加完成。")
|
||||
}
|
||||
|
||||
// 手动更新一条新的0.4kV表计记录
|
||||
func updateMeterManually(c *fiber.Ctx) error {
|
||||
parkId := c.Params("pid")
|
||||
meterId := c.Params("code")
|
||||
meterLog.Info("手动更新一条新的0.4kV表计记录", zap.String("park id", parkId), zap.String("meter id", meterId))
|
||||
result := response.NewResult(c)
|
||||
if pass, err := checkParkBelongs(parkId, meterLog, c, &result); !pass {
|
||||
return err
|
||||
}
|
||||
var updateForm vo.MeterModificationForm
|
||||
if err := c.BodyParser(&updateForm); err != nil {
|
||||
meterLog.Error("无法手动更新一条新的0.4kV表计记录,无法解析表计更新表单", zap.Error(err))
|
||||
return result.NotAccept(err.Error())
|
||||
}
|
||||
if err := service.MeterService.UpdateMeterRecord(parkId, meterId, &updateForm); err != nil {
|
||||
meterLog.Error("无法手动更新一条新的0.4kV表计记录,无法更新表计记录", zap.Error(err))
|
||||
return result.NotAccept(err.Error())
|
||||
}
|
||||
return result.Updated("0.4kV表计已经更新完成。")
|
||||
}
|
||||
|
||||
// 下载指定的园区表计登记模板
|
||||
func downloadMeterArchiveTemplate(c *fiber.Ctx) error {
|
||||
parkId := c.Params("pid")
|
||||
meterLog.Info("下载指定的园区表计登记模板", zap.String("park id", parkId))
|
||||
result := response.NewResult(c)
|
||||
if pass, err := checkParkBelongs(parkId, meterLog, c, &result); !pass {
|
||||
return err
|
||||
}
|
||||
parkDetail, err := repository.ParkRepository.RetrieveParkDetail(parkId)
|
||||
if err != nil {
|
||||
meterLog.Error("无法下载指定的园区表计登记模板,无法获取园区信息", zap.Error(err))
|
||||
return result.NotFound(err.Error())
|
||||
}
|
||||
buildings, err := repository.ParkRepository.RetrieveParkBuildings(parkId)
|
||||
if err != nil {
|
||||
meterLog.Error("无法下载指定的园区表计登记模板,无法获取园区建筑列表", zap.Error(err))
|
||||
return result.NotFound(fmt.Sprintf("无法获取园区建筑列表,%s", err.Error()))
|
||||
}
|
||||
if err != nil {
|
||||
meterLog.Error("无法下载指定的园区表计登记模板,无法生成表计登记模板", zap.Error(err))
|
||||
return result.NotFound(fmt.Sprintf("无法生成表计登记模板,%s", err.Error()))
|
||||
}
|
||||
|
||||
templateGenerator := excel.NewMeterArchiveExcelTemplateGenerator()
|
||||
defer templateGenerator.Close()
|
||||
err = templateGenerator.WriteTemplateData(buildings)
|
||||
if err != nil {
|
||||
meterLog.Error("无法下载指定的园区表计登记模板,无法生成表计登记模板", zap.Error(err))
|
||||
return result.Error(fiber.StatusInternalServerError, fmt.Sprintf("无法生成表计登记模板,%s", err.Error()))
|
||||
}
|
||||
|
||||
c.Status(fiber.StatusOK)
|
||||
c.Set(fiber.HeaderContentType, fiber.MIMEOctetStream)
|
||||
c.Set("Content-Transfer-Encoding", "binary")
|
||||
c.Set(fiber.HeaderContentDisposition, fmt.Sprintf("attachment; filename=%s-表计登记模板.xlsx", parkDetail.Name))
|
||||
templateGenerator.WriteTo(c.Response().BodyWriter())
|
||||
return nil
|
||||
}
|
||||
|
||||
// 从Excel文件中导入表计档案
|
||||
func uploadMeterArchive(c *fiber.Ctx) error {
|
||||
result := response.NewResult(c)
|
||||
parkId := c.Params("pid")
|
||||
if pass, err := checkParkBelongs(parkId, meterLog, c, &result); !pass {
|
||||
return err
|
||||
}
|
||||
uploadFile, err := c.FormFile("data")
|
||||
if err != nil {
|
||||
meterLog.Error("无法从Excel文件中导入表计档案,无法获取上传的文件", zap.Error(err))
|
||||
return result.NotAccept(fmt.Sprintf("没有接收到上传的文件,%s", err.Error()))
|
||||
}
|
||||
errs, err := service.MeterService.BatchImportMeters(parkId, uploadFile)
|
||||
if err != nil {
|
||||
meterLog.Error("无法从Excel文件中导入表计档案,无法导入表计档案", zap.Error(err))
|
||||
return result.Json(fiber.StatusNotAcceptable, "上传的表计档案存在错误。", fiber.Map{"errors": errs})
|
||||
}
|
||||
return result.Success("表计档案已经导入完成。", fiber.Map{"errors": errs})
|
||||
}
|
||||
|
||||
// 更换系统中的表计
|
||||
func replaceMeter(c *fiber.Ctx) error {
|
||||
parkId := c.Params("pid")
|
||||
meterId := c.Params("code")
|
||||
meterLog.Info("更换系统中的表计", zap.String("park id", parkId), zap.String("meter id", meterId))
|
||||
result := response.NewResult(c)
|
||||
if pass, err := checkParkBelongs(parkId, meterLog, c, &result); !pass {
|
||||
return err
|
||||
}
|
||||
var replacementForm vo.MeterReplacingForm
|
||||
if err := c.BodyParser(&replacementForm); err != nil {
|
||||
meterLog.Error("无法更换系统中的表计,无法解析表计更换表单", zap.Error(err))
|
||||
return result.NotAccept(err.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 列出指定公摊表计下的所有关联表计
|
||||
func listAssociatedMeters(c *fiber.Ctx) error {
|
||||
result := response.NewResult(c)
|
||||
parkId := c.Params("pid")
|
||||
if pass, err := checkParkBelongs(parkId, meterLog, c, &result); !pass {
|
||||
return err
|
||||
}
|
||||
meterId := c.Params("code")
|
||||
meterLog.Info("列出指定公摊表计下的所有关联表计", zap.String("park id", parkId), zap.String("meter id", meterId))
|
||||
meters, err := service.MeterService.ListPooledMeterRelations(parkId, meterId)
|
||||
if err != nil {
|
||||
meterLog.Error("无法列出指定公摊表计下的所有关联表计,无法获取关联表计列表", zap.Error(err))
|
||||
return result.Error(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
return result.Success("已经取得指定公摊表计下的所有关联表计列表。", fiber.Map{"meters": meters})
|
||||
}
|
||||
|
||||
// 向指定表计绑定关联表计
|
||||
func bindAssociatedMeters(c *fiber.Ctx) error {
|
||||
result := response.NewResult(c)
|
||||
parkId := c.Params("pid")
|
||||
if pass, err := checkParkBelongs(parkId, meterLog, c, &result); !pass {
|
||||
return err
|
||||
}
|
||||
meterId := c.Params("code")
|
||||
meterLog.Info("向指定表计绑定关联表计", zap.String("park id", parkId), zap.String("meter id", meterId))
|
||||
var meters = make([]string, 0)
|
||||
if err := c.BodyParser(&meters); err != nil {
|
||||
meterLog.Error("无法向指定表计绑定关联表计,无法解析关联表计列表", zap.Error(err))
|
||||
return result.NotAccept(err.Error())
|
||||
}
|
||||
ok, err := service.MeterService.BindMeter(parkId, meterId, meters)
|
||||
if err != nil {
|
||||
meterLog.Error("无法向指定表计绑定关联表计,无法绑定关联表计", zap.Error(err))
|
||||
return result.Error(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
if !ok {
|
||||
meterLog.Warn("无法向指定表计绑定关联表计,表计关联失败。")
|
||||
return result.NotAccept("表计关联失败。")
|
||||
}
|
||||
return result.Created("已经向指定表计绑定关联表计。")
|
||||
}
|
||||
|
||||
// 解除指定园区下两个表计之间的关联关系
|
||||
func unbindAssociatedMeters(c *fiber.Ctx) error {
|
||||
result := response.NewResult(c)
|
||||
parkId := c.Params("pid")
|
||||
if pass, err := checkParkBelongs(parkId, meterLog, c, &result); !pass {
|
||||
return err
|
||||
}
|
||||
masterMeter := c.Params("code")
|
||||
slaveMeter := c.Params("slave")
|
||||
if len(masterMeter) == 0 || len(slaveMeter) == 0 {
|
||||
meterLog.Warn("无法解除指定园区下两个表计之间的关联关系,表计编号为空。")
|
||||
return result.NotAccept("存在未给定要操作的表计编号。")
|
||||
}
|
||||
ok, err := service.MeterService.UnbindMeter(parkId, masterMeter, []string{slaveMeter})
|
||||
if err != nil {
|
||||
meterLog.Error("无法解除指定园区下两个表计之间的关联关系,无法解除关联关系", zap.Error(err))
|
||||
return result.Error(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
if !ok {
|
||||
meterLog.Warn("无法解除指定园区下两个表计之间的关联关系,表计关联解除失败。")
|
||||
return result.NotAccept("表计关联解除失败。")
|
||||
}
|
||||
return result.Created("已经解除指定园区下两个表计之间的关联关系。")
|
||||
}
|
||||
|
||||
// 分页列出园区中的公摊表计
|
||||
func listPooledMeters(c *fiber.Ctx) error {
|
||||
result := response.NewResult(c)
|
||||
parkId := c.Params("pid")
|
||||
if pass, err := checkParkBelongs(parkId, meterLog, c, &result); !pass {
|
||||
return err
|
||||
}
|
||||
page := c.QueryInt("page", 1)
|
||||
keyword := c.Query("keyword")
|
||||
meters, total, err := service.MeterService.SearchPooledMetersDetail(parkId, uint(page), &keyword)
|
||||
if err != nil {
|
||||
meterLog.Error("无法分页列出园区中的公摊表计,无法获取公摊表计列表", zap.Error(err))
|
||||
return result.Error(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
return result.Success(
|
||||
"已经取得符合条件的公摊表计列表。",
|
||||
response.NewPagedResponse(page, total).ToMap(),
|
||||
fiber.Map{"meters": meters},
|
||||
)
|
||||
}
|
||||
|
||||
// 列出指定园区中尚未绑定公摊表计的表计
|
||||
func listUnboundMeters(c *fiber.Ctx) error {
|
||||
result := response.NewResult(c)
|
||||
session, err := _retreiveSession(c)
|
||||
if err != nil {
|
||||
meterLog.Error("无法列出指定园区中尚未绑定公摊表计的表计,无法获取当前用户会话", zap.Error(err))
|
||||
return result.Unauthorized(err.Error())
|
||||
}
|
||||
parkId := tools.EmptyToNil(c.Query("park"))
|
||||
if pass, err := checkParkBelongs(*parkId, meterLog, c, &result); !pass {
|
||||
return err
|
||||
}
|
||||
keyword := c.Query("keyword")
|
||||
limit := uint(c.QueryInt("limit", 6))
|
||||
meters, err := repository.MeterRepository.ListUnboundMeters(session.Uid, parkId, &keyword, &limit)
|
||||
if err != nil {
|
||||
meterLog.Error("无法列出指定园区中尚未绑定公摊表计的表计,无法获取表计列表", zap.Error(err))
|
||||
return result.Error(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
var simplifiedMeters = make([]*vo.SimplifiedMeterQueryResponse, 0)
|
||||
copier.Copy(&simplifiedMeters, &meters)
|
||||
return result.Success("已经取得符合条件的表计列表。", fiber.Map{"meters": simplifiedMeters})
|
||||
}
|
||||
|
||||
// 列出指定园区中尚未绑定商户的表计
|
||||
func listUnboundTenementMeters(c *fiber.Ctx) error {
|
||||
result := response.NewResult(c)
|
||||
session, err := _retreiveSession(c)
|
||||
if err != nil {
|
||||
meterLog.Error("无法列出指定园区中尚未绑定商户的表计,无法获取当前用户会话", zap.Error(err))
|
||||
return result.Unauthorized(err.Error())
|
||||
}
|
||||
parkId := c.Query("park")
|
||||
if len(parkId) == 0 {
|
||||
meterLog.Error("无法列出指定园区中尚未绑定商户的表计,未指定要访问的园区ID")
|
||||
return result.NotAccept("未指定要访问的园区。")
|
||||
}
|
||||
if pass, err := checkParkBelongs(parkId, meterLog, c, &result); !pass {
|
||||
return err
|
||||
}
|
||||
keyword := c.Query("keyword")
|
||||
limit := uint(c.QueryInt("limit", 6))
|
||||
meters, err := repository.MeterRepository.ListUnboundTenementMeters(session.Uid, &parkId, &keyword, &limit)
|
||||
if err != nil {
|
||||
meterLog.Error("无法列出指定园区中尚未绑定商户的表计,无法获取表计列表", zap.Error(err))
|
||||
return result.Error(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
var simplifiedMeters = make([]*vo.SimplifiedMeterQueryResponse, 0)
|
||||
copier.Copy(&simplifiedMeters, &meters)
|
||||
return result.Success("已经取得符合条件的表计列表。", fiber.Map{"meters": simplifiedMeters})
|
||||
}
|
||||
|
||||
// 查询指定园区中的表计读数
|
||||
func queryMeterReadings(c *fiber.Ctx) error {
|
||||
result := response.NewResult(c)
|
||||
parkId := c.Params("pid")
|
||||
if pass, err := checkParkBelongs(parkId, meterLog, c, &result); !pass {
|
||||
return err
|
||||
}
|
||||
keyword := tools.EmptyToNil(c.Query("keyword"))
|
||||
page := c.QueryInt("page", 1)
|
||||
building := tools.EmptyToNil(c.Query("building"))
|
||||
start := c.Query("start_date")
|
||||
var startDate *types.Date = nil
|
||||
if len(start) > 0 {
|
||||
if parsedDate, err := types.ParseDate(start); err != nil {
|
||||
meterLog.Error("查询指定园区中的表计读数,无法解析开始日期", zap.Error(err))
|
||||
} else {
|
||||
startDate = &parsedDate
|
||||
}
|
||||
}
|
||||
end := c.Query("end_date")
|
||||
var endDate *types.Date = nil
|
||||
if len(end) > 0 {
|
||||
if parsedDate, err := types.ParseDate(end); err != nil {
|
||||
meterLog.Error("查询指定园区中的表计读数,无法解析结束日期", zap.Error(err))
|
||||
} else {
|
||||
endDate = &parsedDate
|
||||
}
|
||||
}
|
||||
readings, total, err := service.MeterService.SearchMeterReadings(parkId, building, startDate, endDate, uint(page), keyword)
|
||||
if err != nil {
|
||||
meterLog.Error("查询指定园区中的表计读数,无法获取表计读数列表", zap.Error(err))
|
||||
return result.Error(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
convertedReadings := lo.Map(readings, func(element *model.DetailedMeterReading, _ int) vo.MeterReadingDetailResponse {
|
||||
return vo.FromDetailedMeterReading(*element)
|
||||
})
|
||||
return result.Success(
|
||||
"指定园区的表计读数已经列出。",
|
||||
response.NewPagedResponse(page, total).ToMap(),
|
||||
fiber.Map{"records": convertedReadings},
|
||||
)
|
||||
}
|
||||
|
||||
// 记录一条新的表计抄表记录
|
||||
func recordMeterReading(c *fiber.Ctx) error {
|
||||
result := response.NewResult(c)
|
||||
parkId := c.Params("pid")
|
||||
if pass, err := checkParkBelongs(parkId, meterLog, c, &result); !pass {
|
||||
return err
|
||||
}
|
||||
meterCode := c.Params("code")
|
||||
var readingForm vo.MeterReadingForm
|
||||
if err := c.BodyParser(&readingForm); err != nil {
|
||||
meterLog.Error("记录一条新的表计抄表记录,无法解析表计抄表表单", zap.Error(err))
|
||||
return result.NotAccept(fmt.Sprintf("无法解析表计抄表表单,%s", err.Error()))
|
||||
}
|
||||
if !readingForm.Validate() {
|
||||
meterLog.Warn("记录一条新的表计抄表记录,表计读数不能正常配平,尖、峰、谷电量和超过总电量。")
|
||||
return result.NotAccept("表计读数不能正常配平,尖、峰、谷电量和超过总电量。")
|
||||
}
|
||||
err := service.MeterService.RecordReading(parkId, meterCode, &readingForm)
|
||||
if err != nil {
|
||||
meterLog.Error("记录一条新的表计抄表记录,无法记录表计抄表记录", zap.Error(err))
|
||||
return result.Error(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
return result.Created("表计抄表记录已经记录完成。")
|
||||
}
|
||||
|
||||
// 更新指定园区中指定表计的抄表记录
|
||||
func updateMeterReading(c *fiber.Ctx) error {
|
||||
result := response.NewResult(c)
|
||||
parkId := c.Params("pid")
|
||||
if pass, err := checkParkBelongs(parkId, meterLog, c, &result); !pass {
|
||||
return err
|
||||
}
|
||||
meterCode := c.Params("code")
|
||||
readingAtMicro, err := c.ParamsInt("reading")
|
||||
if err != nil {
|
||||
meterLog.Error("更新一条新的表计抄表记录,无法解析抄表时间", zap.Error(err))
|
||||
return result.NotAccept(fmt.Sprintf("无法解析抄表时间,%s", err.Error()))
|
||||
}
|
||||
readingAt := types.FromUnixMicro(int64(readingAtMicro))
|
||||
var readingForm vo.MeterReadingForm
|
||||
if err := c.BodyParser(&readingForm); err != nil {
|
||||
meterLog.Error("更新一条新的表计抄表记录,无法解析表计抄表表单", zap.Error(err))
|
||||
return result.NotAccept(fmt.Sprintf("无法解析表计抄表表单,%s", err.Error()))
|
||||
}
|
||||
ok, err := repository.MeterRepository.UpdateMeterReading(parkId, meterCode, readingAt, &readingForm)
|
||||
if err != nil {
|
||||
meterLog.Error("更新一条新的表计抄表记录,无法更新表计抄表记录", zap.Error(err))
|
||||
return result.Error(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
if !ok {
|
||||
meterLog.Warn("更新一条新的表计抄表记录,表计抄表更新失败。")
|
||||
return result.NotAccept("表计抄表记录未能成功更新,可能指定抄表记录不存在。")
|
||||
}
|
||||
return result.Success("表计抄表记录已经更新完成。")
|
||||
}
|
||||
|
||||
// 下载指定园区的表计抄表模板
|
||||
func downloadMeterReadingsTemplate(c *fiber.Ctx) error {
|
||||
parkId := c.Params("pid")
|
||||
meterLog.Info("下载指定的园区表计抄表模板", zap.String("park id", parkId))
|
||||
result := response.NewResult(c)
|
||||
if pass, err := checkParkBelongs(parkId, meterLog, c, &result); !pass {
|
||||
return err
|
||||
}
|
||||
parkDetail, err := repository.ParkRepository.RetrieveParkDetail(parkId)
|
||||
if err != nil {
|
||||
meterLog.Error("无法下载指定的园区表计登记模板,无法获取园区信息", zap.Error(err))
|
||||
return result.NotFound(err.Error())
|
||||
}
|
||||
meterDocs, err := repository.MeterRepository.ListMeterDocForTemplate(parkId)
|
||||
if err != nil {
|
||||
meterLog.Error("无法下载指定的园区表计抄表模板,无法获取表计档案列表", zap.Error(err))
|
||||
return result.NotFound(fmt.Sprintf("无法获取表计档案列表,%s", err.Error()))
|
||||
}
|
||||
if err != nil {
|
||||
meterLog.Error("无法下载指定的园区表计登记模板,无法生成表计登记模板", zap.Error(err))
|
||||
return result.NotFound(fmt.Sprintf("无法生成表计登记模板,%s", err.Error()))
|
||||
}
|
||||
templateGenerator := excel.NewMeterReadingsExcelTemplateGenerator()
|
||||
defer templateGenerator.Close()
|
||||
err = templateGenerator.WriteTemplateData(meterDocs)
|
||||
if err != nil {
|
||||
meterLog.Error("无法下载指定的园区表计抄表模板,无法生成表计抄表模板", zap.Error(err))
|
||||
return result.Error(fiber.StatusInternalServerError, fmt.Sprintf("无法生成表计抄表模板,%s", err.Error()))
|
||||
}
|
||||
c.Status(fiber.StatusOK)
|
||||
c.Set(fiber.HeaderContentType, fiber.MIMEOctetStream)
|
||||
c.Set("Content-Transfer-Encoding", "binary")
|
||||
c.Set(fiber.HeaderContentDisposition, fmt.Sprintf("attachment; filename=%s-表计抄表模板.xlsx", parkDetail.Name))
|
||||
templateGenerator.WriteTo(c.Response().BodyWriter())
|
||||
return nil
|
||||
}
|
||||
|
||||
// 处理上传的抄表记录文件
|
||||
func uploadMeterReadings(c *fiber.Ctx) error {
|
||||
parkId := c.Params("pid")
|
||||
meterLog.Info("从Excel文件中导入抄表档案", zap.String("park id", parkId))
|
||||
result := response.NewResult(c)
|
||||
if pass, err := checkParkBelongs(parkId, meterLog, c, &result); !pass {
|
||||
return err
|
||||
}
|
||||
uploadFile, err := c.FormFile("data")
|
||||
if err != nil {
|
||||
meterLog.Error("无法从Excel文件中导入抄表档案,无法获取上传的文件", zap.Error(err))
|
||||
return result.NotAccept(fmt.Sprintf("没有接收到上传的文件,%s", err.Error()))
|
||||
}
|
||||
errs, err := service.MeterService.BatchImportReadings(parkId, uploadFile)
|
||||
if err != nil {
|
||||
meterLog.Error("无法从Excel文件中导入抄表档案,无法导入抄表档案", zap.Error(err))
|
||||
return result.Json(fiber.StatusNotAcceptable, "上传的抄表档案存在错误。", fiber.Map{"errors": errs})
|
||||
}
|
||||
return result.Success("表计档案已经导入完成。", fiber.Map{"errors": errs})
|
||||
}
|
||||
@@ -1,206 +0,0 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"electricity_bill_calc/excel"
|
||||
"electricity_bill_calc/model"
|
||||
"electricity_bill_calc/response"
|
||||
"electricity_bill_calc/security"
|
||||
"electricity_bill_calc/service"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/samber/lo"
|
||||
"github.com/shopspring/decimal"
|
||||
)
|
||||
|
||||
func InitializeMeter04kVController(router *gin.Engine) {
|
||||
router.GET("/park/:pid/meter/template", download04kvMeterArchiveTemplate)
|
||||
router.GET("/park/:pid/meters", security.EnterpriseAuthorize, ListPaged04kVMeter)
|
||||
router.GET("/park/:pid/meter/:code", security.EnterpriseAuthorize, fetch04kVMeterDetail)
|
||||
router.POST("/park/:pid/meter", security.EnterpriseAuthorize, createSingle04kVMeter)
|
||||
router.PUT("/park/:pid/meter/:code", security.EnterpriseAuthorize, modifySingle04kVMeter)
|
||||
router.POST("/park/:pid/meter/batch", security.EnterpriseAuthorize, batchImport04kVMeterArchive)
|
||||
}
|
||||
|
||||
func download04kvMeterArchiveTemplate(c *gin.Context) {
|
||||
result := response.NewResult(c)
|
||||
requestParkId := c.Param("pid")
|
||||
// if !ensureParkBelongs(c, result, requestParkId) {
|
||||
// return
|
||||
// }
|
||||
parkDetail, err := service.ParkService.FetchParkDetail(requestParkId)
|
||||
if err != nil {
|
||||
result.NotFound("未找到指定的园区信息。")
|
||||
return
|
||||
}
|
||||
c.Status(http.StatusOK)
|
||||
c.Header("Content-Type", "application/octet-stream")
|
||||
c.Header("Content-Transfer-Encoding", "binary")
|
||||
c.FileAttachment("./assets/meter_04kv_template.xlsx", fmt.Sprintf("%s-户表档案.xlsx", parkDetail.Name))
|
||||
}
|
||||
|
||||
func ListPaged04kVMeter(c *gin.Context) {
|
||||
result := response.NewResult(c)
|
||||
requestParkId := c.Param("pid")
|
||||
if !ensureParkBelongs(c, result, requestParkId) {
|
||||
return
|
||||
}
|
||||
requestPage, err := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||
if err != nil {
|
||||
result.NotAccept("查询参数[page]格式不正确。")
|
||||
return
|
||||
}
|
||||
requestKeyword := c.DefaultQuery("keyword", "")
|
||||
meters, totalItem, err := service.Meter04kVService.ListMeterDetail(requestParkId, requestKeyword, requestPage)
|
||||
if err != nil {
|
||||
result.NotFound(err.Error())
|
||||
return
|
||||
}
|
||||
result.Json(
|
||||
http.StatusOK,
|
||||
"已获取到符合条件的0.4kV表计集合。",
|
||||
response.NewPagedResponse(requestPage, totalItem).ToMap(),
|
||||
gin.H{"meters": meters},
|
||||
)
|
||||
}
|
||||
|
||||
func fetch04kVMeterDetail(c *gin.Context) {
|
||||
result := response.NewResult(c)
|
||||
requestParkId := c.Param("pid")
|
||||
if !ensureParkBelongs(c, result, requestParkId) {
|
||||
return
|
||||
}
|
||||
requestMeterCode := c.Param("code")
|
||||
meter, err := service.Meter04kVService.Get04kVMeterDetail(requestParkId, requestMeterCode)
|
||||
if err != nil {
|
||||
result.NotFound(err.Error())
|
||||
return
|
||||
}
|
||||
if meter == nil {
|
||||
result.Json(http.StatusNotFound, "指定的表计信息未能找到。", gin.H{"meter": nil})
|
||||
return
|
||||
}
|
||||
result.Json(http.StatusOK, "指定的表计信息已找到。", gin.H{"meter": meter})
|
||||
}
|
||||
|
||||
type _MeterModificationFormData struct {
|
||||
Address *string `json:"address" form:"address"`
|
||||
CustomerName *string `json:"customerName" form:"customerName"`
|
||||
ContactName *string `json:"contactName" form:"contactName"`
|
||||
ContactPhone *string `json:"contactPhone" form:"contactPhone"`
|
||||
Ratio decimal.Decimal `json:"ratio" form:"ratio"`
|
||||
Seq int `json:"seq" form:"seq"`
|
||||
IsPublicMeter bool `json:"isPublicMeter" form:"isPublicMeter"`
|
||||
WillDilute bool `json:"willDilute" form:"willDilute"`
|
||||
Enabled bool `json:"enabled" form:"enabled"`
|
||||
}
|
||||
|
||||
type _MeterCreationFormData struct {
|
||||
Code string `json:"code" form:"code"`
|
||||
Address *string `json:"address" form:"address"`
|
||||
CustomerName *string `json:"customerName" form:"customerName"`
|
||||
ContactName *string `json:"contactName" form:"contactName"`
|
||||
ContactPhone *string `json:"contactPhone" form:"contactPhone"`
|
||||
Ratio decimal.Decimal `json:"ratio" form:"ratio"`
|
||||
Seq int `json:"seq" form:"seq"`
|
||||
IsPublicMeter bool `json:"isPublicMeter" form:"isPublicMeter"`
|
||||
WillDilute bool `json:"willDilute" form:"willDilute"`
|
||||
Enabled bool `json:"enabled" form:"enabled"`
|
||||
}
|
||||
|
||||
func createSingle04kVMeter(c *gin.Context) {
|
||||
result := response.NewResult(c)
|
||||
requestParkId := c.Param("pid")
|
||||
if !ensureParkBelongs(c, result, requestParkId) {
|
||||
return
|
||||
}
|
||||
formData := new(_MeterCreationFormData)
|
||||
c.BindJSON(formData)
|
||||
log.Printf("[controller|debug] form: %+v", formData)
|
||||
newMeter := new(model.Meter04KV)
|
||||
copier.Copy(newMeter, formData)
|
||||
newMeter.ParkId = requestParkId
|
||||
log.Printf("[controller|debug] meter: %+v", newMeter)
|
||||
err := service.Meter04kVService.CreateSingleMeter(*newMeter)
|
||||
if err != nil {
|
||||
result.Error(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
result.Created("新0.4kV表计已经添加完成。")
|
||||
}
|
||||
|
||||
func modifySingle04kVMeter(c *gin.Context) {
|
||||
result := response.NewResult(c)
|
||||
requestParkId := c.Param("pid")
|
||||
if !ensureParkBelongs(c, result, requestParkId) {
|
||||
return
|
||||
}
|
||||
requestMeterCode := c.Param("code")
|
||||
meterDetail, err := service.Meter04kVService.Get04kVMeterDetail(requestParkId, requestMeterCode)
|
||||
if err != nil {
|
||||
result.NotFound(err.Error())
|
||||
return
|
||||
}
|
||||
if meterDetail == nil {
|
||||
result.NotFound("指定表计的信息为找到,不能修改。")
|
||||
return
|
||||
}
|
||||
formData := new(_MeterModificationFormData)
|
||||
c.BindJSON(formData)
|
||||
copier.Copy(meterDetail, formData)
|
||||
err = service.Meter04kVService.UpdateSingleMeter(meterDetail)
|
||||
if err != nil {
|
||||
result.Error(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
result.Updated("指定0.4kV表计信息已经更新。")
|
||||
}
|
||||
|
||||
func batchImport04kVMeterArchive(c *gin.Context) {
|
||||
result := response.NewResult(c)
|
||||
requestParkId := c.Param("pid")
|
||||
if !ensureParkBelongs(c, result, requestParkId) {
|
||||
return
|
||||
}
|
||||
uploadedFile, err := c.FormFile("data")
|
||||
if err != nil {
|
||||
result.NotAccept("没有接收到上传的档案文件。")
|
||||
return
|
||||
}
|
||||
archiveFile, err := uploadedFile.Open()
|
||||
if err != nil {
|
||||
result.Error(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
analyzer, err := excel.NewMeterArchiveExcelAnalyzer(archiveFile)
|
||||
if err != nil {
|
||||
result.Error(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
records, errs := analyzer.Analysis(*new(model.Meter04KV))
|
||||
if len(errs) > 0 {
|
||||
result.Json(http.StatusNotAcceptable, "上传的表计档案文件存在错误。", gin.H{"errors": errs})
|
||||
return
|
||||
}
|
||||
|
||||
mergedMeters := lo.Map(records, func(meter model.Meter04KV, index int) model.Meter04KV {
|
||||
meter.ParkId = requestParkId
|
||||
meter.Enabled = true
|
||||
return meter
|
||||
})
|
||||
errs = service.Meter04kVService.DuplicateMeterCodeValidate(mergedMeters)
|
||||
if len(errs) > 0 {
|
||||
result.Json(http.StatusNotAcceptable, "上传的表计档案文件存在错误。", gin.H{"errors": errs})
|
||||
return
|
||||
}
|
||||
err = service.Meter04kVService.BatchCreateMeter(mergedMeters)
|
||||
if err != nil {
|
||||
result.Error(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
result.Json(http.StatusOK, "上传的表计档案已经全部导入。", gin.H{"errors": make([]excel.ExcelAnalysisError, 0)})
|
||||
}
|
||||
@@ -1,193 +1,331 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"electricity_bill_calc/model"
|
||||
"electricity_bill_calc/logger"
|
||||
"electricity_bill_calc/repository"
|
||||
"electricity_bill_calc/response"
|
||||
"electricity_bill_calc/security"
|
||||
"electricity_bill_calc/service"
|
||||
"electricity_bill_calc/tools"
|
||||
"electricity_bill_calc/vo"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/shopspring/decimal"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
func InitializeParkController(router *gin.Engine) {
|
||||
router.GET("/parks", security.EnterpriseAuthorize, listAllParksUnderSessionUser)
|
||||
router.GET("/parks/:uid", security.MustAuthenticated, listAllParksUnderSpecificUser)
|
||||
router.POST("/park", security.EnterpriseAuthorize, createNewPark)
|
||||
router.PUT("/park/:pid", security.EnterpriseAuthorize, modifyPark)
|
||||
router.GET("/park/:pid", security.EnterpriseAuthorize, fetchParkDetail)
|
||||
router.PUT("/park/:pid/enabled", security.EnterpriseAuthorize, changeParkEnableState)
|
||||
router.DELETE("/park/:pid", security.EnterpriseAuthorize, deleteSpecificPark)
|
||||
var parkLog = logger.Named("Handler", "Park")
|
||||
|
||||
func InitializeParkHandlers(router *fiber.App) {
|
||||
router.Get("/park", security.EnterpriseAuthorize, listParksBelongsToCurrentUser)
|
||||
router.Post("/park", security.EnterpriseAuthorize, createPark)
|
||||
router.Get("/park/belongs/:uid", security.OPSAuthorize, listParksBelongsTo)
|
||||
router.Get("/park/:pid", security.EnterpriseAuthorize, fetchParkDetail)
|
||||
router.Put("/park/:pid", security.EnterpriseAuthorize, modifySpecificPark)
|
||||
router.Delete("/park/:pid", security.EnterpriseAuthorize, deleteSpecificPark)
|
||||
router.Put("/park/:pid/enabled", security.EnterpriseAuthorize, modifyParkEnabling)
|
||||
router.Get("/park/:pid/building", security.EnterpriseAuthorize, listBuildingsBelongsToPark)
|
||||
router.Post("/park/:pid/building", security.EnterpriseAuthorize, createBuildingInPark)
|
||||
router.Put("/park/:pid/building/:bid", security.EnterpriseAuthorize, modifySpecificBuildingInPark)
|
||||
router.Delete("/park/:pid/building/:bid", security.EnterpriseAuthorize, deletedParkBuilding)
|
||||
router.Put("/park/:pid/building/:bid/enabled", security.EnterpriseAuthorize, modifyParkBuildingEnabling)
|
||||
}
|
||||
|
||||
func ensureParkBelongs(c *gin.Context, result *response.Result, requestParkId string) bool {
|
||||
userSession, err := _retreiveSession(c)
|
||||
if err != nil {
|
||||
result.Unauthorized(err.Error())
|
||||
return false
|
||||
}
|
||||
sure, err := service.ParkService.EnsurePark(userSession.Uid, requestParkId)
|
||||
if err != nil {
|
||||
result.Error(http.StatusInternalServerError, err.Error())
|
||||
return false
|
||||
}
|
||||
if !sure {
|
||||
result.Unauthorized("不能访问不属于自己的园区。")
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func listAllParksUnderSessionUser(c *gin.Context) {
|
||||
// 列出隶属于当前用户的全部园区
|
||||
func listParksBelongsToCurrentUser(c *fiber.Ctx) error {
|
||||
result := response.NewResult(c)
|
||||
userSession, err := _retreiveSession(c)
|
||||
session, err := _retreiveSession(c)
|
||||
if err != nil {
|
||||
result.Unauthorized(err.Error())
|
||||
return
|
||||
parkLog.Error("列出当前用的全部园区,无法获取当前用户的会话。")
|
||||
return result.Unauthorized(err.Error())
|
||||
}
|
||||
parks, err := service.ParkService.ListAllParkBelongsTo(userSession.Uid)
|
||||
parkLog.Info("列出当前用户下的全部园区", zap.String("user id", session.Uid))
|
||||
parks, err := repository.ParkRepository.ListAllParks(session.Uid)
|
||||
if err != nil {
|
||||
result.Error(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
parkLog.Error("无法获取园区列表。", zap.String("user id", session.Uid))
|
||||
return result.Error(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
result.Json(http.StatusOK, "已获取到指定用户下的园区。", gin.H{"parks": parks})
|
||||
return result.Success("已获取到指定用户的下的园区", fiber.Map{"parks": parks})
|
||||
}
|
||||
|
||||
func listAllParksUnderSpecificUser(c *gin.Context) {
|
||||
// 列出隶属于指定用户的全部园区
|
||||
func listParksBelongsTo(c *fiber.Ctx) error {
|
||||
result := response.NewResult(c)
|
||||
requestUserId := c.Param("uid")
|
||||
parks, err := service.ParkService.ListAllParkBelongsTo(requestUserId)
|
||||
userId := c.Params("uid")
|
||||
parkLog.Info("列出指定用户下的全部园区", zap.String("user id", userId))
|
||||
parks, err := repository.ParkRepository.ListAllParks(userId)
|
||||
if err != nil {
|
||||
result.Error(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
parkLog.Error("无法获取园区列表。", zap.String("user id", userId))
|
||||
return result.Error(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
result.Json(http.StatusOK, "已获取到指定用户下的园区。", gin.H{"parks": parks})
|
||||
return result.Success("已获取到指定用户的下的园区", fiber.Map{"parks": parks})
|
||||
}
|
||||
|
||||
type _ParkInfoFormData struct {
|
||||
Name string `json:"name" form:"name"`
|
||||
Region *string `json:"region" form:"region"`
|
||||
Address *string `json:"address" form:"address"`
|
||||
Contact *string `json:"contact" form:"contact"`
|
||||
Phone *string `json:"phone" from:"phone"`
|
||||
Area decimal.NullDecimal `json:"area" from:"area"`
|
||||
Capacity decimal.NullDecimal `json:"capacity" from:"capacity"`
|
||||
Tenement decimal.NullDecimal `json:"tenement" from:"tenement"`
|
||||
Category int `json:"category" form:"category"`
|
||||
Submeter int `json:"submeter" form:"submeter"`
|
||||
}
|
||||
|
||||
func createNewPark(c *gin.Context) {
|
||||
// 获取指定园区的详细信息
|
||||
func fetchParkDetail(c *fiber.Ctx) error {
|
||||
result := response.NewResult(c)
|
||||
userSession, err := _retreiveSession(c)
|
||||
parkId := c.Params("pid")
|
||||
parkLog.Info("获取指定园区的详细信息", zap.String("park id", parkId))
|
||||
park, err := repository.ParkRepository.RetrieveParkDetail(parkId)
|
||||
if err != nil {
|
||||
result.Unauthorized(err.Error())
|
||||
return
|
||||
parkLog.Error("无法获取园区信息。", zap.String("park id", parkId))
|
||||
return result.Error(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
formData := new(_ParkInfoFormData)
|
||||
c.BindJSON(formData)
|
||||
newPark := new(model.Park)
|
||||
copier.Copy(newPark, formData)
|
||||
newPark.Id = uuid.New().String()
|
||||
newPark.UserId = userSession.Uid
|
||||
nameAbbr := tools.PinyinAbbr(newPark.Name)
|
||||
newPark.Abbr = &nameAbbr
|
||||
newPark.Enabled = true
|
||||
err = service.ParkService.SaveNewPark(*newPark)
|
||||
if err != nil {
|
||||
result.Error(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
result.Created("新园区完成创建。")
|
||||
return result.Success("已获取到指定园区的详细信息", fiber.Map{"park": park})
|
||||
}
|
||||
|
||||
func modifyPark(c *gin.Context) {
|
||||
// 创建一个新的园区
|
||||
func createPark(c *fiber.Ctx) error {
|
||||
result := response.NewResult(c)
|
||||
userSession, err := _retreiveSession(c)
|
||||
session, err := _retreiveSession(c)
|
||||
if err != nil {
|
||||
result.Unauthorized(err.Error())
|
||||
return
|
||||
parkLog.Error("创建一个新的园区,无法获取当前用户的会话。")
|
||||
return result.Unauthorized(err.Error())
|
||||
}
|
||||
requestParkId := c.Param("pid")
|
||||
formData := new(_ParkInfoFormData)
|
||||
c.BindJSON(formData)
|
||||
park, err := service.ParkService.FetchParkDetail(requestParkId)
|
||||
parkLog.Info("创建一个新的园区", zap.String("user id", session.Uid))
|
||||
creationForm := new(vo.ParkInformationForm)
|
||||
if err := c.BodyParser(creationForm); err != nil {
|
||||
parkLog.Error("无法解析园区表单数据。", zap.String("user id", session.Uid), zap.Error(err))
|
||||
return result.NotAccept(err.Error())
|
||||
}
|
||||
park, err := creationForm.TryIntoPark()
|
||||
if err != nil {
|
||||
result.Error(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
parkLog.Error("无法将园区表单数据转换为园区对象。", zap.String("user id", session.Uid), zap.Error(err))
|
||||
return result.NotAccept(err.Error())
|
||||
}
|
||||
if userSession.Uid != park.UserId {
|
||||
result.Unauthorized("不能修改不属于自己的园区。")
|
||||
return
|
||||
ok, err := repository.ParkRepository.CreatePark(session.Uid, park)
|
||||
switch {
|
||||
case err == nil && !ok:
|
||||
parkLog.Error("无法创建新的园区。", zap.String("user id", session.Uid))
|
||||
return result.NotAccept("无法创建新的园区。")
|
||||
case err != nil:
|
||||
parkLog.Error("无法创建新的园区。", zap.String("user id", session.Uid), zap.Error(err))
|
||||
return result.Error(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
copier.Copy(park, formData)
|
||||
nameAbbr := tools.PinyinAbbr(formData.Name)
|
||||
park.Abbr = &nameAbbr
|
||||
err = service.ParkService.UpdateParkInfo(park)
|
||||
if err != nil {
|
||||
result.Error(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
result.Updated("指定园区资料已更新。")
|
||||
return result.Created("已创建一个新的园区")
|
||||
}
|
||||
|
||||
func fetchParkDetail(c *gin.Context) {
|
||||
// 修改指定园区的信息
|
||||
func modifySpecificPark(c *fiber.Ctx) error {
|
||||
result := response.NewResult(c)
|
||||
requestParkId := c.Param("pid")
|
||||
if !ensureParkBelongs(c, result, requestParkId) {
|
||||
return
|
||||
}
|
||||
park, err := service.ParkService.FetchParkDetail(requestParkId)
|
||||
parkId := c.Params("pid")
|
||||
session, err := _retreiveSession(c)
|
||||
if err != nil {
|
||||
result.Error(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
parkLog.Error("修改指定园区的信息,无法获取当前用户的会话。")
|
||||
return result.Unauthorized(err.Error())
|
||||
}
|
||||
result.Json(http.StatusOK, "已经获取到指定园区的信息。", gin.H{"park": park})
|
||||
if pass, err := checkParkBelongs(parkId, parkLog, c, &result); !pass {
|
||||
return err
|
||||
}
|
||||
parkForm := new(vo.ParkInformationForm)
|
||||
if err := c.BodyParser(parkForm); err != nil {
|
||||
parkLog.Error("无法解析园区表单数据。", zap.String("park id", parkId), zap.String("user id", session.Uid), zap.Error(err))
|
||||
return result.NotAccept(err.Error())
|
||||
}
|
||||
park, err := parkForm.TryIntoPark()
|
||||
if err != nil {
|
||||
parkLog.Error("无法将园区表单数据转换为园区对象。", zap.String("park id", parkId), zap.String("user id", session.Uid), zap.Error(err))
|
||||
return result.NotAccept(err.Error())
|
||||
}
|
||||
ok, err := repository.ParkRepository.UpdatePark(parkId, park)
|
||||
switch {
|
||||
case err == nil && !ok:
|
||||
parkLog.Error("无法更新园区信息。", zap.String("park id", parkId), zap.String("user id", session.Uid))
|
||||
return result.NotAccept("无法更新园区信息。")
|
||||
case err != nil:
|
||||
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.Updated("已更新指定园区的详细信息")
|
||||
}
|
||||
|
||||
type _ParkStateFormData struct {
|
||||
Enabled bool `json:"enabled" form:"enabled"`
|
||||
}
|
||||
|
||||
func changeParkEnableState(c *gin.Context) {
|
||||
// 修改指定园区的可用性
|
||||
func modifyParkEnabling(c *fiber.Ctx) error {
|
||||
result := response.NewResult(c)
|
||||
userSession, err := _retreiveSession(c)
|
||||
parkId := c.Params("pid")
|
||||
session, err := _retreiveSession(c)
|
||||
if err != nil {
|
||||
result.Unauthorized(err.Error())
|
||||
return
|
||||
parkLog.Error("修改指定园区的可用性,无法获取当前用户的会话。")
|
||||
return result.Unauthorized(err.Error())
|
||||
}
|
||||
requestParkId := c.Param("pid")
|
||||
if !ensureParkBelongs(c, result, requestParkId) {
|
||||
return
|
||||
if pass, err := checkParkBelongs(parkId, parkLog, c, &result); !pass {
|
||||
return err
|
||||
}
|
||||
formData := new(_ParkStateFormData)
|
||||
c.BindJSON(formData)
|
||||
err = service.ParkService.ChangeParkState(userSession.Uid, requestParkId, formData.Enabled)
|
||||
if err != nil {
|
||||
result.Error(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
stateForm := new(vo.StateForm)
|
||||
if err := c.BodyParser(stateForm); err != nil {
|
||||
parkLog.Error("无法解析园区表单数据。", zap.String("park id", parkId), zap.String("user id", session.Uid), zap.Error(err))
|
||||
return result.NotAccept(err.Error())
|
||||
}
|
||||
result.Updated("指定园区的可用性状态已成功更新。")
|
||||
ok, err := repository.ParkRepository.EnablingPark(parkId, stateForm.Enabled)
|
||||
switch {
|
||||
case err == nil && !ok:
|
||||
parkLog.Error("无法更新园区可用性。", zap.String("park id", parkId), zap.String("user id", session.Uid))
|
||||
return result.NotAccept("无法更新园区可用性。")
|
||||
case err != nil:
|
||||
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.Updated("已更新指定园区的可用性。")
|
||||
}
|
||||
|
||||
func deleteSpecificPark(c *gin.Context) {
|
||||
// 删除指定的园区
|
||||
func deleteSpecificPark(c *fiber.Ctx) error {
|
||||
result := response.NewResult(c)
|
||||
userSession, err := _retreiveSession(c)
|
||||
parkId := c.Params("pid")
|
||||
session, err := _retreiveSession(c)
|
||||
if err != nil {
|
||||
result.Unauthorized(err.Error())
|
||||
return
|
||||
parkLog.Error("删除指定的园区,无法获取当前用户的会话。")
|
||||
return result.Unauthorized(err.Error())
|
||||
}
|
||||
requestParkId := c.Param("pid")
|
||||
if !ensureParkBelongs(c, result, requestParkId) {
|
||||
return
|
||||
if pass, err := checkParkBelongs(parkId, parkLog, c, &result); !pass {
|
||||
return err
|
||||
}
|
||||
err = service.ParkService.DeletePark(userSession.Uid, requestParkId)
|
||||
if err != nil {
|
||||
result.Error(http.StatusInternalServerError, err.Error())
|
||||
ok, err := repository.ParkRepository.DeletePark(parkId)
|
||||
switch {
|
||||
case err == nil && !ok:
|
||||
parkLog.Error("无法删除园区。", zap.String("park id", parkId), zap.String("user id", session.Uid))
|
||||
return result.NotAccept("无法删除园区。")
|
||||
case err != nil:
|
||||
parkLog.Error("无法删除园区。", zap.String("park id", parkId), zap.String("user id", session.Uid), zap.Error(err))
|
||||
return result.Error(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
result.Deleted("指定园区已成功删除。")
|
||||
return result.Deleted("已删除指定的园区")
|
||||
}
|
||||
|
||||
// 列出指定园区中已经登记的建筑
|
||||
func listBuildingsBelongsToPark(c *fiber.Ctx) error {
|
||||
result := response.NewResult(c)
|
||||
parkId := c.Params("pid")
|
||||
session, err := _retreiveSession(c)
|
||||
if err != nil {
|
||||
parkLog.Error("列出指定园区中已经登记的建筑,无法获取当前用户的会话。")
|
||||
return result.Unauthorized(err.Error())
|
||||
}
|
||||
ok, err := repository.ParkRepository.IsParkBelongs(parkId, session.Uid)
|
||||
switch {
|
||||
case err != nil:
|
||||
parkLog.Error("无法判断园区是否隶属于当前用户。", zap.String("park id", parkId), zap.String("user id", session.Uid), zap.Error(err))
|
||||
return result.Error(http.StatusInternalServerError, err.Error())
|
||||
case err == nil && !ok:
|
||||
parkLog.Error("用户试图访问不属于自己的园区。", zap.String("park id", parkId), zap.String("user id", session.Uid))
|
||||
return result.Forbidden("您无权访问该园区。")
|
||||
}
|
||||
buildings, err := repository.ParkRepository.RetrieveParkBuildings(parkId)
|
||||
if err != nil {
|
||||
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.Success("已获取到指定园区中的建筑列表", fiber.Map{"buildings": buildings})
|
||||
}
|
||||
|
||||
// 在指定园区中创建一个新的建筑
|
||||
func createBuildingInPark(c *fiber.Ctx) error {
|
||||
result := response.NewResult(c)
|
||||
parkId := c.Params("pid")
|
||||
session, err := _retreiveSession(c)
|
||||
if err != nil {
|
||||
parkLog.Error("在指定园区中创建一个新的建筑,无法获取当前用户的会话。")
|
||||
return result.Unauthorized(err.Error())
|
||||
}
|
||||
if pass, err := checkParkBelongs(parkId, parkLog, c, &result); !pass {
|
||||
return err
|
||||
}
|
||||
buildingForm := new(vo.ParkBuildingInformationForm)
|
||||
if err := c.BodyParser(buildingForm); err != nil {
|
||||
parkLog.Error("无法解析建筑表单数据。", zap.String("park id", parkId), zap.String("user id", session.Uid), zap.Error(err))
|
||||
return result.NotAccept(err.Error())
|
||||
}
|
||||
ok, err := repository.ParkRepository.CreateParkBuilding(parkId, buildingForm.Name, &buildingForm.Floors)
|
||||
switch {
|
||||
case err == nil && !ok:
|
||||
parkLog.Error("无法创建新的建筑。", zap.String("park id", parkId), zap.String("user id", session.Uid))
|
||||
return result.NotAccept("无法创建新的建筑。")
|
||||
case err != nil:
|
||||
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.Created("已创建一个新的建筑")
|
||||
}
|
||||
|
||||
// 修改指定园区中的指定建筑的信息
|
||||
func modifySpecificBuildingInPark(c *fiber.Ctx) error {
|
||||
result := response.NewResult(c)
|
||||
parkId := c.Params("pid")
|
||||
buildingId := c.Params("bid")
|
||||
session, err := _retreiveSession(c)
|
||||
if err != nil {
|
||||
parkLog.Error("修改指定园区中的指定建筑的信息,无法获取当前用户的会话。")
|
||||
return result.Unauthorized(err.Error())
|
||||
}
|
||||
if pass, err := checkParkBelongs(parkId, parkLog, c, &result); !pass {
|
||||
return err
|
||||
}
|
||||
buildingForm := new(vo.ParkBuildingInformationForm)
|
||||
if err := c.BodyParser(buildingForm); err != nil {
|
||||
parkLog.Error("无法解析建筑表单数据。", zap.String("park id", parkId), zap.String("user id", session.Uid), zap.Error(err))
|
||||
return result.NotAccept(err.Error())
|
||||
}
|
||||
ok, err := repository.ParkRepository.ModifyParkBuilding(buildingId, parkId, buildingForm.Name, &buildingForm.Floors)
|
||||
switch {
|
||||
case err == nil && !ok:
|
||||
parkLog.Error("无法更新建筑信息。", zap.String("park id", parkId), zap.String("user id", session.Uid))
|
||||
return result.NotAccept("无法更新建筑信息。")
|
||||
case err != nil:
|
||||
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.Updated("已更新指定建筑的信息")
|
||||
}
|
||||
|
||||
// 修改指定园区中指定建筑的可用性
|
||||
func modifyParkBuildingEnabling(c *fiber.Ctx) error {
|
||||
result := response.NewResult(c)
|
||||
parkId := c.Params("pid")
|
||||
buildingId := c.Params("bid")
|
||||
session, err := _retreiveSession(c)
|
||||
if err != nil {
|
||||
parkLog.Error("修改指定园区中指定建筑的可用性,无法获取当前用户的会话。")
|
||||
return result.Unauthorized(err.Error())
|
||||
}
|
||||
if pass, err := checkParkBelongs(parkId, parkLog, c, &result); !pass {
|
||||
return err
|
||||
}
|
||||
stateForm := new(vo.StateForm)
|
||||
if err := c.BodyParser(stateForm); err != nil {
|
||||
parkLog.Error("无法解析建筑表单数据。", zap.String("park id", parkId), zap.String("user id", session.Uid), zap.Error(err))
|
||||
return result.NotAccept(err.Error())
|
||||
}
|
||||
ok, err := repository.ParkRepository.EnablingParkBuilding(buildingId, parkId, stateForm.Enabled)
|
||||
switch {
|
||||
case err == nil && !ok:
|
||||
parkLog.Error("无法更新建筑可用性。", zap.String("park id", parkId), zap.String("user id", session.Uid))
|
||||
return result.NotAccept("无法更新建筑可用性。")
|
||||
case err != nil:
|
||||
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.Updated("已更新指定建筑的可用性")
|
||||
}
|
||||
|
||||
// 删除指定园区中的指定建筑
|
||||
func deletedParkBuilding(c *fiber.Ctx) error {
|
||||
result := response.NewResult(c)
|
||||
parkId := c.Params("pid")
|
||||
buildingId := c.Params("bid")
|
||||
session, err := _retreiveSession(c)
|
||||
if err != nil {
|
||||
parkLog.Error("删除指定园区中的指定建筑,无法获取当前用户的会话。")
|
||||
return result.Unauthorized(err.Error())
|
||||
}
|
||||
if pass, err := checkParkBelongs(parkId, parkLog, c, &result); !pass {
|
||||
return err
|
||||
}
|
||||
ok, err := repository.ParkRepository.DeleteParkBuilding(buildingId, parkId)
|
||||
switch {
|
||||
case err == nil && !ok:
|
||||
parkLog.Error("无法删除建筑。", zap.String("park id", parkId), zap.String("user id", session.Uid))
|
||||
return result.NotAccept("无法删除建筑。")
|
||||
case err != nil:
|
||||
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("已删除指定的建筑")
|
||||
}
|
||||
|
||||
@@ -1,44 +1,40 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"electricity_bill_calc/repository"
|
||||
"electricity_bill_calc/response"
|
||||
"electricity_bill_calc/service"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
func InitializeRegionController(router *gin.Engine) {
|
||||
router.GET("/region/:rid", fetchRegions)
|
||||
router.GET("/regions/:rid", fetchAllLeveledRegions)
|
||||
func InitializeRegionHandlers(router *fiber.App) {
|
||||
router.Get("/region/:rid", getSubRegions)
|
||||
router.Get("/regions/:rid", getParentRegions)
|
||||
}
|
||||
|
||||
func fetchRegions(c *gin.Context) {
|
||||
func getSubRegions(c *fiber.Ctx) error {
|
||||
result := response.NewResult(c)
|
||||
requestParentId := c.Param("rid")
|
||||
regions, err := service.RegionService.FetchSubRegions(requestParentId)
|
||||
requestParentId := c.Params("rid")
|
||||
regions, err := repository.RegionRepository.FindSubRegions(requestParentId)
|
||||
if err != nil {
|
||||
result.Error(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
return result.Error(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
if len(regions) == 0 {
|
||||
result.Json(http.StatusNotFound, "未能获取到相关的行政区划。", gin.H{"regions": make([]string, 0)})
|
||||
return
|
||||
return result.Json(http.StatusNotFound, "未能获取到相关的行政区划。", fiber.Map{"regions": make([]string, 0)})
|
||||
}
|
||||
result.Json(http.StatusOK, "已经获取到相关的行政区划。", gin.H{"regions": regions})
|
||||
return result.Json(http.StatusOK, "已经获取到相关的行政区划。", fiber.Map{"regions": regions})
|
||||
}
|
||||
|
||||
func fetchAllLeveledRegions(c *gin.Context) {
|
||||
func getParentRegions(c *fiber.Ctx) error {
|
||||
result := response.NewResult(c)
|
||||
requestRegionCode := c.Param("rid")
|
||||
regions, err := service.RegionService.FetchAllParentRegions(requestRegionCode)
|
||||
requestRegionCode := c.Params("rid")
|
||||
regions, err := repository.RegionRepository.FindParentRegions(requestRegionCode)
|
||||
if err != nil {
|
||||
result.Error(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
return result.Error(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
if len(regions) == 0 {
|
||||
result.Json(http.StatusNotFound, "未能获取到相关的行政区划。", gin.H{"regions": make([]string, 0)})
|
||||
return
|
||||
return result.Json(http.StatusNotFound, "未能获取到相关的行政区划。", fiber.Map{"regions": make([]string, 0)})
|
||||
}
|
||||
result.Json(http.StatusOK, "以及获取到相关的行政区划。", gin.H{"regions": regions})
|
||||
return result.Json(http.StatusOK, "以及获取到相关的行政区划。", fiber.Map{"regions": regions})
|
||||
}
|
||||
|
||||
@@ -1,478 +1,425 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"electricity_bill_calc/exceptions"
|
||||
"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"
|
||||
"log"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
"electricity_bill_calc/types"
|
||||
"electricity_bill_calc/vo"
|
||||
|
||||
"github.com/fufuok/utils"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/samber/lo"
|
||||
"github.com/shopspring/decimal"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
func InitializeReportController(router *gin.Engine) {
|
||||
router.GET("/reports/with/drafts", security.EnterpriseAuthorize, fetchNewestReportOfParkWithDraft)
|
||||
router.POST("/park/:pid/report", security.EnterpriseAuthorize, initializeNewReport)
|
||||
router.GET("/report/:rid/step/state", security.EnterpriseAuthorize, fetchReportStepStates)
|
||||
router.GET("/report/:rid/summary", security.EnterpriseAuthorize, fetchReportParkSummary)
|
||||
router.PUT("/report/:rid/summary", security.EnterpriseAuthorize, fillReportSummary)
|
||||
router.GET("/report/:rid/summary/calculate", security.EnterpriseAuthorize, testCalculateReportSummary)
|
||||
router.POST("/report/:rid/summary/calculate", security.EnterpriseAuthorize, progressReportSummary)
|
||||
router.GET("/report/:rid/maintenance", security.EnterpriseAuthorize, fetchWillDilutedFees)
|
||||
router.POST("/report/:rid/maintenance", security.EnterpriseAuthorize, createTemporaryWillDilutedFee)
|
||||
router.POST("/report/:rid/maintenance/import", security.EnterpriseAuthorize, importPredefinedMaintenanceFees)
|
||||
router.PUT("/report/:rid/maintenance/:mid", security.EnterpriseAuthorize, modifyWillDilutedFee)
|
||||
router.DELETE("/report/:rid/maintenance/:mid", security.EnterpriseAuthorize, deleteTemporaryWillDilutedFee)
|
||||
router.PUT("/report/:rid/step/diluted/fees", security.EnterpriseAuthorize, progressReportWillDilutedFee)
|
||||
router.PUT("/report/:rid/step/meter/register", security.EnterpriseAuthorize, progressEndUserRegister)
|
||||
router.POST("/report/:rid/publish", security.EnterpriseAuthorize, publishReport)
|
||||
router.GET("/reports", security.MustAuthenticated, searchReports)
|
||||
router.GET("/report/:rid", security.MustAuthenticated, fetchReportPublicity)
|
||||
router.POST("/report/:rid/calculate", security.EnterpriseAuthorize, calculateReport)
|
||||
var reportLog = logger.Named("Handler", "Report")
|
||||
|
||||
func InitializeReportHandlers(router *fiber.App) {
|
||||
router.Get("/reports", security.MustAuthenticated, reportComprehensiveSearch)
|
||||
router.Post("/report", security.EnterpriseAuthorize, initNewReportCalculateTask)
|
||||
router.Get("/report/draft", security.EnterpriseAuthorize, listDraftReportIndicies)
|
||||
//TODO: 2023-07-20将calcualte错误请求改为正确的calculate请求
|
||||
router.Post("/report/calculate", security.EnterpriseAuthorize, testCalculateReportSummary)
|
||||
router.Get("/report/calculate/status", security.EnterpriseAuthorize, listCalculateTaskStatus)
|
||||
router.Get("/report/:rid", security.EnterpriseAuthorize, getReportDetail)
|
||||
router.Put("/report/:rid", security.EnterpriseAuthorize, updateReportCalculateTask)
|
||||
router.Post("/report/:rid/publish", security.EnterpriseAuthorize, publishReport)
|
||||
router.Put("/report/:rid/calculate", security.EnterpriseAuthorize, initiateCalculateTask)
|
||||
router.Get("/report/:rid/publics", security.MustAuthenticated, listPublicMetersInReport)
|
||||
router.Get("/report/:rid/summary", security.MustAuthenticated, getReportSummary)
|
||||
router.Get("/report/:rid/summary/filled", security.EnterpriseAuthorize, getParkFilledSummary)
|
||||
router.Get("/report/:rid/pooled", security.MustAuthenticated, listPooledMetersInReport)
|
||||
router.Get("/report/:rid/pooled/:code/submeter", security.MustAuthenticated, listSubmetersInPooledMeter)
|
||||
router.Get("/report/:rid/tenement", security.MustAuthenticated, listTenementsInReport)
|
||||
router.Get("/report/:rid/tenement/:tid", security.MustAuthenticated, getTenementDetailInReport)
|
||||
}
|
||||
|
||||
func ensureReportBelongs(c *gin.Context, result *response.Result, requestReportId string) bool {
|
||||
_, err := _retreiveSession(c)
|
||||
// 检查指定报表是否属于当前用户
|
||||
func checkReportBelongs(reportId string, log *zap.Logger, c *fiber.Ctx, result *response.Result) (bool, error) {
|
||||
session, err := _retreiveSession(c)
|
||||
if err != nil {
|
||||
result.Unauthorized(err.Error())
|
||||
return false
|
||||
log.Error("无法获取当前用户的会话信息", zap.Error(err))
|
||||
return false, result.Unauthorized("无法获取当前用户的会话信息。")
|
||||
}
|
||||
requestReport, err := service.ReportService.RetreiveReportIndex(requestReportId)
|
||||
ok, err := repository.ReportRepository.IsBelongsTo(reportId, session.Uid)
|
||||
if err != nil {
|
||||
result.NotFound(err.Error())
|
||||
return false
|
||||
log.Error("无法检查核算报表的所有权", zap.Error(err))
|
||||
return false, result.Error(fiber.StatusInternalServerError, "无法检查核算报表的所有权。")
|
||||
}
|
||||
if requestReport == nil {
|
||||
result.NotFound("指定报表未能找到。")
|
||||
return false
|
||||
if !ok {
|
||||
log.Error("核算报表不属于当前用户")
|
||||
return false, result.Forbidden("核算报表不属于当前用户。")
|
||||
}
|
||||
return ensureParkBelongs(c, result, requestReport.ParkId)
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func fetchNewestReportOfParkWithDraft(c *gin.Context) {
|
||||
result := response.NewResult(c)
|
||||
userSession, err := _retreiveSession(c)
|
||||
if err != nil {
|
||||
result.Unauthorized(err.Error())
|
||||
return
|
||||
}
|
||||
parks, err := service.ReportService.FetchParksWithNewestReport(userSession.Uid)
|
||||
if err != nil {
|
||||
result.Error(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
result.Json(http.StatusOK, "已获取到指定用户下所有园区的最新报表记录。", gin.H{"parks": parks})
|
||||
}
|
||||
|
||||
func initializeNewReport(c *gin.Context) {
|
||||
result := response.NewResult(c)
|
||||
requestParkId := c.Param("pid")
|
||||
userSession, err := _retreiveSession(c)
|
||||
if err != nil {
|
||||
result.Unauthorized(err.Error())
|
||||
return
|
||||
}
|
||||
if !ensureParkBelongs(c, result, requestParkId) {
|
||||
return
|
||||
}
|
||||
requestPeriod := c.Query("period")
|
||||
reportPeriod, err := time.Parse("2006-01", requestPeriod)
|
||||
if err != nil {
|
||||
result.NotAccept("提供的初始化期数格式不正确。")
|
||||
return
|
||||
}
|
||||
valid, err := service.ReportService.IsNewPeriodValid(userSession.Uid, reportPeriod)
|
||||
if err != nil {
|
||||
result.Error(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
if !valid {
|
||||
result.NotAccept("只能初始化已发布报表下一个月份的新报表。")
|
||||
return
|
||||
}
|
||||
newId, err := service.ReportService.InitializeNewReport(requestParkId, reportPeriod)
|
||||
if err != nil {
|
||||
result.Error(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
result.Created("新一期报表初始化成功。", gin.H{"reportId": newId})
|
||||
}
|
||||
|
||||
func fetchReportStepStates(c *gin.Context) {
|
||||
result := response.NewResult(c)
|
||||
requestReportId := c.Param("rid")
|
||||
if !ensureReportBelongs(c, result, requestReportId) {
|
||||
return
|
||||
}
|
||||
requestReport, err := service.ReportService.RetreiveReportIndex(requestReportId)
|
||||
if err != nil {
|
||||
result.NotFound(err.Error())
|
||||
return
|
||||
}
|
||||
result.Json(http.StatusOK, "已经获取到指定报表的填写状态。", gin.H{"steps": requestReport.StepState})
|
||||
}
|
||||
|
||||
func fetchReportParkSummary(c *gin.Context) {
|
||||
result := response.NewResult(c)
|
||||
requestReportId := c.Param("rid")
|
||||
if !ensureReportBelongs(c, result, requestReportId) {
|
||||
return
|
||||
}
|
||||
summary, err := service.ReportService.RetreiveReportSummary(requestReportId)
|
||||
if err != nil {
|
||||
result.NotFound(err.Error())
|
||||
return
|
||||
}
|
||||
if summary == nil {
|
||||
result.NotFound("指定报表未能找到。")
|
||||
return
|
||||
}
|
||||
result.Json(http.StatusOK, "已经获取到指定报表中的园区概况。", gin.H{"summary": summary})
|
||||
}
|
||||
|
||||
type ReportSummaryFormData struct {
|
||||
Overall decimal.Decimal `json:"overall" form:"overall"`
|
||||
OverallFee decimal.Decimal `json:"overallFee" form:"overallFee"`
|
||||
Critical decimal.Decimal `json:"critical" form:"critical"`
|
||||
CriticalFee decimal.Decimal `json:"criticalFee" form:"criticalFee"`
|
||||
Peak decimal.Decimal `json:"peak" form:"peak"`
|
||||
PeakFee decimal.Decimal `json:"peakFee" form:"peakFee"`
|
||||
Valley decimal.Decimal `json:"valley" form:"valley"`
|
||||
ValleyFee decimal.Decimal `json:"valleyFee" form:"valleyFee"`
|
||||
BasicFee decimal.Decimal `json:"basicFee" form:"basicFee"`
|
||||
AdjustFee decimal.Decimal `json:"adjustFee" from:"adjustFee"`
|
||||
}
|
||||
|
||||
func fillReportSummary(c *gin.Context) {
|
||||
result := response.NewResult(c)
|
||||
requestReportId := c.Param("rid")
|
||||
if !ensureReportBelongs(c, result, requestReportId) {
|
||||
return
|
||||
}
|
||||
formData := new(ReportSummaryFormData)
|
||||
c.BindJSON(formData)
|
||||
originSummary, err := service.ReportService.RetreiveReportSummary(requestReportId)
|
||||
if err != nil {
|
||||
result.NotFound(err.Error())
|
||||
return
|
||||
}
|
||||
copier.Copy(originSummary, formData)
|
||||
err = service.ReportService.UpdateReportSummary(originSummary)
|
||||
if err != nil {
|
||||
result.Error(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
result.Updated("指定电费公示报表中的园区概况基本数据已经完成更新。")
|
||||
}
|
||||
|
||||
func testCalculateReportSummary(c *gin.Context) {
|
||||
result := response.NewResult(c)
|
||||
requestReportId := c.Param("rid")
|
||||
if !ensureReportBelongs(c, result, requestReportId) {
|
||||
return
|
||||
}
|
||||
summary, err := service.ReportService.RetreiveReportSummary(requestReportId)
|
||||
if err != nil {
|
||||
result.NotFound(err.Error())
|
||||
return
|
||||
}
|
||||
summary.CalculatePrices()
|
||||
calcResults := tools.ConvertStructToMap(summary)
|
||||
result.Json(http.StatusOK, "已完成园区概况的试计算。", gin.H{"result": lo.PickByKeys(calcResults, []string{"overallPrice", "criticalPrice", "peakPrice", "flat", "flatFee", "flatPrice", "valleyPrice"})})
|
||||
}
|
||||
|
||||
func progressReportSummary(c *gin.Context) {
|
||||
result := response.NewResult(c)
|
||||
requestReportId := c.Param("rid")
|
||||
if !ensureReportBelongs(c, result, requestReportId) {
|
||||
return
|
||||
}
|
||||
err := service.ReportService.CalculateSummaryAndFinishStep(requestReportId)
|
||||
if err != nil {
|
||||
if nfErr, ok := err.(exceptions.NotFoundError); ok {
|
||||
result.NotFound(nfErr.Error())
|
||||
} else {
|
||||
result.Error(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
return
|
||||
}
|
||||
result.Success("已经完成园区概况的计算,并可以进行到下一步骤。")
|
||||
}
|
||||
|
||||
func fetchWillDilutedFees(c *gin.Context) {
|
||||
result := response.NewResult(c)
|
||||
requestReportId := c.Param("rid")
|
||||
if !ensureReportBelongs(c, result, requestReportId) {
|
||||
return
|
||||
}
|
||||
fees, err := service.ReportService.FetchWillDulutedMaintenanceFees(requestReportId)
|
||||
if err != nil {
|
||||
result.NotFound(err.Error())
|
||||
return
|
||||
}
|
||||
result.Json(http.StatusOK, "待摊薄费用已经获取到。", gin.H{"fees": fees})
|
||||
}
|
||||
|
||||
type DilutedFeeCreationFormData struct {
|
||||
ParkId string `json:"parkId" form:"parkId"`
|
||||
Name string `json:"name" form:"name"`
|
||||
Fee decimal.Decimal `json:"fee" form:"fee"`
|
||||
Memo *string `json:"memo" form:"memo"`
|
||||
}
|
||||
|
||||
func createTemporaryWillDilutedFee(c *gin.Context) {
|
||||
result := response.NewResult(c)
|
||||
requestReportId := c.Param("rid")
|
||||
if !ensureReportBelongs(c, result, requestReportId) {
|
||||
return
|
||||
}
|
||||
formData := new(DilutedFeeCreationFormData)
|
||||
c.BindJSON(formData)
|
||||
report, err := service.ReportService.RetreiveReportIndex(requestReportId)
|
||||
if err != nil {
|
||||
result.NotFound(err.Error())
|
||||
return
|
||||
}
|
||||
if formData.ParkId != report.ParkId {
|
||||
result.NotAccept("选择的园区与公示报表所属的园区不一致。")
|
||||
return
|
||||
}
|
||||
newWillDilutedFee := new(model.WillDilutedFee)
|
||||
copier.Copy(newWillDilutedFee, formData)
|
||||
newWillDilutedFee.ReportId = report.Id
|
||||
err = service.ReportService.CreateTemporaryWillDilutedMaintenanceFee(*newWillDilutedFee)
|
||||
if err != nil {
|
||||
result.Error(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
result.Created("公示报表中所要使用的临时待摊薄费用已添加。")
|
||||
}
|
||||
|
||||
func importPredefinedMaintenanceFees(c *gin.Context) {
|
||||
result := response.NewResult(c)
|
||||
requestReportId := c.Param("rid")
|
||||
if !ensureReportBelongs(c, result, requestReportId) {
|
||||
return
|
||||
}
|
||||
report, err := service.ReportService.RetreiveReportIndex(requestReportId)
|
||||
if err != nil {
|
||||
result.NotFound(err.Error())
|
||||
return
|
||||
}
|
||||
maintenanceFees, err := service.MaintenanceFeeService.ListMaintenanceFees([]string{report.ParkId})
|
||||
if err != nil {
|
||||
result.Error(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
log.Printf("[cotroller] [debug] fees: %+v", maintenanceFees)
|
||||
enabledMaintenanceFees := lo.Filter(
|
||||
maintenanceFees,
|
||||
func(elem model.MaintenanceFee, index int) bool {
|
||||
return elem.Enabled
|
||||
},
|
||||
)
|
||||
log.Printf("[cotroller] [debug] fees: %+v", enabledMaintenanceFees)
|
||||
if len(enabledMaintenanceFees) == 0 {
|
||||
result.NotFound("没有找到可供导入的配电维护费记录。")
|
||||
return
|
||||
}
|
||||
dilutedFees := lo.Map(
|
||||
enabledMaintenanceFees,
|
||||
func(elem model.MaintenanceFee, index int) model.WillDilutedFee {
|
||||
fee := &model.WillDilutedFee{
|
||||
Id: utils.UUIDString(),
|
||||
ReportId: report.Id,
|
||||
SourceId: lo.ToPtr(elem.Id),
|
||||
Name: elem.Name,
|
||||
Fee: elem.Fee,
|
||||
Memo: elem.Memo,
|
||||
}
|
||||
return *fee
|
||||
},
|
||||
)
|
||||
err = service.ReportService.BatchSaveMaintenanceFee(report.Id, dilutedFees)
|
||||
if err != nil {
|
||||
result.Error(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
result.Created("预定义的配电维护费已经导入。")
|
||||
}
|
||||
|
||||
type DilutedFeeModificationFormData struct {
|
||||
Name *string `json:"name,omitempty" form:"name"`
|
||||
Fee decimal.Decimal `json:"fee" form:"fee"`
|
||||
Memo *string `json:"memo,omitempty" form:"memo"`
|
||||
}
|
||||
|
||||
func modifyWillDilutedFee(c *gin.Context) {
|
||||
result := response.NewResult(c)
|
||||
requestReportId := c.Param("rid")
|
||||
if !ensureReportBelongs(c, result, requestReportId) {
|
||||
return
|
||||
}
|
||||
requestFeeId := c.Param("mid")
|
||||
formData := new(DilutedFeeModificationFormData)
|
||||
c.BindJSON(formData)
|
||||
updateValues := tools.ConvertStructToMap(formData)
|
||||
err := service.ReportService.UpdateMaintenanceFee(requestFeeId, updateValues)
|
||||
if err != nil {
|
||||
result.Error(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
result.Updated("指定待摊薄费用信息已经更新。")
|
||||
}
|
||||
|
||||
func deleteTemporaryWillDilutedFee(c *gin.Context) {
|
||||
result := response.NewResult(c)
|
||||
requestReportId := c.Param("rid")
|
||||
if !ensureReportBelongs(c, result, requestReportId) {
|
||||
return
|
||||
}
|
||||
requestFeeId := c.Param("mid")
|
||||
err := service.ReportService.DeleteWillDilutedFee(requestFeeId)
|
||||
if err != nil {
|
||||
result.Error(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
result.Deleted("指定待摊薄费用信息已经删除。")
|
||||
}
|
||||
|
||||
func progressReportWillDilutedFee(c *gin.Context) {
|
||||
result := response.NewResult(c)
|
||||
requestReportId := c.Param("rid")
|
||||
if !ensureReportBelongs(c, result, requestReportId) {
|
||||
return
|
||||
}
|
||||
report, err := service.ReportService.RetreiveReportIndex(requestReportId)
|
||||
if err != nil {
|
||||
result.NotFound(err.Error())
|
||||
return
|
||||
}
|
||||
err = service.ReportService.ProgressReportWillDilutedFee(*report)
|
||||
if err != nil {
|
||||
result.Error(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
result.Success("待摊薄费用编辑步骤已经完成。")
|
||||
}
|
||||
|
||||
func progressEndUserRegister(c *gin.Context) {
|
||||
result := response.NewResult(c)
|
||||
requestReportId := c.Param("rid")
|
||||
if !ensureReportBelongs(c, result, requestReportId) {
|
||||
return
|
||||
}
|
||||
report, err := service.ReportService.RetreiveReportIndex(requestReportId)
|
||||
if err != nil {
|
||||
result.NotFound(err.Error())
|
||||
return
|
||||
}
|
||||
err = service.ReportService.ProgressReportRegisterEndUser(*report)
|
||||
if err != nil {
|
||||
result.Error(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
result.Success("终端用户抄表编辑步骤已经完成。")
|
||||
}
|
||||
|
||||
func publishReport(c *gin.Context) {
|
||||
result := response.NewResult(c)
|
||||
requestReportId := c.Param("rid")
|
||||
if !ensureReportBelongs(c, result, requestReportId) {
|
||||
return
|
||||
}
|
||||
report, err := service.ReportService.RetreiveReportIndex(requestReportId)
|
||||
if err != nil {
|
||||
result.NotFound(err.Error())
|
||||
return
|
||||
}
|
||||
err = service.ReportService.PublishReport(*report)
|
||||
if err != nil {
|
||||
result.Error(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
result.Success("指定的公示报表已经发布。")
|
||||
}
|
||||
|
||||
func searchReports(c *gin.Context) {
|
||||
// 获取当前登录用户下所有园区的尚未发布的核算报表索引
|
||||
func listDraftReportIndicies(c *fiber.Ctx) error {
|
||||
result := response.NewResult(c)
|
||||
session, err := _retreiveSession(c)
|
||||
if err != nil {
|
||||
result.Unauthorized(err.Error())
|
||||
return
|
||||
reportLog.Error("无法获取当前用户的会话信息", zap.Error(err))
|
||||
return result.Unauthorized("无法获取当前用户的会话信息。")
|
||||
}
|
||||
requestUser := lo.
|
||||
If(session.Type == model.USER_TYPE_ENT, session.Uid).
|
||||
Else(c.DefaultQuery("user", ""))
|
||||
requestPark := c.DefaultQuery("park", "")
|
||||
if len(requestPark) > 0 && session.Type == model.USER_TYPE_ENT {
|
||||
if !ensureParkBelongs(c, result, requestPark) {
|
||||
return
|
||||
}
|
||||
}
|
||||
requestPeriodString := c.DefaultQuery("period", "")
|
||||
var requestPeriod *time.Time = nil
|
||||
if len(requestPeriodString) > 0 {
|
||||
parsedPeriod, err := time.Parse("2006-01", requestPeriodString)
|
||||
if err != nil {
|
||||
result.NotAccept("参数[period]的格式不正确。")
|
||||
return
|
||||
}
|
||||
requestPeriod = lo.ToPtr(parsedPeriod)
|
||||
}
|
||||
requestKeyword := c.DefaultQuery("keyword", "")
|
||||
requestPage, err := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||
reportLog.Info("检索指定用户下的未发布核算报表索引", zap.String("User", session.Uid))
|
||||
indicies, err := service.ReportService.ListDraftReportIndicies(session.Uid)
|
||||
if err != nil {
|
||||
result.NotAccept("查询参数[page]格式不正确。")
|
||||
return
|
||||
reportLog.Error("无法获取当前用户的核算报表索引", zap.Error(err))
|
||||
return result.NotFound("当前用户下未找到核算报表索引。")
|
||||
}
|
||||
records, totalItems, err := service.ReportService.SearchReport(requestUser, requestPark, requestKeyword, requestPeriod, requestPage)
|
||||
if err != nil {
|
||||
result.NotFound(err.Error())
|
||||
return
|
||||
}
|
||||
result.Success(
|
||||
"已经取得符合条件的公示报表记录。",
|
||||
response.NewPagedResponse(requestPage, totalItems).ToMap(),
|
||||
gin.H{"reports": records},
|
||||
return result.Success(
|
||||
"已经获取到指定用户的报表索引。",
|
||||
fiber.Map{"reports": indicies},
|
||||
)
|
||||
}
|
||||
|
||||
func fetchReportPublicity(c *gin.Context) {
|
||||
// 初始化一个新的核算任务
|
||||
func initNewReportCalculateTask(c *fiber.Ctx) error {
|
||||
result := response.NewResult(c)
|
||||
requestReportId := c.Param("rid")
|
||||
publicity, err := service.ReportService.AssembleReportPublicity(requestReportId)
|
||||
session, err := _retreiveSession(c)
|
||||
if err != nil {
|
||||
if nfErr, ok := err.(exceptions.NotFoundError); ok {
|
||||
result.NotFound(nfErr.Error())
|
||||
return
|
||||
} else {
|
||||
result.Error(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
reportLog.Error("无法获取当前用户的会话信息", zap.Error(err))
|
||||
return result.Unauthorized("无法获取当前用户的会话信息。")
|
||||
}
|
||||
result.Success("已经取得指定公示报表的发布版本。", tools.ConvertStructToMap(publicity))
|
||||
reportLog.Info("初始化指定用户的一个新核算任务", zap.String("User", session.Uid))
|
||||
var form vo.ReportCreationForm
|
||||
if err := c.BodyParser(&form); err != nil {
|
||||
reportLog.Error("无法解析创建核算报表的请求数据。", zap.Error(err))
|
||||
return result.BadRequest("无法解析创建核算报表的请求数据。")
|
||||
}
|
||||
if pass, err := checkParkBelongs(form.Park, reportLog, c, &result); !pass {
|
||||
return err
|
||||
}
|
||||
ok, err := repository.ReportRepository.CreateReport(&form)
|
||||
if err != nil {
|
||||
reportLog.Error("无法创建核算报表", zap.Error(err))
|
||||
return result.Error(fiber.StatusInternalServerError, "无法创建核算报表。")
|
||||
}
|
||||
if !ok {
|
||||
reportLog.Error("未能完成核算报表的保存。")
|
||||
return result.NotAccept("未能完成核算报表的保存。")
|
||||
}
|
||||
return result.Success("已经成功创建核算报表。")
|
||||
}
|
||||
|
||||
func calculateReport(c *gin.Context) {
|
||||
// 更新指定的核算任务
|
||||
func updateReportCalculateTask(c *fiber.Ctx) error {
|
||||
result := response.NewResult(c)
|
||||
requestReportId := c.Param("rid")
|
||||
if !ensureReportBelongs(c, result, requestReportId) {
|
||||
return
|
||||
reportId := c.Params("rid")
|
||||
if pass, err := checkReportBelongs(reportId, reportLog, c, &result); !pass {
|
||||
return err
|
||||
}
|
||||
err := service.CalculateService.ComprehensivelyCalculateReport(requestReportId)
|
||||
var form vo.ReportModifyForm
|
||||
if err := c.BodyParser(&form); err != nil {
|
||||
reportLog.Error("无法解析更新核算报表的请求数据。", zap.Error(err))
|
||||
return result.BadRequest("无法解析更新核算报表的请求数据。")
|
||||
}
|
||||
ok, err := repository.ReportRepository.UpdateReportSummary(reportId, &form)
|
||||
if err != nil {
|
||||
result.Error(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
reportLog.Error("无法更新核算报表", zap.Error(err))
|
||||
return result.Error(fiber.StatusInternalServerError, "无法更新核算报表。")
|
||||
}
|
||||
result.Success("指定公示报表中的数据已经计算完毕。")
|
||||
if !ok {
|
||||
reportLog.Error("未能完成核算报表的更新。")
|
||||
return result.NotAccept("未能完成核算报表的更新。")
|
||||
}
|
||||
return result.Success("已经成功更新核算报表。")
|
||||
}
|
||||
|
||||
// 启动指定的核算任务
|
||||
func initiateCalculateTask(c *fiber.Ctx) error {
|
||||
result := response.NewResult(c)
|
||||
reportId := c.Params("rid")
|
||||
if pass, err := checkReportBelongs(reportId, reportLog, c, &result); !pass {
|
||||
return err
|
||||
}
|
||||
err := service.ReportService.DispatchReportCalculate(reportId)
|
||||
if err != nil {
|
||||
reportLog.Error("无法启动核算报表计算任务", zap.Error(err))
|
||||
return result.Error(fiber.StatusInternalServerError, "无法启动核算报表计算任务。")
|
||||
}
|
||||
return result.Success("已经成功启动核算报表计算任务。")
|
||||
}
|
||||
|
||||
// 获取自己园区的已经填写的园区电量信息
|
||||
func getParkFilledSummary(c *fiber.Ctx) error {
|
||||
result := response.NewResult(c)
|
||||
reportId := c.Params("rid")
|
||||
if pass, err := checkReportBelongs(reportId, reportLog, c, &result); !pass {
|
||||
return err
|
||||
}
|
||||
reportLog.Info("获取园区电量信息", zap.String("Report", reportId))
|
||||
summary, err := repository.ReportRepository.RetrieveReportSummary(reportId)
|
||||
if err != nil {
|
||||
reportLog.Error("无法获取核算报表的园区电量信息", zap.Error(err))
|
||||
return result.Error(fiber.StatusInternalServerError, "无法获取核算报表的园区电量信息。")
|
||||
}
|
||||
if summary == nil {
|
||||
reportLog.Error("未找到核算报表的园区电量信息")
|
||||
return result.NotFound("未找到核算报表的园区电量信息。")
|
||||
}
|
||||
var summaryResponse vo.SimplifiedReportSummary
|
||||
copier.Copy(&summaryResponse, summary)
|
||||
return result.Success(
|
||||
"已经获取到核算报表的园区电量信息。",
|
||||
fiber.Map{"summary": summaryResponse},
|
||||
)
|
||||
}
|
||||
|
||||
// 对提供的园区电量信息进行试计算,返回试计算结果
|
||||
func testCalculateReportSummary(c *fiber.Ctx) error {
|
||||
result := response.NewResult(c)
|
||||
reportLog.Info("试计算园区电量信息")
|
||||
var form vo.TestCalculateForm
|
||||
if err := c.BodyParser(&form); err != nil {
|
||||
reportLog.Error("无法解析试计算核算报表的请求数据。", zap.Error(err))
|
||||
return result.BadRequest("无法解析试计算核算报表的请求数据。")
|
||||
}
|
||||
return result.Success(
|
||||
"电量电费试计算已经完成。",
|
||||
fiber.Map{"summary": form.Calculate()},
|
||||
)
|
||||
}
|
||||
|
||||
// 获取指定园区中尚未发布的核算报表计算状态
|
||||
func listCalculateTaskStatus(c *fiber.Ctx) error {
|
||||
result := response.NewResult(c)
|
||||
session, err := _retreiveSession(c)
|
||||
if err != nil {
|
||||
reportLog.Error("无法获取当前用户的会话信息", zap.Error(err))
|
||||
return result.Unauthorized("无法获取当前用户的会话信息。")
|
||||
}
|
||||
status, err := repository.ReportRepository.GetReportTaskStatus(session.Uid)
|
||||
if err != nil {
|
||||
reportLog.Error("无法获取核算报表计算状态", zap.Error(err))
|
||||
return result.Error(fiber.StatusInternalServerError, "无法获取核算报表计算状态。")
|
||||
}
|
||||
statusResponse := make([]*vo.ReportCalculateTaskStatusResponse, 0)
|
||||
copier.Copy(&statusResponse, &status)
|
||||
return result.Success(
|
||||
"已经获取到核算报表计算状态。",
|
||||
fiber.Map{"status": statusResponse},
|
||||
)
|
||||
}
|
||||
|
||||
// 获取指定报表的详细信息
|
||||
func getReportDetail(c *fiber.Ctx) error {
|
||||
result := response.NewResult(c)
|
||||
reportId := c.Params("rid")
|
||||
reportLog.Info("获取核算报表的详细信息", zap.String("Report", reportId))
|
||||
user, park, report, err := service.ReportService.RetrieveReportIndexDetail(reportId)
|
||||
if err != nil {
|
||||
reportLog.Error("无法获取核算报表的详细信息", zap.Error(err))
|
||||
return result.NotFound("无法获取核算报表的详细信息。")
|
||||
}
|
||||
return result.Success(
|
||||
"已经获取到核算报表的详细信息。",
|
||||
fiber.Map{
|
||||
"detail": vo.NewReportDetailQueryResponse(user, park, report),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// 获取指定核算报表的总览信息
|
||||
func getReportSummary(c *fiber.Ctx) error {
|
||||
result := response.NewResult(c)
|
||||
reportId := c.Params("rid")
|
||||
report, err := repository.ReportRepository.RetrieveReportSummary(reportId)
|
||||
if err != nil {
|
||||
reportLog.Error("无法获取核算报表的总览信息", zap.Error(err))
|
||||
return result.Error(fiber.StatusInternalServerError, "无法获取核算报表的总览信息。")
|
||||
}
|
||||
if report == nil {
|
||||
reportLog.Error("未找到核算报表的总览信息")
|
||||
return result.NotFound("未找到核算报表的总览信息。")
|
||||
}
|
||||
var summaryResponse vo.ParkSummaryResponse
|
||||
copier.Copy(&summaryResponse, report)
|
||||
return result.Success(
|
||||
"已经获取到核算报表的总览信息。",
|
||||
fiber.Map{"summary": summaryResponse},
|
||||
)
|
||||
}
|
||||
|
||||
// 获取指定报表中分页的公共表计的核算摘要信息
|
||||
func listPublicMetersInReport(c *fiber.Ctx) error {
|
||||
result := response.NewResult(c)
|
||||
reportId := c.Params("rid")
|
||||
reportLog.Info("获取核算报表中的公共表计信息", zap.String("Report", reportId))
|
||||
page := c.QueryInt("page", 1)
|
||||
keyword := tools.EmptyToNil(c.Query("keyword"))
|
||||
meters, total, err := repository.ReportRepository.ListPublicMetersInReport(reportId, uint(page), keyword)
|
||||
if err != nil {
|
||||
reportLog.Error("无法获取核算报表中的公共表计信息", zap.Error(err))
|
||||
return result.Error(fiber.StatusInternalServerError, "无法获取核算报表中的公共表计信息。")
|
||||
}
|
||||
meterResponse := lo.Map(meters, func(meter *model.ReportDetailedPublicConsumption, _ int) *vo.ReportPublicQueryResponse {
|
||||
m := &vo.ReportPublicQueryResponse{}
|
||||
m.FromReportDetailPublicConsumption(meter)
|
||||
return m
|
||||
})
|
||||
return result.Success(
|
||||
"已经获取到指定核算报表中的分页公共表计的核算信息。",
|
||||
response.NewPagedResponse(page, total).ToMap(),
|
||||
fiber.Map{"public": meterResponse},
|
||||
)
|
||||
}
|
||||
|
||||
// 获取指定报表中的分页的公摊表计的核算摘要信息
|
||||
func listPooledMetersInReport(c *fiber.Ctx) error {
|
||||
result := response.NewResult(c)
|
||||
reportId := c.Params("rid")
|
||||
reportLog.Info("获取核算报表中的公摊表计信息", zap.String("Report", reportId))
|
||||
page := c.QueryInt("page", 1)
|
||||
keyword := tools.EmptyToNil(c.Query("keyword"))
|
||||
meters, total, err := repository.ReportRepository.ListPooledMetersInReport(reportId, uint(page), keyword)
|
||||
if err != nil {
|
||||
reportLog.Error("无法获取核算报表中的公摊表计信息", zap.Error(err))
|
||||
return result.Error(fiber.StatusInternalServerError, "无法获取核算报表中的公摊表计信息。")
|
||||
}
|
||||
meterResponse := lo.Map(meters, func(meter *model.ReportDetailedPooledConsumption, _ int) *vo.ReportPooledQueryResponse {
|
||||
m := &vo.ReportPooledQueryResponse{}
|
||||
m.FromReportDetailPooledConsumption(meter)
|
||||
return m
|
||||
})
|
||||
return result.Success(
|
||||
"已经获取到指定核算报表中的分页公摊表计的核算信息。",
|
||||
response.NewPagedResponse(page, total).ToMap(),
|
||||
fiber.Map{"pooled": meterResponse},
|
||||
)
|
||||
}
|
||||
|
||||
// 列出指定报表中指定公共表计下各个分摊表计的消耗数据
|
||||
func listSubmetersInPooledMeter(c *fiber.Ctx) error {
|
||||
result := response.NewResult(c)
|
||||
reportId := c.Params("rid")
|
||||
meterId := c.Params("code")
|
||||
if len(meterId) == 0 {
|
||||
reportLog.Error("未提供公共表计的编号")
|
||||
return result.BadRequest("未提供公共表计的编号。")
|
||||
}
|
||||
meters, err := repository.ReportRepository.ListPooledMeterDetailInReport(reportId, meterId)
|
||||
if err != nil {
|
||||
reportLog.Error("无法获取核算报表中的公共表计信息", zap.Error(err))
|
||||
return result.Error(fiber.StatusInternalServerError, "无法获取核算报表中的公共表计信息。")
|
||||
}
|
||||
meterResponse := lo.Map(meters, func(meter *model.ReportDetailNestedMeterConsumption, _ int) *vo.ReportPooledQueryResponse {
|
||||
m := &vo.ReportPooledQueryResponse{}
|
||||
m.FromReportDetailNestedMeterConsumption(meter)
|
||||
return m
|
||||
})
|
||||
return result.Success(
|
||||
"已经获取到指定核算报表中的公共表计的核算信息。",
|
||||
fiber.Map{"meters": meterResponse},
|
||||
)
|
||||
}
|
||||
|
||||
// 获取指定报表中分页的商户核算电量电费概要数据
|
||||
func listTenementsInReport(c *fiber.Ctx) error {
|
||||
result := response.NewResult(c)
|
||||
reportId := c.Params("rid")
|
||||
page := c.QueryInt("page", 1)
|
||||
keyword := tools.EmptyToNil(c.Query("keyword"))
|
||||
tenements, total, err := repository.ReportRepository.ListTenementInReport(reportId, uint(page), keyword)
|
||||
if err != nil {
|
||||
reportLog.Error("无法获取核算报表中的商户信息", zap.Error(err))
|
||||
return result.Error(fiber.StatusInternalServerError, "无法获取核算报表中的商户信息。")
|
||||
}
|
||||
tenementsResponse := lo.Map(tenements, func(tenement *model.ReportTenement, _ int) *vo.ReportTenementSummaryResponse {
|
||||
t := &vo.ReportTenementSummaryResponse{}
|
||||
t.FromReportTenement(tenement)
|
||||
return t
|
||||
})
|
||||
return result.Success(
|
||||
"已经获取到指定核算报表中的分页商户的核算信息。",
|
||||
response.NewPagedResponse(page, total).ToMap(),
|
||||
fiber.Map{"tenements": tenementsResponse},
|
||||
)
|
||||
}
|
||||
|
||||
// 获取指定报表中指定商户的详细核算信息
|
||||
func getTenementDetailInReport(c *fiber.Ctx) error {
|
||||
result := response.NewResult(c)
|
||||
reportId := c.Params("rid")
|
||||
tenementId := c.Params("tid")
|
||||
detail, err := repository.ReportRepository.GetTenementDetailInReport(reportId, tenementId)
|
||||
if err != nil {
|
||||
reportLog.Error("无法获取核算报表中的商户信息", zap.Error(err))
|
||||
return result.Error(fiber.StatusInternalServerError, "无法获取核算报表中的商户信息。")
|
||||
}
|
||||
var detailResponse vo.ReportTenementDetailResponse
|
||||
detailResponse.FromReportTenement(detail)
|
||||
return result.Success(
|
||||
"已经获取到指定核算报表中的商户的详细核算信息。",
|
||||
fiber.Map{"detail": detailResponse},
|
||||
)
|
||||
}
|
||||
|
||||
// 发布指定的核算报表
|
||||
func publishReport(c *fiber.Ctx) error {
|
||||
result := response.NewResult(c)
|
||||
reportId := c.Params("rid")
|
||||
if pass, err := checkReportBelongs(reportId, reportLog, c, &result); !pass {
|
||||
return err
|
||||
}
|
||||
ok, err := repository.ReportRepository.PublishReport(reportId)
|
||||
if err != nil {
|
||||
reportLog.Error("无法发布核算报表", zap.Error(err))
|
||||
return result.Error(fiber.StatusInternalServerError, "发布核算报表出错。")
|
||||
}
|
||||
if !ok {
|
||||
reportLog.Error("未能完成核算报表的发布。")
|
||||
return result.NotAccept("未能完成核算报表的发布。")
|
||||
}
|
||||
return result.Success("已经成功发布核算报表。")
|
||||
}
|
||||
|
||||
// 对核算报表进行综合检索
|
||||
func reportComprehensiveSearch(c *fiber.Ctx) error {
|
||||
result := response.NewResult(c)
|
||||
user := tools.EmptyToNil(c.Query("user"))
|
||||
session, err := _retreiveSession(c)
|
||||
if err != nil {
|
||||
reportLog.Error("无法获取当前用户的会话信息", zap.Error(err))
|
||||
return result.Unauthorized("无法获取当前用户的会话信息。")
|
||||
}
|
||||
park := tools.EmptyToNil(c.Query("park_id"))
|
||||
if session.Type == model.USER_TYPE_ENT && park != nil && len(*park) > 0 {
|
||||
if pass, err := checkParkBelongs(*park, reportLog, c, &result); !pass {
|
||||
return err
|
||||
}
|
||||
}
|
||||
var requestUser *string
|
||||
if session.Type == model.USER_TYPE_ENT {
|
||||
requestUser = lo.ToPtr(tools.DefaultTo(user, session.Uid))
|
||||
} else {
|
||||
requestUser = user
|
||||
}
|
||||
page := c.QueryInt("page", 1)
|
||||
keyword := tools.EmptyToNil(c.Query("keyword"))
|
||||
startDate, err := types.ParseDatep(c.Query("period_start"))
|
||||
if err != nil {
|
||||
reportLog.Error("无法解析核算报表查询的开始日期", zap.Error(err))
|
||||
return result.BadRequest("无法解析核算报表查询的开始日期。")
|
||||
}
|
||||
endDate, err := types.ParseDatep(c.Query("period_end"))
|
||||
if err != nil {
|
||||
reportLog.Error("无法解析核算报表查询的结束日期", zap.Error(err))
|
||||
return result.BadRequest("无法解析核算报表查询的结束日期。")
|
||||
}
|
||||
reports, total, err := service.ReportService.QueryReports(requestUser, park, uint(page), keyword, startDate, endDate)
|
||||
if err != nil {
|
||||
reportLog.Error("无法查询核算报表", zap.Error(err))
|
||||
return result.Error(fiber.StatusInternalServerError, "无法查询核算报表。")
|
||||
}
|
||||
return result.Success(
|
||||
"已经获取到指定核算报表的分页信息。",
|
||||
response.NewPagedResponse(page, total).ToMap(),
|
||||
fiber.Map{"reports": reports},
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,40 +1,43 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"electricity_bill_calc/logger"
|
||||
"electricity_bill_calc/model"
|
||||
"electricity_bill_calc/response"
|
||||
"electricity_bill_calc/security"
|
||||
"electricity_bill_calc/service"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"go.uber.org/zap"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func InitializeStatisticsController(router *gin.Engine) {
|
||||
router.GET("/audits", security.OPSAuthorize, currentAuditAmount)
|
||||
router.GET("/stat/reports", security.MustAuthenticated, statReports)
|
||||
var StatisticsWithdrawLog = logger.Named("Handler", "StatisticsWithdraw")
|
||||
|
||||
func InitializeStatisticsController(router *fiber.App) {
|
||||
router.Get("/audits", security.OPSAuthorize, currentAuditAmount)
|
||||
router.Get("/stat/reports", security.OPSAuthorize, statReports)
|
||||
|
||||
}
|
||||
|
||||
func currentAuditAmount(c *gin.Context) {
|
||||
//获取当前系统中待审核的内容数量
|
||||
func currentAuditAmount(c *fiber.Ctx) error {
|
||||
StatisticsWithdrawLog.Info("开始获取当前系统中待审核的内容数量")
|
||||
result := response.NewResult(c)
|
||||
amount, err := service.WithdrawService.AuditWaits()
|
||||
if err != nil {
|
||||
result.Error(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
StatisticsWithdrawLog.Error("获取当前系统中待审核的内容数量出错", zap.Error(err))
|
||||
return result.Error(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
result.Json(http.StatusOK, "已经获取到指定的统计信息。", gin.H{
|
||||
"amounts": map[string]int64{
|
||||
"withdraw": amount,
|
||||
},
|
||||
})
|
||||
|
||||
return result.Success("已经获取到指定的统计信息",
|
||||
fiber.Map{"withdraw": amount})
|
||||
}
|
||||
|
||||
func statReports(c *gin.Context) {
|
||||
func statReports(c *fiber.Ctx) error {
|
||||
result := response.NewResult(c)
|
||||
session, err := _retreiveSession(c)
|
||||
if err != nil {
|
||||
result.Unauthorized(err.Error())
|
||||
return
|
||||
return result.Unauthorized(err.Error())
|
||||
}
|
||||
var (
|
||||
enterprises int64 = 0
|
||||
@@ -44,34 +47,36 @@ func statReports(c *gin.Context) {
|
||||
if session.Type != 0 {
|
||||
enterprises, err = service.StatisticsService.EnabledEnterprises()
|
||||
if err != nil {
|
||||
result.Error(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
StatisticsWithdrawLog.Error(err.Error())
|
||||
return result.Error(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
parks, err = service.StatisticsService.EnabledParks()
|
||||
if err != nil {
|
||||
result.Error(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
StatisticsWithdrawLog.Error(err.Error())
|
||||
return result.Error(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
reports, err = service.StatisticsService.ParksNewestState()
|
||||
//TODO: 2023.07.26 报表数据库结构改变,此处逻辑复杂放在最后处理
|
||||
reports, err = service.StatisticsService.ParkNewestState()
|
||||
if err != nil {
|
||||
result.Error(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
StatisticsWithdrawLog.Error(err.Error())
|
||||
return result.Error(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
} else {
|
||||
parks, err = service.StatisticsService.EnabledParks(session.Uid)
|
||||
if err != nil {
|
||||
result.Error(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
StatisticsWithdrawLog.Error(err.Error())
|
||||
return result.Error(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
reports, err = service.StatisticsService.ParksNewestState(session.Uid)
|
||||
//TODO: 2023.07.26 报表数据库结构改变,此处逻辑复杂放在最后处理
|
||||
reports, err = service.StatisticsService.ParkNewestState(session.Uid)
|
||||
if err != nil {
|
||||
result.Error(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
StatisticsWithdrawLog.Error(err.Error())
|
||||
return result.Error(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
result.Json(http.StatusOK, "已经完成园区报告的统计。", gin.H{
|
||||
"statistics": gin.H{
|
||||
return result.Success("已经完成园区报告的统计。", fiber.Map{
|
||||
"statistics": fiber.Map{
|
||||
"enterprises": enterprises,
|
||||
"parks": parks,
|
||||
"reports": reports,
|
||||
|
||||
288
controller/tenement.go
Normal file
288
controller/tenement.go
Normal file
@@ -0,0 +1,288 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"electricity_bill_calc/logger"
|
||||
"electricity_bill_calc/repository"
|
||||
"electricity_bill_calc/response"
|
||||
"electricity_bill_calc/security"
|
||||
"electricity_bill_calc/service"
|
||||
"electricity_bill_calc/tools"
|
||||
"electricity_bill_calc/types"
|
||||
"electricity_bill_calc/vo"
|
||||
"fmt"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/samber/lo"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
var tenementLog = logger.Named("Handler", "Tenement")
|
||||
|
||||
func InitializeTenementHandler(router *fiber.App) {
|
||||
router.Get("/tenement/choice", security.EnterpriseAuthorize, listTenementForChoice)
|
||||
router.Get("/tenement/:pid", security.EnterpriseAuthorize, listTenement)
|
||||
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)
|
||||
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)
|
||||
}
|
||||
|
||||
// 列出园区中的商户
|
||||
func listTenement(c *fiber.Ctx) error {
|
||||
result := response.NewResult(c)
|
||||
parkId := c.Params("pid")
|
||||
tenementLog.Info("列出园区中的商户", zap.String("Park", parkId))
|
||||
if pass, err := checkParkBelongs(parkId, tenementLog, c, &result); !pass {
|
||||
return err
|
||||
}
|
||||
page := c.QueryInt("page", 1)
|
||||
keyword := tools.EmptyToNil(c.Query("keyword"))
|
||||
building := tools.EmptyToNil(c.Query("building"))
|
||||
startDate, err := types.ParseDatep(c.Query("startDate"))
|
||||
if err != nil {
|
||||
tenementLog.Error("列出园区中的商户失败,未能解析查询开始日期", zap.Error(err))
|
||||
return result.BadRequest(err.Error())
|
||||
}
|
||||
endDate, err := types.ParseDatep(c.Query("endDate"))
|
||||
if err != nil {
|
||||
tenementLog.Error("列出园区中的商户失败,未能解析查询结束日期", zap.Error(err))
|
||||
return result.BadRequest(err.Error())
|
||||
}
|
||||
state := c.QueryInt("state", 0)
|
||||
tenements, total, err := repository.TenementRepository.ListTenements(parkId, uint(page), keyword, building, startDate, endDate, state)
|
||||
if err != nil {
|
||||
tenementLog.Error("列出园区中的商户失败,未能获取商户列表", zap.Error(err))
|
||||
return result.Error(fiber.StatusInternalServerError, err.Error())
|
||||
}
|
||||
tenementsResponse := make([]*vo.TenementQueryResponse, 0)
|
||||
copier.Copy(&tenementsResponse, &tenements)
|
||||
return result.Success(
|
||||
"已经获取到要查询的商户。",
|
||||
response.NewPagedResponse(page, total).ToMap(),
|
||||
fiber.Map{
|
||||
"tenements": tenementsResponse,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// 列出指定商户下所有的表计
|
||||
func listMeters(c *fiber.Ctx) error {
|
||||
result := response.NewResult(c)
|
||||
parkId := c.Params("pid")
|
||||
if pass, err := checkParkBelongs(parkId, tenementLog, c, &result); !pass {
|
||||
return err
|
||||
}
|
||||
tenementId := c.Params("tid")
|
||||
tenementLog.Info("列出指定商户下所有的表计", zap.String("Park", parkId), zap.String("Tenement", tenementId))
|
||||
meters, err := service.TenementService.ListMeter(parkId, tenementId)
|
||||
if err != nil {
|
||||
tenementLog.Error("列出指定商户下所有的表计失败,未能获取表计列表", zap.Error(err))
|
||||
return result.Error(fiber.StatusInternalServerError, err.Error())
|
||||
}
|
||||
return result.Success(
|
||||
"已经获取到要查询的表计。",
|
||||
fiber.Map{
|
||||
"meters": meters,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// 增加一个新的商户
|
||||
func addTenement(c *fiber.Ctx) error {
|
||||
result := response.NewResult(c)
|
||||
parkId := c.Params("pid")
|
||||
if pass, err := checkParkBelongs(parkId, tenementLog, c, &result); !pass {
|
||||
return err
|
||||
}
|
||||
tenementLog.Info("增加一个新的商户", zap.String("Park", parkId))
|
||||
var form vo.TenementCreationForm
|
||||
if err := c.BodyParser(&form); err != nil {
|
||||
tenementLog.Error("增加一个新的商户失败,未能解析要添加的商户信息", zap.Error(err))
|
||||
return result.BadRequest(fmt.Sprintf("无法解析要添加的商户信息,%s", err.Error()))
|
||||
}
|
||||
err := service.TenementService.CreateTenementRecord(parkId, &form)
|
||||
if err != nil {
|
||||
tenementLog.Error("增加一个新的商户失败,未能添加商户记录", zap.Error(err))
|
||||
return result.NotAccept(fmt.Sprintf("无法添加商户记录,%s", err.Error()))
|
||||
}
|
||||
return result.Success("已经成功添加商户。")
|
||||
}
|
||||
|
||||
// 给指定商户绑定一个新的表计
|
||||
func bindMeterToTenement(c *fiber.Ctx) error {
|
||||
result := response.NewResult(c)
|
||||
parkId := c.Params("pid")
|
||||
if pass, err := checkParkBelongs(parkId, tenementLog, c, &result); !pass {
|
||||
return err
|
||||
}
|
||||
tenementId := c.Params("tid")
|
||||
if len(tenementId) == 0 {
|
||||
tenementLog.Error("给指定商户绑定一个新的表计失败,未指定商户。")
|
||||
return result.BadRequest("未指定商户。")
|
||||
}
|
||||
tenementLog.Info("向指定商户绑定一个表计。", zap.String("Park", parkId), zap.String("Tenement", tenementId))
|
||||
var form vo.MeterReadingFormWithCode
|
||||
if err := c.BodyParser(&form); err != nil {
|
||||
tenementLog.Error("给指定商户绑定一个新的表计失败,未能解析要绑定的表计信息", zap.Error(err))
|
||||
return result.BadRequest(fmt.Sprintf("无法解析要绑定的表计信息,%s", err.Error()))
|
||||
}
|
||||
if !form.MeterReadingForm.Validate() {
|
||||
tenementLog.Error("给指定商户绑定一个新的表计失败,表计读数不能正确配平,尖锋电量、峰电量、谷电量之和超过总电量。")
|
||||
return result.NotAccept("表计读数不能正确配平,尖锋电量、峰电量、谷电量之和超过总电量。")
|
||||
}
|
||||
err := service.TenementService.BindMeter(parkId, tenementId, form.Code, &form.MeterReadingForm)
|
||||
if err != nil {
|
||||
tenementLog.Error("给指定商户绑定一个新的表计失败,未能绑定表计", zap.Error(err))
|
||||
return result.NotAccept(fmt.Sprintf("无法绑定表计,%s", err.Error()))
|
||||
}
|
||||
return result.Success("已经成功绑定表计。")
|
||||
}
|
||||
|
||||
// 从指定商户下解除一个表计的绑定
|
||||
func unbindMeterFromTenement(c *fiber.Ctx) error {
|
||||
result := response.NewResult(c)
|
||||
parkId := c.Params("pid")
|
||||
if pass, err := checkParkBelongs(parkId, tenementLog, c, &result); !pass {
|
||||
return err
|
||||
}
|
||||
tenementId := c.Params("tid")
|
||||
if len(tenementId) == 0 {
|
||||
tenementLog.Error("从指定商户下解除一个表计的绑定失败,未指定商户。")
|
||||
return result.BadRequest("未指定商户。")
|
||||
}
|
||||
meterCode := c.Params("code")
|
||||
if len(meterCode) == 0 {
|
||||
tenementLog.Error("从指定商户下解除一个表计的绑定失败,未指定表计。")
|
||||
return result.BadRequest("未指定表计。")
|
||||
}
|
||||
tenementLog.Info("从指定商户处解绑一个表计。", zap.String("Park", parkId), zap.String("Tenement", tenementId), zap.String("Meter", meterCode))
|
||||
var form vo.MeterReadingForm
|
||||
if err := c.BodyParser(&form); err != nil {
|
||||
tenementLog.Error("从指定商户下解除一个表计的绑定失败,未能解析要解除绑定的表计抄表数据。", zap.Error(err))
|
||||
return result.BadRequest(fmt.Sprintf("无法解析要解除绑定的表计抄表数据,%s", err.Error()))
|
||||
}
|
||||
err := service.TenementService.UnbindMeter(parkId, tenementId, meterCode, &form)
|
||||
if err != nil {
|
||||
tenementLog.Error("从指定商户下解除一个表计的绑定失败,未能解除绑定表计。", zap.Error(err))
|
||||
return result.NotAccept(fmt.Sprintf("无法解除绑定表计,%s", err.Error()))
|
||||
}
|
||||
return result.Success("已经成功解除表计绑定。")
|
||||
}
|
||||
|
||||
// 修改指定商户的详细信息
|
||||
func updateTenement(c *fiber.Ctx) error {
|
||||
result := response.NewResult(c)
|
||||
parkId := c.Params("pid")
|
||||
if pass, err := checkParkBelongs(parkId, tenementLog, c, &result); !pass {
|
||||
return err
|
||||
}
|
||||
tenementId := c.Params("tid")
|
||||
if len(tenementId) == 0 {
|
||||
tenementLog.Error("修改指定商户的详细信息失败,未指定商户。")
|
||||
return result.BadRequest("未指定商户。")
|
||||
}
|
||||
tenementLog.Info("修改指定商户的详细信息。", zap.String("Park", parkId), zap.String("Tenement", tenementId))
|
||||
var form vo.TenementCreationForm
|
||||
if err := c.BodyParser(&form); err != nil {
|
||||
tenementLog.Error("修改指定商户的详细信息失败,未能解析要修改的商户信息", zap.Error(err))
|
||||
return result.BadRequest(fmt.Sprintf("无法解析要修改的商户信息,%s", err.Error()))
|
||||
}
|
||||
err := repository.TenementRepository.UpdateTenement(parkId, tenementId, &form)
|
||||
if err != nil {
|
||||
tenementLog.Error("修改指定商户的详细信息失败,未能修改商户信息", zap.Error(err))
|
||||
return result.NotAccept(fmt.Sprintf("无法修改商户信息,%s", err.Error()))
|
||||
}
|
||||
return result.Success("商户信息修改成功。")
|
||||
}
|
||||
|
||||
// 迁出指定园区中的商户
|
||||
func moveOutTenement(c *fiber.Ctx) error {
|
||||
result := response.NewResult(c)
|
||||
parkId := c.Params("pid")
|
||||
if pass, err := checkParkBelongs(parkId, tenementLog, c, &result); !pass {
|
||||
return err
|
||||
}
|
||||
tenementId := c.Params("tid")
|
||||
if len(tenementId) == 0 {
|
||||
tenementLog.Error("迁出指定园区中的商户失败,未指定商户。")
|
||||
return result.BadRequest("未指定商户。")
|
||||
}
|
||||
tenementLog.Info("迁出指定园区中的商户。", zap.String("Park", parkId), zap.String("Tenement", tenementId))
|
||||
var readings []*vo.MeterReadingFormWithCode
|
||||
if err := c.BodyParser(&readings); err != nil {
|
||||
tenementLog.Error("迁出指定园区中的商户失败,未能解析要迁出商户的抄表数据。", zap.Error(err))
|
||||
return result.BadRequest(fmt.Sprintf("无法解析要迁出商户的抄表数据,%s", err.Error()))
|
||||
}
|
||||
err := service.TenementService.MoveOutTenement(parkId, tenementId, readings)
|
||||
if err != nil {
|
||||
tenementLog.Error("迁出指定园区中的商户失败,未能迁出商户。", zap.Error(err))
|
||||
return result.NotAccept(fmt.Sprintf("无法迁出商户,%s", err.Error()))
|
||||
}
|
||||
return result.Success("商户迁出成功。")
|
||||
}
|
||||
|
||||
// 列出园区中的商户列表,主要用于下拉列表
|
||||
func listTenementForChoice(c *fiber.Ctx) error {
|
||||
result := response.NewResult(c)
|
||||
session, err := _retreiveSession(c)
|
||||
if err != nil {
|
||||
tenementLog.Error("列出园区中的商户列表失败,未能获取当前用户会话信息", zap.Error(err))
|
||||
return result.Unauthorized("未能获取当前用户会话信息。")
|
||||
}
|
||||
parkId := tools.EmptyToNil(c.Params("pid"))
|
||||
if parkId != nil && len(*parkId) > 0 {
|
||||
if pass, err := checkParkBelongs(*parkId, tenementLog, c, &result); !pass {
|
||||
return err
|
||||
}
|
||||
}
|
||||
tenementLog.Info("列出园区中的商户列表,主要用于下拉列表。", zap.String("Ent", session.Uid), zap.Stringp("Park", parkId))
|
||||
keyword := tools.EmptyToNil(c.Query("keyword"))
|
||||
limit := c.QueryInt("limit", 6)
|
||||
tenements, err := repository.TenementRepository.ListForSelect(session.Uid, parkId, keyword, lo.ToPtr(uint(limit)))
|
||||
if err != nil {
|
||||
tenementLog.Error("列出园区中的商户列表失败,未能获取商户列表", zap.Error(err))
|
||||
return result.NotFound(fmt.Sprintf("未能获取商户列表,%s", err.Error()))
|
||||
}
|
||||
var tenementsResponse []*vo.SimplifiedTenementResponse
|
||||
copier.Copy(&tenementsResponse, &tenements)
|
||||
return result.Success(
|
||||
"已经获取到要查询的商户。",
|
||||
fiber.Map{
|
||||
"tenements": tenementsResponse,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// 获取指定园区中指定商户的详细信息
|
||||
func getTenementDetail(c *fiber.Ctx) error {
|
||||
result := response.NewResult(c)
|
||||
parkId := c.Params("pid")
|
||||
if pass, err := checkParkBelongs(parkId, tenementLog, c, &result); !pass {
|
||||
return err
|
||||
}
|
||||
tenementId := c.Params("tid")
|
||||
if len(tenementId) == 0 {
|
||||
tenementLog.Error("获取指定园区中指定商户的详细信息失败,未指定商户。")
|
||||
return result.BadRequest("未指定商户。")
|
||||
}
|
||||
tenementLog.Info("获取指定园区中指定商户的详细信息。", zap.String("Park", parkId), zap.String("Tenement", tenementId))
|
||||
tenement, err := repository.TenementRepository.RetrieveTenementDetail(parkId, tenementId)
|
||||
if err != nil {
|
||||
tenementLog.Error("获取指定园区中指定商户的详细信息失败,未能获取商户信息", zap.Error(err))
|
||||
return result.NotFound(fmt.Sprintf("未能获取商户信息,%s", err.Error()))
|
||||
}
|
||||
var detail vo.TenementDetailResponse
|
||||
copier.Copy(&detail, &tenement)
|
||||
return result.Success(
|
||||
"已经获取到要查询的商户。",
|
||||
fiber.Map{
|
||||
"tenement": detail,
|
||||
},
|
||||
)
|
||||
}
|
||||
142
controller/top_up.go
Normal file
142
controller/top_up.go
Normal file
@@ -0,0 +1,142 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"electricity_bill_calc/logger"
|
||||
"electricity_bill_calc/repository"
|
||||
"electricity_bill_calc/response"
|
||||
"electricity_bill_calc/security"
|
||||
"electricity_bill_calc/tools"
|
||||
"electricity_bill_calc/types"
|
||||
"electricity_bill_calc/vo"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/jinzhu/copier"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
var topUpLog = logger.Named("Controller", "TopUp")
|
||||
|
||||
func InitializeTopUpHandlers(router *fiber.App) {
|
||||
router.Get("/topup/:pid", security.EnterpriseAuthorize, listTopUps)
|
||||
router.Post("/topup/:pid", security.EnterpriseAuthorize, createTopUp)
|
||||
router.Get("/topup/:pid/:code", security.EnterpriseAuthorize, getTopUp)
|
||||
router.Delete("/topup/:pid/:code", security.EnterpriseAuthorize, deleteTopUp)
|
||||
}
|
||||
|
||||
// 查询符合条件的商户充值记录
|
||||
func listTopUps(c *fiber.Ctx) error {
|
||||
result := response.NewResult(c)
|
||||
park := tools.EmptyToNil(c.Params("pid"))
|
||||
if park == nil {
|
||||
topUpLog.Error("查询符合条件的商户充值记录,未指定要访问的园区")
|
||||
return result.BadRequest("未指定要访问的园区")
|
||||
}
|
||||
if pass, err := checkParkBelongs(*park, topUpLog, c, &result); !pass {
|
||||
return err
|
||||
}
|
||||
keyword := tools.EmptyToNil(c.Query("keyword"))
|
||||
startDate, err := types.ParseDatep(c.Query("start_date"))
|
||||
if err != nil {
|
||||
topUpLog.Error("查询符合条件的商户充值记录,查询起始日期格式错误", zap.Error(err))
|
||||
return result.BadRequest("查询起始日期格式错误")
|
||||
}
|
||||
endDate, err := types.ParseDatep(c.Query("end_date"))
|
||||
if err != nil {
|
||||
topUpLog.Error("查询符合条件的商户充值记录,查询结束日期格式错误", zap.Error(err))
|
||||
return result.BadRequest("查询结束日期格式错误")
|
||||
}
|
||||
page := c.QueryInt("page", 1)
|
||||
topUps, total, err := repository.TopUpRepository.ListTopUps(*park, startDate, endDate, keyword, uint(page))
|
||||
if err != nil {
|
||||
topUpLog.Error("查询符合条件的商户充值记录,查询失败", zap.Error(err))
|
||||
return result.Error(fiber.StatusInternalServerError, "商户充值记录查询不成功")
|
||||
}
|
||||
topUpLog.Debug("检查获取到的数据", zap.Any("topUps", topUps), zap.Int64("total", total))
|
||||
topUpDetails := make([]*vo.TopUpDetailQueryResponse, 0)
|
||||
copier.Copy(&topUpDetails, &topUps)
|
||||
topUpLog.Debug("检查转换后的数据", zap.Any("topUpDetails", topUpDetails))
|
||||
return result.Success(
|
||||
"已经获取到符合条件的商户充值记录",
|
||||
response.NewPagedResponse(page, total).ToMap(),
|
||||
fiber.Map{"topUps": topUpDetails},
|
||||
)
|
||||
}
|
||||
|
||||
// 获取指定充值记录的详细内容
|
||||
func getTopUp(c *fiber.Ctx) error {
|
||||
result := response.NewResult(c)
|
||||
park := tools.EmptyToNil(c.Params("pid"))
|
||||
if park == nil {
|
||||
topUpLog.Error("获取指定充值记录的详细内容,未指定要访问的园区")
|
||||
return result.BadRequest("未指定要访问的园区")
|
||||
}
|
||||
if pass, err := checkParkBelongs(*park, topUpLog, c, &result); !pass {
|
||||
return err
|
||||
}
|
||||
topUpCode := tools.EmptyToNil(c.Params("code"))
|
||||
if topUpCode == nil {
|
||||
topUpLog.Error("获取指定充值记录的详细内容,未指定要查询的充值记录")
|
||||
return result.BadRequest("未指定要查询的充值记录")
|
||||
}
|
||||
topUp, err := repository.TopUpRepository.GetTopUp(*park, *topUpCode)
|
||||
if err != nil {
|
||||
topUpLog.Error("获取指定充值记录的详细内容,查询失败", zap.Error(err))
|
||||
return result.NotFound("未找到指定的商户充值记录")
|
||||
}
|
||||
var topUpDetail vo.TopUpDetailQueryResponse
|
||||
copier.Copy(&topUpDetail, &topUp)
|
||||
return result.Success(
|
||||
"已经获取到指定充值记录的详细内容",
|
||||
fiber.Map{"topup": topUpDetail},
|
||||
)
|
||||
}
|
||||
|
||||
// 创建一条新的商户充值记录
|
||||
func createTopUp(c *fiber.Ctx) error {
|
||||
result := response.NewResult(c)
|
||||
park := tools.EmptyToNil(c.Params("pid"))
|
||||
if park == nil {
|
||||
topUpLog.Error("创建一条新的商户充值记录,未指定要访问的园区")
|
||||
return result.BadRequest("未指定要访问的园区")
|
||||
}
|
||||
if pass, err := checkParkBelongs(*park, topUpLog, c, &result); !pass {
|
||||
return err
|
||||
}
|
||||
var form vo.TopUpCreationForm
|
||||
if err := c.BodyParser(&form); err != nil {
|
||||
topUpLog.Error("创建一条新的商户充值记录,请求体解析失败", zap.Error(err))
|
||||
return result.BadRequest("请求体解析失败")
|
||||
}
|
||||
if err := repository.TopUpRepository.CreateTopUp(*park, &form); err != nil {
|
||||
topUpLog.Error("创建一条新的商户充值记录,创建失败", zap.Error(err))
|
||||
return result.NotAccept("商户充值记录创建不成功")
|
||||
}
|
||||
return result.Created(
|
||||
"已经创建一条新的商户充值记录",
|
||||
)
|
||||
}
|
||||
|
||||
// 删除一条指定的商户充值记录
|
||||
func deleteTopUp(c *fiber.Ctx) error {
|
||||
result := response.NewResult(c)
|
||||
park := tools.EmptyToNil(c.Params("pid"))
|
||||
if park == nil {
|
||||
topUpLog.Error("删除一条指定的商户充值记录,未指定要访问的园区")
|
||||
return result.BadRequest("未指定要访问的园区")
|
||||
}
|
||||
if pass, err := checkParkBelongs(*park, topUpLog, c, &result); !pass {
|
||||
return err
|
||||
}
|
||||
topUpCode := tools.EmptyToNil(c.Params("code"))
|
||||
if topUpCode == nil {
|
||||
topUpLog.Error("删除一条指定的商户充值记录,未指定要删除的充值记录")
|
||||
return result.BadRequest("未指定要删除的充值记录")
|
||||
}
|
||||
if err := repository.TopUpRepository.DeleteTopUp(*park, *topUpCode); err != nil {
|
||||
topUpLog.Error("删除一条指定的商户充值记录,删除失败", zap.Error(err))
|
||||
return result.NotAccept("商户充值记录删除不成功")
|
||||
}
|
||||
return result.Deleted(
|
||||
"已经删除一条指定的商户充值记录",
|
||||
)
|
||||
}
|
||||
@@ -3,154 +3,100 @@ package controller
|
||||
import (
|
||||
"electricity_bill_calc/cache"
|
||||
"electricity_bill_calc/exceptions"
|
||||
"electricity_bill_calc/global"
|
||||
"electricity_bill_calc/logger"
|
||||
"electricity_bill_calc/model"
|
||||
"electricity_bill_calc/repository"
|
||||
"electricity_bill_calc/response"
|
||||
"electricity_bill_calc/security"
|
||||
"electricity_bill_calc/service"
|
||||
"fmt"
|
||||
"electricity_bill_calc/tools"
|
||||
"electricity_bill_calc/vo"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/shopspring/decimal"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
func InitializeUserController(router *gin.Engine) {
|
||||
router.DELETE("/password/:uid", security.OPSAuthorize, invalidUserPassword)
|
||||
router.DELETE("/login", security.MustAuthenticated, logout)
|
||||
router.PUT("/password", resetUserPassword)
|
||||
router.GET("/accounts", security.OPSAuthorize, listPagedUser)
|
||||
router.POST("/login", login)
|
||||
router.PUT("/account/enabled/state", security.OPSAuthorize, switchUserEnabling)
|
||||
router.POST("/account", security.OPSAuthorize, createOPSAndManagementAccount)
|
||||
router.GET("/account/:uid", security.MustAuthenticated, getUserDetail)
|
||||
router.POST("/enterprise", security.OPSAuthorize, createEnterpriseAccount)
|
||||
router.PUT("/account/:uid", security.OPSAuthorize, modifyAccountDetail)
|
||||
router.GET("/enterprise/quick/search", security.OPSAuthorize, quickSearchEnterprise)
|
||||
router.GET("/expiration", security.EnterpriseAuthorize, fetchExpiration)
|
||||
var userLog = logger.Named("Handler", "User")
|
||||
|
||||
func InitializeUserHandlers(router *fiber.App) {
|
||||
router.Delete("/login", security.MustAuthenticated, doLogout)
|
||||
router.Post("/login", doLogin)
|
||||
router.Get("/account", security.OPSAuthorize, searchUsers)
|
||||
router.Post("/account", security.OPSAuthorize, createOPSAccount)
|
||||
router.Get("/account/:uid", security.MustAuthenticated, fetchUserInformation)
|
||||
router.Put("/account/:uid", security.OPSAuthorize, modifyUserInformation)
|
||||
router.Put("/account/enabled/state", security.OPSAuthorize, changeUserState)
|
||||
router.Get("/expiration", security.EnterpriseAuthorize, getAccountExpiration)
|
||||
router.Post("/enterprise", security.OPSAuthorize, createEnterpriseAccount)
|
||||
router.Get("/enterprise/quick/search", security.OPSAuthorize, quickSearchEnterprise)
|
||||
router.Put("/password", resetUserPassword)
|
||||
router.Delete("/password/:uid", security.OPSAuthorize, invalidUserPassword)
|
||||
}
|
||||
|
||||
type _LoginFormData struct {
|
||||
type _LoginForm struct {
|
||||
Username string `json:"uname"`
|
||||
Password string `json:"upass"`
|
||||
Type int8 `json:"type"`
|
||||
Type int16 `json:"type"`
|
||||
}
|
||||
|
||||
func login(c *gin.Context) {
|
||||
result := response.NewResult(c)
|
||||
loginData := new(_LoginFormData)
|
||||
err := c.BindJSON(loginData)
|
||||
if err != nil {
|
||||
result.Error(http.StatusInternalServerError, "表单解析失败。")
|
||||
return
|
||||
func doLogin(c *fiber.Ctx) error {
|
||||
result := response.NewResult(c) //创建一个相应结果对象
|
||||
loginData := new(_LoginForm) //创建一个解析登录表单数据的实体
|
||||
if err := c.BodyParser(loginData); err != nil { //解析请求体中的Json数据到loginData里,如果解析出错就返回错误
|
||||
userLog.Error("表单解析失败!", zap.Error(err))
|
||||
return result.Error(http.StatusInternalServerError, "表单解析失败。") //返回一个内部服务器错误的相应结果
|
||||
}
|
||||
var (
|
||||
session *model.Session
|
||||
err error
|
||||
)
|
||||
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 {
|
||||
session, err = service.UserService.ProcessManagementUserLogin(loginData.Username, loginData.Password)
|
||||
userLog.Info("该用户是管理用户")
|
||||
session, err = service.UserService.ProcessManagementUserLogin(loginData.Username, loginData.Password) //管理用户
|
||||
}
|
||||
if err != nil {
|
||||
if authError, ok := err.(*exceptions.AuthenticationError); ok {
|
||||
if authError.NeedReset {
|
||||
result.LoginNeedReset()
|
||||
return
|
||||
if authError, ok := err.(*exceptions.AuthenticationError); ok { //检查错误是否为身份验证错误
|
||||
if authError.NeedReset { //如果需要重置密码则返回对应结果
|
||||
return result.LoginNeedReset()
|
||||
}
|
||||
result.Error(int(authError.Code), authError.Message)
|
||||
return
|
||||
return result.Error(int(authError.Code), authError.Message) //返回身份验证错误相应
|
||||
} else {
|
||||
result.Error(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
userLog.Error("用户登录请求处理失败!", zap.Error(err))
|
||||
return result.Error(http.StatusInternalServerError, err.Error()) //返回内部服务器错误
|
||||
}
|
||||
}
|
||||
result.LoginSuccess(session)
|
||||
return result.LoginSuccess(session) //返回登录成功相应结果,包含会话信息
|
||||
}
|
||||
|
||||
func logout(c *gin.Context) {
|
||||
func doLogout(c *fiber.Ctx) error {
|
||||
result := response.NewResult(c)
|
||||
session, exists := c.Get("session")
|
||||
if !exists {
|
||||
result.Success("用户会话已结束。")
|
||||
return
|
||||
}
|
||||
_, err := cache.ClearSession(session.(*model.Session).Token)
|
||||
session, err := _retreiveSession(c)
|
||||
if err != nil {
|
||||
result.Error(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
return result.Success("用户会话已结束。")
|
||||
}
|
||||
result.Success("用户已成功登出系统。")
|
||||
_, err = cache.ClearSession(session.Token)
|
||||
if err != nil {
|
||||
userLog.Error("用户登出处理失败!", zap.Error(err))
|
||||
return result.Error(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
return result.Success("用户已成功登出系统。")
|
||||
}
|
||||
|
||||
func invalidUserPassword(c *gin.Context) {
|
||||
func searchUsers(c *fiber.Ctx) error {
|
||||
result := response.NewResult(c)
|
||||
targetUserId := c.Param("uid")
|
||||
verifyCode, err := service.UserService.InvalidUserPassword(targetUserId)
|
||||
if _, ok := err.(exceptions.NotFoundError); ok {
|
||||
result.NotFound("未找到指定用户。")
|
||||
return
|
||||
}
|
||||
if _, ok := err.(exceptions.UnsuccessfulOperationError); ok {
|
||||
result.NotAccept("未能成功更新用户的密码。")
|
||||
return
|
||||
}
|
||||
requestPage, err := strconv.Atoi(c.Query("page", "1"))
|
||||
if err != nil {
|
||||
result.Error(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
result.Json(http.StatusAccepted, "用户密码已经失效", gin.H{"verify": verifyCode})
|
||||
}
|
||||
|
||||
type _ResetPasswordFormData struct {
|
||||
VerifyCode string `json:"verifyCode"`
|
||||
Username string `json:"uname"`
|
||||
NewPassword string `json:"newPass"`
|
||||
}
|
||||
|
||||
func resetUserPassword(c *gin.Context) {
|
||||
result := response.NewResult(c)
|
||||
resetForm := new(_ResetPasswordFormData)
|
||||
c.BindJSON(resetForm)
|
||||
verified, err := service.UserService.VerifyUserPassword(resetForm.Username, resetForm.VerifyCode)
|
||||
if _, ok := err.(exceptions.NotFoundError); ok {
|
||||
result.NotFound("指定的用户不存在。")
|
||||
return
|
||||
return result.NotAccept("查询参数[page]格式不正确。")
|
||||
}
|
||||
requestKeyword := c.Query("keyword")
|
||||
requestUserType, err := strconv.Atoi(c.Query("type", "-1"))
|
||||
if err != nil {
|
||||
result.Error(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
if !verified {
|
||||
result.Error(http.StatusUnauthorized, "验证码不正确。")
|
||||
return
|
||||
}
|
||||
completed, err := service.UserService.ResetUserPassword(resetForm.Username, resetForm.NewPassword)
|
||||
if err != nil {
|
||||
result.Error(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
if completed {
|
||||
result.Updated("用户凭据已更新。")
|
||||
return
|
||||
}
|
||||
result.NotAccept("用户凭据未能成功更新。")
|
||||
}
|
||||
|
||||
func listPagedUser(c *gin.Context) {
|
||||
result := response.NewResult(c)
|
||||
requestPage, err := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||
if err != nil {
|
||||
result.NotAccept("查询参数[page]格式不正确。")
|
||||
return
|
||||
}
|
||||
requestKeyword := c.DefaultQuery("keyword", "")
|
||||
requestUserType, err := strconv.Atoi(c.DefaultQuery("type", "-1"))
|
||||
if err != nil {
|
||||
result.NotAccept("查询参数[type]格式不正确。")
|
||||
return
|
||||
return result.NotAccept("查询参数[type]格式不正确。")
|
||||
}
|
||||
var requestUserStat *bool
|
||||
state, err := strconv.ParseBool(c.Query("state"))
|
||||
@@ -159,213 +105,230 @@ func listPagedUser(c *gin.Context) {
|
||||
} else {
|
||||
requestUserStat = &state
|
||||
}
|
||||
users, total, err := service.UserService.ListUserDetail(requestKeyword, requestUserType, requestUserStat, requestPage)
|
||||
users, total, err := repository.UserRepository.FindUser(
|
||||
&requestKeyword,
|
||||
int16(requestUserType),
|
||||
requestUserStat,
|
||||
uint(requestPage),
|
||||
)
|
||||
if err != nil {
|
||||
result.NotFound(err.Error())
|
||||
return
|
||||
return result.NotFound(err.Error())
|
||||
}
|
||||
result.Json(
|
||||
http.StatusOK,
|
||||
return result.Success(
|
||||
"已取得符合条件的用户集合。",
|
||||
response.NewPagedResponse(requestPage, total).ToMap(),
|
||||
gin.H{"accounts": users},
|
||||
fiber.Map{"accounts": users},
|
||||
)
|
||||
}
|
||||
|
||||
type _UserStateChangeFormData struct {
|
||||
UserID string `json:"uid" form:"uid"`
|
||||
Enabled bool `json:"enabled" form:"enabled"`
|
||||
}
|
||||
|
||||
func switchUserEnabling(c *gin.Context) {
|
||||
result := response.NewResult(c)
|
||||
switchForm := new(_UserStateChangeFormData)
|
||||
c.BindJSON(switchForm)
|
||||
err := service.UserService.SwitchUserState(switchForm.UserID, switchForm.Enabled)
|
||||
if err != nil {
|
||||
if nfErr, ok := err.(*exceptions.NotFoundError); ok {
|
||||
result.NotFound(nfErr.Message)
|
||||
return
|
||||
} else {
|
||||
result.Error(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
result.Updated("用户状态已经更新。")
|
||||
}
|
||||
|
||||
type _OPSAccountCreationFormData struct {
|
||||
Username string `json:"username" form:"username"`
|
||||
Name string `json:"name" form:"name"`
|
||||
Contact *string `json:"contact" form:"contact"`
|
||||
Phone *string `json:"phone" form:"phone"`
|
||||
Type int `json:"type" form:"type"`
|
||||
}
|
||||
|
||||
func createOPSAndManagementAccount(c *gin.Context) {
|
||||
result := response.NewResult(c)
|
||||
creationForm := new(_OPSAccountCreationFormData)
|
||||
c.BindJSON(creationForm)
|
||||
exists, err := service.UserService.IsUsernameExists(creationForm.Username)
|
||||
if exists {
|
||||
result.Conflict("指定的用户名已经被使用了。")
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
result.Error(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
newUser := new(model.User)
|
||||
newUser.Username = creationForm.Username
|
||||
newUser.Type = int8(creationForm.Type)
|
||||
newUser.Enabled = true
|
||||
newUserDetail := new(model.UserDetail)
|
||||
newUserDetail.Name = &creationForm.Name
|
||||
newUserDetail.Contact = creationForm.Contact
|
||||
newUserDetail.Phone = creationForm.Phone
|
||||
newUserDetail.UnitServiceFee = decimal.Zero
|
||||
newUserDetail.ServiceExpiration, _ = time.Parse("2006-01-02 15:04:05", "2099-12-31 23:59:59")
|
||||
verifyCode, err := service.UserService.CreateUser(newUser, newUserDetail)
|
||||
if err != nil {
|
||||
result.Error(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
cache.AbolishRelation("user")
|
||||
result.Json(http.StatusCreated, "用户已经成功创建。", gin.H{"verify": verifyCode})
|
||||
}
|
||||
|
||||
func getUserDetail(c *gin.Context) {
|
||||
result := response.NewResult(c)
|
||||
targetUserId := c.Param("uid")
|
||||
exists, err := service.UserService.IsUserExists(targetUserId)
|
||||
if !exists {
|
||||
result.NotFound("指定的用户不存在。")
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
result.Error(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
userDetail, err := service.UserService.FetchUserDetail(targetUserId)
|
||||
if err != nil {
|
||||
result.Error(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
result.Json(http.StatusOK, "用户详细信息已获取到。", gin.H{"user": userDetail})
|
||||
}
|
||||
|
||||
type _EnterpriseCreationFormData struct {
|
||||
Username string `json:"username" form:"username"`
|
||||
Name string `json:"name" form:"name"`
|
||||
Region *string `json:"region" form:"region"`
|
||||
Address *string `json:"address" form:"address"`
|
||||
Contact *string `json:"contact" form:"contact"`
|
||||
Phone *string `json:"phone" form:"phone"`
|
||||
UnitServiceFee *string `json:"unitServiceFee" form:"unitServiceFee"`
|
||||
}
|
||||
|
||||
func createEnterpriseAccount(c *gin.Context) {
|
||||
result := response.NewResult(c)
|
||||
creationForm := new(_EnterpriseCreationFormData)
|
||||
c.BindJSON(creationForm)
|
||||
exists, err := service.UserService.IsUsernameExists(creationForm.Username)
|
||||
if exists {
|
||||
result.Conflict("指定的用户名已经被使用了。")
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
result.Error(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
newUser := new(model.User)
|
||||
newUser.Username = creationForm.Username
|
||||
newUser.Type = model.USER_TYPE_ENT
|
||||
newUser.Enabled = true
|
||||
newUserDetail := new(model.UserDetail)
|
||||
newUserDetail.Name = &creationForm.Name
|
||||
newUserDetail.Contact = creationForm.Contact
|
||||
newUserDetail.Phone = creationForm.Phone
|
||||
newUserDetail.UnitServiceFee, err = decimal.NewFromString(*creationForm.UnitServiceFee)
|
||||
if err != nil {
|
||||
result.BadRequest("用户月服务费无法解析。")
|
||||
return
|
||||
}
|
||||
newUserDetail.ServiceExpiration = time.Now()
|
||||
|
||||
verifyCode, err := service.UserService.CreateUser(newUser, newUserDetail)
|
||||
if err != nil {
|
||||
result.Error(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
cache.AbolishRelation("user")
|
||||
result.Json(http.StatusCreated, "用户已经成功创建。", gin.H{"verify": verifyCode})
|
||||
}
|
||||
|
||||
type _AccountModificationFormData struct {
|
||||
Name string `json:"name" form:"name"`
|
||||
Region *string `json:"region" form:"region"`
|
||||
Address *string `json:"address" form:"address"`
|
||||
Contact *string `json:"contact" form:"contact"`
|
||||
Phone *string `json:"phone" form:"phone"`
|
||||
UnitServiceFee *string `json:"unitServiceFee" form:"unitServiceFee"`
|
||||
}
|
||||
|
||||
func modifyAccountDetail(c *gin.Context) {
|
||||
result := response.NewResult(c)
|
||||
targetUserId := c.Param("uid")
|
||||
modForm := new(_AccountModificationFormData)
|
||||
c.BindJSON(modForm)
|
||||
exists, err := service.UserService.IsUserExists(targetUserId)
|
||||
if !exists {
|
||||
result.NotFound("指定的用户不存在。")
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
result.Error(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
newUserInfo := new(model.UserDetail)
|
||||
newUserInfo.Name = &modForm.Name
|
||||
newUserInfo.Region = modForm.Region
|
||||
newUserInfo.Address = modForm.Address
|
||||
newUserInfo.Contact = modForm.Contact
|
||||
newUserInfo.Phone = modForm.Phone
|
||||
newUserInfo.UnitServiceFee, err = decimal.NewFromString(*modForm.UnitServiceFee)
|
||||
if err != nil {
|
||||
result.BadRequest("用户月服务费无法解析。")
|
||||
return
|
||||
}
|
||||
_, err = global.DBConn.ID(targetUserId).Update(newUserInfo)
|
||||
if err != nil {
|
||||
result.Error(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
cache.AbolishRelation("user")
|
||||
cache.AbolishRelation(fmt.Sprintf("user_%s", targetUserId))
|
||||
result.Updated("指定用户的信息已经更新。")
|
||||
}
|
||||
|
||||
func quickSearchEnterprise(c *gin.Context) {
|
||||
result := response.NewResult(c)
|
||||
keyword := c.Query("keyword")
|
||||
searchResult, err := service.UserService.SearchLimitUsers(keyword, 6)
|
||||
if err != nil {
|
||||
result.Error(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
result.Json(http.StatusOK, "已查询到存在符合条件的企业", gin.H{"users": searchResult})
|
||||
}
|
||||
|
||||
func fetchExpiration(c *gin.Context) {
|
||||
func getAccountExpiration(c *fiber.Ctx) error {
|
||||
result := response.NewResult(c)
|
||||
session, err := _retreiveSession(c)
|
||||
if err != nil {
|
||||
result.Unauthorized(err.Error())
|
||||
return
|
||||
userLog.Error("未找到有效的用户会话。", zap.Error(err))
|
||||
return result.Error(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
user, err := service.UserService.FetchUserDetail(session.Uid)
|
||||
userDetail, err := repository.UserRepository.FindUserDetailById(session.Uid)
|
||||
if err != nil {
|
||||
result.NotFound(err.Error())
|
||||
return
|
||||
return result.NotFound("未找到指定的用户档案")
|
||||
}
|
||||
result.Json(http.StatusOK, "已经取得用户的服务期限信息", gin.H{"expiration": user.ServiceExpiration.Format("2006-01-02")})
|
||||
return result.Success(
|
||||
"已经取得用户的服务期限信息",
|
||||
fiber.Map{"expiration": userDetail.ServiceExpiration.Format("2006-01-02")},
|
||||
)
|
||||
}
|
||||
|
||||
func createOPSAccount(c *fiber.Ctx) error {
|
||||
userLog.Info("请求创建运维或监管账户。")
|
||||
result := response.NewResult(c)
|
||||
creationForm := new(vo.MGTAndOPSAccountCreationForm)
|
||||
if err := c.BodyParser(creationForm); err != nil {
|
||||
userLog.Error("表单解析失败!", zap.Error(err))
|
||||
return result.UnableToParse("无法解析提交的数据。")
|
||||
}
|
||||
exists, err := repository.UserRepository.IsUsernameExists(creationForm.Username)
|
||||
if err != nil {
|
||||
userLog.Error("检查用户名是否已经被使用时发生错误!", zap.Error(err))
|
||||
return result.Error(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
if exists {
|
||||
return result.Conflict("指定的用户名已经被使用了。")
|
||||
}
|
||||
verifyCode, err := service.UserService.CreateUserAccount(creationForm.IntoUser(), creationForm.IntoUserDetail())
|
||||
if err != nil {
|
||||
userLog.Error("创建用户账户时发生错误!", zap.Error(err))
|
||||
return result.Error(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
return result.Created("用户已经成功创建。", fiber.Map{"verify": verifyCode})
|
||||
}
|
||||
|
||||
func fetchUserInformation(c *fiber.Ctx) error {
|
||||
userLog.Info("请求获取用户详细信息。")
|
||||
result := response.NewResult(c)
|
||||
targetUserId := c.Params("uid")
|
||||
exists, err := repository.UserRepository.IsUserExists(targetUserId)
|
||||
if err != nil {
|
||||
return result.Error(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
if !exists {
|
||||
return result.NotFound("指定的用户不存在。")
|
||||
}
|
||||
userDetail, err := repository.UserRepository.FindUserDetailById(targetUserId)
|
||||
if err != nil {
|
||||
return result.Error(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
return result.Success("用户详细信息已获取到。", fiber.Map{"user": userDetail})
|
||||
}
|
||||
|
||||
func modifyUserInformation(c *fiber.Ctx) error {
|
||||
userLog.Info("请求修改用户详细信息。")
|
||||
session, _ := _retreiveSession(c)
|
||||
result := response.NewResult(c)
|
||||
targetUserId := c.Params("uid")
|
||||
exists, err := repository.UserRepository.IsUserExists(targetUserId)
|
||||
if err != nil {
|
||||
userLog.Error("检查用户是否存在时发生错误!", zap.Error(err))
|
||||
return result.Error(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
if !exists {
|
||||
return result.NotFound("指定的用户不存在。")
|
||||
}
|
||||
modificationForm := new(vo.UserDetailModificationForm)
|
||||
if err := c.BodyParser(modificationForm); err != nil {
|
||||
userLog.Error("表单解析失败!", zap.Error(err))
|
||||
return result.UnableToParse("无法解析提交的数据。")
|
||||
}
|
||||
userLog.Debug("用户服务费修改表单:", zap.Any("form", modificationForm))
|
||||
detailFormForUpdate, err := modificationForm.IntoModificationModel()
|
||||
if err != nil {
|
||||
userLog.Error("用户服务费解析转换失败!", zap.Error(err))
|
||||
return result.UnableToParse("无法解析提交的数据,服务费格式不正确。")
|
||||
}
|
||||
if ok, err := repository.UserRepository.UpdateDetail(targetUserId, *detailFormForUpdate, &session.Uid); err != nil || !ok {
|
||||
userLog.Error("更新用户详细信息失败!", zap.Error(err))
|
||||
return result.Error(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
return result.Updated("指定用户的信息已经更新。")
|
||||
}
|
||||
|
||||
func changeUserState(c *fiber.Ctx) error {
|
||||
userLog.Info("请求修改用户状态。")
|
||||
result := response.NewResult(c)
|
||||
modificationForm := new(vo.UserStateChangeForm)
|
||||
if err := c.BodyParser(modificationForm); err != nil {
|
||||
userLog.Error("表单解析失败!", zap.Error(err))
|
||||
return result.UnableToParse("无法解析提交的数据。")
|
||||
}
|
||||
exists, err := repository.UserRepository.IsUserExists(modificationForm.Uid)
|
||||
if err != nil {
|
||||
userLog.Error("检查用户是否存在时发生错误!", zap.Error(err))
|
||||
return result.Error(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
if !exists {
|
||||
return result.NotFound("指定的用户不存在。")
|
||||
}
|
||||
if ok, err := repository.UserRepository.ChangeState(modificationForm.Uid, modificationForm.Enabled); err != nil || !ok {
|
||||
userLog.Error("更新用户状态失败!", zap.Error(err))
|
||||
return result.NotAccept("无法更新用户状态。")
|
||||
}
|
||||
return result.Updated("用户的状态已经更新。")
|
||||
}
|
||||
|
||||
func createEnterpriseAccount(c *fiber.Ctx) error {
|
||||
userLog.Info("请求创建企业账户。")
|
||||
result := response.NewResult(c)
|
||||
creationForm := new(vo.EnterpriseAccountCreationForm)
|
||||
if err := c.BodyParser(creationForm); err != nil {
|
||||
userLog.Error("表单解析失败!", zap.Error(err))
|
||||
return result.UnableToParse("无法解析提交的数据。")
|
||||
}
|
||||
exists, err := repository.UserRepository.IsUsernameExists(creationForm.Username)
|
||||
if err != nil {
|
||||
userLog.Error("检查用户名是否已经被使用时发生错误!", zap.Error(err))
|
||||
return result.Error(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
if exists {
|
||||
return result.Conflict("指定的用户名已经被使用了。")
|
||||
}
|
||||
userDetail, err := creationForm.IntoUserDetail()
|
||||
if err != nil {
|
||||
userLog.Error("转换用户详细档案时发生错误!", zap.Error(err))
|
||||
return result.UnableToParse("无法解析提交的数据,服务费格式不正确。")
|
||||
}
|
||||
verifyCode, err := service.UserService.CreateUserAccount(creationForm.IntoUser(), userDetail)
|
||||
if err != nil {
|
||||
userLog.Error("创建用户账户时发生错误!", zap.Error(err))
|
||||
return result.Error(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
return result.Created("用户已经成功创建。", fiber.Map{"verify": verifyCode})
|
||||
}
|
||||
|
||||
func quickSearchEnterprise(c *fiber.Ctx) error {
|
||||
userLog.Info("请求快速查询企业账户。")
|
||||
result := response.NewResult(c)
|
||||
keyword := c.Query("keyword")
|
||||
limit := c.QueryInt("limit", 6)
|
||||
if limit < 1 {
|
||||
limit = 6
|
||||
}
|
||||
users, err := repository.UserRepository.SearchUsersWithLimit(nil, &keyword, uint(limit))
|
||||
if err != nil {
|
||||
userLog.Error("查询用户时发生错误!", zap.Error(err))
|
||||
return result.Error(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
return result.Success("已查询到存在符合条件的企业", fiber.Map{"users": users})
|
||||
}
|
||||
|
||||
func resetUserPassword(c *fiber.Ctx) error {
|
||||
userLog.Info("请求重置用户密码。")
|
||||
result := response.NewResult(c)
|
||||
repasswordForm := new(vo.RepasswordForm)
|
||||
if err := c.BodyParser(repasswordForm); err != nil {
|
||||
userLog.Error("表单解析失败!", zap.Error(err))
|
||||
return result.UnableToParse("无法解析提交的数据。")
|
||||
}
|
||||
user, err := repository.UserRepository.FindUserByUsername(repasswordForm.Username)
|
||||
if err != nil {
|
||||
userLog.Error("检查用户是否存在时发生错误!", zap.Error(err))
|
||||
return result.Error(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
if user == nil {
|
||||
return result.NotFound("指定的用户不存在。")
|
||||
}
|
||||
if !service.UserService.MatchUserPassword(user.Password, repasswordForm.VerifyCode) {
|
||||
return result.Unauthorized("验证码不正确。")
|
||||
}
|
||||
ok, err := repository.UserRepository.UpdatePassword(user.Id, repasswordForm.NewPassword, false)
|
||||
if err != nil {
|
||||
userLog.Error("更新用户凭据时发生错误!", zap.Error(err))
|
||||
return result.Error(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
if !ok {
|
||||
return result.NotAccept("无法更新用户凭据。")
|
||||
}
|
||||
return result.Updated("用户凭据已经更新。")
|
||||
}
|
||||
|
||||
func invalidUserPassword(c *fiber.Ctx) error {
|
||||
userLog.Info("请求使用户凭据失效。")
|
||||
result := response.NewResult(c)
|
||||
uid := c.Params("uid")
|
||||
exists, err := repository.UserRepository.IsUserExists(uid)
|
||||
if err != nil {
|
||||
userLog.Error("检查用户是否存在时发生错误!", zap.Error(err))
|
||||
return result.Error(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
if !exists {
|
||||
return result.NotFound("指定的用户不存在。")
|
||||
}
|
||||
verifyCode := tools.RandStr(10)
|
||||
ok, err := repository.UserRepository.UpdatePassword(uid, verifyCode, true)
|
||||
if err != nil {
|
||||
userLog.Error("更新用户凭据时发生错误!", zap.Error(err))
|
||||
return result.Error(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
if !ok {
|
||||
return result.NotAccept("未能更新用户凭据。")
|
||||
}
|
||||
return result.Updated("用户凭据已经更新。", fiber.Map{"verify": verifyCode})
|
||||
}
|
||||
|
||||
@@ -1,86 +1,134 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"electricity_bill_calc/exceptions"
|
||||
"electricity_bill_calc/logger"
|
||||
"electricity_bill_calc/repository"
|
||||
"electricity_bill_calc/response"
|
||||
"electricity_bill_calc/security"
|
||||
"electricity_bill_calc/service"
|
||||
"electricity_bill_calc/vo"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"go.uber.org/zap"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func InitializeWithdrawController(router *gin.Engine) {
|
||||
router.DELETE("/publicity/:pid", security.EnterpriseAuthorize, applyReportWithdraw)
|
||||
router.GET("/withdraws", security.OPSAuthorize, fetchWithdrawsWaitingAutdit)
|
||||
router.PUT("/withdraw/:rid", security.OPSAuthorize, auditWithdraw)
|
||||
var withdrawLog = logger.Named("Handler", "Withdraw")
|
||||
|
||||
func InitializeWithdrawHandlers(router *fiber.App) {
|
||||
router.Get("/withdraw", security.OPSAuthorize, withdraw)
|
||||
router.Put("/withdraw/:rid", security.OPSAuthorize, reviewRequestWithdraw)
|
||||
router.Delete("/withdraw/:rid", security.EnterpriseAuthorize, recallReport)
|
||||
}
|
||||
|
||||
func applyReportWithdraw(c *gin.Context) {
|
||||
//用于分页检索用户的核算报表
|
||||
func withdraw(c *fiber.Ctx) error {
|
||||
//记录日志
|
||||
withdrawLog.Info("带分页的待审核的核算撤回申请列表")
|
||||
//获取请求参数
|
||||
result := response.NewResult(c)
|
||||
requestReportId := c.Param("pid")
|
||||
if !ensureReportBelongs(c, result, requestReportId) {
|
||||
return
|
||||
}
|
||||
deleted, err := service.WithdrawService.ApplyWithdraw(requestReportId)
|
||||
keyword := c.Query("keyword", "")
|
||||
page := c.QueryInt("page", 1)
|
||||
withdrawLog.Info("参数为: ", zap.String("keyword", keyword), zap.Int("page", page))
|
||||
//中间数据库操作暂且省略。。。。
|
||||
//首先进行核算报表的分页查询
|
||||
withdraws, total, err := repository.WithdrawRepository.FindWithdraw(uint(page), &keyword)
|
||||
if err != nil {
|
||||
if nfErr, ok := err.(exceptions.NotFoundError); ok {
|
||||
result.NotFound(nfErr.Error())
|
||||
return
|
||||
} else if ioErr, ok := err.(exceptions.ImproperOperateError); ok {
|
||||
result.NotAccept(ioErr.Error())
|
||||
return
|
||||
} else {
|
||||
result.Error(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
withdrawLog.Error("检索用户核算报表失败。", zap.Error(err))
|
||||
return result.Error(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
if !deleted {
|
||||
result.Error(http.StatusInternalServerError, "未能完成公示报表的申请撤回操作。")
|
||||
return
|
||||
}
|
||||
result.Success("指定的公示报表已经申请撤回。")
|
||||
}
|
||||
|
||||
func fetchWithdrawsWaitingAutdit(c *gin.Context) {
|
||||
result := response.NewResult(c)
|
||||
keyword := c.DefaultQuery("keyword", "")
|
||||
requestPage, err := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||
if err != nil {
|
||||
result.NotAccept("查询参数[page]格式不正确。")
|
||||
return
|
||||
}
|
||||
reports, totalitems, err := service.WithdrawService.FetchPagedWithdrawApplies(requestPage, keyword)
|
||||
if err != nil {
|
||||
result.NotFound(err.Error())
|
||||
return
|
||||
}
|
||||
result.Json(
|
||||
http.StatusOK,
|
||||
"已经取得符合条件的等待审核的撤回申请。",
|
||||
response.NewPagedResponse(requestPage, totalitems).ToMap(),
|
||||
gin.H{"records": reports},
|
||||
//TODO: 2023-07-18 此处返回值是个示例,具体返回值需要查询数据库(完成)
|
||||
return result.Success(
|
||||
"withdraw请求成功",
|
||||
response.NewPagedResponse(page, total).ToMap(),
|
||||
fiber.Map{"records": withdraws},
|
||||
)
|
||||
}
|
||||
|
||||
type WithdrawAuditFormData struct {
|
||||
Audit bool `json:"audit" form:"audit"`
|
||||
//用于审核撤回报表
|
||||
func reviewRequestWithdraw(c *fiber.Ctx) error {
|
||||
Rid := c.Params("rid", "")
|
||||
Data := new(vo.ReviewWithdraw)
|
||||
result := response.NewResult(c)
|
||||
|
||||
if err := c.BodyParser(&Data); err != nil {
|
||||
withdrawLog.Error("无法解析审核指定报表的请求数据", zap.Error(err))
|
||||
return result.BadRequest("无法解析审核指定报表的请求数据。")
|
||||
}
|
||||
|
||||
if Data.Audit == true { //审核通过
|
||||
ok, err := repository.WithdrawRepository.ReviewTrueReportWithdraw(Rid)
|
||||
if err != nil {
|
||||
withdrawLog.Error("审核同意撤回报表失败")
|
||||
return result.Error(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
if !ok {
|
||||
withdrawLog.Error("审核同意撤回报表失败")
|
||||
return result.NotAccept("审核同意撤回报表失败")
|
||||
} else {
|
||||
return result.Success("审核同意撤回报表成功!")
|
||||
}
|
||||
} else { //审核不通过
|
||||
ok, err := repository.WithdrawRepository.ReviewFalseReportWithdraw(Rid)
|
||||
if err != nil {
|
||||
withdrawLog.Error("审核拒绝撤回报表失败")
|
||||
return result.Error(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
if !ok {
|
||||
withdrawLog.Error("审核拒绝撤回报表失败")
|
||||
return result.NotAccept("审核拒绝撤回报表失败")
|
||||
} else {
|
||||
return result.Success("审核拒绝撤回报表成功!")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func auditWithdraw(c *gin.Context) {
|
||||
//用于撤回电费核算
|
||||
func recallReport(c *fiber.Ctx) error {
|
||||
// 获取用户会话信息和参数
|
||||
rid := c.Params("rid", "")
|
||||
result := response.NewResult(c)
|
||||
requestReportId := c.Param("rid")
|
||||
formData := new(WithdrawAuditFormData)
|
||||
c.BindJSON(formData)
|
||||
err := service.WithdrawService.AuditWithdraw(requestReportId, formData.Audit)
|
||||
session, err := _retreiveSession(c)
|
||||
if err != nil {
|
||||
if nfErr, ok := err.(exceptions.NotFoundError); ok {
|
||||
result.NotFound(nfErr.Error())
|
||||
} else {
|
||||
result.NotAccept(err.Error())
|
||||
}
|
||||
return
|
||||
withdrawLog.Error("无法获取当前用户的会话。")
|
||||
return result.Unauthorized(err.Error())
|
||||
}
|
||||
result.Success("指定公示报表的撤回申请已经完成审核")
|
||||
// 检查指定报表的所属情况
|
||||
isBelongsTo, err := repository.ReportRepository.IsBelongsTo(rid, session.Uid)
|
||||
if err != nil {
|
||||
withdrawLog.Error("检查报表所属情况出现错误。", zap.Error(err))
|
||||
return result.Error(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
if err == nil && isBelongsTo == true {
|
||||
// 判断指定报表是否是当前园区的最后一张报表
|
||||
isLastReport, err := repository.ReportRepository.IsLastReport(rid)
|
||||
if err != nil {
|
||||
withdrawLog.Error("判断指定报表是否为当前园区的最后一张报表时出错", zap.Error(err))
|
||||
return result.Error(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
if err == nil && isLastReport == true {
|
||||
// 申请撤回指定的核算报表
|
||||
//TODO: 2023.07.25 申请撤回指定核算报表,正确状态未处理(完成)
|
||||
ok, err := repository.ReportRepository.ApplyWithdrawReport(rid)
|
||||
if err != nil {
|
||||
withdrawLog.Error("申请撤回指定核算报表出错", zap.Error(err))
|
||||
return result.Error(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
if ok {
|
||||
withdrawLog.Info("申请撤回指定核算报表成功")
|
||||
return result.Success("申请撤回指定核算报表成功")
|
||||
}
|
||||
} else {
|
||||
withdrawLog.Info("当前报表不是当前园区的最后一张报表")
|
||||
return result.Error(http.StatusForbidden, "当前报表不是当前园区的最后一张报表")
|
||||
}
|
||||
} else {
|
||||
withdrawLog.Info("指定的核算报表不属于当前用户。")
|
||||
return result.Error(http.StatusForbidden, "指定的核算报表不属于当前用户")
|
||||
}
|
||||
|
||||
return result.Error(http.StatusInternalServerError, "其他错误")
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
package excel
|
||||
|
||||
import (
|
||||
"electricity_bill_calc/model"
|
||||
"electricity_bill_calc/tools"
|
||||
"electricity_bill_calc/types"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
@@ -18,13 +19,13 @@ import (
|
||||
type ExcelTemplateGenerator interface {
|
||||
Close()
|
||||
WriteTo(w io.Writer) (int64, error)
|
||||
WriteMeterData(meters []model.EndUserDetail) error
|
||||
}
|
||||
|
||||
type ColumnRecognizer struct {
|
||||
Pattern []string
|
||||
Pattern [][]string
|
||||
Tag string
|
||||
MatchIndex int
|
||||
MustFill bool
|
||||
}
|
||||
|
||||
type ExcelAnalyzer[T any] struct {
|
||||
@@ -43,7 +44,7 @@ type ExcelAnalysisError struct {
|
||||
Err AnalysisError `json:"error"`
|
||||
}
|
||||
|
||||
func NewColumnRecognizer(tag string, patterns ...string) ColumnRecognizer {
|
||||
func NewColumnRecognizer(tag string, patterns ...[]string) ColumnRecognizer {
|
||||
return ColumnRecognizer{
|
||||
Pattern: patterns,
|
||||
Tag: tag,
|
||||
@@ -65,9 +66,17 @@ func (e ExcelAnalysisError) Error() string {
|
||||
|
||||
func (r *ColumnRecognizer) Recognize(cellValue string) bool {
|
||||
matches := make([]bool, 0)
|
||||
for _, p := range r.Pattern {
|
||||
matches = append(matches, strings.Contains(cellValue, p))
|
||||
for _, pG := range r.Pattern {
|
||||
groupMatch := make([]bool, 0)
|
||||
for _, p := range pG {
|
||||
groupMatch = append(groupMatch, strings.Contains(cellValue, p))
|
||||
}
|
||||
// 这句表示在每一个匹配组中,只要有一个匹配项,就算匹配成功
|
||||
matches = append(matches, lo.Reduce(groupMatch, func(acc, elem bool, index int) bool {
|
||||
return acc || elem
|
||||
}, false))
|
||||
}
|
||||
// 这句表示在尊有的匹配组中,必须全部的匹配组都完成匹配,才算匹配成功
|
||||
return lo.Reduce(matches, func(acc, elem bool, index int) bool {
|
||||
return acc && elem
|
||||
}, true)
|
||||
@@ -126,8 +135,18 @@ func (a *ExcelAnalyzer[T]) Analysis(bean T) ([]T, []ExcelAnalysisError) {
|
||||
if alias, ok := field.Tag.Lookup("excel"); ok {
|
||||
for _, recognizer := range a.Regconizers {
|
||||
if alias == recognizer.Tag && recognizer.MatchIndex != -1 {
|
||||
var matchValue string
|
||||
actualField := instance.Elem().FieldByName(field.Name)
|
||||
matchValue := cols[recognizer.MatchIndex]
|
||||
if recognizer.MatchIndex > len(cols)-1 {
|
||||
if recognizer.MustFill {
|
||||
errs = append(errs, ExcelAnalysisError{Row: rowIndex + 1, Col: recognizer.MatchIndex + 1, Err: AnalysisError{Err: errors.New("单元格内不能没有内容。")}})
|
||||
continue
|
||||
} else {
|
||||
matchValue = ""
|
||||
}
|
||||
} else {
|
||||
matchValue = cols[recognizer.MatchIndex]
|
||||
}
|
||||
switch field.Type.String() {
|
||||
case "string":
|
||||
actualField.Set(reflect.ValueOf(matchValue))
|
||||
@@ -154,7 +173,9 @@ func (a *ExcelAnalyzer[T]) Analysis(bean T) ([]T, []ExcelAnalysisError) {
|
||||
errs = append(errs, ExcelAnalysisError{Row: rowIndex + 1, Col: recognizer.MatchIndex + 1, Err: AnalysisError{Err: fmt.Errorf("单元格内容应为纯数字内容。%w", err)}})
|
||||
actualField.Set(reflect.ValueOf((nullValue)))
|
||||
} else {
|
||||
actualField.Set(reflect.ValueOf(decimalValue))
|
||||
value := decimal.NewNullDecimal(decimalValue)
|
||||
value.Valid = true
|
||||
actualField.Set(reflect.ValueOf(value))
|
||||
}
|
||||
}
|
||||
case "int64", "int":
|
||||
@@ -175,6 +196,54 @@ func (a *ExcelAnalyzer[T]) Analysis(bean T) ([]T, []ExcelAnalysisError) {
|
||||
} else {
|
||||
actualField.SetBool(false)
|
||||
}
|
||||
case "types.Date":
|
||||
if len(matchValue) == 0 {
|
||||
actualField.Set(reflect.ValueOf(types.NewEmptyDate()))
|
||||
} else {
|
||||
v, err := types.ParseDate(matchValue)
|
||||
if err != nil {
|
||||
errs = append(errs, ExcelAnalysisError{Row: rowIndex + 1, Col: recognizer.MatchIndex + 1, Err: AnalysisError{Err: fmt.Errorf("单元格内容应为日期格式。%w", err)}})
|
||||
actualField.Set(reflect.ValueOf(types.NewEmptyDate()))
|
||||
} else {
|
||||
actualField.Set(reflect.ValueOf(v))
|
||||
}
|
||||
}
|
||||
case "*types.Date":
|
||||
if len(matchValue) == 0 {
|
||||
actualField.Set(reflect.ValueOf(nil))
|
||||
} else {
|
||||
v, err := types.ParseDate(matchValue)
|
||||
if err != nil {
|
||||
errs = append(errs, ExcelAnalysisError{Row: rowIndex + 1, Col: recognizer.MatchIndex + 1, Err: AnalysisError{Err: fmt.Errorf("单元格内容应为日期格式。%w", err)}})
|
||||
actualField.Set(reflect.ValueOf(nil))
|
||||
} else {
|
||||
actualField.Set(reflect.ValueOf(&v))
|
||||
}
|
||||
}
|
||||
case "types.DateTime":
|
||||
if len(matchValue) == 0 {
|
||||
actualField.Set(reflect.ValueOf(types.NewEmptyDateTime()))
|
||||
} else {
|
||||
v, err := types.ParseDateTime(matchValue)
|
||||
if err != nil {
|
||||
errs = append(errs, ExcelAnalysisError{Row: rowIndex + 1, Col: recognizer.MatchIndex + 1, Err: AnalysisError{Err: fmt.Errorf("单元格内容应为日期时间格式。%w", err)}})
|
||||
actualField.Set(reflect.ValueOf(types.NewEmptyDateTime()))
|
||||
} else {
|
||||
actualField.Set(reflect.ValueOf(v))
|
||||
}
|
||||
}
|
||||
case "*types.DateTime":
|
||||
if len(matchValue) == 0 {
|
||||
actualField.Set(reflect.ValueOf(nil))
|
||||
} else {
|
||||
v, err := types.ParseDateTime(matchValue)
|
||||
if err != nil {
|
||||
errs = append(errs, ExcelAnalysisError{Row: rowIndex + 1, Col: recognizer.MatchIndex + 1, Err: AnalysisError{Err: fmt.Errorf("单元格内容应为日期时间格式。%w", err)}})
|
||||
actualField.Set(reflect.ValueOf(nil))
|
||||
} else {
|
||||
actualField.Set(reflect.ValueOf(&v))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
package excel
|
||||
|
||||
import (
|
||||
"electricity_bill_calc/model"
|
||||
"io"
|
||||
)
|
||||
|
||||
var (
|
||||
endUserNonPVRecognizers = []*ColumnRecognizer{
|
||||
{Pattern: []string{"电表编号"}, Tag: "meterId", MatchIndex: -1},
|
||||
{Pattern: []string{"本期", "(总)"}, Tag: "currentPeriodOverall", MatchIndex: -1},
|
||||
{Pattern: []string{"退补", "(总)"}, Tag: "adjustOverall", MatchIndex: -1},
|
||||
}
|
||||
endUserPVRecognizers = append(
|
||||
endUserNonPVRecognizers,
|
||||
&ColumnRecognizer{Pattern: []string{"本期", "(尖峰)"}, Tag: "currentPeriodCritical", MatchIndex: -1},
|
||||
&ColumnRecognizer{Pattern: []string{"本期", "(峰)"}, Tag: "currentPeriodPeak", MatchIndex: -1},
|
||||
&ColumnRecognizer{Pattern: []string{"本期", "(谷)"}, Tag: "currentPeriodValley", MatchIndex: -1},
|
||||
&ColumnRecognizer{Pattern: []string{"退补", "(尖峰)"}, Tag: "adjustCritical", MatchIndex: -1},
|
||||
&ColumnRecognizer{Pattern: []string{"退补", "(峰)"}, Tag: "adjustPeak", MatchIndex: -1},
|
||||
&ColumnRecognizer{Pattern: []string{"退补", "(谷)"}, Tag: "adjustValley", MatchIndex: -1},
|
||||
)
|
||||
)
|
||||
|
||||
func NewEndUserNonPVExcelAnalyzer(file io.Reader) (*ExcelAnalyzer[model.EndUserImport], error) {
|
||||
return NewExcelAnalyzer[model.EndUserImport](file, endUserNonPVRecognizers)
|
||||
}
|
||||
|
||||
func NewEndUserPVExcelAnalyzer(file io.Reader) (*ExcelAnalyzer[model.EndUserImport], error) {
|
||||
return NewExcelAnalyzer[model.EndUserImport](file, endUserPVRecognizers)
|
||||
}
|
||||
@@ -1,22 +1,139 @@
|
||||
package excel
|
||||
|
||||
import (
|
||||
"electricity_bill_calc/logger"
|
||||
"electricity_bill_calc/model"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/samber/lo"
|
||||
"github.com/xuri/excelize/v2"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
var meter04kVExcelRecognizers = []*ColumnRecognizer{
|
||||
{Pattern: []string{"表号"}, Tag: "code", MatchIndex: -1},
|
||||
{Pattern: []string{"户名"}, Tag: "name", MatchIndex: -1},
|
||||
{Pattern: []string{"户址"}, Tag: "address", MatchIndex: -1},
|
||||
{Pattern: []string{"联系人"}, Tag: "contact", MatchIndex: -1},
|
||||
{Pattern: []string{"电话"}, Tag: "phone", MatchIndex: -1},
|
||||
{Pattern: []string{"倍率"}, Tag: "ratio", MatchIndex: -1},
|
||||
{Pattern: []string{"序号"}, Tag: "seq", MatchIndex: -1},
|
||||
{Pattern: []string{"公用设备"}, Tag: "public", MatchIndex: -1},
|
||||
{Pattern: []string{"摊薄"}, Tag: "dilute", MatchIndex: -1},
|
||||
var meterArchiveRecognizers = []*ColumnRecognizer{
|
||||
{Pattern: [][]string{{"表号"}}, Tag: "code", MatchIndex: -1, MustFill: true},
|
||||
{Pattern: [][]string{{"表址", "地址", "户址"}}, Tag: "address", MatchIndex: -1},
|
||||
{Pattern: [][]string{{"类型"}}, Tag: "meterType", MatchIndex: -1, MustFill: true},
|
||||
{Pattern: [][]string{{"建筑"}}, Tag: "building", MatchIndex: -1},
|
||||
{Pattern: [][]string{{"楼层"}}, Tag: "onFloor", MatchIndex: -1},
|
||||
{Pattern: [][]string{{"面积"}}, Tag: "area", MatchIndex: -1},
|
||||
{Pattern: [][]string{{"倍率"}}, Tag: "ratio", MatchIndex: -1, MustFill: true},
|
||||
{Pattern: [][]string{{"序号"}}, Tag: "seq", MatchIndex: -1, MustFill: true},
|
||||
{Pattern: [][]string{{"抄表"}, {"时间", "日期"}}, Tag: "readAt", MatchIndex: -1, MustFill: true},
|
||||
{Pattern: [][]string{{"有功", "表底", "底数"}, {"总"}}, Tag: "overall", MatchIndex: -1, MustFill: true},
|
||||
{Pattern: [][]string{{"有功", "表底", "底数"}, {"尖"}}, Tag: "critical", MatchIndex: -1},
|
||||
{Pattern: [][]string{{"有功", "表底", "底数"}, {"峰"}}, Tag: "peak", MatchIndex: -1},
|
||||
{Pattern: [][]string{{"有功", "表底", "底数"}, {"平"}}, Tag: "flat", MatchIndex: -1},
|
||||
{Pattern: [][]string{{"有功", "表底", "底数"}, {"谷"}}, Tag: "valley", MatchIndex: -1},
|
||||
}
|
||||
|
||||
func NewMeterArchiveExcelAnalyzer(file io.Reader) (*ExcelAnalyzer[model.Meter04KV], error) {
|
||||
return NewExcelAnalyzer[model.Meter04KV](file, meter04kVExcelRecognizers)
|
||||
func NewMeterArchiveExcelAnalyzer(file io.Reader) (*ExcelAnalyzer[model.MeterImportRow], error) {
|
||||
return NewExcelAnalyzer[model.MeterImportRow](file, meterArchiveRecognizers)
|
||||
}
|
||||
|
||||
type MeterArchiveExcelTemplateGenerator struct {
|
||||
file *excelize.File
|
||||
log *zap.Logger
|
||||
}
|
||||
|
||||
func NewMeterArchiveExcelTemplateGenerator() *MeterArchiveExcelTemplateGenerator {
|
||||
return &MeterArchiveExcelTemplateGenerator{
|
||||
file: excelize.NewFile(),
|
||||
log: logger.Named("Excel", "MeterArchive"),
|
||||
}
|
||||
}
|
||||
|
||||
func (MeterArchiveExcelTemplateGenerator) titles() *[]interface{} {
|
||||
return &[]interface{}{
|
||||
"序号",
|
||||
"表址",
|
||||
"表号",
|
||||
"表计类型",
|
||||
"倍率",
|
||||
"所在建筑",
|
||||
"所在楼层",
|
||||
"辖盖面积",
|
||||
"抄表时间",
|
||||
"有功(总)",
|
||||
"有功(尖)",
|
||||
"有功(峰)",
|
||||
"有功(平)",
|
||||
"有功(谷)",
|
||||
}
|
||||
}
|
||||
|
||||
func (g *MeterArchiveExcelTemplateGenerator) Close() {
|
||||
g.file.Close()
|
||||
}
|
||||
|
||||
func (g MeterArchiveExcelTemplateGenerator) WriteTo(w io.Writer) (int64, error) {
|
||||
return g.file.WriteTo(w)
|
||||
}
|
||||
|
||||
func (g *MeterArchiveExcelTemplateGenerator) WriteTemplateData(buildings []*model.ParkBuilding) error {
|
||||
var err error
|
||||
defaultSheet := g.file.GetSheetName(0)
|
||||
g.log.Debug("选定默认输出表格", zap.String("sheet", defaultSheet))
|
||||
err = g.file.SetColWidth(defaultSheet, "B", "I", 20)
|
||||
if err != nil {
|
||||
g.log.Error("未能设定长型列宽。", zap.Error(err))
|
||||
return fmt.Errorf("未能设定长型列宽,%w", err)
|
||||
}
|
||||
err = g.file.SetColWidth(defaultSheet, "J", "N", 15)
|
||||
if err != nil {
|
||||
g.log.Error("未能设定短型列宽。", zap.Error(err))
|
||||
return fmt.Errorf("未能设定短型列宽,%w", err)
|
||||
}
|
||||
err = g.file.SetSheetRow(defaultSheet, "A1", g.titles())
|
||||
if err != nil {
|
||||
g.log.Error("未能输出模板标题。", zap.Error(err))
|
||||
return fmt.Errorf("未能输出模板标题,%w", err)
|
||||
}
|
||||
err = g.file.SetRowHeight(defaultSheet, 1, 20)
|
||||
if err != nil {
|
||||
g.log.Error("未能设定标题行高度。", zap.Error(err))
|
||||
return fmt.Errorf("未能设定标题行高度,%w", err)
|
||||
}
|
||||
|
||||
dateTimeExp := "yyyy-mm-dd hh:mm"
|
||||
dateTimeColStyle, err := g.file.NewStyle(&excelize.Style{
|
||||
CustomNumFmt: &dateTimeExp,
|
||||
})
|
||||
if err != nil {
|
||||
g.log.Error("未能创建日期时间格式。", zap.Error(err))
|
||||
return fmt.Errorf("未能创建日期时间格式,%w", err)
|
||||
}
|
||||
g.file.SetCellStyle(defaultSheet, "I2", "I1048576", dateTimeColStyle)
|
||||
|
||||
numExp := "0.0000"
|
||||
numColStyle, err := g.file.NewStyle(&excelize.Style{
|
||||
CustomNumFmt: &numExp,
|
||||
})
|
||||
if err != nil {
|
||||
g.log.Error("未能创建抄表数字格式。", zap.Error(err))
|
||||
return fmt.Errorf("未能创建抄表数字格式,%w", err)
|
||||
}
|
||||
g.file.SetCellStyle(defaultSheet, "J2", "N1048576", numColStyle)
|
||||
|
||||
meterInstallationTypeValidation := excelize.NewDataValidation(false)
|
||||
meterInstallationTypeValidation.SetDropList([]string{"商户表", "公共表", "楼道表"})
|
||||
meterInstallationTypeValidation.Sqref = "D2:D1048576"
|
||||
err = g.file.AddDataValidation(defaultSheet, meterInstallationTypeValidation)
|
||||
if err != nil {
|
||||
g.log.Error("未能设定表计类型选择器。", zap.Error(err))
|
||||
return fmt.Errorf("未能设定表计类型选择器,%w", err)
|
||||
}
|
||||
buildingValidation := excelize.NewDataValidation(true)
|
||||
buildingNames := lo.Map(buildings, func(b *model.ParkBuilding, _ int) string {
|
||||
return b.Name
|
||||
})
|
||||
buildingValidation.SetDropList(buildingNames)
|
||||
buildingValidation.Sqref = "F2:F1048576"
|
||||
err = g.file.AddDataValidation(defaultSheet, buildingValidation)
|
||||
if err != nil {
|
||||
g.log.Error("未能设定所在建筑选择器。", zap.Error(err))
|
||||
return fmt.Errorf("未能设定所在建筑选择器,%w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
package excel
|
||||
|
||||
import (
|
||||
"electricity_bill_calc/model"
|
||||
"io"
|
||||
|
||||
"github.com/xuri/excelize/v2"
|
||||
)
|
||||
|
||||
type MeterNonPVExcelTemplateGenerator struct {
|
||||
file *excelize.File
|
||||
}
|
||||
|
||||
// 生成峰谷计量抄表Excel模板
|
||||
func NewMeterNonPVExcelTemplateGenerator() *MeterNonPVExcelTemplateGenerator {
|
||||
return &MeterNonPVExcelTemplateGenerator{
|
||||
file: excelize.NewFile(),
|
||||
}
|
||||
}
|
||||
|
||||
func (MeterNonPVExcelTemplateGenerator) titles() []interface{} {
|
||||
return []interface{}{
|
||||
"序号",
|
||||
"用户名称",
|
||||
"户址",
|
||||
"电表编号",
|
||||
"倍率",
|
||||
"上期表底(总)",
|
||||
"本期表底(总)",
|
||||
"退补电量(总)",
|
||||
}
|
||||
}
|
||||
|
||||
func (t *MeterNonPVExcelTemplateGenerator) Close() {
|
||||
t.file.Close()
|
||||
}
|
||||
|
||||
func (t MeterNonPVExcelTemplateGenerator) WriteTo(w io.Writer) (int64, error) {
|
||||
return t.file.WriteTo(w)
|
||||
}
|
||||
|
||||
func (t *MeterNonPVExcelTemplateGenerator) WriteMeterData(meters []model.EndUserDetail) error {
|
||||
defaultSheet := t.file.GetSheetName(0)
|
||||
stream, err := t.file.NewStreamWriter(defaultSheet)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
firstCell, err := excelize.CoordinatesToCellName(1, 1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
stream.SetColWidth(2, 4, 20)
|
||||
stream.SetColWidth(6, 8, 15)
|
||||
stream.SetRow(firstCell, t.titles(), excelize.RowOpts{Height: 20})
|
||||
|
||||
for index, meter := range meters {
|
||||
firstCell, err := excelize.CoordinatesToCellName(1, index+2)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
customerName := ""
|
||||
if meter.CustomerName != nil {
|
||||
customerName = *meter.CustomerName
|
||||
}
|
||||
customerAddress := ""
|
||||
if meter.Address != nil {
|
||||
customerAddress = *meter.Address
|
||||
}
|
||||
if err = stream.SetRow(
|
||||
firstCell,
|
||||
[]interface{}{
|
||||
meter.Seq,
|
||||
customerName,
|
||||
customerAddress,
|
||||
meter.MeterId,
|
||||
meter.Ratio,
|
||||
meter.LastPeriodOverall,
|
||||
nil,
|
||||
nil,
|
||||
},
|
||||
excelize.RowOpts{Height: 15},
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err = stream.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
package excel
|
||||
|
||||
import (
|
||||
"electricity_bill_calc/model"
|
||||
"io"
|
||||
|
||||
"github.com/xuri/excelize/v2"
|
||||
)
|
||||
|
||||
type MeterPVExcelTemplateGenerator struct {
|
||||
file *excelize.File
|
||||
}
|
||||
|
||||
// 生成峰谷计量抄表Excel模板
|
||||
func NewMeterPVExcelTemplateGenerator() *MeterPVExcelTemplateGenerator {
|
||||
return &MeterPVExcelTemplateGenerator{
|
||||
file: excelize.NewFile(),
|
||||
}
|
||||
}
|
||||
|
||||
func (MeterPVExcelTemplateGenerator) titles() []interface{} {
|
||||
return []interface{}{
|
||||
"序号",
|
||||
"用户名称",
|
||||
"户址",
|
||||
"电表编号",
|
||||
"倍率",
|
||||
"上期表底(总)",
|
||||
"本期表底(总)",
|
||||
"上期表底(尖峰)",
|
||||
"本期表底(尖峰)",
|
||||
"上期表底(峰)",
|
||||
"本期表底(峰)",
|
||||
"上期表底(谷)",
|
||||
"本期表底(谷)",
|
||||
"退补电量(总)",
|
||||
"退补电量(尖峰)",
|
||||
"退补电量(峰)",
|
||||
"退补电量(谷)",
|
||||
}
|
||||
}
|
||||
|
||||
func (t *MeterPVExcelTemplateGenerator) Close() {
|
||||
t.file.Close()
|
||||
}
|
||||
|
||||
func (t MeterPVExcelTemplateGenerator) WriteTo(w io.Writer) (int64, error) {
|
||||
return t.file.WriteTo(w)
|
||||
}
|
||||
|
||||
func (t *MeterPVExcelTemplateGenerator) WriteMeterData(meters []model.EndUserDetail) error {
|
||||
defaultSheet := t.file.GetSheetName(0)
|
||||
stream, err := t.file.NewStreamWriter(defaultSheet)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
firstCell, err := excelize.CoordinatesToCellName(1, 1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
stream.SetColWidth(2, 4, 20)
|
||||
stream.SetColWidth(6, 17, 15)
|
||||
stream.SetRow(firstCell, t.titles(), excelize.RowOpts{Height: 20})
|
||||
|
||||
for index, meter := range meters {
|
||||
firstCell, err := excelize.CoordinatesToCellName(1, index+2)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
customerName := ""
|
||||
if meter.CustomerName != nil {
|
||||
customerName = *meter.CustomerName
|
||||
}
|
||||
customerAddress := ""
|
||||
if meter.Address != nil {
|
||||
customerAddress = *meter.Address
|
||||
}
|
||||
if err = stream.SetRow(
|
||||
firstCell,
|
||||
[]interface{}{
|
||||
meter.Seq,
|
||||
customerName,
|
||||
customerAddress,
|
||||
meter.MeterId,
|
||||
meter.Ratio,
|
||||
meter.LastPeriodOverall,
|
||||
nil,
|
||||
meter.LastPeriodCritical,
|
||||
nil,
|
||||
meter.LastPeriodPeak,
|
||||
nil,
|
||||
meter.LastPeriodValley,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
},
|
||||
excelize.RowOpts{Height: 15},
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err = stream.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
131
excel/meter_reading.go
Normal file
131
excel/meter_reading.go
Normal file
@@ -0,0 +1,131 @@
|
||||
package excel
|
||||
|
||||
import (
|
||||
"electricity_bill_calc/logger"
|
||||
"electricity_bill_calc/model"
|
||||
"electricity_bill_calc/tools"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/xuri/excelize/v2"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
var meterReadingsRecognizers = []*ColumnRecognizer{
|
||||
{Pattern: [][]string{{"表", "表计"}, {"编号"}}, Tag: "code", MatchIndex: -1, MustFill: true},
|
||||
{Pattern: [][]string{{"抄表", "结束"}, {"时间", "日期"}}, Tag: "readAt", MatchIndex: -1, MustFill: true},
|
||||
{Pattern: [][]string{{"用电", "有功", "表底", "底数"}, {"总", "量"}}, Tag: "overall", MatchIndex: -1, MustFill: true},
|
||||
{Pattern: [][]string{{"有功", "表底", "底数"}, {"尖"}}, Tag: "critical", MatchIndex: -1},
|
||||
{Pattern: [][]string{{"有功", "表底", "底数"}, {"峰"}}, Tag: "peak", MatchIndex: -1},
|
||||
{Pattern: [][]string{{"有功", "表底", "底数"}, {"谷"}}, Tag: "valley", MatchIndex: -1},
|
||||
}
|
||||
|
||||
func NewMeterReadingsExcelAnalyzer(file io.Reader) (*ExcelAnalyzer[model.ReadingImportRow], error) {
|
||||
return NewExcelAnalyzer[model.ReadingImportRow](file, meterReadingsRecognizers)
|
||||
}
|
||||
|
||||
type MeterReadingsExcelTemplateGenerator struct {
|
||||
file *excelize.File
|
||||
log *zap.Logger
|
||||
}
|
||||
|
||||
func NewMeterReadingsExcelTemplateGenerator() *MeterReadingsExcelTemplateGenerator {
|
||||
return &MeterReadingsExcelTemplateGenerator{
|
||||
file: excelize.NewFile(),
|
||||
log: logger.Named("Excel", "MeterReadings"),
|
||||
}
|
||||
}
|
||||
|
||||
func (MeterReadingsExcelTemplateGenerator) titles() *[]interface{} {
|
||||
return &[]interface{}{
|
||||
"抄表序号",
|
||||
"抄表时间",
|
||||
"表计编号",
|
||||
"表计名称",
|
||||
"商户名称",
|
||||
"倍率",
|
||||
"有功(总)",
|
||||
"有功(尖)",
|
||||
"有功(峰)",
|
||||
"有功(谷)",
|
||||
}
|
||||
}
|
||||
|
||||
func (g MeterReadingsExcelTemplateGenerator) Close() {
|
||||
g.file.Close()
|
||||
}
|
||||
|
||||
func (g MeterReadingsExcelTemplateGenerator) WriteTo(w io.Writer) (int64, error) {
|
||||
return g.file.WriteTo(w)
|
||||
}
|
||||
|
||||
func (g MeterReadingsExcelTemplateGenerator) WriteTemplateData(meters []*model.SimpleMeterDocument) error {
|
||||
var err error
|
||||
defaultSheet := g.file.GetSheetName(0)
|
||||
g.log.Debug("选定默认输出表格", zap.String("sheet", defaultSheet))
|
||||
err = g.file.SetColWidth(defaultSheet, "A", "E", 30)
|
||||
if err != nil {
|
||||
g.log.Error("未能设定长型单元格的宽度。", zap.Error(err))
|
||||
return fmt.Errorf("未能设定长型单元格的宽度,%w", err)
|
||||
}
|
||||
err = g.file.SetColWidth(defaultSheet, "F", "F", 10)
|
||||
if err != nil {
|
||||
g.log.Error("未能设定倍率单元格的宽度。", zap.Error(err))
|
||||
return fmt.Errorf("未能设定倍率单元格的宽度,%w", err)
|
||||
}
|
||||
err = g.file.SetColWidth(defaultSheet, "G", "J", 20)
|
||||
if err != nil {
|
||||
g.log.Error("未能设定短型单元格的宽度。", zap.Error(err))
|
||||
return fmt.Errorf("未能设定短型单元格的宽度,%w", err)
|
||||
}
|
||||
err = g.file.SetSheetRow(defaultSheet, "A1", g.titles())
|
||||
if err != nil {
|
||||
g.log.Error("未能输出模板标题。", zap.Error(err))
|
||||
return fmt.Errorf("未能输出模板标题,%w", err)
|
||||
}
|
||||
err = g.file.SetRowHeight(defaultSheet, 1, 30)
|
||||
if err != nil {
|
||||
g.log.Error("未能设定标题行的高度。", zap.Error(err))
|
||||
return fmt.Errorf("未能设定标题行的高度,%w", err)
|
||||
}
|
||||
|
||||
dateTimeExp := "yyyy-mm-dd hh:mm"
|
||||
dateTimeColStyle, err := g.file.NewStyle(&excelize.Style{
|
||||
CustomNumFmt: &dateTimeExp,
|
||||
})
|
||||
if err != nil {
|
||||
g.log.Error("未能创建日期时间格式。", zap.Error(err))
|
||||
return fmt.Errorf("未能创建日期时间格式,%w", err)
|
||||
}
|
||||
endCellCoord, _ := excelize.CoordinatesToCellName(2, len(meters)+1)
|
||||
g.file.SetCellStyle(defaultSheet, "B2", endCellCoord, dateTimeColStyle)
|
||||
|
||||
numExp := "0.0000"
|
||||
numColStyle, err := g.file.NewStyle(&excelize.Style{
|
||||
CustomNumFmt: &numExp,
|
||||
})
|
||||
if err != nil {
|
||||
g.log.Error("未能创建抄表数字格式。", zap.Error(err))
|
||||
return fmt.Errorf("未能创建抄表数字格式,%w", err)
|
||||
}
|
||||
endCellCoord, _ = excelize.CoordinatesToCellName(9, len(meters)+1)
|
||||
g.file.SetCellStyle(defaultSheet, "F2", endCellCoord, numColStyle)
|
||||
|
||||
for i, meter := range meters {
|
||||
cellCoord, _ := excelize.CoordinatesToCellName(1, i+2)
|
||||
ratio, _ := meter.Ratio.Float64()
|
||||
if err := g.file.SetSheetRow(defaultSheet, cellCoord, &[]interface{}{
|
||||
meter.Seq,
|
||||
"",
|
||||
meter.Code,
|
||||
tools.DefaultTo(meter.Address, ""),
|
||||
tools.DefaultTo(meter.TenementName, ""),
|
||||
ratio,
|
||||
}); err != nil {
|
||||
g.log.Error("向模板写入数据出现错误。", zap.Error(err))
|
||||
return fmt.Errorf("向模板写入数据出现错误,%w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
@@ -31,5 +31,5 @@ func NewUnauthorizedError(msg string) *UnauthorizedError {
|
||||
}
|
||||
|
||||
func (e UnauthorizedError) Error() string {
|
||||
return fmt.Sprintf("Unauthorized: %s", e.Message)
|
||||
return fmt.Sprintf("用户未获得授权: %s", e.Message)
|
||||
}
|
||||
|
||||
@@ -15,5 +15,5 @@ func NewIllegalArgumentsError(msg string, arguments ...string) IllegalArgumentsE
|
||||
}
|
||||
|
||||
func (e IllegalArgumentsError) Error() string {
|
||||
return fmt.Sprintf("Illegal Arguments, %s", e.Message)
|
||||
return fmt.Sprintf("使用了非法参数, %s", e.Message)
|
||||
}
|
||||
|
||||
@@ -15,5 +15,5 @@ func NewImproperOperateError(msg string, arguments ...string) ImproperOperateErr
|
||||
}
|
||||
|
||||
func (e ImproperOperateError) Error() string {
|
||||
return fmt.Sprintf("Improper Operate, %s", e.Message)
|
||||
return fmt.Sprintf("操作不恰当, %s", e.Message)
|
||||
}
|
||||
|
||||
16
exceptions/insufficient_data.go
Normal file
16
exceptions/insufficient_data.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package exceptions
|
||||
|
||||
import "fmt"
|
||||
|
||||
type InsufficientDataError struct {
|
||||
Field string
|
||||
Message string
|
||||
}
|
||||
|
||||
func NewInsufficientDataError(field, msg string) *InsufficientDataError {
|
||||
return &InsufficientDataError{Field: field, Message: msg}
|
||||
}
|
||||
|
||||
func (e InsufficientDataError) Error() string {
|
||||
return fmt.Sprintf("字段 [%s] 数据不足,%s", e.Field, e.Message)
|
||||
}
|
||||
@@ -11,7 +11,7 @@ func NewNotFoundError(msg string) *NotFoundError {
|
||||
}
|
||||
|
||||
func NewNotFoundErrorFromError(msg string, err error) *NotFoundError {
|
||||
return &NotFoundError{Message: fmt.Sprintf("%s,%v", msg, err)}
|
||||
return &NotFoundError{Message: fmt.Sprintf("所需数据未找到,%s,%v", msg, err)}
|
||||
}
|
||||
|
||||
func (e NotFoundError) Error() string {
|
||||
|
||||
@@ -1,11 +1,130 @@
|
||||
package exceptions
|
||||
|
||||
type UnsuccessfulOperationError struct{}
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
func NewUnsuccessfulOperationError() *UnsuccessfulOperationError {
|
||||
return &UnsuccessfulOperationError{}
|
||||
type OperationType int16
|
||||
|
||||
const (
|
||||
OPERATE_CREATE OperationType = iota
|
||||
OPERATE_UPDATE
|
||||
OPERATE_DELETE
|
||||
OEPRATE_QUERY
|
||||
OPERATE_CALCULATE
|
||||
OPERATE_DB
|
||||
OPERATE_DB_TRANSACTION
|
||||
OPERATE_CUSTOM OperationType = 98
|
||||
OPERATE_OTHER OperationType = 99
|
||||
)
|
||||
|
||||
type UnsuccessfulOperationError struct {
|
||||
Operate OperationType
|
||||
Description string
|
||||
Message string
|
||||
}
|
||||
|
||||
func (UnsuccessfulOperationError) Error() string {
|
||||
return "Unsuccessful Operation"
|
||||
func NewUnsuccessfulOperationError(oeprate OperationType, describe, message string) *UnsuccessfulOperationError {
|
||||
return &UnsuccessfulOperationError{
|
||||
Operate: oeprate,
|
||||
Description: describe,
|
||||
Message: message,
|
||||
}
|
||||
}
|
||||
|
||||
func NewUnsuccessCreateError(message string) *UnsuccessfulOperationError {
|
||||
return &UnsuccessfulOperationError{
|
||||
Operate: OPERATE_CREATE,
|
||||
Message: message,
|
||||
}
|
||||
}
|
||||
|
||||
func NewUnsuccessUpdateError(message string) *UnsuccessfulOperationError {
|
||||
return &UnsuccessfulOperationError{
|
||||
Operate: OPERATE_UPDATE,
|
||||
Message: message,
|
||||
}
|
||||
}
|
||||
|
||||
func NewUnsuccessDeleteError(message string) *UnsuccessfulOperationError {
|
||||
return &UnsuccessfulOperationError{
|
||||
Operate: OPERATE_DELETE,
|
||||
Message: message,
|
||||
}
|
||||
}
|
||||
|
||||
func NewUnsuccessQueryError(message string) *UnsuccessfulOperationError {
|
||||
return &UnsuccessfulOperationError{
|
||||
Operate: OEPRATE_QUERY,
|
||||
Message: message,
|
||||
}
|
||||
}
|
||||
|
||||
func NewUnsuccessCalculateError(message string) *UnsuccessfulOperationError {
|
||||
return &UnsuccessfulOperationError{
|
||||
Operate: OPERATE_CALCULATE,
|
||||
Message: message,
|
||||
}
|
||||
}
|
||||
|
||||
func NewUnsuccessDBError(message string) *UnsuccessfulOperationError {
|
||||
return &UnsuccessfulOperationError{
|
||||
Operate: OPERATE_DB,
|
||||
Message: message,
|
||||
}
|
||||
}
|
||||
|
||||
func NewUnsuccessDBTransactionError(message string) *UnsuccessfulOperationError {
|
||||
return &UnsuccessfulOperationError{
|
||||
Operate: OPERATE_DB_TRANSACTION,
|
||||
Message: message,
|
||||
}
|
||||
}
|
||||
|
||||
func NewUnsuccessCustomError(describe, message string) *UnsuccessfulOperationError {
|
||||
return &UnsuccessfulOperationError{
|
||||
Operate: OPERATE_CUSTOM,
|
||||
Description: describe,
|
||||
Message: message,
|
||||
}
|
||||
}
|
||||
|
||||
func NewUnsuccessOtherError(message string) *UnsuccessfulOperationError {
|
||||
return &UnsuccessfulOperationError{
|
||||
Operate: OPERATE_OTHER,
|
||||
Message: message,
|
||||
}
|
||||
}
|
||||
|
||||
func (e UnsuccessfulOperationError) Error() string {
|
||||
var builder strings.Builder
|
||||
switch e.Operate {
|
||||
case OPERATE_CREATE:
|
||||
builder.WriteString("创建")
|
||||
case OPERATE_UPDATE:
|
||||
builder.WriteString("更新")
|
||||
case OPERATE_DELETE:
|
||||
builder.WriteString("删除")
|
||||
case OEPRATE_QUERY:
|
||||
builder.WriteString("查询")
|
||||
case OPERATE_CALCULATE:
|
||||
builder.WriteString("计算")
|
||||
case OPERATE_DB:
|
||||
builder.WriteString("数据库")
|
||||
case OPERATE_DB_TRANSACTION:
|
||||
builder.WriteString("数据库事务")
|
||||
case OPERATE_CUSTOM:
|
||||
builder.WriteString(e.Description)
|
||||
case OPERATE_OTHER:
|
||||
builder.WriteString("其他")
|
||||
default:
|
||||
builder.WriteString("未知")
|
||||
}
|
||||
builder.WriteString("操作不成功,")
|
||||
if len(e.Message) > 0 {
|
||||
builder.WriteString(e.Message)
|
||||
} else {
|
||||
builder.WriteString("未知原因")
|
||||
}
|
||||
return builder.String()
|
||||
}
|
||||
|
||||
16
global/context.go
Normal file
16
global/context.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package global
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 生成一个超时时间为5秒的倍率的上下文,如果不传递任何值,默认生成6倍的上下文,即超时时间为30秒。
|
||||
func TimeoutContext(multiply ...int64) (context.Context, context.CancelFunc) {
|
||||
var ratio int64 = 6
|
||||
if len(multiply) > 0 {
|
||||
ratio = multiply[0]
|
||||
}
|
||||
timeout := time.Duration(ratio*5) * time.Second
|
||||
return context.WithTimeout(context.TODO(), timeout)
|
||||
}
|
||||
98
global/db.go
98
global/db.go
@@ -1,55 +1,91 @@
|
||||
package global
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"electricity_bill_calc/config"
|
||||
"electricity_bill_calc/logger"
|
||||
|
||||
// _ "github.com/lib/pq"
|
||||
_ "github.com/jackc/pgx/v5/stdlib"
|
||||
"xorm.io/xorm"
|
||||
"xorm.io/xorm/log"
|
||||
"github.com/jackc/pgx/v5"
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
"github.com/samber/lo"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
var (
|
||||
DBConn *xorm.Engine
|
||||
DB *pgxpool.Pool
|
||||
)
|
||||
|
||||
func SetupDatabaseConnection() error {
|
||||
var err error
|
||||
// 以下连接方式是采用pgx驱动的时候使用的。
|
||||
DBConn, err = xorm.NewEngine("pgx", fmt.Sprintf(
|
||||
"postgresql://%s:%s@%s:%d/%s?sslmode=disable&",
|
||||
connString := fmt.Sprintf(
|
||||
"postgres://%s:%s@%s:%d/%s?sslmode=disable&connect_timeout=%d&application_name=%s&pool_max_conns=%d&pool_min_conns=%d&pool_max_conn_lifetime=%s&pool_max_conn_idle_time=%s&pool_health_check_period=%s",
|
||||
config.DatabaseSettings.User,
|
||||
config.DatabaseSettings.Pass,
|
||||
config.DatabaseSettings.Host,
|
||||
config.DatabaseSettings.Port,
|
||||
config.DatabaseSettings.DB,
|
||||
))
|
||||
// 以下连接方式是采用lib/pq驱动的时候使用的。
|
||||
// DBConn, err = xorm.NewEngine("postgres", fmt.Sprintf(
|
||||
// "host=%s user=%s password=%s dbname=%s port=%d sslmode=disable TimeZone=Asia/Shanghai connect_timeout=0",
|
||||
// config.DatabaseSettings.Host,
|
||||
// config.DatabaseSettings.User,
|
||||
// config.DatabaseSettings.Pass,
|
||||
// config.DatabaseSettings.DB,
|
||||
// config.DatabaseSettings.Port,
|
||||
// ))
|
||||
0,
|
||||
"elec_service_go",
|
||||
config.DatabaseSettings.MaxOpenConns,
|
||||
config.DatabaseSettings.MaxIdleConns,
|
||||
"60m",
|
||||
"10m",
|
||||
"10s",
|
||||
)
|
||||
poolConfig, err := pgxpool.ParseConfig(connString)
|
||||
if err != nil {
|
||||
logger.Named("DB INIT").Error("数据库连接初始化失败。", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
|
||||
DBConn.Ping()
|
||||
DBConn.SetMaxIdleConns(config.DatabaseSettings.MaxIdleConns)
|
||||
DBConn.SetMaxOpenConns(config.DatabaseSettings.MaxOpenConns)
|
||||
DBConn.SetConnMaxLifetime(60 * time.Second)
|
||||
|
||||
if strings.ToLower(config.ServerSettings.RunMode) == "debug" {
|
||||
DBConn.ShowSQL(true)
|
||||
DBConn.Logger().SetLevel(log.LOG_DEBUG)
|
||||
}
|
||||
poolConfig.ConnConfig.Tracer = QueryLogger{logger: logger.Named("PG")}
|
||||
DB, _ = pgxpool.NewWithConfig(context.Background(), poolConfig)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type QueryLogger struct {
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
func (ql QueryLogger) TraceQueryStart(ctx context.Context, conn *pgx.Conn, data pgx.TraceQueryStartData) context.Context {
|
||||
ql.logger.Info(fmt.Sprintf("将要执行查询: %s", data.SQL))
|
||||
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
|
||||
}
|
||||
|
||||
func (ql QueryLogger) TraceQueryEnd(ctx context.Context, conn *pgx.Conn, data pgx.TraceQueryEndData) {
|
||||
var logFunc func(string, ...zap.Field)
|
||||
var templateString string
|
||||
if data.Err != nil {
|
||||
logFunc = ql.logger.Error
|
||||
templateString = "命令 [%s] 执行失败。"
|
||||
} else {
|
||||
logFunc = ql.logger.Info
|
||||
templateString = "命令 [%s] 执行成功。"
|
||||
}
|
||||
switch {
|
||||
case data.CommandTag.Update():
|
||||
fallthrough
|
||||
case data.CommandTag.Delete():
|
||||
fallthrough
|
||||
case data.CommandTag.Insert():
|
||||
logFunc(
|
||||
fmt.Sprintf(templateString, data.CommandTag.String()),
|
||||
zap.Error(data.Err),
|
||||
zap.Any("affected", data.CommandTag.RowsAffected()))
|
||||
case data.CommandTag.Select():
|
||||
logFunc(
|
||||
fmt.Sprintf(templateString, data.CommandTag.String()),
|
||||
zap.Error(data.Err))
|
||||
default:
|
||||
logFunc(
|
||||
fmt.Sprintf(templateString, data.CommandTag.String()),
|
||||
zap.Error(data.Err))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,22 +9,25 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
RedisConn rueidis.Client
|
||||
Ctx = context.Background()
|
||||
Rd rueidis.Client
|
||||
Ctx = context.Background()
|
||||
)
|
||||
|
||||
func SetupRedisConnection() error {
|
||||
var err error
|
||||
RedisConn, err = rueidis.NewClient(rueidis.ClientOption{
|
||||
InitAddress: []string{fmt.Sprintf("%s:%d", config.RedisSettings.Host, config.RedisSettings.Port)},
|
||||
Password: config.RedisSettings.Password,
|
||||
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: "",
|
||||
SelectDB: config.RedisSettings.DB,
|
||||
DisableCache:true,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pingCmd := RedisConn.B().Ping().Build()
|
||||
result := RedisConn.Do(Ctx, pingCmd)
|
||||
pingCmd := Rd.B().Ping().Build()
|
||||
result := Rd.Do(Ctx, pingCmd)
|
||||
if result.Error() != nil {
|
||||
return result.Error()
|
||||
}
|
||||
|
||||
97
go.mod
97
go.mod
@@ -4,62 +4,83 @@ go 1.19
|
||||
|
||||
require (
|
||||
github.com/deckarep/golang-set/v2 v2.1.0
|
||||
github.com/fufuok/utils v0.7.13
|
||||
github.com/gin-gonic/gin v1.8.1
|
||||
github.com/fufuok/utils v0.10.2
|
||||
github.com/georgysavva/scany/v2 v2.0.0
|
||||
github.com/gofiber/fiber/v2 v2.46.0
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/jackc/pgx/v5 v5.0.0-beta.1
|
||||
github.com/jackc/pgx/v5 v5.3.1
|
||||
github.com/jinzhu/copier v0.3.5
|
||||
github.com/liamylian/jsontime/v2 v2.0.0
|
||||
github.com/mozillazg/go-pinyin v0.19.0
|
||||
github.com/rueian/rueidis v0.0.73
|
||||
github.com/samber/lo v1.27.0
|
||||
github.com/mozillazg/go-pinyin v0.20.0
|
||||
github.com/rueian/rueidis v0.0.100
|
||||
github.com/samber/lo v1.38.1
|
||||
github.com/shopspring/decimal v1.3.1
|
||||
github.com/spf13/viper v1.12.0
|
||||
github.com/xuri/excelize/v2 v2.6.1
|
||||
xorm.io/builder v0.3.12
|
||||
xorm.io/xorm v1.3.1
|
||||
github.com/spf13/viper v1.16.0
|
||||
github.com/valyala/fasthttp v1.47.0
|
||||
github.com/xuri/excelize/v2 v2.7.1
|
||||
go.uber.org/zap v1.24.0
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/fsnotify/fsnotify v1.5.4 // indirect
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/go-playground/locales v0.14.0 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.0 // indirect
|
||||
github.com/go-playground/validator/v10 v10.11.0 // indirect
|
||||
github.com/goccy/go-json v0.9.10 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/andybalholm/brotli v1.0.5 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // 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 (
|
||||
github.com/doug-martin/goqu/v9 v9.18.0
|
||||
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/leodido/go-urn v1.2.1 // indirect
|
||||
github.com/magiconair/properties v1.8.6 // indirect
|
||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||
github.com/magiconair/properties v1.8.7 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
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/onsi/ginkgo v1.16.5 // indirect
|
||||
github.com/pelletier/go-toml v1.9.5 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.0.2 // 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
|
||||
github.com/spf13/afero v1.8.2 // indirect
|
||||
github.com/spf13/cast v1.5.0 // indirect
|
||||
github.com/spf13/afero v1.9.5 // indirect
|
||||
github.com/spf13/cast v1.5.1 // indirect
|
||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/subosito/gotenv v1.3.0 // indirect
|
||||
github.com/syndtr/goleveldb v1.0.0 // indirect
|
||||
github.com/ugorji/go/codec v1.2.7 // indirect
|
||||
github.com/xuri/efp v0.0.0-20220603152613-6918739fd470 // indirect
|
||||
github.com/xuri/nfp v0.0.0-20220409054826-5e722a1d9e22 // indirect
|
||||
golang.org/x/crypto v0.0.0-20220817201139-bc19a97f63c8 // indirect
|
||||
golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 // indirect
|
||||
golang.org/x/net v0.0.0-20220812174116-3211cb980234 // indirect
|
||||
golang.org/x/sys v0.0.0-20220808155132-1c4a2a72c664 // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
google.golang.org/protobuf v1.28.1 // indirect
|
||||
gopkg.in/ini.v1 v1.66.4 // 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
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
golang.org/x/crypto v0.10.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 // indirect
|
||||
golang.org/x/net v0.10.0 // indirect
|
||||
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
|
||||
)
|
||||
|
||||
184
logger/logger.go
Normal file
184
logger/logger.go
Normal file
@@ -0,0 +1,184 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"electricity_bill_calc/types"
|
||||
"os"
|
||||
|
||||
"github.com/shopspring/decimal"
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
)
|
||||
|
||||
var (
|
||||
logger *zap.Logger
|
||||
sugaredLogger *zap.SugaredLogger
|
||||
)
|
||||
|
||||
func init() {
|
||||
consoleWriterSync := zapcore.AddSync(os.Stderr)
|
||||
rollingWriterSync := zapcore.AddSync(newRollingWriter())
|
||||
|
||||
consoleEncoderConfig := zap.NewProductionEncoderConfig()
|
||||
consoleEncoderConfig.EncodeTime = zapcore.RFC3339TimeEncoder
|
||||
consoleEncoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder
|
||||
|
||||
jsonEncoderConfig := zap.NewProductionEncoderConfig()
|
||||
jsonEncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
|
||||
|
||||
consoleEncoder := zapcore.NewConsoleEncoder(consoleEncoderConfig)
|
||||
jsonEncoder := zapcore.NewJSONEncoder(jsonEncoderConfig)
|
||||
core := zapcore.NewTee(
|
||||
zapcore.NewCore(
|
||||
consoleEncoder,
|
||||
consoleWriterSync,
|
||||
zapcore.DebugLevel,
|
||||
),
|
||||
zapcore.NewCore(
|
||||
jsonEncoder,
|
||||
rollingWriterSync,
|
||||
zapcore.DebugLevel,
|
||||
),
|
||||
)
|
||||
|
||||
logger = zap.New(core).Named("App")
|
||||
sugaredLogger = logger.Sugar()
|
||||
logger.Info("日志系统初始化完成。")
|
||||
}
|
||||
|
||||
func GetLogger() *zap.Logger {
|
||||
return logger
|
||||
}
|
||||
|
||||
func Panic(msg string, fields ...zap.Field) {
|
||||
logger.Panic(msg, fields...)
|
||||
}
|
||||
|
||||
func Fatal(msg string, fields ...zap.Field) {
|
||||
logger.Fatal(msg, fields...)
|
||||
}
|
||||
|
||||
func Error(msg string, fields ...zap.Field) {
|
||||
logger.Error(msg, fields...)
|
||||
}
|
||||
|
||||
func Warn(msg string, fields ...zap.Field) {
|
||||
logger.Warn(msg, fields...)
|
||||
}
|
||||
|
||||
func Info(msg string, fields ...zap.Field) {
|
||||
logger.Info(msg, fields...)
|
||||
}
|
||||
|
||||
func Debug(msg string, fields ...zap.Field) {
|
||||
logger.Debug(msg, fields...)
|
||||
}
|
||||
|
||||
func Panicr(v ...interface{}) {
|
||||
sugaredLogger.Panic(v...)
|
||||
}
|
||||
|
||||
func Panicf(format string, v ...interface{}) {
|
||||
sugaredLogger.Panicf(format, v...)
|
||||
}
|
||||
|
||||
func Errorr(v ...interface{}) {
|
||||
sugaredLogger.Panic(v...)
|
||||
}
|
||||
|
||||
func Errorf(format string, v ...interface{}) {
|
||||
sugaredLogger.Panicf(format, v...)
|
||||
}
|
||||
|
||||
func Warnr(v ...interface{}) {
|
||||
sugaredLogger.Warn(v...)
|
||||
}
|
||||
|
||||
func Warnf(format string, v ...interface{}) {
|
||||
sugaredLogger.Warnf(format, v...)
|
||||
}
|
||||
|
||||
func Infor(v ...interface{}) {
|
||||
sugaredLogger.Info(v...)
|
||||
}
|
||||
|
||||
func Infof(format string, v ...interface{}) {
|
||||
sugaredLogger.Infof(format, v...)
|
||||
}
|
||||
|
||||
func Debugr(v ...interface{}) {
|
||||
sugaredLogger.Debug(v...)
|
||||
}
|
||||
|
||||
func Debugf(format string, v ...interface{}) {
|
||||
sugaredLogger.Debugf(format, v...)
|
||||
}
|
||||
|
||||
func Named(names ...string) *zap.Logger {
|
||||
var l = logger
|
||||
for _, name := range names {
|
||||
l = l.Named(name)
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
func NamedSugar(names ...string) *zap.SugaredLogger {
|
||||
return Named(names...).Sugar()
|
||||
}
|
||||
|
||||
func With(fields ...zap.Field) *zap.Logger {
|
||||
return logger.With(fields...)
|
||||
}
|
||||
|
||||
func WithSugar(fields ...zap.Field) *zap.SugaredLogger {
|
||||
return logger.With(fields...).Sugar()
|
||||
}
|
||||
|
||||
func DecimalField(key string, val decimal.Decimal) zap.Field {
|
||||
return zap.String(key, val.String())
|
||||
}
|
||||
|
||||
func DecimalFieldp(key string, val *decimal.Decimal) zap.Field {
|
||||
if val == nil {
|
||||
return zap.Stringp(key, nil)
|
||||
}
|
||||
return DecimalField(key, *val)
|
||||
}
|
||||
|
||||
func NullDecimalField(key string, val decimal.NullDecimal) zap.Field {
|
||||
if val.Valid {
|
||||
return DecimalField(key, val.Decimal)
|
||||
}
|
||||
return zap.Stringp(key, nil)
|
||||
}
|
||||
|
||||
func NullDecimalFieldp(key string, val *decimal.NullDecimal) zap.Field {
|
||||
if val == nil {
|
||||
return zap.Stringp(key, nil)
|
||||
}
|
||||
if val.Valid {
|
||||
return DecimalField(key, val.Decimal)
|
||||
}
|
||||
return zap.Stringp(key, nil)
|
||||
}
|
||||
|
||||
func DateField(key string, val types.Date) zap.Field {
|
||||
return val.Log(key)
|
||||
}
|
||||
|
||||
func DateFieldp(key string, val *types.Date) zap.Field {
|
||||
if val == nil {
|
||||
return zap.Stringp(key, nil)
|
||||
}
|
||||
return DateField(key, *val)
|
||||
}
|
||||
|
||||
func DateTimeField(key string, val types.DateTime) zap.Field {
|
||||
return val.Log(key)
|
||||
}
|
||||
|
||||
func DateTimeFieldp(key string, val *types.DateTime) zap.Field {
|
||||
if val == nil {
|
||||
return zap.Stringp(key, nil)
|
||||
}
|
||||
return DateTimeField(key, *val)
|
||||
}
|
||||
91
logger/middleware.go
Normal file
91
logger/middleware.go
Normal file
@@ -0,0 +1,91 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// Config defines the config for middleware
|
||||
type LogMiddlewareConfig struct {
|
||||
// Next defines a function to skip this middleware when returned true.
|
||||
//
|
||||
// Optional. Default: nil
|
||||
Next func(c *fiber.Ctx) bool
|
||||
|
||||
// Logger defines zap logger instance
|
||||
Logger *zap.Logger
|
||||
}
|
||||
|
||||
// New creates a new middleware handler
|
||||
func NewLogMiddleware(config LogMiddlewareConfig) fiber.Handler {
|
||||
var (
|
||||
errPadding = 15
|
||||
start, stop time.Time
|
||||
once sync.Once
|
||||
errHandler fiber.ErrorHandler
|
||||
)
|
||||
|
||||
return func(c *fiber.Ctx) error {
|
||||
if config.Next != nil && config.Next(c) {
|
||||
return c.Next()
|
||||
}
|
||||
|
||||
once.Do(func() {
|
||||
errHandler = c.App().Config().ErrorHandler
|
||||
stack := c.App().Stack()
|
||||
for m := range stack {
|
||||
for r := range stack[m] {
|
||||
if len(stack[m][r].Path) > errPadding {
|
||||
errPadding = len(stack[m][r].Path)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
start = time.Now()
|
||||
|
||||
chainErr := c.Next()
|
||||
|
||||
if chainErr != nil {
|
||||
if err := errHandler(c, chainErr); err != nil {
|
||||
_ = c.SendStatus(fiber.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
|
||||
stop = time.Now()
|
||||
|
||||
fields := []zap.Field{
|
||||
zap.Namespace("context"),
|
||||
zap.String("pid", strconv.Itoa(os.Getpid())),
|
||||
zap.String("method", c.Method()),
|
||||
zap.String("remote", c.IP()),
|
||||
zap.Strings("forwarded", c.IPs()),
|
||||
zap.String("url", c.OriginalURL()),
|
||||
zap.String("time", stop.Sub(start).String()),
|
||||
// zap.Object("response", Resp(c.Response())),
|
||||
// zap.Object("request", Req(c)),
|
||||
}
|
||||
|
||||
if u := c.Locals("userId"); u != nil {
|
||||
fields = append(fields, zap.Uint("userId", u.(uint)))
|
||||
}
|
||||
|
||||
formatErr := ""
|
||||
if chainErr != nil {
|
||||
formatErr = chainErr.Error()
|
||||
fields = append(fields, zap.String("error", formatErr))
|
||||
config.Logger.With(fields...).Error(formatErr)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
config.Logger.With(fields...).Info("api.request")
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
119
logger/middleware_types.go
Normal file
119
logger/middleware_types.go
Normal file
@@ -0,0 +1,119 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"strings"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/valyala/fasthttp"
|
||||
"go.uber.org/zap/zapcore"
|
||||
)
|
||||
|
||||
func getAllowedHeaders() map[string]bool {
|
||||
return map[string]bool{
|
||||
"User-Agent": true,
|
||||
"X-Mobile": true,
|
||||
}
|
||||
}
|
||||
|
||||
type resp struct {
|
||||
code int
|
||||
_type string
|
||||
}
|
||||
|
||||
func Resp(r *fasthttp.Response) *resp {
|
||||
return &resp{
|
||||
code: r.StatusCode(),
|
||||
_type: bytes.NewBuffer(r.Header.ContentType()).String(),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *resp) MarshalLogObject(enc zapcore.ObjectEncoder) error {
|
||||
enc.AddString("type", r._type)
|
||||
enc.AddInt("code", r.code)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type req struct {
|
||||
body string
|
||||
fullPath string
|
||||
user string
|
||||
ip string
|
||||
method string
|
||||
route string
|
||||
headers *headerbag
|
||||
}
|
||||
|
||||
func Req(c *fiber.Ctx) *req {
|
||||
reqq := c.Request()
|
||||
var body []byte
|
||||
buffer := new(bytes.Buffer)
|
||||
err := json.Compact(buffer, reqq.Body())
|
||||
if err != nil {
|
||||
body = reqq.Body()
|
||||
} else {
|
||||
body = buffer.Bytes()
|
||||
}
|
||||
|
||||
headers := &headerbag{
|
||||
vals: make(map[string]string),
|
||||
}
|
||||
allowedHeaders := getAllowedHeaders()
|
||||
reqq.Header.VisitAll(func(key, val []byte) {
|
||||
k := bytes.NewBuffer(key).String()
|
||||
if _, exist := allowedHeaders[k]; exist {
|
||||
headers.vals[strings.ToLower(k)] = bytes.NewBuffer(val).String()
|
||||
}
|
||||
})
|
||||
|
||||
var userEmail string
|
||||
if u := c.Locals("userEmail"); u != nil {
|
||||
userEmail = u.(string)
|
||||
}
|
||||
|
||||
return &req{
|
||||
body: bytes.NewBuffer(body).String(),
|
||||
fullPath: bytes.NewBuffer(reqq.RequestURI()).String(),
|
||||
headers: headers,
|
||||
ip: c.IP(),
|
||||
method: c.Method(),
|
||||
route: c.Route().Path,
|
||||
user: userEmail,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *req) MarshalLogObject(enc zapcore.ObjectEncoder) error {
|
||||
enc.AddString("fullPath", r.fullPath)
|
||||
enc.AddString("ip", r.ip)
|
||||
enc.AddString("method", r.method)
|
||||
enc.AddString("route", r.route)
|
||||
|
||||
if r.body != "" {
|
||||
enc.AddString("body", r.body)
|
||||
}
|
||||
|
||||
if r.user != "" {
|
||||
enc.AddString("user", r.user)
|
||||
}
|
||||
|
||||
err := enc.AddObject("headers", r.headers)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type headerbag struct {
|
||||
vals map[string]string
|
||||
}
|
||||
|
||||
func (h *headerbag) MarshalLogObject(enc zapcore.ObjectEncoder) error {
|
||||
for k, v := range h.vals {
|
||||
enc.AddString(k, v)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
27
logger/rolling.go
Normal file
27
logger/rolling.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"math"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"gopkg.in/natefinch/lumberjack.v2"
|
||||
)
|
||||
|
||||
func newRollingWriter() io.Writer {
|
||||
if err := os.MkdirAll("log", 0744); err != nil {
|
||||
log.Println("不能创建用于保存日志的目录。")
|
||||
return nil
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
return &lumberjack.Logger{
|
||||
Filename: fmt.Sprintf("log/service_%s.log", now.Format("2006-01-02_15")),
|
||||
MaxBackups: math.MaxInt, // files
|
||||
MaxSize: 200, // megabytes
|
||||
MaxAge: math.MaxInt, // days
|
||||
}
|
||||
}
|
||||
160
main.go
160
main.go
@@ -1,165 +1,103 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"electricity_bill_calc/cache"
|
||||
"electricity_bill_calc/config"
|
||||
"electricity_bill_calc/global"
|
||||
"electricity_bill_calc/logger"
|
||||
"electricity_bill_calc/model"
|
||||
"electricity_bill_calc/repository"
|
||||
"electricity_bill_calc/router"
|
||||
"electricity_bill_calc/service"
|
||||
"encoding/csv"
|
||||
"electricity_bill_calc/types"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
jsontime "github.com/liamylian/jsontime/v2/v2"
|
||||
"github.com/samber/lo"
|
||||
"github.com/shopspring/decimal"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
func init() {
|
||||
l := logger.Named("Init")
|
||||
err := config.SetupSetting()
|
||||
if err != nil {
|
||||
log.Fatalf("Configuration load failed: %v", err)
|
||||
l.Fatal("服务配置文件加载失败!", zap.Error(err))
|
||||
}
|
||||
log.Println("Configuration loaded!")
|
||||
l.Info("服务配置已经完成加载。")
|
||||
|
||||
err = global.SetupDatabaseConnection()
|
||||
if err != nil {
|
||||
log.Fatalf("Main Database connect failed: %v", err)
|
||||
l.Fatal("主数据库连接失败!", zap.Error(err))
|
||||
}
|
||||
log.Println("Main Database connected!")
|
||||
|
||||
err = global.DBConn.Sync(
|
||||
&model.Region{},
|
||||
&model.User{},
|
||||
&model.UserDetail{},
|
||||
&model.UserCharge{},
|
||||
&model.Park{},
|
||||
&model.Meter04KV{},
|
||||
&model.MaintenanceFee{},
|
||||
&model.Report{},
|
||||
&model.ReportSummary{},
|
||||
&model.WillDilutedFee{},
|
||||
&model.EndUserDetail{})
|
||||
if err != nil {
|
||||
log.Fatalf("Database structure synchronize failed: %v", err)
|
||||
}
|
||||
log.Println("Database structure synchronized.")
|
||||
l.Info("主数据库已经连接。")
|
||||
|
||||
err = global.SetupRedisConnection()
|
||||
if err != nil {
|
||||
log.Fatalf("Main Cache Database connect failed: %v", err)
|
||||
l.Fatal("主缓存数据库连接失败!", zap.Error(err))
|
||||
}
|
||||
log.Println("Main Cache Database connected!")
|
||||
|
||||
err = initializeRegions()
|
||||
if err != nil {
|
||||
log.Fatalf("Regions initialize failed: %v", err)
|
||||
}
|
||||
log.Println("Regions synchronized.")
|
||||
l.Info("主缓存数据库已经连接。")
|
||||
|
||||
err = intializeSingularity()
|
||||
if err != nil {
|
||||
log.Fatalf("Singularity account intialize failed: %v", err)
|
||||
l.Fatal("奇点账号初始化失败。", zap.Error(err))
|
||||
}
|
||||
log.Println("Singularity account intialized.")
|
||||
|
||||
timeZoneShanghai, _ := time.LoadLocation("Asia/Shanghai")
|
||||
jsontime.AddTimeFormatAlias("simple_datetime", "2006-01-02 15:04:05")
|
||||
jsontime.AddTimeFormatAlias("simple_date", "2006-01-02")
|
||||
jsontime.AddLocaleAlias("shanghai", timeZoneShanghai)
|
||||
}
|
||||
|
||||
func initializeRegions() error {
|
||||
log.Println("Synchronize regions...")
|
||||
regionCsvFile, err := os.Open("regions.csv")
|
||||
if err != nil {
|
||||
return fmt.Errorf("region initialize file is not found: %w", err)
|
||||
}
|
||||
defer regionCsvFile.Close()
|
||||
|
||||
var existRegions = make([]string, 0)
|
||||
err = global.DBConn.Table("region").Cols("code").Find(&existRegions)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to retreive regions from database: %w", err)
|
||||
}
|
||||
|
||||
regionCsv := csv.NewReader(regionCsvFile)
|
||||
transaction := global.DBConn.NewSession()
|
||||
defer transaction.Close()
|
||||
if err = transaction.Begin(); err != nil {
|
||||
return fmt.Errorf("unable to intiate database transaction: %w", err)
|
||||
}
|
||||
for {
|
||||
record, err := regionCsv.Read()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if lo.Contains(existRegions, record[0]) {
|
||||
continue
|
||||
}
|
||||
level, err := strconv.Atoi(record[2])
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if _, err = transaction.Insert(&model.Region{Code: record[0], Name: record[1], Level: level, Parent: record[3]}); err != nil {
|
||||
return fmt.Errorf("region synchronize in failed: %v, %w", record, err)
|
||||
}
|
||||
}
|
||||
if err = transaction.Commit(); err != nil {
|
||||
return fmt.Errorf("synchronize regions to database failed: %w", err)
|
||||
}
|
||||
return nil
|
||||
l.Info("奇点账号已经完成初始化。")
|
||||
}
|
||||
|
||||
func intializeSingularity() error {
|
||||
singularityExists, err := service.UserService.IsUserExists("000")
|
||||
l := logger.Named("Init", "Singularity")
|
||||
singularityExists, err := repository.UserRepository.IsUserExists("000")
|
||||
if err != nil {
|
||||
return fmt.Errorf("singularity detect failed: %w", err)
|
||||
l.Error("检测奇点账号失败。", zap.Error(err))
|
||||
return fmt.Errorf("检测奇点账号失败: %w", err)
|
||||
}
|
||||
if singularityExists {
|
||||
l.Info("奇点账号已经存在,跳过剩余初始化步骤。")
|
||||
return nil
|
||||
}
|
||||
singularity := &model.User{
|
||||
Id: "000",
|
||||
singularityId := "000"
|
||||
singularityExpires, err := types.ParseDate("2099-12-31")
|
||||
if err != nil {
|
||||
l.Error("奇点用户账号过期时间解析失败。", zap.Error(err))
|
||||
return fmt.Errorf("奇点用户账号过期时间解析失败: %w", err)
|
||||
}
|
||||
singularity := &model.ManagementAccountCreationForm{
|
||||
Id: &singularityId,
|
||||
Username: "singularity",
|
||||
Name: "Singularity",
|
||||
Type: 2,
|
||||
Enabled: true,
|
||||
Expires: singularityExpires,
|
||||
}
|
||||
singularityName := "Singularity"
|
||||
singularityExpires, err := time.Parse("2006-01-02 15:04:05", "2099-12-31 23:59:59")
|
||||
verifyCode, err := service.UserService.CreateUserAccount(
|
||||
singularity.IntoUser(),
|
||||
singularity.IntoUserDetail())
|
||||
if err != nil {
|
||||
return fmt.Errorf("singularity expires time parse failed: %w", err)
|
||||
l.Error("创建奇点账号失败。", zap.Error(err))
|
||||
return fmt.Errorf("创建奇点账号失败: %w", err)
|
||||
}
|
||||
singularityDetail := &model.UserDetail{
|
||||
Name: &singularityName,
|
||||
UnitServiceFee: decimal.Zero,
|
||||
ServiceExpiration: singularityExpires,
|
||||
}
|
||||
verifyCode, err := service.UserService.CreateUser(singularity, singularityDetail)
|
||||
if err != nil {
|
||||
return fmt.Errorf("singularity account failed to create: %w", err)
|
||||
}
|
||||
log.Printf("Singularity account created, use %s as verify code to reset password.", verifyCode)
|
||||
logger.Info(
|
||||
fmt.Sprintf("奇点账号已经完成创建, 首次登录需要使用验证码 [%s] 重置密码。", *verifyCode),
|
||||
zap.String("账号名称", "singularity"),
|
||||
zap.String("验证码", *verifyCode),
|
||||
)
|
||||
return nil
|
||||
}
|
||||
|
||||
func DBConnectionKeepLive() {
|
||||
for range time.Tick(30 * time.Second) {
|
||||
err := global.DBConn.Ping()
|
||||
// 清理Redis缓存中的孤儿键。
|
||||
func RedisOrphanCleanup() {
|
||||
cleanLogger := logger.Named("Cache").With(zap.String("function", "Cleanup"))
|
||||
for range time.Tick(2 * time.Minute) {
|
||||
cleanLogger.Info("Proceeding cleanup orphan keys.")
|
||||
err := cache.ClearOrphanRelationItems()
|
||||
if err != nil {
|
||||
cleanLogger.Error("Orphan keys clear failed.")
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
go DBConnectionKeepLive()
|
||||
gin.SetMode(config.ServerSettings.RunMode)
|
||||
r := router.Router()
|
||||
r.Run(fmt.Sprintf(":%d", config.ServerSettings.HttpPort))
|
||||
go RedisOrphanCleanup()
|
||||
app := router.App()
|
||||
app.Listen(fmt.Sprintf(":%d", config.ServerSettings.HttpPort))
|
||||
}
|
||||
|
||||
213
model/calculate/calculate.go
Normal file
213
model/calculate/calculate.go
Normal file
@@ -0,0 +1,213 @@
|
||||
package calculate
|
||||
|
||||
import (
|
||||
"electricity_bill_calc/model"
|
||||
"electricity_bill_calc/types"
|
||||
"fmt"
|
||||
|
||||
"github.com/shopspring/decimal"
|
||||
)
|
||||
|
||||
type Reading struct {
|
||||
ReadAt types.DateTime
|
||||
Ratio decimal.Decimal
|
||||
Overall decimal.Decimal
|
||||
Critical decimal.Decimal
|
||||
Peak decimal.Decimal
|
||||
Flat decimal.Decimal
|
||||
Valley decimal.Decimal
|
||||
}
|
||||
|
||||
type Pooling struct {
|
||||
Code string
|
||||
Detail model.ConsumptionUnit
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
type PrimaryTenementStatistics struct {
|
||||
Tenement model.Tenement
|
||||
Meters []Meter
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
type Summary struct {
|
||||
ReportId string
|
||||
OverallArea decimal.Decimal
|
||||
Overall model.ConsumptionUnit
|
||||
ConsumptionFee decimal.Decimal
|
||||
Critical model.ConsumptionUnit
|
||||
Peak model.ConsumptionUnit
|
||||
Flat model.ConsumptionUnit
|
||||
Valley model.ConsumptionUnit
|
||||
Loss decimal.Decimal
|
||||
LossFee decimal.Decimal
|
||||
LossProportion decimal.Decimal
|
||||
AuthoizeLoss model.ConsumptionUnit
|
||||
BasicFee decimal.Decimal
|
||||
BasicPooledPriceConsumption decimal.Decimal
|
||||
BasicPooledPriceArea decimal.Decimal
|
||||
AdjustFee decimal.Decimal
|
||||
AdjustPooledPriceConsumption decimal.Decimal
|
||||
AdjustPooledPriceArea decimal.Decimal
|
||||
LossDilutedPrice decimal.Decimal
|
||||
TotalConsumption decimal.Decimal
|
||||
FinalDilutedOverall decimal.Decimal
|
||||
}
|
||||
|
||||
type PoolingSummary struct {
|
||||
Tenement string
|
||||
Meter string
|
||||
TargetMeter string
|
||||
Area decimal.NullDecimal
|
||||
OverallAmount decimal.Decimal
|
||||
PoolingProportion decimal.Decimal
|
||||
}
|
||||
|
||||
func FromReportSummary(summary *model.ReportSummary, pricingMode *model.ReportIndex) Summary {
|
||||
var parkPrice float64
|
||||
switch pricingMode.PricePolicy {
|
||||
case model.PRICING_POLICY_CONSUMPTION:
|
||||
parkPrice = summary.ConsumptionFee.Decimal.InexactFloat64() / summary.Overall.Amount.InexactFloat64()
|
||||
case model.PRICING_POLICY_ALL:
|
||||
parkPrice = summary.Overall.Fee.InexactFloat64() / summary.Overall.Amount.InexactFloat64()
|
||||
default:
|
||||
fmt.Println("无法识别类型")
|
||||
}
|
||||
|
||||
flatAmount := summary.Overall.Amount.InexactFloat64() -
|
||||
summary.Critical.Amount.InexactFloat64() -
|
||||
summary.Peak.Amount.InexactFloat64() -
|
||||
summary.Valley.Amount.InexactFloat64()
|
||||
|
||||
flatFee := summary.Overall.Amount.InexactFloat64() -
|
||||
summary.Critical.Fee.InexactFloat64() -
|
||||
summary.Peak.Fee.InexactFloat64() -
|
||||
summary.Valley.Fee.InexactFloat64()
|
||||
|
||||
var OverallPrice float64
|
||||
if summary.Overall.Amount.GreaterThan(decimal.Zero) {
|
||||
OverallPrice = parkPrice
|
||||
} else {
|
||||
OverallPrice = decimal.Zero.InexactFloat64()
|
||||
}
|
||||
|
||||
var CriticalPrice float64
|
||||
if summary.Critical.Amount.GreaterThan(decimal.Zero) {
|
||||
CriticalPrice = summary.Critical.Fee.InexactFloat64() / summary.Critical.Amount.InexactFloat64()
|
||||
} else {
|
||||
CriticalPrice = decimal.Zero.InexactFloat64()
|
||||
}
|
||||
|
||||
var PeakPrice float64
|
||||
if summary.Peak.Amount.GreaterThan(decimal.Zero) {
|
||||
PeakPrice = summary.Peak.Fee.InexactFloat64() / summary.Peak.Amount.InexactFloat64()
|
||||
} else {
|
||||
PeakPrice = decimal.Zero.InexactFloat64()
|
||||
}
|
||||
|
||||
var FlatPrice float64
|
||||
if decimal.NewFromFloat(flatAmount).GreaterThan(decimal.Zero) {
|
||||
FlatPrice = flatFee / flatAmount
|
||||
} else {
|
||||
FlatPrice = decimal.Zero.InexactFloat64()
|
||||
}
|
||||
|
||||
var ValleyPrice float64
|
||||
if summary.Valley.Amount.GreaterThan(decimal.Zero) {
|
||||
ValleyPrice = summary.Valley.Fee.InexactFloat64() / summary.Valley.Amount.InexactFloat64()
|
||||
} else {
|
||||
ValleyPrice = decimal.Zero.InexactFloat64()
|
||||
}
|
||||
|
||||
var LossDilutedPrice float64
|
||||
if summary.Overall.Amount.GreaterThan(decimal.Zero) {
|
||||
LossDilutedPrice = parkPrice
|
||||
} else {
|
||||
LossDilutedPrice = decimal.Zero.InexactFloat64()
|
||||
}
|
||||
|
||||
_ = parkPrice
|
||||
|
||||
return Summary{
|
||||
ReportId: summary.ReportId,
|
||||
OverallArea: decimal.Zero,
|
||||
Overall: model.ConsumptionUnit{
|
||||
Amount: summary.Overall.Amount,
|
||||
Fee: summary.Overall.Fee,
|
||||
Price: decimal.NewFromFloat(OverallPrice),
|
||||
Proportion: decimal.NewFromFloat(1.0),
|
||||
},
|
||||
ConsumptionFee: summary.ConsumptionFee.Decimal,
|
||||
Critical: model.ConsumptionUnit{
|
||||
Amount: summary.Critical.Amount,
|
||||
Fee: summary.Critical.Fee,
|
||||
Price: decimal.NewFromFloat(CriticalPrice),
|
||||
Proportion: decimal.NewFromFloat(summary.Critical.Amount.InexactFloat64() / summary.Overall.Amount.InexactFloat64()),
|
||||
},
|
||||
Peak: model.ConsumptionUnit{
|
||||
Amount: summary.Peak.Amount,
|
||||
Fee: summary.Peak.Fee,
|
||||
Price: decimal.NewFromFloat(PeakPrice),
|
||||
Proportion: decimal.NewFromFloat(summary.Peak.Amount.InexactFloat64() / summary.Overall.Amount.InexactFloat64()),
|
||||
},
|
||||
Flat: model.ConsumptionUnit{
|
||||
Amount: decimal.NewFromFloat(flatAmount),
|
||||
Fee: decimal.NewFromFloat(flatFee),
|
||||
Price: decimal.NewFromFloat(FlatPrice),
|
||||
Proportion: decimal.NewFromFloat(flatAmount / summary.Overall.Amount.InexactFloat64()),
|
||||
},
|
||||
Valley: model.ConsumptionUnit{
|
||||
Amount: summary.Valley.Amount,
|
||||
Fee: summary.Valley.Fee,
|
||||
Price: decimal.NewFromFloat(ValleyPrice),
|
||||
Proportion: decimal.NewFromFloat(summary.Valley.Amount.InexactFloat64() / summary.Overall.Amount.InexactFloat64()),
|
||||
},
|
||||
Loss: decimal.Zero,
|
||||
LossFee: decimal.Zero,
|
||||
LossProportion: decimal.Zero,
|
||||
AuthoizeLoss: model.ConsumptionUnit{},
|
||||
BasicFee: summary.BasicFee,
|
||||
BasicPooledPriceConsumption: decimal.Zero,
|
||||
BasicPooledPriceArea: decimal.Zero,
|
||||
AdjustFee: summary.AdjustFee,
|
||||
AdjustPooledPriceConsumption: decimal.Zero,
|
||||
AdjustPooledPriceArea: decimal.Zero,
|
||||
LossDilutedPrice: decimal.NewFromFloat(LossDilutedPrice),
|
||||
TotalConsumption: decimal.Zero,
|
||||
FinalDilutedOverall: decimal.Zero,
|
||||
}
|
||||
}
|
||||
32
model/charge.go
Normal file
32
model/charge.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"electricity_bill_calc/types"
|
||||
|
||||
"github.com/shopspring/decimal"
|
||||
)
|
||||
|
||||
type UserChargeDetail struct {
|
||||
Seq int64 `json:"seq"`
|
||||
UserId string `json:"userId" db:"user_id"`
|
||||
Name string `json:"name"`
|
||||
Fee *float64 `json:"fee"`
|
||||
Discount *float64 `json:"discount"`
|
||||
Amount *float64 `json:"amount"`
|
||||
ChargeTo types.Date `json:"chargeTo"`
|
||||
Settled bool `json:"settled"`
|
||||
SettledAt *types.DateTime `json:"settledAt"`
|
||||
Cancelled bool `json:"cancelled"`
|
||||
CancelledAt *types.DateTime `json:"cancelledAt"`
|
||||
Refunded bool `json:"refunded"`
|
||||
RefundedAt *types.DateTime `json:"refundedAt"`
|
||||
CreatedAt types.DateTime `json:"createdAt"`
|
||||
}
|
||||
|
||||
type ChargeRecordCreationForm struct {
|
||||
UserId string `json:"userId"`
|
||||
Fee decimal.NullDecimal `json:"fee"`
|
||||
Discount decimal.NullDecimal `json:"discount"`
|
||||
Amount decimal.NullDecimal `json:"amount"`
|
||||
ChargeTo types.Date `json:"chargeTo"`
|
||||
}
|
||||
10
model/cunsumption.go
Normal file
10
model/cunsumption.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package model
|
||||
|
||||
import "github.com/shopspring/decimal"
|
||||
|
||||
type ConsumptionUnit struct {
|
||||
Amount decimal.Decimal `json:"amount"`
|
||||
Fee decimal.Decimal `json:"fee"`
|
||||
Price decimal.Decimal `json:"price"`
|
||||
Proportion decimal.Decimal `json:"proportion"`
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/shopspring/decimal"
|
||||
)
|
||||
|
||||
type EndUserDetail struct {
|
||||
CreatedAndModified `xorm:"extends"`
|
||||
ReportId string `xorm:"varchar(120) pk not null" json:"reportId"`
|
||||
ParkId string `xorm:"varchar(120) pk not null" json:"parkId"`
|
||||
MeterId string `xorm:"meter_04kv_id varchar(120) pk not null" json:"meterId"`
|
||||
Seq int64 `xorm:"bigint not null default 0" json:"seq"`
|
||||
Ratio decimal.Decimal `xorm:"numeric(8,4) not null default 1" json:"ratio"`
|
||||
Address *string `xorm:"varchar(100)" json:"address"`
|
||||
CustomerName *string `xorm:"varchar(100)" json:"customerName"`
|
||||
ContactName *string `xorm:"varchar(70)" json:"contactName"`
|
||||
ContactPhone *string `xorm:"varchar(50)" json:"contactPhone"`
|
||||
IsPublicMeter bool `xorm:"'public_meter' bool not null default false" json:"isPublicMeter"`
|
||||
WillDilute bool `xorm:"'dilute' bool not null default false" json:"willDilute"`
|
||||
LastPeriodOverall decimal.Decimal `xorm:"numeric(14,2) not null default 0" json:"lastPeriodOverall"`
|
||||
LastPeriodCritical decimal.Decimal `xorm:"numeric(14,2) not null default 0" json:"lastPeriodCritical"`
|
||||
LastPeriodPeak decimal.Decimal `xorm:"numeric(14,2) not null default 0" json:"lastPeriodPeak"`
|
||||
LastPeriodFlat decimal.Decimal `xorm:"numeric(14,2) not null default 0" json:"lastPeriodFlat"`
|
||||
LastPeriodValley decimal.Decimal `xorm:"numeric(14,2) not null default 0" json:"lastPeriodValley"`
|
||||
CurrentPeriodOverall decimal.Decimal `xorm:"numeric(14,2) not null default 0" json:"currentPeriodOverall"`
|
||||
CurrentPeriodCritical decimal.Decimal `xorm:"numeric(14,2) not null default 0" json:"currentPeriodCritical"`
|
||||
CurrentPeriodPeak decimal.Decimal `xorm:"numeric(14,2) not null default 0" json:"currentPeriodPeak"`
|
||||
CurrentPeriodFlat decimal.Decimal `xorm:"numeric(14,2) not null default 0" json:"currentPeriodFlat"`
|
||||
CurrentPeriodValley decimal.Decimal `xorm:"numeric(14,2) not null default 0" json:"currentPeriodValley"`
|
||||
AdjustOverall decimal.Decimal `xorm:"numeric(14,2) not null default 0" json:"adjustOverall"`
|
||||
AdjustCritical decimal.Decimal `xorm:"numeric(14,2) not null default 0" json:"adjustCritical"`
|
||||
AdjustPeak decimal.Decimal `xorm:"numeric(14,2) not null default 0" json:"adjustPeak"`
|
||||
AdjustFlat decimal.Decimal `xorm:"numeric(14,2) not null default 0" json:"adjustFlat"`
|
||||
AdjustValley decimal.Decimal `xorm:"numeric(14,2) not null default 0" json:"adjustValley"`
|
||||
Overall decimal.NullDecimal `xorm:"numeric(14,2)" json:"overall"`
|
||||
OverallFee decimal.NullDecimal `xorm:"numeric(14,2)" json:"overallFee"`
|
||||
OverallProportion decimal.Decimal `xorm:"numeric(16,15) not null default 0" json:"-"`
|
||||
Critical decimal.NullDecimal `xorm:"numeric(14,2)" json:"critical"`
|
||||
CriticalFee decimal.NullDecimal `xorm:"numeric(18,8)" json:"criticalFee"`
|
||||
Peak decimal.NullDecimal `xorm:"numeric(14,2)" json:"peak"`
|
||||
PeakFee decimal.NullDecimal `xorm:"numeric(18,8)" json:"peakFee"`
|
||||
Flat decimal.NullDecimal `xorm:"numeric(14,2)" json:"flat"`
|
||||
FlatFee decimal.NullDecimal `xorm:"numeric(18,8)" json:"flatFee"`
|
||||
Valley decimal.NullDecimal `xorm:"numeric(14,2)" json:"valley"`
|
||||
ValleyFee decimal.NullDecimal `xorm:"numeric(18,8)" json:"valleyFee"`
|
||||
BasicFeeDiluted decimal.NullDecimal `xorm:"numeric(18,8)" json:"basicFeeDiluted"`
|
||||
AdjustFeeDiluted decimal.NullDecimal `xorm:"numeric(18,8)" json:"adjustFeeDiluted"`
|
||||
LossDiluted decimal.NullDecimal `xorm:"numeric(18,8)" json:"lossDiluted"`
|
||||
LossFeeDiluted decimal.NullDecimal `xorm:"numeric(18,8)" json:"lossFeeDiluted"`
|
||||
MaintenanceFeeDiluted decimal.NullDecimal `xorm:"numeric(18,8)" json:"maintenanceFeeDiluted"`
|
||||
PublicConsumptionDiluted decimal.NullDecimal `xorm:"numeric(18,8)" json:"publicConsumptionDiluted"`
|
||||
FinalDiluted decimal.NullDecimal `xorm:"numeric(14,2)" json:"finalDiluted"`
|
||||
FinalCharge decimal.NullDecimal `xorm:"numeric(14,2)" json:"finalCharge"`
|
||||
}
|
||||
|
||||
func (EndUserDetail) TableName() string {
|
||||
return "end_user_detail"
|
||||
}
|
||||
|
||||
func (d EndUserDetail) Validate() (bool, error) {
|
||||
lastPeriodSum := decimal.Sum(d.LastPeriodCritical, d.LastPeriodPeak, d.LastPeriodValley)
|
||||
if lastPeriodSum.GreaterThan(d.LastPeriodOverall) {
|
||||
return false, errors.New("上期峰谷计量总量大于上期总计电量")
|
||||
}
|
||||
currentPeriodSum := decimal.Sum(d.CurrentPeriodCritical, d.CurrentPeriodPeak, d.CurrentPeriodValley)
|
||||
if currentPeriodSum.GreaterThan(d.CurrentPeriodOverall) {
|
||||
return false, errors.New("本期峰谷计量总量大于本期总计电量")
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (d *EndUserDetail) CalculatePeriod() {
|
||||
d.CurrentPeriodFlat = d.CurrentPeriodOverall.Sub(d.CurrentPeriodCritical).Sub(d.CurrentPeriodPeak).Sub(d.CurrentPeriodValley)
|
||||
d.Overall = decimal.NewNullDecimal(d.CurrentPeriodOverall.Sub(d.LastPeriodOverall).Mul(d.Ratio).Add(d.AdjustOverall).RoundBank(2))
|
||||
d.Critical = decimal.NewNullDecimal(d.CurrentPeriodCritical.Sub(d.LastPeriodCritical).Mul(d.Ratio).Add(d.AdjustCritical).RoundBank(2))
|
||||
d.Peak = decimal.NewNullDecimal(d.CurrentPeriodPeak.Sub(d.LastPeriodPeak).Mul(d.Ratio).Add(d.AdjustPeak).RoundBank(2))
|
||||
d.Flat = decimal.NewNullDecimal(d.CurrentPeriodFlat.Sub(d.LastPeriodFlat).Mul(d.Ratio).Add(d.AdjustFlat).RoundBank(2))
|
||||
d.Valley = decimal.NewNullDecimal(d.CurrentPeriodValley.Sub(d.LastPeriodValley).Mul(d.Ratio).Add(d.AdjustValley).RoundBank(2))
|
||||
}
|
||||
|
||||
type EndUserImport struct {
|
||||
MeterId string `excel:"meterId"`
|
||||
CurrentPeriodOverall decimal.Decimal `excel:"currentPeriodOverall"`
|
||||
CurrentPeriodCritical decimal.NullDecimal `excel:"currentPeriodCritical"`
|
||||
CurrentPeriodPeak decimal.NullDecimal `excel:"currentPeriodPeak"`
|
||||
CurrentPeriodValley decimal.NullDecimal `excel:"currentPeriodValley"`
|
||||
AdjustOverall decimal.Decimal `excel:"adjustOverall"`
|
||||
AdjustCritical decimal.NullDecimal `excel:"adjustCritical"`
|
||||
AdjustPeak decimal.NullDecimal `excel:"adjustPeak"`
|
||||
AdjustFlat decimal.NullDecimal `excel:"adjustFlat"`
|
||||
AdjustValley decimal.NullDecimal `excel:"adjustValley"`
|
||||
}
|
||||
90
model/enums.go
Normal file
90
model/enums.go
Normal file
@@ -0,0 +1,90 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
ELECTRICITY_CATE_TWO_PART int16 = iota
|
||||
ELECTRICITY_CATE_UNITARY_PV
|
||||
ELECTRICITY_CATE_FULL_PV
|
||||
)
|
||||
|
||||
const (
|
||||
METER_TYPE_UNITARY int16 = iota
|
||||
METER_TYPE_PV
|
||||
)
|
||||
|
||||
const (
|
||||
METER_INSTALLATION_TENEMENT int16 = iota
|
||||
METER_INSTALLATION_PARK
|
||||
METER_INSTALLATION_POOLING
|
||||
)
|
||||
|
||||
func ParseMeterInstallationType(s string) (int16, error) {
|
||||
switch {
|
||||
case strings.Contains(s, "商户"):
|
||||
return METER_INSTALLATION_TENEMENT, nil
|
||||
case strings.Contains(s, "公共"):
|
||||
return METER_INSTALLATION_PARK, nil
|
||||
case strings.Contains(s, "楼道"):
|
||||
return METER_INSTALLATION_POOLING, nil
|
||||
default:
|
||||
return -1, fmt.Errorf("提供了一个无法识别的表计类型: %s", s)
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
PRICING_POLICY_CONSUMPTION int16 = iota
|
||||
PRICING_POLICY_ALL
|
||||
)
|
||||
|
||||
const (
|
||||
POOLING_MODE_NONE int16 = iota
|
||||
POOLING_MODE_CONSUMPTION
|
||||
POOLING_MODE_AREA
|
||||
)
|
||||
|
||||
const (
|
||||
PAYMENT_CASH int16 = iota
|
||||
PAYMENT_BANK_CARD
|
||||
PAYMENT_ALIPAY
|
||||
PAYMENT_WECHAT
|
||||
PAYMENT_UNION_PAY
|
||||
PAYMENT_OTHER int16 = 99
|
||||
)
|
||||
|
||||
const (
|
||||
METER_TELEMETER_HYBRID int16 = iota
|
||||
METER_TELEMETER_AUTOMATIC
|
||||
METER_TELEMETER_MANUAL
|
||||
)
|
||||
|
||||
const (
|
||||
RETRY_INTERVAL_ALGORITHM_EXPONENTIAL_BACKOFF int16 = iota
|
||||
RETRY_INTERVAL_ALGORITHM_DOUBLE_LINEAR_BACKOFF
|
||||
RETRY_INTERVAL_ALGORITHM_TRIPLE_LINEAR_BACKOFF
|
||||
RETRY_INTERVAL_ALGORITHM_FIXED
|
||||
)
|
||||
|
||||
const (
|
||||
TAX_METHOD_INCLUSIVE int16 = iota
|
||||
TAX_METHOD_EXCLUSIVE
|
||||
)
|
||||
|
||||
const (
|
||||
REPORT_CALCULATE_TASK_STATUS_PENDING int16 = iota
|
||||
REPORT_CALCULATE_TASK_STATUS_SUCCESS
|
||||
REPORT_CALCULATE_TASK_STATUS_INSUFICIENT_DATA
|
||||
REPORT_CALCULATE_TASK_STATUS_SUSPENDED
|
||||
REPORT_CALCULATE_TASK_STATUS_UNKNOWN_ERROR
|
||||
REPORT_CALCULATE_TASK_STATUS_UNEXISTS = 99
|
||||
)
|
||||
|
||||
const (
|
||||
REPORT_WITHDRAW_NON int16 = iota
|
||||
REPORT_WITHDRAW_APPLYING
|
||||
REPORT_WITHDRAW_DENIED
|
||||
REPORT_WITHDRAW_GRANTED
|
||||
)
|
||||
45
model/invoice.go
Normal file
45
model/invoice.go
Normal file
@@ -0,0 +1,45 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"electricity_bill_calc/tools"
|
||||
"electricity_bill_calc/types"
|
||||
|
||||
"github.com/shopspring/decimal"
|
||||
)
|
||||
|
||||
type InvoiceTitle struct {
|
||||
Name string `json:"name"`
|
||||
USCI string `json:"usci"`
|
||||
Address string `json:"address"`
|
||||
Phone string `json:"phone"`
|
||||
Bank string `json:"bank"`
|
||||
Account string `json:"account"`
|
||||
}
|
||||
|
||||
type InvoiceCargo struct {
|
||||
Name string `json:"name"`
|
||||
Price decimal.Decimal `json:"price"`
|
||||
Unit string `json:"unit"`
|
||||
Quantity decimal.Decimal `json:"quantity"`
|
||||
TaxRate decimal.Decimal `json:"taxRate"`
|
||||
Tax decimal.Decimal `json:"tax"`
|
||||
Total decimal.Decimal `json:"total"`
|
||||
}
|
||||
|
||||
type Invoice struct {
|
||||
InvoiceNo string `json:"invoiceNo"`
|
||||
Park string `json:"parkId" db:"park_id"`
|
||||
Tenement string `json:"tenementId" db:"tenement_id"`
|
||||
InvoiceType *string `json:"type" db:"type"`
|
||||
Info InvoiceTitle `json:"invoiceInfo" db:"invoice_info"`
|
||||
Cargos []InvoiceCargo `json:"cargos"`
|
||||
TaxRate decimal.Decimal `json:"taxRate" db:"tax_rate"`
|
||||
TaxMethod int16 `json:"taxMethod" db:"tax_method"`
|
||||
Total decimal.Decimal `json:"total" db:"total"`
|
||||
IssuedAt types.DateTime `json:"issuedAt" db:"issued_at"`
|
||||
Covers []string `json:"covers"`
|
||||
}
|
||||
|
||||
func (i Invoice) Type() string {
|
||||
return tools.DefaultOrEmptyStr(i.InvoiceType, "")
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"github.com/shopspring/decimal"
|
||||
)
|
||||
|
||||
type MaintenanceFee struct {
|
||||
CreatedAndModified `xorm:"extends"`
|
||||
Deleted `xorm:"extends"`
|
||||
Id string `xorm:"varchar(120) pk not null" json:"id"`
|
||||
ParkId string `xorm:"varchar(120) not null" json:"parkId"`
|
||||
Name string `xorm:"varchar(50) not null" json:"name"`
|
||||
Fee decimal.Decimal `xorm:"numeric(8,2) not null" json:"fee"`
|
||||
Memo *string `xorm:"text" json:"memo"`
|
||||
Enabled bool `xorm:"bool not null" json:"enabled"`
|
||||
}
|
||||
|
||||
func (MaintenanceFee) TableName() string {
|
||||
return "maintenance_fee"
|
||||
}
|
||||
106
model/meter.go
Normal file
106
model/meter.go
Normal file
@@ -0,0 +1,106 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"electricity_bill_calc/types"
|
||||
|
||||
"github.com/shopspring/decimal"
|
||||
)
|
||||
|
||||
type MeterDetail struct {
|
||||
Code string `json:"code" db:"code"`
|
||||
Park string `json:"parkId" db:"park_id"`
|
||||
Address *string `json:"address" db:"address"`
|
||||
MeterType int16 `json:"type" db:"meter_type"`
|
||||
Building *string `json:"building" db:"building"`
|
||||
BuildingName *string `json:"buildingName" db:"building_name"`
|
||||
OnFloor *string `json:"onFloor" db:"on_floor" `
|
||||
Area decimal.NullDecimal `json:"area" db:"area"`
|
||||
Ratio decimal.Decimal `json:"ratio" db:"ratio"`
|
||||
Seq int64 `json:"seq" db:"seq"`
|
||||
Enabled bool `json:"enabled" db:"enabled"`
|
||||
AttachedAt *types.DateTime `json:"attachedAt" db:"attached_at"`
|
||||
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"`
|
||||
}
|
||||
|
||||
type MeterRelation struct {
|
||||
Id string `json:"id"`
|
||||
Park string `json:"parkId" db:"park_id"`
|
||||
MasterMeter string `json:"masterMeterId" db:"master_meter_id"`
|
||||
SlaveMeter string `json:"slaveMeterId" db:"slave_meter_id"`
|
||||
EstablishedAt types.DateTime `json:"establishedAt"`
|
||||
SuspendedAt *types.DateTime `json:"suspendedAt"`
|
||||
RevokedAt *types.DateTime `json:"revokedAt"`
|
||||
}
|
||||
|
||||
type MeterSynchronization struct {
|
||||
Park string `json:"parkId" db:"park_id"`
|
||||
Meter string `json:"meterId" db:"meter_id"`
|
||||
ForeignMeter string `json:"foreignMeter"`
|
||||
SystemType string `json:"systemType"`
|
||||
SystemIdentity string `json:"systemIdentity"`
|
||||
Enabled bool `json:"enabled"`
|
||||
LastSynchronizedAt types.DateTime `json:"lastSynchronizedAt" db:"last_synchronized_at"`
|
||||
RevokeAt *types.DateTime `json:"revokeAt" db:"revoke_at"`
|
||||
}
|
||||
|
||||
type SimpleMeterDocument struct {
|
||||
Code string `json:"code"`
|
||||
Seq int64 `json:"seq"`
|
||||
Address *string `json:"address"`
|
||||
Ratio decimal.Decimal `json:"ratio"`
|
||||
TenementName *string `json:"tenementName"`
|
||||
}
|
||||
|
||||
type NestedMeter struct {
|
||||
MeterId string `json:"meterId"`
|
||||
MeterDetail MeterDetail `json:"meterDetail"`
|
||||
LastTermReadings Reading `json:"lastTermReadings"`
|
||||
CurrentTermReadings Reading `json:"currentTermReadings"`
|
||||
Overall ConsumptionUnit `json:"overall"`
|
||||
Critical ConsumptionUnit `json:"critical"`
|
||||
Peak ConsumptionUnit `json:"peak"`
|
||||
Flat ConsumptionUnit `json:"flat"`
|
||||
Valley ConsumptionUnit `json:"valley"`
|
||||
BasicPooled decimal.Decimal `json:"basicPooled"`
|
||||
AdjustPooled decimal.Decimal `json:"adjustPooled"`
|
||||
LossPooled decimal.Decimal `json:"lossPooled"`
|
||||
PublicPooled decimal.Decimal `json:"publicPooled"`
|
||||
FinalTotal decimal.Decimal `json:"finalTotal"`
|
||||
Area decimal.Decimal `json:"area"`
|
||||
Proportion decimal.Decimal `json:"proportion"`
|
||||
}
|
||||
|
||||
type PooledMeterDetailCompound struct {
|
||||
MeterDetail
|
||||
BindMeters []MeterDetail `json:"bindedMeters"`
|
||||
}
|
||||
|
||||
// 以下结构体用于导入表计档案数据
|
||||
type MeterImportRow struct {
|
||||
Code string `json:"code" excel:"code"`
|
||||
Address *string `json:"address" excel:"address"`
|
||||
MeterType *string `json:"meterType" excel:"meterType"`
|
||||
Building *string `json:"building" excel:"building"`
|
||||
OnFloor *string `json:"onFloor" excel:"onFloor"`
|
||||
Area decimal.NullDecimal `json:"area" excel:"area"`
|
||||
Ratio decimal.Decimal `json:"ratio" excel:"ratio"`
|
||||
Seq int64 `json:"seq" excel:"seq"`
|
||||
ReadAt types.DateTime `json:"readAt" excel:"readAt"`
|
||||
Overall decimal.Decimal `json:"overall" excel:"overall"`
|
||||
Critical decimal.NullDecimal `json:"critical" excel:"critical"`
|
||||
Peak decimal.NullDecimal `json:"peak" excel:"peak"`
|
||||
Flat decimal.NullDecimal `json:"flat" excel:"flat"`
|
||||
Valley decimal.NullDecimal `json:"valley" excel:"valley"`
|
||||
}
|
||||
|
||||
// 以下结构体用于导入表计抄表数据
|
||||
type ReadingImportRow struct {
|
||||
Code string `json:"code" excel:"code"`
|
||||
ReadAt types.DateTime `json:"readAt" excel:"readAt"`
|
||||
Overall decimal.Decimal `json:"overall" excel:"overall"`
|
||||
Critical decimal.NullDecimal `json:"critical" excel:"critical"`
|
||||
Peak decimal.NullDecimal `json:"peak" excel:"peak"`
|
||||
Valley decimal.NullDecimal `json:"valley" excel:"valley"`
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"github.com/shopspring/decimal"
|
||||
)
|
||||
|
||||
type Meter04KV struct {
|
||||
CreatedAndModified `xorm:"extends"`
|
||||
Code string `xorm:"varchar(120) pk not null" json:"code" excel:"code"`
|
||||
ParkId string `xorm:"varchar(120) pk not null" json:"parkId"`
|
||||
Address *string `xorm:"varchar(100)" json:"address" excel:"address"`
|
||||
CustomerName *string `xorm:"varchar(100)" json:"customerName" excel:"name"`
|
||||
ContactName *string `xorm:"varchar(70)" json:"contactName" excel:"contact"`
|
||||
ContactPhone *string `xorm:"varchar(50)" json:"contactPhone" excel:"phone"`
|
||||
Ratio decimal.Decimal `xorm:"numeric(8,4) not null default 1" json:"ratio" excel:"ratio"`
|
||||
Seq int64 `xorm:"bigint not null" json:"seq" excel:"seq"`
|
||||
IsPublicMeter bool `xorm:"'public_meter' bool not null default false" json:"isPublicMeter" excel:"public"`
|
||||
WillDilute bool `xorm:"'dilute' bool not null default false" json:"willDilute" excel:"dilute"`
|
||||
Enabled bool `xorm:"bool not null default true" json:"enabled"`
|
||||
}
|
||||
|
||||
func (Meter04KV) TableName() string {
|
||||
return "meter_04kv"
|
||||
}
|
||||
@@ -1,67 +1,45 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"electricity_bill_calc/types"
|
||||
"time"
|
||||
|
||||
"github.com/shopspring/decimal"
|
||||
)
|
||||
|
||||
const (
|
||||
CATEGORY_TWO_PART int8 = iota
|
||||
CATEGORY_SINGLE_PV
|
||||
CATEGORY_SINGLE_NON_PV
|
||||
)
|
||||
|
||||
const (
|
||||
CUSTOMER_METER_NON_PV int8 = iota
|
||||
CUSTOMER_METER_PV
|
||||
)
|
||||
|
||||
type Park struct {
|
||||
CreatedAndModified `xorm:"extends"`
|
||||
Deleted `xorm:"extends"`
|
||||
Id string `xorm:"varchar(120) pk not null" json:"id"`
|
||||
UserId string `xorm:"varchar(120) not null" json:"userId"`
|
||||
Name string `xorm:"varchar(70) not null" json:"name"`
|
||||
Abbr *string `xorm:"varchar(50)" json:"abbr"`
|
||||
Area decimal.NullDecimal `xorm:"numeric(14,2)" json:"area"`
|
||||
TenementQuantity decimal.NullDecimal `xorm:"numeric(8,0)" json:"tenement"`
|
||||
Capacity decimal.NullDecimal `xorm:"numeric(16,2)" json:"capacity"`
|
||||
Category int8 `xorm:"smallint not null default 0" json:"category"`
|
||||
SubmeterType int8 `xorm:"'meter_04kv_type' smallint not null default 0" json:"meter04kvType"`
|
||||
Region *string `xorm:"varchar(10)" json:"region"`
|
||||
Address *string `xorm:"varchar(120)" json:"address"`
|
||||
Contact *string `xorm:"varchar(100)" json:"contact"`
|
||||
Phone *string `xorm:"varchar(50)" json:"phone"`
|
||||
Enabled bool `xorm:"bool not null" json:"enabled"`
|
||||
Id string `json:"id"`
|
||||
UserId string `json:"userId"`
|
||||
Name string `json:"name"`
|
||||
Abbr string `json:"-"`
|
||||
Area decimal.NullDecimal `json:"area"`
|
||||
TenementQuantity decimal.NullDecimal `json:"tenement"`
|
||||
Capacity decimal.NullDecimal `json:"capacity"`
|
||||
Category int16 `json:"category"`
|
||||
MeterType int16 `json:"meter04kvType" db:"meter_04kv_type"`
|
||||
PricePolicy int16 `json:"pricePolicy"`
|
||||
BasicPooled int16 `json:"basicDiluted"`
|
||||
AdjustPooled int16 `json:"adjustDiluted"`
|
||||
LossPooled int16 `json:"lossDiluted"`
|
||||
PublicPooled int16 `json:"publicDiluted"`
|
||||
TaxRate decimal.NullDecimal `json:"taxRate"`
|
||||
Region *string `json:"region"`
|
||||
Address *string `json:"address"`
|
||||
Contact *string `json:"contact"`
|
||||
Phone *string `json:"phone"`
|
||||
Enabled bool `json:"enabled"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
LastModifiedAt time.Time `json:"lastModifiedAt"`
|
||||
DeletedAt *time.Time `json:"deletedAt"`
|
||||
}
|
||||
|
||||
func (Park) TableName() string {
|
||||
return "park"
|
||||
}
|
||||
|
||||
type ParkSimplified struct {
|
||||
Id string `xorm:"varchar(120) pk not null" json:"id"`
|
||||
UserId string `xorm:"varchar(120) not null" json:"userId"`
|
||||
Name string `xorm:"varchar(70) not null" json:"name"`
|
||||
Abbr *string `xorm:"varchar(50)" json:"abbr"`
|
||||
Area decimal.NullDecimal `xorm:"numeric(14,2)" json:"area"`
|
||||
TenementQuantity decimal.NullDecimal `xorm:"numeric(8,0)" json:"tenement"`
|
||||
Capacity decimal.NullDecimal `xorm:"numeric(16,2)" json:"capacity"`
|
||||
Category int8 `xorm:"smallint not null" json:"category"`
|
||||
SubmeterType int8 `xorm:"'meter_04kv_type' smallint not null" json:"meter04kvType"`
|
||||
Region *string `xorm:"varchar(10)" json:"region"`
|
||||
Address *string `xorm:"varchar(120)" json:"address"`
|
||||
Contact *string `xorm:"varchar(100)" json:"contact"`
|
||||
Phone *string `xorm:"varchar(50)" json:"phone"`
|
||||
}
|
||||
|
||||
func (ParkSimplified) TableName() string {
|
||||
return "park"
|
||||
type Parks struct {
|
||||
Park
|
||||
NormAuthorizedLossRate float64 `json:"norm_authorized_loss_rate"`
|
||||
}
|
||||
|
||||
type ParkPeriodStatistics struct {
|
||||
Id string `xorm:"varchar(120) not null" json:"id"`
|
||||
Name string `xorm:"varchar(120) not null" json:"name"`
|
||||
Period *time.Time `xorm:"date" json:"period" time_format:"simple_date" time_location:"shanghai"`
|
||||
Id string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Period *types.DateRange
|
||||
}
|
||||
|
||||
14
model/park_building.go
Normal file
14
model/park_building.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package model
|
||||
|
||||
import "time"
|
||||
|
||||
type ParkBuilding struct {
|
||||
Id string `json:"id"`
|
||||
Park string `json:"parkId" db:"park_id"`
|
||||
Name string `json:"name"`
|
||||
Floors *string `json:"floors"`
|
||||
Enabled bool `json:"enabled"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
LastModifiedAt time.Time `json:"lastModifiedAt"`
|
||||
DeletedAt *time.Time `json:"deletedAt"`
|
||||
}
|
||||
@@ -1,113 +0,0 @@
|
||||
package model
|
||||
|
||||
import "github.com/shopspring/decimal"
|
||||
|
||||
type PaidPart struct {
|
||||
Overall decimal.Decimal `json:"overall"`
|
||||
OverallPrice decimal.Decimal `json:"overallPrice"`
|
||||
ConsumptionFee decimal.Decimal `json:"consumptionFee"`
|
||||
OverallFee decimal.Decimal `json:"overallFee"`
|
||||
Critical decimal.NullDecimal `json:"critical"`
|
||||
CriticalPrice decimal.NullDecimal `json:"criticalPrice"`
|
||||
CriticalFee decimal.NullDecimal `json:"criticalFee"`
|
||||
Peak decimal.NullDecimal `json:"peak"`
|
||||
PeakPrice decimal.NullDecimal `json:"peakPrice"`
|
||||
PeakFee decimal.NullDecimal `json:"peakFee"`
|
||||
Flat decimal.NullDecimal `json:"flat"`
|
||||
FlatPrice decimal.NullDecimal `json:"flatPrice"`
|
||||
FlatFee decimal.NullDecimal `json:"flatFee"`
|
||||
Valley decimal.NullDecimal `json:"valley"`
|
||||
ValleyPrice decimal.NullDecimal `json:"valleyPrice"`
|
||||
ValleyFee decimal.NullDecimal `json:"valleyFee"`
|
||||
BasicFee decimal.Decimal `json:"basicFee"`
|
||||
AdjustFee decimal.Decimal `json:"adjustFee"`
|
||||
}
|
||||
|
||||
type EndUserOverallPart struct {
|
||||
Overall decimal.Decimal `json:"overall"`
|
||||
OverallPrice decimal.Decimal `json:"overallPrice"`
|
||||
OverallFee decimal.Decimal `json:"consumptionFee"`
|
||||
Critical decimal.NullDecimal `json:"critical"`
|
||||
CriticalPrice decimal.NullDecimal `json:"criticalPrice"`
|
||||
CriticalFee decimal.NullDecimal `json:"criticalFee"`
|
||||
Peak decimal.NullDecimal `json:"peak"`
|
||||
PeakPrice decimal.NullDecimal `json:"peakPrice"`
|
||||
PeakFee decimal.NullDecimal `json:"peakFee"`
|
||||
Flat decimal.NullDecimal `json:"flat"`
|
||||
FlatPrice decimal.NullDecimal `json:"flatPrice"`
|
||||
FlatFee decimal.NullDecimal `json:"flatFee"`
|
||||
Valley decimal.NullDecimal `json:"valley"`
|
||||
ValleyPrice decimal.NullDecimal `json:"valleyPrice"`
|
||||
ValleyFee decimal.NullDecimal `json:"valleyFee"`
|
||||
}
|
||||
|
||||
type PublicConsumptionOverallPart struct {
|
||||
Overall decimal.Decimal `json:"overall"`
|
||||
OverallPrice decimal.Decimal `json:"overallPrice"`
|
||||
ConsumptionFee decimal.Decimal `json:"consumptionFee"`
|
||||
OverallFee decimal.Decimal `json:"overallFee"`
|
||||
Critical decimal.NullDecimal `json:"critical"`
|
||||
CriticalPrice decimal.NullDecimal `json:"criticalPrice"`
|
||||
CriticalFee decimal.NullDecimal `json:"criticalFee"`
|
||||
Peak decimal.NullDecimal `json:"peak"`
|
||||
PeakPrice decimal.NullDecimal `json:"peakPrice"`
|
||||
PeakFee decimal.NullDecimal `json:"peakFee"`
|
||||
Flat decimal.NullDecimal `json:"flat"`
|
||||
FlatPrice decimal.NullDecimal `json:"flatPrice"`
|
||||
FlatFee decimal.NullDecimal `json:"flatFee"`
|
||||
Valley decimal.NullDecimal `json:"valley"`
|
||||
ValleyPrice decimal.NullDecimal `json:"valleyPrice"`
|
||||
ValleyFee decimal.NullDecimal `json:"valleyFee"`
|
||||
Proportion decimal.Decimal `json:"proportion"`
|
||||
}
|
||||
|
||||
type LossPart struct {
|
||||
Quantity decimal.Decimal `json:"quantity"`
|
||||
Price decimal.Decimal `json:"price"`
|
||||
ConsumptionFee decimal.Decimal `json:"consumptionFee"`
|
||||
Proportion decimal.Decimal `json:"proportion"`
|
||||
}
|
||||
|
||||
type OtherShouldCollectionPart struct {
|
||||
MaintenanceFee decimal.NullDecimal `json:"maintenanceFee"`
|
||||
BasicFees decimal.Decimal `json:"basicFees"`
|
||||
}
|
||||
|
||||
type MaintenancePart struct {
|
||||
BasicFees decimal.Decimal `json:"basicFees"`
|
||||
LossFee decimal.Decimal `json:"lossFee"`
|
||||
PublicConsumptionFee decimal.Decimal `json:"publicConsumptionFee"`
|
||||
MaintenanceFee decimal.Decimal `json:"maintenanceFee"`
|
||||
FinalMaintenance decimal.Decimal `json:"finalMaintenance"`
|
||||
MaintenanceProportion decimal.Decimal `json:"maintenanceProportion"`
|
||||
MaintenancePrice decimal.Decimal `json:"maintenancePrice"`
|
||||
PriceRatio decimal.Decimal `json:"priceRatio"`
|
||||
}
|
||||
|
||||
type EndUserSummary struct {
|
||||
CustomerName *string `json:"customerName"`
|
||||
Address *string `json:"address"`
|
||||
MeterId string `json:"meterId"`
|
||||
Overall decimal.Decimal `json:"overall"`
|
||||
OverallFee decimal.Decimal `json:"overallFee"`
|
||||
Critical decimal.NullDecimal `json:"critical"`
|
||||
CriticalFee decimal.NullDecimal `json:"criticalFee"`
|
||||
Peak decimal.NullDecimal `json:"peak"`
|
||||
PeakFee decimal.NullDecimal `json:"peakFee"`
|
||||
Valley decimal.NullDecimal `json:"valley"`
|
||||
ValleyFee decimal.NullDecimal `json:"valleyFee"`
|
||||
Maintenance decimal.Decimal `json:"maintenance"`
|
||||
}
|
||||
|
||||
type Publicity struct {
|
||||
Report Report `json:"index"`
|
||||
User UserDetail `json:"enterprise"`
|
||||
Park Park `json:"park"`
|
||||
Paid PaidPart `json:"paid"`
|
||||
EndUser EndUserOverallPart `json:"endUserSum"`
|
||||
Loss LossPart `json:"loss"`
|
||||
PublicConsumptionOverall PublicConsumptionOverallPart `json:"public"`
|
||||
OtherCollections OtherShouldCollectionPart `json:"others"`
|
||||
Maintenance MaintenancePart `json:"maintenance"`
|
||||
EndUserDetails []EndUserSummary `json:"endUser"`
|
||||
}
|
||||
56
model/reading.go
Normal file
56
model/reading.go
Normal file
@@ -0,0 +1,56 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"electricity_bill_calc/types"
|
||||
|
||||
"github.com/shopspring/decimal"
|
||||
)
|
||||
|
||||
type Reading struct {
|
||||
Ratio decimal.Decimal `json:"ratio"`
|
||||
Overall decimal.Decimal `json:"overall"`
|
||||
Critical decimal.Decimal `json:"critical"`
|
||||
Peak decimal.Decimal `json:"peak"`
|
||||
Flat decimal.Decimal `json:"flat"`
|
||||
Valley decimal.Decimal `json:"valley"`
|
||||
}
|
||||
|
||||
func NewPVReading(ratio, overall, critical, peak, flat, valley decimal.Decimal) *Reading {
|
||||
return &Reading{
|
||||
Ratio: ratio,
|
||||
Overall: overall,
|
||||
Critical: critical,
|
||||
Peak: peak,
|
||||
Flat: flat,
|
||||
Valley: valley,
|
||||
}
|
||||
}
|
||||
|
||||
func NewUnitaryReading(ratio, overall decimal.Decimal) *Reading {
|
||||
return &Reading{
|
||||
Ratio: ratio,
|
||||
Overall: overall,
|
||||
Critical: decimal.Zero,
|
||||
Peak: decimal.Zero,
|
||||
Flat: overall,
|
||||
Valley: decimal.Zero,
|
||||
}
|
||||
}
|
||||
|
||||
type MeterReading struct {
|
||||
ReadAt types.DateTime `json:"readAt"`
|
||||
Park string `json:"parkId" db:"park_id"`
|
||||
Meter string `json:"meterId" db:"meter_id"`
|
||||
MeterType int16 `json:"meterType"`
|
||||
Ratio decimal.Decimal `json:"ratio"`
|
||||
Overall decimal.Decimal `json:"overall"`
|
||||
Critical decimal.Decimal `json:"critical"`
|
||||
Peak decimal.Decimal `json:"peak"`
|
||||
Flat decimal.Decimal `json:"flat"`
|
||||
Valley decimal.Decimal `json:"valley"`
|
||||
}
|
||||
|
||||
type DetailedMeterReading struct {
|
||||
Detail MeterDetail `json:"detail"`
|
||||
Reading MeterReading `json:"reading"`
|
||||
}
|
||||
@@ -1,12 +1,8 @@
|
||||
package model
|
||||
|
||||
type Region struct {
|
||||
Code string `xorm:"varchar(15) pk not null" json:"code"`
|
||||
Name string `xorm:"varchar(50) not null" json:"name"`
|
||||
Level int `xorm:"int not null default 0" json:"level"`
|
||||
Parent string `xorm:"varchar(15) not null default '0'" json:"parent"`
|
||||
}
|
||||
|
||||
func (Region) TableName() string {
|
||||
return "region"
|
||||
Code string `json:"code"`
|
||||
Name string `json:"name"`
|
||||
Level int32 `json:"level"`
|
||||
Parent string `json:"parent"`
|
||||
}
|
||||
|
||||
179
model/report.go
179
model/report.go
@@ -1,90 +1,139 @@
|
||||
package model
|
||||
|
||||
import "time"
|
||||
import (
|
||||
"electricity_bill_calc/types"
|
||||
|
||||
const (
|
||||
REPORT_NOT_WITHDRAW int8 = iota
|
||||
REPORT_WITHDRAW_APPLIED
|
||||
REPORT_WITHDRAW_DENIED
|
||||
REPORT_WITHDRAW_GRANTED
|
||||
"github.com/shopspring/decimal"
|
||||
)
|
||||
|
||||
type Report struct {
|
||||
CreatedAndModified `xorm:"extends"`
|
||||
Id string `xorm:"varchar(120) pk not null" json:"id"`
|
||||
ParkId string `xorm:"varchar(120) not null" json:"parkId"`
|
||||
Period time.Time `xorm:"date not null" json:"period" time_format:"simple_date" time_location:"shanghai"`
|
||||
Category int8 `xorm:"smallint not null default 0" json:"category"`
|
||||
SubmeterType int8 `xorm:"'meter_04kv_type' smallint not null default 0" json:"meter04kvType"`
|
||||
StepState Steps `xorm:"text not null json" json:"stepState"`
|
||||
Published bool `xorm:"bool not null default false" json:"published"`
|
||||
PublishedAt *time.Time `xorm:"timestampz" json:"publishedAt" time_format:"simple_datetime" time_location:"shanghai"`
|
||||
Withdraw int8 `xorm:"smallint not null default 0" json:"withdraw"`
|
||||
LastWithdrawAppliedAt *time.Time `xorm:"timestampz" json:"lastWithdrawAppliedAt" time_format:"simple_datetime" time_location:"shanghai"`
|
||||
LastWithdrawAuditAt *time.Time `xorm:"timestampz" json:"lastWithdrawAuditAt" time_format:"simple_datetime" time_location:"shanghai"`
|
||||
type ReportIndex struct {
|
||||
Id string `json:"id"`
|
||||
Park string `json:"parkId" db:"park_id"`
|
||||
Period types.DateRange `json:"period"`
|
||||
Category int16 `json:"category"`
|
||||
MeterType int16 `json:"meter04kvType" db:"meter_04kv_type"`
|
||||
PricePolicy int16 `json:"pricePolicy"`
|
||||
BasisPooled int16 `json:"basisPooled"`
|
||||
AdjustPooled int16 `json:"adjustPooled"`
|
||||
LossPooled int16 `json:"lossPooled"`
|
||||
PublicPooled int16 `json:"publicPooled"`
|
||||
Published bool `json:"published"`
|
||||
PublishedAt *types.DateTime `json:"publishedAt" db:"published_at"`
|
||||
Withdraw int16 `json:"withdraw"`
|
||||
LastWithdrawAppliedAt *types.DateTime `json:"lastWithdrawAppliedAt" db:"last_withdraw_applied_at"`
|
||||
LastWithdrawAuditAt *types.DateTime `json:"lastWithdrawAuditAt" db:"last_withdraw_audit_at"`
|
||||
Status *int16 `json:"status"`
|
||||
Message *string `json:"message"`
|
||||
CreatedAt types.DateTime `json:"createdAt" db:"created_at"`
|
||||
LastModifiedAt types.DateTime `json:"lastModifiedAt" db:"last_modified_at"`
|
||||
AuthorizedLossRate float64 `json:"authorized_loss_rate" db:"authorized_loss_rate"`
|
||||
AuthorizedLossRateIncrement float64 `json:"authorized_loss_rate_increment" db:"authorized_loss_rate_increment"`
|
||||
}
|
||||
|
||||
type Steps struct {
|
||||
Summary bool `json:"summary"`
|
||||
WillDiluted bool `json:"willDiluted"`
|
||||
Submeter bool `json:"submeter"`
|
||||
Calculate bool `json:"calculate"`
|
||||
Preview bool `json:"preview"`
|
||||
Publish bool `json:"publish"`
|
||||
type ReportSummary struct {
|
||||
ReportId string `json:"reportId" db:"report_id"`
|
||||
OverallArea decimal.Decimal `json:"overallArea" db:"overall_area"`
|
||||
Overall ConsumptionUnit `json:"overall"`
|
||||
ConsumptionFee decimal.NullDecimal `json:"consumptionFee" db:"consumption_fee"`
|
||||
Critical ConsumptionUnit `json:"critical"`
|
||||
Peak ConsumptionUnit `json:"peak"`
|
||||
Flat ConsumptionUnit `json:"flat"`
|
||||
Valley ConsumptionUnit `json:"valley"`
|
||||
Loss decimal.NullDecimal `json:"loss"`
|
||||
LossFee decimal.NullDecimal `json:"lossFee" db:"loss_fee"`
|
||||
LossProportion decimal.NullDecimal `json:"lossProportion" db:"loss_proportion"`
|
||||
AuthorizeLoss *ConsumptionUnit `json:"authorizeLoss" db:"authorize_loss"`
|
||||
BasicFee decimal.Decimal `json:"basicFee" db:"basic_fee"`
|
||||
BasicPooledPriceConsumption decimal.NullDecimal `json:"basicPooledPriceConsumption" db:"basic_pooled_price_consumption"`
|
||||
BasicPooledPriceArea decimal.NullDecimal `json:"basicPooledPriceArea" db:"basic_pooled_price_area"`
|
||||
AdjustFee decimal.Decimal `json:"adjustFee" db:"adjust_fee"`
|
||||
AdjustPooledPriceConsumption decimal.NullDecimal `json:"adjustPooledPriceConsumption" db:"adjust_pooled_price_consumption"`
|
||||
AdjustPooledPriceArea decimal.NullDecimal `json:"adjustPooledPriceArea" db:"adjust_pooled_price_area"`
|
||||
LossDilutedPrice decimal.NullDecimal `json:"lossDilutedPrice" db:"loss_diluted_price"`
|
||||
TotalConsumption decimal.Decimal `json:"totalConsumption" db:"total_consumption"`
|
||||
FinalDilutedOverall decimal.NullDecimal `json:"finalDilutedOverall" db:"final_diluted_overall"`
|
||||
}
|
||||
|
||||
func (Report) TableName() string {
|
||||
return "report"
|
||||
}
|
||||
|
||||
func NewSteps() Steps {
|
||||
return Steps{
|
||||
Summary: false,
|
||||
WillDiluted: false,
|
||||
Submeter: false,
|
||||
Calculate: false,
|
||||
Preview: false,
|
||||
Publish: false,
|
||||
func (rs ReportSummary) GetConsumptionFee() decimal.Decimal {
|
||||
if !rs.ConsumptionFee.Valid {
|
||||
return rs.Overall.Fee.Sub(rs.BasicFee).Sub(rs.AdjustFee)
|
||||
}
|
||||
return rs.ConsumptionFee.Decimal
|
||||
}
|
||||
|
||||
type ParkNewestReport struct {
|
||||
Park Park `xorm:"extends" json:"park"`
|
||||
Report *Report `xorm:"extends" json:"report"`
|
||||
type ReportPublicConsumption struct {
|
||||
ReportId string `json:"reportId" db:"report_id"`
|
||||
MeterId string `json:"parkMeterId" db:"park_meter_id"`
|
||||
Overall ConsumptionUnit `json:"overall"`
|
||||
Critical ConsumptionUnit `json:"critical"`
|
||||
Peak ConsumptionUnit `json:"peak"`
|
||||
Flat ConsumptionUnit `json:"flat"`
|
||||
Valley ConsumptionUnit `json:"valley"`
|
||||
LossAdjust ConsumptionUnit `json:"lossAdjust"`
|
||||
ConsumptionTotal decimal.Decimal `json:"consumptionTotal" db:"consumption_total"`
|
||||
LossAdjustTotal decimal.Decimal `json:"lossAdjustTotal" db:"loss_adjust_total"`
|
||||
FinalTotal decimal.Decimal `json:"finalTotal" db:"final_total"`
|
||||
PublicPooled int16 `json:"publicPooled" db:"public_pooled"`
|
||||
}
|
||||
|
||||
func (ParkNewestReport) TableName() string {
|
||||
return "park"
|
||||
type ReportDetailedPublicConsumption struct {
|
||||
MeterDetail
|
||||
ReportPublicConsumption
|
||||
}
|
||||
|
||||
func (p *ParkNewestReport) AfterLoad() {
|
||||
if p.Report != nil && len(p.Report.Id) == 0 {
|
||||
p.Report = nil
|
||||
}
|
||||
type ReportPooledConsumption struct {
|
||||
ReportId string `json:"reportId" db:"report_id"`
|
||||
MeterId string `json:"pooledMeterId" db:"pooled_meter_id"`
|
||||
Overall ConsumptionUnit `json:"overall"`
|
||||
Critical ConsumptionUnit `json:"critical"`
|
||||
Peak ConsumptionUnit `json:"peak"`
|
||||
Flat ConsumptionUnit `json:"flat"`
|
||||
Valley ConsumptionUnit `json:"valley"`
|
||||
PooledArea decimal.Decimal `json:"pooledArea" db:"pooled_area"`
|
||||
Diluted []NestedMeter `json:"diluted"`
|
||||
}
|
||||
|
||||
type ReportIndexSimplified struct {
|
||||
Id string `xorm:"varchar(120) pk not null" json:"id"`
|
||||
ParkId string `xorm:"varchar(120) not null" json:"parkId"`
|
||||
Period time.Time `xorm:"date not null" json:"period" time_format:"simple_date" time_location:"shanghai"`
|
||||
StepState Steps `xorm:"text not null json" json:"stepState"`
|
||||
Published bool `xorm:"bool not null default false" json:"published"`
|
||||
PublishedAt *time.Time `xorm:"timestampz" json:"publishedAt" time_format:"simple_datetime" time_location:"shanghai"`
|
||||
Withdraw int8 `xorm:"smallint not null default 0" json:"withdraw"`
|
||||
LastWithdrawAppliedAt *time.Time `xorm:"timestampz" json:"lastWithdrawAppliedAt" time_format:"simple_datetime" time_location:"shanghai"`
|
||||
LastWithdrawAuditAt *time.Time `xorm:"timestampz" json:"lastWithdrawAuditAt" time_format:"simple_datetime" time_location:"shanghai"`
|
||||
type ReportDetailedPooledConsumption struct {
|
||||
MeterDetail
|
||||
ReportPooledConsumption
|
||||
PublicPooled int16 `json:"publicPooled"`
|
||||
}
|
||||
|
||||
func (ReportIndexSimplified) TableName() string {
|
||||
return "report"
|
||||
type ReportDetailNestedMeterConsumption struct {
|
||||
Meter MeterDetail `json:"meter"`
|
||||
Consumption NestedMeter `json:"consumption"`
|
||||
}
|
||||
|
||||
type JoinedReportForWithdraw struct {
|
||||
Report Report `xorm:"extends" json:"report"`
|
||||
Park ParkSimplified `xorm:"extends" json:"park"`
|
||||
User UserDetailSimplified `xorm:"extends" json:"user"`
|
||||
type ReportTenement struct {
|
||||
ReportId string `json:"reportId" db:"report_id"`
|
||||
Tenement string `json:"tenementId" db:"tenement_id"`
|
||||
Detail Tenement `json:"tenementDetail" db:"tenement_detail"`
|
||||
Period types.DateRange `json:"calcPeriod" db:"calc_period"`
|
||||
Overall ConsumptionUnit `json:"overall"`
|
||||
Critical ConsumptionUnit `json:"critical"`
|
||||
Peak ConsumptionUnit `json:"peak"`
|
||||
Flat ConsumptionUnit `json:"flat"`
|
||||
Valley ConsumptionUnit `json:"valley"`
|
||||
BasicFeePooled decimal.Decimal `json:"basicFeePooled" db:"basic_fee_pooled"`
|
||||
AdjustFeePooled decimal.Decimal `json:"adjustFeePooled" db:"adjust_fee_pooled"`
|
||||
LossFeePooled decimal.Decimal `json:"lossFeePooled" db:"loss_fee_pooled"`
|
||||
FinalPooled decimal.Decimal `json:"finalPooled" db:"final_pooled"`
|
||||
FinalCharge decimal.Decimal `json:"finalCharge" db:"final_charge"`
|
||||
Invoice []string `json:"invoice" db:"invoice"`
|
||||
Meters []NestedMeter `json:"meters" db:"meters"`
|
||||
Pooled []NestedMeter `json:"pooled" db:"pooled"`
|
||||
}
|
||||
|
||||
func (JoinedReportForWithdraw) TableName() string {
|
||||
return "report"
|
||||
type ReportTask struct {
|
||||
Id string `json:"id"`
|
||||
LastModifiedAt types.DateTime `json:"lastModifiedAt" db:"last_modified_at"`
|
||||
Status int16 `json:"status"`
|
||||
Message *string `json:"message"`
|
||||
}
|
||||
|
||||
type SimplifiedTenementCharge struct {
|
||||
ReportId string `json:"reportId" db:"report_id"`
|
||||
Period types.DateRange `json:"period"`
|
||||
TotalConsumption decimal.Decimal `json:"totalConsumption" db:"total_consumption"`
|
||||
FinalCharge decimal.Decimal `json:"finalCharge" db:"final_charge"`
|
||||
}
|
||||
|
||||
@@ -1,106 +0,0 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/shopspring/decimal"
|
||||
)
|
||||
|
||||
type ReportSummary struct {
|
||||
ReportId string `xorm:"varchar(120) pk not null" json:"-"`
|
||||
Overall decimal.Decimal `xorm:"numeric(14,2) not null default 0" json:"overall"`
|
||||
OverallFee decimal.Decimal `xorm:"numeric(14,2) not null default 0" json:"overallFee"`
|
||||
ConsumptionFee decimal.NullDecimal `xorm:"numeric(14,2)" json:"consumptionFee"`
|
||||
OverallPrice decimal.NullDecimal `xorm:"numeric(16,8)" json:"overallPrice"`
|
||||
Critical decimal.Decimal `xorm:"numeric(14,2) not null default 0" json:"critical"`
|
||||
CriticalFee decimal.Decimal `xorm:"numeric(14,2) not null default 0" json:"criticalFee"`
|
||||
CriticalPrice decimal.NullDecimal `xorm:"numeric(16,8)" json:"criticalPrice"`
|
||||
Peak decimal.Decimal `xorm:"numeric(14,2) not null default 0" json:"peak"`
|
||||
PeakFee decimal.Decimal `xorm:"numeric(14,2) not null default 0" json:"peakFee"`
|
||||
PeakPrice decimal.NullDecimal `xorm:"numeric(16,8)" json:"peakPrice"`
|
||||
Flat decimal.Decimal `xorm:"numeric(14,2) not null default 0" json:"flat"`
|
||||
FlatFee decimal.Decimal `xorm:"numeric(14,2) not null default 0" json:"flatFee"`
|
||||
FlatPrice decimal.NullDecimal `xorm:"numeric(16,8)" json:"flatPrice"`
|
||||
Valley decimal.Decimal `xorm:"numeric(14,2) not null default 0" json:"valley"`
|
||||
ValleyFee decimal.Decimal `xorm:"numeric(14,2) not null default 0" json:"valleyFee"`
|
||||
ValleyPrice decimal.NullDecimal `xorm:"numeric(16,8)" json:"valleyPrice"`
|
||||
Loss decimal.NullDecimal `xorm:"numeric(14,2)" json:"loss"`
|
||||
LossFee decimal.NullDecimal `xorm:"numeric(16,2)" json:"lossFee"`
|
||||
LossProportion decimal.NullDecimal `xorm:"numeric(16,15)" json:"lossProportion"`
|
||||
CustomerConsumption decimal.NullDecimal `xorm:"numeric(16,2)" json:"customerConsumption"`
|
||||
CustomerConsumptionFee decimal.NullDecimal `xorm:"numeric(14,2)" json:"customerConsumptionFee"`
|
||||
CustomerConsumptionCritical decimal.NullDecimal `xorm:"numeric(16,2)" json:"customerConsumptionCritical"`
|
||||
CustomerConsumptionCriticalFee decimal.NullDecimal `xorm:"numeric(14,2)" json:"customerConsumptionCriticalFee"`
|
||||
CustomerConsumptionPeak decimal.NullDecimal `xorm:"numeric(16,2)" json:"customerConsumptionPeak"`
|
||||
CustomerConsumptionPeakFee decimal.NullDecimal `xorm:"numeric(14,2)" json:"customerConsumptionPeakFee"`
|
||||
CustomerConsumptionFlat decimal.NullDecimal `xorm:"numeric(16,2)" json:"customerConsumptionFlat"`
|
||||
CustomerConsumptionFlatFee decimal.NullDecimal `xorm:"numeric(14,2)" json:"customerConsumptionFlatFee"`
|
||||
CustomerConsumptionValley decimal.NullDecimal `xorm:"numeric(16,2)" json:"customerConsumptionValley"`
|
||||
CustomerConsumptionValleyFee decimal.NullDecimal `xorm:"numeric(14,2)" json:"customerConsumptionValleyFee"`
|
||||
PublicConsumption decimal.NullDecimal `xorm:"numeric(14,2)" json:"publicConsumption"`
|
||||
PublicConsumptionFee decimal.NullDecimal `xorm:"numeric(14,2)" json:"publicConsumptionFee"`
|
||||
PublicConsumptionProportion decimal.NullDecimal `xorm:"numeric(16,15)" json:"publicConsumptionProportion"`
|
||||
PublicConsumptionCritical decimal.NullDecimal `xorm:"numeric(16,2)" json:"publicConsumptionCritical"`
|
||||
PublicConsumptionCriticalFee decimal.NullDecimal `xorm:"numeric(14,2)" json:"publicConsumptionCriticalFee"`
|
||||
PublicConsumptionPeak decimal.NullDecimal `xorm:"numeric(16,2)" json:"publicConsumptionPeak"`
|
||||
PublicConsumptionPeakFee decimal.NullDecimal `xorm:"numeric(14,2)" json:"publicConsumptionPeakFee"`
|
||||
PublicConsumptionFlat decimal.NullDecimal `xorm:"numeric(16,2)" json:"publicConsumptionFlat"`
|
||||
PublicConsumptionFlatFee decimal.NullDecimal `xorm:"numeric(14,2)" json:"publicConsumptionFlatFee"`
|
||||
PublicConsumptionValley decimal.NullDecimal `xorm:"numeric(16,2)" json:"publicConsumptionValley"`
|
||||
PublicConsumptionValleyFee decimal.NullDecimal `xorm:"numeric(14,2)" json:"publicConsumptionValleyFee"`
|
||||
BasicFee decimal.Decimal `xorm:"numeric(14,2) not null default 0" json:"basicFee"`
|
||||
BasicDilutedPrice decimal.NullDecimal `xorm:"numeric(18,8)" json:"basicDilutedPrice"`
|
||||
AdjustFee decimal.Decimal `xorm:"numeric(14,2) not null default 0" json:"adjustFee"`
|
||||
AdjustDilutedPrice decimal.NullDecimal `xorm:"numeric(18,8)" json:"adjustDilutedPrice"`
|
||||
MaintenanceDilutedPrice decimal.NullDecimal `xorm:"numeric(16,8)" json:"maintencanceDilutedPrice"`
|
||||
LossDilutedPrice decimal.NullDecimal `xorm:"numeric(16,8)" json:"lossDilutedPrice"`
|
||||
PublicConsumptionDilutedPrice decimal.NullDecimal `xorm:"numeric(16,8)" json:"publicConsumptionDilutedPrice"`
|
||||
FinalDilutedOverall decimal.NullDecimal `xorm:"numeric(14,2)" json:"finalDilutedOverall"`
|
||||
}
|
||||
|
||||
func (ReportSummary) TableName() string {
|
||||
return "report_summary"
|
||||
}
|
||||
|
||||
func (s ReportSummary) Validate() (bool, error) {
|
||||
amountSum := decimal.Sum(s.Critical, s.Peak, s.Valley)
|
||||
if amountSum.GreaterThan(s.Overall) {
|
||||
return false, errors.New("峰谷计量总量大于总计电量")
|
||||
}
|
||||
feeSum := decimal.Sum(s.CriticalFee, s.PeakFee, s.ValleyFee)
|
||||
if feeSum.GreaterThan(s.OverallFee) {
|
||||
return false, errors.New("峰谷计量费用大于总计费用")
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (s *ReportSummary) CalculatePrices() {
|
||||
s.ConsumptionFee = decimal.NewNullDecimal(s.OverallFee.Sub(s.BasicFee).Sub(s.AdjustFee))
|
||||
if s.Overall.GreaterThan(decimal.Zero) {
|
||||
s.OverallPrice = decimal.NewNullDecimal(s.ConsumptionFee.Decimal.Div(s.Overall).RoundBank(8))
|
||||
} else {
|
||||
s.OverallPrice = decimal.NewNullDecimal(decimal.Zero)
|
||||
}
|
||||
if s.Critical.GreaterThan(decimal.Zero) {
|
||||
s.CriticalPrice = decimal.NewNullDecimal(s.CriticalFee.Div(s.Critical).RoundBank(8))
|
||||
} else {
|
||||
s.CriticalPrice = decimal.NewNullDecimal(decimal.Zero)
|
||||
}
|
||||
if s.Peak.GreaterThan(decimal.Zero) {
|
||||
s.PeakPrice = decimal.NewNullDecimal(s.PeakFee.Div(s.Peak).RoundBank(8))
|
||||
} else {
|
||||
s.PeakPrice = decimal.NewNullDecimal(decimal.Zero)
|
||||
}
|
||||
if s.Valley.GreaterThan(decimal.Zero) {
|
||||
s.ValleyPrice = decimal.NewNullDecimal(s.ValleyFee.Div(s.Valley).RoundBank(8))
|
||||
} else {
|
||||
s.ValleyPrice = decimal.NewNullDecimal(decimal.Zero)
|
||||
}
|
||||
s.Flat = s.Overall.Sub(s.Critical).Sub(s.Peak).Sub(s.Valley)
|
||||
s.FlatFee = s.OverallFee.Sub(s.CriticalFee).Sub(s.PeakFee).Sub(s.ValleyFee)
|
||||
if s.Flat.GreaterThan(decimal.Zero) {
|
||||
s.FlatPrice = decimal.NewNullDecimal(s.FlatFee.Div(s.Flat).RoundBank(8))
|
||||
} else {
|
||||
s.FlatPrice = decimal.NewNullDecimal(decimal.Zero)
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import "time"
|
||||
type Session struct {
|
||||
Uid string `json:"uid"`
|
||||
Name string `json:"name"`
|
||||
Type int8 `json:"type"`
|
||||
Type int16 `json:"type"`
|
||||
Token string `json:"token"`
|
||||
ExpiresAt time.Time `json:"expiresAt" time_format:"simple_datetime" time_location:"shanghai"`
|
||||
}
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
package model
|
||||
|
||||
import "time"
|
||||
|
||||
type Created struct {
|
||||
CreatedAt time.Time `xorm:"timestampz not null created" json:"createdAt" time_format:"simple_datetime" time_location:"shanghai"`
|
||||
}
|
||||
|
||||
type CreatedWithUser struct {
|
||||
Created `xorm:"extends"`
|
||||
CreatedBy *string `xorm:"varchar(100)" json:"createdBy"`
|
||||
}
|
||||
|
||||
type Deleted struct {
|
||||
DeletedAt *time.Time `xorm:"timestampz deleted" json:"deletedAt" time_format:"simple_datetime" time_location:"shanghai"`
|
||||
}
|
||||
|
||||
type DeletedWithUser struct {
|
||||
Deleted `xorm:"extends"`
|
||||
DeletedBy *string `xorm:"varchar(120)" json:"deletedBy"`
|
||||
}
|
||||
|
||||
type CreatedAndModified struct {
|
||||
Created `xorm:"extends"`
|
||||
LastModifiedAt *time.Time `xorm:"timestampz updated" json:"lastModifiedAt" time_format:"simple_datetime" time_location:"shanghai"`
|
||||
}
|
||||
|
||||
type CreatedAndModifiedWithUser struct {
|
||||
CreatedAndModified `xorm:"extends"`
|
||||
CreatedBy *string `xorm:"varchar(100)" json:"createdBy"`
|
||||
LastModifiedBy *string `xorm:"varchar(100)" json:"lastModifiedBy"`
|
||||
}
|
||||
33
model/tenement.go
Normal file
33
model/tenement.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package model
|
||||
|
||||
import "electricity_bill_calc/types"
|
||||
|
||||
type Tenement struct {
|
||||
Id string `json:"id"`
|
||||
Park string `json:"parkId" db:"park_id"`
|
||||
FullName string `json:"fullName" db:"full_name"`
|
||||
ShortName *string `json:"shortName" db:"short_name"`
|
||||
Abbr string `json:"-"`
|
||||
Address string `json:"address"`
|
||||
ContactName string `json:"contactName" db:"contact_name"`
|
||||
ContactPhone string `json:"contactPhone" db:"contact_phone"`
|
||||
Building *string `json:"building"`
|
||||
BuildingName *string `json:"buildingName" db:"building_name"`
|
||||
OnFloor *string `json:"onFloor" db:"on_floor"`
|
||||
InvoiceInfo *InvoiceTitle `json:"invoiceInfo" db:"invoice_info"`
|
||||
MovedInAt *types.DateTime `json:"movedInAt" db:"moved_in_at"`
|
||||
MovedOutAt *types.DateTime `json:"movedOutAt" db:"moved_out_at"`
|
||||
CreatedAt types.DateTime `json:"createdAt" db:"created_at"`
|
||||
LastModifiedAt types.DateTime `json:"lastModifiedAt" db:"last_modified_at"`
|
||||
DeletedAt *types.DateTime `json:"deletedAt" db:"deleted_at"`
|
||||
}
|
||||
|
||||
type TenementMeter struct {
|
||||
ParkId string `db:"park_id"`
|
||||
TenementId string `db:"tenement_id"`
|
||||
MeterId string `db:"meter_id"`
|
||||
ForeignRelation bool `db:"foreign_relation"`
|
||||
AssociatedAt types.DateTime `db:"associated_at"`
|
||||
DisassociatedAt types.DateTime `db:"disassociated_at"`
|
||||
SynchronizedAt types.DateTime `db:"synchronized_at"`
|
||||
}
|
||||
33
model/top_up.go
Normal file
33
model/top_up.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"electricity_bill_calc/types"
|
||||
|
||||
"github.com/shopspring/decimal"
|
||||
)
|
||||
|
||||
type TopUp struct {
|
||||
TopUpCode string `json:"topUpCode" db:"top_up_code"`
|
||||
Park string `json:"parkId" db:"park_id"`
|
||||
Tenement string `json:"tenementId" db:"tenement_id"`
|
||||
TenementName string `json:"tenementName" db:"tenement_name"`
|
||||
Meter string `json:"meterId" db:"meter_id"`
|
||||
MeterAddress *string `json:"meterAddress" db:"meter_address"`
|
||||
ToppedUpAt types.DateTime `json:"toppedUpAt" db:"topped_up_at"`
|
||||
Amount decimal.Decimal `json:"amount" db:"amount"`
|
||||
PaymentType int16 `json:"paymentType" db:"payment_type"`
|
||||
SuccessfulSynchronized bool `json:"successfulSynchronized" db:"successful_synchronized"`
|
||||
SynchronizedAt *types.DateTime `json:"synchronizedAt" db:"synchronized_at"`
|
||||
CancelledAt *types.DateTime `json:"cancelledAt" db:"cancelled_at"`
|
||||
}
|
||||
|
||||
func (t TopUp) SyncStatus() int16 {
|
||||
switch {
|
||||
case t.SuccessfulSynchronized && t.SynchronizedAt != nil:
|
||||
return 1
|
||||
case !t.SuccessfulSynchronized && t.SynchronizedAt != nil:
|
||||
return 2
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
123
model/user.go
123
model/user.go
@@ -1,35 +1,114 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"electricity_bill_calc/types"
|
||||
"time"
|
||||
|
||||
"github.com/shopspring/decimal"
|
||||
)
|
||||
|
||||
const (
|
||||
USER_TYPE_ENT int8 = iota
|
||||
USER_TYPE_ENT int16 = iota
|
||||
USER_TYPE_SUP
|
||||
USER_TYPE_OPS
|
||||
)
|
||||
|
||||
type ManagementAccountCreationForm struct {
|
||||
Id *string `json:"id"`
|
||||
Username string `json:"username"`
|
||||
Name string `json:"name"`
|
||||
Contact *string `json:"contact"`
|
||||
Phone *string `json:"phone"`
|
||||
Type int16 `json:"type"`
|
||||
Enabled bool `json:"enabled"`
|
||||
Expires types.Date `json:"expires"`
|
||||
}
|
||||
|
||||
func (m ManagementAccountCreationForm) IntoUser() *User {
|
||||
return &User{
|
||||
Id: *m.Id,
|
||||
Username: m.Username,
|
||||
Password: "",
|
||||
ResetNeeded: false,
|
||||
UserType: m.Type,
|
||||
Enabled: m.Enabled,
|
||||
CreatedAt: nil,
|
||||
}
|
||||
}
|
||||
|
||||
func (m ManagementAccountCreationForm) IntoUserDetail() *UserDetail {
|
||||
return &UserDetail{
|
||||
Id: *m.Id,
|
||||
Name: &m.Name,
|
||||
Abbr: nil,
|
||||
Region: nil,
|
||||
Address: nil,
|
||||
Contact: m.Contact,
|
||||
Phone: m.Phone,
|
||||
UnitServiceFee: decimal.Zero,
|
||||
ServiceExpiration: m.Expires,
|
||||
CreatedAt: types.Now(),
|
||||
CreatedBy: nil,
|
||||
LastModifiedAt: types.Now(),
|
||||
LastModifiedBy: nil,
|
||||
DeletedAt: nil,
|
||||
DeletedBy: nil,
|
||||
}
|
||||
}
|
||||
|
||||
type UserModificationForm struct {
|
||||
Name string `json:"name"`
|
||||
Region *string `json:"region"`
|
||||
Address *string `json:"address"`
|
||||
Contact *string `json:"contact"`
|
||||
Phone *string `json:"phone"`
|
||||
UnitServiceFee *decimal.Decimal `json:"unitServiceFee"`
|
||||
}
|
||||
|
||||
type User struct {
|
||||
Created `xorm:"extends"`
|
||||
Id string `xorm:"varchar(120) pk not null" json:"id"`
|
||||
Username string `xorm:"varchar(30) not null" json:"username"`
|
||||
Password string `xorm:"varchar(256) not null" json:"-"`
|
||||
ResetNeeded bool `xorm:"bool not null" json:"resetNeeded"`
|
||||
Type int8 `xorm:"smallint not null" json:"type"`
|
||||
Enabled bool `xorm:"bool not null" json:"enabled"`
|
||||
Id string `json:"id"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
ResetNeeded bool `json:"resetNeeded"`
|
||||
UserType int16 `db:"type"`
|
||||
Enabled bool `json:"enabled"`
|
||||
CreatedAt *time.Time `json:"createdAt"`
|
||||
}
|
||||
|
||||
func (User) TableName() string {
|
||||
return "user"
|
||||
type UserDetail struct {
|
||||
Id string `json:"id"`
|
||||
Name *string `json:"name"`
|
||||
Abbr *string `json:"abbr"`
|
||||
Region *string `json:"region"`
|
||||
Address *string `json:"address"`
|
||||
Contact *string `json:"contact"`
|
||||
Phone *string `json:"phone"`
|
||||
UnitServiceFee decimal.Decimal `db:"unit_service_fee" json:"unitServiceFee"`
|
||||
ServiceExpiration types.Date `json:"serviceExpiration"`
|
||||
CreatedAt types.DateTime `json:"createdAt"`
|
||||
CreatedBy *string `json:"createdBy"`
|
||||
LastModifiedAt types.DateTime `json:"lastModifiedAt"`
|
||||
LastModifiedBy *string `json:"lastModifiedBy"`
|
||||
DeletedAt *types.DateTime `json:"deletedAt"`
|
||||
DeletedBy *string `json:"deletedBy"`
|
||||
}
|
||||
|
||||
type UserWithCredentials struct {
|
||||
Created `xorm:"extends"`
|
||||
Id string `xorm:"varchar(120) pk not null" json:"id"`
|
||||
Username string `xorm:"varchar(30) not null" json:"username"`
|
||||
Password string `xorm:"varchar(256) not null" json:"credential"`
|
||||
ResetNeeded bool `xorm:"bool not null" json:"resetNeeded"`
|
||||
Type int8 `xorm:"smallint not null" json:"type"`
|
||||
Enabled bool `xorm:"bool not null" json:"enabled"`
|
||||
}
|
||||
|
||||
func (UserWithCredentials) TableName() string {
|
||||
return "user"
|
||||
type UserWithDetail struct {
|
||||
Id string `json:"id"`
|
||||
Username string `json:"username"`
|
||||
ResetNeeded bool `json:"resetNeeded"`
|
||||
UserType int16 `db:"type" json:"type"`
|
||||
Enabled bool `json:"enabled"`
|
||||
Name *string `json:"name"`
|
||||
Abbr *string `json:"abbr"`
|
||||
Region *string `json:"region"`
|
||||
Address *string `json:"address"`
|
||||
Contact *string `json:"contact"`
|
||||
Phone *string `json:"phone"`
|
||||
UnitServiceFee decimal.Decimal `db:"unit_service_fee" json:"unitServiceFee"`
|
||||
ServiceExpiration types.Date `json:"serviceExpiration"`
|
||||
CreatedAt types.DateTime `json:"createdAt"`
|
||||
CreatedBy *string `json:"createdBy"`
|
||||
LastModifiedAt types.DateTime `json:"lastModifiedAt"`
|
||||
LastModifiedBy *string `json:"lastModifiedBy"`
|
||||
}
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/shopspring/decimal"
|
||||
)
|
||||
|
||||
type UserCharge struct {
|
||||
Created `xorm:"extends"`
|
||||
Seq int64 `xorm:"bigint pk not null autoincr" json:"seq"`
|
||||
UserId string `xorm:"varchar(120) not null" json:"userId"`
|
||||
Fee decimal.NullDecimal `xorm:"numeric(12,2)" json:"fee"`
|
||||
Discount decimal.NullDecimal `xorm:"numeric(5,4)" json:"discount"`
|
||||
Amount decimal.NullDecimal `xorm:"numeric(12,2)" json:"amount"`
|
||||
ChargeTo time.Time `xorm:"date not null" json:"chargeTo" time_format:"simple_date" time_location:"shanghai"`
|
||||
Settled bool `xorm:"bool not null default false" json:"settled"`
|
||||
SettledAt *time.Time `xorm:"timestampz" json:"settledAt" time_format:"simple_datetime" time_location:"shanghai"`
|
||||
Cancelled bool `xorm:"bool not null default false" json:"cancelled"`
|
||||
CancelledAt *time.Time `xorm:"timestampz" json:"cancelledAt" time_format:"simple_datetime" time_location:"shanghai"`
|
||||
Refunded bool `xorm:"bool not null default false" json:"refunded"`
|
||||
RefundedAt *time.Time `xorm:"timestampz" json:"refundedAt" time_format:"simple_datetime" time_location:"shanghai"`
|
||||
}
|
||||
|
||||
func (UserCharge) TableName() string {
|
||||
return "user_charge"
|
||||
}
|
||||
|
||||
type ChargeWithName struct {
|
||||
UserDetail `xorm:"extends"`
|
||||
UserCharge `xorm:"extends"`
|
||||
}
|
||||
|
||||
func (ChargeWithName) TableName() string {
|
||||
return "user_detail"
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/shopspring/decimal"
|
||||
)
|
||||
|
||||
type UserDetail struct {
|
||||
CreatedAndModifiedWithUser `xorm:"extends"`
|
||||
DeletedWithUser `xorm:"extends"`
|
||||
Id string `xorm:"varchar(120) pk not null" json:"-"`
|
||||
Name *string `xorm:"varchar(100)" json:"name"`
|
||||
Abbr *string `xorm:"varchar(50)" json:"abbr"`
|
||||
Region *string `xorm:"varchar(10)" json:"region"`
|
||||
Address *string `xorm:"varchar(120)" json:"address"`
|
||||
Contact *string `xorm:"varchar(100)" json:"contact"`
|
||||
Phone *string `xorm:"varchar(50)" json:"phone"`
|
||||
UnitServiceFee decimal.Decimal `xorm:"numeric(8,2) not null" json:"unitServiceFee"`
|
||||
ServiceExpiration time.Time `xorm:"date not null" json:"serviceExpiration" time_format:"simple_date" time_location:"shanghai"`
|
||||
}
|
||||
|
||||
func (UserDetail) TableName() string {
|
||||
return "user_detail"
|
||||
}
|
||||
|
||||
type JoinedUserDetail struct {
|
||||
UserDetail `xorm:"extends"`
|
||||
Id string `json:"id"`
|
||||
Username string `json:"username"`
|
||||
Type int8 `json:"type"`
|
||||
Enabled bool `json:"enabled"`
|
||||
}
|
||||
|
||||
func (JoinedUserDetail) TableName() string {
|
||||
return "user"
|
||||
}
|
||||
|
||||
type FullJoinedUserDetail struct {
|
||||
UserDetail `xorm:"extends"`
|
||||
User `xorm:"extends"`
|
||||
}
|
||||
|
||||
func (FullJoinedUserDetail) TableName() string {
|
||||
return "user_detail"
|
||||
}
|
||||
|
||||
type UserDetailSimplified struct {
|
||||
Id string `xorm:"varchar(120) pk not null" json:"id"`
|
||||
Name *string `xorm:"varchar(100)" json:"name"`
|
||||
Abbr *string `xorm:"varchar(50)" json:"abbr"`
|
||||
Region *string `xorm:"varchar(10)" json:"region"`
|
||||
Address *string `xorm:"varchar(120)" json:"address"`
|
||||
Contact *string `xorm:"varchar(100)" json:"contact"`
|
||||
Phone *string `xorm:"varchar(50)" json:"phone"`
|
||||
}
|
||||
|
||||
func (UserDetailSimplified) TableName() string {
|
||||
return "user_detail"
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
package model
|
||||
|
||||
import "github.com/shopspring/decimal"
|
||||
|
||||
type WillDilutedFee struct {
|
||||
CreatedAndModified `xorm:"extends"`
|
||||
Id string `xorm:"varchar(120) pk not null" json:"id"`
|
||||
ReportId string `xorm:"varchar(120) not null" json:"reportId"`
|
||||
SourceId *string `xorm:"varchar(120)" json:"sourceId"`
|
||||
Name string `xorm:"varchar(50) not null" json:"name"`
|
||||
Fee decimal.Decimal `xorm:"numeric(8,2) not null default 0" json:"fee"`
|
||||
Memo *string `xorm:"text" json:"memo"`
|
||||
}
|
||||
|
||||
func (WillDilutedFee) TableName() string {
|
||||
return "will_diluted_fee"
|
||||
}
|
||||
84
model/withdraw.go
Normal file
84
model/withdraw.go
Normal file
@@ -0,0 +1,84 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"electricity_bill_calc/types"
|
||||
"github.com/shopspring/decimal"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Withdraw struct {
|
||||
Park SimplifiedPark `json:"park"`
|
||||
Report SimplifiedReport `json:"report"`
|
||||
User UserInfos `json:"user"` // 简易用户详细信息
|
||||
}
|
||||
|
||||
// 简易园区信息
|
||||
type SimplifiedPark struct {
|
||||
Address *string `json:"address"` // 园区地址
|
||||
Area *string `json:"area"` // 园区面积
|
||||
Capacity *string `json:"capacity"` // 供电容量
|
||||
Category int16 `json:"category"` // 用电分类,0:两部制,1:单一峰谷,2:单一单一
|
||||
Contact *string `json:"contact"` // 园区联系人
|
||||
ID string `json:"id"` // 园区ID
|
||||
Meter04KvType int16 `json:"meter04kvType"` // 户表计量类型,0:非峰谷,1:峰谷
|
||||
Name string `json:"name"` // 园区名称
|
||||
Phone *string `json:"phone"` // 园区联系人电话
|
||||
Region *string `json:"region"` // 园区所在行政区划
|
||||
Tenement *string `json:"tenement"` // 园区住户数量
|
||||
UserID string `json:"userId"` // 园区所属用户ID
|
||||
}
|
||||
|
||||
// 简易核算报表信息
|
||||
type SimplifiedReport struct {
|
||||
ID string `json:"id"` // 报表ID
|
||||
LastWithdrawAppliedAt *string `json:"lastWithdrawAppliedAt"` // 最后一次申请撤回的时间,格式为 yyyy-MM-dd HH:mm:ss
|
||||
LastWithdrawAuditAt *string `json:"lastWithdrawAuditAt"` // 最后一次申请审核的时间,格式为 yyyy-MM-dd HH:mm:ss
|
||||
Message *string `json:"message"` // 当前状态的错误提示
|
||||
ParkID string `json:"parkId"` // 所属园区ID
|
||||
PeriodBegin string `json:"periodBegin"` // 核算起始日期,格式为 yyyy-MM-dd
|
||||
PeriodEnd string `json:"periodEnd"` // 核算结束日期,格式为 yyyy-MM-dd
|
||||
Published bool `json:"published"` // 是否已发布
|
||||
PublishedAt *string `json:"publishedAt"` // 发布时间
|
||||
Status float64 `json:"status,omitempty"` // 当前状态,0:计算任务已队列,1:计算任务已完成,2:计算数据不足
|
||||
Withdraw int16 `json:"withdraw"` // 报表撤回状态,0:未撤回,1:申请撤回中,2:申请拒绝,3:申请批准
|
||||
}
|
||||
|
||||
// 简易用户信息
|
||||
type UserInfos struct {
|
||||
Address *string `json:"address"` // 用户地址
|
||||
Contact *string `json:"contact"` // 用户联系人
|
||||
ID string `json:"id"` // 用户ID
|
||||
Name *string `json:"name"` // 用户名称
|
||||
Phone *string `json:"phone"` // 用户联系人电话
|
||||
Region *string `json:"region"` // 用户所在行政区划
|
||||
}
|
||||
|
||||
//用于映射数据库的报表结构体
|
||||
type ReportRes struct {
|
||||
ReportId string `db:"report_id"`
|
||||
LastWithdrawAppliedAt *time.Time `db:"last_withdraw_applied_at"`
|
||||
LastWithdrawAuditAt *time.Time `db:"last_withdraw_audit_at"`
|
||||
ParkID string `db:"report_park_id"`
|
||||
Period types.DateRange `db:"period"`
|
||||
Published bool `db:"published"`
|
||||
PublishedAt *time.Time `db: "published_at"`
|
||||
Withdraw int16 `db:"withdraw"`
|
||||
ParkAddress *string `db:"park_address"`
|
||||
Area decimal.NullDecimal `db:"area"`
|
||||
Capacity decimal.NullDecimal `db:"capacity"`
|
||||
Category int16
|
||||
ParkContact *string `db:"park_contact"`
|
||||
ParkId string `db:"park_id"`
|
||||
Meter04KVType int16 `db:"meter_04kv_type"`
|
||||
ParkName string `db:"park_name"`
|
||||
ParkPhone *string `db:"park_phone"`
|
||||
ParkRegion string `db:"park_region"`
|
||||
TenementQuantity decimal.NullDecimal `db:"tenement_quantity"`
|
||||
UserID string `db:"user_id"`
|
||||
Address *string
|
||||
Contact string `db:"user_detail_contact"`
|
||||
ID string `db:"ud_id"`
|
||||
Name *string `db:"user_detail_name"`
|
||||
Phone string `db:"user_detail_phone"`
|
||||
Region *string `db:"user_detail_region"`
|
||||
}
|
||||
241
repository/calculate.go
Normal file
241
repository/calculate.go
Normal file
@@ -0,0 +1,241 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"electricity_bill_calc/global"
|
||||
"electricity_bill_calc/logger"
|
||||
"electricity_bill_calc/model"
|
||||
"electricity_bill_calc/types"
|
||||
"time"
|
||||
|
||||
"github.com/doug-martin/goqu/v9"
|
||||
_ "github.com/doug-martin/goqu/v9/dialect/postgres"
|
||||
"github.com/georgysavva/scany/v2/pgxscan"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type _CalculateRepository struct {
|
||||
log *zap.Logger
|
||||
ds goqu.DialectWrapper
|
||||
}
|
||||
|
||||
var CalculateRepository = _CalculateRepository{
|
||||
log: logger.Named("Repository", "Calculate"),
|
||||
ds: goqu.Dialect("postgres"),
|
||||
}
|
||||
|
||||
// 获取当前正在等待计算的核算任务ID列表
|
||||
func (cr _CalculateRepository) ListPendingTasks() ([]string, error) {
|
||||
cr.log.Info("获取当前正在等待计算的核算任务ID列表")
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
var ids []string
|
||||
querySql, queryArgs, _ := cr.ds.
|
||||
From("report_task").
|
||||
Select("id").
|
||||
Where(goqu.C("status").Eq(model.REPORT_CALCULATE_TASK_STATUS_PENDING)).
|
||||
Prepared(true).ToSQL()
|
||||
if err := pgxscan.Select(ctx, global.DB, &ids, querySql, queryArgs...); err != nil {
|
||||
cr.log.Error("未能获取到当前正在等待计算的核算任务ID列表", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
return ids, nil
|
||||
}
|
||||
|
||||
// 更新指定报表的核算状态
|
||||
func (cr _CalculateRepository) UpdateReportTaskStatus(rid string, status int16, message *string) (bool, error) {
|
||||
cr.log.Info("更新指定报表的核算状态", zap.String("Report", rid), zap.Int16("Status", status))
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
currentTime := types.Now()
|
||||
updateSql, updateArgs, _ := cr.ds.
|
||||
Update("report_task").
|
||||
Set(goqu.Record{
|
||||
"status": status,
|
||||
"last_modified_at": currentTime,
|
||||
"message": message,
|
||||
}).
|
||||
Where(goqu.C("id").Eq(rid)).
|
||||
Prepared(true).ToSQL()
|
||||
res, err := global.DB.Exec(ctx, updateSql, updateArgs...)
|
||||
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 res.RowsAffected() > 0, nil
|
||||
}
|
||||
|
||||
//获取当前园区中所有公摊表计与商户表计之间的关联关系,包括已经解除的
|
||||
func (cr _CalculateRepository) GetAllPoolingMeterRelations(pid string, revokedAfter time.Time) ([]model.MeterRelation, error) {
|
||||
cr.log.Info("获取当前园区中所有公摊表计与商户表计之间的关联关系,包括已经解除的", zap.String("pid", pid), zap.Time("revokedAfter", revokedAfter))
|
||||
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
relationsSql, relationsArgs, _ := cr.ds.
|
||||
From(goqu.T("meter_relations")).
|
||||
Where(goqu.I("park_id").Eq(pid)).
|
||||
Where(goqu.Or(
|
||||
goqu.I("revoked_at").IsNull(),
|
||||
goqu.I("revoked_at").Gte(revokedAfter),
|
||||
)).ToSQL()
|
||||
|
||||
var meterRelation []model.MeterRelation
|
||||
|
||||
err := pgxscan.Select(ctx, global.DB, meterRelation, relationsSql, relationsArgs...)
|
||||
if err != nil {
|
||||
cr.log.Error("获取当前园区中所有公摊表计与商户表计之间的关联关系,包括已经解除的出错", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
return meterRelation, nil
|
||||
}
|
||||
|
||||
//获取当前园区中所有的商户与表计的关联关系,包括已经解除的
|
||||
func (cr _CalculateRepository) GetAllTenementMeterRelations(pid string, associatedBefore time.Time, disassociatedAfter time.Time) ([]model.TenementMeter, error) {
|
||||
cr.log.Info("获取当前园区中所有的商户与表计的关联关系,包括已经解除的", zap.String("pid", pid), zap.Time("associatedBefore", associatedBefore), zap.Time("disassociatedAfter", disassociatedAfter))
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
relationsQuerySql, relationsQueryArgs, _ := cr.ds.
|
||||
From(goqu.T("tenement_meter")).
|
||||
Where(goqu.I("park_id").Eq(pid)).
|
||||
Where(goqu.And(
|
||||
goqu.I("associated_at").IsNull(),
|
||||
goqu.I("associated_at").Lte(associatedBefore),
|
||||
)).
|
||||
Where(goqu.And(
|
||||
goqu.I("associated_at").IsNull(),
|
||||
goqu.I("associated_at").Gte(disassociatedAfter),
|
||||
)).ToSQL()
|
||||
|
||||
var tenementMeter []model.TenementMeter
|
||||
|
||||
err := pgxscan.Select(ctx, global.DB, tenementMeter, relationsQuerySql, relationsQueryArgs...)
|
||||
if err != nil {
|
||||
cr.log.Error("获取当前园区中所有的商户与表计的关联关系,包括已经解除的", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
return tenementMeter, nil
|
||||
|
||||
}
|
||||
|
||||
//获取指定报表中所有涉及到的指定类型表计在核算时间段内的所有读数数据
|
||||
func (cr _CalculateRepository) GetMeterReadings(rid string, meterType int16) ([]model.MeterReading, error) {
|
||||
cr.log.Info("获取指定报表中所有涉及到的指定类型表计在核算时间段内的所有读数数据", zap.String("rid", rid), zap.Int16("meterType", meterType))
|
||||
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
readingsQuerySql, readingsQueryArgs, _ := cr.ds.
|
||||
From(goqu.T("meter_reading").As(goqu.I("mr"))).
|
||||
Join(
|
||||
goqu.T("report").As("r"),
|
||||
goqu.On(goqu.I("r.park_id").Eq(goqu.I("mr.park_id"))),
|
||||
).
|
||||
Where(
|
||||
goqu.I("r.id").Eq(rid),
|
||||
goqu.I("mr.meter_type").Eq(meterType),
|
||||
// TODO:2023.08.02 此方法出错优先查看是否这里出问题
|
||||
goqu.I("mr.read_at::date <@ r.period"),
|
||||
).
|
||||
Order(goqu.I("mr.read_at").Asc()).Select(goqu.I("mr.*")).ToSQL()
|
||||
|
||||
var readings []model.MeterReading
|
||||
|
||||
err := pgxscan.Select(ctx, global.DB, readings, readingsQuerySql, readingsQueryArgs...)
|
||||
if err != nil {
|
||||
cr.log.Error("获取指定报表中所有涉及到的指定类型表计在核算时间段内的所有读数数据出错", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
return readings, nil
|
||||
}
|
||||
|
||||
// 获取指定报表中所有涉及到的表计在核算起始日期前的最后一次读数
|
||||
func (cr _CalculateRepository) GetLastPeriodReadings(rid string, meterType int16) ([]model.MeterReading, error) {
|
||||
cr.log.Info("获取指定报表中所有涉及到的表计在核算起始日期前的最后一次读数", zap.String("rid", rid), zap.Int16("meterType", meterType))
|
||||
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
readingsSql, readingsArgs, _ := cr.ds.From(goqu.T("meter_reading").As("mr")).
|
||||
Select(
|
||||
goqu.MAX("mr.read_at").As("read_at"),
|
||||
goqu.I("mr.park_id"),
|
||||
goqu.I("mr.meter_id"),
|
||||
goqu.I("mr.meter_type"),
|
||||
goqu.I("mr.ratio"),
|
||||
goqu.I("mr.overall"),
|
||||
goqu.I("mr.critical"),
|
||||
goqu.I("mr.peak"),
|
||||
goqu.I("mr.flat"),
|
||||
goqu.I("mr.valley"),
|
||||
).
|
||||
Join(
|
||||
goqu.T("report").As("r"),
|
||||
goqu.On(goqu.I("r.park_id").Eq(goqu.I("mr.park_id"))),
|
||||
).
|
||||
Where(
|
||||
goqu.I("r.id").Eq(rid),
|
||||
goqu.I("mr.meter_type").Eq(meterType),
|
||||
goqu.I(" mr.read_at::date <= lower(r.period)"),
|
||||
).
|
||||
GroupBy(
|
||||
goqu.I("mr.park_id"),
|
||||
goqu.I("mr.meter_id"),
|
||||
goqu.I("mr.meter_type"),
|
||||
goqu.I("mr.ratio"),
|
||||
goqu.I("mr.overall"),
|
||||
goqu.I("mr.critical"),
|
||||
goqu.I("mr.peak"),
|
||||
goqu.I("mr.flat"),
|
||||
goqu.I("mr.valley"),
|
||||
goqu.I("r.period"),
|
||||
).ToSQL()
|
||||
|
||||
var readings []model.MeterReading
|
||||
err := pgxscan.Select(ctx, global.DB, readings, readingsSql, readingsArgs...)
|
||||
if err != nil {
|
||||
cr.log.Error("获取指定报表中所有涉及到的表计在核算起始日期前的最后一次读数出错", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
return readings, nil
|
||||
}
|
||||
|
||||
// 取得指定报表所涉及的所有商户信息
|
||||
func (cr _CalculateRepository) GetAllTenements(rid string) ([]model.Tenement, error) {
|
||||
cr.log.Info("取得指定报表所涉及的所有商户信息", zap.String("rid", rid))
|
||||
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
tenementQuerySql, tenementQueryArgs, _ := cr.ds.
|
||||
From(goqu.T("tenement").As("t")).
|
||||
LeftJoin(
|
||||
goqu.T("park_building").As("b"),
|
||||
goqu.On(goqu.I("b.id").Eq(goqu.I("t.building"))),
|
||||
).
|
||||
Join(
|
||||
goqu.T("report").As("r"),
|
||||
goqu.On(goqu.I("r.park_id").Eq(goqu.I("t.park_id"))),
|
||||
).
|
||||
Select(
|
||||
goqu.I("t.*"),
|
||||
goqu.I("b.name").As("building_name"),
|
||||
).
|
||||
Where(
|
||||
goqu.I("r.id").Eq(rid),
|
||||
goqu.I("t.moved_in_at <= upper(r.period)"),
|
||||
).ToSQL()
|
||||
|
||||
var tenements []model.Tenement
|
||||
err := pgxscan.Select(ctx, global.DB, tenements, tenementQuerySql, tenementQueryArgs...)
|
||||
if err != nil {
|
||||
cr.log.Error("取得指定报表所涉及的所有商户信息出错", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
return tenements, nil
|
||||
}
|
||||
152
repository/charge.go
Normal file
152
repository/charge.go
Normal file
@@ -0,0 +1,152 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
"electricity_bill_calc/config"
|
||||
"electricity_bill_calc/global"
|
||||
"electricity_bill_calc/logger"
|
||||
"electricity_bill_calc/model"
|
||||
"electricity_bill_calc/types"
|
||||
"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/samber/lo"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type _ChargeRepository struct {
|
||||
log *zap.Logger
|
||||
ds goqu.DialectWrapper
|
||||
}
|
||||
|
||||
var ChargeRepository = &_ChargeRepository{
|
||||
log: logger.Named("Repository", "Charge"),
|
||||
ds: goqu.Dialect("postgres"),
|
||||
}
|
||||
|
||||
// 分页查询用户的充值记录
|
||||
func (cr _ChargeRepository) FindCharges(page uint, beginTime, endTime *types.Date, keyword *string) ([]*model.UserChargeDetail, int64, error) {
|
||||
cr.log.Info("查询用户的充值记录。", logger.DateFieldp("beginTime", beginTime), logger.DateFieldp("endTime", endTime), zap.Stringp("keyword", keyword), zap.Uint("page", page))
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
chargeQuery := cr.ds.
|
||||
From(goqu.T("user_charge").As("c")).
|
||||
Join(goqu.T("user_detail").As("ud"), goqu.On(goqu.I("c.user_id").Eq(goqu.I("ud.id")))).
|
||||
Join(goqu.T("user").As("u"), goqu.On(goqu.I("ud.id").Eq(goqu.I("u.id")))).
|
||||
Select(
|
||||
"c.seq", "c.user_id", "ud.name", "c.fee", "c.discount", "c.amount", "c.charge_to",
|
||||
"c.settled", "c.settled_at", "c.cancelled", "c.cancelled_at", "c.refunded", "c.refunded_at", "c.created_at",
|
||||
)
|
||||
countQuery := cr.ds.
|
||||
From(goqu.T("user_charge").As("c")).
|
||||
Join(goqu.T("user_detail").As("ud"), goqu.On(goqu.I("c.user_id").Eq(goqu.I("ud.id")))).
|
||||
Join(goqu.T("user").As("u"), goqu.On(goqu.I("ud.id").Eq(goqu.I("u.id")))).
|
||||
Select(goqu.COUNT("*"))
|
||||
|
||||
if keyword != nil && len(*keyword) > 0 {
|
||||
pattern := fmt.Sprintf("%%%s%%", *keyword)
|
||||
chargeQuery = chargeQuery.Where(goqu.Or(
|
||||
goqu.I("ud.name").ILike(pattern),
|
||||
goqu.I("ud.abbr").ILike(pattern),
|
||||
goqu.I("u.username").ILike(pattern),
|
||||
))
|
||||
countQuery = countQuery.Where(goqu.Or(
|
||||
goqu.I("ud.name").ILike(pattern),
|
||||
goqu.I("ud.abbr").ILike(pattern),
|
||||
goqu.I("u.username").ILike(pattern),
|
||||
))
|
||||
}
|
||||
|
||||
if beginTime != nil {
|
||||
chargeQuery = chargeQuery.Where(goqu.I("c.created_at").Gte(beginTime.ToBeginningOfDate()))
|
||||
countQuery = countQuery.Where(goqu.I("c.created_at").Gte(beginTime.ToBeginningOfDate()))
|
||||
}
|
||||
|
||||
if endTime != nil {
|
||||
chargeQuery = chargeQuery.Where(goqu.I("c.created_at").Lte(endTime.ToEndingOfDate()))
|
||||
countQuery = countQuery.Where(goqu.I("c.created_at").Lte(endTime.ToEndingOfDate()))
|
||||
}
|
||||
|
||||
chargeQuery = chargeQuery.Order(goqu.I("c.created_at").Desc())
|
||||
|
||||
currentPostion := (page - 1) * config.ServiceSettings.ItemsPageSize
|
||||
chargeQuery = chargeQuery.Offset(currentPostion).Limit(config.ServiceSettings.ItemsPageSize)
|
||||
|
||||
chargeSql, chargeArgs, _ := chargeQuery.Prepared(true).ToSQL()
|
||||
countSql, countArgs, _ := countQuery.Prepared(true).ToSQL()
|
||||
|
||||
var (
|
||||
charges []*model.UserChargeDetail = make([]*model.UserChargeDetail, 0)
|
||||
total int64
|
||||
)
|
||||
if err := pgxscan.Select(ctx, global.DB, &charges, chargeSql, chargeArgs...); err != nil {
|
||||
cr.log.Error("查询用户的充值记录失败。", zap.Error(err))
|
||||
return make([]*model.UserChargeDetail, 0), 0, err
|
||||
}
|
||||
if err := pgxscan.Get(ctx, global.DB, &total, countSql, countArgs...); err != nil {
|
||||
cr.log.Error("查询用户的充值记录总数失败。", zap.Error(err))
|
||||
return make([]*model.UserChargeDetail, 0), 0, err
|
||||
}
|
||||
return charges, total, nil
|
||||
}
|
||||
|
||||
// 在用户充值记录中创建一条新的记录
|
||||
func (cr _ChargeRepository) CreateChargeRecord(tx pgx.Tx, ctx context.Context, uid string, fee, discount, amount *float64, chargeTo types.Date) (bool, error) {
|
||||
createQuery, createArgs, _ := cr.ds.
|
||||
Insert(goqu.T("user_charge")).
|
||||
Cols("user_id", "fee", "discount", "amount", "charge_to", "created_at").
|
||||
Vals(goqu.Vals{uid, fee, discount, amount, chargeTo, types.Now()}).
|
||||
Prepared(true).ToSQL()
|
||||
|
||||
rs, err := tx.Exec(ctx, createQuery, createArgs...)
|
||||
if err != nil {
|
||||
cr.log.Error("创建用户充值记录失败。", zap.Error(err))
|
||||
return false, err
|
||||
}
|
||||
return rs.RowsAffected() > 0, nil
|
||||
}
|
||||
|
||||
// 撤销用户的充值记录
|
||||
func (cr _ChargeRepository) CancelCharge(tx pgx.Tx, ctx context.Context, uid string, seq int64) (bool, error) {
|
||||
updateQuerySql, updateArgs, _ := cr.ds.
|
||||
Update(goqu.T("user_charge")).
|
||||
Set(goqu.Record{"cancelled": true, "cancelled_at": types.Now()}).
|
||||
Where(goqu.I("user_id").Eq(uid), goqu.I("seq").Eq(seq)).
|
||||
Prepared(true).ToSQL()
|
||||
|
||||
rs, err := tx.Exec(ctx, updateQuerySql, updateArgs...)
|
||||
if err != nil {
|
||||
cr.log.Error("撤销用户的充值记录失败。", zap.Error(err))
|
||||
return false, err
|
||||
}
|
||||
return rs.RowsAffected() > 0, nil
|
||||
}
|
||||
|
||||
// 检索用户最近有效的服务期限
|
||||
func (cr _ChargeRepository) LatestValidChargeTo(tx pgx.Tx, ctx context.Context, uid string) (*types.Date, error) {
|
||||
searchSql, searchArgs, _ := cr.ds.
|
||||
From(goqu.T("user_charge")).
|
||||
Select("charge_to").
|
||||
Where(
|
||||
goqu.I("settled").Eq(true),
|
||||
goqu.I("cancelled").Eq(false),
|
||||
goqu.I("refunded").Eq(false),
|
||||
goqu.I("user_id").Eq(uid),
|
||||
).
|
||||
Prepared(true).ToSQL()
|
||||
|
||||
var chargeTo []*types.Date
|
||||
if err := pgxscan.Select(ctx, tx, &chargeTo, searchSql, searchArgs...); err != nil {
|
||||
cr.log.Error("检索用户有效服务期限列表失败。", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
if len(chargeTo) == 0 {
|
||||
return nil, fmt.Errorf("无法找到用户最近的有效服务期限。")
|
||||
}
|
||||
lastCharge := lo.MaxBy(chargeTo, func(a, b *types.Date) bool { return a.Time.After(b.Time) })
|
||||
return lastCharge, nil
|
||||
}
|
||||
449
repository/god_mode.go
Normal file
449
repository/god_mode.go
Normal file
@@ -0,0 +1,449 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
"electricity_bill_calc/global"
|
||||
"electricity_bill_calc/logger"
|
||||
"fmt"
|
||||
"github.com/doug-martin/goqu/v9"
|
||||
"github.com/georgysavva/scany/v2/pgxscan"
|
||||
"github.com/jackc/pgx/v5"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type _GMRepository struct {
|
||||
log *zap.Logger
|
||||
ds goqu.DialectWrapper
|
||||
}
|
||||
|
||||
var GMRepository = &_GMRepository{
|
||||
log: logger.Named("Repository", "GM"),
|
||||
ds: goqu.Dialect("postgres"),
|
||||
}
|
||||
|
||||
func (gm _GMRepository) DeleteMeterBinding(ctx context.Context, tx pgx.Tx, pid string, tenements []string, meterCodes ...[]string) error {
|
||||
DeleteQuery := gm.ds.From(goqu.T("tenement_meter")).
|
||||
Where(goqu.I("park_id").Eq(pid)).
|
||||
Delete()
|
||||
|
||||
if len(tenements) > 0 {
|
||||
DeleteQuery = DeleteQuery.
|
||||
Where(goqu.I("tenement_id").In(tenements))
|
||||
}
|
||||
|
||||
if len(meterCodes) > 0 {
|
||||
DeleteQuery = DeleteQuery.
|
||||
Where(goqu.I("meter_id").In(meterCodes))
|
||||
}
|
||||
|
||||
DeleteQuerySql, DeleteQueryArgs, _ := DeleteQuery.ToSQL()
|
||||
|
||||
_, err := tx.Exec(ctx, DeleteQuerySql, DeleteQueryArgs...)
|
||||
if err != nil {
|
||||
gm.log.Error("数据库在删除tenement_meter表数据中出错", zap.Error(err))
|
||||
tx.Rollback(ctx)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gm _GMRepository) DeleteTenements(ctx context.Context, tx pgx.Tx, pid string, tenements ...[]string) error {
|
||||
DeleteTenements := gm.ds.
|
||||
From("tenement").
|
||||
Where(goqu.I("park_id").Eq(pid)).
|
||||
Delete()
|
||||
|
||||
fmt.Println(len(tenements))
|
||||
if len(tenements) > 0 {
|
||||
DeleteTenements = DeleteTenements.
|
||||
Where(goqu.I("id").In(tenements))
|
||||
}
|
||||
|
||||
DeleteTenementsSql, DeleteTenementsArgs, _ := DeleteTenements.ToSQL()
|
||||
|
||||
_, err := tx.Exec(ctx, DeleteTenementsSql, DeleteTenementsArgs...)
|
||||
if err != nil {
|
||||
tx.Rollback(ctx)
|
||||
gm.log.Error("删除商户信息出错", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gm _GMRepository) DeleteInvoices(ctx context.Context, tx pgx.Tx, parks string, val ...[]string) error {
|
||||
if len(val) > 0 {
|
||||
updateQuery, updateQueryArgs, _ := gm.ds.
|
||||
Update(goqu.T("report_tenement")).
|
||||
Set(goqu.Record{"invoice": nil}).
|
||||
Where(goqu.I("invoice").In(val)).
|
||||
Where(
|
||||
goqu.I("report_id").
|
||||
Eq(
|
||||
gm.ds.
|
||||
From(goqu.T("report")).
|
||||
Where(goqu.I("park_id").Eq(parks)),
|
||||
),
|
||||
).ToSQL()
|
||||
_, err := tx.Exec(ctx, updateQuery, updateQueryArgs...)
|
||||
if err != nil {
|
||||
tx.Rollback(ctx)
|
||||
gm.log.Error("更新发票记录出错", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
updateQuery, updateQueryArgs, _ := gm.ds.
|
||||
Update(goqu.T("report_tenement")).
|
||||
Set(goqu.Record{"invoice": nil}).
|
||||
Where(
|
||||
goqu.I("report_id").
|
||||
Eq(gm.ds.
|
||||
From(goqu.T("report")).
|
||||
Where(goqu.I("park_id").Eq(parks)),
|
||||
)).ToSQL()
|
||||
_, err := tx.Exec(ctx, updateQuery, updateQueryArgs...)
|
||||
if err != nil {
|
||||
tx.Rollback(ctx)
|
||||
gm.log.Error("更新发票记录出错", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
deleteQuery := gm.ds.
|
||||
From(goqu.T("invoices")).
|
||||
Where(goqu.I("park_id").Eq(parks)).
|
||||
Delete()
|
||||
if len(val) > 0 {
|
||||
deleteQuery.Where(goqu.I("invoice_code").In(val))
|
||||
}
|
||||
deleteQuerySql, deleteQueryArgs, _ := deleteQuery.ToSQL()
|
||||
|
||||
_, err := tx.Exec(ctx, deleteQuerySql, deleteQueryArgs...)
|
||||
if err != nil {
|
||||
tx.Rollback(ctx)
|
||||
gm.log.Error("删除指定园区发票记录出错", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func (gm _GMRepository) DeleteMeterPoolings(ctx context.Context, tx pgx.Tx, parks string, val ...[]string) error {
|
||||
deleteQuery := gm.ds.
|
||||
Delete(goqu.T("meter_relations")).
|
||||
Where(goqu.I("park_id").Eq(parks))
|
||||
|
||||
if len(val) > 0 {
|
||||
deleteQuery = deleteQuery.
|
||||
Where(
|
||||
goqu.I("master_meter_id").In(val),
|
||||
goqu.Or(goqu.I("slave_meter_id").In(val)),
|
||||
)
|
||||
}
|
||||
deleteQuerySql, deleteQueryArgs, _ := deleteQuery.ToSQL()
|
||||
_, err := tx.Exec(ctx, deleteQuerySql, deleteQueryArgs...)
|
||||
if err != nil {
|
||||
tx.Rollback(ctx)
|
||||
gm.log.Error("删除指定园区中的表计分摊关系失败", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gm _GMRepository) DeleteMeters(ctx context.Context, tx pgx.Tx, parks string, val ...[]string) error {
|
||||
deleteQuery := gm.ds.
|
||||
Delete(goqu.T("meter_04kv")).
|
||||
Where(goqu.I("park_id").Eq(parks))
|
||||
|
||||
if len(val) > 0 {
|
||||
deleteQuery = deleteQuery.Where(goqu.I("code").In(val))
|
||||
}
|
||||
|
||||
deleteQuerySql, deleteQueryArgs, _ := deleteQuery.ToSQL()
|
||||
|
||||
_, err := tx.Exec(ctx, deleteQuerySql, deleteQueryArgs...)
|
||||
if err != nil {
|
||||
tx.Rollback(ctx)
|
||||
gm.log.Error("删除指定园区的符合条件的标记出错", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gm _GMRepository) DeleteReports(ctx context.Context, tx pgx.Tx, parks string, val ...[]string) error {
|
||||
var err error
|
||||
|
||||
if len(val) > 0 {
|
||||
deleteReportTenementQuerySql, deleteReportTenementQueryArgs, _ := gm.ds.
|
||||
Delete(goqu.T("report_tenement")).
|
||||
Where(goqu.I("report_id").In(
|
||||
gm.ds.
|
||||
From(goqu.T("report")).
|
||||
Where(goqu.I("park_id").Eq(parks)).
|
||||
Where(goqu.I("id").In(val)),
|
||||
)).ToSQL()
|
||||
_, err = tx.Exec(ctx, deleteReportTenementQuerySql, deleteReportTenementQueryArgs...)
|
||||
if err != nil {
|
||||
tx.Rollback(ctx)
|
||||
return err
|
||||
}
|
||||
|
||||
deleteReportPooledConsumptionQuerySql, deleteReportPooledConsumptionQueryArgs, _ := gm.ds.
|
||||
Delete(goqu.T("report_pooled_consumption")).
|
||||
Where(goqu.I("report_id").In(
|
||||
gm.ds.
|
||||
From(goqu.T("report")).
|
||||
Where(goqu.I("park_id").Eq(parks)).
|
||||
Where(goqu.I("id").In(val)),
|
||||
)).ToSQL()
|
||||
_, err = tx.Exec(ctx, deleteReportPooledConsumptionQuerySql, deleteReportPooledConsumptionQueryArgs...)
|
||||
if err != nil {
|
||||
tx.Rollback(ctx)
|
||||
return err
|
||||
}
|
||||
|
||||
deleteReportPublicConsumptionQuerySql, deleteReportPublicConsumptionQueryArgs, _ := gm.ds.
|
||||
Delete(goqu.T("report_public_consumption")).
|
||||
Where(goqu.I("report_id").In(
|
||||
gm.ds.
|
||||
From(goqu.T("report")).
|
||||
Where(goqu.I("park_id").Eq(parks)).
|
||||
Where(goqu.I("id").In(val)),
|
||||
)).ToSQL()
|
||||
_, err = tx.Exec(ctx, deleteReportPublicConsumptionQuerySql, deleteReportPublicConsumptionQueryArgs...)
|
||||
if err != nil {
|
||||
tx.Rollback(ctx)
|
||||
return err
|
||||
}
|
||||
|
||||
deleteReportSummaryQuerySql, deleteReportSummaryQueryArgs, _ := gm.ds.
|
||||
Delete(goqu.T("report_summary")).
|
||||
Where(goqu.I("report_id").In(
|
||||
gm.ds.
|
||||
From(goqu.T("report")).
|
||||
Where(goqu.I("park_id").Eq(parks)).
|
||||
Where(goqu.I("id").In(val)),
|
||||
)).ToSQL()
|
||||
_, err = tx.Exec(ctx, deleteReportSummaryQuerySql, deleteReportSummaryQueryArgs...)
|
||||
if err != nil {
|
||||
tx.Rollback(ctx)
|
||||
return err
|
||||
}
|
||||
|
||||
deleteReportTaskQuerySql, deleteReportTaskQueryArgs, _ := gm.ds.
|
||||
Delete(goqu.T("report_task")).
|
||||
Where(goqu.I("report_id").In(
|
||||
gm.ds.
|
||||
From(goqu.T("report")).
|
||||
Where(goqu.I("park_id").Eq(parks)).
|
||||
Where(goqu.I("id").In(val)),
|
||||
)).ToSQL()
|
||||
_, err = tx.Exec(ctx, deleteReportTaskQuerySql, deleteReportTaskQueryArgs...)
|
||||
if err != nil {
|
||||
tx.Rollback(ctx)
|
||||
return err
|
||||
}
|
||||
|
||||
deleteReportQuerySql, deleteReportQueryArgs, _ := gm.ds.
|
||||
Delete(goqu.T("report")).
|
||||
Where(goqu.I("park_id").Eq(parks)).
|
||||
Where(goqu.I("id").In(val)).ToSQL()
|
||||
_, err = tx.Exec(ctx, deleteReportQuerySql, deleteReportQueryArgs...)
|
||||
if err != nil {
|
||||
tx.Rollback(ctx)
|
||||
return err
|
||||
}
|
||||
|
||||
} else {
|
||||
deleteReportTenementQuerySql, deleteReportTenementQueryArgs, _ := gm.ds.
|
||||
Delete(goqu.T("report_tenement")).
|
||||
Where(goqu.I("report_id").In(
|
||||
gm.ds.
|
||||
From(goqu.T("report")).
|
||||
Where(goqu.I("park_id").Eq(parks)),
|
||||
)).ToSQL()
|
||||
_, err = tx.Exec(ctx, deleteReportTenementQuerySql, deleteReportTenementQueryArgs...)
|
||||
if err != nil {
|
||||
tx.Rollback(ctx)
|
||||
return err
|
||||
}
|
||||
|
||||
deleteReportPooledConsumptionQuerySql, deleteReportPooledConsumptionQueryArgs, _ := gm.ds.
|
||||
Delete(goqu.T("report_pooled_consumption")).
|
||||
Where(goqu.I("report_id").In(
|
||||
gm.ds.
|
||||
From(goqu.T("report")).
|
||||
Where(goqu.I("park_id").Eq(parks)),
|
||||
)).ToSQL()
|
||||
_, err = tx.Exec(ctx, deleteReportPooledConsumptionQuerySql, deleteReportPooledConsumptionQueryArgs...)
|
||||
if err != nil {
|
||||
tx.Rollback(ctx)
|
||||
return err
|
||||
}
|
||||
|
||||
deleteReportPublicConsumptionQuerySql, deleteReportPublicConsumptionQueryArgs, _ := gm.ds.
|
||||
Delete(goqu.T("report_public_consumption")).
|
||||
Where(goqu.I("report_id").In(
|
||||
gm.ds.
|
||||
From(goqu.T("report")).
|
||||
Where(goqu.I("park_id").Eq(parks)),
|
||||
)).ToSQL()
|
||||
_, err = tx.Exec(ctx, deleteReportPublicConsumptionQuerySql, deleteReportPublicConsumptionQueryArgs...)
|
||||
if err != nil {
|
||||
tx.Rollback(ctx)
|
||||
return err
|
||||
}
|
||||
|
||||
deleteReportSummaryQuerySql, deleteReportSummaryQueryArgs, _ := gm.ds.
|
||||
Delete(goqu.T("report_summary")).
|
||||
Where(goqu.I("report_id").In(
|
||||
gm.ds.
|
||||
From(goqu.T("report")).
|
||||
Where(goqu.I("park_id").Eq(parks)),
|
||||
)).ToSQL()
|
||||
_, err = tx.Exec(ctx, deleteReportSummaryQuerySql, deleteReportSummaryQueryArgs...)
|
||||
if err != nil {
|
||||
tx.Rollback(ctx)
|
||||
return err
|
||||
}
|
||||
|
||||
deleteReportTaskQuerySql, deleteReportTaskQueryArgs, _ := gm.ds.
|
||||
Delete(goqu.T("report_task")).
|
||||
Where(goqu.I("report_id").In(
|
||||
gm.ds.
|
||||
From(goqu.T("report")).
|
||||
Where(goqu.I("park_id").Eq(parks)),
|
||||
)).ToSQL()
|
||||
_, err = tx.Exec(ctx, deleteReportTaskQuerySql, deleteReportTaskQueryArgs...)
|
||||
if err != nil {
|
||||
tx.Rollback(ctx)
|
||||
return err
|
||||
}
|
||||
|
||||
deleteReportQuerySql, deleteReportQueryArgs, _ := gm.ds.
|
||||
Delete(goqu.T("report")).
|
||||
Where(goqu.I("park_id").Eq(parks)).ToSQL()
|
||||
_, err = tx.Exec(ctx, deleteReportQuerySql, deleteReportQueryArgs...)
|
||||
if err != nil {
|
||||
tx.Rollback(ctx)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gm _GMRepository) DeleteBuildings(ctx context.Context, tx pgx.Tx, parks string, val ...[]string) error {
|
||||
if len(val) > 0 {
|
||||
updateBulidingSql, updateBlidingArgs, _ := gm.ds.
|
||||
Update(goqu.T("tenement")).
|
||||
Set(goqu.Record{"building": nil}).
|
||||
Where(goqu.I("park_id").Eq(parks)).
|
||||
Where(goqu.I("building").In(
|
||||
gm.ds.
|
||||
From(goqu.I("park_building")).
|
||||
Where(goqu.I("park_id").Eq(parks)).
|
||||
Where(goqu.I("id").In(val)).
|
||||
Select(goqu.I("id")),
|
||||
)).ToSQL()
|
||||
_, err := tx.Exec(ctx, updateBulidingSql, updateBlidingArgs...)
|
||||
if err != nil {
|
||||
tx.Rollback(ctx)
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
updateBulidingSql, updateBlidingArgs, _ := gm.ds.
|
||||
Update(goqu.T("tenement")).
|
||||
Set(goqu.Record{"building": nil}).
|
||||
Where(goqu.I("park_id").Eq(parks)).ToSQL()
|
||||
_, err := tx.Exec(ctx, updateBulidingSql, updateBlidingArgs...)
|
||||
if err != nil {
|
||||
tx.Rollback(ctx)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
deleteQuery := gm.ds.
|
||||
Delete(goqu.I("park_building")).
|
||||
Where(goqu.I("park_id").Eq(parks))
|
||||
|
||||
if len(val) > 0 {
|
||||
deleteQuery = deleteQuery.
|
||||
Where(goqu.I("id").In(val))
|
||||
}
|
||||
|
||||
deleteQuerySql, deleteQueryArgs, _ := deleteQuery.ToSQL()
|
||||
_, err := tx.Exec(ctx, deleteQuerySql, deleteQueryArgs...)
|
||||
if err != nil {
|
||||
tx.Rollback(ctx)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gm _GMRepository) DeleteParks(ctx context.Context, tx pgx.Tx, park []string) error {
|
||||
deleteParksSql, deleteParksArgs, _ := gm.ds.
|
||||
Delete(goqu.T("park")).
|
||||
Where(goqu.I("id").In(park)).ToSQL()
|
||||
|
||||
_, err := tx.Exec(ctx, deleteParksSql, deleteParksArgs...)
|
||||
if err != nil {
|
||||
tx.Rollback(ctx)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gm _GMRepository) ListAllParkIdsInUser(ctx context.Context, tx pgx.Tx, uid string) ([]string, error) {
|
||||
SearchParkIdsSql, SearchParkIdsArgs, _ := gm.ds.
|
||||
From(goqu.T("park")).
|
||||
Where(goqu.I("user_id").Eq(uid)).
|
||||
Select(goqu.I("id")).ToSQL()
|
||||
var pids []string
|
||||
err := pgxscan.Select(ctx, global.DB, &pids, SearchParkIdsSql, SearchParkIdsArgs...)
|
||||
if err != nil {
|
||||
gm.log.Error("查询["+uid+"]用户下的所有园区失败", zap.Error(err))
|
||||
tx.Rollback(ctx)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return pids, nil
|
||||
}
|
||||
|
||||
func (gm _GMRepository) DeleteUsers(ctx context.Context, tx pgx.Tx, uid string) error {
|
||||
var err error
|
||||
//删除用户关联
|
||||
DeleteUserChargeSql, DeleteUserChargeArgs, _ := gm.ds.
|
||||
Delete(goqu.T("user_charge")).
|
||||
Where(goqu.I("id").Eq(uid)).ToSQL()
|
||||
|
||||
_, err = tx.Exec(ctx,DeleteUserChargeSql,DeleteUserChargeArgs...)
|
||||
if err != nil {
|
||||
gm.log.Error("user_charge表关联出错",zap.Error(err))
|
||||
tx.Rollback(ctx)
|
||||
return err
|
||||
}
|
||||
|
||||
//删除用户详细信息
|
||||
DeleteUserDetailSql, DeleteUserDetailArgs,_ := gm.ds.
|
||||
Delete(goqu.T("user_detail")).
|
||||
Where(goqu.I("id").Eq(uid)).ToSQL()
|
||||
_, err = tx.Exec(ctx,DeleteUserDetailSql,DeleteUserDetailArgs...)
|
||||
if err != nil {
|
||||
gm.log.Error("user_detail表详细信息出错",zap.Error(err))
|
||||
tx.Rollback(ctx)
|
||||
return err
|
||||
}
|
||||
|
||||
//删除用户基础信息
|
||||
DeleteUserSql, DeleteUserArgs,_ := gm.ds.
|
||||
Delete(goqu.T("users")).
|
||||
Where(goqu.I("id").Eq(uid)).ToSQL()
|
||||
_, err = tx.Exec(ctx,DeleteUserSql,DeleteUserArgs...)
|
||||
if err != nil {
|
||||
gm.log.Error("user表基础信息出错",zap.Error(err))
|
||||
tx.Rollback(ctx)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
348
repository/invoice.go
Normal file
348
repository/invoice.go
Normal file
@@ -0,0 +1,348 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
"electricity_bill_calc/config"
|
||||
"electricity_bill_calc/global"
|
||||
"electricity_bill_calc/logger"
|
||||
"electricity_bill_calc/model"
|
||||
"electricity_bill_calc/types"
|
||||
"errors"
|
||||
"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"
|
||||
)
|
||||
|
||||
type _InvoiceRepository struct {
|
||||
log *zap.Logger
|
||||
ds goqu.DialectWrapper
|
||||
}
|
||||
|
||||
var InvoiceRepository = _InvoiceRepository{
|
||||
log: logger.Named("Repository", "Invoice"),
|
||||
ds: goqu.Dialect("postgres"),
|
||||
}
|
||||
|
||||
// 查询指定园区中符合条件的发票
|
||||
func (ir _InvoiceRepository) ListInvoice(pid *string, startDate, endDate *types.Date, keyword *string, page uint) ([]*model.Invoice, int64, error) {
|
||||
ir.log.Info("查询指定园区的发票。", zap.Stringp("Park", pid), logger.DateFieldp("StartDate", startDate), logger.DateFieldp("EndDate", endDate), zap.Stringp("Keyword", keyword), zap.Uint("Page", page))
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
invoiceQuery := ir.ds.
|
||||
From(goqu.T("invoice").As("i")).
|
||||
Join(goqu.T("tenement").As("t"), goqu.On(goqu.I("i.tenement_id").Eq(goqu.I("t.id")))).
|
||||
Select("i.*")
|
||||
countQuery := ir.ds.
|
||||
From(goqu.T("invoice").As("i")).
|
||||
Join(goqu.T("tenement").As("t"), goqu.On(goqu.I("i.tenement_id").Eq(goqu.I("t.id")))).
|
||||
Select(goqu.COUNT("*"))
|
||||
|
||||
if pid != nil && len(*pid) > 0 {
|
||||
invoiceQuery = invoiceQuery.Where(goqu.I("t.park_id").Eq(*pid))
|
||||
countQuery = countQuery.Where(goqu.I("t.park_id").Eq(*pid))
|
||||
}
|
||||
|
||||
if keyword != nil && len(*keyword) > 0 {
|
||||
pattern := fmt.Sprintf("%%%s%%", *keyword)
|
||||
invoiceQuery = invoiceQuery.Where(goqu.Or(
|
||||
goqu.I("i.invoice_no").ILike(pattern),
|
||||
goqu.I("t.full_name").ILike(pattern),
|
||||
goqu.I("t.short_name").ILike(pattern),
|
||||
goqu.I("t.abbr").ILike(pattern),
|
||||
goqu.I("t.contact_name").ILike(pattern),
|
||||
goqu.I("t.contact_phone").ILike(pattern),
|
||||
goqu.L("t.invoice_info->>'usci'").ILike(pattern),
|
||||
))
|
||||
countQuery = countQuery.Where(goqu.Or(
|
||||
goqu.I("i.invoice_no").ILike(pattern),
|
||||
goqu.I("t.full_name").ILike(pattern),
|
||||
goqu.I("t.short_name").ILike(pattern),
|
||||
goqu.I("t.abbr").ILike(pattern),
|
||||
goqu.I("t.contact_name").ILike(pattern),
|
||||
goqu.I("t.contact_phone").ILike(pattern),
|
||||
goqu.L("t.invoice_info->>'usci'").ILike(pattern),
|
||||
))
|
||||
}
|
||||
|
||||
var queryRange = types.NewEmptyDateTimeRange()
|
||||
if startDate != nil {
|
||||
queryRange.SetLower(startDate.ToBeginningOfDate())
|
||||
}
|
||||
if endDate != nil {
|
||||
queryRange.SetUpper(endDate.ToEndingOfDate())
|
||||
}
|
||||
if !queryRange.IsEmptyOrWild() {
|
||||
invoiceQuery = invoiceQuery.Where(goqu.L("i.issued_at <@ ?", queryRange))
|
||||
countQuery = countQuery.Where(goqu.L("i.issued_at <@ ?", queryRange))
|
||||
}
|
||||
|
||||
startRow := (page - 1) * config.ServiceSettings.ItemsPageSize
|
||||
invoiceQuery = invoiceQuery.
|
||||
Order(goqu.I("i.issued_at").Desc()).
|
||||
Offset(startRow).
|
||||
Limit(config.ServiceSettings.ItemsPageSize)
|
||||
|
||||
var (
|
||||
invoices []*model.Invoice = make([]*model.Invoice, 0)
|
||||
total int64
|
||||
)
|
||||
querySql, queryArgs, _ := invoiceQuery.Prepared(true).ToSQL()
|
||||
if err := pgxscan.Select(ctx, global.DB, &invoices, querySql, queryArgs...); err != nil {
|
||||
ir.log.Error("查询发票记录失败。", zap.Error(err))
|
||||
return invoices, 0, err
|
||||
}
|
||||
countSql, countArgs, _ := countQuery.Prepared(true).ToSQL()
|
||||
if err := pgxscan.Get(ctx, global.DB, &total, countSql, countArgs...); err != nil {
|
||||
ir.log.Error("查询发票记录数失败。", zap.Error(err))
|
||||
return invoices, 0, err
|
||||
}
|
||||
return invoices, total, nil
|
||||
}
|
||||
|
||||
// 查询指定商户未开票的核算记录,改记录将只包括商户整体核算,不包括商户各个表计的详细
|
||||
func (ir _InvoiceRepository) ListUninvoicedTenementCharges(tid string) ([]*model.SimplifiedTenementCharge, error) {
|
||||
ir.log.Info("查询指定商户的未开票核算记录", zap.String("Tenement", tid))
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
chargeSql, chargeArgs, _ := ir.ds.
|
||||
From(goqu.T("report_tenement").As("t")).
|
||||
Join(goqu.T("report").As("r"), goqu.On(goqu.I("t.report_id").Eq(goqu.I("r.id")))).
|
||||
Select(
|
||||
goqu.I("t.report_id"),
|
||||
goqu.I("r.period"),
|
||||
goqu.L("(t.overall->>'amount')::numeric").As("amount"),
|
||||
goqu.I("t.final_charge"),
|
||||
).
|
||||
Where(
|
||||
goqu.I("t.tenement_id").Eq(tid),
|
||||
goqu.I("t.invoice").IsNull(),
|
||||
).
|
||||
Prepared(true).ToSQL()
|
||||
var charges []*model.SimplifiedTenementCharge
|
||||
if err := pgxscan.Select(ctx, global.DB, &charges, chargeSql, chargeArgs...); err != nil {
|
||||
ir.log.Error("查询未开票核算记录失败。", zap.Error(err))
|
||||
return charges, err
|
||||
}
|
||||
return charges, nil
|
||||
}
|
||||
|
||||
// 更新指定核算中指定商户的开票状态以及对应发票号。
|
||||
// 如果给定了发票号,那么指定记录状态为已开票,如果给定的发票号为`nil`,啊么指定记录为未开票。
|
||||
func (ir _InvoiceRepository) UpdateTenementInvoicedState(tx pgx.Tx, ctx context.Context, rid, tid string, invoiceNo *string) error {
|
||||
ir.log.Info("更新指定核算中指定商户的开票状态和记录", zap.String("Report", rid), zap.String("Tenement", tid), zap.Stringp("InvoiceNo", invoiceNo))
|
||||
updateSql, updateArgs, _ := ir.ds.
|
||||
Update(goqu.T("report_tenement")).
|
||||
Set(goqu.Record{
|
||||
"invoice": invoiceNo,
|
||||
}).
|
||||
Where(
|
||||
goqu.I("report_id").Eq(rid),
|
||||
goqu.I("tenement_id").Eq(tid),
|
||||
).
|
||||
Prepared(true).ToSQL()
|
||||
if _, err := tx.Exec(ctx, updateSql, updateArgs...); err != nil {
|
||||
ir.log.Error("更新核算记录的开票状态失败。", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 查询指定发票的详细记录信息
|
||||
func (ir _InvoiceRepository) GetInvoiceDetail(invoiceNo string) (*model.Invoice, error) {
|
||||
ir.log.Info("查询指定发票的详细信息", zap.String("InvoiceNo", invoiceNo))
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
invoiceSql, invoiceArgs, _ := ir.ds.
|
||||
From(goqu.T("invoice")).
|
||||
Select("*").
|
||||
Where(goqu.I("invoice_no").Eq(invoiceNo)).
|
||||
Prepared(true).ToSQL()
|
||||
var invoice model.Invoice
|
||||
if err := pgxscan.Get(ctx, global.DB, &invoice, invoiceSql, invoiceArgs...); err != nil {
|
||||
ir.log.Error("查询发票记录失败。", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
return &invoice, nil
|
||||
}
|
||||
|
||||
// 获取指定商户的简化核算记录
|
||||
func (ir _InvoiceRepository) GetSimplifiedTenementCharges(tid string, rids []string) ([]*model.SimplifiedTenementCharge, error) {
|
||||
ir.log.Info("查询庄园商户的简化核算记录", zap.String("Tenement", tid), zap.Strings("Reports", rids))
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
chargeSql, chargeArgs, _ := ir.ds.
|
||||
From(goqu.T("report_tenement").As("t")).
|
||||
Join(goqu.T("report").As("r"), goqu.On(goqu.I("t.report_id").Eq(goqu.I("r.id")))).
|
||||
Select(
|
||||
goqu.I("t.report_id"),
|
||||
goqu.I("r.period"),
|
||||
goqu.L("(t.overall->>'amount')::numeric").As("amount"),
|
||||
goqu.I("t.final_charge"),
|
||||
).
|
||||
Where(
|
||||
goqu.I("t.tenement_id").Eq(tid),
|
||||
goqu.I("t.report_id").In(rids),
|
||||
).
|
||||
Prepared(true).ToSQL()
|
||||
var charges []*model.SimplifiedTenementCharge
|
||||
if err := pgxscan.Select(ctx, global.DB, &charges, chargeSql, chargeArgs...); err != nil {
|
||||
ir.log.Error("查询简化核算记录失败。", zap.Error(err))
|
||||
return charges, err
|
||||
}
|
||||
return charges, nil
|
||||
}
|
||||
|
||||
// 查询发票号码对应的商户 ID
|
||||
// ! 这个方法不能被加入缓存,这个方法存在的目的就是为了清除缓存。
|
||||
func (ir _InvoiceRepository) GetInvoiceBelongs(invoiceNo string) ([]string, error) {
|
||||
ir.log.Info("查询发票号码对应的商户 ID", zap.String("InvoiceNo", invoiceNo))
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
tenementSql, tenementArgs, _ := ir.ds.
|
||||
From(goqu.T("invoice")).
|
||||
Select("tenement_id").
|
||||
Where(goqu.I("i.invoice_no").Eq(invoiceNo)).
|
||||
Prepared(true).ToSQL()
|
||||
var tenementIds []string
|
||||
if err := pgxscan.Select(ctx, global.DB, &tenementIds, tenementSql, tenementArgs...); err != nil {
|
||||
ir.log.Error("查询发票号码对应的商户 ID 失败。", zap.Error(err))
|
||||
return tenementIds, err
|
||||
}
|
||||
return tenementIds, nil
|
||||
}
|
||||
|
||||
// 删除指定的发票记录
|
||||
func (ir _InvoiceRepository) Delete(tx pgx.Tx, ctx context.Context, invoiceNo string) error {
|
||||
ir.log.Info("删除指定的发票记录", zap.String("InvoiceNo", invoiceNo))
|
||||
deleteSql, deleteArgs, _ := ir.ds.
|
||||
Delete(goqu.T("invoice")).
|
||||
Where(goqu.I("invoice_no").Eq(invoiceNo)).
|
||||
Prepared(true).ToSQL()
|
||||
if _, err := tx.Exec(ctx, deleteSql, deleteArgs...); err != nil {
|
||||
ir.log.Error("删除发票记录失败。", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 删除指定发票记录与指定核算记录之间的关联
|
||||
func (ir _InvoiceRepository) DeleteInvoiceTenementRelation(tx pgx.Tx, ctx context.Context, invoiceNo string) error {
|
||||
ir.log.Info("删除指定发票记录与指定核算记录之间的关联", zap.String("InvoiceNo", invoiceNo))
|
||||
updateSql, updateArgs, _ := ir.ds.
|
||||
Update(goqu.T("report_tenement")).
|
||||
Set(goqu.Record{
|
||||
"invoice": nil,
|
||||
}).
|
||||
Where(goqu.I("invoice").Eq(invoiceNo)).
|
||||
Prepared(true).ToSQL()
|
||||
if _, err := tx.Exec(ctx, updateSql, updateArgs...); err != nil {
|
||||
ir.log.Error("删除发票记录与核算记录之间的关联失败。", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 确认发票的归属
|
||||
func (ir _InvoiceRepository) IsBelongsTo(invoiceNo, uid string) (bool, error) {
|
||||
ir.log.Info("确认发票的归属", zap.String("InvoiceNo", invoiceNo), zap.String("User", uid))
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
querySql, queryArgs, _ := ir.ds.
|
||||
From(goqu.T("invoice").As("i")).
|
||||
Join(goqu.T("park").As("p"), goqu.On(goqu.I("p.id").Eq(goqu.I("i.park_id")))).
|
||||
Select(goqu.COUNT("i.*")).
|
||||
Where(
|
||||
goqu.I("i.invoice_no").Eq(invoiceNo),
|
||||
goqu.I("p.user_id").Eq(uid),
|
||||
).
|
||||
Prepared(true).ToSQL()
|
||||
var count int64
|
||||
if err := pgxscan.Get(ctx, global.DB, &count, querySql, queryArgs...); err != nil {
|
||||
ir.log.Error("查询发票归属失败", zap.Error(err))
|
||||
return false, err
|
||||
}
|
||||
return count > 0, nil
|
||||
}
|
||||
|
||||
// 创建一条新的发票记录
|
||||
func (ir _InvoiceRepository) Create(pid, tid, invoiceNo string, invoiceType *string, amount decimal.Decimal, issuedAt types.DateTime, taxMethod int16, taxRate decimal.Decimal, cargos *[]*model.InvoiceCargo, covers *[]string) error {
|
||||
ir.log.Info("记录一个新的发票", zap.String("Park", pid), zap.String("Tenement", tid), zap.String("Invoice", invoiceNo))
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
tx, err := global.DB.Begin(ctx)
|
||||
if err != nil {
|
||||
ir.log.Error("开启事务失败。", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
|
||||
tenemenetSql, tenementArgs, _ := ir.ds.
|
||||
From(goqu.T("tenement").As("t")).
|
||||
LeftJoin(goqu.T("park_building").As("b"), goqu.On(goqu.I("t.building").Eq(goqu.I("b.id")))).
|
||||
Select(
|
||||
"t.*", goqu.I("b.name").As("building_name"),
|
||||
).
|
||||
Where(goqu.I("t.id").Eq(tid)).
|
||||
Prepared(true).ToSQL()
|
||||
var tenement model.Tenement
|
||||
if err := pgxscan.Get(ctx, global.DB, &tenement, tenemenetSql, tenementArgs...); err != nil {
|
||||
ir.log.Error("查询商户信息失败。", zap.Error(err))
|
||||
tx.Rollback(ctx)
|
||||
return err
|
||||
}
|
||||
|
||||
if tenement.InvoiceInfo == nil {
|
||||
ir.log.Error("尚未设定商户的发票抬头信息")
|
||||
tx.Rollback(ctx)
|
||||
return errors.New("尚未设定商户的发票抬头信息")
|
||||
}
|
||||
|
||||
createSql, createArgs, _ := ir.ds.
|
||||
Insert(goqu.T("invoice")).
|
||||
Cols(
|
||||
"invoice_no", "park_id", "tenement_id", "invoice_type", "amount", "issued_at", "tax_method", "tax_rate", "cargos", "covers",
|
||||
).
|
||||
Vals(goqu.Vals{
|
||||
invoiceNo, pid, tid, invoiceType, amount, issuedAt, taxMethod, taxRate, cargos, covers,
|
||||
}).
|
||||
Prepared(true).ToSQL()
|
||||
if _, err := tx.Exec(ctx, createSql, createArgs...); err != nil {
|
||||
ir.log.Error("创建发票记录失败。", zap.Error(err))
|
||||
tx.Rollback(ctx)
|
||||
return err
|
||||
}
|
||||
|
||||
updateSql, updateArgs, _ := ir.ds.
|
||||
Update(goqu.T("report_tenement")).
|
||||
Set(goqu.Record{
|
||||
"invoice": invoiceNo,
|
||||
}).
|
||||
Where(
|
||||
goqu.I("tenement_id").Eq(tid),
|
||||
goqu.I("report_id").In(*covers),
|
||||
).
|
||||
Prepared(true).ToSQL()
|
||||
if _, err := tx.Exec(ctx, updateSql, updateArgs...); err != nil {
|
||||
ir.log.Error("更新核算记录的开票状态失败。", zap.Error(err))
|
||||
tx.Rollback(ctx)
|
||||
return err
|
||||
}
|
||||
|
||||
err = tx.Commit(ctx)
|
||||
if err != nil {
|
||||
ir.log.Error("提交事务失败。", zap.Error(err))
|
||||
tx.Rollback(ctx)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
991
repository/meter.go
Normal file
991
repository/meter.go
Normal file
@@ -0,0 +1,991 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
"electricity_bill_calc/cache"
|
||||
"electricity_bill_calc/config"
|
||||
"electricity_bill_calc/global"
|
||||
"electricity_bill_calc/logger"
|
||||
"electricity_bill_calc/model"
|
||||
"electricity_bill_calc/tools"
|
||||
"electricity_bill_calc/tools/serial"
|
||||
"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"
|
||||
)
|
||||
|
||||
type _MeterRepository struct {
|
||||
log *zap.Logger
|
||||
ds goqu.DialectWrapper
|
||||
}
|
||||
|
||||
var MeterRepository = _MeterRepository{
|
||||
log: logger.Named("Repository", "Meter"),
|
||||
ds: goqu.Dialect("postgres"),
|
||||
}
|
||||
|
||||
// 获取指定园区中所有的表计信息
|
||||
func (mr _MeterRepository) AllMeters(pid string) ([]*model.MeterDetail, error) {
|
||||
mr.log.Info("列出指定园区中的所有表计", zap.String("park id", pid))
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
var meters []*model.MeterDetail
|
||||
metersSql, metersArgs, _ := 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.detachedAt").IsNull(),
|
||||
).
|
||||
Order(goqu.I("m.seq").Asc()).
|
||||
Prepared(true).ToSQL()
|
||||
|
||||
if err := pgxscan.Select(ctx, global.DB, &meters, metersSql, metersArgs...); err != nil {
|
||||
mr.log.Error("查询表计信息失败", zap.Error(err))
|
||||
return make([]*model.MeterDetail, 0), err
|
||||
}
|
||||
|
||||
return meters, nil
|
||||
}
|
||||
|
||||
// 列出指定园区下的所有表计信息,包含已经拆除的表计
|
||||
func (mr _MeterRepository) AllUsedMeters(pid string) ([]*model.MeterDetail, error) {
|
||||
mr.log.Info("列出指定园区中的所有使用过的表计", zap.String("park id", pid))
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
var meters []*model.MeterDetail
|
||||
metersSql, metersArgs, _ := 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),
|
||||
).
|
||||
Order(goqu.I("m.seq").Asc()).
|
||||
Prepared(true).ToSQL()
|
||||
|
||||
if err := pgxscan.Select(ctx, global.DB, &meters, metersSql, metersArgs...); err != nil {
|
||||
mr.log.Error("查询表计信息失败", zap.Error(err))
|
||||
return make([]*model.MeterDetail, 0), err
|
||||
}
|
||||
|
||||
return meters, nil
|
||||
}
|
||||
|
||||
// 列出指定核算报表中所使用的所有表计,包含已经拆除的表计
|
||||
func (mr _MeterRepository) AllUsedMetersInReport(rid string) ([]*model.MeterDetail, error) {
|
||||
mr.log.Info("列出指定核算报表中所使用的所有表计", zap.String("report id", rid))
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
var meters []*model.MeterDetail
|
||||
metersSql, metersArgs, _ := 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")))).
|
||||
Join(goqu.T("report").As("r"), goqu.On(goqu.I("m.park_id").Eq(goqu.I("r.park_id")))).
|
||||
Where(
|
||||
goqu.I("r.id").Eq(rid),
|
||||
goqu.I("m.enabled").Eq(true),
|
||||
goqu.L("m.attached_at::date < upper(r.period)"),
|
||||
goqu.Or(
|
||||
goqu.I("m.detached_at").IsNull(),
|
||||
goqu.L("m.detached_at::date >= lower(r.period)"),
|
||||
),
|
||||
).
|
||||
Select(
|
||||
"m.*", goqu.I("b.name").As("building_name"),
|
||||
).
|
||||
Order(goqu.I("m.seq").Asc()).
|
||||
Prepared(true).ToSQL()
|
||||
|
||||
if err := pgxscan.Select(ctx, global.DB, &meters, metersSql, metersArgs...); err != nil {
|
||||
mr.log.Error("查询表计信息失败", zap.Error(err))
|
||||
return make([]*model.MeterDetail, 0), err
|
||||
}
|
||||
|
||||
return meters, nil
|
||||
}
|
||||
|
||||
// 分页列出指定园区下的表计信息
|
||||
func (mr _MeterRepository) MetersIn(pid string, page uint, keyword *string) ([]*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")))).
|
||||
Select(
|
||||
"m.*", goqu.I("b.name").As("building_name"),
|
||||
).
|
||||
Where(
|
||||
goqu.I("m.park_id").Eq(pid),
|
||||
goqu.I("m.detached_at").IsNull(),
|
||||
)
|
||||
countQuery := mr.ds.
|
||||
From(goqu.T("meter_04kv").As("m")).
|
||||
Select(goqu.COUNT("*")).
|
||||
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),
|
||||
),
|
||||
)
|
||||
countQuery = countQuery.Where(
|
||||
goqu.Or(
|
||||
goqu.I("m.code").ILike(pattern),
|
||||
goqu.I("m.address").ILike(pattern),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
startRow := (page - 1) * config.ServiceSettings.ItemsPageSize
|
||||
meterQuery = meterQuery.Order(goqu.I("m.seq").Asc()).Offset(startRow).Limit(config.ServiceSettings.ItemsPageSize)
|
||||
|
||||
meterSql, meterArgs, _ := meterQuery.Prepared(true).ToSQL()
|
||||
countSql, countArgs, _ := countQuery.Prepared(true).ToSQL()
|
||||
|
||||
var (
|
||||
meters []*model.MeterDetail
|
||||
total int64
|
||||
)
|
||||
if err := pgxscan.Select(ctx, global.DB, &meters, meterSql, meterArgs...); err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
// 列出指定园区中指定列表中所有表计的详细信息,将忽略所有表计的当前状态
|
||||
func (mr _MeterRepository) ListMetersByIDs(pid string, ids []string) ([]*model.MeterDetail, error) {
|
||||
mr.log.Info("列出指定园区中指定列表中所有表计的详细信息", zap.String("park id", pid), zap.Strings("meter ids", ids))
|
||||
if len(ids) == 0 {
|
||||
return make([]*model.MeterDetail, 0), nil
|
||||
}
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
var meters []*model.MeterDetail
|
||||
metersSql, metersArgs, _ := 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.code").In(ids),
|
||||
).
|
||||
Order(goqu.I("m.seq").Asc()).
|
||||
Prepared(true).ToSQL()
|
||||
|
||||
if err := pgxscan.Select(ctx, global.DB, &meters, metersSql, metersArgs...); err != nil {
|
||||
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) {
|
||||
mr.log.Info("获取指定表计的详细信息", zap.String("park id", pid), zap.String("meter code", code))
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
var meter model.MeterDetail
|
||||
meterSql, meterArgs, _ := 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.code").Eq(code),
|
||||
).
|
||||
Prepared(true).ToSQL()
|
||||
|
||||
if err := pgxscan.Get(ctx, global.DB, &meter, meterSql, meterArgs...); err != nil {
|
||||
mr.log.Error("查询表计信息失败", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &meter, nil
|
||||
}
|
||||
|
||||
// 创建一条新的表计信息
|
||||
func (mr _MeterRepository) CreateMeter(tx pgx.Tx, ctx context.Context, pid string, meter vo.MeterCreationForm) (bool, error) {
|
||||
mr.log.Info("创建一条新的表计信息", zap.String("park id", pid), zap.String("meter code", meter.Code))
|
||||
timeNow := types.Now()
|
||||
meterSql, meterArgs, _ := mr.ds.
|
||||
Insert(goqu.T("meter_04kv")).
|
||||
Cols(
|
||||
"park_id", "code", "address", "ratio", "seq", "meter_type", "building", "on_floor", "area", "enabled",
|
||||
"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,
|
||||
},
|
||||
).
|
||||
Prepared(true).ToSQL()
|
||||
|
||||
ok, err := tx.Exec(ctx, meterSql, meterArgs...)
|
||||
if err != nil {
|
||||
mr.log.Error("创建表计信息失败", zap.Error(err))
|
||||
return false, err
|
||||
}
|
||||
return ok.RowsAffected() > 0, nil
|
||||
}
|
||||
|
||||
// 创建或者更新一条表计的信息
|
||||
func (mr _MeterRepository) CreateOrUpdateMeter(tx pgx.Tx, ctx context.Context, pid string, meter vo.MeterCreationForm) (bool, error) {
|
||||
mr.log.Info("创建或者更新一条表计的信息", zap.String("park id", pid), zap.String("meter code", meter.Code))
|
||||
timeNow := types.Now()
|
||||
meterSql, meterArgs, _ := mr.ds.
|
||||
Insert(goqu.T("meter_04kv")).
|
||||
Cols(
|
||||
"park_id", "code", "address", "ratio", "seq", "meter_type", "building", "on_floor", "area", "enabled",
|
||||
"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,
|
||||
},
|
||||
).
|
||||
OnConflict(
|
||||
goqu.DoUpdate("code, park_id",
|
||||
goqu.Record{
|
||||
"address": goqu.I("excluded.address"),
|
||||
"seq": goqu.I("excluded.seq"),
|
||||
"ratio": goqu.I("excluded.ratio"),
|
||||
"meter_type": goqu.I("excluded.meter_type"),
|
||||
"building": goqu.I("excluded.building"),
|
||||
"on_floor": goqu.I("excluded.on_floor"),
|
||||
"area": goqu.I("excluded.area"),
|
||||
"last_modified_at": goqu.I("excluded.last_modified_at"),
|
||||
}),
|
||||
).
|
||||
Prepared(true).ToSQL()
|
||||
res, err := tx.Exec(ctx, meterSql, meterArgs...)
|
||||
if err != nil {
|
||||
mr.log.Error("创建或者更新表计信息失败", zap.Error(err))
|
||||
return false, err
|
||||
}
|
||||
return res.RowsAffected() > 0, nil
|
||||
}
|
||||
|
||||
// 记录一条表计的抄表信息
|
||||
func (mr _MeterRepository) RecordReading(tx pgx.Tx, ctx context.Context, pid, code string, meterType int16, ratio decimal.Decimal, reading *vo.MeterReadingForm) (bool, error) {
|
||||
mr.log.Info("记录一条表计的抄表信息", zap.String("park id", pid), zap.String("meter code", code))
|
||||
readAt := tools.DefaultTo(reading.ReadAt, types.Now())
|
||||
readingSql, readingArgs, _ := mr.ds.
|
||||
Insert(goqu.T("meter_reading")).
|
||||
Cols(
|
||||
"park_id", "meter_id", "read_at", "meter_type", "ratio", "overall", "critical", "peak", "flat", "valley",
|
||||
).
|
||||
Vals(
|
||||
goqu.Vals{pid, code, readAt, meterType, ratio, reading.Overall, reading.Critical, reading.Peak, reading.Flat, reading.Valley},
|
||||
).
|
||||
Prepared(true).ToSQL()
|
||||
|
||||
ok, err := tx.Exec(ctx, readingSql, readingArgs...)
|
||||
if err != nil {
|
||||
mr.log.Error("记录表计抄表信息失败", zap.Error(err))
|
||||
return false, err
|
||||
}
|
||||
|
||||
return ok.RowsAffected() > 0, nil
|
||||
}
|
||||
|
||||
// 更新一条表计的详细信息
|
||||
func (mr _MeterRepository) UpdateMeter(tx pgx.Tx, ctx context.Context, pid, code string, detail *vo.MeterModificationForm) (bool, error) {
|
||||
mr.log.Info("更新一条表计的详细信息", zap.String("park id", pid), zap.String("meter code", code))
|
||||
timeNow := types.Now()
|
||||
meterSql, meterArgs, _ := mr.ds.
|
||||
Update(goqu.T("meter_04kv")).
|
||||
Set(
|
||||
goqu.Record{
|
||||
"address": detail.Address,
|
||||
"seq": detail.Seq,
|
||||
"ratio": detail.Ratio,
|
||||
"enabled": detail.Enabled,
|
||||
"meter_type": detail.MeterType,
|
||||
"building": detail.Building,
|
||||
"on_floor": detail.OnFloor,
|
||||
"area": detail.Area,
|
||||
"last_modified_at": timeNow,
|
||||
},
|
||||
).
|
||||
Where(
|
||||
goqu.I("park_id").Eq(pid),
|
||||
goqu.I("code").Eq(code),
|
||||
).
|
||||
Prepared(true).ToSQL()
|
||||
|
||||
ok, err := tx.Exec(ctx, meterSql, meterArgs...)
|
||||
if err != nil {
|
||||
mr.log.Error("更新表计信息失败", zap.Error(err))
|
||||
return false, err
|
||||
}
|
||||
|
||||
return ok.RowsAffected() > 0, nil
|
||||
}
|
||||
|
||||
// 列出指定园区中已经存在的表计编号,无论该表计是否已经不再使用。
|
||||
func (mr _MeterRepository) ListMeterCodes(pid string) ([]string, error) {
|
||||
mr.log.Info("列出指定园区中已经存在的表计编号", zap.String("park id", pid))
|
||||
cacheConditions := []string{pid}
|
||||
if codes, err := cache.RetrieveSearch[[]string]("meter_codes", cacheConditions...); err == nil {
|
||||
mr.log.Info("从缓存中获取到了指定园区中的表计编号", zap.Int("count", len(*codes)))
|
||||
return *codes, nil
|
||||
}
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
var codes []string
|
||||
codesSql, codesArgs, _ := mr.ds.
|
||||
From(goqu.T("meter_04kv")).
|
||||
Select("code").
|
||||
Where(
|
||||
goqu.I("park_id").Eq(pid),
|
||||
).
|
||||
Order(goqu.I("seq").Asc()).
|
||||
Prepared(true).ToSQL()
|
||||
|
||||
if err := pgxscan.Select(ctx, global.DB, &codes, codesSql, codesArgs...); err != nil {
|
||||
mr.log.Error("查询表计编号失败", zap.Error(err))
|
||||
return make([]string, 0), err
|
||||
}
|
||||
|
||||
return codes, nil
|
||||
}
|
||||
|
||||
// 解除指定园区中指定表计的使用
|
||||
func (mr _MeterRepository) DetachMeter(tx pgx.Tx, ctx context.Context, pid, code string) (bool, error) {
|
||||
mr.log.Info("解除指定园区中指定表计的使用", zap.String("park id", pid), zap.String("meter code", code))
|
||||
timeNow := types.Now()
|
||||
meterSql, meterArgs, _ := mr.ds.
|
||||
Update(goqu.T("meter_04kv")).
|
||||
Set(
|
||||
goqu.Record{
|
||||
"detached_at": timeNow,
|
||||
"last_modified_at": timeNow,
|
||||
},
|
||||
).
|
||||
Where(
|
||||
goqu.I("park_id").Eq(pid),
|
||||
goqu.I("code").Eq(code),
|
||||
).
|
||||
Prepared(true).ToSQL()
|
||||
|
||||
ok, err := tx.Exec(ctx, meterSql, meterArgs...)
|
||||
if err != nil {
|
||||
mr.log.Error("解除表计使用失败", zap.Error(err))
|
||||
return false, err
|
||||
}
|
||||
|
||||
return ok.RowsAffected() > 0, nil
|
||||
}
|
||||
|
||||
// 将商户表计绑定到公摊表计上
|
||||
func (mr _MeterRepository) BindMeter(tx pgx.Tx, ctx context.Context, pid, masterMeter, slaveMeter string) (bool, error) {
|
||||
mr.log.Info("将商户表计绑定到公摊表计上", zap.String("master meter code", masterMeter), zap.String("slave meter code", slaveMeter))
|
||||
masterDetail, err := mr.FetchMeterDetail(pid, masterMeter)
|
||||
if err != nil {
|
||||
mr.log.Error("查询公摊表计信息失败", zap.Error(err))
|
||||
return false, err
|
||||
}
|
||||
if masterDetail.MeterType != model.METER_INSTALLATION_POOLING {
|
||||
mr.log.Error("给定的公摊表计不是公摊表计", zap.Error(err))
|
||||
return false, fmt.Errorf("给定的公摊表计不是公摊表计")
|
||||
}
|
||||
slaveDetail, err := mr.FetchMeterDetail(pid, slaveMeter)
|
||||
if err != nil {
|
||||
mr.log.Error("查询商户表计信息失败", zap.Error(err))
|
||||
return false, err
|
||||
}
|
||||
if slaveDetail.MeterType != model.METER_INSTALLATION_TENEMENT {
|
||||
mr.log.Error("给定的商户表计不是商户表计", zap.Error(err))
|
||||
return false, fmt.Errorf("给定的商户表计不是商户表计")
|
||||
}
|
||||
|
||||
timeNow := types.Now()
|
||||
serial.StringSerialRequestChan <- 1
|
||||
code := serial.Prefix("PB", <-serial.StringSerialResponseChan)
|
||||
relationSql, relationArgs, _ := mr.ds.
|
||||
Insert(goqu.T("meter_relations")).
|
||||
Cols(
|
||||
"id", "park_id", "master_meter_id", "slave_meter_id", "established_at",
|
||||
).
|
||||
Vals(
|
||||
goqu.Vals{
|
||||
code,
|
||||
pid,
|
||||
masterMeter,
|
||||
slaveMeter,
|
||||
timeNow,
|
||||
},
|
||||
).
|
||||
Prepared(true).ToSQL()
|
||||
|
||||
ok, err := tx.Exec(ctx, relationSql, relationArgs...)
|
||||
if err != nil {
|
||||
mr.log.Error("绑定表计关系失败", zap.Error(err))
|
||||
return false, err
|
||||
}
|
||||
|
||||
return ok.RowsAffected() > 0, nil
|
||||
}
|
||||
|
||||
// 解除两个表计之间的关联
|
||||
func (mr _MeterRepository) UnbindMeter(tx pgx.Tx, ctx context.Context, pid, masterMeter, slaveMeter string) (bool, error) {
|
||||
mr.log.Info("解除两个表计之间的关联", zap.String("master meter code", masterMeter), zap.String("slave meter code", slaveMeter))
|
||||
relationSql, relationArgs, _ := mr.ds.
|
||||
Update(goqu.T("meter_relations")).
|
||||
Set(
|
||||
goqu.Record{
|
||||
"revoked_at": types.Now(),
|
||||
},
|
||||
).
|
||||
Where(
|
||||
goqu.I("park_id").Eq(pid),
|
||||
goqu.I("master_meter_id").Eq(masterMeter),
|
||||
goqu.I("slave_meter_id").Eq(slaveMeter),
|
||||
goqu.I("revoked_at").IsNull(),
|
||||
).
|
||||
Prepared(true).ToSQL()
|
||||
|
||||
ok, err := tx.Exec(ctx, relationSql, relationArgs...)
|
||||
if err != nil {
|
||||
mr.log.Error("解除表计关系失败", zap.Error(err))
|
||||
return false, err
|
||||
}
|
||||
|
||||
return ok.RowsAffected() > 0, nil
|
||||
}
|
||||
|
||||
// 列出指定公摊表计的所有关联表计关系
|
||||
func (mr _MeterRepository) ListPooledMeterRelations(pid, code string) ([]*model.MeterRelation, error) {
|
||||
mr.log.Info("列出指定公摊表计的所有关联表计关系", zap.String("park id", pid), zap.String("meter code", code))
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
var relations []*model.MeterRelation
|
||||
relationsSql, relationsArgs, _ := mr.ds.
|
||||
From(goqu.T("meter_relations").As("r")).
|
||||
Select("r.*").
|
||||
Where(
|
||||
goqu.I("r.park_id").Eq(pid),
|
||||
goqu.I("r.master_meter_id").Eq(code),
|
||||
goqu.I("r.revoked_at").IsNull(),
|
||||
).
|
||||
Prepared(true).ToSQL()
|
||||
|
||||
if err := pgxscan.Select(ctx, global.DB, &relations, relationsSql, relationsArgs...); err != nil {
|
||||
mr.log.Error("查询表计关系失败", zap.Error(err))
|
||||
return make([]*model.MeterRelation, 0), err
|
||||
}
|
||||
|
||||
return relations, nil
|
||||
}
|
||||
|
||||
// 列出指定公摊表计列表所包含的全部关联表计关系
|
||||
func (mr _MeterRepository) ListPooledMeterRelationsByCodes(pid string, codes []string) ([]*model.MeterRelation, error) {
|
||||
mr.log.Info("列出指定公摊表计列表所包含的全部关联表计关系", zap.String("park id", pid), zap.Strings("meter codes", codes))
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
var relations []*model.MeterRelation
|
||||
relationsSql, relationsArgs, _ := mr.ds.
|
||||
From(goqu.T("meter_relations").As("r")).
|
||||
Select("r.*").
|
||||
Where(
|
||||
goqu.I("r.park_id").Eq(pid),
|
||||
goqu.I("r.master_meter_id").In(codes),
|
||||
goqu.I("r.revoked_at").IsNull(),
|
||||
).
|
||||
Prepared(true).ToSQL()
|
||||
|
||||
if err := pgxscan.Select(ctx, global.DB, &relations, relationsSql, relationsArgs...); err != nil {
|
||||
mr.log.Error("查询表计关系失败", zap.Error(err))
|
||||
return make([]*model.MeterRelation, 0), err
|
||||
}
|
||||
|
||||
return relations, nil
|
||||
}
|
||||
|
||||
// 列出指定商户表计、园区表计与公摊表计之间的关联关系
|
||||
func (mr _MeterRepository) ListMeterRelations(pid, code string) ([]*model.MeterRelation, error) {
|
||||
mr.log.Info("列出指定商户表计、园区表计与公摊表计之间的关联关系", zap.String("park id", pid), zap.String("meter code", code))
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
var relations []*model.MeterRelation
|
||||
relationsSql, relationsArgs, _ := mr.ds.
|
||||
From(goqu.T("meter_relations")).
|
||||
Select("*").
|
||||
Where(
|
||||
goqu.I("r.park_id").Eq(pid),
|
||||
goqu.I("r.slave_meter_id").Eq(code),
|
||||
goqu.I("r.revoked_at").IsNull(),
|
||||
).
|
||||
Prepared(true).ToSQL()
|
||||
|
||||
if err := pgxscan.Select(ctx, global.DB, &relations, relationsSql, relationsArgs...); err != nil {
|
||||
mr.log.Error("查询表计关系失败", zap.Error(err))
|
||||
return make([]*model.MeterRelation, 0), err
|
||||
}
|
||||
|
||||
return relations, nil
|
||||
}
|
||||
|
||||
// 列出指定园区中的所有公摊表计
|
||||
func (mr _MeterRepository) ListPoolingMeters(pid string, page uint, keyword *string) ([]*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")))).
|
||||
Select(
|
||||
"m.*", goqu.I("b.name").As("building_name"),
|
||||
).
|
||||
Where(
|
||||
goqu.I("m.park_id").Eq(pid),
|
||||
goqu.I("m.enabled").IsTrue(),
|
||||
goqu.I("m.meter_type").Eq(model.METER_INSTALLATION_POOLING),
|
||||
)
|
||||
countQuery := mr.ds.
|
||||
From(goqu.T("meter_04kv").As("m")).
|
||||
Select(goqu.COUNT("*")).
|
||||
Where(
|
||||
goqu.I("m.park_id").Eq(pid),
|
||||
goqu.I("m.enabled").IsTrue(),
|
||||
goqu.I("m.meter_type").Eq(model.METER_INSTALLATION_POOLING),
|
||||
)
|
||||
|
||||
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),
|
||||
),
|
||||
)
|
||||
countQuery = countQuery.Where(
|
||||
goqu.Or(
|
||||
goqu.I("m.code").ILike(pattern),
|
||||
goqu.I("m.address").ILike(pattern),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
startRow := (page - 1) * config.ServiceSettings.ItemsPageSize
|
||||
meterQuery = meterQuery.Order(goqu.I("m.code").Asc()).Offset(startRow).Limit(config.ServiceSettings.ItemsPageSize)
|
||||
|
||||
meterSql, meterArgs, _ := meterQuery.Prepared(true).ToSQL()
|
||||
countSql, countArgs, _ := countQuery.Prepared(true).ToSQL()
|
||||
|
||||
var (
|
||||
meters []*model.MeterDetail
|
||||
total int64
|
||||
)
|
||||
if err := pgxscan.Select(ctx, global.DB, &meters, meterSql, meterArgs...); err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
// 列出目前尚未绑定到公摊表计的商户表计
|
||||
func (mr _MeterRepository) ListUnboundMeters(uid string, pid *string, keyword *string, limit *uint) ([]*model.MeterDetail, error) {
|
||||
mr.log.Info("列出目前尚未绑定到公摊表计的商户表计", zap.Stringp("park id", pid), zap.String("user id", uid), zap.String("keyword", tools.DefaultTo(keyword, "")), zap.Uint("limit", tools.DefaultTo(limit, 0)))
|
||||
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")))).
|
||||
Select(
|
||||
"m.*", goqu.I("b.name").As("building_name"),
|
||||
).
|
||||
Where(
|
||||
goqu.I("m.meter_type").Eq(model.METER_INSTALLATION_TENEMENT),
|
||||
goqu.I("m.enabled").IsTrue(),
|
||||
)
|
||||
|
||||
if pid != nil && len(*pid) > 0 {
|
||||
meterQuery = meterQuery.Where(
|
||||
goqu.I("m.park_id").Eq(*pid),
|
||||
)
|
||||
}
|
||||
|
||||
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),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
slaveMeterQuery := mr.ds.
|
||||
From("meter_relations").
|
||||
Select("id")
|
||||
if pid != nil && len(*pid) > 0 {
|
||||
slaveMeterQuery = slaveMeterQuery.Where(
|
||||
goqu.I("park_id").Eq(*pid),
|
||||
)
|
||||
} else {
|
||||
slaveMeterQuery = slaveMeterQuery.Where(
|
||||
goqu.I("park_id").In(
|
||||
mr.ds.
|
||||
From("park").
|
||||
Select("id").
|
||||
Where(goqu.I("user_id").Eq(uid)),
|
||||
))
|
||||
}
|
||||
slaveMeterQuery = slaveMeterQuery.Where(
|
||||
goqu.I("revoked_at").IsNull(),
|
||||
)
|
||||
meterQuery = meterQuery.Where(
|
||||
goqu.I("m.code").NotIn(slaveMeterQuery),
|
||||
).
|
||||
Order(goqu.I("m.attached_at").Asc())
|
||||
|
||||
if limit != nil && *limit > 0 {
|
||||
meterQuery = meterQuery.Limit(*limit)
|
||||
}
|
||||
|
||||
meterSql, meterArgs, _ := meterQuery.Prepared(true).ToSQL()
|
||||
var meters []*model.MeterDetail
|
||||
if err := pgxscan.Select(ctx, global.DB, &meters, meterSql, meterArgs...); err != nil {
|
||||
mr.log.Error("查询商户表计信息失败", zap.Error(err))
|
||||
return make([]*model.MeterDetail, 0), err
|
||||
}
|
||||
|
||||
return meters, nil
|
||||
}
|
||||
|
||||
// 列出目前未绑定到商户的商户表计
|
||||
func (mr _MeterRepository) ListUnboundTenementMeters(uid string, pid *string, keyword *string, limit *uint) ([]*model.MeterDetail, error) {
|
||||
mr.log.Info("列出目前未绑定到商户的商户表计", zap.Stringp("park id", pid), zap.String("user id", uid), zap.String("keyword", tools.DefaultTo(keyword, "")), zap.Uint("limit", tools.DefaultTo(limit, 0)))
|
||||
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")))).
|
||||
Select(
|
||||
"m.*", goqu.I("b.name").As("building_name"),
|
||||
).
|
||||
Where(
|
||||
goqu.I("m.meter_type").Eq(model.METER_INSTALLATION_TENEMENT),
|
||||
goqu.I("m.enabled").IsTrue(),
|
||||
)
|
||||
|
||||
if pid != nil && len(*pid) > 0 {
|
||||
meterQuery = meterQuery.Where(
|
||||
goqu.I("m.park_id").Eq(*pid),
|
||||
)
|
||||
}
|
||||
|
||||
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),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
subMeterQuery := mr.ds.
|
||||
From("tenement_meter").
|
||||
Select("meter_id")
|
||||
if pid != nil && len(*pid) > 0 {
|
||||
subMeterQuery = subMeterQuery.Where(
|
||||
goqu.I("park_id").Eq(*pid),
|
||||
)
|
||||
} else {
|
||||
subMeterQuery = subMeterQuery.Where(
|
||||
goqu.I("park_id").In(
|
||||
mr.ds.
|
||||
From("park").
|
||||
Select("id").
|
||||
Where(goqu.I("user_id").Eq(uid)),
|
||||
))
|
||||
}
|
||||
subMeterQuery = subMeterQuery.Where(
|
||||
goqu.I("disassociated_at").IsNull(),
|
||||
)
|
||||
meterQuery = meterQuery.Where(
|
||||
goqu.I("m.code").NotIn(subMeterQuery),
|
||||
).
|
||||
Order(goqu.I("m.attached_at").Asc())
|
||||
|
||||
if limit != nil && *limit > 0 {
|
||||
meterQuery = meterQuery.Limit(*limit)
|
||||
}
|
||||
|
||||
meterSql, meterArgs, _ := meterQuery.Prepared(true).ToSQL()
|
||||
var meters []*model.MeterDetail
|
||||
if err := pgxscan.Select(ctx, global.DB, &meters, meterSql, meterArgs...); err != nil {
|
||||
mr.log.Error("查询商户表计信息失败", zap.Error(err))
|
||||
return make([]*model.MeterDetail, 0), err
|
||||
}
|
||||
|
||||
return meters, nil
|
||||
}
|
||||
|
||||
// 查询指定园区中的符合条件的抄表记录
|
||||
func (mr _MeterRepository) ListMeterReadings(pid string, keyword *string, page uint, start, end *types.Date, buidling *string) ([]*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")))).
|
||||
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")))).
|
||||
Select(goqu.COUNT("*")).
|
||||
Where(
|
||||
goqu.I("r.park_id").Eq(pid),
|
||||
)
|
||||
|
||||
if keyword != nil && len(*keyword) > 0 {
|
||||
pattern := fmt.Sprintf("%%%s%%", *keyword)
|
||||
readingQuery = readingQuery.Where(
|
||||
goqu.Or(
|
||||
goqu.I("m.code").ILike(pattern),
|
||||
goqu.I("m.address").ILike(pattern),
|
||||
),
|
||||
)
|
||||
countQuery = countQuery.Where(
|
||||
goqu.Or(
|
||||
goqu.I("m.code").ILike(pattern),
|
||||
goqu.I("m.address").ILike(pattern),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
if start != nil {
|
||||
readingQuery = readingQuery.Where(
|
||||
goqu.I("r.read_at").Gte(start.ToBeginningOfDate()),
|
||||
)
|
||||
countQuery = countQuery.Where(
|
||||
goqu.I("r.read_at").Gte(start.ToBeginningOfDate()),
|
||||
)
|
||||
}
|
||||
|
||||
if end != nil {
|
||||
readingQuery = readingQuery.Where(
|
||||
goqu.I("r.read_at").Lte(end.ToEndingOfDate()),
|
||||
)
|
||||
countQuery = countQuery.Where(
|
||||
goqu.I("r.read_at").Lte(end.ToEndingOfDate()),
|
||||
)
|
||||
}
|
||||
|
||||
if buidling != nil && len(*buidling) > 0 {
|
||||
readingQuery = readingQuery.Where(
|
||||
goqu.I("m.building").Eq(*buidling),
|
||||
)
|
||||
countQuery = countQuery.Where(
|
||||
goqu.I("m.building").Eq(*buidling),
|
||||
)
|
||||
}
|
||||
|
||||
startRow := (page - 1) * 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()
|
||||
|
||||
var (
|
||||
readings []*model.MeterReading
|
||||
total int64
|
||||
)
|
||||
if err := pgxscan.Select(ctx, global.DB, &readings, readingSql, readingArgs...); err != nil {
|
||||
mr.log.Error("查询抄表记录失败", zap.Error(err))
|
||||
return make([]*model.MeterReading, 0), 0, err
|
||||
}
|
||||
if err := pgxscan.Get(ctx, global.DB, &total, countSql, countArgs...); err != nil {
|
||||
mr.log.Error("查询抄表记录数量失败", zap.Error(err))
|
||||
return make([]*model.MeterReading, 0), 0, err
|
||||
}
|
||||
|
||||
return readings, total, nil
|
||||
}
|
||||
|
||||
// 修改指定表计的指定抄表记录
|
||||
func (mr _MeterRepository) UpdateMeterReading(pid, mid string, readAt types.DateTime, reading *vo.MeterReadingForm) (bool, error) {
|
||||
mr.log.Info("修改指定表计的指定抄表记录", zap.String("park id", pid), zap.String("meter id", mid), logger.DateTimeField("read at", readAt), zap.Any("reading", reading))
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
updateSql, updateArgs, _ := mr.ds.
|
||||
Update(goqu.T("meter_reading")).
|
||||
Set(
|
||||
goqu.Record{
|
||||
"overall": reading.Overall,
|
||||
"critical": reading.Critical,
|
||||
"peak": reading.Peak,
|
||||
"flat": reading.Flat,
|
||||
"valley": reading.Valley,
|
||||
},
|
||||
).
|
||||
Where(
|
||||
goqu.I("park_id").Eq(pid),
|
||||
goqu.I("meter_id").Eq(mid),
|
||||
goqu.I("read_at").Eq(readAt),
|
||||
).
|
||||
Prepared(true).ToSQL()
|
||||
|
||||
ok, err := global.DB.Exec(ctx, updateSql, updateArgs...)
|
||||
if err != nil {
|
||||
mr.log.Error("更新抄表记录失败", zap.Error(err))
|
||||
return false, err
|
||||
}
|
||||
|
||||
return ok.RowsAffected() > 0, nil
|
||||
}
|
||||
|
||||
// 列出指定园区中指定时间区域内的所有表计抄表记录
|
||||
func (mr _MeterRepository) ListMeterReadingsByTimeRange(pid string, start, end types.Date) ([]*model.MeterReading, error) {
|
||||
mr.log.Info("列出指定园区中指定时间区域内的所有表计抄表记录", zap.String("park id", pid), zap.Time("start", start.Time), zap.Time("end", end.Time))
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
var readings []*model.MeterReading
|
||||
readingSql, readingArgs, _ := mr.ds.
|
||||
From(goqu.T("meter_reading").As("r")).
|
||||
Select("*").
|
||||
Where(
|
||||
goqu.I("r.park_id").Eq(pid),
|
||||
goqu.I("r.read_at").Gte(start.ToBeginningOfDate()),
|
||||
goqu.I("r.read_at").Lte(end.ToEndingOfDate()),
|
||||
).
|
||||
Order(goqu.I("r.read_at").Desc()).
|
||||
Prepared(true).ToSQL()
|
||||
|
||||
if err := pgxscan.Select(ctx, global.DB, &readings, readingSql, readingArgs...); err != nil {
|
||||
mr.log.Error("查询抄表记录失败", zap.Error(err))
|
||||
return make([]*model.MeterReading, 0), err
|
||||
}
|
||||
|
||||
return readings, nil
|
||||
}
|
||||
|
||||
// 列出指定园区中在指定日期之前的最后一次抄表记录
|
||||
func (mr _MeterRepository) ListLastMeterReading(pid string, date types.Date) ([]*model.MeterReading, error) {
|
||||
mr.log.Info("列出指定园区中在指定日期之前的最后一次抄表记录", zap.String("park id", pid), zap.Time("date", date.Time))
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
var readings []*model.MeterReading
|
||||
readingSql, readingArgs, _ := mr.ds.
|
||||
From(goqu.T("meter_reading")).
|
||||
Select(
|
||||
goqu.MAX("read_at").As("read_at"),
|
||||
"park_id", "meter_id", "overall", "critical", "peak", "flat", "valley",
|
||||
).
|
||||
Where(
|
||||
goqu.I("park_id").Eq(pid),
|
||||
goqu.I("read_at").Lt(date.ToEndingOfDate()),
|
||||
).
|
||||
GroupBy("park_id", "meter_id", "overall", "critical", "peak", "flat", "valley").
|
||||
Order(goqu.I("read_at").Desc()).
|
||||
Limit(1).
|
||||
Prepared(true).ToSQL()
|
||||
|
||||
if err := pgxscan.Select(ctx, global.DB, &readings, readingSql, readingArgs...); err != nil {
|
||||
mr.log.Error("查询抄表记录失败", zap.Error(err))
|
||||
return make([]*model.MeterReading, 0), err
|
||||
}
|
||||
|
||||
return readings, nil
|
||||
}
|
||||
|
||||
// 列出指定园区中的表计与商户的关联详细记录,用于写入Excel模板文件
|
||||
func (mr _MeterRepository) ListMeterDocForTemplate(pid string) ([]*model.SimpleMeterDocument, error) {
|
||||
mr.log.Info("列出指定园区中的表计与商户的关联详细记录", zap.String("park id", pid))
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
var docs []*model.SimpleMeterDocument
|
||||
docSql, docArgs, _ := mr.ds.
|
||||
From(goqu.T("meter_04kv").As("m")).
|
||||
LeftJoin(
|
||||
goqu.T("tenement_meter").As("tm"),
|
||||
goqu.On(
|
||||
goqu.I("m.code").Eq(goqu.I("tm.meter_id")),
|
||||
goqu.I("m.park_id").Eq(goqu.I("tm.park_id")),
|
||||
),
|
||||
).
|
||||
LeftJoin(
|
||||
goqu.T("tenement").As("t"),
|
||||
goqu.On(
|
||||
goqu.I("tm.tenement_id").Eq(goqu.I("t.id")),
|
||||
goqu.I("tm.park_id").Eq(goqu.I("t.park_id")),
|
||||
),
|
||||
).
|
||||
Select(
|
||||
"m.code", "m.address", "m.ratio", "m.seq", goqu.I("t.full_name").As("tenement_name"),
|
||||
).
|
||||
Where(
|
||||
goqu.I("m.park_id").Eq(pid),
|
||||
goqu.I("m.enabled").IsTrue(),
|
||||
goqu.I("tm.disassociated_at").IsNull(),
|
||||
).
|
||||
Order(goqu.I("m.seq").Asc()).
|
||||
Prepared(true).ToSQL()
|
||||
|
||||
if err := pgxscan.Select(ctx, global.DB, &docs, docSql, docArgs...); err != nil {
|
||||
mr.log.Error("查询表计与商户关联信息失败", zap.Error(err))
|
||||
return make([]*model.SimpleMeterDocument, 0), err
|
||||
}
|
||||
|
||||
return docs, nil
|
||||
}
|
||||
419
repository/park.go
Normal file
419
repository/park.go
Normal file
@@ -0,0 +1,419 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
"electricity_bill_calc/global"
|
||||
"electricity_bill_calc/logger"
|
||||
"electricity_bill_calc/model"
|
||||
"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"
|
||||
"github.com/jackc/pgx/v5"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type _ParkRepository struct {
|
||||
log *zap.Logger
|
||||
ds goqu.DialectWrapper
|
||||
}
|
||||
|
||||
var ParkRepository = _ParkRepository{
|
||||
log: logger.Named("Repository", "Park"),
|
||||
ds: goqu.Dialect("postgres"),
|
||||
}
|
||||
|
||||
// 列出指定用户下的所有园区
|
||||
func (pr _ParkRepository) ListAllParks(uid string) ([]*model.Park, error) {
|
||||
pr.log.Info("列出指定用户下的所有园区", zap.String("uid", uid))
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
var parks = make([]*model.Park, 0)
|
||||
parkQuerySql, parkParams, _ := pr.ds.
|
||||
From("park").
|
||||
Select(
|
||||
"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",
|
||||
).
|
||||
Where(
|
||||
goqu.I("user_id").Eq(uid),
|
||||
goqu.I("deleted_at").IsNull(),
|
||||
).
|
||||
Order(goqu.I("created_at").Asc()).
|
||||
Prepared(true).ToSQL()
|
||||
if err := pgxscan.Select(ctx, global.DB, &parks, parkQuerySql, parkParams...); err != nil {
|
||||
pr.log.Error("列出指定用户下的所有园区失败!", zap.Error(err))
|
||||
return make([]*model.Park, 0), err
|
||||
}
|
||||
return parks, nil
|
||||
}
|
||||
|
||||
// 检查并确定指定园区的归属情况
|
||||
func (pr _ParkRepository) IsParkBelongs(pid, uid string) (bool, error) {
|
||||
pr.log.Info("检查并确定指定园区的归属情况", zap.String("pid", pid), zap.String("uid", uid))
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
var count int64
|
||||
parkQuerySql, parkParams, _ := pr.ds.
|
||||
From("park").
|
||||
Select(goqu.COUNT("*")).
|
||||
Where(
|
||||
goqu.I("id").Eq(pid),
|
||||
goqu.I("user_id").Eq(uid),
|
||||
).
|
||||
Prepared(true).ToSQL()
|
||||
if err := pgxscan.Get(ctx, global.DB, &count, parkQuerySql, parkParams...); err != nil {
|
||||
pr.log.Error("检查并确定指定园区的归属情况失败!", zap.Error(err))
|
||||
return false, err
|
||||
}
|
||||
return count > 0, nil
|
||||
}
|
||||
|
||||
// 创建一个属于指定用户的新园区。该创建功能不会对园区的名称进行检查。
|
||||
func (pr _ParkRepository) CreatePark(ownerId string, park *model.Park) (bool, error) {
|
||||
pr.log.Info("创建一个属于指定用户的新园区", zap.String("ownerId", ownerId))
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
timeNow := types.Now()
|
||||
serial.StringSerialRequestChan <- 1
|
||||
code := serial.Prefix("P", <-serial.StringSerialResponseChan)
|
||||
createSql, createArgs, _ := pr.ds.
|
||||
Insert("park").
|
||||
Cols(
|
||||
"id", "user_id", "name", "abbr", "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",
|
||||
).
|
||||
Vals(goqu.Vals{
|
||||
code,
|
||||
ownerId, park.Name, tools.PinyinAbbr(park.Name),
|
||||
park.Area, park.TenementQuantity, park.Capacity, park.Category,
|
||||
park.MeterType, park.Region, park.Address, park.Contact, park.Phone, park.Enabled, park.PricePolicy, park.TaxRate,
|
||||
park.BasicPooled, park.AdjustPooled, park.LossPooled, park.PublicPooled, timeNow, timeNow,
|
||||
}).
|
||||
Prepared(true).ToSQL()
|
||||
rs, err := global.DB.Exec(ctx, createSql, createArgs...)
|
||||
if err != nil {
|
||||
pr.log.Error("创建一个属于指定用户的新园区失败!", zap.Error(err))
|
||||
return false, err
|
||||
}
|
||||
return rs.RowsAffected() > 0, nil
|
||||
}
|
||||
|
||||
// 获取指定园区的详细信息
|
||||
func (pr _ParkRepository) RetrieveParkDetail(pid string) (*model.Park, error) {
|
||||
pr.log.Info("获取指定园区的详细信息", zap.String("pid", pid))
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
var park model.Park
|
||||
parkSql, parkArgs, _ := pr.ds.
|
||||
From("park").
|
||||
Select(
|
||||
"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",
|
||||
).
|
||||
Where(goqu.I("id").Eq(pid)).
|
||||
Prepared(true).ToSQL()
|
||||
if err := pgxscan.Get(ctx, global.DB, &park, parkSql, parkArgs...); err != nil {
|
||||
pr.log.Error("获取指定园区的详细信息失败!", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
return &park, nil
|
||||
}
|
||||
|
||||
// 获取园区对应的用户ID
|
||||
func (pr _ParkRepository) RetrieveParkBelongs(pid string) (string, error) {
|
||||
pr.log.Info("获取园区对应的用户ID", zap.String("pid", pid))
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
var uid string
|
||||
parkSql, parkArgs, _ := pr.ds.
|
||||
From("park").
|
||||
Select(goqu.I("user_id")).
|
||||
Where(goqu.I("id").Eq(pid)).
|
||||
Prepared(true).ToSQL()
|
||||
if err := pgxscan.Get(ctx, global.DB, &uid, parkSql, parkArgs...); err != nil {
|
||||
pr.log.Error("获取园区对应的用户ID失败!", zap.Error(err))
|
||||
return "", err
|
||||
}
|
||||
return uid, nil
|
||||
}
|
||||
|
||||
// 更新指定园区的信息
|
||||
func (pr _ParkRepository) UpdatePark(pid string, park *model.Park) (bool, error) {
|
||||
pr.log.Info("更新指定园区的信息", zap.String("pid", pid))
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
timeNow := types.Now()
|
||||
updateSql, updateArgs, _ := pr.ds.
|
||||
Update("park").
|
||||
Set(goqu.Record{
|
||||
"name": park.Name,
|
||||
"abbr": tools.PinyinAbbr(park.Name),
|
||||
"area": park.Area,
|
||||
"tenement_quantity": park.TenementQuantity,
|
||||
"capacity": park.Capacity,
|
||||
"category": park.Category,
|
||||
"meter_04kv_type": park.MeterType,
|
||||
"region": park.Region,
|
||||
"address": park.Address,
|
||||
"contact": park.Contact,
|
||||
"phone": park.Phone,
|
||||
"price_policy": park.PricePolicy,
|
||||
"tax_rate": park.TaxRate,
|
||||
"basic_pooled": park.BasicPooled,
|
||||
"adjust_pooled": park.AdjustPooled,
|
||||
"loss_pooled": park.LossPooled,
|
||||
"public_pooled": park.PublicPooled,
|
||||
"last_modified_at": timeNow,
|
||||
}).
|
||||
Where(goqu.I("id").Eq(pid)).
|
||||
Prepared(true).ToSQL()
|
||||
|
||||
ok, err := global.DB.Exec(ctx, updateSql, updateArgs...)
|
||||
if err != nil {
|
||||
pr.log.Error("更新指定园区的信息失败!", zap.Error(err))
|
||||
return false, err
|
||||
}
|
||||
return ok.RowsAffected() > 0, nil
|
||||
}
|
||||
|
||||
// 设定园区的可用状态
|
||||
func (pr _ParkRepository) EnablingPark(pid string, enabled bool) (bool, error) {
|
||||
pr.log.Info("设定园区的可用状态", zap.String("pid", pid), zap.Bool("enabled", enabled))
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
timeNow := types.Now()
|
||||
updateSql, updateArgs, _ := pr.ds.
|
||||
Update("park").
|
||||
Set(goqu.Record{
|
||||
"enabled": enabled,
|
||||
"last_modified_at": timeNow,
|
||||
}).
|
||||
Where(goqu.I("id").Eq(pid)).
|
||||
Prepared(true).ToSQL()
|
||||
|
||||
ok, err := global.DB.Exec(ctx, updateSql, updateArgs...)
|
||||
if err != nil {
|
||||
pr.log.Error("设定园区的可用状态失败!", zap.Error(err))
|
||||
return false, err
|
||||
}
|
||||
return ok.RowsAffected() > 0, nil
|
||||
}
|
||||
|
||||
// 删除指定园区(软删除)
|
||||
func (pr _ParkRepository) DeletePark(pid string) (bool, error) {
|
||||
pr.log.Info("删除指定园区(软删除)", zap.String("pid", pid))
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
timeNow := types.Now()
|
||||
updateSql, updateArgs, _ := pr.ds.
|
||||
Update("park").
|
||||
Set(goqu.Record{
|
||||
"deleted_at": timeNow,
|
||||
"last_modified_at": timeNow,
|
||||
}).
|
||||
Where(goqu.I("id").Eq(pid)).
|
||||
Prepared(true).ToSQL()
|
||||
|
||||
ok, err := global.DB.Exec(ctx, updateSql, updateArgs...)
|
||||
if err != nil {
|
||||
pr.log.Error("删除指定园区(软删除)失败!", zap.Error(err))
|
||||
return false, err
|
||||
}
|
||||
return ok.RowsAffected() > 0, nil
|
||||
}
|
||||
|
||||
// 检索给定的园区详细信息列表
|
||||
func (pr _ParkRepository) RetrieveParks(pids []string) ([]*model.Park, error) {
|
||||
pr.log.Info("检索给定的园区详细信息列表", zap.Strings("pids", pids))
|
||||
if len(pids) == 0 {
|
||||
pr.log.Info("给定要检索的园区ID列表为空,执行快速返回。")
|
||||
return make([]*model.Park, 0), nil
|
||||
}
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
var parks []*model.Park
|
||||
parkSql, parkArgs, _ := pr.ds.
|
||||
From("park").
|
||||
Where(goqu.I("id").In(pids)).
|
||||
Prepared(true).ToSQL()
|
||||
if err := pgxscan.Select(ctx, global.DB, &parks, parkSql, parkArgs...); err != nil {
|
||||
pr.log.Error("检索给定的园区详细信息列表失败!", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
return parks, nil
|
||||
}
|
||||
|
||||
// 获取指定园区中的建筑
|
||||
func (pr _ParkRepository) RetrieveParkBuildings(pid string) ([]*model.ParkBuilding, error) {
|
||||
pr.log.Info("获取指定园区中的建筑", zap.String("pid", pid))
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
var buildings []*model.ParkBuilding
|
||||
buildingSql, buildingArgs, _ := pr.ds.
|
||||
From("park_building").
|
||||
Where(
|
||||
goqu.I("park_id").Eq(pid),
|
||||
goqu.I("deleted_at").IsNull(),
|
||||
).
|
||||
Order(goqu.I("created_at").Asc()).
|
||||
Prepared(true).ToSQL()
|
||||
|
||||
if err := pgxscan.Select(ctx, global.DB, &buildings, buildingSql, buildingArgs...); err != nil {
|
||||
pr.log.Error("获取指定园区中的建筑失败!", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
return buildings, nil
|
||||
}
|
||||
|
||||
// 在指定园区中创建一个新建筑
|
||||
func (pr _ParkRepository) CreateParkBuilding(pid, name string, floor *string) (bool, error) {
|
||||
pr.log.Info("在指定园区中创建一个新建筑", zap.String("pid", pid), zap.String("name", name), zap.Stringp("floor", floor))
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
timeNow := types.Now()
|
||||
serial.StringSerialRequestChan <- 1
|
||||
code := serial.Prefix("B", <-serial.StringSerialResponseChan)
|
||||
createSql, createArgs, _ := pr.ds.
|
||||
Insert("park_building").
|
||||
Cols(
|
||||
"id", "park_id", "name", "floors", "enabled", "created_at", "last_modified_at",
|
||||
).
|
||||
Vals(goqu.Vals{
|
||||
code,
|
||||
pid, name, floor, true, timeNow, timeNow,
|
||||
}).
|
||||
Prepared(true).ToSQL()
|
||||
|
||||
rs, err := global.DB.Exec(ctx, createSql, createArgs...)
|
||||
if err != nil {
|
||||
pr.log.Error("在指定园区中创建一个新建筑失败!", zap.Error(err))
|
||||
return false, err
|
||||
}
|
||||
return rs.RowsAffected() > 0, nil
|
||||
}
|
||||
|
||||
// 在指定园区中创建一个建筑,这个方法会使用事务
|
||||
func (pr _ParkRepository) CreateParkBuildingWithTransaction(tx pgx.Tx, ctx context.Context, pid, name string, floor *string) (bool, error) {
|
||||
timeNow := types.Now()
|
||||
serial.StringSerialRequestChan <- 1
|
||||
code := serial.Prefix("B", <-serial.StringSerialResponseChan)
|
||||
createSql, createArgs, _ := pr.ds.
|
||||
Insert("park_building").
|
||||
Cols(
|
||||
"id", "park_id", "name", "floors", "enabled", "created_at", "last_modified_at",
|
||||
).
|
||||
Vals(goqu.Vals{
|
||||
code,
|
||||
pid, name, floor, true, timeNow, timeNow,
|
||||
}).
|
||||
Prepared(true).ToSQL()
|
||||
|
||||
rs, err := tx.Exec(ctx, createSql, createArgs...)
|
||||
if err != nil {
|
||||
pr.log.Error("在指定园区中创建一个新建筑失败!", zap.Error(err))
|
||||
return false, err
|
||||
}
|
||||
return rs.RowsAffected() > 0, nil
|
||||
}
|
||||
|
||||
// 修改指定园区中指定建筑的信息
|
||||
func (pr _ParkRepository) ModifyParkBuilding(id, pid, name string, floor *string) (bool, error) {
|
||||
pr.log.Info("修改指定园区中指定建筑的信息", zap.String("id", id), zap.String("pid", pid), zap.String("name", name), zap.Stringp("floor", floor))
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
timeNow := types.Now()
|
||||
updateSql, updateArgs, _ := pr.ds.
|
||||
Update("park_building").
|
||||
Set(goqu.Record{
|
||||
"name": name,
|
||||
"floors": floor,
|
||||
"last_modified_at": timeNow,
|
||||
}).
|
||||
Where(
|
||||
goqu.I("id").Eq(id),
|
||||
goqu.I("park_id").Eq(pid),
|
||||
).
|
||||
Prepared(true).ToSQL()
|
||||
|
||||
rs, err := global.DB.Exec(ctx, updateSql, updateArgs...)
|
||||
if err != nil {
|
||||
pr.log.Error("修改指定园区中指定建筑的信息失败!", zap.Error(err))
|
||||
return false, err
|
||||
}
|
||||
return rs.RowsAffected() > 0, nil
|
||||
}
|
||||
|
||||
// 修改指定建筑的可以状态
|
||||
func (pr _ParkRepository) EnablingParkBuilding(id, pid string, enabled bool) (bool, error) {
|
||||
pr.log.Info("修改指定建筑的可以状态", zap.String("id", id), zap.String("pid", pid), zap.Bool("enabled", enabled))
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
timeNow := types.Now()
|
||||
updateSql, updateArgs, _ := pr.ds.
|
||||
Update("park_building").
|
||||
Set(goqu.Record{
|
||||
"enabled": enabled,
|
||||
"last_modified_at": timeNow,
|
||||
}).
|
||||
Where(
|
||||
goqu.I("id").Eq(id),
|
||||
goqu.I("park_id").Eq(pid),
|
||||
).
|
||||
Prepared(true).ToSQL()
|
||||
|
||||
rs, err := global.DB.Exec(ctx, updateSql, updateArgs...)
|
||||
if err != nil {
|
||||
pr.log.Error("修改指定建筑的可以状态失败!", zap.Error(err))
|
||||
return false, err
|
||||
}
|
||||
return rs.RowsAffected() > 0, nil
|
||||
}
|
||||
|
||||
// 删除指定建筑(软删除)
|
||||
func (pr _ParkRepository) DeleteParkBuilding(id, pid string) (bool, error) {
|
||||
pr.log.Info("删除指定建筑(软删除)", zap.String("id", id), zap.String("pid", pid))
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
timeNow := types.Now()
|
||||
updateSql, updateArgs, _ := pr.ds.
|
||||
Update("park_building").
|
||||
Set(goqu.Record{
|
||||
"deleted_at": timeNow,
|
||||
"last_modified_at": timeNow,
|
||||
}).
|
||||
Where(
|
||||
goqu.I("id").Eq(id),
|
||||
goqu.I("park_id").Eq(pid),
|
||||
).
|
||||
Prepared(true).ToSQL()
|
||||
|
||||
rs, err := global.DB.Exec(ctx, updateSql, updateArgs...)
|
||||
if err != nil {
|
||||
pr.log.Error("删除指定建筑(软删除)失败!", zap.Error(err))
|
||||
return false, err
|
||||
}
|
||||
return rs.RowsAffected() > 0, nil
|
||||
}
|
||||
79
repository/region.go
Normal file
79
repository/region.go
Normal file
@@ -0,0 +1,79 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"electricity_bill_calc/global"
|
||||
"electricity_bill_calc/logger"
|
||||
"electricity_bill_calc/model"
|
||||
|
||||
"github.com/doug-martin/goqu/v9"
|
||||
_ "github.com/doug-martin/goqu/v9/dialect/postgres"
|
||||
"github.com/georgysavva/scany/v2/pgxscan"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type _RegionRepository struct {
|
||||
log *zap.Logger
|
||||
ds goqu.DialectWrapper
|
||||
}
|
||||
|
||||
var RegionRepository = _RegionRepository{
|
||||
log: logger.Named("Repository", "Region"),
|
||||
ds: goqu.Dialect("postgres"),
|
||||
}
|
||||
|
||||
// 获取指定行政区划下所有直接子级行政区划
|
||||
func (r *_RegionRepository) FindSubRegions(parent string) ([]model.Region, error) {
|
||||
r.log.Info("获取指定行政区划下所有直接子级行政区划", zap.String("parent", parent))
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
var regions []model.Region
|
||||
regionQuerySql, regionParams, _ := r.ds.
|
||||
From("region").
|
||||
Where(goqu.Ex{"parent": parent}).
|
||||
Prepared(true).ToSQL()
|
||||
if err := pgxscan.Select(ctx, global.DB, ®ions, regionQuerySql, regionParams...); err != nil {
|
||||
r.log.Error("获取指定行政区划下所有直接子级行政区划失败!", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
return regions, nil
|
||||
}
|
||||
|
||||
// 获取一个指定编号的行政区划详细信息
|
||||
func (r *_RegionRepository) FindRegion(code string) (*model.Region, error) {
|
||||
r.log.Info("获取指定行政区划信息", zap.String("code", code))
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
var region model.Region
|
||||
regionQuerySql, regionParams, _ := r.ds.
|
||||
From("region").
|
||||
Where(goqu.Ex{"code": code}).
|
||||
Prepared(true).ToSQL()
|
||||
|
||||
if err := pgxscan.Get(ctx, global.DB, ®ion, regionQuerySql, regionParams...); err != nil {
|
||||
r.log.Error("获取指定行政区划信息失败!", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
return ®ion, nil
|
||||
}
|
||||
|
||||
// 获取指定行政区划的所有直接和非直接父级
|
||||
func (r *_RegionRepository) FindParentRegions(code string) ([]*model.Region, error) {
|
||||
r.log.Info("获取指定行政区划的所有直接和非直接父级", zap.String("code", code))
|
||||
var (
|
||||
regionsScanTask = []string{code}
|
||||
regions = make([]*model.Region, 0)
|
||||
)
|
||||
for len(regionsScanTask) > 0 {
|
||||
region, err := r.FindRegion(regionsScanTask[0])
|
||||
regionsScanTask = append([]string{}, regionsScanTask[1:]...)
|
||||
if err == nil && region != nil {
|
||||
regions = append(regions, region)
|
||||
if region.Parent != "0" {
|
||||
regionsScanTask = append(regionsScanTask, region.Parent)
|
||||
}
|
||||
}
|
||||
}
|
||||
return regions, nil
|
||||
}
|
||||
846
repository/report.go
Normal file
846
repository/report.go
Normal file
@@ -0,0 +1,846 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"electricity_bill_calc/config"
|
||||
"electricity_bill_calc/exceptions"
|
||||
"electricity_bill_calc/global"
|
||||
"electricity_bill_calc/logger"
|
||||
"electricity_bill_calc/model"
|
||||
"electricity_bill_calc/tools/serial"
|
||||
"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/samber/lo"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type _ReportRepository struct {
|
||||
log *zap.Logger
|
||||
ds goqu.DialectWrapper
|
||||
}
|
||||
|
||||
var ReportRepository = _ReportRepository{
|
||||
log: logger.Named("Repository", "Report"),
|
||||
ds: goqu.Dialect("postgres"),
|
||||
}
|
||||
|
||||
// 检查指定核算报表的归属情况
|
||||
func (rr _ReportRepository) IsBelongsTo(rid, uid string) (bool, error) {
|
||||
rr.log.Info("检查指定核算报表的归属", zap.String("User", uid), zap.String("Report", rid))
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
querySql, queryParams, _ := rr.ds.
|
||||
From(goqu.T("report").As("r")).
|
||||
Join(goqu.T("park").As("p"), goqu.On(goqu.I("p.id").Eq(goqu.I("r.park_id")))).
|
||||
Select(goqu.COUNT("r.*")).
|
||||
Where(
|
||||
goqu.I("r.id").Eq(rid),
|
||||
goqu.I("p.user_id").Eq(uid),
|
||||
).
|
||||
Prepared(true).ToSQL()
|
||||
|
||||
var count int64
|
||||
if err := pgxscan.Get(ctx, global.DB, &count, querySql, queryParams...); err != nil {
|
||||
rr.log.Error("检查指定核算报表的归属出现错误", zap.Error(err))
|
||||
return false, err
|
||||
}
|
||||
return count > 0, nil
|
||||
}
|
||||
|
||||
// 获取指定用户下所有园区的尚未发布的简易核算报表索引内容
|
||||
func (rr _ReportRepository) ListDraftReportIndicies(uid string) ([]*model.ReportIndex, error) {
|
||||
rr.log.Info("获取指定用户下的所有尚未发布的报表索引", zap.String("User", uid))
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
querySql, queryParams, _ := rr.ds.
|
||||
From(goqu.T("report").As("r")).
|
||||
Join(goqu.T("report_task").As("t"), goqu.On(goqu.I("t.id").Eq(goqu.I("r.id")))).
|
||||
Join(goqu.T("park").As("p"), goqu.On(goqu.I("p.id").Eq(goqu.I("r.park_id")))).
|
||||
Select("r.*", goqu.I("t.status"), goqu.I("t.message")).
|
||||
Where(
|
||||
goqu.I("p.user_id").Eq(uid),
|
||||
goqu.I("r.published").IsFalse(),
|
||||
).
|
||||
Order(goqu.I("r.created_at").Desc()).
|
||||
Prepared(true).ToSQL()
|
||||
|
||||
var indicies []*model.ReportIndex = make([]*model.ReportIndex, 0)
|
||||
if err := pgxscan.Select(ctx, global.DB, &indicies, querySql, queryParams...); err != nil {
|
||||
rr.log.Error("获取指定用户下的所有尚未发布的报表索引出现错误", zap.Error(err))
|
||||
return indicies, err
|
||||
}
|
||||
return indicies, nil
|
||||
}
|
||||
|
||||
// 获取指定报表的详细索引内容
|
||||
func (rr _ReportRepository) GetReportIndex(rid string) (*model.ReportIndex, error) {
|
||||
rr.log.Info("获取指定报表的详细索引", zap.String("Report", rid))
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
querySql, queryParams, _ := rr.ds.
|
||||
From(goqu.T("report").As("r")).
|
||||
Join(goqu.T("report_task").As("t"), goqu.On(goqu.I("t.id").Eq(goqu.I("r.id")))).
|
||||
Select("r.*", goqu.I("t.status"), goqu.I("t.message")).
|
||||
Where(goqu.I("r.id").Eq(rid)).
|
||||
Prepared(true).ToSQL()
|
||||
|
||||
var index model.ReportIndex
|
||||
if err := pgxscan.Get(ctx, global.DB, &index, querySql, queryParams...); err != nil {
|
||||
rr.log.Error("获取指定报表的详细索引出现错误", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
return &index, nil
|
||||
}
|
||||
|
||||
// 为指园区创建一个新的核算报表
|
||||
func (rr _ReportRepository) CreateReport(form *vo.ReportCreationForm) (bool, error) {
|
||||
rr.log.Info("为指定园区创建一个新的核算报表", zap.String("Park", form.Park))
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
tx, err := global.DB.Begin(ctx)
|
||||
if err != nil {
|
||||
rr.log.Error("未能开始一个数据库事务", zap.Error(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)
|
||||
}
|
||||
createTime := types.Now()
|
||||
periodRange := types.NewDateRange(&form.PeriodBegin, &form.PeriodEnd)
|
||||
serial.StringSerialRequestChan <- 1
|
||||
reportId := serial.Prefix("R", <-serial.StringSerialResponseChan)
|
||||
|
||||
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",
|
||||
"last_modified_at",
|
||||
).
|
||||
Vals(goqu.Vals{
|
||||
reportId, park.Id, periodRange, park.Category, park.MeterType, park.PricePolicy,
|
||||
park.BasicPooled, park.AdjustPooled, park.LossPooled, park.PublicPooled, createTime,
|
||||
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{
|
||||
Amount: form.Critical,
|
||||
Fee: form.CriticalFee,
|
||||
},
|
||||
model.ConsumptionUnit{
|
||||
Amount: form.Peak,
|
||||
Fee: form.PeakFee,
|
||||
},
|
||||
model.ConsumptionUnit{
|
||||
Amount: form.Flat,
|
||||
Fee: form.FlatFee,
|
||||
},
|
||||
model.ConsumptionUnit{
|
||||
Amount: form.Valley,
|
||||
Fee: form.ValleyFee,
|
||||
},
|
||||
form.BasicFee,
|
||||
form.AdjustFee,
|
||||
}).
|
||||
Prepared(true).ToSQL()
|
||||
taskSql, taskArgs, _ := rr.ds.
|
||||
Insert(goqu.T("report_task")).
|
||||
Cols("id", "status", "last_modified_at").
|
||||
Vals(goqu.Vals{reportId, model.REPORT_CALCULATE_TASK_STATUS_PENDING, createTime}).
|
||||
Prepared(true).ToSQL()
|
||||
|
||||
resIndex, err := tx.Exec(ctx, createSql, createArgs...)
|
||||
if err != nil {
|
||||
rr.log.Error("创建核算报表索引时出现错误", zap.Error(err))
|
||||
tx.Rollback(ctx)
|
||||
return false, err
|
||||
}
|
||||
if resIndex.RowsAffected() == 0 {
|
||||
rr.log.Error("保存核算报表索引时出现错误", zap.Error(err))
|
||||
tx.Rollback(ctx)
|
||||
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
|
||||
}
|
||||
if resSummary.RowsAffected() == 0 {
|
||||
rr.log.Error("保存核算报表汇总时出现错误", zap.Error(err))
|
||||
tx.Rollback(ctx)
|
||||
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
|
||||
}
|
||||
if resTask.RowsAffected() == 0 {
|
||||
rr.log.Error("保存核算报表任务时出现错误", zap.Error(err))
|
||||
tx.Rollback(ctx)
|
||||
return false, exceptions.NewUnsuccessCreateError("创建核算报表任务时出现错误")
|
||||
}
|
||||
err = tx.Commit(ctx)
|
||||
if err != nil {
|
||||
rr.log.Error("提交核算报表创建事务时出现错误", zap.Error(err))
|
||||
tx.Rollback(ctx)
|
||||
return false, err
|
||||
}
|
||||
return resIndex.RowsAffected() > 0 && resSummary.RowsAffected() > 0 && resTask.RowsAffected() > 0, nil
|
||||
}
|
||||
|
||||
// 更新报表的基本信息
|
||||
func (rr _ReportRepository) UpdateReportSummary(rid string, form *vo.ReportModifyForm) (bool, error) {
|
||||
rr.log.Info("更新指定报表的基本信息", zap.String("Report", rid))
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
tx, err := global.DB.Begin(ctx)
|
||||
if err != nil {
|
||||
rr.log.Error("未能开始一个数据库事务", zap.Error(err))
|
||||
return false, err
|
||||
}
|
||||
|
||||
updateTime := types.Now()
|
||||
newPeriod := types.NewDateRange(&form.PeriodBegin, &form.PeriodEnd)
|
||||
|
||||
udpateIndexSql, updateIndexArgs, _ := rr.ds.
|
||||
Update(goqu.T("report")).
|
||||
Set(goqu.Record{
|
||||
"period": newPeriod,
|
||||
"last_modified_at": updateTime,
|
||||
}).
|
||||
Where(goqu.I("id").Eq(rid)).
|
||||
Prepared(true).ToSQL()
|
||||
|
||||
updateSummarySql, updateSummaryArgs, _ := rr.ds.
|
||||
Update(goqu.T("report_summary")).
|
||||
Set(goqu.Record{
|
||||
"overall": model.ConsumptionUnit{Amount: form.Overall, Fee: form.OverallFee},
|
||||
"critical": model.ConsumptionUnit{Amount: form.Critical, Fee: form.CriticalFee},
|
||||
"peak": model.ConsumptionUnit{Amount: form.Peak, Fee: form.PeakFee},
|
||||
"flat": model.ConsumptionUnit{Amount: form.Flat, Fee: form.FlatFee},
|
||||
"valley": model.ConsumptionUnit{Amount: form.Valley, Fee: form.ValleyFee},
|
||||
"basic_fee": form.BasicFee,
|
||||
"adjust_fee": form.AdjustFee,
|
||||
}).
|
||||
Where(goqu.I("report_id").Eq(rid)).
|
||||
Prepared(true).ToSQL()
|
||||
|
||||
resIndex, err := tx.Exec(ctx, udpateIndexSql, updateIndexArgs...)
|
||||
if err != nil {
|
||||
rr.log.Error("更新核算报表索引时出现错误", zap.Error(err))
|
||||
tx.Rollback(ctx)
|
||||
return false, err
|
||||
}
|
||||
if resIndex.RowsAffected() == 0 {
|
||||
rr.log.Error("保存核算报表索引时出现错误", zap.Error(err))
|
||||
tx.Rollback(ctx)
|
||||
return false, exceptions.NewUnsuccessUpdateError("更新核算报表索引时出现错误")
|
||||
}
|
||||
resSummary, err := tx.Exec(ctx, updateSummarySql, updateSummaryArgs...)
|
||||
if err != nil {
|
||||
rr.log.Error("更新核算报表汇总时出现错误", zap.Error(err))
|
||||
tx.Rollback(ctx)
|
||||
return false, err
|
||||
}
|
||||
if resSummary.RowsAffected() == 0 {
|
||||
rr.log.Error("保存核算报表汇总时出现错误", zap.Error(err))
|
||||
tx.Rollback(ctx)
|
||||
return false, exceptions.NewUnsuccessUpdateError("更新核算报表汇总时出现错误")
|
||||
}
|
||||
|
||||
err = tx.Commit(ctx)
|
||||
if err != nil {
|
||||
rr.log.Error("提交核算报表更新事务时出现错误", zap.Error(err))
|
||||
tx.Rollback(ctx)
|
||||
return false, err
|
||||
}
|
||||
return resIndex.RowsAffected() > 0 && resSummary.RowsAffected() > 0, nil
|
||||
}
|
||||
|
||||
// 获取指定报表的总览信息
|
||||
func (rr _ReportRepository) RetrieveReportSummary(rid string) (*model.ReportSummary, error) {
|
||||
rr.log.Info("获取指定报表的总览信息", zap.String("Report", rid))
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
querySql, queryParams, _ := rr.ds.
|
||||
From(goqu.T("report_summary")).
|
||||
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))
|
||||
return nil, err
|
||||
}
|
||||
return &summary, nil
|
||||
}
|
||||
|
||||
// 获取指定用户的尚未发布的核算报表的计算状态
|
||||
func (rr _ReportRepository) GetReportTaskStatus(uid string) ([]*model.ReportTask, error) {
|
||||
rr.log.Info("获取指定用户的尚未发布的核算报表的计算状态", zap.String("User", uid))
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
querySql, queryParams, _ := rr.ds.
|
||||
From(goqu.T("report_task").As("t")).
|
||||
Join(goqu.T("report").As("r"), goqu.On(goqu.I("r.id").Eq(goqu.I("t.id")))).
|
||||
Join(goqu.T("park").As("p"), goqu.On(goqu.I("p.id").Eq(goqu.I("r.park_id")))).
|
||||
Select(
|
||||
goqu.I("t.*"),
|
||||
).
|
||||
Where(
|
||||
goqu.I("p.user_id").Eq(uid),
|
||||
goqu.I("r.published").IsFalse(),
|
||||
).
|
||||
Prepared(true).ToSQL()
|
||||
|
||||
var tasks []*model.ReportTask = make([]*model.ReportTask, 0)
|
||||
if err := pgxscan.Select(ctx, global.DB, &tasks, querySql, queryParams...); err != nil {
|
||||
rr.log.Error("获取指定用户的尚未发布的核算报表的计算状态时出现错误", zap.Error(err))
|
||||
return tasks, err
|
||||
}
|
||||
return tasks, nil
|
||||
}
|
||||
|
||||
// 检索指定核算报表中园区公共表计的核算记录
|
||||
func (rr _ReportRepository) ListPublicMetersInReport(rid string, page uint, keyword *string) ([]*model.ReportDetailedPublicConsumption, 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("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"),
|
||||
).
|
||||
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("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.*"))).
|
||||
Where(goqu.I("r.report_id").Eq(rid))
|
||||
|
||||
if keyword != nil && len(*keyword) > 0 {
|
||||
pattern := fmt.Sprintf("%%%s%%", *keyword)
|
||||
reportQuery = reportQuery.Where(goqu.Or(
|
||||
goqu.I("m.code").ILike(pattern),
|
||||
goqu.I("m.address").ILike(pattern),
|
||||
))
|
||||
countQuery = countQuery.Where(goqu.Or(
|
||||
goqu.I("m.code").ILike(pattern),
|
||||
goqu.I("m.address").ILike(pattern),
|
||||
))
|
||||
}
|
||||
|
||||
startRow := (page - 1) * config.ServiceSettings.ItemsPageSize
|
||||
reportQuery = reportQuery.
|
||||
Order(goqu.I("m.code").Asc()).
|
||||
Offset(startRow).Limit(config.ServiceSettings.ItemsPageSize)
|
||||
|
||||
var (
|
||||
consumptions []*model.ReportDetailedPublicConsumption = make([]*model.ReportDetailedPublicConsumption, 0)
|
||||
count int64
|
||||
)
|
||||
querySql, queryArgs, _ := reportQuery.Prepared(true).ToSQL()
|
||||
countSql, countArgs, _ := countQuery.Prepared(true).ToSQL()
|
||||
if err := pgxscan.Select(ctx, global.DB, &consumptions, querySql, queryArgs...); err != nil {
|
||||
rr.log.Error("检索指定核算报表中园区公共表计的核算记录时出现错误", zap.Error(err))
|
||||
return consumptions, 0, err
|
||||
}
|
||||
if err := pgxscan.Get(ctx, global.DB, &count, countSql, countArgs...); err != nil {
|
||||
rr.log.Error("检索指定核算报表中园区公共表计的核算记录时出现错误", zap.Error(err))
|
||||
return consumptions, 0, err
|
||||
}
|
||||
return consumptions, count, nil
|
||||
}
|
||||
|
||||
// 检索指定核算报表中公摊表计的核算记录
|
||||
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("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")))).
|
||||
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))
|
||||
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")))).
|
||||
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.*"))).
|
||||
Where(goqu.I("r.report_id").Eq(rid))
|
||||
|
||||
if keyword != nil && len(*keyword) > 0 {
|
||||
pattern := fmt.Sprintf("%%%s%%", *keyword)
|
||||
reportQuery = reportQuery.Where(goqu.Or(
|
||||
goqu.I("m.code").ILike(pattern),
|
||||
goqu.I("m.address").ILike(pattern),
|
||||
))
|
||||
countQuery = countQuery.Where(goqu.Or(
|
||||
goqu.I("m.code").ILike(pattern),
|
||||
goqu.I("m.address").ILike(pattern),
|
||||
))
|
||||
}
|
||||
|
||||
startRow := (page - 1) * config.ServiceSettings.ItemsPageSize
|
||||
reportQuery = reportQuery.
|
||||
Order(goqu.I("m.code").Asc()).
|
||||
Offset(startRow).Limit(config.ServiceSettings.ItemsPageSize)
|
||||
|
||||
var (
|
||||
consumptions []*model.ReportDetailedPooledConsumption = make([]*model.ReportDetailedPooledConsumption, 0)
|
||||
count int64
|
||||
)
|
||||
querySql, queryArgs, _ := reportQuery.Prepared(true).ToSQL()
|
||||
countSql, countArgs, _ := countQuery.Prepared(true).ToSQL()
|
||||
if err := pgxscan.Select(ctx, global.DB, &consumptions, querySql, queryArgs...); err != nil {
|
||||
rr.log.Error("检索指定核算报表中公摊表计的核算记录时出现错误", zap.Error(err))
|
||||
return consumptions, 0, err
|
||||
}
|
||||
if err := pgxscan.Get(ctx, global.DB, &count, countSql, countArgs...); err != nil {
|
||||
rr.log.Error("检索指定核算报表中公摊表计的核算记录时出现错误", zap.Error(err))
|
||||
return consumptions, 0, err
|
||||
}
|
||||
return consumptions, count, nil
|
||||
}
|
||||
|
||||
// 列出指定核算报表中指定公共表计下参与公共表计费用分摊的表计及分摊详细
|
||||
func (rr _ReportRepository) ListPooledMeterDetailInReport(rid, mid string) ([]*model.ReportDetailNestedMeterConsumption, error) {
|
||||
rr.log.Info("列出指定核算报表中指定公共表计下参与公共表计费用分摊的表计及分摊详细", zap.String("Report", rid), zap.String("Meter", mid))
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
meterSql, meterArgs, _ := rr.ds.
|
||||
From(goqu.T("report_pooled_consumption")).
|
||||
Select("*").
|
||||
Where(goqu.I("report_id").Eq(rid), goqu.I("pooled_meter_id").Eq(mid)).
|
||||
Prepared(true).ToSQL()
|
||||
|
||||
var meter model.ReportPooledConsumption
|
||||
if err := pgxscan.Get(ctx, global.DB, &meter, meterSql, meterArgs...); err != nil {
|
||||
rr.log.Error("列出指定核算报表中指定公共表计下参与公共表计费用分摊的表计编号时出现错误", zap.Error(err))
|
||||
return make([]*model.ReportDetailNestedMeterConsumption, 0), err
|
||||
}
|
||||
meterCodes := lo.Map(meter.Diluted, func(m model.NestedMeter, _ int) string {
|
||||
return m.MeterId
|
||||
})
|
||||
|
||||
meterSql, meterArgs, _ = rr.ds.
|
||||
From(goqu.T("meter_04kv").As("m")).
|
||||
LeftJoin(goqu.T("park_building").As("b"), goqu.On(goqu.I("b.id").Eq(goqu.I("m.building")))).
|
||||
Select(
|
||||
goqu.I("m.*"), goqu.I("b.name").As("building_name"),
|
||||
).
|
||||
Where(goqu.I("m.code").In(meterCodes)).
|
||||
Prepared(true).ToSQL()
|
||||
var meterDetails []*model.MeterDetail = make([]*model.MeterDetail, 0)
|
||||
if err := pgxscan.Select(ctx, global.DB, &meterDetails, meterSql, meterArgs...); err != nil {
|
||||
rr.log.Error("列出指定核算报表中指定公共表计下参与公共表计费用分摊的表计详细时出现错误", zap.Error(err))
|
||||
return make([]*model.ReportDetailNestedMeterConsumption, 0), err
|
||||
}
|
||||
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
|
||||
})
|
||||
return &model.ReportDetailNestedMeterConsumption{
|
||||
Meter: *meterDetail,
|
||||
Consumption: m,
|
||||
}
|
||||
})
|
||||
return assembled, nil
|
||||
}
|
||||
|
||||
// 列出指定核算报表下商户的简要计费信息
|
||||
func (rr _ReportRepository) ListTenementInReport(rid string, page uint, keyword *string) ([]*model.ReportTenement, int64, error) {
|
||||
rr.log.Info("查询指定核算报表下的商户简要计费信息", zap.String("Report", rid))
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
reportQuery := rr.ds.
|
||||
From(goqu.T("report_tenement").As("rt")).
|
||||
Join(goqu.T("tenement").As("t"), goqu.On(goqu.I("t.id").Eq(goqu.I("rt.tenement_id")))).
|
||||
Select("rt.*").
|
||||
Where(goqu.I("rt.report_id").Eq(rid))
|
||||
countQuery := rr.ds.
|
||||
From(goqu.T("report_tenement").As("rt")).
|
||||
Join(goqu.T("tenement").As("t"), goqu.On(goqu.I("t.id").Eq(goqu.I("rt.tenement_id")))).
|
||||
Select(goqu.COUNT(goqu.I("rt.*"))).
|
||||
Where(goqu.I("rt.report_id").Eq(rid))
|
||||
|
||||
if keyword != nil && len(*keyword) > 0 {
|
||||
pattern := fmt.Sprintf("%%%s%%", *keyword)
|
||||
reportQuery = reportQuery.Where(goqu.Or(
|
||||
goqu.I("t.full_name").ILike(pattern),
|
||||
goqu.T("t.short_name").ILike(pattern),
|
||||
goqu.I("t.abbr").ILike(pattern),
|
||||
goqu.I("t.address").ILike(pattern),
|
||||
goqu.I("t.contact_name").ILike(pattern),
|
||||
goqu.I("t.contact_phone").ILike(pattern),
|
||||
))
|
||||
countQuery = countQuery.Where(goqu.Or(
|
||||
goqu.I("t.full_name").ILike(pattern),
|
||||
goqu.T("t.short_name").ILike(pattern),
|
||||
goqu.I("t.abbr").ILike(pattern),
|
||||
goqu.I("t.address").ILike(pattern),
|
||||
goqu.I("t.contact_name").ILike(pattern),
|
||||
goqu.I("t.contact_phone").ILike(pattern),
|
||||
))
|
||||
}
|
||||
|
||||
startRow := (page - 1) * config.ServiceSettings.ItemsPageSize
|
||||
reportQuery = reportQuery.
|
||||
Order(goqu.I("t.moved_in_at").Asc()).
|
||||
Offset(startRow).Limit(config.ServiceSettings.ItemsPageSize)
|
||||
|
||||
var (
|
||||
tenements []*model.ReportTenement = make([]*model.ReportTenement, 0)
|
||||
count int64
|
||||
)
|
||||
querySql, queryArgs, _ := reportQuery.Prepared(true).ToSQL()
|
||||
countSql, countArgs, _ := countQuery.Prepared(true).ToSQL()
|
||||
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
|
||||
}
|
||||
return tenements, count, nil
|
||||
}
|
||||
|
||||
// 获取指定核算报表中指定商户的详细核算信息
|
||||
func (rr _ReportRepository) GetTenementDetailInReport(rid, tid string) (*model.ReportTenement, error) {
|
||||
rr.log.Info("获取指定核算报表中指定商户的详细核算信息", zap.String("Report", rid), zap.String("Tenement", tid))
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
querySql, queryParams, _ := rr.ds.
|
||||
From(goqu.T("report_tenement").As("rt")).
|
||||
Select("rt.*").
|
||||
Where(goqu.I("rt.report_id").Eq(rid), goqu.I("rt.tenement_id").Eq(tid)).
|
||||
Prepared(true).ToSQL()
|
||||
|
||||
var tenement model.ReportTenement
|
||||
if err := pgxscan.Get(ctx, global.DB, &tenement, querySql, queryParams...); err != nil {
|
||||
rr.log.Error("获取指定核算报表中指定商户的详细核算信息时出现错误", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
return &tenement, nil
|
||||
}
|
||||
|
||||
// 更改指定核算报表的状态为已发布
|
||||
func (rr _ReportRepository) PublishReport(rid string) (bool, error) {
|
||||
rr.log.Info("更改指定核算报表的状态为已发布", zap.String("Report", rid))
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
currentTime := types.Now()
|
||||
updateSql, updateArgs, _ := rr.ds.
|
||||
Update(goqu.T("report")).
|
||||
Set(goqu.Record{
|
||||
"published": true,
|
||||
"published_at": currentTime,
|
||||
}).
|
||||
Where(goqu.I("id").Eq(rid)).
|
||||
Prepared(true).ToSQL()
|
||||
|
||||
res, err := global.DB.Exec(ctx, updateSql, updateArgs...)
|
||||
if err != nil {
|
||||
rr.log.Error("更改指定核算报表的状态为已发布时出现错误", zap.Error(err))
|
||||
return false, err
|
||||
}
|
||||
return res.RowsAffected() > 0, nil
|
||||
}
|
||||
|
||||
// 对指定核算报表进行综合检索
|
||||
func (rr _ReportRepository) ComprehensiveReportSearch(uid, pid *string, page uint, keyword *string, start, end *types.Date) ([]*model.ReportIndex, int64, error) {
|
||||
rr.log.Info("对指定核算报表进行综合检索", zap.Stringp("User", uid), zap.Stringp("Park", pid))
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
reportQuery := rr.ds.
|
||||
From(goqu.T("report").As("r")).
|
||||
Join(goqu.T("park").As("p"), goqu.On(goqu.I("p.id").Eq(goqu.I("r.park_id")))).
|
||||
Join(goqu.T("user_detail").As("ud"), goqu.On(goqu.I("ud.id").Eq(goqu.I("p.user_id")))).
|
||||
LeftJoin(goqu.T("report_task").As("t"), goqu.On(goqu.I("t.id").Eq(goqu.I("r.id")))).
|
||||
Select("r.*", goqu.I("t.status"), goqu.I("t.message"))
|
||||
countQuery := rr.ds.
|
||||
From(goqu.T("report").As("r")).
|
||||
Join(goqu.T("park").As("p"), goqu.On(goqu.I("p.id").Eq(goqu.I("r.park_id")))).
|
||||
Join(goqu.T("user_detail").As("ud"), goqu.On(goqu.I("ud.id").Eq(goqu.I("p.user_id")))).
|
||||
LeftJoin(goqu.T("report_task").As("t"), goqu.On(goqu.I("t.id").Eq(goqu.I("r.id")))).
|
||||
Select(goqu.COUNT(goqu.I("r.*")))
|
||||
|
||||
if uid != nil && len(*uid) > 0 {
|
||||
reportQuery = reportQuery.Where(goqu.I("ud.id").Eq(*uid))
|
||||
countQuery = countQuery.Where(goqu.I("ud.id").Eq(*uid))
|
||||
}
|
||||
|
||||
if pid != nil && len(*pid) > 0 {
|
||||
reportQuery = reportQuery.Where(goqu.I("p.id").Eq(*pid))
|
||||
countQuery = countQuery.Where(goqu.I("p.id").Eq(*pid))
|
||||
}
|
||||
|
||||
queryDateRange := types.NewDateRange(start, end)
|
||||
reportQuery = reportQuery.Where(goqu.L("r.period <@ ?", queryDateRange))
|
||||
countQuery = countQuery.Where(goqu.L("r.period <@ ?", queryDateRange))
|
||||
|
||||
if keyword != nil && len(*keyword) > 0 {
|
||||
pattern := fmt.Sprintf("%%%s%%", *keyword)
|
||||
reportQuery = reportQuery.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),
|
||||
))
|
||||
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),
|
||||
))
|
||||
}
|
||||
|
||||
startRow := (page - 1) * config.ServiceSettings.ItemsPageSize
|
||||
reportQuery = reportQuery.
|
||||
Order(goqu.I("r.created_at").Desc()).
|
||||
Offset(startRow).Limit(config.ServiceSettings.ItemsPageSize)
|
||||
|
||||
var (
|
||||
reports []*model.ReportIndex = make([]*model.ReportIndex, 0)
|
||||
count int64
|
||||
)
|
||||
querySql, queryArgs, _ := reportQuery.Prepared(true).ToSQL()
|
||||
countSql, countArgs, _ := countQuery.Prepared(true).ToSQL()
|
||||
if err := pgxscan.Select(ctx, global.DB, &reports, querySql, queryArgs...); err != nil {
|
||||
rr.log.Error("对指定核算报表进行综合检索时出现错误", zap.Error(err))
|
||||
return reports, 0, err
|
||||
}
|
||||
if err := pgxscan.Get(ctx, global.DB, &count, countSql, countArgs...); err != nil {
|
||||
rr.log.Error("对指定核算报表进行综合检索总数量时出现错误", zap.Error(err))
|
||||
return reports, 0, err
|
||||
}
|
||||
return reports, count, nil
|
||||
}
|
||||
|
||||
// 判断指定报表是否是当前园区的最后一张报表
|
||||
func (rr _ReportRepository) IsLastReport(rid string) (bool, error) {
|
||||
rr.log.Info("判断指定报表是否是当前园区的最后一张报表", zap.String("Report", rid))
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
checkSql, checkArgs, _ := rr.ds.
|
||||
From(goqu.T("report").As("r")).
|
||||
Select(goqu.COUNT("*")).
|
||||
Where(
|
||||
goqu.I("r.id").Eq(rid),
|
||||
goqu.Func("lower", goqu.I("r.period")).Gte(rr.ds.
|
||||
From(goqu.T("report").As("ri")).
|
||||
Select(goqu.Func("max", goqu.Func("upper", goqu.I("ri.period")))).
|
||||
Where(
|
||||
goqu.I("ri.park_id").Eq(goqu.I("r.park_id")),
|
||||
goqu.I("ri.id").Neq(rid),
|
||||
),
|
||||
),
|
||||
goqu.I("r.published").IsTrue(),
|
||||
).
|
||||
Prepared(true).ToSQL()
|
||||
|
||||
var count int64
|
||||
if err := pgxscan.Get(ctx, global.DB, &count, checkSql, checkArgs...); err != nil {
|
||||
rr.log.Error("判断指定报表是否是当前园区的最后一张报表时出现错误", zap.Error(err))
|
||||
return false, err
|
||||
}
|
||||
return count > 0, nil
|
||||
}
|
||||
|
||||
// 申请撤回指定的核算报表
|
||||
func (rr _ReportRepository) ApplyWithdrawReport(rid string) (bool, error) {
|
||||
rr.log.Info("申请撤回指定的核算报表", zap.String("Report", rid))
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
currentTime := types.Now()
|
||||
updateSql, updateArgs, _ := rr.ds.
|
||||
Update(goqu.T("report")).
|
||||
Set(goqu.Record{
|
||||
"withdraw": model.REPORT_WITHDRAW_APPLYING,
|
||||
"last_withdraw_applied_at": currentTime,
|
||||
}).
|
||||
Where(goqu.I("id").Eq(rid)).
|
||||
Prepared(true).ToSQL()
|
||||
|
||||
res, err := global.DB.Exec(ctx, updateSql, updateArgs...)
|
||||
if err != nil {
|
||||
rr.log.Error("申请撤回指定的核算报表时出现错误", zap.Error(err))
|
||||
return false, err
|
||||
}
|
||||
return res.RowsAffected() > 0, nil
|
||||
}
|
||||
|
||||
// 批准核算报表的撤回申请
|
||||
func (rr _ReportRepository) ApproveWithdrawReport(rid string) (bool, error) {
|
||||
rr.log.Info("批准核算报表的撤回申请", zap.String("Report", rid))
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
currentTime := types.Now()
|
||||
updateSql, updateArgs, _ := rr.ds.
|
||||
Update(goqu.T("report")).
|
||||
Set(goqu.Record{
|
||||
"withdraw": model.REPORT_WITHDRAW_GRANTED,
|
||||
"last_withdraw_audit_at": currentTime,
|
||||
"published": false,
|
||||
"published_at": nil,
|
||||
}).
|
||||
Where(goqu.I("id").Eq(rid)).
|
||||
Prepared(true).ToSQL()
|
||||
|
||||
res, err := global.DB.Exec(ctx, updateSql, updateArgs...)
|
||||
if err != nil {
|
||||
rr.log.Error("批准核算报表的撤回申请时出现错误", zap.Error(err))
|
||||
return false, err
|
||||
}
|
||||
return res.RowsAffected() > 0, nil
|
||||
}
|
||||
|
||||
// 拒绝核算报表的撤回申请
|
||||
func (rr _ReportRepository) RejectWithdrawReport(rid string) (bool, error) {
|
||||
rr.log.Info("拒绝核算报表的撤回申请", zap.String("Report", rid))
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
currentTime := types.Now()
|
||||
updateSql, updateArgs, _ := rr.ds.
|
||||
Update(goqu.T("report")).
|
||||
Set(goqu.Record{
|
||||
"withdraw": model.REPORT_WITHDRAW_DENIED,
|
||||
"last_withdraw_audit_at": currentTime,
|
||||
}).
|
||||
Where(goqu.I("id").Eq(rid)).
|
||||
Prepared(true).ToSQL()
|
||||
|
||||
res, err := global.DB.Exec(ctx, updateSql, updateArgs...)
|
||||
if err != nil {
|
||||
rr.log.Error("拒绝核算报表的撤回申请时出现错误", zap.Error(err))
|
||||
return false, err
|
||||
}
|
||||
return res.RowsAffected() > 0, nil
|
||||
}
|
||||
|
||||
// 列出当前正在等待审核的已经申请撤回的核算报表
|
||||
func (rr _ReportRepository) ListWithdrawAppliedReports(page uint, keyword *string) ([]*model.ReportIndex, int64, error) {
|
||||
rr.log.Info("列出当前正在等待审核的已经申请撤回的核算报表")
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
reportQuery := rr.ds.
|
||||
From(goqu.T("report").As("r")).
|
||||
Join(goqu.T("park").As("p"), goqu.On(goqu.I("p.id").Eq(goqu.I("r.park_id")))).
|
||||
Join(goqu.T("user_detail").As("ud"), goqu.On(goqu.I("ud.id").Eq(goqu.I("p.user_id")))).
|
||||
LeftJoin(goqu.T("report_task").As("t"), goqu.On(goqu.I("t.id").Eq(goqu.I("r.id")))).
|
||||
Select("r.*", goqu.I("t.status"), goqu.I("t.message")).
|
||||
Where(goqu.I("r.withdraw").Eq(model.REPORT_WITHDRAW_APPLYING))
|
||||
countQuery := rr.ds.
|
||||
From(goqu.T("report").As("r")).
|
||||
Join(goqu.T("park").As("p"), goqu.On(goqu.I("p.id").Eq(goqu.I("r.park_id")))).
|
||||
Join(goqu.T("user_detail").As("ud"), goqu.On(goqu.I("ud.id").Eq(goqu.I("p.user_id")))).
|
||||
LeftJoin(goqu.T("report_task").As("t"), goqu.On(goqu.I("t.id").Eq(goqu.I("r.id")))).
|
||||
Select(goqu.COUNT(goqu.I("r.*"))).
|
||||
Where(goqu.I("r.withdraw").Eq(model.REPORT_WITHDRAW_APPLYING))
|
||||
|
||||
if keyword != nil && len(*keyword) > 0 {
|
||||
pattern := fmt.Sprintf("%%%s%%", *keyword)
|
||||
reportQuery = reportQuery.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),
|
||||
))
|
||||
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("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),
|
||||
))
|
||||
}
|
||||
|
||||
startRow := (page - 1) * config.ServiceSettings.ItemsPageSize
|
||||
reportQuery = reportQuery.
|
||||
Order(goqu.I("r.last_withdraw_applied_at").Desc()).
|
||||
Offset(startRow).Limit(config.ServiceSettings.ItemsPageSize)
|
||||
|
||||
var (
|
||||
reports []*model.ReportIndex = make([]*model.ReportIndex, 0)
|
||||
count int64
|
||||
)
|
||||
querySql, queryArgs, _ := reportQuery.Prepared(true).ToSQL()
|
||||
countSql, countArgs, _ := countQuery.Prepared(true).ToSQL()
|
||||
if err := pgxscan.Select(ctx, global.DB, &reports, querySql, queryArgs...); err != nil {
|
||||
rr.log.Error("列出当前正在等待审核的已经申请撤回的核算报表时出现错误", zap.Error(err))
|
||||
return reports, 0, err
|
||||
}
|
||||
if err := pgxscan.Get(ctx, global.DB, &count, countSql, countArgs...); err != nil {
|
||||
rr.log.Error("列出当前正在等待审核的已经申请撤回的核算报表总数量时出现错误", zap.Error(err))
|
||||
return reports, 0, err
|
||||
}
|
||||
return reports, count, nil
|
||||
}
|
||||
458
repository/tenement.go
Normal file
458
repository/tenement.go
Normal file
@@ -0,0 +1,458 @@
|
||||
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/tools/serial"
|
||||
"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"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type _TenementRepository struct {
|
||||
log *zap.Logger
|
||||
ds goqu.DialectWrapper
|
||||
}
|
||||
|
||||
var TenementRepository = _TenementRepository{
|
||||
log: logger.Named("Repository", "Tenement"),
|
||||
ds: goqu.Dialect("postgres"),
|
||||
}
|
||||
|
||||
// 判断指定商户是否属于指定用户的管辖
|
||||
func (tr _TenementRepository) IsTenementBelongs(tid, uid string) (bool, error) {
|
||||
tr.log.Info("检查指定商户是否属于指定企业管辖", zap.String("Tenement", tid), zap.String("Enterprise", uid))
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
countSql, countArgs, _ := tr.ds.
|
||||
From(goqu.T("tenement").As("t")).
|
||||
Join(goqu.T("park").As("p"), goqu.On(goqu.I("t.park_id").Eq(goqu.I("p.id")))).
|
||||
Select(goqu.COUNT("t.*")).
|
||||
Where(
|
||||
goqu.I("t.id").Eq(tid),
|
||||
goqu.I("p.user_id").Eq(uid),
|
||||
).
|
||||
Prepared(true).ToSQL()
|
||||
var count int
|
||||
if err := pgxscan.Get(ctx, global.DB, &count, countSql, countArgs...); err != nil {
|
||||
tr.log.Error("检查指定商户是否属于指定企业管辖失败", zap.Error(err))
|
||||
return false, err
|
||||
}
|
||||
return count > 0, nil
|
||||
}
|
||||
|
||||
// 列出指定园区中的所有商户
|
||||
func (tr _TenementRepository) ListTenements(pid string, page uint, keyword, building *string, startDate, endDate *types.Date, state int) ([]*model.Tenement, int64, error) {
|
||||
tr.log.Info(
|
||||
"检索查询指定园区中符合条件的商户",
|
||||
zap.String("Park", pid),
|
||||
zap.Uint("Page", page),
|
||||
zap.Stringp("Keyword", keyword),
|
||||
zap.Stringp("Building", building),
|
||||
logger.DateFieldp("StartDate", startDate),
|
||||
logger.DateFieldp("EndDate", endDate),
|
||||
zap.Int("State", state),
|
||||
)
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
tenementQuery := tr.ds.
|
||||
From(goqu.T("tenement").As("t")).
|
||||
LeftJoin(goqu.T("park_building").As("b"), goqu.On(goqu.I("b.id").Eq(goqu.I("t.building")))).
|
||||
Select("t.*", goqu.I("b.name").As("building_name")).
|
||||
Where(goqu.I("t.park_id").Eq(pid))
|
||||
countQuery := tr.ds.
|
||||
From(goqu.T("tenement").As("t")).
|
||||
Select(goqu.COUNT("t.*")).
|
||||
Where(goqu.I("t.park_id").Eq(pid))
|
||||
|
||||
if keyword != nil && len(*keyword) > 0 {
|
||||
pattern := fmt.Sprintf("%%%s%%", *keyword)
|
||||
tenementQuery = tenementQuery.Where(
|
||||
goqu.Or(
|
||||
goqu.I("t.full_name").ILike(pattern),
|
||||
goqu.I("t.short_name").ILike(pattern),
|
||||
goqu.I("t.abbr").ILike(pattern),
|
||||
goqu.I("t.contact_name").ILike(pattern),
|
||||
goqu.I("t.contact_phone").ILike(pattern),
|
||||
goqu.I("t.address").ILike(pattern),
|
||||
),
|
||||
)
|
||||
countQuery = countQuery.Where(
|
||||
goqu.Or(
|
||||
goqu.I("t.full_name").ILike(pattern),
|
||||
goqu.I("t.short_name").ILike(pattern),
|
||||
goqu.I("t.abbr").ILike(pattern),
|
||||
goqu.I("t.contact_name").ILike(pattern),
|
||||
goqu.I("t.contact_phone").ILike(pattern),
|
||||
goqu.I("t.address").ILike(pattern),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
if building != nil && len(*building) > 0 {
|
||||
tenementQuery = tenementQuery.Where(goqu.I("t.building").Eq(*building))
|
||||
countQuery = countQuery.Where(goqu.I("t.building").Eq(*building))
|
||||
}
|
||||
|
||||
if startDate != nil {
|
||||
tenementQuery = tenementQuery.Where(
|
||||
goqu.Or(
|
||||
goqu.I("t.moved_in_at").Gte(startDate.ToBeginningOfDate()),
|
||||
goqu.I("t.moved_out_at").Gte(startDate.ToBeginningOfDate()),
|
||||
),
|
||||
)
|
||||
countQuery = countQuery.Where(
|
||||
goqu.Or(
|
||||
goqu.I("t.moved_in_at").Gte(startDate.ToBeginningOfDate()),
|
||||
goqu.I("t.moved_out_at").Gte(startDate.ToBeginningOfDate()),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
if endDate != nil {
|
||||
tenementQuery = tenementQuery.Where(
|
||||
goqu.Or(
|
||||
goqu.I("t.moved_in_at").Lte(endDate.ToEndingOfDate()),
|
||||
goqu.I("t.moved_out_at").Lte(endDate.ToEndingOfDate()),
|
||||
),
|
||||
)
|
||||
countQuery = countQuery.Where(
|
||||
goqu.Or(
|
||||
goqu.I("t.moved_in_at").Lte(endDate.ToEndingOfDate()),
|
||||
goqu.I("t.moved_out_at").Lte(endDate.ToEndingOfDate()),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
if state == 0 {
|
||||
tenementQuery = tenementQuery.Where(
|
||||
goqu.I("t.moved_out_at").IsNull(),
|
||||
)
|
||||
countQuery = countQuery.Where(
|
||||
goqu.I("t.moved_out_at").IsNull(),
|
||||
)
|
||||
} else {
|
||||
tenementQuery = tenementQuery.Where(
|
||||
goqu.I("t.moved_out_at").IsNotNull(),
|
||||
)
|
||||
countQuery = countQuery.Where(
|
||||
goqu.I("t.moved_out_at").IsNotNull(),
|
||||
)
|
||||
}
|
||||
|
||||
startRow := (page - 1) * config.ServiceSettings.ItemsPageSize
|
||||
tenementQuery = tenementQuery.Order(goqu.I("t.created_at").Desc()).Limit(config.ServiceSettings.ItemsPageSize).Offset(startRow)
|
||||
|
||||
tenementSql, tenementArgs, _ := tenementQuery.Prepared(true).ToSQL()
|
||||
countSql, countArgs, _ := countQuery.Prepared(true).ToSQL()
|
||||
|
||||
var (
|
||||
tenements []*model.Tenement = make([]*model.Tenement, 0)
|
||||
total int64
|
||||
)
|
||||
if err := pgxscan.Select(ctx, global.DB, &tenements, tenementSql, tenementArgs...); err != nil {
|
||||
tr.log.Error("检索查询指定园区中符合条件的商户失败", zap.Error(err))
|
||||
return tenements, 0, err
|
||||
}
|
||||
if err := pgxscan.Get(ctx, global.DB, &total, countSql, countArgs...); err != nil {
|
||||
tr.log.Error("检索查询指定园区中符合条件的商户总数量失败", zap.Error(err))
|
||||
return tenements, 0, err
|
||||
}
|
||||
return tenements, total, nil
|
||||
}
|
||||
|
||||
// 查询指定园区中某一商户下的所有表计编号,不包含公摊表计
|
||||
func (tr _TenementRepository) ListMeterCodesBelongsTo(pid, tid string) ([]string, error) {
|
||||
tr.log.Info("查询指定商户下所有的表计编号", zap.String("Park", pid), zap.String("Tenement", tid))
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
sql, args, _ := tr.ds.
|
||||
From("tenement_meter").
|
||||
Select("meter_id").
|
||||
Where(
|
||||
goqu.I("park_id").Eq(pid),
|
||||
goqu.I("tenement_id").Eq(tid),
|
||||
goqu.I("disassociated_at").IsNull(),
|
||||
).
|
||||
Prepared(true).ToSQL()
|
||||
|
||||
var meterCodes []string = make([]string, 0)
|
||||
if err := pgxscan.Select(ctx, global.DB, &meterCodes, sql, args...); err != nil {
|
||||
tr.log.Error("查询指定商户下所有的表计编号失败", zap.Error(err))
|
||||
return meterCodes, err
|
||||
}
|
||||
return meterCodes, nil
|
||||
}
|
||||
|
||||
// 在指定园区中创建一个新的商户
|
||||
func (tr _TenementRepository) AddTenement(tx pgx.Tx, ctx context.Context, pid string, tenement *vo.TenementCreationForm) error {
|
||||
tr.log.Info("在指定园区中创建一个新的商户", zap.String("Park", pid))
|
||||
|
||||
serial.StringSerialRequestChan <- 1
|
||||
tenementId := serial.Prefix("T", <-serial.StringSerialResponseChan)
|
||||
currentTime := types.Now()
|
||||
if _, err := tx.Exec(
|
||||
ctx,
|
||||
"INSERT INTO tenement (id, park_id, full_name, short_name, abbr, address, contact_name, contact_phone, building, on_floor, invoice_info, moved_in_at, created_at, last_modified_at) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)",
|
||||
[]interface{}{
|
||||
tenementId,
|
||||
pid,
|
||||
tenement.Name,
|
||||
tenement.ShortName,
|
||||
tools.PinyinAbbr(tenement.Name),
|
||||
tenement.Address,
|
||||
tenement.Contact,
|
||||
tenement.Phone,
|
||||
tenement.Building,
|
||||
tenement.OnFloor,
|
||||
&model.InvoiceTitle{
|
||||
Name: tenement.Name,
|
||||
USCI: tenement.USCI,
|
||||
Address: tools.DefaultOrEmptyStr(tenement.InvoiceAddress, ""),
|
||||
Phone: tools.DefaultOrEmptyStr(tenement.InvoicePhone, ""),
|
||||
Bank: tools.DefaultOrEmptyStr(tenement.Bank, ""),
|
||||
Account: tools.DefaultOrEmptyStr(tenement.Account, ""),
|
||||
},
|
||||
currentTime,
|
||||
currentTime,
|
||||
currentTime,
|
||||
}...,
|
||||
); err != nil {
|
||||
tr.log.Error("在指定园区中创建一个新的商户失败", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 向园区中指定商户下绑定一个新的表计
|
||||
func (tr _TenementRepository) BindMeter(tx pgx.Tx, ctx context.Context, pid, tid, meter string) error {
|
||||
tr.log.Info("向园区中指定商户下绑定一个新的表计", zap.String("Park", pid), zap.String("Tenement", tid), zap.String("Meter", meter))
|
||||
|
||||
createSql, createArgs, _ := tr.ds.
|
||||
Insert("tenement_meter").
|
||||
Cols(
|
||||
"park_id", "tenement_id", "meter_id", "associated_at",
|
||||
).
|
||||
Vals(
|
||||
goqu.Vals{
|
||||
pid,
|
||||
tid,
|
||||
meter,
|
||||
types.Now(),
|
||||
},
|
||||
).
|
||||
Prepared(true).ToSQL()
|
||||
if _, err := tx.Exec(ctx, createSql, createArgs...); err != nil {
|
||||
tr.log.Error("向园区中指定商户下绑定一个新的表计失败", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 将指定商户与指定表计解绑
|
||||
func (tr _TenementRepository) UnbindMeter(tx pgx.Tx, ctx context.Context, pid, tid, meter string) error {
|
||||
tr.log.Info("将指定商户与指定表计解绑", zap.String("Park", pid), zap.String("Tenement", tid), zap.String("Meter", meter))
|
||||
|
||||
updateSql, updateArgs, _ := tr.ds.
|
||||
Update("tenement_meter").
|
||||
Set(
|
||||
goqu.Record{
|
||||
"disassociated_at": types.Now(),
|
||||
},
|
||||
).
|
||||
Where(
|
||||
goqu.I("park_id").Eq(pid),
|
||||
goqu.I("tenement_id").Eq(tid),
|
||||
goqu.I("meter_id").Eq(meter),
|
||||
).
|
||||
Prepared(true).ToSQL()
|
||||
if _, err := tx.Exec(ctx, updateSql, updateArgs...); err != nil {
|
||||
tr.log.Error("将指定商户与指定表计解绑失败", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 修改指定商户的信息
|
||||
func (tr _TenementRepository) UpdateTenement(pid, tid string, tenement *vo.TenementCreationForm) error {
|
||||
tr.log.Info("修改指定商户的信息", zap.String("Park", pid), zap.String("Tenement", tid))
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
updateSql, updateArgs, _ := tr.ds.
|
||||
Update("tenement").
|
||||
Set(
|
||||
goqu.Record{
|
||||
"full_name": tenement.Name,
|
||||
"short_name": tenement.ShortName,
|
||||
"abbr": tools.PinyinAbbr(tenement.Name),
|
||||
"address": tenement.Address,
|
||||
"contact_name": tenement.Contact,
|
||||
"contact_phone": tenement.Phone,
|
||||
"building": tenement.Building,
|
||||
"on_floor": tenement.OnFloor,
|
||||
"invoice_info": &model.InvoiceTitle{
|
||||
Name: tenement.Name,
|
||||
USCI: tenement.USCI,
|
||||
Address: tools.DefaultOrEmptyStr(tenement.InvoiceAddress, ""),
|
||||
Phone: tools.DefaultOrEmptyStr(tenement.InvoicePhone, ""),
|
||||
Bank: tools.DefaultOrEmptyStr(tenement.Bank, ""),
|
||||
Account: tools.DefaultOrEmptyStr(tenement.Account, ""),
|
||||
},
|
||||
"last_modified_at": types.Now(),
|
||||
},
|
||||
).
|
||||
Where(
|
||||
goqu.I("id").Eq(tid),
|
||||
goqu.I("park_id").Eq(pid),
|
||||
).
|
||||
Prepared(true).ToSQL()
|
||||
if _, err := global.DB.Exec(ctx, updateSql, updateArgs...); err != nil {
|
||||
tr.log.Error("修改指定商户的信息失败", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 迁出指定商户
|
||||
func (tr _TenementRepository) MoveOut(tx pgx.Tx, ctx context.Context, pid, tid string) error {
|
||||
tr.log.Info("迁出指定商户", zap.String("Park", pid), zap.String("Tenement", tid))
|
||||
|
||||
updateSql, updateArgs, _ := tr.ds.
|
||||
Update("tenement").
|
||||
Set(
|
||||
goqu.Record{
|
||||
"moved_out_at": types.Now(),
|
||||
},
|
||||
).
|
||||
Where(
|
||||
goqu.I("id").Eq(tid),
|
||||
goqu.I("park_id").Eq(pid),
|
||||
).
|
||||
Prepared(true).ToSQL()
|
||||
if _, err := tx.Exec(ctx, updateSql, updateArgs...); err != nil {
|
||||
tr.log.Error("迁出指定商户失败", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 列出用于下拉列表的符合指定条件的商户信息
|
||||
func (tr _TenementRepository) ListForSelect(uid string, pid, keyword *string, limit *uint) ([]*model.Tenement, error) {
|
||||
tr.log.Info("列出用于下拉列表的符合指定条件的商户信息", zap.String("Ent", uid), zap.String("Park", tools.DefaultOrEmptyStr(pid, "All")), zap.Stringp("Keyword", keyword), zap.Uintp("Limit", limit))
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
tenementQuery := tr.ds.
|
||||
From(goqu.T("tenement").As("t")).
|
||||
LeftJoin(goqu.T("park_building").As("b"), goqu.On(goqu.I("b.id").Eq(goqu.I("t.building")))).
|
||||
Join(goqu.T("park").As("p"), goqu.On(goqu.I("p.id").Eq(goqu.I("t.park_id")))).
|
||||
Select(
|
||||
"t.*", goqu.I("b.name").As("building_name"),
|
||||
).
|
||||
Where(
|
||||
goqu.I("p.user_id").Eq(uid),
|
||||
goqu.I("t.moved_out_at").IsNull(),
|
||||
)
|
||||
|
||||
if pid != nil && len(*pid) > 0 {
|
||||
tenementQuery = tenementQuery.Where(goqu.I("p.id").Eq(*pid))
|
||||
}
|
||||
|
||||
if keyword != nil && len(*keyword) > 0 {
|
||||
pattern := fmt.Sprintf("%%%s%%", *keyword)
|
||||
tenementQuery = tenementQuery.Where(
|
||||
goqu.Or(
|
||||
goqu.I("t.full_name").ILike(pattern),
|
||||
goqu.I("t.short_name").ILike(pattern),
|
||||
goqu.I("t.abbr").ILike(pattern),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
tenementQuery = tenementQuery.Order(goqu.I("t.created_at").Desc())
|
||||
|
||||
if limit != nil && *limit > 0 {
|
||||
tenementQuery = tenementQuery.Limit(*limit)
|
||||
}
|
||||
|
||||
tenementSql, tenementArgs, _ := tenementQuery.Prepared(true).ToSQL()
|
||||
|
||||
var tenements = make([]*model.Tenement, 0)
|
||||
if err := pgxscan.Select(ctx, global.DB, &tenements, tenementSql, tenementArgs...); err != nil {
|
||||
tr.log.Error("列出用于下拉列表的符合指定条件的商户信息失败", zap.Error(err))
|
||||
return tenements, err
|
||||
}
|
||||
return tenements, nil
|
||||
}
|
||||
|
||||
// 列出指定园区中在指定时间区间内存在过入住的商户
|
||||
func (tr _TenementRepository) ListTenementsInTimeRange(pid string, start, end types.Date) ([]*model.Tenement, error) {
|
||||
tr.log.Info("列出指定园区中在指定时间区间内存在过入住的商户", zap.String("Park", pid), logger.DateField("Start", start), logger.DateField("End", end))
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
tenementQuery := tr.ds.
|
||||
From(goqu.T("tenement").As("t")).
|
||||
LeftJoin(goqu.T("park_building").As("b"), goqu.On(goqu.I("b.id").Eq(goqu.I("t.building")))).
|
||||
Select(
|
||||
"t.*", goqu.I("b.name").As("building_name"),
|
||||
).
|
||||
Where(
|
||||
goqu.I("t.park_id").Eq(pid),
|
||||
goqu.I("t.moved_in_at").Lte(end.ToEndingOfDate()),
|
||||
goqu.Or(
|
||||
goqu.I("t.moved_out_at").IsNull(),
|
||||
goqu.I("t.moved_out_at").Gte(start.ToBeginningOfDate()),
|
||||
),
|
||||
).
|
||||
Order(goqu.I("t.created_at").Desc())
|
||||
|
||||
tenementSql, tenementArgs, _ := tenementQuery.Prepared(true).ToSQL()
|
||||
|
||||
var tenements = make([]*model.Tenement, 0)
|
||||
if err := pgxscan.Select(ctx, global.DB, &tenements, tenementSql, tenementArgs...); err != nil {
|
||||
tr.log.Error("列出指定园区中在指定时间区间内存在过入住的商户失败", zap.Error(err))
|
||||
return tenements, err
|
||||
}
|
||||
|
||||
return tenements, nil
|
||||
}
|
||||
|
||||
// 获取指定园区中指定商户的详细信息
|
||||
func (tr _TenementRepository) RetrieveTenementDetail(pid, tid string) (*model.Tenement, error) {
|
||||
tr.log.Info("获取指定园区中指定商户的详细信息", zap.String("Park", pid), zap.String("Tenement", tid))
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
tenementSql, tenementArgs, _ := tr.ds.
|
||||
From(goqu.T("tenement").As("t")).
|
||||
LeftJoin(goqu.T("park_building").As("b"), goqu.On(goqu.I("b.id").Eq(goqu.I("t.building")))).
|
||||
Select(
|
||||
"t.*", goqu.I("b.name").As("building_name"),
|
||||
).
|
||||
Where(
|
||||
goqu.I("t.id").Eq(tid),
|
||||
goqu.I("t.park_id").Eq(pid),
|
||||
).
|
||||
Prepared(true).ToSQL()
|
||||
var tenement model.Tenement
|
||||
if err := pgxscan.Get(ctx, global.DB, &tenement, tenementSql, tenementArgs...); err != nil {
|
||||
tr.log.Error("获取指定园区中指定商户的详细信息失败", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
return &tenement, nil
|
||||
}
|
||||
166
repository/top_up.go
Normal file
166
repository/top_up.go
Normal file
@@ -0,0 +1,166 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"electricity_bill_calc/config"
|
||||
"electricity_bill_calc/global"
|
||||
"electricity_bill_calc/logger"
|
||||
"electricity_bill_calc/model"
|
||||
"electricity_bill_calc/tools/serial"
|
||||
"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"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type _TopUpRepository struct {
|
||||
log *zap.Logger
|
||||
ds goqu.DialectWrapper
|
||||
}
|
||||
|
||||
var TopUpRepository = _TopUpRepository{
|
||||
log: logger.Named("Repository", "TopUp"),
|
||||
ds: goqu.Dialect("postgres"),
|
||||
}
|
||||
|
||||
// 检索符合条件的商户充值记录
|
||||
func (tur _TopUpRepository) ListTopUps(pid string, startDate, endDate *types.Date, keyword *string, page uint) ([]*model.TopUp, int64, error) {
|
||||
tur.log.Info("查询符合条件的商户充值记录", zap.String("Park", pid), logger.DateFieldp("StartDate", startDate), logger.DateFieldp("EndDate", endDate), zap.Stringp("keyword", keyword), zap.Uint("page", page))
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
topUpQuery := tur.ds.
|
||||
From(goqu.T("tenement_top_ups").As("t")).
|
||||
LeftJoin(goqu.T("tenement").As("te"), goqu.On(goqu.I("te.id").Eq(goqu.I("t.tenement_id")), goqu.I("te.park_id").Eq(goqu.I("t.park_id")))).
|
||||
LeftJoin(goqu.T("meter_04kv").As("m"), goqu.On(goqu.I("m.code").Eq(goqu.I("t.meter_id")), goqu.I("m.park_id").Eq(goqu.I("t.park_id")))).
|
||||
Select("t.*", goqu.I("te.full_name").As("tenement_name"), goqu.I("m.address").As("meter_address")).
|
||||
Where(goqu.I("t.park_id").Eq(pid))
|
||||
countQuery := tur.ds.
|
||||
From(goqu.T("tenement_top_ups").As("t")).
|
||||
LeftJoin(goqu.T("tenement").As("te"), goqu.On(goqu.I("te.id").Eq(goqu.I("t.tenement_id")), goqu.I("te.park_id").Eq(goqu.I("t.park_id")))).
|
||||
LeftJoin(goqu.T("meter_04kv").As("m"), goqu.On(goqu.I("m.code").Eq(goqu.I("t.meter_id")), goqu.I("m.park_id").Eq(goqu.I("t.park_id")))).
|
||||
Select(goqu.COUNT("t.*")).
|
||||
Where(goqu.I("t.park_id").Eq(pid))
|
||||
|
||||
if keyword != nil && len(*keyword) > 0 {
|
||||
pattern := fmt.Sprintf("%%%s%%", *keyword)
|
||||
topUpQuery = topUpQuery.Where(goqu.Or(
|
||||
goqu.I("te.full_name").ILike(pattern),
|
||||
goqu.I("te.short_name").ILike(pattern),
|
||||
goqu.I("te.abbr").ILike(pattern),
|
||||
goqu.I("m.code").ILike(pattern),
|
||||
goqu.I("m.address").ILike(pattern),
|
||||
))
|
||||
countQuery = countQuery.Where(goqu.Or(
|
||||
goqu.I("te.full_name").ILike(pattern),
|
||||
goqu.I("te.short_name").ILike(pattern),
|
||||
goqu.I("te.abbr").ILike(pattern),
|
||||
goqu.I("m.code").ILike(pattern),
|
||||
goqu.I("m.address").ILike(pattern),
|
||||
))
|
||||
}
|
||||
|
||||
if startDate != nil {
|
||||
topUpQuery = topUpQuery.Where(goqu.I("t.topped_up_at").Gte(startDate.ToBeginningOfDate()))
|
||||
countQuery = countQuery.Where(goqu.I("t.topped_up_at").Gte(startDate.ToBeginningOfDate()))
|
||||
}
|
||||
|
||||
if endDate != nil {
|
||||
topUpQuery = topUpQuery.Where(goqu.I("t.topped_up_at").Lte(endDate.ToEndingOfDate()))
|
||||
countQuery = countQuery.Where(goqu.I("t.topped_up_at").Lte(endDate.ToEndingOfDate()))
|
||||
}
|
||||
|
||||
startRow := (page - 1) * config.ServiceSettings.ItemsPageSize
|
||||
topUpQuery = topUpQuery.Order(goqu.I("t.topped_up_at").Desc()).Offset(startRow).Limit(config.ServiceSettings.ItemsPageSize)
|
||||
|
||||
topUpSql, topUpArgs, _ := topUpQuery.Prepared(true).ToSQL()
|
||||
countSql, countArgs, _ := countQuery.Prepared(true).ToSQL()
|
||||
|
||||
var (
|
||||
topUps []*model.TopUp = make([]*model.TopUp, 0)
|
||||
total int64 = 0
|
||||
)
|
||||
if err := pgxscan.Select(ctx, global.DB, &topUps, topUpSql, topUpArgs...); err != nil {
|
||||
tur.log.Error("查询商户充值记录失败", zap.Error(err))
|
||||
return topUps, 0, err
|
||||
}
|
||||
if err := pgxscan.Get(ctx, global.DB, &total, countSql, countArgs...); err != nil {
|
||||
tur.log.Error("查询商户充值记录总数失败", zap.Error(err))
|
||||
return topUps, 0, err
|
||||
}
|
||||
return topUps, total, nil
|
||||
}
|
||||
|
||||
// 取得一个充值记录的详细信息
|
||||
func (tur _TopUpRepository) GetTopUp(pid, topUpCode string) (*model.TopUp, error) {
|
||||
tur.log.Info("查询充值记录", zap.String("Park", pid), zap.String("TopUpCode", topUpCode))
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
topUpSql, topUpArgs, _ := tur.ds.
|
||||
From(goqu.T("tenement_top_ups").As("t")).
|
||||
LeftJoin(goqu.T("tenement").As("te"), goqu.On(goqu.I("te.id").Eq(goqu.I("t.tenement_id")), goqu.I("te.park_id").Eq(goqu.I("t.park_id")))).
|
||||
LeftJoin(goqu.T("meter").As("m"), goqu.On(goqu.I("m.code").Eq(goqu.I("t.meter_id")), goqu.I("m.park_id").Eq(goqu.I("t.park_id")))).
|
||||
Select("t.*", goqu.I("te.full_name").As("tenement_name"), goqu.I("m.address").As("meter_address")).
|
||||
Where(goqu.I("t.park_id").Eq(pid), goqu.I("t.top_up_code").Eq(topUpCode)).
|
||||
Prepared(true).ToSQL()
|
||||
|
||||
var topUp model.TopUp
|
||||
if err := pgxscan.Get(ctx, global.DB, &topUp, topUpSql, topUpArgs...); err != nil {
|
||||
tur.log.Error("查询充值记录失败", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
return &topUp, nil
|
||||
}
|
||||
|
||||
// 创建一条新的充值记录
|
||||
func (tur _TopUpRepository) CreateTopUp(pid string, form *vo.TopUpCreationForm) error {
|
||||
tur.log.Info("创建一条新的充值记录", zap.String("Park", pid), zap.String("Tenement", form.Tenement), zap.String("Meter", form.Meter))
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
serial.StringSerialRequestChan <- 1
|
||||
topUpCode := serial.Prefix("U", <-serial.StringSerialResponseChan)
|
||||
topUpTime := types.Now()
|
||||
topUpSql, topUpArgs, _ := tur.ds.
|
||||
Insert("tenement_top_ups").
|
||||
Cols("top_up_code", "park_id", "tenement_id", "meter_id", "topped_up_at", "amount", "payment_type").
|
||||
Vals(goqu.Vals{
|
||||
topUpCode,
|
||||
pid,
|
||||
form.Tenement,
|
||||
form.Meter,
|
||||
topUpTime,
|
||||
form.Amount,
|
||||
model.PAYMENT_CASH,
|
||||
}).
|
||||
Prepared(true).ToSQL()
|
||||
|
||||
if _, err := global.DB.Exec(ctx, topUpSql, topUpArgs...); err != nil {
|
||||
tur.log.Error("创建充值记录失败", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 删除一条充值记录
|
||||
func (tur _TopUpRepository) DeleteTopUp(pid, topUpCode string) error {
|
||||
tur.log.Info("删除一条充值记录", zap.String("Park", pid), zap.String("TopUpCode", topUpCode))
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
topUpSql, topUpArgs, _ := tur.ds.
|
||||
Update("tenement_top_ups").
|
||||
Set(goqu.Record{"cancelled_at": types.Now()}).
|
||||
Where(goqu.I("park_id").Eq(pid), goqu.I("top_up_code").Eq(topUpCode)).
|
||||
Prepared(true).ToSQL()
|
||||
|
||||
if _, err := global.DB.Exec(ctx, topUpSql, topUpArgs...); err != nil {
|
||||
tur.log.Error("删除充值记录失败", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
419
repository/user.go
Normal file
419
repository/user.go
Normal file
@@ -0,0 +1,419 @@
|
||||
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/types"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/doug-martin/goqu/v9"
|
||||
_ "github.com/doug-martin/goqu/v9/dialect/postgres"
|
||||
"github.com/fufuok/utils/xhash"
|
||||
"github.com/georgysavva/scany/v2/pgxscan"
|
||||
"github.com/jackc/pgx/v5"
|
||||
"github.com/samber/lo"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type _UserRepository struct {
|
||||
log *zap.Logger
|
||||
ds goqu.DialectWrapper
|
||||
}
|
||||
|
||||
var UserRepository = _UserRepository{
|
||||
log: logger.Named("Repository", "User"),
|
||||
ds: goqu.Dialect("postgres"),
|
||||
}
|
||||
|
||||
// 使用用户名查询指定用户的基本信息
|
||||
func (ur _UserRepository) FindUserByUsername(username string) (*model.User, error) {
|
||||
ur.log.Info("根据用户名查询指定用户的基本信息。", zap.String("username", username))
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
var user model.User
|
||||
sql, params, _ := ur.ds.From("user").Where(goqu.Ex{"username": username}).Prepared(true).ToSQL()
|
||||
if err := pgxscan.Get(ctx, global.DB, &user, sql, params...); err != nil {
|
||||
ur.log.Error("从数据库查询指定用户名的用户基本信息失败。", zap.String("username", username), zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
// 使用用户唯一编号查询指定用户的基本信息
|
||||
func (ur _UserRepository) FindUserById(uid string) (*model.User, error) {
|
||||
ur.log.Info("根据用户唯一编号查询指定用户的基本信息。", zap.String("user id", uid))
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
var user model.User
|
||||
sql, params, _ := ur.ds.From("user").Where(goqu.Ex{"id": uid}).Prepared(true).ToSQL()
|
||||
if err := pgxscan.Get(ctx, global.DB, &user, sql, params...); err != nil {
|
||||
ur.log.Error("从数据库查询指定用户唯一编号的用户基本信息失败。", zap.String("user id", uid), zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
// 使用用户的唯一编号获取用户的详细信息
|
||||
func (ur _UserRepository) FindUserDetailById(uid string) (*model.UserDetail, error) {
|
||||
ur.log.Info("根据用户唯一编号查询指定用户的详细信息。", zap.String("user id", uid))
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
var user model.UserDetail
|
||||
sql, params, _ := ur.ds.From("user_detail").Where(goqu.Ex{"id": uid}).Prepared(true).ToSQL()
|
||||
if err := pgxscan.Get(ctx, global.DB, &user, sql, params...); err != nil {
|
||||
ur.log.Error("从数据库查询指定用户唯一编号的用户详细信息失败。", zap.String("user id", uid), zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
// 使用用户唯一编号获取用户的综合详细信息
|
||||
func (ur _UserRepository) FindUserInformation(uid string) (*model.UserWithDetail, error) {
|
||||
ur.log.Info("根据用户唯一编号查询用户的综合详细信息", zap.String("user id", uid))
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
var user model.UserWithDetail
|
||||
sql, params, _ := ur.ds.
|
||||
From("user").As("u").
|
||||
Join(
|
||||
goqu.T("user_detail").As("ud"),
|
||||
goqu.On(goqu.Ex{
|
||||
"ud.id": goqu.I("u.id"),
|
||||
})).
|
||||
Select(
|
||||
"u.id", "u.username", "u.reset_needed", "u.type", "u.enabled",
|
||||
"ud.name", "ud.abbr", "ud.region", "ud.address", "ud.contact", "ud.phone",
|
||||
"ud.unit_service_fee", "ud.service_expiration",
|
||||
"ud.created_at", "ud.created_by", "ud.last_modified_at", "ud.last_modified_by").
|
||||
Where(goqu.Ex{"u.id": uid}).
|
||||
Prepared(true).ToSQL()
|
||||
if err := pgxscan.Get(
|
||||
ctx, global.DB, &user, sql, params...); err != nil {
|
||||
ur.log.Error("从数据库查询指定用户唯一编号的用户详细信息失败。", zap.String("user id", uid), zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
// 检查指定用户唯一编号是否存在对应的用户
|
||||
func (ur _UserRepository) IsUserExists(uid string) (bool, error) {
|
||||
ur.log.Info("检查指定用户唯一编号是否存在对应的用户。", zap.String("user id", uid))
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
var userCount int
|
||||
sql, params, _ := ur.ds.From("user").Select(goqu.COUNT("*")).Where(goqu.Ex{"id": uid}).Prepared(true).ToSQL()
|
||||
if err := pgxscan.Get(ctx, global.DB, &userCount, sql, params...); err != nil {
|
||||
ur.log.Error("从数据库查询指定用户唯一编号的用户基本信息失败。", zap.String("user id", uid), zap.Error(err))
|
||||
return false, err
|
||||
}
|
||||
return userCount > 0, nil
|
||||
}
|
||||
|
||||
// 检查指定用户名在数据库中是否已经存在
|
||||
func (ur _UserRepository) IsUsernameExists(username string) (bool, error) {
|
||||
ur.log.Info("检查指定用户名在数据库中是否已经存在。", zap.String("username", username))
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
var userCount int
|
||||
sql, params, _ := ur.ds.From("user").Select(goqu.COUNT("*")).Where(goqu.Ex{"username": username}).Prepared(true).ToSQL()
|
||||
if err := pgxscan.Get(ctx, global.DB, &userCount, sql, params...); err != nil {
|
||||
ur.log.Error("从数据库查询指定用户名的用户基本信息失败。", zap.String("username", username), zap.Error(err))
|
||||
return false, err
|
||||
}
|
||||
return userCount > 0, nil
|
||||
}
|
||||
|
||||
// 创建一个新用户
|
||||
func (ur _UserRepository) CreateUser(user model.User, detail model.UserDetail, operator *string) (bool, error) {
|
||||
ur.log.Info("创建一个新用户。", zap.String("username", user.Username))
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
tx, err := global.DB.Begin(ctx)
|
||||
if err != nil {
|
||||
ur.log.Error("启动数据库事务失败。", zap.Error(err))
|
||||
return false, err
|
||||
}
|
||||
|
||||
createdTime := types.Now()
|
||||
userSql, userParams, _ := ur.ds.
|
||||
Insert("user").
|
||||
Rows(
|
||||
goqu.Record{
|
||||
"id": user.Id, "username": user.Username, "password": user.Password,
|
||||
"reset_needed": user.ResetNeeded, "type": user.UserType, "enabled": user.Enabled,
|
||||
"created_at": createdTime,
|
||||
},
|
||||
).
|
||||
Prepared(true).ToSQL()
|
||||
userResult, err := tx.Exec(ctx, userSql, userParams...)
|
||||
if err != nil {
|
||||
ur.log.Error("向数据库插入新用户基本信息失败。", zap.Error(err))
|
||||
tx.Rollback(ctx)
|
||||
return false, err
|
||||
}
|
||||
userDetailSql, userDetailParams, _ := ur.ds.
|
||||
Insert("user_detail").
|
||||
Rows(
|
||||
goqu.Record{
|
||||
"id": user.Id, "name": detail.Name, "abbr": tools.PinyinAbbr(*detail.Name), "region": detail.Region,
|
||||
"address": detail.Address, "contact": detail.Contact, "phone": detail.Phone,
|
||||
"unit_service_fee": detail.UnitServiceFee, "service_expiration": detail.ServiceExpiration,
|
||||
"created_at": createdTime, "created_by": operator,
|
||||
"last_modified_at": createdTime, "last_modified_by": operator,
|
||||
},
|
||||
).
|
||||
Prepared(true).ToSQL()
|
||||
detailResult, err := tx.Exec(ctx, userDetailSql, userDetailParams...)
|
||||
if err != nil {
|
||||
ur.log.Error("向数据库插入新用户详细信息失败。", zap.Error(err))
|
||||
tx.Rollback(ctx)
|
||||
return false, err
|
||||
}
|
||||
|
||||
err = tx.Commit(ctx)
|
||||
if err != nil {
|
||||
ur.log.Error("提交数据库事务失败。", zap.Error(err))
|
||||
tx.Rollback(ctx)
|
||||
return false, err
|
||||
}
|
||||
return userResult.RowsAffected() > 0 && detailResult.RowsAffected() > 0, nil
|
||||
}
|
||||
|
||||
// 根据给定的条件检索用户
|
||||
func (ur _UserRepository) FindUser(keyword *string, userType int16, state *bool, page uint) ([]*model.UserWithDetail, int64, error) {
|
||||
ur.log.Info("根据给定的条件检索用户。", zap.Uint("page", page), zap.Stringp("keyword", keyword), zap.Int16("user type", userType), zap.Boolp("state", state))
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
var (
|
||||
userWithDetails []*model.UserWithDetail
|
||||
userCount int64
|
||||
)
|
||||
userQuery := ur.ds.
|
||||
From(goqu.T("user").As("u")).
|
||||
Join(goqu.T("user_detail").As("ud"), goqu.On(goqu.Ex{"ud.id": goqu.I("u.id")})).
|
||||
Select(
|
||||
"u.id", "u.username", "u.reset_needed", "u.type", "u.enabled",
|
||||
"ud.name", "ud.abbr", "ud.region", "ud.address", "ud.contact", "ud.phone",
|
||||
"ud.unit_service_fee", "ud.service_expiration",
|
||||
"ud.created_at", "ud.created_by", "ud.last_modified_at", "ud.last_modified_by",
|
||||
)
|
||||
countQuery := ur.ds.
|
||||
From(goqu.T("user").As("u")).
|
||||
Join(goqu.T("user_detail").As("ud"), goqu.On(goqu.Ex{"ud.id": goqu.I("u.id")})).
|
||||
Select(goqu.COUNT("*"))
|
||||
|
||||
if keyword != nil && len(*keyword) > 0 {
|
||||
pattern := fmt.Sprintf("%%%s%%", *keyword)
|
||||
userQuery = userQuery.Where(
|
||||
goqu.Or(
|
||||
goqu.I("u.username").ILike(pattern),
|
||||
goqu.I("ud.name").ILike(pattern),
|
||||
goqu.I("ud.abbr").ILike(pattern),
|
||||
),
|
||||
)
|
||||
countQuery = countQuery.Where(
|
||||
goqu.Or(
|
||||
goqu.I("u.username").ILike(pattern),
|
||||
goqu.I("ud.name").ILike(pattern),
|
||||
goqu.I("ud.abbr").ILike(pattern),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
if userType != -1 {
|
||||
userQuery = userQuery.Where(goqu.Ex{"u.type": userType})
|
||||
countQuery = countQuery.Where(goqu.Ex{"u.type": userType})
|
||||
}
|
||||
|
||||
if state != nil {
|
||||
userQuery = userQuery.Where(goqu.Ex{"u.enabled": state})
|
||||
countQuery = countQuery.Where(goqu.Ex{"u.enabled": state})
|
||||
}
|
||||
|
||||
userQuery.Order(goqu.I("u.created_at").Desc())
|
||||
|
||||
currentPosition := (page - 1) * config.ServiceSettings.ItemsPageSize
|
||||
userQuery = userQuery.Offset(currentPosition).Limit(config.ServiceSettings.ItemsPageSize)
|
||||
|
||||
userSql, userParams, _ := userQuery.Prepared(true).ToSQL()
|
||||
countSql, countParams, _ := countQuery.Prepared(true).ToSQL()
|
||||
if err := pgxscan.Select(ctx, global.DB, &userWithDetails, userSql, userParams...); err != nil {
|
||||
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 {
|
||||
ur.log.Error("从数据库查询用户列表总数失败。", zap.Error(err))
|
||||
return make([]*model.UserWithDetail, 0), 0, err
|
||||
}
|
||||
return userWithDetails, userCount, nil
|
||||
}
|
||||
|
||||
// 更新指定用户的详细信息
|
||||
func (ur _UserRepository) UpdateDetail(uid string, userDetail model.UserModificationForm, operator *string) (bool, error) {
|
||||
ur.log.Info("更新指定用户的详细信息。", zap.String("user id", uid))
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
updates := goqu.Record{
|
||||
"name": userDetail.Name, "abbr": tools.PinyinAbbr(userDetail.Name), "region": userDetail.Region,
|
||||
"address": userDetail.Address, "contact": userDetail.Contact, "phone": userDetail.Phone,
|
||||
"last_modified_at": types.Now(), "last_modified_by": operator,
|
||||
}
|
||||
if userDetail.UnitServiceFee != nil {
|
||||
updates = lo.Assign(updates, goqu.Record{"unit_service_fee": userDetail.UnitServiceFee})
|
||||
}
|
||||
|
||||
userDetailUpdateQuery := ur.ds.
|
||||
Update("user_detail").
|
||||
Set(updates).
|
||||
Where(goqu.Ex{"id": uid})
|
||||
|
||||
userDetailSql, userDetailParams, _ := userDetailUpdateQuery.
|
||||
Prepared(true).ToSQL()
|
||||
|
||||
if res, err := global.DB.Exec(ctx, userDetailSql, userDetailParams...); err != nil {
|
||||
ur.log.Error("向数据库更新指定用户的详细信息失败。", zap.String("user id", uid), zap.Error(err))
|
||||
return false, err
|
||||
} else {
|
||||
return res.RowsAffected() > 0, nil
|
||||
}
|
||||
}
|
||||
|
||||
// 更新指定用户的登录凭据
|
||||
func (ur _UserRepository) UpdatePassword(uid, newCredential string, needReset bool) (bool, error) {
|
||||
ur.log.Info("更新指定用户的登录凭据。", zap.String("user id", uid))
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
userUpdateQuery := ur.ds.
|
||||
Update("user").
|
||||
Set(goqu.Record{"password": xhash.Sha512Hex(newCredential), "reset_needed": needReset}).
|
||||
Where(goqu.Ex{"id": uid})
|
||||
|
||||
userSql, userParams, _ := userUpdateQuery.
|
||||
Prepared(true).ToSQL()
|
||||
|
||||
if res, err := global.DB.Exec(ctx, userSql, userParams...); err != nil {
|
||||
ur.log.Error("向数据库更新指定用户的登录凭据失败。", zap.String("user id", uid), zap.Error(err))
|
||||
return false, err
|
||||
} else {
|
||||
return res.RowsAffected() > 0, nil
|
||||
}
|
||||
}
|
||||
|
||||
// 更新指定用户的可用性状态
|
||||
func (ur _UserRepository) ChangeState(uid string, state bool) (bool, error) {
|
||||
ur.log.Info("更新指定用户的可用性状态。", zap.String("user id", uid))
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
userUpdateQuery := ur.ds.
|
||||
Update("user").
|
||||
Set(goqu.Record{"enabled": state}).
|
||||
Where(goqu.Ex{"id": uid})
|
||||
|
||||
userSql, userParams, _ := userUpdateQuery.
|
||||
Prepared(true).ToSQL()
|
||||
|
||||
if res, err := global.DB.Exec(ctx, userSql, userParams...); err != nil {
|
||||
ur.log.Error("向数据库更新指定用户的可用性状态失败。", zap.String("user id", uid), zap.Error(err))
|
||||
return false, err
|
||||
} else {
|
||||
return res.RowsAffected() > 0, nil
|
||||
}
|
||||
}
|
||||
|
||||
// 检索条目数量有限的用户详细信息
|
||||
func (ur _UserRepository) SearchUsersWithLimit(userType *int16, keyword *string, limit uint) ([]*model.UserWithDetail, error) {
|
||||
ur.log.Info("检索条目数量有限的用户详细信息。", zap.Int16p("user type", userType), zap.Uint("limit", limit), zap.Stringp("keyword", keyword))
|
||||
actualUserType := tools.DefaultTo(userType, model.USER_TYPE_ENT)
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
var users = make([]*model.UserWithDetail, 0)
|
||||
userQuery := ur.ds.
|
||||
From(goqu.T("user").As("u")).
|
||||
Join(goqu.T("user_detail").As("ud"), goqu.On(goqu.Ex{"ud.id": goqu.I("u.id")})).
|
||||
Select(
|
||||
"u.id", "u.username", "u.reset_needed", "u.type", "u.enabled",
|
||||
"ud.name", "ud.abbr", "ud.region", "ud.address", "ud.contact", "ud.phone",
|
||||
"ud.unit_service_fee", "ud.service_expiration",
|
||||
"ud.created_at", "ud.created_by", "ud.last_modified_at", "ud.last_modified_by",
|
||||
)
|
||||
|
||||
if keyword != nil && len(*keyword) > 0 {
|
||||
pattern := fmt.Sprintf("%%%s%%", *keyword)
|
||||
userQuery = userQuery.Where(
|
||||
goqu.Or(
|
||||
goqu.I("u.username").ILike(pattern),
|
||||
goqu.I("ud.name").ILike(pattern),
|
||||
goqu.I("ud.abbr").ILike(pattern),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
userQuery = userQuery.Where(goqu.Ex{"u.type": actualUserType})
|
||||
|
||||
userQuery.Order(goqu.I("u.created_at").Desc()).Limit(limit)
|
||||
|
||||
userSql, userParams, _ := userQuery.Prepared(true).ToSQL()
|
||||
if err := pgxscan.Select(ctx, global.DB, &users, userSql, userParams...); err != nil {
|
||||
ur.log.Error("从数据库查询用户列表失败。", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
return users, nil
|
||||
}
|
||||
|
||||
// 更新指定用户的服务有效期限
|
||||
func (ur _UserRepository) UpdateServiceExpiration(tx pgx.Tx, ctx context.Context, uid string, expiration time.Time) (bool, error) {
|
||||
ur.log.Info("更新指定用户的服务有效期限。", zap.String("user id", uid))
|
||||
|
||||
userDetailUpdateQuery := ur.ds.
|
||||
Update("user_detail").
|
||||
Set(goqu.Record{"service_expiration": expiration}).
|
||||
Where(goqu.Ex{"id": uid})
|
||||
|
||||
userDetailSql, userDetailParams, _ := userDetailUpdateQuery.
|
||||
Prepared(true).ToSQL()
|
||||
|
||||
if res, err := tx.Exec(ctx, userDetailSql, userDetailParams...); err != nil {
|
||||
ur.log.Error("向数据库更新指定用户的服务有效期限失败。", zap.String("user id", uid), zap.Error(err))
|
||||
return false, err
|
||||
} else {
|
||||
return res.RowsAffected() > 0, nil
|
||||
}
|
||||
}
|
||||
|
||||
// 检索指定用户列表的详细信息
|
||||
func (ur _UserRepository) RetrieveUsersDetail(uids []string) ([]*model.UserDetail, error) {
|
||||
ur.log.Info("检索指定用户列表的详细信息。", zap.Strings("user ids", uids))
|
||||
if len(uids) == 0 {
|
||||
return make([]*model.UserDetail, 0), nil
|
||||
}
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
var users []*model.UserDetail
|
||||
userQuery := ur.ds.
|
||||
From("user_detail").
|
||||
Where(goqu.Ex{"id": uids})
|
||||
|
||||
userSql, userParams, _ := userQuery.Prepared(true).ToSQL()
|
||||
if err := pgxscan.Select(ctx, global.DB, &users, userSql, userParams...); err != nil {
|
||||
ur.log.Error("从数据库查询用户列表失败。", zap.Error(err))
|
||||
return make([]*model.UserDetail, 0), err
|
||||
}
|
||||
return users, nil
|
||||
}
|
||||
242
repository/withdraw.go
Normal file
242
repository/withdraw.go
Normal file
@@ -0,0 +1,242 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"electricity_bill_calc/config"
|
||||
"electricity_bill_calc/global"
|
||||
"electricity_bill_calc/logger"
|
||||
"electricity_bill_calc/model"
|
||||
"electricity_bill_calc/tools"
|
||||
"electricity_bill_calc/types"
|
||||
"fmt"
|
||||
"github.com/doug-martin/goqu/v9"
|
||||
"github.com/georgysavva/scany/v2/pgxscan"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type _WithdrawRepository struct {
|
||||
log *zap.Logger
|
||||
ds goqu.DialectWrapper
|
||||
}
|
||||
|
||||
var WithdrawRepository = &_WithdrawRepository{
|
||||
log: logger.Named("Repository", "Withdraw"),
|
||||
ds: goqu.Dialect("postgres"),
|
||||
}
|
||||
|
||||
//该方法用于分页查询核算报表
|
||||
func (wd _WithdrawRepository) FindWithdraw(page uint, keyword *string) ([]model.Withdraw, int64, error) {
|
||||
wd.log.Info("查询核算报表", zap.Stringp("keyword", keyword), zap.Int("page", int(page)))
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
/**
|
||||
如果访问数据库次数过多出现时间过长的话可以用这个尝试优化,未测试的sql语句
|
||||
|
||||
wd.ds.From(goqu.T("report")).
|
||||
Where(goqu.I("withdraw").Eq(1)).
|
||||
Select(
|
||||
goqu.I("report.*"),
|
||||
goqu.I("park.*"),
|
||||
goqu.I("user_detail.*"),
|
||||
).
|
||||
Join(
|
||||
goqu.T("park"), goqu.On(goqu.I("report.park_id").Eq(goqu.I("park.id"))),
|
||||
).
|
||||
Join(
|
||||
goqu.T("user_detail"), goqu.On(goqu.I("park.user_id").Eq(goqu.I("user_detail.id"))),
|
||||
).ToSQL()
|
||||
|
||||
SELECT report.*, park.*, user_detail.*
|
||||
FROM report as r
|
||||
JOIN park as p ON r.park_id = p.id
|
||||
JOIN user_detail as ud ON p.user_id = ud.id
|
||||
WHERE withdraw = 1
|
||||
AND p.name Like '%keyword%'
|
||||
AND ud.name Like '%keyword%'
|
||||
*/
|
||||
reportQuery := wd.ds.
|
||||
From(goqu.T("report").As("r")).
|
||||
Where(goqu.I("withdraw").Eq(1)).
|
||||
Join(goqu.T("park").As("p"), goqu.On(goqu.I("r.park_id").Eq(goqu.I("p.id")))).
|
||||
Join(goqu.T("user_detail").As("ud"), goqu.On(goqu.I("p.user_id").Eq(goqu.I("ud.id")))).
|
||||
Where(goqu.I("p.deleted_at").IsNull()).
|
||||
Where(goqu.I("ud.deleted_at").IsNull()).
|
||||
Select(
|
||||
goqu.I("r.id").As("report_id"), goqu.I("r.last_withdraw_applied_at"), goqu.I("r.last_withdraw_audit_at"),
|
||||
goqu.I("r.park_id").As("report_park_id"), goqu.I("r.period"), goqu.I("r.published"), goqu.I("r.published_at"), goqu.I("r.withdraw"),
|
||||
goqu.I("p.address").As("park_address"), goqu.I("p.area"), goqu.I("p.capacity"), goqu.I("p.category"), goqu.I("p.contact").As("park_contact"),
|
||||
goqu.I("p.id").As("park_id"), goqu.I("p.meter_04kv_type"), goqu.I("p.name").As("park_name"), goqu.I("p.phone").As("park_phone"), goqu.I("p.region").As("park_region"),
|
||||
goqu.I("p.tenement_quantity"), goqu.I("p.user_id"), goqu.I("ud.address"), goqu.I("ud.contact").As("user_detail_contact"),
|
||||
goqu.I("ud.id").As("ud_id"), goqu.I("ud.name").As("user_detail_name"), goqu.I("ud.phone").As("user_detail_phone"), goqu.I("ud.region").As("user_detail_region"),
|
||||
)
|
||||
|
||||
countReportQuery := wd.ds.
|
||||
From(goqu.T("report").As("r")).
|
||||
Where(goqu.I("withdraw").Eq(1)).
|
||||
Join(goqu.T("park").As("p"), goqu.On(goqu.I("r.park_id").Eq(goqu.I("p.id")))).
|
||||
Join(goqu.T("user_detail").As("ud"), goqu.On(goqu.I("p.user_id").Eq(goqu.I("ud.id")))).
|
||||
Select(goqu.COUNT("*"))
|
||||
|
||||
if keyword != nil && len(*keyword) > 0 {
|
||||
pattern := fmt.Sprintf("%%%s%%", *keyword)
|
||||
reportQuery = reportQuery.Where(goqu.Or(
|
||||
goqu.I("p.name").ILike(pattern),
|
||||
goqu.I("ud.name").ILike(pattern),
|
||||
))
|
||||
}
|
||||
|
||||
reportQuery = reportQuery.Order(goqu.I("r.created_at").Desc())
|
||||
|
||||
currentPostion := (page - 1) * config.ServiceSettings.ItemsPageSize
|
||||
reportQuery = reportQuery.Offset(currentPostion).Limit(config.ServiceSettings.ItemsPageSize)
|
||||
|
||||
reportSql, reportArgs, _ := reportQuery.Prepared(true).ToSQL()
|
||||
|
||||
countReportQuerySql, countReportQueryArgs, _ := countReportQuery.Prepared(true).ToSQL()
|
||||
|
||||
var (
|
||||
reports []*model.ReportRes = make([]*model.ReportRes, 0)
|
||||
total int64
|
||||
)
|
||||
var err error
|
||||
|
||||
err = pgxscan.Select(ctx, global.DB, &reports, reportSql, reportArgs...)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
wd.log.Error("查询报表记录失败。", zap.Error(err))
|
||||
return make([]model.Withdraw, 0), 0, err
|
||||
}
|
||||
|
||||
if err = pgxscan.Get(ctx, global.DB, &total, countReportQuerySql, countReportQueryArgs...); err != nil {
|
||||
wd.log.Error("查询报表记录总数失败。", zap.Error(err))
|
||||
return make([]model.Withdraw, 0), 0, err
|
||||
}
|
||||
|
||||
if len(reports) <= 0 {
|
||||
return make([]model.Withdraw, 0), total, nil
|
||||
}
|
||||
|
||||
var withdrawReses []model.Withdraw
|
||||
//TODO: 2023.07.24对查询到的数据进行拼接(完成)
|
||||
for _, v := range reports {
|
||||
Begin := v.Period.SafeLower().Format("2006-01-02")
|
||||
End := v.Period.SafeUpper().Format("2006-01-02")
|
||||
|
||||
var withdrawRes model.Withdraw
|
||||
report := model.SimplifiedReport{
|
||||
ID: v.ReportId,
|
||||
LastWithdrawAppliedAt: tools.TimeToStringPtr(v.LastWithdrawAppliedAt),
|
||||
LastWithdrawAuditAt: tools.TimeToStringPtr(v.LastWithdrawAuditAt),
|
||||
Message: nil,
|
||||
ParkID: v.ParkID,
|
||||
PeriodBegin: Begin,
|
||||
PeriodEnd: End,
|
||||
Published: v.Published,
|
||||
PublishedAt: tools.TimeToStringPtr(v.LastWithdrawAuditAt),
|
||||
Status: 0.,
|
||||
Withdraw: v.Withdraw,
|
||||
}
|
||||
park := model.SimplifiedPark{
|
||||
Address: v.ParkAddress,
|
||||
Area: tools.NullDecimalToString(v.Area),
|
||||
Capacity: tools.NullDecimalToString(v.Capacity),
|
||||
Category: int16(v.Category),
|
||||
Contact: v.ParkContact,
|
||||
ID: v.ParkId,
|
||||
Meter04KvType: v.Meter04KVType,
|
||||
Name: v.ParkName,
|
||||
Phone: v.ParkPhone,
|
||||
Region: &v.ParkRegion,
|
||||
Tenement: tools.NullDecimalToString(v.TenementQuantity),
|
||||
UserID: v.UserID,
|
||||
}
|
||||
userInfo := model.UserInfos{
|
||||
Address: v.Address,
|
||||
Contact: &v.Contact,
|
||||
ID: v.ID,
|
||||
Name: v.Name,
|
||||
Phone: &v.Phone,
|
||||
Region: v.Region,
|
||||
}
|
||||
|
||||
withdrawRes.Report = report
|
||||
withdrawRes.Park = park
|
||||
withdrawRes.User = userInfo
|
||||
withdrawReses = append(withdrawReses, withdrawRes)
|
||||
}
|
||||
|
||||
return withdrawReses, total, nil
|
||||
}
|
||||
|
||||
//该方法用于审核同意报表撤回
|
||||
func (wd _WithdrawRepository) ReviewTrueReportWithdraw( rid string) (bool, error) {
|
||||
wd.log.Info("审核指定的报表", zap.String("rid", rid))
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
//update report set withdraw=$2,
|
||||
//last_withdraw_audit_at=$3, published=false,
|
||||
//published_at=null where id=$1
|
||||
|
||||
tx, err := global.DB.Begin(ctx)
|
||||
if err != nil {
|
||||
wd.log.Error("开启数据库事务失败", zap.Error(err))
|
||||
}
|
||||
updateQuerySql, updateArgs, _ := wd.ds.
|
||||
Update(goqu.T("report")).
|
||||
Set(goqu.Record{
|
||||
"withdraw": model.REPORT_WITHDRAW_GRANTED,
|
||||
"last_withdraw_audit_at": types.Now(),
|
||||
"published": false,
|
||||
"published_at": nil,
|
||||
}).
|
||||
Where(goqu.I("id").Eq(rid)).
|
||||
Prepared(true).ToSQL()
|
||||
|
||||
rs, err := tx.Exec(ctx, updateQuerySql, updateArgs...)
|
||||
if err != nil {
|
||||
wd.log.Error("审核报表失败", zap.Error(err))
|
||||
return false, err
|
||||
}
|
||||
err = tx.Commit(ctx)
|
||||
if err != nil {
|
||||
wd.log.Error("提交数据库事务失败", zap.Error(err))
|
||||
return false, err
|
||||
}
|
||||
|
||||
return rs.RowsAffected() > 0, nil
|
||||
}
|
||||
|
||||
//该方法用于审核拒绝报表撤回
|
||||
func (wd _WithdrawRepository) ReviewFalseReportWithdraw( rid string) (bool, error) {
|
||||
wd.log.Info("审核指定的报表", zap.String("rid", rid))
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
tx, err := global.DB.Begin(ctx)
|
||||
if err != nil {
|
||||
wd.log.Error("开启数据库事务失败", zap.Error(err))
|
||||
}
|
||||
updateQuerySql, updateArgs, _ := wd.ds.
|
||||
Update(goqu.T("report")).
|
||||
Set(goqu.Record{
|
||||
"withdraw": model.REPORT_WITHDRAW_DENIED,
|
||||
"last_withdraw_audit_at": types.Now(),
|
||||
"published": false,
|
||||
"published_at": nil,
|
||||
}).
|
||||
Where(goqu.I("id").Eq(rid)).
|
||||
Prepared(true).ToSQL()
|
||||
|
||||
rs, err := tx.Exec(ctx, updateQuerySql, updateArgs...)
|
||||
if err != nil {
|
||||
wd.log.Error("审核报表失败", zap.Error(err))
|
||||
return false, err
|
||||
}
|
||||
err = tx.Commit(ctx)
|
||||
if err != nil {
|
||||
wd.log.Error("提交数据库事务失败", zap.Error(err))
|
||||
return false, err
|
||||
}
|
||||
|
||||
return rs.RowsAffected() > 0, nil
|
||||
}
|
||||
@@ -2,13 +2,12 @@ package response
|
||||
|
||||
import (
|
||||
"electricity_bill_calc/config"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
type Result struct {
|
||||
Ctx *gin.Context
|
||||
Ctx *fiber.Ctx
|
||||
}
|
||||
|
||||
type BaseResponse struct {
|
||||
@@ -18,15 +17,15 @@ type BaseResponse struct {
|
||||
|
||||
type PagedResponse struct {
|
||||
Page int `json:"current"`
|
||||
Size int `json:"pageSize"`
|
||||
Size uint `json:"pageSize"`
|
||||
Total int64 `json:"total"`
|
||||
}
|
||||
|
||||
func NewResult(ctx *gin.Context) *Result {
|
||||
return &Result{Ctx: ctx}
|
||||
func NewResult(ctx *fiber.Ctx) Result {
|
||||
return Result{Ctx: ctx}
|
||||
}
|
||||
|
||||
func (r *Result) response(status, code int, msg string, payloads ...map[string]interface{}) {
|
||||
func (r Result) response(status, code int, msg string, payloads ...map[string]interface{}) error {
|
||||
var finalPayload = make(map[string]interface{}, 0)
|
||||
finalPayload["code"] = code
|
||||
finalPayload["message"] = msg
|
||||
@@ -37,68 +36,78 @@ func (r *Result) response(status, code int, msg string, payloads ...map[string]i
|
||||
}
|
||||
}
|
||||
|
||||
r.Ctx.JSON(status, finalPayload)
|
||||
return r.Ctx.Status(status).JSON(finalPayload)
|
||||
}
|
||||
|
||||
// 禁止访问响应
|
||||
func (r Result) Forbidden(msg string) error {
|
||||
return r.response(fiber.StatusForbidden, fiber.StatusForbidden, msg)
|
||||
}
|
||||
|
||||
// 操作出错(未成功)响应
|
||||
func (r *Result) Failure(code int, msg string) {
|
||||
r.response(http.StatusInternalServerError, code, msg)
|
||||
func (r Result) Failure(code int, msg string) error {
|
||||
return r.response(fiber.StatusInternalServerError, code, msg)
|
||||
}
|
||||
|
||||
// 操作出错(未成功)响应
|
||||
func (r *Result) Error(code int, msg string) {
|
||||
r.response(http.StatusOK, code, msg)
|
||||
func (r Result) Error(code int, msg string) error {
|
||||
return r.response(fiber.StatusOK, code, msg)
|
||||
}
|
||||
|
||||
// 不能解析传入的参数
|
||||
func (r Result) UnableToParse(msg string) error {
|
||||
return r.response(fiber.StatusInternalServerError, fiber.StatusInternalServerError, msg)
|
||||
}
|
||||
|
||||
// 用户未获得授权)响应
|
||||
func (r *Result) Unauthorized(msg string) {
|
||||
r.response(http.StatusOK, http.StatusUnauthorized, msg)
|
||||
func (r *Result) Unauthorized(msg string) error {
|
||||
return r.response(fiber.StatusUnauthorized, fiber.StatusUnauthorized, msg)
|
||||
}
|
||||
|
||||
// 简易操作成功信息
|
||||
func (r *Result) Success(msg string, payloads ...map[string]interface{}) {
|
||||
r.response(http.StatusOK, http.StatusOK, msg, payloads...)
|
||||
func (r *Result) Success(msg string, payloads ...map[string]interface{}) error {
|
||||
return r.response(fiber.StatusOK, fiber.StatusOK, msg, payloads...)
|
||||
}
|
||||
|
||||
// 数据成功创建
|
||||
func (r *Result) Created(msg string, payloads ...map[string]interface{}) {
|
||||
r.response(http.StatusOK, http.StatusCreated, msg, payloads...)
|
||||
func (r Result) Created(msg string, payloads ...map[string]interface{}) error {
|
||||
return r.response(fiber.StatusCreated, fiber.StatusCreated, msg, payloads...)
|
||||
}
|
||||
|
||||
// 数据成功更新
|
||||
func (r *Result) Updated(msg string) {
|
||||
r.response(http.StatusOK, http.StatusAccepted, msg)
|
||||
func (r Result) Updated(msg string, payloads ...map[string]interface{}) error {
|
||||
return r.response(fiber.StatusAccepted, fiber.StatusAccepted, msg, payloads...)
|
||||
}
|
||||
|
||||
// 数据已成功删除
|
||||
func (r *Result) Deleted(msg string) {
|
||||
r.response(http.StatusOK, http.StatusNoContent, msg)
|
||||
func (r Result) Deleted(msg string) error {
|
||||
return r.response(fiber.StatusNoContent, fiber.StatusNoContent, msg)
|
||||
}
|
||||
|
||||
// 指定操作未被接受
|
||||
func (r *Result) BadRequest(msg string) {
|
||||
r.response(http.StatusOK, http.StatusBadRequest, msg)
|
||||
func (r Result) BadRequest(msg string) error {
|
||||
return r.response(fiber.StatusBadRequest, fiber.StatusBadRequest, msg)
|
||||
}
|
||||
|
||||
// 指定操作未被接受
|
||||
func (r *Result) NotAccept(msg string) {
|
||||
r.response(http.StatusOK, http.StatusNotAcceptable, msg)
|
||||
func (r Result) NotAccept(msg string) error {
|
||||
return r.response(fiber.StatusNotAcceptable, fiber.StatusNotAcceptable, msg)
|
||||
}
|
||||
|
||||
// 数据未找到
|
||||
func (r *Result) NotFound(msg string) {
|
||||
r.response(http.StatusOK, http.StatusNotFound, msg)
|
||||
func (r Result) NotFound(msg string) error {
|
||||
return r.response(fiber.StatusNotFound, fiber.StatusNotFound, msg)
|
||||
}
|
||||
|
||||
// 数据存在冲突
|
||||
func (r *Result) Conflict(msg string) {
|
||||
r.response(http.StatusOK, http.StatusConflict, msg)
|
||||
func (r Result) Conflict(msg string) error {
|
||||
return r.response(fiber.StatusConflict, fiber.StatusConflict, msg)
|
||||
}
|
||||
|
||||
// 快速自由JSON格式响应
|
||||
// ! 注意,给定的map中,同名的键会被覆盖。
|
||||
func (r *Result) Json(code int, msg string, payloads ...map[string]interface{}) {
|
||||
r.response(http.StatusOK, code, msg, payloads...)
|
||||
func (r Result) Json(code int, msg string, payloads ...map[string]interface{}) error {
|
||||
return r.response(fiber.StatusOK, code, msg, payloads...)
|
||||
}
|
||||
|
||||
func NewPagedResponse(page int, total int64) *PagedResponse {
|
||||
@@ -106,7 +115,7 @@ func NewPagedResponse(page int, total int64) *PagedResponse {
|
||||
}
|
||||
|
||||
func (r PagedResponse) ToMap() map[string]interface{} {
|
||||
return gin.H{
|
||||
return fiber.Map{
|
||||
"current": r.Page,
|
||||
"pageSize": r.Size,
|
||||
"total": r.Total,
|
||||
|
||||
@@ -3,6 +3,8 @@ package response
|
||||
import (
|
||||
"electricity_bill_calc/model"
|
||||
"net/http"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
type LoginResponse struct {
|
||||
@@ -11,19 +13,19 @@ type LoginResponse struct {
|
||||
Session *model.Session `json:"session"`
|
||||
}
|
||||
|
||||
func (r *Result) LoginSuccess(session *model.Session) {
|
||||
func (r Result) LoginSuccess(session *model.Session) error {
|
||||
res := &LoginResponse{}
|
||||
res.Code = http.StatusOK
|
||||
res.Message = "用户已成功登录。"
|
||||
res.Message = "用户已成功登录。"+ "👋!"
|
||||
res.NeedReset = false
|
||||
res.Session = session
|
||||
r.Ctx.JSON(http.StatusOK, res)
|
||||
return r.Ctx.Status(fiber.StatusOK).JSON(res)
|
||||
}
|
||||
|
||||
func (r *Result) LoginNeedReset() {
|
||||
func (r Result) LoginNeedReset() error {
|
||||
res := &LoginResponse{}
|
||||
res.Code = http.StatusUnauthorized
|
||||
res.Message = "用户凭据已失效。"
|
||||
res.NeedReset = true
|
||||
r.Ctx.JSON(http.StatusOK, res)
|
||||
return r.Ctx.Status(fiber.StatusOK).JSON(res)
|
||||
}
|
||||
|
||||
104
router/router.go
104
router/router.go
@@ -2,48 +2,82 @@ package router
|
||||
|
||||
import (
|
||||
"electricity_bill_calc/controller"
|
||||
"electricity_bill_calc/response"
|
||||
"electricity_bill_calc/logger"
|
||||
"electricity_bill_calc/security"
|
||||
"log"
|
||||
"runtime/debug"
|
||||
"fmt"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/gofiber/fiber/v2/middleware/compress"
|
||||
"github.com/gofiber/fiber/v2/middleware/recover"
|
||||
jsontime "github.com/liamylian/jsontime/v2/v2"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
func Router() *gin.Engine {
|
||||
router := gin.Default()
|
||||
router.Use(Recover)
|
||||
router.Use(security.SessionRecovery)
|
||||
var json = jsontime.ConfigWithCustomTimeFormat
|
||||
|
||||
controller.InitializeUserController(router)
|
||||
controller.InitializeRegionController(router)
|
||||
controller.InitializeChargesController(router)
|
||||
controller.InitializeParkController(router)
|
||||
controller.InitializeMaintenanceFeeController(router)
|
||||
controller.InitializeMeter04kVController(router)
|
||||
controller.InitializeReportController(router)
|
||||
controller.InitializeEndUserController(router)
|
||||
controller.InitializeWithdrawController(router)
|
||||
controller.InitializeStatisticsController(router)
|
||||
|
||||
return router
|
||||
func init() {
|
||||
timeZoneShanghai, _ := time.LoadLocation("Asia/Shanghai")
|
||||
jsontime.AddTimeFormatAlias("simple_datetime", "2006-01-02 15:04:05")
|
||||
jsontime.AddTimeFormatAlias("simple_date", "2006-01-02")
|
||||
jsontime.AddLocaleAlias("shanghai", timeZoneShanghai)
|
||||
}
|
||||
|
||||
// 404
|
||||
func HandleNotFound(c *gin.Context) {
|
||||
response.NewResult(c).NotFound("指定资源未找到。")
|
||||
func App() *fiber.App {
|
||||
app := fiber.New(fiber.Config{ //创建fiber实例的时候选择配置选项
|
||||
BodyLimit: 30 * 1024 * 1024, //设置请求正文允许的最大大小。
|
||||
EnablePrintRoutes: true, //自定义方案,用于启动消息
|
||||
EnableTrustedProxyCheck: false, //禁用受信代理
|
||||
Prefork: false, //禁止预处理(如果要启用预处理则需要通过shell脚本运行)
|
||||
ErrorHandler: errorHandler, //相应全局处理错误
|
||||
JSONEncoder: json.Marshal, //json编码
|
||||
JSONDecoder: json.Unmarshal, //json解码
|
||||
})
|
||||
app.Use(compress.New()) //压缩中间件
|
||||
app.Use(recover.New(recover.Config{
|
||||
EnableStackTrace: true,
|
||||
StackTraceHandler: stackTraceHandler,
|
||||
})) //恢复中间件
|
||||
app.Use(logger.NewLogMiddleware(logger.LogMiddlewareConfig{
|
||||
Logger: logger.Named("App"),
|
||||
})) //日志中间件
|
||||
app.Use(security.SessionRecovery) //会话恢复中间件
|
||||
|
||||
controller.InitializeUserHandlers(app)
|
||||
controller.InitializeRegionHandlers(app)
|
||||
controller.InitializeChargeHandlers(app)
|
||||
controller.InitializeParkHandlers(app)
|
||||
controller.InitializeTenementHandler(app)
|
||||
controller.InitializeMeterHandlers(app)
|
||||
controller.InitializeInvoiceHandler(app)
|
||||
controller.InitializeTopUpHandlers(app)
|
||||
controller.InitializeReportHandlers(app)
|
||||
|
||||
controller.InitializeWithdrawHandlers(app) // 公示撤回
|
||||
controller.InitializeFoundationHandlers(app) // 基础数据
|
||||
controller.InitializeStatisticsController(app) // 首页信息
|
||||
controller.InitializeGmController(app) // 天神模式
|
||||
|
||||
return app
|
||||
}
|
||||
|
||||
// 500
|
||||
func Recover(c *gin.Context) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
//打印错误堆栈信息
|
||||
log.Printf("panic: %v\n", r)
|
||||
debug.PrintStack()
|
||||
// response.NewResult(c).Error(500, "服务器内部错误")
|
||||
}
|
||||
}()
|
||||
//继续后续接口调用
|
||||
c.Next()
|
||||
// 全局错误处理
|
||||
func errorHandler(c *fiber.Ctx, err error) error {
|
||||
code := fiber.StatusInternalServerError
|
||||
if e, ok := err.(*fiber.Error); ok {
|
||||
code = e.Code
|
||||
}
|
||||
e := c.Status(code).SendString(err.Error())
|
||||
if e != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).SendString(err.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 处理Recover中间件输出的栈追踪信息
|
||||
func stackTraceHandler(c *fiber.Ctx, e interface{}) {
|
||||
buf := make([]byte, 1024)
|
||||
buf = buf[:runtime.Stack(buf, false)]
|
||||
logger.Named("App", "StackTrace").Warn(fmt.Sprintf("panic: %+v", e), zap.ByteString("trace", buf), zap.Any("origin", e))
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user